From: Sean Christopherson <[email protected]>

Add support for doing ucalls on TDX by abusing TDVMCALL_REPORT_FATAL_ERROR
to pass the address of the payload to the host.  The "fatal error" TDVMCALL
is perfectly suited for passing information to host userspace, is both the
TDX Module and KVM allow the guest to pass (almost) all registers to the
host, i.e. provide enough of a data payload to make a collision with a real
fatal error practically impossible.

TDX can't use port I/O, as the TDX ABI doesn't allow the guest to share
arbitrary register state with the host on a port I/O exit, and the port I/O
data payload is limited to 4 bytes, i.e. would potentially truncate the
ucall address.

Alternatively, TDX could use MMIO, but using a magic emulated MMIO address
is fragile (see the TODO in __vm_create()), especially for TDX since TDX
doesn't support read-only memslots, i.e. doesn't have line of sight towards
addressing the TODO.  E.g. TDX could hardcode the address to something that
is all but guaranteed to be unused on x86, e.g. the I/O APIC base address
or the HPET address, but that doesn't truly address the fragility concerns,
and it's ugly because ucall_arch_init() would completely ignore the passed
in @mmio_gpa despite obviously utilizing emulated MMIO.

Signed-off-by: Sean Christopherson <[email protected]>
Signed-off-by: Lisa Wang <[email protected]>
---
 tools/testing/selftests/kvm/include/x86/tdx/tdx.h |  7 ++++++
 tools/testing/selftests/kvm/lib/x86/ucall.c       | 27 ++++++++++++++++++++++-
 2 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/kvm/include/x86/tdx/tdx.h 
b/tools/testing/selftests/kvm/include/x86/tdx/tdx.h
index d41a1efc8a63..9982aaaba885 100644
--- a/tools/testing/selftests/kvm/include/x86/tdx/tdx.h
+++ b/tools/testing/selftests/kvm/include/x86/tdx/tdx.h
@@ -4,6 +4,13 @@
 
 #include <linux/types.h>
 
+/* TDX hypercall Leaf IDs */
+#define TDVMCALL_GET_TD_VM_CALL_INFO           0x10000
+#define TDVMCALL_MAP_GPA                       0x10001
+#define TDVMCALL_GET_QUOTE                     0x10002
+#define TDVMCALL_REPORT_FATAL_ERROR            0x10003
+#define TDVMCALL_SETUP_EVENT_NOTIFY_INTERRUPT  0x10004
+
 u64 __tdvmcall(u64 fn, u64 r12, u64 r13, u64 r14, u64 r15);
 
 #endif /* SELFTEST_KVM_TDX_TDX_H */
diff --git a/tools/testing/selftests/kvm/lib/x86/ucall.c 
b/tools/testing/selftests/kvm/lib/x86/ucall.c
index c003df3c7b8a..c1cd32d68464 100644
--- a/tools/testing/selftests/kvm/lib/x86/ucall.c
+++ b/tools/testing/selftests/kvm/lib/x86/ucall.c
@@ -5,8 +5,28 @@
  * Copyright (C) 2018, Red Hat, Inc.
  */
 #include "kvm_util.h"
+#include "tdx/tdx.h"
+#include "tdx/tdx_util.h"
 
-#define UCALL_PIO_PORT ((u16)0x1000)
+#define UCALL_PIO_PORT         ((u16)0x1000)
+#define UCALL_TDX_MAGIC                0xabacadabaULL
+
+static void ucall_tdx_do_ucall(gva_t uc)
+{
+       __tdvmcall(TDVMCALL_REPORT_FATAL_ERROR, UCALL_TDX_MAGIC, uc, 0, 0);
+}
+
+static void *ucall_tdx_get_ucall(struct kvm_vcpu *vcpu)
+{
+       struct kvm_run *run = vcpu->run;
+
+       if (run->exit_reason == KVM_EXIT_SYSTEM_EVENT &&
+           run->system_event.type == KVM_SYSTEM_EVENT_TDX_FATAL &&
+           run->system_event.data[12] == UCALL_TDX_MAGIC)
+               return (void *)(run->system_event.data[13]);
+
+       return NULL;
+}
 
 static void ucall_x86_do_ucall(gva_t uc)
 {
@@ -37,6 +57,11 @@ static struct {
 
 void ucall_arch_init(struct kvm_vm *vm, gpa_t mmio_gpa)
 {
+       if (is_tdx_vm(vm)) {
+               ucall_x86_ops.do_ucall = ucall_tdx_do_ucall;
+               ucall_x86_ops.get_ucall = ucall_tdx_get_ucall;
+       }
+
        sync_global_to_guest(vm, ucall_x86_ops);
 }
 

-- 
2.56.0.rc1.315.gc6ed9934b7-goog


Reply via email to