vmspawn: verify selected fw matches requirements
Carry the firmware descriptor's feature list in OvmfConfig, and verify that firmware selected via an explicit --firmware= descriptor declares the amd-sev-snp/intel-tdx feature, is stateless, and is in raw format, the same constraints automatic discovery already imposes. This replaces the cached supports_sb boolean with a generic feature lookup. Signed-off-by: Paul Meyer <katexochen0@gmail.com>
This commit is contained in:
@@ -133,7 +133,7 @@ TEST(find_ovmf_config) {
|
||||
ASSERT_STREQ(config->vars, "/test/vars.fd");
|
||||
ASSERT_STREQ(config->format, "raw");
|
||||
ASSERT_FALSE(ovmf_config_is_stateless(config));
|
||||
ASSERT_FALSE(config->supports_sb);
|
||||
ASSERT_FALSE(ovmf_config_has_feature(config, "secure-boot"));
|
||||
config = ovmf_config_free(config);
|
||||
|
||||
/* With FIND_OVMF_STATELESS only firmware in stateless flash mode is considered. */
|
||||
@@ -188,14 +188,20 @@ TEST(find_ovmf_config) {
|
||||
|
||||
/* The first matching descriptor in sort order wins. */
|
||||
check_find(STRV_MAKE("vmspawn-test-e1"), /* exclude= */ NULL, /* flags= */ 0, "/test/e-both.fd", &config);
|
||||
ASSERT_TRUE(config->supports_sb);
|
||||
ASSERT_TRUE(ovmf_config_has_feature(config, "secure-boot"));
|
||||
config = ovmf_config_free(config);
|
||||
|
||||
/* Descriptors with an excluded feature are skipped. */
|
||||
check_find(STRV_MAKE("vmspawn-test-e1"), STRV_MAKE("vmspawn-test-e2"), /* flags= */ 0, "/test/e-one.fd", /* ret= */ NULL);
|
||||
check_find(STRV_MAKE("vmspawn-test-e1"), STRV_MAKE("vmspawn-test-e2"), /* flags= */ 0, "/test/e-one.fd", &config);
|
||||
ASSERT_TRUE(ovmf_config_has_feature(config, "vmspawn-test-e1"));
|
||||
ASSERT_FALSE(ovmf_config_has_feature(config, "vmspawn-test-e2"));
|
||||
config = ovmf_config_free(config);
|
||||
|
||||
/* Inclusion wins over exclusion. */
|
||||
check_find(STRV_MAKE("vmspawn-test-e1", "vmspawn-test-e2"), STRV_MAKE("vmspawn-test-e2"), /* flags= */ 0, "/test/e-both.fd", /* ret= */ NULL);
|
||||
check_find(STRV_MAKE("vmspawn-test-e1", "vmspawn-test-e2"), STRV_MAKE("vmspawn-test-e2"), /* flags= */ 0, "/test/e-both.fd", &config);
|
||||
ASSERT_TRUE(ovmf_config_has_feature(config, "vmspawn-test-e1"));
|
||||
ASSERT_TRUE(ovmf_config_has_feature(config, "vmspawn-test-e2"));
|
||||
config = ovmf_config_free(config);
|
||||
|
||||
/* All included features must be present. */
|
||||
check_find(STRV_MAKE("vmspawn-test-e1", "vmspawn-test-nonexistent"), /* exclude= */ NULL, /* flags= */ 0, /* expect_path= */ NULL, /* ret= */ NULL);
|
||||
|
||||
@@ -64,9 +64,17 @@ OvmfConfig* ovmf_config_free(OvmfConfig *config) {
|
||||
free(config->vars_format);
|
||||
free(config->device);
|
||||
free(config->mode);
|
||||
strv_free(config->features);
|
||||
return mfree(config);
|
||||
}
|
||||
|
||||
bool ovmf_config_has_feature(const OvmfConfig *config, const char *feature) {
|
||||
assert(config);
|
||||
assert(feature);
|
||||
|
||||
return strv_contains(config->features, feature);
|
||||
}
|
||||
|
||||
static bool firmware_is_stateless(const char *device, const char *mode) {
|
||||
/* Memory-mapped firmware (loaded via -bios) carries no NVRAM. For flash, only mode "stateless"
|
||||
* is stateless: an absent mode means "split" (executable plus NVRAM template), and "combined"
|
||||
@@ -185,12 +193,6 @@ static bool firmware_data_matches_machine(const FirmwareData *fwd, const char *a
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -447,7 +449,7 @@ static int ovmf_config_make(FirmwareData *fwd, OvmfConfig **ret) {
|
||||
.vars_format = TAKE_PTR(fwd->vars_format),
|
||||
.device = TAKE_PTR(fwd->device),
|
||||
.mode = TAKE_PTR(fwd->mode),
|
||||
.supports_sb = firmware_data_supports_sb(fwd),
|
||||
.features = TAKE_PTR(fwd->features),
|
||||
};
|
||||
|
||||
*ret = TAKE_PTR(config);
|
||||
|
||||
@@ -101,7 +101,7 @@ typedef struct OvmfConfig {
|
||||
char *vars_format;
|
||||
char *device;
|
||||
char *mode;
|
||||
bool supports_sb;
|
||||
char **features;
|
||||
} OvmfConfig;
|
||||
|
||||
static inline const char* ovmf_config_format(const OvmfConfig *c) {
|
||||
@@ -111,7 +111,9 @@ static inline const char* ovmf_config_format(const OvmfConfig *c) {
|
||||
static inline const char* ovmf_config_vars_format(const OvmfConfig *c) {
|
||||
return ASSERT_PTR(c)->vars_format ?: "raw";
|
||||
}
|
||||
|
||||
bool ovmf_config_is_stateless(const OvmfConfig *config);
|
||||
bool ovmf_config_has_feature(const OvmfConfig *config, const char *feature);
|
||||
|
||||
OvmfConfig* ovmf_config_free(OvmfConfig *ovmf_config);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(OvmfConfig*, ovmf_config_free);
|
||||
|
||||
+35
-3
@@ -2669,11 +2669,42 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
return r;
|
||||
}
|
||||
|
||||
if (set_contains(arg_firmware_features_include, "secure-boot") && !ovmf_config->supports_sb)
|
||||
/* Flash mode "combined" places the variable store in the (writable) executable,
|
||||
* which would have to be cloned for each guest. */
|
||||
if (streq_ptr(ovmf_config->mode, "combined"))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"Firmware descriptor '%s' declares flash mode 'combined', which is not supported.",
|
||||
arg_firmware ?: ovmf_config->path);
|
||||
|
||||
if (arg_confidential_computing != COCO_NO) {
|
||||
const char *coco_feature = coco_firmware_feature_to_string(arg_confidential_computing);
|
||||
|
||||
if (!ovmf_config_has_feature(ovmf_config, coco_feature))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE),
|
||||
"Firmware descriptor '%s' does not declare the '%s' feature, but "
|
||||
"--coco=%s requires firmware built specifically for it.",
|
||||
arg_firmware ?: ovmf_config->path, coco_feature,
|
||||
confidential_computing_to_string(arg_confidential_computing));
|
||||
if (!ovmf_config_is_stateless(ovmf_config))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE),
|
||||
"Firmware descriptor '%s' does not describe stateless firmware, "
|
||||
"but --coco=%s requires stateless firmware.",
|
||||
arg_firmware ?: ovmf_config->path,
|
||||
confidential_computing_to_string(arg_confidential_computing));
|
||||
if (!streq(ovmf_config_format(ovmf_config), "raw"))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE),
|
||||
"Firmware image '%s' is in %s format, "
|
||||
"but --coco=%s requires a raw image.",
|
||||
ovmf_config->path, ovmf_config_format(ovmf_config),
|
||||
confidential_computing_to_string(arg_confidential_computing));
|
||||
}
|
||||
|
||||
bool sb = ovmf_config_has_feature(ovmf_config, "secure-boot");
|
||||
if (set_contains(arg_firmware_features_include, "secure-boot") && !sb)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE),
|
||||
"Secure Boot requested, but selected OVMF firmware doesn't support it.");
|
||||
|
||||
log_debug("Using OVMF firmware %s Secure Boot support.", ovmf_config->supports_sb ? "with" : "without");
|
||||
log_debug("Using OVMF firmware %s Secure Boot support.", sb ? "with" : "without");
|
||||
}
|
||||
|
||||
_cleanup_(machine_bind_user_context_freep) MachineBindUserContext *bind_user_context = NULL;
|
||||
@@ -2742,7 +2773,8 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
}
|
||||
|
||||
if (ovmf_config && ARCHITECTURE_SUPPORTS_SMM && arg_confidential_computing == COCO_NO) {
|
||||
r = qemu_config_key(config_file, "smm", on_off(ovmf_config->supports_sb));
|
||||
bool sb = ovmf_config_has_feature(ovmf_config, "secure-boot");
|
||||
r = qemu_config_key(config_file, "smm", on_off(sb));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user