[POC PATCH 2/6] KVM: selftests: Call snp_launch_update_data() providing copy of memory

Ackerley Tng
2026-03-26T23:37:03Z

Call snp_launch_update_data() providing a copy of the memory to be
loaded. KVM_SEV_SNP_LAUNCH_UPDATE populates memory into private memory by
first GUP-ing the source memory, then encrypting it into private memory.

The hva that was specified as the source is in this case also the
destination where the private memory will be placed after encryption.

KVM_SEV_SNP_LAUNCH_UPDATE requires the destination to be private memory,
but private memory cannot be accessed by the host and hence cannot be
GUP-ed. Hence, make a copy of the memory to be loaded, and use that as the
source, so that the source can be GUP-ed, and the destination is still
private.

Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
tools/testing/selftests/kvm/lib/x86/sev.c | 35 +++++++++++++++++++----
1 file changed, 29 insertions(+), 6 deletions(-)

diff --git a/tools/testing/selftests/kvm/lib/x86/sev.c b/tools/testing/selftests/kvm/lib/x86/sev.c
index d3a7241e5fc13..1b937034a5c11 100644
--- a/tools/testing/selftests/kvm/lib/x86/sev.c
+++ b/tools/testing/selftests/kvm/lib/x86/sev.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <stdint.h>
#include <stdbool.h>
+#include <sys/mman.h>

#include "sev.h"

@@ -31,17 +32,39 @@ static void encrypt_region(struct kvm_vm *vm, struct userspace_mem_region *regio
sparsebit_for_each_set_range(protected_phy_pages, i, j) {
const uint64_t size = (j - i + 1) * vm->page_size;
const uint64_t offset = (i - lowest_page_in_region) * vm->page_size;
+ void *source;
+
+ /*
+ * Is SNP the only place where private=true? If yes,
+ * then we don't need the private parameter, we can
+ * just check if the vm is SNP. Or maybe it depends on
+ * whether TDX, etc use the private parameter.
+ */
+ if (private) {
+ const void *hva = addr_gpa2hva(vm, gpa_base + offset);
+
+ source = kvm_mmap(size, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1);
+ /*
+ * Make a copy before setting private, because
+ * snp_launch_update_data() needs to GUP the
+ * source, and private memory cannot be
+ * GUP-ed.
+ */
+ memcpy(source, hva, size);

- if (private)
vm_mem_set_private(vm, gpa_base + offset, size, 0);
+ }

- if (is_sev_snp_vm(vm))
+ if (is_sev_snp_vm(vm)) {
snp_launch_update_data(vm, gpa_base + offset,
- (uint64_t)addr_gpa2hva(vm, gpa_base + offset),
- size, page_type);
- else
- sev_launch_update_data(vm, gpa_base + offset, size);
+ (uint64_t)source, size,
+ page_type);

+ kvm_munmap(source, size);
+ } else {
+ sev_launch_update_data(vm, gpa_base + offset, size);
+ }
}
}

--
2.53.0.1018.g2bb0e51243-goog