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