Add IgvmNativeVpContextX64 struct holding the register state (see igvm
spec), and the qigvm_x86_load_context() function to load the register
state.
Wire up using two new functions: qigvm_x86_set_vp_context() is called
from igvm file handling code and stores the boot processor context.
qigvm_x86_bsp_reset() is called from i386 target cpu reset code and
loads the context into the cpu registers.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
include/system/igvm.h | 2 +
target/i386/cpu.h | 3 +
backends/igvm.c | 30 +++++-----
stubs/igvm.c | 5 ++
target/i386/cpu.c | 4 ++
target/i386/igvm.c | 133 ++++++++++++++++++++++++++++++++++++++++++
6 files changed, 162 insertions(+), 15 deletions(-)
diff --git a/include/system/igvm.h b/include/system/igvm.h
index 3f72a40b8897..48ce20604259 100644
--- a/include/system/igvm.h
+++ b/include/system/igvm.h
@@ -23,5 +23,7 @@ int qigvm_process_file(IgvmCfg *igvm, ConfidentialGuestSupport *cgs,
int qigvm_x86_get_mem_map_entry(int index,
ConfidentialGuestMemoryMapEntry *entry,
Error **errp);
+int qigvm_x86_set_vp_context(void *data, int index,
+ Error **errp);
#endif
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index ce948861a765..78d4b427c67a 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -2867,6 +2867,9 @@ void x86_cpu_dump_local_apic_state(CPUState *cs, int flags);
#endif
+/* igvm.c */
+void qigvm_x86_bsp_reset(CPUX86State *env);
+
/* cpu.c */
bool cpu_is_bsp(X86CPU *cpu);
diff --git a/backends/igvm.c b/backends/igvm.c
index e949c81abb9a..ee5ee74c7a66 100644
--- a/backends/igvm.c
+++ b/backends/igvm.c
@@ -432,18 +432,6 @@ static int qigvm_directive_vp_context(QIgvm *ctx, const uint8_t *header_data,
return 0;
}
- /*
- * A confidential guest support object must be provided for setting
- * a VP context.
- */
- if (!ctx->cgs) {
- error_setg(
- errp,
- "A VP context is present in the IGVM file but is not supported "
- "by the current system.");
- return -1;
- }
-
data_handle = igvm_get_header_data(ctx->file, IGVM_HEADER_SECTION_DIRECTIVE,
ctx->current_header_index);
if (data_handle < 0) {
@@ -453,9 +441,21 @@ static int qigvm_directive_vp_context(QIgvm *ctx, const uint8_t *header_data,
}
data = (uint8_t *)igvm_get_buffer(ctx->file, data_handle);
- result = ctx->cgsc->set_guest_state(
- vp_context->gpa, data, igvm_get_buffer_size(ctx->file, data_handle),
- CGS_PAGE_TYPE_VMSA, vp_context->vp_index, errp);
+
+ if (ctx->cgs) {
+ result = ctx->cgsc->set_guest_state(
+ vp_context->gpa, data, igvm_get_buffer_size(ctx->file, data_handle),
+ CGS_PAGE_TYPE_VMSA, vp_context->vp_index, errp);
+ } else if (target_arch() == SYS_EMU_TARGET_X86_64) {
+ result = qigvm_x86_set_vp_context(data, vp_context->vp_index, errp);
+ } else {
+ error_setg(
+ errp,
+ "A VP context is present in the IGVM file but is not supported "
+ "by the current system.");
+ result = -1;
+ }
+
igvm_free_buffer(ctx->file, data_handle);
if (result < 0) {
return result;
diff --git a/stubs/igvm.c b/stubs/igvm.c
index c32058eb2a6e..17cd1e903e35 100644
--- a/stubs/igvm.c
+++ b/stubs/igvm.c
@@ -19,3 +19,8 @@ int qigvm_x86_get_mem_map_entry(int index,
{
return -1;
}
+
+int qigvm_x86_set_vp_context(void *data, int index, Error **errp)
+{
+ return -1;
+}
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index 455caff6b230..8dd5ac8020c2 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -8770,6 +8770,10 @@ static void x86_cpu_reset_hold(Object *obj, ResetType type)
cs->halted = !cpu_is_bsp(cpu);
+ if (cpu_is_bsp(cpu)) {
+ qigvm_x86_bsp_reset(env);
+ }
+
if (kvm_enabled()) {
kvm_arch_reset_vcpu(cpu);
}
diff --git a/target/i386/igvm.c b/target/i386/igvm.c
index 2ed6cd052c79..0921ec830508 100644
--- a/target/i386/igvm.c
+++ b/target/i386/igvm.c
@@ -11,9 +11,114 @@
#include "qemu/osdep.h"
+#include "cpu.h"
#include "hw/i386/e820_memory_layout.h"
#include "system/igvm.h"
+struct IgvmNativeVpContextX64 {
+ uint64_t rax;
+ uint64_t rcx;
+ uint64_t rdx;
+ uint64_t rbx;
+ uint64_t rsp;
+ uint64_t rbp;
+ uint64_t rsi;
+ uint64_t rdi;
+ uint64_t r8;
+ uint64_t r9;
+ uint64_t r10;
+ uint64_t r11;
+ uint64_t r12;
+ uint64_t r13;
+ uint64_t r14;
+ uint64_t r15;
+ uint64_t rip;
+ uint64_t rflags;
+ uint64_t idtr_base;
+ uint16_t idtr_limit;
+ uint16_t reserved[2];
+ uint16_t gdtr_limit;
+ uint64_t gdtr_base;
+
+ uint16_t code_selector;
+ uint16_t code_attributes;
+ uint32_t code_base;
+ uint32_t code_limit;
+
+ uint16_t data_selector;
+ uint16_t data_attributes;
+ uint32_t data_base;
+ uint32_t data_limit;
+
+ uint64_t gs_base;
+ uint64_t cr0;
+ uint64_t cr3;
+ uint64_t cr4;
+ uint64_t efer;
+};
+
+#define FLAGS_TO_SEGCACHE(flags) \
+ (((unsigned int)flags) << 8)
+
+static void qigvm_x86_load_context(struct IgvmNativeVpContextX64 *context, CPUX86State *env)
+{
+ cpu_load_efer(env, context->efer);
+ cpu_x86_update_cr4(env, context->cr4);
+ cpu_x86_update_cr0(env, context->cr0);
+ cpu_x86_update_cr3(env, context->cr3);
+
+ cpu_x86_load_seg_cache(
+ env, R_CS, context->code_selector,
+ context->code_base, context->code_limit,
+ FLAGS_TO_SEGCACHE(context->code_attributes));
+ cpu_x86_load_seg_cache(
+ env, R_DS, context->data_selector,
+ context->data_base, context->data_limit,
+ FLAGS_TO_SEGCACHE(context->data_attributes));
+ cpu_x86_load_seg_cache(
+ env, R_ES, context->data_selector,
+ context->data_base, context->data_limit,
+ FLAGS_TO_SEGCACHE(context->data_attributes));
+ cpu_x86_load_seg_cache(
+ env, R_FS, context->data_selector,
+ context->data_base, context->data_limit,
+ FLAGS_TO_SEGCACHE(context->data_attributes));
+ cpu_x86_load_seg_cache(
+ env, R_GS, context->data_selector,
+ context->data_base, context->data_limit,
+ FLAGS_TO_SEGCACHE(context->data_attributes));
+ cpu_x86_load_seg_cache(
+ env, R_SS, context->data_selector,
+ context->data_base, context->data_limit,
+ FLAGS_TO_SEGCACHE(context->data_attributes));
+
+ env->gdt.base = context->gdtr_base;
+ env->gdt.limit = context->gdtr_limit;
+ env->idt.base = context->idtr_base;
+ env->idt.limit = context->idtr_limit;
+
+ env->regs[R_EAX] = context->rax;
+ env->regs[R_ECX] = context->rcx;
+ env->regs[R_EDX] = context->rdx;
+ env->regs[R_EBX] = context->rbx;
+ env->regs[R_ESP] = context->rsp;
+ env->regs[R_EBP] = context->rbp;
+ env->regs[R_ESI] = context->rsi;
+ env->regs[R_EDI] = context->rdi;
+#ifdef TARGET_X86_64
+ env->regs[R_R8] = context->r8;
+ env->regs[R_R9] = context->r9;
+ env->regs[R_R10] = context->r10;
+ env->regs[R_R11] = context->r11;
+ env->regs[R_R12] = context->r12;
+ env->regs[R_R13] = context->r13;
+ env->regs[R_R14] = context->r14;
+ env->regs[R_R15] = context->r15;
+#endif
+ env->eip = context->rip;
+ env->eflags = context->rflags;
+}
+
/*
* convert e820 table into igvm memory map
*/
@@ -44,3 +149,31 @@ int qigvm_x86_get_mem_map_entry(int index,
}
return 0;
}
+
+/*
+ * set initial cpu context
+ */
+static struct IgvmNativeVpContextX64 *bsp_context;
+
+int qigvm_x86_set_vp_context(void *data, int index, Error **errp)
+{
+ if (index != 0) {
+ error_setg(errp, "context can be set for BSP only");
+ return -1;
+ }
+
+ if (bsp_context == NULL) {
+ bsp_context = g_new0(struct IgvmNativeVpContextX64, 1);
+ }
+ memcpy(bsp_context, data, sizeof(struct IgvmNativeVpContextX64));
+ return 0;
+}
+
+void qigvm_x86_bsp_reset(CPUX86State *env)
+{
+ if (bsp_context == NULL) {
+ return;
+ }
+
+ qigvm_x86_load_context(bsp_context, env);
+}
--
2.51.0
On Wed, Oct 15, 2025 at 01:23:40PM +0200, Gerd Hoffmann wrote:
>Add IgvmNativeVpContextX64 struct holding the register state (see igvm
>spec), and the qigvm_x86_load_context() function to load the register
>state.
>
>Wire up using two new functions: qigvm_x86_set_vp_context() is called
>from igvm file handling code and stores the boot processor context.
>qigvm_x86_bsp_reset() is called from i386 target cpu reset code and
>loads the context into the cpu registers.
>
>Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
>---
> include/system/igvm.h | 2 +
> target/i386/cpu.h | 3 +
> backends/igvm.c | 30 +++++-----
> stubs/igvm.c | 5 ++
> target/i386/cpu.c | 4 ++
> target/i386/igvm.c | 133 ++++++++++++++++++++++++++++++++++++++++++
> 6 files changed, 162 insertions(+), 15 deletions(-)
>
>diff --git a/include/system/igvm.h b/include/system/igvm.h
>index 3f72a40b8897..48ce20604259 100644
>--- a/include/system/igvm.h
>+++ b/include/system/igvm.h
>@@ -23,5 +23,7 @@ int qigvm_process_file(IgvmCfg *igvm, ConfidentialGuestSupport *cgs,
> int qigvm_x86_get_mem_map_entry(int index,
> ConfidentialGuestMemoryMapEntry *entry,
> Error **errp);
>+int qigvm_x86_set_vp_context(void *data, int index,
>+ Error **errp);
>
> #endif
>diff --git a/target/i386/cpu.h b/target/i386/cpu.h
>index ce948861a765..78d4b427c67a 100644
>--- a/target/i386/cpu.h
>+++ b/target/i386/cpu.h
>@@ -2867,6 +2867,9 @@ void x86_cpu_dump_local_apic_state(CPUState *cs, int flags);
>
> #endif
>
>+/* igvm.c */
>+void qigvm_x86_bsp_reset(CPUX86State *env);
>+
> /* cpu.c */
> bool cpu_is_bsp(X86CPU *cpu);
>
>diff --git a/backends/igvm.c b/backends/igvm.c
>index e949c81abb9a..ee5ee74c7a66 100644
>--- a/backends/igvm.c
>+++ b/backends/igvm.c
>@@ -432,18 +432,6 @@ static int qigvm_directive_vp_context(QIgvm *ctx, const uint8_t *header_data,
> return 0;
> }
>
>- /*
>- * A confidential guest support object must be provided for setting
>- * a VP context.
>- */
>- if (!ctx->cgs) {
>- error_setg(
>- errp,
>- "A VP context is present in the IGVM file but is not supported "
>- "by the current system.");
>- return -1;
>- }
>-
> data_handle = igvm_get_header_data(ctx->file, IGVM_HEADER_SECTION_DIRECTIVE,
> ctx->current_header_index);
> if (data_handle < 0) {
>@@ -453,9 +441,21 @@ static int qigvm_directive_vp_context(QIgvm *ctx, const uint8_t *header_data,
> }
>
> data = (uint8_t *)igvm_get_buffer(ctx->file, data_handle);
>- result = ctx->cgsc->set_guest_state(
>- vp_context->gpa, data, igvm_get_buffer_size(ctx->file, data_handle),
>- CGS_PAGE_TYPE_VMSA, vp_context->vp_index, errp);
>+
>+ if (ctx->cgs) {
>+ result = ctx->cgsc->set_guest_state(
>+ vp_context->gpa, data, igvm_get_buffer_size(ctx->file, data_handle),
>+ CGS_PAGE_TYPE_VMSA, vp_context->vp_index, errp);
>+ } else if (target_arch() == SYS_EMU_TARGET_X86_64) {
>+ result = qigvm_x86_set_vp_context(data, vp_context->vp_index, errp);
>+ } else {
>+ error_setg(
>+ errp,
>+ "A VP context is present in the IGVM file but is not supported "
>+ "by the current system.");
It is pre-existing, but should we avoid splitting error messages to
simplify searching for them in the code?
I don't remember if it's the same thing in QEMU, but in Linux we prefer
this one, IIRC.
>+ result = -1;
>+ }
>+
> igvm_free_buffer(ctx->file, data_handle);
> if (result < 0) {
> return result;
>diff --git a/stubs/igvm.c b/stubs/igvm.c
>index c32058eb2a6e..17cd1e903e35 100644
>--- a/stubs/igvm.c
>+++ b/stubs/igvm.c
>@@ -19,3 +19,8 @@ int qigvm_x86_get_mem_map_entry(int index,
> {
> return -1;
> }
>+
>+int qigvm_x86_set_vp_context(void *data, int index, Error **errp)
>+{
>+ return -1;
>+}
>diff --git a/target/i386/cpu.c b/target/i386/cpu.c
>index 455caff6b230..8dd5ac8020c2 100644
>--- a/target/i386/cpu.c
>+++ b/target/i386/cpu.c
>@@ -8770,6 +8770,10 @@ static void x86_cpu_reset_hold(Object *obj, ResetType type)
>
> cs->halted = !cpu_is_bsp(cpu);
>
>+ if (cpu_is_bsp(cpu)) {
>+ qigvm_x86_bsp_reset(env);
>+ }
>+
> if (kvm_enabled()) {
> kvm_arch_reset_vcpu(cpu);
> }
>diff --git a/target/i386/igvm.c b/target/i386/igvm.c
>index 2ed6cd052c79..0921ec830508 100644
>--- a/target/i386/igvm.c
>+++ b/target/i386/igvm.c
>@@ -11,9 +11,114 @@
>
> #include "qemu/osdep.h"
>
>+#include "cpu.h"
> #include "hw/i386/e820_memory_layout.h"
> #include "system/igvm.h"
>
>+struct IgvmNativeVpContextX64 {
Do you know why this is not exposed by igvm headers?
BTW this patch LGTM:
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
>+ uint64_t rax;
>+ uint64_t rcx;
>+ uint64_t rdx;
>+ uint64_t rbx;
>+ uint64_t rsp;
>+ uint64_t rbp;
>+ uint64_t rsi;
>+ uint64_t rdi;
>+ uint64_t r8;
>+ uint64_t r9;
>+ uint64_t r10;
>+ uint64_t r11;
>+ uint64_t r12;
>+ uint64_t r13;
>+ uint64_t r14;
>+ uint64_t r15;
>+ uint64_t rip;
>+ uint64_t rflags;
>+ uint64_t idtr_base;
>+ uint16_t idtr_limit;
>+ uint16_t reserved[2];
>+ uint16_t gdtr_limit;
>+ uint64_t gdtr_base;
>+
>+ uint16_t code_selector;
>+ uint16_t code_attributes;
>+ uint32_t code_base;
>+ uint32_t code_limit;
>+
>+ uint16_t data_selector;
>+ uint16_t data_attributes;
>+ uint32_t data_base;
>+ uint32_t data_limit;
>+
>+ uint64_t gs_base;
>+ uint64_t cr0;
>+ uint64_t cr3;
>+ uint64_t cr4;
>+ uint64_t efer;
>+};
>+
>+#define FLAGS_TO_SEGCACHE(flags) \
>+ (((unsigned int)flags) << 8)
>+
>+static void qigvm_x86_load_context(struct IgvmNativeVpContextX64 *context, CPUX86State *env)
>+{
>+ cpu_load_efer(env, context->efer);
>+ cpu_x86_update_cr4(env, context->cr4);
>+ cpu_x86_update_cr0(env, context->cr0);
>+ cpu_x86_update_cr3(env, context->cr3);
>+
>+ cpu_x86_load_seg_cache(
>+ env, R_CS, context->code_selector,
>+ context->code_base, context->code_limit,
>+ FLAGS_TO_SEGCACHE(context->code_attributes));
>+ cpu_x86_load_seg_cache(
>+ env, R_DS, context->data_selector,
>+ context->data_base, context->data_limit,
>+ FLAGS_TO_SEGCACHE(context->data_attributes));
>+ cpu_x86_load_seg_cache(
>+ env, R_ES, context->data_selector,
>+ context->data_base, context->data_limit,
>+ FLAGS_TO_SEGCACHE(context->data_attributes));
>+ cpu_x86_load_seg_cache(
>+ env, R_FS, context->data_selector,
>+ context->data_base, context->data_limit,
>+ FLAGS_TO_SEGCACHE(context->data_attributes));
>+ cpu_x86_load_seg_cache(
>+ env, R_GS, context->data_selector,
>+ context->data_base, context->data_limit,
>+ FLAGS_TO_SEGCACHE(context->data_attributes));
>+ cpu_x86_load_seg_cache(
>+ env, R_SS, context->data_selector,
>+ context->data_base, context->data_limit,
>+ FLAGS_TO_SEGCACHE(context->data_attributes));
>+
>+ env->gdt.base = context->gdtr_base;
>+ env->gdt.limit = context->gdtr_limit;
>+ env->idt.base = context->idtr_base;
>+ env->idt.limit = context->idtr_limit;
>+
>+ env->regs[R_EAX] = context->rax;
>+ env->regs[R_ECX] = context->rcx;
>+ env->regs[R_EDX] = context->rdx;
>+ env->regs[R_EBX] = context->rbx;
>+ env->regs[R_ESP] = context->rsp;
>+ env->regs[R_EBP] = context->rbp;
>+ env->regs[R_ESI] = context->rsi;
>+ env->regs[R_EDI] = context->rdi;
>+#ifdef TARGET_X86_64
>+ env->regs[R_R8] = context->r8;
>+ env->regs[R_R9] = context->r9;
>+ env->regs[R_R10] = context->r10;
>+ env->regs[R_R11] = context->r11;
>+ env->regs[R_R12] = context->r12;
>+ env->regs[R_R13] = context->r13;
>+ env->regs[R_R14] = context->r14;
>+ env->regs[R_R15] = context->r15;
>+#endif
>+ env->eip = context->rip;
>+ env->eflags = context->rflags;
>+}
>+
> /*
> * convert e820 table into igvm memory map
> */
>@@ -44,3 +149,31 @@ int qigvm_x86_get_mem_map_entry(int index,
> }
> return 0;
> }
>+
>+/*
>+ * set initial cpu context
>+ */
>+static struct IgvmNativeVpContextX64 *bsp_context;
>+
>+int qigvm_x86_set_vp_context(void *data, int index, Error **errp)
>+{
>+ if (index != 0) {
>+ error_setg(errp, "context can be set for BSP only");
>+ return -1;
>+ }
>+
>+ if (bsp_context == NULL) {
>+ bsp_context = g_new0(struct IgvmNativeVpContextX64, 1);
>+ }
>+ memcpy(bsp_context, data, sizeof(struct IgvmNativeVpContextX64));
>+ return 0;
>+}
>+
>+void qigvm_x86_bsp_reset(CPUX86State *env)
>+{
>+ if (bsp_context == NULL) {
>+ return;
>+ }
>+
>+ qigvm_x86_load_context(bsp_context, env);
>+}
>--
>2.51.0
>
Hi,
> > +struct IgvmNativeVpContextX64 {
>
> Do you know why this is not exposed by igvm headers?
No idea. Given this is part of the file format it is highly unlikely to
ever change, so having our own copy shouldn't be much of a maintenance
problem.
take care,
Gerd
Hi, > > + error_setg( > > + errp, > > + "A VP context is present in the IGVM file but is not supported " > > + "by the current system."); > > It is pre-existing, but should we avoid splitting error messages to simplify > searching for them in the code? checkpatch.pl throws an error about a long line if I drop the split. take care, Gerd
© 2016 - 2026 Red Hat, Inc.