diff options
| author | Paolo Bonzini <pbonzini@redhat.com> | 2026-06-12 10:13:03 +0200 |
|---|---|---|
| committer | Paolo Bonzini <pbonzini@redhat.com> | 2026-06-12 10:13:03 +0200 |
| commit | b39c6bd6fd4dc05ce0608b5059f840eeec994e94 (patch) | |
| tree | 4db0f78d5434cd6f7e143d04c13c543eab125b0f /tools/testing/selftests | |
| parent | b02a4f8c4284e2cbbf539a95b27647687adae816 (diff) | |
| parent | 97cd21d57e9bd2da79845178d9250cfd19289cd4 (diff) | |
Merge tag 'kvm-x86-sev-7.2' of https://github.com/kvm-x86/linux into HEAD
KVM SEV changes for 7.2
- Don't advertise support for unusuable VM types, and account for VM types
that are disabled by firmware, e.g. to mitigate security vulnerabilities.
- Rewrite the SEV {en,de}crypt debug ioctls as they were riddle with bugs and
unnecessarily complicated, and add comprehensive tests.
- Clean up and deduplicate the SEV page pinning code.
- Fix minor goofs related to writing back CPUID information after firmware
rejects a CPUID page for an SNP vCPU.
Diffstat (limited to 'tools/testing/selftests')
| -rw-r--r-- | tools/testing/selftests/kvm/Makefile.kvm | 1 | ||||
| -rw-r--r-- | tools/testing/selftests/kvm/include/x86/sev.h | 24 | ||||
| -rw-r--r-- | tools/testing/selftests/kvm/x86/sev_dbg_test.c | 118 | ||||
| -rw-r--r-- | tools/testing/selftests/kvm/x86/sev_init2_tests.c | 14 | ||||
| -rw-r--r-- | tools/testing/selftests/kvm/x86/sev_migrate_tests.c | 2 | ||||
| -rw-r--r-- | tools/testing/selftests/kvm/x86/sev_smoke_test.c | 4 |
6 files changed, 152 insertions, 11 deletions
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm index 0a0a02a1dffe..2908eca1647a 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -141,6 +141,7 @@ TEST_GEN_PROGS_x86 += x86/tsc_msrs_test TEST_GEN_PROGS_x86 += x86/vmx_pmu_caps_test TEST_GEN_PROGS_x86 += x86/xen_shinfo_test TEST_GEN_PROGS_x86 += x86/xen_vmcall_test +TEST_GEN_PROGS_x86 += x86/sev_dbg_test TEST_GEN_PROGS_x86 += x86/sev_init2_tests TEST_GEN_PROGS_x86 += x86/sev_migrate_tests TEST_GEN_PROGS_x86 += x86/sev_smoke_test diff --git a/tools/testing/selftests/kvm/include/x86/sev.h b/tools/testing/selftests/kvm/include/x86/sev.h index 1af44c151d60..dec383e59a47 100644 --- a/tools/testing/selftests/kvm/include/x86/sev.h +++ b/tools/testing/selftests/kvm/include/x86/sev.h @@ -144,4 +144,28 @@ static inline void snp_launch_update_data(struct kvm_vm *vm, gpa_t gpa, vm_sev_ioctl(vm, KVM_SEV_SNP_LAUNCH_UPDATE, &update_data); } +static inline void sev_dbg_crypt_memory(struct kvm_vm *vm, unsigned int cmd, + void *dst, void *src, unsigned int len) +{ + struct kvm_sev_dbg dbg = { + .src_uaddr = (unsigned long)src, + .dst_uaddr = (unsigned long)dst, + .len = len, + }; + + vm_sev_ioctl(vm, cmd, &dbg); +} + +static inline void sev_decrypt_memory(struct kvm_vm *vm, void *dst, void *src, + unsigned int len) +{ + sev_dbg_crypt_memory(vm, KVM_SEV_DBG_DECRYPT, dst, src, len); +} + +static inline void sev_encrypt_memory(struct kvm_vm *vm, void *dst, void *src, + unsigned int len) +{ + sev_dbg_crypt_memory(vm, KVM_SEV_DBG_ENCRYPT, dst, src, len); +} + #endif /* SELFTEST_KVM_SEV_H */ diff --git a/tools/testing/selftests/kvm/x86/sev_dbg_test.c b/tools/testing/selftests/kvm/x86/sev_dbg_test.c new file mode 100644 index 000000000000..a9d8e4c059f9 --- /dev/null +++ b/tools/testing/selftests/kvm/x86/sev_dbg_test.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <fcntl.h> +#include <string.h> +#include <sys/ioctl.h> + +#include "test_util.h" +#include "kvm_util.h" +#include "processor.h" +#include "sev.h" + +#define BUFFER_SIZE (PAGE_SIZE * 2) + +static u8 *data; +static u8 src[BUFFER_SIZE] __aligned(PAGE_SIZE); +static u8 dst[BUFFER_SIZE] __aligned(PAGE_SIZE); + +static void validate_dst(int i, int nr_bytes, u8 pattern) +{ + for ( ; i < nr_bytes; i++) + TEST_ASSERT(dst[i] == pattern, + "Expected 0x%x at byte %u, got 0x%x", + pattern, i, dst[i]); +} + +static void validate_buffers(void) +{ + int i; + + for (i = 0; i < BUFFER_SIZE; i++) + TEST_ASSERT(src[i] == dst[i], + "Expected src[%u] (0x%x) == dst[%u] (0x%x)", + i, src[i], i, dst[i]); +} + +static void ____test_sev_dbg(struct kvm_vm *vm, int i, int j, int nr_bytes) +{ + u8 pattern = guest_random_u32(&guest_rng); + + if (i + nr_bytes > BUFFER_SIZE || j + nr_bytes > BUFFER_SIZE) + return; + + memset(&src[i], pattern, nr_bytes); + sev_encrypt_memory(vm, &data[j], &src[i], nr_bytes); + sev_decrypt_memory(vm, &dst[i], &data[j], nr_bytes); + validate_buffers(); + validate_dst(i, nr_bytes, pattern); +} + +static void __test_sev_dbg(struct kvm_vm *vm, int nr_bytes) +{ + /* + * In a perfect world, all sizes at all combinations within the buffers + * would be tested. In reality, even this much testing is quite slow. + * Target sizes and offsets around the chunk (16 bytes) and page (4096 + * bytes) sizes. + */ + int x[] = { 1, 8, 15, 16, 23 }; + int p = PAGE_SIZE - 24; + int i, j; + + ____test_sev_dbg(vm, 0, 0, nr_bytes); + + for (i = 0; i < ARRAY_SIZE(x); i++) { + for (j = 0; j < ARRAY_SIZE(x); j++) { + ____test_sev_dbg(vm, x[i], x[j], nr_bytes); + ____test_sev_dbg(vm, x[i], p + x[j], nr_bytes); + ____test_sev_dbg(vm, p + x[i], x[j], nr_bytes); + ____test_sev_dbg(vm, p + x[i], p + x[j], nr_bytes); + } + } +} + +static void test_sev_dbg(u32 type, u64 policy) +{ + int sizes[] = { 1, 8, 15, 16, 17, 32, 33 }; + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + int i; + + if (!(kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(type))) + return; + + vm = vm_sev_create_with_one_vcpu(type, NULL, &vcpu); + + data = addr_gva2hva(vm, vm_alloc(vm, BUFFER_SIZE, KVM_UTIL_MIN_VADDR)); + memset(data, 0xaa, BUFFER_SIZE); + + vm_sev_launch(vm, policy, NULL); + + sev_decrypt_memory(vm, dst, data, BUFFER_SIZE); + validate_dst(0, BUFFER_SIZE, 0xaa); + + memset(src, 0x55, BUFFER_SIZE); + sev_encrypt_memory(vm, data, src, BUFFER_SIZE); + sev_decrypt_memory(vm, dst, data, BUFFER_SIZE); + validate_dst(0, BUFFER_SIZE, 0x55); + + __test_sev_dbg(vm, PAGE_SIZE); + + for (i = 0; i < ARRAY_SIZE(sizes); i++) { + __test_sev_dbg(vm, sizes[i]); + __test_sev_dbg(vm, PAGE_SIZE - sizes[i]); + __test_sev_dbg(vm, PAGE_SIZE + sizes[i]); + __test_sev_dbg(vm, BUFFER_SIZE - sizes[i]); + } + + kvm_vm_free(vm); +} + +int main(int argc, char *argv[]) +{ + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SEV)); + + /* Note, KVM doesn't support {de,en}crypt commands for SNP. */ + test_sev_dbg(KVM_X86_SEV_VM, 0); + test_sev_dbg(KVM_X86_SEV_ES_VM, SEV_POLICY_ES); + return 0; +} diff --git a/tools/testing/selftests/kvm/x86/sev_init2_tests.c b/tools/testing/selftests/kvm/x86/sev_init2_tests.c index 8eeba2327c7c..8db88c355f16 100644 --- a/tools/testing/selftests/kvm/x86/sev_init2_tests.c +++ b/tools/testing/selftests/kvm/x86/sev_init2_tests.c @@ -136,16 +136,14 @@ int main(int argc, char *argv[]) kvm_check_cap(KVM_CAP_VM_TYPES), 1 << KVM_X86_SEV_VM); TEST_REQUIRE(kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_VM)); - have_sev_es = kvm_cpu_has(X86_FEATURE_SEV_ES); + have_sev_es = kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_ES_VM); - TEST_ASSERT(have_sev_es == !!(kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_ES_VM)), - "sev-es: KVM_CAP_VM_TYPES (%x) does not match cpuid (checking %x)", - kvm_check_cap(KVM_CAP_VM_TYPES), 1 << KVM_X86_SEV_ES_VM); + TEST_ASSERT(!have_sev_es || kvm_cpu_has(X86_FEATURE_SEV_ES), + "sev-es: SEV_ES_VM supported without SEV_ES in CPUID"); - have_snp = kvm_cpu_has(X86_FEATURE_SEV_SNP); - TEST_ASSERT(have_snp == !!(kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SNP_VM)), - "sev-snp: KVM_CAP_VM_TYPES (%x) indicates SNP support (bit %d), but CPUID does not", - kvm_check_cap(KVM_CAP_VM_TYPES), KVM_X86_SNP_VM); + have_snp = kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SNP_VM); + TEST_ASSERT(!have_snp || kvm_cpu_has(X86_FEATURE_SEV_SNP), + "sev-snp: SNP_VM supported without SEV_SNP in CPUID"); test_vm_types(); diff --git a/tools/testing/selftests/kvm/x86/sev_migrate_tests.c b/tools/testing/selftests/kvm/x86/sev_migrate_tests.c index 6b0928e69051..42bc023d5193 100644 --- a/tools/testing/selftests/kvm/x86/sev_migrate_tests.c +++ b/tools/testing/selftests/kvm/x86/sev_migrate_tests.c @@ -374,7 +374,7 @@ int main(int argc, char *argv[]) TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SEV)); - have_sev_es = kvm_cpu_has(X86_FEATURE_SEV_ES); + have_sev_es = kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_ES_VM); if (kvm_has_cap(KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM)) { test_sev_migrate_from(/* es= */ false); diff --git a/tools/testing/selftests/kvm/x86/sev_smoke_test.c b/tools/testing/selftests/kvm/x86/sev_smoke_test.c index 1a49ee391586..6b2cbe2a90b7 100644 --- a/tools/testing/selftests/kvm/x86/sev_smoke_test.c +++ b/tools/testing/selftests/kvm/x86/sev_smoke_test.c @@ -249,10 +249,10 @@ int main(int argc, char *argv[]) test_sev_smoke(guest_sev_code, KVM_X86_SEV_VM, 0); - if (kvm_cpu_has(X86_FEATURE_SEV_ES)) + if (kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_ES_VM)) test_sev_smoke(guest_sev_es_code, KVM_X86_SEV_ES_VM, SEV_POLICY_ES); - if (kvm_cpu_has(X86_FEATURE_SEV_SNP)) + if (kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SNP_VM)) test_sev_smoke(guest_snp_code, KVM_X86_SNP_VM, snp_default_policy()); return 0; |
