diff mbox series

[v4,50/66] i386/tdx: handle TDG.VP.VMCALL<GetQuote>

Message ID 20240125032328.2522472-51-xiaoyao.li@intel.com (mailing list archive)
State New, archived
Headers show
Series QEMU Guest memfd + QEMU TDX support | expand

Commit Message

Xiaoyao Li Jan. 25, 2024, 3:23 a.m. UTC
From: Isaku Yamahata <isaku.yamahata@intel.com>

Add property "quote-generation-socket" to tdx-guest, which is a property
of type SocketAddress to specify Quote Generation Service(QGS).

On request of GetQuote, it connects to the QGS socket, read request
data from shared guest memory, send the request data to the QGS,
and store the response into shared guest memory, at last 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":"1","port":"1234"}}' \
    -machine confidential-guest-support=tdx0

Note, above example uses vsock type socket because the QGS we used
implements the vsock socket. It can be other types, like UNIX socket,
which depends on the implementation of QGS.

To avoid no response from QGS server, setup a timer for the transaction.
If timeout, make it an error and interrupt guest. Define the threshold of
time to 30s at present, maybe change to other value if not appropriate.

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>
Codeveloped-by: Xiaoyao Li <xiaoyao.li@intel.com>
Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
---
Changes in v4:
- merge next patch "i386/tdx: setup a timer for the qio channel";

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                         |   6 +-
 target/i386/kvm/meson.build           |   2 +-
 target/i386/kvm/tdx-quote-generator.c | 170 ++++++++++++++++++++
 target/i386/kvm/tdx-quote-generator.h |  95 +++++++++++
 target/i386/kvm/tdx.c                 | 216 ++++++++++++++++++++++++++
 target/i386/kvm/tdx.h                 |   6 +
 6 files changed, 493 insertions(+), 2 deletions(-)
 create mode 100644 target/i386/kvm/tdx-quote-generator.c
 create mode 100644 target/i386/kvm/tdx-quote-generator.h

Comments

Daniel P. Berrangé Feb. 19, 2024, 12:55 p.m. UTC | #1
On Mon, Feb 19, 2024 at 01:50:12PM +0100, Markus Armbruster wrote:
> Xiaoyao Li <xiaoyao.li@intel.com> writes:
> 
> > From: Isaku Yamahata <isaku.yamahata@intel.com>
> >
> > Add property "quote-generation-socket" to tdx-guest, which is a property
> > of type SocketAddress to specify Quote Generation Service(QGS).
> >
> > On request of GetQuote, it connects to the QGS socket, read request
> > data from shared guest memory, send the request data to the QGS,
> > and store the response into shared guest memory, at last 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":"1","port":"1234"}}' \
> >     -machine confidential-guest-support=tdx0
> >
> > Note, above example uses vsock type socket because the QGS we used
> > implements the vsock socket. It can be other types, like UNIX socket,
> > which depends on the implementation of QGS.
> >
> > To avoid no response from QGS server, setup a timer for the transaction.
> > If timeout, make it an error and interrupt guest. Define the threshold of
> > time to 30s at present, maybe change to other value if not appropriate.
> >
> > 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>
> > Codeveloped-by: Xiaoyao Li <xiaoyao.li@intel.com>
> > Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
> > ---
> > Changes in v4:
> > - merge next patch "i386/tdx: setup a timer for the qio channel";
> >
> > 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                         |   6 +-
> >  target/i386/kvm/meson.build           |   2 +-
> >  target/i386/kvm/tdx-quote-generator.c | 170 ++++++++++++++++++++
> >  target/i386/kvm/tdx-quote-generator.h |  95 +++++++++++
> >  target/i386/kvm/tdx.c                 | 216 ++++++++++++++++++++++++++
> >  target/i386/kvm/tdx.h                 |   6 +
> >  6 files changed, 493 insertions(+), 2 deletions(-)
> >  create mode 100644 target/i386/kvm/tdx-quote-generator.c
> >  create mode 100644 target/i386/kvm/tdx-quote-generator.h
> >
> > diff --git a/qapi/qom.json b/qapi/qom.json
> > index 15445f9e41fc..c60fb5710961 100644
> > --- a/qapi/qom.json
> > +++ b/qapi/qom.json
> > @@ -914,13 +914,17 @@
> >  #     e.g., specific to the workload rather than the run-time or OS.
> >  #     base64 encoded SHA384 digest.
> >  #
> > +# @quote-generation-socket: socket address for Quote Generation
> > +#     Service(QGS)
> 
> Space between "Service" and "(QGS)", please.
> 
> The description feels too terse.  What is the "Quote Generation
> Service", and why should I care?

The "Quote Generation Service" is a daemon running on the host.
The reference implementation is at

  https://github.com/intel/SGXDataCenterAttestationPrimitives/tree/master/QuoteGeneration/quote_wrapper/qgs

If you don't provide this, then quests won't bet able to generate
quotes needed for attestation. So although this is technically
optional, in practice for a sane deployment, an admin should always
provide this

> 
> > +#
> >  # Since: 9.0
> >  ##
> >  { 'struct': 'TdxGuestProperties',
> >    'data': { '*sept-ve-disable': 'bool',
> >              '*mrconfigid': 'str',
> >              '*mrowner': 'str',
> > -            '*mrownerconfig': 'str' } }
> > +            '*mrownerconfig': 'str',
> > +            '*quote-generation-socket': 'SocketAddress' } }
> >  
> >  ##
> >  # @ThreadContextProperties:
> 
> [...]
> 

With regards,
Daniel
Markus Armbruster Feb. 19, 2024, 2:41 p.m. UTC | #2
Daniel P. Berrangé <berrange@redhat.com> writes:

> On Mon, Feb 19, 2024 at 01:50:12PM +0100, Markus Armbruster wrote:
>> Xiaoyao Li <xiaoyao.li@intel.com> writes:
>> 
>> > From: Isaku Yamahata <isaku.yamahata@intel.com>
>> >
>> > Add property "quote-generation-socket" to tdx-guest, which is a property
>> > of type SocketAddress to specify Quote Generation Service(QGS).
>> >
>> > On request of GetQuote, it connects to the QGS socket, read request
>> > data from shared guest memory, send the request data to the QGS,
>> > and store the response into shared guest memory, at last 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":"1","port":"1234"}}' \
>> >     -machine confidential-guest-support=tdx0
>> >
>> > Note, above example uses vsock type socket because the QGS we used
>> > implements the vsock socket. It can be other types, like UNIX socket,
>> > which depends on the implementation of QGS.
>> >
>> > To avoid no response from QGS server, setup a timer for the transaction.
>> > If timeout, make it an error and interrupt guest. Define the threshold of
>> > time to 30s at present, maybe change to other value if not appropriate.
>> >
>> > 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>
>> > Codeveloped-by: Xiaoyao Li <xiaoyao.li@intel.com>
>> > Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
>> > ---
>> > Changes in v4:
>> > - merge next patch "i386/tdx: setup a timer for the qio channel";
>> >
>> > 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                         |   6 +-
>> >  target/i386/kvm/meson.build           |   2 +-
>> >  target/i386/kvm/tdx-quote-generator.c | 170 ++++++++++++++++++++
>> >  target/i386/kvm/tdx-quote-generator.h |  95 +++++++++++
>> >  target/i386/kvm/tdx.c                 | 216 ++++++++++++++++++++++++++
>> >  target/i386/kvm/tdx.h                 |   6 +
>> >  6 files changed, 493 insertions(+), 2 deletions(-)
>> >  create mode 100644 target/i386/kvm/tdx-quote-generator.c
>> >  create mode 100644 target/i386/kvm/tdx-quote-generator.h
>> >
>> > diff --git a/qapi/qom.json b/qapi/qom.json
>> > index 15445f9e41fc..c60fb5710961 100644
>> > --- a/qapi/qom.json
>> > +++ b/qapi/qom.json
>> > @@ -914,13 +914,17 @@
>> >  #     e.g., specific to the workload rather than the run-time or OS.
>> >  #     base64 encoded SHA384 digest.
>> >  #
>> > +# @quote-generation-socket: socket address for Quote Generation
>> > +#     Service(QGS)
>> 
>> Space between "Service" and "(QGS)", please.
>> 
>> The description feels too terse.  What is the "Quote Generation
>> Service", and why should I care?
>
> The "Quote Generation Service" is a daemon running on the host.
> The reference implementation is at
>
>   https://github.com/intel/SGXDataCenterAttestationPrimitives/tree/master/QuoteGeneration/quote_wrapper/qgs
>
> If you don't provide this, then quests won't bet able to generate
> quotes needed for attestation. So although this is technically
> optional, in practice for a sane deployment, an admin should always
> provide this

Thanks.  Care to work some of this information into the doc comment?

>> > +#
>> >  # Since: 9.0
>> >  ##
>> >  { 'struct': 'TdxGuestProperties',
>> >    'data': { '*sept-ve-disable': 'bool',
>> >              '*mrconfigid': 'str',
>> >              '*mrowner': 'str',
>> > -            '*mrownerconfig': 'str' } }
>> > +            '*mrownerconfig': 'str',
>> > +            '*quote-generation-socket': 'SocketAddress' } }
>> >  
>> >  ##
>> >  # @ThreadContextProperties:
>> 
>> [...]
>> 
>
> With regards,
> Daniel
Xiaoyao Li Feb. 20, 2024, 2:16 p.m. UTC | #3
On 2/19/2024 10:41 PM, Markus Armbruster wrote:
> Daniel P. Berrangé <berrange@redhat.com> writes:
> 
>> On Mon, Feb 19, 2024 at 01:50:12PM +0100, Markus Armbruster wrote:
>>> Xiaoyao Li <xiaoyao.li@intel.com> writes:
>>>
>>>> From: Isaku Yamahata <isaku.yamahata@intel.com>
>>>>
>>>> Add property "quote-generation-socket" to tdx-guest, which is a property
>>>> of type SocketAddress to specify Quote Generation Service(QGS).
>>>>
>>>> On request of GetQuote, it connects to the QGS socket, read request
>>>> data from shared guest memory, send the request data to the QGS,
>>>> and store the response into shared guest memory, at last 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":"1","port":"1234"}}' \
>>>>      -machine confidential-guest-support=tdx0
>>>>
>>>> Note, above example uses vsock type socket because the QGS we used
>>>> implements the vsock socket. It can be other types, like UNIX socket,
>>>> which depends on the implementation of QGS.
>>>>
>>>> To avoid no response from QGS server, setup a timer for the transaction.
>>>> If timeout, make it an error and interrupt guest. Define the threshold of
>>>> time to 30s at present, maybe change to other value if not appropriate.
>>>>
>>>> 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>
>>>> Codeveloped-by: Xiaoyao Li <xiaoyao.li@intel.com>
>>>> Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
>>>> ---
>>>> Changes in v4:
>>>> - merge next patch "i386/tdx: setup a timer for the qio channel";
>>>>
>>>> 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                         |   6 +-
>>>>   target/i386/kvm/meson.build           |   2 +-
>>>>   target/i386/kvm/tdx-quote-generator.c | 170 ++++++++++++++++++++
>>>>   target/i386/kvm/tdx-quote-generator.h |  95 +++++++++++
>>>>   target/i386/kvm/tdx.c                 | 216 ++++++++++++++++++++++++++
>>>>   target/i386/kvm/tdx.h                 |   6 +
>>>>   6 files changed, 493 insertions(+), 2 deletions(-)
>>>>   create mode 100644 target/i386/kvm/tdx-quote-generator.c
>>>>   create mode 100644 target/i386/kvm/tdx-quote-generator.h
>>>>
>>>> diff --git a/qapi/qom.json b/qapi/qom.json
>>>> index 15445f9e41fc..c60fb5710961 100644
>>>> --- a/qapi/qom.json
>>>> +++ b/qapi/qom.json
>>>> @@ -914,13 +914,17 @@
>>>>   #     e.g., specific to the workload rather than the run-time or OS.
>>>>   #     base64 encoded SHA384 digest.
>>>>   #
>>>> +# @quote-generation-socket: socket address for Quote Generation
>>>> +#     Service(QGS)
>>>
>>> Space between "Service" and "(QGS)", please.
>>>
>>> The description feels too terse.  What is the "Quote Generation
>>> Service", and why should I care?
>>
>> The "Quote Generation Service" is a daemon running on the host.
>> The reference implementation is at
>>
>>    https://github.com/intel/SGXDataCenterAttestationPrimitives/tree/master/QuoteGeneration/quote_wrapper/qgs
>>
>> If you don't provide this, then quests won't bet able to generate
>> quotes needed for attestation. So although this is technically
>> optional, in practice for a sane deployment, an admin should always
>> provide this
> 
> Thanks.  Care to work some of this information into the doc comment?

Sure. Will add a new section of Attestation in the last patch.

>>>> +#
>>>>   # Since: 9.0
>>>>   ##
>>>>   { 'struct': 'TdxGuestProperties',
>>>>     'data': { '*sept-ve-disable': 'bool',
>>>>               '*mrconfigid': 'str',
>>>>               '*mrowner': 'str',
>>>> -            '*mrownerconfig': 'str' } }
>>>> +            '*mrownerconfig': 'str',
>>>> +            '*quote-generation-socket': 'SocketAddress' } }
>>>>   
>>>>   ##
>>>>   # @ThreadContextProperties:
>>>
>>> [...]
>>>
>>
>> With regards,
>> Daniel
>
Daniel P. Berrangé Feb. 22, 2024, 4:30 p.m. UTC | #4
On Wed, Jan 24, 2024 at 10:23:12PM -0500, Xiaoyao Li wrote:
> From: Isaku Yamahata <isaku.yamahata@intel.com>
> 
> Add property "quote-generation-socket" to tdx-guest, which is a property
> of type SocketAddress to specify Quote Generation Service(QGS).
> 
> On request of GetQuote, it connects to the QGS socket, read request
> data from shared guest memory, send the request data to the QGS,
> and store the response into shared guest memory, at last 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":"1","port":"1234"}}' \
>     -machine confidential-guest-support=tdx0
> 
> Note, above example uses vsock type socket because the QGS we used
> implements the vsock socket. It can be other types, like UNIX socket,
> which depends on the implementation of QGS.

Can you confirm again exactly what QGS impl you are testing against ?

I've tried the impl at

   https://github.com/intel/SGXDataCenterAttestationPrimitives/tree/master/QuoteGeneration/quote_wrapper/qgs

which supports UNIX sockets and VSOCK. In both cases, however, it
appears to be speaking a different protocol than your QEMU impl
below uses.

Specifically here:

  https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/master/QuoteGeneration/quote_wrapper/qgs/qgs_server.cpp#L143

it is reading 4 bytes of header, which are interpreted as the length
of the payload which will then be read off the wire. IIUC the payload
it expects is the TDREPORT struct.

Your QEMU patches here meanwhile are just sending the payload from
the GetQuote hypercall which is the TDREPORT struct.

IOW, QEMU is not sending the 4 byte length header the QGS expects.
and whole thing fails.

> 
> To avoid no response from QGS server, setup a timer for the transaction.
> If timeout, make it an error and interrupt guest. Define the threshold of
> time to 30s at present, maybe change to other value if not appropriate.
> 
> 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>
> Codeveloped-by: Xiaoyao Li <xiaoyao.li@intel.com>
> Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
> ---

With regards,
Daniel
Xiaoyao Li Feb. 23, 2024, 1:06 a.m. UTC | #5
+ Feng Qiu,

On 2/23/2024 12:30 AM, Daniel P. Berrangé wrote:
> On Wed, Jan 24, 2024 at 10:23:12PM -0500, Xiaoyao Li wrote:
>> From: Isaku Yamahata <isaku.yamahata@intel.com>
>>
>> Add property "quote-generation-socket" to tdx-guest, which is a property
>> of type SocketAddress to specify Quote Generation Service(QGS).
>>
>> On request of GetQuote, it connects to the QGS socket, read request
>> data from shared guest memory, send the request data to the QGS,
>> and store the response into shared guest memory, at last 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":"1","port":"1234"}}' \
>>      -machine confidential-guest-support=tdx0
>>
>> Note, above example uses vsock type socket because the QGS we used
>> implements the vsock socket. It can be other types, like UNIX socket,
>> which depends on the implementation of QGS.
> 
> Can you confirm again exactly what QGS impl you are testing against ?
> > I've tried the impl at
> 
>     https://github.com/intel/SGXDataCenterAttestationPrimitives/tree/master/QuoteGeneration/quote_wrapper/qgs
> 
> which supports UNIX sockets and VSOCK. In both cases, however, it
> appears to be speaking a different protocol than your QEMU impl
> below uses.
> 
> Specifically here:
> 
>    https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/master/QuoteGeneration/quote_wrapper/qgs/qgs_server.cpp#L143
> 
> it is reading 4 bytes of header, which are interpreted as the length
> of the payload which will then be read off the wire. IIUC the payload
> it expects is the TDREPORT struct.
> 
> Your QEMU patches here meanwhile are just sending the payload from
> the GetQuote hypercall which is the TDREPORT struct.
> 
> IOW, QEMU is not sending the 4 byte length header the QGS expects.
> and whole thing fails.

I'm using the one provided by internal folks, which supports 
interpreting the payload without the header.

I don't know when will the updated implementation show up in public 
github. @Feng Liu can help on it.

>>
>> To avoid no response from QGS server, setup a timer for the transaction.
>> If timeout, make it an error and interrupt guest. Define the threshold of
>> time to 30s at present, maybe change to other value if not appropriate.
>>
>> 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>
>> Codeveloped-by: Xiaoyao Li <xiaoyao.li@intel.com>
>> Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
>> ---
> 
> With regards,
> Daniel
Qiu, Feng Feb. 23, 2024, 1:48 a.m. UTC | #6
Actually the 4 byte length header is provided by client 
library(https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/master/QuoteGeneration/quote_wrapper/tdx_attest/tdx_attest.c#L295), 
not QEMU. QEMUjust treats the how payload including the header a whole blob.
BTW, in the latest stable kernel, the TDX guest driver changed to TSM 
based 
solution(https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/virt/coco/tdx-guest/tdx-guest.c?h=v6.7.5) 
and it will only send raw report without 4 byte length header and other 
stuff. Existing official QGS doesn't compatible with this change and we 
will deliver compatible QGS in the end of Q1.

On 2/23/2024 9:06 AM, Xiaoyao Li wrote:
> + Feng Qiu,
> 
> On 2/23/2024 12:30 AM, Daniel P. Berrangé wrote:
>> On Wed, Jan 24, 2024 at 10:23:12PM -0500, Xiaoyao Li wrote:
>>> From: Isaku Yamahata <isaku.yamahata@intel.com>
>>>
>>> Add property "quote-generation-socket" to tdx-guest, which is a property
>>> of type SocketAddress to specify Quote Generation Service(QGS).
>>>
>>> On request of GetQuote, it connects to the QGS socket, read request
>>> data from shared guest memory, send the request data to the QGS,
>>> and store the response into shared guest memory, at last 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":"1","port":"1234"}}' \
>>>      -machine confidential-guest-support=tdx0
>>>
>>> Note, above example uses vsock type socket because the QGS we used
>>> implements the vsock socket. It can be other types, like UNIX socket,
>>> which depends on the implementation of QGS.
>>
>> Can you confirm again exactly what QGS impl you are testing against ?
>> > I've tried the impl at
>>
>>     
>> https://github.com/intel/SGXDataCenterAttestationPrimitives/tree/master/QuoteGeneration/quote_wrapper/qgs
>>
>> which supports UNIX sockets and VSOCK. In both cases, however, it
>> appears to be speaking a different protocol than your QEMU impl
>> below uses.
>>
>> Specifically here:
>>
>>    
>> https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/master/QuoteGeneration/quote_wrapper/qgs/qgs_server.cpp#L143
>>
>> it is reading 4 bytes of header, which are interpreted as the length
>> of the payload which will then be read off the wire. IIUC the payload
>> it expects is the TDREPORT struct.
>>
>> Your QEMU patches here meanwhile are just sending the payload from
>> the GetQuote hypercall which is the TDREPORT struct.
>>
>> IOW, QEMU is not sending the 4 byte length header the QGS expects.
>> and whole thing fails.
> 
> I'm using the one provided by internal folks, which supports 
> interpreting the payload without the header.
> 
> I don't know when will the updated implementation show up in public 
> github. @Feng Liu can help on it.
> 
>>>
>>> To avoid no response from QGS server, setup a timer for the transaction.
>>> If timeout, make it an error and interrupt guest. Define the 
>>> threshold of
>>> time to 30s at present, maybe change to other value if not appropriate.
>>>
>>> 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>
>>> Codeveloped-by: Xiaoyao Li <xiaoyao.li@intel.com>
>>> Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
>>> ---
>>
>> With regards,
>> Daniel
>
diff mbox series

Patch

diff --git a/qapi/qom.json b/qapi/qom.json
index 15445f9e41fc..c60fb5710961 100644
--- a/qapi/qom.json
+++ b/qapi/qom.json
@@ -914,13 +914,17 @@ 
 #     e.g., specific to the workload rather than the run-time or OS.
 #     base64 encoded SHA384 digest.
 #
+# @quote-generation-socket: socket address for Quote Generation
+#     Service(QGS)
+#
 # Since: 9.0
 ##
 { 'struct': 'TdxGuestProperties',
   'data': { '*sept-ve-disable': 'bool',
             '*mrconfigid': 'str',
             '*mrowner': 'str',
-            '*mrownerconfig': 'str' } }
+            '*mrownerconfig': 'str',
+            '*quote-generation-socket': 'SocketAddress' } }
 
 ##
 # @ThreadContextProperties:
diff --git a/target/i386/kvm/meson.build b/target/i386/kvm/meson.build
index 30a90b4d371d..2fd7c27b76c4 100644
--- a/target/i386/kvm/meson.build
+++ b/target/i386/kvm/meson.build
@@ -9,7 +9,7 @@  i386_kvm_ss.add(when: 'CONFIG_XEN_EMU', if_true: files('xen-emu.c'))
 
 i386_kvm_ss.add(when: 'CONFIG_SEV', if_false: files('sev-stub.c'))
 
-i386_kvm_ss.add(when: 'CONFIG_TDX', if_true: files('tdx.c'), if_false: files('tdx-stub.c'))
+i386_kvm_ss.add(when: 'CONFIG_TDX', if_true: files('tdx.c', 'tdx-quote-generator.c'), if_false: files('tdx-stub.c'))
 
 i386_system_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c'), if_false: files('hyperv-stub.c'))
 
diff --git a/target/i386/kvm/tdx-quote-generator.c b/target/i386/kvm/tdx-quote-generator.c
new file mode 100644
index 000000000000..057ad09e7e95
--- /dev/null
+++ b/target/i386/kvm/tdx-quote-generator.c
@@ -0,0 +1,170 @@ 
+/*
+ * QEMU TDX support
+ *
+ * Copyright Intel
+ *
+ * Author:
+ *      Xiaoyao Li <xiaoyao.li@intel.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "qapi/qapi-visit-sockets.h"
+
+#include "tdx-quote-generator.h"
+
+typedef struct TdxQuoteGeneratorClass {
+    DeviceClass parent_class;
+} TdxQuoteGeneratorClass;
+
+OBJECT_DEFINE_TYPE(TdxQuoteGenerator, tdx_quote_generator, TDX_QUOTE_GENERATOR, OBJECT)
+
+static void tdx_quote_generator_finalize(Object *obj)
+{
+}
+
+static void tdx_quote_generator_class_init(ObjectClass *oc, void *data)
+{
+}
+
+static void tdx_quote_generator_init(Object *obj)
+{
+}
+
+static void tdx_generate_quote_cleanup(struct tdx_generate_quote_task *task)
+{
+    timer_del(&task->timer);
+
+    g_source_remove(task->watch);
+    qio_channel_close(QIO_CHANNEL(task->sioc), NULL);
+    object_unref(OBJECT(task->sioc));
+
+    /* Maintain the number of in-flight requests. */
+    qemu_mutex_lock(&task->quote_gen->lock);
+    task->quote_gen->num--;
+    qemu_mutex_unlock(&task->quote_gen->lock);
+
+    task->completion(task);
+}
+
+static gboolean tdx_get_quote_read(QIOChannel *ioc, GIOCondition condition,
+                                   gpointer opaque)
+{
+    struct tdx_generate_quote_task *task = opaque;
+    Error *err = NULL;
+    int ret;
+
+    ret = qio_channel_read(ioc, task->receive_buf + task->receive_buf_received,
+                           task->payload_len - task->receive_buf_received, &err);
+    if (ret < 0) {
+        if (ret ==  QIO_CHANNEL_ERR_BLOCK) {
+            return G_SOURCE_CONTINUE;
+        } else {
+            error_report_err(err);
+            task->status_code = TDX_VP_GET_QUOTE_ERROR;
+            goto end;
+        }
+    }
+
+    task->receive_buf_received += ret;
+    if (ret == 0 || task->receive_buf_received == task->payload_len) {
+        task->status_code = TDX_VP_GET_QUOTE_SUCCESS;
+        goto end;
+    }
+
+    return G_SOURCE_CONTINUE;
+
+end:
+    tdx_generate_quote_cleanup(task);
+    return G_SOURCE_REMOVE;
+}
+
+static gboolean tdx_send_report(QIOChannel *ioc, GIOCondition condition,
+                                gpointer opaque)
+{
+    struct tdx_generate_quote_task *task = opaque;
+    Error *err = NULL;
+    int ret;
+
+    ret = qio_channel_write(ioc, task->send_data + task->send_data_sent,
+                            task->send_data_size - task->send_data_sent, &err);
+    if (ret < 0) {
+        if (ret == QIO_CHANNEL_ERR_BLOCK) {
+            ret = 0;
+        } else {
+            error_report_err(err);
+            task->status_code = TDX_VP_GET_QUOTE_ERROR;
+            tdx_generate_quote_cleanup(task);
+            goto end;
+        }
+    }
+    task->send_data_sent += ret;
+
+    if (task->send_data_sent == task->send_data_size) {
+        task->watch = qio_channel_add_watch(QIO_CHANNEL(task->sioc), G_IO_IN,
+                                            tdx_get_quote_read, task, NULL);
+        goto end;
+    }
+
+    return G_SOURCE_CONTINUE;
+
+end:
+    return G_SOURCE_REMOVE;
+}
+
+static void tdx_quote_generator_connected(QIOTask *qio_task, gpointer opaque)
+{
+    struct tdx_generate_quote_task *task = opaque;
+    Error *err = NULL;
+    int ret;
+
+    ret = qio_task_propagate_error(qio_task, &err);
+    if (ret) {
+        error_report_err(err);
+        task->status_code = TDX_VP_GET_QUOTE_QGS_UNAVAILABLE;
+        tdx_generate_quote_cleanup(task);
+        return;
+    }
+
+    task->watch = qio_channel_add_watch(QIO_CHANNEL(task->sioc), G_IO_OUT,
+                                        tdx_send_report, task, NULL);
+}
+
+#define TRANSACTION_TIMEOUT 30000
+
+static void getquote_expired(void *opaque)
+{
+    struct tdx_generate_quote_task *task = opaque;
+
+    task->status_code = TDX_VP_GET_QUOTE_ERROR;
+    tdx_generate_quote_cleanup(task);
+}
+
+static void setup_get_quote_timer(struct tdx_generate_quote_task *task)
+{
+    int64_t time;
+
+    timer_init_ms(&task->timer, QEMU_CLOCK_VIRTUAL, getquote_expired, task);
+    time = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);
+    timer_mod(&task->timer, time + TRANSACTION_TIMEOUT);
+}
+
+void tdx_generate_quote(struct tdx_generate_quote_task *task)
+{
+    struct TdxQuoteGenerator *quote_gen = task->quote_gen;
+    QIOChannelSocket *sioc;
+
+    sioc = qio_channel_socket_new();
+    task->sioc = sioc;
+
+    setup_get_quote_timer(task);
+
+    qio_channel_socket_connect_async(sioc, quote_gen->socket,
+                                     tdx_quote_generator_connected, task,
+                                     NULL, NULL);
+}
diff --git a/target/i386/kvm/tdx-quote-generator.h b/target/i386/kvm/tdx-quote-generator.h
new file mode 100644
index 000000000000..54899d44aa6f
--- /dev/null
+++ b/target/i386/kvm/tdx-quote-generator.h
@@ -0,0 +1,95 @@ 
+#ifndef QEMU_I386_TDX_QUOTE_GENERATOR_H
+#define QEMU_I386_TDX_QUOTE_GENERATOR_H
+
+#include "qom/object_interfaces.h"
+#include "io/channel-socket.h"
+#include "exec/hwaddr.h"
+
+/* tdx quote generation */
+struct TdxQuoteGenerator {
+    Object parent_obj;
+
+    int num;
+    SocketAddress *socket;
+
+    QemuMutex lock;
+};
+
+#define TYPE_TDX_QUOTE_GENERATOR "tdx-quote-generator"
+
+OBJECT_DECLARE_SIMPLE_TYPE(TdxQuoteGenerator, TDX_QUOTE_GENERATOR)
+
+
+#define TDX_GET_QUOTE_STRUCTURE_VERSION 1ULL
+
+#define TDX_VP_GET_QUOTE_SUCCESS                0ULL
+#define TDX_VP_GET_QUOTE_IN_FLIGHT              (-1ULL)
+#define TDX_VP_GET_QUOTE_ERROR                  0x8000000000000000ULL
+#define TDX_VP_GET_QUOTE_QGS_UNAVAILABLE        0x8000000000000001ULL
+
+/* Limit to avoid resource starvation. */
+#define TDX_GET_QUOTE_MAX_BUF_LEN       (128 * 1024)
+#define TDX_MAX_GET_QUOTE_REQUEST       16
+
+#define TDX_GET_QUOTE_HDR_SIZE          24
+
+/* Format of pages shared with guest. */
+struct tdx_get_quote_header {
+    /* Format version: must be 1 in little endian. */
+    uint64_t structure_version;
+
+    /*
+     * GetQuote status code in little endian:
+     *   Guest must set error_code to 0 to avoid information leak.
+     *   Qemu sets this before interrupting guest.
+     */
+    uint64_t error_code;
+
+    /*
+     * in-message size in little endian: The message will follow this header.
+     * The in-message will be send to QGS.
+     */
+    uint32_t in_len;
+
+    /*
+     * out-message size in little endian:
+     * On request, out_len must be zero to avoid information leak.
+     * On return, message size from QGS. Qemu overwrites this field.
+     * The message will follows this header.  The in-message is overwritten.
+     */
+    uint32_t out_len;
+
+    /*
+     * Message buffer follows.
+     * Guest sets message that will be send to QGS.  If out_len > in_len, guest
+     * should zero remaining buffer to avoid information leak.
+     * Qemu overwrites this buffer with a message returned from QGS.
+     */
+};
+
+struct tdx_generate_quote_task {
+    hwaddr buf_gpa;
+    hwaddr payload_gpa;
+    uint64_t payload_len;
+
+    char *send_data;
+    uint64_t send_data_size;
+    uint64_t send_data_sent;
+
+    char *receive_buf;
+    uint64_t receive_buf_received;
+
+    uint64_t status_code;
+    struct tdx_get_quote_header hdr;
+
+    QIOChannelSocket *sioc;
+    guint watch;
+    QEMUTimer timer;
+    struct TdxQuoteGenerator *quote_gen;
+
+    void (*completion)(struct tdx_generate_quote_task *task);
+};
+
+void tdx_generate_quote(struct tdx_generate_quote_task *task);
+
+#endif /* QEMU_I386_TDX_QUOTE_GENERATOR_H */
diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c
index 602b5656d462..d27e775eec5d 100644
--- a/target/i386/kvm/tdx.c
+++ b/target/i386/kvm/tdx.c
@@ -16,6 +16,7 @@ 
 #include "qemu/base64.h"
 #include "qemu/mmap-alloc.h"
 #include "qapi/error.h"
+#include "qapi/qapi-visit-sockets.h"
 #include "qom/object_interfaces.h"
 #include "standard-headers/asm-x86/kvm_para.h"
 #include "sysemu/kvm.h"
@@ -23,12 +24,16 @@ 
 #include "exec/address-spaces.h"
 #include "exec/ramblock.h"
 
+#include "hw/i386/apic_internal.h"
+#include "hw/i386/apic-msidef.h"
 #include "hw/i386/e820_memory_layout.h"
 #include "hw/i386/x86.h"
 #include "hw/i386/tdvf.h"
 #include "hw/i386/tdvf-hob.h"
+#include "hw/pci/msi.h"
 #include "kvm_i386.h"
 #include "tdx.h"
+#include "tdx-quote-generator.h"
 #include "../cpu-internal.h"
 
 #define TDX_SUPPORTED_KVM_FEATURES  ((1U << KVM_FEATURE_NOP_IO_DELAY) | \
@@ -852,6 +857,175 @@  int tdx_parse_tdvf(void *flash_ptr, int size)
     return tdvf_parse_metadata(&tdx_guest->tdvf, flash_ptr, size);
 }
 
+static void tdx_inject_interrupt(uint32_t apicid, uint32_t vector)
+{
+    int ret;
+
+    if (vector < 32 || vector > 255) {
+        return;
+    }
+
+    MSIMessage msg = {
+        .address = ((apicid & 0xff) << MSI_ADDR_DEST_ID_SHIFT) |
+                   (((uint64_t)apicid & 0xffffff00) << 32),
+        .data = vector | (APIC_DM_FIXED << MSI_DATA_DELIVERY_MODE_SHIFT),
+    };
+
+    ret = kvm_irqchip_send_msi(kvm_state, msg);
+    if (ret < 0) {
+        /* In this case, no better way to tell it to guest.  Log it. */
+        error_report("TDX: injection %d failed, interrupt lost (%s).\n",
+                     vector, strerror(-ret));
+    }
+}
+
+static hwaddr tdx_shared_bit(X86CPU *cpu)
+{
+    return (cpu->phys_bits > 48) ? BIT_ULL(51) : BIT_ULL(47);
+}
+
+static void tdx_get_quote_completion(struct tdx_generate_quote_task *task)
+{
+    int ret;
+
+    if (task->status_code == TDX_VP_GET_QUOTE_SUCCESS) {
+        ret = address_space_write(&address_space_memory, task->payload_gpa,
+                                  MEMTXATTRS_UNSPECIFIED, task->receive_buf,
+                                  task->receive_buf_received);
+        if (ret != MEMTX_OK) {
+            error_report("TDX: get-quote: failed to write quote data.\n");
+        } else {
+            task->hdr.out_len = cpu_to_le64(task->receive_buf_received);
+        }
+    }
+    task->hdr.error_code = cpu_to_le32(task->status_code);
+
+    /* Publish the response contents before marking this request completed. */
+    smp_wmb();
+    ret = address_space_write(&address_space_memory, task->buf_gpa,
+                              MEMTXATTRS_UNSPECIFIED, &task->hdr,
+                              TDX_GET_QUOTE_HDR_SIZE);
+    if (ret != MEMTX_OK) {
+        error_report("TDX: get-quote: failed to update GetQuote header.");
+    }
+
+    tdx_inject_interrupt(tdx_guest->event_notify_apicid,
+                         tdx_guest->event_notify_vector);
+
+    g_free(task->send_data);
+    g_free(task->receive_buf);
+    g_free(task);
+}
+
+static int tdx_handle_get_quote(X86CPU *cpu, struct kvm_tdx_vmcall *vmcall)
+{
+    struct tdx_generate_quote_task *task;
+    struct tdx_get_quote_header hdr;
+    hwaddr buf_gpa = vmcall->in_r12;
+    uint64_t buf_len = vmcall->in_r13;
+
+    QEMU_BUILD_BUG_ON(sizeof(struct tdx_get_quote_header) != TDX_GET_QUOTE_HDR_SIZE);
+
+    vmcall->status_code = TDG_VP_VMCALL_INVALID_OPERAND;
+
+    if (buf_len == 0) {
+        return 0;
+    }
+
+    /* GPA must be shared. */
+    if (!(buf_gpa & tdx_shared_bit(cpu))) {
+        return 0;
+    }
+    buf_gpa &= ~tdx_shared_bit(cpu);
+
+    if (!QEMU_IS_ALIGNED(buf_gpa, 4096) || !QEMU_IS_ALIGNED(buf_len, 4096)) {
+        vmcall->status_code = TDG_VP_VMCALL_ALIGN_ERROR;
+        return 0;
+    }
+
+    if (address_space_read(&address_space_memory, buf_gpa, MEMTXATTRS_UNSPECIFIED,
+                           &hdr, TDX_GET_QUOTE_HDR_SIZE) != MEMTX_OK) {
+        error_report("TDX: get-quote: failed to read GetQuote header.\n");
+        return -1;
+    }
+
+    if (le64_to_cpu(hdr.structure_version) != TDX_GET_QUOTE_STRUCTURE_VERSION) {
+        return 0;
+    }
+
+    /*
+     * 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 0;
+    }
+
+    /* 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) > buf_len - TDX_GET_QUOTE_HDR_SIZE) {
+        return 0;
+    }
+
+    vmcall->status_code = TDG_VP_VMCALL_SUCCESS;
+    if (!tdx_guest->quote_generator) {
+        hdr.error_code = cpu_to_le64(TDX_VP_GET_QUOTE_QGS_UNAVAILABLE);
+        if (address_space_write(&address_space_memory, buf_gpa,
+                                MEMTXATTRS_UNSPECIFIED,
+                                &hdr, TDX_GET_QUOTE_HDR_SIZE) != MEMTX_OK) {
+            error_report("TDX: failed to update GetQuote header.\n");
+            return -1;
+        }
+        return 0;
+    }
+
+    qemu_mutex_lock(&tdx_guest->quote_generator->lock);
+    if (tdx_guest->quote_generator->num >= TDX_MAX_GET_QUOTE_REQUEST) {
+        qemu_mutex_unlock(&tdx_guest->quote_generator->lock);
+        vmcall->status_code = TDG_VP_VMCALL_RETRY;
+        return 0;
+    }
+    tdx_guest->quote_generator->num++;
+    qemu_mutex_unlock(&tdx_guest->quote_generator->lock);
+
+    /* Mark the buffer in-flight. */
+    hdr.error_code = cpu_to_le64(TDX_VP_GET_QUOTE_IN_FLIGHT);
+    if (address_space_write(&address_space_memory, buf_gpa,
+                            MEMTXATTRS_UNSPECIFIED,
+                            &hdr, TDX_GET_QUOTE_HDR_SIZE) != MEMTX_OK) {
+        error_report("TDX: failed to update GetQuote header.\n");
+        return -1;
+    }
+
+    task = g_malloc(sizeof(*task));
+    task->buf_gpa = buf_gpa;
+    task->payload_gpa = buf_gpa + TDX_GET_QUOTE_HDR_SIZE;
+    task->payload_len = buf_len - TDX_GET_QUOTE_HDR_SIZE;
+    task->hdr = hdr;
+    task->quote_gen = tdx_guest->quote_generator;
+    task->completion = tdx_get_quote_completion;
+
+    task->send_data_size = le32_to_cpu(hdr.in_len);
+    task->send_data = g_malloc(task->send_data_size);
+    task->send_data_sent = 0;
+
+    if (address_space_read(&address_space_memory, task->payload_gpa,
+                           MEMTXATTRS_UNSPECIFIED, task->send_data,
+                           task->send_data_size) != MEMTX_OK) {
+        g_free(task->send_data);
+        return -1;
+    }
+
+    task->receive_buf = g_malloc0(task->payload_len);
+    task->receive_buf_received = 0;
+
+    tdx_generate_quote(task);
+
+    return 0;
+}
+
 static int tdx_handle_setup_event_notify_interrupt(X86CPU *cpu,
                                                    struct kvm_tdx_vmcall *vmcall)
 {
@@ -882,6 +1056,8 @@  static int tdx_handle_vmcall(X86CPU *cpu, struct kvm_tdx_vmcall *vmcall)
     }
 
     switch (vmcall->subfunction) {
+    case TDG_VP_VMCALL_GET_QUOTE:
+        return tdx_handle_get_quote(cpu, vmcall);
     case TDG_VP_VMCALL_SETUP_EVENT_NOTIFY_INTERRUPT:
         return tdx_handle_setup_event_notify_interrupt(cpu, vmcall);
     default:
@@ -978,6 +1154,40 @@  static void tdx_guest_set_mrownerconfig(Object *obj, const char *value, Error **
     tdx->mrownerconfig = g_strdup(value);
 }
 
+static void tdx_guest_get_quote_generation(Object *obj, Visitor *v,
+                                            const char *name, void *opaque,
+                                            Error **errp)
+{
+    TdxGuest *tdx = TDX_GUEST(obj);
+
+    visit_type_SocketAddress(v, name, &tdx->quote_generator->socket, errp);
+}
+
+static void tdx_guest_set_quote_generation(Object *obj, Visitor *v,
+                                           const char *name, void *opaque,
+                                           Error **errp)
+{
+    TdxGuest *tdx = TDX_GUEST(obj);
+    SocketAddress *sock = NULL;
+    Object *qg_obj;
+    TdxQuoteGenerator *quote_generator;
+
+    if (!visit_type_SocketAddress(v, name, &sock, errp)) {
+        return;
+    }
+
+    if (tdx->quote_generator) {
+        object_unref(tdx->quote_generator);
+    }
+
+    qg_obj = object_new(TYPE_TDX_QUOTE_GENERATOR);
+    quote_generator = TDX_QUOTE_GENERATOR(qg_obj);
+    quote_generator->socket = sock;
+    qemu_mutex_init(&quote_generator->lock);
+
+    tdx->quote_generator = quote_generator;
+}
+
 /* tdx guest */
 OBJECT_DEFINE_TYPE_WITH_INTERFACES(TdxGuest,
                                    tdx_guest,
@@ -1012,6 +1222,12 @@  static void tdx_guest_init(Object *obj)
                             tdx_guest_get_mrownerconfig,
                             tdx_guest_set_mrownerconfig);
 
+    tdx->quote_generator = NULL;
+    object_property_add(obj, "quote-generation-socket", "SocketAddress",
+                            tdx_guest_get_quote_generation,
+                            tdx_guest_set_quote_generation,
+                            NULL, NULL);
+
     tdx->event_notify_vector = -1;
     tdx->event_notify_apicid = -1;
 }
diff --git a/target/i386/kvm/tdx.h b/target/i386/kvm/tdx.h
index 992916e4c905..c6e4c275262d 100644
--- a/target/i386/kvm/tdx.h
+++ b/target/i386/kvm/tdx.h
@@ -9,6 +9,8 @@ 
 #include "hw/i386/tdvf.h"
 #include "sysemu/kvm.h"
 
+#include "tdx-quote-generator.h"
+
 #define TYPE_TDX_GUEST "tdx-guest"
 #define TDX_GUEST(obj)  OBJECT_CHECK(TdxGuest, (obj), TYPE_TDX_GUEST)
 
@@ -16,6 +18,7 @@  typedef struct TdxGuestClass {
     ConfidentialGuestSupportClass parent_class;
 } TdxGuestClass;
 
+#define TDG_VP_VMCALL_GET_QUOTE                         0x10002ULL
 #define TDG_VP_VMCALL_SETUP_EVENT_NOTIFY_INTERRUPT      0x10004ULL
 
 #define TDG_VP_VMCALL_SUCCESS           0x0000000000000000ULL
@@ -55,6 +58,9 @@  typedef struct TdxGuest {
     /* runtime state */
     uint32_t event_notify_vector;
     uint32_t event_notify_apicid;
+
+    /* GetQuote */
+    TdxQuoteGenerator *quote_generator;
 } TdxGuest;
 
 #ifdef CONFIG_TDX