]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.c
OvmfPkg/QemuBootOrderLib: adapt Q35 SATA PMPN to UEFI spec Mantis 1353
[mirror_edk2.git] / OvmfPkg / Library / QemuBootOrderLib / QemuBootOrderLib.c
index 276d675f505d1539babae4ed940cfb24446de389..582b0bc90ebc17b73a0fc5f04fea297a60177404 100644 (file)
@@ -27,6 +27,7 @@
 #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
@@ -556,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
@@ -576,16 +582,23 @@ 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 PciRoot;\r
+  CHAR8  *Comma;\r
   UINTN  FirstNonBridge;\r
   CHAR16 Bridges[BRIDGE_TRANSLATION_OUTPUT_SIZE];\r
   UINTN  BridgesLen;\r
@@ -594,7 +607,17 @@ TranslatePciOfwNodes (
   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
@@ -602,6 +625,35 @@ TranslatePciOfwNodes (
     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
@@ -709,13 +761,57 @@ TranslatePciOfwNodes (
     Written = UnicodeSPrintAsciiFormat (\r
       Translated,\r
       *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
-      "PciRoot(0x0)%s/Pci(0x%Lx,0x%Lx)/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 >= 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
@@ -753,7 +849,8 @@ TranslatePciOfwNodes (
     Written = UnicodeSPrintAsciiFormat (\r
       Translated,\r
       *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
-      "PciRoot(0x0)%s/Pci(0x%Lx,0x%Lx)/Floppy(0x%Lx)",\r
+      "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Floppy(0x%Lx)",\r
+      PciRoot,\r
       Bridges,\r
       PciDevFun[0],\r
       PciDevFun[1],\r
@@ -781,7 +878,8 @@ TranslatePciOfwNodes (
     Written = UnicodeSPrintAsciiFormat (\r
       Translated,\r
       *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
-      "PciRoot(0x0)%s/Pci(0x%Lx,0x%Lx)/HD(",\r
+      "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/HD(",\r
+      PciRoot,\r
       Bridges,\r
       PciDevFun[0],\r
       PciDevFun[1]\r
@@ -825,13 +923,69 @@ TranslatePciOfwNodes (
     Written = UnicodeSPrintAsciiFormat (\r
       Translated,\r
       *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
-      "PciRoot(0x0)%s/Pci(0x%Lx,0x%Lx)/Scsi(0x%Lx,0x%Lx)",\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
@@ -849,7 +1003,8 @@ TranslatePciOfwNodes (
     Written = UnicodeSPrintAsciiFormat (\r
       Translated,\r
       *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
-      "PciRoot(0x0)%s/Pci(0x%Lx,0x%Lx)",\r
+      "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",\r
+      PciRoot,\r
       Bridges,\r
       PciDevFun[0],\r
       PciDevFun[1]\r
@@ -1057,6 +1212,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
@@ -1077,14 +1237,19 @@ 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
@@ -1092,8 +1257,8 @@ TranslateOfwNodes (
   Status = RETURN_UNSUPPORTED;\r
 \r
   if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {\r
-    Status = TranslatePciOfwNodes (OfwNode, NumNodes, Translated,\r
-               TranslatedSize);\r
+    Status = TranslatePciOfwNodes (OfwNode, NumNodes, ExtraPciRoots,\r
+               Translated, TranslatedSize);\r
   }\r
   if (Status == RETURN_UNSUPPORTED &&\r
       FeaturePcdGet (PcdQemuBootOrderMmioTranslation)) {\r
@@ -1117,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
@@ -1142,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
@@ -1154,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
@@ -1204,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
@@ -1219,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
@@ -1512,6 +1695,8 @@ 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
 \r
@@ -1556,13 +1741,24 @@ SetBootOrderFromQemu (
     goto ErrorFreeBootOrder;\r
   }\r
 \r
+  if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {\r
+    Status = CreateExtraRootBusMap (&ExtraPciRoots);\r
+    if (EFI_ERROR (Status)) {\r
+      goto ErrorFreeActiveOption;\r
+    }\r
+  } else {\r
+    ExtraPciRoots = NULL;\r
+  }\r
+\r
   //\r
   // translate each OpenFirmware path\r
   //\r
   TranslatedSize = sizeof (Translated) / sizeof (Translated[0]);\r
-  Status = TranslateOfwPath (&FwCfgPtr, 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
@@ -1582,7 +1778,7 @@ SetBootOrderFromQemu (
           //\r
           Status = BootOrderAppend (&BootOrder, &ActiveOption[Idx]);\r
           if (Status != RETURN_SUCCESS) {\r
-            goto ErrorFreeActiveOption;\r
+            goto ErrorFreeExtraPciRoots;\r
           }\r
           break;\r
         }\r
@@ -1590,7 +1786,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
@@ -1601,7 +1798,7 @@ SetBootOrderFromQemu (
     //\r
     Status = BootOrderComplete (&BootOrder, ActiveOption, ActiveCount);\r
     if (RETURN_ERROR (Status)) {\r
-      goto ErrorFreeActiveOption;\r
+      goto ErrorFreeExtraPciRoots;\r
     }\r
 \r
     //\r
@@ -1619,13 +1816,18 @@ 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