[RFC PATCH 09/15] x86/virt/tdx: Add interface to generate a Quote

From: Xu Yilun

Date: Fri May 22 2026 - 00:10:06 EST


From: Peter Fang <peter.fang@xxxxxxxxx>

Use the TDX Quoting extension's TDH.QUOTE.GET SEAMCALL to generate a
Quote. Since the interface is shared across all KVM instances,
serialize access to the SEAMCALL buffer with a mutex.

Allocate and return a per-call buffer containing the generated Quote so
callers don't need to size the Quote buffer themselves. The caller is
responsible for freeing the returned buffer.

Signed-off-by: Peter Fang <peter.fang@xxxxxxxxx>
Signed-off-by: Xu Yilun <yilun.xu@xxxxxxxxxxxxxxx>
---
arch/x86/include/asm/tdx.h | 2 +
arch/x86/virt/vmx/tdx/tdx.h | 1 +
arch/x86/virt/vmx/tdx/tdx.c | 82 +++++++++++++++++++++++++++++++++++++
3 files changed, 85 insertions(+)

diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
index 7b257088aa1e..bc512a00a0d0 100644
--- a/arch/x86/include/asm/tdx.h
+++ b/arch/x86/include/asm/tdx.h
@@ -177,6 +177,8 @@ struct tdx_vp {
};

bool tdx_quote_enabled(void);
+void *tdx_quote_generate(struct tdx_td *td, void *in_data, u32 in_data_len,
+ u32 *quote_len);

static inline u64 mk_keyed_paddr(u16 hkid, struct page *page)
{
diff --git a/arch/x86/virt/vmx/tdx/tdx.h b/arch/x86/virt/vmx/tdx/tdx.h
index 3849f4f9cc78..01a7d7d8ada9 100644
--- a/arch/x86/virt/vmx/tdx/tdx.h
+++ b/arch/x86/virt/vmx/tdx/tdx.h
@@ -49,6 +49,7 @@
#define TDH_EXT_INIT 60
#define TDH_EXT_MEM_ADD 61
#define TDH_SYS_DISABLE 69
+#define TDH_QUOTE_GET 98
#define TDH_QUOTE_INIT 100

/*
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
index b305fa5aab5c..821f677e9a86 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -62,6 +62,8 @@ static LIST_HEAD(tdx_memlist);
static struct tdx_sys_info tdx_sysinfo __ro_after_init;
static bool tdx_module_initialized __ro_after_init;

+static DEFINE_MUTEX(tdx_quote_lock);
+
static struct quote_data {
void *buf;
u64 buf_len;
@@ -1228,6 +1230,86 @@ bool tdx_quote_enabled(void)
}
EXPORT_SYMBOL_FOR_KVM(tdx_quote_enabled);

+#define QUOTE_ID_MASK GENMASK_U64(47, 32)
+
+static u64 tdx_quote_get(struct tdx_td *td, u64 in_data_pa, u64 in_data_len,
+ u64 hpa_list_pa, u64 total_len, u64 *quote_len)
+{
+ struct tdx_module_args args = {
+ .rcx = tdx_tdr_pa(td),
+ /* Don't bother specifying the quote id */
+ .rdx = QUOTE_ID_MASK & (u64)-1,
+ .r8 = in_data_pa,
+ .r9 = in_data_len,
+ .r10 = hpa_list_pa,
+ .r11 = total_len,
+ };
+ u64 r;
+
+ do {
+ r = seamcall_ret(TDH_QUOTE_GET, &args);
+ } while (r == TDX_INTERRUPTED_RESUMABLE);
+
+ *quote_len = args.rcx;
+
+ return r;
+}
+
+/**
+ * tdx_quote_generate() - Generate a quote for a TD
+ * @td: The TD to generate the quote for.
+ * @in_data: Input data for the quote request.
+ * @in_data_len: Size of the input data in bytes.
+ * @quote_len: Returned size of the generated quote in bytes.
+ *
+ * Use the TDX Quoting extension to generate a TD quote. Pass the input data
+ * through the shared quote buffer and return the quote.
+ *
+ * Return: Newly allocated quote buffer or %NULL on failure.
+ * The caller must free the returned buffer with kvfree().
+ */
+void *tdx_quote_generate(struct tdx_td *td, void *in_data, u32 in_data_len,
+ u32 *quote_len)
+{
+ void *quote_dup = NULL;
+ u64 r, out_len;
+
+ if (!tdx_quote_enabled())
+ return NULL;
+
+ /* TDH.QUOTE.GET expects the input data to fit in a page */
+ if (in_data_len > PAGE_SIZE)
+ return NULL;
+
+ mutex_lock(&tdx_quote_lock);
+
+ /*
+ * Use the first page of the quote buffer for input data. The buffer
+ * must be at least one page in size. @in_data may not be page-aligned,
+ * but TDH.QUOTE.GET expects page-aligned addresses.
+ */
+ memcpy(quote_data.buf, in_data, (size_t)in_data_len);
+
+ r = tdx_quote_get(td, quote_data.hpa_list[0], (u64)in_data_len,
+ quote_data.hpa_list_pa, quote_data.buf_len, &out_len);
+ if (r || !out_len || out_len > quote_data.buf_len)
+ goto out;
+
+ /*
+ * The quote buffer is a shared resource, so use it only for the
+ * SEAMCALL and copy the data out as soon as possible.
+ */
+ quote_dup = kvmemdup(quote_data.buf, out_len, GFP_KERNEL);
+
+out:
+ mutex_unlock(&tdx_quote_lock);
+
+ *quote_len = (u32)out_len;
+
+ return quote_dup;
+}
+EXPORT_SYMBOL_FOR_KVM(tdx_quote_generate);
+
#define HPAS_PER_PAGE (PAGE_SIZE / sizeof(u64))

static int tdx_quote_create_buf(unsigned int nr_pages, struct quote_data *qdata)
--
2.25.1