summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMate Kukri <mate.kukri@canonical.com>2024-04-18 10:58:30 +0100
committerPeter Jones <pjones@redhat.com>2025-02-11 10:43:37 -0500
commit2bff46034aeefe4b266b6d6dd7d6cd771c1bf4de (patch)
treede9ec99cc7275db0d0a15f0a99e6745915838a5b
parente43aea89f1fe8aaa996cb7e5b0612b09ce812a45 (diff)
downloadefi-boot-shim-2bff46034aeefe4b266b6d6dd7d6cd771c1bf4de.tar.gz
efi-boot-shim-2bff46034aeefe4b266b6d6dd7d6cd771c1bf4de.zip
loader-proto: Add support for loading files from disk to LoadImage()
Currently the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL and EFI_LOAD_FILE2_PROTOCOL are supported. Signed-off-by: Mate Kukri <mate.kukri@canonical.com>
-rw-r--r--loader-proto.c205
-rw-r--r--shim.h1
2 files changed, 188 insertions, 18 deletions
diff --git a/loader-proto.c b/loader-proto.c
index f0df122c..dcfa1a68 100644
--- a/loader-proto.c
+++ b/loader-proto.c
@@ -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;
@@ -48,6 +36,133 @@ unhook_system_services(void)
BS = systab->BootServices;
}
+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;
+ UINT64 tmpsz = 0;
+
+ 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, &tmpsz);
+ if (EFI_ERROR(status))
+ goto out;
+ bprop->size = (size_t)tmpsz;
+ 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(&gEfiLoadFile2ProtocolGuid, &bprop->dp, &bprop->hnd);
+ if (EFI_ERROR(status))
+ goto out;
+
+ /* find protocol */
+ status = BS->HandleProtocol(bprop->hnd, &gEfiLoadFile2ProtocolGuid, (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,
@@ -55,21 +170,59 @@ shim_load_image(BOOLEAN BootPolicy, EFI_HANDLE ParentImageHandle,
{
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(&gEfiDevicePathProtocolGuid,
+ &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);
+ if (!image->li.FilePath ||
+ !image->loaded_image_device_path) {
+ efi_status = EFI_OUT_OF_RESOURCES;
+ goto free_image;
+ }
efi_status = handle_image(SourceBuffer, SourceSize, &image->li,
&image->entry_point, &image->alloc_address,
@@ -81,16 +234,28 @@ shim_load_image(BOOLEAN BootPolicy, EFI_HANDLE ParentImageHandle,
efi_status = BS->InstallMultipleProtocolInterfaces(ImageHandle,
&SHIM_LOADED_IMAGE_GUID, image,
&EFI_LOADED_IMAGE_GUID, &image->li,
+ &gEfiLoadedImageDevicePathProtocolGuid,
+ 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:
+ if (image->loaded_image_device_path)
+ FreePool(image->loaded_image_device_path);
+ if (image->li.FilePath)
+ FreePool(image->li.FilePath);
FreePool(image);
+free_buffer:
+ if (bprop.buffer)
+ FreePool(bprop.buffer);
return efi_status;
}
@@ -133,9 +298,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,
+ &gEfiLoadedImageDevicePathProtocolGuid,
+ 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;
diff --git a/shim.h b/shim.h
index a3f8a505..09d23780 100644
--- a/shim.h
+++ b/shim.h
@@ -341,6 +341,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_ */