x86: fix HPET regression in 2.6.26 versus 2.6.25, check hpet against BAR, v3

David Witbrodt tracked down (and bisected) a hpet bootup hang on his
system to the following problem: a BIOS bug made the hpet device
visible as a generic PCI device. If e820 reserved entries happen to
be registered first in the resource tree [which v2.6.26 started doing],
then the PCI code will reallocate that device's BAR to some other
address - breaking timer IRQs and hanging the system.

( Normally hpet devices are hidden by the BIOS from the OS's PCI
discovery via chipset magic. Sometimes the hpet is not a PCI device
at all. )

Solve this fundamental fragility by making non-PCI platform drivers
insert resources into the resource tree even if it overlaps the e820
reserved entry, to keep the resource manager from updating the BAR.

Also do these checks for the ioapic and mmconfig addresses, and emit
a warning if this happens.

Bisected-by: David Witbrodt <dawitbro@sbcglobal.net>
Signed-off-by: Yinghai Lu <yhlu.kernel@gmail.com>
Tested-by: David Witbrodt <dawitbro@sbcglobal.net>
Signed-off-by: Ingo Molnar <mingo@elte.hu>

authored by Yinghai Lu and committed by Ingo Molnar a2bd7274 060700b5

+78
+78
arch/x86/pci/i386.c
··· 31 31 #include <linux/ioport.h> 32 32 #include <linux/errno.h> 33 33 #include <linux/bootmem.h> 34 + #include <linux/acpi.h> 34 35 35 36 #include <asm/pat.h> 37 + #include <asm/hpet.h> 38 + #include <asm/io_apic.h> 36 39 37 40 #include "pci.h" 38 41 ··· 80 77 } 81 78 EXPORT_SYMBOL(pcibios_align_resource); 82 79 80 + static int check_res_with_valid(struct pci_dev *dev, struct resource *res) 81 + { 82 + unsigned long base; 83 + unsigned long size; 84 + int i; 85 + 86 + base = res->start; 87 + size = (res->start == 0 && res->end == res->start) ? 0 : 88 + (res->end - res->start + 1); 89 + 90 + if (!base || !size) 91 + return 0; 92 + 93 + #ifdef CONFIG_HPET_TIMER 94 + /* for hpet */ 95 + if (base == hpet_address && (res->flags & IORESOURCE_MEM)) { 96 + dev_info(&dev->dev, "BAR has HPET at %08lx-%08lx\n", 97 + base, base + size - 1); 98 + return 1; 99 + } 100 + #endif 101 + 102 + #ifdef CONFIG_X86_IO_APIC 103 + for (i = 0; i < nr_ioapics; i++) { 104 + unsigned long ioapic_phys = mp_ioapics[i].mp_apicaddr; 105 + 106 + if (base == ioapic_phys && (res->flags & IORESOURCE_MEM)) { 107 + dev_info(&dev->dev, "BAR has ioapic at %08lx-%08lx\n", 108 + base, base + size - 1); 109 + return 1; 110 + } 111 + } 112 + #endif 113 + 114 + #ifdef CONFIG_PCI_MMCONFIG 115 + for (i = 0; i < pci_mmcfg_config_num; i++) { 116 + unsigned long addr; 117 + 118 + addr = pci_mmcfg_config[i].address; 119 + if (base == addr && (res->flags & IORESOURCE_MEM)) { 120 + dev_info(&dev->dev, "BAR has MMCONFIG at %08lx-%08lx\n", 121 + base, base + size - 1); 122 + return 1; 123 + } 124 + } 125 + #endif 126 + 127 + return 0; 128 + } 129 + 130 + static int check_platform(struct pci_dev *dev, struct resource *res) 131 + { 132 + struct resource *root = NULL; 133 + 134 + /* 135 + * forcibly insert it into the 136 + * resource tree 137 + */ 138 + if (res->flags & IORESOURCE_MEM) 139 + root = &iomem_resource; 140 + else if (res->flags & IORESOURCE_IO) 141 + root = &ioport_resource; 142 + 143 + if (root && check_res_with_valid(dev, res)) { 144 + insert_resource(root, res); 145 + 146 + return 1; 147 + } 148 + 149 + return 0; 150 + } 83 151 /* 84 152 * Handle resources of PCI devices. If the world were perfect, we could 85 153 * just allocate all the resource regions and do nothing more. It isn't. ··· 202 128 pr = pci_find_parent_resource(dev, r); 203 129 if (!r->start || !pr || 204 130 request_resource(pr, r) < 0) { 131 + if (check_platform(dev, r)) 132 + continue; 205 133 dev_err(&dev->dev, "BAR %d: can't " 206 134 "allocate resource\n", idx); 207 135 /* ··· 247 171 r->flags, disabled, pass); 248 172 pr = pci_find_parent_resource(dev, r); 249 173 if (!pr || request_resource(pr, r) < 0) { 174 + if (check_platform(dev, r)) 175 + continue; 250 176 dev_err(&dev->dev, "BAR %d: can't " 251 177 "allocate resource\n", idx); 252 178 /* We'll assign a new address later */