|
| 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 | +} |
0 commit comments