From patchwork Mon Jan 4 16:14:24 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Berger X-Patchwork-Id: 562449 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 082AC1401CD for ; Tue, 5 Jan 2016 03:15:25 +1100 (AEDT) Received: from localhost ([::1]:45671 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aG7n0-0006Ul-UM for incoming@patchwork.ozlabs.org; Mon, 04 Jan 2016 11:15:22 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:37928) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aG7mN-0005FB-E0 for qemu-devel@nongnu.org; Mon, 04 Jan 2016 11:14:46 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1aG7mJ-0005Ys-0O for qemu-devel@nongnu.org; Mon, 04 Jan 2016 11:14:43 -0500 Received: from e17.ny.us.ibm.com ([129.33.205.207]:44285) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aG7mI-0005YX-RC for qemu-devel@nongnu.org; Mon, 04 Jan 2016 11:14:38 -0500 Received: from localhost by e17.ny.us.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Mon, 4 Jan 2016 11:14:37 -0500 Received: from d01dlp02.pok.ibm.com (9.56.250.167) by e17.ny.us.ibm.com (146.89.104.204) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Mon, 4 Jan 2016 11:14:34 -0500 X-IBM-Helo: d01dlp02.pok.ibm.com X-IBM-MailFrom: stefanb@us.ibm.com X-IBM-RcptTo: qemu-devel@nongnu.org;qemu-ppc@nongnu.org Received: from b01cxnp23033.gho.pok.ibm.com (b01cxnp23033.gho.pok.ibm.com [9.57.198.28]) by d01dlp02.pok.ibm.com (Postfix) with ESMTP id 646F96E8041; Mon, 4 Jan 2016 11:14:32 -0500 (EST) Received: from d01av01.pok.ibm.com (d01av01.pok.ibm.com [9.56.224.215]) by b01cxnp23033.gho.pok.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id u04GEWNX25624722; Mon, 4 Jan 2016 16:14:32 GMT Received: from d01av01.pok.ibm.com (localhost [127.0.0.1]) by d01av01.pok.ibm.com (8.14.4/8.14.4/NCO v10.0 AVout) with ESMTP id u04GEUEY003153; Mon, 4 Jan 2016 11:14:31 -0500 Received: from sbct-3.watson.ibm.com (sbct-3.watson.ibm.com [9.2.141.158]) by d01av01.pok.ibm.com (8.14.4/8.14.4/NCO v10.0 AVin) with ESMTP id u04GEPRt002369; Mon, 4 Jan 2016 11:14:29 -0500 From: Stefan Berger To: qemu-ppc@nongnu.org Date: Mon, 4 Jan 2016 11:14:24 -0500 Message-Id: <1451924064-8625-4-git-send-email-stefanb@us.ibm.com> X-Mailer: git-send-email 2.4.3 In-Reply-To: <1451924064-8625-1-git-send-email-stefanb@us.ibm.com> References: <1451924064-8625-1-git-send-email-stefanb@us.ibm.com> X-TM-AS-MML: disable X-Content-Scanned: Fidelis XPS MAILER x-cbid: 16010416-0041-0000-0000-000002F19575 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 129.33.205.207 Cc: thuth@redhat.com, aik@au1.ibm.com, nikunj@linux.vnet.ibm.com, Stefan Berger , agraf@suse.de, qemu-devel@nongnu.org, jb613w@att.com Subject: [Qemu-devel] [PATCH v2 3/3] tpm: Support TPM for ppc64 using CRQ based interface X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Stefan Berger Implement support for TPM on ppc64 by implementing the vTPM CRQ interface (following PAPR) as a frontend. Have the frontend call the existing passthrough TPM device backend using data structures from the TPM TIS hardware interface emulation (tpm_tis.c). The TIS implements a superset of functionality and the vTPM CRQ interface uses part of it, such as only 1 of the 5 localities. The Linux vTPM driver for ppc64 works with this emulation. Signed-off-by: Stefan Berger --- hw/tpm/Makefile.objs | 2 + hw/tpm/spapr_vtpm.c | 541 +++++++++++++++++++++++++++++++++++++++++++++ hw/tpm/spapr_vtpm.h | 65 ++++++ include/hw/ppc/spapr_vio.h | 1 + include/sysemu/tpm.h | 1 + qapi-schema.json | 6 +- 6 files changed, 615 insertions(+), 1 deletion(-) create mode 100644 hw/tpm/spapr_vtpm.c create mode 100644 hw/tpm/spapr_vtpm.h diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs index 64cecc3..f40f31d 100644 --- a/hw/tpm/Makefile.objs +++ b/hw/tpm/Makefile.objs @@ -1,2 +1,4 @@ common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o tpm_util.o + +obj-$(CONFIG_PSERIES) += spapr_vtpm.o diff --git a/hw/tpm/spapr_vtpm.c b/hw/tpm/spapr_vtpm.c new file mode 100644 index 0000000..2560a9b --- /dev/null +++ b/hw/tpm/spapr_vtpm.c @@ -0,0 +1,541 @@ +/* + * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator + * + * PAPR Virtual TPM, aka ibmvtpm + * + * Parts based on spapr_vscsi.c + * Copyright (c) 2010,2011 Benjamin Herrenschmidt, IBM Corporation. + * + * Parts based on tpm_tis.c + * Copyright (C) 2006,2010-2013 IBM Corporation + * + * Copyright (c) 2015 IBM Corporation. + * + * Authors: + * Stefan Berger + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +/* + * For communication with the backend we are using the data structures used + * by the TPM TIS implementation (tpm_tis.c), which provides a superset of + * functionality with up to 5 localities. We always use locality = 0 and + * reuse the buffer to transfer the TPM command packets to the backend and + * receive TPM responses from the backend. + */ + +#include "sysemu/tpm_backend.h" +#include "tpm_int.h" +#include "qemu/main-loop.h" +#include "sysemu/tpm_backend.h" + +#include "spapr_vtpm.h" +#include "hw/ppc/spapr.h" +#include "hw/ppc/spapr_vio.h" + +#include + +#define DEBUG_SPAPR_VTPM 0 + +#define DPRINTF(fmt, ...) do { \ + if (DEBUG_SPAPR_VTPM) { \ + printf("QEMU-vTPM:" fmt, ## __VA_ARGS__); \ + } \ +} while (0); + + +#define TYPE_VIO_SPAPR_VTPM_DEVICE "spapr-vtpm" +#define VIO_SPAPR_VTPM_DEVICE(obj) \ + OBJECT_CHECK(SPAPRvTPMState, (obj), TYPE_VIO_SPAPR_VTPM_DEVICE) + +typedef struct { + VIOsPAPRDevice vdev; + + spapr_vtpm_crq crq; /* track single TPM command */ + + union { + /* + * The backends expect TIS related data structures; + * we reuse it but only use locality 0. + */ + TPMTISEmuState tis; + } s; + + uint8_t locty_number; + TPMLocality *locty_data; + + char *backend; + TPMBackend *be_driver; + TPMVersion be_tpm_version; + + QemuMutex state_lock; + QemuCond cmd_complete; +} SPAPRvTPMState; + +/* Only use 1 locality (locality 0) */ +#define SPAPR_VTPM_NUM_LOCALITIES 1 + +static uint32_t spapr_vtpm_get_size_from_buffer(const TPMSizedBuffer *sb) +{ + return be32_to_cpu(*(uint32_t *)&sb->buffer[2]); +} + +static void spapr_vtpm_show_buffer(const TPMSizedBuffer *sb, const char *string) +{ +#if DEBUG_SPAPR_VTPM + uint32_t len, i; + + len = spapr_vtpm_get_size_from_buffer(sb); + printf("spapr_vtpm: %s length = %d\n", string, len); + for (i = 0; i < len; i++) { + if (i && !(i % 16)) { + printf("\n"); + } + printf("%.2X ", sb->buffer[i]); + } + printf("\n"); +#endif +} + +/* + * Send a request to the TPM. + */ +static void spapr_vtpm_tpm_send(SPAPRvTPMState *s, uint8_t locty) +{ + TPMTISEmuState *tis = &s->s.tis; + + spapr_vtpm_show_buffer(&tis->loc[locty].w_buffer, "spapr_vtpm: Tx TPM"); + + s->locty_number = locty; + s->locty_data = &tis->loc[locty]; + + /* + * w_offset serves as length indicator for length of data; + * it's reset when the response comes back. + * Since we copy the data via DMA, we need to set it here explicitly. + */ + tis->loc[locty].w_offset = + spapr_vtpm_get_size_from_buffer(&tis->loc[locty].w_buffer); + + tis->loc[locty].state = TPM_TIS_STATE_EXECUTION; + + tpm_backend_deliver_request(s->be_driver); +} + +static void spapr_vtpm_got_payload(SPAPRvTPMState *s, spapr_vtpm_crq *crq) +{ + TPMTISEmuState *tis = &s->s.tis; + uint8_t locty = 0; + + DPRINTF("vtpm_got_payload: crq->s.data = 0x%x crq->s.len = %d\n", + crq->s.data, crq->s.len); + /* XXX Handle failure differently ? */ + if (spapr_vio_dma_read(&s->vdev, crq->s.data, + tis->loc[locty].w_buffer.buffer, + tis->loc[locty].w_buffer.size)) { + fprintf(stderr, "vtpm_got_payload: DMA read failure !\n"); + return; + } + + /* let vTPM handle any malformed request */ + spapr_vtpm_tpm_send(s, locty); +} + +static int spapr_vtpm_do_crq(struct VIOsPAPRDevice *dev, uint8_t *crq_data) +{ + SPAPRvTPMState *s = VIO_SPAPR_VTPM_DEVICE(dev); + TPMTISEmuState *tis = &s->s.tis; + uint8_t locty = 0; + spapr_vtpm_crq local_crq; + spapr_vtpm_crq *crq = &s->crq; /* use for TPM requests only */ + + memcpy(&local_crq.raw, crq_data, sizeof(local_crq.raw)); + + DPRINTF("VTPM: do_crq %02x %02x ...\n", + 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: + DPRINTF("vtpm_do_crq: SPAPR_VTPM_INIT_CRQ_RESULT\n"); + 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: + DPRINTF("vtpm_do_crq: SPAPR_VTPM_INIT_CRQ_COMP_RESULT\n"); + 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: + DPRINTF("vtpm_do_crq: got TPM command payload!\n"); + if (tis->loc[locty].state == TPM_TIS_STATE_EXECUTION) + return H_BUSY; + /* this crq is tracked */ + memcpy(crq->raw, crq_data, sizeof(crq->raw)); + crq->s.valid = be16_to_cpu(0); + crq->s.len = be16_to_cpu(crq->s.len); + crq->s.data = be32_to_cpu(crq->s.data); + spapr_vtpm_got_payload(s, crq); + break; + + case SPAPR_VTPM_GET_RTCE_BUFFER_SIZE: + DPRINTF("vtpm_do_crq: resp: buffer size is %u\n", + tis->loc[locty].w_buffer.size); + local_crq.s.msg |= SPAPR_VTPM_MSG_RESULT; + local_crq.s.len = cpu_to_be16(tis->loc[locty].w_buffer.size); + spapr_vio_send_crq(dev, local_crq.raw); + break; + + case SPAPR_VTPM_GET_VERSION: + DPRINTF("vtpm_do_crq: resp: version 1\n"); + 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; + } + spapr_vio_send_crq(dev, local_crq.raw); + break; + + case SPAPR_VTPM_PREPARE_TO_SUSPEND: + DPRINTF("vtpm_do_crq: resp: prep to suspend\n"); + local_crq.s.msg |= SPAPR_VTPM_MSG_RESULT; + spapr_vio_send_crq(dev, local_crq.raw); + break; + + default: + fprintf(stderr, "vtpm_do_crq: Unknown message type %02x\n", + crq->s.msg); + } + break; + default: + fprintf(stderr, "vtpm_do_crq: unknown CRQ %02x %02x ...\n", + local_crq.raw[0], local_crq.raw[1]); + }; + + return 0; +} + +static void spapr_vtpm_receive_bh(void *opaque) +{ + SPAPRvTPMState *s = opaque; + TPMTISEmuState *tis = &s->s.tis; + spapr_vtpm_crq *crq = &s->crq; + uint8_t locty = 0; + uint32_t len; + int rc; + + tis->bh_scheduled = false; + + qemu_mutex_lock(&s->state_lock); + + tis->loc[locty].state = TPM_TIS_STATE_COMPLETION; + tis->loc[locty].r_offset = 0; + tis->loc[locty].w_offset = 0; + + len = spapr_vtpm_get_size_from_buffer(&tis->loc[locty].r_buffer); + + spapr_vtpm_show_buffer(&tis->loc[locty].r_buffer, "spapr_vtpm: rx TPM"); + + DPRINTF("dma_write to crq->s.data = 0x%x\n", crq->s.data); + rc = spapr_vio_dma_write(&s->vdev, crq->s.data, + tis->loc[locty].r_buffer.buffer, + MIN(len, tis->loc[locty].r_buffer.size)); + + crq->s.valid = SPAPR_VTPM_MSG_RESULT; + crq->s.msg = SPAPR_VTPM_TPM_COMMAND | SPAPR_VTPM_MSG_RESULT; + crq->s.len = cpu_to_be16(len); + crq->s.data = cpu_to_be32(crq->s.data); + + if (rc == 0) { + rc = spapr_vio_send_crq(&s->vdev, crq->raw); + if (rc) { + fprintf(stderr, "spapr_vtpm_receive_bh: Error sending response\n"); + } + } else { + fprintf(stderr, "spapr_vtpm_receive_bh: Error with DMA write\n"); + } + + /* notify of completed command */ + qemu_cond_signal(&s->cmd_complete); + qemu_mutex_unlock(&s->state_lock); +} + +/* + * Callback from the TPM to indicate that the response was received. + */ +static void spapr_vtpm_receive_cb(void *opaque, uint8_t locty, + bool is_selftest_done) +{ + SPAPRvTPMState *s = opaque; + TPMTISEmuState *tis = &s->s.tis; + + qemu_mutex_lock(&s->state_lock); + /* notify of completed command */ + qemu_cond_signal(&s->cmd_complete); + qemu_mutex_unlock(&s->state_lock); + + qemu_bh_schedule(tis->bh); + + tis->bh_scheduled = true; +} + +static int spapr_vtpm_do_startup_tpm(SPAPRvTPMState *s) +{ + return tpm_backend_startup_tpm(s->be_driver); +} + +/* + * Get the TPMVersion of the backend device being used + */ +TPMVersion spapr_vtpm_get_tpm_version(Object *obj) +{ + SPAPRvTPMState *s = VIO_SPAPR_VTPM_DEVICE(obj); + + return tpm_backend_get_tpm_version(s->be_driver); +} + +static void spapr_vtpm_reset(VIOsPAPRDevice *dev) +{ + SPAPRvTPMState *s = VIO_SPAPR_VTPM_DEVICE(dev); + TPMTISEmuState *tis = &s->s.tis; + int c; + + s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver); + + tpm_backend_reset(s->be_driver); + + for (c = 0; c < SPAPR_VTPM_NUM_LOCALITIES; c++) { + tis->loc[c].w_offset = 0; + tpm_backend_realloc_buffer(s->be_driver, &tis->loc[c].w_buffer); + tis->loc[c].r_offset = 0; + tpm_backend_realloc_buffer(s->be_driver, &tis->loc[c].r_buffer); + } + + spapr_vtpm_do_startup_tpm(s); +} + +void spapr_vtpm_create(VIOsPAPRBus *bus) +{ + DeviceState *dev; + + DPRINTF("%s\n", __func__); + + dev = qdev_create(&bus->bus, "spapr-vtpm"); + + qdev_init_nofail(dev); +} + +/* persistent state handling */ + +static void spapr_vtpm_pre_save(void *opaque) +{ + TPMState *s = opaque; + TPMTISEmuState *tis = &s->s.tis; + uint8_t locty = 0; + + DPRINTF("vtpm: suspend: locty = %d : r_offset = %d, w_offset = %d\n", + locty, tis->loc[0].r_offset, tis->loc[0].w_offset); + + qemu_mutex_lock(&s->state_lock); + + /* wait for outstanding request to complete */ + if (tis->loc[locty].state == TPM_TIS_STATE_EXECUTION) { + /* + * If we get here when the bh is scheduled but did not run, + * we won't get notified... + */ + if (!tis->bh_scheduled) { + /* backend thread to notify us */ + qemu_cond_wait(&s->cmd_complete, &s->state_lock); + } + if (tis->loc[locty].state == TPM_TIS_STATE_EXECUTION) { + /* bottom half did not run - run its function */ + qemu_mutex_unlock(&s->state_lock); + spapr_vtpm_receive_bh(opaque); + qemu_mutex_lock(&s->state_lock); + } + } + + qemu_mutex_unlock(&s->state_lock); + + /* + * requests are immediately sent to the backend, so we only ever + * have a buffer with TPM response data. + */ + switch (tis->loc[locty].state) { + case TPM_TIS_STATE_COMPLETION: + memcpy(tis->buf, + tis->loc[locty].r_buffer.buffer, + MIN(sizeof(tis->buf), + tis->loc[locty].r_buffer.size)); + break; + default: + /* leak nothing */ + memset(tis->buf, 0, sizeof(tis->buf)); + break; + } +} + +static int spapr_vtpm_post_load(void *opaque, + int version_id __attribute__((unused))) +{ + TPMState *s = opaque; + TPMTISEmuState *tis = &s->s.tis; + uint8_t locty = 0; + + switch (tis->loc[locty].state) { + case TPM_TIS_STATE_COMPLETION: + memcpy(tis->loc[locty].r_buffer.buffer, + tis->buf, + MIN(sizeof(tis->buf), + tis->loc[locty].r_buffer.size)); + break; + default: + break; + } + + DPRINTF("tpm_tis: resume : locty = %d : r_offset = %d, w_offset = %d\n", + locty, tis->loc[0].r_offset, tis->loc[0].w_offset); + + return 0; +} + +static const VMStateDescription vmstate_locty = { + .name = "loc", + .version_id = 1, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(state, TPMLocality), + VMSTATE_END_OF_LIST(), + } +}; + +static const VMStateDescription vmstate_spapr_vtpm = { + .name = "spapr_vtpm", + .version_id = 1, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .pre_save = spapr_vtpm_pre_save, + .post_load = spapr_vtpm_post_load, + .fields = (VMStateField[]) { + VMSTATE_SPAPR_VIO(vdev, SPAPRvTPMState), + + VMSTATE_STRUCT_ARRAY(s.tis.loc, TPMState, TPM_TIS_NUM_LOCALITIES, 1, + vmstate_locty, TPMLocality), + + VMSTATE_END_OF_LIST() + }, +}; + +static Property spapr_vtpm_properties[] = { + DEFINE_SPAPR_PROPERTIES(SPAPRvTPMState, vdev), + DEFINE_PROP_STRING("tpmdev", SPAPRvTPMState, backend), + DEFINE_PROP_END_OF_LIST(), +}; + +static void spapr_vtpm_realizefn(VIOsPAPRDevice *dev, Error **errp) +{ + SPAPRvTPMState *s = VIO_SPAPR_VTPM_DEVICE(dev); + + dev->crq.SendFunc = spapr_vtpm_do_crq; + + s->be_driver = qemu_find_tpm(s->backend); + if (!s->be_driver) { + error_setg(errp, "spapr_vtpm: backend driver with id %s could not be " + "found", s->backend); + return; + } + + s->be_driver->fe_model = TPM_MODEL_SPAPR_VTPM; + + if (tpm_backend_init(s->be_driver, s, &s->locty_number, &s->locty_data, + spapr_vtpm_receive_cb)) { + error_setg(errp, "spapr_vtpm: backend driver with id %s could not be " + "initialized", s->backend); + return; + } + + s->s.tis.bh = qemu_bh_new(spapr_vtpm_receive_bh, s); +} + +static void spapr_vtpm_initfn(Object *obj) +{ + SPAPRvTPMState *s = VIO_SPAPR_VTPM_DEVICE(obj); + + qemu_mutex_init(&s->state_lock); + qemu_cond_init(&s->cmd_complete); +} + +static void spapr_vtpm_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VIOsPAPRDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); + + k->realize = spapr_vtpm_realizefn; + k->reset = spapr_vtpm_reset; + k->dt_name = "vtpm"; + k->dt_type = "IBM,vtpm"; + k->dt_compatible = "IBM,vtpm"; + k->signal_mask = 0x00000001; + k->rtce_window_size = 0x10000000; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + dc->props = spapr_vtpm_properties; + dc->vmsd = &vmstate_spapr_vtpm; +} + +static const TypeInfo spapr_vtpm_info = { + .name = TYPE_VIO_SPAPR_VTPM_DEVICE, + .parent = TYPE_VIO_SPAPR_DEVICE, + .instance_size = sizeof(SPAPRvTPMState), + .instance_init = spapr_vtpm_initfn, + .class_init = spapr_vtpm_class_init, +}; + +static void spapr_vtpm_register_types(void) +{ + type_register_static(&spapr_vtpm_info); + tpm_register_model(TPM_MODEL_SPAPR_VTPM); +} + +type_init(spapr_vtpm_register_types) diff --git a/hw/tpm/spapr_vtpm.h b/hw/tpm/spapr_vtpm.h new file mode 100644 index 0000000..d4fc693 --- /dev/null +++ b/hw/tpm/spapr_vtpm.h @@ -0,0 +1,65 @@ +/* + * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator + * + * PAPR Virtual TPM, aka ibmvtpm + * + * Copyright (c) 2015 IBM Corporation. + * + * Authors: + * Stefan Berger + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +#ifndef TPM_SPAPR_VTPM_H +#define TPM_SPAPR_VTPM_H + +#include "hw/ppc/spapr.h" +#include "hw/ppc/spapr_vio.h" + +typedef struct vio_crq { + 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; +} vio_crq; + +typedef union spapr_vtpm_crq { + vio_crq s; + uint8_t raw[sizeof(vio_crq)]; +} spapr_vtpm_crq; + +#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 + +#endif /* TPM_SPAPR_VTPM_H */ diff --git a/include/hw/ppc/spapr_vio.h b/include/hw/ppc/spapr_vio.h index 2299a54..5efdf4f 100644 --- a/include/hw/ppc/spapr_vio.h +++ b/include/hw/ppc/spapr_vio.h @@ -133,6 +133,7 @@ void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len); void spapr_vty_create(VIOsPAPRBus *bus, CharDriverState *chardev); void spapr_vlan_create(VIOsPAPRBus *bus, NICInfo *nd); void spapr_vscsi_create(VIOsPAPRBus *bus); +void spapr_vtpm_create(VIOsPAPRBus *bus); VIOsPAPRDevice *spapr_vty_get_default(VIOsPAPRBus *bus); diff --git a/include/sysemu/tpm.h b/include/sysemu/tpm.h index c8afa17..45feee3 100644 --- a/include/sysemu/tpm.h +++ b/include/sysemu/tpm.h @@ -27,6 +27,7 @@ typedef enum TPMVersion { } TPMVersion; TPMVersion tpm_tis_get_tpm_version(Object *obj); +TPMVersion spapr_vtpm_get_tpm_version(Object *obj); #define TYPE_TPM_TIS "tpm-tis" diff --git a/qapi-schema.json b/qapi-schema.json index e0ef212..2ab4dad 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -3315,8 +3315,12 @@ # @tpm-tis: TPM TIS model # # Since: 1.5 +# +# @spapr-vtpm: PPC64 vTPM device model +# +# Since: 2.6 ## -{ 'enum': 'TpmModel', 'data': [ 'tpm-tis' ] } +{ 'enum': 'TpmModel', 'data': [ 'tpm-tis', 'spapr-vtpm' ] } ## # @query-tpm-models: