forked from luck/tmp_suning_uos_patched
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>
This commit is contained in:
parent
060700b571
commit
a2bd7274b4
|
@ -31,8 +31,11 @@
|
|||
#include <linux/ioport.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include <asm/pat.h>
|
||||
#include <asm/hpet.h>
|
||||
#include <asm/io_apic.h>
|
||||
|
||||
#include "pci.h"
|
||||
|
||||
|
@ -77,6 +80,77 @@ pcibios_align_resource(void *data, struct resource *res,
|
|||
}
|
||||
EXPORT_SYMBOL(pcibios_align_resource);
|
||||
|
||||
static int check_res_with_valid(struct pci_dev *dev, struct resource *res)
|
||||
{
|
||||
unsigned long base;
|
||||
unsigned long size;
|
||||
int i;
|
||||
|
||||
base = res->start;
|
||||
size = (res->start == 0 && res->end == res->start) ? 0 :
|
||||
(res->end - res->start + 1);
|
||||
|
||||
if (!base || !size)
|
||||
return 0;
|
||||
|
||||
#ifdef CONFIG_HPET_TIMER
|
||||
/* for hpet */
|
||||
if (base == hpet_address && (res->flags & IORESOURCE_MEM)) {
|
||||
dev_info(&dev->dev, "BAR has HPET at %08lx-%08lx\n",
|
||||
base, base + size - 1);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_X86_IO_APIC
|
||||
for (i = 0; i < nr_ioapics; i++) {
|
||||
unsigned long ioapic_phys = mp_ioapics[i].mp_apicaddr;
|
||||
|
||||
if (base == ioapic_phys && (res->flags & IORESOURCE_MEM)) {
|
||||
dev_info(&dev->dev, "BAR has ioapic at %08lx-%08lx\n",
|
||||
base, base + size - 1);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PCI_MMCONFIG
|
||||
for (i = 0; i < pci_mmcfg_config_num; i++) {
|
||||
unsigned long addr;
|
||||
|
||||
addr = pci_mmcfg_config[i].address;
|
||||
if (base == addr && (res->flags & IORESOURCE_MEM)) {
|
||||
dev_info(&dev->dev, "BAR has MMCONFIG at %08lx-%08lx\n",
|
||||
base, base + size - 1);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_platform(struct pci_dev *dev, struct resource *res)
|
||||
{
|
||||
struct resource *root = NULL;
|
||||
|
||||
/*
|
||||
* forcibly insert it into the
|
||||
* resource tree
|
||||
*/
|
||||
if (res->flags & IORESOURCE_MEM)
|
||||
root = &iomem_resource;
|
||||
else if (res->flags & IORESOURCE_IO)
|
||||
root = &ioport_resource;
|
||||
|
||||
if (root && check_res_with_valid(dev, res)) {
|
||||
insert_resource(root, res);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Handle resources of PCI devices. If the world were perfect, we could
|
||||
* just allocate all the resource regions and do nothing more. It isn't.
|
||||
|
@ -128,6 +202,8 @@ static void __init pcibios_allocate_bus_resources(struct list_head *bus_list)
|
|||
pr = pci_find_parent_resource(dev, r);
|
||||
if (!r->start || !pr ||
|
||||
request_resource(pr, r) < 0) {
|
||||
if (check_platform(dev, r))
|
||||
continue;
|
||||
dev_err(&dev->dev, "BAR %d: can't "
|
||||
"allocate resource\n", idx);
|
||||
/*
|
||||
|
@ -171,6 +247,8 @@ static void __init pcibios_allocate_resources(int pass)
|
|||
r->flags, disabled, pass);
|
||||
pr = pci_find_parent_resource(dev, r);
|
||||
if (!pr || request_resource(pr, r) < 0) {
|
||||
if (check_platform(dev, r))
|
||||
continue;
|
||||
dev_err(&dev->dev, "BAR %d: can't "
|
||||
"allocate resource\n", idx);
|
||||
/* We'll assign a new address later */
|
||||
|
|
Loading…
Reference in New Issue
Block a user