diff options
Diffstat (limited to 'meta/recipes-devtools/qemu/qemu/0010-tpm-Added-support-for-TPM-emulator.patch')
-rw-r--r-- | meta/recipes-devtools/qemu/qemu/0010-tpm-Added-support-for-TPM-emulator.patch | 1059 |
1 files changed, 0 insertions, 1059 deletions
diff --git a/meta/recipes-devtools/qemu/qemu/0010-tpm-Added-support-for-TPM-emulator.patch b/meta/recipes-devtools/qemu/qemu/0010-tpm-Added-support-for-TPM-emulator.patch deleted file mode 100644 index 968e12e88a..0000000000 --- a/meta/recipes-devtools/qemu/qemu/0010-tpm-Added-support-for-TPM-emulator.patch +++ /dev/null @@ -1,1059 +0,0 @@ -From 70e73b7c6c7cf982d645db9c81c74588e6b10a2b Mon Sep 17 00:00:00 2001 -From: Amarnath Valluri <amarnath.valluri@intel.com> -Date: Wed, 29 Mar 2017 15:39:41 +0300 -Subject: [PATCH 10/12] tpm: Added support for TPM emulator - -This change introduces a new TPM backend driver that can communicate with -swtpm(software TPM emulator) using unix domain socket interface. QEMU talks to -TPM emulator using QEMU's socket-based chardev backend device. - -Swtpm uses two Unix sockets for communications, one for plain TPM commands and -responses, and one for out-of-band control messages. QEMU passes data socket to -be used over the control channel. - -The swtpm and associated tools can be found here: - https://github.com/stefanberger/swtpm - -The swtpm's control channel protocol specification can be found here: - https://github.com/stefanberger/swtpm/wiki/Control-Channel-Specification - -Usage: - # setup TPM state directory - mkdir /tmp/mytpm - chown -R tss:root /tmp/mytpm - /usr/bin/swtpm_setup --tpm-state /tmp/mytpm --createek - - # Ask qemu to use TPM emulator with given tpm state directory - qemu-system-x86_64 \ - [...] \ - -chardev socket,id=chrtpm,path=/tmp/swtpm-sock \ - -tpmdev emulator,id=tpm0,chardev=chrtpm \ - -device tpm-tis,tpmdev=tpm0 \ - [...] - -Signed-off-by: Amarnath Valluri <amarnath.valluri@intel.com> - -Upstream-Status: Backport [f4ede81eed29e6140374177d1f2808248c5b5650] ---- - configure | 13 +- - hmp.c | 5 + - hw/tpm/Makefile.objs | 1 + - hw/tpm/tpm_emulator.c | 583 ++++++++++++++++++++++++++++++++++++++++++++++++++ - hw/tpm/tpm_ioctl.h | 246 +++++++++++++++++++++ - qapi-schema.json | 18 +- - qemu-options.hx | 22 +- - 7 files changed, 882 insertions(+), 6 deletions(-) - create mode 100644 hw/tpm/tpm_emulator.c - create mode 100644 hw/tpm/tpm_ioctl.h - -diff --git a/configure b/configure -index dd73cce62f..9a25537096 100755 ---- a/configure -+++ b/configure -@@ -3503,6 +3503,12 @@ else - tpm_passthrough=no - fi - -+# TPM emulator is for all posix systems -+if test "$mingw32" != "yes"; then -+ tpm_emulator=$tpm -+else -+ tpm_emulator=no -+fi - ########################################## - # attr probe - -@@ -5396,6 +5402,7 @@ echo "gcov enabled $gcov" - echo "TPM support $tpm" - echo "libssh2 support $libssh2" - echo "TPM passthrough $tpm_passthrough" -+echo "TPM emulator $tpm_emulator" - echo "QOM debugging $qom_cast_debug" - echo "Live block migration $live_block_migration" - echo "lzo support $lzo" -@@ -5983,12 +5990,16 @@ else - echo "HOST_USB=stub" >> $config_host_mak - fi - --# TPM passthrough support? - if test "$tpm" = "yes"; then - echo 'CONFIG_TPM=$(CONFIG_SOFTMMU)' >> $config_host_mak -+ # TPM passthrough support? - if test "$tpm_passthrough" = "yes"; then - echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_host_mak - fi -+ # TPM emulator support? -+ if test "$tpm_emulator" = "yes"; then -+ echo "CONFIG_TPM_EMULATOR=y" >> $config_host_mak -+ fi - fi - - echo "TRACE_BACKENDS=$trace_backends" >> $config_host_mak -diff --git a/hmp.c b/hmp.c -index fd80dce758..820aa8f002 100644 ---- a/hmp.c -+++ b/hmp.c -@@ -995,6 +995,7 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict) - Error *err = NULL; - unsigned int c = 0; - TPMPassthroughOptions *tpo; -+ TPMEmulatorOptions *teo; - - info_list = qmp_query_tpm(&err); - if (err) { -@@ -1024,6 +1025,10 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict) - tpo->has_cancel_path ? ",cancel-path=" : "", - tpo->has_cancel_path ? tpo->cancel_path : ""); - break; -+ case TPM_TYPE_OPTIONS_KIND_EMULATOR: -+ teo = ti->options->u.emulator.data; -+ monitor_printf(mon, ",chardev=%s", teo->chardev); -+ break; - case TPM_TYPE_OPTIONS_KIND__MAX: - break; - } -diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs -index 64cecc3b67..41f0b7a590 100644 ---- a/hw/tpm/Makefile.objs -+++ b/hw/tpm/Makefile.objs -@@ -1,2 +1,3 @@ - common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o - common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o tpm_util.o -+common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o tpm_util.o -diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c -new file mode 100644 -index 0000000000..433bc4fa8a ---- /dev/null -+++ b/hw/tpm/tpm_emulator.c -@@ -0,0 +1,583 @@ -+/* -+ * Emulator TPM driver -+ * -+ * Copyright (c) 2017 Intel Corporation -+ * Author: Amarnath Valluri <amarnath.valluri@intel.com> -+ * -+ * Copyright (c) 2010 - 2013 IBM Corporation -+ * Authors: -+ * Stefan Berger <stefanb@us.ibm.com> -+ * -+ * Copyright (C) 2011 IAIK, Graz University of Technology -+ * Author: Andreas Niederl -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation; either -+ * version 2 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, see <http://www.gnu.org/licenses/> -+ * -+ */ -+ -+#include "qemu/osdep.h" -+#include "qemu/error-report.h" -+#include "qemu/sockets.h" -+#include "io/channel-socket.h" -+#include "sysemu/tpm_backend.h" -+#include "tpm_int.h" -+#include "hw/hw.h" -+#include "hw/i386/pc.h" -+#include "tpm_util.h" -+#include "tpm_ioctl.h" -+#include "migration/blocker.h" -+#include "qapi/error.h" -+#include "qapi/clone-visitor.h" -+#include "chardev/char-fe.h" -+ -+#include <fcntl.h> -+#include <sys/types.h> -+#include <sys/stat.h> -+#include <stdio.h> -+ -+#define DEBUG_TPM 0 -+ -+#define DPRINTF(fmt, ...) do { \ -+ if (DEBUG_TPM) { \ -+ fprintf(stderr, "tpm-emulator:"fmt"\n", ## __VA_ARGS__); \ -+ } \ -+} while (0) -+ -+#define TYPE_TPM_EMULATOR "tpm-emulator" -+#define TPM_EMULATOR(obj) \ -+ OBJECT_CHECK(TPMEmulator, (obj), TYPE_TPM_EMULATOR) -+ -+#define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps & (cap)) == (cap)) -+ -+static const TPMDriverOps tpm_emulator_driver; -+ -+/* data structures */ -+typedef struct TPMEmulator { -+ TPMBackend parent; -+ -+ TPMEmulatorOptions *options; -+ CharBackend ctrl_chr; -+ QIOChannel *data_ioc; -+ TPMVersion tpm_version; -+ ptm_cap caps; /* capabilities of the TPM */ -+ uint8_t cur_locty_number; /* last set locality */ -+ Error *migration_blocker; -+} TPMEmulator; -+ -+ -+static int tpm_emulator_ctrlcmd(CharBackend *dev, unsigned long cmd, void *msg, -+ size_t msg_len_in, size_t msg_len_out) -+{ -+ uint32_t cmd_no = cpu_to_be32(cmd); -+ ssize_t n = sizeof(uint32_t) + msg_len_in; -+ uint8_t *buf = NULL; -+ -+ buf = g_alloca(n); -+ memcpy(buf, &cmd_no, sizeof(cmd_no)); -+ memcpy(buf + sizeof(cmd_no), msg, msg_len_in); -+ -+ n = qemu_chr_fe_write_all(dev, buf, n); -+ if (n <= 0) { -+ return -1; -+ } -+ -+ if (msg_len_out != 0) { -+ n = qemu_chr_fe_read_all(dev, msg, msg_len_out); -+ if (n <= 0) { -+ return -1; -+ } -+ } -+ -+ return 0; -+} -+ -+static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu, -+ const uint8_t *in, uint32_t in_len, -+ uint8_t *out, uint32_t out_len, -+ bool *selftest_done, -+ Error **err) -+{ -+ ssize_t ret; -+ bool is_selftest = false; -+ const struct tpm_resp_hdr *hdr = NULL; -+ -+ if (selftest_done) { -+ *selftest_done = false; -+ is_selftest = tpm_util_is_selftest(in, in_len); -+ } -+ -+ ret = qio_channel_write(tpm_emu->data_ioc, (char *)in, in_len, err); -+ if (ret != in_len) { -+ return -1; -+ } -+ -+ ret = qio_channel_read(tpm_emu->data_ioc, (char *)out, out_len, err); -+ if (ret <= 0 || ret < sizeof(*hdr)) { -+ return -1; -+ } -+ -+ hdr = (struct tpm_resp_hdr *)out; -+ if (be32_to_cpu(hdr->len) != ret) { -+ return -1; -+ } -+ -+ if (is_selftest) { -+ *selftest_done = (be32_to_cpu(hdr->errcode) == 0); -+ } -+ -+ return 0; -+} -+ -+static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number) -+{ -+ ptm_loc loc; -+ -+ DPRINTF("%s : locality: 0x%x", __func__, locty_number); -+ -+ if (tpm_emu->cur_locty_number == locty_number) { -+ return 0; -+ } -+ -+ DPRINTF("setting locality : 0x%x", locty_number); -+ loc.u.req.loc = locty_number; -+ if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_SET_LOCALITY, &loc, -+ sizeof(loc), sizeof(loc)) < 0) { -+ error_report("tpm-emulator: could not set locality : %s", -+ strerror(errno)); -+ return -1; -+ } -+ -+ loc.u.resp.tpm_result = be32_to_cpu(loc.u.resp.tpm_result); -+ if (loc.u.resp.tpm_result != 0) { -+ error_report("tpm-emulator: TPM result for set locality : 0x%x", -+ loc.u.resp.tpm_result); -+ return -1; -+ } -+ -+ tpm_emu->cur_locty_number = locty_number; -+ -+ return 0; -+} -+ -+static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd cmd) -+{ -+ TPMEmulator *tpm_emu = TPM_EMULATOR(tb); -+ TPMLocality *locty = NULL; -+ bool selftest_done = false; -+ Error *err = NULL; -+ -+ DPRINTF("processing command type %d", cmd); -+ -+ switch (cmd) { -+ case TPM_BACKEND_CMD_PROCESS_CMD: -+ locty = tb->tpm_state->locty_data; -+ if (tpm_emulator_set_locality(tpm_emu, -+ tb->tpm_state->locty_number) < 0 || -+ tpm_emulator_unix_tx_bufs(tpm_emu, locty->w_buffer.buffer, -+ locty->w_offset, locty->r_buffer.buffer, -+ locty->r_buffer.size, &selftest_done, -+ &err) < 0) { -+ tpm_util_write_fatal_error_response(locty->r_buffer.buffer, -+ locty->r_buffer.size); -+ error_report_err(err); -+ } -+ -+ tb->recv_data_callback(tb->tpm_state, tb->tpm_state->locty_number, -+ selftest_done); -+ -+ break; -+ case TPM_BACKEND_CMD_INIT: -+ case TPM_BACKEND_CMD_END: -+ case TPM_BACKEND_CMD_TPM_RESET: -+ /* nothing to do */ -+ break; -+ } -+} -+ -+static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu) -+{ -+ DPRINTF("%s", __func__); -+ if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_GET_CAPABILITY, -+ &tpm_emu->caps, 0, sizeof(tpm_emu->caps)) < 0) { -+ error_report("tpm-emulator: probing failed : %s", strerror(errno)); -+ return -1; -+ } -+ -+ tpm_emu->caps = be64_to_cpu(tpm_emu->caps); -+ -+ DPRINTF("capbilities : 0x%lx", tpm_emu->caps); -+ -+ return 0; -+} -+ -+static int tpm_emulator_check_caps(TPMEmulator *tpm_emu) -+{ -+ ptm_cap caps = 0; -+ const char *tpm = NULL; -+ -+ /* check for min. required capabilities */ -+ switch (tpm_emu->tpm_version) { -+ case TPM_VERSION_1_2: -+ caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED | -+ PTM_CAP_SET_LOCALITY | PTM_CAP_SET_DATAFD; -+ tpm = "1.2"; -+ break; -+ case TPM_VERSION_2_0: -+ caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED | -+ PTM_CAP_SET_LOCALITY | PTM_CAP_RESET_TPMESTABLISHED | -+ PTM_CAP_SET_DATAFD; -+ tpm = "2"; -+ break; -+ case TPM_VERSION_UNSPEC: -+ error_report("tpm-emulator: TPM version has not been set"); -+ return -1; -+ } -+ -+ if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, caps)) { -+ error_report("tpm-emulator: TPM does not implement minimum set of " -+ "required capabilities for TPM %s (0x%x)", tpm, (int)caps); -+ return -1; -+ } -+ -+ return 0; -+} -+ -+static int tpm_emulator_startup_tpm(TPMBackend *tb) -+{ -+ TPMEmulator *tpm_emu = TPM_EMULATOR(tb); -+ ptm_init init; -+ ptm_res res; -+ -+ DPRINTF("%s", __func__); -+ if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_INIT, &init, sizeof(init), -+ sizeof(init)) < 0) { -+ error_report("tpm-emulator: could not send INIT: %s", -+ strerror(errno)); -+ goto err_exit; -+ } -+ -+ res = be32_to_cpu(init.u.resp.tpm_result); -+ if (res) { -+ error_report("tpm-emulator: TPM result for CMD_INIT: 0x%x", res); -+ goto err_exit; -+ } -+ return 0; -+ -+err_exit: -+ return -1; -+} -+ -+static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb) -+{ -+ TPMEmulator *tpm_emu = TPM_EMULATOR(tb); -+ ptm_est est; -+ -+ DPRINTF("%s", __func__); -+ if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_GET_TPMESTABLISHED, &est, -+ 0, sizeof(est)) < 0) { -+ error_report("tpm-emulator: Could not get the TPM established flag: %s", -+ strerror(errno)); -+ return false; -+ } -+ DPRINTF("established flag: %0x", est.u.resp.bit); -+ -+ return (est.u.resp.bit != 0); -+} -+ -+static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb, -+ uint8_t locty) -+{ -+ TPMEmulator *tpm_emu = TPM_EMULATOR(tb); -+ ptm_reset_est reset_est; -+ ptm_res res; -+ -+ /* only a TPM 2.0 will support this */ -+ if (tpm_emu->tpm_version != TPM_VERSION_2_0) { -+ return 0; -+ } -+ -+ reset_est.u.req.loc = tpm_emu->cur_locty_number; -+ if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_RESET_TPMESTABLISHED, -+ &reset_est, sizeof(reset_est), -+ sizeof(reset_est)) < 0) { -+ error_report("tpm-emulator: Could not reset the establishment bit: %s", -+ strerror(errno)); -+ return -1; -+ } -+ -+ res = be32_to_cpu(reset_est.u.resp.tpm_result); -+ if (res) { -+ error_report("tpm-emulator: TPM result for rest establixhed flag: 0x%x", -+ res); -+ return -1; -+ } -+ -+ return 0; -+} -+ -+static void tpm_emulator_cancel_cmd(TPMBackend *tb) -+{ -+ TPMEmulator *tpm_emu = TPM_EMULATOR(tb); -+ ptm_res res; -+ -+ if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, PTM_CAP_CANCEL_TPM_CMD)) { -+ DPRINTF("Backend does not support CANCEL_TPM_CMD"); -+ return; -+ } -+ -+ if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_CANCEL_TPM_CMD, &res, 0, -+ sizeof(res)) < 0) { -+ error_report("tpm-emulator: Could not cancel command: %s", -+ strerror(errno)); -+ } else if (res != 0) { -+ error_report("tpm-emulator: Failed to cancel TPM: 0x%x", -+ be32_to_cpu(res)); -+ } -+} -+ -+static TPMVersion tpm_emulator_get_tpm_version(TPMBackend *tb) -+{ -+ TPMEmulator *tpm_emu = TPM_EMULATOR(tb); -+ -+ return tpm_emu->tpm_version; -+} -+ -+static int tpm_emulator_block_migration(TPMEmulator *tpm_emu) -+{ -+ Error *err = NULL; -+ -+ error_setg(&tpm_emu->migration_blocker, -+ "Migration disabled: TPM emulator not yet migratable"); -+ migrate_add_blocker(tpm_emu->migration_blocker, &err); -+ if (err) { -+ error_report_err(err); -+ error_free(tpm_emu->migration_blocker); -+ tpm_emu->migration_blocker = NULL; -+ -+ return -1; -+ } -+ -+ return 0; -+} -+ -+static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_emu) -+{ -+ ptm_res res; -+ Error *err = NULL; -+ int fds[2] = { -1, -1 }; -+ -+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { -+ error_report("tpm-emulator: Failed to create socketpair"); -+ return -1; -+ } -+ -+ qemu_chr_fe_set_msgfds(&tpm_emu->ctrl_chr, fds + 1, 1); -+ -+ if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_SET_DATAFD, &res, 0, -+ sizeof(res)) || res != 0) { -+ error_report("tpm-emulator: Failed to send CMD_SET_DATAFD: %s", -+ strerror(errno)); -+ goto err_exit; -+ } -+ -+ tpm_emu->data_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fds[0], &err)); -+ if (err) { -+ error_prepend(&err, "tpm-emulator: Failed to create io channel: "); -+ error_report_err(err); -+ goto err_exit; -+ } -+ -+ closesocket(fds[1]); -+ -+ return 0; -+ -+err_exit: -+ closesocket(fds[0]); -+ closesocket(fds[1]); -+ return -1; -+} -+ -+static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_emu, QemuOpts *opts) -+{ -+ const char *value; -+ -+ value = qemu_opt_get(opts, "chardev"); -+ if (value) { -+ Error *err = NULL; -+ Chardev *dev = qemu_chr_find(value); -+ -+ if (!dev) { -+ error_report("tpm-emulator: tpm chardev '%s' not found.", value); -+ goto err; -+ } -+ -+ if (!qemu_chr_fe_init(&tpm_emu->ctrl_chr, dev, &err)) { -+ error_prepend(&err, "tpm-emulator: No valid chardev found at '%s':", -+ value); -+ error_report_err(err); -+ goto err; -+ } -+ -+ tpm_emu->options->chardev = g_strdup(value); -+ } -+ -+ if (tpm_emulator_prepare_data_fd(tpm_emu) < 0) { -+ goto err; -+ } -+ -+ /* FIXME: tpm_util_test_tpmdev() accepts only on socket fd, as it also used -+ * by passthrough driver, which not yet using GIOChannel. -+ */ -+ if (tpm_util_test_tpmdev(QIO_CHANNEL_SOCKET(tpm_emu->data_ioc)->fd, -+ &tpm_emu->tpm_version)) { -+ error_report("'%s' is not emulating TPM device. Error: %s", -+ tpm_emu->options->chardev, strerror(errno)); -+ goto err; -+ } -+ -+ DPRINTF("TPM Version %s", tpm_emu->tpm_version == TPM_VERSION_1_2 ? "1.2" : -+ (tpm_emu->tpm_version == TPM_VERSION_2_0 ? "2.0" : "Unspecified")); -+ -+ if (tpm_emulator_probe_caps(tpm_emu) || -+ tpm_emulator_check_caps(tpm_emu)) { -+ goto err; -+ } -+ -+ return tpm_emulator_block_migration(tpm_emu); -+ -+err: -+ DPRINTF("Startup error"); -+ return -1; -+} -+ -+static TPMBackend *tpm_emulator_create(QemuOpts *opts, const char *id) -+{ -+ TPMBackend *tb = TPM_BACKEND(object_new(TYPE_TPM_EMULATOR)); -+ -+ tb->id = g_strdup(id); -+ -+ if (tpm_emulator_handle_device_opts(TPM_EMULATOR(tb), opts)) { -+ goto err_exit; -+ } -+ -+ return tb; -+ -+err_exit: -+ object_unref(OBJECT(tb)); -+ -+ return NULL; -+} -+ -+static TpmTypeOptions *tpm_emulator_get_tpm_options(TPMBackend *tb) -+{ -+ TPMEmulator *tpm_emu = TPM_EMULATOR(tb); -+ TpmTypeOptions *options = g_new0(TpmTypeOptions, 1); -+ -+ options->type = TPM_TYPE_OPTIONS_KIND_EMULATOR; -+ options->u.emulator.data = QAPI_CLONE(TPMEmulatorOptions, tpm_emu->options); -+ -+ return options; -+} -+ -+static const QemuOptDesc tpm_emulator_cmdline_opts[] = { -+ TPM_STANDARD_CMDLINE_OPTS, -+ { -+ .name = "chardev", -+ .type = QEMU_OPT_STRING, -+ .help = "Character device to use for out-of-band control messages", -+ }, -+ { /* end of list */ }, -+}; -+ -+static const TPMDriverOps tpm_emulator_driver = { -+ .type = TPM_TYPE_EMULATOR, -+ .opts = tpm_emulator_cmdline_opts, -+ .desc = "TPM emulator backend driver", -+ -+ .create = tpm_emulator_create, -+ .startup_tpm = tpm_emulator_startup_tpm, -+ .cancel_cmd = tpm_emulator_cancel_cmd, -+ .get_tpm_established_flag = tpm_emulator_get_tpm_established_flag, -+ .reset_tpm_established_flag = tpm_emulator_reset_tpm_established_flag, -+ .get_tpm_version = tpm_emulator_get_tpm_version, -+ .get_tpm_options = tpm_emulator_get_tpm_options, -+}; -+ -+static void tpm_emulator_inst_init(Object *obj) -+{ -+ TPMEmulator *tpm_emu = TPM_EMULATOR(obj); -+ -+ DPRINTF("%s", __func__); -+ tpm_emu->options = g_new0(TPMEmulatorOptions, 1); -+ tpm_emu->cur_locty_number = ~0; -+} -+ -+/* -+ * Gracefully shut down the external TPM -+ */ -+static void tpm_emulator_shutdown(TPMEmulator *tpm_emu) -+{ -+ ptm_res res; -+ -+ if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_SHUTDOWN, &res, 0, -+ sizeof(res)) < 0) { -+ error_report("tpm-emulator: Could not cleanly shutdown the TPM: %s", -+ strerror(errno)); -+ } else if (res != 0) { -+ error_report("tpm-emulator: TPM result for sutdown: 0x%x", -+ be32_to_cpu(res)); -+ } -+} -+ -+static void tpm_emulator_inst_finalize(Object *obj) -+{ -+ TPMEmulator *tpm_emu = TPM_EMULATOR(obj); -+ -+ tpm_emulator_shutdown(tpm_emu); -+ -+ object_unref(OBJECT(tpm_emu->data_ioc)); -+ -+ qemu_chr_fe_deinit(&tpm_emu->ctrl_chr, false); -+ -+ qapi_free_TPMEmulatorOptions(tpm_emu->options); -+ -+ if (tpm_emu->migration_blocker) { -+ migrate_del_blocker(tpm_emu->migration_blocker); -+ error_free(tpm_emu->migration_blocker); -+ } -+} -+ -+static void tpm_emulator_class_init(ObjectClass *klass, void *data) -+{ -+ TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass); -+ tbc->ops = &tpm_emulator_driver; -+ tbc->handle_request = tpm_emulator_handle_request; -+} -+ -+static const TypeInfo tpm_emulator_info = { -+ .name = TYPE_TPM_EMULATOR, -+ .parent = TYPE_TPM_BACKEND, -+ .instance_size = sizeof(TPMEmulator), -+ .class_init = tpm_emulator_class_init, -+ .instance_init = tpm_emulator_inst_init, -+ .instance_finalize = tpm_emulator_inst_finalize, -+}; -+ -+static void tpm_emulator_register(void) -+{ -+ type_register_static(&tpm_emulator_info); -+ tpm_register_driver(&tpm_emulator_driver); -+} -+ -+type_init(tpm_emulator_register) -diff --git a/hw/tpm/tpm_ioctl.h b/hw/tpm/tpm_ioctl.h -new file mode 100644 -index 0000000000..33564b11de ---- /dev/null -+++ b/hw/tpm/tpm_ioctl.h -@@ -0,0 +1,246 @@ -+/* -+ * tpm_ioctl.h -+ * -+ * (c) Copyright IBM Corporation 2014, 2015. -+ * -+ * This file is licensed under the terms of the 3-clause BSD license -+ */ -+#ifndef _TPM_IOCTL_H_ -+#define _TPM_IOCTL_H_ -+ -+#include <stdint.h> -+#include <sys/uio.h> -+#include <sys/types.h> -+#include <sys/ioctl.h> -+ -+/* -+ * Every response from a command involving a TPM command execution must hold -+ * the ptm_res as the first element. -+ * ptm_res corresponds to the error code of a command executed by the TPM. -+ */ -+ -+typedef uint32_t ptm_res; -+ -+/* PTM_GET_TPMESTABLISHED: get the establishment bit */ -+struct ptm_est { -+ union { -+ struct { -+ ptm_res tpm_result; -+ unsigned char bit; /* TPM established bit */ -+ } resp; /* response */ -+ } u; -+}; -+ -+/* PTM_RESET_TPMESTABLISHED: reset establishment bit */ -+struct ptm_reset_est { -+ union { -+ struct { -+ uint8_t loc; /* locality to use */ -+ } req; /* request */ -+ struct { -+ ptm_res tpm_result; -+ } resp; /* response */ -+ } u; -+}; -+ -+/* PTM_INIT */ -+struct ptm_init { -+ union { -+ struct { -+ uint32_t init_flags; /* see definitions below */ -+ } req; /* request */ -+ struct { -+ ptm_res tpm_result; -+ } resp; /* response */ -+ } u; -+}; -+ -+/* above init_flags */ -+#define PTM_INIT_FLAG_DELETE_VOLATILE (1 << 0) -+ /* delete volatile state file after reading it */ -+ -+/* PTM_SET_LOCALITY */ -+struct ptm_loc { -+ union { -+ struct { -+ uint8_t loc; /* locality to set */ -+ } req; /* request */ -+ struct { -+ ptm_res tpm_result; -+ } resp; /* response */ -+ } u; -+}; -+ -+/* PTM_HASH_DATA: hash given data */ -+struct ptm_hdata { -+ union { -+ struct { -+ uint32_t length; -+ uint8_t data[4096]; -+ } req; /* request */ -+ struct { -+ ptm_res tpm_result; -+ } resp; /* response */ -+ } u; -+}; -+ -+/* -+ * size of the TPM state blob to transfer; x86_64 can handle 8k, -+ * ppc64le only ~7k; keep the response below a 4k page size -+ */ -+#define PTM_STATE_BLOB_SIZE (3 * 1024) -+ -+/* -+ * The following is the data structure to get state blobs from the TPM. -+ * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple reads -+ * with this ioctl and with adjusted offset are necessary. All bytes -+ * must be transferred and the transfer is done once the last byte has been -+ * returned. -+ * It is possible to use the read() interface for reading the data; however, the -+ * first bytes of the state blob will be part of the response to the ioctl(); a -+ * subsequent read() is only necessary if the total length (totlength) exceeds -+ * the number of received bytes. seek() is not supported. -+ */ -+struct ptm_getstate { -+ union { -+ struct { -+ uint32_t state_flags; /* may be: PTM_STATE_FLAG_DECRYPTED */ -+ uint32_t type; /* which blob to pull */ -+ uint32_t offset; /* offset from where to read */ -+ } req; /* request */ -+ struct { -+ ptm_res tpm_result; -+ uint32_t state_flags; /* may be: PTM_STATE_FLAG_ENCRYPTED */ -+ uint32_t totlength; /* total length that will be transferred */ -+ uint32_t length; /* number of bytes in following buffer */ -+ uint8_t data[PTM_STATE_BLOB_SIZE]; -+ } resp; /* response */ -+ } u; -+}; -+ -+/* TPM state blob types */ -+#define PTM_BLOB_TYPE_PERMANENT 1 -+#define PTM_BLOB_TYPE_VOLATILE 2 -+#define PTM_BLOB_TYPE_SAVESTATE 3 -+ -+/* state_flags above : */ -+#define PTM_STATE_FLAG_DECRYPTED 1 /* on input: get decrypted state */ -+#define PTM_STATE_FLAG_ENCRYPTED 2 /* on output: state is encrypted */ -+ -+/* -+ * The following is the data structure to set state blobs in the TPM. -+ * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple -+ * 'writes' using this ioctl are necessary. The last packet is indicated -+ * by the length being smaller than the PTM_STATE_BLOB_SIZE. -+ * The very first packet may have a length indicator of '0' enabling -+ * a write() with all the bytes from a buffer. If the write() interface -+ * is used, a final ioctl with a non-full buffer must be made to indicate -+ * that all data were transferred (a write with 0 bytes would not work). -+ */ -+struct ptm_setstate { -+ union { -+ struct { -+ uint32_t state_flags; /* may be PTM_STATE_FLAG_ENCRYPTED */ -+ uint32_t type; /* which blob to set */ -+ uint32_t length; /* length of the data; -+ use 0 on the first packet to -+ transfer using write() */ -+ uint8_t data[PTM_STATE_BLOB_SIZE]; -+ } req; /* request */ -+ struct { -+ ptm_res tpm_result; -+ } resp; /* response */ -+ } u; -+}; -+ -+/* -+ * PTM_GET_CONFIG: Data structure to get runtime configuration information -+ * such as which keys are applied. -+ */ -+struct ptm_getconfig { -+ union { -+ struct { -+ ptm_res tpm_result; -+ uint32_t flags; -+ } resp; /* response */ -+ } u; -+}; -+ -+#define PTM_CONFIG_FLAG_FILE_KEY 0x1 -+#define PTM_CONFIG_FLAG_MIGRATION_KEY 0x2 -+ -+ -+typedef uint64_t ptm_cap; -+typedef struct ptm_est ptm_est; -+typedef struct ptm_reset_est ptm_reset_est; -+typedef struct ptm_loc ptm_loc; -+typedef struct ptm_hdata ptm_hdata; -+typedef struct ptm_init ptm_init; -+typedef struct ptm_getstate ptm_getstate; -+typedef struct ptm_setstate ptm_setstate; -+typedef struct ptm_getconfig ptm_getconfig; -+ -+/* capability flags returned by PTM_GET_CAPABILITY */ -+#define PTM_CAP_INIT (1) -+#define PTM_CAP_SHUTDOWN (1 << 1) -+#define PTM_CAP_GET_TPMESTABLISHED (1 << 2) -+#define PTM_CAP_SET_LOCALITY (1 << 3) -+#define PTM_CAP_HASHING (1 << 4) -+#define PTM_CAP_CANCEL_TPM_CMD (1 << 5) -+#define PTM_CAP_STORE_VOLATILE (1 << 6) -+#define PTM_CAP_RESET_TPMESTABLISHED (1 << 7) -+#define PTM_CAP_GET_STATEBLOB (1 << 8) -+#define PTM_CAP_SET_STATEBLOB (1 << 9) -+#define PTM_CAP_STOP (1 << 10) -+#define PTM_CAP_GET_CONFIG (1 << 11) -+#define PTM_CAP_SET_DATAFD (1 << 12) -+ -+enum { -+ PTM_GET_CAPABILITY = _IOR('P', 0, ptm_cap), -+ PTM_INIT = _IOWR('P', 1, ptm_init), -+ PTM_SHUTDOWN = _IOR('P', 2, ptm_res), -+ PTM_GET_TPMESTABLISHED = _IOR('P', 3, ptm_est), -+ PTM_SET_LOCALITY = _IOWR('P', 4, ptm_loc), -+ PTM_HASH_START = _IOR('P', 5, ptm_res), -+ PTM_HASH_DATA = _IOWR('P', 6, ptm_hdata), -+ PTM_HASH_END = _IOR('P', 7, ptm_res), -+ PTM_CANCEL_TPM_CMD = _IOR('P', 8, ptm_res), -+ PTM_STORE_VOLATILE = _IOR('P', 9, ptm_res), -+ PTM_RESET_TPMESTABLISHED = _IOWR('P', 10, ptm_reset_est), -+ PTM_GET_STATEBLOB = _IOWR('P', 11, ptm_getstate), -+ PTM_SET_STATEBLOB = _IOWR('P', 12, ptm_setstate), -+ PTM_STOP = _IOR('P', 13, ptm_res), -+ PTM_GET_CONFIG = _IOR('P', 14, ptm_getconfig), -+ PTM_SET_DATAFD = _IOR('P', 15, ptm_res), -+}; -+ -+/* -+ * Commands used by the non-CUSE TPMs -+ * -+ * All messages container big-endian data. -+ * -+ * The return messages only contain the 'resp' part of the unions -+ * in the data structures above. Besides that the limits in the -+ * buffers above (ptm_hdata:u.req.data and ptm_get_state:u.resp.data -+ * and ptm_set_state:u.req.data) are 0xffffffff. -+ */ -+enum { -+ CMD_GET_CAPABILITY = 1, -+ CMD_INIT, -+ CMD_SHUTDOWN, -+ CMD_GET_TPMESTABLISHED, -+ CMD_SET_LOCALITY, -+ CMD_HASH_START, -+ CMD_HASH_DATA, -+ CMD_HASH_END, -+ CMD_CANCEL_TPM_CMD, -+ CMD_STORE_VOLATILE, -+ CMD_RESET_TPMESTABLISHED, -+ CMD_GET_STATEBLOB, -+ CMD_SET_STATEBLOB, -+ CMD_STOP, -+ CMD_GET_CONFIG, -+ CMD_SET_DATAFD -+}; -+ -+#endif /* _TPM_IOCTL_H */ -diff --git a/qapi-schema.json b/qapi-schema.json -index 802ea53d00..78a00bc868 100644 ---- a/qapi-schema.json -+++ b/qapi-schema.json -@@ -5314,10 +5314,12 @@ - # An enumeration of TPM types - # - # @passthrough: TPM passthrough type -+# @emulator: Software Emulator TPM type -+# Since: 2.11 - # - # Since: 1.5 - ## --{ 'enum': 'TpmType', 'data': [ 'passthrough' ] } -+{ 'enum': 'TpmType', 'data': [ 'passthrough', 'emulator' ] } - - ## - # @query-tpm-types: -@@ -5352,6 +5354,17 @@ - '*cancel-path' : 'str'} } - - ## -+# @TPMEmulatorOptions: -+# -+# Information about the TPM emulator type -+# -+# @chardev: Name of a unix socket chardev -+# -+# Since: 2.11 -+## -+{ 'struct': 'TPMEmulatorOptions', 'data': { 'chardev' : 'str' } } -+ -+## - # @TpmTypeOptions: - # - # A union referencing different TPM backend types' configuration options -@@ -5361,7 +5374,8 @@ - # Since: 1.5 - ## - { 'union': 'TpmTypeOptions', -- 'data': { 'passthrough' : 'TPMPassthroughOptions' } } -+ 'data': { 'passthrough' : 'TPMPassthroughOptions', -+ 'emulator': 'TPMEmulatorOptions'} } - - ## - # @TPMInfo: -diff --git a/qemu-options.hx b/qemu-options.hx -index 9f6e2adfff..60eb193c23 100644 ---- a/qemu-options.hx -+++ b/qemu-options.hx -@@ -3121,7 +3121,9 @@ DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \ - "-tpmdev passthrough,id=id[,path=path][,cancel-path=path]\n" - " use path to provide path to a character device; default is /dev/tpm0\n" - " use cancel-path to provide path to TPM's cancel sysfs entry; if\n" -- " not provided it will be searched for in /sys/class/misc/tpm?/device\n", -+ " not provided it will be searched for in /sys/class/misc/tpm?/device\n" -+ "-tpmdev emulator,id=id,chardev=dev\n" -+ " configure the TPM device using chardev backend\n", - QEMU_ARCH_ALL) - STEXI - -@@ -3130,8 +3132,8 @@ The general form of a TPM device option is: - - @item -tpmdev @var{backend} ,id=@var{id} [,@var{options}] - @findex -tpmdev --Backend type must be: --@option{passthrough}. -+Backend type must be either one of the following: -+@option{passthrough}, @option{emulator}. - - The specific backend type will determine the applicable options. - The @code{-tpmdev} option creates the TPM backend and requires a -@@ -3181,6 +3183,20 @@ To create a passthrough TPM use the following two options: - Note that the @code{-tpmdev} id is @code{tpm0} and is referenced by - @code{tpmdev=tpm0} in the device option. - -+@item -tpmdev emulator, id=@var{id}, chardev=@var{dev} -+ -+(Linux-host only) Enable access to a TPM emulator using Unix domain socket based -+chardev backend. -+ -+@option{chardev} specifies the unique ID of a character device backend that provides connection to the software TPM server. -+ -+To create a TPM emulator backend device with chardev socket backend: -+@example -+ -+-chardev socket,id=chrtpm,path=/tmp/swtpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0 -+ -+@end example -+ - @end table - - ETEXI --- -2.11.0 - |