mirror of
https://github.com/freebsd/freebsd-src
synced 2024-10-07 17:10:36 +00:00
Extend the support for PCI-e memory mapped configuration space access:
- Rename pciereg_cfgopen() to pcie_cfgregopen() and expose it to the rest of the kernel. It now also accepts parameters via function arguments rather than global variables. - Add a notion of minimum and maximum bus numbers and reject requests for an out of range bus. - Add more range checks on slot/func/reg/bytes parameters to the cfg reg read/write routines. Don't panic on any invalid parameters, just fail the request (writes do nothing, reads return -1). This matches the behavior of the other cfg mechanisms. - Port the memory mapped configuration space access to amd64. On amd64 we simply use the direct map (via pmap_mapdev()) for the memory mapped window. - During acpi_attach() just after loading the ACPI tables, check for a MCFG table. If it exists, call pciereg_cfgopen() on each subtable (memory mapped window). For now we only support windows for domain 0 that start with bus 0. This removes the need for more chipset-specific quirks in the MD code. - Remove the chipset-specific quirks for the Intel 5000P/V/Z chipsets since these machines should all have MCFG tables via ACPI. - Updated pci_cfgregopen() to DTRT if ACPI had invoked pcie_cfgregopen() earlier. MFC after: 2 weeks
This commit is contained in:
parent
643cd4ffe7
commit
d320e05ca5
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=181987
|
@ -37,6 +37,7 @@
|
|||
#define CONF1_ENABLE_MSK1 0x80000001ul
|
||||
#define CONF1_ENABLE_RES1 0x80000000ul
|
||||
|
||||
int pcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus);
|
||||
int pci_cfgregopen(void);
|
||||
u_int32_t pci_cfgregread(int bus, int slot, int func, int reg, int bytes);
|
||||
void pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes);
|
||||
|
|
|
@ -36,11 +36,26 @@ __FBSDID("$FreeBSD$");
|
|||
#include <sys/mutex.h>
|
||||
#include <dev/pci/pcivar.h>
|
||||
#include <dev/pci/pcireg.h>
|
||||
#include <vm/vm.h>
|
||||
#include <vm/pmap.h>
|
||||
#include <machine/pci_cfgreg.h>
|
||||
|
||||
enum {
|
||||
CFGMECH_NONE = 0,
|
||||
CFGMECH_1,
|
||||
CFGMECH_PCIE,
|
||||
};
|
||||
|
||||
static int pciereg_cfgread(int bus, unsigned slot, unsigned func,
|
||||
unsigned reg, unsigned bytes);
|
||||
static void pciereg_cfgwrite(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);
|
||||
|
||||
static int cfgmech;
|
||||
static vm_offset_t pcie_base;
|
||||
static int pcie_minbus, pcie_maxbus;
|
||||
static struct mtx pcicfg_mtx;
|
||||
|
||||
/*
|
||||
|
@ -49,12 +64,42 @@ static struct mtx pcicfg_mtx;
|
|||
int
|
||||
pci_cfgregopen(void)
|
||||
{
|
||||
static int opened = 0;
|
||||
uint64_t pciebar;
|
||||
uint16_t did, vid;
|
||||
|
||||
if (opened)
|
||||
if (cfgmech != CFGMECH_NONE)
|
||||
return (1);
|
||||
mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN);
|
||||
opened = 1;
|
||||
cfgmech = CFGMECH_1;
|
||||
|
||||
/*
|
||||
* Grope around in the PCI config space to see if this is a
|
||||
* chipset that is capable of doing memory-mapped config cycles.
|
||||
* This also implies that it can do PCIe extended config cycles.
|
||||
*/
|
||||
|
||||
/* Check for supported chipsets */
|
||||
vid = pci_cfgregread(0, 0, 0, PCIR_VENDOR, 2);
|
||||
did = pci_cfgregread(0, 0, 0, PCIR_DEVICE, 2);
|
||||
switch (vid) {
|
||||
case 0x8086:
|
||||
switch (did) {
|
||||
case 0x3590:
|
||||
case 0x3592:
|
||||
/* Intel 7520 or 7320 */
|
||||
pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16;
|
||||
pcie_cfgregopen(pciebar, 0, 255);
|
||||
break;
|
||||
case 0x2580:
|
||||
case 0x2584:
|
||||
case 0x2590:
|
||||
/* Intel 915, 925, or 915GM */
|
||||
pciebar = pci_cfgregread(0, 0, 0, 0x48, 4);
|
||||
pcie_cfgregopen(pciebar, 0, 255);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
|
@ -130,6 +175,11 @@ pcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
|
|||
int data = -1;
|
||||
int port;
|
||||
|
||||
if (cfgmech == CFGMECH_PCIE) {
|
||||
data = pciereg_cfgread(bus, slot, func, reg, bytes);
|
||||
return (data);
|
||||
}
|
||||
|
||||
mtx_lock_spin(&pcicfg_mtx);
|
||||
port = pci_cfgenable(bus, slot, func, reg, bytes);
|
||||
if (port != 0) {
|
||||
|
@ -155,6 +205,11 @@ pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
|
|||
{
|
||||
int port;
|
||||
|
||||
if (cfgmech == CFGMECH_PCIE) {
|
||||
pciereg_cfgwrite(bus, slot, func, reg, data, bytes);
|
||||
return;
|
||||
}
|
||||
|
||||
mtx_lock_spin(&pcicfg_mtx);
|
||||
port = pci_cfgenable(bus, slot, func, reg, bytes);
|
||||
if (port != 0) {
|
||||
|
@ -173,3 +228,82 @@ pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
|
|||
}
|
||||
mtx_unlock_spin(&pcicfg_mtx);
|
||||
}
|
||||
|
||||
int
|
||||
pcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus)
|
||||
{
|
||||
|
||||
if (minbus != 0)
|
||||
return (0);
|
||||
|
||||
if (bootverbose)
|
||||
printf("PCIe: Memory Mapped configuration base @ 0x%lx\n",
|
||||
base);
|
||||
|
||||
/* XXX: We should make sure this really fits into the direct map. */
|
||||
pcie_base = (vm_offset_t)pmap_mapdev(base, (maxbus + 1) << 20);
|
||||
pcie_minbus = minbus;
|
||||
pcie_maxbus = maxbus;
|
||||
cfgmech = CFGMECH_PCIE;
|
||||
return (1);
|
||||
}
|
||||
|
||||
#define PCIE_VADDR(base, reg, bus, slot, func) \
|
||||
((base) + \
|
||||
((((bus) & 0xff) << 20) | \
|
||||
(((slot) & 0x1f) << 15) | \
|
||||
(((func) & 0x7) << 12) | \
|
||||
((reg) & 0xfff)))
|
||||
|
||||
static int
|
||||
pciereg_cfgread(int bus, unsigned slot, unsigned func, unsigned reg,
|
||||
unsigned bytes)
|
||||
{
|
||||
volatile vm_offset_t va;
|
||||
int data = -1;
|
||||
|
||||
if (bus < pcie_minbus || bus > pcie_maxbus || slot >= 32 ||
|
||||
func > PCI_FUNCMAX || reg >= 0x1000)
|
||||
return (-1);
|
||||
|
||||
va = PCIE_VADDR(pcie_base, reg, bus, slot, func);
|
||||
|
||||
switch (bytes) {
|
||||
case 4:
|
||||
data = *(volatile uint32_t *)(va);
|
||||
break;
|
||||
case 2:
|
||||
data = *(volatile uint16_t *)(va);
|
||||
break;
|
||||
case 1:
|
||||
data = *(volatile uint8_t *)(va);
|
||||
break;
|
||||
}
|
||||
|
||||
return (data);
|
||||
}
|
||||
|
||||
static void
|
||||
pciereg_cfgwrite(int bus, unsigned slot, unsigned func, unsigned reg, int data,
|
||||
unsigned bytes)
|
||||
{
|
||||
volatile vm_offset_t va;
|
||||
|
||||
if (bus < pcie_minbus || bus > pcie_maxbus || slot >= 32 ||
|
||||
func > PCI_FUNCMAX || reg >= 0x1000)
|
||||
return;
|
||||
|
||||
va = PCIE_VADDR(pcie_base, reg, bus, slot, func);
|
||||
|
||||
switch (bytes) {
|
||||
case 4:
|
||||
*(volatile uint32_t *)(va) = data;
|
||||
break;
|
||||
case 2:
|
||||
*(volatile uint16_t *)(va) = data;
|
||||
break;
|
||||
case 1:
|
||||
*(volatile uint8_t *)(va) = data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,9 @@ __FBSDID("$FreeBSD$");
|
|||
#include <sys/sbuf.h>
|
||||
#include <sys/smp.h>
|
||||
|
||||
#if defined(__i386__) || defined(__amd64__)
|
||||
#include <machine/pci_cfgreg.h>
|
||||
#endif
|
||||
#include <machine/resource.h>
|
||||
#include <machine/bus.h>
|
||||
#include <sys/rman.h>
|
||||
|
@ -152,6 +155,9 @@ static int acpi_child_location_str_method(device_t acdev, device_t child,
|
|||
char *buf, size_t buflen);
|
||||
static int acpi_child_pnpinfo_str_method(device_t acdev, device_t child,
|
||||
char *buf, size_t buflen);
|
||||
#if defined(__i386__) || defined(__amd64__)
|
||||
static void acpi_enable_pcie(void);
|
||||
#endif
|
||||
|
||||
static device_method_t acpi_methods[] = {
|
||||
/* Device interface */
|
||||
|
@ -448,6 +454,11 @@ acpi_attach(device_t dev)
|
|||
goto out;
|
||||
}
|
||||
|
||||
#if defined(__i386__) || defined(__amd64__)
|
||||
/* Handle MCFG table if present. */
|
||||
acpi_enable_pcie();
|
||||
#endif
|
||||
|
||||
/* Install the default address space handlers. */
|
||||
status = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
|
||||
ACPI_ADR_SPACE_SYSTEM_MEMORY, ACPI_DEFAULT_HANDLER, NULL, NULL);
|
||||
|
@ -1466,6 +1477,36 @@ acpi_isa_pnp_probe(device_t bus, device_t child, struct isa_pnp_id *ids)
|
|||
return_VALUE (result);
|
||||
}
|
||||
|
||||
#if defined(__i386__) || defined(__amd64__)
|
||||
/*
|
||||
* Look for a MCFG table. If it is present, use the settings for
|
||||
* domain (segment) 0 to setup PCI config space access via the memory
|
||||
* map.
|
||||
*/
|
||||
static void
|
||||
acpi_enable_pcie(void)
|
||||
{
|
||||
ACPI_TABLE_HEADER *hdr;
|
||||
ACPI_MCFG_ALLOCATION *alloc, *end;
|
||||
ACPI_STATUS status;
|
||||
|
||||
status = AcpiGetTable(ACPI_SIG_MCFG, 1, &hdr);
|
||||
if (ACPI_FAILURE(status))
|
||||
return;
|
||||
|
||||
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;
|
||||
}
|
||||
alloc++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Scan all of the ACPI namespace and attach child devices.
|
||||
*
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#define CONF2_ENABLE_CHK 0x0e
|
||||
#define CONF2_ENABLE_RES 0x0e
|
||||
|
||||
int pcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus);
|
||||
int pci_cfgregopen(void);
|
||||
u_int32_t pci_cfgregread(int bus, int slot, int func, int reg, int bytes);
|
||||
void pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes);
|
||||
|
|
|
@ -75,7 +75,8 @@ enum {
|
|||
};
|
||||
|
||||
static TAILQ_HEAD(pcie_cfg_list, pcie_cfg_elem) pcie_list[MAXCPU];
|
||||
static uint64_t pciebar;
|
||||
static uint64_t pcie_base;
|
||||
static int pcie_minbus, pcie_maxbus;
|
||||
static int cfgmech;
|
||||
static int devmax;
|
||||
static struct mtx pcicfg_mtx;
|
||||
|
@ -84,13 +85,11 @@ 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);
|
||||
#ifndef XEN
|
||||
static int pcireg_cfgopen(void);
|
||||
|
||||
static int pciereg_cfgopen(void);
|
||||
#endif
|
||||
static int pciereg_cfgread(int bus, int slot, int func, int reg,
|
||||
int bytes);
|
||||
static void pciereg_cfgwrite(int bus, int slot, int func, int reg,
|
||||
int data, int bytes);
|
||||
static int pciereg_cfgread(int bus, unsigned slot, unsigned func,
|
||||
unsigned reg, unsigned bytes);
|
||||
static void pciereg_cfgwrite(int bus, unsigned slot, unsigned func,
|
||||
unsigned reg, int data, unsigned bytes);
|
||||
|
||||
/*
|
||||
* Some BIOS writers seem to want to ignore the spec and put
|
||||
|
@ -140,14 +139,15 @@ pci_cfgregopen(void)
|
|||
return (0);
|
||||
#else
|
||||
static int opened = 0;
|
||||
uint64_t pciebar;
|
||||
u_int16_t vid, did;
|
||||
u_int16_t v;
|
||||
|
||||
if (opened)
|
||||
return(1);
|
||||
return (1);
|
||||
|
||||
if (pcireg_cfgopen() == 0)
|
||||
return(0);
|
||||
if (cfgmech == CFGMECH_NONE && pcireg_cfgopen() == 0)
|
||||
return (0);
|
||||
|
||||
v = pcibios_get_version();
|
||||
if (v > 0)
|
||||
|
@ -160,6 +160,9 @@ pci_cfgregopen(void)
|
|||
if (v >= 0x0210)
|
||||
pci_pir_open();
|
||||
|
||||
if (cfgmech == CFGMECH_PCIE)
|
||||
return (1);
|
||||
|
||||
/*
|
||||
* Grope around in the PCI config space to see if this is a
|
||||
* chipset that is capable of doing memory-mapped config cycles.
|
||||
|
@ -176,22 +179,15 @@ pci_cfgregopen(void)
|
|||
case 0x3592:
|
||||
/* Intel 7520 or 7320 */
|
||||
pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16;
|
||||
pciereg_cfgopen();
|
||||
pcie_cfgregopen(pciebar, 0, 255);
|
||||
break;
|
||||
case 0x2580:
|
||||
case 0x2584:
|
||||
case 0x2590:
|
||||
/* Intel 915, 925, or 915GM */
|
||||
pciebar = pci_cfgregread(0, 0, 0, 0x48, 4);
|
||||
pciereg_cfgopen();
|
||||
pcie_cfgregopen(pciebar, 0, 255);
|
||||
break;
|
||||
case 0x25d0:
|
||||
case 0x25d4:
|
||||
case 0x25d8:
|
||||
/* Intel 5000Z/V/P */
|
||||
pciebar = pci_cfgregread(0, 16, 0, 0x64, 4) << 16;
|
||||
pciereg_cfgopen();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -504,8 +500,8 @@ pcireg_cfgopen(void)
|
|||
return (cfgmech);
|
||||
}
|
||||
|
||||
static int
|
||||
pciereg_cfgopen(void)
|
||||
int
|
||||
pcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus)
|
||||
{
|
||||
struct pcie_cfg_list *pcielist;
|
||||
struct pcie_cfg_elem *pcie_array, *elem;
|
||||
|
@ -515,20 +511,22 @@ pciereg_cfgopen(void)
|
|||
vm_offset_t va;
|
||||
int i;
|
||||
|
||||
if (minbus != 0)
|
||||
return (0);
|
||||
|
||||
#ifndef PAE
|
||||
if (pciebar >= 0x100000000) {
|
||||
if (base >= 0x100000000) {
|
||||
if (bootverbose)
|
||||
printf(
|
||||
"PCI: Memory Mapped PCI configuration area base 0x%jx too high\n",
|
||||
(uintmax_t)pciebar);
|
||||
pciebar = 0;
|
||||
(uintmax_t)base);
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (bootverbose)
|
||||
printf("Setting up PCIe mappings for BAR 0x%jx\n",
|
||||
(uintmax_t)pciebar);
|
||||
printf("PCIe: Memory Mapped configuration base @ 0x%jx\n",
|
||||
(uintmax_t)base);
|
||||
|
||||
#ifdef SMP
|
||||
SLIST_FOREACH(pc, &cpuhead, pc_allcpu)
|
||||
|
@ -560,7 +558,9 @@ pciereg_cfgopen(void)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
pcie_base = base;
|
||||
pcie_minbus = minbus;
|
||||
pcie_maxbus = maxbus;
|
||||
cfgmech = CFGMECH_PCIE;
|
||||
devmax = 32;
|
||||
return (1);
|
||||
|
@ -610,15 +610,20 @@ pciereg_findelem(vm_paddr_t papage)
|
|||
}
|
||||
|
||||
static int
|
||||
pciereg_cfgread(int bus, int slot, int func, int reg, int bytes)
|
||||
pciereg_cfgread(int bus, unsigned slot, unsigned func, unsigned reg,
|
||||
unsigned bytes)
|
||||
{
|
||||
struct pcie_cfg_elem *elem;
|
||||
volatile vm_offset_t va;
|
||||
vm_paddr_t pa, papage;
|
||||
int data;
|
||||
int data = -1;
|
||||
|
||||
if (bus < pcie_minbus || bus > pcie_maxbus || slot >= 32 ||
|
||||
func > PCI_FUNCMAX || reg >= 0x1000 || bytes > 4 || bytes == 3)
|
||||
return (-1);
|
||||
|
||||
critical_enter();
|
||||
pa = PCIE_PADDR(pciebar, reg, bus, slot, func);
|
||||
pa = PCIE_PADDR(pcie_base, reg, bus, slot, func);
|
||||
papage = pa & ~PAGE_MASK;
|
||||
elem = pciereg_findelem(papage);
|
||||
va = elem->vapage | (pa & PAGE_MASK);
|
||||
|
@ -633,8 +638,6 @@ pciereg_cfgread(int bus, int slot, int func, int reg, int bytes)
|
|||
case 1:
|
||||
data = *(volatile uint8_t *)(va);
|
||||
break;
|
||||
default:
|
||||
panic("pciereg_cfgread: invalid width");
|
||||
}
|
||||
|
||||
critical_exit();
|
||||
|
@ -642,14 +645,19 @@ pciereg_cfgread(int bus, int slot, int func, int reg, int bytes)
|
|||
}
|
||||
|
||||
static void
|
||||
pciereg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
|
||||
pciereg_cfgwrite(int bus, unsigned slot, unsigned func, unsigned reg, int data,
|
||||
unsigned bytes)
|
||||
{
|
||||
struct pcie_cfg_elem *elem;
|
||||
volatile vm_offset_t va;
|
||||
vm_paddr_t pa, papage;
|
||||
|
||||
if (bus < pcie_minbus || bus > pcie_maxbus || slot >= 32 ||
|
||||
func > PCI_FUNCMAX || reg >= 0x1000)
|
||||
return;
|
||||
|
||||
critical_enter();
|
||||
pa = PCIE_PADDR(pciebar, reg, bus, slot, func);
|
||||
pa = PCIE_PADDR(pcie_base, reg, bus, slot, func);
|
||||
papage = pa & ~PAGE_MASK;
|
||||
elem = pciereg_findelem(papage);
|
||||
va = elem->vapage | (pa & PAGE_MASK);
|
||||
|
@ -664,8 +672,6 @@ pciereg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
|
|||
case 1:
|
||||
*(volatile uint8_t *)(va) = data;
|
||||
break;
|
||||
default:
|
||||
panic("pciereg_cfgwrite: invalid width");
|
||||
}
|
||||
|
||||
critical_exit();
|
||||
|
|
Loading…
Reference in a new issue