From 4a94a1b83f8857b906bc7924b3f853768b5499b5 Mon Sep 17 00:00:00 2001 From: Felix Pehla <29adc1fd92@gmail.com> Date: Sat, 27 Sep 2025 15:01:06 +0200 Subject: [PATCH 1/4] shared/bootspec: parse 'uki' boot entry option Commit e2a3d562189c413de3262ec47cdc1e1b0b13d78b (as part of #36314) makes sd-boot recognize a 'uki' stanza in a boot loader entry and uapi-group/specifications@3f2bd8236d7f9ce6dedf8bda9cadffd0d363cb08 adds it to the BLS, but bootctl and other components parsing said config do not know about it, leading to the error message `Unknown line 'uki', ignoring.` when attempting to parse the same entry. This commit makes it get parsed the same way that that 'efi' is. --- src/bootctl/bootctl-status.c | 4 ++++ src/shared/bootspec.c | 12 +++++++++--- src/shared/bootspec.h | 1 + src/shared/varlink-io.systemd.BootControl.c | 1 + 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/bootctl/bootctl-status.c b/src/bootctl/bootctl-status.c index dc79779db11..436f2099b23 100644 --- a/src/bootctl/bootctl-status.c +++ b/src/bootctl/bootctl-status.c @@ -732,6 +732,9 @@ static int count_known_files(const BootConfig *config, const char* root, Hashmap if (r < 0) return r; r = ref_file(&known_files, e->efi, +1); + if (r < 0) + return r; + r = ref_file(&known_files, e->uki, +1); if (r < 0) return r; STRV_FOREACH(s, e->initrd) { @@ -792,6 +795,7 @@ static int unlink_entry(const BootConfig *config, const char *root, const char * deref_unlink_file(&known_files, e->kernel, e->root); deref_unlink_file(&known_files, e->efi, e->root); + deref_unlink_file(&known_files, e->uki, e->root); STRV_FOREACH(s, e->initrd) deref_unlink_file(&known_files, *s, e->root); deref_unlink_file(&known_files, e->device_tree, e->root); diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index a0bbb7c17db..2054ea6fb88 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -93,6 +93,7 @@ static void boot_entry_free(BootEntry *entry) { boot_entry_addons_done(&entry->local_addons); free(entry->kernel); free(entry->efi); + free(entry->uki); strv_free(entry->initrd); free(entry->device_tree); strv_free(entry->device_tree_overlay); @@ -403,6 +404,8 @@ static int boot_entry_load_type1( r = parse_path_one(tmp.path, line, field, &tmp.kernel, p); else if (streq(field, "efi")) r = parse_path_one(tmp.path, line, field, &tmp.efi, p); + else if (streq(field, "uki")) + r = parse_path_one(tmp.path, line, field, &tmp.uki, p); else if (streq(field, "initrd")) r = parse_path_strv(tmp.path, line, field, &tmp.initrd, p); else if (streq(field, "devicetree")) @@ -1896,6 +1899,8 @@ int show_boot_entry( boot_entry_file_list("linux", e->root, e->kernel, &status); if (e->efi) boot_entry_file_list("efi", e->root, e->efi, &status); + if (e->uki) + boot_entry_file_list("uki", e->root, e->uki, &status); STRV_FOREACH(s, e->initrd) boot_entry_file_list(s == e->initrd ? "initrd" : NULL, @@ -1957,9 +1962,8 @@ int boot_entry_to_json(const BootConfig *c, size_t i, sd_json_variant **ret) { SD_JSON_BUILD_PAIR_CONDITION(!!opts, "options", SD_JSON_BUILD_STRING(opts)), SD_JSON_BUILD_PAIR_CONDITION(!!e->kernel, "linux", SD_JSON_BUILD_STRING(e->kernel)), SD_JSON_BUILD_PAIR_CONDITION(!!e->efi, "efi", SD_JSON_BUILD_STRING(e->efi)), - SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(e->initrd), "initrd", SD_JSON_BUILD_STRV(e->initrd)), - SD_JSON_BUILD_PAIR_CONDITION(!!e->device_tree, "devicetree", SD_JSON_BUILD_STRING(e->device_tree)), - SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(e->device_tree_overlay), "devicetreeOverlay", SD_JSON_BUILD_STRV(e->device_tree_overlay))); + SD_JSON_BUILD_PAIR_CONDITION(!!e->uki, "uki", SD_JSON_BUILD_STRING(e->uki)), + SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(e->initrd), "initrd", SD_JSON_BUILD_STRV(e->initrd))); if (r < 0) return log_oom(); @@ -1968,6 +1972,8 @@ int boot_entry_to_json(const BootConfig *c, size_t i, sd_json_variant **ret) { * at once. */ r = sd_json_variant_merge_objectbo( &v, + SD_JSON_BUILD_PAIR_CONDITION(!!e->device_tree, "devicetree", SD_JSON_BUILD_STRING(e->device_tree)), + SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(e->device_tree_overlay), "devicetreeOverlay", SD_JSON_BUILD_STRV(e->device_tree_overlay)), SD_JSON_BUILD_PAIR("isReported", SD_JSON_BUILD_BOOLEAN(e->reported_by_loader)), SD_JSON_BUILD_PAIR_CONDITION(e->tries_left != UINT_MAX, "triesLeft", SD_JSON_BUILD_UNSIGNED(e->tries_left)), SD_JSON_BUILD_PAIR_CONDITION(e->tries_done != UINT_MAX, "triesDone", SD_JSON_BUILD_UNSIGNED(e->tries_done)), diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h index 0edf6e084e6..1be26d9d39f 100644 --- a/src/shared/bootspec.h +++ b/src/shared/bootspec.h @@ -50,6 +50,7 @@ typedef struct BootEntry { const BootEntryAddons *global_addons; /* Backpointer into the BootConfig; we don't own this here */ char *kernel; /* linux is #defined to 1, yikes! */ char *efi; + char *uki; char **initrd; char *device_tree; char **device_tree_overlay; diff --git a/src/shared/varlink-io.systemd.BootControl.c b/src/shared/varlink-io.systemd.BootControl.c index eeaef291e44..2b990932d44 100644 --- a/src/shared/varlink-io.systemd.BootControl.c +++ b/src/shared/varlink-io.systemd.BootControl.c @@ -47,6 +47,7 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE( SD_VARLINK_DEFINE_FIELD(options, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(linux, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(efi, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_DEFINE_FIELD(uki, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(initrd, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY), SD_VARLINK_DEFINE_FIELD(devicetree, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(devicetreeOverlay, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY), From 5fb90fa3194d998a971b21e4a643670ae5903f85 Mon Sep 17 00:00:00 2001 From: Felix Pehla <29adc1fd92@gmail.com> Date: Sun, 28 Sep 2025 18:50:06 +0200 Subject: [PATCH 2/4] shared/bootspec: parse 'profile' boot entry option Commit 1e9c9773b994f2f703a5aa5ba80961e90be3a892 makes sd-boot recognize a 'profile' option in a boot loader entry but bootctl and other components parsing said config do not know about it. This commit makes the option get parsed correctly and displays it too. --- src/shared/bootspec.c | 6 ++++++ src/shared/bootspec.h | 1 + src/shared/varlink-io.systemd.BootControl.c | 1 + 3 files changed, 8 insertions(+) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index 2054ea6fb88..9fa50608f5f 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -406,6 +406,8 @@ static int boot_entry_load_type1( r = parse_path_one(tmp.path, line, field, &tmp.efi, p); else if (streq(field, "uki")) r = parse_path_one(tmp.path, line, field, &tmp.uki, p); + else if (streq(field, "profile")) + r = safe_atou_full(p, 10, &tmp.profile); else if (streq(field, "initrd")) r = parse_path_strv(tmp.path, line, field, &tmp.initrd, p); else if (streq(field, "devicetree")) @@ -1631,6 +1633,7 @@ int boot_config_augment_from_loader( .reported_by_loader = true, .tries_left = UINT_MAX, .tries_done = UINT_MAX, + .profile = UINT_MAX, .global_addons = &no_addons, }; } @@ -1901,6 +1904,8 @@ int show_boot_entry( boot_entry_file_list("efi", e->root, e->efi, &status); if (e->uki) boot_entry_file_list("uki", e->root, e->uki, &status); + if (e->profile != UINT_MAX) + printf(" profile: %u\n", e->profile); STRV_FOREACH(s, e->initrd) boot_entry_file_list(s == e->initrd ? "initrd" : NULL, @@ -1963,6 +1968,7 @@ int boot_entry_to_json(const BootConfig *c, size_t i, sd_json_variant **ret) { SD_JSON_BUILD_PAIR_CONDITION(!!e->kernel, "linux", SD_JSON_BUILD_STRING(e->kernel)), SD_JSON_BUILD_PAIR_CONDITION(!!e->efi, "efi", SD_JSON_BUILD_STRING(e->efi)), SD_JSON_BUILD_PAIR_CONDITION(!!e->uki, "uki", SD_JSON_BUILD_STRING(e->uki)), + SD_JSON_BUILD_PAIR_CONDITION(e->profile != UINT_MAX, "profile", SD_JSON_BUILD_UNSIGNED(e->profile)), SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(e->initrd), "initrd", SD_JSON_BUILD_STRV(e->initrd))); if (r < 0) return log_oom(); diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h index 1be26d9d39f..47723689a60 100644 --- a/src/shared/bootspec.h +++ b/src/shared/bootspec.h @@ -65,6 +65,7 @@ typedef struct BootEntry { .source = (s), \ .tries_left = UINT_MAX, \ .tries_done = UINT_MAX, \ + .profile = UINT_MAX, \ } typedef struct BootConfig { diff --git a/src/shared/varlink-io.systemd.BootControl.c b/src/shared/varlink-io.systemd.BootControl.c index 2b990932d44..305fac5c381 100644 --- a/src/shared/varlink-io.systemd.BootControl.c +++ b/src/shared/varlink-io.systemd.BootControl.c @@ -48,6 +48,7 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE( SD_VARLINK_DEFINE_FIELD(linux, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(efi, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(uki, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_DEFINE_FIELD(profile, SD_VARLINK_INT, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(initrd, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY), SD_VARLINK_DEFINE_FIELD(devicetree, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(devicetreeOverlay, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY), From aaa3e6880da71c2faefb619ea7a974dab6a8443d Mon Sep 17 00:00:00 2001 From: Felix Pehla <29adc1fd92@gmail.com> Date: Sat, 27 Sep 2025 21:58:21 +0200 Subject: [PATCH 3/4] shared/bootspec: parse loader.conf options added in v258 --- src/shared/bootspec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index 9fa50608f5f..7bae3b00757 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -516,7 +516,8 @@ int boot_loader_read_conf(BootConfig *config, FILE *file, const char *path) { r = free_and_strdup(&config->default_pattern, p); else if (STR_IN_SET(field, "timeout", "editor", "auto-entries", "auto-firmware", "auto-poweroff", "auto-reboot", "beep", "reboot-for-bitlocker", - "secure-boot-enroll", "console-mode")) + "reboot-on-error", "secure-boot-enroll", "secure-boot-enroll-action", + "console-mode")) r = 0; /* we don't parse these in userspace, but they are OK */ else { log_syntax(NULL, LOG_WARNING, path, line, 0, "Unknown line '%s', ignoring.", field); From 0b89d8cfeca7a6ad602efc65d9bc383104b5677d Mon Sep 17 00:00:00 2001 From: Felix Pehla <29adc1fd92@gmail.com> Date: Mon, 29 Sep 2025 23:06:16 +0200 Subject: [PATCH 4/4] shared/bootspec: update valid loader.conf options --- src/boot/boot.c | 5 +++++ src/shared/bootspec.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/boot/boot.c b/src/boot/boot.c index 69c3927e667..3632f005c16 100644 --- a/src/boot/boot.c +++ b/src/boot/boot.c @@ -1013,6 +1013,8 @@ static void config_defaults_load_from_file(Config *config, char *content) { assert(config); assert(content); + /* If you add, remove, or change an option name here, please also update + * shared/bootspec.c@boot_loader_read_conf() to make parsing by bootctl/logind/etc. work. */ while ((line = line_get_key_value(content, " \t", &pos, &key, &value))) if (streq8(key, "timeout")) { if (streq8(value, "menu-disabled")) @@ -1291,6 +1293,8 @@ static void boot_entry_add_type1( .call = call_image_start, }; + /* If you add, remove, or change an option name here, please also update shared/bootspec.c and + * shared/varlink-io.systemd.BootControl to make parsing by bootctl/logind/etc. work. */ while ((line = line_get_key_value(content, " \t", &pos, &key, &value))) if (streq8(key, "title")) { free(entry->title); @@ -1299,6 +1303,7 @@ static void boot_entry_add_type1( } else if (streq8(key, "sort-key")) { free(entry->sort_key); entry->sort_key = xstr8_to_16(value); + } else if (streq8(key, "profile")) { uint64_t u; if (parse_number8(value, &u, NULL) && u <= UINT_MAX) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index 7bae3b00757..28baab98161 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -517,7 +517,7 @@ int boot_loader_read_conf(BootConfig *config, FILE *file, const char *path) { else if (STR_IN_SET(field, "timeout", "editor", "auto-entries", "auto-firmware", "auto-poweroff", "auto-reboot", "beep", "reboot-for-bitlocker", "reboot-on-error", "secure-boot-enroll", "secure-boot-enroll-action", - "console-mode")) + "secure-boot-enroll-timeout-sec", "console-mode", "log-level")) r = 0; /* we don't parse these in userspace, but they are OK */ else { log_syntax(NULL, LOG_WARNING, path, line, 0, "Unknown line '%s', ignoring.", field);