Skip to content

Commit

Permalink
loader-proto: Add support for loading files from disk to LoadImage()
Browse files Browse the repository at this point in the history
Currently the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL and EFI_LOAD_FILE2_PROTOCOL
are supported.

Signed-off-by: Mate Kukri <[email protected]>
  • Loading branch information
kukrimate committed Apr 18, 2024
1 parent 1dd055f commit b997ca7
Show file tree
Hide file tree
Showing 5 changed files with 256 additions and 18 deletions.
3 changes: 3 additions & 0 deletions include/guid.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,8 @@ extern EFI_GUID SHIM_IMAGE_LOADER_GUID;
extern EFI_GUID SHIM_LOADED_IMAGE_GUID;
extern EFI_GUID MOK_VARIABLE_STORE;
extern EFI_GUID SECUREBOOT_EFI_NAMESPACE_GUID;
extern EFI_GUID EFI_DEVICE_PATH_GUID;
extern EFI_GUID EFI_LOADED_IMAGE_DEVICE_PATH_GUID;
extern EFI_GUID EFI_LOAD_FILE2_GUID;

#endif /* SHIM_GUID_H */
76 changes: 76 additions & 0 deletions include/lf2.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/** @file
Load File protocol as defined in the UEFI 2.0 specification.
Load file protocol exists to supports the addition of new boot devices,
and to support booting from devices that do not map well to file system.
Network boot is done via a LoadFile protocol.
UEFI 2.0 can boot from any device that produces a LoadFile protocol.
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/

#ifndef __EFI_LOAD_FILE2_PROTOCOL_H__
#define __EFI_LOAD_FILE2_PROTOCOL_H__

#define EFI_LOAD_FILE2_PROTOCOL_GUID \
{ \
0x4006c0c1, 0xfcb3, 0x403e, {0x99, 0x6d, 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d } \
}

///
/// Protocol Guid defined by UEFI2.1.
///
#define LOAD_FILE2_PROTOCOL EFI_LOAD_FILE2_PROTOCOL_GUID

typedef struct _EFI_LOAD_FILE2_PROTOCOL EFI_LOAD_FILE2_PROTOCOL;

/**
Causes the driver to load a specified file.
@param This Protocol instance pointer.
@param FilePath The device specific path of the file to load.
@param BootPolicy Should always be FALSE.
@param BufferSize On input the size of Buffer in bytes. On output with a return
code of EFI_SUCCESS, the amount of data transferred to
Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
the size of Buffer required to retrieve the requested file.
@param Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
then no the size of the requested file is returned in
BufferSize.
@retval EFI_SUCCESS The file was loaded.
@retval EFI_UNSUPPORTED BootPolicy is TRUE.
@retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or
BufferSize is NULL.
@retval EFI_NO_MEDIA No medium was present to load the file.
@retval EFI_DEVICE_ERROR The file was not loaded due to a device error.
@retval EFI_NO_RESPONSE The remote system did not respond.
@retval EFI_NOT_FOUND The file was not found
@retval EFI_ABORTED The file load process was manually canceled.
@retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current
directory entry. BufferSize has been updated with
the size needed to complete the request.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_LOAD_FILE2)(
IN EFI_LOAD_FILE2_PROTOCOL *This,
IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
IN BOOLEAN BootPolicy,
IN OUT UINTN *BufferSize,
IN VOID *Buffer OPTIONAL
);

///
/// The EFI_LOAD_FILE_PROTOCOL is a simple protocol used to obtain files from arbitrary devices.
///
struct _EFI_LOAD_FILE2_PROTOCOL {
EFI_LOAD_FILE2 LoadFile;
};

#endif
3 changes: 3 additions & 0 deletions lib/guid.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,6 @@ EFI_GUID SHIM_IMAGE_LOADER_GUID = {0x1f492041, 0xfadb, 0x4e59, {0x9e, 0x57, 0x7c
EFI_GUID SHIM_LOADED_IMAGE_GUID = {0x6e6baeb8, 0x7108, 0x4179, {0x94, 0x9d, 0xa3, 0x49, 0x34, 0x15, 0xec, 0x97 } };
EFI_GUID MOK_VARIABLE_STORE = {0xc451ed2b, 0x9694, 0x45d3, {0xba, 0xba, 0xed, 0x9f, 0x89, 0x88, 0xa3, 0x89} };
EFI_GUID SECUREBOOT_EFI_NAMESPACE_GUID = {0x77fa9abd, 0x0359, 0x4d32, {0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b} };
EFI_GUID EFI_DEVICE_PATH_GUID = EFI_DEVICE_PATH_PROTOCOL_GUID;
EFI_GUID EFI_LOADED_IMAGE_DEVICE_PATH_GUID = EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID;
EFI_GUID EFI_LOAD_FILE2_GUID = EFI_LOAD_FILE2_PROTOCOL_GUID;
190 changes: 172 additions & 18 deletions loader-proto.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,9 @@
* loader-proto.c - shim's loader protocol
*
* Copyright Red Hat, Inc
* Copyright Canonical, Ltd
*/

/* Chemical agents lend themselves to covert use in sabotage against
* which it is exceedingly difficult to visualize any really effective
* defense... I will not dwell upon this use of CBW because, as one
* pursues the possibilities of such covert uses, one discovers that the
* scenarios resemble that in which the components of a nuclear weapon
* are smuggled into New York City and assembled in the basement of the
* Empire State Building.
* In other words, once the possibility is recognized to exist, about
* all that one can do is worry about it.
* -- Dr. Ivan L Bennett, Jr., testifying before the Subcommittee on
* National Security Policy and Scientific Developments, November 20,
* 1969.
*/
#include "shim.h"

static EFI_SYSTEM_TABLE *systab;
Expand Down Expand Up @@ -48,28 +36,182 @@ unhook_system_services(void)
BS = systab->BootServices;
}

/* Reviewers: Is this better than a bunch of double pointers? */
typedef struct {
EFI_HANDLE hnd;
EFI_DEVICE_PATH *dp;
void *buffer;
size_t size;
} buffer_properties_t;

static EFI_STATUS
try_load_from_sfs(EFI_DEVICE_PATH *dp, buffer_properties_t *bprop)
{
EFI_STATUS status = EFI_SUCCESS;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *sfs = NULL;
EFI_FILE_HANDLE root = NULL;
EFI_FILE_HANDLE file = NULL;

bprop->buffer = NULL;

/* look for a handle with SFS support from the input DP */
bprop->dp = dp;
status = BS->LocateDevicePath(&EFI_SIMPLE_FILE_SYSTEM_GUID, &bprop->dp, &bprop->hnd);
if (EFI_ERROR(status)) {
goto out;
}

/* make sure the remaining DP portion is really a file path */
if (DevicePathType(bprop->dp) != MEDIA_DEVICE_PATH ||
DevicePathSubType(bprop->dp) != MEDIA_FILEPATH_DP) {
status = EFI_LOAD_ERROR;
goto out;
}

/* find protocol, open the root directory, then open file */
status = BS->HandleProtocol(bprop->hnd, &EFI_SIMPLE_FILE_SYSTEM_GUID, (void **)&sfs);
if (EFI_ERROR(status))
goto out;
status = sfs->OpenVolume(sfs, &root);
if (EFI_ERROR(status))
goto out;
status = root->Open(root, &file, ((FILEPATH_DEVICE_PATH *) bprop->dp)->PathName, EFI_FILE_MODE_READ, 0);
if (EFI_ERROR(status))
goto out;

/* get file size */
status = file->SetPosition(file, -1ULL);
if (EFI_ERROR(status))
goto out;
status = file->GetPosition(file, &bprop->size);
if (EFI_ERROR(status))
goto out;
status = file->SetPosition(file, 0);
if (EFI_ERROR(status))
goto out;

/* allocate buffer */
bprop->buffer = AllocatePool(bprop->size);
if (bprop->buffer == NULL) {
status = EFI_OUT_OF_RESOURCES;
goto out;
}

/* read file */
status = file->Read(file, &bprop->size, bprop->buffer);

out:
if (EFI_ERROR(status) && bprop->buffer)
FreePool(bprop->buffer);
if (file)
file->Close(file);
if (root)
root->Close(root);
return status;
}


static EFI_STATUS
try_load_from_lf2(EFI_DEVICE_PATH *dp, buffer_properties_t *bprop)
{
EFI_STATUS status = EFI_SUCCESS;
EFI_LOAD_FILE2_PROTOCOL *lf2 = NULL;

bprop->buffer = NULL;

/* look for a handle with LF2 support from the input DP */
bprop->dp = dp;
status = BS->LocateDevicePath(&EFI_LOAD_FILE2_GUID, &bprop->dp, &bprop->hnd);
if (EFI_ERROR(status))
goto out;

/* find protocol */
status = BS->LocateProtocol(bprop->hnd, &EFI_LOAD_FILE2_GUID, (void **) &lf2);
if (EFI_ERROR(status))
goto out;

/* get file size */
bprop->size = 0; /* this shouldn't be read when Buffer=NULL but better be safe */
status = lf2->LoadFile(lf2, bprop->dp, /*BootPolicy=*/false, &bprop->size, NULL);
/* NOTE: the spec is somewhat ambiguous what is the correct return status code
when asking for the buffer size with Buffer=NULL. I am assuming EFI_SUCCESS
and EFI_BUFFER_TOO_SMALL are the only reasonable interpretations. */
if (EFI_ERROR(status) && status != EFI_BUFFER_TOO_SMALL) {
status = EFI_LOAD_ERROR;
goto out;
}

/* allocate buffer */
bprop->buffer = AllocatePool(bprop->size);
if (!bprop->buffer) {
status = EFI_OUT_OF_RESOURCES;
goto out;
}

/* read file */
status = lf2->LoadFile(lf2, bprop->dp, /*BootPolicy=*/false, &bprop->size, bprop->buffer);
if (EFI_ERROR(status))
goto out;

out:
if (EFI_ERROR(status) && bprop->buffer)
FreePool(bprop->buffer);
return status;
}

static EFI_STATUS EFIAPI
shim_load_image(BOOLEAN BootPolicy, EFI_HANDLE ParentImageHandle,
EFI_DEVICE_PATH *FilePath, VOID *SourceBuffer,
UINTN SourceSize, EFI_HANDLE *ImageHandle)
{
SHIM_LOADED_IMAGE *image;
EFI_STATUS efi_status;
buffer_properties_t bprop;

(void)FilePath;

if (BootPolicy || !SourceBuffer || !SourceSize)
if (BootPolicy)
return EFI_UNSUPPORTED;

if (!SourceBuffer || !SourceSize) {
if (try_load_from_sfs(FilePath, &bprop) == EFI_SUCCESS)
;
else if (try_load_from_lf2(FilePath, &bprop) == EFI_SUCCESS)
;
else
/* no buffer given and we cannot load from this device */
return EFI_LOAD_ERROR;

SourceBuffer = bprop.buffer;
SourceSize = bprop.size;
} else {
bprop.buffer = NULL;
/* even if we are using a buffer, try populating the
* device_handle and file_path fields the best we can */
bprop.dp = FilePath;
efi_status = BS->LocateDevicePath(&EFI_DEVICE_PATH_GUID,
&bprop.dp,
&bprop.hnd);
if (efi_status != EFI_SUCCESS) {
/* can't seem to pull apart this DP */
bprop.dp = FilePath;
bprop.hnd = NULL;
}

}

image = AllocatePool(sizeof(*image));
if (!image)
return EFI_OUT_OF_RESOURCES;
if (!image) {
efi_status = EFI_OUT_OF_RESOURCES;
goto free_buffer;
}

SetMem(image, sizeof(*image), 0);

image->li.Revision = 0x1000;
image->li.ParentHandle = ParentImageHandle;
image->li.SystemTable = systab;
image->li.DeviceHandle = bprop.hnd;
image->li.FilePath = DuplicateDevicePath(bprop.dp);
image->loaded_image_device_path = DuplicateDevicePath(FilePath);

efi_status = handle_image(SourceBuffer, SourceSize, &image->li,
&image->entry_point, &image->alloc_address,
Expand All @@ -81,16 +223,24 @@ shim_load_image(BOOLEAN BootPolicy, EFI_HANDLE ParentImageHandle,
efi_status = BS->InstallMultipleProtocolInterfaces(ImageHandle,
&SHIM_LOADED_IMAGE_GUID, image,
&EFI_LOADED_IMAGE_GUID, &image->li,
&EFI_LOADED_IMAGE_DEVICE_PATH_GUID,
image->loaded_image_device_path,
NULL);
if (EFI_ERROR(efi_status))
goto free_alloc;

if (bprop.buffer)
FreePool(bprop.buffer);

return EFI_SUCCESS;

free_alloc:
BS->FreePages(image->alloc_address, image->alloc_pages);
free_image:
FreePool(image);
free_buffer:
if (bprop.buffer)
FreePool(bprop.buffer);
return efi_status;
}

Expand Down Expand Up @@ -133,9 +283,13 @@ shim_start_image(IN EFI_HANDLE ImageHandle, OUT UINTN *ExitDataSize,
BS->UninstallMultipleProtocolInterfaces(ImageHandle,
&EFI_LOADED_IMAGE_GUID, image,
&SHIM_LOADED_IMAGE_GUID, &image->li,
&EFI_LOADED_IMAGE_DEVICE_PATH_GUID,
image->loaded_image_device_path,
NULL);

BS->FreePages(image->alloc_address, image->alloc_pages);
BS->FreePool(image->li.FilePath);
BS->FreePool(image->loaded_image_device_path);
FreePool(image);

return efi_status;
Expand Down
2 changes: 2 additions & 0 deletions shim.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@
#include "include/ucs2.h"
#include "include/variables.h"
#include "include/hexdump.h"
#include "include/lf2.h"

#include "version.h"

Expand Down Expand Up @@ -339,6 +340,7 @@ typedef struct {
UINTN exit_data_size;
jmp_buf longjmp_buf;
BOOLEAN started;
EFI_DEVICE_PATH *loaded_image_device_path;
} SHIM_LOADED_IMAGE;

#endif /* SHIM_H_ */

0 comments on commit b997ca7

Please sign in to comment.