1 // SPDX-License-Identifier: GPL-2.0-only 2 #include <fcntl.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <string.h> 6 #include <sys/ioctl.h> 7 #include <math.h> 8 9 #include "test_util.h" 10 #include "kvm_util.h" 11 #include "processor.h" 12 #include "svm_util.h" 13 #include "linux/psp-sev.h" 14 #include "sev.h" 15 16 17 #define XFEATURE_MASK_X87_AVX (XFEATURE_MASK_FP | XFEATURE_MASK_SSE | XFEATURE_MASK_YMM) 18 19 static void guest_sev_es_code(void) 20 { 21 /* TODO: Check CPUID after GHCB-based hypercall support is added. */ 22 GUEST_ASSERT(rdmsr(MSR_AMD64_SEV) & MSR_AMD64_SEV_ENABLED); 23 GUEST_ASSERT(rdmsr(MSR_AMD64_SEV) & MSR_AMD64_SEV_ES_ENABLED); 24 25 /* 26 * TODO: Add GHCB and ucall support for SEV-ES guests. For now, simply 27 * force "termination" to signal "done" via the GHCB MSR protocol. 28 */ 29 wrmsr(MSR_AMD64_SEV_ES_GHCB, GHCB_MSR_TERM_REQ); 30 __asm__ __volatile__("rep; vmmcall"); 31 } 32 33 static void guest_sev_code(void) 34 { 35 GUEST_ASSERT(this_cpu_has(X86_FEATURE_SEV)); 36 GUEST_ASSERT(rdmsr(MSR_AMD64_SEV) & MSR_AMD64_SEV_ENABLED); 37 38 GUEST_DONE(); 39 } 40 41 /* Stash state passed via VMSA before any compiled code runs. */ 42 extern void guest_code_xsave(void); 43 asm("guest_code_xsave:\n" 44 "mov $-1, %eax\n" 45 "mov $-1, %edx\n" 46 "xsave (%rdi)\n" 47 "jmp guest_sev_es_code"); 48 49 static void compare_xsave(u8 *from_host, u8 *from_guest) 50 { 51 int i; 52 bool bad = false; 53 for (i = 0; i < 4095; i++) { 54 if (from_host[i] != from_guest[i]) { 55 printf("mismatch at %02hhx | %02hhx %02hhx\n", i, from_host[i], from_guest[i]); 56 bad = true; 57 } 58 } 59 60 if (bad) 61 abort(); 62 } 63 64 static void test_sync_vmsa(uint32_t policy) 65 { 66 struct kvm_vcpu *vcpu; 67 struct kvm_vm *vm; 68 vm_vaddr_t gva; 69 void *hva; 70 71 double x87val = M_PI; 72 struct kvm_xsave __attribute__((aligned(64))) xsave = { 0 }; 73 struct kvm_sregs sregs; 74 struct kvm_xcrs xcrs = { 75 .nr_xcrs = 1, 76 .xcrs[0].xcr = 0, 77 .xcrs[0].value = XFEATURE_MASK_X87_AVX, 78 }; 79 80 vm = vm_sev_create_with_one_vcpu(KVM_X86_SEV_ES_VM, guest_code_xsave, &vcpu); 81 gva = vm_vaddr_alloc_shared(vm, PAGE_SIZE, KVM_UTIL_MIN_VADDR, 82 MEM_REGION_TEST_DATA); 83 hva = addr_gva2hva(vm, gva); 84 85 vcpu_args_set(vcpu, 1, gva); 86 87 vcpu_sregs_get(vcpu, &sregs); 88 sregs.cr4 |= X86_CR4_OSFXSR | X86_CR4_OSXSAVE; 89 vcpu_sregs_set(vcpu, &sregs); 90 91 vcpu_xcrs_set(vcpu, &xcrs); 92 asm("fninit\n" 93 "vpcmpeqb %%ymm4, %%ymm4, %%ymm4\n" 94 "fldl %3\n" 95 "xsave (%2)\n" 96 "fstp %%st\n" 97 : "=m"(xsave) 98 : "A"(XFEATURE_MASK_X87_AVX), "r"(&xsave), "m" (x87val) 99 : "ymm4", "st", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)"); 100 vcpu_xsave_set(vcpu, &xsave); 101 102 vm_sev_launch(vm, SEV_POLICY_ES | policy, NULL); 103 104 /* This page is shared, so make it decrypted. */ 105 memset(hva, 0, 4096); 106 107 vcpu_run(vcpu); 108 109 TEST_ASSERT(vcpu->run->exit_reason == KVM_EXIT_SYSTEM_EVENT, 110 "Wanted SYSTEM_EVENT, got %s", 111 exit_reason_str(vcpu->run->exit_reason)); 112 TEST_ASSERT_EQ(vcpu->run->system_event.type, KVM_SYSTEM_EVENT_SEV_TERM); 113 TEST_ASSERT_EQ(vcpu->run->system_event.ndata, 1); 114 TEST_ASSERT_EQ(vcpu->run->system_event.data[0], GHCB_MSR_TERM_REQ); 115 116 compare_xsave((u8 *)&xsave, (u8 *)hva); 117 118 kvm_vm_free(vm); 119 } 120 121 static void test_sev(void *guest_code, uint64_t policy) 122 { 123 struct kvm_vcpu *vcpu; 124 struct kvm_vm *vm; 125 struct ucall uc; 126 127 uint32_t type = policy & SEV_POLICY_ES ? KVM_X86_SEV_ES_VM : KVM_X86_SEV_VM; 128 129 vm = vm_sev_create_with_one_vcpu(type, guest_code, &vcpu); 130 131 /* TODO: Validate the measurement is as expected. */ 132 vm_sev_launch(vm, policy, NULL); 133 134 for (;;) { 135 vcpu_run(vcpu); 136 137 if (policy & SEV_POLICY_ES) { 138 TEST_ASSERT(vcpu->run->exit_reason == KVM_EXIT_SYSTEM_EVENT, 139 "Wanted SYSTEM_EVENT, got %s", 140 exit_reason_str(vcpu->run->exit_reason)); 141 TEST_ASSERT_EQ(vcpu->run->system_event.type, KVM_SYSTEM_EVENT_SEV_TERM); 142 TEST_ASSERT_EQ(vcpu->run->system_event.ndata, 1); 143 TEST_ASSERT_EQ(vcpu->run->system_event.data[0], GHCB_MSR_TERM_REQ); 144 break; 145 } 146 147 switch (get_ucall(vcpu, &uc)) { 148 case UCALL_SYNC: 149 continue; 150 case UCALL_DONE: 151 return; 152 case UCALL_ABORT: 153 REPORT_GUEST_ASSERT(uc); 154 default: 155 TEST_FAIL("Unexpected exit: %s", 156 exit_reason_str(vcpu->run->exit_reason)); 157 } 158 } 159 160 kvm_vm_free(vm); 161 } 162 163 int main(int argc, char *argv[]) 164 { 165 TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SEV)); 166 167 test_sev(guest_sev_code, SEV_POLICY_NO_DBG); 168 test_sev(guest_sev_code, 0); 169 170 if (kvm_cpu_has(X86_FEATURE_SEV_ES)) { 171 test_sev(guest_sev_es_code, SEV_POLICY_ES | SEV_POLICY_NO_DBG); 172 test_sev(guest_sev_es_code, SEV_POLICY_ES); 173 174 if (kvm_has_cap(KVM_CAP_XCRS) && 175 (xgetbv(0) & XFEATURE_MASK_X87_AVX) == XFEATURE_MASK_X87_AVX) { 176 test_sync_vmsa(0); 177 test_sync_vmsa(SEV_POLICY_NO_DBG); 178 } 179 } 180 181 return 0; 182 } 183
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.