Path: blob/master/tools/testing/selftests/kvm/x86/hyperv_features.c
38237 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* Copyright (C) 2021, Red Hat, Inc.3*4* Tests for Hyper-V features enablement5*/6#include <asm/kvm_para.h>7#include <linux/kvm_para.h>8#include <stdint.h>910#include "test_util.h"11#include "kvm_util.h"12#include "processor.h"13#include "hyperv.h"1415/*16* HYPERV_CPUID_ENLIGHTMENT_INFO.EBX is not a 'feature' CPUID leaf17* but to activate the feature it is sufficient to set it to a non-zero18* value. Use BIT(0) for that.19*/20#define HV_PV_SPINLOCKS_TEST \21KVM_X86_CPU_FEATURE(HYPERV_CPUID_ENLIGHTMENT_INFO, 0, EBX, 0)2223struct msr_data {24uint32_t idx;25bool fault_expected;26bool write;27u64 write_val;28};2930struct hcall_data {31uint64_t control;32uint64_t expect;33bool ud_expected;34};3536static bool is_write_only_msr(uint32_t msr)37{38return msr == HV_X64_MSR_EOI;39}4041static void guest_msr(struct msr_data *msr)42{43uint8_t vector = 0;44uint64_t msr_val = 0;4546GUEST_ASSERT(msr->idx);4748if (msr->write)49vector = wrmsr_safe(msr->idx, msr->write_val);5051if (!vector && (!msr->write || !is_write_only_msr(msr->idx)))52vector = rdmsr_safe(msr->idx, &msr_val);5354if (msr->fault_expected)55__GUEST_ASSERT(vector == GP_VECTOR,56"Expected #GP on %sMSR(0x%x), got %s",57msr->write ? "WR" : "RD", msr->idx, ex_str(vector));58else59__GUEST_ASSERT(!vector,60"Expected success on %sMSR(0x%x), got %s",61msr->write ? "WR" : "RD", msr->idx, ex_str(vector));6263if (vector || is_write_only_msr(msr->idx))64goto done;6566if (msr->write)67__GUEST_ASSERT(!vector,68"WRMSR(0x%x) to '0x%lx', RDMSR read '0x%lx'",69msr->idx, msr->write_val, msr_val);7071/* Invariant TSC bit appears when TSC invariant control MSR is written to */72if (msr->idx == HV_X64_MSR_TSC_INVARIANT_CONTROL) {73if (!this_cpu_has(HV_ACCESS_TSC_INVARIANT))74GUEST_ASSERT(this_cpu_has(X86_FEATURE_INVTSC));75else76GUEST_ASSERT(this_cpu_has(X86_FEATURE_INVTSC) ==77!!(msr_val & HV_INVARIANT_TSC_EXPOSED));78}7980done:81GUEST_DONE();82}8384static void guest_hcall(vm_vaddr_t pgs_gpa, struct hcall_data *hcall)85{86u64 res, input, output;87uint8_t vector;8889GUEST_ASSERT_NE(hcall->control, 0);9091wrmsr(HV_X64_MSR_GUEST_OS_ID, HYPERV_LINUX_OS_ID);92wrmsr(HV_X64_MSR_HYPERCALL, pgs_gpa);9394if (!(hcall->control & HV_HYPERCALL_FAST_BIT)) {95input = pgs_gpa;96output = pgs_gpa + PAGE_SIZE;97} else {98input = output = 0;99}100101vector = __hyperv_hypercall(hcall->control, input, output, &res);102if (hcall->ud_expected) {103__GUEST_ASSERT(vector == UD_VECTOR,104"Expected #UD for control '%lu', got %s",105hcall->control, ex_str(vector));106} else {107__GUEST_ASSERT(!vector,108"Expected no exception for control '%lu', got %s",109hcall->control, ex_str(vector));110GUEST_ASSERT_EQ(res, hcall->expect);111}112113GUEST_DONE();114}115116static void vcpu_reset_hv_cpuid(struct kvm_vcpu *vcpu)117{118/*119* Enable all supported Hyper-V features, then clear the leafs holding120* the features that will be tested one by one.121*/122vcpu_set_hv_cpuid(vcpu);123124vcpu_clear_cpuid_entry(vcpu, HYPERV_CPUID_FEATURES);125vcpu_clear_cpuid_entry(vcpu, HYPERV_CPUID_ENLIGHTMENT_INFO);126vcpu_clear_cpuid_entry(vcpu, HYPERV_CPUID_SYNDBG_PLATFORM_CAPABILITIES);127}128129static void guest_test_msrs_access(void)130{131struct kvm_cpuid2 *prev_cpuid = NULL;132struct kvm_vcpu *vcpu;133struct kvm_vm *vm;134struct ucall uc;135int stage = 0;136vm_vaddr_t msr_gva;137struct msr_data *msr;138bool has_invtsc = kvm_cpu_has(X86_FEATURE_INVTSC);139140while (true) {141vm = vm_create_with_one_vcpu(&vcpu, guest_msr);142143msr_gva = vm_vaddr_alloc_page(vm);144memset(addr_gva2hva(vm, msr_gva), 0x0, getpagesize());145msr = addr_gva2hva(vm, msr_gva);146147vcpu_args_set(vcpu, 1, msr_gva);148vcpu_enable_cap(vcpu, KVM_CAP_HYPERV_ENFORCE_CPUID, 1);149150if (!prev_cpuid) {151vcpu_reset_hv_cpuid(vcpu);152153prev_cpuid = allocate_kvm_cpuid2(vcpu->cpuid->nent);154} else {155vcpu_init_cpuid(vcpu, prev_cpuid);156}157158/* TODO: Make this entire test easier to maintain. */159if (stage >= 21)160vcpu_enable_cap(vcpu, KVM_CAP_HYPERV_SYNIC2, 0);161162switch (stage) {163case 0:164/*165* Only available when Hyper-V identification is set166*/167msr->idx = HV_X64_MSR_GUEST_OS_ID;168msr->write = false;169msr->fault_expected = true;170break;171case 1:172msr->idx = HV_X64_MSR_HYPERCALL;173msr->write = false;174msr->fault_expected = true;175break;176case 2:177vcpu_set_cpuid_feature(vcpu, HV_MSR_HYPERCALL_AVAILABLE);178/*179* HV_X64_MSR_GUEST_OS_ID has to be written first to make180* HV_X64_MSR_HYPERCALL available.181*/182msr->idx = HV_X64_MSR_GUEST_OS_ID;183msr->write = true;184msr->write_val = HYPERV_LINUX_OS_ID;185msr->fault_expected = false;186break;187case 3:188msr->idx = HV_X64_MSR_GUEST_OS_ID;189msr->write = false;190msr->fault_expected = false;191break;192case 4:193msr->idx = HV_X64_MSR_HYPERCALL;194msr->write = false;195msr->fault_expected = false;196break;197198case 5:199msr->idx = HV_X64_MSR_VP_RUNTIME;200msr->write = false;201msr->fault_expected = true;202break;203case 6:204vcpu_set_cpuid_feature(vcpu, HV_MSR_VP_RUNTIME_AVAILABLE);205msr->idx = HV_X64_MSR_VP_RUNTIME;206msr->write = false;207msr->fault_expected = false;208break;209case 7:210/* Read only */211msr->idx = HV_X64_MSR_VP_RUNTIME;212msr->write = true;213msr->write_val = 1;214msr->fault_expected = true;215break;216217case 8:218msr->idx = HV_X64_MSR_TIME_REF_COUNT;219msr->write = false;220msr->fault_expected = true;221break;222case 9:223vcpu_set_cpuid_feature(vcpu, HV_MSR_TIME_REF_COUNT_AVAILABLE);224msr->idx = HV_X64_MSR_TIME_REF_COUNT;225msr->write = false;226msr->fault_expected = false;227break;228case 10:229/* Read only */230msr->idx = HV_X64_MSR_TIME_REF_COUNT;231msr->write = true;232msr->write_val = 1;233msr->fault_expected = true;234break;235236case 11:237msr->idx = HV_X64_MSR_VP_INDEX;238msr->write = false;239msr->fault_expected = true;240break;241case 12:242vcpu_set_cpuid_feature(vcpu, HV_MSR_VP_INDEX_AVAILABLE);243msr->idx = HV_X64_MSR_VP_INDEX;244msr->write = false;245msr->fault_expected = false;246break;247case 13:248/* Read only */249msr->idx = HV_X64_MSR_VP_INDEX;250msr->write = true;251msr->write_val = 1;252msr->fault_expected = true;253break;254255case 14:256msr->idx = HV_X64_MSR_RESET;257msr->write = false;258msr->fault_expected = true;259break;260case 15:261vcpu_set_cpuid_feature(vcpu, HV_MSR_RESET_AVAILABLE);262msr->idx = HV_X64_MSR_RESET;263msr->write = false;264msr->fault_expected = false;265break;266case 16:267msr->idx = HV_X64_MSR_RESET;268msr->write = true;269/*270* TODO: the test only writes '0' to HV_X64_MSR_RESET271* at the moment, writing some other value there will272* trigger real vCPU reset and the code is not prepared273* to handle it yet.274*/275msr->write_val = 0;276msr->fault_expected = false;277break;278279case 17:280msr->idx = HV_X64_MSR_REFERENCE_TSC;281msr->write = false;282msr->fault_expected = true;283break;284case 18:285vcpu_set_cpuid_feature(vcpu, HV_MSR_REFERENCE_TSC_AVAILABLE);286msr->idx = HV_X64_MSR_REFERENCE_TSC;287msr->write = false;288msr->fault_expected = false;289break;290case 19:291msr->idx = HV_X64_MSR_REFERENCE_TSC;292msr->write = true;293msr->write_val = 0;294msr->fault_expected = false;295break;296297case 20:298msr->idx = HV_X64_MSR_EOM;299msr->write = false;300msr->fault_expected = true;301break;302case 21:303/*304* Remains unavailable even with KVM_CAP_HYPERV_SYNIC2305* capability enabled and guest visible CPUID bit unset.306*/307msr->idx = HV_X64_MSR_EOM;308msr->write = false;309msr->fault_expected = true;310break;311case 22:312vcpu_set_cpuid_feature(vcpu, HV_MSR_SYNIC_AVAILABLE);313msr->idx = HV_X64_MSR_EOM;314msr->write = false;315msr->fault_expected = false;316break;317case 23:318msr->idx = HV_X64_MSR_EOM;319msr->write = true;320msr->write_val = 0;321msr->fault_expected = false;322break;323324case 24:325msr->idx = HV_X64_MSR_STIMER0_CONFIG;326msr->write = false;327msr->fault_expected = true;328break;329case 25:330vcpu_set_cpuid_feature(vcpu, HV_MSR_SYNTIMER_AVAILABLE);331msr->idx = HV_X64_MSR_STIMER0_CONFIG;332msr->write = false;333msr->fault_expected = false;334break;335case 26:336msr->idx = HV_X64_MSR_STIMER0_CONFIG;337msr->write = true;338msr->write_val = 0;339msr->fault_expected = false;340break;341case 27:342/* Direct mode test */343msr->idx = HV_X64_MSR_STIMER0_CONFIG;344msr->write = true;345msr->write_val = 1 << 12;346msr->fault_expected = true;347break;348case 28:349vcpu_set_cpuid_feature(vcpu, HV_STIMER_DIRECT_MODE_AVAILABLE);350msr->idx = HV_X64_MSR_STIMER0_CONFIG;351msr->write = true;352msr->write_val = 1 << 12;353msr->fault_expected = false;354break;355356case 29:357msr->idx = HV_X64_MSR_EOI;358msr->write = false;359msr->fault_expected = true;360break;361case 30:362vcpu_set_cpuid_feature(vcpu, HV_MSR_APIC_ACCESS_AVAILABLE);363msr->idx = HV_X64_MSR_EOI;364msr->write = true;365msr->write_val = 1;366msr->fault_expected = false;367break;368369case 31:370msr->idx = HV_X64_MSR_TSC_FREQUENCY;371msr->write = false;372msr->fault_expected = true;373break;374case 32:375vcpu_set_cpuid_feature(vcpu, HV_ACCESS_FREQUENCY_MSRS);376msr->idx = HV_X64_MSR_TSC_FREQUENCY;377msr->write = false;378msr->fault_expected = false;379break;380case 33:381/* Read only */382msr->idx = HV_X64_MSR_TSC_FREQUENCY;383msr->write = true;384msr->write_val = 1;385msr->fault_expected = true;386break;387388case 34:389msr->idx = HV_X64_MSR_REENLIGHTENMENT_CONTROL;390msr->write = false;391msr->fault_expected = true;392break;393case 35:394vcpu_set_cpuid_feature(vcpu, HV_ACCESS_REENLIGHTENMENT);395msr->idx = HV_X64_MSR_REENLIGHTENMENT_CONTROL;396msr->write = false;397msr->fault_expected = false;398break;399case 36:400msr->idx = HV_X64_MSR_REENLIGHTENMENT_CONTROL;401msr->write = true;402msr->write_val = 1;403msr->fault_expected = false;404break;405case 37:406/* Can only write '0' */407msr->idx = HV_X64_MSR_TSC_EMULATION_STATUS;408msr->write = true;409msr->write_val = 1;410msr->fault_expected = true;411break;412413case 38:414msr->idx = HV_X64_MSR_CRASH_P0;415msr->write = false;416msr->fault_expected = true;417break;418case 39:419vcpu_set_cpuid_feature(vcpu, HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE);420msr->idx = HV_X64_MSR_CRASH_P0;421msr->write = false;422msr->fault_expected = false;423break;424case 40:425msr->idx = HV_X64_MSR_CRASH_P0;426msr->write = true;427msr->write_val = 1;428msr->fault_expected = false;429break;430431case 41:432msr->idx = HV_X64_MSR_SYNDBG_STATUS;433msr->write = false;434msr->fault_expected = true;435break;436case 42:437vcpu_set_cpuid_feature(vcpu, HV_FEATURE_DEBUG_MSRS_AVAILABLE);438vcpu_set_cpuid_feature(vcpu, HV_X64_SYNDBG_CAP_ALLOW_KERNEL_DEBUGGING);439msr->idx = HV_X64_MSR_SYNDBG_STATUS;440msr->write = false;441msr->fault_expected = false;442break;443case 43:444msr->idx = HV_X64_MSR_SYNDBG_STATUS;445msr->write = true;446msr->write_val = 0;447msr->fault_expected = false;448break;449450case 44:451/* MSR is not available when CPUID feature bit is unset */452if (!has_invtsc)453goto next_stage;454msr->idx = HV_X64_MSR_TSC_INVARIANT_CONTROL;455msr->write = false;456msr->fault_expected = true;457break;458case 45:459/* MSR is vailable when CPUID feature bit is set */460if (!has_invtsc)461goto next_stage;462vcpu_set_cpuid_feature(vcpu, HV_ACCESS_TSC_INVARIANT);463msr->idx = HV_X64_MSR_TSC_INVARIANT_CONTROL;464msr->write = false;465msr->fault_expected = false;466break;467case 46:468/* Writing bits other than 0 is forbidden */469if (!has_invtsc)470goto next_stage;471msr->idx = HV_X64_MSR_TSC_INVARIANT_CONTROL;472msr->write = true;473msr->write_val = 0xdeadbeef;474msr->fault_expected = true;475break;476case 47:477/* Setting bit 0 enables the feature */478if (!has_invtsc)479goto next_stage;480msr->idx = HV_X64_MSR_TSC_INVARIANT_CONTROL;481msr->write = true;482msr->write_val = 1;483msr->fault_expected = false;484break;485486default:487kvm_vm_free(vm);488return;489}490491vcpu_set_cpuid(vcpu);492493memcpy(prev_cpuid, vcpu->cpuid, kvm_cpuid2_size(vcpu->cpuid->nent));494495pr_debug("Stage %d: testing msr: 0x%x for %s\n", stage,496msr->idx, msr->write ? "write" : "read");497498vcpu_run(vcpu);499TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);500501switch (get_ucall(vcpu, &uc)) {502case UCALL_ABORT:503REPORT_GUEST_ASSERT(uc);504return;505case UCALL_DONE:506break;507default:508TEST_FAIL("Unhandled ucall: %ld", uc.cmd);509return;510}511512next_stage:513stage++;514kvm_vm_free(vm);515}516}517518static void guest_test_hcalls_access(void)519{520struct kvm_cpuid2 *prev_cpuid = NULL;521struct kvm_vcpu *vcpu;522struct kvm_vm *vm;523struct ucall uc;524int stage = 0;525vm_vaddr_t hcall_page, hcall_params;526struct hcall_data *hcall;527528while (true) {529vm = vm_create_with_one_vcpu(&vcpu, guest_hcall);530531/* Hypercall input/output */532hcall_page = vm_vaddr_alloc_pages(vm, 2);533memset(addr_gva2hva(vm, hcall_page), 0x0, 2 * getpagesize());534535hcall_params = vm_vaddr_alloc_page(vm);536memset(addr_gva2hva(vm, hcall_params), 0x0, getpagesize());537hcall = addr_gva2hva(vm, hcall_params);538539vcpu_args_set(vcpu, 2, addr_gva2gpa(vm, hcall_page), hcall_params);540vcpu_enable_cap(vcpu, KVM_CAP_HYPERV_ENFORCE_CPUID, 1);541542if (!prev_cpuid) {543vcpu_reset_hv_cpuid(vcpu);544545prev_cpuid = allocate_kvm_cpuid2(vcpu->cpuid->nent);546} else {547vcpu_init_cpuid(vcpu, prev_cpuid);548}549550switch (stage) {551case 0:552vcpu_set_cpuid_feature(vcpu, HV_MSR_HYPERCALL_AVAILABLE);553hcall->control = 0xbeef;554hcall->expect = HV_STATUS_INVALID_HYPERCALL_CODE;555break;556557case 1:558hcall->control = HVCALL_POST_MESSAGE;559hcall->expect = HV_STATUS_ACCESS_DENIED;560break;561case 2:562vcpu_set_cpuid_feature(vcpu, HV_POST_MESSAGES);563hcall->control = HVCALL_POST_MESSAGE;564hcall->expect = HV_STATUS_INVALID_HYPERCALL_INPUT;565break;566567case 3:568hcall->control = HVCALL_SIGNAL_EVENT;569hcall->expect = HV_STATUS_ACCESS_DENIED;570break;571case 4:572vcpu_set_cpuid_feature(vcpu, HV_SIGNAL_EVENTS);573hcall->control = HVCALL_SIGNAL_EVENT;574hcall->expect = HV_STATUS_INVALID_HYPERCALL_INPUT;575break;576577case 5:578hcall->control = HVCALL_RESET_DEBUG_SESSION;579hcall->expect = HV_STATUS_INVALID_HYPERCALL_CODE;580break;581case 6:582vcpu_set_cpuid_feature(vcpu, HV_X64_SYNDBG_CAP_ALLOW_KERNEL_DEBUGGING);583hcall->control = HVCALL_RESET_DEBUG_SESSION;584hcall->expect = HV_STATUS_ACCESS_DENIED;585break;586case 7:587vcpu_set_cpuid_feature(vcpu, HV_DEBUGGING);588hcall->control = HVCALL_RESET_DEBUG_SESSION;589hcall->expect = HV_STATUS_OPERATION_DENIED;590break;591592case 8:593hcall->control = HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE;594hcall->expect = HV_STATUS_ACCESS_DENIED;595break;596case 9:597vcpu_set_cpuid_feature(vcpu, HV_X64_REMOTE_TLB_FLUSH_RECOMMENDED);598hcall->control = HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE;599hcall->expect = HV_STATUS_SUCCESS;600break;601case 10:602hcall->control = HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX;603hcall->expect = HV_STATUS_ACCESS_DENIED;604break;605case 11:606vcpu_set_cpuid_feature(vcpu, HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED);607hcall->control = HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX;608hcall->expect = HV_STATUS_SUCCESS;609break;610611case 12:612hcall->control = HVCALL_SEND_IPI;613hcall->expect = HV_STATUS_ACCESS_DENIED;614break;615case 13:616vcpu_set_cpuid_feature(vcpu, HV_X64_CLUSTER_IPI_RECOMMENDED);617hcall->control = HVCALL_SEND_IPI;618hcall->expect = HV_STATUS_INVALID_HYPERCALL_INPUT;619break;620case 14:621/* Nothing in 'sparse banks' -> success */622hcall->control = HVCALL_SEND_IPI_EX;623hcall->expect = HV_STATUS_SUCCESS;624break;625626case 15:627hcall->control = HVCALL_NOTIFY_LONG_SPIN_WAIT;628hcall->expect = HV_STATUS_ACCESS_DENIED;629break;630case 16:631vcpu_set_cpuid_feature(vcpu, HV_PV_SPINLOCKS_TEST);632hcall->control = HVCALL_NOTIFY_LONG_SPIN_WAIT;633hcall->expect = HV_STATUS_SUCCESS;634break;635case 17:636/* XMM fast hypercall */637hcall->control = HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE | HV_HYPERCALL_FAST_BIT;638hcall->ud_expected = true;639break;640case 18:641vcpu_set_cpuid_feature(vcpu, HV_X64_HYPERCALL_XMM_INPUT_AVAILABLE);642hcall->control = HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE | HV_HYPERCALL_FAST_BIT;643hcall->ud_expected = false;644hcall->expect = HV_STATUS_SUCCESS;645break;646case 19:647hcall->control = HV_EXT_CALL_QUERY_CAPABILITIES;648hcall->expect = HV_STATUS_ACCESS_DENIED;649break;650case 20:651vcpu_set_cpuid_feature(vcpu, HV_ENABLE_EXTENDED_HYPERCALLS);652hcall->control = HV_EXT_CALL_QUERY_CAPABILITIES | HV_HYPERCALL_FAST_BIT;653hcall->expect = HV_STATUS_INVALID_PARAMETER;654break;655case 21:656kvm_vm_free(vm);657return;658}659660vcpu_set_cpuid(vcpu);661662memcpy(prev_cpuid, vcpu->cpuid, kvm_cpuid2_size(vcpu->cpuid->nent));663664pr_debug("Stage %d: testing hcall: 0x%lx\n", stage, hcall->control);665666vcpu_run(vcpu);667TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);668669switch (get_ucall(vcpu, &uc)) {670case UCALL_ABORT:671REPORT_GUEST_ASSERT(uc);672return;673case UCALL_DONE:674break;675default:676TEST_FAIL("Unhandled ucall: %ld", uc.cmd);677return;678}679680stage++;681kvm_vm_free(vm);682}683}684685int main(void)686{687TEST_REQUIRE(kvm_has_cap(KVM_CAP_HYPERV_ENFORCE_CPUID));688689pr_info("Testing access to Hyper-V specific MSRs\n");690guest_test_msrs_access();691692pr_info("Testing access to Hyper-V hypercalls\n");693guest_test_hcalls_access();694}695696697