ArmPlatformPkg/Bds: Added boot options reordering
authorRonald Cron <ronald.cron@arm.com>
Tue, 29 Jul 2014 14:19:57 +0000 (14:19 +0000)
committeroliviermartin <oliviermartin@6f19259b-4bc3-4df7-8a09-765794883524>
Tue, 29 Jul 2014 14:19:57 +0000 (14:19 +0000)
Added the reordering of the boot options feature to the boot manager.
The BootMenuSelectBootOption() has been split into
DisplayBootOptions() that only displays the boot options and
SelectBootOptions() that asks to select one.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Ronald Cron <ronald.cron@arm.com>
Reviewed-By: Olivier Martin <olivier.martin@arm.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@15718 6f19259b-4bc3-4df7-8a09-765794883524

ArmPlatformPkg/Bds/BdsInternal.h
ArmPlatformPkg/Bds/BootMenu.c

index 32e4b65d2bab1bd371dc62dd1bc2ce8c0f186c70..f14e28591a095794b43835dbe9d55eca933bc796 100644 (file)
@@ -46,6 +46,7 @@
 \r
 #define UPDATE_BOOT_ENTRY L"Update entry: "\r
 #define DELETE_BOOT_ENTRY L"Delete entry: "\r
+#define MOVE_BOOT_ENTRY   L"Move entry: "\r
 \r
 typedef enum {\r
     BDS_LOADER_EFI_APPLICATION = 0,\r
index 065e2ee9c1d48e149a1091673d78a613759d9831..b96a1c3f923cc30a69e82974a0e44b526f7af839 100644 (file)
 extern EFI_HANDLE mImageHandle;\r
 extern BDS_LOAD_OPTION_SUPPORT *BdsLoadOptionSupportList;\r
 \r
+/**\r
+  Worker function that displays the list of boot options that is passed in.\r
+\r
+  The function loops over the entries of the list of boot options that is passed\r
+  in. For each entry, the boot option description is displayed on a single line\r
+  along with the position of the option in the list. In debug mode, the UEFI\r
+  device path and the arguments of the boot option are displayed as well in\r
+  subsequent lines.\r
+\r
+  @param[in]  BootOptionsList  List of the boot options\r
+\r
+**/\r
+STATIC\r
+VOID\r
+DisplayBootOptions (\r
+  IN  LIST_ENTRY*   BootOptionsList\r
+  )\r
+{\r
+  EFI_STATUS        Status;\r
+  UINTN             BootOptionCount;\r
+  LIST_ENTRY       *Entry;\r
+  BDS_LOAD_OPTION  *BdsLoadOption;\r
+  BOOLEAN           IsUnicode;\r
+\r
+  BootOptionCount = 0 ;\r
+  for (Entry = GetFirstNode (BootOptionsList);\r
+       !IsNull (BootOptionsList, Entry);\r
+       Entry = GetNextNode (BootOptionsList, Entry)\r
+      ) {\r
+\r
+    BdsLoadOption = LOAD_OPTION_FROM_LINK (Entry);\r
+    Print (L"[%d] %s\n", ++BootOptionCount, BdsLoadOption->Description);\r
+\r
+    DEBUG_CODE_BEGIN ();\r
+      CHAR16*                           DevicePathTxt;\r
+      EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;\r
+      ARM_BDS_LOADER_TYPE               LoaderType;\r
+      ARM_BDS_LOADER_OPTIONAL_DATA*     OptionalData;\r
+\r
+      Status = gBS->LocateProtocol (\r
+                     &gEfiDevicePathToTextProtocolGuid,\r
+                     NULL,\r
+                     (VOID **)&DevicePathToTextProtocol\r
+                     );\r
+      ASSERT_EFI_ERROR (Status);\r
+      DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (\r
+                                                  BdsLoadOption->FilePathList,\r
+                                                  TRUE,\r
+                                                  TRUE\r
+                                                  );\r
+      Print (L"\t- %s\n", DevicePathTxt);\r
+\r
+      OptionalData = BdsLoadOption->OptionalData;\r
+      if (IS_ARM_BDS_BOOTENTRY (BdsLoadOption)) {\r
+        LoaderType = (ARM_BDS_LOADER_TYPE)ReadUnaligned32 ((CONST UINT32*)&OptionalData->Header.LoaderType);\r
+        if ((LoaderType == BDS_LOADER_KERNEL_LINUX_ATAG) ||\r
+            (LoaderType == BDS_LOADER_KERNEL_LINUX_FDT )   ) {\r
+          Print (L"\t- Arguments: %a\n", &OptionalData->Arguments.LinuxArguments + 1);\r
+        }\r
+      } else if (OptionalData != NULL) {\r
+        if (IsPrintableString (OptionalData, &IsUnicode)) {\r
+          if (IsUnicode) {\r
+            Print (L"\t- Arguments: %s\n", OptionalData);\r
+          } else {\r
+            AsciiPrint ("\t- Arguments: %a\n", OptionalData);\r
+          }\r
+        }\r
+      }\r
+\r
+      FreePool (DevicePathTxt);\r
+    DEBUG_CODE_END ();\r
+  }\r
+}\r
+\r
+/**\r
+  Worker function that asks for a boot option to be selected and returns a\r
+  pointer to the structure describing the selected boot option.\r
+\r
+  @param[in]  BootOptionsList  List of the boot options\r
+\r
+  @retval     EFI_SUCCESS      Selection succeeded\r
+  @retval     !EFI_SUCCESS     Input error or input cancelled\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+SelectBootOption (\r
+  IN  LIST_ENTRY*               BootOptionsList,\r
+  IN  CONST CHAR16*             InputStatement,\r
+  OUT BDS_LOAD_OPTION_ENTRY**   BdsLoadOptionEntry\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+  UINTN                         BootOptionCount;\r
+  UINT16                       *BootOrder;\r
+  LIST_ENTRY*                   Entry;\r
+  UINTN                         BootOptionSelected;\r
+  UINTN                         Index;\r
+\r
+  // Get the number of boot options\r
+  Status = GetGlobalEnvironmentVariable (\r
+            L"BootOrder", NULL, &BootOptionCount, (VOID**)&BootOrder\r
+            );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ErrorExit;\r
+  }\r
+  FreePool (BootOrder);\r
+  BootOptionCount /= sizeof (UINT16);\r
 \r
+  // Check if a valid boot option(s) is found\r
+  if (BootOptionCount == 0) {\r
+    if (StrCmp (InputStatement, DELETE_BOOT_ENTRY) == 0) {\r
+      Print (L"Nothing to remove!\n");\r
+    } else if (StrCmp (InputStatement, UPDATE_BOOT_ENTRY) == 0) {\r
+      Print (L"Nothing to update!\n");\r
+    } else if (StrCmp (InputStatement, MOVE_BOOT_ENTRY) == 0) {\r
+      Print (L"Nothing to move!\n");\r
+    } else {\r
+      Print (L"No supported Boot Entry.\n");\r
+    }\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  // Get the index of the boot device to delete\r
+  BootOptionSelected = 0;\r
+  while (BootOptionSelected == 0) {\r
+    Print (InputStatement);\r
+    Status = GetHIInputInteger (&BootOptionSelected);\r
+    if (EFI_ERROR (Status)) {\r
+      Print (L"\n");\r
+      goto ErrorExit;\r
+    } else if ((BootOptionSelected == 0) || (BootOptionSelected > BootOptionCount)) {\r
+      Print (L"Invalid input (max %d)\n", BootOptionCount);\r
+      BootOptionSelected = 0;\r
+    }\r
+  }\r
+\r
+  // Get the structure of the Boot device to delete\r
+  Index = 1;\r
+  for (Entry = GetFirstNode (BootOptionsList);\r
+       !IsNull (BootOptionsList, Entry);\r
+       Entry = GetNextNode (BootOptionsList,Entry)\r
+       )\r
+  {\r
+    if (Index == BootOptionSelected) {\r
+      *BdsLoadOptionEntry = LOAD_OPTION_ENTRY_FROM_LINK (Entry);\r
+      break;\r
+    }\r
+    Index++;\r
+  }\r
+\r
+ErrorExit:\r
+  return Status;\r
+}\r
+\r
+STATIC\r
 EFI_STATUS\r
 SelectBootDevice (\r
   OUT BDS_SUPPORTED_DEVICE** SupportedBootDevice\r
@@ -254,109 +409,6 @@ EXIT:
   return Status;\r
 }\r
 \r
-STATIC\r
-EFI_STATUS\r
-BootMenuSelectBootOption (\r
-  IN  LIST_ENTRY*               BootOptionsList,\r
-  IN  CONST CHAR16*             InputStatement,\r
-  OUT BDS_LOAD_OPTION_ENTRY**   BdsLoadOptionEntry\r
-  )\r
-{\r
-  EFI_STATUS                    Status;\r
-  LIST_ENTRY*                   Entry;\r
-  BDS_LOAD_OPTION*              BdsLoadOption;\r
-  UINTN                         BootOptionSelected;\r
-  UINTN                         BootOptionCount;\r
-  UINTN                         Index;\r
-  BOOLEAN                       IsUnicode;\r
-\r
-  // Display the list of supported boot devices\r
-  BootOptionCount = 0;\r
-  for (Entry = GetFirstNode (BootOptionsList);\r
-       !IsNull (BootOptionsList,Entry);\r
-       Entry = GetNextNode (BootOptionsList, Entry)\r
-       )\r
-  {\r
-    BdsLoadOption = LOAD_OPTION_FROM_LINK(Entry);\r
-\r
-    Print (L"[%d] %s\n", (BootOptionCount + 1), BdsLoadOption->Description);\r
-\r
-    DEBUG_CODE_BEGIN();\r
-      CHAR16*                           DevicePathTxt;\r
-      EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;\r
-      ARM_BDS_LOADER_TYPE               LoaderType;\r
-      ARM_BDS_LOADER_OPTIONAL_DATA*     OptionalData;\r
-\r
-      Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);\r
-      ASSERT_EFI_ERROR(Status);\r
-      DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText(BdsLoadOption->FilePathList,TRUE,TRUE);\r
-\r
-      Print(L"\t- %s\n",DevicePathTxt);\r
-      OptionalData = BdsLoadOption->OptionalData;\r
-      if (IS_ARM_BDS_BOOTENTRY (BdsLoadOption)) {\r
-        LoaderType = (ARM_BDS_LOADER_TYPE)ReadUnaligned32 ((CONST UINT32*)&OptionalData->Header.LoaderType);\r
-        if ((LoaderType == BDS_LOADER_KERNEL_LINUX_ATAG) || (LoaderType == BDS_LOADER_KERNEL_LINUX_FDT)) {\r
-          Print (L"\t- Arguments: %a\n",&OptionalData->Arguments.LinuxArguments + 1);\r
-        }\r
-      } else if (OptionalData != NULL) {\r
-        if (IsPrintableString (OptionalData, &IsUnicode)) {\r
-          if (IsUnicode) {\r
-            Print (L"\t- Arguments: %s\n", OptionalData);\r
-          } else {\r
-            AsciiPrint ("\t- Arguments: %a\n", OptionalData);\r
-          }\r
-        }\r
-      }\r
-\r
-      FreePool(DevicePathTxt);\r
-    DEBUG_CODE_END();\r
-\r
-    BootOptionCount++;\r
-  }\r
-\r
-  // Check if a valid boot option(s) is found\r
-  if (BootOptionCount == 0) {\r
-    if (StrCmp (InputStatement, DELETE_BOOT_ENTRY) == 0) {\r
-      Print (L"Nothing to remove!\n");\r
-    } else if (StrCmp (InputStatement, UPDATE_BOOT_ENTRY) == 0) {\r
-      Print (L"Couldn't find valid boot entries\n");\r
-    } else{\r
-      Print (L"No supported Boot Entry.\n");\r
-    }\r
-\r
-    return EFI_NOT_FOUND;\r
-  }\r
-\r
-  // Get the index of the boot device to delete\r
-  BootOptionSelected = 0;\r
-  while (BootOptionSelected == 0) {\r
-    Print(InputStatement);\r
-    Status = GetHIInputInteger (&BootOptionSelected);\r
-    if (EFI_ERROR(Status)) {\r
-      return Status;\r
-    } else if ((BootOptionSelected == 0) || (BootOptionSelected > BootOptionCount)) {\r
-      Print(L"Invalid input (max %d)\n",BootOptionCount);\r
-      BootOptionSelected = 0;\r
-    }\r
-  }\r
-\r
-  // Get the structure of the Boot device to delete\r
-  Index = 1;\r
-  for (Entry = GetFirstNode (BootOptionsList);\r
-       !IsNull (BootOptionsList, Entry);\r
-       Entry = GetNextNode (BootOptionsList,Entry)\r
-       )\r
-  {\r
-    if (Index == BootOptionSelected) {\r
-      *BdsLoadOptionEntry = LOAD_OPTION_ENTRY_FROM_LINK(Entry);\r
-      break;\r
-    }\r
-    Index++;\r
-  }\r
-\r
-  return EFI_SUCCESS;\r
-}\r
-\r
 EFI_STATUS\r
 BootMenuRemoveBootOption (\r
   IN LIST_ENTRY *BootOptionsList\r
@@ -365,8 +417,9 @@ BootMenuRemoveBootOption (
   EFI_STATUS                    Status;\r
   BDS_LOAD_OPTION_ENTRY*        BootOptionEntry;\r
 \r
-  Status = BootMenuSelectBootOption (BootOptionsList, DELETE_BOOT_ENTRY, &BootOptionEntry);\r
-  if (EFI_ERROR(Status)) {\r
+  DisplayBootOptions (BootOptionsList);\r
+  Status = SelectBootOption (BootOptionsList, DELETE_BOOT_ENTRY, &BootOptionEntry);\r
+  if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
@@ -410,8 +463,9 @@ BootMenuUpdateBootOption (
   BOOLEAN                       IsPrintable;\r
   BOOLEAN                       IsUnicode;\r
 \r
-  Status = BootMenuSelectBootOption (BootOptionsList, UPDATE_BOOT_ENTRY, &BootOptionEntry);\r
-  if (EFI_ERROR(Status)) {\r
+  DisplayBootOptions (BootOptionsList);\r
+  Status = SelectBootOption (BootOptionsList, UPDATE_BOOT_ENTRY, &BootOptionEntry);\r
+  if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
   BootOption = BootOptionEntry->BdsLoadOption;\r
@@ -589,6 +643,160 @@ EXIT:
   return Status;\r
 }\r
 \r
+/**\r
+  Reorder boot options\r
+\r
+  Ask for the boot option to move and then move it when up or down arrows\r
+  are pressed. This function is called when the user selects the "Reorder Boot\r
+  Device Entries" entry in the boot manager menu.\r
+  The order of the boot options in BootOptionList and in the UEFI BootOrder\r
+  global variable are kept coherent until the user confirm his reordering (ie:\r
+  he does not exit by pressing escape).\r
+\r
+  @param[in]  BootOptionsList  List of the boot devices constructed in\r
+                               BootMenuMain()\r
+\r
+  @retval  EFI_SUCCESS   No error encountered.\r
+  @retval  !EFI_SUCCESS  An error has occured either in the selection of the\r
+                         boot option to move or while interacting with the user.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+BootMenuReorderBootOptions (\r
+  IN LIST_ENTRY *BootOptionsList\r
+  )\r
+{\r
+  EFI_STATUS              Status;\r
+  BDS_LOAD_OPTION_ENTRY  *BootOptionEntry;\r
+  LIST_ENTRY             *SelectedEntry;\r
+  LIST_ENTRY             *PrevEntry;\r
+  BOOLEAN                 Move;\r
+  BOOLEAN                 Save;\r
+  BOOLEAN                 Cancel;\r
+  UINTN                   WaitIndex;\r
+  EFI_INPUT_KEY           Key;\r
+  LIST_ENTRY             *SecondEntry;\r
+  UINTN                   BootOrderSize;\r
+  UINT16                 *BootOrder;\r
+  LIST_ENTRY             *Entry;\r
+  UINTN                   Index;\r
+\r
+  DisplayBootOptions (BootOptionsList);\r
+\r
+  // Ask to select the boot option to move\r
+  while (TRUE) {\r
+    Status = SelectBootOption (BootOptionsList, MOVE_BOOT_ENTRY, &BootOptionEntry);\r
+    if (EFI_ERROR (Status)) {\r
+      goto ErrorExit;\r
+    }\r
+\r
+    SelectedEntry = &BootOptionEntry->Link;\r
+    // Note down the previous entry in the list to be able to cancel changes\r
+    PrevEntry = GetPreviousNode (BootOptionsList, SelectedEntry);\r
+\r
+    //  Start of interaction\r
+    while (TRUE) {\r
+      Print (\r
+        L"* Use up/down arrows to move the entry '%s'",\r
+        BootOptionEntry->BdsLoadOption->Description\r
+        );\r
+\r
+      // Wait for a move, save or cancel request\r
+      Move   = FALSE;\r
+      Save   = FALSE;\r
+      Cancel = FALSE;\r
+      do {\r
+        Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &WaitIndex);\r
+        if (!EFI_ERROR (Status)) {\r
+          Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);\r
+        }\r
+        if (EFI_ERROR (Status)) {\r
+          Print (L"\n");\r
+          goto ErrorExit;\r
+        }\r
+\r
+        switch (Key.ScanCode) {\r
+        case SCAN_NULL:\r
+          Save = (Key.UnicodeChar == CHAR_LINEFEED)        ||\r
+                 (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) ||\r
+                 (Key.UnicodeChar == 0x7f);\r
+          break;\r
+\r
+        case SCAN_UP:\r
+          SecondEntry = GetPreviousNode (BootOptionsList, SelectedEntry);\r
+          Move = SecondEntry != BootOptionsList;\r
+          break;\r
+\r
+        case SCAN_DOWN:\r
+          SecondEntry = GetNextNode (BootOptionsList, SelectedEntry);\r
+          Move = SecondEntry != BootOptionsList;\r
+          break;\r
+\r
+        case SCAN_ESC:\r
+          Cancel = TRUE;\r
+          break;\r
+        }\r
+      } while ((!Move) && (!Save) && (!Cancel));\r
+\r
+      if (Move) {\r
+        SwapListEntries (SelectedEntry, SecondEntry);\r
+      } else {\r
+        if (Save) {\r
+          Status = GetGlobalEnvironmentVariable (\r
+                    L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder\r
+                    );\r
+          BootOrderSize /= sizeof (UINT16);\r
+\r
+          if (!EFI_ERROR (Status)) {\r
+            // The order of the boot options in the 'BootOptionsList' is the\r
+            // new order that has been just defined by the user. Save this new\r
+            // order in "BootOrder" UEFI global variable.\r
+            Entry = GetFirstNode (BootOptionsList);\r
+            for (Index = 0; Index < BootOrderSize; Index++) {\r
+              BootOrder[Index] = (LOAD_OPTION_FROM_LINK (Entry))->LoadOptionIndex;\r
+              Entry = GetNextNode (BootOptionsList, Entry);\r
+            }\r
+            Status = gRT->SetVariable (\r
+                           (CHAR16*)L"BootOrder",\r
+                           &gEfiGlobalVariableGuid,\r
+                           EFI_VARIABLE_NON_VOLATILE       |\r
+                           EFI_VARIABLE_BOOTSERVICE_ACCESS |\r
+                           EFI_VARIABLE_RUNTIME_ACCESS,\r
+                           BootOrderSize * sizeof (UINT16),\r
+                           BootOrder\r
+                           );\r
+            FreePool (BootOrder);\r
+          }\r
+\r
+          if (EFI_ERROR (Status)) {\r
+            Print (L"\nAn error occurred, move not completed!\n");\r
+            Cancel = TRUE;\r
+          }\r
+        }\r
+\r
+        if (Cancel) {\r
+          //\r
+          // Restore initial position of the selected boot option\r
+          //\r
+          RemoveEntryList (SelectedEntry);\r
+          InsertHeadList (PrevEntry, SelectedEntry);\r
+        }\r
+      }\r
+\r
+      Print (L"\n");\r
+      DisplayBootOptions (BootOptionsList);\r
+      // Saved or cancelled, back to the choice of boot option to move\r
+      if (!Move) {\r
+        break;\r
+      }\r
+    }\r
+  }\r
+\r
+ErrorExit:\r
+  return Status ;\r
+}\r
+\r
 EFI_STATUS\r
 UpdateFdtPath (\r
   IN LIST_ENTRY *BootOptionsList\r
@@ -699,6 +907,7 @@ struct BOOT_MANAGER_ENTRY {
     { L"Add Boot Device Entry", BootMenuAddBootOption },\r
     { L"Update Boot Device Entry", BootMenuUpdateBootOption },\r
     { L"Remove Boot Device Entry", BootMenuRemoveBootOption },\r
+    { L"Reorder Boot Device Entries", BootMenuReorderBootOptions },\r
     { L"Update FDT path", UpdateFdtPath },\r
     { L"Set Boot Timeout", BootMenuSetBootTimeout },\r
 };\r