| /** @file |
| * |
| * Copyright (c) 2011-2012, 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" |
| |
| //TODO: RemoveMe |
| #include <Protocol/DevicePathToText.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; |
| |
| 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 (&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 = GetEnvironmentVariable (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 (&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; |
| } |