From: Daan De Meyer Date: Fri, 27 Mar 2026 11:55:32 +0000 (+0100) Subject: vmspawn: Add --firmware=describe X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d6e5fbca73dbb732661efefb6cef5b3b656aa853;p=thirdparty%2Fsystemd.git vmspawn: Add --firmware=describe It's useful to be able to check what firmware description vmspawn will select. In particular, this will allow me to figure out the nvram template file that will be picked up so I can pick it up in mkosi and operate on it to pass a modified version of it to vmspawn with --efi-nvram-template=. --- diff --git a/man/systemd-vmspawn.xml b/man/systemd-vmspawn.xml index 72dbcb15d9d..28070cfe8f6 100644 --- a/man/systemd-vmspawn.xml +++ b/man/systemd-vmspawn.xml @@ -330,7 +330,9 @@ Takes an absolute path, or a relative path beginning with ./. Specifies a JSON firmware definition file, which allows selecting the firmware to boot in the VM. If not specified, a suitable firmware is automatically discovered. If the - special string list is specified lists all discovered firmwares. + special string list is specified lists all discovered firmwares. If the special + string describe is specified, the firmware that would be selected (taking + into account) is printed and the program exits. diff --git a/shell-completion/bash/systemd-vmspawn b/shell-completion/bash/systemd-vmspawn index a6ce9708abe..b035a42a655 100644 --- a/shell-completion/bash/systemd-vmspawn +++ b/shell-completion/bash/systemd-vmspawn @@ -54,7 +54,7 @@ _systemd_vmspawn() { comps=$(compgen -f -- "$cur" ) elif __contains_word "$prev" ${OPTS[FIRMWARE]}; then compopt -o nospace -o filenames - comps="list $(compgen -f -- "$cur" )" + comps="list describe $(compgen -f -- "$cur" )" elif __contains_word "$prev" ${OPTS[FIRMWARE_FEATURES]}; then comps='list' elif __contains_word "$prev" ${OPTS[BIND]}; then diff --git a/src/vmspawn/vmspawn-util.c b/src/vmspawn/vmspawn-util.c index b8e2c09c830..149fb8f7c91 100644 --- a/src/vmspawn/vmspawn-util.c +++ b/src/vmspawn/vmspawn-util.c @@ -244,7 +244,7 @@ int list_ovmf_config(char ***ret) { return 0; } -static int load_firmware_data(const char *path, FirmwareData **ret) { +static int load_firmware_data(const char *path, FirmwareData **ret, sd_json_variant **ret_json) { int r; assert(path); @@ -281,6 +281,10 @@ static int load_firmware_data(const char *path, FirmwareData **ret) { return r; *ret = TAKE_PTR(fwd); + + if (ret_json) + *ret_json = TAKE_PTR(json); + return 0; } @@ -298,7 +302,7 @@ int list_ovmf_firmware_features(char ***ret) { STRV_FOREACH(file, conf_files) { _cleanup_(firmware_data_freep) FirmwareData *fwd = NULL; - r = load_firmware_data(*file, &fwd); + r = load_firmware_data(*file, &fwd, /* ret_json= */ NULL); if (r < 0) { log_debug_errno(r, "Failed to load JSON file '%s', skipping: %m", *file); continue; @@ -347,14 +351,18 @@ int load_ovmf_config(const char *path, OvmfConfig **ret) { assert(path); assert(ret); - r = load_firmware_data(path, &fwd); + r = load_firmware_data(path, &fwd, /* ret_json= */ NULL); if (r < 0) return r; return ovmf_config_make(fwd, ret); } -int find_ovmf_config(Set *features_include, Set *features_exclude, OvmfConfig **ret) { +int find_ovmf_config( + Set *features_include, + Set *features_exclude, + OvmfConfig **ret, + sd_json_variant **ret_firmware_json) { _cleanup_(ovmf_config_freep) OvmfConfig *config = NULL; _cleanup_strv_free_ char **conf_files = NULL; const char* native_arch_qemu; @@ -380,8 +388,9 @@ int find_ovmf_config(Set *features_include, Set *features_exclude, OvmfConfig ** STRV_FOREACH(file, conf_files) { _cleanup_(firmware_data_freep) FirmwareData *fwd = NULL; + _cleanup_(sd_json_variant_unrefp) sd_json_variant *json = NULL; - r = load_firmware_data(*file, &fwd); + r = load_firmware_data(*file, &fwd, ret_firmware_json ? &json : NULL); if (r < 0) { log_debug_errno(r, "Failed to load JSON file '%s', skipping: %m", *file); continue; @@ -428,6 +437,10 @@ int find_ovmf_config(Set *features_include, Set *features_exclude, OvmfConfig ** return r; log_debug("Selected firmware definition %s.", *file); + + if (ret_firmware_json) + *ret_firmware_json = TAKE_PTR(json); + break; } diff --git a/src/vmspawn/vmspawn-util.h b/src/vmspawn/vmspawn-util.h index 28c2df78a70..90efd936612 100644 --- a/src/vmspawn/vmspawn-util.h +++ b/src/vmspawn/vmspawn-util.h @@ -89,7 +89,7 @@ int qemu_check_vsock_support(void); int list_ovmf_config(char ***ret); int list_ovmf_firmware_features(char ***ret); int load_ovmf_config(const char *path, OvmfConfig **ret); -int find_ovmf_config(Set *features_include, Set *features_exclude, OvmfConfig **ret); +int find_ovmf_config(Set *features_include, Set *features_exclude, OvmfConfig **ret, sd_json_variant **ret_firmware_json); int find_qemu_binary(char **ret_qemu_binary); int vsock_fix_child_cid(int vhost_device_fd, unsigned *machine_cid, const char *machine); diff --git a/src/vmspawn/vmspawn.c b/src/vmspawn/vmspawn.c index fbe4d2145bf..02f2b0df2e0 100644 --- a/src/vmspawn/vmspawn.c +++ b/src/vmspawn/vmspawn.c @@ -138,6 +138,7 @@ static MachineCredentialContext arg_credentials = {}; static uid_t arg_uid_shift = UID_INVALID, arg_uid_range = 0x10000U; static RuntimeMountContext arg_runtime_mounts = {}; static char *arg_firmware = NULL; +static bool arg_firmware_describe = false; static Set *arg_firmware_features_include = NULL; static Set *arg_firmware_features_exclude = NULL; static char *arg_forward_journal = NULL; @@ -239,7 +240,9 @@ static int help(void) { " --network-user-mode Use user mode networking\n" " --secure-boot=BOOL|auto\n" " Enable searching for firmware supporting SecureBoot\n" - " --firmware=PATH|list Select firmware definition file (or list available)\n" + " --firmware=PATH|list|describe\n" + " Select firmware definition file (or list/describe\n" + " available)\n" " --firmware-features=FEATURE[,FEATURE...]|list\n" " Require/exclude specific firmware features\n" " --discard-disk=BOOL Control processing of discard requests\n" @@ -737,6 +740,16 @@ static int parse_argv(int argc, char *argv[]) { return 0; } + if (streq(optarg, "describe")) { + /* Handled after argument parsing so that --firmware-features= is + * taken into account. */ + arg_firmware = mfree(arg_firmware); + arg_firmware_describe = true; + break; + } + + arg_firmware_describe = false; + if (!isempty(optarg) && !path_is_absolute(optarg) && !startswith(optarg, "./")) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Absolute path or path starting with './' required."); @@ -2171,7 +2184,7 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) { if (arg_firmware) r = load_ovmf_config(arg_firmware, &ovmf_config); else - r = find_ovmf_config(arg_firmware_features_include, arg_firmware_features_exclude, &ovmf_config); + r = find_ovmf_config(arg_firmware_features_include, arg_firmware_features_exclude, &ovmf_config, /* ret_firmware_json= */ NULL); if (r < 0) return log_error_errno(r, "Failed to find OVMF config: %m"); @@ -3637,6 +3650,21 @@ static int run(int argc, char *argv[]) { if (r <= 0) return r; + if (arg_firmware_describe) { + _cleanup_(ovmf_config_freep) OvmfConfig *ovmf_config = NULL; + _cleanup_(sd_json_variant_unrefp) sd_json_variant *json = NULL; + + r = find_ovmf_config(arg_firmware_features_include, arg_firmware_features_exclude, &ovmf_config, &json); + if (r < 0) + return log_error_errno(r, "Failed to find OVMF config: %m"); + + r = sd_json_variant_dump(json, SD_JSON_FORMAT_PRETTY|SD_JSON_FORMAT_COLOR_AUTO, stdout, /* prefix= */ NULL); + if (r < 0) + return log_error_errno(r, "Failed to output JSON: %m"); + + return 0; + } + r = determine_names(); if (r < 0) return r;