File Index
Files and directories
Sign in
FILE

sev_migrate_tests.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/kvm/x86/sev_migrate_tests.c C
  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}
~/ Read-only file preview.