aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobert Hancock <hancockr@shaw.ca>2008-02-15 04:27:20 -0500
committerIngo Molnar <mingo@elte.hu>2008-04-26 17:41:03 -0400
commit7752d5cfe3d11ca0bb9c673ec38bd78ba6578f8e (patch)
tree280c5accec5ac0d9821ef9d7e3e095f9df87f8c5
parentc3bf9bc243092c53946fd6d8ebd6dc2f4e572d48 (diff)
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: Robert Hancock <hancockr@shaw.ca> Signed-off-by: Andi Kleen <ak@suse.de> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Greg KH <greg@kroah.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Tested-by: Andi Kleen <ak@suse.de> Cc: Rajesh Shah <rajesh.shah@intel.com> Cc: Jesse Barnes <jbarnes@virtuousgeek.org> Acked-by: Linus Torvalds <torvalds@linux-foundation.org> Cc: Andi Kleen <ak@suse.de> Cc: Greg KH <greg@kroah.com> Signed-off-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r--arch/x86/pci/init.c4
-rw-r--r--arch/x86/pci/mmconfig-shared.c149
-rw-r--r--arch/x86/pci/pci.h1
-rw-r--r--drivers/acpi/bus.c2
-rw-r--r--include/linux/pci.h8
5 files changed, 143 insertions, 21 deletions
diff --git a/arch/x86/pci/init.c b/arch/x86/pci/init.c
index 3de9f9ba2da6..2080b04b3bcc 100644
--- a/arch/x86/pci/init.c
+++ b/arch/x86/pci/init.c
@@ -11,9 +11,7 @@ static __init int pci_access_init(void)
11#ifdef CONFIG_PCI_DIRECT 11#ifdef CONFIG_PCI_DIRECT
12 type = pci_direct_probe(); 12 type = pci_direct_probe();
13#endif 13#endif
14#ifdef CONFIG_PCI_MMCONFIG 14 pci_mmcfg_early_init(type);
15 pci_mmcfg_init(type);
16#endif
17 if (raw_pci_ops) 15 if (raw_pci_ops)
18 return 0; 16 return 0;
19#ifdef CONFIG_PCI_BIOS 17#ifdef CONFIG_PCI_BIOS
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c
index 8d54df4dfaad..498e35ee428e 100644
--- a/arch/x86/pci/mmconfig-shared.c
+++ b/arch/x86/pci/mmconfig-shared.c
@@ -173,9 +173,78 @@ static void __init pci_mmcfg_insert_resources(unsigned long resource_flags)
173 pci_mmcfg_resources_inserted = 1; 173 pci_mmcfg_resources_inserted = 1;
174} 174}
175 175
176static void __init pci_mmcfg_reject_broken(int type) 176static acpi_status __init check_mcfg_resource(struct acpi_resource *res,
177 void *data)
178{
179 struct resource *mcfg_res = data;
180 struct acpi_resource_address64 address;
181 acpi_status status;
182
183 if (res->type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) {
184 struct acpi_resource_fixed_memory32 *fixmem32 =
185 &res->data.fixed_memory32;
186 if (!fixmem32)
187 return AE_OK;
188 if ((mcfg_res->start >= fixmem32->address) &&
189 (mcfg_res->end < (fixmem32->address +
190 fixmem32->address_length))) {
191 mcfg_res->flags = 1;
192 return AE_CTRL_TERMINATE;
193 }
194 }
195 if ((res->type != ACPI_RESOURCE_TYPE_ADDRESS32) &&
196 (res->type != ACPI_RESOURCE_TYPE_ADDRESS64))
197 return AE_OK;
198
199 status = acpi_resource_to_address64(res, &address);
200 if (ACPI_FAILURE(status) ||
201 (address.address_length <= 0) ||
202 (address.resource_type != ACPI_MEMORY_RANGE))
203 return AE_OK;
204
205 if ((mcfg_res->start >= address.minimum) &&
206 (mcfg_res->end < (address.minimum + address.address_length))) {
207 mcfg_res->flags = 1;
208 return AE_CTRL_TERMINATE;
209 }
210 return AE_OK;
211}
212
213static acpi_status __init find_mboard_resource(acpi_handle handle, u32 lvl,
214 void *context, void **rv)
215{
216 struct resource *mcfg_res = context;
217
218 acpi_walk_resources(handle, METHOD_NAME__CRS,
219 check_mcfg_resource, context);
220
221 if (mcfg_res->flags)
222 return AE_CTRL_TERMINATE;
223
224 return AE_OK;
225}
226
227static int __init is_acpi_reserved(unsigned long start, unsigned long end)
228{
229 struct resource mcfg_res;
230
231 mcfg_res.start = start;
232 mcfg_res.end = end;
233 mcfg_res.flags = 0;
234
235 acpi_get_devices("PNP0C01", find_mboard_resource, &mcfg_res, NULL);
236
237 if (!mcfg_res.flags)
238 acpi_get_devices("PNP0C02", find_mboard_resource, &mcfg_res,
239 NULL);
240
241 return mcfg_res.flags;
242}
243
244static void __init pci_mmcfg_reject_broken(void)
177{ 245{
178 typeof(pci_mmcfg_config[0]) *cfg; 246 typeof(pci_mmcfg_config[0]) *cfg;
247 int i;
179 248
180 if ((pci_mmcfg_config_num == 0) || 249 if ((pci_mmcfg_config_num == 0) ||
181 (pci_mmcfg_config == NULL) || 250 (pci_mmcfg_config == NULL) ||
@@ -196,17 +265,37 @@ static void __init pci_mmcfg_reject_broken(int type)
196 goto reject; 265 goto reject;
197 } 266 }
198 267
199 /* 268 for (i = 0; i < pci_mmcfg_config_num; i++) {
200 * Only do this check when type 1 works. If it doesn't work 269 u32 size = (cfg->end_bus_number + 1) << 20;
201 * assume we run on a Mac and always use MCFG 270 cfg = &pci_mmcfg_config[i];
202 */ 271 printk(KERN_NOTICE "PCI: MCFG configuration %d: base %lu "
203 if (type == 1 && !e820_all_mapped(cfg->address, 272 "segment %hu buses %u - %u\n",
204 cfg->address + MMCONFIG_APER_MIN, 273 i, (unsigned long)cfg->address, cfg->pci_segment,
205 E820_RESERVED)) { 274 (unsigned int)cfg->start_bus_number,
206 printk(KERN_ERR "PCI: BIOS Bug: MCFG area at %Lx is not" 275 (unsigned int)cfg->end_bus_number);
207 " E820-reserved\n", cfg->address); 276 if (is_acpi_reserved(cfg->address, cfg->address + size - 1)) {
208 goto reject; 277 printk(KERN_NOTICE "PCI: MCFG area at %Lx reserved "
278 "in ACPI motherboard resources\n",
279 cfg->address);
280 } else {
281 printk(KERN_ERR "PCI: BIOS Bug: MCFG area at %Lx is not"
282 " reserved in ACPI motherboard resources\n",
283 cfg->address);
284 /* Don't try to do this check unless configuration
285 type 1 is available. */
286 if ((pci_probe & PCI_PROBE_CONF1) &&
287 e820_all_mapped(cfg->address,
288 cfg->address + size - 1,
289 E820_RESERVED))
290 printk(KERN_NOTICE
291 "PCI: MCFG area at %Lx reserved in "
292 "E820\n",
293 cfg->address);
294 else
295 goto reject;
296 }
209 } 297 }
298
210 return; 299 return;
211 300
212reject: 301reject:
@@ -216,20 +305,46 @@ reject:
216 pci_mmcfg_config_num = 0; 305 pci_mmcfg_config_num = 0;
217} 306}
218 307
219void __init pci_mmcfg_init(int type) 308void __init pci_mmcfg_early_init(int type)
309{
310 if ((pci_probe & PCI_PROBE_MMCONF) == 0)
311 return;
312
313 /* If type 1 access is available, no need to enable MMCONFIG yet, we can
314 defer until later when the ACPI interpreter is available to better
315 validate things. */
316 if (type == 1)
317 return;
318
319 acpi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg);
320
321 if ((pci_mmcfg_config_num == 0) ||
322 (pci_mmcfg_config == NULL) ||
323 (pci_mmcfg_config[0].address == 0))
324 return;
325
326 if (pci_mmcfg_arch_init())
327 pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF;
328}
329
330void __init pci_mmcfg_late_init(void)
220{ 331{
221 int known_bridge = 0; 332 int known_bridge = 0;
222 333
334 /* MMCONFIG disabled */
223 if ((pci_probe & PCI_PROBE_MMCONF) == 0) 335 if ((pci_probe & PCI_PROBE_MMCONF) == 0)
224 return; 336 return;
225 337
226 if (type == 1 && pci_mmcfg_check_hostbridge()) 338 /* MMCONFIG already enabled */
227 known_bridge = 1; 339 if (!(pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF))
340 return;
228 341
229 if (!known_bridge) { 342 if ((pci_probe & PCI_PROBE_CONF1) && pci_mmcfg_check_hostbridge())
343 known_bridge = 1;
344 else
230 acpi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg); 345 acpi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg);
231 pci_mmcfg_reject_broken(type); 346
232 } 347 pci_mmcfg_reject_broken();
233 348
234 if ((pci_mmcfg_config_num == 0) || 349 if ((pci_mmcfg_config_num == 0) ||
235 (pci_mmcfg_config == NULL) || 350 (pci_mmcfg_config == NULL) ||
diff --git a/arch/x86/pci/pci.h b/arch/x86/pci/pci.h
index c4bddaeff619..28b9b72ce7c7 100644
--- a/arch/x86/pci/pci.h
+++ b/arch/x86/pci/pci.h
@@ -97,7 +97,6 @@ extern struct pci_raw_ops pci_direct_conf1;
97extern int pci_direct_probe(void); 97extern int pci_direct_probe(void);
98extern void pci_direct_init(int type); 98extern void pci_direct_init(int type);
99extern void pci_pcbios_init(void); 99extern void pci_pcbios_init(void);
100extern void pci_mmcfg_init(int type);
101 100
102/* pci-mmconfig.c */ 101/* pci-mmconfig.c */
103 102
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 2d1955c11833..a6dbcf4d9ef5 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -35,6 +35,7 @@
35#ifdef CONFIG_X86 35#ifdef CONFIG_X86
36#include <asm/mpspec.h> 36#include <asm/mpspec.h>
37#endif 37#endif
38#include <linux/pci.h>
38#include <acpi/acpi_bus.h> 39#include <acpi/acpi_bus.h>
39#include <acpi/acpi_drivers.h> 40#include <acpi/acpi_drivers.h>
40 41
@@ -784,6 +785,7 @@ static int __init acpi_init(void)
784 result = acpi_bus_init(); 785 result = acpi_bus_init();
785 786
786 if (!result) { 787 if (!result) {
788 pci_mmcfg_late_init();
787 if (!(pm_flags & PM_APM)) 789 if (!(pm_flags & PM_APM))
788 pm_flags |= PM_ACPI; 790 pm_flags |= PM_ACPI;
789 else { 791 else {
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 292491324b01..43a4f9cae67d 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1053,5 +1053,13 @@ extern unsigned long pci_cardbus_mem_size;
1053 1053
1054extern int pcibios_add_platform_entries(struct pci_dev *dev); 1054extern int pcibios_add_platform_entries(struct pci_dev *dev);
1055 1055
1056#ifdef CONFIG_PCI_MMCONFIG
1057extern void __init pci_mmcfg_early_init(int type);
1058extern void __init pci_mmcfg_late_init(void);
1059#else
1060static inline void pci_mmcfg_early_init(int type) { }
1061static inline void pci_mmcfg_late_init(void) { }
1062#endif
1063
1056#endif /* __KERNEL__ */ 1064#endif /* __KERNEL__ */
1057#endif /* LINUX_PCI_H */ 1065#endif /* LINUX_PCI_H */