On 5/1/25 03:15, Adriano Cordova wrote:
Current support for initrd in EFI booting has two flaws:

1. Installs a NULL initrd via LoadFile2 protocol if `efi_binary_run_dp` does
    not provide an initrd. In this case, a LoadFile2 should not be installed
    at all.
2. Initrd is not properly uninstalled if booting fails at some point between
    initrd installation and the call to `do_bootefi_exec`. This affects both
    booting via `efi_bootbin` and via `efi_bootmgr`.

This commit splits the process of setting an initrd into two steps:

First, `efi_initrd_set_from_mem` creates a memory-backed device path for the
initrd. (This step is not necessary when using `efi_bootmgr`, and for now is
only used when booting FIT images, but the `bootefi` command could now be
expanded to receive an initrd argument.)

Second, `efi_initrd_install` creates an initrd handle and installs the
LoadFile2 protocol exposing the initrd. This is needed both when booting with
`efi_bootbin` and with `efi_bootmgr`, so it is called in `do_bootefi_exec`,
which is the first common point between the two bootflows.

Also, as `do_bootefi_exec` is the only place where `efi_initrd_install` is
called, the cleanup function `efi_initrd_uninstall` can be called before
exiting `do_bootefi_exec` instead of using the event
`efi_guid_event_group_return_to_efibootmgr`. (This also makes more sense as
now initrd is also used by `efi_bootbin`.)

Fixes: 36835a9105c ("efi_loader: binary_run: register an initrd")
Reported-by: Weizhao Ouyang <o451686...@gmail.com>
Reported-by: Heinrich Schuchardt <xypron.g...@gmx.de>
Signed-off-by: Adriano Cordova <adriano.cord...@canonical.com>
---
  include/efi_loader.h             |  8 ++-
  lib/efi_loader/efi_bootbin.c     | 14 +++---
  lib/efi_loader/efi_bootmgr.c     |  7 ---
  lib/efi_loader/efi_helper.c      | 11 +++++
  lib/efi_loader/efi_load_initrd.c | 84 +++++++++++++++++---------------
  5 files changed, 68 insertions(+), 56 deletions(-)

diff --git a/include/efi_loader.h b/include/efi_loader.h
index 144b749278a..0a3ca799287 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -597,6 +597,12 @@ efi_status_t efi_env_set_load_options(efi_handle_t handle, 
const char *env_var,
  void *efi_get_configuration_table(const efi_guid_t *guid);
  /* Install device tree */
  efi_status_t efi_install_fdt(void *fdt);
+/* Install initrd LoadFile2 protocol */
+efi_status_t efi_initrd_install(void);
+/* Uninstall initrd LoadFile2 protocol */
+efi_status_t efi_initrd_uninstall(void);
+/* Set an initrd */
+efi_status_t efi_initrd_set_from_mem(void *initrd, size_t initd_sz);
  /* Execute loaded UEFI image */
  efi_status_t do_bootefi_exec(efi_handle_t handle, void *load_options);
  /* Run loaded UEFI image with given fdt */
@@ -667,8 +673,6 @@ 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(struct efi_device_path *dp_initrd);
-efi_status_t efi_initrd_deregister(void);
  /* Called by bootefi to make SMBIOS tables available */
  /**
   * efi_acpi_register() - write out ACPI tables
diff --git a/lib/efi_loader/efi_bootbin.c b/lib/efi_loader/efi_bootbin.c
index d0f7da309ce..637f2c0d195 100644
--- a/lib/efi_loader/efi_bootbin.c
+++ b/lib/efi_loader/efi_bootbin.c
@@ -220,7 +220,6 @@ static efi_status_t efi_binary_run_dp(void *image, size_t 
size, void *fdt,
                                      struct efi_device_path *dp_img)
  {
        efi_status_t ret;
-       struct efi_device_path *dp_initrd;
/* Initialize EFI drivers */
        ret = efi_init_obj_list();
@@ -234,13 +233,12 @@ static efi_status_t efi_binary_run_dp(void *image, size_t 
size, void *fdt,
        if (ret != EFI_SUCCESS)
                return ret;
- dp_initrd = efi_dp_from_mem(EFI_LOADER_DATA, (uintptr_t)initrd, initd_sz);
-       if (!dp_initrd)
-               return EFI_OUT_OF_RESOURCES;
-
-       ret = efi_initrd_register(dp_initrd);
-       if (ret != EFI_SUCCESS)
-               return ret;
+       /* Set initrd if provided */
+       if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD)) {
+               ret = efi_initrd_set_from_mem(initrd, initd_sz);
+               if (ret != EFI_SUCCESS)
+                       return ret;
+       }
return efi_run_image(image, size, dp_dev, dp_img);
  }
diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
index c0df5cb9acd..19c7e16a25b 100644
--- a/lib/efi_loader/efi_bootmgr.c
+++ b/lib/efi_loader/efi_bootmgr.c
@@ -683,13 +683,6 @@ static efi_status_t try_load_entry(u16 n, efi_handle_t 
*handle,
        if (ret != EFI_SUCCESS)
                goto error;
- /* try to register load file2 for initrd's */
-       if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD)) {
-               ret = efi_initrd_register(NULL);
-               if (ret != EFI_SUCCESS)
-                       goto error;
-       }
-
        log_info("Booting: Label: %ls Device path: %pD\n", lo.label, 
lo.file_path);
/* Ignore the optional data in auto-generated boot options */
diff --git a/lib/efi_loader/efi_helper.c b/lib/efi_loader/efi_helper.c
index 3936139ca41..b4c207ea33f 100644
--- a/lib/efi_loader/efi_helper.c
+++ b/lib/efi_loader/efi_helper.c
@@ -658,6 +658,13 @@ efi_status_t do_bootefi_exec(efi_handle_t handle, void 
*load_options)
                goto out;
        }
+ /* Create initrd handle and install LoadFile2 protocol */
+       if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD)) {
+               ret = efi_initrd_install();
+               if (ret != EFI_SUCCESS)
+                       goto out;
+       }
+
        /* Call our payload! */
        ret = EFI_CALL(efi_start_image(handle, &exit_data_size, &exit_data));
        if (ret != EFI_SUCCESS) {
@@ -683,6 +690,10 @@ out:
                }
        }
+ /* Destroy initrd handle with LoadFile2 protocol */
+       if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD))
+               ret = efi_initrd_uninstall();
+
        /* Control is returned to U-Boot, disable EFI watchdog */
        efi_set_watchdog(0);
diff --git a/lib/efi_loader/efi_load_initrd.c b/lib/efi_loader/efi_load_initrd.c
index b5d58943a80..7210bf16d09 100644
--- a/lib/efi_loader/efi_load_initrd.c
+++ b/lib/efi_loader/efi_load_initrd.c
@@ -74,7 +74,40 @@ static efi_status_t get_initrd_fp(struct efi_device_path 
**initrd_fp)
  }
/**
- * efi_initrd_from_mem() - load initial RAM disk from memory
+ * efi_initrd_set_from_mem() - set initial RAM disk from memory
+ *
+ * Set efi_initrd_dp to a memory mapped device path pointing
+ * to @initrd. If @initrd is NULL then efi_initrd_dp gets cleared.
+ *
+ *
+ * @initrd:    address of initrd or NULL if none is provided
+ * @initrd_sz: size of initrd
+ * Return:     status code
+ */
+efi_status_t efi_initrd_set_from_mem(void *initrd, size_t initd_sz)
+{
+       struct efi_device_path *old_initrd_dp;
+
+       old_initrd_dp = efi_initrd_dp;

In efi_initrd_uninstall() we free the device-path and set efi_initrd_dp = NULL.
It seems you are handling the case of efi_initrd_install() failing here.

To me this looks a bit convoluted. Calling efi_initrd_register() as in your v3 series would allow handling errors when they occur.

The static variable efi_initrd_dp should receive some documentation in the code explaining how it is used. This could be in a separate patch.

I will merge your v3 series as it fixes the case of no initrd.

We can handle the uninstallation of the LoadFile2 protocol in a patch on top of that.

Best regards

Heinrich

+
+       if (!initrd) {
+               efi_initrd_dp = NULL;
+               goto out;
+       }
+
+       efi_initrd_dp = efi_dp_from_mem(EFI_LOADER_DATA, (uintptr_t)initrd, 
initd_sz);
+       if (!efi_initrd_dp) {
+               efi_initrd_dp = old_initrd_dp;
+               return EFI_OUT_OF_RESOURCES;
+       }
+
+out:
+       efi_free_pool(old_initrd_dp);
+       return EFI_SUCCESS;
+}
+
+/**
+ * efi_initrd_get_from_mem() - get initial RAM disk from memory
   *
   * This function copies the initrd from the memory mapped device
   * path pointed to by efi_initrd_dp
@@ -84,7 +117,7 @@ static efi_status_t get_initrd_fp(struct efi_device_path 
**initrd_fp)
   *
   * Return:                    status code
   */
-static efi_status_t efi_initrd_from_mem(efi_uintn_t *buffer_size, void *buffer)
+static efi_status_t efi_initrd_get_from_mem(efi_uintn_t *buffer_size, void 
*buffer)
  {
        efi_status_t ret = EFI_NOT_FOUND;
        efi_uintn_t bs;
@@ -155,7 +188,7 @@ efi_load_file2_initrd(struct efi_load_file_protocol *this,
        }
if (efi_initrd_dp)
-               return EFI_EXIT(efi_initrd_from_mem(buffer_size, buffer));
+               return EFI_EXIT(efi_initrd_get_from_mem(buffer_size, buffer));
ret = get_initrd_fp(&initrd_fp);
        if (ret != EFI_SUCCESS)
@@ -224,14 +257,14 @@ out:
  }
/**
- * efi_initrd_deregister() - delete the handle for loading initial RAM disk
+ * efi_initrd_uninstall() - 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
+ * path and the installed EFI_LOAD_FILE2_PROTOCOL for loading an initrd
   *
   * Return:    status code
   */
-efi_status_t efi_initrd_deregister(void)
+efi_status_t efi_initrd_uninstall(void)
  {
        efi_status_t ret;
@@ -255,42 +288,22 @@ efi_status_t efi_initrd_deregister(void)
  }
/**
- * efi_initrd_return_notify() - return to efibootmgr callback
- *
- * @event:     the event for which this notification function is registered
- * @context:   event context
- */
-static void EFIAPI efi_initrd_return_notify(struct efi_event *event,
-                                                 void *context)
-{
-       efi_status_t ret;
-
-       EFI_ENTRY("%p, %p", event, context);
-       ret = efi_initrd_deregister();
-       EFI_EXIT(ret);
-}
-
-/**
- * efi_initrd_register() - create handle for loading initial RAM disk
+ * efi_initrd_install() - 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. If dp_initrd is
- * not provided, the initrd will be taken from the BootCurrent variable
- *
- * @dp_initrd: optional device path containing an initrd
+ * EFI_LOAD_FILE2_PROTOCOL to read the initial RAM disk. If efi_initrd_dp
+ * is not set the initrd will be taken from the BootCurrent variable. If
+ * an initrd is not found the handle is not created.
   *
   * Return:    status code
   */
-efi_status_t efi_initrd_register(struct efi_device_path *dp_initrd)
+efi_status_t efi_initrd_install(void)
  {
        efi_status_t ret;
-       struct efi_event *event;
- if (dp_initrd) {
-               efi_initrd_dp = dp_initrd;
-       } else {
+       if (!efi_initrd_dp) {
                /*
                * Allow the user to continue if Boot#### file path is not set 
for
                * an initrd
@@ -314,12 +327,5 @@ efi_status_t efi_initrd_register(struct efi_device_path 
*dp_initrd)
                return ret;
        }
- ret = efi_create_event(EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
-                              efi_initrd_return_notify, NULL,
-                              &efi_guid_event_group_return_to_efibootmgr,
-                              &event);
-       if (ret != EFI_SUCCESS)
-               log_err("Creating event failed\n");
-
        return ret;
  }

Reply via email to