]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/Library/QemuBootOrderLib/ExtraRootBusMap.c
OvmfPkg: QemuBootOrderLib: introduce ExtraRootBusMap
[mirror_edk2.git] / OvmfPkg / Library / QemuBootOrderLib / ExtraRootBusMap.c
diff --git a/OvmfPkg/Library/QemuBootOrderLib/ExtraRootBusMap.c b/OvmfPkg/Library/QemuBootOrderLib/ExtraRootBusMap.c
new file mode 100644 (file)
index 0000000..ec42214
--- /dev/null
@@ -0,0 +1,313 @@
+/** @file\r
+  Map positions of extra PCI root buses to bus numbers.\r
+\r
+  Copyright (C) 2015, Red Hat, Inc.\r
+\r
+  This program and the accompanying materials are licensed and made available\r
+  under the terms and conditions of the BSD License which accompanies this\r
+  distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
+  WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+**/\r
+\r
+#include <Library/DebugLib.h>\r
+#include <Library/DevicePathLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/OrderedCollectionLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Protocol/DevicePath.h>\r
+#include <Protocol/PciRootBridgeIo.h>\r
+\r
+#include "ExtraRootBusMap.h"\r
+\r
+//\r
+// The BusNumbers field is an array with Count elements. The elements increase\r
+// strictry monotonically. Zero is not an element (because the zero bus number\r
+// belongs to the "main" root bus, never to an extra root bus). Offset N in the\r
+// array maps the extra root bus with position (N+1) to its bus number (because\r
+// the root bus with position 0 is always the main root bus, therefore we don't\r
+// store it).\r
+//\r
+// If there are no extra root buses in the system, then Count is 0, and\r
+// BusNumbers is NULL.\r
+//\r
+struct EXTRA_ROOT_BUS_MAP_STRUCT {\r
+  UINT32 *BusNumbers;\r
+  UINTN  Count;\r
+};\r
+\r
+\r
+/**\r
+  An ORDERED_COLLECTION_USER_COMPARE function that compares root bridge\r
+  protocol device paths based on UID.\r
+\r
+  @param[in] UserStruct1  Pointer to the first ACPI_HID_DEVICE_PATH.\r
+\r
+  @param[in] UserStruct2  Pointer to the second ACPI_HID_DEVICE_PATH.\r
+\r
+  @retval <0  If UserStruct1 compares less than UserStruct2.\r
+\r
+  @retval  0  If UserStruct1 compares equal to UserStruct2.\r
+\r
+  @retval >0  If UserStruct1 compares greater than UserStruct2.\r
+**/\r
+STATIC\r
+INTN\r
+EFIAPI\r
+RootBridgePathCompare (\r
+  IN CONST VOID *UserStruct1,\r
+  IN CONST VOID *UserStruct2\r
+  )\r
+{\r
+  CONST ACPI_HID_DEVICE_PATH *Acpi1;\r
+  CONST ACPI_HID_DEVICE_PATH *Acpi2;\r
+\r
+  Acpi1 = UserStruct1;\r
+  Acpi2 = UserStruct2;\r
+\r
+  return Acpi1->UID < Acpi2->UID ? -1 :\r
+         Acpi1->UID > Acpi2->UID ?  1 :\r
+         0;\r
+}\r
+\r
+\r
+/**\r
+  An ORDERED_COLLECTION_KEY_COMPARE function that compares a root bridge\r
+  protocol device path against a UID.\r
+\r
+  @param[in] StandaloneKey  Pointer to the bare UINT32 UID.\r
+\r
+  @param[in] UserStruct     Pointer to the ACPI_HID_DEVICE_PATH with the\r
+                            embedded UINT32 UID.\r
+\r
+  @retval <0  If StandaloneKey compares less than UserStruct's key.\r
+\r
+  @retval  0  If StandaloneKey compares equal to UserStruct's key.\r
+\r
+  @retval >0  If StandaloneKey compares greater than UserStruct's key.\r
+**/\r
+STATIC\r
+INTN\r
+EFIAPI\r
+RootBridgePathKeyCompare (\r
+  IN CONST VOID *StandaloneKey,\r
+  IN CONST VOID *UserStruct\r
+  )\r
+{\r
+  CONST UINT32               *Uid;\r
+  CONST ACPI_HID_DEVICE_PATH *Acpi;\r
+\r
+  Uid  = StandaloneKey;\r
+  Acpi = UserStruct;\r
+\r
+  return *Uid < Acpi->UID ? -1 :\r
+         *Uid > Acpi->UID ?  1 :\r
+         0;\r
+}\r
+\r
+\r
+/**\r
+  Create a structure that maps the relative positions of PCI root buses to bus\r
+  numbers.\r
+\r
+  In the "bootorder" fw_cfg file, QEMU refers to extra PCI root buses by their\r
+  positions, in relative root bus number order, not by their actual PCI bus\r
+  numbers. The ACPI HID device path nodes however that are associated with\r
+  PciRootBridgeIo protocol instances in the system have their UID fields set to\r
+  the bus numbers. Create a map that gives, for each extra PCI root bus's\r
+  position (ie. "serial number") its actual PCI bus number.\r
+\r
+  @param[out] ExtraRootBusMap  The data structure implementing the map.\r
+\r
+  @retval EFI_SUCCESS           ExtraRootBusMap has been populated.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.\r
+\r
+  @retval EFI_ALREADY_STARTED   A duplicate root bus number has been found in\r
+                                the system. (This should never happen.)\r
+\r
+  @return                       Error codes returned by\r
+                                gBS->LocateHandleBuffer() and\r
+                                gBS->HandleProtocol().\r
+\r
+**/\r
+EFI_STATUS\r
+CreateExtraRootBusMap (\r
+  OUT EXTRA_ROOT_BUS_MAP **ExtraRootBusMap\r
+  )\r
+{\r
+  EFI_STATUS               Status;\r
+  UINTN                    NumHandles;\r
+  EFI_HANDLE               *Handles;\r
+  ORDERED_COLLECTION       *Collection;\r
+  EXTRA_ROOT_BUS_MAP       *Map;\r
+  UINTN                    Idx;\r
+  ORDERED_COLLECTION_ENTRY *Entry, *Entry2;\r
+\r
+  //\r
+  // Handles and Collection are temporary / helper variables, while in Map we\r
+  // build the return value.\r
+  //\r
+\r
+  Status = gBS->LocateHandleBuffer (ByProtocol,\r
+                  &gEfiPciRootBridgeIoProtocolGuid, NULL /* SearchKey */,\r
+                  &NumHandles, &Handles);\r
+  if (EFI_ERROR (Status))  {\r
+    return Status;\r
+  }\r
+\r
+  Collection = OrderedCollectionInit (RootBridgePathCompare,\r
+                 RootBridgePathKeyCompare);\r
+  if (Collection == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto FreeHandles;\r
+  }\r
+\r
+  Map = AllocateZeroPool (sizeof *Map);\r
+  if (Map == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto FreeCollection;\r
+  }\r
+\r
+  //\r
+  // Collect the ACPI device path protocols of the root bridges.\r
+  //\r
+  for (Idx = 0; Idx < NumHandles; ++Idx) {\r
+    EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
+\r
+    Status = gBS->HandleProtocol (Handles[Idx], &gEfiDevicePathProtocolGuid,\r
+                    (VOID**)&DevicePath);\r
+    if (EFI_ERROR (Status)) {\r
+      goto FreeMap;\r
+    }\r
+\r
+    //\r
+    // Examine if the device path is an ACPI HID one, and if so, if UID is\r
+    // nonzero (ie. the root bridge that the bus number belongs to is "extra",\r
+    // not the main one). In that case, link the device path into Collection.\r
+    //\r
+    if (DevicePathType (DevicePath) == ACPI_DEVICE_PATH &&\r
+        DevicePathSubType (DevicePath) == ACPI_DP &&\r
+        ((ACPI_HID_DEVICE_PATH *)DevicePath)->HID == EISA_PNP_ID(0x0A03) &&\r
+        ((ACPI_HID_DEVICE_PATH *)DevicePath)->UID > 0) {\r
+      Status = OrderedCollectionInsert (Collection, NULL, DevicePath);\r
+      if (EFI_ERROR (Status)) {\r
+        goto FreeMap;\r
+      }\r
+      ++Map->Count;\r
+    }\r
+  }\r
+\r
+  if (Map->Count > 0) {\r
+    //\r
+    // At least one extra PCI root bus exists.\r
+    //\r
+    Map->BusNumbers = AllocatePool (Map->Count * sizeof *Map->BusNumbers);\r
+    if (Map->BusNumbers == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto FreeMap;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Now collect the bus numbers of the extra PCI root buses into Map.\r
+  //\r
+  Idx = 0;\r
+  Entry = OrderedCollectionMin (Collection);\r
+  while (Idx < Map->Count) {\r
+    ACPI_HID_DEVICE_PATH *Acpi;\r
+\r
+    ASSERT (Entry != NULL);\r
+    Acpi = OrderedCollectionUserStruct (Entry);\r
+    Map->BusNumbers[Idx] = Acpi->UID;\r
+    DEBUG ((EFI_D_VERBOSE,\r
+      "%a: extra bus position 0x%Lx maps to bus number (UID) 0x%x\n",\r
+      __FUNCTION__, (UINT64)(Idx + 1), Acpi->UID));\r
+    ++Idx;\r
+    Entry = OrderedCollectionNext (Entry);\r
+  }\r
+  ASSERT (Entry == NULL);\r
+\r
+  *ExtraRootBusMap = Map;\r
+  Status = EFI_SUCCESS;\r
+\r
+  //\r
+  // Fall through in order to release temporaries.\r
+  //\r
+\r
+FreeMap:\r
+  if (EFI_ERROR (Status)) {\r
+    if (Map->BusNumbers != NULL) {\r
+      FreePool (Map->BusNumbers);\r
+    }\r
+    FreePool (Map);\r
+  }\r
+\r
+FreeCollection:\r
+  for (Entry = OrderedCollectionMin (Collection); Entry != NULL;\r
+       Entry = Entry2) {\r
+    Entry2 = OrderedCollectionNext (Entry);\r
+    OrderedCollectionDelete (Collection, Entry, NULL);\r
+  }\r
+  OrderedCollectionUninit (Collection);\r
+\r
+FreeHandles:\r
+  FreePool (Handles);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Release a map created with CreateExtraRootBusMap().\r
+\r
+  @param[in] ExtraRootBusMap  The map to release.\r
+*/\r
+VOID\r
+DestroyExtraRootBusMap (\r
+  IN EXTRA_ROOT_BUS_MAP *ExtraRootBusMap\r
+  )\r
+{\r
+  if (ExtraRootBusMap->BusNumbers != NULL) {\r
+    FreePool (ExtraRootBusMap->BusNumbers);\r
+  }\r
+  FreePool (ExtraRootBusMap);\r
+}\r
+\r
+/**\r
+  Map the position (serial number) of an extra PCI root bus to its bus number.\r
+\r
+  @param[in]  ExtraRootBusMap  The map created with CreateExtraRootBusMap();\r
+\r
+  @param[in]  RootBusPos       The extra PCI root bus position to map.\r
+\r
+  @param[out] RootBusNr        The bus number belonging to the extra PCI root\r
+                               bus identified by RootBusPos.\r
+\r
+  @retval EFI_INVALID_PARAMETER  RootBusPos is zero. The zero position\r
+                                 identifies the main root bus, whose bus number\r
+                                 is always zero, and is therefore never\r
+                                 maintained in ExtraRootBusMap.\r
+\r
+  @retval EFI_NOT_FOUND          RootBusPos is not found in ExtraRootBusMap.\r
+\r
+  @retval EFI_SUCCESS            Mapping successful.\r
+**/\r
+EFI_STATUS\r
+MapRootBusPosToBusNr (\r
+  IN  CONST EXTRA_ROOT_BUS_MAP *ExtraRootBusMap,\r
+  IN  UINT64                   RootBusPos,\r
+  OUT UINT32                   *RootBusNr\r
+  )\r
+{\r
+  if (RootBusPos == 0) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  if (RootBusPos > ExtraRootBusMap->Count) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+  *RootBusNr = ExtraRootBusMap->BusNumbers[RootBusPos - 1];\r
+  return EFI_SUCCESS;\r
+}\r