@samitouri / QOSamiQemu / commits / 662b6c053e

target/i386/mshv: migrate LAPIC state

This change implements loading and storing the hyperv lapic state as part of the load/store routines for a vcpu. The HyperV LAPIC is similar to the the split-irqchip in KVM. MSHV currently keeps PIC/IOAPIC emulation in userspace, while LAPIC interrupt injection is handled through hypercalls. We introduced dedicated apic infra in hw/i386/mshv to handle the migration and move lapic related functions from target/i386/mshv there. References have been the WHPX's whpx-apic implemenation and the mshv-ioctls crate's get_/set_lapic() impl for the mapping between MSHV/QEMU lapic state. We are mapping the lapic state that we receive from the hypervisor to fields in APICCommonState. Common fields are used where feasible, with an mshv-specific MshvAPICState object that carries mshv-specific fields. We have introduced a guard in pic_irq_request() that will early exit for the mshv accelerator, because mshv cannot take part in the userland path for legacy PIC interrupt injection. The TSC_DEADLINE MSR is also migrated as part of LAPIC migration. Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com> Reviewed-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com> Link: https://lore.kernel.org/r/20260710101534.664604-6-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

Magnus Kulke committed Jul 10, 2026 at 12:15 UTC 662b6c053ec24517b4cbfdf1911e2e78cf73aee4
12 files changed +460 -114
accel/mshv/mshv-all.c
+1
@@ -813,6 +813,7 @@ static void mshv_accel_ops_class_init(ObjectClass *oc, const void *data)
813 ops->synchronize_state = mshv_cpu_synchronize;
814 ops->synchronize_pre_loadvm = mshv_cpu_synchronize_pre_loadvm;
815 ops->cpus_are_resettable = mshv_cpus_are_resettable;
816 + ops->cpu_thread_is_idle = mshv_vcpu_thread_is_idle;
817 ops->handle_interrupt = generic_handle_interrupt;
818 }
819
hw/i386/meson.build
+1 -1
@@ -38,8 +38,8 @@ i386_ss.add(when: 'CONFIG_X86_FW_OVMF', if_true: files('pc_sysfw_ovmf.c'),
38 i386_ss.add(when: 'CONFIG_TDX', if_true: files('tdvf.c', 'tdvf-hob.c'))
39
40 subdir('kvm')
41 -subdir('xen')
41 subdir('mshv')
42 +subdir('xen')
43
44 i386_ss.add_all(xenpv_ss)
45
hw/i386/mshv/apic.c new
+395
@@ -0,0 +1,395 @@
1 +/*
2 + * MSHV in-kernel APIC support
3 + *
4 + * Copyright Microsoft, Corp. 2026
5 + *
6 + * Authors: Magnus Kulke <magnuskulke@microsoft.com>
7 + *
8 + * SPDX-License-Identifier: GPL-2.0-or-later
9 + */
10 +
11 +#include "qemu/osdep.h"
12 +#include "qemu/module.h"
13 +#include "qemu/memalign.h"
14 +#include "qemu/error-report.h"
15 +#include "hw/i386/apic_internal.h"
16 +#include "hw/i386/apic-msidef.h"
17 +#include "hw/pci/msi.h"
18 +#include "migration/vmstate.h"
19 +#include "qemu/typedefs.h"
20 +#include "system/hw_accel.h"
21 +#include "system/mshv.h"
22 +#include "system/mshv_int.h"
23 +
24 +typedef struct hv_local_interrupt_controller_state
25 + hv_local_interrupt_controller_state;
26 +
27 +#define TYPE_MSHV_APIC "mshv-apic"
28 +OBJECT_DECLARE_SIMPLE_TYPE(MshvAPICState, MSHV_APIC)
29 +
30 +struct MshvAPICState {
31 + APICCommonState parent_obj;
32 +
33 + uint32_t apic_version;
34 + uint32_t apic_lvt_cmci;
35 + uint32_t apic_error_status;
36 + uint32_t apic_counter_value;
37 + uint32_t apic_remote_read;
38 +};
39 +
40 +static int get_lapic(int cpu_fd,
41 + struct hv_local_interrupt_controller_state *state)
42 +{
43 + int ret;
44 + size_t size = 4096;
45 + /* buffer aligned to 4k, as *state requires that */
46 + void *buffer = qemu_memalign(size, size);
47 + struct mshv_get_set_vp_state mshv_state = { 0 };
48 +
49 + mshv_state.buf_ptr = (uint64_t) buffer;
50 + mshv_state.buf_sz = size;
51 + mshv_state.type = MSHV_VP_STATE_LAPIC;
52 +
53 + ret = mshv_get_vp_state(cpu_fd, &mshv_state);
54 + if (ret == 0) {
55 + memcpy(state, buffer, sizeof(*state));
56 + }
57 + qemu_vfree(buffer);
58 + if (ret < 0) {
59 + error_report("failed to get lapic");
60 + return -1;
61 + }
62 +
63 + return 0;
64 +}
65 +
66 +static int set_lapic(int cpu_fd,
67 + const struct hv_local_interrupt_controller_state *state)
68 +{
69 + int ret;
70 + size_t size = 4096;
71 + /* buffer aligned to 4k, as *state requires that */
72 + void *buffer = qemu_memalign(size, size);
73 + struct mshv_get_set_vp_state mshv_state = { 0 };
74 +
75 + if (!state) {
76 + error_report("lapic state is NULL");
77 + return -1;
78 + }
79 + memcpy(buffer, state, sizeof(*state));
80 +
81 + mshv_state.buf_ptr = (uint64_t) buffer;
82 + mshv_state.buf_sz = size;
83 + mshv_state.type = MSHV_VP_STATE_LAPIC;
84 +
85 + ret = mshv_set_vp_state(cpu_fd, &mshv_state);
86 + qemu_vfree(buffer);
87 + if (ret < 0) {
88 + error_report("failed to set lapic: %s", strerror(errno));
89 + return -1;
90 + }
91 +
92 + return 0;
93 +}
94 +
95 +static void populate_apic_state(CPUState *cpu,
96 + const hv_local_interrupt_controller_state *hv)
97 +{
98 + X86CPU *x86cpu = X86_CPU(cpu);
99 + MshvAPICState *ms = MSHV_APIC(x86cpu->apic_state);
100 + APICCommonState *s = &ms->parent_obj;
101 + size_t i;
102 +
103 + /*
104 + * x2APIC:
105 + * - APIC ID is the full 32-bit initial_apic_id
106 + * - LDR is read-only, architecturally derived from the ID
107 + * - DFR does not exist in x2APIC mode
108 + */
109 + if (is_x2apic_mode(s)) {
110 + s->initial_apic_id = hv->apic_id;
111 + } else {
112 + s->id = hv->apic_id >> 24;
113 + s->log_dest = hv->apic_ldr >> 24;
114 + s->dest_mode = hv->apic_dfr >> 28;
115 + }
116 + ms->apic_version = hv->apic_version;
117 + s->spurious_vec = hv->apic_spurious;
118 + for (i = 0; i < 8; i++) {
119 + s->isr[i] = hv->apic_isr[i];
120 + s->tmr[i] = hv->apic_tmr[i];
121 + s->irr[i] = hv->apic_irr[i];
122 + }
123 + s->esr = hv->apic_esr;
124 + s->icr[1] = hv->apic_icr_high;
125 + s->icr[0] = hv->apic_icr_low;
126 +
127 + s->lvt[APIC_LVT_TIMER] = hv->apic_lvt_timer;
128 + s->lvt[APIC_LVT_THERMAL] = hv->apic_lvt_thermal;
129 + s->lvt[APIC_LVT_PERFORM] = hv->apic_lvt_perfmon;
130 + s->lvt[APIC_LVT_LINT0] = hv->apic_lvt_lint0;
131 + s->lvt[APIC_LVT_LINT1] = hv->apic_lvt_lint1;
132 + s->lvt[APIC_LVT_ERROR] = hv->apic_lvt_error;
133 + ms->apic_lvt_cmci = hv->apic_lvt_cmci;
134 +
135 + ms->apic_error_status = hv->apic_error_status;
136 + s->initial_count = hv->apic_initial_count;
137 + ms->apic_counter_value = hv->apic_counter_value;
138 + s->divide_conf = hv->apic_divide_configuration;
139 + ms->apic_remote_read = hv->apic_remote_read;
140 +}
141 +
142 +static uint32_t set_apic_delivery_mode(uint32_t reg, uint32_t mode)
143 +{
144 + return ((reg) & ~0x700) | ((mode) << 8);
145 +}
146 +
147 +int mshv_init_lint(CPUState *cpu)
148 +{
149 + uint32_t *lvt_lint0, *lvt_lint1;
150 + int cpu_fd = mshv_vcpufd(cpu);
151 + int ret;
152 + struct hv_local_interrupt_controller_state lapic_state = { 0 };
153 +
154 + ret = get_lapic(cpu_fd, &lapic_state);
155 + if (ret < 0) {
156 + return ret;
157 + }
158 +
159 + lvt_lint0 = &lapic_state.apic_lvt_lint0;
160 + *lvt_lint0 = set_apic_delivery_mode(*lvt_lint0, APIC_DM_EXTINT);
161 +
162 + lvt_lint1 = &lapic_state.apic_lvt_lint1;
163 + *lvt_lint1 = set_apic_delivery_mode(*lvt_lint1, APIC_DM_NMI);
164 +
165 + /* TODO: should we skip setting lapic if the values are the same? */
166 +
167 + ret = set_lapic(cpu_fd, &lapic_state);
168 + if (ret < 0) {
169 + return -1;
170 + }
171 +
172 + populate_apic_state(cpu, &lapic_state);
173 +
174 + return 0;
175 +}
176 +
177 +static void populate_hv_lapic_state(hv_local_interrupt_controller_state *hv,
178 + const CPUState *cpu)
179 +{
180 + uint32_t x2apic_id;
181 + X86CPU *x86cpu = X86_CPU(cpu);
182 + MshvAPICState *ms = MSHV_APIC(x86cpu->apic_state);
183 + APICCommonState *s = &ms->parent_obj;
184 + size_t i;
185 +
186 + /*
187 + * x2APIC:
188 + * - APIC ID is the full 32-bit initial_apic_id
189 + * - LDR is read-only, architecturally derived from the ID
190 + * - DFR does not exist in x2APIC mode
191 + */
192 + if (is_x2apic_mode(s)) {
193 + x2apic_id = s->initial_apic_id;
194 +
195 + hv->apic_id = x2apic_id;
196 + hv->apic_ldr = ((x2apic_id >> 4) << 16) | (1 << (x2apic_id & 0xf));
197 + hv->apic_dfr = 0;
198 + } else {
199 + hv->apic_id = s->id << 24;
200 + hv->apic_ldr = s->log_dest << 24;
201 + hv->apic_dfr = s->dest_mode << 28 | 0x0fffffff;
202 + }
203 + hv->apic_version = ms->apic_version;
204 + hv->apic_spurious = s->spurious_vec;
205 + for (i = 0; i < 8; i++) {
206 + hv->apic_isr[i] = s->isr[i];
207 + hv->apic_tmr[i] = s->tmr[i];
208 + hv->apic_irr[i] = s->irr[i];
209 + }
210 + hv->apic_esr = s->esr;
211 + hv->apic_icr_high = s->icr[1];
212 + hv->apic_icr_low = s->icr[0];
213 +
214 + hv->apic_lvt_timer = s->lvt[APIC_LVT_TIMER];
215 + hv->apic_lvt_thermal = s->lvt[APIC_LVT_THERMAL];
216 + hv->apic_lvt_perfmon = s->lvt[APIC_LVT_PERFORM];
217 + hv->apic_lvt_lint0 = s->lvt[APIC_LVT_LINT0];
218 + hv->apic_lvt_lint1 = s->lvt[APIC_LVT_LINT1];
219 + hv->apic_lvt_error = s->lvt[APIC_LVT_ERROR];
220 + hv->apic_lvt_cmci = ms->apic_lvt_cmci;
221 +
222 + hv->apic_error_status = ms->apic_error_status;
223 + hv->apic_initial_count = s->initial_count;
224 + hv->apic_counter_value = ms->apic_counter_value;
225 + hv->apic_divide_configuration = s->divide_conf;
226 + hv->apic_remote_read = ms->apic_remote_read;
227 +}
228 +
229 +int mshv_set_lapic(const CPUState *cpu)
230 +{
231 + int cpu_fd = mshv_vcpufd(cpu);
232 + struct hv_local_interrupt_controller_state lapic_state = { 0 };
233 +
234 + populate_hv_lapic_state(&lapic_state, cpu);
235 +
236 + return set_lapic(cpu_fd, &lapic_state);
237 +}
238 +
239 +int mshv_get_lapic(CPUState *cpu)
240 +{
241 + int cpu_fd = mshv_vcpufd(cpu);
242 + int ret;
243 + struct hv_local_interrupt_controller_state lapic_state = { 0 };
244 +
245 + ret = get_lapic(cpu_fd, &lapic_state);
246 + if (ret < 0) {
247 + return -1;
248 + }
249 +
250 + populate_apic_state(cpu, &lapic_state);
251 +
252 + return 0;
253 +}
254 +
255 +static int mshv_apic_set_base(APICCommonState *s, uint64_t val)
256 +{
257 + s->apicbase = val;
258 +
259 + return 0;
260 +}
261 +
262 +static void mshv_apic_set_tpr(APICCommonState *s, uint8_t val)
263 +{
264 + s->tpr = (val & APIC_PR_SUB_CLASS) << APIC_PR_CLASS_SHIFT;
265 +}
266 +
267 +static uint8_t mshv_apic_get_tpr(APICCommonState *s)
268 +{
269 + return s->tpr >> APIC_PR_CLASS_SHIFT;
270 +}
271 +
272 +static void mshv_apic_external_nmi(APICCommonState *s)
273 +{
274 +}
275 +
276 +static void mshv_apic_vapic_base_update(APICCommonState *s)
277 +{
278 +}
279 +
280 +static void mshv_send_msi(MSIMessage *msi)
281 +{
282 + uint64_t addr;
283 + uint32_t data, dest;
284 + uint8_t vector, dest_mode, trigger_mode, delivery;
285 +
286 + addr = msi->address;
287 + data = msi->data;
288 + dest = (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT |
289 + (addr >> 32);
290 + vector = (data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT;
291 + dest_mode = (addr >> MSI_ADDR_DEST_MODE_SHIFT) & 0x1;
292 + trigger_mode = (data >> MSI_DATA_TRIGGER_SHIFT) & 0x1;
293 + delivery = (data >> MSI_DATA_DELIVERY_MODE_SHIFT) &
294 + MSI_DATA_DELIVERY_MODE_MASK;
295 +
296 + mshv_request_interrupt(mshv_state, delivery, vector, dest, dest_mode,
297 + trigger_mode);
298 +}
299 +
300 +static uint64_t mshv_apic_mem_read(void *opaque, hwaddr addr,
301 + unsigned size)
302 +{
303 + return UINT64_MAX;
304 +}
305 +
306 +static void mshv_apic_mem_write(void *opaque, hwaddr addr,
307 + uint64_t data, unsigned size)
308 +{
309 + MSIMessage msg = { .address = addr, .data = data };
310 +
311 + mshv_send_msi(&msg);
312 +}
313 +
314 +static const MemoryRegionOps mshv_apic_io_ops = {
315 + .read = mshv_apic_mem_read,
316 + .write = mshv_apic_mem_write,
317 + .endianness = DEVICE_LITTLE_ENDIAN,
318 +};
319 +
320 +static void mshv_apic_reset(APICCommonState *s)
321 +{
322 + s->wait_for_sipi = 0;
323 +}
324 +
325 +static const VMStateDescription vmstate_mshv_apic = {
326 + .name = "mshv-apic",
327 + .version_id = 1,
328 + .minimum_version_id = 1,
329 + .fields = (const VMStateField[]) {
330 + VMSTATE_UINT32(apic_version, MshvAPICState),
331 + VMSTATE_UINT32(apic_lvt_cmci, MshvAPICState),
332 + VMSTATE_UINT32(apic_error_status, MshvAPICState),
333 + VMSTATE_UINT32(apic_counter_value, MshvAPICState),
334 + VMSTATE_UINT32(apic_remote_read, MshvAPICState),
335 + VMSTATE_END_OF_LIST()
336 + }
337 +};
338 +
339 +static void mshv_apic_realize(DeviceState *dev, Error **errp)
340 +{
341 + APICCommonState *s = APIC_COMMON(dev);
342 + MshvAPICState *ms = MSHV_APIC(dev);
343 +
344 + memory_region_init_io(&s->io_memory, OBJECT(s), &mshv_apic_io_ops, s,
345 + "mshv-apic-msi", APIC_SPACE_SIZE);
346 +
347 + msi_nonbroken = true;
348 +
349 + /*
350 + * We register this state explicity, rather than going via dc->vmsd.
351 + * The auto-wiring would register the state with
352 + * instance_id == VMSTATE_INSTANCE_ID_ANY, which for the APIC doesn't
353 + * work, b/c the ID carries semantic meaning for restoring the state
354 + * on the destination (which vcpu it belongs to).
355 + */
356 + vmstate_register_with_alias_id(NULL,
357 + s->initial_apic_id, &vmstate_mshv_apic, ms,
358 + -1, 0, NULL);
359 +}
360 +
361 +static void mshv_apic_unrealize(DeviceState *dev)
362 +{
363 + MshvAPICState *ms = MSHV_APIC(dev);
364 +
365 + vmstate_unregister(NULL, &vmstate_mshv_apic, ms);
366 +}
367 +
368 +static void mshv_apic_class_init(ObjectClass *klass, const void *data)
369 +{
370 + APICCommonClass *k = APIC_COMMON_CLASS(klass);
371 +
372 + k->realize = mshv_apic_realize;
373 + k->unrealize = mshv_apic_unrealize;
374 + k->reset = mshv_apic_reset;
375 + k->set_base = mshv_apic_set_base;
376 + k->set_tpr = mshv_apic_set_tpr;
377 + k->get_tpr = mshv_apic_get_tpr;
378 + k->external_nmi = mshv_apic_external_nmi;
379 + k->vapic_base_update = mshv_apic_vapic_base_update;
380 + k->send_msi = mshv_send_msi;
381 +}
382 +
383 +static const TypeInfo mshv_apic_info = {
384 + .name = TYPE_MSHV_APIC,
385 + .parent = TYPE_APIC_COMMON,
386 + .instance_size = sizeof(MshvAPICState),
387 + .class_init = mshv_apic_class_init,
388 +};
389 +
390 +static void mshv_apic_register_types(void)
391 +{
392 + type_register_static(&mshv_apic_info);
393 +}
394 +
395 +type_init(mshv_apic_register_types)
hw/i386/mshv/meson.build
+1
@@ -1,4 +1,5 @@
1 i386_mshv_ss = ss.source_set()
2 i386_mshv_ss.add(files('clock.c'))
3 +i386_mshv_ss.add(when: 'CONFIG_APIC', if_true: files('apic.c'))
4
5 i386_ss.add_all(when: 'CONFIG_MSHV', if_true: i386_mshv_ss)
hw/i386/x86-cpu.c
+6
@@ -22,6 +22,7 @@
22 */
23 #include "qemu/osdep.h"
24 #include "system/whpx.h"
25 +#include "system/mshv.h"
26 #include "system/cpu-timers.h"
27 #include "trace.h"
28
@@ -44,6 +45,11 @@ static void pic_irq_request(void *opaque, int irq, int level)
45 X86CPU *cpu = X86_CPU(cs);
46
47 trace_x86_pic_interrupt(irq, level);
48 +
49 + if (mshv_irqchip_in_kernel()) {
50 + return;
51 + }
52 +
53 if (cpu_is_apic_enabled(cpu->apic_state) && !kvm_irqchip_in_kernel() &&
54 !whpx_irqchip_in_kernel()) {
55 CPU_FOREACH(cs) {
include/hw/hyperv/hvgdk_mini.h
+2
@@ -168,6 +168,7 @@ typedef enum hv_register_name {
168 /* Available */
169
170 HV_X64_REGISTER_SPEC_CTRL = 0x00080084,
171 + HV_X64_REGISTER_TSC_DEADLINE = 0x00080095,
172 HV_X64_REGISTER_TSC_ADJUST = 0x00080096,
173
174 /* CET / Shadow Stack */
@@ -930,6 +931,7 @@ struct hv_cpuid {
931 #define IA32_MSR_DEBUG_CTL 0x1D9
932 #define IA32_MSR_SPEC_CTRL 0x00000048
933 #define IA32_MSR_TSC_ADJUST 0x0000003b
934 +#define IA32_MSR_TSC_DEADLINE 0x000006e0
935
936 #define IA32_MSR_MISC_ENABLE 0x000001a0
937
include/hw/i386/apic-msidef.h
+1
@@ -13,6 +13,7 @@
13 #define MSI_DATA_VECTOR_MASK 0x000000ff
14
15 #define MSI_DATA_DELIVERY_MODE_SHIFT 8
16 +#define MSI_DATA_DELIVERY_MODE_MASK 7
17 #define MSI_DATA_LEVEL_SHIFT 14
18 #define MSI_DATA_TRIGGER_SHIFT 15
19
include/system/mshv.h
+2
@@ -41,9 +41,11 @@
41 extern bool mshv_allowed;
42 #define mshv_enabled() (mshv_allowed)
43 #define mshv_msi_via_irqfd_enabled() mshv_enabled()
44 +#define mshv_irqchip_in_kernel() mshv_enabled()
45 #else /* CONFIG_MSHV_IS_POSSIBLE */
46 #define mshv_enabled() false
47 #define mshv_msi_via_irqfd_enabled() mshv_enabled()
48 +#define mshv_irqchip_in_kernel() mshv_enabled()
49 #endif
50
51 #define TYPE_MSHV_ACCEL ACCEL_CLASS_NAME("mshv")
include/system/mshv_int.h
+7 -1
@@ -105,10 +105,16 @@ void mshv_arch_amend_proc_features(
105 void mshv_arch_disable_partition_proc_features(
106 union hv_partition_processor_features *disabled_features);
107 int mshv_arch_post_init_vm(int vm_fd);
108 -
108 +int mshv_get_vp_state(int cpu_fd, struct mshv_get_set_vp_state *state);
109 +int mshv_set_vp_state(int cpu_fd, const struct mshv_get_set_vp_state *state);
110 typedef struct mshv_root_hvcall mshv_root_hvcall;
111 int mshv_hvcall(int fd, const mshv_root_hvcall *args);
112
113 +/* apic */
114 +int mshv_init_lint(CPUState *cpu);
115 +int mshv_set_lapic(const CPUState *cpu);
116 +int mshv_get_lapic(CPUState *cpu);
117 +
118 /* memory */
119 typedef struct MshvMemoryRegion {
120 uint64_t guest_phys_addr;
target/i386/cpu-apic.c
+3
@@ -14,6 +14,7 @@
14 #include "system/hw_accel.h"
15 #include "system/kvm.h"
16 #include "system/xen.h"
17 +#include "system/mshv.h"
18 #include "system/address-spaces.h"
19 #include "hw/core/qdev-properties.h"
20 #include "hw/i386/apic_internal.h"
@@ -34,6 +35,8 @@ APICCommonClass *apic_get_class(Error **errp)
35 apic_type = "xen-apic";
36 } else if (whpx_irqchip_in_kernel()) {
37 apic_type = "whpx-apic";
38 + } else if (mshv_enabled()) {
39 + apic_type = "mshv-apic";
40 }
41
42 return APIC_COMMON_CLASS(object_class_by_name(apic_type));
target/i386/mshv/mshv-cpu.c
+39 -112
@@ -21,7 +21,6 @@
21 #include "hw/hyperv/hvgdk.h"
22 #include "hw/hyperv/hvgdk_mini.h"
23 #include "hw/hyperv/hvhdk_mini.h"
24 -#include "hw/i386/apic_internal.h"
24
25 #include "cpu.h"
26 #include "host-cpu.h"
@@ -955,6 +954,11 @@ int mshv_arch_load_vcpu_state(CPUState *cpu)
954 return ret;
955 }
956
957 + ret = mshv_get_lapic(cpu);
958 + if (ret < 0) {
959 + return ret;
960 + }
961 +
962 ret = mshv_get_msrs(cpu);
963 if (ret < 0) {
964 return ret;
@@ -1377,116 +1381,6 @@ static int set_xc_reg(const CPUState *cpu)
1381 return 0;
1382 }
1383
1380 -static int get_vp_state(int cpu_fd, struct mshv_get_set_vp_state *state)
1381 -{
1382 - int ret;
1383 -
1384 - ret = ioctl(cpu_fd, MSHV_GET_VP_STATE, state);
1385 - if (ret < 0) {
1386 - error_report("failed to get partition state: %s", strerror(errno));
1387 - return -1;
1388 - }
1389 -
1390 - return 0;
1391 -}
1392 -
1393 -static int get_lapic(const CPUState *cpu,
1394 - struct hv_local_interrupt_controller_state *state)
1395 -{
1396 - int ret;
1397 - size_t size = 4096;
1398 - /* buffer aligned to 4k, as *state requires that */
1399 - void *buffer = qemu_memalign(size, size);
1400 - struct mshv_get_set_vp_state mshv_state = { 0 };
1401 - int cpu_fd = mshv_vcpufd(cpu);
1402 -
1403 - mshv_state.buf_ptr = (uint64_t) buffer;
1404 - mshv_state.buf_sz = size;
1405 - mshv_state.type = MSHV_VP_STATE_LAPIC;
1406 -
1407 - ret = get_vp_state(cpu_fd, &mshv_state);
1408 - if (ret == 0) {
1409 - memcpy(state, buffer, sizeof(*state));
1410 - }
1411 - qemu_vfree(buffer);
1412 - if (ret < 0) {
1413 - error_report("failed to get lapic");
1414 - return -1;
1415 - }
1416 -
1417 - return 0;
1418 -}
1419 -
1420 -static uint32_t set_apic_delivery_mode(uint32_t reg, uint32_t mode)
1421 -{
1422 - return ((reg) & ~0x700) | ((mode) << 8);
1423 -}
1424 -
1425 -static int set_vp_state(int cpu_fd, const struct mshv_get_set_vp_state *state)
1426 -{
1427 - int ret;
1428 -
1429 - ret = ioctl(cpu_fd, MSHV_SET_VP_STATE, state);
1430 - if (ret < 0) {
1431 - error_report("failed to set partition state: %s", strerror(errno));
1432 - return -1;
1433 - }
1434 -
1435 - return 0;
1436 -}
1437 -
1438 -static int set_lapic(const CPUState *cpu,
1439 - const struct hv_local_interrupt_controller_state *state)
1440 -{
1441 - int ret;
1442 - size_t size = 4096;
1443 - /* buffer aligned to 4k, as *state requires that */
1444 - void *buffer = qemu_memalign(size, size);
1445 - struct mshv_get_set_vp_state mshv_state = { 0 };
1446 - int cpu_fd = mshv_vcpufd(cpu);
1447 -
1448 - if (!state) {
1449 - error_report("lapic state is NULL");
1450 - return -1;
1451 - }
1452 - memcpy(buffer, state, sizeof(*state));
1453 -
1454 - mshv_state.buf_ptr = (uint64_t) buffer;
1455 - mshv_state.buf_sz = size;
1456 - mshv_state.type = MSHV_VP_STATE_LAPIC;
1457 -
1458 - ret = set_vp_state(cpu_fd, &mshv_state);
1459 - qemu_vfree(buffer);
1460 - if (ret < 0) {
1461 - error_report("failed to set lapic: %s", strerror(errno));
1462 - return -1;
1463 - }
1464 -
1465 - return 0;
1466 -}
1467 -
1468 -static int init_lint(const CPUState *cpu)
1469 -{
1470 - int ret;
1471 - uint32_t *lvt_lint0, *lvt_lint1;
1472 -
1473 - struct hv_local_interrupt_controller_state lapic_state = { 0 };
1474 - ret = get_lapic(cpu, &lapic_state);
1475 - if (ret < 0) {
1476 - return ret;
1477 - }
1478 -
1479 - lvt_lint0 = &lapic_state.apic_lvt_lint0;
1480 - *lvt_lint0 = set_apic_delivery_mode(*lvt_lint0, APIC_DM_EXTINT);
1481 -
1482 - lvt_lint1 = &lapic_state.apic_lvt_lint1;
1483 - *lvt_lint1 = set_apic_delivery_mode(*lvt_lint1, APIC_DM_NMI);
1484 -
1485 - /* TODO: should we skip setting lapic if the values are the same? */
1486 -
1487 - return set_lapic(cpu, &lapic_state);
1488 -}
1489 -
1384 int mshv_arch_store_vcpu_state(const CPUState *cpu)
1385 {
1386 int ret;
@@ -1511,6 +1405,12 @@ int mshv_arch_store_vcpu_state(const CPUState *cpu)
1405 return ret;
1406 }
1407
1408 + /* INVARIANT: special regs (APIC_BASE) must be restored before LAPIC */
1409 + ret = mshv_set_lapic(cpu);
1410 + if (ret < 0) {
1411 + return ret;
1412 + }
1413 +
1414 ret = mshv_set_msrs(cpu);
1415 if (ret < 0) {
1416 return ret;
@@ -2100,7 +2000,7 @@ void mshv_arch_init_vcpu(CPUState *cpu)
2000 ret = mshv_init_msrs(cpu);
2001 assert(ret == 0);
2002
2103 - ret = init_lint(cpu);
2003 + ret = mshv_init_lint(cpu);
2004 assert(ret == 0);
2005 }
2006
@@ -2247,6 +2147,33 @@ static void mshv_cpu_xsave_init(void)
2147 }
2148 }
2149
2150 +int mshv_set_vp_state(int cpu_fd, const struct mshv_get_set_vp_state *state)
2151 +{
2152 + int ret;
2153 +
2154 + ret = ioctl(cpu_fd, MSHV_SET_VP_STATE, state);
2155 + if (ret < 0) {
2156 + error_report("failed to set partition state: %s", strerror(errno));
2157 + return -1;
2158 + }
2159 +
2160 + return 0;
2161 +}
2162 +
2163 +
2164 +int mshv_get_vp_state(int cpu_fd, struct mshv_get_set_vp_state *state)
2165 +{
2166 + int ret;
2167 +
2168 + ret = ioctl(cpu_fd, MSHV_GET_VP_STATE, state);
2169 + if (ret < 0) {
2170 + error_report("failed to get partition state: %s", strerror(errno));
2171 + return -1;
2172 + }
2173 +
2174 + return 0;
2175 +}
2176 +
2177 static void mshv_cpu_instance_init(CPUState *cs)
2178 {
2179 X86CPU *cpu = X86_CPU(cs);
target/i386/mshv/msr.c
+2
@@ -60,6 +60,8 @@ static const MshvMsrEnvMap msr_env_map[] = {
60 offsetof(CPUX86State, tsc_aux) },
61 { IA32_MSR_TSC_ADJUST, HV_X64_REGISTER_TSC_ADJUST,
62 offsetof(CPUX86State, tsc_adjust) },
63 + { IA32_MSR_TSC_DEADLINE, HV_X64_REGISTER_TSC_DEADLINE,
64 + offsetof(CPUX86State, tsc_deadline) },
65
66 /* Hyper-V per-partition MSRs */
67 { HV_X64_MSR_HYPERCALL, HV_X64_REGISTER_HYPERCALL,