This completes the transition to the new BDS.
The FILE_GUID in "QemuBootOrderLib.inf" is intentionally not changed.
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Gary Ching-Pang Lin <glin@suse.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ruiyu Ni <ruiyu.ni@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
UefiBootManagerLib|MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf\r
PlatformBootManagerLib|ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf\r
CustomizedDisplayLib|MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.inf\r
- QemuBootOrderLib|OvmfPkg/Library/QemuNewBootOrderLib/QemuBootOrderLib.inf\r
+ QemuBootOrderLib|OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.inf\r
FileExplorerLib|MdeModulePkg/Library/FileExplorerLib/FileExplorerLib.inf\r
PciPcdProducerLib|ArmVirtPkg/Library/FdtPciPcdProducerLib/FdtPciPcdProducerLib.inf\r
\r
UefiBootManagerLib|MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf\r
PlatformBootManagerLib|ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf\r
CustomizedDisplayLib|MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.inf\r
- QemuBootOrderLib|OvmfPkg/Library/QemuNewBootOrderLib/QemuBootOrderLib.inf\r
+ QemuBootOrderLib|OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.inf\r
FileExplorerLib|MdeModulePkg/Library/FileExplorerLib/FileExplorerLib.inf\r
PciPcdProducerLib|ArmVirtPkg/Library/FdtPciPcdProducerLib/FdtPciPcdProducerLib.inf\r
\r
--- /dev/null
+/** @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
--- /dev/null
+/** @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
--- /dev/null
+/** @file\r
+ Rewrite the BootOrder NvVar based on QEMU's "bootorder" fw_cfg file.\r
+\r
+ Copyright (C) 2012 - 2014, Red Hat, Inc.\r
+ Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.<BR>\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/QemuFwCfgLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiBootManagerLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/PrintLib.h>\r
+#include <Library/DevicePathLib.h>\r
+#include <Library/QemuBootOrderLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Guid/GlobalVariable.h>\r
+#include <Guid/VirtioMmioTransport.h>\r
+\r
+#include "ExtraRootBusMap.h"\r
+\r
+/**\r
+ OpenFirmware to UEFI device path translation output buffer size in CHAR16's.\r
+**/\r
+#define TRANSLATION_OUTPUT_SIZE 0x100\r
+\r
+/**\r
+ Output buffer size for OpenFirmware to UEFI device path fragment translation,\r
+ in CHAR16's, for a sequence of PCI bridges.\r
+**/\r
+#define BRIDGE_TRANSLATION_OUTPUT_SIZE 0x40\r
+\r
+/**\r
+ Numbers of nodes in OpenFirmware device paths that are required and examined.\r
+**/\r
+#define REQUIRED_PCI_OFW_NODES 2\r
+#define REQUIRED_MMIO_OFW_NODES 1\r
+#define EXAMINED_OFW_NODES 6\r
+\r
+\r
+/**\r
+ Simple character classification routines, corresponding to POSIX class names\r
+ and ASCII encoding.\r
+**/\r
+STATIC\r
+BOOLEAN\r
+IsAlnum (\r
+ IN CHAR8 Chr\r
+ )\r
+{\r
+ return (('0' <= Chr && Chr <= '9') ||\r
+ ('A' <= Chr && Chr <= 'Z') ||\r
+ ('a' <= Chr && Chr <= 'z')\r
+ );\r
+}\r
+\r
+\r
+STATIC\r
+BOOLEAN\r
+IsDriverNamePunct (\r
+ IN CHAR8 Chr\r
+ )\r
+{\r
+ return (Chr == ',' || Chr == '.' || Chr == '_' ||\r
+ Chr == '+' || Chr == '-'\r
+ );\r
+}\r
+\r
+\r
+STATIC\r
+BOOLEAN\r
+IsPrintNotDelim (\r
+ IN CHAR8 Chr\r
+ )\r
+{\r
+ return (32 <= Chr && Chr <= 126 &&\r
+ Chr != '/' && Chr != '@' && Chr != ':');\r
+}\r
+\r
+\r
+/**\r
+ Utility types and functions.\r
+**/\r
+typedef struct {\r
+ CONST CHAR8 *Ptr; // not necessarily NUL-terminated\r
+ UINTN Len; // number of non-NUL characters\r
+} SUBSTRING;\r
+\r
+\r
+/**\r
+\r
+ Check if Substring and String have identical contents.\r
+\r
+ The function relies on the restriction that a SUBSTRING cannot have embedded\r
+ NULs either.\r
+\r
+ @param[in] Substring The SUBSTRING input to the comparison.\r
+\r
+ @param[in] String The ASCII string input to the comparison.\r
+\r
+\r
+ @return Whether the inputs have identical contents.\r
+\r
+**/\r
+STATIC\r
+BOOLEAN\r
+SubstringEq (\r
+ IN SUBSTRING Substring,\r
+ IN CONST CHAR8 *String\r
+ )\r
+{\r
+ UINTN Pos;\r
+ CONST CHAR8 *Chr;\r
+\r
+ Pos = 0;\r
+ Chr = String;\r
+\r
+ while (Pos < Substring.Len && Substring.Ptr[Pos] == *Chr) {\r
+ ++Pos;\r
+ ++Chr;\r
+ }\r
+\r
+ return (BOOLEAN)(Pos == Substring.Len && *Chr == '\0');\r
+}\r
+\r
+\r
+/**\r
+\r
+ Parse a comma-separated list of hexadecimal integers into the elements of an\r
+ UINT64 array.\r
+\r
+ Whitespace, "0x" prefixes, leading or trailing commas, sequences of commas,\r
+ or an empty string are not allowed; they are rejected.\r
+\r
+ The function relies on ASCII encoding.\r
+\r
+ @param[in] UnitAddress The substring to parse.\r
+\r
+ @param[out] Result The array, allocated by the caller, to receive\r
+ the parsed values. This parameter may be NULL if\r
+ NumResults is zero on input.\r
+\r
+ @param[in out] NumResults On input, the number of elements allocated for\r
+ Result. On output, the number of elements it has\r
+ taken (or would have taken) to parse the string\r
+ fully.\r
+\r
+\r
+ @retval RETURN_SUCCESS UnitAddress has been fully parsed.\r
+ NumResults is set to the number of parsed\r
+ values; the corresponding elements have\r
+ been set in Result. The rest of Result's\r
+ elements are unchanged.\r
+\r
+ @retval RETURN_BUFFER_TOO_SMALL UnitAddress has been fully parsed.\r
+ NumResults is set to the number of parsed\r
+ values, but elements have been stored only\r
+ up to the input value of NumResults, which\r
+ is less than what has been parsed.\r
+\r
+ @retval RETURN_INVALID_PARAMETER Parse error. The contents of Results is\r
+ indeterminate. NumResults has not been\r
+ changed.\r
+\r
+**/\r
+STATIC\r
+RETURN_STATUS\r
+ParseUnitAddressHexList (\r
+ IN SUBSTRING UnitAddress,\r
+ OUT UINT64 *Result,\r
+ IN OUT UINTN *NumResults\r
+ )\r
+{\r
+ UINTN Entry; // number of entry currently being parsed\r
+ UINT64 EntryVal; // value being constructed for current entry\r
+ CHAR8 PrevChr; // UnitAddress character previously checked\r
+ UINTN Pos; // current position within UnitAddress\r
+ RETURN_STATUS Status;\r
+\r
+ Entry = 0;\r
+ EntryVal = 0;\r
+ PrevChr = ',';\r
+\r
+ for (Pos = 0; Pos < UnitAddress.Len; ++Pos) {\r
+ CHAR8 Chr;\r
+ INT8 Val;\r
+\r
+ Chr = UnitAddress.Ptr[Pos];\r
+ Val = ('a' <= Chr && Chr <= 'f') ? (Chr - 'a' + 10) :\r
+ ('A' <= Chr && Chr <= 'F') ? (Chr - 'A' + 10) :\r
+ ('0' <= Chr && Chr <= '9') ? (Chr - '0' ) :\r
+ -1;\r
+\r
+ if (Val >= 0) {\r
+ if (EntryVal > 0xFFFFFFFFFFFFFFFull) {\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+ EntryVal = LShiftU64 (EntryVal, 4) | Val;\r
+ } else if (Chr == ',') {\r
+ if (PrevChr == ',') {\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+ if (Entry < *NumResults) {\r
+ Result[Entry] = EntryVal;\r
+ }\r
+ ++Entry;\r
+ EntryVal = 0;\r
+ } else {\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+\r
+ PrevChr = Chr;\r
+ }\r
+\r
+ if (PrevChr == ',') {\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+ if (Entry < *NumResults) {\r
+ Result[Entry] = EntryVal;\r
+ Status = RETURN_SUCCESS;\r
+ } else {\r
+ Status = RETURN_BUFFER_TOO_SMALL;\r
+ }\r
+ ++Entry;\r
+\r
+ *NumResults = Entry;\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ A simple array of Boot Option ID's.\r
+**/\r
+typedef struct {\r
+ UINT16 *Data;\r
+ UINTN Allocated;\r
+ UINTN Produced;\r
+} BOOT_ORDER;\r
+\r
+\r
+/**\r
+ Array element tracking an enumerated boot option that has the\r
+ LOAD_OPTION_ACTIVE attribute.\r
+**/\r
+typedef struct {\r
+ CONST EFI_BOOT_MANAGER_LOAD_OPTION *BootOption; // reference only, no\r
+ // ownership\r
+ BOOLEAN Appended; // has been added to a\r
+ // BOOT_ORDER?\r
+} ACTIVE_OPTION;\r
+\r
+\r
+/**\r
+\r
+ Append an active boot option to BootOrder, reallocating the latter if needed.\r
+\r
+ @param[in out] BootOrder The structure pointing to the array and holding\r
+ allocation and usage counters.\r
+\r
+ @param[in] ActiveOption The active boot option whose ID should be\r
+ appended to the array.\r
+\r
+\r
+ @retval RETURN_SUCCESS ID of ActiveOption appended.\r
+\r
+ @retval RETURN_OUT_OF_RESOURCES Memory reallocation failed.\r
+\r
+**/\r
+STATIC\r
+RETURN_STATUS\r
+BootOrderAppend (\r
+ IN OUT BOOT_ORDER *BootOrder,\r
+ IN OUT ACTIVE_OPTION *ActiveOption\r
+ )\r
+{\r
+ if (BootOrder->Produced == BootOrder->Allocated) {\r
+ UINTN AllocatedNew;\r
+ UINT16 *DataNew;\r
+\r
+ ASSERT (BootOrder->Allocated > 0);\r
+ AllocatedNew = BootOrder->Allocated * 2;\r
+ DataNew = ReallocatePool (\r
+ BootOrder->Allocated * sizeof (*BootOrder->Data),\r
+ AllocatedNew * sizeof (*DataNew),\r
+ BootOrder->Data\r
+ );\r
+ if (DataNew == NULL) {\r
+ return RETURN_OUT_OF_RESOURCES;\r
+ }\r
+ BootOrder->Allocated = AllocatedNew;\r
+ BootOrder->Data = DataNew;\r
+ }\r
+\r
+ BootOrder->Data[BootOrder->Produced++] =\r
+ (UINT16) ActiveOption->BootOption->OptionNumber;\r
+ ActiveOption->Appended = TRUE;\r
+ return RETURN_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+\r
+ Create an array of ACTIVE_OPTION elements for a boot option array.\r
+\r
+ @param[in] BootOptions A boot option array, created with\r
+ EfiBootManagerRefreshAllBootOption () and\r
+ EfiBootManagerGetLoadOptions ().\r
+\r
+ @param[in] BootOptionCount The number of elements in BootOptions.\r
+\r
+ @param[out] ActiveOption Pointer to the first element in the new array.\r
+ The caller is responsible for freeing the array\r
+ with FreePool() after use.\r
+\r
+ @param[out] Count Number of elements in the new array.\r
+\r
+\r
+ @retval RETURN_SUCCESS The ActiveOption array has been created.\r
+\r
+ @retval RETURN_NOT_FOUND No active entry has been found in\r
+ BootOptions.\r
+\r
+ @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.\r
+\r
+**/\r
+STATIC\r
+RETURN_STATUS\r
+CollectActiveOptions (\r
+ IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions,\r
+ IN UINTN BootOptionCount,\r
+ OUT ACTIVE_OPTION **ActiveOption,\r
+ OUT UINTN *Count\r
+ )\r
+{\r
+ UINTN Index;\r
+ UINTN ScanMode;\r
+\r
+ *ActiveOption = NULL;\r
+\r
+ //\r
+ // Scan the list twice:\r
+ // - count active entries,\r
+ // - store links to active entries.\r
+ //\r
+ for (ScanMode = 0; ScanMode < 2; ++ScanMode) {\r
+ *Count = 0;\r
+ for (Index = 0; Index < BootOptionCount; Index++) {\r
+ if ((BootOptions[Index].Attributes & LOAD_OPTION_ACTIVE) != 0) {\r
+ if (ScanMode == 1) {\r
+ (*ActiveOption)[*Count].BootOption = &BootOptions[Index];\r
+ (*ActiveOption)[*Count].Appended = FALSE;\r
+ }\r
+ ++*Count;\r
+ }\r
+ }\r
+\r
+ if (ScanMode == 0) {\r
+ if (*Count == 0) {\r
+ return RETURN_NOT_FOUND;\r
+ }\r
+ *ActiveOption = AllocatePool (*Count * sizeof **ActiveOption);\r
+ if (*ActiveOption == NULL) {\r
+ return RETURN_OUT_OF_RESOURCES;\r
+ }\r
+ }\r
+ }\r
+ return RETURN_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ OpenFirmware device path node\r
+**/\r
+typedef struct {\r
+ SUBSTRING DriverName;\r
+ SUBSTRING UnitAddress;\r
+ SUBSTRING DeviceArguments;\r
+} OFW_NODE;\r
+\r
+\r
+/**\r
+\r
+ Parse an OpenFirmware device path node into the caller-allocated OFW_NODE\r
+ structure, and advance in the input string.\r
+\r
+ The node format is mostly parsed after IEEE 1275-1994, 3.2.1.1 "Node names"\r
+ (a leading slash is expected and not returned):\r
+\r
+ /driver-name@unit-address[:device-arguments][<LF>]\r
+\r
+ A single trailing <LF> character is consumed but not returned. A trailing\r
+ <LF> or NUL character terminates the device path.\r
+\r
+ The function relies on ASCII encoding.\r
+\r
+ @param[in out] Ptr Address of the pointer pointing to the start of the\r
+ node string. After successful parsing *Ptr is set to\r
+ the byte immediately following the consumed\r
+ characters. On error it points to the byte that\r
+ caused the error. The input string is never modified.\r
+\r
+ @param[out] OfwNode The members of this structure point into the input\r
+ string, designating components of the node.\r
+ Separators are never included. If "device-arguments"\r
+ is missing, then DeviceArguments.Ptr is set to NULL.\r
+ All components that are present have nonzero length.\r
+\r
+ If the call doesn't succeed, the contents of this\r
+ structure is indeterminate.\r
+\r
+ @param[out] IsFinal In case of successul parsing, this parameter signals\r
+ whether the node just parsed is the final node in the\r
+ device path. The call after a final node will attempt\r
+ to start parsing the next path. If the call doesn't\r
+ succeed, then this parameter is not changed.\r
+\r
+\r
+ @retval RETURN_SUCCESS Parsing successful.\r
+\r
+ @retval RETURN_NOT_FOUND Parsing terminated. *Ptr was (and is)\r
+ pointing to an empty string.\r
+\r
+ @retval RETURN_INVALID_PARAMETER Parse error.\r
+\r
+**/\r
+STATIC\r
+RETURN_STATUS\r
+ParseOfwNode (\r
+ IN OUT CONST CHAR8 **Ptr,\r
+ OUT OFW_NODE *OfwNode,\r
+ OUT BOOLEAN *IsFinal\r
+ )\r
+{\r
+ //\r
+ // A leading slash is expected. End of string is tolerated.\r
+ //\r
+ switch (**Ptr) {\r
+ case '\0':\r
+ return RETURN_NOT_FOUND;\r
+\r
+ case '/':\r
+ ++*Ptr;\r
+ break;\r
+\r
+ default:\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // driver-name\r
+ //\r
+ OfwNode->DriverName.Ptr = *Ptr;\r
+ OfwNode->DriverName.Len = 0;\r
+ while (OfwNode->DriverName.Len < 32 &&\r
+ (IsAlnum (**Ptr) || IsDriverNamePunct (**Ptr))\r
+ ) {\r
+ ++*Ptr;\r
+ ++OfwNode->DriverName.Len;\r
+ }\r
+\r
+ if (OfwNode->DriverName.Len == 0 || OfwNode->DriverName.Len == 32) {\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+\r
+\r
+ //\r
+ // unit-address\r
+ //\r
+ if (**Ptr != '@') {\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+ ++*Ptr;\r
+\r
+ OfwNode->UnitAddress.Ptr = *Ptr;\r
+ OfwNode->UnitAddress.Len = 0;\r
+ while (IsPrintNotDelim (**Ptr)) {\r
+ ++*Ptr;\r
+ ++OfwNode->UnitAddress.Len;\r
+ }\r
+\r
+ if (OfwNode->UnitAddress.Len == 0) {\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+\r
+\r
+ //\r
+ // device-arguments, may be omitted\r
+ //\r
+ OfwNode->DeviceArguments.Len = 0;\r
+ if (**Ptr == ':') {\r
+ ++*Ptr;\r
+ OfwNode->DeviceArguments.Ptr = *Ptr;\r
+\r
+ while (IsPrintNotDelim (**Ptr)) {\r
+ ++*Ptr;\r
+ ++OfwNode->DeviceArguments.Len;\r
+ }\r
+\r
+ if (OfwNode->DeviceArguments.Len == 0) {\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+ }\r
+ else {\r
+ OfwNode->DeviceArguments.Ptr = NULL;\r
+ }\r
+\r
+ switch (**Ptr) {\r
+ case '\n':\r
+ ++*Ptr;\r
+ //\r
+ // fall through\r
+ //\r
+\r
+ case '\0':\r
+ *IsFinal = TRUE;\r
+ break;\r
+\r
+ case '/':\r
+ *IsFinal = FALSE;\r
+ break;\r
+\r
+ default:\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+\r
+ DEBUG ((\r
+ DEBUG_VERBOSE,\r
+ "%a: DriverName=\"%.*a\" UnitAddress=\"%.*a\" DeviceArguments=\"%.*a\"\n",\r
+ __FUNCTION__,\r
+ OfwNode->DriverName.Len, OfwNode->DriverName.Ptr,\r
+ OfwNode->UnitAddress.Len, OfwNode->UnitAddress.Ptr,\r
+ OfwNode->DeviceArguments.Len,\r
+ OfwNode->DeviceArguments.Ptr == NULL ? "" : OfwNode->DeviceArguments.Ptr\r
+ ));\r
+ return RETURN_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+\r
+ Translate a PCI-like array of OpenFirmware device nodes to a UEFI device path\r
+ fragment.\r
+\r
+ @param[in] OfwNode Array of OpenFirmware device nodes to\r
+ translate, constituting the beginning of an\r
+ OpenFirmware device path.\r
+\r
+ @param[in] NumNodes Number of elements in OfwNode.\r
+\r
+ @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with\r
+ CreateExtraRootBusMap(), to be used for\r
+ translating positions of extra root buses to\r
+ bus numbers.\r
+\r
+ @param[out] Translated Destination array receiving the UEFI path\r
+ fragment, allocated by the caller. If the\r
+ return value differs from RETURN_SUCCESS, its\r
+ contents is indeterminate.\r
+\r
+ @param[in out] TranslatedSize On input, the number of CHAR16's in\r
+ Translated. On RETURN_SUCCESS this parameter\r
+ is assigned the number of non-NUL CHAR16's\r
+ written to Translated. In case of other return\r
+ values, TranslatedSize is indeterminate.\r
+\r
+\r
+ @retval RETURN_SUCCESS Translation successful.\r
+\r
+ @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number\r
+ of bytes provided.\r
+\r
+ @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't\r
+ be translated in the current implementation.\r
+\r
+ @retval RETURN_PROTOCOL_ERROR The initial OpenFirmware node refers to an\r
+ extra PCI root bus (by serial number) that\r
+ is invalid according to ExtraPciRoots.\r
+\r
+**/\r
+STATIC\r
+RETURN_STATUS\r
+TranslatePciOfwNodes (\r
+ IN CONST OFW_NODE *OfwNode,\r
+ IN UINTN NumNodes,\r
+ IN CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,\r
+ OUT CHAR16 *Translated,\r
+ IN OUT UINTN *TranslatedSize\r
+ )\r
+{\r
+ UINT32 PciRoot;\r
+ CHAR8 *Comma;\r
+ UINTN FirstNonBridge;\r
+ CHAR16 Bridges[BRIDGE_TRANSLATION_OUTPUT_SIZE];\r
+ UINTN BridgesLen;\r
+ UINT64 PciDevFun[2];\r
+ UINTN NumEntries;\r
+ UINTN Written;\r
+\r
+ //\r
+ // Resolve the PCI root bus number.\r
+ //\r
+ // The initial OFW node for the main root bus (ie. bus number 0) is:\r
+ //\r
+ // /pci@i0cf8\r
+ //\r
+ // For extra root buses, the initial OFW node is\r
+ //\r
+ // /pci@i0cf8,4\r
+ // ^\r
+ // root bus serial number (not PCI bus number)\r
+ //\r
+ if (NumNodes < REQUIRED_PCI_OFW_NODES ||\r
+ !SubstringEq (OfwNode[0].DriverName, "pci")\r
+ ) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ PciRoot = 0;\r
+ Comma = ScanMem8 (OfwNode[0].UnitAddress.Ptr, OfwNode[0].UnitAddress.Len,\r
+ ',');\r
+ if (Comma != NULL) {\r
+ SUBSTRING PciRootSerialSubString;\r
+ UINT64 PciRootSerial;\r
+\r
+ //\r
+ // Parse the root bus serial number from the unit address after the comma.\r
+ //\r
+ PciRootSerialSubString.Ptr = Comma + 1;\r
+ PciRootSerialSubString.Len = OfwNode[0].UnitAddress.Len -\r
+ (PciRootSerialSubString.Ptr -\r
+ OfwNode[0].UnitAddress.Ptr);\r
+ NumEntries = 1;\r
+ if (RETURN_ERROR (ParseUnitAddressHexList (PciRootSerialSubString,\r
+ &PciRootSerial, &NumEntries))) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // Map the extra root bus's serial number to its actual bus number.\r
+ //\r
+ if (EFI_ERROR (MapRootBusPosToBusNr (ExtraPciRoots, PciRootSerial,\r
+ &PciRoot))) {\r
+ return RETURN_PROTOCOL_ERROR;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Translate a sequence of PCI bridges. For each bridge, the OFW node is:\r
+ //\r
+ // pci-bridge@1e[,0]\r
+ // ^ ^\r
+ // PCI slot & function on the parent, holding the bridge\r
+ //\r
+ // and the UEFI device path node is:\r
+ //\r
+ // Pci(0x1E,0x0)\r
+ //\r
+ FirstNonBridge = 1;\r
+ Bridges[0] = L'\0';\r
+ BridgesLen = 0;\r
+ do {\r
+ UINT64 BridgeDevFun[2];\r
+ UINTN BridgesFreeBytes;\r
+\r
+ if (!SubstringEq (OfwNode[FirstNonBridge].DriverName, "pci-bridge")) {\r
+ break;\r
+ }\r
+\r
+ BridgeDevFun[1] = 0;\r
+ NumEntries = sizeof BridgeDevFun / sizeof BridgeDevFun[0];\r
+ if (ParseUnitAddressHexList (OfwNode[FirstNonBridge].UnitAddress,\r
+ BridgeDevFun, &NumEntries) != RETURN_SUCCESS) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ BridgesFreeBytes = sizeof Bridges - BridgesLen * sizeof Bridges[0];\r
+ Written = UnicodeSPrintAsciiFormat (Bridges + BridgesLen, BridgesFreeBytes,\r
+ "/Pci(0x%Lx,0x%Lx)", BridgeDevFun[0], BridgeDevFun[1]);\r
+ BridgesLen += Written;\r
+\r
+ //\r
+ // There's no way to differentiate between "completely used up without\r
+ // truncation" and "truncated", so treat the former as the latter.\r
+ //\r
+ if (BridgesLen + 1 == BRIDGE_TRANSLATION_OUTPUT_SIZE) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ ++FirstNonBridge;\r
+ } while (FirstNonBridge < NumNodes);\r
+\r
+ if (FirstNonBridge == NumNodes) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // Parse the OFW nodes starting with the first non-bridge node.\r
+ //\r
+ PciDevFun[1] = 0;\r
+ NumEntries = sizeof (PciDevFun) / sizeof (PciDevFun[0]);\r
+ if (ParseUnitAddressHexList (\r
+ OfwNode[FirstNonBridge].UnitAddress,\r
+ PciDevFun,\r
+ &NumEntries\r
+ ) != RETURN_SUCCESS\r
+ ) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ if (NumNodes >= FirstNonBridge + 3 &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "ide") &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "drive") &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")\r
+ ) {\r
+ //\r
+ // OpenFirmware device path (IDE disk, IDE CD-ROM):\r
+ //\r
+ // /pci@i0cf8/ide@1,1/drive@0/disk@0\r
+ // ^ ^ ^ ^ ^\r
+ // | | | | master or slave\r
+ // | | | primary or secondary\r
+ // | PCI slot & function holding IDE controller\r
+ // PCI root at system bus port, PIO\r
+ //\r
+ // UEFI device path:\r
+ //\r
+ // PciRoot(0x0)/Pci(0x1,0x1)/Ata(Primary,Master,0x0)\r
+ // ^\r
+ // fixed LUN\r
+ //\r
+ UINT64 Secondary;\r
+ UINT64 Slave;\r
+\r
+ NumEntries = 1;\r
+ if (ParseUnitAddressHexList (\r
+ OfwNode[FirstNonBridge + 1].UnitAddress,\r
+ &Secondary,\r
+ &NumEntries\r
+ ) != RETURN_SUCCESS ||\r
+ Secondary > 1 ||\r
+ ParseUnitAddressHexList (\r
+ OfwNode[FirstNonBridge + 2].UnitAddress,\r
+ &Slave,\r
+ &NumEntries // reuse after previous single-element call\r
+ ) != RETURN_SUCCESS ||\r
+ Slave > 1\r
+ ) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ Written = UnicodeSPrintAsciiFormat (\r
+ Translated,\r
+ *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+ "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Ata(%a,%a,0x0)",\r
+ PciRoot,\r
+ Bridges,\r
+ PciDevFun[0],\r
+ PciDevFun[1],\r
+ Secondary ? "Secondary" : "Primary",\r
+ Slave ? "Slave" : "Master"\r
+ );\r
+ } else if (NumNodes >= FirstNonBridge + 3 &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "pci8086,2922") &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "drive") &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")\r
+ ) {\r
+ //\r
+ // OpenFirmware device path (Q35 SATA disk and CD-ROM):\r
+ //\r
+ // /pci@i0cf8/pci8086,2922@1f,2/drive@1/disk@0\r
+ // ^ ^ ^ ^ ^\r
+ // | | | | device number (fixed 0)\r
+ // | | | channel (port) number\r
+ // | PCI slot & function holding SATA HBA\r
+ // PCI root at system bus port, PIO\r
+ //\r
+ // UEFI device path:\r
+ //\r
+ // PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x1,0xFFFF,0x0)\r
+ // ^ ^ ^\r
+ // | | LUN (always 0 on Q35)\r
+ // | port multiplier port number,\r
+ // | always 0xFFFF on Q35\r
+ // channel (port) number\r
+ //\r
+ UINT64 Channel;\r
+\r
+ NumEntries = 1;\r
+ if (RETURN_ERROR (ParseUnitAddressHexList (\r
+ OfwNode[FirstNonBridge + 1].UnitAddress, &Channel,\r
+ &NumEntries))) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ Written = UnicodeSPrintAsciiFormat (\r
+ Translated,\r
+ *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+ "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Sata(0x%Lx,0xFFFF,0x0)",\r
+ PciRoot,\r
+ Bridges,\r
+ PciDevFun[0],\r
+ PciDevFun[1],\r
+ Channel\r
+ );\r
+ } else if (NumNodes >= FirstNonBridge + 3 &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "isa") &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "fdc") &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "floppy")\r
+ ) {\r
+ //\r
+ // OpenFirmware device path (floppy disk):\r
+ //\r
+ // /pci@i0cf8/isa@1/fdc@03f0/floppy@0\r
+ // ^ ^ ^ ^\r
+ // | | | A: or B:\r
+ // | | ISA controller io-port (hex)\r
+ // | PCI slot holding ISA controller\r
+ // PCI root at system bus port, PIO\r
+ //\r
+ // UEFI device path:\r
+ //\r
+ // PciRoot(0x0)/Pci(0x1,0x0)/Floppy(0x0)\r
+ // ^\r
+ // ACPI UID\r
+ //\r
+ UINT64 AcpiUid;\r
+\r
+ NumEntries = 1;\r
+ if (ParseUnitAddressHexList (\r
+ OfwNode[FirstNonBridge + 2].UnitAddress,\r
+ &AcpiUid,\r
+ &NumEntries\r
+ ) != RETURN_SUCCESS ||\r
+ AcpiUid > 1\r
+ ) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ Written = UnicodeSPrintAsciiFormat (\r
+ Translated,\r
+ *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+ "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Floppy(0x%Lx)",\r
+ PciRoot,\r
+ Bridges,\r
+ PciDevFun[0],\r
+ PciDevFun[1],\r
+ AcpiUid\r
+ );\r
+ } else if (NumNodes >= FirstNonBridge + 2 &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "scsi") &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "disk")\r
+ ) {\r
+ //\r
+ // OpenFirmware device path (virtio-blk disk):\r
+ //\r
+ // /pci@i0cf8/scsi@6[,3]/disk@0,0\r
+ // ^ ^ ^ ^ ^\r
+ // | | | fixed\r
+ // | | PCI function corresponding to disk (optional)\r
+ // | PCI slot holding disk\r
+ // PCI root at system bus port, PIO\r
+ //\r
+ // UEFI device path prefix:\r
+ //\r
+ // PciRoot(0x0)/Pci(0x6,0x0)/HD( -- if PCI function is 0 or absent\r
+ // PciRoot(0x0)/Pci(0x6,0x3)/HD( -- if PCI function is present and nonzero\r
+ //\r
+ Written = UnicodeSPrintAsciiFormat (\r
+ Translated,\r
+ *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+ "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/HD(",\r
+ PciRoot,\r
+ Bridges,\r
+ PciDevFun[0],\r
+ PciDevFun[1]\r
+ );\r
+ } else if (NumNodes >= FirstNonBridge + 3 &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "scsi") &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "channel") &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")\r
+ ) {\r
+ //\r
+ // OpenFirmware device path (virtio-scsi disk):\r
+ //\r
+ // /pci@i0cf8/scsi@7[,3]/channel@0/disk@2,3\r
+ // ^ ^ ^ ^ ^\r
+ // | | | | LUN\r
+ // | | | target\r
+ // | | channel (unused, fixed 0)\r
+ // | PCI slot[, function] holding SCSI controller\r
+ // PCI root at system bus port, PIO\r
+ //\r
+ // UEFI device path prefix:\r
+ //\r
+ // PciRoot(0x0)/Pci(0x7,0x0)/Scsi(0x2,0x3)\r
+ // -- if PCI function is 0 or absent\r
+ // PciRoot(0x0)/Pci(0x7,0x3)/Scsi(0x2,0x3)\r
+ // -- if PCI function is present and nonzero\r
+ //\r
+ UINT64 TargetLun[2];\r
+\r
+ TargetLun[1] = 0;\r
+ NumEntries = sizeof (TargetLun) / sizeof (TargetLun[0]);\r
+ if (ParseUnitAddressHexList (\r
+ OfwNode[FirstNonBridge + 2].UnitAddress,\r
+ TargetLun,\r
+ &NumEntries\r
+ ) != RETURN_SUCCESS\r
+ ) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ Written = UnicodeSPrintAsciiFormat (\r
+ Translated,\r
+ *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+ "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Scsi(0x%Lx,0x%Lx)",\r
+ PciRoot,\r
+ Bridges,\r
+ PciDevFun[0],\r
+ PciDevFun[1],\r
+ TargetLun[0],\r
+ TargetLun[1]\r
+ );\r
+ } else if (NumNodes >= FirstNonBridge + 2 &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "pci8086,5845") &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "namespace")\r
+ ) {\r
+ //\r
+ // OpenFirmware device path (NVMe device):\r
+ //\r
+ // /pci@i0cf8/pci8086,5845@6[,1]/namespace@1,0\r
+ // ^ ^ ^ ^ ^\r
+ // | | | | Extended Unique Identifier\r
+ // | | | | (EUI-64), big endian interp.\r
+ // | | | namespace ID\r
+ // | PCI slot & function holding NVMe controller\r
+ // PCI root at system bus port, PIO\r
+ //\r
+ // UEFI device path:\r
+ //\r
+ // PciRoot(0x0)/Pci(0x6,0x1)/NVMe(0x1,00-00-00-00-00-00-00-00)\r
+ // ^ ^\r
+ // | octets of the EUI-64\r
+ // | in address order\r
+ // namespace ID\r
+ //\r
+ UINT64 Namespace[2];\r
+ UINTN RequiredEntries;\r
+ UINT8 *Eui64;\r
+\r
+ RequiredEntries = sizeof (Namespace) / sizeof (Namespace[0]);\r
+ NumEntries = RequiredEntries;\r
+ if (ParseUnitAddressHexList (\r
+ OfwNode[FirstNonBridge + 1].UnitAddress,\r
+ Namespace,\r
+ &NumEntries\r
+ ) != RETURN_SUCCESS ||\r
+ NumEntries != RequiredEntries ||\r
+ Namespace[0] == 0 ||\r
+ Namespace[0] >= MAX_UINT32\r
+ ) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ Eui64 = (UINT8 *)&Namespace[1];\r
+ Written = UnicodeSPrintAsciiFormat (\r
+ Translated,\r
+ *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+ "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/"\r
+ "NVMe(0x%Lx,%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x)",\r
+ PciRoot,\r
+ Bridges,\r
+ PciDevFun[0],\r
+ PciDevFun[1],\r
+ Namespace[0],\r
+ Eui64[7], Eui64[6], Eui64[5], Eui64[4],\r
+ Eui64[3], Eui64[2], Eui64[1], Eui64[0]\r
+ );\r
+ } else {\r
+ //\r
+ // Generic OpenFirmware device path for PCI devices:\r
+ //\r
+ // /pci@i0cf8/ethernet@3[,2]\r
+ // ^ ^\r
+ // | PCI slot[, function] holding Ethernet card\r
+ // PCI root at system bus port, PIO\r
+ //\r
+ // UEFI device path prefix (dependent on presence of nonzero PCI function):\r
+ //\r
+ // PciRoot(0x0)/Pci(0x3,0x0)\r
+ // PciRoot(0x0)/Pci(0x3,0x2)\r
+ //\r
+ Written = UnicodeSPrintAsciiFormat (\r
+ Translated,\r
+ *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+ "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",\r
+ PciRoot,\r
+ Bridges,\r
+ PciDevFun[0],\r
+ PciDevFun[1]\r
+ );\r
+ }\r
+\r
+ //\r
+ // There's no way to differentiate between "completely used up without\r
+ // truncation" and "truncated", so treat the former as the latter, and return\r
+ // success only for "some room left unused".\r
+ //\r
+ if (Written + 1 < *TranslatedSize) {\r
+ *TranslatedSize = Written;\r
+ return RETURN_SUCCESS;\r
+ }\r
+\r
+ return RETURN_BUFFER_TOO_SMALL;\r
+}\r
+\r
+\r
+//\r
+// A type providing easy raw access to the base address of a virtio-mmio\r
+// transport.\r
+//\r
+typedef union {\r
+ UINT64 Uint64;\r
+ UINT8 Raw[8];\r
+} VIRTIO_MMIO_BASE_ADDRESS;\r
+\r
+\r
+/**\r
+\r
+ Translate an MMIO-like array of OpenFirmware device nodes to a UEFI device\r
+ path fragment.\r
+\r
+ @param[in] OfwNode Array of OpenFirmware device nodes to\r
+ translate, constituting the beginning of an\r
+ OpenFirmware device path.\r
+\r
+ @param[in] NumNodes Number of elements in OfwNode.\r
+\r
+ @param[out] Translated Destination array receiving the UEFI path\r
+ fragment, allocated by the caller. If the\r
+ return value differs from RETURN_SUCCESS, its\r
+ contents is indeterminate.\r
+\r
+ @param[in out] TranslatedSize On input, the number of CHAR16's in\r
+ Translated. On RETURN_SUCCESS this parameter\r
+ is assigned the number of non-NUL CHAR16's\r
+ written to Translated. In case of other return\r
+ values, TranslatedSize is indeterminate.\r
+\r
+\r
+ @retval RETURN_SUCCESS Translation successful.\r
+\r
+ @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number\r
+ of bytes provided.\r
+\r
+ @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't\r
+ be translated in the current implementation.\r
+\r
+**/\r
+STATIC\r
+RETURN_STATUS\r
+TranslateMmioOfwNodes (\r
+ IN CONST OFW_NODE *OfwNode,\r
+ IN UINTN NumNodes,\r
+ OUT CHAR16 *Translated,\r
+ IN OUT UINTN *TranslatedSize\r
+ )\r
+{\r
+ VIRTIO_MMIO_BASE_ADDRESS VirtioMmioBase;\r
+ CHAR16 VenHwString[60 + 1];\r
+ UINTN NumEntries;\r
+ UINTN Written;\r
+\r
+ //\r
+ // Get the base address of the virtio-mmio transport.\r
+ //\r
+ if (NumNodes < REQUIRED_MMIO_OFW_NODES ||\r
+ !SubstringEq (OfwNode[0].DriverName, "virtio-mmio")\r
+ ) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+ NumEntries = 1;\r
+ if (ParseUnitAddressHexList (\r
+ OfwNode[0].UnitAddress,\r
+ &VirtioMmioBase.Uint64,\r
+ &NumEntries\r
+ ) != RETURN_SUCCESS\r
+ ) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ UnicodeSPrintAsciiFormat (VenHwString, sizeof VenHwString,\r
+ "VenHw(%g,%02X%02X%02X%02X%02X%02X%02X%02X)", &gVirtioMmioTransportGuid,\r
+ VirtioMmioBase.Raw[0], VirtioMmioBase.Raw[1], VirtioMmioBase.Raw[2],\r
+ VirtioMmioBase.Raw[3], VirtioMmioBase.Raw[4], VirtioMmioBase.Raw[5],\r
+ VirtioMmioBase.Raw[6], VirtioMmioBase.Raw[7]);\r
+\r
+ if (NumNodes >= 2 &&\r
+ SubstringEq (OfwNode[1].DriverName, "disk")) {\r
+ //\r
+ // OpenFirmware device path (virtio-blk disk):\r
+ //\r
+ // /virtio-mmio@000000000a003c00/disk@0,0\r
+ // ^ ^ ^\r
+ // | fixed\r
+ // base address of virtio-mmio register block\r
+ //\r
+ // UEFI device path prefix:\r
+ //\r
+ // <VenHwString>/HD(\r
+ //\r
+ Written = UnicodeSPrintAsciiFormat (\r
+ Translated,\r
+ *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+ "%s/HD(",\r
+ VenHwString\r
+ );\r
+ } else if (NumNodes >= 3 &&\r
+ SubstringEq (OfwNode[1].DriverName, "channel") &&\r
+ SubstringEq (OfwNode[2].DriverName, "disk")) {\r
+ //\r
+ // OpenFirmware device path (virtio-scsi disk):\r
+ //\r
+ // /virtio-mmio@000000000a003a00/channel@0/disk@2,3\r
+ // ^ ^ ^ ^\r
+ // | | | LUN\r
+ // | | target\r
+ // | channel (unused, fixed 0)\r
+ // base address of virtio-mmio register block\r
+ //\r
+ // UEFI device path prefix:\r
+ //\r
+ // <VenHwString>/Scsi(0x2,0x3)\r
+ //\r
+ UINT64 TargetLun[2];\r
+\r
+ TargetLun[1] = 0;\r
+ NumEntries = sizeof (TargetLun) / sizeof (TargetLun[0]);\r
+ if (ParseUnitAddressHexList (\r
+ OfwNode[2].UnitAddress,\r
+ TargetLun,\r
+ &NumEntries\r
+ ) != RETURN_SUCCESS\r
+ ) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ Written = UnicodeSPrintAsciiFormat (\r
+ Translated,\r
+ *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+ "%s/Scsi(0x%Lx,0x%Lx)",\r
+ VenHwString,\r
+ TargetLun[0],\r
+ TargetLun[1]\r
+ );\r
+ } else if (NumNodes >= 2 &&\r
+ SubstringEq (OfwNode[1].DriverName, "ethernet-phy")) {\r
+ //\r
+ // OpenFirmware device path (virtio-net NIC):\r
+ //\r
+ // /virtio-mmio@000000000a003e00/ethernet-phy@0\r
+ // ^ ^\r
+ // | fixed\r
+ // base address of virtio-mmio register block\r
+ //\r
+ // UEFI device path prefix (dependent on presence of nonzero PCI function):\r
+ //\r
+ // <VenHwString>/MAC(\r
+ //\r
+ Written = UnicodeSPrintAsciiFormat (\r
+ Translated,\r
+ *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+ "%s/MAC(",\r
+ VenHwString\r
+ );\r
+ } else {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // There's no way to differentiate between "completely used up without\r
+ // truncation" and "truncated", so treat the former as the latter, and return\r
+ // success only for "some room left unused".\r
+ //\r
+ if (Written + 1 < *TranslatedSize) {\r
+ *TranslatedSize = Written;\r
+ return RETURN_SUCCESS;\r
+ }\r
+\r
+ return RETURN_BUFFER_TOO_SMALL;\r
+}\r
+\r
+\r
+/**\r
+\r
+ Translate an array of OpenFirmware device nodes to a UEFI device path\r
+ fragment.\r
+\r
+ @param[in] OfwNode Array of OpenFirmware device nodes to\r
+ translate, constituting the beginning of an\r
+ OpenFirmware device path.\r
+\r
+ @param[in] NumNodes Number of elements in OfwNode.\r
+\r
+ @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with\r
+ CreateExtraRootBusMap(), to be used for\r
+ translating positions of extra root buses to\r
+ bus numbers.\r
+\r
+ @param[out] Translated Destination array receiving the UEFI path\r
+ fragment, allocated by the caller. If the\r
+ return value differs from RETURN_SUCCESS, its\r
+ contents is indeterminate.\r
+\r
+ @param[in out] TranslatedSize On input, the number of CHAR16's in\r
+ Translated. On RETURN_SUCCESS this parameter\r
+ is assigned the number of non-NUL CHAR16's\r
+ written to Translated. In case of other return\r
+ values, TranslatedSize is indeterminate.\r
+\r
+\r
+ @retval RETURN_SUCCESS Translation successful.\r
+\r
+ @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number\r
+ of bytes provided.\r
+\r
+ @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't\r
+ be translated in the current implementation.\r
+\r
+ @retval RETURN_PROTOCOL_ERROR The array of OpenFirmware device nodes has\r
+ been (partially) recognized, but it contains\r
+ a logic error / doesn't match system state.\r
+\r
+**/\r
+STATIC\r
+RETURN_STATUS\r
+TranslateOfwNodes (\r
+ IN CONST OFW_NODE *OfwNode,\r
+ IN UINTN NumNodes,\r
+ IN CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,\r
+ OUT CHAR16 *Translated,\r
+ IN OUT UINTN *TranslatedSize\r
+ )\r
+{\r
+ RETURN_STATUS Status;\r
+\r
+ Status = RETURN_UNSUPPORTED;\r
+\r
+ if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {\r
+ Status = TranslatePciOfwNodes (OfwNode, NumNodes, ExtraPciRoots,\r
+ Translated, TranslatedSize);\r
+ }\r
+ if (Status == RETURN_UNSUPPORTED &&\r
+ FeaturePcdGet (PcdQemuBootOrderMmioTranslation)) {\r
+ Status = TranslateMmioOfwNodes (OfwNode, NumNodes, Translated,\r
+ TranslatedSize);\r
+ }\r
+ return Status;\r
+}\r
+\r
+/**\r
+\r
+ Translate an OpenFirmware device path fragment to a UEFI device path\r
+ fragment, and advance in the input string.\r
+\r
+ @param[in out] Ptr Address of the pointer pointing to the start\r
+ of the path string. After successful\r
+ translation (RETURN_SUCCESS) or at least\r
+ successful parsing (RETURN_UNSUPPORTED,\r
+ RETURN_BUFFER_TOO_SMALL), *Ptr is set to the\r
+ byte immediately following the consumed\r
+ characters. In other error cases, it points to\r
+ the byte that caused the error.\r
+\r
+ @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with\r
+ CreateExtraRootBusMap(), to be used for\r
+ translating positions of extra root buses to\r
+ bus numbers.\r
+\r
+ @param[out] Translated Destination array receiving the UEFI path\r
+ fragment, allocated by the caller. If the\r
+ return value differs from RETURN_SUCCESS, its\r
+ contents is indeterminate.\r
+\r
+ @param[in out] TranslatedSize On input, the number of CHAR16's in\r
+ Translated. On RETURN_SUCCESS this parameter\r
+ is assigned the number of non-NUL CHAR16's\r
+ written to Translated. In case of other return\r
+ values, TranslatedSize is indeterminate.\r
+\r
+\r
+ @retval RETURN_SUCCESS Translation successful.\r
+\r
+ @retval RETURN_BUFFER_TOO_SMALL The OpenFirmware device path was parsed\r
+ successfully, but its translation did not\r
+ fit into the number of bytes provided.\r
+ Further calls to this function are\r
+ possible.\r
+\r
+ @retval RETURN_UNSUPPORTED The OpenFirmware device path was parsed\r
+ successfully, but it can't be translated in\r
+ the current implementation. Further calls\r
+ to this function are possible.\r
+\r
+ @retval RETURN_PROTOCOL_ERROR The OpenFirmware device path has been\r
+ (partially) recognized, but it contains a\r
+ logic error / doesn't match system state.\r
+ Further calls to this function are\r
+ possible.\r
+\r
+ @retval RETURN_NOT_FOUND Translation terminated. On input, *Ptr was\r
+ pointing to the empty string or "HALT". On\r
+ output, *Ptr points to the empty string\r
+ (ie. "HALT" is consumed transparently when\r
+ present).\r
+\r
+ @retval RETURN_INVALID_PARAMETER Parse error. This is a permanent error.\r
+\r
+**/\r
+STATIC\r
+RETURN_STATUS\r
+TranslateOfwPath (\r
+ IN OUT CONST CHAR8 **Ptr,\r
+ IN CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,\r
+ OUT CHAR16 *Translated,\r
+ IN OUT UINTN *TranslatedSize\r
+ )\r
+{\r
+ UINTN NumNodes;\r
+ RETURN_STATUS Status;\r
+ OFW_NODE Node[EXAMINED_OFW_NODES];\r
+ BOOLEAN IsFinal;\r
+ OFW_NODE Skip;\r
+\r
+ IsFinal = FALSE;\r
+ NumNodes = 0;\r
+ if (AsciiStrCmp (*Ptr, "HALT") == 0) {\r
+ *Ptr += 4;\r
+ Status = RETURN_NOT_FOUND;\r
+ } else {\r
+ Status = ParseOfwNode (Ptr, &Node[NumNodes], &IsFinal);\r
+ }\r
+\r
+ if (Status == RETURN_NOT_FOUND) {\r
+ DEBUG ((DEBUG_VERBOSE, "%a: no more nodes\n", __FUNCTION__));\r
+ return RETURN_NOT_FOUND;\r
+ }\r
+\r
+ while (Status == RETURN_SUCCESS && !IsFinal) {\r
+ ++NumNodes;\r
+ Status = ParseOfwNode (\r
+ Ptr,\r
+ (NumNodes < EXAMINED_OFW_NODES) ? &Node[NumNodes] : &Skip,\r
+ &IsFinal\r
+ );\r
+ }\r
+\r
+ switch (Status) {\r
+ case RETURN_SUCCESS:\r
+ ++NumNodes;\r
+ break;\r
+\r
+ case RETURN_INVALID_PARAMETER:\r
+ DEBUG ((DEBUG_VERBOSE, "%a: parse error\n", __FUNCTION__));\r
+ return RETURN_INVALID_PARAMETER;\r
+\r
+ default:\r
+ ASSERT (0);\r
+ }\r
+\r
+ Status = TranslateOfwNodes (\r
+ Node,\r
+ NumNodes < EXAMINED_OFW_NODES ? NumNodes : EXAMINED_OFW_NODES,\r
+ ExtraPciRoots,\r
+ Translated,\r
+ TranslatedSize);\r
+ switch (Status) {\r
+ case RETURN_SUCCESS:\r
+ DEBUG ((DEBUG_VERBOSE, "%a: success: \"%s\"\n", __FUNCTION__, Translated));\r
+ break;\r
+\r
+ case RETURN_BUFFER_TOO_SMALL:\r
+ DEBUG ((DEBUG_VERBOSE, "%a: buffer too small\n", __FUNCTION__));\r
+ break;\r
+\r
+ case RETURN_UNSUPPORTED:\r
+ DEBUG ((DEBUG_VERBOSE, "%a: unsupported\n", __FUNCTION__));\r
+ break;\r
+\r
+ case RETURN_PROTOCOL_ERROR:\r
+ DEBUG ((DEBUG_VERBOSE, "%a: logic error / system state mismatch\n",\r
+ __FUNCTION__));\r
+ break;\r
+\r
+ default:\r
+ ASSERT (0);\r
+ }\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+\r
+ Convert the UEFI DevicePath to full text representation with DevPathToText,\r
+ then match the UEFI device path fragment in Translated against it.\r
+\r
+ @param[in] Translated UEFI device path fragment, translated from\r
+ OpenFirmware format, to search for.\r
+\r
+ @param[in] TranslatedLength The length of Translated in CHAR16's.\r
+\r
+ @param[in] DevicePath Boot option device path whose textual rendering\r
+ to search in.\r
+\r
+ @param[in] DevPathToText Binary-to-text conversion protocol for DevicePath.\r
+\r
+\r
+ @retval TRUE If Translated was found at the beginning of DevicePath after\r
+ converting the latter to text.\r
+\r
+ @retval FALSE If DevicePath was NULL, or it could not be converted, or there\r
+ was no match.\r
+\r
+**/\r
+STATIC\r
+BOOLEAN\r
+Match (\r
+ IN CONST CHAR16 *Translated,\r
+ IN UINTN TranslatedLength,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath\r
+ )\r
+{\r
+ CHAR16 *Converted;\r
+ BOOLEAN Result;\r
+ VOID *FileBuffer;\r
+ UINTN FileSize;\r
+ EFI_DEVICE_PATH_PROTOCOL *AbsDevicePath;\r
+ CHAR16 *AbsConverted;\r
+ BOOLEAN Shortform;\r
+ EFI_DEVICE_PATH_PROTOCOL *Node;\r
+\r
+ Converted = ConvertDevicePathToText (\r
+ DevicePath,\r
+ FALSE, // DisplayOnly\r
+ FALSE // AllowShortcuts\r
+ );\r
+ if (Converted == NULL) {\r
+ return FALSE;\r
+ }\r
+\r
+ Result = FALSE;\r
+ Shortform = FALSE;\r
+ //\r
+ // Expand the short-form device path to full device path\r
+ //\r
+ if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&\r
+ (DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP)) {\r
+ //\r
+ // Harddrive shortform device path\r
+ //\r
+ Shortform = TRUE;\r
+ } else if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&\r
+ (DevicePathSubType (DevicePath) == MEDIA_FILEPATH_DP)) {\r
+ //\r
+ // File-path shortform device path\r
+ //\r
+ Shortform = TRUE;\r
+ } else if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&\r
+ (DevicePathSubType (DevicePath) == MSG_URI_DP)) {\r
+ //\r
+ // URI shortform device path\r
+ //\r
+ Shortform = TRUE;\r
+ } else {\r
+ for ( Node = DevicePath\r
+ ; !IsDevicePathEnd (Node)\r
+ ; Node = NextDevicePathNode (Node)\r
+ ) {\r
+ if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) &&\r
+ ((DevicePathSubType (Node) == MSG_USB_CLASS_DP) ||\r
+ (DevicePathSubType (Node) == MSG_USB_WWID_DP))) {\r
+ Shortform = TRUE;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Attempt to expand any relative UEFI device path to\r
+ // an absolute device path first.\r
+ //\r
+ if (Shortform) {\r
+ FileBuffer = EfiBootManagerGetLoadOptionBuffer (\r
+ DevicePath, &AbsDevicePath, &FileSize\r
+ );\r
+ if (FileBuffer == NULL) {\r
+ goto Exit;\r
+ }\r
+ FreePool (FileBuffer);\r
+ AbsConverted = ConvertDevicePathToText (AbsDevicePath, FALSE, FALSE);\r
+ FreePool (AbsDevicePath);\r
+ if (AbsConverted == NULL) {\r
+ goto Exit;\r
+ }\r
+ DEBUG ((DEBUG_VERBOSE,\r
+ "%a: expanded relative device path \"%s\" for prefix matching\n",\r
+ __FUNCTION__, Converted));\r
+ FreePool (Converted);\r
+ Converted = AbsConverted;\r
+ }\r
+\r
+ //\r
+ // Is Translated a prefix of Converted?\r
+ //\r
+ Result = (BOOLEAN)(StrnCmp (Converted, Translated, TranslatedLength) == 0);\r
+ DEBUG ((\r
+ DEBUG_VERBOSE,\r
+ "%a: against \"%s\": %a\n",\r
+ __FUNCTION__,\r
+ Converted,\r
+ Result ? "match" : "no match"\r
+ ));\r
+Exit:\r
+ FreePool (Converted);\r
+ return Result;\r
+}\r
+\r
+\r
+/**\r
+ Append some of the unselected active boot options to the boot order.\r
+\r
+ This function should accommodate any further policy changes in "boot option\r
+ survival". Currently we're adding back everything that starts with neither\r
+ PciRoot() nor HD() nor a virtio-mmio VenHw() node.\r
+\r
+ @param[in,out] BootOrder The structure holding the boot order to\r
+ complete. The caller is responsible for\r
+ initializing (and potentially populating) it\r
+ before calling this function.\r
+\r
+ @param[in,out] ActiveOption The array of active boot options to scan.\r
+ Entries marked as Appended will be skipped.\r
+ Those of the rest that satisfy the survival\r
+ policy will be added to BootOrder with\r
+ BootOrderAppend().\r
+\r
+ @param[in] ActiveCount Number of elements in ActiveOption.\r
+\r
+\r
+ @retval RETURN_SUCCESS BootOrder has been extended with any eligible boot\r
+ options.\r
+\r
+ @return Error codes returned by BootOrderAppend().\r
+**/\r
+STATIC\r
+RETURN_STATUS\r
+BootOrderComplete (\r
+ IN OUT BOOT_ORDER *BootOrder,\r
+ IN OUT ACTIVE_OPTION *ActiveOption,\r
+ IN UINTN ActiveCount\r
+ )\r
+{\r
+ RETURN_STATUS Status;\r
+ UINTN Idx;\r
+\r
+ Status = RETURN_SUCCESS;\r
+ Idx = 0;\r
+ while (!RETURN_ERROR (Status) && Idx < ActiveCount) {\r
+ if (!ActiveOption[Idx].Appended) {\r
+ CONST EFI_BOOT_MANAGER_LOAD_OPTION *Current;\r
+ CONST EFI_DEVICE_PATH_PROTOCOL *FirstNode;\r
+\r
+ Current = ActiveOption[Idx].BootOption;\r
+ FirstNode = Current->FilePath;\r
+ if (FirstNode != NULL) {\r
+ CHAR16 *Converted;\r
+ STATIC CHAR16 ConvFallBack[] = L"<unable to convert>";\r
+ BOOLEAN Keep;\r
+\r
+ Converted = ConvertDevicePathToText (FirstNode, FALSE, FALSE);\r
+ if (Converted == NULL) {\r
+ Converted = ConvFallBack;\r
+ }\r
+\r
+ Keep = TRUE;\r
+ if (DevicePathType(FirstNode) == MEDIA_DEVICE_PATH &&\r
+ DevicePathSubType(FirstNode) == MEDIA_HARDDRIVE_DP) {\r
+ //\r
+ // drop HD()\r
+ //\r
+ Keep = FALSE;\r
+ } else if (DevicePathType(FirstNode) == ACPI_DEVICE_PATH &&\r
+ DevicePathSubType(FirstNode) == ACPI_DP) {\r
+ ACPI_HID_DEVICE_PATH *Acpi;\r
+\r
+ Acpi = (ACPI_HID_DEVICE_PATH *) FirstNode;\r
+ if ((Acpi->HID & PNP_EISA_ID_MASK) == PNP_EISA_ID_CONST &&\r
+ EISA_ID_TO_NUM (Acpi->HID) == 0x0a03) {\r
+ //\r
+ // drop PciRoot() if we enabled the user to select PCI-like boot\r
+ // options, by providing translation for such OFW device path\r
+ // fragments\r
+ //\r
+ Keep = !FeaturePcdGet (PcdQemuBootOrderPciTranslation);\r
+ }\r
+ } else if (DevicePathType(FirstNode) == HARDWARE_DEVICE_PATH &&\r
+ DevicePathSubType(FirstNode) == HW_VENDOR_DP) {\r
+ VENDOR_DEVICE_PATH *VenHw;\r
+\r
+ VenHw = (VENDOR_DEVICE_PATH *)FirstNode;\r
+ if (CompareGuid (&VenHw->Guid, &gVirtioMmioTransportGuid)) {\r
+ //\r
+ // drop virtio-mmio if we enabled the user to select boot options\r
+ // referencing such device paths\r
+ //\r
+ Keep = !FeaturePcdGet (PcdQemuBootOrderMmioTranslation);\r
+ }\r
+ }\r
+\r
+ if (Keep) {\r
+ Status = BootOrderAppend (BootOrder, &ActiveOption[Idx]);\r
+ if (!RETURN_ERROR (Status)) {\r
+ DEBUG ((DEBUG_VERBOSE, "%a: keeping \"%s\"\n", __FUNCTION__,\r
+ Converted));\r
+ }\r
+ } else {\r
+ DEBUG ((DEBUG_VERBOSE, "%a: dropping \"%s\"\n", __FUNCTION__,\r
+ Converted));\r
+ }\r
+\r
+ if (Converted != ConvFallBack) {\r
+ FreePool (Converted);\r
+ }\r
+ }\r
+ }\r
+ ++Idx;\r
+ }\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Delete Boot#### variables that stand for such active boot options that have\r
+ been dropped (ie. have not been selected by either matching or "survival\r
+ policy").\r
+\r
+ @param[in] ActiveOption The array of active boot options to scan. Each\r
+ entry not marked as appended will trigger the\r
+ deletion of the matching Boot#### variable.\r
+\r
+ @param[in] ActiveCount Number of elements in ActiveOption.\r
+**/\r
+STATIC\r
+VOID\r
+PruneBootVariables (\r
+ IN CONST ACTIVE_OPTION *ActiveOption,\r
+ IN UINTN ActiveCount\r
+ )\r
+{\r
+ UINTN Idx;\r
+\r
+ for (Idx = 0; Idx < ActiveCount; ++Idx) {\r
+ if (!ActiveOption[Idx].Appended) {\r
+ CHAR16 VariableName[9];\r
+\r
+ UnicodeSPrintAsciiFormat (VariableName, sizeof VariableName, "Boot%04x",\r
+ ActiveOption[Idx].BootOption->OptionNumber);\r
+\r
+ //\r
+ // "The space consumed by the deleted variable may not be available until\r
+ // the next power cycle", but that's good enough.\r
+ //\r
+ gRT->SetVariable (VariableName, &gEfiGlobalVariableGuid,\r
+ 0, // Attributes, 0 means deletion\r
+ 0, // DataSize, 0 means deletion\r
+ NULL // Data\r
+ );\r
+ }\r
+ }\r
+}\r
+\r
+\r
+/**\r
+\r
+ Set the boot order based on configuration retrieved from QEMU.\r
+\r
+ Attempt to retrieve the "bootorder" fw_cfg file from QEMU. Translate the\r
+ OpenFirmware device paths therein to UEFI device path fragments. Match the\r
+ translated fragments against the current list of boot options, and rewrite\r
+ the BootOrder NvVar so that it corresponds to the order described in fw_cfg.\r
+\r
+ Platform BDS should call this function after EfiBootManagerConnectAll () and\r
+ EfiBootManagerRefreshAllBootOption () return.\r
+\r
+ @retval RETURN_SUCCESS BootOrder NvVar rewritten.\r
+\r
+ @retval RETURN_UNSUPPORTED QEMU's fw_cfg is not supported.\r
+\r
+ @retval RETURN_NOT_FOUND Empty or nonexistent "bootorder" fw_cfg\r
+ file, or no match found between the\r
+ "bootorder" fw_cfg file and BootOptionList.\r
+\r
+ @retval RETURN_INVALID_PARAMETER Parse error in the "bootorder" fw_cfg file.\r
+\r
+ @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.\r
+\r
+ @return Values returned by gBS->LocateProtocol ()\r
+ or gRT->SetVariable ().\r
+\r
+**/\r
+RETURN_STATUS\r
+SetBootOrderFromQemu (\r
+ VOID\r
+ )\r
+{\r
+ RETURN_STATUS Status;\r
+ FIRMWARE_CONFIG_ITEM FwCfgItem;\r
+ UINTN FwCfgSize;\r
+ CHAR8 *FwCfg;\r
+ CONST CHAR8 *FwCfgPtr;\r
+\r
+ BOOT_ORDER BootOrder;\r
+ ACTIVE_OPTION *ActiveOption;\r
+ UINTN ActiveCount;\r
+\r
+ EXTRA_ROOT_BUS_MAP *ExtraPciRoots;\r
+\r
+ UINTN TranslatedSize;\r
+ CHAR16 Translated[TRANSLATION_OUTPUT_SIZE];\r
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
+ UINTN BootOptionCount;\r
+\r
+ Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);\r
+ if (Status != RETURN_SUCCESS) {\r
+ return Status;\r
+ }\r
+\r
+ if (FwCfgSize == 0) {\r
+ return RETURN_NOT_FOUND;\r
+ }\r
+\r
+ FwCfg = AllocatePool (FwCfgSize);\r
+ if (FwCfg == NULL) {\r
+ return RETURN_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ QemuFwCfgSelectItem (FwCfgItem);\r
+ QemuFwCfgReadBytes (FwCfgSize, FwCfg);\r
+ if (FwCfg[FwCfgSize - 1] != '\0') {\r
+ Status = RETURN_INVALID_PARAMETER;\r
+ goto ErrorFreeFwCfg;\r
+ }\r
+\r
+ DEBUG ((DEBUG_VERBOSE, "%a: FwCfg:\n", __FUNCTION__));\r
+ DEBUG ((DEBUG_VERBOSE, "%a\n", FwCfg));\r
+ DEBUG ((DEBUG_VERBOSE, "%a: FwCfg: <end>\n", __FUNCTION__));\r
+ FwCfgPtr = FwCfg;\r
+\r
+ BootOrder.Produced = 0;\r
+ BootOrder.Allocated = 1;\r
+ BootOrder.Data = AllocatePool (\r
+ BootOrder.Allocated * sizeof (*BootOrder.Data)\r
+ );\r
+ if (BootOrder.Data == NULL) {\r
+ Status = RETURN_OUT_OF_RESOURCES;\r
+ goto ErrorFreeFwCfg;\r
+ }\r
+\r
+ BootOptions = EfiBootManagerGetLoadOptions (\r
+ &BootOptionCount, LoadOptionTypeBoot\r
+ );\r
+ if (BootOptions == NULL) {\r
+ Status = RETURN_NOT_FOUND;\r
+ goto ErrorFreeBootOrder;\r
+ }\r
+\r
+ Status = CollectActiveOptions (\r
+ BootOptions, BootOptionCount, &ActiveOption, &ActiveCount\r
+ );\r
+ if (RETURN_ERROR (Status)) {\r
+ goto ErrorFreeBootOptions;\r
+ }\r
+\r
+ if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {\r
+ Status = CreateExtraRootBusMap (&ExtraPciRoots);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ErrorFreeActiveOption;\r
+ }\r
+ } else {\r
+ ExtraPciRoots = NULL;\r
+ }\r
+\r
+ //\r
+ // translate each OpenFirmware path\r
+ //\r
+ TranslatedSize = sizeof (Translated) / sizeof (Translated[0]);\r
+ Status = TranslateOfwPath (&FwCfgPtr, ExtraPciRoots, Translated,\r
+ &TranslatedSize);\r
+ while (Status == RETURN_SUCCESS ||\r
+ Status == RETURN_UNSUPPORTED ||\r
+ Status == RETURN_PROTOCOL_ERROR ||\r
+ Status == RETURN_BUFFER_TOO_SMALL) {\r
+ if (Status == RETURN_SUCCESS) {\r
+ UINTN Idx;\r
+\r
+ //\r
+ // match translated OpenFirmware path against all active boot options\r
+ //\r
+ for (Idx = 0; Idx < ActiveCount; ++Idx) {\r
+ if (Match (\r
+ Translated,\r
+ TranslatedSize, // contains length, not size, in CHAR16's here\r
+ ActiveOption[Idx].BootOption->FilePath\r
+ )\r
+ ) {\r
+ //\r
+ // match found, store ID and continue with next OpenFirmware path\r
+ //\r
+ Status = BootOrderAppend (&BootOrder, &ActiveOption[Idx]);\r
+ if (Status != RETURN_SUCCESS) {\r
+ goto ErrorFreeExtraPciRoots;\r
+ }\r
+ break;\r
+ }\r
+ } // scanned all active boot options\r
+ } // translation successful\r
+\r
+ TranslatedSize = sizeof (Translated) / sizeof (Translated[0]);\r
+ Status = TranslateOfwPath (&FwCfgPtr, ExtraPciRoots, Translated,\r
+ &TranslatedSize);\r
+ } // scanning of OpenFirmware paths done\r
+\r
+ if (Status == RETURN_NOT_FOUND && BootOrder.Produced > 0) {\r
+ //\r
+ // No more OpenFirmware paths, some matches found: rewrite BootOrder NvVar.\r
+ // Some of the active boot options that have not been selected over fw_cfg\r
+ // should be preserved at the end of the boot order.\r
+ //\r
+ Status = BootOrderComplete (&BootOrder, ActiveOption, ActiveCount);\r
+ if (RETURN_ERROR (Status)) {\r
+ goto ErrorFreeExtraPciRoots;\r
+ }\r
+\r
+ //\r
+ // See Table 10 in the UEFI Spec 2.3.1 with Errata C for the required\r
+ // attributes.\r
+ //\r
+ Status = gRT->SetVariable (\r
+ L"BootOrder",\r
+ &gEfiGlobalVariableGuid,\r
+ EFI_VARIABLE_NON_VOLATILE |\r
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |\r
+ EFI_VARIABLE_RUNTIME_ACCESS,\r
+ BootOrder.Produced * sizeof (*BootOrder.Data),\r
+ BootOrder.Data\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "%a: setting BootOrder: %r\n", __FUNCTION__, Status));\r
+ goto ErrorFreeExtraPciRoots;\r
+ }\r
+\r
+ DEBUG ((DEBUG_INFO, "%a: setting BootOrder: success\n", __FUNCTION__));\r
+ PruneBootVariables (ActiveOption, ActiveCount);\r
+ }\r
+\r
+ErrorFreeExtraPciRoots:\r
+ if (ExtraPciRoots != NULL) {\r
+ DestroyExtraRootBusMap (ExtraPciRoots);\r
+ }\r
+\r
+ErrorFreeActiveOption:\r
+ FreePool (ActiveOption);\r
+\r
+ErrorFreeBootOptions:\r
+ EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
+\r
+ErrorFreeBootOrder:\r
+ FreePool (BootOrder.Data);\r
+\r
+ErrorFreeFwCfg:\r
+ FreePool (FwCfg);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Calculate the number of seconds we should be showing the FrontPage progress\r
+ bar for.\r
+\r
+ @return The TimeoutDefault argument for PlatformBdsEnterFrontPage().\r
+**/\r
+UINT16\r
+GetFrontPageTimeoutFromQemu (\r
+ VOID\r
+ )\r
+{\r
+ FIRMWARE_CONFIG_ITEM BootMenuWaitItem;\r
+ UINTN BootMenuWaitSize;\r
+\r
+ QemuFwCfgSelectItem (QemuFwCfgItemBootMenu);\r
+ if (QemuFwCfgRead16 () == 0) {\r
+ //\r
+ // The user specified "-boot menu=off", or didn't specify "-boot\r
+ // menu=(on|off)" at all. Return the platform default.\r
+ //\r
+ return PcdGet16 (PcdPlatformBootTimeOut);\r
+ }\r
+\r
+ if (RETURN_ERROR (QemuFwCfgFindFile ("etc/boot-menu-wait", &BootMenuWaitItem,\r
+ &BootMenuWaitSize)) ||\r
+ BootMenuWaitSize != sizeof (UINT16)) {\r
+ //\r
+ // "-boot menu=on" was specified without "splash-time=N". In this case,\r
+ // return three seconds if the platform default would cause us to skip the\r
+ // front page, and return the platform default otherwise.\r
+ //\r
+ UINT16 Timeout;\r
+\r
+ Timeout = PcdGet16 (PcdPlatformBootTimeOut);\r
+ if (Timeout == 0) {\r
+ Timeout = 3;\r
+ }\r
+ return Timeout;\r
+ }\r
+\r
+ //\r
+ // "-boot menu=on,splash-time=N" was specified, where N is in units of\r
+ // milliseconds. The Intel BDS Front Page progress bar only supports whole\r
+ // seconds, round N up.\r
+ //\r
+ QemuFwCfgSelectItem (BootMenuWaitItem);\r
+ return (UINT16)((QemuFwCfgRead16 () + 999) / 1000);\r
+}\r
--- /dev/null
+## @file\r
+# Rewrite the BootOrder NvVar based on QEMU's "bootorder" fw_cfg file.\r
+#\r
+# Copyright (C) 2012 - 2014, Red Hat, Inc.\r
+# Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.<BR>\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,\r
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR\r
+# IMPLIED.\r
+#\r
+##\r
+\r
+[Defines]\r
+ INF_VERSION = 0x00010005\r
+ BASE_NAME = QemuBootOrderLib\r
+ FILE_GUID = 1D677A58-C753-4AF1-B552-EFE142DF8F57\r
+ MODULE_TYPE = DXE_DRIVER\r
+ VERSION_STRING = 1.0\r
+ LIBRARY_CLASS = QemuBootOrderLib|DXE_DRIVER\r
+\r
+#\r
+# The following information is for reference only and not required by the build\r
+# tools.\r
+#\r
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC ARM AARCH64\r
+#\r
+\r
+[Sources]\r
+ QemuBootOrderLib.c\r
+ ExtraRootBusMap.c\r
+\r
+[Packages]\r
+ MdePkg/MdePkg.dec\r
+ MdeModulePkg/MdeModulePkg.dec\r
+ OvmfPkg/OvmfPkg.dec\r
+\r
+[LibraryClasses]\r
+ QemuFwCfgLib\r
+ DebugLib\r
+ MemoryAllocationLib\r
+ UefiBootManagerLib\r
+ UefiBootServicesTableLib\r
+ UefiRuntimeServicesTableLib\r
+ BaseLib\r
+ PrintLib\r
+ DevicePathLib\r
+ BaseMemoryLib\r
+ OrderedCollectionLib\r
+\r
+[Guids]\r
+ gEfiGlobalVariableGuid\r
+ gVirtioMmioTransportGuid\r
+\r
+[FeaturePcd]\r
+ gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderPciTranslation\r
+ gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderMmioTranslation\r
+\r
+[Pcd]\r
+ gEfiMdePkgTokenSpaceGuid.PcdPlatformBootTimeOut\r
+\r
+[Protocols]\r
+ gEfiDevicePathProtocolGuid ## CONSUMES\r
+ gEfiPciRootBridgeIoProtocolGuid ## CONSUMES\r
+++ /dev/null
-/** @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
+++ /dev/null
-/** @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
+++ /dev/null
-/** @file\r
- Rewrite the BootOrder NvVar based on QEMU's "bootorder" fw_cfg file.\r
-\r
- Copyright (C) 2012 - 2014, Red Hat, Inc.\r
- Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.<BR>\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/QemuFwCfgLib.h>\r
-#include <Library/DebugLib.h>\r
-#include <Library/MemoryAllocationLib.h>\r
-#include <Library/UefiBootManagerLib.h>\r
-#include <Library/UefiBootServicesTableLib.h>\r
-#include <Library/UefiRuntimeServicesTableLib.h>\r
-#include <Library/BaseLib.h>\r
-#include <Library/PrintLib.h>\r
-#include <Library/DevicePathLib.h>\r
-#include <Library/QemuBootOrderLib.h>\r
-#include <Library/BaseMemoryLib.h>\r
-#include <Guid/GlobalVariable.h>\r
-#include <Guid/VirtioMmioTransport.h>\r
-\r
-#include "ExtraRootBusMap.h"\r
-\r
-/**\r
- OpenFirmware to UEFI device path translation output buffer size in CHAR16's.\r
-**/\r
-#define TRANSLATION_OUTPUT_SIZE 0x100\r
-\r
-/**\r
- Output buffer size for OpenFirmware to UEFI device path fragment translation,\r
- in CHAR16's, for a sequence of PCI bridges.\r
-**/\r
-#define BRIDGE_TRANSLATION_OUTPUT_SIZE 0x40\r
-\r
-/**\r
- Numbers of nodes in OpenFirmware device paths that are required and examined.\r
-**/\r
-#define REQUIRED_PCI_OFW_NODES 2\r
-#define REQUIRED_MMIO_OFW_NODES 1\r
-#define EXAMINED_OFW_NODES 6\r
-\r
-\r
-/**\r
- Simple character classification routines, corresponding to POSIX class names\r
- and ASCII encoding.\r
-**/\r
-STATIC\r
-BOOLEAN\r
-IsAlnum (\r
- IN CHAR8 Chr\r
- )\r
-{\r
- return (('0' <= Chr && Chr <= '9') ||\r
- ('A' <= Chr && Chr <= 'Z') ||\r
- ('a' <= Chr && Chr <= 'z')\r
- );\r
-}\r
-\r
-\r
-STATIC\r
-BOOLEAN\r
-IsDriverNamePunct (\r
- IN CHAR8 Chr\r
- )\r
-{\r
- return (Chr == ',' || Chr == '.' || Chr == '_' ||\r
- Chr == '+' || Chr == '-'\r
- );\r
-}\r
-\r
-\r
-STATIC\r
-BOOLEAN\r
-IsPrintNotDelim (\r
- IN CHAR8 Chr\r
- )\r
-{\r
- return (32 <= Chr && Chr <= 126 &&\r
- Chr != '/' && Chr != '@' && Chr != ':');\r
-}\r
-\r
-\r
-/**\r
- Utility types and functions.\r
-**/\r
-typedef struct {\r
- CONST CHAR8 *Ptr; // not necessarily NUL-terminated\r
- UINTN Len; // number of non-NUL characters\r
-} SUBSTRING;\r
-\r
-\r
-/**\r
-\r
- Check if Substring and String have identical contents.\r
-\r
- The function relies on the restriction that a SUBSTRING cannot have embedded\r
- NULs either.\r
-\r
- @param[in] Substring The SUBSTRING input to the comparison.\r
-\r
- @param[in] String The ASCII string input to the comparison.\r
-\r
-\r
- @return Whether the inputs have identical contents.\r
-\r
-**/\r
-STATIC\r
-BOOLEAN\r
-SubstringEq (\r
- IN SUBSTRING Substring,\r
- IN CONST CHAR8 *String\r
- )\r
-{\r
- UINTN Pos;\r
- CONST CHAR8 *Chr;\r
-\r
- Pos = 0;\r
- Chr = String;\r
-\r
- while (Pos < Substring.Len && Substring.Ptr[Pos] == *Chr) {\r
- ++Pos;\r
- ++Chr;\r
- }\r
-\r
- return (BOOLEAN)(Pos == Substring.Len && *Chr == '\0');\r
-}\r
-\r
-\r
-/**\r
-\r
- Parse a comma-separated list of hexadecimal integers into the elements of an\r
- UINT64 array.\r
-\r
- Whitespace, "0x" prefixes, leading or trailing commas, sequences of commas,\r
- or an empty string are not allowed; they are rejected.\r
-\r
- The function relies on ASCII encoding.\r
-\r
- @param[in] UnitAddress The substring to parse.\r
-\r
- @param[out] Result The array, allocated by the caller, to receive\r
- the parsed values. This parameter may be NULL if\r
- NumResults is zero on input.\r
-\r
- @param[in out] NumResults On input, the number of elements allocated for\r
- Result. On output, the number of elements it has\r
- taken (or would have taken) to parse the string\r
- fully.\r
-\r
-\r
- @retval RETURN_SUCCESS UnitAddress has been fully parsed.\r
- NumResults is set to the number of parsed\r
- values; the corresponding elements have\r
- been set in Result. The rest of Result's\r
- elements are unchanged.\r
-\r
- @retval RETURN_BUFFER_TOO_SMALL UnitAddress has been fully parsed.\r
- NumResults is set to the number of parsed\r
- values, but elements have been stored only\r
- up to the input value of NumResults, which\r
- is less than what has been parsed.\r
-\r
- @retval RETURN_INVALID_PARAMETER Parse error. The contents of Results is\r
- indeterminate. NumResults has not been\r
- changed.\r
-\r
-**/\r
-STATIC\r
-RETURN_STATUS\r
-ParseUnitAddressHexList (\r
- IN SUBSTRING UnitAddress,\r
- OUT UINT64 *Result,\r
- IN OUT UINTN *NumResults\r
- )\r
-{\r
- UINTN Entry; // number of entry currently being parsed\r
- UINT64 EntryVal; // value being constructed for current entry\r
- CHAR8 PrevChr; // UnitAddress character previously checked\r
- UINTN Pos; // current position within UnitAddress\r
- RETURN_STATUS Status;\r
-\r
- Entry = 0;\r
- EntryVal = 0;\r
- PrevChr = ',';\r
-\r
- for (Pos = 0; Pos < UnitAddress.Len; ++Pos) {\r
- CHAR8 Chr;\r
- INT8 Val;\r
-\r
- Chr = UnitAddress.Ptr[Pos];\r
- Val = ('a' <= Chr && Chr <= 'f') ? (Chr - 'a' + 10) :\r
- ('A' <= Chr && Chr <= 'F') ? (Chr - 'A' + 10) :\r
- ('0' <= Chr && Chr <= '9') ? (Chr - '0' ) :\r
- -1;\r
-\r
- if (Val >= 0) {\r
- if (EntryVal > 0xFFFFFFFFFFFFFFFull) {\r
- return RETURN_INVALID_PARAMETER;\r
- }\r
- EntryVal = LShiftU64 (EntryVal, 4) | Val;\r
- } else if (Chr == ',') {\r
- if (PrevChr == ',') {\r
- return RETURN_INVALID_PARAMETER;\r
- }\r
- if (Entry < *NumResults) {\r
- Result[Entry] = EntryVal;\r
- }\r
- ++Entry;\r
- EntryVal = 0;\r
- } else {\r
- return RETURN_INVALID_PARAMETER;\r
- }\r
-\r
- PrevChr = Chr;\r
- }\r
-\r
- if (PrevChr == ',') {\r
- return RETURN_INVALID_PARAMETER;\r
- }\r
- if (Entry < *NumResults) {\r
- Result[Entry] = EntryVal;\r
- Status = RETURN_SUCCESS;\r
- } else {\r
- Status = RETURN_BUFFER_TOO_SMALL;\r
- }\r
- ++Entry;\r
-\r
- *NumResults = Entry;\r
- return Status;\r
-}\r
-\r
-\r
-/**\r
- A simple array of Boot Option ID's.\r
-**/\r
-typedef struct {\r
- UINT16 *Data;\r
- UINTN Allocated;\r
- UINTN Produced;\r
-} BOOT_ORDER;\r
-\r
-\r
-/**\r
- Array element tracking an enumerated boot option that has the\r
- LOAD_OPTION_ACTIVE attribute.\r
-**/\r
-typedef struct {\r
- CONST EFI_BOOT_MANAGER_LOAD_OPTION *BootOption; // reference only, no\r
- // ownership\r
- BOOLEAN Appended; // has been added to a\r
- // BOOT_ORDER?\r
-} ACTIVE_OPTION;\r
-\r
-\r
-/**\r
-\r
- Append an active boot option to BootOrder, reallocating the latter if needed.\r
-\r
- @param[in out] BootOrder The structure pointing to the array and holding\r
- allocation and usage counters.\r
-\r
- @param[in] ActiveOption The active boot option whose ID should be\r
- appended to the array.\r
-\r
-\r
- @retval RETURN_SUCCESS ID of ActiveOption appended.\r
-\r
- @retval RETURN_OUT_OF_RESOURCES Memory reallocation failed.\r
-\r
-**/\r
-STATIC\r
-RETURN_STATUS\r
-BootOrderAppend (\r
- IN OUT BOOT_ORDER *BootOrder,\r
- IN OUT ACTIVE_OPTION *ActiveOption\r
- )\r
-{\r
- if (BootOrder->Produced == BootOrder->Allocated) {\r
- UINTN AllocatedNew;\r
- UINT16 *DataNew;\r
-\r
- ASSERT (BootOrder->Allocated > 0);\r
- AllocatedNew = BootOrder->Allocated * 2;\r
- DataNew = ReallocatePool (\r
- BootOrder->Allocated * sizeof (*BootOrder->Data),\r
- AllocatedNew * sizeof (*DataNew),\r
- BootOrder->Data\r
- );\r
- if (DataNew == NULL) {\r
- return RETURN_OUT_OF_RESOURCES;\r
- }\r
- BootOrder->Allocated = AllocatedNew;\r
- BootOrder->Data = DataNew;\r
- }\r
-\r
- BootOrder->Data[BootOrder->Produced++] =\r
- (UINT16) ActiveOption->BootOption->OptionNumber;\r
- ActiveOption->Appended = TRUE;\r
- return RETURN_SUCCESS;\r
-}\r
-\r
-\r
-/**\r
-\r
- Create an array of ACTIVE_OPTION elements for a boot option array.\r
-\r
- @param[in] BootOptions A boot option array, created with\r
- EfiBootManagerRefreshAllBootOption () and\r
- EfiBootManagerGetLoadOptions ().\r
-\r
- @param[in] BootOptionCount The number of elements in BootOptions.\r
-\r
- @param[out] ActiveOption Pointer to the first element in the new array.\r
- The caller is responsible for freeing the array\r
- with FreePool() after use.\r
-\r
- @param[out] Count Number of elements in the new array.\r
-\r
-\r
- @retval RETURN_SUCCESS The ActiveOption array has been created.\r
-\r
- @retval RETURN_NOT_FOUND No active entry has been found in\r
- BootOptions.\r
-\r
- @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.\r
-\r
-**/\r
-STATIC\r
-RETURN_STATUS\r
-CollectActiveOptions (\r
- IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions,\r
- IN UINTN BootOptionCount,\r
- OUT ACTIVE_OPTION **ActiveOption,\r
- OUT UINTN *Count\r
- )\r
-{\r
- UINTN Index;\r
- UINTN ScanMode;\r
-\r
- *ActiveOption = NULL;\r
-\r
- //\r
- // Scan the list twice:\r
- // - count active entries,\r
- // - store links to active entries.\r
- //\r
- for (ScanMode = 0; ScanMode < 2; ++ScanMode) {\r
- *Count = 0;\r
- for (Index = 0; Index < BootOptionCount; Index++) {\r
- if ((BootOptions[Index].Attributes & LOAD_OPTION_ACTIVE) != 0) {\r
- if (ScanMode == 1) {\r
- (*ActiveOption)[*Count].BootOption = &BootOptions[Index];\r
- (*ActiveOption)[*Count].Appended = FALSE;\r
- }\r
- ++*Count;\r
- }\r
- }\r
-\r
- if (ScanMode == 0) {\r
- if (*Count == 0) {\r
- return RETURN_NOT_FOUND;\r
- }\r
- *ActiveOption = AllocatePool (*Count * sizeof **ActiveOption);\r
- if (*ActiveOption == NULL) {\r
- return RETURN_OUT_OF_RESOURCES;\r
- }\r
- }\r
- }\r
- return RETURN_SUCCESS;\r
-}\r
-\r
-\r
-/**\r
- OpenFirmware device path node\r
-**/\r
-typedef struct {\r
- SUBSTRING DriverName;\r
- SUBSTRING UnitAddress;\r
- SUBSTRING DeviceArguments;\r
-} OFW_NODE;\r
-\r
-\r
-/**\r
-\r
- Parse an OpenFirmware device path node into the caller-allocated OFW_NODE\r
- structure, and advance in the input string.\r
-\r
- The node format is mostly parsed after IEEE 1275-1994, 3.2.1.1 "Node names"\r
- (a leading slash is expected and not returned):\r
-\r
- /driver-name@unit-address[:device-arguments][<LF>]\r
-\r
- A single trailing <LF> character is consumed but not returned. A trailing\r
- <LF> or NUL character terminates the device path.\r
-\r
- The function relies on ASCII encoding.\r
-\r
- @param[in out] Ptr Address of the pointer pointing to the start of the\r
- node string. After successful parsing *Ptr is set to\r
- the byte immediately following the consumed\r
- characters. On error it points to the byte that\r
- caused the error. The input string is never modified.\r
-\r
- @param[out] OfwNode The members of this structure point into the input\r
- string, designating components of the node.\r
- Separators are never included. If "device-arguments"\r
- is missing, then DeviceArguments.Ptr is set to NULL.\r
- All components that are present have nonzero length.\r
-\r
- If the call doesn't succeed, the contents of this\r
- structure is indeterminate.\r
-\r
- @param[out] IsFinal In case of successul parsing, this parameter signals\r
- whether the node just parsed is the final node in the\r
- device path. The call after a final node will attempt\r
- to start parsing the next path. If the call doesn't\r
- succeed, then this parameter is not changed.\r
-\r
-\r
- @retval RETURN_SUCCESS Parsing successful.\r
-\r
- @retval RETURN_NOT_FOUND Parsing terminated. *Ptr was (and is)\r
- pointing to an empty string.\r
-\r
- @retval RETURN_INVALID_PARAMETER Parse error.\r
-\r
-**/\r
-STATIC\r
-RETURN_STATUS\r
-ParseOfwNode (\r
- IN OUT CONST CHAR8 **Ptr,\r
- OUT OFW_NODE *OfwNode,\r
- OUT BOOLEAN *IsFinal\r
- )\r
-{\r
- //\r
- // A leading slash is expected. End of string is tolerated.\r
- //\r
- switch (**Ptr) {\r
- case '\0':\r
- return RETURN_NOT_FOUND;\r
-\r
- case '/':\r
- ++*Ptr;\r
- break;\r
-\r
- default:\r
- return RETURN_INVALID_PARAMETER;\r
- }\r
-\r
- //\r
- // driver-name\r
- //\r
- OfwNode->DriverName.Ptr = *Ptr;\r
- OfwNode->DriverName.Len = 0;\r
- while (OfwNode->DriverName.Len < 32 &&\r
- (IsAlnum (**Ptr) || IsDriverNamePunct (**Ptr))\r
- ) {\r
- ++*Ptr;\r
- ++OfwNode->DriverName.Len;\r
- }\r
-\r
- if (OfwNode->DriverName.Len == 0 || OfwNode->DriverName.Len == 32) {\r
- return RETURN_INVALID_PARAMETER;\r
- }\r
-\r
-\r
- //\r
- // unit-address\r
- //\r
- if (**Ptr != '@') {\r
- return RETURN_INVALID_PARAMETER;\r
- }\r
- ++*Ptr;\r
-\r
- OfwNode->UnitAddress.Ptr = *Ptr;\r
- OfwNode->UnitAddress.Len = 0;\r
- while (IsPrintNotDelim (**Ptr)) {\r
- ++*Ptr;\r
- ++OfwNode->UnitAddress.Len;\r
- }\r
-\r
- if (OfwNode->UnitAddress.Len == 0) {\r
- return RETURN_INVALID_PARAMETER;\r
- }\r
-\r
-\r
- //\r
- // device-arguments, may be omitted\r
- //\r
- OfwNode->DeviceArguments.Len = 0;\r
- if (**Ptr == ':') {\r
- ++*Ptr;\r
- OfwNode->DeviceArguments.Ptr = *Ptr;\r
-\r
- while (IsPrintNotDelim (**Ptr)) {\r
- ++*Ptr;\r
- ++OfwNode->DeviceArguments.Len;\r
- }\r
-\r
- if (OfwNode->DeviceArguments.Len == 0) {\r
- return RETURN_INVALID_PARAMETER;\r
- }\r
- }\r
- else {\r
- OfwNode->DeviceArguments.Ptr = NULL;\r
- }\r
-\r
- switch (**Ptr) {\r
- case '\n':\r
- ++*Ptr;\r
- //\r
- // fall through\r
- //\r
-\r
- case '\0':\r
- *IsFinal = TRUE;\r
- break;\r
-\r
- case '/':\r
- *IsFinal = FALSE;\r
- break;\r
-\r
- default:\r
- return RETURN_INVALID_PARAMETER;\r
- }\r
-\r
- DEBUG ((\r
- DEBUG_VERBOSE,\r
- "%a: DriverName=\"%.*a\" UnitAddress=\"%.*a\" DeviceArguments=\"%.*a\"\n",\r
- __FUNCTION__,\r
- OfwNode->DriverName.Len, OfwNode->DriverName.Ptr,\r
- OfwNode->UnitAddress.Len, OfwNode->UnitAddress.Ptr,\r
- OfwNode->DeviceArguments.Len,\r
- OfwNode->DeviceArguments.Ptr == NULL ? "" : OfwNode->DeviceArguments.Ptr\r
- ));\r
- return RETURN_SUCCESS;\r
-}\r
-\r
-\r
-/**\r
-\r
- Translate a PCI-like array of OpenFirmware device nodes to a UEFI device path\r
- fragment.\r
-\r
- @param[in] OfwNode Array of OpenFirmware device nodes to\r
- translate, constituting the beginning of an\r
- OpenFirmware device path.\r
-\r
- @param[in] NumNodes Number of elements in OfwNode.\r
-\r
- @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with\r
- CreateExtraRootBusMap(), to be used for\r
- translating positions of extra root buses to\r
- bus numbers.\r
-\r
- @param[out] Translated Destination array receiving the UEFI path\r
- fragment, allocated by the caller. If the\r
- return value differs from RETURN_SUCCESS, its\r
- contents is indeterminate.\r
-\r
- @param[in out] TranslatedSize On input, the number of CHAR16's in\r
- Translated. On RETURN_SUCCESS this parameter\r
- is assigned the number of non-NUL CHAR16's\r
- written to Translated. In case of other return\r
- values, TranslatedSize is indeterminate.\r
-\r
-\r
- @retval RETURN_SUCCESS Translation successful.\r
-\r
- @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number\r
- of bytes provided.\r
-\r
- @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't\r
- be translated in the current implementation.\r
-\r
- @retval RETURN_PROTOCOL_ERROR The initial OpenFirmware node refers to an\r
- extra PCI root bus (by serial number) that\r
- is invalid according to ExtraPciRoots.\r
-\r
-**/\r
-STATIC\r
-RETURN_STATUS\r
-TranslatePciOfwNodes (\r
- IN CONST OFW_NODE *OfwNode,\r
- IN UINTN NumNodes,\r
- IN CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,\r
- OUT CHAR16 *Translated,\r
- IN OUT UINTN *TranslatedSize\r
- )\r
-{\r
- UINT32 PciRoot;\r
- CHAR8 *Comma;\r
- UINTN FirstNonBridge;\r
- CHAR16 Bridges[BRIDGE_TRANSLATION_OUTPUT_SIZE];\r
- UINTN BridgesLen;\r
- UINT64 PciDevFun[2];\r
- UINTN NumEntries;\r
- UINTN Written;\r
-\r
- //\r
- // Resolve the PCI root bus number.\r
- //\r
- // The initial OFW node for the main root bus (ie. bus number 0) is:\r
- //\r
- // /pci@i0cf8\r
- //\r
- // For extra root buses, the initial OFW node is\r
- //\r
- // /pci@i0cf8,4\r
- // ^\r
- // root bus serial number (not PCI bus number)\r
- //\r
- if (NumNodes < REQUIRED_PCI_OFW_NODES ||\r
- !SubstringEq (OfwNode[0].DriverName, "pci")\r
- ) {\r
- return RETURN_UNSUPPORTED;\r
- }\r
-\r
- PciRoot = 0;\r
- Comma = ScanMem8 (OfwNode[0].UnitAddress.Ptr, OfwNode[0].UnitAddress.Len,\r
- ',');\r
- if (Comma != NULL) {\r
- SUBSTRING PciRootSerialSubString;\r
- UINT64 PciRootSerial;\r
-\r
- //\r
- // Parse the root bus serial number from the unit address after the comma.\r
- //\r
- PciRootSerialSubString.Ptr = Comma + 1;\r
- PciRootSerialSubString.Len = OfwNode[0].UnitAddress.Len -\r
- (PciRootSerialSubString.Ptr -\r
- OfwNode[0].UnitAddress.Ptr);\r
- NumEntries = 1;\r
- if (RETURN_ERROR (ParseUnitAddressHexList (PciRootSerialSubString,\r
- &PciRootSerial, &NumEntries))) {\r
- return RETURN_UNSUPPORTED;\r
- }\r
-\r
- //\r
- // Map the extra root bus's serial number to its actual bus number.\r
- //\r
- if (EFI_ERROR (MapRootBusPosToBusNr (ExtraPciRoots, PciRootSerial,\r
- &PciRoot))) {\r
- return RETURN_PROTOCOL_ERROR;\r
- }\r
- }\r
-\r
- //\r
- // Translate a sequence of PCI bridges. For each bridge, the OFW node is:\r
- //\r
- // pci-bridge@1e[,0]\r
- // ^ ^\r
- // PCI slot & function on the parent, holding the bridge\r
- //\r
- // and the UEFI device path node is:\r
- //\r
- // Pci(0x1E,0x0)\r
- //\r
- FirstNonBridge = 1;\r
- Bridges[0] = L'\0';\r
- BridgesLen = 0;\r
- do {\r
- UINT64 BridgeDevFun[2];\r
- UINTN BridgesFreeBytes;\r
-\r
- if (!SubstringEq (OfwNode[FirstNonBridge].DriverName, "pci-bridge")) {\r
- break;\r
- }\r
-\r
- BridgeDevFun[1] = 0;\r
- NumEntries = sizeof BridgeDevFun / sizeof BridgeDevFun[0];\r
- if (ParseUnitAddressHexList (OfwNode[FirstNonBridge].UnitAddress,\r
- BridgeDevFun, &NumEntries) != RETURN_SUCCESS) {\r
- return RETURN_UNSUPPORTED;\r
- }\r
-\r
- BridgesFreeBytes = sizeof Bridges - BridgesLen * sizeof Bridges[0];\r
- Written = UnicodeSPrintAsciiFormat (Bridges + BridgesLen, BridgesFreeBytes,\r
- "/Pci(0x%Lx,0x%Lx)", BridgeDevFun[0], BridgeDevFun[1]);\r
- BridgesLen += Written;\r
-\r
- //\r
- // There's no way to differentiate between "completely used up without\r
- // truncation" and "truncated", so treat the former as the latter.\r
- //\r
- if (BridgesLen + 1 == BRIDGE_TRANSLATION_OUTPUT_SIZE) {\r
- return RETURN_UNSUPPORTED;\r
- }\r
-\r
- ++FirstNonBridge;\r
- } while (FirstNonBridge < NumNodes);\r
-\r
- if (FirstNonBridge == NumNodes) {\r
- return RETURN_UNSUPPORTED;\r
- }\r
-\r
- //\r
- // Parse the OFW nodes starting with the first non-bridge node.\r
- //\r
- PciDevFun[1] = 0;\r
- NumEntries = sizeof (PciDevFun) / sizeof (PciDevFun[0]);\r
- if (ParseUnitAddressHexList (\r
- OfwNode[FirstNonBridge].UnitAddress,\r
- PciDevFun,\r
- &NumEntries\r
- ) != RETURN_SUCCESS\r
- ) {\r
- return RETURN_UNSUPPORTED;\r
- }\r
-\r
- if (NumNodes >= FirstNonBridge + 3 &&\r
- SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "ide") &&\r
- SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "drive") &&\r
- SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")\r
- ) {\r
- //\r
- // OpenFirmware device path (IDE disk, IDE CD-ROM):\r
- //\r
- // /pci@i0cf8/ide@1,1/drive@0/disk@0\r
- // ^ ^ ^ ^ ^\r
- // | | | | master or slave\r
- // | | | primary or secondary\r
- // | PCI slot & function holding IDE controller\r
- // PCI root at system bus port, PIO\r
- //\r
- // UEFI device path:\r
- //\r
- // PciRoot(0x0)/Pci(0x1,0x1)/Ata(Primary,Master,0x0)\r
- // ^\r
- // fixed LUN\r
- //\r
- UINT64 Secondary;\r
- UINT64 Slave;\r
-\r
- NumEntries = 1;\r
- if (ParseUnitAddressHexList (\r
- OfwNode[FirstNonBridge + 1].UnitAddress,\r
- &Secondary,\r
- &NumEntries\r
- ) != RETURN_SUCCESS ||\r
- Secondary > 1 ||\r
- ParseUnitAddressHexList (\r
- OfwNode[FirstNonBridge + 2].UnitAddress,\r
- &Slave,\r
- &NumEntries // reuse after previous single-element call\r
- ) != RETURN_SUCCESS ||\r
- Slave > 1\r
- ) {\r
- return RETURN_UNSUPPORTED;\r
- }\r
-\r
- Written = UnicodeSPrintAsciiFormat (\r
- Translated,\r
- *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
- "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Ata(%a,%a,0x0)",\r
- PciRoot,\r
- Bridges,\r
- PciDevFun[0],\r
- PciDevFun[1],\r
- Secondary ? "Secondary" : "Primary",\r
- Slave ? "Slave" : "Master"\r
- );\r
- } else if (NumNodes >= FirstNonBridge + 3 &&\r
- SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "pci8086,2922") &&\r
- SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "drive") &&\r
- SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")\r
- ) {\r
- //\r
- // OpenFirmware device path (Q35 SATA disk and CD-ROM):\r
- //\r
- // /pci@i0cf8/pci8086,2922@1f,2/drive@1/disk@0\r
- // ^ ^ ^ ^ ^\r
- // | | | | device number (fixed 0)\r
- // | | | channel (port) number\r
- // | PCI slot & function holding SATA HBA\r
- // PCI root at system bus port, PIO\r
- //\r
- // UEFI device path:\r
- //\r
- // PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x1,0xFFFF,0x0)\r
- // ^ ^ ^\r
- // | | LUN (always 0 on Q35)\r
- // | port multiplier port number,\r
- // | always 0xFFFF on Q35\r
- // channel (port) number\r
- //\r
- UINT64 Channel;\r
-\r
- NumEntries = 1;\r
- if (RETURN_ERROR (ParseUnitAddressHexList (\r
- OfwNode[FirstNonBridge + 1].UnitAddress, &Channel,\r
- &NumEntries))) {\r
- return RETURN_UNSUPPORTED;\r
- }\r
-\r
- Written = UnicodeSPrintAsciiFormat (\r
- Translated,\r
- *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
- "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Sata(0x%Lx,0xFFFF,0x0)",\r
- PciRoot,\r
- Bridges,\r
- PciDevFun[0],\r
- PciDevFun[1],\r
- Channel\r
- );\r
- } else if (NumNodes >= FirstNonBridge + 3 &&\r
- SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "isa") &&\r
- SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "fdc") &&\r
- SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "floppy")\r
- ) {\r
- //\r
- // OpenFirmware device path (floppy disk):\r
- //\r
- // /pci@i0cf8/isa@1/fdc@03f0/floppy@0\r
- // ^ ^ ^ ^\r
- // | | | A: or B:\r
- // | | ISA controller io-port (hex)\r
- // | PCI slot holding ISA controller\r
- // PCI root at system bus port, PIO\r
- //\r
- // UEFI device path:\r
- //\r
- // PciRoot(0x0)/Pci(0x1,0x0)/Floppy(0x0)\r
- // ^\r
- // ACPI UID\r
- //\r
- UINT64 AcpiUid;\r
-\r
- NumEntries = 1;\r
- if (ParseUnitAddressHexList (\r
- OfwNode[FirstNonBridge + 2].UnitAddress,\r
- &AcpiUid,\r
- &NumEntries\r
- ) != RETURN_SUCCESS ||\r
- AcpiUid > 1\r
- ) {\r
- return RETURN_UNSUPPORTED;\r
- }\r
-\r
- Written = UnicodeSPrintAsciiFormat (\r
- Translated,\r
- *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
- "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Floppy(0x%Lx)",\r
- PciRoot,\r
- Bridges,\r
- PciDevFun[0],\r
- PciDevFun[1],\r
- AcpiUid\r
- );\r
- } else if (NumNodes >= FirstNonBridge + 2 &&\r
- SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "scsi") &&\r
- SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "disk")\r
- ) {\r
- //\r
- // OpenFirmware device path (virtio-blk disk):\r
- //\r
- // /pci@i0cf8/scsi@6[,3]/disk@0,0\r
- // ^ ^ ^ ^ ^\r
- // | | | fixed\r
- // | | PCI function corresponding to disk (optional)\r
- // | PCI slot holding disk\r
- // PCI root at system bus port, PIO\r
- //\r
- // UEFI device path prefix:\r
- //\r
- // PciRoot(0x0)/Pci(0x6,0x0)/HD( -- if PCI function is 0 or absent\r
- // PciRoot(0x0)/Pci(0x6,0x3)/HD( -- if PCI function is present and nonzero\r
- //\r
- Written = UnicodeSPrintAsciiFormat (\r
- Translated,\r
- *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
- "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/HD(",\r
- PciRoot,\r
- Bridges,\r
- PciDevFun[0],\r
- PciDevFun[1]\r
- );\r
- } else if (NumNodes >= FirstNonBridge + 3 &&\r
- SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "scsi") &&\r
- SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "channel") &&\r
- SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")\r
- ) {\r
- //\r
- // OpenFirmware device path (virtio-scsi disk):\r
- //\r
- // /pci@i0cf8/scsi@7[,3]/channel@0/disk@2,3\r
- // ^ ^ ^ ^ ^\r
- // | | | | LUN\r
- // | | | target\r
- // | | channel (unused, fixed 0)\r
- // | PCI slot[, function] holding SCSI controller\r
- // PCI root at system bus port, PIO\r
- //\r
- // UEFI device path prefix:\r
- //\r
- // PciRoot(0x0)/Pci(0x7,0x0)/Scsi(0x2,0x3)\r
- // -- if PCI function is 0 or absent\r
- // PciRoot(0x0)/Pci(0x7,0x3)/Scsi(0x2,0x3)\r
- // -- if PCI function is present and nonzero\r
- //\r
- UINT64 TargetLun[2];\r
-\r
- TargetLun[1] = 0;\r
- NumEntries = sizeof (TargetLun) / sizeof (TargetLun[0]);\r
- if (ParseUnitAddressHexList (\r
- OfwNode[FirstNonBridge + 2].UnitAddress,\r
- TargetLun,\r
- &NumEntries\r
- ) != RETURN_SUCCESS\r
- ) {\r
- return RETURN_UNSUPPORTED;\r
- }\r
-\r
- Written = UnicodeSPrintAsciiFormat (\r
- Translated,\r
- *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
- "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Scsi(0x%Lx,0x%Lx)",\r
- PciRoot,\r
- Bridges,\r
- PciDevFun[0],\r
- PciDevFun[1],\r
- TargetLun[0],\r
- TargetLun[1]\r
- );\r
- } else if (NumNodes >= FirstNonBridge + 2 &&\r
- SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "pci8086,5845") &&\r
- SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "namespace")\r
- ) {\r
- //\r
- // OpenFirmware device path (NVMe device):\r
- //\r
- // /pci@i0cf8/pci8086,5845@6[,1]/namespace@1,0\r
- // ^ ^ ^ ^ ^\r
- // | | | | Extended Unique Identifier\r
- // | | | | (EUI-64), big endian interp.\r
- // | | | namespace ID\r
- // | PCI slot & function holding NVMe controller\r
- // PCI root at system bus port, PIO\r
- //\r
- // UEFI device path:\r
- //\r
- // PciRoot(0x0)/Pci(0x6,0x1)/NVMe(0x1,00-00-00-00-00-00-00-00)\r
- // ^ ^\r
- // | octets of the EUI-64\r
- // | in address order\r
- // namespace ID\r
- //\r
- UINT64 Namespace[2];\r
- UINTN RequiredEntries;\r
- UINT8 *Eui64;\r
-\r
- RequiredEntries = sizeof (Namespace) / sizeof (Namespace[0]);\r
- NumEntries = RequiredEntries;\r
- if (ParseUnitAddressHexList (\r
- OfwNode[FirstNonBridge + 1].UnitAddress,\r
- Namespace,\r
- &NumEntries\r
- ) != RETURN_SUCCESS ||\r
- NumEntries != RequiredEntries ||\r
- Namespace[0] == 0 ||\r
- Namespace[0] >= MAX_UINT32\r
- ) {\r
- return RETURN_UNSUPPORTED;\r
- }\r
-\r
- Eui64 = (UINT8 *)&Namespace[1];\r
- Written = UnicodeSPrintAsciiFormat (\r
- Translated,\r
- *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
- "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/"\r
- "NVMe(0x%Lx,%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x)",\r
- PciRoot,\r
- Bridges,\r
- PciDevFun[0],\r
- PciDevFun[1],\r
- Namespace[0],\r
- Eui64[7], Eui64[6], Eui64[5], Eui64[4],\r
- Eui64[3], Eui64[2], Eui64[1], Eui64[0]\r
- );\r
- } else {\r
- //\r
- // Generic OpenFirmware device path for PCI devices:\r
- //\r
- // /pci@i0cf8/ethernet@3[,2]\r
- // ^ ^\r
- // | PCI slot[, function] holding Ethernet card\r
- // PCI root at system bus port, PIO\r
- //\r
- // UEFI device path prefix (dependent on presence of nonzero PCI function):\r
- //\r
- // PciRoot(0x0)/Pci(0x3,0x0)\r
- // PciRoot(0x0)/Pci(0x3,0x2)\r
- //\r
- Written = UnicodeSPrintAsciiFormat (\r
- Translated,\r
- *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
- "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",\r
- PciRoot,\r
- Bridges,\r
- PciDevFun[0],\r
- PciDevFun[1]\r
- );\r
- }\r
-\r
- //\r
- // There's no way to differentiate between "completely used up without\r
- // truncation" and "truncated", so treat the former as the latter, and return\r
- // success only for "some room left unused".\r
- //\r
- if (Written + 1 < *TranslatedSize) {\r
- *TranslatedSize = Written;\r
- return RETURN_SUCCESS;\r
- }\r
-\r
- return RETURN_BUFFER_TOO_SMALL;\r
-}\r
-\r
-\r
-//\r
-// A type providing easy raw access to the base address of a virtio-mmio\r
-// transport.\r
-//\r
-typedef union {\r
- UINT64 Uint64;\r
- UINT8 Raw[8];\r
-} VIRTIO_MMIO_BASE_ADDRESS;\r
-\r
-\r
-/**\r
-\r
- Translate an MMIO-like array of OpenFirmware device nodes to a UEFI device\r
- path fragment.\r
-\r
- @param[in] OfwNode Array of OpenFirmware device nodes to\r
- translate, constituting the beginning of an\r
- OpenFirmware device path.\r
-\r
- @param[in] NumNodes Number of elements in OfwNode.\r
-\r
- @param[out] Translated Destination array receiving the UEFI path\r
- fragment, allocated by the caller. If the\r
- return value differs from RETURN_SUCCESS, its\r
- contents is indeterminate.\r
-\r
- @param[in out] TranslatedSize On input, the number of CHAR16's in\r
- Translated. On RETURN_SUCCESS this parameter\r
- is assigned the number of non-NUL CHAR16's\r
- written to Translated. In case of other return\r
- values, TranslatedSize is indeterminate.\r
-\r
-\r
- @retval RETURN_SUCCESS Translation successful.\r
-\r
- @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number\r
- of bytes provided.\r
-\r
- @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't\r
- be translated in the current implementation.\r
-\r
-**/\r
-STATIC\r
-RETURN_STATUS\r
-TranslateMmioOfwNodes (\r
- IN CONST OFW_NODE *OfwNode,\r
- IN UINTN NumNodes,\r
- OUT CHAR16 *Translated,\r
- IN OUT UINTN *TranslatedSize\r
- )\r
-{\r
- VIRTIO_MMIO_BASE_ADDRESS VirtioMmioBase;\r
- CHAR16 VenHwString[60 + 1];\r
- UINTN NumEntries;\r
- UINTN Written;\r
-\r
- //\r
- // Get the base address of the virtio-mmio transport.\r
- //\r
- if (NumNodes < REQUIRED_MMIO_OFW_NODES ||\r
- !SubstringEq (OfwNode[0].DriverName, "virtio-mmio")\r
- ) {\r
- return RETURN_UNSUPPORTED;\r
- }\r
- NumEntries = 1;\r
- if (ParseUnitAddressHexList (\r
- OfwNode[0].UnitAddress,\r
- &VirtioMmioBase.Uint64,\r
- &NumEntries\r
- ) != RETURN_SUCCESS\r
- ) {\r
- return RETURN_UNSUPPORTED;\r
- }\r
-\r
- UnicodeSPrintAsciiFormat (VenHwString, sizeof VenHwString,\r
- "VenHw(%g,%02X%02X%02X%02X%02X%02X%02X%02X)", &gVirtioMmioTransportGuid,\r
- VirtioMmioBase.Raw[0], VirtioMmioBase.Raw[1], VirtioMmioBase.Raw[2],\r
- VirtioMmioBase.Raw[3], VirtioMmioBase.Raw[4], VirtioMmioBase.Raw[5],\r
- VirtioMmioBase.Raw[6], VirtioMmioBase.Raw[7]);\r
-\r
- if (NumNodes >= 2 &&\r
- SubstringEq (OfwNode[1].DriverName, "disk")) {\r
- //\r
- // OpenFirmware device path (virtio-blk disk):\r
- //\r
- // /virtio-mmio@000000000a003c00/disk@0,0\r
- // ^ ^ ^\r
- // | fixed\r
- // base address of virtio-mmio register block\r
- //\r
- // UEFI device path prefix:\r
- //\r
- // <VenHwString>/HD(\r
- //\r
- Written = UnicodeSPrintAsciiFormat (\r
- Translated,\r
- *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
- "%s/HD(",\r
- VenHwString\r
- );\r
- } else if (NumNodes >= 3 &&\r
- SubstringEq (OfwNode[1].DriverName, "channel") &&\r
- SubstringEq (OfwNode[2].DriverName, "disk")) {\r
- //\r
- // OpenFirmware device path (virtio-scsi disk):\r
- //\r
- // /virtio-mmio@000000000a003a00/channel@0/disk@2,3\r
- // ^ ^ ^ ^\r
- // | | | LUN\r
- // | | target\r
- // | channel (unused, fixed 0)\r
- // base address of virtio-mmio register block\r
- //\r
- // UEFI device path prefix:\r
- //\r
- // <VenHwString>/Scsi(0x2,0x3)\r
- //\r
- UINT64 TargetLun[2];\r
-\r
- TargetLun[1] = 0;\r
- NumEntries = sizeof (TargetLun) / sizeof (TargetLun[0]);\r
- if (ParseUnitAddressHexList (\r
- OfwNode[2].UnitAddress,\r
- TargetLun,\r
- &NumEntries\r
- ) != RETURN_SUCCESS\r
- ) {\r
- return RETURN_UNSUPPORTED;\r
- }\r
-\r
- Written = UnicodeSPrintAsciiFormat (\r
- Translated,\r
- *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
- "%s/Scsi(0x%Lx,0x%Lx)",\r
- VenHwString,\r
- TargetLun[0],\r
- TargetLun[1]\r
- );\r
- } else if (NumNodes >= 2 &&\r
- SubstringEq (OfwNode[1].DriverName, "ethernet-phy")) {\r
- //\r
- // OpenFirmware device path (virtio-net NIC):\r
- //\r
- // /virtio-mmio@000000000a003e00/ethernet-phy@0\r
- // ^ ^\r
- // | fixed\r
- // base address of virtio-mmio register block\r
- //\r
- // UEFI device path prefix (dependent on presence of nonzero PCI function):\r
- //\r
- // <VenHwString>/MAC(\r
- //\r
- Written = UnicodeSPrintAsciiFormat (\r
- Translated,\r
- *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
- "%s/MAC(",\r
- VenHwString\r
- );\r
- } else {\r
- return RETURN_UNSUPPORTED;\r
- }\r
-\r
- //\r
- // There's no way to differentiate between "completely used up without\r
- // truncation" and "truncated", so treat the former as the latter, and return\r
- // success only for "some room left unused".\r
- //\r
- if (Written + 1 < *TranslatedSize) {\r
- *TranslatedSize = Written;\r
- return RETURN_SUCCESS;\r
- }\r
-\r
- return RETURN_BUFFER_TOO_SMALL;\r
-}\r
-\r
-\r
-/**\r
-\r
- Translate an array of OpenFirmware device nodes to a UEFI device path\r
- fragment.\r
-\r
- @param[in] OfwNode Array of OpenFirmware device nodes to\r
- translate, constituting the beginning of an\r
- OpenFirmware device path.\r
-\r
- @param[in] NumNodes Number of elements in OfwNode.\r
-\r
- @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with\r
- CreateExtraRootBusMap(), to be used for\r
- translating positions of extra root buses to\r
- bus numbers.\r
-\r
- @param[out] Translated Destination array receiving the UEFI path\r
- fragment, allocated by the caller. If the\r
- return value differs from RETURN_SUCCESS, its\r
- contents is indeterminate.\r
-\r
- @param[in out] TranslatedSize On input, the number of CHAR16's in\r
- Translated. On RETURN_SUCCESS this parameter\r
- is assigned the number of non-NUL CHAR16's\r
- written to Translated. In case of other return\r
- values, TranslatedSize is indeterminate.\r
-\r
-\r
- @retval RETURN_SUCCESS Translation successful.\r
-\r
- @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number\r
- of bytes provided.\r
-\r
- @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't\r
- be translated in the current implementation.\r
-\r
- @retval RETURN_PROTOCOL_ERROR The array of OpenFirmware device nodes has\r
- been (partially) recognized, but it contains\r
- a logic error / doesn't match system state.\r
-\r
-**/\r
-STATIC\r
-RETURN_STATUS\r
-TranslateOfwNodes (\r
- IN CONST OFW_NODE *OfwNode,\r
- IN UINTN NumNodes,\r
- IN CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,\r
- OUT CHAR16 *Translated,\r
- IN OUT UINTN *TranslatedSize\r
- )\r
-{\r
- RETURN_STATUS Status;\r
-\r
- Status = RETURN_UNSUPPORTED;\r
-\r
- if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {\r
- Status = TranslatePciOfwNodes (OfwNode, NumNodes, ExtraPciRoots,\r
- Translated, TranslatedSize);\r
- }\r
- if (Status == RETURN_UNSUPPORTED &&\r
- FeaturePcdGet (PcdQemuBootOrderMmioTranslation)) {\r
- Status = TranslateMmioOfwNodes (OfwNode, NumNodes, Translated,\r
- TranslatedSize);\r
- }\r
- return Status;\r
-}\r
-\r
-/**\r
-\r
- Translate an OpenFirmware device path fragment to a UEFI device path\r
- fragment, and advance in the input string.\r
-\r
- @param[in out] Ptr Address of the pointer pointing to the start\r
- of the path string. After successful\r
- translation (RETURN_SUCCESS) or at least\r
- successful parsing (RETURN_UNSUPPORTED,\r
- RETURN_BUFFER_TOO_SMALL), *Ptr is set to the\r
- byte immediately following the consumed\r
- characters. In other error cases, it points to\r
- the byte that caused the error.\r
-\r
- @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with\r
- CreateExtraRootBusMap(), to be used for\r
- translating positions of extra root buses to\r
- bus numbers.\r
-\r
- @param[out] Translated Destination array receiving the UEFI path\r
- fragment, allocated by the caller. If the\r
- return value differs from RETURN_SUCCESS, its\r
- contents is indeterminate.\r
-\r
- @param[in out] TranslatedSize On input, the number of CHAR16's in\r
- Translated. On RETURN_SUCCESS this parameter\r
- is assigned the number of non-NUL CHAR16's\r
- written to Translated. In case of other return\r
- values, TranslatedSize is indeterminate.\r
-\r
-\r
- @retval RETURN_SUCCESS Translation successful.\r
-\r
- @retval RETURN_BUFFER_TOO_SMALL The OpenFirmware device path was parsed\r
- successfully, but its translation did not\r
- fit into the number of bytes provided.\r
- Further calls to this function are\r
- possible.\r
-\r
- @retval RETURN_UNSUPPORTED The OpenFirmware device path was parsed\r
- successfully, but it can't be translated in\r
- the current implementation. Further calls\r
- to this function are possible.\r
-\r
- @retval RETURN_PROTOCOL_ERROR The OpenFirmware device path has been\r
- (partially) recognized, but it contains a\r
- logic error / doesn't match system state.\r
- Further calls to this function are\r
- possible.\r
-\r
- @retval RETURN_NOT_FOUND Translation terminated. On input, *Ptr was\r
- pointing to the empty string or "HALT". On\r
- output, *Ptr points to the empty string\r
- (ie. "HALT" is consumed transparently when\r
- present).\r
-\r
- @retval RETURN_INVALID_PARAMETER Parse error. This is a permanent error.\r
-\r
-**/\r
-STATIC\r
-RETURN_STATUS\r
-TranslateOfwPath (\r
- IN OUT CONST CHAR8 **Ptr,\r
- IN CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,\r
- OUT CHAR16 *Translated,\r
- IN OUT UINTN *TranslatedSize\r
- )\r
-{\r
- UINTN NumNodes;\r
- RETURN_STATUS Status;\r
- OFW_NODE Node[EXAMINED_OFW_NODES];\r
- BOOLEAN IsFinal;\r
- OFW_NODE Skip;\r
-\r
- IsFinal = FALSE;\r
- NumNodes = 0;\r
- if (AsciiStrCmp (*Ptr, "HALT") == 0) {\r
- *Ptr += 4;\r
- Status = RETURN_NOT_FOUND;\r
- } else {\r
- Status = ParseOfwNode (Ptr, &Node[NumNodes], &IsFinal);\r
- }\r
-\r
- if (Status == RETURN_NOT_FOUND) {\r
- DEBUG ((DEBUG_VERBOSE, "%a: no more nodes\n", __FUNCTION__));\r
- return RETURN_NOT_FOUND;\r
- }\r
-\r
- while (Status == RETURN_SUCCESS && !IsFinal) {\r
- ++NumNodes;\r
- Status = ParseOfwNode (\r
- Ptr,\r
- (NumNodes < EXAMINED_OFW_NODES) ? &Node[NumNodes] : &Skip,\r
- &IsFinal\r
- );\r
- }\r
-\r
- switch (Status) {\r
- case RETURN_SUCCESS:\r
- ++NumNodes;\r
- break;\r
-\r
- case RETURN_INVALID_PARAMETER:\r
- DEBUG ((DEBUG_VERBOSE, "%a: parse error\n", __FUNCTION__));\r
- return RETURN_INVALID_PARAMETER;\r
-\r
- default:\r
- ASSERT (0);\r
- }\r
-\r
- Status = TranslateOfwNodes (\r
- Node,\r
- NumNodes < EXAMINED_OFW_NODES ? NumNodes : EXAMINED_OFW_NODES,\r
- ExtraPciRoots,\r
- Translated,\r
- TranslatedSize);\r
- switch (Status) {\r
- case RETURN_SUCCESS:\r
- DEBUG ((DEBUG_VERBOSE, "%a: success: \"%s\"\n", __FUNCTION__, Translated));\r
- break;\r
-\r
- case RETURN_BUFFER_TOO_SMALL:\r
- DEBUG ((DEBUG_VERBOSE, "%a: buffer too small\n", __FUNCTION__));\r
- break;\r
-\r
- case RETURN_UNSUPPORTED:\r
- DEBUG ((DEBUG_VERBOSE, "%a: unsupported\n", __FUNCTION__));\r
- break;\r
-\r
- case RETURN_PROTOCOL_ERROR:\r
- DEBUG ((DEBUG_VERBOSE, "%a: logic error / system state mismatch\n",\r
- __FUNCTION__));\r
- break;\r
-\r
- default:\r
- ASSERT (0);\r
- }\r
- return Status;\r
-}\r
-\r
-\r
-/**\r
-\r
- Convert the UEFI DevicePath to full text representation with DevPathToText,\r
- then match the UEFI device path fragment in Translated against it.\r
-\r
- @param[in] Translated UEFI device path fragment, translated from\r
- OpenFirmware format, to search for.\r
-\r
- @param[in] TranslatedLength The length of Translated in CHAR16's.\r
-\r
- @param[in] DevicePath Boot option device path whose textual rendering\r
- to search in.\r
-\r
- @param[in] DevPathToText Binary-to-text conversion protocol for DevicePath.\r
-\r
-\r
- @retval TRUE If Translated was found at the beginning of DevicePath after\r
- converting the latter to text.\r
-\r
- @retval FALSE If DevicePath was NULL, or it could not be converted, or there\r
- was no match.\r
-\r
-**/\r
-STATIC\r
-BOOLEAN\r
-Match (\r
- IN CONST CHAR16 *Translated,\r
- IN UINTN TranslatedLength,\r
- IN EFI_DEVICE_PATH_PROTOCOL *DevicePath\r
- )\r
-{\r
- CHAR16 *Converted;\r
- BOOLEAN Result;\r
- VOID *FileBuffer;\r
- UINTN FileSize;\r
- EFI_DEVICE_PATH_PROTOCOL *AbsDevicePath;\r
- CHAR16 *AbsConverted;\r
- BOOLEAN Shortform;\r
- EFI_DEVICE_PATH_PROTOCOL *Node;\r
-\r
- Converted = ConvertDevicePathToText (\r
- DevicePath,\r
- FALSE, // DisplayOnly\r
- FALSE // AllowShortcuts\r
- );\r
- if (Converted == NULL) {\r
- return FALSE;\r
- }\r
-\r
- Result = FALSE;\r
- Shortform = FALSE;\r
- //\r
- // Expand the short-form device path to full device path\r
- //\r
- if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&\r
- (DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP)) {\r
- //\r
- // Harddrive shortform device path\r
- //\r
- Shortform = TRUE;\r
- } else if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&\r
- (DevicePathSubType (DevicePath) == MEDIA_FILEPATH_DP)) {\r
- //\r
- // File-path shortform device path\r
- //\r
- Shortform = TRUE;\r
- } else if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&\r
- (DevicePathSubType (DevicePath) == MSG_URI_DP)) {\r
- //\r
- // URI shortform device path\r
- //\r
- Shortform = TRUE;\r
- } else {\r
- for ( Node = DevicePath\r
- ; !IsDevicePathEnd (Node)\r
- ; Node = NextDevicePathNode (Node)\r
- ) {\r
- if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) &&\r
- ((DevicePathSubType (Node) == MSG_USB_CLASS_DP) ||\r
- (DevicePathSubType (Node) == MSG_USB_WWID_DP))) {\r
- Shortform = TRUE;\r
- break;\r
- }\r
- }\r
- }\r
-\r
- //\r
- // Attempt to expand any relative UEFI device path to\r
- // an absolute device path first.\r
- //\r
- if (Shortform) {\r
- FileBuffer = EfiBootManagerGetLoadOptionBuffer (\r
- DevicePath, &AbsDevicePath, &FileSize\r
- );\r
- if (FileBuffer == NULL) {\r
- goto Exit;\r
- }\r
- FreePool (FileBuffer);\r
- AbsConverted = ConvertDevicePathToText (AbsDevicePath, FALSE, FALSE);\r
- FreePool (AbsDevicePath);\r
- if (AbsConverted == NULL) {\r
- goto Exit;\r
- }\r
- DEBUG ((DEBUG_VERBOSE,\r
- "%a: expanded relative device path \"%s\" for prefix matching\n",\r
- __FUNCTION__, Converted));\r
- FreePool (Converted);\r
- Converted = AbsConverted;\r
- }\r
-\r
- //\r
- // Is Translated a prefix of Converted?\r
- //\r
- Result = (BOOLEAN)(StrnCmp (Converted, Translated, TranslatedLength) == 0);\r
- DEBUG ((\r
- DEBUG_VERBOSE,\r
- "%a: against \"%s\": %a\n",\r
- __FUNCTION__,\r
- Converted,\r
- Result ? "match" : "no match"\r
- ));\r
-Exit:\r
- FreePool (Converted);\r
- return Result;\r
-}\r
-\r
-\r
-/**\r
- Append some of the unselected active boot options to the boot order.\r
-\r
- This function should accommodate any further policy changes in "boot option\r
- survival". Currently we're adding back everything that starts with neither\r
- PciRoot() nor HD() nor a virtio-mmio VenHw() node.\r
-\r
- @param[in,out] BootOrder The structure holding the boot order to\r
- complete. The caller is responsible for\r
- initializing (and potentially populating) it\r
- before calling this function.\r
-\r
- @param[in,out] ActiveOption The array of active boot options to scan.\r
- Entries marked as Appended will be skipped.\r
- Those of the rest that satisfy the survival\r
- policy will be added to BootOrder with\r
- BootOrderAppend().\r
-\r
- @param[in] ActiveCount Number of elements in ActiveOption.\r
-\r
-\r
- @retval RETURN_SUCCESS BootOrder has been extended with any eligible boot\r
- options.\r
-\r
- @return Error codes returned by BootOrderAppend().\r
-**/\r
-STATIC\r
-RETURN_STATUS\r
-BootOrderComplete (\r
- IN OUT BOOT_ORDER *BootOrder,\r
- IN OUT ACTIVE_OPTION *ActiveOption,\r
- IN UINTN ActiveCount\r
- )\r
-{\r
- RETURN_STATUS Status;\r
- UINTN Idx;\r
-\r
- Status = RETURN_SUCCESS;\r
- Idx = 0;\r
- while (!RETURN_ERROR (Status) && Idx < ActiveCount) {\r
- if (!ActiveOption[Idx].Appended) {\r
- CONST EFI_BOOT_MANAGER_LOAD_OPTION *Current;\r
- CONST EFI_DEVICE_PATH_PROTOCOL *FirstNode;\r
-\r
- Current = ActiveOption[Idx].BootOption;\r
- FirstNode = Current->FilePath;\r
- if (FirstNode != NULL) {\r
- CHAR16 *Converted;\r
- STATIC CHAR16 ConvFallBack[] = L"<unable to convert>";\r
- BOOLEAN Keep;\r
-\r
- Converted = ConvertDevicePathToText (FirstNode, FALSE, FALSE);\r
- if (Converted == NULL) {\r
- Converted = ConvFallBack;\r
- }\r
-\r
- Keep = TRUE;\r
- if (DevicePathType(FirstNode) == MEDIA_DEVICE_PATH &&\r
- DevicePathSubType(FirstNode) == MEDIA_HARDDRIVE_DP) {\r
- //\r
- // drop HD()\r
- //\r
- Keep = FALSE;\r
- } else if (DevicePathType(FirstNode) == ACPI_DEVICE_PATH &&\r
- DevicePathSubType(FirstNode) == ACPI_DP) {\r
- ACPI_HID_DEVICE_PATH *Acpi;\r
-\r
- Acpi = (ACPI_HID_DEVICE_PATH *) FirstNode;\r
- if ((Acpi->HID & PNP_EISA_ID_MASK) == PNP_EISA_ID_CONST &&\r
- EISA_ID_TO_NUM (Acpi->HID) == 0x0a03) {\r
- //\r
- // drop PciRoot() if we enabled the user to select PCI-like boot\r
- // options, by providing translation for such OFW device path\r
- // fragments\r
- //\r
- Keep = !FeaturePcdGet (PcdQemuBootOrderPciTranslation);\r
- }\r
- } else if (DevicePathType(FirstNode) == HARDWARE_DEVICE_PATH &&\r
- DevicePathSubType(FirstNode) == HW_VENDOR_DP) {\r
- VENDOR_DEVICE_PATH *VenHw;\r
-\r
- VenHw = (VENDOR_DEVICE_PATH *)FirstNode;\r
- if (CompareGuid (&VenHw->Guid, &gVirtioMmioTransportGuid)) {\r
- //\r
- // drop virtio-mmio if we enabled the user to select boot options\r
- // referencing such device paths\r
- //\r
- Keep = !FeaturePcdGet (PcdQemuBootOrderMmioTranslation);\r
- }\r
- }\r
-\r
- if (Keep) {\r
- Status = BootOrderAppend (BootOrder, &ActiveOption[Idx]);\r
- if (!RETURN_ERROR (Status)) {\r
- DEBUG ((DEBUG_VERBOSE, "%a: keeping \"%s\"\n", __FUNCTION__,\r
- Converted));\r
- }\r
- } else {\r
- DEBUG ((DEBUG_VERBOSE, "%a: dropping \"%s\"\n", __FUNCTION__,\r
- Converted));\r
- }\r
-\r
- if (Converted != ConvFallBack) {\r
- FreePool (Converted);\r
- }\r
- }\r
- }\r
- ++Idx;\r
- }\r
- return Status;\r
-}\r
-\r
-\r
-/**\r
- Delete Boot#### variables that stand for such active boot options that have\r
- been dropped (ie. have not been selected by either matching or "survival\r
- policy").\r
-\r
- @param[in] ActiveOption The array of active boot options to scan. Each\r
- entry not marked as appended will trigger the\r
- deletion of the matching Boot#### variable.\r
-\r
- @param[in] ActiveCount Number of elements in ActiveOption.\r
-**/\r
-STATIC\r
-VOID\r
-PruneBootVariables (\r
- IN CONST ACTIVE_OPTION *ActiveOption,\r
- IN UINTN ActiveCount\r
- )\r
-{\r
- UINTN Idx;\r
-\r
- for (Idx = 0; Idx < ActiveCount; ++Idx) {\r
- if (!ActiveOption[Idx].Appended) {\r
- CHAR16 VariableName[9];\r
-\r
- UnicodeSPrintAsciiFormat (VariableName, sizeof VariableName, "Boot%04x",\r
- ActiveOption[Idx].BootOption->OptionNumber);\r
-\r
- //\r
- // "The space consumed by the deleted variable may not be available until\r
- // the next power cycle", but that's good enough.\r
- //\r
- gRT->SetVariable (VariableName, &gEfiGlobalVariableGuid,\r
- 0, // Attributes, 0 means deletion\r
- 0, // DataSize, 0 means deletion\r
- NULL // Data\r
- );\r
- }\r
- }\r
-}\r
-\r
-\r
-/**\r
-\r
- Set the boot order based on configuration retrieved from QEMU.\r
-\r
- Attempt to retrieve the "bootorder" fw_cfg file from QEMU. Translate the\r
- OpenFirmware device paths therein to UEFI device path fragments. Match the\r
- translated fragments against the current list of boot options, and rewrite\r
- the BootOrder NvVar so that it corresponds to the order described in fw_cfg.\r
-\r
- Platform BDS should call this function after EfiBootManagerConnectAll () and\r
- EfiBootManagerRefreshAllBootOption () return.\r
-\r
- @retval RETURN_SUCCESS BootOrder NvVar rewritten.\r
-\r
- @retval RETURN_UNSUPPORTED QEMU's fw_cfg is not supported.\r
-\r
- @retval RETURN_NOT_FOUND Empty or nonexistent "bootorder" fw_cfg\r
- file, or no match found between the\r
- "bootorder" fw_cfg file and BootOptionList.\r
-\r
- @retval RETURN_INVALID_PARAMETER Parse error in the "bootorder" fw_cfg file.\r
-\r
- @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.\r
-\r
- @return Values returned by gBS->LocateProtocol ()\r
- or gRT->SetVariable ().\r
-\r
-**/\r
-RETURN_STATUS\r
-SetBootOrderFromQemu (\r
- VOID\r
- )\r
-{\r
- RETURN_STATUS Status;\r
- FIRMWARE_CONFIG_ITEM FwCfgItem;\r
- UINTN FwCfgSize;\r
- CHAR8 *FwCfg;\r
- CONST CHAR8 *FwCfgPtr;\r
-\r
- BOOT_ORDER BootOrder;\r
- ACTIVE_OPTION *ActiveOption;\r
- UINTN ActiveCount;\r
-\r
- EXTRA_ROOT_BUS_MAP *ExtraPciRoots;\r
-\r
- UINTN TranslatedSize;\r
- CHAR16 Translated[TRANSLATION_OUTPUT_SIZE];\r
- EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
- UINTN BootOptionCount;\r
-\r
- Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);\r
- if (Status != RETURN_SUCCESS) {\r
- return Status;\r
- }\r
-\r
- if (FwCfgSize == 0) {\r
- return RETURN_NOT_FOUND;\r
- }\r
-\r
- FwCfg = AllocatePool (FwCfgSize);\r
- if (FwCfg == NULL) {\r
- return RETURN_OUT_OF_RESOURCES;\r
- }\r
-\r
- QemuFwCfgSelectItem (FwCfgItem);\r
- QemuFwCfgReadBytes (FwCfgSize, FwCfg);\r
- if (FwCfg[FwCfgSize - 1] != '\0') {\r
- Status = RETURN_INVALID_PARAMETER;\r
- goto ErrorFreeFwCfg;\r
- }\r
-\r
- DEBUG ((DEBUG_VERBOSE, "%a: FwCfg:\n", __FUNCTION__));\r
- DEBUG ((DEBUG_VERBOSE, "%a\n", FwCfg));\r
- DEBUG ((DEBUG_VERBOSE, "%a: FwCfg: <end>\n", __FUNCTION__));\r
- FwCfgPtr = FwCfg;\r
-\r
- BootOrder.Produced = 0;\r
- BootOrder.Allocated = 1;\r
- BootOrder.Data = AllocatePool (\r
- BootOrder.Allocated * sizeof (*BootOrder.Data)\r
- );\r
- if (BootOrder.Data == NULL) {\r
- Status = RETURN_OUT_OF_RESOURCES;\r
- goto ErrorFreeFwCfg;\r
- }\r
-\r
- BootOptions = EfiBootManagerGetLoadOptions (\r
- &BootOptionCount, LoadOptionTypeBoot\r
- );\r
- if (BootOptions == NULL) {\r
- Status = RETURN_NOT_FOUND;\r
- goto ErrorFreeBootOrder;\r
- }\r
-\r
- Status = CollectActiveOptions (\r
- BootOptions, BootOptionCount, &ActiveOption, &ActiveCount\r
- );\r
- if (RETURN_ERROR (Status)) {\r
- goto ErrorFreeBootOptions;\r
- }\r
-\r
- if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {\r
- Status = CreateExtraRootBusMap (&ExtraPciRoots);\r
- if (EFI_ERROR (Status)) {\r
- goto ErrorFreeActiveOption;\r
- }\r
- } else {\r
- ExtraPciRoots = NULL;\r
- }\r
-\r
- //\r
- // translate each OpenFirmware path\r
- //\r
- TranslatedSize = sizeof (Translated) / sizeof (Translated[0]);\r
- Status = TranslateOfwPath (&FwCfgPtr, ExtraPciRoots, Translated,\r
- &TranslatedSize);\r
- while (Status == RETURN_SUCCESS ||\r
- Status == RETURN_UNSUPPORTED ||\r
- Status == RETURN_PROTOCOL_ERROR ||\r
- Status == RETURN_BUFFER_TOO_SMALL) {\r
- if (Status == RETURN_SUCCESS) {\r
- UINTN Idx;\r
-\r
- //\r
- // match translated OpenFirmware path against all active boot options\r
- //\r
- for (Idx = 0; Idx < ActiveCount; ++Idx) {\r
- if (Match (\r
- Translated,\r
- TranslatedSize, // contains length, not size, in CHAR16's here\r
- ActiveOption[Idx].BootOption->FilePath\r
- )\r
- ) {\r
- //\r
- // match found, store ID and continue with next OpenFirmware path\r
- //\r
- Status = BootOrderAppend (&BootOrder, &ActiveOption[Idx]);\r
- if (Status != RETURN_SUCCESS) {\r
- goto ErrorFreeExtraPciRoots;\r
- }\r
- break;\r
- }\r
- } // scanned all active boot options\r
- } // translation successful\r
-\r
- TranslatedSize = sizeof (Translated) / sizeof (Translated[0]);\r
- Status = TranslateOfwPath (&FwCfgPtr, ExtraPciRoots, Translated,\r
- &TranslatedSize);\r
- } // scanning of OpenFirmware paths done\r
-\r
- if (Status == RETURN_NOT_FOUND && BootOrder.Produced > 0) {\r
- //\r
- // No more OpenFirmware paths, some matches found: rewrite BootOrder NvVar.\r
- // Some of the active boot options that have not been selected over fw_cfg\r
- // should be preserved at the end of the boot order.\r
- //\r
- Status = BootOrderComplete (&BootOrder, ActiveOption, ActiveCount);\r
- if (RETURN_ERROR (Status)) {\r
- goto ErrorFreeExtraPciRoots;\r
- }\r
-\r
- //\r
- // See Table 10 in the UEFI Spec 2.3.1 with Errata C for the required\r
- // attributes.\r
- //\r
- Status = gRT->SetVariable (\r
- L"BootOrder",\r
- &gEfiGlobalVariableGuid,\r
- EFI_VARIABLE_NON_VOLATILE |\r
- EFI_VARIABLE_BOOTSERVICE_ACCESS |\r
- EFI_VARIABLE_RUNTIME_ACCESS,\r
- BootOrder.Produced * sizeof (*BootOrder.Data),\r
- BootOrder.Data\r
- );\r
- if (EFI_ERROR (Status)) {\r
- DEBUG ((DEBUG_ERROR, "%a: setting BootOrder: %r\n", __FUNCTION__, Status));\r
- goto ErrorFreeExtraPciRoots;\r
- }\r
-\r
- DEBUG ((DEBUG_INFO, "%a: setting BootOrder: success\n", __FUNCTION__));\r
- PruneBootVariables (ActiveOption, ActiveCount);\r
- }\r
-\r
-ErrorFreeExtraPciRoots:\r
- if (ExtraPciRoots != NULL) {\r
- DestroyExtraRootBusMap (ExtraPciRoots);\r
- }\r
-\r
-ErrorFreeActiveOption:\r
- FreePool (ActiveOption);\r
-\r
-ErrorFreeBootOptions:\r
- EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
-\r
-ErrorFreeBootOrder:\r
- FreePool (BootOrder.Data);\r
-\r
-ErrorFreeFwCfg:\r
- FreePool (FwCfg);\r
-\r
- return Status;\r
-}\r
-\r
-\r
-/**\r
- Calculate the number of seconds we should be showing the FrontPage progress\r
- bar for.\r
-\r
- @return The TimeoutDefault argument for PlatformBdsEnterFrontPage().\r
-**/\r
-UINT16\r
-GetFrontPageTimeoutFromQemu (\r
- VOID\r
- )\r
-{\r
- FIRMWARE_CONFIG_ITEM BootMenuWaitItem;\r
- UINTN BootMenuWaitSize;\r
-\r
- QemuFwCfgSelectItem (QemuFwCfgItemBootMenu);\r
- if (QemuFwCfgRead16 () == 0) {\r
- //\r
- // The user specified "-boot menu=off", or didn't specify "-boot\r
- // menu=(on|off)" at all. Return the platform default.\r
- //\r
- return PcdGet16 (PcdPlatformBootTimeOut);\r
- }\r
-\r
- if (RETURN_ERROR (QemuFwCfgFindFile ("etc/boot-menu-wait", &BootMenuWaitItem,\r
- &BootMenuWaitSize)) ||\r
- BootMenuWaitSize != sizeof (UINT16)) {\r
- //\r
- // "-boot menu=on" was specified without "splash-time=N". In this case,\r
- // return three seconds if the platform default would cause us to skip the\r
- // front page, and return the platform default otherwise.\r
- //\r
- UINT16 Timeout;\r
-\r
- Timeout = PcdGet16 (PcdPlatformBootTimeOut);\r
- if (Timeout == 0) {\r
- Timeout = 3;\r
- }\r
- return Timeout;\r
- }\r
-\r
- //\r
- // "-boot menu=on,splash-time=N" was specified, where N is in units of\r
- // milliseconds. The Intel BDS Front Page progress bar only supports whole\r
- // seconds, round N up.\r
- //\r
- QemuFwCfgSelectItem (BootMenuWaitItem);\r
- return (UINT16)((QemuFwCfgRead16 () + 999) / 1000);\r
-}\r
+++ /dev/null
-## @file\r
-# Rewrite the BootOrder NvVar based on QEMU's "bootorder" fw_cfg file.\r
-#\r
-# Copyright (C) 2012 - 2014, Red Hat, Inc.\r
-# Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.<BR>\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,\r
-# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR\r
-# IMPLIED.\r
-#\r
-##\r
-\r
-[Defines]\r
- INF_VERSION = 0x00010005\r
- BASE_NAME = QemuNewBootOrderLib\r
- FILE_GUID = 1D677A58-C753-4AF1-B552-EFE142DF8F57\r
- MODULE_TYPE = DXE_DRIVER\r
- VERSION_STRING = 1.0\r
- LIBRARY_CLASS = QemuBootOrderLib|DXE_DRIVER\r
-\r
-#\r
-# The following information is for reference only and not required by the build\r
-# tools.\r
-#\r
-# VALID_ARCHITECTURES = IA32 X64 IPF EBC ARM AARCH64\r
-#\r
-\r
-[Sources]\r
- QemuBootOrderLib.c\r
- ExtraRootBusMap.c\r
-\r
-[Packages]\r
- MdePkg/MdePkg.dec\r
- MdeModulePkg/MdeModulePkg.dec\r
- OvmfPkg/OvmfPkg.dec\r
-\r
-[LibraryClasses]\r
- QemuFwCfgLib\r
- DebugLib\r
- MemoryAllocationLib\r
- UefiBootManagerLib\r
- UefiBootServicesTableLib\r
- UefiRuntimeServicesTableLib\r
- BaseLib\r
- PrintLib\r
- DevicePathLib\r
- BaseMemoryLib\r
- OrderedCollectionLib\r
-\r
-[Guids]\r
- gEfiGlobalVariableGuid\r
- gVirtioMmioTransportGuid\r
-\r
-[FeaturePcd]\r
- gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderPciTranslation\r
- gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderMmioTranslation\r
-\r
-[Pcd]\r
- gEfiMdePkgTokenSpaceGuid.PcdPlatformBootTimeOut\r
-\r
-[Protocols]\r
- gEfiDevicePathProtocolGuid ## CONSUMES\r
- gEfiPciRootBridgeIoProtocolGuid ## CONSUMES\r
UdpIoLib|MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf\r
DpcLib|MdeModulePkg/Library/DxeDpcLib/DxeDpcLib.inf\r
PlatformBootManagerLib|OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf\r
- QemuBootOrderLib|OvmfPkg/Library/QemuNewBootOrderLib/QemuBootOrderLib.inf\r
+ QemuBootOrderLib|OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.inf\r
CpuExceptionHandlerLib|UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf\r
!if $(SMM_REQUIRE) == TRUE\r
LockBoxLib|MdeModulePkg/Library/SmmLockBoxLib/SmmLockBoxDxeLib.inf\r
UdpIoLib|MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf\r
DpcLib|MdeModulePkg/Library/DxeDpcLib/DxeDpcLib.inf\r
PlatformBootManagerLib|OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf\r
- QemuBootOrderLib|OvmfPkg/Library/QemuNewBootOrderLib/QemuBootOrderLib.inf\r
+ QemuBootOrderLib|OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.inf\r
CpuExceptionHandlerLib|UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf\r
!if $(SMM_REQUIRE) == TRUE\r
LockBoxLib|MdeModulePkg/Library/SmmLockBoxLib/SmmLockBoxDxeLib.inf\r
UdpIoLib|MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf\r
DpcLib|MdeModulePkg/Library/DxeDpcLib/DxeDpcLib.inf\r
PlatformBootManagerLib|OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf\r
- QemuBootOrderLib|OvmfPkg/Library/QemuNewBootOrderLib/QemuBootOrderLib.inf\r
+ QemuBootOrderLib|OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.inf\r
CpuExceptionHandlerLib|UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf\r
!if $(SMM_REQUIRE) == TRUE\r
LockBoxLib|MdeModulePkg/Library/SmmLockBoxLib/SmmLockBoxDxeLib.inf\r