diff --git a/src/system/kernel/arch/x86/arch_int.cpp b/src/system/kernel/arch/x86/arch_int.cpp index 3e235ce6c4..7a9eae624c 100644 --- a/src/system/kernel/arch/x86/arch_int.cpp +++ b/src/system/kernel/arch/x86/arch_int.cpp @@ -1,4 +1,5 @@ /* + * Copyright 2008-2011, Michael Lotz, mmlr@mlotz.ch. * Copyright 2010, Clemens Zeidler, haiku@clemens-zeidler.de. * Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de. * Copyright 2002-2010, Axel Dörfler, axeld@pinc-software.de. @@ -588,6 +589,12 @@ acpi_set_interrupt_model(acpi_module_info* acpiModule, uint32 interruptModel) parameter.count = 1; parameter.pointer = &model; + dprintf("setting ACPI interrupt model to %s\n", + interruptModel == 0 ? "PIC" + : (interruptModel == 1 ? "APIC" + : (interruptModel == 2 ? "SAPIC" + : "unknown"))); + return acpiModule->evaluate_method(NULL, "\\_PIC", ¶meter, NULL); } @@ -696,30 +703,11 @@ ioapic_init(kernel_args* args) print_irq_routing_table(&table); #endif - // configure io apic interrupts from pci routing table + // configure io apic interrupts from PCI routing table for (int i = 0; i < table.Count(); i++) { - uint8 irq = 0; - uint32 config = 0; - irq_routing_entry& entry = table.ElementAt(i); - if (entry.source == 0) { - // fixed irq configuration - irq = entry.source_index; - config = B_LEVEL_TRIGGERED | B_LOW_ACTIVE_POLARITY; - } else { - // irq comes from the link device and is configurable - irq_descriptor irqDescriptor; - status = read_current_irq(acpiModule, entry.source, &irqDescriptor); - if (status != B_OK) { - dprintf("failed to read current irq config of link device\n"); - continue; - } - - irq = irqDescriptor.irq; - config = irqDescriptor.polarity | irqDescriptor.interrupt_mode; - } - - ioapic_configure_io_interrupt(irq, config); + ioapic_configure_io_interrupt(entry.irq, + entry.polarity | entry.trigger_mode); } // disable the legacy PIC diff --git a/src/system/kernel/arch/x86/irq_routing_table.cpp b/src/system/kernel/arch/x86/irq_routing_table.cpp index 8359b4edfa..fb1e553b9a 100644 --- a/src/system/kernel/arch/x86/irq_routing_table.cpp +++ b/src/system/kernel/arch/x86/irq_routing_table.cpp @@ -1,4 +1,5 @@ /* + * Copyright 2011, Michael Lotz mmlr@mlotz.ch. * Copyright 2009, Clemens Zeidler haiku@clemens-zeidler.de. * All rights reserved. * @@ -11,8 +12,10 @@ #include +#include -#define TRACE_PRT + +//#define TRACE_PRT #ifdef TRACE_PRT # define TRACE(x...) dprintf("IRQRoutingTable: "x) #else @@ -31,7 +34,7 @@ irq_descriptor::irq_descriptor() irq(0), shareable(false), polarity(B_HIGH_ACTIVE_POLARITY), - interrupt_mode(B_EDGE_TRIGGERED) + trigger_mode(B_EDGE_TRIGGERED) { } @@ -44,54 +47,180 @@ print_irq_descriptor(irq_descriptor* descriptor) const char* levelTriggeredString = "level triggered"; const char* edgeTriggeredString = "edge triggered"; - dprintf("irq: %u, shareable: %u, polarity: %s, interrupt_mode: %s\n", + dprintf("irq: %u, shareable: %u, polarity: %s, trigger_mode: %s\n", descriptor->irq, descriptor->shareable, descriptor->polarity == B_HIGH_ACTIVE_POLARITY ? activeHighString : activeLowString, - descriptor->interrupt_mode == B_LEVEL_TRIGGERED ? levelTriggeredString + descriptor->trigger_mode == B_LEVEL_TRIGGERED ? levelTriggeredString : edgeTriggeredString); } +static void +print_irq_routing_entry(const irq_routing_entry& entry) +{ + dprintf("address 0x%04llx; pin %u;", entry.device_address, entry.pin); + + if (entry.source_index != 0) + dprintf(" GSI %lu;", entry.source_index); + else + dprintf(" source %p %lu;", entry.source, entry.source_index); + + dprintf(" pci %u:%u\n", entry.pci_bus, entry.pci_device); +} + + void print_irq_routing_table(IRQRoutingTable* table) { dprintf("IRQ routing table with %i entries\n", (int)table->Count()); - for (int i = 0; i < table->Count(); i++) { - irq_routing_entry& entry = table->ElementAt(i); - dprintf("\tentry %i\n", i); - dprintf("\t\taddress: %x\n", entry.device_address); - dprintf("\t\tpin: %i\n", entry.pin); - dprintf("\t\tsource: 0x%x\n", int(entry.source)); - dprintf("\t\tsource index: %i\n", entry.source_index); - } + for (int i = 0; i < table->Count(); i++) + print_irq_routing_entry(table->ElementAt(i)); } static status_t -read_device_irq_routing_table(acpi_module_info* acpi, acpi_handle device, +update_pci_info_for_entry(pci_module_info* pci, irq_routing_entry& entry) +{ + pci_info info; + long index = 0; + uint32 updateCount = 0; + while (pci->get_nth_pci_info(index++, &info) >= 0) { + if (info.bus != entry.pci_bus || info.device != entry.pci_device) + continue; + + uint8 pin = 0; + switch (info.header_type & PCI_header_type_mask) { + case PCI_header_type_generic: + pin = info.u.h0.interrupt_pin; + break; + + 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. + pin = info.u.h1.interrupt_pin; + break; + + default: + // Skip anything unknown as we wouldn't know how to update the + // info anyway. + continue; + } + + if (pin == 0) + continue; // no interrupts are used + + // Now match the pin to find the corresponding function, note that PCI + // pins are 1 based while ACPI ones are 0 based. + if (pin == entry.pin + 1) { + if (pci->update_interrupt_line(info.bus, info.device, info.function, + entry.irq) == B_OK) { + updateCount++; + } + } + + // Sadly multiple functions can share the same interrupt pin so we + // have to run through the whole list each time... + } + + return updateCount > 0 ? B_OK : B_ENTRY_NOT_FOUND; +} + + +static status_t +read_bridge_irq_routing_table(acpi_module_info* acpi, pci_module_info* pci, + acpi_handle possibleBridge, acpi_handle rootPciHandle, IRQRoutingTable* table) { acpi_data buffer; buffer.pointer = NULL; buffer.length = ACPI_ALLOCATE_BUFFER; - status_t status = acpi->get_irq_routing_table(device, &buffer); + status_t status = acpi->get_irq_routing_table(possibleBridge, &buffer); if (status != B_OK) return status; + // It appears to be a bridge device, resolve the PCI infos. As all routing + // table entries will be relative to this bus we can then just address the + // devices using the device number extracted from the address field. + acpi_pci_info pciInfo; + status = acpi->get_pci_info(rootPciHandle, possibleBridge, &pciInfo); + if (status != B_OK) { + dprintf("failed to resolve pci info of bridge device, can't configure" + " irq routing of devices below\n"); + return B_ERROR; + } + + // Find the secondary bus number (the "downstream" bus number for the + // attached devices) in the bridge configuration. + uint8 secondaryBus = pci->read_pci_config(pciInfo.bus, pciInfo.device, + pciInfo.function, PCI_secondary_bus, 1); + if (secondaryBus == 255) + return B_ERROR; + + if (secondaryBus == pciInfo.bus && possibleBridge != rootPciHandle) { + dprintf("invalid secondary bus %u on primary bus %u, can't configure" + " irq routing of devices below\n", secondaryBus, pciInfo.bus); + return B_ERROR; + } + irq_routing_entry irqEntry; acpi_pci_routing_table* acpiTable = (acpi_pci_routing_table*)buffer.pointer; while (acpiTable->length) { acpi_handle source; - bool noSource = acpiTable->source == NULL || acpiTable->source[0] == 0; + + bool noSource = acpiTable->source[0] == '\0'; + // The above line would be correct according to specs... + noSource = acpiTable->source_index != 0; + // ... but we use this one as there seem to be quirks where + // a source is indicated but not actually present. With a source + // index != 0 a GSI is generally indicated. + if (noSource || acpi->get_handle(NULL, acpiTable->source, &source) == B_OK) { irqEntry.device_address = acpiTable->address; irqEntry.pin = acpiTable->pin; - irqEntry.source = noSource ? 0 : source; + irqEntry.source = noSource ? NULL : source; irqEntry.source_index = acpiTable->source_index; - table->PushBack(irqEntry); + irqEntry.pci_bus = secondaryBus; + irqEntry.pci_device = (uint8)(acpiTable->address >> 16); + + // resolve any link device so we get a straight GSI in all cases + if (noSource) { + // fixed GSI already + irqEntry.irq = irqEntry.source_index; + irqEntry.polarity = B_LOW_ACTIVE_POLARITY; + irqEntry.trigger_mode = B_LEVEL_TRIGGERED; + } else { + irq_descriptor irqDescriptor; + status = read_current_irq(acpi, source, &irqDescriptor); + if (status != B_OK) { + dprintf("failed to read current IRQ of link device\n"); + irqEntry.irq = 0; + } else { + irqEntry.irq = irqDescriptor.irq; + irqEntry.polarity = irqDescriptor.polarity; + irqEntry.trigger_mode = irqDescriptor.trigger_mode; + } + } + + if (irqEntry.irq != 0) { + if (update_pci_info_for_entry(pci, irqEntry) != B_OK) { + // Note: This isn't necesarily fatal, as there can be many + // entries in the table pointing to disabled/optional + // devices. Also they can be used to describe the full actual + // wireing regardless of the presence of devices, in which + // case many entries won't have matches. +#ifdef TRACE_PRT + dprintf("didn't find a matching PCI device for irq entry," + " can't update interrupt_line info:\n"); + print_irq_routing_entry(irqEntry); +#endif + } + + table->PushBack(irqEntry); + } } acpiTable = (acpi_pci_routing_table*)((uint8*)acpiTable @@ -116,28 +245,44 @@ read_irq_routing_table(acpi_module_info* acpi, IRQRoutingTable* table) status = acpi->get_handle(NULL, rootPciName, &rootPciHandle); if (status != B_OK) return status; - TRACE("Read root pci bus irq rooting table\n"); - status = read_device_irq_routing_table(acpi, rootPciHandle, table); - if (status != B_OK) + + pci_module_info* pci; + status = get_module(B_PCI_MODULE_NAME, (module_info**)&pci); + if (status != B_OK) { + // shouldn't happen, since the PCI module is a dependency of the + // ACPI module and we shouldn't be here at all if it wasn't loaded + dprintf("failed to get PCI module!\n"); return status; + } + + TRACE("read root pci bus irq routing table\n"); + status = read_bridge_irq_routing_table(acpi, pci, rootPciHandle, + rootPciHandle, table); + if (status != B_OK) { + put_module(B_PCI_MODULE_NAME); + return status; + } TRACE("find routing tables \n"); char name[255]; name[0] = 0; - void *counter = NULL; + void* counter = NULL; while (acpi->get_next_entry(ACPI_TYPE_DEVICE, rootPciName, name, sizeof(name), &counter) == B_OK) { - acpi_handle brigde; - status = acpi->get_handle(NULL, name, &brigde); + acpi_handle possibleBridge; + status = acpi->get_handle(NULL, name, &possibleBridge); if (status != B_OK) continue; - status = read_device_irq_routing_table(acpi, brigde, table); + status = read_bridge_irq_routing_table(acpi, pci, possibleBridge, + rootPciHandle, table); if (status == B_OK) TRACE("routing table found %s\n", name); + // if it failed it simply was no bridge at all } + put_module(B_PCI_MODULE_NAME); return table->Count() > 0 ? B_OK : B_ERROR; } @@ -179,7 +324,7 @@ read_irq_descriptor(acpi_module_info* acpi, acpi_handle device, descriptor->irq = irq->interrupts[0]; descriptor->shareable = irq->sharable != 0; - descriptor->interrupt_mode = irq->triggering == 0 + descriptor->trigger_mode = irq->triggering == 0 ? B_LEVEL_TRIGGERED : B_EDGE_TRIGGERED; descriptor->polarity = irq->polarity == 0 ? B_HIGH_ACTIVE_POLARITY : B_LOW_ACTIVE_POLARITY; @@ -214,7 +359,7 @@ read_irq_descriptor(acpi_module_info* acpi, acpi_handle device, descriptor->irq = irq->interrupts[0]; descriptor->shareable = irq->sharable != 0; - descriptor->interrupt_mode = irq->triggering == 0 + descriptor->trigger_mode = irq->triggering == 0 ? B_LEVEL_TRIGGERED : B_EDGE_TRIGGERED; descriptor->polarity = irq->polarity == 0 ? B_HIGH_ACTIVE_POLARITY : B_LOW_ACTIVE_POLARITY; @@ -282,7 +427,7 @@ set_acpi_irq(acpi_module_info* acpi, acpi_handle device, data[3] = 0; int8 bit; - bit = (descriptor->interrupt_mode == B_HIGH_ACTIVE_POLARITY) ? 0 : 1; + bit = (descriptor->trigger_mode == B_HIGH_ACTIVE_POLARITY) ? 0 : 1; data[3] |= bit; bit = (descriptor->polarity == B_LEVEL_TRIGGERED) ? 0 : 1; data[3] |= bit << 3; diff --git a/src/system/kernel/arch/x86/irq_routing_table.h b/src/system/kernel/arch/x86/irq_routing_table.h index 72da807533..550ee9d517 100644 --- a/src/system/kernel/arch/x86/irq_routing_table.h +++ b/src/system/kernel/arch/x86/irq_routing_table.h @@ -13,15 +13,21 @@ struct irq_routing_entry { - int device_address; - int8 pin; + // ACPI specifics + uint64 device_address; + uint8 pin; acpi_handle source; - int source_index; + uint32 source_index; - // pci busmanager connection - uchar pci_bus; - uchar pci_device; + // PCI bus_manager connection + uint8 pci_bus; + uint8 pci_device; + + // Distilled configuration info + uint8 irq; // Global System Interrupt (GSI) + uint8 polarity; // B_{HIGH|LOW}_ACTIVE_POLARITY + uint8 trigger_mode; // B_{LEVEL|EDGE}_TRIGGERED }; @@ -36,7 +42,7 @@ struct irq_descriptor { // B_LOW_ACTIVE_POLARITY or B_HIGH_ACTIVE_POLARITY uint8 polarity; // B_LEVEL_TRIGGERED or B_EDGE_TRIGGERED - uint8 interrupt_mode; + uint8 trigger_mode; };