Re: [PATCH v15 22/23] KVM: selftests: Add support for TDX ucalls, via TDVMCALL_REPORT_FATAL_ERROR

From: Binbin Wu

Date: Sat Oct 10 2026 - 03:20:33 EST



On 10/2/2026 3:37 AM, Lisa Wang wrote:
> From: Sean Christopherson <seanjc@xxxxxxxxxx>
>
> 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
^
as

> 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 <seanjc@xxxxxxxxxx>
> Signed-off-by: Lisa Wang <wyihan@xxxxxxxxxx>

Also one nit below.

otherwise,
Reviewed-by: Binbin Wu <binbin.wu@xxxxxxxxxxxxxxx>

> ---
> 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]);

Nit:
It's more readable to have comment for data[12] and data[13] to
indicate they are the R12 and R13 passed.

> +
> + 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);
> }
>
>