Home | History | Annotate | Download | only in QemuBootOrderLib
      1 /** @file
      2   Rewrite the BootOrder NvVar based on QEMU's "bootorder" fw_cfg file.
      3 
      4   Copyright (C) 2012 - 2014, Red Hat, Inc.
      5   Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.<BR>
      6 
      7   This program and the accompanying materials are licensed and made available
      8   under the terms and conditions of the BSD License which accompanies this
      9   distribution.  The full text of the license may be found at
     10   http://opensource.org/licenses/bsd-license.php
     11 
     12   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
     13   WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     14 **/
     15 
     16 #include <Library/QemuFwCfgLib.h>
     17 #include <Library/DebugLib.h>
     18 #include <Library/MemoryAllocationLib.h>
     19 #include <Library/UefiBootManagerLib.h>
     20 #include <Library/UefiBootServicesTableLib.h>
     21 #include <Library/UefiRuntimeServicesTableLib.h>
     22 #include <Library/BaseLib.h>
     23 #include <Library/PrintLib.h>
     24 #include <Library/DevicePathLib.h>
     25 #include <Library/QemuBootOrderLib.h>
     26 #include <Library/BaseMemoryLib.h>
     27 #include <Guid/GlobalVariable.h>
     28 #include <Guid/VirtioMmioTransport.h>
     29 
     30 #include "ExtraRootBusMap.h"
     31 
     32 /**
     33   OpenFirmware to UEFI device path translation output buffer size in CHAR16's.
     34 **/
     35 #define TRANSLATION_OUTPUT_SIZE 0x100
     36 
     37 /**
     38   Output buffer size for OpenFirmware to UEFI device path fragment translation,
     39   in CHAR16's, for a sequence of PCI bridges.
     40 **/
     41 #define BRIDGE_TRANSLATION_OUTPUT_SIZE 0x40
     42 
     43 /**
     44   Numbers of nodes in OpenFirmware device paths that are required and examined.
     45 **/
     46 #define REQUIRED_PCI_OFW_NODES  2
     47 #define REQUIRED_MMIO_OFW_NODES 1
     48 #define EXAMINED_OFW_NODES      6
     49 
     50 
     51 /**
     52   Simple character classification routines, corresponding to POSIX class names
     53   and ASCII encoding.
     54 **/
     55 STATIC
     56 BOOLEAN
     57 IsAlnum (
     58   IN  CHAR8 Chr
     59   )
     60 {
     61   return (('0' <= Chr && Chr <= '9') ||
     62           ('A' <= Chr && Chr <= 'Z') ||
     63           ('a' <= Chr && Chr <= 'z')
     64           );
     65 }
     66 
     67 
     68 STATIC
     69 BOOLEAN
     70 IsDriverNamePunct (
     71   IN  CHAR8 Chr
     72   )
     73 {
     74   return (Chr == ',' ||  Chr == '.' || Chr == '_' ||
     75           Chr == '+' || Chr == '-'
     76           );
     77 }
     78 
     79 
     80 STATIC
     81 BOOLEAN
     82 IsPrintNotDelim (
     83   IN  CHAR8 Chr
     84   )
     85 {
     86   return (32 <= Chr && Chr <= 126 &&
     87           Chr != '/' && Chr != '@' && Chr != ':');
     88 }
     89 
     90 
     91 /**
     92   Utility types and functions.
     93 **/
     94 typedef struct {
     95   CONST CHAR8 *Ptr; // not necessarily NUL-terminated
     96   UINTN       Len;  // number of non-NUL characters
     97 } SUBSTRING;
     98 
     99 
    100 /**
    101 
    102   Check if Substring and String have identical contents.
    103 
    104   The function relies on the restriction that a SUBSTRING cannot have embedded
    105   NULs either.
    106 
    107   @param[in] Substring  The SUBSTRING input to the comparison.
    108 
    109   @param[in] String     The ASCII string input to the comparison.
    110 
    111 
    112   @return  Whether the inputs have identical contents.
    113 
    114 **/
    115 STATIC
    116 BOOLEAN
    117 SubstringEq (
    118   IN  SUBSTRING   Substring,
    119   IN  CONST CHAR8 *String
    120   )
    121 {
    122   UINTN       Pos;
    123   CONST CHAR8 *Chr;
    124 
    125   Pos = 0;
    126   Chr = String;
    127 
    128   while (Pos < Substring.Len && Substring.Ptr[Pos] == *Chr) {
    129     ++Pos;
    130     ++Chr;
    131   }
    132 
    133   return (BOOLEAN)(Pos == Substring.Len && *Chr == '\0');
    134 }
    135 
    136 
    137 /**
    138 
    139   Parse a comma-separated list of hexadecimal integers into the elements of an
    140   UINT64 array.
    141 
    142   Whitespace, "0x" prefixes, leading or trailing commas, sequences of commas,
    143   or an empty string are not allowed; they are rejected.
    144 
    145   The function relies on ASCII encoding.
    146 
    147   @param[in]     UnitAddress  The substring to parse.
    148 
    149   @param[out]    Result       The array, allocated by the caller, to receive
    150                               the parsed values. This parameter may be NULL if
    151                               NumResults is zero on input.
    152 
    153   @param[in out] NumResults   On input, the number of elements allocated for
    154                               Result. On output, the number of elements it has
    155                               taken (or would have taken) to parse the string
    156                               fully.
    157 
    158 
    159   @retval RETURN_SUCCESS            UnitAddress has been fully parsed.
    160                                     NumResults is set to the number of parsed
    161                                     values; the corresponding elements have
    162                                     been set in Result. The rest of Result's
    163                                     elements are unchanged.
    164 
    165   @retval RETURN_BUFFER_TOO_SMALL   UnitAddress has been fully parsed.
    166                                     NumResults is set to the number of parsed
    167                                     values, but elements have been stored only
    168                                     up to the input value of NumResults, which
    169                                     is less than what has been parsed.
    170 
    171   @retval RETURN_INVALID_PARAMETER  Parse error. The contents of Results is
    172                                     indeterminate. NumResults has not been
    173                                     changed.
    174 
    175 **/
    176 STATIC
    177 RETURN_STATUS
    178 ParseUnitAddressHexList (
    179   IN      SUBSTRING  UnitAddress,
    180   OUT     UINT64     *Result,
    181   IN OUT  UINTN      *NumResults
    182   )
    183 {
    184   UINTN         Entry;    // number of entry currently being parsed
    185   UINT64        EntryVal; // value being constructed for current entry
    186   CHAR8         PrevChr;  // UnitAddress character previously checked
    187   UINTN         Pos;      // current position within UnitAddress
    188   RETURN_STATUS Status;
    189 
    190   Entry    = 0;
    191   EntryVal = 0;
    192   PrevChr  = ',';
    193 
    194   for (Pos = 0; Pos < UnitAddress.Len; ++Pos) {
    195     CHAR8 Chr;
    196     INT8  Val;
    197 
    198     Chr = UnitAddress.Ptr[Pos];
    199     Val = ('a' <= Chr && Chr <= 'f') ? (Chr - 'a' + 10) :
    200           ('A' <= Chr && Chr <= 'F') ? (Chr - 'A' + 10) :
    201           ('0' <= Chr && Chr <= '9') ? (Chr - '0'     ) :
    202           -1;
    203 
    204     if (Val >= 0) {
    205       if (EntryVal > 0xFFFFFFFFFFFFFFFull) {
    206         return RETURN_INVALID_PARAMETER;
    207       }
    208       EntryVal = LShiftU64 (EntryVal, 4) | Val;
    209     } else if (Chr == ',') {
    210       if (PrevChr == ',') {
    211         return RETURN_INVALID_PARAMETER;
    212       }
    213       if (Entry < *NumResults) {
    214         Result[Entry] = EntryVal;
    215       }
    216       ++Entry;
    217       EntryVal = 0;
    218     } else {
    219       return RETURN_INVALID_PARAMETER;
    220     }
    221 
    222     PrevChr = Chr;
    223   }
    224 
    225   if (PrevChr == ',') {
    226     return RETURN_INVALID_PARAMETER;
    227   }
    228   if (Entry < *NumResults) {
    229     Result[Entry] = EntryVal;
    230     Status = RETURN_SUCCESS;
    231   } else {
    232     Status = RETURN_BUFFER_TOO_SMALL;
    233   }
    234   ++Entry;
    235 
    236   *NumResults = Entry;
    237   return Status;
    238 }
    239 
    240 
    241 /**
    242   A simple array of Boot Option ID's.
    243 **/
    244 typedef struct {
    245   UINT16 *Data;
    246   UINTN  Allocated;
    247   UINTN  Produced;
    248 } BOOT_ORDER;
    249 
    250 
    251 /**
    252   Array element tracking an enumerated boot option that has the
    253   LOAD_OPTION_ACTIVE attribute.
    254 **/
    255 typedef struct {
    256   CONST EFI_BOOT_MANAGER_LOAD_OPTION *BootOption; // reference only, no
    257                                                   //   ownership
    258   BOOLEAN                            Appended;    // has been added to a
    259                                                   //   BOOT_ORDER?
    260 } ACTIVE_OPTION;
    261 
    262 
    263 /**
    264 
    265   Append an active boot option to BootOrder, reallocating the latter if needed.
    266 
    267   @param[in out] BootOrder     The structure pointing to the array and holding
    268                                allocation and usage counters.
    269 
    270   @param[in]     ActiveOption  The active boot option whose ID should be
    271                                appended to the array.
    272 
    273 
    274   @retval RETURN_SUCCESS           ID of ActiveOption appended.
    275 
    276   @retval RETURN_OUT_OF_RESOURCES  Memory reallocation failed.
    277 
    278 **/
    279 STATIC
    280 RETURN_STATUS
    281 BootOrderAppend (
    282   IN OUT  BOOT_ORDER    *BootOrder,
    283   IN OUT  ACTIVE_OPTION *ActiveOption
    284   )
    285 {
    286   if (BootOrder->Produced == BootOrder->Allocated) {
    287     UINTN  AllocatedNew;
    288     UINT16 *DataNew;
    289 
    290     ASSERT (BootOrder->Allocated > 0);
    291     AllocatedNew = BootOrder->Allocated * 2;
    292     DataNew = ReallocatePool (
    293                 BootOrder->Allocated * sizeof (*BootOrder->Data),
    294                 AllocatedNew         * sizeof (*DataNew),
    295                 BootOrder->Data
    296                 );
    297     if (DataNew == NULL) {
    298       return RETURN_OUT_OF_RESOURCES;
    299     }
    300     BootOrder->Allocated = AllocatedNew;
    301     BootOrder->Data      = DataNew;
    302   }
    303 
    304   BootOrder->Data[BootOrder->Produced++] =
    305                                (UINT16) ActiveOption->BootOption->OptionNumber;
    306   ActiveOption->Appended = TRUE;
    307   return RETURN_SUCCESS;
    308 }
    309 
    310 
    311 /**
    312 
    313   Create an array of ACTIVE_OPTION elements for a boot option array.
    314 
    315   @param[in]  BootOptions      A boot option array, created with
    316                                EfiBootManagerRefreshAllBootOption () and
    317                                EfiBootManagerGetLoadOptions ().
    318 
    319   @param[in]  BootOptionCount  The number of elements in BootOptions.
    320 
    321   @param[out] ActiveOption     Pointer to the first element in the new array.
    322                                The caller is responsible for freeing the array
    323                                with FreePool() after use.
    324 
    325   @param[out] Count            Number of elements in the new array.
    326 
    327 
    328   @retval RETURN_SUCCESS           The ActiveOption array has been created.
    329 
    330   @retval RETURN_NOT_FOUND         No active entry has been found in
    331                                    BootOptions.
    332 
    333   @retval RETURN_OUT_OF_RESOURCES  Memory allocation failed.
    334 
    335 **/
    336 STATIC
    337 RETURN_STATUS
    338 CollectActiveOptions (
    339   IN   CONST EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions,
    340   IN   UINTN                              BootOptionCount,
    341   OUT  ACTIVE_OPTION                      **ActiveOption,
    342   OUT  UINTN                              *Count
    343   )
    344 {
    345   UINTN Index;
    346   UINTN ScanMode;
    347 
    348   *ActiveOption = NULL;
    349 
    350   //
    351   // Scan the list twice:
    352   // - count active entries,
    353   // - store links to active entries.
    354   //
    355   for (ScanMode = 0; ScanMode < 2; ++ScanMode) {
    356     *Count = 0;
    357     for (Index = 0; Index < BootOptionCount; Index++) {
    358       if ((BootOptions[Index].Attributes & LOAD_OPTION_ACTIVE) != 0) {
    359         if (ScanMode == 1) {
    360           (*ActiveOption)[*Count].BootOption = &BootOptions[Index];
    361           (*ActiveOption)[*Count].Appended   = FALSE;
    362         }
    363         ++*Count;
    364       }
    365     }
    366 
    367     if (ScanMode == 0) {
    368       if (*Count == 0) {
    369         return RETURN_NOT_FOUND;
    370       }
    371       *ActiveOption = AllocatePool (*Count * sizeof **ActiveOption);
    372       if (*ActiveOption == NULL) {
    373         return RETURN_OUT_OF_RESOURCES;
    374       }
    375     }
    376   }
    377   return RETURN_SUCCESS;
    378 }
    379 
    380 
    381 /**
    382   OpenFirmware device path node
    383 **/
    384 typedef struct {
    385   SUBSTRING DriverName;
    386   SUBSTRING UnitAddress;
    387   SUBSTRING DeviceArguments;
    388 } OFW_NODE;
    389 
    390 
    391 /**
    392 
    393   Parse an OpenFirmware device path node into the caller-allocated OFW_NODE
    394   structure, and advance in the input string.
    395 
    396   The node format is mostly parsed after IEEE 1275-1994, 3.2.1.1 "Node names"
    397   (a leading slash is expected and not returned):
    398 
    399     /driver-name@unit-address[:device-arguments][<LF>]
    400 
    401   A single trailing <LF> character is consumed but not returned. A trailing
    402   <LF> or NUL character terminates the device path.
    403 
    404   The function relies on ASCII encoding.
    405 
    406   @param[in out] Ptr      Address of the pointer pointing to the start of the
    407                           node string. After successful parsing *Ptr is set to
    408                           the byte immediately following the consumed
    409                           characters. On error it points to the byte that
    410                           caused the error. The input string is never modified.
    411 
    412   @param[out]    OfwNode  The members of this structure point into the input
    413                           string, designating components of the node.
    414                           Separators are never included. If "device-arguments"
    415                           is missing, then DeviceArguments.Ptr is set to NULL.
    416                           All components that are present have nonzero length.
    417 
    418                           If the call doesn't succeed, the contents of this
    419                           structure is indeterminate.
    420 
    421   @param[out]    IsFinal  In case of successul parsing, this parameter signals
    422                           whether the node just parsed is the final node in the
    423                           device path. The call after a final node will attempt
    424                           to start parsing the next path. If the call doesn't
    425                           succeed, then this parameter is not changed.
    426 
    427 
    428   @retval RETURN_SUCCESS            Parsing successful.
    429 
    430   @retval RETURN_NOT_FOUND          Parsing terminated. *Ptr was (and is)
    431                                     pointing to an empty string.
    432 
    433   @retval RETURN_INVALID_PARAMETER  Parse error.
    434 
    435 **/
    436 STATIC
    437 RETURN_STATUS
    438 ParseOfwNode (
    439   IN OUT  CONST CHAR8 **Ptr,
    440   OUT     OFW_NODE    *OfwNode,
    441   OUT     BOOLEAN     *IsFinal
    442   )
    443 {
    444   //
    445   // A leading slash is expected. End of string is tolerated.
    446   //
    447   switch (**Ptr) {
    448   case '\0':
    449     return RETURN_NOT_FOUND;
    450 
    451   case '/':
    452     ++*Ptr;
    453     break;
    454 
    455   default:
    456     return RETURN_INVALID_PARAMETER;
    457   }
    458 
    459   //
    460   // driver-name
    461   //
    462   OfwNode->DriverName.Ptr = *Ptr;
    463   OfwNode->DriverName.Len = 0;
    464   while (OfwNode->DriverName.Len < 32 &&
    465          (IsAlnum (**Ptr) || IsDriverNamePunct (**Ptr))
    466          ) {
    467     ++*Ptr;
    468     ++OfwNode->DriverName.Len;
    469   }
    470 
    471   if (OfwNode->DriverName.Len == 0 || OfwNode->DriverName.Len == 32) {
    472     return RETURN_INVALID_PARAMETER;
    473   }
    474 
    475 
    476   //
    477   // unit-address
    478   //
    479   if (**Ptr != '@') {
    480     return RETURN_INVALID_PARAMETER;
    481   }
    482   ++*Ptr;
    483 
    484   OfwNode->UnitAddress.Ptr = *Ptr;
    485   OfwNode->UnitAddress.Len = 0;
    486   while (IsPrintNotDelim (**Ptr)) {
    487     ++*Ptr;
    488     ++OfwNode->UnitAddress.Len;
    489   }
    490 
    491   if (OfwNode->UnitAddress.Len == 0) {
    492     return RETURN_INVALID_PARAMETER;
    493   }
    494 
    495 
    496   //
    497   // device-arguments, may be omitted
    498   //
    499   OfwNode->DeviceArguments.Len = 0;
    500   if (**Ptr == ':') {
    501     ++*Ptr;
    502     OfwNode->DeviceArguments.Ptr = *Ptr;
    503 
    504     while (IsPrintNotDelim (**Ptr)) {
    505       ++*Ptr;
    506       ++OfwNode->DeviceArguments.Len;
    507     }
    508 
    509     if (OfwNode->DeviceArguments.Len == 0) {
    510       return RETURN_INVALID_PARAMETER;
    511     }
    512   }
    513   else {
    514     OfwNode->DeviceArguments.Ptr = NULL;
    515   }
    516 
    517   switch (**Ptr) {
    518   case '\n':
    519     ++*Ptr;
    520     //
    521     // fall through
    522     //
    523 
    524   case '\0':
    525     *IsFinal = TRUE;
    526     break;
    527 
    528   case '/':
    529     *IsFinal = FALSE;
    530     break;
    531 
    532   default:
    533     return RETURN_INVALID_PARAMETER;
    534   }
    535 
    536   DEBUG ((
    537     DEBUG_VERBOSE,
    538     "%a: DriverName=\"%.*a\" UnitAddress=\"%.*a\" DeviceArguments=\"%.*a\"\n",
    539     __FUNCTION__,
    540     OfwNode->DriverName.Len, OfwNode->DriverName.Ptr,
    541     OfwNode->UnitAddress.Len, OfwNode->UnitAddress.Ptr,
    542     OfwNode->DeviceArguments.Len,
    543     OfwNode->DeviceArguments.Ptr == NULL ? "" : OfwNode->DeviceArguments.Ptr
    544     ));
    545   return RETURN_SUCCESS;
    546 }
    547 
    548 
    549 /**
    550 
    551   Translate a PCI-like array of OpenFirmware device nodes to a UEFI device path
    552   fragment.
    553 
    554   @param[in]     OfwNode         Array of OpenFirmware device nodes to
    555                                  translate, constituting the beginning of an
    556                                  OpenFirmware device path.
    557 
    558   @param[in]     NumNodes        Number of elements in OfwNode.
    559 
    560   @param[in]     ExtraPciRoots   An EXTRA_ROOT_BUS_MAP object created with
    561                                  CreateExtraRootBusMap(), to be used for
    562                                  translating positions of extra root buses to
    563                                  bus numbers.
    564 
    565   @param[out]    Translated      Destination array receiving the UEFI path
    566                                  fragment, allocated by the caller. If the
    567                                  return value differs from RETURN_SUCCESS, its
    568                                  contents is indeterminate.
    569 
    570   @param[in out] TranslatedSize  On input, the number of CHAR16's in
    571                                  Translated. On RETURN_SUCCESS this parameter
    572                                  is assigned the number of non-NUL CHAR16's
    573                                  written to Translated. In case of other return
    574                                  values, TranslatedSize is indeterminate.
    575 
    576 
    577   @retval RETURN_SUCCESS           Translation successful.
    578 
    579   @retval RETURN_BUFFER_TOO_SMALL  The translation does not fit into the number
    580                                    of bytes provided.
    581 
    582   @retval RETURN_UNSUPPORTED       The array of OpenFirmware device nodes can't
    583                                    be translated in the current implementation.
    584 
    585   @retval RETURN_PROTOCOL_ERROR    The initial OpenFirmware node refers to an
    586                                    extra PCI root bus (by serial number) that
    587                                    is invalid according to ExtraPciRoots.
    588 
    589 **/
    590 STATIC
    591 RETURN_STATUS
    592 TranslatePciOfwNodes (
    593   IN      CONST OFW_NODE           *OfwNode,
    594   IN      UINTN                    NumNodes,
    595   IN      CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,
    596   OUT     CHAR16                   *Translated,
    597   IN OUT  UINTN                    *TranslatedSize
    598   )
    599 {
    600   UINT32 PciRoot;
    601   CHAR8  *Comma;
    602   UINTN  FirstNonBridge;
    603   CHAR16 Bridges[BRIDGE_TRANSLATION_OUTPUT_SIZE];
    604   UINTN  BridgesLen;
    605   UINT64 PciDevFun[2];
    606   UINTN  NumEntries;
    607   UINTN  Written;
    608 
    609   //
    610   // Resolve the PCI root bus number.
    611   //
    612   // The initial OFW node for the main root bus (ie. bus number 0) is:
    613   //
    614   //   /pci@i0cf8
    615   //
    616   // For extra root buses, the initial OFW node is
    617   //
    618   //   /pci@i0cf8,4
    619   //              ^
    620   //              root bus serial number (not PCI bus number)
    621   //
    622   if (NumNodes < REQUIRED_PCI_OFW_NODES ||
    623       !SubstringEq (OfwNode[0].DriverName, "pci")
    624       ) {
    625     return RETURN_UNSUPPORTED;
    626   }
    627 
    628   PciRoot = 0;
    629   Comma = ScanMem8 (OfwNode[0].UnitAddress.Ptr, OfwNode[0].UnitAddress.Len,
    630             ',');
    631   if (Comma != NULL) {
    632     SUBSTRING PciRootSerialSubString;
    633     UINT64    PciRootSerial;
    634 
    635     //
    636     // Parse the root bus serial number from the unit address after the comma.
    637     //
    638     PciRootSerialSubString.Ptr = Comma + 1;
    639     PciRootSerialSubString.Len = OfwNode[0].UnitAddress.Len -
    640                                  (PciRootSerialSubString.Ptr -
    641                                   OfwNode[0].UnitAddress.Ptr);
    642     NumEntries = 1;
    643     if (RETURN_ERROR (ParseUnitAddressHexList (PciRootSerialSubString,
    644                       &PciRootSerial, &NumEntries))) {
    645       return RETURN_UNSUPPORTED;
    646     }
    647 
    648     //
    649     // Map the extra root bus's serial number to its actual bus number.
    650     //
    651     if (EFI_ERROR (MapRootBusPosToBusNr (ExtraPciRoots, PciRootSerial,
    652                      &PciRoot))) {
    653       return RETURN_PROTOCOL_ERROR;
    654     }
    655   }
    656 
    657   //
    658   // Translate a sequence of PCI bridges. For each bridge, the OFW node is:
    659   //
    660   //   pci-bridge@1e[,0]
    661   //              ^   ^
    662   //              PCI slot & function on the parent, holding the bridge
    663   //
    664   // and the UEFI device path node is:
    665   //
    666   //   Pci(0x1E,0x0)
    667   //
    668   FirstNonBridge = 1;
    669   Bridges[0] = L'\0';
    670   BridgesLen = 0;
    671   do {
    672     UINT64 BridgeDevFun[2];
    673     UINTN  BridgesFreeBytes;
    674 
    675     if (!SubstringEq (OfwNode[FirstNonBridge].DriverName, "pci-bridge")) {
    676       break;
    677     }
    678 
    679     BridgeDevFun[1] = 0;
    680     NumEntries = sizeof BridgeDevFun / sizeof BridgeDevFun[0];
    681     if (ParseUnitAddressHexList (OfwNode[FirstNonBridge].UnitAddress,
    682           BridgeDevFun, &NumEntries) != RETURN_SUCCESS) {
    683       return RETURN_UNSUPPORTED;
    684     }
    685 
    686     BridgesFreeBytes = sizeof Bridges - BridgesLen * sizeof Bridges[0];
    687     Written = UnicodeSPrintAsciiFormat (Bridges + BridgesLen, BridgesFreeBytes,
    688                 "/Pci(0x%Lx,0x%Lx)", BridgeDevFun[0], BridgeDevFun[1]);
    689     BridgesLen += Written;
    690 
    691     //
    692     // There's no way to differentiate between "completely used up without
    693     // truncation" and "truncated", so treat the former as the latter.
    694     //
    695     if (BridgesLen + 1 == BRIDGE_TRANSLATION_OUTPUT_SIZE) {
    696       return RETURN_UNSUPPORTED;
    697     }
    698 
    699     ++FirstNonBridge;
    700   } while (FirstNonBridge < NumNodes);
    701 
    702   if (FirstNonBridge == NumNodes) {
    703     return RETURN_UNSUPPORTED;
    704   }
    705 
    706   //
    707   // Parse the OFW nodes starting with the first non-bridge node.
    708   //
    709   PciDevFun[1] = 0;
    710   NumEntries = ARRAY_SIZE (PciDevFun);
    711   if (ParseUnitAddressHexList (
    712         OfwNode[FirstNonBridge].UnitAddress,
    713         PciDevFun,
    714         &NumEntries
    715         ) != RETURN_SUCCESS
    716       ) {
    717     return RETURN_UNSUPPORTED;
    718   }
    719 
    720   if (NumNodes >= FirstNonBridge + 3 &&
    721       SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "ide") &&
    722       SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "drive") &&
    723       SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")
    724       ) {
    725     //
    726     // OpenFirmware device path (IDE disk, IDE CD-ROM):
    727     //
    728     //   /pci@i0cf8/ide@1,1/drive@0/disk@0
    729     //        ^         ^ ^       ^      ^
    730     //        |         | |       |      master or slave
    731     //        |         | |       primary or secondary
    732     //        |         PCI slot & function holding IDE controller
    733     //        PCI root at system bus port, PIO
    734     //
    735     // UEFI device path:
    736     //
    737     //   PciRoot(0x0)/Pci(0x1,0x1)/Ata(Primary,Master,0x0)
    738     //                                                ^
    739     //                                                fixed LUN
    740     //
    741     UINT64 Secondary;
    742     UINT64 Slave;
    743 
    744     NumEntries = 1;
    745     if (ParseUnitAddressHexList (
    746           OfwNode[FirstNonBridge + 1].UnitAddress,
    747           &Secondary,
    748           &NumEntries
    749           ) != RETURN_SUCCESS ||
    750         Secondary > 1 ||
    751         ParseUnitAddressHexList (
    752           OfwNode[FirstNonBridge + 2].UnitAddress,
    753           &Slave,
    754           &NumEntries // reuse after previous single-element call
    755           ) != RETURN_SUCCESS ||
    756         Slave > 1
    757         ) {
    758       return RETURN_UNSUPPORTED;
    759     }
    760 
    761     Written = UnicodeSPrintAsciiFormat (
    762       Translated,
    763       *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
    764       "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Ata(%a,%a,0x0)",
    765       PciRoot,
    766       Bridges,
    767       PciDevFun[0],
    768       PciDevFun[1],
    769       Secondary ? "Secondary" : "Primary",
    770       Slave ? "Slave" : "Master"
    771       );
    772   } else if (NumNodes >= FirstNonBridge + 3 &&
    773       SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "pci8086,2922") &&
    774       SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "drive") &&
    775       SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")
    776       ) {
    777     //
    778     // OpenFirmware device path (Q35 SATA disk and CD-ROM):
    779     //
    780     //   /pci@i0cf8/pci8086,2922@1f,2/drive@1/disk@0
    781     //        ^                  ^  ^       ^      ^
    782     //        |                  |  |       |      device number (fixed 0)
    783     //        |                  |  |       channel (port) number
    784     //        |                  PCI slot & function holding SATA HBA
    785     //        PCI root at system bus port, PIO
    786     //
    787     // UEFI device path:
    788     //
    789     //   PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x1,0xFFFF,0x0)
    790     //                                   ^   ^      ^
    791     //                                   |   |      LUN (always 0 on Q35)
    792     //                                   |   port multiplier port number,
    793     //                                   |   always 0xFFFF on Q35
    794     //                                   channel (port) number
    795     //
    796     UINT64 Channel;
    797 
    798     NumEntries = 1;
    799     if (RETURN_ERROR (ParseUnitAddressHexList (
    800                         OfwNode[FirstNonBridge + 1].UnitAddress, &Channel,
    801                         &NumEntries))) {
    802       return RETURN_UNSUPPORTED;
    803     }
    804 
    805     Written = UnicodeSPrintAsciiFormat (
    806       Translated,
    807       *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
    808       "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Sata(0x%Lx,0xFFFF,0x0)",
    809       PciRoot,
    810       Bridges,
    811       PciDevFun[0],
    812       PciDevFun[1],
    813       Channel
    814       );
    815   } else if (NumNodes >= FirstNonBridge + 3 &&
    816              SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "isa") &&
    817              SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "fdc") &&
    818              SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "floppy")
    819              ) {
    820     //
    821     // OpenFirmware device path (floppy disk):
    822     //
    823     //   /pci@i0cf8/isa@1/fdc@03f0/floppy@0
    824     //        ^         ^     ^           ^
    825     //        |         |     |           A: or B:
    826     //        |         |     ISA controller io-port (hex)
    827     //        |         PCI slot holding ISA controller
    828     //        PCI root at system bus port, PIO
    829     //
    830     // UEFI device path:
    831     //
    832     //   PciRoot(0x0)/Pci(0x1,0x0)/Floppy(0x0)
    833     //                                    ^
    834     //                                    ACPI UID
    835     //
    836     UINT64 AcpiUid;
    837 
    838     NumEntries = 1;
    839     if (ParseUnitAddressHexList (
    840           OfwNode[FirstNonBridge + 2].UnitAddress,
    841           &AcpiUid,
    842           &NumEntries
    843           ) != RETURN_SUCCESS ||
    844         AcpiUid > 1
    845         ) {
    846       return RETURN_UNSUPPORTED;
    847     }
    848 
    849     Written = UnicodeSPrintAsciiFormat (
    850       Translated,
    851       *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
    852       "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Floppy(0x%Lx)",
    853       PciRoot,
    854       Bridges,
    855       PciDevFun[0],
    856       PciDevFun[1],
    857       AcpiUid
    858       );
    859   } else if (NumNodes >= FirstNonBridge + 2 &&
    860              SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "scsi") &&
    861              SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "disk")
    862              ) {
    863     //
    864     // OpenFirmware device path (virtio-blk disk):
    865     //
    866     //   /pci@i0cf8/scsi@6[,3]/disk@0,0
    867     //        ^          ^  ^       ^ ^
    868     //        |          |  |       fixed
    869     //        |          |  PCI function corresponding to disk (optional)
    870     //        |          PCI slot holding disk
    871     //        PCI root at system bus port, PIO
    872     //
    873     // UEFI device path prefix:
    874     //
    875     //   PciRoot(0x0)/Pci(0x6,0x0) -- if PCI function is 0 or absent
    876     //   PciRoot(0x0)/Pci(0x6,0x3) -- if PCI function is present and nonzero
    877     //
    878     Written = UnicodeSPrintAsciiFormat (
    879       Translated,
    880       *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
    881       "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",
    882       PciRoot,
    883       Bridges,
    884       PciDevFun[0],
    885       PciDevFun[1]
    886       );
    887   } else if (NumNodes >= FirstNonBridge + 3 &&
    888              SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "scsi") &&
    889              SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "channel") &&
    890              SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")
    891              ) {
    892     //
    893     // OpenFirmware device path (virtio-scsi disk):
    894     //
    895     //   /pci@i0cf8/scsi@7[,3]/channel@0/disk@2,3
    896     //        ^          ^             ^      ^ ^
    897     //        |          |             |      | LUN
    898     //        |          |             |      target
    899     //        |          |             channel (unused, fixed 0)
    900     //        |          PCI slot[, function] holding SCSI controller
    901     //        PCI root at system bus port, PIO
    902     //
    903     // UEFI device path prefix:
    904     //
    905     //   PciRoot(0x0)/Pci(0x7,0x0)/Scsi(0x2,0x3)
    906     //                                        -- if PCI function is 0 or absent
    907     //   PciRoot(0x0)/Pci(0x7,0x3)/Scsi(0x2,0x3)
    908     //                                -- if PCI function is present and nonzero
    909     //
    910     UINT64 TargetLun[2];
    911 
    912     TargetLun[1] = 0;
    913     NumEntries = ARRAY_SIZE (TargetLun);
    914     if (ParseUnitAddressHexList (
    915           OfwNode[FirstNonBridge + 2].UnitAddress,
    916           TargetLun,
    917           &NumEntries
    918           ) != RETURN_SUCCESS
    919         ) {
    920       return RETURN_UNSUPPORTED;
    921     }
    922 
    923     Written = UnicodeSPrintAsciiFormat (
    924       Translated,
    925       *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
    926       "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Scsi(0x%Lx,0x%Lx)",
    927       PciRoot,
    928       Bridges,
    929       PciDevFun[0],
    930       PciDevFun[1],
    931       TargetLun[0],
    932       TargetLun[1]
    933       );
    934   } else if (NumNodes >= FirstNonBridge + 2 &&
    935       SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "pci8086,5845") &&
    936       SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "namespace")
    937       ) {
    938     //
    939     // OpenFirmware device path (NVMe device):
    940     //
    941     //   /pci@i0cf8/pci8086,5845@6[,1]/namespace@1,0
    942     //        ^                  ^  ^            ^ ^
    943     //        |                  |  |            | Extended Unique Identifier
    944     //        |                  |  |            | (EUI-64), big endian interp.
    945     //        |                  |  |            namespace ID
    946     //        |                  PCI slot & function holding NVMe controller
    947     //        PCI root at system bus port, PIO
    948     //
    949     // UEFI device path:
    950     //
    951     //   PciRoot(0x0)/Pci(0x6,0x1)/NVMe(0x1,00-00-00-00-00-00-00-00)
    952     //                                  ^   ^
    953     //                                  |   octets of the EUI-64
    954     //                                  |   in address order
    955     //                                  namespace ID
    956     //
    957     UINT64 Namespace[2];
    958     UINTN  RequiredEntries;
    959     UINT8  *Eui64;
    960 
    961     RequiredEntries = ARRAY_SIZE (Namespace);
    962     NumEntries = RequiredEntries;
    963     if (ParseUnitAddressHexList (
    964           OfwNode[FirstNonBridge + 1].UnitAddress,
    965           Namespace,
    966           &NumEntries
    967           ) != RETURN_SUCCESS ||
    968         NumEntries != RequiredEntries ||
    969         Namespace[0] == 0 ||
    970         Namespace[0] >= MAX_UINT32
    971         ) {
    972       return RETURN_UNSUPPORTED;
    973     }
    974 
    975     Eui64 = (UINT8 *)&Namespace[1];
    976     Written = UnicodeSPrintAsciiFormat (
    977       Translated,
    978       *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
    979       "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/"
    980       "NVMe(0x%Lx,%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x)",
    981       PciRoot,
    982       Bridges,
    983       PciDevFun[0],
    984       PciDevFun[1],
    985       Namespace[0],
    986       Eui64[7], Eui64[6], Eui64[5], Eui64[4],
    987       Eui64[3], Eui64[2], Eui64[1], Eui64[0]
    988       );
    989   } else {
    990     //
    991     // Generic OpenFirmware device path for PCI devices:
    992     //
    993     //   /pci@i0cf8/ethernet@3[,2]
    994     //        ^              ^
    995     //        |              PCI slot[, function] holding Ethernet card
    996     //        PCI root at system bus port, PIO
    997     //
    998     // UEFI device path prefix (dependent on presence of nonzero PCI function):
    999     //
   1000     //   PciRoot(0x0)/Pci(0x3,0x0)
   1001     //   PciRoot(0x0)/Pci(0x3,0x2)
   1002     //
   1003     Written = UnicodeSPrintAsciiFormat (
   1004       Translated,
   1005       *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
   1006       "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",
   1007       PciRoot,
   1008       Bridges,
   1009       PciDevFun[0],
   1010       PciDevFun[1]
   1011       );
   1012   }
   1013 
   1014   //
   1015   // There's no way to differentiate between "completely used up without
   1016   // truncation" and "truncated", so treat the former as the latter, and return
   1017   // success only for "some room left unused".
   1018   //
   1019   if (Written + 1 < *TranslatedSize) {
   1020     *TranslatedSize = Written;
   1021     return RETURN_SUCCESS;
   1022   }
   1023 
   1024   return RETURN_BUFFER_TOO_SMALL;
   1025 }
   1026 
   1027 
   1028 //
   1029 // A type providing easy raw access to the base address of a virtio-mmio
   1030 // transport.
   1031 //
   1032 typedef union {
   1033   UINT64 Uint64;
   1034   UINT8  Raw[8];
   1035 } VIRTIO_MMIO_BASE_ADDRESS;
   1036 
   1037 
   1038 /**
   1039 
   1040   Translate an MMIO-like array of OpenFirmware device nodes to a UEFI device
   1041   path fragment.
   1042 
   1043   @param[in]     OfwNode         Array of OpenFirmware device nodes to
   1044                                  translate, constituting the beginning of an
   1045                                  OpenFirmware device path.
   1046 
   1047   @param[in]     NumNodes        Number of elements in OfwNode.
   1048 
   1049   @param[out]    Translated      Destination array receiving the UEFI path
   1050                                  fragment, allocated by the caller. If the
   1051                                  return value differs from RETURN_SUCCESS, its
   1052                                  contents is indeterminate.
   1053 
   1054   @param[in out] TranslatedSize  On input, the number of CHAR16's in
   1055                                  Translated. On RETURN_SUCCESS this parameter
   1056                                  is assigned the number of non-NUL CHAR16's
   1057                                  written to Translated. In case of other return
   1058                                  values, TranslatedSize is indeterminate.
   1059 
   1060 
   1061   @retval RETURN_SUCCESS           Translation successful.
   1062 
   1063   @retval RETURN_BUFFER_TOO_SMALL  The translation does not fit into the number
   1064                                    of bytes provided.
   1065 
   1066   @retval RETURN_UNSUPPORTED       The array of OpenFirmware device nodes can't
   1067                                    be translated in the current implementation.
   1068 
   1069 **/
   1070 STATIC
   1071 RETURN_STATUS
   1072 TranslateMmioOfwNodes (
   1073   IN      CONST OFW_NODE *OfwNode,
   1074   IN      UINTN          NumNodes,
   1075   OUT     CHAR16         *Translated,
   1076   IN OUT  UINTN          *TranslatedSize
   1077   )
   1078 {
   1079   VIRTIO_MMIO_BASE_ADDRESS VirtioMmioBase;
   1080   CHAR16                   VenHwString[60 + 1];
   1081   UINTN                    NumEntries;
   1082   UINTN                    Written;
   1083 
   1084   //
   1085   // Get the base address of the virtio-mmio transport.
   1086   //
   1087   if (NumNodes < REQUIRED_MMIO_OFW_NODES ||
   1088       !SubstringEq (OfwNode[0].DriverName, "virtio-mmio")
   1089       ) {
   1090     return RETURN_UNSUPPORTED;
   1091   }
   1092   NumEntries = 1;
   1093   if (ParseUnitAddressHexList (
   1094         OfwNode[0].UnitAddress,
   1095         &VirtioMmioBase.Uint64,
   1096         &NumEntries
   1097         ) != RETURN_SUCCESS
   1098       ) {
   1099     return RETURN_UNSUPPORTED;
   1100   }
   1101 
   1102   UnicodeSPrintAsciiFormat (VenHwString, sizeof VenHwString,
   1103     "VenHw(%g,%02X%02X%02X%02X%02X%02X%02X%02X)", &gVirtioMmioTransportGuid,
   1104     VirtioMmioBase.Raw[0], VirtioMmioBase.Raw[1], VirtioMmioBase.Raw[2],
   1105     VirtioMmioBase.Raw[3], VirtioMmioBase.Raw[4], VirtioMmioBase.Raw[5],
   1106     VirtioMmioBase.Raw[6], VirtioMmioBase.Raw[7]);
   1107 
   1108   if (NumNodes >= 2 &&
   1109       SubstringEq (OfwNode[1].DriverName, "disk")) {
   1110     //
   1111     // OpenFirmware device path (virtio-blk disk):
   1112     //
   1113     //   /virtio-mmio@000000000a003c00/disk@0,0
   1114     //                ^                     ^ ^
   1115     //                |                     fixed
   1116     //                base address of virtio-mmio register block
   1117     //
   1118     // UEFI device path prefix:
   1119     //
   1120     //   <VenHwString>
   1121     //
   1122     Written = UnicodeSPrintAsciiFormat (
   1123                 Translated,
   1124                 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
   1125                 "%s",
   1126                 VenHwString
   1127                 );
   1128   } else if (NumNodes >= 3 &&
   1129              SubstringEq (OfwNode[1].DriverName, "channel") &&
   1130              SubstringEq (OfwNode[2].DriverName, "disk")) {
   1131     //
   1132     // OpenFirmware device path (virtio-scsi disk):
   1133     //
   1134     //   /virtio-mmio@000000000a003a00/channel@0/disk@2,3
   1135     //                ^                        ^      ^ ^
   1136     //                |                        |      | LUN
   1137     //                |                        |      target
   1138     //                |                        channel (unused, fixed 0)
   1139     //                base address of virtio-mmio register block
   1140     //
   1141     // UEFI device path prefix:
   1142     //
   1143     //   <VenHwString>/Scsi(0x2,0x3)
   1144     //
   1145     UINT64 TargetLun[2];
   1146 
   1147     TargetLun[1] = 0;
   1148     NumEntries = ARRAY_SIZE (TargetLun);
   1149     if (ParseUnitAddressHexList (
   1150           OfwNode[2].UnitAddress,
   1151           TargetLun,
   1152           &NumEntries
   1153           ) != RETURN_SUCCESS
   1154         ) {
   1155       return RETURN_UNSUPPORTED;
   1156     }
   1157 
   1158     Written = UnicodeSPrintAsciiFormat (
   1159                 Translated,
   1160                 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
   1161                 "%s/Scsi(0x%Lx,0x%Lx)",
   1162                 VenHwString,
   1163                 TargetLun[0],
   1164                 TargetLun[1]
   1165                 );
   1166   } else if (NumNodes >= 2 &&
   1167              SubstringEq (OfwNode[1].DriverName, "ethernet-phy")) {
   1168     //
   1169     // OpenFirmware device path (virtio-net NIC):
   1170     //
   1171     //   /virtio-mmio@000000000a003e00/ethernet-phy@0
   1172     //                ^                             ^
   1173     //                |                             fixed
   1174     //                base address of virtio-mmio register block
   1175     //
   1176     // UEFI device path prefix (dependent on presence of nonzero PCI function):
   1177     //
   1178     //   <VenHwString>/MAC(
   1179     //
   1180     Written = UnicodeSPrintAsciiFormat (
   1181                 Translated,
   1182                 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
   1183                 "%s/MAC(",
   1184                 VenHwString
   1185                 );
   1186   } else {
   1187     return RETURN_UNSUPPORTED;
   1188   }
   1189 
   1190   //
   1191   // There's no way to differentiate between "completely used up without
   1192   // truncation" and "truncated", so treat the former as the latter, and return
   1193   // success only for "some room left unused".
   1194   //
   1195   if (Written + 1 < *TranslatedSize) {
   1196     *TranslatedSize = Written;
   1197     return RETURN_SUCCESS;
   1198   }
   1199 
   1200   return RETURN_BUFFER_TOO_SMALL;
   1201 }
   1202 
   1203 
   1204 /**
   1205 
   1206   Translate an array of OpenFirmware device nodes to a UEFI device path
   1207   fragment.
   1208 
   1209   @param[in]     OfwNode         Array of OpenFirmware device nodes to
   1210                                  translate, constituting the beginning of an
   1211                                  OpenFirmware device path.
   1212 
   1213   @param[in]     NumNodes        Number of elements in OfwNode.
   1214 
   1215   @param[in]     ExtraPciRoots   An EXTRA_ROOT_BUS_MAP object created with
   1216                                  CreateExtraRootBusMap(), to be used for
   1217                                  translating positions of extra root buses to
   1218                                  bus numbers.
   1219 
   1220   @param[out]    Translated      Destination array receiving the UEFI path
   1221                                  fragment, allocated by the caller. If the
   1222                                  return value differs from RETURN_SUCCESS, its
   1223                                  contents is indeterminate.
   1224 
   1225   @param[in out] TranslatedSize  On input, the number of CHAR16's in
   1226                                  Translated. On RETURN_SUCCESS this parameter
   1227                                  is assigned the number of non-NUL CHAR16's
   1228                                  written to Translated. In case of other return
   1229                                  values, TranslatedSize is indeterminate.
   1230 
   1231 
   1232   @retval RETURN_SUCCESS           Translation successful.
   1233 
   1234   @retval RETURN_BUFFER_TOO_SMALL  The translation does not fit into the number
   1235                                    of bytes provided.
   1236 
   1237   @retval RETURN_UNSUPPORTED       The array of OpenFirmware device nodes can't
   1238                                    be translated in the current implementation.
   1239 
   1240   @retval RETURN_PROTOCOL_ERROR    The array of OpenFirmware device nodes has
   1241                                    been (partially) recognized, but it contains
   1242                                    a logic error / doesn't match system state.
   1243 
   1244 **/
   1245 STATIC
   1246 RETURN_STATUS
   1247 TranslateOfwNodes (
   1248   IN      CONST OFW_NODE           *OfwNode,
   1249   IN      UINTN                    NumNodes,
   1250   IN      CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,
   1251   OUT     CHAR16                   *Translated,
   1252   IN OUT  UINTN                    *TranslatedSize
   1253   )
   1254 {
   1255   RETURN_STATUS Status;
   1256 
   1257   Status = RETURN_UNSUPPORTED;
   1258 
   1259   if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {
   1260     Status = TranslatePciOfwNodes (OfwNode, NumNodes, ExtraPciRoots,
   1261                Translated, TranslatedSize);
   1262   }
   1263   if (Status == RETURN_UNSUPPORTED &&
   1264       FeaturePcdGet (PcdQemuBootOrderMmioTranslation)) {
   1265     Status = TranslateMmioOfwNodes (OfwNode, NumNodes, Translated,
   1266                TranslatedSize);
   1267   }
   1268   return Status;
   1269 }
   1270 
   1271 /**
   1272 
   1273   Translate an OpenFirmware device path fragment to a UEFI device path
   1274   fragment, and advance in the input string.
   1275 
   1276   @param[in out] Ptr             Address of the pointer pointing to the start
   1277                                  of the path string. After successful
   1278                                  translation (RETURN_SUCCESS) or at least
   1279                                  successful parsing (RETURN_UNSUPPORTED,
   1280                                  RETURN_BUFFER_TOO_SMALL), *Ptr is set to the
   1281                                  byte immediately following the consumed
   1282                                  characters. In other error cases, it points to
   1283                                  the byte that caused the error.
   1284 
   1285   @param[in]     ExtraPciRoots   An EXTRA_ROOT_BUS_MAP object created with
   1286                                  CreateExtraRootBusMap(), to be used for
   1287                                  translating positions of extra root buses to
   1288                                  bus numbers.
   1289 
   1290   @param[out]    Translated      Destination array receiving the UEFI path
   1291                                  fragment, allocated by the caller. If the
   1292                                  return value differs from RETURN_SUCCESS, its
   1293                                  contents is indeterminate.
   1294 
   1295   @param[in out] TranslatedSize  On input, the number of CHAR16's in
   1296                                  Translated. On RETURN_SUCCESS this parameter
   1297                                  is assigned the number of non-NUL CHAR16's
   1298                                  written to Translated. In case of other return
   1299                                  values, TranslatedSize is indeterminate.
   1300 
   1301 
   1302   @retval RETURN_SUCCESS            Translation successful.
   1303 
   1304   @retval RETURN_BUFFER_TOO_SMALL   The OpenFirmware device path was parsed
   1305                                     successfully, but its translation did not
   1306                                     fit into the number of bytes provided.
   1307                                     Further calls to this function are
   1308                                     possible.
   1309 
   1310   @retval RETURN_UNSUPPORTED        The OpenFirmware device path was parsed
   1311                                     successfully, but it can't be translated in
   1312                                     the current implementation. Further calls
   1313                                     to this function are possible.
   1314 
   1315   @retval RETURN_PROTOCOL_ERROR     The OpenFirmware device path has been
   1316                                     (partially) recognized, but it contains a
   1317                                     logic error / doesn't match system state.
   1318                                     Further calls to this function are
   1319                                     possible.
   1320 
   1321   @retval RETURN_NOT_FOUND          Translation terminated. On input, *Ptr was
   1322                                     pointing to the empty string or "HALT". On
   1323                                     output, *Ptr points to the empty string
   1324                                     (ie. "HALT" is consumed transparently when
   1325                                     present).
   1326 
   1327   @retval RETURN_INVALID_PARAMETER  Parse error. This is a permanent error.
   1328 
   1329 **/
   1330 STATIC
   1331 RETURN_STATUS
   1332 TranslateOfwPath (
   1333   IN OUT  CONST CHAR8              **Ptr,
   1334   IN      CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,
   1335   OUT     CHAR16                   *Translated,
   1336   IN OUT  UINTN                    *TranslatedSize
   1337   )
   1338 {
   1339   UINTN         NumNodes;
   1340   RETURN_STATUS Status;
   1341   OFW_NODE      Node[EXAMINED_OFW_NODES];
   1342   BOOLEAN       IsFinal;
   1343   OFW_NODE      Skip;
   1344 
   1345   IsFinal = FALSE;
   1346   NumNodes = 0;
   1347   if (AsciiStrCmp (*Ptr, "HALT") == 0) {
   1348     *Ptr += 4;
   1349     Status = RETURN_NOT_FOUND;
   1350   } else {
   1351     Status = ParseOfwNode (Ptr, &Node[NumNodes], &IsFinal);
   1352   }
   1353 
   1354   if (Status == RETURN_NOT_FOUND) {
   1355     DEBUG ((DEBUG_VERBOSE, "%a: no more nodes\n", __FUNCTION__));
   1356     return RETURN_NOT_FOUND;
   1357   }
   1358 
   1359   while (Status == RETURN_SUCCESS && !IsFinal) {
   1360     ++NumNodes;
   1361     Status = ParseOfwNode (
   1362                Ptr,
   1363                (NumNodes < EXAMINED_OFW_NODES) ? &Node[NumNodes] : &Skip,
   1364                &IsFinal
   1365                );
   1366   }
   1367 
   1368   switch (Status) {
   1369   case RETURN_SUCCESS:
   1370     ++NumNodes;
   1371     break;
   1372 
   1373   case RETURN_INVALID_PARAMETER:
   1374     DEBUG ((DEBUG_VERBOSE, "%a: parse error\n", __FUNCTION__));
   1375     return RETURN_INVALID_PARAMETER;
   1376 
   1377   default:
   1378     ASSERT (0);
   1379   }
   1380 
   1381   Status = TranslateOfwNodes (
   1382              Node,
   1383              NumNodes < EXAMINED_OFW_NODES ? NumNodes : EXAMINED_OFW_NODES,
   1384              ExtraPciRoots,
   1385              Translated,
   1386              TranslatedSize);
   1387   switch (Status) {
   1388   case RETURN_SUCCESS:
   1389     DEBUG ((DEBUG_VERBOSE, "%a: success: \"%s\"\n", __FUNCTION__, Translated));
   1390     break;
   1391 
   1392   case RETURN_BUFFER_TOO_SMALL:
   1393     DEBUG ((DEBUG_VERBOSE, "%a: buffer too small\n", __FUNCTION__));
   1394     break;
   1395 
   1396   case RETURN_UNSUPPORTED:
   1397     DEBUG ((DEBUG_VERBOSE, "%a: unsupported\n", __FUNCTION__));
   1398     break;
   1399 
   1400   case RETURN_PROTOCOL_ERROR:
   1401     DEBUG ((DEBUG_VERBOSE, "%a: logic error / system state mismatch\n",
   1402       __FUNCTION__));
   1403     break;
   1404 
   1405   default:
   1406     ASSERT (0);
   1407   }
   1408   return Status;
   1409 }
   1410 
   1411 
   1412 /**
   1413 
   1414   Convert the UEFI DevicePath to full text representation with DevPathToText,
   1415   then match the UEFI device path fragment in Translated against it.
   1416 
   1417   @param[in] Translated        UEFI device path fragment, translated from
   1418                                OpenFirmware format, to search for.
   1419 
   1420   @param[in] TranslatedLength  The length of Translated in CHAR16's.
   1421 
   1422   @param[in] DevicePath        Boot option device path whose textual rendering
   1423                                to search in.
   1424 
   1425   @param[in] DevPathToText  Binary-to-text conversion protocol for DevicePath.
   1426 
   1427 
   1428   @retval TRUE   If Translated was found at the beginning of DevicePath after
   1429                  converting the latter to text.
   1430 
   1431   @retval FALSE  If DevicePath was NULL, or it could not be converted, or there
   1432                  was no match.
   1433 
   1434 **/
   1435 STATIC
   1436 BOOLEAN
   1437 Match (
   1438   IN  CONST CHAR16                           *Translated,
   1439   IN  UINTN                                  TranslatedLength,
   1440   IN  EFI_DEVICE_PATH_PROTOCOL               *DevicePath
   1441   )
   1442 {
   1443   CHAR16                   *Converted;
   1444   BOOLEAN                  Result;
   1445   VOID                     *FileBuffer;
   1446   UINTN                    FileSize;
   1447   EFI_DEVICE_PATH_PROTOCOL *AbsDevicePath;
   1448   CHAR16                   *AbsConverted;
   1449   BOOLEAN                  Shortform;
   1450   EFI_DEVICE_PATH_PROTOCOL *Node;
   1451 
   1452   Converted = ConvertDevicePathToText (
   1453                 DevicePath,
   1454                 FALSE, // DisplayOnly
   1455                 FALSE  // AllowShortcuts
   1456                 );
   1457   if (Converted == NULL) {
   1458     return FALSE;
   1459   }
   1460 
   1461   Result = FALSE;
   1462   Shortform = FALSE;
   1463   //
   1464   // Expand the short-form device path to full device path
   1465   //
   1466   if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&
   1467       (DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP)) {
   1468     //
   1469     // Harddrive shortform device path
   1470     //
   1471     Shortform = TRUE;
   1472   } else if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&
   1473              (DevicePathSubType (DevicePath) == MEDIA_FILEPATH_DP)) {
   1474     //
   1475     // File-path shortform device path
   1476     //
   1477     Shortform = TRUE;
   1478   } else if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
   1479              (DevicePathSubType (DevicePath) == MSG_URI_DP)) {
   1480     //
   1481     // URI shortform device path
   1482     //
   1483     Shortform = TRUE;
   1484   } else {
   1485     for ( Node = DevicePath
   1486         ; !IsDevicePathEnd (Node)
   1487         ; Node = NextDevicePathNode (Node)
   1488         ) {
   1489       if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) &&
   1490           ((DevicePathSubType (Node) == MSG_USB_CLASS_DP) ||
   1491            (DevicePathSubType (Node) == MSG_USB_WWID_DP))) {
   1492         Shortform = TRUE;
   1493         break;
   1494       }
   1495     }
   1496   }
   1497 
   1498   //
   1499   // Attempt to expand any relative UEFI device path to
   1500   // an absolute device path first.
   1501   //
   1502   if (Shortform) {
   1503     FileBuffer = EfiBootManagerGetLoadOptionBuffer (
   1504                    DevicePath, &AbsDevicePath, &FileSize
   1505                    );
   1506     if (FileBuffer == NULL) {
   1507       goto Exit;
   1508     }
   1509     FreePool (FileBuffer);
   1510     AbsConverted = ConvertDevicePathToText (AbsDevicePath, FALSE, FALSE);
   1511     FreePool (AbsDevicePath);
   1512     if (AbsConverted == NULL) {
   1513       goto Exit;
   1514     }
   1515     DEBUG ((DEBUG_VERBOSE,
   1516       "%a: expanded relative device path \"%s\" for prefix matching\n",
   1517       __FUNCTION__, Converted));
   1518     FreePool (Converted);
   1519     Converted = AbsConverted;
   1520   }
   1521 
   1522   //
   1523   // Is Translated a prefix of Converted?
   1524   //
   1525   Result = (BOOLEAN)(StrnCmp (Converted, Translated, TranslatedLength) == 0);
   1526   DEBUG ((
   1527     DEBUG_VERBOSE,
   1528     "%a: against \"%s\": %a\n",
   1529     __FUNCTION__,
   1530     Converted,
   1531     Result ? "match" : "no match"
   1532     ));
   1533 Exit:
   1534   FreePool (Converted);
   1535   return Result;
   1536 }
   1537 
   1538 
   1539 /**
   1540   Append some of the unselected active boot options to the boot order.
   1541 
   1542   This function should accommodate any further policy changes in "boot option
   1543   survival". Currently we're adding back everything that starts with neither
   1544   PciRoot() nor HD() nor a virtio-mmio VenHw() node.
   1545 
   1546   @param[in,out] BootOrder     The structure holding the boot order to
   1547                                complete. The caller is responsible for
   1548                                initializing (and potentially populating) it
   1549                                before calling this function.
   1550 
   1551   @param[in,out] ActiveOption  The array of active boot options to scan.
   1552                                Entries marked as Appended will be skipped.
   1553                                Those of the rest that satisfy the survival
   1554                                policy will be added to BootOrder with
   1555                                BootOrderAppend().
   1556 
   1557   @param[in]     ActiveCount   Number of elements in ActiveOption.
   1558 
   1559 
   1560   @retval RETURN_SUCCESS  BootOrder has been extended with any eligible boot
   1561                           options.
   1562 
   1563   @return                 Error codes returned by BootOrderAppend().
   1564 **/
   1565 STATIC
   1566 RETURN_STATUS
   1567 BootOrderComplete (
   1568   IN OUT  BOOT_ORDER    *BootOrder,
   1569   IN OUT  ACTIVE_OPTION *ActiveOption,
   1570   IN      UINTN         ActiveCount
   1571   )
   1572 {
   1573   RETURN_STATUS Status;
   1574   UINTN         Idx;
   1575 
   1576   Status = RETURN_SUCCESS;
   1577   Idx = 0;
   1578   while (!RETURN_ERROR (Status) && Idx < ActiveCount) {
   1579     if (!ActiveOption[Idx].Appended) {
   1580       CONST EFI_BOOT_MANAGER_LOAD_OPTION *Current;
   1581       CONST EFI_DEVICE_PATH_PROTOCOL     *FirstNode;
   1582 
   1583       Current = ActiveOption[Idx].BootOption;
   1584       FirstNode = Current->FilePath;
   1585       if (FirstNode != NULL) {
   1586         CHAR16        *Converted;
   1587         STATIC CHAR16 ConvFallBack[] = L"<unable to convert>";
   1588         BOOLEAN       Keep;
   1589 
   1590         Converted = ConvertDevicePathToText (FirstNode, FALSE, FALSE);
   1591         if (Converted == NULL) {
   1592           Converted = ConvFallBack;
   1593         }
   1594 
   1595         Keep = TRUE;
   1596         if (DevicePathType(FirstNode) == MEDIA_DEVICE_PATH &&
   1597             DevicePathSubType(FirstNode) == MEDIA_HARDDRIVE_DP) {
   1598           //
   1599           // drop HD()
   1600           //
   1601           Keep = FALSE;
   1602         } else if (DevicePathType(FirstNode) == ACPI_DEVICE_PATH &&
   1603                    DevicePathSubType(FirstNode) == ACPI_DP) {
   1604           ACPI_HID_DEVICE_PATH *Acpi;
   1605 
   1606           Acpi = (ACPI_HID_DEVICE_PATH *) FirstNode;
   1607           if ((Acpi->HID & PNP_EISA_ID_MASK) == PNP_EISA_ID_CONST &&
   1608               EISA_ID_TO_NUM (Acpi->HID) == 0x0a03) {
   1609             //
   1610             // drop PciRoot() if we enabled the user to select PCI-like boot
   1611             // options, by providing translation for such OFW device path
   1612             // fragments
   1613             //
   1614             Keep = !FeaturePcdGet (PcdQemuBootOrderPciTranslation);
   1615           }
   1616         } else if (DevicePathType(FirstNode) == HARDWARE_DEVICE_PATH &&
   1617                    DevicePathSubType(FirstNode) == HW_VENDOR_DP) {
   1618           VENDOR_DEVICE_PATH *VenHw;
   1619 
   1620           VenHw = (VENDOR_DEVICE_PATH *)FirstNode;
   1621           if (CompareGuid (&VenHw->Guid, &gVirtioMmioTransportGuid)) {
   1622             //
   1623             // drop virtio-mmio if we enabled the user to select boot options
   1624             // referencing such device paths
   1625             //
   1626             Keep = !FeaturePcdGet (PcdQemuBootOrderMmioTranslation);
   1627           }
   1628         }
   1629 
   1630         if (Keep) {
   1631           Status = BootOrderAppend (BootOrder, &ActiveOption[Idx]);
   1632           if (!RETURN_ERROR (Status)) {
   1633             DEBUG ((DEBUG_VERBOSE, "%a: keeping \"%s\"\n", __FUNCTION__,
   1634               Converted));
   1635           }
   1636         } else {
   1637           DEBUG ((DEBUG_VERBOSE, "%a: dropping \"%s\"\n", __FUNCTION__,
   1638             Converted));
   1639         }
   1640 
   1641         if (Converted != ConvFallBack) {
   1642           FreePool (Converted);
   1643         }
   1644       }
   1645     }
   1646     ++Idx;
   1647   }
   1648   return Status;
   1649 }
   1650 
   1651 
   1652 /**
   1653   Delete Boot#### variables that stand for such active boot options that have
   1654   been dropped (ie. have not been selected by either matching or "survival
   1655   policy").
   1656 
   1657   @param[in]  ActiveOption  The array of active boot options to scan. Each
   1658                             entry not marked as appended will trigger the
   1659                             deletion of the matching Boot#### variable.
   1660 
   1661   @param[in]  ActiveCount   Number of elements in ActiveOption.
   1662 **/
   1663 STATIC
   1664 VOID
   1665 PruneBootVariables (
   1666   IN  CONST ACTIVE_OPTION *ActiveOption,
   1667   IN  UINTN               ActiveCount
   1668   )
   1669 {
   1670   UINTN Idx;
   1671 
   1672   for (Idx = 0; Idx < ActiveCount; ++Idx) {
   1673     if (!ActiveOption[Idx].Appended) {
   1674       CHAR16 VariableName[9];
   1675 
   1676       UnicodeSPrintAsciiFormat (VariableName, sizeof VariableName, "Boot%04x",
   1677         ActiveOption[Idx].BootOption->OptionNumber);
   1678 
   1679       //
   1680       // "The space consumed by the deleted variable may not be available until
   1681       // the next power cycle", but that's good enough.
   1682       //
   1683       gRT->SetVariable (VariableName, &gEfiGlobalVariableGuid,
   1684              0,   // Attributes, 0 means deletion
   1685              0,   // DataSize, 0 means deletion
   1686              NULL // Data
   1687              );
   1688     }
   1689   }
   1690 }
   1691 
   1692 
   1693 /**
   1694 
   1695   Set the boot order based on configuration retrieved from QEMU.
   1696 
   1697   Attempt to retrieve the "bootorder" fw_cfg file from QEMU. Translate the
   1698   OpenFirmware device paths therein to UEFI device path fragments. Match the
   1699   translated fragments against the current list of boot options, and rewrite
   1700   the BootOrder NvVar so that it corresponds to the order described in fw_cfg.
   1701 
   1702   Platform BDS should call this function after EfiBootManagerConnectAll () and
   1703   EfiBootManagerRefreshAllBootOption () return.
   1704 
   1705   @retval RETURN_SUCCESS            BootOrder NvVar rewritten.
   1706 
   1707   @retval RETURN_UNSUPPORTED        QEMU's fw_cfg is not supported.
   1708 
   1709   @retval RETURN_NOT_FOUND          Empty or nonexistent "bootorder" fw_cfg
   1710                                     file, or no match found between the
   1711                                     "bootorder" fw_cfg file and BootOptionList.
   1712 
   1713   @retval RETURN_INVALID_PARAMETER  Parse error in the "bootorder" fw_cfg file.
   1714 
   1715   @retval RETURN_OUT_OF_RESOURCES   Memory allocation failed.
   1716 
   1717   @return                           Values returned by gBS->LocateProtocol ()
   1718                                     or gRT->SetVariable ().
   1719 
   1720 **/
   1721 RETURN_STATUS
   1722 SetBootOrderFromQemu (
   1723   VOID
   1724   )
   1725 {
   1726   RETURN_STATUS                    Status;
   1727   FIRMWARE_CONFIG_ITEM             FwCfgItem;
   1728   UINTN                            FwCfgSize;
   1729   CHAR8                            *FwCfg;
   1730   CONST CHAR8                      *FwCfgPtr;
   1731 
   1732   BOOT_ORDER                       BootOrder;
   1733   ACTIVE_OPTION                    *ActiveOption;
   1734   UINTN                            ActiveCount;
   1735 
   1736   EXTRA_ROOT_BUS_MAP               *ExtraPciRoots;
   1737 
   1738   UINTN                            TranslatedSize;
   1739   CHAR16                           Translated[TRANSLATION_OUTPUT_SIZE];
   1740   EFI_BOOT_MANAGER_LOAD_OPTION     *BootOptions;
   1741   UINTN                            BootOptionCount;
   1742 
   1743   Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);
   1744   if (Status != RETURN_SUCCESS) {
   1745     return Status;
   1746   }
   1747 
   1748   if (FwCfgSize == 0) {
   1749     return RETURN_NOT_FOUND;
   1750   }
   1751 
   1752   FwCfg = AllocatePool (FwCfgSize);
   1753   if (FwCfg == NULL) {
   1754     return RETURN_OUT_OF_RESOURCES;
   1755   }
   1756 
   1757   QemuFwCfgSelectItem (FwCfgItem);
   1758   QemuFwCfgReadBytes (FwCfgSize, FwCfg);
   1759   if (FwCfg[FwCfgSize - 1] != '\0') {
   1760     Status = RETURN_INVALID_PARAMETER;
   1761     goto ErrorFreeFwCfg;
   1762   }
   1763 
   1764   DEBUG ((DEBUG_VERBOSE, "%a: FwCfg:\n", __FUNCTION__));
   1765   DEBUG ((DEBUG_VERBOSE, "%a\n", FwCfg));
   1766   DEBUG ((DEBUG_VERBOSE, "%a: FwCfg: <end>\n", __FUNCTION__));
   1767   FwCfgPtr = FwCfg;
   1768 
   1769   BootOrder.Produced  = 0;
   1770   BootOrder.Allocated = 1;
   1771   BootOrder.Data = AllocatePool (
   1772                      BootOrder.Allocated * sizeof (*BootOrder.Data)
   1773                      );
   1774   if (BootOrder.Data == NULL) {
   1775     Status = RETURN_OUT_OF_RESOURCES;
   1776     goto ErrorFreeFwCfg;
   1777   }
   1778 
   1779   BootOptions = EfiBootManagerGetLoadOptions (
   1780                   &BootOptionCount, LoadOptionTypeBoot
   1781                   );
   1782   if (BootOptions == NULL) {
   1783     Status = RETURN_NOT_FOUND;
   1784     goto ErrorFreeBootOrder;
   1785   }
   1786 
   1787   Status = CollectActiveOptions (
   1788              BootOptions, BootOptionCount, &ActiveOption, &ActiveCount
   1789              );
   1790   if (RETURN_ERROR (Status)) {
   1791     goto ErrorFreeBootOptions;
   1792   }
   1793 
   1794   if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {
   1795     Status = CreateExtraRootBusMap (&ExtraPciRoots);
   1796     if (EFI_ERROR (Status)) {
   1797       goto ErrorFreeActiveOption;
   1798     }
   1799   } else {
   1800     ExtraPciRoots = NULL;
   1801   }
   1802 
   1803   //
   1804   // translate each OpenFirmware path
   1805   //
   1806   TranslatedSize = ARRAY_SIZE (Translated);
   1807   Status = TranslateOfwPath (&FwCfgPtr, ExtraPciRoots, Translated,
   1808              &TranslatedSize);
   1809   while (Status == RETURN_SUCCESS ||
   1810          Status == RETURN_UNSUPPORTED ||
   1811          Status == RETURN_PROTOCOL_ERROR ||
   1812          Status == RETURN_BUFFER_TOO_SMALL) {
   1813     if (Status == RETURN_SUCCESS) {
   1814       UINTN Idx;
   1815 
   1816       //
   1817       // match translated OpenFirmware path against all active boot options
   1818       //
   1819       for (Idx = 0; Idx < ActiveCount; ++Idx) {
   1820         if (Match (
   1821               Translated,
   1822               TranslatedSize, // contains length, not size, in CHAR16's here
   1823               ActiveOption[Idx].BootOption->FilePath
   1824               )
   1825             ) {
   1826           //
   1827           // match found, store ID and continue with next OpenFirmware path
   1828           //
   1829           Status = BootOrderAppend (&BootOrder, &ActiveOption[Idx]);
   1830           if (Status != RETURN_SUCCESS) {
   1831             goto ErrorFreeExtraPciRoots;
   1832           }
   1833           break;
   1834         }
   1835       } // scanned all active boot options
   1836     }   // translation successful
   1837 
   1838     TranslatedSize = ARRAY_SIZE (Translated);
   1839     Status = TranslateOfwPath (&FwCfgPtr, ExtraPciRoots, Translated,
   1840                &TranslatedSize);
   1841   } // scanning of OpenFirmware paths done
   1842 
   1843   if (Status == RETURN_NOT_FOUND && BootOrder.Produced > 0) {
   1844     //
   1845     // No more OpenFirmware paths, some matches found: rewrite BootOrder NvVar.
   1846     // Some of the active boot options that have not been selected over fw_cfg
   1847     // should be preserved at the end of the boot order.
   1848     //
   1849     Status = BootOrderComplete (&BootOrder, ActiveOption, ActiveCount);
   1850     if (RETURN_ERROR (Status)) {
   1851       goto ErrorFreeExtraPciRoots;
   1852     }
   1853 
   1854     //
   1855     // See Table 10 in the UEFI Spec 2.3.1 with Errata C for the required
   1856     // attributes.
   1857     //
   1858     Status = gRT->SetVariable (
   1859                     L"BootOrder",
   1860                     &gEfiGlobalVariableGuid,
   1861                     EFI_VARIABLE_NON_VOLATILE |
   1862                       EFI_VARIABLE_BOOTSERVICE_ACCESS |
   1863                       EFI_VARIABLE_RUNTIME_ACCESS,
   1864                     BootOrder.Produced * sizeof (*BootOrder.Data),
   1865                     BootOrder.Data
   1866                     );
   1867     if (EFI_ERROR (Status)) {
   1868       DEBUG ((DEBUG_ERROR, "%a: setting BootOrder: %r\n", __FUNCTION__, Status));
   1869       goto ErrorFreeExtraPciRoots;
   1870     }
   1871 
   1872     DEBUG ((DEBUG_INFO, "%a: setting BootOrder: success\n", __FUNCTION__));
   1873     PruneBootVariables (ActiveOption, ActiveCount);
   1874   }
   1875 
   1876 ErrorFreeExtraPciRoots:
   1877   if (ExtraPciRoots != NULL) {
   1878     DestroyExtraRootBusMap (ExtraPciRoots);
   1879   }
   1880 
   1881 ErrorFreeActiveOption:
   1882   FreePool (ActiveOption);
   1883 
   1884 ErrorFreeBootOptions:
   1885   EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
   1886 
   1887 ErrorFreeBootOrder:
   1888   FreePool (BootOrder.Data);
   1889 
   1890 ErrorFreeFwCfg:
   1891   FreePool (FwCfg);
   1892 
   1893   return Status;
   1894 }
   1895 
   1896 
   1897 /**
   1898   Calculate the number of seconds we should be showing the FrontPage progress
   1899   bar for.
   1900 
   1901   @return  The TimeoutDefault argument for PlatformBdsEnterFrontPage().
   1902 **/
   1903 UINT16
   1904 GetFrontPageTimeoutFromQemu (
   1905   VOID
   1906   )
   1907 {
   1908   FIRMWARE_CONFIG_ITEM BootMenuWaitItem;
   1909   UINTN                BootMenuWaitSize;
   1910 
   1911   QemuFwCfgSelectItem (QemuFwCfgItemBootMenu);
   1912   if (QemuFwCfgRead16 () == 0) {
   1913     //
   1914     // The user specified "-boot menu=off", or didn't specify "-boot
   1915     // menu=(on|off)" at all. Return the platform default.
   1916     //
   1917     return PcdGet16 (PcdPlatformBootTimeOut);
   1918   }
   1919 
   1920   if (RETURN_ERROR (QemuFwCfgFindFile ("etc/boot-menu-wait", &BootMenuWaitItem,
   1921                       &BootMenuWaitSize)) ||
   1922       BootMenuWaitSize != sizeof (UINT16)) {
   1923     //
   1924     // "-boot menu=on" was specified without "splash-time=N". In this case,
   1925     // return three seconds if the platform default would cause us to skip the
   1926     // front page, and return the platform default otherwise.
   1927     //
   1928     UINT16 Timeout;
   1929 
   1930     Timeout = PcdGet16 (PcdPlatformBootTimeOut);
   1931     if (Timeout == 0) {
   1932       Timeout = 3;
   1933     }
   1934     return Timeout;
   1935   }
   1936 
   1937   //
   1938   // "-boot menu=on,splash-time=N" was specified, where N is in units of
   1939   // milliseconds. The Intel BDS Front Page progress bar only supports whole
   1940   // seconds, round N up.
   1941   //
   1942   QemuFwCfgSelectItem (BootMenuWaitItem);
   1943   return (UINT16)((QemuFwCfgRead16 () + 999) / 1000);
   1944 }
   1945