diff --git a/src/hardware/pci/bios-v2/bios-v2.c b/src/hardware/pci/bios-v2/bios-v2.c index e6ade61..14c1237 100644 --- a/src/hardware/pci/bios-v2/bios-v2.c +++ b/src/hardware/pci/bios-v2/bios-v2.c @@ -79,11 +79,49 @@ struct { IRQRoutingOptionsBuffer buf; uint8_t databuf [2048]; } route_buf; + static uint64_t free_mem_start, free_mem_end; static uint32_t lastbus, version, hardware; int verbose = 0; +typedef struct _pci_reverse_bus_list { + struct _pci_reverse_bus_list* next; + uint8_t sec_bus; + uint8_t bus; + uint8_t devfunc; +} pci_reverse_bus_list; + +static pci_reverse_bus_list ** find_reverse_bus(uint8_t sec_bus) { + static pci_reverse_bus_list *reverse_bus_list = NULL; + + slogf (_SLOGC_PCI, _SLOG_DEBUG1, "Looking up reverse bus for %d", sec_bus); + + pci_reverse_bus_list** reverse_bus_list_entry = &reverse_bus_list; + while ((*reverse_bus_list_entry != NULL) && ((*reverse_bus_list_entry)->sec_bus != sec_bus)) { + reverse_bus_list_entry = &((*reverse_bus_list_entry)->next); + }; + if (*reverse_bus_list_entry) { + slogf (_SLOGC_PCI, _SLOG_DEBUG1, "Found bus %d dev %d at %08X", + (*reverse_bus_list_entry)->bus, + PCI_DEVNO((*reverse_bus_list_entry)->devfunc), + (intptr_t)reverse_bus_list_entry + ); + } else { + slogf (_SLOGC_PCI, _SLOG_DEBUG1, "Not found! Last value is %08X (from %08X)", + (intptr_t)reverse_bus_list_entry, + (intptr_t)&reverse_bus_list + ); + }; + + return reverse_bus_list_entry; +}; + +static uint32_t pci_irq_swizzle(uint8_t devfunc, uint32_t intpin ) { + return ((((intpin -1) + PCI_DEVNO(devfunc)) % 4) +1); +}; + + sigjmp_buf env; /**************************************************************************/ @@ -303,7 +341,25 @@ int i, irq, found; } } if (!found) { - slogf (_SLOGC_PCI, _SLOG_ERROR, "Bus %d device %x - No routing found", bus, PCI_DEVNO(devfn)); + slogf (_SLOGC_PCI, _SLOG_ERROR, "Bus %d device %x pin %d - No routing found", bus, PCI_DEVNO(devfn),intpin); + /* This might be because we're behind a PCI-to-PCI bridge. If so, we can */ + /* try looking up at the previous bridge, and so on, until we get a match. */ + if (bus > 0) { + /* Since we have no way to easily interact with the bus list maintained by the server, we do this the hard way */ + while (bus) { + pci_reverse_bus_list** parent = find_reverse_bus(bus); + if (*parent) { + intpin = pci_irq_swizzle(devfn,intpin); + bus = (*parent)->bus; + devfn = (*parent)->devfunc; + int result = scan_irq_routing(handle,bus,devfn,intpin,intel); + if (result != -1) return result; + } else { + return -1; + }; + }; + } + return (-1); } if (r->irq [intpin - 1].link) { @@ -642,7 +698,10 @@ uint8_t *databuffer; int bios_cnfg_bridge (void *handle, uint32_t bus, uint32_t devfunc, pci_bus_t *pbus) { -bios_dev_t *pdev = handle; + bios_dev_t *pdev = handle; + int i; + uint32_t dword = 0; + int pri_bus, sec_bus; DEBUGFUNC(); if (pbus->pciex_addr != NULL) { @@ -650,6 +709,40 @@ bios_dev_t *pdev = handle; pdev->pciex_size = pbus->pciex_size; } + /* Add an entry to the reverse bus list, for IRQ mapping */ + /* this config read will return 4 bytes. Byte 0 is the primary bus, should be the same as 'bus'. Byte 1 is the secondary bus */ + if ((i = bios_read_cnfg (handle, bus, devfunc, offsetof (struct _pci_bridge_config_regs, Primary_Bus_Number), + 4, &dword)) != PCI_SUCCESS) { + return (i); + } + pri_bus = dword & 0xFF; + sec_bus = (dword >> 8) & 0xff; + if ((pri_bus == bus) && (sec_bus != 0)) { + pci_reverse_bus_list** reverse_bus_list_entry = find_reverse_bus(sec_bus); + + if (!*reverse_bus_list_entry) { + *reverse_bus_list_entry = malloc(sizeof(pci_reverse_bus_list)); + slogf (_SLOGC_PCI, _SLOG_DEBUG1, "Adding new entry for %d at %X, neext address is %X", + pbus->sub_bus, + (intptr_t)reverse_bus_list_entry, + (intptr_t)&((*reverse_bus_list_entry)->next) + ); + if (*reverse_bus_list_entry) { (*reverse_bus_list_entry)->next = NULL; }; + }; + if (*reverse_bus_list_entry) { + slogf (_SLOGC_PCI, _SLOG_DEBUG1, "Updating at %X: B: %d D: %d SB: %d", + (intptr_t)reverse_bus_list_entry, + bus, + devfunc, + pbus->sub_bus + ); + + (*reverse_bus_list_entry)->bus = bus; + (*reverse_bus_list_entry)->devfunc = devfunc; + (*reverse_bus_list_entry)->sec_bus = sec_bus; + }; + }; + return (PCI_SUCCESS); }