]> git.ipfire.org Git - thirdparty/u-boot.git/commitdiff
efi_loader: efi_load_initrd: provide a memory mapped initrd
authorAdriano Cordova <adrianox@gmail.com>
Wed, 19 Mar 2025 14:44:59 +0000 (11:44 -0300)
committerHeinrich Schuchardt <heinrich.schuchardt@canonical.com>
Fri, 11 Apr 2025 11:20:37 +0000 (13:20 +0200)
U-Boot can pass an initrd to subsequent boot stages via the
EFI_LOAD_FILE2_PROTOCOL. The current implementation only supports
this functionality via the efi boot manager: the initrd is taken
from the load options of the BootCurrent variable. This commit adds
support for registering a memory mapped initrd, e.g. loaded from a
FIT image. For now this new method takes precedence over loading the
initrd from the BootCurrent variable (if both are present) because
the BootCurrent variable is not cleared on exiting the boot manager.

Signed-off-by: Adriano Cordova <adriano.cordova@canonical.com>
Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
include/efi_loader.h
lib/efi_loader/efi_bootmgr.c
lib/efi_loader/efi_load_initrd.c

index 5f7697867866c5635c2c5433f5bdf1455de206e9..72bee60abaf9169d3df2beccf8aba77f2a6a18a6 100644 (file)
@@ -667,7 +667,7 @@ efi_status_t efi_http_register(const efi_handle_t handle,
                               struct efi_service_binding_protocol *http_service_binding);
 /* Called by bootefi to make the watchdog available */
 efi_status_t efi_watchdog_register(void);
-efi_status_t efi_initrd_register(void);
+efi_status_t efi_initrd_register(struct efi_device_path *dp_initrd);
 efi_status_t efi_initrd_deregister(void);
 /* Called by bootefi to make SMBIOS tables available */
 /**
index f9534ef85edb55782f572219db3e295ebb0cf647..2791afa02a9bae48d3dc27c3d94263a1f6059ab5 100644 (file)
@@ -685,7 +685,7 @@ static efi_status_t try_load_entry(u16 n, efi_handle_t *handle,
 
        /* try to register load file2 for initrd's */
        if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD)) {
-               ret = efi_initrd_register();
+               ret = efi_initrd_register(NULL);
                if (ret != EFI_SUCCESS)
                        goto error;
        }
index fb8cc7bcbe3c21fcfcc30ef19bd14d54625bda97..b5d58943a805a688eca7e9f8d9a251a5d74cdd08 100644 (file)
@@ -42,6 +42,7 @@ static const struct efi_lo_dp_prefix dp_lf2_handle = {
 };
 
 static efi_handle_t efi_initrd_handle;
+static struct efi_device_path *efi_initrd_dp;
 
 /**
  * get_initrd_fp() - Get initrd device path from a FilePathList device path
@@ -72,6 +73,41 @@ static efi_status_t get_initrd_fp(struct efi_device_path **initrd_fp)
        return EFI_SUCCESS;
 }
 
+/**
+ * efi_initrd_from_mem() - load initial RAM disk from memory
+ *
+ * This function copies the initrd from the memory mapped device
+ * path pointed to by efi_initrd_dp
+ *
+ * @buffer_size:               size of allocated buffer
+ * @buffer:                    buffer to load the file
+ *
+ * Return:                     status code
+ */
+static efi_status_t efi_initrd_from_mem(efi_uintn_t *buffer_size, void *buffer)
+{
+       efi_status_t ret = EFI_NOT_FOUND;
+       efi_uintn_t bs;
+       struct efi_device_path_memory *mdp;
+
+       mdp = (struct efi_device_path_memory *)efi_initrd_dp;
+       if (!mdp)
+               return ret;
+
+       bs = mdp->end_address - mdp->start_address;
+
+       if (!buffer || *buffer_size < bs) {
+               ret = EFI_BUFFER_TOO_SMALL;
+               *buffer_size = bs;
+       } else {
+               memcpy(buffer, (void *)(uintptr_t)mdp->start_address, bs);
+               *buffer_size = bs;
+               ret = EFI_SUCCESS;
+       }
+
+       return ret;
+}
+
 /**
  * efi_load_file2_initrd() - load initial RAM disk
  *
@@ -118,6 +154,9 @@ efi_load_file2_initrd(struct efi_load_file_protocol *this,
                goto out;
        }
 
+       if (efi_initrd_dp)
+               return EFI_EXIT(efi_initrd_from_mem(buffer_size, buffer));
+
        ret = get_initrd_fp(&initrd_fp);
        if (ret != EFI_SUCCESS)
                goto out;
@@ -209,6 +248,9 @@ efi_status_t efi_initrd_deregister(void)
                                                         NULL);
        efi_initrd_handle = NULL;
 
+       efi_free_pool(efi_initrd_dp);
+       efi_initrd_dp = NULL;
+
        return ret;
 }
 
@@ -234,24 +276,31 @@ static void EFIAPI efi_initrd_return_notify(struct efi_event *event,
  * 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.
+ * EFI_LOAD_FILE2_PROTOCOL to read the initial RAM disk. If dp_initrd is
+ * not provided, the initrd will be taken from the BootCurrent variable
+ *
+ * @dp_initrd: optional device path containing an initrd
  *
  * Return:     status code
  */
-efi_status_t efi_initrd_register(void)
+efi_status_t efi_initrd_register(struct efi_device_path *dp_initrd)
 {
        efi_status_t ret;
        struct efi_event *event;
 
-       /*
-        * 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;
+       if (dp_initrd) {
+               efi_initrd_dp = dp_initrd;
+       } else {
+               /*
+               * 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_install_multiple_protocol_interfaces(&efi_initrd_handle,
                                                       /* initramfs */