]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/Library/PlatformBdsLib/QemuBootOrder.c
ArmVirtualizationPkg: PlatformIntelBdsLib: add basic policy
[mirror_edk2.git] / OvmfPkg / Library / PlatformBdsLib / QemuBootOrder.c
index 72f78f1ca0d68b4bbabd5ac2fdc3966d1b34396b..bd0fb768779bd7c84ad4a299dfbae4bfbfb5b110 100644 (file)
@@ -244,20 +244,22 @@ typedef struct {
 **/\r
 typedef struct {\r
   CONST BDS_COMMON_OPTION *BootOption; // reference only, no ownership\r
+  BOOLEAN                 Appended;    // has been added to a BOOT_ORDER?\r
 } ACTIVE_OPTION;\r
 \r
 \r
 /**\r
 \r
-  Append BootOptionId to BootOrder, reallocating the latter if needed.\r
+  Append an active boot option to BootOrder, reallocating the latter if needed.\r
 \r
   @param[in out] BootOrder     The structure pointing to the array and holding\r
                                allocation and usage counters.\r
 \r
-  @param[in]     BootOptionId  The value to append to the array.\r
+  @param[in]     ActiveOption  The active boot option whose ID should be\r
+                               appended to the array.\r
 \r
 \r
-  @retval RETURN_SUCCESS           BootOptionId appended.\r
+  @retval RETURN_SUCCESS           ID of ActiveOption appended.\r
 \r
   @retval RETURN_OUT_OF_RESOURCES  Memory reallocation failed.\r
 \r
@@ -265,8 +267,8 @@ typedef struct {
 STATIC\r
 RETURN_STATUS\r
 BootOrderAppend (\r
-  IN OUT  BOOT_ORDER *BootOrder,\r
-  IN      UINT16     BootOptionId\r
+  IN OUT  BOOT_ORDER    *BootOrder,\r
+  IN OUT  ACTIVE_OPTION *ActiveOption\r
   )\r
 {\r
   if (BootOrder->Produced == BootOrder->Allocated) {\r
@@ -287,7 +289,9 @@ BootOrderAppend (
     BootOrder->Data      = DataNew;\r
   }\r
 \r
-  BootOrder->Data[BootOrder->Produced++] = BootOptionId;\r
+  BootOrder->Data[BootOrder->Produced++] =\r
+                                         ActiveOption->BootOption->BootCurrent;\r
+  ActiveOption->Appended = TRUE;\r
   return RETURN_SUCCESS;\r
 }\r
 \r
@@ -343,6 +347,7 @@ CollectActiveOptions (
       if (IS_LOAD_OPTION_TYPE (Current->Attribute, LOAD_OPTION_ACTIVE)) {\r
         if (ScanMode == 1) {\r
           (*ActiveOption)[*Count].BootOption = Current;\r
+          (*ActiveOption)[*Count].Appended   = FALSE;\r
         }\r
         ++*Count;\r
       }\r
@@ -758,37 +763,27 @@ TranslateOfwNodes (
       TargetLun[0],\r
       TargetLun[1]\r
       );\r
-  } else if (NumNodes >= 3 &&\r
-             SubstringEq (OfwNode[1].DriverName, "ethernet") &&\r
-             SubstringEq (OfwNode[2].DriverName, "ethernet-phy")\r
-             ) {\r
+  } else {\r
     //\r
-    // OpenFirmware device path (Ethernet NIC):\r
+    // Generic OpenFirmware device path for PCI devices:\r
     //\r
-    //   /pci@i0cf8/ethernet@3[,2]/ethernet-phy@0\r
-    //        ^              ^                  ^\r
-    //        |              |                  fixed\r
+    //   /pci@i0cf8/ethernet@3[,2]\r
+    //        ^              ^\r
     //        |              PCI slot[, function] holding Ethernet card\r
     //        PCI root at system bus port, PIO\r
     //\r
     // UEFI device path prefix (dependent on presence of nonzero PCI function):\r
     //\r
-    //   PciRoot(0x0)/Pci(0x3,0x0)/MAC(525400E15EEF,0x1)\r
-    //   PciRoot(0x0)/Pci(0x3,0x2)/MAC(525400E15EEF,0x1)\r
-    //                                 ^            ^\r
-    //                                 MAC address  IfType (1 == Ethernet)\r
-    //\r
-    // (Some UEFI NIC drivers don't set 0x1 for IfType.)\r
+    //   PciRoot(0x0)/Pci(0x3,0x0)\r
+    //   PciRoot(0x0)/Pci(0x3,0x2)\r
     //\r
     Written = UnicodeSPrintAsciiFormat (\r
       Translated,\r
       *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
-      "PciRoot(0x0)/Pci(0x%x,0x%x)/MAC",\r
+      "PciRoot(0x0)/Pci(0x%x,0x%x)",\r
       PciDevFun[0],\r
       PciDevFun[1]\r
       );\r
-  } else {\r
-    return RETURN_UNSUPPORTED;\r
   }\r
 \r
   //\r
@@ -844,8 +839,11 @@ TranslateOfwNodes (
                                     the current implementation. Further calls\r
                                     to this function are possible.\r
 \r
-  @retval RETURN_NOT_FOUND          Translation terminated, *Ptr was (and is)\r
-                                    pointing to an empty string.\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
+                                    (ie. "HALT" is consumed transparently when\r
+                                    present).\r
 \r
   @retval RETURN_INVALID_PARAMETER  Parse error. This is a permanent error.\r
 \r
@@ -864,8 +862,14 @@ TranslateOfwPath (
   BOOLEAN       IsFinal;\r
   OFW_NODE      Skip;\r
 \r
+  IsFinal = FALSE;\r
   NumNodes = 0;\r
-  Status = ParseOfwNode (Ptr, &Node[NumNodes], &IsFinal);\r
+  if (AsciiStrCmp (*Ptr, "HALT") == 0) {\r
+    *Ptr += 4;\r
+    Status = RETURN_NOT_FOUND;\r
+  } else {\r
+    Status = ParseOfwNode (Ptr, &Node[NumNodes], &IsFinal);\r
+  }\r
 \r
   if (Status == RETURN_NOT_FOUND) {\r
     DEBUG ((DEBUG_VERBOSE, "%a: no more nodes\n", __FUNCTION__));\r
@@ -1007,6 +1011,146 @@ Exit:
 }\r
 \r
 \r
+/**\r
+  Append some of the unselected active boot options to the boot order.\r
+\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
+\r
+  @param[in,out] BootOrder     The structure holding the boot order to\r
+                               complete. The caller is responsible for\r
+                               initializing (and potentially populating) it\r
+                               before calling this function.\r
+\r
+  @param[in,out] ActiveOption  The array of active boot options to scan.\r
+                               Entries marked as Appended will be skipped.\r
+                               Those of the rest that satisfy the survival\r
+                               policy will be added to BootOrder with\r
+                               BootOrderAppend().\r
+\r
+  @param[in]     ActiveCount   Number of elements in ActiveOption.\r
+\r
+\r
+  @retval RETURN_SUCCESS  BootOrder has been extended with any eligible boot\r
+                          options.\r
+\r
+  @return                 Error codes returned by BootOrderAppend().\r
+**/\r
+STATIC\r
+RETURN_STATUS\r
+BootOrderComplete (\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
+\r
+  Status = RETURN_SUCCESS;\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
+\r
+      Current = ActiveOption[Idx].BootOption;\r
+      FirstNode = Current->DevicePath;\r
+      if (FirstNode != NULL) {\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
+          Converted = ConvFallBack;\r
+        }\r
+\r
+        Keep = TRUE;\r
+        if (DevicePathType(FirstNode) == MEDIA_DEVICE_PATH &&\r
+            DevicePathSubType(FirstNode) == MEDIA_HARDDRIVE_DP) {\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
+            //\r
+            // drop PciRoot()\r
+            //\r
+            Keep = FALSE;\r
+          }\r
+        }\r
+\r
+        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
+          }\r
+        } else {\r
+          DEBUG ((DEBUG_VERBOSE, "%a: dropping \"%s\"\n", __FUNCTION__,\r
+            Converted));\r
+        }\r
+\r
+        if (Converted != ConvFallBack) {\r
+          FreePool (Converted);\r
+        }\r
+      }\r
+    }\r
+    ++Idx;\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
+  policy").\r
+\r
+  @param[in]  ActiveOption  The array of active boot options to scan. Each\r
+                            entry not marked as appended will trigger the\r
+                            deletion of the matching Boot#### variable.\r
+\r
+  @param[in]  ActiveCount   Number of elements in ActiveOption.\r
+**/\r
+STATIC\r
+VOID\r
+PruneBootVariables (\r
+  IN  CONST ACTIVE_OPTION *ActiveOption,\r
+  IN  UINTN               ActiveCount\r
+  )\r
+{\r
+  UINTN Idx;\r
+\r
+  for (Idx = 0; Idx < ActiveCount; ++Idx) {\r
+    if (!ActiveOption[Idx].Appended) {\r
+      CHAR16 VariableName[9];\r
+\r
+      UnicodeSPrintAsciiFormat (VariableName, sizeof VariableName, "Boot%04x",\r
+        ActiveOption[Idx].BootOption->BootCurrent);\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
+             0,   // Attributes, 0 means deletion\r
+             0,   // DataSize, 0 means deletion\r
+             NULL // Data\r
+             );\r
+    }\r
+  }\r
+}\r
+\r
+\r
 /**\r
 \r
   Set the boot order based on configuration retrieved from QEMU.\r
@@ -1119,8 +1263,7 @@ SetBootOrderFromQemu (
           //\r
           // match found, store ID and continue with next OpenFirmware path\r
           //\r
-          Status = BootOrderAppend (&BootOrder,\r
-                     ActiveOption[Idx].BootOption->BootCurrent);\r
+          Status = BootOrderAppend (&BootOrder, &ActiveOption[Idx]);\r
           if (Status != RETURN_SUCCESS) {\r
             goto ErrorFreeActiveOption;\r
           }\r
@@ -1136,6 +1279,15 @@ SetBootOrderFromQemu (
   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
+    // should be preserved at the end of the boot order.\r
+    //\r
+    Status = BootOrderComplete (&BootOrder, ActiveOption, ActiveCount);\r
+    if (RETURN_ERROR (Status)) {\r
+      goto ErrorFreeActiveOption;\r
+    }\r
+\r
+    //\r
     // See Table 10 in the UEFI Spec 2.3.1 with Errata C for the required\r
     // attributes.\r
     //\r
@@ -1148,12 +1300,13 @@ SetBootOrderFromQemu (
                     BootOrder.Produced * sizeof (*BootOrder.Data),\r
                     BootOrder.Data\r
                     );\r
-    DEBUG ((\r
-      DEBUG_INFO,\r
-      "%a: setting BootOrder: %a\n",\r
-      __FUNCTION__,\r
-      Status == EFI_SUCCESS ? "success" : "error"\r
-      ));\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((DEBUG_ERROR, "%a: setting BootOrder: %r\n", __FUNCTION__, Status));\r
+      goto ErrorFreeActiveOption;\r
+    }\r
+\r
+    DEBUG ((DEBUG_INFO, "%a: setting BootOrder: success\n", __FUNCTION__));\r
+    PruneBootVariables (ActiveOption, ActiveCount);\r
   }\r
 \r
 ErrorFreeActiveOption:\r