* [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25
@ 2026-06-26 10:17 Paolo Bonzini
2026-06-26 10:17 ` [PULL 01/45] accel/mshv: use mshv_create_partition_v2 payload Paolo Bonzini
` (45 more replies)
0 siblings, 46 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel
The following changes since commit b83371668192a705b878e909c5ae9c1233cbd5fb:
Merge tag 'pbouvier/pr/plugins-20260618' of https://gitlab.com/p-b-o/qemu into staging (2026-06-19 15:00:01 -0400)
are available in the Git repository at:
https://gitlab.com/bonzini/qemu.git tags/for-upstream
for you to fetch changes up to db13fe80ef455f9e2be60c1bdc6be67a6b82d24f:
i386/tdx: Add CPUID_24_0_EBX_AVX10_VL_MASK as supported (2026-06-26 00:48:23 +0200)
----------------------------------------------------------------
* target/i386/mshv: CPU model support
* target/i386/mshv: first part of migration support
* target/i386/mshv: faster register access for MMIO exits
* target/i386/tdx: add support for AMX alias bits in CPUID and AVX10
* Deprecate memory-encryption in favor of confidential-guest-support
----------------------------------------------------------------
Doru Blânzeanu (4):
include/hw/hyperv: add hv_vp_register_page struct definition
target/i386/mshv: hv_vp_register_page setup for the vcpu
target/i386/mshv: use the register page to get registers
target/i386/mshv: use the register page to set registers
Magnus Kulke (32):
accel/mshv: use mshv_create_partition_v2 payload
target/i386/mshv: fix cpuid propagation bug
target/i386/mshv: fix various cpuid traversal bugs
target/i386/mshv: change cpuid mask to UINT32_MAX
target/i386/mshv: set cpu model name on -cpu host
target/i386: query mshv accel for supported cpuids
target/i386/mshv: populate xsave area offsets
target/i386/mshv: use hv-provided [0xD,1+2].EBX
accel/mshv: disable la57 (5lvl paging)
target/i386/mshv: use arch_load/store_reg fns
target/i386/mshv: use generic FPU/xcr0 state
target/i386/mshv: impl init/load/store_vcpu_state
accel/accel-irq: add AccelRouteChange abstraction
accel/accel-irq: add generic begin_route_changes
accel/accel-irq: add generic commit_route_changes
accel/mshv: add irq_routes to state
accel/mshv: update s->irq_routes in add_msi_route
accel/mshv: update s->irq_routes in update_msi_route
accel/mshv: update s->irq_routes in release_virq
accel/mshv: use s->irq_routes in commit_routes
accel/mshv: reserve ioapic routes on s->irq_routes
accel/mshv: remove redundant msi controller
accel/mshv: store partition proc features
target/i386/mshv: expose mshv_get_generic_regs
accel/mshv: enable dirty page tracking
target/i386/mshv: move msr code to arch
target/i386/mshv: migrate pending ints/excs
target/i386/mshv: migrate XSAVE state
target/i386/mshv: reconstruct hflags after load
target/i386/mshv: migrate MSRs
target/i386/mshv: migrate MTRR MSRs
target/i386/mshv: migrate CET/SS MSRs
Paolo Bonzini (1):
accel: remove unnecessary #ifdefs
Xiaoyao Li (8):
i386/sev: Remove the example that references memory-encryption
qemu-options: Change memory-encryption to confidential-guest-support in the example
qemu-options: Add confidential-guest-support to machine options
qemu-options: Add description of tdx-guest object
machine: Deprecate memory-encryption
i386/tdx: Use .has_gpa field to check if the gpa is valid
i386/tdx: Make AMX alias bits supported
i386/tdx: Add CPUID_24_0_EBX_AVX10_VL_MASK as supported
docs/about/deprecated.rst | 7 +
include/accel/accel-route.h | 17 +
include/hw/hyperv/hvgdk_mini.h | 133 +++++
include/hw/hyperv/hvhdk.h | 195 +++++++
include/system/accel-irq.h | 6 +-
include/system/kvm.h | 23 +-
include/system/mshv.h | 18 +-
include/system/mshv_int.h | 72 +--
target/i386/cpu.h | 9 +-
accel/accel-irq.c | 51 +-
accel/kvm/kvm-all.c | 6 +-
accel/mshv/irq.c | 362 ++++++-------
accel/mshv/mem.c | 211 ++++++++
accel/mshv/mshv-all.c | 161 +++++-
accel/mshv/msr.c | 375 -------------
accel/stubs/kvm-stub.c | 2 +-
accel/stubs/mshv-stub.c | 7 +-
hw/core/machine.c | 2 +
hw/misc/ivshmem-pci.c | 8 +-
hw/vfio/pci.c | 11 +-
hw/virtio/virtio-pci.c | 3 +-
system/runstate.c | 2 +-
target/i386/cpu.c | 8 +
target/i386/kvm/kvm.c | 5 +-
target/i386/kvm/tdx.c | 28 +-
target/i386/mshv/mshv-cpu.c | 1143 ++++++++++++++++++++++++++++++----------
target/i386/mshv/msr.c | 425 +++++++++++++++
target/i386/sev.c | 4 -
accel/mshv/meson.build | 1 -
qemu-options.hx | 52 +-
target/i386/meson.build | 2 +-
target/i386/mshv/meson.build | 1 +
32 files changed, 2353 insertions(+), 997 deletions(-)
create mode 100644 include/accel/accel-route.h
delete mode 100644 accel/mshv/msr.c
create mode 100644 target/i386/mshv/msr.c
--
2.54.0
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PULL 01/45] accel/mshv: use mshv_create_partition_v2 payload
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 02/45] target/i386/mshv: fix cpuid propagation bug Paolo Bonzini
` (44 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel
Cc: Magnus Kulke, Wei Liu, Anirudh Rayabharam (Microsoft),
Doru Blânzeanu
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
When using the extended request format we can instruct the hypervisor to
provision enough space for requested XSAVE features. This is required
for supporting QEMU models provided via the -cpu flag properly.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Acked-by: Wei Liu <wei.liu@kernel.org>
Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
Reviewed-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260416121116.527927-2-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/hw/hyperv/hvgdk_mini.h | 2 +
include/hw/hyperv/hvhdk.h | 195 +++++++++++++++++++++++++++++++++
accel/mshv/mshv-all.c | 71 ++++++++++--
3 files changed, 261 insertions(+), 7 deletions(-)
diff --git a/include/hw/hyperv/hvgdk_mini.h b/include/hw/hyperv/hvgdk_mini.h
index c3a8f33280a..367519143e2 100644
--- a/include/hw/hyperv/hvgdk_mini.h
+++ b/include/hw/hyperv/hvgdk_mini.h
@@ -454,6 +454,8 @@ typedef struct hv_input_set_vp_registers {
struct hv_register_assoc elements[];
} QEMU_PACKED hv_input_set_vp_registers;
+#define MSHV_VP_MAX_REGISTERS 128
+
union hv_interrupt_control {
uint64_t as_uint64;
struct {
diff --git a/include/hw/hyperv/hvhdk.h b/include/hw/hyperv/hvhdk.h
index 41af7438471..9ad16c47da9 100644
--- a/include/hw/hyperv/hvhdk.h
+++ b/include/hw/hyperv/hvhdk.h
@@ -11,6 +11,16 @@
#define HV_PARTITION_SYNTHETIC_PROCESSOR_FEATURES_BANKS 1
+struct hv_input_get_partition_property {
+ uint64_t partition_id;
+ uint32_t property_code; /* enum hv_partition_property_code */
+ uint32_t padding;
+} QEMU_PACKED;
+
+struct hv_output_get_partition_property {
+ uint64_t property_value;
+} QEMU_PACKED;
+
struct hv_input_set_partition_property {
uint64_t partition_id;
uint32_t property_code; /* enum hv_partition_property_code */
@@ -161,6 +171,191 @@ union hv_partition_synthetic_processor_features {
};
};
+union hv_partition_processor_xsave_features {
+ struct {
+ uint64_t xsave_support:1;
+ uint64_t xsaveopt_support:1;
+ uint64_t avx_support:1;
+ uint64_t avx2_support:1;
+ uint64_t fma_support:1;
+ uint64_t mpx_support:1;
+ uint64_t avx512_support:1;
+ uint64_t avx512_dq_support:1;
+ uint64_t avx512_cd_support:1;
+ uint64_t avx512_bw_support:1;
+ uint64_t avx512_vl_support:1;
+ uint64_t xsave_comp_support:1;
+ uint64_t xsave_supervisor_support:1;
+ uint64_t xcr1_support:1;
+ uint64_t avx512_bitalg_support:1;
+ uint64_t avx512_i_fma_support:1;
+ uint64_t avx512_v_bmi_support:1;
+ uint64_t avx512_v_bmi2_support:1;
+ uint64_t avx512_vnni_support:1;
+ uint64_t gfni_support:1;
+ uint64_t vaes_support:1;
+ uint64_t avx512_v_popcntdq_support:1;
+ uint64_t vpclmulqdq_support:1;
+ uint64_t avx512_bf16_support:1;
+ uint64_t avx512_vp2_intersect_support:1;
+ uint64_t avx512_fp16_support:1;
+ uint64_t xfd_support:1;
+ uint64_t amx_tile_support:1;
+ uint64_t amx_bf16_support:1;
+ uint64_t amx_int8_support:1;
+ uint64_t avx_vnni_support:1;
+ uint64_t avx_ifma_support:1;
+ uint64_t avx_ne_convert_support:1;
+ uint64_t avx_vnni_int8_support:1;
+ uint64_t avx_vnni_int16_support:1;
+ uint64_t avx10_1_256_support:1;
+ uint64_t avx10_1_512_support:1;
+ uint64_t amx_fp16_support:1;
+ uint64_t reserved1:26;
+ };
+ uint64_t as_uint64;
+};
+
+#define HV_PARTITION_PROCESSOR_FEATURES_BANKS 2
+#define HV_PARTITION_PROCESSOR_FEATURES_RESERVEDBANK1_BITFIELD_COUNT 4
+
+
+union hv_partition_processor_features {
+ uint64_t as_uint64[HV_PARTITION_PROCESSOR_FEATURES_BANKS];
+ struct {
+ uint64_t sse3_support:1;
+ uint64_t lahf_sahf_support:1;
+ uint64_t ssse3_support:1;
+ uint64_t sse4_1_support:1;
+ uint64_t sse4_2_support:1;
+ uint64_t sse4a_support:1;
+ uint64_t xop_support:1;
+ uint64_t pop_cnt_support:1;
+ uint64_t cmpxchg16b_support:1;
+ uint64_t altmovcr8_support:1;
+ uint64_t lzcnt_support:1;
+ uint64_t mis_align_sse_support:1;
+ uint64_t mmx_ext_support:1;
+ uint64_t amd3dnow_support:1;
+ uint64_t extended_amd3dnow_support:1;
+ uint64_t page_1gb_support:1;
+ uint64_t aes_support:1;
+ uint64_t pclmulqdq_support:1;
+ uint64_t pcid_support:1;
+ uint64_t fma4_support:1;
+ uint64_t f16c_support:1;
+ uint64_t rd_rand_support:1;
+ uint64_t rd_wr_fs_gs_support:1;
+ uint64_t smep_support:1;
+ uint64_t enhanced_fast_string_support:1;
+ uint64_t bmi1_support:1;
+ uint64_t bmi2_support:1;
+ uint64_t hle_support_deprecated:1;
+ uint64_t rtm_support_deprecated:1;
+ uint64_t movbe_support:1;
+ uint64_t npiep1_support:1;
+ uint64_t dep_x87_fpu_save_support:1;
+ uint64_t rd_seed_support:1;
+ uint64_t adx_support:1;
+ uint64_t intel_prefetch_support:1;
+ uint64_t smap_support:1;
+ uint64_t hle_support:1;
+ uint64_t rtm_support:1;
+ uint64_t rdtscp_support:1;
+ uint64_t clflushopt_support:1;
+ uint64_t clwb_support:1;
+ uint64_t sha_support:1;
+ uint64_t x87_pointers_saved_support:1;
+ uint64_t invpcid_support:1;
+ uint64_t ibrs_support:1;
+ uint64_t stibp_support:1;
+ uint64_t ibpb_support:1;
+ uint64_t unrestricted_guest_support:1;
+ uint64_t mdd_support:1;
+ uint64_t fast_short_rep_mov_support:1;
+ uint64_t l1dcache_flush_support:1;
+ uint64_t rdcl_no_support:1;
+ uint64_t ibrs_all_support:1;
+ uint64_t skip_l1df_support:1;
+ uint64_t ssb_no_support:1;
+ uint64_t rsb_a_no_support:1;
+ uint64_t virt_spec_ctrl_support:1;
+ uint64_t rd_pid_support:1;
+ uint64_t umip_support:1;
+ uint64_t mbs_no_support:1;
+ uint64_t mb_clear_support:1;
+ uint64_t taa_no_support:1;
+ uint64_t tsx_ctrl_support:1;
+ uint64_t reserved_bank0:1;
+
+ /* N.B. Begin bank 1 processor features. */
+ uint64_t a_count_m_count_support:1;
+ uint64_t tsc_invariant_support:1;
+ uint64_t cl_zero_support:1;
+ uint64_t rdpru_support:1;
+ uint64_t la57_support:1;
+ uint64_t mbec_support:1;
+ uint64_t nested_virt_support:1;
+ uint64_t psfd_support:1;
+ uint64_t cet_ss_support:1;
+ uint64_t cet_ibt_support:1;
+ uint64_t vmx_exception_inject_support:1;
+ uint64_t enqcmd_support:1;
+ uint64_t umwait_tpause_support:1;
+ uint64_t movdiri_support:1;
+ uint64_t movdir64b_support:1;
+ uint64_t cldemote_support:1;
+ uint64_t serialize_support:1;
+ uint64_t tsc_deadline_tmr_support:1;
+ uint64_t tsc_adjust_support:1;
+ uint64_t fzl_rep_movsb:1;
+ uint64_t fs_rep_stosb:1;
+ uint64_t fs_rep_cmpsb:1;
+ uint64_t tsx_ld_trk_support:1;
+ uint64_t vmx_ins_outs_exit_info_support:1;
+ uint64_t hlat_support:1;
+ uint64_t sbdr_ssdp_no_support:1;
+ uint64_t fbsdp_no_support:1;
+ uint64_t psdp_no_support:1;
+ uint64_t fb_clear_support:1;
+ uint64_t btc_no_support:1;
+ uint64_t ibpb_rsb_flush_support:1;
+ uint64_t stibp_always_on_support:1;
+ uint64_t perf_global_ctrl_support:1;
+ uint64_t npt_execute_only_support:1;
+ uint64_t npt_ad_flags_support:1;
+ uint64_t npt1_gb_page_support:1;
+ uint64_t amd_processor_topology_node_id_support:1;
+ uint64_t local_machine_check_support:1;
+ uint64_t extended_topology_leaf_fp256_amd_support:1;
+ uint64_t gds_no_support:1;
+ uint64_t cmpccxadd_support:1;
+ uint64_t tsc_aux_virtualization_support:1;
+ uint64_t rmp_query_support:1;
+ uint64_t bhi_no_support:1;
+ uint64_t bhi_dis_support:1;
+ uint64_t prefetch_i_support:1;
+ uint64_t sha512_support:1;
+ uint64_t mitigation_ctrl_support:1;
+ uint64_t rfds_no_support:1;
+ uint64_t rfds_clear_support:1;
+ uint64_t sm3_support:1;
+ uint64_t sm4_support:1;
+ uint64_t secure_avic_support:1;
+ uint64_t guest_intercept_ctrl_support:1;
+ uint64_t sbpb_supported:1;
+ uint64_t ibpb_br_type_supported:1;
+ uint64_t srso_no_supported:1;
+ uint64_t srso_user_kernel_no_supported:1;
+ uint64_t vrew_clear_supported:1;
+ uint64_t tsa_l1_no_supported:1;
+ uint64_t tsa_sq_no_supported:1;
+ uint64_t lass_support:1;
+ uint64_t idle_hlt_intercept_support:1;
+ uint64_t msr_list_support:1;
+ };
+};
+
enum hv_translate_gva_result_code {
HV_TRANSLATE_GVA_SUCCESS = 0,
diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
index 58d8638c0c1..0f65b65d9ca 100644
--- a/accel/mshv/mshv-all.c
+++ b/accel/mshv/mshv-all.c
@@ -110,21 +110,78 @@ static int resume_vm(int vm_fd)
return 0;
}
+static int get_host_partition_property(int mshv_fd, uint32_t property_code,
+ uint64_t *value)
+{
+ int ret;
+ struct hv_input_get_partition_property in = {0};
+ struct hv_output_get_partition_property out = {0};
+ struct mshv_root_hvcall args = {0};
+
+ in.property_code = property_code;
+
+ args.code = HVCALL_GET_PARTITION_PROPERTY;
+ args.in_sz = sizeof(in);
+ args.in_ptr = (uint64_t)∈
+ args.out_sz = sizeof(out);
+ args.out_ptr = (uint64_t)&out;
+
+ ret = ioctl(mshv_fd, MSHV_ROOT_HVCALL, &args);
+ if (ret < 0) {
+ error_report("Failed to get host partition property bank: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ *value = out.property_value;
+ return 0;
+}
+
static int create_partition(int mshv_fd, int *vm_fd)
{
int ret;
- struct mshv_create_partition args = {0};
+ uint64_t pt_flags, host_proc_features;
+ union hv_partition_processor_xsave_features disabled_xsave_features;
+ struct mshv_create_partition_v2 args = {0};
+
+ QEMU_BUILD_BUG_ON(MSHV_NUM_CPU_FEATURES_BANKS != 2);
/* Initialize pt_flags with the desired features */
- uint64_t pt_flags = (1ULL << MSHV_PT_BIT_LAPIC) |
- (1ULL << MSHV_PT_BIT_X2APIC) |
- (1ULL << MSHV_PT_BIT_GPA_SUPER_PAGES);
+ pt_flags = (1ULL << MSHV_PT_BIT_LAPIC) |
+ (1ULL << MSHV_PT_BIT_X2APIC) |
+ (1ULL << MSHV_PT_BIT_GPA_SUPER_PAGES) |
+ (1ULL << MSHV_PT_BIT_CPU_AND_XSAVE_FEATURES);
- /* Set default isolation type */
- uint64_t pt_isolation = MSHV_PT_ISOLATION_NONE;
+ /* enable all */
+ disabled_xsave_features.as_uint64 = 0;
+ /*
+ * query host for supported processor features and disable unsupported
+ * features: (0 means supported, 1 means disabled, hence the negation)
+ */
+ ret = get_host_partition_property(mshv_fd,
+ HV_PARTITION_PROPERTY_PROCESSOR_FEATURES0,
+ &host_proc_features);
+ if (ret < 0) {
+ error_report("Failed to get host processor feature bank 0");
+ return -1;
+ }
+ args.pt_cpu_fbanks[0] = ~host_proc_features;
+
+ ret = get_host_partition_property(mshv_fd,
+ HV_PARTITION_PROPERTY_PROCESSOR_FEATURES1,
+ &host_proc_features);
+ if (ret < 0) {
+ error_report("Failed to get host processor feature bank 1");
+ return -1;
+ }
+ args.pt_cpu_fbanks[1] = ~host_proc_features;
+
+ /* populate args structure */
args.pt_flags = pt_flags;
- args.pt_isolation = pt_isolation;
+ args.pt_isolation = MSHV_PT_ISOLATION_NONE;
+ args.pt_disabled_xsave = disabled_xsave_features.as_uint64;
+ args.pt_num_cpu_fbanks = MSHV_NUM_CPU_FEATURES_BANKS;
ret = ioctl(mshv_fd, MSHV_CREATE_PARTITION, &args);
if (ret < 0) {
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 02/45] target/i386/mshv: fix cpuid propagation bug
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
2026-06-26 10:17 ` [PULL 01/45] accel/mshv: use mshv_create_partition_v2 payload Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 03/45] target/i386/mshv: fix various cpuid traversal bugs Paolo Bonzini
` (43 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel
Cc: Magnus Kulke, Philippe Mathieu-Daudé,
Anirudh Rayabharam (Microsoft), Doru Blânzeanu
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
The list argument was missing a pointer and hence e.g. -cpu qemu64 and
-cpu EPYC would produce the same of cpuid leaves.
Fixes: 4fa04dd1621 ("target/i386: Register CPUID entries with MSHV")
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
Reviewed-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260416121116.527927-3-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
target/i386/mshv/mshv-cpu.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 4ed6e7548f6..38994ed6cfd 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -441,7 +441,7 @@ int mshv_load_regs(CPUState *cpu)
return 0;
}
-static void add_cpuid_entry(GList *cpuid_entries,
+static void add_cpuid_entry(GList **cpuid_entries,
uint32_t function, uint32_t index,
uint32_t eax, uint32_t ebx,
uint32_t ecx, uint32_t edx)
@@ -456,10 +456,10 @@ static void add_cpuid_entry(GList *cpuid_entries,
entry->ecx = ecx;
entry->edx = edx;
- cpuid_entries = g_list_append(cpuid_entries, entry);
+ *cpuid_entries = g_list_append(*cpuid_entries, entry);
}
-static void collect_cpuid_entries(const CPUState *cpu, GList *cpuid_entries)
+static void collect_cpuid_entries(const CPUState *cpu, GList **cpuid_entries)
{
X86CPU *x86_cpu = X86_CPU(cpu);
CPUX86State *env = &x86_cpu->env;
@@ -615,7 +615,7 @@ static int set_cpuid2(const CPUState *cpu)
struct hv_cpuid_entry *entry;
GList *entries = NULL;
- collect_cpuid_entries(cpu, entries);
+ collect_cpuid_entries(cpu, &entries);
n_entries = g_list_length(entries);
cpuid_size = sizeof(struct hv_cpuid)
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 03/45] target/i386/mshv: fix various cpuid traversal bugs
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
2026-06-26 10:17 ` [PULL 01/45] accel/mshv: use mshv_create_partition_v2 payload Paolo Bonzini
2026-06-26 10:17 ` [PULL 02/45] target/i386/mshv: fix cpuid propagation bug Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 04/45] target/i386/mshv: change cpuid mask to UINT32_MAX Paolo Bonzini
` (42 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke, Wei Liu, Doru Blânzeanu
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
- Hardcoded max_leaf was not accurate. We query leaf 0x0 and 0x80000000 to
get the actual max leaves
- On all 0 zeroes on leaf 0x0d, we register 0-63 subleaves with zeros
indicating XSAVE is disabled
- Subleaf 0 was hardcoded, so the Hypervisor returned defaults for other
subleaves
- Subleaf 0 was hardcoded, so we were passing 0 instead of actual subleaf
when adding entries. We now pass the correct subleaf value to
add_cpuid_entry()
- Leaves 0x04,0x07,0d,0f,10 weren't marked as subleaf-specific
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Acked-by: Wei Liu <wei.liu@kernel.org>
Reviewed-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260416121116.527927-4-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
target/i386/mshv/mshv-cpu.c | 88 +++++++++++++++++++++++++------------
1 file changed, 61 insertions(+), 27 deletions(-)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 38994ed6cfd..bfcccd19916 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -465,14 +465,17 @@ static void collect_cpuid_entries(const CPUState *cpu, GList **cpuid_entries)
CPUX86State *env = &x86_cpu->env;
uint32_t eax, ebx, ecx, edx;
uint32_t leaf, subleaf;
- size_t max_leaf = 0x1F;
- size_t max_subleaf = 0x20;
-
- uint32_t leaves_with_subleaves[] = {0x4, 0x7, 0xD, 0xF, 0x10};
+ uint32_t max_basic_leaf, max_extended_leaf;
+ uint32_t max_subleaf = 0x20;
+ uint32_t leaves_with_subleaves[] = {0x04, 0x07, 0x0d, 0x0f, 0x10};
int n_subleaf_leaves = ARRAY_SIZE(leaves_with_subleaves);
- /* Regular leaves without subleaves */
- for (leaf = 0; leaf <= max_leaf; leaf++) {
+ /* Get maximum basic and and extended CPUID leaves */
+ cpu_x86_cpuid(env, 0, 0, &max_basic_leaf, &ebx, &ecx, &edx);
+ cpu_x86_cpuid(env, 0x80000000, 0, &max_extended_leaf, &ebx, &ecx, &edx);
+
+ /* Collect basic leaves (0x0 to max_basic_leaf) */
+ for (leaf = 0; leaf <= max_basic_leaf; leaf++) {
bool has_subleaves = false;
for (int i = 0; i < n_subleaf_leaves; i++) {
if (leaf == leaves_with_subleaves[i]) {
@@ -483,27 +486,40 @@ static void collect_cpuid_entries(const CPUState *cpu, GList **cpuid_entries)
if (!has_subleaves) {
cpu_x86_cpuid(env, leaf, 0, &eax, &ebx, &ecx, &edx);
- if (eax == 0 && ebx == 0 && ecx == 0 && edx == 0) {
- /* all zeroes indicates no more leaves */
- continue;
- }
-
add_cpuid_entry(cpuid_entries, leaf, 0, eax, ebx, ecx, edx);
continue;
}
+ /*
+ * Valid XSAVE components can exist at a higher index se we need to set
+ * all subleaves for leaf 0x0d, even if we encounter an empty one.
+ */
+ if (leaf == 0x0d) {
+ for (subleaf = 0; subleaf <= 63; subleaf++) {
+ cpu_x86_cpuid(env, leaf, subleaf, &eax, &ebx, &ecx, &edx);
+ add_cpuid_entry(cpuid_entries, leaf, subleaf,
+ eax, ebx, ecx, edx);
+ }
+ continue;
+ }
+
subleaf = 0;
while (subleaf < max_subleaf) {
cpu_x86_cpuid(env, leaf, subleaf, &eax, &ebx, &ecx, &edx);
if (eax == 0 && ebx == 0 && ecx == 0 && edx == 0) {
- /* all zeroes indicates no more leaves */
break;
}
- add_cpuid_entry(cpuid_entries, leaf, 0, eax, ebx, ecx, edx);
+ add_cpuid_entry(cpuid_entries, leaf, subleaf, eax, ebx, ecx, edx);
subleaf++;
}
}
+
+ /* Collect extended leaves (0x80000000 to max_extended_leaf) */
+ for (leaf = 0x80000000; leaf <= max_extended_leaf; leaf++) {
+ cpu_x86_cpuid(env, leaf, 0, &eax, &ebx, &ecx, &edx);
+ add_cpuid_entry(cpuid_entries, leaf, 0, eax, ebx, ecx, edx);
+ }
}
static int register_intercept_result_cpuid_entry(const CPUState *cpu,
@@ -576,22 +592,40 @@ static int register_intercept_result_cpuid(const CPUState *cpu,
subleaf_specific = 0;
always_override = 1;
- /* Intel */
- /* 0xb - Extended Topology Enumeration Leaf */
- /* 0x1f - V2 Extended Topology Enumeration Leaf */
- /* AMD */
- /* 0x8000_001e - Processor Topology Information */
- /* 0x8000_0026 - Extended CPU Topology */
- if (entry->function == 0xb
- || entry->function == 0x1f
- || entry->function == 0x8000001e
- || entry->function == 0x80000026) {
+ /*
+ * Intel
+ * 0xb - Extended Topology Enumeration Leaf
+ * 0x1f - V2 Extended Topology Enumeration Leaf
+ * AMD
+ * 0x8000_001e - Processor Topology Information
+ * 0x8000_0026 - Extended CPU Topology
+ */
+ if (entry->function == 0xb ||
+ entry->function == 0x1f ||
+ entry->function == 0x8000001e ||
+ entry->function == 0x80000026) {
subleaf_specific = 1;
always_override = 1;
- } else if (entry->function == 0x00000001
- || entry->function == 0x80000000
- || entry->function == 0x80000001
- || entry->function == 0x80000008) {
+ /*
+ * Feature enumeration leaves (subleaf-specific)
+ * 0x04: Deterministic Cache Parameters
+ * 0x07: Structured Extended Feature Flags
+ * 0x0D: Processor Extended State Enumeration
+ * 0x0F: Platform QoS Monitoring
+ * 0x10: Platform QoS Enforcement
+ */
+ } else if (entry->function == 0x04 ||
+ entry->function == 0x07 ||
+ entry->function == 0x0d ||
+ entry->function == 0x0f ||
+ entry->function == 0x10) {
+ subleaf_specific = 1;
+ always_override = 1;
+ /* Basic feature leaves (no subleaves) */
+ } else if (entry->function == 0x00000001 ||
+ entry->function == 0x80000000 ||
+ entry->function == 0x80000001 ||
+ entry->function == 0x80000008) {
subleaf_specific = 0;
always_override = 1;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 04/45] target/i386/mshv: change cpuid mask to UINT32_MAX
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (2 preceding siblings ...)
2026-06-26 10:17 ` [PULL 03/45] target/i386/mshv: fix various cpuid traversal bugs Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 05/45] target/i386/mshv: set cpu model name on -cpu host Paolo Bonzini
` (41 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke, Wei Liu, Doru Blânzeanu
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
The current implementation would only override the feature bits that are
enabled, however we also want to consider disabled features, hence all
bits are set on the masks in the hypercall argument.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Acked-by: Wei Liu <wei.liu@kernel.org>
Reviewed-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260416121116.527927-5-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
target/i386/mshv/mshv-cpu.c | 17 ++++++-----------
1 file changed, 6 insertions(+), 11 deletions(-)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index bfcccd19916..2ee46bd7025 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -538,22 +538,17 @@ static int register_intercept_result_cpuid_entry(const CPUState *cpu,
.input.always_override = always_override,
.input.padding = 0,
/*
- * With regard to masks - these are to specify bits to be overwritten
- * The current CpuidEntry structure wouldn't allow to carry the masks
- * in addition to the actual register values. For this reason, the
- * masks are set to the exact values of the corresponding register bits
- * to be registered for an overwrite. To view resulting values the
- * hypervisor would return, HvCallGetVpCpuidValues hypercall can be
- * used.
+ * Masks specify which bits to override. Set to 0xFFFFFFFF to
+ * override all bits with the values from the QEMU CPU model.
*/
.result.eax = entry->eax,
- .result.eax_mask = entry->eax,
+ .result.eax_mask = 0xFFFFFFFF,
.result.ebx = entry->ebx,
- .result.ebx_mask = entry->ebx,
+ .result.ebx_mask = 0xFFFFFFFF,
.result.ecx = entry->ecx,
- .result.ecx_mask = entry->ecx,
+ .result.ecx_mask = 0xFFFFFFFF,
.result.edx = entry->edx,
- .result.edx_mask = entry->edx,
+ .result.edx_mask = 0xFFFFFFFF,
};
union hv_register_intercept_result_parameters parameters = {
.cpuid = cpuid_params,
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 05/45] target/i386/mshv: set cpu model name on -cpu host
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (3 preceding siblings ...)
2026-06-26 10:17 ` [PULL 04/45] target/i386/mshv: change cpuid mask to UINT32_MAX Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 06/45] target/i386: query mshv accel for supported cpuids Paolo Bonzini
` (40 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke, Wei Liu, Doru Blânzeanu
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
By registering a cpu_accel_class for mshv, we can report an accurate
model name on -cpu host.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Acked-by: Wei Liu <wei.liu@kernel.org>
Reviewed-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260416121116.527927-6-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
target/i386/mshv/mshv-cpu.c | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 2ee46bd7025..d65cba5de15 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -24,10 +24,13 @@
#include "hw/i386/apic_internal.h"
#include "cpu.h"
+#include "host-cpu.h"
#include "emulate/x86_decode.h"
#include "emulate/x86_emu.h"
#include "emulate/x86_flags.h"
+#include "accel/accel-cpu-target.h"
+
#include "trace-accel_mshv.h"
#include "trace.h"
@@ -1700,3 +1703,31 @@ int mshv_arch_post_init_vm(int vm_fd)
return ret;
}
+
+static void mshv_cpu_instance_init(CPUState *cs)
+{
+ X86CPU *cpu = X86_CPU(cs);
+
+ host_cpu_instance_init(cpu);
+}
+
+static void mshv_cpu_accel_class_init(ObjectClass *oc, const void *data)
+{
+ AccelCPUClass *acc = ACCEL_CPU_CLASS(oc);
+
+ acc->cpu_instance_init = mshv_cpu_instance_init;
+}
+
+static const TypeInfo mshv_cpu_accel_type_info = {
+ .name = ACCEL_CPU_NAME("mshv"),
+ .parent = TYPE_ACCEL_CPU,
+ .class_init = mshv_cpu_accel_class_init,
+ .abstract = true,
+};
+
+static void mshv_cpu_accel_register_types(void)
+{
+ type_register_static(&mshv_cpu_accel_type_info);
+}
+
+type_init(mshv_cpu_accel_register_types);
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 06/45] target/i386: query mshv accel for supported cpuids
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (4 preceding siblings ...)
2026-06-26 10:17 ` [PULL 05/45] target/i386/mshv: set cpu model name on -cpu host Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 07/45] target/i386/mshv: populate xsave area offsets Paolo Bonzini
` (39 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke, Doru Blânzeanu
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
We implement mshv_get_supported_cpuid() and invoke it in
x86_cpu_get_supported_feature_word() retrieve the cpu features that the
host is supporting. Initially we mask the virtualization capabilitities
potentially we might need to mask more in the future.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Reviewed-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260416121116.527927-7-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/system/mshv.h | 3 +++
target/i386/cpu.c | 8 ++++++++
target/i386/mshv/mshv-cpu.c | 27 +++++++++++++++++++++++++++
3 files changed, 38 insertions(+)
diff --git a/include/system/mshv.h b/include/system/mshv.h
index 75286baf165..51b04207358 100644
--- a/include/system/mshv.h
+++ b/include/system/mshv.h
@@ -60,4 +60,7 @@ int mshv_irqchip_add_irqfd_notifier_gsi(const EventNotifier *n,
const EventNotifier *rn, int virq);
int mshv_irqchip_remove_irqfd_notifier_gsi(const EventNotifier *n, int virq);
+/* cpuid */
+uint32_t mshv_get_supported_cpuid(uint32_t func, uint32_t idx, int reg);
+
#endif
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index 8929a75c7c9..5805d33ab92 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -26,6 +26,7 @@
#include "tcg/helper-tcg.h"
#include "exec/translation-block.h"
#include "system/hvf.h"
+#include "system/mshv.h"
#include "system/whpx.h"
#include "whpx/whpx-i386.h"
#include "hvf/hvf-i386.h"
@@ -8233,6 +8234,13 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w)
r = hvf_get_supported_cpuid(wi->cpuid.eax,
wi->cpuid.ecx,
wi->cpuid.reg);
+ } else if (mshv_enabled()) {
+ if (wi->type != CPUID_FEATURE_WORD) {
+ return 0;
+ }
+ r = mshv_get_supported_cpuid(wi->cpuid.eax,
+ wi->cpuid.ecx,
+ wi->cpuid.reg);
} else if (whpx_enabled()) {
switch (wi->type) {
case CPUID_FEATURE_WORD:
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index d65cba5de15..00b8ac18e46 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -1664,6 +1664,33 @@ void mshv_arch_destroy_vcpu(CPUState *cpu)
g_clear_pointer(&env->emu_mmio_buf, g_free);
}
+uint32_t mshv_get_supported_cpuid(uint32_t func, uint32_t idx, int reg)
+{
+ uint32_t eax, ebx, ecx, edx;
+ uint32_t ret = 0;
+
+ host_cpuid(func, idx, &eax, &ebx, &ecx, &edx);
+ switch (reg) {
+ case R_EAX:
+ ret = eax; break;
+ case R_EBX:
+ ret = ebx; break;
+ case R_ECX:
+ ret = ecx; break;
+ case R_EDX:
+ ret = edx; break;
+ }
+
+ /* Disable nested virtualization features not yet supported by MSHV */
+ if (func == 0x80000001 && reg == R_ECX) {
+ ret &= ~CPUID_EXT3_SVM;
+ }
+ if (func == 0x01 && reg == R_ECX) {
+ ret &= ~CPUID_EXT_VMX;
+ }
+ return ret;
+}
+
/*
* Default Microsoft Hypervisor behavior for unimplemented MSR is to send a
* fault to the guest if it tries to access it. It is possible to override
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 07/45] target/i386/mshv: populate xsave area offsets
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (5 preceding siblings ...)
2026-06-26 10:17 ` [PULL 06/45] target/i386: query mshv accel for supported cpuids Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 08/45] target/i386/mshv: use hv-provided [0xD,1+2].EBX Paolo Bonzini
` (38 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel
Cc: Magnus Kulke, Anirudh Rayabharam (Microsoft), Doru Blânzeanu
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
This function is copied verbatim from kvm_cpu_xsave_init(), for MSHV we
need to populate the offsets similarly.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
Reviewed-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260416121116.527927-8-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
target/i386/mshv/mshv-cpu.c | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 00b8ac18e46..66733390c3e 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -1731,11 +1731,42 @@ int mshv_arch_post_init_vm(int vm_fd)
return ret;
}
+static void mshv_cpu_xsave_init(void)
+{
+ static bool first = true;
+ uint32_t eax, ebx, ecx, edx;
+ int i;
+
+ if (!first) {
+ return;
+ }
+ first = false;
+
+ /* x87 and SSE states are in the legacy region of the XSAVE area. */
+ x86_ext_save_areas[XSTATE_FP_BIT].offset = 0;
+ x86_ext_save_areas[XSTATE_SSE_BIT].offset = 0;
+
+ for (i = XSTATE_SSE_BIT + 1; i < XSAVE_STATE_AREA_COUNT; i++) {
+ ExtSaveArea *esa = &x86_ext_save_areas[i];
+
+ if (!esa->size) {
+ continue;
+ }
+ host_cpuid(0xd, i, &eax, &ebx, &ecx, &edx);
+ if (eax != 0) {
+ assert(esa->size == eax);
+ esa->offset = ebx;
+ esa->ecx = ecx;
+ }
+ }
+}
+
static void mshv_cpu_instance_init(CPUState *cs)
{
X86CPU *cpu = X86_CPU(cs);
host_cpu_instance_init(cpu);
+ mshv_cpu_xsave_init();
}
static void mshv_cpu_accel_class_init(ObjectClass *oc, const void *data)
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 08/45] target/i386/mshv: use hv-provided [0xD,1+2].EBX
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (6 preceding siblings ...)
2026-06-26 10:17 ` [PULL 07/45] target/i386/mshv: populate xsave area offsets Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 09/45] accel/mshv: disable la57 (5lvl paging) Paolo Bonzini
` (37 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke, Doru Blânzeanu
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
We cannot statically set the responses for CPUID[0xD,{1,2}].EBX, b/c
those are dynamic, dependent on which features the guest enables.
Hence we mask EBX when registering answers for those subleaves at the
hypervisor, which will result in the hypervisor providing us answers,
considering XCR0 and XSS.
The reported size now reflects the field masks properly (without the
mask they were 576 and 10728, which is wrong):
$ cpuid -l 0xd -s 0
CPU 0:
XSAVE features (0xd/0):
XCR0 valid bit field mask = 0x00000000000600e7
...
bytes required by fields in XCR0 = 0x00002b00 (11008)
bytes required by XSAVE/XRSTOR area = 0x00002b00 (11008)
$ cpuid -l 0xd -s 1
CPU 0:
XSAVE features (0xd/1):
...
SAVE area size in bytes = 0x000029c0 (10688)
IA32_XSS lower 32 bits valid bit field mask = 0x00001800
IA32_XSS upper 32 bits valid bit field mask = 0x00000000
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Reviewed-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260416121116.527927-9-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
target/i386/mshv/mshv-cpu.c | 23 +++++++++++++++++++++--
1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 66733390c3e..e85311af2af 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -528,6 +528,7 @@ static void collect_cpuid_entries(const CPUState *cpu, GList **cpuid_entries)
static int register_intercept_result_cpuid_entry(const CPUState *cpu,
uint8_t subleaf_specific,
uint8_t always_override,
+ uint32_t ebx_mask,
struct hv_cpuid_entry *entry)
{
int ret;
@@ -543,11 +544,12 @@ static int register_intercept_result_cpuid_entry(const CPUState *cpu,
/*
* Masks specify which bits to override. Set to 0xFFFFFFFF to
* override all bits with the values from the QEMU CPU model.
+ * A mask of 0 lets the hypervisor supply its own value.
*/
.result.eax = entry->eax,
.result.eax_mask = 0xFFFFFFFF,
.result.ebx = entry->ebx,
- .result.ebx_mask = 0xFFFFFFFF,
+ .result.ebx_mask = ebx_mask,
.result.ecx = entry->ecx,
.result.ecx_mask = 0xFFFFFFFF,
.result.edx = entry->edx,
@@ -582,6 +584,7 @@ static int register_intercept_result_cpuid(const CPUState *cpu,
int ret = 0, entry_ret;
struct hv_cpuid_entry *entry;
uint8_t subleaf_specific, always_override;
+ uint32_t ebx_mask;
for (size_t i = 0; i < cpuid->nent; i++) {
entry = &cpuid->entries[i];
@@ -589,6 +592,7 @@ static int register_intercept_result_cpuid(const CPUState *cpu,
/* set defaults */
subleaf_specific = 0;
always_override = 1;
+ ebx_mask = 0xFFFFFFFF;
/*
* Intel
@@ -628,8 +632,22 @@ static int register_intercept_result_cpuid(const CPUState *cpu,
always_override = 1;
}
- entry_ret = register_intercept_result_cpuid_entry(cpu, subleaf_specific,
+ /*
+ * CPUID[0xD,0].EBX and CPUID[0xD,1].EBX report the XSAVE area
+ * size based on features currently enabled in XCR0/XSS. These
+ * values are dynamic and must not be overridden with static
+ * results from the QEMU CPU model. Setting ebx_mask to 0 lets
+ * the hypervisor supply EBX based on the guest's actual state.
+ */
+ if (entry->function == 0x0d &&
+ (entry->index == 0 || entry->index == 1)) {
+ ebx_mask = 0;
+ }
+
+ entry_ret = register_intercept_result_cpuid_entry(cpu,
+ subleaf_specific,
always_override,
+ ebx_mask,
entry);
if ((entry_ret < 0) && (ret == 0)) {
ret = entry_ret;
@@ -1688,6 +1706,7 @@ uint32_t mshv_get_supported_cpuid(uint32_t func, uint32_t idx, int reg)
if (func == 0x01 && reg == R_ECX) {
ret &= ~CPUID_EXT_VMX;
}
+
return ret;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 09/45] accel/mshv: disable la57 (5lvl paging)
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (7 preceding siblings ...)
2026-06-26 10:17 ` [PULL 08/45] target/i386/mshv: use hv-provided [0xD,1+2].EBX Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 10/45] target/i386/mshv: use arch_load/store_reg fns Paolo Bonzini
` (36 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel
Cc: Magnus Kulke, Anirudh Rayabharam (Microsoft), Wei Liu,
Doru Blânzeanu
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
This change disable la57 paging on the mshv hypervisor on both the
mshv processor feature bitmap and mask the cpuid feature leaf to the
guest.
Since the removal of hypervisor-assisted gva=>gpa translation in
1c85a4a3d7 we have seen MMIO errors in guests on la57-enabled hw. We
will have to investigate and test this further.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
Reviewed-by: Wei Liu <wei.liu@kernel.org>
Reviewed-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260416121116.527927-10-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/system/mshv_int.h | 2 ++
accel/mshv/mshv-all.c | 7 +++++++
target/i386/mshv/mshv-cpu.c | 15 +++++++++++++++
3 files changed, 24 insertions(+)
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index 35386c422fa..ca156cdf4be 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -94,6 +94,8 @@ void mshv_arch_init_vcpu(CPUState *cpu);
void mshv_arch_destroy_vcpu(CPUState *cpu);
void mshv_arch_amend_proc_features(
union hv_partition_synthetic_processor_features *features);
+void mshv_arch_disable_partition_proc_features(
+ union hv_partition_processor_features *disabled_features);
int mshv_arch_post_init_vm(int vm_fd);
typedef struct mshv_root_hvcall mshv_root_hvcall;
diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
index 0f65b65d9ca..52623be5d93 100644
--- a/accel/mshv/mshv-all.c
+++ b/accel/mshv/mshv-all.c
@@ -142,6 +142,8 @@ static int create_partition(int mshv_fd, int *vm_fd)
int ret;
uint64_t pt_flags, host_proc_features;
union hv_partition_processor_xsave_features disabled_xsave_features;
+ union hv_partition_processor_features disabled_partition_features = {0};
+
struct mshv_create_partition_v2 args = {0};
QEMU_BUILD_BUG_ON(MSHV_NUM_CPU_FEATURES_BANKS != 2);
@@ -177,6 +179,11 @@ static int create_partition(int mshv_fd, int *vm_fd)
}
args.pt_cpu_fbanks[1] = ~host_proc_features;
+ /* arch-specific features we disable regardless of host support */
+ mshv_arch_disable_partition_proc_features(&disabled_partition_features);
+ args.pt_cpu_fbanks[0] |= disabled_partition_features.as_uint64[0];
+ args.pt_cpu_fbanks[1] |= disabled_partition_features.as_uint64[1];
+
/* populate args structure */
args.pt_flags = pt_flags;
args.pt_isolation = MSHV_PT_ISOLATION_NONE;
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index e85311af2af..3523cfcb70c 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -1129,6 +1129,12 @@ void mshv_arch_amend_proc_features(
features->access_guest_idle_reg = 1;
}
+void mshv_arch_disable_partition_proc_features(
+ union hv_partition_processor_features *disabled_features)
+{
+ disabled_features->la57_support = 1;
+}
+
static int set_memory_info(const struct hyperv_message *msg,
struct hv_x64_memory_intercept_message *info)
{
@@ -1707,6 +1713,15 @@ uint32_t mshv_get_supported_cpuid(uint32_t func, uint32_t idx, int reg)
ret &= ~CPUID_EXT_VMX;
}
+ if (func == 0x07 && idx == 0 && reg == R_ECX) {
+ /*
+ * LA57 (5-level paging) causes incorrect GVA=>GPA translations
+ * in the instruction decoder/emulator. Disable until page table
+ * walk in x86_mmu.c works w/ 5-level paging.
+ */
+ ret &= ~CPUID_7_0_ECX_LA57;
+ }
+
return ret;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 10/45] target/i386/mshv: use arch_load/store_reg fns
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (8 preceding siblings ...)
2026-06-26 10:17 ` [PULL 09/45] accel/mshv: disable la57 (5lvl paging) Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 11/45] target/i386/mshv: use generic FPU/xcr0 state Paolo Bonzini
` (35 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Improved consistency around the naming of load/store register fn's. this
is required since we want to roundtrip more registers in a migration
than what's currently required for MMIO emulation.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260417105618.3621-2-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/system/mshv_int.h | 6 ++---
accel/mshv/mshv-all.c | 2 +-
target/i386/mshv/mshv-cpu.c | 52 ++++++++++++++-----------------------
3 files changed, 23 insertions(+), 37 deletions(-)
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index ca156cdf4be..bf0fe571f01 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -82,11 +82,9 @@ void mshv_init_mmio_emu(void);
int mshv_create_vcpu(int vm_fd, uint8_t vp_index, int *cpu_fd);
void mshv_remove_vcpu(int vm_fd, int cpu_fd);
int mshv_configure_vcpu(const CPUState *cpu, const MshvFPU *fpu, uint64_t xcr0);
-int mshv_get_standard_regs(CPUState *cpu);
-int mshv_get_special_regs(CPUState *cpu);
int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit);
-int mshv_load_regs(CPUState *cpu);
-int mshv_store_regs(CPUState *cpu);
+int mshv_arch_load_regs(CPUState *cpu);
+int mshv_arch_store_regs(CPUState *cpu);
int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs,
size_t n_regs);
int mshv_arch_put_registers(const CPUState *cpu);
diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
index 52623be5d93..ec9c90cddda 100644
--- a/accel/mshv/mshv-all.c
+++ b/accel/mshv/mshv-all.c
@@ -729,7 +729,7 @@ static void mshv_cpu_synchronize_pre_loadvm(CPUState *cpu)
static void do_mshv_cpu_synchronize(CPUState *cpu, run_on_cpu_data arg)
{
if (!cpu->accel->dirty) {
- int ret = mshv_load_regs(cpu);
+ int ret = mshv_arch_load_regs(cpu);
if (ret < 0) {
error_report("Failed to load registers for vcpu %d",
cpu->cpu_index);
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 3523cfcb70c..fba95392b9d 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -110,6 +110,8 @@ static enum hv_register_name FPU_REGISTER_NAMES[26] = {
HV_X64_REGISTER_XMM_CONTROL_STATUS,
};
+static int set_special_regs(const CPUState *cpu);
+
static int translate_gva(const CPUState *cpu, uint64_t gva, uint64_t *gpa,
uint64_t flags)
{
@@ -288,7 +290,7 @@ static int set_standard_regs(const CPUState *cpu)
return 0;
}
-int mshv_store_regs(CPUState *cpu)
+int mshv_arch_store_regs(CPUState *cpu)
{
int ret;
@@ -298,6 +300,12 @@ int mshv_store_regs(CPUState *cpu)
return -1;
}
+ ret = set_special_regs(cpu);
+ if (ret < 0) {
+ error_report("Failed to store speical registers");
+ return ret;
+ }
+
return 0;
}
@@ -326,7 +334,7 @@ static void populate_standard_regs(const hv_register_assoc *assocs,
rflags_to_lflags(env);
}
-int mshv_get_standard_regs(CPUState *cpu)
+static int get_standard_regs(CPUState *cpu)
{
struct hv_register_assoc assocs[ARRAY_SIZE(STANDARD_REGISTER_NAMES)];
int ret;
@@ -404,8 +412,7 @@ static void populate_special_regs(const hv_register_assoc *assocs,
cpu_set_apic_base(x86cpu->apic_state, assocs[16].value.reg64);
}
-
-int mshv_get_special_regs(CPUState *cpu)
+static int get_special_regs(CPUState *cpu)
{
struct hv_register_assoc assocs[ARRAY_SIZE(SPECIAL_REGISTER_NAMES)];
int ret;
@@ -425,17 +432,17 @@ int mshv_get_special_regs(CPUState *cpu)
return 0;
}
-int mshv_load_regs(CPUState *cpu)
+int mshv_arch_load_regs(CPUState *cpu)
{
int ret;
- ret = mshv_get_standard_regs(cpu);
+ ret = get_standard_regs(cpu);
if (ret < 0) {
error_report("Failed to load standard registers");
return -1;
}
- ret = mshv_get_special_regs(cpu);
+ ret = get_special_regs(cpu);
if (ret < 0) {
error_report("Failed to load special registers");
return -1;
@@ -1159,16 +1166,16 @@ static int emulate_instruction(CPUState *cpu,
int ret;
x86_insn_stream stream = { .bytes = insn_bytes, .len = insn_len };
- ret = mshv_load_regs(cpu);
+ ret = mshv_arch_load_regs(cpu);
if (ret < 0) {
- error_report("failed to load registers");
+ error_report("Failed to load registers");
return -1;
}
decode_instruction_stream(env, &decode, &stream);
exec_instruction(env, &decode);
- ret = mshv_store_regs(cpu);
+ ret = mshv_arch_store_regs(cpu);
if (ret < 0) {
error_report("failed to store registers");
return -1;
@@ -1347,25 +1354,6 @@ static int handle_pio_non_str(const CPUState *cpu,
return 0;
}
-static int fetch_guest_state(CPUState *cpu)
-{
- int ret;
-
- ret = mshv_get_standard_regs(cpu);
- if (ret < 0) {
- error_report("Failed to get standard registers");
- return -1;
- }
-
- ret = mshv_get_special_regs(cpu);
- if (ret < 0) {
- error_report("Failed to get special registers");
- return -1;
- }
-
- return 0;
-}
-
static int read_memory(const CPUState *cpu, uint64_t initial_gva,
uint64_t initial_gpa, uint64_t gva, uint8_t *data,
size_t len)
@@ -1485,9 +1473,9 @@ static int handle_pio_str(CPUState *cpu, hv_x64_io_port_intercept_message *info)
X86CPU *x86_cpu = X86_CPU(cpu);
CPUX86State *env = &x86_cpu->env;
- ret = fetch_guest_state(cpu);
+ ret = mshv_arch_load_regs(cpu);
if (ret < 0) {
- error_report("Failed to fetch guest state");
+ error_report("Failed to load registers");
return -1;
}
@@ -1518,7 +1506,7 @@ static int handle_pio_str(CPUState *cpu, hv_x64_io_port_intercept_message *info)
ret = set_x64_registers(cpu, reg_names, reg_values);
if (ret < 0) {
- error_report("Failed to set x64 registers");
+ error_report("Failed to set RIP and RAX registers");
return -1;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 11/45] target/i386/mshv: use generic FPU/xcr0 state
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (9 preceding siblings ...)
2026-06-26 10:17 ` [PULL 10/45] target/i386/mshv: use arch_load/store_reg fns Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 12/45] target/i386/mshv: impl init/load/store_vcpu_state Paolo Bonzini
` (34 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Instead of using an mshv-specific FPU state representation we switch to
the generic i386 representation of the registers.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260417105618.3621-3-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/system/mshv_int.h | 15 +-------
target/i386/mshv/mshv-cpu.c | 76 ++++++++++++++++++++++---------------
2 files changed, 47 insertions(+), 44 deletions(-)
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index bf0fe571f01..bb7f6740bba 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -58,19 +58,6 @@ typedef struct MshvMsiControl {
#define mshv_vcpufd(cpu) (cpu->accel->cpufd)
/* cpu */
-typedef struct MshvFPU {
- uint8_t fpr[8][16];
- uint16_t fcw;
- uint16_t fsw;
- uint8_t ftwx;
- uint8_t pad1;
- uint16_t last_opcode;
- uint64_t last_ip;
- uint64_t last_dp;
- uint8_t xmm[16][16];
- uint32_t mxcsr;
- uint32_t pad2;
-} MshvFPU;
typedef enum MshvVmExit {
MshvVmExitIgnore = 0,
@@ -81,7 +68,7 @@ typedef enum MshvVmExit {
void mshv_init_mmio_emu(void);
int mshv_create_vcpu(int vm_fd, uint8_t vp_index, int *cpu_fd);
void mshv_remove_vcpu(int vm_fd, int cpu_fd);
-int mshv_configure_vcpu(const CPUState *cpu, const MshvFPU *fpu, uint64_t xcr0);
+int mshv_configure_vcpu(const CPUState *cpu);
int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit);
int mshv_arch_load_regs(CPUState *cpu);
int mshv_arch_store_regs(CPUState *cpu);
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index fba95392b9d..b5c0c504cb1 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -111,6 +111,9 @@ static enum hv_register_name FPU_REGISTER_NAMES[26] = {
};
static int set_special_regs(const CPUState *cpu);
+static int get_generic_regs(CPUState *cpu,
+ struct hv_register_assoc *assocs,
+ size_t n_regs);
static int translate_gva(const CPUState *cpu, uint64_t gva, uint64_t *gpa,
uint64_t flags)
@@ -767,48 +770,65 @@ static int set_special_regs(const CPUState *cpu)
return 0;
}
-static int set_fpu(const CPUState *cpu, const struct MshvFPU *regs)
+static int set_fpu(const CPUState *cpu)
{
struct hv_register_assoc assocs[ARRAY_SIZE(FPU_REGISTER_NAMES)];
union hv_register_value *value;
- size_t fp_i;
union hv_x64_fp_control_status_register *ctrl_status;
union hv_x64_xmm_control_status_register *xmm_ctrl_status;
int ret;
size_t n_regs = ARRAY_SIZE(FPU_REGISTER_NAMES);
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
+ size_t i, fp_i;
+ bool valid;
/* first 16 registers are xmm0-xmm15 */
- for (size_t i = 0; i < 16; i++) {
+ for (i = 0; i < 16; i++) {
assocs[i].name = FPU_REGISTER_NAMES[i];
value = &assocs[i].value;
- memcpy(&value->reg128, ®s->xmm[i], 16);
+ value->reg128.low_part = env->xmm_regs[i].ZMM_Q(0);
+ value->reg128.high_part = env->xmm_regs[i].ZMM_Q(1);
}
/* next 8 registers are fp_mmx0-fp_mmx7 */
- for (size_t i = 16; i < 24; i++) {
- assocs[i].name = FPU_REGISTER_NAMES[i];
+ for (i = 16; i < 24; i++) {
fp_i = (i - 16);
+ assocs[i].name = FPU_REGISTER_NAMES[i];
value = &assocs[i].value;
- memcpy(&value->reg128, ®s->fpr[fp_i], 16);
+ value->fp.mantissa = env->fpregs[fp_i].d.low;
+ value->fp.biased_exponent = env->fpregs[fp_i].d.high & 0x7FFF;
+ value->fp.sign = (env->fpregs[fp_i].d.high >> 15) & 0x1;
+ value->fp.reserved = 0;
}
/* last two registers are fp_control_status and xmm_control_status */
assocs[24].name = FPU_REGISTER_NAMES[24];
value = &assocs[24].value;
ctrl_status = &value->fp_control_status;
- ctrl_status->fp_control = regs->fcw;
- ctrl_status->fp_status = regs->fsw;
- ctrl_status->fp_tag = regs->ftwx;
+
+ ctrl_status->fp_control = env->fpuc;
+ /* bits 11,12,13 are the top of stack pointer */
+ ctrl_status->fp_status = (env->fpus & ~0x3800) | ((env->fpstt & 0x7) << 11);
+
+ ctrl_status->fp_tag = 0;
+ for (i = 0; i < 8; i++) {
+ valid = (env->fptags[i] == 0);
+ if (valid) {
+ ctrl_status->fp_tag |= (1u << i);
+ }
+ }
+
ctrl_status->reserved = 0;
- ctrl_status->last_fp_op = regs->last_opcode;
- ctrl_status->last_fp_rip = regs->last_ip;
+ ctrl_status->last_fp_op = env->fpop;
+ ctrl_status->last_fp_rip = env->fpip;
assocs[25].name = FPU_REGISTER_NAMES[25];
value = &assocs[25].value;
xmm_ctrl_status = &value->xmm_control_status;
- xmm_ctrl_status->xmm_status_control = regs->mxcsr;
- xmm_ctrl_status->xmm_status_control_mask = 0;
- xmm_ctrl_status->last_fp_rdp = regs->last_dp;
+ xmm_ctrl_status->xmm_status_control = env->mxcsr;
+ xmm_ctrl_status->xmm_status_control_mask = 0x0000ffff;
+ xmm_ctrl_status->last_fp_rdp = env->fpdp;
ret = mshv_set_generic_regs(cpu, assocs, n_regs);
if (ret < 0) {
@@ -819,12 +839,15 @@ static int set_fpu(const CPUState *cpu, const struct MshvFPU *regs)
return 0;
}
-static int set_xc_reg(const CPUState *cpu, uint64_t xcr0)
+static int set_xc_reg(const CPUState *cpu)
{
int ret;
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
+
struct hv_register_assoc assoc = {
.name = HV_X64_REGISTER_XFEM,
- .value.reg64 = xcr0,
+ .value.reg64 = env->xcr0,
};
ret = mshv_set_generic_regs(cpu, &assoc, 1);
@@ -835,8 +858,7 @@ static int set_xc_reg(const CPUState *cpu, uint64_t xcr0)
return 0;
}
-static int set_cpu_state(const CPUState *cpu, const MshvFPU *fpu_regs,
- uint64_t xcr0)
+static int set_cpu_state(const CPUState *cpu)
{
int ret;
@@ -848,11 +870,11 @@ static int set_cpu_state(const CPUState *cpu, const MshvFPU *fpu_regs,
if (ret < 0) {
return ret;
}
- ret = set_fpu(cpu, fpu_regs);
+ ret = set_fpu(cpu);
if (ret < 0) {
return ret;
}
- ret = set_xc_reg(cpu, xcr0);
+ ret = set_xc_reg(cpu);
if (ret < 0) {
return ret;
}
@@ -1001,8 +1023,7 @@ static int setup_msrs(const CPUState *cpu)
* CPUX86State *env = &x86cpu->env;
* X86CPUTopoInfo *topo_info = &env->topo_info;
*/
-int mshv_configure_vcpu(const CPUState *cpu, const struct MshvFPU *fpu,
- uint64_t xcr0)
+int mshv_configure_vcpu(const CPUState *cpu)
{
int ret;
int cpu_fd = mshv_vcpufd(cpu);
@@ -1019,7 +1040,7 @@ int mshv_configure_vcpu(const CPUState *cpu, const struct MshvFPU *fpu,
return -1;
}
- ret = set_cpu_state(cpu, fpu, xcr0);
+ ret = set_cpu_state(cpu);
if (ret < 0) {
error_report("failed to set cpu state");
return -1;
@@ -1036,14 +1057,9 @@ int mshv_configure_vcpu(const CPUState *cpu, const struct MshvFPU *fpu,
static int put_regs(const CPUState *cpu)
{
- X86CPU *x86cpu = X86_CPU(cpu);
- CPUX86State *env = &x86cpu->env;
- MshvFPU fpu = {0};
int ret;
- memset(&fpu, 0, sizeof(fpu));
-
- ret = mshv_configure_vcpu(cpu, &fpu, env->xcr0);
+ ret = mshv_configure_vcpu(cpu);
if (ret < 0) {
error_report("failed to configure vcpu");
return ret;
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 12/45] target/i386/mshv: impl init/load/store_vcpu_state
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (10 preceding siblings ...)
2026-06-26 10:17 ` [PULL 11/45] target/i386/mshv: use generic FPU/xcr0 state Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 13/45] accel/accel-irq: add AccelRouteChange abstraction Paolo Bonzini
` (33 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
In migration we will handle more than registers, so we rework the
routines that were used to load & store CPU registers from/to the
hypervisor into more explicit init/load/store_vcpu_state() functions
that can be called from the appropriate hooks.
load/store_regs() still exists for the purpose of MMIO emulation, but it
will only address standard and special x86 registers.
Functions to retrieve FPU and XCR0 state from the hypervsisor have been
introduced.
MSR and APIC state covered are covered only as part of init_vcpu(). They
are not yet part of the load/store routines.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260417105618.3621-4-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/system/mshv_int.h | 5 +-
accel/mshv/mshv-all.c | 10 +-
target/i386/mshv/mshv-cpu.c | 360 ++++++++++++++++++------------------
3 files changed, 188 insertions(+), 187 deletions(-)
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index bb7f6740bba..1d9902c9bbe 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -70,11 +70,10 @@ int mshv_create_vcpu(int vm_fd, uint8_t vp_index, int *cpu_fd);
void mshv_remove_vcpu(int vm_fd, int cpu_fd);
int mshv_configure_vcpu(const CPUState *cpu);
int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit);
-int mshv_arch_load_regs(CPUState *cpu);
-int mshv_arch_store_regs(CPUState *cpu);
int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs,
size_t n_regs);
-int mshv_arch_put_registers(const CPUState *cpu);
+int mshv_arch_store_vcpu_state(const CPUState *cpu);
+int mshv_arch_load_vcpu_state(CPUState *cpu);
void mshv_arch_init_vcpu(CPUState *cpu);
void mshv_arch_destroy_vcpu(CPUState *cpu);
void mshv_arch_amend_proc_features(
diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
index ec9c90cddda..a6263effa41 100644
--- a/accel/mshv/mshv-all.c
+++ b/accel/mshv/mshv-all.c
@@ -479,13 +479,13 @@ static int mshv_init_vcpu(CPUState *cpu)
int ret;
cpu->accel = g_new0(AccelCPUState, 1);
- mshv_arch_init_vcpu(cpu);
ret = mshv_create_vcpu(vm_fd, vp_index, &cpu->accel->cpufd);
if (ret < 0) {
return -1;
}
+ mshv_arch_init_vcpu(cpu);
cpu->accel->dirty = true;
return 0;
@@ -567,7 +567,7 @@ static int mshv_cpu_exec(CPUState *cpu)
do {
if (cpu->accel->dirty) {
- ret = mshv_arch_put_registers(cpu);
+ ret = mshv_arch_store_vcpu_state(cpu);
if (ret) {
error_report("Failed to put registers after init: %s",
strerror(-ret));
@@ -689,7 +689,7 @@ static void mshv_start_vcpu_thread(CPUState *cpu)
static void do_mshv_cpu_synchronize_post_init(CPUState *cpu,
run_on_cpu_data arg)
{
- int ret = mshv_arch_put_registers(cpu);
+ int ret = mshv_arch_store_vcpu_state(cpu);
if (ret < 0) {
error_report("Failed to put registers after init: %s", strerror(-ret));
abort();
@@ -705,7 +705,7 @@ static void mshv_cpu_synchronize_post_init(CPUState *cpu)
static void mshv_cpu_synchronize_post_reset(CPUState *cpu)
{
- int ret = mshv_arch_put_registers(cpu);
+ int ret = mshv_arch_store_vcpu_state(cpu);
if (ret) {
error_report("Failed to put registers after reset: %s",
strerror(-ret));
@@ -729,7 +729,7 @@ static void mshv_cpu_synchronize_pre_loadvm(CPUState *cpu)
static void do_mshv_cpu_synchronize(CPUState *cpu, run_on_cpu_data arg)
{
if (!cpu->accel->dirty) {
- int ret = mshv_arch_load_regs(cpu);
+ int ret = mshv_arch_load_vcpu_state(cpu);
if (ret < 0) {
error_report("Failed to load registers for vcpu %d",
cpu->cpu_index);
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index b5c0c504cb1..e3f163ef630 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -115,6 +115,92 @@ static int get_generic_regs(CPUState *cpu,
struct hv_register_assoc *assocs,
size_t n_regs);
+static void populate_fpu(const hv_register_assoc *assocs, X86CPU *x86cpu)
+{
+ union hv_register_value value;
+ const union hv_x64_fp_control_status_register *ctrl_status;
+ const union hv_x64_xmm_control_status_register *xmm_ctrl;
+ CPUX86State *env = &x86cpu->env;
+ size_t i, fp_i;
+ bool valid;
+
+ /* first 16 registers are xmm0-xmm15 */
+ for (i = 0; i < 16; i++) {
+ value = assocs[i].value;
+ env->xmm_regs[i].ZMM_Q(0) = value.reg128.low_part;
+ env->xmm_regs[i].ZMM_Q(1) = value.reg128.high_part;
+ }
+
+ /* next 8 registers are fp_mmx0-fp_mmx7 */
+ for (i = 16; i < 24; i++) {
+ fp_i = i - 16;
+ value = assocs[i].value;
+ env->fpregs[fp_i].d.low = value.fp.mantissa;
+ env->fpregs[fp_i].d.high = (value.fp.sign << 15)
+ | (value.fp.biased_exponent & 0x7FFF);
+ }
+
+ /* last two registers are fp_control_status and xmm_control_status */
+ ctrl_status = &assocs[24].value.fp_control_status;
+ env->fpuc = ctrl_status->fp_control;
+
+ env->fpus = ctrl_status->fp_status & ~0x3800;
+ /* bits 11,12,13 are the top of stack pointer */
+ env->fpstt = (ctrl_status->fp_status >> 11) & 0x7;
+
+ for (i = 0; i < 8; i++) {
+ valid = ctrl_status->fp_tag & (1 << i);
+ env->fptags[i] = valid ? 0 : 1;
+ }
+
+ env->fpop = ctrl_status->last_fp_op;
+ env->fpip = ctrl_status->last_fp_rip;
+
+ xmm_ctrl = &assocs[25].value.xmm_control_status;
+ env->mxcsr = xmm_ctrl->xmm_status_control;
+ env->fpdp = xmm_ctrl->last_fp_rdp;
+}
+
+static int get_fpu(CPUState *cpu)
+{
+ struct hv_register_assoc assocs[ARRAY_SIZE(FPU_REGISTER_NAMES)];
+ int ret;
+ X86CPU *x86cpu = X86_CPU(cpu);
+ size_t n_regs = ARRAY_SIZE(FPU_REGISTER_NAMES);
+
+ for (size_t i = 0; i < n_regs; i++) {
+ assocs[i].name = FPU_REGISTER_NAMES[i];
+ }
+ ret = get_generic_regs(cpu, assocs, n_regs);
+ if (ret < 0) {
+ error_report("failed to get special registers");
+ return -errno;
+ }
+
+ populate_fpu(assocs, x86cpu);
+
+ return 0;
+}
+
+static int get_xc_reg(CPUState *cpu)
+{
+ int ret;
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
+ struct hv_register_assoc assocs[1];
+
+ assocs[0].name = HV_X64_REGISTER_XFEM;
+
+ ret = get_generic_regs(cpu, assocs, 1);
+ if (ret < 0) {
+ error_report("failed to get xcr0");
+ return -1;
+ }
+ env->xcr0 = assocs[0].value.reg64;
+
+ return 0;
+}
+
static int translate_gva(const CPUState *cpu, uint64_t gva, uint64_t *gpa,
uint64_t flags)
{
@@ -293,7 +379,7 @@ static int set_standard_regs(const CPUState *cpu)
return 0;
}
-int mshv_arch_store_regs(CPUState *cpu)
+static int store_regs(CPUState *cpu)
{
int ret;
@@ -435,20 +521,45 @@ static int get_special_regs(CPUState *cpu)
return 0;
}
-int mshv_arch_load_regs(CPUState *cpu)
+static int load_regs(CPUState *cpu)
{
int ret;
ret = get_standard_regs(cpu);
if (ret < 0) {
- error_report("Failed to load standard registers");
- return -1;
+ return ret;
}
ret = get_special_regs(cpu);
if (ret < 0) {
- error_report("Failed to load special registers");
- return -1;
+ return ret;
+ }
+
+ return 0;
+}
+
+int mshv_arch_load_vcpu_state(CPUState *cpu)
+{
+ int ret;
+
+ ret = get_standard_regs(cpu);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = get_special_regs(cpu);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = get_xc_reg(cpu);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = get_fpu(cpu);
+ if (ret < 0) {
+ return ret;
}
return 0;
@@ -667,7 +778,7 @@ static int register_intercept_result_cpuid(const CPUState *cpu,
return ret;
}
-static int set_cpuid2(const CPUState *cpu)
+static int init_cpuid2(const CPUState *cpu)
{
int ret;
size_t n_entries, cpuid_size;
@@ -858,29 +969,6 @@ static int set_xc_reg(const CPUState *cpu)
return 0;
}
-static int set_cpu_state(const CPUState *cpu)
-{
- int ret;
-
- ret = set_standard_regs(cpu);
- if (ret < 0) {
- return ret;
- }
- ret = set_special_regs(cpu);
- if (ret < 0) {
- return ret;
- }
- ret = set_fpu(cpu);
- if (ret < 0) {
- return ret;
- }
- ret = set_xc_reg(cpu);
- if (ret < 0) {
- return ret;
- }
- return 0;
-}
-
static int get_vp_state(int cpu_fd, struct mshv_get_set_vp_state *state)
{
int ret;
@@ -894,7 +982,7 @@ static int get_vp_state(int cpu_fd, struct mshv_get_set_vp_state *state)
return 0;
}
-static int get_lapic(int cpu_fd,
+static int get_lapic(const CPUState *cpu,
struct hv_local_interrupt_controller_state *state)
{
int ret;
@@ -902,6 +990,7 @@ static int get_lapic(int cpu_fd,
/* buffer aligned to 4k, as *state requires that */
void *buffer = qemu_memalign(size, size);
struct mshv_get_set_vp_state mshv_state = { 0 };
+ int cpu_fd = mshv_vcpufd(cpu);
mshv_state.buf_ptr = (uint64_t) buffer;
mshv_state.buf_sz = size;
@@ -938,7 +1027,7 @@ static int set_vp_state(int cpu_fd, const struct mshv_get_set_vp_state *state)
return 0;
}
-static int set_lapic(int cpu_fd,
+static int set_lapic(const CPUState *cpu,
const struct hv_local_interrupt_controller_state *state)
{
int ret;
@@ -946,6 +1035,7 @@ static int set_lapic(int cpu_fd,
/* buffer aligned to 4k, as *state requires that */
void *buffer = qemu_memalign(size, size);
struct mshv_get_set_vp_state mshv_state = { 0 };
+ int cpu_fd = mshv_vcpufd(cpu);
if (!state) {
error_report("lapic state is NULL");
@@ -967,13 +1057,13 @@ static int set_lapic(int cpu_fd,
return 0;
}
-static int set_lint(int cpu_fd)
+static int init_lint(const CPUState *cpu)
{
int ret;
uint32_t *lvt_lint0, *lvt_lint1;
struct hv_local_interrupt_controller_state lapic_state = { 0 };
- ret = get_lapic(cpu_fd, &lapic_state);
+ ret = get_lapic(cpu, &lapic_state);
if (ret < 0) {
return ret;
}
@@ -986,161 +1076,31 @@ static int set_lint(int cpu_fd)
/* TODO: should we skip setting lapic if the values are the same? */
- return set_lapic(cpu_fd, &lapic_state);
+ return set_lapic(cpu, &lapic_state);
}
-static int setup_msrs(const CPUState *cpu)
-{
- int ret;
- uint64_t default_type = MSR_MTRR_ENABLE | MSR_MTRR_MEM_TYPE_WB;
-
- /* boot msr entries */
- MshvMsrEntry msrs[9] = {
- { .index = IA32_MSR_SYSENTER_CS, .data = 0x0, },
- { .index = IA32_MSR_SYSENTER_ESP, .data = 0x0, },
- { .index = IA32_MSR_SYSENTER_EIP, .data = 0x0, },
- { .index = IA32_MSR_STAR, .data = 0x0, },
- { .index = IA32_MSR_CSTAR, .data = 0x0, },
- { .index = IA32_MSR_LSTAR, .data = 0x0, },
- { .index = IA32_MSR_KERNEL_GS_BASE, .data = 0x0, },
- { .index = IA32_MSR_SFMASK, .data = 0x0, },
- { .index = IA32_MSR_MTRR_DEF_TYPE, .data = default_type, },
- };
-
- ret = mshv_configure_msr(cpu, msrs, 9);
- if (ret < 0) {
- error_report("failed to setup msrs");
- return -1;
- }
-
- return 0;
-}
-
-/*
- * TODO: populate topology info:
- *
- * X86CPU *x86cpu = X86_CPU(cpu);
- * CPUX86State *env = &x86cpu->env;
- * X86CPUTopoInfo *topo_info = &env->topo_info;
- */
-int mshv_configure_vcpu(const CPUState *cpu)
-{
- int ret;
- int cpu_fd = mshv_vcpufd(cpu);
-
- ret = set_cpuid2(cpu);
- if (ret < 0) {
- error_report("failed to set cpuid");
- return -1;
- }
-
- ret = setup_msrs(cpu);
- if (ret < 0) {
- error_report("failed to setup msrs");
- return -1;
- }
-
- ret = set_cpu_state(cpu);
- if (ret < 0) {
- error_report("failed to set cpu state");
- return -1;
- }
-
- ret = set_lint(cpu_fd);
- if (ret < 0) {
- error_report("failed to set lpic int");
- return -1;
- }
-
- return 0;
-}
-
-static int put_regs(const CPUState *cpu)
+int mshv_arch_store_vcpu_state(const CPUState *cpu)
{
int ret;
- ret = mshv_configure_vcpu(cpu);
+ ret = set_standard_regs(cpu);
if (ret < 0) {
- error_report("failed to configure vcpu");
return ret;
}
- return 0;
-}
-
-struct MsrPair {
- uint32_t index;
- uint64_t value;
-};
-
-static int put_msrs(const CPUState *cpu)
-{
- int ret = 0;
- X86CPU *x86cpu = X86_CPU(cpu);
- CPUX86State *env = &x86cpu->env;
- MshvMsrEntries *msrs = g_malloc0(sizeof(MshvMsrEntries));
-
- struct MsrPair pairs[] = {
- { MSR_IA32_SYSENTER_CS, env->sysenter_cs },
- { MSR_IA32_SYSENTER_ESP, env->sysenter_esp },
- { MSR_IA32_SYSENTER_EIP, env->sysenter_eip },
- { MSR_EFER, env->efer },
- { MSR_PAT, env->pat },
- { MSR_STAR, env->star },
- { MSR_CSTAR, env->cstar },
- { MSR_LSTAR, env->lstar },
- { MSR_KERNELGSBASE, env->kernelgsbase },
- { MSR_FMASK, env->fmask },
- { MSR_MTRRdefType, env->mtrr_deftype },
- { MSR_VM_HSAVE_PA, env->vm_hsave },
- { MSR_SMI_COUNT, env->msr_smi_count },
- { MSR_IA32_PKRS, env->pkrs },
- { MSR_IA32_BNDCFGS, env->msr_bndcfgs },
- { MSR_IA32_XSS, env->xss },
- { MSR_IA32_UMWAIT_CONTROL, env->umwait },
- { MSR_IA32_TSX_CTRL, env->tsx_ctrl },
- { MSR_AMD64_TSC_RATIO, env->amd_tsc_scale_msr },
- { MSR_TSC_AUX, env->tsc_aux },
- { MSR_TSC_ADJUST, env->tsc_adjust },
- { MSR_IA32_SMBASE, env->smbase },
- { MSR_IA32_SPEC_CTRL, env->spec_ctrl },
- { MSR_VIRT_SSBD, env->virt_ssbd },
- };
-
- if (ARRAY_SIZE(pairs) > MSHV_MSR_ENTRIES_COUNT) {
- error_report("MSR entries exceed maximum size");
- g_free(msrs);
- return -1;
- }
-
- for (size_t i = 0; i < ARRAY_SIZE(pairs); i++) {
- MshvMsrEntry *entry = &msrs->entries[i];
- entry->index = pairs[i].index;
- entry->reserved = 0;
- entry->data = pairs[i].value;
- msrs->nmsrs++;
- }
-
- ret = mshv_configure_msr(cpu, &msrs->entries[0], msrs->nmsrs);
- g_free(msrs);
- return ret;
-}
-
-
-int mshv_arch_put_registers(const CPUState *cpu)
-{
- int ret;
-
- ret = put_regs(cpu);
+ ret = set_special_regs(cpu);
if (ret < 0) {
- error_report("Failed to put registers");
- return -1;
+ return ret;
}
- ret = put_msrs(cpu);
+ ret = set_xc_reg(cpu);
if (ret < 0) {
- error_report("Failed to put msrs");
- return -1;
+ return ret;
+ }
+
+ ret = set_fpu(cpu);
+ if (ret < 0) {
+ return ret;
}
return 0;
@@ -1182,7 +1142,7 @@ static int emulate_instruction(CPUState *cpu,
int ret;
x86_insn_stream stream = { .bytes = insn_bytes, .len = insn_len };
- ret = mshv_arch_load_regs(cpu);
+ ret = load_regs(cpu);
if (ret < 0) {
error_report("Failed to load registers");
return -1;
@@ -1191,7 +1151,7 @@ static int emulate_instruction(CPUState *cpu,
decode_instruction_stream(env, &decode, &stream);
exec_instruction(env, &decode);
- ret = mshv_arch_store_regs(cpu);
+ ret = store_regs(cpu);
if (ret < 0) {
error_report("failed to store registers");
return -1;
@@ -1489,7 +1449,7 @@ static int handle_pio_str(CPUState *cpu, hv_x64_io_port_intercept_message *info)
X86CPU *x86_cpu = X86_CPU(cpu);
CPUX86State *env = &x86_cpu->env;
- ret = mshv_arch_load_regs(cpu);
+ ret = load_regs(cpu);
if (ret < 0) {
error_report("Failed to load registers");
return -1;
@@ -1660,6 +1620,33 @@ void mshv_init_mmio_emu(void)
init_emu(&mshv_x86_emul_ops);
}
+static int init_msrs(const CPUState *cpu)
+{
+ int ret;
+ uint64_t d_t = MSR_MTRR_ENABLE | MSR_MTRR_MEM_TYPE_WB;
+
+ const struct hv_register_assoc assocs[] = {
+ { .name = HV_X64_REGISTER_SYSENTER_CS, .value.reg64 = 0x0 },
+ { .name = HV_X64_REGISTER_SYSENTER_ESP, .value.reg64 = 0x0 },
+ { .name = HV_X64_REGISTER_SYSENTER_EIP, .value.reg64 = 0x0 },
+ { .name = HV_X64_REGISTER_STAR, .value.reg64 = 0x0 },
+ { .name = HV_X64_REGISTER_CSTAR, .value.reg64 = 0x0 },
+ { .name = HV_X64_REGISTER_LSTAR, .value.reg64 = 0x0 },
+ { .name = HV_X64_REGISTER_KERNEL_GS_BASE, .value.reg64 = 0x0 },
+ { .name = HV_X64_REGISTER_SFMASK, .value.reg64 = 0x0 },
+ { .name = HV_X64_REGISTER_MSR_MTRR_DEF_TYPE, .value.reg64 = d_t },
+ };
+ QEMU_BUILD_BUG_ON(ARRAY_SIZE(assocs) > MSHV_MSR_ENTRIES_COUNT);
+
+ ret = mshv_set_generic_regs(cpu, assocs, ARRAY_SIZE(assocs));
+ if (ret < 0) {
+ error_report("failed to put msrs");
+ return -1;
+ }
+
+ return 0;
+}
+
void mshv_arch_init_vcpu(CPUState *cpu)
{
X86CPU *x86_cpu = X86_CPU(cpu);
@@ -1667,6 +1654,7 @@ void mshv_arch_init_vcpu(CPUState *cpu)
AccelCPUState *state = cpu->accel;
size_t page = HV_HYP_PAGE_SIZE;
void *mem = qemu_memalign(page, 2 * page);
+ int ret;
/* sanity check, to make sure we don't overflow the page */
QEMU_BUILD_BUG_ON((MAX_REGISTER_COUNT
@@ -1679,6 +1667,20 @@ void mshv_arch_init_vcpu(CPUState *cpu)
state->hvcall_args.output_page = (uint8_t *)mem + page;
env->emu_mmio_buf = g_new(char, 4096);
+
+ /*
+ * TODO: populate topology info:
+ * X86CPUTopoInfo *topo_info = &env->topo_info;
+ */
+
+ ret = init_cpuid2(cpu);
+ assert(ret == 0);
+
+ ret = init_msrs(cpu);
+ assert(ret == 0);
+
+ ret = init_lint(cpu);
+ assert(ret == 0);
}
void mshv_arch_destroy_vcpu(CPUState *cpu)
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 13/45] accel/accel-irq: add AccelRouteChange abstraction
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (11 preceding siblings ...)
2026-06-26 10:17 ` [PULL 12/45] target/i386/mshv: impl init/load/store_vcpu_state Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 14/45] accel/accel-irq: add generic begin_route_changes Paolo Bonzini
` (32 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
The accelerated irqchip routines use a record of changes to batch
changes when programming routes.
Currently this mechanism is coupled to the KVM accelerator, this change
introduces an abstraction that replaces KVMRouteChange and keeps a
pointer to an abstract AccelState instead of the concrete type,
converting the state where necessary.
This is done to further align the irqchip programming in the MSHV
accelerator with the existing KVM code in QEMU. Subsequent commits will
introduce AccelRouteChange to the MSHV accelerator code.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260417105618.3621-5-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/accel/accel-route.h | 17 +++++++++++++++++
include/system/accel-irq.h | 5 +++--
include/system/kvm.h | 21 ++++++++++-----------
include/system/mshv.h | 1 +
accel/accel-irq.c | 4 ++--
accel/kvm/kvm-all.c | 6 +++---
accel/stubs/kvm-stub.c | 2 +-
hw/misc/ivshmem-pci.c | 2 +-
hw/vfio/pci.c | 2 +-
hw/virtio/virtio-pci.c | 3 +--
target/i386/kvm/kvm.c | 2 +-
11 files changed, 41 insertions(+), 24 deletions(-)
create mode 100644 include/accel/accel-route.h
diff --git a/include/accel/accel-route.h b/include/accel/accel-route.h
new file mode 100644
index 00000000000..07fac27e2a7
--- /dev/null
+++ b/include/accel/accel-route.h
@@ -0,0 +1,17 @@
+/*
+ * Accelerator MSI route change tracking
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef ACCEL_ROUTE_H
+#define ACCEL_ROUTE_H
+
+#include "qemu/accel.h"
+
+typedef struct AccelRouteChange {
+ AccelState *accel;
+ int changes;
+} AccelRouteChange;
+
+#endif /* ACCEL_ROUTE_H */
diff --git a/include/system/accel-irq.h b/include/system/accel-irq.h
index a2caa06f547..a1489207113 100644
--- a/include/system/accel-irq.h
+++ b/include/system/accel-irq.h
@@ -25,9 +25,10 @@ static inline bool accel_irqchip_is_split(void)
return mshv_msi_via_irqfd_enabled() || kvm_irqchip_is_split();
}
-int accel_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev);
+int accel_irqchip_add_msi_route(AccelRouteChange *c, int vector,
+ PCIDevice *dev);
int accel_irqchip_update_msi_route(int vector, MSIMessage msg, PCIDevice *dev);
-void accel_irqchip_commit_route_changes(KVMRouteChange *c);
+void accel_irqchip_commit_route_changes(AccelRouteChange *c);
void accel_irqchip_commit_routes(void);
void accel_irqchip_release_virq(int virq);
int accel_irqchip_add_irqfd_notifier_gsi(EventNotifier *n, EventNotifier *rn,
diff --git a/include/system/kvm.h b/include/system/kvm.h
index 5fa33eddda3..ccf90b83416 100644
--- a/include/system/kvm.h
+++ b/include/system/kvm.h
@@ -18,6 +18,7 @@
#include "exec/memattrs.h"
#include "qemu/accel.h"
+#include "accel/accel-route.h"
#include "qom/object.h"
#ifdef COMPILING_PER_TARGET
@@ -183,11 +184,6 @@ extern KVMState *kvm_state;
typedef struct Notifier Notifier;
typedef struct NotifierWithReturn NotifierWithReturn;
-typedef struct KVMRouteChange {
- KVMState *s;
- int changes;
-} KVMRouteChange;
-
/* external API */
unsigned int kvm_get_max_memslots(void);
@@ -466,7 +462,7 @@ void kvm_init_cpu_signals(CPUState *cpu);
/**
* kvm_irqchip_add_msi_route - Add MSI route for specific vector
- * @c: KVMRouteChange instance.
+ * @c: AccelRouteChange instance.
* @vector: which vector to add. This can be either MSI/MSIX
* vector. The function will automatically detect whether
* MSI/MSIX is enabled, and fetch corresponding MSI
@@ -475,20 +471,23 @@ void kvm_init_cpu_signals(CPUState *cpu);
* as @NULL, an empty MSI message will be inited.
* @return: virq (>=0) when success, errno (<0) when failed.
*/
-int kvm_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev);
+int kvm_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev);
int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg,
PCIDevice *dev);
void kvm_irqchip_commit_routes(KVMState *s);
-static inline KVMRouteChange kvm_irqchip_begin_route_changes(KVMState *s)
+static inline AccelRouteChange kvm_irqchip_begin_route_changes(KVMState *s)
{
- return (KVMRouteChange) { .s = s, .changes = 0 };
+ return (AccelRouteChange) {
+ .accel = ACCEL(s),
+ .changes = 0,
+ };
}
-static inline void kvm_irqchip_commit_route_changes(KVMRouteChange *c)
+static inline void kvm_irqchip_commit_route_changes(AccelRouteChange *c)
{
if (c->changes) {
- kvm_irqchip_commit_routes(c->s);
+ kvm_irqchip_commit_routes(KVM_STATE(c->accel));
c->changes = 0;
}
}
diff --git a/include/system/mshv.h b/include/system/mshv.h
index 51b04207358..8dd77a76241 100644
--- a/include/system/mshv.h
+++ b/include/system/mshv.h
@@ -21,6 +21,7 @@
#include "qapi/qapi-types-common.h"
#include "system/memory.h"
#include "accel/accel-ops.h"
+#include "accel/accel-route.h"
#ifdef COMPILING_PER_TARGET
#ifdef CONFIG_MSHV
diff --git a/accel/accel-irq.c b/accel/accel-irq.c
index 7f864e35c4e..0aa04c033d4 100644
--- a/accel/accel-irq.c
+++ b/accel/accel-irq.c
@@ -16,7 +16,7 @@
#include "system/mshv.h"
#include "system/accel-irq.h"
-int accel_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev)
+int accel_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev)
{
#ifdef CONFIG_MSHV_IS_POSSIBLE
if (mshv_msi_via_irqfd_enabled()) {
@@ -42,7 +42,7 @@ int accel_irqchip_update_msi_route(int vector, MSIMessage msg, PCIDevice *dev)
return -ENOSYS;
}
-void accel_irqchip_commit_route_changes(KVMRouteChange *c)
+void accel_irqchip_commit_route_changes(AccelRouteChange *c)
{
#ifdef CONFIG_MSHV_IS_POSSIBLE
if (mshv_msi_via_irqfd_enabled()) {
diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
index f4f0e64fbd0..7217baf8d40 100644
--- a/accel/kvm/kvm-all.c
+++ b/accel/kvm/kvm-all.c
@@ -2359,11 +2359,11 @@ int kvm_irqchip_send_msi(KVMState *s, MSIMessage msg)
return kvm_vm_ioctl(s, KVM_SIGNAL_MSI, &msi);
}
-int kvm_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev)
+int kvm_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev)
{
struct kvm_irq_routing_entry kroute = {};
int virq;
- KVMState *s = c->s;
+ KVMState *s = KVM_STATE(c->accel);
MSIMessage msg = {0, 0};
if (pci_available && dev) {
@@ -2506,7 +2506,7 @@ int kvm_irqchip_send_msi(KVMState *s, MSIMessage msg)
abort();
}
-int kvm_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev)
+int kvm_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev)
{
return -ENOSYS;
}
diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c
index c4617caac6b..32b4b074035 100644
--- a/accel/stubs/kvm-stub.c
+++ b/accel/stubs/kvm-stub.c
@@ -44,7 +44,7 @@ int kvm_on_sigbus(int code, void *addr)
return 1;
}
-int kvm_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev)
+int kvm_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev)
{
return -ENOSYS;
}
diff --git a/hw/misc/ivshmem-pci.c b/hw/misc/ivshmem-pci.c
index c987eebb983..aa8f2717555 100644
--- a/hw/misc/ivshmem-pci.c
+++ b/hw/misc/ivshmem-pci.c
@@ -424,7 +424,7 @@ static void ivshmem_add_kvm_msi_virq(IVShmemState *s, int vector,
Error **errp)
{
PCIDevice *pdev = PCI_DEVICE(s);
- KVMRouteChange c;
+ AccelRouteChange c;
int ret;
IVSHMEM_DPRINTF("ivshmem_add_kvm_msi_virq vector:%d\n", vector);
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index 9c06b25e637..1817f39cde7 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -50,7 +50,7 @@
#include "vfio-helpers.h"
/* Protected by BQL */
-static KVMRouteChange vfio_route_change;
+static AccelRouteChange vfio_route_change;
static void vfio_disable_interrupts(VFIOPCIDevice *vdev);
static void vfio_mmap_set_enabled(VFIOPCIDevice *vdev, bool enabled);
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index fd9719547f9..90df7bf3a1a 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -869,7 +869,7 @@ static int kvm_virtio_pci_vq_vector_use(VirtIOPCIProxy *proxy,
int ret;
if (irqfd->users == 0) {
- KVMRouteChange c = kvm_irqchip_begin_route_changes(kvm_state);
+ AccelRouteChange c = kvm_irqchip_begin_route_changes(kvm_state);
ret = accel_irqchip_add_msi_route(&c, vector, &proxy->pci_dev);
if (ret < 0) {
return ret;
@@ -2669,4 +2669,3 @@ static void virtio_pci_register_types(void)
}
type_init(virtio_pci_register_types)
-
diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c
index 9e352882c8c..478a5573cd2 100644
--- a/target/i386/kvm/kvm.c
+++ b/target/i386/kvm/kvm.c
@@ -6678,7 +6678,7 @@ void kvm_arch_init_irq_routing(KVMState *s)
kvm_gsi_routing_allowed = true;
if (kvm_irqchip_is_split()) {
- KVMRouteChange c = kvm_irqchip_begin_route_changes(s);
+ AccelRouteChange c = kvm_irqchip_begin_route_changes(s);
int i;
/* If the ioapic is in QEMU and the lapics are in KVM, reserve
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 14/45] accel/accel-irq: add generic begin_route_changes
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (12 preceding siblings ...)
2026-06-26 10:17 ` [PULL 13/45] accel/accel-irq: add AccelRouteChange abstraction Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 15/45] accel/accel-irq: add generic commit_route_changes Paolo Bonzini
` (31 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
A generic accel_irqchip_begin_route_change() fn has been introduced for
usage in the MSHV accelerator. It replaces the respective kvm_ fn.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260417105618.3621-6-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/system/accel-irq.h | 1 +
include/system/kvm.h | 8 --------
accel/accel-irq.c | 21 +++++++++++++++++++++
hw/misc/ivshmem-pci.c | 4 ++--
hw/vfio/pci.c | 5 +++--
hw/virtio/virtio-pci.c | 2 +-
target/i386/kvm/kvm.c | 3 ++-
7 files changed, 30 insertions(+), 14 deletions(-)
diff --git a/include/system/accel-irq.h b/include/system/accel-irq.h
index a1489207113..fc94c54264b 100644
--- a/include/system/accel-irq.h
+++ b/include/system/accel-irq.h
@@ -25,6 +25,7 @@ static inline bool accel_irqchip_is_split(void)
return mshv_msi_via_irqfd_enabled() || kvm_irqchip_is_split();
}
+AccelRouteChange accel_irqchip_begin_route_changes(void);
int accel_irqchip_add_msi_route(AccelRouteChange *c, int vector,
PCIDevice *dev);
int accel_irqchip_update_msi_route(int vector, MSIMessage msg, PCIDevice *dev);
diff --git a/include/system/kvm.h b/include/system/kvm.h
index ccf90b83416..fec24d2135a 100644
--- a/include/system/kvm.h
+++ b/include/system/kvm.h
@@ -476,14 +476,6 @@ int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg,
PCIDevice *dev);
void kvm_irqchip_commit_routes(KVMState *s);
-static inline AccelRouteChange kvm_irqchip_begin_route_changes(KVMState *s)
-{
- return (AccelRouteChange) {
- .accel = ACCEL(s),
- .changes = 0,
- };
-}
-
static inline void kvm_irqchip_commit_route_changes(AccelRouteChange *c)
{
if (c->changes) {
diff --git a/accel/accel-irq.c b/accel/accel-irq.c
index 0aa04c033d4..3815f6727c6 100644
--- a/accel/accel-irq.c
+++ b/accel/accel-irq.c
@@ -10,6 +10,7 @@
*/
#include "qemu/osdep.h"
+#include "qemu/error-report.h"
#include "hw/pci/msi.h"
#include "system/kvm.h"
@@ -104,3 +105,23 @@ int accel_irqchip_remove_irqfd_notifier_gsi(EventNotifier *n, int virq)
}
return -ENOSYS;
}
+
+inline AccelRouteChange accel_irqchip_begin_route_changes(void)
+{
+#ifdef CONFIG_MSHV_IS_POSSIBLE
+ if (mshv_msi_via_irqfd_enabled()) {
+ return (AccelRouteChange) {
+ .accel = ACCEL(mshv_state),
+ .changes = 0,
+ };
+ }
+#endif
+ if (kvm_enabled()) {
+ return (AccelRouteChange) {
+ .accel = ACCEL(kvm_state),
+ .changes = 0,
+ };
+ }
+ error_report("can't initiate route change, no accel irqchip available");
+ abort();
+}
diff --git a/hw/misc/ivshmem-pci.c b/hw/misc/ivshmem-pci.c
index aa8f2717555..fa23562886b 100644
--- a/hw/misc/ivshmem-pci.c
+++ b/hw/misc/ivshmem-pci.c
@@ -26,7 +26,7 @@
#include "hw/core/qdev-properties-system.h"
#include "hw/pci/msi.h"
#include "hw/pci/msix.h"
-#include "system/kvm.h"
+#include "system/accel-irq.h"
#include "migration/blocker.h"
#include "migration/vmstate.h"
#include "qemu/error-report.h"
@@ -430,7 +430,7 @@ static void ivshmem_add_kvm_msi_virq(IVShmemState *s, int vector,
IVSHMEM_DPRINTF("ivshmem_add_kvm_msi_virq vector:%d\n", vector);
assert(!s->msi_vectors[vector].pdev);
- c = kvm_irqchip_begin_route_changes(kvm_state);
+ c = accel_irqchip_begin_route_changes();
ret = kvm_irqchip_add_msi_route(&c, vector, pdev);
if (ret < 0) {
error_setg(errp, "kvm_irqchip_add_msi_route failed");
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index 1817f39cde7..3a6e17c9d77 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -38,6 +38,7 @@
#include "qemu/module.h"
#include "qemu/range.h"
#include "qemu/units.h"
+#include "system/accel-irq.h"
#include "system/kvm.h"
#include "system/runstate.h"
#include "pci.h"
@@ -688,7 +689,7 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr,
if (vdev->defer_kvm_irq_routing) {
vfio_pci_add_kvm_msi_virq(vdev, vector, nr, true);
} else {
- vfio_route_change = kvm_irqchip_begin_route_changes(kvm_state);
+ vfio_route_change = accel_irqchip_begin_route_changes();
vfio_pci_add_kvm_msi_virq(vdev, vector, nr, true);
kvm_irqchip_commit_route_changes(&vfio_route_change);
vfio_connect_kvm_msi_virq(vector, nr);
@@ -789,7 +790,7 @@ void vfio_pci_prepare_kvm_msi_virq_batch(VFIOPCIDevice *vdev)
{
assert(!vdev->defer_kvm_irq_routing);
vdev->defer_kvm_irq_routing = true;
- vfio_route_change = kvm_irqchip_begin_route_changes(kvm_state);
+ vfio_route_change = accel_irqchip_begin_route_changes();
}
void vfio_pci_commit_kvm_msi_virq_batch(VFIOPCIDevice *vdev)
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index 90df7bf3a1a..6f5db5fc42b 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -869,7 +869,7 @@ static int kvm_virtio_pci_vq_vector_use(VirtIOPCIProxy *proxy,
int ret;
if (irqfd->users == 0) {
- AccelRouteChange c = kvm_irqchip_begin_route_changes(kvm_state);
+ AccelRouteChange c = accel_irqchip_begin_route_changes();
ret = accel_irqchip_add_msi_route(&c, vector, &proxy->pci_dev);
if (ret < 0) {
return ret;
diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c
index 478a5573cd2..03312922d50 100644
--- a/target/i386/kvm/kvm.c
+++ b/target/i386/kvm/kvm.c
@@ -32,6 +32,7 @@
#include "vmsr_energy.h"
#include "system/system.h"
#include "system/hw_accel.h"
+#include "system/accel-irq.h"
#include "system/kvm_int.h"
#include "system/runstate.h"
#include "system/ramblock.h"
@@ -6678,7 +6679,7 @@ void kvm_arch_init_irq_routing(KVMState *s)
kvm_gsi_routing_allowed = true;
if (kvm_irqchip_is_split()) {
- AccelRouteChange c = kvm_irqchip_begin_route_changes(s);
+ AccelRouteChange c = accel_irqchip_begin_route_changes();
int i;
/* If the ioapic is in QEMU and the lapics are in KVM, reserve
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 15/45] accel/accel-irq: add generic commit_route_changes
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (13 preceding siblings ...)
2026-06-26 10:17 ` [PULL 14/45] accel/accel-irq: add generic begin_route_changes Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 16/45] accel/mshv: add irq_routes to state Paolo Bonzini
` (30 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
A generic accel_irqchip_commit_route_changes() fn has been introduced for
usage in the MSHV accelerator. The respective kvm_ fn can be removed
since we handle the commit op in a generic way.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260417105618.3621-7-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/system/kvm.h | 8 --------
accel/accel-irq.c | 10 +++-------
hw/misc/ivshmem-pci.c | 2 +-
hw/vfio/pci.c | 4 ++--
target/i386/kvm/kvm.c | 2 +-
5 files changed, 7 insertions(+), 19 deletions(-)
diff --git a/include/system/kvm.h b/include/system/kvm.h
index fec24d2135a..cdd1856ac5f 100644
--- a/include/system/kvm.h
+++ b/include/system/kvm.h
@@ -476,14 +476,6 @@ int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg,
PCIDevice *dev);
void kvm_irqchip_commit_routes(KVMState *s);
-static inline void kvm_irqchip_commit_route_changes(AccelRouteChange *c)
-{
- if (c->changes) {
- kvm_irqchip_commit_routes(KVM_STATE(c->accel));
- c->changes = 0;
- }
-}
-
int kvm_irqchip_get_virq(KVMState *s);
void kvm_irqchip_release_virq(KVMState *s, int virq);
diff --git a/accel/accel-irq.c b/accel/accel-irq.c
index 3815f6727c6..7e71b525557 100644
--- a/accel/accel-irq.c
+++ b/accel/accel-irq.c
@@ -45,13 +45,9 @@ int accel_irqchip_update_msi_route(int vector, MSIMessage msg, PCIDevice *dev)
void accel_irqchip_commit_route_changes(AccelRouteChange *c)
{
-#ifdef CONFIG_MSHV_IS_POSSIBLE
- if (mshv_msi_via_irqfd_enabled()) {
- mshv_irqchip_commit_routes();
- }
-#endif
- if (kvm_enabled()) {
- kvm_irqchip_commit_route_changes(c);
+ if (c->changes) {
+ accel_irqchip_commit_routes();
+ c->changes = 0;
}
}
diff --git a/hw/misc/ivshmem-pci.c b/hw/misc/ivshmem-pci.c
index fa23562886b..536475e9de8 100644
--- a/hw/misc/ivshmem-pci.c
+++ b/hw/misc/ivshmem-pci.c
@@ -436,7 +436,7 @@ static void ivshmem_add_kvm_msi_virq(IVShmemState *s, int vector,
error_setg(errp, "kvm_irqchip_add_msi_route failed");
return;
}
- kvm_irqchip_commit_route_changes(&c);
+ accel_irqchip_commit_route_changes(&c);
s->msi_vectors[vector].virq = ret;
s->msi_vectors[vector].pdev = pdev;
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index 3a6e17c9d77..d2aba8aaf50 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -691,7 +691,7 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr,
} else {
vfio_route_change = accel_irqchip_begin_route_changes();
vfio_pci_add_kvm_msi_virq(vdev, vector, nr, true);
- kvm_irqchip_commit_route_changes(&vfio_route_change);
+ accel_irqchip_commit_route_changes(&vfio_route_change);
vfio_connect_kvm_msi_virq(vector, nr);
}
}
@@ -800,7 +800,7 @@ void vfio_pci_commit_kvm_msi_virq_batch(VFIOPCIDevice *vdev)
assert(vdev->defer_kvm_irq_routing);
vdev->defer_kvm_irq_routing = false;
- kvm_irqchip_commit_route_changes(&vfio_route_change);
+ accel_irqchip_commit_route_changes(&vfio_route_change);
for (i = 0; i < vdev->nr_vectors; i++) {
vfio_connect_kvm_msi_virq(&vdev->msi_vectors[i], i);
diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c
index 03312922d50..64cc421abe6 100644
--- a/target/i386/kvm/kvm.c
+++ b/target/i386/kvm/kvm.c
@@ -6690,7 +6690,7 @@ void kvm_arch_init_irq_routing(KVMState *s)
exit(1);
}
}
- kvm_irqchip_commit_route_changes(&c);
+ accel_irqchip_commit_route_changes(&c);
}
}
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 16/45] accel/mshv: add irq_routes to state
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (14 preceding siblings ...)
2026-06-26 10:17 ` [PULL 15/45] accel/accel-irq: add generic commit_route_changes Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 17/45] accel/mshv: update s->irq_routes in add_msi_route Paolo Bonzini
` (29 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
This change adds fields related to irq routing to the MSHV state, following
similar fields in the KVM implementation.
So far the fields are only initialized, they will be used in subsequent
commits for bookkeeping purposes and storing uncommitted interrupt routes.
The TYPE_MSHV_ACCEL defines have been moved to the header.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260417105618.3621-8-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/system/mshv.h | 7 +++++++
include/system/mshv_int.h | 5 +++++
accel/mshv/irq.c | 10 ++++++++++
accel/mshv/mshv-all.c | 6 ++----
4 files changed, 24 insertions(+), 4 deletions(-)
diff --git a/include/system/mshv.h b/include/system/mshv.h
index 8dd77a76241..1e9bcc1b0b6 100644
--- a/include/system/mshv.h
+++ b/include/system/mshv.h
@@ -45,7 +45,13 @@ extern bool mshv_allowed;
#define mshv_msi_via_irqfd_enabled() mshv_enabled()
#endif
+#define TYPE_MSHV_ACCEL ACCEL_CLASS_NAME("mshv")
+
typedef struct MshvState MshvState;
+
+DECLARE_INSTANCE_CHECKER(MshvState, MSHV_STATE,
+ TYPE_MSHV_ACCEL)
+
extern MshvState *mshv_state;
/* interrupt */
@@ -60,6 +66,7 @@ void mshv_irqchip_release_virq(int virq);
int mshv_irqchip_add_irqfd_notifier_gsi(const EventNotifier *n,
const EventNotifier *rn, int virq);
int mshv_irqchip_remove_irqfd_notifier_gsi(const EventNotifier *n, int virq);
+void mshv_init_irq_routing(MshvState *s);
/* cpuid */
uint32_t mshv_get_supported_cpuid(uint32_t func, uint32_t idx, int reg);
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index 1d9902c9bbe..a0fa9a50f76 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -48,6 +48,11 @@ struct MshvState {
int nr_as;
MshvAddressSpace *as;
int fd;
+ /* irqchip routing */
+ struct mshv_user_irq_table *irq_routes;
+ int nr_allocated_irq_routes;
+ unsigned long *used_gsi_bitmap;
+ unsigned int gsi_count;
};
typedef struct MshvMsiControl {
diff --git a/accel/mshv/irq.c b/accel/mshv/irq.c
index 3c238c33c33..82f2022c7c3 100644
--- a/accel/mshv/irq.c
+++ b/accel/mshv/irq.c
@@ -396,3 +396,13 @@ int mshv_reserve_ioapic_msi_routes(int vm_fd)
return 0;
}
+
+void mshv_init_irq_routing(MshvState *s)
+{
+ int gsi_count = MSHV_MAX_MSI_ROUTES;
+
+ s->irq_routes = g_malloc0(sizeof(*s->irq_routes));
+ s->nr_allocated_irq_routes = 0;
+ s->gsi_count = gsi_count;
+ s->used_gsi_bitmap = bitmap_new(gsi_count);
+}
diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
index a6263effa41..2ed3ef4b841 100644
--- a/accel/mshv/mshv-all.c
+++ b/accel/mshv/mshv-all.c
@@ -43,10 +43,6 @@
#include <err.h>
#include <sys/ioctl.h>
-#define TYPE_MSHV_ACCEL ACCEL_CLASS_NAME("mshv")
-
-DECLARE_INSTANCE_CHECKER(MshvState, MSHV_STATE, TYPE_MSHV_ACCEL)
-
bool mshv_allowed;
MshvState *mshv_state;
@@ -536,6 +532,8 @@ static int mshv_init(AccelState *as, MachineState *ms)
mshv_state = s;
+ mshv_init_irq_routing(s);
+
register_mshv_memory_listener(s, &s->memory_listener, &address_space_memory,
0, "mshv-memory");
memory_listener_register(&mshv_io_listener, &address_space_io);
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 17/45] accel/mshv: update s->irq_routes in add_msi_route
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (15 preceding siblings ...)
2026-06-26 10:17 ` [PULL 16/45] accel/mshv: add irq_routes to state Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 18/45] accel/mshv: update s->irq_routes in update_msi_route Paolo Bonzini
` (28 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
The irq_routes field of the state is populated with native mshv irq
route entries. The allocation logic is modelled after the KVM
implementation: we will always allocate a minumum of 64 entries and use
a bitmask to find/set/clear GSIs.
The old implementation of add_msi_routes will be removed in a subsequent
commit.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260417105618.3621-9-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/system/mshv.h | 3 +-
accel/accel-irq.c | 2 +-
accel/mshv/irq.c | 89 +++++++++++++++++++++++++++++++++++++----
accel/stubs/mshv-stub.c | 2 +-
4 files changed, 85 insertions(+), 11 deletions(-)
diff --git a/include/system/mshv.h b/include/system/mshv.h
index 1e9bcc1b0b6..d22f82982d0 100644
--- a/include/system/mshv.h
+++ b/include/system/mshv.h
@@ -33,6 +33,7 @@
#endif
#define MSHV_MAX_MSI_ROUTES 4096
+#define MSHV_MIN_ALLOCATED_MSI_ROUTES 64
#define MSHV_PAGE_SHIFT 12
@@ -59,7 +60,7 @@ int mshv_request_interrupt(MshvState *mshv_state, uint32_t interrupt_type, uint3
uint32_t vp_index, bool logical_destination_mode,
bool level_triggered);
-int mshv_irqchip_add_msi_route(int vector, PCIDevice *dev);
+int mshv_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev);
int mshv_irqchip_update_msi_route(int virq, MSIMessage msg, PCIDevice *dev);
void mshv_irqchip_commit_routes(void);
void mshv_irqchip_release_virq(int virq);
diff --git a/accel/accel-irq.c b/accel/accel-irq.c
index 7e71b525557..5a97a345b23 100644
--- a/accel/accel-irq.c
+++ b/accel/accel-irq.c
@@ -21,7 +21,7 @@ int accel_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev)
{
#ifdef CONFIG_MSHV_IS_POSSIBLE
if (mshv_msi_via_irqfd_enabled()) {
- return mshv_irqchip_add_msi_route(vector, dev);
+ return mshv_irqchip_add_msi_route(c, vector, dev);
}
#endif
if (kvm_enabled()) {
diff --git a/accel/mshv/irq.c b/accel/mshv/irq.c
index 82f2022c7c3..9d6bdde27ac 100644
--- a/accel/mshv/irq.c
+++ b/accel/mshv/irq.c
@@ -278,18 +278,91 @@ static int irqchip_update_irqfd_notifier_gsi(const EventNotifier *event,
return register_irqfd(vm_fd, fd, virq);
}
-
-int mshv_irqchip_add_msi_route(int vector, PCIDevice *dev)
+static int irqchip_allocate_gsi(MshvState *s, int *gsi)
{
- MSIMessage msg = { 0, 0 };
- int virq = 0;
+ int next_gsi;
- if (pci_available && dev) {
- msg = pci_get_msi_message(dev, vector);
- virq = add_msi_routing(msg.address, le32_to_cpu(msg.data));
+ /* Return the lowest unused GSI in the bitmap */
+ next_gsi = find_first_zero_bit(s->used_gsi_bitmap, s->gsi_count);
+ if (next_gsi >= s->gsi_count) {
+ return -ENOSPC;
}
- return virq;
+ *gsi = next_gsi;
+
+ return 0;
+}
+
+static void irqchip_release_gsi(MshvState *s, int gsi)
+{
+ clear_bit(gsi, s->used_gsi_bitmap);
+}
+
+static void add_routing_entry(MshvState *s, struct mshv_user_irq_entry *entry)
+{
+ struct mshv_user_irq_entry *new;
+ int n, size;
+
+ if (s->irq_routes->nr == s->nr_allocated_irq_routes) {
+ n = s->nr_allocated_irq_routes * 2;
+ if (n < MSHV_MIN_ALLOCATED_MSI_ROUTES) {
+ n = MSHV_MIN_ALLOCATED_MSI_ROUTES;
+ }
+ size = sizeof(struct mshv_user_irq_table);
+ size += n * sizeof(*new);
+ s->irq_routes = g_realloc(s->irq_routes, size);
+ s->nr_allocated_irq_routes = n;
+ }
+
+ n = s->irq_routes->nr;
+ s->irq_routes->nr++;
+ new = &s->irq_routes->entries[n];
+
+ *new = *entry;
+
+ set_bit(entry->gsi, s->used_gsi_bitmap);
+
+ trace_mshv_add_msi_routing(entry->address_lo | entry->address_hi,
+ entry->data);
+}
+
+int mshv_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev)
+{
+ struct mshv_user_irq_entry entry = { 0 };
+ MSIMessage msg = { 0 };
+ uint32_t data, high_addr, low_addr;
+ int gsi, ret;
+ MshvState *s = MSHV_STATE(c->accel);
+
+ if (!pci_available || !dev) {
+ return 0;
+ }
+
+ msg = pci_get_msi_message(dev, vector);
+
+ ret = irqchip_allocate_gsi(mshv_state, &gsi);
+ if (ret < 0) {
+ error_report("Could not allocate GSI for MSI route");
+ return -1;
+ }
+ high_addr = msg.address >> 32;
+ low_addr = msg.address & 0xFFFFFFFF;
+ data = le32_to_cpu(msg.data);
+
+ entry.gsi = gsi;
+ entry.address_hi = high_addr;
+ entry.address_lo = low_addr;
+ entry.data = data;
+
+ if (s->irq_routes->nr < s->gsi_count) {
+ add_routing_entry(s, &entry);
+ c->changes++;
+ } else {
+ irqchip_release_gsi(s, gsi);
+ return -ENOSPC;
+ }
+
+ return gsi;
}
void mshv_irqchip_release_virq(int virq)
diff --git a/accel/stubs/mshv-stub.c b/accel/stubs/mshv-stub.c
index e499b199d9d..998c9e2fc6c 100644
--- a/accel/stubs/mshv-stub.c
+++ b/accel/stubs/mshv-stub.c
@@ -14,7 +14,7 @@
bool mshv_allowed;
-int mshv_irqchip_add_msi_route(int vector, PCIDevice *dev)
+int mshv_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev)
{
return -ENOSYS;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 18/45] accel/mshv: update s->irq_routes in update_msi_route
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (16 preceding siblings ...)
2026-06-26 10:17 ` [PULL 17/45] accel/mshv: update s->irq_routes in add_msi_route Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 19/45] accel/mshv: update s->irq_routes in release_virq Paolo Bonzini
` (27 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
The state's irq_routes field will be updated when an irqchip's gsi
is requested to be updated with a new dest/vector.
The old set_msi_routing() fn is redundant and can be removed.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260417105618.3621-10-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
accel/mshv/irq.c | 87 +++++++++++++++++++++---------------------------
1 file changed, 38 insertions(+), 49 deletions(-)
diff --git a/accel/mshv/irq.c b/accel/mshv/irq.c
index 9d6bdde27ac..b5a047b3672 100644
--- a/accel/mshv/irq.c
+++ b/accel/mshv/irq.c
@@ -36,52 +36,6 @@ void mshv_init_msicontrol(void)
msi_control->updated = false;
}
-static int set_msi_routing(uint32_t gsi, uint64_t addr, uint32_t data)
-{
- struct mshv_user_irq_entry *entry;
- uint32_t high_addr = addr >> 32;
- uint32_t low_addr = addr & 0xFFFFFFFF;
- GHashTable *gsi_routes;
-
- trace_mshv_set_msi_routing(gsi, addr, data);
-
- if (gsi >= MSHV_MAX_MSI_ROUTES) {
- error_report("gsi >= MSHV_MAX_MSI_ROUTES");
- return -1;
- }
-
- assert(msi_control);
-
- WITH_QEMU_LOCK_GUARD(&msi_control_mutex) {
- gsi_routes = msi_control->gsi_routes;
- entry = g_hash_table_lookup(gsi_routes, GINT_TO_POINTER(gsi));
-
- if (entry
- && entry->address_hi == high_addr
- && entry->address_lo == low_addr
- && entry->data == data)
- {
- /* nothing to update */
- return 0;
- }
-
- /* free old entry */
- g_free(entry);
-
- /* create new entry */
- entry = g_new0(struct mshv_user_irq_entry, 1);
- entry->gsi = gsi;
- entry->address_hi = high_addr;
- entry->address_lo = low_addr;
- entry->data = data;
-
- g_hash_table_insert(gsi_routes, GINT_TO_POINTER(gsi), entry);
- msi_control->updated = true;
- }
-
- return 0;
-}
-
static int add_msi_routing(uint64_t addr, uint32_t data)
{
struct mshv_user_irq_entry *route_entry;
@@ -370,16 +324,51 @@ void mshv_irqchip_release_virq(int virq)
remove_msi_routing(virq);
}
+static int update_routing_entry(MshvState *s,
+ struct mshv_user_irq_entry *new_entry)
+{
+ struct mshv_user_irq_entry *entry;
+ int n;
+
+ for (n = 0; n < s->irq_routes->nr; n++) {
+ entry = &s->irq_routes->entries[n];
+ if (entry->gsi != new_entry->gsi) {
+ continue;
+ }
+
+ if (!memcmp(entry, new_entry, sizeof *entry)) {
+ return 0;
+ }
+
+ *entry = *new_entry;
+
+ return 0;
+ }
+
+ return -ESRCH;
+}
+
int mshv_irqchip_update_msi_route(int virq, MSIMessage msg, PCIDevice *dev)
{
+ uint32_t addr_hi = msg.address >> 32;
+ uint32_t addr_lo = msg.address & 0xFFFFFFFF;
+ uint32_t data = le32_to_cpu(msg.data);
+ struct mshv_user_irq_entry entry = {
+ .gsi = virq,
+ .address_hi = addr_hi,
+ .address_lo = addr_lo,
+ .data = data,
+ };
int ret;
- ret = set_msi_routing(virq, msg.address, le32_to_cpu(msg.data));
+ ret = update_routing_entry(mshv_state, &entry);
if (ret < 0) {
- error_report("Failed to set msi routing");
- return -1;
+ error_report("Failed to set msi routing for gsi %d", virq);
+ abort();
}
+ trace_mshv_set_msi_routing(virq, msg.address, data);
+
return 0;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 19/45] accel/mshv: update s->irq_routes in release_virq
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (17 preceding siblings ...)
2026-06-26 10:17 ` [PULL 18/45] accel/mshv: update s->irq_routes in update_msi_route Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 20/45] accel/mshv: use s->irq_routes in commit_routes Paolo Bonzini
` (26 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
The state's irq_routes field will be updated when an irqchip's gsi
is requested to be released.
The old remove_msi_routing() fn is redundant and can be removed.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260417105618.3621-11-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/system/mshv.h | 2 +-
accel/accel-irq.c | 2 +-
accel/mshv/irq.c | 43 ++++++++++++++---------------------------
accel/stubs/mshv-stub.c | 2 +-
4 files changed, 17 insertions(+), 32 deletions(-)
diff --git a/include/system/mshv.h b/include/system/mshv.h
index d22f82982d0..fa716c1ee97 100644
--- a/include/system/mshv.h
+++ b/include/system/mshv.h
@@ -63,7 +63,7 @@ int mshv_request_interrupt(MshvState *mshv_state, uint32_t interrupt_type, uint3
int mshv_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev);
int mshv_irqchip_update_msi_route(int virq, MSIMessage msg, PCIDevice *dev);
void mshv_irqchip_commit_routes(void);
-void mshv_irqchip_release_virq(int virq);
+void mshv_irqchip_release_virq(MshvState *s, int virq);
int mshv_irqchip_add_irqfd_notifier_gsi(const EventNotifier *n,
const EventNotifier *rn, int virq);
int mshv_irqchip_remove_irqfd_notifier_gsi(const EventNotifier *n, int virq);
diff --git a/accel/accel-irq.c b/accel/accel-irq.c
index 5a97a345b23..0cb3ef78d3a 100644
--- a/accel/accel-irq.c
+++ b/accel/accel-irq.c
@@ -67,7 +67,7 @@ void accel_irqchip_release_virq(int virq)
{
#ifdef CONFIG_MSHV_IS_POSSIBLE
if (mshv_msi_via_irqfd_enabled()) {
- mshv_irqchip_release_virq(virq);
+ mshv_irqchip_release_virq(mshv_state, virq);
}
#endif
if (kvm_enabled()) {
diff --git a/accel/mshv/irq.c b/accel/mshv/irq.c
index b5a047b3672..990ce346204 100644
--- a/accel/mshv/irq.c
+++ b/accel/mshv/irq.c
@@ -123,33 +123,6 @@ static int commit_msi_routing_table(int vm_fd)
return 0;
}
-static int remove_msi_routing(uint32_t gsi)
-{
- struct mshv_user_irq_entry *route_entry;
- GHashTable *gsi_routes;
-
- trace_mshv_remove_msi_routing(gsi);
-
- if (gsi >= MSHV_MAX_MSI_ROUTES) {
- error_report("Invalid GSI: %u", gsi);
- return -1;
- }
-
- assert(msi_control);
-
- WITH_QEMU_LOCK_GUARD(&msi_control_mutex) {
- gsi_routes = msi_control->gsi_routes;
- route_entry = g_hash_table_lookup(gsi_routes, GINT_TO_POINTER(gsi));
- if (route_entry) {
- g_hash_table_remove(gsi_routes, GINT_TO_POINTER(gsi));
- g_free(route_entry);
- msi_control->updated = true;
- }
- }
-
- return 0;
-}
-
/* Pass an eventfd which is to be used for injecting interrupts from userland */
static int irqfd(int vm_fd, int fd, int resample_fd, uint32_t gsi,
uint32_t flags)
@@ -319,9 +292,21 @@ int mshv_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev)
return gsi;
}
-void mshv_irqchip_release_virq(int virq)
+void mshv_irqchip_release_virq(MshvState *s, int virq)
{
- remove_msi_routing(virq);
+ struct mshv_user_irq_entry *e;
+ int i;
+
+ for (i = 0; i < s->irq_routes->nr; i++) {
+ e = &s->irq_routes->entries[i];
+ if (e->gsi == virq) {
+ s->irq_routes->nr--;
+ *e = s->irq_routes->entries[s->irq_routes->nr];
+ }
+ }
+ irqchip_release_gsi(s, virq);
+
+ trace_mshv_remove_msi_routing(virq);
}
static int update_routing_entry(MshvState *s,
diff --git a/accel/stubs/mshv-stub.c b/accel/stubs/mshv-stub.c
index 998c9e2fc6c..dadf05511a6 100644
--- a/accel/stubs/mshv-stub.c
+++ b/accel/stubs/mshv-stub.c
@@ -19,7 +19,7 @@ int mshv_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev)
return -ENOSYS;
}
-void mshv_irqchip_release_virq(int virq)
+void mshv_irqchip_release_virq(MshvState *s, int virq)
{
}
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 20/45] accel/mshv: use s->irq_routes in commit_routes
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (18 preceding siblings ...)
2026-06-26 10:17 ` [PULL 19/45] accel/mshv: update s->irq_routes in release_virq Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 21/45] accel/mshv: reserve ioapic routes on s->irq_routes Paolo Bonzini
` (25 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
In mshv_irqchip_commit_routes() the entries that have been accumulated
in s->irq_routes are committed directly to MSHV's irqchip.
The old commit_msi_routing_table() fn will be removed in a subsquent commit.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260417105618.3621-12-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/system/mshv.h | 2 +-
accel/accel-irq.c | 2 +-
accel/mshv/irq.c | 7 ++++---
accel/stubs/mshv-stub.c | 2 +-
4 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/include/system/mshv.h b/include/system/mshv.h
index fa716c1ee97..46fe3fcebc2 100644
--- a/include/system/mshv.h
+++ b/include/system/mshv.h
@@ -62,8 +62,8 @@ int mshv_request_interrupt(MshvState *mshv_state, uint32_t interrupt_type, uint3
int mshv_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev);
int mshv_irqchip_update_msi_route(int virq, MSIMessage msg, PCIDevice *dev);
-void mshv_irqchip_commit_routes(void);
void mshv_irqchip_release_virq(MshvState *s, int virq);
+void mshv_irqchip_commit_routes(MshvState *s);
int mshv_irqchip_add_irqfd_notifier_gsi(const EventNotifier *n,
const EventNotifier *rn, int virq);
int mshv_irqchip_remove_irqfd_notifier_gsi(const EventNotifier *n, int virq);
diff --git a/accel/accel-irq.c b/accel/accel-irq.c
index 0cb3ef78d3a..4303e10e408 100644
--- a/accel/accel-irq.c
+++ b/accel/accel-irq.c
@@ -55,7 +55,7 @@ void accel_irqchip_commit_routes(void)
{
#ifdef CONFIG_MSHV_IS_POSSIBLE
if (mshv_msi_via_irqfd_enabled()) {
- mshv_irqchip_commit_routes();
+ mshv_irqchip_commit_routes(mshv_state);
}
#endif
if (kvm_enabled()) {
diff --git a/accel/mshv/irq.c b/accel/mshv/irq.c
index 990ce346204..9ba837f0e2e 100644
--- a/accel/mshv/irq.c
+++ b/accel/mshv/irq.c
@@ -394,16 +394,17 @@ int mshv_request_interrupt(MshvState *mshv_state, uint32_t interrupt_type, uint3
return 0;
}
-void mshv_irqchip_commit_routes(void)
+void mshv_irqchip_commit_routes(MshvState *s)
{
int ret;
- int vm_fd = mshv_state->vm;
+ int vm_fd = s->vm;
- ret = commit_msi_routing_table(vm_fd);
+ ret = ioctl(vm_fd, MSHV_SET_MSI_ROUTING, s->irq_routes);
if (ret < 0) {
error_report("Failed to commit msi routing table");
abort();
}
+ trace_mshv_commit_msi_routing_table(vm_fd, s->irq_routes->nr);
}
int mshv_irqchip_add_irqfd_notifier_gsi(const EventNotifier *event,
diff --git a/accel/stubs/mshv-stub.c b/accel/stubs/mshv-stub.c
index dadf05511a6..8b69539a85a 100644
--- a/accel/stubs/mshv-stub.c
+++ b/accel/stubs/mshv-stub.c
@@ -28,7 +28,7 @@ int mshv_irqchip_update_msi_route(int virq, MSIMessage msg, PCIDevice *dev)
return -ENOSYS;
}
-void mshv_irqchip_commit_routes(void)
+void mshv_irqchip_commit_routes(MshvState *s)
{
}
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 21/45] accel/mshv: reserve ioapic routes on s->irq_routes
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (19 preceding siblings ...)
2026-06-26 10:17 ` [PULL 20/45] accel/mshv: use s->irq_routes in commit_routes Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 22/45] accel/mshv: remove redundant msi controller Paolo Bonzini
` (24 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
We reserve 24 ioapic routes using the new functions that operate on the
mshv apic state.
commit/add_msi_routing() fn's can be removed now.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260417105618.3621-13-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/system/mshv_int.h | 1 -
accel/mshv/irq.c | 115 ++++++--------------------------------
accel/mshv/mshv-all.c | 5 --
3 files changed, 18 insertions(+), 103 deletions(-)
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index a0fa9a50f76..68b84ce2c8d 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -122,6 +122,5 @@ int mshv_configure_msr(const CPUState *cpu, const MshvMsrEntry *msrs,
/* interrupt */
void mshv_init_msicontrol(void);
-int mshv_reserve_ioapic_msi_routes(int vm_fd);
#endif
diff --git a/accel/mshv/irq.c b/accel/mshv/irq.c
index 9ba837f0e2e..52b8ac94790 100644
--- a/accel/mshv/irq.c
+++ b/accel/mshv/irq.c
@@ -36,93 +36,6 @@ void mshv_init_msicontrol(void)
msi_control->updated = false;
}
-static int add_msi_routing(uint64_t addr, uint32_t data)
-{
- struct mshv_user_irq_entry *route_entry;
- uint32_t high_addr = addr >> 32;
- uint32_t low_addr = addr & 0xFFFFFFFF;
- int gsi;
- GHashTable *gsi_routes;
-
- trace_mshv_add_msi_routing(addr, data);
-
- assert(msi_control);
-
- WITH_QEMU_LOCK_GUARD(&msi_control_mutex) {
- /* find an empty slot */
- gsi = 0;
- gsi_routes = msi_control->gsi_routes;
- while (gsi < MSHV_MAX_MSI_ROUTES) {
- route_entry = g_hash_table_lookup(gsi_routes, GINT_TO_POINTER(gsi));
- if (!route_entry) {
- break;
- }
- gsi++;
- }
- if (gsi >= MSHV_MAX_MSI_ROUTES) {
- error_report("No empty gsi slot available");
- return -1;
- }
-
- /* create new entry */
- route_entry = g_new0(struct mshv_user_irq_entry, 1);
- route_entry->gsi = gsi;
- route_entry->address_hi = high_addr;
- route_entry->address_lo = low_addr;
- route_entry->data = data;
-
- g_hash_table_insert(gsi_routes, GINT_TO_POINTER(gsi), route_entry);
- msi_control->updated = true;
- }
-
- return gsi;
-}
-
-static int commit_msi_routing_table(int vm_fd)
-{
- guint len;
- int i, ret;
- size_t table_size;
- struct mshv_user_irq_table *table;
- GHashTableIter iter;
- gpointer key, value;
-
- assert(msi_control);
-
- WITH_QEMU_LOCK_GUARD(&msi_control_mutex) {
- if (!msi_control->updated) {
- /* nothing to update */
- return 0;
- }
-
- /* Calculate the size of the table */
- len = g_hash_table_size(msi_control->gsi_routes);
- table_size = sizeof(struct mshv_user_irq_table)
- + len * sizeof(struct mshv_user_irq_entry);
- table = g_malloc0(table_size);
-
- g_hash_table_iter_init(&iter, msi_control->gsi_routes);
- i = 0;
- while (g_hash_table_iter_next(&iter, &key, &value)) {
- struct mshv_user_irq_entry *entry = value;
- table->entries[i] = *entry;
- i++;
- }
- table->nr = i;
-
- trace_mshv_commit_msi_routing_table(vm_fd, len);
-
- ret = ioctl(vm_fd, MSHV_SET_MSI_ROUTING, table);
- g_free(table);
- if (ret < 0) {
- error_report("Failed to commit msi routing table");
- return -1;
- }
- msi_control->updated = false;
- }
- return 0;
-}
-
/* Pass an eventfd which is to be used for injecting interrupts from userland */
static int irqfd(int vm_fd, int fd, int resample_fd, uint32_t gsi,
uint32_t flags)
@@ -420,37 +333,45 @@ int mshv_irqchip_remove_irqfd_notifier_gsi(const EventNotifier *event,
return irqchip_update_irqfd_notifier_gsi(event, NULL, virq, false);
}
-int mshv_reserve_ioapic_msi_routes(int vm_fd)
+static int mshv_reserve_ioapic_msi_routes(MshvState *s)
{
- int ret, gsi;
+ int ret, i;
+ int gsi = 0;
+ struct mshv_user_irq_entry blank_entry = { 0 };
/*
* Reserve GSI 0-23 for IOAPIC pins, to avoid conflicts of legacy
* peripherals with MSI-X devices
*/
- for (gsi = 0; gsi < IOAPIC_NUM_PINS; gsi++) {
- ret = add_msi_routing(0, 0);
+ for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+ /* ret = add_msi_routing(0, 0); */
+ ret = irqchip_allocate_gsi(s, &gsi);
if (ret < 0) {
- error_report("Failed to reserve GSI %d", gsi);
+ error_report("Failed to reserve GSI %d: %s", gsi, strerror(-ret));
return -1;
}
+ blank_entry.gsi = gsi;
+ add_routing_entry(s, &blank_entry);
}
- ret = commit_msi_routing_table(vm_fd);
- if (ret < 0) {
- error_report("Failed to commit reserved IOAPIC MSI routes");
- return -1;
- }
+ mshv_irqchip_commit_routes(s);
return 0;
}
void mshv_init_irq_routing(MshvState *s)
{
+ int ret;
int gsi_count = MSHV_MAX_MSI_ROUTES;
s->irq_routes = g_malloc0(sizeof(*s->irq_routes));
s->nr_allocated_irq_routes = 0;
s->gsi_count = gsi_count;
s->used_gsi_bitmap = bitmap_new(gsi_count);
+
+ ret = mshv_reserve_ioapic_msi_routes(s);
+ if (ret < 0) {
+ error_report("Failed to reserve IOAPIC MSI routes");
+ abort();
+ }
}
diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
index 2ed3ef4b841..19a44bb13de 100644
--- a/accel/mshv/mshv-all.c
+++ b/accel/mshv/mshv-all.c
@@ -264,11 +264,6 @@ static int create_vm(int mshv_fd, int *vm_fd)
return -1;
}
- ret = mshv_reserve_ioapic_msi_routes(*vm_fd);
- if (ret < 0) {
- return -1;
- }
-
ret = mshv_arch_post_init_vm(*vm_fd);
if (ret < 0) {
return -1;
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 22/45] accel/mshv: remove redundant msi controller
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (20 preceding siblings ...)
2026-06-26 10:17 ` [PULL 21/45] accel/mshv: reserve ioapic routes on s->irq_routes Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 23/45] accel/mshv: store partition proc features Paolo Bonzini
` (23 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
The remaining MsiControl infrastructure can be removed now
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260417105618.3621-14-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/system/mshv_int.h | 3 ---
accel/mshv/irq.c | 11 -----------
accel/mshv/mshv-all.c | 2 --
3 files changed, 16 deletions(-)
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index 68b84ce2c8d..5eef2e6e2cb 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -120,7 +120,4 @@ typedef struct MshvMsrEntries {
int mshv_configure_msr(const CPUState *cpu, const MshvMsrEntry *msrs,
size_t n_msrs);
-/* interrupt */
-void mshv_init_msicontrol(void);
-
#endif
diff --git a/accel/mshv/irq.c b/accel/mshv/irq.c
index 52b8ac94790..4828ac51ac2 100644
--- a/accel/mshv/irq.c
+++ b/accel/mshv/irq.c
@@ -25,17 +25,6 @@
#define MSHV_IRQFD_RESAMPLE_FLAG (1 << MSHV_IRQFD_BIT_RESAMPLE)
#define MSHV_IRQFD_BIT_DEASSIGN_FLAG (1 << MSHV_IRQFD_BIT_DEASSIGN)
-static MshvMsiControl *msi_control;
-static QemuMutex msi_control_mutex;
-
-void mshv_init_msicontrol(void)
-{
- qemu_mutex_init(&msi_control_mutex);
- msi_control = g_new0(MshvMsiControl, 1);
- msi_control->gsi_routes = g_hash_table_new(g_direct_hash, g_direct_equal);
- msi_control->updated = false;
-}
-
/* Pass an eventfd which is to be used for injecting interrupts from userland */
static int irqfd(int vm_fd, int fd, int resample_fd, uint32_t gsi,
uint32_t flags)
diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
index 19a44bb13de..b06eea2a5e9 100644
--- a/accel/mshv/mshv-all.c
+++ b/accel/mshv/mshv-all.c
@@ -505,8 +505,6 @@ static int mshv_init(AccelState *as, MachineState *ms)
mshv_init_mmio_emu();
- mshv_init_msicontrol();
-
ret = create_vm(mshv_fd, &vm_fd);
if (ret < 0) {
close(mshv_fd);
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 23/45] accel/mshv: store partition proc features
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (21 preceding siblings ...)
2026-06-26 10:17 ` [PULL 22/45] accel/mshv: remove redundant msi controller Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 24/45] target/i386/mshv: expose mshv_get_generic_regs Paolo Bonzini
` (22 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
We retrieve and store processor features on the state, so we can query
them later when deciding which MSRs to migrate.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260417105618.3621-19-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/hw/hyperv/hvhdk.h | 2 +-
include/system/mshv_int.h | 3 +++
accel/mshv/mshv-all.c | 57 +++++++++++++++++++++++++++++++++++++++
3 files changed, 61 insertions(+), 1 deletion(-)
diff --git a/include/hw/hyperv/hvhdk.h b/include/hw/hyperv/hvhdk.h
index 9ad16c47da9..9e6dcb22f61 100644
--- a/include/hw/hyperv/hvhdk.h
+++ b/include/hw/hyperv/hvhdk.h
@@ -353,7 +353,7 @@ union hv_partition_processor_features {
uint64_t lass_support:1;
uint64_t idle_hlt_intercept_support:1;
uint64_t msr_list_support:1;
- };
+ } QEMU_PACKED;
};
enum hv_translate_gva_result_code {
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index 5eef2e6e2cb..0cf68d2879e 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -14,6 +14,8 @@
#ifndef QEMU_MSHV_INT_H
#define QEMU_MSHV_INT_H
+#include "hw/hyperv/hvhdk.h"
+
#define MSHV_MSR_ENTRIES_COUNT 64
typedef struct hyperv_message hv_message;
@@ -53,6 +55,7 @@ struct MshvState {
int nr_allocated_irq_routes;
unsigned long *used_gsi_bitmap;
unsigned int gsi_count;
+ union hv_partition_processor_features processor_features;
};
typedef struct MshvMsiControl {
diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
index b06eea2a5e9..d939982eb79 100644
--- a/accel/mshv/mshv-all.c
+++ b/accel/mshv/mshv-all.c
@@ -133,6 +133,57 @@ static int get_host_partition_property(int mshv_fd, uint32_t property_code,
return 0;
}
+static int get_partition_property(int vm_fd, uint32_t feature_bank,
+ uint64_t *value)
+{
+ struct hv_input_get_partition_property in = {0};
+ struct hv_output_get_partition_property out = {0};
+ struct mshv_root_hvcall args = {0};
+ int ret;
+
+ in.property_code = feature_bank;
+
+ args.code = HVCALL_GET_PARTITION_PROPERTY;
+ args.in_sz = sizeof(in);
+ args.in_ptr = (uint64_t)∈
+ args.out_sz = sizeof(out);
+ args.out_ptr = (uint64_t)&out;
+
+ ret = ioctl(vm_fd, MSHV_ROOT_HVCALL, &args);
+ if (ret < 0) {
+ error_report("Failed to get guest partition property bank: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ *value = out.property_value;
+ return 0;
+}
+
+static int get_proc_features(int vm_fd,
+ union hv_partition_processor_features *features)
+{
+ int ret;
+
+ ret = get_partition_property(vm_fd,
+ HV_PARTITION_PROPERTY_PROCESSOR_FEATURES0,
+ features[0].as_uint64);
+ if (ret < 0) {
+ error_report("Failed to get processor features bank 0");
+ return -1;
+ }
+
+ ret = get_partition_property(vm_fd,
+ HV_PARTITION_PROPERTY_PROCESSOR_FEATURES1,
+ features[1].as_uint64);
+ if (ret < 0) {
+ error_report("Failed to get processor features bank 1");
+ return -1;
+ }
+
+ return 0;
+}
+
static int create_partition(int mshv_fd, int *vm_fd)
{
int ret;
@@ -520,6 +571,12 @@ static int mshv_init(AccelState *as, MachineState *ms)
s->vm = vm_fd;
s->fd = mshv_fd;
+
+ ret = get_proc_features(vm_fd, &s->processor_features);
+ if (ret < 0) {
+ return -1;
+ }
+
s->nr_as = 1;
s->as = g_new0(MshvAddressSpace, s->nr_as);
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 24/45] target/i386/mshv: expose mshv_get_generic_regs
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (22 preceding siblings ...)
2026-06-26 10:17 ` [PULL 23/45] accel/mshv: store partition proc features Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 25/45] accel/mshv: enable dirty page tracking Paolo Bonzini
` (21 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
We expose the fn, so we can call them from the other source files
(msr.c).
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260417105618.3621-20-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/system/mshv_int.h | 2 ++
target/i386/mshv/mshv-cpu.c | 15 ++++++---------
2 files changed, 8 insertions(+), 9 deletions(-)
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index 0cf68d2879e..ac26bfafe48 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -80,6 +80,8 @@ int mshv_configure_vcpu(const CPUState *cpu);
int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit);
int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs,
size_t n_regs);
+int mshv_get_generic_regs(CPUState *cpu, hv_register_assoc *assocs,
+ size_t n_regs);
int mshv_arch_store_vcpu_state(const CPUState *cpu);
int mshv_arch_load_vcpu_state(CPUState *cpu);
void mshv_arch_init_vcpu(CPUState *cpu);
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index e3f163ef630..a0a25cd2c05 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -111,9 +111,6 @@ static enum hv_register_name FPU_REGISTER_NAMES[26] = {
};
static int set_special_regs(const CPUState *cpu);
-static int get_generic_regs(CPUState *cpu,
- struct hv_register_assoc *assocs,
- size_t n_regs);
static void populate_fpu(const hv_register_assoc *assocs, X86CPU *x86cpu)
{
@@ -171,7 +168,7 @@ static int get_fpu(CPUState *cpu)
for (size_t i = 0; i < n_regs; i++) {
assocs[i].name = FPU_REGISTER_NAMES[i];
}
- ret = get_generic_regs(cpu, assocs, n_regs);
+ ret = mshv_get_generic_regs(cpu, assocs, n_regs);
if (ret < 0) {
error_report("failed to get special registers");
return -errno;
@@ -191,7 +188,7 @@ static int get_xc_reg(CPUState *cpu)
assocs[0].name = HV_X64_REGISTER_XFEM;
- ret = get_generic_regs(cpu, assocs, 1);
+ ret = mshv_get_generic_regs(cpu, assocs, 1);
if (ret < 0) {
error_report("failed to get xcr0");
return -1;
@@ -284,8 +281,8 @@ int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs,
return 0;
}
-static int get_generic_regs(CPUState *cpu, hv_register_assoc *assocs,
- size_t n_regs)
+int mshv_get_generic_regs(CPUState *cpu, hv_register_assoc *assocs,
+ size_t n_regs)
{
int cpu_fd = mshv_vcpufd(cpu);
int vp_index = cpu->cpu_index;
@@ -434,7 +431,7 @@ static int get_standard_regs(CPUState *cpu)
for (size_t i = 0; i < n_regs; i++) {
assocs[i].name = STANDARD_REGISTER_NAMES[i];
}
- ret = get_generic_regs(cpu, assocs, n_regs);
+ ret = mshv_get_generic_regs(cpu, assocs, n_regs);
if (ret < 0) {
error_report("failed to get standard registers");
return -1;
@@ -511,7 +508,7 @@ static int get_special_regs(CPUState *cpu)
for (size_t i = 0; i < n_regs; i++) {
assocs[i].name = SPECIAL_REGISTER_NAMES[i];
}
- ret = get_generic_regs(cpu, assocs, n_regs);
+ ret = mshv_get_generic_regs(cpu, assocs, n_regs);
if (ret < 0) {
error_report("failed to get special registers");
return -errno;
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 25/45] accel/mshv: enable dirty page tracking
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (23 preceding siblings ...)
2026-06-26 10:17 ` [PULL 24/45] target/i386/mshv: expose mshv_get_generic_regs Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 26/45] target/i386/mshv: move msr code to arch Paolo Bonzini
` (20 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
This change introduces the functions required to perform dirty page
tracking to speed up migrations. We are using the sync, global_start,
and global_stop hooks.
The sync is implemented in batches.
Before we can disable the dirty page tracking we have to set all dirty bits.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260417105618.3621-35-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/system/mshv_int.h | 4 +
accel/mshv/mem.c | 211 ++++++++++++++++++++++++++++++++++++++
accel/mshv/mshv-all.c | 3 +
3 files changed, 218 insertions(+)
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index ac26bfafe48..3bdf16ecd35 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -17,6 +17,7 @@
#include "hw/hyperv/hvhdk.h"
#define MSHV_MSR_ENTRIES_COUNT 64
+#define MSHV_DIRTY_PAGES_BATCH_SIZE 0x10000
typedef struct hyperv_message hv_message;
@@ -109,6 +110,9 @@ int mshv_guest_mem_write(uint64_t gpa, const uint8_t *data, uintptr_t size,
bool is_secure_mode);
void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section,
bool add);
+void mshv_log_sync(MemoryListener *listener, MemoryRegionSection *section);
+bool mshv_log_global_start(MemoryListener *listener, Error **errp);
+void mshv_log_global_stop(MemoryListener *listener);
/* msr */
typedef struct MshvMsrEntry {
diff --git a/accel/mshv/mem.c b/accel/mshv/mem.c
index e55c38d4db9..820f87ef0c9 100644
--- a/accel/mshv/mem.c
+++ b/accel/mshv/mem.c
@@ -12,10 +12,13 @@
#include "qemu/osdep.h"
#include "qemu/error-report.h"
+#include "qapi/error.h"
#include "linux/mshv.h"
#include "system/address-spaces.h"
#include "system/mshv.h"
#include "system/mshv_int.h"
+#include "hw/hyperv/hvhdk_mini.h"
+#include "system/physmem.h"
#include "exec/memattrs.h"
#include <sys/ioctl.h>
#include "trace.h"
@@ -211,3 +214,211 @@ void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section,
abort();
}
}
+
+static int enable_dirty_page_tracking(int vm_fd)
+{
+ int ret;
+ struct hv_input_set_partition_property in = {0};
+ struct mshv_root_hvcall args = {0};
+
+ in.property_code = HV_PARTITION_PROPERTY_GPA_PAGE_ACCESS_TRACKING;
+ in.property_value = 1;
+
+ args.code = HVCALL_SET_PARTITION_PROPERTY;
+ args.in_sz = sizeof(in);
+ args.in_ptr = (uint64_t)∈
+
+ ret = mshv_hvcall(vm_fd, &args);
+ if (ret < 0) {
+ error_report("Failed to enable dirty page tracking: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Retrieve dirty page bitmap for a GPA range, clearing the dirty bits
+ * atomically. Large ranges are handled in batches.
+ */
+static int get_dirty_log(int vm_fd, uint64_t base_pfn, uint64_t page_count,
+ unsigned long *bitmap, size_t bitmap_size)
+{
+ uint64_t batch, bitmap_offset, completed = 0;
+ struct mshv_gpap_access_bitmap args = {0};
+ int ret;
+
+ QEMU_BUILD_BUG_ON(MSHV_DIRTY_PAGES_BATCH_SIZE % BITS_PER_LONG != 0);
+ assert(bitmap_size >= ROUND_UP(page_count, BITS_PER_LONG) / 8);
+
+ while (completed < page_count) {
+ batch = MIN(MSHV_DIRTY_PAGES_BATCH_SIZE, page_count - completed);
+ bitmap_offset = completed / BITS_PER_LONG;
+
+ args.access_type = MSHV_GPAP_ACCESS_TYPE_DIRTY;
+ args.access_op = MSHV_GPAP_ACCESS_OP_CLEAR;
+ args.page_count = batch;
+ args.gpap_base = base_pfn + completed;
+ args.bitmap_ptr = (uint64_t)(bitmap + bitmap_offset);
+
+ ret = ioctl(vm_fd, MSHV_GET_GPAP_ACCESS_BITMAP, &args);
+ if (ret < 0) {
+ error_report("Failed to get dirty log (base_pfn=0x%" PRIx64
+ " batch=%" PRIu64 "): %s",
+ base_pfn + completed, batch, strerror(errno));
+ return -1;
+ }
+ completed += batch;
+ }
+
+ return 0;
+}
+
+bool mshv_log_global_start(MemoryListener *listener, Error **errp)
+{
+ int ret;
+
+ ret = enable_dirty_page_tracking(mshv_state->vm);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "Failed to enable dirty page tracking");
+ return false;
+ }
+ return true;
+}
+
+static int disable_dirty_page_tracking(int vm_fd)
+{
+ int ret;
+ struct hv_input_set_partition_property in = {0};
+ struct mshv_root_hvcall args = {0};
+
+ in.property_code = HV_PARTITION_PROPERTY_GPA_PAGE_ACCESS_TRACKING;
+ in.property_value = 0;
+
+ args.code = HVCALL_SET_PARTITION_PROPERTY;
+ args.in_sz = sizeof(in);
+ args.in_ptr = (uint64_t)∈
+
+ ret = mshv_hvcall(vm_fd, &args);
+ if (ret < 0) {
+ error_report("Failed to disable dirty page tracking: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int set_dirty_pages(int vm_fd, uint64_t base_pfn, uint64_t page_count)
+{
+ uint64_t batch, completed = 0;
+ unsigned long bitmap[MSHV_DIRTY_PAGES_BATCH_SIZE / BITS_PER_LONG];
+ struct mshv_gpap_access_bitmap args = {0};
+ int ret;
+
+ while (completed < page_count) {
+ batch = MIN(MSHV_DIRTY_PAGES_BATCH_SIZE, page_count - completed);
+
+ args.access_type = MSHV_GPAP_ACCESS_TYPE_DIRTY;
+ args.access_op = MSHV_GPAP_ACCESS_OP_SET;
+ args.page_count = batch;
+ args.gpap_base = base_pfn + completed;
+ args.bitmap_ptr = (uint64_t)bitmap;
+
+ ret = ioctl(vm_fd, MSHV_GET_GPAP_ACCESS_BITMAP, &args);
+ if (ret < 0) {
+ error_report("Failed to set dirty pages (base_pfn=0x%" PRIx64
+ " batch=%" PRIu64 "): %s",
+ base_pfn + completed, batch, strerror(errno));
+ return -1;
+ }
+ completed += batch;
+ }
+
+ return 0;
+}
+
+static bool set_dirty_bits_cb(Int128 start, Int128 len, const MemoryRegion *mr,
+ hwaddr offset_in_region, void *opaque)
+{
+ int ret, *errp = opaque;
+ hwaddr gpa, size;
+ uint64_t page_count, base_pfn;
+
+ gpa = int128_get64(start);
+ size = int128_get64(len);
+ page_count = size >> MSHV_PAGE_SHIFT;
+ base_pfn = gpa >> MSHV_PAGE_SHIFT;
+
+ if (!mr->ram || mr->readonly) {
+ return false;
+ }
+
+ if (page_count == 0) {
+ return false;
+ }
+
+ ret = set_dirty_pages(mshv_state->vm, base_pfn, page_count);
+
+ /* true aborts the iteration, which is what we want if there's an error */
+ if (ret < 0) {
+ *errp = ret;
+ return true;
+ }
+
+ return false;
+}
+
+void mshv_log_global_stop(MemoryListener *listener)
+{
+ int err = 0;
+ /* MSHV requires all dirty bits to be set before disabling tracking. */
+ FlatView *fv = address_space_to_flatview(&address_space_memory);
+ flatview_for_each_range(fv, set_dirty_bits_cb, &err);
+
+ if (err < 0) {
+ error_report("Failed to set dirty bits before disabling tracking");
+ }
+
+ disable_dirty_page_tracking(mshv_state->vm);
+}
+
+void mshv_log_sync(MemoryListener *listener, MemoryRegionSection *section)
+{
+ hwaddr size, start_addr, mr_offset;
+ uint64_t page_count, base_pfn;
+ size_t bitmap_size;
+ unsigned long *bitmap;
+ ram_addr_t ram_addr;
+ int ret;
+ MemoryRegion *mr = section->mr;
+
+ if (!memory_region_is_ram(mr) || memory_region_is_rom(mr)) {
+ return;
+ }
+
+ size = align_section(section, &start_addr);
+ if (!size) {
+ return;
+ }
+
+ page_count = size >> MSHV_PAGE_SHIFT;
+ base_pfn = start_addr >> MSHV_PAGE_SHIFT;
+ bitmap_size = ROUND_UP(page_count, BITS_PER_LONG) / 8;
+ bitmap = g_malloc0(bitmap_size);
+
+ ret = get_dirty_log(mshv_state->vm, base_pfn, page_count, bitmap,
+ bitmap_size);
+ if (ret < 0) {
+ g_free(bitmap);
+ return;
+ }
+
+ mr_offset = section->offset_within_region + start_addr -
+ section->offset_within_address_space;
+ ram_addr = memory_region_get_ram_addr(mr) + mr_offset;
+
+ physical_memory_set_dirty_lebitmap(bitmap, ram_addr, page_count);
+ g_free(bitmap);
+}
diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
index d939982eb79..9452504ac28 100644
--- a/accel/mshv/mshv-all.c
+++ b/accel/mshv/mshv-all.c
@@ -477,6 +477,9 @@ static MemoryListener mshv_memory_listener = {
.region_del = mem_region_del,
.eventfd_add = mem_ioeventfd_add,
.eventfd_del = mem_ioeventfd_del,
+ .log_sync = mshv_log_sync,
+ .log_global_start = mshv_log_global_start,
+ .log_global_stop = mshv_log_global_stop,
};
static MemoryListener mshv_io_listener = {
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 26/45] target/i386/mshv: move msr code to arch
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (24 preceding siblings ...)
2026-06-26 10:17 ` [PULL 25/45] accel/mshv: enable dirty page tracking Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 27/45] target/i386/mshv: migrate pending ints/excs Paolo Bonzini
` (19 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
The MSR code is x86 specific, hence it's better suited in the arch
tree.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260417105618.3621-18-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
{accel => target/i386}/mshv/msr.c | 0
accel/mshv/meson.build | 1 -
target/i386/mshv/meson.build | 1 +
3 files changed, 1 insertion(+), 1 deletion(-)
rename {accel => target/i386}/mshv/msr.c (100%)
diff --git a/accel/mshv/msr.c b/target/i386/mshv/msr.c
similarity index 100%
rename from accel/mshv/msr.c
rename to target/i386/mshv/msr.c
diff --git a/accel/mshv/meson.build b/accel/mshv/meson.build
index c1b1787c5e6..e433187cde0 100644
--- a/accel/mshv/meson.build
+++ b/accel/mshv/meson.build
@@ -1,6 +1,5 @@
system_ss.add(when: 'CONFIG_MSHV', if_true: files(
'irq.c',
'mem.c',
- 'msr.c',
'mshv-all.c'
))
diff --git a/target/i386/mshv/meson.build b/target/i386/mshv/meson.build
index 49f28d4a5b9..6091c218874 100644
--- a/target/i386/mshv/meson.build
+++ b/target/i386/mshv/meson.build
@@ -2,6 +2,7 @@ i386_mshv_ss = ss.source_set()
i386_mshv_ss.add(files(
'mshv-cpu.c',
+ 'msr.c',
))
i386_system_ss.add_all(when: 'CONFIG_MSHV', if_true: i386_mshv_ss)
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 27/45] target/i386/mshv: migrate pending ints/excs
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (25 preceding siblings ...)
2026-06-26 10:17 ` [PULL 26/45] target/i386/mshv: move msr code to arch Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 28/45] target/i386/mshv: migrate XSAVE state Paolo Bonzini
` (18 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
We use PENDING_INTERRUPTION, INTERRUPT_STATE, PENDING_EVENT hv registers
to map and roundtrip from/to CPUX86State.
We ignore HV_REGISTER_PENDING_EVENT1 which represent events for nested
virt contexts, as we don't support nested virt with MSHV currently.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260417105618.3621-30-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/hw/hyperv/hvgdk_mini.h | 3 +
include/system/mshv_int.h | 14 +++
target/i386/mshv/mshv-cpu.c | 168 +++++++++++++++++++++++++++++++++
3 files changed, 185 insertions(+)
diff --git a/include/hw/hyperv/hvgdk_mini.h b/include/hw/hyperv/hvgdk_mini.h
index 367519143e2..81fe7f7efb0 100644
--- a/include/hw/hyperv/hvgdk_mini.h
+++ b/include/hw/hyperv/hvgdk_mini.h
@@ -12,6 +12,9 @@
typedef enum hv_register_name {
/* Pending Interruption Register */
HV_REGISTER_PENDING_INTERRUPTION = 0x00010002,
+ HV_REGISTER_INTERRUPT_STATE = 0x00010003,
+ HV_REGISTER_PENDING_EVENT0 = 0x00010004,
+ HV_REGISTER_PENDING_EVENT1 = 0x00010005,
/* X64 User-Mode Registers */
HV_X64_REGISTER_RAX = 0x00020000,
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index 3bdf16ecd35..5a30dceeea8 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -17,6 +17,20 @@
#include "hw/hyperv/hvhdk.h"
#define MSHV_MSR_ENTRIES_COUNT 64
+
+/*
+ * Interruption-type encoding, used by the hypervisor in
+ * hv_x64_pending_interruption_register.interruption_type
+ * See TLFS 6.0 section 7.9.2, p55
+ * https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/tlfs/tlfs
+ */
+#define MSHV_HV_INTERRUPTION_TYPE_EXT_INT 0
+#define MSHV_HV_INTERRUPTION_TYPE_NMI 2
+#define MSHV_HV_INTERRUPTION_TYPE_HW_EXC 3
+#define MSHV_HV_INTERRUPTION_TYPE_SW_INT 4
+#define MSHV_HV_INTERRUPTION_TYPE_PRIV_SW_EXC 5
+#define MSHV_HV_INTERRUPTION_TYPE_SW_EXC 6
+
#define MSHV_DIRTY_PAGES_BATCH_SIZE 0x10000
typedef struct hyperv_message hv_message;
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index a0a25cd2c05..9f513a0e436 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -535,6 +535,164 @@ static int load_regs(CPUState *cpu)
return 0;
}
+static int get_vcpu_events(CPUState *cpu)
+{
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
+ struct hv_register_assoc assocs[] = {
+ { .name = HV_REGISTER_PENDING_INTERRUPTION },
+ { .name = HV_REGISTER_INTERRUPT_STATE },
+ { .name = HV_REGISTER_PENDING_EVENT0 },
+ };
+ union hv_x64_pending_interruption_register pending_int;
+ union hv_x64_interrupt_state_register int_state;
+ union hv_x64_pending_exception_event pending_exc;
+ int ret;
+
+ ret = mshv_get_generic_regs(cpu, assocs, ARRAY_SIZE(assocs));
+ if (ret < 0) {
+ error_report("failed to get vcpu event registers");
+ return -1;
+ }
+
+ pending_int.as_uint64 = assocs[0].value.reg64;
+ int_state.as_uint64 = assocs[1].value.reg64;
+ pending_exc = assocs[2].value.pending_exception_event;
+
+ /* Clear previous state. injected ints/excs are blanked w/ -1 */
+ env->interrupt_injected = -1;
+ env->soft_interrupt = 0;
+ env->exception_injected = 0;
+ env->exception_pending = 0;
+ env->exception_nr = -1;
+ env->has_error_code = 0;
+ env->error_code = 0;
+ env->exception_has_payload = 0;
+ env->exception_payload = 0;
+ env->nmi_injected = 0;
+
+ if (pending_int.interruption_pending) {
+ switch (pending_int.interruption_type) {
+ case MSHV_HV_INTERRUPTION_TYPE_EXT_INT:
+ env->interrupt_injected = pending_int.interruption_vector;
+ break;
+ case MSHV_HV_INTERRUPTION_TYPE_NMI:
+ env->nmi_injected = 1;
+ break;
+ case MSHV_HV_INTERRUPTION_TYPE_HW_EXC:
+ env->exception_injected = 1;
+ env->exception_nr = pending_int.interruption_vector;
+ env->has_error_code = pending_int.deliver_error_code;
+ env->error_code = pending_int.error_code;
+ break;
+ case MSHV_HV_INTERRUPTION_TYPE_SW_INT:
+ env->interrupt_injected = pending_int.interruption_vector;
+ env->soft_interrupt = 1;
+ break;
+ case MSHV_HV_INTERRUPTION_TYPE_SW_EXC:
+ case MSHV_HV_INTERRUPTION_TYPE_PRIV_SW_EXC:
+ env->exception_injected = 1;
+ env->exception_nr = pending_int.interruption_vector;
+ env->has_error_code = pending_int.deliver_error_code;
+ env->error_code = pending_int.error_code;
+ break;
+ default:
+ error_report("unknown interruption type %u",
+ pending_int.interruption_type);
+ return -EINVAL;
+ }
+ }
+
+ /* disabled for one instr after STI, MOV/POP SS, see hvf_store_events() */
+ if (int_state.interrupt_shadow) {
+ env->hflags |= HF_INHIBIT_IRQ_MASK;
+ } else {
+ env->hflags &= ~HF_INHIBIT_IRQ_MASK;
+ }
+
+ /* see kvm_get_vcpu_events(), hvf_store_events() */
+ if (int_state.nmi_masked) {
+ env->hflags2 |= HF2_NMI_MASK;
+ } else {
+ env->hflags2 &= ~HF2_NMI_MASK;
+ }
+
+ /* HV_REGISTER_PENDING_EVENT0: pending exception not yet injected */
+ if (pending_exc.event_pending) {
+ env->exception_pending = 1;
+ env->exception_nr = pending_exc.vector;
+ env->has_error_code = pending_exc.deliver_error_code;
+ env->error_code = pending_exc.error_code;
+ env->exception_has_payload = (pending_exc.exception_parameter != 0);
+ env->exception_payload = pending_exc.exception_parameter;
+ }
+
+ /*
+ * Ignoring HV_REGISTER_PENDING_EVENT1, virtualization fault events, MSHV
+ * does not support nested virtualization.
+ */
+
+ return 0;
+}
+
+static int set_vcpu_events(const CPUState *cpu)
+{
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
+ union hv_x64_pending_interruption_register pending_int = { 0 };
+ union hv_x64_interrupt_state_register int_state = { 0 };
+ union hv_x64_pending_exception_event pending_exc = { 0 };
+ struct hv_register_assoc assocs[3];
+ int ret;
+
+ /* build pending_int from CPUX86State */
+ if (env->exception_injected) {
+ pending_int.interruption_pending = 1;
+ pending_int.interruption_type = MSHV_HV_INTERRUPTION_TYPE_HW_EXC;
+ pending_int.interruption_vector = env->exception_nr;
+ pending_int.deliver_error_code = env->has_error_code;
+ pending_int.error_code = env->error_code;
+ } else if (env->nmi_injected) {
+ pending_int.interruption_pending = 1;
+ pending_int.interruption_type = MSHV_HV_INTERRUPTION_TYPE_NMI;
+ pending_int.interruption_vector = EXCP02_NMI;
+ } else if (env->interrupt_injected >= 0) {
+ pending_int.interruption_pending = 1;
+ pending_int.interruption_type = env->soft_interrupt
+ ? MSHV_HV_INTERRUPTION_TYPE_SW_INT
+ : MSHV_HV_INTERRUPTION_TYPE_EXT_INT;
+ pending_int.interruption_vector = env->interrupt_injected;
+ }
+
+ /* build int_state, normalize to bool */
+ int_state.interrupt_shadow = !!(env->hflags & HF_INHIBIT_IRQ_MASK);
+ int_state.nmi_masked = !!(env->hflags2 & HF2_NMI_MASK);
+
+ /* build pending_exc */
+ if (env->exception_pending) {
+ pending_exc.event_pending = 1;
+ pending_exc.vector = env->exception_nr;
+ pending_exc.deliver_error_code = env->has_error_code;
+ pending_exc.error_code = env->error_code;
+ pending_exc.exception_parameter = env->exception_payload;
+ }
+
+ assocs[0].name = HV_REGISTER_PENDING_INTERRUPTION;
+ assocs[0].value.reg64 = pending_int.as_uint64;
+ assocs[1].name = HV_REGISTER_INTERRUPT_STATE;
+ assocs[1].value.reg64 = int_state.as_uint64;
+ assocs[2].name = HV_REGISTER_PENDING_EVENT0;
+ assocs[2].value.pending_exception_event = pending_exc;
+
+ ret = mshv_set_generic_regs(cpu, assocs, ARRAY_SIZE(assocs));
+ if (ret < 0) {
+ error_report("failed to set vcpu event registers");
+ return -1;
+ }
+
+ return 0;
+}
+
int mshv_arch_load_vcpu_state(CPUState *cpu)
{
int ret;
@@ -559,6 +717,11 @@ int mshv_arch_load_vcpu_state(CPUState *cpu)
return ret;
}
+ ret = get_vcpu_events(cpu);
+ if (ret < 0) {
+ return ret;
+ }
+
return 0;
}
@@ -1100,6 +1263,11 @@ int mshv_arch_store_vcpu_state(const CPUState *cpu)
return ret;
}
+ ret = set_vcpu_events(cpu);
+ if (ret < 0) {
+ return ret;
+ }
+
return 0;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 28/45] target/i386/mshv: migrate XSAVE state
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (26 preceding siblings ...)
2026-06-26 10:17 ` [PULL 27/45] target/i386/mshv: migrate pending ints/excs Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 29/45] target/i386/mshv: reconstruct hflags after load Paolo Bonzini
` (17 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
We implement fn's that roundtrip XSAVE state in migration. We are using
the xsave_helper routines to move individual components from CPUX86State
to an xsave_buf and then we have to compact the buffer to XSAVEC format,
which is what the hypervisor expects.
And the same applies in the other direction for restoring state from the
hypervisor.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260417105618.3621-32-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
target/i386/mshv/mshv-cpu.c | 100 +++++++++++++++++++++++++++++++++++-
target/i386/meson.build | 2 +-
2 files changed, 100 insertions(+), 2 deletions(-)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 9f513a0e436..bbf874f6413 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -112,6 +112,78 @@ static enum hv_register_name FPU_REGISTER_NAMES[26] = {
static int set_special_regs(const CPUState *cpu);
+static int get_xsave_state(CPUState *cpu)
+{
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
+ int cpu_fd = mshv_vcpufd(cpu);
+ int ret;
+ void *xsavec_buf;
+ const size_t page = HV_HYP_PAGE_SIZE;
+ size_t xsavec_buf_len = page;
+
+ /* TODO: should properly determine xsavec size based on CPUID */
+ xsavec_buf = qemu_memalign(page, xsavec_buf_len);
+ memset(xsavec_buf, 0, xsavec_buf_len);
+
+ struct mshv_get_set_vp_state args = {
+ .type = MSHV_VP_STATE_XSAVE,
+ .buf_sz = xsavec_buf_len,
+ .buf_ptr = (uintptr_t)xsavec_buf,
+ };
+
+ ret = ioctl(cpu_fd, MSHV_GET_VP_STATE, &args);
+ if (ret < 0) {
+ error_report("failed to get xsave state: %s", strerror(errno));
+ return -errno;
+ }
+
+ ret = decompact_xsave_area(xsavec_buf, xsavec_buf_len, env);
+ g_free(xsavec_buf);
+ if (ret < 0) {
+ error_report("failed to decompact xsave area");
+ return ret;
+ }
+ x86_cpu_xrstor_all_areas(x86cpu, env->xsave_buf, env->xsave_buf_len);
+
+ return 0;
+}
+
+static int set_xsave_state(const CPUState *cpu)
+{
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
+ int cpu_fd = mshv_vcpufd(cpu);
+ int ret;
+ void *xsavec_buf;
+ size_t page = HV_HYP_PAGE_SIZE, xsavec_buf_len;
+
+ /* allocate and populate compacted buffer */
+ xsavec_buf = qemu_memalign(page, page);
+ xsavec_buf_len = page;
+
+ /* save registers to standard format buffer */
+ x86_cpu_xsave_all_areas(x86cpu, env->xsave_buf, env->xsave_buf_len);
+
+ /* store compacted version of xsave area in xsavec_buf */
+ compact_xsave_area(env, xsavec_buf, xsavec_buf_len);
+
+ struct mshv_get_set_vp_state args = {
+ .type = MSHV_VP_STATE_XSAVE,
+ .buf_sz = xsavec_buf_len,
+ .buf_ptr = (uintptr_t)xsavec_buf,
+ };
+
+ ret = ioctl(cpu_fd, MSHV_SET_VP_STATE, &args);
+ g_free(xsavec_buf);
+ if (ret < 0) {
+ error_report("failed to set xsave state: %s", strerror(errno));
+ return -errno;
+ }
+
+ return 0;
+}
+
static void populate_fpu(const hv_register_assoc *assocs, X86CPU *x86cpu)
{
union hv_register_value value;
@@ -717,6 +789,11 @@ int mshv_arch_load_vcpu_state(CPUState *cpu)
return ret;
}
+ ret = get_xsave_state(cpu);
+ if (ret < 0) {
+ return ret;
+ }
+
ret = get_vcpu_events(cpu);
if (ret < 0) {
return ret;
@@ -1263,6 +1340,11 @@ int mshv_arch_store_vcpu_state(const CPUState *cpu)
return ret;
}
+ ret = set_xsave_state(cpu);
+ if (ret < 0) {
+ return ret;
+ }
+
ret = set_vcpu_events(cpu);
if (ret < 0) {
return ret;
@@ -1817,9 +1899,10 @@ void mshv_arch_init_vcpu(CPUState *cpu)
X86CPU *x86_cpu = X86_CPU(cpu);
CPUX86State *env = &x86_cpu->env;
AccelCPUState *state = cpu->accel;
- size_t page = HV_HYP_PAGE_SIZE;
+ size_t page = HV_HYP_PAGE_SIZE, xsave_len;
void *mem = qemu_memalign(page, 2 * page);
int ret;
+ X86XSaveHeader *header;
/* sanity check, to make sure we don't overflow the page */
QEMU_BUILD_BUG_ON((MAX_REGISTER_COUNT
@@ -1833,6 +1916,17 @@ void mshv_arch_init_vcpu(CPUState *cpu)
env->emu_mmio_buf = g_new(char, 4096);
+ /* Initialize XSAVE buffer page-aligned */
+ /* TODO: pick proper size based on CPUID */
+ xsave_len = page;
+ env->xsave_buf = qemu_memalign(page, xsave_len);
+ env->xsave_buf_len = xsave_len;
+ memset(env->xsave_buf, 0, env->xsave_buf_len);
+
+ /* we need to set the compacted format bit in xsave header for mshv */
+ header = (X86XSaveHeader *)(env->xsave_buf + sizeof(X86LegacyXSaveArea));
+ header->xcomp_bv = header->xstate_bv | (1ULL << 63);
+
/*
* TODO: populate topology info:
* X86CPUTopoInfo *topo_info = &env->topo_info;
@@ -1857,6 +1951,10 @@ void mshv_arch_destroy_vcpu(CPUState *cpu)
g_free(state->hvcall_args.base);
state->hvcall_args = (MshvHvCallArgs){0};
g_clear_pointer(&env->emu_mmio_buf, g_free);
+
+ qemu_vfree(env->xsave_buf);
+ env->xsave_buf = NULL;
+ env->xsave_buf_len = 0;
}
uint32_t mshv_get_supported_cpuid(uint32_t func, uint32_t idx, int reg)
diff --git a/target/i386/meson.build b/target/i386/meson.build
index 80062d1d0d8..b4fbf8a5374 100644
--- a/target/i386/meson.build
+++ b/target/i386/meson.build
@@ -12,7 +12,7 @@ i386_ss.add(when: 'CONFIG_KVM', if_true: files('xsave_helper.c', 'host-cpu.c'))
i386_ss.add(when: 'CONFIG_HVF', if_true: files('xsave_helper.c', 'host-cpu.c'))
i386_ss.add(when: 'CONFIG_WHPX', if_true: files('xsave_helper.c', 'host-cpu.c'))
i386_ss.add(when: 'CONFIG_NVMM', if_true: files('host-cpu.c'))
-i386_ss.add(when: 'CONFIG_MSHV', if_true: files('host-cpu.c'))
+i386_ss.add(when: 'CONFIG_MSHV', if_true: files('xsave_helper.c', 'host-cpu.c'))
i386_system_ss = ss.source_set()
i386_system_ss.add(files(
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 29/45] target/i386/mshv: reconstruct hflags after load
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (27 preceding siblings ...)
2026-06-26 10:17 ` [PULL 28/45] target/i386/mshv: migrate XSAVE state Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 30/45] target/i386/mshv: migrate MSRs Paolo Bonzini
` (16 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
hflags is a cached bitmap derived from standard and special regs. We
want to reconstruct this state after regs and sregs have been read from
the hypervisor, similar to how it's one in other accelerators.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260417105618.3621-33-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
target/i386/mshv/mshv-cpu.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index bbf874f6413..8bd63bd9628 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -765,6 +765,16 @@ static int set_vcpu_events(const CPUState *cpu)
return 0;
}
+static int update_hflags(CPUState *cpu)
+{
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
+
+ x86_update_hflags(env);
+
+ return 0;
+}
+
int mshv_arch_load_vcpu_state(CPUState *cpu)
{
int ret;
@@ -779,6 +789,9 @@ int mshv_arch_load_vcpu_state(CPUState *cpu)
return ret;
}
+ /* INVARIANT: hflags are derived from regs+sregs, need to get both first */
+ update_hflags(cpu);
+
ret = get_xc_reg(cpu);
if (ret < 0) {
return ret;
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 30/45] target/i386/mshv: migrate MSRs
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (28 preceding siblings ...)
2026-06-26 10:17 ` [PULL 29/45] target/i386/mshv: reconstruct hflags after load Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 31/45] target/i386/mshv: migrate MTRR MSRs Paolo Bonzini
` (15 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
In this change the we rewrite the existing MSR logic to make MSRs
migratable:
- we map them on existing QEMU fields in the CPU. A table and a macro
MSHV_ENV_FIELD is used to associate a HV register name to the their msr
index and their offset in the cpu state struct. The list is not
exhaustive and will be extended in follow-up commits.
- mshv_set/get_msrs() fns are called in the arch_load/store_vcpu_state()
fns. they use use generic registers ioctl's and map the input/output
via load/store_to/from_env() from/to the hv register content to the
cpu state representation.
- init_msrs() has been moved from mshv-vcpu to the msr source file
- we need to perform some filtering of MSR because before writing and
reading, because the hvcalls will fail if the partition doesn't
support a given MSRs.
- Some MSRs are partition-wide and so we will only write the to on the
BSP.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260417105618.3621-21-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/hw/hyperv/hvgdk_mini.h | 14 +
include/system/mshv_int.h | 16 +-
target/i386/mshv/mshv-cpu.c | 39 +--
target/i386/mshv/msr.c | 574 ++++++++++++++-------------------
4 files changed, 264 insertions(+), 379 deletions(-)
diff --git a/include/hw/hyperv/hvgdk_mini.h b/include/hw/hyperv/hvgdk_mini.h
index 81fe7f7efb0..b00ec8a6367 100644
--- a/include/hw/hyperv/hvgdk_mini.h
+++ b/include/hw/hyperv/hvgdk_mini.h
@@ -9,6 +9,19 @@
#define MSHV_IOCTL 0xB8
+/* Hyper-V specific model specific registers (MSRs) */
+
+/* HV_X64_SYNTHETIC_MSR */
+#define HV_X64_MSR_GUEST_OS_ID 0x40000000
+#define HV_X64_MSR_HYPERCALL 0x40000001
+#define HV_X64_MSR_VP_INDEX 0x40000002
+#define HV_X64_MSR_RESET 0x40000003
+#define HV_X64_MSR_VP_RUNTIME 0x40000010
+#define HV_X64_MSR_TIME_REF_COUNT 0x40000020
+#define HV_X64_MSR_REFERENCE_TSC 0x40000021
+#define HV_X64_MSR_TSC_FREQUENCY 0x40000022
+#define HV_X64_MSR_APIC_FREQUENCY 0x40000023
+
typedef enum hv_register_name {
/* Pending Interruption Register */
HV_REGISTER_PENDING_INTERRUPTION = 0x00010002,
@@ -161,6 +174,7 @@ typedef enum hv_register_name {
HV_X64_REGISTER_MSR_IA32_MISC_ENABLE = 0x000800A0,
/* Misc */
+ HV_X64_REGISTER_HYPERCALL = 0x00090001,
HV_REGISTER_GUEST_OS_ID = 0x00090002,
HV_REGISTER_REFERENCE_TSC = 0x00090017,
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index 5a30dceeea8..ba3073888ac 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -129,18 +129,8 @@ bool mshv_log_global_start(MemoryListener *listener, Error **errp);
void mshv_log_global_stop(MemoryListener *listener);
/* msr */
-typedef struct MshvMsrEntry {
- uint32_t index;
- uint32_t reserved;
- uint64_t data;
-} MshvMsrEntry;
-
-typedef struct MshvMsrEntries {
- MshvMsrEntry entries[MSHV_MSR_ENTRIES_COUNT];
- uint32_t nmsrs;
-} MshvMsrEntries;
-
-int mshv_configure_msr(const CPUState *cpu, const MshvMsrEntry *msrs,
- size_t n_msrs);
+int mshv_init_msrs(const CPUState *cpu);
+int mshv_get_msrs(CPUState *cpu);
+int mshv_set_msrs(const CPUState *cpu);
#endif
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 8bd63bd9628..a383e0ab104 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -802,6 +802,11 @@ int mshv_arch_load_vcpu_state(CPUState *cpu)
return ret;
}
+ ret = mshv_get_msrs(cpu);
+ if (ret < 0) {
+ return ret;
+ }
+
ret = get_xsave_state(cpu);
if (ret < 0) {
return ret;
@@ -1353,6 +1358,11 @@ int mshv_arch_store_vcpu_state(const CPUState *cpu)
return ret;
}
+ ret = mshv_set_msrs(cpu);
+ if (ret < 0) {
+ return ret;
+ }
+
ret = set_xsave_state(cpu);
if (ret < 0) {
return ret;
@@ -1880,33 +1890,6 @@ void mshv_init_mmio_emu(void)
init_emu(&mshv_x86_emul_ops);
}
-static int init_msrs(const CPUState *cpu)
-{
- int ret;
- uint64_t d_t = MSR_MTRR_ENABLE | MSR_MTRR_MEM_TYPE_WB;
-
- const struct hv_register_assoc assocs[] = {
- { .name = HV_X64_REGISTER_SYSENTER_CS, .value.reg64 = 0x0 },
- { .name = HV_X64_REGISTER_SYSENTER_ESP, .value.reg64 = 0x0 },
- { .name = HV_X64_REGISTER_SYSENTER_EIP, .value.reg64 = 0x0 },
- { .name = HV_X64_REGISTER_STAR, .value.reg64 = 0x0 },
- { .name = HV_X64_REGISTER_CSTAR, .value.reg64 = 0x0 },
- { .name = HV_X64_REGISTER_LSTAR, .value.reg64 = 0x0 },
- { .name = HV_X64_REGISTER_KERNEL_GS_BASE, .value.reg64 = 0x0 },
- { .name = HV_X64_REGISTER_SFMASK, .value.reg64 = 0x0 },
- { .name = HV_X64_REGISTER_MSR_MTRR_DEF_TYPE, .value.reg64 = d_t },
- };
- QEMU_BUILD_BUG_ON(ARRAY_SIZE(assocs) > MSHV_MSR_ENTRIES_COUNT);
-
- ret = mshv_set_generic_regs(cpu, assocs, ARRAY_SIZE(assocs));
- if (ret < 0) {
- error_report("failed to put msrs");
- return -1;
- }
-
- return 0;
-}
-
void mshv_arch_init_vcpu(CPUState *cpu)
{
X86CPU *x86_cpu = X86_CPU(cpu);
@@ -1948,7 +1931,7 @@ void mshv_arch_init_vcpu(CPUState *cpu)
ret = init_cpuid2(cpu);
assert(ret == 0);
- ret = init_msrs(cpu);
+ ret = mshv_init_msrs(cpu);
assert(ret == 0);
ret = init_lint(cpu);
diff --git a/target/i386/mshv/msr.c b/target/i386/mshv/msr.c
index e6e5baef507..7447467dbf5 100644
--- a/target/i386/mshv/msr.c
+++ b/target/i386/mshv/msr.c
@@ -14,362 +14,260 @@
#include "hw/hyperv/hvgdk_mini.h"
#include "linux/mshv.h"
#include "qemu/error-report.h"
+#include "cpu.h"
-static uint32_t supported_msrs[64] = {
- IA32_MSR_TSC,
- IA32_MSR_EFER,
- IA32_MSR_KERNEL_GS_BASE,
- IA32_MSR_APIC_BASE,
- IA32_MSR_PAT,
- IA32_MSR_SYSENTER_CS,
- IA32_MSR_SYSENTER_ESP,
- IA32_MSR_SYSENTER_EIP,
- IA32_MSR_STAR,
- IA32_MSR_LSTAR,
- IA32_MSR_CSTAR,
- IA32_MSR_SFMASK,
- IA32_MSR_MTRR_DEF_TYPE,
- IA32_MSR_MTRR_PHYSBASE0,
- IA32_MSR_MTRR_PHYSMASK0,
- IA32_MSR_MTRR_PHYSBASE1,
- IA32_MSR_MTRR_PHYSMASK1,
- IA32_MSR_MTRR_PHYSBASE2,
- IA32_MSR_MTRR_PHYSMASK2,
- IA32_MSR_MTRR_PHYSBASE3,
- IA32_MSR_MTRR_PHYSMASK3,
- IA32_MSR_MTRR_PHYSBASE4,
- IA32_MSR_MTRR_PHYSMASK4,
- IA32_MSR_MTRR_PHYSBASE5,
- IA32_MSR_MTRR_PHYSMASK5,
- IA32_MSR_MTRR_PHYSBASE6,
- IA32_MSR_MTRR_PHYSMASK6,
- IA32_MSR_MTRR_PHYSBASE7,
- IA32_MSR_MTRR_PHYSMASK7,
- IA32_MSR_MTRR_FIX64K_00000,
- IA32_MSR_MTRR_FIX16K_80000,
- IA32_MSR_MTRR_FIX16K_A0000,
- IA32_MSR_MTRR_FIX4K_C0000,
- IA32_MSR_MTRR_FIX4K_C8000,
- IA32_MSR_MTRR_FIX4K_D0000,
- IA32_MSR_MTRR_FIX4K_D8000,
- IA32_MSR_MTRR_FIX4K_E0000,
- IA32_MSR_MTRR_FIX4K_E8000,
- IA32_MSR_MTRR_FIX4K_F0000,
- IA32_MSR_MTRR_FIX4K_F8000,
- IA32_MSR_TSC_AUX,
- IA32_MSR_DEBUG_CTL,
- HV_X64_MSR_GUEST_OS_ID,
- HV_X64_MSR_SINT0,
- HV_X64_MSR_SINT1,
- HV_X64_MSR_SINT2,
- HV_X64_MSR_SINT3,
- HV_X64_MSR_SINT4,
- HV_X64_MSR_SINT5,
- HV_X64_MSR_SINT6,
- HV_X64_MSR_SINT7,
- HV_X64_MSR_SINT8,
- HV_X64_MSR_SINT9,
- HV_X64_MSR_SINT10,
- HV_X64_MSR_SINT11,
- HV_X64_MSR_SINT12,
- HV_X64_MSR_SINT13,
- HV_X64_MSR_SINT14,
- HV_X64_MSR_SINT15,
- HV_X64_MSR_SCONTROL,
- HV_X64_MSR_SIEFP,
- HV_X64_MSR_SIMP,
- HV_X64_MSR_REFERENCE_TSC,
- HV_X64_MSR_EOM,
+#define MSHV_ENV_FIELD(env, offset) (*(uint64_t *)((char *)(env) + (offset)))
+
+typedef struct MshvMsrEnvMap {
+ uint32_t msr_index;
+ uint32_t hv_name;
+ ptrdiff_t env_offset;
+} MshvMsrEnvMap;
+
+/* We assert that 64bit access to sysenter_cs is safe because of padding */
+QEMU_BUILD_BUG_ON(offsetof(CPUX86State, sysenter_esp) -
+ offsetof(CPUX86State, sysenter_cs)
+ < sizeof(uint64_t));
+
+/* Those MSRs have a direct mapping to fields in CPUX86State */
+static const MshvMsrEnvMap msr_env_map[] = {
+ /* Architectural */
+ { IA32_MSR_EFER, HV_X64_REGISTER_EFER, offsetof(CPUX86State, efer) },
+ { IA32_MSR_PAT, HV_X64_REGISTER_PAT, offsetof(CPUX86State, pat) },
+
+ /* Syscall */
+ { IA32_MSR_SYSENTER_CS, HV_X64_REGISTER_SYSENTER_CS,
+ offsetof(CPUX86State, sysenter_cs) },
+ { IA32_MSR_SYSENTER_ESP, HV_X64_REGISTER_SYSENTER_ESP,
+ offsetof(CPUX86State, sysenter_esp) },
+ { IA32_MSR_SYSENTER_EIP, HV_X64_REGISTER_SYSENTER_EIP,
+ offsetof(CPUX86State, sysenter_eip) },
+ { IA32_MSR_STAR, HV_X64_REGISTER_STAR,
+ offsetof(CPUX86State, star) },
+ { IA32_MSR_LSTAR, HV_X64_REGISTER_LSTAR,
+ offsetof(CPUX86State, lstar) },
+ { IA32_MSR_CSTAR, HV_X64_REGISTER_CSTAR,
+ offsetof(CPUX86State, cstar) },
+ { IA32_MSR_SFMASK, HV_X64_REGISTER_SFMASK,
+ offsetof(CPUX86State, fmask) },
+ { IA32_MSR_KERNEL_GS_BASE, HV_X64_REGISTER_KERNEL_GS_BASE,
+ offsetof(CPUX86State, kernelgsbase) },
+
+ /* TSC-related */
+ { IA32_MSR_TSC, HV_X64_REGISTER_TSC,
+ offsetof(CPUX86State, tsc) },
+ { IA32_MSR_TSC_AUX, HV_X64_REGISTER_TSC_AUX,
+ offsetof(CPUX86State, tsc_aux) },
+ { IA32_MSR_TSC_ADJUST, HV_X64_REGISTER_TSC_ADJUST,
+ offsetof(CPUX86State, tsc_adjust) },
+
+ /* Hyper-V per-partition MSRs */
+ { HV_X64_MSR_HYPERCALL, HV_X64_REGISTER_HYPERCALL,
+ offsetof(CPUX86State, msr_hv_hypercall) },
+ { HV_X64_MSR_GUEST_OS_ID, HV_REGISTER_GUEST_OS_ID,
+ offsetof(CPUX86State, msr_hv_guest_os_id) },
+ { HV_X64_MSR_REFERENCE_TSC, HV_REGISTER_REFERENCE_TSC,
+ offsetof(CPUX86State, msr_hv_tsc) },
+
+ /* Hyper-V MSRs (non-SINT) */
+ { HV_X64_MSR_SCONTROL, HV_REGISTER_SCONTROL,
+ offsetof(CPUX86State, msr_hv_synic_control) },
+ { HV_X64_MSR_SIEFP, HV_REGISTER_SIEFP,
+ offsetof(CPUX86State, msr_hv_synic_evt_page) },
+ { HV_X64_MSR_SIMP, HV_REGISTER_SIMP,
+ offsetof(CPUX86State, msr_hv_synic_msg_page) },
+
+ /* Other */
+
+ /* TODO: find out processor features that correlate to unsupported MSRs. */
+ /* { IA32_MSR_MISC_ENABLE, HV_X64_REGISTER_MSR_IA32_MISC_ENABLE, */
+ /* offsetof(CPUX86State, msr_ia32_misc_enable) }, */
+ /* { IA32_MSR_BNDCFGS, HV_X64_REGISTER_BNDCFGS, */
+ /* offsetof(CPUX86State, msr_bndcfgs) }, */
+ { IA32_MSR_SPEC_CTRL, HV_X64_REGISTER_SPEC_CTRL,
+ offsetof(CPUX86State, spec_ctrl) },
};
-static const size_t msr_count = ARRAY_SIZE(supported_msrs);
-static int compare_msr_index(const void *a, const void *b)
+int mshv_init_msrs(const CPUState *cpu)
{
- return *(uint32_t *)a - *(uint32_t *)b;
-}
-
-__attribute__((constructor))
-static void init_sorted_msr_map(void)
-{
- qsort(supported_msrs, msr_count, sizeof(uint32_t), compare_msr_index);
-}
-
-static int mshv_is_supported_msr(uint32_t msr)
-{
- return bsearch(&msr, supported_msrs, msr_count, sizeof(uint32_t),
- compare_msr_index) != NULL;
-}
-
-static int mshv_msr_to_hv_reg_name(uint32_t msr, uint32_t *hv_reg)
-{
- switch (msr) {
- case IA32_MSR_TSC:
- *hv_reg = HV_X64_REGISTER_TSC;
- return 0;
- case IA32_MSR_EFER:
- *hv_reg = HV_X64_REGISTER_EFER;
- return 0;
- case IA32_MSR_KERNEL_GS_BASE:
- *hv_reg = HV_X64_REGISTER_KERNEL_GS_BASE;
- return 0;
- case IA32_MSR_APIC_BASE:
- *hv_reg = HV_X64_REGISTER_APIC_BASE;
- return 0;
- case IA32_MSR_PAT:
- *hv_reg = HV_X64_REGISTER_PAT;
- return 0;
- case IA32_MSR_SYSENTER_CS:
- *hv_reg = HV_X64_REGISTER_SYSENTER_CS;
- return 0;
- case IA32_MSR_SYSENTER_ESP:
- *hv_reg = HV_X64_REGISTER_SYSENTER_ESP;
- return 0;
- case IA32_MSR_SYSENTER_EIP:
- *hv_reg = HV_X64_REGISTER_SYSENTER_EIP;
- return 0;
- case IA32_MSR_STAR:
- *hv_reg = HV_X64_REGISTER_STAR;
- return 0;
- case IA32_MSR_LSTAR:
- *hv_reg = HV_X64_REGISTER_LSTAR;
- return 0;
- case IA32_MSR_CSTAR:
- *hv_reg = HV_X64_REGISTER_CSTAR;
- return 0;
- case IA32_MSR_SFMASK:
- *hv_reg = HV_X64_REGISTER_SFMASK;
- return 0;
- case IA32_MSR_MTRR_CAP:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_CAP;
- return 0;
- case IA32_MSR_MTRR_DEF_TYPE:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_DEF_TYPE;
- return 0;
- case IA32_MSR_MTRR_PHYSBASE0:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE0;
- return 0;
- case IA32_MSR_MTRR_PHYSMASK0:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK0;
- return 0;
- case IA32_MSR_MTRR_PHYSBASE1:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE1;
- return 0;
- case IA32_MSR_MTRR_PHYSMASK1:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK1;
- return 0;
- case IA32_MSR_MTRR_PHYSBASE2:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE2;
- return 0;
- case IA32_MSR_MTRR_PHYSMASK2:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK2;
- return 0;
- case IA32_MSR_MTRR_PHYSBASE3:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE3;
- return 0;
- case IA32_MSR_MTRR_PHYSMASK3:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK3;
- return 0;
- case IA32_MSR_MTRR_PHYSBASE4:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE4;
- return 0;
- case IA32_MSR_MTRR_PHYSMASK4:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK4;
- return 0;
- case IA32_MSR_MTRR_PHYSBASE5:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE5;
- return 0;
- case IA32_MSR_MTRR_PHYSMASK5:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK5;
- return 0;
- case IA32_MSR_MTRR_PHYSBASE6:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE6;
- return 0;
- case IA32_MSR_MTRR_PHYSMASK6:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK6;
- return 0;
- case IA32_MSR_MTRR_PHYSBASE7:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE7;
- return 0;
- case IA32_MSR_MTRR_PHYSMASK7:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK7;
- return 0;
- case IA32_MSR_MTRR_FIX64K_00000:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX64K00000;
- return 0;
- case IA32_MSR_MTRR_FIX16K_80000:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX16K80000;
- return 0;
- case IA32_MSR_MTRR_FIX16K_A0000:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX16KA0000;
- return 0;
- case IA32_MSR_MTRR_FIX4K_C0000:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KC0000;
- return 0;
- case IA32_MSR_MTRR_FIX4K_C8000:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KC8000;
- return 0;
- case IA32_MSR_MTRR_FIX4K_D0000:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KD0000;
- return 0;
- case IA32_MSR_MTRR_FIX4K_D8000:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KD8000;
- return 0;
- case IA32_MSR_MTRR_FIX4K_E0000:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KE0000;
- return 0;
- case IA32_MSR_MTRR_FIX4K_E8000:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KE8000;
- return 0;
- case IA32_MSR_MTRR_FIX4K_F0000:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KF0000;
- return 0;
- case IA32_MSR_MTRR_FIX4K_F8000:
- *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KF8000;
- return 0;
- case IA32_MSR_TSC_AUX:
- *hv_reg = HV_X64_REGISTER_TSC_AUX;
- return 0;
- case IA32_MSR_BNDCFGS:
- *hv_reg = HV_X64_REGISTER_BNDCFGS;
- return 0;
- case IA32_MSR_DEBUG_CTL:
- *hv_reg = HV_X64_REGISTER_DEBUG_CTL;
- return 0;
- case IA32_MSR_TSC_ADJUST:
- *hv_reg = HV_X64_REGISTER_TSC_ADJUST;
- return 0;
- case IA32_MSR_SPEC_CTRL:
- *hv_reg = HV_X64_REGISTER_SPEC_CTRL;
- return 0;
- case HV_X64_MSR_GUEST_OS_ID:
- *hv_reg = HV_REGISTER_GUEST_OS_ID;
- return 0;
- case HV_X64_MSR_SINT0:
- *hv_reg = HV_REGISTER_SINT0;
- return 0;
- case HV_X64_MSR_SINT1:
- *hv_reg = HV_REGISTER_SINT1;
- return 0;
- case HV_X64_MSR_SINT2:
- *hv_reg = HV_REGISTER_SINT2;
- return 0;
- case HV_X64_MSR_SINT3:
- *hv_reg = HV_REGISTER_SINT3;
- return 0;
- case HV_X64_MSR_SINT4:
- *hv_reg = HV_REGISTER_SINT4;
- return 0;
- case HV_X64_MSR_SINT5:
- *hv_reg = HV_REGISTER_SINT5;
- return 0;
- case HV_X64_MSR_SINT6:
- *hv_reg = HV_REGISTER_SINT6;
- return 0;
- case HV_X64_MSR_SINT7:
- *hv_reg = HV_REGISTER_SINT7;
- return 0;
- case HV_X64_MSR_SINT8:
- *hv_reg = HV_REGISTER_SINT8;
- return 0;
- case HV_X64_MSR_SINT9:
- *hv_reg = HV_REGISTER_SINT9;
- return 0;
- case HV_X64_MSR_SINT10:
- *hv_reg = HV_REGISTER_SINT10;
- return 0;
- case HV_X64_MSR_SINT11:
- *hv_reg = HV_REGISTER_SINT11;
- return 0;
- case HV_X64_MSR_SINT12:
- *hv_reg = HV_REGISTER_SINT12;
- return 0;
- case HV_X64_MSR_SINT13:
- *hv_reg = HV_REGISTER_SINT13;
- return 0;
- case HV_X64_MSR_SINT14:
- *hv_reg = HV_REGISTER_SINT14;
- return 0;
- case HV_X64_MSR_SINT15:
- *hv_reg = HV_REGISTER_SINT15;
- return 0;
- case IA32_MSR_MISC_ENABLE:
- *hv_reg = HV_X64_REGISTER_MSR_IA32_MISC_ENABLE;
- return 0;
- case HV_X64_MSR_SCONTROL:
- *hv_reg = HV_REGISTER_SCONTROL;
- return 0;
- case HV_X64_MSR_SIEFP:
- *hv_reg = HV_REGISTER_SIEFP;
- return 0;
- case HV_X64_MSR_SIMP:
- *hv_reg = HV_REGISTER_SIMP;
- return 0;
- case HV_X64_MSR_REFERENCE_TSC:
- *hv_reg = HV_REGISTER_REFERENCE_TSC;
- return 0;
- case HV_X64_MSR_EOM:
- *hv_reg = HV_REGISTER_EOM;
- return 0;
- default:
- error_report("failed to map MSR %u to HV register name", msr);
- return -1;
- }
-}
-
-static int set_msrs(const CPUState *cpu, GList *msrs)
-{
- size_t n_msrs;
- GList *entries;
- MshvMsrEntry *entry;
- enum hv_register_name name;
- struct hv_register_assoc *assoc;
int ret;
- size_t i = 0;
+ uint64_t d_t = MSR_MTRR_ENABLE | MSR_MTRR_MEM_TYPE_WB;
- n_msrs = g_list_length(msrs);
- hv_register_assoc *assocs = g_new0(hv_register_assoc, n_msrs);
+ const struct hv_register_assoc assocs[] = {
+ { .name = HV_X64_REGISTER_SYSENTER_CS, .value.reg64 = 0x0 },
+ { .name = HV_X64_REGISTER_SYSENTER_ESP, .value.reg64 = 0x0 },
+ { .name = HV_X64_REGISTER_SYSENTER_EIP, .value.reg64 = 0x0 },
+ { .name = HV_X64_REGISTER_STAR, .value.reg64 = 0x0 },
+ { .name = HV_X64_REGISTER_CSTAR, .value.reg64 = 0x0 },
+ { .name = HV_X64_REGISTER_LSTAR, .value.reg64 = 0x0 },
+ { .name = HV_X64_REGISTER_KERNEL_GS_BASE, .value.reg64 = 0x0 },
+ { .name = HV_X64_REGISTER_SFMASK, .value.reg64 = 0x0 },
+ { .name = HV_X64_REGISTER_MSR_MTRR_DEF_TYPE, .value.reg64 = d_t },
+ };
- entries = msrs;
- for (const GList *elem = entries; elem != NULL; elem = elem->next) {
- entry = elem->data;
- ret = mshv_msr_to_hv_reg_name(entry->index, &name);
- if (ret < 0) {
- g_free(assocs);
- return ret;
- }
- assoc = &assocs[i];
- assoc->name = name;
- /* the union has been initialized to 0 */
- assoc->value.reg64 = entry->data;
- i++;
- }
- ret = mshv_set_generic_regs(cpu, assocs, n_msrs);
- g_free(assocs);
+ ret = mshv_set_generic_regs(cpu, assocs, ARRAY_SIZE(assocs));
if (ret < 0) {
- error_report("failed to set msrs");
+ error_report("failed to put msrs");
return -1;
}
+
return 0;
}
-int mshv_configure_msr(const CPUState *cpu, const MshvMsrEntry *msrs,
- size_t n_msrs)
+/*
+ * INVARIANT: this fn expects assocs in the same order as they appear in
+ * msr_env_map.
+ */
+static void store_in_env(CPUState *cpu, const struct hv_register_assoc *assocs,
+ size_t n_assocs)
{
- GList *valid_msrs = NULL;
- uint32_t msr_index;
- int ret;
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ size_t i, j;
+ const MshvMsrEnvMap *mapping;
+ union hv_register_value hv_value;
+ ptrdiff_t offset;
+ uint32_t hv_name;
- for (size_t i = 0; i < n_msrs; i++) {
- msr_index = msrs[i].index;
- /* check whether index of msrs is in SUPPORTED_MSRS */
- if (mshv_is_supported_msr(msr_index)) {
- valid_msrs = g_list_append(valid_msrs, (void *) &msrs[i]);
+ assert(n_assocs <= (ARRAY_SIZE(msr_env_map)));
+
+ for (i = 0, j = 0; i < ARRAY_SIZE(msr_env_map); i++) {
+ hv_name = assocs[j].name;
+ mapping = &msr_env_map[i];
+ if (hv_name != mapping->hv_name) {
+ continue;
}
+
+ hv_value = assocs[j].value;
+ offset = mapping->env_offset;
+ MSHV_ENV_FIELD(env, offset) = hv_value.reg64;
+ j++;
+ }
+}
+
+static void set_hv_name_in_assocs(struct hv_register_assoc *assocs,
+ size_t n_assocs)
+{
+ size_t i;
+
+ assert(n_assocs == ARRAY_SIZE(msr_env_map));
+ for (i = 0; i < ARRAY_SIZE(msr_env_map); i++) {
+ assocs[i].name = msr_env_map[i].hv_name;
+ }
+}
+
+static bool msr_supported(uint32_t name)
+{
+ /*
+ * This check is not done comprehensively, it's meant to avoid hvcall
+ * failures for certain MSRs on architectures that don't support them.
+ */
+
+ switch (name) {
+ case HV_X64_REGISTER_SPEC_CTRL:
+ return mshv_state->processor_features.ibrs_support;
+ case HV_X64_REGISTER_TSC_ADJUST:
+ return mshv_state->processor_features.tsc_adjust_support;
}
- ret = set_msrs(cpu, valid_msrs);
- g_list_free(valid_msrs);
+ return true;
+}
- return ret;
+int mshv_get_msrs(CPUState *cpu)
+{
+ int ret = 0;
+ size_t n_assocs = ARRAY_SIZE(msr_env_map);
+ struct hv_register_assoc assocs[ARRAY_SIZE(msr_env_map)];
+ size_t i, j;
+ uint32_t name;
+
+ set_hv_name_in_assocs(assocs, n_assocs);
+
+ /* Filter out MSRs that cannot be read */
+ for (i = 0, j = 0; i < n_assocs; i++) {
+ name = assocs[i].name;
+
+ if (!msr_supported(name)) {
+ continue;
+ }
+
+ if (j != i) {
+ assocs[j] = assocs[i];
+ }
+ j++;
+ }
+ n_assocs = j;
+
+ ret = mshv_get_generic_regs(cpu, assocs, n_assocs);
+ if (ret < 0) {
+ error_report("Failed to get MSRs");
+ return -errno;
+ }
+
+ store_in_env(cpu, assocs, n_assocs);
+
+ return 0;
+}
+
+static void load_from_env(const CPUState *cpu, struct hv_register_assoc *assocs,
+ size_t n_assocs)
+{
+ size_t i;
+ const MshvMsrEnvMap *mapping;
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ ptrdiff_t offset;
+ union hv_register_value *hv_value;
+
+ assert(n_assocs == ARRAY_SIZE(msr_env_map));
+
+ for (i = 0; i < ARRAY_SIZE(msr_env_map); i++) {
+ mapping = &msr_env_map[i];
+ offset = mapping->env_offset;
+ assocs[i].name = mapping->hv_name;
+ hv_value = &assocs[i].value;
+ hv_value->reg64 = MSHV_ENV_FIELD(env, offset);
+ }
+}
+
+int mshv_set_msrs(const CPUState *cpu)
+{
+ size_t n_assocs = ARRAY_SIZE(msr_env_map);
+ struct hv_register_assoc assocs[ARRAY_SIZE(msr_env_map)];
+ int ret;
+ size_t i, j;
+
+ load_from_env(cpu, assocs, n_assocs);
+
+ /* Filter out MSRs that cannot be written */
+ for (i = 0, j = 0; i < n_assocs; i++) {
+ uint32_t name = assocs[i].name;
+
+ /* Partition-wide MSRs: only write on vCPU 0 */
+ if (cpu->cpu_index != 0 &&
+ (name == HV_X64_REGISTER_HYPERCALL ||
+ name == HV_REGISTER_GUEST_OS_ID ||
+ name == HV_REGISTER_REFERENCE_TSC)) {
+ continue;
+ }
+
+ if (!msr_supported(name)) {
+ continue;
+ }
+
+ if (j != i) {
+ assocs[j] = assocs[i];
+ }
+ j++;
+ }
+ n_assocs = j;
+
+ ret = mshv_set_generic_regs(cpu, assocs, n_assocs);
+ if (ret < 0) {
+ error_report("Failed to set MSRs");
+ return -errno;
+ }
+
+ return 0;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 31/45] target/i386/mshv: migrate MTRR MSRs
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (29 preceding siblings ...)
2026-06-26 10:17 ` [PULL 30/45] target/i386/mshv: migrate MSRs Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 32/45] target/i386/mshv: migrate CET/SS MSRs Paolo Bonzini
` (14 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
This change roundtrips memory access/caching MSRs. The mapping scheme
is a bit more elaborate on these, so we have added a special handling
instead of individual entries in the MSR mapping table.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260417105618.3621-22-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
target/i386/mshv/msr.c | 136 ++++++++++++++++++++++++++++++++++++++---
1 file changed, 129 insertions(+), 7 deletions(-)
diff --git a/target/i386/mshv/msr.c b/target/i386/mshv/msr.c
index 7447467dbf5..76b593060a7 100644
--- a/target/i386/mshv/msr.c
+++ b/target/i386/mshv/msr.c
@@ -77,6 +77,10 @@ static const MshvMsrEnvMap msr_env_map[] = {
{ HV_X64_MSR_SIMP, HV_REGISTER_SIMP,
offsetof(CPUX86State, msr_hv_synic_msg_page) },
+ /* MTRR default type */
+ { IA32_MSR_MTRR_DEF_TYPE, HV_X64_REGISTER_MSR_MTRR_DEF_TYPE,
+ offsetof(CPUX86State, mtrr_deftype) },
+
/* Other */
/* TODO: find out processor features that correlate to unsupported MSRs. */
@@ -88,6 +92,98 @@ static const MshvMsrEnvMap msr_env_map[] = {
offsetof(CPUX86State, spec_ctrl) },
};
+/*
+ * The assocs have to be set according to this schema:
+ * 8 entries for 0-7 mtrr_base
+ * 8 entries for mtrr_mask 0-7
+ * 11 entries for 1 x 64k, 2 x 16k, 8 x 4k fixed MTRR
+ * 27 total entries
+ */
+
+#define MSHV_MTRR_MSR_COUNT 27
+#define MSHV_MSR_TOTAL_COUNT (ARRAY_SIZE(msr_env_map) + MSHV_MTRR_MSR_COUNT)
+
+static void store_in_env_mtrr_phys(CPUState *cpu,
+ const struct hv_register_assoc *assocs,
+ size_t n_assocs)
+{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ size_t i, fixed_offset;
+ hv_register_name hv_name;
+ uint64_t base, mask;
+
+ assert(n_assocs == MSHV_MTRR_MSR_COUNT);
+
+ for (i = 0; i < MSR_MTRRcap_VCNT; i++) {
+ hv_name = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE0 + i;
+ assert(assocs[i].name == hv_name);
+ hv_name = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK0 + i;
+ assert(assocs[i + MSR_MTRRcap_VCNT].name == hv_name);
+
+ base = assocs[i].value.reg64;
+ mask = assocs[i + MSR_MTRRcap_VCNT].value.reg64;
+ env->mtrr_var[i].base = base;
+ env->mtrr_var[i].mask = mask;
+ }
+
+ /* fixed 1x 64, 2x 16, 8x 4 kB */
+ fixed_offset = MSR_MTRRcap_VCNT * 2;
+ for (i = 0; i < 11; i++) {
+ hv_name = HV_X64_REGISTER_MSR_MTRR_FIX64K00000 + i;
+ assert(assocs[fixed_offset + i].name == hv_name);
+ env->mtrr_fixed[i] = assocs[fixed_offset + i].value.reg64;
+ }
+}
+
+/*
+ * The assocs have to be set according to this schema:
+ * 8 entries for 0-7 mtrr_base
+ * 8 entries for mtrr_mask 0-7
+ * 11 entries for 1 x 64k, 2 x 16k, 8 x 4k fixed MTRR
+ * 27 total entries
+ */
+static void load_from_env_mtrr_phys(const CPUState *cpu,
+ struct hv_register_assoc *assocs,
+ size_t n_assocs)
+{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ size_t i, fixed_offset;
+ uint64_t base, mask, fixed_value;
+ hv_register_name base_name, mask_name, fixed_name;
+ hv_register_assoc *assoc;
+
+ assert(n_assocs == MSHV_MTRR_MSR_COUNT);
+
+ for (i = 0; i < MSR_MTRRcap_VCNT; i++) {
+ base = env->mtrr_var[i].base;
+ mask = env->mtrr_var[i].mask;
+
+ base_name = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE0 + i;
+ mask_name = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK0 + i;
+
+ assoc = &assocs[i];
+ assoc->name = base_name;
+ assoc->value.reg64 = base;
+
+ assoc = &assocs[i + MSR_MTRRcap_VCNT];
+ assoc->name = mask_name;
+ assoc->value.reg64 = mask;
+ }
+
+ /* fixed 1x 64, 2x 16, 8x 4 kB */
+ fixed_offset = MSR_MTRRcap_VCNT * 2;
+ for (i = 0; i < 11; i++) {
+ fixed_name = HV_X64_REGISTER_MSR_MTRR_FIX64K00000 + i;
+ fixed_value = env->mtrr_fixed[i];
+
+ assoc = &assocs[fixed_offset + i];
+ assoc->name = fixed_name;
+ assoc->value.reg64 = fixed_value;
+ }
+}
+
int mshv_init_msrs(const CPUState *cpu)
{
int ret;
@@ -129,8 +225,9 @@ static void store_in_env(CPUState *cpu, const struct hv_register_assoc *assocs,
union hv_register_value hv_value;
ptrdiff_t offset;
uint32_t hv_name;
+ size_t mtrr_index;
- assert(n_assocs <= (ARRAY_SIZE(msr_env_map)));
+ assert(n_assocs <= MSHV_MSR_TOTAL_COUNT);
for (i = 0, j = 0; i < ARRAY_SIZE(msr_env_map); i++) {
hv_name = assocs[j].name;
@@ -144,17 +241,38 @@ static void store_in_env(CPUState *cpu, const struct hv_register_assoc *assocs,
MSHV_ENV_FIELD(env, offset) = hv_value.reg64;
j++;
}
+
+ mtrr_index = j;
+ store_in_env_mtrr_phys(cpu, &assocs[mtrr_index], MSHV_MTRR_MSR_COUNT);
}
static void set_hv_name_in_assocs(struct hv_register_assoc *assocs,
size_t n_assocs)
{
size_t i;
+ size_t mtrr_offset, mtrr_fixed_offset;
+ hv_register_name hv_name;
+
+ assert(n_assocs == MSHV_MSR_TOTAL_COUNT);
- assert(n_assocs == ARRAY_SIZE(msr_env_map));
for (i = 0; i < ARRAY_SIZE(msr_env_map); i++) {
assocs[i].name = msr_env_map[i].hv_name;
}
+
+ mtrr_offset = ARRAY_SIZE(msr_env_map);
+ for (i = 0; i < MSR_MTRRcap_VCNT; i++) {
+ hv_name = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE0 + i;
+ assocs[mtrr_offset + i].name = hv_name;
+ hv_name = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK0 + i;
+ assocs[mtrr_offset + MSR_MTRRcap_VCNT + i].name = hv_name;
+ }
+
+ /* fixed 1x 64, 2x 16, 8x 4 kB */
+ mtrr_fixed_offset = mtrr_offset + MSR_MTRRcap_VCNT * 2;
+ for (i = 0; i < 11; i++) {
+ hv_name = HV_X64_REGISTER_MSR_MTRR_FIX64K00000 + i;
+ assocs[mtrr_fixed_offset + i].name = hv_name;
+ }
}
static bool msr_supported(uint32_t name)
@@ -177,8 +295,8 @@ static bool msr_supported(uint32_t name)
int mshv_get_msrs(CPUState *cpu)
{
int ret = 0;
- size_t n_assocs = ARRAY_SIZE(msr_env_map);
- struct hv_register_assoc assocs[ARRAY_SIZE(msr_env_map)];
+ size_t n_assocs = MSHV_MSR_TOTAL_COUNT;
+ struct hv_register_assoc assocs[MSHV_MSR_TOTAL_COUNT];
size_t i, j;
uint32_t name;
@@ -219,8 +337,9 @@ static void load_from_env(const CPUState *cpu, struct hv_register_assoc *assocs,
CPUX86State *env = &x86_cpu->env;
ptrdiff_t offset;
union hv_register_value *hv_value;
+ size_t mtrr_offset;
- assert(n_assocs == ARRAY_SIZE(msr_env_map));
+ assert(n_assocs == MSHV_MSR_TOTAL_COUNT);
for (i = 0; i < ARRAY_SIZE(msr_env_map); i++) {
mapping = &msr_env_map[i];
@@ -229,12 +348,15 @@ static void load_from_env(const CPUState *cpu, struct hv_register_assoc *assocs,
hv_value = &assocs[i].value;
hv_value->reg64 = MSHV_ENV_FIELD(env, offset);
}
+
+ mtrr_offset = ARRAY_SIZE(msr_env_map);
+ load_from_env_mtrr_phys(cpu, &assocs[mtrr_offset], MSHV_MTRR_MSR_COUNT);
}
int mshv_set_msrs(const CPUState *cpu)
{
- size_t n_assocs = ARRAY_SIZE(msr_env_map);
- struct hv_register_assoc assocs[ARRAY_SIZE(msr_env_map)];
+ size_t n_assocs = MSHV_MSR_TOTAL_COUNT;
+ struct hv_register_assoc assocs[MSHV_MSR_TOTAL_COUNT];
int ret;
size_t i, j;
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 32/45] target/i386/mshv: migrate CET/SS MSRs
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (30 preceding siblings ...)
2026-06-26 10:17 ` [PULL 31/45] target/i386/mshv: migrate MTRR MSRs Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 33/45] accel: remove unnecessary #ifdefs Paolo Bonzini
` (13 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
This change migrates the MSRs required for CET shadow stack and indirect
branch tracking. They are gated behind cet_ss_support || cet_ibt_support
mshv processor feature flags.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20260417105618.3621-24-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/hw/hyperv/hvgdk_mini.h | 11 +++++++++++
target/i386/mshv/msr.c | 30 ++++++++++++++++++++++++++++++
2 files changed, 41 insertions(+)
diff --git a/include/hw/hyperv/hvgdk_mini.h b/include/hw/hyperv/hvgdk_mini.h
index b00ec8a6367..c6749a95255 100644
--- a/include/hw/hyperv/hvgdk_mini.h
+++ b/include/hw/hyperv/hvgdk_mini.h
@@ -170,6 +170,17 @@ typedef enum hv_register_name {
HV_X64_REGISTER_SPEC_CTRL = 0x00080084,
HV_X64_REGISTER_TSC_ADJUST = 0x00080096,
+ /* CET / Shadow Stack */
+ HV_X64_REGISTER_U_XSS = 0x0008008B,
+ HV_X64_REGISTER_U_CET = 0x0008008C,
+ HV_X64_REGISTER_S_CET = 0x0008008D,
+ HV_X64_REGISTER_SSP = 0x0008008E,
+ HV_X64_REGISTER_PL0_SSP = 0x0008008F,
+ HV_X64_REGISTER_PL1_SSP = 0x00080090,
+ HV_X64_REGISTER_PL2_SSP = 0x00080091,
+ HV_X64_REGISTER_PL3_SSP = 0x00080092,
+ HV_X64_REGISTER_INTERRUPT_SSP_TABLE_ADDR = 0x00080093,
+
/* Other MSRs */
HV_X64_REGISTER_MSR_IA32_MISC_ENABLE = 0x000800A0,
diff --git a/target/i386/mshv/msr.c b/target/i386/mshv/msr.c
index 76b593060a7..8c220a99423 100644
--- a/target/i386/mshv/msr.c
+++ b/target/i386/mshv/msr.c
@@ -81,6 +81,26 @@ static const MshvMsrEnvMap msr_env_map[] = {
{ IA32_MSR_MTRR_DEF_TYPE, HV_X64_REGISTER_MSR_MTRR_DEF_TYPE,
offsetof(CPUX86State, mtrr_deftype) },
+ /* CET / Shadow Stack */
+ { MSR_IA32_U_CET, HV_X64_REGISTER_U_CET,
+ offsetof(CPUX86State, u_cet) },
+ { MSR_IA32_S_CET, HV_X64_REGISTER_S_CET,
+ offsetof(CPUX86State, s_cet) },
+ { MSR_IA32_PL0_SSP, HV_X64_REGISTER_PL0_SSP,
+ offsetof(CPUX86State, pl0_ssp) },
+ { MSR_IA32_PL1_SSP, HV_X64_REGISTER_PL1_SSP,
+ offsetof(CPUX86State, pl1_ssp) },
+ { MSR_IA32_PL2_SSP, HV_X64_REGISTER_PL2_SSP,
+ offsetof(CPUX86State, pl2_ssp) },
+ { MSR_IA32_PL3_SSP, HV_X64_REGISTER_PL3_SSP,
+ offsetof(CPUX86State, pl3_ssp) },
+ { MSR_IA32_INT_SSP_TAB, HV_X64_REGISTER_INTERRUPT_SSP_TABLE_ADDR,
+ offsetof(CPUX86State, int_ssp_table) },
+
+ /* XSAVE Supervisor State */
+ { MSR_IA32_XSS, HV_X64_REGISTER_U_XSS,
+ offsetof(CPUX86State, xss) },
+
/* Other */
/* TODO: find out processor features that correlate to unsupported MSRs. */
@@ -287,6 +307,16 @@ static bool msr_supported(uint32_t name)
return mshv_state->processor_features.ibrs_support;
case HV_X64_REGISTER_TSC_ADJUST:
return mshv_state->processor_features.tsc_adjust_support;
+ case HV_X64_REGISTER_U_CET:
+ case HV_X64_REGISTER_S_CET:
+ case HV_X64_REGISTER_PL0_SSP:
+ case HV_X64_REGISTER_PL1_SSP:
+ case HV_X64_REGISTER_PL2_SSP:
+ case HV_X64_REGISTER_PL3_SSP:
+ case HV_X64_REGISTER_INTERRUPT_SSP_TABLE_ADDR:
+ case HV_X64_REGISTER_U_XSS:
+ return mshv_state->processor_features.cet_ss_support ||
+ mshv_state->processor_features.cet_ibt_support;
}
return true;
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 33/45] accel: remove unnecessary #ifdefs
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (31 preceding siblings ...)
2026-06-26 10:17 ` [PULL 32/45] target/i386/mshv: migrate CET/SS MSRs Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 34/45] include/hw/hyperv: add hv_vp_register_page struct definition Paolo Bonzini
` (12 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
accel/accel-irq.c | 14 --------------
accel/stubs/mshv-stub.c | 1 +
2 files changed, 1 insertion(+), 14 deletions(-)
diff --git a/accel/accel-irq.c b/accel/accel-irq.c
index 4303e10e408..830be27c7f9 100644
--- a/accel/accel-irq.c
+++ b/accel/accel-irq.c
@@ -19,11 +19,9 @@
int accel_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev)
{
-#ifdef CONFIG_MSHV_IS_POSSIBLE
if (mshv_msi_via_irqfd_enabled()) {
return mshv_irqchip_add_msi_route(c, vector, dev);
}
-#endif
if (kvm_enabled()) {
return kvm_irqchip_add_msi_route(c, vector, dev);
}
@@ -32,11 +30,9 @@ int accel_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev)
int accel_irqchip_update_msi_route(int vector, MSIMessage msg, PCIDevice *dev)
{
-#ifdef CONFIG_MSHV_IS_POSSIBLE
if (mshv_msi_via_irqfd_enabled()) {
return mshv_irqchip_update_msi_route(vector, msg, dev);
}
-#endif
if (kvm_enabled()) {
return kvm_irqchip_update_msi_route(kvm_state, vector, msg, dev);
}
@@ -53,11 +49,9 @@ void accel_irqchip_commit_route_changes(AccelRouteChange *c)
void accel_irqchip_commit_routes(void)
{
-#ifdef CONFIG_MSHV_IS_POSSIBLE
if (mshv_msi_via_irqfd_enabled()) {
mshv_irqchip_commit_routes(mshv_state);
}
-#endif
if (kvm_enabled()) {
kvm_irqchip_commit_routes(kvm_state);
}
@@ -65,11 +59,9 @@ void accel_irqchip_commit_routes(void)
void accel_irqchip_release_virq(int virq)
{
-#ifdef CONFIG_MSHV_IS_POSSIBLE
if (mshv_msi_via_irqfd_enabled()) {
mshv_irqchip_release_virq(mshv_state, virq);
}
-#endif
if (kvm_enabled()) {
kvm_irqchip_release_virq(kvm_state, virq);
}
@@ -78,11 +70,9 @@ void accel_irqchip_release_virq(int virq)
int accel_irqchip_add_irqfd_notifier_gsi(EventNotifier *n, EventNotifier *rn,
int virq)
{
-#ifdef CONFIG_MSHV_IS_POSSIBLE
if (mshv_msi_via_irqfd_enabled()) {
return mshv_irqchip_add_irqfd_notifier_gsi(n, rn, virq);
}
-#endif
if (kvm_enabled()) {
return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, rn, virq);
}
@@ -91,11 +81,9 @@ int accel_irqchip_add_irqfd_notifier_gsi(EventNotifier *n, EventNotifier *rn,
int accel_irqchip_remove_irqfd_notifier_gsi(EventNotifier *n, int virq)
{
-#ifdef CONFIG_MSHV_IS_POSSIBLE
if (mshv_msi_via_irqfd_enabled()) {
return mshv_irqchip_remove_irqfd_notifier_gsi(n, virq);
}
-#endif
if (kvm_enabled()) {
return kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, n, virq);
}
@@ -104,14 +92,12 @@ int accel_irqchip_remove_irqfd_notifier_gsi(EventNotifier *n, int virq)
inline AccelRouteChange accel_irqchip_begin_route_changes(void)
{
-#ifdef CONFIG_MSHV_IS_POSSIBLE
if (mshv_msi_via_irqfd_enabled()) {
return (AccelRouteChange) {
.accel = ACCEL(mshv_state),
.changes = 0,
};
}
-#endif
if (kvm_enabled()) {
return (AccelRouteChange) {
.accel = ACCEL(kvm_state),
diff --git a/accel/stubs/mshv-stub.c b/accel/stubs/mshv-stub.c
index 8b69539a85a..566a17ff9e4 100644
--- a/accel/stubs/mshv-stub.c
+++ b/accel/stubs/mshv-stub.c
@@ -13,6 +13,7 @@
#include "system/mshv.h"
bool mshv_allowed;
+MshvState *mshv_state;
int mshv_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev)
{
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 34/45] include/hw/hyperv: add hv_vp_register_page struct definition
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (32 preceding siblings ...)
2026-06-26 10:17 ` [PULL 33/45] accel: remove unnecessary #ifdefs Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 35/45] target/i386/mshv: hv_vp_register_page setup for the vcpu Paolo Bonzini
` (11 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Doru Blânzeanu, Mohamed Mediouni
From: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Define the `hv_vp_register_page` structure that the linux kernel uses
to allow access to vcpu registers.
This structure is going to be used in later patches to access vcpu
registers.
Signed-off-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Reviewed-by: Mohamed Mediouni <mohamed@unpredictable.fr>
Link: https://lore.kernel.org/r/20260428135053.251200-4-dblanzeanu@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/hw/hyperv/hvgdk_mini.h | 103 +++++++++++++++++++++++++++++++++
1 file changed, 103 insertions(+)
diff --git a/include/hw/hyperv/hvgdk_mini.h b/include/hw/hyperv/hvgdk_mini.h
index c6749a95255..f8838a31bbe 100644
--- a/include/hw/hyperv/hvgdk_mini.h
+++ b/include/hw/hyperv/hvgdk_mini.h
@@ -504,6 +504,109 @@ struct hv_input_assert_virtual_interrupt {
uint16_t rsvd_z1;
} QEMU_PACKED;
+/* Flags for dirty mask of hv_vp_register_page */
+enum hv_x64_register_class_type {
+ HV_X64_REGISTER_CLASS_GENERAL = 0,
+ HV_X64_REGISTER_CLASS_IP = 1,
+ HV_X64_REGISTER_CLASS_XMM = 2,
+ HV_X64_REGISTER_CLASS_SEGMENT = 3,
+ HV_X64_REGISTER_CLASS_FLAGS = 4,
+};
+
+#define HV_VP_REGISTER_PAGE_MAX_VECTOR_COUNT 7
+
+union hv_vp_register_page_interrupt_vectors {
+ uint64_t as_uint64;
+ struct {
+ uint8_t vector_count;
+ uint8_t vector[HV_VP_REGISTER_PAGE_MAX_VECTOR_COUNT];
+ };
+};
+
+struct hv_vp_register_page {
+ uint16_t version;
+ uint8_t isvalid;
+ uint8_t rsvdz;
+ uint32_t dirty;
+
+ union {
+ struct {
+ /* General purpose registers (HV_X64_REGISTER_CLASS_GENERAL) */
+ union {
+ struct {
+ 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;
+ } QEMU_PACKED;
+
+ uint64_t gp_registers[16];
+ };
+ /* Instruction pointer (HV_X64_REGISTER_CLASS_IP) */
+ uint64_t rip;
+ /* Flags (HV_X64_REGISTER_CLASS_FLAGS) */
+ uint64_t rflags;
+ } QEMU_PACKED;
+
+ uint64_t registers[18];
+ };
+ uint8_t reserved[8];
+ /* Volatile XMM registers (HV_X64_REGISTER_CLASS_XMM) */
+ union {
+ struct {
+ struct hv_u128 xmm0;
+ struct hv_u128 xmm1;
+ struct hv_u128 xmm2;
+ struct hv_u128 xmm3;
+ struct hv_u128 xmm4;
+ struct hv_u128 xmm5;
+ } QEMU_PACKED;
+
+ struct hv_u128 xmm_registers[6];
+ };
+ /* Segment registers (HV_X64_REGISTER_CLASS_SEGMENT) */
+ union {
+ struct {
+ struct hv_x64_segment_register es;
+ struct hv_x64_segment_register cs;
+ struct hv_x64_segment_register ss;
+ struct hv_x64_segment_register ds;
+ struct hv_x64_segment_register fs;
+ struct hv_x64_segment_register gs;
+ } QEMU_PACKED;
+
+ struct hv_x64_segment_register segment_registers[6];
+ };
+ /* Misc. control registers (cannot be set via this interface) */
+ uint64_t cr0;
+ uint64_t cr3;
+ uint64_t cr4;
+ uint64_t cr8;
+ uint64_t efer;
+ uint64_t dr7;
+ union hv_x64_pending_interruption_register pending_interruption;
+ union hv_x64_interrupt_state_register interrupt_state;
+ uint64_t instruction_emulation_hints;
+ uint64_t xfem;
+
+ uint8_t reserved1[0x100];
+
+ /* Interrupts injected as part of HvCallDispatchVp. */
+ union hv_vp_register_page_interrupt_vectors interrupt_vectors;
+} QEMU_PACKED;
+
/* /dev/mshv */
#define MSHV_CREATE_PARTITION _IOW(MSHV_IOCTL, 0x00, struct mshv_create_partition)
#define MSHV_CREATE_VP _IOW(MSHV_IOCTL, 0x01, struct mshv_create_vp)
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 35/45] target/i386/mshv: hv_vp_register_page setup for the vcpu
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (33 preceding siblings ...)
2026-06-26 10:17 ` [PULL 34/45] include/hw/hyperv: add hv_vp_register_page struct definition Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 36/45] target/i386/mshv: use the register page to get registers Paolo Bonzini
` (10 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Doru Blânzeanu, Mohamed Mediouni
From: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
When the vcpu is created, call mmap to configure access to the register page.
Update CPUArchState to store a pointer to the mmapped hv_vp_register_page.
Signed-off-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Reviewed-by: Mohamed Mediouni <mohamed@unpredictable.fr>
Link: https://lore.kernel.org/r/20260428135053.251200-5-dblanzeanu@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
target/i386/cpu.h | 9 +++++++--
target/i386/mshv/mshv-cpu.c | 17 +++++++++++++++++
2 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 67e2ecf3251..e6a197602d8 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -1983,6 +1983,9 @@ typedef struct CPUCaches {
CPUCacheInfo *l3_cache;
} CPUCaches;
+struct kvm_msrs;
+struct hv_vp_register_page;
+
typedef struct CPUArchState {
/* standard registers */
target_ulong regs[CPU_NB_EREGS];
@@ -2289,6 +2292,10 @@ typedef struct CPUArchState {
QEMUTimer *xen_periodic_timer;
QemuMutex xen_timers_lock;
#endif
+#if defined(CONFIG_MSHV)
+ /* Shared register page */
+ struct hv_vp_register_page *regs_page;
+#endif
#if defined(CONFIG_HVF) || defined(CONFIG_MSHV) || defined(CONFIG_WHPX)
void *emu_mmio_buf;
#endif
@@ -2315,8 +2322,6 @@ typedef struct CPUArchState {
DECLARE_BITMAP(avail_cpu_topo, CPU_TOPOLOGY_LEVEL__MAX);
} CPUX86State;
-struct kvm_msrs;
-
/**
* X86CPU:
* @env: #CPUX86State
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index a383e0ab104..67dfb3c42ab 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -1906,6 +1906,18 @@ void mshv_arch_init_vcpu(CPUState *cpu)
+ sizeof(hv_input_get_vp_registers)
> HV_HYP_PAGE_SIZE));
+ /* mmap the registers page */
+ void *rp = mmap(NULL, page, PROT_READ | PROT_WRITE,
+ MAP_SHARED, mshv_vcpufd(cpu),
+ MSHV_VP_MMAP_OFFSET_REGISTERS * page);
+ if (rp == MAP_FAILED) {
+ warn_report("register page mmap failed, falling back to hypercalls: %s",
+ strerror(errno));
+ env->regs_page = NULL;
+ } else {
+ env->regs_page = (struct hv_vp_register_page *) rp;
+ }
+
state->hvcall_args.base = mem;
state->hvcall_args.input_page = mem;
state->hvcall_args.output_page = (uint8_t *)mem + page;
@@ -1944,6 +1956,11 @@ void mshv_arch_destroy_vcpu(CPUState *cpu)
CPUX86State *env = &x86_cpu->env;
AccelCPUState *state = cpu->accel;
+ /* Unmap the register page */
+ if (env->regs_page) {
+ munmap(env->regs_page, HV_HYP_PAGE_SIZE);
+ env->regs_page = NULL;
+ }
g_free(state->hvcall_args.base);
state->hvcall_args = (MshvHvCallArgs){0};
g_clear_pointer(&env->emu_mmio_buf, g_free);
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 36/45] target/i386/mshv: use the register page to get registers
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (34 preceding siblings ...)
2026-06-26 10:17 ` [PULL 35/45] target/i386/mshv: hv_vp_register_page setup for the vcpu Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 37/45] target/i386/mshv: use the register page to set registers Paolo Bonzini
` (9 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Doru Blânzeanu, Mohamed Mediouni
From: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Change the mshv_load_regs to use the register page when it is mmapped
and is valid.
Otherwise use the existing logic that uses ioctls to fetch registers.
When retrieving the special registers, there are some registers that are
not present in the register page: TR, LDTR, GDTR, IDTR, CR2, APIC_BASE.
For this ones we still need to use ioctls to correctly fetch.
Signed-off-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Reviewed-by: Mohamed Mediouni <mohamed@unpredictable.fr>
Link: https://lore.kernel.org/r/20260428135053.251200-6-dblanzeanu@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
target/i386/mshv/mshv-cpu.c | 117 ++++++++++++++++++++++++++++++++++++
1 file changed, 117 insertions(+)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 67dfb3c42ab..ed78fa09934 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -270,6 +270,15 @@ static int get_xc_reg(CPUState *cpu)
return 0;
}
+static enum hv_register_name NON_VP_PAGE_REGISTER_NAMES[6] = {
+ HV_X64_REGISTER_TR,
+ HV_X64_REGISTER_LDTR,
+ HV_X64_REGISTER_GDTR,
+ HV_X64_REGISTER_IDTR,
+ HV_X64_REGISTER_CR2,
+ HV_X64_REGISTER_APIC_BASE,
+};
+
static int translate_gva(const CPUState *cpu, uint64_t gva, uint64_t *gpa,
uint64_t flags)
{
@@ -570,6 +579,106 @@ static void populate_special_regs(const hv_register_assoc *assocs,
cpu_set_apic_base(x86cpu->apic_state, assocs[16].value.reg64);
}
+static void mshv_get_standard_regs_vp_page(CPUState *cpu)
+{
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
+
+ /* General Purpose Registers */
+ env->regs[R_EAX] = env->regs_page->rax;
+ env->regs[R_EBX] = env->regs_page->rbx;
+ env->regs[R_ECX] = env->regs_page->rcx;
+ env->regs[R_EDX] = env->regs_page->rdx;
+ env->regs[R_ESI] = env->regs_page->rsi;
+ env->regs[R_EDI] = env->regs_page->rdi;
+ env->regs[R_ESP] = env->regs_page->rsp;
+ env->regs[R_EBP] = env->regs_page->rbp;
+ env->regs[R_R8] = env->regs_page->r8;
+ env->regs[R_R9] = env->regs_page->r9;
+ env->regs[R_R10] = env->regs_page->r10;
+ env->regs[R_R11] = env->regs_page->r11;
+ env->regs[R_R12] = env->regs_page->r12;
+ env->regs[R_R13] = env->regs_page->r13;
+ env->regs[R_R14] = env->regs_page->r14;
+ env->regs[R_R15] = env->regs_page->r15;
+
+ env->eip = env->regs_page->rip;
+ env->eflags = env->regs_page->rflags;
+ rflags_to_lflags(env);
+}
+
+static int mshv_get_special_regs_vp_page(CPUState *cpu)
+{
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
+ struct hv_register_assoc assocs[ARRAY_SIZE(NON_VP_PAGE_REGISTER_NAMES)];
+ int ret;
+ size_t n_regs = ARRAY_SIZE(NON_VP_PAGE_REGISTER_NAMES);
+ hv_x64_segment_register seg;
+
+ /* Populate special registers that are in the VP register page */
+ env->cr[0] = env->regs_page->cr0;
+ env->cr[3] = env->regs_page->cr3;
+ env->cr[4] = env->regs_page->cr4;
+ env->efer = env->regs_page->efer;
+ cpu_set_apic_tpr(x86cpu->apic_state, env->regs_page->cr8);
+
+ /* Segment Registers - copy from packed struct to avoid unaligned access */
+ memcpy(&seg, &env->regs_page->es, sizeof(hv_x64_segment_register));
+ populate_segment_reg(&seg, &env->segs[R_ES]);
+ memcpy(&seg, &env->regs_page->cs, sizeof(hv_x64_segment_register));
+ populate_segment_reg(&seg, &env->segs[R_CS]);
+ memcpy(&seg, &env->regs_page->ss, sizeof(hv_x64_segment_register));
+ populate_segment_reg(&seg, &env->segs[R_SS]);
+ memcpy(&seg, &env->regs_page->ds, sizeof(hv_x64_segment_register));
+ populate_segment_reg(&seg, &env->segs[R_DS]);
+ memcpy(&seg, &env->regs_page->fs, sizeof(hv_x64_segment_register));
+ populate_segment_reg(&seg, &env->segs[R_FS]);
+ memcpy(&seg, &env->regs_page->gs, sizeof(hv_x64_segment_register));
+ populate_segment_reg(&seg, &env->segs[R_GS]);
+
+ /* The rest of the special registers that are not in the VP register page */
+ for (size_t i = 0; i < n_regs; i++) {
+ assocs[i].name = NON_VP_PAGE_REGISTER_NAMES[i];
+ }
+
+ ret = mshv_get_generic_regs(cpu, assocs, n_regs);
+ if (ret < 0) {
+ error_report("failed to get non-vp-page special registers");
+ return -1;
+ }
+
+ /* Non-VP page registers - TR, LDTR, GDTR, IDTR, CR2, APIC_BASE */
+ populate_segment_reg(&assocs[0].value.segment, &env->tr);
+ populate_segment_reg(&assocs[1].value.segment, &env->ldt);
+
+ populate_table_reg(&assocs[2].value.table, &env->gdt);
+ populate_table_reg(&assocs[3].value.table, &env->idt);
+ env->cr[2] = assocs[4].value.reg64;
+
+ cpu_set_apic_base(x86cpu->apic_state, assocs[5].value.reg64);
+
+ return ret;
+}
+
+static int mshv_get_registers_vp_page(CPUState *cpu)
+{
+ int ret;
+
+ /* General Purpose Registers */
+ mshv_get_standard_regs_vp_page(cpu);
+
+ /* Special Registers - makes a hypercall */
+ ret = mshv_get_special_regs_vp_page(cpu);
+ if (ret < 0) {
+ error_report("failed to get special registers for vp page");
+ return -1;
+ }
+
+ return 0;
+}
+
+
static int get_special_regs(CPUState *cpu)
{
struct hv_register_assoc assocs[ARRAY_SIZE(SPECIAL_REGISTER_NAMES)];
@@ -592,8 +701,16 @@ static int get_special_regs(CPUState *cpu)
static int load_regs(CPUState *cpu)
{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
int ret;
+ /* Use register vp page to optimize registers access */
+ if (env->regs_page && env->regs_page->isvalid != 0) {
+ ret = mshv_get_registers_vp_page(cpu);
+ return ret;
+ }
+
ret = get_standard_regs(cpu);
if (ret < 0) {
return ret;
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 37/45] target/i386/mshv: use the register page to set registers
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (35 preceding siblings ...)
2026-06-26 10:17 ` [PULL 36/45] target/i386/mshv: use the register page to get registers Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 38/45] i386/sev: Remove the example that references memory-encryption Paolo Bonzini
` (8 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Doru Blânzeanu, Magnus Kulke, Mohamed Mediouni
From: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Update mshv_store_regs to use the register page when it is mmapped and
valid to set registers.
Otherwise use the ioctls to set the registers.
Signed-off-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Reviewed-By: Magnus Kulke <magnuskulke@linux.microsoft.com>
Reviewed-by: Mohamed Mediouni <mohamed@unpredictable.fr>
Link: https://lore.kernel.org/r/20260428135053.251200-7-dblanzeanu@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
target/i386/mshv/mshv-cpu.c | 44 +++++++++++++++++++++++++++++++++----
1 file changed, 40 insertions(+), 4 deletions(-)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index ed78fa09934..126ca40b481 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -457,14 +457,50 @@ static int set_standard_regs(const CPUState *cpu)
return 0;
}
+static void mshv_set_standard_regs_vp_page(CPUState *cpu)
+{
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
+
+ env->regs_page->rax = env->regs[R_EAX];
+ env->regs_page->rbx = env->regs[R_EBX];
+ env->regs_page->rcx = env->regs[R_ECX];
+ env->regs_page->rdx = env->regs[R_EDX];
+ env->regs_page->rsi = env->regs[R_ESI];
+ env->regs_page->rdi = env->regs[R_EDI];
+ env->regs_page->rsp = env->regs[R_ESP];
+ env->regs_page->rbp = env->regs[R_EBP];
+ env->regs_page->r8 = env->regs[R_R8];
+ env->regs_page->r9 = env->regs[R_R9];
+ env->regs_page->r10 = env->regs[R_R10];
+ env->regs_page->r11 = env->regs[R_R11];
+ env->regs_page->r12 = env->regs[R_R12];
+ env->regs_page->r13 = env->regs[R_R13];
+ env->regs_page->r14 = env->regs[R_R14];
+ env->regs_page->r15 = env->regs[R_R15];
+ env->regs_page->rip = env->eip;
+ lflags_to_rflags(env);
+ env->regs_page->rflags = env->eflags;
+
+ env->regs_page->dirty |= (1u << HV_X64_REGISTER_CLASS_GENERAL)
+ | (1u << HV_X64_REGISTER_CLASS_IP)
+ | (1u << HV_X64_REGISTER_CLASS_FLAGS);
+}
+
static int store_regs(CPUState *cpu)
{
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
int ret;
- ret = set_standard_regs(cpu);
- if (ret < 0) {
- error_report("Failed to store standard registers");
- return -1;
+ /* Use register vp page to optimize registers access */
+ if (env->regs_page && env->regs_page->isvalid != 0) {
+ mshv_set_standard_regs_vp_page(cpu);
+ } else {
+ ret = set_standard_regs(cpu);
+ if (ret < 0) {
+ return ret;
+ }
}
ret = set_special_regs(cpu);
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 38/45] i386/sev: Remove the example that references memory-encryption
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (36 preceding siblings ...)
2026-06-26 10:17 ` [PULL 37/45] target/i386/mshv: use the register page to set registers Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 39/45] qemu-options: Change memory-encryption to confidential-guest-support in the example Paolo Bonzini
` (7 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Xiaoyao Li, Daniel P. Berrangé
From: Xiaoyao Li <xiaoyao.li@intel.com>
"confidential-guest-support" is the recommended property to configure
sev for the machine, and amd-memory-encryption.rst has already switched
to use "confidential-guest-support" in the example.
Instead of changing "memory-encryption" to "confidential-guest-support"
in the comment of struct SevGuestState, just drop the example for
simplicity.
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
Link: https://lore.kernel.org/r/20260512084458.622465-2-xiaoyao.li@intel.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
target/i386/sev.c | 4 ----
1 file changed, 4 deletions(-)
diff --git a/target/i386/sev.c b/target/i386/sev.c
index 99cf30806be..debe6344ab4 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -160,10 +160,6 @@ struct SevCommonStateClass {
*
* The SevGuestState object is used for creating and managing a SEV
* guest.
- *
- * # $QEMU \
- * -object sev-guest,id=sev0 \
- * -machine ...,memory-encryption=sev0
*/
struct SevGuestState {
SevCommonState parent_obj;
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 39/45] qemu-options: Change memory-encryption to confidential-guest-support in the example
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (37 preceding siblings ...)
2026-06-26 10:17 ` [PULL 38/45] i386/sev: Remove the example that references memory-encryption Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 40/45] qemu-options: Add confidential-guest-support to machine options Paolo Bonzini
` (6 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Xiaoyao Li, Daniel P. Berrangé
From: Xiaoyao Li <xiaoyao.li@intel.com>
"confidential-guest-support" is the recommended property instead of
"memory-encryption". Switch to "confidential-guest-support" in the
example of sev-guest.
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
Link: https://lore.kernel.org/r/20260512084458.622465-3-xiaoyao.li@intel.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
qemu-options.hx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/qemu-options.hx b/qemu-options.hx
index c799286153f..03456527910 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -6364,7 +6364,7 @@ SRST
# |qemu_system_x86| \\
...... \\
-object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=1 \\
- -machine ...,memory-encryption=sev0 \\
+ -machine ...,confidential-guest-support=sev0 \\
.....
``-object igvm-cfg,file=file``
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 40/45] qemu-options: Add confidential-guest-support to machine options
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (38 preceding siblings ...)
2026-06-26 10:17 ` [PULL 39/45] qemu-options: Change memory-encryption to confidential-guest-support in the example Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 41/45] qemu-options: Add description of tdx-guest object Paolo Bonzini
` (5 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Xiaoyao Li, Daniel P. Berrangé
From: Xiaoyao Li <xiaoyao.li@intel.com>
"confidential-guest-support" is the recommended property to configure
machine with confidential computing technology instead of
"memory-encryption".
Add "confidential-guest-support" to machine options and call out
explicitly "memory-encryption" is the alias of it and not recommended.
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
Link: https://lore.kernel.org/r/20260512084458.622465-4-xiaoyao.li@intel.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
qemu-options.hx | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/qemu-options.hx b/qemu-options.hx
index 03456527910..a8ee5d14e14 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -36,7 +36,8 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \
" dea-key-wrap=on|off controls support for DEA key wrapping (default=on)\n"
" suppress-vmdesc=on|off disables self-describing migration (default=off)\n"
" nvdimm=on|off controls NVDIMM support (default=off)\n"
- " memory-encryption=<id> memory encryption object to use (default=none)\n"
+ " confidential-guest-support=<id> specifies confidential guest support object (default=none)\n"
+ " memory-encryption=<id> (memory-encryption is the alias of confidential-guest-support, recommend to use confidential-guest-support)\n"
" hmat=on|off controls ACPI HMAT support (default=off)\n"
" spcr=on|off controls ACPI SPCR support (default=on)\n"
#ifdef CONFIG_POSIX
@@ -100,8 +101,12 @@ SRST
``nvdimm=on|off``
Enables or disables NVDIMM support. The default is off.
+ ``confidential-guest-support=<id>``
+ confidential guest support object to use. The default is none.
+
``memory-encryption=<id>``
- Memory encryption object to use. The default is none.
+ The alias of ``confidential-guest-support``. Recommend to use
+ confidential-guest-support.
``hmat=on|off``
Enables or disables ACPI Heterogeneous Memory Attribute Table
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 41/45] qemu-options: Add description of tdx-guest object
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (39 preceding siblings ...)
2026-06-26 10:17 ` [PULL 40/45] qemu-options: Add confidential-guest-support to machine options Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 42/45] machine: Deprecate memory-encryption Paolo Bonzini
` (4 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Xiaoyao Li
From: Xiaoyao Li <xiaoyao.li@intel.com>
Add description of tdx-guest object so that QEMU doc page can have the
description.
Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
Link: https://lore.kernel.org/r/20260512084458.622465-5-xiaoyao.li@intel.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
qemu-options.hx | 41 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/qemu-options.hx b/qemu-options.hx
index a8ee5d14e14..f1874a05915 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -6372,6 +6372,47 @@ SRST
-machine ...,confidential-guest-support=sev0 \\
.....
+ ``-object tdx-guest,id=id,[attributes=attrs,sept-ve-disable=on|off,mrconfigid=sha384_digest,mrowner=sha384_digest,mrownerconfig=sha384_digest,quote-generation-socket=socketaddr]``
+ Create an Intel Trusted Domain eXtensions (TDX) guest object, which is
+ the type of ``confidential-guest-support`` object. When pass the object
+ ID to machine's ``confidential-guest-support`` property, it can create
+ a TDX guest.
+
+ The ``attributes`` property is a 64-bit integer, which specifies the
+ TD attributes of the TD.
+
+ The ``sept-ve-disable`` property controls the bit 28 of TD attributes
+ specifically. When it's on, the EPT violation conversion to #VE on
+ guest access of PENDING pages is disabled. Some guest OS (e.g., Linux
+ TD guest) may require this to be set, otherwise they refuse to boot.
+ The default value is on.
+
+ The ``mrconfigid`` property is base64 encoded SHA384 digest, which
+ provides the ID for non-owner-defined configuration of the guest TD,
+ e.g., run-time or OS configuration. The default value is all zeros.
+
+ The ``mrowner`` property is base64 encoded SHA384 digest, which
+ provides the ID for guest TD's owner. The default value is all zeros.
+
+ The ``mrownerconfig`` property is base64 encoded SHA384 digest, which
+ provides the ID for owner-defined configuration of the guest TD, e.g.,
+ the configuration specific to the workload rather than the run-time of
+ OS. The default value is all zeros.
+
+ The ``quote-generation-socket`` property specifies the socket address
+ of the Quote Generation Service (QGS). QGS is a daemon running on the
+ host. QEMU forwards the <GetQuote> request from TD guest to QGS and
+ sents the reply (which contains generated QUOTE on success) from QGS
+ to guest TD.
+
+ .. parsed-literal::
+
+ # |qemu_system_x86| \\
+ ...... \\
+ -object '{"qom-type":"tdx-guest","id":"tdx","quote-generation-socket":{"type":"unix","path":"/var/run/qgs.socket"}}' \\
+ -machine ...,confidential-guest-support=tdx \\
+ ......
+
``-object igvm-cfg,file=file``
Create an IGVM configuration object that defines the initial state
of the guest using a file in that conforms to the Independent Guest
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 42/45] machine: Deprecate memory-encryption
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (40 preceding siblings ...)
2026-06-26 10:17 ` [PULL 41/45] qemu-options: Add description of tdx-guest object Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 43/45] i386/tdx: Use .has_gpa field to check if the gpa is valid Paolo Bonzini
` (3 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Xiaoyao Li, Daniel P. Berrangé
From: Xiaoyao Li <xiaoyao.li@intel.com>
We've had 'confidential-guest-support' for long enough that no one should
be using 'memory-encryption' anymore.
Deprecate 'memory-encryption' by adding notes in docs/about/deprecated.rst
and print a warning when 'memory-encryptio' is used.
Suggested-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
Link: https://lore.kernel.org/qemu-devel/aMPYkUsytGxLPIM7@redhat.com/
Link: https://lore.kernel.org/r/20260512084458.622465-6-xiaoyao.li@intel.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
docs/about/deprecated.rst | 7 +++++++
hw/core/machine.c | 2 ++
2 files changed, 9 insertions(+)
diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst
index 97750f5edc9..cf3e1a007c7 100644
--- a/docs/about/deprecated.rst
+++ b/docs/about/deprecated.rst
@@ -249,6 +249,13 @@ The RISC-V ``shakti_c`` machine hasn't had meaningful contributions since 2021
and is currently unmaintained. The machine is scheduled to be removed as it
appears to have no users.
+``memory-encryption`` machine property (since 11.1)
+'''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Use ``confidential-guest-support`` instead. The ``memory-encryption`` object
+was an early implementation of memory encryption support in QEMU, but it has
+been superseded by the more comprehensive ``confidential-guest-support``
+object.
Backend options
---------------
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 9a10e45aabd..5b97df6db96 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -497,6 +497,8 @@ static void machine_set_memory_encryption(Object *obj, const char *value,
Object *cgs =
object_resolve_path_component(object_get_objects_root(), value);
+ warn_report("memory-encryption is deprecated, use confidential-guest-support instead");
+
if (!cgs) {
error_setg(errp, "No such memory encryption object '%s'", value);
return;
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 43/45] i386/tdx: Use .has_gpa field to check if the gpa is valid
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (41 preceding siblings ...)
2026-06-26 10:17 ` [PULL 42/45] machine: Deprecate memory-encryption Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 44/45] i386/tdx: Make AMX alias bits supported Paolo Bonzini
` (2 subsequent siblings)
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Xiaoyao Li, Daniel P. Berrangé, Zhenzhong Duan
From: Xiaoyao Li <xiaoyao.li@intel.com>
When translating the QAPI type GuestPanicInformationTdx into its C
struct, the generated code provides a .has_gpa boolean field to indicate
whether the optional gpa field is present.
Replace the magic sentinel value -1ULL, previously used to signal "no
valid GPA", with the idiomatic .has_gpa field. This removes the
implicit sentinel coupling and makes the validity check self-documenting.
Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Link: https://lore.kernel.org/r/20260512082108.621596-2-xiaoyao.li@intel.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
system/runstate.c | 2 +-
target/i386/kvm/tdx.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/system/runstate.c b/system/runstate.c
index 0e1cb3b4e67..09be7c8d202 100644
--- a/system/runstate.c
+++ b/system/runstate.c
@@ -706,7 +706,7 @@ void qemu_system_guest_panicked(GuestPanicInformation *info)
" error code: 0x%" PRIx32 " error message:\"%s\"\n",
info->u.tdx.error_code, message);
g_free(message);
- if (info->u.tdx.gpa != -1ull) {
+ if (info->u.tdx.has_gpa) {
qemu_log_mask(LOG_GUEST_ERROR, "Additional error information "
"can be found at gpa page: 0x%" PRIx64 "\n",
info->u.tdx.gpa);
diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c
index df46fce7693..da4478cb21f 100644
--- a/target/i386/kvm/tdx.c
+++ b/target/i386/kvm/tdx.c
@@ -1403,7 +1403,7 @@ int tdx_handle_report_fatal_error(X86CPU *cpu, struct kvm_run *run)
uint64_t reg_mask = run->system_event.data[R_ECX];
char *message = NULL;
uint64_t *tmp;
- uint64_t gpa = -1ull;
+ uint64_t gpa = 0;
bool has_gpa = false;
if (error_code & 0xffff) {
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 44/45] i386/tdx: Make AMX alias bits supported
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (42 preceding siblings ...)
2026-06-26 10:17 ` [PULL 43/45] i386/tdx: Use .has_gpa field to check if the gpa is valid Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-26 10:17 ` [PULL 45/45] i386/tdx: Add CPUID_24_0_EBX_AVX10_VL_MASK as supported Paolo Bonzini
2026-06-28 11:01 ` [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Stefan Hajnoczi
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Xiaoyao Li, Chenyi Qiang
From: Xiaoyao Li <xiaoyao.li@intel.com>
When booting a TD guest on a platform that supports AMX alias bits, QEMU
emits the warning such as:
qemu-system-x86_64: warning: TDX forcibly sets the feature: CPUID[eax=1Eh,ecx=01h].EAX.amx-int8-alias [bit 0]
...
Bit[3:0] of CPUID(0x1e,1).EAX alias the AMX CPUID bits from leaf 7.
Their TDX virtualization type is "CPUID_Enabled & Native": the value is
determined by the leaf-7 AMX bit they are aliased to and the native
hardware value.
These bits must be added to the TDX supported bits list so that they can
be enabled without triggering the forced-set warning. For simplicity,
mark them as supported whenever the corresponding AMX XFAM bit is
supported, rather than checking each aliased leaf-7 bit individually.
This reduces code complexity. Any platform that supports the AMX XFAM bit
but not these alias bits will still be handled correctly, since the TDX
module provides the real value via tdx_check_features().
Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
Tested-by: Chenyi Qiang <chenyi.qiang@intel.com>
Link: https://lore.kernel.org/r/20260512082108.621596-3-xiaoyao.li@intel.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
target/i386/kvm/tdx.c | 22 ++++++++++++++++------
1 file changed, 16 insertions(+), 6 deletions(-)
diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c
index da4478cb21f..e3c5eb99842 100644
--- a/target/i386/kvm/tdx.c
+++ b/target/i386/kvm/tdx.c
@@ -560,13 +560,19 @@ typedef struct TdxXFAMDep {
} TdxXFAMDep;
/*
- * Note, only the CPUID bits whose virtualization type are "XFAM & Native" are
- * defiend here.
+ * Note, usually the CPUID bits whose virtualization type are "XFAM & Native"
+ * are defined here while "XFAM & Configured & Native" are not. Because the
+ * latter are reported as configurable bits by KVM when they are supported.
+ * And they are not supported when not in the configurable bits list from KVM
+ * even if the corresponding XFAM bit is supported.
*
- * For those whose virtualization type are "XFAM & Configured & Native", they
- * are reported as configurable bits. And they are not supported if not in the
- * configureable bits list from KVM even if the corresponding XFAM bit is
- * supported.
+ * Special cases:
+ *
+ * - AMX alias bits, their type is "CPUID_Enabled & Native" which means their
+ * value is determined by the CPUID bit they are aliased to.
+ *
+ * For simplicity, relax the dependency to related XFAM bit.
+ * tdx_check_features() will eventually catch the unsupported configurations.
*/
TdxXFAMDep tdx_xfam_deps[] = {
{ XSTATE_YMM_BIT, { FEAT_1_ECX, CPUID_EXT_FMA } },
@@ -580,6 +586,10 @@ TdxXFAMDep tdx_xfam_deps[] = {
{ XSTATE_XTILE_CFG_BIT, { FEAT_7_0_EDX, CPUID_7_0_EDX_AMX_BF16 } },
{ XSTATE_XTILE_CFG_BIT, { FEAT_7_0_EDX, CPUID_7_0_EDX_AMX_TILE } },
{ XSTATE_XTILE_CFG_BIT, { FEAT_7_0_EDX, CPUID_7_0_EDX_AMX_INT8 } },
+ { XSTATE_XTILE_CFG_BIT, { FEAT_1E_1_EAX, CPUID_1E_1_EAX_AMX_INT8_ALIAS } },
+ { XSTATE_XTILE_CFG_BIT, { FEAT_1E_1_EAX, CPUID_1E_1_EAX_AMX_BF16_ALIAS } },
+ { XSTATE_XTILE_CFG_BIT, { FEAT_1E_1_EAX, CPUID_1E_1_EAX_AMX_COMPLEX_ALIAS } },
+ { XSTATE_XTILE_CFG_BIT, { FEAT_1E_1_EAX, CPUID_1E_1_EAX_AMX_FP16_ALIAS } },
};
static struct kvm_cpuid_entry2 *find_in_supported_entry(uint32_t function,
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PULL 45/45] i386/tdx: Add CPUID_24_0_EBX_AVX10_VL_MASK as supported
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (43 preceding siblings ...)
2026-06-26 10:17 ` [PULL 44/45] i386/tdx: Make AMX alias bits supported Paolo Bonzini
@ 2026-06-26 10:17 ` Paolo Bonzini
2026-06-28 11:01 ` [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Stefan Hajnoczi
45 siblings, 0 replies; 47+ messages in thread
From: Paolo Bonzini @ 2026-06-26 10:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Xiaoyao Li, Chenyi Qiang
From: Xiaoyao Li <xiaoyao.li@intel.com>
AVX10 depends on CPUID_24_0_EBX_AVX10_VL_MASK as defined in
feature_dependencies[]. Currently CPUID_24_0_EBX_AVX10_VL_MASK is always
not supported for TDX, so AVX10 cannot be exposed to TD guest.
The TDX virtualization type of these bits is "XFAM & CPUID_Enabled &
Native": their value is determined by XFAM[5:7], the AVX10 CPUID bit,
and the native hardware value.
For simplicity, add CPUID_24_0_EBX_AVX10_VL_MASK to tdx_xfam_deps[]
under the AVX512/XFAM dependency, without separately checking the AVX10
bit. It's safe because any invalid combination supplied by the user will
be caught by tdx_check_features().
Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
Tested-by: Chenyi Qiang <chenyi.qiang@intel.com>
Link: https://lore.kernel.org/r/20260512082108.621596-4-xiaoyao.li@intel.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
target/i386/kvm/tdx.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c
index e3c5eb99842..f882578c707 100644
--- a/target/i386/kvm/tdx.c
+++ b/target/i386/kvm/tdx.c
@@ -571,6 +571,9 @@ typedef struct TdxXFAMDep {
* - AMX alias bits, their type is "CPUID_Enabled & Native" which means their
* value is determined by the CPUID bit they are aliased to.
*
+ * - AVX10_VL_MASK, their type is "XFAM & CPUID_Enabled & Native" which means
+ * their value is determined by both the corresponding XFAM bit and CPUID bit.
+ *
* For simplicity, relax the dependency to related XFAM bit.
* tdx_check_features() will eventually catch the unsupported configurations.
*/
@@ -579,6 +582,7 @@ TdxXFAMDep tdx_xfam_deps[] = {
{ XSTATE_YMM_BIT, { FEAT_7_0_EBX, CPUID_7_0_EBX_AVX2 } },
{ XSTATE_OPMASK_BIT, { FEAT_7_0_ECX, CPUID_7_0_ECX_AVX512_VBMI } },
{ XSTATE_OPMASK_BIT, { FEAT_7_0_EDX, CPUID_7_0_EDX_AVX512_FP16 } },
+ { XSTATE_OPMASK_BIT, { FEAT_24_0_EBX, CPUID_24_0_EBX_AVX10_VL_MASK } },
{ XSTATE_PT_BIT, { FEAT_7_0_EBX, CPUID_7_0_EBX_INTEL_PT } },
{ XSTATE_PKRU_BIT, { FEAT_7_0_ECX, CPUID_7_0_ECX_PKU } },
{ XSTATE_CET_U_BIT, { FEAT_7_0_ECX, CPUID_7_0_ECX_CET_SHSTK } },
--
2.54.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* Re: [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
` (44 preceding siblings ...)
2026-06-26 10:17 ` [PULL 45/45] i386/tdx: Add CPUID_24_0_EBX_AVX10_VL_MASK as supported Paolo Bonzini
@ 2026-06-28 11:01 ` Stefan Hajnoczi
45 siblings, 0 replies; 47+ messages in thread
From: Stefan Hajnoczi @ 2026-06-28 11:01 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 116 bytes --]
Applied, thanks.
Please update the changelog at https://wiki.qemu.org/ChangeLog/11.1 for any user-visible changes.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 47+ messages in thread
end of thread, other threads:[~2026-06-28 11:03 UTC | newest]
Thread overview: 47+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-26 10:17 [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Paolo Bonzini
2026-06-26 10:17 ` [PULL 01/45] accel/mshv: use mshv_create_partition_v2 payload Paolo Bonzini
2026-06-26 10:17 ` [PULL 02/45] target/i386/mshv: fix cpuid propagation bug Paolo Bonzini
2026-06-26 10:17 ` [PULL 03/45] target/i386/mshv: fix various cpuid traversal bugs Paolo Bonzini
2026-06-26 10:17 ` [PULL 04/45] target/i386/mshv: change cpuid mask to UINT32_MAX Paolo Bonzini
2026-06-26 10:17 ` [PULL 05/45] target/i386/mshv: set cpu model name on -cpu host Paolo Bonzini
2026-06-26 10:17 ` [PULL 06/45] target/i386: query mshv accel for supported cpuids Paolo Bonzini
2026-06-26 10:17 ` [PULL 07/45] target/i386/mshv: populate xsave area offsets Paolo Bonzini
2026-06-26 10:17 ` [PULL 08/45] target/i386/mshv: use hv-provided [0xD,1+2].EBX Paolo Bonzini
2026-06-26 10:17 ` [PULL 09/45] accel/mshv: disable la57 (5lvl paging) Paolo Bonzini
2026-06-26 10:17 ` [PULL 10/45] target/i386/mshv: use arch_load/store_reg fns Paolo Bonzini
2026-06-26 10:17 ` [PULL 11/45] target/i386/mshv: use generic FPU/xcr0 state Paolo Bonzini
2026-06-26 10:17 ` [PULL 12/45] target/i386/mshv: impl init/load/store_vcpu_state Paolo Bonzini
2026-06-26 10:17 ` [PULL 13/45] accel/accel-irq: add AccelRouteChange abstraction Paolo Bonzini
2026-06-26 10:17 ` [PULL 14/45] accel/accel-irq: add generic begin_route_changes Paolo Bonzini
2026-06-26 10:17 ` [PULL 15/45] accel/accel-irq: add generic commit_route_changes Paolo Bonzini
2026-06-26 10:17 ` [PULL 16/45] accel/mshv: add irq_routes to state Paolo Bonzini
2026-06-26 10:17 ` [PULL 17/45] accel/mshv: update s->irq_routes in add_msi_route Paolo Bonzini
2026-06-26 10:17 ` [PULL 18/45] accel/mshv: update s->irq_routes in update_msi_route Paolo Bonzini
2026-06-26 10:17 ` [PULL 19/45] accel/mshv: update s->irq_routes in release_virq Paolo Bonzini
2026-06-26 10:17 ` [PULL 20/45] accel/mshv: use s->irq_routes in commit_routes Paolo Bonzini
2026-06-26 10:17 ` [PULL 21/45] accel/mshv: reserve ioapic routes on s->irq_routes Paolo Bonzini
2026-06-26 10:17 ` [PULL 22/45] accel/mshv: remove redundant msi controller Paolo Bonzini
2026-06-26 10:17 ` [PULL 23/45] accel/mshv: store partition proc features Paolo Bonzini
2026-06-26 10:17 ` [PULL 24/45] target/i386/mshv: expose mshv_get_generic_regs Paolo Bonzini
2026-06-26 10:17 ` [PULL 25/45] accel/mshv: enable dirty page tracking Paolo Bonzini
2026-06-26 10:17 ` [PULL 26/45] target/i386/mshv: move msr code to arch Paolo Bonzini
2026-06-26 10:17 ` [PULL 27/45] target/i386/mshv: migrate pending ints/excs Paolo Bonzini
2026-06-26 10:17 ` [PULL 28/45] target/i386/mshv: migrate XSAVE state Paolo Bonzini
2026-06-26 10:17 ` [PULL 29/45] target/i386/mshv: reconstruct hflags after load Paolo Bonzini
2026-06-26 10:17 ` [PULL 30/45] target/i386/mshv: migrate MSRs Paolo Bonzini
2026-06-26 10:17 ` [PULL 31/45] target/i386/mshv: migrate MTRR MSRs Paolo Bonzini
2026-06-26 10:17 ` [PULL 32/45] target/i386/mshv: migrate CET/SS MSRs Paolo Bonzini
2026-06-26 10:17 ` [PULL 33/45] accel: remove unnecessary #ifdefs Paolo Bonzini
2026-06-26 10:17 ` [PULL 34/45] include/hw/hyperv: add hv_vp_register_page struct definition Paolo Bonzini
2026-06-26 10:17 ` [PULL 35/45] target/i386/mshv: hv_vp_register_page setup for the vcpu Paolo Bonzini
2026-06-26 10:17 ` [PULL 36/45] target/i386/mshv: use the register page to get registers Paolo Bonzini
2026-06-26 10:17 ` [PULL 37/45] target/i386/mshv: use the register page to set registers Paolo Bonzini
2026-06-26 10:17 ` [PULL 38/45] i386/sev: Remove the example that references memory-encryption Paolo Bonzini
2026-06-26 10:17 ` [PULL 39/45] qemu-options: Change memory-encryption to confidential-guest-support in the example Paolo Bonzini
2026-06-26 10:17 ` [PULL 40/45] qemu-options: Add confidential-guest-support to machine options Paolo Bonzini
2026-06-26 10:17 ` [PULL 41/45] qemu-options: Add description of tdx-guest object Paolo Bonzini
2026-06-26 10:17 ` [PULL 42/45] machine: Deprecate memory-encryption Paolo Bonzini
2026-06-26 10:17 ` [PULL 43/45] i386/tdx: Use .has_gpa field to check if the gpa is valid Paolo Bonzini
2026-06-26 10:17 ` [PULL 44/45] i386/tdx: Make AMX alias bits supported Paolo Bonzini
2026-06-26 10:17 ` [PULL 45/45] i386/tdx: Add CPUID_24_0_EBX_AVX10_VL_MASK as supported Paolo Bonzini
2026-06-28 11:01 ` [PULL 00/45] target/i386, CLI deprecation patches for 2026-06-25 Stefan Hajnoczi
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.