forked from rpms/qemu-kvm
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
269 lines
11 KiB
269 lines
11 KiB
3 months ago
|
From ab6197309551bd6ddd9f8239191f68dfac23684b Mon Sep 17 00:00:00 2001
|
||
|
From: Michael Roth <michael.roth@amd.com>
|
||
|
Date: Tue, 9 Jul 2024 23:10:05 -0500
|
||
|
Subject: [PATCH 090/100] i386/sev: Don't allow automatic fallback to legacy
|
||
|
KVM_SEV*_INIT
|
||
|
MIME-Version: 1.0
|
||
|
Content-Type: text/plain; charset=UTF-8
|
||
|
Content-Transfer-Encoding: 8bit
|
||
|
|
||
|
RH-Author: Paolo Bonzini <pbonzini@redhat.com>
|
||
|
RH-MergeRequest: 245: SEV-SNP support
|
||
|
RH-Jira: RHEL-39544
|
||
|
RH-Acked-by: Thomas Huth <thuth@redhat.com>
|
||
|
RH-Acked-by: Bandan Das <bdas@redhat.com>
|
||
|
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
|
||
|
RH-Commit: [90/91] 2b1345faa56f993bb6e13d63e11656c784e20412 (bonzini/rhel-qemu-kvm)
|
||
|
|
||
|
Currently if the 'legacy-vm-type' property of the sev-guest object is
|
||
|
'on', QEMU will attempt to use the newer KVM_SEV_INIT2 kernel
|
||
|
interface in conjunction with the newer KVM_X86_SEV_VM and
|
||
|
KVM_X86_SEV_ES_VM KVM VM types.
|
||
|
|
||
|
This can lead to measurement changes if, for instance, an SEV guest was
|
||
|
created on a host that originally had an older kernel that didn't
|
||
|
support KVM_SEV_INIT2, but is booted on the same host later on after the
|
||
|
host kernel was upgraded.
|
||
|
|
||
|
Instead, if legacy-vm-type is 'off', QEMU should fail if the
|
||
|
KVM_SEV_INIT2 interface is not provided by the current host kernel.
|
||
|
Modify the fallback handling accordingly.
|
||
|
|
||
|
In the future, VMSA features and other flags might be added to QEMU
|
||
|
which will require legacy-vm-type to be 'off' because they will rely
|
||
|
on the newer KVM_SEV_INIT2 interface. It may be difficult to convey to
|
||
|
users what values of legacy-vm-type are compatible with which
|
||
|
features/options, so as part of this rework, switch legacy-vm-type to a
|
||
|
tri-state OnOffAuto option. 'auto' in this case will automatically
|
||
|
switch to using the newer KVM_SEV_INIT2, but only if it is required to
|
||
|
make use of new VMSA features or other options only available via
|
||
|
KVM_SEV_INIT2.
|
||
|
|
||
|
Defining 'auto' in this way would avoid inadvertantly breaking
|
||
|
compatibility with older kernels since it would only be used in cases
|
||
|
where users opt into newer features that are only available via
|
||
|
KVM_SEV_INIT2 and newer kernels, and provide better default behavior
|
||
|
than the legacy-vm-type=off behavior that was previously in place, so
|
||
|
make it the default for 9.1+ machine types.
|
||
|
|
||
|
Cc: Daniel P. Berrangé <berrange@redhat.com>
|
||
|
Cc: Paolo Bonzini <pbonzini@redhat.com>
|
||
|
cc: kvm@vger.kernel.org
|
||
|
Signed-off-by: Michael Roth <michael.roth@amd.com>
|
||
|
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
|
||
|
Link: https://lore.kernel.org/r/20240710041005.83720-1-michael.roth@amd.com
|
||
|
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||
|
(cherry picked from commit 9d38d9dca2a81aaf5752d45d221021ef96d496cd)
|
||
|
|
||
|
RHEL: adjust compatiility setting, applying it to 9.4 machine type
|
||
|
---
|
||
|
hw/i386/pc.c | 2 +-
|
||
|
qapi/qom.json | 18 ++++++----
|
||
|
target/i386/sev.c | 85 +++++++++++++++++++++++++++++++++++++++--------
|
||
|
3 files changed, 83 insertions(+), 22 deletions(-)
|
||
|
|
||
|
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
|
||
|
index b25d075b59..e9c5ea5d8f 100644
|
||
|
--- a/hw/i386/pc.c
|
||
|
+++ b/hw/i386/pc.c
|
||
|
@@ -352,7 +352,7 @@ const size_t pc_rhel_compat_len = G_N_ELEMENTS(pc_rhel_compat);
|
||
|
GlobalProperty pc_rhel_9_5_compat[] = {
|
||
|
/* pc_rhel_9_5_compat from pc_compat_pc_9_0 (backported from 9.1) */
|
||
|
{ TYPE_X86_CPU, "guest-phys-bits", "0" },
|
||
|
- { "sev-guest", "legacy-vm-type", "true" },
|
||
|
+ { "sev-guest", "legacy-vm-type", "on" },
|
||
|
};
|
||
|
const size_t pc_rhel_9_5_compat_len = G_N_ELEMENTS(pc_rhel_9_5_compat);
|
||
|
|
||
|
diff --git a/qapi/qom.json b/qapi/qom.json
|
||
|
index 8bd299265e..17bd5a0cf7 100644
|
||
|
--- a/qapi/qom.json
|
||
|
+++ b/qapi/qom.json
|
||
|
@@ -912,12 +912,16 @@
|
||
|
# @handle: SEV firmware handle (default: 0)
|
||
|
#
|
||
|
# @legacy-vm-type: Use legacy KVM_SEV_INIT KVM interface for creating the VM.
|
||
|
-# The newer KVM_SEV_INIT2 interface syncs additional vCPU
|
||
|
-# state when initializing the VMSA structures, which will
|
||
|
-# result in a different guest measurement. Set this to
|
||
|
-# maintain compatibility with older QEMU or kernel versions
|
||
|
-# that rely on legacy KVM_SEV_INIT behavior.
|
||
|
-# (default: false) (since 9.1)
|
||
|
+# The newer KVM_SEV_INIT2 interface, from Linux >= 6.10, syncs
|
||
|
+# additional vCPU state when initializing the VMSA structures,
|
||
|
+# which will result in a different guest measurement. Set
|
||
|
+# this to 'on' to force compatibility with older QEMU or kernel
|
||
|
+# versions that rely on legacy KVM_SEV_INIT behavior. 'auto'
|
||
|
+# will behave identically to 'on', but will automatically
|
||
|
+# switch to using KVM_SEV_INIT2 if the user specifies any
|
||
|
+# additional options that require it. If set to 'off', QEMU
|
||
|
+# will require KVM_SEV_INIT2 unconditionally.
|
||
|
+# (default: off) (since 9.1)
|
||
|
#
|
||
|
# Since: 2.12
|
||
|
##
|
||
|
@@ -927,7 +931,7 @@
|
||
|
'*session-file': 'str',
|
||
|
'*policy': 'uint32',
|
||
|
'*handle': 'uint32',
|
||
|
- '*legacy-vm-type': 'bool' } }
|
||
|
+ '*legacy-vm-type': 'OnOffAuto' } }
|
||
|
|
||
|
##
|
||
|
# @SevSnpGuestProperties:
|
||
|
diff --git a/target/i386/sev.c b/target/i386/sev.c
|
||
|
index 491fab74fd..b921defb63 100644
|
||
|
--- a/target/i386/sev.c
|
||
|
+++ b/target/i386/sev.c
|
||
|
@@ -144,7 +144,7 @@ struct SevGuestState {
|
||
|
uint32_t policy;
|
||
|
char *dh_cert_file;
|
||
|
char *session_file;
|
||
|
- bool legacy_vm_type;
|
||
|
+ OnOffAuto legacy_vm_type;
|
||
|
};
|
||
|
|
||
|
struct SevSnpGuestState {
|
||
|
@@ -1334,6 +1334,17 @@ sev_vm_state_change(void *opaque, bool running, RunState state)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
+/*
|
||
|
+ * This helper is to examine sev-guest properties and determine if any options
|
||
|
+ * have been set which rely on the newer KVM_SEV_INIT2 interface and associated
|
||
|
+ * KVM VM types.
|
||
|
+ */
|
||
|
+static bool sev_init2_required(SevGuestState *sev_guest)
|
||
|
+{
|
||
|
+ /* Currently no KVM_SEV_INIT2-specific options are exposed via QEMU */
|
||
|
+ return false;
|
||
|
+}
|
||
|
+
|
||
|
static int sev_kvm_type(X86ConfidentialGuest *cg)
|
||
|
{
|
||
|
SevCommonState *sev_common = SEV_COMMON(cg);
|
||
|
@@ -1344,14 +1355,39 @@ static int sev_kvm_type(X86ConfidentialGuest *cg)
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
+ /* These are the only cases where legacy VM types can be used. */
|
||
|
+ if (sev_guest->legacy_vm_type == ON_OFF_AUTO_ON ||
|
||
|
+ (sev_guest->legacy_vm_type == ON_OFF_AUTO_AUTO &&
|
||
|
+ !sev_init2_required(sev_guest))) {
|
||
|
+ sev_common->kvm_type = KVM_X86_DEFAULT_VM;
|
||
|
+ goto out;
|
||
|
+ }
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Newer VM types are required, either explicitly via legacy-vm-type=on, or
|
||
|
+ * implicitly via legacy-vm-type=auto along with additional sev-guest
|
||
|
+ * properties that require the newer VM types.
|
||
|
+ */
|
||
|
kvm_type = (sev_guest->policy & SEV_POLICY_ES) ?
|
||
|
KVM_X86_SEV_ES_VM : KVM_X86_SEV_VM;
|
||
|
- if (kvm_is_vm_type_supported(kvm_type) && !sev_guest->legacy_vm_type) {
|
||
|
- sev_common->kvm_type = kvm_type;
|
||
|
- } else {
|
||
|
- sev_common->kvm_type = KVM_X86_DEFAULT_VM;
|
||
|
+ if (!kvm_is_vm_type_supported(kvm_type)) {
|
||
|
+ if (sev_guest->legacy_vm_type == ON_OFF_AUTO_AUTO) {
|
||
|
+ error_report("SEV: host kernel does not support requested %s VM type, which is required "
|
||
|
+ "for the set of options specified. To allow use of the legacy "
|
||
|
+ "KVM_X86_DEFAULT_VM VM type, please disable any options that are not "
|
||
|
+ "compatible with the legacy VM type, or upgrade your kernel.",
|
||
|
+ kvm_type == KVM_X86_SEV_VM ? "KVM_X86_SEV_VM" : "KVM_X86_SEV_ES_VM");
|
||
|
+ } else {
|
||
|
+ error_report("SEV: host kernel does not support requested %s VM type. To allow use of "
|
||
|
+ "the legacy KVM_X86_DEFAULT_VM VM type, the 'legacy-vm-type' argument "
|
||
|
+ "must be set to 'on' or 'auto' for the sev-guest object.",
|
||
|
+ kvm_type == KVM_X86_SEV_VM ? "KVM_X86_SEV_VM" : "KVM_X86_SEV_ES_VM");
|
||
|
+ }
|
||
|
+
|
||
|
+ return -1;
|
||
|
}
|
||
|
|
||
|
+ sev_common->kvm_type = kvm_type;
|
||
|
out:
|
||
|
return sev_common->kvm_type;
|
||
|
}
|
||
|
@@ -1442,14 +1478,24 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
|
||
|
}
|
||
|
|
||
|
trace_kvm_sev_init();
|
||
|
- if (x86_klass->kvm_type(X86_CONFIDENTIAL_GUEST(sev_common)) == KVM_X86_DEFAULT_VM) {
|
||
|
+ switch (x86_klass->kvm_type(X86_CONFIDENTIAL_GUEST(sev_common))) {
|
||
|
+ case KVM_X86_DEFAULT_VM:
|
||
|
cmd = sev_es_enabled() ? KVM_SEV_ES_INIT : KVM_SEV_INIT;
|
||
|
|
||
|
ret = sev_ioctl(sev_common->sev_fd, cmd, NULL, &fw_error);
|
||
|
- } else {
|
||
|
+ break;
|
||
|
+ case KVM_X86_SEV_VM:
|
||
|
+ case KVM_X86_SEV_ES_VM:
|
||
|
+ case KVM_X86_SNP_VM: {
|
||
|
struct kvm_sev_init args = { 0 };
|
||
|
|
||
|
ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_INIT2, &args, &fw_error);
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ default:
|
||
|
+ error_setg(errp, "%s: host kernel does not support the requested SEV configuration.",
|
||
|
+ __func__);
|
||
|
+ return -1;
|
||
|
}
|
||
|
|
||
|
if (ret) {
|
||
|
@@ -2037,14 +2083,23 @@ sev_guest_set_session_file(Object *obj, const char *value, Error **errp)
|
||
|
SEV_GUEST(obj)->session_file = g_strdup(value);
|
||
|
}
|
||
|
|
||
|
-static bool sev_guest_get_legacy_vm_type(Object *obj, Error **errp)
|
||
|
+static void sev_guest_get_legacy_vm_type(Object *obj, Visitor *v,
|
||
|
+ const char *name, void *opaque,
|
||
|
+ Error **errp)
|
||
|
{
|
||
|
- return SEV_GUEST(obj)->legacy_vm_type;
|
||
|
+ SevGuestState *sev_guest = SEV_GUEST(obj);
|
||
|
+ OnOffAuto legacy_vm_type = sev_guest->legacy_vm_type;
|
||
|
+
|
||
|
+ visit_type_OnOffAuto(v, name, &legacy_vm_type, errp);
|
||
|
}
|
||
|
|
||
|
-static void sev_guest_set_legacy_vm_type(Object *obj, bool value, Error **errp)
|
||
|
+static void sev_guest_set_legacy_vm_type(Object *obj, Visitor *v,
|
||
|
+ const char *name, void *opaque,
|
||
|
+ Error **errp)
|
||
|
{
|
||
|
- SEV_GUEST(obj)->legacy_vm_type = value;
|
||
|
+ SevGuestState *sev_guest = SEV_GUEST(obj);
|
||
|
+
|
||
|
+ visit_type_OnOffAuto(v, name, &sev_guest->legacy_vm_type, errp);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
@@ -2070,9 +2125,9 @@ sev_guest_class_init(ObjectClass *oc, void *data)
|
||
|
sev_guest_set_session_file);
|
||
|
object_class_property_set_description(oc, "session-file",
|
||
|
"guest owners session parameters (encoded with base64)");
|
||
|
- object_class_property_add_bool(oc, "legacy-vm-type",
|
||
|
- sev_guest_get_legacy_vm_type,
|
||
|
- sev_guest_set_legacy_vm_type);
|
||
|
+ object_class_property_add(oc, "legacy-vm-type", "OnOffAuto",
|
||
|
+ sev_guest_get_legacy_vm_type,
|
||
|
+ sev_guest_set_legacy_vm_type, NULL, NULL);
|
||
|
object_class_property_set_description(oc, "legacy-vm-type",
|
||
|
"use legacy VM type to maintain measurement compatibility with older QEMU or kernel versions.");
|
||
|
}
|
||
|
@@ -2088,6 +2143,8 @@ sev_guest_instance_init(Object *obj)
|
||
|
object_property_add_uint32_ptr(obj, "policy", &sev_guest->policy,
|
||
|
OBJ_PROP_FLAG_READWRITE);
|
||
|
object_apply_compat_props(obj);
|
||
|
+
|
||
|
+ sev_guest->legacy_vm_type = ON_OFF_AUTO_AUTO;
|
||
|
}
|
||
|
|
||
|
/* guest info specific sev/sev-es */
|
||
|
--
|
||
|
2.39.3
|
||
|
|