200 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2022, Red Hat, Inc.4 *5 * Tests for Hyper-V extensions to SVM.6 */7#include <fcntl.h>8#include <stdio.h>9#include <stdlib.h>10#include <string.h>11#include <sys/ioctl.h>12#include <linux/bitmap.h>13 14#include "test_util.h"15 16#include "kvm_util.h"17#include "processor.h"18#include "svm_util.h"19#include "hyperv.h"20 21#define L2_GUEST_STACK_SIZE 25622 23/* Exit to L1 from L2 with RDMSR instruction */24static inline void rdmsr_from_l2(uint32_t msr)25{26 /* Currently, L1 doesn't preserve GPRs during vmexits. */27 __asm__ __volatile__ ("rdmsr" : : "c"(msr) :28 "rax", "rbx", "rdx", "rsi", "rdi", "r8", "r9",29 "r10", "r11", "r12", "r13", "r14", "r15");30}31 32void l2_guest_code(void)33{34 u64 unused;35 36 GUEST_SYNC(3);37 /* Exit to L1 */38 vmmcall();39 40 /* MSR-Bitmap tests */41 rdmsr_from_l2(MSR_FS_BASE); /* intercepted */42 rdmsr_from_l2(MSR_FS_BASE); /* intercepted */43 rdmsr_from_l2(MSR_GS_BASE); /* not intercepted */44 vmmcall();45 rdmsr_from_l2(MSR_GS_BASE); /* intercepted */46 47 GUEST_SYNC(5);48 49 /* L2 TLB flush tests */50 hyperv_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE |51 HV_HYPERCALL_FAST_BIT, 0x0,52 HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES |53 HV_FLUSH_ALL_PROCESSORS);54 rdmsr_from_l2(MSR_FS_BASE);55 /*56 * Note: hypercall status (RAX) is not preserved correctly by L1 after57 * synthetic vmexit, use unchecked version.58 */59 __hyperv_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE |60 HV_HYPERCALL_FAST_BIT, 0x0,61 HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES |62 HV_FLUSH_ALL_PROCESSORS, &unused);63 64 /* Done, exit to L1 and never come back. */65 vmmcall();66}67 68static void __attribute__((__flatten__)) guest_code(struct svm_test_data *svm,69 struct hyperv_test_pages *hv_pages,70 vm_vaddr_t pgs_gpa)71{72 unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE];73 struct vmcb *vmcb = svm->vmcb;74 struct hv_vmcb_enlightenments *hve = &vmcb->control.hv_enlightenments;75 76 GUEST_SYNC(1);77 78 wrmsr(HV_X64_MSR_GUEST_OS_ID, HYPERV_LINUX_OS_ID);79 wrmsr(HV_X64_MSR_HYPERCALL, pgs_gpa);80 enable_vp_assist(hv_pages->vp_assist_gpa, hv_pages->vp_assist);81 82 GUEST_ASSERT(svm->vmcb_gpa);83 /* Prepare for L2 execution. */84 generic_svm_setup(svm, l2_guest_code,85 &l2_guest_stack[L2_GUEST_STACK_SIZE]);86 87 /* L2 TLB flush setup */88 hve->partition_assist_page = hv_pages->partition_assist_gpa;89 hve->hv_enlightenments_control.nested_flush_hypercall = 1;90 hve->hv_vm_id = 1;91 hve->hv_vp_id = 1;92 current_vp_assist->nested_control.features.directhypercall = 1;93 *(u32 *)(hv_pages->partition_assist) = 0;94 95 GUEST_SYNC(2);96 run_guest(vmcb, svm->vmcb_gpa);97 GUEST_ASSERT(vmcb->control.exit_code == SVM_EXIT_VMMCALL);98 GUEST_SYNC(4);99 vmcb->save.rip += 3;100 101 /* Intercept RDMSR 0xc0000100 */102 vmcb->control.intercept |= 1ULL << INTERCEPT_MSR_PROT;103 __set_bit(2 * (MSR_FS_BASE & 0x1fff), svm->msr + 0x800);104 run_guest(vmcb, svm->vmcb_gpa);105 GUEST_ASSERT(vmcb->control.exit_code == SVM_EXIT_MSR);106 vmcb->save.rip += 2; /* rdmsr */107 108 /* Enable enlightened MSR bitmap */109 hve->hv_enlightenments_control.msr_bitmap = 1;110 run_guest(vmcb, svm->vmcb_gpa);111 GUEST_ASSERT(vmcb->control.exit_code == SVM_EXIT_MSR);112 vmcb->save.rip += 2; /* rdmsr */113 114 /* Intercept RDMSR 0xc0000101 without telling KVM about it */115 __set_bit(2 * (MSR_GS_BASE & 0x1fff), svm->msr + 0x800);116 /* Make sure HV_VMX_ENLIGHTENED_CLEAN_FIELD_MSR_BITMAP is set */117 vmcb->control.clean |= HV_VMCB_NESTED_ENLIGHTENMENTS;118 run_guest(vmcb, svm->vmcb_gpa);119 /* Make sure we don't see SVM_EXIT_MSR here so eMSR bitmap works */120 GUEST_ASSERT(vmcb->control.exit_code == SVM_EXIT_VMMCALL);121 vmcb->save.rip += 3; /* vmcall */122 123 /* Now tell KVM we've changed MSR-Bitmap */124 vmcb->control.clean &= ~HV_VMCB_NESTED_ENLIGHTENMENTS;125 run_guest(vmcb, svm->vmcb_gpa);126 GUEST_ASSERT(vmcb->control.exit_code == SVM_EXIT_MSR);127 vmcb->save.rip += 2; /* rdmsr */128 129 130 /*131 * L2 TLB flush test. First VMCALL should be handled directly by L0,132 * no VMCALL exit expected.133 */134 run_guest(vmcb, svm->vmcb_gpa);135 GUEST_ASSERT(vmcb->control.exit_code == SVM_EXIT_MSR);136 vmcb->save.rip += 2; /* rdmsr */137 /* Enable synthetic vmexit */138 *(u32 *)(hv_pages->partition_assist) = 1;139 run_guest(vmcb, svm->vmcb_gpa);140 GUEST_ASSERT(vmcb->control.exit_code == HV_SVM_EXITCODE_ENL);141 GUEST_ASSERT(vmcb->control.exit_info_1 == HV_SVM_ENL_EXITCODE_TRAP_AFTER_FLUSH);142 143 run_guest(vmcb, svm->vmcb_gpa);144 GUEST_ASSERT(vmcb->control.exit_code == SVM_EXIT_VMMCALL);145 GUEST_SYNC(6);146 147 GUEST_DONE();148}149 150int main(int argc, char *argv[])151{152 vm_vaddr_t nested_gva = 0, hv_pages_gva = 0;153 vm_vaddr_t hcall_page;154 struct kvm_vcpu *vcpu;155 struct kvm_vm *vm;156 struct ucall uc;157 int stage;158 159 TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM));160 TEST_REQUIRE(kvm_hv_cpu_has(HV_X64_NESTED_DIRECT_FLUSH));161 162 /* Create VM */163 vm = vm_create_with_one_vcpu(&vcpu, guest_code);164 vcpu_set_hv_cpuid(vcpu);165 vcpu_alloc_svm(vm, &nested_gva);166 vcpu_alloc_hyperv_test_pages(vm, &hv_pages_gva);167 168 hcall_page = vm_vaddr_alloc_pages(vm, 1);169 memset(addr_gva2hva(vm, hcall_page), 0x0, getpagesize());170 171 vcpu_args_set(vcpu, 3, nested_gva, hv_pages_gva, addr_gva2gpa(vm, hcall_page));172 vcpu_set_msr(vcpu, HV_X64_MSR_VP_INDEX, vcpu->id);173 174 for (stage = 1;; stage++) {175 vcpu_run(vcpu);176 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);177 178 switch (get_ucall(vcpu, &uc)) {179 case UCALL_ABORT:180 REPORT_GUEST_ASSERT(uc);181 /* NOT REACHED */182 case UCALL_SYNC:183 break;184 case UCALL_DONE:185 goto done;186 default:187 TEST_FAIL("Unknown ucall %lu", uc.cmd);188 }189 190 /* UCALL_SYNC is handled here. */191 TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") &&192 uc.args[1] == stage, "Stage %d: Unexpected register values vmexit, got %lx",193 stage, (ulong)uc.args[1]);194 195 }196 197done:198 kvm_vm_free(vm);199}200