@@ -3043,9 +3043,15 @@ 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*
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
@@ -12,3 +12,8 @@ config TPM_EMULATOR
bool
default y
depends on TPM_BACKEND
+
+config TPM_MSSIM
+ bool
+ default y
+ depends on TPM_BACKEND
@@ -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
new file mode 100644
@@ -0,0 +1,265 @@
+/*
+ * 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\n", 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->ctrl_qc && !runstate_check(RUN_STATE_INMIGRATE))
+ 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->has_command) {
+ mo->has_command = true;
+ 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->has_control) {
+ int port;
+
+ mo->has_control = true;
+ 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)
new file mode 100644
@@ -0,0 +1,43 @@
+/*
+ * 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
@@ -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
@@ -841,6 +841,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) {
@@ -874,6 +875,12 @@ 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;
}
@@ -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' }
##