File 2002-systemd-stub-addons-load-generic-addons-via-EFI-prot.patch of Package systemd
From 6685f5de70af793d4e24c85712ba1effcf2ae0a4 Mon Sep 17 00:00:00 2001
From: Raito Bezarius <masterancpp@gmail.com>
Date: Sun, 18 Jun 2023 04:26:05 +0200
Subject: [PATCH 2002/2007] systemd-stub(addons): load generic addons via EFI
protocol
- reads from EFI protocol
- make the loading phase agnostic from the "read source"
- restructure the addon entry to be generic and not only cmdline for
future initrd loading, etc.
- add a feature called `OFFER_ADDONS` on sd-boot to signal support for
this EFI protocol
---
src/boot/boot.c | 1 +
src/boot/stub.c | 128 +++++++++++++++++++++-----
src/fundamental/efivars-fundamental.h | 1 +
3 files changed, 106 insertions(+), 24 deletions(-)
diff --git a/src/boot/boot.c b/src/boot/boot.c
index ea6b8b150e..509e0d9b84 100644
--- a/src/boot/boot.c
+++ b/src/boot/boot.c
@@ -2789,6 +2789,7 @@ static void export_loader_variables(
EFI_LOADER_FEATURE_RETAIN_SHIM |
EFI_LOADER_FEATURE_MENU_DISABLE |
EFI_LOADER_FEATURE_MULTI_PROFILE_UKI |
+ EFI_LOADER_FEATURE_OFFER_ADDONS |
0;
assert(loaded_image);
diff --git a/src/boot/stub.c b/src/boot/stub.c
index cf990df2e7..cf3ba95669 100644
--- a/src/boot/stub.c
+++ b/src/boot/stub.c
@@ -49,6 +49,18 @@ DECLARE_NOALLOC_SECTION(".sdmagic", "#### LoaderInfo: systemd-stub " GIT_VERSION
DECLARE_SBAT(SBAT_STUB_SECTION_TEXT);
+struct addon_entry {
+ char16_t *source_path;
+ const EFI_DEVICE_PATH *device_path;
+};
+
+static int cmp_addon_entry(const struct addon_entry *s1, const struct addon_entry *s2) {
+ assert(s1);
+ assert(s2);
+
+ return strcmp16((const char16_t*)&s1->source_path, (const char16_t*)&s2->source_path);
+}
+
static char16_t* pe_section_to_str16(
EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
const PeSectionVector *section) {
@@ -288,20 +300,23 @@ nothing:
}
static EFI_STATUS load_addons_from_dir(
+ EFI_LOADED_IMAGE_PROTOCOL *current_image,
EFI_FILE *root,
const char16_t *prefix,
- char16_t ***items,
+ struct addon_entry **addons,
size_t *n_items,
size_t *n_allocated) {
_cleanup_(file_closep) EFI_FILE *extra_dir = NULL;
_cleanup_free_ EFI_FILE_INFO *dirent = NULL;
+ char16_t *addon_spath = NULL;
+ EFI_DEVICE_PATH *addon_path = NULL;
size_t dirent_size = 0;
EFI_STATUS err;
assert(root);
assert(prefix);
- assert(items);
+ assert(addons);
assert(n_items);
assert(n_allocated);
@@ -336,16 +351,73 @@ static EFI_STATUS load_addons_from_dir(
if (*n_items + 2 > *n_allocated) {
/* We allocate 16 entries at a time, as a matter of optimization */
- if (*n_items > (SIZE_MAX / sizeof(uint16_t)) - 16) /* Overflow check, just in case */
+ if (*n_items > (SIZE_MAX / sizeof(struct addon_entry)) - 16) /* Overflow check, just in case */
+ return log_oom();
+
+ size_t m = *n_items + 16;
+ *addons = xrealloc(*addons, *n_allocated + sizeof(struct addon_entry), m *
+ sizeof(struct addon_entry));
+ *n_allocated = m;
+ }
+
+ addon_spath = xasprintf("%ls\\%ls", prefix, d);
+ err = make_file_device_path(current_image->DeviceHandle, addon_spath, &addon_path);
+ if (err != EFI_SUCCESS)
+ return log_error_status(err, "Error making device path for %ls: %m", addon_spath);
+
+ (*addons)[*n_items] = (struct addon_entry) {
+ .source_path = TAKE_PTR(d),
+ .device_path = TAKE_PTR(addon_path)
+ };
+ *n_items = *n_items + 1;
+ }
+
+ return EFI_SUCCESS;
+}
+
+static EFI_STATUS load_addons_from_efi(
+ EFI_LOADED_IMAGE_PROTOCOL *image,
+ struct addon_entry **addons,
+ size_t *n_items,
+ size_t *n_allocated) {
+
+ EFI_STATUS err;
+ EFI_DEVICE_PATH **addon_paths = NULL;
+
+ err = BS->HandleProtocol(image->DeviceHandle, MAKE_GUID_PTR(SYSTEMD_ADDON_MEDIA), (void **) &addon_paths);
+
+ if (err == EFI_UNSUPPORTED)
+ /* No addons from EFI, that's OK */
+ return EFI_SUCCESS;
+ if (err != EFI_SUCCESS)
+ return log_error_status(err, "Failed to load addons from EFI protocol: %m");
+
+ while (*addon_paths) {
+ char16_t *spath = NULL;
+ /* If we increment this pointer instead of addon_paths_split, we will arrive on a end node
+ * marker */
+ const EFI_DEVICE_PATH *addon_dpath = *addon_paths;
+ err = device_path_to_str(addon_dpath, &spath);
+ if (err != EFI_SUCCESS)
+ return err;
+
+ if (*n_items + 2 > *n_allocated) {
+ /* We allocate 16 entries at a time, as a matter of optimization */
+ if (*n_items > (SIZE_MAX / sizeof(struct addon_entry)) - 16) /* Overflow check, just in case */
return log_oom();
size_t m = *n_items + 16;
- *items = xrealloc(*items, *n_allocated * sizeof(uint16_t *), m * sizeof(uint16_t *));
+ *addons = xrealloc(*addons, *n_allocated + sizeof(struct addon_entry), m *
+ sizeof(struct addon_entry));
*n_allocated = m;
}
- (*items)[(*n_items)++] = TAKE_PTR(d);
- (*items)[*n_items] = NULL; /* Let's always NUL terminate, to make freeing via strv_free() easy */
+ (*addons)[*n_items] = (struct addon_entry) {
+ .source_path = TAKE_PTR(spath),
+ .device_path = TAKE_PTR(addon_dpath)
+ };
+ *n_items = *n_items + 1;
+ addon_paths++;
}
return EFI_SUCCESS;
@@ -534,6 +606,15 @@ static void extend_initrds(
iovec_array_extend(all_initrds, n_all_initrds, *i);
}
+static void items_free(char16_t **items, size_t n_items) {
+ assert(items || n_items == 0);
+
+ for (size_t i = 0; i < n_items; ++i)
+ free(items[i]);
+
+ free(items);
+}
+
static EFI_STATUS load_addons(
EFI_HANDLE stub_image,
EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
@@ -547,9 +628,10 @@ static EFI_STATUS load_addons(
NamedAddon **ucode_addons, /* Ditto */
size_t *n_ucode_addons) {
- _cleanup_(strv_freep) char16_t **items = NULL;
_cleanup_(file_closep) EFI_FILE *root = NULL;
+ _cleanup_free_ struct addon_entry *addons = NULL;
size_t n_items = 0, n_allocated = 0;
+ char16_t **items = NULL;
EFI_STATUS err;
assert(stub_image);
@@ -559,6 +641,8 @@ static EFI_STATUS load_addons(
if (!loaded_image->DeviceHandle)
return EFI_SUCCESS;
+ CLEANUP_ARRAY(items, n_items, items_free);
+
err = open_volume(loaded_image->DeviceHandle, &root);
if (err == EFI_UNSUPPORTED)
/* Error will be unsupported if the bootloader doesn't implement the file system protocol on
@@ -567,7 +651,11 @@ static EFI_STATUS load_addons(
if (err != EFI_SUCCESS)
return log_error_status(err, "Unable to open root directory: %m");
- err = load_addons_from_dir(root, prefix, &items, &n_items, &n_allocated);
+ err = load_addons_from_dir(loaded_image, root, prefix, &addons, &n_items, &n_allocated);
+ if (err != EFI_SUCCESS)
+ return err;
+
+ err = load_addons_from_efi(loaded_image, &addons, &n_items, &n_allocated);
if (err != EFI_SUCCESS)
return err;
@@ -576,28 +664,19 @@ static EFI_STATUS load_addons(
/* Now, sort the files we found, to make this uniform and stable (and to ensure the TPM measurements
* are not dependent on read order) */
- sort_pointer_array((void**) items, n_items, (compare_pointer_func_t) strcmp16);
+ sort_pointer_array((void**) addons, n_items, (compare_pointer_func_t) cmp_addon_entry);
for (size_t i = 0; i < n_items; i++) {
PeSectionVector sections[ELEMENTSOF(unified_sections)] = {};
- _cleanup_free_ EFI_DEVICE_PATH *addon_path = NULL;
_cleanup_(unload_imagep) EFI_HANDLE addon = NULL;
EFI_LOADED_IMAGE_PROTOCOL *loaded_addon = NULL;
- _cleanup_free_ char16_t *addon_spath = NULL;
-
- addon_spath = xasprintf("%ls\\%ls", prefix, items[i]);
- err = make_file_device_path(loaded_image->DeviceHandle, addon_spath, &addon_path);
- if (err != EFI_SUCCESS)
- return log_error_status(err, "Error making device path for %ls: %m", addon_spath);
/* By using shim_load_image, we cover both the case where the PE files are signed with MoK
* and with DB, and running with or without shim. */
- err = shim_load_image(stub_image, addon_path, &addon);
+ err = shim_load_image(stub_image, addons[i].device_path, &addon);
if (err != EFI_SUCCESS) {
log_error_status(err,
- "Failed to read '%ls' from '%ls', ignoring: %m",
- items[i],
- addon_spath);
+ "Failed to read '%ls', ignoring: %m", addons[i].source_path);
continue;
}
@@ -605,7 +684,8 @@ static EFI_STATUS load_addons(
MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL),
(void **) &loaded_addon);
if (err != EFI_SUCCESS)
- return log_error_status(err, "Failed to find protocol in %ls: %m", items[i]);
+ return log_error_status(err, "Failed to find protocol in %ls: %m",
+ addons[i].source_path);
err = pe_memory_locate_sections(loaded_addon->ImageBase, unified_sections, sections);
if (err != EFI_SUCCESS ||
@@ -618,13 +698,13 @@ static EFI_STATUS load_addons(
err = EFI_NOT_FOUND;
log_error_status(err,
"Unable to locate embedded .cmdline/.dtb/.dtbauto/.initrd/.ucode sections in %ls, ignoring: %m",
- items[i]);
+ addons[i].source_path);
continue;
}
/* We want to enforce that addons are not UKIs, i.e.: they must not embed a kernel. */
if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_LINUX)) {
- log_error("%ls is a UKI, not an addon, ignoring.", items[i]);
+ log_error("%ls is a UKI, not an addon, ignoring.", addons[i].source_path);
continue;
}
@@ -634,7 +714,7 @@ static EFI_STATUS load_addons(
!strneq8(uname,
(const char *)loaded_addon->ImageBase + sections[UNIFIED_SECTION_UNAME].memory_offset,
sections[UNIFIED_SECTION_UNAME].memory_size)) {
- log_error(".uname mismatch between %ls and UKI, ignoring", items[i]);
+ log_error(".uname mismatch between %ls and UKI, ignoring", addons[i].source_path);
continue;
}
diff --git a/src/fundamental/efivars-fundamental.h b/src/fundamental/efivars-fundamental.h
index f002e81b53..01bdfee68e 100644
--- a/src/fundamental/efivars-fundamental.h
+++ b/src/fundamental/efivars-fundamental.h
@@ -24,6 +24,7 @@
#define EFI_LOADER_FEATURE_RETAIN_SHIM (UINT64_C(1) << 12)
#define EFI_LOADER_FEATURE_MENU_DISABLE (UINT64_C(1) << 13)
#define EFI_LOADER_FEATURE_MULTI_PROFILE_UKI (UINT64_C(1) << 14)
+#define EFI_LOADER_FEATURE_OFFER_ADDONS (UINT64_C(1) << 15)
/* Features of the stub, i.e. systemd-stub */
#define EFI_STUB_FEATURE_REPORT_BOOT_PARTITION (UINT64_C(1) << 0)
--
2.49.0