[{"id":1776978,"web_url":"http://patchwork.ozlabs.org/comment/1776978/","msgid":"<CAJ+F1C+u4ggH1feJTqzC5dZYfKG0=4H5ihBtDDgiX8UYowfbuA@mail.gmail.com>","list_archive_url":null,"date":"2017-09-28T11:53:39","subject":"Re: [Qemu-devel] [PATCH v9 8/8] tpm: Added support for TPM emulator","submitter":{"id":6442,"url":"http://patchwork.ozlabs.org/api/people/6442/","name":"Marc-André Lureau","email":"marcandre.lureau@gmail.com"},"content":"Hi\n\nOn Thu, Sep 28, 2017 at 11:20 AM, Amarnath Valluri\n<amarnath.valluri@intel.com> wrote:\n> This change introduces a new TPM backend driver that can communicate with\n> swtpm(software TPM emulator) using unix domain socket interface. QEMU talks to\n> TPM emulator using QEMU's socket-based chardev backend device.\n>\n> Swtpm uses two Unix sockets for communications, one for plain TPM commands and\n> responses, and one for out-of-band control messages. QEMU passes data socket to\n> be used over the control channel.\n>\n> The swtpm and associated tools can be found here:\n>     https://github.com/stefanberger/swtpm\n>\n> The swtpm's control channel protocol specification can be found here:\n>     https://github.com/stefanberger/swtpm/wiki/Control-Channel-Specification\n>\n> Usage:\n>     # setup TPM state directory\n>     mkdir /tmp/mytpm\n>     chown -R tss:root /tmp/mytpm\n>     /usr/bin/swtpm_setup --tpm-state /tmp/mytpm --createek\n>\n>     # Ask qemu to use TPM emulator with given tpm state directory\n>     qemu-system-x86_64 \\\n>         [...] \\\n>         -chardev socket,id=chrtpm,path=/tmp/swtpm-sock \\\n>         -tpmdev emulator,id=tpm0,chardev=chrtpm \\\n>         -device tpm-tis,tpmdev=tpm0 \\\n>         [...]\n>\n> Signed-off-by: Amarnath Valluri <amarnath.valluri@intel.com>\n> ---\n>  configure             |  13 +-\n>  hmp.c                 |   5 +\n>  hw/tpm/Makefile.objs  |   1 +\n>  hw/tpm/tpm_emulator.c | 599 ++++++++++++++++++++++++++++++++++++++++++++++++++\n>  hw/tpm/tpm_ioctl.h    | 246 +++++++++++++++++++++\n>  qapi/tpm.json         |  21 +-\n>  qemu-options.hx       |  22 +-\n>  vl.c                  |   1 +\n>  8 files changed, 901 insertions(+), 7 deletions(-)\n>  create mode 100644 hw/tpm/tpm_emulator.c\n>  create mode 100644 hw/tpm/tpm_ioctl.h\n>\n> diff --git a/configure b/configure\n> index cb0f7ed..a1b956e 100755\n> --- a/configure\n> +++ b/configure\n> @@ -3467,6 +3467,12 @@ else\n>    tpm_passthrough=no\n>  fi\n>\n> +# TPM emulator is for all posix systems\n> +if test \"$mingw32\" != \"yes\"; then\n> +  tpm_emulator=$tpm\n> +else\n> +  tpm_emulator=no\n> +fi\n>  ##########################################\n>  # attr probe\n>\n> @@ -5359,6 +5365,7 @@ echo \"gcov enabled      $gcov\"\n>  echo \"TPM support       $tpm\"\n>  echo \"libssh2 support   $libssh2\"\n>  echo \"TPM passthrough   $tpm_passthrough\"\n> +echo \"TPM emulator      $tpm_emulator\"\n>  echo \"QOM debugging     $qom_cast_debug\"\n>  echo \"Live block migration $live_block_migration\"\n>  echo \"lzo support       $lzo\"\n> @@ -5937,12 +5944,16 @@ if test \"$live_block_migration\" = \"yes\" ; then\n>    echo \"CONFIG_LIVE_BLOCK_MIGRATION=y\" >> $config_host_mak\n>  fi\n>\n> -# TPM passthrough support?\n>  if test \"$tpm\" = \"yes\"; then\n>    echo 'CONFIG_TPM=$(CONFIG_SOFTMMU)' >> $config_host_mak\n> +  # TPM passthrough support?\n>    if test \"$tpm_passthrough\" = \"yes\"; then\n>      echo \"CONFIG_TPM_PASSTHROUGH=y\" >> $config_host_mak\n>    fi\n> +  # TPM emulator support?\n> +  if test \"$tpm_emulator\" = \"yes\"; then\n> +    echo \"CONFIG_TPM_EMULATOR=y\" >> $config_host_mak\n> +  fi\n>  fi\n>\n>  echo \"TRACE_BACKENDS=$trace_backends\" >> $config_host_mak\n> diff --git a/hmp.c b/hmp.c\n> index 0fb2bc7..9cd8179 100644\n> --- a/hmp.c\n> +++ b/hmp.c\n> @@ -994,6 +994,7 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)\n>      Error *err = NULL;\n>      unsigned int c = 0;\n>      TPMPassthroughOptions *tpo;\n> +    TPMEmulatorOptions *teo;\n>\n>      info_list = qmp_query_tpm(&err);\n>      if (err) {\n> @@ -1023,6 +1024,10 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)\n>                             tpo->has_cancel_path ? \",cancel-path=\" : \"\",\n>                             tpo->has_cancel_path ? tpo->cancel_path : \"\");\n>              break;\n> +        case TPM_TYPE_OPTIONS_KIND_EMULATOR:\n> +            teo = ti->options->u.emulator.data;\n> +            monitor_printf(mon, \",chardev=%s\", teo->chardev);\n> +            break;\n>          case TPM_TYPE_OPTIONS_KIND__MAX:\n>              break;\n>          }\n> diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs\n> index 64cecc3..41f0b7a 100644\n> --- a/hw/tpm/Makefile.objs\n> +++ b/hw/tpm/Makefile.objs\n> @@ -1,2 +1,3 @@\n>  common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o\n>  common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o tpm_util.o\n> +common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o tpm_util.o\n> diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c\n> new file mode 100644\n> index 0000000..0065b0a\n> --- /dev/null\n> +++ b/hw/tpm/tpm_emulator.c\n> @@ -0,0 +1,599 @@\n> +/*\n> + *  Emulator TPM driver\n> + *\n> + *  Copyright (c) 2017 Intel Corporation\n> + *  Author: Amarnath Valluri <amarnath.valluri@intel.com>\n> + *\n> + *  Copyright (c) 2010 - 2013 IBM Corporation\n> + *  Authors:\n> + *    Stefan Berger <stefanb@us.ibm.com>\n> + *\n> + *  Copyright (C) 2011 IAIK, Graz University of Technology\n> + *    Author: Andreas Niederl\n> + *\n> + * This library is free software; you can redistribute it and/or\n> + * modify it under the terms of the GNU Lesser General Public\n> + * License as published by the Free Software Foundation; either\n> + * version 2 of the License, or (at your option) any later version.\n> + *\n> + * This library is distributed in the hope that it will be useful,\n> + * but WITHOUT ANY WARRANTY; without even the implied warranty of\n> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n> + * Lesser General Public License for more details.\n> + *\n> + * You should have received a copy of the GNU Lesser General Public\n> + * License along with this library; if not, see <http://www.gnu.org/licenses/>\n> + *\n> + */\n> +\n> +#include \"qemu/osdep.h\"\n> +#include \"qemu/error-report.h\"\n> +#include \"qemu/sockets.h\"\n> +#include \"io/channel-socket.h\"\n> +#include \"sysemu/tpm_backend.h\"\n> +#include \"tpm_int.h\"\n> +#include \"hw/hw.h\"\n> +#include \"hw/i386/pc.h\"\n> +#include \"tpm_util.h\"\n> +#include \"tpm_ioctl.h\"\n> +#include \"migration/blocker.h\"\n> +#include \"qapi/error.h\"\n> +#include \"qapi/clone-visitor.h\"\n> +#include \"chardev/char-fe.h\"\n> +\n> +#include <fcntl.h>\n> +#include <sys/types.h>\n> +#include <sys/stat.h>\n> +#include <stdio.h>\n> +\n> +#define DEBUG_TPM 0\n> +\n> +#define DPRINTF(fmt, ...) do { \\\n> +    if (DEBUG_TPM) { \\\n> +        fprintf(stderr, \"tpm-emulator:\"fmt\"\\n\", ## __VA_ARGS__); \\\n> +    } \\\n> +} while (0)\n> +\n> +#define TYPE_TPM_EMULATOR \"tpm-emulator\"\n> +#define TPM_EMULATOR(obj) \\\n> +    OBJECT_CHECK(TPMEmulator, (obj), TYPE_TPM_EMULATOR)\n> +\n> +#define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps & (cap)) == (cap))\n> +\n> +static const TPMDriverOps tpm_emulator_driver;\n> +\n> +/* data structures */\n> +typedef struct TPMEmulator {\n> +    TPMBackend parent;\n> +\n> +    TPMEmulatorOptions *options;\n> +    CharBackend ctrl_chr;\n> +    QIOChannel *data_ioc;\n> +    TPMVersion tpm_version;\n> +    ptm_cap caps; /* capabilities of the TPM */\n> +    uint8_t cur_locty_number; /* last set locality */\n> +    QemuMutex state_lock;\n\nThe mutex isnt doing much with this code, as there should be no\nconcurrent handle_request() (thread-pool has max-thread=1).\n\n(I wonder if we could replace the thread-pool with a coroutine/bh\ndoing IO instead to avoid potential thread races... or just do IO in\nthe current context, like the ctrlcmd)\n\n> +    Error *migration_blocker;\n> +} TPMEmulator;\n> +\n> +\n> +static int tpm_emulator_ctrlcmd(CharBackend *dev, unsigned long cmd, void *msg,\n> +                                size_t msg_len_in, size_t msg_len_out)\n> +{\n> +    uint32_t cmd_no = cpu_to_be32(cmd);\n> +    ssize_t n = sizeof(uint32_t) + msg_len_in;\n> +    uint8_t *buf = NULL;\n> +\n> +    buf = g_alloca(n);\n> +    memcpy(buf, &cmd_no, sizeof(cmd_no));\n> +    memcpy(buf + sizeof(cmd_no), msg, msg_len_in);\n> +\n> +    n = qemu_chr_fe_write_all(dev, buf, n);\n> +    if (n <= 0) {\n> +        return -1;\n> +    }\n> +\n> +    if (msg_len_out != 0) {\n> +        n = qemu_chr_fe_read_all(dev, msg, msg_len_out);\n> +        if (n <= 0) {\n> +            return -1;\n> +        }\n> +    }\n> +\n> +    return 0;\n> +}\n> +\n> +static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu,\n> +                                     const uint8_t *in, uint32_t in_len,\n> +                                     uint8_t *out, uint32_t out_len,\n> +                                     bool *selftest_done,\n> +                                     Error **err)\n> +{\n> +    ssize_t ret;\n> +    bool is_selftest = false;\n> +    const struct tpm_resp_hdr *hdr = NULL;\n> +\n> +    if (selftest_done) {\n> +        *selftest_done = false;\n> +        is_selftest = tpm_util_is_selftest(in, in_len);\n> +    }\n> +\n> +    ret = qio_channel_write_all(tpm_emu->data_ioc, (char *)in, in_len, err);\n> +    if (ret != 0) {\n> +        goto err_exit;\n> +    }\n> +\n> +    ret = qio_channel_read(tpm_emu->data_ioc, (char *)out, out_len, err);\n> +    if (ret < 0) {\n> +        goto err_exit;\n> +    }\n> +    if (ret >= sizeof(*hdr)) {\n\nWhat guarantee is there that the read will not be partial?\n\nI think you should read_all() of sizeof(*hdr), and then\nread_all(hdr->len - sizeof(*hdr))\n\n> +        hdr = (struct tpm_resp_hdr *)out;\n> +    }\n> +\n> +    if (!hdr || be32_to_cpu(hdr->len) != ret) {\n> +        error_setg(err, \"Received invalid response packet from TPM with length:\"\n> +                        \"%ld\", ret);\n> +        goto err_exit;\n> +    }\n> +\n> +    if (is_selftest) {\n> +        *selftest_done = (be32_to_cpu(hdr->errcode) == 0);\n> +    }\n> +\n> +    return 0;\n> +\n> +err_exit:\n> +\n> +    return -1;\n> +}\n> +\n> +static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number)\n> +{\n> +    ptm_loc loc;\n> +\n> +    DPRINTF(\"%s : locality: 0x%x\", __func__, locty_number);\n> +\n> +    if (tpm_emu->cur_locty_number == locty_number) {\n> +        return 0;\n> +    }\n> +\n> +    DPRINTF(\"setting locality : 0x%x\", locty_number);\n> +    loc.u.req.loc = locty_number;\n> +    if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_SET_LOCALITY, &loc,\n> +                             sizeof(loc), sizeof(loc)) < 0) {\n> +        error_report(\"tpm-emulator: could not set locality : %s\",\n> +                     strerror(errno));\n> +        return -1;\n> +    }\n> +\n> +    loc.u.resp.tpm_result = be32_to_cpu(loc.u.resp.tpm_result);\n> +    if (loc.u.resp.tpm_result != 0) {\n> +        error_report(\"tpm-emulator: TPM result for set locality : 0x%x\",\n> +                     loc.u.resp.tpm_result);\n> +        return -1;\n> +    }\n> +\n> +    tpm_emu->cur_locty_number = locty_number;\n> +\n> +    return 0;\n> +}\n> +\n> +static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd cmd)\n> +{\n> +    TPMEmulator *tpm_emu = TPM_EMULATOR(tb);\n> +    TPMLocality *locty = NULL;\n> +    bool selftest_done = false;\n> +    Error *err = NULL;\n> +\n> +    DPRINTF(\"processing command type %d\", cmd);\n> +\n> +    switch (cmd) {\n> +    case TPM_BACKEND_CMD_PROCESS_CMD:\n> +        qemu_mutex_lock(&tpm_emu->state_lock);\n> +        locty = tb->tpm_state->locty_data;\n> +        if (tpm_emulator_set_locality(tpm_emu,\n> +                                      tb->tpm_state->locty_number) < 0 ||\n> +            tpm_emulator_unix_tx_bufs(tpm_emu, locty->w_buffer.buffer,\n> +                                      locty->w_offset, locty->r_buffer.buffer,\n> +                                      locty->r_buffer.size, &selftest_done,\n> +                                      &err) < 0) {\n> +            tpm_util_write_fatal_error_response(locty->r_buffer.buffer,\n> +                                                locty->r_buffer.size);\n> +            error_report_err(err);\n> +        }\n> +\n> +        tb->recv_data_callback(tb->tpm_state, tb->tpm_state->locty_number,\n> +                               selftest_done);\n> +        qemu_mutex_unlock(&tpm_emu->state_lock);\n> +\n> +        break;\n> +    case TPM_BACKEND_CMD_INIT:\n> +    case TPM_BACKEND_CMD_END:\n> +    case TPM_BACKEND_CMD_TPM_RESET:\n> +        /* nothing to do */\n> +        break;\n> +    }\n> +}\n> +\n> +static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu)\n> +{\n> +    DPRINTF(\"%s\", __func__);\n> +    if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_GET_CAPABILITY,\n> +                         &tpm_emu->caps, 0, sizeof(tpm_emu->caps)) < 0) {\n> +        error_report(\"tpm-emulator: probing failed : %s\", strerror(errno));\n> +        return -1;\n> +    }\n> +\n> +    tpm_emu->caps = be64_to_cpu(tpm_emu->caps);\n> +\n> +    DPRINTF(\"capbilities : 0x%lx\", tpm_emu->caps);\n> +\n> +    return 0;\n> +}\n> +\n> +static int tpm_emulator_check_caps(TPMEmulator *tpm_emu)\n> +{\n> +    ptm_cap caps = 0;\n> +    const char *tpm = NULL;\n> +\n> +    /* check for min. required capabilities */\n> +    switch (tpm_emu->tpm_version) {\n> +    case TPM_VERSION_1_2:\n> +        caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED |\n> +               PTM_CAP_SET_LOCALITY | PTM_CAP_SET_DATAFD;\n> +        tpm = \"1.2\";\n> +        break;\n> +    case TPM_VERSION_2_0:\n> +        caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED |\n> +               PTM_CAP_SET_LOCALITY | PTM_CAP_RESET_TPMESTABLISHED |\n> +               PTM_CAP_SET_DATAFD;\n> +        tpm = \"2\";\n> +        break;\n> +    case TPM_VERSION_UNSPEC:\n> +        error_report(\"tpm-emulator: TPM version has not been set\");\n> +        return -1;\n> +    }\n> +\n> +    if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, caps)) {\n> +        error_report(\"tpm-emulator: TPM does not implement minimum set of \"\n> +                     \"required capabilities for TPM %s (0x%x)\", tpm, (int)caps);\n> +        return -1;\n> +    }\n> +\n> +    return 0;\n> +}\n> +\n> +static int tpm_emulator_startup_tpm(TPMBackend *tb)\n> +{\n> +    TPMEmulator *tpm_emu = TPM_EMULATOR(tb);\n> +    ptm_init init;\n> +    ptm_res res;\n> +\n> +    DPRINTF(\"%s\", __func__);\n> +    if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_INIT, &init, sizeof(init),\n> +                         sizeof(init)) < 0) {\n> +        error_report(\"tpm-emulator: could not send INIT: %s\",\n> +                     strerror(errno));\n> +        goto err_exit;\n> +    }\n> +\n> +    res = be32_to_cpu(init.u.resp.tpm_result);\n> +    if (res) {\n> +        error_report(\"tpm-emulator: TPM result for CMD_INIT: 0x%x\", res);\n> +        goto err_exit;\n> +    }\n> +    return 0;\n> +\n> +err_exit:\n> +    return -1;\n> +}\n> +\n> +static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb)\n> +{\n> +    TPMEmulator *tpm_emu = TPM_EMULATOR(tb);\n> +    ptm_est est;\n> +\n> +    DPRINTF(\"%s\", __func__);\n> +    if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_GET_TPMESTABLISHED, &est,\n> +                             0, sizeof(est)) < 0) {\n> +        error_report(\"tpm-emulator: Could not get the TPM established flag: %s\",\n> +                     strerror(errno));\n> +        return false;\n> +    }\n> +    DPRINTF(\"established flag: %0x\", est.u.resp.bit);\n> +\n> +    return (est.u.resp.bit != 0);\n> +}\n> +\n> +static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb,\n> +                                                   uint8_t locty)\n> +{\n> +    TPMEmulator *tpm_emu = TPM_EMULATOR(tb);\n> +    ptm_reset_est reset_est;\n> +    ptm_res res;\n> +\n> +    /* only a TPM 2.0 will support this */\n> +    if (tpm_emu->tpm_version != TPM_VERSION_2_0) {\n> +        return 0;\n> +    }\n> +\n> +    reset_est.u.req.loc = tpm_emu->cur_locty_number;\n> +    if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_RESET_TPMESTABLISHED,\n> +                             &reset_est, sizeof(reset_est),\n> +                             sizeof(reset_est)) < 0) {\n> +        error_report(\"tpm-emulator: Could not reset the establishment bit: %s\",\n> +                     strerror(errno));\n> +        return -1;\n> +    }\n> +\n> +    res = be32_to_cpu(reset_est.u.resp.tpm_result);\n> +    if (res) {\n> +        error_report(\"tpm-emulator: TPM result for rest establixhed flag: 0x%x\",\n> +                     res);\n> +        return -1;\n> +    }\n> +\n> +    return 0;\n> +}\n> +\n> +static void tpm_emulator_cancel_cmd(TPMBackend *tb)\n> +{\n> +    TPMEmulator *tpm_emu = TPM_EMULATOR(tb);\n> +    ptm_res res;\n> +\n> +    if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, PTM_CAP_CANCEL_TPM_CMD)) {\n> +        DPRINTF(\"Backend does not support CANCEL_TPM_CMD\");\n> +        return;\n> +    }\n> +\n> +    if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_CANCEL_TPM_CMD, &res, 0,\n> +                             sizeof(res)) < 0) {\n> +        error_report(\"tpm-emulator: Could not cancel command: %s\",\n> +                     strerror(errno));\n> +    } else if (res != 0) {\n> +        error_report(\"tpm-emulator: Failed to cancel TPM: 0x%x\",\n> +                     be32_to_cpu(res));\n> +    }\n> +}\n> +\n> +static TPMVersion tpm_emulator_get_tpm_version(TPMBackend *tb)\n> +{\n> +    TPMEmulator *tpm_emu = TPM_EMULATOR(tb);\n> +\n> +    return tpm_emu->tpm_version;\n> +}\n> +\n> +static int tpm_emulator_block_migration(TPMEmulator *tpm_emu)\n> +{\n> +    Error *err = NULL;\n> +\n> +    error_setg(&tpm_emu->migration_blocker,\n> +               \"Migration disabled: TPM emulator not yet migratable\");\n> +    migrate_add_blocker(tpm_emu->migration_blocker, &err);\n> +    if (err) {\n> +        error_report_err(err);\n> +        error_free(tpm_emu->migration_blocker);\n> +        tpm_emu->migration_blocker = NULL;\n> +\n> +        return -1;\n> +    }\n> +\n> +    return 0;\n> +}\n> +\n> +static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_emu)\n> +{\n> +    ptm_res res;\n> +    Error *err = NULL;\n> +    int fds[2] = { -1, -1 };\n> +\n> +    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) < 0) {\n> +        error_report(\"tpm-emulator: Failed to create socketpair\");\n> +        return -1;\n> +    }\n> +\n> +    qemu_chr_fe_set_msgfds(&tpm_emu->ctrl_chr, fds + 1, 1);\n> +\n> +    if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_SET_DATAFD, &res, 0,\n> +                    sizeof(res)) || res != 0) {\n> +        error_report(\"tpm-emulator: Failed to send CMD_SET_DATAFD: %s\",\n> +                     strerror(errno));\n> +        goto err_exit;\n> +    }\n> +\n> +    tpm_emu->data_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fds[0], &err));\n> +    if (err) {\n> +        error_prepend(&err, \"tpm-emulator: Failed to create io channel: \");\n> +        error_report_err(err);\n> +        goto err_exit;\n> +    }\n> +\n> +    closesocket(fds[1]);\n> +\n> +    return 0;\n> +\n> +err_exit:\n> +    closesocket(fds[0]);\n> +    closesocket(fds[1]);\n> +    return -1;\n> +}\n> +\n> +static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_emu, QemuOpts *opts)\n> +{\n> +    const char *value;\n> +\n> +    value = qemu_opt_get(opts, \"chardev\");\n> +    if (value) {\n> +        Error *err = NULL;\n> +        Chardev *dev = qemu_chr_find(value);\n> +\n> +        if (!dev) {\n> +            error_report(\"tpm-emulator: tpm chardev '%s' not found.\", value);\n> +            goto err;\n> +        }\n> +\n> +        if (!qemu_chr_fe_init(&tpm_emu->ctrl_chr, dev, &err)) {\n> +            error_prepend(&err, \"tpm-emulator: No valid chardev found at '%s':\",\n> +                          value);\n> +            error_report_err(err);\n> +            goto err;\n> +        }\n> +\n> +        tpm_emu->options->chardev = g_strdup(value);\n> +    }\n> +\n> +    if (tpm_emulator_prepare_data_fd(tpm_emu) < 0) {\n> +        goto err;\n> +    }\n> +\n> +    /* FIXME: tpm_util_test_tpmdev() accepts only on socket fd, as it also used\n> +     * by passthrough driver, which not yet using GIOChannel.\n> +     */\n> +    if (tpm_util_test_tpmdev(QIO_CHANNEL_SOCKET(tpm_emu->data_ioc)->fd,\n> +                             &tpm_emu->tpm_version)) {\n> +        error_report(\"'%s' is not emulating TPM device. Error: %s\",\n> +                      tpm_emu->options->chardev, strerror(errno));\n> +        goto err;\n> +    }\n> +\n> +    DPRINTF(\"TPM Version %s\", tpm_emu->tpm_version == TPM_VERSION_1_2 ? \"1.2\" :\n> +            (tpm_emu->tpm_version == TPM_VERSION_2_0 ?  \"2.0\" : \"Unspecified\"));\n> +\n> +    if (tpm_emulator_probe_caps(tpm_emu) ||\n> +        tpm_emulator_check_caps(tpm_emu)) {\n> +        goto err;\n> +    }\n> +\n> +    return tpm_emulator_block_migration(tpm_emu);\n> +\n> +err:\n> +    DPRINTF(\"Startup error\");\n> +    return -1;\n> +}\n> +\n> +static TPMBackend *tpm_emulator_create(QemuOpts *opts, const char *id)\n> +{\n> +    TPMBackend *tb = TPM_BACKEND(object_new(TYPE_TPM_EMULATOR));\n> +\n> +    tb->id = g_strdup(id);\n> +\n> +    if (tpm_emulator_handle_device_opts(TPM_EMULATOR(tb), opts)) {\n> +        goto err_exit;\n> +    }\n> +\n> +    return tb;\n> +\n> +err_exit:\n> +    object_unref(OBJECT(tb));\n> +\n> +    return NULL;\n> +}\n> +\n> +static TpmTypeOptions *tpm_emulator_get_tpm_options(TPMBackend *tb)\n> +{\n> +    TPMEmulator *tpm_emu = TPM_EMULATOR(tb);\n> +    TpmTypeOptions *options = g_new0(TpmTypeOptions, 1);\n> +\n> +    options->type = TPM_TYPE_OPTIONS_KIND_EMULATOR;\n> +    options->u.emulator.data = QAPI_CLONE(TPMEmulatorOptions, tpm_emu->options);\n> +\n> +    return options;\n> +}\n> +\n> +static const QemuOptDesc tpm_emulator_cmdline_opts[] = {\n> +    TPM_STANDARD_CMDLINE_OPTS,\n> +    {\n> +        .name = \"chardev\",\n> +        .type = QEMU_OPT_STRING,\n> +        .help = \"Character device to use for out-of-band control messages\",\n> +    },\n> +    { /* end of list */ },\n> +};\n> +\n> +static const TPMDriverOps tpm_emulator_driver = {\n> +    .type                     = TPM_TYPE_EMULATOR,\n> +    .opts                     = tpm_emulator_cmdline_opts,\n> +    .desc                     = \"TPM emulator backend driver\",\n> +\n> +    .create                   = tpm_emulator_create,\n> +    .startup_tpm              = tpm_emulator_startup_tpm,\n> +    .cancel_cmd               = tpm_emulator_cancel_cmd,\n> +    .get_tpm_established_flag = tpm_emulator_get_tpm_established_flag,\n> +    .reset_tpm_established_flag = tpm_emulator_reset_tpm_established_flag,\n> +    .get_tpm_version          = tpm_emulator_get_tpm_version,\n> +    .get_tpm_options          = tpm_emulator_get_tpm_options,\n> +};\n> +\n> +static void tpm_emulator_inst_init(Object *obj)\n> +{\n> +    TPMEmulator *tpm_emu = TPM_EMULATOR(obj);\n> +\n> +    DPRINTF(\"%s\", __func__);\n> +    tpm_emu->options = g_new0(TPMEmulatorOptions, 1);\n> +    tpm_emu->cur_locty_number = ~0;\n> +    qemu_mutex_init(&tpm_emu->state_lock);\n> +}\n> +\n> +/*\n> + * Gracefully shut down the external TPM\n> + */\n> +static void tpm_emulator_shutdown(TPMEmulator *tpm_emu)\n> +{\n> +    ptm_res res;\n> +\n> +    if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_SHUTDOWN, &res, 0,\n> +                             sizeof(res)) < 0) {\n> +        error_report(\"tpm-emulator: Could not cleanly shutdown the TPM: %s\",\n> +                     strerror(errno));\n> +    } else if (res != 0) {\n> +        error_report(\"tpm-emulator: TPM result for sutdown: 0x%x\",\n> +                     be32_to_cpu(res));\n> +    }\n> +}\n> +\n> +static void tpm_emulator_inst_finalize(Object *obj)\n> +{\n> +    TPMEmulator *tpm_emu = TPM_EMULATOR(obj);\n> +\n> +    tpm_emulator_shutdown(tpm_emu);\n> +\n> +    if (tpm_emu->data_ioc) {\n> +        qio_channel_close(tpm_emu->data_ioc, NULL);\n\nIt should object_unref(). Last unref will implicitely close, so no\nneed for channel_close() before.\n\n> +    }\n> +\n> +    qemu_chr_fe_deinit(&tpm_emu->ctrl_chr, false);\n> +\n> +    if (tpm_emu->options) {\n\nno need for NULL check, qapi_free() already does it\n\n> +        qapi_free_TPMEmulatorOptions(tpm_emu->options);\n> +    }\n> +\n> +    if (tpm_emu->migration_blocker) {\n\nThe following is also safe to call without NULL check too\n\n> +        migrate_del_blocker(tpm_emu->migration_blocker);\n> +        error_free(tpm_emu->migration_blocker);\n> +    }\n> +}\n> +\n> +static void tpm_emulator_class_init(ObjectClass *klass, void *data)\n> +{\n> +    TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);\n> +    tbc->ops = &tpm_emulator_driver;\n> +    tbc->handle_request = tpm_emulator_handle_request;\n> +}\n> +\n> +static const TypeInfo tpm_emulator_info = {\n> +    .name = TYPE_TPM_EMULATOR,\n> +    .parent = TYPE_TPM_BACKEND,\n> +    .instance_size = sizeof(TPMEmulator),\n> +    .class_init = tpm_emulator_class_init,\n> +    .instance_init = tpm_emulator_inst_init,\n> +    .instance_finalize = tpm_emulator_inst_finalize,\n> +};\n> +\n> +static void tpm_emulator_register(void)\n> +{\n> +    type_register_static(&tpm_emulator_info);\n> +    tpm_register_driver(&tpm_emulator_driver);\n> +}\n> +\n> +type_init(tpm_emulator_register)\n> diff --git a/hw/tpm/tpm_ioctl.h b/hw/tpm/tpm_ioctl.h\n> new file mode 100644\n> index 0000000..33564b1\n> --- /dev/null\n> +++ b/hw/tpm/tpm_ioctl.h\n> @@ -0,0 +1,246 @@\n> +/*\n> + * tpm_ioctl.h\n> + *\n> + * (c) Copyright IBM Corporation 2014, 2015.\n> + *\n> + * This file is licensed under the terms of the 3-clause BSD license\n> + */\n> +#ifndef _TPM_IOCTL_H_\n> +#define _TPM_IOCTL_H_\n> +\n> +#include <stdint.h>\n> +#include <sys/uio.h>\n> +#include <sys/types.h>\n> +#include <sys/ioctl.h>\n> +\n> +/*\n> + * Every response from a command involving a TPM command execution must hold\n> + * the ptm_res as the first element.\n> + * ptm_res corresponds to the error code of a command executed by the TPM.\n> + */\n> +\n> +typedef uint32_t ptm_res;\n> +\n> +/* PTM_GET_TPMESTABLISHED: get the establishment bit */\n> +struct ptm_est {\n> +    union {\n> +        struct {\n> +            ptm_res tpm_result;\n> +            unsigned char bit; /* TPM established bit */\n> +        } resp; /* response */\n> +    } u;\n> +};\n> +\n> +/* PTM_RESET_TPMESTABLISHED: reset establishment bit */\n> +struct ptm_reset_est {\n> +    union {\n> +        struct {\n> +            uint8_t loc; /* locality to use */\n> +        } req; /* request */\n> +        struct {\n> +            ptm_res tpm_result;\n> +        } resp; /* response */\n> +    } u;\n> +};\n> +\n> +/* PTM_INIT */\n> +struct ptm_init {\n> +    union {\n> +        struct {\n> +            uint32_t init_flags; /* see definitions below */\n> +        } req; /* request */\n> +        struct {\n> +            ptm_res tpm_result;\n> +        } resp; /* response */\n> +    } u;\n> +};\n> +\n> +/* above init_flags */\n> +#define PTM_INIT_FLAG_DELETE_VOLATILE (1 << 0)\n> +    /* delete volatile state file after reading it */\n> +\n> +/* PTM_SET_LOCALITY */\n> +struct ptm_loc {\n> +    union {\n> +        struct {\n> +            uint8_t loc; /* locality to set */\n> +        } req; /* request */\n> +        struct {\n> +            ptm_res tpm_result;\n> +        } resp; /* response */\n> +    } u;\n> +};\n> +\n> +/* PTM_HASH_DATA: hash given data */\n> +struct ptm_hdata {\n> +    union {\n> +        struct {\n> +            uint32_t length;\n> +            uint8_t data[4096];\n> +        } req; /* request */\n> +        struct {\n> +            ptm_res tpm_result;\n> +        } resp; /* response */\n> +    } u;\n> +};\n> +\n> +/*\n> + * size of the TPM state blob to transfer; x86_64 can handle 8k,\n> + * ppc64le only ~7k; keep the response below a 4k page size\n> + */\n> +#define PTM_STATE_BLOB_SIZE (3 * 1024)\n> +\n> +/*\n> + * The following is the data structure to get state blobs from the TPM.\n> + * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple reads\n> + * with this ioctl and with adjusted offset are necessary. All bytes\n> + * must be transferred and the transfer is done once the last byte has been\n> + * returned.\n> + * It is possible to use the read() interface for reading the data; however, the\n> + * first bytes of the state blob will be part of the response to the ioctl(); a\n> + * subsequent read() is only necessary if the total length (totlength) exceeds\n> + * the number of received bytes. seek() is not supported.\n> + */\n> +struct ptm_getstate {\n> +    union {\n> +        struct {\n> +            uint32_t state_flags; /* may be: PTM_STATE_FLAG_DECRYPTED */\n> +            uint32_t type;        /* which blob to pull */\n> +            uint32_t offset;      /* offset from where to read */\n> +        } req; /* request */\n> +        struct {\n> +            ptm_res tpm_result;\n> +            uint32_t state_flags; /* may be: PTM_STATE_FLAG_ENCRYPTED */\n> +            uint32_t totlength;   /* total length that will be transferred */\n> +            uint32_t length;      /* number of bytes in following buffer */\n> +            uint8_t  data[PTM_STATE_BLOB_SIZE];\n> +        } resp; /* response */\n> +    } u;\n> +};\n> +\n> +/* TPM state blob types */\n> +#define PTM_BLOB_TYPE_PERMANENT  1\n> +#define PTM_BLOB_TYPE_VOLATILE   2\n> +#define PTM_BLOB_TYPE_SAVESTATE  3\n> +\n> +/* state_flags above : */\n> +#define PTM_STATE_FLAG_DECRYPTED     1 /* on input:  get decrypted state */\n> +#define PTM_STATE_FLAG_ENCRYPTED     2 /* on output: state is encrypted */\n> +\n> +/*\n> + * The following is the data structure to set state blobs in the TPM.\n> + * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple\n> + * 'writes' using this ioctl are necessary. The last packet is indicated\n> + * by the length being smaller than the PTM_STATE_BLOB_SIZE.\n> + * The very first packet may have a length indicator of '0' enabling\n> + * a write() with all the bytes from a buffer. If the write() interface\n> + * is used, a final ioctl with a non-full buffer must be made to indicate\n> + * that all data were transferred (a write with 0 bytes would not work).\n> + */\n> +struct ptm_setstate {\n> +    union {\n> +        struct {\n> +            uint32_t state_flags; /* may be PTM_STATE_FLAG_ENCRYPTED */\n> +            uint32_t type;        /* which blob to set */\n> +            uint32_t length;      /* length of the data;\n> +                                     use 0 on the first packet to\n> +                                     transfer using write() */\n> +            uint8_t data[PTM_STATE_BLOB_SIZE];\n> +        } req; /* request */\n> +        struct {\n> +            ptm_res tpm_result;\n> +        } resp; /* response */\n> +    } u;\n> +};\n> +\n> +/*\n> + * PTM_GET_CONFIG: Data structure to get runtime configuration information\n> + * such as which keys are applied.\n> + */\n> +struct ptm_getconfig {\n> +    union {\n> +        struct {\n> +            ptm_res tpm_result;\n> +            uint32_t flags;\n> +        } resp; /* response */\n> +    } u;\n> +};\n> +\n> +#define PTM_CONFIG_FLAG_FILE_KEY        0x1\n> +#define PTM_CONFIG_FLAG_MIGRATION_KEY   0x2\n> +\n> +\n> +typedef uint64_t ptm_cap;\n> +typedef struct ptm_est ptm_est;\n> +typedef struct ptm_reset_est ptm_reset_est;\n> +typedef struct ptm_loc ptm_loc;\n> +typedef struct ptm_hdata ptm_hdata;\n> +typedef struct ptm_init ptm_init;\n> +typedef struct ptm_getstate ptm_getstate;\n> +typedef struct ptm_setstate ptm_setstate;\n> +typedef struct ptm_getconfig ptm_getconfig;\n> +\n> +/* capability flags returned by PTM_GET_CAPABILITY */\n> +#define PTM_CAP_INIT               (1)\n> +#define PTM_CAP_SHUTDOWN           (1 << 1)\n> +#define PTM_CAP_GET_TPMESTABLISHED (1 << 2)\n> +#define PTM_CAP_SET_LOCALITY       (1 << 3)\n> +#define PTM_CAP_HASHING            (1 << 4)\n> +#define PTM_CAP_CANCEL_TPM_CMD     (1 << 5)\n> +#define PTM_CAP_STORE_VOLATILE     (1 << 6)\n> +#define PTM_CAP_RESET_TPMESTABLISHED (1 << 7)\n> +#define PTM_CAP_GET_STATEBLOB      (1 << 8)\n> +#define PTM_CAP_SET_STATEBLOB      (1 << 9)\n> +#define PTM_CAP_STOP               (1 << 10)\n> +#define PTM_CAP_GET_CONFIG         (1 << 11)\n> +#define PTM_CAP_SET_DATAFD         (1 << 12)\n> +\n> +enum {\n> +    PTM_GET_CAPABILITY     = _IOR('P', 0, ptm_cap),\n> +    PTM_INIT               = _IOWR('P', 1, ptm_init),\n> +    PTM_SHUTDOWN           = _IOR('P', 2, ptm_res),\n> +    PTM_GET_TPMESTABLISHED = _IOR('P', 3, ptm_est),\n> +    PTM_SET_LOCALITY       = _IOWR('P', 4, ptm_loc),\n> +    PTM_HASH_START         = _IOR('P', 5, ptm_res),\n> +    PTM_HASH_DATA          = _IOWR('P', 6, ptm_hdata),\n> +    PTM_HASH_END           = _IOR('P', 7, ptm_res),\n> +    PTM_CANCEL_TPM_CMD     = _IOR('P', 8, ptm_res),\n> +    PTM_STORE_VOLATILE     = _IOR('P', 9, ptm_res),\n> +    PTM_RESET_TPMESTABLISHED = _IOWR('P', 10, ptm_reset_est),\n> +    PTM_GET_STATEBLOB      = _IOWR('P', 11, ptm_getstate),\n> +    PTM_SET_STATEBLOB      = _IOWR('P', 12, ptm_setstate),\n> +    PTM_STOP               = _IOR('P', 13, ptm_res),\n> +    PTM_GET_CONFIG         = _IOR('P', 14, ptm_getconfig),\n> +    PTM_SET_DATAFD         = _IOR('P', 15, ptm_res),\n> +};\n> +\n> +/*\n> + * Commands used by the non-CUSE TPMs\n> + *\n> + * All messages container big-endian data.\n> + *\n> + * The return messages only contain the 'resp' part of the unions\n> + * in the data structures above. Besides that the limits in the\n> + * buffers above (ptm_hdata:u.req.data and ptm_get_state:u.resp.data\n> + * and ptm_set_state:u.req.data) are 0xffffffff.\n> + */\n> +enum {\n> +    CMD_GET_CAPABILITY = 1,\n> +    CMD_INIT,\n> +    CMD_SHUTDOWN,\n> +    CMD_GET_TPMESTABLISHED,\n> +    CMD_SET_LOCALITY,\n> +    CMD_HASH_START,\n> +    CMD_HASH_DATA,\n> +    CMD_HASH_END,\n> +    CMD_CANCEL_TPM_CMD,\n> +    CMD_STORE_VOLATILE,\n> +    CMD_RESET_TPMESTABLISHED,\n> +    CMD_GET_STATEBLOB,\n> +    CMD_SET_STATEBLOB,\n> +    CMD_STOP,\n> +    CMD_GET_CONFIG,\n> +    CMD_SET_DATAFD\n> +};\n> +\n> +#endif /* _TPM_IOCTL_H */\n> diff --git a/qapi/tpm.json b/qapi/tpm.json\n> index e8b2d8d..7093f26 100644\n> --- a/qapi/tpm.json\n> +++ b/qapi/tpm.json\n> @@ -39,10 +39,12 @@\n>  # An enumeration of TPM types\n>  #\n>  # @passthrough: TPM passthrough type\n> +# @emulator: Software Emulator TPM type\n> +#            Since: 2.11\n>  #\n>  # Since: 1.5\n>  ##\n> -{ 'enum': 'TpmType', 'data': [ 'passthrough' ] }\n> +{ 'enum': 'TpmType', 'data': [ 'passthrough', 'emulator' ] }\n>\n>  ##\n>  # @query-tpm-types:\n> @@ -56,7 +58,7 @@\n>  # Example:\n>  #\n>  # -> { \"execute\": \"query-tpm-types\" }\n> -# <- { \"return\": [ \"passthrough\" ] }\n> +# <- { \"return\": [ \"passthrough\", \"emulator\" ] }\n>  #\n>  ##\n>  { 'command': 'query-tpm-types', 'returns': ['TpmType'] }\n> @@ -77,16 +79,29 @@\n>                                               '*cancel-path' : 'str'} }\n>\n>  ##\n> +# @TPMEmulatorOptions:\n> +#\n> +# Information about the TPM emulator type\n> +#\n> +# @chardev: Name of a unix socket chardev\n> +#\n> +# Since: 2.11\n> +##\n> +{ 'struct': 'TPMEmulatorOptions', 'data': { 'chardev' : 'str' } }\n> +\n> +##\n>  # @TpmTypeOptions:\n>  #\n>  # A union referencing different TPM backend types' configuration options\n>  #\n>  # @type: 'passthrough' The configuration options for the TPM passthrough type\n> +#        'emulator' The configuration options for TPM emulator backend type\n>  #\n>  # Since: 1.5\n>  ##\n>  { 'union': 'TpmTypeOptions',\n> -   'data': { 'passthrough' : 'TPMPassthroughOptions' } }\n> +   'data': { 'passthrough' : 'TPMPassthroughOptions',\n> +             'emulator': 'TPMEmulatorOptions' } }\n>\n>  ##\n>  # @TPMInfo:\n> diff --git a/qemu-options.hx b/qemu-options.hx\n> index 77859a2..846b3fc 100644\n> --- a/qemu-options.hx\n> +++ b/qemu-options.hx\n> @@ -3121,7 +3121,9 @@ DEF(\"tpmdev\", HAS_ARG, QEMU_OPTION_tpmdev, \\\n>      \"-tpmdev passthrough,id=id[,path=path][,cancel-path=path]\\n\"\n>      \"                use path to provide path to a character device; default is /dev/tpm0\\n\"\n>      \"                use cancel-path to provide path to TPM's cancel sysfs entry; if\\n\"\n> -    \"                not provided it will be searched for in /sys/class/misc/tpm?/device\\n\",\n> +    \"                not provided it will be searched for in /sys/class/misc/tpm?/device\\n\"\n> +    \"-tpmdev emulator,id=id,chardev=dev\\n\"\n> +    \"                configure the TPM device using chardev backend\\n\",\n>      QEMU_ARCH_ALL)\n>  STEXI\n>\n> @@ -3130,8 +3132,8 @@ The general form of a TPM device option is:\n>\n>  @item -tpmdev @var{backend} ,id=@var{id} [,@var{options}]\n>  @findex -tpmdev\n> -Backend type must be:\n> -@option{passthrough}.\n> +Backend type must be either one of the following:\n> +@option{passthrough}, @option{emulator}.\n>\n>  The specific backend type will determine the applicable options.\n>  The @code{-tpmdev} option creates the TPM backend and requires a\n> @@ -3181,6 +3183,20 @@ To create a passthrough TPM use the following two options:\n>  Note that the @code{-tpmdev} id is @code{tpm0} and is referenced by\n>  @code{tpmdev=tpm0} in the device option.\n>\n> +@item -tpmdev emulator, id=@var{id}, chardev=@var{dev}\n> +\n> +(Linux-host only) Enable access to a TPM emulator using Unix domain socket based\n> +chardev backend.\n> +\n> +@option{chardev} specifies the unique ID of a character device backend that provides connection to the software TPM server.\n> +\n> +To create a TPM emulator backend device with chardev socket backend:\n> +@example\n> +\n> +-chardev socket,id=chrtpm,path=/tmp/swtpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0\n> +\n> +@end example\n> +\n>  @end table\n>\n>  ETEXI\n> diff --git a/vl.c b/vl.c\n> index 9bb5058..ed86209 100644\n> --- a/vl.c\n> +++ b/vl.c\n> @@ -4893,6 +4893,7 @@ int main(int argc, char **argv, char **envp)\n>      res_free();\n>\n>      /* vhost-user must be cleaned up before chardevs.  */\n> +    tpm_cleanup();\n\nunrelated change. If you need that, do it in a seperate patch, and\nremove the atexit(tpm_cleanup) from tpm_init.\n\n>      net_cleanup();\n>      audio_cleanup();\n>      monitor_cleanup();\n> --\n> 2.7.4\n>","headers":{"Return-Path":"<qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>","X-Original-To":"incoming@patchwork.ozlabs.org","Delivered-To":"patchwork-incoming@bilbo.ozlabs.org","Authentication-Results":["ozlabs.org;\n\tspf=pass (mailfrom) smtp.mailfrom=nongnu.org\n\t(client-ip=2001:4830:134:3::11; helo=lists.gnu.org;\n\tenvelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org;\n\treceiver=<UNKNOWN>)","ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"bbTqJQ1O\"; dkim-atps=neutral"],"Received":["from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11])\n\t(using TLSv1 with cipher AES256-SHA (256/256 bits))\n\t(No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 3y2tQH1f07z9tX5\n\tfor <incoming@patchwork.ozlabs.org>;\n\tThu, 28 Sep 2017 21:54:14 +1000 (AEST)","from localhost ([::1]:58615 helo=lists.gnu.org)\n\tby lists.gnu.org with esmtp (Exim 4.71) (envelope-from\n\t<qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>)\n\tid 1dxXON-0005G0-Oi\n\tfor incoming@patchwork.ozlabs.org; Thu, 28 Sep 2017 07:54:11 -0400","from eggs.gnu.org ([2001:4830:134:3::10]:40685)\n\tby lists.gnu.org with esmtp (Exim 4.71)\n\t(envelope-from <marcandre.lureau@gmail.com>) id 1dxXNy-0005EV-0W\n\tfor qemu-devel@nongnu.org; Thu, 28 Sep 2017 07:53:50 -0400","from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71)\n\t(envelope-from <marcandre.lureau@gmail.com>) id 1dxXNt-0001nV-VZ\n\tfor qemu-devel@nongnu.org; Thu, 28 Sep 2017 07:53:46 -0400","from mail-oi0-x232.google.com ([2607:f8b0:4003:c06::232]:56343)\n\tby eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16)\n\t(Exim 4.71) (envelope-from <marcandre.lureau@gmail.com>)\n\tid 1dxXNt-0001nM-Lp\n\tfor qemu-devel@nongnu.org; Thu, 28 Sep 2017 07:53:41 -0400","by mail-oi0-x232.google.com with SMTP id b184so1780685oii.13\n\tfor <qemu-devel@nongnu.org>; Thu, 28 Sep 2017 04:53:41 -0700 (PDT)","by 10.58.17.173 with HTTP; Thu, 28 Sep 2017 04:53:39 -0700 (PDT)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025;\n\th=mime-version:in-reply-to:references:from:date:message-id:subject:to\n\t:cc:content-transfer-encoding;\n\tbh=u9MXUqzgow2QUVYarxCu8I7ADkZH+9ufMQPvYVWYISQ=;\n\tb=bbTqJQ1OreMUUtuXUG/kYLVzH3Y93cL5IlrdwM9zI97VxkdgcZ3tyPNvWk8nmlqg9d\n\t7DX0rmY174OebKDpmzZWcgsgYcxbIebHrZAq0ulc2p0GyRIefCbl4avRsXl/EisUxboT\n\t3Jz8TG5Vi+BqK22BciVgDQq2h/ZiZg9buNv+E30PeUouVElCtj88HGkjJ0OuExbIFfH3\n\tUz+6kHDoMWAejaPg3bL9ySyt2oNj+ZRWVm8y8QVrYmbKJPmXfL/Fr3WnA0n/GHL12MGy\n\tMekBA3Nn/w3bQVK52LXCqY+9y2nR8mqLAMfT8DAUtjkXLVa3H+DrbFKMqeNiKt/a1gTF\n\tbFsg==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:mime-version:in-reply-to:references:from:date\n\t:message-id:subject:to:cc:content-transfer-encoding;\n\tbh=u9MXUqzgow2QUVYarxCu8I7ADkZH+9ufMQPvYVWYISQ=;\n\tb=LF3SkeSlltZjhLSKU2AdJBIwcbaXmag2Y4YCnPZUIpOkCLfRA5SuteCd1JYKAPgvye\n\tNZBjrxdwv5JONIIcg2EhRAJXfK0O3jtbelQrTK9FjBwhZqO0LeX3SF+oU3twGaq+5FHd\n\tWd7uwVUgkT9Ko0PxBx+7MFPb0QZBdnN6++Gx6KzoWlkmt7qtL0quu+5SODv1J7MlZ7rV\n\tHok9RHORPFHx1rwoXSqa4/bbysryzsb/US6C/+BJAyM1DKzdiK8TbjLldf3CwZj21FBp\n\tbSA3LjvoJMDXkxgDzPy+UPiiFCo5T5qo8u7cPzE5GyfDxIpsgqPz32oLAw/E4ERXMoFC\n\t3VeQ==","X-Gm-Message-State":"AMCzsaVGXyXCgcfzMHW4DAAYlhMXq/AuaeaoGJtsC8QSAeSCDVT46nCL\n\tuaZXOArC0ON/I4lfvdRFDrofzs+g/3r8NAAQfV8=","X-Google-Smtp-Source":"AOwi7QDgvQD2HRWE9nkMiZ8/FuJpG3aWg8FnNKydMijVO6AeZQaM6y+CGsuMcLegVq7JQ7TXhpmOQYLXKqBGrf3f+yU=","X-Received":"by 10.202.195.133 with SMTP id t127mr175013oif.327.1506599620270;\n\tThu, 28 Sep 2017 04:53:40 -0700 (PDT)","MIME-Version":"1.0","In-Reply-To":"<1506590409-28857-9-git-send-email-amarnath.valluri@intel.com>","References":"<1506590409-28857-1-git-send-email-amarnath.valluri@intel.com>\n\t<1506590409-28857-9-git-send-email-amarnath.valluri@intel.com>","From":"=?utf-8?q?Marc-Andr=C3=A9_Lureau?= <marcandre.lureau@gmail.com>","Date":"Thu, 28 Sep 2017 13:53:39 +0200","Message-ID":"<CAJ+F1C+u4ggH1feJTqzC5dZYfKG0=4H5ihBtDDgiX8UYowfbuA@mail.gmail.com>","To":"Amarnath Valluri <amarnath.valluri@intel.com>","Content-Type":"text/plain; charset=\"UTF-8\"","Content-Transfer-Encoding":"quoted-printable","X-detected-operating-system":"by eggs.gnu.org: Genre and OS details not\n\trecognized.","X-Received-From":"2607:f8b0:4003:c06::232","Subject":"Re: [Qemu-devel] [PATCH v9 8/8] tpm: Added support for TPM emulator","X-BeenThere":"qemu-devel@nongnu.org","X-Mailman-Version":"2.1.21","Precedence":"list","List-Id":"<qemu-devel.nongnu.org>","List-Unsubscribe":"<https://lists.nongnu.org/mailman/options/qemu-devel>,\n\t<mailto:qemu-devel-request@nongnu.org?subject=unsubscribe>","List-Archive":"<http://lists.nongnu.org/archive/html/qemu-devel/>","List-Post":"<mailto:qemu-devel@nongnu.org>","List-Help":"<mailto:qemu-devel-request@nongnu.org?subject=help>","List-Subscribe":"<https://lists.nongnu.org/mailman/listinfo/qemu-devel>,\n\t<mailto:qemu-devel-request@nongnu.org?subject=subscribe>","Cc":"Stefan Berger <stefanb@linux.vnet.ibm.com>,\n\tMarkus Armbruster <armbru@redhat.com>, QEMU <qemu-devel@nongnu.org>, \n\tPaolo Bonzini <pbonzini@redhat.com>,\n\t\"Dr. David Alan Gilbert\" <dgilbert@redhat.com>","Errors-To":"qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org","Sender":"\"Qemu-devel\"\n\t<qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>"}}]