Re: [PATCH v3 52/70] i386/tdx: handle TDG.VP.VMCALL

Xiaoyao Li
2023-12-29T02:30:24Z

On 11/16/2023 1:58 AM, Daniel P. Berrangé wrote:
> On Wed, Nov 15, 2023 at 02:15:01AM -0500, Xiaoyao Li wrote:
>> From: Isaku Yamahata <isaku.yamahata@intel.com>
>>
>> For GetQuote, delegate a request to Quote Generation Service.
>> Add property "quote-generation-socket" to tdx-guest, whihc is a property
>> of type SocketAddress to specify Quote Generation Service(QGS).
>>
>> On request, connect to the QGS, read request buffer from shared guest
>> memory, send the request buffer to the server and store the response
>> into shared guest memory and notify TD guest by interrupt.
>>
>> command line example:
>> qemu-system-x86_64 \
>> -object '{"qom-type":"tdx-guest","id":"tdx0","quote-generation-socket":{"type": "vsock", "cid":"2","port":"1234"}}' \
>> -machine confidential-guest-support=tdx0
>>
>> Signed-off-by: Isaku Yamahata <isaku.yamahata@intel.com>
>> Codeveloped-by: Chenyi Qiang <chenyi.qiang@intel.com>
>> Signed-off-by: Chenyi Qiang <chenyi.qiang@intel.com>
>> Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
>> ---
>> Changes in v3:
>> - rename property "quote-generation-service" to "quote-generation-socket";
>> - change the type of "quote-generation-socket" from str to
>> SocketAddress;
>> - squash next patch into this one;
>> ---
>> qapi/qom.json | 5 +-
>> target/i386/kvm/tdx.c | 430 ++++++++++++++++++++++++++++++++++++++++++
>> target/i386/kvm/tdx.h | 6 +
>> 3 files changed, 440 insertions(+), 1 deletion(-)
>>
>> +static void tdx_handle_get_quote_connected(QIOTask *task, gpointer opaque)
>> +{
>> + struct tdx_get_quote_task *t = opaque;
>> + Error *err = NULL;
>> + char *in_data = NULL;
>> + MachineState *ms;
>> + TdxGuest *tdx;
>> +
>> + t->hdr.error_code = cpu_to_le64(TDX_VP_GET_QUOTE_ERROR);
>> + if (qio_task_propagate_error(task, NULL)) {
>> + t->hdr.error_code = cpu_to_le64(TDX_VP_GET_QUOTE_QGS_UNAVAILABLE);
>> + goto error;
>> + }
>> +
>> + in_data = g_malloc(le32_to_cpu(t->hdr.in_len));
>> + if (!in_data) {
>> + goto error;
>> + }
>> +
>> + if (address_space_read(&address_space_memory, t->gpa + sizeof(t->hdr),
>> + MEMTXATTRS_UNSPECIFIED, in_data,
>> + le32_to_cpu(t->hdr.in_len)) != MEMTX_OK) {
>> + goto error;
>> + }
>> +
>> + qio_channel_set_blocking(QIO_CHANNEL(t->ioc), false, NULL);
>
> You've set the channel to non-blocking, but....
>
>> +
>> + if (qio_channel_write_all(QIO_CHANNEL(t->ioc), in_data,
>> + le32_to_cpu(t->hdr.in_len), &err) ||
>> + err) {
>
> ...this method will block execution of this thread, by either
> sleeping in poll() or doing a coroutine yield.
>
> I don't think this is in coroutine context, so presumably this
> is just blocking. So what was the point in marking the channel
> non-blocking ?

Hi Dainel,

First of all, I'm not good at socket or qio channel thing. Please
correct me and teach me when I'm wrong.

I'm not the author of this patch. My understanding is that, set it to
non-blocking is for the qio_channel_write_all() to proceed immediately?

If set non-blocking is not needed, I can remove it.

> You are setting up a background watch to wait for the reply
> so we don't block this thread, so you seem to want non-blocking
> behaviour.

Both sending and receiving are in a new thread created by
qio_channel_socket_connect_async(). So I think both of then can be
blocking and don't need to be in another background thread.

what's your suggestion on it? Make both sending and receiving blocking
or non-blocking?

> Given this, you should not be using qio_channel_write_all()
> most likely. I think you need to be using qio_channel_add_watch
> to get notified when it is *writable*, to send 'in_data'
> incrementally & non-blocking. When that is finished then create
> another watch to wait for the reply.
>
>
>> + 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
>>
>
> With regards,
> Daniel