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