master
c 858 lines 22.4 KB
Raw
1 /*
2 * QEMU MSHV support
3 *
4 * Copyright Microsoft, Corp. 2025
5 *
6 * Authors:
7 * Ziqiao Zhou <ziqiaozhou@microsoft.com>
8 * Magnus Kulke <magnuskulke@microsoft.com>
9 * Jinank Jain <jinankjain@microsoft.com>
10 * Wei Liu <liuwe@microsoft.com>
11 *
12 * SPDX-License-Identifier: GPL-2.0-or-later
13 *
14 */
15
16 #include "qemu/osdep.h"
17 #include "qapi/error.h"
18 #include "qemu/error-report.h"
19 #include "qemu/event_notifier.h"
20 #include "qemu/module.h"
21 #include "qemu/main-loop.h"
22 #include "hw/core/boards.h"
23
24 #include "hw/hyperv/hvhdk.h"
25 #include "hw/hyperv/hvhdk_mini.h"
26 #include "hw/hyperv/hvgdk.h"
27 #include "hw/hyperv/hvgdk_mini.h"
28 #include "linux/mshv.h"
29
30 #include "qemu/accel.h"
31 #include "qemu/guest-random.h"
32 #include "accel/accel-ops.h"
33 #include "accel/accel-cpu-ops.h"
34 #include "exec/cpu-common.h"
35 #include "system/cpus.h"
36 #include "system/runstate.h"
37 #include "system/accel-blocker.h"
38 #include "system/address-spaces.h"
39 #include "system/mshv.h"
40 #include "system/mshv_int.h"
41 #include "system/reset.h"
42 #include "migration/qemu-file-types.h"
43 #include "migration/register.h"
44 #include "trace.h"
45 #include <err.h>
46 #include <sys/ioctl.h>
47
48 bool mshv_allowed;
49
50 MshvState *mshv_state;
51
52 static int init_mshv(int *mshv_fd)
53 {
54 int fd = open("/dev/mshv", O_RDWR | O_CLOEXEC);
55 if (fd < 0) {
56 error_report("Failed to open /dev/mshv: %s", strerror(errno));
57 return -1;
58 }
59 *mshv_fd = fd;
60 return 0;
61 }
62
63 static int mshv_load_cleanup(void *opaque)
64 {
65 CPUState *cpu;
66 int ret;
67
68 ret = mshv_arch_set_partition_msrs(first_cpu);
69 if (ret < 0) {
70 error_report("Failed to set partition MSRs: %s", strerror(-ret));
71 return -1;
72 }
73
74 CPU_FOREACH(cpu) {
75 ret = mshv_arch_set_mp_state(cpu);
76 if (ret < 0) {
77 error_report("Failed to set mp state for vCPU %d: %s",
78 cpu->cpu_index, strerror(-ret));
79 return -1;
80 }
81 }
82
83 return 0;
84 }
85
86
87 static int get_host_partition_property(int mshv_fd, uint32_t property_code,
88 uint64_t *value)
89 {
90 int ret;
91 struct hv_input_get_partition_property in = {0};
92 struct hv_output_get_partition_property out = {0};
93 struct mshv_root_hvcall args = {0};
94
95 in.property_code = property_code;
96
97 args.code = HVCALL_GET_PARTITION_PROPERTY;
98 args.in_sz = sizeof(in);
99 args.in_ptr = (uint64_t)&in;
100 args.out_sz = sizeof(out);
101 args.out_ptr = (uint64_t)&out;
102
103 ret = ioctl(mshv_fd, MSHV_ROOT_HVCALL, &args);
104 if (ret < 0) {
105 error_report("Failed to get host partition property bank: %s",
106 strerror(errno));
107 return -1;
108 }
109
110 *value = out.property_value;
111 return 0;
112 }
113
114 static int get_partition_property(int vm_fd, uint32_t feature_bank,
115 uint64_t *value)
116 {
117 struct hv_input_get_partition_property in = {0};
118 struct hv_output_get_partition_property out = {0};
119 struct mshv_root_hvcall args = {0};
120 int ret;
121
122 in.property_code = feature_bank;
123
124 args.code = HVCALL_GET_PARTITION_PROPERTY;
125 args.in_sz = sizeof(in);
126 args.in_ptr = (uint64_t)&in;
127 args.out_sz = sizeof(out);
128 args.out_ptr = (uint64_t)&out;
129
130 ret = ioctl(vm_fd, MSHV_ROOT_HVCALL, &args);
131 if (ret < 0) {
132 error_report("Failed to get guest partition property bank: %s",
133 strerror(errno));
134 return -1;
135 }
136
137 *value = out.property_value;
138 return 0;
139 }
140
141 static int get_proc_features(int vm_fd,
142 union hv_partition_processor_features *features)
143 {
144 int ret;
145
146 ret = get_partition_property(vm_fd,
147 HV_PARTITION_PROPERTY_PROCESSOR_FEATURES0,
148 &features->as_uint64[0]);
149 if (ret < 0) {
150 error_report("Failed to get processor features bank 0");
151 return -1;
152 }
153
154 ret = get_partition_property(vm_fd,
155 HV_PARTITION_PROPERTY_PROCESSOR_FEATURES1,
156 &features->as_uint64[1]);
157 if (ret < 0) {
158 error_report("Failed to get processor features bank 1");
159 return -1;
160 }
161
162 return 0;
163 }
164
165 static int create_partition(int mshv_fd, int *vm_fd)
166 {
167 int ret;
168 uint64_t pt_flags, host_proc_features;
169 union hv_partition_processor_xsave_features disabled_xsave_features;
170 union hv_partition_processor_features disabled_partition_features = {0};
171
172 struct mshv_create_partition_v2 args = {0};
173
174 QEMU_BUILD_BUG_ON(MSHV_NUM_CPU_FEATURES_BANKS != 2);
175
176 /* Initialize pt_flags with the desired features */
177 pt_flags = (1ULL << MSHV_PT_BIT_LAPIC) |
178 (1ULL << MSHV_PT_BIT_X2APIC) |
179 (1ULL << MSHV_PT_BIT_GPA_SUPER_PAGES) |
180 (1ULL << MSHV_PT_BIT_CPU_AND_XSAVE_FEATURES);
181
182 /* enable all */
183 disabled_xsave_features.as_uint64 = 0;
184 /*
185 * AMX TILE XSAVE state (XTILE_DATA) is 8KB, which exceeds the
186 * current fixed 4KB XSAVE buffer size.
187 */
188 disabled_xsave_features.amx_tile_support = 1;
189 disabled_xsave_features.amx_bf16_support = 1;
190 disabled_xsave_features.amx_int8_support = 1;
191 disabled_xsave_features.amx_fp16_support = 1;
192
193 /*
194 * query host for supported processor features and disable unsupported
195 * features: (0 means supported, 1 means disabled, hence the negation)
196 */
197 ret = get_host_partition_property(mshv_fd,
198 HV_PARTITION_PROPERTY_PROCESSOR_FEATURES0,
199 &host_proc_features);
200 if (ret < 0) {
201 error_report("Failed to get host processor feature bank 0");
202 return -1;
203 }
204 args.pt_cpu_fbanks[0] = ~host_proc_features;
205
206 ret = get_host_partition_property(mshv_fd,
207 HV_PARTITION_PROPERTY_PROCESSOR_FEATURES1,
208 &host_proc_features);
209 if (ret < 0) {
210 error_report("Failed to get host processor feature bank 1");
211 return -1;
212 }
213 args.pt_cpu_fbanks[1] = ~host_proc_features;
214
215 /* arch-specific features we disable regardless of host support */
216 mshv_arch_disable_partition_proc_features(&disabled_partition_features);
217 args.pt_cpu_fbanks[0] |= disabled_partition_features.as_uint64[0];
218 args.pt_cpu_fbanks[1] |= disabled_partition_features.as_uint64[1];
219
220 /* populate args structure */
221 args.pt_flags = pt_flags;
222 args.pt_isolation = MSHV_PT_ISOLATION_NONE;
223 args.pt_disabled_xsave = disabled_xsave_features.as_uint64;
224 args.pt_num_cpu_fbanks = MSHV_NUM_CPU_FEATURES_BANKS;
225
226 ret = ioctl(mshv_fd, MSHV_CREATE_PARTITION, &args);
227 if (ret < 0) {
228 error_report("Failed to create partition: %s", strerror(errno));
229 return -1;
230 }
231
232 *vm_fd = ret;
233 return 0;
234 }
235
236 static int set_synthetic_proc_features(int vm_fd)
237 {
238 int ret;
239 struct hv_input_set_partition_property in = {0};
240 union hv_partition_synthetic_processor_features features = {0};
241
242 /* Access the bitfield and set the desired features */
243 features.hypervisor_present = 1;
244 features.hv1 = 1;
245 features.access_partition_reference_counter = 1;
246 features.access_synic_regs = 1;
247 features.access_synthetic_timer_regs = 1;
248 features.access_partition_reference_tsc = 1;
249 features.access_frequency_regs = 1;
250 features.access_intr_ctrl_regs = 1;
251 features.access_vp_index = 1;
252 features.access_hypercall_regs = 1;
253 features.tb_flush_hypercalls = 1;
254 features.synthetic_cluster_ipi = 1;
255 features.direct_synthetic_timers = 1;
256
257 mshv_arch_amend_proc_features(&features);
258
259 in.property_code = HV_PARTITION_PROPERTY_SYNTHETIC_PROC_FEATURES;
260 in.property_value = features.as_uint64[0];
261
262 struct mshv_root_hvcall args = {0};
263 args.code = HVCALL_SET_PARTITION_PROPERTY;
264 args.in_sz = sizeof(in);
265 args.in_ptr = (uint64_t)&in;
266
267 trace_mshv_hvcall_args("synthetic_proc_features", args.code, args.in_sz);
268
269 ret = mshv_hvcall(vm_fd, &args);
270 if (ret < 0) {
271 error_report("Failed to set synthethic proc features");
272 return -errno;
273 }
274 return 0;
275 }
276
277 static int initialize_vm(int vm_fd)
278 {
279 int ret = ioctl(vm_fd, MSHV_INITIALIZE_PARTITION);
280 if (ret < 0) {
281 error_report("Failed to initialize partition: %s", strerror(errno));
282 return -1;
283 }
284 return 0;
285 }
286
287 static int create_vm(int mshv_fd, int *vm_fd)
288 {
289 int ret = create_partition(mshv_fd, vm_fd);
290 if (ret < 0) {
291 return -1;
292 }
293
294 ret = set_synthetic_proc_features(*vm_fd);
295 if (ret < 0) {
296 return -1;
297 }
298
299 ret = initialize_vm(*vm_fd);
300 if (ret < 0) {
301 return -1;
302 }
303
304 ret = mshv_arch_post_init_vm(*vm_fd);
305 if (ret < 0) {
306 return -1;
307 }
308
309 return 0;
310 }
311
312 static void mem_region_add(MemoryListener *listener,
313 MemoryRegionSection *section)
314 {
315 MshvMemoryListener *mml;
316 mml = container_of(listener, MshvMemoryListener, listener);
317 memory_region_ref(section->mr);
318 mshv_set_phys_mem(mml, section, true);
319 }
320
321 static void mem_region_del(MemoryListener *listener,
322 MemoryRegionSection *section)
323 {
324 MshvMemoryListener *mml;
325 mml = container_of(listener, MshvMemoryListener, listener);
326 mshv_set_phys_mem(mml, section, false);
327 memory_region_unref(section->mr);
328 }
329
330 typedef enum {
331 DATAMATCH_NONE,
332 DATAMATCH_U32,
333 DATAMATCH_U64,
334 } DatamatchTag;
335
336 typedef struct {
337 DatamatchTag tag;
338 union {
339 uint32_t u32;
340 uint64_t u64;
341 } value;
342 } Datamatch;
343
344 /* flags: determine whether to de/assign */
345 static int ioeventfd(int vm_fd, int event_fd, uint64_t addr, Datamatch dm,
346 uint32_t flags)
347 {
348 struct mshv_user_ioeventfd args = {0};
349 args.fd = event_fd;
350 args.addr = addr;
351 args.flags = flags;
352
353 if (dm.tag == DATAMATCH_NONE) {
354 args.datamatch = 0;
355 } else {
356 flags |= BIT(MSHV_IOEVENTFD_BIT_DATAMATCH);
357 args.flags = flags;
358 if (dm.tag == DATAMATCH_U64) {
359 args.len = sizeof(uint64_t);
360 args.datamatch = dm.value.u64;
361 } else {
362 args.len = sizeof(uint32_t);
363 args.datamatch = dm.value.u32;
364 }
365 }
366
367 int ret = ioctl(vm_fd, MSHV_IOEVENTFD, &args);
368 if (ret < 0) {
369 return -errno;
370 }
371
372 return ret;
373 }
374
375 static int unregister_ioevent(int vm_fd, int event_fd, uint64_t mmio_addr,
376 uint64_t data, uint32_t len, bool data_match)
377 {
378 uint32_t flags = 0;
379 Datamatch dm = {0};
380
381 flags |= BIT(MSHV_IOEVENTFD_BIT_DEASSIGN);
382 if (!data_match) {
383 dm.tag = DATAMATCH_NONE;
384 } else if (len == sizeof(uint64_t)) {
385 dm.tag = DATAMATCH_U64;
386 dm.value.u64 = data;
387 } else {
388 dm.tag = DATAMATCH_U32;
389 dm.value.u32 = data;
390 }
391
392 return ioeventfd(vm_fd, event_fd, mmio_addr, dm, flags);
393 }
394
395 static int register_ioevent(int vm_fd, int event_fd, uint64_t mmio_addr,
396 uint64_t val, bool is_64bit, bool is_datamatch)
397 {
398 uint32_t flags = 0;
399 Datamatch dm = {0};
400
401 if (!is_datamatch) {
402 dm.tag = DATAMATCH_NONE;
403 } else if (is_64bit) {
404 dm.tag = DATAMATCH_U64;
405 dm.value.u64 = val;
406 } else {
407 dm.tag = DATAMATCH_U32;
408 dm.value.u32 = val;
409 }
410
411 return ioeventfd(vm_fd, event_fd, mmio_addr, dm, flags);
412 }
413
414 static void mem_ioeventfd_add(MemoryListener *listener,
415 MemoryRegionSection *section,
416 bool match_data, uint64_t data,
417 EventNotifier *e)
418 {
419 int fd = event_notifier_get_fd(e);
420 int ret;
421 bool is_64 = int128_get64(section->size) == 8;
422 uint64_t addr = section->offset_within_address_space;
423
424 trace_mshv_mem_ioeventfd_add(addr, int128_get64(section->size), data);
425
426 ret = register_ioevent(mshv_state->vm, fd, addr, data, is_64, match_data);
427
428 if (ret < 0) {
429 error_report("Failed to register ioeventfd: %s (%d)", strerror(-ret),
430 -ret);
431 abort();
432 }
433 }
434
435 static void mem_ioeventfd_del(MemoryListener *listener,
436 MemoryRegionSection *section,
437 bool match_data, uint64_t data,
438 EventNotifier *e)
439 {
440 int fd = event_notifier_get_fd(e);
441 int ret;
442 uint64_t addr = section->offset_within_address_space;
443 uint64_t len = int128_get64(section->size);
444
445 trace_mshv_mem_ioeventfd_del(section->offset_within_address_space,
446 int128_get64(section->size), data);
447
448 ret = unregister_ioevent(mshv_state->vm, fd, addr, data, len, match_data);
449 if (ret < 0) {
450 error_report("Failed to unregister ioeventfd: %s (%d)", strerror(-ret),
451 -ret);
452 abort();
453 }
454 }
455
456 static MemoryListener mshv_memory_listener = {
457 .name = "mshv",
458 .priority = MEMORY_LISTENER_PRIORITY_ACCEL,
459 .region_add = mem_region_add,
460 .region_del = mem_region_del,
461 .eventfd_add = mem_ioeventfd_add,
462 .eventfd_del = mem_ioeventfd_del,
463 .log_sync = mshv_log_sync,
464 .log_global_start = mshv_log_global_start,
465 .log_global_stop = mshv_log_global_stop,
466 };
467
468 static MemoryListener mshv_io_listener = {
469 .name = "mshv", .priority = MEMORY_LISTENER_PRIORITY_DEV_BACKEND,
470 /* MSHV does not support PIO eventfd */
471 };
472
473 static void register_mshv_memory_listener(MshvState *s, MshvMemoryListener *mml,
474 AddressSpace *as, int as_id,
475 const char *name)
476 {
477 int i;
478
479 mml->listener = mshv_memory_listener;
480 mml->listener.name = name;
481 memory_listener_register(&mml->listener, as);
482 for (i = 0; i < s->nr_as; ++i) {
483 if (!s->as[i].as) {
484 s->as[i].as = as;
485 s->as[i].ml = mml;
486 break;
487 }
488 }
489 }
490
491 int mshv_hvcall(int fd, const mshv_root_hvcall *args)
492 {
493 int ret = 0;
494
495 ret = ioctl(fd, MSHV_ROOT_HVCALL, args);
496 if (ret < 0) {
497 error_report("Failed to perform hvcall: %s", strerror(errno));
498 return -1;
499 }
500 return ret;
501 }
502
503 static int mshv_init_vcpu(CPUState *cpu)
504 {
505 int vm_fd = mshv_state->vm;
506 uint8_t vp_index = cpu->cpu_index;
507 int ret;
508
509 cpu->accel = g_new0(AccelCPUState, 1);
510
511 ret = mshv_create_vcpu(vm_fd, vp_index, &cpu->accel->cpufd);
512 if (ret < 0) {
513 return -1;
514 }
515
516 mshv_arch_init_vcpu(cpu);
517 cpu->vcpu_dirty = true;
518
519 return 0;
520 }
521
522 static SaveVMHandlers savevm_mshv = {
523 .load_cleanup = mshv_load_cleanup,
524 };
525
526 static int mshv_init(AccelState *as, MachineState *ms)
527 {
528 MshvState *s;
529 int mshv_fd, vm_fd, ret;
530
531 if (mshv_state) {
532 warn_report("MSHV accelerator already initialized");
533 return 0;
534 }
535
536 s = MSHV_STATE(as);
537
538 accel_blocker_init();
539
540 s->vm = 0;
541
542 ret = init_mshv(&mshv_fd);
543 if (ret < 0) {
544 return -1;
545 }
546
547 mshv_init_mmio_emu();
548
549 ret = create_vm(mshv_fd, &vm_fd);
550 if (ret < 0) {
551 close(mshv_fd);
552 return -1;
553 }
554
555 s->vm = vm_fd;
556 s->fd = mshv_fd;
557
558 ret = get_proc_features(vm_fd, &s->processor_features);
559 if (ret < 0) {
560 return -1;
561 }
562
563 s->nr_as = 1;
564 s->as = g_new0(MshvAddressSpace, s->nr_as);
565
566 mshv_state = s;
567
568 mshv_init_irq_routing(s);
569
570 register_mshv_memory_listener(s, &s->memory_listener, &address_space_memory,
571 0, "mshv-memory");
572 memory_listener_register(&mshv_io_listener, &address_space_io);
573
574 register_savevm_live("mshv", 0, 1, &savevm_mshv, s);
575
576 mshv_clock_init();
577
578 return 0;
579 }
580
581 static int mshv_destroy_vcpu(CPUState *cpu)
582 {
583 int cpu_fd = mshv_vcpufd(cpu);
584 int vm_fd = mshv_state->vm;
585
586 mshv_remove_vcpu(vm_fd, cpu_fd);
587 mshv_vcpufd(cpu) = 0;
588
589 mshv_arch_destroy_vcpu(cpu);
590 g_clear_pointer(&cpu->accel, g_free);
591 return 0;
592 }
593
594 static int mshv_cpu_exec(CPUState *cpu)
595 {
596 hv_message mshv_msg;
597 enum MshvVmExit exit_reason;
598 int ret = 0;
599
600 bql_unlock();
601 cpu_exec_start(cpu);
602
603 do {
604 if (cpu->vcpu_dirty) {
605 ret = mshv_arch_store_vcpu_state(cpu);
606 if (ret) {
607 error_report("Failed to put registers after init: %s",
608 strerror(-ret));
609 ret = -1;
610 break;
611 }
612 cpu->vcpu_dirty = false;
613 }
614
615 /* Corresponding store-release is in cpu_exit. */
616 if (qatomic_load_acquire(&cpu->exit_request)) {
617 trace_mshv_interrupt_exit_request(cpu->cpu_index);
618 ret = EXCP_INTERRUPT;
619 break;
620 }
621
622 ret = mshv_run_vcpu(mshv_state->vm, cpu, &mshv_msg, &exit_reason);
623 if (ret < 0) {
624 error_report("Failed to run on vcpu %d", cpu->cpu_index);
625 abort();
626 }
627
628 switch (exit_reason) {
629 case MshvVmExitIgnore:
630 break;
631 default:
632 ret = EXCP_INTERRUPT;
633 break;
634 }
635 } while (ret == 0);
636
637 cpu_exec_end(cpu);
638 bql_lock();
639
640 if (ret < 0) {
641 cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
642 vm_stop(RUN_STATE_INTERNAL_ERROR);
643 }
644
645 return ret;
646 }
647
648 /*
649 * We need a dummy handler to make SIG_IPI a deliverable signal. The kernel
650 * handler will be woken up by the caught signal and instruct the hypervisor
651 * to suspend execution (the concrete mechanism differs between schedulers)
652 * and return to userspace.
653 */
654 static void dummy_handler(int sig)
655 {
656 }
657
658 static void init_signal(CPUState *cpu)
659 {
660 /* init cpu signals */
661 struct sigaction sigact;
662 sigset_t set;
663
664 memset(&sigact, 0, sizeof(sigact));
665 sigact.sa_handler = dummy_handler;
666 sigaction(SIG_IPI, &sigact, NULL);
667
668 pthread_sigmask(SIG_BLOCK, NULL, &set);
669 sigdelset(&set, SIG_IPI);
670 pthread_sigmask(SIG_SETMASK, &set, NULL);
671 }
672
673 static void *mshv_vcpu_thread(void *arg)
674 {
675 CPUState *cpu = arg;
676 int ret;
677
678 rcu_register_thread();
679
680 bql_lock();
681 qemu_thread_get_self(cpu->thread);
682 cpu->thread_id = qemu_get_thread_id();
683 current_cpu = cpu;
684 ret = mshv_init_vcpu(cpu);
685 if (ret < 0) {
686 error_report("Failed to init vcpu %d", cpu->cpu_index);
687 goto cleanup;
688 }
689 init_signal(cpu);
690
691 /* signal CPU creation */
692 cpu_thread_signal_created(cpu);
693 qemu_guest_random_seed_thread_part2(cpu->random_seed);
694
695 do {
696 qemu_process_cpu_events(cpu);
697 if (cpu_can_run(cpu)) {
698 mshv_cpu_exec(cpu);
699 }
700 } while (!cpu->unplug || cpu_can_run(cpu));
701
702 mshv_destroy_vcpu(cpu);
703 cleanup:
704 cpu_thread_signal_destroyed(cpu);
705 bql_unlock();
706 rcu_unregister_thread();
707 return NULL;
708 }
709
710 static void mshv_start_vcpu_thread(CPUState *cpu)
711 {
712 char thread_name[VCPU_THREAD_NAME_SIZE];
713
714 snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/MSHV",
715 cpu->cpu_index);
716
717 cpu->thread = g_malloc0(sizeof(QemuThread));
718 cpu->halt_cond = g_malloc0(sizeof(QemuCond));
719
720 qemu_cond_init(cpu->halt_cond);
721
722 trace_mshv_start_vcpu_thread(thread_name, cpu->cpu_index);
723 qemu_thread_create(cpu->thread, thread_name, mshv_vcpu_thread, cpu,
724 QEMU_THREAD_JOINABLE);
725 }
726
727 static void do_mshv_cpu_synchronize_post_init(CPUState *cpu,
728 run_on_cpu_data arg)
729 {
730 int ret = mshv_arch_store_vcpu_state(cpu);
731 if (ret < 0) {
732 error_report("Failed to put registers after init: %s", strerror(-ret));
733 abort();
734 }
735
736 cpu->vcpu_dirty = false;
737 }
738
739 static void mshv_cpu_synchronize_post_init(CPUState *cpu)
740 {
741 run_on_cpu(cpu, do_mshv_cpu_synchronize_post_init, RUN_ON_CPU_NULL);
742 }
743
744 static void mshv_cpu_synchronize_post_reset(CPUState *cpu)
745 {
746 int ret = mshv_arch_store_vcpu_state(cpu);
747 if (ret) {
748 error_report("Failed to put registers after reset: %s",
749 strerror(-ret));
750 cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
751 vm_stop(RUN_STATE_INTERNAL_ERROR);
752 }
753 cpu->vcpu_dirty = false;
754 }
755
756 static void do_mshv_cpu_synchronize_pre_loadvm(CPUState *cpu,
757 run_on_cpu_data arg)
758 {
759 cpu->vcpu_dirty = true;
760 }
761
762 static void mshv_cpu_synchronize_pre_loadvm(CPUState *cpu)
763 {
764 run_on_cpu(cpu, do_mshv_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL);
765 }
766
767 static void do_mshv_cpu_synchronize(CPUState *cpu, run_on_cpu_data arg)
768 {
769 if (!cpu->vcpu_dirty) {
770 int ret = mshv_arch_load_vcpu_state(cpu);
771 if (ret < 0) {
772 error_report("Failed to load registers for vcpu %d",
773 cpu->cpu_index);
774
775 cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
776 vm_stop(RUN_STATE_INTERNAL_ERROR);
777 }
778
779 cpu->vcpu_dirty = true;
780 }
781 }
782
783 static void mshv_cpu_synchronize(CPUState *cpu)
784 {
785 if (!cpu->vcpu_dirty) {
786 run_on_cpu(cpu, do_mshv_cpu_synchronize, RUN_ON_CPU_NULL);
787 }
788 }
789
790 static bool mshv_cpus_are_resettable(void)
791 {
792 return false;
793 }
794
795 static void mshv_accel_class_init(ObjectClass *oc, const void *data)
796 {
797 AccelClass *ac = ACCEL_CLASS(oc);
798
799 ac->name = "MSHV";
800 ac->init_machine = mshv_init;
801 ac->allowed = &mshv_allowed;
802 }
803
804 static void mshv_accel_instance_init(Object *obj)
805 {
806 MshvState *s = MSHV_STATE(obj);
807
808 s->vm = 0;
809 }
810
811 static const TypeInfo mshv_accel_type = {
812 .name = TYPE_MSHV_ACCEL,
813 .parent = TYPE_ACCEL,
814 .instance_init = mshv_accel_instance_init,
815 .class_init = mshv_accel_class_init,
816 .instance_size = sizeof(MshvState),
817 };
818
819 /*
820 * MSHV manages secondary processors in the hypervisor. SIPI for x86 and
821 * PSCI for Arm are handled internally. Halted vCPUs must still enter
822 * mshv_cpu_exec() so that MSHV_RUN_VP is called and the hypervisor will
823 * wake APs.
824 */
825 static bool mshv_vcpu_thread_is_idle(CPUState *cpu)
826 {
827 return false;
828 }
829
830 static void mshv_accel_ops_class_init(ObjectClass *oc, const void *data)
831 {
832 AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
833
834 ops->create_vcpu_thread = mshv_start_vcpu_thread;
835 ops->cpu_thread_is_idle = mshv_vcpu_thread_is_idle;
836 ops->synchronize_post_init = mshv_cpu_synchronize_post_init;
837 ops->synchronize_post_reset = mshv_cpu_synchronize_post_reset;
838 ops->synchronize_state = mshv_cpu_synchronize;
839 ops->synchronize_pre_loadvm = mshv_cpu_synchronize_pre_loadvm;
840 ops->cpus_are_resettable = mshv_cpus_are_resettable;
841 ops->cpu_thread_is_idle = mshv_vcpu_thread_is_idle;
842 ops->handle_interrupt = generic_handle_interrupt;
843 }
844
845 static const TypeInfo mshv_accel_ops_type = {
846 .name = ACCEL_OPS_NAME("mshv"),
847 .parent = TYPE_ACCEL_OPS,
848 .class_init = mshv_accel_ops_class_init,
849 .abstract = true,
850 };
851
852 static void mshv_type_init(void)
853 {
854 type_register_static(&mshv_accel_type);
855 type_register_static(&mshv_accel_ops_type);
856 }
857
858 type_init(mshv_type_init);