diff options
author | Bjorn Helgaas <bjorn.helgaas@hp.com> | 2009-11-04 12:39:18 -0500 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2009-11-04 16:06:46 -0500 |
commit | 03db42adfeeabe856dbb6894dd3aaff55838330a (patch) | |
tree | f78101e96ce2171a503bd1119d08d89e31118a69 /arch | |
parent | f1db6fde09e201218f488d7205a7cd7bc448d496 (diff) |
x86/PCI: fix bogus host bridge window start/end alignment from _CRS
PCI device BARs are guaranteed to start and end on at least a four-byte
(I/O) or a sixteen-byte (MMIO) boundary because they're aligned on their
size and the low BAR bits are reserved. PCI-to-PCI bridge apertures
have even larger alignment restrictions.
However, some BIOSes (e.g., HP DL360 BIOS P31) report host bridge windows
like "[io 0x0000-0x2cfe]". This is wrong because it excludes the last
port at 0x2cff: it's impossible for a downstream device to claim 0x2cfe
without also claiming 0x2cff. In fact, this BIOS configures a device
behind the bridge to "[io 0x2c00-0x2cff]", so we know the window actually
does include 0x2cff.
This patch rounds the start and end of apertures to the appropriate
boundary. I experimentally determined that Windows contains a similar
workaround; details here:
http://bugzilla.kernel.org/show_bug.cgi?id=14337
Signed-off-by: Bjorn Helgaas <bjorn.helgaas@hp.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/pci/acpi.c | 25 |
1 files changed, 25 insertions, 0 deletions
diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 54db5a04b5e1..8ddf4f4c7253 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c | |||
@@ -59,6 +59,30 @@ bus_has_transparent_bridge(struct pci_bus *bus) | |||
59 | return false; | 59 | return false; |
60 | } | 60 | } |
61 | 61 | ||
62 | static void | ||
63 | align_resource(struct acpi_device *bridge, struct resource *res) | ||
64 | { | ||
65 | int align = (res->flags & IORESOURCE_MEM) ? 16 : 4; | ||
66 | |||
67 | /* | ||
68 | * Host bridge windows are not BARs, but the decoders on the PCI side | ||
69 | * that claim this address space have starting alignment and length | ||
70 | * constraints, so fix any obvious BIOS goofs. | ||
71 | */ | ||
72 | if (res->start & (align - 1)) { | ||
73 | dev_printk(KERN_DEBUG, &bridge->dev, | ||
74 | "host bridge window %pR invalid; " | ||
75 | "aligning start to %d-byte boundary\n", res, align); | ||
76 | res->start &= ~(align - 1); | ||
77 | } | ||
78 | if ((res->end + 1) & (align - 1)) { | ||
79 | dev_printk(KERN_DEBUG, &bridge->dev, | ||
80 | "host bridge window %pR invalid; " | ||
81 | "aligning end to %d-byte boundary\n", res, align); | ||
82 | res->end = roundup(res->end, align) - 1; | ||
83 | } | ||
84 | } | ||
85 | |||
62 | static acpi_status | 86 | static acpi_status |
63 | setup_resource(struct acpi_resource *acpi_res, void *data) | 87 | setup_resource(struct acpi_resource *acpi_res, void *data) |
64 | { | 88 | { |
@@ -107,6 +131,7 @@ setup_resource(struct acpi_resource *acpi_res, void *data) | |||
107 | res->start = start; | 131 | res->start = start; |
108 | res->end = end; | 132 | res->end = end; |
109 | res->child = NULL; | 133 | res->child = NULL; |
134 | align_resource(info->bridge, res); | ||
110 | 135 | ||
111 | if (!(pci_probe & PCI_USE__CRS)) { | 136 | if (!(pci_probe & PCI_USE__CRS)) { |
112 | dev_printk(KERN_DEBUG, &info->bridge->dev, | 137 | dev_printk(KERN_DEBUG, &info->bridge->dev, |