mirror of
https://github.com/systemd/systemd.git
synced 2025-01-09 01:18:19 +03:00
boot: synthesize a separate menu entry from each .profile section
This iterates through the .profile sections a UKI provides and uses it to generate multiple menu entries from them, one for each .profile section.
This commit is contained in:
parent
ea8aa2ea85
commit
382e4da4a5
@ -24,6 +24,7 @@
|
||||
#include "shim.h"
|
||||
#include "ticks.h"
|
||||
#include "tpm2-pcr.h"
|
||||
#include "uki.h"
|
||||
#include "util.h"
|
||||
#include "version.h"
|
||||
#include "vmm.h"
|
||||
@ -52,7 +53,8 @@ typedef enum LoaderType {
|
||||
} LoaderType;
|
||||
|
||||
typedef struct {
|
||||
char16_t *id; /* The unique identifier for this entry (typically the filename of the file defining the entry) */
|
||||
char16_t *id; /* The unique identifier for this entry (typically the filename of the file defining the entry, possibly suffixed with a profile id) */
|
||||
char16_t *id_without_profile; /* same, but without any profile id suffixed */
|
||||
char16_t *title_show; /* The string to actually display (this is made unique before showing) */
|
||||
char16_t *title; /* The raw (human readable) title string of the entry (not necessarily unique) */
|
||||
char16_t *sort_key; /* The string to use as primary sort key, usually ID= from os-release, possibly suffixed */
|
||||
@ -72,6 +74,7 @@ typedef struct {
|
||||
char16_t *path;
|
||||
char16_t *current_name;
|
||||
char16_t *next_name;
|
||||
unsigned profile;
|
||||
} BootEntry;
|
||||
|
||||
typedef struct {
|
||||
@ -584,7 +587,11 @@ static void print_status(Config *config, char16_t *loaded_image_path) {
|
||||
(void) device_path_to_str(dp, &dp_str);
|
||||
|
||||
printf(" boot entry: %zu/%zu\n", i + 1, config->n_entries);
|
||||
printf(" id: %ls\n", entry->id);
|
||||
printf(" id: %ls", entry->id);
|
||||
if (entry->id_without_profile && !streq(entry->id_without_profile, entry->id))
|
||||
printf(" (without profile: %ls)\n", entry->id_without_profile);
|
||||
else
|
||||
printf("\n");
|
||||
if (entry->title)
|
||||
printf(" title: %ls\n", entry->title);
|
||||
if (entry->title_show && !streq16(entry->title, entry->title_show))
|
||||
@ -605,6 +612,8 @@ static void print_status(Config *config, char16_t *loaded_image_path) {
|
||||
printf(" devicetree: %ls\n", entry->devicetree);
|
||||
if (entry->options)
|
||||
printf(" options: %ls\n", entry->options);
|
||||
if (entry->profile > 0)
|
||||
printf(" profile: %u\n", entry->profile);
|
||||
printf(" internal call: %ls\n", yes_no(!!entry->call));
|
||||
|
||||
printf("counting boots: %ls\n", yes_no(entry->tries_left >= 0));
|
||||
@ -1182,6 +1191,7 @@ static BootEntry* boot_entry_free(BootEntry *entry) {
|
||||
return NULL;
|
||||
|
||||
free(entry->id);
|
||||
free(entry->id_without_profile);
|
||||
free(entry->title_show);
|
||||
free(entry->title);
|
||||
free(entry->sort_key);
|
||||
@ -1722,10 +1732,20 @@ static int boot_entry_compare(const BootEntry *a, const BootEntry *b) {
|
||||
/* Now order by ID. The version is likely part of the ID, thus note that this will generatelly put
|
||||
* the newer versions earlier. Specifying a sort key explicitly is preferable, because it gives an
|
||||
* explicit sort order. */
|
||||
r = -strverscmp_improved(a->id, b->id);
|
||||
r = -strverscmp_improved(a->id_without_profile ?: a->id, b->id_without_profile ?: b->id);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
/* Let's sort profiles by their profile */
|
||||
if (a->id_without_profile && b->id_without_profile) {
|
||||
/* Note: the strverscmp_improved() call above checked for us that we are looking at the very
|
||||
* same id, hence at this point we only need to compare profile numbers, since we know they
|
||||
* belong to the same UKI. */
|
||||
r = CMP(a->profile, b->profile);
|
||||
if (r != 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (a->tries_left < 0 || b->tries_left < 0)
|
||||
return 0;
|
||||
|
||||
@ -2121,11 +2141,13 @@ static void boot_entry_add_type2(
|
||||
enum {
|
||||
SECTION_CMDLINE,
|
||||
SECTION_OSREL,
|
||||
SECTION_PROFILE,
|
||||
_SECTION_MAX,
|
||||
};
|
||||
static const char * const section_names[_SECTION_MAX + 1] = {
|
||||
[SECTION_CMDLINE] = ".cmdline",
|
||||
[SECTION_OSREL] = ".osrel",
|
||||
[SECTION_PROFILE] = ".profile",
|
||||
NULL,
|
||||
};
|
||||
|
||||
@ -2141,124 +2163,193 @@ static void boot_entry_add_type2(
|
||||
if (err != EFI_SUCCESS)
|
||||
return;
|
||||
|
||||
/* Load section table once */
|
||||
_cleanup_free_ PeSectionHeader *section_table = NULL;
|
||||
size_t n_section_table;
|
||||
err = pe_section_table_from_file(handle, §ion_table, &n_section_table);
|
||||
if (err != EFI_SUCCESS)
|
||||
return;
|
||||
|
||||
/* Look for .osrel and .cmdline sections in the .efi binary */
|
||||
PeSectionVector sections[_SECTION_MAX] = {};
|
||||
/* Find base profile */
|
||||
PeSectionVector base_sections[_SECTION_MAX] = {};
|
||||
pe_locate_profile_sections(
|
||||
section_table,
|
||||
n_section_table,
|
||||
section_names,
|
||||
/* profile= */ UINT_MAX,
|
||||
/* validate_base= */ 0,
|
||||
sections);
|
||||
if (!PE_SECTION_VECTOR_IS_SET(sections + SECTION_OSREL))
|
||||
return;
|
||||
base_sections);
|
||||
|
||||
_cleanup_free_ char *content = NULL;
|
||||
err = file_handle_read(
|
||||
handle,
|
||||
sections[SECTION_OSREL].file_offset,
|
||||
sections[SECTION_OSREL].size,
|
||||
&content,
|
||||
/* ret_size= */ NULL);
|
||||
if (err != EFI_SUCCESS)
|
||||
return;
|
||||
/* and now iterate through possible profiles, and create a menu item for each profile we find */
|
||||
for (unsigned profile = 0; profile < UNIFIED_PROFILES_MAX; profile ++) {
|
||||
PeSectionVector sections[_SECTION_MAX];
|
||||
|
||||
_cleanup_free_ char16_t *os_pretty_name = NULL, *os_image_id = NULL, *os_name = NULL, *os_id = NULL,
|
||||
*os_image_version = NULL, *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL;
|
||||
char *line, *key, *value;
|
||||
size_t pos = 0;
|
||||
/* Start out with the base sections */
|
||||
memcpy(sections, base_sections, sizeof(sections));
|
||||
|
||||
/* read properties from the embedded os-release file */
|
||||
while ((line = line_get_key_value(content, "=", &pos, &key, &value)))
|
||||
if (streq8(key, "PRETTY_NAME")) {
|
||||
free(os_pretty_name);
|
||||
os_pretty_name = xstr8_to_16(value);
|
||||
err = pe_locate_profile_sections(
|
||||
section_table,
|
||||
n_section_table,
|
||||
section_names,
|
||||
profile,
|
||||
/* validate_base= */ 0,
|
||||
sections);
|
||||
if (err != EFI_SUCCESS && profile > 0) /* It's fine if there's no .profile for the first
|
||||
profile */
|
||||
break;
|
||||
|
||||
} else if (streq8(key, "IMAGE_ID")) {
|
||||
free(os_image_id);
|
||||
os_image_id = xstr8_to_16(value);
|
||||
if (!PE_SECTION_VECTOR_IS_SET(sections + SECTION_OSREL))
|
||||
continue;
|
||||
|
||||
} else if (streq8(key, "NAME")) {
|
||||
free(os_name);
|
||||
os_name = xstr8_to_16(value);
|
||||
_cleanup_free_ char *content = NULL;
|
||||
err = file_handle_read(
|
||||
handle,
|
||||
sections[SECTION_OSREL].file_offset,
|
||||
sections[SECTION_OSREL].size,
|
||||
&content,
|
||||
/* ret_size= */ NULL);
|
||||
if (err != EFI_SUCCESS)
|
||||
continue;
|
||||
|
||||
} else if (streq8(key, "ID")) {
|
||||
free(os_id);
|
||||
os_id = xstr8_to_16(value);
|
||||
_cleanup_free_ char16_t *os_pretty_name = NULL, *os_image_id = NULL, *os_name = NULL, *os_id = NULL,
|
||||
*os_image_version = NULL, *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL;
|
||||
char *line, *key, *value;
|
||||
size_t pos = 0;
|
||||
|
||||
} else if (streq8(key, "IMAGE_VERSION")) {
|
||||
free(os_image_version);
|
||||
os_image_version = xstr8_to_16(value);
|
||||
/* read properties from the embedded os-release file */
|
||||
while ((line = line_get_key_value(content, "=", &pos, &key, &value)))
|
||||
if (streq8(key, "PRETTY_NAME")) {
|
||||
free(os_pretty_name);
|
||||
os_pretty_name = xstr8_to_16(value);
|
||||
|
||||
} else if (streq8(key, "VERSION")) {
|
||||
free(os_version);
|
||||
os_version = xstr8_to_16(value);
|
||||
} else if (streq8(key, "IMAGE_ID")) {
|
||||
free(os_image_id);
|
||||
os_image_id = xstr8_to_16(value);
|
||||
|
||||
} else if (streq8(key, "VERSION_ID")) {
|
||||
free(os_version_id);
|
||||
os_version_id = xstr8_to_16(value);
|
||||
} else if (streq8(key, "NAME")) {
|
||||
free(os_name);
|
||||
os_name = xstr8_to_16(value);
|
||||
|
||||
} else if (streq8(key, "BUILD_ID")) {
|
||||
free(os_build_id);
|
||||
os_build_id = xstr8_to_16(value);
|
||||
} else if (streq8(key, "ID")) {
|
||||
free(os_id);
|
||||
os_id = xstr8_to_16(value);
|
||||
|
||||
} else if (streq8(key, "IMAGE_VERSION")) {
|
||||
free(os_image_version);
|
||||
os_image_version = xstr8_to_16(value);
|
||||
|
||||
} else if (streq8(key, "VERSION")) {
|
||||
free(os_version);
|
||||
os_version = xstr8_to_16(value);
|
||||
|
||||
} else if (streq8(key, "VERSION_ID")) {
|
||||
free(os_version_id);
|
||||
os_version_id = xstr8_to_16(value);
|
||||
|
||||
} else if (streq8(key, "BUILD_ID")) {
|
||||
free(os_build_id);
|
||||
os_build_id = xstr8_to_16(value);
|
||||
}
|
||||
|
||||
const char16_t *good_name, *good_version, *good_sort_key;
|
||||
if (!bootspec_pick_name_version_sort_key(
|
||||
os_pretty_name,
|
||||
os_image_id,
|
||||
os_name,
|
||||
os_id,
|
||||
os_image_version,
|
||||
os_version,
|
||||
os_version_id,
|
||||
os_build_id,
|
||||
&good_name,
|
||||
&good_version,
|
||||
&good_sort_key))
|
||||
continue;
|
||||
|
||||
_cleanup_free_ char16_t *profile_id = NULL, *profile_title = NULL;
|
||||
|
||||
if (PE_SECTION_VECTOR_IS_SET(sections + SECTION_PROFILE)) {
|
||||
content = mfree(content);
|
||||
|
||||
/* Read any .profile data from the file, if we have it */
|
||||
|
||||
err = file_handle_read(
|
||||
handle,
|
||||
sections[SECTION_PROFILE].file_offset,
|
||||
sections[SECTION_PROFILE].size,
|
||||
&content,
|
||||
/* ret_size= */ NULL);
|
||||
if (err != EFI_SUCCESS)
|
||||
continue;
|
||||
|
||||
/* read properties from the embedded os-release file */
|
||||
pos = 0;
|
||||
while ((line = line_get_key_value(content, "=", &pos, &key, &value)))
|
||||
if (streq8(key, "ID")) {
|
||||
free(profile_id);
|
||||
profile_id = xstr8_to_16(value);
|
||||
} else if (streq8(key, "TITLE")) {
|
||||
free(profile_title);
|
||||
profile_title = xstr8_to_16(value);
|
||||
}
|
||||
}
|
||||
|
||||
const char16_t *good_name, *good_version, *good_sort_key;
|
||||
if (!bootspec_pick_name_version_sort_key(
|
||||
os_pretty_name,
|
||||
os_image_id,
|
||||
os_name,
|
||||
os_id,
|
||||
os_image_version,
|
||||
os_version,
|
||||
os_version_id,
|
||||
os_build_id,
|
||||
&good_name,
|
||||
&good_version,
|
||||
&good_sort_key))
|
||||
return;
|
||||
_cleanup_free_ char16_t *id = NULL;
|
||||
if (profile > 0) {
|
||||
if (profile_id)
|
||||
id = xasprintf("%ls@%ls", filename, profile_id);
|
||||
else
|
||||
id = xasprintf("%ls@%u", filename, profile);
|
||||
} else
|
||||
id = xstrdup16(filename);
|
||||
|
||||
BootEntry *entry = xnew(BootEntry, 1);
|
||||
*entry = (BootEntry) {
|
||||
.id = xstrdup16(filename),
|
||||
.type = LOADER_UNIFIED_LINUX,
|
||||
.title = xstrdup16(good_name),
|
||||
.version = xstrdup16(good_version),
|
||||
.device = device,
|
||||
.loader = xasprintf("\\EFI\\Linux\\%ls", filename),
|
||||
.sort_key = xstrdup16(good_sort_key),
|
||||
.key = 'l',
|
||||
.tries_done = -1,
|
||||
.tries_left = -1,
|
||||
};
|
||||
_cleanup_free_ char16_t *title = NULL;
|
||||
if (profile_title)
|
||||
title = xasprintf("%ls (%ls)", good_name, profile_title);
|
||||
else if (profile > 0) {
|
||||
if (profile_id)
|
||||
title = xasprintf("%ls (%ls)", good_name, profile_id);
|
||||
else
|
||||
title = xasprintf("%ls (Profile #%u)", good_name, profile + 1);
|
||||
} else
|
||||
title = xstrdup16(good_name);
|
||||
|
||||
strtolower16(entry->id);
|
||||
config_add_entry(config, entry);
|
||||
boot_entry_parse_tries(entry, u"\\EFI\\Linux", filename, u".efi");
|
||||
BootEntry *entry = xnew(BootEntry, 1);
|
||||
*entry = (BootEntry) {
|
||||
.id = strtolower16(TAKE_PTR(id)),
|
||||
.id_without_profile = profile > 0 ? strtolower16(xstrdup16(filename)) : NULL,
|
||||
.type = LOADER_UNIFIED_LINUX,
|
||||
.title = TAKE_PTR(title),
|
||||
.version = xstrdup16(good_version),
|
||||
.device = device,
|
||||
.loader = xasprintf("\\EFI\\Linux\\%ls", filename),
|
||||
.sort_key = xstrdup16(good_sort_key),
|
||||
.key = 'l',
|
||||
.tries_done = -1,
|
||||
.tries_left = -1,
|
||||
.profile = profile,
|
||||
};
|
||||
|
||||
if (!PE_SECTION_VECTOR_IS_SET(sections + SECTION_CMDLINE))
|
||||
return;
|
||||
config_add_entry(config, entry);
|
||||
boot_entry_parse_tries(entry, u"\\EFI\\Linux", filename, u".efi");
|
||||
|
||||
content = mfree(content);
|
||||
if (!PE_SECTION_VECTOR_IS_SET(sections + SECTION_CMDLINE))
|
||||
return;
|
||||
|
||||
/* read the embedded cmdline file */
|
||||
size_t cmdline_len;
|
||||
err = file_handle_read(
|
||||
handle,
|
||||
sections[SECTION_CMDLINE].file_offset,
|
||||
sections[SECTION_CMDLINE].size,
|
||||
&content,
|
||||
&cmdline_len);
|
||||
if (err == EFI_SUCCESS) {
|
||||
entry->options = xstrn8_to_16(content, cmdline_len);
|
||||
mangle_stub_cmdline(entry->options);
|
||||
entry->options_implied = true;
|
||||
content = mfree(content);
|
||||
|
||||
/* Read the embedded cmdline file for display purposes */
|
||||
size_t cmdline_len;
|
||||
err = file_handle_read(
|
||||
handle,
|
||||
sections[SECTION_CMDLINE].file_offset,
|
||||
sections[SECTION_CMDLINE].size,
|
||||
&content,
|
||||
&cmdline_len);
|
||||
if (err == EFI_SUCCESS) {
|
||||
entry->options = mangle_stub_cmdline(xstrn8_to_16(content, cmdline_len));
|
||||
entry->options_implied = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2458,10 +2549,22 @@ static EFI_STATUS image_start(
|
||||
const char *extra = smbios_find_oem_string("io.systemd.boot.kernel-cmdline-extra");
|
||||
if (extra) {
|
||||
_cleanup_free_ char16_t *tmp = TAKE_PTR(options), *extra16 = xstr8_to_16(extra);
|
||||
options = xasprintf("%ls %ls", tmp, extra16);
|
||||
if (isempty(tmp))
|
||||
options = TAKE_PTR(extra16);
|
||||
else
|
||||
options = xasprintf("%ls %ls", tmp, extra16);
|
||||
}
|
||||
}
|
||||
|
||||
/* Prefix profile if it's non-zero */
|
||||
if (entry->profile > 0) {
|
||||
_cleanup_free_ char16_t *tmp = TAKE_PTR(options);
|
||||
if (isempty(tmp))
|
||||
options = xasprintf("@%u", entry->profile);
|
||||
else
|
||||
options = xasprintf("@%u %ls", entry->profile, tmp);
|
||||
}
|
||||
|
||||
if (options) {
|
||||
loaded_image->LoadOptions = options;
|
||||
loaded_image->LoadOptionsSize = strsize16(options);
|
||||
@ -2623,6 +2726,7 @@ static void export_loader_variables(
|
||||
EFI_LOADER_FEATURE_SECUREBOOT_ENROLL |
|
||||
EFI_LOADER_FEATURE_RETAIN_SHIM |
|
||||
EFI_LOADER_FEATURE_MENU_DISABLE |
|
||||
EFI_LOADER_FEATURE_MULTI_PROFILE_UKI |
|
||||
0;
|
||||
|
||||
assert(loaded_image);
|
||||
|
@ -23,6 +23,7 @@
|
||||
#define EFI_LOADER_FEATURE_SECUREBOOT_ENROLL (UINT64_C(1) << 11)
|
||||
#define EFI_LOADER_FEATURE_RETAIN_SHIM (UINT64_C(1) << 12)
|
||||
#define EFI_LOADER_FEATURE_MENU_DISABLE (UINT64_C(1) << 13)
|
||||
#define EFI_LOADER_FEATURE_MULTI_PROFILE_UKI (UINT64_C(1) << 14)
|
||||
|
||||
/* Features of the stub, i.e. systemd-stub */
|
||||
#define EFI_STUB_FEATURE_REPORT_BOOT_PARTITION (UINT64_C(1) << 0)
|
||||
|
@ -28,3 +28,6 @@ static inline bool unified_section_measure(UnifiedSection section) {
|
||||
* the measurement, and hence shouldn't be input to it. */
|
||||
return section >= 0 && section < _UNIFIED_SECTION_MAX && section != UNIFIED_SECTION_PCRSIG;
|
||||
}
|
||||
|
||||
/* Max number of profiles per UKI */
|
||||
#define UNIFIED_PROFILES_MAX 256U
|
||||
|
Loading…
Reference in New Issue
Block a user