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, 1059 insertions, 0 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 new file mode 100644 index 0000000000..968e12e88a --- /dev/null +++ b/meta/recipes-devtools/qemu/qemu/0010-tpm-Added-support-for-TPM-emulator.patch @@ -0,0 +1,1059 @@ +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 + |