From: Thomas Huth <thuth@redhat.com>
To: Paolo Bonzini <pbonzini@redhat.com>, qemu-devel@nongnu.org
Cc: "Pierrick Bouvier" <pierrick.bouvier@linaro.org>,
"Michael S. Tsirkin" <mst@redhat.com>,
"Richard Henderson" <richard.henderson@linaro.org>,
"Philippe Mathieu-Daudé" <philmd@linaro.org>,
"Zhao Liu" <zhao1.liu@intel.com>,
"Thomas Huth" <thuth@redhat.com>
Subject: [PATCH 09/10] target/i386: Support migrating from i386 to x86_64 target
Date: Thu, 2 Apr 2026 11:51:31 +0200 [thread overview]
Message-ID: <20260402095132.29245-10-thuth@redhat.com> (raw)
In-Reply-To: <20260402095132.29245-1-thuth@redhat.com>
From: Thomas Huth <thuth@redhat.com>
For migrating from qemu-system-i386 to qemu-system-x86_64, we have
to support the CPU vmstate of the 32-bit target.
Signed-off-by: Thomas Huth <thuth@redhat.com>
---
target/i386/cpu.h | 47 +++++++--
target/i386/cpu.c | 8 +-
target/i386/machine.c | 222 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 270 insertions(+), 7 deletions(-)
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 78f2dadc2e3..88fcf44fdc0 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -1632,6 +1632,13 @@ typedef struct SegmentCache {
uint32_t flags;
} SegmentCache;
+typedef struct SegmentCache32 {
+ uint32_t selector;
+ uint32_t base;
+ uint32_t limit;
+ uint32_t flags;
+} SegmentCache32;
+
typedef union MMXReg {
uint8_t _b_MMXReg[64 / 8];
uint16_t _w_MMXReg[64 / 16];
@@ -1974,10 +1981,16 @@ typedef struct CPUCaches {
typedef struct CPUArchState {
/* standard registers */
target_ulong regs[CPU_NB_EREGS];
- target_ulong eip;
- target_ulong eflags; /* eflags register. During CPU emulation, CC
- flags and DF are set to zero because they are
- stored elsewhere */
+ union {
+ target_ulong eip;
+ uint32_t eip32;
+ };
+ union {
+ target_ulong eflags; /* eflags register. During CPU emulation, CC
+ flags and DF are set to zero because they are
+ stored elsewhere */
+ uint32_t eflags32;
+ };
/* emulator internal eflags handling */
target_ulong cc_dst;
@@ -2042,8 +2055,14 @@ typedef struct CPUArchState {
/* sysenter registers */
uint32_t sysenter_cs;
- target_ulong sysenter_esp;
- target_ulong sysenter_eip;
+ union {
+ target_ulong sysenter_esp;
+ uint32_t sysenter_esp32;
+ };
+ union {
+ target_ulong sysenter_eip;
+ uint32_t sysenter_eip32;
+ };
uint64_t star;
uint64_t vm_hsave;
@@ -2294,6 +2313,21 @@ typedef struct CPUArchState {
uint16_t fptag_vmstate;
uint16_t fpregs_format_vmstate;
+#ifdef TARGET_X86_64
+ /*
+ * These fields are only used for migrating from qemu-system-i386
+ * to qemu-system-x86_64
+ */
+ uint32_t regs32[CPU_NB_REGS32];
+ SegmentCache32 segs32[6]; /* selector values */
+ SegmentCache32 ldt32;
+ SegmentCache32 tr32;
+ SegmentCache32 gdt32; /* only base and limit are used */
+ SegmentCache32 idt32; /* only base and limit are used */
+ uint32_t cr32[5];
+ uint32_t dr32[8];
+#endif
+
uint64_t xss;
uint32_t umwait;
@@ -2546,6 +2580,7 @@ struct X86CPUClass {
#ifndef CONFIG_USER_ONLY
extern const VMStateDescription vmstate_x86_cpu;
+extern const VMStateDescription vmstate_i386_cpu;
#endif
int x86_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cpu,
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index a8ff1b29f33..a087a45dbfe 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -10701,7 +10701,7 @@ static const Property x86_cpu_properties[] = {
#ifndef CONFIG_USER_ONLY
#include "hw/core/sysemu-cpu-ops.h"
-static const struct SysemuCPUOps i386_sysemu_ops = {
+static struct SysemuCPUOps i386_sysemu_ops = {
.has_work = x86_cpu_has_work,
.get_memory_mapping = x86_cpu_get_memory_mapping,
.get_paging_enabled = x86_cpu_get_paging_enabled,
@@ -10746,8 +10746,14 @@ static void x86_cpu_common_class_init(ObjectClass *oc, const void *data)
#ifndef CONFIG_USER_ONLY
cc->max_as = X86ASIdx_MAX;
+#ifdef TARGET_X86_64
+ if (target_i386()) {
+ i386_sysemu_ops.legacy_vmsd = &vmstate_i386_cpu;
+ }
+#endif
cc->sysemu_ops = &i386_sysemu_ops;
#endif /* !CONFIG_USER_ONLY */
+
#ifdef CONFIG_TCG
cc->tcg_ops = &x86_tcg_ops;
#endif /* CONFIG_TCG */
diff --git a/target/i386/machine.c b/target/i386/machine.c
index 48a2a4b3190..bb6019d3419 100644
--- a/target/i386/machine.c
+++ b/target/i386/machine.c
@@ -310,6 +310,44 @@ static int cpu_pre_save(void *opaque)
return 0;
}
+
+#ifdef TARGET_X86_64
+static void copy_segcache(SegmentCache32 *sc32, SegmentCache *sc64)
+{
+ sc64->selector = sc32->selector;
+ sc64->base = sc32->base;
+ sc64->limit = sc32->limit;
+ sc64->flags = sc32->flags;
+}
+#endif
+
+static void cpu_post_load_fixup32(CPUX86State *env)
+{
+#ifdef TARGET_X86_64
+ int i;
+
+ for (i = 0; i < CPU_NB_REGS32; i++) {
+ env->regs[i] = env->regs32[i];
+ }
+
+ for (i = 0; i < ARRAY_SIZE(env->segs); i++) {
+ copy_segcache(&env->segs32[i], &env->segs[i]);
+ }
+
+ copy_segcache(&env->ldt32, &env->ldt);
+ copy_segcache(&env->tr32, &env->tr);
+ copy_segcache(&env->gdt32, &env->gdt);
+ copy_segcache(&env->idt32, &env->idt);
+
+ for (i = 0; i < ARRAY_SIZE(env->cr); i++) {
+ env->cr[i] = env->cr32[i];
+ }
+ for (i = 0; i < ARRAY_SIZE(env->dr); i++) {
+ env->dr[i] = env->dr32[i];
+ }
+#endif
+}
+
static int cpu_post_load(void *opaque, int version_id)
{
X86CPU *cpu = opaque;
@@ -317,6 +355,10 @@ static int cpu_post_load(void *opaque, int version_id)
CPUX86State *env = &cpu->env;
int i;
+ if (target_i386()) {
+ cpu_post_load_fixup32(env);
+ }
+
if (env->tsc_khz && env->user_tsc_khz &&
env->tsc_khz != env->user_tsc_khz) {
error_report("Mismatch between user-specified TSC frequency and "
@@ -1920,3 +1962,183 @@ const VMStateDescription vmstate_x86_cpu = {
NULL
}
};
+
+/* ***************** 32-bit target hacks below **************** */
+
+#ifdef TARGET_X86_64
+
+static const VMStateDescription vmstate_segment32 = {
+ .name = "segment",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32(selector, SegmentCache32),
+ VMSTATE_UINT32(base, SegmentCache32),
+ VMSTATE_UINT32(limit, SegmentCache32),
+ VMSTATE_UINT32(flags, SegmentCache32),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+#define VMSTATE_SEGMENT32(_field, _state) { \
+ .name = (stringify(_field)), \
+ .size = sizeof(SegmentCache32), \
+ .vmsd = &vmstate_segment32, \
+ .flags = VMS_STRUCT, \
+ .offset = offsetof(_state, _field) \
+ + type_check(SegmentCache32, typeof_field(_state, _field)) \
+}
+
+#define VMSTATE_SEGMENT32_ARRAY(_field, _state, _n) \
+ VMSTATE_STRUCT_ARRAY(_field, _state, _n, 0, vmstate_segment32, SegmentCache32)
+
+#define VMSTATE_XMM32_REGS(_field, _state, _start) \
+ VMSTATE_STRUCT_SUB_ARRAY(_field, _state, _start, CPU_NB_REGS32, 0, \
+ vmstate_xmm_reg, ZMMReg)
+
+#define VMSTATE_YMMH32_REGS_VARS(_field, _state, _start, _v) \
+ VMSTATE_STRUCT_SUB_ARRAY(_field, _state, _start, CPU_NB_REGS32, _v, \
+ vmstate_ymmh_reg, ZMMReg)
+
+const VMStateDescription vmstate_i386_cpu = {
+ .name = "cpu",
+ .version_id = 12,
+ .minimum_version_id = 11,
+ .pre_save = cpu_pre_save,
+ .post_load = cpu_post_load,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32_SUB_ARRAY(env.regs32, X86CPU, 0, CPU_NB_REGS32),
+ VMSTATE_UINT32(env.eip32, X86CPU),
+ VMSTATE_UINT32(env.eflags32, X86CPU),
+ VMSTATE_UINT32(env.hflags, X86CPU),
+ /* FPU */
+ VMSTATE_UINT16(env.fpuc, X86CPU),
+ VMSTATE_UINT16(env.fpus_vmstate, X86CPU),
+ VMSTATE_UINT16(env.fptag_vmstate, X86CPU),
+ VMSTATE_UINT16(env.fpregs_format_vmstate, X86CPU),
+
+ VMSTATE_STRUCT_ARRAY(env.fpregs, X86CPU, 8, 0, vmstate_fpreg, FPReg),
+
+ VMSTATE_SEGMENT32_ARRAY(env.segs32, X86CPU, 6),
+ VMSTATE_SEGMENT32(env.ldt32, X86CPU),
+ VMSTATE_SEGMENT32(env.tr32, X86CPU),
+ VMSTATE_SEGMENT32(env.gdt32, X86CPU),
+ VMSTATE_SEGMENT32(env.idt32, X86CPU),
+
+ VMSTATE_UINT32(env.sysenter_cs, X86CPU),
+ VMSTATE_UINT32(env.sysenter_esp32, X86CPU),
+ VMSTATE_UINT32(env.sysenter_eip32, X86CPU),
+
+ VMSTATE_UINT32(env.cr32[0], X86CPU),
+ VMSTATE_UINT32(env.cr32[2], X86CPU),
+ VMSTATE_UINT32(env.cr32[3], X86CPU),
+ VMSTATE_UINT32(env.cr32[4], X86CPU),
+ VMSTATE_UINT32_ARRAY(env.dr32, X86CPU, 8),
+ /* MMU */
+ VMSTATE_INT32(env.a20_mask, X86CPU),
+ /* XMM */
+ VMSTATE_UINT32(env.mxcsr, X86CPU),
+ VMSTATE_XMM32_REGS(env.xmm_regs, X86CPU, 0),
+
+ VMSTATE_UINT32(env.smbase, X86CPU),
+
+ VMSTATE_UINT64(env.pat, X86CPU),
+ VMSTATE_UINT32(env.hflags2, X86CPU),
+
+ VMSTATE_UINT64(env.vm_hsave, X86CPU),
+ VMSTATE_UINT64(env.vm_vmcb, X86CPU),
+ VMSTATE_UINT64(env.tsc_offset, X86CPU),
+ VMSTATE_UINT64(env.intercept, X86CPU),
+ VMSTATE_UINT16(env.intercept_cr_read, X86CPU),
+ VMSTATE_UINT16(env.intercept_cr_write, X86CPU),
+ VMSTATE_UINT16(env.intercept_dr_read, X86CPU),
+ VMSTATE_UINT16(env.intercept_dr_write, X86CPU),
+ VMSTATE_UINT32(env.intercept_exceptions, X86CPU),
+ VMSTATE_UINT8(env.v_tpr, X86CPU),
+ /* MTRRs */
+ VMSTATE_UINT64_ARRAY(env.mtrr_fixed, X86CPU, 11),
+ VMSTATE_UINT64(env.mtrr_deftype, X86CPU),
+ VMSTATE_MTRR_VARS(env.mtrr_var, X86CPU, MSR_MTRRcap_VCNT, 8),
+ /* KVM-related states */
+ VMSTATE_INT32(env.interrupt_injected, X86CPU),
+ VMSTATE_UINT32(env.mp_state, X86CPU),
+ VMSTATE_UINT64(env.tsc, X86CPU),
+ VMSTATE_INT32(env.exception_nr, X86CPU),
+ VMSTATE_UINT8(env.soft_interrupt, X86CPU),
+ VMSTATE_UINT8(env.nmi_injected, X86CPU),
+ VMSTATE_UINT8(env.nmi_pending, X86CPU),
+ VMSTATE_UINT8(env.has_error_code, X86CPU),
+ VMSTATE_UINT32(env.sipi_vector, X86CPU),
+ /* MCE */
+ VMSTATE_UINT64(env.mcg_cap, X86CPU),
+ VMSTATE_UINT64(env.mcg_status, X86CPU),
+ VMSTATE_UINT64(env.mcg_ctl, X86CPU),
+ VMSTATE_UINT64_ARRAY(env.mce_banks, X86CPU, MCE_BANKS_DEF * 4),
+ /* rdtscp */
+ VMSTATE_UINT64(env.tsc_aux, X86CPU),
+ /* KVM pvclock msr */
+ VMSTATE_UINT64(env.system_time_msr, X86CPU),
+ VMSTATE_UINT64(env.wall_clock_msr, X86CPU),
+ /* XSAVE related fields */
+ VMSTATE_UINT64_V(env.xcr0, X86CPU, 12),
+ VMSTATE_UINT64_V(env.xstate_bv, X86CPU, 12),
+ VMSTATE_YMMH32_REGS_VARS(env.xmm_regs, X86CPU, 0, 12),
+ VMSTATE_END_OF_LIST()
+ /* The above list is not sorted /wrt version numbers, watch out! */
+ },
+ .subsections = (const VMStateDescription * const []) {
+ &vmstate_exception_info,
+ &vmstate_error_code,
+ &vmstate_async_pf_msr,
+ &vmstate_async_pf_int_msr,
+ &vmstate_pv_eoi_msr,
+ &vmstate_steal_time_msr,
+ &vmstate_poll_control_msr,
+ &vmstate_fpop_ip_dp,
+ &vmstate_msr_tsc_adjust,
+ &vmstate_msr_tscdeadline,
+ &vmstate_msr_ia32_misc_enable,
+ &vmstate_msr_ia32_feature_control,
+ &vmstate_msr_architectural_pmu,
+ &vmstate_mpx,
+ &vmstate_msr_hyperv_hypercall,
+ &vmstate_msr_hyperv_vapic,
+ &vmstate_msr_hyperv_time,
+ &vmstate_msr_hyperv_crash,
+ &vmstate_msr_hyperv_runtime,
+ &vmstate_msr_hyperv_synic,
+ &vmstate_msr_hyperv_stimer,
+ &vmstate_msr_hyperv_reenlightenment,
+ &vmstate_avx512,
+ &vmstate_xss,
+ &vmstate_umwait,
+ &vmstate_tsc_khz,
+ &vmstate_msr_smi_count,
+ &vmstate_pkru,
+ &vmstate_pkrs,
+ &vmstate_spec_ctrl,
+ &amd_tsc_scale_msr_ctrl,
+ &vmstate_mcg_ext_ctl,
+ &vmstate_msr_intel_pt,
+ &vmstate_msr_virt_ssbd,
+ &vmstate_svm_npt,
+ &vmstate_svm_guest,
+#ifdef CONFIG_KVM
+ &vmstate_nested_state,
+ &vmstate_xen_vcpu,
+#endif
+ &vmstate_msr_tsx_ctrl,
+ &vmstate_msr_intel_sgx,
+ &vmstate_pdptrs,
+ &vmstate_msr_xfd,
+ &vmstate_msr_hwcr,
+ &vmstate_arch_lbr,
+ &vmstate_triple_fault,
+ &vmstate_pl0_ssp,
+ &vmstate_cet,
+
+ NULL
+ }
+};
+
+#endif /* TARGET_X86_64 */
--
2.53.0
next prev parent reply other threads:[~2026-04-02 9:53 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-02 9:51 [PATCH for-11.1 00/10] Deprecate the qemu-system-i386 binary Thomas Huth
2026-04-02 9:51 ` [PATCH 01/10] target/i386/tcg/sysemu: Move target specific SMM code to separate functions Thomas Huth
2026-04-02 9:51 ` [PATCH 02/10] target/i386/tcg/sysemu: Allow 32-bit SMM code to be used in the 64-bit binary Thomas Huth
2026-04-02 9:51 ` [PATCH 03/10] target-info: Add functions for querying whether the target is i386 or x86_64 Thomas Huth
2026-04-02 9:51 ` [PATCH 04/10] cpu: Add a way to detect 32-bit mode from argv0 Thomas Huth
2026-04-02 9:51 ` [PATCH 05/10] target/i386/cpu: Allow to limit the 64-bit binary to 32-bit mode only Thomas Huth
2026-04-02 9:51 ` [PATCH 06/10] target/i386: Select a 32-bit/64-bit default CPU during runtime Thomas Huth
2026-04-02 9:51 ` [PATCH 07/10] target/i386: Adjust the suffix of the CPU devices to 32-bit/64-bit mode Thomas Huth
2026-04-02 9:51 ` [PATCH 08/10] hw/i386/isapc: Adjust the check for valid CPUs in the isapc machine Thomas Huth
2026-04-02 9:51 ` Thomas Huth [this message]
2026-04-02 9:51 ` [PATCH 10/10] docs/about/deprecated: Deprecate the qemu-system-i386 binary Thomas Huth
2026-04-02 10:06 ` Daniel P. Berrangé
2026-04-02 10:11 ` Thomas Huth
2026-04-02 10:41 ` Daniel P. Berrangé
2026-04-02 10:44 ` Daniel P. Berrangé
2026-04-02 11:20 ` [PATCH for-11.1 00/10] " Peter Maydell
2026-04-02 13:02 ` Thomas Huth
2026-04-02 16:37 ` Daniel P. Berrangé
2026-04-02 16:44 ` Thomas Huth
2026-04-06 4:47 ` Pierrick Bouvier
2026-04-08 7:20 ` Thomas Huth
2026-04-08 15:42 ` Pierrick Bouvier
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260402095132.29245-10-thuth@redhat.com \
--to=thuth@redhat.com \
--cc=mst@redhat.com \
--cc=pbonzini@redhat.com \
--cc=philmd@linaro.org \
--cc=pierrick.bouvier@linaro.org \
--cc=qemu-devel@nongnu.org \
--cc=richard.henderson@linaro.org \
--cc=zhao1.liu@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.