diff --git a/man/bootctl.xml b/man/bootctl.xml index 1664f68157a..85820ff6068 100644 --- a/man/bootctl.xml +++ b/man/bootctl.xml @@ -377,6 +377,19 @@ Install binaries for all supported EFI architectures (this implies ). + + + Description of the entry added to the firmware's boot option list. Defaults to Linux + Boot Manager. + + Using the default entry name Linux Boot Manager is generally preferable as only + one bootloader installed to a single ESP partition should be used to boot any number of OS installations + found on the various disks installed in the system. Specifically distributions should not use this flag + to install a branded entry in the boot option list. However in situations with multiple disks, each with + their own ESP partition, it can be beneficial to make it easier to identify the bootloader being used in + the firmware's boot option menu. + + diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 10da1c8d7f2..ddeeed0c3d0 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -57,6 +57,12 @@ #include "verbs.h" #include "virt.h" +/* EFI_BOOT_OPTION_DESCRIPTION_MAX sets the maximum length for the boot option description + * stored in NVRAM. The UEFI spec does not specify a minimum or maximum length for this + * string, but we limit the length to something reasonable to prevent from the firmware + * having to deal with a potentially too long string. */ +#define EFI_BOOT_OPTION_DESCRIPTION_MAX ((size_t) 255) + static char *arg_esp_path = NULL; static char *arg_xbootldr_path = NULL; static bool arg_print_esp_path = false; @@ -85,6 +91,7 @@ static enum { ARG_INSTALL_SOURCE_HOST, ARG_INSTALL_SOURCE_AUTO, } arg_install_source = ARG_INSTALL_SOURCE_AUTO; +static char *arg_efi_boot_option_description = NULL; STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep); STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep); @@ -92,12 +99,17 @@ STATIC_DESTRUCTOR_REGISTER(arg_install_layout, freep); STATIC_DESTRUCTOR_REGISTER(arg_entry_token, freep); STATIC_DESTRUCTOR_REGISTER(arg_root, freep); STATIC_DESTRUCTOR_REGISTER(arg_image, freep); +STATIC_DESTRUCTOR_REGISTER(arg_efi_boot_option_description, freep); static const char *arg_dollar_boot_path(void) { /* $BOOT shall be the XBOOTLDR partition if it exists, and otherwise the ESP */ return arg_xbootldr_path ?: arg_esp_path; } +static const char *pick_efi_boot_option_description(void) { + return arg_efi_boot_option_description ?: "Linux Boot Manager"; +} + static int acquire_esp( bool unprivileged_mode, bool graceful, @@ -321,7 +333,7 @@ static int settle_entry_token(void) { break; } - if (isempty(arg_entry_token) || !string_is_safe(arg_entry_token)) + if (isempty(arg_entry_token) || !(utf8_is_valid(arg_entry_token) && string_is_safe(arg_entry_token))) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected entry token not valid: %s", arg_entry_token); log_debug("Using entry token: %s", arg_entry_token); @@ -1139,13 +1151,13 @@ static int install_variables(const char *esp_path, "Failed to determine current boot order: %m"); if (first || r == 0) { - r = efi_add_boot_option(slot, "Linux Boot Manager", + r = efi_add_boot_option(slot, pick_efi_boot_option_description(), part, pstart, psize, uuid, path); if (r < 0) return log_error_errno(r, "Failed to create EFI Boot variable entry: %m"); - log_info("Created EFI boot entry \"Linux Boot Manager\"."); + log_info("Created EFI boot entry \"%s\".", pick_efi_boot_option_description()); } return insert_into_order(slot, first); @@ -1465,6 +1477,8 @@ static int help(int argc, char *argv[], void *userdata) { " Generate JSON output\n" " --all-architectures\n" " Install all supported EFI architectures\n" + " --efi-boot-option-description=DESCRIPTION\n" + " Description of the entry in the boot option list\n" "\nSee the %2$s for details.\n", program_invocation_short_name, link, @@ -1491,29 +1505,31 @@ static int parse_argv(int argc, char *argv[]) { ARG_ENTRY_TOKEN, ARG_JSON, ARG_ARCH_ALL, + ARG_EFI_BOOT_OPTION_DESCRIPTION, }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "esp-path", required_argument, NULL, ARG_ESP_PATH }, - { "path", required_argument, NULL, ARG_ESP_PATH }, /* Compatibility alias */ - { "boot-path", required_argument, NULL, ARG_BOOT_PATH }, - { "root", required_argument, NULL, ARG_ROOT }, - { "image", required_argument, NULL, ARG_IMAGE }, - { "install-source", required_argument, NULL, ARG_INSTALL_SOURCE }, - { "print-esp-path", no_argument, NULL, 'p' }, - { "print-path", no_argument, NULL, 'p' }, /* Compatibility alias */ - { "print-boot-path", no_argument, NULL, 'x' }, - { "no-variables", no_argument, NULL, ARG_NO_VARIABLES }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "graceful", no_argument, NULL, ARG_GRACEFUL }, - { "quiet", no_argument, NULL, 'q' }, - { "make-entry-directory", required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY }, - { "make-machine-id-directory", required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY }, /* Compatibility alias */ - { "entry-token", required_argument, NULL, ARG_ENTRY_TOKEN }, - { "json", required_argument, NULL, ARG_JSON }, - { "all-architectures", no_argument, NULL, ARG_ARCH_ALL }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "esp-path", required_argument, NULL, ARG_ESP_PATH }, + { "path", required_argument, NULL, ARG_ESP_PATH }, /* Compatibility alias */ + { "boot-path", required_argument, NULL, ARG_BOOT_PATH }, + { "root", required_argument, NULL, ARG_ROOT }, + { "image", required_argument, NULL, ARG_IMAGE }, + { "install-source", required_argument, NULL, ARG_INSTALL_SOURCE }, + { "print-esp-path", no_argument, NULL, 'p' }, + { "print-path", no_argument, NULL, 'p' }, /* Compatibility alias */ + { "print-boot-path", no_argument, NULL, 'x' }, + { "no-variables", no_argument, NULL, ARG_NO_VARIABLES }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "graceful", no_argument, NULL, ARG_GRACEFUL }, + { "quiet", no_argument, NULL, 'q' }, + { "make-entry-directory", required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY }, + { "make-machine-id-directory", required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY }, /* Compatibility alias */ + { "entry-token", required_argument, NULL, ARG_ENTRY_TOKEN }, + { "json", required_argument, NULL, ARG_JSON }, + { "all-architectures", no_argument, NULL, ARG_ARCH_ALL }, + { "efi-boot-option-description", required_argument, NULL, ARG_EFI_BOOT_OPTION_DESCRIPTION }, {} }; @@ -1647,6 +1663,22 @@ static int parse_argv(int argc, char *argv[]) { arg_arch_all = true; break; + case ARG_EFI_BOOT_OPTION_DESCRIPTION: + if (isempty(optarg) || !(string_is_safe(optarg) && utf8_is_valid(optarg))) { + _cleanup_free_ char *escaped = NULL; + + escaped = cescape(optarg); + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Invalid --efi-boot-option-description=: %s", strna(escaped)); + } + if (strlen(optarg) > EFI_BOOT_OPTION_DESCRIPTION_MAX) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "--efi-boot-option-description= too long: %zu > %zu", strlen(optarg), EFI_BOOT_OPTION_DESCRIPTION_MAX); + r = free_and_strdup_warn(&arg_efi_boot_option_description, optarg); + if (r < 0) + return r; + break; + case '?': return -EINVAL;