master
c 596 lines 15.8 KB
Raw
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3 * QEMU Windows Hypervisor Platform accelerator (WHPX)
4 *
5 * Copyright Microsoft Corp. 2017
6 */
7
8 #include "qemu/osdep.h"
9 #include "cpu.h"
10 #include "system/address-spaces.h"
11 #include "system/ioport.h"
12 #include "gdbstub/helpers.h"
13 #include "qemu/accel.h"
14 #include "accel/accel-ops.h"
15 #include "system/memory.h"
16 #include "system/whpx.h"
17 #include "system/cpus.h"
18 #include "system/runstate.h"
19 #include "qemu/main-loop.h"
20 #include "hw/core/boards.h"
21 #include "hw/intc/ioapic.h"
22 #include "qemu/error-report.h"
23 #include "qapi/error.h"
24 #include "qapi/qapi-types-common.h"
25 #include "qapi/qapi-visit-common.h"
26 #include "migration/blocker.h"
27 #include "accel/accel-cpu-target.h"
28 #include <winerror.h>
29
30 #include "system/whpx-internal.h"
31 #include "system/whpx-accel-ops.h"
32 #include "system/whpx-common.h"
33 #include "system/whpx-all.h"
34
35 #include <winhvplatform.h>
36 #include <winhvplatformdefs.h>
37
38 bool whpx_allowed;
39 bool whpx_irqchip_in_kernel;
40 static bool whp_dispatch_initialized;
41 static HMODULE hWinHvPlatform;
42
43 struct whpx_state whpx_global;
44 struct WHPDispatch whp_dispatch;
45
46 void whpx_flush_cpu_state(CPUState *cpu)
47 {
48 if (cpu->vcpu_dirty) {
49 whpx_set_registers(cpu, WHPX_LEVEL_RUNTIME_STATE);
50 cpu->vcpu_dirty = false;
51 }
52 }
53
54 void whpx_get_reg(CPUState *cpu, WHV_REGISTER_NAME reg, WHV_REGISTER_VALUE* val)
55 {
56 struct whpx_state *whpx = &whpx_global;
57 HRESULT hr;
58
59 whpx_flush_cpu_state(cpu);
60
61 hr = whp_dispatch.WHvGetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index,
62 &reg, 1, val);
63
64 if (FAILED(hr)) {
65 error_report("WHPX: Failed to get register %08x, hr=%08lx", reg, hr);
66 }
67 }
68
69 void whpx_set_reg(CPUState *cpu, WHV_REGISTER_NAME reg, WHV_REGISTER_VALUE val)
70 {
71 struct whpx_state *whpx = &whpx_global;
72 HRESULT hr;
73 hr = whp_dispatch.WHvSetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index,
74 &reg, 1, &val);
75
76 if (FAILED(hr)) {
77 error_report("WHPX: Failed to set register %08x, hr=%08lx", reg, hr);
78 }
79 }
80
81 /* Tries to find a breakpoint at the specified address. */
82 struct whpx_breakpoint *whpx_lookup_breakpoint_by_addr(uint64_t address)
83 {
84 struct whpx_state *whpx = &whpx_global;
85 int i;
86
87 if (whpx->breakpoints.breakpoints) {
88 for (i = 0; i < whpx->breakpoints.breakpoints->used; i++) {
89 if (address == whpx->breakpoints.breakpoints->data[i].address) {
90 return &whpx->breakpoints.breakpoints->data[i];
91 }
92 }
93 }
94
95 return NULL;
96 }
97
98 /*
99 * This function is called when the a VCPU is about to start and no other
100 * VCPUs have been started so far. Since the VCPU start order could be
101 * arbitrary, it doesn't have to be VCPU#0.
102 *
103 * It is used to commit the breakpoints into memory, and configure WHPX
104 * to intercept debug exceptions.
105 *
106 * Note that whpx_set_exception_exit_bitmap() cannot be called if one or
107 * more VCPUs are already running, so this is the best place to do it.
108 */
109 int whpx_first_vcpu_starting(CPUState *cpu)
110 {
111 struct whpx_state *whpx = &whpx_global;
112
113 g_assert(bql_locked());
114
115 if (!QTAILQ_EMPTY(&cpu->breakpoints) ||
116 (whpx->breakpoints.breakpoints &&
117 whpx->breakpoints.breakpoints->used)) {
118 CPUBreakpoint *bp;
119 int i = 0;
120 bool update_pending = false;
121
122 QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
123 if (i >= whpx->breakpoints.original_address_count ||
124 bp->pc != whpx->breakpoints.original_addresses[i]) {
125 update_pending = true;
126 }
127
128 i++;
129 }
130
131 if (i != whpx->breakpoints.original_address_count) {
132 update_pending = true;
133 }
134
135 if (update_pending) {
136 /*
137 * The CPU breakpoints have changed since the last call to
138 * whpx_translate_cpu_breakpoints(). WHPX breakpoints must
139 * now be recomputed.
140 */
141 whpx_translate_cpu_breakpoints(&whpx->breakpoints, cpu, i);
142 }
143 /* Actually insert the breakpoints into the memory. */
144 whpx_apply_breakpoints(whpx->breakpoints.breakpoints, cpu, true);
145 }
146 HRESULT hr;
147 uint64_t exception_mask;
148 if (whpx->step_pending ||
149 (whpx->breakpoints.breakpoints &&
150 whpx->breakpoints.breakpoints->used)) {
151 /*
152 * We are either attempting to single-step one or more CPUs, or
153 * have one or more breakpoints enabled. Both require intercepting
154 * the WHvX64ExceptionTypeBreakpointTrap exception.
155 */
156 exception_mask = 1UL << WHPX_INTERCEPT_DEBUG_TRAPS;
157 } else {
158 /* Let the guest handle all exceptions. */
159 exception_mask = 0;
160 }
161 hr = whpx_set_exception_exit_bitmap(exception_mask);
162 if (!SUCCEEDED(hr)) {
163 error_report("WHPX: Failed to update exception exit mask,"
164 "hr=%08lx.", hr);
165 return 1;
166 }
167 return 0;
168 }
169
170 /*
171 * This function is called when the last VCPU has finished running.
172 * It is used to remove any previously set breakpoints from memory.
173 */
174 int whpx_last_vcpu_stopping(CPUState *cpu)
175 {
176 whpx_apply_breakpoints(whpx_global.breakpoints.breakpoints, cpu, false);
177 return 0;
178 }
179
180 static void do_whpx_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg)
181 {
182 if (!cpu->vcpu_dirty) {
183 whpx_get_registers(cpu, WHPX_LEVEL_FULL_STATE);
184 cpu->vcpu_dirty = true;
185 }
186 }
187
188 static void do_whpx_cpu_synchronize_post_reset(CPUState *cpu,
189 run_on_cpu_data arg)
190 {
191 whpx_set_registers(cpu, WHPX_LEVEL_RESET_STATE);
192 cpu->vcpu_dirty = false;
193 }
194
195 static void do_whpx_cpu_synchronize_post_init(CPUState *cpu,
196 run_on_cpu_data arg)
197 {
198 whpx_set_registers(cpu, WHPX_LEVEL_FULL_STATE);
199 cpu->vcpu_dirty = false;
200 }
201
202 static void do_whpx_cpu_synchronize_pre_loadvm(CPUState *cpu,
203 run_on_cpu_data arg)
204 {
205 cpu->vcpu_dirty = true;
206 }
207
208 /*
209 * CPU support.
210 */
211
212 void whpx_cpu_synchronize_state(CPUState *cpu)
213 {
214 if (!cpu->vcpu_dirty) {
215 run_on_cpu(cpu, do_whpx_cpu_synchronize_state, RUN_ON_CPU_NULL);
216 }
217 }
218
219 void whpx_cpu_synchronize_post_reset(CPUState *cpu)
220 {
221 run_on_cpu(cpu, do_whpx_cpu_synchronize_post_reset, RUN_ON_CPU_NULL);
222 }
223
224 void whpx_cpu_synchronize_post_init(CPUState *cpu)
225 {
226 run_on_cpu(cpu, do_whpx_cpu_synchronize_post_init, RUN_ON_CPU_NULL);
227 }
228
229 void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu)
230 {
231 run_on_cpu(cpu, do_whpx_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL);
232 }
233
234 static void whpx_pre_resume_vm(AccelState *as, bool step_pending)
235 {
236 whpx_global.step_pending = step_pending;
237 }
238
239 /*
240 * Vcpu support.
241 */
242
243 int whpx_vcpu_exec(CPUState *cpu)
244 {
245 int ret;
246 int fatal;
247
248 for (;;) {
249 if (cpu->exception_index >= EXCP_INTERRUPT) {
250 ret = cpu->exception_index;
251 cpu->exception_index = -1;
252 break;
253 }
254
255 fatal = whpx_vcpu_run(cpu);
256
257 if (fatal) {
258 error_report("WHPX: Failed to exec a virtual processor");
259 abort();
260 }
261 }
262
263 return ret;
264 }
265
266 void whpx_destroy_vcpu(CPUState *cpu)
267 {
268 struct whpx_state *whpx = &whpx_global;
269
270 whp_dispatch.WHvDeleteVirtualProcessor(whpx->partition, cpu->cpu_index);
271 whpx_arch_destroy_vcpu(cpu);
272 g_free(cpu->accel);
273 }
274
275
276 void whpx_vcpu_kick(CPUState *cpu)
277 {
278 struct whpx_state *whpx = &whpx_global;
279 whp_dispatch.WHvCancelRunVirtualProcessor(
280 whpx->partition, cpu->cpu_index, 0);
281 }
282
283 /*
284 * Memory support.
285 */
286
287 static void whpx_set_phys_mem(MemoryRegionSection *section, bool add)
288 {
289 struct whpx_state *whpx = &whpx_global;
290 MemoryRegion *area = section->mr;
291 bool writable = !area->readonly && !area->rom_device;
292 WHV_MAP_GPA_RANGE_FLAGS flags;
293 uint64_t page_size = qemu_real_host_page_size();
294 uint64_t gva = section->offset_within_address_space;
295 uint64_t size = int128_get64(section->size);
296 HRESULT hr;
297 void *mem;
298
299 if (!memory_region_is_ram(area)) {
300 if (writable) {
301 return;
302 } else if (!memory_region_is_romd(area)) {
303 add = false;
304 }
305 }
306
307 if (!QEMU_IS_ALIGNED(size, page_size) ||
308 !QEMU_IS_ALIGNED(gva, page_size)) {
309 /* Not page aligned, so we can not map as RAM */
310 add = false;
311 }
312
313 if (!add) {
314 hr = whp_dispatch.WHvUnmapGpaRange(whpx->partition,
315 gva, size);
316 if (FAILED(hr)) {
317 error_report("WHPX: failed to unmap GPA range");
318 abort();
319 }
320 return;
321 }
322
323 flags = WHvMapGpaRangeFlagRead | WHvMapGpaRangeFlagExecute
324 | (writable ? WHvMapGpaRangeFlagWrite : 0);
325 mem = memory_region_get_ram_ptr(area) + section->offset_within_region;
326
327 hr = whp_dispatch.WHvMapGpaRange(whpx->partition,
328 mem, gva, size, flags);
329 if (FAILED(hr)) {
330 error_report("WHPX: failed to map GPA range");
331 abort();
332 }
333 }
334
335 static void whpx_region_add(MemoryListener *listener,
336 MemoryRegionSection *section)
337 {
338 whpx_set_phys_mem(section, true);
339 }
340
341 static void whpx_region_del(MemoryListener *listener,
342 MemoryRegionSection *section)
343 {
344 whpx_set_phys_mem(section, false);
345 }
346
347 static void whpx_transaction_begin(MemoryListener *listener)
348 {
349 }
350
351 static void whpx_transaction_commit(MemoryListener *listener)
352 {
353 }
354
355 static void whpx_log_sync(MemoryListener *listener,
356 MemoryRegionSection *section)
357 {
358 MemoryRegion *mr = section->mr;
359
360 if (!memory_region_is_ram(mr)) {
361 return;
362 }
363
364 memory_region_set_dirty(mr, 0, int128_get64(section->size));
365 }
366
367 static MemoryListener whpx_memory_listener = {
368 .name = "whpx",
369 .begin = whpx_transaction_begin,
370 .commit = whpx_transaction_commit,
371 .region_add = whpx_region_add,
372 .region_del = whpx_region_del,
373 .log_sync = whpx_log_sync,
374 .priority = MEMORY_LISTENER_PRIORITY_ACCEL,
375 };
376
377 void whpx_memory_init(void)
378 {
379 memory_listener_register(&whpx_memory_listener, &address_space_memory);
380 }
381
382 /*
383 * Load the functions from the given library, using the given handle. If a
384 * handle is provided, it is used, otherwise the library is opened. The
385 * handle will be updated on return with the opened one.
386 */
387 static bool load_whp_dispatch_fns(HMODULE *handle,
388 WHPFunctionList function_list)
389 {
390 HMODULE hLib = *handle;
391
392 #define WINHV_PLATFORM_DLL "WinHvPlatform.dll"
393 #define WHP_LOAD_FIELD_OPTIONAL(return_type, function_name, signature) \
394 whp_dispatch.function_name = \
395 (function_name ## _t)GetProcAddress(hLib, #function_name); \
396
397 #define WHP_LOAD_FIELD(return_type, function_name, signature) \
398 whp_dispatch.function_name = \
399 (function_name ## _t)GetProcAddress(hLib, #function_name); \
400 if (!whp_dispatch.function_name) { \
401 error_report("Could not load function %s", #function_name); \
402 goto error; \
403 } \
404
405 #define WHP_LOAD_LIB(lib_name, handle_lib) \
406 if (!handle_lib) { \
407 handle_lib = LoadLibrary(lib_name); \
408 if (!handle_lib) { \
409 error_report("Could not load library %s.", lib_name); \
410 goto error; \
411 } \
412 } \
413
414 switch (function_list) {
415 case WINHV_PLATFORM_FNS_DEFAULT:
416 WHP_LOAD_LIB(WINHV_PLATFORM_DLL, hLib)
417 LIST_WINHVPLATFORM_FUNCTIONS(WHP_LOAD_FIELD)
418 break;
419 case WINHV_PLATFORM_FNS_SUPPLEMENTAL:
420 WHP_LOAD_LIB(WINHV_PLATFORM_DLL, hLib)
421 LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL(WHP_LOAD_FIELD_OPTIONAL)
422 break;
423 }
424
425 *handle = hLib;
426 return true;
427
428 error:
429 if (hLib) {
430 FreeLibrary(hLib);
431 }
432
433 return false;
434 }
435
436 static void whpx_set_kernel_irqchip(Object *obj, Visitor *v,
437 const char *name, void *opaque,
438 Error **errp)
439 {
440 struct whpx_state *whpx = &whpx_global;
441 OnOffSplit mode;
442
443 if (!visit_type_OnOffSplit(v, name, &mode, errp)) {
444 return;
445 }
446
447 switch (mode) {
448 case ON_OFF_SPLIT_ON:
449 whpx->kernel_irqchip_allowed = true;
450 whpx->kernel_irqchip_required = true;
451 break;
452
453 case ON_OFF_SPLIT_OFF:
454 whpx->kernel_irqchip_allowed = false;
455 whpx->kernel_irqchip_required = false;
456 break;
457
458 case ON_OFF_SPLIT_SPLIT:
459 error_setg(errp, "WHPX: split irqchip currently not supported");
460 error_append_hint(errp,
461 "Try without kernel-irqchip or with kernel-irqchip=on|off");
462 break;
463
464 default:
465 /*
466 * The value was checked in visit_type_OnOffSplit() above. If
467 * we get here, then something is wrong in QEMU.
468 */
469 abort();
470 }
471 }
472
473 static void whpx_set_hyperv(Object *obj, Visitor *v,
474 const char *name, void *opaque,
475 Error **errp)
476 {
477 struct whpx_state *whpx = &whpx_global;
478 OnOffAuto mode;
479
480 if (!visit_type_OnOffAuto(v, name, &mode, errp)) {
481 return;
482 }
483
484 switch (mode) {
485 case ON_OFF_AUTO_ON:
486 whpx->hyperv_enlightenments_allowed = true;
487 whpx->hyperv_enlightenments_required = true;
488 break;
489
490 case ON_OFF_AUTO_OFF:
491 whpx->hyperv_enlightenments_allowed = false;
492 whpx->hyperv_enlightenments_required = false;
493 break;
494
495 case ON_OFF_AUTO_AUTO:
496 whpx->hyperv_enlightenments_allowed = true;
497 whpx->hyperv_enlightenments_required = false;
498 break;
499 default:
500 /*
501 * The value was checked in visit_type_OnOffAuto() above. If
502 * we get here, then something is wrong in QEMU.
503 */
504 abort();
505 }
506 }
507
508 static void whpx_cpu_accel_class_init(ObjectClass *oc, const void *data)
509 {
510 AccelCPUClass *acc = ACCEL_CPU_CLASS(oc);
511
512 acc->cpu_instance_init = whpx_cpu_instance_init;
513 }
514
515 static const TypeInfo whpx_cpu_accel_type = {
516 .name = ACCEL_CPU_NAME("whpx"),
517
518 .parent = TYPE_ACCEL_CPU,
519 .class_init = whpx_cpu_accel_class_init,
520 .abstract = true,
521 };
522
523 static void whpx_accel_class_init(ObjectClass *oc, const void *data)
524 {
525 AccelClass *ac = ACCEL_CLASS(oc);
526 ac->name = "WHPX";
527 ac->init_machine = whpx_accel_init;
528 ac->pre_resume_vm = whpx_pre_resume_vm;
529 ac->allowed = &whpx_allowed;
530
531 object_class_property_add(oc, "kernel-irqchip", "on|off|split",
532 NULL, whpx_set_kernel_irqchip,
533 NULL, NULL);
534 object_class_property_set_description(oc, "kernel-irqchip",
535 "Configure WHPX in-kernel irqchip");
536 object_class_property_add(oc, "hyperv", "OnOffAuto",
537 NULL, whpx_set_hyperv,
538 NULL, NULL);
539 object_class_property_set_description(oc, "hyperv",
540 "Configure Hyper-V enlightenments");
541
542 whpx_arch_accel_class_init(oc);
543 }
544
545 static void whpx_accel_instance_init(Object *obj)
546 {
547 struct whpx_state *whpx = &whpx_global;
548
549 memset(whpx, 0, sizeof(struct whpx_state));
550 /* Turn on kernel-irqchip, by default */
551 whpx->kernel_irqchip_allowed = true;
552
553 whpx->hyperv_enlightenments_allowed = true;
554 whpx->hyperv_enlightenments_required = false;
555 /* Value determined at whpx_accel_init */
556 whpx->hyperv_enlightenments_enabled = false;
557 whpx->ignore_unknown_msr = true;
558 whpx->intercept_msr_gp = false;
559 whpx->separate_security_domain = true;
560 }
561
562 static const TypeInfo whpx_accel_type = {
563 .name = ACCEL_CLASS_NAME("whpx"),
564 .parent = TYPE_ACCEL,
565 .instance_init = whpx_accel_instance_init,
566 .class_init = whpx_accel_class_init,
567 };
568
569 static void whpx_type_init(void)
570 {
571 type_register_static(&whpx_accel_type);
572 type_register_static(&whpx_cpu_accel_type);
573 }
574
575 bool init_whp_dispatch(void)
576 {
577 if (whp_dispatch_initialized) {
578 return true;
579 }
580
581 if (!load_whp_dispatch_fns(&hWinHvPlatform, WINHV_PLATFORM_FNS_DEFAULT)) {
582 goto error;
583 }
584 assert(load_whp_dispatch_fns(&hWinHvPlatform,
585 WINHV_PLATFORM_FNS_SUPPLEMENTAL));
586 whp_dispatch_initialized = true;
587
588 return true;
589 error:
590 if (hWinHvPlatform) {
591 FreeLibrary(hWinHvPlatform);
592 }
593 return false;
594 }
595
596 type_init(whpx_type_init);