]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.c
OvmfPkg/QemuBootOrderLib: skip unsupported entries in StoreQemuBootOrder
[mirror_edk2.git] / OvmfPkg / Library / QemuBootOrderLib / QemuBootOrderLib.c
index bc2fb56954259926033db5206e5756a4d8598f21..18646daa67e3247403628bba0774d3025736f53b 100644 (file)
@@ -2,21 +2,15 @@
   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
+  Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.<BR>\r
 \r
-  This program and the accompanying materials are licensed and made available\r
-  under the terms and conditions of the BSD License which accompanies this\r
-  distribution.  The full text of the license may be found at\r
-  http://opensource.org/licenses/bsd-license.php\r
-\r
-  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
-  WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\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/UefiBootManagerLib.h>\r
 #include <Library/UefiBootServicesTableLib.h>\r
 #include <Library/UefiRuntimeServicesTableLib.h>\r
 #include <Library/BaseLib.h>\r
 #include <Guid/GlobalVariable.h>\r
 #include <Guid/VirtioMmioTransport.h>\r
 \r
+#include "ExtraRootBusMap.h"\r
 \r
 /**\r
   OpenFirmware to UEFI device path translation output buffer size in CHAR16's.\r
 **/\r
-#define TRANSLATION_OUTPUT_SIZE 0x100\r
+#define TRANSLATION_OUTPUT_SIZE  0x100\r
 \r
+/**\r
+  Output buffer size for OpenFirmware to UEFI device path fragment translation,\r
+  in CHAR16's, for a sequence of PCI bridges.\r
+**/\r
+#define BRIDGE_TRANSLATION_OUTPUT_SIZE  0x40\r
 \r
 /**\r
   Numbers of nodes in OpenFirmware device paths that are required and examined.\r
 **/\r
-#define REQUIRED_PCI_OFW_NODES  2\r
-#define REQUIRED_MMIO_OFW_NODES 1\r
-#define EXAMINED_OFW_NODES      4\r
-\r
+#define REQUIRED_PCI_OFW_NODES   2\r
+#define REQUIRED_MMIO_OFW_NODES  1\r
+#define EXAMINED_OFW_NODES       6\r
 \r
 /**\r
   Simple character classification routines, corresponding to POSIX class names\r
@@ -49,7 +48,7 @@
 STATIC\r
 BOOLEAN\r
 IsAlnum (\r
-  IN  CHAR8 Chr\r
+  IN  CHAR8  Chr\r
   )\r
 {\r
   return (('0' <= Chr && Chr <= '9') ||\r
@@ -58,11 +57,10 @@ IsAlnum (
           );\r
 }\r
 \r
-\r
 STATIC\r
 BOOLEAN\r
 IsDriverNamePunct (\r
-  IN  CHAR8 Chr\r
+  IN  CHAR8  Chr\r
   )\r
 {\r
   return (Chr == ',' ||  Chr == '.' || Chr == '_' ||\r
@@ -70,27 +68,24 @@ IsDriverNamePunct (
           );\r
 }\r
 \r
-\r
 STATIC\r
 BOOLEAN\r
 IsPrintNotDelim (\r
-  IN  CHAR8 Chr\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
+  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
@@ -109,12 +104,12 @@ typedef struct {
 STATIC\r
 BOOLEAN\r
 SubstringEq (\r
-  IN  SUBSTRING   Substring,\r
-  IN  CONST CHAR8 *String\r
+  IN  SUBSTRING    Substring,\r
+  IN  CONST CHAR8  *String\r
   )\r
 {\r
-  UINTN       Pos;\r
-  CONST CHAR8 *Chr;\r
+  UINTN        Pos;\r
+  CONST CHAR8  *Chr;\r
 \r
   Pos = 0;\r
   Chr = String;\r
@@ -127,7 +122,6 @@ SubstringEq (
   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
@@ -175,38 +169,41 @@ ParseUnitAddressHexList (
   IN OUT  UINTN      *NumResults\r
   )\r
 {\r
-  UINTN         Entry;    // number of entry currently being parsed\r
-  UINT64        EntryVal; // value being constructed for current entry\r
-  CHAR8         PrevChr;  // UnitAddress character previously checked\r
-  UINTN         Pos;      // current position within UnitAddress\r
-  RETURN_STATUS Status;\r
+  UINTN          Entry;    // number of entry currently being parsed\r
+  UINT64         EntryVal; // value being constructed for current entry\r
+  CHAR8          PrevChr;  // UnitAddress character previously checked\r
+  UINTN          Pos;      // current position within UnitAddress\r
+  RETURN_STATUS  Status;\r
 \r
   Entry    = 0;\r
   EntryVal = 0;\r
   PrevChr  = ',';\r
 \r
   for (Pos = 0; Pos < UnitAddress.Len; ++Pos) {\r
-    CHAR8 Chr;\r
-    INT8  Val;\r
+    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
+          ('0' <= Chr && Chr <= '9') ? (Chr - '0') :\r
           -1;\r
 \r
     if (Val >= 0) {\r
       if (EntryVal > 0xFFFFFFFFFFFFFFFull) {\r
         return RETURN_INVALID_PARAMETER;\r
       }\r
+\r
       EntryVal = LShiftU64 (EntryVal, 4) | Val;\r
     } else if (Chr == ',') {\r
       if (PrevChr == ',') {\r
         return RETURN_INVALID_PARAMETER;\r
       }\r
+\r
       if (Entry < *NumResults) {\r
         Result[Entry] = EntryVal;\r
       }\r
+\r
       ++Entry;\r
       EntryVal = 0;\r
     } else {\r
@@ -219,39 +216,40 @@ ParseUnitAddressHexList (
   if (PrevChr == ',') {\r
     return RETURN_INVALID_PARAMETER;\r
   }\r
+\r
   if (Entry < *NumResults) {\r
     Result[Entry] = EntryVal;\r
-    Status = RETURN_SUCCESS;\r
+    Status        = RETURN_SUCCESS;\r
   } else {\r
     Status = RETURN_BUFFER_TOO_SMALL;\r
   }\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
+  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
+  CONST EFI_BOOT_MANAGER_LOAD_OPTION    *BootOption; // reference only, no\r
+                                                     //   ownership\r
+  BOOLEAN                               Appended;    // has been added to a\r
+                                                     //   BOOT_ORDER?\r
 } ACTIVE_OPTION;\r
 \r
-\r
 /**\r
 \r
   Append an active boot option to BootOrder, reallocating the latter if needed.\r
@@ -271,53 +269,56 @@ typedef struct {
 STATIC\r
 RETURN_STATUS\r
 BootOrderAppend (\r
-  IN OUT  BOOT_ORDER    *BootOrder,\r
-  IN OUT  ACTIVE_OPTION *ActiveOption\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
+    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
+    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
+\r
     BootOrder->Allocated = AllocatedNew;\r
     BootOrder->Data      = DataNew;\r
   }\r
 \r
   BootOrder->Data[BootOrder->Produced++] =\r
-                                         ActiveOption->BootOption->BootCurrent;\r
+    (UINT16)ActiveOption->BootOption->OptionNumber;\r
   ActiveOption->Appended = TRUE;\r
   return RETURN_SUCCESS;\r
 }\r
 \r
-\r
 /**\r
 \r
-  Create an array of ACTIVE_OPTION elements for a boot option list.\r
+  Create an array of ACTIVE_OPTION elements for a boot option array.\r
+\r
+  @param[in]  BootOptions      A boot option array, created with\r
+                               EfiBootManagerRefreshAllBootOption () and\r
+                               EfiBootManagerGetLoadOptions ().\r
 \r
-  @param[in]  BootOptionList  A boot option list, created with\r
-                              BdsLibEnumerateAllBootOption().\r
+  @param[in]  BootOptionCount  The number of elements in BootOptions.\r
 \r
-  @param[out] ActiveOption    Pointer to the first element in the new array.\r
-                              The caller is responsible for freeing the array\r
-                              with FreePool() after use.\r
+  @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
+  @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
+                                   BootOptions.\r
 \r
   @retval RETURN_OUT_OF_RESOURCES  Memory allocation failed.\r
 \r
@@ -325,12 +326,14 @@ BootOrderAppend (
 STATIC\r
 RETURN_STATUS\r
 CollectActiveOptions (\r
-  IN   CONST LIST_ENTRY *BootOptionList,\r
-  OUT  ACTIVE_OPTION    **ActiveOption,\r
-  OUT  UINTN            *Count\r
+  IN   CONST EFI_BOOT_MANAGER_LOAD_OPTION  *BootOptions,\r
+  IN   UINTN                               BootOptionCount,\r
+  OUT  ACTIVE_OPTION                       **ActiveOption,\r
+  OUT  UINTN                               *Count\r
   )\r
 {\r
-  UINTN ScanMode;\r
+  UINTN  Index;\r
+  UINTN  ScanMode;\r
 \r
   *ActiveOption = NULL;\r
 \r
@@ -340,48 +343,42 @@ CollectActiveOptions (
   // - 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
+    for (Index = 0; Index < BootOptionCount; Index++) {\r
+      if ((BootOptions[Index].Attributes & LOAD_OPTION_ACTIVE) != 0) {\r
         if (ScanMode == 1) {\r
-          (*ActiveOption)[*Count].BootOption = Current;\r
+          (*ActiveOption)[*Count].BootOption = &BootOptions[Index];\r
           (*ActiveOption)[*Count].Appended   = FALSE;\r
         }\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
+\r
       *ActiveOption = AllocatePool (*Count * sizeof **ActiveOption);\r
       if (*ActiveOption == NULL) {\r
         return RETURN_OUT_OF_RESOURCES;\r
       }\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
+  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
@@ -412,7 +409,7 @@ typedef struct {
                           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
+  @param[out]    IsFinal  In case of successful 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
@@ -430,24 +427,26 @@ typedef struct {
 STATIC\r
 RETURN_STATUS\r
 ParseOfwNode (\r
-  IN OUT  CONST CHAR8 **Ptr,\r
-  OUT     OFW_NODE    *OfwNode,\r
-  OUT     BOOLEAN     *IsFinal\r
+  IN OUT  CONST CHAR8  **Ptr,\r
+  OUT     OFW_NODE     *OfwNode,\r
+  OUT     BOOLEAN      *IsFinal\r
   )\r
 {\r
+  BOOLEAN  AcceptSlash = FALSE;\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
+    case '\0':\r
+      return RETURN_NOT_FOUND;\r
 \r
-  case '/':\r
-    ++*Ptr;\r
-    break;\r
+    case '/':\r
+      ++*Ptr;\r
+      break;\r
 \r
-  default:\r
-    return RETURN_INVALID_PARAMETER;\r
+    default:\r
+      return RETURN_INVALID_PARAMETER;\r
   }\r
 \r
   //\r
@@ -457,15 +456,30 @@ ParseOfwNode (
   OfwNode->DriverName.Len = 0;\r
   while (OfwNode->DriverName.Len < 32 &&\r
          (IsAlnum (**Ptr) || IsDriverNamePunct (**Ptr))\r
-         ) {\r
+         )\r
+  {\r
     ++*Ptr;\r
     ++OfwNode->DriverName.Len;\r
   }\r
 \r
-  if (OfwNode->DriverName.Len == 0 || OfwNode->DriverName.Len == 32) {\r
+  if ((OfwNode->DriverName.Len == 0) || (OfwNode->DriverName.Len == 32)) {\r
     return RETURN_INVALID_PARAMETER;\r
   }\r
 \r
+  if (SubstringEq (OfwNode->DriverName, "rom")) {\r
+    //\r
+    // bug compatibility hack\r
+    //\r
+    // qemu passes fw_cfg filenames as rom unit address.\r
+    // The filenames have slashes:\r
+    //      /rom@genroms/linuxboot_dma.bin\r
+    //\r
+    // Alow slashes in the unit address to avoid the parser trip up,\r
+    // so we can successfully parse the following lines (the rom\r
+    // entries themself are ignored).\r
+    //\r
+    AcceptSlash = TRUE;\r
+  }\r
 \r
   //\r
   // unit-address\r
@@ -473,11 +487,12 @@ ParseOfwNode (
   if (**Ptr != '@') {\r
     return RETURN_INVALID_PARAMETER;\r
   }\r
+\r
   ++*Ptr;\r
 \r
   OfwNode->UnitAddress.Ptr = *Ptr;\r
   OfwNode->UnitAddress.Len = 0;\r
-  while (IsPrintNotDelim (**Ptr)) {\r
+  while (IsPrintNotDelim (**Ptr) || (AcceptSlash && **Ptr == '/')) {\r
     ++*Ptr;\r
     ++OfwNode->UnitAddress.Len;\r
   }\r
@@ -486,7 +501,6 @@ ParseOfwNode (
     return RETURN_INVALID_PARAMETER;\r
   }\r
 \r
-\r
   //\r
   // device-arguments, may be omitted\r
   //\r
@@ -503,43 +517,43 @@ ParseOfwNode (
     if (OfwNode->DeviceArguments.Len == 0) {\r
       return RETURN_INVALID_PARAMETER;\r
     }\r
-  }\r
-  else {\r
+  } else {\r
     OfwNode->DeviceArguments.Ptr = NULL;\r
   }\r
 \r
   switch (**Ptr) {\r
-  case '\n':\r
-    ++*Ptr;\r
+    case '\n':\r
+      ++*Ptr;\r
     //\r
     // fall through\r
     //\r
 \r
-  case '\0':\r
-    *IsFinal = TRUE;\r
-    break;\r
+    case '\0':\r
+      *IsFinal = TRUE;\r
+      break;\r
 \r
-  case '/':\r
-    *IsFinal = FALSE;\r
-    break;\r
+    case '/':\r
+      *IsFinal = FALSE;\r
+      break;\r
 \r
-  default:\r
-    return RETURN_INVALID_PARAMETER;\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->DriverName.Len,\r
+    OfwNode->DriverName.Ptr,\r
+    OfwNode->UnitAddress.Len,\r
+    OfwNode->UnitAddress.Ptr,\r
     OfwNode->DeviceArguments.Len,\r
     OfwNode->DeviceArguments.Ptr == NULL ? "" : OfwNode->DeviceArguments.Ptr\r
     ));\r
   return RETURN_SUCCESS;\r
 }\r
 \r
-\r
 /**\r
 \r
   Translate a PCI-like array of OpenFirmware device nodes to a UEFI device path\r
@@ -551,6 +565,11 @@ ParseOfwNode (
 \r
   @param[in]     NumNodes        Number of elements in OfwNode.\r
 \r
+  @param[in]     ExtraPciRoots   An EXTRA_ROOT_BUS_MAP object created with\r
+                                 CreateExtraRootBusMap(), to be used for\r
+                                 translating positions of extra root buses to\r
+                                 bus numbers.\r
+\r
   @param[out]    Translated      Destination array receiving the UEFI path\r
                                  fragment, allocated by the caller. If the\r
                                  return value differs from RETURN_SUCCESS, its\r
@@ -571,44 +590,173 @@ ParseOfwNode (
   @retval RETURN_UNSUPPORTED       The array of OpenFirmware device nodes can't\r
                                    be translated in the current implementation.\r
 \r
+  @retval RETURN_PROTOCOL_ERROR    The initial OpenFirmware node refers to an\r
+                                   extra PCI root bus (by serial number) that\r
+                                   is invalid according to ExtraPciRoots.\r
+\r
 **/\r
 STATIC\r
 RETURN_STATUS\r
 TranslatePciOfwNodes (\r
-  IN      CONST OFW_NODE *OfwNode,\r
-  IN      UINTN          NumNodes,\r
-  OUT     CHAR16         *Translated,\r
-  IN OUT  UINTN          *TranslatedSize\r
+  IN      CONST OFW_NODE            *OfwNode,\r
+  IN      UINTN                     NumNodes,\r
+  IN      CONST EXTRA_ROOT_BUS_MAP  *ExtraPciRoots,\r
+  OUT     CHAR16                    *Translated,\r
+  IN OUT  UINTN                     *TranslatedSize\r
   )\r
 {\r
-  UINT64 PciDevFun[2];\r
-  UINTN  NumEntries;\r
-  UINTN  Written;\r
+  UINT32  PciRoot;\r
+  CHAR8   *Comma;\r
+  UINTN   FirstNonBridge;\r
+  CHAR16  Bridges[BRIDGE_TRANSLATION_OUTPUT_SIZE];\r
+  UINTN   BridgesLen;\r
+  UINT64  PciDevFun[2];\r
+  UINTN   NumEntries;\r
+  UINTN   Written;\r
 \r
   //\r
-  // Get PCI device and optional PCI function. Assume a single PCI root.\r
+  // Resolve the PCI root bus number.\r
+  //\r
+  // The initial OFW node for the main root bus (ie. bus number 0) is:\r
   //\r
-  if (NumNodes < REQUIRED_PCI_OFW_NODES ||\r
+  //   /pci@i0cf8\r
+  //\r
+  // For extra root buses, the initial OFW node is\r
+  //\r
+  //   /pci@i0cf8,4\r
+  //              ^\r
+  //              root bus serial number (not PCI bus number)\r
+  //\r
+  if ((NumNodes < REQUIRED_PCI_OFW_NODES) ||\r
       !SubstringEq (OfwNode[0].DriverName, "pci")\r
-      ) {\r
+      )\r
+  {\r
     return RETURN_UNSUPPORTED;\r
   }\r
+\r
+  PciRoot = 0;\r
+  Comma   = ScanMem8 (\r
+              OfwNode[0].UnitAddress.Ptr,\r
+              OfwNode[0].UnitAddress.Len,\r
+              ','\r
+              );\r
+  if (Comma != NULL) {\r
+    SUBSTRING  PciRootSerialSubString;\r
+    UINT64     PciRootSerial;\r
+\r
+    //\r
+    // Parse the root bus serial number from the unit address after the comma.\r
+    //\r
+    PciRootSerialSubString.Ptr = Comma + 1;\r
+    PciRootSerialSubString.Len = OfwNode[0].UnitAddress.Len -\r
+                                 (PciRootSerialSubString.Ptr -\r
+                                  OfwNode[0].UnitAddress.Ptr);\r
+    NumEntries = 1;\r
+    if (RETURN_ERROR (\r
+          ParseUnitAddressHexList (\r
+            PciRootSerialSubString,\r
+            &PciRootSerial,\r
+            &NumEntries\r
+            )\r
+          ))\r
+    {\r
+      return RETURN_UNSUPPORTED;\r
+    }\r
+\r
+    //\r
+    // Map the extra root bus's serial number to its actual bus number.\r
+    //\r
+    if (EFI_ERROR (\r
+          MapRootBusPosToBusNr (\r
+            ExtraPciRoots,\r
+            PciRootSerial,\r
+            &PciRoot\r
+            )\r
+          ))\r
+    {\r
+      return RETURN_PROTOCOL_ERROR;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Translate a sequence of PCI bridges. For each bridge, the OFW node is:\r
+  //\r
+  //   pci-bridge@1e[,0]\r
+  //              ^   ^\r
+  //              PCI slot & function on the parent, holding the bridge\r
+  //\r
+  // and the UEFI device path node is:\r
+  //\r
+  //   Pci(0x1E,0x0)\r
+  //\r
+  FirstNonBridge = 1;\r
+  Bridges[0]     = L'\0';\r
+  BridgesLen     = 0;\r
+  do {\r
+    UINT64  BridgeDevFun[2];\r
+    UINTN   BridgesFreeBytes;\r
+\r
+    if (!SubstringEq (OfwNode[FirstNonBridge].DriverName, "pci-bridge")) {\r
+      break;\r
+    }\r
+\r
+    BridgeDevFun[1] = 0;\r
+    NumEntries      = sizeof BridgeDevFun / sizeof BridgeDevFun[0];\r
+    if (ParseUnitAddressHexList (\r
+          OfwNode[FirstNonBridge].UnitAddress,\r
+          BridgeDevFun,\r
+          &NumEntries\r
+          ) != RETURN_SUCCESS)\r
+    {\r
+      return RETURN_UNSUPPORTED;\r
+    }\r
+\r
+    BridgesFreeBytes = sizeof Bridges - BridgesLen * sizeof Bridges[0];\r
+    Written          = UnicodeSPrintAsciiFormat (\r
+                         Bridges + BridgesLen,\r
+                         BridgesFreeBytes,\r
+                         "/Pci(0x%Lx,0x%Lx)",\r
+                         BridgeDevFun[0],\r
+                         BridgeDevFun[1]\r
+                         );\r
+    BridgesLen += Written;\r
+\r
+    //\r
+    // There's no way to differentiate between "completely used up without\r
+    // truncation" and "truncated", so treat the former as the latter.\r
+    //\r
+    if (BridgesLen + 1 == BRIDGE_TRANSLATION_OUTPUT_SIZE) {\r
+      return RETURN_UNSUPPORTED;\r
+    }\r
+\r
+    ++FirstNonBridge;\r
+  } while (FirstNonBridge < NumNodes);\r
+\r
+  if (FirstNonBridge == NumNodes) {\r
+    return RETURN_UNSUPPORTED;\r
+  }\r
+\r
+  //\r
+  // Parse the OFW nodes starting with the first non-bridge node.\r
+  //\r
   PciDevFun[1] = 0;\r
-  NumEntries = sizeof (PciDevFun) / sizeof (PciDevFun[0]);\r
+  NumEntries   = ARRAY_SIZE (PciDevFun);\r
   if (ParseUnitAddressHexList (\r
-        OfwNode[1].UnitAddress,\r
+        OfwNode[FirstNonBridge].UnitAddress,\r
         PciDevFun,\r
         &NumEntries\r
         ) != RETURN_SUCCESS\r
-      ) {\r
+      )\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
+  if ((NumNodes >= FirstNonBridge + 3) &&\r
+      SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "ide") &&\r
+      SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "drive") &&\r
+      SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")\r
+      )\r
+  {\r
     //\r
     // OpenFirmware device path (IDE disk, IDE CD-ROM):\r
     //\r
@@ -625,40 +773,93 @@ TranslatePciOfwNodes (
     //                                                ^\r
     //                                                fixed LUN\r
     //\r
-    UINT64 Secondary;\r
-    UINT64 Slave;\r
+    UINT64  Secondary;\r
+    UINT64  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
+    if ((ParseUnitAddressHexList (\r
+           OfwNode[FirstNonBridge + 1].UnitAddress,\r
+           &Secondary,\r
+           &NumEntries\r
+           ) != RETURN_SUCCESS) ||\r
+        (Secondary > 1) ||\r
+        (ParseUnitAddressHexList (\r
+           OfwNode[FirstNonBridge + 2].UnitAddress,\r
+           &Slave,\r
+           &NumEntries // reuse after previous single-element call\r
+           ) != RETURN_SUCCESS) ||\r
+        (Slave > 1)\r
+        )\r
+    {\r
+      return RETURN_UNSUPPORTED;\r
+    }\r
+\r
+    Written = UnicodeSPrintAsciiFormat (\r
+                Translated,\r
+                *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+                "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Ata(%a,%a,0x0)",\r
+                PciRoot,\r
+                Bridges,\r
+                PciDevFun[0],\r
+                PciDevFun[1],\r
+                Secondary ? "Secondary" : "Primary",\r
+                Slave ? "Slave" : "Master"\r
+                );\r
+  } else if ((NumNodes >= FirstNonBridge + 3) &&\r
+             SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "pci8086,2922") &&\r
+             SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "drive") &&\r
+             SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")\r
+             )\r
+  {\r
+    //\r
+    // OpenFirmware device path (Q35 SATA disk and CD-ROM):\r
+    //\r
+    //   /pci@i0cf8/pci8086,2922@1f,2/drive@1/disk@0\r
+    //        ^                  ^  ^       ^      ^\r
+    //        |                  |  |       |      device number (fixed 0)\r
+    //        |                  |  |       channel (port) number\r
+    //        |                  PCI slot & function holding SATA HBA\r
+    //        PCI root at system bus port, PIO\r
+    //\r
+    // UEFI device path:\r
+    //\r
+    //   PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x1,0xFFFF,0x0)\r
+    //                                   ^   ^      ^\r
+    //                                   |   |      LUN (always 0 on Q35)\r
+    //                                   |   port multiplier port number,\r
+    //                                   |   always 0xFFFF on Q35\r
+    //                                   channel (port) number\r
+    //\r
+    UINT64  Channel;\r
+\r
+    NumEntries = 1;\r
+    if (RETURN_ERROR (\r
+          ParseUnitAddressHexList (\r
+            OfwNode[FirstNonBridge + 1].UnitAddress,\r
+            &Channel,\r
+            &NumEntries\r
+            )\r
+          ))\r
+    {\r
       return RETURN_UNSUPPORTED;\r
     }\r
 \r
     Written = UnicodeSPrintAsciiFormat (\r
-      Translated,\r
-      *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
-      "PciRoot(0x0)/Pci(0x%Lx,0x%Lx)/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
+                Translated,\r
+                *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+                "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Sata(0x%Lx,0xFFFF,0x0)",\r
+                PciRoot,\r
+                Bridges,\r
+                PciDevFun[0],\r
+                PciDevFun[1],\r
+                Channel\r
+                );\r
+  } else if ((NumNodes >= FirstNonBridge + 3) &&\r
+             SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "isa") &&\r
+             SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "fdc") &&\r
+             SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "floppy")\r
+             )\r
+  {\r
     //\r
     // OpenFirmware device path (floppy disk):\r
     //\r
@@ -675,31 +876,35 @@ TranslatePciOfwNodes (
     //                                    ^\r
     //                                    ACPI UID\r
     //\r
-    UINT64 AcpiUid;\r
+    UINT64  AcpiUid;\r
 \r
     NumEntries = 1;\r
-    if (ParseUnitAddressHexList (\r
-          OfwNode[3].UnitAddress,\r
-          &AcpiUid,\r
-          &NumEntries\r
-          ) != RETURN_SUCCESS ||\r
-        AcpiUid > 1\r
-        ) {\r
+    if ((ParseUnitAddressHexList (\r
+           OfwNode[FirstNonBridge + 2].UnitAddress,\r
+           &AcpiUid,\r
+           &NumEntries\r
+           ) != RETURN_SUCCESS) ||\r
+        (AcpiUid > 1)\r
+        )\r
+    {\r
       return RETURN_UNSUPPORTED;\r
     }\r
 \r
     Written = UnicodeSPrintAsciiFormat (\r
-      Translated,\r
-      *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
-      "PciRoot(0x0)/Pci(0x%Lx,0x%Lx)/Floppy(0x%Lx)",\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
+                Translated,\r
+                *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+                "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Floppy(0x%Lx)",\r
+                PciRoot,\r
+                Bridges,\r
+                PciDevFun[0],\r
+                PciDevFun[1],\r
+                AcpiUid\r
+                );\r
+  } else if ((NumNodes >= FirstNonBridge + 2) &&\r
+             SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "scsi") &&\r
+             SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "disk")\r
+             )\r
+  {\r
     //\r
     // OpenFirmware device path (virtio-blk disk):\r
     //\r
@@ -712,21 +917,24 @@ TranslatePciOfwNodes (
     //\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
+    //   PciRoot(0x0)/Pci(0x6,0x0) -- if PCI function is 0 or absent\r
+    //   PciRoot(0x0)/Pci(0x6,0x3) -- 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%Lx,0x%Lx)/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
+                Translated,\r
+                *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+                "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",\r
+                PciRoot,\r
+                Bridges,\r
+                PciDevFun[0],\r
+                PciDevFun[1]\r
+                );\r
+  } else if ((NumNodes >= FirstNonBridge + 3) &&\r
+             SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "scsi") &&\r
+             SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "channel") &&\r
+             SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")\r
+             )\r
+  {\r
     //\r
     // OpenFirmware device path (virtio-scsi disk):\r
     //\r
@@ -745,28 +953,139 @@ TranslatePciOfwNodes (
     //   PciRoot(0x0)/Pci(0x7,0x3)/Scsi(0x2,0x3)\r
     //                                -- if PCI function is present and nonzero\r
     //\r
-    UINT64 TargetLun[2];\r
+    UINT64  TargetLun[2];\r
 \r
     TargetLun[1] = 0;\r
-    NumEntries = sizeof (TargetLun) / sizeof (TargetLun[0]);\r
+    NumEntries   = ARRAY_SIZE (TargetLun);\r
     if (ParseUnitAddressHexList (\r
-          OfwNode[3].UnitAddress,\r
+          OfwNode[FirstNonBridge + 2].UnitAddress,\r
           TargetLun,\r
           &NumEntries\r
           ) != RETURN_SUCCESS\r
-        ) {\r
+        )\r
+    {\r
+      return RETURN_UNSUPPORTED;\r
+    }\r
+\r
+    Written = UnicodeSPrintAsciiFormat (\r
+                Translated,\r
+                *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+                "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Scsi(0x%Lx,0x%Lx)",\r
+                PciRoot,\r
+                Bridges,\r
+                PciDevFun[0],\r
+                PciDevFun[1],\r
+                TargetLun[0],\r
+                TargetLun[1]\r
+                );\r
+  } else if ((NumNodes >= FirstNonBridge + 2) &&\r
+             SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "pci8086,5845") &&\r
+             SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "namespace")\r
+             )\r
+  {\r
+    //\r
+    // OpenFirmware device path (NVMe device):\r
+    //\r
+    //   /pci@i0cf8/pci8086,5845@6[,1]/namespace@1,0\r
+    //        ^                  ^  ^            ^ ^\r
+    //        |                  |  |            | Extended Unique Identifier\r
+    //        |                  |  |            | (EUI-64), big endian interp.\r
+    //        |                  |  |            namespace ID\r
+    //        |                  PCI slot & function holding NVMe controller\r
+    //        PCI root at system bus port, PIO\r
+    //\r
+    // UEFI device path:\r
+    //\r
+    //   PciRoot(0x0)/Pci(0x6,0x1)/NVMe(0x1,00-00-00-00-00-00-00-00)\r
+    //                                  ^   ^\r
+    //                                  |   octets of the EUI-64\r
+    //                                  |   in address order\r
+    //                                  namespace ID\r
+    //\r
+    UINT64  Namespace[2];\r
+    UINTN   RequiredEntries;\r
+    UINT8   *Eui64;\r
+\r
+    RequiredEntries = ARRAY_SIZE (Namespace);\r
+    NumEntries      = RequiredEntries;\r
+    if ((ParseUnitAddressHexList (\r
+           OfwNode[FirstNonBridge + 1].UnitAddress,\r
+           Namespace,\r
+           &NumEntries\r
+           ) != RETURN_SUCCESS) ||\r
+        (NumEntries != RequiredEntries) ||\r
+        (Namespace[0] == 0) ||\r
+        (Namespace[0] >= MAX_UINT32)\r
+        )\r
+    {\r
+      return RETURN_UNSUPPORTED;\r
+    }\r
+\r
+    Eui64   = (UINT8 *)&Namespace[1];\r
+    Written = UnicodeSPrintAsciiFormat (\r
+                Translated,\r
+                *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+                "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/"\r
+                "NVMe(0x%Lx,%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x)",\r
+                PciRoot,\r
+                Bridges,\r
+                PciDevFun[0],\r
+                PciDevFun[1],\r
+                Namespace[0],\r
+                Eui64[7],\r
+                Eui64[6],\r
+                Eui64[5],\r
+                Eui64[4],\r
+                Eui64[3],\r
+                Eui64[2],\r
+                Eui64[1],\r
+                Eui64[0]\r
+                );\r
+  } else if ((NumNodes >= FirstNonBridge + 2) &&\r
+             SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "usb") &&\r
+             SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "storage"))\r
+  {\r
+    //\r
+    // OpenFirmware device path (usb-storage device in XHCI port):\r
+    //\r
+    //   /pci@i0cf8/usb@3[,1]/storage@2/channel@0/disk@0,0\r
+    //        ^         ^  ^          ^         ^      ^ ^\r
+    //        |         |  |          |         fixed  fixed\r
+    //        |         |  |          XHCI port number, 1-based\r
+    //        |         |  PCI function corresponding to XHCI (optional)\r
+    //        |         PCI slot holding XHCI\r
+    //        PCI root at system bus port, PIO\r
+    //\r
+    // UEFI device path prefix:\r
+    //\r
+    //   PciRoot(0x0)/Pci(0x3,0x1)/USB(0x1,0x0)\r
+    //                        ^        ^\r
+    //                        |        XHCI port number in 0-based notation\r
+    //                        0x0 if PCI function is 0, or absent from OFW\r
+    //\r
+    RETURN_STATUS  ParseStatus;\r
+    UINT64         OneBasedXhciPort;\r
+\r
+    NumEntries  = 1;\r
+    ParseStatus = ParseUnitAddressHexList (\r
+                    OfwNode[FirstNonBridge + 1].UnitAddress,\r
+                    &OneBasedXhciPort,\r
+                    &NumEntries\r
+                    );\r
+    if (RETURN_ERROR (ParseStatus) || (OneBasedXhciPort == 0)) {\r
       return RETURN_UNSUPPORTED;\r
     }\r
 \r
     Written = UnicodeSPrintAsciiFormat (\r
-      Translated,\r
-      *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
-      "PciRoot(0x0)/Pci(0x%Lx,0x%Lx)/Scsi(0x%Lx,0x%Lx)",\r
-      PciDevFun[0],\r
-      PciDevFun[1],\r
-      TargetLun[0],\r
-      TargetLun[1]\r
-      );\r
+                Translated,\r
+                *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+                "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/USB(0x%Lx,0x0)",\r
+                PciRoot,\r
+                Bridges,\r
+                PciDevFun[0],\r
+                PciDevFun[1],\r
+                OneBasedXhciPort - 1\r
+                );\r
   } else {\r
     //\r
     // Generic OpenFirmware device path for PCI devices:\r
@@ -782,12 +1101,14 @@ TranslatePciOfwNodes (
     //   PciRoot(0x0)/Pci(0x3,0x2)\r
     //\r
     Written = UnicodeSPrintAsciiFormat (\r
-      Translated,\r
-      *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
-      "PciRoot(0x0)/Pci(0x%Lx,0x%Lx)",\r
-      PciDevFun[0],\r
-      PciDevFun[1]\r
-      );\r
+                Translated,\r
+                *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+                "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",\r
+                PciRoot,\r
+                Bridges,\r
+                PciDevFun[0],\r
+                PciDevFun[1]\r
+                );\r
   }\r
 \r
   //\r
@@ -803,17 +1124,15 @@ TranslatePciOfwNodes (
   return RETURN_BUFFER_TOO_SMALL;\r
 }\r
 \r
-\r
 //\r
 // A type providing easy raw access to the base address of a virtio-mmio\r
 // transport.\r
 //\r
 typedef union {\r
-  UINT64 Uint64;\r
-  UINT8  Raw[8];\r
+  UINT64    Uint64;\r
+  UINT8     Raw[8];\r
 } VIRTIO_MMIO_BASE_ADDRESS;\r
 \r
-\r
 /**\r
 \r
   Translate an MMIO-like array of OpenFirmware device nodes to a UEFI device\r
@@ -849,43 +1168,56 @@ typedef union {
 STATIC\r
 RETURN_STATUS\r
 TranslateMmioOfwNodes (\r
-  IN      CONST OFW_NODE *OfwNode,\r
-  IN      UINTN          NumNodes,\r
-  OUT     CHAR16         *Translated,\r
-  IN OUT  UINTN          *TranslatedSize\r
+  IN      CONST OFW_NODE  *OfwNode,\r
+  IN      UINTN           NumNodes,\r
+  OUT     CHAR16          *Translated,\r
+  IN OUT  UINTN           *TranslatedSize\r
   )\r
 {\r
-  VIRTIO_MMIO_BASE_ADDRESS VirtioMmioBase;\r
-  CHAR16                   VenHwString[60 + 1];\r
-  UINTN                    NumEntries;\r
-  UINTN                    Written;\r
+  VIRTIO_MMIO_BASE_ADDRESS  VirtioMmioBase;\r
+  CHAR16                    VenHwString[60 + 1];\r
+  UINTN                     NumEntries;\r
+  UINTN                     Written;\r
 \r
   //\r
   // Get the base address of the virtio-mmio transport.\r
   //\r
-  if (NumNodes < REQUIRED_MMIO_OFW_NODES ||\r
+  if ((NumNodes < REQUIRED_MMIO_OFW_NODES) ||\r
       !SubstringEq (OfwNode[0].DriverName, "virtio-mmio")\r
-      ) {\r
+      )\r
+  {\r
     return RETURN_UNSUPPORTED;\r
   }\r
+\r
   NumEntries = 1;\r
   if (ParseUnitAddressHexList (\r
         OfwNode[0].UnitAddress,\r
         &VirtioMmioBase.Uint64,\r
         &NumEntries\r
         ) != RETURN_SUCCESS\r
-      ) {\r
+      )\r
+  {\r
     return RETURN_UNSUPPORTED;\r
   }\r
 \r
-  UnicodeSPrintAsciiFormat (VenHwString, sizeof VenHwString,\r
-    "VenHw(%g,%02X%02X%02X%02X%02X%02X%02X%02X)", &gVirtioMmioTransportGuid,\r
-    VirtioMmioBase.Raw[0], VirtioMmioBase.Raw[1], VirtioMmioBase.Raw[2],\r
-    VirtioMmioBase.Raw[3], VirtioMmioBase.Raw[4], VirtioMmioBase.Raw[5],\r
-    VirtioMmioBase.Raw[6], VirtioMmioBase.Raw[7]);\r
-\r
-  if (NumNodes >= 2 &&\r
-      SubstringEq (OfwNode[1].DriverName, "disk")) {\r
+  UnicodeSPrintAsciiFormat (\r
+    VenHwString,\r
+    sizeof VenHwString,\r
+    "VenHw(%g,%02X%02X%02X%02X%02X%02X%02X%02X)",\r
+    &gVirtioMmioTransportGuid,\r
+    VirtioMmioBase.Raw[0],\r
+    VirtioMmioBase.Raw[1],\r
+    VirtioMmioBase.Raw[2],\r
+    VirtioMmioBase.Raw[3],\r
+    VirtioMmioBase.Raw[4],\r
+    VirtioMmioBase.Raw[5],\r
+    VirtioMmioBase.Raw[6],\r
+    VirtioMmioBase.Raw[7]\r
+    );\r
+\r
+  if ((NumNodes >= 2) &&\r
+      SubstringEq (OfwNode[1].DriverName, "disk"))\r
+  {\r
     //\r
     // OpenFirmware device path (virtio-blk disk):\r
     //\r
@@ -896,17 +1228,18 @@ TranslateMmioOfwNodes (
     //\r
     // UEFI device path prefix:\r
     //\r
-    //   <VenHwString>/HD(\r
+    //   <VenHwString>\r
     //\r
     Written = UnicodeSPrintAsciiFormat (\r
                 Translated,\r
                 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
-                "%s/HD(",\r
+                "%s",\r
                 VenHwString\r
                 );\r
-  } else if (NumNodes >= 3 &&\r
+  } else if ((NumNodes >= 3) &&\r
              SubstringEq (OfwNode[1].DriverName, "channel") &&\r
-             SubstringEq (OfwNode[2].DriverName, "disk")) {\r
+             SubstringEq (OfwNode[2].DriverName, "disk"))\r
+  {\r
     //\r
     // OpenFirmware device path (virtio-scsi disk):\r
     //\r
@@ -921,16 +1254,17 @@ TranslateMmioOfwNodes (
     //\r
     //   <VenHwString>/Scsi(0x2,0x3)\r
     //\r
-    UINT64 TargetLun[2];\r
+    UINT64  TargetLun[2];\r
 \r
     TargetLun[1] = 0;\r
-    NumEntries = sizeof (TargetLun) / sizeof (TargetLun[0]);\r
+    NumEntries   = ARRAY_SIZE (TargetLun);\r
     if (ParseUnitAddressHexList (\r
           OfwNode[2].UnitAddress,\r
           TargetLun,\r
           &NumEntries\r
           ) != RETURN_SUCCESS\r
-        ) {\r
+        )\r
+    {\r
       return RETURN_UNSUPPORTED;\r
     }\r
 \r
@@ -942,8 +1276,9 @@ TranslateMmioOfwNodes (
                 TargetLun[0],\r
                 TargetLun[1]\r
                 );\r
-  } else if (NumNodes >= 2 &&\r
-             SubstringEq (OfwNode[1].DriverName, "ethernet-phy")) {\r
+  } else if ((NumNodes >= 2) &&\r
+             SubstringEq (OfwNode[1].DriverName, "ethernet-phy"))\r
+  {\r
     //\r
     // OpenFirmware device path (virtio-net NIC):\r
     //\r
@@ -952,14 +1287,14 @@ TranslateMmioOfwNodes (
     //                |                             fixed\r
     //                base address of virtio-mmio register block\r
     //\r
-    // UEFI device path prefix (dependent on presence of nonzero PCI function):\r
+    // UEFI device path prefix:\r
     //\r
-    //   <VenHwString>/MAC(\r
+    //   <VenHwString>\r
     //\r
     Written = UnicodeSPrintAsciiFormat (\r
                 Translated,\r
                 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
-                "%s/MAC(",\r
+                "%s",\r
                 VenHwString\r
                 );\r
   } else {\r
@@ -979,7 +1314,6 @@ TranslateMmioOfwNodes (
   return RETURN_BUFFER_TOO_SMALL;\r
 }\r
 \r
-\r
 /**\r
 \r
   Translate an array of OpenFirmware device nodes to a UEFI device path\r
@@ -991,6 +1325,11 @@ TranslateMmioOfwNodes (
 \r
   @param[in]     NumNodes        Number of elements in OfwNode.\r
 \r
+  @param[in]     ExtraPciRoots   An EXTRA_ROOT_BUS_MAP object created with\r
+                                 CreateExtraRootBusMap(), to be used for\r
+                                 translating positions of extra root buses to\r
+                                 bus numbers.\r
+\r
   @param[out]    Translated      Destination array receiving the UEFI path\r
                                  fragment, allocated by the caller. If the\r
                                  return value differs from RETURN_SUCCESS, its\r
@@ -1011,29 +1350,46 @@ TranslateMmioOfwNodes (
   @retval RETURN_UNSUPPORTED       The array of OpenFirmware device nodes can't\r
                                    be translated in the current implementation.\r
 \r
+  @retval RETURN_PROTOCOL_ERROR    The array of OpenFirmware device nodes has\r
+                                   been (partially) recognized, but it contains\r
+                                   a logic error / doesn't match system state.\r
+\r
 **/\r
 STATIC\r
 RETURN_STATUS\r
 TranslateOfwNodes (\r
-  IN      CONST OFW_NODE *OfwNode,\r
-  IN      UINTN          NumNodes,\r
-  OUT     CHAR16         *Translated,\r
-  IN OUT  UINTN          *TranslatedSize\r
+  IN      CONST OFW_NODE            *OfwNode,\r
+  IN      UINTN                     NumNodes,\r
+  IN      CONST EXTRA_ROOT_BUS_MAP  *ExtraPciRoots,\r
+  OUT     CHAR16                    *Translated,\r
+  IN OUT  UINTN                     *TranslatedSize\r
   )\r
 {\r
-  RETURN_STATUS Status;\r
+  RETURN_STATUS  Status;\r
 \r
   Status = RETURN_UNSUPPORTED;\r
 \r
   if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {\r
-    Status = TranslatePciOfwNodes (OfwNode, NumNodes, Translated,\r
-               TranslatedSize);\r
+    Status = TranslatePciOfwNodes (\r
+               OfwNode,\r
+               NumNodes,\r
+               ExtraPciRoots,\r
+               Translated,\r
+               TranslatedSize\r
+               );\r
   }\r
-  if (Status == RETURN_UNSUPPORTED &&\r
-      FeaturePcdGet (PcdQemuBootOrderMmioTranslation)) {\r
-    Status = TranslateMmioOfwNodes (OfwNode, NumNodes, Translated,\r
-               TranslatedSize);\r
+\r
+  if ((Status == RETURN_UNSUPPORTED) &&\r
+      FeaturePcdGet (PcdQemuBootOrderMmioTranslation))\r
+  {\r
+    Status = TranslateMmioOfwNodes (\r
+               OfwNode,\r
+               NumNodes,\r
+               Translated,\r
+               TranslatedSize\r
+               );\r
   }\r
+\r
   return Status;\r
 }\r
 \r
@@ -1051,6 +1407,11 @@ TranslateOfwNodes (
                                  characters. In other error cases, it points to\r
                                  the byte that caused the error.\r
 \r
+  @param[in]     ExtraPciRoots   An EXTRA_ROOT_BUS_MAP object created with\r
+                                 CreateExtraRootBusMap(), to be used for\r
+                                 translating positions of extra root buses to\r
+                                 bus numbers.\r
+\r
   @param[out]    Translated      Destination array receiving the UEFI path\r
                                  fragment, allocated by the caller. If the\r
                                  return value differs from RETURN_SUCCESS, its\r
@@ -1076,6 +1437,12 @@ TranslateOfwNodes (
                                     the current implementation. Further calls\r
                                     to this function are possible.\r
 \r
+  @retval RETURN_PROTOCOL_ERROR     The OpenFirmware device path has been\r
+                                    (partially) recognized, but it contains a\r
+                                    logic error / doesn't match system state.\r
+                                    Further calls to this function are\r
+                                    possible.\r
+\r
   @retval RETURN_NOT_FOUND          Translation terminated. On input, *Ptr was\r
                                     pointing to the empty string or "HALT". On\r
                                     output, *Ptr points to the empty string\r
@@ -1088,21 +1455,22 @@ TranslateOfwNodes (
 STATIC\r
 RETURN_STATUS\r
 TranslateOfwPath (\r
-  IN OUT  CONST CHAR8 **Ptr,\r
-  OUT     CHAR16      *Translated,\r
-  IN OUT  UINTN       *TranslatedSize\r
+  IN OUT  CONST CHAR8               **Ptr,\r
+  IN      CONST EXTRA_ROOT_BUS_MAP  *ExtraPciRoots,\r
+  OUT     CHAR16                    *Translated,\r
+  IN OUT  UINTN                     *TranslatedSize\r
   )\r
 {\r
-  UINTN         NumNodes;\r
-  RETURN_STATUS Status;\r
-  OFW_NODE      Node[EXAMINED_OFW_NODES];\r
-  BOOLEAN       IsFinal;\r
-  OFW_NODE      Skip;\r
+  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
+  IsFinal  = FALSE;\r
   NumNodes = 0;\r
   if (AsciiStrCmp (*Ptr, "HALT") == 0) {\r
-    *Ptr += 4;\r
+    *Ptr  += 4;\r
     Status = RETURN_NOT_FOUND;\r
   } else {\r
     Status = ParseOfwNode (Ptr, &Node[NumNodes], &IsFinal);\r
@@ -1123,42 +1491,343 @@ TranslateOfwPath (
   }\r
 \r
   switch (Status) {\r
-  case RETURN_SUCCESS:\r
-    ++NumNodes;\r
-    break;\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
+    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
+    default:\r
+      ASSERT (0);\r
   }\r
 \r
   Status = TranslateOfwNodes (\r
              Node,\r
              NumNodes < EXAMINED_OFW_NODES ? NumNodes : EXAMINED_OFW_NODES,\r
+             ExtraPciRoots,\r
              Translated,\r
-             TranslatedSize);\r
+             TranslatedSize\r
+             );\r
   switch (Status) {\r
-  case RETURN_SUCCESS:\r
-    DEBUG ((DEBUG_VERBOSE, "%a: success: \"%s\"\n", __FUNCTION__, Translated));\r
-    break;\r
+    case RETURN_SUCCESS:\r
+      DEBUG ((DEBUG_VERBOSE, "%a: success: \"%s\"\n", __FUNCTION__, Translated));\r
+      break;\r
+\r
+    case RETURN_BUFFER_TOO_SMALL:\r
+      DEBUG ((DEBUG_VERBOSE, "%a: buffer too small\n", __FUNCTION__));\r
+      break;\r
+\r
+    case RETURN_UNSUPPORTED:\r
+      DEBUG ((DEBUG_VERBOSE, "%a: unsupported\n", __FUNCTION__));\r
+      break;\r
+\r
+    case RETURN_PROTOCOL_ERROR:\r
+      DEBUG ((\r
+        DEBUG_VERBOSE,\r
+        "%a: logic error / system state mismatch\n",\r
+        __FUNCTION__\r
+        ));\r
+      break;\r
+\r
+    default:\r
+      ASSERT (0);\r
+  }\r
 \r
-  case RETURN_BUFFER_TOO_SMALL:\r
-    DEBUG ((DEBUG_VERBOSE, "%a: buffer too small\n", __FUNCTION__));\r
-    break;\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Connect devices based on the boot order 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. Connect the\r
+  devices identified by the UEFI devpath prefixes as narrowly as possible, then\r
+  connect all their child devices, recursively.\r
 \r
-  case RETURN_UNSUPPORTED:\r
-    DEBUG ((DEBUG_VERBOSE, "%a: unsupported\n", __FUNCTION__));\r
-    break;\r
+  If this function fails, then platform BDS should fall back to\r
+  EfiBootManagerConnectAll(), or some other method for connecting any expected\r
+  boot devices.\r
 \r
-  default:\r
-    ASSERT (0);\r
+  @retval RETURN_SUCCESS            The "bootorder" fw_cfg file has been\r
+                                    parsed, and the referenced device-subtrees\r
+                                    have been connected.\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.\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                           Error statuses propagated from underlying\r
+                                    functions.\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+ConnectDevicesFromQemu (\r
+  VOID\r
+  )\r
+{\r
+  RETURN_STATUS         Status;\r
+  FIRMWARE_CONFIG_ITEM  FwCfgItem;\r
+  UINTN                 FwCfgSize;\r
+  CHAR8                 *FwCfg;\r
+  EFI_STATUS            EfiStatus;\r
+  EXTRA_ROOT_BUS_MAP    *ExtraPciRoots;\r
+  CONST CHAR8           *FwCfgPtr;\r
+  UINTN                 NumConnected;\r
+  UINTN                 TranslatedSize;\r
+  CHAR16                Translated[TRANSLATION_OUTPUT_SIZE];\r
+\r
+  Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);\r
+  if (RETURN_ERROR (Status)) {\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 FreeFwCfg;\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
+\r
+  if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {\r
+    EfiStatus = CreateExtraRootBusMap (&ExtraPciRoots);\r
+    if (EFI_ERROR (EfiStatus)) {\r
+      Status = (RETURN_STATUS)EfiStatus;\r
+      goto FreeFwCfg;\r
+    }\r
+  } else {\r
+    ExtraPciRoots = NULL;\r
+  }\r
+\r
+  //\r
+  // Translate each OpenFirmware path to a UEFI devpath prefix.\r
+  //\r
+  FwCfgPtr       = FwCfg;\r
+  NumConnected   = 0;\r
+  TranslatedSize = ARRAY_SIZE (Translated);\r
+  Status         = TranslateOfwPath (\r
+                     &FwCfgPtr,\r
+                     ExtraPciRoots,\r
+                     Translated,\r
+                     &TranslatedSize\r
+                     );\r
+  while (!RETURN_ERROR (Status)) {\r
+    EFI_DEVICE_PATH_PROTOCOL  *DevicePath;\r
+    EFI_HANDLE                Controller;\r
+\r
+    //\r
+    // Convert the UEFI devpath prefix to binary representation.\r
+    //\r
+    ASSERT (Translated[TranslatedSize] == L'\0');\r
+    DevicePath = ConvertTextToDevicePath (Translated);\r
+    if (DevicePath == NULL) {\r
+      Status = RETURN_OUT_OF_RESOURCES;\r
+      goto FreeExtraPciRoots;\r
+    }\r
+\r
+    //\r
+    // Advance along DevicePath, connecting the nodes individually, and asking\r
+    // drivers not to produce sibling nodes. Retrieve the controller handle\r
+    // associated with the full DevicePath -- this is the device that QEMU's\r
+    // OFW devpath refers to.\r
+    //\r
+    EfiStatus = EfiBootManagerConnectDevicePath (DevicePath, &Controller);\r
+    FreePool (DevicePath);\r
+    if (EFI_ERROR (EfiStatus)) {\r
+      Status = (RETURN_STATUS)EfiStatus;\r
+      goto FreeExtraPciRoots;\r
+    }\r
+\r
+    //\r
+    // Because QEMU's OFW devpaths have lesser expressive power than UEFI\r
+    // devpaths (i.e., DevicePath is considered a prefix), connect the tree\r
+    // rooted at Controller, recursively. If no children are produced\r
+    // (EFI_NOT_FOUND), that's OK.\r
+    //\r
+    EfiStatus = gBS->ConnectController (Controller, NULL, NULL, TRUE);\r
+    if (EFI_ERROR (EfiStatus) && (EfiStatus != EFI_NOT_FOUND)) {\r
+      Status = (RETURN_STATUS)EfiStatus;\r
+      goto FreeExtraPciRoots;\r
+    }\r
+\r
+    ++NumConnected;\r
+    //\r
+    // Move to the next OFW devpath.\r
+    //\r
+    TranslatedSize = ARRAY_SIZE (Translated);\r
+    Status         = TranslateOfwPath (\r
+                       &FwCfgPtr,\r
+                       ExtraPciRoots,\r
+                       Translated,\r
+                       &TranslatedSize\r
+                       );\r
+  }\r
+\r
+  if ((Status == RETURN_NOT_FOUND) && (NumConnected > 0)) {\r
+    DEBUG ((\r
+      DEBUG_INFO,\r
+      "%a: %Lu OpenFirmware device path(s) connected\n",\r
+      __FUNCTION__,\r
+      (UINT64)NumConnected\r
+      ));\r
+    Status = RETURN_SUCCESS;\r
+  }\r
+\r
+FreeExtraPciRoots:\r
+  if (ExtraPciRoots != NULL) {\r
+    DestroyExtraRootBusMap (ExtraPciRoots);\r
   }\r
+\r
+FreeFwCfg:\r
+  FreePool (FwCfg);\r
+\r
   return Status;\r
 }\r
 \r
+/**\r
+  Write qemu boot order to uefi variables.\r
+\r
+  Attempt to retrieve the "bootorder" fw_cfg file from QEMU. Translate\r
+  the OpenFirmware device paths therein to UEFI device path fragments.\r
+\r
+  On Success store the device path in QemuBootOrderNNNN variables.\r
+**/\r
+VOID\r
+EFIAPI\r
+StoreQemuBootOrder (\r
+  VOID\r
+  )\r
+{\r
+  RETURN_STATUS         Status;\r
+  FIRMWARE_CONFIG_ITEM  FwCfgItem;\r
+  UINTN                 FwCfgSize;\r
+  CHAR8                 *FwCfg;\r
+  EFI_STATUS            EfiStatus;\r
+  EXTRA_ROOT_BUS_MAP    *ExtraPciRoots;\r
+  CONST CHAR8           *FwCfgPtr;\r
+  UINTN                 TranslatedSize;\r
+  CHAR16                Translated[TRANSLATION_OUTPUT_SIZE];\r
+  UINTN                 VariableIndex = 0;\r
+  CHAR16                VariableName[20];\r
+\r
+  Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);\r
+  if (RETURN_ERROR (Status)) {\r
+    return;\r
+  }\r
+\r
+  if (FwCfgSize == 0) {\r
+    return;\r
+  }\r
+\r
+  FwCfg = AllocatePool (FwCfgSize);\r
+  if (FwCfg == NULL) {\r
+    return;\r
+  }\r
+\r
+  QemuFwCfgSelectItem (FwCfgItem);\r
+  QemuFwCfgReadBytes (FwCfgSize, FwCfg);\r
+  if (FwCfg[FwCfgSize - 1] != '\0') {\r
+    Status = RETURN_INVALID_PARAMETER;\r
+    goto FreeFwCfg;\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
+\r
+  if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {\r
+    EfiStatus = CreateExtraRootBusMap (&ExtraPciRoots);\r
+    if (EFI_ERROR (EfiStatus)) {\r
+      Status = (RETURN_STATUS)EfiStatus;\r
+      goto FreeFwCfg;\r
+    }\r
+  } else {\r
+    ExtraPciRoots = NULL;\r
+  }\r
+\r
+  //\r
+  // Translate each OpenFirmware path to a UEFI devpath prefix.\r
+  //\r
+  FwCfgPtr       = FwCfg;\r
+  TranslatedSize = ARRAY_SIZE (Translated);\r
+  Status         = TranslateOfwPath (\r
+                     &FwCfgPtr,\r
+                     ExtraPciRoots,\r
+                     Translated,\r
+                     &TranslatedSize\r
+                     );\r
+  while (Status == EFI_SUCCESS ||\r
+         Status == EFI_UNSUPPORTED)\r
+  {\r
+    if (Status == EFI_SUCCESS) {\r
+      EFI_DEVICE_PATH_PROTOCOL  *DevicePath;\r
+\r
+      //\r
+      // Convert the UEFI devpath prefix to binary representation.\r
+      //\r
+      ASSERT (Translated[TranslatedSize] == L'\0');\r
+      DevicePath = ConvertTextToDevicePath (Translated);\r
+      if (DevicePath == NULL) {\r
+        Status = RETURN_OUT_OF_RESOURCES;\r
+        goto FreeExtraPciRoots;\r
+      }\r
+\r
+      UnicodeSPrint (\r
+        VariableName,\r
+        sizeof (VariableName),\r
+        L"QemuBootOrder%04d",\r
+        VariableIndex++\r
+        );\r
+      DEBUG ((DEBUG_INFO, "%a: %s = %s\n", __FUNCTION__, VariableName, Translated));\r
+      gRT->SetVariable (\r
+             VariableName,\r
+             &gQemuBootOrderGuid,\r
+             EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
+             GetDevicePathSize (DevicePath),\r
+             DevicePath\r
+             );\r
+      FreePool (DevicePath);\r
+    }\r
+\r
+    //\r
+    // Move to the next OFW devpath.\r
+    //\r
+    TranslatedSize = ARRAY_SIZE (Translated);\r
+    Status         = TranslateOfwPath (\r
+                       &FwCfgPtr,\r
+                       ExtraPciRoots,\r
+                       Translated,\r
+                       &TranslatedSize\r
+                       );\r
+  }\r
+\r
+FreeExtraPciRoots:\r
+  if (ExtraPciRoots != NULL) {\r
+    DestroyExtraRootBusMap (ExtraPciRoots);\r
+  }\r
+\r
+FreeFwCfg:\r
+  FreePool (FwCfg);\r
+}\r
 \r
 /**\r
 \r
@@ -1186,13 +1855,19 @@ TranslateOfwPath (
 STATIC\r
 BOOLEAN\r
 Match (\r
-  IN  CONST CHAR16                           *Translated,\r
-  IN  UINTN                                  TranslatedLength,\r
-  IN  CONST EFI_DEVICE_PATH_PROTOCOL         *DevicePath\r
+  IN  CONST CHAR16              *Translated,\r
+  IN  UINTN                     TranslatedLength,\r
+  IN  EFI_DEVICE_PATH_PROTOCOL  *DevicePath\r
   )\r
 {\r
-  CHAR16  *Converted;\r
-  BOOLEAN Result;\r
+  CHAR16                    *Converted;\r
+  BOOLEAN                   Result;\r
+  VOID                      *FileBuffer;\r
+  UINTN                     FileSize;\r
+  EFI_DEVICE_PATH_PROTOCOL  *AbsDevicePath;\r
+  CHAR16                    *AbsConverted;\r
+  BOOLEAN                   Shortform;\r
+  EFI_DEVICE_PATH_PROTOCOL  *Node;\r
 \r
   Converted = ConvertDevicePathToText (\r
                 DevicePath,\r
@@ -1203,30 +1878,75 @@ Match (
     return FALSE;\r
   }\r
 \r
+  Result    = FALSE;\r
+  Shortform = FALSE;\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
+  // Expand the short-form device path to full device path\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
+  if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&\r
+      (DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP))\r
+  {\r
+    //\r
+    // Harddrive shortform device path\r
+    //\r
+    Shortform = TRUE;\r
+  } else if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&\r
+             (DevicePathSubType (DevicePath) == MEDIA_FILEPATH_DP))\r
+  {\r
+    //\r
+    // File-path shortform device path\r
+    //\r
+    Shortform = TRUE;\r
+  } else if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&\r
+             (DevicePathSubType (DevicePath) == MSG_URI_DP))\r
+  {\r
+    //\r
+    // URI shortform device path\r
+    //\r
+    Shortform = TRUE;\r
+  } else {\r
+    for ( Node = DevicePath\r
+          ; !IsDevicePathEnd (Node)\r
+          ; Node = NextDevicePathNode (Node)\r
+          )\r
+    {\r
+      if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) &&\r
+          ((DevicePathSubType (Node) == MSG_USB_CLASS_DP) ||\r
+           (DevicePathSubType (Node) == MSG_USB_WWID_DP)))\r
+      {\r
+        Shortform = TRUE;\r
+        break;\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
+  // Attempt to expand any relative UEFI device path to\r
+  // an absolute device path first.\r
+  //\r
+  if (Shortform) {\r
+    FileBuffer = EfiBootManagerGetLoadOptionBuffer (\r
+                   DevicePath,\r
+                   &AbsDevicePath,\r
+                   &FileSize\r
+                   );\r
+    if (FileBuffer == NULL) {\r
       goto Exit;\r
     }\r
+\r
+    FreePool (FileBuffer);\r
     AbsConverted = ConvertDevicePathToText (AbsDevicePath, FALSE, FALSE);\r
+    FreePool (AbsDevicePath);\r
     if (AbsConverted == NULL) {\r
       goto Exit;\r
     }\r
-    DEBUG ((DEBUG_VERBOSE,\r
+\r
+    DEBUG ((\r
+      DEBUG_VERBOSE,\r
       "%a: expanded relative device path \"%s\" for prefix matching\n",\r
-      __FUNCTION__, Converted));\r
+      __FUNCTION__,\r
+      Converted\r
+      ));\r
     FreePool (Converted);\r
     Converted = AbsConverted;\r
   }\r
@@ -1247,7 +1967,6 @@ Exit:
   return Result;\r
 }\r
 \r
-\r
 /**\r
   Append some of the unselected active boot options to the boot order.\r
 \r
@@ -1277,27 +1996,27 @@ Exit:
 STATIC\r
 RETURN_STATUS\r
 BootOrderComplete (\r
-  IN OUT  BOOT_ORDER    *BootOrder,\r
-  IN OUT  ACTIVE_OPTION *ActiveOption,\r
-  IN      UINTN         ActiveCount\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
+  RETURN_STATUS  Status;\r
+  UINTN          Idx;\r
 \r
   Status = RETURN_SUCCESS;\r
-  Idx = 0;\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
+      CONST EFI_BOOT_MANAGER_LOAD_OPTION  *Current;\r
+      CONST EFI_DEVICE_PATH_PROTOCOL      *FirstNode;\r
 \r
-      Current = ActiveOption[Idx].BootOption;\r
-      FirstNode = Current->DevicePath;\r
+      Current   = ActiveOption[Idx].BootOption;\r
+      FirstNode = Current->FilePath;\r
       if (FirstNode != NULL) {\r
-        CHAR16        *Converted;\r
-        STATIC CHAR16 ConvFallBack[] = L"<unable to convert>";\r
-        BOOLEAN       Keep;\r
+        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
@@ -1305,19 +2024,22 @@ BootOrderComplete (
         }\r
 \r
         Keep = TRUE;\r
-        if (DevicePathType(FirstNode) == MEDIA_DEVICE_PATH &&\r
-            DevicePathSubType(FirstNode) == MEDIA_HARDDRIVE_DP) {\r
+        if ((DevicePathType (FirstNode) == MEDIA_DEVICE_PATH) &&\r
+            (DevicePathSubType (FirstNode) == MEDIA_HARDDRIVE_DP))\r
+        {\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
+        } else if ((DevicePathType (FirstNode) == ACPI_DEVICE_PATH) &&\r
+                   (DevicePathSubType (FirstNode) == ACPI_DP))\r
+        {\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
             //\r
             // drop PciRoot() if we enabled the user to select PCI-like boot\r
             // options, by providing translation for such OFW device path\r
@@ -1325,9 +2047,10 @@ BootOrderComplete (
             //\r
             Keep = !FeaturePcdGet (PcdQemuBootOrderPciTranslation);\r
           }\r
-        } else if (DevicePathType(FirstNode) == HARDWARE_DEVICE_PATH &&\r
-                   DevicePathSubType(FirstNode) == HW_VENDOR_DP) {\r
-          VENDOR_DEVICE_PATH *VenHw;\r
+        } else if ((DevicePathType (FirstNode) == HARDWARE_DEVICE_PATH) &&\r
+                   (DevicePathSubType (FirstNode) == HW_VENDOR_DP))\r
+        {\r
+          VENDOR_DEVICE_PATH  *VenHw;\r
 \r
           VenHw = (VENDOR_DEVICE_PATH *)FirstNode;\r
           if (CompareGuid (&VenHw->Guid, &gVirtioMmioTransportGuid)) {\r
@@ -1342,12 +2065,20 @@ BootOrderComplete (
         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
+            DEBUG ((\r
+              DEBUG_VERBOSE,\r
+              "%a: keeping \"%s\"\n",\r
+              __FUNCTION__,\r
+              Converted\r
+              ));\r
           }\r
         } else {\r
-          DEBUG ((DEBUG_VERBOSE, "%a: dropping \"%s\"\n", __FUNCTION__,\r
-            Converted));\r
+          DEBUG ((\r
+            DEBUG_VERBOSE,\r
+            "%a: dropping \"%s\"\n",\r
+            __FUNCTION__,\r
+            Converted\r
+            ));\r
         }\r
 \r
         if (Converted != ConvFallBack) {\r
@@ -1355,12 +2086,13 @@ BootOrderComplete (
         }\r
       }\r
     }\r
+\r
     ++Idx;\r
   }\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
@@ -1375,24 +2107,30 @@ BootOrderComplete (
 STATIC\r
 VOID\r
 PruneBootVariables (\r
-  IN  CONST ACTIVE_OPTION *ActiveOption,\r
-  IN  UINTN               ActiveCount\r
+  IN  CONST ACTIVE_OPTION  *ActiveOption,\r
+  IN  UINTN                ActiveCount\r
   )\r
 {\r
-  UINTN Idx;\r
+  UINTN  Idx;\r
 \r
   for (Idx = 0; Idx < ActiveCount; ++Idx) {\r
     if (!ActiveOption[Idx].Appended) {\r
-      CHAR16 VariableName[9];\r
+      CHAR16  VariableName[9];\r
 \r
-      UnicodeSPrintAsciiFormat (VariableName, sizeof VariableName, "Boot%04x",\r
-        ActiveOption[Idx].BootOption->BootCurrent);\r
+      UnicodeSPrintAsciiFormat (\r
+        VariableName,\r
+        sizeof VariableName,\r
+        "Boot%04x",\r
+        ActiveOption[Idx].BootOption->OptionNumber\r
+        );\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
+      gRT->SetVariable (\r
+             VariableName,\r
+             &gEfiGlobalVariableGuid,\r
              0,   // Attributes, 0 means deletion\r
              0,   // DataSize, 0 means deletion\r
              NULL // Data\r
@@ -1401,19 +2139,17 @@ PruneBootVariables (
   }\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
+  translated fragments against the current list of boot options, and rewrite\r
+  the BootOrder NvVar so that it corresponds to the order described in fw_cfg.\r
 \r
+  Platform BDS should call this function after connecting any expected boot\r
+  devices and calling EfiBootManagerRefreshAllBootOption ().\r
 \r
   @retval RETURN_SUCCESS            BootOrder NvVar rewritten.\r
 \r
@@ -1432,22 +2168,27 @@ PruneBootVariables (
 \r
 **/\r
 RETURN_STATUS\r
+EFIAPI\r
 SetBootOrderFromQemu (\r
-  IN  CONST LIST_ENTRY *BootOptionList\r
+  VOID\r
   )\r
 {\r
-  RETURN_STATUS                    Status;\r
-  FIRMWARE_CONFIG_ITEM             FwCfgItem;\r
-  UINTN                            FwCfgSize;\r
-  CHAR8                            *FwCfg;\r
-  CONST CHAR8                      *FwCfgPtr;\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
+  BOOT_ORDER     BootOrder;\r
+  ACTIVE_OPTION  *ActiveOption;\r
+  UINTN          ActiveCount;\r
 \r
-  UINTN                            TranslatedSize;\r
-  CHAR16                           Translated[TRANSLATION_OUTPUT_SIZE];\r
+  EXTRA_ROOT_BUS_MAP  *ExtraPciRoots;\r
+\r
+  UINTN                         TranslatedSize;\r
+  CHAR16                        Translated[TRANSLATION_OUTPUT_SIZE];\r
+  EFI_BOOT_MANAGER_LOAD_OPTION  *BootOptions;\r
+  UINTN                         BootOptionCount;\r
 \r
   Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);\r
   if (Status != RETURN_SUCCESS) {\r
@@ -1477,57 +2218,93 @@ SetBootOrderFromQemu (
 \r
   BootOrder.Produced  = 0;\r
   BootOrder.Allocated = 1;\r
-  BootOrder.Data = AllocatePool (\r
-                     BootOrder.Allocated * sizeof (*BootOrder.Data)\r
-                     );\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
+  BootOptions = EfiBootManagerGetLoadOptions (\r
+                  &BootOptionCount,\r
+                  LoadOptionTypeBoot\r
+                  );\r
+  if (BootOptions == NULL) {\r
+    Status = RETURN_NOT_FOUND;\r
     goto ErrorFreeBootOrder;\r
   }\r
 \r
+  Status = CollectActiveOptions (\r
+             BootOptions,\r
+             BootOptionCount,\r
+             &ActiveOption,\r
+             &ActiveCount\r
+             );\r
+  if (RETURN_ERROR (Status)) {\r
+    goto ErrorFreeBootOptions;\r
+  }\r
+\r
+  if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {\r
+    Status = CreateExtraRootBusMap (&ExtraPciRoots);\r
+    if (EFI_ERROR (Status)) {\r
+      goto ErrorFreeActiveOption;\r
+    }\r
+  } else {\r
+    ExtraPciRoots = NULL;\r
+  }\r
+\r
   //\r
   // translate each OpenFirmware path\r
   //\r
-  TranslatedSize = sizeof (Translated) / sizeof (Translated[0]);\r
-  Status = TranslateOfwPath (&FwCfgPtr, Translated, &TranslatedSize);\r
+  TranslatedSize = ARRAY_SIZE (Translated);\r
+  Status         = TranslateOfwPath (\r
+                     &FwCfgPtr,\r
+                     ExtraPciRoots,\r
+                     Translated,\r
+                     &TranslatedSize\r
+                     );\r
   while (Status == RETURN_SUCCESS ||\r
          Status == RETURN_UNSUPPORTED ||\r
-         Status == RETURN_BUFFER_TOO_SMALL) {\r
+         Status == RETURN_PROTOCOL_ERROR ||\r
+         Status == RETURN_BUFFER_TOO_SMALL)\r
+  {\r
     if (Status == RETURN_SUCCESS) {\r
-      UINTN Idx;\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
+        if (!ActiveOption[Idx].Appended &&\r
+            Match (\r
               Translated,\r
               TranslatedSize, // contains length, not size, in CHAR16's here\r
-              ActiveOption[Idx].BootOption->DevicePath\r
+              ActiveOption[Idx].BootOption->FilePath\r
               )\r
-            ) {\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
+            goto ErrorFreeExtraPciRoots;\r
           }\r
-          break;\r
         }\r
       } // scanned all active boot options\r
     }   // translation successful\r
 \r
-    TranslatedSize = sizeof (Translated) / sizeof (Translated[0]);\r
-    Status = TranslateOfwPath (&FwCfgPtr, Translated, &TranslatedSize);\r
+    TranslatedSize = ARRAY_SIZE (Translated);\r
+    Status         = TranslateOfwPath (\r
+                       &FwCfgPtr,\r
+                       ExtraPciRoots,\r
+                       Translated,\r
+                       &TranslatedSize\r
+                       );\r
   } // scanning of OpenFirmware paths done\r
 \r
-  if (Status == RETURN_NOT_FOUND && BootOrder.Produced > 0) {\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
@@ -1535,7 +2312,7 @@ SetBootOrderFromQemu (
     //\r
     Status = BootOrderComplete (&BootOrder, ActiveOption, ActiveCount);\r
     if (RETURN_ERROR (Status)) {\r
-      goto ErrorFreeActiveOption;\r
+      goto ErrorFreeExtraPciRoots;\r
     }\r
 \r
     //\r
@@ -1546,23 +2323,36 @@ SetBootOrderFromQemu (
                     L"BootOrder",\r
                     &gEfiGlobalVariableGuid,\r
                     EFI_VARIABLE_NON_VOLATILE |\r
-                      EFI_VARIABLE_BOOTSERVICE_ACCESS |\r
-                      EFI_VARIABLE_RUNTIME_ACCESS,\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
+      DEBUG ((\r
+        DEBUG_ERROR,\r
+        "%a: setting BootOrder: %r\n",\r
+        __FUNCTION__,\r
+        Status\r
+        ));\r
+      goto ErrorFreeExtraPciRoots;\r
     }\r
 \r
     DEBUG ((DEBUG_INFO, "%a: setting BootOrder: success\n", __FUNCTION__));\r
     PruneBootVariables (ActiveOption, ActiveCount);\r
   }\r
 \r
+ErrorFreeExtraPciRoots:\r
+  if (ExtraPciRoots != NULL) {\r
+    DestroyExtraRootBusMap (ExtraPciRoots);\r
+  }\r
+\r
 ErrorFreeActiveOption:\r
   FreePool (ActiveOption);\r
 \r
+ErrorFreeBootOptions:\r
+  EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
+\r
 ErrorFreeBootOrder:\r
   FreePool (BootOrder.Data);\r
 \r
@@ -1571,3 +2361,62 @@ ErrorFreeFwCfg:
 \r
   return Status;\r
 }\r
+\r
+/**\r
+  Calculate the number of seconds we should be showing the FrontPage progress\r
+  bar for.\r
+\r
+  @return  The TimeoutDefault argument for PlatformBdsEnterFrontPage().\r
+**/\r
+UINT16\r
+EFIAPI\r
+GetFrontPageTimeoutFromQemu (\r
+  VOID\r
+  )\r
+{\r
+  FIRMWARE_CONFIG_ITEM  BootMenuWaitItem;\r
+  UINTN                 BootMenuWaitSize;\r
+  UINT16                Timeout = PcdGet16 (PcdPlatformBootTimeOut);\r
+\r
+  if (!QemuFwCfgIsAvailable ()) {\r
+    return Timeout;\r
+  }\r
+\r
+  QemuFwCfgSelectItem (QemuFwCfgItemBootMenu);\r
+  if (QemuFwCfgRead16 () == 0) {\r
+    //\r
+    // The user specified "-boot menu=off", or didn't specify "-boot\r
+    // menu=(on|off)" at all. Return the platform default.\r
+    //\r
+    return PcdGet16 (PcdPlatformBootTimeOut);\r
+  }\r
+\r
+  if (RETURN_ERROR (\r
+        QemuFwCfgFindFile (\r
+          "etc/boot-menu-wait",\r
+          &BootMenuWaitItem,\r
+          &BootMenuWaitSize\r
+          )\r
+        ) ||\r
+      (BootMenuWaitSize != sizeof (UINT16)))\r
+  {\r
+    //\r
+    // "-boot menu=on" was specified without "splash-time=N". In this case,\r
+    // return three seconds if the platform default would cause us to skip the\r
+    // front page, and return the platform default otherwise.\r
+    //\r
+    if (Timeout == 0) {\r
+      Timeout = 3;\r
+    }\r
+\r
+    return Timeout;\r
+  }\r
+\r
+  //\r
+  // "-boot menu=on,splash-time=N" was specified, where N is in units of\r
+  // milliseconds. The Intel BDS Front Page progress bar only supports whole\r
+  // seconds, round N up.\r
+  //\r
+  QemuFwCfgSelectItem (BootMenuWaitItem);\r
+  return (UINT16)((QemuFwCfgRead16 () + 999) / 1000);\r
+}\r