aboutsummaryrefslogtreecommitdiffstats
path: root/roms/u-boot/lib/efi_loader/efi_load_initrd.c
diff options
context:
space:
mode:
authorAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
committerAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
commitaf1a266670d040d2f4083ff309d732d648afba2a (patch)
tree2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/u-boot/lib/efi_loader/efi_load_initrd.c
parente02cda008591317b1625707ff8e115a4841aa889 (diff)
Add submodule dependency filesHEADmaster
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/u-boot/lib/efi_loader/efi_load_initrd.c')
-rw-r--r--roms/u-boot/lib/efi_loader/efi_load_initrd.c236
1 files changed, 236 insertions, 0 deletions
diff --git a/roms/u-boot/lib/efi_loader/efi_load_initrd.c b/roms/u-boot/lib/efi_loader/efi_load_initrd.c
new file mode 100644
index 000000000..e2a806302
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_load_initrd.c
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2020, Linaro Limited
+ */
+
+#define LOG_CATEGORY LOGC_EFI
+#include <common.h>
+#include <efi_loader.h>
+#include <efi_load_initrd.h>
+#include <efi_variable.h>
+#include <fs.h>
+#include <malloc.h>
+#include <mapmem.h>
+
+static efi_status_t EFIAPI
+efi_load_file2_initrd(struct efi_load_file_protocol *this,
+ struct efi_device_path *file_path, bool boot_policy,
+ efi_uintn_t *buffer_size, void *buffer);
+
+static const struct efi_load_file_protocol efi_lf2_protocol = {
+ .load_file = efi_load_file2_initrd,
+};
+
+/*
+ * Device path defined by Linux to identify the handle providing the
+ * EFI_LOAD_FILE2_PROTOCOL used for loading the initial ramdisk.
+ */
+static const struct efi_initrd_dp dp_lf2_handle = {
+ .vendor = {
+ {
+ DEVICE_PATH_TYPE_MEDIA_DEVICE,
+ DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
+ sizeof(dp_lf2_handle.vendor),
+ },
+ EFI_INITRD_MEDIA_GUID,
+ },
+ .end = {
+ DEVICE_PATH_TYPE_END,
+ DEVICE_PATH_SUB_TYPE_END,
+ sizeof(dp_lf2_handle.end),
+ }
+};
+
+static efi_handle_t efi_initrd_handle;
+
+/**
+ * get_initrd_fp() - Get initrd device path from a FilePathList device path
+ *
+ * @initrd_fp: the final initrd filepath
+ *
+ * Return: status code. Caller must free initrd_fp
+ */
+static efi_status_t get_initrd_fp(struct efi_device_path **initrd_fp)
+{
+ const efi_guid_t lf2_initrd_guid = EFI_INITRD_MEDIA_GUID;
+ struct efi_device_path *dp = NULL;
+
+ /*
+ * if bootmgr is setup with and initrd, the device path will be
+ * in the FilePathList[] of our load options in Boot####.
+ * The first device path of the multi instance device path will
+ * start with a VenMedia and the initrds will follow.
+ *
+ * If the device path is not found return EFI_INVALID_PARAMETER.
+ * We can then use this specific return value and not install the
+ * protocol, while allowing the boot to continue
+ */
+ dp = efi_get_dp_from_boot(lf2_initrd_guid);
+ if (!dp)
+ return EFI_INVALID_PARAMETER;
+
+ *initrd_fp = dp;
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_load_file2_initrd() - load initial RAM disk
+ *
+ * This function implements the LoadFile service of the EFI_LOAD_FILE2_PROTOCOL
+ * in order to load an initial RAM disk requested by the Linux kernel stub.
+ *
+ * See the UEFI spec for details.
+ *
+ * @this: EFI_LOAD_FILE2_PROTOCOL instance
+ * @file_path: media device path of the file, "" in this case
+ * @boot_policy: must be false
+ * @buffer_size: size of allocated buffer
+ * @buffer: buffer to load the file
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI
+efi_load_file2_initrd(struct efi_load_file_protocol *this,
+ struct efi_device_path *file_path, bool boot_policy,
+ efi_uintn_t *buffer_size, void *buffer)
+{
+ struct efi_device_path *initrd_fp = NULL;
+ efi_status_t ret = EFI_NOT_FOUND;
+ struct efi_file_handle *f = NULL;
+ efi_uintn_t bs;
+
+ EFI_ENTRY("%p, %p, %d, %p, %p", this, file_path, boot_policy,
+ buffer_size, buffer);
+
+ if (!this || this != &efi_lf2_protocol ||
+ !buffer_size) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ if (file_path->type != dp_lf2_handle.end.type ||
+ file_path->sub_type != dp_lf2_handle.end.sub_type) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ if (boot_policy) {
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+
+ ret = get_initrd_fp(&initrd_fp);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /* Open file */
+ f = efi_file_from_path(initrd_fp);
+ if (!f) {
+ log_err("Can't find initrd specified in Boot####\n");
+ ret = EFI_NOT_FOUND;
+ goto out;
+ }
+
+ /* Get file size */
+ ret = efi_file_size(f, &bs);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ if (!buffer || *buffer_size < bs) {
+ ret = EFI_BUFFER_TOO_SMALL;
+ *buffer_size = bs;
+ } else {
+ ret = EFI_CALL(f->read(f, &bs, (void *)(uintptr_t)buffer));
+ *buffer_size = bs;
+ }
+
+out:
+ efi_free_pool(initrd_fp);
+ if (f)
+ EFI_CALL(f->close(f));
+ return EFI_EXIT(ret);
+}
+
+/**
+ * check_initrd() - Determine if the file defined as an initrd in Boot####
+ * load_options device path is present
+ *
+ * Return: status code
+ */
+static efi_status_t check_initrd(void)
+{
+ struct efi_device_path *initrd_fp = NULL;
+ struct efi_file_handle *f;
+ efi_status_t ret;
+
+ ret = get_initrd_fp(&initrd_fp);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /*
+ * If the file is not found, but the file path is set, return an error
+ * and trigger the bootmgr fallback
+ */
+ f = efi_file_from_path(initrd_fp);
+ if (!f) {
+ log_err("Can't find initrd specified in Boot####\n");
+ ret = EFI_NOT_FOUND;
+ goto out;
+ }
+
+ EFI_CALL(f->close(f));
+
+out:
+ efi_free_pool(initrd_fp);
+ return ret;
+}
+
+/**
+ * efi_initrd_register() - create handle for loading initial RAM disk
+ *
+ * This function creates a new handle and installs a Linux specific vendor
+ * device path and an EFI_LOAD_FILE2_PROTOCOL. Linux uses the device path
+ * to identify the handle and then calls the LoadFile service of the
+ * EFI_LOAD_FILE2_PROTOCOL to read the initial RAM disk.
+ *
+ * Return: status code
+ */
+efi_status_t efi_initrd_register(void)
+{
+ efi_status_t ret;
+
+ /*
+ * Allow the user to continue if Boot#### file path is not set for
+ * an initrd
+ */
+ ret = check_initrd();
+ if (ret == EFI_INVALID_PARAMETER)
+ return EFI_SUCCESS;
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ ret = EFI_CALL(efi_install_multiple_protocol_interfaces
+ (&efi_initrd_handle,
+ /* initramfs */
+ &efi_guid_device_path, &dp_lf2_handle,
+ /* LOAD_FILE2 */
+ &efi_guid_load_file2_protocol,
+ (void *)&efi_lf2_protocol,
+ NULL));
+
+ return ret;
+}
+
+/**
+ * efi_initrd_deregister() - delete the handle for loading initial RAM disk
+ *
+ * This will delete the handle containing the Linux specific vendor device
+ * path and EFI_LOAD_FILE2_PROTOCOL for loading an initrd
+ *
+ * Return: status code
+ */
+void efi_initrd_deregister(void)
+{
+ efi_delete_handle(efi_initrd_handle);
+ efi_initrd_handle = NULL;
+}