Changeset View
Standalone View
sys/dev/pci/pci_user.c
Show All 25 Lines | |||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include "opt_bus.h" /* XXX trim includes */ | #include "opt_bus.h" /* XXX trim includes */ | ||||
#include <sys/types.h> | |||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/linker.h> | #include <sys/linker.h> | ||||
#include <sys/fcntl.h> | #include <sys/fcntl.h> | ||||
#include <sys/conf.h> | #include <sys/conf.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/mman.h> | |||||
#include <sys/proc.h> | #include <sys/proc.h> | ||||
#include <sys/queue.h> | #include <sys/queue.h> | ||||
#include <sys/types.h> | #include <sys/rwlock.h> | ||||
#include <sys/sglist.h> | |||||
#include <vm/vm.h> | #include <vm/vm.h> | ||||
#include <vm/pmap.h> | #include <vm/pmap.h> | ||||
#include <vm/vm_extern.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 <sys/bus.h> | ||||
#include <machine/bus.h> | #include <machine/bus.h> | ||||
#include <sys/rman.h> | #include <sys/rman.h> | ||||
#include <machine/resource.h> | #include <machine/resource.h> | ||||
#include <sys/pciio.h> | #include <sys/pciio.h> | ||||
#include <dev/pci/pcireg.h> | #include <dev/pci/pcireg.h> | ||||
▲ Show 20 Lines • Show All 635 Lines • ▼ Show 20 Lines | #endif /* PRE7_COMPAT */ | ||||
default: | default: | ||||
/* programmer error */ | /* programmer error */ | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
static int | 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 || | |||||
!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); | |||||
imp: What's to prevent concurrent access with other drivers? | |||||
Not Done Inline ActionsI am not sure what do you mean there. What other parallel action is undesirable there ? Note that I listed in the questions the driver behavior WRT pci device disappearing, but I do not think that this was your question. kib: I am not sure what do you mean there. What other parallel action is undesirable there ?
Note… | |||||
Not Done Inline ActionsMy original worry was an in-kernel driver racing against this operation if somehow a driver was loaded. But the other worry is what happens if there's user programs run, perhaps accidentally, at the same time. imp: My original worry was an in-kernel driver racing against this operation if somehow a driver was… | |||||
Not Done Inline ActionsIMO it is up to user/app/driver to coordinate. My own use case for this functionality was driver-less bitstream update for some PCIe card with FPGA, where the bits must be uploaded before real driver can attach. kib: IMO it is up to user/app/driver to coordinate.
My own use case for this functionality was… | |||||
if (pm == NULL) | |||||
return (EINVAL); | |||||
if (!pci_bar_enabled(pcidev, pm)) | |||||
return (EBUSY); /* XXXKIB enable if _ACTIVATE */ | |||||
Not Done Inline ActionsI'm not sure this is quite the right thing to do here. This just says to check the enabled bits for io or memory based on the type of BAR it is... In a normal device's resource activation, we turn on these bits. I'd likely turn on the mem bit in the config register if PCIIO_BAR_MMAP_ACTIVATE is set, but I'd be even more likely to turn it on just always. I'd also be tempted to filter PCIR_IS_BIOS bars as well. imp: I'm not sure this is quite the right thing to do here. This just says to check the enabled bits… | |||||
Not Done Inline ActionsThis was another question. Do we always allocate resources for BARs, even for devices which do not have the driver attached ? How attaching a driver later and attempting to activate the BAR would interact with this ? kib: This was another question. Do we always allocate resources for BARs, even for devices which do… | |||||
Not Done Inline ActionsYes and no. If the BIOS allocates things, we mostly honor those allocations. If not, we do a lazy assignment of resources. Some devices come up with the IO/MEM bits in their command register set, others require the driver to set them. If this code is executed in cooperation with some driver, then this will be a good test. If not, then it's hit or miss depending on the specific hardware, I believe. My knowledge here may be a bit dated as I've seen this mostly on pre-pcie cards. imp: Yes and no. If the BIOS allocates things, we mostly honor those allocations. If not, we do a… | |||||
Not Done Inline ActionsI and jhb discussed this, it seems that the completely correct approach is to make BAR activation from mapping transparent to the device driver, so that bus_activate_resource() does not fail if the BAR is already active due to mapping. Also, deactivation of resources should not result in BAR deactivation while mmapings still exist. I.e. the resource should be refcounted. I also think that the vm object should be stored in pci_map structure. Anyway, this functionality is completely missing from the patch, and there is an opinion that just providing the ioctl is a start. kib: I and jhb discussed this, it seems that the completely correct approach is to make BAR… | |||||
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); | |||||
} | |||||
static int | |||||
pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) | pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) | ||||
{ | { | ||||
device_t pcidev; | device_t pcidev; | ||||
const char *name; | const char *name; | ||||
struct devlist *devlist_head; | struct devlist *devlist_head; | ||||
struct pci_conf_io *cio = NULL; | struct pci_conf_io *cio = NULL; | ||||
struct pci_devinfo *dinfo; | struct pci_devinfo *dinfo; | ||||
struct pci_io *io; | struct pci_io *io; | ||||
struct pci_bar_io *bio; | struct pci_bar_io *bio; | ||||
struct pci_list_vpd_io *lvio; | struct pci_list_vpd_io *lvio; | ||||
struct pci_match_conf *pattern_buf; | struct pci_match_conf *pattern_buf; | ||||
struct pci_map *pm; | struct pci_map *pm; | ||||
struct pci_bar_mmap *pbm; | |||||
size_t confsz, iolen; | size_t confsz, iolen; | ||||
int error, ionum, i, num_patterns; | int error, ionum, i, num_patterns; | ||||
union pci_conf_union pcu; | union pci_conf_union pcu; | ||||
#ifdef PRE7_COMPAT | #ifdef PRE7_COMPAT | ||||
struct pci_io iodata; | struct pci_io iodata; | ||||
struct pci_io_old *io_old; | struct pci_io_old *io_old; | ||||
io_old = NULL; | io_old = NULL; | ||||
#endif | #endif | ||||
if (!(flag & FWRITE)) { | if (!(flag & FWRITE)) { | ||||
switch (cmd) { | switch (cmd) { | ||||
case PCIOCGETCONF: | case PCIOCGETCONF: | ||||
#ifdef PRE7_COMPAT | #ifdef PRE7_COMPAT | ||||
case PCIOCGETCONF_OLD: | case PCIOCGETCONF_OLD: | ||||
#ifdef COMPAT_FREEBSD32 | #ifdef COMPAT_FREEBSD32 | ||||
case PCIOCGETCONF_OLD32: | case PCIOCGETCONF_OLD32: | ||||
#endif | #endif | ||||
#endif | #endif | ||||
case PCIOCGETBAR: | case PCIOCGETBAR: | ||||
case PCIOCLISTVPD: | case PCIOCLISTVPD: | ||||
case PCIOCBARMMAP: | |||||
break; | break; | ||||
default: | default: | ||||
return (EPERM); | return (EPERM); | ||||
} | } | ||||
} | } | ||||
switch (cmd) { | switch (cmd) { | ||||
▲ Show 20 Lines • Show All 307 Lines • ▼ Show 20 Lines | pcidev = pci_find_dbsf(lvio->plvi_sel.pc_domain, | ||||
lvio->plvi_sel.pc_bus, lvio->plvi_sel.pc_dev, | lvio->plvi_sel.pc_bus, lvio->plvi_sel.pc_dev, | ||||
lvio->plvi_sel.pc_func); | lvio->plvi_sel.pc_func); | ||||
if (pcidev == NULL) { | if (pcidev == NULL) { | ||||
error = ENODEV; | error = ENODEV; | ||||
break; | break; | ||||
} | } | ||||
error = pci_list_vpd(pcidev, lvio); | error = pci_list_vpd(pcidev, lvio); | ||||
break; | break; | ||||
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; | |||||
default: | default: | ||||
error = ENOTTY; | error = ENOTTY; | ||||
break; | break; | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } |
What's to prevent concurrent access with other drivers?