mirror of
https://github.com/Dasharo/systemd.git
synced 2026-03-06 15:02:31 -08:00
Merge pull request #26258 from DaanDeMeyer/boot-smbios
stub: Read extra kernel command line items from SMBIOS
This commit is contained in:
@@ -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>
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user