forked from mirrors/linux
		
	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>
			
			
This commit is contained in:
		
							parent
							
								
									f1db6fde09
								
							
						
					
					
						commit
						03db42adfe
					
				
					 1 changed files with 25 additions and 0 deletions
				
			
		| 
						 | 
					@ -59,6 +59,30 @@ bus_has_transparent_bridge(struct pci_bus *bus)
 | 
				
			||||||
	return false;
 | 
						return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					align_resource(struct acpi_device *bridge, struct resource *res)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int align = (res->flags & IORESOURCE_MEM) ? 16 : 4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Host bridge windows are not BARs, but the decoders on the PCI side
 | 
				
			||||||
 | 
						 * that claim this address space have starting alignment and length
 | 
				
			||||||
 | 
						 * constraints, so fix any obvious BIOS goofs.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (res->start & (align - 1)) {
 | 
				
			||||||
 | 
							dev_printk(KERN_DEBUG, &bridge->dev,
 | 
				
			||||||
 | 
								   "host bridge window %pR invalid; "
 | 
				
			||||||
 | 
								   "aligning start to %d-byte boundary\n", res, align);
 | 
				
			||||||
 | 
							res->start &= ~(align - 1);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if ((res->end + 1) & (align - 1)) {
 | 
				
			||||||
 | 
							dev_printk(KERN_DEBUG, &bridge->dev,
 | 
				
			||||||
 | 
								   "host bridge window %pR invalid; "
 | 
				
			||||||
 | 
								   "aligning end to %d-byte boundary\n", res, align);
 | 
				
			||||||
 | 
							res->end = roundup(res->end, align) - 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static acpi_status
 | 
					static acpi_status
 | 
				
			||||||
setup_resource(struct acpi_resource *acpi_res, void *data)
 | 
					setup_resource(struct acpi_resource *acpi_res, void *data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -107,6 +131,7 @@ setup_resource(struct acpi_resource *acpi_res, void *data)
 | 
				
			||||||
	res->start = start;
 | 
						res->start = start;
 | 
				
			||||||
	res->end = end;
 | 
						res->end = end;
 | 
				
			||||||
	res->child = NULL;
 | 
						res->child = NULL;
 | 
				
			||||||
 | 
						align_resource(info->bridge, res);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!(pci_probe & PCI_USE__CRS)) {
 | 
						if (!(pci_probe & PCI_USE__CRS)) {
 | 
				
			||||||
		dev_printk(KERN_DEBUG, &info->bridge->dev,
 | 
							dev_printk(KERN_DEBUG, &info->bridge->dev,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue