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

Reply via email to