summaryrefslogtreecommitdiffstats
path: root/src/boot
diff options
context:
space:
mode:
Diffstat (limited to 'src/boot')
-rw-r--r--src/boot/efi/boot.c71
-rw-r--r--src/boot/efi/cpio.c23
-rw-r--r--src/boot/efi/cpio.h7
-rw-r--r--src/boot/efi/export-vars.c44
-rw-r--r--src/boot/efi/export-vars.h5
-rw-r--r--src/boot/efi/linux.c22
-rw-r--r--src/boot/efi/linux.h13
-rw-r--r--src/boot/efi/linux_x86.c40
-rw-r--r--src/boot/efi/measure.c72
-rw-r--r--src/boot/efi/measure.h19
-rw-r--r--src/boot/efi/meson.build1
-rw-r--r--src/boot/efi/pe.c172
-rw-r--r--src/boot/efi/pe.h18
-rw-r--r--src/boot/efi/stub.c929
-rw-r--r--src/boot/efi/util.c12
-rw-r--r--src/boot/efi/util.h2
16 files changed, 851 insertions, 599 deletions
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index 79de121f0d..9dbf96f169 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -7,6 +7,7 @@
#include "devicetree.h"
#include "drivers.h"
#include "efivars-fundamental.h"
+#include "export-vars.h"
#include "graphics.h"
#include "initrd.h"
#include "linux.h"
@@ -1855,23 +1856,24 @@ static void generate_boot_entry_titles(Config *config) {
}
static bool is_sd_boot(EFI_FILE *root_dir, const char16_t *loader_path) {
- EFI_STATUS err;
static const char * const sections[] = {
".sdmagic",
NULL
};
- size_t offset = 0, size = 0, read;
_cleanup_free_ char *content = NULL;
+ PeSectionVector vector = {};
+ EFI_STATUS err;
+ size_t read;
assert(root_dir);
assert(loader_path);
- err = pe_file_locate_sections(root_dir, loader_path, sections, &offset, &size);
- if (err != EFI_SUCCESS || size != sizeof(SD_MAGIC))
+ err = pe_file_locate_sections(root_dir, loader_path, sections, &vector);
+ if (err != EFI_SUCCESS || vector.size != sizeof(SD_MAGIC))
return false;
- err = file_read(root_dir, loader_path, offset, size, &content, &read);
- if (err != EFI_SUCCESS || size != read)
+ err = file_read(root_dir, loader_path, vector.file_offset, vector.size, &content, &read);
+ if (err != EFI_SUCCESS || vector.size != read)
return false;
return memcmp(content, SD_MAGIC, sizeof(SD_MAGIC)) == 0;
@@ -2104,7 +2106,7 @@ static void config_load_type2_entries(
_SECTION_MAX,
};
- static const char * const sections[_SECTION_MAX + 1] = {
+ static const char * const section_names[_SECTION_MAX + 1] = {
[SECTION_CMDLINE] = ".cmdline",
[SECTION_OSREL] = ".osrel",
NULL,
@@ -2114,8 +2116,9 @@ static void config_load_type2_entries(
*os_image_version = NULL, *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL;
const char16_t *good_name, *good_version, *good_sort_key;
_cleanup_free_ char *content = NULL;
- size_t offs[_SECTION_MAX] = {}, szs[_SECTION_MAX] = {}, pos = 0;
+ PeSectionVector sections[_SECTION_MAX] = {};
char *line, *key, *value;
+ size_t pos = 0;
err = readdir(linux_dir, &f, &f_size);
if (err != EFI_SUCCESS || !f)
@@ -2131,11 +2134,16 @@ static void config_load_type2_entries(
continue;
/* look for .osrel and .cmdline sections in the .efi binary */
- err = pe_file_locate_sections(linux_dir, f->FileName, sections, offs, szs);
- if (err != EFI_SUCCESS || szs[SECTION_OSREL] == 0)
+ err = pe_file_locate_sections(linux_dir, f->FileName, section_names, sections);
+ if (err != EFI_SUCCESS || !PE_SECTION_VECTOR_IS_SET(sections + SECTION_OSREL))
continue;
- err = file_read(linux_dir, f->FileName, offs[SECTION_OSREL], szs[SECTION_OSREL], &content, NULL);
+ err = file_read(linux_dir,
+ f->FileName,
+ sections[SECTION_OSREL].file_offset,
+ sections[SECTION_OSREL].size,
+ &content,
+ NULL);
if (err != EFI_SUCCESS)
continue;
@@ -2206,14 +2214,19 @@ static void config_load_type2_entries(
config_add_entry(config, entry);
boot_entry_parse_tries(entry, u"\\EFI\\Linux", f->FileName, u".efi");
- if (szs[SECTION_CMDLINE] == 0)
+ if (!PE_SECTION_VECTOR_IS_SET(sections + SECTION_CMDLINE))
continue;
content = mfree(content);
/* read the embedded cmdline file */
size_t cmdline_len;
- err = file_read(linux_dir, f->FileName, offs[SECTION_CMDLINE], szs[SECTION_CMDLINE], &content, &cmdline_len);
+ err = file_read(linux_dir,
+ f->FileName,
+ sections[SECTION_CMDLINE].file_offset,
+ sections[SECTION_CMDLINE].size,
+ &content,
+ &cmdline_len);
if (err == EFI_SUCCESS) {
entry->options = xstrn8_to_16(content, cmdline_len);
mangle_stub_cmdline(entry->options);
@@ -2526,9 +2539,8 @@ static EFI_STATUS secure_boot_discover_keys(Config *config, EFI_FILE *root_dir)
return EFI_SUCCESS;
}
-static void export_variables(
+static void export_loader_variables(
EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
- const char16_t *loaded_image_path,
uint64_t init_usec) {
static const uint64_t loader_features =
@@ -2548,28 +2560,11 @@ static void export_variables(
EFI_LOADER_FEATURE_MENU_DISABLE |
0;
- _cleanup_free_ char16_t *infostr = NULL, *typestr = NULL;
-
assert(loaded_image);
- efivar_set_time_usec(MAKE_GUID_PTR(LOADER), u"LoaderTimeInitUSec", init_usec);
- efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderInfo", u"systemd-boot " GIT_VERSION, 0);
-
- infostr = xasprintf("%ls %u.%02u", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
- efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareInfo", infostr, 0);
-
- typestr = xasprintf("UEFI %u.%02u", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
- efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareType", typestr, 0);
-
+ (void) efivar_set_time_usec(MAKE_GUID_PTR(LOADER), u"LoaderTimeInitUSec", init_usec);
+ (void) efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderInfo", u"systemd-boot " GIT_VERSION, 0);
(void) efivar_set_uint64_le(MAKE_GUID_PTR(LOADER), u"LoaderFeatures", loader_features, 0);
-
- /* the filesystem path to this image, to prevent adding ourselves to the menu */
- efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderImageIdentifier", loaded_image_path, 0);
-
- /* export the device path this image is started from */
- _cleanup_free_ char16_t *uuid = disk_get_part_uuid(loaded_image->DeviceHandle);
- if (uuid)
- efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderDevicePartUUID", uuid, 0);
}
static void config_load_all_entries(
@@ -2669,7 +2664,6 @@ static EFI_STATUS run(EFI_HANDLE image) {
EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
_cleanup_(file_closep) EFI_FILE *root_dir = NULL;
_cleanup_(config_free) Config config = {};
- _cleanup_free_ char16_t *loaded_image_path = NULL;
EFI_STATUS err;
uint64_t init_usec;
bool menu = false;
@@ -2684,9 +2678,8 @@ static EFI_STATUS run(EFI_HANDLE image) {
if (err != EFI_SUCCESS)
return log_error_status(err, "Error getting a LoadedImageProtocol handle: %m");
- (void) device_path_to_str(loaded_image->FilePath, &loaded_image_path);
-
- export_variables(loaded_image, loaded_image_path, init_usec);
+ export_common_variables(loaded_image);
+ export_loader_variables(loaded_image, init_usec);
err = discover_root_dir(loaded_image, &root_dir);
if (err != EFI_SUCCESS)
@@ -2694,6 +2687,8 @@ static EFI_STATUS run(EFI_HANDLE image) {
(void) load_drivers(image, loaded_image, root_dir);
+ _cleanup_free_ char16_t *loaded_image_path = NULL;
+ (void) device_path_to_str(loaded_image->FilePath, &loaded_image_path);
config_load_all_entries(&config, loaded_image, loaded_image_path, root_dir);
if (config.n_entries == 0)
diff --git a/src/boot/efi/cpio.c b/src/boot/efi/cpio.c
index bd1118a58a..ba439740f7 100644
--- a/src/boot/efi/cpio.c
+++ b/src/boot/efi/cpio.c
@@ -311,8 +311,7 @@ EFI_STATUS pack_cpio(
uint32_t access_mode,
uint32_t tpm_pcr,
const char16_t *tpm_description,
- void **ret_buffer,
- size_t *ret_buffer_size,
+ struct iovec *ret_buffer,
bool *ret_measured) {
_cleanup_(file_closep) EFI_FILE *root = NULL, *extra_dir = NULL;
@@ -327,7 +326,6 @@ EFI_STATUS pack_cpio(
assert(loaded_image);
assert(target_dir_prefix);
assert(ret_buffer);
- assert(ret_buffer_size);
if (!loaded_image->DeviceHandle)
goto nothing;
@@ -430,7 +428,7 @@ EFI_STATUS pack_cpio(
if (err != EFI_SUCCESS)
return log_error_status(err, "Failed to pack cpio trailer: %m");
- err = tpm_log_event(
+ err = tpm_log_ipl_event(
tpm_pcr, POINTER_TO_PHYSICAL_ADDRESS(buffer), buffer_size, tpm_description, ret_measured);
if (err != EFI_SUCCESS)
return log_error_status(
@@ -439,14 +437,11 @@ EFI_STATUS pack_cpio(
tpm_pcr,
tpm_description);
- *ret_buffer = TAKE_PTR(buffer);
- *ret_buffer_size = buffer_size;
-
+ *ret_buffer = IOVEC_MAKE(TAKE_PTR(buffer), buffer_size);
return EFI_SUCCESS;
nothing:
- *ret_buffer = NULL;
- *ret_buffer_size = 0;
+ *ret_buffer = (struct iovec) {};
if (ret_measured)
*ret_measured = false;
@@ -463,8 +458,7 @@ EFI_STATUS pack_cpio_literal(
uint32_t access_mode,
uint32_t tpm_pcr,
const char16_t *tpm_description,
- void **ret_buffer,
- size_t *ret_buffer_size,
+ struct iovec *ret_buffer,
bool *ret_measured) {
uint32_t inode = 1; /* inode counter, so that each item gets a new inode */
@@ -476,7 +470,6 @@ EFI_STATUS pack_cpio_literal(
assert(target_dir_prefix);
assert(target_filename);
assert(ret_buffer);
- assert(ret_buffer_size);
/* Generate the leading directory inodes right before adding the first files, to the
* archive. Otherwise the cpio archive cannot be unpacked, since the leading dirs won't exist. */
@@ -499,7 +492,7 @@ EFI_STATUS pack_cpio_literal(
if (err != EFI_SUCCESS)
return log_error_status(err, "Failed to pack cpio trailer: %m");
- err = tpm_log_event(
+ err = tpm_log_ipl_event(
tpm_pcr, POINTER_TO_PHYSICAL_ADDRESS(buffer), buffer_size, tpm_description, ret_measured);
if (err != EFI_SUCCESS)
return log_error_status(
@@ -508,8 +501,6 @@ EFI_STATUS pack_cpio_literal(
tpm_pcr,
tpm_description);
- *ret_buffer = TAKE_PTR(buffer);
- *ret_buffer_size = buffer_size;
-
+ *ret_buffer = IOVEC_MAKE(TAKE_PTR(buffer), buffer_size);
return EFI_SUCCESS;
}
diff --git a/src/boot/efi/cpio.h b/src/boot/efi/cpio.h
index 9d14fa15a2..660372c0b7 100644
--- a/src/boot/efi/cpio.h
+++ b/src/boot/efi/cpio.h
@@ -2,6 +2,7 @@
#pragma once
#include "efi.h"
+#include "iovec-util-fundamental.h"
#include "proto/loaded-image.h"
EFI_STATUS pack_cpio(
@@ -14,8 +15,7 @@ EFI_STATUS pack_cpio(
uint32_t access_mode,
uint32_t tpm_pcr,
const char16_t *tpm_description,
- void **ret_buffer,
- size_t *ret_buffer_size,
+ struct iovec *ret_buffer,
bool *ret_measured);
EFI_STATUS pack_cpio_literal(
@@ -27,6 +27,5 @@ EFI_STATUS pack_cpio_literal(
uint32_t access_mode,
uint32_t tpm_pcr,
const char16_t *tpm_description,
- void **ret_buffer,
- size_t *ret_buffer_size,
+ struct iovec *ret_buffer,
bool *ret_measured);
diff --git a/src/boot/efi/export-vars.c b/src/boot/efi/export-vars.c
new file mode 100644
index 0000000000..3926747bba
--- /dev/null
+++ b/src/boot/efi/export-vars.c
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "device-path-util.h"
+#include "export-vars.h"
+#include "part-discovery.h"
+#include "util.h"
+
+void export_common_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) {
+ assert(loaded_image);
+
+ /* Export the device path this image is started from, if it's not set yet */
+ if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderDevicePartUUID", NULL, NULL) != EFI_SUCCESS) {
+ _cleanup_free_ char16_t *uuid = disk_get_part_uuid(loaded_image->DeviceHandle);
+ if (uuid)
+ efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderDevicePartUUID", uuid, 0);
+ }
+
+ /* If LoaderImageIdentifier is not set, assume the image with this stub was loaded directly from the
+ * UEFI firmware without any boot loader, and hence set the LoaderImageIdentifier ourselves. Note
+ * that some boot chain loaders neither set LoaderImageIdentifier nor make FilePath available to us,
+ * in which case there's simple nothing to set for us. (The UEFI spec doesn't really say who's wrong
+ * here, i.e. whether FilePath may be NULL or not, hence handle this gracefully and check if FilePath
+ * is non-NULL explicitly.) */
+ if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderImageIdentifier", NULL, NULL) != EFI_SUCCESS &&
+ loaded_image->FilePath) {
+ _cleanup_free_ char16_t *s = NULL;
+ if (device_path_to_str(loaded_image->FilePath, &s) == EFI_SUCCESS)
+ efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderImageIdentifier", s, 0);
+ }
+
+ /* if LoaderFirmwareInfo is not set, let's set it */
+ if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareInfo", NULL, NULL) != EFI_SUCCESS) {
+ _cleanup_free_ char16_t *s = NULL;
+ s = xasprintf("%ls %u.%02u", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
+ efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareInfo", s, 0);
+ }
+
+ /* ditto for LoaderFirmwareType */
+ if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareType", NULL, NULL) != EFI_SUCCESS) {
+ _cleanup_free_ char16_t *s = NULL;
+ s = xasprintf("UEFI %u.%02u", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
+ efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareType", s, 0);
+ }
+}
diff --git a/src/boot/efi/export-vars.h b/src/boot/efi/export-vars.h
new file mode 100644
index 0000000000..a925d09827
--- /dev/null
+++ b/src/boot/efi/export-vars.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "proto/loaded-image.h"
+
+void export_common_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image);
diff --git a/src/boot/efi/linux.c b/src/boot/efi/linux.c
index 65bc176df7..706648b49d 100644
--- a/src/boot/efi/linux.c
+++ b/src/boot/efi/linux.c
@@ -93,19 +93,17 @@ static EFI_STATUS load_image(EFI_HANDLE parent, const void *source, size_t len,
EFI_STATUS linux_exec(
EFI_HANDLE parent,
const char16_t *cmdline,
- const void *linux_buffer,
- size_t linux_length,
- const void *initrd_buffer,
- size_t initrd_length) {
+ const struct iovec *kernel,
+ const struct iovec *initrd) {
uint32_t compat_address;
EFI_STATUS err;
assert(parent);
- assert(linux_buffer && linux_length > 0);
- assert(initrd_buffer || initrd_length == 0);
+ assert(iovec_is_set(kernel));
+ assert(iovec_is_valid(initrd));
- err = pe_kernel_info(linux_buffer, &compat_address);
+ err = pe_kernel_info(kernel->iov_base, &compat_address);
#if defined(__i386__) || defined(__x86_64__)
if (err == EFI_UNSUPPORTED)
/* Kernel is too old to support LINUX_INITRD_MEDIA_GUID, try the deprecated EFI handover
@@ -113,16 +111,14 @@ EFI_STATUS linux_exec(
return linux_exec_efi_handover(
parent,
cmdline,
- linux_buffer,
- linux_length,
- initrd_buffer,
- initrd_length);
+ kernel,
+ initrd);
#endif
if (err != EFI_SUCCESS)
return log_error_status(err, "Bad kernel image: %m");
_cleanup_(unload_imagep) EFI_HANDLE kernel_image = NULL;
- err = load_image(parent, linux_buffer, linux_length, &kernel_image);
+ err = load_image(parent, kernel->iov_base, kernel->iov_len, &kernel_image);
if (err != EFI_SUCCESS)
return log_error_status(err, "Error loading kernel image: %m");
@@ -138,7 +134,7 @@ EFI_STATUS linux_exec(
}
_cleanup_(cleanup_initrd) EFI_HANDLE initrd_handle = NULL;
- err = initrd_register(initrd_buffer, initrd_length, &initrd_handle);
+ err = initrd_register(initrd->iov_base, initrd->iov_len, &initrd_handle);
if (err != EFI_SUCCESS)
return log_error_status(err, "Error registering initrd: %m");
diff --git a/src/boot/efi/linux.h b/src/boot/efi/linux.h
index 46b5f4f4d7..1bee8907c4 100644
--- a/src/boot/efi/linux.h
+++ b/src/boot/efi/linux.h
@@ -2,18 +2,15 @@
#pragma once
#include "efi.h"
+#include "iovec-util-fundamental.h"
EFI_STATUS linux_exec(
EFI_HANDLE parent,
const char16_t *cmdline,
- const void *linux_buffer,
- size_t linux_length,
- const void *initrd_buffer,
- size_t initrd_length);
+ const struct iovec *kernel,
+ const struct iovec *initrd);
EFI_STATUS linux_exec_efi_handover(
EFI_HANDLE parent,
const char16_t *cmdline,
- const void *linux_buffer,
- size_t linux_length,
- const void *initrd_buffer,
- size_t initrd_length);
+ const struct iovec *kernel,
+ const struct iovec *initrd);
diff --git a/src/boot/efi/linux_x86.c b/src/boot/efi/linux_x86.c
index 757902daac..c456209831 100644
--- a/src/boot/efi/linux_x86.c
+++ b/src/boot/efi/linux_x86.c
@@ -123,19 +123,17 @@ static void linux_efi_handover(EFI_HANDLE parent, uintptr_t kernel, BootParams *
EFI_STATUS linux_exec_efi_handover(
EFI_HANDLE parent,
const char16_t *cmdline,
- const void *linux_buffer,
- size_t linux_length,
- const void *initrd_buffer,
- size_t initrd_length) {
+ const struct iovec *kernel,
+ const struct iovec *initrd) {
assert(parent);
- assert(linux_buffer);
- assert(initrd_buffer || initrd_length == 0);
+ assert(iovec_is_set(kernel));
+ assert(iovec_is_valid(initrd));
- if (linux_length < sizeof(BootParams))
+ if (kernel->iov_len < sizeof(BootParams))
return EFI_LOAD_ERROR;
- const BootParams *image_params = (const BootParams *) linux_buffer;
+ const BootParams *image_params = (const BootParams *) kernel->iov_base;
if (image_params->hdr.header != SETUP_MAGIC || image_params->hdr.boot_flag != BOOT_FLAG_MAGIC)
return log_error_status(EFI_UNSUPPORTED, "Unsupported kernel image.");
if (image_params->hdr.version < SETUP_VERSION_2_11)
@@ -155,22 +153,26 @@ EFI_STATUS linux_exec_efi_handover(
/* There is no way to pass the high bits of code32_start. Newer kernels seems to handle this
* just fine, but older kernels will fail even if they otherwise have above 4G boot support. */
_cleanup_pages_ Pages linux_relocated = {};
- if (POINTER_TO_PHYSICAL_ADDRESS(linux_buffer) + linux_length > UINT32_MAX) {
+ const void *linux_buffer;
+ if (POINTER_TO_PHYSICAL_ADDRESS(kernel->iov_base) + kernel->iov_len > UINT32_MAX) {
linux_relocated = xmalloc_pages(
- AllocateMaxAddress, EfiLoaderCode, EFI_SIZE_TO_PAGES(linux_length), UINT32_MAX);
+ AllocateMaxAddress, EfiLoaderCode, EFI_SIZE_TO_PAGES(kernel->iov_len), UINT32_MAX);
linux_buffer = memcpy(
- PHYSICAL_ADDRESS_TO_POINTER(linux_relocated.addr), linux_buffer, linux_length);
- }
+ PHYSICAL_ADDRESS_TO_POINTER(linux_relocated.addr), kernel->iov_base, kernel->iov_len);
+ } else
+ linux_buffer = kernel->iov_base;
_cleanup_pages_ Pages initrd_relocated = {};
- if (!can_4g && POINTER_TO_PHYSICAL_ADDRESS(initrd_buffer) + initrd_length > UINT32_MAX) {
+ const void *initrd_buffer;
+ if (!can_4g && POINTER_TO_PHYSICAL_ADDRESS(initrd->iov_base) + initrd->iov_len > UINT32_MAX) {
initrd_relocated = xmalloc_pages(
- AllocateMaxAddress, EfiLoaderData, EFI_SIZE_TO_PAGES(initrd_length), UINT32_MAX);
+ AllocateMaxAddress, EfiLoaderData, EFI_SIZE_TO_PAGES(initrd->iov_len), UINT32_MAX);
initrd_buffer = memcpy(
PHYSICAL_ADDRESS_TO_POINTER(initrd_relocated.addr),
- initrd_buffer,
- initrd_length);
- }
+ initrd->iov_base,
+ initrd->iov_len);
+ } else
+ initrd_buffer = initrd->iov_base;
_cleanup_pages_ Pages boot_params_page = xmalloc_pages(
can_4g ? AllocateAnyPages : AllocateMaxAddress,
@@ -215,8 +217,8 @@ EFI_STATUS linux_exec_efi_handover(
boot_params->hdr.ramdisk_image = (uintptr_t) initrd_buffer;
boot_params->ext_ramdisk_image = POINTER_TO_PHYSICAL_ADDRESS(initrd_buffer) >> 32;
- boot_params->hdr.ramdisk_size = initrd_length;
- boot_params->ext_ramdisk_size = ((uint64_t) initrd_length) >> 32;
+ boot_params->hdr.ramdisk_size = initrd->iov_len;
+ boot_params->ext_ramdisk_size = ((uint64_t) initrd->iov_len) >> 32;
log_wait();
linux_efi_handover(parent, (uintptr_t) linux_buffer, boot_params);
diff --git a/src/boot/efi/measure.c b/src/boot/efi/measure.c
index 08a2ecdba8..15f1ba9aff 100644
--- a/src/boot/efi/measure.c
+++ b/src/boot/efi/measure.c
@@ -27,6 +27,8 @@ static EFI_STATUS tpm2_measure_to_pcr_and_tagged_event_log(
assert(tcg);
assert(description);
+ /* New style stuff we log as EV_EVENT_TAG with a recognizable event tag. */
+
desc_len = strsize16(description);
event_size = offsetof(EFI_TCG2_EVENT, Event) + offsetof(EFI_TCG2_TAGGED_EVENT, Event) + desc_len;
@@ -53,7 +55,7 @@ static EFI_STATUS tpm2_measure_to_pcr_and_tagged_event_log(
&event->tcg_event);
}
-static EFI_STATUS tpm2_measure_to_pcr_and_event_log(
+static EFI_STATUS tpm2_measure_to_pcr_and_ipl_event_log(
EFI_TCG2_PROTOCOL *tcg,
uint32_t pcrindex,
EFI_PHYSICAL_ADDRESS buffer,
@@ -66,11 +68,10 @@ static EFI_STATUS tpm2_measure_to_pcr_and_event_log(
assert(tcg);
assert(description);
- /* NB: We currently record everything as EV_IPL. Which sucks, because it makes it hard to
- * recognize from the event log which of the events are ours. Measurement logs are kinda API hence
- * this is hard to change for existing, established events. But for future additions, let's use
- * EV_EVENT_TAG instead, with a tag of our choosing that makes clear what precisely we are measuring
- * here. */
+ /* We record older stuff as EV_IPL. Which sucks, because it makes it hard to recognize from the event
+ * log which of the events are ours. Measurement logs are kinda API hence this is hard to change for
+ * existing, established events. But for future additions, let's use EV_EVENT_TAG instead, with a tag
+ * of our choosing that makes clear what precisely we are measuring here. See above. */
desc_len = strsize16(description);
tcg_event = xmalloc(offsetof(EFI_TCG2_EVENT, Event) + desc_len);
@@ -91,7 +92,7 @@ static EFI_STATUS tpm2_measure_to_pcr_and_event_log(
tcg_event);
}
-static EFI_STATUS cc_measure_to_mr_and_event_log(
+static EFI_STATUS cc_measure_to_mr_and_ipl_event_log(
EFI_CC_MEASUREMENT_PROTOCOL *cc,
uint32_t pcrindex,
EFI_PHYSICAL_ADDRESS buffer,
@@ -187,19 +188,24 @@ bool tpm_present(void) {
return tcg2_interface_check();
}
-static EFI_STATUS tcg2_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const char16_t *description, bool *ret_measured) {
+static EFI_STATUS tcg2_log_ipl_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const char16_t *description, bool *ret_measured) {
EFI_TCG2_PROTOCOL *tpm2;
EFI_STATUS err = EFI_SUCCESS;
assert(ret_measured);
tpm2 = tcg2_interface_check();
- if (tpm2)
- err = tpm2_measure_to_pcr_and_event_log(tpm2, pcrindex, buffer, buffer_size, description);
+ if (!tpm2) {
+ *ret_measured = false;
+ return EFI_SUCCESS;
+ }
- *ret_measured = tpm2 && (err == EFI_SUCCESS);
+ err = tpm2_measure_to_pcr_and_ipl_event_log(tpm2, pcrindex, buffer, buffer_size, description);
+ if (err != EFI_SUCCESS)
+ return err;
- return err;
+ *ret_measured = true;
+ return EFI_SUCCESS;
}
static EFI_STATUS cc_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const char16_t *description, bool *ret_measured) {
@@ -209,15 +215,20 @@ static EFI_STATUS cc_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, s
assert(ret_measured);
cc = cc_interface_check();
- if (cc)
- err = cc_measure_to_mr_and_event_log(cc, pcrindex, buffer, buffer_size, description);
+ if (!cc) {
+ *ret_measured = false;
+ return EFI_SUCCESS;
+ }
- *ret_measured = cc && (err == EFI_SUCCESS);
+ err = cc_measure_to_mr_and_ipl_event_log(cc, pcrindex, buffer, buffer_size, description);
+ if (err != EFI_SUCCESS)
+ return err;
- return err;
+ *ret_measured = true;
+ return EFI_SUCCESS;
}
-EFI_STATUS tpm_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const char16_t *description, bool *ret_measured) {
+EFI_STATUS tpm_log_ipl_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const char16_t *description, bool *ret_measured) {
EFI_STATUS err;
bool tpm_ret_measured, cc_ret_measured;
@@ -238,12 +249,14 @@ EFI_STATUS tpm_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t
if (err != EFI_SUCCESS)
return err;
- err = tcg2_log_event(pcrindex, buffer, buffer_size, description, &tpm_ret_measured);
+ err = tcg2_log_ipl_event(pcrindex, buffer, buffer_size, description, &tpm_ret_measured);
+ if (err != EFI_SUCCESS)
+ return err;
- if (err == EFI_SUCCESS && ret_measured)
+ if (ret_measured)
*ret_measured = tpm_ret_measured || cc_ret_measured;
- return err;
+ return EFI_SUCCESS;
}
EFI_STATUS tpm_log_tagged_event(
@@ -272,42 +285,39 @@ EFI_STATUS tpm_log_tagged_event(
}
err = tpm2_measure_to_pcr_and_tagged_event_log(tpm2, pcrindex, buffer, buffer_size, event_id, description);
- if (err == EFI_SUCCESS && ret_measured)
- *ret_measured = true;
+ if (!err)
+ return err;
- return err;
+ *ret_measured = true;
+ return EFI_SUCCESS;
}
-EFI_STATUS tpm_log_event_ascii(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const char *description, bool *ret_measured) {
+EFI_STATUS tpm_log_ipl_event_ascii(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const char *description, bool *ret_measured) {
_cleanup_free_ char16_t *c = NULL;
if (description)
c = xstr8_to_16(description);
- return tpm_log_event(pcrindex, buffer, buffer_size, c, ret_measured);
+ return tpm_log_ipl_event(pcrindex, buffer, buffer_size, c, ret_measured);
}
EFI_STATUS tpm_log_load_options(const char16_t *load_options, bool *ret_measured) {
- bool measured = false;
EFI_STATUS err;
/* Measures a load options string into the TPM2, i.e. the kernel command line */
- err = tpm_log_event(
+ err = tpm_log_ipl_event(
TPM2_PCR_KERNEL_CONFIG,
POINTER_TO_PHYSICAL_ADDRESS(load_options),
strsize16(load_options),
load_options,
- &measured);
+ ret_measured);
if (err != EFI_SUCCESS)
return log_error_status(
err,
"Unable to add load options (i.e. kernel command) line measurement to PCR %i: %m",
TPM2_PCR_KERNEL_CONFIG);
- if (ret_measured)
- *ret_measured = measured;
-
return EFI_SUCCESS;
}
diff --git a/src/boot/efi/measure.h b/src/boot/efi/measure.h
index c3c4e0a9ad..9dde93b94d 100644
--- a/src/boot/efi/measure.h
+++ b/src/boot/efi/measure.h
@@ -6,9 +6,20 @@
#if ENABLE_TPM
bool tpm_present(void);
-EFI_STATUS tpm_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const char16_t *description, bool *ret_measured);
-EFI_STATUS tpm_log_event_ascii(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const char *description, bool *ret_measured);
+
+/* Routines for boot-time TPM PCR measurement as well as submitting an event log entry about it. The latter
+ * can be done with two different event log record types. For old stuff we use EV_IPL (which is legacy, and
+ * not great to recognize properly during PCR validation). For new stuff we use properly tagged
+ * EV_EVENT_TAG record. */
+
+/* Old stuff is logged as EV_IPL */
+EFI_STATUS tpm_log_ipl_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const char16_t *description, bool *ret_measured);
+EFI_STATUS tpm_log_ipl_event_ascii(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const
+char *description, bool *ret_measured);
+
+/* New stuff is logged as EV_EVENT_TAG */
EFI_STATUS tpm_log_tagged_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, uint32_t event_id, const char16_t *description, bool *ret_measured);
+
EFI_STATUS tpm_log_load_options(const char16_t *cmdline, bool *ret_measured);
#else
@@ -17,13 +28,13 @@ static inline bool tpm_present(void) {
return false;
}
-static inline EFI_STATUS tpm_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const char16_t *description, bool *ret_measured) {
+static inline EFI_STATUS tpm_log_ipl_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const char16_t *description, bool *ret_measured) {
if (ret_measured)
*ret_measured = false;
return EFI_SUCCESS;
}
-static inline EFI_STATUS tpm_log_event_ascii(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const char *description, bool *ret_measured) {
+static inline EFI_STATUS tpm_log_ipl_event_ascii(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const char *description, bool *ret_measured) {
if (ret_measured)
*ret_measured = false;
return EFI_SUCCESS;
diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build
index 7a60b0ec7e..42a7914bf6 100644
--- a/src/boot/efi/meson.build
+++ b/src/boot/efi/meson.build
@@ -258,6 +258,7 @@ libefi_sources = files(
'devicetree.c',
'drivers.c',
'efi-string.c',
+ 'export-vars.c',
'graphics.c',
'initrd.c',
'log.c',
diff --git a/src/boot/efi/pe.c b/src/boot/efi/pe.c
index 829266b7f5..587e3ef7b1 100644
--- a/src/boot/efi/pe.c
+++ b/src/boot/efi/pe.c
@@ -101,7 +101,7 @@ typedef struct PeOptionalHeader {
} _packed_ PeOptionalHeader;
typedef struct PeFileHeader {
- uint8_t Magic[4];
+ uint8_t Magic[4];
CoffFileHeader FileHeader;
PeOptionalHeader OptionalHeader;
} _packed_ PeFileHeader;
@@ -119,69 +119,134 @@ typedef struct PeSectionHeader {
uint32_t Characteristics;
} _packed_ PeSectionHeader;
+#define SECTION_TABLE_BYTES_MAX (16U * 1024U * 1024U)
+
static bool verify_dos(const DosFileHeader *dos) {
assert(dos);
- return memcmp(dos->Magic, DOS_FILE_MAGIC, STRLEN(DOS_FILE_MAGIC)) == 0;
+
+ DISABLE_WARNING_TYPE_LIMITS;
+ return memcmp(dos->Magic, DOS_FILE_MAGIC, STRLEN(DOS_FILE_MAGIC)) == 0 &&
+ dos->ExeHeader >= sizeof(DosFileHeader) &&
+ (size_t) dos->ExeHeader <= SIZE_MAX - sizeof(PeFileHeader);
+ REENABLE_WARNING;
}
-static bool verify_pe(const PeFileHeader *pe, bool allow_compatibility) {
+static bool verify_pe(
+ const DosFileHeader *dos,
+ const PeFileHeader *pe,
+ bool allow_compatibility) {
+
+ assert(dos);
assert(pe);
+
return memcmp(pe->Magic, PE_FILE_MAGIC, STRLEN(PE_FILE_MAGIC)) == 0 &&
- (pe->FileHeader.Machine == TARGET_MACHINE_TYPE ||
- (allow_compatibility && pe->FileHeader.Machine == TARGET_MACHINE_TYPE_COMPATIBILITY)) &&
- pe->FileHeader.NumberOfSections > 0 &&
- pe->FileHeader.NumberOfSections <= MAX_SECTIONS &&
- IN_SET(pe->OptionalHeader.Magic, OPTHDR32_MAGIC, OPTHDR64_MAGIC);
+ (pe->FileHeader.Machine == TARGET_MACHINE_TYPE ||
+ (allow_compatibility && pe->FileHeader.Machine == TARGET_MACHINE_TYPE_COMPATIBILITY)) &&
+ pe->FileHeader.NumberOfSections > 0 &&
+ pe->FileHeader.NumberOfSections <= MAX_SECTIONS &&
+ IN_SET(pe->OptionalHeader.Magic, OPTHDR32_MAGIC, OPTHDR64_MAGIC) &&
+ pe->FileHeader.SizeOfOptionalHeader < SIZE_MAX - (dos->ExeHeader + offsetof(PeFileHeader, OptionalHeader));
}
static size_t section_table_offset(const DosFileHeader *dos, const PeFileHeader *pe) {
assert(dos);
assert(pe);
+
return dos->ExeHeader + offsetof(PeFileHeader, OptionalHeader) + pe->FileHeader.SizeOfOptionalHeader;
}
-static void locate_sections(
+static bool pe_section_name_equal(const char *a, const char *b) {
+
+ if (a == b)
+ return true;
+ if (!a != !b)
+ return false;
+
+ /* Compares up to 8 characters of a and b i.e. the name size limit in the PE section header */
+
+ for (size_t i = 0; i < sizeof_field(PeSectionHeader, Name); i++) {
+ if (a[i] != b[i])
+ return false;
+
+ if (a[i] == 0) /* Name is shorter than 8 */
+ return true;
+ }
+
+ return true;
+}
+
+static void pe_locate_sections(
const PeSectionHeader section_table[],
- size_t n_table,
+ size_t n_section_table,
const char * const sections[],
- size_t *offsets,
- size_t *sizes,
- bool in_memory) {
+ size_t validate_base,
+ PeSectionVector *ret_sections) {
- assert(section_table);
+ assert(section_table || n_section_table == 0);
assert(sections);
- assert(offsets);
- assert(sizes);
+ assert(ret_sections);
+
+ /* Searches for the sections listed in 'sections[]' within the section table. Validates the resulted
+ * data. If 'validate_base' is non-zero also takes base offset when loaded into memory into account for
+ * qchecking for overflows. */
- for (size_t i = 0; i < n_table; i++) {
- const PeSectionHeader *sect = section_table + i;
+ for (size_t i = 0; sections[i]; i++)
+ FOREACH_ARRAY(j, section_table, n_section_table) {
+
+ if (!pe_section_name_equal((const char*) j->Name, sections[i]))
+ continue;
- for (size_t j = 0; sections[j]; j++) {
- if (memcmp(sect->Name, sections[j], strlen8(sections[j])) != 0)
+ /* Overflow check: ignore sections that are impossibly large, relative to the file
+ * address for the section. */
+ size_t size_max = SIZE_MAX - j->PointerToRawData;
+ if ((size_t) j->VirtualSize > size_max)
continue;
- offsets[j] = in_memory ? sect->VirtualAddress : sect->PointerToRawData;
- sizes[j] = sect->VirtualSize;
+ /* Overflow check: ignore sections that are impossibly large, given the virtual
+ * address for the section */
+ size_max = SIZE_MAX - j->VirtualAddress;
+ if (j->VirtualSize > size_max)
+ continue;
+
+ /* 2nd overflow check: ignore sections that are impossibly large also taking the
+ * loaded base into account. */
+ if (validate_base != 0) {
+ if (validate_base > size_max)
+ continue;
+ size_max -= validate_base;
+
+ if (j->VirtualAddress > size_max)
+ continue;
+ }
+
+ /* At this time, the sizes and offsets have been validated. Store them away */
+ ret_sections[i] = (PeSectionVector) {
+ .size = j->VirtualSize,
+ .file_offset = j->PointerToRawData,
+ .memory_offset = j->VirtualAddress,
+ };
+
+ /* First matching section wins, ignore the rest */
+ break;
}
- }
}
static uint32_t get_compatibility_entry_address(const DosFileHeader *dos, const PeFileHeader *pe) {
- size_t addr = 0, size = 0;
static const char *sections[] = { ".compat", NULL };
+ PeSectionVector vector = {};
/* The kernel may provide alternative PE entry points for different PE architectures. This allows
* booting a 64-bit kernel on 32-bit EFI that is otherwise running on a 64-bit CPU. The locations of any
* such compat entry points are located in a special PE section. */
- locate_sections((const PeSectionHeader *) ((const uint8_t *) dos + section_table_offset(dos, pe)),
+ pe_locate_sections(
+ (const PeSectionHeader *) ((const uint8_t *) dos + section_table_offset(dos, pe)),
pe->FileHeader.NumberOfSections,
sections,
- &addr,
- &size,
- /*in_memory=*/true);
+ PTR_TO_SIZE(dos),
+ &vector);
- if (size == 0)
+ if (vector.size == 0) /* not found */
return 0;
typedef struct {
@@ -191,6 +256,8 @@ static uint32_t get_compatibility_entry_address(const DosFileHeader *dos, const
uint32_t entry_point;
} _packed_ LinuxPeCompat1;
+ size_t addr = vector.memory_offset, size = vector.size;
+
while (size >= sizeof(LinuxPeCompat1) && addr % alignof(LinuxPeCompat1) == 0) {
LinuxPeCompat1 *compat = (LinuxPeCompat1 *) ((uint8_t *) dos + addr);
@@ -218,7 +285,7 @@ EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address) {
return EFI_LOAD_ERROR;
const PeFileHeader *pe = (const PeFileHeader *) ((const uint8_t *) base + dos->ExeHeader);
- if (!verify_pe(pe, /* allow_compatibility= */ true))
+ if (!verify_pe(dos, pe, /* allow_compatibility= */ true))
return EFI_LOAD_ERROR;
/* Support for LINUX_INITRD_MEDIA_GUID was added in kernel stub 1.0. */
@@ -239,31 +306,34 @@ EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address) {
return EFI_SUCCESS;
}
-EFI_STATUS pe_memory_locate_sections(const void *base, const char * const sections[], size_t *addrs, size_t *sizes) {
+EFI_STATUS pe_memory_locate_sections(
+ const void *base,
+ const char* const sections[],
+ PeSectionVector *ret_sections) {
+
const DosFileHeader *dos;
const PeFileHeader *pe;
size_t offset;
assert(base);
assert(sections);
- assert(addrs);
- assert(sizes);
+ assert(ret_sections);
dos = (const DosFileHeader *) base;
if (!verify_dos(dos))
return EFI_LOAD_ERROR;
- pe = (const PeFileHeader *) ((uint8_t *) base + dos->ExeHeader);
- if (!verify_pe(pe, /* allow_compatibility= */ false))
+ pe = (const PeFileHeader *) ((const uint8_t *) base + dos->ExeHeader);
+ if (!verify_pe(dos, pe, /* allow_compatibility= */ false))
return EFI_LOAD_ERROR;
offset = section_table_offset(dos, pe);
- locate_sections((PeSectionHeader *) ((uint8_t *) base + offset),
+ pe_locate_sections(
+ (const PeSectionHeader *) ((const uint8_t *) base + offset),
pe->FileHeader.NumberOfSections,
sections,
- addrs,
- sizes,
- /*in_memory=*/true);
+ PTR_TO_SIZE(base),
+ ret_sections);
return EFI_SUCCESS;
}
@@ -272,8 +342,7 @@ EFI_STATUS pe_file_locate_sections(
EFI_FILE *dir,
const char16_t *path,
const char * const sections[],
- size_t *offsets,
- size_t *sizes) {
+ PeSectionVector *ret_sections) {
_cleanup_free_ PeSectionHeader *section_table = NULL;
_cleanup_(file_closep) EFI_FILE *handle = NULL;
DosFileHeader dos;
@@ -284,8 +353,7 @@ EFI_STATUS pe_file_locate_sections(
assert(dir);
assert(path);
assert(sections);
- assert(offsets);
- assert(sizes);
+ assert(ret_sections);
err = dir->Open(dir, &handle, (char16_t *) path, EFI_FILE_MODE_READ, 0ULL);
if (err != EFI_SUCCESS)
@@ -306,10 +374,16 @@ EFI_STATUS pe_file_locate_sections(
err = handle->Read(handle, &len, &pe);
if (err != EFI_SUCCESS)
return err;
- if (len != sizeof(pe) || !verify_pe(&pe, /* allow_compatibility= */ false))
+ if (len != sizeof(pe) || !verify_pe(&dos, &pe, /* allow_compatibility= */ false))
return EFI_LOAD_ERROR;
- section_table_len = pe.FileHeader.NumberOfSections * sizeof(PeSectionHeader);
+ DISABLE_WARNING_TYPE_LIMITS;
+ if ((size_t) pe.FileHeader.NumberOfSections > SIZE_MAX / sizeof(PeSectionHeader))
+ return EFI_OUT_OF_RESOURCES;
+ REENABLE_WARNING;
+ section_table_len = (size_t) pe.FileHeader.NumberOfSections * sizeof(PeSectionHeader);
+ if (section_table_len > SECTION_TABLE_BYTES_MAX)
+ return EFI_OUT_OF_RESOURCES;
section_table = xmalloc(section_table_len);
if (!section_table)
return EFI_OUT_OF_RESOURCES;
@@ -325,8 +399,12 @@ EFI_STATUS pe_file_locate_sections(
if (len != section_table_len)
return EFI_LOAD_ERROR;
- locate_sections(section_table, pe.FileHeader.NumberOfSections,
- sections, offsets, sizes, /*in_memory=*/false);
+ pe_locate_sections(
+ section_table,
+ pe.FileHeader.NumberOfSections,
+ sections,
+ /* validate_base= */ 0, /* don't validate base */
+ ret_sections);
return EFI_SUCCESS;
}
diff --git a/src/boot/efi/pe.h b/src/boot/efi/pe.h
index 7e2258fceb..bc6d74beeb 100644
--- a/src/boot/efi/pe.h
+++ b/src/boot/efi/pe.h
@@ -3,17 +3,27 @@
#include "efi.h"
+/* This is a subset of the full PE section header structure, with validated values, and without
+ * the noise. */
+typedef struct PeSectionVector {
+ size_t size;
+ size_t memory_offset; /* Offset in memory, relative to base address */
+ uint64_t file_offset; /* Offset on disk, relative to beginning of file */
+} PeSectionVector;
+
+static inline bool PE_SECTION_VECTOR_IS_SET(const PeSectionVector *v) {
+ return v && v->size != 0;
+}
+
EFI_STATUS pe_memory_locate_sections(
const void *base,
const char * const sections[],
- size_t *addrs,
- size_t *sizes);
+ PeSectionVector *ret_sections);
EFI_STATUS pe_file_locate_sections(
EFI_FILE *dir,
const char16_t *path,
const char * const sections[],
- size_t *offsets,
- size_t *sizes);
+ PeSectionVector *ret_sections);
EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address);
diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c
index 9aa605b756..f51812d0d1 100644
--- a/src/boot/efi/stub.c
+++ b/src/boot/efi/stub.c
@@ -3,7 +3,9 @@
#include "cpio.h"
#include "device-path-util.h"
#include "devicetree.h"
+#include "export-vars.h"
#include "graphics.h"
+#include "iovec-util-fundamental.h"
#include "linux.h"
#include "measure.h"
#include "memory-util-fundamental.h"
@@ -21,28 +23,88 @@
#include "version.h"
#include "vmm.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_GLOBAL_CREDENTIAL,
+ INITRD_SYSEXT,
+ INITRD_CONFEXT,
+ INITRD_PCRSIG,
+ INITRD_PCRPKEY,
+ _INITRD_MAX,
+};
+
/* magic string to find in the binary image */
DECLARE_NOALLOC_SECTION(".sdmagic", "#### LoaderInfo: systemd-stub " GIT_VERSION " ####");
DECLARE_SBAT(SBAT_STUB_SECTION_TEXT);
+static char16_t* pe_section_to_str16(
+ EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
+ const PeSectionVector *section) {
+
+ assert(loaded_image);
+ assert(section);
+
+ if (!PE_SECTION_VECTOR_IS_SET(section))
+ return NULL;
+
+ return xstrn8_to_16((const char *) loaded_image->ImageBase + section->memory_offset, section->size);
+}
+
+static char *pe_section_to_str8(
+ EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
+ const PeSectionVector *section) {
+
+ assert(loaded_image);
+ assert(section);
+
+ if (!PE_SECTION_VECTOR_IS_SET(section))
+ return NULL;
+
+ return xstrndup8((const char *)loaded_image->ImageBase + section->memory_offset, section->size);
+}
+
+static void combine_measured_flag(int *value, int measured) {
+ assert(value);
+
+ /* Combine the "measured" flag in a sensible way: if we haven't measured anything yet, the first
+ * write is taken as is. Later writes can only turn off the flag, never on again. Or in other words,
+ * we eventually want to return true iff we really measured *everything* there was to measure.
+ *
+ * Reminder how the "measured" flag actually works:
+ * > 0 → something was measured
+ * == 0 → there was something to measure but we didn't (because no TPM or so)
+ * < 0 → nothing has been submitted for measurement so far
+ */
+
+ if (measured < 0)
+ return;
+
+ *value = *value < 0 ? measured : *value && measured;
+}
+
/* Combine initrds by concatenation in memory */
static EFI_STATUS combine_initrds(
- const void * const initrds[], const size_t initrd_sizes[], size_t n_initrds,
- Pages *ret_initr_pages, size_t *ret_initrd_size) {
+ struct iovec initrds[], size_t n_initrds,
+ Pages *ret_initrd_pages, size_t *ret_initrd_size) {
size_t n = 0;
- assert(ret_initr_pages);
+ assert(initrds || n_initrds == 0);
+ assert(ret_initrd_pages);
assert(ret_initrd_size);
- for (size_t i = 0; i < n_initrds; i++) {
- if (!initrds[i])
- continue;
+ FOREACH_ARRAY(i, initrds, n_initrds) {
+ /* some initrds (the ones from UKI sections) need padding, pad all to be safe */
- /* some initrds (the ones from UKI sections) need padding,
- * pad all to be safe */
- size_t initrd_size = ALIGN4(initrd_sizes[i]);
+ size_t initrd_size = ALIGN4(i->iov_len);
if (n > SIZE_MAX - initrd_size)
return EFI_OUT_OF_RESOURCES;
@@ -55,31 +117,29 @@ static EFI_STATUS combine_initrds(
EFI_SIZE_TO_PAGES(n),
UINT32_MAX /* Below 4G boundary. */);
uint8_t *p = PHYSICAL_ADDRESS_TO_POINTER(pages.addr);
- for (size_t i = 0; i < n_initrds; i++) {
- if (!initrds[i])
- continue;
-
+ FOREACH_ARRAY(i, initrds, n_initrds) {
size_t pad;
- p = mempcpy(p, initrds[i], initrd_sizes[i]);
+ p = mempcpy(p, i->iov_base, i->iov_len);
- pad = ALIGN4(initrd_sizes[i]) - initrd_sizes[i];
- if (pad > 0) {
- memzero(p, pad);
- p += pad;
- }
+ pad = ALIGN4(i->iov_len) - i->iov_len;
+ if (pad == 0)
+ continue;
+
+ memzero(p, pad);
+ p += pad;
}
assert(PHYSICAL_ADDRESS_TO_POINTER(pages.addr + n) == p);
- *ret_initr_pages = pages;
+ *ret_initrd_pages = pages;
*ret_initrd_size = n;
pages.n_pages = 0;
return EFI_SUCCESS;
}
-static void export_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) {
+static void export_stub_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) {
static const uint64_t stub_features =
EFI_STUB_FEATURE_REPORT_BOOT_PARTITION | /* We set LoaderDevicePartUUID */
EFI_STUB_FEATURE_PICK_UP_CREDENTIALS | /* We pick up credentials from the boot partition */
@@ -94,41 +154,6 @@ static void export_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) {
assert(loaded_image);
- /* Export the device path this image is started from, if it's not set yet */
- if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderDevicePartUUID", NULL, NULL) != EFI_SUCCESS) {
- _cleanup_free_ char16_t *uuid = disk_get_part_uuid(loaded_image->DeviceHandle);
- if (uuid)
- efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderDevicePartUUID", uuid, 0);
- }
-
- /* If LoaderImageIdentifier is not set, assume the image with this stub was loaded directly from the
- * UEFI firmware without any boot loader, and hence set the LoaderImageIdentifier ourselves. Note
- * that some boot chain loaders neither set LoaderImageIdentifier nor make FilePath available to us,
- * in which case there's simple nothing to set for us. (The UEFI spec doesn't really say who's wrong
- * here, i.e. whether FilePath may be NULL or not, hence handle this gracefully and check if FilePath
- * is non-NULL explicitly.) */
- if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderImageIdentifier", NULL, NULL) != EFI_SUCCESS &&
- loaded_image->FilePath) {
- _cleanup_free_ char16_t *s = NULL;
- if (device_path_to_str(loaded_image->FilePath, &s) == EFI_SUCCESS)
- efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderImageIdentifier", s, 0);
- }
-
- /* if LoaderFirmwareInfo is not set, let's set it */
- if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareInfo", NULL, NULL) != EFI_SUCCESS) {
- _cleanup_free_ char16_t *s = NULL;
- s = xasprintf("%ls %u.%02u", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
- efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareInfo", s, 0);
- }
-
- /* ditto for LoaderFirmwareType */
- if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareType", NULL, NULL) != EFI_SUCCESS) {
- _cleanup_free_ char16_t *s = NULL;
- s = xasprintf("UEFI %u.%02u", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
- efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareType", s, 0);
- }
-
-
/* add StubInfo (this is one is owned by the stub, hence we unconditionally override this with our
* own data) */
(void) efivar_set(MAKE_GUID_PTR(LOADER), u"StubInfo", u"systemd-stub " GIT_VERSION, 0);
@@ -152,20 +177,19 @@ static bool use_load_options(
if (secure_boot_enabled() && (have_cmdline || is_confidential_vm()))
return false;
- /* We also do a superficial check whether first character of passed command line
- * is printable character (for compat with some Dell systems which fill in garbage?). */
- if (loaded_image->LoadOptionsSize < sizeof(char16_t) || ((char16_t *) loaded_image->LoadOptions)[0] <= 0x1F)
- return false;
-
/* The UEFI shell registers EFI_SHELL_PARAMETERS_PROTOCOL onto images it runs. This lets us know that
* LoadOptions starts with the stub binary path which we want to strip off. */
EFI_SHELL_PARAMETERS_PROTOCOL *shell;
- if (BS->HandleProtocol(stub_image, MAKE_GUID_PTR(EFI_SHELL_PARAMETERS_PROTOCOL), (void **) &shell)
- != EFI_SUCCESS) {
+ if (BS->HandleProtocol(stub_image, MAKE_GUID_PTR(EFI_SHELL_PARAMETERS_PROTOCOL), (void **) &shell) != EFI_SUCCESS) {
+
+ /* We also do a superficial check whether first character of passed command line
+ * is printable character (for compat with some Dell systems which fill in garbage?). */
+ if (loaded_image->LoadOptionsSize < sizeof(char16_t) || ((const char16_t *) loaded_image->LoadOptions)[0] <= 0x1F)
+ return false;
+
/* Not running from EFI shell, use entire LoadOptions. Note that LoadOptions is a void*, so
* it could be anything! */
- *ret = xstrndup16(loaded_image->LoadOptions, loaded_image->LoadOptionsSize / sizeof(char16_t));
- mangle_stub_cmdline(*ret);
+ *ret = mangle_stub_cmdline(xstrndup16(loaded_image->LoadOptions, loaded_image->LoadOptionsSize / sizeof(char16_t)));
return true;
}
@@ -180,7 +204,6 @@ static bool use_load_options(
*ret = xasprintf("%ls %ls", old, shell->Argv[i]);
}
- mangle_stub_cmdline(*ret);
return true;
}
@@ -249,96 +272,88 @@ static EFI_STATUS load_addons_from_dir(
}
static void cmdline_append_and_measure_addons(
- char16_t *cmdline_global,
- char16_t *cmdline_uki,
+ char16_t *cmdline_addon,
char16_t **cmdline_append,
- bool *ret_parameters_measured) {
-
- _cleanup_free_ char16_t *tmp = NULL, *merged = NULL;
- bool m = false;
+ int *parameters_measured) {
assert(cmdline_append);
- assert(ret_parameters_measured);
+ assert(parameters_measured);
- if (isempty(cmdline_global) && isempty(cmdline_uki))
+ if (isempty(cmdline_addon))
return;
- merged = xasprintf("%ls%ls%ls",
- strempty(cmdline_global),
- isempty(cmdline_global) || isempty(cmdline_uki) ? u"" : u" ",
- strempty(cmdline_uki));
-
- mangle_stub_cmdline(merged);
-
- if (isempty(merged))
+ _cleanup_free_ char16_t *copy = mangle_stub_cmdline(xstrdup16(cmdline_addon));
+ if (isempty(copy))
return;
- (void) tpm_log_load_options(merged, &m);
- *ret_parameters_measured = m;
-
- tmp = TAKE_PTR(*cmdline_append);
- *cmdline_append = xasprintf("%ls%ls%ls", strempty(tmp), isempty(tmp) ? u"" : u" ", merged);
+ bool m = false;
+ (void) tpm_log_load_options(copy, &m);
+ combine_measured_flag(parameters_measured, m);
+
+ _cleanup_free_ char16_t *tmp = TAKE_PTR(*cmdline_append);
+ if (isempty(tmp))
+ *cmdline_append = TAKE_PTR(copy);
+ else
+ *cmdline_append = xasprintf("%ls %ls", tmp, copy);
}
-static void dtb_install_addons(
- struct devicetree_state *dt_state,
- void **dt_bases,
- size_t *dt_sizes,
- char16_t **dt_filenames,
- size_t n_dts,
- bool *ret_parameters_measured) {
+typedef struct DevicetreeAddon {
+ char16_t *filename;
+ struct iovec blob;
+} DevicetreeAddon;
- int parameters_measured = -1;
- EFI_STATUS err;
+static void devicetree_addon_done(DevicetreeAddon *a) {
+ assert(a);
- assert(dt_state);
- assert(n_dts == 0 || (dt_bases && dt_sizes && dt_filenames));
- assert(ret_parameters_measured);
+ a->filename = mfree(a->filename);
+ iovec_done(&a->blob);
+}
- for (size_t i = 0; i < n_dts; ++i) {
- err = devicetree_install_from_memory(dt_state, dt_bases[i], dt_sizes[i]);
- if (err != EFI_SUCCESS)
- log_error_status(err, "Error loading addon devicetree, ignoring: %m");
- else {
- bool m = false;
+static void devicetree_addon_free_many(DevicetreeAddon *a, size_t n) {
+ assert(a || n == 0);
- err = tpm_log_tagged_event(
- TPM2_PCR_KERNEL_CONFIG,
- POINTER_TO_PHYSICAL_ADDRESS(dt_bases[i]),
- dt_sizes[i],
- DEVICETREE_ADDON_EVENT_TAG_ID,
- dt_filenames[i],
- &m);
- if (err != EFI_SUCCESS)
- return (void) log_error_status(
- err,
- "Unable to add measurement of DTB addon #%zu to PCR %i: %m",
- i,
- TPM2_PCR_KERNEL_CONFIG);
-
- parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
- }
- }
+ FOREACH_ARRAY(i, a, n)
+ devicetree_addon_done(i);
- *ret_parameters_measured = parameters_measured;
+ free(a);
}
-static void dt_bases_free(void **dt_bases, size_t n_dt) {
- assert(dt_bases || n_dt == 0);
+static void install_addon_devicetrees(
+ struct devicetree_state *dt_state,
+ DevicetreeAddon *addons,
+ size_t n_addons,
+ int *parameters_measured) {
- for (size_t i = 0; i < n_dt; ++i)
- free(dt_bases[i]);
+ EFI_STATUS err;
- free(dt_bases);
-}
+ assert(dt_state);
+ assert(addons || n_addons == 0);
+ assert(parameters_measured);
-static void dt_filenames_free(char16_t **dt_filenames, size_t n_dt) {
- assert(dt_filenames || n_dt == 0);
+ FOREACH_ARRAY(a, addons, n_addons) {
+ err = devicetree_install_from_memory(dt_state, a->blob.iov_base, a->blob.iov_len);
+ if (err != EFI_SUCCESS) {
+ log_error_status(err, "Error loading addon devicetree, ignoring: %m");
+ continue;
+ }
- for (size_t i = 0; i < n_dt; ++i)
- free(dt_filenames[i]);
+ bool m = false;
+ err = tpm_log_tagged_event(
+ TPM2_PCR_KERNEL_CONFIG,
+ POINTER_TO_PHYSICAL_ADDRESS(a->blob.iov_base),
+ a->blob.iov_len,
+ DEVICETREE_ADDON_EVENT_TAG_ID,
+ a->filename,
+ &m);
+ if (err != EFI_SUCCESS)
+ return (void) log_error_status(
+ err,
+ "Unable to extend PCR %i with DTB addon '%ls': %m",
+ TPM2_PCR_KERNEL_CONFIG,
+ a->filename);
- free(dt_filenames);
+ combine_measured_flag(parameters_measured, m);
+ }
}
static EFI_STATUS load_addons(
@@ -346,34 +361,22 @@ static EFI_STATUS load_addons(
EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
const char16_t *prefix,
const char *uname,
- char16_t **ret_cmdline,
- void ***ret_dt_bases,
- size_t **ret_dt_sizes,
- char16_t ***ret_dt_filenames,
- size_t *ret_n_dt) {
+ char16_t **cmdline, /* Both input+output, extended with new addons we find */
+ DevicetreeAddon **devicetree_addons, /* Ditto */
+ size_t *n_devicetree_addons) {
- _cleanup_free_ size_t *dt_sizes = NULL;
_cleanup_(strv_freep) char16_t **items = NULL;
_cleanup_(file_closep) EFI_FILE *root = NULL;
- _cleanup_free_ char16_t *cmdline = NULL;
- size_t n_items = 0, n_allocated = 0, n_dt = 0;
- char16_t **dt_filenames = NULL;
- void **dt_bases = NULL;
+ size_t n_items = 0, n_allocated = 0;
EFI_STATUS err;
assert(stub_image);
assert(loaded_image);
assert(prefix);
- assert(!!ret_dt_bases == !!ret_dt_sizes);
- assert(!!ret_dt_bases == !!ret_n_dt);
- assert(!!ret_dt_filenames == !!ret_n_dt);
if (!loaded_image->DeviceHandle)
return EFI_SUCCESS;
- CLEANUP_ARRAY(dt_bases, n_dt, dt_bases_free);
- CLEANUP_ARRAY(dt_filenames, n_dt, dt_filenames_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
@@ -394,7 +397,7 @@ static EFI_STATUS load_addons(
sort_pointer_array((void**) items, n_items, (compare_pointer_func_t) strcmp16);
for (size_t i = 0; i < n_items; i++) {
- size_t addrs[_UNIFIED_SECTION_MAX] = {}, szs[_UNIFIED_SECTION_MAX] = {};
+ 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;
@@ -422,9 +425,10 @@ static EFI_STATUS load_addons(
if (err != EFI_SUCCESS)
return log_error_status(err, "Failed to find protocol in %ls: %m", items[i]);
- err = pe_memory_locate_sections(loaded_addon->ImageBase, unified_sections, addrs, szs);
+ err = pe_memory_locate_sections(loaded_addon->ImageBase, unified_sections, sections);
if (err != EFI_SUCCESS ||
- (szs[UNIFIED_SECTION_CMDLINE] == 0 && szs[UNIFIED_SECTION_DTB] == 0)) {
+ (!PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_CMDLINE) &&
+ !PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTB))) {
if (err == EFI_SUCCESS)
err = EFI_NOT_FOUND;
log_error_status(err,
@@ -434,141 +438,80 @@ static EFI_STATUS load_addons(
}
/* We want to enforce that addons are not UKIs, i.e.: they must not embed a kernel. */
- if (szs[UNIFIED_SECTION_LINUX] > 0) {
- log_error_status(EFI_INVALID_PARAMETER, "%ls is a UKI, not an addon, ignoring: %m", items[i]);
+ if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_LINUX)) {
+ log_error("%ls is a UKI, not an addon, ignoring.", items[i]);
continue;
}
/* Also enforce that, in case it is specified, .uname matches as a quick way to allow
* enforcing compatibility with a specific UKI only */
- if (uname && szs[UNIFIED_SECTION_UNAME] > 0 &&
+ if (uname && PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_UNAME) &&
!strneq8(uname,
- (char *)loaded_addon->ImageBase + addrs[UNIFIED_SECTION_UNAME],
- szs[UNIFIED_SECTION_UNAME])) {
+ (const char *)loaded_addon->ImageBase + sections[UNIFIED_SECTION_UNAME].memory_offset,
+ sections[UNIFIED_SECTION_UNAME].size)) {
log_error(".uname mismatch between %ls and UKI, ignoring", items[i]);
continue;
}
- if (ret_cmdline && szs[UNIFIED_SECTION_CMDLINE] > 0) {
- _cleanup_free_ char16_t *tmp = TAKE_PTR(cmdline),
- *extra16 = xstrn8_to_16((char *)loaded_addon->ImageBase + addrs[UNIFIED_SECTION_CMDLINE],
- szs[UNIFIED_SECTION_CMDLINE]);
- cmdline = xasprintf("%ls%ls%ls", strempty(tmp), isempty(tmp) ? u"" : u" ", extra16);
- }
-
- if (ret_dt_bases && szs[UNIFIED_SECTION_DTB] > 0) {
- dt_sizes = xrealloc(dt_sizes,
- n_dt * sizeof(size_t),
- (n_dt + 1) * sizeof(size_t));
- dt_sizes[n_dt] = szs[UNIFIED_SECTION_DTB];
-
- dt_bases = xrealloc(dt_bases,
- n_dt * sizeof(void *),
- (n_dt + 1) * sizeof(void *));
- dt_bases[n_dt] = xmemdup((uint8_t*)loaded_addon->ImageBase + addrs[UNIFIED_SECTION_DTB],
- dt_sizes[n_dt]);
-
- dt_filenames = xrealloc(dt_filenames,
- n_dt * sizeof(char16_t *),
- (n_dt + 1) * sizeof(char16_t *));
- dt_filenames[n_dt] = xstrdup16(items[i]);
+ if (cmdline && PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_CMDLINE)) {
+ _cleanup_free_ char16_t *tmp = TAKE_PTR(*cmdline),
+ *extra16 = mangle_stub_cmdline(pe_section_to_str16(loaded_addon, sections + UNIFIED_SECTION_CMDLINE));
- ++n_dt;
+ *cmdline = xasprintf("%ls%ls%ls", strempty(tmp), isempty(tmp) ? u"" : u" ", extra16);
}
- }
- if (ret_cmdline && !isempty(cmdline))
- *ret_cmdline = TAKE_PTR(cmdline);
+ if (devicetree_addons && PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTB)) {
+ *devicetree_addons = xrealloc(*devicetree_addons,
+ *n_devicetree_addons * sizeof(size_t),
+ (*n_devicetree_addons + 1) * sizeof(size_t));
- if (ret_n_dt && n_dt > 0) {
- *ret_dt_filenames = TAKE_PTR(dt_filenames);
- *ret_dt_bases = TAKE_PTR(dt_bases);
- *ret_dt_sizes = TAKE_PTR(dt_sizes);
- *ret_n_dt = n_dt;
+ *devicetree_addons[(*n_devicetree_addons)++] = (DevicetreeAddon) {
+ .blob = {
+ .iov_base = xmemdup((const uint8_t*) loaded_addon->ImageBase + sections[UNIFIED_SECTION_DTB].memory_offset, sections[UNIFIED_SECTION_DTB].size),
+ .iov_len = sections[UNIFIED_SECTION_DTB].size,
+ },
+ .filename = xstrdup16(items[i]),
+ };
+ }
}
return EFI_SUCCESS;
}
-static EFI_STATUS run(EFI_HANDLE image) {
- _cleanup_free_ void *credential_initrd = NULL, *global_credential_initrd = NULL, *sysext_initrd = NULL, *confext_initrd = NULL, *pcrsig_initrd = NULL, *pcrpkey_initrd = NULL;
- size_t credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0, confext_initrd_size = 0, pcrsig_initrd_size = 0, pcrpkey_initrd_size = 0;
- void **dt_bases_addons_global = NULL, **dt_bases_addons_uki = NULL;
- char16_t **dt_filenames_addons_global = NULL, **dt_filenames_addons_uki = NULL;
- _cleanup_free_ size_t *dt_sizes_addons_global = NULL, *dt_sizes_addons_uki = NULL;
- size_t linux_size, initrd_size, ucode_size, dt_size, n_dts_addons_global = 0, n_dts_addons_uki = 0;
- EFI_PHYSICAL_ADDRESS linux_base, initrd_base, ucode_base, dt_base;
- _cleanup_(devicetree_cleanup) struct devicetree_state dt_state = {};
- EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
- size_t addrs[_UNIFIED_SECTION_MAX] = {}, szs[_UNIFIED_SECTION_MAX] = {};
- _cleanup_free_ char16_t *cmdline = NULL, *cmdline_addons_global = NULL, *cmdline_addons_uki = NULL;
- int sections_measured = -1, parameters_measured = -1;
- _cleanup_free_ char *uname = NULL;
- bool sysext_measured = false, confext_measured = false, m;
- uint64_t loader_features = 0;
+static void refresh_random_seed(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) {
EFI_STATUS err;
- 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 a LoadedImageProtocol handle: %m");
+ assert(loaded_image);
- if (loaded_image->DeviceHandle && /* Handle case, where bootloader doesn't support DeviceHandle. */
- (efivar_get_uint64_le(MAKE_GUID_PTR(LOADER), u"LoaderFeatures", &loader_features) != EFI_SUCCESS ||
- !FLAGS_SET(loader_features, EFI_LOADER_FEATURE_RANDOM_SEED))) {
- _cleanup_(file_closep) EFI_FILE *esp_dir = NULL;
+ /* Handle case, where bootloader doesn't support DeviceHandle. */
+ if (!loaded_image->DeviceHandle)
+ return;
- err = partition_open(MAKE_GUID_PTR(ESP), loaded_image->DeviceHandle, NULL, &esp_dir);
- if (err == EFI_SUCCESS) /* Non-fatal on failure, so that we still boot without it. */
- (void) process_random_seed(esp_dir);
- }
+ uint64_t loader_features = 0;
+ err = efivar_get_uint64_le(MAKE_GUID_PTR(LOADER), u"LoaderFeatures", &loader_features);
+ if (err != EFI_SUCCESS)
+ return;
- err = pe_memory_locate_sections(loaded_image->ImageBase, unified_sections, addrs, szs);
- if (err != EFI_SUCCESS || szs[UNIFIED_SECTION_LINUX] == 0) {
- if (err == EFI_SUCCESS)
- err = EFI_NOT_FOUND;
- return log_error_status(err, "Unable to locate embedded .linux section: %m");
- }
+ /* Don't measure again, if sd-boot already initialized the random seed */
+ if (!FLAGS_SET(loader_features, EFI_LOADER_FEATURE_RANDOM_SEED))
+ return;
- CLEANUP_ARRAY(dt_bases_addons_global, n_dts_addons_global, dt_bases_free);
- CLEANUP_ARRAY(dt_bases_addons_uki, n_dts_addons_uki, dt_bases_free);
- CLEANUP_ARRAY(dt_filenames_addons_global, n_dts_addons_global, dt_filenames_free);
- CLEANUP_ARRAY(dt_filenames_addons_uki, n_dts_addons_uki, dt_filenames_free);
+ _cleanup_(file_closep) EFI_FILE *esp_dir = NULL;
+ err = partition_open(MAKE_GUID_PTR(ESP), loaded_image->DeviceHandle, NULL, &esp_dir);
+ if (err != EFI_SUCCESS) /* Non-fatal on failure, so that we still boot without it. */
+ return;
- if (szs[UNIFIED_SECTION_UNAME] > 0)
- uname = xstrndup8((char *)loaded_image->ImageBase + addrs[UNIFIED_SECTION_UNAME],
- szs[UNIFIED_SECTION_UNAME]);
+ (void) process_random_seed(esp_dir);
+}
- /* Now that we have the UKI sections loaded, also load global first and then local (per-UKI)
- * addons. The data is loaded at once, and then used later. */
- err = load_addons(
- image,
- loaded_image,
- u"\\loader\\addons",
- uname,
- &cmdline_addons_global,
- &dt_bases_addons_global,
- &dt_sizes_addons_global,
- &dt_filenames_addons_global,
- &n_dts_addons_global);
- if (err != EFI_SUCCESS)
- log_error_status(err, "Error loading global addons, ignoring: %m");
+static void measure_sections(
+ EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
+ const PeSectionVector sections[static _UNIFIED_SECTION_MAX],
+ int *sections_measured) {
- /* Some bootloaders always pass NULL in FilePath, so we need to check for it here. */
- _cleanup_free_ char16_t *dropin_dir = get_extra_dir(loaded_image->FilePath);
- if (dropin_dir) {
- err = load_addons(
- image,
- loaded_image,
- dropin_dir,
- uname,
- &cmdline_addons_uki,
- &dt_bases_addons_uki,
- &dt_sizes_addons_uki,
- &dt_filenames_addons_uki,
- &n_dts_addons_uki);
- if (err != EFI_SUCCESS)
- log_error_status(err, "Error loading UKI-specific addons, ignoring: %m");
- }
+ assert(loaded_image);
+ assert(sections);
+ assert(sections_measured);
/* Measure all "payload" of this PE image into a separate PCR (i.e. where nothing else is written
* into so far), so that we have one PCR that we can nicely write policies against because it
@@ -578,80 +521,102 @@ static EFI_STATUS run(EFI_HANDLE image) {
if (!unified_section_measure(section)) /* shall not measure? */
continue;
- if (szs[section] == 0) /* not found */
+ if (!PE_SECTION_VECTOR_IS_SET(sections + section)) /* not found */
continue;
- m = false;
-
/* First measure the name of the section */
- (void) tpm_log_event_ascii(
+ bool m = false;
+ (void) tpm_log_ipl_event_ascii(
TPM2_PCR_KERNEL_BOOT,
POINTER_TO_PHYSICAL_ADDRESS(unified_sections[section]),
strsize8(unified_sections[section]), /* including NUL byte */
unified_sections[section],
&m);
-
- sections_measured = sections_measured < 0 ? m : (sections_measured && m);
+ combine_measured_flag(sections_measured, m);
/* Then measure the data of the section */
- (void) tpm_log_event_ascii(
+ m = false;
+ (void) tpm_log_ipl_event_ascii(
TPM2_PCR_KERNEL_BOOT,
- POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[section],
- szs[section],
+ POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + sections[section].memory_offset,
+ sections[section].size,
unified_sections[section],
&m);
-
- sections_measured = sections_measured < 0 ? m : (sections_measured && m);
+ combine_measured_flag(sections_measured, m);
}
+}
- /* After we are done, set an EFI variable that tells userspace this was done successfully, and encode
- * in it which PCR was used. */
- if (sections_measured > 0)
- (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrKernelImage", TPM2_PCR_KERNEL_BOOT, 0);
+static void cmdline_append_and_measure_smbios(char16_t **cmdline, int *parameters_measured) {
+ assert(cmdline);
+ assert(parameters_measured);
- /* Show splash screen as early as possible */
- graphics_splash((const uint8_t*) loaded_image->ImageBase + addrs[UNIFIED_SECTION_SPLASH], szs[UNIFIED_SECTION_SPLASH]);
+ /* SMBIOS OEM Strings data is controlled by the host admin and not covered by the VM attestation, so
+ * MUST NOT be trusted when in a confidential VM */
+ if (is_confidential_vm())
+ return;
- if (use_load_options(image, loaded_image, szs[UNIFIED_SECTION_CMDLINE] > 0, &cmdline)) {
- /* Let's measure the passed kernel command line into the TPM. Note that this possibly
- * duplicates what we already did in the boot menu, if that was already used. However, since
- * we want the boot menu to support an EFI binary, and want to this stub to be usable from
- * any boot menu, let's measure things anyway. */
- m = false;
- (void) tpm_log_load_options(cmdline, &m);
- parameters_measured = m;
- } else if (szs[UNIFIED_SECTION_CMDLINE] > 0) {
- cmdline = xstrn8_to_16(
- (char *) loaded_image->ImageBase + addrs[UNIFIED_SECTION_CMDLINE],
- szs[UNIFIED_SECTION_CMDLINE]);
- mangle_stub_cmdline(cmdline);
- }
+ const char *extra = smbios_find_oem_string("io.systemd.stub.kernel-cmdline-extra");
+ if (!extra)
+ return;
- /* If we have any extra command line to add via PE addons, load them now and append, and
- * measure the additions together, after the embedded options, but before the smbios ones,
- * so that the order is reversed from "most hardcoded" to "most dynamic". The global addons are
- * loaded first, and the image-specific ones later, for the same reason. */
- cmdline_append_and_measure_addons(cmdline_addons_global, cmdline_addons_uki, &cmdline, &m);
- parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
-
- /* SMBIOS OEM Strings data is controlled by the host admin and not covered
- * by the VM attestation, so MUST NOT be trusted when in a confidential VM */
- if (!is_confidential_vm()) {
- const char *extra = smbios_find_oem_string("io.systemd.stub.kernel-cmdline-extra");
- if (extra) {
- _cleanup_free_ char16_t *tmp = TAKE_PTR(cmdline), *extra16 = xstr8_to_16(extra);
- cmdline = xasprintf("%ls %ls", tmp, extra16);
-
- /* SMBIOS strings are measured in PCR1, but we also want to measure them in our specific
- * PCR12, as firmware-owned PCRs are very difficult to use as they'll contain unpredictable
- * measurements that are not under control of the machine owner. */
- m = false;
- (void) tpm_log_load_options(extra16, &m);
- parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
- }
+ _cleanup_free_ char16_t *extra16 = mangle_stub_cmdline(xstr8_to_16(extra));
+ if (isempty(extra16))
+ return;
+
+ /* SMBIOS strings are measured in PCR1, but we also want to measure them in our specific PCR12, as
+ * firmware-owned PCRs are very difficult to use as they'll contain unpredictable measurements that
+ * are not under control of the machine owner. */
+ bool m = false;
+ (void) tpm_log_load_options(extra16, &m);
+ combine_measured_flag(parameters_measured, m);
+
+ _cleanup_free_ char16_t *tmp = TAKE_PTR(*cmdline);
+ if (isempty(tmp))
+ *cmdline = TAKE_PTR(extra16);
+ else
+ *cmdline = xasprintf("%ls %ls", tmp, extra16);
+}
+
+static void initrds_free(struct iovec (*initrds)[_INITRD_MAX]) {
+ assert(initrds);
+
+ /* Free the dynamic initrds, but leave the non-dynamic ones around */
+
+ for (size_t i = _INITRD_DYNAMIC_FIRST; i < _INITRD_MAX; i++)
+ iovec_done((*initrds) + i);
+}
+
+static bool initrds_need_combine(struct iovec initrds[static _INITRD_MAX]) {
+ assert(initrds);
+
+ /* Returns true if we have any initrds set that aren't the base initrd. In that case we need to
+ * merge, otherwise we can pass the embedded initrd as is */
+
+ for (size_t i = 0; i < _INITRD_MAX; i++) {
+ if (i == INITRD_BASE)
+ continue;
+
+ if (iovec_is_set(initrds + i))
+ return true;
}
- export_variables(loaded_image);
+ return false;
+}
+
+static void generate_sidecar_initrds(
+ EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
+ struct iovec initrds[static _INITRD_MAX],
+ int *parameters_measured,
+ int *sysext_measured,
+ int *confext_measured) {
+
+ bool m;
+
+ assert(loaded_image);
+ assert(initrds);
+ assert(parameters_measured);
+ assert(sysext_measured);
+ assert(confext_measured);
if (pack_cpio(loaded_image,
/* dropin_dir= */ NULL,
@@ -662,10 +627,9 @@ static EFI_STATUS run(EFI_HANDLE image) {
/* access_mode= */ 0400,
/* tpm_pcr= */ TPM2_PCR_KERNEL_CONFIG,
u"Credentials initrd",
- &credential_initrd,
- &credential_initrd_size,
+ initrds + INITRD_CREDENTIAL,
&m) == EFI_SUCCESS)
- parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
+ combine_measured_flag(parameters_measured, m);
if (pack_cpio(loaded_image,
u"\\loader\\credentials",
@@ -676,10 +640,9 @@ static EFI_STATUS run(EFI_HANDLE image) {
/* access_mode= */ 0400,
/* tpm_pcr= */ TPM2_PCR_KERNEL_CONFIG,
u"Global credentials initrd",
- &global_credential_initrd,
- &global_credential_initrd_size,
+ initrds + INITRD_GLOBAL_CREDENTIAL,
&m) == EFI_SUCCESS)
- parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
+ combine_measured_flag(parameters_measured, m);
if (pack_cpio(loaded_image,
/* dropin_dir= */ NULL,
@@ -690,10 +653,9 @@ static EFI_STATUS run(EFI_HANDLE image) {
/* access_mode= */ 0444,
/* tpm_pcr= */ TPM2_PCR_SYSEXTS,
u"System extension initrd",
- &sysext_initrd,
- &sysext_initrd_size,
+ initrds + INITRD_CONFEXT,
&m) == EFI_SUCCESS)
- sysext_measured = m;
+ combine_measured_flag(sysext_measured, m);
if (pack_cpio(loaded_image,
/* dropin_dir= */ NULL,
@@ -704,137 +666,278 @@ static EFI_STATUS run(EFI_HANDLE image) {
/* access_mode= */ 0444,
/* tpm_pcr= */ TPM2_PCR_KERNEL_CONFIG,
u"Configuration extension initrd",
- &confext_initrd,
- &confext_initrd_size,
+ initrds + INITRD_SYSEXT,
&m) == EFI_SUCCESS)
- confext_measured = m;
-
- dt_size = szs[UNIFIED_SECTION_DTB];
- dt_base = dt_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_DTB] : 0;
-
- /* First load the base device tree, then fix it up using addons - global first, then per-UKI. */
- if (dt_size > 0) {
- err = devicetree_install_from_memory(
- &dt_state, PHYSICAL_ADDRESS_TO_POINTER(dt_base), dt_size);
- if (err != EFI_SUCCESS)
- log_error_status(err, "Error loading embedded devicetree: %m");
- }
+ combine_measured_flag(confext_measured, m);
+}
- dtb_install_addons(&dt_state,
- dt_bases_addons_global,
- dt_sizes_addons_global,
- dt_filenames_addons_global,
- n_dts_addons_global,
- &m);
- parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
- dtb_install_addons(&dt_state,
- dt_bases_addons_uki,
- dt_sizes_addons_uki,
- dt_filenames_addons_uki,
- n_dts_addons_uki,
- &m);
- parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
+static void generate_embedded_initrds(
+ EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
+ PeSectionVector sections[_UNIFIED_SECTION_MAX],
+ struct iovec initrds[static _INITRD_MAX]) {
- if (parameters_measured > 0)
- (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrKernelParameters", TPM2_PCR_KERNEL_CONFIG, 0);
- if (sysext_measured)
- (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrInitRDSysExts", TPM2_PCR_SYSEXTS, 0);
- if (confext_measured)
- (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrInitRDConfExts", TPM2_PCR_KERNEL_CONFIG, 0);
+ assert(loaded_image);
+ assert(initrds);
/* If the PCR signature was embedded in the PE image, then let's wrap it in a cpio and also pass it
* to the kernel, so that it can be read from /.extra/tpm2-pcr-signature.json. Note that this section
* is not measured, neither as raw section (see above), nor as cpio (here), because it is the
* signature of expected PCR values, i.e. its input are PCR measurements, and hence it shouldn't
* itself be input for PCR measurements. */
- if (szs[UNIFIED_SECTION_PCRSIG] > 0)
+ if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_PCRSIG))
(void) pack_cpio_literal(
- (uint8_t*) loaded_image->ImageBase + addrs[UNIFIED_SECTION_PCRSIG],
- szs[UNIFIED_SECTION_PCRSIG],
+ (const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_PCRSIG].memory_offset,
+ sections[UNIFIED_SECTION_PCRSIG].size,
".extra",
u"tpm2-pcr-signature.json",
/* dir_mode= */ 0555,
/* access_mode= */ 0444,
/* tpm_pcr= */ UINT32_MAX,
/* tpm_description= */ NULL,
- &pcrsig_initrd,
- &pcrsig_initrd_size,
+ initrds + INITRD_PCRSIG,
/* ret_measured= */ NULL);
/* If the public key used for the PCR signatures was embedded in the PE image, then let's wrap it in
* a cpio and also pass it to the kernel, so that it can be read from
* /.extra/tpm2-pcr-public-key.pem. This section is already measure above, hence we won't measure the
* cpio. */
- if (szs[UNIFIED_SECTION_PCRPKEY] > 0)
+ if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_PCRPKEY))
(void) pack_cpio_literal(
- (uint8_t*) loaded_image->ImageBase + addrs[UNIFIED_SECTION_PCRPKEY],
- szs[UNIFIED_SECTION_PCRPKEY],
+ (const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_PCRPKEY].memory_offset,
+ sections[UNIFIED_SECTION_PCRPKEY].size,
".extra",
u"tpm2-pcr-public-key.pem",
/* dir_mode= */ 0555,
/* access_mode= */ 0444,
/* tpm_pcr= */ UINT32_MAX,
/* tpm_description= */ NULL,
- &pcrpkey_initrd,
- &pcrpkey_initrd_size,
+ initrds + INITRD_PCRPKEY,
/* ret_measured= */ NULL);
+}
- linux_size = szs[UNIFIED_SECTION_LINUX];
- linux_base = POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_LINUX];
+static void lookup_embedded_initrds(
+ EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
+ PeSectionVector sections[_UNIFIED_SECTION_MAX],
+ struct iovec initrds[static _INITRD_MAX]) {
- initrd_size = szs[UNIFIED_SECTION_INITRD];
- initrd_base = initrd_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_INITRD] : 0;
+ assert(loaded_image);
+ assert(sections);
+ assert(initrds);
+
+ if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_INITRD))
+ initrds[INITRD_BASE] = IOVEC_MAKE(
+ (const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_INITRD].memory_offset,
+ sections[UNIFIED_SECTION_INITRD].size);
+
+ if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_UCODE))
+ initrds[INITRD_UCODE] = IOVEC_MAKE(
+ (const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_UCODE].memory_offset,
+ sections[UNIFIED_SECTION_UCODE].size);
+}
- ucode_size = szs[UNIFIED_SECTION_UCODE];
- ucode_base = ucode_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_UCODE] : 0;
+static void export_pcr_variables(
+ int sections_measured,
+ int parameters_measured,
+ int sysext_measured,
+ int confext_measured) {
+ /* After we are done with measuring, set an EFI variable that tells userspace this was done
+ * successfully, and encode in it which PCR was used. */
+
+ if (sections_measured > 0)
+ (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrKernelImage", TPM2_PCR_KERNEL_BOOT, 0);
+ if (parameters_measured > 0)
+ (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrKernelParameters", TPM2_PCR_KERNEL_CONFIG, 0);
+ if (sysext_measured > 0)
+ (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrInitRDSysExts", TPM2_PCR_SYSEXTS, 0);
+ if (confext_measured > 0)
+ (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrInitRDConfExts", TPM2_PCR_KERNEL_CONFIG, 0);
+}
+
+static void install_embedded_devicetree(
+ EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
+ const PeSectionVector sections[static _UNIFIED_SECTION_MAX],
+ struct devicetree_state *dt_state) {
+
+ EFI_STATUS err;
+
+ assert(loaded_image);
+ assert(sections);
+ assert(dt_state);
+
+ if (!PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTB))
+ return;
+
+ err = devicetree_install_from_memory(
+ dt_state,
+ (const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_DTB].memory_offset,
+ sections[UNIFIED_SECTION_DTB].size);
+ if (err != EFI_SUCCESS)
+ log_error_status(err, "Error loading embedded devicetree, igoring: %m");
+}
+
+static void load_all_addons(
+ EFI_HANDLE image,
+ EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
+ const char *uname,
+ char16_t **cmdline_addons,
+ DevicetreeAddon **dt_addons,
+ size_t *n_dt_addons) {
+
+ EFI_STATUS err;
+
+ assert(loaded_image);
+ assert(cmdline_addons);
+ assert(dt_addons);
+ assert(n_dt_addons);
+
+ err = load_addons(
+ image,
+ loaded_image,
+ u"\\loader\\addons",
+ uname,
+ cmdline_addons,
+ dt_addons,
+ n_dt_addons);
+ if (err != EFI_SUCCESS)
+ log_error_status(err, "Error loading global addons, ignoring: %m");
+
+ /* Some bootloaders always pass NULL in FilePath, so we need to check for it here. */
+ _cleanup_free_ char16_t *dropin_dir = get_extra_dir(loaded_image->FilePath);
+ if (!dropin_dir)
+ return;
+
+ err = load_addons(
+ image,
+ loaded_image,
+ dropin_dir,
+ uname,
+ cmdline_addons,
+ dt_addons,
+ n_dt_addons);
+ if (err != EFI_SUCCESS)
+ log_error_status(err, "Error loading UKI-specific addons, ignoring: %m");
+}
+
+static void display_splash(
+ EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
+ const PeSectionVector sections[static _UNIFIED_SECTION_MAX]) {
+
+ assert(loaded_image);
+ assert(sections);
+
+ if (!PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_SPLASH))
+ return;
+
+ graphics_splash((const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_SPLASH].memory_offset, sections[UNIFIED_SECTION_SPLASH].size);
+}
+
+static void determine_cmdline(
+ EFI_HANDLE image,
+ EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
+ const PeSectionVector sections[static _UNIFIED_SECTION_MAX],
+ char16_t **ret_cmdline,
+ int *parameters_measured) {
+
+ assert(loaded_image);
+ assert(sections);
+
+ if (use_load_options(image, loaded_image, /* have_cmdline= */ PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_CMDLINE), ret_cmdline)) {
+ /* Let's measure the passed kernel command line into the TPM. Note that this possibly
+ * duplicates what we already did in the boot menu, if that was already used. However, since
+ * we want the boot menu to support an EFI binary, and want to this stub to be usable from
+ * any boot menu, let's measure things anyway. */
+ bool m = false;
+ (void) tpm_log_load_options(*ret_cmdline, &m);
+ combine_measured_flag(parameters_measured, m);
+ } else
+ *ret_cmdline = mangle_stub_cmdline(pe_section_to_str16(loaded_image, sections + UNIFIED_SECTION_CMDLINE));
+}
+
+static EFI_STATUS run(EFI_HANDLE image) {
+ int sections_measured = -1, parameters_measured = -1, sysext_measured = -1, confext_measured = -1;
+ _cleanup_(devicetree_cleanup) struct devicetree_state dt_state = {};
+ _cleanup_free_ char16_t *cmdline = NULL, *cmdline_addons = NULL;
+ _cleanup_(initrds_free) struct iovec initrds[_INITRD_MAX] = {};
+ PeSectionVector sections[ELEMENTSOF(unified_sections)] = {};
+ EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
+ _cleanup_free_ char *uname = NULL;
+ DevicetreeAddon *dt_addons = NULL;
+ size_t n_dt_addons = 0;
+ EFI_STATUS err;
+
+ 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 a LoadedImageProtocol handle: %m");
+
+ err = pe_memory_locate_sections(loaded_image->ImageBase, unified_sections, sections);
+ if (err != EFI_SUCCESS)
+ return log_error_status(err, "Unable to locate embedded PE sections: %m");
+ if (!PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_LINUX))
+ return log_error_status(EFI_NOT_FOUND, "Image lacks .linux section.");
+
+ measure_sections(loaded_image, sections, &sections_measured);
+
+ /* Show splash screen as early as possible, but after measuring it */
+ display_splash(loaded_image, sections);
+
+ refresh_random_seed(loaded_image);
+
+ uname = pe_section_to_str8(loaded_image, sections + UNIFIED_SECTION_UNAME);
+
+ determine_cmdline(image, loaded_image, sections, &cmdline, &parameters_measured);
+
+ /* Now that we have the UKI sections loaded, also load global first and then local (per-UKI)
+ * addons. The data is loaded at once, and then used later. */
+ CLEANUP_ARRAY(dt_addons, n_dt_addons, devicetree_addon_free_many);
+ load_all_addons(image, loaded_image, uname, &cmdline_addons, &dt_addons, &n_dt_addons);
+
+ /* If we have any extra command line to add via PE addons, load them now and append, and measure the
+ * additions together, after the embedded options, but before the smbios ones, so that the order is
+ * reversed from "most hardcoded" to "most dynamic". The global addons are loaded first, and the
+ * image-specific ones later, for the same reason. */
+ cmdline_append_and_measure_addons(cmdline_addons, &cmdline, &parameters_measured);
+ cmdline_append_and_measure_smbios(&cmdline, &parameters_measured);
+
+ export_common_variables(loaded_image);
+ export_stub_variables(loaded_image);
+
+ /* First load the base device tree, then fix it up using addons - global first, then per-UKI. */
+ install_embedded_devicetree(loaded_image, sections, &dt_state);
+ install_addon_devicetrees(&dt_state, dt_addons, n_dt_addons, &parameters_measured);
+
+ /* Generate & find all initrds */
+ generate_sidecar_initrds(loaded_image, initrds, &parameters_measured, &sysext_measured, &confext_measured);
+ generate_embedded_initrds(loaded_image, sections, initrds);
+ lookup_embedded_initrds(loaded_image, sections, initrds);
+
+ /* Export variables indicating what we measured */
+ export_pcr_variables(sections_measured, parameters_measured, sysext_measured, confext_measured);
+
+ /* Combine the initrds into one */
_cleanup_pages_ Pages initrd_pages = {};
- if (ucode_base || credential_initrd || global_credential_initrd || sysext_initrd || confext_initrd || pcrsig_initrd || pcrpkey_initrd) {
- /* If we have generated initrds dynamically or there is a microcode initrd, combine them with the built-in initrd. */
- err = combine_initrds(
- (const void*const[]) {
- /* Microcode must always be first as kernel only scans uncompressed cpios
- * and later initrds might be compressed. */
- PHYSICAL_ADDRESS_TO_POINTER(ucode_base),
- PHYSICAL_ADDRESS_TO_POINTER(initrd_base),
- credential_initrd,
- global_credential_initrd,
- sysext_initrd,
- confext_initrd,
- pcrsig_initrd,
- pcrpkey_initrd,
- },
- (const size_t[]) {
- ucode_size,
- initrd_size,
- credential_initrd_size,
- global_credential_initrd_size,
- sysext_initrd_size,
- confext_initrd_size,
- pcrsig_initrd_size,
- pcrpkey_initrd_size,
- },
- 8,
- &initrd_pages, &initrd_size);
+ struct iovec final_initrd;
+ if (initrds_need_combine(initrds)) {
+ /* If we have generated initrds dynamically or there is a microcode initrd, combine them with
+ * the built-in initrd. */
+ err = combine_initrds(initrds, _INITRD_MAX, &initrd_pages, &final_initrd.iov_len);
if (err != EFI_SUCCESS)
return err;
- initrd_base = initrd_pages.addr;
+ final_initrd.iov_base = PHYSICAL_ADDRESS_TO_POINTER(initrd_pages.addr);
- /* Given these might be large let's free them explicitly, quickly. */
- credential_initrd = mfree(credential_initrd);
- global_credential_initrd = mfree(global_credential_initrd);
- sysext_initrd = mfree(sysext_initrd);
- confext_initrd = mfree(confext_initrd);
- pcrsig_initrd = mfree(pcrsig_initrd);
- pcrpkey_initrd = mfree(pcrpkey_initrd);
- }
+ /* Given these might be large let's free them explicitly before we pass control to Linux */
+ initrds_free(&initrds);
+ } else
+ final_initrd = initrds[INITRD_BASE];
+
+ struct iovec kernel = IOVEC_MAKE(
+ (const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_LINUX].memory_offset,
+ sections[UNIFIED_SECTION_LINUX].size);
- err = linux_exec(image, cmdline,
- PHYSICAL_ADDRESS_TO_POINTER(linux_base), linux_size,
- PHYSICAL_ADDRESS_TO_POINTER(initrd_base), initrd_size);
+ err = linux_exec(image, cmdline, &kernel, &final_initrd);
graphics_mode(false);
return err;
}
-DEFINE_EFI_MAIN_FUNCTION(run, "systemd-stub", /*wait_for_debugger=*/false);
+DEFINE_EFI_MAIN_FUNCTION(run, "systemd-stub", /* wait_for_debugger= */ false);
diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c
index b5c8c6306e..eb29eb2d5b 100644
--- a/src/boot/efi/util.c
+++ b/src/boot/efi/util.c
@@ -330,7 +330,14 @@ EFI_STATUS chunked_read(EFI_FILE *file, size_t *size, void *buf) {
return EFI_SUCCESS;
}
-EFI_STATUS file_read(EFI_FILE *dir, const char16_t *name, size_t off, size_t size, char **ret, size_t *ret_size) {
+EFI_STATUS file_read(
+ EFI_FILE *dir,
+ const char16_t *name,
+ uint64_t off,
+ size_t size,
+ char **ret,
+ size_t *ret_size) {
+
_cleanup_(file_closep) EFI_FILE *handle = NULL;
_cleanup_free_ char *buf = NULL;
EFI_STATUS err;
@@ -350,6 +357,9 @@ EFI_STATUS file_read(EFI_FILE *dir, const char16_t *name, size_t off, size_t siz
if (err != EFI_SUCCESS)
return err;
+ if (info->FileSize > SIZE_MAX)
+ return EFI_BAD_BUFFER_SIZE;
+
size = info->FileSize;
}
diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h
index ceac07ca39..dc624f45ae 100644
--- a/src/boot/efi/util.h
+++ b/src/boot/efi/util.h
@@ -102,7 +102,7 @@ char16_t *xstr8_to_path(const char *stra);
char16_t *mangle_stub_cmdline(char16_t *cmdline);
EFI_STATUS chunked_read(EFI_FILE *file, size_t *size, void *buf);
-EFI_STATUS file_read(EFI_FILE *dir, const char16_t *name, size_t off, size_t size, char **content, size_t *content_size);
+EFI_STATUS file_read(EFI_FILE *dir, const char16_t *name, uint64_t off, size_t size, char **content, size_t *content_size);
static inline void file_closep(EFI_FILE **handle) {
if (!*handle)