]> git.proxmox.com Git - mirror_edk2.git/commitdiff
OvmfPkg: QemuBootOrderLib: introduce ExtraRootBusMap
authorLaszlo Ersek <lersek@redhat.com>
Tue, 14 Jul 2015 12:02:39 +0000 (12:02 +0000)
committerlersek <lersek@Edk2>
Tue, 14 Jul 2015 12:02:39 +0000 (12:02 +0000)
SeaBIOS requires the OpenFirmware device paths exported in the "bootorder"
fw-cfg file to refer to extra (PXB) root buses by their relative positions
(in increasing bus number order) rather than by actual bus numbers.

However, OVMF's PCI host bridge / root bridge driver creates PciRoot(UID)
device path nodes for extra PCI root buses with UID=bus_nr, not position.
(These ACPI devpath UID values must, and do, match the UID values exposed
in QEMU's ACPI payload, generated for PXB root buses.)

Therefore the boot order matching logic will have to map extra root bus
positions to bus numbers. Add a small group of utility functions to help
with that.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@17964 6f19259b-4bc3-4df7-8a09-765794883524

OvmfPkg/Library/QemuBootOrderLib/ExtraRootBusMap.c [new file with mode: 0644]
OvmfPkg/Library/QemuBootOrderLib/ExtraRootBusMap.h [new file with mode: 0644]
OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.inf

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
diff --git a/OvmfPkg/Library/QemuBootOrderLib/ExtraRootBusMap.h b/OvmfPkg/Library/QemuBootOrderLib/ExtraRootBusMap.h
new file mode 100644 (file)
index 0000000..e2dbc38
--- /dev/null
@@ -0,0 +1,40 @@
+/** @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
+#ifndef __EXTRA_ROOT_BUS_MAP_H__\r
+#define __EXTRA_ROOT_BUS_MAP_H__\r
+\r
+/**\r
+  Incomplete ("opaque") data type implementing the map.\r
+**/\r
+typedef struct EXTRA_ROOT_BUS_MAP_STRUCT EXTRA_ROOT_BUS_MAP;\r
+\r
+EFI_STATUS\r
+CreateExtraRootBusMap (\r
+  OUT EXTRA_ROOT_BUS_MAP **ExtraRootBusMap\r
+  );\r
+\r
+VOID\r
+DestroyExtraRootBusMap (\r
+  IN EXTRA_ROOT_BUS_MAP *ExtraRootBusMap\r
+  );\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
+#endif\r
index e972ae99238622158867f1759b07c920e15228f4..102432848bca2ad43d9da3e9c0c4864ee7eb4b6b 100644 (file)
@@ -32,6 +32,7 @@
 \r
 [Sources]\r
   QemuBootOrderLib.c\r
+  ExtraRootBusMap.c\r
 \r
 [Packages]\r
   MdePkg/MdePkg.dec\r
@@ -49,6 +50,7 @@
   PrintLib\r
   DevicePathLib\r
   BaseMemoryLib\r
+  OrderedCollectionLib\r
 \r
 [Guids]\r
   gEfiGlobalVariableGuid\r
@@ -60,3 +62,7 @@
 \r
 [Pcd]\r
   gEfiMdePkgTokenSpaceGuid.PcdPlatformBootTimeOut\r
+\r
+[Protocols]\r
+  gEfiDevicePathProtocolGuid                            ## CONSUMES\r
+  gEfiPciRootBridgeIoProtocolGuid                       ## CONSUMES\r