diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/pci/pci.c | 202 | ||||
-rw-r--r-- | sys/dev/pci/pci_user.c | 53 | ||||
-rw-r--r-- | sys/dev/pci/pcireg.h | 1 | ||||
-rw-r--r-- | sys/dev/pci/pcivar.h | 15 |
4 files changed, 172 insertions, 99 deletions
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index 8e600ff..40e3b0c 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -69,6 +69,11 @@ __FBSDID("$FreeBSD$"); #include "pcib_if.h" #include "pci_if.h" +#define PCIR_IS_BIOS(cfg, reg) \ + (((cfg)->hdrtype == PCIM_HDRTYPE_NORMAL && reg == PCIR_BIOS) || \ + ((cfg)->hdrtype == PCIM_HDRTYPE_BRIDGE && reg == PCIR_BIOS_1)) + + static pci_addr_t pci_mapbase(uint64_t mapreg); static const char *pci_maptype(uint64_t mapreg); static int pci_mapsize(uint64_t testval); @@ -531,6 +536,7 @@ pci_read_device(device_t pcib, int d, int b, int s, int f, size_t size) cfg->mfdev = (cfg->hdrtype & PCIM_MFDEV) != 0; cfg->hdrtype &= ~PCIM_MFDEV; + STAILQ_INIT(&cfg->maps); pci_fixancient(cfg); pci_hdrtypedata(pcib, b, s, f, cfg); @@ -2106,6 +2112,7 @@ int pci_freecfg(struct pci_devinfo *dinfo) { struct devlist *devlist_head; + struct pci_map *pm, *next; int i; devlist_head = &pci_devq; @@ -2119,6 +2126,9 @@ pci_freecfg(struct pci_devinfo *dinfo) free(dinfo->cfg.vpd.vpd_w[i].value, M_DEVBUF); free(dinfo->cfg.vpd.vpd_w, M_DEVBUF); } + STAILQ_FOREACH_SAFE(pm, &dinfo->cfg.maps, pm_link, next) { + free(pm, M_DEVBUF); + } STAILQ_REMOVE(devlist_head, dinfo, pci_devinfo, pci_links); free(dinfo, M_DEVBUF); @@ -2393,6 +2403,7 @@ pci_memen(device_t dev) static void pci_read_bar(device_t dev, int reg, pci_addr_t *mapp, pci_addr_t *testvalp) { + struct pci_devinfo *dinfo; pci_addr_t map, testval; int ln2range; uint16_t cmd; @@ -2402,7 +2413,8 @@ pci_read_bar(device_t dev, int reg, pci_addr_t *mapp, pci_addr_t *testvalp) * memory BAR. Bit 0 is special and should not be set when * sizing the BAR. */ - if (reg == PCIR_BIOS) { + dinfo = device_get_ivars(dev); + if (PCIR_IS_BIOS(&dinfo->cfg, reg)) { map = pci_read_config(dev, reg, 4); pci_write_config(dev, reg, 0xfffffffe, 4); testval = pci_read_config(dev, reg, 4); @@ -2453,20 +2465,99 @@ pci_read_bar(device_t dev, int reg, pci_addr_t *mapp, pci_addr_t *testvalp) } static void -pci_write_bar(device_t dev, int reg, pci_addr_t base) +pci_write_bar(device_t dev, struct pci_map *pm, pci_addr_t base) { - pci_addr_t map; + struct pci_devinfo *dinfo; int ln2range; - map = pci_read_config(dev, reg, 4); - - /* The device ROM BAR is always 32-bits. */ - if (reg == PCIR_BIOS) - return; - ln2range = pci_maprange(map); - pci_write_config(dev, reg, base, 4); + /* The device ROM BAR is always a 32-bit memory BAR. */ + dinfo = device_get_ivars(dev); + if (PCIR_IS_BIOS(&dinfo->cfg, pm->pm_reg)) + ln2range = 32; + else + ln2range = pci_maprange(pm->pm_value); + pci_write_config(dev, pm->pm_reg, base, 4); + if (ln2range == 64) + pci_write_config(dev, pm->pm_reg + 4, base >> 32, 4); + pm->pm_value = pci_read_config(dev, pm->pm_reg, 4); if (ln2range == 64) - pci_write_config(dev, reg + 4, base >> 32, 4); + pm->pm_value |= (pci_addr_t)pci_read_config(dev, pm->pm_reg + 4, 4) << 32; +} + +struct pci_map * +pci_find_bar(device_t dev, int reg) +{ + struct pci_devinfo *dinfo; + struct pci_map *pm; + + dinfo = device_get_ivars(dev); + STAILQ_FOREACH(pm, &dinfo->cfg.maps, pm_link) { + if (pm->pm_reg == reg) + return (pm); + } + return (NULL); +} + +int +pci_bar_enabled(device_t dev, struct pci_map *pm) +{ + struct pci_devinfo *dinfo; + uint16_t cmd; + + dinfo = device_get_ivars(dev); + if (PCIR_IS_BIOS(&dinfo->cfg, pm->pm_reg) && + !(pm->pm_value & PCIM_BIOS_ENABLE)) + return (0); + cmd = pci_read_config(dev, PCIR_COMMAND, 2); + if (PCIR_IS_BIOS(&dinfo->cfg, pm->pm_reg) || PCI_BAR_MEM(pm->pm_value)) + return ((cmd & PCIM_CMD_MEMEN) != 0); + else + return ((cmd & PCIM_CMD_PORTEN) != 0); +} + +static struct pci_map * +pci_add_bar(device_t dev, int reg, pci_addr_t value, pci_addr_t size) +{ + struct pci_devinfo *dinfo; + struct pci_map *pm, *prev; + + dinfo = device_get_ivars(dev); + pm = malloc(sizeof(*pm), M_DEVBUF, M_WAITOK | M_ZERO); + pm->pm_reg = reg; + pm->pm_value = value; + pm->pm_size = size; + STAILQ_FOREACH(prev, &dinfo->cfg.maps, pm_link) { + KASSERT(prev->pm_reg != pm->pm_reg, ("duplicate map %02x", + reg)); + if (STAILQ_NEXT(prev, pm_link) == NULL || + STAILQ_NEXT(prev, pm_link)->pm_reg > pm->pm_reg) + break; + } + if (prev != NULL) + STAILQ_INSERT_AFTER(&dinfo->cfg.maps, prev, pm, pm_link); + else + STAILQ_INSERT_TAIL(&dinfo->cfg.maps, pm, pm_link); + return (pm); +} + +static void +pci_restore_bars(device_t dev) +{ + struct pci_devinfo *dinfo; + struct pci_map *pm; + int ln2range; + + dinfo = device_get_ivars(dev); + STAILQ_FOREACH(pm, &dinfo->cfg.maps, pm_link) { + if (PCIR_IS_BIOS(&dinfo->cfg, pm->pm_reg)) + ln2range = 32; + else + ln2range = pci_maprange(pm->pm_value); + pci_write_config(dev, pm->pm_reg, pm->pm_value, 4); + if (ln2range == 64) + pci_write_config(dev, pm->pm_reg + 4, + pm->pm_value >> 32, 4); + } } /* @@ -2477,6 +2568,7 @@ static int pci_add_map(device_t bus, device_t dev, int reg, struct resource_list *rl, int force, int prefetch) { + struct pci_map *pm; pci_addr_t base, map, testval; pci_addr_t start, end, count; int barlen, basezero, maprange, mapsize, type; @@ -2513,6 +2605,8 @@ pci_add_map(device_t bus, device_t dev, int reg, struct resource_list *rl, (type == SYS_RES_IOPORT && mapsize < 2)) return (barlen); + /* Save a record of this BAR. */ + pm = pci_add_bar(dev, reg, map, mapsize); if (bootverbose) { printf("\tmap[%02x]: type %s, range %2d, base %#jx, size %2d", reg, pci_maptype(map), maprange, (uintmax_t)base, mapsize); @@ -2600,7 +2694,7 @@ pci_add_map(device_t bus, device_t dev, int reg, struct resource_list *rl, start = 0; } else start = rman_get_start(res); - pci_write_bar(dev, reg, start); + pci_write_bar(dev, pm, start); return (barlen); } @@ -3735,31 +3829,41 @@ pci_reserve_map(device_t dev, device_t child, int type, int *rid, struct resource_list *rl = &dinfo->resources; struct resource_list_entry *rle; struct resource *res; + struct pci_map *pm; pci_addr_t map, testval; int mapsize; - /* - * Weed out the bogons, and figure out how large the BAR/map - * is. Bars that read back 0 here are bogus and unimplemented. - * Note: atapci in legacy mode are special and handled elsewhere - * in the code. If you have a atapci device in legacy mode and - * it fails here, that other code is broken. - */ res = NULL; - pci_read_bar(child, *rid, &map, &testval); + pm = pci_find_bar(child, *rid); + if (pm != NULL) { + /* This is a BAR that we failed to allocate earlier. */ + mapsize = pm->pm_size; + map = pm->pm_value; + } else { + /* + * Weed out the bogons, and figure out how large the + * BAR/map is. BARs that read back 0 here are bogus + * and unimplemented. Note: atapci in legacy mode are + * special and handled elsewhere in the code. If you + * have a atapci device in legacy mode and it fails + * here, that other code is broken. + */ + pci_read_bar(child, *rid, &map, &testval); - /* - * Determine the size of the BAR and ignore BARs with a size - * of 0. Device ROM BARs use a different mask value. - */ - if (*rid == PCIR_BIOS) - mapsize = pci_romsize(testval); - else - mapsize = pci_mapsize(testval); - if (mapsize == 0) - goto out; + /* + * Determine the size of the BAR and ignore BARs with a size + * of 0. Device ROM BARs use a different mask value. + */ + if (PCIR_IS_BIOS(&dinfo->cfg, *rid)) + mapsize = pci_romsize(testval); + else + mapsize = pci_mapsize(testval); + if (mapsize == 0) + goto out; + pm = pci_add_bar(child, *rid, map, mapsize); + } - if (PCI_BAR_MEM(testval) || *rid == PCIR_BIOS) { + if (PCI_BAR_MEM(map) || PCIR_IS_BIOS(&dinfo->cfg, *rid)) { if (type != SYS_RES_MEMORY) { if (bootverbose) device_printf(dev, @@ -3789,12 +3893,12 @@ pci_reserve_map(device_t dev, device_t child, int type, int *rid, count = (pci_addr_t)1 << mapsize; if (RF_ALIGNMENT(flags) < mapsize) flags = (flags & ~RF_ALIGNMENT_MASK) | RF_ALIGNMENT_LOG2(mapsize); - if (PCI_BAR_MEM(testval) && (testval & PCIM_BAR_MEM_PREFETCH)) + if (PCI_BAR_MEM(map) && (map & PCIM_BAR_MEM_PREFETCH)) flags |= RF_PREFETCHABLE; /* * Allocate enough resource, and then write back the - * appropriate bar for that resource. + * appropriate BAR for that resource. */ res = BUS_ALLOC_RESOURCE(device_get_parent(dev), child, type, rid, start, end, count, flags & ~RF_ACTIVE); @@ -3818,7 +3922,7 @@ pci_reserve_map(device_t dev, device_t child, int type, int *rid, "Lazy allocation of %#lx bytes rid %#x type %d at %#lx\n", count, *rid, type, rman_get_start(res)); map = rman_get_start(res); - pci_write_bar(child, *rid, map); + pci_write_bar(child, pm, map); out:; return (res); } @@ -3879,6 +3983,7 @@ int pci_activate_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { + struct pci_devinfo *dinfo; int error; error = bus_generic_activate_resource(dev, child, type, rid, r); @@ -3888,9 +3993,10 @@ pci_activate_resource(device_t dev, device_t child, int type, int rid, /* Enable decoding in the command register when activating BARs. */ if (device_get_parent(child) == dev) { /* Device ROMs need their decoding explicitly enabled. */ - if (rid == PCIR_BIOS) - pci_write_config(child, rid, rman_get_start(r) | - PCIM_BIOS_ENABLE, 4); + dinfo = device_get_ivars(child); + if (PCIR_IS_BIOS(&dinfo->cfg, rid)) + pci_write_bar(child, pci_find_bar(child, rid), + rman_get_start(r) | PCIM_BIOS_ENABLE); switch (type) { case SYS_RES_IOPORT: case SYS_RES_MEMORY: @@ -3905,15 +4011,20 @@ int pci_deactivate_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { + struct pci_devinfo *dinfo; int error; error = bus_generic_deactivate_resource(dev, child, type, rid, r); if (error) return (error); - /* Disable decoding for device ROMs. */ - if (rid == PCIR_BIOS) - pci_write_config(child, rid, rman_get_start(r), 4); + /* Disable decoding for device ROMs. */ + if (device_get_parent(child) == dev) { + dinfo = device_get_ivars(child); + if (PCIR_IS_BIOS(&dinfo->cfg, rid)) + pci_write_bar(child, pci_find_bar(child, rid), + rman_get_start(r)); + } return (0); } @@ -3991,7 +4102,7 @@ pci_delete_resource(device_t dev, device_t child, int type, int rid) switch (type) { case SYS_RES_IOPORT: case SYS_RES_MEMORY: - pci_write_bar(child, rid, 0); + pci_write_bar(child, pci_find_bar(child, rid), 0); break; } #endif @@ -4090,7 +4201,6 @@ pci_modevent(module_t mod, int what, void *arg) void pci_cfg_restore(device_t dev, struct pci_devinfo *dinfo) { - int i; /* * Only do header type 0 devices. Type 1 devices are bridges, @@ -4112,9 +4222,7 @@ pci_cfg_restore(device_t dev, struct pci_devinfo *dinfo) */ if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) pci_set_powerstate(dev, PCI_POWERSTATE_D0); - for (i = 0; i < dinfo->cfg.nummaps; i++) - pci_write_config(dev, PCIR_BAR(i), dinfo->cfg.bar[i], 4); - pci_write_config(dev, PCIR_BIOS, dinfo->cfg.bios, 4); + pci_restore_bars(dev); pci_write_config(dev, PCIR_COMMAND, dinfo->cfg.cmdreg, 2); pci_write_config(dev, PCIR_INTLINE, dinfo->cfg.intline, 1); pci_write_config(dev, PCIR_INTPIN, dinfo->cfg.intpin, 1); @@ -4135,7 +4243,6 @@ pci_cfg_restore(device_t dev, struct pci_devinfo *dinfo) void pci_cfg_save(device_t dev, struct pci_devinfo *dinfo, int setstate) { - int i; uint32_t cls; int ps; @@ -4148,9 +4255,6 @@ pci_cfg_save(device_t dev, struct pci_devinfo *dinfo, int setstate) */ if ((dinfo->cfg.hdrtype & PCIM_HDRTYPE) != PCIM_HDRTYPE_NORMAL) return; - for (i = 0; i < dinfo->cfg.nummaps; i++) - dinfo->cfg.bar[i] = pci_read_config(dev, PCIR_BAR(i), 4); - dinfo->cfg.bios = pci_read_config(dev, PCIR_BIOS, 4); /* * Some drivers apparently write to these registers w/o updating our diff --git a/sys/dev/pci/pci_user.c b/sys/dev/pci/pci_user.c index fd622e8..db67da9 100644 --- a/sys/dev/pci/pci_user.c +++ b/sys/dev/pci/pci_user.c @@ -309,8 +309,7 @@ pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *t struct pci_io *io; struct pci_bar_io *bio; struct pci_match_conf *pattern_buf; - struct resource_list_entry *rle; - uint32_t value; + struct pci_map *pm; size_t confsz, iolen, pbufsz; int error, ionum, i, num_patterns; #ifdef PRE7_COMPAT @@ -685,54 +684,14 @@ getconfexit: 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) { + pm = pci_find_bar(pcidev, bio->pbi_reg); + if (pm == 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; + bio->pbi_base = pm->pm_value; + bio->pbi_length = (pci_addr_t)1 << pm->pm_size; + bio->pbi_enabled = pci_bar_enabled(pcidev, pm); error = 0; break; case PCIOCATTACHED: diff --git a/sys/dev/pci/pcireg.h b/sys/dev/pci/pcireg.h index 02fa7ea..db74809 100644 --- a/sys/dev/pci/pcireg.h +++ b/sys/dev/pci/pcireg.h @@ -212,6 +212,7 @@ #define PCIM_BRPM_64 0x1 #define PCIM_BRPM_MASK 0xf +#define PCIR_BIOS_1 0x38 #define PCIR_BRIDGECTL_1 0x3e /* config registers for header type 2 (CardBus) devices */ diff --git a/sys/dev/pci/pcivar.h b/sys/dev/pci/pcivar.h index ac75014..ae3fe79 100644 --- a/sys/dev/pci/pcivar.h +++ b/sys/dev/pci/pcivar.h @@ -46,7 +46,14 @@ struct pcicfg_pp { uint8_t pp_bse; /* conf. space addr. of PM BSE reg */ uint8_t pp_data; /* conf. space addr. of PM data reg */ }; - + +struct pci_map { + pci_addr_t pm_value; /* Raw BAR value */ + pci_addr_t pm_size; + uint8_t pm_reg; + STAILQ_ENTRY(pci_map) pm_link; +}; + struct vpd_readonly { char keyword[2]; char *value; @@ -120,8 +127,7 @@ struct pcicfg_ht { typedef struct pcicfg { struct device *dev; /* device which owns this */ - uint32_t bar[PCI_MAXMAPS_0]; /* BARs */ - uint32_t bios; /* BIOS mapping */ + STAILQ_HEAD(, pci_map) maps; /* BARs */ uint16_t subvendor; /* card vendor ID */ uint16_t subdevice; /* card device ID, assigned by card vendor */ @@ -477,4 +483,7 @@ STAILQ_HEAD(devlist, pci_devinfo); extern struct devlist pci_devq; extern uint32_t pci_generation; +struct pci_map *pci_find_bar(device_t dev, int reg); +int pci_bar_enabled(device_t dev, struct pci_map *pm); + #endif /* _PCIVAR_H_ */ |