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