Index: sys/dev/pci/pci.c =================================================================== --- sys/dev/pci/pci.c +++ sys/dev/pci/pci.c @@ -63,6 +63,11 @@ #include #include +#ifdef PCI_IOV +#include +#include +#endif + #include #include #include @@ -694,6 +699,78 @@ #undef REG static void +pci_ea_fill_info(device_t pcib, pcicfgregs *cfg) +{ +#define REG(n, w) PCIB_READ_CONFIG(pcib, cfg->bus, cfg->slot, cfg->func, \ + cfg->ea.ea_location + (n), w) + int num_ent; + int ptr; + int a, b; + uint32_t val; + int ent_size; + uint32_t dw[4]; + uint64_t base, max_offset; + struct pci_ea_entry *eae; + + if (cfg->ea.ea_location == 0) + return; + + STAILQ_INIT(&cfg->ea.ea_entries); + + /* Determine the number of entries */ + num_ent = REG(PCIR_EA_NUM_ENT, 2); + num_ent &= PCIM_EA_NUM_ENT_MASK; + + /* Find the first entry to care of */ + ptr = PCIR_EA_FIRST_ENT; + + /* Skip DWORD 2 for type 1 functions */ + if ((cfg->hdrtype & PCIM_HDRTYPE) == PCIM_HDRTYPE_BRIDGE) + ptr += 4; + + for (a = 0; a < num_ent; a++) { + /* Read a number of dwords in the entry */ + val = REG(ptr, 4); + ptr += 4; + ent_size = (val & PCIM_EA_ES); + + for (b = 0; b < ent_size; b++) { + dw[b] = REG(ptr, 4); + ptr += 4; + } + + eae = malloc(sizeof(*eae), M_DEVBUF, M_WAITOK | M_ZERO); + eae->eae_flags = val; + eae->eae_bei = (PCIM_EA_BEI & val) >> PCIM_EA_BEI_OFFSET; + + base = dw[0] & PCIM_EA_FIELD_MASK; + max_offset = dw[1] | ~PCIM_EA_FIELD_MASK; + b = 2; + if (((dw[0] & PCIM_EA_IS_64) != 0) && (b < ent_size)) { + base |= (uint64_t)dw[b] << 32UL; + b++; + } + if (((dw[1] & PCIM_EA_IS_64) != 0) + && (b < ent_size)) { + max_offset |= (uint64_t)dw[b] << 32UL; + b++; + } + + eae->eae_base = base; + eae->eae_max_offset = max_offset; + + STAILQ_INSERT_TAIL(&cfg->ea.ea_entries, eae, eae_link); + + if (bootverbose) { + printf("PCI(EA) dev %04x:%04x, bei %d, flags #%x, base #%lx, max_offset #%lx\n", + cfg->vendor, cfg->device, eae->eae_bei, eae->eae_flags, + eae->eae_base, eae->eae_max_offset); + } + } +} +#undef REG + +static void pci_read_cap(device_t pcib, pcicfgregs *cfg) { #define REG(n, w) PCIB_READ_CONFIG(pcib, cfg->bus, cfg->slot, cfg->func, n, w) @@ -830,6 +907,10 @@ val = REG(ptr + PCIER_FLAGS, 2); cfg->pcie.pcie_type = val & PCIEM_FLAGS_TYPE; break; + case PCIY_EA: /* Enhanced Allocation */ + cfg->ea.ea_location = ptr; + pci_ea_fill_info(pcib, cfg); + break; default: break; } @@ -3504,6 +3585,135 @@ } #endif +static int +pci_ea_bei_to_rid(device_t bus, device_t dev, uint8_t bei) +{ +#ifdef PCI_IOV + struct pci_devinfo *dinfo; + int iov_pos; + struct pcicfg_iov *iov; + + dinfo = device_get_ivars(dev); + iov = dinfo->cfg.iov; + if (iov != NULL) + iov_pos = iov->iov_pos; + else + iov_pos = 0; +#endif + + /* Check if matches BAR */ + if ((bei >= PCIM_EA_BEI_BAR_0) && + (bei <= PCIM_EA_BEI_BAR_5)) + return (PCIR_BAR(bei)); + + /* Check ROM */ + if (bei == PCIM_EA_BEI_ROM) + return (PCIR_BIOS); + +#ifdef PCI_IOV + /* Check if matches VF_BAR */ + if ((iov != NULL) && (bei >= PCIM_EA_BEI_VF_BAR_0) && + (bei <= PCIM_EA_BEI_VF_BAR_5)) + return (PCIR_SRIOV_BAR(bei - PCIM_EA_BEI_VF_BAR_0) + + iov_pos); +#endif + + return (-1); +} + +void +pci_add_resources_ea(device_t bus, device_t dev, int alloc_iov) +{ + struct pci_ea_entry *ea; + struct pci_devinfo *dinfo; + pci_addr_t start, end, count; + struct resource_list *rl; + int type, flags, rid; + struct resource *res; +#ifdef PCI_IOV + struct pcicfg_iov *iov; +#endif + + dinfo = device_get_ivars(dev); + rl = &dinfo->resources; + flags = 0; + +#ifdef PCI_IOV + iov = dinfo->cfg.iov; +#endif + + if (dinfo->cfg.ea.ea_location == 0) + return; + + STAILQ_FOREACH(ea, &dinfo->cfg.ea.ea_entries, eae_link) { + + if ((ea->eae_flags & PCIM_EA_ENABLE) == 0) + continue; + + switch ((ea->eae_flags & PCIM_EA_PP) >> PCIM_EA_PP_OFFSET) { + case PCIM_EA_P_MEM_PREFETCH: + case PCIM_EA_P_VF_MEM_PREFETCH: + flags = RF_PREFETCHABLE; + case PCIM_EA_P_VF_MEM: + case PCIM_EA_P_MEM: + type = SYS_RES_MEMORY; + break; + case PCIM_EA_P_IO: + type = SYS_RES_IOPORT; + break; + default: + continue; + } + + if (alloc_iov != 0) { +#ifdef PCI_IOV + /* Allocating IOV, confirm BEI matches */ + if ((ea->eae_bei < PCIM_EA_BEI_VF_BAR_0) || + (ea->eae_bei > PCIM_EA_BEI_VF_BAR_5)) + continue; +#else + continue; +#endif + } else { + /* Allocating BAR, confirm BEI matches */ + if (((ea->eae_bei < PCIM_EA_BEI_BAR_0) || + (ea->eae_bei > PCIM_EA_BEI_BAR_5)) && + (ea->eae_bei != PCIM_EA_BEI_ROM)) + continue; + } + + rid = pci_ea_bei_to_rid(bus, dev, ea->eae_bei); + if (rid < 0) + continue; + + /* Skip resources already allocated by EA */ + if ((resource_list_find(rl, SYS_RES_MEMORY, rid) != NULL) || + (resource_list_find(rl, SYS_RES_IOPORT, rid) != NULL)) + continue; + + start = ea->eae_base; + count = ea->eae_max_offset + 1; +#ifdef PCI_IOV + if (iov != NULL) + count = count * iov->iov_num_vfs; +#endif + end = start + count - 1; + if (count == 0) + continue; + + resource_list_add(rl, type, rid, start, end, count); + res = resource_list_reserve(rl, bus, dev, type, &rid, start, end, count, + flags); + if (res == NULL) { + resource_list_delete(rl, type, rid); + continue; + } + + /* As per specification, fill BAR with zeros */ + pci_write_config(dev, rid, 0, 4); + } +} + void pci_add_resources(device_t bus, device_t dev, int force, uint32_t prefetchmask) { @@ -3519,6 +3729,9 @@ rl = &dinfo->resources; devid = (cfg->device << 16) | cfg->vendor; + /* Allocate resources using Enhanced Allocation */ + pci_add_resources_ea(bus, dev, 0); + /* ATA devices needs special map treatment */ if ((pci_get_class(dev) == PCIC_STORAGE) && (pci_get_subclass(dev) == PCIS_STORAGE_IDE) && @@ -3528,6 +3741,13 @@ pci_ata_maps(bus, dev, rl, force, prefetchmask); else for (i = 0; i < cfg->nummaps;) { + /* Skip resources already allocated by EA */ + if ((resource_list_find(rl, SYS_RES_MEMORY, PCIR_BAR(i)) != NULL) || + (resource_list_find(rl, SYS_RES_IOPORT, PCIR_BAR(i)) != NULL)) { + i++; + continue; + } + /* * Skip quirked resources. */ Index: sys/dev/pci/pci_iov.c =================================================================== --- sys/dev/pci/pci_iov.c +++ sys/dev/pci/pci_iov.c @@ -513,6 +513,37 @@ } static int +pci_iov_alloc_bar_ea(struct pci_devinfo *dinfo, int bar) +{ + struct pcicfg_iov *iov; + rman_res_t start, end; + struct resource *res; + struct resource_list *rl; + struct resource_list_entry *rle; + + rl = &dinfo->resources; + iov = dinfo->cfg.iov; + + rle = resource_list_find(rl, SYS_RES_MEMORY, + iov->iov_pos + PCIR_SRIOV_BAR(bar)); + if (rle == NULL) + rle = resource_list_find(rl, SYS_RES_IOPORT, + iov->iov_pos + PCIR_SRIOV_BAR(bar)); + if (rle == NULL) + return (ENXIO); + res = rle->res; + + iov->iov_bar[bar].res = res; + iov->iov_bar[bar].bar_size = rman_get_size(res) / iov->iov_num_vfs; + iov->iov_bar[bar].bar_shift = pci_mapsize(iov->iov_bar[bar].bar_size); + + start = rman_get_start(res); + end = rman_get_end(res); + + return (rman_manage_region(&iov->rman, start, end)); +} + +static int pci_iov_setup_bars(struct pci_devinfo *dinfo) { device_t dev; @@ -524,7 +555,14 @@ dev = dinfo->cfg.dev; last_64 = 0; + pci_add_resources_ea(device_get_parent(dev), dev, 1); + for (i = 0; i <= PCIR_MAX_BAR_0; i++) { + /* First, try to use BARs allocated with EA */ + error = pci_iov_alloc_bar_ea(dinfo, i); + if (error == 0) + continue; + /* * If a PCI BAR is a 64-bit wide BAR, then it spans two * consecutive registers. Therefore if the last BAR that Index: sys/dev/pci/pci_private.h =================================================================== --- sys/dev/pci/pci_private.h +++ sys/dev/pci/pci_private.h @@ -55,6 +55,7 @@ uint16_t rid, uint16_t vid, uint16_t did); void pci_add_resources(device_t bus, device_t dev, int force, uint32_t prefetchmask); +void pci_add_resources_ea(device_t bus, device_t dev, int alloc_iov); int pci_attach_common(device_t dev); void pci_delete_child(device_t dev, device_t child); void pci_driver_added(device_t dev, driver_t *driver); Index: sys/dev/pci/pcireg.h =================================================================== --- sys/dev/pci/pcireg.h +++ sys/dev/pci/pcireg.h @@ -146,6 +146,7 @@ #define PCIY_MSIX 0x11 /* MSI-X */ #define PCIY_SATA 0x12 /* SATA */ #define PCIY_PCIAF 0x13 /* PCI Advanced Features */ +#define PCIY_EA 0x14 /* PCI Extended Allocation */ /* Extended Capability Register Fields */ @@ -586,6 +587,49 @@ #define PCIR_MSI_MASK 0x10 #define PCIR_MSI_PENDING 0x14 +/* PCI Enhanced Allocation registers */ +#define PCIR_EA_NUM_ENT 2 /* Number of Capability Entries */ +#define PCIM_EA_NUM_ENT_MASK 0x3f /* Num Entries Mask */ +#define PCIR_EA_FIRST_ENT 4 /* First EA Entry in List */ +#define PCIR_EA_FIRST_ENT_BRIDGE 8 /* First EA Entry for Bridges */ +#define PCIM_EA_ES 0x00000007 /* Entry Size */ +#define PCIM_EA_BEI 0x000000f0 /* BAR Equivalent Indicator */ +#define PCIM_EA_BEI_OFFSET 4 +/* 0-5 map to BARs 0-5 respectively */ +#define PCIM_EA_BEI_BAR_0 0 +#define PCIM_EA_BEI_BAR_5 5 +#define PCIM_EA_BEI_BAR(x) (((x) >> PCIM_EA_BEI_OFFSET) & 0xf) +#define PCIM_EA_BEI_BRIDGE 0x6 /* Resource behind bridge */ +#define PCIM_EA_BEI_ENI 0x7 /* Equivalent Not Indicated */ +#define PCIM_EA_BEI_ROM 0x8 /* Expansion ROM */ +/* 9-14 map to VF BARs 0-5 respectively */ +#define PCIM_EA_BEI_VF_BAR_0 9 +#define PCIM_EA_BEI_VF_BAR_5 14 +#define PCIM_EA_BEI_RESERVED 0xf0 /* Reserved - Treat like ENI */ +#define PCIM_EA_PP 0x0000ff00 /* Primary Properties */ +#define PCIM_EA_PP_OFFSET 8 +#define PCIM_EA_SP_OFFSET 16 +#define PCIM_EA_SP 0x00ff0000 /* Secondary Properties */ +#define PCIM_EA_P_MEM 0x00 /* Non-Prefetch Memory */ +#define PCIM_EA_P_MEM_PREFETCH 0x01 /* Prefetchable Memory */ +#define PCIM_EA_P_IO 0x02 /* I/O Space */ +#define PCIM_EA_P_VF_MEM_PREFETCH 0x03 /* VF Prefetchable Memory */ +#define PCIM_EA_P_VF_MEM 0x04 /* VF Non-Prefetch Memory */ +#define PCIM_EA_P_BRIDGE_MEM 0x05 /* Bridge Non-Prefetch Memory */ +#define PCIM_EA_P_BRIDGE_MEM_PREFETCH 0x06 /* Bridge Prefetchable Memory */ +#define PCIM_EA_P_BRIDGE_IO 0x07 /* Bridge I/O Space */ +/* 0x08-0xfc reserved */ +#define PCIM_EA_P_MEM_RESERVED 0xfd /* Reserved Memory */ +#define PCIM_EA_P_IO_RESERVED 0xfe /* Reserved I/O Space */ +#define PCIM_EA_P_UNAVAILABLE 0xff /* Entry Unavailable */ +#define PCIM_EA_WRITABLE 0x40000000 /* Writable: 1 = RW, 0 = HwInit */ +#define PCIM_EA_ENABLE 0x80000000 /* Enable for this entry */ +#define PCIM_EA_BASE 4 /* Base Address Offset */ +#define PCIM_EA_MAX_OFFSET 8 /* MaxOffset (resource length) */ +/* bit 0 is reserved */ +#define PCIM_EA_IS_64 0x00000002 /* 64-bit field flag */ +#define PCIM_EA_FIELD_MASK 0xfffffffc /* For Base & Max Offset */ + /* PCI-X definitions */ /* For header type 0 devices */ Index: sys/dev/pci/pcivar.h =================================================================== --- sys/dev/pci/pcivar.h +++ sys/dev/pci/pcivar.h @@ -156,6 +156,19 @@ int index; }; +struct pci_ea_entry { + uint32_t eae_bei; + uint32_t eae_flags; + uint64_t eae_base; + uint64_t eae_max_offset; + STAILQ_ENTRY(pci_ea_entry) eae_link; +}; + +struct pcicfg_ea { + int ea_location; /* Structure offset in Configuration Header */ + STAILQ_HEAD(, pci_ea_entry) ea_entries; /* EA entries */ +}; + #define PCICFG_VF 0x0001 /* Device is an SR-IOV Virtual Function */ /* config header information common to all header types */ @@ -207,6 +220,7 @@ struct pcicfg_pcix pcix; /* PCI-X */ struct pcicfg_iov *iov; /* SR-IOV */ struct pcicfg_vf vf; /* SR-IOV Virtual Function */ + struct pcicfg_ea ea; /* Enhanced Allocation */ } pcicfgregs; /* additional type 1 device config header information (PCI to PCI bridge) */