qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PACTH v3 0/5] Add vTPM emulator supportfor ppc64 platform
@ 2019-12-11 16:20 Stefan Berger
  2019-12-11 16:20 ` [PACTH v3 1/5] tpm_spapr: Support TPM for ppc64 using CRQ based interface Stefan Berger
                   ` (4 more replies)
  0 siblings, 5 replies; 12+ messages in thread
From: Stefan Berger @ 2019-12-11 16:20 UTC (permalink / raw)
  To: qemu-ppc; +Cc: marcandre.lureau, Stefan Berger, qemu-devel, david

The following series of patches adds vTPM emulator support for the
ppc64 platform (pSeries). 

It can be tested as follows with swtpm/libtpms:

mkdir /tmp/mytpm1
swtpm socket --tpmstate dir=/tmp/mytpm1 \
  --ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \
  --log level=20

If TPM 2 is desired, add --tpm2 as parameter to the above.

In another terminal start QEMU:

sudo ./ppc64-softmmu/qemu-system-ppc64 -display sdl \
	-machine pseries,accel=kvm \
	-m 1024 -bios slof.bin -boot menu=on \
	-nodefaults -device VGA -device pci-ohci -device usb-kbd \
	-chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \
	-tpmdev emulator,id=tpm0,chardev=chrtpm \
	-device tpm-spapr,tpmdev=tpm0 \
	-device spapr-vscsi,id=scsi0,reg=0x00002000 \
	-device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x3,drive=drive-virtio-disk0,id=virtio-disk0 \
	-drive file=test.img,format=raw,if=none,id=drive-virtio-disk0

Links:
 - libtpms: https://github.com/stefanberger/libtpms/wiki
 - swtpm: https://github.com/stefanberger/swtpm/wiki

Changes:
 v2->v3:
  - patch 1: a TPM 2 is identified by IBM,vtpm20 in the compatible node
  - patch 1: convert to tracing to display Tx and Rx buffers
  - added documentation patch
  - added patch to enable TPM device as part of pSeries

 v1->v2:
  - followed Cedric Le Goater's suggestions to patch 1
  - send appropriate CRQ error responses if DMA read or write fails
  - renamed tpm_spapr_got_payload to tpm_spapr_process_cmd and
    pass endianess-adjusted data pointer from CRQ to it

Regards,
    Stefan

Stefan Berger (5):
  tpm_spapr: Support TPM for ppc64 using CRQ based interface
  tpm: Return bool from tpm_backend_finish_sync
  tpm_spapr: Support suspend and resume
  hw/ppc/Kconfig: Enable TPMDEV as part of PSERIES config
  docs: tpm: Add example command line for ppc64 and tpm-spapr

 backends/tpm.c               |   6 +-
 docs/specs/tpm.txt           |  20 +-
 hw/ppc/Kconfig               |   1 +
 hw/tpm/Makefile.objs         |   1 +
 hw/tpm/tpm_spapr.c           | 445 +++++++++++++++++++++++++++++++++++
 hw/tpm/trace-events          |  12 +
 include/sysemu/tpm.h         |   3 +
 include/sysemu/tpm_backend.h |   4 +-
 qapi/tpm.json                |   6 +-
 9 files changed, 492 insertions(+), 6 deletions(-)
 create mode 100644 hw/tpm/tpm_spapr.c

-- 
2.21.0



^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PACTH v3 1/5] tpm_spapr: Support TPM for ppc64 using CRQ based interface
  2019-12-11 16:20 [PACTH v3 0/5] Add vTPM emulator supportfor ppc64 platform Stefan Berger
@ 2019-12-11 16:20 ` Stefan Berger
  2019-12-12 11:05   ` Marc-André Lureau
  2019-12-11 16:20 ` [PACTH v3 2/5] tpm: Return bool from tpm_backend_finish_sync Stefan Berger
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 12+ messages in thread
From: Stefan Berger @ 2019-12-11 16:20 UTC (permalink / raw)
  To: qemu-ppc; +Cc: marcandre.lureau, Stefan Berger, qemu-devel, david

Implement support for TPM on ppc64 by implementing the vTPM CRQ interface
as a frontend. It can use the tpm_emulator driver backend with the external
swtpm.

The Linux vTPM driver for ppc64 works with this emulation.

This TPM emulator also handles the TPM 2 case.

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
---
 hw/tpm/Makefile.objs |   1 +
 hw/tpm/tpm_spapr.c   | 405 +++++++++++++++++++++++++++++++++++++++++++
 hw/tpm/trace-events  |  12 ++
 include/sysemu/tpm.h |   3 +
 qapi/tpm.json        |   6 +-
 5 files changed, 424 insertions(+), 3 deletions(-)
 create mode 100644 hw/tpm/tpm_spapr.c

diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs
index de0b85d02a..f9f29e0082 100644
--- a/hw/tpm/Makefile.objs
+++ b/hw/tpm/Makefile.objs
@@ -4,3 +4,4 @@ common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o
 common-obj-$(CONFIG_TPM_CRB) += tpm_crb.o
 common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
 common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o
+obj-$(CONFIG_PSERIES) += tpm_spapr.o
diff --git a/hw/tpm/tpm_spapr.c b/hw/tpm/tpm_spapr.c
new file mode 100644
index 0000000000..c4a67e2403
--- /dev/null
+++ b/hw/tpm/tpm_spapr.c
@@ -0,0 +1,405 @@
+/*
+ * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
+ *
+ * PAPR Virtual TPM
+ *
+ * Copyright (c) 2015, 2017 IBM Corporation.
+ *
+ * Authors:
+ *    Stefan Berger <stefanb@linux.vnet.ibm.com>
+ *
+ * This code is licensed under the GPL version 2 or later. See the
+ * COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+
+#include "sysemu/tpm_backend.h"
+#include "tpm_int.h"
+#include "tpm_util.h"
+
+#include "hw/ppc/spapr.h"
+#include "hw/ppc/spapr_vio.h"
+#include "trace.h"
+
+#define DEBUG_SPAPR 0
+
+#define VIO_SPAPR_VTPM(obj) \
+     OBJECT_CHECK(SPAPRvTPMState, (obj), TYPE_TPM_SPAPR)
+
+typedef struct VioCRQ {
+    uint8_t valid;  /* 0x80: cmd; 0xc0: init crq */
+                    /* 0x81-0x83: CRQ message response */
+    uint8_t msg;    /* see below */
+    uint16_t len;   /* len of TPM request; len of TPM response */
+    uint32_t data;  /* rtce_dma_handle when sending TPM request */
+    uint64_t reserved;
+} VioCRQ;
+
+typedef union TPMSpaprCRQ {
+    VioCRQ s;
+    uint8_t raw[sizeof(VioCRQ)];
+} TPMSpaprCRQ;
+
+#define SPAPR_VTPM_VALID_INIT_CRQ_COMMAND  0xC0
+#define SPAPR_VTPM_VALID_COMMAND           0x80
+#define SPAPR_VTPM_MSG_RESULT              0x80
+
+/* msg types for valid = SPAPR_VTPM_VALID_INIT_CRQ */
+#define SPAPR_VTPM_INIT_CRQ_RESULT           0x1
+#define SPAPR_VTPM_INIT_CRQ_COMPLETE_RESULT  0x2
+
+/* msg types for valid = SPAPR_VTPM_VALID_CMD */
+#define SPAPR_VTPM_GET_VERSION               0x1
+#define SPAPR_VTPM_TPM_COMMAND               0x2
+#define SPAPR_VTPM_GET_RTCE_BUFFER_SIZE      0x3
+#define SPAPR_VTPM_PREPARE_TO_SUSPEND        0x4
+
+/* response error messages */
+#define SPAPR_VTPM_VTPM_ERROR                0xff
+
+/* error codes */
+#define SPAPR_VTPM_ERR_COPY_IN_FAILED        0x3
+#define SPAPR_VTPM_ERR_COPY_OUT_FAILED       0x4
+
+#define MAX_BUFFER_SIZE TARGET_PAGE_SIZE
+
+typedef struct {
+    SpaprVioDevice vdev;
+
+    TPMSpaprCRQ crq; /* track single TPM command */
+
+    uint8_t state;
+#define SPAPR_VTPM_STATE_NONE         0
+#define SPAPR_VTPM_STATE_EXECUTION    1
+#define SPAPR_VTPM_STATE_COMPLETION   2
+
+    unsigned char buffer[MAX_BUFFER_SIZE];
+
+    TPMBackendCmd cmd;
+
+    TPMBackend *be_driver;
+    TPMVersion be_tpm_version;
+
+    size_t be_buffer_size;
+} SPAPRvTPMState;
+
+static void tpm_spapr_show_buffer(const unsigned char *buffer,
+                                  size_t buffer_size, const char *string)
+{
+    size_t len, i;
+    char *line_buffer, *p;
+
+    len = MIN(tpm_cmd_get_size(buffer), buffer_size);
+
+    /*
+     * allocate enough room for 3 chars per buffer entry plus a
+     * newline after every 16 chars and a final null terminator.
+     */
+    line_buffer = g_malloc(len * 3 + (len / 16) + 1);
+
+    for (i = 0, p = line_buffer; i < len; i++) {
+        if (i && !(i % 16)) {
+            p += sprintf(p, "\n");
+        }
+        p += sprintf(p, "%.2X ", buffer[i]);
+    }
+    trace_tpm_spapr_show_buffer(string, len, line_buffer);
+
+    g_free(line_buffer);
+}
+
+/*
+ * Send a request to the TPM.
+ */
+static void tpm_spapr_tpm_send(SPAPRvTPMState *s)
+{
+    if (trace_event_get_state_backends(TRACE_TPM_SPAPR_SHOW_BUFFER)) {
+        tpm_spapr_show_buffer(s->buffer, sizeof(s->buffer), "To TPM");
+    }
+
+    s->state = SPAPR_VTPM_STATE_EXECUTION;
+    s->cmd = (TPMBackendCmd) {
+        .locty = 0,
+        .in = s->buffer,
+        .in_len = MIN(tpm_cmd_get_size(s->buffer), sizeof(s->buffer)),
+        .out = s->buffer,
+        .out_len = sizeof(s->buffer),
+    };
+
+    tpm_backend_deliver_request(s->be_driver, &s->cmd);
+}
+
+static int tpm_spapr_process_cmd(SPAPRvTPMState *s, uint64_t dataptr)
+{
+    long rc;
+
+    /* a max. of be_buffer_size bytes can be transported */
+    rc = spapr_vio_dma_read(&s->vdev, dataptr,
+                            s->buffer, s->be_buffer_size);
+    if (rc) {
+        error_report("tpm_spapr_got_payload: DMA read failure");
+    }
+    /* let vTPM handle any malformed request */
+    tpm_spapr_tpm_send(s);
+
+    return rc;
+}
+
+static int tpm_spapr_do_crq(struct SpaprVioDevice *dev, uint8_t *crq_data)
+{
+    SPAPRvTPMState *s = VIO_SPAPR_VTPM(dev);
+    TPMSpaprCRQ local_crq;
+    TPMSpaprCRQ *crq = &s->crq; /* requests only */
+    int rc;
+
+    memcpy(&local_crq.raw, crq_data, sizeof(local_crq.raw));
+
+    trace_tpm_spapr_do_crq(local_crq.raw[0], local_crq.raw[1]);
+
+    switch (local_crq.s.valid) {
+    case SPAPR_VTPM_VALID_INIT_CRQ_COMMAND: /* Init command/response */
+
+        /* Respond to initialization request */
+        switch (local_crq.s.msg) {
+        case SPAPR_VTPM_INIT_CRQ_RESULT:
+            trace_tpm_spapr_do_crq_crq_result();
+            memset(local_crq.raw, 0, sizeof(local_crq.raw));
+            local_crq.s.valid = SPAPR_VTPM_VALID_INIT_CRQ_COMMAND;
+            local_crq.s.msg = SPAPR_VTPM_INIT_CRQ_RESULT;
+            spapr_vio_send_crq(dev, local_crq.raw);
+            break;
+
+        case SPAPR_VTPM_INIT_CRQ_COMPLETE_RESULT:
+            trace_tpm_spapr_do_crq_crq_complete_result();
+            memset(local_crq.raw, 0, sizeof(local_crq.raw));
+            local_crq.s.valid = SPAPR_VTPM_VALID_INIT_CRQ_COMMAND;
+            local_crq.s.msg = SPAPR_VTPM_INIT_CRQ_COMPLETE_RESULT;
+            spapr_vio_send_crq(dev, local_crq.raw);
+            break;
+        }
+
+        break;
+    case SPAPR_VTPM_VALID_COMMAND: /* Payloads */
+        switch (local_crq.s.msg) {
+        case SPAPR_VTPM_TPM_COMMAND:
+            trace_tpm_spapr_do_crq_tpm_command();
+            if (s->state == SPAPR_VTPM_STATE_EXECUTION) {
+                return H_BUSY;
+            }
+            /* this crq is tracked */
+            memcpy(crq->raw, crq_data, sizeof(crq->raw));
+
+            rc = tpm_spapr_process_cmd(s, be32_to_cpu(crq->s.data));
+
+            if (rc == H_SUCCESS) {
+                crq->s.valid = be16_to_cpu(0);
+            } else {
+                local_crq.s.valid = SPAPR_VTPM_MSG_RESULT;
+                local_crq.s.msg = SPAPR_VTPM_VTPM_ERROR;
+                local_crq.s.data = cpu_to_be32(SPAPR_VTPM_ERR_COPY_IN_FAILED);
+                spapr_vio_send_crq(dev, local_crq.raw);
+            }
+            break;
+
+        case SPAPR_VTPM_GET_RTCE_BUFFER_SIZE:
+            trace_tpm_spapr_do_crq_tpm_get_rtce_buffer_size(s->be_buffer_size);
+            local_crq.s.msg |= SPAPR_VTPM_MSG_RESULT;
+            local_crq.s.len = cpu_to_be16(s->be_buffer_size);
+            spapr_vio_send_crq(dev, local_crq.raw);
+            break;
+
+        case SPAPR_VTPM_GET_VERSION:
+            local_crq.s.msg |= SPAPR_VTPM_MSG_RESULT;
+            local_crq.s.len = cpu_to_be16(0);
+            switch (s->be_tpm_version) {
+            case TPM_VERSION_UNSPEC:
+                local_crq.s.data = cpu_to_be32(0);
+                break;
+            case TPM_VERSION_1_2:
+                local_crq.s.data = cpu_to_be32(1);
+                break;
+            case TPM_VERSION_2_0:
+                local_crq.s.data = cpu_to_be32(2);
+                break;
+            }
+            trace_tpm_spapr_do_crq_get_version(be32_to_cpu(local_crq.s.data));
+            spapr_vio_send_crq(dev, local_crq.raw);
+            break;
+
+        case SPAPR_VTPM_PREPARE_TO_SUSPEND:
+            trace_tpm_spapr_do_crq_prepare_to_suspend();
+            local_crq.s.msg |= SPAPR_VTPM_MSG_RESULT;
+            spapr_vio_send_crq(dev, local_crq.raw);
+            break;
+
+        default:
+            trace_tpm_spapr_do_crq_unknown_msg_type(crq->s.msg);
+        }
+        break;
+    default:
+        trace_tpm_spapr_do_crq_unknown_crq(local_crq.raw[0], local_crq.raw[1]);
+    };
+
+    return H_SUCCESS;
+}
+
+static void tpm_spapr_request_completed(TPMIf *ti, int ret)
+{
+    SPAPRvTPMState *s = VIO_SPAPR_VTPM(ti);
+    TPMSpaprCRQ *crq = &s->crq;
+    uint32_t len;
+    int rc;
+
+    s->state = SPAPR_VTPM_STATE_COMPLETION;
+
+    /* a max. of be_buffer_size bytes can be transported */
+    len = MIN(tpm_cmd_get_size(s->buffer), s->be_buffer_size);
+    rc = spapr_vio_dma_write(&s->vdev, be32_to_cpu(crq->s.data),
+                             s->buffer, len);
+
+    if (trace_event_get_state_backends(TRACE_TPM_SPAPR_SHOW_BUFFER)) {
+        tpm_spapr_show_buffer(s->buffer, len, "From TPM");
+    }
+
+    crq->s.valid = SPAPR_VTPM_MSG_RESULT;
+    if (rc == H_SUCCESS) {
+        crq->s.msg = SPAPR_VTPM_TPM_COMMAND | SPAPR_VTPM_MSG_RESULT;
+        crq->s.len = cpu_to_be16(len);
+    } else {
+        error_report("%s: DMA write failure", __func__);
+        crq->s.msg = SPAPR_VTPM_VTPM_ERROR;
+        crq->s.len = cpu_to_be16(0);
+        crq->s.data = cpu_to_be32(SPAPR_VTPM_ERR_COPY_OUT_FAILED);
+    }
+
+    rc = spapr_vio_send_crq(&s->vdev, crq->raw);
+    if (rc) {
+        error_report("%s: Error sending response", __func__);
+    }
+}
+
+static int tpm_spapr_do_startup_tpm(SPAPRvTPMState *s, size_t buffersize)
+{
+    return tpm_backend_startup_tpm(s->be_driver, buffersize);
+}
+
+static void tpm_spapr_update_deviceclass(SpaprVioDevice *dev)
+{
+    SPAPRvTPMState *s = VIO_SPAPR_VTPM(dev);
+    SpaprVioDeviceClass *k = VIO_SPAPR_DEVICE_GET_CLASS(dev);
+
+    switch (s->be_tpm_version) {
+    case TPM_VERSION_UNSPEC:
+        assert(false);
+        break;
+    case TPM_VERSION_1_2:
+        k->dt_name = "vtpm";
+        k->dt_type = "IBM,vtpm";
+        k->dt_compatible = "IBM,vtpm";
+        break;
+    case TPM_VERSION_2_0:
+        k->dt_name = "vtpm";
+        k->dt_type = "IBM,vtpm";
+        k->dt_compatible = "IBM,vtpm20";
+        break;
+    }
+}
+
+static void tpm_spapr_reset(SpaprVioDevice *dev)
+{
+    SPAPRvTPMState *s = VIO_SPAPR_VTPM(dev);
+
+    s->state = SPAPR_VTPM_STATE_NONE;
+
+    s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver);
+    tpm_spapr_update_deviceclass(dev);
+
+    s->be_buffer_size = MAX(ROUND_UP(tpm_backend_get_buffer_size(s->be_driver),
+                                     TARGET_PAGE_SIZE),
+                            sizeof(s->buffer));
+
+    tpm_backend_reset(s->be_driver);
+    tpm_spapr_do_startup_tpm(s, s->be_buffer_size);
+}
+
+static enum TPMVersion tpm_spapr_get_version(TPMIf *ti)
+{
+    SPAPRvTPMState *s = VIO_SPAPR_VTPM(ti);
+
+    if (tpm_backend_had_startup_error(s->be_driver)) {
+        return TPM_VERSION_UNSPEC;
+    }
+
+    return tpm_backend_get_tpm_version(s->be_driver);
+}
+
+static const VMStateDescription vmstate_spapr_vtpm = {
+    .name = "tpm-spapr",
+    .unmigratable = 1,
+};
+
+static Property tpm_spapr_properties[] = {
+    DEFINE_SPAPR_PROPERTIES(SPAPRvTPMState, vdev),
+    DEFINE_PROP_TPMBE("tpmdev", SPAPRvTPMState, be_driver),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void tpm_spapr_realizefn(SpaprVioDevice *dev, Error **errp)
+{
+    SPAPRvTPMState *s = VIO_SPAPR_VTPM(dev);
+
+    if (!tpm_find()) {
+        error_setg(errp, "at most one TPM device is permitted");
+        return;
+    }
+
+    dev->crq.SendFunc = tpm_spapr_do_crq;
+
+    if (!s->be_driver) {
+        error_setg(errp, "'tpmdev' property is required");
+        return;
+    }
+}
+
+static void tpm_spapr_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SpaprVioDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass);
+    TPMIfClass *tc = TPM_IF_CLASS(klass);
+
+    k->realize = tpm_spapr_realizefn;
+    k->reset = tpm_spapr_reset;
+    k->signal_mask = 0x00000001;
+    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+    dc->props = tpm_spapr_properties;
+    k->rtce_window_size = 0x10000000;
+    dc->vmsd = &vmstate_spapr_vtpm;
+
+    tc->model = TPM_MODEL_TPM_SPAPR;
+    tc->get_version = tpm_spapr_get_version;
+    tc->request_completed = tpm_spapr_request_completed;
+}
+
+static const TypeInfo tpm_spapr_info = {
+    .name          = TYPE_TPM_SPAPR,
+    .parent        = TYPE_VIO_SPAPR_DEVICE,
+    .instance_size = sizeof(SPAPRvTPMState),
+    .class_init    = tpm_spapr_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_TPM_IF },
+        { }
+    }
+};
+
+static void tpm_spapr_register_types(void)
+{
+    type_register_static(&tpm_spapr_info);
+}
+
+type_init(tpm_spapr_register_types)
diff --git a/hw/tpm/trace-events b/hw/tpm/trace-events
index 89804bcd64..6278a39618 100644
--- a/hw/tpm/trace-events
+++ b/hw/tpm/trace-events
@@ -55,3 +55,15 @@ tpm_tis_pre_save(uint8_t locty, uint32_t rw_offset) "locty: %d, rw_offset = %u"
 
 # tpm_ppi.c
 tpm_ppi_memset(uint8_t *ptr, size_t size) "memset: %p %zu"
+
+# hw/tpm/tpm_spapr.c
+tpm_spapr_show_buffer(const char *direction, size_t len, const char *buf) "direction: %s len: %zu\n%s"
+tpm_spapr_do_crq(uint8_t raw1, uint8_t raw2) "1st 2 bytes in CRQ: 0x%02x 0x%02x"
+tpm_spapr_do_crq_crq_result(void) "SPAPR_VTPM_INIT_CRQ_RESULT"
+tpm_spapr_do_crq_crq_complete_result(void) "SPAPR_VTPM_INIT_CRQ_COMP_RESULT"
+tpm_spapr_do_crq_tpm_command(void) "got TPM command payload"
+tpm_spapr_do_crq_tpm_get_rtce_buffer_size(size_t buffersize) "response: buffer size is %zu"
+tpm_spapr_do_crq_get_version(uint32_t version) "response: version %u"
+tpm_spapr_do_crq_prepare_to_suspend(void) "response: preparing to suspend"
+tpm_spapr_do_crq_unknown_msg_type(uint8_t type) "Unknown message type 0x%02x"
+tpm_spapr_do_crq_unknown_crq(uint8_t raw1, uint8_t raw2) "unknown CRQ 0x%02x 0x%02x ..."
diff --git a/include/sysemu/tpm.h b/include/sysemu/tpm.h
index 5b541a71c8..15979a3647 100644
--- a/include/sysemu/tpm.h
+++ b/include/sysemu/tpm.h
@@ -45,11 +45,14 @@ typedef struct TPMIfClass {
 
 #define TYPE_TPM_TIS                "tpm-tis"
 #define TYPE_TPM_CRB                "tpm-crb"
+#define TYPE_TPM_SPAPR              "tpm-spapr"
 
 #define TPM_IS_TIS(chr)                             \
     object_dynamic_cast(OBJECT(chr), TYPE_TPM_TIS)
 #define TPM_IS_CRB(chr)                             \
     object_dynamic_cast(OBJECT(chr), TYPE_TPM_CRB)
+#define TPM_IS_SPAPR(chr)                           \
+    object_dynamic_cast(OBJECT(chr), TYPE_TPM_SPAPR)
 
 /* returns NULL unless there is exactly one TPM device */
 static inline TPMIf *tpm_find(void)
diff --git a/qapi/tpm.json b/qapi/tpm.json
index b30323bb6b..63878aa0f4 100644
--- a/qapi/tpm.json
+++ b/qapi/tpm.json
@@ -12,11 +12,11 @@
 #
 # @tpm-tis: TPM TIS model
 # @tpm-crb: TPM CRB model (since 2.12)
+# @tpm-spapr: TPM SPAPR model (since 5.0)
 #
 # Since: 1.5
 ##
-{ 'enum': 'TpmModel', 'data': [ 'tpm-tis', 'tpm-crb' ] }
-
+{ 'enum': 'TpmModel', 'data': [ 'tpm-tis', 'tpm-crb', 'tpm-spapr' ] }
 ##
 # @query-tpm-models:
 #
@@ -29,7 +29,7 @@
 # Example:
 #
 # -> { "execute": "query-tpm-models" }
-# <- { "return": [ "tpm-tis", "tpm-crb" ] }
+# <- { "return": [ "tpm-tis", "tpm-crb", "tpm-spapr" ] }
 #
 ##
 { 'command': 'query-tpm-models', 'returns': ['TpmModel'] }
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PACTH v3 2/5] tpm: Return bool from tpm_backend_finish_sync
  2019-12-11 16:20 [PACTH v3 0/5] Add vTPM emulator supportfor ppc64 platform Stefan Berger
  2019-12-11 16:20 ` [PACTH v3 1/5] tpm_spapr: Support TPM for ppc64 using CRQ based interface Stefan Berger
@ 2019-12-11 16:20 ` Stefan Berger
  2019-12-11 16:20 ` [PACTH v3 3/5] tpm_spapr: Support suspend and resume Stefan Berger
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 12+ messages in thread
From: Stefan Berger @ 2019-12-11 16:20 UTC (permalink / raw)
  To: qemu-ppc; +Cc: marcandre.lureau, Stefan Berger, qemu-devel, david

Return true in case we had to wait for an outstanding response
to come back, false otherwise.

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
 backends/tpm.c               | 6 +++++-
 include/sysemu/tpm_backend.h | 4 +++-
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/backends/tpm.c b/backends/tpm.c
index 375587e743..1f75883d8a 100644
--- a/backends/tpm.c
+++ b/backends/tpm.c
@@ -49,11 +49,15 @@ static int tpm_backend_worker_thread(gpointer data)
     return 0;
 }
 
-void tpm_backend_finish_sync(TPMBackend *s)
+bool tpm_backend_finish_sync(TPMBackend *s)
 {
+    bool ret = s->cmd != NULL;
+
     while (s->cmd) {
         aio_poll(qemu_get_aio_context(), true);
     }
+
+    return ret;
 }
 
 enum TpmType tpm_backend_get_type(TPMBackend *s)
diff --git a/include/sysemu/tpm_backend.h b/include/sysemu/tpm_backend.h
index 9e7451fb52..c35fe85c62 100644
--- a/include/sysemu/tpm_backend.h
+++ b/include/sysemu/tpm_backend.h
@@ -200,8 +200,10 @@ size_t tpm_backend_get_buffer_size(TPMBackend *s);
  *
  * Finish the pending command synchronously (this will call aio_poll()
  * on qemu main AIOContext until it ends)
+ *
+ * Returns true in case there was a pending command, false otherwise.
  */
-void tpm_backend_finish_sync(TPMBackend *s);
+bool tpm_backend_finish_sync(TPMBackend *s);
 
 /**
  * tpm_backend_query_tpm:
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PACTH v3 3/5] tpm_spapr: Support suspend and resume
  2019-12-11 16:20 [PACTH v3 0/5] Add vTPM emulator supportfor ppc64 platform Stefan Berger
  2019-12-11 16:20 ` [PACTH v3 1/5] tpm_spapr: Support TPM for ppc64 using CRQ based interface Stefan Berger
  2019-12-11 16:20 ` [PACTH v3 2/5] tpm: Return bool from tpm_backend_finish_sync Stefan Berger
@ 2019-12-11 16:20 ` Stefan Berger
  2019-12-12 11:00   ` Marc-André Lureau
  2019-12-11 16:20 ` [PACTH v3 4/5] hw/ppc/Kconfig: Enable TPMDEV as part of PSERIES config Stefan Berger
  2019-12-11 16:20 ` [PACTH v3 5/5] docs: tpm: Add example command line for ppc64 and tpm-spapr Stefan Berger
  4 siblings, 1 reply; 12+ messages in thread
From: Stefan Berger @ 2019-12-11 16:20 UTC (permalink / raw)
  To: qemu-ppc; +Cc: marcandre.lureau, Stefan Berger, qemu-devel, david

Extend the tpm_spapr frontend with VM suspend and resume support.

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
 hw/tpm/tpm_spapr.c | 42 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 41 insertions(+), 1 deletion(-)

diff --git a/hw/tpm/tpm_spapr.c b/hw/tpm/tpm_spapr.c
index c4a67e2403..d9153bd95c 100644
--- a/hw/tpm/tpm_spapr.c
+++ b/hw/tpm/tpm_spapr.c
@@ -87,6 +87,8 @@ typedef struct {
     TPMVersion be_tpm_version;
 
     size_t be_buffer_size;
+
+    bool deliver_response; /* whether to deliver response after VM resume */
 } SPAPRvTPMState;
 
 static void tpm_spapr_show_buffer(const unsigned char *buffer,
@@ -339,9 +341,47 @@ static enum TPMVersion tpm_spapr_get_version(TPMIf *ti)
     return tpm_backend_get_tpm_version(s->be_driver);
 }
 
+/* persistent state handling */
+
+static int tpm_spapr_pre_save(void *opaque)
+{
+    SPAPRvTPMState *s = opaque;
+
+    s->deliver_response = tpm_backend_finish_sync(s->be_driver);
+    /*
+     * we cannot deliver the results to the VM since DMA would touch VM memory
+     */
+
+    return 0;
+}
+
+static int tpm_spapr_post_load(void *opaque, int version_id)
+{
+    SPAPRvTPMState *s = opaque;
+
+    if (s->deliver_response) {
+        /* deliver the results to the VM via DMA */
+        tpm_spapr_request_completed(TPM_IF(s), 0);
+    }
+
+    return 0;
+}
+
 static const VMStateDescription vmstate_spapr_vtpm = {
     .name = "tpm-spapr",
-    .unmigratable = 1,
+    .version_id = 1,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .pre_save = tpm_spapr_pre_save,
+    .post_load = tpm_spapr_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_SPAPR_VIO(vdev, SPAPRvTPMState),
+
+        VMSTATE_UINT8(state, SPAPRvTPMState),
+        VMSTATE_BUFFER(buffer, SPAPRvTPMState),
+        VMSTATE_BOOL(deliver_response, SPAPRvTPMState),
+        VMSTATE_END_OF_LIST(),
+    }
 };
 
 static Property tpm_spapr_properties[] = {
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PACTH v3 4/5] hw/ppc/Kconfig: Enable TPMDEV as part of PSERIES config
  2019-12-11 16:20 [PACTH v3 0/5] Add vTPM emulator supportfor ppc64 platform Stefan Berger
                   ` (2 preceding siblings ...)
  2019-12-11 16:20 ` [PACTH v3 3/5] tpm_spapr: Support suspend and resume Stefan Berger
@ 2019-12-11 16:20 ` Stefan Berger
  2019-12-11 16:20 ` [PACTH v3 5/5] docs: tpm: Add example command line for ppc64 and tpm-spapr Stefan Berger
  4 siblings, 0 replies; 12+ messages in thread
From: Stefan Berger @ 2019-12-11 16:20 UTC (permalink / raw)
  To: qemu-ppc
  Cc: marcandre.lureau, Stefan Berger, Stefan Berger, qemu-devel, david

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 hw/ppc/Kconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig
index f927ec9c74..99eaaf17a1 100644
--- a/hw/ppc/Kconfig
+++ b/hw/ppc/Kconfig
@@ -10,6 +10,7 @@ config PSERIES
     select XICS_SPAPR
     select XIVE_SPAPR
     select MSI_NONBROKEN
+    select TPMDEV
 
 config SPAPR_RNG
     bool
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PACTH v3 5/5] docs: tpm: Add example command line for ppc64 and tpm-spapr
  2019-12-11 16:20 [PACTH v3 0/5] Add vTPM emulator supportfor ppc64 platform Stefan Berger
                   ` (3 preceding siblings ...)
  2019-12-11 16:20 ` [PACTH v3 4/5] hw/ppc/Kconfig: Enable TPMDEV as part of PSERIES config Stefan Berger
@ 2019-12-11 16:20 ` Stefan Berger
  2019-12-12 11:11   ` Marc-André Lureau
  4 siblings, 1 reply; 12+ messages in thread
From: Stefan Berger @ 2019-12-11 16:20 UTC (permalink / raw)
  To: qemu-ppc
  Cc: marcandre.lureau, Stefan Berger, Stefan Berger, qemu-devel, david

Add an example to the TPM docs for how to add a TPM SPAPR
device model to a QEMU VM emulating a pSeries machine.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 docs/specs/tpm.txt | 20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/docs/specs/tpm.txt b/docs/specs/tpm.txt
index 9c8cca042d..9c3e67d8a7 100644
--- a/docs/specs/tpm.txt
+++ b/docs/specs/tpm.txt
@@ -34,6 +34,12 @@ The CRB interface makes a memory mapped IO region in the area 0xfed40000 -
 QEMU files related to TPM CRB interface:
  - hw/tpm/tpm_crb.c
 
+
+pSeries (ppc64) machines offer a tpm-spapr device model.
+
+QEMU files related to the SPAPR interface:
+ - hw/tpm/tpm_spapr.c
+
 = fw_cfg interface =
 
 The bios/firmware may read the "etc/tpm/config" fw_cfg entry for
@@ -281,7 +287,7 @@ swtpm socket --tpmstate dir=/tmp/mytpm1 \
   --log level=20
 
 Command line to start QEMU with the TPM emulator device communicating with
-the swtpm:
+the swtpm (x86):
 
 qemu-system-x86_64 -display sdl -accel kvm \
   -m 1024 -boot d -bios bios-256k.bin -boot menu=on \
@@ -289,6 +295,18 @@ qemu-system-x86_64 -display sdl -accel kvm \
   -tpmdev emulator,id=tpm0,chardev=chrtpm \
   -device tpm-tis,tpmdev=tpm0 test.img
 
+In case a pSeries machine is emulated, use the following command line:
+
+qemu-system-ppc64 -display sdl -machine pseries,accel=kvm \
+  -m 1024 -bios slof.bin -boot menu=on \
+  -nodefaults -device VGA -device pci-ohci -device usb-kbd \
+  -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \
+  -tpmdev emulator,id=tpm0,chardev=chrtpm \
+  -device tpm-spapr,tpmdev=tpm0 \
+  -device spapr-vscsi,id=scsi0,reg=0x00002000 \
+  -device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x3,drive=drive-virtio-disk0,id=virtio-disk0 \
+  -drive file=test.img,format=raw,if=none,id=drive-virtio-disk0
+
 
 In case SeaBIOS is used as firmware, it should show the TPM menu item
 after entering the menu with 'ESC'.
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PACTH v3 3/5] tpm_spapr: Support suspend and resume
  2019-12-11 16:20 ` [PACTH v3 3/5] tpm_spapr: Support suspend and resume Stefan Berger
@ 2019-12-12 11:00   ` Marc-André Lureau
  2019-12-12 13:22     ` Stefan Berger
  0 siblings, 1 reply; 12+ messages in thread
From: Marc-André Lureau @ 2019-12-12 11:00 UTC (permalink / raw)
  To: Stefan Berger; +Cc: open list:sPAPR pseries, QEMU, David Gibson

Hi

On Wed, Dec 11, 2019 at 8:27 PM Stefan Berger
<stefanb@linux.vnet.ibm.com> wrote:
>
> Extend the tpm_spapr frontend with VM suspend and resume support.
>
> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
> ---
>  hw/tpm/tpm_spapr.c | 42 +++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 41 insertions(+), 1 deletion(-)
>
> diff --git a/hw/tpm/tpm_spapr.c b/hw/tpm/tpm_spapr.c
> index c4a67e2403..d9153bd95c 100644
> --- a/hw/tpm/tpm_spapr.c
> +++ b/hw/tpm/tpm_spapr.c
> @@ -87,6 +87,8 @@ typedef struct {
>      TPMVersion be_tpm_version;
>
>      size_t be_buffer_size;
> +
> +    bool deliver_response; /* whether to deliver response after VM resume */
>  } SPAPRvTPMState;
>
>  static void tpm_spapr_show_buffer(const unsigned char *buffer,
> @@ -339,9 +341,47 @@ static enum TPMVersion tpm_spapr_get_version(TPMIf *ti)
>      return tpm_backend_get_tpm_version(s->be_driver);
>  }
>
> +/* persistent state handling */
> +
> +static int tpm_spapr_pre_save(void *opaque)
> +{
> +    SPAPRvTPMState *s = opaque;
> +
> +    s->deliver_response = tpm_backend_finish_sync(s->be_driver);
> +    /*
> +     * we cannot deliver the results to the VM since DMA would touch VM memory
> +     */
> +
> +    return 0;
> +}
> +
> +static int tpm_spapr_post_load(void *opaque, int version_id)
> +{
> +    SPAPRvTPMState *s = opaque;
> +
> +    if (s->deliver_response) {
> +        /* deliver the results to the VM via DMA */
> +        tpm_spapr_request_completed(TPM_IF(s), 0);

Why isn't it enough to rely on tpm_spapr_request_completed callback
being called during pre-save when tpm_backend_finish_sync() is called?
(like tis & crb)

> +    }
> +
> +    return 0;
> +}
> +
>  static const VMStateDescription vmstate_spapr_vtpm = {
>      .name = "tpm-spapr",
> -    .unmigratable = 1,
> +    .version_id = 1,
> +    .minimum_version_id = 0,
> +    .minimum_version_id_old = 0,
> +    .pre_save = tpm_spapr_pre_save,
> +    .post_load = tpm_spapr_post_load,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_SPAPR_VIO(vdev, SPAPRvTPMState),
> +
> +        VMSTATE_UINT8(state, SPAPRvTPMState),
> +        VMSTATE_BUFFER(buffer, SPAPRvTPMState),
> +        VMSTATE_BOOL(deliver_response, SPAPRvTPMState),
> +        VMSTATE_END_OF_LIST(),
> +    }
>  };
>
>  static Property tpm_spapr_properties[] = {
> --
> 2.21.0
>
>


-- 
Marc-André Lureau


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PACTH v3 1/5] tpm_spapr: Support TPM for ppc64 using CRQ based interface
  2019-12-11 16:20 ` [PACTH v3 1/5] tpm_spapr: Support TPM for ppc64 using CRQ based interface Stefan Berger
@ 2019-12-12 11:05   ` Marc-André Lureau
  2019-12-12 12:59     ` Stefan Berger
  0 siblings, 1 reply; 12+ messages in thread
From: Marc-André Lureau @ 2019-12-12 11:05 UTC (permalink / raw)
  To: Stefan Berger; +Cc: open list:sPAPR pseries, QEMU, David Gibson

Hi

On Wed, Dec 11, 2019 at 8:25 PM Stefan Berger
<stefanb@linux.vnet.ibm.com> wrote:
>
> Implement support for TPM on ppc64 by implementing the vTPM CRQ interface
> as a frontend. It can use the tpm_emulator driver backend with the external
> swtpm.
>
> The Linux vTPM driver for ppc64 works with this emulation.
>
> This TPM emulator also handles the TPM 2 case.
>
> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
> Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
> ---
>  hw/tpm/Makefile.objs |   1 +
>  hw/tpm/tpm_spapr.c   | 405 +++++++++++++++++++++++++++++++++++++++++++
>  hw/tpm/trace-events  |  12 ++
>  include/sysemu/tpm.h |   3 +
>  qapi/tpm.json        |   6 +-
>  5 files changed, 424 insertions(+), 3 deletions(-)
>  create mode 100644 hw/tpm/tpm_spapr.c
>
> diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs
> index de0b85d02a..f9f29e0082 100644
> --- a/hw/tpm/Makefile.objs
> +++ b/hw/tpm/Makefile.objs
> @@ -4,3 +4,4 @@ common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o
>  common-obj-$(CONFIG_TPM_CRB) += tpm_crb.o
>  common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
>  common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o
> +obj-$(CONFIG_PSERIES) += tpm_spapr.o

looks good to me, but why not introduce CONFIG_TPM_SPAPR ?

> diff --git a/hw/tpm/tpm_spapr.c b/hw/tpm/tpm_spapr.c
> new file mode 100644
> index 0000000000..c4a67e2403
> --- /dev/null
> +++ b/hw/tpm/tpm_spapr.c
> @@ -0,0 +1,405 @@
> +/*
> + * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
> + *
> + * PAPR Virtual TPM
> + *
> + * Copyright (c) 2015, 2017 IBM Corporation.
> + *
> + * Authors:
> + *    Stefan Berger <stefanb@linux.vnet.ibm.com>
> + *
> + * This code is licensed under the GPL version 2 or later. See the
> + * COPYING file in the top-level directory.
> + *
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/error-report.h"
> +#include "qapi/error.h"
> +#include "hw/qdev-properties.h"
> +#include "migration/vmstate.h"
> +
> +#include "sysemu/tpm_backend.h"
> +#include "tpm_int.h"
> +#include "tpm_util.h"
> +
> +#include "hw/ppc/spapr.h"
> +#include "hw/ppc/spapr_vio.h"
> +#include "trace.h"
> +
> +#define DEBUG_SPAPR 0
> +
> +#define VIO_SPAPR_VTPM(obj) \
> +     OBJECT_CHECK(SPAPRvTPMState, (obj), TYPE_TPM_SPAPR)
> +
> +typedef struct VioCRQ {
> +    uint8_t valid;  /* 0x80: cmd; 0xc0: init crq */
> +                    /* 0x81-0x83: CRQ message response */
> +    uint8_t msg;    /* see below */
> +    uint16_t len;   /* len of TPM request; len of TPM response */
> +    uint32_t data;  /* rtce_dma_handle when sending TPM request */
> +    uint64_t reserved;
> +} VioCRQ;
> +
> +typedef union TPMSpaprCRQ {
> +    VioCRQ s;
> +    uint8_t raw[sizeof(VioCRQ)];
> +} TPMSpaprCRQ;
> +
> +#define SPAPR_VTPM_VALID_INIT_CRQ_COMMAND  0xC0
> +#define SPAPR_VTPM_VALID_COMMAND           0x80
> +#define SPAPR_VTPM_MSG_RESULT              0x80
> +
> +/* msg types for valid = SPAPR_VTPM_VALID_INIT_CRQ */
> +#define SPAPR_VTPM_INIT_CRQ_RESULT           0x1
> +#define SPAPR_VTPM_INIT_CRQ_COMPLETE_RESULT  0x2
> +
> +/* msg types for valid = SPAPR_VTPM_VALID_CMD */
> +#define SPAPR_VTPM_GET_VERSION               0x1
> +#define SPAPR_VTPM_TPM_COMMAND               0x2
> +#define SPAPR_VTPM_GET_RTCE_BUFFER_SIZE      0x3
> +#define SPAPR_VTPM_PREPARE_TO_SUSPEND        0x4
> +
> +/* response error messages */
> +#define SPAPR_VTPM_VTPM_ERROR                0xff
> +
> +/* error codes */
> +#define SPAPR_VTPM_ERR_COPY_IN_FAILED        0x3
> +#define SPAPR_VTPM_ERR_COPY_OUT_FAILED       0x4
> +
> +#define MAX_BUFFER_SIZE TARGET_PAGE_SIZE
> +
> +typedef struct {
> +    SpaprVioDevice vdev;
> +
> +    TPMSpaprCRQ crq; /* track single TPM command */
> +
> +    uint8_t state;
> +#define SPAPR_VTPM_STATE_NONE         0
> +#define SPAPR_VTPM_STATE_EXECUTION    1
> +#define SPAPR_VTPM_STATE_COMPLETION   2
> +
> +    unsigned char buffer[MAX_BUFFER_SIZE];
> +
> +    TPMBackendCmd cmd;
> +
> +    TPMBackend *be_driver;
> +    TPMVersion be_tpm_version;
> +
> +    size_t be_buffer_size;
> +} SPAPRvTPMState;
> +
> +static void tpm_spapr_show_buffer(const unsigned char *buffer,
> +                                  size_t buffer_size, const char *string)
> +{
> +    size_t len, i;
> +    char *line_buffer, *p;
> +
> +    len = MIN(tpm_cmd_get_size(buffer), buffer_size);
> +
> +    /*
> +     * allocate enough room for 3 chars per buffer entry plus a
> +     * newline after every 16 chars and a final null terminator.
> +     */
> +    line_buffer = g_malloc(len * 3 + (len / 16) + 1);
> +
> +    for (i = 0, p = line_buffer; i < len; i++) {
> +        if (i && !(i % 16)) {
> +            p += sprintf(p, "\n");
> +        }
> +        p += sprintf(p, "%.2X ", buffer[i]);
> +    }
> +    trace_tpm_spapr_show_buffer(string, len, line_buffer);
> +
> +    g_free(line_buffer);
> +}
> +
> +/*
> + * Send a request to the TPM.
> + */
> +static void tpm_spapr_tpm_send(SPAPRvTPMState *s)
> +{
> +    if (trace_event_get_state_backends(TRACE_TPM_SPAPR_SHOW_BUFFER)) {
> +        tpm_spapr_show_buffer(s->buffer, sizeof(s->buffer), "To TPM");
> +    }
> +
> +    s->state = SPAPR_VTPM_STATE_EXECUTION;
> +    s->cmd = (TPMBackendCmd) {
> +        .locty = 0,
> +        .in = s->buffer,
> +        .in_len = MIN(tpm_cmd_get_size(s->buffer), sizeof(s->buffer)),
> +        .out = s->buffer,
> +        .out_len = sizeof(s->buffer),
> +    };
> +
> +    tpm_backend_deliver_request(s->be_driver, &s->cmd);
> +}
> +
> +static int tpm_spapr_process_cmd(SPAPRvTPMState *s, uint64_t dataptr)
> +{
> +    long rc;
> +
> +    /* a max. of be_buffer_size bytes can be transported */
> +    rc = spapr_vio_dma_read(&s->vdev, dataptr,
> +                            s->buffer, s->be_buffer_size);
> +    if (rc) {
> +        error_report("tpm_spapr_got_payload: DMA read failure");
> +    }
> +    /* let vTPM handle any malformed request */
> +    tpm_spapr_tpm_send(s);
> +
> +    return rc;
> +}
> +
> +static int tpm_spapr_do_crq(struct SpaprVioDevice *dev, uint8_t *crq_data)
> +{
> +    SPAPRvTPMState *s = VIO_SPAPR_VTPM(dev);
> +    TPMSpaprCRQ local_crq;
> +    TPMSpaprCRQ *crq = &s->crq; /* requests only */
> +    int rc;
> +
> +    memcpy(&local_crq.raw, crq_data, sizeof(local_crq.raw));
> +
> +    trace_tpm_spapr_do_crq(local_crq.raw[0], local_crq.raw[1]);
> +
> +    switch (local_crq.s.valid) {
> +    case SPAPR_VTPM_VALID_INIT_CRQ_COMMAND: /* Init command/response */
> +
> +        /* Respond to initialization request */
> +        switch (local_crq.s.msg) {
> +        case SPAPR_VTPM_INIT_CRQ_RESULT:
> +            trace_tpm_spapr_do_crq_crq_result();
> +            memset(local_crq.raw, 0, sizeof(local_crq.raw));
> +            local_crq.s.valid = SPAPR_VTPM_VALID_INIT_CRQ_COMMAND;
> +            local_crq.s.msg = SPAPR_VTPM_INIT_CRQ_RESULT;
> +            spapr_vio_send_crq(dev, local_crq.raw);
> +            break;
> +
> +        case SPAPR_VTPM_INIT_CRQ_COMPLETE_RESULT:
> +            trace_tpm_spapr_do_crq_crq_complete_result();
> +            memset(local_crq.raw, 0, sizeof(local_crq.raw));
> +            local_crq.s.valid = SPAPR_VTPM_VALID_INIT_CRQ_COMMAND;
> +            local_crq.s.msg = SPAPR_VTPM_INIT_CRQ_COMPLETE_RESULT;
> +            spapr_vio_send_crq(dev, local_crq.raw);
> +            break;
> +        }
> +
> +        break;
> +    case SPAPR_VTPM_VALID_COMMAND: /* Payloads */
> +        switch (local_crq.s.msg) {
> +        case SPAPR_VTPM_TPM_COMMAND:
> +            trace_tpm_spapr_do_crq_tpm_command();
> +            if (s->state == SPAPR_VTPM_STATE_EXECUTION) {
> +                return H_BUSY;
> +            }
> +            /* this crq is tracked */
> +            memcpy(crq->raw, crq_data, sizeof(crq->raw));
> +
> +            rc = tpm_spapr_process_cmd(s, be32_to_cpu(crq->s.data));
> +
> +            if (rc == H_SUCCESS) {
> +                crq->s.valid = be16_to_cpu(0);
> +            } else {
> +                local_crq.s.valid = SPAPR_VTPM_MSG_RESULT;
> +                local_crq.s.msg = SPAPR_VTPM_VTPM_ERROR;
> +                local_crq.s.data = cpu_to_be32(SPAPR_VTPM_ERR_COPY_IN_FAILED);
> +                spapr_vio_send_crq(dev, local_crq.raw);
> +            }
> +            break;
> +
> +        case SPAPR_VTPM_GET_RTCE_BUFFER_SIZE:
> +            trace_tpm_spapr_do_crq_tpm_get_rtce_buffer_size(s->be_buffer_size);
> +            local_crq.s.msg |= SPAPR_VTPM_MSG_RESULT;
> +            local_crq.s.len = cpu_to_be16(s->be_buffer_size);
> +            spapr_vio_send_crq(dev, local_crq.raw);
> +            break;
> +
> +        case SPAPR_VTPM_GET_VERSION:
> +            local_crq.s.msg |= SPAPR_VTPM_MSG_RESULT;
> +            local_crq.s.len = cpu_to_be16(0);
> +            switch (s->be_tpm_version) {
> +            case TPM_VERSION_UNSPEC:
> +                local_crq.s.data = cpu_to_be32(0);
> +                break;
> +            case TPM_VERSION_1_2:
> +                local_crq.s.data = cpu_to_be32(1);
> +                break;
> +            case TPM_VERSION_2_0:
> +                local_crq.s.data = cpu_to_be32(2);
> +                break;
> +            }
> +            trace_tpm_spapr_do_crq_get_version(be32_to_cpu(local_crq.s.data));
> +            spapr_vio_send_crq(dev, local_crq.raw);
> +            break;
> +
> +        case SPAPR_VTPM_PREPARE_TO_SUSPEND:
> +            trace_tpm_spapr_do_crq_prepare_to_suspend();
> +            local_crq.s.msg |= SPAPR_VTPM_MSG_RESULT;
> +            spapr_vio_send_crq(dev, local_crq.raw);
> +            break;
> +
> +        default:
> +            trace_tpm_spapr_do_crq_unknown_msg_type(crq->s.msg);
> +        }
> +        break;
> +    default:
> +        trace_tpm_spapr_do_crq_unknown_crq(local_crq.raw[0], local_crq.raw[1]);
> +    };
> +
> +    return H_SUCCESS;
> +}
> +
> +static void tpm_spapr_request_completed(TPMIf *ti, int ret)
> +{
> +    SPAPRvTPMState *s = VIO_SPAPR_VTPM(ti);
> +    TPMSpaprCRQ *crq = &s->crq;
> +    uint32_t len;
> +    int rc;
> +
> +    s->state = SPAPR_VTPM_STATE_COMPLETION;
> +
> +    /* a max. of be_buffer_size bytes can be transported */
> +    len = MIN(tpm_cmd_get_size(s->buffer), s->be_buffer_size);
> +    rc = spapr_vio_dma_write(&s->vdev, be32_to_cpu(crq->s.data),
> +                             s->buffer, len);
> +
> +    if (trace_event_get_state_backends(TRACE_TPM_SPAPR_SHOW_BUFFER)) {
> +        tpm_spapr_show_buffer(s->buffer, len, "From TPM");
> +    }
> +
> +    crq->s.valid = SPAPR_VTPM_MSG_RESULT;
> +    if (rc == H_SUCCESS) {
> +        crq->s.msg = SPAPR_VTPM_TPM_COMMAND | SPAPR_VTPM_MSG_RESULT;
> +        crq->s.len = cpu_to_be16(len);
> +    } else {
> +        error_report("%s: DMA write failure", __func__);
> +        crq->s.msg = SPAPR_VTPM_VTPM_ERROR;
> +        crq->s.len = cpu_to_be16(0);
> +        crq->s.data = cpu_to_be32(SPAPR_VTPM_ERR_COPY_OUT_FAILED);
> +    }
> +
> +    rc = spapr_vio_send_crq(&s->vdev, crq->raw);
> +    if (rc) {
> +        error_report("%s: Error sending response", __func__);
> +    }
> +}
> +
> +static int tpm_spapr_do_startup_tpm(SPAPRvTPMState *s, size_t buffersize)
> +{
> +    return tpm_backend_startup_tpm(s->be_driver, buffersize);
> +}
> +
> +static void tpm_spapr_update_deviceclass(SpaprVioDevice *dev)
> +{
> +    SPAPRvTPMState *s = VIO_SPAPR_VTPM(dev);
> +    SpaprVioDeviceClass *k = VIO_SPAPR_DEVICE_GET_CLASS(dev);
> +
> +    switch (s->be_tpm_version) {
> +    case TPM_VERSION_UNSPEC:
> +        assert(false);
> +        break;
> +    case TPM_VERSION_1_2:
> +        k->dt_name = "vtpm";
> +        k->dt_type = "IBM,vtpm";
> +        k->dt_compatible = "IBM,vtpm";
> +        break;
> +    case TPM_VERSION_2_0:
> +        k->dt_name = "vtpm";
> +        k->dt_type = "IBM,vtpm";
> +        k->dt_compatible = "IBM,vtpm20";
> +        break;
> +    }
> +}
> +
> +static void tpm_spapr_reset(SpaprVioDevice *dev)
> +{
> +    SPAPRvTPMState *s = VIO_SPAPR_VTPM(dev);
> +
> +    s->state = SPAPR_VTPM_STATE_NONE;
> +
> +    s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver);
> +    tpm_spapr_update_deviceclass(dev);
> +
> +    s->be_buffer_size = MAX(ROUND_UP(tpm_backend_get_buffer_size(s->be_driver),
> +                                     TARGET_PAGE_SIZE),
> +                            sizeof(s->buffer));
> +
> +    tpm_backend_reset(s->be_driver);
> +    tpm_spapr_do_startup_tpm(s, s->be_buffer_size);
> +}
> +
> +static enum TPMVersion tpm_spapr_get_version(TPMIf *ti)
> +{
> +    SPAPRvTPMState *s = VIO_SPAPR_VTPM(ti);
> +
> +    if (tpm_backend_had_startup_error(s->be_driver)) {
> +        return TPM_VERSION_UNSPEC;
> +    }
> +
> +    return tpm_backend_get_tpm_version(s->be_driver);
> +}
> +
> +static const VMStateDescription vmstate_spapr_vtpm = {
> +    .name = "tpm-spapr",
> +    .unmigratable = 1,
> +};
> +
> +static Property tpm_spapr_properties[] = {
> +    DEFINE_SPAPR_PROPERTIES(SPAPRvTPMState, vdev),
> +    DEFINE_PROP_TPMBE("tpmdev", SPAPRvTPMState, be_driver),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void tpm_spapr_realizefn(SpaprVioDevice *dev, Error **errp)
> +{
> +    SPAPRvTPMState *s = VIO_SPAPR_VTPM(dev);
> +
> +    if (!tpm_find()) {
> +        error_setg(errp, "at most one TPM device is permitted");
> +        return;
> +    }
> +
> +    dev->crq.SendFunc = tpm_spapr_do_crq;
> +
> +    if (!s->be_driver) {
> +        error_setg(errp, "'tpmdev' property is required");
> +        return;
> +    }
> +}
> +
> +static void tpm_spapr_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    SpaprVioDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass);
> +    TPMIfClass *tc = TPM_IF_CLASS(klass);
> +
> +    k->realize = tpm_spapr_realizefn;
> +    k->reset = tpm_spapr_reset;
> +    k->signal_mask = 0x00000001;
> +    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
> +    dc->props = tpm_spapr_properties;
> +    k->rtce_window_size = 0x10000000;
> +    dc->vmsd = &vmstate_spapr_vtpm;
> +
> +    tc->model = TPM_MODEL_TPM_SPAPR;
> +    tc->get_version = tpm_spapr_get_version;
> +    tc->request_completed = tpm_spapr_request_completed;
> +}
> +
> +static const TypeInfo tpm_spapr_info = {
> +    .name          = TYPE_TPM_SPAPR,
> +    .parent        = TYPE_VIO_SPAPR_DEVICE,
> +    .instance_size = sizeof(SPAPRvTPMState),
> +    .class_init    = tpm_spapr_class_init,
> +    .interfaces = (InterfaceInfo[]) {
> +        { TYPE_TPM_IF },
> +        { }
> +    }
> +};
> +
> +static void tpm_spapr_register_types(void)
> +{
> +    type_register_static(&tpm_spapr_info);
> +}
> +
> +type_init(tpm_spapr_register_types)
> diff --git a/hw/tpm/trace-events b/hw/tpm/trace-events
> index 89804bcd64..6278a39618 100644
> --- a/hw/tpm/trace-events
> +++ b/hw/tpm/trace-events
> @@ -55,3 +55,15 @@ tpm_tis_pre_save(uint8_t locty, uint32_t rw_offset) "locty: %d, rw_offset = %u"
>
>  # tpm_ppi.c
>  tpm_ppi_memset(uint8_t *ptr, size_t size) "memset: %p %zu"
> +
> +# hw/tpm/tpm_spapr.c
> +tpm_spapr_show_buffer(const char *direction, size_t len, const char *buf) "direction: %s len: %zu\n%s"
> +tpm_spapr_do_crq(uint8_t raw1, uint8_t raw2) "1st 2 bytes in CRQ: 0x%02x 0x%02x"
> +tpm_spapr_do_crq_crq_result(void) "SPAPR_VTPM_INIT_CRQ_RESULT"
> +tpm_spapr_do_crq_crq_complete_result(void) "SPAPR_VTPM_INIT_CRQ_COMP_RESULT"
> +tpm_spapr_do_crq_tpm_command(void) "got TPM command payload"
> +tpm_spapr_do_crq_tpm_get_rtce_buffer_size(size_t buffersize) "response: buffer size is %zu"
> +tpm_spapr_do_crq_get_version(uint32_t version) "response: version %u"
> +tpm_spapr_do_crq_prepare_to_suspend(void) "response: preparing to suspend"
> +tpm_spapr_do_crq_unknown_msg_type(uint8_t type) "Unknown message type 0x%02x"
> +tpm_spapr_do_crq_unknown_crq(uint8_t raw1, uint8_t raw2) "unknown CRQ 0x%02x 0x%02x ..."
> diff --git a/include/sysemu/tpm.h b/include/sysemu/tpm.h
> index 5b541a71c8..15979a3647 100644
> --- a/include/sysemu/tpm.h
> +++ b/include/sysemu/tpm.h
> @@ -45,11 +45,14 @@ typedef struct TPMIfClass {
>
>  #define TYPE_TPM_TIS                "tpm-tis"
>  #define TYPE_TPM_CRB                "tpm-crb"
> +#define TYPE_TPM_SPAPR              "tpm-spapr"
>
>  #define TPM_IS_TIS(chr)                             \
>      object_dynamic_cast(OBJECT(chr), TYPE_TPM_TIS)
>  #define TPM_IS_CRB(chr)                             \
>      object_dynamic_cast(OBJECT(chr), TYPE_TPM_CRB)
> +#define TPM_IS_SPAPR(chr)                           \
> +    object_dynamic_cast(OBJECT(chr), TYPE_TPM_SPAPR)
>
>  /* returns NULL unless there is exactly one TPM device */
>  static inline TPMIf *tpm_find(void)
> diff --git a/qapi/tpm.json b/qapi/tpm.json
> index b30323bb6b..63878aa0f4 100644
> --- a/qapi/tpm.json
> +++ b/qapi/tpm.json
> @@ -12,11 +12,11 @@
>  #
>  # @tpm-tis: TPM TIS model
>  # @tpm-crb: TPM CRB model (since 2.12)
> +# @tpm-spapr: TPM SPAPR model (since 5.0)
>  #
>  # Since: 1.5
>  ##
> -{ 'enum': 'TpmModel', 'data': [ 'tpm-tis', 'tpm-crb' ] }
> -
> +{ 'enum': 'TpmModel', 'data': [ 'tpm-tis', 'tpm-crb', 'tpm-spapr' ] }
>  ##
>  # @query-tpm-models:
>  #
> @@ -29,7 +29,7 @@
>  # Example:
>  #
>  # -> { "execute": "query-tpm-models" }
> -# <- { "return": [ "tpm-tis", "tpm-crb" ] }
> +# <- { "return": [ "tpm-tis", "tpm-crb", "tpm-spapr" ] }
>  #
>  ##
>  { 'command': 'query-tpm-models', 'returns': ['TpmModel'] }
> --
> 2.21.0
>
>


-- 
Marc-André Lureau


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PACTH v3 5/5] docs: tpm: Add example command line for ppc64 and tpm-spapr
  2019-12-11 16:20 ` [PACTH v3 5/5] docs: tpm: Add example command line for ppc64 and tpm-spapr Stefan Berger
@ 2019-12-12 11:11   ` Marc-André Lureau
  0 siblings, 0 replies; 12+ messages in thread
From: Marc-André Lureau @ 2019-12-12 11:11 UTC (permalink / raw)
  To: Stefan Berger; +Cc: David Gibson, open list:sPAPR pseries, QEMU, Stefan Berger

Hi

On Wed, Dec 11, 2019 at 8:25 PM Stefan Berger
<stefanb@linux.vnet.ibm.com> wrote:
>
> Add an example to the TPM docs for how to add a TPM SPAPR
> device model to a QEMU VM emulating a pSeries machine.
>
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

(I'll look at converting it to rst)

> ---
>  docs/specs/tpm.txt | 20 +++++++++++++++++++-
>  1 file changed, 19 insertions(+), 1 deletion(-)
>
> diff --git a/docs/specs/tpm.txt b/docs/specs/tpm.txt
> index 9c8cca042d..9c3e67d8a7 100644
> --- a/docs/specs/tpm.txt
> +++ b/docs/specs/tpm.txt
> @@ -34,6 +34,12 @@ The CRB interface makes a memory mapped IO region in the area 0xfed40000 -
>  QEMU files related to TPM CRB interface:
>   - hw/tpm/tpm_crb.c
>
> +
> +pSeries (ppc64) machines offer a tpm-spapr device model.
> +
> +QEMU files related to the SPAPR interface:
> + - hw/tpm/tpm_spapr.c
> +
>  = fw_cfg interface =
>
>  The bios/firmware may read the "etc/tpm/config" fw_cfg entry for
> @@ -281,7 +287,7 @@ swtpm socket --tpmstate dir=/tmp/mytpm1 \
>    --log level=20
>
>  Command line to start QEMU with the TPM emulator device communicating with
> -the swtpm:
> +the swtpm (x86):
>
>  qemu-system-x86_64 -display sdl -accel kvm \
>    -m 1024 -boot d -bios bios-256k.bin -boot menu=on \
> @@ -289,6 +295,18 @@ qemu-system-x86_64 -display sdl -accel kvm \
>    -tpmdev emulator,id=tpm0,chardev=chrtpm \
>    -device tpm-tis,tpmdev=tpm0 test.img
>
> +In case a pSeries machine is emulated, use the following command line:
> +
> +qemu-system-ppc64 -display sdl -machine pseries,accel=kvm \
> +  -m 1024 -bios slof.bin -boot menu=on \
> +  -nodefaults -device VGA -device pci-ohci -device usb-kbd \
> +  -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \
> +  -tpmdev emulator,id=tpm0,chardev=chrtpm \
> +  -device tpm-spapr,tpmdev=tpm0 \
> +  -device spapr-vscsi,id=scsi0,reg=0x00002000 \
> +  -device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x3,drive=drive-virtio-disk0,id=virtio-disk0 \
> +  -drive file=test.img,format=raw,if=none,id=drive-virtio-disk0
> +
>
>  In case SeaBIOS is used as firmware, it should show the TPM menu item
>  after entering the menu with 'ESC'.
> --
> 2.21.0
>
>


-- 
Marc-André Lureau


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PACTH v3 1/5] tpm_spapr: Support TPM for ppc64 using CRQ based interface
  2019-12-12 11:05   ` Marc-André Lureau
@ 2019-12-12 12:59     ` Stefan Berger
  0 siblings, 0 replies; 12+ messages in thread
From: Stefan Berger @ 2019-12-12 12:59 UTC (permalink / raw)
  To: Marc-André Lureau, Stefan Berger
  Cc: open list:sPAPR pseries, QEMU, David Gibson

On 12/12/19 6:05 AM, Marc-André Lureau wrote:
> Hi
>
> On Wed, Dec 11, 2019 at 8:25 PM Stefan Berger
> <stefanb@linux.vnet.ibm.com> wrote:
>> Implement support for TPM on ppc64 by implementing the vTPM CRQ interface
>> as a frontend. It can use the tpm_emulator driver backend with the external
>> swtpm.
>>
>> The Linux vTPM driver for ppc64 works with this emulation.
>>
>> This TPM emulator also handles the TPM 2 case.
>>
>> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
>> Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
>> ---
>>   hw/tpm/Makefile.objs |   1 +
>>   hw/tpm/tpm_spapr.c   | 405 +++++++++++++++++++++++++++++++++++++++++++
>>   hw/tpm/trace-events  |  12 ++
>>   include/sysemu/tpm.h |   3 +
>>   qapi/tpm.json        |   6 +-
>>   5 files changed, 424 insertions(+), 3 deletions(-)
>>   create mode 100644 hw/tpm/tpm_spapr.c
>>
>> diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs
>> index de0b85d02a..f9f29e0082 100644
>> --- a/hw/tpm/Makefile.objs
>> +++ b/hw/tpm/Makefile.objs
>> @@ -4,3 +4,4 @@ common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o
>>   common-obj-$(CONFIG_TPM_CRB) += tpm_crb.o
>>   common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
>>   common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o
>> +obj-$(CONFIG_PSERIES) += tpm_spapr.o
> looks good to me, but why not introduce CONFIG_TPM_SPAPR ?


Good point. I fixed it.





^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PACTH v3 3/5] tpm_spapr: Support suspend and resume
  2019-12-12 11:00   ` Marc-André Lureau
@ 2019-12-12 13:22     ` Stefan Berger
  2019-12-12 17:12       ` Stefan Berger
  0 siblings, 1 reply; 12+ messages in thread
From: Stefan Berger @ 2019-12-12 13:22 UTC (permalink / raw)
  To: Marc-André Lureau, Stefan Berger
  Cc: open list:sPAPR pseries, QEMU, David Gibson

On 12/12/19 6:00 AM, Marc-André Lureau wrote:
> Hi
>
> On Wed, Dec 11, 2019 at 8:27 PM Stefan Berger
> <stefanb@linux.vnet.ibm.com> wrote:
>> Extend the tpm_spapr frontend with VM suspend and resume support.
>>
>> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
>> ---
>>   hw/tpm/tpm_spapr.c | 42 +++++++++++++++++++++++++++++++++++++++++-
>>   1 file changed, 41 insertions(+), 1 deletion(-)
>>
>> diff --git a/hw/tpm/tpm_spapr.c b/hw/tpm/tpm_spapr.c
>> index c4a67e2403..d9153bd95c 100644
>> --- a/hw/tpm/tpm_spapr.c
>> +++ b/hw/tpm/tpm_spapr.c
>> @@ -87,6 +87,8 @@ typedef struct {
>>       TPMVersion be_tpm_version;
>>
>>       size_t be_buffer_size;
>> +
>> +    bool deliver_response; /* whether to deliver response after VM resume */
>>   } SPAPRvTPMState;
>>
>>   static void tpm_spapr_show_buffer(const unsigned char *buffer,
>> @@ -339,9 +341,47 @@ static enum TPMVersion tpm_spapr_get_version(TPMIf *ti)
>>       return tpm_backend_get_tpm_version(s->be_driver);
>>   }
>>
>> +/* persistent state handling */
>> +
>> +static int tpm_spapr_pre_save(void *opaque)
>> +{
>> +    SPAPRvTPMState *s = opaque;
>> +
>> +    s->deliver_response = tpm_backend_finish_sync(s->be_driver);
>> +    /*
>> +     * we cannot deliver the results to the VM since DMA would touch VM memory
>> +     */
>> +
>> +    return 0;
>> +}
>> +
>> +static int tpm_spapr_post_load(void *opaque, int version_id)
>> +{
>> +    SPAPRvTPMState *s = opaque;
>> +
>> +    if (s->deliver_response) {
>> +        /* deliver the results to the VM via DMA */
>> +        tpm_spapr_request_completed(TPM_IF(s), 0);
> Why isn't it enough to rely on tpm_spapr_request_completed callback
> being called during pre-save when tpm_backend_finish_sync() is called?
> (like tis & crb)


When .pre_save is called the VM memory has been fully replicated and 
only the devices need to save their state, right? So TIS and CRB save 
the response in memory of the device for the OS driver to pick up after 
resume. The SPAPR device model is expected to write the response into VM 
memory using DMA but memory won't be marked dirty anymore and replicated 
(afaik). So we may have the mechanism of having 
tpm_spapr_request_completed() invoked but in addition we need to 
re-deliver a response after resume so that the OS driver reads the 
proper response then. I'll investigate, though...




^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PACTH v3 3/5] tpm_spapr: Support suspend and resume
  2019-12-12 13:22     ` Stefan Berger
@ 2019-12-12 17:12       ` Stefan Berger
  0 siblings, 0 replies; 12+ messages in thread
From: Stefan Berger @ 2019-12-12 17:12 UTC (permalink / raw)
  To: Marc-André Lureau, Stefan Berger
  Cc: open list:sPAPR pseries, QEMU, David Gibson

On 12/12/19 8:22 AM, Stefan Berger wrote:
> On 12/12/19 6:00 AM, Marc-André Lureau wrote:
>> Hi
>>
>> On Wed, Dec 11, 2019 at 8:27 PM Stefan Berger
>> <stefanb@linux.vnet.ibm.com> wrote:
>>> Extend the tpm_spapr frontend with VM suspend and resume support.
>>>
>>> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
>>> ---
>>>   hw/tpm/tpm_spapr.c | 42 +++++++++++++++++++++++++++++++++++++++++-
>>>   1 file changed, 41 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/hw/tpm/tpm_spapr.c b/hw/tpm/tpm_spapr.c
>>> index c4a67e2403..d9153bd95c 100644
>>> --- a/hw/tpm/tpm_spapr.c
>>> +++ b/hw/tpm/tpm_spapr.c
>>> @@ -87,6 +87,8 @@ typedef struct {
>>>       TPMVersion be_tpm_version;
>>>
>>>       size_t be_buffer_size;
>>> +
>>> +    bool deliver_response; /* whether to deliver response after VM 
>>> resume */
>>>   } SPAPRvTPMState;
>>>
>>>   static void tpm_spapr_show_buffer(const unsigned char *buffer,
>>> @@ -339,9 +341,47 @@ static enum TPMVersion 
>>> tpm_spapr_get_version(TPMIf *ti)
>>>       return tpm_backend_get_tpm_version(s->be_driver);
>>>   }
>>>
>>> +/* persistent state handling */
>>> +
>>> +static int tpm_spapr_pre_save(void *opaque)
>>> +{
>>> +    SPAPRvTPMState *s = opaque;
>>> +
>>> +    s->deliver_response = tpm_backend_finish_sync(s->be_driver);
>>> +    /*
>>> +     * we cannot deliver the results to the VM since DMA would 
>>> touch VM memory
>>> +     */
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int tpm_spapr_post_load(void *opaque, int version_id)
>>> +{
>>> +    SPAPRvTPMState *s = opaque;
>>> +
>>> +    if (s->deliver_response) {
>>> +        /* deliver the results to the VM via DMA */
>>> +        tpm_spapr_request_completed(TPM_IF(s), 0);
>> Why isn't it enough to rely on tpm_spapr_request_completed callback
>> being called during pre-save when tpm_backend_finish_sync() is called?
>> (like tis & crb)
>
>
> When .pre_save is called the VM memory has been fully replicated and 
> only the devices need to save their state, right? So TIS and CRB save 
> the response in memory of the device for the OS driver to pick up 
> after resume. The SPAPR device model is expected to write the response 
> into VM memory using DMA but memory won't be marked dirty anymore and 
> replicated (afaik). So we may have the mechanism of having 
> tpm_spapr_request_completed() invoked but in addition we need to 
> re-deliver a response after resume so that the OS driver reads the 
> proper response then. I'll investigate, though...


Suspend/resume works fine for as long as we don't catch a 'delayed 
response', meaning a response received while the devices suspend (and 
.pre_save is called). With this device the troubles are starting when 
having to deliver such a 'delayed response' because the whole 
tpm_spapr_request_completed() has to be deferred until after resume, 
otherwise the OS driver gets stuck. So, in tpm_spapr_request_completed() 
we need to be able to query the backend whether it is suspending, 
meaning whether its .pre_save() has been invoked and we received the 
response due to polling. If so, we must not do anything in this function 
at this time but do it in post_load and we need to remember this in a 
field 'deliver_response'. I have this field now but the reason why I set 
it was not correct, at least not for the tpm_emulator backend. In case 
we do get a delayed response while in the tpm_spapr's pre_save function, 
we do need to set the 'deliver_response' as well in order to call the 
tpm_spapr_request_completed() in tpm_spapr_post_load() when resuming.

The easiest way to trigger all this is to read a TPM 2.0's PCRs inside a 
VM and have libtpms instrumented with a sleep(4) in PCRRead() function 
to cause enough delays to trigger the critical code when suspending the VM.

I'll repost with these fixes.

    Stefan


>
>



^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2019-12-12 18:13 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-12-11 16:20 [PACTH v3 0/5] Add vTPM emulator supportfor ppc64 platform Stefan Berger
2019-12-11 16:20 ` [PACTH v3 1/5] tpm_spapr: Support TPM for ppc64 using CRQ based interface Stefan Berger
2019-12-12 11:05   ` Marc-André Lureau
2019-12-12 12:59     ` Stefan Berger
2019-12-11 16:20 ` [PACTH v3 2/5] tpm: Return bool from tpm_backend_finish_sync Stefan Berger
2019-12-11 16:20 ` [PACTH v3 3/5] tpm_spapr: Support suspend and resume Stefan Berger
2019-12-12 11:00   ` Marc-André Lureau
2019-12-12 13:22     ` Stefan Berger
2019-12-12 17:12       ` Stefan Berger
2019-12-11 16:20 ` [PACTH v3 4/5] hw/ppc/Kconfig: Enable TPMDEV as part of PSERIES config Stefan Berger
2019-12-11 16:20 ` [PACTH v3 5/5] docs: tpm: Add example command line for ppc64 and tpm-spapr Stefan Berger
2019-12-12 11:11   ` Marc-André Lureau

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).