Olivier Martin 5d9e9d1a1c ArmPlatformPkg/Bds: Added support for booting legacy kernel from BDS
When PcdBdsLinuxSupport is enabled, users can create boot
entries for the legacy EFI Linux loader.

The ARM BDS detects if the image is a EFI image if not
then it assumes it is a legacy Linux kernel (a kernel
without EFI Stub).
If the Boot Manager did not manage to load the binary
(it could happen when the binary is on the network or
not present on the media yet).

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Olivier Martin <Olivier.Martin@arm.com>
Reviewed-by: Ronald Cron <Ronald.Cron@arm.com>



git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@17974 6f19259b-4bc3-4df7-8a09-765794883524
2015-07-14 14:41:46 +00:00

466 lines
12 KiB
C

/** @file
*
* Copyright (c) 2011 - 2014, 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 <Library/NetLib.h>
#include "BdsInternal.h"
EFI_STATUS
EditHIInputStr (
IN OUT CHAR16 *CmdLine,
IN UINTN MaxCmdLine
)
{
UINTN CmdLineIndex;
UINTN WaitIndex;
CHAR8 Char;
EFI_INPUT_KEY Key;
EFI_STATUS Status;
// The command line must be at least one character long
ASSERT (MaxCmdLine > 0);
// Ensure the last character of the buffer is the NULL character
CmdLine[MaxCmdLine - 1] = '\0';
Print (CmdLine);
// To prevent a buffer overflow, we only allow to enter (MaxCmdLine-1) characters
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"\r\n");
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 if (CmdLineIndex < (MaxCmdLine-1)) {
CmdLine[CmdLineIndex++] = Key.UnicodeChar;
Print (L"%c", Key.UnicodeChar);
}
}
return EFI_SUCCESS;
}
EFI_STATUS
GetHIInputStr (
IN OUT CHAR16 *CmdLine,
IN UINTN MaxCmdLine
)
{
EFI_STATUS Status;
// For a new input just passed an empty string
CmdLine[0] = L'\0';
Status = EditHIInputStr (CmdLine, MaxCmdLine);
return Status;
}
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);
if (!EFI_ERROR(Status)) {
UnicodeStrToAsciiStr (Str, CmdLine);
}
FreePool (Str);
return Status;
}
EFI_STATUS
GetHIInputAscii (
IN OUT CHAR8 *CmdLine,
IN UINTN MaxCmdLine
)
{
// For a new input just passed an empty string
CmdLine[0] = '\0';
return EditHIInputAscii (CmdLine,MaxCmdLine);
}
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;
}
/**
Get an IPv4 address
The function asks the user for an IPv4 address. If the input
string defines a valid IPv4 address, the four bytes of the
corresponding IPv4 address are extracted from the string and returned by
the function. As long as the user does not define a valid IP
address, he is asked for one. He can always escape by
pressing ESC.
@param[out] EFI_IP_ADDRESS OutIpAddr Returned IPv4 address. Valid if
and only if the returned value
is equal to EFI_SUCCESS
@retval EFI_SUCCESS Input completed
@retval EFI_ABORTED Editing aborted by the user
@retval EFI_OUT_OF_RESOURCES Fail to perform the operation due to
lack of resource
**/
EFI_STATUS
GetHIInputIP (
OUT EFI_IP_ADDRESS *OutIpAddr
)
{
EFI_STATUS Status;
CHAR16 CmdLine[48];
while (TRUE) {
CmdLine[0] = '\0';
Status = EditHIInputStr (CmdLine, 48);
if (EFI_ERROR (Status)) {
return EFI_ABORTED;
}
Status = NetLibStrToIp4 (CmdLine, &OutIpAddr->v4);
if (Status == EFI_INVALID_PARAMETER) {
Print (L"Invalid address\n");
} else {
return Status;
}
}
}
/**
Edit an IPv4 address
The function displays as a string following the "%d.%d.%d.%d" format the
IPv4 address that is passed in and asks the user to modify it. If the
resulting string defines a valid IPv4 address, the four bytes of the
corresponding IPv4 address are extracted from the string and returned by
the function. As long as the user does not define a valid IP
address, he is asked for one. He can always escape by
pressing ESC.
@param[in ] EFI_IP_ADDRESS InIpAddr Input IPv4 address
@param[out] EFI_IP_ADDRESS OutIpAddr Returned IPv4 address. Valid if
and only if the returned value
is equal to EFI_SUCCESS
@retval EFI_SUCCESS Update completed
@retval EFI_ABORTED Editing aborted by the user
@retval EFI_INVALID_PARAMETER The string returned by the user is
mal-formated
@retval EFI_OUT_OF_RESOURCES Fail to perform the operation due to
lack of resource
**/
EFI_STATUS
EditHIInputIP (
IN EFI_IP_ADDRESS *InIpAddr,
OUT EFI_IP_ADDRESS *OutIpAddr
)
{
EFI_STATUS Status;
CHAR16 CmdLine[48];
while (TRUE) {
UnicodeSPrint (
CmdLine, 48, L"%d.%d.%d.%d",
InIpAddr->v4.Addr[0], InIpAddr->v4.Addr[1],
InIpAddr->v4.Addr[2], InIpAddr->v4.Addr[3]
);
Status = EditHIInputStr (CmdLine, 48);
if (EFI_ERROR (Status)) {
return EFI_ABORTED;
}
Status = NetLibStrToIp4 (CmdLine, &OutIpAddr->v4);
if (Status == EFI_INVALID_PARAMETER) {
Print (L"Invalid address\n");
} else {
return Status;
}
}
}
EFI_STATUS
GetHIInputBoolean (
OUT BOOLEAN *Value
)
{
CHAR16 CmdBoolean[2];
EFI_STATUS Status;
while(1) {
Print (L"[y/n] ");
Status = GetHIInputStr (CmdBoolean, 2);
if (EFI_ERROR(Status)) {
return Status;
} else if ((CmdBoolean[0] == L'y') || (CmdBoolean[0] == L'Y')) {
if (Value) *Value = TRUE;
return EFI_SUCCESS;
} else if ((CmdBoolean[0] == L'n') || (CmdBoolean[0] == L'N')) {
if (Value) *Value = FALSE;
return EFI_SUCCESS;
}
}
}
// Return the last non end-type Device Path Node from a Device Path
EFI_DEVICE_PATH*
GetLastDevicePathNode (
IN EFI_DEVICE_PATH* DevicePath
)
{
EFI_DEVICE_PATH* PrevDevicePathNode;
PrevDevicePathNode = DevicePath;
while (!IsDevicePathEndType (DevicePath)) {
PrevDevicePathNode = DevicePath;
DevicePath = NextDevicePathNode (DevicePath);
}
return PrevDevicePathNode;
}
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;
}
EFI_STATUS
BdsStartBootOption (
IN CHAR16* BootOption
)
{
EFI_STATUS Status;
BDS_LOAD_OPTION *BdsLoadOption;
Status = BootOptionFromLoadOptionVariable (BootOption, &BdsLoadOption);
if (!EFI_ERROR(Status)) {
Status = BootOptionStart (BdsLoadOption);
FreePool (BdsLoadOption);
if (!EFI_ERROR(Status)) {
Status = EFI_SUCCESS;
} else {
Status = EFI_NOT_STARTED;
}
} else {
Status = EFI_NOT_FOUND;
}
return Status;
}
UINTN
GetUnalignedDevicePathSize (
IN EFI_DEVICE_PATH* DevicePath
)
{
UINTN Size;
EFI_DEVICE_PATH* AlignedDevicePath;
if ((UINTN)DevicePath & 0x1) {
AlignedDevicePath = DuplicateDevicePath (DevicePath);
Size = GetDevicePathSize (AlignedDevicePath);
FreePool (AlignedDevicePath);
} else {
Size = GetDevicePathSize (DevicePath);
}
return Size;
}
EFI_DEVICE_PATH*
GetAlignedDevicePath (
IN EFI_DEVICE_PATH* DevicePath
)
{
if ((UINTN)DevicePath & 0x1) {
return DuplicateDevicePath (DevicePath);
} else {
return DevicePath;
}
}
BOOLEAN
IsUnicodeString (
IN VOID* String
)
{
// We do not support NULL pointer
ASSERT (String != NULL);
if (*(CHAR16*)String < 0x100) {
//Note: We could get issue if the string is an empty Ascii string...
return TRUE;
} else {
return FALSE;
}
}
/*
* Try to detect if the given string is an ASCII or Unicode string
*
* There are actually few limitation to this function but it is mainly to give
* a user friendly output.
*
* Some limitations:
* - it only supports unicode string that use ASCII character (< 0x100)
* - single character ASCII strings are interpreted as Unicode string
* - string cannot be longer than BOOT_DEVICE_OPTION_MAX characters and
* thus (BOOT_DEVICE_OPTION_MAX*2) bytes for an Unicode string and
* BOOT_DEVICE_OPTION_MAX bytes for an ASCII string.
*
* @param String Buffer that might contain a Unicode or Ascii string
* @param IsUnicode If not NULL this boolean value returns if the string is an
* ASCII or Unicode string.
*/
BOOLEAN
IsPrintableString (
IN VOID* String,
OUT BOOLEAN *IsUnicode
)
{
BOOLEAN UnicodeDetected;
BOOLEAN IsPrintable;
UINTN Index;
CHAR16 Character;
// We do not support NULL pointer
ASSERT (String != NULL);
// Test empty string
if (*(CHAR16*)String == L'\0') {
if (IsUnicode) {
*IsUnicode = TRUE;
}
return TRUE;
} else if (*(CHAR16*)String == '\0') {
if (IsUnicode) {
*IsUnicode = FALSE;
}
return TRUE;
}
// Limitation: if the string is an ASCII single character string. This comparison
// will assume it is a Unicode string.
if (*(CHAR16*)String < 0x100) {
UnicodeDetected = TRUE;
} else {
UnicodeDetected = FALSE;
}
IsPrintable = FALSE;
for (Index = 0; Index < BOOT_DEVICE_OPTION_MAX; Index++) {
if (UnicodeDetected) {
Character = ((CHAR16*)String)[Index];
} else {
Character = ((CHAR8*)String)[Index];
}
if (Character == '\0') {
// End of the string
IsPrintable = TRUE;
break;
} else if ((Character < 0x20) || (Character > 0x7f)) {
// We only support the range of printable ASCII character
IsPrintable = FALSE;
break;
}
}
if (IsPrintable && IsUnicode) {
*IsUnicode = UnicodeDetected;
}
return IsPrintable;
}