2022-12-23 00:40:32

by Vishal Annapurve

[permalink] [raw]
Subject: [V3 PATCH 0/8] KVM: selftests: SEV: selftests for fd-based private memory

This series implements selftests executing SEV VMs to target the feature
implemented by Chao via:
https://lore.kernel.org/lkml/[email protected]/T/

Below changes aim to test the fd based approach for guest private memory
in context of SEV VMs executing on AMD SEV compatible platforms.

sev_private_mem_test.c file adds selftest to access private memory from
the guest via private/shared accesses and checking if the contents can be
leaked to/accessed by vmm via shared memory view before/after
conversions.

To allow SEV/SEV-ES VMs to toggle the encryption bit during memory
conversion, support is added for mapping guest pagetables to guest va
ranges and passing the mapping information to guests via shared pages.

Updates in v3:
1) Dropped RFC tag.
2) Pagetable mapping logic is revisited to reduce the APIs and passing
the information to guest is simplified.
3) Additional changes to execute hypercall as per cpu type are added
4) Selftest implementation is based on revised non-confidential VM
selftests.

Link to RFC v2:
https://lore.kernel.org/lkml/[email protected]/T/

This series has dependency on following patch series:
1) Series mentioned above from Chao
2) Selftests testing fd based memory for non-confidential VMs:
https://lore.kernel.org/lkml/[email protected]/T/
3) Selftests to add SEV VM creation and execution from Peter and Michael:
https://lore.kernel.org/lkml/[email protected]/T/
4) Series to execute hypercall natively:
https://lore.kernel.org/lkml/[email protected]/

Github link for the patches posted as part of this series:
https://github.com/vishals4gh/linux/commits/sev_upm_selftests_rfc_v3

Vishal Annapurve (8):
KVM: selftests: private_mem: Use native hypercall
KVM: selftests: Support mapping pagetables to guest virtual memory
KVM: selftests: x86: Support changing gpa encryption masks
KVM: selftests: Split SEV VM creation logic
KVM: selftests: Enable pagetable mapping for SEV VMs
KVM: selftests: Refactor private_mem_test
KVM: selftests: private_mem_test: Add support for SEV VMs
KVM: selftests: Add private mem test for SEV VMs

tools/testing/selftests/kvm/.gitignore | 1 +
tools/testing/selftests/kvm/Makefile | 2 +
.../selftests/kvm/include/kvm_util_base.h | 88 +++++++
.../include/x86_64/private_mem_test_helper.h | 18 ++
.../selftests/kvm/include/x86_64/processor.h | 4 +
.../selftests/kvm/include/x86_64/sev.h | 4 +
tools/testing/selftests/kvm/lib/kvm_util.c | 88 ++++++-
.../selftests/kvm/lib/x86_64/private_mem.c | 2 +-
.../kvm/lib/x86_64/private_mem_test_helper.c | 228 ++++++++++++++++++
.../selftests/kvm/lib/x86_64/processor.c | 80 ++++++
tools/testing/selftests/kvm/lib/x86_64/sev.c | 25 +-
.../selftests/kvm/x86_64/private_mem_test.c | 187 +-------------
.../kvm/x86_64/sev_private_mem_test.c | 26 ++
13 files changed, 562 insertions(+), 191 deletions(-)
create mode 100644 tools/testing/selftests/kvm/include/x86_64/private_mem_test_helper.h
create mode 100644 tools/testing/selftests/kvm/lib/x86_64/private_mem_test_helper.c
create mode 100644 tools/testing/selftests/kvm/x86_64/sev_private_mem_test.c

--
2.39.0.314.g84b9a713c41-goog


2022-12-23 00:41:30

by Vishal Annapurve

[permalink] [raw]
Subject: [V3 PATCH 7/8] KVM: selftests: private_mem_test: Add support for SEV VMs

Add support of executing private mem test with SEV VMs to allow
creating SEV VMs and make the guest code do page table updates in
case of executiong from SEV VM context.

Signed-off-by: Vishal Annapurve <[email protected]>
---
.../include/x86_64/private_mem_test_helper.h | 3 ++
.../kvm/lib/x86_64/private_mem_test_helper.c | 37 +++++++++++++++++--
2 files changed, 37 insertions(+), 3 deletions(-)

diff --git a/tools/testing/selftests/kvm/include/x86_64/private_mem_test_helper.h b/tools/testing/selftests/kvm/include/x86_64/private_mem_test_helper.h
index 4d32c025876c..e54870b72369 100644
--- a/tools/testing/selftests/kvm/include/x86_64/private_mem_test_helper.h
+++ b/tools/testing/selftests/kvm/include/x86_64/private_mem_test_helper.h
@@ -12,4 +12,7 @@
void execute_vm_with_private_test_mem(
enum vm_mem_backing_src_type test_mem_src);

+void execute_sev_vm_with_private_test_mem(
+ enum vm_mem_backing_src_type test_mem_src);
+
#endif /* SELFTEST_KVM_PRIVATE_MEM_TEST_HELPER_H */
diff --git a/tools/testing/selftests/kvm/lib/x86_64/private_mem_test_helper.c b/tools/testing/selftests/kvm/lib/x86_64/private_mem_test_helper.c
index 600bd21d1bb8..36a8b1ab1c74 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/private_mem_test_helper.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/private_mem_test_helper.c
@@ -22,6 +22,9 @@
#include <private_mem.h>
#include <private_mem_test_helper.h>
#include <processor.h>
+#include <sev.h>
+
+static bool is_guest_sev_vm;

#define TEST_AREA_SLOT 10
#define TEST_AREA_GPA 0xC0000000
@@ -104,6 +107,8 @@ static void guest_conv_test_fn(void)
GUEST_ASSERT(verify_test_area(test_area_base, TEST_MEM_DATA_PATTERN1,
TEST_MEM_DATA_PATTERN1));

+ if (is_guest_sev_vm)
+ guest_set_region_shared(guest_test_mem, guest_test_size);
kvm_hypercall_map_shared((uint64_t)guest_test_mem, guest_test_size);

populate_guest_test_mem(guest_test_mem, TEST_MEM_DATA_PATTERN2);
@@ -112,6 +117,9 @@ static void guest_conv_test_fn(void)
GUEST_ASSERT(verify_test_area(test_area_base, TEST_MEM_DATA_PATTERN1,
TEST_MEM_DATA_PATTERN5));

+ if (is_guest_sev_vm)
+ guest_set_region_private(guest_test_mem, guest_test_size);
+
kvm_hypercall_map_private((uint64_t)guest_test_mem, guest_test_size);

populate_guest_test_mem(guest_test_mem, TEST_MEM_DATA_PATTERN3);
@@ -170,14 +178,19 @@ static void host_conv_test_fn(struct kvm_vm *vm, struct kvm_vcpu *vcpu)
ASSERT_GUEST_DONE(vcpu);
}

-void execute_vm_with_private_test_mem(
- enum vm_mem_backing_src_type test_mem_src)
+static void execute_private_mem_test(enum vm_mem_backing_src_type test_mem_src,
+ bool is_sev_vm)
{
struct kvm_vm *vm;
struct kvm_enable_cap cap;
struct kvm_vcpu *vcpu;

- vm = vm_create_with_one_vcpu(&vcpu, guest_conv_test_fn);
+ if (is_sev_vm)
+ vm = sev_vm_init_with_one_vcpu(SEV_POLICY_NO_DBG,
+ guest_conv_test_fn, &vcpu);
+ else
+ vm = vm_create_with_one_vcpu(&vcpu, guest_conv_test_fn);
+ TEST_ASSERT(vm, "VM creation failed\n");

vm_check_cap(vm, KVM_CAP_EXIT_HYPERCALL);
cap.cap = KVM_CAP_EXIT_HYPERCALL;
@@ -191,7 +204,25 @@ void execute_vm_with_private_test_mem(

virt_map(vm, TEST_AREA_GPA, TEST_AREA_GPA, TEST_AREA_SIZE/vm->page_size);

+ if (is_sev_vm) {
+ is_guest_sev_vm = true;
+ sync_global_to_guest(vm, is_guest_sev_vm);
+ sev_vm_finalize(vm, SEV_POLICY_NO_DBG);
+ }
+
host_conv_test_fn(vm, vcpu);

kvm_vm_free(vm);
}
+
+void execute_vm_with_private_test_mem(
+ enum vm_mem_backing_src_type test_mem_src)
+{
+ execute_private_mem_test(test_mem_src, false);
+}
+
+void execute_sev_vm_with_private_test_mem(
+ enum vm_mem_backing_src_type test_mem_src)
+{
+ execute_private_mem_test(test_mem_src, true);
+}
--
2.39.0.314.g84b9a713c41-goog

2022-12-23 00:41:31

by Vishal Annapurve

[permalink] [raw]
Subject: [V3 PATCH 6/8] KVM: selftests: Refactor private_mem_test

Move most of the logic from private mem test to a library to allow
possible sharing of private_mem_test logic amongst non-confidential and
confidential VM selftests.

Signed-off-by: Vishal Annapurve <[email protected]>
---
tools/testing/selftests/kvm/Makefile | 1 +
.../include/x86_64/private_mem_test_helper.h | 15 ++
.../kvm/lib/x86_64/private_mem_test_helper.c | 197 ++++++++++++++++++
.../selftests/kvm/x86_64/private_mem_test.c | 187 +----------------
4 files changed, 214 insertions(+), 186 deletions(-)
create mode 100644 tools/testing/selftests/kvm/include/x86_64/private_mem_test_helper.h
create mode 100644 tools/testing/selftests/kvm/lib/x86_64/private_mem_test_helper.c

diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index ee8c3aebee80..83c649c9de23 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -56,6 +56,7 @@ LIBKVM_x86_64 += lib/x86_64/handlers.S
LIBKVM_x86_64 += lib/x86_64/hyperv.c
LIBKVM_x86_64 += lib/x86_64/memstress.c
LIBKVM_x86_64 += lib/x86_64/private_mem.c
+LIBKVM_x86_64 += lib/x86_64/private_mem_test_helper.c
LIBKVM_x86_64 += lib/x86_64/processor.c
LIBKVM_x86_64 += lib/x86_64/svm.c
LIBKVM_x86_64 += lib/x86_64/ucall.c
diff --git a/tools/testing/selftests/kvm/include/x86_64/private_mem_test_helper.h b/tools/testing/selftests/kvm/include/x86_64/private_mem_test_helper.h
new file mode 100644
index 000000000000..4d32c025876c
--- /dev/null
+++ b/tools/testing/selftests/kvm/include/x86_64/private_mem_test_helper.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2022, Google LLC.
+ */
+
+#ifndef SELFTEST_KVM_PRIVATE_MEM_TEST_HELPER_H
+#define SELFTEST_KVM_PRIVATE_MEM_TEST_HELPER_H
+
+#include <stdint.h>
+#include <kvm_util.h>
+
+void execute_vm_with_private_test_mem(
+ enum vm_mem_backing_src_type test_mem_src);
+
+#endif /* SELFTEST_KVM_PRIVATE_MEM_TEST_HELPER_H */
diff --git a/tools/testing/selftests/kvm/lib/x86_64/private_mem_test_helper.c b/tools/testing/selftests/kvm/lib/x86_64/private_mem_test_helper.c
new file mode 100644
index 000000000000..600bd21d1bb8
--- /dev/null
+++ b/tools/testing/selftests/kvm/lib/x86_64/private_mem_test_helper.c
@@ -0,0 +1,197 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022, Google LLC.
+ */
+#define _GNU_SOURCE /* for program_invocation_short_name */
+#include <fcntl.h>
+#include <limits.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/kvm_para.h>
+#include <linux/memfd.h>
+
+#include <test_util.h>
+#include <kvm_util.h>
+#include <private_mem.h>
+#include <private_mem_test_helper.h>
+#include <processor.h>
+
+#define TEST_AREA_SLOT 10
+#define TEST_AREA_GPA 0xC0000000
+#define TEST_AREA_SIZE (2 * 1024 * 1024)
+#define GUEST_TEST_MEM_OFFSET (1 * 1024 * 1024)
+#define GUEST_TEST_MEM_SIZE (10 * 4096)
+
+#define VM_STAGE_PROCESSED(x) pr_info("Processed stage %s\n", #x)
+
+#define TEST_MEM_DATA_PATTERN1 0x66
+#define TEST_MEM_DATA_PATTERN2 0x99
+#define TEST_MEM_DATA_PATTERN3 0x33
+#define TEST_MEM_DATA_PATTERN4 0xaa
+#define TEST_MEM_DATA_PATTERN5 0x12
+
+static bool verify_mem_contents(void *mem, uint32_t size, uint8_t pattern)
+{
+ uint8_t *buf = (uint8_t *)mem;
+
+ for (uint32_t i = 0; i < size; i++) {
+ if (buf[i] != pattern)
+ return false;
+ }
+
+ return true;
+}
+
+static void populate_test_area(void *test_area_base, uint64_t pattern)
+{
+ memset(test_area_base, pattern, TEST_AREA_SIZE);
+}
+
+static void populate_guest_test_mem(void *guest_test_mem, uint64_t pattern)
+{
+ memset(guest_test_mem, pattern, GUEST_TEST_MEM_SIZE);
+}
+
+static bool verify_test_area(void *test_area_base, uint64_t area_pattern,
+ uint64_t guest_pattern)
+{
+ void *guest_test_mem = test_area_base + GUEST_TEST_MEM_OFFSET;
+ void *test_area2_base = guest_test_mem + GUEST_TEST_MEM_SIZE;
+ uint64_t test_area2_size = (TEST_AREA_SIZE - (GUEST_TEST_MEM_OFFSET +
+ GUEST_TEST_MEM_SIZE));
+
+ return (verify_mem_contents(test_area_base, GUEST_TEST_MEM_OFFSET, area_pattern) &&
+ verify_mem_contents(guest_test_mem, GUEST_TEST_MEM_SIZE, guest_pattern) &&
+ verify_mem_contents(test_area2_base, test_area2_size, area_pattern));
+}
+
+#define GUEST_STARTED 0
+#define GUEST_PRIVATE_MEM_POPULATED 1
+#define GUEST_SHARED_MEM_POPULATED 2
+#define GUEST_PRIVATE_MEM_POPULATED2 3
+
+/*
+ * Run memory conversion tests with explicit conversion:
+ * Execute KVM hypercall to map/unmap gpa range which will cause userspace exit
+ * to back/unback private memory. Subsequent accesses by guest to the gpa range
+ * will not cause exit to userspace.
+ *
+ * Test memory conversion scenarios with following steps:
+ * 1) Access private memory using private access and verify that memory contents
+ * are not visible to userspace.
+ * 2) Convert memory to shared using explicit conversions and ensure that
+ * userspace is able to access the shared regions.
+ * 3) Convert memory back to private using explicit conversions and ensure that
+ * userspace is again not able to access converted private regions.
+ */
+static void guest_conv_test_fn(void)
+{
+ void *test_area_base = (void *)TEST_AREA_GPA;
+ void *guest_test_mem = (void *)(TEST_AREA_GPA + GUEST_TEST_MEM_OFFSET);
+ uint64_t guest_test_size = GUEST_TEST_MEM_SIZE;
+
+ GUEST_SYNC(GUEST_STARTED);
+
+ populate_test_area(test_area_base, TEST_MEM_DATA_PATTERN1);
+ GUEST_SYNC(GUEST_PRIVATE_MEM_POPULATED);
+ GUEST_ASSERT(verify_test_area(test_area_base, TEST_MEM_DATA_PATTERN1,
+ TEST_MEM_DATA_PATTERN1));
+
+ kvm_hypercall_map_shared((uint64_t)guest_test_mem, guest_test_size);
+
+ populate_guest_test_mem(guest_test_mem, TEST_MEM_DATA_PATTERN2);
+
+ GUEST_SYNC(GUEST_SHARED_MEM_POPULATED);
+ GUEST_ASSERT(verify_test_area(test_area_base, TEST_MEM_DATA_PATTERN1,
+ TEST_MEM_DATA_PATTERN5));
+
+ kvm_hypercall_map_private((uint64_t)guest_test_mem, guest_test_size);
+
+ populate_guest_test_mem(guest_test_mem, TEST_MEM_DATA_PATTERN3);
+ GUEST_SYNC(GUEST_PRIVATE_MEM_POPULATED2);
+
+ GUEST_ASSERT(verify_test_area(test_area_base, TEST_MEM_DATA_PATTERN1,
+ TEST_MEM_DATA_PATTERN3));
+ GUEST_DONE();
+}
+
+#define ASSERT_CONV_TEST_EXIT_IO(vcpu, stage) \
+ { \
+ struct ucall uc; \
+ ASSERT_EQ(vcpu->run->exit_reason, KVM_EXIT_IO); \
+ ASSERT_EQ(get_ucall(vcpu, &uc), UCALL_SYNC); \
+ ASSERT_EQ(uc.args[1], stage); \
+ }
+
+#define ASSERT_GUEST_DONE(vcpu) \
+ { \
+ struct ucall uc; \
+ ASSERT_EQ(vcpu->run->exit_reason, KVM_EXIT_IO); \
+ ASSERT_EQ(get_ucall(vcpu, &uc), UCALL_DONE); \
+ }
+
+static void host_conv_test_fn(struct kvm_vm *vm, struct kvm_vcpu *vcpu)
+{
+ void *test_area_hva = addr_gpa2hva(vm, TEST_AREA_GPA);
+ void *guest_test_mem_hva = (test_area_hva + GUEST_TEST_MEM_OFFSET);
+
+ vcpu_run_and_handle_mapgpa(vm, vcpu);
+ ASSERT_CONV_TEST_EXIT_IO(vcpu, GUEST_STARTED);
+ populate_test_area(test_area_hva, TEST_MEM_DATA_PATTERN4);
+ VM_STAGE_PROCESSED(GUEST_STARTED);
+
+ vcpu_run_and_handle_mapgpa(vm, vcpu);
+ ASSERT_CONV_TEST_EXIT_IO(vcpu, GUEST_PRIVATE_MEM_POPULATED);
+ TEST_ASSERT(verify_test_area(test_area_hva, TEST_MEM_DATA_PATTERN4,
+ TEST_MEM_DATA_PATTERN4), "failed");
+ VM_STAGE_PROCESSED(GUEST_PRIVATE_MEM_POPULATED);
+
+ vcpu_run_and_handle_mapgpa(vm, vcpu);
+ ASSERT_CONV_TEST_EXIT_IO(vcpu, GUEST_SHARED_MEM_POPULATED);
+ TEST_ASSERT(verify_test_area(test_area_hva, TEST_MEM_DATA_PATTERN4,
+ TEST_MEM_DATA_PATTERN2), "failed");
+ populate_guest_test_mem(guest_test_mem_hva, TEST_MEM_DATA_PATTERN5);
+ VM_STAGE_PROCESSED(GUEST_SHARED_MEM_POPULATED);
+
+ vcpu_run_and_handle_mapgpa(vm, vcpu);
+ ASSERT_CONV_TEST_EXIT_IO(vcpu, GUEST_PRIVATE_MEM_POPULATED2);
+ TEST_ASSERT(verify_test_area(test_area_hva, TEST_MEM_DATA_PATTERN4,
+ TEST_MEM_DATA_PATTERN5), "failed");
+ VM_STAGE_PROCESSED(GUEST_PRIVATE_MEM_POPULATED2);
+
+ vcpu_run_and_handle_mapgpa(vm, vcpu);
+ ASSERT_GUEST_DONE(vcpu);
+}
+
+void execute_vm_with_private_test_mem(
+ enum vm_mem_backing_src_type test_mem_src)
+{
+ struct kvm_vm *vm;
+ struct kvm_enable_cap cap;
+ struct kvm_vcpu *vcpu;
+
+ vm = vm_create_with_one_vcpu(&vcpu, guest_conv_test_fn);
+
+ vm_check_cap(vm, KVM_CAP_EXIT_HYPERCALL);
+ cap.cap = KVM_CAP_EXIT_HYPERCALL;
+ cap.flags = 0;
+ cap.args[0] = (1 << KVM_HC_MAP_GPA_RANGE);
+ vm_ioctl(vm, KVM_ENABLE_CAP, &cap);
+
+ vm_userspace_mem_region_add(vm, test_mem_src, TEST_AREA_GPA,
+ TEST_AREA_SLOT, TEST_AREA_SIZE / vm->page_size, KVM_MEM_PRIVATE);
+ vm_allocate_private_mem(vm, TEST_AREA_GPA, TEST_AREA_SIZE);
+
+ virt_map(vm, TEST_AREA_GPA, TEST_AREA_GPA, TEST_AREA_SIZE/vm->page_size);
+
+ host_conv_test_fn(vm, vcpu);
+
+ kvm_vm_free(vm);
+}
diff --git a/tools/testing/selftests/kvm/x86_64/private_mem_test.c b/tools/testing/selftests/kvm/x86_64/private_mem_test.c
index 015ada2e3d54..72c2f913ee92 100644
--- a/tools/testing/selftests/kvm/x86_64/private_mem_test.c
+++ b/tools/testing/selftests/kvm/x86_64/private_mem_test.c
@@ -3,197 +3,12 @@
* Copyright (C) 2022, Google LLC.
*/
#define _GNU_SOURCE /* for program_invocation_short_name */
-#include <fcntl.h>
-#include <limits.h>
-#include <sched.h>
-#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/ioctl.h>

-#include <linux/compiler.h>
-#include <linux/kernel.h>
-#include <linux/kvm_para.h>
-#include <linux/memfd.h>
-
-#include <test_util.h>
#include <kvm_util.h>
-#include <private_mem.h>
-#include <processor.h>
-
-#define TEST_AREA_SLOT 10
-#define TEST_AREA_GPA 0xC0000000
-#define TEST_AREA_SIZE (2 * 1024 * 1024)
-#define GUEST_TEST_MEM_OFFSET (1 * 1024 * 1024)
-#define GUEST_TEST_MEM_SIZE (10 * 4096)
-
-#define VM_STAGE_PROCESSED(x) pr_info("Processed stage %s\n", #x)
-
-#define TEST_MEM_DATA_PATTERN1 0x66
-#define TEST_MEM_DATA_PATTERN2 0x99
-#define TEST_MEM_DATA_PATTERN3 0x33
-#define TEST_MEM_DATA_PATTERN4 0xaa
-#define TEST_MEM_DATA_PATTERN5 0x12
-
-static bool verify_mem_contents(void *mem, uint32_t size, uint8_t pattern)
-{
- uint8_t *buf = (uint8_t *)mem;
-
- for (uint32_t i = 0; i < size; i++) {
- if (buf[i] != pattern)
- return false;
- }
-
- return true;
-}
-
-static void populate_test_area(void *test_area_base, uint64_t pattern)
-{
- memset(test_area_base, pattern, TEST_AREA_SIZE);
-}
-
-static void populate_guest_test_mem(void *guest_test_mem, uint64_t pattern)
-{
- memset(guest_test_mem, pattern, GUEST_TEST_MEM_SIZE);
-}
-
-static bool verify_test_area(void *test_area_base, uint64_t area_pattern,
- uint64_t guest_pattern)
-{
- void *guest_test_mem = test_area_base + GUEST_TEST_MEM_OFFSET;
- void *test_area2_base = guest_test_mem + GUEST_TEST_MEM_SIZE;
- uint64_t test_area2_size = (TEST_AREA_SIZE - (GUEST_TEST_MEM_OFFSET +
- GUEST_TEST_MEM_SIZE));
-
- return (verify_mem_contents(test_area_base, GUEST_TEST_MEM_OFFSET, area_pattern) &&
- verify_mem_contents(guest_test_mem, GUEST_TEST_MEM_SIZE, guest_pattern) &&
- verify_mem_contents(test_area2_base, test_area2_size, area_pattern));
-}
-
-#define GUEST_STARTED 0
-#define GUEST_PRIVATE_MEM_POPULATED 1
-#define GUEST_SHARED_MEM_POPULATED 2
-#define GUEST_PRIVATE_MEM_POPULATED2 3
-
-/*
- * Run memory conversion tests with explicit conversion:
- * Execute KVM hypercall to map/unmap gpa range which will cause userspace exit
- * to back/unback private memory. Subsequent accesses by guest to the gpa range
- * will not cause exit to userspace.
- *
- * Test memory conversion scenarios with following steps:
- * 1) Access private memory using private access and verify that memory contents
- * are not visible to userspace.
- * 2) Convert memory to shared using explicit conversions and ensure that
- * userspace is able to access the shared regions.
- * 3) Convert memory back to private using explicit conversions and ensure that
- * userspace is again not able to access converted private regions.
- */
-static void guest_conv_test_fn(void)
-{
- void *test_area_base = (void *)TEST_AREA_GPA;
- void *guest_test_mem = (void *)(TEST_AREA_GPA + GUEST_TEST_MEM_OFFSET);
- uint64_t guest_test_size = GUEST_TEST_MEM_SIZE;
-
- GUEST_SYNC(GUEST_STARTED);
-
- populate_test_area(test_area_base, TEST_MEM_DATA_PATTERN1);
- GUEST_SYNC(GUEST_PRIVATE_MEM_POPULATED);
- GUEST_ASSERT(verify_test_area(test_area_base, TEST_MEM_DATA_PATTERN1,
- TEST_MEM_DATA_PATTERN1));
-
- kvm_hypercall_map_shared((uint64_t)guest_test_mem, guest_test_size);
-
- populate_guest_test_mem(guest_test_mem, TEST_MEM_DATA_PATTERN2);
-
- GUEST_SYNC(GUEST_SHARED_MEM_POPULATED);
- GUEST_ASSERT(verify_test_area(test_area_base, TEST_MEM_DATA_PATTERN1,
- TEST_MEM_DATA_PATTERN5));
-
- kvm_hypercall_map_private((uint64_t)guest_test_mem, guest_test_size);
-
- populate_guest_test_mem(guest_test_mem, TEST_MEM_DATA_PATTERN3);
- GUEST_SYNC(GUEST_PRIVATE_MEM_POPULATED2);
-
- GUEST_ASSERT(verify_test_area(test_area_base, TEST_MEM_DATA_PATTERN1,
- TEST_MEM_DATA_PATTERN3));
- GUEST_DONE();
-}
-
-#define ASSERT_CONV_TEST_EXIT_IO(vcpu, stage) \
- { \
- struct ucall uc; \
- ASSERT_EQ(vcpu->run->exit_reason, KVM_EXIT_IO); \
- ASSERT_EQ(get_ucall(vcpu, &uc), UCALL_SYNC); \
- ASSERT_EQ(uc.args[1], stage); \
- }
-
-#define ASSERT_GUEST_DONE(vcpu) \
- { \
- struct ucall uc; \
- ASSERT_EQ(vcpu->run->exit_reason, KVM_EXIT_IO); \
- ASSERT_EQ(get_ucall(vcpu, &uc), UCALL_DONE); \
- }
-
-static void host_conv_test_fn(struct kvm_vm *vm, struct kvm_vcpu *vcpu)
-{
- void *test_area_hva = addr_gpa2hva(vm, TEST_AREA_GPA);
- void *guest_test_mem_hva = (test_area_hva + GUEST_TEST_MEM_OFFSET);
-
- vcpu_run_and_handle_mapgpa(vm, vcpu);
- ASSERT_CONV_TEST_EXIT_IO(vcpu, GUEST_STARTED);
- populate_test_area(test_area_hva, TEST_MEM_DATA_PATTERN4);
- VM_STAGE_PROCESSED(GUEST_STARTED);
-
- vcpu_run_and_handle_mapgpa(vm, vcpu);
- ASSERT_CONV_TEST_EXIT_IO(vcpu, GUEST_PRIVATE_MEM_POPULATED);
- TEST_ASSERT(verify_test_area(test_area_hva, TEST_MEM_DATA_PATTERN4,
- TEST_MEM_DATA_PATTERN4), "failed");
- VM_STAGE_PROCESSED(GUEST_PRIVATE_MEM_POPULATED);
-
- vcpu_run_and_handle_mapgpa(vm, vcpu);
- ASSERT_CONV_TEST_EXIT_IO(vcpu, GUEST_SHARED_MEM_POPULATED);
- TEST_ASSERT(verify_test_area(test_area_hva, TEST_MEM_DATA_PATTERN4,
- TEST_MEM_DATA_PATTERN2), "failed");
- populate_guest_test_mem(guest_test_mem_hva, TEST_MEM_DATA_PATTERN5);
- VM_STAGE_PROCESSED(GUEST_SHARED_MEM_POPULATED);
-
- vcpu_run_and_handle_mapgpa(vm, vcpu);
- ASSERT_CONV_TEST_EXIT_IO(vcpu, GUEST_PRIVATE_MEM_POPULATED2);
- TEST_ASSERT(verify_test_area(test_area_hva, TEST_MEM_DATA_PATTERN4,
- TEST_MEM_DATA_PATTERN5), "failed");
- VM_STAGE_PROCESSED(GUEST_PRIVATE_MEM_POPULATED2);
-
- vcpu_run_and_handle_mapgpa(vm, vcpu);
- ASSERT_GUEST_DONE(vcpu);
-}
-
-static void execute_vm_with_private_test_mem(
- enum vm_mem_backing_src_type test_mem_src)
-{
- struct kvm_vm *vm;
- struct kvm_enable_cap cap;
- struct kvm_vcpu *vcpu;
-
- vm = vm_create_with_one_vcpu(&vcpu, guest_conv_test_fn);
-
- vm_check_cap(vm, KVM_CAP_EXIT_HYPERCALL);
- cap.cap = KVM_CAP_EXIT_HYPERCALL;
- cap.flags = 0;
- cap.args[0] = (1 << KVM_HC_MAP_GPA_RANGE);
- vm_ioctl(vm, KVM_ENABLE_CAP, &cap);
-
- vm_userspace_mem_region_add(vm, test_mem_src, TEST_AREA_GPA,
- TEST_AREA_SLOT, TEST_AREA_SIZE / vm->page_size, KVM_MEM_PRIVATE);
- vm_allocate_private_mem(vm, TEST_AREA_GPA, TEST_AREA_SIZE);
-
- virt_map(vm, TEST_AREA_GPA, TEST_AREA_GPA, TEST_AREA_SIZE/vm->page_size);
-
- host_conv_test_fn(vm, vcpu);
-
- kvm_vm_free(vm);
-}
+#include <private_mem_test_helper.h>

int main(int argc, char *argv[])
{
--
2.39.0.314.g84b9a713c41-goog

2022-12-23 00:42:24

by Vishal Annapurve

[permalink] [raw]
Subject: [V3 PATCH 8/8] KVM: selftests: Add private mem test for SEV VMs

Add SEV VM specific private mem test to invoke selftest logic similar
to the one executed for non-confidential VMs.

Signed-off-by: Vishal Annapurve <[email protected]>
---
tools/testing/selftests/kvm/.gitignore | 1 +
tools/testing/selftests/kvm/Makefile | 1 +
.../kvm/x86_64/sev_private_mem_test.c | 26 +++++++++++++++++++
3 files changed, 28 insertions(+)
create mode 100644 tools/testing/selftests/kvm/x86_64/sev_private_mem_test.c

diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
index f73639dcbebb..e5c82a1cd733 100644
--- a/tools/testing/selftests/kvm/.gitignore
+++ b/tools/testing/selftests/kvm/.gitignore
@@ -40,6 +40,7 @@
/x86_64/set_sregs_test
/x86_64/sev_all_boot_test
/x86_64/sev_migrate_tests
+/x86_64/sev_private_mem_test
/x86_64/smaller_maxphyaddr_emulation_test
/x86_64/smm_test
/x86_64/state_test
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index 83c649c9de23..a8ee7c473644 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -104,6 +104,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/pmu_event_filter_test
TEST_GEN_PROGS_x86_64 += x86_64/private_mem_test
TEST_GEN_PROGS_x86_64 += x86_64/set_boot_cpu_id
TEST_GEN_PROGS_x86_64 += x86_64/set_sregs_test
+TEST_GEN_PROGS_x86_64 += x86_64/sev_private_mem_test
TEST_GEN_PROGS_x86_64 += x86_64/smaller_maxphyaddr_emulation_test
TEST_GEN_PROGS_x86_64 += x86_64/smm_test
TEST_GEN_PROGS_x86_64 += x86_64/state_test
diff --git a/tools/testing/selftests/kvm/x86_64/sev_private_mem_test.c b/tools/testing/selftests/kvm/x86_64/sev_private_mem_test.c
new file mode 100644
index 000000000000..943fdfbe41d9
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86_64/sev_private_mem_test.c
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022, Google LLC.
+ */
+#define _GNU_SOURCE /* for program_invocation_short_name */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <private_mem_test_helper.h>
+
+int main(int argc, char *argv[])
+{
+ execute_sev_vm_with_private_test_mem(
+ VM_MEM_SRC_ANONYMOUS_AND_RESTRICTED_MEMFD);
+
+ /* Needs 2MB Hugepages */
+ if (get_free_huge_2mb_pages() >= 1) {
+ printf("Running SEV VM private mem test with 2M pages\n");
+ execute_sev_vm_with_private_test_mem(
+ VM_MEM_SRC_ANON_HTLB2M_AND_RESTRICTED_MEMFD);
+ } else
+ printf("Skipping SEV VM private mem test with 2M pages\n");
+
+ return 0;
+}
--
2.39.0.314.g84b9a713c41-goog

2022-12-23 00:42:35

by Vishal Annapurve

[permalink] [raw]
Subject: [V3 PATCH 5/8] KVM: selftests: Enable pagetable mapping for SEV VMs

Enable pagetable tracking and mapping for SEV VMs to allow guest code to
execute guest_map_region_shared/private APIs.

Signed-off-by: Vishal Annapurve <[email protected]>
---
tools/testing/selftests/kvm/lib/x86_64/sev.c | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/tools/testing/selftests/kvm/lib/x86_64/sev.c b/tools/testing/selftests/kvm/lib/x86_64/sev.c
index 96d3dbc2ba74..0dfffdc224d6 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/sev.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/sev.c
@@ -215,6 +215,8 @@ static void sev_vm_measure(struct kvm_vm *vm)
pr_debug("\n");
}

+#define GUEST_PGT_MIN_VADDR 0x10000
+
struct kvm_vm *sev_vm_init_with_one_vcpu(uint32_t policy, void *guest_code,
struct kvm_vcpu **cpu)
{
@@ -224,6 +226,7 @@ struct kvm_vm *sev_vm_init_with_one_vcpu(uint32_t policy, void *guest_code,

vm = ____vm_create(mode, nr_pages);

+ vm_set_pgt_alloc_tracking(vm);
kvm_sev_ioctl(vm, KVM_SEV_INIT, NULL);

configure_sev_pte_masks(vm);
@@ -238,6 +241,8 @@ struct kvm_vm *sev_vm_init_with_one_vcpu(uint32_t policy, void *guest_code,

void sev_vm_finalize(struct kvm_vm *vm, uint32_t policy)
{
+ vm_map_page_table(vm, GUEST_PGT_MIN_VADDR);
+
sev_vm_launch(vm, policy);

sev_vm_measure(vm);
--
2.39.0.314.g84b9a713c41-goog

2022-12-23 00:49:01

by Vishal Annapurve

[permalink] [raw]
Subject: [V3 PATCH 3/8] KVM: selftests: x86: Support changing gpa encryption masks

Add support for guest side functionality to modify encryption/shared
masks for entries in page table to allow accessing GPA ranges as private
or shared.

Signed-off-by: Vishal Annapurve <[email protected]>
---
.../selftests/kvm/include/x86_64/processor.h | 4 ++
.../selftests/kvm/lib/x86_64/processor.c | 39 +++++++++++++++++++
2 files changed, 43 insertions(+)

diff --git a/tools/testing/selftests/kvm/include/x86_64/processor.h b/tools/testing/selftests/kvm/include/x86_64/processor.h
index 3617f83bb2e5..c8c55f54c14f 100644
--- a/tools/testing/selftests/kvm/include/x86_64/processor.h
+++ b/tools/testing/selftests/kvm/include/x86_64/processor.h
@@ -945,6 +945,10 @@ void vcpu_init_descriptor_tables(struct kvm_vcpu *vcpu);
void vm_install_exception_handler(struct kvm_vm *vm, int vector,
void (*handler)(struct ex_regs *));

+void guest_set_region_shared(void *vaddr, uint64_t size);
+
+void guest_set_region_private(void *vaddr, uint64_t size);
+
/* If a toddler were to say "abracadabra". */
#define KVM_EXCEPTION_MAGIC 0xabacadabaULL

diff --git a/tools/testing/selftests/kvm/lib/x86_64/processor.c b/tools/testing/selftests/kvm/lib/x86_64/processor.c
index ab7d4cc4b848..42d1e4074f32 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/processor.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/processor.c
@@ -276,6 +276,45 @@ static uint64_t *guest_code_get_pte(uint64_t vaddr)
return (uint64_t *)&pte[index[0]];
}

+static void guest_code_change_region_prot(void *vaddr_start, uint64_t mem_size,
+ bool private)
+{
+ uint64_t vaddr = (uint64_t)vaddr_start;
+ uint32_t num_pages;
+
+ GUEST_ASSERT(gpgt_info != NULL);
+ uint32_t guest_page_size = gpgt_info->page_size;
+
+ GUEST_ASSERT(!(mem_size % guest_page_size) && !(vaddr % guest_page_size));
+ GUEST_ASSERT(gpgt_info->enc_mask | gpgt_info->shared_mask);
+
+ num_pages = mem_size / guest_page_size;
+ for (uint32_t i = 0; i < num_pages; i++) {
+ uint64_t *pte = guest_code_get_pte(vaddr);
+
+ GUEST_ASSERT(pte);
+ if (private) {
+ *pte &= ~(gpgt_info->shared_mask);
+ *pte |= gpgt_info->enc_mask;
+ } else {
+ *pte &= ~(gpgt_info->enc_mask);
+ *pte |= gpgt_info->shared_mask;
+ }
+ asm volatile("invlpg (%0)" :: "r"(vaddr) : "memory");
+ vaddr += guest_page_size;
+ }
+}
+
+void guest_set_region_shared(void *vaddr, uint64_t size)
+{
+ guest_code_change_region_prot(vaddr, size, /* shared */ false);
+}
+
+void guest_set_region_private(void *vaddr, uint64_t size)
+{
+ guest_code_change_region_prot(vaddr, size, /* private */ true);
+}
+
void sync_vm_gpgt_info(struct kvm_vm *vm, vm_vaddr_t pgt_info)
{
gpgt_info = (struct guest_pgt_info *)pgt_info;
--
2.39.0.314.g84b9a713c41-goog

2022-12-23 00:52:35

by Vishal Annapurve

[permalink] [raw]
Subject: [V3 PATCH 2/8] KVM: selftests: Support mapping pagetables to guest virtual memory

Add support for mapping guest pagetables to guest VM regions to allow
guests to modify the page table entries at runtime.

Add following APIs:
1) vm_set_pgt_alloc_tracking: Track allocations for guest page tables so
that such pages can be mapped back to guests later.
2) vm_map_page_table: Map page table pages to guests and pass the
information about page table physical pages to virtual address
mappings to guests.

This framework is useful for the guests whose pagetables can't be
manipulated from userspace e.g. confidential VM selftests. Confidential
VMs need to change the memory access type by modifying gpa values in
their page table entries.

Signed-off-by: Vishal Annapurve <[email protected]>
---
.../selftests/kvm/include/kvm_util_base.h | 88 +++++++++++++++++++
tools/testing/selftests/kvm/lib/kvm_util.c | 88 ++++++++++++++++++-
.../selftests/kvm/lib/x86_64/processor.c | 41 +++++++++
3 files changed, 216 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/kvm/include/kvm_util_base.h b/tools/testing/selftests/kvm/include/kvm_util_base.h
index a736d6d18fa5..6c286686ec7c 100644
--- a/tools/testing/selftests/kvm/include/kvm_util_base.h
+++ b/tools/testing/selftests/kvm/include/kvm_util_base.h
@@ -78,6 +78,11 @@ struct protected_vm {
int8_t protected_bit;
};

+struct pgt_page {
+ vm_paddr_t paddr;
+ struct list_head list;
+};
+
struct kvm_vm {
int mode;
unsigned long type;
@@ -108,6 +113,10 @@ struct kvm_vm {

/* VM protection enabled: SEV, etc*/
bool protected;
+ struct list_head pgt_pages;
+ bool track_pgt_pages;
+ uint32_t num_pgt_pages;
+ vm_vaddr_t pgt_vaddr_start;

/* Cache of information for binary stats interface */
int stats_fd;
@@ -196,6 +205,25 @@ struct vm_guest_mode_params {
unsigned int page_size;
unsigned int page_shift;
};
+
+/*
+ * Structure shared with the guest containing information about:
+ * - Starting virtual address for num_pgt_pages physical pagetable
+ * page addresses tracked via paddrs array
+ * - page size of the guest
+ *
+ * Guest can walk through its pagetables using this information to
+ * read/modify pagetable attributes.
+ */
+struct guest_pgt_info {
+ uint64_t num_pgt_pages;
+ uint64_t pgt_vaddr_start;
+ uint64_t page_size;
+ uint64_t enc_mask;
+ uint64_t shared_mask;
+ uint64_t paddrs[];
+};
+
extern const struct vm_guest_mode_params vm_guest_mode_params[];

int open_path_or_exit(const char *path, int flags);
@@ -411,6 +439,48 @@ void vm_mem_region_delete(struct kvm_vm *vm, uint32_t slot);
struct kvm_vcpu *__vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id);
vm_vaddr_t vm_vaddr_unused_gap(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min);
vm_vaddr_t vm_vaddr_alloc(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min);
+
+/*
+ * function called by guest code to translate physical address of a pagetable
+ * page to guest virtual address.
+ *
+ * input args:
+ * gpgt_info - pointer to the guest_pgt_info structure containing info
+ * about guest virtual address mappings for guest physical
+ * addresses of page table pages.
+ * pgt_pa - physical address of guest page table page to be translated
+ * to a virtual address.
+ *
+ * output args: none
+ *
+ * return:
+ * pointer to the pagetable page, null in case physical address is not
+ * tracked via given guest_pgt_info structure.
+ */
+void *guest_code_get_pgt_vaddr(struct guest_pgt_info *gpgt_info, uint64_t pgt_pa);
+
+/*
+ * 1) Map page table physical pages to the guest virtual address range
+ * 2) Allocate and setup a page to be shared with guest containing guest_pgt_info
+ * structure.
+ *
+ * Note:
+ * 1) vm_set_pgt_alloc_tracking function should be used to start tracking
+ * of physical page table page allocation.
+ * 2) This function should be invoked after needed pagetable pages are
+ * mapped to the VM using virt_pg_map.
+ *
+ * input args:
+ * vm - virtual machine
+ * vaddr_min - Minimum guest virtual address to start mapping the
+ * guest pagetable pages and guest_pgt_info structure page(s).
+ *
+ * output args: none
+ *
+ * return: none
+ */
+void vm_map_page_table(struct kvm_vm *vm, vm_vaddr_t vaddr_min);
+
vm_vaddr_t vm_vaddr_alloc_shared(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min);
vm_vaddr_t vm_vaddr_alloc_pages(struct kvm_vm *vm, int nr_pages);
vm_vaddr_t vm_vaddr_alloc_page(struct kvm_vm *vm);
@@ -673,10 +743,28 @@ void kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing);

const char *exit_reason_str(unsigned int exit_reason);

+void sync_vm_gpgt_info(struct kvm_vm *vm, vm_vaddr_t pgt_info);
+
vm_paddr_t vm_phy_page_alloc(struct kvm_vm *vm, vm_paddr_t paddr_min,
uint32_t memslot);
vm_paddr_t _vm_phy_pages_alloc(struct kvm_vm *vm, size_t num,
vm_paddr_t paddr_min, uint32_t memslot, bool protected);
+
+/*
+ * Enable tracking of physical guest pagetable pages for the given vm.
+ * This function should be called right after vm creation before any pages are
+ * mapped into the VM using vm_alloc_* / vm_vaddr_alloc* functions.
+ *
+ * input args:
+ * vm - virtual machine
+ *
+ * output args: none
+ *
+ * return:
+ * None
+ */
+void vm_set_pgt_alloc_tracking(struct kvm_vm *vm);
+
vm_paddr_t vm_alloc_page_table(struct kvm_vm *vm);

static inline vm_paddr_t vm_phy_pages_alloc(struct kvm_vm *vm, size_t num,
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index 37f342a17350..b56be997216a 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -202,6 +202,7 @@ struct kvm_vm *____vm_create(enum vm_guest_mode mode, uint64_t nr_pages)
TEST_ASSERT(vm != NULL, "Insufficient Memory");

INIT_LIST_HEAD(&vm->vcpus);
+ INIT_LIST_HEAD(&vm->pgt_pages);
vm->regions.gpa_tree = RB_ROOT;
vm->regions.hva_tree = RB_ROOT;
hash_init(vm->regions.slot_hash);
@@ -695,6 +696,7 @@ void kvm_vm_free(struct kvm_vm *vmp)
{
int ctr;
struct hlist_node *node;
+ struct pgt_page *entry, *nentry;
struct userspace_mem_region *region;

if (vmp == NULL)
@@ -710,6 +712,9 @@ void kvm_vm_free(struct kvm_vm *vmp)
hash_for_each_safe(vmp->regions.slot_hash, ctr, node, region, slot_node)
__vm_mem_region_delete(vmp, region, false);

+ list_for_each_entry_safe(entry, nentry, &vmp->pgt_pages, list)
+ free(entry);
+
/* Free sparsebit arrays. */
sparsebit_free(&vmp->vpages_valid);
sparsebit_free(&vmp->vpages_mapped);
@@ -1330,6 +1335,72 @@ vm_vaddr_t vm_vaddr_unused_gap(struct kvm_vm *vm, size_t sz,
return pgidx_start * vm->page_size;
}

+void __weak sync_vm_gpgt_info(struct kvm_vm *vm, vm_vaddr_t pgt_info)
+{
+}
+
+void *guest_code_get_pgt_vaddr(struct guest_pgt_info *gpgt_info,
+ uint64_t pgt_pa)
+{
+ uint64_t num_pgt_pages = gpgt_info->num_pgt_pages;
+ uint64_t pgt_vaddr_start = gpgt_info->pgt_vaddr_start;
+ uint64_t page_size = gpgt_info->page_size;
+
+ for (uint32_t i = 0; i < num_pgt_pages; i++) {
+ if (gpgt_info->paddrs[i] == pgt_pa)
+ return (void *)(pgt_vaddr_start + i * page_size);
+ }
+ return NULL;
+}
+
+static void vm_setup_pgt_info_buf(struct kvm_vm *vm, vm_vaddr_t vaddr_min)
+{
+ struct pgt_page *pgt_page_entry;
+ struct guest_pgt_info *gpgt_info;
+ uint64_t info_size = sizeof(*gpgt_info) + (sizeof(uint64_t) * vm->num_pgt_pages);
+ uint64_t num_pages = align_up(info_size, vm->page_size);
+ vm_vaddr_t buf_start = vm_vaddr_alloc(vm, num_pages, vaddr_min);
+ uint32_t i = 0;
+
+ gpgt_info = (struct guest_pgt_info *)addr_gva2hva(vm, buf_start);
+ gpgt_info->num_pgt_pages = vm->num_pgt_pages;
+ gpgt_info->pgt_vaddr_start = vm->pgt_vaddr_start;
+ gpgt_info->page_size = vm->page_size;
+ if (vm->protected) {
+ gpgt_info->enc_mask = vm->arch.c_bit;
+ gpgt_info->shared_mask = vm->arch.s_bit;
+ }
+ list_for_each_entry(pgt_page_entry, &vm->pgt_pages, list) {
+ gpgt_info->paddrs[i] = pgt_page_entry->paddr;
+ i++;
+ }
+ TEST_ASSERT((i == vm->num_pgt_pages), "pgt entries mismatch with the counter");
+ sync_vm_gpgt_info(vm, buf_start);
+}
+
+void vm_map_page_table(struct kvm_vm *vm, vm_vaddr_t vaddr_min)
+{
+ struct pgt_page *pgt_page_entry;
+ vm_vaddr_t vaddr;
+
+ /* Stop tracking further pgt pages, mapping pagetable may itself need
+ * new pages.
+ */
+ vm->track_pgt_pages = false;
+ vm_vaddr_t vaddr_start = vm_vaddr_unused_gap(vm,
+ vm->num_pgt_pages * vm->page_size, vaddr_min);
+ vaddr = vaddr_start;
+ list_for_each_entry(pgt_page_entry, &vm->pgt_pages, list) {
+ /* Map the virtual page. */
+ virt_pg_map(vm, vaddr, pgt_page_entry->paddr & ~vm->arch.c_bit);
+ sparsebit_set(vm->vpages_mapped, vaddr >> vm->page_shift);
+ vaddr += vm->page_size;
+ }
+ vm->pgt_vaddr_start = vaddr_start;
+
+ vm_setup_pgt_info_buf(vm, vaddr_min);
+}
+
/*
* VM Virtual Address Allocate Shared/Encrypted
*
@@ -1981,9 +2052,24 @@ vm_paddr_t vm_phy_page_alloc(struct kvm_vm *vm, vm_paddr_t paddr_min,
/* Arbitrary minimum physical address used for virtual translation tables. */
#define KVM_GUEST_PAGE_TABLE_MIN_PADDR 0x180000

+void vm_set_pgt_alloc_tracking(struct kvm_vm *vm)
+{
+ vm->track_pgt_pages = true;
+}
+
vm_paddr_t vm_alloc_page_table(struct kvm_vm *vm)
{
- return vm_phy_page_alloc(vm, KVM_GUEST_PAGE_TABLE_MIN_PADDR, 0);
+ struct pgt_page *pgt;
+ vm_paddr_t paddr = vm_phy_page_alloc(vm, KVM_GUEST_PAGE_TABLE_MIN_PADDR, 0);
+
+ if (vm->track_pgt_pages) {
+ pgt = calloc(1, sizeof(*pgt));
+ TEST_ASSERT(pgt != NULL, "Insufficient memory");
+ pgt->paddr = (paddr | vm->arch.c_bit);
+ list_add(&pgt->list, &vm->pgt_pages);
+ vm->num_pgt_pages++;
+ }
+ return paddr;
}

/*
diff --git a/tools/testing/selftests/kvm/lib/x86_64/processor.c b/tools/testing/selftests/kvm/lib/x86_64/processor.c
index 429e55f2609f..ab7d4cc4b848 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/processor.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/processor.c
@@ -19,6 +19,7 @@
#define MAX_NR_CPUID_ENTRIES 100

vm_vaddr_t exception_handlers;
+static struct guest_pgt_info *gpgt_info;

static bool is_cpu_vendor_intel;
static bool is_cpu_vendor_amd;
@@ -241,6 +242,46 @@ void virt_arch_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr)
__virt_pg_map(vm, vaddr, paddr, PG_LEVEL_4K);
}

+static uint64_t *guest_code_get_pte(uint64_t vaddr)
+{
+ uint16_t index[4];
+ uint64_t *pml4e, *pdpe, *pde, *pte;
+ uint64_t pgt_paddr = get_cr3();
+
+ GUEST_ASSERT(gpgt_info != NULL);
+ uint64_t page_size = gpgt_info->page_size;
+
+ index[0] = (vaddr >> 12) & 0x1ffu;
+ index[1] = (vaddr >> 21) & 0x1ffu;
+ index[2] = (vaddr >> 30) & 0x1ffu;
+ index[3] = (vaddr >> 39) & 0x1ffu;
+
+ pml4e = guest_code_get_pgt_vaddr(gpgt_info, pgt_paddr);
+ GUEST_ASSERT(pml4e && (pml4e[index[3]] & PTE_PRESENT_MASK));
+
+ pgt_paddr = (PTE_GET_PFN(pml4e[index[3]]) * page_size);
+ pdpe = guest_code_get_pgt_vaddr(gpgt_info, pgt_paddr);
+ GUEST_ASSERT(pdpe && (pdpe[index[2]] & PTE_PRESENT_MASK) &&
+ !(pdpe[index[2]] & PTE_LARGE_MASK));
+
+ pgt_paddr = (PTE_GET_PFN(pdpe[index[2]]) * page_size);
+ pde = guest_code_get_pgt_vaddr(gpgt_info, pgt_paddr);
+ GUEST_ASSERT(pde && (pde[index[1]] & PTE_PRESENT_MASK) &&
+ !(pde[index[1]] & PTE_LARGE_MASK));
+
+ pgt_paddr = (PTE_GET_PFN(pde[index[1]]) * page_size);
+ pte = guest_code_get_pgt_vaddr(gpgt_info, pgt_paddr);
+ GUEST_ASSERT(pte && (pte[index[0]] & PTE_PRESENT_MASK));
+
+ return (uint64_t *)&pte[index[0]];
+}
+
+void sync_vm_gpgt_info(struct kvm_vm *vm, vm_vaddr_t pgt_info)
+{
+ gpgt_info = (struct guest_pgt_info *)pgt_info;
+ sync_global_to_guest(vm, gpgt_info);
+}
+
void virt_map_level(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr,
uint64_t nr_bytes, int level)
{
--
2.39.0.314.g84b9a713c41-goog

2022-12-23 00:55:59

by Vishal Annapurve

[permalink] [raw]
Subject: [V3 PATCH 1/8] KVM: selftests: private_mem: Use native hypercall

CVMs need to execute hypercalls as per the cpu type without relying on
KVM emulation.

Execute hypercall from the guest using native instructions.

Signed-off-by: Vishal Annapurve <[email protected]>
---
tools/testing/selftests/kvm/lib/x86_64/private_mem.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/testing/selftests/kvm/lib/x86_64/private_mem.c b/tools/testing/selftests/kvm/lib/x86_64/private_mem.c
index 2b97fc34ec4a..5a8fd8c3bc04 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/private_mem.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/private_mem.c
@@ -24,7 +24,7 @@
static inline uint64_t __kvm_hypercall_map_gpa_range(uint64_t gpa, uint64_t size,
uint64_t flags)
{
- return kvm_hypercall(KVM_HC_MAP_GPA_RANGE, gpa, size >> PAGE_SHIFT, flags, 0);
+ return kvm_native_hypercall(KVM_HC_MAP_GPA_RANGE, gpa, size >> PAGE_SHIFT, flags, 0);
}

static inline void kvm_hypercall_map_gpa_range(uint64_t gpa, uint64_t size,
--
2.39.0.314.g84b9a713c41-goog

2022-12-23 01:37:18

by Vishal Annapurve

[permalink] [raw]
Subject: [V3 PATCH 4/8] KVM: selftests: Split SEV VM creation logic

Split SEV VM creation logic to allow additional modifications
to SEV VM configuration e.g. adding memslots.

Signed-off-by: Vishal Annapurve <[email protected]>
---
.../selftests/kvm/include/x86_64/sev.h | 4 ++++
tools/testing/selftests/kvm/lib/x86_64/sev.c | 20 ++++++++++++++++---
2 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/tools/testing/selftests/kvm/include/x86_64/sev.h b/tools/testing/selftests/kvm/include/x86_64/sev.h
index 1148db928d0b..6bf2015fff7a 100644
--- a/tools/testing/selftests/kvm/include/x86_64/sev.h
+++ b/tools/testing/selftests/kvm/include/x86_64/sev.h
@@ -19,4 +19,8 @@ bool is_kvm_sev_supported(void);
struct kvm_vm *vm_sev_create_with_one_vcpu(uint32_t policy, void *guest_code,
struct kvm_vcpu **cpu);

+struct kvm_vm *sev_vm_init_with_one_vcpu(uint32_t policy, void *guest_code,
+ struct kvm_vcpu **cpu);
+
+void sev_vm_finalize(struct kvm_vm *vm, uint32_t policy);
#endif /* SELFTEST_KVM_SEV_H */
diff --git a/tools/testing/selftests/kvm/lib/x86_64/sev.c b/tools/testing/selftests/kvm/lib/x86_64/sev.c
index 49c62f25363e..96d3dbc2ba74 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/sev.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/sev.c
@@ -215,7 +215,7 @@ static void sev_vm_measure(struct kvm_vm *vm)
pr_debug("\n");
}

-struct kvm_vm *vm_sev_create_with_one_vcpu(uint32_t policy, void *guest_code,
+struct kvm_vm *sev_vm_init_with_one_vcpu(uint32_t policy, void *guest_code,
struct kvm_vcpu **cpu)
{
enum vm_guest_mode mode = VM_MODE_PXXV48_4K;
@@ -231,14 +231,28 @@ struct kvm_vm *vm_sev_create_with_one_vcpu(uint32_t policy, void *guest_code,
*cpu = vm_vcpu_add(vm, 0, guest_code);
kvm_vm_elf_load(vm, program_invocation_name);

+ pr_info("SEV guest created, policy: 0x%x, size: %lu KB\n", policy,
+ nr_pages * vm->page_size / 1024);
+ return vm;
+}
+
+void sev_vm_finalize(struct kvm_vm *vm, uint32_t policy)
+{
sev_vm_launch(vm, policy);

sev_vm_measure(vm);

sev_vm_launch_finish(vm);
+}

- pr_info("SEV guest created, policy: 0x%x, size: %lu KB\n", policy,
- nr_pages * vm->page_size / 1024);
+struct kvm_vm *vm_sev_create_with_one_vcpu(uint32_t policy, void *guest_code,
+ struct kvm_vcpu **cpu)
+{
+ struct kvm_vm *vm;
+
+ vm = sev_vm_init_with_one_vcpu(policy, guest_code, cpu);
+
+ sev_vm_finalize(vm, policy);

return vm;
}
--
2.39.0.314.g84b9a713c41-goog