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