]> 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 9f94e2424a02b19267a0fb7d4070ad24b0720488..bd0fb768779bd7c84ad4a299dfbae4bfbfb5b110 100644 (file)
@@ -1,7 +1,8 @@
 /** @file\r
   Rewrite the BootOrder NvVar based on QEMU's "bootorder" fw_cfg file.\r
 \r
-  Copyright (C) 2012, Red Hat, Inc.\r
+  Copyright (C) 2012 - 2013, Red Hat, Inc.\r
+  Copyright (c) 2013, 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
@@ -20,7 +21,7 @@
 #include <Library/UefiRuntimeServicesTableLib.h>\r
 #include <Library/BaseLib.h>\r
 #include <Library/PrintLib.h>\r
-#include <Protocol/DevicePathToText.h>\r
+#include <Library/DevicePathLib.h>\r
 #include <Guid/GlobalVariable.h>\r
 \r
 \r
@@ -237,17 +238,28 @@ typedef struct {
 } 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
+} 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
@@ -255,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
@@ -277,7 +289,81 @@ 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
+\r
+/**\r
+\r
+  Create an array of ACTIVE_OPTION elements for a boot option list.\r
+\r
+  @param[in]  BootOptionList  A boot option list, created with\r
+                              BdsLibEnumerateAllBootOption().\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
+\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
+\r
+  @retval RETURN_OUT_OF_RESOURCES  Memory allocation failed.\r
+\r
+**/\r
+STATIC\r
+RETURN_STATUS\r
+CollectActiveOptions (\r
+  IN   CONST LIST_ENTRY *BootOptionList,\r
+  OUT  ACTIVE_OPTION    **ActiveOption,\r
+  OUT  UINTN            *Count\r
+  )\r
+{\r
+  UINTN ScanMode;\r
+\r
+  *ActiveOption = NULL;\r
+\r
+  //\r
+  // Scan the list twice:\r
+  // - count active entries,\r
+  // - 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
+        if (ScanMode == 1) {\r
+          (*ActiveOption)[*Count].BootOption = Current;\r
+          (*ActiveOption)[*Count].Appended   = FALSE;\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
+      *ActiveOption = AllocatePool (*Count * sizeof **ActiveOption);\r
+      if (*ActiveOption == NULL) {\r
+        return RETURN_OUT_OF_RESOURCES;\r
+      }\r
+    }\r
+  }\r
   return RETURN_SUCCESS;\r
 }\r
 \r
@@ -677,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
@@ -763,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
@@ -783,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
@@ -866,22 +951,49 @@ BOOLEAN
 Match (\r
   IN  CONST CHAR16                           *Translated,\r
   IN  UINTN                                  TranslatedLength,\r
-  IN  CONST EFI_DEVICE_PATH_PROTOCOL         *DevicePath,\r
-  IN  CONST EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *DevPathToText\r
+  IN  CONST EFI_DEVICE_PATH_PROTOCOL         *DevicePath\r
   )\r
 {\r
   CHAR16  *Converted;\r
   BOOLEAN Result;\r
 \r
-  Converted = DevPathToText->ConvertDevicePathToText (\r
-                               DevicePath,\r
-                               FALSE, // DisplayOnly\r
-                               FALSE  // AllowShortcuts\r
-                               );\r
+  Converted = ConvertDevicePathToText (\r
+                DevicePath,\r
+                FALSE, // DisplayOnly\r
+                FALSE  // AllowShortcuts\r
+                );\r
   if (Converted == NULL) {\r
     return FALSE;\r
   }\r
 \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
+  //\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
+      goto Exit;\r
+    }\r
+    AbsConverted = ConvertDevicePathToText (AbsDevicePath, FALSE, FALSE);\r
+    if (AbsConverted == NULL) {\r
+      goto Exit;\r
+    }\r
+    DEBUG ((DEBUG_VERBOSE,\r
+      "%a: expanded relative device path \"%s\" for prefix matching\n",\r
+      __FUNCTION__, Converted));\r
+    FreePool (Converted);\r
+    Converted = AbsConverted;\r
+  }\r
+\r
   //\r
   // Is Translated a prefix of Converted?\r
   //\r
@@ -893,11 +1005,152 @@ Match (
     Converted,\r
     Result ? "match" : "no match"\r
     ));\r
+Exit:\r
   FreePool (Converted);\r
   return Result;\r
 }\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
@@ -933,28 +1186,18 @@ SetBootOrderFromQemu (
   )\r
 {\r
   RETURN_STATUS                    Status;\r
-\r
-  EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *DevPathToText;\r
-\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
 \r
   UINTN                            TranslatedSize;\r
   CHAR16                           Translated[TRANSLATION_OUTPUT_SIZE];\r
 \r
-  Status = gBS->LocateProtocol (\r
-                  &gEfiDevicePathToTextProtocolGuid,\r
-                  NULL, // optional registration key\r
-                  (VOID **) &DevPathToText\r
-                  );\r
-  if (Status != EFI_SUCCESS) {\r
-    return Status;\r
-  }\r
-\r
   Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);\r
   if (Status != RETURN_SUCCESS) {\r
     return Status;\r
@@ -991,6 +1234,11 @@ SetBootOrderFromQemu (
     goto ErrorFreeFwCfg;\r
   }\r
 \r
+  Status = CollectActiveOptions (BootOptionList, &ActiveOption, &ActiveCount);\r
+  if (RETURN_ERROR (Status)) {\r
+    goto ErrorFreeBootOrder;\r
+  }\r
+\r
   //\r
   // translate each OpenFirmware path\r
   //\r
@@ -1000,39 +1248,28 @@ SetBootOrderFromQemu (
          Status == RETURN_UNSUPPORTED ||\r
          Status == RETURN_BUFFER_TOO_SMALL) {\r
     if (Status == RETURN_SUCCESS) {\r
-      CONST LIST_ENTRY *Link;\r
+      UINTN Idx;\r
 \r
       //\r
-      // match translated OpenFirmware path against all enumerated boot options\r
+      // match translated OpenFirmware path against all active boot options\r
       //\r
-      for (Link = BootOptionList->ForwardLink; Link != BootOptionList;\r
-           Link = Link->ForwardLink) {\r
-        CONST BDS_COMMON_OPTION *BootOption;\r
-\r
-        BootOption = CR (\r
-                       Link,\r
-                       BDS_COMMON_OPTION,\r
-                       Link,\r
-                       BDS_LOAD_OPTION_SIGNATURE\r
-                       );\r
-        if (IS_LOAD_OPTION_TYPE (BootOption->Attribute, LOAD_OPTION_ACTIVE) &&\r
-            Match (\r
+      for (Idx = 0; Idx < ActiveCount; ++Idx) {\r
+        if (Match (\r
               Translated,\r
               TranslatedSize, // contains length, not size, in CHAR16's here\r
-              BootOption->DevicePath,\r
-              DevPathToText\r
+              ActiveOption[Idx].BootOption->DevicePath\r
               )\r
             ) {\r
           //\r
           // match found, store ID and continue with next OpenFirmware path\r
           //\r
-          Status = BootOrderAppend (&BootOrder, BootOption->BootCurrent);\r
+          Status = BootOrderAppend (&BootOrder, &ActiveOption[Idx]);\r
           if (Status != RETURN_SUCCESS) {\r
-            goto ErrorFreeBootOrder;\r
+            goto ErrorFreeActiveOption;\r
           }\r
           break;\r
         }\r
-      } // scanned all enumerated boot options\r
+      } // scanned all active boot options\r
     }   // translation successful\r
 \r
     TranslatedSize = sizeof (Translated) / sizeof (Translated[0]);\r
@@ -1042,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
@@ -1054,14 +1300,18 @@ 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
+  FreePool (ActiveOption);\r
+\r
 ErrorFreeBootOrder:\r
   FreePool (BootOrder.Data);\r
 \r