Merge pull request #26258 from DaanDeMeyer/boot-smbios

stub: Read extra kernel command line items from SMBIOS
This commit is contained in:
Daan De Meyer
2023-02-23 15:59:18 +01:00
committed by GitHub
7 changed files with 143 additions and 21 deletions

View File

@@ -379,6 +379,23 @@
default, this is done for the TPM2 PCR signature and public key files.</para>
</refsect1>
<refsect1>
<title>SMBIOS Type 11 Strings</title>
<para><command>systemd-stub</command> can be configured using SMBIOS Type 11 strings. Applicable strings
consist of a name, followed by <literal>=</literal>, followed by the value.
<command>systemd-stub</command> will search the table for a string with a specific name, and if found,
use its value. The following strings are read:</para>
<variablelist>
<varlistentry>
<term><varname>io.systemd.stub.kernel-cmdline-extra</varname></term>
<listitem><para>If set, the value of this string is added to the list of kernel command line
arguments that are passed to the kernel.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Assembling Kernel Images</title>

View File

@@ -112,7 +112,7 @@ DEFINE_STRCPY(char16_t, strcpy16);
s++; \
} \
\
return NULL; \
return c ? NULL : (type *) s; \
}
DEFINE_STRCHR(char, strchr8);
@@ -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);
@@ -863,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;

View File

@@ -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);
@@ -151,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
@@ -164,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);

View File

@@ -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,

View File

@@ -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) {
@@ -351,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)); \
@@ -610,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);

View File

@@ -184,17 +184,23 @@ typedef struct {
uint8_t bios_characteristics_ext[2];
} _packed_ SmbiosTableType0;
static void *find_smbios_configuration_table(uint64_t *ret_size) {
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);
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;
@@ -204,9 +210,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 *ret_size_left) {
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;
@@ -214,7 +220,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)
@@ -223,8 +229,11 @@ static 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;
@@ -232,22 +241,18 @@ static SmbiosHeader *get_smbios_table(uint8_t type) {
/* 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;
}
}
@@ -256,7 +261,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, NULL);
if (!type0 || type0->header.length < sizeof(SmbiosTableType0))
return false;
@@ -272,3 +277,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;
}

View File

@@ -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);