Path: blob/master/tools/testing/selftests/kvm/x86/kvm_pv_test.c
38245 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* Copyright (C) 2020, Google LLC.3*4* Tests for KVM paravirtual feature disablement5*/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"1314struct msr_data {15uint32_t idx;16const char *name;17};1819#define TEST_MSR(msr) { .idx = msr, .name = #msr }20#define UCALL_PR_MSR 0xdeadbeef21#define PR_MSR(msr) ucall(UCALL_PR_MSR, 1, msr)2223/*24* KVM paravirtual msrs to test. Expect a #GP if any of these msrs are read or25* written, as the KVM_CPUID_FEATURES leaf is cleared.26*/27static struct msr_data msrs_to_test[] = {28TEST_MSR(MSR_KVM_SYSTEM_TIME),29TEST_MSR(MSR_KVM_SYSTEM_TIME_NEW),30TEST_MSR(MSR_KVM_WALL_CLOCK),31TEST_MSR(MSR_KVM_WALL_CLOCK_NEW),32TEST_MSR(MSR_KVM_ASYNC_PF_EN),33TEST_MSR(MSR_KVM_STEAL_TIME),34TEST_MSR(MSR_KVM_PV_EOI_EN),35TEST_MSR(MSR_KVM_POLL_CONTROL),36TEST_MSR(MSR_KVM_ASYNC_PF_INT),37TEST_MSR(MSR_KVM_ASYNC_PF_ACK),38};3940static void test_msr(struct msr_data *msr)41{42uint64_t ignored;43uint8_t vector;4445PR_MSR(msr);4647vector = rdmsr_safe(msr->idx, &ignored);48GUEST_ASSERT_EQ(vector, GP_VECTOR);4950vector = wrmsr_safe(msr->idx, 0);51GUEST_ASSERT_EQ(vector, GP_VECTOR);52}5354struct hcall_data {55uint64_t nr;56const char *name;57};5859#define TEST_HCALL(hc) { .nr = hc, .name = #hc }60#define UCALL_PR_HCALL 0xdeadc0de61#define PR_HCALL(hc) ucall(UCALL_PR_HCALL, 1, hc)6263/*64* KVM hypercalls to test. Expect -KVM_ENOSYS when called, as the corresponding65* features have been cleared in KVM_CPUID_FEATURES.66*/67static struct hcall_data hcalls_to_test[] = {68TEST_HCALL(KVM_HC_KICK_CPU),69TEST_HCALL(KVM_HC_SEND_IPI),70TEST_HCALL(KVM_HC_SCHED_YIELD),71};7273static void test_hcall(struct hcall_data *hc)74{75uint64_t r;7677PR_HCALL(hc);78r = kvm_hypercall(hc->nr, 0, 0, 0, 0);79GUEST_ASSERT_EQ(r, -KVM_ENOSYS);80}8182static void guest_main(void)83{84int i;8586for (i = 0; i < ARRAY_SIZE(msrs_to_test); i++) {87test_msr(&msrs_to_test[i]);88}8990for (i = 0; i < ARRAY_SIZE(hcalls_to_test); i++) {91test_hcall(&hcalls_to_test[i]);92}9394GUEST_DONE();95}9697static void pr_msr(struct ucall *uc)98{99struct msr_data *msr = (struct msr_data *)uc->args[0];100101pr_info("testing msr: %s (%#x)\n", msr->name, msr->idx);102}103104static void pr_hcall(struct ucall *uc)105{106struct hcall_data *hc = (struct hcall_data *)uc->args[0];107108pr_info("testing hcall: %s (%lu)\n", hc->name, hc->nr);109}110111static void enter_guest(struct kvm_vcpu *vcpu)112{113struct ucall uc;114115while (true) {116vcpu_run(vcpu);117TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);118119switch (get_ucall(vcpu, &uc)) {120case UCALL_PR_MSR:121pr_msr(&uc);122break;123case UCALL_PR_HCALL:124pr_hcall(&uc);125break;126case UCALL_ABORT:127REPORT_GUEST_ASSERT(uc);128return;129case UCALL_DONE:130return;131}132}133}134135static void test_pv_unhalt(void)136{137struct kvm_vcpu *vcpu;138struct kvm_vm *vm;139struct kvm_cpuid_entry2 *ent;140u32 kvm_sig_old;141int r;142143if (!(kvm_check_cap(KVM_CAP_X86_DISABLE_EXITS) & KVM_X86_DISABLE_EXITS_HLT))144return;145146pr_info("testing KVM_FEATURE_PV_UNHALT\n");147148/* KVM_PV_UNHALT test */149vm = vm_create_with_one_vcpu(&vcpu, guest_main);150vcpu_set_cpuid_feature(vcpu, X86_FEATURE_KVM_PV_UNHALT);151152TEST_ASSERT(vcpu_cpuid_has(vcpu, X86_FEATURE_KVM_PV_UNHALT),153"Enabling X86_FEATURE_KVM_PV_UNHALT had no effect");154155/* Verify KVM disallows disabling exits after vCPU creation. */156r = __vm_enable_cap(vm, KVM_CAP_X86_DISABLE_EXITS, KVM_X86_DISABLE_EXITS_HLT);157TEST_ASSERT(r && errno == EINVAL,158"Disabling exits after vCPU creation didn't fail as expected");159160kvm_vm_free(vm);161162/* Verify that KVM clear PV_UNHALT from guest CPUID. */163vm = vm_create(1);164vm_enable_cap(vm, KVM_CAP_X86_DISABLE_EXITS, KVM_X86_DISABLE_EXITS_HLT);165166vcpu = vm_vcpu_add(vm, 0, NULL);167TEST_ASSERT(!vcpu_cpuid_has(vcpu, X86_FEATURE_KVM_PV_UNHALT),168"vCPU created with PV_UNHALT set by default");169170vcpu_set_cpuid_feature(vcpu, X86_FEATURE_KVM_PV_UNHALT);171TEST_ASSERT(!vcpu_cpuid_has(vcpu, X86_FEATURE_KVM_PV_UNHALT),172"PV_UNHALT set in guest CPUID when HLT-exiting is disabled");173174/*175* Clobber the KVM PV signature and verify KVM does NOT clear PV_UNHALT176* when KVM PV is not present, and DOES clear PV_UNHALT when switching177* back to the correct signature..178*/179ent = vcpu_get_cpuid_entry(vcpu, KVM_CPUID_SIGNATURE);180kvm_sig_old = ent->ebx;181ent->ebx = 0xdeadbeef;182vcpu_set_cpuid(vcpu);183184vcpu_set_cpuid_feature(vcpu, X86_FEATURE_KVM_PV_UNHALT);185TEST_ASSERT(vcpu_cpuid_has(vcpu, X86_FEATURE_KVM_PV_UNHALT),186"PV_UNHALT cleared when using bogus KVM PV signature");187188ent = vcpu_get_cpuid_entry(vcpu, KVM_CPUID_SIGNATURE);189ent->ebx = kvm_sig_old;190vcpu_set_cpuid(vcpu);191192TEST_ASSERT(!vcpu_cpuid_has(vcpu, X86_FEATURE_KVM_PV_UNHALT),193"PV_UNHALT set in guest CPUID when HLT-exiting is disabled");194195/* FIXME: actually test KVM_FEATURE_PV_UNHALT feature */196197kvm_vm_free(vm);198}199200int main(void)201{202struct kvm_vcpu *vcpu;203struct kvm_vm *vm;204205TEST_REQUIRE(kvm_has_cap(KVM_CAP_ENFORCE_PV_FEATURE_CPUID));206207vm = vm_create_with_one_vcpu(&vcpu, guest_main);208209vcpu_enable_cap(vcpu, KVM_CAP_ENFORCE_PV_FEATURE_CPUID, 1);210211vcpu_clear_cpuid_entry(vcpu, KVM_CPUID_FEATURES);212213enter_guest(vcpu);214kvm_vm_free(vm);215216test_pv_unhalt();217}218219220