sd-boot: also read type #1 entries from SMBIOS Type #11

With this we can now do:

systemd-vmspawn -n -i foobar.raw -s io.systemd.boot.entries-extra:particleos-current.conf=$'title ParticleOS Current\nuki-url http://example.com/somedir/uki.efi'

Assuming sd-boot is available inside the ESP of foobar.raw a new item
will show up in the boot menu that allows booting directly into the
specified UKI.
This commit is contained in:
Lennart Poettering
2025-02-21 10:04:15 +01:00
parent fab0f6e0fc
commit 4f3a3b7695
6 changed files with 93 additions and 14 deletions
+10
View File
@@ -74,6 +74,16 @@
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>io.systemd.boot-entries.extra:</varname><replaceable>ID=DEFINITION</replaceable></term>
<listitem><para>This allows inserting additional entries into the <command>systemd-boot</command>
menu. For details see
<citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry></para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
</variablelist>
</refsect1>
+22
View File
@@ -594,6 +594,28 @@
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>io.systemd.boot-entries.extra:</varname><replaceable>ID=DEFINITION</replaceable></term>
<listitem><para>This allows inserting additional entries into the <command>systemd-boot</command>
menu. Take a pair of menu entry identifier and menu entry definition string. The former should be
suitable for use as a filename of a Boot Loader Specification Type #1 entry filename (note that it is
used for identification purposes only, no file of this name is actually accessed), the latter shall
follow the syntax of the contents of a Type #1 entry. Any menu entry defined this way is processed
and shown in pretty much the same way as a Type #1 entry read from the ESP or XBOOTLDR
partition. Example:</para>
<programlisting>io.systemd.boot-entries.extra:fooos-current.conf=title FooOS (Current)
uki-url http://example.com/somedir/fooos.efi</programlisting>
<para>Note that this example contains a newline character. When generating this string from a shell
care must be taken to encode it correctly.</para>
<para>Pass multiple strings formatted this way to generate multiple menu entries.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
</variablelist>
</refsect1>
+55 -8
View File
@@ -1464,7 +1464,6 @@ static void boot_entry_add_type1(
assert(config);
assert(device);
assert(root_dir);
assert(path);
assert(file);
assert(content);
@@ -1607,7 +1606,8 @@ static void boot_entry_add_type1(
config_add_entry(config, entry);
boot_entry_parse_tries(entry, path, file, u".conf");
if (path)
boot_entry_parse_tries(entry, path, file, u".conf");
TAKE_PTR(entry);
}
@@ -1717,6 +1717,19 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) {
(void) efivar_get_str16(MAKE_GUID_PTR(LOADER), u"LoaderEntryLastBooted", &config->entry_saved);
}
static bool valid_type1_filename(const char16_t *fname) {
assert(fname);
if (IN_SET(fname[0], u'.', u'\0'))
return false;
if (!endswith_no_case(fname, u".conf"))
return false;
if (startswith_no_case(fname, u"auto-"))
return false;
return true;
}
static void config_load_type1_entries(
Config *config,
EFI_HANDLE *device,
@@ -1747,13 +1760,9 @@ static void config_load_type1_entries(
if (err != EFI_SUCCESS || !f)
break;
if (f->FileName[0] == '.')
continue;
if (FLAGS_SET(f->Attribute, EFI_FILE_DIRECTORY))
continue;
if (!endswith_no_case(f->FileName, u".conf"))
continue;
if (startswith_no_case(f->FileName, u"auto-"))
if (!valid_type1_filename(f->FileName))
continue;
err = file_read(entries_dir,
@@ -1769,6 +1778,41 @@ static void config_load_type1_entries(
}
}
static void config_load_smbios_entries(
Config *config,
EFI_HANDLE *device,
EFI_FILE *root_dir,
const char16_t *loaded_image_path) {
assert(config);
assert(device);
assert(root_dir);
/* Loads Boot Loader Type #1 entries from SMBIOS 11 */
if (is_confidential_vm())
return; /* Don't consume SMBIOS in CoCo contexts */
for (const char *after = NULL, *extra;; after = extra) {
extra = smbios_find_oem_string("io.systemd.boot.entries-extra:", after);
if (!extra)
break;
const char *eq = strchr8(extra, '=');
if (!eq)
continue;
_cleanup_free_ char16_t *fname = xstrn8_to_16(extra, eq - extra);
if (!valid_type1_filename(fname))
continue;
/* Make a copy, since boot_entry_add_type1() wants to modify it */
_cleanup_free_ char *contents = xstrdup8(eq + 1);
boot_entry_add_type1(config, device, root_dir, /* path= */ NULL, fname, contents, loaded_image_path);
}
}
static int boot_entry_compare(const BootEntry *a, const BootEntry *b) {
int r;
@@ -2775,7 +2819,7 @@ static EFI_STATUS image_start(
_cleanup_free_ char16_t *options = xstrdup16(options_initrd ?: entry->options_implied ? NULL : entry->options);
if (entry->type == LOADER_LINUX && !is_confidential_vm()) {
const char *extra = smbios_find_oem_string("io.systemd.boot.kernel-cmdline-extra");
const char *extra = smbios_find_oem_string("io.systemd.boot.kernel-cmdline-extra=", /* after= */ NULL);
if (extra) {
_cleanup_free_ char16_t *tmp = TAKE_PTR(options), *extra16 = xstr8_to_16(extra);
if (isempty(tmp))
@@ -2989,6 +3033,9 @@ static void config_load_all_entries(
/* Similar, but on any XBOOTLDR partition */
config_load_xbootldr(config, loaded_image->DeviceHandle);
/* Pick up entries defined via SMBIOS Type #11 */
config_load_smbios_entries(config, loaded_image->DeviceHandle, root_dir, loaded_image_path);
/* Sort entries after version number */
sort_pointer_array((void **) config->entries, config->n_entries, (compare_pointer_func_t) boot_entry_compare);
+4 -4
View File
@@ -182,7 +182,7 @@ bool smbios_in_hypervisor(void) {
return FLAGS_SET(type0->bios_characteristics_ext[1], 1 << 4);
}
const char* smbios_find_oem_string(const char *name) {
const char* smbios_find_oem_string(const char *name, const char *after) {
uint64_t left;
assert(name);
@@ -199,9 +199,9 @@ const char* smbios_find_oem_string(const char *name) {
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;
const char *suffix = startswith8(p, name);
if (suffix && (!after || suffix > after))
return suffix;
p = e + 1;
}
+1 -1
View File
@@ -5,7 +5,7 @@
bool smbios_in_hypervisor(void);
const char* smbios_find_oem_string(const char *name);
const char* smbios_find_oem_string(const char *name, const char *after);
typedef struct RawSmbiosInfo {
const char *manufacturer;
+1 -1
View File
@@ -783,7 +783,7 @@ static void cmdline_append_and_measure_smbios(char16_t **cmdline, int *parameter
if (is_confidential_vm())
return;
const char *extra = smbios_find_oem_string("io.systemd.stub.kernel-cmdline-extra");
const char *extra = smbios_find_oem_string("io.systemd.stub.kernel-cmdline-extra=", /* after= */ NULL);
if (!extra)
return;