]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/Library/PciHostBridgeLib/PciHostBridgeLib.c
OvmfPkg/PciHostBridgeLib: clear PCI aperture vars for (re)init
[mirror_edk2.git] / OvmfPkg / Library / PciHostBridgeLib / PciHostBridgeLib.c
index 3752993f65aa2fe32671a638a3e12bb5460e3988..65d0ef9252c55d09ca475de041c8e53b1a6d2825 100644 (file)
 \r
 **/\r
 #include <PiDxe.h>\r
-#include <Library/PciHostBridgeLib.h>\r
+\r
+#include <IndustryStandard/Pci.h>\r
+#include <IndustryStandard/Q35MchIch9.h>\r
+\r
+#include <Protocol/PciHostBridgeResourceAllocation.h>\r
+#include <Protocol/PciRootBridgeIo.h>\r
+\r
+#include <Library/BaseMemoryLib.h>\r
 #include <Library/DebugLib.h>\r
+#include <Library/DevicePathLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/PciHostBridgeLib.h>\r
+#include <Library/PciLib.h>\r
+#include <Library/QemuFwCfgLib.h>\r
+#include "PciHostBridge.h"\r
+\r
+\r
+#pragma pack(1)\r
+typedef struct {\r
+  ACPI_HID_DEVICE_PATH     AcpiDevicePath;\r
+  EFI_DEVICE_PATH_PROTOCOL EndDevicePath;\r
+} OVMF_PCI_ROOT_BRIDGE_DEVICE_PATH;\r
+#pragma pack ()\r
+\r
 \r
 GLOBAL_REMOVE_IF_UNREFERENCED\r
 CHAR16 *mPciHostBridgeLibAcpiAddressSpaceTypeStr[] = {\r
   L"Mem", L"I/O", L"Bus"\r
 };\r
 \r
+\r
+STATIC\r
+CONST\r
+OVMF_PCI_ROOT_BRIDGE_DEVICE_PATH mRootBridgeDevicePathTemplate = {\r
+  {\r
+    {\r
+      ACPI_DEVICE_PATH,\r
+      ACPI_DP,\r
+      {\r
+        (UINT8) (sizeof(ACPI_HID_DEVICE_PATH)),\r
+        (UINT8) ((sizeof(ACPI_HID_DEVICE_PATH)) >> 8)\r
+      }\r
+    },\r
+    EISA_PNP_ID(0x0A03), // HID\r
+    0                    // UID\r
+  },\r
+\r
+  {\r
+    END_DEVICE_PATH_TYPE,\r
+    END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
+    {\r
+      END_DEVICE_PATH_LENGTH,\r
+      0\r
+    }\r
+  }\r
+};\r
+\r
+STATIC PCI_ROOT_BRIDGE_APERTURE mNonExistAperture = { MAX_UINT64, 0 };\r
+\r
+/**\r
+  Initialize a PCI_ROOT_BRIDGE structure.\r
+\r
+  @param[in]  Supports         Supported attributes.\r
+\r
+  @param[in]  Attributes       Initial attributes.\r
+\r
+  @param[in]  AllocAttributes  Allocation attributes.\r
+\r
+  @param[in]  RootBusNumber    The bus number to store in RootBus.\r
+\r
+  @param[in]  MaxSubBusNumber  The inclusive maximum bus number that can be\r
+                               assigned to any subordinate bus found behind any\r
+                               PCI bridge hanging off this root bus.\r
+\r
+                               The caller is repsonsible for ensuring that\r
+                               RootBusNumber <= MaxSubBusNumber. If\r
+                               RootBusNumber equals MaxSubBusNumber, then the\r
+                               root bus has no room for subordinate buses.\r
+\r
+  @param[in]  Io               IO aperture.\r
+\r
+  @param[in]  Mem              MMIO aperture.\r
+\r
+  @param[in]  MemAbove4G       MMIO aperture above 4G.\r
+\r
+  @param[in]  PMem             Prefetchable MMIO aperture.\r
+\r
+  @param[in]  PMemAbove4G      Prefetchable MMIO aperture above 4G.\r
+\r
+  @param[out] RootBus          The PCI_ROOT_BRIDGE structure (allocated by the\r
+                               caller) that should be filled in by this\r
+                               function.\r
+\r
+  @retval EFI_SUCCESS           Initialization successful. A device path\r
+                                consisting of an ACPI device path node, with\r
+                                UID = RootBusNumber, has been allocated and\r
+                                linked into RootBus.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.\r
+**/\r
+EFI_STATUS\r
+InitRootBridge (\r
+  IN  UINT64                   Supports,\r
+  IN  UINT64                   Attributes,\r
+  IN  UINT64                   AllocAttributes,\r
+  IN  UINT8                    RootBusNumber,\r
+  IN  UINT8                    MaxSubBusNumber,\r
+  IN  PCI_ROOT_BRIDGE_APERTURE *Io,\r
+  IN  PCI_ROOT_BRIDGE_APERTURE *Mem,\r
+  IN  PCI_ROOT_BRIDGE_APERTURE *MemAbove4G,\r
+  IN  PCI_ROOT_BRIDGE_APERTURE *PMem,\r
+  IN  PCI_ROOT_BRIDGE_APERTURE *PMemAbove4G,\r
+  OUT PCI_ROOT_BRIDGE          *RootBus\r
+  )\r
+{\r
+  OVMF_PCI_ROOT_BRIDGE_DEVICE_PATH *DevicePath;\r
+\r
+  //\r
+  // Be safe if other fields are added to PCI_ROOT_BRIDGE later.\r
+  //\r
+  ZeroMem (RootBus, sizeof *RootBus);\r
+\r
+  RootBus->Segment = 0;\r
+\r
+  RootBus->Supports   = Supports;\r
+  RootBus->Attributes = Attributes;\r
+\r
+  RootBus->DmaAbove4G = FALSE;\r
+\r
+  RootBus->AllocationAttributes = AllocAttributes;\r
+  RootBus->Bus.Base  = RootBusNumber;\r
+  RootBus->Bus.Limit = MaxSubBusNumber;\r
+  CopyMem (&RootBus->Io, Io, sizeof (*Io));\r
+  CopyMem (&RootBus->Mem, Mem, sizeof (*Mem));\r
+  CopyMem (&RootBus->MemAbove4G, MemAbove4G, sizeof (*MemAbove4G));\r
+  CopyMem (&RootBus->PMem, PMem, sizeof (*PMem));\r
+  CopyMem (&RootBus->PMemAbove4G, PMemAbove4G, sizeof (*PMemAbove4G));\r
+\r
+  RootBus->NoExtendedConfigSpace = (PcdGet16 (PcdOvmfHostBridgePciDevId) !=\r
+                                    INTEL_Q35_MCH_DEVICE_ID);\r
+\r
+  DevicePath = AllocateCopyPool (sizeof mRootBridgeDevicePathTemplate,\r
+                 &mRootBridgeDevicePathTemplate);\r
+  if (DevicePath == NULL) {\r
+    DEBUG ((EFI_D_ERROR, "%a: %r\n", __FUNCTION__, EFI_OUT_OF_RESOURCES));\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  DevicePath->AcpiDevicePath.UID = RootBusNumber;\r
+  RootBus->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)DevicePath;\r
+\r
+  DEBUG ((EFI_D_INFO,\r
+    "%a: populated root bus %d, with room for %d subordinate bus(es)\n",\r
+    __FUNCTION__, RootBusNumber, MaxSubBusNumber - RootBusNumber));\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Uninitialize a PCI_ROOT_BRIDGE structure set up with InitRootBridge().\r
+\r
+  param[in] RootBus  The PCI_ROOT_BRIDGE structure, allocated by the caller and\r
+                     initialized with InitRootBridge(), that should be\r
+                     uninitialized. This function doesn't free RootBus.\r
+**/\r
+STATIC\r
+VOID\r
+UninitRootBridge (\r
+  IN PCI_ROOT_BRIDGE *RootBus\r
+  )\r
+{\r
+  FreePool (RootBus->DevicePath);\r
+}\r
+\r
+\r
 /**\r
   Return all the root bridge instances in an array.\r
 \r
@@ -37,10 +203,167 @@ PciHostBridgeGetRootBridges (
   UINTN *Count\r
   )\r
 {\r
+  EFI_STATUS           Status;\r
+  FIRMWARE_CONFIG_ITEM FwCfgItem;\r
+  UINTN                FwCfgSize;\r
+  UINT64               ExtraRootBridges;\r
+  PCI_ROOT_BRIDGE      *Bridges;\r
+  UINTN                Initialized;\r
+  UINTN                LastRootBridgeNumber;\r
+  UINTN                RootBridgeNumber;\r
+  UINT64               Attributes;\r
+  UINT64               AllocationAttributes;\r
+  PCI_ROOT_BRIDGE_APERTURE Io;\r
+  PCI_ROOT_BRIDGE_APERTURE Mem;\r
+  PCI_ROOT_BRIDGE_APERTURE MemAbove4G;\r
+\r
+  if (PcdGetBool (PcdPciDisableBusEnumeration)) {\r
+    return ScanForRootBridges (Count);\r
+  }\r
+\r
+  ZeroMem (&Io, sizeof (Io));\r
+  ZeroMem (&Mem, sizeof (Mem));\r
+  ZeroMem (&MemAbove4G, sizeof (MemAbove4G));\r
+\r
+  Attributes = EFI_PCI_ATTRIBUTE_IDE_PRIMARY_IO |\r
+    EFI_PCI_ATTRIBUTE_IDE_SECONDARY_IO |\r
+    EFI_PCI_ATTRIBUTE_ISA_IO_16 |\r
+    EFI_PCI_ATTRIBUTE_ISA_MOTHERBOARD_IO |\r
+    EFI_PCI_ATTRIBUTE_VGA_MEMORY |\r
+    EFI_PCI_ATTRIBUTE_VGA_IO_16 |\r
+    EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO_16;\r
+\r
+  AllocationAttributes = EFI_PCI_HOST_BRIDGE_COMBINE_MEM_PMEM;\r
+  if (PcdGet64 (PcdPciMmio64Size) > 0) {\r
+    AllocationAttributes |= EFI_PCI_HOST_BRIDGE_MEM64_DECODE;\r
+    MemAbove4G.Base = PcdGet64 (PcdPciMmio64Base);\r
+    MemAbove4G.Limit = PcdGet64 (PcdPciMmio64Base) +\r
+                       PcdGet64 (PcdPciMmio64Size) - 1;\r
+  } else {\r
+    CopyMem (&MemAbove4G, &mNonExistAperture, sizeof (mNonExistAperture));\r
+  }\r
+\r
+  Io.Base = PcdGet64 (PcdPciIoBase);\r
+  Io.Limit = PcdGet64 (PcdPciIoBase) + (PcdGet64 (PcdPciIoSize) - 1);\r
+  Mem.Base = PcdGet64 (PcdPciMmio32Base);\r
+  Mem.Limit = PcdGet64 (PcdPciMmio32Base) + (PcdGet64 (PcdPciMmio32Size) - 1);\r
+\r
   *Count = 0;\r
+\r
+  //\r
+  // QEMU provides the number of extra root buses, shortening the exhaustive\r
+  // search below. If there is no hint, the feature is missing.\r
+  //\r
+  Status = QemuFwCfgFindFile ("etc/extra-pci-roots", &FwCfgItem, &FwCfgSize);\r
+  if (EFI_ERROR (Status) || FwCfgSize != sizeof ExtraRootBridges) {\r
+    ExtraRootBridges = 0;\r
+  } else {\r
+    QemuFwCfgSelectItem (FwCfgItem);\r
+    QemuFwCfgReadBytes (FwCfgSize, &ExtraRootBridges);\r
+\r
+    if (ExtraRootBridges > PCI_MAX_BUS) {\r
+      DEBUG ((EFI_D_ERROR, "%a: invalid count of extra root buses (%Lu) "\r
+        "reported by QEMU\n", __FUNCTION__, ExtraRootBridges));\r
+      return NULL;\r
+    }\r
+    DEBUG ((EFI_D_INFO, "%a: %Lu extra root buses reported by QEMU\n",\r
+      __FUNCTION__, ExtraRootBridges));\r
+  }\r
+\r
+  //\r
+  // Allocate the "main" root bridge, and any extra root bridges.\r
+  //\r
+  Bridges = AllocatePool ((1 + (UINTN)ExtraRootBridges) * sizeof *Bridges);\r
+  if (Bridges == NULL) {\r
+    DEBUG ((EFI_D_ERROR, "%a: %r\n", __FUNCTION__, EFI_OUT_OF_RESOURCES));\r
+    return NULL;\r
+  }\r
+  Initialized = 0;\r
+\r
+  //\r
+  // The "main" root bus is always there.\r
+  //\r
+  LastRootBridgeNumber = 0;\r
+\r
+  //\r
+  // Scan all other root buses. If function 0 of any device on a bus returns a\r
+  // VendorId register value different from all-bits-one, then that bus is\r
+  // alive.\r
+  //\r
+  for (RootBridgeNumber = 1;\r
+       RootBridgeNumber <= PCI_MAX_BUS && Initialized < ExtraRootBridges;\r
+       ++RootBridgeNumber) {\r
+    UINTN Device;\r
+\r
+    for (Device = 0; Device <= PCI_MAX_DEVICE; ++Device) {\r
+      if (PciRead16 (PCI_LIB_ADDRESS (RootBridgeNumber, Device, 0,\r
+                       PCI_VENDOR_ID_OFFSET)) != MAX_UINT16) {\r
+        break;\r
+      }\r
+    }\r
+    if (Device <= PCI_MAX_DEVICE) {\r
+      //\r
+      // Found the next root bus. We can now install the *previous* one,\r
+      // because now we know how big a bus number range *that* one has, for any\r
+      // subordinate buses that might exist behind PCI bridges hanging off it.\r
+      //\r
+      Status = InitRootBridge (\r
+        Attributes,\r
+        Attributes,\r
+        AllocationAttributes,\r
+        (UINT8) LastRootBridgeNumber,\r
+        (UINT8) (RootBridgeNumber - 1),\r
+        &Io,\r
+        &Mem,\r
+        &MemAbove4G,\r
+        &mNonExistAperture,\r
+        &mNonExistAperture,\r
+        &Bridges[Initialized]\r
+        );\r
+      if (EFI_ERROR (Status)) {\r
+        goto FreeBridges;\r
+      }\r
+      ++Initialized;\r
+      LastRootBridgeNumber = RootBridgeNumber;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Install the last root bus (which might be the only, ie. main, root bus, if\r
+  // we've found no extra root buses).\r
+  //\r
+  Status = InitRootBridge (\r
+    Attributes,\r
+    Attributes,\r
+    AllocationAttributes,\r
+    (UINT8) LastRootBridgeNumber,\r
+    PCI_MAX_BUS,\r
+    &Io,\r
+    &Mem,\r
+    &MemAbove4G,\r
+    &mNonExistAperture,\r
+    &mNonExistAperture,\r
+    &Bridges[Initialized]\r
+    );\r
+  if (EFI_ERROR (Status)) {\r
+    goto FreeBridges;\r
+  }\r
+  ++Initialized;\r
+\r
+  *Count = Initialized;\r
+  return Bridges;\r
+\r
+FreeBridges:\r
+  while (Initialized > 0) {\r
+    --Initialized;\r
+    UninitRootBridge (&Bridges[Initialized]);\r
+  }\r
+\r
+  FreePool (Bridges);\r
   return NULL;\r
 }\r
 \r
+\r
 /**\r
   Free the root bridge instances array returned from\r
   PciHostBridgeGetRootBridges().\r
@@ -55,9 +378,20 @@ PciHostBridgeFreeRootBridges (
   UINTN           Count\r
   )\r
 {\r
-  return;\r
+  if (Bridges == NULL && Count == 0) {\r
+    return;\r
+  }\r
+  ASSERT (Bridges != NULL && Count > 0);\r
+\r
+  do {\r
+    --Count;\r
+    UninitRootBridge (&Bridges[Count]);\r
+  } while (Count > 0);\r
+\r
+  FreePool (Bridges);\r
 }\r
 \r
+\r
 /**\r
   Inform the platform that the resource conflict happens.\r
 \r
@@ -89,9 +423,7 @@ PciHostBridgeResourceConflict (
     DEBUG ((EFI_D_ERROR, "RootBridge[%d]:\n", RootBridgeIndex++));\r
     for (; Descriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR; Descriptor++) {\r
       ASSERT (Descriptor->ResType <\r
-              (sizeof (mPciHostBridgeLibAcpiAddressSpaceTypeStr) /\r
-               sizeof (mPciHostBridgeLibAcpiAddressSpaceTypeStr[0])\r
-               )\r
+              ARRAY_SIZE (mPciHostBridgeLibAcpiAddressSpaceTypeStr)\r
               );\r
       DEBUG ((EFI_D_ERROR, " %s: Length/Alignment = 0x%lx / 0x%lx\n",\r
               mPciHostBridgeLibAcpiAddressSpaceTypeStr[Descriptor->ResType],\r