Rewrite the BootOrder NvVar based on QEMU's "bootorder" fw_cfg file.\r
\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
\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
- Append BootOptionId to BootOrder, reallocating the latter if needed.\r
+\r
+/**\r
+\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
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
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
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
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
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
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
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
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
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
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
+ 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
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
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