diff --git a/man/systemd-stub.xml b/man/systemd-stub.xml index bb0376ed86..9f87f19f4c 100644 --- a/man/systemd-stub.xml +++ b/man/systemd-stub.xml @@ -53,13 +53,14 @@ The UEFI boot stub looks for various resources for the kernel invocation inside the UEFI PE binary itself. This allows combining various resources inside a single PE binary image (usually called "Unified Kernel Image", or "UKI" for short), which may then be signed via UEFI SecureBoot as a whole, covering all - individual resources at once. Specifically it may include: + individual resources at once. Specifically it may include the following PE sections: - A .linux section with the ELF Linux kernel image. + A .linux section with the ELF Linux kernel + image. (Required) An .osrel section with OS release information, i.e. the contents of the os-release5 file @@ -95,21 +96,29 @@ signature data in the .pcrsig section. - If UEFI SecureBoot is enabled and the .cmdline section is present in the executed - image, any attempts to override the kernel command line by passing one as invocation parameters to the - EFI binary are ignored. Thus, in order to allow overriding the kernel command line, either disable UEFI - SecureBoot, or don't include a kernel command line PE section in the kernel image file. If a command line - is accepted via EFI invocation parameters to the EFI binary it is measured into TPM PCR 12 (if a TPM is - present). + Generally, the sections above should appear at most once in a UKI. That said, a concept of + "profiles" is defined, that allows multiple sets of these sections to exist in a single UKI file, of + which one can be selected at boot. For this an additional PE section .profile is + defined which can be used as separator between multiple sets of these settings. The + .profile section itself may contain meta-information about the section, and follows a + similar structure as the contents of the .osrel section. For further details about + multi-profile UKIs, see below. If UEFI SecureBoot is enabled and the + .cmdline section is present in the executed image, any attempts to override the kernel + command line by passing one as invocation parameters to the EFI binary are ignored. Thus, in order to + allow overriding the kernel command line, either disable UEFI SecureBoot, or don't include a kernel + command line PE section in the kernel image file. If a command line is accepted via EFI invocation + parameters to the EFI binary it is measured into TPM PCR 12 (if a TPM is present). If a + DeviceTree is embedded in the .dtb section, it replaces an existing DeviceTree in the + corresponding EFI configuration table. systemd-stub will ask the firmware via the + EFI_DT_FIXUP_PROTOCOL for hardware specific fixups to the DeviceTree. The + contents of 11 of these 12 sections are measured into TPM PCR 11. It is otherwise not used and thus the + result can be pre-calculated without too much effort. The .pcrsig section is not + included in this PCR measurement, since it is supposed to contain signatures for the output of the + measurement operation, and thus cannot also be input to it. If an UKI contains multiple profiles, only + the PE sections of the selected profile (and those of the base profile, except if overriden) are + measured. - If a DeviceTree is embedded in the .dtb section, it replaces an existing - DeviceTree in the corresponding EFI configuration table. systemd-stub will ask the firmware via the - EFI_DT_FIXUP_PROTOCOL for hardware specific fixups to the DeviceTree. - - The contents of eight of these nine sections are measured into TPM PCR 11. It is otherwise not used - and thus the result can be pre-calculated without too much effort. The .pcrsig section - is not included in this PCR measurement, since it is supposed to contain signatures for the output of the - measurement operation, and thus cannot also be input to it. + If non-zero, the selected numeric profile is measured into PCR 12. When .pcrsig and/or .pcrpkey sections are present in a unified kernel image their contents are passed to the booted kernel in an synthetic initrd cpio archive @@ -231,19 +240,124 @@ details); in case of the system extension images by using signed Verity images. + + Multi-Profile UKIs + + In many contexts it is useful to allow invocation of a single UKI in multiple different modes (or + "profiles") without compromising the cryptographic integrity, measurements and so on of the boot + process. For example, a single UKI might provide three distinct profiles: a regular boot one, one that + invokes a "factory reset" operation, and one that boots into a storage target mode (as in + systemd-storagetm.service8). Each + profile would then use the same .linux and .initrd sections, but would + have a separate .cmdline section. For example the latter two profiles would extend the + regular kernel command line with systemd.unit=factory-reset.target or + rd.systemd.unit=storagetm.target. + + A single UKI may support multiple profiles by means of the special .profile PE + section. This section acts as separator between the PE sections of the individual + profiles. .profile PE sections hence may appear multiple times in a single UKI, and + the other PE sections listed above may appear multiple times too, if .profile are + used, but only once before the first .profile section, once between each subsequent + pair, and once after the last appearance of .profile. The sections listed before the + first .profile are considered the "base" profile of the UKI. Each + .profile section then introduces a new profile, which are numbered starting from + zero. The PE sections following each .profile are specific to that profile. When + booting into a specific profile the base section's profiles are used in combination with the specific + profile's sections: if the same section is defined in both, the per-profile section overrides the base + profile's version, otherwise the per-profile sections is used together with the base profile + sections. A UKI that contains no .profile is consider equivalent to one + that just contains a single .profile, as having only a single profile @0. + + Here's a simple example for a multi-profile UKI's sections, inspired by the setup suggested above: + + + Multi-Profile UKI Example + + + + + + + + Section + Profile + + + + + + .linux + Base profile + + + .osrel + + + .cmdline + + + .initrd + + + .profile + Profile @0 + + + .profile + Profile @1 + + + .cmdline + + + .profile + Profile @2 + + + .cmdline + + + +
+ + The section list above would define three profiles. The first four sections make up the base + profile. A .profile section then introduces profile @0. It doesn't override any + sections (or add any) from the base section, hence it is immediately followed by another + .profile section that then introduces section @1. This profile overrides the kernel + command line. Finally, the last two sections define section @2, again overriding the command line. (Note + that in this example the first .cmdline could also moved behind the first + .profile with equivalent effect. To keep things nicely extensible, it's probably a + good idea to keep the generic command line in the base section instead of profile 0, in case later added + profiles might want to reuse it.) + + The profile to boot may be controlled via the UKI's own command line: if the first argument starts + with @, followed by a positive integer number in decimal, it selects the profile to + boot into. If the first argument is not specified like that, the UKI will automatically boot into profile + 0. + + A .profile section may contain meta-information about the profile. It follows a + similar format as .osrel (i.e. an environment-variable-assignment-block-like list of + newline separated strings). Currently two fields are defined: ID= is supposed to carry + a short identifying string that identifies the profile + (e.g. ID=factory-reset). TITLE= should contain a human readable + string that may appear in the boot menu entry for this profile (e.g. TITLE='Factory Reset this + Device'). +
+ TPM PCR Notes Note that when a unified kernel using systemd-stub is invoked the firmware will measure it as a whole to TPM PCR 4, covering all embedded resources, such as the stub code itself, the - core kernel, the embedded initrd and kernel command line (see above for a full list). + core kernel, the embedded initrd and kernel command line (see above for a full list), including all UKI + profiles. Also note that the Linux kernel will measure all initrds it receives into TPM PCR 9. This means - every type of initrd will be measured two or three times: the initrds embedded in the kernel image will be - measured to PCR 4, PCR 9 and PCR 11; the initrd synthesized from credentials (and the one synthesized - from configuration extensions) will be measured to both PCR 9 and PCR 12; the initrd synthesized from - system extensions will be measured to both PCR 4 and PCR 9. Let's summarize the OS resources and the PCRs - they are measured to: + every type of initrd (of the selected UKI profile) will possibly be measured two or three times: the + initrds embedded in the kernel image will be measured to PCR 4, PCR 9 and PCR 11; the initrd synthesized + from credentials (and the one synthesized from configuration extensions) will be measured to both PCR 9 + and PCR 12; the initrd synthesized from system extensions will be measured to both PCR 4 and PCR 9. Let's + summarize the OS resources and the PCRs they are measured to: OS Resource PCR Summary @@ -324,6 +438,11 @@ Configuration Extensions (synthesized initrd from companion files)9 + 12 + + + Selected profile unless zero + 12 +
@@ -422,6 +541,16 @@ + + + StubProfile + + The numeric index of the selected profile, without the @, + formatted as decimal string. Set both on single-profile and multi-profile UKIs. (In the former case + this variable will be set to 0 unconditionally.) + + + Note that some of the variables above may also be set by the boot loader. The stub will only set @@ -500,6 +629,15 @@ + + + /.extra/profile + /.extra/os-release + The contents of the .profile and .osrel + sections of the selected profile, if any. + + + Note that all these files are located in the tmpfs file system the kernel sets diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 7f8671a9cf..0ace97013f 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -38,6 +38,8 @@ enum { INITRD_CONFEXT, INITRD_PCRSIG, INITRD_PCRPKEY, + INITRD_OSREL, + INITRD_PROFILE, _INITRD_MAX, }; @@ -139,7 +141,7 @@ static EFI_STATUS combine_initrds( return EFI_SUCCESS; } -static void export_stub_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) { +static void export_stub_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image, unsigned profile) { 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 */ @@ -150,6 +152,7 @@ static void export_stub_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) { EFI_STUB_FEATURE_CMDLINE_ADDONS | /* We pick up .cmdline addons */ EFI_STUB_FEATURE_CMDLINE_SMBIOS | /* We support extending kernel cmdline from SMBIOS Type #11 */ EFI_STUB_FEATURE_DEVICETREE_ADDONS | /* We pick up .dtb addons */ + EFI_STUB_FEATURE_MULTI_PROFILE_UKI | /* We grok the "@1" profile command line argument */ 0; assert(loaded_image); @@ -159,23 +162,76 @@ static void export_stub_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) { (void) efivar_set_str16(MAKE_GUID_PTR(LOADER), u"StubInfo", u"systemd-stub " GIT_VERSION, 0); (void) efivar_set_uint64_le(MAKE_GUID_PTR(LOADER), u"StubFeatures", stub_features, 0); + + (void) efivar_set_uint64_str16(MAKE_GUID_PTR(LOADER), u"StubProfile", profile, 0); } -static bool use_load_options( +static bool parse_profile_from_cmdline(char16_t **cmdline, unsigned *ret_profile) { + assert(cmdline); + assert(*cmdline); + assert(ret_profile); + + const char16_t *p = *cmdline; + if (p[0] != '@') + goto nothing; + + uint64_t u; + const char16_t *tail; + if (!parse_number16(p + 1, &u, &tail)) + goto nothing; + if (u > UINT_MAX) + goto nothing; + /* Remove exactly one separating space. No further mangling, in order to not disturb measurements – + * and thus making prediction harder –, after all we want that people can safely prefix their command + * lines with a profile without having to be bothered with additional whitespace the command line + * might already contain. */ + if (tail[0] == u' ') + tail++; + else if (tail[0] != 0) /* If this is neither a space nor the end of the string, it must be something else */ + goto nothing; + + /* Drop prefix */ + free_and_xstrdup16(cmdline, tail); + *ret_profile = u; + return true; + +nothing: + *ret_profile = 0; + return false; +} + +static bool parse_profile_from_argument(const char16_t *arg, unsigned *ret_profile) { + assert(arg); + assert(ret_profile); + + if (arg[0] != '@') + goto nothing; + + uint64_t u; + if (!parse_number16(arg + 1, &u, /* ret_tail= */ NULL)) + goto nothing; + + if (u > UINT_MAX) + goto nothing; + + *ret_profile = u; + return true; + +nothing: + *ret_profile = 0; + return false; +} + +static void process_arguments( EFI_HANDLE stub_image, EFI_LOADED_IMAGE_PROTOCOL *loaded_image, - bool have_cmdline, - char16_t **ret) { + unsigned *ret_profile, + char16_t **ret_cmdline) { assert(stub_image); assert(loaded_image); - assert(ret); - - /* We only allow custom command lines if we aren't in secure boot or if no cmdline was baked into - * the stub image. - * We also don't allow it if we are in confidential vms and secureboot is on. */ - if (secure_boot_enabled() && (have_cmdline || is_confidential_vm())) - return false; + assert(ret_profile); + assert(ret_cmdline); /* 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. */ @@ -185,26 +241,40 @@ static bool use_load_options( /* 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; + goto nothing; /* Not running from EFI shell, use entire LoadOptions. Note that LoadOptions is a void*, so - * it could be anything! */ - *ret = mangle_stub_cmdline(xstrndup16(loaded_image->LoadOptions, loaded_image->LoadOptionsSize / sizeof(char16_t))); - return true; + * it could actually be anything! */ + char16_t *c = xstrndup16(loaded_image->LoadOptions, loaded_image->LoadOptionsSize / sizeof(char16_t)); + parse_profile_from_cmdline(&c, ret_profile); + *ret_cmdline = mangle_stub_cmdline(c); + return; } - if (shell->Argc < 2) - /* No arguments were provided? Then we fall back to built-in cmdline. */ - return false; + if (shell->Argc <= 1) /* No arguments were provided? Then we fall back to built-in cmdline. */ + goto nothing; - /* Assemble the command line ourselves without our stub path. */ - *ret = xstrdup16(shell->Argv[1]); - for (size_t i = 2; i < shell->Argc; i++) { - _cleanup_free_ char16_t *old = *ret; - *ret = xasprintf("%ls %ls", old, shell->Argv[i]); - } + size_t i = 1; - return true; + /* The first argument is possibly an "@5" style profile specifier */ + i += parse_profile_from_argument(shell->Argv[i], ret_profile); + + if (i < shell->Argc) { + /* Assemble the command line ourselves without our stub path. */ + *ret_cmdline = xstrdup16(shell->Argv[i++]); + for (; i < shell->Argc; i++) { + _cleanup_free_ char16_t *old = *ret_cmdline; + *ret_cmdline = xasprintf("%ls %ls", old, shell->Argv[i]); + } + } else + *ret_cmdline = NULL; + + return; + +nothing: + *ret_profile = 0; + *ret_cmdline = NULL; + return; } static EFI_STATUS load_addons_from_dir( @@ -734,43 +804,53 @@ static void generate_embedded_initrds( const PeSectionVector sections[static _UNIFIED_SECTION_MAX], struct iovec initrds[static _INITRD_MAX]) { + static const struct { + UnifiedSection section; + size_t initrd_index; + const char16_t *filename; + } table[] = { + /* 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. */ + { UNIFIED_SECTION_PCRSIG, INITRD_PCRSIG, u"tpm2-pcr-signature.json" }, + + /* 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 measured above, hence we won't + * measure the cpio. */ + { UNIFIED_SECTION_PCRPKEY, INITRD_PCRPKEY, u"tpm2-pcr-public-key.pem" }, + + /* If we boot a specific profile, let's place the chosen profile in a file that userspace can + * make use of this information reasonably. */ + { UNIFIED_SECTION_PROFILE, INITRD_PROFILE, u"profile" }, + + /* Similar, pass the .osrel section too. Userspace should have this information anyway, but + * it's so nicely symmetric to the .profile section which we pass around, and who knows, + * maybe this is useful to some. */ + { UNIFIED_SECTION_OSREL, INITRD_OSREL, u"os-release" }, + }; + 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 (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_PCRSIG)) - (void) pack_cpio_literal( - (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, - initrds + INITRD_PCRSIG, - /* ret_measured= */ NULL); + FOREACH_ELEMENT(t, table) { + if (!PE_SECTION_VECTOR_IS_SET(sections + t->section)) + continue; - /* 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 (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_PCRPKEY)) (void) pack_cpio_literal( - (const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_PCRPKEY].memory_offset, - sections[UNIFIED_SECTION_PCRPKEY].size, + (const uint8_t*) loaded_image->ImageBase + sections[t->section].memory_offset, + sections[t->section].size, ".extra", - u"tpm2-pcr-public-key.pem", + t->filename, /* dir_mode= */ 0555, /* access_mode= */ 0444, /* tpm_pcr= */ UINT32_MAX, /* tpm_description= */ NULL, - initrds + INITRD_PCRPKEY, + initrds + t->initrd_index, /* ret_measured= */ NULL); + } } static void lookup_embedded_initrds( @@ -898,26 +978,103 @@ static void display_splash( 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, +static EFI_STATUS find_sections( EFI_LOADED_IMAGE_PROTOCOL *loaded_image, - const PeSectionVector sections[static _UNIFIED_SECTION_MAX], - char16_t **ret_cmdline, - int *parameters_measured) { + unsigned profile, + PeSectionVector sections[static _UNIFIED_SECTION_MAX]) { + + EFI_STATUS err; 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)); + const PeSectionHeader *section_table; + size_t n_section_table; + err = pe_section_table_from_base(loaded_image->ImageBase, §ion_table, &n_section_table); + if (err != EFI_SUCCESS) + return log_error_status(err, "Unable to locate PE section table: %m"); + + /* Get the base sections */ + err = pe_locate_profile_sections( + section_table, + n_section_table, + unified_sections, + /* profile= */ UINT_MAX, + /* validate_base= */ PTR_TO_SIZE(loaded_image->ImageBase), + sections); + if (err != EFI_SUCCESS) + return log_error_status(err, "Unable to locate embedded base PE sections: %m"); + + if (profile != UINT_MAX) { + /* And then override them with the per-profile sections of the selected profile */ + err = pe_locate_profile_sections( + section_table, + n_section_table, + unified_sections, + profile, + /* validate_base= */ PTR_TO_SIZE(loaded_image->ImageBase), + sections); + if (err != EFI_SUCCESS && !(err == EFI_NOT_FOUND && profile == 0)) /* the first profile is implied if it doesn't exist */ + return log_error_status(err, "Unable to locate embedded per-profile PE sections: %m"); + } + + if (!PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_LINUX)) + return log_error_status(EFI_NOT_FOUND, "Image lacks .linux section."); + + return EFI_SUCCESS; +} + +static void settle_command_line( + EFI_LOADED_IMAGE_PROTOCOL *loaded_image, + const PeSectionVector sections[static _UNIFIED_SECTION_MAX], + char16_t **cmdline, + int *parameters_measured) { + + assert(loaded_image); + assert(sections); + assert(cmdline); + + /* This determines which command line to use. On input *cmdline contains the custom passed in cmdline + * if there is any. + * + * We'll suppress the custom cmdline if we are in Secure Boot mode, and if either there is already + * a cmdline baked into the UKI or we are in confidential VM mode. */ + + if (!isempty(*cmdline)) { + if (secure_boot_enabled() && (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_CMDLINE) || is_confidential_vm())) + /* Drop the custom cmdline */ + *cmdline = mfree(*cmdline); + else { + /* 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(*cmdline, &m); + combine_measured_flag(parameters_measured, m); + } + } + + /* No cmdline specified? Or suppressed? Then let's take the one from the UKI, if there is any. */ + if (isempty(*cmdline)) + *cmdline = mangle_stub_cmdline(pe_section_to_str16(loaded_image, sections + UNIFIED_SECTION_CMDLINE)); +} + +static void measure_profile(unsigned profile, int *parameters_measured) { + if (profile == 0) /* don't measure anything about the default profile */ + return; + + _cleanup_free_ char16_t *s = xasprintf("%u", profile); + + bool m = false; + (void) tpm_log_tagged_event( + TPM2_PCR_KERNEL_CONFIG, + POINTER_TO_PHYSICAL_ADDRESS(s), + strsize16(s), + UKI_PROFILE_EVENT_TAG_ID, + s, + &m); + combine_measured_flag(parameters_measured, m); } static EFI_STATUS run(EFI_HANDLE image) { @@ -932,18 +1089,24 @@ static EFI_STATUS run(EFI_HANDLE image) { size_t n_dt_addons = 0, n_ucode_addons = 0; _cleanup_free_ struct iovec *all_initrds = NULL; size_t n_all_initrds = 0; + unsigned profile = 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."); + /* Pick up the arguments passed to us, split out the prefixing profile parameter, and return the rest + * as potential command line to use. */ + (void) process_arguments(image, loaded_image, &profile, &cmdline); + /* Find the sections we want to operate on, both the basic ones, and the one appropriate for the + * selected profile. */ + err = find_sections(loaded_image, profile, sections); + if (err != EFI_SUCCESS) + return err; + + measure_profile(profile, ¶meters_measured); measure_sections(loaded_image, sections, §ions_measured); /* Show splash screen as early as possible, but after measuring it */ @@ -953,7 +1116,8 @@ static EFI_STATUS run(EFI_HANDLE image) { uname = pe_section_to_str8(loaded_image, sections + UNIFIED_SECTION_UNAME); - determine_cmdline(image, loaded_image, sections, &cmdline, ¶meters_measured); + /* Let's now check if we actually want to use the command line, measure it if it was passed in. */ + settle_command_line(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. */ @@ -969,7 +1133,7 @@ static EFI_STATUS run(EFI_HANDLE image) { cmdline_append_and_measure_smbios(&cmdline, ¶meters_measured); export_common_variables(loaded_image); - export_stub_variables(loaded_image); + export_stub_variables(loaded_image, profile); /* 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); diff --git a/src/fundamental/efivars-fundamental.h b/src/fundamental/efivars-fundamental.h index 01b18ec241..10afe38c17 100644 --- a/src/fundamental/efivars-fundamental.h +++ b/src/fundamental/efivars-fundamental.h @@ -34,6 +34,7 @@ #define EFI_STUB_FEATURE_CMDLINE_SMBIOS (UINT64_C(1) << 6) #define EFI_STUB_FEATURE_DEVICETREE_ADDONS (UINT64_C(1) << 7) #define EFI_STUB_FEATURE_PICK_UP_CONFEXTS (UINT64_C(1) << 8) +#define EFI_STUB_FEATURE_MULTI_PROFILE_UKI (UINT64_C(1) << 9) typedef enum SecureBootMode { SECURE_BOOT_UNSUPPORTED, diff --git a/src/fundamental/tpm2-pcr.h b/src/fundamental/tpm2-pcr.h index ff957afcd0..9f5529b827 100644 --- a/src/fundamental/tpm2-pcr.h +++ b/src/fundamental/tpm2-pcr.h @@ -52,3 +52,6 @@ enum { /* The tag used for EV_EVENT_TAG event log records covering ucode addons (effectively initrds) */ #define UCODE_ADDON_EVENT_TAG_ID UINT32_C(0xdac08e1a) + +/* The tag used for EV_EVENT_TAG event log records covering the selected UKI profile */ +#define UKI_PROFILE_EVENT_TAG_ID UINT32_C(0x13aed6db)