Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/kvm/x86/sev_smoke_test.c
38237 views
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, 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
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
231