]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
stub: load previous initrd that is already configured, too
authorLennart Poettering <lennart@amutable.com>
Fri, 20 Mar 2026 20:48:12 +0000 (21:48 +0100)
committerLennart Poettering <lennart@amutable.com>
Tue, 21 Apr 2026 06:30:26 +0000 (08:30 +0200)
This changes the initrd combination logic to also include any initrd
already configured via the "LINUX_INITRD_MEDIA_GUID" device in the
initrd we pass to the linux kernel.

Or in other words: with this systemd-stub starts operating purely
incremental: it will extend any previously installed initrd with its own
stuff, so that both the previous initrd(s) and systemd-stub's are in
effect.

src/boot/initrd.c
src/boot/initrd.h
src/boot/stub.c

index b81334ea06a30db629d58bd5f0a49e07ca7a3d2b..4780715e7929da1c84ff4dfef40b745e2ace26f7 100644 (file)
@@ -171,3 +171,47 @@ EFI_STATUS initrd_unregister(EFI_HANDLE initrd_handle) {
         free(loader);
         return EFI_SUCCESS;
 }
+
+EFI_STATUS initrd_read_previous(struct iovec *ret_initrd) {
+        EFI_STATUS err;
+
+        /* If there's already an initrd registered, read it out, so that we can incorporate it in ours */
+
+        assert(ret_initrd);
+
+        /* Get from the device path to the handle */
+        EFI_DEVICE_PATH *dp = (EFI_DEVICE_PATH *) &efi_initrd_device_path;
+        EFI_HANDLE handle;
+        err = BS->LocateDevicePath(MAKE_GUID_PTR(EFI_LOAD_FILE2_PROTOCOL), &dp, &handle);
+        if (err != EFI_SUCCESS)
+                return err;
+
+        /* Get from the handle to the protocol */
+        EFI_LOAD_FILE2_PROTOCOL *protocol = NULL;
+        err = BS->HandleProtocol(handle, MAKE_GUID_PTR(EFI_LOAD_FILE2_PROTOCOL), (void**) &protocol);
+        if (err != EFI_SUCCESS)
+                return err;
+
+        size_t size = 0;
+        err = protocol->LoadFile(protocol, dp, /* bootPolicy= */ false, &size, /* Buffer= */ NULL);
+        if (err == EFI_SUCCESS) /* Success? Kinda unexpected given we set Buffer to NULL, but it probably
+                                 * means, that the file is zero-sized, let's treat it as such. */
+                size = 0;
+        else if (err != EFI_BUFFER_TOO_SMALL)
+                return err;
+
+        if (size == 0)
+                return EFI_NOT_FOUND; /* Treat empty initrds like missing ones */
+
+        _cleanup_free_ void *data = xmalloc(size);
+        err = protocol->LoadFile(protocol, dp, /* bootPolicy= */ false, &size, data);
+        if (err != EFI_SUCCESS)
+                return err;
+
+        *ret_initrd = (struct iovec) {
+                .iov_base = TAKE_PTR(data),
+                .iov_len = size,
+        };
+
+        return EFI_SUCCESS;
+}
index 50987c497d66768a60c2a923628749af18af65c0..d34f8ef4c4ff7db8935dad4217b6376d377baa0e 100644 (file)
@@ -13,3 +13,5 @@ static inline void cleanup_initrd(EFI_HANDLE *initrd_handle) {
         (void) initrd_unregister(*initrd_handle);
         *initrd_handle = NULL;
 }
+
+EFI_STATUS initrd_read_previous(struct iovec *ret_initrd);
index e2a8569cb33d73c6f2ef7c2a0fa414c9ef1f0552..664ee7cb851d541a683d39683c5ef499b5cadd08 100644 (file)
@@ -10,6 +10,7 @@
 #include "efi-string.h"
 #include "export-vars.h"
 #include "graphics.h"
+#include "initrd.h"
 #include "iovec-util-fundamental.h"
 #include "linux.h"
 #include "measure.h"
 
 /* The list of initrds we combine into one, in the order we want to merge them */
 enum {
-        /* The first two are part of the PE binary */
-        INITRD_UCODE,
-        INITRD_BASE,
-
-        /* The rest are dynamically generated, and hence in dynamic memory */
-        _INITRD_DYNAMIC_FIRST,
-        INITRD_CREDENTIAL = _INITRD_DYNAMIC_FIRST,
+        INITRD_UCODE,    /* Part of the PE binary */
+        INITRD_PREVIOUS, /* initrd already configured via the EFI protocol before we were invoked */
+        INITRD_BASE,     /* Part of the PE binary */
+        INITRD_CREDENTIAL,
         INITRD_GLOBAL_CREDENTIAL,
         INITRD_SYSEXT,
         INITRD_GLOBAL_SYSEXT,
@@ -52,6 +50,8 @@ enum {
         _INITRD_MAX,
 };
 
+#define INITRD_IS_STATIC(idx) IN_SET(idx, INITRD_UCODE, INITRD_BASE)
+
 /* magic string to find in the binary image */
 DECLARE_NOALLOC_SECTION(".sdmagic", "#### LoaderInfo: systemd-stub " GIT_VERSION " ####");
 
@@ -550,6 +550,21 @@ static void extend_initrds(
                 iovec_array_extend(all_initrds, n_all_initrds, *i);
 }
 
+static void acquire_previous_initrd(struct iovec initrds[static _INITRD_MAX]) {
+        EFI_STATUS err;
+
+        /* NB: the assumption here is that any previously installed initrd are measured by whatever
+         * registered them, and we just pass them on here. */
+
+        err = initrd_read_previous(initrds + INITRD_PREVIOUS);
+        if (err == EFI_NOT_FOUND)
+                log_debug_status(err, "No previous initrd registered.");
+        else if (err != EFI_SUCCESS)
+                log_warning_status(err, "Failed to read previously registered initrd, ignoring.");
+        else
+                log_debug("Successfully loaded previously registered initrd (%zu bytes).", initrds[INITRD_PREVIOUS].iov_len);
+}
+
 static EFI_STATUS load_addons(
                 EFI_HANDLE stub_image,
                 EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
@@ -821,10 +836,11 @@ static void cmdline_append_and_measure_smbios(char16_t **cmdline, int *parameter
 static void initrds_free(struct iovec (*initrds)[_INITRD_MAX]) {
         assert(initrds);
 
-        /* Free the dynamic initrds, but leave the non-dynamic ones around */
+        /* Free the non-static initrds, but leave the static (i.e. PE embedded) ones around */
 
-        for (size_t i = _INITRD_DYNAMIC_FIRST; i < _INITRD_MAX; i++)
-                iovec_done((*initrds) + i);
+        for (size_t i = 0; i < _INITRD_MAX; i++)
+                if (!INITRD_IS_STATIC(i))
+                        iovec_done((*initrds) + i);
 }
 
 static void generate_sidecar_initrds(
@@ -1309,6 +1325,7 @@ static EFI_STATUS run(EFI_HANDLE image) {
         install_addon_devicetrees(&dt_state, dt_addons, n_dt_addons, &parameters_measured);
 
         /* Generate & find all initrds */
+        acquire_previous_initrd(initrds);
         generate_sidecar_initrds(loaded_image, initrds, &parameters_measured, &sysext_measured, &confext_measured);
         generate_embedded_initrds(loaded_image, sections, initrds);
         generate_boot_secret_initrd(boot_secret, initrds);
@@ -1319,9 +1336,10 @@ static EFI_STATUS run(EFI_HANDLE image) {
          * We want addons to take precedence over the base initrds, so the order is:
          * 1. Ucode addons
          * 2. UKI ucode
-         * 3. UKI initrd
-         * 4. Generated initrds
-         * 5. initrd addons */
+         * 3. Previous initrds
+         * 4. UKI initrd
+         * 5. Generated initrds
+         * 6. initrd addons */
         measure_and_append_ucode_addons(&all_initrds, &n_all_initrds, ucode_addons, n_ucode_addons, &parameters_measured);
         extend_initrds(initrds, &all_initrds, &n_all_initrds);
         measure_and_append_initrd_addons(&all_initrds, &n_all_initrds, initrd_addons, n_initrd_addons, &parameters_measured);