@samitouri / QOSamiQemu / commits / ec9bafd2ea

i386/sev: add support for confidential guest reset

When the KVM VM file descriptor changes as a part of the confidential guest reset mechanism, it necessary to create a new confidential guest context and re-encrypt the VM memory. This happens for SEV-ES and SEV-SNP virtual machines as a part of SEV_LAUNCH_FINISH, SEV_SNP_LAUNCH_FINISH operations. A new resettable interface for SEV module has been added. A new reset callback for the reset 'exit' state has been implemented to perform the above operations when the VM file descriptor has changed during VM reset. Tracepoints has been added also for tracing purpose. Signed-off-by: Ani Sinha <anisinha@redhat.com> Link: https://lore.kernel.org/r/20260225035000.385950-23-anisinha@redhat.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

Ani Sinha committed Feb 25, 2026 at 09:19 UTC ec9bafd2ea9d12cdd607c12fbb3084e34ea40099
2 files changed +59
target/i386/sev.c
+58
@@ -30,8 +30,10 @@
30 #include "system/kvm.h"
31 #include "kvm/kvm_i386.h"
32 #include "sev.h"
33 +#include "system/cpus.h"
34 #include "system/system.h"
35 #include "system/runstate.h"
36 +#include "system/reset.h"
37 #include "trace.h"
38 #include "migration/blocker.h"
39 #include "qom/object.h"
@@ -86,6 +88,10 @@ typedef struct QEMU_PACKED PaddedSevHashTable {
88 uint8_t padding[ROUND_UP(sizeof(SevHashTable), 16) - sizeof(SevHashTable)];
89 } PaddedSevHashTable;
90
91 +static void sev_handle_reset(Object *obj, ResetType type);
92 +
93 +SevKernelLoaderContext sev_load_ctx = {};
94 +
95 QEMU_BUILD_BUG_ON(sizeof(PaddedSevHashTable) % 16 != 0);
96
97 #define SEV_INFO_BLOCK_GUID "00f771de-1a7e-4fcb-890e-68c77e2fb44e"
@@ -129,6 +135,7 @@ struct SevCommonState {
135 uint8_t build_id;
136 int sev_fd;
137 SevState state;
138 + ResettableState reset_state;
139
140 QTAILQ_HEAD(, SevLaunchVmsa) launch_vmsa;
141 };
@@ -1666,6 +1673,11 @@ sev_vm_state_change(void *opaque, bool running, RunState state)
1673 error_setg(&sev_mig_blocker,
1674 "SEV: Migration is not implemented");
1675 migrate_add_blocker(&sev_mig_blocker, &error_fatal);
1676 + /*
1677 + * mark SEV guest as resettable so that we can reinitialize
1678 + * SEV upon reset.
1679 + */
1680 + qemu_register_resettable(OBJECT(sev_common));
1681 }
1682 }
1683 }
@@ -1991,6 +2003,41 @@ static int sev_snp_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
2003 return 0;
2004 }
2005
2006 +/*
2007 + * handle sev vm reset
2008 + */
2009 +static void sev_handle_reset(Object *obj, ResetType type)
2010 +{
2011 + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs);
2012 + SevCommonStateClass *klass = SEV_COMMON_GET_CLASS(sev_common);
2013 +
2014 + if (!sev_common) {
2015 + return;
2016 + }
2017 +
2018 + if (!runstate_is_running()) {
2019 + return;
2020 + }
2021 +
2022 + sev_add_kernel_loader_hashes(&sev_load_ctx, &error_fatal);
2023 + if (sev_es_enabled() && !sev_snp_enabled()) {
2024 + sev_launch_get_measure(NULL, NULL);
2025 + }
2026 + if (!sev_check_state(sev_common, SEV_STATE_RUNNING)) {
2027 + /* this calls sev_snp_launch_finish() etc */
2028 + klass->launch_finish(sev_common);
2029 + }
2030 +
2031 + trace_sev_handle_reset();
2032 + return;
2033 +}
2034 +
2035 +static ResettableState *sev_reset_state(Object *obj)
2036 +{
2037 + SevCommonState *sev_common = SEV_COMMON(obj);
2038 + return &sev_common->reset_state;
2039 +}
2040 +
2041 int
2042 sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp)
2043 {
@@ -2469,6 +2516,8 @@ bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp)
2516 return false;
2517 }
2518
2519 + /* save the context here so that it can be re-used when vm is reset */
2520 + memcpy(&sev_load_ctx, ctx, sizeof(*ctx));
2521 return klass->build_kernel_loader_hashes(sev_common, area, ctx, errp);
2522 }
2523
@@ -2729,8 +2778,16 @@ static void
2778 sev_common_class_init(ObjectClass *oc, const void *data)
2779 {
2780 ConfidentialGuestSupportClass *klass = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc);
2781 + ResettableClass *rc = RESETTABLE_CLASS(oc);
2782
2783 klass->kvm_init = sev_common_kvm_init;
2784 + /*
2785 + * the exit phase makes sure sev handles reset after all legacy resets
2786 + * have taken place (in the hold phase) and IGVM has also properly
2787 + * set up the boot state.
2788 + */
2789 + rc->phases.exit = sev_handle_reset;
2790 + rc->get_state = sev_reset_state;
2791
2792 object_class_property_add_str(oc, "sev-device",
2793 sev_common_get_sev_device,
@@ -2780,6 +2837,7 @@ static const TypeInfo sev_common_info = {
2837 .abstract = true,
2838 .interfaces = (const InterfaceInfo[]) {
2839 { TYPE_USER_CREATABLE },
2840 + { TYPE_RESETTABLE_INTERFACE },
2841 { }
2842 }
2843 };
target/i386/trace-events
+1
@@ -14,3 +14,4 @@ kvm_sev_attestation_report(const char *mnonce, const char *data) "mnonce %s data
14 kvm_sev_snp_launch_start(uint64_t policy, char *gosvw) "policy 0x%" PRIx64 " gosvw %s"
15 kvm_sev_snp_launch_update(uint64_t src, uint64_t gpa, uint64_t len, const char *type) "src 0x%" PRIx64 " gpa 0x%" PRIx64 " len 0x%" PRIx64 " (%s page)"
16 kvm_sev_snp_launch_finish(char *id_block, char *id_auth, char *host_data) "id_block %s id_auth %s host_data %s"
17 +sev_handle_reset(void) ""