diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:49:52 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:49:52 +0000 |
commit | 55944e5e40b1be2afc4855d8d2baf4b73d1876b5 (patch) | |
tree | 33f869f55a1b149e9b7c2b7e201867ca5dd52992 /src/boot/efi/initrd.c | |
parent | Initial commit. (diff) | |
download | systemd-55944e5e40b1be2afc4855d8d2baf4b73d1876b5.tar.xz systemd-55944e5e40b1be2afc4855d8d2baf4b73d1876b5.zip |
Adding upstream version 255.4.upstream/255.4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/boot/efi/initrd.c')
-rw-r--r-- | src/boot/efi/initrd.c | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/src/boot/efi/initrd.c b/src/boot/efi/initrd.c new file mode 100644 index 0000000..527b05f --- /dev/null +++ b/src/boot/efi/initrd.c @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "initrd.h" +#include "macro-fundamental.h" +#include "proto/device-path.h" +#include "proto/load-file.h" +#include "util.h" + +#define LINUX_INITRD_MEDIA_GUID \ + GUID_DEF(0x5568e427, 0x68fc, 0x4f3d, 0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68) + +/* extend LoadFileProtocol */ +struct initrd_loader { + EFI_LOAD_FILE_PROTOCOL load_file; + const void *address; + size_t length; +}; + +/* static structure for LINUX_INITRD_MEDIA device path + see https://github.com/torvalds/linux/blob/v5.13/drivers/firmware/efi/libstub/efi-stub-helper.c + */ +static const struct { + VENDOR_DEVICE_PATH vendor; + EFI_DEVICE_PATH end; +} _packed_ efi_initrd_device_path = { + .vendor = { + .Header = { + .Type = MEDIA_DEVICE_PATH, + .SubType = MEDIA_VENDOR_DP, + .Length = sizeof(efi_initrd_device_path.vendor), + }, + .Guid = LINUX_INITRD_MEDIA_GUID + }, + .end = { + .Type = END_DEVICE_PATH_TYPE, + .SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE, + .Length = sizeof(efi_initrd_device_path.end), + } +}; + +static EFIAPI EFI_STATUS initrd_load_file( + EFI_LOAD_FILE_PROTOCOL *this, + EFI_DEVICE_PATH *file_path, + bool boot_policy, + size_t *buffer_size, + void *buffer) { + + struct initrd_loader *loader; + + if (!this || !buffer_size || !file_path) + return EFI_INVALID_PARAMETER; + if (boot_policy) + return EFI_UNSUPPORTED; + + loader = (struct initrd_loader *) this; + + if (loader->length == 0 || !loader->address) + return EFI_NOT_FOUND; + + if (!buffer || *buffer_size < loader->length) { + *buffer_size = loader->length; + return EFI_BUFFER_TOO_SMALL; + } + + memcpy(buffer, loader->address, loader->length); + *buffer_size = loader->length; + return EFI_SUCCESS; +} + +EFI_STATUS initrd_register( + const void *initrd_address, + size_t initrd_length, + EFI_HANDLE *ret_initrd_handle) { + + EFI_STATUS err; + EFI_DEVICE_PATH *dp = (EFI_DEVICE_PATH *) &efi_initrd_device_path; + EFI_HANDLE handle; + struct initrd_loader *loader; + + assert(ret_initrd_handle); + + if (!initrd_address || initrd_length == 0) + return EFI_SUCCESS; + + /* check if a LINUX_INITRD_MEDIA_GUID DevicePath is already registered. + LocateDevicePath checks for the "closest DevicePath" and returns its handle, + where as InstallMultipleProtocolInterfaces only matches identical DevicePaths. + */ + err = BS->LocateDevicePath(MAKE_GUID_PTR(EFI_LOAD_FILE2_PROTOCOL), &dp, &handle); + if (err != EFI_NOT_FOUND) /* InitrdMedia is already registered */ + return EFI_ALREADY_STARTED; + + loader = xnew(struct initrd_loader, 1); + *loader = (struct initrd_loader) { + .load_file.LoadFile = initrd_load_file, + .address = initrd_address, + .length = initrd_length + }; + + /* create a new handle and register the LoadFile2 protocol with the InitrdMediaPath on it */ + err = BS->InstallMultipleProtocolInterfaces( + ret_initrd_handle, MAKE_GUID_PTR(EFI_DEVICE_PATH_PROTOCOL), + &efi_initrd_device_path, MAKE_GUID_PTR(EFI_LOAD_FILE2_PROTOCOL), + loader, + NULL); + if (err != EFI_SUCCESS) + free(loader); + + return err; +} + +EFI_STATUS initrd_unregister(EFI_HANDLE initrd_handle) { + EFI_STATUS err; + struct initrd_loader *loader; + + if (!initrd_handle) + return EFI_SUCCESS; + + /* get the LoadFile2 protocol that we allocated earlier */ + err = BS->HandleProtocol(initrd_handle, MAKE_GUID_PTR(EFI_LOAD_FILE2_PROTOCOL), (void **) &loader); + if (err != EFI_SUCCESS) + return err; + + /* uninstall all protocols thus destroying the handle */ + err = BS->UninstallMultipleProtocolInterfaces( + initrd_handle, MAKE_GUID_PTR(EFI_DEVICE_PATH_PROTOCOL), + &efi_initrd_device_path, MAKE_GUID_PTR(EFI_LOAD_FILE2_PROTOCOL), + loader, + NULL); + if (err != EFI_SUCCESS) + return err; + + initrd_handle = NULL; + free(loader); + return EFI_SUCCESS; +} |