On Mon, Nov 03, 2014 at 08:59:34PM +0100, Laszlo Ersek wrote: > > Additionally, initialize PCI_INTERRUPT_LINE registers for the typical > > set of PCI devices included by QEMU with the Q35 machine type. The > > corresponding PIIX4 initialization of PCI_INTERRUPT_LINE registers is > > cleaned up and the list of PIIX4 PCI devices updated to the list > > typically included with QEMU. > > > > NOTE: The initialization of PCI_INTERRUPT_LINE registers *should* > > be accomplished programmatically by enumerating all PCI devices > > present in the system and computing PCI_INTERRUPT_LINE from > > PCI_INTERRUPT_PIN, the slot/position of the device, and the available > > host IRQs (for an example, see SeaBIOS pci_bios_init_devices() in > > src/fw/pciinit.c). At the time of this patch, the relevant bits of > > OVMF PCI initialization are shown in the following call tree: > > > > (2) It's not hard to do the enumeration that you want. You just need to > locate all handles with PciIo protocol instances on them (since we're > just after a BdsLibConnectAll() call), open the PciIo protocol interface > on each in turn, read the header from the PCI config space, and do what > you want. > > Luckily, this code already exists for you, precisely in this file -- > "OvmfPkg/Library/PlatformBdsLib/BdsPlatform.c". The function you want to > call is > > VisitAllPciInstances()
Again, thanks for the awesome tutorial ! I have something that works now (see patch on top of v4 6/6 below). But I'm a bit confuse about whether (and if yes, how) to handle multiple-bus situations. The SeaBIOS code I'm porting (slightly paraphrased for clarity, but hopefully functionally identical to the real thing) goes like this: /* host irqs corresponding to PCI irqs A-D */ const u8 pci_irqs[4] = { 10, 10, 11, 11 }; static int piix_pci_slot_get_irq(struct pci_device *pci, int pin) { int index, slot, pin_addend = 0; while (pci->parent != NULL) { pin_addend += pci_bdf_to_dev(pci->bdf); pci = pci->parent; } slot = pci_bdf_to_dev(pci->bdf); index = pin - 1 + pin_addend + slot - 1; return pci_irqs[index & 3]; } static int mch_pci_slot_get_irq(struct pci_device *pci, int pin) { int index, slot, pin_addend = 0; while (pci->parent != NULL) { pin_addend += pci_bdf_to_dev(pci->bdf); pci = pci->parent; } slot = pci_bdf_to_dev(pci->bdf); if (slot <= 24) { /* Slots 0-24 rotate slot:pin mapping similar to piix above, but with a different starting index - see q35-acpi-dsdt.dsl */ index = pin - 1 + pin_addend + slot; } else { /* Slots 25-31 all use LNKA mapping (or LNKE, but A:D = E:H) */ index = pin - 1 + pin_addend; } return pci_irqs[index & 3]; } >From what I have been able to see, "pci->parent" is always NULL from the very beginning, so the IRQ is computed based only on the "pin" and the device number ("pci_bdf_to_dev(pci->bdf)"), and by that I mean the *original* pci->bdf, not the result of walking up the parent link. Now, here's my current code: diff --git a/OvmfPkg/Library/PlatformBdsLib/BdsPlatform.c b/OvmfPkg/Library/PlatformBdsLib/BdsPlatform.c index 98bc05f..3121dcf 100644 --- a/OvmfPkg/Library/PlatformBdsLib/BdsPlatform.c +++ b/OvmfPkg/Library/PlatformBdsLib/BdsPlatform.c @@ -25,7 +25,15 @@ EFI_EVENT mEfiDevPathEvent; VOID *mEmuVariableEventReg; EFI_EVENT mEmuVariableEvent; BOOLEAN mDetectVgaOnly; +UINT16 mHostBridgeDevId; +// +// Table of host IRQs matching PCI IRQs A-D +// (for configuring PCI Interrupt Line register) +// +CONST UINT8 PciHostIrqs[] = { + 0x0a, 0x0a, 0x0b, 0x0b +}; // // Type definitions @@ -716,18 +724,96 @@ Returns: } +/** + Configure PCI Interrupt Line register for applicable devices + + @param[in] Handle - Handle of PCI device instance + @param[in] PciIo - PCI IO protocol instance + @param[in] PciHdr - PCI Header register block + + @retval EFI_SUCCESS - PCI Interrupt Line register configured successfully. + +**/ +EFI_STATUS +EFIAPI +ConfigurePciInterruptLine ( + IN EFI_HANDLE Handle, + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN PCI_TYPE00 *PciHdr + ) +{ + EFI_DEVICE_PATH_PROTOCOL *DevPathNode; + PCI_DEVICE_PATH *PciDevPathData; + UINTN Idx; + + if (PciHdr->Device.InterruptPin != 0) { + DevPathNode = DevicePathFromHandle (Handle); + ASSERT (DevPathNode != NULL); + + // + // Walk PCI hardware device path + // + while (!IsDevicePathEnd (DevPathNode)) { + if (DevicePathType (DevPathNode) == HARDWARE_DEVICE_PATH && + DevicePathSubType (DevPathNode) == HW_PCI_DP) { + + PciDevPathData = (PCI_DEVICE_PATH*) DevPathNode; + + // + // Calculate index into PciHostIrqs[] table + // (assuming all devices connected directly to root PCI bus 0) + // + switch (mHostBridgeDevId) { + case INTEL_82441_DEVICE_ID: + Idx = PciHdr->Device.InterruptPin - 1 + PciDevPathData->Device - 1; + break; + case INTEL_Q35_MCH_DEVICE_ID: + Idx = PciHdr->Device.InterruptPin - 1; + if (PciDevPathData->Device <= 24) { + Idx += PciDevPathData->Device; + } + break; + default: + ASSERT (FALSE); // should never get here + } + + // + // Set PCI Interrupt Line register for this device + // + PciWrite8 ( + PCI_LIB_ADDRESS ( + 0, + PciDevPathData->Device, + PciDevPathData->Function, + PCI_INT_LINE_OFFSET + ), + PciHostIrqs[Idx % 4] //FIXME: array_size(PciHostIrqs) macro ? + ); + + // + // Stop after first PCI hardware device path node found + // + break; + } + DevPathNode = NextDevicePathNode (DevPathNode); + } + } + + return EFI_SUCCESS; +} I don't know how I could bring a bus number into the mix (I just assume the bus number is 0 and stop after the first device path node, which happens to match the SeaBIOS (pci->parent == NULL) case. But how would I (and should I even try) to port the whole thing ? I'm thinking I could maybe walk the whole DevicePath (and maybe encounter more pci hardware device path nodes in the process in a way equivalent to climbing the seabios pci->parent links), but I still don't know how I would figure out the bus number of the original device for which I'm trying to compute the IRQ line (for the PCI_LIB_ADDRESS write to PCI_INT_LINE_OFFSET). Thanks much for any more ideas, --Gabriel VOID PciAcpiInitialization ( ) { - UINT16 HostBridgeDevId; UINTN Pmba; // // Query Host Bridge DID to determine platform type // - HostBridgeDevId = PcdGet16 (PcdOvmfHostBridgePciDevId); - switch (HostBridgeDevId) { + mHostBridgeDevId = PcdGet16 (PcdOvmfHostBridgePciDevId); + switch (mHostBridgeDevId) { case INTEL_82441_DEVICE_ID: Pmba = POWER_MGMT_REGISTER_PIIX4 (0x40); // @@ -754,54 +840,19 @@ PciAcpiInitialization ( break; default: DEBUG ((EFI_D_ERROR, "%a: Unknown Host Bridge Device ID: 0x%04x\n", - __FUNCTION__, HostBridgeDevId)); + __FUNCTION__, mHostBridgeDevId)); ASSERT (FALSE); } // + // Initialize PCI_INTERRUPT_LINE for applicable configured PCI devices + // + VisitAllPciInstances (ConfigurePciInterruptLine); + + // // Set ACPI SCI_EN bit in PMCNTRL // IoOr16 ((PciRead32 (Pmba) & ~BIT0) + 4, BIT0); - - // - // Initialize PCI_INTERRUPT_LINE for commonly encountered devices and slots - // - // FIXME: This should instead be accomplished programmatically by - // ennumerating all PCI devices present in the system and - // computing PCI_INTERRUPT_LINE from PCI_INTERRUPT_PIN, the - // slot/position of the device, and the available host IRQs - // (for an example, see SeaBIOS pci_bios_init_devices() in - // src/fw/pciinit.c) - // - switch (HostBridgeDevId) { - case INTEL_82441_DEVICE_ID: - PciWrite8 (PCI_LIB_ADDRESS (0, 1, 2, 0x3c), 0x0b); // usb (northbr.) - PciWrite8 (PCI_LIB_ADDRESS (0, 1, 3, 0x3c), 0x0a); // acpi (northbr.) - PciWrite8 (PCI_LIB_ADDRESS (0, 3, 0, 0x3c), 0x0b); - PciWrite8 (PCI_LIB_ADDRESS (0, 4, 0, 0x3c), 0x0b); - PciWrite8 (PCI_LIB_ADDRESS (0, 5, 0, 0x3c), 0x0a); - PciWrite8 (PCI_LIB_ADDRESS (0, 6, 0, 0x3c), 0x0a); - PciWrite8 (PCI_LIB_ADDRESS (0, 7, 0, 0x3c), 0x0b); - PciWrite8 (PCI_LIB_ADDRESS (0, 8, 0, 0x3c), 0x0b); - break; - case INTEL_Q35_MCH_DEVICE_ID: - PciWrite8 (PCI_LIB_ADDRESS (0, 2, 0, 0x3c), 0x0b); - PciWrite8 (PCI_LIB_ADDRESS (0, 3, 0, 0x3c), 0x0b); - PciWrite8 (PCI_LIB_ADDRESS (0, 4, 0, 0x3c), 0x0a); - PciWrite8 (PCI_LIB_ADDRESS (0, 5, 0, 0x3c), 0x0a); - PciWrite8 (PCI_LIB_ADDRESS (0, 6, 0, 0x3c), 0x0b); - PciWrite8 (PCI_LIB_ADDRESS (0, 7, 0, 0x3c), 0x0b); - PciWrite8 (PCI_LIB_ADDRESS (0, 8, 0, 0x3c), 0x0a); - PciWrite8 (PCI_LIB_ADDRESS (0, 0x1d, 0, 0x3c), 0x0a); // uhci1 - PciWrite8 (PCI_LIB_ADDRESS (0, 0x1d, 1, 0x3c), 0x0a); // uhci2 - PciWrite8 (PCI_LIB_ADDRESS (0, 0x1d, 2, 0x3c), 0x0b); // uhci3 - PciWrite8 (PCI_LIB_ADDRESS (0, 0x1d, 7, 0x3c), 0x0b); // ehci1 - PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 2, 0x3c), 0x0a); // ahci (northbr.) - PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 3, 0x3c), 0x0a); // smbus (northbr.) - break; - default: - ASSERT (FALSE); // should never be reached - } } -- 1.9.3 ------------------------------------------------------------------------------ _______________________________________________ edk2-devel mailing list edk2-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/edk2-devel