diff --git a/ArmPlatformPkg/ArmPlatformPkg.dec b/ArmPlatformPkg/ArmPlatformPkg.dec index be0919b1e0..45aeaeee75 100644 --- a/ArmPlatformPkg/ArmPlatformPkg.dec +++ b/ArmPlatformPkg/ArmPlatformPkg.dec @@ -61,6 +61,9 @@ # we assume the OS will handle the FrameBuffer from the UEFI GOP information. gArmPlatformTokenSpaceGuid.PcdGopDisableOnExitBootServices|FALSE|BOOLEAN|0x0000003D + # Enable Legacy Linux support in the BDS + gArmPlatformTokenSpaceGuid.PcdBdsLinuxSupport|TRUE|BOOLEAN|0x0000002E + [PcdsFixedAtBuild.common] gArmPlatformTokenSpaceGuid.PcdCoreCount|1|UINT32|0x00000039 gArmPlatformTokenSpaceGuid.PcdClusterCount|1|UINT32|0x00000038 diff --git a/ArmPlatformPkg/Bds/Bds.inf b/ArmPlatformPkg/Bds/Bds.inf index 3b6ffc38a8..06e8d914fd 100644 --- a/ArmPlatformPkg/Bds/Bds.inf +++ b/ArmPlatformPkg/Bds/Bds.inf @@ -27,6 +27,7 @@ [Sources] Bds.c BdsHelper.c + BootLinux.c BootMenu.c BootOption.c BootOptionSupport.c @@ -72,6 +73,9 @@ gEfiDhcp4ServiceBindingProtocolGuid gEfiMtftp4ServiceBindingProtocolGuid +[FeaturePcd] + gArmPlatformTokenSpaceGuid.PcdBdsLinuxSupport + [Pcd] gArmPlatformTokenSpaceGuid.PcdFirmwareVendor gArmPlatformTokenSpaceGuid.PcdDefaultBootDescription diff --git a/ArmPlatformPkg/Bds/BdsHelper.c b/ArmPlatformPkg/Bds/BdsHelper.c index b3003e99b4..732292cdb9 100644 --- a/ArmPlatformPkg/Bds/BdsHelper.c +++ b/ArmPlatformPkg/Bds/BdsHelper.c @@ -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 EFI_DEVICE_PATH* GetLastDevicePathNode ( diff --git a/ArmPlatformPkg/Bds/BdsInternal.h b/ArmPlatformPkg/Bds/BdsInternal.h index fe4fd79289..ddf5308e8b 100644 --- a/ArmPlatformPkg/Bds/BdsInternal.h +++ b/ArmPlatformPkg/Bds/BdsInternal.h @@ -1,6 +1,6 @@ /** @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 * 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_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 BootDeviceListSupportedInit ( IN OUT LIST_ENTRY *SupportedDeviceList @@ -141,11 +147,6 @@ GetHIInputBoolean ( OUT BOOLEAN *Value ); -BOOLEAN -HasFilePathEfiExtension ( - IN CHAR16* FilePath - ); - EFI_DEVICE_PATH* GetLastDevicePathNode ( IN EFI_DEVICE_PATH* DevicePath @@ -260,4 +261,17 @@ EmptyCallbackFunction ( 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_ */ diff --git a/ArmPlatformPkg/Bds/BootLinux.c b/ArmPlatformPkg/Bds/BootLinux.c new file mode 100644 index 0000000000..0445e894e1 --- /dev/null +++ b/ArmPlatformPkg/Bds/BootLinux.c @@ -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; +} diff --git a/ArmPlatformPkg/Bds/BootMenu.c b/ArmPlatformPkg/Bds/BootMenu.c index af7f1f1ac4..8caa3ce5ce 100644 --- a/ArmPlatformPkg/Bds/BootMenu.c +++ b/ArmPlatformPkg/Bds/BootMenu.c @@ -269,6 +269,8 @@ BootMenuAddBootOption ( EFI_DEVICE_PATH_PROTOCOL *DevicePathNodes; UINT8* OptionalData; UINTN OptionalDataSize; + BOOLEAN EfiBinary; + CHAR16 *LinuxDevicePath; Attributes = 0; SupportedBootDevice = NULL; @@ -281,8 +283,12 @@ BootMenuAddBootOption ( } // Create the specific device path node - Status = SupportedBootDevice->Support->CreateDevicePathNode (L"EFI Application or the kernel", &DevicePathNodes); - if (EFI_ERROR(Status)) { + if (FeaturePcdGet (PcdBdsLinuxSupport) && mLinuxLoaderDevicePath) { + 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; goto EXIT; } @@ -293,8 +299,39 @@ BootMenuAddBootOption ( 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"- -c \"\"\n"); + Print (L"- -c \"\" -f \n"); + Print (L"- -c \"\" -a \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: "); - Status = GetHIInputStr (CmdLine, BOOT_DEVICE_OPTION_MAX); + Status = EditHIInputStr (CmdLine, BOOT_DEVICE_OPTION_MAX); if (EFI_ERROR (Status)) { Status = EFI_ABORTED; goto EXIT; @@ -366,11 +403,13 @@ BootMenuUpdateBootOption ( CHAR16 BootDescription[BOOT_DEVICE_DESCRIPTION_MAX]; CHAR8 CmdLine[BOOT_DEVICE_OPTION_MAX]; CHAR16 UnicodeCmdLine[BOOT_DEVICE_OPTION_MAX]; + CHAR16 *LinuxDevicePath; EFI_DEVICE_PATH *DevicePath; UINT8* OptionalData; UINTN OptionalDataSize; BOOLEAN IsPrintable; BOOLEAN IsUnicode; + BOOLEAN EfiBinary; DisplayBootOptions (BootOptionsList); Status = SelectBootOption (BootOptionsList, UPDATE_BOOT_ENTRY, &BootOptionEntry); @@ -386,46 +425,87 @@ BootMenuUpdateBootOption ( return EFI_UNSUPPORTED; } - Status = DeviceSupport->UpdateDevicePathNode (BootOption->FilePathList, L"EFI Application or the kernel", &DevicePath); - if (EFI_ERROR(Status)) { - Status = EFI_ABORTED; - goto EXIT; + EfiBinary = TRUE; + if (FeaturePcdGet (PcdBdsLinuxSupport) && mLinuxLoaderDevicePath) { + Status = DeviceSupport->UpdateDevicePathNode (BootOption->FilePathList, L"EFI Application or the kernel", &DevicePath); + 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"- -c \"\"\n"); + Print (L"- -c \"\" -f \n"); + Print (L"- -c \"\" -a \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: "); - if (BootOption->OptionalDataSize > 0) { - IsPrintable = IsPrintableString (BootOption->OptionalData, &IsUnicode); - if (IsPrintable) { - // - // The size in bytes of the string, final zero included, should - // be equal to or at least lower than "BootOption->OptionalDataSize" - // and the "IsPrintableString()" has already tested that the length - // in number of characters is smaller than BOOT_DEVICE_OPTION_MAX, - // final '\0' included. We can thus copy the string for editing - // using "CopyMem()". Furthermore, note that in the case of an Unicode - // string "StrnCpy()" and "StrCpy()" can not be used to copy the - // string because the data pointed to by "BootOption->OptionalData" - // is not necessarily 2-byte aligned. - // - if (IsUnicode) { - CopyMem ( - UnicodeCmdLine, BootOption->OptionalData, - MIN (sizeof (UnicodeCmdLine), - BootOption->OptionalDataSize) - ); - } else { - CopyMem ( - CmdLine, BootOption->OptionalData, - MIN (sizeof (CmdLine), - BootOption->OptionalDataSize) - ); + // When the command line has not been initialized by the embedded Linux loader earlier + if (EfiBinary) { + if (BootOption->OptionalDataSize > 0) { + IsPrintable = IsPrintableString (BootOption->OptionalData, &IsUnicode); + if (IsPrintable) { + // + // The size in bytes of the string, final zero included, should + // be equal to or at least lower than "BootOption->OptionalDataSize" + // and the "IsPrintableString()" has already tested that the length + // in number of characters is smaller than BOOT_DEVICE_OPTION_MAX, + // final '\0' included. We can thus copy the string for editing + // using "CopyMem()". Furthermore, note that in the case of an Unicode + // string "StrnCpy()" and "StrCpy()" can not be used to copy the + // string because the data pointed to by "BootOption->OptionalData" + // is not necessarily 2-byte aligned. + // + if (IsUnicode) { + CopyMem ( + UnicodeCmdLine, BootOption->OptionalData, + MIN (sizeof (UnicodeCmdLine), + BootOption->OptionalDataSize) + ); + } else { + CopyMem ( + 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 @@ -909,7 +989,6 @@ struct BOOT_MAIN_ENTRY { { L"Boot Manager", BootMenuManager }, }; - EFI_STATUS BootMenuMain ( VOID @@ -929,6 +1008,12 @@ BootMenuMain ( BootOption = NULL; 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) { // Get Boot#### list BootOptionList (&BootOptionsList);