* [PATCH 1/5] i386/sev: Add sev-emulated QOM object with TCG support
2026-03-17 11:38 [PATCH 0/5] i386/sev: Add TCG-emulated AMD SEV guest support Tommaso Califano
@ 2026-03-17 11:38 ` Tommaso Califano
2026-03-19 12:31 ` Markus Armbruster
2026-03-19 17:49 ` Daniel P. Berrangé
2026-03-17 11:38 ` [PATCH 2/5] target/i386: Add MSR SEV support and C-bit reset on TCG Tommaso Califano
` (4 subsequent siblings)
5 siblings, 2 replies; 21+ messages in thread
From: Tommaso Califano @ 2026-03-17 11:38 UTC (permalink / raw)
To: qemu-devel
Cc: kvm, Eduardo Habkost, Markus Armbruster, Zhao Liu,
Daniel P. Berrangé, Marcelo Tosatti, Eric Blake,
Oliver Steffen, Stefano Garzarella, Giuseppe Lettieri,
Paolo Bonzini, Luigi Leonardi, Richard Henderson,
Tommaso Califano
QEMU's AMD SEV support requires KVM on costly AMD EPYC processors,
limiting development and testing to users with specialized server
hardware. This makes it hard to validate SEV guest behavior, like
OVMF boots or SEV-aware software, on common dev machines.
A solution to this is the emulation of SEV from the guest's
perspective using TCG.
This change begins this process with the exposure of the SEV CPUID leaf.
In target/i386/cpu.c:cpu_x86_cpuid() case 0x8000001F:
case 0x8000001F:
*eax = *ebx = *ecx = *edx = 0;
if (sev_enabled()) {
*eax = 0x2;
*eax |= sev_es_enabled() ? 0x8 : 0;
*eax |= sev_snp_enabled() ? 0x10 : 0;
*ebx = sev_get_cbit_position() & 0x3f; /* EBX[5:0] */
*ebx |= (sev_get_reduced_phys_bits() & 0x3f) << 6; /* EBX[11:6] */
}
break;
sev_enabled() verifies if the QOM object is TYPE_SEV_GUEST;
TYPE_SEV_EMULATED is derived from TYPE_SEV_GUEST with SevEmulatedState
to satisfy this check with minimal changes. In particular this allows
to bypass all the sev_enabled() checks for future features.
Since KVM hardware isn't available, override the QOM's kvm_init() and add
a conditional confidential_guest_kvm_init() call during machine_init() to
set up emulated confidential support using the ConfidentialGuestSupport
structure.
With this change it is possible to run a VM with the SEV CPUID active
adding:
-accel tcg \
-object sev-emulated,id=sev0,cbitpos=47,reduced-phys-bits=1 \
-machine memory-encryption=sev0
To the QEMU start arguments.
Signed-off-by: Tommaso Califano <califano.tommaso@gmail.com>
---
accel/tcg/tcg-all.c | 18 ++++++++++++-
qapi/qom.json | 15 +++++++++++
target/i386/sev.c | 65 +++++++++++++++++++++++++++++++++++++++++++++
target/i386/sev.h | 1 +
4 files changed, 98 insertions(+), 1 deletion(-)
diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c
index 8eb4a6b89e..d55827e214 100644
--- a/accel/tcg/tcg-all.c
+++ b/accel/tcg/tcg-all.c
@@ -45,7 +45,7 @@
#include "accel/accel-cpu-ops.h"
#include "accel/tcg/cpu-ops.h"
#include "internal-common.h"
-
+#include "system/confidential-guest-support.h"
struct TCGState {
AccelState parent_obj;
@@ -107,6 +107,9 @@ static int tcg_init_machine(AccelState *as, MachineState *ms)
unsigned max_threads = 1;
#ifndef CONFIG_USER_ONLY
+ int ret;
+ Error *local_err = NULL;
+ const char *cgs_type = NULL;
CPUClass *cc = CPU_CLASS(object_class_by_name(target_cpu_type()));
bool mttcg_supported = cc->tcg_ops->mttcg_supported;
@@ -163,6 +166,19 @@ static int tcg_init_machine(AccelState *as, MachineState *ms)
#ifdef CONFIG_USER_ONLY
qdev_create_fake_machine();
+#else
+ /* TCG SEV/Confidential Guest Support init */
+ if (ms->cgs) {
+ cgs_type = object_get_typename(OBJECT(ms->cgs));
+
+ if (g_str_has_prefix(cgs_type, "sev-emulated")) {
+ ret = confidential_guest_kvm_init(ms->cgs, &local_err);
+ if (ret < 0) {
+ error_report_err(local_err);
+ return ret;
+ }
+ }
+ }
#endif
return 0;
diff --git a/qapi/qom.json b/qapi/qom.json
index c653248f85..35cda819ec 100644
--- a/qapi/qom.json
+++ b/qapi/qom.json
@@ -1057,6 +1057,19 @@
'*handle': 'uint32',
'*legacy-vm-type': 'OnOffAuto' } }
+##
+# @SevEmulatedProperties:
+#
+# Properties for sev-emulated objects.
+# This object functionally emulates AMD SEV hardware via TCG, so
+# it does not require real hardware to run.
+#
+# Since: 10.1.0
+##
+{ 'struct': 'SevEmulatedProperties',
+ 'base': 'SevGuestProperties',
+ 'data': {}}
+
##
# @SevSnpGuestProperties:
#
@@ -1241,6 +1254,7 @@
{ 'name': 'secret_keyring',
'if': 'CONFIG_SECRET_KEYRING' },
'sev-guest',
+ 'sev-emulated',
'sev-snp-guest',
'thread-context',
's390-pv-guest',
@@ -1318,6 +1332,7 @@
'secret_keyring': { 'type': 'SecretKeyringProperties',
'if': 'CONFIG_SECRET_KEYRING' },
'sev-guest': 'SevGuestProperties',
+ 'sev-emulated': 'SevEmulatedProperties',
'sev-snp-guest': 'SevSnpGuestProperties',
'tdx-guest': 'TdxGuestProperties',
'thread-context': 'ThreadContextProperties',
diff --git a/target/i386/sev.c b/target/i386/sev.c
index 9dde972c11..2502e860e2 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -51,6 +51,7 @@
OBJECT_DECLARE_TYPE(SevCommonState, SevCommonStateClass, SEV_COMMON)
OBJECT_DECLARE_TYPE(SevGuestState, SevCommonStateClass, SEV_GUEST)
+OBJECT_DECLARE_TYPE(SevEmulatedState, SevCommonStateClass, SEV_EMULATED)
OBJECT_DECLARE_TYPE(SevSnpGuestState, SevCommonStateClass, SEV_SNP_GUEST)
/* hard code sha256 digest size */
@@ -177,6 +178,21 @@ struct SevGuestState {
OnOffAuto legacy_vm_type;
};
+/**
+ * SevEmulatedState:
+ *
+ * The SevEmulatedState object is used for creating and managing a SEV emulated
+ * guest.
+ *
+ * # $QEMU \
+ * -object sev-emulated,id=sev0 \
+ * -machine ...,memory-encryption=sev0
+ */
+
+typedef struct SevEmulatedState {
+ SevGuestState parent_obj;
+} SevEmulatedState;
+
struct SevSnpGuestState {
SevCommonState parent_obj;
@@ -2936,6 +2952,46 @@ sev_guest_instance_init(Object *obj)
sev_guest->legacy_vm_type = ON_OFF_AUTO_AUTO;
}
+static int sev_emulated_init(ConfidentialGuestSupport *cgs, Error **errp)
+{
+ SevCommonState *sev_common = SEV_COMMON(cgs);
+
+ /*
+ * The cbitpos value will be placed in bit positions 5:0 of the EBX
+ * register of CPUID 0x8000001F. We need to verify the range as the
+ * comparison with the host cbitpos is missing.
+ */
+ if (sev_common->cbitpos < 32 ||
+ sev_common->cbitpos > 63) {
+ error_setg(errp, "%s: cbitpos check failed, requested '%d',"
+ "the firmware requires >=32",
+ __func__, sev_common->cbitpos);
+ return -1;
+ }
+
+ /*
+ * The reduced-phys-bits value will be placed in bit positions 11:6 of
+ * the EBX register of CPUID 0x8000001F, so verify the supplied value
+ * is in the range of 1 to 63.
+ */
+ if (sev_common->reduced_phys_bits < 1 ||
+ sev_common->reduced_phys_bits > 63) {
+ error_setg(errp, "%s: reduced_phys_bits check failed,"
+ " it should be in the range of 1 to 63, requested '%d'",
+ __func__, sev_common->reduced_phys_bits);
+ return -1;
+ }
+ cgs->ready = true;
+ return 0;
+}
+
+static void sev_emulated_class_init(ObjectClass *oc, const void *data)
+{
+ ConfidentialGuestSupportClass *klass = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc);
+ /* Override the sev-common method that uses kvm */
+ klass->kvm_init = sev_emulated_init;
+}
+
/* guest info specific sev/sev-es */
static const TypeInfo sev_guest_info = {
.parent = TYPE_SEV_COMMON,
@@ -2945,6 +3001,14 @@ static const TypeInfo sev_guest_info = {
.class_init = sev_guest_class_init,
};
+/* emulated sev */
+static const TypeInfo sev_emulated_info = {
+ .parent = TYPE_SEV_GUEST,
+ .name = TYPE_SEV_EMULATED,
+ .instance_size = sizeof(SevEmulatedState),
+ .class_init = sev_emulated_class_init
+};
+
static void
sev_snp_guest_get_policy(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
@@ -3207,6 +3271,7 @@ static void
sev_register_types(void)
{
type_register_static(&sev_common_info);
+ type_register_static(&sev_emulated_info);
type_register_static(&sev_guest_info);
type_register_static(&sev_snp_guest_info);
}
diff --git a/target/i386/sev.h b/target/i386/sev.h
index 4358df40e4..839656e2be 100644
--- a/target/i386/sev.h
+++ b/target/i386/sev.h
@@ -33,6 +33,7 @@ bool sev_snp_enabled(void);
#if !defined(CONFIG_USER_ONLY)
#define TYPE_SEV_COMMON "sev-common"
+#define TYPE_SEV_EMULATED "sev-emulated"
#define TYPE_SEV_GUEST "sev-guest"
#define TYPE_SEV_SNP_GUEST "sev-snp-guest"
--
2.53.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH 1/5] i386/sev: Add sev-emulated QOM object with TCG support
2026-03-17 11:38 ` [PATCH 1/5] i386/sev: Add sev-emulated QOM object with TCG support Tommaso Califano
@ 2026-03-19 12:31 ` Markus Armbruster
2026-03-20 14:25 ` Tommaso Califano
2026-03-19 17:49 ` Daniel P. Berrangé
1 sibling, 1 reply; 21+ messages in thread
From: Markus Armbruster @ 2026-03-19 12:31 UTC (permalink / raw)
To: Tommaso Califano
Cc: qemu-devel, kvm, Eduardo Habkost, Zhao Liu,
Daniel P. Berrangé, Marcelo Tosatti, Eric Blake,
Oliver Steffen, Stefano Garzarella, Giuseppe Lettieri,
Paolo Bonzini, Luigi Leonardi, Richard Henderson
Tommaso Califano <califano.tommaso@gmail.com> writes:
> QEMU's AMD SEV support requires KVM on costly AMD EPYC processors,
> limiting development and testing to users with specialized server
> hardware. This makes it hard to validate SEV guest behavior, like
> OVMF boots or SEV-aware software, on common dev machines.
> A solution to this is the emulation of SEV from the guest's
> perspective using TCG.
>
> This change begins this process with the exposure of the SEV CPUID leaf.
> In target/i386/cpu.c:cpu_x86_cpuid() case 0x8000001F:
>
> case 0x8000001F:
> *eax = *ebx = *ecx = *edx = 0;
> if (sev_enabled()) {
> *eax = 0x2;
> *eax |= sev_es_enabled() ? 0x8 : 0;
> *eax |= sev_snp_enabled() ? 0x10 : 0;
> *ebx = sev_get_cbit_position() & 0x3f; /* EBX[5:0] */
> *ebx |= (sev_get_reduced_phys_bits() & 0x3f) << 6; /* EBX[11:6] */
> }
> break;
>
> sev_enabled() verifies if the QOM object is TYPE_SEV_GUEST;
> TYPE_SEV_EMULATED is derived from TYPE_SEV_GUEST with SevEmulatedState
> to satisfy this check with minimal changes. In particular this allows
> to bypass all the sev_enabled() checks for future features.
>
> Since KVM hardware isn't available, override the QOM's kvm_init() and add
> a conditional confidential_guest_kvm_init() call during machine_init() to
> set up emulated confidential support using the ConfidentialGuestSupport
> structure.
>
> With this change it is possible to run a VM with the SEV CPUID active
> adding:
>
> -accel tcg \
> -object sev-emulated,id=sev0,cbitpos=47,reduced-phys-bits=1 \
> -machine memory-encryption=sev0
>
> To the QEMU start arguments.
>
> Signed-off-by: Tommaso Califano <califano.tommaso@gmail.com>
[...]
> diff --git a/qapi/qom.json b/qapi/qom.json
> index c653248f85..35cda819ec 100644
> --- a/qapi/qom.json
> +++ b/qapi/qom.json
> @@ -1057,6 +1057,19 @@
> '*handle': 'uint32',
> '*legacy-vm-type': 'OnOffAuto' } }
>
> +##
> +# @SevEmulatedProperties:
> +#
> +# Properties for sev-emulated objects.
> +# This object functionally emulates AMD SEV hardware via TCG, so
> +# it does not require real hardware to run.
Wrap the paragraph, please:
# Properties for sev-emulated objects. This object functionally
# emulates AMD SEV hardware via TCG, so it does not require real
# hardware to run.
> +#
> +# Since: 10.1.0
11.0 right now, but realistically 11.1.
> +##
> +{ 'struct': 'SevEmulatedProperties',
> + 'base': 'SevGuestProperties',
> + 'data': {}}
> +
> ##
> # @SevSnpGuestProperties:
> #
> @@ -1241,6 +1254,7 @@
> { 'name': 'secret_keyring',
> 'if': 'CONFIG_SECRET_KEYRING' },
> 'sev-guest',
> + 'sev-emulated',
> 'sev-snp-guest',
> 'thread-context',
> 's390-pv-guest',
Please insert before sev-guest to keep things more or less sorted.
> @@ -1318,6 +1332,7 @@
> 'secret_keyring': { 'type': 'SecretKeyringProperties',
> 'if': 'CONFIG_SECRET_KEYRING' },
> 'sev-guest': 'SevGuestProperties',
> + 'sev-emulated': 'SevEmulatedProperties',
Likewise.
> 'sev-snp-guest': 'SevSnpGuestProperties',
> 'tdx-guest': 'TdxGuestProperties',
> 'thread-context': 'ThreadContextProperties',
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH 1/5] i386/sev: Add sev-emulated QOM object with TCG support
2026-03-19 12:31 ` Markus Armbruster
@ 2026-03-20 14:25 ` Tommaso Califano
2026-03-20 14:48 ` Markus Armbruster
0 siblings, 1 reply; 21+ messages in thread
From: Tommaso Califano @ 2026-03-20 14:25 UTC (permalink / raw)
To: Markus Armbruster
Cc: qemu-devel, kvm, Eduardo Habkost, Zhao Liu,
Daniel P. Berrangé, Marcelo Tosatti, Eric Blake,
Oliver Steffen, Stefano Garzarella, Giuseppe Lettieri,
Paolo Bonzini, Luigi Leonardi, Richard Henderson
Il 19/03/26 13:31, Markus Armbruster ha scritto:
> Tommaso Califano <califano.tommaso@gmail.com> writes:
>
>> QEMU's AMD SEV support requires KVM on costly AMD EPYC processors,
>> limiting development and testing to users with specialized server
>> hardware. This makes it hard to validate SEV guest behavior, like
>> OVMF boots or SEV-aware software, on common dev machines.
>> A solution to this is the emulation of SEV from the guest's
>> perspective using TCG.
>>
>> This change begins this process with the exposure of the SEV CPUID leaf.
>> In target/i386/cpu.c:cpu_x86_cpuid() case 0x8000001F:
>>
>> case 0x8000001F:
>> *eax = *ebx = *ecx = *edx = 0;
>> if (sev_enabled()) {
>> *eax = 0x2;
>> *eax |= sev_es_enabled() ? 0x8 : 0;
>> *eax |= sev_snp_enabled() ? 0x10 : 0;
>> *ebx = sev_get_cbit_position() & 0x3f; /* EBX[5:0] */
>> *ebx |= (sev_get_reduced_phys_bits() & 0x3f) << 6; /* EBX[11:6] */
>> }
>> break;
>>
>> sev_enabled() verifies if the QOM object is TYPE_SEV_GUEST;
>> TYPE_SEV_EMULATED is derived from TYPE_SEV_GUEST with SevEmulatedState
>> to satisfy this check with minimal changes. In particular this allows
>> to bypass all the sev_enabled() checks for future features.
>>
>> Since KVM hardware isn't available, override the QOM's kvm_init() and add
>> a conditional confidential_guest_kvm_init() call during machine_init() to
>> set up emulated confidential support using the ConfidentialGuestSupport
>> structure.
>>
>> With this change it is possible to run a VM with the SEV CPUID active
>> adding:
>>
>> -accel tcg \
>> -object sev-emulated,id=sev0,cbitpos=47,reduced-phys-bits=1 \
>> -machine memory-encryption=sev0
>>
>> To the QEMU start arguments.
>>
>> Signed-off-by: Tommaso Califano <califano.tommaso@gmail.com>
>
> [...]
>
>> diff --git a/qapi/qom.json b/qapi/qom.json
>> index c653248f85..35cda819ec 100644
>> --- a/qapi/qom.json
>> +++ b/qapi/qom.json
>> @@ -1057,6 +1057,19 @@
>> '*handle': 'uint32',
>> '*legacy-vm-type': 'OnOffAuto' } }
>>
>> +##
>> +# @SevEmulatedProperties:
>> +#
>> +# Properties for sev-emulated objects.
>> +# This object functionally emulates AMD SEV hardware via TCG, so
>> +# it does not require real hardware to run.
>
> Wrap the paragraph, please:
>
> # Properties for sev-emulated objects. This object functionally
> # emulates AMD SEV hardware via TCG, so it does not require real
> # hardware to run.
>
I'll do it.
>> +#
>> +# Since: 10.1.0
>
> 11.0 right now, but realistically 11.1.
>
I'll update it to 11.1.
>> +##
>> +{ 'struct': 'SevEmulatedProperties',
>> + 'base': 'SevGuestProperties',
>> + 'data': {}}
>> +
>> ##
>> # @SevSnpGuestProperties:
>> #
>> @@ -1241,6 +1254,7 @@
>> { 'name': 'secret_keyring',
>> 'if': 'CONFIG_SECRET_KEYRING' },
>> 'sev-guest',
>> + 'sev-emulated',
>> 'sev-snp-guest',
>> 'thread-context',
>> 's390-pv-guest',
>
> Please insert before sev-guest to keep things more or less sorted.
>
I'll do it, but I don't understand the convention. I'd organized them
by object derivation hierarchy, so what is the expected sorting order?
>> @@ -1318,6 +1332,7 @@
>> 'secret_keyring': { 'type': 'SecretKeyringProperties',
>> 'if': 'CONFIG_SECRET_KEYRING' },
>> 'sev-guest': 'SevGuestProperties',
>> + 'sev-emulated': 'SevEmulatedProperties',
>
> Likewise.
>
Yes.
>> 'sev-snp-guest': 'SevSnpGuestProperties',
>> 'tdx-guest': 'TdxGuestProperties',
>> 'thread-context': 'ThreadContextProperties',
>
Best regards,
Tommaso Califano
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH 1/5] i386/sev: Add sev-emulated QOM object with TCG support
2026-03-20 14:25 ` Tommaso Califano
@ 2026-03-20 14:48 ` Markus Armbruster
2026-03-20 15:34 ` Tommaso Califano
0 siblings, 1 reply; 21+ messages in thread
From: Markus Armbruster @ 2026-03-20 14:48 UTC (permalink / raw)
To: Tommaso Califano
Cc: qemu-devel, kvm, Eduardo Habkost, Zhao Liu,
Daniel P. Berrangé, Marcelo Tosatti, Eric Blake,
Oliver Steffen, Stefano Garzarella, Giuseppe Lettieri,
Paolo Bonzini, Luigi Leonardi, Richard Henderson
Tommaso Califano <califano.tommaso@gmail.com> writes:
> Il 19/03/26 13:31, Markus Armbruster ha scritto:
>> Tommaso Califano <califano.tommaso@gmail.com> writes:
>>
>>> QEMU's AMD SEV support requires KVM on costly AMD EPYC processors,
>>> limiting development and testing to users with specialized server
>>> hardware. This makes it hard to validate SEV guest behavior, like
>>> OVMF boots or SEV-aware software, on common dev machines.
>>> A solution to this is the emulation of SEV from the guest's
>>> perspective using TCG.
>>>
>>> This change begins this process with the exposure of the SEV CPUID leaf.
>>> In target/i386/cpu.c:cpu_x86_cpuid() case 0x8000001F:
>>>
>>> case 0x8000001F:
>>> *eax = *ebx = *ecx = *edx = 0;
>>> if (sev_enabled()) {
>>> *eax = 0x2;
>>> *eax |= sev_es_enabled() ? 0x8 : 0;
>>> *eax |= sev_snp_enabled() ? 0x10 : 0;
>>> *ebx = sev_get_cbit_position() & 0x3f; /* EBX[5:0] */
>>> *ebx |= (sev_get_reduced_phys_bits() & 0x3f) << 6; /* EBX[11:6] */
>>> }
>>> break;
>>>
>>> sev_enabled() verifies if the QOM object is TYPE_SEV_GUEST;
>>> TYPE_SEV_EMULATED is derived from TYPE_SEV_GUEST with SevEmulatedState
>>> to satisfy this check with minimal changes. In particular this allows
>>> to bypass all the sev_enabled() checks for future features.
>>>
>>> Since KVM hardware isn't available, override the QOM's kvm_init() and add
>>> a conditional confidential_guest_kvm_init() call during machine_init() to
>>> set up emulated confidential support using the ConfidentialGuestSupport
>>> structure.
>>>
>>> With this change it is possible to run a VM with the SEV CPUID active
>>> adding:
>>>
>>> -accel tcg \
>>> -object sev-emulated,id=sev0,cbitpos=47,reduced-phys-bits=1 \
>>> -machine memory-encryption=sev0
>>>
>>> To the QEMU start arguments.
>>>
>>> Signed-off-by: Tommaso Califano <califano.tommaso@gmail.com>
>>
>> [...]
>>
>>> diff --git a/qapi/qom.json b/qapi/qom.json
>>> index c653248f85..35cda819ec 100644
>>> --- a/qapi/qom.json
>>> +++ b/qapi/qom.json
[...]
>>> @@ -1241,6 +1254,7 @@
>>> { 'name': 'secret_keyring',
>>> 'if': 'CONFIG_SECRET_KEYRING' },
>>> 'sev-guest',
>>> + 'sev-emulated',
>>> 'sev-snp-guest',
>>> 'thread-context',
>>> 's390-pv-guest',
>>
>> Please insert before sev-guest to keep things more or less sorted.
>>
>
> I'll do it, but I don't understand the convention. I'd organized them
> by object derivation hierarchy, so what is the expected sorting order?
It looks alphabetical modulo lazy mistakes to me.
[...]
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH 1/5] i386/sev: Add sev-emulated QOM object with TCG support
2026-03-20 14:48 ` Markus Armbruster
@ 2026-03-20 15:34 ` Tommaso Califano
0 siblings, 0 replies; 21+ messages in thread
From: Tommaso Califano @ 2026-03-20 15:34 UTC (permalink / raw)
To: Markus Armbruster
Cc: qemu-devel, kvm, Eduardo Habkost, Zhao Liu,
Daniel P. Berrangé, Marcelo Tosatti, Eric Blake,
Oliver Steffen, Stefano Garzarella, Giuseppe Lettieri,
Paolo Bonzini, Luigi Leonardi, Richard Henderson
Il 20/03/26 15:48, Markus Armbruster ha scritto:
> Tommaso Califano <califano.tommaso@gmail.com> writes:
>
>> Il 19/03/26 13:31, Markus Armbruster ha scritto:
>>> Tommaso Califano <califano.tommaso@gmail.com> writes:
>>>
>>>> QEMU's AMD SEV support requires KVM on costly AMD EPYC processors,
>>>> limiting development and testing to users with specialized server
>>>> hardware. This makes it hard to validate SEV guest behavior, like
>>>> OVMF boots or SEV-aware software, on common dev machines.
>>>> A solution to this is the emulation of SEV from the guest's
>>>> perspective using TCG.
>>>>
>>>> This change begins this process with the exposure of the SEV CPUID leaf.
>>>> In target/i386/cpu.c:cpu_x86_cpuid() case 0x8000001F:
>>>>
>>>> case 0x8000001F:
>>>> *eax = *ebx = *ecx = *edx = 0;
>>>> if (sev_enabled()) {
>>>> *eax = 0x2;
>>>> *eax |= sev_es_enabled() ? 0x8 : 0;
>>>> *eax |= sev_snp_enabled() ? 0x10 : 0;
>>>> *ebx = sev_get_cbit_position() & 0x3f; /* EBX[5:0] */
>>>> *ebx |= (sev_get_reduced_phys_bits() & 0x3f) << 6; /* EBX[11:6] */
>>>> }
>>>> break;
>>>>
>>>> sev_enabled() verifies if the QOM object is TYPE_SEV_GUEST;
>>>> TYPE_SEV_EMULATED is derived from TYPE_SEV_GUEST with SevEmulatedState
>>>> to satisfy this check with minimal changes. In particular this allows
>>>> to bypass all the sev_enabled() checks for future features.
>>>>
>>>> Since KVM hardware isn't available, override the QOM's kvm_init() and add
>>>> a conditional confidential_guest_kvm_init() call during machine_init() to
>>>> set up emulated confidential support using the ConfidentialGuestSupport
>>>> structure.
>>>>
>>>> With this change it is possible to run a VM with the SEV CPUID active
>>>> adding:
>>>>
>>>> -accel tcg \
>>>> -object sev-emulated,id=sev0,cbitpos=47,reduced-phys-bits=1 \
>>>> -machine memory-encryption=sev0
>>>>
>>>> To the QEMU start arguments.
>>>>
>>>> Signed-off-by: Tommaso Califano <califano.tommaso@gmail.com>
>>>
>>> [...]
>>>
>>>> diff --git a/qapi/qom.json b/qapi/qom.json
>>>> index c653248f85..35cda819ec 100644
>>>> --- a/qapi/qom.json
>>>> +++ b/qapi/qom.json
>
> [...]
>
>>>> @@ -1241,6 +1254,7 @@
>>>> { 'name': 'secret_keyring',
>>>> 'if': 'CONFIG_SECRET_KEYRING' },
>>>> 'sev-guest',
>>>> + 'sev-emulated',
>>>> 'sev-snp-guest',
>>>> 'thread-context',
>>>> 's390-pv-guest',
>>>
>>> Please insert before sev-guest to keep things more or less sorted.
>>>
>>
>> I'll do it, but I don't understand the convention. I'd organized them
>> by object derivation hierarchy, so what is the expected sorting order?
>
> It looks alphabetical modulo lazy mistakes to me.
>
> [...]
>
Thanks for the clarification.
Best regards,
Tommaso Califano
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 1/5] i386/sev: Add sev-emulated QOM object with TCG support
2026-03-17 11:38 ` [PATCH 1/5] i386/sev: Add sev-emulated QOM object with TCG support Tommaso Califano
2026-03-19 12:31 ` Markus Armbruster
@ 2026-03-19 17:49 ` Daniel P. Berrangé
2026-03-20 7:44 ` Markus Armbruster
2026-03-20 12:39 ` Daniel P. Berrangé
1 sibling, 2 replies; 21+ messages in thread
From: Daniel P. Berrangé @ 2026-03-19 17:49 UTC (permalink / raw)
To: Tommaso Califano
Cc: qemu-devel, kvm, Eduardo Habkost, Markus Armbruster, Zhao Liu,
Marcelo Tosatti, Eric Blake, Oliver Steffen, Stefano Garzarella,
Giuseppe Lettieri, Paolo Bonzini, Luigi Leonardi,
Richard Henderson
On Tue, Mar 17, 2026 at 12:38:36PM +0100, Tommaso Califano wrote:
> With this change it is possible to run a VM with the SEV CPUID active
> adding:
>
> -accel tcg \
> -object sev-emulated,id=sev0,cbitpos=47,reduced-phys-bits=1 \
> -machine memory-encryption=sev0
snip
> diff --git a/qapi/qom.json b/qapi/qom.json
> index c653248f85..35cda819ec 100644
> --- a/qapi/qom.json
> +++ b/qapi/qom.json
> @@ -1057,6 +1057,19 @@
> '*handle': 'uint32',
> '*legacy-vm-type': 'OnOffAuto' } }
>
> +##
> +# @SevEmulatedProperties:
> +#
> +# Properties for sev-emulated objects.
> +# This object functionally emulates AMD SEV hardware via TCG, so
> +# it does not require real hardware to run.
> +#
> +# Since: 10.1.0
> +##
> +{ 'struct': 'SevEmulatedProperties',
> + 'base': 'SevGuestProperties',
> + 'data': {}}
This is deriving 'sev-emulated' from 'sev-guest' which means it
supports all the properties that 'sev-guest' does, which for
the record is:
sev-guest options:
dh-cert-file=<string> - guest owners DH certificate (encoded with base64)
kernel-hashes=<bool> - add kernel hashes to guest firmware for measured Linux boot
legacy-vm-type=<OnOffAuto> - use legacy VM type to maintain measurement compatibility with older QEMU or kernel versions.
session-file=<string> - guest owners session parameters (encoded with base64)
sev-device=<string> - SEV device to use
Of those properties
* dh-cert-file + session-file are traditionally used
as a means to transfer the TIK+TEK to the SEV firmware,
with wrapping to protect them from the hypervisor.
These can't be used with sev-emulated, as implemented,
since they require a key derivation from the PDH, a
concept which IIUC is not implemented in this series.
Instead, in a later patch 'tik' and 'tek' properties
are added to 'sev-emulated', and to pass the TIK+TEK
in clear text.
* sev-device + legacy-vm-type - these are only relevant
to the KVM integration, so not applicable for emulation
* kernel-hashes - would be relevant if formally emulating
LAUNCH_UPDATE_DATA for attestation, but IIUC, this is
not done/used by this series
IOW, we're deriving from 'sev-guest' but AFAICT none of
its properties are relevant to the emulation. The
dh-cert-file and session-file could potentially be
relevant if implementing the PDH concept and key
derivation, but that's not done, instead the tik/tek
are passed explicitly.
What is the value we get from this sev-guest -> sev-emulated
inheritance ? My gut feeling is that this perhaps isn't
the right way to be modelling things unless there's a plan
for future work that would benefit from them.
Another question related to modelling is whether there is
an intention to support SEV-SNP at a later date, would that
imply a sev-snp-emulated object type too ? If so, would it
inherit from sev-emulated or from sev-snp-guest ?
> +
> ##
> # @SevSnpGuestProperties:
> #
> @@ -1241,6 +1254,7 @@
> { 'name': 'secret_keyring',
> 'if': 'CONFIG_SECRET_KEYRING' },
> 'sev-guest',
> + 'sev-emulated',
> 'sev-snp-guest',
> 'thread-context',
> 's390-pv-guest',
> @@ -1318,6 +1332,7 @@
> 'secret_keyring': { 'type': 'SecretKeyringProperties',
> 'if': 'CONFIG_SECRET_KEYRING' },
> 'sev-guest': 'SevGuestProperties',
> + 'sev-emulated': 'SevEmulatedProperties',
> 'sev-snp-guest': 'SevSnpGuestProperties',
> 'tdx-guest': 'TdxGuestProperties',
> 'thread-context': 'ThreadContextProperties',
> diff --git a/target/i386/sev.c b/target/i386/sev.c
> index 9dde972c11..2502e860e2 100644
> --- a/target/i386/sev.c
> +++ b/target/i386/sev.c
> @@ -51,6 +51,7 @@
>
> OBJECT_DECLARE_TYPE(SevCommonState, SevCommonStateClass, SEV_COMMON)
> OBJECT_DECLARE_TYPE(SevGuestState, SevCommonStateClass, SEV_GUEST)
> +OBJECT_DECLARE_TYPE(SevEmulatedState, SevCommonStateClass, SEV_EMULATED)
> OBJECT_DECLARE_TYPE(SevSnpGuestState, SevCommonStateClass, SEV_SNP_GUEST)
>
> /* hard code sha256 digest size */
> @@ -177,6 +178,21 @@ struct SevGuestState {
> OnOffAuto legacy_vm_type;
> };
>
> +/**
> + * SevEmulatedState:
> + *
> + * The SevEmulatedState object is used for creating and managing a SEV emulated
> + * guest.
> + *
> + * # $QEMU \
> + * -object sev-emulated,id=sev0 \
> + * -machine ...,memory-encryption=sev0
> + */
> +
> +typedef struct SevEmulatedState {
> + SevGuestState parent_obj;
> +} SevEmulatedState;
> +
> struct SevSnpGuestState {
> SevCommonState parent_obj;
>
> @@ -2936,6 +2952,46 @@ sev_guest_instance_init(Object *obj)
> sev_guest->legacy_vm_type = ON_OFF_AUTO_AUTO;
> }
>
> +static int sev_emulated_init(ConfidentialGuestSupport *cgs, Error **errp)
> +{
> + SevCommonState *sev_common = SEV_COMMON(cgs);
> +
> + /*
> + * The cbitpos value will be placed in bit positions 5:0 of the EBX
> + * register of CPUID 0x8000001F. We need to verify the range as the
> + * comparison with the host cbitpos is missing.
> + */
> + if (sev_common->cbitpos < 32 ||
> + sev_common->cbitpos > 63) {
> + error_setg(errp, "%s: cbitpos check failed, requested '%d',"
> + "the firmware requires >=32",
> + __func__, sev_common->cbitpos);
> + return -1;
> + }
> +
> + /*
> + * The reduced-phys-bits value will be placed in bit positions 11:6 of
> + * the EBX register of CPUID 0x8000001F, so verify the supplied value
> + * is in the range of 1 to 63.
> + */
> + if (sev_common->reduced_phys_bits < 1 ||
> + sev_common->reduced_phys_bits > 63) {
> + error_setg(errp, "%s: reduced_phys_bits check failed,"
> + " it should be in the range of 1 to 63, requested '%d'",
> + __func__, sev_common->reduced_phys_bits);
> + return -1;
> + }
> + cgs->ready = true;
> + return 0;
> +}
> +
> +static void sev_emulated_class_init(ObjectClass *oc, const void *data)
> +{
> + ConfidentialGuestSupportClass *klass = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc);
> + /* Override the sev-common method that uses kvm */
> + klass->kvm_init = sev_emulated_init;
> +}
> +
> /* guest info specific sev/sev-es */
> static const TypeInfo sev_guest_info = {
> .parent = TYPE_SEV_COMMON,
> @@ -2945,6 +3001,14 @@ static const TypeInfo sev_guest_info = {
> .class_init = sev_guest_class_init,
> };
>
> +/* emulated sev */
> +static const TypeInfo sev_emulated_info = {
> + .parent = TYPE_SEV_GUEST,
> + .name = TYPE_SEV_EMULATED,
> + .instance_size = sizeof(SevEmulatedState),
> + .class_init = sev_emulated_class_init
> +};
> +
> static void
> sev_snp_guest_get_policy(Object *obj, Visitor *v, const char *name,
> void *opaque, Error **errp)
> @@ -3207,6 +3271,7 @@ static void
> sev_register_types(void)
> {
> type_register_static(&sev_common_info);
> + type_register_static(&sev_emulated_info);
> type_register_static(&sev_guest_info);
> type_register_static(&sev_snp_guest_info);
> }
> diff --git a/target/i386/sev.h b/target/i386/sev.h
> index 4358df40e4..839656e2be 100644
> --- a/target/i386/sev.h
> +++ b/target/i386/sev.h
> @@ -33,6 +33,7 @@ bool sev_snp_enabled(void);
> #if !defined(CONFIG_USER_ONLY)
>
> #define TYPE_SEV_COMMON "sev-common"
> +#define TYPE_SEV_EMULATED "sev-emulated"
> #define TYPE_SEV_GUEST "sev-guest"
> #define TYPE_SEV_SNP_GUEST "sev-snp-guest"
>
> --
> 2.53.0
>
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH 1/5] i386/sev: Add sev-emulated QOM object with TCG support
2026-03-19 17:49 ` Daniel P. Berrangé
@ 2026-03-20 7:44 ` Markus Armbruster
2026-03-20 12:40 ` Daniel P. Berrangé
2026-03-20 12:39 ` Daniel P. Berrangé
1 sibling, 1 reply; 21+ messages in thread
From: Markus Armbruster @ 2026-03-20 7:44 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: Tommaso Califano, qemu-devel, kvm, Eduardo Habkost, Zhao Liu,
Marcelo Tosatti, Eric Blake, Oliver Steffen, Stefano Garzarella,
Giuseppe Lettieri, Paolo Bonzini, Luigi Leonardi,
Richard Henderson
Daniel P. Berrangé <berrange@redhat.com> writes:
> On Tue, Mar 17, 2026 at 12:38:36PM +0100, Tommaso Califano wrote:
>> With this change it is possible to run a VM with the SEV CPUID active
>> adding:
>>
>> -accel tcg \
>> -object sev-emulated,id=sev0,cbitpos=47,reduced-phys-bits=1 \
>> -machine memory-encryption=sev0
>
> snip
>
>> diff --git a/qapi/qom.json b/qapi/qom.json
>> index c653248f85..35cda819ec 100644
>> --- a/qapi/qom.json
>> +++ b/qapi/qom.json
>> @@ -1057,6 +1057,19 @@
>> '*handle': 'uint32',
>> '*legacy-vm-type': 'OnOffAuto' } }
>>
>> +##
>> +# @SevEmulatedProperties:
>> +#
>> +# Properties for sev-emulated objects.
>> +# This object functionally emulates AMD SEV hardware via TCG, so
>> +# it does not require real hardware to run.
>> +#
>> +# Since: 10.1.0
>> +##
>> +{ 'struct': 'SevEmulatedProperties',
>> + 'base': 'SevGuestProperties',
>> + 'data': {}}
>
> This is deriving 'sev-emulated' from 'sev-guest' which means it
> supports all the properties that 'sev-guest' does, which for
> the record is:
Uh, I somehow misread the base as SevCommonProperties! Had I read
correctly, I would've had similar questions.
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH 1/5] i386/sev: Add sev-emulated QOM object with TCG support
2026-03-20 7:44 ` Markus Armbruster
@ 2026-03-20 12:40 ` Daniel P. Berrangé
2026-03-20 15:23 ` Tommaso Califano
2026-03-23 7:24 ` Markus Armbruster
0 siblings, 2 replies; 21+ messages in thread
From: Daniel P. Berrangé @ 2026-03-20 12:40 UTC (permalink / raw)
To: Markus Armbruster
Cc: Tommaso Califano, qemu-devel, kvm, Eduardo Habkost, Zhao Liu,
Marcelo Tosatti, Eric Blake, Oliver Steffen, Stefano Garzarella,
Giuseppe Lettieri, Paolo Bonzini, Luigi Leonardi,
Richard Henderson
On Fri, Mar 20, 2026 at 08:44:40AM +0100, Markus Armbruster wrote:
> Daniel P. Berrangé <berrange@redhat.com> writes:
>
> > On Tue, Mar 17, 2026 at 12:38:36PM +0100, Tommaso Califano wrote:
> >> With this change it is possible to run a VM with the SEV CPUID active
> >> adding:
> >>
> >> -accel tcg \
> >> -object sev-emulated,id=sev0,cbitpos=47,reduced-phys-bits=1 \
> >> -machine memory-encryption=sev0
> >
> > snip
> >
> >> diff --git a/qapi/qom.json b/qapi/qom.json
> >> index c653248f85..35cda819ec 100644
> >> --- a/qapi/qom.json
> >> +++ b/qapi/qom.json
> >> @@ -1057,6 +1057,19 @@
> >> '*handle': 'uint32',
> >> '*legacy-vm-type': 'OnOffAuto' } }
> >>
> >> +##
> >> +# @SevEmulatedProperties:
> >> +#
> >> +# Properties for sev-emulated objects.
> >> +# This object functionally emulates AMD SEV hardware via TCG, so
> >> +# it does not require real hardware to run.
> >> +#
> >> +# Since: 10.1.0
> >> +##
> >> +{ 'struct': 'SevEmulatedProperties',
> >> + 'base': 'SevGuestProperties',
> >> + 'data': {}}
> >
> > This is deriving 'sev-emulated' from 'sev-guest' which means it
> > supports all the properties that 'sev-guest' does, which for
> > the record is:
>
> Uh, I somehow misread the base as SevCommonProperties! Had I read
> correctly, I would've had similar questions.
Even SevCommonProperties has stuff that's irrelevant for
emulation that I mentioned.
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH 1/5] i386/sev: Add sev-emulated QOM object with TCG support
2026-03-20 12:40 ` Daniel P. Berrangé
@ 2026-03-20 15:23 ` Tommaso Califano
2026-03-23 7:24 ` Markus Armbruster
1 sibling, 0 replies; 21+ messages in thread
From: Tommaso Califano @ 2026-03-20 15:23 UTC (permalink / raw)
To: Daniel P. Berrangé, Markus Armbruster
Cc: qemu-devel, kvm, Eduardo Habkost, Zhao Liu, Marcelo Tosatti,
Eric Blake, Oliver Steffen, Stefano Garzarella, Giuseppe Lettieri,
Paolo Bonzini, Luigi Leonardi, Richard Henderson
Il 20/03/26 13:40, Daniel P. Berrangé ha scritto:
> On Fri, Mar 20, 2026 at 08:44:40AM +0100, Markus Armbruster wrote:
>> Daniel P. Berrangé <berrange@redhat.com> writes:
>>
>>> On Tue, Mar 17, 2026 at 12:38:36PM +0100, Tommaso Califano wrote:
>>>> With this change it is possible to run a VM with the SEV CPUID active
>>>> adding:
>>>>
>>>> -accel tcg \
>>>> -object sev-emulated,id=sev0,cbitpos=47,reduced-phys-bits=1 \
>>>> -machine memory-encryption=sev0
>>>
>>> snip
>>>
>>>> diff --git a/qapi/qom.json b/qapi/qom.json
>>>> index c653248f85..35cda819ec 100644
>>>> --- a/qapi/qom.json
>>>> +++ b/qapi/qom.json
>>>> @@ -1057,6 +1057,19 @@
>>>> '*handle': 'uint32',
>>>> '*legacy-vm-type': 'OnOffAuto' } }
>>>>
>>>> +##
>>>> +# @SevEmulatedProperties:
>>>> +#
>>>> +# Properties for sev-emulated objects.
>>>> +# This object functionally emulates AMD SEV hardware via TCG, so
>>>> +# it does not require real hardware to run.
>>>> +#
>>>> +# Since: 10.1.0
>>>> +##
>>>> +{ 'struct': 'SevEmulatedProperties',
>>>> + 'base': 'SevGuestProperties',
>>>> + 'data': {}}
>>>
>>> This is deriving 'sev-emulated' from 'sev-guest' which means it
>>> supports all the properties that 'sev-guest' does, which for
>>> the record is:
>>
>> Uh, I somehow misread the base as SevCommonProperties! Had I read
>> correctly, I would've had similar questions.
>
> Even SevCommonProperties has stuff that's irrelevant for
> emulation that I mentioned.
>
Maybe I'm missing something, but the only sev-common property not being
used is `sev-device`, since "/dev/sev" doesn't exist in the emulated context.
That said, all other properties are functional:
- cbitpos=<int>
- reduced-phys-bits=<int>
- kernel-hashes=<bool>
Additionally, deriving from sev-common ensures the TYPE_SEV_COMMON type
for the sev_enabled() cast.
So I think deriving from sev-common is beneficial.
Best regards,
Tommaso Califano
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH 1/5] i386/sev: Add sev-emulated QOM object with TCG support
2026-03-20 12:40 ` Daniel P. Berrangé
2026-03-20 15:23 ` Tommaso Califano
@ 2026-03-23 7:24 ` Markus Armbruster
1 sibling, 0 replies; 21+ messages in thread
From: Markus Armbruster @ 2026-03-23 7:24 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: Tommaso Califano, qemu-devel, kvm, Eduardo Habkost, Zhao Liu,
Marcelo Tosatti, Eric Blake, Oliver Steffen, Stefano Garzarella,
Giuseppe Lettieri, Paolo Bonzini, Luigi Leonardi,
Richard Henderson
Daniel P. Berrangé <berrange@redhat.com> writes:
> On Fri, Mar 20, 2026 at 08:44:40AM +0100, Markus Armbruster wrote:
>> Daniel P. Berrangé <berrange@redhat.com> writes:
>>
>> > On Tue, Mar 17, 2026 at 12:38:36PM +0100, Tommaso Califano wrote:
>> >> With this change it is possible to run a VM with the SEV CPUID active
>> >> adding:
>> >>
>> >> -accel tcg \
>> >> -object sev-emulated,id=sev0,cbitpos=47,reduced-phys-bits=1 \
>> >> -machine memory-encryption=sev0
>> >
>> > snip
>> >
>> >> diff --git a/qapi/qom.json b/qapi/qom.json
>> >> index c653248f85..35cda819ec 100644
>> >> --- a/qapi/qom.json
>> >> +++ b/qapi/qom.json
>> >> @@ -1057,6 +1057,19 @@
>> >> '*handle': 'uint32',
>> >> '*legacy-vm-type': 'OnOffAuto' } }
>> >>
>> >> +##
>> >> +# @SevEmulatedProperties:
>> >> +#
>> >> +# Properties for sev-emulated objects.
>> >> +# This object functionally emulates AMD SEV hardware via TCG, so
>> >> +# it does not require real hardware to run.
>> >> +#
>> >> +# Since: 10.1.0
>> >> +##
>> >> +{ 'struct': 'SevEmulatedProperties',
>> >> + 'base': 'SevGuestProperties',
>> >> + 'data': {}}
>> >
>> > This is deriving 'sev-emulated' from 'sev-guest' which means it
>> > supports all the properties that 'sev-guest' does, which for
>> > the record is:
>>
>> Uh, I somehow misread the base as SevCommonProperties! Had I read
>> correctly, I would've had similar questions.
>
> Even SevCommonProperties has stuff that's irrelevant for
> emulation that I mentioned.
Yes. I was too hasty. Glad you spotted it!
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 1/5] i386/sev: Add sev-emulated QOM object with TCG support
2026-03-19 17:49 ` Daniel P. Berrangé
2026-03-20 7:44 ` Markus Armbruster
@ 2026-03-20 12:39 ` Daniel P. Berrangé
2026-03-20 15:03 ` Tommaso Califano
1 sibling, 1 reply; 21+ messages in thread
From: Daniel P. Berrangé @ 2026-03-20 12:39 UTC (permalink / raw)
To: Tommaso Califano, qemu-devel, kvm, Eduardo Habkost,
Markus Armbruster, Zhao Liu, Marcelo Tosatti, Eric Blake,
Oliver Steffen, Stefano Garzarella, Giuseppe Lettieri,
Paolo Bonzini, Luigi Leonardi, Richard Henderson
On Thu, Mar 19, 2026 at 05:49:18PM +0000, Daniel P. Berrangé wrote:
> On Tue, Mar 17, 2026 at 12:38:36PM +0100, Tommaso Califano wrote:
> > With this change it is possible to run a VM with the SEV CPUID active
> > adding:
> >
> > -accel tcg \
> > -object sev-emulated,id=sev0,cbitpos=47,reduced-phys-bits=1 \
> > -machine memory-encryption=sev0
>
> snip
>
> > diff --git a/qapi/qom.json b/qapi/qom.json
> > index c653248f85..35cda819ec 100644
> > --- a/qapi/qom.json
> > +++ b/qapi/qom.json
> > @@ -1057,6 +1057,19 @@
> > '*handle': 'uint32',
> > '*legacy-vm-type': 'OnOffAuto' } }
> >
> > +##
> > +# @SevEmulatedProperties:
> > +#
> > +# Properties for sev-emulated objects.
> > +# This object functionally emulates AMD SEV hardware via TCG, so
> > +# it does not require real hardware to run.
> > +#
> > +# Since: 10.1.0
> > +##
> > +{ 'struct': 'SevEmulatedProperties',
> > + 'base': 'SevGuestProperties',
> > + 'data': {}}
>
> This is deriving 'sev-emulated' from 'sev-guest' which means it
> supports all the properties that 'sev-guest' does, which for
> the record is:
>
> sev-guest options:
> dh-cert-file=<string> - guest owners DH certificate (encoded with base64)
> kernel-hashes=<bool> - add kernel hashes to guest firmware for measured Linux boot
> legacy-vm-type=<OnOffAuto> - use legacy VM type to maintain measurement compatibility with older QEMU or kernel versions.
> session-file=<string> - guest owners session parameters (encoded with base64)
> sev-device=<string> - SEV device to use
Sigh, I was mislead by '-object sev-guest,help' omitting
information about anything that is not a class property.
So there is also
- cbitpos=<int>
- reduced-phys-bits=<int>
- handle=<int>
- policy=<int>
>
>
> Of those properties
>
> * dh-cert-file + session-file are traditionally used
> as a means to transfer the TIK+TEK to the SEV firmware,
> with wrapping to protect them from the hypervisor.
>
> These can't be used with sev-emulated, as implemented,
> since they require a key derivation from the PDH, a
> concept which IIUC is not implemented in this series.
>
> Instead, in a later patch 'tik' and 'tek' properties
> are added to 'sev-emulated', and to pass the TIK+TEK
> in clear text.
>
> * sev-device + legacy-vm-type - these are only relevant
> to the KVM integration, so not applicable for emulation
>
> * kernel-hashes - would be relevant if formally emulating
> LAUNCH_UPDATE_DATA for attestation, but IIUC, this is
> not done/used by this series
>
>
> IOW, we're deriving from 'sev-guest' but AFAICT none of
> its properties are relevant to the emulation. The
> dh-cert-file and session-file could potentially be
> relevant if implementing the PDH concept and key
> derivation, but that's not done, instead the tik/tek
> are passed explicitly.
>
> What is the value we get from this sev-guest -> sev-emulated
> inheritance ? My gut feeling is that this perhaps isn't
> the right way to be modelling things unless there's a plan
> for future work that would benefit from them.
>
> Another question related to modelling is whether there is
> an intention to support SEV-SNP at a later date, would that
> imply a sev-snp-emulated object type too ? If so, would it
> inherit from sev-emulated or from sev-snp-guest ?
>
> > +
> > ##
> > # @SevSnpGuestProperties:
> > #
> > @@ -1241,6 +1254,7 @@
> > { 'name': 'secret_keyring',
> > 'if': 'CONFIG_SECRET_KEYRING' },
> > 'sev-guest',
> > + 'sev-emulated',
> > 'sev-snp-guest',
> > 'thread-context',
> > 's390-pv-guest',
> > @@ -1318,6 +1332,7 @@
> > 'secret_keyring': { 'type': 'SecretKeyringProperties',
> > 'if': 'CONFIG_SECRET_KEYRING' },
> > 'sev-guest': 'SevGuestProperties',
> > + 'sev-emulated': 'SevEmulatedProperties',
> > 'sev-snp-guest': 'SevSnpGuestProperties',
> > 'tdx-guest': 'TdxGuestProperties',
> > 'thread-context': 'ThreadContextProperties',
> > diff --git a/target/i386/sev.c b/target/i386/sev.c
> > index 9dde972c11..2502e860e2 100644
> > --- a/target/i386/sev.c
> > +++ b/target/i386/sev.c
> > @@ -51,6 +51,7 @@
> >
> > OBJECT_DECLARE_TYPE(SevCommonState, SevCommonStateClass, SEV_COMMON)
> > OBJECT_DECLARE_TYPE(SevGuestState, SevCommonStateClass, SEV_GUEST)
> > +OBJECT_DECLARE_TYPE(SevEmulatedState, SevCommonStateClass, SEV_EMULATED)
> > OBJECT_DECLARE_TYPE(SevSnpGuestState, SevCommonStateClass, SEV_SNP_GUEST)
> >
> > /* hard code sha256 digest size */
> > @@ -177,6 +178,21 @@ struct SevGuestState {
> > OnOffAuto legacy_vm_type;
> > };
> >
> > +/**
> > + * SevEmulatedState:
> > + *
> > + * The SevEmulatedState object is used for creating and managing a SEV emulated
> > + * guest.
> > + *
> > + * # $QEMU \
> > + * -object sev-emulated,id=sev0 \
> > + * -machine ...,memory-encryption=sev0
> > + */
> > +
> > +typedef struct SevEmulatedState {
> > + SevGuestState parent_obj;
> > +} SevEmulatedState;
> > +
> > struct SevSnpGuestState {
> > SevCommonState parent_obj;
> >
> > @@ -2936,6 +2952,46 @@ sev_guest_instance_init(Object *obj)
> > sev_guest->legacy_vm_type = ON_OFF_AUTO_AUTO;
> > }
> >
> > +static int sev_emulated_init(ConfidentialGuestSupport *cgs, Error **errp)
> > +{
> > + SevCommonState *sev_common = SEV_COMMON(cgs);
> > +
> > + /*
> > + * The cbitpos value will be placed in bit positions 5:0 of the EBX
> > + * register of CPUID 0x8000001F. We need to verify the range as the
> > + * comparison with the host cbitpos is missing.
> > + */
> > + if (sev_common->cbitpos < 32 ||
> > + sev_common->cbitpos > 63) {
> > + error_setg(errp, "%s: cbitpos check failed, requested '%d',"
> > + "the firmware requires >=32",
> > + __func__, sev_common->cbitpos);
> > + return -1;
> > + }
> > +
> > + /*
> > + * The reduced-phys-bits value will be placed in bit positions 11:6 of
> > + * the EBX register of CPUID 0x8000001F, so verify the supplied value
> > + * is in the range of 1 to 63.
> > + */
> > + if (sev_common->reduced_phys_bits < 1 ||
> > + sev_common->reduced_phys_bits > 63) {
> > + error_setg(errp, "%s: reduced_phys_bits check failed,"
> > + " it should be in the range of 1 to 63, requested '%d'",
> > + __func__, sev_common->reduced_phys_bits);
> > + return -1;
> > + }
> > + cgs->ready = true;
> > + return 0;
> > +}
> > +
> > +static void sev_emulated_class_init(ObjectClass *oc, const void *data)
> > +{
> > + ConfidentialGuestSupportClass *klass = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc);
> > + /* Override the sev-common method that uses kvm */
> > + klass->kvm_init = sev_emulated_init;
> > +}
> > +
> > /* guest info specific sev/sev-es */
> > static const TypeInfo sev_guest_info = {
> > .parent = TYPE_SEV_COMMON,
> > @@ -2945,6 +3001,14 @@ static const TypeInfo sev_guest_info = {
> > .class_init = sev_guest_class_init,
> > };
> >
> > +/* emulated sev */
> > +static const TypeInfo sev_emulated_info = {
> > + .parent = TYPE_SEV_GUEST,
> > + .name = TYPE_SEV_EMULATED,
> > + .instance_size = sizeof(SevEmulatedState),
> > + .class_init = sev_emulated_class_init
> > +};
> > +
> > static void
> > sev_snp_guest_get_policy(Object *obj, Visitor *v, const char *name,
> > void *opaque, Error **errp)
> > @@ -3207,6 +3271,7 @@ static void
> > sev_register_types(void)
> > {
> > type_register_static(&sev_common_info);
> > + type_register_static(&sev_emulated_info);
> > type_register_static(&sev_guest_info);
> > type_register_static(&sev_snp_guest_info);
> > }
> > diff --git a/target/i386/sev.h b/target/i386/sev.h
> > index 4358df40e4..839656e2be 100644
> > --- a/target/i386/sev.h
> > +++ b/target/i386/sev.h
> > @@ -33,6 +33,7 @@ bool sev_snp_enabled(void);
> > #if !defined(CONFIG_USER_ONLY)
> >
> > #define TYPE_SEV_COMMON "sev-common"
> > +#define TYPE_SEV_EMULATED "sev-emulated"
> > #define TYPE_SEV_GUEST "sev-guest"
> > #define TYPE_SEV_SNP_GUEST "sev-snp-guest"
> >
> > --
> > 2.53.0
> >
>
> With regards,
> Daniel
> --
> |: https://berrange.com ~~ https://hachyderm.io/@berrange :|
> |: https://libvirt.org ~~ https://entangle-photo.org :|
> |: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
>
>
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH 1/5] i386/sev: Add sev-emulated QOM object with TCG support
2026-03-20 12:39 ` Daniel P. Berrangé
@ 2026-03-20 15:03 ` Tommaso Califano
2026-03-20 15:32 ` Tommaso Califano
0 siblings, 1 reply; 21+ messages in thread
From: Tommaso Califano @ 2026-03-20 15:03 UTC (permalink / raw)
To: Daniel P. Berrangé, qemu-devel, kvm, Eduardo Habkost,
Markus Armbruster, Zhao Liu, Marcelo Tosatti, Eric Blake,
Oliver Steffen, Stefano Garzarella, Giuseppe Lettieri,
Paolo Bonzini, Luigi Leonardi, Richard Henderson
Il 20/03/26 13:39, Daniel P. Berrangé ha scritto:
> On Thu, Mar 19, 2026 at 05:49:18PM +0000, Daniel P. Berrangé wrote:
>> On Tue, Mar 17, 2026 at 12:38:36PM +0100, Tommaso Califano wrote:
>>> With this change it is possible to run a VM with the SEV CPUID active
>>> adding:
>>>
>>> -accel tcg \
>>> -object sev-emulated,id=sev0,cbitpos=47,reduced-phys-bits=1 \
>>> -machine memory-encryption=sev0
>>
>> snip
>>
>>> diff --git a/qapi/qom.json b/qapi/qom.json
>>> index c653248f85..35cda819ec 100644
>>> --- a/qapi/qom.json
>>> +++ b/qapi/qom.json
>>> @@ -1057,6 +1057,19 @@
>>> '*handle': 'uint32',
>>> '*legacy-vm-type': 'OnOffAuto' } }
>>>
>>> +##
>>> +# @SevEmulatedProperties:
>>> +#
>>> +# Properties for sev-emulated objects.
>>> +# This object functionally emulates AMD SEV hardware via TCG, so
>>> +# it does not require real hardware to run.
>>> +#
>>> +# Since: 10.1.0
>>> +##
>>> +{ 'struct': 'SevEmulatedProperties',
>>> + 'base': 'SevGuestProperties',
>>> + 'data': {}}
>>
>> This is deriving 'sev-emulated' from 'sev-guest' which means it
>> supports all the properties that 'sev-guest' does, which for
>> the record is:
>>
>> sev-guest options:
>> dh-cert-file=<string> - guest owners DH certificate (encoded with base64)
>> kernel-hashes=<bool> - add kernel hashes to guest firmware for measured Linux boot
>> legacy-vm-type=<OnOffAuto> - use legacy VM type to maintain measurement compatibility with older QEMU or kernel versions.
>> session-file=<string> - guest owners session parameters (encoded with base64)
>> sev-device=<string> - SEV device to use
>
> Sigh, I was mislead by '-object sev-guest,help' omitting
> information about anything that is not a class property.
> So there is also
>
> - cbitpos=<int>
> - reduced-phys-bits=<int>
> - handle=<int>
> - policy=<int>
>
>>
>>
>> Of those properties
>>
>> * dh-cert-file + session-file are traditionally used
>> as a means to transfer the TIK+TEK to the SEV firmware,
>> with wrapping to protect them from the hypervisor.
>>
>> These can't be used with sev-emulated, as implemented,
>> since they require a key derivation from the PDH, a
>> concept which IIUC is not implemented in this series.
>>
>> Instead, in a later patch 'tik' and 'tek' properties
>> are added to 'sev-emulated', and to pass the TIK+TEK
>> in clear text.
>>
>> * sev-device + legacy-vm-type - these are only relevant
>> to the KVM integration, so not applicable for emulation
>>
>> * kernel-hashes - would be relevant if formally emulating
>> LAUNCH_UPDATE_DATA for attestation, but IIUC, this is
>> not done/used by this series
>>
>>
>> IOW, we're deriving from 'sev-guest' but AFAICT none of
>> its properties are relevant to the emulation. The
>> dh-cert-file and session-file could potentially be
>> relevant if implementing the PDH concept and key
>> derivation, but that's not done, instead the tik/tek
>> are passed explicitly.
>>
>> What is the value we get from this sev-guest -> sev-emulated
>> inheritance ? My gut feeling is that this perhaps isn't
>> the right way to be modelling things unless there's a plan
>> for future work that would benefit from them.
>>
I know most of these properties aren't used in emulation. I chose
to derive from `sev-guest' primarily for the policy property
(needed for SEV-ES future implementation).
For the TIK and TEK properties, I considered reusing dh-cert-file
and session-file, but since those keys are CPU-generated anyway,
I opted to simplify the cryptographic protocol given the fact
that security isn't the focus here.
That said, you're right that sev-guest inheritance may not add
much value. If you'd prefer interface consistency for testing
(reusing dh-cert-file/session-file) those properties could then
become relevant. Otherwise, for pure SEV emulation, deriving from
sev-common makes sense instead; though I'm unsure how that would
impact the SEV-ES implementation, perhaps we could re-add just the
policy property if needed.
>> Another question related to modelling is whether there is
>> an intention to support SEV-SNP at a later date, would that
>> imply a sev-snp-emulated object type too ? If so, would it
>> inherit from sev-emulated or from sev-snp-guest ?
>>
While I haven't studied it in depth, I think it will. It seems
best to derive a new object (sev-snp-emulated) from sev-guest-snp
because of the sev_snp_enabled() function, which will work through
TYPE_SEV_SNP_GUEST casting (similar to the current sev_enabled()
function).
Best regards,
Tommaso Califano
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH 1/5] i386/sev: Add sev-emulated QOM object with TCG support
2026-03-20 15:03 ` Tommaso Califano
@ 2026-03-20 15:32 ` Tommaso Califano
0 siblings, 0 replies; 21+ messages in thread
From: Tommaso Califano @ 2026-03-20 15:32 UTC (permalink / raw)
To: Daniel P. Berrangé, qemu-devel, kvm, Eduardo Habkost,
Markus Armbruster, Zhao Liu, Marcelo Tosatti, Eric Blake,
Oliver Steffen, Stefano Garzarella, Giuseppe Lettieri,
Paolo Bonzini, Luigi Leonardi, Richard Henderson
Il 20/03/26 16:03, Tommaso Califano ha scritto:
>
>
> Il 20/03/26 13:39, Daniel P. Berrangé ha scritto:
>> On Thu, Mar 19, 2026 at 05:49:18PM +0000, Daniel P. Berrangé wrote:
>>> On Tue, Mar 17, 2026 at 12:38:36PM +0100, Tommaso Califano wrote:
>>>> With this change it is possible to run a VM with the SEV CPUID active
>>>> adding:
>>>>
>>>> -accel tcg \
>>>> -object sev-emulated,id=sev0,cbitpos=47,reduced-phys-bits=1 \
>>>> -machine memory-encryption=sev0
>>>
>>> snip
>>>
>>>> diff --git a/qapi/qom.json b/qapi/qom.json
>>>> index c653248f85..35cda819ec 100644
>>>> --- a/qapi/qom.json
>>>> +++ b/qapi/qom.json
>>>> @@ -1057,6 +1057,19 @@
>>>> '*handle': 'uint32',
>>>> '*legacy-vm-type': 'OnOffAuto' } }
>>>>
>>>> +##
>>>> +# @SevEmulatedProperties:
>>>> +#
>>>> +# Properties for sev-emulated objects.
>>>> +# This object functionally emulates AMD SEV hardware via TCG, so
>>>> +# it does not require real hardware to run.
>>>> +#
>>>> +# Since: 10.1.0
>>>> +##
>>>> +{ 'struct': 'SevEmulatedProperties',
>>>> + 'base': 'SevGuestProperties',
>>>> + 'data': {}}
>>>
>>> This is deriving 'sev-emulated' from 'sev-guest' which means it
>>> supports all the properties that 'sev-guest' does, which for
>>> the record is:
>>>
>>> sev-guest options:
>>> dh-cert-file=<string> - guest owners DH certificate (encoded with base64)
>>> kernel-hashes=<bool> - add kernel hashes to guest firmware for measured Linux boot
>>> legacy-vm-type=<OnOffAuto> - use legacy VM type to maintain measurement compatibility with older QEMU or kernel versions.
>>> session-file=<string> - guest owners session parameters (encoded with base64)
>>> sev-device=<string> - SEV device to use
>>
>> Sigh, I was mislead by '-object sev-guest,help' omitting
>> information about anything that is not a class property.
>> So there is also
>>
>> - cbitpos=<int>
>> - reduced-phys-bits=<int>
>> - handle=<int>
>> - policy=<int>
>>
>>>
>>>
>>> Of those properties
>>>
>>> * dh-cert-file + session-file are traditionally used
>>> as a means to transfer the TIK+TEK to the SEV firmware,
>>> with wrapping to protect them from the hypervisor.
>>>
>>> These can't be used with sev-emulated, as implemented,
>>> since they require a key derivation from the PDH, a
>>> concept which IIUC is not implemented in this series.
>>>
>>> Instead, in a later patch 'tik' and 'tek' properties
>>> are added to 'sev-emulated', and to pass the TIK+TEK
>>> in clear text.
>>>
>>> * sev-device + legacy-vm-type - these are only relevant
>>> to the KVM integration, so not applicable for emulation
>>>
>>> * kernel-hashes - would be relevant if formally emulating
>>> LAUNCH_UPDATE_DATA for attestation, but IIUC, this is
>>> not done/used by this series
LAUNCH_UPDATE_DATA is supported in the sense that the called
memory regions are saved for measurement calculation. With this
method, even if kernel-hashes is active, the measurement remains
correct and the attestation workflow stays the same.
Thinking of it now, I could add a small improvement by checking
if the memory region is already in the QEMUIOVector ld_data,
avoiding multiple addition of the same region.
>>>
>>>
>>> IOW, we're deriving from 'sev-guest' but AFAICT none of
>>> its properties are relevant to the emulation. The
>>> dh-cert-file and session-file could potentially be
>>> relevant if implementing the PDH concept and key
>>> derivation, but that's not done, instead the tik/tek
>>> are passed explicitly.
>>>
>>> What is the value we get from this sev-guest -> sev-emulated
>>> inheritance ? My gut feeling is that this perhaps isn't
>>> the right way to be modelling things unless there's a plan
>>> for future work that would benefit from them.
>>>
>
> I know most of these properties aren't used in emulation. I chose
> to derive from `sev-guest' primarily for the policy property
> (needed for SEV-ES future implementation).
>
> For the TIK and TEK properties, I considered reusing dh-cert-file
> and session-file, but since those keys are CPU-generated anyway,
> I opted to simplify the cryptographic protocol given the fact
> that security isn't the focus here.
>
> That said, you're right that sev-guest inheritance may not add
> much value. If you'd prefer interface consistency for testing
> (reusing dh-cert-file/session-file) those properties could then
> become relevant. Otherwise, for pure SEV emulation, deriving from
> sev-common makes sense instead; though I'm unsure how that would
> impact the SEV-ES implementation, perhaps we could re-add just the
> policy property if needed.
>
>>> Another question related to modelling is whether there is
>>> an intention to support SEV-SNP at a later date, would that
>>> imply a sev-snp-emulated object type too ? If so, would it
>>> inherit from sev-emulated or from sev-snp-guest ?
>>>
>
> While I haven't studied it in depth, I think it will. It seems
> best to derive a new object (sev-snp-emulated) from sev-guest-snp
> because of the sev_snp_enabled() function, which will work through
> TYPE_SEV_SNP_GUEST casting (similar to the current sev_enabled()
> function).
>
Apologies for the multiple emails; I forgot to explain the kernel-hashes
implementation.
Best regards,
Tommaso Califano
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 2/5] target/i386: Add MSR SEV support and C-bit reset on TCG
2026-03-17 11:38 [PATCH 0/5] i386/sev: Add TCG-emulated AMD SEV guest support Tommaso Califano
2026-03-17 11:38 ` [PATCH 1/5] i386/sev: Add sev-emulated QOM object with TCG support Tommaso Califano
@ 2026-03-17 11:38 ` Tommaso Califano
2026-03-17 11:38 ` [PATCH 3/5] i386/sev: Implement SEV launch state sequence and query-sev Tommaso Califano
` (3 subsequent siblings)
5 siblings, 0 replies; 21+ messages in thread
From: Tommaso Califano @ 2026-03-17 11:38 UTC (permalink / raw)
To: qemu-devel
Cc: kvm, Eduardo Habkost, Markus Armbruster, Zhao Liu,
Daniel P. Berrangé, Marcelo Tosatti, Eric Blake,
Oliver Steffen, Stefano Garzarella, Giuseppe Lettieri,
Paolo Bonzini, Luigi Leonardi, Richard Henderson,
Tommaso Califano
Exposing SEV CPUID leaf informs the guest of SEV support,
but activation of the features requires setting bit 0 in
the SEV MSR (0xc0010131).
sev_emulated_enable() is implemented to enable the MSR only when
emulation is active. This is invoked from helper_rdmsr() to control
MSR write behavior.
SEV MSR activation prompts OVMF to use C-bits in PTEs, altering paging.
To address this, C-bits are reset in all PTEs and CR3 via
sev_emulated_convert_pte().
This change enables the features of the guest adding to QEMU arguments:
-cpu "EPYC-Milan" \
-accel tcg \
-object sev-emulated,id=sev0,cbitpos=47,reduced-phys-bits=1 \
-machine memory-encryption=sev0
A SEV capable cpu profile must be selected to enable the emulation.
Signed-off-by: Tommaso Califano <califano.tommaso@gmail.com>
---
target/i386/cpu.h | 2 ++
target/i386/sev.c | 18 ++++++++++++++++
target/i386/sev.h | 3 +++
target/i386/tcg/system/excp_helper.c | 31 ++++++++++++++++++++++++++++
target/i386/tcg/system/misc_helper.c | 13 ++++++++++++
5 files changed, 67 insertions(+)
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 0b539155c4..dc2f82837f 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -659,6 +659,8 @@ typedef enum X86Seg {
#define ESA_FEATURE_ALIGN64_MASK (1U << ESA_FEATURE_ALIGN64_BIT)
#define ESA_FEATURE_XFD_MASK (1U << ESA_FEATURE_XFD_BIT)
+/* AMD SEV MSR */
+#define MSR_AMD64_SEV 0xc0010131
/* CPUID feature bits available in XCR0 */
#define CPUID_XSTATE_XCR0_MASK (XSTATE_FP_MASK | XSTATE_SSE_MASK | \
diff --git a/target/i386/sev.c b/target/i386/sev.c
index 2502e860e2..cdadd83ab5 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -690,6 +690,14 @@ static int sev_set_cpu_context(uint16_t cpu_index, const void *ctx,
return 0;
}
+bool
+sev_emulated_enabled(void)
+{
+ ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs;
+
+ return !!object_dynamic_cast(OBJECT(cgs), TYPE_SEV_EMULATED);
+}
+
bool
sev_enabled(void)
{
@@ -731,6 +739,16 @@ sev_get_reduced_phys_bits(void)
return sev_common ? sev_common->reduced_phys_bits : 0;
}
+uint64_t
+sev_emulated_convert_pte(uint64_t pte)
+{
+ if (unlikely(sev_emulated_enabled())) {
+ pte &= ~(1ULL << sev_get_cbit_position());
+ }
+
+ return pte;
+}
+
static SevInfo *sev_get_info(void)
{
SevInfo *info;
diff --git a/target/i386/sev.h b/target/i386/sev.h
index 839656e2be..d69275d40f 100644
--- a/target/i386/sev.h
+++ b/target/i386/sev.h
@@ -24,10 +24,12 @@
#define sev_enabled() 0
#define sev_es_enabled() 0
#define sev_snp_enabled() 0
+#define sev_emulated_enabled() 0
#else
bool sev_enabled(void);
bool sev_es_enabled(void);
bool sev_snp_enabled(void);
+bool sev_emulated_enabled(void);
#endif
#if !defined(CONFIG_USER_ONLY)
@@ -197,5 +199,6 @@ void pc_system_parse_sev_metadata(uint8_t *flash_ptr, size_t flash_size);
uint32_t sev_get_cbit_position(void);
uint32_t sev_get_reduced_phys_bits(void);
+uint64_t sev_emulated_convert_pte(uint64_t);
#endif
diff --git a/target/i386/tcg/system/excp_helper.c b/target/i386/tcg/system/excp_helper.c
index d7ea77c855..307a4a7e6b 100644
--- a/target/i386/tcg/system/excp_helper.c
+++ b/target/i386/tcg/system/excp_helper.c
@@ -26,6 +26,7 @@
#include "exec/target_page.h"
#include "exec/tlb-flags.h"
#include "tcg/helper-tcg.h"
+#include "sev.h"
typedef struct TranslateParams {
target_ulong addr;
@@ -160,7 +161,13 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in,
int prot;
restart_all:
+#ifdef CONFIG_SEV
+ rsvd_mask = ~MAKE_64BIT_MASK(0,
+ env_archcpu(env)->phys_bits - sev_get_reduced_phys_bits());
+ rsvd_mask = sev_emulated_convert_pte(rsvd_mask);
+#else
rsvd_mask = ~MAKE_64BIT_MASK(0, env_archcpu(env)->phys_bits);
+#endif
rsvd_mask &= PG_ADDRESS_MASK;
if (!(pg_mode & PG_MODE_NXE)) {
rsvd_mask |= PG_NX_MASK;
@@ -179,6 +186,9 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in,
}
restart_5:
pte = ptw_ldq(&pte_trans, ra);
+ #ifdef CONFIG_SEV
+ pte = sev_emulated_convert_pte(pte);
+ #endif
if (!(pte & PG_PRESENT_MASK)) {
goto do_fault;
}
@@ -203,6 +213,9 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in,
}
restart_4:
pte = ptw_ldq(&pte_trans, ra);
+ #ifdef CONFIG_SEV
+ pte = sev_emulated_convert_pte(pte);
+ #endif
if (!(pte & PG_PRESENT_MASK)) {
goto do_fault;
}
@@ -223,6 +236,9 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in,
}
restart_3_lma:
pte = ptw_ldq(&pte_trans, ra);
+ #ifdef CONFIG_SEV
+ pte = sev_emulated_convert_pte(pte);
+ #endif
if (!(pte & PG_PRESENT_MASK)) {
goto do_fault;
}
@@ -251,6 +267,9 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in,
rsvd_mask |= PG_HI_USER_MASK;
restart_3_nolma:
pte = ptw_ldq(&pte_trans, ra);
+ #ifdef CONFIG_SEV
+ pte = sev_emulated_convert_pte(pte);
+ #endif
if (!(pte & PG_PRESENT_MASK)) {
goto do_fault;
}
@@ -272,6 +291,9 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in,
}
restart_2_pae:
pte = ptw_ldq(&pte_trans, ra);
+ #ifdef CONFIG_SEV
+ pte = sev_emulated_convert_pte(pte);
+ #endif
if (!(pte & PG_PRESENT_MASK)) {
goto do_fault;
}
@@ -297,6 +319,9 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in,
return false;
}
pte = ptw_ldq(&pte_trans, ra);
+ #ifdef CONFIG_SEV
+ pte = sev_emulated_convert_pte(pte);
+ #endif
if (!(pte & PG_PRESENT_MASK)) {
goto do_fault;
}
@@ -316,6 +341,9 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in,
}
restart_2_nopae:
pte = ptw_ldl(&pte_trans, ra);
+ #ifdef CONFIG_SEV
+ pte = sev_emulated_convert_pte(pte);
+ #endif
if (!(pte & PG_PRESENT_MASK)) {
goto do_fault;
}
@@ -344,6 +372,9 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in,
return false;
}
pte = ptw_ldl(&pte_trans, ra);
+ #ifdef CONFIG_SEV
+ pte = sev_emulated_convert_pte(pte);
+ #endif
if (!(pte & PG_PRESENT_MASK)) {
goto do_fault;
}
diff --git a/target/i386/tcg/system/misc_helper.c b/target/i386/tcg/system/misc_helper.c
index bb79d4e470..aa6ed5cda1 100644
--- a/target/i386/tcg/system/misc_helper.c
+++ b/target/i386/tcg/system/misc_helper.c
@@ -27,6 +27,7 @@
#include "exec/cputlb.h"
#include "tcg/helper-tcg.h"
#include "hw/i386/apic.h"
+#include "target/i386/sev.h"
void helper_outb(CPUX86State *env, uint32_t port, uint32_t data)
{
@@ -89,6 +90,10 @@ void helper_write_crN(CPUX86State *env, int reg, target_ulong t0)
cpu_x86_update_cr0(env, t0);
break;
case 3:
+ #ifdef CONFIG_SEV
+ /* If SEV emulation is active, reset the C-bit */
+ t0 = sev_emulated_convert_pte(t0);
+ #endif
if ((env->efer & MSR_EFER_LMA) &&
(t0 & ((~0ULL) << env_archcpu(env)->phys_bits))) {
cpu_vmexit(env, SVM_EXIT_ERR, 0, GETPC());
@@ -468,6 +473,14 @@ void helper_rdmsr(CPUX86State *env)
case MSR_IA32_UCODE_REV:
val = x86_cpu->ucode_rev;
break;
+ case MSR_AMD64_SEV:
+ if (sev_emulated_enabled()) {
+ val = 1;
+ } else {
+ /* XXX: exception? */
+ val = 0;
+ }
+ break;
case MSR_CORE_THREAD_COUNT: {
val = cpu_x86_get_msr_core_thread_count(x86_cpu);
break;
--
2.53.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH 3/5] i386/sev: Implement SEV launch state sequence and query-sev
2026-03-17 11:38 [PATCH 0/5] i386/sev: Add TCG-emulated AMD SEV guest support Tommaso Califano
2026-03-17 11:38 ` [PATCH 1/5] i386/sev: Add sev-emulated QOM object with TCG support Tommaso Califano
2026-03-17 11:38 ` [PATCH 2/5] target/i386: Add MSR SEV support and C-bit reset on TCG Tommaso Califano
@ 2026-03-17 11:38 ` Tommaso Califano
2026-03-17 11:38 ` [PATCH 4/5] i386/sev: Add launch measurement emulation and TIK property Tommaso Califano
` (2 subsequent siblings)
5 siblings, 0 replies; 21+ messages in thread
From: Tommaso Califano @ 2026-03-17 11:38 UTC (permalink / raw)
To: qemu-devel
Cc: kvm, Eduardo Habkost, Markus Armbruster, Zhao Liu,
Daniel P. Berrangé, Marcelo Tosatti, Eric Blake,
Oliver Steffen, Stefano Garzarella, Giuseppe Lettieri,
Paolo Bonzini, Luigi Leonardi, Richard Henderson,
Tommaso Califano
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 <califano.tommaso@gmail.com>
---
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
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH 4/5] i386/sev: Add launch measurement emulation and TIK property
2026-03-17 11:38 [PATCH 0/5] i386/sev: Add TCG-emulated AMD SEV guest support Tommaso Califano
` (2 preceding siblings ...)
2026-03-17 11:38 ` [PATCH 3/5] i386/sev: Implement SEV launch state sequence and query-sev Tommaso Califano
@ 2026-03-17 11:38 ` Tommaso Califano
2026-03-19 12:33 ` Markus Armbruster
2026-03-17 11:38 ` [PATCH 5/5] i386/sev: Implement emulated launch secret injection and TEK property Tommaso Califano
2026-03-17 13:01 ` [PATCH 0/5] i386/sev: Add TCG-emulated AMD SEV guest support Luigi Leonardi
5 siblings, 1 reply; 21+ messages in thread
From: Tommaso Califano @ 2026-03-17 11:38 UTC (permalink / raw)
To: qemu-devel
Cc: kvm, Eduardo Habkost, Markus Armbruster, Zhao Liu,
Daniel P. Berrangé, Marcelo Tosatti, Eric Blake,
Oliver Steffen, Stefano Garzarella, Giuseppe Lettieri,
Paolo Bonzini, Luigi Leonardi, Richard Henderson,
Tommaso Califano
The next step for completing the SEV launch emulation is to implement the
"query-sev-launch-measure" feature, responsible for returning the
measurement. In this case the measurement will be computed in QEMU.
Implement sev_emulated_launch_get_measure() to emulate the LAUNCH_MEASURE
command per AMD SEV API spec section 6.5.1. It generates a random 16-byte
mnonce, computes the launch digest as SHA-256 over ld_data, then derives
the measurement via HMAC-SHA256
(TIK;0x04|| API version || build ID || policy || launch digest || mnonce).
The base64-encoded result (32-byte HMAC + 16-byte mnonce) populates
"query-sev-launch-measure" data, advancing state to LAUNCH_SECRET for
secret injection.
The TIK is supplied via 16-byte binary file specified in new
SevEmulatedProperty "tik" path; absent this, keys default to zeroed.
Example QEMU arguments with the key passed:
-cpu "EPYC-Milan" \
-accel tcg \
-object sev-emulated,id=sev0,cbitpos=47,reduced-phys-bits=1,\
tik=/path/to/tik.bin \
-machine memory-encryption=sev0
Signed-off-by: Tommaso Califano <califano.tommaso@gmail.com>
---
qapi/qom.json | 3 +-
target/i386/sev.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 157 insertions(+), 1 deletion(-)
diff --git a/qapi/qom.json b/qapi/qom.json
index 35cda819ec..affb5024b5 100644
--- a/qapi/qom.json
+++ b/qapi/qom.json
@@ -1064,11 +1064,12 @@
# This object functionally emulates AMD SEV hardware via TCG, so
# it does not require real hardware to run.
#
+# @tik: binary file of the SEV TIK (default: all 0).
# Since: 10.1.0
##
{ 'struct': 'SevEmulatedProperties',
'base': 'SevGuestProperties',
- 'data': {}}
+ 'data': {'*tik': 'str'}}
##
# @SevSnpGuestProperties:
diff --git a/target/i386/sev.c b/target/i386/sev.c
index 5904f2c983..5b1c001633 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -48,6 +48,8 @@
#include "hw/i386/e820_memory_layout.h"
#include "qemu/queue.h"
#include "qemu/cutils.h"
+#include "crypto/hmac.h"
+#include "crypto/random.h"
OBJECT_DECLARE_TYPE(SevCommonState, SevCommonStateClass, SEV_COMMON)
OBJECT_DECLARE_TYPE(SevGuestState, SevCommonStateClass, SEV_GUEST)
@@ -72,6 +74,9 @@ OBJECT_DECLARE_TYPE(SevSnpGuestState, SevCommonStateClass, SEV_SNP_GUEST)
#define FAKE_API_MAJOR 1
#define FAKE_API_MINOR 40
+/* SEV TEK, TIK and IV size in byte for emulation */
+#define TEK_TIK_IV_SIZE 16
+
typedef struct QEMU_PACKED SevHashTableEntry {
QemuUUID guid;
uint16_t len;
@@ -197,6 +202,7 @@ struct SevGuestState {
typedef struct SevEmulatedState {
SevGuestState parent_obj;
+ uint8_t *tik;
QEMUIOVector ld_data;
} SevEmulatedState;
@@ -1431,6 +1437,89 @@ sev_launch_get_measure(Notifier *notifier, void *unused)
trace_kvm_sev_launch_measurement(sev_guest->measurement);
}
+static void
+sev_emulated_launch_get_measure(Notifier *notifier, void *unused)
+{
+ SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs);
+ SevGuestState *sev_guest = SEV_GUEST(sev_common);
+ SevEmulatedState *sev_emulated = SEV_EMULATED(sev_guest);
+
+ uint8_t prefix = 0x04;
+ uint8_t concat_data[56];
+ uint8_t mnonce[16];
+ uint8_t *hmac_raw;
+ uint8_t *ld;
+ gsize hmac_len, ld_len;
+ GByteArray *measure_raw = g_byte_array_sized_new(48);
+ QCryptoHmac *hmac = NULL;
+ Error *err = NULL;
+
+ if (!sev_check_state(sev_common, SEV_STATE_LAUNCH_UPDATE)) {
+ return;
+ }
+
+ /* Generate the mnonce (16B) */
+ if (qcrypto_random_bytes(mnonce, sizeof(mnonce), &err) < 0) {
+ error_report_err(err);
+ return;
+ }
+
+ /* Compute the Launch Digest (ld) */
+ if (qcrypto_hash_bytesv(QCRYPTO_HASH_ALGO_SHA256, sev_emulated->ld_data.iov,
+ sev_emulated->ld_data.niov, &ld, &ld_len, &err) < 0){
+ error_report_err(err);
+ return;
+ }
+ assert(ld_len == HASH_SIZE);
+
+ /*
+ * The HMAC is calculated as specified in SEV API spec in section 6.5.1:
+ * HMAC(0x04 || API_MAJOR || API_MINOR || BUILD || GCTX.POLICY
+ * || GCTX.LD || MNONCE; GCTX.TIK)
+ */
+ concat_data[0] = prefix;
+ concat_data[1] = sev_common->api_major;
+ concat_data[2] = sev_common->api_minor;
+ concat_data[3] = sev_common->build_id;
+ memcpy(&concat_data[4], &sev_guest->policy, 4);
+ memcpy(&concat_data[8], ld, ld_len);
+ memcpy(&concat_data[40], mnonce, 16);
+
+ g_free(ld);
+
+ /* Initialize HMAC with TIK */
+ hmac = qcrypto_hmac_new(QCRYPTO_HASH_ALGO_SHA256,
+ (uint8_t *)sev_emulated->tik,
+ TEK_TIK_IV_SIZE,
+ &err);
+ if (!hmac) {
+ error_report_err(err);
+ return;
+ }
+
+ /* Compute the HMAC */
+ if (qcrypto_hmac_bytes(hmac, (char *)concat_data, sizeof(concat_data),
+ &hmac_raw, &hmac_len, &err) < 0) {
+ error_report_err(err);
+ qcrypto_hmac_free(hmac);
+ return;
+ }
+ qcrypto_hmac_free(hmac);
+ assert(hmac_len == HASH_SIZE);
+
+ /* Construct the measurement: HMAC(32B) + mnonce(16B) */
+ g_byte_array_append(measure_raw, hmac_raw, 32);
+ g_byte_array_append(measure_raw, mnonce, 16);
+
+ g_free(hmac_raw);
+
+ sev_guest->measurement =
+ g_base64_encode(measure_raw->data, measure_raw->len);
+ g_byte_array_free(measure_raw, TRUE);
+
+ sev_set_guest_state(sev_common, SEV_STATE_LAUNCH_SECRET);
+}
+
static char *sev_get_launch_measurement(void)
{
ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs;
@@ -1466,6 +1555,10 @@ static Notifier sev_machine_done_notify = {
.notify = sev_launch_get_measure,
};
+static Notifier sev_emu_machine_done_notify = {
+ .notify = sev_emulated_launch_get_measure,
+};
+
static void
sev_launch_finish(SevCommonState *sev_common)
{
@@ -3054,6 +3147,9 @@ static int sev_emulated_init(ConfidentialGuestSupport *cgs, Error **errp)
qemu_add_vm_change_state_handler(sev_vm_state_change, sev_common);
cgs->ready = true;
+
+ qemu_add_machine_init_done_notifier(&sev_emu_machine_done_notify);
+
return 0;
}
@@ -3076,6 +3172,48 @@ sev_emulated_launch_finish(SevCommonState *sev_common)
sev_set_guest_state(sev_common, SEV_STATE_RUNNING);
}
+static void sev_emulated_read_key(uint8_t *key,
+ const char *key_filename, Error **errp)
+{
+ gsize len;
+ FILE *fp = fopen(key_filename, "rb");
+
+ if (!fp) {
+ error_setg(errp, "SEV-EMULATED: Failed to open %s", key_filename);
+ return;
+ }
+
+ len = fread(key, 1, TEK_TIK_IV_SIZE, fp);
+
+ fclose(fp);
+
+ if (len != TEK_TIK_IV_SIZE) {
+ error_setg(errp, "parameter length: key size %" G_GSIZE_FORMAT
+ " is not equal to %u",
+ len, TEK_TIK_IV_SIZE);
+ return;
+ }
+}
+
+static char *sev_emulated_get_tik(Object *obj, Error **errp)
+{
+ SevEmulatedState *sev_emulated = SEV_EMULATED(obj);
+
+ return g_memdup2(sev_emulated->tik, TEK_TIK_IV_SIZE);
+}
+
+static void sev_emulated_set_tik(
+ Object *obj, const char *key_filename, Error **errp)
+{
+ SevEmulatedState *sev_emulated = SEV_EMULATED(obj);
+
+ sev_emulated_read_key(
+ sev_emulated->tik,
+ key_filename,
+ errp
+ );
+}
+
static void sev_emulated_class_init(ObjectClass *oc, const void *data)
{
SevCommonStateClass *scc = SEV_COMMON_CLASS(oc);
@@ -3084,6 +3222,22 @@ static void sev_emulated_class_init(ObjectClass *oc, const void *data)
klass->kvm_init = sev_emulated_init;
scc->launch_update_data = sev_emulated_launch_update_data;
scc->launch_finish = sev_emulated_launch_finish;
+
+ /* Adding emulation specific property */
+ object_class_property_add_str(oc, "tik",
+ sev_emulated_get_tik,
+ sev_emulated_set_tik);
+ object_class_property_set_description(oc, "tik",
+ "Path to the binary file containing the"
+ "SEV Transport Integrity Key (16 bytes)");
+}
+
+static void sev_emulated_instance_init(Object *obj)
+{
+ SevEmulatedState *sev_emulated = SEV_EMULATED(obj);
+
+ /* Initialize the key for emulation */
+ sev_emulated->tik = g_malloc0(TEK_TIK_IV_SIZE);
}
/* guest info specific sev/sev-es */
@@ -3100,6 +3254,7 @@ static const TypeInfo sev_emulated_info = {
.parent = TYPE_SEV_GUEST,
.name = TYPE_SEV_EMULATED,
.instance_size = sizeof(SevEmulatedState),
+ .instance_init = sev_emulated_instance_init,
.class_init = sev_emulated_class_init
};
--
2.53.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH 4/5] i386/sev: Add launch measurement emulation and TIK property
2026-03-17 11:38 ` [PATCH 4/5] i386/sev: Add launch measurement emulation and TIK property Tommaso Califano
@ 2026-03-19 12:33 ` Markus Armbruster
2026-03-20 14:31 ` Tommaso Califano
0 siblings, 1 reply; 21+ messages in thread
From: Markus Armbruster @ 2026-03-19 12:33 UTC (permalink / raw)
To: Tommaso Califano
Cc: qemu-devel, kvm, Eduardo Habkost, Markus Armbruster, Zhao Liu,
Daniel P. Berrangé, Marcelo Tosatti, Eric Blake,
Oliver Steffen, Stefano Garzarella, Giuseppe Lettieri,
Paolo Bonzini, Luigi Leonardi, Richard Henderson
Tommaso Califano <califano.tommaso@gmail.com> writes:
> The next step for completing the SEV launch emulation is to implement the
> "query-sev-launch-measure" feature, responsible for returning the
> measurement. In this case the measurement will be computed in QEMU.
>
> Implement sev_emulated_launch_get_measure() to emulate the LAUNCH_MEASURE
> command per AMD SEV API spec section 6.5.1. It generates a random 16-byte
> mnonce, computes the launch digest as SHA-256 over ld_data, then derives
> the measurement via HMAC-SHA256
> (TIK;0x04|| API version || build ID || policy || launch digest || mnonce).
> The base64-encoded result (32-byte HMAC + 16-byte mnonce) populates
> "query-sev-launch-measure" data, advancing state to LAUNCH_SECRET for
> secret injection.
>
> The TIK is supplied via 16-byte binary file specified in new
> SevEmulatedProperty "tik" path; absent this, keys default to zeroed.
> Example QEMU arguments with the key passed:
>
> -cpu "EPYC-Milan" \
> -accel tcg \
> -object sev-emulated,id=sev0,cbitpos=47,reduced-phys-bits=1,\
> tik=/path/to/tik.bin \
> -machine memory-encryption=sev0
>
> Signed-off-by: Tommaso Califano <califano.tommaso@gmail.com>
> ---
> qapi/qom.json | 3 +-
> target/i386/sev.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 157 insertions(+), 1 deletion(-)
>
> diff --git a/qapi/qom.json b/qapi/qom.json
> index 35cda819ec..affb5024b5 100644
> --- a/qapi/qom.json
> +++ b/qapi/qom.json
> @@ -1064,11 +1064,12 @@
> # This object functionally emulates AMD SEV hardware via TCG, so
> # it does not require real hardware to run.
> #
> +# @tik: binary file of the SEV TIK (default: all 0).
Is this a file name?
Blank line here, please.
> # Since: 10.1.0
> ##
> { 'struct': 'SevEmulatedProperties',
> 'base': 'SevGuestProperties',
> - 'data': {}}
> + 'data': {'*tik': 'str'}}
>
> ##
> # @SevSnpGuestProperties:
[...]
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH 4/5] i386/sev: Add launch measurement emulation and TIK property
2026-03-19 12:33 ` Markus Armbruster
@ 2026-03-20 14:31 ` Tommaso Califano
0 siblings, 0 replies; 21+ messages in thread
From: Tommaso Califano @ 2026-03-20 14:31 UTC (permalink / raw)
To: Markus Armbruster
Cc: qemu-devel, kvm, Eduardo Habkost, Zhao Liu,
Daniel P. Berrangé, Marcelo Tosatti, Eric Blake,
Oliver Steffen, Stefano Garzarella, Giuseppe Lettieri,
Paolo Bonzini, Luigi Leonardi, Richard Henderson
Il 19/03/26 13:33, Markus Armbruster ha scritto:
> Tommaso Califano <califano.tommaso@gmail.com> writes:
>
>> The next step for completing the SEV launch emulation is to implement the
>> "query-sev-launch-measure" feature, responsible for returning the
>> measurement. In this case the measurement will be computed in QEMU.
>>
>> Implement sev_emulated_launch_get_measure() to emulate the LAUNCH_MEASURE
>> command per AMD SEV API spec section 6.5.1. It generates a random 16-byte
>> mnonce, computes the launch digest as SHA-256 over ld_data, then derives
>> the measurement via HMAC-SHA256
>> (TIK;0x04|| API version || build ID || policy || launch digest || mnonce).
>> The base64-encoded result (32-byte HMAC + 16-byte mnonce) populates
>> "query-sev-launch-measure" data, advancing state to LAUNCH_SECRET for
>> secret injection.
>>
>> The TIK is supplied via 16-byte binary file specified in new
>> SevEmulatedProperty "tik" path; absent this, keys default to zeroed.
>> Example QEMU arguments with the key passed:
>>
>> -cpu "EPYC-Milan" \
>> -accel tcg \
>> -object sev-emulated,id=sev0,cbitpos=47,reduced-phys-bits=1,\
>> tik=/path/to/tik.bin \
>> -machine memory-encryption=sev0
>>
>> Signed-off-by: Tommaso Califano <califano.tommaso@gmail.com>
>> ---
>> qapi/qom.json | 3 +-
>> target/i386/sev.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++
>> 2 files changed, 157 insertions(+), 1 deletion(-)
>>
>> diff --git a/qapi/qom.json b/qapi/qom.json
>> index 35cda819ec..affb5024b5 100644
>> --- a/qapi/qom.json
>> +++ b/qapi/qom.json
>> @@ -1064,11 +1064,12 @@
>> # This object functionally emulates AMD SEV hardware via TCG, so
>> # it does not require real hardware to run.
>> #
>> +# @tik: binary file of the SEV TIK (default: all 0).
>
> Is this a file name?
>
Yes, I'll specify it better writing "Path to the binary file..."
> Blank line here, please.
>
I'll add it.
>> # Since: 10.1.0
>> ##
>> { 'struct': 'SevEmulatedProperties',
>> 'base': 'SevGuestProperties',
>> - 'data': {}}
>> + 'data': {'*tik': 'str'}}
>>
>> ##
>> # @SevSnpGuestProperties:
>
> [...]
>
Best regards,
Tommaso Califano
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 5/5] i386/sev: Implement emulated launch secret injection and TEK property
2026-03-17 11:38 [PATCH 0/5] i386/sev: Add TCG-emulated AMD SEV guest support Tommaso Califano
` (3 preceding siblings ...)
2026-03-17 11:38 ` [PATCH 4/5] i386/sev: Add launch measurement emulation and TIK property Tommaso Califano
@ 2026-03-17 11:38 ` Tommaso Califano
2026-03-17 13:01 ` [PATCH 0/5] i386/sev: Add TCG-emulated AMD SEV guest support Luigi Leonardi
5 siblings, 0 replies; 21+ messages in thread
From: Tommaso Califano @ 2026-03-17 11:38 UTC (permalink / raw)
To: qemu-devel
Cc: kvm, Eduardo Habkost, Markus Armbruster, Zhao Liu,
Daniel P. Berrangé, Marcelo Tosatti, Eric Blake,
Oliver Steffen, Stefano Garzarella, Giuseppe Lettieri,
Paolo Bonzini, Luigi Leonardi, Richard Henderson,
Tommaso Califano
The final step to complete SEV attestation is the implementation of
"sev-inject-launch-secret", which enables injecting a secret into
guest memory.
The function sev_emulated_injection() performs this step.
It is invoked from the existing sev_inject_launch_secret() when
sev_emulated_enabled() is active, allowing the bypass of
the KVM_SEV_LAUNCH_SECRET ioctl.
Upon invocation, it decrypts the secret packet using AES-128-CTR
with the configured TEK, extracting the IV from header bytes 4–19,
and writes the decrypted payload to the guest-specified memory
address.
After injection, the SEV state transitions to RUNNING, completing
the TCG-emulated SEV launch sequence for testing guests without
AMD SEV hardware.
The TEK is provided through a 16-byte binary file, similar to
the TIK, and specified via the new SevEmulatedProperty "tek"
path. If unspecified, the key defaults to all zeroes.
Example of all QEMU arguments:
-cpu "EPYC-Milan" \
-accel tcg \
-object sev-emulated,id=sev0,cbitpos=47,reduced-phys-bits=1,\
tik=/path/to/tik.bin,tek=/path/to/tek.bin \
-machine memory-encryption=sev0
Signed-off-by: Tommaso Califano <califano.tommaso@gmail.com>
---
qapi/qom.json | 5 ++-
target/i386/sev.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 95 insertions(+), 2 deletions(-)
diff --git a/qapi/qom.json b/qapi/qom.json
index affb5024b5..405b6fc858 100644
--- a/qapi/qom.json
+++ b/qapi/qom.json
@@ -1065,11 +1065,14 @@
# it does not require real hardware to run.
#
# @tik: binary file of the SEV TIK (default: all 0).
+#
+# @tek: binary file of the SEV TEK (default: all 0).
# Since: 10.1.0
##
{ 'struct': 'SevEmulatedProperties',
'base': 'SevGuestProperties',
- 'data': {'*tik': 'str'}}
+ 'data': {'*tik': 'str',
+ '*tek': 'str' }}
##
# @SevSnpGuestProperties:
diff --git a/target/i386/sev.c b/target/i386/sev.c
index 5b1c001633..89b3fe3507 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -48,6 +48,7 @@
#include "hw/i386/e820_memory_layout.h"
#include "qemu/queue.h"
#include "qemu/cutils.h"
+#include "crypto/cipher.h"
#include "crypto/hmac.h"
#include "crypto/random.h"
@@ -202,6 +203,7 @@ struct SevGuestState {
typedef struct SevEmulatedState {
SevGuestState parent_obj;
+ uint8_t *tek;
uint8_t *tik;
QEMUIOVector ld_data;
} SevEmulatedState;
@@ -2219,6 +2221,58 @@ sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp)
return 0;
}
+static int sev_emulated_injection(void *hva, guchar *data,
+ gsize data_sz, guchar *hdr)
+{
+
+ SevEmulatedState *sev_emulated =
+ SEV_EMULATED(MACHINE(qdev_get_machine())->cgs);
+ uint8_t iv[TEK_TIK_IV_SIZE];
+ QCryptoCipher *cipher = NULL;
+ g_autofree guchar *plaintext = g_new0(guchar, data_sz);
+ int ret = 0;
+ Error *err = NULL;
+
+ /* Prepare the cipher */
+ cipher = qcrypto_cipher_new(
+ QCRYPTO_CIPHER_ALGO_AES_128,
+ QCRYPTO_CIPHER_MODE_CTR,
+ sev_emulated->tek,
+ TEK_TIK_IV_SIZE,
+ &err
+ );
+ if (!cipher) {
+ error_report_err(err);
+ return 1;
+ }
+
+ /* Extract the IV from the header */
+ memcpy(iv, hdr + 4, TEK_TIK_IV_SIZE);
+ ret = qcrypto_cipher_setiv(cipher, iv, TEK_TIK_IV_SIZE, &err);
+ if (ret < 0) {
+ error_report_err(err);
+ qcrypto_cipher_free(cipher);
+ return 1;
+ }
+
+ /* Decrypt the payload */
+ ret = qcrypto_cipher_decrypt(cipher,
+ data,
+ plaintext,
+ data_sz,
+ &err);
+ if (ret < 0) {
+ error_report_err(err);
+ qcrypto_cipher_free(cipher);
+ return 1;
+ }
+ qcrypto_cipher_free(cipher);
+ memcpy(hva, plaintext, data_sz);
+
+ return 0;
+}
+
+
int sev_inject_launch_secret(const char *packet_hdr, const char *secret,
uint64_t gpa, Error **errp)
{
@@ -2273,6 +2327,15 @@ int sev_inject_launch_secret(const char *packet_hdr, const char *secret,
trace_kvm_sev_launch_secret(gpa, input.guest_uaddr,
input.trans_uaddr, input.trans_len);
+ /*
+ * If SEV emulation is enabled, skip the KVM ioctl (sev_fd == -1) and
+ * inject the secret directly into guest memory via
+ * sev_emulated_injection().
+ */
+ if (sev_emulated_enabled()) {
+ return sev_emulated_injection(hva, data, data_sz, hdr);
+ }
+
ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_SECRET,
&input, &error);
if (ret) {
@@ -3214,6 +3277,25 @@ static void sev_emulated_set_tik(
);
}
+static char *sev_emulated_get_tek(Object *obj, Error **errp)
+{
+ SevEmulatedState *sev_emulated = SEV_EMULATED(obj);
+
+ return g_memdup2(sev_emulated->tek, TEK_TIK_IV_SIZE);
+}
+
+static void sev_emulated_set_tek(
+ Object *obj, const char *key_filename, Error **errp)
+{
+ SevEmulatedState *sev_emulated = SEV_EMULATED(obj);
+
+ sev_emulated_read_key(
+ sev_emulated->tek,
+ key_filename,
+ errp
+ );
+}
+
static void sev_emulated_class_init(ObjectClass *oc, const void *data)
{
SevCommonStateClass *scc = SEV_COMMON_CLASS(oc);
@@ -3223,7 +3305,14 @@ static void sev_emulated_class_init(ObjectClass *oc, const void *data)
scc->launch_update_data = sev_emulated_launch_update_data;
scc->launch_finish = sev_emulated_launch_finish;
- /* Adding emulation specific property */
+ /* Adding emulation specific properties */
+ object_class_property_add_str(oc, "tek",
+ sev_emulated_get_tek,
+ sev_emulated_set_tek);
+ object_class_property_set_description(oc, "tek",
+ "Path to the binary file containing the"
+ "SEV Transport Encryption Key (16 bytes)");
+
object_class_property_add_str(oc, "tik",
sev_emulated_get_tik,
sev_emulated_set_tik);
@@ -3238,6 +3327,7 @@ static void sev_emulated_instance_init(Object *obj)
/* Initialize the key for emulation */
sev_emulated->tik = g_malloc0(TEK_TIK_IV_SIZE);
+ sev_emulated->tek = g_malloc0(TEK_TIK_IV_SIZE);
}
/* guest info specific sev/sev-es */
--
2.53.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH 0/5] i386/sev: Add TCG-emulated AMD SEV guest support
2026-03-17 11:38 [PATCH 0/5] i386/sev: Add TCG-emulated AMD SEV guest support Tommaso Califano
` (4 preceding siblings ...)
2026-03-17 11:38 ` [PATCH 5/5] i386/sev: Implement emulated launch secret injection and TEK property Tommaso Califano
@ 2026-03-17 13:01 ` Luigi Leonardi
5 siblings, 0 replies; 21+ messages in thread
From: Luigi Leonardi @ 2026-03-17 13:01 UTC (permalink / raw)
To: Tommaso Califano
Cc: qemu-devel, kvm, Eduardo Habkost, Markus Armbruster, Zhao Liu,
Daniel P. Berrangé, Marcelo Tosatti, Eric Blake,
Oliver Steffen, Stefano Garzarella, Giuseppe Lettieri,
Paolo Bonzini, Richard Henderson
On Tue, Mar 17, 2026 at 12:38:35PM +0100, Tommaso Califano wrote:
>From: Tommaso Califano <califano.tommaso@gmail.com>
>
>QEMU's AMD SEV support currently requires KVM on expensive AMD EPYC
>hardware, limiting development and testing of SEV-aware guest software to
>users with server-grade machines.
>
>This series introduces a TCG-emulated SEV guest mode that enables SEV
>validation without hardware dependencies, focusing on functional testing
>rather than reproducing the hardware’s cryptographic context.
>
>The emulation exposes SEV from the guest's perspective:
> - Exposed CPUID leaf 0x8000001F to indicate active support.
> - Active bit 0 in MSR 0xc0010131 to enable SEV on the guest.
> - C-bit manipulation in PTEs/CR3 for paging consistency with the host.
> - Complete SEV attestation workflow for injecting secrets into guest
> memory (including direct kernel boot support).
>
>The emulation uses a new QOM object "sev-emulated", derived from
>"sev-guest", to maximize reuse of the existing SEV infrastructure while
>maintaining a compiling dependency with KVM.
>Below are the pros and cons of this choice.
>
>In addition to inherited guest properties, two new ones are added (binary
>files; default all-zero):
> - tik: 16-byte Transport Integrity Key (TIK) for measurement HMAC.
> - tek: 16-byte Transport Encryption Key (TEK) for secret payload
> decryption.
>
>Code reuse benefits:
> - SEV detection via sev_enabled() and TYPE_SEV_COMMON object cast enables
> the required checks for emulation without adding new conditions to the
> codebase.
> - QAPI fields for query-sev/launch-measure inherited from SevCommonState
> and SevGuestState.
> - Identical QMP interface (query-sev, query-sev-launch-measure,
> sev-inject-launch-secret) as real SEV.
> - Shared state machine (sev_set_guest_state()); override backend vtable
> only (kvm_init → sev_emulated_init, launch_update_data, launch_finish,
> sev_launch_get_measure).
>
>Trade-offs:
> - KVM linkage: sev-guest is KVM-centric; even if KVM is not used at
> runtime, its code is required for compilation, so it is not possible to
> use emulation with --disable-kvm.
>
>Example usage:
>
> -cpu "EPYC-Milan" \
> -accel tcg \
> -object sev-emulated,id=sev0,cbitpos=47,reduced-phys-bits=1,\
> tik=/path/to/tik.bin,tek=/path/to/tek.bin \
> -machine memory-encryption=sev0
>
>Build requirements:
>
> ../qemu/configure --enable-gcrypt --enable-crypto-afalg
>
>These provide libgcrypt support for crypto/hmac.h, crypto/cipher.h, and
>crypto/random.h, used for:
> - HMAC-SHA256 launch measurement (TIK key).
> - Secret payload decryption (TEK key).
>Note: --disable-kvm unsupported due to sev-guest inheritance (KVM code
>linked, no runtime dependency).
>
>
To give maintainers some more context: this is part of an ongoing work to
enable SNP emulation in QEMU, which would be very useful for development
purposes (eg coconut-SVSM). However, it should not be used in a production
environment as it provides no security guarantees.
Please consider this as an RFC.
Luigi
^ permalink raw reply [flat|nested] 21+ messages in thread