From 53eb64dd71b843957962959403e6fb27c99bc871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Revol?= Date: Sun, 23 Dec 2012 17:53:42 +0100 Subject: [PATCH] PCI: Work around devices wrongly set as bridge class by buggy BIOS This is a proper fix for the issue I tried to fix with hrev43552. Previous fix only fixed the stack overflow caused by it but still generated ghost devices due to the duplicated enumeration. Affected motherboards include FIC PA-2013 (mine), and FIC VA503+ as mentionned on: http://lkml.indiana.edu/hypermail/linux/kernel/9912.0/0539.html We now check the header type for bridge devices and just ignore wrong ones. --- src/add-ons/kernel/bus_managers/pci/pci.cpp | 33 +++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/add-ons/kernel/bus_managers/pci/pci.cpp b/src/add-ons/kernel/bus_managers/pci/pci.cpp index c91ce3d149..73447cc639 100644 --- a/src/add-ons/kernel/bus_managers/pci/pci.cpp +++ b/src/add-ons/kernel/bus_managers/pci/pci.cpp @@ -739,6 +739,12 @@ PCI::_EnumerateBus(int domain, uint8 bus, uint8 *subordinateBus) if (baseClass != PCI_bridge || subClass != PCI_pci) continue; + // skip incorrectly configured devices + uint8 headerType = ReadConfig(domain, bus, dev, function, + PCI_header_type, 1) & PCI_header_type_mask; + if (headerType != PCI_header_type_PCI_to_PCI_bridge) + continue; + TRACE(("PCI: found PCI-PCI bridge: domain %u, bus %u, dev %u, func %u\n", domain, bus, dev, function)); TRACE(("PCI: original settings: pcicmd %04" B_PRIx32 ", primary-bus " @@ -793,6 +799,12 @@ PCI::_EnumerateBus(int domain, uint8 bus, uint8 *subordinateBus) if (baseClass != PCI_bridge || subClass != PCI_pci) continue; + // skip incorrectly configured devices + uint8 headerType = ReadConfig(domain, bus, dev, function, + PCI_header_type, 1) & PCI_header_type_mask; + if (headerType != PCI_header_type_PCI_to_PCI_bridge) + continue; + TRACE(("PCI: configuring PCI-PCI bridge: domain %u, bus %u, dev %u, func %u\n", domain, bus, dev, function)); @@ -877,6 +889,18 @@ PCI::_FixupDevices(int domain, uint8 bus) if (subClass != PCI_pci) continue; + // some FIC motherboards have a buggy BIOS... + // make sure the header type is correct for a bridge, + uint8 headerType = ReadConfig(domain, bus, dev, function, + PCI_header_type, 1) & PCI_header_type_mask; + if (headerType != PCI_header_type_PCI_to_PCI_bridge) { + dprintf("PCI: dom %u, bus %u, dev %2u, func %u, PCI bridge" + " class but wrong header type 0x%02x, ignoring.\n", + domain, bus, dev, function, headerType); + continue; + } + + int busBehindBridge = ReadConfig(domain, bus, dev, function, PCI_secondary_bus, 1); @@ -894,7 +918,9 @@ PCI::_ConfigureBridges(PCIBus *bus) { for (PCIDev *dev = bus->child; dev; dev = dev->next) { if (dev->info.class_base == PCI_bridge - && dev->info.class_sub == PCI_pci) { + && dev->info.class_sub == PCI_pci + && (dev->info.header_type & PCI_header_type_mask) + == PCI_header_type_PCI_to_PCI_bridge) { uint16 bridgeControlOld = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_bridge_control, 2); uint16 bridgeControlNew = bridgeControlOld; @@ -1058,7 +1084,10 @@ PCI::_DiscoverDevice(PCIBus *bus, uint8 dev, uint8 function) PCI_class_base, 1); uint8 subClass = ReadConfig(bus->domain, bus->bus, dev, function, PCI_class_sub, 1); - if (baseClass == PCI_bridge && subClass == PCI_pci) { + uint8 headerType = ReadConfig(bus->domain, bus->bus, dev, function, + PCI_header_type, 1) & PCI_header_type_mask; + if (baseClass == PCI_bridge && subClass == PCI_pci + && headerType == PCI_header_type_PCI_to_PCI_bridge) { uint8 secondaryBus = ReadConfig(bus->domain, bus->bus, dev, function, PCI_secondary_bus, 1); PCIBus *newBus = _CreateBus(newDev, bus->domain, secondaryBus);