X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=ArmPlatformPkg%2FBds%2FBootMenu.c;h=76c66fd0bb3f867442dbdbc3473774e4d715dc4b;hp=d583c02256219a8410adda0b6ee061f0bb6ad02e;hb=6332ffb0affadde53e45325d81b084bc9936ef02;hpb=a6e97d28aa583e4b7959431cbf8bbd7269d5065d diff --git a/ArmPlatformPkg/Bds/BootMenu.c b/ArmPlatformPkg/Bds/BootMenu.c index d583c02256..76c66fd0bb 100644 --- a/ArmPlatformPkg/Bds/BootMenu.c +++ b/ArmPlatformPkg/Bds/BootMenu.c @@ -1,6 +1,6 @@ /** @file * -* Copyright (c) 2011, ARM Limited. All rights reserved. +* Copyright (c) 2011 - 2015, ARM Limited. All rights reserved. * * This program and the accompanying materials * are licensed and made available under the terms and conditions of the BSD License @@ -14,9 +14,166 @@ #include "BdsInternal.h" -extern EFI_HANDLE mImageHandle; +#include + 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 @@ -53,9 +210,9 @@ SelectBootDevice ( CHAR16* DevicePathTxt; EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol; - Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol); + Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol); ASSERT_EFI_ERROR(Status); - DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText((*SupportedBootDevice)->DevicePathProtocol,TRUE,TRUE); + DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText ((*SupportedBootDevice)->DevicePathProtocol,TRUE,TRUE); Print(L"\t- %s\n",DevicePathTxt); @@ -82,7 +239,7 @@ SelectBootDevice ( Status = EFI_ABORTED; goto EXIT; } else if ((SupportedDeviceSelected == 0) || (SupportedDeviceSelected > SupportedDeviceCount)) { - Print(L"Invalid input (max %d)\n",SupportedDeviceSelected); + Print(L"Invalid input (max %d)\n",SupportedDeviceCount); SupportedDeviceSelected = 0; } } @@ -102,7 +259,7 @@ SelectBootDevice ( } Index++; } - + EXIT: BootDeviceListSupportedFree (&SupportedDeviceList, *SupportedBootDevice); return Status; @@ -117,16 +274,20 @@ BootMenuAddBootOption ( BDS_SUPPORTED_DEVICE* SupportedBootDevice; ARM_BDS_LOADER_ARGUMENTS* BootArguments; CHAR16 BootDescription[BOOT_DEVICE_DESCRIPTION_MAX]; - CHAR8 CmdLine[BOOT_DEVICE_OPTION_MAX]; + CHAR8 AsciiCmdLine[BOOT_DEVICE_OPTION_MAX]; + CHAR16 CmdLine[BOOT_DEVICE_OPTION_MAX]; UINT32 Attributes; ARM_BDS_LOADER_TYPE BootType; BDS_LOAD_OPTION_ENTRY *BdsLoadOptionEntry; EFI_DEVICE_PATH *DevicePath; - EFI_DEVICE_PATH_PROTOCOL *DevicePathNode; - EFI_DEVICE_PATH_PROTOCOL *InitrdPathNode; + EFI_DEVICE_PATH_PROTOCOL *DevicePathNodes; + EFI_DEVICE_PATH_PROTOCOL *InitrdPathNodes; EFI_DEVICE_PATH_PROTOCOL *InitrdPath; UINTN CmdLineSize; + BOOLEAN InitrdSupport; UINTN InitrdSize; + UINT8* OptionalData; + UINTN OptionalDataSize; Attributes = 0; SupportedBootDevice = NULL; @@ -139,49 +300,90 @@ BootMenuAddBootOption ( } // Create the specific device path node - Print(L"File path of the EFI Application or the kernel: "); - Status = SupportedBootDevice->Support->CreateDevicePathNode (SupportedBootDevice, &DevicePathNode, &BootType, &Attributes); + Status = SupportedBootDevice->Support->CreateDevicePathNode (L"EFI Application or the kernel", &DevicePathNodes); if (EFI_ERROR(Status)) { Status = EFI_ABORTED; goto EXIT; } - // Append the Device Path node to the select device path - DevicePath = AppendDevicePathNode (SupportedBootDevice->DevicePathProtocol, (CONST EFI_DEVICE_PATH_PROTOCOL *)DevicePathNode); + // Append the Device Path to the selected device path + DevicePath = AppendDevicePath (SupportedBootDevice->DevicePathProtocol, (CONST EFI_DEVICE_PATH_PROTOCOL *)DevicePathNodes); + if (DevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + if (SupportedBootDevice->Support->RequestBootType) { + Status = BootDeviceGetType (DevicePath, &BootType, &Attributes); + if (EFI_ERROR(Status)) { + Status = EFI_ABORTED; + goto EXIT; + } + } else { + BootType = BDS_LOADER_EFI_APPLICATION; + } if ((BootType == BDS_LOADER_KERNEL_LINUX_ATAG) || (BootType == BDS_LOADER_KERNEL_LINUX_FDT)) { - // Create the specific device path node - Print(L"File path of the initrd: "); - Status = SupportedBootDevice->Support->CreateDevicePathNode (SupportedBootDevice, &InitrdPathNode, NULL, NULL); - if (EFI_ERROR(Status) && Status != EFI_NOT_FOUND) { // EFI_NOT_FOUND is returned on empty input string, but we can boot without an initrd + Print(L"Add an initrd: "); + Status = GetHIInputBoolean (&InitrdSupport); + if (EFI_ERROR(Status)) { Status = EFI_ABORTED; goto EXIT; } - if (InitrdPathNode != NULL) { - // Append the Device Path node to the select device path - InitrdPath = AppendDevicePathNode (SupportedBootDevice->DevicePathProtocol, (CONST EFI_DEVICE_PATH_PROTOCOL *)InitrdPathNode); + if (InitrdSupport) { + // Create the specific device path node + Status = SupportedBootDevice->Support->CreateDevicePathNode (L"initrd", &InitrdPathNodes); + if (EFI_ERROR(Status) && Status != EFI_NOT_FOUND) { // EFI_NOT_FOUND is returned on empty input string, but we can boot without an initrd + Status = EFI_ABORTED; + goto EXIT; + } + + if (InitrdPathNodes != NULL) { + // Append the Device Path to the selected device path + InitrdPath = AppendDevicePath (SupportedBootDevice->DevicePathProtocol, (CONST EFI_DEVICE_PATH_PROTOCOL *)InitrdPathNodes); + // Free the InitrdPathNodes created by Support->CreateDevicePathNode() + FreePool (InitrdPathNodes); + + if (InitrdPath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + } else { + InitrdPath = NULL; + } } else { InitrdPath = NULL; } Print(L"Arguments to pass to the binary: "); - Status = GetHIInputAscii (CmdLine,BOOT_DEVICE_OPTION_MAX); + Status = GetHIInputAscii (AsciiCmdLine, BOOT_DEVICE_OPTION_MAX); if (EFI_ERROR(Status)) { Status = EFI_ABORTED; goto FREE_DEVICE_PATH; } - CmdLineSize = AsciiStrSize (CmdLine); + CmdLineSize = AsciiStrSize (AsciiCmdLine); InitrdSize = GetDevicePathSize (InitrdPath); - BootArguments = (ARM_BDS_LOADER_ARGUMENTS*)AllocatePool (sizeof(ARM_BDS_LOADER_ARGUMENTS) + CmdLineSize + InitrdSize); - + OptionalDataSize = sizeof(ARM_BDS_LOADER_ARGUMENTS) + CmdLineSize + InitrdSize; + BootArguments = (ARM_BDS_LOADER_ARGUMENTS*)AllocatePool (OptionalDataSize); + BootArguments->LinuxArguments.CmdLineSize = CmdLineSize; BootArguments->LinuxArguments.InitrdSize = InitrdSize; - CopyMem ((VOID*)(&BootArguments->LinuxArguments + 1), CmdLine, CmdLineSize); + CopyMem ((VOID*)(&BootArguments->LinuxArguments + 1), AsciiCmdLine, CmdLineSize); CopyMem ((VOID*)((UINTN)(&BootArguments->LinuxArguments + 1) + CmdLineSize), InitrdPath, InitrdSize); + + OptionalData = (UINT8*)BootArguments; } else { - BootArguments = NULL; + Print (L"Arguments to pass to the EFI Application: "); + Status = GetHIInputStr (CmdLine, BOOT_DEVICE_OPTION_MAX); + if (EFI_ERROR (Status)) { + Status = EFI_ABORTED; + goto EXIT; + } + + OptionalData = (UINT8*)CmdLine; + OptionalDataSize = StrSize (CmdLine); } Print(L"Description for this new Entry: "); @@ -193,7 +395,7 @@ BootMenuAddBootOption ( // Create new entry BdsLoadOptionEntry = (BDS_LOAD_OPTION_ENTRY*)AllocatePool (sizeof(BDS_LOAD_OPTION_ENTRY)); - Status = BootOptionCreate (Attributes, BootDescription, DevicePath, BootType, BootArguments, &BdsLoadOptionEntry->BdsLoadOption); + Status = BootOptionCreate (Attributes, BootDescription, DevicePath, BootType, OptionalData, OptionalDataSize, &BdsLoadOptionEntry->BdsLoadOption); if (!EFI_ERROR(Status)) { InsertTailList (BootOptionsList, &BdsLoadOptionEntry->Link); } @@ -201,7 +403,6 @@ BootMenuAddBootOption ( FREE_DEVICE_PATH: FreePool (DevicePath); - EXIT: if (Status == EFI_ABORTED) { Print(L"\n"); @@ -210,95 +411,6 @@ EXIT: return Status; } -STATIC -EFI_STATUS -BootMenuSelectBootOption ( - IN LIST_ENTRY* BootOptionsList, - IN CONST CHAR16* InputStatement, - IN BOOLEAN OnlyArmBdsBootEntry, - OUT BDS_LOAD_OPTION_ENTRY** BdsLoadOptionEntry - ) -{ - EFI_STATUS Status; - LIST_ENTRY* Entry; - BDS_LOAD_OPTION* BdsLoadOption; - UINTN BootOptionSelected; - UINTN BootOptionCount; - UINTN Index; - - // Display the list of supported boot devices - BootOptionCount = 1; - for (Entry = GetFirstNode (BootOptionsList); - !IsNull (BootOptionsList,Entry); - Entry = GetNextNode (BootOptionsList, Entry) - ) - { - BdsLoadOption = LOAD_OPTION_FROM_LINK(Entry); - - if (OnlyArmBdsBootEntry && !IS_ARM_BDS_BOOTENTRY (BdsLoadOption)) { - continue; - } - - 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; - 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); - } - - FreePool(DevicePathTxt); - DEBUG_CODE_END(); - - BootOptionCount++; - } - - if (BootOptionCount == 0) { - 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-1); - 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 @@ -307,8 +419,9 @@ BootMenuRemoveBootOption ( EFI_STATUS Status; BDS_LOAD_OPTION_ENTRY* BootOptionEntry; - Status = BootMenuSelectBootOption (BootOptionsList, L"Delete entry: ", FALSE, &BootOptionEntry); - if (EFI_ERROR(Status)) { + DisplayBootOptions (BootOptionsList); + Status = SelectBootOption (BootOptionsList, DELETE_BOOT_ENTRY, &BootOptionEntry); + if (EFI_ERROR (Status)) { return Status; } @@ -336,55 +449,119 @@ BootMenuUpdateBootOption ( ARM_BDS_LOADER_ARGUMENTS* BootArguments; CHAR16 BootDescription[BOOT_DEVICE_DESCRIPTION_MAX]; CHAR8 CmdLine[BOOT_DEVICE_OPTION_MAX]; - EFI_DEVICE_PATH* DevicePath; + CHAR16 UnicodeCmdLine[BOOT_DEVICE_OPTION_MAX]; + EFI_DEVICE_PATH *DevicePath; + EFI_DEVICE_PATH *TempInitrdPath; ARM_BDS_LOADER_TYPE BootType; - ARM_BDS_LOADER_OPTIONAL_DATA* OptionalData; + ARM_BDS_LOADER_OPTIONAL_DATA* LoaderOptionalData; ARM_BDS_LINUX_ARGUMENTS* LinuxArguments; - EFI_DEVICE_PATH* InitrdPathList; + EFI_DEVICE_PATH *InitrdPathNodes; + EFI_DEVICE_PATH *InitrdPath; UINTN InitrdSize; UINTN CmdLineSize; - - Status = BootMenuSelectBootOption (BootOptionsList, L"Update entry: ", TRUE, &BootOptionEntry); - if (EFI_ERROR(Status)) { + BOOLEAN InitrdSupport; + UINT8* OptionalData; + UINTN OptionalDataSize; + BOOLEAN IsPrintable; + BOOLEAN IsUnicode; + + DisplayBootOptions (BootOptionsList); + Status = SelectBootOption (BootOptionsList, UPDATE_BOOT_ENTRY, &BootOptionEntry); + if (EFI_ERROR (Status)) { return Status; } BootOption = BootOptionEntry->BdsLoadOption; // Get the device support for this Boot Option - Status = BootDeviceGetDeviceSupport (BootOption,&DeviceSupport); + Status = BootDeviceGetDeviceSupport (BootOption->FilePathList, &DeviceSupport); if (EFI_ERROR(Status)) { - Print(L"Impossible to retrieve the supported device for the update\n"); + Print(L"Not possible to retrieve the supported device for the update\n"); return EFI_UNSUPPORTED; } - Print(L"File path of the EFI Application or the kernel: "); - Status = DeviceSupport->UpdateDevicePathNode (BootOption->FilePathList, &DevicePath, NULL, NULL); + Status = DeviceSupport->UpdateDevicePathNode (BootOption->FilePathList, L"EFI Application or the kernel", &DevicePath); if (EFI_ERROR(Status)) { Status = EFI_ABORTED; goto EXIT; } - OptionalData = BootOption->OptionalData; - BootType = (ARM_BDS_LOADER_TYPE)ReadUnaligned32 ((UINT32 *)(&OptionalData->Header.LoaderType)); + if (DeviceSupport->RequestBootType) { + Status = BootDeviceGetType (DevicePath, &BootType, &BootOption->Attributes); + if (EFI_ERROR(Status)) { + Status = EFI_ABORTED; + goto EXIT; + } + } + + LoaderOptionalData = BootOption->OptionalData; + if (LoaderOptionalData != NULL) { + BootType = (ARM_BDS_LOADER_TYPE)ReadUnaligned32 ((UINT32 *)(&LoaderOptionalData->Header.LoaderType)); + } else { + BootType = BDS_LOADER_EFI_APPLICATION; + } - // TODO: Allow adding an initrd to a boot entry without one if ((BootType == BDS_LOADER_KERNEL_LINUX_ATAG) || (BootType == BDS_LOADER_KERNEL_LINUX_FDT)) { - LinuxArguments = &OptionalData->Arguments.LinuxArguments; + LinuxArguments = &LoaderOptionalData->Arguments.LinuxArguments; CmdLineSize = ReadUnaligned16 ((CONST UINT16*)&LinuxArguments->CmdLineSize); - InitrdSize = GetUnalignedDevicePathSize ((EFI_DEVICE_PATH*)((LinuxArguments + 1) + CmdLineSize)); - Print(L"File path of the initrd: "); - Status = DeviceSupport->UpdateDevicePathNode ( - (EFI_DEVICE_PATH_PROTOCOL *)((UINTN)(LinuxArguments + 1) + CmdLineSize), &InitrdPathList, NULL, NULL); - if (EFI_ERROR(Status) && Status != EFI_NOT_FOUND) {// EFI_NOT_FOUND is returned on empty input string, but we can boot without an initrd + InitrdSize = ReadUnaligned16 ((CONST UINT16*)&LinuxArguments->InitrdSize); + if (InitrdSize > 0) { + Print(L"Keep the initrd: "); + } else { + Print(L"Add an initrd: "); + } + Status = GetHIInputBoolean (&InitrdSupport); + if (EFI_ERROR(Status)) { Status = EFI_ABORTED; goto EXIT; } - Print(L"Arguments to pass to the binary: "); + if (InitrdSupport) { + if (InitrdSize > 0) { + // Case we update the initrd device path + Status = DeviceSupport->UpdateDevicePathNode ((EFI_DEVICE_PATH*)((UINTN)(LinuxArguments + 1) + CmdLineSize), L"initrd", &InitrdPath); + if (EFI_ERROR(Status) && Status != EFI_NOT_FOUND) {// EFI_NOT_FOUND is returned on empty input string, but we can boot without an initrd + Status = EFI_ABORTED; + goto EXIT; + } + InitrdSize = GetDevicePathSize (InitrdPath); + } else { + // Case we create the initrd device path + + Status = DeviceSupport->CreateDevicePathNode (L"initrd", &InitrdPathNodes); + if (EFI_ERROR(Status) && Status != EFI_NOT_FOUND) { // EFI_NOT_FOUND is returned on empty input string, but we can boot without an initrd + Status = EFI_ABORTED; + goto EXIT; + } + + if (InitrdPathNodes != NULL) { + // Duplicate Linux kernel Device Path + TempInitrdPath = DuplicateDevicePath (BootOption->FilePathList); + // Replace Linux kernel Node by EndNode + SetDevicePathEndNode (GetLastDevicePathNode (TempInitrdPath)); + // Append the Device Path to the selected device path + InitrdPath = AppendDevicePath (TempInitrdPath, (CONST EFI_DEVICE_PATH_PROTOCOL *)InitrdPathNodes); + FreePool (TempInitrdPath); + // Free the InitrdPathNodes created by Support->CreateDevicePathNode() + FreePool (InitrdPathNodes); + if (InitrdPath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + InitrdSize = GetDevicePathSize (InitrdPath); + } else { + InitrdPath = NULL; + } + } + } else { + InitrdSize = 0; + } + + Print(L"Arguments to pass to the binary: "); if (CmdLineSize > 0) { - AsciiStrnCpy(CmdLine, (CONST CHAR8*)(LinuxArguments + 1), CmdLineSize); + AsciiStrnCpy (CmdLine, (CONST CHAR8*)(LinuxArguments + 1), sizeof (CmdLine)); + CmdLine[sizeof (CmdLine) - 1] = '\0'; } else { CmdLine[0] = '\0'; } @@ -395,18 +572,82 @@ BootMenuUpdateBootOption ( } CmdLineSize = AsciiStrSize (CmdLine); - InitrdSize = GetDevicePathSize (InitrdPathList); - BootArguments = (ARM_BDS_LOADER_ARGUMENTS*)AllocatePool(sizeof(ARM_BDS_LOADER_ARGUMENTS) + CmdLineSize + InitrdSize); + OptionalDataSize = sizeof(ARM_BDS_LOADER_ARGUMENTS) + CmdLineSize + InitrdSize; + BootArguments = (ARM_BDS_LOADER_ARGUMENTS*)AllocatePool (OptionalDataSize); BootArguments->LinuxArguments.CmdLineSize = CmdLineSize; BootArguments->LinuxArguments.InitrdSize = InitrdSize; CopyMem (&BootArguments->LinuxArguments + 1, CmdLine, CmdLineSize); - CopyMem ((UINTN)(&BootArguments->LinuxArguments + 1) + CmdLine, InitrdPathList, InitrdSize); + CopyMem ((VOID*)((UINTN)(&BootArguments->LinuxArguments + 1) + CmdLineSize), InitrdPath, InitrdSize); + + OptionalData = (UINT8*)BootArguments; } else { - BootArguments = NULL; + Print (L"Arguments to pass to the EFI Application: "); + + if (BootOption->OptionalDataSize > 0) { + IsPrintable = IsPrintableString (BootOption->OptionalData, &IsUnicode); + if (IsPrintable) { + // + // The size in bytes of the string, final zero included, should + // be equal to or at least lower than "BootOption->OptionalDataSize" + // and the "IsPrintableString()" has already tested that the length + // in number of characters is smaller than BOOT_DEVICE_OPTION_MAX, + // final '\0' included. We can thus copy the string for editing + // using "CopyMem()". Furthermore, note that in the case of an Unicode + // string "StrnCpy()" and "StrCpy()" can not be used to copy the + // string because the data pointed to by "BootOption->OptionalData" + // is not necessarily 2-byte aligned. + // + if (IsUnicode) { + CopyMem ( + UnicodeCmdLine, BootOption->OptionalData, + MIN (sizeof (UnicodeCmdLine), + BootOption->OptionalDataSize) + ); + } else { + CopyMem ( + CmdLine, BootOption->OptionalData, + MIN (sizeof (CmdLine), + BootOption->OptionalDataSize) + ); + } + } + } else { + UnicodeCmdLine[0] = L'\0'; + IsPrintable = TRUE; + IsUnicode = TRUE; + } + + // We do not request arguments for OptionalData that cannot be printed + if (IsPrintable) { + if (IsUnicode) { + Status = EditHIInputStr (UnicodeCmdLine, BOOT_DEVICE_OPTION_MAX); + if (EFI_ERROR (Status)) { + Status = EFI_ABORTED; + goto FREE_DEVICE_PATH; + } + + OptionalData = (UINT8*)UnicodeCmdLine; + OptionalDataSize = StrSize (UnicodeCmdLine); + } else { + Status = EditHIInputAscii (CmdLine, BOOT_DEVICE_OPTION_MAX); + if (EFI_ERROR (Status)) { + Status = EFI_ABORTED; + goto FREE_DEVICE_PATH; + } + + OptionalData = (UINT8*)CmdLine; + OptionalDataSize = AsciiStrSize (CmdLine); + } + } else { + // We keep the former OptionalData + OptionalData = BootOption->OptionalData; + OptionalDataSize = BootOption->OptionalDataSize; + } } Print(L"Description for this new Entry: "); + StrnCpy (BootDescription, BootOption->Description, BOOT_DEVICE_DESCRIPTION_MAX); Status = EditHIInputStr (BootDescription, BOOT_DEVICE_DESCRIPTION_MAX); if (EFI_ERROR(Status)) { Status = EFI_ABORTED; @@ -414,7 +655,7 @@ BootMenuUpdateBootOption ( } // Update the entry - Status = BootOptionUpdate (BootOption, BootOption->Attributes, BootDescription, DevicePath, BootType, BootArguments); + Status = BootOptionUpdate (BootOption, BootOption->Attributes, BootDescription, DevicePath, BootType, OptionalData, OptionalDataSize); FREE_DEVICE_PATH: FreePool (DevicePath); @@ -426,6 +667,211 @@ 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; + SecondEntry = NULL; + // 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) { + if ((SelectedEntry != NULL) && (SecondEntry != NULL)) { + 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 ; +} + +/** + Set boot timeout + + Ask for the boot timeout in seconds and if the input succeeds assign the + input value to the UEFI global variable "Timeout". This function is called + when the user selects the "Set Boot Timeout" of the boot manager menu. + + @param[in] BootOptionsList List of the boot devices, not used here + + @retval EFI_SUCCESS Boot timeout in second retrieved from the standard + input and assigned to the UEFI "Timeout" global + variable + @retval !EFI_SUCCESS Either the input or the setting of the UEFI global + variable "Timeout" has failed. +**/ +EFI_STATUS +STATIC +BootMenuSetBootTimeout ( + IN LIST_ENTRY *BootOptionsList + ) +{ + EFI_STATUS Status; + UINTN Input; + UINT16 Timeout; + + Print (L"Timeout duration (in seconds): "); + Status = GetHIInputInteger (&Input); + if (EFI_ERROR (Status)) { + Print (L"\n"); + goto ErrorExit; + } + + Timeout = Input; + Status = gRT->SetVariable ( + (CHAR16*)L"Timeout", + &gEfiGlobalVariableGuid, + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + sizeof (UINT16), + &Timeout + ); + ASSERT_EFI_ERROR (Status); + +ErrorExit: + return Status; +} + struct BOOT_MANAGER_ENTRY { CONST CHAR16* Description; EFI_STATUS (*Callback) (IN LIST_ENTRY *BootOptionsList); @@ -433,6 +879,8 @@ 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"Set Boot Timeout", BootMenuSetBootTimeout }, }; EFI_STATUS @@ -463,22 +911,21 @@ BootMenuManager ( } return EFI_SUCCESS; } else if ((OptionSelected > 0) && (OptionSelected <= BootManagerEntryCount)) { - Status = BootManagerEntries[OptionSelected-1].Callback (BootOptionsList); + BootManagerEntries[OptionSelected-1].Callback (BootOptionsList); } } - - return EFI_SUCCESS; + // Should never go here } EFI_STATUS -BootEBL ( +BootShell ( IN LIST_ENTRY *BootOptionsList ) { EFI_STATUS Status; // Start EFI Shell - Status = BdsLoadApplication(mImageHandle, L"Ebl"); + Status = BdsLoadApplication (gImageHandle, L"Shell", 0, NULL); if (Status == EFI_NOT_FOUND) { Print (L"Error: EFI Application not found.\n"); } else if (EFI_ERROR(Status)) { @@ -492,7 +939,7 @@ struct BOOT_MAIN_ENTRY { CONST CHAR16* Description; EFI_STATUS (*Callback) (IN LIST_ENTRY *BootOptionsList); } BootMainEntries[] = { - { L"EBL", BootEBL }, + { L"Shell", BootShell }, { L"Boot Manager", BootMenuManager }, }; @@ -511,8 +958,9 @@ BootMenuMain ( UINTN BootOptionSelected; UINTN Index; UINTN BootMainEntryCount; + BOOLEAN IsUnicode; - BootOption = NULL; + BootOption = NULL; BootMainEntryCount = sizeof(BootMainEntries) / sizeof(struct BOOT_MAIN_ENTRY); while (TRUE) { @@ -529,22 +977,22 @@ BootMenuMain ( { BootOption = LOAD_OPTION_FROM_LINK(Entry); - Print(L"[%d] %s\n",OptionCount,BootOption->Description); + Print(L"[%d] %s\n", OptionCount, BootOption->Description); DEBUG_CODE_BEGIN(); CHAR16* DevicePathTxt; EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol; ARM_BDS_LOADER_OPTIONAL_DATA* OptionalData; UINTN CmdLineSize; - ARM_BDS_LOADER_TYPE LoaderType; + ARM_BDS_LOADER_TYPE LoaderType; - Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol); + Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol); if (EFI_ERROR(Status)) { // You must provide an implementation of DevicePathToTextProtocol in your firmware (eg: DevicePathDxe) DEBUG((EFI_D_ERROR,"Error: Bds requires DevicePathToTextProtocol\n")); return Status; } - DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText(BootOption->FilePathList,TRUE,TRUE); + DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (BootOption->FilePathList, TRUE, TRUE); Print(L"\t- %s\n",DevicePathTxt); @@ -559,9 +1007,35 @@ BootMenuMain ( GetAlignedDevicePath ((EFI_DEVICE_PATH*)((UINTN)(&OptionalData->Arguments.LinuxArguments + 1) + CmdLineSize)), TRUE, TRUE); Print(L"\t- Initrd: %s\n", DevicePathTxt); } - Print(L"\t- Arguments: %a\n", (&OptionalData->Arguments.LinuxArguments + 1)); + if (ReadUnaligned16 (&OptionalData->Arguments.LinuxArguments.CmdLineSize) > 0) { + Print(L"\t- Arguments: %a\n", (&OptionalData->Arguments.LinuxArguments + 1)); + } + } + + switch (LoaderType) { + case BDS_LOADER_EFI_APPLICATION: + Print(L"\t- LoaderType: EFI Application\n"); + break; + + case BDS_LOADER_KERNEL_LINUX_ATAG: + Print(L"\t- LoaderType: Linux kernel with ATAG support\n"); + break; + + case BDS_LOADER_KERNEL_LINUX_FDT: + Print(L"\t- LoaderType: Linux kernel with FDT support\n"); + break; + + default: + Print(L"\t- LoaderType: Not recognized (%d)\n", LoaderType); + } + } else if (BootOption->OptionalData != NULL) { + if (IsPrintableString (BootOption->OptionalData, &IsUnicode)) { + if (IsUnicode) { + Print (L"\t- Arguments: %s\n", BootOption->OptionalData); + } else { + AsciiPrint ("\t- Arguments: %a\n", BootOption->OptionalData); + } } - Print(L"\t- LoaderType: %d\n", LoaderType); } FreePool(DevicePathTxt); DEBUG_CODE_END(); @@ -609,6 +1083,5 @@ BootMenuMain ( Status = BootOptionStart (BootOption); } } - - return Status; + // Should never go here }