With prior patches exposing SEV CPUID and MSR, the guest recognizes SEV
as active, but SEV progress states and "query-sev" QMP output remain
incorrect, breaking the attestation workflow.

For TCG emulation aimed at debugging and testing SEV guests—without real
cryptography needs—SEV_STATE_LAUNCH_START (crypto context initialization)
is skipped, proceeding directly to LAUNCH_UPDATE. Here, instead of
encrypting firmware and (if kernel-hashes=on) kernel, this memory
locations are tracked for future Launch Digest (LD) computation,
required for the launch measurement in the next phase. These regions are
stored in the QEMUIOVector ld_data within SevEmulatedState using
sev_emulated_launch_update_data().
For the last state, sev_emulated_launch_finish() handles the transition to
RUNNING state for the VM, while preserving the migration blocker.

sev_emulated_init() initializes all fields for accurate "query-sev" output
alongside state setup.

This is preparatory for sev_launch_measurement() implementation.

Note: In sev_kvm_type(), there is a condition that forces the legacy VM
type for consistency. Normally, this function is never called during a
TCG run. However, since sev-emulated derives from sev-guest, it is possible
to run it with KVM support. This leads to incomplete emulation (MSR will be
inactive, and C-bit management will be missing), although it still
functions. In such cases, when the function is invoked and
legacy-vm-type=off is set, KVM compatibility checks will inevitably fail.
Instead, this allows the VM to boot, issuing a warning about the change.
Additionally, qmp_query_sev_capabilities and
qmp_query_sev_attestation_report return a new error indicating that these
functions are not supported if emulation is active.

From this point, the VM follows the correct state transitions
(except LAUNCH_SECRET), and the data reported by "query-sev" is consistent.

Signed-off-by: Tommaso Califano <[email protected]>
---
 target/i386/sev.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 77 insertions(+), 1 deletion(-)

diff --git a/target/i386/sev.c b/target/i386/sev.c
index cdadd83ab5..5904f2c983 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -66,6 +66,12 @@ OBJECT_DECLARE_TYPE(SevSnpGuestState, SevCommonStateClass, 
SEV_SNP_GUEST)
 #define FLAGS_SEGCACHE_TO_VMSA(flags) \
     ((((flags) & 0xff00) >> 8) | (((flags) & 0xf00000) >> 12))
 
+/* SEV-EMULATED default values */
+#define INVALID_FD -1
+#define FAKE_BUILD_ID 40
+#define FAKE_API_MAJOR 1
+#define FAKE_API_MINOR 40
+
 typedef struct QEMU_PACKED SevHashTableEntry {
     QemuUUID guid;
     uint16_t len;
@@ -191,6 +197,7 @@ struct SevGuestState {
 
 typedef struct SevEmulatedState {
     SevGuestState parent_obj;
+    QEMUIOVector ld_data;
 } SevEmulatedState;
 
 struct SevSnpGuestState {
@@ -915,6 +922,12 @@ static SevCapability *sev_get_capabilities(Error **errp)
     SevCommonState *sev_common;
     char *sev_device;
 
+    if (sev_emulated_enabled()) {
+        error_setg(errp, "SEV emulation does not support"
+                                    "returning capabilities");
+        return NULL;
+    }
+
     if (!kvm_enabled()) {
         error_setg(errp, "KVM not enabled");
         return NULL;
@@ -1024,6 +1037,12 @@ static SevAttestationReport 
*sev_get_attestation_report(const char *mnonce,
         return NULL;
     }
 
+    if (sev_emulated_enabled()) {
+        error_setg(errp, "SEV emulation does not support"
+                                    "attestation report");
+        return NULL;
+    }
+
     /* lets decode the mnonce string */
     buf = g_base64_decode(mnonce, &len);
     if (!buf) {
@@ -1752,6 +1771,21 @@ static int sev_kvm_type(X86ConfidentialGuest *cg)
      */
     kvm_type = (sev_guest->policy & SEV_POLICY_ES) ?
                 KVM_X86_SEV_ES_VM : KVM_X86_SEV_VM;
+
+    /*
+     * If we are in emulated mode, force the legacy VM type as the only
+     * actively supported option.
+     */
+    if (sev_emulated_enabled()) {
+        if (kvm_type != KVM_X86_DEFAULT_VM) {
+            warn_report("Only legacy VM are supported in emulated mode:"
+            " using KVM_X86_DEFAULT_VM");
+            kvm_type = KVM_X86_DEFAULT_VM;
+        }
+        sev_common->kvm_type = kvm_type;
+        goto out;
+    }
+
     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 "
@@ -2973,6 +3007,10 @@ sev_guest_instance_init(Object *obj)
 static int sev_emulated_init(ConfidentialGuestSupport *cgs, Error **errp)
 {
     SevCommonState *sev_common = SEV_COMMON(cgs);
+    SevGuestState *sev_guest = SEV_GUEST(sev_common);
+    SevEmulatedState *sev_emulated = SEV_EMULATED(sev_guest);
+
+    sev_common->state = SEV_STATE_UNINIT;
 
     /*
      * The cbitpos value will be placed in bit positions 5:0 of the EBX
@@ -2999,15 +3037,53 @@ static int sev_emulated_init(ConfidentialGuestSupport 
*cgs, Error **errp)
                    __func__, sev_common->reduced_phys_bits);
         return -1;
     }
+    /*
+     * The device does not exist so we initialize the values as default.
+     * We can skip to SEV_STATE_LAUNCH_UPDATE as there is nothing to encrypt.
+     * This avoids the launch start call.
+     */
+    sev_set_guest_state(sev_common, SEV_STATE_LAUNCH_UPDATE);
+    sev_common->sev_fd = INVALID_FD;
+    sev_common->build_id = FAKE_BUILD_ID;
+    sev_common->api_major = FAKE_API_MAJOR;
+    sev_common->api_minor = FAKE_API_MINOR;
+
+    /* Initialize the iovec for the measurements blobs */
+    qemu_iovec_init(&sev_emulated->ld_data, 3);
+
+    qemu_add_vm_change_state_handler(sev_vm_state_change, sev_common);
+
     cgs->ready = true;
     return 0;
 }
 
+static int sev_emulated_launch_update_data(SevCommonState *sev_common,
+        hwaddr gpa, uint8_t *addr, size_t len, Error **errp)
+{
+    SevEmulatedState *sev_emulated = SEV_EMULATED(sev_common);
+
+    if (!addr || !len) {
+        return 1;
+    }
+    qemu_iovec_add(&sev_emulated->ld_data, addr, len);
+
+    return 0;
+}
+
+static void
+sev_emulated_launch_finish(SevCommonState *sev_common)
+{
+    sev_set_guest_state(sev_common, SEV_STATE_RUNNING);
+}
+
 static void sev_emulated_class_init(ObjectClass *oc, const void *data)
 {
+    SevCommonStateClass *scc = SEV_COMMON_CLASS(oc);
     ConfidentialGuestSupportClass *klass = 
CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc);
-    /* Override the sev-common method that uses kvm */
+    /* Override the sev-common methods that use kvm */
     klass->kvm_init = sev_emulated_init;
+    scc->launch_update_data = sev_emulated_launch_update_data;
+    scc->launch_finish = sev_emulated_launch_finish;
 }
 
 /* guest info specific sev/sev-es */
-- 
2.53.0

Reply via email to