pci_cfgreg.c: Use io port config access for early boot time.
Some early PCIe chipsets are explicitly listed in the white-list to enable use of the MMIO config space accesses, perhaps because ACPI tables were not reliable source of the base MCFG address at that time. For that chipsets, MCFG base was read from the known chipset MCFGbase config register. During very early stage of boot, when access to the PCI config space is performed (see e.g. pci_early_quirks.c), we cannot map 255MB of registers because the method used with pre-boot pmap overflows initial kernel page tables. Move fallback to read MCFGbase to the attachment method of the x86/legacy device, which removes code duplication, and results in the use of io accesses until MCFG is parsed or legacy attach called. For amd64, pre-initialize cfgmech with CFGMECH_1, right now we dynamically assign CFGMECH_1 to it anyway, and remove checks for CFGMECH_NONE. There is a mention in the Intel documentation for corresponding chipsets that OS must use either io port or MMIO access method, but we already break this rule by reading MCFGbase register, so one more access seems to be innocent. Reported by: longwitz@incore.de PR: 236838 Reviewed by: avg (other version), jhb Sponsored by: The FreeBSD Foundation MFC after: 1 week Differential revision: https://reviews.freebsd.org/D19833
This commit is contained in:
parent
c9c9de9366
commit
2a508645b4
@ -44,12 +44,6 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <vm/pmap.h>
|
#include <vm/pmap.h>
|
||||||
#include <machine/pci_cfgreg.h>
|
#include <machine/pci_cfgreg.h>
|
||||||
|
|
||||||
enum {
|
|
||||||
CFGMECH_NONE = 0,
|
|
||||||
CFGMECH_1,
|
|
||||||
CFGMECH_PCIE,
|
|
||||||
};
|
|
||||||
|
|
||||||
static uint32_t pci_docfgregread(int bus, int slot, int func, int reg,
|
static uint32_t pci_docfgregread(int bus, int slot, int func, int reg,
|
||||||
int bytes);
|
int bytes);
|
||||||
static int pciereg_cfgread(int bus, unsigned slot, unsigned func,
|
static int pciereg_cfgread(int bus, unsigned slot, unsigned func,
|
||||||
@ -61,7 +55,13 @@ static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int
|
|||||||
|
|
||||||
SYSCTL_DECL(_hw_pci);
|
SYSCTL_DECL(_hw_pci);
|
||||||
|
|
||||||
static int cfgmech;
|
/*
|
||||||
|
* For amd64 we assume that type 1 I/O port-based access always works.
|
||||||
|
* If an ACPI MCFG table exists, pcie_cfgregopen() will be called to
|
||||||
|
* switch to memory-mapped access.
|
||||||
|
*/
|
||||||
|
int cfgmech = CFGMECH_1;
|
||||||
|
|
||||||
static vm_offset_t pcie_base;
|
static vm_offset_t pcie_base;
|
||||||
static int pcie_minbus, pcie_maxbus;
|
static int pcie_minbus, pcie_maxbus;
|
||||||
static uint32_t pcie_badslots;
|
static uint32_t pcie_badslots;
|
||||||
@ -71,46 +71,9 @@ static int mcfg_enable = 1;
|
|||||||
SYSCTL_INT(_hw_pci, OID_AUTO, mcfg, CTLFLAG_RDTUN, &mcfg_enable, 0,
|
SYSCTL_INT(_hw_pci, OID_AUTO, mcfg, CTLFLAG_RDTUN, &mcfg_enable, 0,
|
||||||
"Enable support for PCI-e memory mapped config access");
|
"Enable support for PCI-e memory mapped config access");
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialise access to PCI configuration space
|
|
||||||
*/
|
|
||||||
int
|
int
|
||||||
pci_cfgregopen(void)
|
pci_cfgregopen(void)
|
||||||
{
|
{
|
||||||
uint64_t pciebar;
|
|
||||||
uint16_t did, vid;
|
|
||||||
|
|
||||||
if (cfgmech != CFGMECH_NONE)
|
|
||||||
return (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);
|
return (1);
|
||||||
}
|
}
|
||||||
@ -135,9 +98,6 @@ pci_cfgregread(int bus, int slot, int func, int reg, int bytes)
|
|||||||
{
|
{
|
||||||
uint32_t line;
|
uint32_t line;
|
||||||
|
|
||||||
if (cfgmech == CFGMECH_NONE)
|
|
||||||
return (0xffffffff);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Some BIOS writers seem to want to ignore the spec and put
|
* Some BIOS writers seem to want to ignore the spec and put
|
||||||
* 0 in the intline rather than 255 to indicate none. Some use
|
* 0 in the intline rather than 255 to indicate none. Some use
|
||||||
@ -162,9 +122,6 @@ void
|
|||||||
pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
|
pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (cfgmech == CFGMECH_NONE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (cfgmech == CFGMECH_PCIE &&
|
if (cfgmech == CFGMECH_PCIE &&
|
||||||
(bus >= pcie_minbus && bus <= pcie_maxbus) &&
|
(bus >= pcie_minbus && bus <= pcie_maxbus) &&
|
||||||
(bus != 0 || !(1 << slot & pcie_badslots)))
|
(bus != 0 || !(1 << slot & pcie_badslots)))
|
||||||
|
@ -64,20 +64,13 @@ struct pcie_cfg_elem {
|
|||||||
vm_paddr_t papage;
|
vm_paddr_t papage;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
|
||||||
CFGMECH_NONE = 0,
|
|
||||||
CFGMECH_1,
|
|
||||||
CFGMECH_2,
|
|
||||||
CFGMECH_PCIE,
|
|
||||||
};
|
|
||||||
|
|
||||||
SYSCTL_DECL(_hw_pci);
|
SYSCTL_DECL(_hw_pci);
|
||||||
|
|
||||||
static TAILQ_HEAD(pcie_cfg_list, pcie_cfg_elem) pcie_list[MAXCPU];
|
static TAILQ_HEAD(pcie_cfg_list, pcie_cfg_elem) pcie_list[MAXCPU];
|
||||||
static uint64_t pcie_base;
|
static uint64_t pcie_base;
|
||||||
static int pcie_minbus, pcie_maxbus;
|
static int pcie_minbus, pcie_maxbus;
|
||||||
static uint32_t pcie_badslots;
|
static uint32_t pcie_badslots;
|
||||||
static int cfgmech;
|
int cfgmech;
|
||||||
static int devmax;
|
static int devmax;
|
||||||
static struct mtx pcicfg_mtx;
|
static struct mtx pcicfg_mtx;
|
||||||
static int mcfg_enable = 1;
|
static int mcfg_enable = 1;
|
||||||
@ -136,10 +129,8 @@ pcibios_get_version(void)
|
|||||||
int
|
int
|
||||||
pci_cfgregopen(void)
|
pci_cfgregopen(void)
|
||||||
{
|
{
|
||||||
static int opened = 0;
|
uint16_t v;
|
||||||
uint64_t pciebar;
|
static int opened = 0;
|
||||||
u_int16_t vid, did;
|
|
||||||
u_int16_t v;
|
|
||||||
|
|
||||||
if (opened)
|
if (opened)
|
||||||
return (1);
|
return (1);
|
||||||
@ -158,38 +149,7 @@ pci_cfgregopen(void)
|
|||||||
if (v >= 0x0210)
|
if (v >= 0x0210)
|
||||||
pci_pir_open();
|
pci_pir_open();
|
||||||
|
|
||||||
if (cfgmech == CFGMECH_PCIE)
|
return (1);
|
||||||
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.
|
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t
|
static uint32_t
|
||||||
|
@ -48,6 +48,15 @@
|
|||||||
#define CONF2_ENABLE_CHK 0x0e
|
#define CONF2_ENABLE_CHK 0x0e
|
||||||
#define CONF2_ENABLE_RES 0x0e
|
#define CONF2_ENABLE_RES 0x0e
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CFGMECH_NONE = 0,
|
||||||
|
CFGMECH_1,
|
||||||
|
CFGMECH_2,
|
||||||
|
CFGMECH_PCIE,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern int cfgmech;
|
||||||
|
|
||||||
rman_res_t hostb_alloc_start(int type, rman_res_t start, rman_res_t end, rman_res_t count);
|
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, uint8_t minbus, uint8_t maxbus);
|
||||||
int pci_cfgregopen(void);
|
int pci_cfgregopen(void);
|
||||||
|
@ -46,8 +46,10 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <sys/pcpu.h>
|
#include <sys/pcpu.h>
|
||||||
#include <sys/rman.h>
|
#include <sys/rman.h>
|
||||||
#include <sys/smp.h>
|
#include <sys/smp.h>
|
||||||
|
#include <dev/pci/pcireg.h>
|
||||||
|
|
||||||
#include <machine/clock.h>
|
#include <machine/clock.h>
|
||||||
|
#include <machine/pci_cfgreg.h>
|
||||||
#include <machine/resource.h>
|
#include <machine/resource.h>
|
||||||
#include <x86/legacyvar.h>
|
#include <x86/legacyvar.h>
|
||||||
|
|
||||||
@ -111,11 +113,53 @@ legacy_probe(device_t dev)
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
legacy_pci_cfgregopen(device_t dev)
|
||||||
|
{
|
||||||
|
uint64_t pciebar;
|
||||||
|
u_int16_t did, vid;
|
||||||
|
|
||||||
|
if (cfgmech == CFGMECH_NONE || cfgmech == CFGMECH_PCIE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bootverbose && cfgmech == CFGMECH_PCIE)
|
||||||
|
device_printf(dev, "Enabled ECAM PCIe accesses\n");
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
legacy_attach(device_t dev)
|
legacy_attach(device_t dev)
|
||||||
{
|
{
|
||||||
device_t child;
|
device_t child;
|
||||||
|
|
||||||
|
legacy_pci_cfgregopen(dev);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Let our child drivers identify any child devices that they
|
* Let our child drivers identify any child devices that they
|
||||||
* can find. Once that is done attach any devices that we
|
* can find. Once that is done attach any devices that we
|
||||||
|
Loading…
Reference in New Issue
Block a user