Path: blob/master/tools/testing/selftests/kvm/x86/hyperv_svm_test.c
38237 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* Copyright (C) 2022, Red Hat, Inc.3*4* Tests for Hyper-V extensions to SVM.5*/6#include <fcntl.h>7#include <stdio.h>8#include <stdlib.h>9#include <string.h>10#include <sys/ioctl.h>11#include <linux/bitmap.h>1213#include "test_util.h"1415#include "kvm_util.h"16#include "processor.h"17#include "svm_util.h"18#include "hyperv.h"1920#define L2_GUEST_STACK_SIZE 2562122/* Exit to L1 from L2 with RDMSR instruction */23static inline void rdmsr_from_l2(uint32_t msr)24{25/* Currently, L1 doesn't preserve GPRs during vmexits. */26__asm__ __volatile__ ("rdmsr" : : "c"(msr) :27"rax", "rbx", "rdx", "rsi", "rdi", "r8", "r9",28"r10", "r11", "r12", "r13", "r14", "r15");29}3031void l2_guest_code(void)32{33u64 unused;3435GUEST_SYNC(3);36/* Exit to L1 */37vmmcall();3839/* MSR-Bitmap tests */40rdmsr_from_l2(MSR_FS_BASE); /* intercepted */41rdmsr_from_l2(MSR_FS_BASE); /* intercepted */42rdmsr_from_l2(MSR_GS_BASE); /* not intercepted */43vmmcall();44rdmsr_from_l2(MSR_GS_BASE); /* intercepted */4546GUEST_SYNC(5);4748/* L2 TLB flush tests */49hyperv_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE |50HV_HYPERCALL_FAST_BIT, 0x0,51HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES |52HV_FLUSH_ALL_PROCESSORS);53rdmsr_from_l2(MSR_FS_BASE);54/*55* Note: hypercall status (RAX) is not preserved correctly by L1 after56* synthetic vmexit, use unchecked version.57*/58__hyperv_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE |59HV_HYPERCALL_FAST_BIT, 0x0,60HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES |61HV_FLUSH_ALL_PROCESSORS, &unused);6263/* Done, exit to L1 and never come back. */64vmmcall();65}6667static void __attribute__((__flatten__)) guest_code(struct svm_test_data *svm,68struct hyperv_test_pages *hv_pages,69vm_vaddr_t pgs_gpa)70{71unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE];72struct vmcb *vmcb = svm->vmcb;73struct hv_vmcb_enlightenments *hve = &vmcb->control.hv_enlightenments;7475GUEST_SYNC(1);7677wrmsr(HV_X64_MSR_GUEST_OS_ID, HYPERV_LINUX_OS_ID);78wrmsr(HV_X64_MSR_HYPERCALL, pgs_gpa);79enable_vp_assist(hv_pages->vp_assist_gpa, hv_pages->vp_assist);8081GUEST_ASSERT(svm->vmcb_gpa);82/* Prepare for L2 execution. */83generic_svm_setup(svm, l2_guest_code,84&l2_guest_stack[L2_GUEST_STACK_SIZE]);8586/* L2 TLB flush setup */87hve->partition_assist_page = hv_pages->partition_assist_gpa;88hve->hv_enlightenments_control.nested_flush_hypercall = 1;89hve->hv_vm_id = 1;90hve->hv_vp_id = 1;91current_vp_assist->nested_control.features.directhypercall = 1;92*(u32 *)(hv_pages->partition_assist) = 0;9394GUEST_SYNC(2);95run_guest(vmcb, svm->vmcb_gpa);96GUEST_ASSERT(vmcb->control.exit_code == SVM_EXIT_VMMCALL);97GUEST_SYNC(4);98vmcb->save.rip += 3;99100/* Intercept RDMSR 0xc0000100 */101vmcb->control.intercept |= 1ULL << INTERCEPT_MSR_PROT;102__set_bit(2 * (MSR_FS_BASE & 0x1fff), svm->msr + 0x800);103run_guest(vmcb, svm->vmcb_gpa);104GUEST_ASSERT(vmcb->control.exit_code == SVM_EXIT_MSR);105vmcb->save.rip += 2; /* rdmsr */106107/* Enable enlightened MSR bitmap */108hve->hv_enlightenments_control.msr_bitmap = 1;109run_guest(vmcb, svm->vmcb_gpa);110GUEST_ASSERT(vmcb->control.exit_code == SVM_EXIT_MSR);111vmcb->save.rip += 2; /* rdmsr */112113/* Intercept RDMSR 0xc0000101 without telling KVM about it */114__set_bit(2 * (MSR_GS_BASE & 0x1fff), svm->msr + 0x800);115/* Make sure HV_VMX_ENLIGHTENED_CLEAN_FIELD_MSR_BITMAP is set */116vmcb->control.clean |= HV_VMCB_NESTED_ENLIGHTENMENTS;117run_guest(vmcb, svm->vmcb_gpa);118/* Make sure we don't see SVM_EXIT_MSR here so eMSR bitmap works */119GUEST_ASSERT(vmcb->control.exit_code == SVM_EXIT_VMMCALL);120vmcb->save.rip += 3; /* vmcall */121122/* Now tell KVM we've changed MSR-Bitmap */123vmcb->control.clean &= ~HV_VMCB_NESTED_ENLIGHTENMENTS;124run_guest(vmcb, svm->vmcb_gpa);125GUEST_ASSERT(vmcb->control.exit_code == SVM_EXIT_MSR);126vmcb->save.rip += 2; /* rdmsr */127128129/*130* L2 TLB flush test. First VMCALL should be handled directly by L0,131* no VMCALL exit expected.132*/133run_guest(vmcb, svm->vmcb_gpa);134GUEST_ASSERT(vmcb->control.exit_code == SVM_EXIT_MSR);135vmcb->save.rip += 2; /* rdmsr */136/* Enable synthetic vmexit */137*(u32 *)(hv_pages->partition_assist) = 1;138run_guest(vmcb, svm->vmcb_gpa);139GUEST_ASSERT(vmcb->control.exit_code == HV_SVM_EXITCODE_ENL);140GUEST_ASSERT(vmcb->control.exit_info_1 == HV_SVM_ENL_EXITCODE_TRAP_AFTER_FLUSH);141142run_guest(vmcb, svm->vmcb_gpa);143GUEST_ASSERT(vmcb->control.exit_code == SVM_EXIT_VMMCALL);144GUEST_SYNC(6);145146GUEST_DONE();147}148149int main(int argc, char *argv[])150{151vm_vaddr_t nested_gva = 0, hv_pages_gva = 0;152vm_vaddr_t hcall_page;153struct kvm_vcpu *vcpu;154struct kvm_vm *vm;155struct ucall uc;156int stage;157158TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM));159TEST_REQUIRE(kvm_hv_cpu_has(HV_X64_NESTED_DIRECT_FLUSH));160161/* Create VM */162vm = vm_create_with_one_vcpu(&vcpu, guest_code);163vcpu_set_hv_cpuid(vcpu);164vcpu_alloc_svm(vm, &nested_gva);165vcpu_alloc_hyperv_test_pages(vm, &hv_pages_gva);166167hcall_page = vm_vaddr_alloc_pages(vm, 1);168memset(addr_gva2hva(vm, hcall_page), 0x0, getpagesize());169170vcpu_args_set(vcpu, 3, nested_gva, hv_pages_gva, addr_gva2gpa(vm, hcall_page));171vcpu_set_msr(vcpu, HV_X64_MSR_VP_INDEX, vcpu->id);172173for (stage = 1;; stage++) {174vcpu_run(vcpu);175TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);176177switch (get_ucall(vcpu, &uc)) {178case UCALL_ABORT:179REPORT_GUEST_ASSERT(uc);180/* NOT REACHED */181case UCALL_SYNC:182break;183case UCALL_DONE:184goto done;185default:186TEST_FAIL("Unknown ucall %lu", uc.cmd);187}188189/* UCALL_SYNC is handled here. */190TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") &&191uc.args[1] == stage, "Stage %d: Unexpected register values vmexit, got %lx",192stage, (ulong)uc.args[1]);193194}195196done:197kvm_vm_free(vm);198}199200201