diff mbox series

[v2,41/58] i386/tdx: handle TDG.VP.VMCALL<GetQuote>

Message ID 20230818095041.1973309-42-xiaoyao.li@intel.com (mailing list archive)
State New, archived
Headers show
Series TDX QEMU support | expand

Commit Message

Xiaoyao Li Aug. 18, 2023, 9:50 a.m. UTC
From: Isaku Yamahata <isaku.yamahata@intel.com>

For GetQuote, delegate a request to Quote Generation Service.  Add property
of address of quote generation server and On request, connect to the
server, 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.

"quote-generation-service" is a property to specify Quote Generation
Service(QGS) in qemu socket address format.  The examples of the supported
format are "vsock:2:1234", "unix:/run/qgs", "localhost:1234".

command line example:
  qemu-system-x86_64 \
    -object 'tdx-guest,id=tdx0,quote-generation-service=localhost:1234' \
    -machine confidential-guest-support=tdx0

Signed-off-by: Isaku Yamahata <isaku.yamahata@intel.com>
Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
---
 qapi/qom.json         |   5 +-
 target/i386/kvm/tdx.c | 380 ++++++++++++++++++++++++++++++++++++++++++
 target/i386/kvm/tdx.h |   7 +
 3 files changed, 391 insertions(+), 1 deletion(-)

Comments

Daniel P. Berrangé Aug. 22, 2023, 8:24 a.m. UTC | #1
On Tue, Aug 22, 2023 at 08:52:30AM +0200, Markus Armbruster wrote:
> Xiaoyao Li <xiaoyao.li@intel.com> writes:
> 
> > From: Isaku Yamahata <isaku.yamahata@intel.com>
> >
> > For GetQuote, delegate a request to Quote Generation Service.  Add property
> > of address of quote generation server and On request, connect to the
> > server, 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.
> >
> > "quote-generation-service" is a property to specify Quote Generation
> > Service(QGS) in qemu socket address format.  The examples of the supported
> > format are "vsock:2:1234", "unix:/run/qgs", "localhost:1234".
> >
> > command line example:
> >   qemu-system-x86_64 \
> >     -object 'tdx-guest,id=tdx0,quote-generation-service=localhost:1234' \
> >     -machine confidential-guest-support=tdx0
> >
> > Signed-off-by: Isaku Yamahata <isaku.yamahata@intel.com>
> > Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
> > ---
> >  qapi/qom.json         |   5 +-
> >  target/i386/kvm/tdx.c | 380 ++++++++++++++++++++++++++++++++++++++++++
> >  target/i386/kvm/tdx.h |   7 +
> >  3 files changed, 391 insertions(+), 1 deletion(-)
> >
> > diff --git a/qapi/qom.json b/qapi/qom.json
> > index 87c1d440f331..37139949d761 100644
> > --- a/qapi/qom.json
> > +++ b/qapi/qom.json
> > @@ -879,13 +879,16 @@
> >  #
> >  # @mrownerconfig: MROWNERCONFIG SHA384 hex string of 48 * 2 length (default: 0)
> >  #
> > +# @quote-generation-service: socket address for Quote Generation Service(QGS)
> > +#
> >  # Since: 8.2
> >  ##
> >  { 'struct': 'TdxGuestProperties',
> >    'data': { '*sept-ve-disable': 'bool',
> >              '*mrconfigid': 'str',
> >              '*mrowner': 'str',
> > -            '*mrownerconfig': 'str' } }
> > +            '*mrownerconfig': 'str',
> > +            '*quote-generation-service': 'str' } }
> 
> Why not type SocketAddress?

Yes, the code uses SocketAddress internally when it eventually
calls qio_channel_socket_connect_async(), so we should directly
use SocketAddress in the QAPI from the start.

With regards,
Daniel
Chenyi Qiang Aug. 29, 2023, 5:31 a.m. UTC | #2
On 8/22/2023 4:24 PM, Daniel P. Berrangé wrote:
> On Tue, Aug 22, 2023 at 08:52:30AM +0200, Markus Armbruster wrote:
>> Xiaoyao Li <xiaoyao.li@intel.com> writes:
>>
>>> From: Isaku Yamahata <isaku.yamahata@intel.com>
>>>
>>> For GetQuote, delegate a request to Quote Generation Service.  Add property
>>> of address of quote generation server and On request, connect to the
>>> server, 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.
>>>
>>> "quote-generation-service" is a property to specify Quote Generation
>>> Service(QGS) in qemu socket address format.  The examples of the supported
>>> format are "vsock:2:1234", "unix:/run/qgs", "localhost:1234".
>>>
>>> command line example:
>>>   qemu-system-x86_64 \
>>>     -object 'tdx-guest,id=tdx0,quote-generation-service=localhost:1234' \
>>>     -machine confidential-guest-support=tdx0
>>>
>>> Signed-off-by: Isaku Yamahata <isaku.yamahata@intel.com>
>>> Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
>>> ---
>>>  qapi/qom.json         |   5 +-
>>>  target/i386/kvm/tdx.c | 380 ++++++++++++++++++++++++++++++++++++++++++
>>>  target/i386/kvm/tdx.h |   7 +
>>>  3 files changed, 391 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/qapi/qom.json b/qapi/qom.json
>>> index 87c1d440f331..37139949d761 100644
>>> --- a/qapi/qom.json
>>> +++ b/qapi/qom.json
>>> @@ -879,13 +879,16 @@
>>>  #
>>>  # @mrownerconfig: MROWNERCONFIG SHA384 hex string of 48 * 2 length (default: 0)
>>>  #
>>> +# @quote-generation-service: socket address for Quote Generation Service(QGS)
>>> +#
>>>  # Since: 8.2
>>>  ##
>>>  { 'struct': 'TdxGuestProperties',
>>>    'data': { '*sept-ve-disable': 'bool',
>>>              '*mrconfigid': 'str',
>>>              '*mrowner': 'str',
>>> -            '*mrownerconfig': 'str' } }
>>> +            '*mrownerconfig': 'str',
>>> +            '*quote-generation-service': 'str' } }
>>
>> Why not type SocketAddress?
> 
> Yes, the code uses SocketAddress internally when it eventually
> calls qio_channel_socket_connect_async(), so we should directly
> use SocketAddress in the QAPI from the start.

Any benefit to directly use SocketAddress?

"quote-generation-service" here is optional, it seems not trivial to add
and parse the SocketAddress type in QEMU command. After I change 'str'
to 'SocketAddress' and specify the command like "-object
tdx-guest,type=vsock,cid=2,port=1234...", it will report "invalid
parameter cid".

> 
> With regards,
> Daniel
Daniel P. Berrangé Aug. 29, 2023, 10:25 a.m. UTC | #3
On Tue, Aug 29, 2023 at 01:31:37PM +0800, Chenyi Qiang wrote:
> 
> 
> On 8/22/2023 4:24 PM, Daniel P. Berrangé wrote:
> > On Tue, Aug 22, 2023 at 08:52:30AM +0200, Markus Armbruster wrote:
> >> Xiaoyao Li <xiaoyao.li@intel.com> writes:
> >>
> >>> From: Isaku Yamahata <isaku.yamahata@intel.com>
> >>>
> >>> For GetQuote, delegate a request to Quote Generation Service.  Add property
> >>> of address of quote generation server and On request, connect to the
> >>> server, 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.
> >>>
> >>> "quote-generation-service" is a property to specify Quote Generation
> >>> Service(QGS) in qemu socket address format.  The examples of the supported
> >>> format are "vsock:2:1234", "unix:/run/qgs", "localhost:1234".
> >>>
> >>> command line example:
> >>>   qemu-system-x86_64 \
> >>>     -object 'tdx-guest,id=tdx0,quote-generation-service=localhost:1234' \
> >>>     -machine confidential-guest-support=tdx0
> >>>
> >>> Signed-off-by: Isaku Yamahata <isaku.yamahata@intel.com>
> >>> Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
> >>> ---
> >>>  qapi/qom.json         |   5 +-
> >>>  target/i386/kvm/tdx.c | 380 ++++++++++++++++++++++++++++++++++++++++++
> >>>  target/i386/kvm/tdx.h |   7 +
> >>>  3 files changed, 391 insertions(+), 1 deletion(-)
> >>>
> >>> diff --git a/qapi/qom.json b/qapi/qom.json
> >>> index 87c1d440f331..37139949d761 100644
> >>> --- a/qapi/qom.json
> >>> +++ b/qapi/qom.json
> >>> @@ -879,13 +879,16 @@
> >>>  #
> >>>  # @mrownerconfig: MROWNERCONFIG SHA384 hex string of 48 * 2 length (default: 0)
> >>>  #
> >>> +# @quote-generation-service: socket address for Quote Generation Service(QGS)
> >>> +#
> >>>  # Since: 8.2
> >>>  ##
> >>>  { 'struct': 'TdxGuestProperties',
> >>>    'data': { '*sept-ve-disable': 'bool',
> >>>              '*mrconfigid': 'str',
> >>>              '*mrowner': 'str',
> >>> -            '*mrownerconfig': 'str' } }
> >>> +            '*mrownerconfig': 'str',
> >>> +            '*quote-generation-service': 'str' } }
> >>
> >> Why not type SocketAddress?
> > 
> > Yes, the code uses SocketAddress internally when it eventually
> > calls qio_channel_socket_connect_async(), so we should directly
> > use SocketAddress in the QAPI from the start.
> 
> Any benefit to directly use SocketAddress?

We don't want whatever code consumes the configuration to
do a second level of parsing to convert the 'str' value
into the 'SocketAddress' object it actually needs.

QEMU has a long history of having a second round of ad-hoc
parsing of configuration and we've found it to be a serious
maintenence burden. Thus we strive to have everything
represented in QAPI using the desired final type, and avoid
the second round of parsing.

> "quote-generation-service" here is optional, it seems not trivial to add
> and parse the SocketAddress type in QEMU command. After I change 'str'
> to 'SocketAddress' and specify the command like "-object
> tdx-guest,type=vsock,cid=2,port=1234...", it will report "invalid
> parameter cid".

The -object parameter supports JSON syntax for this reason

   -object '{"qom-type":"tdx-guest","quote-generation-service":{"type": "vsock", "cid":"2","port":"1234"}}'

libvirt will always use the JSON syntax for -object with a new enough
QEMU.

With regards,
Daniel
Chenyi Qiang Aug. 30, 2023, 5:18 a.m. UTC | #4
On 8/29/2023 6:25 PM, Daniel P. Berrangé wrote:
> On Tue, Aug 29, 2023 at 01:31:37PM +0800, Chenyi Qiang wrote:
>>
>>
>> On 8/22/2023 4:24 PM, Daniel P. Berrangé wrote:
>>> On Tue, Aug 22, 2023 at 08:52:30AM +0200, Markus Armbruster wrote:
>>>> Xiaoyao Li <xiaoyao.li@intel.com> writes:
>>>>
>>>>> From: Isaku Yamahata <isaku.yamahata@intel.com>
>>>>>
>>>>> For GetQuote, delegate a request to Quote Generation Service.  Add property
>>>>> of address of quote generation server and On request, connect to the
>>>>> server, 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.
>>>>>
>>>>> "quote-generation-service" is a property to specify Quote Generation
>>>>> Service(QGS) in qemu socket address format.  The examples of the supported
>>>>> format are "vsock:2:1234", "unix:/run/qgs", "localhost:1234".
>>>>>
>>>>> command line example:
>>>>>   qemu-system-x86_64 \
>>>>>     -object 'tdx-guest,id=tdx0,quote-generation-service=localhost:1234' \
>>>>>     -machine confidential-guest-support=tdx0
>>>>>
>>>>> Signed-off-by: Isaku Yamahata <isaku.yamahata@intel.com>
>>>>> Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
>>>>> ---
>>>>>  qapi/qom.json         |   5 +-
>>>>>  target/i386/kvm/tdx.c | 380 ++++++++++++++++++++++++++++++++++++++++++
>>>>>  target/i386/kvm/tdx.h |   7 +
>>>>>  3 files changed, 391 insertions(+), 1 deletion(-)
>>>>>
>>>>> diff --git a/qapi/qom.json b/qapi/qom.json
>>>>> index 87c1d440f331..37139949d761 100644
>>>>> --- a/qapi/qom.json
>>>>> +++ b/qapi/qom.json
>>>>> @@ -879,13 +879,16 @@
>>>>>  #
>>>>>  # @mrownerconfig: MROWNERCONFIG SHA384 hex string of 48 * 2 length (default: 0)
>>>>>  #
>>>>> +# @quote-generation-service: socket address for Quote Generation Service(QGS)
>>>>> +#
>>>>>  # Since: 8.2
>>>>>  ##
>>>>>  { 'struct': 'TdxGuestProperties',
>>>>>    'data': { '*sept-ve-disable': 'bool',
>>>>>              '*mrconfigid': 'str',
>>>>>              '*mrowner': 'str',
>>>>> -            '*mrownerconfig': 'str' } }
>>>>> +            '*mrownerconfig': 'str',
>>>>> +            '*quote-generation-service': 'str' } }
>>>>
>>>> Why not type SocketAddress?
>>>
>>> Yes, the code uses SocketAddress internally when it eventually
>>> calls qio_channel_socket_connect_async(), so we should directly
>>> use SocketAddress in the QAPI from the start.
>>
>> Any benefit to directly use SocketAddress?
> 
> We don't want whatever code consumes the configuration to
> do a second level of parsing to convert the 'str' value
> into the 'SocketAddress' object it actually needs.
> 
> QEMU has a long history of having a second round of ad-hoc
> parsing of configuration and we've found it to be a serious
> maintenence burden. Thus we strive to have everything
> represented in QAPI using the desired final type, and avoid
> the second round of parsing.

Thanks for your explanation.

> 
>> "quote-generation-service" here is optional, it seems not trivial to add
>> and parse the SocketAddress type in QEMU command. After I change 'str'
>> to 'SocketAddress' and specify the command like "-object
>> tdx-guest,type=vsock,cid=2,port=1234...", it will report "invalid
>> parameter cid".
> 
> The -object parameter supports JSON syntax for this reason
> 
>    -object '{"qom-type":"tdx-guest","quote-generation-service":{"type": "vsock", "cid":"2","port":"1234"}}'
> 
> libvirt will always use the JSON syntax for -object with a new enough
> QEMU.

The JSON syntax works for me. Then, we need to add some doc about using
JSON syntax when quote-generation-service is required.

> 
> With regards,
> Daniel
Xiaoyao Li Aug. 30, 2023, 5:57 a.m. UTC | #5
On 8/30/2023 1:18 PM, Chenyi Qiang wrote:
> 
> 
> On 8/29/2023 6:25 PM, Daniel P. Berrangé wrote:
>> On Tue, Aug 29, 2023 at 01:31:37PM +0800, Chenyi Qiang wrote:
>>>
>>>
>>> On 8/22/2023 4:24 PM, Daniel P. Berrangé wrote:
>>>> On Tue, Aug 22, 2023 at 08:52:30AM +0200, Markus Armbruster wrote:
>>>>> Xiaoyao Li <xiaoyao.li@intel.com> writes:
>>>>>
>>>>>> From: Isaku Yamahata <isaku.yamahata@intel.com>
>>>>>>
>>>>>> For GetQuote, delegate a request to Quote Generation Service.  Add property
>>>>>> of address of quote generation server and On request, connect to the
>>>>>> server, 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.
>>>>>>
>>>>>> "quote-generation-service" is a property to specify Quote Generation
>>>>>> Service(QGS) in qemu socket address format.  The examples of the supported
>>>>>> format are "vsock:2:1234", "unix:/run/qgs", "localhost:1234".
>>>>>>
>>>>>> command line example:
>>>>>>    qemu-system-x86_64 \
>>>>>>      -object 'tdx-guest,id=tdx0,quote-generation-service=localhost:1234' \
>>>>>>      -machine confidential-guest-support=tdx0
>>>>>>
>>>>>> Signed-off-by: Isaku Yamahata <isaku.yamahata@intel.com>
>>>>>> Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
>>>>>> ---
>>>>>>   qapi/qom.json         |   5 +-
>>>>>>   target/i386/kvm/tdx.c | 380 ++++++++++++++++++++++++++++++++++++++++++
>>>>>>   target/i386/kvm/tdx.h |   7 +
>>>>>>   3 files changed, 391 insertions(+), 1 deletion(-)
>>>>>>
>>>>>> diff --git a/qapi/qom.json b/qapi/qom.json
>>>>>> index 87c1d440f331..37139949d761 100644
>>>>>> --- a/qapi/qom.json
>>>>>> +++ b/qapi/qom.json
>>>>>> @@ -879,13 +879,16 @@
>>>>>>   #
>>>>>>   # @mrownerconfig: MROWNERCONFIG SHA384 hex string of 48 * 2 length (default: 0)
>>>>>>   #
>>>>>> +# @quote-generation-service: socket address for Quote Generation Service(QGS)
>>>>>> +#
>>>>>>   # Since: 8.2
>>>>>>   ##
>>>>>>   { 'struct': 'TdxGuestProperties',
>>>>>>     'data': { '*sept-ve-disable': 'bool',
>>>>>>               '*mrconfigid': 'str',
>>>>>>               '*mrowner': 'str',
>>>>>> -            '*mrownerconfig': 'str' } }
>>>>>> +            '*mrownerconfig': 'str',
>>>>>> +            '*quote-generation-service': 'str' } }
>>>>>
>>>>> Why not type SocketAddress?
>>>>
>>>> Yes, the code uses SocketAddress internally when it eventually
>>>> calls qio_channel_socket_connect_async(), so we should directly
>>>> use SocketAddress in the QAPI from the start.
>>>
>>> Any benefit to directly use SocketAddress?
>>
>> We don't want whatever code consumes the configuration to
>> do a second level of parsing to convert the 'str' value
>> into the 'SocketAddress' object it actually needs.
>>
>> QEMU has a long history of having a second round of ad-hoc
>> parsing of configuration and we've found it to be a serious
>> maintenence burden. Thus we strive to have everything
>> represented in QAPI using the desired final type, and avoid
>> the second round of parsing.
> 
> Thanks for your explanation.
> 
>>
>>> "quote-generation-service" here is optional, it seems not trivial to add
>>> and parse the SocketAddress type in QEMU command. After I change 'str'
>>> to 'SocketAddress' and specify the command like "-object
>>> tdx-guest,type=vsock,cid=2,port=1234...", it will report "invalid
>>> parameter cid".
>>
>> The -object parameter supports JSON syntax for this reason
>>
>>     -object '{"qom-type":"tdx-guest","quote-generation-service":{"type": "vsock", "cid":"2","port":"1234"}}'
>>
>> libvirt will always use the JSON syntax for -object with a new enough
>> QEMU.
> 
> The JSON syntax works for me. Then, we need to add some doc about using
> JSON syntax when quote-generation-service is required.

This limitation doesn't look reasonable to me.

@Daniel,

Is it acceptable by QEMU community?

>>
>> With regards,
>> Daniel
Daniel P. Berrangé Aug. 30, 2023, 7:48 a.m. UTC | #6
On Wed, Aug 30, 2023 at 01:57:59PM +0800, Xiaoyao Li wrote:
> On 8/30/2023 1:18 PM, Chenyi Qiang wrote:
> > 
> > 
> > On 8/29/2023 6:25 PM, Daniel P. Berrangé wrote:
> > > On Tue, Aug 29, 2023 at 01:31:37PM +0800, Chenyi Qiang wrote:
> > > > 
> > > > 
> > > > On 8/22/2023 4:24 PM, Daniel P. Berrangé wrote:
> > > > > On Tue, Aug 22, 2023 at 08:52:30AM +0200, Markus Armbruster wrote:
> > > > > > Xiaoyao Li <xiaoyao.li@intel.com> writes:
> > > > > > 
> > > > > > > From: Isaku Yamahata <isaku.yamahata@intel.com>
> > > > > > > 
> > > > > > > For GetQuote, delegate a request to Quote Generation Service.  Add property
> > > > > > > of address of quote generation server and On request, connect to the
> > > > > > > server, 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.
> > > > > > > 
> > > > > > > "quote-generation-service" is a property to specify Quote Generation
> > > > > > > Service(QGS) in qemu socket address format.  The examples of the supported
> > > > > > > format are "vsock:2:1234", "unix:/run/qgs", "localhost:1234".
> > > > > > > 
> > > > > > > command line example:
> > > > > > >    qemu-system-x86_64 \
> > > > > > >      -object 'tdx-guest,id=tdx0,quote-generation-service=localhost:1234' \
> > > > > > >      -machine confidential-guest-support=tdx0
> > > > > > > 
> > > > > > > Signed-off-by: Isaku Yamahata <isaku.yamahata@intel.com>
> > > > > > > Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
> > > > > > > ---
> > > > > > >   qapi/qom.json         |   5 +-
> > > > > > >   target/i386/kvm/tdx.c | 380 ++++++++++++++++++++++++++++++++++++++++++
> > > > > > >   target/i386/kvm/tdx.h |   7 +
> > > > > > >   3 files changed, 391 insertions(+), 1 deletion(-)
> > > > > > > 
> > > > > > > diff --git a/qapi/qom.json b/qapi/qom.json
> > > > > > > index 87c1d440f331..37139949d761 100644
> > > > > > > --- a/qapi/qom.json
> > > > > > > +++ b/qapi/qom.json
> > > > > > > @@ -879,13 +879,16 @@
> > > > > > >   #
> > > > > > >   # @mrownerconfig: MROWNERCONFIG SHA384 hex string of 48 * 2 length (default: 0)
> > > > > > >   #
> > > > > > > +# @quote-generation-service: socket address for Quote Generation Service(QGS)
> > > > > > > +#
> > > > > > >   # Since: 8.2
> > > > > > >   ##
> > > > > > >   { 'struct': 'TdxGuestProperties',
> > > > > > >     'data': { '*sept-ve-disable': 'bool',
> > > > > > >               '*mrconfigid': 'str',
> > > > > > >               '*mrowner': 'str',
> > > > > > > -            '*mrownerconfig': 'str' } }
> > > > > > > +            '*mrownerconfig': 'str',
> > > > > > > +            '*quote-generation-service': 'str' } }
> > > > > > 
> > > > > > Why not type SocketAddress?
> > > > > 
> > > > > Yes, the code uses SocketAddress internally when it eventually
> > > > > calls qio_channel_socket_connect_async(), so we should directly
> > > > > use SocketAddress in the QAPI from the start.
> > > > 
> > > > Any benefit to directly use SocketAddress?
> > > 
> > > We don't want whatever code consumes the configuration to
> > > do a second level of parsing to convert the 'str' value
> > > into the 'SocketAddress' object it actually needs.
> > > 
> > > QEMU has a long history of having a second round of ad-hoc
> > > parsing of configuration and we've found it to be a serious
> > > maintenence burden. Thus we strive to have everything
> > > represented in QAPI using the desired final type, and avoid
> > > the second round of parsing.
> > 
> > Thanks for your explanation.
> > 
> > > 
> > > > "quote-generation-service" here is optional, it seems not trivial to add
> > > > and parse the SocketAddress type in QEMU command. After I change 'str'
> > > > to 'SocketAddress' and specify the command like "-object
> > > > tdx-guest,type=vsock,cid=2,port=1234...", it will report "invalid
> > > > parameter cid".
> > > 
> > > The -object parameter supports JSON syntax for this reason
> > > 
> > >     -object '{"qom-type":"tdx-guest","quote-generation-service":{"type": "vsock", "cid":"2","port":"1234"}}'
> > > 
> > > libvirt will always use the JSON syntax for -object with a new enough
> > > QEMU.
> > 
> > The JSON syntax works for me. Then, we need to add some doc about using
> > JSON syntax when quote-generation-service is required.
> 
> This limitation doesn't look reasonable to me.
> 
> @Daniel,
> 
> Is it acceptable by QEMU community?

This is the expected approach for object types which have non-scalar
properties.

With regards,
Daniel
Xiaoyao Li Aug. 31, 2023, 6:49 a.m. UTC | #7
On 8/30/2023 3:48 PM, Daniel P. Berrangé wrote:
> On Wed, Aug 30, 2023 at 01:57:59PM +0800, Xiaoyao Li wrote:
>> On 8/30/2023 1:18 PM, Chenyi Qiang wrote:
>>>
>>>
>>> On 8/29/2023 6:25 PM, Daniel P. Berrangé wrote:
>>>> On Tue, Aug 29, 2023 at 01:31:37PM +0800, Chenyi Qiang wrote:
>>>>>
>>>>>
>>>>> On 8/22/2023 4:24 PM, Daniel P. Berrangé wrote:
>>>>>> On Tue, Aug 22, 2023 at 08:52:30AM +0200, Markus Armbruster wrote:
>>>>>>> Xiaoyao Li <xiaoyao.li@intel.com> writes:
>>>>>>>
>>>>>>>> From: Isaku Yamahata <isaku.yamahata@intel.com>
>>>>>>>>
>>>>>>>> For GetQuote, delegate a request to Quote Generation Service.  Add property
>>>>>>>> of address of quote generation server and On request, connect to the
>>>>>>>> server, 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.
>>>>>>>>
>>>>>>>> "quote-generation-service" is a property to specify Quote Generation
>>>>>>>> Service(QGS) in qemu socket address format.  The examples of the supported
>>>>>>>> format are "vsock:2:1234", "unix:/run/qgs", "localhost:1234".
>>>>>>>>
>>>>>>>> command line example:
>>>>>>>>     qemu-system-x86_64 \
>>>>>>>>       -object 'tdx-guest,id=tdx0,quote-generation-service=localhost:1234' \
>>>>>>>>       -machine confidential-guest-support=tdx0
>>>>>>>>
>>>>>>>> Signed-off-by: Isaku Yamahata <isaku.yamahata@intel.com>
>>>>>>>> Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
>>>>>>>> ---
>>>>>>>>    qapi/qom.json         |   5 +-
>>>>>>>>    target/i386/kvm/tdx.c | 380 ++++++++++++++++++++++++++++++++++++++++++
>>>>>>>>    target/i386/kvm/tdx.h |   7 +
>>>>>>>>    3 files changed, 391 insertions(+), 1 deletion(-)
>>>>>>>>
>>>>>>>> diff --git a/qapi/qom.json b/qapi/qom.json
>>>>>>>> index 87c1d440f331..37139949d761 100644
>>>>>>>> --- a/qapi/qom.json
>>>>>>>> +++ b/qapi/qom.json
>>>>>>>> @@ -879,13 +879,16 @@
>>>>>>>>    #
>>>>>>>>    # @mrownerconfig: MROWNERCONFIG SHA384 hex string of 48 * 2 length (default: 0)
>>>>>>>>    #
>>>>>>>> +# @quote-generation-service: socket address for Quote Generation Service(QGS)
>>>>>>>> +#
>>>>>>>>    # Since: 8.2
>>>>>>>>    ##
>>>>>>>>    { 'struct': 'TdxGuestProperties',
>>>>>>>>      'data': { '*sept-ve-disable': 'bool',
>>>>>>>>                '*mrconfigid': 'str',
>>>>>>>>                '*mrowner': 'str',
>>>>>>>> -            '*mrownerconfig': 'str' } }
>>>>>>>> +            '*mrownerconfig': 'str',
>>>>>>>> +            '*quote-generation-service': 'str' } }
>>>>>>>
>>>>>>> Why not type SocketAddress?
>>>>>>
>>>>>> Yes, the code uses SocketAddress internally when it eventually
>>>>>> calls qio_channel_socket_connect_async(), so we should directly
>>>>>> use SocketAddress in the QAPI from the start.
>>>>>
>>>>> Any benefit to directly use SocketAddress?
>>>>
>>>> We don't want whatever code consumes the configuration to
>>>> do a second level of parsing to convert the 'str' value
>>>> into the 'SocketAddress' object it actually needs.
>>>>
>>>> QEMU has a long history of having a second round of ad-hoc
>>>> parsing of configuration and we've found it to be a serious
>>>> maintenence burden. Thus we strive to have everything
>>>> represented in QAPI using the desired final type, and avoid
>>>> the second round of parsing.
>>>
>>> Thanks for your explanation.
>>>
>>>>
>>>>> "quote-generation-service" here is optional, it seems not trivial to add
>>>>> and parse the SocketAddress type in QEMU command. After I change 'str'
>>>>> to 'SocketAddress' and specify the command like "-object
>>>>> tdx-guest,type=vsock,cid=2,port=1234...", it will report "invalid
>>>>> parameter cid".
>>>>
>>>> The -object parameter supports JSON syntax for this reason
>>>>
>>>>      -object '{"qom-type":"tdx-guest","quote-generation-service":{"type": "vsock", "cid":"2","port":"1234"}}'
>>>>
>>>> libvirt will always use the JSON syntax for -object with a new enough
>>>> QEMU.
>>>
>>> The JSON syntax works for me. Then, we need to add some doc about using
>>> JSON syntax when quote-generation-service is required.
>>
>> This limitation doesn't look reasonable to me.
>>
>> @Daniel,
>>
>> Is it acceptable by QEMU community?
> 
> This is the expected approach for object types which have non-scalar
> properties.

Learned it.

thanks!

> With regards,
> Daniel
diff mbox series

Patch

diff --git a/qapi/qom.json b/qapi/qom.json
index 87c1d440f331..37139949d761 100644
--- a/qapi/qom.json
+++ b/qapi/qom.json
@@ -879,13 +879,16 @@ 
 #
 # @mrownerconfig: MROWNERCONFIG SHA384 hex string of 48 * 2 length (default: 0)
 #
+# @quote-generation-service: socket address for Quote Generation Service(QGS)
+#
 # Since: 8.2
 ##
 { 'struct': 'TdxGuestProperties',
   'data': { '*sept-ve-disable': 'bool',
             '*mrconfigid': 'str',
             '*mrowner': 'str',
-            '*mrownerconfig': 'str' } }
+            '*mrownerconfig': 'str',
+            '*quote-generation-service': 'str' } }
 
 ##
 # @ThreadContextProperties:
diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c
index 1b444886e294..73d6cd88af9e 100644
--- a/target/i386/kvm/tdx.c
+++ b/target/i386/kvm/tdx.c
@@ -22,6 +22,8 @@ 
 #include "exec/address-spaces.h"
 #include "exec/ramblock.h"
 
+#include "exec/address-spaces.h"
+#include "hw/i386/apic_internal.h"
 #include "hw/i386/e820_memory_layout.h"
 #include "hw/i386/x86.h"
 #include "hw/i386/tdvf.h"
@@ -863,6 +865,25 @@  static void tdx_guest_set_sept_ve_disable(Object *obj, bool value, Error **errp)
     }
 }
 
+static char *tdx_guest_get_quote_generation(
+    Object *obj, Error **errp)
+{
+    TdxGuest *tdx = TDX_GUEST(obj);
+    return g_strdup(tdx->quote_generation_str);
+}
+
+static void tdx_guest_set_quote_generation(
+    Object *obj, const char *value, Error **errp)
+{
+    TdxGuest *tdx = TDX_GUEST(obj);
+    tdx->quote_generation = socket_parse(value, errp);
+    if (!tdx->quote_generation)
+        return;
+
+    g_free(tdx->quote_generation_str);
+    tdx->quote_generation_str = g_strdup(value);
+}
+
 /* tdx guest */
 OBJECT_DEFINE_TYPE_WITH_INTERFACES(TdxGuest,
                                    tdx_guest,
@@ -895,6 +916,12 @@  static void tdx_guest_init(Object *obj)
     object_property_add_sha384(obj, "mrownerconfig", tdx->mrownerconfig,
                                OBJ_PROP_FLAG_READWRITE);
 
+    tdx->quote_generation_str = NULL;
+    tdx->quote_generation = NULL;
+    object_property_add_str(obj, "quote-generation-service",
+                            tdx_guest_get_quote_generation,
+                            tdx_guest_set_quote_generation);
+
     tdx->event_notify_interrupt = -1;
     tdx->event_notify_apic_id = -1;
 }
@@ -907,6 +934,7 @@  static void tdx_guest_class_init(ObjectClass *oc, void *data)
 {
 }
 
+#define TDG_VP_VMCALL_GET_QUOTE                         0x10002ULL
 #define TDG_VP_VMCALL_SETUP_EVENT_NOTIFY_INTERRUPT      0x10004ULL
 
 #define TDG_VP_VMCALL_SUCCESS           0x0000000000000000ULL
@@ -915,6 +943,355 @@  static void tdx_guest_class_init(ObjectClass *oc, void *data)
 #define TDG_VP_VMCALL_GPA_INUSE         0x8000000000000001ULL
 #define TDG_VP_VMCALL_ALIGN_ERROR       0x8000000000000002ULL
 
+#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
+
+/* 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.
+     */
+};
+
+static hwaddr tdx_shared_bit(X86CPU *cpu)
+{
+    return (cpu->phys_bits > 48) ? BIT_ULL(51) : BIT_ULL(47);
+}
+
+struct tdx_get_quote_task {
+    uint32_t apic_id;
+    hwaddr gpa;
+    uint64_t buf_len;
+    struct tdx_get_quote_header hdr;
+    int event_notify_interrupt;
+    QIOChannelSocket *ioc;
+};
+
+struct x86_msi {
+    union {
+        struct {
+            uint32_t    reserved_0              : 2,
+                        dest_mode_logical       : 1,
+                        redirect_hint           : 1,
+                        reserved_1              : 1,
+                        virt_destid_8_14        : 7,
+                        destid_0_7              : 8,
+                        base_address            : 12;
+        } QEMU_PACKED x86_address_lo;
+        uint32_t address_lo;
+    };
+    union {
+        struct {
+            uint32_t    reserved        : 8,
+                        destid_8_31     : 24;
+        } QEMU_PACKED x86_address_hi;
+        uint32_t address_hi;
+    };
+    union {
+        struct {
+            uint32_t    vector                  : 8,
+                        delivery_mode           : 3,
+                        dest_mode_logical       : 1,
+                        reserved                : 2,
+                        active_low              : 1,
+                        is_level                : 1;
+        } QEMU_PACKED x86_data;
+        uint32_t data;
+    };
+};
+
+static void tdx_td_notify(struct tdx_get_quote_task *t)
+{
+    struct x86_msi x86_msi;
+    struct kvm_msi msi;
+    int ret;
+
+    /* It is optional for host VMM to interrupt TD. */
+    if(!(32 <= t->event_notify_interrupt && t->event_notify_interrupt <= 255))
+        return;
+
+    x86_msi = (struct x86_msi) {
+        .x86_address_lo  = {
+            .reserved_0 = 0,
+            .dest_mode_logical = 0,
+            .redirect_hint = 0,
+            .reserved_1 = 0,
+            .virt_destid_8_14 = 0,
+            .destid_0_7 = t->apic_id & 0xff,
+        },
+        .x86_address_hi = {
+            .reserved = 0,
+            .destid_8_31 = t->apic_id >> 8,
+        },
+        .x86_data = {
+            .vector = t->event_notify_interrupt,
+            .delivery_mode = APIC_DM_FIXED,
+            .dest_mode_logical = 0,
+            .reserved = 0,
+            .active_low = 0,
+            .is_level = 0,
+        },
+    };
+    msi = (struct kvm_msi) {
+        .address_lo = x86_msi.address_lo,
+        .address_hi = x86_msi.address_hi,
+        .data = x86_msi.data,
+        .flags = 0,
+        .devid = 0,
+    };
+    ret = kvm_vm_ioctl(kvm_state, KVM_SIGNAL_MSI, &msi);
+    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",
+                     t->event_notify_interrupt, strerror(-ret));
+    }
+}
+
+/*
+ * TODO: If QGS doesn't reply for long time, make it an error and interrupt
+ * guest.
+ */
+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;
+    char *out_data = NULL;
+    size_t out_len;
+    ssize_t size;
+    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 (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;
+    }
+
+    if (qio_channel_write_all(QIO_CHANNEL(t->ioc), in_data,
+                              le32_to_cpu(t->hdr.in_len), &err) ||
+        err) {
+        t->hdr.error_code = cpu_to_le64(TDX_VP_GET_QUOTE_QGS_UNAVAILABLE);
+        goto error;
+    }
+
+    out_data = g_malloc(t->buf_len);
+    out_len = 0;
+    size = 0;
+    while (true) {
+        char *buf;
+        size_t buf_size;
+
+        if (out_len < t->buf_len) {
+            buf = out_data + out_len;
+            buf_size = t->buf_len - out_len;
+        } else {
+            /*
+             * The received data is too large to fit in the shared GPA.
+             * Discard the received data and try to know the data size.
+             */
+            buf = out_data;
+            buf_size = t->buf_len;
+        }
+
+        size = qio_channel_read(QIO_CHANNEL(t->ioc), buf, buf_size, &err);
+        if (err) {
+            break;
+        }
+        if (size <= 0) {
+            break;
+        }
+        out_len += size;
+    }
+    /*
+     * Treat partial read as success and let the QGS client to handle it because
+     * the client knows better about the QGS.
+     */
+    if (out_len == 0 && (err || size < 0)) {
+        t->hdr.error_code = cpu_to_le64(TDX_VP_GET_QUOTE_QGS_UNAVAILABLE);
+        goto error;
+    }
+    if (out_len > 0 && out_len > t->buf_len) {
+        /*
+         * There is no specific error code defined for this case(E2BIG) at the
+         * moment.
+         * TODO: Once an error code for this case is defined in GHCI spec ,
+         * update the error code.
+         */
+        t->hdr.error_code = cpu_to_le64(TDX_VP_GET_QUOTE_ERROR);
+        t->hdr.out_len = cpu_to_le32(out_len);
+        goto error_hdr;
+    }
+
+    if (address_space_write(
+            &address_space_memory, t->gpa + sizeof(t->hdr),
+            MEMTXATTRS_UNSPECIFIED, out_data, out_len) != MEMTX_OK) {
+        goto error;
+    }
+    /*
+     * Even if out_len == 0, it's a success.  It's up to the QGS-client contract
+     * how to interpret the zero-sized message as return message.
+     */
+    t->hdr.out_len = cpu_to_le32(out_len);
+    t->hdr.error_code = cpu_to_le64(TDX_VP_GET_QUOTE_SUCCESS);
+
+error:
+    if (t->hdr.error_code != cpu_to_le64(TDX_VP_GET_QUOTE_SUCCESS)) {
+        t->hdr.out_len = cpu_to_le32(0);
+    }
+error_hdr:
+    if (address_space_write(
+            &address_space_memory, t->gpa,
+            MEMTXATTRS_UNSPECIFIED, &t->hdr, sizeof(t->hdr)) != MEMTX_OK) {
+        error_report("TDX: failed to updsate GetQuote header.\n");
+    }
+    tdx_td_notify(t);
+
+    qio_channel_close(QIO_CHANNEL(t->ioc), &err);
+    object_unref(OBJECT(t->ioc));
+    g_free(in_data);
+    g_free(out_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->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);
+        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, g_free,
+        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)
 {
@@ -943,6 +1320,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 50a151fc79c2..d861d8516668 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,11 @@  typedef struct TdxGuest {
     /* runtime state */
     int event_notify_interrupt;
     uint32_t event_notify_apic_id;
+
+    /* GetQuote */
+    int quote_generation_num;
+    char *quote_generation_str;
+    SocketAddress *quote_generation;
 } TdxGuest;
 
 #ifdef CONFIG_TDX