]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-boot: also read type #1 entries from SMBIOS Type #11
authorLennart Poettering <lennart@poettering.net>
Wed, 12 Feb 2025 08:31:29 +0000 (09:31 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 21 Feb 2025 09:04:15 +0000 (10:04 +0100)
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.

man/smbios-type-11.xml
man/systemd-boot.xml
src/boot/boot.c
src/boot/smbios.c
src/boot/smbios.h
src/boot/stub.c

index 4d78f612e7e73be011c1c6c5dc3d7c76efd079eb..b576677d2766aed273e612622ec6c1068be464ca 100644 (file)
 
         <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>
 
index 22f3cdb4cae39baa70ca2bd9b0957a83b3375d77..6da27145810b913c3e8874c061482b1a0a5d5153 100644 (file)
 
         <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>
 
index 980fcff7c3209a5efe6dafe69b812703c177e423..75f764bc4e1185eea177df628cc1862a7a67304d 100644 (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);
 
index e1bcd62e2c792ec0cbe66b92a053f95dcd1a0aee..184aca14ce2debb1c1094783129f2df668afa5f7 100644 (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;
         }
index 83c3c43589869eec30b649bba9cd8bb35d923937..694ef568e6818b115229e78ab69383cf565179ba 100644 (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;
index ac6c112459020e033079f37952f5b3ebcbe9e64e..4793391b06d21182d2d68a02d54a0bf38edc8891 100644 (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;