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 }, };