/** @file | |
* | |
* Copyright (c) 2011-2013, 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 | |
* which accompanies this distribution. The full text of the license may be found at | |
* http://opensource.org/licenses/bsd-license.php | |
* | |
* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
* | |
**/ | |
#include "LinuxInternal.h" | |
#define DEFAULT_BOOT_ENTRY_DESCRIPTION L"Linux" | |
#define MAX_STR_INPUT 300 | |
#define MAX_ASCII_INPUT 300 | |
typedef enum { | |
LINUX_LOADER_NEW = 1, | |
LINUX_LOADER_UPDATE | |
} LINUX_LOADER_ACTION; | |
STATIC | |
EFI_STATUS | |
EditHIInputStr ( | |
IN OUT CHAR16 *CmdLine, | |
IN UINTN MaxCmdLine | |
) | |
{ | |
UINTN CmdLineIndex; | |
UINTN WaitIndex; | |
CHAR8 Char; | |
EFI_INPUT_KEY Key; | |
EFI_STATUS Status; | |
Print (CmdLine); | |
for (CmdLineIndex = StrLen (CmdLine); CmdLineIndex < MaxCmdLine; ) { | |
Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &WaitIndex); | |
ASSERT_EFI_ERROR (Status); | |
Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); | |
ASSERT_EFI_ERROR (Status); | |
// Unicode character is valid when Scancode is NUll | |
if (Key.ScanCode == SCAN_NULL) { | |
// Scan code is NUll, hence read Unicode character | |
Char = (CHAR8)Key.UnicodeChar; | |
} else { | |
Char = CHAR_NULL; | |
} | |
if ((Char == CHAR_LINEFEED) || (Char == CHAR_CARRIAGE_RETURN) || (Char == 0x7f)) { | |
CmdLine[CmdLineIndex] = '\0'; | |
Print (L"\n\r"); | |
return EFI_SUCCESS; | |
} else if ((Key.UnicodeChar == L'\b') || (Key.ScanCode == SCAN_LEFT) || (Key.ScanCode == SCAN_DELETE)){ | |
if (CmdLineIndex != 0) { | |
CmdLineIndex--; | |
Print (L"\b \b"); | |
} | |
} else if ((Key.ScanCode == SCAN_ESC) || (Char == 0x1B) || (Char == 0x0)) { | |
return EFI_INVALID_PARAMETER; | |
} else { | |
CmdLine[CmdLineIndex++] = Key.UnicodeChar; | |
Print (L"%c", Key.UnicodeChar); | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
STATIC | |
EFI_STATUS | |
EditHIInputAscii ( | |
IN OUT CHAR8 *CmdLine, | |
IN UINTN MaxCmdLine | |
) | |
{ | |
CHAR16* Str; | |
EFI_STATUS Status; | |
Str = (CHAR16*)AllocatePool (MaxCmdLine * sizeof(CHAR16)); | |
AsciiStrToUnicodeStr (CmdLine, Str); | |
Status = EditHIInputStr (Str, MaxCmdLine); | |
UnicodeStrToAsciiStr (Str, CmdLine); | |
FreePool (Str); | |
return Status; | |
} | |
STATIC | |
EFI_STATUS | |
GetHIInputInteger ( | |
OUT UINTN *Integer | |
) | |
{ | |
CHAR16 CmdLine[255]; | |
EFI_STATUS Status; | |
CmdLine[0] = '\0'; | |
Status = EditHIInputStr (CmdLine, 255); | |
if (!EFI_ERROR(Status)) { | |
*Integer = StrDecimalToUintn (CmdLine); | |
} | |
return Status; | |
} | |
#if 0 | |
EFI_STATUS | |
GenerateDeviceDescriptionName ( | |
IN EFI_HANDLE Handle, | |
IN OUT CHAR16* Description | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_COMPONENT_NAME_PROTOCOL* ComponentName2Protocol; | |
EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol; | |
EFI_DEVICE_PATH_PROTOCOL* DevicePathProtocol; | |
CHAR16* DriverName; | |
CHAR16* DevicePathTxt; | |
EFI_DEVICE_PATH* DevicePathNode; | |
ComponentName2Protocol = NULL; | |
Status = gBS->HandleProtocol (Handle, &gEfiComponentName2ProtocolGuid, (VOID **)&ComponentName2Protocol); | |
if (!EFI_ERROR(Status)) { | |
//TODO: Fixme. we must find the best langague | |
Status = ComponentName2Protocol->GetDriverName (ComponentName2Protocol,"en",&DriverName); | |
if (!EFI_ERROR(Status)) { | |
StrnCpy (Description,DriverName,BOOT_DEVICE_DESCRIPTION_MAX); | |
} | |
} | |
if (EFI_ERROR(Status)) { | |
// Use the lastest non null entry of the Device path as a description | |
Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePathProtocol); | |
if (EFI_ERROR(Status)) { | |
return Status; | |
} | |
// Convert the last non end-type Device Path Node in text for the description | |
DevicePathNode = GetLastDevicePathNode (DevicePathProtocol); | |
Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol); | |
ASSERT_EFI_ERROR(Status); | |
DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText(DevicePathNode,TRUE,TRUE); | |
StrnCpy (Description, DevicePathTxt, BOOT_DEVICE_DESCRIPTION_MAX); | |
FreePool (DevicePathTxt); | |
} | |
return EFI_SUCCESS; | |
} | |
#endif | |
EFI_STATUS | |
LinuxLoaderConfig ( | |
IN EFI_LOADED_IMAGE_PROTOCOL *LoadedImage | |
) | |
{ | |
EFI_STATUS Status; | |
LINUX_LOADER_ACTION Choice; | |
UINTN BootOrderSize; | |
UINT16* BootOrder; | |
UINTN BootOrderCount; | |
UINTN Index; | |
CHAR16 Description[MAX_ASCII_INPUT]; | |
CHAR8 CmdLine[MAX_ASCII_INPUT]; | |
CHAR16 Initrd[MAX_STR_INPUT]; | |
UINT16 InitrdPathListLength; | |
UINT16 CmdLineLength; | |
BDS_LOAD_OPTION* BdsLoadOption; | |
BDS_LOAD_OPTION** SupportedBdsLoadOptions; | |
UINTN SupportedBdsLoadOptionCount; | |
LINUX_LOADER_OPTIONAL_DATA* LinuxOptionalData; | |
EFI_DEVICE_PATH* DevicePathRoot; | |
Choice = (LINUX_LOADER_ACTION)0; | |
SupportedBdsLoadOptions = NULL; | |
SupportedBdsLoadOptionCount = 0; | |
do { | |
Print (L"[%d] Create new Linux Boot Entry\n",LINUX_LOADER_NEW); | |
Print (L"[%d] Update Linux Boot Entry\n",LINUX_LOADER_UPDATE); | |
Print (L"Option: "); | |
Status = GetHIInputInteger ((UINTN*)&Choice); | |
if (Status == EFI_INVALID_PARAMETER) { | |
Print (L"\n"); | |
return Status; | |
} else if ((Choice != LINUX_LOADER_NEW) && (Choice != LINUX_LOADER_UPDATE)) { | |
Print (L"Error: the option should be either '%d' or '%d'\n",LINUX_LOADER_NEW,LINUX_LOADER_UPDATE); | |
Status = EFI_INVALID_PARAMETER; | |
} | |
} while (EFI_ERROR(Status)); | |
if (Choice == LINUX_LOADER_UPDATE) { | |
// If no compatible entry then we just create a new entry | |
Choice = LINUX_LOADER_NEW; | |
// Scan the OptionalData of every entry for the correct signature | |
Status = GetGlobalEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder); | |
if (!EFI_ERROR(Status)) { | |
BootOrderCount = BootOrderSize / sizeof(UINT16); | |
// Allocate an array to handle maximum number of supported Boot Entry | |
SupportedBdsLoadOptions = (BDS_LOAD_OPTION**)AllocatePool(sizeof(BDS_LOAD_OPTION*) * BootOrderCount); | |
SupportedBdsLoadOptionCount = 0; | |
// Check if the signature is present in the list of the current Boot entries | |
for (Index = 0; Index < BootOrderCount; Index++) { | |
Status = BootOptionFromLoadOptionIndex (BootOrder[Index], &BdsLoadOption); | |
if (!EFI_ERROR(Status)) { | |
if ((BdsLoadOption->OptionalDataSize >= sizeof(UINT32)) && | |
(*(UINT32*)BdsLoadOption->OptionalData == LINUX_LOADER_SIGNATURE)) { | |
SupportedBdsLoadOptions[SupportedBdsLoadOptionCount++] = BdsLoadOption; | |
Choice = LINUX_LOADER_UPDATE; | |
} | |
} | |
} | |
} | |
FreePool (BootOrder); | |
} | |
if (Choice == LINUX_LOADER_NEW) { | |
Description[0] = '\0'; | |
CmdLine[0] = '\0'; | |
Initrd[0] = '\0'; | |
BdsLoadOption = (BDS_LOAD_OPTION*)AllocateZeroPool (sizeof(BDS_LOAD_OPTION)); | |
DEBUG_CODE_BEGIN(); | |
CHAR16* DevicePathTxt; | |
EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol; | |
Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol); | |
ASSERT_EFI_ERROR(Status); | |
DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (LoadedImage->FilePath, TRUE, TRUE); | |
Print(L"EFI OS Loader: %s\n",DevicePathTxt); | |
FreePool(DevicePathTxt); | |
DEBUG_CODE_END(); | |
// | |
// Fill the known fields of BdsLoadOption | |
// | |
BdsLoadOption->Attributes = LOAD_OPTION_ACTIVE | LOAD_OPTION_CATEGORY_BOOT; | |
// Get the full Device Path for this file | |
Status = gBS->HandleProtocol (LoadedImage->DeviceHandle, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePathRoot); | |
ASSERT_EFI_ERROR(Status); | |
BdsLoadOption->FilePathList = AppendDevicePath (DevicePathRoot, LoadedImage->FilePath); | |
BdsLoadOption->FilePathListLength = GetDevicePathSize (BdsLoadOption->FilePathList); | |
} else { | |
if (SupportedBdsLoadOptionCount > 1) { | |
for (Index = 0; Index < SupportedBdsLoadOptionCount; Index++) { | |
Print (L"[%d] %s\n",Index + 1,SupportedBdsLoadOptions[Index]->Description); | |
} | |
do { | |
Print (L"Update Boot Entry: "); | |
Status = GetHIInputInteger ((UINTN*)&Choice); | |
if (Status == EFI_INVALID_PARAMETER) { | |
Print (L"\n"); | |
return Status; | |
} else if ((Choice < 1) && (Choice > SupportedBdsLoadOptionCount)) { | |
Print (L"Choose entry from 1 to %d\n",SupportedBdsLoadOptionCount); | |
Status = EFI_INVALID_PARAMETER; | |
} | |
} while (EFI_ERROR(Status)); | |
BdsLoadOption = SupportedBdsLoadOptions[Choice-1]; | |
} | |
StrnCpy (Description, BdsLoadOption->Description, MAX_STR_INPUT); | |
LinuxOptionalData = (LINUX_LOADER_OPTIONAL_DATA*)BdsLoadOption->OptionalData; | |
if (LinuxOptionalData->CmdLineLength > 0) { | |
CopyMem (CmdLine, (CHAR8*)LinuxOptionalData + sizeof(LINUX_LOADER_OPTIONAL_DATA), LinuxOptionalData->CmdLineLength); | |
} else { | |
CmdLine[0] = '\0'; | |
} | |
if (LinuxOptionalData->InitrdPathListLength > 0) { | |
CopyMem (Initrd, (CHAR8*)LinuxOptionalData + sizeof(LINUX_LOADER_OPTIONAL_DATA) + LinuxOptionalData->CmdLineLength, LinuxOptionalData->InitrdPathListLength); | |
} else { | |
Initrd[0] = L'\0'; | |
} | |
DEBUG((EFI_D_ERROR,"L\n")); | |
} | |
// Description | |
Print (L"Description: "); | |
Status = EditHIInputStr (Description, MAX_STR_INPUT); | |
if (EFI_ERROR(Status)) { | |
return Status; | |
} | |
if (StrLen (Description) == 0) { | |
StrnCpy (Description, DEFAULT_BOOT_ENTRY_DESCRIPTION, MAX_STR_INPUT); | |
} | |
BdsLoadOption->Description = Description; | |
// CmdLine | |
Print (L"Command Line: "); | |
Status = EditHIInputAscii (CmdLine, MAX_ASCII_INPUT); | |
if (EFI_ERROR(Status)) { | |
return Status; | |
} | |
// Initrd | |
Print (L"Initrd name: "); | |
Status = EditHIInputStr (Initrd, MAX_STR_INPUT); | |
if (EFI_ERROR(Status)) { | |
return Status; | |
} | |
CmdLineLength = AsciiStrLen (CmdLine); | |
if (CmdLineLength > 0) { | |
CmdLineLength += sizeof(CHAR8); | |
} | |
InitrdPathListLength = StrLen (Initrd) * sizeof(CHAR16); | |
if (InitrdPathListLength > 0) { | |
InitrdPathListLength += sizeof(CHAR16); | |
} | |
BdsLoadOption->OptionalDataSize = sizeof(LINUX_LOADER_OPTIONAL_DATA) + CmdLineLength + InitrdPathListLength; | |
LinuxOptionalData = (LINUX_LOADER_OPTIONAL_DATA*)AllocatePool (BdsLoadOption->OptionalDataSize); | |
BdsLoadOption->OptionalData = LinuxOptionalData; | |
LinuxOptionalData->Signature = LINUX_LOADER_SIGNATURE; | |
LinuxOptionalData->CmdLineLength = CmdLineLength; | |
LinuxOptionalData->InitrdPathListLength = InitrdPathListLength; | |
if (CmdLineLength > 0) { | |
CopyMem (LinuxOptionalData + 1, CmdLine, CmdLineLength); | |
} | |
if (InitrdPathListLength > 0) { | |
CopyMem ((UINT8*)(LinuxOptionalData + 1) + CmdLineLength, Initrd, InitrdPathListLength); | |
} | |
// Create or Update the boot entry | |
Status = BootOptionToLoadOptionVariable (BdsLoadOption); | |
return Status; | |
} |