summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/dev/pci/pci_user.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/dev/pci/pci_user.c')
-rw-r--r--freebsd/sys/dev/pci/pci_user.c113
1 files changed, 109 insertions, 4 deletions
diff --git a/freebsd/sys/dev/pci/pci_user.c b/freebsd/sys/dev/pci/pci_user.c
index c9d500a8..b3a2e9e2 100644
--- a/freebsd/sys/dev/pci/pci_user.c
+++ b/freebsd/sys/dev/pci/pci_user.c
@@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$");
#include <rtems/bsd/local/opt_bus.h> /* XXX trim includes */
+#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
@@ -41,13 +42,19 @@ __FBSDID("$FreeBSD$");
#include <sys/fcntl.h>
#include <sys/conf.h>
#include <sys/kernel.h>
+#include <sys/mman.h>
#include <sys/proc.h>
#include <sys/queue.h>
-#include <sys/types.h>
+#include <sys/rwlock.h>
+#include <sys/sglist.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include <vm/vm_extern.h>
+#include <vm/vm_map.h>
+#include <vm/vm_object.h>
+#include <vm/vm_page.h>
+#include <vm/vm_pager.h>
#include <sys/bus.h>
#include <machine/bus.h>
@@ -441,6 +448,14 @@ pci_conf_match(u_long cmd, struct pci_match_conf *matches, int num_matches,
}
}
+/*
+ * Like PVE_NEXT but takes an explicit length since 'pve' is a user
+ * pointer that cannot be dereferenced.
+ */
+#define PVE_NEXT_LEN(pve, datalen) \
+ ((struct pci_vpd_element *)((char *)(pve) + \
+ sizeof(struct pci_vpd_element) + (datalen)))
+
static int
pci_list_vpd(device_t dev, struct pci_list_vpd_io *lvio)
{
@@ -489,7 +504,7 @@ pci_list_vpd(device_t dev, struct pci_list_vpd_io *lvio)
strlen(vpd->vpd_ident));
if (error)
return (error);
- vpd_user = PVE_NEXT(vpd_user);
+ vpd_user = PVE_NEXT_LEN(vpd_user, vpd_element.pve_datalen);
vpd_element.pve_flags = 0;
for (i = 0; i < vpd->vpd_rocnt; i++) {
vpd_element.pve_keyword[0] = vpd->vpd_ros[i].keyword[0];
@@ -502,7 +517,7 @@ pci_list_vpd(device_t dev, struct pci_list_vpd_io *lvio)
vpd->vpd_ros[i].len);
if (error)
return (error);
- vpd_user = PVE_NEXT(vpd_user);
+ vpd_user = PVE_NEXT_LEN(vpd_user, vpd_element.pve_datalen);
}
vpd_element.pve_flags = PVE_FLAG_RW;
for (i = 0; i < vpd->vpd_wcnt; i++) {
@@ -516,7 +531,7 @@ pci_list_vpd(device_t dev, struct pci_list_vpd_io *lvio)
vpd->vpd_w[i].len);
if (error)
return (error);
- vpd_user = PVE_NEXT(vpd_user);
+ vpd_user = PVE_NEXT_LEN(vpd_user, vpd_element.pve_datalen);
}
KASSERT((char *)vpd_user - (char *)lvio->plvi_data == len,
("length mismatch"));
@@ -698,6 +713,79 @@ pci_conf_for_copyout(const struct pci_conf *pcp, union pci_conf_union *pcup,
}
}
+#ifndef __rtems__
+static int
+pci_bar_mmap(device_t pcidev, struct pci_bar_mmap *pbm)
+{
+ vm_map_t map;
+ vm_object_t obj;
+ struct thread *td;
+ struct sglist *sg;
+ struct pci_map *pm;
+ vm_paddr_t pbase;
+ vm_size_t plen;
+ vm_offset_t addr;
+ vm_prot_t prot;
+ int error, flags;
+
+ td = curthread;
+ map = &td->td_proc->p_vmspace->vm_map;
+ if ((pbm->pbm_flags & ~(PCIIO_BAR_MMAP_FIXED | PCIIO_BAR_MMAP_EXCL |
+ PCIIO_BAR_MMAP_RW | PCIIO_BAR_MMAP_ACTIVATE)) != 0 ||
+ pbm->pbm_memattr != (vm_memattr_t)pbm->pbm_memattr ||
+ !pmap_is_valid_memattr(map->pmap, pbm->pbm_memattr))
+ return (EINVAL);
+
+ /* Fetch the BAR physical base and length. */
+ pm = pci_find_bar(pcidev, pbm->pbm_reg);
+ if (pm == NULL)
+ return (EINVAL);
+ if (!pci_bar_enabled(pcidev, pm))
+ return (EBUSY); /* XXXKIB enable if _ACTIVATE */
+ if (!PCI_BAR_MEM(pm->pm_value))
+ return (EIO);
+ pbase = trunc_page(pm->pm_value);
+ plen = round_page(pm->pm_value + ((pci_addr_t)1 << pm->pm_size)) -
+ pbase;
+ prot = VM_PROT_READ | (((pbm->pbm_flags & PCIIO_BAR_MMAP_RW) != 0) ?
+ VM_PROT_WRITE : 0);
+
+ /* Create vm structures and mmap. */
+ sg = sglist_alloc(1, M_WAITOK);
+ error = sglist_append_phys(sg, pbase, plen);
+ if (error != 0)
+ goto out;
+ obj = vm_pager_allocate(OBJT_SG, sg, plen, prot, 0, td->td_ucred);
+ if (obj == NULL) {
+ error = EIO;
+ goto out;
+ }
+ obj->memattr = pbm->pbm_memattr;
+ flags = MAP_SHARED;
+ addr = 0;
+ if ((pbm->pbm_flags & PCIIO_BAR_MMAP_FIXED) != 0) {
+ addr = (uintptr_t)pbm->pbm_map_base;
+ flags |= MAP_FIXED;
+ }
+ if ((pbm->pbm_flags & PCIIO_BAR_MMAP_EXCL) != 0)
+ flags |= MAP_CHECK_EXCL;
+ error = vm_mmap_object(map, &addr, plen, prot, prot, flags, obj, 0,
+ FALSE, td);
+ if (error != 0) {
+ vm_object_deallocate(obj);
+ goto out;
+ }
+ pbm->pbm_map_base = (void *)addr;
+ pbm->pbm_map_length = plen;
+ pbm->pbm_bar_off = pm->pm_value - pbase;
+ pbm->pbm_bar_length = (pci_addr_t)1 << pm->pm_size;
+
+out:
+ sglist_free(sg);
+ return (error);
+}
+#endif /* __rtems__ */
+
static int
pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
{
@@ -711,6 +799,9 @@ pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *t
struct pci_list_vpd_io *lvio;
struct pci_match_conf *pattern_buf;
struct pci_map *pm;
+#ifndef __rtems__
+ struct pci_bar_mmap *pbm;
+#endif /* __rtems__ */
size_t confsz, iolen;
int error, ionum, i, num_patterns;
union pci_conf_union pcu;
@@ -1055,6 +1146,20 @@ getconfexit:
}
error = pci_list_vpd(pcidev, lvio);
break;
+
+#ifndef __rtems__
+ case PCIOCBARMMAP:
+ pbm = (struct pci_bar_mmap *)data;
+ if ((flag & FWRITE) == 0 &&
+ (pbm->pbm_flags & PCIIO_BAR_MMAP_RW) != 0)
+ return (EPERM);
+ pcidev = pci_find_dbsf(pbm->pbm_sel.pc_domain,
+ pbm->pbm_sel.pc_bus, pbm->pbm_sel.pc_dev,
+ pbm->pbm_sel.pc_func);
+ error = pcidev == NULL ? ENODEV : pci_bar_mmap(pcidev, pbm);
+ break;
+#endif /* __rtems__ */
+
default:
error = ENOTTY;
break;