From bbef5a9617e91b4b1bc30266eb9dcbda395a8c61 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Mon, 30 Jan 2023 16:22:10 +0100 Subject: [PATCH 1/7] efi-string: Fix strchr() null byte handling strchr() should be able to search for the terminating null byte, our implementation doesn't, let's fix that. --- src/boot/efi/efi-string.c | 2 +- src/boot/efi/test-efi-string.c | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/boot/efi/efi-string.c b/src/boot/efi/efi-string.c index ee3dc1c4a9..dd71123e9f 100644 --- a/src/boot/efi/efi-string.c +++ b/src/boot/efi/efi-string.c @@ -112,7 +112,7 @@ DEFINE_STRCPY(char16_t, strcpy16); s++; \ } \ \ - return NULL; \ + return c ? NULL : (type *) s; \ } DEFINE_STRCHR(char, strchr8); diff --git a/src/boot/efi/test-efi-string.c b/src/boot/efi/test-efi-string.c index c26973d8bd..c7e42c7b94 100644 --- a/src/boot/efi/test-efi-string.c +++ b/src/boot/efi/test-efi-string.c @@ -229,6 +229,8 @@ TEST(strchr8) { assert_se(strchr8(str, 'a') == &str[0]); assert_se(strchr8(str, 'c') == &str[2]); assert_se(strchr8(str, 'B') == &str[4]); + + assert_se(strchr8(str, 0) == str + strlen8(str)); } TEST(strchr16) { @@ -240,6 +242,8 @@ TEST(strchr16) { assert_se(strchr16(str, 'a') == &str[0]); assert_se(strchr16(str, 'c') == &str[2]); assert_se(strchr16(str, 'B') == &str[4]); + + assert_se(strchr16(str, 0) == str + strlen16(str)); } TEST(xstrndup8) { From ad36d31ea578622883c3b5297c971374096a504a Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Mon, 30 Jan 2023 16:25:23 +0100 Subject: [PATCH 2/7] efi-string: Add startswith8() startswith() from string-util-fundamental.h is defined for sd_char which is char16_t, so let's add an implementation for char as well. --- src/boot/efi/efi-string.c | 15 +++++++++++++++ src/boot/efi/efi-string.h | 2 ++ src/boot/efi/test-efi-string.c | 12 ++++++++++++ 3 files changed, 29 insertions(+) diff --git a/src/boot/efi/efi-string.c b/src/boot/efi/efi-string.c index dd71123e9f..911579ed5b 100644 --- a/src/boot/efi/efi-string.c +++ b/src/boot/efi/efi-string.c @@ -212,6 +212,21 @@ char16_t *xstrn8_to_16(const char *str8, size_t n) { return str16; } +char *startswith8(const char *s, const char *prefix) { + size_t l; + + assert(prefix); + + if (!s) + return NULL; + + l = strlen8(prefix); + if (!strneq8(s, prefix, l)) + return NULL; + + return (char*) s + l; +} + static bool efi_fnmatch_prefix(const char16_t *p, const char16_t *h, const char16_t **ret_p, const char16_t **ret_h) { assert(p); assert(h); diff --git a/src/boot/efi/efi-string.h b/src/boot/efi/efi-string.h index 6b6b0d57b4..0927bbf82d 100644 --- a/src/boot/efi/efi-string.h +++ b/src/boot/efi/efi-string.h @@ -101,6 +101,8 @@ static inline char16_t *xstr8_to_16(const char *str8) { return xstrn8_to_16(str8, strlen8(str8)); } +char *startswith8(const char *s, const char *prefix); + bool efi_fnmatch(const char16_t *pattern, const char16_t *haystack); bool parse_number8(const char *s, uint64_t *ret_u, const char **ret_tail); diff --git a/src/boot/efi/test-efi-string.c b/src/boot/efi/test-efi-string.c index c7e42c7b94..be7f8f9b1c 100644 --- a/src/boot/efi/test-efi-string.c +++ b/src/boot/efi/test-efi-string.c @@ -355,6 +355,18 @@ TEST(xstrn8_to_16) { free(s); } +TEST(startswith8) { + assert_se(streq8(startswith8("", ""), "")); + assert_se(streq8(startswith8("x", ""), "x")); + assert_se(!startswith8("", "x")); + assert_se(!startswith8("", "xxxxxxxx")); + assert_se(streq8(startswith8("xxx", "x"), "xx")); + assert_se(streq8(startswith8("xxx", "xx"), "x")); + assert_se(streq8(startswith8("xxx", "xxx"), "")); + assert_se(!startswith8("xxx", "xxxx")); + assert_se(!startswith8(NULL, "")); +} + #define TEST_FNMATCH_ONE(pattern, haystack, expect) \ ({ \ assert_se(fnmatch(pattern, haystack, 0) == (expect ? 0 : FNM_NOMATCH)); \ From e71f0f63da87fb8043f665a142261bc393fe0216 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Tue, 31 Jan 2023 15:39:40 +0100 Subject: [PATCH 3/7] efi-string: Add efi_memchr() --- src/boot/efi/efi-string.c | 14 ++++++++++++++ src/boot/efi/efi-string.h | 2 ++ src/boot/efi/test-efi-string.c | 13 +++++++++++++ 3 files changed, 29 insertions(+) diff --git a/src/boot/efi/efi-string.c b/src/boot/efi/efi-string.c index 911579ed5b..a94e2e4c17 100644 --- a/src/boot/efi/efi-string.c +++ b/src/boot/efi/efi-string.c @@ -878,16 +878,30 @@ char16_t *xvasprintf_status(EFI_STATUS status, const char *format, va_list ap) { #if SD_BOOT /* To provide the actual implementation for these we need to remove the redirection to the builtins. */ +# undef memchr # undef memcmp # undef memcpy # undef memset #else /* And for userspace unit testing we need to give them an efi_ prefix. */ +# define memchr efi_memchr # define memcmp efi_memcmp # define memcpy efi_memcpy # define memset efi_memset #endif +_used_ void *memchr(const void *p, int c, size_t n) { + if (!p || n == 0) + return NULL; + + const uint8_t *q = p; + for (size_t i = 0; i < n; i++) + if (q[i] == (unsigned char) c) + return (void *) (q + i); + + return NULL; +} + _used_ int memcmp(const void *p1, const void *p2, size_t n) { const uint8_t *up1 = p1, *up2 = p2; int r; diff --git a/src/boot/efi/efi-string.h b/src/boot/efi/efi-string.h index 0927bbf82d..410bfd8ef5 100644 --- a/src/boot/efi/efi-string.h +++ b/src/boot/efi/efi-string.h @@ -153,6 +153,7 @@ _gnu_printf_(2, 0) _warn_unused_result_ char16_t *xvasprintf_status(EFI_STATUS s * compiling with -ffreestanding. By referring to builtins, the compiler can check arguments and do * optimizations again. Note that we still need to provide implementations as the compiler is free to not * inline its own implementation and instead issue a library call. */ +# define memchr __builtin_memchr # define memcmp __builtin_memcmp # define memcpy __builtin_memcpy # define memset __builtin_memset @@ -166,6 +167,7 @@ static inline void *mempcpy(void * restrict dest, const void * restrict src, siz #else /* For unit testing. */ +void *efi_memchr(const void *p, int c, size_t n); int efi_memcmp(const void *p1, const void *p2, size_t n); void *efi_memcpy(void * restrict dest, const void * restrict src, size_t n); void *efi_memset(void *p, int c, size_t n); diff --git a/src/boot/efi/test-efi-string.c b/src/boot/efi/test-efi-string.c index be7f8f9b1c..d214b1536e 100644 --- a/src/boot/efi/test-efi-string.c +++ b/src/boot/efi/test-efi-string.c @@ -626,6 +626,19 @@ TEST(xvasprintf_status) { s = mfree(s); } +TEST(efi_memchr) { + assert_se(streq8(efi_memchr("abcde", 'c', 5), "cde")); + assert_se(streq8(efi_memchr("abcde", 'c', 3), "cde")); + assert_se(streq8(efi_memchr("abcde", 'c', 2), NULL)); + assert_se(streq8(efi_memchr("abcde", 'c', 7), "cde")); + assert_se(streq8(efi_memchr("abcde", 'q', 5), NULL)); + assert_se(streq8(efi_memchr("abcde", 'q', 0), NULL)); + /* Test that the character is interpreted as unsigned char. */ + assert_se(streq8(efi_memchr("abcde", 'a', 6), efi_memchr("abcde", 'a' + 0x100, 6))); + assert_se(streq8(efi_memchr("abcde", 0, 6), "")); + assert_se(efi_memchr(NULL, 0, 0) == NULL); +} + TEST(efi_memcmp) { assert_se(efi_memcmp(NULL, NULL, 0) == 0); assert_se(efi_memcmp(NULL, NULL, 1) == 0); From 761f62fe98cab82a3742bdae49f79626ede2ceaf Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Mon, 30 Jan 2023 21:15:12 +0100 Subject: [PATCH 4/7] vmm: Add more const SMBIOS tables are immutable, so let's access it via const pointers where possible. --- src/boot/efi/vmm.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/boot/efi/vmm.c b/src/boot/efi/vmm.c index c3816e1b2d..c526ca0854 100644 --- a/src/boot/efi/vmm.c +++ b/src/boot/efi/vmm.c @@ -204,17 +204,17 @@ typedef struct { uint8_t bios_characteristics_ext[2]; } _packed_ SmbiosTableType0; -static void *find_smbios_configuration_table(uint64_t *ret_size) { +static const void *find_smbios_configuration_table(uint64_t *ret_size) { assert(ret_size); - Smbios3EntryPoint *entry3 = find_configuration_table(MAKE_GUID_PTR(SMBIOS3_TABLE)); + const Smbios3EntryPoint *entry3 = find_configuration_table(MAKE_GUID_PTR(SMBIOS3_TABLE)); if (entry3 && memcmp(entry3->anchor_string, "_SM3_", 5) == 0 && entry3->entry_point_length <= sizeof(*entry3)) { *ret_size = entry3->table_maximum_size; return PHYSICAL_ADDRESS_TO_POINTER(entry3->table_address); } - SmbiosEntryPoint *entry = find_configuration_table(MAKE_GUID_PTR(SMBIOS_TABLE)); + const SmbiosEntryPoint *entry = find_configuration_table(MAKE_GUID_PTR(SMBIOS_TABLE)); if (entry && memcmp(entry->anchor_string, "_SM_", 4) == 0 && entry->entry_point_length <= sizeof(*entry)) { *ret_size = entry->table_length; @@ -224,9 +224,9 @@ static void *find_smbios_configuration_table(uint64_t *ret_size) { return NULL; } -static SmbiosHeader *get_smbios_table(uint8_t type) { +static const SmbiosHeader *get_smbios_table(uint8_t type) { uint64_t size = 0; - uint8_t *p = find_smbios_configuration_table(&size); + const uint8_t *p = find_smbios_configuration_table(&size); if (!p) return false; @@ -234,7 +234,7 @@ static SmbiosHeader *get_smbios_table(uint8_t type) { if (size < sizeof(SmbiosHeader)) return NULL; - SmbiosHeader *header = (SmbiosHeader *) p; + const SmbiosHeader *header = (const SmbiosHeader *) p; /* End of table. */ if (header->type == 127) @@ -276,7 +276,7 @@ static SmbiosHeader *get_smbios_table(uint8_t type) { static bool smbios_in_hypervisor(void) { /* Look up BIOS Information (Type 0). */ - SmbiosTableType0 *type0 = (SmbiosTableType0 *) get_smbios_table(0); + const SmbiosTableType0 *type0 = (const SmbiosTableType0 *) get_smbios_table(0); if (!type0 || type0->header.length < sizeof(SmbiosTableType0)) return false; From a885188b3ab71c222cbcc42b083ba671884aa651 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Mon, 30 Jan 2023 16:26:14 +0100 Subject: [PATCH 5/7] vmm: Add smbios_find_oem_string() This function can be used to find SMBIOS strings in the SMBIOS Type 11 table. --- src/boot/efi/vmm.c | 44 +++++++++++++++++++++++++++++++++++++++++--- src/boot/efi/vmm.h | 2 ++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/boot/efi/vmm.c b/src/boot/efi/vmm.c index c526ca0854..1e6d4de4a6 100644 --- a/src/boot/efi/vmm.c +++ b/src/boot/efi/vmm.c @@ -204,6 +204,12 @@ typedef struct { uint8_t bios_characteristics_ext[2]; } _packed_ SmbiosTableType0; +typedef struct { + SmbiosHeader header; + uint8_t count; + char contents[]; +} _packed_ SmbiosTableType11; + static const void *find_smbios_configuration_table(uint64_t *ret_size) { assert(ret_size); @@ -224,7 +230,7 @@ static const void *find_smbios_configuration_table(uint64_t *ret_size) { return NULL; } -static const SmbiosHeader *get_smbios_table(uint8_t type) { +static const SmbiosHeader *get_smbios_table(uint8_t type, uint64_t *ret_size_left) { uint64_t size = 0; const uint8_t *p = find_smbios_configuration_table(&size); if (!p) @@ -243,8 +249,11 @@ static const SmbiosHeader *get_smbios_table(uint8_t type) { if (size < header->length) return NULL; - if (header->type == type) + if (header->type == type) { + if (ret_size_left) + *ret_size_left = size; return header; /* Yay! */ + } /* Skip over formatted area. */ size -= header->length; @@ -276,7 +285,7 @@ static const SmbiosHeader *get_smbios_table(uint8_t type) { static bool smbios_in_hypervisor(void) { /* Look up BIOS Information (Type 0). */ - const SmbiosTableType0 *type0 = (const SmbiosTableType0 *) get_smbios_table(0); + const SmbiosTableType0 *type0 = (const SmbiosTableType0 *) get_smbios_table(0, NULL); if (!type0 || type0->header.length < sizeof(SmbiosTableType0)) return false; @@ -292,3 +301,32 @@ bool in_hypervisor(void) { cache = cpuid_in_hypervisor() || smbios_in_hypervisor(); return cache; } + +const char* smbios_find_oem_string(const char *name) { + uint64_t left; + + assert(name); + + const SmbiosTableType11 *type11 = (const SmbiosTableType11 *) get_smbios_table(11, &left); + if (!type11 || type11->header.length < sizeof(SmbiosTableType11)) + return NULL; + + assert(left >= type11->header.length); + + const char *s = type11->contents; + left -= type11->header.length; + + for (const char *p = s; p < s + left; ) { + const char *e = memchr(p, 0, s + left - p); + if (!e || e == p) /* Double NUL byte means we've reached the end of the OEM strings. */ + break; + + const char *eq = startswith8(p, name); + if (eq && *eq == '=') + return eq + 1; + + p = e + 1; + } + + return NULL; +} diff --git a/src/boot/efi/vmm.h b/src/boot/efi/vmm.h index 32f4fa3345..061ad692ec 100644 --- a/src/boot/efi/vmm.h +++ b/src/boot/efi/vmm.h @@ -7,3 +7,5 @@ bool is_direct_boot(EFI_HANDLE device); EFI_STATUS vmm_open(EFI_HANDLE *ret_qemu_dev, EFI_FILE **ret_qemu_dir); bool in_hypervisor(void); + +const char* smbios_find_oem_string(const char *name); From 717af0de4648ccc223f06683a6baf73d64271e02 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Mon, 30 Jan 2023 16:26:50 +0100 Subject: [PATCH 6/7] stub: Read extra kernel command line items from SMBIOS Let's read more kernel command line arguments from SMBIOS OEM string io.systemd.stub.kernel-cmdline-extra. This allows adding debug kernel command line arguments when booting in qemy without having to modify the UKI. --- man/systemd-stub.xml | 17 +++++++++++++++++ src/boot/efi/stub.c | 7 +++++++ 2 files changed, 24 insertions(+) diff --git a/man/systemd-stub.xml b/man/systemd-stub.xml index c230168780..7934f344f8 100644 --- a/man/systemd-stub.xml +++ b/man/systemd-stub.xml @@ -379,6 +379,23 @@ default, this is done for the TPM2 PCR signature and public key files. + + SMBIOS Type 11 Strings + + systemd-stub can be configured using SMBIOS Type 11 strings. Applicable strings + consist of a name, followed by =, followed by the value. + systemd-stub will search the table for a string with a specific name, and if found, + use its value. The following strings are read: + + + + io.systemd.stub.kernel-cmdline-extra + If set, the value of this string is added to the list of kernel command line + arguments that are passed to the kernel. + + + + Assembling Kernel Images diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index d85424ceae..25fd1bdb7f 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -14,6 +14,7 @@ #include "splash.h" #include "tpm-pcr.h" #include "util.h" +#include "vmm.h" /* magic string to find in the binary image */ _used_ _section_(".sdmagic") static const char magic[] = "#### LoaderInfo: systemd-stub " GIT_VERSION " ####"; @@ -275,6 +276,12 @@ static EFI_STATUS run(EFI_HANDLE image) { mangle_stub_cmdline(cmdline); } + const char *extra = smbios_find_oem_string("io.systemd.stub.kernel-cmdline-extra"); + if (extra) { + _cleanup_free_ char16_t *tmp = TAKE_PTR(cmdline), *extra16 = xstr8_to_16(extra); + cmdline = xasprintf("%ls %ls", tmp, extra16); + } + export_variables(loaded_image); if (pack_cpio(loaded_image, From c8e5d82c97a1478b15d2f97ffebd9591e81663ba Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Wed, 22 Feb 2023 17:04:58 +0100 Subject: [PATCH 7/7] vmm: Modernize get_smbios_table() --- src/boot/efi/vmm.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/boot/efi/vmm.c b/src/boot/efi/vmm.c index 1e6d4de4a6..cfdd5b01a9 100644 --- a/src/boot/efi/vmm.c +++ b/src/boot/efi/vmm.c @@ -261,22 +261,18 @@ static const SmbiosHeader *get_smbios_table(uint8_t type, uint64_t *ret_size_lef /* Skip over string table. */ for (;;) { - while (size > 0 && *p != '\0') { + const uint8_t *e = memchr(p, 0, size); + if (!e) + return NULL; + + if (e == p) {/* Double NUL byte means we've reached the end of the string table. */ p++; size--; - } - if (size == 0) - return NULL; - p++; - size--; - - /* Double NUL terminates string table. */ - if (*p == '\0') { - if (size == 0) - return NULL; - p++; break; } + + size -= e + 1 - p; + p = e + 1; } }