diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/pci/pci_user.c | 73 | ||||
-rw-r--r-- | sys/dev/pci/pcireg.h | 4 |
2 files changed, 73 insertions, 4 deletions
diff --git a/sys/dev/pci/pci_user.c b/sys/dev/pci/pci_user.c index b7ae55a..ed75353 100644 --- a/sys/dev/pci/pci_user.c +++ b/sys/dev/pci/pci_user.c @@ -307,7 +307,10 @@ pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *t struct pci_conf_io *cio; struct pci_devinfo *dinfo; struct pci_io *io; + struct pci_bar_io *bio; struct pci_match_conf *pattern_buf; + struct resource_list_entry *rle; + uint32_t value; size_t confsz, iolen, pbufsz; int error, ionum, i, num_patterns; #ifdef PRE7_COMPAT @@ -319,11 +322,11 @@ pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *t io_old = NULL; pattern_buf_old = NULL; - if (!(flag & FWRITE) && - (cmd != PCIOCGETCONF && cmd != PCIOCGETCONF_OLD)) + if (!(flag & FWRITE) && cmd != PCIOCGETBAR && + cmd != PCIOCGETCONF && cmd != PCIOCGETCONF_OLD) return EPERM; #else - if (!(flag & FWRITE) && cmd != PCIOCGETCONF) + if (!(flag & FWRITE) && cmd != PCIOCGETBAR && cmd != PCIOCGETCONF) return EPERM; #endif @@ -669,6 +672,70 @@ getconfexit: } break; + case PCIOCGETBAR: + bio = (struct pci_bar_io *)data; + + /* + * Assume that the user-level bus number is + * in fact the physical PCI bus number. + */ + pcidev = pci_find_dbsf(bio->pbi_sel.pc_domain, + bio->pbi_sel.pc_bus, bio->pbi_sel.pc_dev, + bio->pbi_sel.pc_func); + if (pcidev == NULL) { + error = ENODEV; + break; + } + dinfo = device_get_ivars(pcidev); + + /* + * Look for a resource list entry matching the requested BAR. + * + * XXX: This will not find BARs that are not initialized, but + * maybe that is ok? + */ + rle = resource_list_find(&dinfo->resources, SYS_RES_MEMORY, + bio->pbi_reg); + if (rle == NULL) + rle = resource_list_find(&dinfo->resources, + SYS_RES_IOPORT, bio->pbi_reg); + if (rle == NULL || rle->res == NULL) { + error = EINVAL; + break; + } + + /* + * Ok, we have a resource for this BAR. Read the lower + * 32 bits to get any flags. + */ + value = pci_read_config(pcidev, bio->pbi_reg, 4); + if (PCI_BAR_MEM(value)) { + if (rle->type != SYS_RES_MEMORY) { + error = EINVAL; + break; + } + value &= ~PCIM_BAR_MEM_BASE; + } else { + if (rle->type != SYS_RES_IOPORT) { + error = EINVAL; + break; + } + value &= ~PCIM_BAR_IO_BASE; + } + bio->pbi_base = rman_get_start(rle->res) | value; + bio->pbi_length = rman_get_size(rle->res); + + /* + * Check the command register to determine if this BAR + * is enabled. + */ + value = pci_read_config(pcidev, PCIR_COMMAND, 2); + if (rle->type == SYS_RES_MEMORY) + bio->pbi_enabled = (value & PCIM_CMD_MEMEN) != 0; + else + bio->pbi_enabled = (value & PCIM_CMD_PORTEN) != 0; + error = 0; + break; default: error = ENOTTY; break; diff --git a/sys/dev/pci/pcireg.h b/sys/dev/pci/pcireg.h index e9ba5e7..0b69ca4 100644 --- a/sys/dev/pci/pcireg.h +++ b/sys/dev/pci/pcireg.h @@ -117,7 +117,7 @@ #define PCIR_BARS 0x10 #define PCIR_BAR(x) (PCIR_BARS + (x) * 4) -#define PCI_MAX_BAR_0 5 /* Number of standard bars */ +#define PCIR_MAX_BAR_0 5 #define PCI_RID2BAR(rid) (((rid) - PCIR_BARS) / 4) #define PCI_BAR_IO(x) (((x) & PCIM_BAR_SPACE) == PCIM_BAR_IO_SPACE) #define PCI_BAR_MEM(x) (((x) & PCIM_BAR_SPACE) == PCIM_BAR_MEM_SPACE) @@ -158,6 +158,7 @@ /* config registers for header type 1 (PCI-to-PCI bridge) devices */ +#define PCIR_MAX_BAR_1 1 #define PCIR_SECSTAT_1 0x1e #define PCIR_PRIBUS_1 0x18 @@ -188,6 +189,7 @@ /* config registers for header type 2 (CardBus) devices */ +#define PCIR_MAX_BAR_2 0 #define PCIR_CAP_PTR_2 0x14 #define PCIR_SECSTAT_2 0x16 |