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
This commit is contained in:
Olivier Martin 2015-07-14 14:41:46 +00:00 committed by oliviermartin
parent a81ccae41a
commit 5d9e9d1a1c
6 changed files with 274 additions and 53 deletions

View File

@ -61,6 +61,9 @@
# we assume the OS will handle the FrameBuffer from the UEFI GOP information. # we assume the OS will handle the FrameBuffer from the UEFI GOP information.
gArmPlatformTokenSpaceGuid.PcdGopDisableOnExitBootServices|FALSE|BOOLEAN|0x0000003D gArmPlatformTokenSpaceGuid.PcdGopDisableOnExitBootServices|FALSE|BOOLEAN|0x0000003D
# Enable Legacy Linux support in the BDS
gArmPlatformTokenSpaceGuid.PcdBdsLinuxSupport|TRUE|BOOLEAN|0x0000002E
[PcdsFixedAtBuild.common] [PcdsFixedAtBuild.common]
gArmPlatformTokenSpaceGuid.PcdCoreCount|1|UINT32|0x00000039 gArmPlatformTokenSpaceGuid.PcdCoreCount|1|UINT32|0x00000039
gArmPlatformTokenSpaceGuid.PcdClusterCount|1|UINT32|0x00000038 gArmPlatformTokenSpaceGuid.PcdClusterCount|1|UINT32|0x00000038

View File

@ -27,6 +27,7 @@
[Sources] [Sources]
Bds.c Bds.c
BdsHelper.c BdsHelper.c
BootLinux.c
BootMenu.c BootMenu.c
BootOption.c BootOption.c
BootOptionSupport.c BootOptionSupport.c
@ -72,6 +73,9 @@
gEfiDhcp4ServiceBindingProtocolGuid gEfiDhcp4ServiceBindingProtocolGuid
gEfiMtftp4ServiceBindingProtocolGuid gEfiMtftp4ServiceBindingProtocolGuid
[FeaturePcd]
gArmPlatformTokenSpaceGuid.PcdBdsLinuxSupport
[Pcd] [Pcd]
gArmPlatformTokenSpaceGuid.PcdFirmwareVendor gArmPlatformTokenSpaceGuid.PcdFirmwareVendor
gArmPlatformTokenSpaceGuid.PcdDefaultBootDescription gArmPlatformTokenSpaceGuid.PcdDefaultBootDescription

View File

@ -256,15 +256,6 @@ GetHIInputBoolean (
} }
} }
BOOLEAN
HasFilePathEfiExtension (
IN CHAR16* FilePath
)
{
return (StrCmp (FilePath + (StrSize (FilePath) / sizeof (CHAR16)) - 5, L".EFI") == 0) ||
(StrCmp (FilePath + (StrSize (FilePath) / sizeof (CHAR16)) - 5, L".efi") == 0);
}
// Return the last non end-type Device Path Node from a Device Path // Return the last non end-type Device Path Node from a Device Path
EFI_DEVICE_PATH* EFI_DEVICE_PATH*
GetLastDevicePathNode ( GetLastDevicePathNode (

View File

@ -1,6 +1,6 @@
/** @file /** @file
* *
* Copyright (c) 2011-2014, ARM Limited. All rights reserved. * Copyright (c) 2011-2015, ARM Limited. All rights reserved.
* *
* This program and the accompanying materials * This program and the accompanying materials
* are licensed and made available under the terms and conditions of the BSD License * are licensed and made available under the terms and conditions of the BSD License
@ -79,6 +79,12 @@ typedef struct _BDS_LOAD_OPTION_SUPPORT {
#define LOAD_OPTION_ENTRY_FROM_LINK(a) BASE_CR(a, BDS_LOAD_OPTION_ENTRY, Link) #define LOAD_OPTION_ENTRY_FROM_LINK(a) BASE_CR(a, BDS_LOAD_OPTION_ENTRY, Link)
#define LOAD_OPTION_FROM_LINK(a) ((BDS_LOAD_OPTION_ENTRY*)BASE_CR(a, BDS_LOAD_OPTION_ENTRY, Link))->BdsLoadOption #define LOAD_OPTION_FROM_LINK(a) ((BDS_LOAD_OPTION_ENTRY*)BASE_CR(a, BDS_LOAD_OPTION_ENTRY, Link))->BdsLoadOption
// GUID of the EFI Linux Loader
extern CONST EFI_GUID mLinuxLoaderAppGuid;
// Device path of the EFI Linux Loader in the Firmware Volume
extern EFI_DEVICE_PATH* mLinuxLoaderDevicePath;
EFI_STATUS EFI_STATUS
BootDeviceListSupportedInit ( BootDeviceListSupportedInit (
IN OUT LIST_ENTRY *SupportedDeviceList IN OUT LIST_ENTRY *SupportedDeviceList
@ -141,11 +147,6 @@ GetHIInputBoolean (
OUT BOOLEAN *Value OUT BOOLEAN *Value
); );
BOOLEAN
HasFilePathEfiExtension (
IN CHAR16* FilePath
);
EFI_DEVICE_PATH* EFI_DEVICE_PATH*
GetLastDevicePathNode ( GetLastDevicePathNode (
IN EFI_DEVICE_PATH* DevicePath IN EFI_DEVICE_PATH* DevicePath
@ -260,4 +261,17 @@ EmptyCallbackFunction (
IN VOID *Context IN VOID *Context
); );
/**
* This function check if the DevicePath defines an EFI binary
*
* This function is used when the BDS support Linux loader to
* detect if the binary is an EFI application or potentially a
* Linux kernel.
*/
EFI_STATUS
IsEfiBinary (
IN EFI_DEVICE_PATH* DevicePath,
OUT BOOLEAN *EfiBinary
);
#endif /* _BDSINTERNAL_H_ */ #endif /* _BDSINTERNAL_H_ */

View File

@ -0,0 +1,124 @@
/** @file
*
* 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
* 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 "BdsInternal.h"
// This GUID is defined in the INGF file of ArmPkg/Application/LinuxLoader
CONST EFI_GUID mLinuxLoaderAppGuid = { 0x701f54f2, 0x0d70, 0x4b89, { 0xbc, 0x0a, 0xd9, 0xca, 0x25, 0x37, 0x90, 0x59 }};
// Device path of the EFI Linux Loader in the Firmware Volume
EFI_DEVICE_PATH* mLinuxLoaderDevicePath = NULL;
STATIC
BOOLEAN
HasFilePathEfiExtension (
IN CHAR16* FilePath
)
{
return (StrCmp (FilePath + (StrSize (FilePath) / sizeof (CHAR16)) - 5, L".EFI") == 0) ||
(StrCmp (FilePath + (StrSize (FilePath) / sizeof (CHAR16)) - 5, L".efi") == 0);
}
/**
* This function check if the DevicePath defines an EFI binary
*
* This function is used when the BDS support Linux loader to
* detect if the binary is an EFI application or potentially a
* Linux kernel.
*/
EFI_STATUS
IsEfiBinary (
IN EFI_DEVICE_PATH* DevicePath,
OUT BOOLEAN *EfiBinary
)
{
EFI_STATUS Status;
CHAR16* FileName;
EFI_DEVICE_PATH* PrevDevicePathNode;
EFI_DEVICE_PATH* DevicePathNode;
EFI_PHYSICAL_ADDRESS Image;
UINTN FileSize;
EFI_IMAGE_DOS_HEADER* DosHeader;
UINTN PeCoffHeaderOffset;
EFI_IMAGE_NT_HEADERS32* NtHeader;
ASSERT (EfiBinary != NULL);
//
// Check if the last node of the device path is a FilePath node
//
PrevDevicePathNode = NULL;
DevicePathNode = DevicePath;
while ((DevicePathNode != NULL) && !IsDevicePathEnd (DevicePathNode)) {
PrevDevicePathNode = DevicePathNode;
DevicePathNode = NextDevicePathNode (DevicePathNode);
}
if ((PrevDevicePathNode != NULL) &&
(PrevDevicePathNode->Type == MEDIA_DEVICE_PATH) &&
(PrevDevicePathNode->SubType == MEDIA_FILEPATH_DP))
{
FileName = ((FILEPATH_DEVICE_PATH*)PrevDevicePathNode)->PathName;
} else {
FileName = NULL;
}
if (FileName == NULL) {
Print (L"Is an EFI Application? ");
Status = GetHIInputBoolean (EfiBinary);
if (EFI_ERROR (Status)) {
return EFI_ABORTED;
}
} else if (HasFilePathEfiExtension (FileName)) {
*EfiBinary = TRUE;
} else {
// Check if the file exist
Status = BdsLoadImage (DevicePath, AllocateAnyPages, &Image, &FileSize);
if (!EFI_ERROR (Status)) {
DosHeader = (EFI_IMAGE_DOS_HEADER *)(UINTN) Image;
if (DosHeader->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
//
// DOS image header is present,
// so read the PE header after the DOS image header.
//
PeCoffHeaderOffset = DosHeader->e_lfanew;
} else {
PeCoffHeaderOffset = 0;
}
//
// Check PE/COFF image.
//
NtHeader = (EFI_IMAGE_NT_HEADERS32 *)(UINTN) (Image + PeCoffHeaderOffset);
if (NtHeader->Signature != EFI_IMAGE_NT_SIGNATURE) {
*EfiBinary = FALSE;
} else {
*EfiBinary = TRUE;
}
// Free memory
gBS->FreePages (Image, EFI_SIZE_TO_PAGES (FileSize));
} else {
// If we did not manage to open it then ask for the type
Print (L"Is an EFI Application? ");
Status = GetHIInputBoolean (EfiBinary);
if (EFI_ERROR (Status)) {
return EFI_ABORTED;
}
}
}
return EFI_SUCCESS;
}

View File

@ -269,6 +269,8 @@ BootMenuAddBootOption (
EFI_DEVICE_PATH_PROTOCOL *DevicePathNodes; EFI_DEVICE_PATH_PROTOCOL *DevicePathNodes;
UINT8* OptionalData; UINT8* OptionalData;
UINTN OptionalDataSize; UINTN OptionalDataSize;
BOOLEAN EfiBinary;
CHAR16 *LinuxDevicePath;
Attributes = 0; Attributes = 0;
SupportedBootDevice = NULL; SupportedBootDevice = NULL;
@ -281,8 +283,12 @@ BootMenuAddBootOption (
} }
// Create the specific device path node // Create the specific device path node
Status = SupportedBootDevice->Support->CreateDevicePathNode (L"EFI Application or the kernel", &DevicePathNodes); if (FeaturePcdGet (PcdBdsLinuxSupport) && mLinuxLoaderDevicePath) {
if (EFI_ERROR(Status)) { Status = SupportedBootDevice->Support->CreateDevicePathNode (L"EFI Application or the kernel", &DevicePathNodes);
} else {
Status = SupportedBootDevice->Support->CreateDevicePathNode (L"EFI Application", &DevicePathNodes);
}
if (EFI_ERROR (Status)) {
Status = EFI_ABORTED; Status = EFI_ABORTED;
goto EXIT; goto EXIT;
} }
@ -293,8 +299,39 @@ BootMenuAddBootOption (
goto EXIT; goto EXIT;
} }
// Is it an EFI application?
if (FeaturePcdGet (PcdBdsLinuxSupport) && mLinuxLoaderDevicePath) {
Status = IsEfiBinary (DevicePath, &EfiBinary);
if (EFI_ERROR (Status)) {
Status = EFI_ABORTED;
goto EXIT;
}
if (EfiBinary == FALSE) {
Print (L"It is assumed the binary is a Linux kernel and the embedded Linux Loader is going to be used.\n");
Print (L"Supported command line formats by the embedded Linux Loader:\n");
Print (L"- <EFI device path of the Linux kernel> -c \"<Linux kernel command line>\"\n");
Print (L"- <EFI device path of the Linux kernel> -c \"<Linux kernel command line>\" -f <EFI Device Path of the Linux initrd>\n");
Print (L"- <EFI device path of the Linux kernel> -c \"<Linux kernel command line>\" -a <Machine Type for ATAG Linux kernel>\n");
// Copy the Linux path into the command line
LinuxDevicePath = ConvertDevicePathToText (DevicePath, FALSE, FALSE);
CopyMem (CmdLine, LinuxDevicePath, MAX (sizeof (CmdLine), StrSize (LinuxDevicePath)));
FreePool (LinuxDevicePath);
// Free the generated Device Path
FreePool (DevicePath);
// and use the embedded Linux Loader as the EFI application
DevicePath = mLinuxLoaderDevicePath;
} else {
CmdLine[0] = L'\0';
}
} else {
CmdLine[0] = L'\0';
}
Print (L"Arguments to pass to the EFI Application: "); Print (L"Arguments to pass to the EFI Application: ");
Status = GetHIInputStr (CmdLine, BOOT_DEVICE_OPTION_MAX); Status = EditHIInputStr (CmdLine, BOOT_DEVICE_OPTION_MAX);
if (EFI_ERROR (Status)) { if (EFI_ERROR (Status)) {
Status = EFI_ABORTED; Status = EFI_ABORTED;
goto EXIT; goto EXIT;
@ -366,11 +403,13 @@ BootMenuUpdateBootOption (
CHAR16 BootDescription[BOOT_DEVICE_DESCRIPTION_MAX]; CHAR16 BootDescription[BOOT_DEVICE_DESCRIPTION_MAX];
CHAR8 CmdLine[BOOT_DEVICE_OPTION_MAX]; CHAR8 CmdLine[BOOT_DEVICE_OPTION_MAX];
CHAR16 UnicodeCmdLine[BOOT_DEVICE_OPTION_MAX]; CHAR16 UnicodeCmdLine[BOOT_DEVICE_OPTION_MAX];
CHAR16 *LinuxDevicePath;
EFI_DEVICE_PATH *DevicePath; EFI_DEVICE_PATH *DevicePath;
UINT8* OptionalData; UINT8* OptionalData;
UINTN OptionalDataSize; UINTN OptionalDataSize;
BOOLEAN IsPrintable; BOOLEAN IsPrintable;
BOOLEAN IsUnicode; BOOLEAN IsUnicode;
BOOLEAN EfiBinary;
DisplayBootOptions (BootOptionsList); DisplayBootOptions (BootOptionsList);
Status = SelectBootOption (BootOptionsList, UPDATE_BOOT_ENTRY, &BootOptionEntry); Status = SelectBootOption (BootOptionsList, UPDATE_BOOT_ENTRY, &BootOptionEntry);
@ -386,46 +425,87 @@ BootMenuUpdateBootOption (
return EFI_UNSUPPORTED; return EFI_UNSUPPORTED;
} }
Status = DeviceSupport->UpdateDevicePathNode (BootOption->FilePathList, L"EFI Application or the kernel", &DevicePath); EfiBinary = TRUE;
if (EFI_ERROR(Status)) { if (FeaturePcdGet (PcdBdsLinuxSupport) && mLinuxLoaderDevicePath) {
Status = EFI_ABORTED; Status = DeviceSupport->UpdateDevicePathNode (BootOption->FilePathList, L"EFI Application or the kernel", &DevicePath);
goto EXIT; if (EFI_ERROR (Status)) {
Status = EFI_ABORTED;
goto EXIT;
}
// Is it an EFI application?
Status = IsEfiBinary (DevicePath, &EfiBinary);
if (EFI_ERROR (Status)) {
Status = EFI_ABORTED;
goto EXIT;
}
if (EfiBinary == FALSE) {
Print (L"It is assumed the binary is a Linux kernel and the embedded Linux Loader is going to be used.\n");
Print (L"Supported command line formats by the embedded Linux Loader:\n");
Print (L"- <EFI device path of the Linux kernel> -c \"<Linux kernel command line>\"\n");
Print (L"- <EFI device path of the Linux kernel> -c \"<Linux kernel command line>\" -f <EFI Device Path of the Linux initrd>\n");
Print (L"- <EFI device path of the Linux kernel> -c \"<Linux kernel command line>\" -a <Machine Type for ATAG Linux kernel>\n");
// Copy the Linux path into the command line
LinuxDevicePath = ConvertDevicePathToText (DevicePath, FALSE, FALSE);
CopyMem (UnicodeCmdLine, LinuxDevicePath, MAX (sizeof (UnicodeCmdLine), StrSize (LinuxDevicePath)));
FreePool (LinuxDevicePath);
// Free the generated Device Path
FreePool (DevicePath);
// and use the embedded Linux Loader as the EFI application
DevicePath = mLinuxLoaderDevicePath;
// The command line is a unicode printable string
IsPrintable = TRUE;
IsUnicode = TRUE;
}
} else {
Status = DeviceSupport->UpdateDevicePathNode (BootOption->FilePathList, L"EFI Application", &DevicePath);
if (EFI_ERROR (Status)) {
Status = EFI_ABORTED;
goto EXIT;
}
} }
Print (L"Arguments to pass to the EFI Application: "); Print (L"Arguments to pass to the EFI Application: ");
if (BootOption->OptionalDataSize > 0) { // When the command line has not been initialized by the embedded Linux loader earlier
IsPrintable = IsPrintableString (BootOption->OptionalData, &IsUnicode); if (EfiBinary) {
if (IsPrintable) { if (BootOption->OptionalDataSize > 0) {
// IsPrintable = IsPrintableString (BootOption->OptionalData, &IsUnicode);
// The size in bytes of the string, final zero included, should if (IsPrintable) {
// be equal to or at least lower than "BootOption->OptionalDataSize" //
// and the "IsPrintableString()" has already tested that the length // The size in bytes of the string, final zero included, should
// in number of characters is smaller than BOOT_DEVICE_OPTION_MAX, // be equal to or at least lower than "BootOption->OptionalDataSize"
// final '\0' included. We can thus copy the string for editing // and the "IsPrintableString()" has already tested that the length
// using "CopyMem()". Furthermore, note that in the case of an Unicode // in number of characters is smaller than BOOT_DEVICE_OPTION_MAX,
// string "StrnCpy()" and "StrCpy()" can not be used to copy the // final '\0' included. We can thus copy the string for editing
// string because the data pointed to by "BootOption->OptionalData" // using "CopyMem()". Furthermore, note that in the case of an Unicode
// is not necessarily 2-byte aligned. // string "StrnCpy()" and "StrCpy()" can not be used to copy the
// // string because the data pointed to by "BootOption->OptionalData"
if (IsUnicode) { // is not necessarily 2-byte aligned.
CopyMem ( //
UnicodeCmdLine, BootOption->OptionalData, if (IsUnicode) {
MIN (sizeof (UnicodeCmdLine), CopyMem (
BootOption->OptionalDataSize) UnicodeCmdLine, BootOption->OptionalData,
); MIN (sizeof (UnicodeCmdLine),
} else { BootOption->OptionalDataSize)
CopyMem ( );
CmdLine, BootOption->OptionalData, } else {
MIN (sizeof (CmdLine), CopyMem (
BootOption->OptionalDataSize) CmdLine, BootOption->OptionalData,
); MIN (sizeof (CmdLine),
BootOption->OptionalDataSize)
);
}
} }
} else {
UnicodeCmdLine[0] = L'\0';
IsPrintable = TRUE;
IsUnicode = TRUE;
} }
} else {
UnicodeCmdLine[0] = L'\0';
IsPrintable = TRUE;
IsUnicode = TRUE;
} }
// We do not request arguments for OptionalData that cannot be printed // We do not request arguments for OptionalData that cannot be printed
@ -909,7 +989,6 @@ struct BOOT_MAIN_ENTRY {
{ L"Boot Manager", BootMenuManager }, { L"Boot Manager", BootMenuManager },
}; };
EFI_STATUS EFI_STATUS
BootMenuMain ( BootMenuMain (
VOID VOID
@ -929,6 +1008,12 @@ BootMenuMain (
BootOption = NULL; BootOption = NULL;
BootMainEntryCount = sizeof(BootMainEntries) / sizeof(struct BOOT_MAIN_ENTRY); BootMainEntryCount = sizeof(BootMainEntries) / sizeof(struct BOOT_MAIN_ENTRY);
if (FeaturePcdGet (PcdBdsLinuxSupport)) {
// Check Linux Loader is present
Status = LocateEfiApplicationInFvByGuid (&mLinuxLoaderAppGuid, &mLinuxLoaderDevicePath);
ASSERT_EFI_ERROR (Status);
}
while (TRUE) { while (TRUE) {
// Get Boot#### list // Get Boot#### list
BootOptionList (&BootOptionsList); BootOptionList (&BootOptionsList);