diff --git a/man/bootctl.xml b/man/bootctl.xml
index b317d84020..1e98b5ca7a 100644
--- a/man/bootctl.xml
+++ b/man/bootctl.xml
@@ -524,6 +524,18 @@
+
+
+ Takes a boolean, defaults to false. Controls whether to append disk model information
+ to the firmware boot option item description (as configured with
+ above). This is useful when installing multiple
+ operating systems on separate disks on the same system, as it ensures the firmware boot options are discernable
+ and give a hint which disk is booted into. Note that this uses hardware model information, and hence
+ might not be too useful in case multiple disks of an identical model are used.
+
+
+
+
Dry run for and .
diff --git a/src/bootctl/bootctl-install.c b/src/bootctl/bootctl-install.c
index d6ce3471b1..976c5380e1 100644
--- a/src/bootctl/bootctl-install.c
+++ b/src/bootctl/bootctl-install.c
@@ -3,9 +3,11 @@
#include
#include
+#include "sd-device.h"
#include "sd-varlink.h"
#include "alloc-util.h"
+#include "blockdev-util.h"
#include "boot-entry.h"
#include "bootctl.h"
#include "bootctl-install.h"
@@ -1321,8 +1323,56 @@ static int remove_from_order(uint16_t slot) {
return 0;
}
-static const char *pick_efi_boot_option_description(void) {
- return arg_efi_boot_option_description ?: "Linux Boot Manager";
+static int pick_efi_boot_option_description(int esp_fd, char **ret) {
+ int r;
+
+ assert(esp_fd >= 0);
+ assert(ret);
+
+ /* early declarations, so that they are definitely initialized even if we follow any of the gotos */
+ _cleanup_(sd_device_unrefp) sd_device *d = NULL;
+ _cleanup_free_ char *j = NULL;
+
+ const char *b = arg_efi_boot_option_description ?: "Linux Boot Manager";
+ if (!arg_efi_boot_option_description_with_device)
+ goto fallback;
+
+ r = block_device_new_from_fd(
+ esp_fd,
+ BLOCK_DEVICE_LOOKUP_WHOLE_DISK|BLOCK_DEVICE_LOOKUP_BACKING,
+ &d);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to find backing device of ESP: %m");
+ goto fallback;
+ }
+
+ const char *serial;
+ r = sd_device_get_property_value(d, "ID_SERIAL", &serial);
+ if (r < 0) {
+ log_debug_errno(r, "Unable to read ID_SERIAL field of backing device of ESP: %m");
+ goto fallback;
+ }
+
+ j = strjoin(b, " (", serial, ")");
+ if (!j)
+ return log_oom();
+
+ if (strlen(j) > EFI_BOOT_OPTION_DESCRIPTION_MAX) {
+ log_debug("Boot option string suffixed with device serial would be too long, skipping: %s", j);
+ j = mfree(j);
+ goto fallback;
+ }
+
+ *ret = TAKE_PTR(j);
+ return 0;
+
+fallback:
+ j = strdup(b);
+ if (!j)
+ return log_oom();
+
+ *ret = TAKE_PTR(j);
+ return 0;
}
static int install_variables(
@@ -1369,9 +1419,15 @@ static int install_variables(
bool existing = r > 0;
if (c->operation == INSTALL_NEW || !existing) {
+ _cleanup_free_ char *description = NULL;
+
+ r = pick_efi_boot_option_description(esp_fd, &description);
+ if (r < 0)
+ return r;
+
r = efi_add_boot_option(
slot,
- pick_efi_boot_option_description(),
+ description,
c->esp_part,
c->esp_pstart,
c->esp_psize,
@@ -1388,7 +1444,7 @@ static int install_variables(
log_info("%s EFI boot entry \"%s\".",
existing ? "Updated" : "Created",
- pick_efi_boot_option_description());
+ description);
}
return insert_into_order(c, slot);
diff --git a/src/bootctl/bootctl.c b/src/bootctl/bootctl.c
index dee65d51d7..8d3cabc865 100644
--- a/src/bootctl/bootctl.c
+++ b/src/bootctl/bootctl.c
@@ -40,12 +40,6 @@
#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 GracefulMode _arg_graceful = ARG_GRACEFUL_NO;
char *arg_esp_path = NULL;
@@ -70,6 +64,7 @@ char *arg_root = NULL;
char *arg_image = NULL;
InstallSource arg_install_source = INSTALL_SOURCE_AUTO;
char *arg_efi_boot_option_description = NULL;
+bool arg_efi_boot_option_description_with_device = false;
bool arg_dry_run = false;
ImagePolicy *arg_image_policy = NULL;
bool arg_varlink = false;
@@ -349,6 +344,8 @@ static int help(int argc, char *argv[], void *userdata) {
" Install all supported EFI architectures\n"
" --efi-boot-option-description=DESCRIPTION\n"
" Description of the entry in the boot option list\n"
+ " --efi-boot-option-description-with-device=yes\n"
+ " Suffix description with disk vendor/model/serial\n"
" --dry-run Dry run (unlink and cleanup)\n"
" --secure-boot-auto-enroll=yes|no\n"
" Set up secure boot auto-enrollment\n"
@@ -398,6 +395,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_JSON,
ARG_ARCH_ALL,
ARG_EFI_BOOT_OPTION_DESCRIPTION,
+ ARG_EFI_BOOT_OPTION_DESCRIPTION_WITH_DEVICE,
ARG_DRY_RUN,
ARG_PRINT_LOADER_PATH,
ARG_PRINT_STUB_PATH,
@@ -409,39 +407,40 @@ static int parse_argv(int argc, char *argv[]) {
};
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 },
- { "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
- { "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' },
- { "print-loader-path", no_argument, NULL, ARG_PRINT_LOADER_PATH },
- { "print-stub-path", no_argument, NULL, ARG_PRINT_STUB_PATH },
- { "print-root-device", no_argument, NULL, 'R' },
- { "variables", required_argument, NULL, ARG_VARIABLES },
- { "no-variables", no_argument, NULL, ARG_NO_VARIABLES }, /* Compatibility alias */
- { "random-seed", required_argument, NULL, ARG_RANDOM_SEED },
- { "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 },
- { "dry-run", no_argument, NULL, ARG_DRY_RUN },
- { "secure-boot-auto-enroll", required_argument, NULL, ARG_SECURE_BOOT_AUTO_ENROLL },
- { "certificate", required_argument, NULL, ARG_CERTIFICATE },
- { "certificate-source", required_argument, NULL, ARG_CERTIFICATE_SOURCE },
- { "private-key", required_argument, NULL, ARG_PRIVATE_KEY },
- { "private-key-source", required_argument, NULL, ARG_PRIVATE_KEY_SOURCE },
+ { "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 },
+ { "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
+ { "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' },
+ { "print-loader-path", no_argument, NULL, ARG_PRINT_LOADER_PATH },
+ { "print-stub-path", no_argument, NULL, ARG_PRINT_STUB_PATH },
+ { "print-root-device", no_argument, NULL, 'R' },
+ { "variables", required_argument, NULL, ARG_VARIABLES },
+ { "no-variables", no_argument, NULL, ARG_NO_VARIABLES }, /* Compatibility alias */
+ { "random-seed", required_argument, NULL, ARG_RANDOM_SEED },
+ { "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 },
+ { "efi-boot-option-description-with-device", required_argument, NULL, ARG_EFI_BOOT_OPTION_DESCRIPTION_WITH_DEVICE },
+ { "dry-run", no_argument, NULL, ARG_DRY_RUN },
+ { "secure-boot-auto-enroll", required_argument, NULL, ARG_SECURE_BOOT_AUTO_ENROLL },
+ { "certificate", required_argument, NULL, ARG_CERTIFICATE },
+ { "certificate-source", required_argument, NULL, ARG_CERTIFICATE_SOURCE },
+ { "private-key", required_argument, NULL, ARG_PRIVATE_KEY },
+ { "private-key-source", required_argument, NULL, ARG_PRIVATE_KEY_SOURCE },
{}
};
@@ -577,9 +576,7 @@ static int parse_argv(int argc, char *argv[]) {
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);
+ _cleanup_free_ char *escaped = cescape(optarg);
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Invalid --efi-boot-option-description=: %s", strna(escaped));
}
@@ -592,6 +589,13 @@ static int parse_argv(int argc, char *argv[]) {
return r;
break;
+ case ARG_EFI_BOOT_OPTION_DESCRIPTION_WITH_DEVICE:
+ r = parse_boolean_argument("--efi-boot-option-description-with-device=", optarg, &arg_efi_boot_option_description_with_device);
+ if (r < 0)
+ return r;
+
+ break;
+
case ARG_DRY_RUN:
arg_dry_run = true;
break;
diff --git a/src/bootctl/bootctl.h b/src/bootctl/bootctl.h
index f4d6bb40ba..93a1ca733b 100644
--- a/src/bootctl/bootctl.h
+++ b/src/bootctl/bootctl.h
@@ -37,6 +37,7 @@ extern char *arg_root;
extern char *arg_image;
extern InstallSource arg_install_source;
extern char *arg_efi_boot_option_description;
+extern bool arg_efi_boot_option_description_with_device;
extern bool arg_dry_run;
extern ImagePolicy *arg_image_policy;
extern bool arg_varlink;
@@ -59,3 +60,9 @@ int acquire_esp(int unprivileged_mode, bool graceful, uint32_t *ret_part, uint64
int acquire_xbootldr(int unprivileged_mode, sd_id128_t *ret_uuid, dev_t *ret_devid);
bool touch_variables(void);
+
+/* 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)