From 845707aae23b3129db635604edb95c4048a5922a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 20 Sep 2021 13:47:32 +0200 Subject: [PATCH] stub: when booting a kernel foo.efi then pack foo.efi.extra.d/*.{cred,raw} up as synthetic initrd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds support for the EFI stub to look for credential files and sysext files next to the EFI kernel image being loaded, and pack them up in an initrd cpio image, and pass them to the kernel. Specifically, for a kernel image foo.efi it looks for foo.efi.extra.d/*.cred and packs these files up in an initrd, placing it inside a directory /.extra/credentials/. It then looks for foo.efi.extra.d/*.raw and pack these files up in an initrd, placing them inside a directory /.extra/sysexts/. It then concatenates any other initrd with these two initrds, so they are combined. Or in other words auxiliary files placed next to the kernel image are picked up automatically by the EFI stub and be made available in the initrd in the /.extra/ directory. What's the usecase for this? This is supposed to be useful in context of implementing fully trusted initrds, i.e. initrds that are not built locally on the system and unsigned/unmeasured – as we do things currently —, but instead are built by the vendor, and measured to TPM. The idea is that a basic initrd is always linked into the kernel EFI image anyway. This will already be sufficient for many cases. However, in some cases it is necessary to parameterize initrds, or to extend the basic initrds with additional subsystems (e.g. think complex storage, or passing server info/certificates/… to initrds). The idea is that the parameterization is done using the "credentials" logic we already have in systemd, with these credential files (which can optionally be encrypted+authenticated by TPM2) being placed in the ESP next to the kernel image. And the initrd extension via the "sysext" logic we already have in systemd too. Note that the files read by this code are not verified immediately, they are copied *as-is* and placed into /.extra/ in the initrd. In a trusted environment they need to be validated later, but before first use. For the credentials logic this should be done via the TPM2 encryption/authentication logic. For the sysext stuff the idea is that this is done via signed images, as implemented by #20691. --- src/boot/efi/cpio.c | 466 +++++++++++++++++++++++++++++++++++++++ src/boot/efi/cpio.h | 15 ++ src/boot/efi/meson.build | 2 + src/boot/efi/stub.c | 122 +++++++++- src/boot/efi/util.h | 7 + 5 files changed, 609 insertions(+), 3 deletions(-) create mode 100644 src/boot/efi/cpio.c create mode 100644 src/boot/efi/cpio.h diff --git a/src/boot/efi/cpio.c b/src/boot/efi/cpio.c new file mode 100644 index 00000000000..9c99454d9a8 --- /dev/null +++ b/src/boot/efi/cpio.c @@ -0,0 +1,466 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "cpio.h" +#include "measure.h" +#include "util.h" + +#define EXTRA_DIR_SUFFIX L".extra.d" + +static CHAR8* write_cpio_word(CHAR8 *p, UINT32 v) { + static const char hex[] = "0123456789abcdef"; + + assert(p); + + /* Writes a CPIO header 8 character hex value */ + + for (UINTN i = 0; i < 8; i++) + p[7-i] = hex[(v >> (4 * i)) & 0xF]; + + return p + 8; +} + +static CHAR8* mangle_filename(CHAR8 *p, const CHAR16 *f) { + CHAR8* w; + + assert(p); + assert(f); + + /* Basically converts UTF-16 to plain ASCII (note that we filtered non-ASCII filenames beforehand, so + * this operation is always safe) */ + + for (w = p; *f != 0; f++) { + assert(*f <= 0x7fu); + + *(w++) = *f; + } + + *w = 0; + return w; +} + +static CHAR8* pad4(CHAR8 *p, const CHAR8* start) { + assert(p); + assert(start); + assert(p >= start); + + /* Appends NUL bytes to 'p', until the address is divisable by 4, when taken relative to 'start' */ + + while ((p - start) % 4 != 0) + *(p++) = 0; + + return p; +} + +static EFI_STATUS pack_cpio_one( + const CHAR16 *fname, + const VOID *contents, + UINTN contents_size, + const CHAR8 *target_dir_prefix, + UINT32 access_mode, + UINT32 *inode_counter, + VOID **cpio_buffer, + UINTN *cpio_buffer_size) { + + UINTN l, target_dir_prefix_size, fname_size, q; + CHAR8 *a; + + assert(fname); + assert(contents_size || contents_size == 0); + assert(target_dir_prefix); + assert(inode_counter); + assert(cpio_buffer); + assert(cpio_buffer_size); + + /* Serializes one file in the cpio format understood by the kernel initrd logic. + * + * See: https://www.kernel.org/doc/Documentation/early-userspace/buffer-format.txt */ + + if (contents_size > UINT32_MAX) /* cpio cannot deal with > 32bit file sizes */ + return EFI_LOAD_ERROR; + + if (*inode_counter == UINT32_MAX) /* more than 2^32-1 inodes? yikes. cpio doesn't support that either */ + return EFI_OUT_OF_RESOURCES; + + l = 6 + 13*8 + 1 + 1; /* Fixed CPIO header size, slash separator, and NUL byte after the file name*/ + + target_dir_prefix_size = strlena(target_dir_prefix); + if (l > UINTN_MAX - target_dir_prefix_size) + return EFI_OUT_OF_RESOURCES; + l += target_dir_prefix_size; + + fname_size = StrLen(fname); + if (l > UINTN_MAX - fname_size) + return EFI_OUT_OF_RESOURCES; + l += fname_size; /* append space for file name */ + + /* CPIO can't deal with fnames longer than 2^32-1 */ + if (target_dir_prefix_size + fname_size >= UINT32_MAX) + return EFI_OUT_OF_RESOURCES; + + /* Align the whole header to 4 byte size */ + l = ALIGN_TO(l, 4); + if (l == UINTN_MAX) /* overflow check */ + return EFI_OUT_OF_RESOURCES; + + /* Align the contents to 4 byte size */ + q = ALIGN_TO(contents_size, 4); + if (q == UINTN_MAX) /* overflow check */ + return EFI_OUT_OF_RESOURCES; + + if (l > UINTN_MAX - q) /* overflow check */ + return EFI_OUT_OF_RESOURCES; + l += q; /* Add contents to header */ + + if (*cpio_buffer_size > UINTN_MAX - l) /* overflow check */ + return EFI_OUT_OF_RESOURCES; + a = ReallocatePool(*cpio_buffer, *cpio_buffer_size, *cpio_buffer_size + l); + if (!a) + return EFI_OUT_OF_RESOURCES; + + *cpio_buffer = a; + a = (CHAR8*) *cpio_buffer + *cpio_buffer_size; + + CopyMem(a, "070701", 6); /* magic ID */ + a += 6; + + a = write_cpio_word(a, (*inode_counter)++); /* inode */ + a = write_cpio_word(a, access_mode | 0100000 /* = S_IFREG */); /* mode */ + a = write_cpio_word(a, 0); /* uid */ + a = write_cpio_word(a, 0); /* gid */ + a = write_cpio_word(a, 1); /* nlink */ + + /* Note: we don't make any attempt to propagate the mtime here, for two reasons: it's a mess given + * that FAT usually is assumed to operate with timezoned timestamps, while UNIX does not. More + * importantly though: the modifications times would hamper our goals of providing stable + * measurements for the same boots. After all we extend the initrds we generate here into TPM2 + * PCRs. */ + a = write_cpio_word(a, 0); /* mtime */ + a = write_cpio_word(a, contents_size); /* size */ + a = write_cpio_word(a, 0); /* major(dev) */ + a = write_cpio_word(a, 0); /* minor(dev) */ + a = write_cpio_word(a, 0); /* major(rdev) */ + a = write_cpio_word(a, 0); /* minor(rdev) */ + a = write_cpio_word(a, target_dir_prefix_size + fname_size + 1); /* fname size */ + a = write_cpio_word(a, 0); /* "crc" */ + + CopyMem(a, target_dir_prefix, target_dir_prefix_size); + a += target_dir_prefix_size; + *(a++) = '/'; + a = mangle_filename(a, fname); + + /* Pad to next multiple of 4 */ + a = pad4(a, *cpio_buffer); + + CopyMem(a, contents, contents_size); + a += contents_size; + + /* Pad to next multiple of 4 */ + a = pad4(a, *cpio_buffer); + + assert(a == (CHAR8*) *cpio_buffer + *cpio_buffer_size + l); + *cpio_buffer_size += l; + + return EFI_SUCCESS; +} + +static EFI_STATUS pack_cpio_dir( + const CHAR8 *path, + UINT32 access_mode, + UINT32 *inode_counter, + VOID **cpio_buffer, + UINTN *cpio_buffer_size) { + + UINTN l, path_size; + CHAR8 *a; + + assert(path); + assert(inode_counter); + assert(cpio_buffer); + assert(cpio_buffer_size); + + /* Serializes one directory inode in cpio format. Note that cpio archives must first create the dirs + * they want to place files in. */ + + if (*inode_counter == UINT32_MAX) + return EFI_OUT_OF_RESOURCES; + + l = 6 + 13*8 + 1; /* Fixed CPIO header size, and NUL byte after the file name*/ + + path_size = strlena(path); + if (l > UINTN_MAX - path_size) + return EFI_OUT_OF_RESOURCES; + l += path_size; + + /* Align the whole header to 4 byte size */ + l = ALIGN_TO(l, 4); + if (l == UINTN_MAX) /* overflow check */ + return EFI_OUT_OF_RESOURCES; + + if (*cpio_buffer_size > UINTN_MAX - l) /* overflow check */ + return EFI_OUT_OF_RESOURCES; + a = ReallocatePool(*cpio_buffer, *cpio_buffer_size, *cpio_buffer_size + l); + if (!a) + return EFI_OUT_OF_RESOURCES; + + *cpio_buffer = a; + a = (CHAR8*) *cpio_buffer + *cpio_buffer_size; + + CopyMem(a, "070701", 6); /* magic ID */ + a += 6; + + a = write_cpio_word(a, (*inode_counter)++); /* inode */ + a = write_cpio_word(a, access_mode | 0040000 /* = S_IFDIR */); /* mode */ + a = write_cpio_word(a, 0); /* uid */ + a = write_cpio_word(a, 0); /* gid */ + a = write_cpio_word(a, 1); /* nlink */ + a = write_cpio_word(a, 0); /* mtime */ + a = write_cpio_word(a, 0); /* size */ + a = write_cpio_word(a, 0); /* major(dev) */ + a = write_cpio_word(a, 0); /* minor(dev) */ + a = write_cpio_word(a, 0); /* major(rdev) */ + a = write_cpio_word(a, 0); /* minor(rdev) */ + a = write_cpio_word(a, path_size + 1); /* fname size */ + a = write_cpio_word(a, 0); /* "crc" */ + + CopyMem(a, path, path_size + 1); + a += path_size + 1; + + /* Pad to next multiple of 4 */ + a = pad4(a, *cpio_buffer); + + assert(a == (CHAR8*) *cpio_buffer + *cpio_buffer_size + l); + + *cpio_buffer_size += l; + return EFI_SUCCESS; +} + +static EFI_STATUS pack_cpio_prefix( + const CHAR8 *path, + UINT32 dir_mode, + UINT32 *inode_counter, + VOID **cpio_buffer, + UINTN *cpio_buffer_size) { + + EFI_STATUS err; + + assert(path); + assert(inode_counter); + assert(cpio_buffer); + assert(cpio_buffer_size); + + /* Serializes directory inodes of all prefix paths of the specified path in cpio format. Note that + * (similar to mkdir -p behaviour) all leading paths are created with 0555 access mode, only the + * final dir is created with the specified directory access mode. */ + + for (const CHAR8 *p = path;;) { + const CHAR8 *e; + + e = strchra(p, '/'); + if (!e) + break; + + if (e > p) { + _cleanup_freepool_ CHAR8 *t = NULL; + + t = strndup8(path, e - path); + if (!t) + return EFI_OUT_OF_RESOURCES; + + err = pack_cpio_dir(t, 0555, inode_counter, cpio_buffer, cpio_buffer_size); + if (EFI_ERROR(err)) + return err; + } + + p = e + 1; + } + + return pack_cpio_dir(path, dir_mode, inode_counter, cpio_buffer, cpio_buffer_size); +} + +static EFI_STATUS pack_cpio_trailer( + VOID **cpio_buffer, + UINTN *cpio_buffer_size) { + + static const char trailer[] = + "070701" + "00000000" + "00000000" + "00000000" + "00000000" + "00000001" + "00000000" + "00000000" + "00000000" + "00000000" + "00000000" + "00000000" + "0000000B" + "00000000" + "TRAILER!!!\0\0\0"; /* There's a fourth NUL byte appended here, because this is a string */ + + VOID *a; + + /* Generates the cpio trailer record that indicates the end of our initrd cpio archive */ + + assert(cpio_buffer); + assert(cpio_buffer_size); + assert_cc(sizeof(trailer) % 4 == 0); + + a = ReallocatePool(*cpio_buffer, *cpio_buffer_size, *cpio_buffer_size + sizeof(trailer)); + if (!a) + return EFI_OUT_OF_RESOURCES; + + *cpio_buffer = a; + CopyMem((UINT8*) *cpio_buffer + *cpio_buffer_size, trailer, sizeof(trailer)); + *cpio_buffer_size += sizeof(trailer); + + return EFI_SUCCESS; +} + +EFI_STATUS pack_cpio( + EFI_LOADED_IMAGE *loaded_image, + const CHAR16 *match_suffix, + const CHAR8 *target_dir_prefix, + UINT32 dir_mode, + UINT32 access_mode, + UINTN tpm_pcr, + const CHAR16 *tpm_description, + VOID **ret_buffer, + UINTN *ret_buffer_size) { + + _cleanup_(FileHandleClosep) EFI_FILE_HANDLE root = NULL, extra_dir = NULL; + UINTN dirent_size = 0, buffer_size = 0, n_items = 0, n_allocated = 0; + _cleanup_freepool_ CHAR16 *loaded_image_path = NULL, *j = NULL; + _cleanup_freepool_ EFI_FILE_INFO *dirent = NULL; + _cleanup_(strv_freep) CHAR16 **items = NULL; + _cleanup_freepool_ VOID *buffer = NULL; + UINT32 inode = 1; /* inode counter, so that each item gets a new inode */ + EFI_STATUS err; + + assert(loaded_image); + assert(target_dir_prefix); + assert(ret_buffer); + assert(ret_buffer_size); + + root = LibOpenRoot(loaded_image->DeviceHandle); + if (!root) + return log_error_status_stall(EFI_LOAD_ERROR, L"Unable to open root directory."); + + loaded_image_path = DevicePathToStr(loaded_image->FilePath); + if (!loaded_image_path) + return log_oom(); + + j = PoolPrint(L"%s" EXTRA_DIR_SUFFIX, loaded_image_path); + if (!j) + return log_oom(); + + err = open_directory(root, j, &extra_dir); + if (err == EFI_NOT_FOUND) { + /* No extra subdir, that's totally OK */ + *ret_buffer = NULL; + *ret_buffer_size = 0; + return EFI_SUCCESS; + } + if (EFI_ERROR(err)) + return log_error_status_stall(err, L"Failed to open extra directory of loaded image: %r", err); + + for (;;) { + _cleanup_freepool_ CHAR16 *d = NULL; + + err = readdir_harder(extra_dir, &dirent, &dirent_size); + if (EFI_ERROR(err)) + return log_error_status_stall(err, L"Failed to read extra directory of loaded image: %r", err); + if (!dirent) /* End of directory */ + break; + + if (dirent->FileName[0] == '.') + continue; + if (dirent->Attribute & EFI_FILE_DIRECTORY) + continue; + if (match_suffix && !endswith_no_case(dirent->FileName, match_suffix)) + continue; + if (!is_ascii(dirent->FileName)) + continue; + if (StrLen(dirent->FileName) > 255) /* Max filename size on Linux */ + continue; + + d = StrDuplicate(dirent->FileName); + if (!d) + return log_oom(); + + if (n_items+2 > n_allocated) { + UINTN m; + + /* We allocate 16 entries at a time, as a matter of optimization */ + if (n_items > (UINTN_MAX / sizeof(UINT16)) - 16) /* Overflow check, just in case */ + return log_oom(); + + m = n_items + 16; + items = ReallocatePool(items, n_allocated * sizeof(UINT16*), m * sizeof(UINT16*)); + if (!items) + return log_oom(); + + n_allocated = m; + } + + items[n_items++] = TAKE_PTR(d); + items[n_items] = NULL; /* Let's always NUL terminate, to make freeing via strv_free() easy */ + } + + if (n_items == 0) { + /* Empty directory */ + *ret_buffer = NULL; + *ret_buffer_size = 0; + return EFI_SUCCESS; + } + + /* Now, sort the files we found, to make this uniform and stable (and to ensure the TPM measurements + * are not dependent on read order) */ + sort_pointer_array((VOID**) items, n_items, (compare_pointer_func_t) StrCmp); + + /* 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. */ + err = pack_cpio_prefix(target_dir_prefix, dir_mode, &inode, &buffer, &buffer_size); + if (EFI_ERROR(err)) + return log_error_status_stall(err, L"Failed to pack cpio prefix: %r", err); + + for (UINTN i = 0; i < n_items; i++) { + _cleanup_freepool_ CHAR8 *content = NULL; + UINTN contentsize; + + err = file_read(extra_dir, items[i], 0, 0, &content, &contentsize); + if (EFI_ERROR(err)) { + log_error_status_stall(err, L"Failed to read %s, ignoring: %r", items[i], err); + continue; + } + + err = pack_cpio_one( + items[i], + content, contentsize, + target_dir_prefix, + access_mode, + &inode, + &buffer, &buffer_size); + if (EFI_ERROR(err)) + return log_error_status_stall(err, L"Failed to pack cpio file %s: %r", dirent->FileName, err); + } + + err = pack_cpio_trailer(&buffer, &buffer_size); + if (EFI_ERROR(err)) + return log_error_status_stall(err, L"Failed to pack cpio trailer: %r"); + +#if ENABLE_TPM + err = tpm_log_event( + tpm_pcr, + (EFI_PHYSICAL_ADDRESS) (UINTN) buffer, + buffer_size, + tpm_description); + if (EFI_ERROR(err)) + log_error_stall(L"Unable to add initrd TPM measurement for PCR %u (%s), ignoring: %r", tpm_pcr, tpm_description, err); +#endif + + *ret_buffer = TAKE_PTR(buffer); + *ret_buffer_size = buffer_size; + + return EFI_SUCCESS; +} diff --git a/src/boot/efi/cpio.h b/src/boot/efi/cpio.h new file mode 100644 index 00000000000..8a6d0931220 --- /dev/null +++ b/src/boot/efi/cpio.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include + +EFI_STATUS pack_cpio( + EFI_LOADED_IMAGE *loaded_image, + const CHAR16 *match_suffix, + const CHAR8 *target_dir_prefix, + UINT32 dir_mode, + UINT32 access_mode, + UINTN pcr, + const CHAR16 *tpm_description, + VOID **ret_buffer, + UINTN *ret_buffer_size); diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build index a90f04e7662..3ae1c2f180b 100644 --- a/src/boot/efi/meson.build +++ b/src/boot/efi/meson.build @@ -2,6 +2,7 @@ efi_headers = files(''' console.h + cpio.h devicetree.h disk.h graphics.h @@ -39,6 +40,7 @@ stub_sources = ''' linux.c splash.c stub.c + cpio.c '''.split() if conf.get('ENABLE_EFI') == 1 and get_option('gnu-efi') != 'false' diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 9a3c9982d9b..ae06599005b 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -3,6 +3,7 @@ #include #include +#include "cpio.h" #include "disk.h" #include "graphics.h" #include "linux.h" @@ -15,8 +16,80 @@ /* magic string to find in the binary image */ static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-stub " GIT_VERSION " ####"; +static EFI_STATUS combine_initrd( + EFI_PHYSICAL_ADDRESS initrd_base, UINTN initrd_size, + const VOID *credential_initrd, UINTN credential_initrd_size, + const VOID *sysext_initrd, UINTN sysext_initrd_size, + EFI_PHYSICAL_ADDRESS *ret_initrd_base, UINTN *ret_initrd_size) { + + EFI_PHYSICAL_ADDRESS base = UINT32_MAX; /* allocate an area below the 32bit boundary for this */ + EFI_STATUS err; + UINT8 *p; + UINTN n; + + assert(ret_initrd_base); + assert(ret_initrd_size); + + /* Combines three initrds into one, by simple concatenation in memory */ + + n = ALIGN_TO(initrd_size, 4); /* main initrd might not be padded yet */ + if (credential_initrd) { + if (n > UINTN_MAX - credential_initrd_size) + return EFI_OUT_OF_RESOURCES; + + n += credential_initrd_size; + } + if (sysext_initrd) { + if (n > UINTN_MAX - sysext_initrd_size) + return EFI_OUT_OF_RESOURCES; + + n += sysext_initrd_size; + } + + err = uefi_call_wrapper( + BS->AllocatePages, 4, + AllocateMaxAddress, + EfiLoaderData, + EFI_SIZE_TO_PAGES(n), + &base); + if (EFI_ERROR(err)) + return log_error_status_stall(err, L"Failed to allocate space for combined initrd: %r", err); + + p = (UINT8*) (UINTN) base; + if (initrd_base != 0) { + UINTN pad; + + /* Order matters, the real initrd must come first, since it might include microcode updates + * which the kernel only looks for in the first cpio archive */ + CopyMem(p, (VOID*) (UINTN) initrd_base, initrd_size); + p += initrd_size; + + pad = ALIGN_TO(initrd_size, 4) - initrd_size; + if (pad > 0) { + ZeroMem(p, pad); + p += pad; + } + } + + if (credential_initrd) { + CopyMem(p, credential_initrd, credential_initrd_size); + p += credential_initrd_size; + } + + if (sysext_initrd) { + CopyMem(p, sysext_initrd, sysext_initrd_size); + p += sysext_initrd_size; + } + + assert((UINT8*) (UINTN) base + n == p); + + *ret_initrd_base = base; + *ret_initrd_size = n; + + return EFI_SUCCESS; +} + EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { - EFI_LOADED_IMAGE *loaded_image; enum { SECTION_CMDLINE, @@ -34,8 +107,10 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { NULL, }; + UINTN cmdline_len = 0, initrd_size, credential_initrd_size = 0, sysext_initrd_size = 0; + _cleanup_freepool_ VOID *credential_initrd = NULL, *sysext_initrd = NULL; EFI_PHYSICAL_ADDRESS linux_base, initrd_base; - UINTN cmdline_len = 0, initrd_size; + EFI_LOADED_IMAGE *loaded_image; UINTN addrs[_SECTION_MAX] = {}; UINTN szs[_SECTION_MAX] = {}; CHAR8 *cmdline = NULL; @@ -123,13 +198,54 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { if (szs[SECTION_SPLASH] > 0) graphics_splash((UINT8*) (UINTN) loaded_image->ImageBase + addrs[SECTION_SPLASH], szs[SECTION_SPLASH], NULL); + (VOID) pack_cpio(loaded_image, + L".cred", + (const CHAR8*) ".extra/credentials", + /* dir_mode= */ 0500, + /* access_mode= */ 0400, + /* tpm_pcr= */ TPM_PCR_INDEX_KERNEL_PARAMETERS, + L"Credentials initrd", + &credential_initrd, + &credential_initrd_size); + + (VOID) pack_cpio(loaded_image, + L".raw", + (const CHAR8*) ".extra/sysext", + /* dir_mode= */ 0555, + /* access_mode= */ 0444, + /* tpm_pcr= */ TPM_PCR_INDEX_INITRD, + L"System extension initrd", + &sysext_initrd, + &sysext_initrd_size); + linux_base = (EFI_PHYSICAL_ADDRESS) (UINTN) loaded_image->ImageBase + addrs[SECTION_LINUX]; initrd_size = szs[SECTION_INITRD]; initrd_base = initrd_size != 0 ? (EFI_PHYSICAL_ADDRESS) (UINTN) loaded_image->ImageBase + addrs[SECTION_INITRD] : 0; - err = linux_exec(image, cmdline, cmdline_len, linux_base, initrd_base, initrd_size); + if (credential_initrd || sysext_initrd) { + /* If we have generated initrds dynamically, let's combine them with the built-in initrd. */ + err = combine_initrd( + initrd_base, initrd_size, + credential_initrd, credential_initrd_size, + sysext_initrd, sysext_initrd_size, + &initrd_base, &initrd_size); + if (EFI_ERROR(err)) + return err; + /* Given these might be large let's free them explicitly, quickly. */ + if (credential_initrd) { + FreePool(credential_initrd); + credential_initrd = NULL; + } + + if (sysext_initrd) { + FreePool(sysext_initrd); + sysext_initrd = NULL; + } + } + + err = linux_exec(image, cmdline, cmdline_len, linux_base, initrd_base, initrd_size); graphics_mode(FALSE); return log_error_status_stall(err, L"Execution of embedded linux image failed: %r", err); } diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h index ea32a76166a..3a29ff2d7b2 100644 --- a/src/boot/efi/util.h +++ b/src/boot/efi/util.h @@ -6,6 +6,13 @@ #include "string-util-fundamental.h" +/* This TPM PCR is where most Linux infrastructure extends the kernel command line into, and so do we. We also extend + * any passed credentials here. */ +#define TPM_PCR_INDEX_KERNEL_PARAMETERS 8 + +/* This TPM PCR is where most Linux infrastructure extends the initrd binary images into, and so do we. */ +#define TPM_PCR_INDEX_INITRD 4 + #define OFFSETOF(x,y) __builtin_offsetof(x,y) #define UINTN_MAX (~(UINTN)0)