root/tools/testing/selftests/kvm/x86/hyperv_features.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2021, Red Hat, Inc.
 *
 * Tests for Hyper-V features enablement
 */
#include <asm/kvm_para.h>
#include <linux/kvm_para.h>
#include <stdint.h>

#include "test_util.h"
#include "kvm_util.h"
#include "processor.h"
#include "hyperv.h"

/*
 * HYPERV_CPUID_ENLIGHTMENT_INFO.EBX is not a 'feature' CPUID leaf
 * but to activate the feature it is sufficient to set it to a non-zero
 * value. Use BIT(0) for that.
 */
#define HV_PV_SPINLOCKS_TEST            \
        KVM_X86_CPU_FEATURE(HYPERV_CPUID_ENLIGHTMENT_INFO, 0, EBX, 0)

struct msr_data {
        u32 idx;
        bool fault_expected;
        bool write;
        u64 write_val;
        bool reset_expected;
};

struct hcall_data {
        u64 control;
        u64 expect;
        bool ud_expected;
};

static bool is_write_only_msr(u32 msr)
{
        return msr == HV_X64_MSR_EOI;
}

static void guest_msr(struct msr_data *msr)
{
        u8 vector = 0;
        u64 msr_val = 0;

        GUEST_ASSERT(msr->idx);

        if (msr->write)
                vector = wrmsr_safe(msr->idx, msr->write_val);

        if (!vector && (!msr->write || !is_write_only_msr(msr->idx)))
                vector = rdmsr_safe(msr->idx, &msr_val);

        if (msr->fault_expected)
                __GUEST_ASSERT(vector == GP_VECTOR,
                               "Expected #GP on %sMSR(0x%x), got %s",
                               msr->write ? "WR" : "RD", msr->idx, ex_str(vector));
        else
                __GUEST_ASSERT(!vector,
                               "Expected success on %sMSR(0x%x), got %s",
                               msr->write ? "WR" : "RD", msr->idx, ex_str(vector));

        if (vector || is_write_only_msr(msr->idx))
                goto done;

        if (msr->write)
                __GUEST_ASSERT(!vector,
                               "WRMSR(0x%x) to '0x%lx', RDMSR read '0x%lx'",
                               msr->idx, msr->write_val, msr_val);

        /* Invariant TSC bit appears when TSC invariant control MSR is written to */
        if (msr->idx == HV_X64_MSR_TSC_INVARIANT_CONTROL) {
                if (!this_cpu_has(HV_ACCESS_TSC_INVARIANT))
                        GUEST_ASSERT(this_cpu_has(X86_FEATURE_INVTSC));
                else
                        GUEST_ASSERT(this_cpu_has(X86_FEATURE_INVTSC) ==
                                     !!(msr_val & HV_INVARIANT_TSC_EXPOSED));
        }

done:
        GUEST_DONE();
}

static void guest_hcall(gpa_t pgs_gpa, struct hcall_data *hcall)
{
        u64 res, input, output;
        u8 vector;

        GUEST_ASSERT_NE(hcall->control, 0);

        wrmsr(HV_X64_MSR_GUEST_OS_ID, HYPERV_LINUX_OS_ID);
        wrmsr(HV_X64_MSR_HYPERCALL, pgs_gpa);

        if (!(hcall->control & HV_HYPERCALL_FAST_BIT)) {
                input = pgs_gpa;
                output = pgs_gpa + PAGE_SIZE;
        } else {
                input = output = 0;
        }

        vector = __hyperv_hypercall(hcall->control, input, output, &res);
        if (hcall->ud_expected) {
                __GUEST_ASSERT(vector == UD_VECTOR,
                               "Expected #UD for control '%lu', got %s",
                               hcall->control, ex_str(vector));
        } else {
                __GUEST_ASSERT(!vector,
                               "Expected no exception for control '%lu', got %s",
                               hcall->control, ex_str(vector));
                GUEST_ASSERT_EQ(res, hcall->expect);
        }

        GUEST_DONE();
}

static void vcpu_reset_hv_cpuid(struct kvm_vcpu *vcpu)
{
        /*
         * Enable all supported Hyper-V features, then clear the leafs holding
         * the features that will be tested one by one.
         */
        vcpu_set_hv_cpuid(vcpu);

        vcpu_clear_cpuid_entry(vcpu, HYPERV_CPUID_FEATURES);
        vcpu_clear_cpuid_entry(vcpu, HYPERV_CPUID_ENLIGHTMENT_INFO);
        vcpu_clear_cpuid_entry(vcpu, HYPERV_CPUID_SYNDBG_PLATFORM_CAPABILITIES);
}

static void guest_test_msrs_access(void)
{
        struct kvm_cpuid2 *prev_cpuid = NULL;
        struct kvm_vcpu *vcpu;
        struct kvm_vm *vm;
        struct ucall uc;
        int stage = 0;
        gva_t msr_gva;
        struct msr_data *msr;
        bool has_invtsc = kvm_cpu_has(X86_FEATURE_INVTSC);

        while (true) {
                vm = vm_create_with_one_vcpu(&vcpu, guest_msr);

                msr_gva = vm_alloc_page(vm);
                memset(addr_gva2hva(vm, msr_gva), 0x0, getpagesize());
                msr = addr_gva2hva(vm, msr_gva);

                vcpu_args_set(vcpu, 1, msr_gva);
                vcpu_enable_cap(vcpu, KVM_CAP_HYPERV_ENFORCE_CPUID, 1);

                if (!prev_cpuid) {
                        vcpu_reset_hv_cpuid(vcpu);

                        prev_cpuid = allocate_kvm_cpuid2(vcpu->cpuid->nent);
                } else {
                        vcpu_init_cpuid(vcpu, prev_cpuid);
                }

                /* TODO: Make this entire test easier to maintain. */
                if (stage >= 21)
                        vcpu_enable_cap(vcpu, KVM_CAP_HYPERV_SYNIC2, 0);

                switch (stage) {
                case 0:
                        /*
                         * Only available when Hyper-V identification is set
                         */
                        msr->idx = HV_X64_MSR_GUEST_OS_ID;
                        msr->write = false;
                        msr->fault_expected = true;
                        break;
                case 1:
                        msr->idx = HV_X64_MSR_HYPERCALL;
                        msr->write = false;
                        msr->fault_expected = true;
                        break;
                case 2:
                        vcpu_set_cpuid_feature(vcpu, HV_MSR_HYPERCALL_AVAILABLE);
                        /*
                         * HV_X64_MSR_GUEST_OS_ID has to be written first to make
                         * HV_X64_MSR_HYPERCALL available.
                         */
                        msr->idx = HV_X64_MSR_GUEST_OS_ID;
                        msr->write = true;
                        msr->write_val = HYPERV_LINUX_OS_ID;
                        msr->fault_expected = false;
                        break;
                case 3:
                        msr->idx = HV_X64_MSR_GUEST_OS_ID;
                        msr->write = false;
                        msr->fault_expected = false;
                        break;
                case 4:
                        msr->idx = HV_X64_MSR_HYPERCALL;
                        msr->write = false;
                        msr->fault_expected = false;
                        break;

                case 5:
                        msr->idx = HV_X64_MSR_VP_RUNTIME;
                        msr->write = false;
                        msr->fault_expected = true;
                        break;
                case 6:
                        vcpu_set_cpuid_feature(vcpu, HV_MSR_VP_RUNTIME_AVAILABLE);
                        msr->idx = HV_X64_MSR_VP_RUNTIME;
                        msr->write = false;
                        msr->fault_expected = false;
                        break;
                case 7:
                        /* Read only */
                        msr->idx = HV_X64_MSR_VP_RUNTIME;
                        msr->write = true;
                        msr->write_val = 1;
                        msr->fault_expected = true;
                        break;

                case 8:
                        msr->idx = HV_X64_MSR_TIME_REF_COUNT;
                        msr->write = false;
                        msr->fault_expected = true;
                        break;
                case 9:
                        vcpu_set_cpuid_feature(vcpu, HV_MSR_TIME_REF_COUNT_AVAILABLE);
                        msr->idx = HV_X64_MSR_TIME_REF_COUNT;
                        msr->write = false;
                        msr->fault_expected = false;
                        break;
                case 10:
                        /* Read only */
                        msr->idx = HV_X64_MSR_TIME_REF_COUNT;
                        msr->write = true;
                        msr->write_val = 1;
                        msr->fault_expected = true;
                        break;

                case 11:
                        msr->idx = HV_X64_MSR_VP_INDEX;
                        msr->write = false;
                        msr->fault_expected = true;
                        break;
                case 12:
                        vcpu_set_cpuid_feature(vcpu, HV_MSR_VP_INDEX_AVAILABLE);
                        msr->idx = HV_X64_MSR_VP_INDEX;
                        msr->write = false;
                        msr->fault_expected = false;
                        break;
                case 13:
                        /* Read only */
                        msr->idx = HV_X64_MSR_VP_INDEX;
                        msr->write = true;
                        msr->write_val = 1;
                        msr->fault_expected = true;
                        break;

                case 14:
                        msr->idx = HV_X64_MSR_RESET;
                        msr->write = false;
                        msr->fault_expected = true;
                        break;
                case 15:
                        vcpu_set_cpuid_feature(vcpu, HV_MSR_RESET_AVAILABLE);
                        msr->idx = HV_X64_MSR_RESET;
                        msr->write = false;
                        msr->fault_expected = false;
                        break;
                case 16:
                        msr->idx = HV_X64_MSR_RESET;
                        msr->write = true;
                        msr->write_val = 1;
                        msr->fault_expected = false;
                        msr->reset_expected = true;
                        break;

                case 17:
                        msr->idx = HV_X64_MSR_REFERENCE_TSC;
                        msr->write = false;
                        msr->fault_expected = true;
                        break;
                case 18:
                        vcpu_set_cpuid_feature(vcpu, HV_MSR_REFERENCE_TSC_AVAILABLE);
                        msr->idx = HV_X64_MSR_REFERENCE_TSC;
                        msr->write = false;
                        msr->fault_expected = false;
                        break;
                case 19:
                        msr->idx = HV_X64_MSR_REFERENCE_TSC;
                        msr->write = true;
                        msr->write_val = 0;
                        msr->fault_expected = false;
                        break;

                case 20:
                        msr->idx = HV_X64_MSR_EOM;
                        msr->write = false;
                        msr->fault_expected = true;
                        break;
                case 21:
                        /*
                         * Remains unavailable even with KVM_CAP_HYPERV_SYNIC2
                         * capability enabled and guest visible CPUID bit unset.
                         */
                        msr->idx = HV_X64_MSR_EOM;
                        msr->write = false;
                        msr->fault_expected = true;
                        break;
                case 22:
                        vcpu_set_cpuid_feature(vcpu, HV_MSR_SYNIC_AVAILABLE);
                        msr->idx = HV_X64_MSR_EOM;
                        msr->write = false;
                        msr->fault_expected = false;
                        break;
                case 23:
                        msr->idx = HV_X64_MSR_EOM;
                        msr->write = true;
                        msr->write_val = 0;
                        msr->fault_expected = false;
                        break;

                case 24:
                        msr->idx = HV_X64_MSR_STIMER0_CONFIG;
                        msr->write = false;
                        msr->fault_expected = true;
                        break;
                case 25:
                        vcpu_set_cpuid_feature(vcpu, HV_MSR_SYNTIMER_AVAILABLE);
                        msr->idx = HV_X64_MSR_STIMER0_CONFIG;
                        msr->write = false;
                        msr->fault_expected = false;
                        break;
                case 26:
                        msr->idx = HV_X64_MSR_STIMER0_CONFIG;
                        msr->write = true;
                        msr->write_val = 0;
                        msr->fault_expected = false;
                        break;
                case 27:
                        /* Direct mode test */
                        msr->idx = HV_X64_MSR_STIMER0_CONFIG;
                        msr->write = true;
                        msr->write_val = 1 << 12;
                        msr->fault_expected = true;
                        break;
                case 28:
                        vcpu_set_cpuid_feature(vcpu, HV_STIMER_DIRECT_MODE_AVAILABLE);
                        msr->idx = HV_X64_MSR_STIMER0_CONFIG;
                        msr->write = true;
                        msr->write_val = 1 << 12;
                        msr->fault_expected = false;
                        break;

                case 29:
                        msr->idx = HV_X64_MSR_EOI;
                        msr->write = false;
                        msr->fault_expected = true;
                        break;
                case 30:
                        vcpu_set_cpuid_feature(vcpu, HV_MSR_APIC_ACCESS_AVAILABLE);
                        msr->idx = HV_X64_MSR_EOI;
                        msr->write = true;
                        msr->write_val = 1;
                        msr->fault_expected = false;
                        break;

                case 31:
                        msr->idx = HV_X64_MSR_TSC_FREQUENCY;
                        msr->write = false;
                        msr->fault_expected = true;
                        break;
                case 32:
                        vcpu_set_cpuid_feature(vcpu, HV_ACCESS_FREQUENCY_MSRS);
                        msr->idx = HV_X64_MSR_TSC_FREQUENCY;
                        msr->write = false;
                        msr->fault_expected = false;
                        break;
                case 33:
                        /* Read only */
                        msr->idx = HV_X64_MSR_TSC_FREQUENCY;
                        msr->write = true;
                        msr->write_val = 1;
                        msr->fault_expected = true;
                        break;

                case 34:
                        msr->idx = HV_X64_MSR_REENLIGHTENMENT_CONTROL;
                        msr->write = false;
                        msr->fault_expected = true;
                        break;
                case 35:
                        vcpu_set_cpuid_feature(vcpu, HV_ACCESS_REENLIGHTENMENT);
                        msr->idx = HV_X64_MSR_REENLIGHTENMENT_CONTROL;
                        msr->write = false;
                        msr->fault_expected = false;
                        break;
                case 36:
                        msr->idx = HV_X64_MSR_REENLIGHTENMENT_CONTROL;
                        msr->write = true;
                        msr->write_val = 1;
                        msr->fault_expected = false;
                        break;
                case 37:
                        /* Can only write '0' */
                        msr->idx = HV_X64_MSR_TSC_EMULATION_STATUS;
                        msr->write = true;
                        msr->write_val = 1;
                        msr->fault_expected = true;
                        break;

                case 38:
                        msr->idx = HV_X64_MSR_CRASH_P0;
                        msr->write = false;
                        msr->fault_expected = true;
                        break;
                case 39:
                        vcpu_set_cpuid_feature(vcpu, HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE);
                        msr->idx = HV_X64_MSR_CRASH_P0;
                        msr->write = false;
                        msr->fault_expected = false;
                        break;
                case 40:
                        msr->idx = HV_X64_MSR_CRASH_P0;
                        msr->write = true;
                        msr->write_val = 1;
                        msr->fault_expected = false;
                        break;

                case 41:
                        msr->idx = HV_X64_MSR_SYNDBG_STATUS;
                        msr->write = false;
                        msr->fault_expected = true;
                        break;
                case 42:
                        vcpu_set_cpuid_feature(vcpu, HV_FEATURE_DEBUG_MSRS_AVAILABLE);
                        vcpu_set_cpuid_feature(vcpu, HV_X64_SYNDBG_CAP_ALLOW_KERNEL_DEBUGGING);
                        msr->idx = HV_X64_MSR_SYNDBG_STATUS;
                        msr->write = false;
                        msr->fault_expected = false;
                        break;
                case 43:
                        msr->idx = HV_X64_MSR_SYNDBG_STATUS;
                        msr->write = true;
                        msr->write_val = 0;
                        msr->fault_expected = false;
                        break;

                case 44:
                        /* MSR is not available when CPUID feature bit is unset */
                        if (!has_invtsc)
                                goto next_stage;
                        msr->idx = HV_X64_MSR_TSC_INVARIANT_CONTROL;
                        msr->write = false;
                        msr->fault_expected = true;
                        break;
                case 45:
                        /* MSR is available when CPUID feature bit is set */
                        if (!has_invtsc)
                                goto next_stage;
                        vcpu_set_cpuid_feature(vcpu, HV_ACCESS_TSC_INVARIANT);
                        msr->idx = HV_X64_MSR_TSC_INVARIANT_CONTROL;
                        msr->write = false;
                        msr->fault_expected = false;
                        break;
                case 46:
                        /* Writing bits other than 0 is forbidden */
                        if (!has_invtsc)
                                goto next_stage;
                        msr->idx = HV_X64_MSR_TSC_INVARIANT_CONTROL;
                        msr->write = true;
                        msr->write_val = 0xdeadbeef;
                        msr->fault_expected = true;
                        break;
                case 47:
                        /* Setting bit 0 enables the feature */
                        if (!has_invtsc)
                                goto next_stage;
                        msr->idx = HV_X64_MSR_TSC_INVARIANT_CONTROL;
                        msr->write = true;
                        msr->write_val = 1;
                        msr->fault_expected = false;
                        break;

                default:
                        kvm_vm_free(vm);
                        return;
                }

                vcpu_set_cpuid(vcpu);

                memcpy(prev_cpuid, vcpu->cpuid, kvm_cpuid2_size(vcpu->cpuid->nent));

                pr_debug("Stage %d: testing msr: 0x%x for %s\n", stage,
                         msr->idx, msr->write ? "write" : "read");

                vcpu_run(vcpu);

                if (msr->reset_expected) {
                        TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_SYSTEM_EVENT);
                        TEST_ASSERT(vcpu->run->system_event.type == KVM_SYSTEM_EVENT_RESET,
                                    "Expected reset system event, got type %u",
                                    vcpu->run->system_event.type);
                        goto next_stage;
                }

                TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);

                switch (get_ucall(vcpu, &uc)) {
                case UCALL_ABORT:
                        REPORT_GUEST_ASSERT(uc);
                        return;
                case UCALL_DONE:
                        break;
                default:
                        TEST_FAIL("Unhandled ucall: %ld", uc.cmd);
                        return;
                }

next_stage:
                stage++;
                kvm_vm_free(vm);
        }
}

static void guest_test_hcalls_access(void)
{
        struct kvm_cpuid2 *prev_cpuid = NULL;
        struct kvm_vcpu *vcpu;
        struct kvm_vm *vm;
        struct ucall uc;
        int stage = 0;
        gva_t hcall_page, hcall_params;
        struct hcall_data *hcall;

        while (true) {
                vm = vm_create_with_one_vcpu(&vcpu, guest_hcall);

                /* Hypercall input/output */
                hcall_page = vm_alloc_pages(vm, 2);
                memset(addr_gva2hva(vm, hcall_page), 0x0, 2 * getpagesize());

                hcall_params = vm_alloc_page(vm);
                memset(addr_gva2hva(vm, hcall_params), 0x0, getpagesize());
                hcall = addr_gva2hva(vm, hcall_params);

                vcpu_args_set(vcpu, 2, addr_gva2gpa(vm, hcall_page), hcall_params);
                vcpu_enable_cap(vcpu, KVM_CAP_HYPERV_ENFORCE_CPUID, 1);

                if (!prev_cpuid) {
                        vcpu_reset_hv_cpuid(vcpu);

                        prev_cpuid = allocate_kvm_cpuid2(vcpu->cpuid->nent);
                } else {
                        vcpu_init_cpuid(vcpu, prev_cpuid);
                }

                switch (stage) {
                case 0:
                        vcpu_set_cpuid_feature(vcpu, HV_MSR_HYPERCALL_AVAILABLE);
                        hcall->control = 0xbeef;
                        hcall->expect = HV_STATUS_INVALID_HYPERCALL_CODE;
                        break;

                case 1:
                        hcall->control = HVCALL_POST_MESSAGE;
                        hcall->expect = HV_STATUS_ACCESS_DENIED;
                        break;
                case 2:
                        vcpu_set_cpuid_feature(vcpu, HV_POST_MESSAGES);
                        hcall->control = HVCALL_POST_MESSAGE;
                        hcall->expect = HV_STATUS_INVALID_HYPERCALL_INPUT;
                        break;

                case 3:
                        hcall->control = HVCALL_SIGNAL_EVENT;
                        hcall->expect = HV_STATUS_ACCESS_DENIED;
                        break;
                case 4:
                        vcpu_set_cpuid_feature(vcpu, HV_SIGNAL_EVENTS);
                        hcall->control = HVCALL_SIGNAL_EVENT;
                        hcall->expect = HV_STATUS_INVALID_HYPERCALL_INPUT;
                        break;

                case 5:
                        hcall->control = HVCALL_RESET_DEBUG_SESSION;
                        hcall->expect = HV_STATUS_INVALID_HYPERCALL_CODE;
                        break;
                case 6:
                        vcpu_set_cpuid_feature(vcpu, HV_X64_SYNDBG_CAP_ALLOW_KERNEL_DEBUGGING);
                        hcall->control = HVCALL_RESET_DEBUG_SESSION;
                        hcall->expect = HV_STATUS_ACCESS_DENIED;
                        break;
                case 7:
                        vcpu_set_cpuid_feature(vcpu, HV_DEBUGGING);
                        hcall->control = HVCALL_RESET_DEBUG_SESSION;
                        hcall->expect = HV_STATUS_OPERATION_DENIED;
                        break;

                case 8:
                        hcall->control = HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE;
                        hcall->expect = HV_STATUS_ACCESS_DENIED;
                        break;
                case 9:
                        vcpu_set_cpuid_feature(vcpu, HV_X64_REMOTE_TLB_FLUSH_RECOMMENDED);
                        hcall->control = HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE;
                        hcall->expect = HV_STATUS_SUCCESS;
                        break;
                case 10:
                        hcall->control = HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX;
                        hcall->expect = HV_STATUS_ACCESS_DENIED;
                        break;
                case 11:
                        vcpu_set_cpuid_feature(vcpu, HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED);
                        hcall->control = HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX;
                        hcall->expect = HV_STATUS_SUCCESS;
                        break;

                case 12:
                        hcall->control = HVCALL_SEND_IPI;
                        hcall->expect = HV_STATUS_ACCESS_DENIED;
                        break;
                case 13:
                        vcpu_set_cpuid_feature(vcpu, HV_X64_CLUSTER_IPI_RECOMMENDED);
                        hcall->control = HVCALL_SEND_IPI;
                        hcall->expect = HV_STATUS_INVALID_HYPERCALL_INPUT;
                        break;
                case 14:
                        /* Nothing in 'sparse banks' -> success */
                        hcall->control = HVCALL_SEND_IPI_EX;
                        hcall->expect = HV_STATUS_SUCCESS;
                        break;

                case 15:
                        hcall->control = HVCALL_NOTIFY_LONG_SPIN_WAIT;
                        hcall->expect = HV_STATUS_ACCESS_DENIED;
                        break;
                case 16:
                        vcpu_set_cpuid_feature(vcpu, HV_PV_SPINLOCKS_TEST);
                        hcall->control = HVCALL_NOTIFY_LONG_SPIN_WAIT;
                        hcall->expect = HV_STATUS_SUCCESS;
                        break;
                case 17:
                        /* XMM fast hypercall */
                        hcall->control = HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE | HV_HYPERCALL_FAST_BIT;
                        hcall->ud_expected = true;
                        break;
                case 18:
                        vcpu_set_cpuid_feature(vcpu, HV_X64_HYPERCALL_XMM_INPUT_AVAILABLE);
                        hcall->control = HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE | HV_HYPERCALL_FAST_BIT;
                        hcall->ud_expected = false;
                        hcall->expect = HV_STATUS_SUCCESS;
                        break;
                case 19:
                        hcall->control = HV_EXT_CALL_QUERY_CAPABILITIES;
                        hcall->expect = HV_STATUS_ACCESS_DENIED;
                        break;
                case 20:
                        vcpu_set_cpuid_feature(vcpu, HV_ENABLE_EXTENDED_HYPERCALLS);
                        hcall->control = HV_EXT_CALL_QUERY_CAPABILITIES | HV_HYPERCALL_FAST_BIT;
                        hcall->expect = HV_STATUS_INVALID_PARAMETER;
                        break;
                case 21:
                        kvm_vm_free(vm);
                        return;
                }

                vcpu_set_cpuid(vcpu);

                memcpy(prev_cpuid, vcpu->cpuid, kvm_cpuid2_size(vcpu->cpuid->nent));

                pr_debug("Stage %d: testing hcall: 0x%lx\n", stage, hcall->control);

                vcpu_run(vcpu);
                TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);

                switch (get_ucall(vcpu, &uc)) {
                case UCALL_ABORT:
                        REPORT_GUEST_ASSERT(uc);
                        return;
                case UCALL_DONE:
                        break;
                default:
                        TEST_FAIL("Unhandled ucall: %ld", uc.cmd);
                        return;
                }

                stage++;
                kvm_vm_free(vm);
        }
}

int main(void)
{
        TEST_REQUIRE(kvm_has_cap(KVM_CAP_HYPERV_ENFORCE_CPUID));

        pr_info("Testing access to Hyper-V specific MSRs\n");
        guest_test_msrs_access();

        pr_info("Testing access to Hyper-V hypercalls\n");
        guest_test_hcalls_access();
}