[PATCH v2 2/2] virt: tdx-guest: Allocate Quote buffer dynamically

From: Peter Fang

Date: Fri Jul 17 2026 - 17:45:02 EST


From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@xxxxxxxxxxxxxxx>

The TDX attestation driver currently uses a fixed 128 KB Quote buffer
shared with the host VMM. This may be too small for Quotes using schemes
such as post-quantum cryptography (PQC), where larger certificate chains
can increase the Quote size significantly.

Allocate the Quote buffer based on the size reported by the TDX module
instead of always reserving a fixed-size buffer. This avoids wasting
memory on platforms that do not require larger Quotes. Older platforms
fall back to the default 128 KB buffer.

Because the Quote buffer must be physically contiguous, its size is
bound by the buddy allocator's maximum page order (4 MB), which should
be sufficient for current attestation needs.

struct tdx_quote_buf has a trailing flexible array, so use struct_size_t()
to calculate the buffer size.

Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@xxxxxxxxxxxxxxx>
Reviewed-by: Kiryl Shutsemau (Meta) <kas@xxxxxxxxxx>
Reviewed-by: Binbin Wu <binbin.wu@xxxxxxxxxxxxxxx>
Signed-off-by: Peter Fang <peter.fang@xxxxxxxxx>
---
v2:
- Use struct_size_t() instead of offsetof() to compute the buffer size. [Kiryl, Binbin]
- Pass __GFP_NOWARN to alloc_pages_exact() so an oversized size fails quietly. [sashiko]
- Reword the description to avoid implying Quotes can exceed the 4 MB limit. [Binbin]
- Rename quote_data_size to quote_data_len. [Sathya]
- Drop the Assisted-by tags, as the code was not written by AI.
---
drivers/virt/coco/tdx-guest/tdx-guest.c | 55 ++++++++++++++++++-------
1 file changed, 40 insertions(+), 15 deletions(-)

diff --git a/drivers/virt/coco/tdx-guest/tdx-guest.c b/drivers/virt/coco/tdx-guest/tdx-guest.c
index d0303e31e816..ce2c2ef74676 100644
--- a/drivers/virt/coco/tdx-guest/tdx-guest.c
+++ b/drivers/virt/coco/tdx-guest/tdx-guest.c
@@ -162,7 +162,7 @@ static void tdx_mr_deinit(const struct attribute_group *mr_grp)
* DICE-based attestation uses layered evidence that requires
* larger Quote size (~100K).
*/
-#define GET_QUOTE_BUF_SIZE SZ_128K
+#define GET_QUOTE_DEFAULT_BUF_SIZE SZ_128K

#define GET_QUOTE_CMD_VER 1

@@ -170,7 +170,7 @@ static void tdx_mr_deinit(const struct attribute_group *mr_grp)
#define GET_QUOTE_SUCCESS 0
#define GET_QUOTE_IN_FLIGHT 0xffffffffffffffff

-#define TDX_QUOTE_MAX_LEN (GET_QUOTE_BUF_SIZE - sizeof(struct tdx_quote_buf))
+#define TDX_QUOTE_BUF_LEN(n) struct_size_t(struct tdx_quote_buf, data, n)

/* struct tdx_quote_buf: Format of Quote request buffer.
* @version: Quote format version, filled by TD.
@@ -191,8 +191,9 @@ struct tdx_quote_buf {
u8 data[];
};

-/* Quote data buffer */
+/* Quote data buffer and length */
static void *quote_data;
+static size_t quote_data_len;

/* Lock to streamline quote requests */
static DEFINE_MUTEX(quote_lock);
@@ -209,9 +210,8 @@ static long tdx_get_report0(struct tdx_report_req __user *req)
USER_SOCKPTR(req->tdreport));
}

-static void free_quote_buf(void *buf)
+static void free_quote_buf(void *buf, size_t len)
{
- size_t len = PAGE_ALIGN(GET_QUOTE_BUF_SIZE);
unsigned int count = len >> PAGE_SHIFT;

if (set_memory_encrypted((unsigned long)buf, count)) {
@@ -222,19 +222,44 @@ static void free_quote_buf(void *buf)
free_pages_exact(buf, len);
}

-static void *alloc_quote_buf(void)
+static size_t get_quote_buf_size(void)
{
- size_t len = PAGE_ALIGN(GET_QUOTE_BUF_SIZE);
- unsigned int count = len >> PAGE_SHIFT;
+ size_t buf_size = GET_QUOTE_DEFAULT_BUF_SIZE;
+ u32 quote_size;
+
+ quote_size = tdx_get_max_quote_size();
+
+ if (quote_size)
+ /* Reported size does not include GetQuote header */
+ buf_size = TDX_QUOTE_BUF_LEN(quote_size);
+
+ return PAGE_ALIGN(buf_size);
+}
+
+static void *alloc_quote_buf(size_t *buflen)
+{
+ unsigned int count;
+ size_t len;
void *addr;

- addr = alloc_pages_exact(len, GFP_KERNEL | __GFP_ZERO);
+ len = get_quote_buf_size();
+
+ /*
+ * This fails if the requested size exceeds the buddy allocator's
+ * maximum order. Use __GFP_NOWARN since the size comes from the host
+ * and should fail quietly rather than warn.
+ */
+ addr = alloc_pages_exact(len, GFP_KERNEL | __GFP_ZERO | __GFP_NOWARN);
if (!addr)
return NULL;

+ count = len >> PAGE_SHIFT;
+
if (set_memory_decrypted((unsigned long)addr, count))
return NULL;

+ *buflen = len;
+
return addr;
}

@@ -285,7 +310,7 @@ static int tdx_report_new_locked(struct tsm_report *report, void *data)
if (desc->inblob_len != TDX_REPORTDATA_LEN)
return -EINVAL;

- memset(quote_data, 0, GET_QUOTE_BUF_SIZE);
+ memset(quote_data, 0, quote_data_len);

/* Update Quote buffer header */
quote_buf->version = GET_QUOTE_CMD_VER;
@@ -296,7 +321,7 @@ static int tdx_report_new_locked(struct tsm_report *report, void *data)
if (ret)
return ret;

- err = tdx_hcall_get_quote(quote_data, GET_QUOTE_BUF_SIZE);
+ err = tdx_hcall_get_quote(quote_data, quote_data_len);
if (err) {
pr_err("GetQuote hypercall failed, status:%llx\n", err);
return -EIO;
@@ -315,7 +340,7 @@ static int tdx_report_new_locked(struct tsm_report *report, void *data)

out_len = READ_ONCE(quote_buf->out_len);

- if (out_len > TDX_QUOTE_MAX_LEN)
+ if (TDX_QUOTE_BUF_LEN(out_len) > quote_data_len)
return -EFBIG;

buf = kvmemdup(quote_buf->data, out_len, GFP_KERNEL);
@@ -417,7 +442,7 @@ static int __init tdx_guest_init(void)
if (ret)
goto deinit_mr;

- quote_data = alloc_quote_buf();
+ quote_data = alloc_quote_buf(&quote_data_len);
if (!quote_data) {
pr_err("Failed to allocate Quote buffer\n");
ret = -ENOMEM;
@@ -431,7 +456,7 @@ static int __init tdx_guest_init(void)
return 0;

free_quote:
- free_quote_buf(quote_data);
+ free_quote_buf(quote_data, quote_data_len);
free_misc:
misc_deregister(&tdx_misc_dev);
deinit_mr:
@@ -444,7 +469,7 @@ module_init(tdx_guest_init);
static void __exit tdx_guest_exit(void)
{
tsm_report_unregister(&tdx_tsm_ops);
- free_quote_buf(quote_data);
+ free_quote_buf(quote_data, quote_data_len);
misc_deregister(&tdx_misc_dev);
tdx_mr_deinit(tdx_attr_groups[0]);
}
--
2.53.0