stub: load previous initrd that is already configured, too

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.
This commit is contained in:
Lennart Poettering
2026-04-21 08:30:26 +02:00
parent c363bb1869
commit 426134b0e7
3 changed files with 77 additions and 13 deletions
+44
View 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;
}
+2
View 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);
+31 -13
View 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"
@@ -32,13 +33,10 @@
/* 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);