]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.c
OvmfPkg, ArmVirtPkg: rename QemuNewBootOrderLib to QemuBootOrderLib
[mirror_edk2.git] / OvmfPkg / Library / QemuBootOrderLib / QemuBootOrderLib.c
index 174fd1f5541191c294f5ac18e2d71003d841adcf..86082301a8f517d1a85f93cc9e45550a4a7c96ac 100644 (file)
@@ -2,7 +2,7 @@
   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
 #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 <Library/PrintLib.h>\r
 #include <Library/DevicePathLib.h>\r
 #include <Library/QemuBootOrderLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
 #include <Guid/GlobalVariable.h>\r
+#include <Guid/VirtioMmioTransport.h>\r
 \r
+#include "ExtraRootBusMap.h"\r
 \r
 /**\r
   OpenFirmware to UEFI device path translation output buffer size in CHAR16's.\r
 **/\r
 #define TRANSLATION_OUTPUT_SIZE 0x100\r
 \r
+/**\r
+  Output buffer size for OpenFirmware to UEFI device path fragment translation,\r
+  in CHAR16's, for a sequence of PCI bridges.\r
+**/\r
+#define BRIDGE_TRANSLATION_OUTPUT_SIZE 0x40\r
 \r
 /**\r
   Numbers of nodes in OpenFirmware device paths that are required and examined.\r
 **/\r
 #define REQUIRED_PCI_OFW_NODES  2\r
-#define EXAMINED_OFW_NODES      4\r
+#define REQUIRED_MMIO_OFW_NODES 1\r
+#define EXAMINED_OFW_NODES      6\r
 \r
 \r
 /**\r
@@ -128,7 +137,7 @@ SubstringEq (
 /**\r
 \r
   Parse a comma-separated list of hexadecimal integers into the elements of an\r
-  UINT32 array.\r
+  UINT64 array.\r
 \r
   Whitespace, "0x" prefixes, leading or trailing commas, sequences of commas,\r
   or an empty string are not allowed; they are rejected.\r
@@ -168,12 +177,12 @@ STATIC
 RETURN_STATUS\r
 ParseUnitAddressHexList (\r
   IN      SUBSTRING  UnitAddress,\r
-  OUT     UINT32     *Result,\r
+  OUT     UINT64     *Result,\r
   IN OUT  UINTN      *NumResults\r
   )\r
 {\r
   UINTN         Entry;    // number of entry currently being parsed\r
-  UINT32        EntryVal; // value being constructed for current entry\r
+  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
@@ -193,10 +202,10 @@ ParseUnitAddressHexList (
           -1;\r
 \r
     if (Val >= 0) {\r
-      if (EntryVal > 0xFFFFFFF) {\r
+      if (EntryVal > 0xFFFFFFFFFFFFFFFull) {\r
         return RETURN_INVALID_PARAMETER;\r
       }\r
-      EntryVal = (EntryVal << 4) | Val;\r
+      EntryVal = LShiftU64 (EntryVal, 4) | Val;\r
     } else if (Chr == ',') {\r
       if (PrevChr == ',') {\r
         return RETURN_INVALID_PARAMETER;\r
@@ -244,8 +253,10 @@ typedef struct {
   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
@@ -291,7 +302,7 @@ BootOrderAppend (
   }\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
@@ -299,22 +310,25 @@ BootOrderAppend (
 \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
@@ -322,11 +336,13 @@ 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 Index;\r
   UINTN ScanMode;\r
 \r
   *ActiveOption = NULL;\r
@@ -337,22 +353,15 @@ 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
         ++*Count;\r
       }\r
-      Link = Link->ForwardLink;\r
     }\r
 \r
     if (ScanMode == 0) {\r
@@ -548,6 +557,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
@@ -568,32 +582,134 @@ 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
-  UINT32 PciDevFun[2];\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
+  //   /pci@i0cf8\r
+  //\r
+  // For extra root buses, the initial OFW node is\r
+  //\r
+  //   /pci@i0cf8,4\r
+  //              ^\r
+  //              root bus serial number (not PCI bus number)\r
   //\r
   if (NumNodes < REQUIRED_PCI_OFW_NODES ||\r
       !SubstringEq (OfwNode[0].DriverName, "pci")\r
       ) {\r
     return RETURN_UNSUPPORTED;\r
   }\r
+\r
+  PciRoot = 0;\r
+  Comma = ScanMem8 (OfwNode[0].UnitAddress.Ptr, OfwNode[0].UnitAddress.Len,\r
+            ',');\r
+  if (Comma != NULL) {\r
+    SUBSTRING PciRootSerialSubString;\r
+    UINT64    PciRootSerial;\r
+\r
+    //\r
+    // Parse the root bus serial number from the unit address after the comma.\r
+    //\r
+    PciRootSerialSubString.Ptr = Comma + 1;\r
+    PciRootSerialSubString.Len = OfwNode[0].UnitAddress.Len -\r
+                                 (PciRootSerialSubString.Ptr -\r
+                                  OfwNode[0].UnitAddress.Ptr);\r
+    NumEntries = 1;\r
+    if (RETURN_ERROR (ParseUnitAddressHexList (PciRootSerialSubString,\r
+                      &PciRootSerial, &NumEntries))) {\r
+      return RETURN_UNSUPPORTED;\r
+    }\r
+\r
+    //\r
+    // Map the extra root bus's serial number to its actual bus number.\r
+    //\r
+    if (EFI_ERROR (MapRootBusPosToBusNr (ExtraPciRoots, PciRootSerial,\r
+                     &PciRoot))) {\r
+      return RETURN_PROTOCOL_ERROR;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Translate a sequence of PCI bridges. For each bridge, the OFW node is:\r
+  //\r
+  //   pci-bridge@1e[,0]\r
+  //              ^   ^\r
+  //              PCI slot & function on the parent, holding the bridge\r
+  //\r
+  // and the UEFI device path node is:\r
+  //\r
+  //   Pci(0x1E,0x0)\r
+  //\r
+  FirstNonBridge = 1;\r
+  Bridges[0] = L'\0';\r
+  BridgesLen = 0;\r
+  do {\r
+    UINT64 BridgeDevFun[2];\r
+    UINTN  BridgesFreeBytes;\r
+\r
+    if (!SubstringEq (OfwNode[FirstNonBridge].DriverName, "pci-bridge")) {\r
+      break;\r
+    }\r
+\r
+    BridgeDevFun[1] = 0;\r
+    NumEntries = sizeof BridgeDevFun / sizeof BridgeDevFun[0];\r
+    if (ParseUnitAddressHexList (OfwNode[FirstNonBridge].UnitAddress,\r
+          BridgeDevFun, &NumEntries) != RETURN_SUCCESS) {\r
+      return RETURN_UNSUPPORTED;\r
+    }\r
+\r
+    BridgesFreeBytes = sizeof Bridges - BridgesLen * sizeof Bridges[0];\r
+    Written = UnicodeSPrintAsciiFormat (Bridges + BridgesLen, BridgesFreeBytes,\r
+                "/Pci(0x%Lx,0x%Lx)", BridgeDevFun[0], BridgeDevFun[1]);\r
+    BridgesLen += Written;\r
+\r
+    //\r
+    // There's no way to differentiate between "completely used up without\r
+    // truncation" and "truncated", so treat the former as the latter.\r
+    //\r
+    if (BridgesLen + 1 == BRIDGE_TRANSLATION_OUTPUT_SIZE) {\r
+      return RETURN_UNSUPPORTED;\r
+    }\r
+\r
+    ++FirstNonBridge;\r
+  } while (FirstNonBridge < NumNodes);\r
+\r
+  if (FirstNonBridge == NumNodes) {\r
+    return RETURN_UNSUPPORTED;\r
+  }\r
+\r
+  //\r
+  // Parse the OFW nodes starting with the first non-bridge node.\r
+  //\r
   PciDevFun[1] = 0;\r
   NumEntries = sizeof (PciDevFun) / sizeof (PciDevFun[0]);\r
   if (ParseUnitAddressHexList (\r
-        OfwNode[1].UnitAddress,\r
+        OfwNode[FirstNonBridge].UnitAddress,\r
         PciDevFun,\r
         &NumEntries\r
         ) != RETURN_SUCCESS\r
@@ -601,10 +717,10 @@ TranslatePciOfwNodes (
     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
+  if (NumNodes >= FirstNonBridge + 3 &&\r
+      SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "ide") &&\r
+      SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "drive") &&\r
+      SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")\r
       ) {\r
     //\r
     // OpenFirmware device path (IDE disk, IDE CD-ROM):\r
@@ -622,18 +738,18 @@ TranslatePciOfwNodes (
     //                                                ^\r
     //                                                fixed LUN\r
     //\r
-    UINT32 Secondary;\r
-    UINT32 Slave;\r
+    UINT64 Secondary;\r
+    UINT64 Slave;\r
 \r
     NumEntries = 1;\r
     if (ParseUnitAddressHexList (\r
-          OfwNode[2].UnitAddress,\r
+          OfwNode[FirstNonBridge + 1].UnitAddress,\r
           &Secondary,\r
           &NumEntries\r
           ) != RETURN_SUCCESS ||\r
         Secondary > 1 ||\r
         ParseUnitAddressHexList (\r
-          OfwNode[3].UnitAddress,\r
+          OfwNode[FirstNonBridge + 2].UnitAddress,\r
           &Slave,\r
           &NumEntries // reuse after previous single-element call\r
           ) != RETURN_SUCCESS ||\r
@@ -645,16 +761,61 @@ TranslatePciOfwNodes (
     Written = UnicodeSPrintAsciiFormat (\r
       Translated,\r
       *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
-      "PciRoot(0x0)/Pci(0x%x,0x%x)/Ata(%a,%a,0x0)",\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 >= 4 &&\r
-             SubstringEq (OfwNode[1].DriverName, "isa") &&\r
-             SubstringEq (OfwNode[2].DriverName, "fdc") &&\r
-             SubstringEq (OfwNode[3].DriverName, "floppy")\r
+  } else if (NumNodes >= FirstNonBridge + 3 &&\r
+      SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "pci8086,2922") &&\r
+      SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "drive") &&\r
+      SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")\r
+      ) {\r
+    //\r
+    // OpenFirmware device path (Q35 SATA disk and CD-ROM):\r
+    //\r
+    //   /pci@i0cf8/pci8086,2922@1f,2/drive@1/disk@0\r
+    //        ^                  ^  ^       ^      ^\r
+    //        |                  |  |       |      device number (fixed 0)\r
+    //        |                  |  |       channel (port) number\r
+    //        |                  PCI slot & function holding SATA HBA\r
+    //        PCI root at system bus port, PIO\r
+    //\r
+    // UEFI device path:\r
+    //\r
+    //   PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x1,0xFFFF,0x0)\r
+    //                                   ^   ^      ^\r
+    //                                   |   |      LUN (always 0 on Q35)\r
+    //                                   |   port multiplier port number,\r
+    //                                   |   always 0xFFFF on Q35\r
+    //                                   channel (port) number\r
+    //\r
+    UINT64 Channel;\r
+\r
+    NumEntries = 1;\r
+    if (RETURN_ERROR (ParseUnitAddressHexList (\r
+                        OfwNode[FirstNonBridge + 1].UnitAddress, &Channel,\r
+                        &NumEntries))) {\r
+      return RETURN_UNSUPPORTED;\r
+    }\r
+\r
+    Written = UnicodeSPrintAsciiFormat (\r
+      Translated,\r
+      *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+      "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Sata(0x%Lx,0xFFFF,0x0)",\r
+      PciRoot,\r
+      Bridges,\r
+      PciDevFun[0],\r
+      PciDevFun[1],\r
+      Channel\r
+      );\r
+  } else if (NumNodes >= FirstNonBridge + 3 &&\r
+             SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "isa") &&\r
+             SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "fdc") &&\r
+             SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "floppy")\r
              ) {\r
     //\r
     // OpenFirmware device path (floppy disk):\r
@@ -672,11 +833,11 @@ TranslatePciOfwNodes (
     //                                    ^\r
     //                                    ACPI UID\r
     //\r
-    UINT32 AcpiUid;\r
+    UINT64 AcpiUid;\r
 \r
     NumEntries = 1;\r
     if (ParseUnitAddressHexList (\r
-          OfwNode[3].UnitAddress,\r
+          OfwNode[FirstNonBridge + 2].UnitAddress,\r
           &AcpiUid,\r
           &NumEntries\r
           ) != RETURN_SUCCESS ||\r
@@ -688,14 +849,16 @@ TranslatePciOfwNodes (
     Written = UnicodeSPrintAsciiFormat (\r
       Translated,\r
       *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
-      "PciRoot(0x0)/Pci(0x%x,0x%x)/Floppy(0x%x)",\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 >= 3 &&\r
-             SubstringEq (OfwNode[1].DriverName, "scsi") &&\r
-             SubstringEq (OfwNode[2].DriverName, "disk")\r
+  } else if (NumNodes >= FirstNonBridge + 2 &&\r
+             SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "scsi") &&\r
+             SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "disk")\r
              ) {\r
     //\r
     // OpenFirmware device path (virtio-blk disk):\r
@@ -715,14 +878,16 @@ TranslatePciOfwNodes (
     Written = UnicodeSPrintAsciiFormat (\r
       Translated,\r
       *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
-      "PciRoot(0x0)/Pci(0x%x,0x%x)/HD(",\r
+      "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/HD(",\r
+      PciRoot,\r
+      Bridges,\r
       PciDevFun[0],\r
       PciDevFun[1]\r
       );\r
-  } else if (NumNodes >= 4 &&\r
-             SubstringEq (OfwNode[1].DriverName, "scsi") &&\r
-             SubstringEq (OfwNode[2].DriverName, "channel") &&\r
-             SubstringEq (OfwNode[3].DriverName, "disk")\r
+  } else if (NumNodes >= FirstNonBridge + 3 &&\r
+             SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "scsi") &&\r
+             SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "channel") &&\r
+             SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")\r
              ) {\r
     //\r
     // OpenFirmware device path (virtio-scsi disk):\r
@@ -742,12 +907,12 @@ TranslatePciOfwNodes (
     //   PciRoot(0x0)/Pci(0x7,0x3)/Scsi(0x2,0x3)\r
     //                                -- if PCI function is present and nonzero\r
     //\r
-    UINT32 TargetLun[2];\r
+    UINT64 TargetLun[2];\r
 \r
     TargetLun[1] = 0;\r
     NumEntries = sizeof (TargetLun) / sizeof (TargetLun[0]);\r
     if (ParseUnitAddressHexList (\r
-          OfwNode[3].UnitAddress,\r
+          OfwNode[FirstNonBridge + 2].UnitAddress,\r
           TargetLun,\r
           &NumEntries\r
           ) != RETURN_SUCCESS\r
@@ -758,12 +923,69 @@ TranslatePciOfwNodes (
     Written = UnicodeSPrintAsciiFormat (\r
       Translated,\r
       *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
-      "PciRoot(0x0)/Pci(0x%x,0x%x)/Scsi(0x%x,0x%x)",\r
+      "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Scsi(0x%Lx,0x%Lx)",\r
+      PciRoot,\r
+      Bridges,\r
       PciDevFun[0],\r
       PciDevFun[1],\r
       TargetLun[0],\r
       TargetLun[1]\r
       );\r
+  } else if (NumNodes >= FirstNonBridge + 2 &&\r
+      SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "pci8086,5845") &&\r
+      SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "namespace")\r
+      ) {\r
+    //\r
+    // OpenFirmware device path (NVMe device):\r
+    //\r
+    //   /pci@i0cf8/pci8086,5845@6[,1]/namespace@1,0\r
+    //        ^                  ^  ^            ^ ^\r
+    //        |                  |  |            | Extended Unique Identifier\r
+    //        |                  |  |            | (EUI-64), big endian interp.\r
+    //        |                  |  |            namespace ID\r
+    //        |                  PCI slot & function holding NVMe controller\r
+    //        PCI root at system bus port, PIO\r
+    //\r
+    // UEFI device path:\r
+    //\r
+    //   PciRoot(0x0)/Pci(0x6,0x1)/NVMe(0x1,00-00-00-00-00-00-00-00)\r
+    //                                  ^   ^\r
+    //                                  |   octets of the EUI-64\r
+    //                                  |   in address order\r
+    //                                  namespace ID\r
+    //\r
+    UINT64 Namespace[2];\r
+    UINTN  RequiredEntries;\r
+    UINT8  *Eui64;\r
+\r
+    RequiredEntries = sizeof (Namespace) / sizeof (Namespace[0]);\r
+    NumEntries = RequiredEntries;\r
+    if (ParseUnitAddressHexList (\r
+          OfwNode[FirstNonBridge + 1].UnitAddress,\r
+          Namespace,\r
+          &NumEntries\r
+          ) != RETURN_SUCCESS ||\r
+        NumEntries != RequiredEntries ||\r
+        Namespace[0] == 0 ||\r
+        Namespace[0] >= MAX_UINT32\r
+        ) {\r
+      return RETURN_UNSUPPORTED;\r
+    }\r
+\r
+    Eui64 = (UINT8 *)&Namespace[1];\r
+    Written = UnicodeSPrintAsciiFormat (\r
+      Translated,\r
+      *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+      "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/"\r
+      "NVMe(0x%Lx,%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x)",\r
+      PciRoot,\r
+      Bridges,\r
+      PciDevFun[0],\r
+      PciDevFun[1],\r
+      Namespace[0],\r
+      Eui64[7], Eui64[6], Eui64[5], Eui64[4],\r
+      Eui64[3], Eui64[2], Eui64[1], Eui64[0]\r
+      );\r
   } else {\r
     //\r
     // Generic OpenFirmware device path for PCI devices:\r
@@ -781,7 +1003,9 @@ TranslatePciOfwNodes (
     Written = UnicodeSPrintAsciiFormat (\r
       Translated,\r
       *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
-      "PciRoot(0x0)/Pci(0x%x,0x%x)",\r
+      "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",\r
+      PciRoot,\r
+      Bridges,\r
       PciDevFun[0],\r
       PciDevFun[1]\r
       );\r
@@ -801,10 +1025,20 @@ TranslatePciOfwNodes (
 }\r
 \r
 \r
+//\r
+// A type providing easy raw access to the base address of a virtio-mmio\r
+// transport.\r
+//\r
+typedef union {\r
+  UINT64 Uint64;\r
+  UINT8  Raw[8];\r
+} VIRTIO_MMIO_BASE_ADDRESS;\r
+\r
+\r
 /**\r
 \r
-  Translate an array of OpenFirmware device nodes to a UEFI device path\r
-  fragment.\r
+  Translate an MMIO-like array of OpenFirmware device nodes to a UEFI device\r
+  path fragment.\r
 \r
   @param[in]     OfwNode         Array of OpenFirmware device nodes to\r
                                  translate, constituting the beginning of an\r
@@ -835,19 +1069,200 @@ TranslatePciOfwNodes (
 **/\r
 STATIC\r
 RETURN_STATUS\r
-TranslateOfwNodes (\r
+TranslateMmioOfwNodes (\r
   IN      CONST OFW_NODE *OfwNode,\r
   IN      UINTN          NumNodes,\r
   OUT     CHAR16         *Translated,\r
   IN OUT  UINTN          *TranslatedSize\r
   )\r
+{\r
+  VIRTIO_MMIO_BASE_ADDRESS VirtioMmioBase;\r
+  CHAR16                   VenHwString[60 + 1];\r
+  UINTN                    NumEntries;\r
+  UINTN                    Written;\r
+\r
+  //\r
+  // Get the base address of the virtio-mmio transport.\r
+  //\r
+  if (NumNodes < REQUIRED_MMIO_OFW_NODES ||\r
+      !SubstringEq (OfwNode[0].DriverName, "virtio-mmio")\r
+      ) {\r
+    return RETURN_UNSUPPORTED;\r
+  }\r
+  NumEntries = 1;\r
+  if (ParseUnitAddressHexList (\r
+        OfwNode[0].UnitAddress,\r
+        &VirtioMmioBase.Uint64,\r
+        &NumEntries\r
+        ) != RETURN_SUCCESS\r
+      ) {\r
+    return RETURN_UNSUPPORTED;\r
+  }\r
+\r
+  UnicodeSPrintAsciiFormat (VenHwString, sizeof VenHwString,\r
+    "VenHw(%g,%02X%02X%02X%02X%02X%02X%02X%02X)", &gVirtioMmioTransportGuid,\r
+    VirtioMmioBase.Raw[0], VirtioMmioBase.Raw[1], VirtioMmioBase.Raw[2],\r
+    VirtioMmioBase.Raw[3], VirtioMmioBase.Raw[4], VirtioMmioBase.Raw[5],\r
+    VirtioMmioBase.Raw[6], VirtioMmioBase.Raw[7]);\r
+\r
+  if (NumNodes >= 2 &&\r
+      SubstringEq (OfwNode[1].DriverName, "disk")) {\r
+    //\r
+    // OpenFirmware device path (virtio-blk disk):\r
+    //\r
+    //   /virtio-mmio@000000000a003c00/disk@0,0\r
+    //                ^                     ^ ^\r
+    //                |                     fixed\r
+    //                base address of virtio-mmio register block\r
+    //\r
+    // UEFI device path prefix:\r
+    //\r
+    //   <VenHwString>/HD(\r
+    //\r
+    Written = UnicodeSPrintAsciiFormat (\r
+                Translated,\r
+                *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+                "%s/HD(",\r
+                VenHwString\r
+                );\r
+  } else if (NumNodes >= 3 &&\r
+             SubstringEq (OfwNode[1].DriverName, "channel") &&\r
+             SubstringEq (OfwNode[2].DriverName, "disk")) {\r
+    //\r
+    // OpenFirmware device path (virtio-scsi disk):\r
+    //\r
+    //   /virtio-mmio@000000000a003a00/channel@0/disk@2,3\r
+    //                ^                        ^      ^ ^\r
+    //                |                        |      | LUN\r
+    //                |                        |      target\r
+    //                |                        channel (unused, fixed 0)\r
+    //                base address of virtio-mmio register block\r
+    //\r
+    // UEFI device path prefix:\r
+    //\r
+    //   <VenHwString>/Scsi(0x2,0x3)\r
+    //\r
+    UINT64 TargetLun[2];\r
+\r
+    TargetLun[1] = 0;\r
+    NumEntries = sizeof (TargetLun) / sizeof (TargetLun[0]);\r
+    if (ParseUnitAddressHexList (\r
+          OfwNode[2].UnitAddress,\r
+          TargetLun,\r
+          &NumEntries\r
+          ) != RETURN_SUCCESS\r
+        ) {\r
+      return RETURN_UNSUPPORTED;\r
+    }\r
+\r
+    Written = UnicodeSPrintAsciiFormat (\r
+                Translated,\r
+                *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+                "%s/Scsi(0x%Lx,0x%Lx)",\r
+                VenHwString,\r
+                TargetLun[0],\r
+                TargetLun[1]\r
+                );\r
+  } else if (NumNodes >= 2 &&\r
+             SubstringEq (OfwNode[1].DriverName, "ethernet-phy")) {\r
+    //\r
+    // OpenFirmware device path (virtio-net NIC):\r
+    //\r
+    //   /virtio-mmio@000000000a003e00/ethernet-phy@0\r
+    //                ^                             ^\r
+    //                |                             fixed\r
+    //                base address of virtio-mmio register block\r
+    //\r
+    // UEFI device path prefix (dependent on presence of nonzero PCI function):\r
+    //\r
+    //   <VenHwString>/MAC(\r
+    //\r
+    Written = UnicodeSPrintAsciiFormat (\r
+                Translated,\r
+                *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+                "%s/MAC(",\r
+                VenHwString\r
+                );\r
+  } else {\r
+    return RETURN_UNSUPPORTED;\r
+  }\r
+\r
+  //\r
+  // There's no way to differentiate between "completely used up without\r
+  // truncation" and "truncated", so treat the former as the latter, and return\r
+  // success only for "some room left unused".\r
+  //\r
+  if (Written + 1 < *TranslatedSize) {\r
+    *TranslatedSize = Written;\r
+    return RETURN_SUCCESS;\r
+  }\r
+\r
+  return RETURN_BUFFER_TOO_SMALL;\r
+}\r
+\r
+\r
+/**\r
+\r
+  Translate an array of OpenFirmware device nodes to a UEFI device path\r
+  fragment.\r
+\r
+  @param[in]     OfwNode         Array of OpenFirmware device nodes to\r
+                                 translate, constituting the beginning of an\r
+                                 OpenFirmware device path.\r
+\r
+  @param[in]     NumNodes        Number of elements in OfwNode.\r
+\r
+  @param[in]     ExtraPciRoots   An EXTRA_ROOT_BUS_MAP object created with\r
+                                 CreateExtraRootBusMap(), to be used for\r
+                                 translating positions of extra root buses to\r
+                                 bus numbers.\r
+\r
+  @param[out]    Translated      Destination array receiving the UEFI path\r
+                                 fragment, allocated by the caller. If the\r
+                                 return value differs from RETURN_SUCCESS, its\r
+                                 contents is indeterminate.\r
+\r
+  @param[in out] TranslatedSize  On input, the number of CHAR16's in\r
+                                 Translated. On RETURN_SUCCESS this parameter\r
+                                 is assigned the number of non-NUL CHAR16's\r
+                                 written to Translated. In case of other return\r
+                                 values, TranslatedSize is indeterminate.\r
+\r
+\r
+  @retval RETURN_SUCCESS           Translation successful.\r
+\r
+  @retval RETURN_BUFFER_TOO_SMALL  The translation does not fit into the number\r
+                                   of bytes provided.\r
+\r
+  @retval RETURN_UNSUPPORTED       The array of OpenFirmware device nodes can't\r
+                                   be translated in the current implementation.\r
+\r
+  @retval RETURN_PROTOCOL_ERROR    The array of OpenFirmware device nodes has\r
+                                   been (partially) recognized, but it contains\r
+                                   a logic error / doesn't match system state.\r
+\r
+**/\r
+STATIC\r
+RETURN_STATUS\r
+TranslateOfwNodes (\r
+  IN      CONST OFW_NODE           *OfwNode,\r
+  IN      UINTN                    NumNodes,\r
+  IN      CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,\r
+  OUT     CHAR16                   *Translated,\r
+  IN OUT  UINTN                    *TranslatedSize\r
+  )\r
 {\r
   RETURN_STATUS Status;\r
 \r
   Status = RETURN_UNSUPPORTED;\r
 \r
   if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {\r
-    Status = TranslatePciOfwNodes (OfwNode, NumNodes, Translated,\r
+    Status = TranslatePciOfwNodes (OfwNode, NumNodes, ExtraPciRoots,\r
+               Translated, TranslatedSize);\r
+  }\r
+  if (Status == RETURN_UNSUPPORTED &&\r
+      FeaturePcdGet (PcdQemuBootOrderMmioTranslation)) {\r
+    Status = TranslateMmioOfwNodes (OfwNode, NumNodes, Translated,\r
                TranslatedSize);\r
   }\r
   return Status;\r
@@ -867,6 +1282,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
@@ -892,6 +1312,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
@@ -904,9 +1330,10 @@ 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
@@ -954,6 +1381,7 @@ TranslateOfwPath (
   Status = TranslateOfwNodes (\r
              Node,\r
              NumNodes < EXAMINED_OFW_NODES ? NumNodes : EXAMINED_OFW_NODES,\r
+             ExtraPciRoots,\r
              Translated,\r
              TranslatedSize);\r
   switch (Status) {\r
@@ -969,6 +1397,11 @@ TranslateOfwPath (
     DEBUG ((DEBUG_VERBOSE, "%a: unsupported\n", __FUNCTION__));\r
     break;\r
 \r
+  case RETURN_PROTOCOL_ERROR:\r
+    DEBUG ((DEBUG_VERBOSE, "%a: logic error / system state mismatch\n",\r
+      __FUNCTION__));\r
+    break;\r
+\r
   default:\r
     ASSERT (0);\r
   }\r
@@ -1004,11 +1437,17 @@ BOOLEAN
 Match (\r
   IN  CONST CHAR16                           *Translated,\r
   IN  UINTN                                  TranslatedLength,\r
-  IN  CONST EFI_DEVICE_PATH_PROTOCOL         *DevicePath\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
@@ -1019,24 +1458,57 @@ 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
+    // Harddrive shortform device path\r
+    //\r
+    Shortform = TRUE;\r
+  } else if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&\r
+             (DevicePathSubType (DevicePath) == MEDIA_FILEPATH_DP)) {\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
+    // URI shortform device path\r
+    //\r
+    Shortform = TRUE;\r
+  } else {\r
+    for ( Node = DevicePath\r
+        ; !IsDevicePathEnd (Node)\r
+        ; Node = NextDevicePathNode (Node)\r
+        ) {\r
+      if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) &&\r
+          ((DevicePathSubType (Node) == MSG_USB_CLASS_DP) ||\r
+           (DevicePathSubType (Node) == MSG_USB_WWID_DP))) {\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, &AbsDevicePath, &FileSize\r
+                   );\r
+    if (FileBuffer == NULL) {\r
       goto Exit;\r
     }\r
+    FreePool (FileBuffer);\r
     AbsConverted = ConvertDevicePathToText (AbsDevicePath, FALSE, FALSE);\r
+    FreePool (AbsDevicePath);\r
     if (AbsConverted == NULL) {\r
       goto Exit;\r
     }\r
@@ -1069,7 +1541,7 @@ Exit:
 \r
   This function should accommodate any further policy changes in "boot option\r
   survival". Currently we're adding back everything that starts with neither\r
-  PciRoot() nor HD().\r
+  PciRoot() nor HD() nor a virtio-mmio VenHw() node.\r
 \r
   @param[in,out] BootOrder     The structure holding the boot order to\r
                                complete. The caller is responsible for\r
@@ -1105,11 +1577,11 @@ BootOrderComplete (
   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
+      FirstNode = Current->FilePath;\r
       if (FirstNode != NULL) {\r
         CHAR16        *Converted;\r
         STATIC CHAR16 ConvFallBack[] = L"<unable to convert>";\r
@@ -1141,6 +1613,18 @@ 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
+\r
+          VenHw = (VENDOR_DEVICE_PATH *)FirstNode;\r
+          if (CompareGuid (&VenHw->Guid, &gVirtioMmioTransportGuid)) {\r
+            //\r
+            // drop virtio-mmio if we enabled the user to select boot options\r
+            // referencing such device paths\r
+            //\r
+            Keep = !FeaturePcdGet (PcdQemuBootOrderMmioTranslation);\r
+          }\r
         }\r
 \r
         if (Keep) {\r
@@ -1190,7 +1674,7 @@ PruneBootVariables (
       CHAR16 VariableName[9];\r
 \r
       UnicodeSPrintAsciiFormat (VariableName, sizeof VariableName, "Boot%04x",\r
-        ActiveOption[Idx].BootOption->BootCurrent);\r
+        ActiveOption[Idx].BootOption->OptionNumber);\r
 \r
       //\r
       // "The space consumed by the deleted variable may not be available until\r
@@ -1212,12 +1696,11 @@ PruneBootVariables (
 \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 EfiBootManagerConnectAll () and\r
+  EfiBootManagerRefreshAllBootOption () return.\r
 \r
   @retval RETURN_SUCCESS            BootOrder NvVar rewritten.\r
 \r
@@ -1237,7 +1720,7 @@ PruneBootVariables (
 **/\r
 RETURN_STATUS\r
 SetBootOrderFromQemu (\r
-  IN  CONST LIST_ENTRY *BootOptionList\r
+  VOID\r
   )\r
 {\r
   RETURN_STATUS                    Status;\r
@@ -1250,8 +1733,12 @@ SetBootOrderFromQemu (
   ACTIVE_OPTION                    *ActiveOption;\r
   UINTN                            ActiveCount;\r
 \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
@@ -1289,18 +1776,39 @@ SetBootOrderFromQemu (
     goto ErrorFreeFwCfg;\r
   }\r
 \r
-  Status = CollectActiveOptions (BootOptionList, &ActiveOption, &ActiveCount);\r
-  if (RETURN_ERROR (Status)) {\r
+  BootOptions = EfiBootManagerGetLoadOptions (\r
+                  &BootOptionCount, LoadOptionTypeBoot\r
+                  );\r
+  if (BootOptions == NULL) {\r
+    Status = RETURN_NOT_FOUND;\r
     goto ErrorFreeBootOrder;\r
   }\r
 \r
+  Status = CollectActiveOptions (\r
+             BootOptions, BootOptionCount, &ActiveOption, &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
+  Status = TranslateOfwPath (&FwCfgPtr, ExtraPciRoots, Translated,\r
+             &TranslatedSize);\r
   while (Status == RETURN_SUCCESS ||\r
          Status == RETURN_UNSUPPORTED ||\r
+         Status == RETURN_PROTOCOL_ERROR ||\r
          Status == RETURN_BUFFER_TOO_SMALL) {\r
     if (Status == RETURN_SUCCESS) {\r
       UINTN Idx;\r
@@ -1312,7 +1820,7 @@ SetBootOrderFromQemu (
         if (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
@@ -1320,7 +1828,7 @@ SetBootOrderFromQemu (
           //\r
           Status = BootOrderAppend (&BootOrder, &ActiveOption[Idx]);\r
           if (Status != RETURN_SUCCESS) {\r
-            goto ErrorFreeActiveOption;\r
+            goto ErrorFreeExtraPciRoots;\r
           }\r
           break;\r
         }\r
@@ -1328,7 +1836,8 @@ SetBootOrderFromQemu (
     }   // translation successful\r
 \r
     TranslatedSize = sizeof (Translated) / sizeof (Translated[0]);\r
-    Status = TranslateOfwPath (&FwCfgPtr, Translated, &TranslatedSize);\r
+    Status = TranslateOfwPath (&FwCfgPtr, ExtraPciRoots, Translated,\r
+               &TranslatedSize);\r
   } // scanning of OpenFirmware paths done\r
 \r
   if (Status == RETURN_NOT_FOUND && BootOrder.Produced > 0) {\r
@@ -1339,7 +1848,7 @@ SetBootOrderFromQemu (
     //\r
     Status = BootOrderComplete (&BootOrder, ActiveOption, ActiveCount);\r
     if (RETURN_ERROR (Status)) {\r
-      goto ErrorFreeActiveOption;\r
+      goto ErrorFreeExtraPciRoots;\r
     }\r
 \r
     //\r
@@ -1357,16 +1866,24 @@ SetBootOrderFromQemu (
                     );\r
     if (EFI_ERROR (Status)) {\r
       DEBUG ((DEBUG_ERROR, "%a: setting BootOrder: %r\n", __FUNCTION__, Status));\r
-      goto ErrorFreeActiveOption;\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
@@ -1375,3 +1892,53 @@ ErrorFreeFwCfg:
 \r
   return Status;\r
 }\r
+\r
+\r
+/**\r
+  Calculate the number of seconds we should be showing the FrontPage progress\r
+  bar for.\r
+\r
+  @return  The TimeoutDefault argument for PlatformBdsEnterFrontPage().\r
+**/\r
+UINT16\r
+GetFrontPageTimeoutFromQemu (\r
+  VOID\r
+  )\r
+{\r
+  FIRMWARE_CONFIG_ITEM BootMenuWaitItem;\r
+  UINTN                BootMenuWaitSize;\r
+\r
+  QemuFwCfgSelectItem (QemuFwCfgItemBootMenu);\r
+  if (QemuFwCfgRead16 () == 0) {\r
+    //\r
+    // The user specified "-boot menu=off", or didn't specify "-boot\r
+    // menu=(on|off)" at all. Return the platform default.\r
+    //\r
+    return PcdGet16 (PcdPlatformBootTimeOut);\r
+  }\r
+\r
+  if (RETURN_ERROR (QemuFwCfgFindFile ("etc/boot-menu-wait", &BootMenuWaitItem,\r
+                      &BootMenuWaitSize)) ||\r
+      BootMenuWaitSize != sizeof (UINT16)) {\r
+    //\r
+    // "-boot menu=on" was specified without "splash-time=N". In this case,\r
+    // return three seconds if the platform default would cause us to skip the\r
+    // front page, and return the platform default otherwise.\r
+    //\r
+    UINT16 Timeout;\r
+\r
+    Timeout = PcdGet16 (PcdPlatformBootTimeOut);\r
+    if (Timeout == 0) {\r
+      Timeout = 3;\r
+    }\r
+    return Timeout;\r
+  }\r
+\r
+  //\r
+  // "-boot menu=on,splash-time=N" was specified, where N is in units of\r
+  // milliseconds. The Intel BDS Front Page progress bar only supports whole\r
+  // seconds, round N up.\r
+  //\r
+  QemuFwCfgSelectItem (BootMenuWaitItem);\r
+  return (UINT16)((QemuFwCfgRead16 () + 999) / 1000);\r
+}\r