Commit 7752d5cf authored by Robert Hancock's avatar Robert Hancock Committed by Ingo Molnar

x86: validate against acpi motherboard resources

This path adds validation of the MMCONFIG table against the ACPI reserved
motherboard resources.  If the MMCONFIG table is found to be reserved in
ACPI, we don't bother checking the E820 table.  The PCI Express firmware
spec apparently tells BIOS developers that reservation in ACPI is required
and E820 reservation is optional, so checking against ACPI first makes
sense.  Many BIOSes don't reserve the MMCONFIG region in E820 even though
it is perfectly functional, the existing check needlessly disables MMCONFIG
in these cases.

In order to do this, MMCONFIG setup has been split into two phases.  If PCI
configuration type 1 is not available then MMCONFIG is enabled early as
before.  Otherwise, it is enabled later after the ACPI interpreter is
enabled, since we need to be able to execute control methods in order to
check the ACPI reserved resources.  Presently this is just triggered off
the end of ACPI interpreter initialization.

There are a few other behavioral changes here:

- Validate all MMCONFIG configurations provided, not just the first one.

- Validate the entire required length of each configuration according to
  the provided ending bus number is reserved, not just the minimum required
  allocation.

- Validate that the area is reserved even if we read it from the chipset
  directly and not from the MCFG table.  This catches the case where the
  BIOS didn't set the location properly in the chipset and has mapped it
  over other things it shouldn't have.

This also cleans up the MMCONFIG initialization functions so that they
simply do nothing if MMCONFIG is not compiled in.

Based on an original patch by Rajesh Shah from Intel.

[akpm@linux-foundation.org: many fixes and cleanups]
Signed-off-by: default avatarRobert Hancock <hancockr@shaw.ca>
Signed-off-by: default avatarAndi Kleen <ak@suse.de>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Greg KH <greg@kroah.com>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Tested-by: default avatarAndi Kleen <ak@suse.de>
Cc: Rajesh Shah <rajesh.shah@intel.com>
Cc: Jesse Barnes <jbarnes@virtuousgeek.org>
Acked-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
Cc: Andi Kleen <ak@suse.de>
Cc: Greg KH <greg@kroah.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent c3bf9bc2
...@@ -11,9 +11,7 @@ static __init int pci_access_init(void) ...@@ -11,9 +11,7 @@ static __init int pci_access_init(void)
#ifdef CONFIG_PCI_DIRECT #ifdef CONFIG_PCI_DIRECT
type = pci_direct_probe(); type = pci_direct_probe();
#endif #endif
#ifdef CONFIG_PCI_MMCONFIG pci_mmcfg_early_init(type);
pci_mmcfg_init(type);
#endif
if (raw_pci_ops) if (raw_pci_ops)
return 0; return 0;
#ifdef CONFIG_PCI_BIOS #ifdef CONFIG_PCI_BIOS
......
...@@ -173,9 +173,78 @@ static void __init pci_mmcfg_insert_resources(unsigned long resource_flags) ...@@ -173,9 +173,78 @@ static void __init pci_mmcfg_insert_resources(unsigned long resource_flags)
pci_mmcfg_resources_inserted = 1; pci_mmcfg_resources_inserted = 1;
} }
static void __init pci_mmcfg_reject_broken(int type) static acpi_status __init check_mcfg_resource(struct acpi_resource *res,
void *data)
{
struct resource *mcfg_res = data;
struct acpi_resource_address64 address;
acpi_status status;
if (res->type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) {
struct acpi_resource_fixed_memory32 *fixmem32 =
&res->data.fixed_memory32;
if (!fixmem32)
return AE_OK;
if ((mcfg_res->start >= fixmem32->address) &&
(mcfg_res->end < (fixmem32->address +
fixmem32->address_length))) {
mcfg_res->flags = 1;
return AE_CTRL_TERMINATE;
}
}
if ((res->type != ACPI_RESOURCE_TYPE_ADDRESS32) &&
(res->type != ACPI_RESOURCE_TYPE_ADDRESS64))
return AE_OK;
status = acpi_resource_to_address64(res, &address);
if (ACPI_FAILURE(status) ||
(address.address_length <= 0) ||
(address.resource_type != ACPI_MEMORY_RANGE))
return AE_OK;
if ((mcfg_res->start >= address.minimum) &&
(mcfg_res->end < (address.minimum + address.address_length))) {
mcfg_res->flags = 1;
return AE_CTRL_TERMINATE;
}
return AE_OK;
}
static acpi_status __init find_mboard_resource(acpi_handle handle, u32 lvl,
void *context, void **rv)
{
struct resource *mcfg_res = context;
acpi_walk_resources(handle, METHOD_NAME__CRS,
check_mcfg_resource, context);
if (mcfg_res->flags)
return AE_CTRL_TERMINATE;
return AE_OK;
}
static int __init is_acpi_reserved(unsigned long start, unsigned long end)
{
struct resource mcfg_res;
mcfg_res.start = start;
mcfg_res.end = end;
mcfg_res.flags = 0;
acpi_get_devices("PNP0C01", find_mboard_resource, &mcfg_res, NULL);
if (!mcfg_res.flags)
acpi_get_devices("PNP0C02", find_mboard_resource, &mcfg_res,
NULL);
return mcfg_res.flags;
}
static void __init pci_mmcfg_reject_broken(void)
{ {
typeof(pci_mmcfg_config[0]) *cfg; typeof(pci_mmcfg_config[0]) *cfg;
int i;
if ((pci_mmcfg_config_num == 0) || if ((pci_mmcfg_config_num == 0) ||
(pci_mmcfg_config == NULL) || (pci_mmcfg_config == NULL) ||
...@@ -196,17 +265,37 @@ static void __init pci_mmcfg_reject_broken(int type) ...@@ -196,17 +265,37 @@ static void __init pci_mmcfg_reject_broken(int type)
goto reject; goto reject;
} }
/* for (i = 0; i < pci_mmcfg_config_num; i++) {
* Only do this check when type 1 works. If it doesn't work u32 size = (cfg->end_bus_number + 1) << 20;
* assume we run on a Mac and always use MCFG cfg = &pci_mmcfg_config[i];
*/ printk(KERN_NOTICE "PCI: MCFG configuration %d: base %lu "
if (type == 1 && !e820_all_mapped(cfg->address, "segment %hu buses %u - %u\n",
cfg->address + MMCONFIG_APER_MIN, i, (unsigned long)cfg->address, cfg->pci_segment,
E820_RESERVED)) { (unsigned int)cfg->start_bus_number,
printk(KERN_ERR "PCI: BIOS Bug: MCFG area at %Lx is not" (unsigned int)cfg->end_bus_number);
" E820-reserved\n", cfg->address); if (is_acpi_reserved(cfg->address, cfg->address + size - 1)) {
goto reject; printk(KERN_NOTICE "PCI: MCFG area at %Lx reserved "
"in ACPI motherboard resources\n",
cfg->address);
} else {
printk(KERN_ERR "PCI: BIOS Bug: MCFG area at %Lx is not"
" reserved in ACPI motherboard resources\n",
cfg->address);
/* Don't try to do this check unless configuration
type 1 is available. */
if ((pci_probe & PCI_PROBE_CONF1) &&
e820_all_mapped(cfg->address,
cfg->address + size - 1,
E820_RESERVED))
printk(KERN_NOTICE
"PCI: MCFG area at %Lx reserved in "
"E820\n",
cfg->address);
else
goto reject;
}
} }
return; return;
reject: reject:
...@@ -216,20 +305,46 @@ static void __init pci_mmcfg_reject_broken(int type) ...@@ -216,20 +305,46 @@ static void __init pci_mmcfg_reject_broken(int type)
pci_mmcfg_config_num = 0; pci_mmcfg_config_num = 0;
} }
void __init pci_mmcfg_init(int type) void __init pci_mmcfg_early_init(int type)
{
if ((pci_probe & PCI_PROBE_MMCONF) == 0)
return;
/* If type 1 access is available, no need to enable MMCONFIG yet, we can
defer until later when the ACPI interpreter is available to better
validate things. */
if (type == 1)
return;
acpi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg);
if ((pci_mmcfg_config_num == 0) ||
(pci_mmcfg_config == NULL) ||
(pci_mmcfg_config[0].address == 0))
return;
if (pci_mmcfg_arch_init())
pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF;
}
void __init pci_mmcfg_late_init(void)
{ {
int known_bridge = 0; int known_bridge = 0;
/* MMCONFIG disabled */
if ((pci_probe & PCI_PROBE_MMCONF) == 0) if ((pci_probe & PCI_PROBE_MMCONF) == 0)
return; return;
if (type == 1 && pci_mmcfg_check_hostbridge()) /* MMCONFIG already enabled */
known_bridge = 1; if (!(pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF))
return;
if (!known_bridge) { if ((pci_probe & PCI_PROBE_CONF1) && pci_mmcfg_check_hostbridge())
known_bridge = 1;
else
acpi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg); acpi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg);
pci_mmcfg_reject_broken(type);
} pci_mmcfg_reject_broken();
if ((pci_mmcfg_config_num == 0) || if ((pci_mmcfg_config_num == 0) ||
(pci_mmcfg_config == NULL) || (pci_mmcfg_config == NULL) ||
......
...@@ -97,7 +97,6 @@ extern struct pci_raw_ops pci_direct_conf1; ...@@ -97,7 +97,6 @@ extern struct pci_raw_ops pci_direct_conf1;
extern int pci_direct_probe(void); extern int pci_direct_probe(void);
extern void pci_direct_init(int type); extern void pci_direct_init(int type);
extern void pci_pcbios_init(void); extern void pci_pcbios_init(void);
extern void pci_mmcfg_init(int type);
/* pci-mmconfig.c */ /* pci-mmconfig.c */
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#ifdef CONFIG_X86 #ifdef CONFIG_X86
#include <asm/mpspec.h> #include <asm/mpspec.h>
#endif #endif
#include <linux/pci.h>
#include <acpi/acpi_bus.h> #include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h> #include <acpi/acpi_drivers.h>
...@@ -784,6 +785,7 @@ static int __init acpi_init(void) ...@@ -784,6 +785,7 @@ static int __init acpi_init(void)
result = acpi_bus_init(); result = acpi_bus_init();
if (!result) { if (!result) {
pci_mmcfg_late_init();
if (!(pm_flags & PM_APM)) if (!(pm_flags & PM_APM))
pm_flags |= PM_ACPI; pm_flags |= PM_ACPI;
else { else {
......
...@@ -1053,5 +1053,13 @@ extern unsigned long pci_cardbus_mem_size; ...@@ -1053,5 +1053,13 @@ extern unsigned long pci_cardbus_mem_size;
extern int pcibios_add_platform_entries(struct pci_dev *dev); extern int pcibios_add_platform_entries(struct pci_dev *dev);
#ifdef CONFIG_PCI_MMCONFIG
extern void __init pci_mmcfg_early_init(int type);
extern void __init pci_mmcfg_late_init(void);
#else
static inline void pci_mmcfg_early_init(int type) { }
static inline void pci_mmcfg_late_init(void) { }
#endif
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* LINUX_PCI_H */ #endif /* LINUX_PCI_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment