xref: /openbmc/qemu/target/i386/whpx/whpx-all.c (revision db5a06b3a21239ffa9424f89f98ee9afa14e955b)
11fc33bb9SClaudio Fontana /*
21fc33bb9SClaudio Fontana  * QEMU Windows Hypervisor Platform accelerator (WHPX)
31fc33bb9SClaudio Fontana  *
41fc33bb9SClaudio Fontana  * Copyright Microsoft Corp. 2017
51fc33bb9SClaudio Fontana  *
61fc33bb9SClaudio Fontana  * This work is licensed under the terms of the GNU GPL, version 2 or later.
71fc33bb9SClaudio Fontana  * See the COPYING file in the top-level directory.
81fc33bb9SClaudio Fontana  *
91fc33bb9SClaudio Fontana  */
101fc33bb9SClaudio Fontana 
111fc33bb9SClaudio Fontana #include "qemu/osdep.h"
121fc33bb9SClaudio Fontana #include "cpu.h"
131fc33bb9SClaudio Fontana #include "exec/address-spaces.h"
141fc33bb9SClaudio Fontana #include "exec/ioport.h"
154ea5fe99SAlex Bennée #include "gdbstub/helpers.h"
16940e43aaSClaudio Fontana #include "qemu/accel.h"
171fc33bb9SClaudio Fontana #include "sysemu/whpx.h"
181fc33bb9SClaudio Fontana #include "sysemu/cpus.h"
191fc33bb9SClaudio Fontana #include "sysemu/runstate.h"
201fc33bb9SClaudio Fontana #include "qemu/main-loop.h"
211fc33bb9SClaudio Fontana #include "hw/boards.h"
227f54640bSBernhard Beschow #include "hw/intc/ioapic.h"
231fc33bb9SClaudio Fontana #include "hw/i386/apic_internal.h"
241fc33bb9SClaudio Fontana #include "qemu/error-report.h"
251fc33bb9SClaudio Fontana #include "qapi/error.h"
261fc33bb9SClaudio Fontana #include "qapi/qapi-types-common.h"
271fc33bb9SClaudio Fontana #include "qapi/qapi-visit-common.h"
281fc33bb9SClaudio Fontana #include "migration/blocker.h"
291fc33bb9SClaudio Fontana #include <winerror.h>
301fc33bb9SClaudio Fontana 
319102c968SPaolo Bonzini #include "whpx-internal.h"
32b86f59c7SClaudio Fontana #include "whpx-accel-ops.h"
33b86f59c7SClaudio Fontana 
34641b8417SPhilippe Mathieu-Daudé #include <winhvplatform.h>
35641b8417SPhilippe Mathieu-Daudé #include <winhvemulation.h>
361fc33bb9SClaudio Fontana 
371fc33bb9SClaudio Fontana #define HYPERV_APIC_BUS_FREQUENCY      (200000000ULL)
381fc33bb9SClaudio Fontana 
391fc33bb9SClaudio Fontana static const WHV_REGISTER_NAME whpx_register_names[] = {
401fc33bb9SClaudio Fontana 
411fc33bb9SClaudio Fontana     /* X64 General purpose registers */
421fc33bb9SClaudio Fontana     WHvX64RegisterRax,
431fc33bb9SClaudio Fontana     WHvX64RegisterRcx,
441fc33bb9SClaudio Fontana     WHvX64RegisterRdx,
451fc33bb9SClaudio Fontana     WHvX64RegisterRbx,
461fc33bb9SClaudio Fontana     WHvX64RegisterRsp,
471fc33bb9SClaudio Fontana     WHvX64RegisterRbp,
481fc33bb9SClaudio Fontana     WHvX64RegisterRsi,
491fc33bb9SClaudio Fontana     WHvX64RegisterRdi,
501fc33bb9SClaudio Fontana     WHvX64RegisterR8,
511fc33bb9SClaudio Fontana     WHvX64RegisterR9,
521fc33bb9SClaudio Fontana     WHvX64RegisterR10,
531fc33bb9SClaudio Fontana     WHvX64RegisterR11,
541fc33bb9SClaudio Fontana     WHvX64RegisterR12,
551fc33bb9SClaudio Fontana     WHvX64RegisterR13,
561fc33bb9SClaudio Fontana     WHvX64RegisterR14,
571fc33bb9SClaudio Fontana     WHvX64RegisterR15,
581fc33bb9SClaudio Fontana     WHvX64RegisterRip,
591fc33bb9SClaudio Fontana     WHvX64RegisterRflags,
601fc33bb9SClaudio Fontana 
611fc33bb9SClaudio Fontana     /* X64 Segment registers */
621fc33bb9SClaudio Fontana     WHvX64RegisterEs,
631fc33bb9SClaudio Fontana     WHvX64RegisterCs,
641fc33bb9SClaudio Fontana     WHvX64RegisterSs,
651fc33bb9SClaudio Fontana     WHvX64RegisterDs,
661fc33bb9SClaudio Fontana     WHvX64RegisterFs,
671fc33bb9SClaudio Fontana     WHvX64RegisterGs,
681fc33bb9SClaudio Fontana     WHvX64RegisterLdtr,
691fc33bb9SClaudio Fontana     WHvX64RegisterTr,
701fc33bb9SClaudio Fontana 
711fc33bb9SClaudio Fontana     /* X64 Table registers */
721fc33bb9SClaudio Fontana     WHvX64RegisterIdtr,
731fc33bb9SClaudio Fontana     WHvX64RegisterGdtr,
741fc33bb9SClaudio Fontana 
751fc33bb9SClaudio Fontana     /* X64 Control Registers */
761fc33bb9SClaudio Fontana     WHvX64RegisterCr0,
771fc33bb9SClaudio Fontana     WHvX64RegisterCr2,
781fc33bb9SClaudio Fontana     WHvX64RegisterCr3,
791fc33bb9SClaudio Fontana     WHvX64RegisterCr4,
801fc33bb9SClaudio Fontana     WHvX64RegisterCr8,
811fc33bb9SClaudio Fontana 
821fc33bb9SClaudio Fontana     /* X64 Debug Registers */
831fc33bb9SClaudio Fontana     /*
841fc33bb9SClaudio Fontana      * WHvX64RegisterDr0,
851fc33bb9SClaudio Fontana      * WHvX64RegisterDr1,
861fc33bb9SClaudio Fontana      * WHvX64RegisterDr2,
871fc33bb9SClaudio Fontana      * WHvX64RegisterDr3,
881fc33bb9SClaudio Fontana      * WHvX64RegisterDr6,
891fc33bb9SClaudio Fontana      * WHvX64RegisterDr7,
901fc33bb9SClaudio Fontana      */
911fc33bb9SClaudio Fontana 
921fc33bb9SClaudio Fontana     /* X64 Floating Point and Vector Registers */
931fc33bb9SClaudio Fontana     WHvX64RegisterXmm0,
941fc33bb9SClaudio Fontana     WHvX64RegisterXmm1,
951fc33bb9SClaudio Fontana     WHvX64RegisterXmm2,
961fc33bb9SClaudio Fontana     WHvX64RegisterXmm3,
971fc33bb9SClaudio Fontana     WHvX64RegisterXmm4,
981fc33bb9SClaudio Fontana     WHvX64RegisterXmm5,
991fc33bb9SClaudio Fontana     WHvX64RegisterXmm6,
1001fc33bb9SClaudio Fontana     WHvX64RegisterXmm7,
1011fc33bb9SClaudio Fontana     WHvX64RegisterXmm8,
1021fc33bb9SClaudio Fontana     WHvX64RegisterXmm9,
1031fc33bb9SClaudio Fontana     WHvX64RegisterXmm10,
1041fc33bb9SClaudio Fontana     WHvX64RegisterXmm11,
1051fc33bb9SClaudio Fontana     WHvX64RegisterXmm12,
1061fc33bb9SClaudio Fontana     WHvX64RegisterXmm13,
1071fc33bb9SClaudio Fontana     WHvX64RegisterXmm14,
1081fc33bb9SClaudio Fontana     WHvX64RegisterXmm15,
1091fc33bb9SClaudio Fontana     WHvX64RegisterFpMmx0,
1101fc33bb9SClaudio Fontana     WHvX64RegisterFpMmx1,
1111fc33bb9SClaudio Fontana     WHvX64RegisterFpMmx2,
1121fc33bb9SClaudio Fontana     WHvX64RegisterFpMmx3,
1131fc33bb9SClaudio Fontana     WHvX64RegisterFpMmx4,
1141fc33bb9SClaudio Fontana     WHvX64RegisterFpMmx5,
1151fc33bb9SClaudio Fontana     WHvX64RegisterFpMmx6,
1161fc33bb9SClaudio Fontana     WHvX64RegisterFpMmx7,
1171fc33bb9SClaudio Fontana     WHvX64RegisterFpControlStatus,
1181fc33bb9SClaudio Fontana     WHvX64RegisterXmmControlStatus,
1191fc33bb9SClaudio Fontana 
1201fc33bb9SClaudio Fontana     /* X64 MSRs */
1211fc33bb9SClaudio Fontana     WHvX64RegisterEfer,
1221fc33bb9SClaudio Fontana #ifdef TARGET_X86_64
1231fc33bb9SClaudio Fontana     WHvX64RegisterKernelGsBase,
1241fc33bb9SClaudio Fontana #endif
1251fc33bb9SClaudio Fontana     WHvX64RegisterApicBase,
1261fc33bb9SClaudio Fontana     /* WHvX64RegisterPat, */
1271fc33bb9SClaudio Fontana     WHvX64RegisterSysenterCs,
1281fc33bb9SClaudio Fontana     WHvX64RegisterSysenterEip,
1291fc33bb9SClaudio Fontana     WHvX64RegisterSysenterEsp,
1301fc33bb9SClaudio Fontana     WHvX64RegisterStar,
1311fc33bb9SClaudio Fontana #ifdef TARGET_X86_64
1321fc33bb9SClaudio Fontana     WHvX64RegisterLstar,
1331fc33bb9SClaudio Fontana     WHvX64RegisterCstar,
1341fc33bb9SClaudio Fontana     WHvX64RegisterSfmask,
1351fc33bb9SClaudio Fontana #endif
1361fc33bb9SClaudio Fontana 
1371fc33bb9SClaudio Fontana     /* Interrupt / Event Registers */
1381fc33bb9SClaudio Fontana     /*
1391fc33bb9SClaudio Fontana      * WHvRegisterPendingInterruption,
1401fc33bb9SClaudio Fontana      * WHvRegisterInterruptState,
1411fc33bb9SClaudio Fontana      * WHvRegisterPendingEvent0,
1421fc33bb9SClaudio Fontana      * WHvRegisterPendingEvent1
1431fc33bb9SClaudio Fontana      * WHvX64RegisterDeliverabilityNotifications,
1441fc33bb9SClaudio Fontana      */
1451fc33bb9SClaudio Fontana };
1461fc33bb9SClaudio Fontana 
1471fc33bb9SClaudio Fontana struct whpx_register_set {
1481fc33bb9SClaudio Fontana     WHV_REGISTER_VALUE values[RTL_NUMBER_OF(whpx_register_names)];
1491fc33bb9SClaudio Fontana };
1501fc33bb9SClaudio Fontana 
151d7482ffeSIvan Shcherbakov /*
152d7482ffeSIvan Shcherbakov  * The current implementation of instruction stepping sets the TF flag
153d7482ffeSIvan Shcherbakov  * in RFLAGS, causing the CPU to raise an INT1 after each instruction.
154d7482ffeSIvan Shcherbakov  * This corresponds to the WHvX64ExceptionTypeDebugTrapOrFault exception.
155d7482ffeSIvan Shcherbakov  *
156d7482ffeSIvan Shcherbakov  * This approach has a few limitations:
157d7482ffeSIvan Shcherbakov  *     1. Stepping over a PUSHF/SAHF instruction will save the TF flag
158d7482ffeSIvan Shcherbakov  *        along with the other flags, possibly restoring it later. It would
159d7482ffeSIvan Shcherbakov  *        result in another INT1 when the flags are restored, triggering
160d7482ffeSIvan Shcherbakov  *        a stop in gdb that could be cleared by doing another step.
161d7482ffeSIvan Shcherbakov  *
162d7482ffeSIvan Shcherbakov  *        Stepping over a POPF/LAHF instruction will let it overwrite the
163d7482ffeSIvan Shcherbakov  *        TF flags, ending the stepping mode.
164d7482ffeSIvan Shcherbakov  *
165d7482ffeSIvan Shcherbakov  *     2. Stepping over an instruction raising an exception (e.g. INT, DIV,
166d7482ffeSIvan Shcherbakov  *        or anything that could result in a page fault) will save the flags
167d7482ffeSIvan Shcherbakov  *        to the stack, clear the TF flag, and let the guest execute the
168d7482ffeSIvan Shcherbakov  *        handler. Normally, the guest will restore the original flags,
169d7482ffeSIvan Shcherbakov  *        that will continue single-stepping.
170d7482ffeSIvan Shcherbakov  *
171d7482ffeSIvan Shcherbakov  *     3. Debuggers running on the guest may wish to set TF to do instruction
172d7482ffeSIvan Shcherbakov  *        stepping. INT1 events generated by it would be intercepted by us,
173d7482ffeSIvan Shcherbakov  *        as long as the gdb is connected to QEMU.
174d7482ffeSIvan Shcherbakov  *
175d7482ffeSIvan Shcherbakov  * In practice this means that:
176d7482ffeSIvan Shcherbakov  *     1. Stepping through flags-modifying instructions may cause gdb to
177d7482ffeSIvan Shcherbakov  *        continue or stop in unexpected places. This will be fully recoverable
178d7482ffeSIvan Shcherbakov  *        and will not crash the target.
179d7482ffeSIvan Shcherbakov  *
180d7482ffeSIvan Shcherbakov  *     2. Stepping over an instruction that triggers an exception will step
181d7482ffeSIvan Shcherbakov  *        over the exception handler, not into it.
182d7482ffeSIvan Shcherbakov  *
183d7482ffeSIvan Shcherbakov  *     3. Debugging the guest via gdb, while running debugger on the guest
184d7482ffeSIvan Shcherbakov  *        at the same time may lead to unexpected effects. Removing all
185d7482ffeSIvan Shcherbakov  *        breakpoints set via QEMU will prevent any further interference
186d7482ffeSIvan Shcherbakov  *        with the guest-level debuggers.
187d7482ffeSIvan Shcherbakov  *
188d7482ffeSIvan Shcherbakov  * The limitations can be addressed as shown below:
189d7482ffeSIvan Shcherbakov  *     1. PUSHF/SAHF/POPF/LAHF/IRET instructions can be emulated instead of
190d7482ffeSIvan Shcherbakov  *        stepping through them. The exact semantics of the instructions is
191d7482ffeSIvan Shcherbakov  *        defined in the "Combined Volume Set of Intel 64 and IA-32
192d7482ffeSIvan Shcherbakov  *        Architectures Software Developer's Manuals", however it involves a
193d7482ffeSIvan Shcherbakov  *        fair amount of corner cases due to compatibility with real mode,
194d7482ffeSIvan Shcherbakov  *        virtual 8086 mode, and differences between 64-bit and 32-bit modes.
195d7482ffeSIvan Shcherbakov  *
196d7482ffeSIvan Shcherbakov  *     2. We could step into the guest's exception handlers using the following
197d7482ffeSIvan Shcherbakov  *        sequence:
198d7482ffeSIvan Shcherbakov  *          a. Temporarily enable catching of all exception types via
199d7482ffeSIvan Shcherbakov  *             whpx_set_exception_exit_bitmap().
200d7482ffeSIvan Shcherbakov  *          b. Once an exception is intercepted, read the IDT/GDT and locate
201d7482ffeSIvan Shcherbakov  *             the original handler.
202d7482ffeSIvan Shcherbakov  *          c. Patch the original handler, injecting an INT3 at the beginning.
203d7482ffeSIvan Shcherbakov  *          d. Update the exception exit bitmap to only catch the
204d7482ffeSIvan Shcherbakov  *             WHvX64ExceptionTypeBreakpointTrap exception.
205d7482ffeSIvan Shcherbakov  *          e. Let the affected CPU run in the exclusive mode.
206d7482ffeSIvan Shcherbakov  *          f. Restore the original handler and the exception exit bitmap.
207d7482ffeSIvan Shcherbakov  *        Note that handling all corner cases related to IDT/GDT is harder
208d7482ffeSIvan Shcherbakov  *        than it may seem. See x86_cpu_get_phys_page_attrs_debug() for a
209d7482ffeSIvan Shcherbakov  *        rough idea.
210d7482ffeSIvan Shcherbakov  *
211d7482ffeSIvan Shcherbakov  *     3. In order to properly support guest-level debugging in parallel with
212d7482ffeSIvan Shcherbakov  *        the QEMU-level debugging, we would need to be able to pass some INT1
213d7482ffeSIvan Shcherbakov  *        events to the guest. This could be done via the following methods:
214d7482ffeSIvan Shcherbakov  *          a. Using the WHvRegisterPendingEvent register. As of Windows 21H1,
215d7482ffeSIvan Shcherbakov  *             it seems to only work for interrupts and not software
216d7482ffeSIvan Shcherbakov  *             exceptions.
217d7482ffeSIvan Shcherbakov  *          b. Locating and patching the original handler by parsing IDT/GDT.
218d7482ffeSIvan Shcherbakov  *             This involves relatively complex logic outlined in the previous
219d7482ffeSIvan Shcherbakov  *             paragraph.
220d7482ffeSIvan Shcherbakov  *          c. Emulating the exception invocation (i.e. manually updating RIP,
221d7482ffeSIvan Shcherbakov  *             RFLAGS, and pushing the old values to stack). This is even more
222d7482ffeSIvan Shcherbakov  *             complicated than the previous option, since it involves checking
223d7482ffeSIvan Shcherbakov  *             CPL, gate attributes, and doing various adjustments depending
224d7482ffeSIvan Shcherbakov  *             on the current CPU mode, whether the CPL is changing, etc.
225d7482ffeSIvan Shcherbakov  */
226d7482ffeSIvan Shcherbakov typedef enum WhpxStepMode {
227d7482ffeSIvan Shcherbakov     WHPX_STEP_NONE = 0,
228d7482ffeSIvan Shcherbakov     /* Halt other VCPUs */
229d7482ffeSIvan Shcherbakov     WHPX_STEP_EXCLUSIVE,
230d7482ffeSIvan Shcherbakov } WhpxStepMode;
231d7482ffeSIvan Shcherbakov 
232b4f879a4SPhilippe Mathieu-Daudé struct AccelCPUState {
2331fc33bb9SClaudio Fontana     WHV_EMULATOR_HANDLE emulator;
2341fc33bb9SClaudio Fontana     bool window_registered;
2351fc33bb9SClaudio Fontana     bool interruptable;
2361fc33bb9SClaudio Fontana     bool ready_for_pic_interrupt;
2371fc33bb9SClaudio Fontana     uint64_t tpr;
2381fc33bb9SClaudio Fontana     uint64_t apic_base;
2391fc33bb9SClaudio Fontana     bool interruption_pending;
2401fc33bb9SClaudio Fontana 
2411fc33bb9SClaudio Fontana     /* Must be the last field as it may have a tail */
2421fc33bb9SClaudio Fontana     WHV_RUN_VP_EXIT_CONTEXT exit_ctx;
2431fc33bb9SClaudio Fontana };
2441fc33bb9SClaudio Fontana 
2451fc33bb9SClaudio Fontana static bool whpx_allowed;
2461fc33bb9SClaudio Fontana static bool whp_dispatch_initialized;
2471fc33bb9SClaudio Fontana static HMODULE hWinHvPlatform, hWinHvEmulation;
2481fc33bb9SClaudio Fontana static uint32_t max_vcpu_index;
249b6b3da99SSunil Muthuswamy static WHV_PROCESSOR_XSAVE_FEATURES whpx_xsave_cap;
250b6b3da99SSunil Muthuswamy 
2511fc33bb9SClaudio Fontana struct whpx_state whpx_global;
2521fc33bb9SClaudio Fontana struct WHPDispatch whp_dispatch;
2531fc33bb9SClaudio Fontana 
254b6b3da99SSunil Muthuswamy static bool whpx_has_xsave(void)
255b6b3da99SSunil Muthuswamy {
256b6b3da99SSunil Muthuswamy     return whpx_xsave_cap.XsaveSupport;
257b6b3da99SSunil Muthuswamy }
2581fc33bb9SClaudio Fontana 
2591fc33bb9SClaudio Fontana static WHV_X64_SEGMENT_REGISTER whpx_seg_q2h(const SegmentCache *qs, int v86,
2601fc33bb9SClaudio Fontana                                              int r86)
2611fc33bb9SClaudio Fontana {
2621fc33bb9SClaudio Fontana     WHV_X64_SEGMENT_REGISTER hs;
2631fc33bb9SClaudio Fontana     unsigned flags = qs->flags;
2641fc33bb9SClaudio Fontana 
2651fc33bb9SClaudio Fontana     hs.Base = qs->base;
2661fc33bb9SClaudio Fontana     hs.Limit = qs->limit;
2671fc33bb9SClaudio Fontana     hs.Selector = qs->selector;
2681fc33bb9SClaudio Fontana 
2691fc33bb9SClaudio Fontana     if (v86) {
2701fc33bb9SClaudio Fontana         hs.Attributes = 0;
2711fc33bb9SClaudio Fontana         hs.SegmentType = 3;
2721fc33bb9SClaudio Fontana         hs.Present = 1;
2731fc33bb9SClaudio Fontana         hs.DescriptorPrivilegeLevel = 3;
2741fc33bb9SClaudio Fontana         hs.NonSystemSegment = 1;
2751fc33bb9SClaudio Fontana 
2761fc33bb9SClaudio Fontana     } else {
2771fc33bb9SClaudio Fontana         hs.Attributes = (flags >> DESC_TYPE_SHIFT);
2781fc33bb9SClaudio Fontana 
2791fc33bb9SClaudio Fontana         if (r86) {
2801fc33bb9SClaudio Fontana             /* hs.Base &= 0xfffff; */
2811fc33bb9SClaudio Fontana         }
2821fc33bb9SClaudio Fontana     }
2831fc33bb9SClaudio Fontana 
2841fc33bb9SClaudio Fontana     return hs;
2851fc33bb9SClaudio Fontana }
2861fc33bb9SClaudio Fontana 
2871fc33bb9SClaudio Fontana static SegmentCache whpx_seg_h2q(const WHV_X64_SEGMENT_REGISTER *hs)
2881fc33bb9SClaudio Fontana {
2891fc33bb9SClaudio Fontana     SegmentCache qs;
2901fc33bb9SClaudio Fontana 
2911fc33bb9SClaudio Fontana     qs.base = hs->Base;
2921fc33bb9SClaudio Fontana     qs.limit = hs->Limit;
2931fc33bb9SClaudio Fontana     qs.selector = hs->Selector;
2941fc33bb9SClaudio Fontana 
2951fc33bb9SClaudio Fontana     qs.flags = ((uint32_t)hs->Attributes) << DESC_TYPE_SHIFT;
2961fc33bb9SClaudio Fontana 
2971fc33bb9SClaudio Fontana     return qs;
2981fc33bb9SClaudio Fontana }
2991fc33bb9SClaudio Fontana 
300b6b3da99SSunil Muthuswamy /* X64 Extended Control Registers */
301b6b3da99SSunil Muthuswamy static void whpx_set_xcrs(CPUState *cpu)
302b6b3da99SSunil Muthuswamy {
303b6b3da99SSunil Muthuswamy     CPUX86State *env = cpu->env_ptr;
304b6b3da99SSunil Muthuswamy     HRESULT hr;
305b6b3da99SSunil Muthuswamy     struct whpx_state *whpx = &whpx_global;
306b6b3da99SSunil Muthuswamy     WHV_REGISTER_VALUE xcr0;
307b6b3da99SSunil Muthuswamy     WHV_REGISTER_NAME xcr0_name = WHvX64RegisterXCr0;
308b6b3da99SSunil Muthuswamy 
309b6b3da99SSunil Muthuswamy     if (!whpx_has_xsave()) {
310b6b3da99SSunil Muthuswamy         return;
311b6b3da99SSunil Muthuswamy     }
312b6b3da99SSunil Muthuswamy 
313b6b3da99SSunil Muthuswamy     /* Only xcr0 is supported by the hypervisor currently */
314b6b3da99SSunil Muthuswamy     xcr0.Reg64 = env->xcr0;
315b6b3da99SSunil Muthuswamy     hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
316b6b3da99SSunil Muthuswamy         whpx->partition, cpu->cpu_index, &xcr0_name, 1, &xcr0);
317b6b3da99SSunil Muthuswamy     if (FAILED(hr)) {
318b6b3da99SSunil Muthuswamy         error_report("WHPX: Failed to set register xcr0, hr=%08lx", hr);
319b6b3da99SSunil Muthuswamy     }
320b6b3da99SSunil Muthuswamy }
321b6b3da99SSunil Muthuswamy 
3221fc33bb9SClaudio Fontana static int whpx_set_tsc(CPUState *cpu)
3231fc33bb9SClaudio Fontana {
32495e862d7SPhilippe Mathieu-Daudé     CPUX86State *env = cpu->env_ptr;
3251fc33bb9SClaudio Fontana     WHV_REGISTER_NAME tsc_reg = WHvX64RegisterTsc;
3261fc33bb9SClaudio Fontana     WHV_REGISTER_VALUE tsc_val;
3271fc33bb9SClaudio Fontana     HRESULT hr;
3281fc33bb9SClaudio Fontana     struct whpx_state *whpx = &whpx_global;
3291fc33bb9SClaudio Fontana 
3301fc33bb9SClaudio Fontana     /*
3311fc33bb9SClaudio Fontana      * Suspend the partition prior to setting the TSC to reduce the variance
3321fc33bb9SClaudio Fontana      * in TSC across vCPUs. When the first vCPU runs post suspend, the
3331fc33bb9SClaudio Fontana      * partition is automatically resumed.
3341fc33bb9SClaudio Fontana      */
3351fc33bb9SClaudio Fontana     if (whp_dispatch.WHvSuspendPartitionTime) {
3361fc33bb9SClaudio Fontana 
3371fc33bb9SClaudio Fontana         /*
3381fc33bb9SClaudio Fontana          * Unable to suspend partition while setting TSC is not a fatal
3391fc33bb9SClaudio Fontana          * error. It just increases the likelihood of TSC variance between
3401fc33bb9SClaudio Fontana          * vCPUs and some guest OS are able to handle that just fine.
3411fc33bb9SClaudio Fontana          */
3421fc33bb9SClaudio Fontana         hr = whp_dispatch.WHvSuspendPartitionTime(whpx->partition);
3431fc33bb9SClaudio Fontana         if (FAILED(hr)) {
3441fc33bb9SClaudio Fontana             warn_report("WHPX: Failed to suspend partition, hr=%08lx", hr);
3451fc33bb9SClaudio Fontana         }
3461fc33bb9SClaudio Fontana     }
3471fc33bb9SClaudio Fontana 
3481fc33bb9SClaudio Fontana     tsc_val.Reg64 = env->tsc;
3491fc33bb9SClaudio Fontana     hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
3501fc33bb9SClaudio Fontana         whpx->partition, cpu->cpu_index, &tsc_reg, 1, &tsc_val);
3511fc33bb9SClaudio Fontana     if (FAILED(hr)) {
3521fc33bb9SClaudio Fontana         error_report("WHPX: Failed to set TSC, hr=%08lx", hr);
3531fc33bb9SClaudio Fontana         return -1;
3541fc33bb9SClaudio Fontana     }
3551fc33bb9SClaudio Fontana 
3561fc33bb9SClaudio Fontana     return 0;
3571fc33bb9SClaudio Fontana }
3581fc33bb9SClaudio Fontana 
3595ad93fd3SIvan Shcherbakov /*
3605ad93fd3SIvan Shcherbakov  * The CR8 register in the CPU is mapped to the TPR register of the APIC,
3615ad93fd3SIvan Shcherbakov  * however, they use a slightly different encoding. Specifically:
3625ad93fd3SIvan Shcherbakov  *
3635ad93fd3SIvan Shcherbakov  *     APIC.TPR[bits 7:4] = CR8[bits 3:0]
3645ad93fd3SIvan Shcherbakov  *
3655ad93fd3SIvan Shcherbakov  * This mechanism is described in section 10.8.6.1 of Volume 3 of Intel 64
3665ad93fd3SIvan Shcherbakov  * and IA-32 Architectures Software Developer's Manual.
367f000bc74SIvan Shcherbakov  *
368f000bc74SIvan Shcherbakov  * The functions below translate the value of CR8 to TPR and vice versa.
3695ad93fd3SIvan Shcherbakov  */
3705ad93fd3SIvan Shcherbakov 
3715ad93fd3SIvan Shcherbakov static uint64_t whpx_apic_tpr_to_cr8(uint64_t tpr)
3725ad93fd3SIvan Shcherbakov {
3735ad93fd3SIvan Shcherbakov     return tpr >> 4;
3745ad93fd3SIvan Shcherbakov }
3755ad93fd3SIvan Shcherbakov 
376f000bc74SIvan Shcherbakov static uint64_t whpx_cr8_to_apic_tpr(uint64_t cr8)
377f000bc74SIvan Shcherbakov {
378f000bc74SIvan Shcherbakov     return cr8 << 4;
379f000bc74SIvan Shcherbakov }
380f000bc74SIvan Shcherbakov 
3811fc33bb9SClaudio Fontana static void whpx_set_registers(CPUState *cpu, int level)
3821fc33bb9SClaudio Fontana {
3831fc33bb9SClaudio Fontana     struct whpx_state *whpx = &whpx_global;
384441f2449SPhilippe Mathieu-Daudé     AccelCPUState *vcpu = cpu->accel;
38595e862d7SPhilippe Mathieu-Daudé     CPUX86State *env = cpu->env_ptr;
3861fc33bb9SClaudio Fontana     X86CPU *x86_cpu = X86_CPU(cpu);
3871fc33bb9SClaudio Fontana     struct whpx_register_set vcxt;
3881fc33bb9SClaudio Fontana     HRESULT hr;
3891fc33bb9SClaudio Fontana     int idx;
3901fc33bb9SClaudio Fontana     int idx_next;
3911fc33bb9SClaudio Fontana     int i;
3921fc33bb9SClaudio Fontana     int v86, r86;
3931fc33bb9SClaudio Fontana 
3941fc33bb9SClaudio Fontana     assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));
3951fc33bb9SClaudio Fontana 
3961fc33bb9SClaudio Fontana     /*
3971fc33bb9SClaudio Fontana      * Following MSRs have side effects on the guest or are too heavy for
3981fc33bb9SClaudio Fontana      * runtime. Limit them to full state update.
3991fc33bb9SClaudio Fontana      */
4001fc33bb9SClaudio Fontana     if (level >= WHPX_SET_RESET_STATE) {
4011fc33bb9SClaudio Fontana         whpx_set_tsc(cpu);
4021fc33bb9SClaudio Fontana     }
4031fc33bb9SClaudio Fontana 
4041fc33bb9SClaudio Fontana     memset(&vcxt, 0, sizeof(struct whpx_register_set));
4051fc33bb9SClaudio Fontana 
4061fc33bb9SClaudio Fontana     v86 = (env->eflags & VM_MASK);
4071fc33bb9SClaudio Fontana     r86 = !(env->cr[0] & CR0_PE_MASK);
4081fc33bb9SClaudio Fontana 
4095ad93fd3SIvan Shcherbakov     vcpu->tpr = whpx_apic_tpr_to_cr8(cpu_get_apic_tpr(x86_cpu->apic_state));
4101fc33bb9SClaudio Fontana     vcpu->apic_base = cpu_get_apic_base(x86_cpu->apic_state);
4111fc33bb9SClaudio Fontana 
4121fc33bb9SClaudio Fontana     idx = 0;
4131fc33bb9SClaudio Fontana 
4141fc33bb9SClaudio Fontana     /* Indexes for first 16 registers match between HV and QEMU definitions */
4151fc33bb9SClaudio Fontana     idx_next = 16;
4161fc33bb9SClaudio Fontana     for (idx = 0; idx < CPU_NB_REGS; idx += 1) {
4171fc33bb9SClaudio Fontana         vcxt.values[idx].Reg64 = (uint64_t)env->regs[idx];
4181fc33bb9SClaudio Fontana     }
4191fc33bb9SClaudio Fontana     idx = idx_next;
4201fc33bb9SClaudio Fontana 
4211fc33bb9SClaudio Fontana     /* Same goes for RIP and RFLAGS */
4221fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterRip);
4231fc33bb9SClaudio Fontana     vcxt.values[idx++].Reg64 = env->eip;
4241fc33bb9SClaudio Fontana 
4251fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterRflags);
4261fc33bb9SClaudio Fontana     vcxt.values[idx++].Reg64 = env->eflags;
4271fc33bb9SClaudio Fontana 
4281fc33bb9SClaudio Fontana     /* Translate 6+4 segment registers. HV and QEMU order matches  */
4291fc33bb9SClaudio Fontana     assert(idx == WHvX64RegisterEs);
4301fc33bb9SClaudio Fontana     for (i = 0; i < 6; i += 1, idx += 1) {
4311fc33bb9SClaudio Fontana         vcxt.values[idx].Segment = whpx_seg_q2h(&env->segs[i], v86, r86);
4321fc33bb9SClaudio Fontana     }
4331fc33bb9SClaudio Fontana 
4341fc33bb9SClaudio Fontana     assert(idx == WHvX64RegisterLdtr);
4351fc33bb9SClaudio Fontana     vcxt.values[idx++].Segment = whpx_seg_q2h(&env->ldt, 0, 0);
4361fc33bb9SClaudio Fontana 
4371fc33bb9SClaudio Fontana     assert(idx == WHvX64RegisterTr);
4381fc33bb9SClaudio Fontana     vcxt.values[idx++].Segment = whpx_seg_q2h(&env->tr, 0, 0);
4391fc33bb9SClaudio Fontana 
4401fc33bb9SClaudio Fontana     assert(idx == WHvX64RegisterIdtr);
4411fc33bb9SClaudio Fontana     vcxt.values[idx].Table.Base = env->idt.base;
4421fc33bb9SClaudio Fontana     vcxt.values[idx].Table.Limit = env->idt.limit;
4431fc33bb9SClaudio Fontana     idx += 1;
4441fc33bb9SClaudio Fontana 
4451fc33bb9SClaudio Fontana     assert(idx == WHvX64RegisterGdtr);
4461fc33bb9SClaudio Fontana     vcxt.values[idx].Table.Base = env->gdt.base;
4471fc33bb9SClaudio Fontana     vcxt.values[idx].Table.Limit = env->gdt.limit;
4481fc33bb9SClaudio Fontana     idx += 1;
4491fc33bb9SClaudio Fontana 
4501fc33bb9SClaudio Fontana     /* CR0, 2, 3, 4, 8 */
4511fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterCr0);
4521fc33bb9SClaudio Fontana     vcxt.values[idx++].Reg64 = env->cr[0];
4531fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterCr2);
4541fc33bb9SClaudio Fontana     vcxt.values[idx++].Reg64 = env->cr[2];
4551fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterCr3);
4561fc33bb9SClaudio Fontana     vcxt.values[idx++].Reg64 = env->cr[3];
4571fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterCr4);
4581fc33bb9SClaudio Fontana     vcxt.values[idx++].Reg64 = env->cr[4];
4591fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterCr8);
4601fc33bb9SClaudio Fontana     vcxt.values[idx++].Reg64 = vcpu->tpr;
4611fc33bb9SClaudio Fontana 
4621fc33bb9SClaudio Fontana     /* 8 Debug Registers - Skipped */
4631fc33bb9SClaudio Fontana 
464b6b3da99SSunil Muthuswamy     /*
465b6b3da99SSunil Muthuswamy      * Extended control registers needs to be handled separately depending
466b6b3da99SSunil Muthuswamy      * on whether xsave is supported/enabled or not.
467b6b3da99SSunil Muthuswamy      */
468b6b3da99SSunil Muthuswamy     whpx_set_xcrs(cpu);
469b6b3da99SSunil Muthuswamy 
4701fc33bb9SClaudio Fontana     /* 16 XMM registers */
4711fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterXmm0);
4721fc33bb9SClaudio Fontana     idx_next = idx + 16;
4731fc33bb9SClaudio Fontana     for (i = 0; i < sizeof(env->xmm_regs) / sizeof(ZMMReg); i += 1, idx += 1) {
4741fc33bb9SClaudio Fontana         vcxt.values[idx].Reg128.Low64 = env->xmm_regs[i].ZMM_Q(0);
4751fc33bb9SClaudio Fontana         vcxt.values[idx].Reg128.High64 = env->xmm_regs[i].ZMM_Q(1);
4761fc33bb9SClaudio Fontana     }
4771fc33bb9SClaudio Fontana     idx = idx_next;
4781fc33bb9SClaudio Fontana 
4791fc33bb9SClaudio Fontana     /* 8 FP registers */
4801fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterFpMmx0);
4811fc33bb9SClaudio Fontana     for (i = 0; i < 8; i += 1, idx += 1) {
4821fc33bb9SClaudio Fontana         vcxt.values[idx].Fp.AsUINT128.Low64 = env->fpregs[i].mmx.MMX_Q(0);
4831fc33bb9SClaudio Fontana         /* vcxt.values[idx].Fp.AsUINT128.High64 =
4841fc33bb9SClaudio Fontana                env->fpregs[i].mmx.MMX_Q(1);
4851fc33bb9SClaudio Fontana         */
4861fc33bb9SClaudio Fontana     }
4871fc33bb9SClaudio Fontana 
4881fc33bb9SClaudio Fontana     /* FP control status register */
4891fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterFpControlStatus);
4901fc33bb9SClaudio Fontana     vcxt.values[idx].FpControlStatus.FpControl = env->fpuc;
4911fc33bb9SClaudio Fontana     vcxt.values[idx].FpControlStatus.FpStatus =
4921fc33bb9SClaudio Fontana         (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
4931fc33bb9SClaudio Fontana     vcxt.values[idx].FpControlStatus.FpTag = 0;
4941fc33bb9SClaudio Fontana     for (i = 0; i < 8; ++i) {
4951fc33bb9SClaudio Fontana         vcxt.values[idx].FpControlStatus.FpTag |= (!env->fptags[i]) << i;
4961fc33bb9SClaudio Fontana     }
4971fc33bb9SClaudio Fontana     vcxt.values[idx].FpControlStatus.Reserved = 0;
4981fc33bb9SClaudio Fontana     vcxt.values[idx].FpControlStatus.LastFpOp = env->fpop;
4991fc33bb9SClaudio Fontana     vcxt.values[idx].FpControlStatus.LastFpRip = env->fpip;
5001fc33bb9SClaudio Fontana     idx += 1;
5011fc33bb9SClaudio Fontana 
5021fc33bb9SClaudio Fontana     /* XMM control status register */
5031fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterXmmControlStatus);
5041fc33bb9SClaudio Fontana     vcxt.values[idx].XmmControlStatus.LastFpRdp = 0;
5051fc33bb9SClaudio Fontana     vcxt.values[idx].XmmControlStatus.XmmStatusControl = env->mxcsr;
5061fc33bb9SClaudio Fontana     vcxt.values[idx].XmmControlStatus.XmmStatusControlMask = 0x0000ffff;
5071fc33bb9SClaudio Fontana     idx += 1;
5081fc33bb9SClaudio Fontana 
5091fc33bb9SClaudio Fontana     /* MSRs */
5101fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterEfer);
5111fc33bb9SClaudio Fontana     vcxt.values[idx++].Reg64 = env->efer;
5121fc33bb9SClaudio Fontana #ifdef TARGET_X86_64
5131fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterKernelGsBase);
5141fc33bb9SClaudio Fontana     vcxt.values[idx++].Reg64 = env->kernelgsbase;
5151fc33bb9SClaudio Fontana #endif
5161fc33bb9SClaudio Fontana 
5171fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterApicBase);
5181fc33bb9SClaudio Fontana     vcxt.values[idx++].Reg64 = vcpu->apic_base;
5191fc33bb9SClaudio Fontana 
5201fc33bb9SClaudio Fontana     /* WHvX64RegisterPat - Skipped */
5211fc33bb9SClaudio Fontana 
5221fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterSysenterCs);
5231fc33bb9SClaudio Fontana     vcxt.values[idx++].Reg64 = env->sysenter_cs;
5241fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterSysenterEip);
5251fc33bb9SClaudio Fontana     vcxt.values[idx++].Reg64 = env->sysenter_eip;
5261fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterSysenterEsp);
5271fc33bb9SClaudio Fontana     vcxt.values[idx++].Reg64 = env->sysenter_esp;
5281fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterStar);
5291fc33bb9SClaudio Fontana     vcxt.values[idx++].Reg64 = env->star;
5301fc33bb9SClaudio Fontana #ifdef TARGET_X86_64
5311fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterLstar);
5321fc33bb9SClaudio Fontana     vcxt.values[idx++].Reg64 = env->lstar;
5331fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterCstar);
5341fc33bb9SClaudio Fontana     vcxt.values[idx++].Reg64 = env->cstar;
5351fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterSfmask);
5361fc33bb9SClaudio Fontana     vcxt.values[idx++].Reg64 = env->fmask;
5371fc33bb9SClaudio Fontana #endif
5381fc33bb9SClaudio Fontana 
5391fc33bb9SClaudio Fontana     /* Interrupt / Event Registers - Skipped */
5401fc33bb9SClaudio Fontana 
5411fc33bb9SClaudio Fontana     assert(idx == RTL_NUMBER_OF(whpx_register_names));
5421fc33bb9SClaudio Fontana 
5431fc33bb9SClaudio Fontana     hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
5441fc33bb9SClaudio Fontana         whpx->partition, cpu->cpu_index,
5451fc33bb9SClaudio Fontana         whpx_register_names,
5461fc33bb9SClaudio Fontana         RTL_NUMBER_OF(whpx_register_names),
5471fc33bb9SClaudio Fontana         &vcxt.values[0]);
5481fc33bb9SClaudio Fontana 
5491fc33bb9SClaudio Fontana     if (FAILED(hr)) {
5501fc33bb9SClaudio Fontana         error_report("WHPX: Failed to set virtual processor context, hr=%08lx",
5511fc33bb9SClaudio Fontana                      hr);
5521fc33bb9SClaudio Fontana     }
5531fc33bb9SClaudio Fontana 
5541fc33bb9SClaudio Fontana     return;
5551fc33bb9SClaudio Fontana }
5561fc33bb9SClaudio Fontana 
5571fc33bb9SClaudio Fontana static int whpx_get_tsc(CPUState *cpu)
5581fc33bb9SClaudio Fontana {
55995e862d7SPhilippe Mathieu-Daudé     CPUX86State *env = cpu->env_ptr;
5601fc33bb9SClaudio Fontana     WHV_REGISTER_NAME tsc_reg = WHvX64RegisterTsc;
5611fc33bb9SClaudio Fontana     WHV_REGISTER_VALUE tsc_val;
5621fc33bb9SClaudio Fontana     HRESULT hr;
5631fc33bb9SClaudio Fontana     struct whpx_state *whpx = &whpx_global;
5641fc33bb9SClaudio Fontana 
5651fc33bb9SClaudio Fontana     hr = whp_dispatch.WHvGetVirtualProcessorRegisters(
5661fc33bb9SClaudio Fontana         whpx->partition, cpu->cpu_index, &tsc_reg, 1, &tsc_val);
5671fc33bb9SClaudio Fontana     if (FAILED(hr)) {
5681fc33bb9SClaudio Fontana         error_report("WHPX: Failed to get TSC, hr=%08lx", hr);
5691fc33bb9SClaudio Fontana         return -1;
5701fc33bb9SClaudio Fontana     }
5711fc33bb9SClaudio Fontana 
5721fc33bb9SClaudio Fontana     env->tsc = tsc_val.Reg64;
5731fc33bb9SClaudio Fontana     return 0;
5741fc33bb9SClaudio Fontana }
5751fc33bb9SClaudio Fontana 
576b6b3da99SSunil Muthuswamy /* X64 Extended Control Registers */
577b6b3da99SSunil Muthuswamy static void whpx_get_xcrs(CPUState *cpu)
578b6b3da99SSunil Muthuswamy {
579b6b3da99SSunil Muthuswamy     CPUX86State *env = cpu->env_ptr;
580b6b3da99SSunil Muthuswamy     HRESULT hr;
581b6b3da99SSunil Muthuswamy     struct whpx_state *whpx = &whpx_global;
582b6b3da99SSunil Muthuswamy     WHV_REGISTER_VALUE xcr0;
583b6b3da99SSunil Muthuswamy     WHV_REGISTER_NAME xcr0_name = WHvX64RegisterXCr0;
584b6b3da99SSunil Muthuswamy 
585b6b3da99SSunil Muthuswamy     if (!whpx_has_xsave()) {
586b6b3da99SSunil Muthuswamy         return;
587b6b3da99SSunil Muthuswamy     }
588b6b3da99SSunil Muthuswamy 
589b6b3da99SSunil Muthuswamy     /* Only xcr0 is supported by the hypervisor currently */
590b6b3da99SSunil Muthuswamy     hr = whp_dispatch.WHvGetVirtualProcessorRegisters(
591b6b3da99SSunil Muthuswamy         whpx->partition, cpu->cpu_index, &xcr0_name, 1, &xcr0);
592b6b3da99SSunil Muthuswamy     if (FAILED(hr)) {
593b6b3da99SSunil Muthuswamy         error_report("WHPX: Failed to get register xcr0, hr=%08lx", hr);
594b6b3da99SSunil Muthuswamy         return;
595b6b3da99SSunil Muthuswamy     }
596b6b3da99SSunil Muthuswamy 
597b6b3da99SSunil Muthuswamy     env->xcr0 = xcr0.Reg64;
598b6b3da99SSunil Muthuswamy }
599b6b3da99SSunil Muthuswamy 
6001fc33bb9SClaudio Fontana static void whpx_get_registers(CPUState *cpu)
6011fc33bb9SClaudio Fontana {
6021fc33bb9SClaudio Fontana     struct whpx_state *whpx = &whpx_global;
603441f2449SPhilippe Mathieu-Daudé     AccelCPUState *vcpu = cpu->accel;
60495e862d7SPhilippe Mathieu-Daudé     CPUX86State *env = cpu->env_ptr;
6051fc33bb9SClaudio Fontana     X86CPU *x86_cpu = X86_CPU(cpu);
6061fc33bb9SClaudio Fontana     struct whpx_register_set vcxt;
6071fc33bb9SClaudio Fontana     uint64_t tpr, apic_base;
6081fc33bb9SClaudio Fontana     HRESULT hr;
6091fc33bb9SClaudio Fontana     int idx;
6101fc33bb9SClaudio Fontana     int idx_next;
6111fc33bb9SClaudio Fontana     int i;
6121fc33bb9SClaudio Fontana 
6131fc33bb9SClaudio Fontana     assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));
6141fc33bb9SClaudio Fontana 
6151fc33bb9SClaudio Fontana     if (!env->tsc_valid) {
6161fc33bb9SClaudio Fontana         whpx_get_tsc(cpu);
6171fc33bb9SClaudio Fontana         env->tsc_valid = !runstate_is_running();
6181fc33bb9SClaudio Fontana     }
6191fc33bb9SClaudio Fontana 
6201fc33bb9SClaudio Fontana     hr = whp_dispatch.WHvGetVirtualProcessorRegisters(
6211fc33bb9SClaudio Fontana         whpx->partition, cpu->cpu_index,
6221fc33bb9SClaudio Fontana         whpx_register_names,
6231fc33bb9SClaudio Fontana         RTL_NUMBER_OF(whpx_register_names),
6241fc33bb9SClaudio Fontana         &vcxt.values[0]);
6251fc33bb9SClaudio Fontana     if (FAILED(hr)) {
6261fc33bb9SClaudio Fontana         error_report("WHPX: Failed to get virtual processor context, hr=%08lx",
6271fc33bb9SClaudio Fontana                      hr);
6281fc33bb9SClaudio Fontana     }
6291fc33bb9SClaudio Fontana 
6305ad93fd3SIvan Shcherbakov     if (whpx_apic_in_platform()) {
6315ad93fd3SIvan Shcherbakov         /*
6325ad93fd3SIvan Shcherbakov          * Fetch the TPR value from the emulated APIC. It may get overwritten
6335ad93fd3SIvan Shcherbakov          * below with the value from CR8 returned by
6345ad93fd3SIvan Shcherbakov          * WHvGetVirtualProcessorRegisters().
6355ad93fd3SIvan Shcherbakov          */
6365ad93fd3SIvan Shcherbakov         whpx_apic_get(x86_cpu->apic_state);
6375ad93fd3SIvan Shcherbakov         vcpu->tpr = whpx_apic_tpr_to_cr8(
6385ad93fd3SIvan Shcherbakov             cpu_get_apic_tpr(x86_cpu->apic_state));
6395ad93fd3SIvan Shcherbakov     }
6405ad93fd3SIvan Shcherbakov 
6411fc33bb9SClaudio Fontana     idx = 0;
6421fc33bb9SClaudio Fontana 
6431fc33bb9SClaudio Fontana     /* Indexes for first 16 registers match between HV and QEMU definitions */
6441fc33bb9SClaudio Fontana     idx_next = 16;
6451fc33bb9SClaudio Fontana     for (idx = 0; idx < CPU_NB_REGS; idx += 1) {
6461fc33bb9SClaudio Fontana         env->regs[idx] = vcxt.values[idx].Reg64;
6471fc33bb9SClaudio Fontana     }
6481fc33bb9SClaudio Fontana     idx = idx_next;
6491fc33bb9SClaudio Fontana 
6501fc33bb9SClaudio Fontana     /* Same goes for RIP and RFLAGS */
6511fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterRip);
6521fc33bb9SClaudio Fontana     env->eip = vcxt.values[idx++].Reg64;
6531fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterRflags);
6541fc33bb9SClaudio Fontana     env->eflags = vcxt.values[idx++].Reg64;
6551fc33bb9SClaudio Fontana 
6561fc33bb9SClaudio Fontana     /* Translate 6+4 segment registers. HV and QEMU order matches  */
6571fc33bb9SClaudio Fontana     assert(idx == WHvX64RegisterEs);
6581fc33bb9SClaudio Fontana     for (i = 0; i < 6; i += 1, idx += 1) {
6591fc33bb9SClaudio Fontana         env->segs[i] = whpx_seg_h2q(&vcxt.values[idx].Segment);
6601fc33bb9SClaudio Fontana     }
6611fc33bb9SClaudio Fontana 
6621fc33bb9SClaudio Fontana     assert(idx == WHvX64RegisterLdtr);
6631fc33bb9SClaudio Fontana     env->ldt = whpx_seg_h2q(&vcxt.values[idx++].Segment);
6641fc33bb9SClaudio Fontana     assert(idx == WHvX64RegisterTr);
6651fc33bb9SClaudio Fontana     env->tr = whpx_seg_h2q(&vcxt.values[idx++].Segment);
6661fc33bb9SClaudio Fontana     assert(idx == WHvX64RegisterIdtr);
6671fc33bb9SClaudio Fontana     env->idt.base = vcxt.values[idx].Table.Base;
6681fc33bb9SClaudio Fontana     env->idt.limit = vcxt.values[idx].Table.Limit;
6691fc33bb9SClaudio Fontana     idx += 1;
6701fc33bb9SClaudio Fontana     assert(idx == WHvX64RegisterGdtr);
6711fc33bb9SClaudio Fontana     env->gdt.base = vcxt.values[idx].Table.Base;
6721fc33bb9SClaudio Fontana     env->gdt.limit = vcxt.values[idx].Table.Limit;
6731fc33bb9SClaudio Fontana     idx += 1;
6741fc33bb9SClaudio Fontana 
6751fc33bb9SClaudio Fontana     /* CR0, 2, 3, 4, 8 */
6761fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterCr0);
6771fc33bb9SClaudio Fontana     env->cr[0] = vcxt.values[idx++].Reg64;
6781fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterCr2);
6791fc33bb9SClaudio Fontana     env->cr[2] = vcxt.values[idx++].Reg64;
6801fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterCr3);
6811fc33bb9SClaudio Fontana     env->cr[3] = vcxt.values[idx++].Reg64;
6821fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterCr4);
6831fc33bb9SClaudio Fontana     env->cr[4] = vcxt.values[idx++].Reg64;
6841fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterCr8);
6851fc33bb9SClaudio Fontana     tpr = vcxt.values[idx++].Reg64;
6861fc33bb9SClaudio Fontana     if (tpr != vcpu->tpr) {
6871fc33bb9SClaudio Fontana         vcpu->tpr = tpr;
688f000bc74SIvan Shcherbakov         cpu_set_apic_tpr(x86_cpu->apic_state, whpx_cr8_to_apic_tpr(tpr));
6891fc33bb9SClaudio Fontana     }
6901fc33bb9SClaudio Fontana 
6911fc33bb9SClaudio Fontana     /* 8 Debug Registers - Skipped */
6921fc33bb9SClaudio Fontana 
693b6b3da99SSunil Muthuswamy     /*
694b6b3da99SSunil Muthuswamy      * Extended control registers needs to be handled separately depending
695b6b3da99SSunil Muthuswamy      * on whether xsave is supported/enabled or not.
696b6b3da99SSunil Muthuswamy      */
697b6b3da99SSunil Muthuswamy     whpx_get_xcrs(cpu);
698b6b3da99SSunil Muthuswamy 
6991fc33bb9SClaudio Fontana     /* 16 XMM registers */
7001fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterXmm0);
7011fc33bb9SClaudio Fontana     idx_next = idx + 16;
7021fc33bb9SClaudio Fontana     for (i = 0; i < sizeof(env->xmm_regs) / sizeof(ZMMReg); i += 1, idx += 1) {
7031fc33bb9SClaudio Fontana         env->xmm_regs[i].ZMM_Q(0) = vcxt.values[idx].Reg128.Low64;
7041fc33bb9SClaudio Fontana         env->xmm_regs[i].ZMM_Q(1) = vcxt.values[idx].Reg128.High64;
7051fc33bb9SClaudio Fontana     }
7061fc33bb9SClaudio Fontana     idx = idx_next;
7071fc33bb9SClaudio Fontana 
7081fc33bb9SClaudio Fontana     /* 8 FP registers */
7091fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterFpMmx0);
7101fc33bb9SClaudio Fontana     for (i = 0; i < 8; i += 1, idx += 1) {
7111fc33bb9SClaudio Fontana         env->fpregs[i].mmx.MMX_Q(0) = vcxt.values[idx].Fp.AsUINT128.Low64;
7121fc33bb9SClaudio Fontana         /* env->fpregs[i].mmx.MMX_Q(1) =
7131fc33bb9SClaudio Fontana                vcxt.values[idx].Fp.AsUINT128.High64;
7141fc33bb9SClaudio Fontana         */
7151fc33bb9SClaudio Fontana     }
7161fc33bb9SClaudio Fontana 
7171fc33bb9SClaudio Fontana     /* FP control status register */
7181fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterFpControlStatus);
7191fc33bb9SClaudio Fontana     env->fpuc = vcxt.values[idx].FpControlStatus.FpControl;
7201fc33bb9SClaudio Fontana     env->fpstt = (vcxt.values[idx].FpControlStatus.FpStatus >> 11) & 0x7;
7211fc33bb9SClaudio Fontana     env->fpus = vcxt.values[idx].FpControlStatus.FpStatus & ~0x3800;
7221fc33bb9SClaudio Fontana     for (i = 0; i < 8; ++i) {
7231fc33bb9SClaudio Fontana         env->fptags[i] = !((vcxt.values[idx].FpControlStatus.FpTag >> i) & 1);
7241fc33bb9SClaudio Fontana     }
7251fc33bb9SClaudio Fontana     env->fpop = vcxt.values[idx].FpControlStatus.LastFpOp;
7261fc33bb9SClaudio Fontana     env->fpip = vcxt.values[idx].FpControlStatus.LastFpRip;
7271fc33bb9SClaudio Fontana     idx += 1;
7281fc33bb9SClaudio Fontana 
7291fc33bb9SClaudio Fontana     /* XMM control status register */
7301fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterXmmControlStatus);
7311fc33bb9SClaudio Fontana     env->mxcsr = vcxt.values[idx].XmmControlStatus.XmmStatusControl;
7321fc33bb9SClaudio Fontana     idx += 1;
7331fc33bb9SClaudio Fontana 
7341fc33bb9SClaudio Fontana     /* MSRs */
7351fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterEfer);
7361fc33bb9SClaudio Fontana     env->efer = vcxt.values[idx++].Reg64;
7371fc33bb9SClaudio Fontana #ifdef TARGET_X86_64
7381fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterKernelGsBase);
7391fc33bb9SClaudio Fontana     env->kernelgsbase = vcxt.values[idx++].Reg64;
7401fc33bb9SClaudio Fontana #endif
7411fc33bb9SClaudio Fontana 
7421fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterApicBase);
7431fc33bb9SClaudio Fontana     apic_base = vcxt.values[idx++].Reg64;
7441fc33bb9SClaudio Fontana     if (apic_base != vcpu->apic_base) {
7451fc33bb9SClaudio Fontana         vcpu->apic_base = apic_base;
7461fc33bb9SClaudio Fontana         cpu_set_apic_base(x86_cpu->apic_state, vcpu->apic_base);
7471fc33bb9SClaudio Fontana     }
7481fc33bb9SClaudio Fontana 
7491fc33bb9SClaudio Fontana     /* WHvX64RegisterPat - Skipped */
7501fc33bb9SClaudio Fontana 
7511fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterSysenterCs);
7521fc33bb9SClaudio Fontana     env->sysenter_cs = vcxt.values[idx++].Reg64;
7531fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterSysenterEip);
7541fc33bb9SClaudio Fontana     env->sysenter_eip = vcxt.values[idx++].Reg64;
7551fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterSysenterEsp);
7561fc33bb9SClaudio Fontana     env->sysenter_esp = vcxt.values[idx++].Reg64;
7571fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterStar);
7581fc33bb9SClaudio Fontana     env->star = vcxt.values[idx++].Reg64;
7591fc33bb9SClaudio Fontana #ifdef TARGET_X86_64
7601fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterLstar);
7611fc33bb9SClaudio Fontana     env->lstar = vcxt.values[idx++].Reg64;
7621fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterCstar);
7631fc33bb9SClaudio Fontana     env->cstar = vcxt.values[idx++].Reg64;
7641fc33bb9SClaudio Fontana     assert(whpx_register_names[idx] == WHvX64RegisterSfmask);
7651fc33bb9SClaudio Fontana     env->fmask = vcxt.values[idx++].Reg64;
7661fc33bb9SClaudio Fontana #endif
7671fc33bb9SClaudio Fontana 
7681fc33bb9SClaudio Fontana     /* Interrupt / Event Registers - Skipped */
7691fc33bb9SClaudio Fontana 
7701fc33bb9SClaudio Fontana     assert(idx == RTL_NUMBER_OF(whpx_register_names));
7711fc33bb9SClaudio Fontana 
7721fc33bb9SClaudio Fontana     if (whpx_apic_in_platform()) {
7731fc33bb9SClaudio Fontana         whpx_apic_get(x86_cpu->apic_state);
7741fc33bb9SClaudio Fontana     }
7751fc33bb9SClaudio Fontana 
776e5618908SIvan Shcherbakov     x86_update_hflags(env);
777e5618908SIvan Shcherbakov 
7781fc33bb9SClaudio Fontana     return;
7791fc33bb9SClaudio Fontana }
7801fc33bb9SClaudio Fontana 
7811fc33bb9SClaudio Fontana static HRESULT CALLBACK whpx_emu_ioport_callback(
7821fc33bb9SClaudio Fontana     void *ctx,
7831fc33bb9SClaudio Fontana     WHV_EMULATOR_IO_ACCESS_INFO *IoAccess)
7841fc33bb9SClaudio Fontana {
7851fc33bb9SClaudio Fontana     MemTxAttrs attrs = { 0 };
7861fc33bb9SClaudio Fontana     address_space_rw(&address_space_io, IoAccess->Port, attrs,
7871fc33bb9SClaudio Fontana                      &IoAccess->Data, IoAccess->AccessSize,
7881fc33bb9SClaudio Fontana                      IoAccess->Direction);
7891fc33bb9SClaudio Fontana     return S_OK;
7901fc33bb9SClaudio Fontana }
7911fc33bb9SClaudio Fontana 
7921fc33bb9SClaudio Fontana static HRESULT CALLBACK whpx_emu_mmio_callback(
7931fc33bb9SClaudio Fontana     void *ctx,
7941fc33bb9SClaudio Fontana     WHV_EMULATOR_MEMORY_ACCESS_INFO *ma)
7951fc33bb9SClaudio Fontana {
7961fc33bb9SClaudio Fontana     cpu_physical_memory_rw(ma->GpaAddress, ma->Data, ma->AccessSize,
7971fc33bb9SClaudio Fontana                            ma->Direction);
7981fc33bb9SClaudio Fontana     return S_OK;
7991fc33bb9SClaudio Fontana }
8001fc33bb9SClaudio Fontana 
8011fc33bb9SClaudio Fontana static HRESULT CALLBACK whpx_emu_getreg_callback(
8021fc33bb9SClaudio Fontana     void *ctx,
8031fc33bb9SClaudio Fontana     const WHV_REGISTER_NAME *RegisterNames,
8041fc33bb9SClaudio Fontana     UINT32 RegisterCount,
8051fc33bb9SClaudio Fontana     WHV_REGISTER_VALUE *RegisterValues)
8061fc33bb9SClaudio Fontana {
8071fc33bb9SClaudio Fontana     HRESULT hr;
8081fc33bb9SClaudio Fontana     struct whpx_state *whpx = &whpx_global;
8091fc33bb9SClaudio Fontana     CPUState *cpu = (CPUState *)ctx;
8101fc33bb9SClaudio Fontana 
8111fc33bb9SClaudio Fontana     hr = whp_dispatch.WHvGetVirtualProcessorRegisters(
8121fc33bb9SClaudio Fontana         whpx->partition, cpu->cpu_index,
8131fc33bb9SClaudio Fontana         RegisterNames, RegisterCount,
8141fc33bb9SClaudio Fontana         RegisterValues);
8151fc33bb9SClaudio Fontana     if (FAILED(hr)) {
8161fc33bb9SClaudio Fontana         error_report("WHPX: Failed to get virtual processor registers,"
8171fc33bb9SClaudio Fontana                      " hr=%08lx", hr);
8181fc33bb9SClaudio Fontana     }
8191fc33bb9SClaudio Fontana 
8201fc33bb9SClaudio Fontana     return hr;
8211fc33bb9SClaudio Fontana }
8221fc33bb9SClaudio Fontana 
8231fc33bb9SClaudio Fontana static HRESULT CALLBACK whpx_emu_setreg_callback(
8241fc33bb9SClaudio Fontana     void *ctx,
8251fc33bb9SClaudio Fontana     const WHV_REGISTER_NAME *RegisterNames,
8261fc33bb9SClaudio Fontana     UINT32 RegisterCount,
8271fc33bb9SClaudio Fontana     const WHV_REGISTER_VALUE *RegisterValues)
8281fc33bb9SClaudio Fontana {
8291fc33bb9SClaudio Fontana     HRESULT hr;
8301fc33bb9SClaudio Fontana     struct whpx_state *whpx = &whpx_global;
8311fc33bb9SClaudio Fontana     CPUState *cpu = (CPUState *)ctx;
8321fc33bb9SClaudio Fontana 
8331fc33bb9SClaudio Fontana     hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
8341fc33bb9SClaudio Fontana         whpx->partition, cpu->cpu_index,
8351fc33bb9SClaudio Fontana         RegisterNames, RegisterCount,
8361fc33bb9SClaudio Fontana         RegisterValues);
8371fc33bb9SClaudio Fontana     if (FAILED(hr)) {
8381fc33bb9SClaudio Fontana         error_report("WHPX: Failed to set virtual processor registers,"
8391fc33bb9SClaudio Fontana                      " hr=%08lx", hr);
8401fc33bb9SClaudio Fontana     }
8411fc33bb9SClaudio Fontana 
8421fc33bb9SClaudio Fontana     /*
8431fc33bb9SClaudio Fontana      * The emulator just successfully wrote the register state. We clear the
8441fc33bb9SClaudio Fontana      * dirty state so we avoid the double write on resume of the VP.
8451fc33bb9SClaudio Fontana      */
8461fc33bb9SClaudio Fontana     cpu->vcpu_dirty = false;
8471fc33bb9SClaudio Fontana 
8481fc33bb9SClaudio Fontana     return hr;
8491fc33bb9SClaudio Fontana }
8501fc33bb9SClaudio Fontana 
8511fc33bb9SClaudio Fontana static HRESULT CALLBACK whpx_emu_translate_callback(
8521fc33bb9SClaudio Fontana     void *ctx,
8531fc33bb9SClaudio Fontana     WHV_GUEST_VIRTUAL_ADDRESS Gva,
8541fc33bb9SClaudio Fontana     WHV_TRANSLATE_GVA_FLAGS TranslateFlags,
8551fc33bb9SClaudio Fontana     WHV_TRANSLATE_GVA_RESULT_CODE *TranslationResult,
8561fc33bb9SClaudio Fontana     WHV_GUEST_PHYSICAL_ADDRESS *Gpa)
8571fc33bb9SClaudio Fontana {
8581fc33bb9SClaudio Fontana     HRESULT hr;
8591fc33bb9SClaudio Fontana     struct whpx_state *whpx = &whpx_global;
8601fc33bb9SClaudio Fontana     CPUState *cpu = (CPUState *)ctx;
8611fc33bb9SClaudio Fontana     WHV_TRANSLATE_GVA_RESULT res;
8621fc33bb9SClaudio Fontana 
8631fc33bb9SClaudio Fontana     hr = whp_dispatch.WHvTranslateGva(whpx->partition, cpu->cpu_index,
8641fc33bb9SClaudio Fontana                                       Gva, TranslateFlags, &res, Gpa);
8651fc33bb9SClaudio Fontana     if (FAILED(hr)) {
8661fc33bb9SClaudio Fontana         error_report("WHPX: Failed to translate GVA, hr=%08lx", hr);
8671fc33bb9SClaudio Fontana     } else {
8681fc33bb9SClaudio Fontana         *TranslationResult = res.ResultCode;
8691fc33bb9SClaudio Fontana     }
8701fc33bb9SClaudio Fontana 
8711fc33bb9SClaudio Fontana     return hr;
8721fc33bb9SClaudio Fontana }
8731fc33bb9SClaudio Fontana 
8741fc33bb9SClaudio Fontana static const WHV_EMULATOR_CALLBACKS whpx_emu_callbacks = {
8751fc33bb9SClaudio Fontana     .Size = sizeof(WHV_EMULATOR_CALLBACKS),
8761fc33bb9SClaudio Fontana     .WHvEmulatorIoPortCallback = whpx_emu_ioport_callback,
8771fc33bb9SClaudio Fontana     .WHvEmulatorMemoryCallback = whpx_emu_mmio_callback,
8781fc33bb9SClaudio Fontana     .WHvEmulatorGetVirtualProcessorRegisters = whpx_emu_getreg_callback,
8791fc33bb9SClaudio Fontana     .WHvEmulatorSetVirtualProcessorRegisters = whpx_emu_setreg_callback,
8801fc33bb9SClaudio Fontana     .WHvEmulatorTranslateGvaPage = whpx_emu_translate_callback,
8811fc33bb9SClaudio Fontana };
8821fc33bb9SClaudio Fontana 
8831fc33bb9SClaudio Fontana static int whpx_handle_mmio(CPUState *cpu, WHV_MEMORY_ACCESS_CONTEXT *ctx)
8841fc33bb9SClaudio Fontana {
8851fc33bb9SClaudio Fontana     HRESULT hr;
886441f2449SPhilippe Mathieu-Daudé     AccelCPUState *vcpu = cpu->accel;
8871fc33bb9SClaudio Fontana     WHV_EMULATOR_STATUS emu_status;
8881fc33bb9SClaudio Fontana 
8891fc33bb9SClaudio Fontana     hr = whp_dispatch.WHvEmulatorTryMmioEmulation(
8901fc33bb9SClaudio Fontana         vcpu->emulator, cpu,
8911fc33bb9SClaudio Fontana         &vcpu->exit_ctx.VpContext, ctx,
8921fc33bb9SClaudio Fontana         &emu_status);
8931fc33bb9SClaudio Fontana     if (FAILED(hr)) {
8941fc33bb9SClaudio Fontana         error_report("WHPX: Failed to parse MMIO access, hr=%08lx", hr);
8951fc33bb9SClaudio Fontana         return -1;
8961fc33bb9SClaudio Fontana     }
8971fc33bb9SClaudio Fontana 
8981fc33bb9SClaudio Fontana     if (!emu_status.EmulationSuccessful) {
8991fc33bb9SClaudio Fontana         error_report("WHPX: Failed to emulate MMIO access with"
9001fc33bb9SClaudio Fontana                      " EmulatorReturnStatus: %u", emu_status.AsUINT32);
9011fc33bb9SClaudio Fontana         return -1;
9021fc33bb9SClaudio Fontana     }
9031fc33bb9SClaudio Fontana 
9041fc33bb9SClaudio Fontana     return 0;
9051fc33bb9SClaudio Fontana }
9061fc33bb9SClaudio Fontana 
9071fc33bb9SClaudio Fontana static int whpx_handle_portio(CPUState *cpu,
9081fc33bb9SClaudio Fontana                               WHV_X64_IO_PORT_ACCESS_CONTEXT *ctx)
9091fc33bb9SClaudio Fontana {
9101fc33bb9SClaudio Fontana     HRESULT hr;
911441f2449SPhilippe Mathieu-Daudé     AccelCPUState *vcpu = cpu->accel;
9121fc33bb9SClaudio Fontana     WHV_EMULATOR_STATUS emu_status;
9131fc33bb9SClaudio Fontana 
9141fc33bb9SClaudio Fontana     hr = whp_dispatch.WHvEmulatorTryIoEmulation(
9151fc33bb9SClaudio Fontana         vcpu->emulator, cpu,
9161fc33bb9SClaudio Fontana         &vcpu->exit_ctx.VpContext, ctx,
9171fc33bb9SClaudio Fontana         &emu_status);
9181fc33bb9SClaudio Fontana     if (FAILED(hr)) {
9191fc33bb9SClaudio Fontana         error_report("WHPX: Failed to parse PortIO access, hr=%08lx", hr);
9201fc33bb9SClaudio Fontana         return -1;
9211fc33bb9SClaudio Fontana     }
9221fc33bb9SClaudio Fontana 
9231fc33bb9SClaudio Fontana     if (!emu_status.EmulationSuccessful) {
9241fc33bb9SClaudio Fontana         error_report("WHPX: Failed to emulate PortIO access with"
9251fc33bb9SClaudio Fontana                      " EmulatorReturnStatus: %u", emu_status.AsUINT32);
9261fc33bb9SClaudio Fontana         return -1;
9271fc33bb9SClaudio Fontana     }
9281fc33bb9SClaudio Fontana 
9291fc33bb9SClaudio Fontana     return 0;
9301fc33bb9SClaudio Fontana }
9311fc33bb9SClaudio Fontana 
932d7482ffeSIvan Shcherbakov /*
933d7482ffeSIvan Shcherbakov  * Controls whether we should intercept various exceptions on the guest,
934d7482ffeSIvan Shcherbakov  * namely breakpoint/single-step events.
935d7482ffeSIvan Shcherbakov  *
936d7482ffeSIvan Shcherbakov  * The 'exceptions' argument accepts a bitmask, e.g:
937d7482ffeSIvan Shcherbakov  * (1 << WHvX64ExceptionTypeDebugTrapOrFault) | (...)
938d7482ffeSIvan Shcherbakov  */
939d7482ffeSIvan Shcherbakov static HRESULT whpx_set_exception_exit_bitmap(UINT64 exceptions)
940d7482ffeSIvan Shcherbakov {
941d7482ffeSIvan Shcherbakov     struct whpx_state *whpx = &whpx_global;
942d7482ffeSIvan Shcherbakov     WHV_PARTITION_PROPERTY prop = { 0, };
943d7482ffeSIvan Shcherbakov     HRESULT hr;
944d7482ffeSIvan Shcherbakov 
945d7482ffeSIvan Shcherbakov     if (exceptions == whpx->exception_exit_bitmap) {
946d7482ffeSIvan Shcherbakov         return S_OK;
947d7482ffeSIvan Shcherbakov     }
948d7482ffeSIvan Shcherbakov 
949d7482ffeSIvan Shcherbakov     prop.ExceptionExitBitmap = exceptions;
950d7482ffeSIvan Shcherbakov 
951d7482ffeSIvan Shcherbakov     hr = whp_dispatch.WHvSetPartitionProperty(
952d7482ffeSIvan Shcherbakov         whpx->partition,
953d7482ffeSIvan Shcherbakov         WHvPartitionPropertyCodeExceptionExitBitmap,
954d7482ffeSIvan Shcherbakov         &prop,
955d7482ffeSIvan Shcherbakov         sizeof(WHV_PARTITION_PROPERTY));
956d7482ffeSIvan Shcherbakov 
957d7482ffeSIvan Shcherbakov     if (SUCCEEDED(hr)) {
958d7482ffeSIvan Shcherbakov         whpx->exception_exit_bitmap = exceptions;
959d7482ffeSIvan Shcherbakov     }
960d7482ffeSIvan Shcherbakov 
961d7482ffeSIvan Shcherbakov     return hr;
962d7482ffeSIvan Shcherbakov }
963d7482ffeSIvan Shcherbakov 
964d7482ffeSIvan Shcherbakov 
965d7482ffeSIvan Shcherbakov /*
966d7482ffeSIvan Shcherbakov  * This function is called before/after stepping over a single instruction.
967d7482ffeSIvan Shcherbakov  * It will update the CPU registers to arm/disarm the instruction stepping
968d7482ffeSIvan Shcherbakov  * accordingly.
969d7482ffeSIvan Shcherbakov  */
970d7482ffeSIvan Shcherbakov static HRESULT whpx_vcpu_configure_single_stepping(CPUState *cpu,
971d7482ffeSIvan Shcherbakov     bool set,
972d7482ffeSIvan Shcherbakov     uint64_t *exit_context_rflags)
973d7482ffeSIvan Shcherbakov {
974d7482ffeSIvan Shcherbakov     WHV_REGISTER_NAME reg_name;
975d7482ffeSIvan Shcherbakov     WHV_REGISTER_VALUE reg_value;
976d7482ffeSIvan Shcherbakov     HRESULT hr;
977d7482ffeSIvan Shcherbakov     struct whpx_state *whpx = &whpx_global;
978d7482ffeSIvan Shcherbakov 
979d7482ffeSIvan Shcherbakov     /*
980d7482ffeSIvan Shcherbakov      * If we are trying to step over a single instruction, we need to set the
981d7482ffeSIvan Shcherbakov      * TF bit in rflags. Otherwise, clear it.
982d7482ffeSIvan Shcherbakov      */
983d7482ffeSIvan Shcherbakov     reg_name = WHvX64RegisterRflags;
984d7482ffeSIvan Shcherbakov     hr = whp_dispatch.WHvGetVirtualProcessorRegisters(
985d7482ffeSIvan Shcherbakov         whpx->partition,
986d7482ffeSIvan Shcherbakov         cpu->cpu_index,
987d7482ffeSIvan Shcherbakov         &reg_name,
988d7482ffeSIvan Shcherbakov         1,
989d7482ffeSIvan Shcherbakov         &reg_value);
990d7482ffeSIvan Shcherbakov 
991d7482ffeSIvan Shcherbakov     if (FAILED(hr)) {
992d7482ffeSIvan Shcherbakov         error_report("WHPX: Failed to get rflags, hr=%08lx", hr);
993d7482ffeSIvan Shcherbakov         return hr;
994d7482ffeSIvan Shcherbakov     }
995d7482ffeSIvan Shcherbakov 
996d7482ffeSIvan Shcherbakov     if (exit_context_rflags) {
997d7482ffeSIvan Shcherbakov         assert(*exit_context_rflags == reg_value.Reg64);
998d7482ffeSIvan Shcherbakov     }
999d7482ffeSIvan Shcherbakov 
1000d7482ffeSIvan Shcherbakov     if (set) {
1001d7482ffeSIvan Shcherbakov         /* Raise WHvX64ExceptionTypeDebugTrapOrFault after each instruction */
1002d7482ffeSIvan Shcherbakov         reg_value.Reg64 |= TF_MASK;
1003d7482ffeSIvan Shcherbakov     } else {
1004d7482ffeSIvan Shcherbakov         reg_value.Reg64 &= ~TF_MASK;
1005d7482ffeSIvan Shcherbakov     }
1006d7482ffeSIvan Shcherbakov 
1007d7482ffeSIvan Shcherbakov     if (exit_context_rflags) {
1008d7482ffeSIvan Shcherbakov         *exit_context_rflags = reg_value.Reg64;
1009d7482ffeSIvan Shcherbakov     }
1010d7482ffeSIvan Shcherbakov 
1011d7482ffeSIvan Shcherbakov     hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
1012d7482ffeSIvan Shcherbakov         whpx->partition,
1013d7482ffeSIvan Shcherbakov         cpu->cpu_index,
1014d7482ffeSIvan Shcherbakov         &reg_name,
1015d7482ffeSIvan Shcherbakov         1,
1016d7482ffeSIvan Shcherbakov         &reg_value);
1017d7482ffeSIvan Shcherbakov 
1018d7482ffeSIvan Shcherbakov     if (FAILED(hr)) {
1019d7482ffeSIvan Shcherbakov         error_report("WHPX: Failed to set rflags,"
1020d7482ffeSIvan Shcherbakov             " hr=%08lx",
1021d7482ffeSIvan Shcherbakov             hr);
1022d7482ffeSIvan Shcherbakov         return hr;
1023d7482ffeSIvan Shcherbakov     }
1024d7482ffeSIvan Shcherbakov 
1025d7482ffeSIvan Shcherbakov     reg_name = WHvRegisterInterruptState;
1026d7482ffeSIvan Shcherbakov     reg_value.Reg64 = 0;
1027d7482ffeSIvan Shcherbakov 
1028d7482ffeSIvan Shcherbakov     /* Suspend delivery of hardware interrupts during single-stepping. */
1029d7482ffeSIvan Shcherbakov     reg_value.InterruptState.InterruptShadow = set != 0;
1030d7482ffeSIvan Shcherbakov 
1031d7482ffeSIvan Shcherbakov     hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
1032d7482ffeSIvan Shcherbakov     whpx->partition,
1033d7482ffeSIvan Shcherbakov         cpu->cpu_index,
1034d7482ffeSIvan Shcherbakov         &reg_name,
1035d7482ffeSIvan Shcherbakov         1,
1036d7482ffeSIvan Shcherbakov         &reg_value);
1037d7482ffeSIvan Shcherbakov 
1038d7482ffeSIvan Shcherbakov     if (FAILED(hr)) {
1039d7482ffeSIvan Shcherbakov         error_report("WHPX: Failed to set InterruptState,"
1040d7482ffeSIvan Shcherbakov             " hr=%08lx",
1041d7482ffeSIvan Shcherbakov             hr);
1042d7482ffeSIvan Shcherbakov         return hr;
1043d7482ffeSIvan Shcherbakov     }
1044d7482ffeSIvan Shcherbakov 
1045d7482ffeSIvan Shcherbakov     if (!set) {
1046d7482ffeSIvan Shcherbakov         /*
1047d7482ffeSIvan Shcherbakov          * We have just finished stepping over a single instruction,
1048d7482ffeSIvan Shcherbakov          * and intercepted the INT1 generated by it.
1049d7482ffeSIvan Shcherbakov          * We need to now hide the INT1 from the guest,
1050d7482ffeSIvan Shcherbakov          * as it would not be expecting it.
1051d7482ffeSIvan Shcherbakov          */
1052d7482ffeSIvan Shcherbakov 
1053d7482ffeSIvan Shcherbakov         reg_name = WHvX64RegisterPendingDebugException;
1054d7482ffeSIvan Shcherbakov         hr = whp_dispatch.WHvGetVirtualProcessorRegisters(
1055d7482ffeSIvan Shcherbakov         whpx->partition,
1056d7482ffeSIvan Shcherbakov             cpu->cpu_index,
1057d7482ffeSIvan Shcherbakov             &reg_name,
1058d7482ffeSIvan Shcherbakov             1,
1059d7482ffeSIvan Shcherbakov             &reg_value);
1060d7482ffeSIvan Shcherbakov 
1061d7482ffeSIvan Shcherbakov         if (FAILED(hr)) {
1062d7482ffeSIvan Shcherbakov             error_report("WHPX: Failed to get pending debug exceptions,"
1063d7482ffeSIvan Shcherbakov                          "hr=%08lx", hr);
1064d7482ffeSIvan Shcherbakov             return hr;
1065d7482ffeSIvan Shcherbakov         }
1066d7482ffeSIvan Shcherbakov 
1067d7482ffeSIvan Shcherbakov         if (reg_value.PendingDebugException.SingleStep) {
1068d7482ffeSIvan Shcherbakov             reg_value.PendingDebugException.SingleStep = 0;
1069d7482ffeSIvan Shcherbakov 
1070d7482ffeSIvan Shcherbakov             hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
1071d7482ffeSIvan Shcherbakov                 whpx->partition,
1072d7482ffeSIvan Shcherbakov                 cpu->cpu_index,
1073d7482ffeSIvan Shcherbakov                 &reg_name,
1074d7482ffeSIvan Shcherbakov                 1,
1075d7482ffeSIvan Shcherbakov                 &reg_value);
1076d7482ffeSIvan Shcherbakov 
1077d7482ffeSIvan Shcherbakov             if (FAILED(hr)) {
1078d7482ffeSIvan Shcherbakov                 error_report("WHPX: Failed to clear pending debug exceptions,"
1079d7482ffeSIvan Shcherbakov                              "hr=%08lx", hr);
1080d7482ffeSIvan Shcherbakov              return hr;
1081d7482ffeSIvan Shcherbakov             }
1082d7482ffeSIvan Shcherbakov         }
1083d7482ffeSIvan Shcherbakov 
1084d7482ffeSIvan Shcherbakov     }
1085d7482ffeSIvan Shcherbakov 
1086d7482ffeSIvan Shcherbakov     return S_OK;
1087d7482ffeSIvan Shcherbakov }
1088d7482ffeSIvan Shcherbakov 
1089d7482ffeSIvan Shcherbakov /* Tries to find a breakpoint at the specified address. */
1090d7482ffeSIvan Shcherbakov static struct whpx_breakpoint *whpx_lookup_breakpoint_by_addr(uint64_t address)
1091d7482ffeSIvan Shcherbakov {
1092d7482ffeSIvan Shcherbakov     struct whpx_state *whpx = &whpx_global;
1093d7482ffeSIvan Shcherbakov     int i;
1094d7482ffeSIvan Shcherbakov 
1095d7482ffeSIvan Shcherbakov     if (whpx->breakpoints.breakpoints) {
1096d7482ffeSIvan Shcherbakov         for (i = 0; i < whpx->breakpoints.breakpoints->used; i++) {
1097d7482ffeSIvan Shcherbakov             if (address == whpx->breakpoints.breakpoints->data[i].address) {
1098d7482ffeSIvan Shcherbakov                 return &whpx->breakpoints.breakpoints->data[i];
1099d7482ffeSIvan Shcherbakov             }
1100d7482ffeSIvan Shcherbakov         }
1101d7482ffeSIvan Shcherbakov     }
1102d7482ffeSIvan Shcherbakov 
1103d7482ffeSIvan Shcherbakov     return NULL;
1104d7482ffeSIvan Shcherbakov }
1105d7482ffeSIvan Shcherbakov 
1106d7482ffeSIvan Shcherbakov /*
1107d7482ffeSIvan Shcherbakov  * Linux uses int3 (0xCC) during startup (see int3_selftest()) and for
1108d7482ffeSIvan Shcherbakov  * debugging user-mode applications. Since the WHPX API does not offer
1109d7482ffeSIvan Shcherbakov  * an easy way to pass the intercepted exception back to the guest, we
1110d7482ffeSIvan Shcherbakov  * resort to using INT1 instead, and let the guest always handle INT3.
1111d7482ffeSIvan Shcherbakov  */
1112d7482ffeSIvan Shcherbakov static const uint8_t whpx_breakpoint_instruction = 0xF1;
1113d7482ffeSIvan Shcherbakov 
1114d7482ffeSIvan Shcherbakov /*
1115d7482ffeSIvan Shcherbakov  * The WHPX QEMU backend implements breakpoints by writing the INT1
1116d7482ffeSIvan Shcherbakov  * instruction into memory (ignoring the DRx registers). This raises a few
1117d7482ffeSIvan Shcherbakov  * issues that need to be carefully handled:
1118d7482ffeSIvan Shcherbakov  *
1119d7482ffeSIvan Shcherbakov  * 1. Although unlikely, other parts of QEMU may set multiple breakpoints
1120d7482ffeSIvan Shcherbakov  *    at the same location, and later remove them in arbitrary order.
1121d7482ffeSIvan Shcherbakov  *    This should not cause memory corruption, and should only remove the
1122d7482ffeSIvan Shcherbakov  *    physical breakpoint instruction when the last QEMU breakpoint is gone.
1123d7482ffeSIvan Shcherbakov  *
1124d7482ffeSIvan Shcherbakov  * 2. Writing arbitrary virtual memory may fail if it's not mapped to a valid
1125d7482ffeSIvan Shcherbakov  *    physical location. Hence, physically adding/removing a breakpoint can
1126d7482ffeSIvan Shcherbakov  *    theoretically fail at any time. We need to keep track of it.
1127d7482ffeSIvan Shcherbakov  *
1128d7482ffeSIvan Shcherbakov  * The function below rebuilds a list of low-level breakpoints (one per
1129d7482ffeSIvan Shcherbakov  * address, tracking the original instruction and any errors) from the list of
1130d7482ffeSIvan Shcherbakov  * high-level breakpoints (set via cpu_breakpoint_insert()).
1131d7482ffeSIvan Shcherbakov  *
1132d7482ffeSIvan Shcherbakov  * In order to optimize performance, this function stores the list of
1133d7482ffeSIvan Shcherbakov  * high-level breakpoints (a.k.a. CPU breakpoints) used to compute the
1134d7482ffeSIvan Shcherbakov  * low-level ones, so that it won't be re-invoked until these breakpoints
1135d7482ffeSIvan Shcherbakov  * change.
1136d7482ffeSIvan Shcherbakov  *
1137d7482ffeSIvan Shcherbakov  * Note that this function decides which breakpoints should be inserted into,
1138d7482ffeSIvan Shcherbakov  * memory, but doesn't actually do it. The memory accessing is done in
1139d7482ffeSIvan Shcherbakov  * whpx_apply_breakpoints().
1140d7482ffeSIvan Shcherbakov  */
1141d7482ffeSIvan Shcherbakov static void whpx_translate_cpu_breakpoints(
1142d7482ffeSIvan Shcherbakov     struct whpx_breakpoints *breakpoints,
1143d7482ffeSIvan Shcherbakov     CPUState *cpu,
1144d7482ffeSIvan Shcherbakov     int cpu_breakpoint_count)
1145d7482ffeSIvan Shcherbakov {
1146d7482ffeSIvan Shcherbakov     CPUBreakpoint *bp;
1147d7482ffeSIvan Shcherbakov     int cpu_bp_index = 0;
1148d7482ffeSIvan Shcherbakov 
1149d7482ffeSIvan Shcherbakov     breakpoints->original_addresses =
1150d7482ffeSIvan Shcherbakov         g_renew(vaddr, breakpoints->original_addresses, cpu_breakpoint_count);
1151d7482ffeSIvan Shcherbakov 
1152d7482ffeSIvan Shcherbakov     breakpoints->original_address_count = cpu_breakpoint_count;
1153d7482ffeSIvan Shcherbakov 
1154d7482ffeSIvan Shcherbakov     int max_breakpoints = cpu_breakpoint_count +
1155d7482ffeSIvan Shcherbakov         (breakpoints->breakpoints ? breakpoints->breakpoints->used : 0);
1156d7482ffeSIvan Shcherbakov 
1157d7482ffeSIvan Shcherbakov     struct whpx_breakpoint_collection *new_breakpoints =
11580a553c12SMarkus Armbruster         g_malloc0(sizeof(struct whpx_breakpoint_collection)
11590a553c12SMarkus Armbruster                   + max_breakpoints * sizeof(struct whpx_breakpoint));
1160d7482ffeSIvan Shcherbakov 
1161d7482ffeSIvan Shcherbakov     new_breakpoints->allocated = max_breakpoints;
1162d7482ffeSIvan Shcherbakov     new_breakpoints->used = 0;
1163d7482ffeSIvan Shcherbakov 
1164d7482ffeSIvan Shcherbakov     /*
1165d7482ffeSIvan Shcherbakov      * 1. Preserve all old breakpoints that could not be automatically
1166d7482ffeSIvan Shcherbakov      * cleared when the CPU got stopped.
1167d7482ffeSIvan Shcherbakov      */
1168d7482ffeSIvan Shcherbakov     if (breakpoints->breakpoints) {
1169d7482ffeSIvan Shcherbakov         int i;
1170d7482ffeSIvan Shcherbakov         for (i = 0; i < breakpoints->breakpoints->used; i++) {
1171d7482ffeSIvan Shcherbakov             if (breakpoints->breakpoints->data[i].state != WHPX_BP_CLEARED) {
1172d7482ffeSIvan Shcherbakov                 new_breakpoints->data[new_breakpoints->used++] =
1173d7482ffeSIvan Shcherbakov                     breakpoints->breakpoints->data[i];
1174d7482ffeSIvan Shcherbakov             }
1175d7482ffeSIvan Shcherbakov         }
1176d7482ffeSIvan Shcherbakov     }
1177d7482ffeSIvan Shcherbakov 
1178d7482ffeSIvan Shcherbakov     /* 2. Map all CPU breakpoints to WHPX breakpoints */
1179d7482ffeSIvan Shcherbakov     QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
1180d7482ffeSIvan Shcherbakov         int i;
1181d7482ffeSIvan Shcherbakov         bool found = false;
1182d7482ffeSIvan Shcherbakov 
1183d7482ffeSIvan Shcherbakov         /* This will be used to detect changed CPU breakpoints later. */
1184d7482ffeSIvan Shcherbakov         breakpoints->original_addresses[cpu_bp_index++] = bp->pc;
1185d7482ffeSIvan Shcherbakov 
1186d7482ffeSIvan Shcherbakov         for (i = 0; i < new_breakpoints->used; i++) {
1187d7482ffeSIvan Shcherbakov             /*
1188d7482ffeSIvan Shcherbakov              * WARNING: This loop has O(N^2) complexity, where N is the
1189d7482ffeSIvan Shcherbakov              * number of breakpoints. It should not be a bottleneck in
1190d7482ffeSIvan Shcherbakov              * real-world scenarios, since it only needs to run once after
1191d7482ffeSIvan Shcherbakov              * the breakpoints have been modified.
1192d7482ffeSIvan Shcherbakov              * If this ever becomes a concern, it can be optimized by storing
1193d7482ffeSIvan Shcherbakov              * high-level breakpoint objects in a tree or hash map.
1194d7482ffeSIvan Shcherbakov              */
1195d7482ffeSIvan Shcherbakov 
1196d7482ffeSIvan Shcherbakov             if (new_breakpoints->data[i].address == bp->pc) {
1197d7482ffeSIvan Shcherbakov                 /* There was already a breakpoint at this address. */
1198d7482ffeSIvan Shcherbakov                 if (new_breakpoints->data[i].state == WHPX_BP_CLEAR_PENDING) {
1199d7482ffeSIvan Shcherbakov                     new_breakpoints->data[i].state = WHPX_BP_SET;
1200d7482ffeSIvan Shcherbakov                 } else if (new_breakpoints->data[i].state == WHPX_BP_SET) {
1201d7482ffeSIvan Shcherbakov                     new_breakpoints->data[i].state = WHPX_BP_SET_PENDING;
1202d7482ffeSIvan Shcherbakov                 }
1203d7482ffeSIvan Shcherbakov 
1204d7482ffeSIvan Shcherbakov                 found = true;
1205d7482ffeSIvan Shcherbakov                 break;
1206d7482ffeSIvan Shcherbakov             }
1207d7482ffeSIvan Shcherbakov         }
1208d7482ffeSIvan Shcherbakov 
1209d7482ffeSIvan Shcherbakov         if (!found && new_breakpoints->used < new_breakpoints->allocated) {
1210d7482ffeSIvan Shcherbakov             /* No WHPX breakpoint at this address. Create one. */
1211d7482ffeSIvan Shcherbakov             new_breakpoints->data[new_breakpoints->used].address = bp->pc;
1212d7482ffeSIvan Shcherbakov             new_breakpoints->data[new_breakpoints->used].state =
1213d7482ffeSIvan Shcherbakov                 WHPX_BP_SET_PENDING;
1214d7482ffeSIvan Shcherbakov             new_breakpoints->used++;
1215d7482ffeSIvan Shcherbakov         }
1216d7482ffeSIvan Shcherbakov     }
1217d7482ffeSIvan Shcherbakov 
1218d7482ffeSIvan Shcherbakov     /*
1219d7482ffeSIvan Shcherbakov      * Free the previous breakpoint list. This can be optimized by keeping
1220d7482ffeSIvan Shcherbakov      * it as shadow buffer for the next computation instead of freeing
1221d7482ffeSIvan Shcherbakov      * it immediately.
1222d7482ffeSIvan Shcherbakov      */
1223d7482ffeSIvan Shcherbakov     g_free(breakpoints->breakpoints);
1224d7482ffeSIvan Shcherbakov 
1225d7482ffeSIvan Shcherbakov     breakpoints->breakpoints = new_breakpoints;
1226d7482ffeSIvan Shcherbakov }
1227d7482ffeSIvan Shcherbakov 
1228d7482ffeSIvan Shcherbakov /*
1229d7482ffeSIvan Shcherbakov  * Physically inserts/removes the breakpoints by reading and writing the
1230d7482ffeSIvan Shcherbakov  * physical memory, keeping a track of the failed attempts.
1231d7482ffeSIvan Shcherbakov  *
1232d7482ffeSIvan Shcherbakov  * Passing resuming=true  will try to set all previously unset breakpoints.
1233d7482ffeSIvan Shcherbakov  * Passing resuming=false will remove all inserted ones.
1234d7482ffeSIvan Shcherbakov  */
1235d7482ffeSIvan Shcherbakov static void whpx_apply_breakpoints(
1236d7482ffeSIvan Shcherbakov     struct whpx_breakpoint_collection *breakpoints,
1237d7482ffeSIvan Shcherbakov     CPUState *cpu,
1238d7482ffeSIvan Shcherbakov     bool resuming)
1239d7482ffeSIvan Shcherbakov {
1240d7482ffeSIvan Shcherbakov     int i, rc;
1241d7482ffeSIvan Shcherbakov     if (!breakpoints) {
1242d7482ffeSIvan Shcherbakov         return;
1243d7482ffeSIvan Shcherbakov     }
1244d7482ffeSIvan Shcherbakov 
1245d7482ffeSIvan Shcherbakov     for (i = 0; i < breakpoints->used; i++) {
1246d7482ffeSIvan Shcherbakov         /* Decide what to do right now based on the last known state. */
1247d7482ffeSIvan Shcherbakov         WhpxBreakpointState state = breakpoints->data[i].state;
1248d7482ffeSIvan Shcherbakov         switch (state) {
1249d7482ffeSIvan Shcherbakov         case WHPX_BP_CLEARED:
1250d7482ffeSIvan Shcherbakov             if (resuming) {
1251d7482ffeSIvan Shcherbakov                 state = WHPX_BP_SET_PENDING;
1252d7482ffeSIvan Shcherbakov             }
1253d7482ffeSIvan Shcherbakov             break;
1254d7482ffeSIvan Shcherbakov         case WHPX_BP_SET_PENDING:
1255d7482ffeSIvan Shcherbakov             if (!resuming) {
1256d7482ffeSIvan Shcherbakov                 state = WHPX_BP_CLEARED;
1257d7482ffeSIvan Shcherbakov             }
1258d7482ffeSIvan Shcherbakov             break;
1259d7482ffeSIvan Shcherbakov         case WHPX_BP_SET:
1260d7482ffeSIvan Shcherbakov             if (!resuming) {
1261d7482ffeSIvan Shcherbakov                 state = WHPX_BP_CLEAR_PENDING;
1262d7482ffeSIvan Shcherbakov             }
1263d7482ffeSIvan Shcherbakov             break;
1264d7482ffeSIvan Shcherbakov         case WHPX_BP_CLEAR_PENDING:
1265d7482ffeSIvan Shcherbakov             if (resuming) {
1266d7482ffeSIvan Shcherbakov                 state = WHPX_BP_SET;
1267d7482ffeSIvan Shcherbakov             }
1268d7482ffeSIvan Shcherbakov             break;
1269d7482ffeSIvan Shcherbakov         }
1270d7482ffeSIvan Shcherbakov 
1271d7482ffeSIvan Shcherbakov         if (state == WHPX_BP_SET_PENDING) {
1272d7482ffeSIvan Shcherbakov             /* Remember the original instruction. */
1273d7482ffeSIvan Shcherbakov             rc = cpu_memory_rw_debug(cpu,
1274d7482ffeSIvan Shcherbakov                 breakpoints->data[i].address,
1275d7482ffeSIvan Shcherbakov                 &breakpoints->data[i].original_instruction,
1276d7482ffeSIvan Shcherbakov                 1,
1277d7482ffeSIvan Shcherbakov                 false);
1278d7482ffeSIvan Shcherbakov 
1279d7482ffeSIvan Shcherbakov             if (!rc) {
1280d7482ffeSIvan Shcherbakov                 /* Write the breakpoint instruction. */
1281d7482ffeSIvan Shcherbakov                 rc = cpu_memory_rw_debug(cpu,
1282d7482ffeSIvan Shcherbakov                     breakpoints->data[i].address,
1283d7482ffeSIvan Shcherbakov                     (void *)&whpx_breakpoint_instruction,
1284d7482ffeSIvan Shcherbakov                     1,
1285d7482ffeSIvan Shcherbakov                     true);
1286d7482ffeSIvan Shcherbakov             }
1287d7482ffeSIvan Shcherbakov 
1288d7482ffeSIvan Shcherbakov             if (!rc) {
1289d7482ffeSIvan Shcherbakov                 state = WHPX_BP_SET;
1290d7482ffeSIvan Shcherbakov             }
1291d7482ffeSIvan Shcherbakov 
1292d7482ffeSIvan Shcherbakov         }
1293d7482ffeSIvan Shcherbakov 
1294d7482ffeSIvan Shcherbakov         if (state == WHPX_BP_CLEAR_PENDING) {
1295d7482ffeSIvan Shcherbakov             /* Restore the original instruction. */
1296d7482ffeSIvan Shcherbakov             rc = cpu_memory_rw_debug(cpu,
1297d7482ffeSIvan Shcherbakov                 breakpoints->data[i].address,
1298d7482ffeSIvan Shcherbakov                 &breakpoints->data[i].original_instruction,
1299d7482ffeSIvan Shcherbakov                 1,
1300d7482ffeSIvan Shcherbakov                 true);
1301d7482ffeSIvan Shcherbakov 
1302d7482ffeSIvan Shcherbakov             if (!rc) {
1303d7482ffeSIvan Shcherbakov                 state = WHPX_BP_CLEARED;
1304d7482ffeSIvan Shcherbakov             }
1305d7482ffeSIvan Shcherbakov         }
1306d7482ffeSIvan Shcherbakov 
1307d7482ffeSIvan Shcherbakov         breakpoints->data[i].state = state;
1308d7482ffeSIvan Shcherbakov     }
1309d7482ffeSIvan Shcherbakov }
1310d7482ffeSIvan Shcherbakov 
1311d7482ffeSIvan Shcherbakov /*
1312d7482ffeSIvan Shcherbakov  * This function is called when the a VCPU is about to start and no other
1313d7482ffeSIvan Shcherbakov  * VCPUs have been started so far. Since the VCPU start order could be
1314d7482ffeSIvan Shcherbakov  * arbitrary, it doesn't have to be VCPU#0.
1315d7482ffeSIvan Shcherbakov  *
1316d7482ffeSIvan Shcherbakov  * It is used to commit the breakpoints into memory, and configure WHPX
1317d7482ffeSIvan Shcherbakov  * to intercept debug exceptions.
1318d7482ffeSIvan Shcherbakov  *
1319d7482ffeSIvan Shcherbakov  * Note that whpx_set_exception_exit_bitmap() cannot be called if one or
1320d7482ffeSIvan Shcherbakov  * more VCPUs are already running, so this is the best place to do it.
1321d7482ffeSIvan Shcherbakov  */
1322d7482ffeSIvan Shcherbakov static int whpx_first_vcpu_starting(CPUState *cpu)
1323d7482ffeSIvan Shcherbakov {
1324d7482ffeSIvan Shcherbakov     struct whpx_state *whpx = &whpx_global;
1325d7482ffeSIvan Shcherbakov     HRESULT hr;
1326d7482ffeSIvan Shcherbakov 
1327d7482ffeSIvan Shcherbakov     g_assert(qemu_mutex_iothread_locked());
1328d7482ffeSIvan Shcherbakov 
1329d7482ffeSIvan Shcherbakov     if (!QTAILQ_EMPTY(&cpu->breakpoints) ||
1330d7482ffeSIvan Shcherbakov             (whpx->breakpoints.breakpoints &&
1331d7482ffeSIvan Shcherbakov              whpx->breakpoints.breakpoints->used)) {
1332d7482ffeSIvan Shcherbakov         CPUBreakpoint *bp;
1333d7482ffeSIvan Shcherbakov         int i = 0;
1334d7482ffeSIvan Shcherbakov         bool update_pending = false;
1335d7482ffeSIvan Shcherbakov 
1336d7482ffeSIvan Shcherbakov         QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
1337d7482ffeSIvan Shcherbakov             if (i >= whpx->breakpoints.original_address_count ||
1338d7482ffeSIvan Shcherbakov                 bp->pc != whpx->breakpoints.original_addresses[i]) {
1339d7482ffeSIvan Shcherbakov                 update_pending = true;
1340d7482ffeSIvan Shcherbakov             }
1341d7482ffeSIvan Shcherbakov 
1342d7482ffeSIvan Shcherbakov             i++;
1343d7482ffeSIvan Shcherbakov         }
1344d7482ffeSIvan Shcherbakov 
1345d7482ffeSIvan Shcherbakov         if (i != whpx->breakpoints.original_address_count) {
1346d7482ffeSIvan Shcherbakov             update_pending = true;
1347d7482ffeSIvan Shcherbakov         }
1348d7482ffeSIvan Shcherbakov 
1349d7482ffeSIvan Shcherbakov         if (update_pending) {
1350d7482ffeSIvan Shcherbakov             /*
1351d7482ffeSIvan Shcherbakov              * The CPU breakpoints have changed since the last call to
1352d7482ffeSIvan Shcherbakov              * whpx_translate_cpu_breakpoints(). WHPX breakpoints must
1353d7482ffeSIvan Shcherbakov              * now be recomputed.
1354d7482ffeSIvan Shcherbakov              */
1355d7482ffeSIvan Shcherbakov             whpx_translate_cpu_breakpoints(&whpx->breakpoints, cpu, i);
1356d7482ffeSIvan Shcherbakov         }
1357d7482ffeSIvan Shcherbakov 
1358d7482ffeSIvan Shcherbakov         /* Actually insert the breakpoints into the memory. */
1359d7482ffeSIvan Shcherbakov         whpx_apply_breakpoints(whpx->breakpoints.breakpoints, cpu, true);
1360d7482ffeSIvan Shcherbakov     }
1361d7482ffeSIvan Shcherbakov 
1362d7482ffeSIvan Shcherbakov     uint64_t exception_mask;
1363d7482ffeSIvan Shcherbakov     if (whpx->step_pending ||
1364d7482ffeSIvan Shcherbakov         (whpx->breakpoints.breakpoints &&
1365d7482ffeSIvan Shcherbakov          whpx->breakpoints.breakpoints->used)) {
1366d7482ffeSIvan Shcherbakov         /*
1367d7482ffeSIvan Shcherbakov          * We are either attempting to single-step one or more CPUs, or
1368d7482ffeSIvan Shcherbakov          * have one or more breakpoints enabled. Both require intercepting
1369d7482ffeSIvan Shcherbakov          * the WHvX64ExceptionTypeBreakpointTrap exception.
1370d7482ffeSIvan Shcherbakov          */
1371d7482ffeSIvan Shcherbakov 
1372d7482ffeSIvan Shcherbakov         exception_mask = 1UL << WHvX64ExceptionTypeDebugTrapOrFault;
1373d7482ffeSIvan Shcherbakov     } else {
1374d7482ffeSIvan Shcherbakov         /* Let the guest handle all exceptions. */
1375d7482ffeSIvan Shcherbakov         exception_mask = 0;
1376d7482ffeSIvan Shcherbakov     }
1377d7482ffeSIvan Shcherbakov 
1378d7482ffeSIvan Shcherbakov     hr = whpx_set_exception_exit_bitmap(exception_mask);
1379d7482ffeSIvan Shcherbakov     if (!SUCCEEDED(hr)) {
1380d7482ffeSIvan Shcherbakov         error_report("WHPX: Failed to update exception exit mask,"
1381d7482ffeSIvan Shcherbakov                      "hr=%08lx.", hr);
1382d7482ffeSIvan Shcherbakov         return 1;
1383d7482ffeSIvan Shcherbakov     }
1384d7482ffeSIvan Shcherbakov 
1385d7482ffeSIvan Shcherbakov     return 0;
1386d7482ffeSIvan Shcherbakov }
1387d7482ffeSIvan Shcherbakov 
1388d7482ffeSIvan Shcherbakov /*
1389d7482ffeSIvan Shcherbakov  * This function is called when the last VCPU has finished running.
1390d7482ffeSIvan Shcherbakov  * It is used to remove any previously set breakpoints from memory.
1391d7482ffeSIvan Shcherbakov  */
1392d7482ffeSIvan Shcherbakov static int whpx_last_vcpu_stopping(CPUState *cpu)
1393d7482ffeSIvan Shcherbakov {
1394d7482ffeSIvan Shcherbakov     whpx_apply_breakpoints(whpx_global.breakpoints.breakpoints, cpu, false);
1395d7482ffeSIvan Shcherbakov     return 0;
1396d7482ffeSIvan Shcherbakov }
1397d7482ffeSIvan Shcherbakov 
1398d7482ffeSIvan Shcherbakov /* Returns the address of the next instruction that is about to be executed. */
1399d7482ffeSIvan Shcherbakov static vaddr whpx_vcpu_get_pc(CPUState *cpu, bool exit_context_valid)
1400d7482ffeSIvan Shcherbakov {
1401d7482ffeSIvan Shcherbakov     if (cpu->vcpu_dirty) {
1402d7482ffeSIvan Shcherbakov         /* The CPU registers have been modified by other parts of QEMU. */
1403d7482ffeSIvan Shcherbakov         CPUArchState *env = (CPUArchState *)(cpu->env_ptr);
1404d7482ffeSIvan Shcherbakov         return env->eip;
1405d7482ffeSIvan Shcherbakov     } else if (exit_context_valid) {
1406d7482ffeSIvan Shcherbakov         /*
1407d7482ffeSIvan Shcherbakov          * The CPU registers have not been modified by neither other parts
1408d7482ffeSIvan Shcherbakov          * of QEMU, nor this port by calling WHvSetVirtualProcessorRegisters().
1409d7482ffeSIvan Shcherbakov          * This is the most common case.
1410d7482ffeSIvan Shcherbakov          */
1411441f2449SPhilippe Mathieu-Daudé         AccelCPUState *vcpu = cpu->accel;
1412d7482ffeSIvan Shcherbakov         return vcpu->exit_ctx.VpContext.Rip;
1413d7482ffeSIvan Shcherbakov     } else {
1414d7482ffeSIvan Shcherbakov         /*
1415d7482ffeSIvan Shcherbakov          * The CPU registers have been modified by a call to
1416d7482ffeSIvan Shcherbakov          * WHvSetVirtualProcessorRegisters() and must be re-queried from
1417d7482ffeSIvan Shcherbakov          * the target.
1418d7482ffeSIvan Shcherbakov          */
1419d7482ffeSIvan Shcherbakov         WHV_REGISTER_VALUE reg_value;
1420d7482ffeSIvan Shcherbakov         WHV_REGISTER_NAME reg_name = WHvX64RegisterRip;
1421d7482ffeSIvan Shcherbakov         HRESULT hr;
1422d7482ffeSIvan Shcherbakov         struct whpx_state *whpx = &whpx_global;
1423d7482ffeSIvan Shcherbakov 
1424d7482ffeSIvan Shcherbakov         hr = whp_dispatch.WHvGetVirtualProcessorRegisters(
1425d7482ffeSIvan Shcherbakov             whpx->partition,
1426d7482ffeSIvan Shcherbakov             cpu->cpu_index,
1427d7482ffeSIvan Shcherbakov             &reg_name,
1428d7482ffeSIvan Shcherbakov             1,
1429d7482ffeSIvan Shcherbakov             &reg_value);
1430d7482ffeSIvan Shcherbakov 
1431d7482ffeSIvan Shcherbakov         if (FAILED(hr)) {
1432d7482ffeSIvan Shcherbakov             error_report("WHPX: Failed to get PC, hr=%08lx", hr);
1433d7482ffeSIvan Shcherbakov             return 0;
1434d7482ffeSIvan Shcherbakov         }
1435d7482ffeSIvan Shcherbakov 
1436d7482ffeSIvan Shcherbakov         return reg_value.Reg64;
1437d7482ffeSIvan Shcherbakov     }
1438d7482ffeSIvan Shcherbakov }
1439d7482ffeSIvan Shcherbakov 
14401fc33bb9SClaudio Fontana static int whpx_handle_halt(CPUState *cpu)
14411fc33bb9SClaudio Fontana {
144295e862d7SPhilippe Mathieu-Daudé     CPUX86State *env = cpu->env_ptr;
14431fc33bb9SClaudio Fontana     int ret = 0;
14441fc33bb9SClaudio Fontana 
14451fc33bb9SClaudio Fontana     qemu_mutex_lock_iothread();
14461fc33bb9SClaudio Fontana     if (!((cpu->interrupt_request & CPU_INTERRUPT_HARD) &&
14471fc33bb9SClaudio Fontana           (env->eflags & IF_MASK)) &&
14481fc33bb9SClaudio Fontana         !(cpu->interrupt_request & CPU_INTERRUPT_NMI)) {
14491fc33bb9SClaudio Fontana         cpu->exception_index = EXCP_HLT;
14501fc33bb9SClaudio Fontana         cpu->halted = true;
14511fc33bb9SClaudio Fontana         ret = 1;
14521fc33bb9SClaudio Fontana     }
14531fc33bb9SClaudio Fontana     qemu_mutex_unlock_iothread();
14541fc33bb9SClaudio Fontana 
14551fc33bb9SClaudio Fontana     return ret;
14561fc33bb9SClaudio Fontana }
14571fc33bb9SClaudio Fontana 
14581fc33bb9SClaudio Fontana static void whpx_vcpu_pre_run(CPUState *cpu)
14591fc33bb9SClaudio Fontana {
14601fc33bb9SClaudio Fontana     HRESULT hr;
14611fc33bb9SClaudio Fontana     struct whpx_state *whpx = &whpx_global;
1462441f2449SPhilippe Mathieu-Daudé     AccelCPUState *vcpu = cpu->accel;
146395e862d7SPhilippe Mathieu-Daudé     CPUX86State *env = cpu->env_ptr;
14641fc33bb9SClaudio Fontana     X86CPU *x86_cpu = X86_CPU(cpu);
14651fc33bb9SClaudio Fontana     int irq;
14661fc33bb9SClaudio Fontana     uint8_t tpr;
14671fc33bb9SClaudio Fontana     WHV_X64_PENDING_INTERRUPTION_REGISTER new_int;
14681fc33bb9SClaudio Fontana     UINT32 reg_count = 0;
14691fc33bb9SClaudio Fontana     WHV_REGISTER_VALUE reg_values[3];
14701fc33bb9SClaudio Fontana     WHV_REGISTER_NAME reg_names[3];
14711fc33bb9SClaudio Fontana 
14721fc33bb9SClaudio Fontana     memset(&new_int, 0, sizeof(new_int));
14731fc33bb9SClaudio Fontana     memset(reg_values, 0, sizeof(reg_values));
14741fc33bb9SClaudio Fontana 
14751fc33bb9SClaudio Fontana     qemu_mutex_lock_iothread();
14761fc33bb9SClaudio Fontana 
14771fc33bb9SClaudio Fontana     /* Inject NMI */
14781fc33bb9SClaudio Fontana     if (!vcpu->interruption_pending &&
14791fc33bb9SClaudio Fontana         cpu->interrupt_request & (CPU_INTERRUPT_NMI | CPU_INTERRUPT_SMI)) {
14801fc33bb9SClaudio Fontana         if (cpu->interrupt_request & CPU_INTERRUPT_NMI) {
14811fc33bb9SClaudio Fontana             cpu->interrupt_request &= ~CPU_INTERRUPT_NMI;
14821fc33bb9SClaudio Fontana             vcpu->interruptable = false;
14831fc33bb9SClaudio Fontana             new_int.InterruptionType = WHvX64PendingNmi;
14841fc33bb9SClaudio Fontana             new_int.InterruptionPending = 1;
14851fc33bb9SClaudio Fontana             new_int.InterruptionVector = 2;
14861fc33bb9SClaudio Fontana         }
14871fc33bb9SClaudio Fontana         if (cpu->interrupt_request & CPU_INTERRUPT_SMI) {
14881fc33bb9SClaudio Fontana             cpu->interrupt_request &= ~CPU_INTERRUPT_SMI;
14891fc33bb9SClaudio Fontana         }
14901fc33bb9SClaudio Fontana     }
14911fc33bb9SClaudio Fontana 
14921fc33bb9SClaudio Fontana     /*
14931fc33bb9SClaudio Fontana      * Force the VCPU out of its inner loop to process any INIT requests or
14941fc33bb9SClaudio Fontana      * commit pending TPR access.
14951fc33bb9SClaudio Fontana      */
14961fc33bb9SClaudio Fontana     if (cpu->interrupt_request & (CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) {
14971fc33bb9SClaudio Fontana         if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) &&
14981fc33bb9SClaudio Fontana             !(env->hflags & HF_SMM_MASK)) {
14991fc33bb9SClaudio Fontana             cpu->exit_request = 1;
15001fc33bb9SClaudio Fontana         }
15011fc33bb9SClaudio Fontana         if (cpu->interrupt_request & CPU_INTERRUPT_TPR) {
15021fc33bb9SClaudio Fontana             cpu->exit_request = 1;
15031fc33bb9SClaudio Fontana         }
15041fc33bb9SClaudio Fontana     }
15051fc33bb9SClaudio Fontana 
15061fc33bb9SClaudio Fontana     /* Get pending hard interruption or replay one that was overwritten */
15071fc33bb9SClaudio Fontana     if (!whpx_apic_in_platform()) {
15081fc33bb9SClaudio Fontana         if (!vcpu->interruption_pending &&
15091fc33bb9SClaudio Fontana             vcpu->interruptable && (env->eflags & IF_MASK)) {
15101fc33bb9SClaudio Fontana             assert(!new_int.InterruptionPending);
15111fc33bb9SClaudio Fontana             if (cpu->interrupt_request & CPU_INTERRUPT_HARD) {
15121fc33bb9SClaudio Fontana                 cpu->interrupt_request &= ~CPU_INTERRUPT_HARD;
15131fc33bb9SClaudio Fontana                 irq = cpu_get_pic_interrupt(env);
15141fc33bb9SClaudio Fontana                 if (irq >= 0) {
15151fc33bb9SClaudio Fontana                     new_int.InterruptionType = WHvX64PendingInterrupt;
15161fc33bb9SClaudio Fontana                     new_int.InterruptionPending = 1;
15171fc33bb9SClaudio Fontana                     new_int.InterruptionVector = irq;
15181fc33bb9SClaudio Fontana                 }
15191fc33bb9SClaudio Fontana             }
15201fc33bb9SClaudio Fontana         }
15211fc33bb9SClaudio Fontana 
15221fc33bb9SClaudio Fontana         /* Setup interrupt state if new one was prepared */
15231fc33bb9SClaudio Fontana         if (new_int.InterruptionPending) {
15241fc33bb9SClaudio Fontana             reg_values[reg_count].PendingInterruption = new_int;
15251fc33bb9SClaudio Fontana             reg_names[reg_count] = WHvRegisterPendingInterruption;
15261fc33bb9SClaudio Fontana             reg_count += 1;
15271fc33bb9SClaudio Fontana         }
15281fc33bb9SClaudio Fontana     } else if (vcpu->ready_for_pic_interrupt &&
15291fc33bb9SClaudio Fontana                (cpu->interrupt_request & CPU_INTERRUPT_HARD)) {
15301fc33bb9SClaudio Fontana         cpu->interrupt_request &= ~CPU_INTERRUPT_HARD;
15311fc33bb9SClaudio Fontana         irq = cpu_get_pic_interrupt(env);
15321fc33bb9SClaudio Fontana         if (irq >= 0) {
15331fc33bb9SClaudio Fontana             reg_names[reg_count] = WHvRegisterPendingEvent;
15341fc33bb9SClaudio Fontana             reg_values[reg_count].ExtIntEvent = (WHV_X64_PENDING_EXT_INT_EVENT)
15351fc33bb9SClaudio Fontana             {
15361fc33bb9SClaudio Fontana                 .EventPending = 1,
15371fc33bb9SClaudio Fontana                 .EventType = WHvX64PendingEventExtInt,
15381fc33bb9SClaudio Fontana                 .Vector = irq,
15391fc33bb9SClaudio Fontana             };
15401fc33bb9SClaudio Fontana             reg_count += 1;
15411fc33bb9SClaudio Fontana         }
15421fc33bb9SClaudio Fontana      }
15431fc33bb9SClaudio Fontana 
15441fc33bb9SClaudio Fontana     /* Sync the TPR to the CR8 if was modified during the intercept */
1545f000bc74SIvan Shcherbakov     tpr = whpx_apic_tpr_to_cr8(cpu_get_apic_tpr(x86_cpu->apic_state));
15461fc33bb9SClaudio Fontana     if (tpr != vcpu->tpr) {
15471fc33bb9SClaudio Fontana         vcpu->tpr = tpr;
15481fc33bb9SClaudio Fontana         reg_values[reg_count].Reg64 = tpr;
15491fc33bb9SClaudio Fontana         cpu->exit_request = 1;
15501fc33bb9SClaudio Fontana         reg_names[reg_count] = WHvX64RegisterCr8;
15511fc33bb9SClaudio Fontana         reg_count += 1;
15521fc33bb9SClaudio Fontana     }
15531fc33bb9SClaudio Fontana 
15541fc33bb9SClaudio Fontana     /* Update the state of the interrupt delivery notification */
15551fc33bb9SClaudio Fontana     if (!vcpu->window_registered &&
15561fc33bb9SClaudio Fontana         cpu->interrupt_request & CPU_INTERRUPT_HARD) {
15571fc33bb9SClaudio Fontana         reg_values[reg_count].DeliverabilityNotifications =
15581fc33bb9SClaudio Fontana             (WHV_X64_DELIVERABILITY_NOTIFICATIONS_REGISTER) {
15591fc33bb9SClaudio Fontana                 .InterruptNotification = 1
15601fc33bb9SClaudio Fontana             };
15611fc33bb9SClaudio Fontana         vcpu->window_registered = 1;
15621fc33bb9SClaudio Fontana         reg_names[reg_count] = WHvX64RegisterDeliverabilityNotifications;
15631fc33bb9SClaudio Fontana         reg_count += 1;
15641fc33bb9SClaudio Fontana     }
15651fc33bb9SClaudio Fontana 
15661fc33bb9SClaudio Fontana     qemu_mutex_unlock_iothread();
15671fc33bb9SClaudio Fontana     vcpu->ready_for_pic_interrupt = false;
15681fc33bb9SClaudio Fontana 
15691fc33bb9SClaudio Fontana     if (reg_count) {
15701fc33bb9SClaudio Fontana         hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
15711fc33bb9SClaudio Fontana             whpx->partition, cpu->cpu_index,
15721fc33bb9SClaudio Fontana             reg_names, reg_count, reg_values);
15731fc33bb9SClaudio Fontana         if (FAILED(hr)) {
15741fc33bb9SClaudio Fontana             error_report("WHPX: Failed to set interrupt state registers,"
15751fc33bb9SClaudio Fontana                          " hr=%08lx", hr);
15761fc33bb9SClaudio Fontana         }
15771fc33bb9SClaudio Fontana     }
15781fc33bb9SClaudio Fontana 
15791fc33bb9SClaudio Fontana     return;
15801fc33bb9SClaudio Fontana }
15811fc33bb9SClaudio Fontana 
15821fc33bb9SClaudio Fontana static void whpx_vcpu_post_run(CPUState *cpu)
15831fc33bb9SClaudio Fontana {
1584441f2449SPhilippe Mathieu-Daudé     AccelCPUState *vcpu = cpu->accel;
158595e862d7SPhilippe Mathieu-Daudé     CPUX86State *env = cpu->env_ptr;
15861fc33bb9SClaudio Fontana     X86CPU *x86_cpu = X86_CPU(cpu);
15871fc33bb9SClaudio Fontana 
15881fc33bb9SClaudio Fontana     env->eflags = vcpu->exit_ctx.VpContext.Rflags;
15891fc33bb9SClaudio Fontana 
15901fc33bb9SClaudio Fontana     uint64_t tpr = vcpu->exit_ctx.VpContext.Cr8;
15911fc33bb9SClaudio Fontana     if (vcpu->tpr != tpr) {
15921fc33bb9SClaudio Fontana         vcpu->tpr = tpr;
15931fc33bb9SClaudio Fontana         qemu_mutex_lock_iothread();
1594f000bc74SIvan Shcherbakov         cpu_set_apic_tpr(x86_cpu->apic_state, whpx_cr8_to_apic_tpr(vcpu->tpr));
15951fc33bb9SClaudio Fontana         qemu_mutex_unlock_iothread();
15961fc33bb9SClaudio Fontana     }
15971fc33bb9SClaudio Fontana 
15981fc33bb9SClaudio Fontana     vcpu->interruption_pending =
15991fc33bb9SClaudio Fontana         vcpu->exit_ctx.VpContext.ExecutionState.InterruptionPending;
16001fc33bb9SClaudio Fontana 
16011fc33bb9SClaudio Fontana     vcpu->interruptable =
16021fc33bb9SClaudio Fontana         !vcpu->exit_ctx.VpContext.ExecutionState.InterruptShadow;
16031fc33bb9SClaudio Fontana 
16041fc33bb9SClaudio Fontana     return;
16051fc33bb9SClaudio Fontana }
16061fc33bb9SClaudio Fontana 
16071fc33bb9SClaudio Fontana static void whpx_vcpu_process_async_events(CPUState *cpu)
16081fc33bb9SClaudio Fontana {
160995e862d7SPhilippe Mathieu-Daudé     CPUX86State *env = cpu->env_ptr;
16101fc33bb9SClaudio Fontana     X86CPU *x86_cpu = X86_CPU(cpu);
1611441f2449SPhilippe Mathieu-Daudé     AccelCPUState *vcpu = cpu->accel;
16121fc33bb9SClaudio Fontana 
16131fc33bb9SClaudio Fontana     if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) &&
16141fc33bb9SClaudio Fontana         !(env->hflags & HF_SMM_MASK)) {
16151fc33bb9SClaudio Fontana         whpx_cpu_synchronize_state(cpu);
16161fc33bb9SClaudio Fontana         do_cpu_init(x86_cpu);
16171fc33bb9SClaudio Fontana         vcpu->interruptable = true;
16181fc33bb9SClaudio Fontana     }
16191fc33bb9SClaudio Fontana 
16201fc33bb9SClaudio Fontana     if (cpu->interrupt_request & CPU_INTERRUPT_POLL) {
16211fc33bb9SClaudio Fontana         cpu->interrupt_request &= ~CPU_INTERRUPT_POLL;
16221fc33bb9SClaudio Fontana         apic_poll_irq(x86_cpu->apic_state);
16231fc33bb9SClaudio Fontana     }
16241fc33bb9SClaudio Fontana 
16251fc33bb9SClaudio Fontana     if (((cpu->interrupt_request & CPU_INTERRUPT_HARD) &&
16261fc33bb9SClaudio Fontana          (env->eflags & IF_MASK)) ||
16271fc33bb9SClaudio Fontana         (cpu->interrupt_request & CPU_INTERRUPT_NMI)) {
16281fc33bb9SClaudio Fontana         cpu->halted = false;
16291fc33bb9SClaudio Fontana     }
16301fc33bb9SClaudio Fontana 
16311fc33bb9SClaudio Fontana     if (cpu->interrupt_request & CPU_INTERRUPT_SIPI) {
16321fc33bb9SClaudio Fontana         whpx_cpu_synchronize_state(cpu);
16331fc33bb9SClaudio Fontana         do_cpu_sipi(x86_cpu);
16341fc33bb9SClaudio Fontana     }
16351fc33bb9SClaudio Fontana 
16361fc33bb9SClaudio Fontana     if (cpu->interrupt_request & CPU_INTERRUPT_TPR) {
16371fc33bb9SClaudio Fontana         cpu->interrupt_request &= ~CPU_INTERRUPT_TPR;
16381fc33bb9SClaudio Fontana         whpx_cpu_synchronize_state(cpu);
16391fc33bb9SClaudio Fontana         apic_handle_tpr_access_report(x86_cpu->apic_state, env->eip,
16401fc33bb9SClaudio Fontana                                       env->tpr_access_type);
16411fc33bb9SClaudio Fontana     }
16421fc33bb9SClaudio Fontana 
16431fc33bb9SClaudio Fontana     return;
16441fc33bb9SClaudio Fontana }
16451fc33bb9SClaudio Fontana 
16461fc33bb9SClaudio Fontana static int whpx_vcpu_run(CPUState *cpu)
16471fc33bb9SClaudio Fontana {
16481fc33bb9SClaudio Fontana     HRESULT hr;
16491fc33bb9SClaudio Fontana     struct whpx_state *whpx = &whpx_global;
1650441f2449SPhilippe Mathieu-Daudé     AccelCPUState *vcpu = cpu->accel;
1651d7482ffeSIvan Shcherbakov     struct whpx_breakpoint *stepped_over_bp = NULL;
1652d7482ffeSIvan Shcherbakov     WhpxStepMode exclusive_step_mode = WHPX_STEP_NONE;
16531fc33bb9SClaudio Fontana     int ret;
16541fc33bb9SClaudio Fontana 
1655d7482ffeSIvan Shcherbakov     g_assert(qemu_mutex_iothread_locked());
1656d7482ffeSIvan Shcherbakov 
1657d7482ffeSIvan Shcherbakov     if (whpx->running_cpus++ == 0) {
1658d7482ffeSIvan Shcherbakov         /* Insert breakpoints into memory, update exception exit bitmap. */
1659d7482ffeSIvan Shcherbakov         ret = whpx_first_vcpu_starting(cpu);
1660d7482ffeSIvan Shcherbakov         if (ret != 0) {
1661d7482ffeSIvan Shcherbakov             return ret;
1662d7482ffeSIvan Shcherbakov         }
1663d7482ffeSIvan Shcherbakov     }
1664d7482ffeSIvan Shcherbakov 
1665d7482ffeSIvan Shcherbakov     if (whpx->breakpoints.breakpoints &&
1666d7482ffeSIvan Shcherbakov         whpx->breakpoints.breakpoints->used > 0)
1667d7482ffeSIvan Shcherbakov     {
1668d7482ffeSIvan Shcherbakov         uint64_t pc = whpx_vcpu_get_pc(cpu, true);
1669d7482ffeSIvan Shcherbakov         stepped_over_bp = whpx_lookup_breakpoint_by_addr(pc);
1670d7482ffeSIvan Shcherbakov         if (stepped_over_bp && stepped_over_bp->state != WHPX_BP_SET) {
1671d7482ffeSIvan Shcherbakov             stepped_over_bp = NULL;
1672d7482ffeSIvan Shcherbakov         }
1673d7482ffeSIvan Shcherbakov 
1674d7482ffeSIvan Shcherbakov         if (stepped_over_bp) {
1675d7482ffeSIvan Shcherbakov             /*
1676d7482ffeSIvan Shcherbakov              * We are trying to run the instruction overwritten by an active
1677d7482ffeSIvan Shcherbakov              * breakpoint. We will temporarily disable the breakpoint, suspend
1678d7482ffeSIvan Shcherbakov              * other CPUs, and step over the instruction.
1679d7482ffeSIvan Shcherbakov              */
1680d7482ffeSIvan Shcherbakov             exclusive_step_mode = WHPX_STEP_EXCLUSIVE;
1681d7482ffeSIvan Shcherbakov         }
1682d7482ffeSIvan Shcherbakov     }
1683d7482ffeSIvan Shcherbakov 
1684d7482ffeSIvan Shcherbakov     if (exclusive_step_mode == WHPX_STEP_NONE) {
16851fc33bb9SClaudio Fontana         whpx_vcpu_process_async_events(cpu);
16861fc33bb9SClaudio Fontana         if (cpu->halted && !whpx_apic_in_platform()) {
16871fc33bb9SClaudio Fontana             cpu->exception_index = EXCP_HLT;
16881fc33bb9SClaudio Fontana             qatomic_set(&cpu->exit_request, false);
16891fc33bb9SClaudio Fontana             return 0;
16901fc33bb9SClaudio Fontana         }
1691d7482ffeSIvan Shcherbakov     }
16921fc33bb9SClaudio Fontana 
16931fc33bb9SClaudio Fontana     qemu_mutex_unlock_iothread();
1694d7482ffeSIvan Shcherbakov 
1695d7482ffeSIvan Shcherbakov     if (exclusive_step_mode != WHPX_STEP_NONE) {
1696d7482ffeSIvan Shcherbakov         start_exclusive();
1697d7482ffeSIvan Shcherbakov         g_assert(cpu == current_cpu);
1698d7482ffeSIvan Shcherbakov         g_assert(!cpu->running);
1699d7482ffeSIvan Shcherbakov         cpu->running = true;
1700d7482ffeSIvan Shcherbakov 
1701d7482ffeSIvan Shcherbakov         hr = whpx_set_exception_exit_bitmap(
1702d7482ffeSIvan Shcherbakov             1UL << WHvX64ExceptionTypeDebugTrapOrFault);
1703d7482ffeSIvan Shcherbakov         if (!SUCCEEDED(hr)) {
1704d7482ffeSIvan Shcherbakov             error_report("WHPX: Failed to update exception exit mask, "
1705d7482ffeSIvan Shcherbakov                          "hr=%08lx.", hr);
1706d7482ffeSIvan Shcherbakov             return 1;
1707d7482ffeSIvan Shcherbakov         }
1708d7482ffeSIvan Shcherbakov 
1709d7482ffeSIvan Shcherbakov         if (stepped_over_bp) {
1710d7482ffeSIvan Shcherbakov             /* Temporarily disable the triggered breakpoint. */
1711d7482ffeSIvan Shcherbakov             cpu_memory_rw_debug(cpu,
1712d7482ffeSIvan Shcherbakov                 stepped_over_bp->address,
1713d7482ffeSIvan Shcherbakov                 &stepped_over_bp->original_instruction,
1714d7482ffeSIvan Shcherbakov                 1,
1715d7482ffeSIvan Shcherbakov                 true);
1716d7482ffeSIvan Shcherbakov         }
1717d7482ffeSIvan Shcherbakov     } else {
17181fc33bb9SClaudio Fontana         cpu_exec_start(cpu);
1719d7482ffeSIvan Shcherbakov     }
17201fc33bb9SClaudio Fontana 
17211fc33bb9SClaudio Fontana     do {
17221fc33bb9SClaudio Fontana         if (cpu->vcpu_dirty) {
17231fc33bb9SClaudio Fontana             whpx_set_registers(cpu, WHPX_SET_RUNTIME_STATE);
17241fc33bb9SClaudio Fontana             cpu->vcpu_dirty = false;
17251fc33bb9SClaudio Fontana         }
17261fc33bb9SClaudio Fontana 
1727d7482ffeSIvan Shcherbakov         if (exclusive_step_mode == WHPX_STEP_NONE) {
17281fc33bb9SClaudio Fontana             whpx_vcpu_pre_run(cpu);
17291fc33bb9SClaudio Fontana 
17301fc33bb9SClaudio Fontana             if (qatomic_read(&cpu->exit_request)) {
17311fc33bb9SClaudio Fontana                 whpx_vcpu_kick(cpu);
17321fc33bb9SClaudio Fontana             }
1733d7482ffeSIvan Shcherbakov         }
1734d7482ffeSIvan Shcherbakov 
1735d7482ffeSIvan Shcherbakov         if (exclusive_step_mode != WHPX_STEP_NONE || cpu->singlestep_enabled) {
1736d7482ffeSIvan Shcherbakov             whpx_vcpu_configure_single_stepping(cpu, true, NULL);
1737d7482ffeSIvan Shcherbakov         }
17381fc33bb9SClaudio Fontana 
17391fc33bb9SClaudio Fontana         hr = whp_dispatch.WHvRunVirtualProcessor(
17401fc33bb9SClaudio Fontana             whpx->partition, cpu->cpu_index,
17411fc33bb9SClaudio Fontana             &vcpu->exit_ctx, sizeof(vcpu->exit_ctx));
17421fc33bb9SClaudio Fontana 
17431fc33bb9SClaudio Fontana         if (FAILED(hr)) {
17441fc33bb9SClaudio Fontana             error_report("WHPX: Failed to exec a virtual processor,"
17451fc33bb9SClaudio Fontana                          " hr=%08lx", hr);
17461fc33bb9SClaudio Fontana             ret = -1;
17471fc33bb9SClaudio Fontana             break;
17481fc33bb9SClaudio Fontana         }
17491fc33bb9SClaudio Fontana 
1750d7482ffeSIvan Shcherbakov         if (exclusive_step_mode != WHPX_STEP_NONE || cpu->singlestep_enabled) {
1751d7482ffeSIvan Shcherbakov             whpx_vcpu_configure_single_stepping(cpu,
1752d7482ffeSIvan Shcherbakov                 false,
1753d7482ffeSIvan Shcherbakov                 &vcpu->exit_ctx.VpContext.Rflags);
1754d7482ffeSIvan Shcherbakov         }
1755d7482ffeSIvan Shcherbakov 
17561fc33bb9SClaudio Fontana         whpx_vcpu_post_run(cpu);
17571fc33bb9SClaudio Fontana 
17581fc33bb9SClaudio Fontana         switch (vcpu->exit_ctx.ExitReason) {
17591fc33bb9SClaudio Fontana         case WHvRunVpExitReasonMemoryAccess:
17601fc33bb9SClaudio Fontana             ret = whpx_handle_mmio(cpu, &vcpu->exit_ctx.MemoryAccess);
17611fc33bb9SClaudio Fontana             break;
17621fc33bb9SClaudio Fontana 
17631fc33bb9SClaudio Fontana         case WHvRunVpExitReasonX64IoPortAccess:
17641fc33bb9SClaudio Fontana             ret = whpx_handle_portio(cpu, &vcpu->exit_ctx.IoPortAccess);
17651fc33bb9SClaudio Fontana             break;
17661fc33bb9SClaudio Fontana 
17671fc33bb9SClaudio Fontana         case WHvRunVpExitReasonX64InterruptWindow:
17681fc33bb9SClaudio Fontana             vcpu->ready_for_pic_interrupt = 1;
17691fc33bb9SClaudio Fontana             vcpu->window_registered = 0;
17701fc33bb9SClaudio Fontana             ret = 0;
17711fc33bb9SClaudio Fontana             break;
17721fc33bb9SClaudio Fontana 
17731fc33bb9SClaudio Fontana         case WHvRunVpExitReasonX64ApicEoi:
17741fc33bb9SClaudio Fontana             assert(whpx_apic_in_platform());
17751fc33bb9SClaudio Fontana             ioapic_eoi_broadcast(vcpu->exit_ctx.ApicEoi.InterruptVector);
17761fc33bb9SClaudio Fontana             break;
17771fc33bb9SClaudio Fontana 
17781fc33bb9SClaudio Fontana         case WHvRunVpExitReasonX64Halt:
1779d7482ffeSIvan Shcherbakov             /*
1780d7482ffeSIvan Shcherbakov              * WARNING: as of build 19043.1526 (21H1), this exit reason is no
1781d7482ffeSIvan Shcherbakov              * longer used.
1782d7482ffeSIvan Shcherbakov              */
17831fc33bb9SClaudio Fontana             ret = whpx_handle_halt(cpu);
17841fc33bb9SClaudio Fontana             break;
17851fc33bb9SClaudio Fontana 
17861fc33bb9SClaudio Fontana         case WHvRunVpExitReasonX64ApicInitSipiTrap: {
17871fc33bb9SClaudio Fontana             WHV_INTERRUPT_CONTROL ipi = {0};
17881fc33bb9SClaudio Fontana             uint64_t icr = vcpu->exit_ctx.ApicInitSipi.ApicIcr;
17891fc33bb9SClaudio Fontana             uint32_t delivery_mode =
17901fc33bb9SClaudio Fontana                 (icr & APIC_ICR_DELIV_MOD) >> APIC_ICR_DELIV_MOD_SHIFT;
17911fc33bb9SClaudio Fontana             int dest_shorthand =
17921fc33bb9SClaudio Fontana                 (icr & APIC_ICR_DEST_SHORT) >> APIC_ICR_DEST_SHORT_SHIFT;
17931fc33bb9SClaudio Fontana             bool broadcast = false;
17941fc33bb9SClaudio Fontana             bool include_self = false;
17951fc33bb9SClaudio Fontana             uint32_t i;
17961fc33bb9SClaudio Fontana 
17971fc33bb9SClaudio Fontana             /* We only registered for INIT and SIPI exits. */
17981fc33bb9SClaudio Fontana             if ((delivery_mode != APIC_DM_INIT) &&
17991fc33bb9SClaudio Fontana                 (delivery_mode != APIC_DM_SIPI)) {
18001fc33bb9SClaudio Fontana                 error_report(
18011fc33bb9SClaudio Fontana                     "WHPX: Unexpected APIC exit that is not a INIT or SIPI");
18021fc33bb9SClaudio Fontana                 break;
18031fc33bb9SClaudio Fontana             }
18041fc33bb9SClaudio Fontana 
18051fc33bb9SClaudio Fontana             if (delivery_mode == APIC_DM_INIT) {
18061fc33bb9SClaudio Fontana                 ipi.Type = WHvX64InterruptTypeInit;
18071fc33bb9SClaudio Fontana             } else {
18081fc33bb9SClaudio Fontana                 ipi.Type = WHvX64InterruptTypeSipi;
18091fc33bb9SClaudio Fontana             }
18101fc33bb9SClaudio Fontana 
18111fc33bb9SClaudio Fontana             ipi.DestinationMode =
18121fc33bb9SClaudio Fontana                 ((icr & APIC_ICR_DEST_MOD) >> APIC_ICR_DEST_MOD_SHIFT) ?
18131fc33bb9SClaudio Fontana                     WHvX64InterruptDestinationModeLogical :
18141fc33bb9SClaudio Fontana                     WHvX64InterruptDestinationModePhysical;
18151fc33bb9SClaudio Fontana 
18161fc33bb9SClaudio Fontana             ipi.TriggerMode =
18171fc33bb9SClaudio Fontana                 ((icr & APIC_ICR_TRIGGER_MOD) >> APIC_ICR_TRIGGER_MOD_SHIFT) ?
18181fc33bb9SClaudio Fontana                     WHvX64InterruptTriggerModeLevel :
18191fc33bb9SClaudio Fontana                     WHvX64InterruptTriggerModeEdge;
18201fc33bb9SClaudio Fontana 
18211fc33bb9SClaudio Fontana             ipi.Vector = icr & APIC_VECTOR_MASK;
18221fc33bb9SClaudio Fontana             switch (dest_shorthand) {
18231fc33bb9SClaudio Fontana             /* no shorthand. Bits 56-63 contain the destination. */
18241fc33bb9SClaudio Fontana             case 0:
18251fc33bb9SClaudio Fontana                 ipi.Destination = (icr >> 56) & APIC_VECTOR_MASK;
18261fc33bb9SClaudio Fontana                 hr = whp_dispatch.WHvRequestInterrupt(whpx->partition,
18271fc33bb9SClaudio Fontana                         &ipi, sizeof(ipi));
18281fc33bb9SClaudio Fontana                 if (FAILED(hr)) {
18291fc33bb9SClaudio Fontana                     error_report("WHPX: Failed to request interrupt  hr=%08lx",
18301fc33bb9SClaudio Fontana                         hr);
18311fc33bb9SClaudio Fontana                 }
18321fc33bb9SClaudio Fontana 
18331fc33bb9SClaudio Fontana                 break;
18341fc33bb9SClaudio Fontana 
18351fc33bb9SClaudio Fontana             /* self */
18361fc33bb9SClaudio Fontana             case 1:
18371fc33bb9SClaudio Fontana                 include_self = true;
18381fc33bb9SClaudio Fontana                 break;
18391fc33bb9SClaudio Fontana 
18401fc33bb9SClaudio Fontana             /* broadcast, including self */
18411fc33bb9SClaudio Fontana             case 2:
18421fc33bb9SClaudio Fontana                 broadcast = true;
18431fc33bb9SClaudio Fontana                 include_self = true;
18441fc33bb9SClaudio Fontana                 break;
18451fc33bb9SClaudio Fontana 
18461fc33bb9SClaudio Fontana             /* broadcast, excluding self */
18471fc33bb9SClaudio Fontana             case 3:
18481fc33bb9SClaudio Fontana                 broadcast = true;
18491fc33bb9SClaudio Fontana                 break;
18501fc33bb9SClaudio Fontana             }
18511fc33bb9SClaudio Fontana 
18521fc33bb9SClaudio Fontana             if (!broadcast && !include_self) {
18531fc33bb9SClaudio Fontana                 break;
18541fc33bb9SClaudio Fontana             }
18551fc33bb9SClaudio Fontana 
18561fc33bb9SClaudio Fontana             for (i = 0; i <= max_vcpu_index; i++) {
18571fc33bb9SClaudio Fontana                 if (i == cpu->cpu_index && !include_self) {
18581fc33bb9SClaudio Fontana                     continue;
18591fc33bb9SClaudio Fontana                 }
18601fc33bb9SClaudio Fontana 
18611fc33bb9SClaudio Fontana                 /*
18621fc33bb9SClaudio Fontana                  * Assuming that APIC Ids are identity mapped since
18631fc33bb9SClaudio Fontana                  * WHvX64RegisterApicId & WHvX64RegisterInitialApicId registers
18641fc33bb9SClaudio Fontana                  * are not handled yet and the hypervisor doesn't allow the
18651fc33bb9SClaudio Fontana                  * guest to modify the APIC ID.
18661fc33bb9SClaudio Fontana                  */
18671fc33bb9SClaudio Fontana                 ipi.Destination = i;
18681fc33bb9SClaudio Fontana                 hr = whp_dispatch.WHvRequestInterrupt(whpx->partition,
18691fc33bb9SClaudio Fontana                         &ipi, sizeof(ipi));
18701fc33bb9SClaudio Fontana                 if (FAILED(hr)) {
18711fc33bb9SClaudio Fontana                     error_report(
18721fc33bb9SClaudio Fontana                         "WHPX: Failed to request SIPI for %d,  hr=%08lx",
18731fc33bb9SClaudio Fontana                         i, hr);
18741fc33bb9SClaudio Fontana                 }
18751fc33bb9SClaudio Fontana             }
18761fc33bb9SClaudio Fontana 
18771fc33bb9SClaudio Fontana             break;
18781fc33bb9SClaudio Fontana         }
18791fc33bb9SClaudio Fontana 
18801fc33bb9SClaudio Fontana         case WHvRunVpExitReasonCanceled:
1881d7482ffeSIvan Shcherbakov             if (exclusive_step_mode != WHPX_STEP_NONE) {
1882d7482ffeSIvan Shcherbakov                 /*
1883d7482ffeSIvan Shcherbakov                  * We are trying to step over a single instruction, and
1884d7482ffeSIvan Shcherbakov                  * likely got a request to stop from another thread.
1885d7482ffeSIvan Shcherbakov                  * Delay it until we are done stepping
1886d7482ffeSIvan Shcherbakov                  * over.
1887d7482ffeSIvan Shcherbakov                  */
1888d7482ffeSIvan Shcherbakov                 ret = 0;
1889d7482ffeSIvan Shcherbakov             } else {
18901fc33bb9SClaudio Fontana                 cpu->exception_index = EXCP_INTERRUPT;
18911fc33bb9SClaudio Fontana                 ret = 1;
1892d7482ffeSIvan Shcherbakov             }
18931fc33bb9SClaudio Fontana             break;
18941fc33bb9SClaudio Fontana         case WHvRunVpExitReasonX64MsrAccess: {
18951fc33bb9SClaudio Fontana             WHV_REGISTER_VALUE reg_values[3] = {0};
18961fc33bb9SClaudio Fontana             WHV_REGISTER_NAME reg_names[3];
18971fc33bb9SClaudio Fontana             UINT32 reg_count;
18981fc33bb9SClaudio Fontana 
18991fc33bb9SClaudio Fontana             reg_names[0] = WHvX64RegisterRip;
19001fc33bb9SClaudio Fontana             reg_names[1] = WHvX64RegisterRax;
19011fc33bb9SClaudio Fontana             reg_names[2] = WHvX64RegisterRdx;
19021fc33bb9SClaudio Fontana 
19031fc33bb9SClaudio Fontana             reg_values[0].Reg64 =
19041fc33bb9SClaudio Fontana                 vcpu->exit_ctx.VpContext.Rip +
19051fc33bb9SClaudio Fontana                 vcpu->exit_ctx.VpContext.InstructionLength;
19061fc33bb9SClaudio Fontana 
19071fc33bb9SClaudio Fontana             /*
19081fc33bb9SClaudio Fontana              * For all unsupported MSR access we:
19091fc33bb9SClaudio Fontana              *     ignore writes
19101fc33bb9SClaudio Fontana              *     return 0 on read.
19111fc33bb9SClaudio Fontana              */
19121fc33bb9SClaudio Fontana             reg_count = vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite ?
19131fc33bb9SClaudio Fontana                         1 : 3;
19141fc33bb9SClaudio Fontana 
19151fc33bb9SClaudio Fontana             hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
19161fc33bb9SClaudio Fontana                 whpx->partition,
19171fc33bb9SClaudio Fontana                 cpu->cpu_index,
19181fc33bb9SClaudio Fontana                 reg_names, reg_count,
19191fc33bb9SClaudio Fontana                 reg_values);
19201fc33bb9SClaudio Fontana 
19211fc33bb9SClaudio Fontana             if (FAILED(hr)) {
19221fc33bb9SClaudio Fontana                 error_report("WHPX: Failed to set MsrAccess state "
19231fc33bb9SClaudio Fontana                              " registers, hr=%08lx", hr);
19241fc33bb9SClaudio Fontana             }
19251fc33bb9SClaudio Fontana             ret = 0;
19261fc33bb9SClaudio Fontana             break;
19271fc33bb9SClaudio Fontana         }
19281fc33bb9SClaudio Fontana         case WHvRunVpExitReasonX64Cpuid: {
19291fc33bb9SClaudio Fontana             WHV_REGISTER_VALUE reg_values[5];
19301fc33bb9SClaudio Fontana             WHV_REGISTER_NAME reg_names[5];
19311fc33bb9SClaudio Fontana             UINT32 reg_count = 5;
19321fc33bb9SClaudio Fontana             UINT64 cpuid_fn, rip = 0, rax = 0, rcx = 0, rdx = 0, rbx = 0;
19331fc33bb9SClaudio Fontana             X86CPU *x86_cpu = X86_CPU(cpu);
19341fc33bb9SClaudio Fontana             CPUX86State *env = &x86_cpu->env;
19351fc33bb9SClaudio Fontana 
19361fc33bb9SClaudio Fontana             memset(reg_values, 0, sizeof(reg_values));
19371fc33bb9SClaudio Fontana 
19381fc33bb9SClaudio Fontana             rip = vcpu->exit_ctx.VpContext.Rip +
19391fc33bb9SClaudio Fontana                   vcpu->exit_ctx.VpContext.InstructionLength;
19401fc33bb9SClaudio Fontana             cpuid_fn = vcpu->exit_ctx.CpuidAccess.Rax;
19411fc33bb9SClaudio Fontana 
19421fc33bb9SClaudio Fontana             /*
19431fc33bb9SClaudio Fontana              * Ideally, these should be supplied to the hypervisor during VCPU
19441fc33bb9SClaudio Fontana              * initialization and it should be able to satisfy this request.
19451fc33bb9SClaudio Fontana              * But, currently, WHPX doesn't support setting CPUID values in the
19461fc33bb9SClaudio Fontana              * hypervisor once the partition has been setup, which is too late
19471fc33bb9SClaudio Fontana              * since VCPUs are realized later. For now, use the values from
19481fc33bb9SClaudio Fontana              * QEMU to satisfy these requests, until WHPX adds support for
19491fc33bb9SClaudio Fontana              * being able to set these values in the hypervisor at runtime.
19501fc33bb9SClaudio Fontana              */
19511fc33bb9SClaudio Fontana             cpu_x86_cpuid(env, cpuid_fn, 0, (UINT32 *)&rax, (UINT32 *)&rbx,
19521fc33bb9SClaudio Fontana                 (UINT32 *)&rcx, (UINT32 *)&rdx);
19531fc33bb9SClaudio Fontana             switch (cpuid_fn) {
19541fc33bb9SClaudio Fontana             case 0x40000000:
19551fc33bb9SClaudio Fontana                 /* Expose the vmware cpu frequency cpuid leaf */
19561fc33bb9SClaudio Fontana                 rax = 0x40000010;
19571fc33bb9SClaudio Fontana                 rbx = rcx = rdx = 0;
19581fc33bb9SClaudio Fontana                 break;
19591fc33bb9SClaudio Fontana 
19601fc33bb9SClaudio Fontana             case 0x40000010:
19611fc33bb9SClaudio Fontana                 rax = env->tsc_khz;
19621fc33bb9SClaudio Fontana                 rbx = env->apic_bus_freq / 1000; /* Hz to KHz */
19631fc33bb9SClaudio Fontana                 rcx = rdx = 0;
19641fc33bb9SClaudio Fontana                 break;
19651fc33bb9SClaudio Fontana 
19661fc33bb9SClaudio Fontana             case 0x80000001:
19671fc33bb9SClaudio Fontana                 /* Remove any support of OSVW */
19681fc33bb9SClaudio Fontana                 rcx &= ~CPUID_EXT3_OSVW;
19691fc33bb9SClaudio Fontana                 break;
19701fc33bb9SClaudio Fontana             }
19711fc33bb9SClaudio Fontana 
19721fc33bb9SClaudio Fontana             reg_names[0] = WHvX64RegisterRip;
19731fc33bb9SClaudio Fontana             reg_names[1] = WHvX64RegisterRax;
19741fc33bb9SClaudio Fontana             reg_names[2] = WHvX64RegisterRcx;
19751fc33bb9SClaudio Fontana             reg_names[3] = WHvX64RegisterRdx;
19761fc33bb9SClaudio Fontana             reg_names[4] = WHvX64RegisterRbx;
19771fc33bb9SClaudio Fontana 
19781fc33bb9SClaudio Fontana             reg_values[0].Reg64 = rip;
19791fc33bb9SClaudio Fontana             reg_values[1].Reg64 = rax;
19801fc33bb9SClaudio Fontana             reg_values[2].Reg64 = rcx;
19811fc33bb9SClaudio Fontana             reg_values[3].Reg64 = rdx;
19821fc33bb9SClaudio Fontana             reg_values[4].Reg64 = rbx;
19831fc33bb9SClaudio Fontana 
19841fc33bb9SClaudio Fontana             hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
19851fc33bb9SClaudio Fontana                 whpx->partition, cpu->cpu_index,
19861fc33bb9SClaudio Fontana                 reg_names,
19871fc33bb9SClaudio Fontana                 reg_count,
19881fc33bb9SClaudio Fontana                 reg_values);
19891fc33bb9SClaudio Fontana 
19901fc33bb9SClaudio Fontana             if (FAILED(hr)) {
19911fc33bb9SClaudio Fontana                 error_report("WHPX: Failed to set CpuidAccess state registers,"
19921fc33bb9SClaudio Fontana                              " hr=%08lx", hr);
19931fc33bb9SClaudio Fontana             }
19941fc33bb9SClaudio Fontana             ret = 0;
19951fc33bb9SClaudio Fontana             break;
19961fc33bb9SClaudio Fontana         }
1997d7482ffeSIvan Shcherbakov         case WHvRunVpExitReasonException:
1998d7482ffeSIvan Shcherbakov             whpx_get_registers(cpu);
1999d7482ffeSIvan Shcherbakov 
2000d7482ffeSIvan Shcherbakov             if ((vcpu->exit_ctx.VpException.ExceptionType ==
2001d7482ffeSIvan Shcherbakov                  WHvX64ExceptionTypeDebugTrapOrFault) &&
2002d7482ffeSIvan Shcherbakov                 (vcpu->exit_ctx.VpException.InstructionByteCount >= 1) &&
2003d7482ffeSIvan Shcherbakov                 (vcpu->exit_ctx.VpException.InstructionBytes[0] ==
2004d7482ffeSIvan Shcherbakov                  whpx_breakpoint_instruction)) {
2005d7482ffeSIvan Shcherbakov                 /* Stopped at a software breakpoint. */
2006d7482ffeSIvan Shcherbakov                 cpu->exception_index = EXCP_DEBUG;
2007d7482ffeSIvan Shcherbakov             } else if ((vcpu->exit_ctx.VpException.ExceptionType ==
2008d7482ffeSIvan Shcherbakov                         WHvX64ExceptionTypeDebugTrapOrFault) &&
2009d7482ffeSIvan Shcherbakov                        !cpu->singlestep_enabled) {
2010d7482ffeSIvan Shcherbakov                 /*
2011d7482ffeSIvan Shcherbakov                  * Just finished stepping over a breakpoint, but the
2012d7482ffeSIvan Shcherbakov                  * gdb does not expect us to do single-stepping.
2013d7482ffeSIvan Shcherbakov                  * Don't do anything special.
2014d7482ffeSIvan Shcherbakov                  */
2015d7482ffeSIvan Shcherbakov                 cpu->exception_index = EXCP_INTERRUPT;
2016d7482ffeSIvan Shcherbakov             } else {
2017d7482ffeSIvan Shcherbakov                 /* Another exception or debug event. Report it to GDB. */
2018d7482ffeSIvan Shcherbakov                 cpu->exception_index = EXCP_DEBUG;
2019d7482ffeSIvan Shcherbakov             }
2020d7482ffeSIvan Shcherbakov 
2021d7482ffeSIvan Shcherbakov             ret = 1;
2022d7482ffeSIvan Shcherbakov             break;
20231fc33bb9SClaudio Fontana         case WHvRunVpExitReasonNone:
20241fc33bb9SClaudio Fontana         case WHvRunVpExitReasonUnrecoverableException:
20251fc33bb9SClaudio Fontana         case WHvRunVpExitReasonInvalidVpRegisterValue:
20261fc33bb9SClaudio Fontana         case WHvRunVpExitReasonUnsupportedFeature:
20271fc33bb9SClaudio Fontana         default:
20281fc33bb9SClaudio Fontana             error_report("WHPX: Unexpected VP exit code %d",
20291fc33bb9SClaudio Fontana                          vcpu->exit_ctx.ExitReason);
20301fc33bb9SClaudio Fontana             whpx_get_registers(cpu);
20311fc33bb9SClaudio Fontana             qemu_mutex_lock_iothread();
20321fc33bb9SClaudio Fontana             qemu_system_guest_panicked(cpu_get_crash_info(cpu));
20331fc33bb9SClaudio Fontana             qemu_mutex_unlock_iothread();
20341fc33bb9SClaudio Fontana             break;
20351fc33bb9SClaudio Fontana         }
20361fc33bb9SClaudio Fontana 
20371fc33bb9SClaudio Fontana     } while (!ret);
20381fc33bb9SClaudio Fontana 
2039d7482ffeSIvan Shcherbakov     if (stepped_over_bp) {
2040d7482ffeSIvan Shcherbakov         /* Restore the breakpoint we stepped over */
2041d7482ffeSIvan Shcherbakov         cpu_memory_rw_debug(cpu,
2042d7482ffeSIvan Shcherbakov             stepped_over_bp->address,
2043d7482ffeSIvan Shcherbakov             (void *)&whpx_breakpoint_instruction,
2044d7482ffeSIvan Shcherbakov             1,
2045d7482ffeSIvan Shcherbakov             true);
2046d7482ffeSIvan Shcherbakov     }
2047d7482ffeSIvan Shcherbakov 
2048d7482ffeSIvan Shcherbakov     if (exclusive_step_mode != WHPX_STEP_NONE) {
2049d7482ffeSIvan Shcherbakov         g_assert(cpu_in_exclusive_context(cpu));
2050d7482ffeSIvan Shcherbakov         cpu->running = false;
2051d7482ffeSIvan Shcherbakov         end_exclusive();
2052d7482ffeSIvan Shcherbakov 
2053d7482ffeSIvan Shcherbakov         exclusive_step_mode = WHPX_STEP_NONE;
2054d7482ffeSIvan Shcherbakov     } else {
20551fc33bb9SClaudio Fontana         cpu_exec_end(cpu);
2056d7482ffeSIvan Shcherbakov     }
2057d7482ffeSIvan Shcherbakov 
20581fc33bb9SClaudio Fontana     qemu_mutex_lock_iothread();
20591fc33bb9SClaudio Fontana     current_cpu = cpu;
20601fc33bb9SClaudio Fontana 
2061d7482ffeSIvan Shcherbakov     if (--whpx->running_cpus == 0) {
2062d7482ffeSIvan Shcherbakov         whpx_last_vcpu_stopping(cpu);
2063d7482ffeSIvan Shcherbakov     }
2064d7482ffeSIvan Shcherbakov 
20651fc33bb9SClaudio Fontana     qatomic_set(&cpu->exit_request, false);
20661fc33bb9SClaudio Fontana 
20671fc33bb9SClaudio Fontana     return ret < 0;
20681fc33bb9SClaudio Fontana }
20691fc33bb9SClaudio Fontana 
20701fc33bb9SClaudio Fontana static void do_whpx_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg)
20711fc33bb9SClaudio Fontana {
20721fc33bb9SClaudio Fontana     if (!cpu->vcpu_dirty) {
20731fc33bb9SClaudio Fontana         whpx_get_registers(cpu);
20741fc33bb9SClaudio Fontana         cpu->vcpu_dirty = true;
20751fc33bb9SClaudio Fontana     }
20761fc33bb9SClaudio Fontana }
20771fc33bb9SClaudio Fontana 
20781fc33bb9SClaudio Fontana static void do_whpx_cpu_synchronize_post_reset(CPUState *cpu,
20791fc33bb9SClaudio Fontana                                                run_on_cpu_data arg)
20801fc33bb9SClaudio Fontana {
20811fc33bb9SClaudio Fontana     whpx_set_registers(cpu, WHPX_SET_RESET_STATE);
20821fc33bb9SClaudio Fontana     cpu->vcpu_dirty = false;
20831fc33bb9SClaudio Fontana }
20841fc33bb9SClaudio Fontana 
20851fc33bb9SClaudio Fontana static void do_whpx_cpu_synchronize_post_init(CPUState *cpu,
20861fc33bb9SClaudio Fontana                                               run_on_cpu_data arg)
20871fc33bb9SClaudio Fontana {
20881fc33bb9SClaudio Fontana     whpx_set_registers(cpu, WHPX_SET_FULL_STATE);
20891fc33bb9SClaudio Fontana     cpu->vcpu_dirty = false;
20901fc33bb9SClaudio Fontana }
20911fc33bb9SClaudio Fontana 
20921fc33bb9SClaudio Fontana static void do_whpx_cpu_synchronize_pre_loadvm(CPUState *cpu,
20931fc33bb9SClaudio Fontana                                                run_on_cpu_data arg)
20941fc33bb9SClaudio Fontana {
20951fc33bb9SClaudio Fontana     cpu->vcpu_dirty = true;
20961fc33bb9SClaudio Fontana }
20971fc33bb9SClaudio Fontana 
20981fc33bb9SClaudio Fontana /*
20991fc33bb9SClaudio Fontana  * CPU support.
21001fc33bb9SClaudio Fontana  */
21011fc33bb9SClaudio Fontana 
21021fc33bb9SClaudio Fontana void whpx_cpu_synchronize_state(CPUState *cpu)
21031fc33bb9SClaudio Fontana {
21041fc33bb9SClaudio Fontana     if (!cpu->vcpu_dirty) {
21051fc33bb9SClaudio Fontana         run_on_cpu(cpu, do_whpx_cpu_synchronize_state, RUN_ON_CPU_NULL);
21061fc33bb9SClaudio Fontana     }
21071fc33bb9SClaudio Fontana }
21081fc33bb9SClaudio Fontana 
21091fc33bb9SClaudio Fontana void whpx_cpu_synchronize_post_reset(CPUState *cpu)
21101fc33bb9SClaudio Fontana {
21111fc33bb9SClaudio Fontana     run_on_cpu(cpu, do_whpx_cpu_synchronize_post_reset, RUN_ON_CPU_NULL);
21121fc33bb9SClaudio Fontana }
21131fc33bb9SClaudio Fontana 
21141fc33bb9SClaudio Fontana void whpx_cpu_synchronize_post_init(CPUState *cpu)
21151fc33bb9SClaudio Fontana {
21161fc33bb9SClaudio Fontana     run_on_cpu(cpu, do_whpx_cpu_synchronize_post_init, RUN_ON_CPU_NULL);
21171fc33bb9SClaudio Fontana }
21181fc33bb9SClaudio Fontana 
21191fc33bb9SClaudio Fontana void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu)
21201fc33bb9SClaudio Fontana {
21211fc33bb9SClaudio Fontana     run_on_cpu(cpu, do_whpx_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL);
21221fc33bb9SClaudio Fontana }
21231fc33bb9SClaudio Fontana 
2124d7482ffeSIvan Shcherbakov void whpx_cpu_synchronize_pre_resume(bool step_pending)
2125d7482ffeSIvan Shcherbakov {
2126d7482ffeSIvan Shcherbakov     whpx_global.step_pending = step_pending;
2127d7482ffeSIvan Shcherbakov }
2128d7482ffeSIvan Shcherbakov 
21291fc33bb9SClaudio Fontana /*
21301fc33bb9SClaudio Fontana  * Vcpu support.
21311fc33bb9SClaudio Fontana  */
21321fc33bb9SClaudio Fontana 
21331fc33bb9SClaudio Fontana static Error *whpx_migration_blocker;
21341fc33bb9SClaudio Fontana 
2135538f0497SPhilippe Mathieu-Daudé static void whpx_cpu_update_state(void *opaque, bool running, RunState state)
21361fc33bb9SClaudio Fontana {
21371fc33bb9SClaudio Fontana     CPUX86State *env = opaque;
21381fc33bb9SClaudio Fontana 
21391fc33bb9SClaudio Fontana     if (running) {
21401fc33bb9SClaudio Fontana         env->tsc_valid = false;
21411fc33bb9SClaudio Fontana     }
21421fc33bb9SClaudio Fontana }
21431fc33bb9SClaudio Fontana 
21441fc33bb9SClaudio Fontana int whpx_init_vcpu(CPUState *cpu)
21451fc33bb9SClaudio Fontana {
21461fc33bb9SClaudio Fontana     HRESULT hr;
21471fc33bb9SClaudio Fontana     struct whpx_state *whpx = &whpx_global;
2148b4f879a4SPhilippe Mathieu-Daudé     AccelCPUState *vcpu = NULL;
21491fc33bb9SClaudio Fontana     Error *local_error = NULL;
215095e862d7SPhilippe Mathieu-Daudé     CPUX86State *env = cpu->env_ptr;
21511fc33bb9SClaudio Fontana     X86CPU *x86_cpu = X86_CPU(cpu);
21521fc33bb9SClaudio Fontana     UINT64 freq = 0;
21531fc33bb9SClaudio Fontana     int ret;
21541fc33bb9SClaudio Fontana 
21551fc33bb9SClaudio Fontana     /* Add migration blockers for all unsupported features of the
21561fc33bb9SClaudio Fontana      * Windows Hypervisor Platform
21571fc33bb9SClaudio Fontana      */
21581fc33bb9SClaudio Fontana     if (whpx_migration_blocker == NULL) {
21591fc33bb9SClaudio Fontana         error_setg(&whpx_migration_blocker,
21601fc33bb9SClaudio Fontana                "State blocked due to non-migratable CPUID feature support,"
21611fc33bb9SClaudio Fontana                "dirty memory tracking support, and XSAVE/XRSTOR support");
21621fc33bb9SClaudio Fontana 
2163436c831aSMarkus Armbruster         if (migrate_add_blocker(whpx_migration_blocker, &local_error) < 0) {
21641fc33bb9SClaudio Fontana             error_report_err(local_error);
21651fc33bb9SClaudio Fontana             error_free(whpx_migration_blocker);
21661fc33bb9SClaudio Fontana             ret = -EINVAL;
21671fc33bb9SClaudio Fontana             goto error;
21681fc33bb9SClaudio Fontana         }
21691fc33bb9SClaudio Fontana     }
21701fc33bb9SClaudio Fontana 
2171b4f879a4SPhilippe Mathieu-Daudé     vcpu = g_new0(AccelCPUState, 1);
21721fc33bb9SClaudio Fontana 
21731fc33bb9SClaudio Fontana     hr = whp_dispatch.WHvEmulatorCreateEmulator(
21741fc33bb9SClaudio Fontana         &whpx_emu_callbacks,
21751fc33bb9SClaudio Fontana         &vcpu->emulator);
21761fc33bb9SClaudio Fontana     if (FAILED(hr)) {
21771fc33bb9SClaudio Fontana         error_report("WHPX: Failed to setup instruction completion support,"
21781fc33bb9SClaudio Fontana                      " hr=%08lx", hr);
21791fc33bb9SClaudio Fontana         ret = -EINVAL;
21801fc33bb9SClaudio Fontana         goto error;
21811fc33bb9SClaudio Fontana     }
21821fc33bb9SClaudio Fontana 
21831fc33bb9SClaudio Fontana     hr = whp_dispatch.WHvCreateVirtualProcessor(
21841fc33bb9SClaudio Fontana         whpx->partition, cpu->cpu_index, 0);
21851fc33bb9SClaudio Fontana     if (FAILED(hr)) {
21861fc33bb9SClaudio Fontana         error_report("WHPX: Failed to create a virtual processor,"
21871fc33bb9SClaudio Fontana                      " hr=%08lx", hr);
21881fc33bb9SClaudio Fontana         whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator);
21891fc33bb9SClaudio Fontana         ret = -EINVAL;
21901fc33bb9SClaudio Fontana         goto error;
21911fc33bb9SClaudio Fontana     }
21921fc33bb9SClaudio Fontana 
21931fc33bb9SClaudio Fontana     /*
21941fc33bb9SClaudio Fontana      * vcpu's TSC frequency is either specified by user, or use the value
21951fc33bb9SClaudio Fontana      * provided by Hyper-V if the former is not present. In the latter case, we
21961fc33bb9SClaudio Fontana      * query it from Hyper-V and record in env->tsc_khz, so that vcpu's TSC
21971fc33bb9SClaudio Fontana      * frequency can be migrated later via this field.
21981fc33bb9SClaudio Fontana      */
21991fc33bb9SClaudio Fontana     if (!env->tsc_khz) {
22001fc33bb9SClaudio Fontana         hr = whp_dispatch.WHvGetCapability(
22011fc33bb9SClaudio Fontana             WHvCapabilityCodeProcessorClockFrequency, &freq, sizeof(freq),
22021fc33bb9SClaudio Fontana                 NULL);
22031fc33bb9SClaudio Fontana         if (hr != WHV_E_UNKNOWN_CAPABILITY) {
22041fc33bb9SClaudio Fontana             if (FAILED(hr)) {
22051fc33bb9SClaudio Fontana                 printf("WHPX: Failed to query tsc frequency, hr=0x%08lx\n", hr);
22061fc33bb9SClaudio Fontana             } else {
22071fc33bb9SClaudio Fontana                 env->tsc_khz = freq / 1000; /* Hz to KHz */
22081fc33bb9SClaudio Fontana             }
22091fc33bb9SClaudio Fontana         }
22101fc33bb9SClaudio Fontana     }
22111fc33bb9SClaudio Fontana 
22121fc33bb9SClaudio Fontana     env->apic_bus_freq = HYPERV_APIC_BUS_FREQUENCY;
22131fc33bb9SClaudio Fontana     hr = whp_dispatch.WHvGetCapability(
22141fc33bb9SClaudio Fontana         WHvCapabilityCodeInterruptClockFrequency, &freq, sizeof(freq), NULL);
22151fc33bb9SClaudio Fontana     if (hr != WHV_E_UNKNOWN_CAPABILITY) {
22161fc33bb9SClaudio Fontana         if (FAILED(hr)) {
22171fc33bb9SClaudio Fontana             printf("WHPX: Failed to query apic bus frequency hr=0x%08lx\n", hr);
22181fc33bb9SClaudio Fontana         } else {
22191fc33bb9SClaudio Fontana             env->apic_bus_freq = freq;
22201fc33bb9SClaudio Fontana         }
22211fc33bb9SClaudio Fontana     }
22221fc33bb9SClaudio Fontana 
22231fc33bb9SClaudio Fontana     /*
22241fc33bb9SClaudio Fontana      * If the vmware cpuid frequency leaf option is set, and we have a valid
22251fc33bb9SClaudio Fontana      * tsc value, trap the corresponding cpuid's.
22261fc33bb9SClaudio Fontana      */
22271fc33bb9SClaudio Fontana     if (x86_cpu->vmware_cpuid_freq && env->tsc_khz) {
22281fc33bb9SClaudio Fontana         UINT32 cpuidExitList[] = {1, 0x80000001, 0x40000000, 0x40000010};
22291fc33bb9SClaudio Fontana 
22301fc33bb9SClaudio Fontana         hr = whp_dispatch.WHvSetPartitionProperty(
22311fc33bb9SClaudio Fontana                 whpx->partition,
22321fc33bb9SClaudio Fontana                 WHvPartitionPropertyCodeCpuidExitList,
22331fc33bb9SClaudio Fontana                 cpuidExitList,
22341fc33bb9SClaudio Fontana                 RTL_NUMBER_OF(cpuidExitList) * sizeof(UINT32));
22351fc33bb9SClaudio Fontana 
22361fc33bb9SClaudio Fontana         if (FAILED(hr)) {
22371fc33bb9SClaudio Fontana             error_report("WHPX: Failed to set partition CpuidExitList hr=%08lx",
22381fc33bb9SClaudio Fontana                         hr);
22391fc33bb9SClaudio Fontana             ret = -EINVAL;
22401fc33bb9SClaudio Fontana             goto error;
22411fc33bb9SClaudio Fontana         }
22421fc33bb9SClaudio Fontana     }
22431fc33bb9SClaudio Fontana 
22441fc33bb9SClaudio Fontana     vcpu->interruptable = true;
22451fc33bb9SClaudio Fontana     cpu->vcpu_dirty = true;
2246f861b3f3SPhilippe Mathieu-Daudé     cpu->accel = vcpu;
22471fc33bb9SClaudio Fontana     max_vcpu_index = max(max_vcpu_index, cpu->cpu_index);
22481fc33bb9SClaudio Fontana     qemu_add_vm_change_state_handler(whpx_cpu_update_state, cpu->env_ptr);
22491fc33bb9SClaudio Fontana 
22501fc33bb9SClaudio Fontana     return 0;
22511fc33bb9SClaudio Fontana 
22521fc33bb9SClaudio Fontana error:
22531fc33bb9SClaudio Fontana     g_free(vcpu);
22541fc33bb9SClaudio Fontana 
22551fc33bb9SClaudio Fontana     return ret;
22561fc33bb9SClaudio Fontana }
22571fc33bb9SClaudio Fontana 
22581fc33bb9SClaudio Fontana int whpx_vcpu_exec(CPUState *cpu)
22591fc33bb9SClaudio Fontana {
22601fc33bb9SClaudio Fontana     int ret;
22611fc33bb9SClaudio Fontana     int fatal;
22621fc33bb9SClaudio Fontana 
22631fc33bb9SClaudio Fontana     for (;;) {
22641fc33bb9SClaudio Fontana         if (cpu->exception_index >= EXCP_INTERRUPT) {
22651fc33bb9SClaudio Fontana             ret = cpu->exception_index;
22661fc33bb9SClaudio Fontana             cpu->exception_index = -1;
22671fc33bb9SClaudio Fontana             break;
22681fc33bb9SClaudio Fontana         }
22691fc33bb9SClaudio Fontana 
22701fc33bb9SClaudio Fontana         fatal = whpx_vcpu_run(cpu);
22711fc33bb9SClaudio Fontana 
22721fc33bb9SClaudio Fontana         if (fatal) {
22731fc33bb9SClaudio Fontana             error_report("WHPX: Failed to exec a virtual processor");
22741fc33bb9SClaudio Fontana             abort();
22751fc33bb9SClaudio Fontana         }
22761fc33bb9SClaudio Fontana     }
22771fc33bb9SClaudio Fontana 
22781fc33bb9SClaudio Fontana     return ret;
22791fc33bb9SClaudio Fontana }
22801fc33bb9SClaudio Fontana 
22811fc33bb9SClaudio Fontana void whpx_destroy_vcpu(CPUState *cpu)
22821fc33bb9SClaudio Fontana {
22831fc33bb9SClaudio Fontana     struct whpx_state *whpx = &whpx_global;
2284441f2449SPhilippe Mathieu-Daudé     AccelCPUState *vcpu = cpu->accel;
22851fc33bb9SClaudio Fontana 
22861fc33bb9SClaudio Fontana     whp_dispatch.WHvDeleteVirtualProcessor(whpx->partition, cpu->cpu_index);
22871fc33bb9SClaudio Fontana     whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator);
22886ecd2cd0SPhilippe Mathieu-Daudé     g_free(cpu->accel);
22891fc33bb9SClaudio Fontana     return;
22901fc33bb9SClaudio Fontana }
22911fc33bb9SClaudio Fontana 
22921fc33bb9SClaudio Fontana void whpx_vcpu_kick(CPUState *cpu)
22931fc33bb9SClaudio Fontana {
22941fc33bb9SClaudio Fontana     struct whpx_state *whpx = &whpx_global;
22951fc33bb9SClaudio Fontana     whp_dispatch.WHvCancelRunVirtualProcessor(
22961fc33bb9SClaudio Fontana         whpx->partition, cpu->cpu_index, 0);
22971fc33bb9SClaudio Fontana }
22981fc33bb9SClaudio Fontana 
22991fc33bb9SClaudio Fontana /*
23001fc33bb9SClaudio Fontana  * Memory support.
23011fc33bb9SClaudio Fontana  */
23021fc33bb9SClaudio Fontana 
23031fc33bb9SClaudio Fontana static void whpx_update_mapping(hwaddr start_pa, ram_addr_t size,
23041fc33bb9SClaudio Fontana                                 void *host_va, int add, int rom,
23051fc33bb9SClaudio Fontana                                 const char *name)
23061fc33bb9SClaudio Fontana {
23071fc33bb9SClaudio Fontana     struct whpx_state *whpx = &whpx_global;
23081fc33bb9SClaudio Fontana     HRESULT hr;
23091fc33bb9SClaudio Fontana 
23101fc33bb9SClaudio Fontana     /*
23111fc33bb9SClaudio Fontana     if (add) {
23121fc33bb9SClaudio Fontana         printf("WHPX: ADD PA:%p Size:%p, Host:%p, %s, '%s'\n",
23131fc33bb9SClaudio Fontana                (void*)start_pa, (void*)size, host_va,
23141fc33bb9SClaudio Fontana                (rom ? "ROM" : "RAM"), name);
23151fc33bb9SClaudio Fontana     } else {
23161fc33bb9SClaudio Fontana         printf("WHPX: DEL PA:%p Size:%p, Host:%p,      '%s'\n",
23171fc33bb9SClaudio Fontana                (void*)start_pa, (void*)size, host_va, name);
23181fc33bb9SClaudio Fontana     }
23191fc33bb9SClaudio Fontana     */
23201fc33bb9SClaudio Fontana 
23211fc33bb9SClaudio Fontana     if (add) {
23221fc33bb9SClaudio Fontana         hr = whp_dispatch.WHvMapGpaRange(whpx->partition,
23231fc33bb9SClaudio Fontana                                          host_va,
23241fc33bb9SClaudio Fontana                                          start_pa,
23251fc33bb9SClaudio Fontana                                          size,
23261fc33bb9SClaudio Fontana                                          (WHvMapGpaRangeFlagRead |
23271fc33bb9SClaudio Fontana                                           WHvMapGpaRangeFlagExecute |
23281fc33bb9SClaudio Fontana                                           (rom ? 0 : WHvMapGpaRangeFlagWrite)));
23291fc33bb9SClaudio Fontana     } else {
23301fc33bb9SClaudio Fontana         hr = whp_dispatch.WHvUnmapGpaRange(whpx->partition,
23311fc33bb9SClaudio Fontana                                            start_pa,
23321fc33bb9SClaudio Fontana                                            size);
23331fc33bb9SClaudio Fontana     }
23341fc33bb9SClaudio Fontana 
23351fc33bb9SClaudio Fontana     if (FAILED(hr)) {
23361fc33bb9SClaudio Fontana         error_report("WHPX: Failed to %s GPA range '%s' PA:%p, Size:%p bytes,"
23371fc33bb9SClaudio Fontana                      " Host:%p, hr=%08lx",
23381fc33bb9SClaudio Fontana                      (add ? "MAP" : "UNMAP"), name,
23391fc33bb9SClaudio Fontana                      (void *)(uintptr_t)start_pa, (void *)size, host_va, hr);
23401fc33bb9SClaudio Fontana     }
23411fc33bb9SClaudio Fontana }
23421fc33bb9SClaudio Fontana 
23431fc33bb9SClaudio Fontana static void whpx_process_section(MemoryRegionSection *section, int add)
23441fc33bb9SClaudio Fontana {
23451fc33bb9SClaudio Fontana     MemoryRegion *mr = section->mr;
23461fc33bb9SClaudio Fontana     hwaddr start_pa = section->offset_within_address_space;
23471fc33bb9SClaudio Fontana     ram_addr_t size = int128_get64(section->size);
23481fc33bb9SClaudio Fontana     unsigned int delta;
23491fc33bb9SClaudio Fontana     uint64_t host_va;
23501fc33bb9SClaudio Fontana 
23511fc33bb9SClaudio Fontana     if (!memory_region_is_ram(mr)) {
23521fc33bb9SClaudio Fontana         return;
23531fc33bb9SClaudio Fontana     }
23541fc33bb9SClaudio Fontana 
23558e3b0cbbSMarc-André Lureau     delta = qemu_real_host_page_size() - (start_pa & ~qemu_real_host_page_mask());
23568e3b0cbbSMarc-André Lureau     delta &= ~qemu_real_host_page_mask();
23571fc33bb9SClaudio Fontana     if (delta > size) {
23581fc33bb9SClaudio Fontana         return;
23591fc33bb9SClaudio Fontana     }
23601fc33bb9SClaudio Fontana     start_pa += delta;
23611fc33bb9SClaudio Fontana     size -= delta;
23628e3b0cbbSMarc-André Lureau     size &= qemu_real_host_page_mask();
23638e3b0cbbSMarc-André Lureau     if (!size || (start_pa & ~qemu_real_host_page_mask())) {
23641fc33bb9SClaudio Fontana         return;
23651fc33bb9SClaudio Fontana     }
23661fc33bb9SClaudio Fontana 
23671fc33bb9SClaudio Fontana     host_va = (uintptr_t)memory_region_get_ram_ptr(mr)
23681fc33bb9SClaudio Fontana             + section->offset_within_region + delta;
23691fc33bb9SClaudio Fontana 
23701fc33bb9SClaudio Fontana     whpx_update_mapping(start_pa, size, (void *)(uintptr_t)host_va, add,
23711fc33bb9SClaudio Fontana                         memory_region_is_rom(mr), mr->name);
23721fc33bb9SClaudio Fontana }
23731fc33bb9SClaudio Fontana 
23741fc33bb9SClaudio Fontana static void whpx_region_add(MemoryListener *listener,
23751fc33bb9SClaudio Fontana                            MemoryRegionSection *section)
23761fc33bb9SClaudio Fontana {
23771fc33bb9SClaudio Fontana     memory_region_ref(section->mr);
23781fc33bb9SClaudio Fontana     whpx_process_section(section, 1);
23791fc33bb9SClaudio Fontana }
23801fc33bb9SClaudio Fontana 
23811fc33bb9SClaudio Fontana static void whpx_region_del(MemoryListener *listener,
23821fc33bb9SClaudio Fontana                            MemoryRegionSection *section)
23831fc33bb9SClaudio Fontana {
23841fc33bb9SClaudio Fontana     whpx_process_section(section, 0);
23851fc33bb9SClaudio Fontana     memory_region_unref(section->mr);
23861fc33bb9SClaudio Fontana }
23871fc33bb9SClaudio Fontana 
23881fc33bb9SClaudio Fontana static void whpx_transaction_begin(MemoryListener *listener)
23891fc33bb9SClaudio Fontana {
23901fc33bb9SClaudio Fontana }
23911fc33bb9SClaudio Fontana 
23921fc33bb9SClaudio Fontana static void whpx_transaction_commit(MemoryListener *listener)
23931fc33bb9SClaudio Fontana {
23941fc33bb9SClaudio Fontana }
23951fc33bb9SClaudio Fontana 
23961fc33bb9SClaudio Fontana static void whpx_log_sync(MemoryListener *listener,
23971fc33bb9SClaudio Fontana                          MemoryRegionSection *section)
23981fc33bb9SClaudio Fontana {
23991fc33bb9SClaudio Fontana     MemoryRegion *mr = section->mr;
24001fc33bb9SClaudio Fontana 
24011fc33bb9SClaudio Fontana     if (!memory_region_is_ram(mr)) {
24021fc33bb9SClaudio Fontana         return;
24031fc33bb9SClaudio Fontana     }
24041fc33bb9SClaudio Fontana 
24051fc33bb9SClaudio Fontana     memory_region_set_dirty(mr, 0, int128_get64(section->size));
24061fc33bb9SClaudio Fontana }
24071fc33bb9SClaudio Fontana 
24081fc33bb9SClaudio Fontana static MemoryListener whpx_memory_listener = {
2409142518bdSPeter Xu     .name = "whpx",
24101fc33bb9SClaudio Fontana     .begin = whpx_transaction_begin,
24111fc33bb9SClaudio Fontana     .commit = whpx_transaction_commit,
24121fc33bb9SClaudio Fontana     .region_add = whpx_region_add,
24131fc33bb9SClaudio Fontana     .region_del = whpx_region_del,
24141fc33bb9SClaudio Fontana     .log_sync = whpx_log_sync,
24151fc33bb9SClaudio Fontana     .priority = 10,
24161fc33bb9SClaudio Fontana };
24171fc33bb9SClaudio Fontana 
24181fc33bb9SClaudio Fontana static void whpx_memory_init(void)
24191fc33bb9SClaudio Fontana {
24201fc33bb9SClaudio Fontana     memory_listener_register(&whpx_memory_listener, &address_space_memory);
24211fc33bb9SClaudio Fontana }
24221fc33bb9SClaudio Fontana 
24231fc33bb9SClaudio Fontana /*
24241fc33bb9SClaudio Fontana  * Load the functions from the given library, using the given handle. If a
24251fc33bb9SClaudio Fontana  * handle is provided, it is used, otherwise the library is opened. The
24261fc33bb9SClaudio Fontana  * handle will be updated on return with the opened one.
24271fc33bb9SClaudio Fontana  */
24281fc33bb9SClaudio Fontana static bool load_whp_dispatch_fns(HMODULE *handle,
24291fc33bb9SClaudio Fontana     WHPFunctionList function_list)
24301fc33bb9SClaudio Fontana {
24311fc33bb9SClaudio Fontana     HMODULE hLib = *handle;
24321fc33bb9SClaudio Fontana 
24331fc33bb9SClaudio Fontana     #define WINHV_PLATFORM_DLL "WinHvPlatform.dll"
24341fc33bb9SClaudio Fontana     #define WINHV_EMULATION_DLL "WinHvEmulation.dll"
24351fc33bb9SClaudio Fontana     #define WHP_LOAD_FIELD_OPTIONAL(return_type, function_name, signature) \
24361fc33bb9SClaudio Fontana         whp_dispatch.function_name = \
24371fc33bb9SClaudio Fontana             (function_name ## _t)GetProcAddress(hLib, #function_name); \
24381fc33bb9SClaudio Fontana 
24391fc33bb9SClaudio Fontana     #define WHP_LOAD_FIELD(return_type, function_name, signature) \
24401fc33bb9SClaudio Fontana         whp_dispatch.function_name = \
24411fc33bb9SClaudio Fontana             (function_name ## _t)GetProcAddress(hLib, #function_name); \
24421fc33bb9SClaudio Fontana         if (!whp_dispatch.function_name) { \
24431fc33bb9SClaudio Fontana             error_report("Could not load function %s", #function_name); \
24441fc33bb9SClaudio Fontana             goto error; \
24451fc33bb9SClaudio Fontana         } \
24461fc33bb9SClaudio Fontana 
24471fc33bb9SClaudio Fontana     #define WHP_LOAD_LIB(lib_name, handle_lib) \
24481fc33bb9SClaudio Fontana     if (!handle_lib) { \
24491fc33bb9SClaudio Fontana         handle_lib = LoadLibrary(lib_name); \
24501fc33bb9SClaudio Fontana         if (!handle_lib) { \
24511fc33bb9SClaudio Fontana             error_report("Could not load library %s.", lib_name); \
24521fc33bb9SClaudio Fontana             goto error; \
24531fc33bb9SClaudio Fontana         } \
24541fc33bb9SClaudio Fontana     } \
24551fc33bb9SClaudio Fontana 
24561fc33bb9SClaudio Fontana     switch (function_list) {
24571fc33bb9SClaudio Fontana     case WINHV_PLATFORM_FNS_DEFAULT:
24581fc33bb9SClaudio Fontana         WHP_LOAD_LIB(WINHV_PLATFORM_DLL, hLib)
24591fc33bb9SClaudio Fontana         LIST_WINHVPLATFORM_FUNCTIONS(WHP_LOAD_FIELD)
24601fc33bb9SClaudio Fontana         break;
24611fc33bb9SClaudio Fontana 
24621fc33bb9SClaudio Fontana     case WINHV_EMULATION_FNS_DEFAULT:
24631fc33bb9SClaudio Fontana         WHP_LOAD_LIB(WINHV_EMULATION_DLL, hLib)
24641fc33bb9SClaudio Fontana         LIST_WINHVEMULATION_FUNCTIONS(WHP_LOAD_FIELD)
24651fc33bb9SClaudio Fontana         break;
24661fc33bb9SClaudio Fontana 
24671fc33bb9SClaudio Fontana     case WINHV_PLATFORM_FNS_SUPPLEMENTAL:
24681fc33bb9SClaudio Fontana         WHP_LOAD_LIB(WINHV_PLATFORM_DLL, hLib)
24691fc33bb9SClaudio Fontana         LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL(WHP_LOAD_FIELD_OPTIONAL)
24701fc33bb9SClaudio Fontana         break;
24711fc33bb9SClaudio Fontana     }
24721fc33bb9SClaudio Fontana 
24731fc33bb9SClaudio Fontana     *handle = hLib;
24741fc33bb9SClaudio Fontana     return true;
24751fc33bb9SClaudio Fontana 
24761fc33bb9SClaudio Fontana error:
24771fc33bb9SClaudio Fontana     if (hLib) {
24781fc33bb9SClaudio Fontana         FreeLibrary(hLib);
24791fc33bb9SClaudio Fontana     }
24801fc33bb9SClaudio Fontana 
24811fc33bb9SClaudio Fontana     return false;
24821fc33bb9SClaudio Fontana }
24831fc33bb9SClaudio Fontana 
24841fc33bb9SClaudio Fontana static void whpx_set_kernel_irqchip(Object *obj, Visitor *v,
24851fc33bb9SClaudio Fontana                                    const char *name, void *opaque,
24861fc33bb9SClaudio Fontana                                    Error **errp)
24871fc33bb9SClaudio Fontana {
24881fc33bb9SClaudio Fontana     struct whpx_state *whpx = &whpx_global;
24891fc33bb9SClaudio Fontana     OnOffSplit mode;
24901fc33bb9SClaudio Fontana 
24911fc33bb9SClaudio Fontana     if (!visit_type_OnOffSplit(v, name, &mode, errp)) {
24921fc33bb9SClaudio Fontana         return;
24931fc33bb9SClaudio Fontana     }
24941fc33bb9SClaudio Fontana 
24951fc33bb9SClaudio Fontana     switch (mode) {
24961fc33bb9SClaudio Fontana     case ON_OFF_SPLIT_ON:
24971fc33bb9SClaudio Fontana         whpx->kernel_irqchip_allowed = true;
24981fc33bb9SClaudio Fontana         whpx->kernel_irqchip_required = true;
24991fc33bb9SClaudio Fontana         break;
25001fc33bb9SClaudio Fontana 
25011fc33bb9SClaudio Fontana     case ON_OFF_SPLIT_OFF:
25021fc33bb9SClaudio Fontana         whpx->kernel_irqchip_allowed = false;
25031fc33bb9SClaudio Fontana         whpx->kernel_irqchip_required = false;
25041fc33bb9SClaudio Fontana         break;
25051fc33bb9SClaudio Fontana 
25061fc33bb9SClaudio Fontana     case ON_OFF_SPLIT_SPLIT:
25071fc33bb9SClaudio Fontana         error_setg(errp, "WHPX: split irqchip currently not supported");
25081fc33bb9SClaudio Fontana         error_append_hint(errp,
25091fc33bb9SClaudio Fontana             "Try without kernel-irqchip or with kernel-irqchip=on|off");
25101fc33bb9SClaudio Fontana         break;
25111fc33bb9SClaudio Fontana 
25121fc33bb9SClaudio Fontana     default:
25131fc33bb9SClaudio Fontana         /*
25141fc33bb9SClaudio Fontana          * The value was checked in visit_type_OnOffSplit() above. If
25151fc33bb9SClaudio Fontana          * we get here, then something is wrong in QEMU.
25161fc33bb9SClaudio Fontana          */
25171fc33bb9SClaudio Fontana         abort();
25181fc33bb9SClaudio Fontana     }
25191fc33bb9SClaudio Fontana }
25201fc33bb9SClaudio Fontana 
25211fc33bb9SClaudio Fontana /*
25221fc33bb9SClaudio Fontana  * Partition support
25231fc33bb9SClaudio Fontana  */
25241fc33bb9SClaudio Fontana 
25251fc33bb9SClaudio Fontana static int whpx_accel_init(MachineState *ms)
25261fc33bb9SClaudio Fontana {
25271fc33bb9SClaudio Fontana     struct whpx_state *whpx;
25281fc33bb9SClaudio Fontana     int ret;
25291fc33bb9SClaudio Fontana     HRESULT hr;
25301fc33bb9SClaudio Fontana     WHV_CAPABILITY whpx_cap;
25311fc33bb9SClaudio Fontana     UINT32 whpx_cap_size;
25321fc33bb9SClaudio Fontana     WHV_PARTITION_PROPERTY prop;
25331fc33bb9SClaudio Fontana     UINT32 cpuidExitList[] = {1, 0x80000001};
25341fc33bb9SClaudio Fontana     WHV_CAPABILITY_FEATURES features = {0};
25351fc33bb9SClaudio Fontana 
25361fc33bb9SClaudio Fontana     whpx = &whpx_global;
25371fc33bb9SClaudio Fontana 
25381fc33bb9SClaudio Fontana     if (!init_whp_dispatch()) {
25391fc33bb9SClaudio Fontana         ret = -ENOSYS;
25401fc33bb9SClaudio Fontana         goto error;
25411fc33bb9SClaudio Fontana     }
25421fc33bb9SClaudio Fontana 
25431fc33bb9SClaudio Fontana     whpx->mem_quota = ms->ram_size;
25441fc33bb9SClaudio Fontana 
25451fc33bb9SClaudio Fontana     hr = whp_dispatch.WHvGetCapability(
25461fc33bb9SClaudio Fontana         WHvCapabilityCodeHypervisorPresent, &whpx_cap,
25471fc33bb9SClaudio Fontana         sizeof(whpx_cap), &whpx_cap_size);
25481fc33bb9SClaudio Fontana     if (FAILED(hr) || !whpx_cap.HypervisorPresent) {
25491fc33bb9SClaudio Fontana         error_report("WHPX: No accelerator found, hr=%08lx", hr);
25501fc33bb9SClaudio Fontana         ret = -ENOSPC;
25511fc33bb9SClaudio Fontana         goto error;
25521fc33bb9SClaudio Fontana     }
25531fc33bb9SClaudio Fontana 
25541fc33bb9SClaudio Fontana     hr = whp_dispatch.WHvGetCapability(
25551fc33bb9SClaudio Fontana         WHvCapabilityCodeFeatures, &features, sizeof(features), NULL);
25561fc33bb9SClaudio Fontana     if (FAILED(hr)) {
25571fc33bb9SClaudio Fontana         error_report("WHPX: Failed to query capabilities, hr=%08lx", hr);
25581fc33bb9SClaudio Fontana         ret = -EINVAL;
25591fc33bb9SClaudio Fontana         goto error;
25601fc33bb9SClaudio Fontana     }
25611fc33bb9SClaudio Fontana 
25621fc33bb9SClaudio Fontana     hr = whp_dispatch.WHvCreatePartition(&whpx->partition);
25631fc33bb9SClaudio Fontana     if (FAILED(hr)) {
25641fc33bb9SClaudio Fontana         error_report("WHPX: Failed to create partition, hr=%08lx", hr);
25651fc33bb9SClaudio Fontana         ret = -EINVAL;
25661fc33bb9SClaudio Fontana         goto error;
25671fc33bb9SClaudio Fontana     }
25681fc33bb9SClaudio Fontana 
2569b6b3da99SSunil Muthuswamy     /*
2570b6b3da99SSunil Muthuswamy      * Query the XSAVE capability of the partition. Any error here is not
2571b6b3da99SSunil Muthuswamy      * considered fatal.
2572b6b3da99SSunil Muthuswamy      */
2573b6b3da99SSunil Muthuswamy     hr = whp_dispatch.WHvGetPartitionProperty(
2574b6b3da99SSunil Muthuswamy         whpx->partition,
2575b6b3da99SSunil Muthuswamy         WHvPartitionPropertyCodeProcessorXsaveFeatures,
2576b6b3da99SSunil Muthuswamy         &whpx_xsave_cap,
2577b6b3da99SSunil Muthuswamy         sizeof(whpx_xsave_cap),
2578b6b3da99SSunil Muthuswamy         &whpx_cap_size);
2579b6b3da99SSunil Muthuswamy 
2580b6b3da99SSunil Muthuswamy     /*
2581b6b3da99SSunil Muthuswamy      * Windows version which don't support this property will return with the
2582b6b3da99SSunil Muthuswamy      * specific error code.
2583b6b3da99SSunil Muthuswamy      */
2584b6b3da99SSunil Muthuswamy     if (FAILED(hr) && hr != WHV_E_UNKNOWN_PROPERTY) {
2585b6b3da99SSunil Muthuswamy         error_report("WHPX: Failed to query XSAVE capability, hr=%08lx", hr);
2586b6b3da99SSunil Muthuswamy     }
2587b6b3da99SSunil Muthuswamy 
2588b6b3da99SSunil Muthuswamy     if (!whpx_has_xsave()) {
2589b6b3da99SSunil Muthuswamy         printf("WHPX: Partition is not XSAVE capable\n");
2590b6b3da99SSunil Muthuswamy     }
2591b6b3da99SSunil Muthuswamy 
25921fc33bb9SClaudio Fontana     memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
25931fc33bb9SClaudio Fontana     prop.ProcessorCount = ms->smp.cpus;
25941fc33bb9SClaudio Fontana     hr = whp_dispatch.WHvSetPartitionProperty(
25951fc33bb9SClaudio Fontana         whpx->partition,
25961fc33bb9SClaudio Fontana         WHvPartitionPropertyCodeProcessorCount,
25971fc33bb9SClaudio Fontana         &prop,
25981fc33bb9SClaudio Fontana         sizeof(WHV_PARTITION_PROPERTY));
25991fc33bb9SClaudio Fontana 
26001fc33bb9SClaudio Fontana     if (FAILED(hr)) {
2601*db5a06b3SZhao Liu         error_report("WHPX: Failed to set partition processor count to %u,"
2602*db5a06b3SZhao Liu                      " hr=%08lx", prop.ProcessorCount, hr);
26031fc33bb9SClaudio Fontana         ret = -EINVAL;
26041fc33bb9SClaudio Fontana         goto error;
26051fc33bb9SClaudio Fontana     }
26061fc33bb9SClaudio Fontana 
26071fc33bb9SClaudio Fontana     /*
26081fc33bb9SClaudio Fontana      * Error out if WHP doesn't support apic emulation and user is requiring
26091fc33bb9SClaudio Fontana      * it.
26101fc33bb9SClaudio Fontana      */
26111fc33bb9SClaudio Fontana     if (whpx->kernel_irqchip_required && (!features.LocalApicEmulation ||
26121fc33bb9SClaudio Fontana             !whp_dispatch.WHvSetVirtualProcessorInterruptControllerState2)) {
26131fc33bb9SClaudio Fontana         error_report("WHPX: kernel irqchip requested, but unavailable. "
26141fc33bb9SClaudio Fontana             "Try without kernel-irqchip or with kernel-irqchip=off");
26151fc33bb9SClaudio Fontana         ret = -EINVAL;
26161fc33bb9SClaudio Fontana         goto error;
26171fc33bb9SClaudio Fontana     }
26181fc33bb9SClaudio Fontana 
26191fc33bb9SClaudio Fontana     if (whpx->kernel_irqchip_allowed && features.LocalApicEmulation &&
26201fc33bb9SClaudio Fontana         whp_dispatch.WHvSetVirtualProcessorInterruptControllerState2) {
26211fc33bb9SClaudio Fontana         WHV_X64_LOCAL_APIC_EMULATION_MODE mode =
26221fc33bb9SClaudio Fontana             WHvX64LocalApicEmulationModeXApic;
26231fc33bb9SClaudio Fontana         printf("WHPX: setting APIC emulation mode in the hypervisor\n");
26241fc33bb9SClaudio Fontana         hr = whp_dispatch.WHvSetPartitionProperty(
26251fc33bb9SClaudio Fontana             whpx->partition,
26261fc33bb9SClaudio Fontana             WHvPartitionPropertyCodeLocalApicEmulationMode,
26271fc33bb9SClaudio Fontana             &mode,
26281fc33bb9SClaudio Fontana             sizeof(mode));
26291fc33bb9SClaudio Fontana         if (FAILED(hr)) {
26301fc33bb9SClaudio Fontana             error_report("WHPX: Failed to enable kernel irqchip hr=%08lx", hr);
26311fc33bb9SClaudio Fontana             if (whpx->kernel_irqchip_required) {
26321fc33bb9SClaudio Fontana                 error_report("WHPX: kernel irqchip requested, but unavailable");
26331fc33bb9SClaudio Fontana                 ret = -EINVAL;
26341fc33bb9SClaudio Fontana                 goto error;
26351fc33bb9SClaudio Fontana             }
26361fc33bb9SClaudio Fontana         } else {
26371fc33bb9SClaudio Fontana             whpx->apic_in_platform = true;
26381fc33bb9SClaudio Fontana         }
26391fc33bb9SClaudio Fontana     }
26401fc33bb9SClaudio Fontana 
26411fc33bb9SClaudio Fontana     /* Register for MSR and CPUID exits */
26421fc33bb9SClaudio Fontana     memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
26431fc33bb9SClaudio Fontana     prop.ExtendedVmExits.X64MsrExit = 1;
26441fc33bb9SClaudio Fontana     prop.ExtendedVmExits.X64CpuidExit = 1;
2645d7482ffeSIvan Shcherbakov     prop.ExtendedVmExits.ExceptionExit = 1;
26461fc33bb9SClaudio Fontana     if (whpx_apic_in_platform()) {
26471fc33bb9SClaudio Fontana         prop.ExtendedVmExits.X64ApicInitSipiExitTrap = 1;
26481fc33bb9SClaudio Fontana     }
26491fc33bb9SClaudio Fontana 
26501fc33bb9SClaudio Fontana     hr = whp_dispatch.WHvSetPartitionProperty(
26511fc33bb9SClaudio Fontana             whpx->partition,
26521fc33bb9SClaudio Fontana             WHvPartitionPropertyCodeExtendedVmExits,
26531fc33bb9SClaudio Fontana             &prop,
26541fc33bb9SClaudio Fontana             sizeof(WHV_PARTITION_PROPERTY));
26551fc33bb9SClaudio Fontana     if (FAILED(hr)) {
26561fc33bb9SClaudio Fontana         error_report("WHPX: Failed to enable MSR & CPUIDexit, hr=%08lx", hr);
26571fc33bb9SClaudio Fontana         ret = -EINVAL;
26581fc33bb9SClaudio Fontana         goto error;
26591fc33bb9SClaudio Fontana     }
26601fc33bb9SClaudio Fontana 
26611fc33bb9SClaudio Fontana     hr = whp_dispatch.WHvSetPartitionProperty(
26621fc33bb9SClaudio Fontana         whpx->partition,
26631fc33bb9SClaudio Fontana         WHvPartitionPropertyCodeCpuidExitList,
26641fc33bb9SClaudio Fontana         cpuidExitList,
26651fc33bb9SClaudio Fontana         RTL_NUMBER_OF(cpuidExitList) * sizeof(UINT32));
26661fc33bb9SClaudio Fontana 
26671fc33bb9SClaudio Fontana     if (FAILED(hr)) {
26681fc33bb9SClaudio Fontana         error_report("WHPX: Failed to set partition CpuidExitList hr=%08lx",
26691fc33bb9SClaudio Fontana                      hr);
26701fc33bb9SClaudio Fontana         ret = -EINVAL;
26711fc33bb9SClaudio Fontana         goto error;
26721fc33bb9SClaudio Fontana     }
26731fc33bb9SClaudio Fontana 
2674d7482ffeSIvan Shcherbakov     /*
2675d7482ffeSIvan Shcherbakov      * We do not want to intercept any exceptions from the guest,
2676d7482ffeSIvan Shcherbakov      * until we actually start debugging with gdb.
2677d7482ffeSIvan Shcherbakov      */
2678d7482ffeSIvan Shcherbakov     whpx->exception_exit_bitmap = -1;
2679d7482ffeSIvan Shcherbakov     hr = whpx_set_exception_exit_bitmap(0);
2680d7482ffeSIvan Shcherbakov 
2681d7482ffeSIvan Shcherbakov     if (FAILED(hr)) {
2682d7482ffeSIvan Shcherbakov         error_report("WHPX: Failed to set exception exit bitmap, hr=%08lx", hr);
2683d7482ffeSIvan Shcherbakov         ret = -EINVAL;
2684d7482ffeSIvan Shcherbakov         goto error;
2685d7482ffeSIvan Shcherbakov     }
2686d7482ffeSIvan Shcherbakov 
26871fc33bb9SClaudio Fontana     hr = whp_dispatch.WHvSetupPartition(whpx->partition);
26881fc33bb9SClaudio Fontana     if (FAILED(hr)) {
26891fc33bb9SClaudio Fontana         error_report("WHPX: Failed to setup partition, hr=%08lx", hr);
26901fc33bb9SClaudio Fontana         ret = -EINVAL;
26911fc33bb9SClaudio Fontana         goto error;
26921fc33bb9SClaudio Fontana     }
26931fc33bb9SClaudio Fontana 
26941fc33bb9SClaudio Fontana     whpx_memory_init();
26951fc33bb9SClaudio Fontana 
26961fc33bb9SClaudio Fontana     printf("Windows Hypervisor Platform accelerator is operational\n");
26971fc33bb9SClaudio Fontana     return 0;
26981fc33bb9SClaudio Fontana 
26991fc33bb9SClaudio Fontana error:
27001fc33bb9SClaudio Fontana 
27011fc33bb9SClaudio Fontana     if (NULL != whpx->partition) {
27021fc33bb9SClaudio Fontana         whp_dispatch.WHvDeletePartition(whpx->partition);
27031fc33bb9SClaudio Fontana         whpx->partition = NULL;
27041fc33bb9SClaudio Fontana     }
27051fc33bb9SClaudio Fontana 
27061fc33bb9SClaudio Fontana     return ret;
27071fc33bb9SClaudio Fontana }
27081fc33bb9SClaudio Fontana 
27091fc33bb9SClaudio Fontana int whpx_enabled(void)
27101fc33bb9SClaudio Fontana {
27111fc33bb9SClaudio Fontana     return whpx_allowed;
27121fc33bb9SClaudio Fontana }
27131fc33bb9SClaudio Fontana 
271484f4ef17SPaolo Bonzini bool whpx_apic_in_platform(void) {
271584f4ef17SPaolo Bonzini     return whpx_global.apic_in_platform;
271684f4ef17SPaolo Bonzini }
271784f4ef17SPaolo Bonzini 
27181fc33bb9SClaudio Fontana static void whpx_accel_class_init(ObjectClass *oc, void *data)
27191fc33bb9SClaudio Fontana {
27201fc33bb9SClaudio Fontana     AccelClass *ac = ACCEL_CLASS(oc);
27211fc33bb9SClaudio Fontana     ac->name = "WHPX";
27221fc33bb9SClaudio Fontana     ac->init_machine = whpx_accel_init;
27231fc33bb9SClaudio Fontana     ac->allowed = &whpx_allowed;
27241fc33bb9SClaudio Fontana 
27251fc33bb9SClaudio Fontana     object_class_property_add(oc, "kernel-irqchip", "on|off|split",
27261fc33bb9SClaudio Fontana         NULL, whpx_set_kernel_irqchip,
27271fc33bb9SClaudio Fontana         NULL, NULL);
27281fc33bb9SClaudio Fontana     object_class_property_set_description(oc, "kernel-irqchip",
27291fc33bb9SClaudio Fontana         "Configure WHPX in-kernel irqchip");
27301fc33bb9SClaudio Fontana }
27311fc33bb9SClaudio Fontana 
27321fc33bb9SClaudio Fontana static void whpx_accel_instance_init(Object *obj)
27331fc33bb9SClaudio Fontana {
27341fc33bb9SClaudio Fontana     struct whpx_state *whpx = &whpx_global;
27351fc33bb9SClaudio Fontana 
27361fc33bb9SClaudio Fontana     memset(whpx, 0, sizeof(struct whpx_state));
27371fc33bb9SClaudio Fontana     /* Turn on kernel-irqchip, by default */
27381fc33bb9SClaudio Fontana     whpx->kernel_irqchip_allowed = true;
27391fc33bb9SClaudio Fontana }
27401fc33bb9SClaudio Fontana 
27411fc33bb9SClaudio Fontana static const TypeInfo whpx_accel_type = {
27421fc33bb9SClaudio Fontana     .name = ACCEL_CLASS_NAME("whpx"),
27431fc33bb9SClaudio Fontana     .parent = TYPE_ACCEL,
27441fc33bb9SClaudio Fontana     .instance_init = whpx_accel_instance_init,
27451fc33bb9SClaudio Fontana     .class_init = whpx_accel_class_init,
27461fc33bb9SClaudio Fontana };
27471fc33bb9SClaudio Fontana 
27481fc33bb9SClaudio Fontana static void whpx_type_init(void)
27491fc33bb9SClaudio Fontana {
27501fc33bb9SClaudio Fontana     type_register_static(&whpx_accel_type);
27511fc33bb9SClaudio Fontana }
27521fc33bb9SClaudio Fontana 
27531fc33bb9SClaudio Fontana bool init_whp_dispatch(void)
27541fc33bb9SClaudio Fontana {
27551fc33bb9SClaudio Fontana     if (whp_dispatch_initialized) {
27561fc33bb9SClaudio Fontana         return true;
27571fc33bb9SClaudio Fontana     }
27581fc33bb9SClaudio Fontana 
27591fc33bb9SClaudio Fontana     if (!load_whp_dispatch_fns(&hWinHvPlatform, WINHV_PLATFORM_FNS_DEFAULT)) {
27601fc33bb9SClaudio Fontana         goto error;
27611fc33bb9SClaudio Fontana     }
27621fc33bb9SClaudio Fontana 
27631fc33bb9SClaudio Fontana     if (!load_whp_dispatch_fns(&hWinHvEmulation, WINHV_EMULATION_FNS_DEFAULT)) {
27641fc33bb9SClaudio Fontana         goto error;
27651fc33bb9SClaudio Fontana     }
27661fc33bb9SClaudio Fontana 
27671fc33bb9SClaudio Fontana     assert(load_whp_dispatch_fns(&hWinHvPlatform,
27681fc33bb9SClaudio Fontana         WINHV_PLATFORM_FNS_SUPPLEMENTAL));
27691fc33bb9SClaudio Fontana     whp_dispatch_initialized = true;
27701fc33bb9SClaudio Fontana 
27711fc33bb9SClaudio Fontana     return true;
27721fc33bb9SClaudio Fontana error:
27731fc33bb9SClaudio Fontana     if (hWinHvPlatform) {
27741fc33bb9SClaudio Fontana         FreeLibrary(hWinHvPlatform);
27751fc33bb9SClaudio Fontana     }
27761fc33bb9SClaudio Fontana 
27771fc33bb9SClaudio Fontana     if (hWinHvEmulation) {
27781fc33bb9SClaudio Fontana         FreeLibrary(hWinHvEmulation);
27791fc33bb9SClaudio Fontana     }
27801fc33bb9SClaudio Fontana 
27811fc33bb9SClaudio Fontana     return false;
27821fc33bb9SClaudio Fontana }
27831fc33bb9SClaudio Fontana 
27841fc33bb9SClaudio Fontana type_init(whpx_type_init);
2785