char *vars;
} FirmwareData;
+static bool firmware_data_supports_sb(const FirmwareData *fwd) {
+ assert(fwd);
+
+ return strv_contains(fwd->features, "secure-boot");
+}
+
static FirmwareData* firmware_data_free(FirmwareData *fwd) {
if (!fwd)
return NULL;
return json_dispatch(v, table, flags, userdata);
}
+static int get_firmware_search_dirs(char ***ret) {
+ int r;
+
+ assert(ret);
+
+ /* Search in:
+ * - $XDG_CONFIG_HOME/qemu/firmware
+ * - /etc/qemu/firmware
+ * - /usr/share/qemu/firmware
+ *
+ * Prioritising entries in "more specific" directories */
+
+ _cleanup_free_ char *user_firmware_dir = NULL;
+ r = xdg_user_config_dir(&user_firmware_dir, "/qemu/firmware");
+ if (r < 0)
+ return r;
+
+ _cleanup_strv_free_ char **l = NULL;
+ l = strv_new(user_firmware_dir, "/etc/qemu/firmware", "/usr/share/qemu/firmware");
+ if (!l)
+ return log_oom_debug();
+
+ *ret = TAKE_PTR(l);
+ return 0;
+}
+
+int list_ovmf_config(char ***ret) {
+ _cleanup_strv_free_ char **search_dirs = NULL;
+ int r;
+
+ assert(ret);
+
+ r = get_firmware_search_dirs(&search_dirs);
+ if (r < 0)
+ return r;
+
+ r = conf_files_list_strv(
+ ret,
+ ".json",
+ /* root= */ NULL,
+ CONF_FILES_FILTER_MASKED|CONF_FILES_REGULAR,
+ (const char *const*) search_dirs);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to list firmware files: %m");
+
+ return 0;
+}
+
+static int load_firmware_data(const char *path, FirmwareData **ret) {
+ int r;
+
+ assert(path);
+ assert(ret);
+
+ _cleanup_(json_variant_unrefp) JsonVariant *json = NULL;
+ r = json_parse_file(
+ /* f= */ NULL,
+ path,
+ /* flags= */ 0,
+ &json,
+ /* ret_line= */ NULL,
+ /* ret_column= */ NULL);
+ if (r < 0)
+ return r;
+
+ static const JsonDispatch table[] = {
+ { "description", JSON_VARIANT_STRING, NULL, 0, JSON_MANDATORY },
+ { "interface-types", JSON_VARIANT_ARRAY, NULL, 0, JSON_MANDATORY },
+ { "mapping", JSON_VARIANT_OBJECT, firmware_mapping, 0, JSON_MANDATORY },
+ { "targets", JSON_VARIANT_ARRAY, NULL, 0, JSON_MANDATORY },
+ { "features", JSON_VARIANT_ARRAY, json_dispatch_strv, offsetof(FirmwareData, features), JSON_MANDATORY },
+ { "tags", JSON_VARIANT_ARRAY, NULL, 0, JSON_MANDATORY },
+ {}
+ };
+
+ _cleanup_(firmware_data_freep) FirmwareData *fwd = NULL;
+ fwd = new0(FirmwareData, 1);
+ if (!fwd)
+ return -ENOMEM;
+
+ r = json_dispatch(json, table, JSON_ALLOW_EXTENSIONS, fwd);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(fwd);
+ return 0;
+}
+
+static int ovmf_config_make(FirmwareData *fwd, OvmfConfig **ret) {
+ assert(fwd);
+ assert(ret);
+
+ _cleanup_free_ OvmfConfig *config = NULL;
+ config = new(OvmfConfig, 1);
+ if (!config)
+ return -ENOMEM;
+
+ *config = (OvmfConfig) {
+ .path = TAKE_PTR(fwd->firmware),
+ .vars = TAKE_PTR(fwd->vars),
+ .supports_sb = firmware_data_supports_sb(fwd),
+ };
+
+ *ret = TAKE_PTR(config);
+ return 0;
+}
+
+int load_ovmf_config(const char *path, OvmfConfig **ret) {
+ _cleanup_(firmware_data_freep) FirmwareData *fwd = NULL;
+ int r;
+
+ assert(path);
+ assert(ret);
+
+ r = load_firmware_data(path, &fwd);
+ if (r < 0)
+ return r;
+
+ return ovmf_config_make(fwd, ret);
+}
+
int find_ovmf_config(int search_sb, OvmfConfig **ret) {
_cleanup_(ovmf_config_freep) OvmfConfig *config = NULL;
- _cleanup_free_ char *user_firmware_dir = NULL;
_cleanup_strv_free_ char **conf_files = NULL;
int r;
+ assert(ret);
+
/* Search in:
* - $XDG_CONFIG_HOME/qemu/firmware
* - /etc/qemu/firmware
* Prioritising entries in "more specific" directories
*/
- r = xdg_user_config_dir(&user_firmware_dir, "/qemu/firmware");
+ r = list_ovmf_config(&conf_files);
if (r < 0)
return r;
- r = conf_files_list_strv(&conf_files, ".json", NULL, CONF_FILES_FILTER_MASKED|CONF_FILES_REGULAR,
- STRV_MAKE_CONST(user_firmware_dir, "/etc/qemu/firmware", "/usr/share/qemu/firmware"));
- if (r < 0)
- return log_debug_errno(r, "Failed to list config files: %m");
-
STRV_FOREACH(file, conf_files) {
_cleanup_(firmware_data_freep) FirmwareData *fwd = NULL;
- _cleanup_(json_variant_unrefp) JsonVariant *config_json = NULL;
- _cleanup_free_ char *contents = NULL;
- size_t contents_sz = 0;
- r = read_full_file(*file, &contents, &contents_sz);
- if (r == -ENOMEM)
- return r;
+ r = load_firmware_data(*file, &fwd);
if (r < 0) {
- log_debug_errno(r, "Failed to read contents of %s - ignoring: %m", *file);
- continue;
- }
-
- r = json_parse(contents, 0, &config_json, NULL, NULL);
- if (r == -ENOMEM)
- return r;
- if (r < 0) {
- log_debug_errno(r, "Failed to parse the JSON in %s - ignoring: %m", *file);
- continue;
- }
-
- static const JsonDispatch table[] = {
- { "description", JSON_VARIANT_STRING, NULL, 0, JSON_MANDATORY },
- { "interface-types", JSON_VARIANT_ARRAY, NULL, 0, JSON_MANDATORY },
- { "mapping", JSON_VARIANT_OBJECT, firmware_mapping, 0, JSON_MANDATORY },
- { "targets", JSON_VARIANT_ARRAY, NULL, 0, JSON_MANDATORY },
- { "features", JSON_VARIANT_ARRAY, json_dispatch_strv, offsetof(FirmwareData, features), JSON_MANDATORY },
- { "tags", JSON_VARIANT_ARRAY, NULL, 0, JSON_MANDATORY },
- {}
- };
-
- fwd = new0(FirmwareData, 1);
- if (!fwd)
- return -ENOMEM;
-
- r = json_dispatch(config_json, table, JSON_ALLOW_EXTENSIONS, fwd);
- if (r == -ENOMEM)
- return r;
- if (r < 0) {
- log_debug_errno(r, "Failed to extract the required fields from the JSON in %s - ignoring: %m", *file);
+ log_debug_errno(r, "Failed to load JSON file '%s', skipping: %m", *file);
continue;
}
if (strv_contains(fwd->features, "enrolled-keys")) {
- log_debug("Skipping %s, firmware has enrolled keys which has been known to cause issues", *file);
+ log_debug("Skipping %s, firmware has enrolled keys which has been known to cause issues.", *file);
continue;
}
- bool sb_present = strv_contains(fwd->features, "secure-boot");
-
/* exclude firmware which doesn't match our Secure Boot requirements */
- if (search_sb >= 0 && search_sb != sb_present) {
- log_debug("Skipping %s, firmware doesn't fit required Secure Boot configuration", *file);
+ if (search_sb >= 0 && !!search_sb != firmware_data_supports_sb(fwd)) {
+ log_debug("Skipping %s, firmware doesn't fit required Secure Boot configuration.", *file);
continue;
}
- config = new0(OvmfConfig, 1);
- if (!config)
- return -ENOMEM;
+ r = ovmf_config_make(fwd, &config);
+ if (r < 0)
+ return r;
- config->path = TAKE_PTR(fwd->firmware);
- config->vars = TAKE_PTR(fwd->vars);
- config->supports_sb = sb_present;
+ log_debug("Selected firmware definition %s.", *file);
break;
}
static MachineCredentialContext arg_credentials = {};
static SettingsMask arg_settings_mask = 0;
static char **arg_parameters = NULL;
+static char *arg_firmware = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
STATIC_DESTRUCTOR_REGISTER(arg_machine, freep);
STATIC_DESTRUCTOR_REGISTER(arg_qemu_smp, freep);
STATIC_DESTRUCTOR_REGISTER(arg_parameters, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_credentials, machine_credential_context_done);
+STATIC_DESTRUCTOR_REGISTER(arg_firmware, freep);
static int help(void) {
_cleanup_free_ char *link = NULL;
" --qemu-gui Start QEMU in graphical mode\n"
" --secure-boot=BOOL Configure whether to search for firmware which\n"
" supports Secure Boot\n"
+ " --firmware=PATH|list Select firmware definition file (or list available)\n"
"\n%3$sSystem Identity:%4$s\n"
" -M --machine=NAME Set the machine name for the container\n"
"\n%3$sCredentials:%4$s\n"
ARG_SECURE_BOOT,
ARG_SET_CREDENTIAL,
ARG_LOAD_CREDENTIAL,
+ ARG_FIRMWARE,
};
static const struct option options[] = {
{ "secure-boot", required_argument, NULL, ARG_SECURE_BOOT },
{ "set-credential", required_argument, NULL, ARG_SET_CREDENTIAL },
{ "load-credential", required_argument, NULL, ARG_LOAD_CREDENTIAL },
+ { "firmware", required_argument, NULL, ARG_FIRMWARE },
{}
};
break;
}
+ case ARG_FIRMWARE:
+ if (streq(optarg, "list")) {
+ _cleanup_strv_free_ char **l = NULL;
+
+ r = list_ovmf_config(&l);
+ if (r < 0)
+ return log_error_errno(r, "Failed to list firmwares: %m");
+
+ bool nl = false;
+ fputstrv(stdout, l, "\n", &nl);
+ if (nl)
+ putchar('\n');
+
+ return 0;
+ }
+
+ if (!isempty(optarg) && !path_is_absolute(optarg) && !startswith(optarg, "./"))
+ return log_error_errno(SYNTHETIC_ERRNO(errno), "Absolute path or path starting with './' required.");
+
+ r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_firmware);
+ if (r < 0)
+ return r;
+
+ break;
+
case '?':
return -EINVAL;
use_kvm = r;
}
- r = find_ovmf_config(arg_secure_boot, &ovmf_config);
+ if (arg_firmware)
+ r = load_ovmf_config(arg_firmware, &ovmf_config);
+ else
+ r = find_ovmf_config(arg_secure_boot, &ovmf_config);
if (r < 0)
return log_error_errno(r, "Failed to find OVMF config: %m");