From 88657f757517bd0d5870c97acb1b88b59267ab1a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 17 Sep 2021 10:48:46 +0200 Subject: [PATCH 01/24] boot: add a way to indicate overflow in ALIGN_TO() --- src/boot/efi/util.h | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h index a5b6435f1b..2ee4f2b518 100644 --- a/src/boot/efi/util.h +++ b/src/boot/efi/util.h @@ -8,8 +8,20 @@ #define OFFSETOF(x,y) __builtin_offsetof(x,y) +#define UINTN_MAX (~(UINTN)0) +#define INTN_MAX ((INTN)(UINTN_MAX>>1)) +#ifndef UINT32_MAX +#define UINT32_MAX ((UINT32) -1) +#endif +#ifndef UINT64_MAX +#define UINT64_MAX ((UINT64) -1) +#endif + static inline UINTN ALIGN_TO(UINTN l, UINTN ali) { - return ((l + ali - 1) & ~(ali - 1)); + if (l > UINTN_MAX - (ali - 1)) /* Overflow? */ + return UINTN_MAX; + + return ((l + (ali - 1)) & ~(ali - 1)); } EFI_STATUS parse_boolean(const CHAR8 *v, BOOLEAN *b); @@ -65,15 +77,6 @@ static inline void FileHandleClosep(EFI_FILE_HANDLE *handle) { &(const EFI_GUID) { 0x4a67b082, 0x0a4c, 0x41cf, { 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f } } #define EFI_GLOBAL_GUID &(const EFI_GUID) EFI_GLOBAL_VARIABLE -#define UINTN_MAX (~(UINTN)0) -#define INTN_MAX ((INTN)(UINTN_MAX>>1)) -#ifndef UINT32_MAX -#define UINT32_MAX ((UINT32) -1) -#endif -#ifndef UINT64_MAX -#define UINT64_MAX ((UINT64) -1) -#endif - VOID log_error_stall(const CHAR16 *fmt, ...); EFI_STATUS log_oom(void); From c6dfe36044879b77fff49dd235ae168a4ab60024 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 20 Sep 2021 13:47:44 +0200 Subject: [PATCH 02/24] boot: add a bunch of new helper calls --- src/boot/efi/util.c | 194 +++++++++++++++++++++++++++++++++++++++++++- src/boot/efi/util.h | 17 ++++ 2 files changed, 207 insertions(+), 4 deletions(-) diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c index 065e1ea396..68053c2c39 100644 --- a/src/boot/efi/util.c +++ b/src/boot/efi/util.c @@ -428,11 +428,14 @@ CHAR16 *stra_to_path(const CHAR8 *stra) { } CHAR8 *strchra(const CHAR8 *s, CHAR8 c) { - assert(s); + if (!s) + return NULL; + do { if (*s == c) return (CHAR8*) s; } while (*s++); + return NULL; } @@ -451,9 +454,9 @@ EFI_STATUS file_read(EFI_FILE_HANDLE dir, const CHAR16 *name, UINTN off, UINTN s if (size == 0) { _cleanup_freepool_ EFI_FILE_INFO *info = NULL; - info = LibFileInfo(handle); - if (!info) - return EFI_OUT_OF_RESOURCES; + err = get_file_info_harder(handle, &info, NULL); + if (EFI_ERROR(err)) + return err; size = info->FileSize+1; } @@ -527,3 +530,186 @@ VOID clear_screen(UINTN attr) { uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, attr); uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); } + +EFI_STATUS get_file_info_harder( + EFI_FILE_HANDLE handle, + EFI_FILE_INFO **ret, + UINTN *ret_size) { + + static const EFI_GUID EfiFileInfoGuid = EFI_FILE_INFO_ID; + UINTN size = OFFSETOF(EFI_FILE_INFO, FileName) + 256; + _cleanup_freepool_ EFI_FILE_INFO *fi = NULL; + EFI_STATUS err; + + assert(handle); + assert(ret); + + /* A lot like LibFileInfo() but with useful error propagation */ + + fi = AllocatePool(size); + if (!fi) + return EFI_OUT_OF_RESOURCES; + + err = uefi_call_wrapper(handle->GetInfo, 4, handle, (EFI_GUID*) &EfiFileInfoGuid, &size, fi); + if (err == EFI_BUFFER_TOO_SMALL) { + FreePool(fi); + fi = AllocatePool(size); /* GetInfo tells us the required size, let's use that now */ + if (!fi) + return EFI_OUT_OF_RESOURCES; + + err = uefi_call_wrapper(handle->GetInfo, 4, handle, (EFI_GUID*) &EfiFileInfoGuid, &size, fi); + } + + if (EFI_ERROR(err)) + return err; + + *ret = TAKE_PTR(fi); + + if (ret_size) + *ret_size = size; + + return EFI_SUCCESS; +} + +EFI_STATUS readdir_harder( + EFI_FILE_HANDLE handle, + EFI_FILE_INFO **buffer, + UINTN *buffer_size) { + + EFI_STATUS err; + UINTN sz; + + assert(handle); + assert(buffer); + assert(buffer_size); + + /* buffer/buffer_size are both in and output parameters. Should be zero-initialized initially, and + * the specified buffer needs to be freed by caller, after final use. */ + + if (!*buffer) { + sz = OFFSETOF(EFI_FILE_INFO, FileName) /* + 256 */; + + *buffer = AllocatePool(sz); + if (!*buffer) + return EFI_OUT_OF_RESOURCES; + + *buffer_size = sz; + } else + sz = *buffer_size; + + err = uefi_call_wrapper(handle->Read, 3, handle, &sz, *buffer); + if (err == EFI_BUFFER_TOO_SMALL) { + FreePool(*buffer); + + *buffer = AllocatePool(sz); + if (!*buffer) { + *buffer_size = 0; + return EFI_OUT_OF_RESOURCES; + } + + *buffer_size = sz; + + err = uefi_call_wrapper(handle->Read, 3, handle, &sz, *buffer); + } + if (EFI_ERROR(err)) + return err; + + if (sz == 0) { + /* End of directory */ + FreePool(*buffer); + *buffer = NULL; + *buffer_size = 0; + } + + return EFI_SUCCESS; +} + +UINTN strnlena(const CHAR8 *p, UINTN maxlen) { + UINTN c; + + if (!p) + return 0; + + for (c = 0; c < maxlen; c++) + if (p[c] == 0) + break; + + return c; +} + +CHAR8 *strndup8(const CHAR8 *p, UINTN sz) { + CHAR8 *n; + + /* Following efilib's naming scheme this function would be called strndupa(), but we already have a + * function named like this in userspace, and it does something different there, hence to minimize + * confusion, let's pick a different name here */ + + assert(p || sz == 0); + + sz = strnlena(p, sz); + + n = AllocatePool(sz + 1); + if (!n) + return NULL; + + if (sz > 0) + CopyMem(n, p, sz); + n[sz] = 0; + + return n; +} + +BOOLEAN is_ascii(const CHAR16 *f) { + if (!f) + return FALSE; + + for (; *f != 0; f++) + if (*f > 127) + return FALSE; + + return TRUE; +} + +CHAR16 **strv_free(CHAR16 **v) { + if (!v) + return NULL; + + for (CHAR16 **i = v; *i; i++) + FreePool(*i); + + FreePool(v); + return NULL; +} + +EFI_STATUS open_directory( + EFI_FILE_HANDLE root, + const CHAR16 *path, + EFI_FILE_HANDLE *ret) { + + _cleanup_(FileHandleClosep) EFI_FILE_HANDLE dir = NULL; + _cleanup_freepool_ EFI_FILE_INFO *file_info = NULL; + EFI_STATUS err; + + assert(root); + + /* Opens a file, and then verifies it is actually a directory */ + + err = uefi_call_wrapper( + root->Open, 5, + root, + &dir, + (CHAR16*) path, + EFI_FILE_MODE_READ, + 0ULL); + if (EFI_ERROR(err)) + return err; + + err = get_file_info_harder(dir, &file_info, NULL); + if (EFI_ERROR(err)) + return err; + if (!(file_info->Attribute & EFI_FILE_DIRECTORY)) + return EFI_LOAD_ERROR; + + *ret = TAKE_PTR(dir); + return EFI_SUCCESS; +} diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h index 2ee4f2b518..45e6b940cb 100644 --- a/src/boot/efi/util.h +++ b/src/boot/efi/util.h @@ -97,3 +97,20 @@ static inline VOID *mempmem_safe(const VOID *haystack, UINTN haystack_len, const VOID print_at(UINTN x, UINTN y, UINTN attr, const CHAR16 *str); VOID clear_screen(UINTN attr); + +EFI_STATUS get_file_info_harder(EFI_FILE_HANDLE handle, EFI_FILE_INFO **ret, UINTN *ret_size); + +EFI_STATUS readdir_harder(EFI_FILE_HANDLE handle, EFI_FILE_INFO **buffer, UINTN *buffer_size); + +UINTN strnlena(const CHAR8 *p, UINTN maxlen); +CHAR8 *strndup8(const CHAR8 *p, UINTN sz); + +BOOLEAN is_ascii(const CHAR16 *f); + +CHAR16 **strv_free(CHAR16 **l); + +static inline void strv_freep(CHAR16 ***p) { + strv_free(*p); +} + +EFI_STATUS open_directory(EFI_FILE_HANDLE root_dir, const CHAR16 *path, EFI_FILE_HANDLE *ret); From 80b2f4d92ce4d7a52cbfb9b5efdcff96366fc37a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 20 Sep 2021 14:07:42 +0200 Subject: [PATCH 03/24] boot: generalize sorting code Let's make this generic, so that we can reuse it elsewhere later. --- src/boot/efi/boot.c | 21 ++------------------- src/boot/efi/util.c | 30 ++++++++++++++++++++++++++++++ src/boot/efi/util.h | 3 +++ 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 074a856598..734ae56fb1 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -1529,7 +1529,7 @@ static VOID config_load_entries( } } -static INTN config_entry_compare(ConfigEntry *a, ConfigEntry *b) { +static INTN config_entry_compare(const ConfigEntry *a, const ConfigEntry *b) { INTN r; assert(a); @@ -1567,24 +1567,7 @@ static INTN config_entry_compare(ConfigEntry *a, ConfigEntry *b) { static VOID config_sort_entries(Config *config) { assert(config); - for (UINTN i = 1; i < config->entry_count; i++) { - BOOLEAN more; - - more = FALSE; - for (UINTN k = 0; k < config->entry_count - i; k++) { - ConfigEntry *entry; - - if (config_entry_compare(config->entries[k], config->entries[k+1]) <= 0) - continue; - - entry = config->entries[k]; - config->entries[k] = config->entries[k+1]; - config->entries[k+1] = entry; - more = TRUE; - } - if (!more) - break; - } + sort_pointer_array((void**) config->entries, config->entry_count, (compare_pointer_func_t) config_entry_compare); } static INTN config_entry_find(Config *config, CHAR16 *id) { diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c index 68053c2c39..e500069d51 100644 --- a/src/boot/efi/util.c +++ b/src/boot/efi/util.c @@ -531,6 +531,36 @@ VOID clear_screen(UINTN attr) { uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); } +void sort_pointer_array( + VOID **array, + UINTN n_members, + compare_pointer_func_t compare) { + + assert(array || n_members == 0); + assert(compare); + + if (n_members <= 1) + return; + + for (UINTN i = 1; i < n_members; i++) { + BOOLEAN more = FALSE; + + for (UINTN k = 0; k < n_members - i; k++) { + void *entry; + + if (compare(array[k], array[k+1]) <= 0) + continue; + + entry = array[k]; + array[k] = array[k+1]; + array[k+1] = entry; + more = TRUE; + } + if (!more) + break; + } +} + EFI_STATUS get_file_info_harder( EFI_FILE_HANDLE handle, EFI_FILE_INFO **ret, diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h index 45e6b940cb..ea32a76166 100644 --- a/src/boot/efi/util.h +++ b/src/boot/efi/util.h @@ -98,6 +98,9 @@ static inline VOID *mempmem_safe(const VOID *haystack, UINTN haystack_len, const VOID print_at(UINTN x, UINTN y, UINTN attr, const CHAR16 *str); VOID clear_screen(UINTN attr); +typedef INTN (*compare_pointer_func_t)(const VOID *a, const VOID *b); +void sort_pointer_array(VOID **array, UINTN n_members, compare_pointer_func_t compare); + EFI_STATUS get_file_info_harder(EFI_FILE_HANDLE handle, EFI_FILE_INFO **ret, UINTN *ret_size); EFI_STATUS readdir_harder(EFI_FILE_HANDLE handle, EFI_FILE_INFO **buffer, UINTN *buffer_size); From 845707aae23b3129db635604edb95c4048a5922a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 20 Sep 2021 13:47:32 +0200 Subject: [PATCH 04/24] 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 0000000000..9c99454d9a --- /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 0000000000..8a6d093122 --- /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 a90f04e766..3ae1c2f180 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 9a3c9982d9..ae06599005 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 ea32a76166..3a29ff2d7b 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) From faacf1807e8fcbee4bf60495e0145e4522d76393 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 20 Sep 2021 15:10:33 +0200 Subject: [PATCH 05/24] boot: stop making TPM PCR to measure kernel command line into configurable Everyone appears to use PCR 8 for this, hence I think it's safe to hardcode that in systemd too. It's also documented, like here: https://www.gnu.org/software/grub/manual/grub/html_node/Measured-Boot.html or here: https://github.com/rhboot/shim/blob/main/README.tpm (And the previous name was a bit confusing, since we don't actually just measure one thing anymore, but mutliple things into multiple PCRs...) --- meson.build | 2 -- meson_options.txt | 2 -- src/boot/efi/boot.c | 2 +- src/boot/efi/meson.build | 1 - src/boot/efi/stub.c | 2 +- 5 files changed, 2 insertions(+), 7 deletions(-) diff --git a/meson.build b/meson.build index 30609f8515..1447f55521 100644 --- a/meson.build +++ b/meson.build @@ -1636,8 +1636,6 @@ if get_option('efi') have = true conf.set_quoted('EFI_MACHINE_TYPE_NAME', EFI_MACHINE_TYPE_NAME) - - conf.set('SD_TPM_PCR', get_option('tpm-pcrindex')) else have = false endif diff --git a/meson_options.txt b/meson_options.txt index b122f275da..e094d48c6e 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -416,8 +416,6 @@ option('efi-libdir', type : 'string', description : 'path to the EFI lib directory') option('efi-includedir', type : 'string', value : '/usr/include/efi', description : 'path to the EFI header directory') -option('tpm-pcrindex', type : 'integer', value : 8, - description : 'TPM PCR register number to use') option('sbat-distro', type : 'string', description : 'SBAT distribution ID, e.g. fedora, or auto for autodetection') option('sbat-distro-generation', type : 'integer', value : 1, diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 734ae56fb1..e95fe870fe 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -2349,7 +2349,7 @@ static EFI_STATUS image_start( #if ENABLE_TPM /* Try to log any options to the TPM, especially to catch manually edited options */ - err = tpm_log_event(SD_TPM_PCR, + err = tpm_log_event(TPM_PCR_INDEX_KERNEL_PARAMETERS, (EFI_PHYSICAL_ADDRESS) (UINTN) loaded_image->LoadOptions, loaded_image->LoadOptionsSize, loaded_image->LoadOptions); if (EFI_ERROR(err)) diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build index 3ae1c2f180..3fdabb2073 100644 --- a/src/boot/efi/meson.build +++ b/src/boot/efi/meson.build @@ -103,7 +103,6 @@ if have_gnu_efi efi_conf = configuration_data() efi_conf.set_quoted('EFI_MACHINE_TYPE_NAME', EFI_MACHINE_TYPE_NAME) efi_conf.set10('ENABLE_TPM', get_option('tpm')) - efi_conf.set('SD_TPM_PCR', get_option('tpm-pcrindex')) foreach ctype : ['color-normal', 'color-entry', 'color-highlight', 'color-edit'] c = get_option('efi-' + ctype).split(',') diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index ae06599005..c7232332ef 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -148,7 +148,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { #if ENABLE_TPM /* Try to log any options to the TPM, especially manually edited options */ - err = tpm_log_event(SD_TPM_PCR, + err = tpm_log_event(TPM_PCR_INDEX_KERNEL_PARAMETERS, (EFI_PHYSICAL_ADDRESS) (UINTN) loaded_image->LoadOptions, loaded_image->LoadOptionsSize, loaded_image->LoadOptions); if (EFI_ERROR(err)) From e6e24af507f010a0b073acc44240c370e85c84f1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 21 Sep 2021 19:27:25 +0200 Subject: [PATCH 06/24] boot: unify code that measures image options/kernel command line --- src/boot/efi/boot.c | 6 +----- src/boot/efi/measure.c | 14 ++++++++++++++ src/boot/efi/measure.h | 2 ++ src/boot/efi/stub.c | 11 +++++------ 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index e95fe870fe..704213deed 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -2349,11 +2349,7 @@ static EFI_STATUS image_start( #if ENABLE_TPM /* Try to log any options to the TPM, especially to catch manually edited options */ - err = tpm_log_event(TPM_PCR_INDEX_KERNEL_PARAMETERS, - (EFI_PHYSICAL_ADDRESS) (UINTN) loaded_image->LoadOptions, - loaded_image->LoadOptionsSize, loaded_image->LoadOptions); - if (EFI_ERROR(err)) - log_error_stall(L"Unable to add image options measurement: %r", err); + (VOID) tpm_log_load_options(options); #endif } diff --git a/src/boot/efi/measure.c b/src/boot/efi/measure.c index fbca67bbf1..f73c9ff9ad 100644 --- a/src/boot/efi/measure.c +++ b/src/boot/efi/measure.c @@ -162,4 +162,18 @@ EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UIN return EFI_SUCCESS; } +EFI_STATUS tpm_log_load_options(const CHAR16 *load_options) { + EFI_STATUS err; + + /* Measures a load options string into the TPM2, i.e. the kernel command line */ + + err = tpm_log_event(TPM_PCR_INDEX_KERNEL_PARAMETERS, + (EFI_PHYSICAL_ADDRESS) (UINTN) load_options, + StrSize(load_options), load_options); + if (EFI_ERROR(err)) + return log_error_status_stall(err, L"Unable to add load options (i.e. kernel command) line measurement: %r", err); + + return EFI_SUCCESS; +} + #endif diff --git a/src/boot/efi/measure.h b/src/boot/efi/measure.h index e2873adae3..69eb682a2e 100644 --- a/src/boot/efi/measure.h +++ b/src/boot/efi/measure.h @@ -4,3 +4,5 @@ #include EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description); + +EFI_STATUS tpm_log_load_options(const CHAR16 *cmdline); diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index c7232332ef..574842bc55 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -147,12 +147,11 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { cmdline = line; #if ENABLE_TPM - /* Try to log any options to the TPM, especially manually edited options */ - err = tpm_log_event(TPM_PCR_INDEX_KERNEL_PARAMETERS, - (EFI_PHYSICAL_ADDRESS) (UINTN) loaded_image->LoadOptions, - loaded_image->LoadOptionsSize, loaded_image->LoadOptions); - if (EFI_ERROR(err)) - log_error_stall(L"Unable to add image options measurement: %r", err); + /* 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. */ + (VOID) tpm_log_load_options(loaded_image->LoadOptions); #endif } From 5a186322a17ab3b9730d89cf00b08a1820e7679a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 21 Sep 2021 19:28:18 +0200 Subject: [PATCH 07/24] stub: split out code that sets the various efi vars into function of its own Just some refactoring, no code changes beyond the splitting out. --- src/boot/efi/stub.c | 85 ++++++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 39 deletions(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 574842bc55..f8f3e3662f 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -89,6 +89,51 @@ static EFI_STATUS combine_initrd( return EFI_SUCCESS; } +static VOID export_variables(EFI_LOADED_IMAGE *loaded_image) { + CHAR16 uuid[37]; + + assert(loaded_image); + + /* Export the device path this image is started from, if it's not set yet */ + if (efivar_get_raw(LOADER_GUID, L"LoaderDevicePartUUID", NULL, NULL) != EFI_SUCCESS) + if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS) + efivar_set(LOADER_GUID, L"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(LOADER_GUID, L"LoaderImageIdentifier", NULL, NULL) != EFI_SUCCESS && + loaded_image->FilePath) { + _cleanup_freepool_ CHAR16 *s = NULL; + + s = DevicePathToStr(loaded_image->FilePath); + efivar_set(LOADER_GUID, L"LoaderImageIdentifier", s, 0); + } + + /* if LoaderFirmwareInfo is not set, let's set it */ + if (efivar_get_raw(LOADER_GUID, L"LoaderFirmwareInfo", NULL, NULL) != EFI_SUCCESS) { + _cleanup_freepool_ CHAR16 *s = NULL; + + s = PoolPrint(L"%s %d.%02d", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); + efivar_set(LOADER_GUID, L"LoaderFirmwareInfo", s, 0); + } + + /* ditto for LoaderFirmwareType */ + if (efivar_get_raw(LOADER_GUID, L"LoaderFirmwareType", NULL, NULL) != EFI_SUCCESS) { + _cleanup_freepool_ CHAR16 *s = NULL; + + s = PoolPrint(L"UEFI %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); + efivar_set(LOADER_GUID, L"LoaderFirmwareType", s, 0); + } + + /* add StubInfo */ + if (efivar_get_raw(LOADER_GUID, L"StubInfo", NULL, NULL) != EFI_SUCCESS) + efivar_set(LOADER_GUID, L"StubInfo", L"systemd-stub " GIT_VERSION, 0); +} + EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { enum { @@ -114,7 +159,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { UINTN addrs[_SECTION_MAX] = {}; UINTN szs[_SECTION_MAX] = {}; CHAR8 *cmdline = NULL; - CHAR16 uuid[37]; EFI_STATUS err; InitializeLib(image, sys_table); @@ -155,44 +199,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { #endif } - /* Export the device path this image is started from, if it's not set yet */ - if (efivar_get_raw(LOADER_GUID, L"LoaderDevicePartUUID", NULL, NULL) != EFI_SUCCESS) - if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS) - efivar_set(LOADER_GUID, L"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(LOADER_GUID, L"LoaderImageIdentifier", NULL, NULL) != EFI_SUCCESS && - loaded_image->FilePath) { - _cleanup_freepool_ CHAR16 *s = NULL; - - s = DevicePathToStr(loaded_image->FilePath); - efivar_set(LOADER_GUID, L"LoaderImageIdentifier", s, 0); - } - - /* if LoaderFirmwareInfo is not set, let's set it */ - if (efivar_get_raw(LOADER_GUID, L"LoaderFirmwareInfo", NULL, NULL) != EFI_SUCCESS) { - _cleanup_freepool_ CHAR16 *s = NULL; - - s = PoolPrint(L"%s %d.%02d", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); - efivar_set(LOADER_GUID, L"LoaderFirmwareInfo", s, 0); - } - - /* ditto for LoaderFirmwareType */ - if (efivar_get_raw(LOADER_GUID, L"LoaderFirmwareType", NULL, NULL) != EFI_SUCCESS) { - _cleanup_freepool_ CHAR16 *s = NULL; - - s = PoolPrint(L"UEFI %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); - efivar_set(LOADER_GUID, L"LoaderFirmwareType", s, 0); - } - - /* add StubInfo */ - if (efivar_get_raw(LOADER_GUID, L"StubInfo", NULL, NULL) != EFI_SUCCESS) - efivar_set(LOADER_GUID, L"StubInfo", L"systemd-stub " GIT_VERSION, 0); + export_variables(loaded_image); if (szs[SECTION_SPLASH] > 0) graphics_splash((UINT8*) (UINTN) loaded_image->ImageBase + addrs[SECTION_SPLASH], szs[SECTION_SPLASH], NULL); From 94b81afb08e35b44cb15479a0122e4f3c2c88235 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 21 Sep 2021 19:33:01 +0200 Subject: [PATCH 08/24] stub: show splash screen earlier let's move showing of the splash screen to the earliest place we know the splash bmp address. After all a splash screen is all about showing as early as we can. This matters as doing TPM stuff or packing up a large cpio might take time. While we are at it, move the conditionalization of the splash screen into the function instead of doing it ahead of calling it. This should encapsulate things more nicely. --- src/boot/efi/splash.c | 3 +++ src/boot/efi/stub.c | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/boot/efi/splash.c b/src/boot/efi/splash.c index 0286a5a906..bbc1517675 100644 --- a/src/boot/efi/splash.c +++ b/src/boot/efi/splash.c @@ -259,6 +259,9 @@ EFI_STATUS graphics_splash(UINT8 *content, UINTN len, const EFI_GRAPHICS_OUTPUT_ UINTN y_pos = 0; EFI_STATUS err; + if (len == 0) + return EFI_SUCCESS; + assert(content); if (!background) { diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index f8f3e3662f..23f558c885 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -172,6 +172,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { if (EFI_ERROR(err)) return log_error_status_stall(err, L"Unable to locate embedded .linux section: %r", err); + /* Show splash screen as early as possible */ + graphics_splash((UINT8*) loaded_image->ImageBase + addrs[SECTION_SPLASH], szs[SECTION_SPLASH], NULL); + if (szs[SECTION_CMDLINE] > 0) { cmdline = (CHAR8*) loaded_image->ImageBase + addrs[SECTION_CMDLINE]; cmdline_len = szs[SECTION_CMDLINE]; @@ -201,9 +204,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { export_variables(loaded_image); - 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", From 6e161dc86062fc4e221d0e265348ab2d55d79190 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 20 Sep 2021 17:26:48 +0200 Subject: [PATCH 09/24] stub: make splash image payload const --- src/boot/efi/splash.c | 24 ++++++++++++++++-------- src/boot/efi/splash.h | 2 +- src/boot/efi/stub.c | 2 +- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/boot/efi/splash.c b/src/boot/efi/splash.c index bbc1517675..0071a9317b 100644 --- a/src/boot/efi/splash.c +++ b/src/boot/efi/splash.c @@ -37,8 +37,13 @@ struct bmp_map { UINT8 reserved; } _packed_; -static EFI_STATUS bmp_parse_header(UINT8 *bmp, UINTN size, struct bmp_dib **ret_dib, - struct bmp_map **ret_map, UINT8 **pixmap) { +static EFI_STATUS bmp_parse_header( + const UINT8 *bmp, + UINTN size, + struct bmp_dib **ret_dib, + struct bmp_map **ret_map, + const UINT8 **pixmap) { + struct bmp_file *file; struct bmp_dib *dib; struct bmp_map *map; @@ -154,10 +159,13 @@ static VOID pixel_blend(UINT32 *dst, const UINT32 source) { *dst = (rb | g); } -static EFI_STATUS bmp_to_blt(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf, - struct bmp_dib *dib, struct bmp_map *map, - UINT8 *pixmap) { - UINT8 *in; +static EFI_STATUS bmp_to_blt( + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf, + struct bmp_dib *dib, + struct bmp_map *map, + const UINT8 *pixmap) { + + const UINT8 *in; assert(buf); assert(dib); @@ -246,13 +254,13 @@ static EFI_STATUS bmp_to_blt(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf, return EFI_SUCCESS; } -EFI_STATUS graphics_splash(UINT8 *content, UINTN len, const EFI_GRAPHICS_OUTPUT_BLT_PIXEL *background) { +EFI_STATUS graphics_splash(const UINT8 *content, UINTN len, const EFI_GRAPHICS_OUTPUT_BLT_PIXEL *background) { EFI_GRAPHICS_OUTPUT_BLT_PIXEL pixel = {}; static const EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL; struct bmp_dib *dib; struct bmp_map *map; - UINT8 *pixmap; + const UINT8 *pixmap; UINT64 blt_size; _cleanup_freepool_ VOID *blt = NULL; UINTN x_pos = 0; diff --git a/src/boot/efi/splash.h b/src/boot/efi/splash.h index b9f74ffbf2..37ccc6b6a4 100644 --- a/src/boot/efi/splash.h +++ b/src/boot/efi/splash.h @@ -3,4 +3,4 @@ #include -EFI_STATUS graphics_splash(UINT8 *content, UINTN len, const EFI_GRAPHICS_OUTPUT_BLT_PIXEL *background); +EFI_STATUS graphics_splash(const UINT8 *content, UINTN len, const EFI_GRAPHICS_OUTPUT_BLT_PIXEL *background); diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 23f558c885..3bbb25321e 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -173,7 +173,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { return log_error_status_stall(err, L"Unable to locate embedded .linux section: %r", err); /* Show splash screen as early as possible */ - graphics_splash((UINT8*) loaded_image->ImageBase + addrs[SECTION_SPLASH], szs[SECTION_SPLASH], NULL); + graphics_splash((const UINT8*) loaded_image->ImageBase + addrs[SECTION_SPLASH], szs[SECTION_SPLASH], NULL); if (szs[SECTION_CMDLINE] > 0) { cmdline = (CHAR8*) loaded_image->ImageBase + addrs[SECTION_CMDLINE]; From 0be2a06ac736164286a74b89df5bb6c646d74e4d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 21 Sep 2021 14:36:56 +0200 Subject: [PATCH 10/24] boot: split out code that sets various EFI vars from main() Just some refactoring, no actual code changes. --- src/boot/efi/boot.c | 62 ++++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 704213deed..3cb990abd1 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -2415,7 +2415,11 @@ static VOID config_write_entries_to_variable(Config *config) { (void) efivar_set_raw(LOADER_GUID, L"LoaderEntries", buffer, sz, 0); } -EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { +static VOID export_variables( + EFI_LOADED_IMAGE *loaded_image, + const CHAR16 *loaded_image_path, + UINT64 init_usec) { + static const UINT64 loader_features = EFI_LOADER_FEATURE_CONFIG_TIMEOUT | EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT | @@ -2427,18 +2431,11 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { 0; _cleanup_freepool_ CHAR16 *infostr = NULL, *typestr = NULL; - UINT64 osind = 0; - EFI_LOADED_IMAGE *loaded_image; - EFI_FILE *root_dir; - CHAR16 *loaded_image_path; - EFI_STATUS err; - Config config; - UINT64 init_usec; - BOOLEAN menu = FALSE; CHAR16 uuid[37]; - InitializeLib(image, sys_table); - init_usec = time_usec(); + assert(loaded_image); + assert(loaded_image_path); + efivar_set_time_usec(LOADER_GUID, L"LoaderTimeInitUSec", init_usec); efivar_set(LOADER_GUID, L"LoaderInfo", L"systemd-boot " GIT_VERSION, 0); @@ -2450,14 +2447,43 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { (void) efivar_set_uint64_le(LOADER_GUID, L"LoaderFeatures", loader_features, 0); - err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image, - image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); - if (EFI_ERROR(err)) - return log_error_status_stall(err, L"Error getting a LoadedImageProtocol handle: %r", err); + /* the filesystem path to this image, to prevent adding ourselves to the menu */ + efivar_set(LOADER_GUID, L"LoaderImageIdentifier", loaded_image_path, 0); /* export the device path this image is started from */ if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS) efivar_set(LOADER_GUID, L"LoaderDevicePartUUID", uuid, 0); +} + +EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { + _cleanup_freepool_ EFI_LOADED_IMAGE *loaded_image = NULL; + UINT64 osind = 0; + _cleanup_(FileHandleClosep) EFI_FILE *root_dir = NULL; + CHAR16 *loaded_image_path; + EFI_STATUS err; + Config config; + UINT64 init_usec; + BOOLEAN menu = FALSE; + + InitializeLib(image, sys_table); + init_usec = time_usec(); + + err = uefi_call_wrapper( + BS->OpenProtocol, 6, + image, + &LoadedImageProtocol, + (VOID **)&loaded_image, + image, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(err)) + return log_error_status_stall(err, L"Error getting a LoadedImageProtocol handle: %r", err); + + loaded_image_path = DevicePathToStr(loaded_image->FilePath); + if (!loaded_image_path) + return log_oom(); + + export_variables(loaded_image, loaded_image_path, init_usec); root_dir = LibOpenRoot(loaded_image->DeviceHandle); if (!root_dir) @@ -2469,10 +2495,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { return log_error_status_stall(err, L"Error installing security policy: %r", err); } - /* the filesystem path to this image, to prevent adding ourselves to the menu */ - loaded_image_path = DevicePathToStr(loaded_image->FilePath); - efivar_set(LOADER_GUID, L"LoaderImageIdentifier", loaded_image_path, 0); - config_load_defaults(&config, root_dir); /* scan /EFI/Linux/ directory */ @@ -2580,9 +2602,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { } err = EFI_SUCCESS; out: - FreePool(loaded_image_path); config_free(&config); - uefi_call_wrapper(root_dir->Close, 1, root_dir); uefi_call_wrapper(BS->CloseProtocol, 4, image, &LoadedImageProtocol, image, NULL); return err; } From d72f81be7446746b7a6888258916880939f1c5ed Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 21 Sep 2021 14:45:55 +0200 Subject: [PATCH 11/24] boot: split out code that loads the various menu entries into helper call Just some refactoring, no real code changes. --- src/boot/efi/boot.c | 77 +++++++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 31 deletions(-) diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 3cb990abd1..62fc13e150 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -1487,7 +1487,7 @@ static VOID config_load_entries( Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, - CHAR16 *loaded_image_path) { + const CHAR16 *loaded_image_path) { _cleanup_(FileHandleClosep) EFI_FILE_HANDLE entries_dir = NULL; EFI_STATUS err; @@ -2455,9 +2455,52 @@ static VOID export_variables( efivar_set(LOADER_GUID, L"LoaderDevicePartUUID", uuid, 0); } +static VOID config_load_all_entries( + Config *config, + EFI_LOADED_IMAGE *loaded_image, + const CHAR16 *loaded_image_path, + EFI_FILE *root_dir) { + + UINT64 osind = 0; + + assert(config); + assert(loaded_image); + assert(loaded_image_path); + assert(root_dir); + + config_load_defaults(config, root_dir); + + /* scan /EFI/Linux/ directory */ + config_entry_add_linux(config, loaded_image->DeviceHandle, root_dir); + + /* scan /loader/entries/\*.conf files */ + config_load_entries(config, loaded_image->DeviceHandle, root_dir, loaded_image_path); + + /* Similar, but on any XBOOTLDR partition */ + config_load_xbootldr(config, loaded_image->DeviceHandle); + + /* sort entries after version number */ + config_sort_entries(config); + + /* if we find some well-known loaders, add them to the end of the list */ + config_entry_add_osx(config); + config_entry_add_windows(config, loaded_image->DeviceHandle, root_dir); + config_entry_add_loader_auto(config, loaded_image->DeviceHandle, root_dir, NULL, + L"auto-efi-shell", 's', L"EFI Shell", L"\\shell" EFI_MACHINE_TYPE_NAME ".efi"); + config_entry_add_loader_auto(config, loaded_image->DeviceHandle, root_dir, loaded_image_path, + L"auto-efi-default", '\0', L"EFI Default Loader", NULL); + + if (config->auto_firmware && efivar_get_uint64_le(EFI_GLOBAL_GUID, L"OsIndicationsSupported", &osind) == EFI_SUCCESS) { + if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI) + config_entry_add_call(config, + L"auto-reboot-to-firmware-setup", + L"Reboot Into Firmware Interface", + reboot_into_firmware); + } +} + EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { _cleanup_freepool_ EFI_LOADED_IMAGE *loaded_image = NULL; - UINT64 osind = 0; _cleanup_(FileHandleClosep) EFI_FILE *root_dir = NULL; CHAR16 *loaded_image_path; EFI_STATUS err; @@ -2495,35 +2538,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { return log_error_status_stall(err, L"Error installing security policy: %r", err); } - config_load_defaults(&config, root_dir); - - /* scan /EFI/Linux/ directory */ - config_entry_add_linux(&config, loaded_image->DeviceHandle, root_dir); - - /* scan /loader/entries/\*.conf files */ - config_load_entries(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path); - - /* Similar, but on any XBOOTLDR partition */ - config_load_xbootldr(&config, loaded_image->DeviceHandle); - - /* sort entries after version number */ - config_sort_entries(&config); - - /* if we find some well-known loaders, add them to the end of the list */ - config_entry_add_osx(&config); - config_entry_add_windows(&config, loaded_image->DeviceHandle, root_dir); - config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, NULL, - L"auto-efi-shell", 's', L"EFI Shell", L"\\shell" EFI_MACHINE_TYPE_NAME ".efi"); - config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path, - L"auto-efi-default", '\0', L"EFI Default Loader", NULL); - - if (config.auto_firmware && efivar_get_uint64_le(EFI_GLOBAL_GUID, L"OsIndicationsSupported", &osind) == EFI_SUCCESS) { - if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI) - config_entry_add_call(&config, - L"auto-reboot-to-firmware-setup", - L"Reboot Into Firmware Interface", - reboot_into_firmware); - } + config_load_all_entries(&config, loaded_image, loaded_image_path, root_dir); if (config.entry_count == 0) { log_error_stall(L"No loader found. Configuration files in \\loader\\entries\\*.conf are needed."); From 2553a5482c77980429565d14662f309e385bd472 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 21 Sep 2021 14:47:05 +0200 Subject: [PATCH 12/24] boot: automatically load drop-in EFI drivers off the ESP Fixes: #15617 --- src/boot/bootctl.c | 1 + src/boot/efi/boot.c | 4 + src/boot/efi/drivers.c | 150 ++++++++++++++++++++++++++ src/boot/efi/drivers.h | 9 ++ src/boot/efi/meson.build | 2 + src/fundamental/efi-loader-features.h | 1 + 6 files changed, 167 insertions(+) create mode 100644 src/boot/efi/drivers.c create mode 100644 src/boot/efi/drivers.h diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 65e5262a09..27b0086406 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -1298,6 +1298,7 @@ static int verb_status(int argc, char *argv[], void *userdata) { { EFI_LOADER_FEATURE_ENTRY_ONESHOT, "One-shot entry control" }, { EFI_LOADER_FEATURE_XBOOTLDR, "Support for XBOOTLDR partition" }, { EFI_LOADER_FEATURE_RANDOM_SEED, "Support for passing random seed to OS" }, + { EFI_LOADER_FEATURE_LOAD_DRIVER, "Load drop-in drivers" }, }; _cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL; diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 62fc13e150..f1777d8de0 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -7,6 +7,7 @@ #include "console.h" #include "devicetree.h" #include "disk.h" +#include "drivers.h" #include "efi-loader-features.h" #include "graphics.h" #include "linux.h" @@ -2428,6 +2429,7 @@ static VOID export_variables( EFI_LOADER_FEATURE_BOOT_COUNTING | EFI_LOADER_FEATURE_XBOOTLDR | EFI_LOADER_FEATURE_RANDOM_SEED | + EFI_LOADER_FEATURE_LOAD_DRIVER | 0; _cleanup_freepool_ CHAR16 *infostr = NULL, *typestr = NULL; @@ -2538,6 +2540,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { return log_error_status_stall(err, L"Error installing security policy: %r", err); } + (VOID) load_drivers(image, loaded_image, root_dir); + config_load_all_entries(&config, loaded_image, loaded_image_path, root_dir); if (config.entry_count == 0) { diff --git a/src/boot/efi/drivers.c b/src/boot/efi/drivers.c new file mode 100644 index 0000000000..a876c3df7f --- /dev/null +++ b/src/boot/efi/drivers.c @@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include + +#include "drivers.h" +#include "util.h" + +static VOID efi_unload_image(EFI_HANDLE *h) { + if (*h) + (VOID) uefi_call_wrapper(BS->UnloadImage, 1, *h); +} + +static EFI_STATUS load_one_driver( + EFI_HANDLE parent_image, + EFI_LOADED_IMAGE *loaded_image, + const CHAR16 *fname) { + + _cleanup_(efi_unload_image) EFI_HANDLE image = NULL; + _cleanup_freepool_ EFI_DEVICE_PATH *path = NULL; + _cleanup_freepool_ CHAR16 *spath = NULL; + EFI_STATUS err; + + assert(parent_image); + assert(loaded_image); + assert(fname); + + spath = PoolPrint(L"\\EFI\\systemd\\drivers\\%s", fname); + if (!spath) + return log_oom(); + + path = FileDevicePath(loaded_image->DeviceHandle, spath); + if (!path) + return log_oom(); + + err = uefi_call_wrapper( + BS->LoadImage, 6, + FALSE, + parent_image, + path, + NULL, 0, + &image); + if (EFI_ERROR(err)) + return log_error_status_stall(err, L"Failed to load image %s: %r", fname, err); + + err = uefi_call_wrapper( + BS->HandleProtocol, 3, + image, + &LoadedImageProtocol, + (VOID **)&loaded_image); + if (EFI_ERROR(err)) + return log_error_status_stall(err, L"Failed to find protocol in driver image s: %r", fname, err); + + if (loaded_image->ImageCodeType != EfiBootServicesCode && + loaded_image->ImageCodeType != EfiRuntimeServicesCode) + return log_error_status_stall(EFI_INVALID_PARAMETER, L"Image %s is not a driver, refusing: %r", fname); + + err = uefi_call_wrapper( + BS->StartImage, 3, + image, + NULL, + NULL); + if (EFI_ERROR(err)) + return log_error_status_stall(err, L"Failed to start image %s: %r", fname, err); + + TAKE_PTR(image); + return EFI_SUCCESS; +} + +static EFI_STATUS reconnect(VOID) { + _cleanup_freepool_ EFI_HANDLE *handles = NULL; + UINTN n_handles = 0; + EFI_STATUS err; + + /* Reconnects all handles, so that any loaded drivers can take effect. */ + + err = uefi_call_wrapper( + BS->LocateHandleBuffer, 5, + AllHandles, + NULL, + NULL, + &n_handles, + &handles); + if (EFI_ERROR(err)) + return log_error_status_stall(err, L"Failed to get list of handles: %r", err); + + for (UINTN i = 0; i < n_handles; i++) { + err = uefi_call_wrapper( + BS->ConnectController, 4, + handles[i], + NULL, + NULL, + TRUE); + if (err == EFI_NOT_FOUND) /* No drivers for this handle */ + continue; + if (EFI_ERROR(err)) + log_error_status_stall(err, L"Failed to reconnect handle %u, ignoring: %r", i, err); + } + + return EFI_SUCCESS; +} + +EFI_STATUS load_drivers( + EFI_HANDLE parent_image, + EFI_LOADED_IMAGE *loaded_image, + EFI_FILE_HANDLE root_dir) { + + _cleanup_(FileHandleClosep) EFI_FILE_HANDLE drivers_dir = NULL; + _cleanup_freepool_ EFI_FILE_INFO *dirent = NULL; + _cleanup_freepool_ EFI_DEVICE_PATH *path = NULL; + UINTN dirent_size = 0, n_succeeded = 0; + EFI_STATUS err; + + err = open_directory( + root_dir, + L"\\EFI\\systemd\\drivers", + &drivers_dir); + if (err == EFI_NOT_FOUND) + return EFI_SUCCESS; + if (EFI_ERROR(err)) + return log_error_status_stall(err, L"Failed to open \\EFI\\systemd\\drivers: %r", err); + + for (;;) { + _cleanup_freepool_ CHAR16 *d = NULL; + + err = readdir_harder(drivers_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 (!endswith_no_case(dirent->FileName, EFI_MACHINE_TYPE_NAME L".efi")) + continue; + + err = load_one_driver(parent_image, loaded_image, dirent->FileName); + if (EFI_ERROR(err)) + continue; + + n_succeeded++; + } + + if (n_succeeded > 0) + (VOID) reconnect(); + + return EFI_SUCCESS; +} diff --git a/src/boot/efi/drivers.h b/src/boot/efi/drivers.h new file mode 100644 index 0000000000..c192c6d44c --- /dev/null +++ b/src/boot/efi/drivers.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include + +EFI_STATUS load_drivers( + EFI_HANDLE parent_image, + EFI_LOADED_IMAGE *loaded_image, + EFI_FILE_HANDLE root_dir); diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build index 3fdabb2073..fef8572c6a 100644 --- a/src/boot/efi/meson.build +++ b/src/boot/efi/meson.build @@ -5,6 +5,7 @@ efi_headers = files(''' cpio.h devicetree.h disk.h + drivers.h graphics.h linux.h measure.h @@ -31,6 +32,7 @@ systemd_boot_sources = ''' boot.c console.c devicetree.c + drivers.c random-seed.c sha256.c shim.c diff --git a/src/fundamental/efi-loader-features.h b/src/fundamental/efi-loader-features.h index f07dacb859..287ae3c892 100644 --- a/src/fundamental/efi-loader-features.h +++ b/src/fundamental/efi-loader-features.h @@ -12,3 +12,4 @@ #define EFI_LOADER_FEATURE_BOOT_COUNTING (UINT64_C(1) << 4) #define EFI_LOADER_FEATURE_XBOOTLDR (UINT64_C(1) << 5) #define EFI_LOADER_FEATURE_RANDOM_SEED (UINT64_C(1) << 6) +#define EFI_LOADER_FEATURE_LOAD_DRIVER (UINT64_C(1) << 7) From 77c015820c04a9a0562487f6ef8ffd8a5dcef601 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 21 Sep 2021 15:13:29 +0200 Subject: [PATCH 13/24] boot: port more code to use open_directory() helper --- src/boot/efi/boot.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index f1777d8de0..dd7731c4da 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -1498,7 +1498,7 @@ static VOID config_load_entries( assert(root_dir); assert(loaded_image_path); - err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &entries_dir, (CHAR16*) L"\\loader\\entries", EFI_FILE_MODE_READ, 0ULL); + err = open_directory(root_dir, L"\\loader\\entries", &entries_dir); if (EFI_ERROR(err)) return; @@ -1969,7 +1969,7 @@ static VOID config_entry_add_linux( assert(device); assert(root_dir); - err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &linux_dir, (CHAR16*) L"\\EFI\\Linux", EFI_FILE_MODE_READ, 0ULL); + err = open_directory(root_dir, L"\\EFI\\Linux", &linux_dir); if (EFI_ERROR(err)) return; From 4cbecde9370a862d950093350094c6599e841149 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 21 Sep 2021 15:20:40 +0200 Subject: [PATCH 14/24] boot: use _cleanup_freepool_ at more places --- src/boot/efi/boot.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index dd7731c4da..caeb6aa12e 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -518,7 +518,8 @@ static BOOLEAN menu_run( BOOLEAN refresh = TRUE, highlight = FALSE; UINTN x_start = 0, y_start = 0, y_status = 0; UINTN x_max, y_max; - CHAR16 **lines = NULL, *status = NULL, *clearline = NULL; + CHAR16 **lines = NULL; + _cleanup_freepool_ CHAR16 *clearline = NULL, *status = NULL; UINTN timeout_remain = config->timeout_sec; INT16 idx; BOOLEAN exit = FALSE, run = TRUE; @@ -902,7 +903,6 @@ static BOOLEAN menu_run( for (UINTN i = 0; i < config->entry_count; i++) FreePool(lines[i]); FreePool(lines); - FreePool(clearline); clear_screen(COLOR_NORMAL); return run; @@ -1974,6 +1974,9 @@ static VOID config_entry_add_linux( return; for (;;) { + _cleanup_freepool_ CHAR16 *os_name_pretty = NULL, *os_name = NULL, *os_id = NULL, + *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL; + _cleanup_freepool_ CHAR8 *content = NULL; CHAR16 buf[256]; UINTN bufsize = sizeof buf; EFI_FILE_INFO *f; @@ -1984,16 +1987,9 @@ static VOID config_entry_add_linux( }; UINTN offs[ELEMENTSOF(sections)-1] = {}; UINTN szs[ELEMENTSOF(sections)-1] = {}; - CHAR8 *content = NULL; CHAR8 *line; UINTN pos = 0; CHAR8 *key, *value; - CHAR16 *os_name_pretty = NULL; - CHAR16 *os_name = NULL; - CHAR16 *os_id = NULL; - CHAR16 *os_version = NULL; - CHAR16 *os_version_id = NULL; - CHAR16 *os_build_id = NULL; err = uefi_call_wrapper(linux_dir->Read, 3, linux_dir, &bufsize, buf); if (bufsize == 0 || EFI_ERROR(err)) @@ -2082,14 +2078,6 @@ static VOID config_entry_add_linux( config_entry_parse_tries(entry, L"\\EFI\\Linux", f->FileName, L".efi"); } - - FreePool(os_name_pretty); - FreePool(os_name); - FreePool(os_id); - FreePool(os_version); - FreePool(os_version_id); - FreePool(os_build_id); - FreePool(content); } } From 19c896e99c8d200b6a5b67923c49c1e6fc9ef98a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 21 Sep 2021 15:24:50 +0200 Subject: [PATCH 15/24] boot: port more code to readdir_harder() --- src/boot/efi/boot.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index caeb6aa12e..40fb933af4 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -1491,6 +1491,8 @@ static VOID config_load_entries( const CHAR16 *loaded_image_path) { _cleanup_(FileHandleClosep) EFI_FILE_HANDLE entries_dir = NULL; + _cleanup_freepool_ EFI_FILE_INFO *f = NULL; + UINTN f_size = 0; EFI_STATUS err; assert(config); @@ -1503,17 +1505,12 @@ static VOID config_load_entries( return; for (;;) { - CHAR16 buf[256]; - UINTN bufsize; - EFI_FILE_INFO *f; _cleanup_freepool_ CHAR8 *content = NULL; - bufsize = sizeof(buf); - err = uefi_call_wrapper(entries_dir->Read, 3, entries_dir, &bufsize, buf); - if (bufsize == 0 || EFI_ERROR(err)) + err = readdir_harder(entries_dir, &f, &f_size); + if (f_size == 0 || EFI_ERROR(err)) break; - f = (EFI_FILE_INFO *) buf; if (f->FileName[0] == '.') continue; if (f->Attribute & EFI_FILE_DIRECTORY) @@ -1962,8 +1959,10 @@ static VOID config_entry_add_linux( EFI_FILE *root_dir) { _cleanup_(FileHandleClosep) EFI_FILE_HANDLE linux_dir = NULL; - EFI_STATUS err; + _cleanup_freepool_ EFI_FILE_INFO *f = NULL; ConfigEntry *entry; + UINTN f_size = 0; + EFI_STATUS err; assert(config); assert(device); @@ -1977,9 +1976,6 @@ static VOID config_entry_add_linux( _cleanup_freepool_ CHAR16 *os_name_pretty = NULL, *os_name = NULL, *os_id = NULL, *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL; _cleanup_freepool_ CHAR8 *content = NULL; - CHAR16 buf[256]; - UINTN bufsize = sizeof buf; - EFI_FILE_INFO *f; const CHAR8 *sections[] = { (CHAR8 *)".osrel", (CHAR8 *)".cmdline", @@ -1991,11 +1987,10 @@ static VOID config_entry_add_linux( UINTN pos = 0; CHAR8 *key, *value; - err = uefi_call_wrapper(linux_dir->Read, 3, linux_dir, &bufsize, buf); - if (bufsize == 0 || EFI_ERROR(err)) + err = readdir_harder(linux_dir, &f, &f_size); + if (f_size == 0 || EFI_ERROR(err)) break; - f = (EFI_FILE_INFO *) buf; if (f->FileName[0] == '.') continue; if (f->Attribute & EFI_FILE_DIRECTORY) From ef6ff81a53cfbb751d5ecdcd63845106ffb51689 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 21 Sep 2021 15:29:03 +0200 Subject: [PATCH 16/24] boot: port more code over to get_file_info_harder() --- src/boot/efi/boot.c | 25 ++++--------------------- src/boot/efi/devicetree.c | 6 +++--- src/boot/efi/random-seed.c | 6 +++--- 3 files changed, 10 insertions(+), 27 deletions(-) diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 40fb933af4..386ee678ea 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -1226,7 +1226,7 @@ static VOID config_entry_bump_counters( _cleanup_(FileHandleClosep) EFI_FILE_HANDLE handle = NULL; static const EFI_GUID EfiFileInfoGuid = EFI_FILE_INFO_ID; _cleanup_freepool_ EFI_FILE_INFO *file_info = NULL; - UINTN file_info_size, a, b; + UINTN file_info_size; EFI_STATUS r; assert(entry); @@ -1244,26 +1244,9 @@ static VOID config_entry_bump_counters( if (EFI_ERROR(r)) return; - a = StrLen(entry->current_name); - b = StrLen(entry->next_name); - - file_info_size = OFFSETOF(EFI_FILE_INFO, FileName) + (a > b ? a : b) + 1; - - for (;;) { - file_info = AllocatePool(file_info_size); - - r = uefi_call_wrapper(handle->GetInfo, 4, handle, (EFI_GUID*) &EfiFileInfoGuid, &file_info_size, file_info); - if (!EFI_ERROR(r)) - break; - - if (r != EFI_BUFFER_TOO_SMALL || file_info_size * 2 < file_info_size) { - log_error_stall(L"Failed to get file info for '%s': %r", old_path, r); - return; - } - - file_info_size *= 2; - FreePool(file_info); - } + r = get_file_info_harder(handle, &file_info, &file_info_size); + if (EFI_ERROR(r)) + return; /* And rename the file */ StrCpy(file_info->FileName, entry->next_name); diff --git a/src/boot/efi/devicetree.c b/src/boot/efi/devicetree.c index c65936bdb4..87ae97a52e 100644 --- a/src/boot/efi/devicetree.c +++ b/src/boot/efi/devicetree.c @@ -91,9 +91,9 @@ EFI_STATUS devicetree_install(struct devicetree_state *state, if (EFI_ERROR(err)) return err; - info = LibFileInfo(handle); - if (!info) - return EFI_OUT_OF_RESOURCES; + err = get_file_info_harder(handle, &info, NULL); + if (EFI_ERROR(err)) + return err; if (info->FileSize < FDT_V1_SIZE || info->FileSize > 32 * 1024 * 1024) /* 32MB device tree blob doesn't seem right */ return EFI_INVALID_PARAMETER; diff --git a/src/boot/efi/random-seed.c b/src/boot/efi/random-seed.c index d20923eacc..1505b9d93b 100644 --- a/src/boot/efi/random-seed.c +++ b/src/boot/efi/random-seed.c @@ -263,9 +263,9 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) { return err; } - info = LibFileInfo(handle); - if (!info) - return log_oom(); + err = get_file_info_harder(handle, &info, NULL); + if (EFI_ERROR(err)) + return log_error_status_stall(err, L"Failed to get file info for random seed: %r"); size = info->FileSize; if (size < RANDOM_MAX_SIZE_MIN) From 61b6249552ed7d0ce858f2a413aaf7f65b301769 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 21 Sep 2021 15:39:47 +0200 Subject: [PATCH 17/24] boot: move TPM conditionalization into measure.h header Let's move conditionalization of tpm_log_load_options() into the measure.h to encapsulate the ifdeffery a bit more. --- src/boot/efi/boot.c | 2 -- src/boot/efi/measure.h | 14 +++++++++++++- src/boot/efi/stub.c | 2 -- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 386ee678ea..9efd157a8e 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -2314,10 +2314,8 @@ static EFI_STATUS image_start( loaded_image->LoadOptions = options; loaded_image->LoadOptionsSize = StrSize(loaded_image->LoadOptions); -#if ENABLE_TPM /* Try to log any options to the TPM, especially to catch manually edited options */ (VOID) tpm_log_load_options(options); -#endif } efivar_set_time_usec(LOADER_GUID, L"LoaderTimeExecUSec", 0); diff --git a/src/boot/efi/measure.h b/src/boot/efi/measure.h index 69eb682a2e..b92d0574c9 100644 --- a/src/boot/efi/measure.h +++ b/src/boot/efi/measure.h @@ -3,6 +3,18 @@ #include -EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description); +#if ENABLE_TPM +EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description); EFI_STATUS tpm_log_load_options(const CHAR16 *cmdline); + +#else + +static inline EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description) { + return EFI_SUCCESS; +} +static inline EFI_STATUS tpm_log_load_options(const CHAR16 *cmdline) { + return EFI_SUCCESS; +} + +#endif diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 3bbb25321e..ad01ab138b 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -193,13 +193,11 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { line[i] = options[i]; cmdline = line; -#if ENABLE_TPM /* 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. */ (VOID) tpm_log_load_options(loaded_image->LoadOptions); -#endif } export_variables(loaded_image); From a0a644be7060bdba77f51addaf415671aa4bfe32 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 21 Sep 2021 21:57:51 +0200 Subject: [PATCH 18/24] boot: add helper for converting EFI_PHYSICAL_ADDRESS to a pointer This isn't trivial when trying to be compatible with 32bit archs, hence add a set of helper macro-like functions that make the conversion safe. --- src/boot/efi/cpio.c | 2 +- src/boot/efi/devicetree.c | 18 ++++++------------ src/boot/efi/linux.c | 7 ++++--- src/boot/efi/measure.c | 2 +- src/boot/efi/stub.c | 10 +++++----- src/boot/efi/util.h | 20 ++++++++++++++++++++ 6 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src/boot/efi/cpio.c b/src/boot/efi/cpio.c index 9c99454d9a..10a044cc92 100644 --- a/src/boot/efi/cpio.c +++ b/src/boot/efi/cpio.c @@ -452,7 +452,7 @@ EFI_STATUS pack_cpio( #if ENABLE_TPM err = tpm_log_event( tpm_pcr, - (EFI_PHYSICAL_ADDRESS) (UINTN) buffer, + POINTER_TO_PHYSICAL_ADDRESS(buffer), buffer_size, tpm_description); if (EFI_ERROR(err)) diff --git a/src/boot/efi/devicetree.c b/src/boot/efi/devicetree.c index 87ae97a52e..fd4b9c406c 100644 --- a/src/boot/efi/devicetree.c +++ b/src/boot/efi/devicetree.c @@ -28,12 +28,6 @@ static UINTN devicetree_allocated(const struct devicetree_state *state) { return state->pages * EFI_PAGE_SIZE; } -static VOID *devicetree_ptr(const struct devicetree_state *state) { - assert(state); - assert(state->addr <= UINTN_MAX); - return (VOID *)(UINTN)state->addr; -} - static EFI_STATUS devicetree_fixup(struct devicetree_state *state, UINTN len) { EFI_DT_FIXUP_PROTOCOL *fixup; UINTN size; @@ -47,24 +41,24 @@ static EFI_STATUS devicetree_fixup(struct devicetree_state *state, UINTN len) { L"Could not locate device tree fixup protocol, skipping."); size = devicetree_allocated(state); - err = uefi_call_wrapper(fixup->Fixup, 4, fixup, devicetree_ptr(state), &size, + err = uefi_call_wrapper(fixup->Fixup, 4, fixup, PHYSICAL_ADDRESS_TO_POINTER(state->addr), &size, EFI_DT_APPLY_FIXUPS | EFI_DT_RESERVE_MEMORY); if (err == EFI_BUFFER_TOO_SMALL) { EFI_PHYSICAL_ADDRESS oldaddr = state->addr; UINTN oldpages = state->pages; - VOID *oldptr = devicetree_ptr(state); + VOID *oldptr = PHYSICAL_ADDRESS_TO_POINTER(state->addr); err = devicetree_allocate(state, size); if (EFI_ERROR(err)) return err; - CopyMem(devicetree_ptr(state), oldptr, len); + CopyMem(PHYSICAL_ADDRESS_TO_POINTER(state->addr), oldptr, len); err = uefi_call_wrapper(BS->FreePages, 2, oldaddr, oldpages); if (EFI_ERROR(err)) return err; size = devicetree_allocated(state); - err = uefi_call_wrapper(fixup->Fixup, 4, fixup, devicetree_ptr(state), &size, + err = uefi_call_wrapper(fixup->Fixup, 4, fixup, PHYSICAL_ADDRESS_TO_POINTER(state->addr), &size, EFI_DT_APPLY_FIXUPS | EFI_DT_RESERVE_MEMORY); } @@ -104,7 +98,7 @@ EFI_STATUS devicetree_install(struct devicetree_state *state, if (EFI_ERROR(err)) return err; - err = uefi_call_wrapper(handle->Read, 3, handle, &len, devicetree_ptr(state)); + err = uefi_call_wrapper(handle->Read, 3, handle, &len, PHYSICAL_ADDRESS_TO_POINTER(state->addr)); if (EFI_ERROR(err)) return err; @@ -112,7 +106,7 @@ EFI_STATUS devicetree_install(struct devicetree_state *state, if (EFI_ERROR(err)) return err; - return uefi_call_wrapper(BS->InstallConfigurationTable, 2, &EfiDtbTableGuid, devicetree_ptr(state)); + return uefi_call_wrapper(BS->InstallConfigurationTable, 2, &EfiDtbTableGuid, PHYSICAL_ADDRESS_TO_POINTER(state->addr)); } void devicetree_cleanup(struct devicetree_state *state) { diff --git a/src/boot/efi/linux.c b/src/boot/efi/linux.c index 529325fef9..3af69e4d57 100644 --- a/src/boot/efi/linux.c +++ b/src/boot/efi/linux.c @@ -66,9 +66,10 @@ EFI_STATUS linux_exec(EFI_HANDLE image, EFI_SIZE_TO_PAGES(cmdline_len + 1), &addr); if (EFI_ERROR(err)) return err; - CopyMem((VOID *)(UINTN)addr, cmdline, cmdline_len); - ((CHAR8 *)(UINTN)addr)[cmdline_len] = 0; - boot_params->hdr.cmd_line_ptr = (UINT32)addr; + + CopyMem(PHYSICAL_ADDRESS_TO_POINTER(addr), cmdline, cmdline_len); + ((CHAR8 *) PHYSICAL_ADDRESS_TO_POINTER(addr))[cmdline_len] = 0; + boot_params->hdr.cmd_line_ptr = (UINT32) addr; } boot_params->hdr.ramdisk_image = (UINT32)initrd_addr; diff --git a/src/boot/efi/measure.c b/src/boot/efi/measure.c index f73c9ff9ad..de7856bacf 100644 --- a/src/boot/efi/measure.c +++ b/src/boot/efi/measure.c @@ -168,7 +168,7 @@ EFI_STATUS tpm_log_load_options(const CHAR16 *load_options) { /* Measures a load options string into the TPM2, i.e. the kernel command line */ err = tpm_log_event(TPM_PCR_INDEX_KERNEL_PARAMETERS, - (EFI_PHYSICAL_ADDRESS) (UINTN) load_options, + POINTER_TO_PHYSICAL_ADDRESS(load_options), StrSize(load_options), load_options); if (EFI_ERROR(err)) return log_error_status_stall(err, L"Unable to add load options (i.e. kernel command) line measurement: %r", err); diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index ad01ab138b..dad0f61335 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -55,13 +55,13 @@ static EFI_STATUS combine_initrd( if (EFI_ERROR(err)) return log_error_status_stall(err, L"Failed to allocate space for combined initrd: %r", err); - p = (UINT8*) (UINTN) base; + p = PHYSICAL_ADDRESS_TO_POINTER(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); + CopyMem(p, PHYSICAL_ADDRESS_TO_POINTER(initrd_base), initrd_size); p += initrd_size; pad = ALIGN_TO(initrd_size, 4) - initrd_size; @@ -81,7 +81,7 @@ static EFI_STATUS combine_initrd( p += sysext_initrd_size; } - assert((UINT8*) (UINTN) base + n == p); + assert((UINT8*) PHYSICAL_ADDRESS_TO_POINTER(base) + n == p); *ret_initrd_base = base; *ret_initrd_size = n; @@ -222,10 +222,10 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { &sysext_initrd, &sysext_initrd_size); - linux_base = (EFI_PHYSICAL_ADDRESS) (UINTN) loaded_image->ImageBase + addrs[SECTION_LINUX]; + linux_base = POINTER_TO_PHYSICAL_ADDRESS(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; + initrd_base = initrd_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[SECTION_INITRD] : 0; if (credential_initrd || sysext_initrd) { /* If we have generated initrds dynamically, let's combine them with the built-in initrd. */ diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h index 3a29ff2d7b..ca95514d72 100644 --- a/src/boot/efi/util.h +++ b/src/boot/efi/util.h @@ -124,3 +124,23 @@ static inline void strv_freep(CHAR16 ***p) { } EFI_STATUS open_directory(EFI_FILE_HANDLE root_dir, const CHAR16 *path, EFI_FILE_HANDLE *ret); + +/* Conversion between EFI_PHYSICAL_ADDRESS and pointers is not obvious. The former is always 64bit, even on + * 32bit archs. And gcc complains if we cast a pointer to an integer of a different size. Hence let's do the + * conversion indirectly: first into UINTN (which is defined by UEFI to have the same size as a pointer), and + * then extended to EFI_PHYSICAL_ADDRESS. */ +static inline EFI_PHYSICAL_ADDRESS POINTER_TO_PHYSICAL_ADDRESS(const void *p) { + return (EFI_PHYSICAL_ADDRESS) (UINTN) p; +} + +static inline void *PHYSICAL_ADDRESS_TO_POINTER(EFI_PHYSICAL_ADDRESS addr) { +#if __SIZEOF_POINTER__ == 4 + /* On 32bit systems the address might not be convertible (as pointers are 32bit but + * EFI_PHYSICAL_ADDRESS 64bit) */ + assert(addr <= UINT32_MAX); +#elif __SIZEOF_POINTER__ != 8 + #error "Unexpected pointer size" +#endif + + return (void*) (UINTN) addr; +} From 0d43ce5266228182189771901413cf59a57ad585 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 21 Sep 2021 22:00:06 +0200 Subject: [PATCH 19/24] stub: various modernizations to linux.c Let's make some stuff const. Most importanly call AllocatePages() with a pointer to an EFI_PHYSICAL_ADDRESS instead of a pointer to a pointer. On 64bit this makes no difference, but on i386 this is simply not correct, since EFI_PHYSICAL_ADDRESS is 64bit there, even though pointers are 32bit. --- src/boot/efi/linux.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/src/boot/efi/linux.c b/src/boot/efi/linux.c index 3af69e4d57..5232a3ba40 100644 --- a/src/boot/efi/linux.c +++ b/src/boot/efi/linux.c @@ -13,6 +13,7 @@ #endif typedef VOID(*handover_f)(VOID *image, EFI_SYSTEM_TABLE *table, struct boot_params *params) __regparm0__; + static VOID linux_efi_handover(EFI_HANDLE image, struct boot_params *params) { handover_f handover; UINTN start = (UINTN)params->hdr.code32_start; @@ -31,16 +32,17 @@ EFI_STATUS linux_exec(EFI_HANDLE image, CHAR8 *cmdline, UINTN cmdline_len, UINTN linux_addr, UINTN initrd_addr, UINTN initrd_size) { - struct boot_params *image_params; + + const struct boot_params *image_params; struct boot_params *boot_params; - UINT8 setup_sectors; EFI_PHYSICAL_ADDRESS addr; + UINT8 setup_sectors; EFI_STATUS err; assert(image); assert(cmdline); - image_params = (struct boot_params *) linux_addr; + image_params = (const struct boot_params *) linux_addr; if (image_params->hdr.boot_flag != 0xAA55 || image_params->hdr.header != SETUP_MAGIC || @@ -48,22 +50,32 @@ EFI_STATUS linux_exec(EFI_HANDLE image, !image_params->hdr.relocatable_kernel) return EFI_LOAD_ERROR; - boot_params = (struct boot_params *) 0xFFFFFFFF; - err = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData, - EFI_SIZE_TO_PAGES(0x4000), (EFI_PHYSICAL_ADDRESS*) &boot_params); + addr = UINT32_MAX; /* Below the 32bit boundary */ + err = uefi_call_wrapper( + BS->AllocatePages, 4, + AllocateMaxAddress, + EfiLoaderData, + EFI_SIZE_TO_PAGES(0x4000), + &addr); if (EFI_ERROR(err)) return err; + boot_params = (struct boot_params *) PHYSICAL_ADDRESS_TO_POINTER(addr); ZeroMem(boot_params, 0x4000); - CopyMem(&boot_params->hdr, &image_params->hdr, sizeof(struct setup_header)); + boot_params->hdr = image_params->hdr; boot_params->hdr.type_of_loader = 0xff; setup_sectors = image_params->hdr.setup_sects > 0 ? image_params->hdr.setup_sects : 4; boot_params->hdr.code32_start = (UINT32)linux_addr + (setup_sectors + 1) * 512; if (cmdline) { addr = 0xA0000; - err = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData, - EFI_SIZE_TO_PAGES(cmdline_len + 1), &addr); + + err = uefi_call_wrapper( + BS->AllocatePages, 4, + AllocateMaxAddress, + EfiLoaderData, + EFI_SIZE_TO_PAGES(cmdline_len + 1), + &addr); if (EFI_ERROR(err)) return err; @@ -72,8 +84,8 @@ EFI_STATUS linux_exec(EFI_HANDLE image, boot_params->hdr.cmd_line_ptr = (UINT32) addr; } - boot_params->hdr.ramdisk_image = (UINT32)initrd_addr; - boot_params->hdr.ramdisk_size = (UINT32)initrd_size; + boot_params->hdr.ramdisk_image = (UINT32) initrd_addr; + boot_params->hdr.ramdisk_size = (UINT32) initrd_size; linux_efi_handover(image, boot_params); return EFI_LOAD_ERROR; From 3a6249127eeef8c949a3aa5fad7914cf5f755589 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 21 Sep 2021 22:13:23 +0200 Subject: [PATCH 20/24] boot: add get_os_indications_supported() helper We inquire the EFI var for this at two places, let's add a helper that queries it and gracefully handles it if we can't get it, by returning a zero mask, i.e. no features supported. --- src/boot/efi/boot.c | 19 +++++++------------ src/boot/efi/util.c | 14 ++++++++++++++ src/boot/efi/util.h | 2 ++ 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 9efd157a8e..b0e16f7106 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -367,7 +367,7 @@ static UINTN entry_lookup_key(Config *config, UINTN start, CHAR16 key) { } static VOID print_status(Config *config, CHAR16 *loaded_image_path) { - UINT64 key, indvar; + UINT64 key; UINTN timeout; BOOLEAN modevar; _cleanup_freepool_ CHAR16 *partstr = NULL, *defaultstr = NULL; @@ -395,8 +395,7 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) { if (shim_loaded()) Print(L"Shim: present\n"); - if (efivar_get_uint64_le(EFI_GLOBAL_GUID, L"OsIndicationsSupported", &indvar) == EFI_SUCCESS) - Print(L"OsIndicationsSupported: %d\n", indvar); + Print(L"OsIndicationsSupported: %d\n", get_os_indications_supported()); Print(L"\n--- press key ---\n\n"); console_key_read(&key, 0); @@ -2427,8 +2426,6 @@ static VOID config_load_all_entries( const CHAR16 *loaded_image_path, EFI_FILE *root_dir) { - UINT64 osind = 0; - assert(config); assert(loaded_image); assert(loaded_image_path); @@ -2456,13 +2453,11 @@ static VOID config_load_all_entries( config_entry_add_loader_auto(config, loaded_image->DeviceHandle, root_dir, loaded_image_path, L"auto-efi-default", '\0', L"EFI Default Loader", NULL); - if (config->auto_firmware && efivar_get_uint64_le(EFI_GLOBAL_GUID, L"OsIndicationsSupported", &osind) == EFI_SUCCESS) { - if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI) - config_entry_add_call(config, - L"auto-reboot-to-firmware-setup", - L"Reboot Into Firmware Interface", - reboot_into_firmware); - } + if (config->auto_firmware && (get_os_indications_supported() & EFI_OS_INDICATIONS_BOOT_TO_FW_UI)) + config_entry_add_call(config, + L"auto-reboot-to-firmware-setup", + L"Reboot Into Firmware Interface", + reboot_into_firmware); } EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c index e500069d51..13697c9433 100644 --- a/src/boot/efi/util.c +++ b/src/boot/efi/util.c @@ -743,3 +743,17 @@ EFI_STATUS open_directory( *ret = TAKE_PTR(dir); return EFI_SUCCESS; } + +UINT64 get_os_indications_supported(VOID) { + UINT64 osind; + EFI_STATUS err; + + /* Returns the supported OS indications. If we can't acquire it, returns a zeroed out mask, i.e. no + * supported features. */ + + err = efivar_get_uint64_le(EFI_GLOBAL_GUID, L"OsIndicationsSupported", &osind); + if (EFI_ERROR(err)) + return 0; + + return osind; +} diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h index ca95514d72..fa17b18f42 100644 --- a/src/boot/efi/util.h +++ b/src/boot/efi/util.h @@ -144,3 +144,5 @@ static inline void *PHYSICAL_ADDRESS_TO_POINTER(EFI_PHYSICAL_ADDRESS addr) { return (void*) (UINTN) addr; } + +UINT64 get_os_indications_supported(VOID); From 3f9a615dcfe59a13c224f98ad4616d0c6211c2be Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 21 Sep 2021 16:52:24 +0200 Subject: [PATCH 21/24] man: add man page for the systemd UEFI stub Fixes: #17215 --- man/rules/meson.build | 4 + man/systemd-boot.xml | 7 ++ man/systemd-stub.xml | 204 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 215 insertions(+) create mode 100644 man/systemd-stub.xml diff --git a/man/rules/meson.build b/man/rules/meson.build index c6f343d8e5..f9c69f1846 100644 --- a/man/rules/meson.build +++ b/man/rules/meson.build @@ -954,6 +954,10 @@ manpages = [ ['systemd-sleep.conf', '5', ['sleep.conf.d'], ''], ['systemd-socket-activate', '1', [], ''], ['systemd-socket-proxyd', '8', [], ''], + ['systemd-stub', + '7', + ['linuxaa64.efi.stub', 'linuxia32.efi.stub', 'linuxx64.efi.stub'], + 'ENABLE_EFI'], ['systemd-suspend.service', '8', ['systemd-hibernate.service', diff --git a/man/systemd-boot.xml b/man/systemd-boot.xml index 8685ed50ff..83b95efa3e 100644 --- a/man/systemd-boot.xml +++ b/man/systemd-boot.xml @@ -102,6 +102,12 @@ may be used to copy kernel images onto the ESP or the Extended Boot Loader Partition and to generate description files compliant with the Boot Loader Specification. + + systemd-stub7 + may be used as UEFI boot stub for executed kernels, which is useful to show graphical boot splashes + before transitioning into the Linux world. It is also capable of automatically picking up auxiliary + credential files (for boot parameterization) and system extension images, as companion files to the + booted kernel images. @@ -493,6 +499,7 @@ systemd-bless-boot.service8, systemd-boot-system-token.service8, kernel-install8, + systemd-stub7, Boot Loader Specification, Boot Loader Interface diff --git a/man/systemd-stub.xml b/man/systemd-stub.xml new file mode 100644 index 0000000000..2edc64ce17 --- /dev/null +++ b/man/systemd-stub.xml @@ -0,0 +1,204 @@ + + + + + + + systemd-stub + systemd + + + + systemd-stub + 7 + + + + systemd-stub + linuxx64.efi.stub + linuxia32.efi.stub + linuxaa64.efi.stub + A simple UEFI kernel boot stub + + + + /usr/lib/systemd/boot/efi/linuxx64.efi.stub + /usr/lib/systemd/boot/efi/linuxia32.efi.stub + /usr/lib/systemd/boot/efi/linuxaa64.efi.stub + + + + Description + + systemd-stub (stored in per-architecture files + linuxx64.efi.stub, linuxia32.efi.stub, + linuxaa64.efi.stub on disk) is a simple UEFI boot stub. An UEFI boot stub is + attached to a Linux kernel binary image, and is a piece of code that runs in the UEFI firmware + environment before transitioning into the Linux kernel environment. The UEFI boot stub ensures a Linux + kernel is executable as regular UEFI binary, and is able to do various preparations before switching the + system into the Linux world. + + 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, which may then be signed + via UEFI SecureBoot as a whole, covering all individual resources at once. Specifically it may + include: + + + The ELF Linux kernel images will be looked for in the .linux PE + section of the executed image. + + The initial RAM disk (initrd) will be looked for in the .initrd PE + section. + + The kernel command line to pass to the invoked kernel will be looked for in the + .cmdline PE section. + + A boot splash (in Windows .BMP format) to show on screen before + invoking the kernel will be looked for in the .splash PE 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 8 (if a TPM is + present). + + + + Companion Files + + The systemd-stub UEFI boot stub automatically collects two types of auxiliary + companion files optionally placed in a drop-in directory next to the EFI binary and dynamically generates + cpio initrd archives from them, and passes them to the kernel. Specifically: + + + For a kernel binary called foo.efi it + will look for files with the .cred suffix in a directory named + foo.efi.extra.d/, next to it. A cpio + archive is generated from all files found that way, placing them in the + /.extra/credentials/ directory of the initrd file hierarchy. The main initrd may + then access them in this directory. This is supposed to be used to store auxiliary, encrypted, + authenticated credentials for use with LoadCredentialEncrypted= in the UEFI System + Partition. See + systemd.exec5 for + details on encrypted credentials. The generated cpio archive is measured into TPM + PCR 4 (if a TPM is present) + + Similar, files foo.efi.extra.d/*.raw + are packed up as cpio archive and placed in the /.extra/sysext/ + directory in the initrd file hierarchy. This is supposed to be used to pass additional system extension + images to the initrd. See + systemd-sysext8 for + details on system extension images. The generated cpio archive containing these + system extension images is measured into TPM PCR 8 (if a TPM is present). + + + Both mechanisms may be used to parameterize and extend trusted (i.e. signed), immutable initrd + images in a reasonably safe way: all data they contain is measured into TPM PCRs. On access they should be + further validated: in case of the credentials case by encrypting/authenticating them via TPM, as exposed + by systemd-creds encrypt -T (see + systemd-creds1 for + details); in case of the system extension images by using signed Verity images. + + + + EFI Variables + + The following EFI variables are defined, set and read by systemd-stub, under the + vendor UUID 4a67b082-0a4c-41cf-b6c7-440b29bb8c4f, for communication between the boot + stub and the OS: + + + + LoaderDevicePartUUID + + Contains the partition UUID of the EFI System Partition the EFI image was run + from. systemd-gpt-auto-generator8 + uses this information to automatically find the disk booted from, in order to discover various other + partitions on the same disk automatically. + + + + LoaderFirmwareInfo + LoaderFirmwareType + + Brief firmware information. Use + bootctl1 to view this + data. + + + + LoaderImageIdentifier + + The path of EFI executable, relative to the EFI System Partition's root + directory. Use + bootctl1 to view + this data. + + + + StubInfo + + Brief stub information. Use + bootctl1 to view + this data. + + + + Note that some of the variables above may also be set by the boot loader. The stub will only set + them if they aren't set already. Some of these variables are defined by the Boot Loader Interface. + + + + Assembling Kernel Images + + In order to assemble an UEFI PE kernel image from various components as described above, use an + objcopy1 command line + like this: + + objcopy \ + --add-section .osrel=os-release --change-section-vma .osrel=0x20000 \ + --add-section .cmdline=cmdline.txt --change-section-vma .cmdline=0x30000 \ + --add-section .splash=splash.bmp --change-section-vma .splash=0x40000 \ + --add-section .linux=vmlinux --change-section-vma .linux=0x2000000 \ + --add-section .initrd=initrd.cpio --change-section-vma .initrd=0x3000000 \ + /usr/lib/systemd/boot/efi/linuxx64.efi.stub \ + foo-unsigned.efi + + This generates one PE executable file foo-unsigned.efi from the six individual + files for OS release information, kernel command line, boot splash image, kernel image, main initrd and + UEFI boot stub. + + To then sign the resulting image for UEFI SecureBoot use an + sbsign1 command like + the following: + + sbsign \ + --key mykey.pem \ + --cert mykey.crt \ + --output foo.efi \ + foo-unsigned.efi + + This expects a pair of X.509 private key and certificate as parameters and then signs the UEFI PE + executable we generated above for UEFI SecureBoot and generates a signed UEFI PE executable as + result. + + + + See Also + + systemd-boot7, + systemd.exec5, + systemd-creds1, + systemd-sysext8, + Boot Loader Specification, + Boot Loader Interface, + objcopy1, + sbsign1 + + + From b8cdb6627900aa135d253308f04a13b84aa65dbb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 21 Sep 2021 16:52:57 +0200 Subject: [PATCH 22/24] man: the sd-boot menu is probably more "textual" then "graphical", hence don't lie (also, most communication happens between boot loader and OS, only seldom stuff goes the other way, hence mention that the boot loader first) --- man/systemd-boot.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/man/systemd-boot.xml b/man/systemd-boot.xml index 83b95efa3e..823f2d5440 100644 --- a/man/systemd-boot.xml +++ b/man/systemd-boot.xml @@ -25,7 +25,7 @@ Description systemd-boot (short: sd-boot) is a simple UEFI boot - manager. It provides a graphical menu to select the entry to boot and an editor for the kernel command + manager. It provides a textual menu to select the entry to boot and an editor for the kernel command line. systemd-boot supports systems with UEFI firmware only. systemd-boot loads boot entry information from the EFI system partition (ESP), @@ -296,9 +296,9 @@ EFI Variables - The following EFI variables are defined, set and read by systemd-boot, under the vendor - UUID 4a67b082-0a4c-41cf-b6c7-440b29bb8c4f, for communication between the OS and the boot - loader: + The following EFI variables are defined, set and read by systemd-boot, under the + vendor UUID 4a67b082-0a4c-41cf-b6c7-440b29bb8c4f, for communication between the boot + loader and the OS: From 99d51ed9391704da9b3397e395ec8829b6a737be Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 21 Sep 2021 17:28:16 +0200 Subject: [PATCH 23/24] man: document the new systemd-boot drop-in driver dir logic --- man/systemd-boot.xml | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/man/systemd-boot.xml b/man/systemd-boot.xml index 823f2d5440..b7b06bd5eb 100644 --- a/man/systemd-boot.xml +++ b/man/systemd-boot.xml @@ -280,17 +280,29 @@ usually mounted to /efi/, /boot/ or /boot/efi/ during OS runtime. It also processes files on the Extended Boot Loader partition which is typically mounted to /boot/, if it - exists. systemd-boot reads runtime configuration such as the boot timeout and default + exists. + + systemd-boot reads runtime configuration such as the boot timeout and default entry from /loader/loader.conf on the ESP (in combination with data read from EFI variables). See - loader.conf5. Boot - entry description files following the Boot - Loader Specification are read from /loader/entries/ on the ESP and the - Extended Boot Loader partition. Unified kernel boot entries following the loader.conf5. + + Boot entry description files following the Boot Loader Specification are read from - /EFI/Linux/ on the ESP and the Extended Boot Loader partition. Optionally, a random - seed for early boot entropy pool provisioning is stored in /loader/random-seed in - the ESP. + /loader/entries/ on the ESP and the Extended Boot Loader partition. + + Unified kernel boot entries following the Boot Loader Specification are read from + /EFI/Linux/ on the ESP and the Extended Boot Loader partition. + + Optionally, a random seed for early boot entropy pool provisioning is stored in + /loader/random-seed in the ESP. + + During initialization, sd-boot automatically loads all driver files placed in + the /EFI/systemd/drivers/ directory of the ESP. The files placed there must have an + extension of the EFI architecture ID followed by .efi (e.g. for x86-64 this means a + suffix of x64.efi). This may be used to automatically load file system drivers and + similar, to extend the native firmware support. From 8e8415e0d5d6714491180abe4e7d058f93bdf1c9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 23 Sep 2021 17:48:26 +0200 Subject: [PATCH 24/24] boot: prefer IMAGE_VERSION from os-release as version string If the field exists it's probably the best version we have for sorting, since it will change on every single OS image update. --- src/boot/efi/boot.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index b0e16f7106..d82d679a47 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -1956,7 +1956,7 @@ static VOID config_entry_add_linux( for (;;) { _cleanup_freepool_ CHAR16 *os_name_pretty = NULL, *os_name = NULL, *os_id = NULL, - *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL; + *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL, *os_image_version = NULL; _cleanup_freepool_ CHAR8 *content = NULL; const CHAR8 *sections[] = { (CHAR8 *)".osrel", @@ -2028,16 +2028,28 @@ static VOID config_entry_add_linux( os_build_id = stra_to_str(value); continue; } + + if (strcmpa((const CHAR8*) "IMAGE_VERSION", key) == 0) { + FreePool(os_image_version); + os_image_version = stra_to_str(value); + continue; + } } - if ((os_name_pretty || os_name) && os_id && (os_version || os_version_id || os_build_id)) { + if ((os_name_pretty || os_name) && os_id && (os_image_version || os_version || os_version_id || os_build_id)) { _cleanup_freepool_ CHAR16 *path = NULL; path = PoolPrint(L"\\EFI\\Linux\\%s", f->FileName); - entry = config_entry_add_loader(config, device, LOADER_LINUX, f->FileName, 'l', - os_name_pretty ?: os_name, path, - os_version ?: (os_version_id ? : os_build_id)); + entry = config_entry_add_loader( + config, + device, + LOADER_LINUX, + f->FileName, + /* key= */ 'l', + os_name_pretty ?: os_name, + path, + os_image_version ?: (os_version ?: (os_version_id ? : os_build_id))); FreePool(content); content = NULL;