diff options
Diffstat (limited to 'src/boot')
-rw-r--r-- | src/boot/efi/boot.c | 71 | ||||
-rw-r--r-- | src/boot/efi/cpio.c | 23 | ||||
-rw-r--r-- | src/boot/efi/cpio.h | 7 | ||||
-rw-r--r-- | src/boot/efi/export-vars.c | 44 | ||||
-rw-r--r-- | src/boot/efi/export-vars.h | 5 | ||||
-rw-r--r-- | src/boot/efi/linux.c | 22 | ||||
-rw-r--r-- | src/boot/efi/linux.h | 13 | ||||
-rw-r--r-- | src/boot/efi/linux_x86.c | 40 | ||||
-rw-r--r-- | src/boot/efi/measure.c | 72 | ||||
-rw-r--r-- | src/boot/efi/measure.h | 19 | ||||
-rw-r--r-- | src/boot/efi/meson.build | 1 | ||||
-rw-r--r-- | src/boot/efi/pe.c | 172 | ||||
-rw-r--r-- | src/boot/efi/pe.h | 18 | ||||
-rw-r--r-- | src/boot/efi/stub.c | 929 | ||||
-rw-r--r-- | src/boot/efi/util.c | 12 | ||||
-rw-r--r-- | src/boot/efi/util.h | 2 |
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, §ions_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, ¶meters_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, ¶meters_measured); + cmdline_append_and_measure_smbios(&cmdline, ¶meters_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, ¶meters_measured); + + /* Generate & find all initrds */ + generate_sidecar_initrds(loaded_image, initrds, ¶meters_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) |