Author: mmlr Date: 2011-05-26 14:59:18 +0200 (Thu, 26 May 2011) New Revision: 41759 Changeset: https://dev.haiku-os.org/changeset/41759 Modified: haiku/branches/releases/r1alpha3/src/system/kernel/arch/x86/irq_routing_table.cpp Log: * Merge r41726 from trunk as it fixes some missing interrupt routing when using the IO-APIC. * Merge r41758 from trunk as well, but with disabled panics in the fallback cases. It is needed to complete the routing when the IO-APIC is enabled. This also prevents the IO-APIC from being used if the routing can't be fully established. With the disabled panics it will simply print the reason and fall back to PIC mode. Modified: haiku/branches/releases/r1alpha3/src/system/kernel/arch/x86/irq_routing_table.cpp =================================================================== --- haiku/branches/releases/r1alpha3/src/system/kernel/arch/x86/irq_routing_table.cpp 2011-05-26 12:35:02 UTC (rev 41758) +++ haiku/branches/releases/r1alpha3/src/system/kernel/arch/x86/irq_routing_table.cpp 2011-05-26 12:59:18 UTC (rev 41759) @@ -32,6 +32,8 @@ // TODO: as per PCI 3.0, the PCI module hardcodes it in various places as well. static const uint8 kMaxPCIFunctionCount = 8; +static const uint8 kMaxPCIDeviceCount = 32; + // TODO: actually this is mechanism dependent static const uint8 kMaxISAInterrupts = 16; irq_descriptor::irq_descriptor() @@ -111,17 +113,9 @@ // check the base device at function 0 uint8 headerType = pci->read_pci_config(entry.pci_bus, entry.pci_device, 0, PCI_header_type, 1); - switch (headerType & PCI_header_type_mask) { - case PCI_header_type_generic: - case PCI_header_type_PCI_to_PCI_bridge: - // We don't really care about bridges as we won't install - // interrupt handlers for them, but we can still map them and - // update their info for completeness. - break; - - default: - // either an unsupported or a non-present device (0xff) - return B_ENTRY_NOT_FOUND; + if (headerType == 0xff) { + // the device is not present + return B_ENTRY_NOT_FOUND; } // we have a device, check how many functions we need to iterate @@ -358,7 +352,7 @@ static status_t handle_routing_table_entry(acpi_module_info* acpi, pci_module_info* pci, - const acpi_pci_routing_table* acpiTable, const pci_address& pciAddress, + const acpi_pci_routing_table* acpiTable, uint8 currentBus, irq_routing_entry& irqEntry) { bool noSource = acpiTable->Source[0] == '\0'; @@ -383,7 +377,7 @@ irqEntry.pin = acpiTable->Pin; irqEntry.source = noSource ? NULL : source; irqEntry.source_index = acpiTable->SourceIndex; - irqEntry.pci_bus = pciAddress.bus; + irqEntry.pci_bus = currentBus; irqEntry.pci_device = (uint8)(acpiTable->Address >> 16); status = fill_pci_info_for_entry(pci, irqEntry); @@ -393,10 +387,14 @@ // used to describe the full actual wireing regardless of the presence // of devices, in which case many entries won't have a match. #ifdef TRACE_PRT - dprintf("didn't find a matching PCI device for irq entry:\n"); + dprintf("no matching PCI device for irq entry: "); print_irq_routing_entry(irqEntry); #endif - return status; + } else { +#ifdef TRACE_PRT + dprintf("found matching PCI device for irq entry: "); + print_irq_routing_entry(irqEntry); +#endif } if (noSource) { @@ -411,77 +409,250 @@ } +irq_routing_entry* +find_routing_table_entry(IRQRoutingTable& table, uint8 bus, uint8 device, + uint8 pin) +{ + for (int i = 0; i < table.Count(); i++) { + irq_routing_entry& irqEntry = table.ElementAt(i); + if (irqEntry.pci_bus != bus || irqEntry.pci_device != device) + continue; + + if (irqEntry.pin + 1 == pin) + return &irqEntry; + } + + return NULL; +} + + static status_t +ensure_all_functions_matched(pci_module_info* pci, uint8 bus, + IRQRoutingTable& matchedTable, IRQRoutingTable& unmatchedTable, + Vector<pci_address>& parents) +{ + for (uint8 device = 0; device < kMaxPCIDeviceCount; device++) { + if (pci->read_pci_config(bus, device, 0, PCI_vendor_id, 2) == 0xffff) { + // not present + continue; + } + + uint8 headerType = pci->read_pci_config(bus, device, 0, + PCI_header_type, 1); + + uint8 functionCount = 1; + if ((headerType & PCI_multifunction) != 0) + functionCount = kMaxPCIFunctionCount; + + for (uint8 function = 0; function < functionCount; function++) { + // check for device presence by looking for a valid vendor + if (pci->read_pci_config(bus, device, function, PCI_vendor_id, 2) + == 0xffff) { + // not present + continue; + } + + if (function > 0) { + headerType = pci->read_pci_config(bus, device, function, + PCI_header_type, 1); + } + + // if this is a bridge, recurse down + if ((headerType & PCI_header_type_mask) + == PCI_header_type_PCI_to_PCI_bridge) { + + pci_address pciAddress; + pciAddress.bus = bus; + pciAddress.device = device; + pciAddress.function = function; + + parents.PushBack(pciAddress); + + uint8 secondaryBus = pci->read_pci_config(bus, device, function, + PCI_secondary_bus, 1); + if (secondaryBus != 0xff) { + ensure_all_functions_matched(pci, secondaryBus, + matchedTable, unmatchedTable, parents); + } + + parents.PopBack(); + } + + uint8 interruptPin = pci->read_pci_config(bus, device, function, + PCI_interrupt_pin, 1); + if (interruptPin == 0 || interruptPin > 4) { + // not routed + continue; + } + + irq_routing_entry* irqEntry = find_routing_table_entry(matchedTable, + bus, device, interruptPin); + if (irqEntry != NULL) { + // we already have a matching entry for that device/pin, make + // sure the function mask includes us + irqEntry->pci_function_mask |= 1 << function; + continue; + } + + // This function has no matching routing table entry yet. Try to + // figure one out in the parent, based on the device number and + // interrupt pin. + bool matched = false; + uint8 parentPin = ((device + interruptPin - 1) % 4) + 1; + for (int i = parents.Count() - 1; i >= 0; i--) { + pci_address& parent = parents.ElementAt(i); + irqEntry = find_routing_table_entry(matchedTable, parent.bus, + parent.device, parentPin); + if (irqEntry == NULL) { + // try the unmatched table as well + irqEntry = find_routing_table_entry(unmatchedTable, + parent.bus, parent.device, parentPin); + } + + if (irqEntry == NULL) { + // no match in that parent, go further up + parentPin = ((parent.device + parentPin - 1) % 4) + 1; + continue; + } + + // found a match, make a copy and add it to the table + irq_routing_entry newEntry = *irqEntry; + newEntry.device_address = (device << 16) | 0xffff; + newEntry.pin = interruptPin - 1; + newEntry.pci_bus = bus; + newEntry.pci_device = device; + newEntry.pci_function_mask = 1 << function; + + uint8 biosIRQ = pci->read_pci_config(bus, device, function, + PCI_interrupt_line, 1); + if (biosIRQ != 0 && biosIRQ != 255) { + if (newEntry.bios_irq != 0 && newEntry.bios_irq != 255 + && newEntry.bios_irq != biosIRQ) { + // If the function was actually routed to that pin, + // the two bios irqs should match. If they don't + // that means we're not correct in our routing + // assumption. + dprintf("calculated irq routing doesn't match bios for " + "PCI %u:%u:%u\n", bus, device, function); + return B_ERROR; + } + + newEntry.bios_irq = biosIRQ; + } + + dprintf("calculated irq routing entry: "); + print_irq_routing_entry(newEntry); + + matchedTable.PushBack(newEntry); + matched = true; + break; + } + + if (!matched) { + dprintf("unable to find irq routing for PCI %u:%u:%u\n", bus, + device, function); + return B_ERROR; + } + } + } + + return B_OK; +} + + +static status_t read_irq_routing_table_recursive(acpi_module_info* acpi, pci_module_info* pci, - acpi_handle device, const pci_address& parentAddress, - IRQRoutingTable& table, bool rootBridge, + acpi_handle device, uint8 currentBus, IRQRoutingTable& table, + IRQRoutingTable& unmatchedTable, bool rootBridge, interrupt_available_check_function checkFunction) { - acpi_data buffer; - buffer.pointer = NULL; - buffer.length = ACPI_ALLOCATE_BUFFER; - status_t status = acpi->get_irq_routing_table(device, &buffer); - if (status != B_OK) { - // simply not a bridge - return B_OK; - } + if (!rootBridge) { + // check if this actually is a bridge + uint64 value; + pci_address pciAddress; + pciAddress.bus = currentBus; + if (evaluate_integer(acpi, device, "_ADR", value) == B_OK) { + pciAddress.device = (uint8)(value >> 16); + pciAddress.function = (uint8)value; + } else { + pciAddress.device = 0; + pciAddress.function = 0; + } - TRACE("found irq routing table\n"); + uint8 headerType = pci->read_pci_config(pciAddress.bus, + pciAddress.device, pciAddress.function, PCI_header_type, 1); - uint64 value; - pci_address pciAddress = parentAddress; - if (evaluate_integer(acpi, device, "_ADR", value) == B_OK) { - pciAddress.device = (uint8)(value >> 16); - pciAddress.function = (uint8)value; - } else { - pciAddress.device = 0; - pciAddress.function = 0; - } + switch (headerType & PCI_header_type_mask) { + case PCI_header_type_PCI_to_PCI_bridge: + case PCI_header_type_cardbus: + TRACE("found a PCI bridge (0x%02x)\n", headerType); + break; - if (!rootBridge) { + default: + // Simply not a bridge or not present at all. + TRACE("not a PCI bridge (0x%02x)\n", headerType); + return B_OK; + } + // Find the secondary bus number (the "downstream" bus number for the // attached devices) in the bridge configuration. uint8 secondaryBus = pci->read_pci_config(pciAddress.bus, pciAddress.device, pciAddress.function, PCI_secondary_bus, 1); if (secondaryBus == 255) { // The bus below this bridge is inactive, nothing to do. + TRACE("secondary bus is inactive\n"); return B_OK; } // The secondary bus cannot be the same as the current one. - if (secondaryBus == parentAddress.bus) { + if (secondaryBus == currentBus) { dprintf("invalid secondary bus %u on primary bus %u," " can't configure irq routing of devices below\n", - secondaryBus, parentAddress.bus); + secondaryBus, currentBus); return B_ERROR; } // Everything below is now on the secondary bus. - pciAddress.bus = secondaryBus; + TRACE("now scanning bus %u\n", secondaryBus); + currentBus = secondaryBus; } - acpi_pci_routing_table* acpiTable = (acpi_pci_routing_table*)buffer.pointer; - while (acpiTable->Length) { - irq_routing_entry irqEntry; - status = handle_routing_table_entry(acpi, pci, acpiTable, pciAddress, - irqEntry); - if (status == B_OK) { - if (irqEntry.source == NULL && !checkFunction(irqEntry.irq)) { - dprintf("hardwired irq %u not addressable\n", irqEntry.irq); - free(buffer.pointer); - return B_ERROR; + acpi_data buffer; + buffer.pointer = NULL; + buffer.length = ACPI_ALLOCATE_BUFFER; + status_t status = acpi->get_irq_routing_table(device, &buffer); + if (status == B_OK) { + TRACE("found irq routing table\n"); + + acpi_pci_routing_table* acpiTable + = (acpi_pci_routing_table*)buffer.pointer; + while (acpiTable->Length) { + irq_routing_entry irqEntry; + status = handle_routing_table_entry(acpi, pci, acpiTable, + currentBus, irqEntry); + if (status == B_OK) { + if (irqEntry.source == NULL && !checkFunction(irqEntry.irq)) { + dprintf("hardwired irq %u not addressable\n", irqEntry.irq); + free(buffer.pointer); + return B_ERROR; + } + + if (irqEntry.pci_function_mask != 0) + table.PushBack(irqEntry); + else + unmatchedTable.PushBack(irqEntry); } - table.PushBack(irqEntry); + acpiTable = (acpi_pci_routing_table*)((uint8*)acpiTable + + acpiTable->Length); } - acpiTable = (acpi_pci_routing_table*)((uint8*)acpiTable - + acpiTable->Length); + free(buffer.pointer); + } else { + TRACE("no irq routing table present\n"); } - free(buffer.pointer); - - // recurse down to the child devices + // recurse down the ACPI child devices acpi_data pathBuffer; pathBuffer.pointer = NULL; pathBuffer.length = ACPI_ALLOCATE_BUFFER; @@ -505,7 +676,7 @@ TRACE("recursing down to child \"%s\"\n", childName); status = read_irq_routing_table_recursive(acpi, pci, childHandle, - pciAddress, table, false, checkFunction); + currentBus, table, unmatchedTable, false, checkFunction); if (status != B_OK) break; } @@ -530,16 +701,19 @@ if (status != B_OK) return status; - // We reset the structure to 0 here. Any failed evaluation means default + // We reset the root bus to 0 here. Any failed evaluation means default // values, so we don't have to do anything in the error case. - pci_address rootPciAddress; - memset(&rootPciAddress, 0, sizeof(pci_address)); + uint8 rootBus = 0; uint64 value; + if (evaluate_integer(acpi, rootPciHandle, "_BBN", value) == B_OK) + rootBus = (uint8)value; + +#if 0 + // TODO: handle if (evaluate_integer(acpi, rootPciHandle, "_SEG", value) == B_OK) rootPciAddress.segment = (uint8)value; - if (evaluate_integer(acpi, rootPciHandle, "_BBN", value) == B_OK) - rootPciAddress.bus = (uint8)value; +#endif pci_module_info* pci; status = get_module(B_PCI_MODULE_NAME, (module_info**)&pci); @@ -550,15 +724,30 @@ return status; } - status = read_irq_routing_table_recursive(acpi, pci, rootPciHandle, - rootPciAddress, table, true, checkFunction); + IRQRoutingTable unmatchedTable; + status = read_irq_routing_table_recursive(acpi, pci, rootPciHandle, rootBus, + table, unmatchedTable, true, checkFunction); + if (status != B_OK) { + put_module(B_PCI_MODULE_NAME); + return status; + } - put_module(B_PCI_MODULE_NAME); + if (table.Count() == 0) { + put_module(B_PCI_MODULE_NAME); + return B_ERROR; + } - if (status != B_OK) - return status; + // Now go through all the PCI devices and verify that they have a routing + // table entry. For the devices without a match, we calculate their pins + // on the bridges and try to match these in the parent routing table. We + // do this recursively going up the tree until we find a match or arrive + // at the top. + Vector<pci_address> parents; + status = ensure_all_functions_matched(pci, rootBus, table, unmatchedTable, + parents); - return table.Count() > 0 ? B_OK : B_ERROR; + put_module(B_PCI_MODULE_NAME); + return status; } @@ -600,10 +789,9 @@ status = update_pci_info_for_entry(pci, irqEntry); if (status != B_OK) { -#ifdef TRACE_PRT - dprintf("failed to update interrupt_line info for entry:\n"); - print_irq_routing_entry(irqEntry); -#endif + dprintf("failed to update interrupt_line for PCI %u:%u mask %lx\n", + irqEntry.pci_bus, irqEntry.pci_device, + irqEntry.pci_function_mask); } }