mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-29 12:35:53 +01:00
535 lines
14 KiB
C
535 lines
14 KiB
C
/** @file
|
|
Provides 'initrd' dynamic UEFI shell command to load a Linux initrd
|
|
via its GUIDed vendor media path
|
|
|
|
Copyright (c) 2020, Arm, Ltd. All rights reserved.<BR>
|
|
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
**/
|
|
|
|
#include <Uefi.h>
|
|
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/DevicePathLib.h>
|
|
#include <Library/HiiLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/ShellLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/UefiHiiServicesLib.h>
|
|
|
|
#include <Guid/LinuxEfiInitrdMedia.h>
|
|
|
|
#include <Protocol/DevicePath.h>
|
|
#include <Protocol/HiiPackageList.h>
|
|
#include <Protocol/LoadFile2.h>
|
|
#include <Protocol/ShellDynamicCommand.h>
|
|
|
|
#pragma pack (1)
|
|
typedef struct {
|
|
VENDOR_DEVICE_PATH VenMediaNode;
|
|
EFI_DEVICE_PATH_PROTOCOL EndNode;
|
|
} SINGLE_NODE_VENDOR_MEDIA_DEVPATH;
|
|
#pragma pack ()
|
|
|
|
STATIC EFI_HII_HANDLE mLinuxInitrdShellCommandHiiHandle;
|
|
STATIC EFI_PHYSICAL_ADDRESS mInitrdFileAddress;
|
|
STATIC UINTN mInitrdFileSize;
|
|
STATIC EFI_HANDLE mInitrdLoadFile2Handle;
|
|
|
|
STATIC CONST SHELL_PARAM_ITEM ParamList[] = {
|
|
{ L"-u", TypeFlag },
|
|
{ NULL, TypeMax }
|
|
};
|
|
|
|
STATIC CONST SINGLE_NODE_VENDOR_MEDIA_DEVPATH mInitrdDevicePath = {
|
|
{
|
|
{
|
|
MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP,{ sizeof (VENDOR_DEVICE_PATH) }
|
|
},
|
|
LINUX_EFI_INITRD_MEDIA_GUID
|
|
},{
|
|
END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
|
|
{ sizeof (EFI_DEVICE_PATH_PROTOCOL) }
|
|
}
|
|
};
|
|
|
|
STATIC
|
|
BOOLEAN
|
|
IsOtherInitrdDevicePathAlreadyInstalled (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
|
|
EFI_HANDLE Handle;
|
|
|
|
DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)&mInitrdDevicePath;
|
|
Status = gBS->LocateDevicePath (
|
|
&gEfiLoadFile2ProtocolGuid,
|
|
&DevicePath,
|
|
&Handle
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check whether the existing instance is one that we installed during
|
|
// a previous invocation.
|
|
//
|
|
if (Handle == mInitrdLoadFile2Handle) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
InitrdLoadFile2 (
|
|
IN EFI_LOAD_FILE2_PROTOCOL *This,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
|
|
IN BOOLEAN BootPolicy,
|
|
IN OUT UINTN *BufferSize,
|
|
OUT VOID *Buffer OPTIONAL
|
|
)
|
|
{
|
|
if (BootPolicy) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
if ((BufferSize == NULL) || !IsDevicePathValid (FilePath, 0)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((FilePath->Type != END_DEVICE_PATH_TYPE) ||
|
|
(FilePath->SubType != END_ENTIRE_DEVICE_PATH_SUBTYPE) ||
|
|
(mInitrdFileSize == 0))
|
|
{
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
if ((Buffer == NULL) || (*BufferSize < mInitrdFileSize)) {
|
|
*BufferSize = mInitrdFileSize;
|
|
return EFI_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
ASSERT (mInitrdFileAddress != 0);
|
|
|
|
gBS->CopyMem (Buffer, (VOID *)(UINTN)mInitrdFileAddress, mInitrdFileSize);
|
|
*BufferSize = mInitrdFileSize;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
STATIC CONST EFI_LOAD_FILE2_PROTOCOL mInitrdLoadFile2 = {
|
|
InitrdLoadFile2,
|
|
};
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
UninstallLoadFile2Protocol (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
if (mInitrdLoadFile2Handle != NULL) {
|
|
Status = gBS->UninstallMultipleProtocolInterfaces (
|
|
mInitrdLoadFile2Handle,
|
|
&gEfiDevicePathProtocolGuid,
|
|
&mInitrdDevicePath,
|
|
&gEfiLoadFile2ProtocolGuid,
|
|
&mInitrdLoadFile2,
|
|
NULL
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
mInitrdLoadFile2Handle = NULL;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
STATIC
|
|
VOID
|
|
FreeInitrdFile (
|
|
VOID
|
|
)
|
|
{
|
|
if (mInitrdFileSize != 0) {
|
|
gBS->FreePages (mInitrdFileAddress, EFI_SIZE_TO_PAGES (mInitrdFileSize));
|
|
mInitrdFileSize = 0;
|
|
}
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
CacheInitrdFile (
|
|
IN SHELL_FILE_HANDLE FileHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT64 FileSize;
|
|
UINTN ReadSize;
|
|
|
|
Status = gEfiShellProtocol->GetFileSize (FileHandle, &FileSize);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if ((FileSize == 0) || (FileSize > MAX_UINTN)) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
Status = gBS->AllocatePages (
|
|
AllocateAnyPages,
|
|
EfiLoaderData,
|
|
EFI_SIZE_TO_PAGES ((UINTN)FileSize),
|
|
&mInitrdFileAddress
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
ReadSize = (UINTN)FileSize;
|
|
Status = gEfiShellProtocol->ReadFile (
|
|
FileHandle,
|
|
&ReadSize,
|
|
(VOID *)(UINTN)mInitrdFileAddress
|
|
);
|
|
if (EFI_ERROR (Status) || (ReadSize < FileSize)) {
|
|
DEBUG ((
|
|
DEBUG_WARN,
|
|
"%a: failed to read initrd file - %r 0x%lx 0x%lx\n",
|
|
__func__,
|
|
Status,
|
|
(UINT64)ReadSize,
|
|
FileSize
|
|
));
|
|
goto FreeMemory;
|
|
}
|
|
|
|
if (mInitrdLoadFile2Handle == NULL) {
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
|
&mInitrdLoadFile2Handle,
|
|
&gEfiDevicePathProtocolGuid,
|
|
&mInitrdDevicePath,
|
|
&gEfiLoadFile2ProtocolGuid,
|
|
&mInitrdLoadFile2,
|
|
NULL
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
|
|
mInitrdFileSize = (UINTN)FileSize;
|
|
return EFI_SUCCESS;
|
|
|
|
FreeMemory:
|
|
gBS->FreePages (mInitrdFileAddress, EFI_SIZE_TO_PAGES ((UINTN)FileSize));
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Function for 'initrd' command.
|
|
|
|
@param[in] ImageHandle Handle to the Image (NULL if Internal).
|
|
@param[in] SystemTable Pointer to the System Table (NULL if Internal).
|
|
**/
|
|
STATIC
|
|
SHELL_STATUS
|
|
EFIAPI
|
|
RunInitrd (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
LIST_ENTRY *Package;
|
|
CHAR16 *ProblemParam;
|
|
CONST CHAR16 *Param;
|
|
CHAR16 *Filename;
|
|
SHELL_STATUS ShellStatus;
|
|
SHELL_FILE_HANDLE FileHandle;
|
|
|
|
ProblemParam = NULL;
|
|
ShellStatus = SHELL_SUCCESS;
|
|
|
|
Status = ShellInitialize ();
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
//
|
|
// parse the command line
|
|
//
|
|
Status = ShellCommandLineParse (ParamList, &Package, &ProblemParam, TRUE);
|
|
if (EFI_ERROR (Status)) {
|
|
if ((Status == EFI_VOLUME_CORRUPTED) && (ProblemParam != NULL)) {
|
|
ShellPrintHiiEx (
|
|
-1,
|
|
-1,
|
|
NULL,
|
|
STRING_TOKEN (STR_GEN_PROBLEM),
|
|
mLinuxInitrdShellCommandHiiHandle,
|
|
L"initrd",
|
|
ProblemParam
|
|
);
|
|
FreePool (ProblemParam);
|
|
ShellStatus = SHELL_INVALID_PARAMETER;
|
|
} else {
|
|
ASSERT (FALSE);
|
|
}
|
|
} else if (IsOtherInitrdDevicePathAlreadyInstalled ()) {
|
|
ShellPrintHiiEx (
|
|
-1,
|
|
-1,
|
|
NULL,
|
|
STRING_TOKEN (STR_GEN_ALREADY_INSTALLED),
|
|
mLinuxInitrdShellCommandHiiHandle,
|
|
L"initrd"
|
|
);
|
|
ShellStatus = SHELL_UNSUPPORTED;
|
|
} else {
|
|
if (ShellCommandLineGetCount (Package) > 2) {
|
|
ShellPrintHiiEx (
|
|
-1,
|
|
-1,
|
|
NULL,
|
|
STRING_TOKEN (STR_GEN_TOO_MANY),
|
|
mLinuxInitrdShellCommandHiiHandle,
|
|
L"initrd"
|
|
);
|
|
ShellStatus = SHELL_INVALID_PARAMETER;
|
|
} else if (ShellCommandLineGetCount (Package) < 2) {
|
|
if (ShellCommandLineGetFlag (Package, L"-u")) {
|
|
FreeInitrdFile ();
|
|
UninstallLoadFile2Protocol ();
|
|
} else {
|
|
ShellPrintHiiEx (
|
|
-1,
|
|
-1,
|
|
NULL,
|
|
STRING_TOKEN (STR_GEN_TOO_FEW),
|
|
mLinuxInitrdShellCommandHiiHandle,
|
|
L"initrd"
|
|
);
|
|
ShellStatus = SHELL_INVALID_PARAMETER;
|
|
}
|
|
} else {
|
|
Param = ShellCommandLineGetRawValue (Package, 1);
|
|
ASSERT (Param != NULL);
|
|
|
|
Filename = ShellFindFilePath (Param);
|
|
if (Filename == NULL) {
|
|
ShellPrintHiiEx (
|
|
-1,
|
|
-1,
|
|
NULL,
|
|
STRING_TOKEN (STR_GEN_FIND_FAIL),
|
|
mLinuxInitrdShellCommandHiiHandle,
|
|
L"initrd",
|
|
Param
|
|
);
|
|
ShellStatus = SHELL_NOT_FOUND;
|
|
} else {
|
|
Status = ShellOpenFileByName (
|
|
Filename,
|
|
&FileHandle,
|
|
EFI_FILE_MODE_READ,
|
|
0
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
FreeInitrdFile ();
|
|
Status = CacheInitrdFile (FileHandle);
|
|
ShellCloseFile (&FileHandle);
|
|
}
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
ShellPrintHiiEx (
|
|
-1,
|
|
-1,
|
|
NULL,
|
|
STRING_TOKEN (STR_GEN_FILE_OPEN_FAIL),
|
|
mLinuxInitrdShellCommandHiiHandle,
|
|
L"initrd",
|
|
Param
|
|
);
|
|
ShellStatus = SHELL_NOT_FOUND;
|
|
}
|
|
|
|
FreePool (Filename);
|
|
}
|
|
}
|
|
}
|
|
|
|
return ShellStatus;
|
|
}
|
|
|
|
/**
|
|
This is the shell command handler function pointer callback type. This
|
|
function handles the command when it is invoked in the shell.
|
|
|
|
@param[in] This The instance of the
|
|
EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL.
|
|
@param[in] SystemTable The pointer to the system table.
|
|
@param[in] ShellParameters The parameters associated with the command.
|
|
@param[in] Shell The instance of the shell protocol used in
|
|
the context of processing this command.
|
|
|
|
@return EFI_SUCCESS the operation was successful
|
|
@return other the operation failed.
|
|
**/
|
|
SHELL_STATUS
|
|
EFIAPI
|
|
LinuxInitrdCommandHandler (
|
|
IN EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *This,
|
|
IN EFI_SYSTEM_TABLE *SystemTable,
|
|
IN EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters,
|
|
IN EFI_SHELL_PROTOCOL *Shell
|
|
)
|
|
{
|
|
gEfiShellParametersProtocol = ShellParameters;
|
|
gEfiShellProtocol = Shell;
|
|
|
|
return RunInitrd (gImageHandle, SystemTable);
|
|
}
|
|
|
|
/**
|
|
This is the command help handler function pointer callback type. This
|
|
function is responsible for displaying help information for the associated
|
|
command.
|
|
|
|
@param[in] This The instance of the
|
|
EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL.
|
|
@param[in] Language The pointer to the language string to use.
|
|
|
|
@return string Pool allocated help string, must be freed
|
|
by caller
|
|
**/
|
|
STATIC
|
|
CHAR16 *
|
|
EFIAPI
|
|
LinuxInitrdGetHelp (
|
|
IN EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *This,
|
|
IN CONST CHAR8 *Language
|
|
)
|
|
{
|
|
return HiiGetString (
|
|
mLinuxInitrdShellCommandHiiHandle,
|
|
STRING_TOKEN (STR_GET_HELP_INITRD),
|
|
Language
|
|
);
|
|
}
|
|
|
|
STATIC EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL mLinuxInitrdDynamicCommand = {
|
|
L"initrd",
|
|
LinuxInitrdCommandHandler,
|
|
LinuxInitrdGetHelp
|
|
};
|
|
|
|
/**
|
|
Retrieve HII package list from ImageHandle and publish to HII database.
|
|
|
|
@param ImageHandle The image handle of the process.
|
|
|
|
@return HII handle.
|
|
**/
|
|
STATIC
|
|
EFI_HII_HANDLE
|
|
InitializeHiiPackage (
|
|
EFI_HANDLE ImageHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HII_HANDLE HiiHandle;
|
|
|
|
//
|
|
// Publish HII package list to HII Database.
|
|
//
|
|
Status = gHiiDatabase->NewPackageList (
|
|
gHiiDatabase,
|
|
&gModuleHiiPackageList->Header,
|
|
NULL,
|
|
&HiiHandle
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
if (EFI_ERROR (Status)) {
|
|
return NULL;
|
|
}
|
|
|
|
return HiiHandle;
|
|
}
|
|
|
|
/**
|
|
Entry point of Linux Initrd dynamic UEFI Shell command.
|
|
|
|
Produce the DynamicCommand protocol to handle "initrd" command.
|
|
|
|
@param ImageHandle The image handle of the process.
|
|
@param SystemTable The EFI System Table pointer.
|
|
|
|
@retval EFI_SUCCESS Initrd command is executed successfully.
|
|
@retval EFI_ABORTED HII package was failed to initialize.
|
|
@retval others Other errors when executing Initrd command.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
LinuxInitrdDynamicShellCommandEntryPoint (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
mLinuxInitrdShellCommandHiiHandle = InitializeHiiPackage (ImageHandle);
|
|
if (mLinuxInitrdShellCommandHiiHandle == NULL) {
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
Status = gBS->InstallProtocolInterface (
|
|
&ImageHandle,
|
|
&gEfiShellDynamicCommandProtocolGuid,
|
|
EFI_NATIVE_INTERFACE,
|
|
&mLinuxInitrdDynamicCommand
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Unload the dynamic UEFI Shell command.
|
|
|
|
@param ImageHandle The image handle of the process.
|
|
|
|
@retval EFI_SUCCESS The image is unloaded.
|
|
@retval Others Failed to unload the image.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
LinuxInitrdDynamicShellCommandUnload (
|
|
IN EFI_HANDLE ImageHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
FreeInitrdFile ();
|
|
|
|
Status = UninstallLoadFile2Protocol ();
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = gBS->UninstallProtocolInterface (
|
|
ImageHandle,
|
|
&gEfiShellDynamicCommandProtocolGuid,
|
|
&mLinuxInitrdDynamicCommand
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
HiiRemovePackages (mLinuxInitrdShellCommandHiiHandle);
|
|
return EFI_SUCCESS;
|
|
}
|