[RFC PATCH 03/15] x86/tdx: Add TDG.TDI.RD module call wrapper for TDX Connect
From: Zhenzhong Duan
Date: Thu Sep 24 2026 - 00:12:28 EST
Introduce wrapper tdx_mcall_tdi_read() to provide tdx-guest driver with a
standard interface for querying specific metadata fields from a target
Trust Device Interface Control Structure (TDI_CS).
Explicitly mapping architectural TDX error codes into Linux POSIX errno
with two helpers, introducing tdx_mcall_to_errno() to handle general
module calls, while introducing tdx_mcall_tdi_to_errno() specifically for
TDI related module calls.
Unlike TDG.VP.VMCALL based hypercalls which communicate with the host,
TDG.TDI.RD is a module call (mcall) that communicates directly with the
TDX module. This distinction is reflected in the tdx_mcall_ naming
prefix, as opposed to tdx_hcall_ used for host-directed hypercalls.
Note that while host-bound hypercalls (such as tdx_hcall_tdcm()) pass a
platform-specific Device Identifier (devid), the func_id parameter used
here is defined by the TDISP standard (refer to Section 11.2 "TDISP
rules"). Although these values result in the same underlying bit format
when the PCI segment is zero, they represent separate structural
abstractions in their respective specifications.
Signed-off-by: Zhenzhong Duan <zhenzhong.duan@xxxxxxxxx>
---
arch/x86/coco/tdx/tdx.c | 5 ----
arch/x86/coco/tdx/tdx_connect.c | 41 +++++++++++++++++++++++++++++++
arch/x86/include/asm/shared/tdx.h | 1 +
arch/x86/include/asm/tdx.h | 20 +++++++++++++++
4 files changed, 62 insertions(+), 5 deletions(-)
diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c
index 22cc177a6db4..c3db2451c078 100644
--- a/arch/x86/coco/tdx/tdx.c
+++ b/arch/x86/coco/tdx/tdx.c
@@ -34,11 +34,6 @@
#define VE_GET_PORT_NUM(e) ((e) >> 16)
#define VE_IS_IO_STRING(e) ((e) & BIT(4))
-/* TDX Module call error codes */
-#define TDCALL_RETURN_CODE(a) ((a) >> 32)
-#define TDCALL_INVALID_OPERAND 0xc0000100
-#define TDCALL_OPERAND_BUSY 0x80000200
-
#define TDREPORT_SUBTYPE_0 0
static atomic_long_t nr_shared;
diff --git a/arch/x86/coco/tdx/tdx_connect.c b/arch/x86/coco/tdx/tdx_connect.c
index 0c6ca650771a..1409b1a30cc6 100644
--- a/arch/x86/coco/tdx/tdx_connect.c
+++ b/arch/x86/coco/tdx/tdx_connect.c
@@ -8,6 +8,17 @@
#include <asm/tdx.h>
#include <linux/mm.h>
+static inline int tdx_mcall_tdi_to_errno(u64 ret)
+{
+ switch (TDCALL_RETURN_CODE(ret)) {
+ case TDCALL_TDI_NOT_PRESENT:
+ case TDCALL_TDI_INVALID_METADATA:
+ return -ENODEV;
+ default:
+ return tdx_mcall_to_errno(ret);
+ }
+}
+
/*
* tdx_hcall_tdcm - Send a TDCM command to the host via TDG.VP.VMCALL<TDCM>.
*
@@ -30,3 +41,33 @@ u64 tdx_hcall_tdcm(u16 devid, void *buf, size_t size, u8 vector)
return _tdx_hypercall(TDVMCALL_TDCM, devid, cc_mkdec(virt_to_phys(buf)), size, vector);
}
EXPORT_SYMBOL_FOR_MODULES(tdx_hcall_tdcm, "tdx-guest");
+
+/**
+ * tdx_mcall_tdi_read() - Read field from a Trust Device Interface Control Structure (TDI_CS)
+ * @func_id: Function identifier specifying the TDI_CS
+ * @field: Field identifier within the specified TDI_CS
+ * @value: Storage for the successfully retrieved field value
+ *
+ * Invokes the TDG.TDI.RD TDCALL to read the value of @field within the TDI_CS specified
+ * by @func_id.
+ *
+ * Return 0 on success, -ENXIO for invalid operands, -EBUSY for busy operation,
+ * -ENODEV for TDI not present or invalid metadata, or -EIO on other TDCALL failures.
+ */
+int tdx_mcall_tdi_read(u64 func_id, u64 field, u64 *value)
+{
+ struct tdx_module_args args = {
+ .rcx = func_id,
+ .rdx = field,
+ };
+ u64 ret;
+
+ ret = __tdcall_ret(TDG_TDI_READ, &args);
+ if (!ret) {
+ *value = args.rcx;
+ return 0;
+ }
+
+ return tdx_mcall_tdi_to_errno(ret);
+}
+EXPORT_SYMBOL_FOR_MODULES(tdx_mcall_tdi_read, "tdx-guest");
diff --git a/arch/x86/include/asm/shared/tdx.h b/arch/x86/include/asm/shared/tdx.h
index f46a3171a512..665c12925ff6 100644
--- a/arch/x86/include/asm/shared/tdx.h
+++ b/arch/x86/include/asm/shared/tdx.h
@@ -20,6 +20,7 @@
#define TDG_MEM_PAGE_ACCEPT 6
#define TDG_VM_RD 7
#define TDG_VM_WR 8
+#define TDG_TDI_READ 67
/* TDX TD attributes */
#define TDX_TD_ATTR_DEBUG_BIT 0
diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
index df4496ef8a6a..4937e23d5483 100644
--- a/arch/x86/include/asm/tdx.h
+++ b/arch/x86/include/asm/tdx.h
@@ -77,6 +77,25 @@ void tdx_halt(void);
bool tdx_early_handle_ve(struct pt_regs *regs);
+/* TDX Module call error codes */
+#define TDCALL_RETURN_CODE(a) ((a) >> 32)
+#define TDCALL_INVALID_OPERAND 0xc0000100
+#define TDCALL_OPERAND_BUSY 0x80000200
+#define TDCALL_TDI_NOT_PRESENT 0xc0000f40
+#define TDCALL_TDI_INVALID_METADATA 0xc0000f41
+
+static inline int tdx_mcall_to_errno(u64 ret)
+{
+ switch (TDCALL_RETURN_CODE(ret)) {
+ case TDCALL_INVALID_OPERAND:
+ return -ENXIO;
+ case TDCALL_OPERAND_BUSY:
+ return -EBUSY;
+ default:
+ return -EIO;
+ }
+}
+
u64 tdg_vm_rd(u64 field, u64 *value);
int tdx_mcall_get_report0(u8 *reportdata, u8 *tdreport);
@@ -87,6 +106,7 @@ u64 tdx_hcall_get_quote(u8 *buf, size_t size);
#ifdef CONFIG_TDX_CONNECT_GUEST
u64 tdx_hcall_tdcm(u16 devid, void *buf, size_t size, u8 vector);
+int tdx_mcall_tdi_read(u64 func_id, u64 field, u64 *value);
#endif
void __init tdx_dump_attributes(u64 td_attr);
--
2.52.0