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_snp_code(void) 20 { 21 uint64_t sev_msr = rdmsr(MSR_AMD64_SEV); 22 23 GUEST_ASSERT(sev_msr & MSR_AMD64_SEV_ENABLED); 24 GUEST_ASSERT(sev_msr & MSR_AMD64_SEV_ES_ENABLED); 25 GUEST_ASSERT(sev_msr & MSR_AMD64_SEV_SNP_ENABLED); 26 27 wrmsr(MSR_AMD64_SEV_ES_GHCB, GHCB_MSR_TERM_REQ); 28 vmgexit(); 29 } 30 31 static void guest_sev_es_code(void) 32 { 33 /* TODO: Check CPUID after GHCB-based hypercall support is added. */ 34 GUEST_ASSERT(rdmsr(MSR_AMD64_SEV) & MSR_AMD64_SEV_ENABLED); 35 GUEST_ASSERT(rdmsr(MSR_AMD64_SEV) & MSR_AMD64_SEV_ES_ENABLED); 36 37 /* 38 * TODO: Add GHCB and ucall support for SEV-ES guests. For now, simply 39 * force "termination" to signal "done" via the GHCB MSR protocol. 40 */ 41 wrmsr(MSR_AMD64_SEV_ES_GHCB, GHCB_MSR_TERM_REQ); 42 vmgexit(); 43 } 44 45 static void guest_sev_code(void) 46 { 47 GUEST_ASSERT(this_cpu_has(X86_FEATURE_SEV)); 48 GUEST_ASSERT(rdmsr(MSR_AMD64_SEV) & MSR_AMD64_SEV_ENABLED); 49 50 GUEST_DONE(); 51 } 52 53 /* Stash state passed via VMSA before any compiled code runs. */ 54 extern void guest_code_xsave(void); 55 asm("guest_code_xsave:\n" 56 "mov $" __stringify(XFEATURE_MASK_X87_AVX) ", %eax\n" 57 "xor %edx, %edx\n" 58 "xsave (%rdi)\n" 59 "jmp guest_sev_es_code"); 60 61 static void compare_xsave(u8 *from_host, u8 *from_guest) 62 { 63 int i; 64 bool bad = false; 65 for (i = 0; i < 4095; i++) { 66 if (from_host[i] != from_guest[i]) { 67 printf("mismatch at %u | %02hhx %02hhx\n", 68 i, from_host[i], from_guest[i]); 69 bad = true; 70 } 71 } 72 73 if (bad) 74 abort(); 75 } 76 77 static void test_sync_vmsa(uint32_t type, uint64_t policy) 78 { 79 struct kvm_vcpu *vcpu; 80 struct kvm_vm *vm; 81 vm_vaddr_t gva; 82 void *hva; 83 84 double x87val = M_PI; 85 struct kvm_xsave __attribute__((aligned(64))) xsave = { 0 }; 86 87 vm = vm_sev_create_with_one_vcpu(type, guest_code_xsave, &vcpu); 88 gva = vm_vaddr_alloc_shared(vm, PAGE_SIZE, KVM_UTIL_MIN_VADDR, 89 MEM_REGION_TEST_DATA); 90 hva = addr_gva2hva(vm, gva); 91 92 vcpu_args_set(vcpu, 1, gva); 93 94 asm("fninit\n" 95 "vpcmpeqb %%ymm4, %%ymm4, %%ymm4\n" 96 "fldl %3\n" 97 "xsave (%2)\n" 98 "fstp %%st\n" 99 : "=m"(xsave) 100 : "A"(XFEATURE_MASK_X87_AVX), "r"(&xsave), "m" (x87val) 101 : "ymm4", "st", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)"); 102 vcpu_xsave_set(vcpu, &xsave); 103 104 vm_sev_launch(vm, policy, NULL); 105 106 /* This page is shared, so make it decrypted. */ 107 memset(hva, 0, 4096); 108 109 vcpu_run(vcpu); 110 111 TEST_ASSERT(vcpu->run->exit_reason == KVM_EXIT_SYSTEM_EVENT, 112 "Wanted SYSTEM_EVENT, got %s", 113 exit_reason_str(vcpu->run->exit_reason)); 114 TEST_ASSERT_EQ(vcpu->run->system_event.type, KVM_SYSTEM_EVENT_SEV_TERM); 115 TEST_ASSERT_EQ(vcpu->run->system_event.ndata, 1); 116 TEST_ASSERT_EQ(vcpu->run->system_event.data[0], GHCB_MSR_TERM_REQ); 117 118 compare_xsave((u8 *)&xsave, (u8 *)hva); 119 120 kvm_vm_free(vm); 121 } 122 123 static void test_sev(void *guest_code, uint32_t type, uint64_t policy) 124 { 125 struct kvm_vcpu *vcpu; 126 struct kvm_vm *vm; 127 struct ucall uc; 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 (is_sev_es_vm(vm)) { 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 static void guest_shutdown_code(void) 164 { 165 struct desc_ptr idt; 166 167 /* Clobber the IDT so that #UD is guaranteed to trigger SHUTDOWN. */ 168 memset(&idt, 0, sizeof(idt)); 169 set_idt(&idt); 170 171 __asm__ __volatile__("ud2"); 172 } 173 174 static void test_sev_shutdown(uint32_t type, uint64_t policy) 175 { 176 struct kvm_vcpu *vcpu; 177 struct kvm_vm *vm; 178 179 vm = vm_sev_create_with_one_vcpu(type, guest_shutdown_code, &vcpu); 180 181 vm_sev_launch(vm, policy, NULL); 182 183 vcpu_run(vcpu); 184 TEST_ASSERT(vcpu->run->exit_reason == KVM_EXIT_SHUTDOWN, 185 "Wanted SHUTDOWN, got %s", 186 exit_reason_str(vcpu->run->exit_reason)); 187 188 kvm_vm_free(vm); 189 } 190 191 static void test_sev_smoke(void *guest, uint32_t type, uint64_t policy) 192 { 193 const u64 xf_mask = XFEATURE_MASK_X87_AVX; 194 195 if (type == KVM_X86_SNP_VM) 196 test_sev(guest, type, policy | SNP_POLICY_DBG); 197 else 198 test_sev(guest, type, policy | SEV_POLICY_NO_DBG); 199 test_sev(guest, type, policy); 200 201 if (type == KVM_X86_SEV_VM) 202 return; 203 204 test_sev_shutdown(type, policy); 205 206 if (kvm_has_cap(KVM_CAP_XCRS) && 207 (xgetbv(0) & kvm_cpu_supported_xcr0() & xf_mask) == xf_mask) { 208 test_sync_vmsa(type, policy); 209 if (type == KVM_X86_SNP_VM) 210 test_sync_vmsa(type, policy | SNP_POLICY_DBG); 211 else 212 test_sync_vmsa(type, policy | SEV_POLICY_NO_DBG); 213 } 214 } 215 216 int main(int argc, char *argv[]) 217 { 218 TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SEV)); 219 220 test_sev_smoke(guest_sev_code, KVM_X86_SEV_VM, 0); 221 222 if (kvm_cpu_has(X86_FEATURE_SEV_ES)) 223 test_sev_smoke(guest_sev_es_code, KVM_X86_SEV_ES_VM, SEV_POLICY_ES); 224 225 if (kvm_cpu_has(X86_FEATURE_SEV_SNP)) 226 test_sev_smoke(guest_snp_code, KVM_X86_SNP_VM, snp_default_policy()); 227 228 return 0; 229 } 230