File Index
Files and directories
Sign in
FILE

sev_smoke_test.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/kvm/x86/sev_smoke_test.c C
  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
 19static 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
 31static 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
 45static 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.  */
 54extern void guest_code_xsave(void);
 55asm("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
 61static 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
 77static 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, PAGE_SIZE);
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
123static 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
163static 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
174static 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
191static 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
216int 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}
~/ Read-only file preview.