diff --git a/MAINTAINERS b/MAINTAINERS index a6a1d36f52..ee058e2fef 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3908,6 +3908,17 @@ F: roms/edk2-* F: tests/data/uefi-boot-images/ F: tests/uefi-test-tools/ +IGVM Firmware +M: Gerd Hoffmann +M: Stefano Garzarella +R: Ani Sinha +S: Maintained +F: backends/igvm*.c +F: docs/system/igvm.rst +F: include/system/igvm*.h +F: stubs/igvm.c +F: target/i386/igvm.c + VT-d Emulation M: Michael S. Tsirkin R: Jason Wang diff --git a/backends/igvm-cfg.c b/backends/igvm-cfg.c index 45df63e06c..d00acf3512 100644 --- a/backends/igvm-cfg.c +++ b/backends/igvm-cfg.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" #include "system/igvm-cfg.h" -#include "igvm.h" +#include "system/igvm.h" #include "qom/object_interfaces.h" static char *get_igvm(Object *obj, Error **errp) diff --git a/backends/igvm.c b/backends/igvm.c index 9ad41582ee..905bd8d989 100644 --- a/backends/igvm.c +++ b/backends/igvm.c @@ -11,8 +11,9 @@ #include "qemu/osdep.h" -#include "igvm.h" #include "qapi/error.h" +#include "qemu/target-info-qapi.h" +#include "system/igvm.h" #include "system/memory.h" #include "system/address-spaces.h" #include "hw/core/cpu.h" @@ -431,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) { @@ -452,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; @@ -543,6 +544,8 @@ static int qigvm_directive_memory_map(QIgvm *ctx, const uint8_t *header_data, Error **errp) { const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data; + int (*get_mem_map_entry)(int index, ConfidentialGuestMemoryMapEntry *entry, + Error **errp) = NULL; QIgvmParameterData *param_entry; int max_entry_count; int entry = 0; @@ -550,7 +553,13 @@ static int qigvm_directive_memory_map(QIgvm *ctx, const uint8_t *header_data, ConfidentialGuestMemoryMapEntry cgmm_entry; int retval = 0; - if (!ctx->cgs) { + if (ctx->cgs && ctx->cgsc->get_mem_map_entry) { + get_mem_map_entry = ctx->cgsc->get_mem_map_entry; + + } else if (target_arch() == SYS_EMU_TARGET_X86_64) { + get_mem_map_entry = qigvm_x86_get_mem_map_entry; + + } else { error_setg(errp, "IGVM file contains a memory map but this is not supported " "by the current system."); @@ -565,9 +574,9 @@ static int qigvm_directive_memory_map(QIgvm *ctx, const uint8_t *header_data, param_entry->size / sizeof(IGVM_VHS_MEMORY_MAP_ENTRY); mm_entry = (IGVM_VHS_MEMORY_MAP_ENTRY *)param_entry->data; - retval = ctx->cgsc->get_mem_map_entry(entry, &cgmm_entry, errp); + retval = get_mem_map_entry(entry, &cgmm_entry, errp); while (retval == 0) { - if (entry > max_entry_count) { + if (entry >= max_entry_count) { error_setg( errp, "IGVM: guest memory map size exceeds parameter area defined in IGVM file"); @@ -598,8 +607,7 @@ static int qigvm_directive_memory_map(QIgvm *ctx, const uint8_t *header_data, IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED; break; } - retval = - ctx->cgsc->get_mem_map_entry(++entry, &cgmm_entry, errp); + retval = get_mem_map_entry(++entry, &cgmm_entry, errp); } if (retval < 0) { return retval; diff --git a/backends/igvm.h b/include/system/igvm.h similarity index 64% rename from backends/igvm.h rename to include/system/igvm.h index a4abab043a..48ce206042 100644 --- a/backends/igvm.h +++ b/include/system/igvm.h @@ -19,4 +19,11 @@ int qigvm_process_file(IgvmCfg *igvm, ConfidentialGuestSupport *cgs, bool onlyVpContext, Error **errp); +/* x86 native */ +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/stubs/igvm.c b/stubs/igvm.c new file mode 100644 index 0000000000..17cd1e903e --- /dev/null +++ b/stubs/igvm.c @@ -0,0 +1,26 @@ +/* + * QEMU IGVM, stubs + * + * Copyright (C) 2026 Red Hat + * + * Authors: + * Gerd Hoffmann + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" + +#include "system/igvm.h" + +int qigvm_x86_get_mem_map_entry(int index, + ConfidentialGuestMemoryMapEntry *entry, + Error **errp) +{ + return -1; +} + +int qigvm_x86_set_vp_context(void *data, int index, Error **errp) +{ + return -1; +} diff --git a/stubs/meson.build b/stubs/meson.build index 5d577467bf..27be2dec9f 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -74,6 +74,7 @@ if have_system stub_ss.add(files('dump.c')) stub_ss.add(files('cmos.c')) stub_ss.add(files('fw_cfg.c')) + stub_ss.add(files('igvm.c')) stub_ss.add(files('target-get-monitor-def.c')) stub_ss.add(files('target-monitor-defs.c')) stub_ss.add(files('win32-kbd-hook.c')) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 0a66e1fec9..6417775786 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -8770,6 +8770,12 @@ static void x86_cpu_reset_hold(Object *obj, ResetType type) cs->halted = !cpu_is_bsp(cpu); +#if defined(CONFIG_IGVM) + if (cpu_is_bsp(cpu)) { + qigvm_x86_bsp_reset(env); + } +#endif + if (kvm_enabled()) { kvm_arch_reset_vcpu(cpu); } diff --git a/target/i386/cpu.h b/target/i386/cpu.h index d0da9bfe58..cee1f692a1 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2868,6 +2868,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/target/i386/igvm.c b/target/i386/igvm.c new file mode 100644 index 0000000000..457c253b03 --- /dev/null +++ b/target/i386/igvm.c @@ -0,0 +1,180 @@ +/* + * QEMU IGVM, support for native x86 guests + * + * Copyright (C) 2026 Red Hat + * + * Authors: + * Gerd Hoffmann + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#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 + */ +int qigvm_x86_get_mem_map_entry(int index, + ConfidentialGuestMemoryMapEntry *entry, + Error **errp) +{ + struct e820_entry *table; + int num_entries; + + num_entries = e820_get_table(&table); + if ((index < 0) || (index >= num_entries)) { + return 1; + } + entry->gpa = table[index].address; + entry->size = table[index].length; + switch (table[index].type) { + case E820_RAM: + entry->type = CGS_MEM_RAM; + break; + case E820_RESERVED: + entry->type = CGS_MEM_RESERVED; + break; + default: + /* should not happen */ + error_setg(errp, "unknown e820 type"); + return -1; + } + 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); +} diff --git a/target/i386/meson.build b/target/i386/meson.build index 89ba4912aa..d385eafdf7 100644 --- a/target/i386/meson.build +++ b/target/i386/meson.build @@ -26,6 +26,9 @@ i386_system_ss.add(files( )) i386_system_ss.add(when: 'CONFIG_SEV', if_true: files('sev.c'), if_false: files('sev-system-stub.c')) +if igvm.found() + i386_system_ss.add(files('igvm.c')) +endif i386_user_ss = ss.source_set()