OvmfPkg/QemuBootOrderLib: add ConnectDevicesFromQemu()
[mirror_edk2.git] / OvmfPkg / Library / QemuBootOrderLib / QemuBootOrderLib.c
index a4213cf..15e4c67 100644 (file)
@@ -1453,6 +1453,156 @@ TranslateOfwPath (
 }\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
+  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
+  @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
+  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 (&FwCfgPtr, ExtraPciRoots, Translated,\r
+             &TranslatedSize);\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
+    // 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
+    // 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
+    ++NumConnected;\r
+    //\r
+    // Move to the next OFW devpath.\r
+    //\r
+    TranslatedSize = ARRAY_SIZE (Translated);\r
+    Status = TranslateOfwPath (&FwCfgPtr, ExtraPciRoots, Translated,\r
+               &TranslatedSize);\r
+  }\r
+\r
+  if (Status == RETURN_NOT_FOUND && NumConnected > 0) {\r
+    DEBUG ((DEBUG_INFO, "%a: %Lu OpenFirmware device path(s) connected\n",\r
+      __FUNCTION__, (UINT64)NumConnected));\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
 /**\r
 \r
   Convert the UEFI DevicePath to full text representation with DevPathToText,\r
@@ -1743,8 +1893,8 @@ PruneBootVariables (
   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
+  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