x86: Support multiple PCI MCFG regions

In particular, this enables support for PCI config access for domains
(segments) other than 0.

Reported by:	cperciva
Tested by:	cperciva (m7i.metal-48xl AWS instance)
Reviewed by:	imp
Relnotes:	yes
Differential Revision:	https://reviews.freebsd.org/D42828
This commit is contained in:
John Baldwin 2023-11-29 10:32:39 -08:00
parent 9893a4fd31
commit f54a3890b1
5 changed files with 181 additions and 87 deletions

View file

@ -33,6 +33,7 @@
#include <sys/bus.h>
#include <sys/lock.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/sysctl.h>
#include <dev/pci/pcivar.h>
@ -41,12 +42,21 @@
#include <vm/pmap.h>
#include <machine/pci_cfgreg.h>
struct pcie_mcfg_region {
char *base;
uint16_t domain;
uint8_t minbus;
uint8_t maxbus;
};
static uint32_t pci_docfgregread(int domain, int bus, int slot, int func,
int reg, int bytes);
static int pciereg_cfgread(int domain, int bus, unsigned slot,
unsigned func, unsigned reg, unsigned bytes);
static void pciereg_cfgwrite(int domain, int bus, unsigned slot,
unsigned func, unsigned reg, int data, unsigned bytes);
static struct pcie_mcfg_region *pcie_lookup_region(int domain, int bus);
static int pciereg_cfgread(struct pcie_mcfg_region *region, int bus,
unsigned slot, unsigned func, unsigned reg, unsigned bytes);
static void pciereg_cfgwrite(struct pcie_mcfg_region *region, int bus,
unsigned slot, unsigned func, unsigned reg, int data,
unsigned bytes);
static int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
@ -59,11 +69,12 @@ SYSCTL_DECL(_hw_pci);
*/
int cfgmech = CFGMECH_1;
static vm_offset_t pcie_base;
static int pcie_minbus, pcie_maxbus;
static struct pcie_mcfg_region *mcfg_regions;
static int mcfg_numregions;
static uint32_t pcie_badslots;
static struct mtx pcicfg_mtx;
MTX_SYSINIT(pcicfg_mtx, &pcicfg_mtx, "pcicfg_mtx", MTX_SPIN);
static int mcfg_enable = 1;
SYSCTL_INT(_hw_pci, OID_AUTO, mcfg, CTLFLAG_RDTUN, &mcfg_enable, 0,
"Enable support for PCI-e memory mapped config access");
@ -75,16 +86,33 @@ pci_cfgregopen(void)
return (1);
}
static struct pcie_mcfg_region *
pcie_lookup_region(int domain, int bus)
{
for (int i = 0; i < mcfg_numregions; i++)
if (mcfg_regions[i].domain == domain &&
bus >= mcfg_regions[i].minbus &&
bus <= mcfg_regions[i].maxbus)
return (&mcfg_regions[i]);
return (NULL);
}
static uint32_t
pci_docfgregread(int domain, int bus, int slot, int func, int reg, int bytes)
{
if (domain == 0 && bus == 0 && (1 << slot & pcie_badslots) != 0)
return (pcireg_cfgread(bus, slot, func, reg, bytes));
if (cfgmech == CFGMECH_PCIE &&
(bus >= pcie_minbus && bus <= pcie_maxbus))
return (pciereg_cfgread(domain, bus, slot, func, reg, bytes));
else if (domain == 0)
if (cfgmech == CFGMECH_PCIE) {
struct pcie_mcfg_region *region;
region = pcie_lookup_region(domain, bus);
if (region != NULL)
return (pciereg_cfgread(region, bus, slot, func, reg,
bytes));
}
if (domain == 0)
return (pcireg_cfgread(bus, slot, func, reg, bytes));
else
return (-1);
@ -128,10 +156,18 @@ pci_cfgregwrite(int domain, int bus, int slot, int func, int reg, uint32_t data,
return;
}
if (cfgmech == CFGMECH_PCIE &&
(bus >= pcie_minbus && bus <= pcie_maxbus))
pciereg_cfgwrite(domain, bus, slot, func, reg, data, bytes);
else if (domain == 0)
if (cfgmech == CFGMECH_PCIE) {
struct pcie_mcfg_region *region;
region = pcie_lookup_region(domain, bus);
if (region != NULL) {
pciereg_cfgwrite(region, bus, slot, func, reg, data,
bytes);
return;
}
}
if (domain == 0)
pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
}
@ -217,7 +253,7 @@ pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
}
static void
pcie_init_badslots(void)
pcie_init_badslots(struct pcie_mcfg_region *region)
{
uint32_t val1, val2;
int slot;
@ -234,7 +270,7 @@ pcie_init_badslots(void)
if (val1 == 0xffffffff)
continue;
val2 = pciereg_cfgread(0, 0, slot, 0, 0, 4);
val2 = pciereg_cfgread(region, 0, slot, 0, 0, 4);
if (val2 != val1)
pcie_badslots |= (1 << slot);
}
@ -242,26 +278,34 @@ pcie_init_badslots(void)
}
int
pcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus)
pcie_cfgregopen(uint64_t base, uint16_t domain, uint8_t minbus, uint8_t maxbus)
{
struct pcie_mcfg_region *region;
if (!mcfg_enable)
return (0);
if (minbus != 0)
return (0);
if (bootverbose)
printf("PCIe: Memory Mapped configuration base @ 0x%lx\n",
base);
printf("PCI: MCFG domain %u bus %u-%u base @ 0x%lx\n",
domain, minbus, maxbus, base);
/* Resize the array. */
mcfg_regions = realloc(mcfg_regions,
sizeof(*mcfg_regions) * (mcfg_numregions + 1), M_DEVBUF, M_WAITOK);
region = &mcfg_regions[mcfg_numregions];
/* XXX: We should make sure this really fits into the direct map. */
pcie_base = (vm_offset_t)pmap_mapdev_pciecfg(base, (maxbus + 1) << 20);
pcie_minbus = minbus;
pcie_maxbus = maxbus;
region->base = pmap_mapdev_pciecfg(base, (maxbus + 1 - minbus) << 20);
region->domain = domain;
region->minbus = minbus;
region->maxbus = maxbus;
mcfg_numregions++;
cfgmech = CFGMECH_PCIE;
pcie_init_badslots();
if (domain == 0 && minbus == 0)
pcie_init_badslots(region);
return (1);
}
@ -282,17 +326,18 @@ pcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus)
*/
static int
pciereg_cfgread(int domain, int bus, unsigned slot, unsigned func, unsigned reg,
unsigned bytes)
pciereg_cfgread(struct pcie_mcfg_region *region, int bus, unsigned slot,
unsigned func, unsigned reg, unsigned bytes)
{
vm_offset_t va;
char *va;
int data = -1;
if (domain != 0 || bus < pcie_minbus || bus > pcie_maxbus ||
slot > PCI_SLOTMAX || func > PCI_FUNCMAX || reg > PCIE_REGMAX)
MPASS(bus >= region->minbus && bus <= region->maxbus);
if (slot > PCI_SLOTMAX || func > PCI_FUNCMAX || reg > PCIE_REGMAX)
return (-1);
va = PCIE_VADDR(pcie_base, reg, bus, slot, func);
va = PCIE_VADDR(region->base, reg, bus - region->minbus, slot, func);
switch (bytes) {
case 4:
@ -313,16 +358,17 @@ pciereg_cfgread(int domain, int bus, unsigned slot, unsigned func, unsigned reg,
}
static void
pciereg_cfgwrite(int domain, int bus, unsigned slot, unsigned func,
unsigned reg, int data, unsigned bytes)
pciereg_cfgwrite(struct pcie_mcfg_region *region, int bus, unsigned slot,
unsigned func, unsigned reg, int data, unsigned bytes)
{
vm_offset_t va;
char *va;
if (domain != 0 || bus < pcie_minbus || bus > pcie_maxbus ||
slot > PCI_SLOTMAX || func > PCI_FUNCMAX || reg > PCIE_REGMAX)
MPASS(bus >= region->minbus && bus <= region->maxbus);
if (slot > PCI_SLOTMAX || func > PCI_FUNCMAX || reg > PCIE_REGMAX)
return;
va = PCIE_VADDR(pcie_base, reg, bus, slot, func);
va = PCIE_VADDR(region->base, reg, bus - region->minbus, slot, func);
switch (bytes) {
case 4:

View file

@ -2226,11 +2226,8 @@ acpi_enable_pcie(void)
end = (ACPI_MCFG_ALLOCATION *)((char *)hdr + hdr->Length);
alloc = (ACPI_MCFG_ALLOCATION *)((ACPI_TABLE_MCFG *)hdr + 1);
while (alloc < end) {
if (alloc->PciSegment == 0) {
pcie_cfgregopen(alloc->Address, alloc->StartBusNumber,
alloc->EndBusNumber);
return;
}
pcie_cfgregopen(alloc->Address, alloc->PciSegment,
alloc->StartBusNumber, alloc->EndBusNumber);
alloc++;
}
#endif

View file

@ -54,6 +54,13 @@
printf a ; \
} while(0)
struct pcie_mcfg_region {
uint64_t base;
uint16_t domain;
uint8_t minbus;
uint8_t maxbus;
};
#define PCIE_CACHE 8
struct pcie_cfg_elem {
TAILQ_ENTRY(pcie_cfg_elem) elem;
@ -63,26 +70,30 @@ struct pcie_cfg_elem {
SYSCTL_DECL(_hw_pci);
static struct pcie_mcfg_region *mcfg_regions;
static int mcfg_numregions;
static TAILQ_HEAD(pcie_cfg_list, pcie_cfg_elem) pcie_list[MAXCPU];
static uint64_t pcie_base;
static int pcie_minbus, pcie_maxbus;
static int pcie_cache_initted;
static uint32_t pcie_badslots;
int cfgmech;
static int devmax;
static struct mtx pcicfg_mtx;
static int mcfg_enable = 1;
SYSCTL_INT(_hw_pci, OID_AUTO, mcfg, CTLFLAG_RDTUN, &mcfg_enable, 0,
"Enable support for PCI-e memory mapped config access");
static uint32_t pci_docfgregread(int domain, int bus, int slot, int func,
int reg, int bytes);
static struct pcie_mcfg_region *pcie_lookup_region(int domain, int bus);
static int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
static int pcireg_cfgopen(void);
static int pciereg_cfgread(int domain, int bus, unsigned slot,
unsigned func, unsigned reg, unsigned bytes);
static void pciereg_cfgwrite(int domain, int bus, unsigned slot,
unsigned func, unsigned reg, int data, unsigned bytes);
static int pciereg_cfgread(struct pcie_mcfg_region *region, int bus,
unsigned slot, unsigned func, unsigned reg, unsigned bytes);
static void pciereg_cfgwrite(struct pcie_mcfg_region *region, int bus,
unsigned slot, unsigned func, unsigned reg, int data,
unsigned bytes);
/*
* Some BIOS writers seem to want to ignore the spec and put
@ -149,16 +160,33 @@ pci_cfgregopen(void)
return (1);
}
static struct pcie_mcfg_region *
pcie_lookup_region(int domain, int bus)
{
for (int i = 0; i < mcfg_numregions; i++)
if (mcfg_regions[i].domain == domain &&
bus >= mcfg_regions[i].minbus &&
bus <= mcfg_regions[i].maxbus)
return (&mcfg_regions[i]);
return (NULL);
}
static uint32_t
pci_docfgregread(int domain, int bus, int slot, int func, int reg, int bytes)
{
if (domain == 0 && bus == 0 && (1 << slot & pcie_badslots) != 0)
return (pcireg_cfgread(bus, slot, func, reg, bytes));
if (cfgmech == CFGMECH_PCIE &&
(bus >= pcie_minbus && bus <= pcie_maxbus))
return (pciereg_cfgread(domain, bus, slot, func, reg, bytes));
else if (domain == 0)
if (cfgmech == CFGMECH_PCIE) {
struct pcie_mcfg_region *region;
region = pcie_lookup_region(domain, bus);
if (region != NULL)
return (pciereg_cfgread(region, bus, slot, func, reg,
bytes));
}
if (domain == 0)
return (pcireg_cfgread(bus, slot, func, reg, bytes));
else
return (-1);
@ -197,10 +225,18 @@ pci_cfgregwrite(int domain, int bus, int slot, int func, int reg, uint32_t data,
return;
}
if (cfgmech == CFGMECH_PCIE &&
(bus >= pcie_minbus && bus <= pcie_maxbus))
pciereg_cfgwrite(domain, bus, slot, func, reg, data, bytes);
else if (domain == 0)
if (cfgmech == CFGMECH_PCIE) {
struct pcie_mcfg_region *region;
region = pcie_lookup_region(domain, bus);
if (region != NULL) {
pciereg_cfgwrite(region, bus, slot, func, reg, data,
bytes);
return;
}
}
if (domain == 0)
pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
}
@ -479,7 +515,7 @@ pcie_init_cache(void)
}
static void
pcie_init_badslots(void)
pcie_init_badslots(struct pcie_mcfg_region *region)
{
uint32_t val1, val2;
int slot;
@ -496,7 +532,7 @@ pcie_init_badslots(void)
if (val1 == 0xffffffff)
continue;
val2 = pciereg_cfgread(0, 0, slot, 0, 0, 4);
val2 = pciereg_cfgread(region, 0, slot, 0, 0, 4);
if (val2 != val1)
pcie_badslots |= (1 << slot);
}
@ -504,37 +540,51 @@ pcie_init_badslots(void)
}
int
pcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus)
pcie_cfgregopen(uint64_t base, uint16_t domain, uint8_t minbus, uint8_t maxbus)
{
struct pcie_mcfg_region *region;
if (!mcfg_enable)
return (0);
if (minbus != 0)
return (0);
if (!pae_mode && base >= 0x100000000) {
if (bootverbose)
printf(
"PCI: Memory Mapped PCI configuration area base 0x%jx too high\n",
(uintmax_t)base);
"PCI: MCFG domain %u bus %u-%u base 0x%jx too high\n",
domain, minbus, maxbus, (uintmax_t)base);
return (0);
}
if (bootverbose)
printf("PCIe: Memory Mapped configuration base @ 0x%jx\n",
(uintmax_t)base);
printf("PCI: MCFG domain %u bus %u-%u base @ 0x%jx\n",
domain, minbus, maxbus, (uintmax_t)base);
if (!pcie_init_cache())
if (pcie_cache_initted == 0) {
if (!pcie_init_cache())
pcie_cache_initted = -1;
else
pcie_cache_initted = 1;
}
if (pcie_cache_initted == -1)
return (0);
pcie_base = base;
pcie_minbus = minbus;
pcie_maxbus = maxbus;
/* Resize the array. */
mcfg_regions = realloc(mcfg_regions,
sizeof(*mcfg_regions) * (mcfg_numregions + 1), M_DEVBUF, M_WAITOK);
region = &mcfg_regions[mcfg_numregions];
region->base = base;
region->domain = domain;
region->minbus = minbus;
region->maxbus = maxbus;
mcfg_numregions++;
cfgmech = CFGMECH_PCIE;
devmax = 32;
pcie_init_badslots();
if (domain == 0 && minbus == 0)
pcie_init_badslots(region);
return (1);
}
@ -547,13 +597,16 @@ pcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus)
((reg) & 0xfff)))
static __inline vm_offset_t
pciereg_findaddr(int bus, unsigned slot, unsigned func, unsigned reg)
pciereg_findaddr(struct pcie_mcfg_region *region, int bus, unsigned slot,
unsigned func, unsigned reg)
{
struct pcie_cfg_list *pcielist;
struct pcie_cfg_elem *elem;
vm_paddr_t pa, papage;
pa = PCIE_PADDR(pcie_base, reg, bus, slot, func);
MPASS(bus >= region->minbus && bus <= region->maxbus);
pa = PCIE_PADDR(region->base, reg, bus - region->minbus, slot, func);
papage = pa & ~PAGE_MASK;
/*
@ -594,18 +647,17 @@ pciereg_findaddr(int bus, unsigned slot, unsigned func, unsigned reg)
*/
static int
pciereg_cfgread(int domain, int bus, unsigned slot, unsigned func, unsigned reg,
unsigned bytes)
pciereg_cfgread(struct pcie_mcfg_region *region, int bus, unsigned slot,
unsigned func, unsigned reg, unsigned bytes)
{
vm_offset_t va;
int data = -1;
if (domain != 0 || bus < pcie_minbus || bus > pcie_maxbus ||
slot > PCI_SLOTMAX || func > PCI_FUNCMAX || reg > PCIE_REGMAX)
if (slot > PCI_SLOTMAX || func > PCI_FUNCMAX || reg > PCIE_REGMAX)
return (-1);
critical_enter();
va = pciereg_findaddr(bus, slot, func, reg);
va = pciereg_findaddr(region, bus, slot, func, reg);
switch (bytes) {
case 4:
@ -627,17 +679,16 @@ pciereg_cfgread(int domain, int bus, unsigned slot, unsigned func, unsigned reg,
}
static void
pciereg_cfgwrite(int domain, int bus, unsigned slot, unsigned func,
unsigned reg, int data, unsigned bytes)
pciereg_cfgwrite(struct pcie_mcfg_region *region, int bus, unsigned slot,
unsigned func, unsigned reg, int data, unsigned bytes)
{
vm_offset_t va;
if (domain != 0 || bus < pcie_minbus || bus > pcie_maxbus ||
slot > PCI_SLOTMAX || func > PCI_FUNCMAX || reg > PCIE_REGMAX)
if (slot > PCI_SLOTMAX || func > PCI_FUNCMAX || reg > PCIE_REGMAX)
return;
critical_enter();
va = pciereg_findaddr(bus, slot, func, reg);
va = pciereg_findaddr(region, bus, slot, func, reg);
switch (bytes) {
case 4:

View file

@ -56,7 +56,7 @@ enum {
extern int cfgmech;
rman_res_t hostb_alloc_start(int type, rman_res_t start, rman_res_t end, rman_res_t count);
int pcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus);
int pcie_cfgregopen(uint64_t base, uint16_t domain, uint8_t minbus, uint8_t maxbus);
int pci_cfgregopen(void);
u_int32_t pci_cfgregread(int domain, int bus, int slot, int func, int reg, int bytes);
void pci_cfgregwrite(int domain, int bus, int slot, int func, int reg, u_int32_t data, int bytes);

View file

@ -133,14 +133,14 @@ legacy_pci_cfgregopen(device_t dev)
case 0x3592:
/* Intel 7520 or 7320 */
pciebar = pci_cfgregread(0, 0, 0, 0, 0xce, 2) << 16;
pcie_cfgregopen(pciebar, 0, 255);
pcie_cfgregopen(pciebar, 0, 0, 255);
break;
case 0x2580:
case 0x2584:
case 0x2590:
/* Intel 915, 925, or 915GM */
pciebar = pci_cfgregread(0, 0, 0, 0, 0x48, 4);
pcie_cfgregopen(pciebar, 0, 255);
pcie_cfgregopen(pciebar, 0, 0, 255);
break;
}
}