]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: selftests: Add a test to verify SEV {en,de}crypt debug ioctls
authorSean Christopherson <seanjc@google.com>
Fri, 1 May 2026 20:35:33 +0000 (13:35 -0700)
committerSean Christopherson <seanjc@google.com>
Wed, 13 May 2026 22:03:16 +0000 (15:03 -0700)
Add a selftest to verify KVM's handling of {de,en}crypt debug ioctls,
specifically focusing on edge cases around the chunk (16 bytes) and page
(4096) sizes, where KVM had multiple bugs.  E.g. KVM would fail to handle
small sizes that aren't naturally aligned and sized, would buffer overflow
if the destination was unaligned but the source was not, etc.

Attempt to strike a balance between an exhaustive test and a reasonable
runtime.  On a system with both SEV and SEV-ES support, the current runtime
is under 45 seconds.  Which isn't great, but it's tolerable, and it's not
obvious which of the combinations are "better" than the others.

Link: https://patch.msgid.link/20260501203537.2120074-3-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
tools/testing/selftests/kvm/Makefile.kvm
tools/testing/selftests/kvm/include/x86/sev.h
tools/testing/selftests/kvm/x86/sev_dbg_test.c [new file with mode: 0644]

index 9118a5a51b89fdc696976eff0dc5dd1b57bd6b79..82fa943b95038eb9e17a89a2d11456a26a440b4f 100644 (file)
@@ -140,6 +140,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
index 1af44c151d60ae4e2dfc111eea93977d89a304d8..dec383e59a47e134c0f18355903b8229fc621794 100644 (file)
@@ -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 (file)
index 0000000..a9d8e4c
--- /dev/null
@@ -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;
+}