diff mbox series

[v6,2/2] tpm: add backend for mssim

Message ID 20230109161532.6892-3-jejb@linux.ibm.com
State New
Headers show
Series tpm: add mssim backend | expand

Commit Message

James Bottomley Jan. 9, 2023, 4:15 p.m. UTC
From: James Bottomley <James.Bottomley@HansenPartnership.com>

The Microsoft Simulator (mssim) is the reference emulation platform
for the TCG TPM 2.0 specification.

https://github.com/Microsoft/ms-tpm-20-ref.git

It exports a fairly simple network socket based protocol on two
sockets, one for command (default 2321) and one for control (default
2322).  This patch adds a simple backend that can speak the mssim
protocol over the network.  It also allows the two sockets to be
specified on the command line.  The benefits are twofold: firstly it
gives us a backend that actually speaks a standard TPM emulation
protocol instead of the linux specific TPM driver format of the
current emulated TPM backend and secondly, using the microsoft
protocol, the end point of the emulator can be anywhere on the
network, facilitating the cloud use case where a central TPM service
can be used over a control network.

The implementation does basic control commands like power off/on, but
doesn't implement cancellation or startup.  The former because
cancellation is pretty much useless on a fast operating TPM emulator
and the latter because this emulator is designed to be used with OVMF
which itself does TPM startup and I wanted to validate that.

To run this, simply download an emulator based on the MS specification
(package ibmswtpm2 on openSUSE) and run it, then add these two lines
to the qemu command and it will use the emulator.

    -tpmdev mssim,id=tpm0 \
    -device tpm-crb,tpmdev=tpm0 \

to use a remote emulator replace the first line with

    -tpmdev "{'type':'mssim','id':'tpm0','command':{'type':inet,'host':'remote','port':'2321'}}"

tpm-tis also works as the backend.

Signed-off-by: James Bottomley <jejb@linux.ibm.com>

---

v2: convert to SocketAddr json and use qio_channel_socket_connect_sync()
v3: gate control power off by migration state keep control socket disconnected
    to test outside influence and add docs.
---
 MAINTAINERS              |   6 +
 backends/tpm/Kconfig     |   5 +
 backends/tpm/meson.build |   1 +
 backends/tpm/tpm_mssim.c | 290 +++++++++++++++++++++++++++++++++++++++
 backends/tpm/tpm_mssim.h |  44 ++++++
 docs/specs/tpm.rst       |  35 +++++
 monitor/hmp-cmds.c       |   9 ++
 qapi/tpm.json            |  28 +++-
 8 files changed, 414 insertions(+), 4 deletions(-)
 create mode 100644 backends/tpm/tpm_mssim.c
 create mode 100644 backends/tpm/tpm_mssim.h

Comments

Markus Armbruster Sept. 22, 2023, 6 a.m. UTC | #1
Found this cleaning out old mail, sorry for missing it until now!

I think we owe James a quick decision wether we're willing to take the
feature.  Stefan, thoughts?

James Bottomley <jejb@linux.ibm.com> writes:

> From: James Bottomley <James.Bottomley@HansenPartnership.com>
>
> The Microsoft Simulator (mssim) is the reference emulation platform
> for the TCG TPM 2.0 specification.
>
> https://github.com/Microsoft/ms-tpm-20-ref.git
>
> It exports a fairly simple network socket based protocol on two
> sockets, one for command (default 2321) and one for control (default
> 2322).  This patch adds a simple backend that can speak the mssim
> protocol over the network.  It also allows the two sockets to be
> specified on the command line.  The benefits are twofold: firstly it
> gives us a backend that actually speaks a standard TPM emulation
> protocol instead of the linux specific TPM driver format of the
> current emulated TPM backend and secondly, using the microsoft
> protocol, the end point of the emulator can be anywhere on the
> network, facilitating the cloud use case where a central TPM service
> can be used over a control network.
>
> The implementation does basic control commands like power off/on, but
> doesn't implement cancellation or startup.  The former because
> cancellation is pretty much useless on a fast operating TPM emulator
> and the latter because this emulator is designed to be used with OVMF
> which itself does TPM startup and I wanted to validate that.
>
> To run this, simply download an emulator based on the MS specification
> (package ibmswtpm2 on openSUSE) and run it, then add these two lines
> to the qemu command and it will use the emulator.
>
>     -tpmdev mssim,id=tpm0 \
>     -device tpm-crb,tpmdev=tpm0 \
>
> to use a remote emulator replace the first line with
>
>     -tpmdev "{'type':'mssim','id':'tpm0','command':{'type':inet,'host':'remote','port':'2321'}}"
>
> tpm-tis also works as the backend.
>
> Signed-off-by: James Bottomley <jejb@linux.ibm.com>

[...]

> diff --git a/docs/specs/tpm.rst b/docs/specs/tpm.rst
> index 535912a92b..1398735956 100644
> --- a/docs/specs/tpm.rst
> +++ b/docs/specs/tpm.rst
> @@ -270,6 +270,38 @@ available as a module (assuming a TPM 2 is passed through):
>    /sys/devices/LNXSYSTEM:00/LNXSYBUS:00/MSFT0101:00/tpm/tpm0/pcr-sha256/9
>    ...
>  
> +The QEMU TPM Microsoft Simulator Device
> +---------------------------------------
> +
> +The TCG provides a reference implementation for TPM 2.0 written by


Suggest to copy the cover letter's nice introductory paragraph here:

  The Microsoft Simulator (mssim) is the reference emulation platform
  for the TCG TPM 2.0 specification.

  It provides a reference implementation for TPM 2.0 written by

> +Microsoft (See `ms-tpm-20-ref`_ on github).  The reference implementation
> +starts a network server and listens for TPM commands on port 2321 and
> +TPM Platform control commands on port 2322, although these can be
> +altered.  The QEMU mssim TPM backend talks to this implementation.  By
> +default it connects to the default ports on localhost:
> +
> +.. code-block:: console
> +
> +  qemu-system-x86_64 <qemu-options> \
> +    -tpmdev mssim,id=tpm0 \
> +    -device tpm-crb,tpmdev=tpm0
> +
> +
> +Although it can also communicate with a remote host, which must be
> +specified as a SocketAddress via json on the command line for each of

Is the "via JSON" part in "must be specified ... on the command line"
correct?  I'd expect to be able to use dotted keys as well, like

    -tpmdev type=mssim,id=tpm0,command.type=inet,command.host=remote,command.port=2321',control.type=inet,control.host=remote,control.port=2322

Aside: I do recommend management applications stick to JSON.

> +the command and control ports:
> +
> +.. code-block:: console
> +
> +  qemu-system-x86_64 <qemu-options> \
> +    -tpmdev "{'type':'mssim','id':'tpm0','command':{'type':'inet','host':'remote','port':'2321'},'control':{'type':'inet','host':'remote','port':'2322'}}" \
> +    -device tpm-crb,tpmdev=tpm0
> +
> +
> +The mssim backend supports snapshotting and migration, but the state
> +of the Microsoft Simulator server must be preserved (or the server
> +kept running) outside of QEMU for restore to be successful.
> +
>  The QEMU TPM emulator device
>  ----------------------------
>  
> @@ -526,3 +558,6 @@ the following:
>  
>  .. _SWTPM protocol:
>     https://github.com/stefanberger/swtpm/blob/master/man/man3/swtpm_ioctls.pod
> +
> +.. _ms-tpm-20-ref:
> +   https://github.com/microsoft/ms-tpm-20-ref
> diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
> index ed78a87ddd..12482368d0 100644
> --- a/monitor/hmp-cmds.c
> +++ b/monitor/hmp-cmds.c
> @@ -731,6 +731,7 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)
>      unsigned int c = 0;
>      TPMPassthroughOptions *tpo;
>      TPMEmulatorOptions *teo;
> +    TPMmssimOptions *tmo;
>  
>      info_list = qmp_query_tpm(&err);
>      if (err) {
> @@ -764,6 +765,14 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)
>              teo = ti->options->u.emulator.data;
>              monitor_printf(mon, ",chardev=%s", teo->chardev);
>              break;
> +        case TPM_TYPE_MSSIM:
> +            tmo = &ti->options->u.mssim;
> +            monitor_printf(mon, ",command=%s:%s,control=%s:%s",
> +                           tmo->command->u.inet.host,
> +                           tmo->command->u.inet.port,
> +                           tmo->control->u.inet.host,
> +                           tmo->control->u.inet.port);
> +            break;
>          case TPM_TYPE__MAX:
>              break;
>          }
> diff --git a/qapi/tpm.json b/qapi/tpm.json
> index 2b491c28b4..f9dde35377 100644
> --- a/qapi/tpm.json
> +++ b/qapi/tpm.json
> @@ -5,6 +5,7 @@
>  ##
>  # = TPM (trusted platform module) devices
>  ##

Blank line, please.

> +{ 'include': 'sockets.json' }
>  
>  ##
>  # @TpmModel:
> @@ -49,7 +50,7 @@
   #
   # @passthrough: TPM passthrough type
   #
   # @emulator: Software Emulator TPM type (since 2.11)
>  #

Missing member documentation:

   # @mssim: <brief description here> (since 8.2)

>  # Since: 1.5
>  ##
> -{ 'enum': 'TpmType', 'data': [ 'passthrough', 'emulator' ],
> +{ 'enum': 'TpmType', 'data': [ 'passthrough', 'emulator', 'mssim' ],
>    'if': 'CONFIG_TPM' }
>  
>  ##
> @@ -64,7 +65,7 @@
>  # Example:
>  #
>  # -> { "execute": "query-tpm-types" }
> -# <- { "return": [ "passthrough", "emulator" ] }
> +# <- { "return": [ "passthrough", "emulator", "mssim" ] }

Thanks for updating the example.

>  #
>  ##
>  { 'command': 'query-tpm-types', 'returns': ['TpmType'],
> @@ -117,6 +118,22 @@
>    'data': { 'data': 'TPMEmulatorOptions' },
>    'if': 'CONFIG_TPM' }
>  
> +##
> +# @TPMmssimOptions:

Please capitalize similar to TPMPassthroughOptions and
TPMEmulatorOptions: TPMMssimOptions.

> +#
> +# Information for the mssim emulator connection
> +#
> +# @command: command socket for the TPM emulator

Blank line, please.

> +# @control: control socket for the TPM emulator
> +#
> +# Since: 7.2.0

Since 8.2

> +##
> +{ 'struct': 'TPMmssimOptions',
> +  'data': {
> +      '*command': 'SocketAddress',
> +      '*control': 'SocketAddress' },

Locally consistent indentation is

     'data': { '*command': 'SocketAddress',
               '*control': 'SocketAddress' },

> +  'if': 'CONFIG_TPM' }
> +
>  ##
>  # @TpmTypeOptions:
>  #
> @@ -124,6 +141,7 @@
>  #
>  # @type: - 'passthrough' The configuration options for the TPM passthrough type
>  #        - 'emulator' The configuration options for TPM emulator backend type
> +#        - 'mssim' The configuration options for TPM emulator mssim type
>  #
>  # Since: 1.5
>  ##
> @@ -131,7 +149,8 @@
>    'base': { 'type': 'TpmType' },
>    'discriminator': 'type',
>    'data': { 'passthrough' : 'TPMPassthroughOptionsWrapper',
> -            'emulator': 'TPMEmulatorOptionsWrapper' },
> +            'emulator': 'TPMEmulatorOptionsWrapper',
> +            'mssim' : 'TPMmssimOptions' },
>    'if': 'CONFIG_TPM' }
>  
>  ##
> @@ -150,7 +169,8 @@
>              'id' : 'str' },
>    'discriminator': 'type',
>    'data': { 'passthrough' : 'TPMPassthroughOptions',
> -            'emulator': 'TPMEmulatorOptions' },
> +            'emulator': 'TPMEmulatorOptions',
> +            'mssim': 'TPMmssimOptions' },
>    'if': 'CONFIG_TPM' }
>  
>  ##

Address my nitpicking, and you may add

Acked-by: Markus Armbruster <armbru@redhat.com>
Stefan Berger Sept. 22, 2023, 12:41 p.m. UTC | #2
On 9/22/23 02:00, Markus Armbruster wrote:
> Found this cleaning out old mail, sorry for missing it until now!
>
> I think we owe James a quick decision wether we're willing to take the
> feature.  Stefan, thoughts?

I thought we discusses it back then. Does it handle snapshotting and 
migration correctly?

   Stefan

>
> James Bottomley <jejb@linux.ibm.com> writes:
>
>> From: James Bottomley <James.Bottomley@HansenPartnership.com>
>>
>> The Microsoft Simulator (mssim) is the reference emulation platform
>> for the TCG TPM 2.0 specification.
>>
>> https://github.com/Microsoft/ms-tpm-20-ref.git
>>
>> It exports a fairly simple network socket based protocol on two
>> sockets, one for command (default 2321) and one for control (default
>> 2322).  This patch adds a simple backend that can speak the mssim
>> protocol over the network.  It also allows the two sockets to be
>> specified on the command line.  The benefits are twofold: firstly it
>> gives us a backend that actually speaks a standard TPM emulation
>> protocol instead of the linux specific TPM driver format of the
>> current emulated TPM backend and secondly, using the microsoft
>> protocol, the end point of the emulator can be anywhere on the
>> network, facilitating the cloud use case where a central TPM service
>> can be used over a control network.
>>
>> The implementation does basic control commands like power off/on, but
>> doesn't implement cancellation or startup.  The former because
>> cancellation is pretty much useless on a fast operating TPM emulator
>> and the latter because this emulator is designed to be used with OVMF
>> which itself does TPM startup and I wanted to validate that.
>>
>> To run this, simply download an emulator based on the MS specification
>> (package ibmswtpm2 on openSUSE) and run it, then add these two lines
>> to the qemu command and it will use the emulator.
>>
>>      -tpmdev mssim,id=tpm0 \
>>      -device tpm-crb,tpmdev=tpm0 \
>>
>> to use a remote emulator replace the first line with
>>
>>      -tpmdev "{'type':'mssim','id':'tpm0','command':{'type':inet,'host':'remote','port':'2321'}}"
>>
>> tpm-tis also works as the backend.
>>
>> Signed-off-by: James Bottomley <jejb@linux.ibm.com>
> [...]
>
>> diff --git a/docs/specs/tpm.rst b/docs/specs/tpm.rst
>> index 535912a92b..1398735956 100644
>> --- a/docs/specs/tpm.rst
>> +++ b/docs/specs/tpm.rst
>> @@ -270,6 +270,38 @@ available as a module (assuming a TPM 2 is passed through):
>>     /sys/devices/LNXSYSTEM:00/LNXSYBUS:00/MSFT0101:00/tpm/tpm0/pcr-sha256/9
>>     ...
>>   
>> +The QEMU TPM Microsoft Simulator Device
>> +---------------------------------------
>> +
>> +The TCG provides a reference implementation for TPM 2.0 written by
>
> Suggest to copy the cover letter's nice introductory paragraph here:
>
>    The Microsoft Simulator (mssim) is the reference emulation platform
>    for the TCG TPM 2.0 specification.
>
>    It provides a reference implementation for TPM 2.0 written by
>
>> +Microsoft (See `ms-tpm-20-ref`_ on github).  The reference implementation
>> +starts a network server and listens for TPM commands on port 2321 and
>> +TPM Platform control commands on port 2322, although these can be
>> +altered.  The QEMU mssim TPM backend talks to this implementation.  By
>> +default it connects to the default ports on localhost:
>> +
>> +.. code-block:: console
>> +
>> +  qemu-system-x86_64 <qemu-options> \
>> +    -tpmdev mssim,id=tpm0 \
>> +    -device tpm-crb,tpmdev=tpm0
>> +
>> +
>> +Although it can also communicate with a remote host, which must be
>> +specified as a SocketAddress via json on the command line for each of
> Is the "via JSON" part in "must be specified ... on the command line"
> correct?  I'd expect to be able to use dotted keys as well, like
>
>      -tpmdev type=mssim,id=tpm0,command.type=inet,command.host=remote,command.port=2321',control.type=inet,control.host=remote,control.port=2322
>
> Aside: I do recommend management applications stick to JSON.
>
>> +the command and control ports:
>> +
>> +.. code-block:: console
>> +
>> +  qemu-system-x86_64 <qemu-options> \
>> +    -tpmdev "{'type':'mssim','id':'tpm0','command':{'type':'inet','host':'remote','port':'2321'},'control':{'type':'inet','host':'remote','port':'2322'}}" \
>> +    -device tpm-crb,tpmdev=tpm0
>> +
>> +
>> +The mssim backend supports snapshotting and migration, but the state
>> +of the Microsoft Simulator server must be preserved (or the server
>> +kept running) outside of QEMU for restore to be successful.
>> +
>>   The QEMU TPM emulator device
>>   ----------------------------
>>   
>> @@ -526,3 +558,6 @@ the following:
>>   
>>   .. _SWTPM protocol:
>>      https://github.com/stefanberger/swtpm/blob/master/man/man3/swtpm_ioctls.pod
>> +
>> +.. _ms-tpm-20-ref:
>> +   https://github.com/microsoft/ms-tpm-20-ref
>> diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
>> index ed78a87ddd..12482368d0 100644
>> --- a/monitor/hmp-cmds.c
>> +++ b/monitor/hmp-cmds.c
>> @@ -731,6 +731,7 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)
>>       unsigned int c = 0;
>>       TPMPassthroughOptions *tpo;
>>       TPMEmulatorOptions *teo;
>> +    TPMmssimOptions *tmo;
>>   
>>       info_list = qmp_query_tpm(&err);
>>       if (err) {
>> @@ -764,6 +765,14 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)
>>               teo = ti->options->u.emulator.data;
>>               monitor_printf(mon, ",chardev=%s", teo->chardev);
>>               break;
>> +        case TPM_TYPE_MSSIM:
>> +            tmo = &ti->options->u.mssim;
>> +            monitor_printf(mon, ",command=%s:%s,control=%s:%s",
>> +                           tmo->command->u.inet.host,
>> +                           tmo->command->u.inet.port,
>> +                           tmo->control->u.inet.host,
>> +                           tmo->control->u.inet.port);
>> +            break;
>>           case TPM_TYPE__MAX:
>>               break;
>>           }
>> diff --git a/qapi/tpm.json b/qapi/tpm.json
>> index 2b491c28b4..f9dde35377 100644
>> --- a/qapi/tpm.json
>> +++ b/qapi/tpm.json
>> @@ -5,6 +5,7 @@
>>   ##
>>   # = TPM (trusted platform module) devices
>>   ##
> Blank line, please.
>
>> +{ 'include': 'sockets.json' }
>>   
>>   ##
>>   # @TpmModel:
>> @@ -49,7 +50,7 @@
>     #
>     # @passthrough: TPM passthrough type
>     #
>     # @emulator: Software Emulator TPM type (since 2.11)
>>   #
> Missing member documentation:
>
>     # @mssim: <brief description here> (since 8.2)
>
>>   # Since: 1.5
>>   ##
>> -{ 'enum': 'TpmType', 'data': [ 'passthrough', 'emulator' ],
>> +{ 'enum': 'TpmType', 'data': [ 'passthrough', 'emulator', 'mssim' ],
>>     'if': 'CONFIG_TPM' }
>>   
>>   ##
>> @@ -64,7 +65,7 @@
>>   # Example:
>>   #
>>   # -> { "execute": "query-tpm-types" }
>> -# <- { "return": [ "passthrough", "emulator" ] }
>> +# <- { "return": [ "passthrough", "emulator", "mssim" ] }
> Thanks for updating the example.
>
>>   #
>>   ##
>>   { 'command': 'query-tpm-types', 'returns': ['TpmType'],
>> @@ -117,6 +118,22 @@
>>     'data': { 'data': 'TPMEmulatorOptions' },
>>     'if': 'CONFIG_TPM' }
>>   
>> +##
>> +# @TPMmssimOptions:
> Please capitalize similar to TPMPassthroughOptions and
> TPMEmulatorOptions: TPMMssimOptions.
>
>> +#
>> +# Information for the mssim emulator connection
>> +#
>> +# @command: command socket for the TPM emulator
> Blank line, please.
>
>> +# @control: control socket for the TPM emulator
>> +#
>> +# Since: 7.2.0
> Since 8.2
>
>> +##
>> +{ 'struct': 'TPMmssimOptions',
>> +  'data': {
>> +      '*command': 'SocketAddress',
>> +      '*control': 'SocketAddress' },
> Locally consistent indentation is
>
>       'data': { '*command': 'SocketAddress',
>                 '*control': 'SocketAddress' },
>
>> +  'if': 'CONFIG_TPM' }
>> +
>>   ##
>>   # @TpmTypeOptions:
>>   #
>> @@ -124,6 +141,7 @@
>>   #
>>   # @type: - 'passthrough' The configuration options for the TPM passthrough type
>>   #        - 'emulator' The configuration options for TPM emulator backend type
>> +#        - 'mssim' The configuration options for TPM emulator mssim type
>>   #
>>   # Since: 1.5
>>   ##
>> @@ -131,7 +149,8 @@
>>     'base': { 'type': 'TpmType' },
>>     'discriminator': 'type',
>>     'data': { 'passthrough' : 'TPMPassthroughOptionsWrapper',
>> -            'emulator': 'TPMEmulatorOptionsWrapper' },
>> +            'emulator': 'TPMEmulatorOptionsWrapper',
>> +            'mssim' : 'TPMmssimOptions' },
>>     'if': 'CONFIG_TPM' }
>>   
>>   ##
>> @@ -150,7 +169,8 @@
>>               'id' : 'str' },
>>     'discriminator': 'type',
>>     'data': { 'passthrough' : 'TPMPassthroughOptions',
>> -            'emulator': 'TPMEmulatorOptions' },
>> +            'emulator': 'TPMEmulatorOptions',
>> +            'mssim': 'TPMmssimOptions' },
>>     'if': 'CONFIG_TPM' }
>>   
>>   ##
> Address my nitpicking, and you may add
>
> Acked-by: Markus Armbruster <armbru@redhat.com>
>
Daniel P. Berrangé Sept. 22, 2023, 1:02 p.m. UTC | #3
On Fri, Sep 22, 2023 at 08:41:19AM -0400, Stefan Berger wrote:
> 
> On 9/22/23 02:00, Markus Armbruster wrote:
> > Found this cleaning out old mail, sorry for missing it until now!
> > 
> > I think we owe James a quick decision wether we're willing to take the
> > feature.  Stefan, thoughts?
> 
> I thought we discusses it back then. Does it handle snapshotting and
> migration correctly?

To quote the patch itself:

  +The mssim backend supports snapshotting and migration, but the state
  +of the Microsoft Simulator server must be preserved (or the server
  +kept running) outside of QEMU for restore to be successful.


With regards,
Daniel
Stefan Berger Sept. 22, 2023, 1:27 p.m. UTC | #4
On 9/22/23 09:02, Daniel P. Berrangé wrote:
> On Fri, Sep 22, 2023 at 08:41:19AM -0400, Stefan Berger wrote:
>> On 9/22/23 02:00, Markus Armbruster wrote:
>>> Found this cleaning out old mail, sorry for missing it until now!
>>>
>>> I think we owe James a quick decision wether we're willing to take the
>>> feature.  Stefan, thoughts?
>> I thought we discusses it back then. Does it handle snapshotting and
>> migration correctly?
> To quote the patch itself:
>
>    +The mssim backend supports snapshotting and migration, but the state
>    +of the Microsoft Simulator server must be preserved (or the server
>    +kept running) outside of QEMU for restore to be successful.

How does 'it' support snapshotting where the state of the TPM can be 
completely different depending on the snapshot?  I know what it took to 
support this feature with swtpm/libtpms but I don't see the equivalent 
here in this backend driver nor in the TCG reference code that the 
underlying TPM 2 simulator is based upon.

I do not want to stand in the way of it being merged but please 
understand that I will also neither maintain nor fix bugs related to it 
nor its related underlying simulator -- with James being the maintainer 
of it, this should be clear. I have reason why I am saying this and they 
come from dealing with the upstream TPM 2 reference code.

Thanks,

   Stefan


>
> With regards,
> Daniel
James Bottomley Sept. 25, 2023, 12:12 p.m. UTC | #5
On Fri, 2023-09-22 at 09:27 -0400, Stefan Berger wrote:
> 
> On 9/22/23 09:02, Daniel P. Berrangé wrote:
> > On Fri, Sep 22, 2023 at 08:41:19AM -0400, Stefan Berger wrote:
> > > On 9/22/23 02:00, Markus Armbruster wrote:
> > > > Found this cleaning out old mail, sorry for missing it until
> > > > now!
> > > > 
> > > > I think we owe James a quick decision wether we're willing to
> > > > take the feature.  Stefan, thoughts?
> > > I thought we discusses it back then. Does it handle snapshotting
> > > and migration correctly?
> > To quote the patch itself:
> > 
> >    +The mssim backend supports snapshotting and migration, but the
> > state
> >    +of the Microsoft Simulator server must be preserved (or the
> > server
> >    +kept running) outside of QEMU for restore to be successful.
> 
> How does 'it' support snapshotting where the state of the TPM can be 
> completely different depending on the snapshot?

In the same way we support things like external disk devices across
snapshot and migration: it's up to the owner of the device to preserve
the state for the next resume. If you muck with the state (or connect
the wrong device) all bets are off.

>   I know what it took to support this feature with swtpm/libtpms but
> I don't see the equivalent here in this backend driver nor in the TCG
> reference code that the underlying TPM 2 simulator is based upon.
> 
> I do not want to stand in the way of it being merged but please 
> understand that I will also neither maintain nor fix bugs related to
> it nor its related underlying simulator -- with James being the
> maintainer of it, this should be clear. I have reason why I am saying
> this and they come from dealing with the upstream TPM 2 reference
> code.

I already said I'll support this, and added a Maintainers entry and a
specific exclusion from your TPM maintainer entry. I'm not sure what
additional assurances I can give?

James
James Bottomley Sept. 25, 2023, 12:15 p.m. UTC | #6
On Fri, 2023-09-22 at 08:00 +0200, Markus Armbruster wrote:
> Found this cleaning out old mail, sorry for missing it until now!
> 
> I think we owe James a quick decision wether we're willing to take
> the
> feature.  Stefan, thoughts?
> 
> James Bottomley <jejb@linux.ibm.com> writes:
> 
> > From: James Bottomley <James.Bottomley@HansenPartnership.com>
> > 
> > The Microsoft Simulator (mssim) is the reference emulation platform
> > for the TCG TPM 2.0 specification.
> > 
> > https://github.com/Microsoft/ms-tpm-20-ref.git
> > 
> > It exports a fairly simple network socket based protocol on two
> > sockets, one for command (default 2321) and one for control
> > (default
> > 2322).  This patch adds a simple backend that can speak the mssim
> > protocol over the network.  It also allows the two sockets to be
> > specified on the command line.  The benefits are twofold: firstly
> > it
> > gives us a backend that actually speaks a standard TPM emulation
> > protocol instead of the linux specific TPM driver format of the
> > current emulated TPM backend and secondly, using the microsoft
> > protocol, the end point of the emulator can be anywhere on the
> > network, facilitating the cloud use case where a central TPM
> > service
> > can be used over a control network.
> > 
> > The implementation does basic control commands like power off/on,
> > but
> > doesn't implement cancellation or startup.  The former because
> > cancellation is pretty much useless on a fast operating TPM
> > emulator
> > and the latter because this emulator is designed to be used with
> > OVMF
> > which itself does TPM startup and I wanted to validate that.
> > 
> > To run this, simply download an emulator based on the MS
> > specification
> > (package ibmswtpm2 on openSUSE) and run it, then add these two
> > lines
> > to the qemu command and it will use the emulator.
> > 
> >     -tpmdev mssim,id=tpm0 \
> >     -device tpm-crb,tpmdev=tpm0 \
> > 
> > to use a remote emulator replace the first line with
> > 
> >     -tpmdev
> > "{'type':'mssim','id':'tpm0','command':{'type':inet,'host':'remote'
> > ,'port':'2321'}}"
> > 
> > tpm-tis also works as the backend.
> > 
> > Signed-off-by: James Bottomley <jejb@linux.ibm.com>
> 
> [...]
> 
> > diff --git a/docs/specs/tpm.rst b/docs/specs/tpm.rst
> > index 535912a92b..1398735956 100644
> > --- a/docs/specs/tpm.rst
> > +++ b/docs/specs/tpm.rst
> > @@ -270,6 +270,38 @@ available as a module (assuming a TPM 2 is
> > passed through):
> >    /sys/devices/LNXSYSTEM:00/LNXSYBUS:00/MSFT0101:00/tpm/tpm0/pcr-
> > sha256/9
> >    ...
> >  
> > +The QEMU TPM Microsoft Simulator Device
> > +---------------------------------------
> > +
> > +The TCG provides a reference implementation for TPM 2.0 written by
> 
> 
> Suggest to copy the cover letter's nice introductory paragraph here:
> 
>   The Microsoft Simulator (mssim) is the reference emulation platform
>   for the TCG TPM 2.0 specification.
> 
>   It provides a reference implementation for TPM 2.0 written by

Sure, that's easy.

> > +Microsoft (See `ms-tpm-20-ref`_ on github).  The reference
> > implementation
> > +starts a network server and listens for TPM commands on port 2321
> > and
> > +TPM Platform control commands on port 2322, although these can be
> > +altered.  The QEMU mssim TPM backend talks to this
> > implementation.  By
> > +default it connects to the default ports on localhost:
> > +
> > +.. code-block:: console
> > +
> > +  qemu-system-x86_64 <qemu-options> \
> > +    -tpmdev mssim,id=tpm0 \
> > +    -device tpm-crb,tpmdev=tpm0
> > +
> > +
> > +Although it can also communicate with a remote host, which must be
> > +specified as a SocketAddress via json on the command line for each
> > of
> 
> Is the "via JSON" part in "must be specified ... on the command line"
> correct?  I'd expect to be able to use dotted keys as well, like
> 
>     -tpmdev
> type=mssim,id=tpm0,command.type=inet,command.host=remote,command.port
> =2321',control.type=inet,control.host=remote,control.port=2322

Yes, I've verified that the dot notation works as well.  However, I
thought QEMU was calling all stuff like this JSON notation?  If not,
what do you usually call it? "json or dot notation"?

> 
> Aside: I do recommend management applications stick to JSON.
> 
> > +the command and control ports:
> > +
> > +.. code-block:: console
> > +
> > +  qemu-system-x86_64 <qemu-options> \
> > +    -tpmdev
> > "{'type':'mssim','id':'tpm0','command':{'type':'inet','host':'remot
> > e','port':'2321'},'control':{'type':'inet','host':'remote','port':'
> > 2322'}}" \
> > +    -device tpm-crb,tpmdev=tpm0
> > +
> > +
> > +The mssim backend supports snapshotting and migration, but the
> > state
> > +of the Microsoft Simulator server must be preserved (or the server
> > +kept running) outside of QEMU for restore to be successful.
> > +
> >  The QEMU TPM emulator device
> >  ----------------------------
> >  
> > @@ -526,3 +558,6 @@ the following:
> >  
> >  .. _SWTPM protocol:
> >    
> > https://github.com/stefanberger/swtpm/blob/master/man/man3/swtpm_ioctls.pod
> > +
> > +.. _ms-tpm-20-ref:
> > +   https://github.com/microsoft/ms-tpm-20-ref
> > diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
> > index ed78a87ddd..12482368d0 100644
> > --- a/monitor/hmp-cmds.c
> > +++ b/monitor/hmp-cmds.c
> > @@ -731,6 +731,7 @@ void hmp_info_tpm(Monitor *mon, const QDict
> > *qdict)
> >      unsigned int c = 0;
> >      TPMPassthroughOptions *tpo;
> >      TPMEmulatorOptions *teo;
> > +    TPMmssimOptions *tmo;
> >  
> >      info_list = qmp_query_tpm(&err);
> >      if (err) {
> > @@ -764,6 +765,14 @@ void hmp_info_tpm(Monitor *mon, const QDict
> > *qdict)
> >              teo = ti->options->u.emulator.data;
> >              monitor_printf(mon, ",chardev=%s", teo->chardev);
> >              break;
> > +        case TPM_TYPE_MSSIM:
> > +            tmo = &ti->options->u.mssim;
> > +            monitor_printf(mon, ",command=%s:%s,control=%s:%s",
> > +                           tmo->command->u.inet.host,
> > +                           tmo->command->u.inet.port,
> > +                           tmo->control->u.inet.host,
> > +                           tmo->control->u.inet.port);
> > +            break;
> >          case TPM_TYPE__MAX:
> >              break;
> >          }
> > diff --git a/qapi/tpm.json b/qapi/tpm.json
> > index 2b491c28b4..f9dde35377 100644
> > --- a/qapi/tpm.json
> > +++ b/qapi/tpm.json
> > @@ -5,6 +5,7 @@
> >  ##
> >  # = TPM (trusted platform module) devices
> >  ##
> 
> Blank line, please.
> 
> > +{ 'include': 'sockets.json' }
> >  
> >  ##
> >  # @TpmModel:
> > @@ -49,7 +50,7 @@
>    #
>    # @passthrough: TPM passthrough type
>    #
>    # @emulator: Software Emulator TPM type (since 2.11)
> >  #
> 
> Missing member documentation:
> 
>    # @mssim: <brief description here> (since 8.2)
> 
> >  # Since: 1.5
> >  ##
> > -{ 'enum': 'TpmType', 'data': [ 'passthrough', 'emulator' ],
> > +{ 'enum': 'TpmType', 'data': [ 'passthrough', 'emulator', 'mssim'
> > ],
> >    'if': 'CONFIG_TPM' }
> >  
> >  ##
> > @@ -64,7 +65,7 @@
> >  # Example:
> >  #
> >  # -> { "execute": "query-tpm-types" }
> > -# <- { "return": [ "passthrough", "emulator" ] }
> > +# <- { "return": [ "passthrough", "emulator", "mssim" ] }
> 
> Thanks for updating the example.
> 
> >  #
> >  ##
> >  { 'command': 'query-tpm-types', 'returns': ['TpmType'],
> > @@ -117,6 +118,22 @@
> >    'data': { 'data': 'TPMEmulatorOptions' },
> >    'if': 'CONFIG_TPM' }
> >  
> > +##
> > +# @TPMmssimOptions:
> 
> Please capitalize similar to TPMPassthroughOptions and
> TPMEmulatorOptions: TPMMssimOptions.

OK

> 
> > +#
> > +# Information for the mssim emulator connection
> > +#
> > +# @command: command socket for the TPM emulator
> 
> Blank line, please.

OK

> 
> > +# @control: control socket for the TPM emulator
> > +#
> > +# Since: 7.2.0
> 
> Since 8.2

Heh, yes, that keeps creeping with every release ..

> 
> > +##
> > +{ 'struct': 'TPMmssimOptions',
> > +  'data': {
> > +      '*command': 'SocketAddress',
> > +      '*control': 'SocketAddress' },
> 
> Locally consistent indentation is
> 
>      'data': { '*command': 'SocketAddress',
>                '*control': 'SocketAddress' },
> 
> > +  'if': 'CONFIG_TPM' }
> > +
> >  ##
> >  # @TpmTypeOptions:
> >  #
> > @@ -124,6 +141,7 @@
> >  #
> >  # @type: - 'passthrough' The configuration options for the TPM
> > passthrough type
> >  #        - 'emulator' The configuration options for TPM emulator
> > backend type
> > +#        - 'mssim' The configuration options for TPM emulator
> > mssim type
> >  #
> >  # Since: 1.5
> >  ##
> > @@ -131,7 +149,8 @@
> >    'base': { 'type': 'TpmType' },
> >    'discriminator': 'type',
> >    'data': { 'passthrough' : 'TPMPassthroughOptionsWrapper',
> > -            'emulator': 'TPMEmulatorOptionsWrapper' },
> > +            'emulator': 'TPMEmulatorOptionsWrapper',
> > +            'mssim' : 'TPMmssimOptions' },
> >    'if': 'CONFIG_TPM' }
> >  
> >  ##
> > @@ -150,7 +169,8 @@
> >              'id' : 'str' },
> >    'discriminator': 'type',
> >    'data': { 'passthrough' : 'TPMPassthroughOptions',
> > -            'emulator': 'TPMEmulatorOptions' },
> > +            'emulator': 'TPMEmulatorOptions',
> > +            'mssim': 'TPMmssimOptions' },
> >    'if': 'CONFIG_TPM' }
> >  
> >  ##
> 
> Address my nitpicking, and you may add
> 
> Acked-by: Markus Armbruster <armbru@redhat.com>

James
Markus Armbruster Sept. 25, 2023, 12:25 p.m. UTC | #7
James Bottomley <jejb@linux.ibm.com> writes:

> On Fri, 2023-09-22 at 08:00 +0200, Markus Armbruster wrote:
>> Found this cleaning out old mail, sorry for missing it until now!
>> 
>> I think we owe James a quick decision wether we're willing to take
>> the
>> feature.  Stefan, thoughts?
>> 
>> James Bottomley <jejb@linux.ibm.com> writes:
>> 
>> > From: James Bottomley <James.Bottomley@HansenPartnership.com>
>> > 
>> > The Microsoft Simulator (mssim) is the reference emulation platform
>> > for the TCG TPM 2.0 specification.
>> > 
>> > https://github.com/Microsoft/ms-tpm-20-ref.git
>> > 
>> > It exports a fairly simple network socket based protocol on two
>> > sockets, one for command (default 2321) and one for control
>> > (default
>> > 2322).  This patch adds a simple backend that can speak the mssim
>> > protocol over the network.  It also allows the two sockets to be
>> > specified on the command line.  The benefits are twofold: firstly
>> > it
>> > gives us a backend that actually speaks a standard TPM emulation
>> > protocol instead of the linux specific TPM driver format of the
>> > current emulated TPM backend and secondly, using the microsoft
>> > protocol, the end point of the emulator can be anywhere on the
>> > network, facilitating the cloud use case where a central TPM
>> > service
>> > can be used over a control network.
>> > 
>> > The implementation does basic control commands like power off/on,
>> > but
>> > doesn't implement cancellation or startup.  The former because
>> > cancellation is pretty much useless on a fast operating TPM
>> > emulator
>> > and the latter because this emulator is designed to be used with
>> > OVMF
>> > which itself does TPM startup and I wanted to validate that.
>> > 
>> > To run this, simply download an emulator based on the MS
>> > specification
>> > (package ibmswtpm2 on openSUSE) and run it, then add these two
>> > lines
>> > to the qemu command and it will use the emulator.
>> > 
>> >     -tpmdev mssim,id=tpm0 \
>> >     -device tpm-crb,tpmdev=tpm0 \
>> > 
>> > to use a remote emulator replace the first line with
>> > 
>> >     -tpmdev
>> > "{'type':'mssim','id':'tpm0','command':{'type':inet,'host':'remote'
>> > ,'port':'2321'}}"
>> > 
>> > tpm-tis also works as the backend.
>> > 
>> > Signed-off-by: James Bottomley <jejb@linux.ibm.com>
>> 
>> [...]
>> 
>> > diff --git a/docs/specs/tpm.rst b/docs/specs/tpm.rst
>> > index 535912a92b..1398735956 100644
>> > --- a/docs/specs/tpm.rst
>> > +++ b/docs/specs/tpm.rst
>> > @@ -270,6 +270,38 @@ available as a module (assuming a TPM 2 is
>> > passed through):
>> >    /sys/devices/LNXSYSTEM:00/LNXSYBUS:00/MSFT0101:00/tpm/tpm0/pcr-
>> > sha256/9
>> >    ...
>> >  
>> > +The QEMU TPM Microsoft Simulator Device
>> > +---------------------------------------
>> > +
>> > +The TCG provides a reference implementation for TPM 2.0 written by
>> 
>> 
>> Suggest to copy the cover letter's nice introductory paragraph here:
>> 
>>   The Microsoft Simulator (mssim) is the reference emulation platform
>>   for the TCG TPM 2.0 specification.
>> 
>>   It provides a reference implementation for TPM 2.0 written by
>
> Sure, that's easy.
>
>> > +Microsoft (See `ms-tpm-20-ref`_ on github).  The reference
>> > implementation
>> > +starts a network server and listens for TPM commands on port 2321
>> > and
>> > +TPM Platform control commands on port 2322, although these can be
>> > +altered.  The QEMU mssim TPM backend talks to this
>> > implementation.  By
>> > +default it connects to the default ports on localhost:
>> > +
>> > +.. code-block:: console
>> > +
>> > +  qemu-system-x86_64 <qemu-options> \
>> > +    -tpmdev mssim,id=tpm0 \
>> > +    -device tpm-crb,tpmdev=tpm0
>> > +
>> > +
>> > +Although it can also communicate with a remote host, which must be
>> > +specified as a SocketAddress via json on the command line for each
>> > of
>> 
>> Is the "via JSON" part in "must be specified ... on the command line"
>> correct?  I'd expect to be able to use dotted keys as well, like
>> 
>>     -tpmdev
>> type=mssim,id=tpm0,command.type=inet,command.host=remote,command.port
>> =2321',control.type=inet,control.host=remote,control.port=2322
>
> Yes, I've verified that the dot notation works as well.  However, I
> thought QEMU was calling all stuff like this JSON notation?  If not,
> what do you usually call it? "json or dot notation"?

Our terminology is a bit fuzzy there.  We commonly say "dotted keys"
(although that's just the part left of the = strictly speaking) or
"key=value,...".

>> Aside: I do recommend management applications stick to JSON.

[...]
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 7a40d4d865..666ca1eb1c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3063,10 +3063,16 @@  F: include/hw/acpi/tpm.h
 F: include/sysemu/tpm*
 F: qapi/tpm.json
 F: backends/tpm/
+X: backends/tpm/tpm_mssim.*
 F: tests/qtest/*tpm*
 F: docs/specs/tpm.rst
 T: git https://github.com/stefanberger/qemu-tpm.git tpm-next
 
+MSSIM TPM Backend
+M: James Bottomley <jejb@linux.ibm.com>
+S: Maintained
+F: backends/tpm/tpm_mssim.*
+
 Checkpatch
 S: Odd Fixes
 F: scripts/checkpatch.pl
diff --git a/backends/tpm/Kconfig b/backends/tpm/Kconfig
index 5d91eb89c2..d6d6fa53e9 100644
--- a/backends/tpm/Kconfig
+++ b/backends/tpm/Kconfig
@@ -12,3 +12,8 @@  config TPM_EMULATOR
     bool
     default y
     depends on TPM_BACKEND
+
+config TPM_MSSIM
+    bool
+    default y
+    depends on TPM_BACKEND
diff --git a/backends/tpm/meson.build b/backends/tpm/meson.build
index 7f2503f84e..c7c3c79125 100644
--- a/backends/tpm/meson.build
+++ b/backends/tpm/meson.build
@@ -3,4 +3,5 @@  if have_tpm
   softmmu_ss.add(files('tpm_util.c'))
   softmmu_ss.add(when: 'CONFIG_TPM_PASSTHROUGH', if_true: files('tpm_passthrough.c'))
   softmmu_ss.add(when: 'CONFIG_TPM_EMULATOR', if_true: files('tpm_emulator.c'))
+  softmmu_ss.add(when: 'CONFIG_TPM_MSSIM', if_true: files('tpm_mssim.c'))
 endif
diff --git a/backends/tpm/tpm_mssim.c b/backends/tpm/tpm_mssim.c
new file mode 100644
index 0000000000..ed1c4cfd35
--- /dev/null
+++ b/backends/tpm/tpm_mssim.c
@@ -0,0 +1,290 @@ 
+/*
+ * Emulator TPM driver which connects over the mssim protocol
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright (c) 2022
+ * Author: James Bottomley <jejb@linux.ibm.com>
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qemu/sockets.h"
+
+#include "qapi/clone-visitor.h"
+#include "qapi/qapi-visit-tpm.h"
+
+#include "io/channel-socket.h"
+
+#include "sysemu/runstate.h"
+#include "sysemu/tpm_backend.h"
+#include "sysemu/tpm_util.h"
+
+#include "qom/object.h"
+
+#include "tpm_int.h"
+#include "tpm_mssim.h"
+
+#define ERROR_PREFIX "TPM mssim Emulator: "
+
+#define TYPE_TPM_MSSIM "tpm-mssim"
+OBJECT_DECLARE_SIMPLE_TYPE(TPMmssim, TPM_MSSIM)
+
+struct TPMmssim {
+    TPMBackend parent;
+
+    TPMmssimOptions opts;
+
+    QIOChannelSocket *cmd_qc, *ctrl_qc;
+};
+
+static int tpm_send_ctrl(TPMmssim *t, uint32_t cmd, Error **errp)
+{
+    int ret;
+
+    qio_channel_socket_connect_sync(t->ctrl_qc, t->opts.control, errp);
+    cmd = htonl(cmd);
+    ret = qio_channel_write_all(QIO_CHANNEL(t->ctrl_qc),
+                                (char *)&cmd, sizeof(cmd), errp);
+    if (ret != 0) {
+        goto out;
+    }
+
+    ret = qio_channel_read_all(QIO_CHANNEL(t->ctrl_qc),
+                               (char *)&cmd, sizeof(cmd), errp);
+    if (ret != 0) {
+        goto out;
+    }
+    if (cmd != 0) {
+        error_setg(errp, ERROR_PREFIX
+                   "Incorrect ACK recieved on control channel 0x%x", cmd);
+        ret = -1;
+    }
+ out:
+    qio_channel_close(QIO_CHANNEL(t->ctrl_qc), errp);
+    return ret;
+}
+
+static void tpm_mssim_instance_init(Object *obj)
+{
+}
+
+static void tpm_mssim_instance_finalize(Object *obj)
+{
+    TPMmssim *t = TPM_MSSIM(obj);
+
+    if (t->cmd_qc && !runstate_check(RUN_STATE_POSTMIGRATE)) {
+        tpm_send_ctrl(t, TPM_SIGNAL_POWER_OFF, NULL);
+    }
+
+    object_unref(OBJECT(t->ctrl_qc));
+    object_unref(OBJECT(t->cmd_qc));
+}
+
+static void tpm_mssim_cancel_cmd(TPMBackend *tb)
+{
+        return;
+}
+
+static TPMVersion tpm_mssim_get_version(TPMBackend *tb)
+{
+    return TPM_VERSION_2_0;
+}
+
+static size_t tpm_mssim_get_buffer_size(TPMBackend *tb)
+{
+    /* TCG standard profile max buffer size */
+    return 4096;
+}
+
+static TpmTypeOptions *tpm_mssim_get_opts(TPMBackend *tb)
+{
+    TPMmssim *t = TPM_MSSIM(tb);
+    TpmTypeOptions *opts = g_new0(TpmTypeOptions, 1);
+
+    opts->type = TPM_TYPE_MSSIM;
+    opts->u.mssim = t->opts;
+
+    return opts;
+}
+
+static void tpm_mssim_handle_request(TPMBackend *tb, TPMBackendCmd *cmd,
+                                     Error **errp)
+{
+    TPMmssim *t = TPM_MSSIM(tb);
+    uint32_t header, len;
+    uint8_t locality = cmd->locty;
+    struct iovec iov[4];
+    int ret;
+
+    header = htonl(TPM_SEND_COMMAND);
+    len = htonl(cmd->in_len);
+
+    iov[0].iov_base = &header;
+    iov[0].iov_len = sizeof(header);
+    iov[1].iov_base = &locality;
+    iov[1].iov_len = sizeof(locality);
+    iov[2].iov_base = &len;
+    iov[2].iov_len = sizeof(len);
+    iov[3].iov_base = (void *)cmd->in;
+    iov[3].iov_len = cmd->in_len;
+
+    ret = qio_channel_writev_all(QIO_CHANNEL(t->cmd_qc), iov, 4, errp);
+    if (ret != 0) {
+        goto fail;
+    }
+
+    ret = qio_channel_read_all(QIO_CHANNEL(t->cmd_qc),
+                               (char *)&len, sizeof(len), errp);
+    if (ret != 0) {
+        goto fail;
+    }
+
+    len = ntohl(len);
+    if (len > cmd->out_len) {
+        error_setg(errp, "receive size is too large");
+        goto fail;
+    }
+    ret = qio_channel_read_all(QIO_CHANNEL(t->cmd_qc),
+                               (char *)cmd->out, len, errp);
+    if (ret != 0) {
+        goto fail;
+    }
+
+    /* ACK packet */
+    ret = qio_channel_read_all(QIO_CHANNEL(t->cmd_qc),
+                               (char *)&header, sizeof(header), errp);
+    if (ret != 0) {
+        goto fail;
+    }
+    if (header != 0) {
+        error_setg(errp, "incorrect ACK received on command channel 0x%x", len);
+        goto fail;
+    }
+
+    return;
+
+ fail:
+    error_prepend(errp, ERROR_PREFIX);
+    tpm_util_write_fatal_error_response(cmd->out, cmd->out_len);
+}
+
+static TPMBackend *tpm_mssim_create(TpmCreateOptions *opts)
+{
+    TPMBackend *be = TPM_BACKEND(object_new(TYPE_TPM_MSSIM));
+    TPMmssim *t = TPM_MSSIM(be);
+    int sock;
+    Error *errp = NULL;
+    TPMmssimOptions *mo = &opts->u.mssim;
+
+    if (!mo->command) {
+            mo->command = g_new0(SocketAddress, 1);
+            mo->command->type = SOCKET_ADDRESS_TYPE_INET;
+            mo->command->u.inet.host = g_strdup("localhost");
+            mo->command->u.inet.port = g_strdup("2321");
+    }
+    if (!mo->control) {
+            int port;
+
+            mo->control = g_new0(SocketAddress, 1);
+            mo->control->type = SOCKET_ADDRESS_TYPE_INET;
+            mo->control->u.inet.host = g_strdup(mo->command->u.inet.host);
+            /*
+             * in the reference implementation, the control port is
+             * always one above the command port
+             */
+            port = atoi(mo->command->u.inet.port) + 1;
+            mo->control->u.inet.port = g_strdup_printf("%d", port);
+    }
+
+    t->opts = opts->u.mssim;
+    t->cmd_qc = qio_channel_socket_new();
+    t->ctrl_qc = qio_channel_socket_new();
+
+    if (qio_channel_socket_connect_sync(t->cmd_qc, mo->command, &errp) < 0) {
+        goto fail;
+    }
+
+    if (qio_channel_socket_connect_sync(t->ctrl_qc, mo->control, &errp) < 0) {
+        goto fail;
+    }
+    qio_channel_close(QIO_CHANNEL(t->ctrl_qc), &errp);
+
+    if (!runstate_check(RUN_STATE_INMIGRATE)) {
+        /*
+         * reset the TPM using a power cycle sequence, in case someone
+         * has previously powered it up
+         */
+        sock = tpm_send_ctrl(t, TPM_SIGNAL_POWER_OFF, &errp);
+        if (sock != 0) {
+            goto fail;
+        }
+
+        sock = tpm_send_ctrl(t, TPM_SIGNAL_POWER_ON, &errp);
+        if (sock != 0) {
+            goto fail;
+        }
+
+        sock = tpm_send_ctrl(t, TPM_SIGNAL_NV_ON, &errp);
+        if (sock != 0) {
+            goto fail;
+        }
+    }
+
+    return be;
+
+ fail:
+    object_unref(OBJECT(t->ctrl_qc));
+    object_unref(OBJECT(t->cmd_qc));
+    t->ctrl_qc = NULL;
+    t->cmd_qc = NULL;
+    error_prepend(&errp, ERROR_PREFIX);
+    error_report_err(errp);
+    object_unref(OBJECT(be));
+
+    return NULL;
+}
+
+static const QemuOptDesc tpm_mssim_cmdline_opts[] = {
+    TPM_STANDARD_CMDLINE_OPTS,
+    {
+        .name = "command",
+        .type = QEMU_OPT_STRING,
+        .help = "Command socket (default localhost:2321)",
+    },
+    {
+        .name = "control",
+        .type = QEMU_OPT_STRING,
+        .help = "control socket (default localhost:2322)",
+    },
+};
+
+static void tpm_mssim_class_init(ObjectClass *klass, void *data)
+{
+    TPMBackendClass *cl = TPM_BACKEND_CLASS(klass);
+
+    cl->type = TPM_TYPE_MSSIM;
+    cl->opts = tpm_mssim_cmdline_opts;
+    cl->desc = "TPM mssim emulator backend driver";
+    cl->create = tpm_mssim_create;
+    cl->cancel_cmd = tpm_mssim_cancel_cmd;
+    cl->get_tpm_version = tpm_mssim_get_version;
+    cl->get_buffer_size = tpm_mssim_get_buffer_size;
+    cl->get_tpm_options = tpm_mssim_get_opts;
+    cl->handle_request = tpm_mssim_handle_request;
+}
+
+static const TypeInfo tpm_mssim_info = {
+    .name = TYPE_TPM_MSSIM,
+    .parent = TYPE_TPM_BACKEND,
+    .instance_size = sizeof(TPMmssim),
+    .class_init = tpm_mssim_class_init,
+    .instance_init = tpm_mssim_instance_init,
+    .instance_finalize = tpm_mssim_instance_finalize,
+};
+
+static void tpm_mssim_register(void)
+{
+    type_register_static(&tpm_mssim_info);
+}
+
+type_init(tpm_mssim_register)
diff --git a/backends/tpm/tpm_mssim.h b/backends/tpm/tpm_mssim.h
new file mode 100644
index 0000000000..397474e4f6
--- /dev/null
+++ b/backends/tpm/tpm_mssim.h
@@ -0,0 +1,44 @@ 
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * The code below is copied from the Microsoft/TCG Reference implementation
+ *
+ *  https://github.com/Microsoft/ms-tpm-20-ref.git
+ *
+ * In file TPMCmd/Simulator/include/TpmTcpProtocol.h
+ */
+
+#define TPM_SIGNAL_POWER_ON         1
+#define TPM_SIGNAL_POWER_OFF        2
+#define TPM_SIGNAL_PHYS_PRES_ON     3
+#define TPM_SIGNAL_PHYS_PRES_OFF    4
+#define TPM_SIGNAL_HASH_START       5
+#define TPM_SIGNAL_HASH_DATA        6
+/* {uint32_t BufferSize, uint8_t[BufferSize] Buffer} */
+#define TPM_SIGNAL_HASH_END         7
+#define TPM_SEND_COMMAND            8
+/*
+ * {uint8_t Locality, uint32_t InBufferSize, uint8_t[InBufferSize] InBuffer} ->
+ *   {uint32_t OutBufferSize, uint8_t[OutBufferSize] OutBuffer}
+ */
+#define TPM_SIGNAL_CANCEL_ON        9
+#define TPM_SIGNAL_CANCEL_OFF       10
+#define TPM_SIGNAL_NV_ON            11
+#define TPM_SIGNAL_NV_OFF           12
+#define TPM_SIGNAL_KEY_CACHE_ON     13
+#define TPM_SIGNAL_KEY_CACHE_OFF    14
+
+#define TPM_REMOTE_HANDSHAKE        15
+#define TPM_SET_ALTERNATIVE_RESULT  16
+
+#define TPM_SIGNAL_RESET            17
+#define TPM_SIGNAL_RESTART          18
+
+#define TPM_SESSION_END             20
+#define TPM_STOP                    21
+
+#define TPM_GET_COMMAND_RESPONSE_SIZES  25
+
+#define TPM_ACT_GET_SIGNALED        26
+
+#define TPM_TEST_FAILURE_MODE       30
diff --git a/docs/specs/tpm.rst b/docs/specs/tpm.rst
index 535912a92b..1398735956 100644
--- a/docs/specs/tpm.rst
+++ b/docs/specs/tpm.rst
@@ -270,6 +270,38 @@  available as a module (assuming a TPM 2 is passed through):
   /sys/devices/LNXSYSTEM:00/LNXSYBUS:00/MSFT0101:00/tpm/tpm0/pcr-sha256/9
   ...
 
+The QEMU TPM Microsoft Simulator Device
+---------------------------------------
+
+The TCG provides a reference implementation for TPM 2.0 written by
+Microsoft (See `ms-tpm-20-ref`_ on github).  The reference implementation
+starts a network server and listens for TPM commands on port 2321 and
+TPM Platform control commands on port 2322, although these can be
+altered.  The QEMU mssim TPM backend talks to this implementation.  By
+default it connects to the default ports on localhost:
+
+.. code-block:: console
+
+  qemu-system-x86_64 <qemu-options> \
+    -tpmdev mssim,id=tpm0 \
+    -device tpm-crb,tpmdev=tpm0
+
+
+Although it can also communicate with a remote host, which must be
+specified as a SocketAddress via json on the command line for each of
+the command and control ports:
+
+.. code-block:: console
+
+  qemu-system-x86_64 <qemu-options> \
+    -tpmdev "{'type':'mssim','id':'tpm0','command':{'type':'inet','host':'remote','port':'2321'},'control':{'type':'inet','host':'remote','port':'2322'}}" \
+    -device tpm-crb,tpmdev=tpm0
+
+
+The mssim backend supports snapshotting and migration, but the state
+of the Microsoft Simulator server must be preserved (or the server
+kept running) outside of QEMU for restore to be successful.
+
 The QEMU TPM emulator device
 ----------------------------
 
@@ -526,3 +558,6 @@  the following:
 
 .. _SWTPM protocol:
    https://github.com/stefanberger/swtpm/blob/master/man/man3/swtpm_ioctls.pod
+
+.. _ms-tpm-20-ref:
+   https://github.com/microsoft/ms-tpm-20-ref
diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
index ed78a87ddd..12482368d0 100644
--- a/monitor/hmp-cmds.c
+++ b/monitor/hmp-cmds.c
@@ -731,6 +731,7 @@  void hmp_info_tpm(Monitor *mon, const QDict *qdict)
     unsigned int c = 0;
     TPMPassthroughOptions *tpo;
     TPMEmulatorOptions *teo;
+    TPMmssimOptions *tmo;
 
     info_list = qmp_query_tpm(&err);
     if (err) {
@@ -764,6 +765,14 @@  void hmp_info_tpm(Monitor *mon, const QDict *qdict)
             teo = ti->options->u.emulator.data;
             monitor_printf(mon, ",chardev=%s", teo->chardev);
             break;
+        case TPM_TYPE_MSSIM:
+            tmo = &ti->options->u.mssim;
+            monitor_printf(mon, ",command=%s:%s,control=%s:%s",
+                           tmo->command->u.inet.host,
+                           tmo->command->u.inet.port,
+                           tmo->control->u.inet.host,
+                           tmo->control->u.inet.port);
+            break;
         case TPM_TYPE__MAX:
             break;
         }
diff --git a/qapi/tpm.json b/qapi/tpm.json
index 2b491c28b4..f9dde35377 100644
--- a/qapi/tpm.json
+++ b/qapi/tpm.json
@@ -5,6 +5,7 @@ 
 ##
 # = TPM (trusted platform module) devices
 ##
+{ 'include': 'sockets.json' }
 
 ##
 # @TpmModel:
@@ -49,7 +50,7 @@ 
 #
 # Since: 1.5
 ##
-{ 'enum': 'TpmType', 'data': [ 'passthrough', 'emulator' ],
+{ 'enum': 'TpmType', 'data': [ 'passthrough', 'emulator', 'mssim' ],
   'if': 'CONFIG_TPM' }
 
 ##
@@ -64,7 +65,7 @@ 
 # Example:
 #
 # -> { "execute": "query-tpm-types" }
-# <- { "return": [ "passthrough", "emulator" ] }
+# <- { "return": [ "passthrough", "emulator", "mssim" ] }
 #
 ##
 { 'command': 'query-tpm-types', 'returns': ['TpmType'],
@@ -117,6 +118,22 @@ 
   'data': { 'data': 'TPMEmulatorOptions' },
   'if': 'CONFIG_TPM' }
 
+##
+# @TPMmssimOptions:
+#
+# Information for the mssim emulator connection
+#
+# @command: command socket for the TPM emulator
+# @control: control socket for the TPM emulator
+#
+# Since: 7.2.0
+##
+{ 'struct': 'TPMmssimOptions',
+  'data': {
+      '*command': 'SocketAddress',
+      '*control': 'SocketAddress' },
+  'if': 'CONFIG_TPM' }
+
 ##
 # @TpmTypeOptions:
 #
@@ -124,6 +141,7 @@ 
 #
 # @type: - 'passthrough' The configuration options for the TPM passthrough type
 #        - 'emulator' The configuration options for TPM emulator backend type
+#        - 'mssim' The configuration options for TPM emulator mssim type
 #
 # Since: 1.5
 ##
@@ -131,7 +149,8 @@ 
   'base': { 'type': 'TpmType' },
   'discriminator': 'type',
   'data': { 'passthrough' : 'TPMPassthroughOptionsWrapper',
-            'emulator': 'TPMEmulatorOptionsWrapper' },
+            'emulator': 'TPMEmulatorOptionsWrapper',
+            'mssim' : 'TPMmssimOptions' },
   'if': 'CONFIG_TPM' }
 
 ##
@@ -150,7 +169,8 @@ 
             'id' : 'str' },
   'discriminator': 'type',
   'data': { 'passthrough' : 'TPMPassthroughOptions',
-            'emulator': 'TPMEmulatorOptions' },
+            'emulator': 'TPMEmulatorOptions',
+            'mssim': 'TPMmssimOptions' },
   'if': 'CONFIG_TPM' }
 
 ##