Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0-only
2#include <linux/kvm.h>
3#include <linux/psp-sev.h>
4#include <stdio.h>
5#include <sys/ioctl.h>
6#include <stdlib.h>
7#include <errno.h>
8#include <pthread.h>
9
10#include "test_util.h"
11#include "kvm_util.h"
12#include "processor.h"
13#include "sev.h"
14#include "kselftest.h"
15
16#define NR_MIGRATE_TEST_VCPUS 4
17#define NR_MIGRATE_TEST_VMS 3
18#define NR_LOCK_TESTING_THREADS 3
19#define NR_LOCK_TESTING_ITERATIONS 10000
20
21bool have_sev_es;
22
23static struct kvm_vm *sev_vm_create(bool es)
24{
25 struct kvm_vm *vm;
26 int i;
27
28 vm = vm_create_barebones();
29 if (!es)
30 sev_vm_init(vm);
31 else
32 sev_es_vm_init(vm);
33
34 for (i = 0; i < NR_MIGRATE_TEST_VCPUS; ++i)
35 __vm_vcpu_add(vm, i);
36
37 sev_vm_launch(vm, es ? SEV_POLICY_ES : 0);
38
39 return vm;
40}
41
42static struct kvm_vm *aux_vm_create(bool with_vcpus)
43{
44 struct kvm_vm *vm;
45 int i;
46
47 vm = vm_create_barebones();
48 if (!with_vcpus)
49 return vm;
50
51 for (i = 0; i < NR_MIGRATE_TEST_VCPUS; ++i)
52 __vm_vcpu_add(vm, i);
53
54 return vm;
55}
56
57static int __sev_migrate_from(struct kvm_vm *dst, struct kvm_vm *src)
58{
59 return __vm_enable_cap(dst, KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM, src->fd);
60}
61
62
63static void sev_migrate_from(struct kvm_vm *dst, struct kvm_vm *src)
64{
65 int ret;
66
67 ret = __sev_migrate_from(dst, src);
68 TEST_ASSERT(!ret, "Migration failed, ret: %d, errno: %d", ret, errno);
69}
70
71static void test_sev_migrate_from(bool es)
72{
73 struct kvm_vm *src_vm;
74 struct kvm_vm *dst_vms[NR_MIGRATE_TEST_VMS];
75 int i, ret;
76
77 src_vm = sev_vm_create(es);
78 for (i = 0; i < NR_MIGRATE_TEST_VMS; ++i)
79 dst_vms[i] = aux_vm_create(true);
80
81 /* Initial migration from the src to the first dst. */
82 sev_migrate_from(dst_vms[0], src_vm);
83
84 for (i = 1; i < NR_MIGRATE_TEST_VMS; i++)
85 sev_migrate_from(dst_vms[i], dst_vms[i - 1]);
86
87 /* Migrate the guest back to the original VM. */
88 ret = __sev_migrate_from(src_vm, dst_vms[NR_MIGRATE_TEST_VMS - 1]);
89 TEST_ASSERT(ret == -1 && errno == EIO,
90 "VM that was migrated from should be dead. ret %d, errno: %d", ret,
91 errno);
92
93 kvm_vm_free(src_vm);
94 for (i = 0; i < NR_MIGRATE_TEST_VMS; ++i)
95 kvm_vm_free(dst_vms[i]);
96}
97
98struct locking_thread_input {
99 struct kvm_vm *vm;
100 struct kvm_vm *source_vms[NR_LOCK_TESTING_THREADS];
101};
102
103static void *locking_test_thread(void *arg)
104{
105 int i, j;
106 struct locking_thread_input *input = (struct locking_thread_input *)arg;
107
108 for (i = 0; i < NR_LOCK_TESTING_ITERATIONS; ++i) {
109 j = i % NR_LOCK_TESTING_THREADS;
110 __sev_migrate_from(input->vm, input->source_vms[j]);
111 }
112
113 return NULL;
114}
115
116static void test_sev_migrate_locking(void)
117{
118 struct locking_thread_input input[NR_LOCK_TESTING_THREADS];
119 pthread_t pt[NR_LOCK_TESTING_THREADS];
120 int i;
121
122 for (i = 0; i < NR_LOCK_TESTING_THREADS; ++i) {
123 input[i].vm = sev_vm_create(/* es= */ false);
124 input[0].source_vms[i] = input[i].vm;
125 }
126 for (i = 1; i < NR_LOCK_TESTING_THREADS; ++i)
127 memcpy(input[i].source_vms, input[0].source_vms,
128 sizeof(input[i].source_vms));
129
130 for (i = 0; i < NR_LOCK_TESTING_THREADS; ++i)
131 pthread_create(&pt[i], NULL, locking_test_thread, &input[i]);
132
133 for (i = 0; i < NR_LOCK_TESTING_THREADS; ++i)
134 pthread_join(pt[i], NULL);
135 for (i = 0; i < NR_LOCK_TESTING_THREADS; ++i)
136 kvm_vm_free(input[i].vm);
137}
138
139static void test_sev_migrate_parameters(void)
140{
141 struct kvm_vm *sev_vm, *sev_es_vm, *vm_no_vcpu, *vm_no_sev,
142 *sev_es_vm_no_vmsa;
143 int ret;
144
145 vm_no_vcpu = vm_create_barebones();
146 vm_no_sev = aux_vm_create(true);
147 ret = __sev_migrate_from(vm_no_vcpu, vm_no_sev);
148 TEST_ASSERT(ret == -1 && errno == EINVAL,
149 "Migrations require SEV enabled. ret %d, errno: %d", ret,
150 errno);
151
152 if (!have_sev_es)
153 goto out;
154
155 sev_vm = sev_vm_create(/* es= */ false);
156 sev_es_vm = sev_vm_create(/* es= */ true);
157 sev_es_vm_no_vmsa = vm_create_barebones();
158 sev_es_vm_init(sev_es_vm_no_vmsa);
159 __vm_vcpu_add(sev_es_vm_no_vmsa, 1);
160
161 ret = __sev_migrate_from(sev_vm, sev_es_vm);
162 TEST_ASSERT(
163 ret == -1 && errno == EINVAL,
164 "Should not be able migrate to SEV enabled VM. ret: %d, errno: %d",
165 ret, errno);
166
167 ret = __sev_migrate_from(sev_es_vm, sev_vm);
168 TEST_ASSERT(
169 ret == -1 && errno == EINVAL,
170 "Should not be able migrate to SEV-ES enabled VM. ret: %d, errno: %d",
171 ret, errno);
172
173 ret = __sev_migrate_from(vm_no_vcpu, sev_es_vm);
174 TEST_ASSERT(
175 ret == -1 && errno == EINVAL,
176 "SEV-ES migrations require same number of vCPUS. ret: %d, errno: %d",
177 ret, errno);
178
179 ret = __sev_migrate_from(vm_no_vcpu, sev_es_vm_no_vmsa);
180 TEST_ASSERT(
181 ret == -1 && errno == EINVAL,
182 "SEV-ES migrations require UPDATE_VMSA. ret %d, errno: %d",
183 ret, errno);
184
185 kvm_vm_free(sev_vm);
186 kvm_vm_free(sev_es_vm);
187 kvm_vm_free(sev_es_vm_no_vmsa);
188out:
189 kvm_vm_free(vm_no_vcpu);
190 kvm_vm_free(vm_no_sev);
191}
192
193static int __sev_mirror_create(struct kvm_vm *dst, struct kvm_vm *src)
194{
195 return __vm_enable_cap(dst, KVM_CAP_VM_COPY_ENC_CONTEXT_FROM, src->fd);
196}
197
198
199static void sev_mirror_create(struct kvm_vm *dst, struct kvm_vm *src)
200{
201 int ret;
202
203 ret = __sev_mirror_create(dst, src);
204 TEST_ASSERT(!ret, "Copying context failed, ret: %d, errno: %d", ret, errno);
205}
206
207static void verify_mirror_allowed_cmds(struct kvm_vm *vm)
208{
209 struct kvm_sev_guest_status status;
210 int cmd_id;
211
212 for (cmd_id = KVM_SEV_INIT; cmd_id < KVM_SEV_NR_MAX; ++cmd_id) {
213 int ret;
214
215 /*
216 * These commands are allowed for mirror VMs, all others are
217 * not.
218 */
219 switch (cmd_id) {
220 case KVM_SEV_LAUNCH_UPDATE_VMSA:
221 case KVM_SEV_GUEST_STATUS:
222 case KVM_SEV_DBG_DECRYPT:
223 case KVM_SEV_DBG_ENCRYPT:
224 continue;
225 default:
226 break;
227 }
228
229 /*
230 * These commands should be disallowed before the data
231 * parameter is examined so NULL is OK here.
232 */
233 ret = __vm_sev_ioctl(vm, cmd_id, NULL);
234 TEST_ASSERT(
235 ret == -1 && errno == EINVAL,
236 "Should not be able call command: %d. ret: %d, errno: %d",
237 cmd_id, ret, errno);
238 }
239
240 vm_sev_ioctl(vm, KVM_SEV_GUEST_STATUS, &status);
241}
242
243static void test_sev_mirror(bool es)
244{
245 struct kvm_vm *src_vm, *dst_vm;
246 int i;
247
248 src_vm = sev_vm_create(es);
249 dst_vm = aux_vm_create(false);
250
251 sev_mirror_create(dst_vm, src_vm);
252
253 /* Check that we can complete creation of the mirror VM. */
254 for (i = 0; i < NR_MIGRATE_TEST_VCPUS; ++i)
255 __vm_vcpu_add(dst_vm, i);
256
257 if (es)
258 vm_sev_ioctl(dst_vm, KVM_SEV_LAUNCH_UPDATE_VMSA, NULL);
259
260 verify_mirror_allowed_cmds(dst_vm);
261
262 kvm_vm_free(src_vm);
263 kvm_vm_free(dst_vm);
264}
265
266static void test_sev_mirror_parameters(void)
267{
268 struct kvm_vm *sev_vm, *sev_es_vm, *vm_no_vcpu, *vm_with_vcpu;
269 int ret;
270
271 sev_vm = sev_vm_create(/* es= */ false);
272 vm_with_vcpu = aux_vm_create(true);
273 vm_no_vcpu = aux_vm_create(false);
274
275 ret = __sev_mirror_create(sev_vm, sev_vm);
276 TEST_ASSERT(
277 ret == -1 && errno == EINVAL,
278 "Should not be able copy context to self. ret: %d, errno: %d",
279 ret, errno);
280
281 ret = __sev_mirror_create(vm_no_vcpu, vm_with_vcpu);
282 TEST_ASSERT(ret == -1 && errno == EINVAL,
283 "Copy context requires SEV enabled. ret %d, errno: %d", ret,
284 errno);
285
286 ret = __sev_mirror_create(vm_with_vcpu, sev_vm);
287 TEST_ASSERT(
288 ret == -1 && errno == EINVAL,
289 "SEV copy context requires no vCPUS on the destination. ret: %d, errno: %d",
290 ret, errno);
291
292 if (!have_sev_es)
293 goto out;
294
295 sev_es_vm = sev_vm_create(/* es= */ true);
296 ret = __sev_mirror_create(sev_vm, sev_es_vm);
297 TEST_ASSERT(
298 ret == -1 && errno == EINVAL,
299 "Should not be able copy context to SEV enabled VM. ret: %d, errno: %d",
300 ret, errno);
301
302 ret = __sev_mirror_create(sev_es_vm, sev_vm);
303 TEST_ASSERT(
304 ret == -1 && errno == EINVAL,
305 "Should not be able copy context to SEV-ES enabled VM. ret: %d, errno: %d",
306 ret, errno);
307
308 kvm_vm_free(sev_es_vm);
309
310out:
311 kvm_vm_free(sev_vm);
312 kvm_vm_free(vm_with_vcpu);
313 kvm_vm_free(vm_no_vcpu);
314}
315
316static void test_sev_move_copy(void)
317{
318 struct kvm_vm *dst_vm, *dst2_vm, *dst3_vm, *sev_vm, *mirror_vm,
319 *dst_mirror_vm, *dst2_mirror_vm, *dst3_mirror_vm;
320
321 sev_vm = sev_vm_create(/* es= */ false);
322 dst_vm = aux_vm_create(true);
323 dst2_vm = aux_vm_create(true);
324 dst3_vm = aux_vm_create(true);
325 mirror_vm = aux_vm_create(false);
326 dst_mirror_vm = aux_vm_create(false);
327 dst2_mirror_vm = aux_vm_create(false);
328 dst3_mirror_vm = aux_vm_create(false);
329
330 sev_mirror_create(mirror_vm, sev_vm);
331
332 sev_migrate_from(dst_mirror_vm, mirror_vm);
333 sev_migrate_from(dst_vm, sev_vm);
334
335 sev_migrate_from(dst2_vm, dst_vm);
336 sev_migrate_from(dst2_mirror_vm, dst_mirror_vm);
337
338 sev_migrate_from(dst3_mirror_vm, dst2_mirror_vm);
339 sev_migrate_from(dst3_vm, dst2_vm);
340
341 kvm_vm_free(dst_vm);
342 kvm_vm_free(sev_vm);
343 kvm_vm_free(dst2_vm);
344 kvm_vm_free(dst3_vm);
345 kvm_vm_free(mirror_vm);
346 kvm_vm_free(dst_mirror_vm);
347 kvm_vm_free(dst2_mirror_vm);
348 kvm_vm_free(dst3_mirror_vm);
349
350 /*
351 * Run similar test be destroy mirrors before mirrored VMs to ensure
352 * destruction is done safely.
353 */
354 sev_vm = sev_vm_create(/* es= */ false);
355 dst_vm = aux_vm_create(true);
356 mirror_vm = aux_vm_create(false);
357 dst_mirror_vm = aux_vm_create(false);
358
359 sev_mirror_create(mirror_vm, sev_vm);
360
361 sev_migrate_from(dst_mirror_vm, mirror_vm);
362 sev_migrate_from(dst_vm, sev_vm);
363
364 kvm_vm_free(mirror_vm);
365 kvm_vm_free(dst_mirror_vm);
366 kvm_vm_free(dst_vm);
367 kvm_vm_free(sev_vm);
368}
369
370int main(int argc, char *argv[])
371{
372 TEST_REQUIRE(kvm_has_cap(KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM));
373 TEST_REQUIRE(kvm_has_cap(KVM_CAP_VM_COPY_ENC_CONTEXT_FROM));
374
375 TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SEV));
376
377 have_sev_es = kvm_cpu_has(X86_FEATURE_SEV_ES);
378
379 if (kvm_has_cap(KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM)) {
380 test_sev_migrate_from(/* es= */ false);
381 if (have_sev_es)
382 test_sev_migrate_from(/* es= */ true);
383 test_sev_migrate_locking();
384 test_sev_migrate_parameters();
385 if (kvm_has_cap(KVM_CAP_VM_COPY_ENC_CONTEXT_FROM))
386 test_sev_move_copy();
387 }
388 if (kvm_has_cap(KVM_CAP_VM_COPY_ENC_CONTEXT_FROM)) {
389 test_sev_mirror(/* es= */ false);
390 if (have_sev_es)
391 test_sev_mirror(/* es= */ true);
392 test_sev_mirror_parameters();
393 }
394 return 0;
395}