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