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 #include <linux/ioport.h> 32 #include <linux/errno.h> 33 #include <linux/bootmem.h> 34 35 #include <asm/pat.h> 36 37 #include "pci.h" 38 ··· 80 } 81 EXPORT_SYMBOL(pcibios_align_resource); 82 83 /* 84 * Handle resources of PCI devices. If the world were perfect, we could 85 * just allocate all the resource regions and do nothing more. It isn't. ··· 202 pr = pci_find_parent_resource(dev, r); 203 if (!r->start || !pr || 204 request_resource(pr, r) < 0) { 205 dev_err(&dev->dev, "BAR %d: can't " 206 "allocate resource\n", idx); 207 /* ··· 247 r->flags, disabled, pass); 248 pr = pci_find_parent_resource(dev, r); 249 if (!pr || request_resource(pr, r) < 0) { 250 dev_err(&dev->dev, "BAR %d: can't " 251 "allocate resource\n", idx); 252 /* We'll assign a new address later */
··· 31 #include <linux/ioport.h> 32 #include <linux/errno.h> 33 #include <linux/bootmem.h> 34 + #include <linux/acpi.h> 35 36 #include <asm/pat.h> 37 + #include <asm/hpet.h> 38 + #include <asm/io_apic.h> 39 40 #include "pci.h" 41 ··· 77 } 78 EXPORT_SYMBOL(pcibios_align_resource); 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 + } 151 /* 152 * Handle resources of PCI devices. If the world were perfect, we could 153 * just allocate all the resource regions and do nothing more. It isn't. ··· 128 pr = pci_find_parent_resource(dev, r); 129 if (!r->start || !pr || 130 request_resource(pr, r) < 0) { 131 + if (check_platform(dev, r)) 132 + continue; 133 dev_err(&dev->dev, "BAR %d: can't " 134 "allocate resource\n", idx); 135 /* ··· 171 r->flags, disabled, pass); 172 pr = pci_find_parent_resource(dev, r); 173 if (!pr || request_resource(pr, r) < 0) { 174 + if (check_platform(dev, r)) 175 + continue; 176 dev_err(&dev->dev, "BAR %d: can't " 177 "allocate resource\n", idx); 178 /* We'll assign a new address later */