--- /dev/null
+/** @file\r
+ Rewrite the BootOrder NvVar based on QEMU's "bootorder" fw_cfg file --\r
+ include file.\r
+\r
+ Copyright (C) 2012-2014, Red Hat, Inc.\r
+\r
+ This program and the accompanying materials are licensed and made available\r
+ under the terms and conditions of the BSD License which accompanies this\r
+ distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
+ WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+**/\r
+\r
+#ifndef __QEMU_BOOT_ORDER_LIB_H__\r
+#define __QEMU_BOOT_ORDER_LIB_H__\r
+\r
+#include <Uefi/UefiBaseType.h>\r
+#include <Base.h>\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
+#endif\r
**/\r
\r
#include "BdsPlatform.h"\r
-#include "QemuBootOrder.h"\r
+#include <Library/QemuBootOrderLib.h>\r
\r
\r
//\r
[Sources]\r
BdsPlatform.c\r
PlatformData.c\r
- QemuBootOrder.c\r
QemuKernel.c\r
BdsPlatform.h\r
- QemuBootOrder.h\r
\r
[Packages]\r
MdePkg/MdePkg.dec\r
NvVarsFileLib\r
QemuFwCfgLib\r
LoadLinuxLib\r
+ QemuBootOrderLib\r
\r
[Pcd]\r
gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdPlatformBootTimeOut\r
+++ /dev/null
-/** @file\r
- Rewrite the BootOrder NvVar based on QEMU's "bootorder" fw_cfg file.\r
-\r
- Copyright (C) 2012 - 2013, 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 <Guid/GlobalVariable.h>\r
-\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
-/**\r
- Numbers of nodes in OpenFirmware device paths that are required and examined.\r
-**/\r
-#define REQUIRED_OFW_NODES 2\r
-#define EXAMINED_OFW_NODES 4\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
- UINT32 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 UINT32 *Result,\r
- IN OUT UINTN *NumResults\r
- )\r
-{\r
- UINTN Entry; // number of entry currently being parsed\r
- UINT32 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 > 0xFFFFFFF) {\r
- return RETURN_INVALID_PARAMETER;\r
- }\r
- EntryVal = (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 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[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
-TranslateOfwNodes (\r
- IN CONST OFW_NODE *OfwNode,\r
- IN UINTN NumNodes,\r
- OUT CHAR16 *Translated,\r
- IN OUT UINTN *TranslatedSize\r
- )\r
-{\r
- UINT32 PciDevFun[2];\r
- UINTN NumEntries;\r
- UINTN Written;\r
-\r
- //\r
- // Get PCI device and optional PCI function. Assume a single PCI root.\r
- //\r
- if (NumNodes < REQUIRED_OFW_NODES ||\r
- !SubstringEq (OfwNode[0].DriverName, "pci")\r
- ) {\r
- return RETURN_UNSUPPORTED;\r
- }\r
- PciDevFun[1] = 0;\r
- NumEntries = sizeof (PciDevFun) / sizeof (PciDevFun[0]);\r
- if (ParseUnitAddressHexList (\r
- OfwNode[1].UnitAddress,\r
- PciDevFun,\r
- &NumEntries\r
- ) != RETURN_SUCCESS\r
- ) {\r
- return RETURN_UNSUPPORTED;\r
- }\r
-\r
- if (NumNodes >= 4 &&\r
- SubstringEq (OfwNode[1].DriverName, "ide") &&\r
- SubstringEq (OfwNode[2].DriverName, "drive") &&\r
- SubstringEq (OfwNode[3].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
- UINT32 Secondary;\r
- UINT32 Slave;\r
-\r
- NumEntries = 1;\r
- if (ParseUnitAddressHexList (\r
- OfwNode[2].UnitAddress,\r
- &Secondary,\r
- &NumEntries\r
- ) != RETURN_SUCCESS ||\r
- Secondary > 1 ||\r
- ParseUnitAddressHexList (\r
- OfwNode[3].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(0x0)/Pci(0x%x,0x%x)/Ata(%a,%a,0x0)",\r
- PciDevFun[0],\r
- PciDevFun[1],\r
- Secondary ? "Secondary" : "Primary",\r
- Slave ? "Slave" : "Master"\r
- );\r
- } else if (NumNodes >= 4 &&\r
- SubstringEq (OfwNode[1].DriverName, "isa") &&\r
- SubstringEq (OfwNode[2].DriverName, "fdc") &&\r
- SubstringEq (OfwNode[3].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
- UINT32 AcpiUid;\r
-\r
- NumEntries = 1;\r
- if (ParseUnitAddressHexList (\r
- OfwNode[3].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(0x0)/Pci(0x%x,0x%x)/Floppy(0x%x)",\r
- PciDevFun[0],\r
- PciDevFun[1],\r
- AcpiUid\r
- );\r
- } else if (NumNodes >= 3 &&\r
- SubstringEq (OfwNode[1].DriverName, "scsi") &&\r
- SubstringEq (OfwNode[2].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(0x0)/Pci(0x%x,0x%x)/HD(",\r
- PciDevFun[0],\r
- PciDevFun[1]\r
- );\r
- } else if (NumNodes >= 4 &&\r
- SubstringEq (OfwNode[1].DriverName, "scsi") &&\r
- SubstringEq (OfwNode[2].DriverName, "channel") &&\r
- SubstringEq (OfwNode[3].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
- UINT32 TargetLun[2];\r
-\r
- TargetLun[1] = 0;\r
- NumEntries = sizeof (TargetLun) / sizeof (TargetLun[0]);\r
- if (ParseUnitAddressHexList (\r
- OfwNode[3].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(0x0)/Pci(0x%x,0x%x)/Scsi(0x%x,0x%x)",\r
- PciDevFun[0],\r
- PciDevFun[1],\r
- TargetLun[0],\r
- TargetLun[1]\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(0x0)/Pci(0x%x,0x%x)",\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
-\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[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_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
- 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
- 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
- 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().\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()\r
- //\r
- Keep = FALSE;\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
- 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
- //\r
- // translate each OpenFirmware path\r
- //\r
- TranslatedSize = sizeof (Translated) / sizeof (Translated[0]);\r
- Status = TranslateOfwPath (&FwCfgPtr, Translated, &TranslatedSize);\r
- while (Status == RETURN_SUCCESS ||\r
- Status == RETURN_UNSUPPORTED ||\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 ErrorFreeActiveOption;\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, Translated, &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 ErrorFreeActiveOption;\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 ErrorFreeActiveOption;\r
- }\r
-\r
- DEBUG ((DEBUG_INFO, "%a: setting BootOrder: success\n", __FUNCTION__));\r
- PruneBootVariables (ActiveOption, ActiveCount);\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
+++ /dev/null
-/** @file\r
- Rewrite the BootOrder NvVar based on QEMU's "bootorder" fw_cfg file --\r
- include file.\r
-\r
- Copyright (C) 2012, Red Hat, Inc.\r
-\r
- This program and the accompanying materials are licensed and made available\r
- under the terms and conditions of the BSD License which accompanies this\r
- distribution. The full text of the license may be found at\r
- http://opensource.org/licenses/bsd-license.php\r
-\r
- THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
- WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
-**/\r
-\r
-\r
-#include <Uefi/UefiBaseType.h>\r
-#include <Base.h>\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
--- /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 <Guid/GlobalVariable.h>\r
+\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
+/**\r
+ Numbers of nodes in OpenFirmware device paths that are required and examined.\r
+**/\r
+#define REQUIRED_OFW_NODES 2\r
+#define EXAMINED_OFW_NODES 4\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
+ UINT32 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 UINT32 *Result,\r
+ IN OUT UINTN *NumResults\r
+ )\r
+{\r
+ UINTN Entry; // number of entry currently being parsed\r
+ UINT32 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 > 0xFFFFFFF) {\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+ EntryVal = (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 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[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
+TranslateOfwNodes (\r
+ IN CONST OFW_NODE *OfwNode,\r
+ IN UINTN NumNodes,\r
+ OUT CHAR16 *Translated,\r
+ IN OUT UINTN *TranslatedSize\r
+ )\r
+{\r
+ UINT32 PciDevFun[2];\r
+ UINTN NumEntries;\r
+ UINTN Written;\r
+\r
+ //\r
+ // Get PCI device and optional PCI function. Assume a single PCI root.\r
+ //\r
+ if (NumNodes < REQUIRED_OFW_NODES ||\r
+ !SubstringEq (OfwNode[0].DriverName, "pci")\r
+ ) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+ PciDevFun[1] = 0;\r
+ NumEntries = sizeof (PciDevFun) / sizeof (PciDevFun[0]);\r
+ if (ParseUnitAddressHexList (\r
+ OfwNode[1].UnitAddress,\r
+ PciDevFun,\r
+ &NumEntries\r
+ ) != RETURN_SUCCESS\r
+ ) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ if (NumNodes >= 4 &&\r
+ SubstringEq (OfwNode[1].DriverName, "ide") &&\r
+ SubstringEq (OfwNode[2].DriverName, "drive") &&\r
+ SubstringEq (OfwNode[3].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
+ UINT32 Secondary;\r
+ UINT32 Slave;\r
+\r
+ NumEntries = 1;\r
+ if (ParseUnitAddressHexList (\r
+ OfwNode[2].UnitAddress,\r
+ &Secondary,\r
+ &NumEntries\r
+ ) != RETURN_SUCCESS ||\r
+ Secondary > 1 ||\r
+ ParseUnitAddressHexList (\r
+ OfwNode[3].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(0x0)/Pci(0x%x,0x%x)/Ata(%a,%a,0x0)",\r
+ PciDevFun[0],\r
+ PciDevFun[1],\r
+ Secondary ? "Secondary" : "Primary",\r
+ Slave ? "Slave" : "Master"\r
+ );\r
+ } else if (NumNodes >= 4 &&\r
+ SubstringEq (OfwNode[1].DriverName, "isa") &&\r
+ SubstringEq (OfwNode[2].DriverName, "fdc") &&\r
+ SubstringEq (OfwNode[3].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
+ UINT32 AcpiUid;\r
+\r
+ NumEntries = 1;\r
+ if (ParseUnitAddressHexList (\r
+ OfwNode[3].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(0x0)/Pci(0x%x,0x%x)/Floppy(0x%x)",\r
+ PciDevFun[0],\r
+ PciDevFun[1],\r
+ AcpiUid\r
+ );\r
+ } else if (NumNodes >= 3 &&\r
+ SubstringEq (OfwNode[1].DriverName, "scsi") &&\r
+ SubstringEq (OfwNode[2].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(0x0)/Pci(0x%x,0x%x)/HD(",\r
+ PciDevFun[0],\r
+ PciDevFun[1]\r
+ );\r
+ } else if (NumNodes >= 4 &&\r
+ SubstringEq (OfwNode[1].DriverName, "scsi") &&\r
+ SubstringEq (OfwNode[2].DriverName, "channel") &&\r
+ SubstringEq (OfwNode[3].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
+ UINT32 TargetLun[2];\r
+\r
+ TargetLun[1] = 0;\r
+ NumEntries = sizeof (TargetLun) / sizeof (TargetLun[0]);\r
+ if (ParseUnitAddressHexList (\r
+ OfwNode[3].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(0x0)/Pci(0x%x,0x%x)/Scsi(0x%x,0x%x)",\r
+ PciDevFun[0],\r
+ PciDevFun[1],\r
+ TargetLun[0],\r
+ TargetLun[1]\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(0x0)/Pci(0x%x,0x%x)",\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
+\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[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_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
+ 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
+ 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
+ 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().\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()\r
+ //\r
+ Keep = FALSE;\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
+ 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
+ //\r
+ // translate each OpenFirmware path\r
+ //\r
+ TranslatedSize = sizeof (Translated) / sizeof (Translated[0]);\r
+ Status = TranslateOfwPath (&FwCfgPtr, Translated, &TranslatedSize);\r
+ while (Status == RETURN_SUCCESS ||\r
+ Status == RETURN_UNSUPPORTED ||\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 ErrorFreeActiveOption;\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, Translated, &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 ErrorFreeActiveOption;\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 ErrorFreeActiveOption;\r
+ }\r
+\r
+ DEBUG ((DEBUG_INFO, "%a: setting BootOrder: success\n", __FUNCTION__));\r
+ PruneBootVariables (ActiveOption, ActiveCount);\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
--- /dev/null
+## @file\r
+# Rewrite the BootOrder NvVar based on QEMU's "bootorder" fw_cfg file.\r
+#\r
+# Copyright (C) 2012 - 2014, Red Hat, Inc.\r
+# Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>\r
+#\r
+# This program and the accompanying materials are licensed and made available\r
+# under the terms and conditions of the BSD License which accompanies this\r
+# distribution. The full text of the license may be found at\r
+# http://opensource.org/licenses/bsd-license.php\r
+#\r
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR\r
+# IMPLIED.\r
+#\r
+##\r
+\r
+[Defines]\r
+ INF_VERSION = 0x00010005\r
+ BASE_NAME = QemuBootOrderLib\r
+ FILE_GUID = 4FFFA9E1-103D-4CF2-9C06-563BDD03050E\r
+ MODULE_TYPE = DXE_DRIVER\r
+ VERSION_STRING = 1.0\r
+ LIBRARY_CLASS = QemuBootOrderLib|DXE_DRIVER\r
+\r
+#\r
+# The following information is for reference only and not required by the build\r
+# tools.\r
+#\r
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC ARM AARCH64\r
+#\r
+\r
+[Sources]\r
+ QemuBootOrderLib.c\r
+\r
+[Packages]\r
+ MdePkg/MdePkg.dec\r
+ IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec\r
+ OvmfPkg/OvmfPkg.dec\r
+\r
+[LibraryClasses]\r
+ QemuFwCfgLib\r
+ DebugLib\r
+ MemoryAllocationLib\r
+ GenericBdsLib\r
+ UefiBootServicesTableLib\r
+ UefiRuntimeServicesTableLib\r
+ BaseLib\r
+ PrintLib\r
+ DevicePathLib\r
+\r
+[Guids]\r
+ gEfiGlobalVariableGuid\r
#\r
QemuFwCfgLib|Include/Library/QemuFwCfgLib.h\r
\r
+ ## @libraryclass Rewrite the BootOrder NvVar based on QEMU's "bootorder"\r
+ # fw_cfg file.\r
+ #\r
+ QemuBootOrderLib|Include/Library/QemuBootOrderLib.h\r
+\r
## @libraryclass Serialize (and deserialize) variables\r
#\r
SerializeVariablesLib|Include/Library/SerializeVariablesLib.h\r
!ifdef $(SOURCE_DEBUG_ENABLE)\r
DebugAgentLib|SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgentLib.inf\r
!endif\r
+ QemuBootOrderLib|OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.inf\r
\r
[LibraryClasses.common.UEFI_APPLICATION]\r
PcdLib|MdePkg/Library/DxePcdLib/DxePcdLib.inf\r
!ifdef $(SOURCE_DEBUG_ENABLE)\r
DebugAgentLib|SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgentLib.inf\r
!endif\r
+ QemuBootOrderLib|OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.inf\r
\r
[LibraryClasses.common.UEFI_APPLICATION]\r
PcdLib|MdePkg/Library/DxePcdLib/DxePcdLib.inf\r
!ifdef $(SOURCE_DEBUG_ENABLE)\r
DebugAgentLib|SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgentLib.inf\r
!endif\r
+ QemuBootOrderLib|OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.inf\r
\r
[LibraryClasses.common.UEFI_APPLICATION]\r
PcdLib|MdePkg/Library/DxePcdLib/DxePcdLib.inf\r