Skip to content

Commit

Permalink
systemd-boot: load addons from type 1 and expose them via EFI protocol
Browse files Browse the repository at this point in the history
This make it possible to attach addons to any Type systemd#1 entry
and get them back in the next bootable stage via an EFI protocol.
  • Loading branch information
RaitoBezarius authored and keentux committed Apr 16, 2024
1 parent 8b9ad17 commit 48ffc70
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 3 deletions.
48 changes: 48 additions & 0 deletions src/boot/efi/addon-util.c
@@ -0,0 +1,48 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */

#include "addon-util.h"
#include "proto/device-path.h"
#include "util.h"
#include "log.h"

EFI_STATUS addons_install_proto(EFI_LOADED_IMAGE_PROTOCOL *stub_image, char16_t * const *addons) {
EFI_STATUS err;
EFI_DEVICE_PATH **dps;

assert(stub_image);

err = make_multiple_file_device_path(stub_image->DeviceHandle, addons, &dps);
if (err != EFI_SUCCESS || dps == NULL)
return err;

return BS->InstallMultipleProtocolInterfaces(&stub_image->DeviceHandle,
MAKE_GUID_PTR(SYSTEMD_ADDON_MEDIA),
dps, NULL);
}

EFI_STATUS addons_unload_proto(EFI_HANDLE *addons)
{
EFI_STATUS err;
EFI_DEVICE_PATH *dps;

assert(addons);

if (!*addons)
return EFI_SUCCESS;

/* get the EFI_DEVICE_PATH* interface that we allocated earlier */
err = BS->HandleProtocol(*addons, MAKE_GUID_PTR(SYSTEMD_ADDON_MEDIA),
(void **) &dps);
if (err != EFI_SUCCESS)
return err;

err = BS->UninstallMultipleProtocolInterfaces(*addons,
MAKE_GUID_PTR(SYSTEMD_ADDON_MEDIA),
&dps, NULL);

if (err != EFI_SUCCESS)
return err;

*addons = NULL;
return EFI_SUCCESS;
}
12 changes: 12 additions & 0 deletions src/boot/efi/addon-util.h
@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once

#include "proto/loaded-image.h"
#include "device-path-util.h"
#include "util.h"

#define SYSTEMD_ADDON_MEDIA_GUID \
GUID_DEF(0x97ac68bf, 0xc741, 0x4bbb, 0xb7, 0xbf, 0x7f, 0x6c, 0xcc, 0x00, 0x8a, 0x7e)

EFI_STATUS addons_install_proto(EFI_LOADED_IMAGE_PROTOCOL *loaded_image, char16_t * const *addons);
EFI_STATUS addons_unload_proto(EFI_HANDLE *addons);
31 changes: 28 additions & 3 deletions src/boot/efi/boot.c
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */

#include "addon-util.h"
#include "bcd.h"
#include "bootspec-fundamental.h"
#include "console.h"
Expand Down Expand Up @@ -63,6 +64,7 @@ typedef struct {
char16_t *options;
bool options_implied; /* If true, these options are implied if we invoke the PE binary without any parameters (as in: UKI). If false we must specify these options explicitly. */
char16_t **initrd;
char16_t **addons; /* systemd-addons for this entry */
char16_t key;
EFI_STATUS (*call)(void);
int tries_done;
Expand Down Expand Up @@ -599,6 +601,8 @@ static void print_status(Config *config, char16_t *loaded_image_path) {
printf(" loader: %ls\n", entry->loader);
STRV_FOREACH(initrd, entry->initrd)
printf(" initrd: %ls\n", *initrd);
STRV_FOREACH(addon, entry->addons)
printf(" addon: %ls\n", *addon);
if (entry->devicetree)
printf(" devicetree: %ls\n", entry->devicetree);
if (entry->options)
Expand Down Expand Up @@ -1181,6 +1185,7 @@ static BootEntry* boot_entry_free(BootEntry *entry) {
free(entry->devicetree);
free(entry->options);
strv_free(entry->initrd);
strv_free(entry->addons);
free(entry->path);
free(entry->current_name);
free(entry->next_name);
Expand Down Expand Up @@ -1417,7 +1422,7 @@ static void boot_entry_add_type1(

_cleanup_(boot_entry_freep) BootEntry *entry = NULL;
char *line;
size_t pos = 0, n_initrd = 0;
size_t pos = 0, n_initrd = 0, n_addons = 0;
char *key, *value;
EFI_STATUS err;

Expand Down Expand Up @@ -1486,7 +1491,14 @@ static void boot_entry_add_type1(
(n_initrd + 2) * sizeof(uint16_t *));
entry->initrd[n_initrd++] = xstr8_to_path(value);
entry->initrd[n_initrd] = NULL;

} else if (streq8(key, "add-on")) {
entry->addons = xrealloc(
entry->addons,
n_addons == 0 ? 0 : (n_addons + 1) * sizeof(uint16_t *),
(n_addons + 2) * sizeof(uint16_t *));
entry->addons[n_addons++] = xstr8_to_path(value);
entry->addons[n_addons] = NULL;
continue;
} else if (streq8(key, "options")) {
_cleanup_free_ char16_t *new = NULL;

Expand Down Expand Up @@ -2317,6 +2329,13 @@ static EFI_STATUS initrd_prepare(
return EFI_SUCCESS;
}

static void cleanup_loaded_image(EFI_LOADED_IMAGE_PROTOCOL **loaded_image) {
assert(loaded_image);

(void) addons_unload_proto((EFI_HANDLE *)*loaded_image);
*loaded_image = NULL;
}

static EFI_STATUS image_start(
EFI_HANDLE parent_image,
const BootEntry *entry) {
Expand Down Expand Up @@ -2366,7 +2385,7 @@ static EFI_STATUS image_start(
if (err != EFI_SUCCESS)
return log_error_status(err, "Error registering initrd: %m");

EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
_cleanup_(cleanup_loaded_image) EFI_LOADED_IMAGE_PROTOCOL *loaded_image = NULL;
err = BS->HandleProtocol(image, MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL), (void **) &loaded_image);
if (err != EFI_SUCCESS)
return log_error_status(err, "Error getting LoadedImageProtocol handle: %m");
Expand All @@ -2392,6 +2411,12 @@ static EFI_STATUS image_start(
(void) tpm_log_load_options(options, NULL);
}

if (entry->addons) {
err = addons_install_proto(loaded_image, entry->addons);
if (err != EFI_SUCCESS)
return log_error_status(err, "Error installing addons protocol: %m");
}

efivar_set_time_usec(MAKE_GUID_PTR(LOADER), u"LoaderTimeExecUSec", 0);
err = BS->StartImage(image, NULL, NULL);
graphics_mode(false);
Expand Down
1 change: 1 addition & 0 deletions src/boot/efi/meson.build
Expand Up @@ -253,6 +253,7 @@ endif
############################################################

libefi_sources = files(
'addon-util.c',
'console.c',
'device-path-util.c',
'devicetree.c',
Expand Down

0 comments on commit 48ffc70

Please sign in to comment.