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 "svm_util.h"
14 #include "kselftest.h"
15 #include "../lib/kvm_util_internal.h"
16 
17 #define SEV_POLICY_ES 0b100
18 
19 #define NR_MIGRATE_TEST_VCPUS 4
20 #define NR_MIGRATE_TEST_VMS 3
21 #define NR_LOCK_TESTING_THREADS 3
22 #define NR_LOCK_TESTING_ITERATIONS 10000
23 
24 static void sev_ioctl(int vm_fd, int cmd_id, void *data)
25 {
26 	struct kvm_sev_cmd cmd = {
27 		.id = cmd_id,
28 		.data = (uint64_t)data,
29 		.sev_fd = open_sev_dev_path_or_exit(),
30 	};
31 	int ret;
32 
33 	ret = ioctl(vm_fd, KVM_MEMORY_ENCRYPT_OP, &cmd);
34 	TEST_ASSERT((ret == 0 || cmd.error == SEV_RET_SUCCESS),
35 		    "%d failed: return code: %d, errno: %d, fw error: %d",
36 		    cmd_id, ret, errno, cmd.error);
37 }
38 
39 static struct kvm_vm *sev_vm_create(bool es)
40 {
41 	struct kvm_vm *vm;
42 	struct kvm_sev_launch_start start = { 0 };
43 	int i;
44 
45 	vm = vm_create(VM_MODE_DEFAULT, 0, O_RDWR);
46 	sev_ioctl(vm->fd, es ? KVM_SEV_ES_INIT : KVM_SEV_INIT, NULL);
47 	for (i = 0; i < NR_MIGRATE_TEST_VCPUS; ++i)
48 		vm_vcpu_add(vm, i);
49 	if (es)
50 		start.policy |= SEV_POLICY_ES;
51 	sev_ioctl(vm->fd, KVM_SEV_LAUNCH_START, &start);
52 	if (es)
53 		sev_ioctl(vm->fd, KVM_SEV_LAUNCH_UPDATE_VMSA, NULL);
54 	return vm;
55 }
56 
57 static struct kvm_vm *__vm_create(void)
58 {
59 	struct kvm_vm *vm;
60 	int i;
61 
62 	vm = vm_create(VM_MODE_DEFAULT, 0, O_RDWR);
63 	for (i = 0; i < NR_MIGRATE_TEST_VCPUS; ++i)
64 		vm_vcpu_add(vm, i);
65 
66 	return vm;
67 }
68 
69 static int __sev_migrate_from(int dst_fd, int src_fd)
70 {
71 	struct kvm_enable_cap cap = {
72 		.cap = KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM,
73 		.args = { src_fd }
74 	};
75 
76 	return ioctl(dst_fd, KVM_ENABLE_CAP, &cap);
77 }
78 
79 
80 static void sev_migrate_from(int dst_fd, int src_fd)
81 {
82 	int ret;
83 
84 	ret = __sev_migrate_from(dst_fd, src_fd);
85 	TEST_ASSERT(!ret, "Migration failed, ret: %d, errno: %d\n", ret, errno);
86 }
87 
88 static void test_sev_migrate_from(bool es)
89 {
90 	struct kvm_vm *src_vm;
91 	struct kvm_vm *dst_vms[NR_MIGRATE_TEST_VMS];
92 	int i;
93 
94 	src_vm = sev_vm_create(es);
95 	for (i = 0; i < NR_MIGRATE_TEST_VMS; ++i)
96 		dst_vms[i] = __vm_create();
97 
98 	/* Initial migration from the src to the first dst. */
99 	sev_migrate_from(dst_vms[0]->fd, src_vm->fd);
100 
101 	for (i = 1; i < NR_MIGRATE_TEST_VMS; i++)
102 		sev_migrate_from(dst_vms[i]->fd, dst_vms[i - 1]->fd);
103 
104 	/* Migrate the guest back to the original VM. */
105 	sev_migrate_from(src_vm->fd, dst_vms[NR_MIGRATE_TEST_VMS - 1]->fd);
106 
107 	kvm_vm_free(src_vm);
108 	for (i = 0; i < NR_MIGRATE_TEST_VMS; ++i)
109 		kvm_vm_free(dst_vms[i]);
110 }
111 
112 struct locking_thread_input {
113 	struct kvm_vm *vm;
114 	int source_fds[NR_LOCK_TESTING_THREADS];
115 };
116 
117 static void *locking_test_thread(void *arg)
118 {
119 	int i, j;
120 	struct locking_thread_input *input = (struct locking_thread_input *)arg;
121 
122 	for (i = 0; i < NR_LOCK_TESTING_ITERATIONS; ++i) {
123 		j = i % NR_LOCK_TESTING_THREADS;
124 		__sev_migrate_from(input->vm->fd, input->source_fds[j]);
125 	}
126 
127 	return NULL;
128 }
129 
130 static void test_sev_migrate_locking(void)
131 {
132 	struct locking_thread_input input[NR_LOCK_TESTING_THREADS];
133 	pthread_t pt[NR_LOCK_TESTING_THREADS];
134 	int i;
135 
136 	for (i = 0; i < NR_LOCK_TESTING_THREADS; ++i) {
137 		input[i].vm = sev_vm_create(/* es= */ false);
138 		input[0].source_fds[i] = input[i].vm->fd;
139 	}
140 	for (i = 1; i < NR_LOCK_TESTING_THREADS; ++i)
141 		memcpy(input[i].source_fds, input[0].source_fds,
142 		       sizeof(input[i].source_fds));
143 
144 	for (i = 0; i < NR_LOCK_TESTING_THREADS; ++i)
145 		pthread_create(&pt[i], NULL, locking_test_thread, &input[i]);
146 
147 	for (i = 0; i < NR_LOCK_TESTING_THREADS; ++i)
148 		pthread_join(pt[i], NULL);
149 }
150 
151 static void test_sev_migrate_parameters(void)
152 {
153 	struct kvm_vm *sev_vm, *sev_es_vm, *vm_no_vcpu, *vm_no_sev,
154 		*sev_es_vm_no_vmsa;
155 	int ret;
156 
157 	sev_vm = sev_vm_create(/* es= */ false);
158 	sev_es_vm = sev_vm_create(/* es= */ true);
159 	vm_no_vcpu = vm_create(VM_MODE_DEFAULT, 0, O_RDWR);
160 	vm_no_sev = __vm_create();
161 	sev_es_vm_no_vmsa = vm_create(VM_MODE_DEFAULT, 0, O_RDWR);
162 	sev_ioctl(sev_es_vm_no_vmsa->fd, KVM_SEV_ES_INIT, NULL);
163 	vm_vcpu_add(sev_es_vm_no_vmsa, 1);
164 
165 
166 	ret = __sev_migrate_from(sev_vm->fd, sev_es_vm->fd);
167 	TEST_ASSERT(
168 		ret == -1 && errno == EINVAL,
169 		"Should not be able migrate to SEV enabled VM. ret: %d, errno: %d\n",
170 		ret, errno);
171 
172 	ret = __sev_migrate_from(sev_es_vm->fd, sev_vm->fd);
173 	TEST_ASSERT(
174 		ret == -1 && errno == EINVAL,
175 		"Should not be able migrate to SEV-ES enabled VM. ret: %d, errno: %d\n",
176 		ret, errno);
177 
178 	ret = __sev_migrate_from(vm_no_vcpu->fd, sev_es_vm->fd);
179 	TEST_ASSERT(
180 		ret == -1 && errno == EINVAL,
181 		"SEV-ES migrations require same number of vCPUS. ret: %d, errno: %d\n",
182 		ret, errno);
183 
184 	ret = __sev_migrate_from(vm_no_vcpu->fd, sev_es_vm_no_vmsa->fd);
185 	TEST_ASSERT(
186 		ret == -1 && errno == EINVAL,
187 		"SEV-ES migrations require UPDATE_VMSA. ret %d, errno: %d\n",
188 		ret, errno);
189 
190 	ret = __sev_migrate_from(vm_no_vcpu->fd, vm_no_sev->fd);
191 	TEST_ASSERT(ret == -1 && errno == EINVAL,
192 		    "Migrations require SEV enabled. ret %d, errno: %d\n", ret,
193 		    errno);
194 }
195 
196 int main(int argc, char *argv[])
197 {
198 	test_sev_migrate_from(/* es= */ false);
199 	test_sev_migrate_from(/* es= */ true);
200 	test_sev_migrate_locking();
201 	test_sev_migrate_parameters();
202 	return 0;
203 }
204