| /** @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; | |
| } |