+ t->hdr.error_code = cpu_to_le64(TDX_VP_GET_QUOTE_QGS_UNAVAILABLE);
+ goto error;
+ }
+
+ g_free(in_data);
+ qemu_set_fd_handler(t->ioc->fd, tdx_get_quote_read, NULL, t);
+
+ return;
+error:
+ t->hdr.out_len = cpu_to_le32(0);
+
+ if (address_space_write(
+ &address_space_memory, t->gpa,
+ MEMTXATTRS_UNSPECIFIED, &t->hdr, sizeof(t->hdr)) != MEMTX_OK) {
+ error_report("TDX: failed to update GetQuote header.\n");
+ }
+ tdx_td_notify(t);
+
+ qio_channel_close(QIO_CHANNEL(t->ioc), &err);
+ object_unref(OBJECT(t->ioc));
+ g_free(t);
+ g_free(in_data);
+
+ /* Maintain the number of in-flight requests. */
+ ms = MACHINE(qdev_get_machine());
+ tdx = TDX_GUEST(ms->cgs);
+ qemu_mutex_lock(&tdx->lock);
+ tdx->quote_generation_num--;
+ qemu_mutex_unlock(&tdx->lock);
+ return;
+}
+
+static void tdx_handle_get_quote(X86CPU *cpu, struct kvm_tdx_vmcall *vmcall)
+{
+ hwaddr gpa = vmcall->in_r12;
+ uint64_t buf_len = vmcall->in_r13;
+ struct tdx_get_quote_header hdr;
+ MachineState *ms;
+ TdxGuest *tdx;
+ QIOChannelSocket *ioc;
+ struct tdx_get_quote_task *t;
+
+ vmcall->status_code = TDG_VP_VMCALL_INVALID_OPERAND;
+
+ /* GPA must be shared. */
+ if (!(gpa & tdx_shared_bit(cpu))) {
+ return;
+ }
+ gpa &= ~tdx_shared_bit(cpu);
+
+ if (!QEMU_IS_ALIGNED(gpa, 4096) || !QEMU_IS_ALIGNED(buf_len, 4096)) {
+ vmcall->status_code = TDG_VP_VMCALL_ALIGN_ERROR;
+ return;
+ }
+ if (buf_len == 0) {
+ return;
+ }
+
+ if (address_space_read(&address_space_memory, gpa, MEMTXATTRS_UNSPECIFIED,
+ &hdr, sizeof(hdr)) != MEMTX_OK) {
+ return;
+ }
+ if (le64_to_cpu(hdr.structure_version) != TDX_GET_QUOTE_STRUCTURE_VERSION) {
+ return;
+ }
+ /*
+ * Paranoid: Guest should clear error_code and out_len to avoid information
+ * leak. Enforce it. The initial value of them doesn't matter for qemu to
+ * process the request.
+ */
+ if (le64_to_cpu(hdr.error_code) != TDX_VP_GET_QUOTE_SUCCESS ||
+ le32_to_cpu(hdr.out_len) != 0) {
+ return;
+ }
+
+ /* Only safe-guard check to avoid too large buffer size. */
+ if (buf_len > TDX_GET_QUOTE_MAX_BUF_LEN ||
+ le32_to_cpu(hdr.in_len) > TDX_GET_QUOTE_MAX_BUF_LEN ||
+ le32_to_cpu(hdr.in_len) > buf_len) {
+ return;
+ }
+
+ /* Mark the buffer in-flight. */
+ hdr.error_code = cpu_to_le64(TDX_VP_GET_QUOTE_IN_FLIGHT);
+ if (address_space_write(&address_space_memory, gpa, MEMTXATTRS_UNSPECIFIED,
+ &hdr, sizeof(hdr)) != MEMTX_OK) {
+ return;
+ }
+
+ ms = MACHINE(qdev_get_machine());
+ tdx = TDX_GUEST(ms->cgs);
+ ioc = qio_channel_socket_new();
+
+ t = g_malloc(sizeof(*t));
+ t->apic_id = tdx->event_notify_apic_id;
+ t->gpa = gpa;
+ t->buf_len = buf_len;
+ t->out_data = g_malloc(t->buf_len);
+ t->out_len = 0;
+ t->hdr = hdr;
+ t->ioc = ioc;
+
+ qemu_mutex_lock(&tdx->lock);
+ if (!tdx->quote_generation ||
+ /* Prevent too many in-flight get-quote request. */
+ tdx->quote_generation_num >= TDX_MAX_GET_QUOTE_REQUEST) {
+ qemu_mutex_unlock(&tdx->lock);
+ vmcall->status_code = TDG_VP_VMCALL_RETRY;
+ object_unref(OBJECT(ioc));
+ g_free(t->out_data);
+ g_free(t);
+ return;
+ }
+ tdx->quote_generation_num++;
+ t->event_notify_interrupt = tdx->event_notify_interrupt;
+ qio_channel_socket_connect_async(
+ ioc, tdx->quote_generation, tdx_handle_get_quote_connected, t, NULL,
+ NULL);
+ qemu_mutex_unlock(&tdx->lock);
+
+ vmcall->status_code = TDG_VP_VMCALL_SUCCESS;
+}
+
static void tdx_handle_setup_event_notify_interrupt(X86CPU *cpu,
struct kvm_tdx_vmcall *vmcall)
{
@@ -1005,6 +1432,9 @@ static void tdx_handle_vmcall(X86CPU *cpu, struct kvm_tdx_vmcall *vmcall)
}
switch (vmcall->subfunction) {
+ case TDG_VP_VMCALL_GET_QUOTE:
+ tdx_handle_get_quote(cpu, vmcall);
+ break;
case TDG_VP_VMCALL_SETUP_EVENT_NOTIFY_INTERRUPT:
tdx_handle_setup_event_notify_interrupt(cpu, vmcall);
break;
diff --git a/target/i386/kvm/tdx.h b/target/i386/kvm/tdx.h
index 4a8d67cc9fdb..4a989805493e 100644
--- a/target/i386/kvm/tdx.h
+++ b/target/i386/kvm/tdx.h
@@ -5,8 +5,10 @@
#include CONFIG_DEVICES /* CONFIG_TDX */
#endif
+#include <linux/kvm.h>
#include "exec/confidential-guest-support.h"
#include "hw/i386/tdvf.h"
+#include "io/channel-socket.h"
#include "sysemu/kvm.h"
#define TYPE_TDX_GUEST "tdx-guest"
@@ -47,6 +49,10 @@ typedef struct TdxGuest {
/* runtime state */
int event_notify_interrupt;
uint32_t event_notify_apic_id;
+
+ /* GetQuote */
+ int quote_generation_num;
+ SocketAddress *quote_generation;
} TdxGuest;
#ifdef CONFIG_TDX
--
2.34.1