From: Sagi Shahar <sagis@google.com>
To: linux-kselftest@vger.kernel.org,
Paolo Bonzini <pbonzini@redhat.com>,
Shuah Khan <shuah@kernel.org>,
Sean Christopherson <seanjc@google.com>,
Ackerley Tng <ackerleytng@google.com>,
Ryan Afranji <afranji@google.com>,
Andrew Jones <ajones@ventanamicro.com>,
Isaku Yamahata <isaku.yamahata@intel.com>,
Erdem Aktas <erdemaktas@google.com>,
Rick Edgecombe <rick.p.edgecombe@intel.com>,
Sagi Shahar <sagis@google.com>,
Roger Wang <runanwang@google.com>,
Binbin Wu <binbin.wu@linux.intel.com>,
Oliver Upton <oliver.upton@linux.dev>,
"Pratik R. Sampat" <pratikrajesh.sampat@amd.com>,
Reinette Chatre <reinette.chatre@intel.com>
Cc: linux-kernel@vger.kernel.org, kvm@vger.kernel.org
Subject: [PATCH v7 24/30] KVM: selftests: TDX: Add shared memory test
Date: Fri, 13 Jun 2025 12:13:51 -0700 [thread overview]
Message-ID: <20250613191359.35078-25-sagis@google.com> (raw)
In-Reply-To: <20250613191359.35078-1-sagis@google.com>
From: Ryan Afranji <afranji@google.com>
Test that host and guest can exchange data via shared memory.
Set up shared memory by first allocating as private and then mapping
the same GPA as shared. The guest starts with a request to map a
page of memory to shared. This request is done via a hypercall
(TDG.VP.VMCALL<MapGPA>) from the guest that the kernel converts to
KVM_EXIT_HYPERCALL with KVM_HC_MAP_GPA_RANGE that is sent back to the
test for handling. Handle the guest's request using the
KVM_SET_MEMORY_ATTRIBUTES ioctl().
After the shared memory is set up the guest writes to it and notifies the
host from where the data is verified. In return, the host writes to the
same memory that is verified by the guest.
Co-developed-by: Ackerley Tng <ackerleytng@google.com>
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
Co-developed-by: Binbin Wu <binbin.wu@linux.intel.com>
Signed-off-by: Binbin Wu <binbin.wu@linux.intel.com>
Signed-off-by: Ryan Afranji <afranji@google.com>
Signed-off-by: Sagi Shahar <sagis@google.com>
---
tools/testing/selftests/kvm/Makefile.kvm | 1 +
.../selftests/kvm/include/x86/tdx/tdx.h | 4 +
.../selftests/kvm/include/x86/tdx/tdx_util.h | 2 +
tools/testing/selftests/kvm/lib/x86/tdx/tdx.c | 26 ++++
.../selftests/kvm/lib/x86/tdx/tdx_util.c | 32 +++++
.../selftests/kvm/x86/tdx_shared_mem_test.c | 129 ++++++++++++++++++
6 files changed, 194 insertions(+)
create mode 100644 tools/testing/selftests/kvm/x86/tdx_shared_mem_test.c
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 4a147d498bd2..bdeb40a258e1 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -152,6 +152,7 @@ TEST_GEN_PROGS_x86 += steal_time
TEST_GEN_PROGS_x86 += system_counter_offset_test
TEST_GEN_PROGS_x86 += pre_fault_memory_test
TEST_GEN_PROGS_x86 += x86/tdx_vm_test
+TEST_GEN_PROGS_x86 += x86/tdx_shared_mem_test
# Compiled outputs used by test targets
TEST_GEN_PROGS_EXTENDED_x86 += x86/nx_huge_pages_test
diff --git a/tools/testing/selftests/kvm/include/x86/tdx/tdx.h b/tools/testing/selftests/kvm/include/x86/tdx/tdx.h
index 801ca879664e..88f3571df16f 100644
--- a/tools/testing/selftests/kvm/include/x86/tdx/tdx.h
+++ b/tools/testing/selftests/kvm/include/x86/tdx/tdx.h
@@ -9,6 +9,7 @@
#define TDG_VP_INFO 1
#define TDG_VP_VMCALL_GET_TD_VM_CALL_INFO 0x10000
+#define TDG_VP_VMCALL_MAP_GPA 0x10001
#define TDG_VP_VMCALL_REPORT_FATAL_ERROR 0x10003
#define TDG_VP_VMCALL_INSTRUCTION_CPUID 10
@@ -18,6 +19,8 @@
#define TDG_VP_VMCALL_INSTRUCTION_WRMSR 32
#define TDG_VP_VMCALL_VE_REQUEST_MMIO 48
+void handle_userspace_map_gpa(struct kvm_vcpu *vcpu);
+
uint64_t tdg_vp_vmcall_instruction_io(uint64_t port, uint64_t size,
uint64_t write, uint64_t *data);
void tdg_vp_vmcall_report_fatal_error(uint64_t error_code, uint64_t data_gpa);
@@ -36,5 +39,6 @@ uint64_t tdg_vp_vmcall_instruction_cpuid(uint32_t eax, uint32_t ecx,
uint64_t tdg_vp_info(uint64_t *rcx, uint64_t *rdx,
uint64_t *r8, uint64_t *r9,
uint64_t *r10, uint64_t *r11);
+uint64_t tdg_vp_vmcall_map_gpa(uint64_t address, uint64_t size, uint64_t *data_out);
#endif // SELFTEST_TDX_TDX_H
diff --git a/tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h b/tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h
index c942aec7ad26..ae39b78aa4af 100644
--- a/tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h
+++ b/tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h
@@ -17,5 +17,7 @@ void td_initialize(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
uint64_t attributes);
void td_finalize(struct kvm_vm *vm);
void td_vcpu_run(struct kvm_vcpu *vcpu);
+void handle_memory_conversion(struct kvm_vm *vm, uint32_t vcpu_id, uint64_t gpa,
+ uint64_t size, bool shared_to_private);
#endif // SELFTESTS_TDX_KVM_UTIL_H
diff --git a/tools/testing/selftests/kvm/lib/x86/tdx/tdx.c b/tools/testing/selftests/kvm/lib/x86/tdx/tdx.c
index ab6fd3d7ae4b..bae84c34c19e 100644
--- a/tools/testing/selftests/kvm/lib/x86/tdx/tdx.c
+++ b/tools/testing/selftests/kvm/lib/x86/tdx/tdx.c
@@ -1,11 +1,21 @@
// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/kvm_para.h>
#include <string.h>
#include "tdx/tdcall.h"
#include "tdx/tdx.h"
+#include "tdx/tdx_util.h"
#include "tdx/test_util.h"
+void handle_userspace_map_gpa(struct kvm_vcpu *vcpu)
+{
+ handle_memory_conversion(vcpu->vm, vcpu->id, vcpu->run->hypercall.args[0],
+ vcpu->run->hypercall.args[1] << 12,
+ vcpu->run->hypercall.args[2] & KVM_MAP_GPA_RANGE_ENCRYPTED);
+ vcpu->run->hypercall.ret = 0;
+}
+
uint64_t tdg_vp_vmcall_instruction_io(uint64_t port, uint64_t size,
uint64_t write, uint64_t *data)
{
@@ -189,3 +199,19 @@ uint64_t tdg_vp_info(uint64_t *rcx, uint64_t *rdx,
return ret;
}
+
+uint64_t tdg_vp_vmcall_map_gpa(uint64_t address, uint64_t size, uint64_t *data_out)
+{
+ struct tdx_hypercall_args args = {
+ .r11 = TDG_VP_VMCALL_MAP_GPA,
+ .r12 = address,
+ .r13 = size
+ };
+ uint64_t ret;
+
+ ret = __tdx_hypercall(&args, TDX_HCALL_HAS_OUTPUT);
+
+ if (data_out)
+ *data_out = args.r11;
+ return ret;
+}
diff --git a/tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c b/tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c
index 5e4455be828a..c5bee67099c5 100644
--- a/tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c
+++ b/tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c
@@ -608,4 +608,36 @@ void td_finalize(struct kvm_vm *vm)
void td_vcpu_run(struct kvm_vcpu *vcpu)
{
vcpu_run(vcpu);
+
+ /* Handle TD VMCALLs that require userspace handling. */
+ if (vcpu->run->exit_reason == KVM_EXIT_HYPERCALL &&
+ vcpu->run->hypercall.nr == KVM_HC_MAP_GPA_RANGE) {
+ handle_userspace_map_gpa(vcpu);
+ }
+}
+
+/*
+ * Handle conversion of memory with @size beginning @gpa for @vm. Set
+ * @shared_to_private to true for shared to private conversions and false
+ * otherwise.
+ *
+ * Since this is just for selftests, just keep both pieces of backing
+ * memory allocated and not deallocate/allocate memory; just do the
+ * minimum of calling KVM_MEMORY_ENCRYPT_REG_REGION and
+ * KVM_MEMORY_ENCRYPT_UNREG_REGION.
+ */
+void handle_memory_conversion(struct kvm_vm *vm, uint32_t vcpu_id, uint64_t gpa,
+ uint64_t size, bool shared_to_private)
+{
+ struct kvm_memory_attributes range;
+
+ range.address = gpa;
+ range.size = size;
+ range.attributes = shared_to_private ? KVM_MEMORY_ATTRIBUTE_PRIVATE : 0;
+ range.flags = 0;
+
+ pr_debug("\t... call KVM_SET_MEMORY_ATTRIBUTES ioctl from vCPU %u with gpa=%#lx, size=%#lx, attributes=%#llx\n",
+ vcpu_id, gpa, size, range.attributes);
+
+ vm_ioctl(vm, KVM_SET_MEMORY_ATTRIBUTES, &range);
}
diff --git a/tools/testing/selftests/kvm/x86/tdx_shared_mem_test.c b/tools/testing/selftests/kvm/x86/tdx_shared_mem_test.c
new file mode 100644
index 000000000000..79745e36ce3a
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86/tdx_shared_mem_test.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/kvm.h>
+#include <stdint.h>
+
+#include "kvm_util.h"
+#include "processor.h"
+#include "tdx/tdcall.h"
+#include "tdx/tdx.h"
+#include "tdx/tdx_util.h"
+#include "tdx/test_util.h"
+#include "test_util.h"
+
+#define TDX_SHARED_MEM_TEST_PRIVATE_GVA (0x80000000)
+#define TDX_SHARED_MEM_TEST_VADDR_SHARED_MASK BIT_ULL(30)
+#define TDX_SHARED_MEM_TEST_SHARED_GVA \
+ (TDX_SHARED_MEM_TEST_PRIVATE_GVA | \
+ TDX_SHARED_MEM_TEST_VADDR_SHARED_MASK)
+
+#define TDX_SHARED_MEM_TEST_GUEST_WRITE_VALUE (0xcafecafe)
+#define TDX_SHARED_MEM_TEST_HOST_WRITE_VALUE (0xabcdabcd)
+
+#define TDX_SHARED_MEM_TEST_INFO_PORT 0x87
+
+/*
+ * Shared variable between guest and host
+ */
+static uint64_t test_mem_shared_gpa;
+
+void guest_shared_mem(void)
+{
+ uint32_t *test_mem_shared_gva =
+ (uint32_t *)TDX_SHARED_MEM_TEST_SHARED_GVA;
+
+ uint64_t placeholder;
+ uint64_t ret;
+
+ /* Map gpa as shared */
+ ret = tdg_vp_vmcall_map_gpa(test_mem_shared_gpa, PAGE_SIZE,
+ &placeholder);
+ if (ret)
+ tdx_test_fatal_with_data(ret, __LINE__);
+
+ *test_mem_shared_gva = TDX_SHARED_MEM_TEST_GUEST_WRITE_VALUE;
+
+ /* Exit so host can read shared value */
+ ret = tdg_vp_vmcall_instruction_io(TDX_SHARED_MEM_TEST_INFO_PORT, 4,
+ PORT_WRITE, &placeholder);
+ if (ret)
+ tdx_test_fatal_with_data(ret, __LINE__);
+
+ /* Read value written by host and send it back out for verification */
+ ret = tdg_vp_vmcall_instruction_io(TDX_SHARED_MEM_TEST_INFO_PORT, 4,
+ PORT_WRITE,
+ (uint64_t *)test_mem_shared_gva);
+ if (ret)
+ tdx_test_fatal_with_data(ret, __LINE__);
+}
+
+int verify_shared_mem(void)
+{
+ vm_vaddr_t test_mem_private_gva;
+ uint64_t test_mem_private_gpa;
+ uint32_t *test_mem_hva;
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+
+ vm = td_create();
+ td_initialize(vm, VM_MEM_SRC_ANONYMOUS, 0);
+ vcpu = td_vcpu_add(vm, 0, guest_shared_mem);
+
+ /*
+ * Set up shared memory page for testing by first allocating as private
+ * and then mapping the same GPA again as shared. This way, the TD does
+ * not have to remap its page tables at runtime.
+ */
+ test_mem_private_gva = vm_vaddr_alloc(vm, vm->page_size,
+ TDX_SHARED_MEM_TEST_PRIVATE_GVA);
+ TEST_ASSERT_EQ(test_mem_private_gva, TDX_SHARED_MEM_TEST_PRIVATE_GVA);
+
+ test_mem_hva = addr_gva2hva(vm, test_mem_private_gva);
+ TEST_ASSERT(test_mem_hva,
+ "Guest address not found in guest memory regions\n");
+
+ test_mem_private_gpa = addr_gva2gpa(vm, test_mem_private_gva);
+ virt_map_shared(vm, TDX_SHARED_MEM_TEST_SHARED_GVA, test_mem_private_gpa, 1);
+
+ test_mem_shared_gpa = test_mem_private_gpa | vm->arch.s_bit;
+ sync_global_to_guest(vm, test_mem_shared_gpa);
+
+ td_finalize(vm);
+
+ vm_enable_cap(vm, KVM_CAP_EXIT_HYPERCALL, BIT_ULL(KVM_HC_MAP_GPA_RANGE));
+
+ printf("Verifying shared memory accesses for TDX\n");
+
+ /* Begin guest execution; guest writes to shared memory. */
+ printf("\t ... Starting guest execution\n");
+
+ /* Handle map gpa as shared */
+ tdx_run(vcpu);
+
+ tdx_run(vcpu);
+ tdx_test_assert_io(vcpu, TDX_SHARED_MEM_TEST_INFO_PORT, 4, PORT_WRITE);
+ TEST_ASSERT_EQ(*test_mem_hva, TDX_SHARED_MEM_TEST_GUEST_WRITE_VALUE);
+
+ *test_mem_hva = TDX_SHARED_MEM_TEST_HOST_WRITE_VALUE;
+ tdx_run(vcpu);
+ tdx_test_assert_io(vcpu, TDX_SHARED_MEM_TEST_INFO_PORT, 4, PORT_WRITE);
+ TEST_ASSERT_EQ(*(uint32_t *)((void *)vcpu->run + vcpu->run->io.data_offset),
+ TDX_SHARED_MEM_TEST_HOST_WRITE_VALUE);
+
+ printf("\t ... PASSED\n");
+
+ kvm_vm_free(vm);
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ if (!is_tdx_enabled()) {
+ printf("TDX is not supported by the KVM\n"
+ "Skipping the TDX tests.\n");
+ return 0;
+ }
+
+ return verify_shared_mem();
+}
--
2.50.0.rc2.692.g299adb8693-goog
next prev parent reply other threads:[~2025-06-13 19:14 UTC|newest]
Thread overview: 35+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-06-13 19:13 [PATCH v7 00/30] TDX KVM selftests Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 01/30] KVM: selftests: Add function to allow one-to-one GVA to GPA mappings Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 02/30] KVM: selftests: Expose function that sets up sregs based on VM's mode Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 03/30] KVM: selftests: Store initial stack address in struct kvm_vcpu Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 04/30] KVM: selftests: Add vCPU descriptor table initialization utility Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 05/30] KVM: selftests: Update kvm_init_vm_address_properties() for TDX Sagi Shahar
2025-07-21 21:51 ` Ira Weiny
2025-08-01 23:10 ` Sagi Shahar
2025-08-05 15:41 ` Ira Weiny
2025-08-07 20:19 ` Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 06/30] KVM: selftests: Add helper functions to create TDX VMs Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 07/30] KVM: selftests: TDX: Use KVM_TDX_CAPABILITIES to validate TDs' attribute configuration Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 08/30] KVM: selftests: TDX: Update load_td_memory_region() for VM memory backed by guest memfd Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 09/30] KVM: selftests: TDX: Add TDX lifecycle test Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 10/30] KVM: selftests: TDX: Add report_fatal_error test Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 11/30] KVM: selftests: TDX: Adding test case for TDX port IO Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 12/30] KVM: selftests: TDX: Add basic TDX CPUID test Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 13/30] KVM: selftests: TDX: Add basic TDG.VP.VMCALL<GetTdVmCallInfo> test Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 14/30] KVM: selftests: TDX: Add TDX IO writes test Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 15/30] KVM: selftests: TDX: Add TDX IO reads test Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 16/30] KVM: selftests: TDX: Add TDX MSR read/write tests Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 17/30] KVM: selftests: TDX: Add TDX HLT exit test Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 18/30] KVM: selftests: TDX: Add TDX MMIO reads test Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 19/30] KVM: selftests: TDX: Add TDX MMIO writes test Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 20/30] KVM: selftests: TDX: Add TDX CPUID TDVMCALL test Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 21/30] KVM: selftests: TDX: Verify the behavior when host consumes a TD private memory Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 22/30] KVM: selftests: TDX: Add TDG.VP.INFO test Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 23/30] KVM: selftests: Add functions to allow mapping as shared Sagi Shahar
2025-06-13 19:13 ` Sagi Shahar [this message]
2025-06-13 19:13 ` [PATCH v7 25/30] KVM: selftests: KVM: selftests: Expose new vm_vaddr_alloc_private() Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 26/30] KVM: selftests: TDX: Add support for TDG.MEM.PAGE.ACCEPT Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 27/30] KVM: selftests: TDX: Add support for TDG.VP.VEINFO.GET Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 28/30] KVM: selftests: TDX: Add TDX UPM selftest Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 29/30] KVM: selftests: TDX: Add TDX UPM selftests for implicit conversion Sagi Shahar
2025-06-13 19:13 ` [PATCH v7 30/30] KVM: selftests: TDX: Test LOG_DIRTY_PAGES flag to a non-GUEST_MEMFD memslot Sagi Shahar
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250613191359.35078-25-sagis@google.com \
--to=sagis@google.com \
--cc=ackerleytng@google.com \
--cc=afranji@google.com \
--cc=ajones@ventanamicro.com \
--cc=binbin.wu@linux.intel.com \
--cc=erdemaktas@google.com \
--cc=isaku.yamahata@intel.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=oliver.upton@linux.dev \
--cc=pbonzini@redhat.com \
--cc=pratikrajesh.sampat@amd.com \
--cc=reinette.chatre@intel.com \
--cc=rick.p.edgecombe@intel.com \
--cc=runanwang@google.com \
--cc=seanjc@google.com \
--cc=shuah@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox