1195 lines
36 KiB
Diff
1195 lines
36 KiB
Diff
From 425f6bc8392c71d2f29b572d19232785d0ab0b73 Mon Sep 17 00:00:00 2001
|
|
From: jiangfangjie <jiangfangjie@huawei.com>
|
|
Date: Tue, 11 Aug 2020 02:55:35 +0000
|
|
Subject: [PATCH 12/19] tpm: Separate tpm_tis common functions from isa code
|
|
|
|
Move the device agnostic code into tpm_tis_common.c and
|
|
put the ISA device specific code into tpm_tis_isa.c
|
|
|
|
Signed-off-by: Eric Auger <eric.auger@redhat.com>
|
|
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
|
|
Tested-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
Acked-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
Message-id: 20200305165149.618-4-eric.auger@redhat.com
|
|
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
|
|
Signed-off-by: jiangfangjie <jiangfangjie@huawei.com>
|
|
---
|
|
hw/tpm/Makefile.objs | 2 +-
|
|
hw/tpm/{tpm_tis.c => tpm_tis.c.orig} | 0
|
|
hw/tpm/tpm_tis.h | 91 +++
|
|
hw/tpm/tpm_tis_common.c | 869 +++++++++++++++++++++++++++
|
|
hw/tpm/tpm_tis_isa.c | 170 ++++++
|
|
5 files changed, 1131 insertions(+), 1 deletion(-)
|
|
rename hw/tpm/{tpm_tis.c => tpm_tis.c.orig} (100%)
|
|
create mode 100644 hw/tpm/tpm_tis.h
|
|
create mode 100644 hw/tpm/tpm_tis_common.c
|
|
create mode 100644 hw/tpm/tpm_tis_isa.c
|
|
|
|
diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs
|
|
index 85eb99ae..fcc4c2f2 100644
|
|
--- a/hw/tpm/Makefile.objs
|
|
+++ b/hw/tpm/Makefile.objs
|
|
@@ -1,6 +1,6 @@
|
|
common-obj-$(CONFIG_TPM) += tpm_util.o
|
|
obj-$(call lor,$(CONFIG_TPM_TIS),$(CONFIG_TPM_CRB)) += tpm_ppi.o
|
|
-common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o
|
|
+common-obj-$(CONFIG_TPM_TIS) += tpm_tis_isa.o tpm_tis_common.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
|
|
diff --git a/hw/tpm/tpm_tis.c b/hw/tpm/tpm_tis.c.orig
|
|
similarity index 100%
|
|
rename from hw/tpm/tpm_tis.c
|
|
rename to hw/tpm/tpm_tis.c.orig
|
|
diff --git a/hw/tpm/tpm_tis.h b/hw/tpm/tpm_tis.h
|
|
new file mode 100644
|
|
index 00000000..55549893
|
|
--- /dev/null
|
|
+++ b/hw/tpm/tpm_tis.h
|
|
@@ -0,0 +1,91 @@
|
|
+/*
|
|
+ * tpm_tis.h - QEMU's TPM TIS common header
|
|
+ *
|
|
+ * Copyright (C) 2006,2010-2013 IBM Corporation
|
|
+ *
|
|
+ * Authors:
|
|
+ * Stefan Berger <stefanb@us.ibm.com>
|
|
+ * David Safford <safford@us.ibm.com>
|
|
+ *
|
|
+ * Xen 4 support: Andrease Niederl <andreas.niederl@iaik.tugraz.at>
|
|
+ *
|
|
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
|
|
+ * See the COPYING file in the top-level directory.
|
|
+ *
|
|
+ * Implementation of the TIS interface according to specs found at
|
|
+ * http://www.trustedcomputinggroup.org. This implementation currently
|
|
+ * supports version 1.3, 21 March 2013
|
|
+ * In the developers menu choose the PC Client section then find the TIS
|
|
+ * specification.
|
|
+ *
|
|
+ * TPM TIS for TPM 2 implementation following TCG PC Client Platform
|
|
+ * TPM Profile (PTP) Specification, Familiy 2.0, Revision 00.43
|
|
+ */
|
|
+#ifndef TPM_TPM_TIS_H
|
|
+#define TPM_TPM_TIS_H
|
|
+
|
|
+#include "qemu/osdep.h"
|
|
+#include "sysemu/tpm_backend.h"
|
|
+#include "tpm_ppi.h"
|
|
+
|
|
+#define TPM_TIS_NUM_LOCALITIES 5 /* per spec */
|
|
+#define TPM_TIS_LOCALITY_SHIFT 12
|
|
+#define TPM_TIS_NO_LOCALITY 0xff
|
|
+
|
|
+#define TPM_TIS_IS_VALID_LOCTY(x) ((x) < TPM_TIS_NUM_LOCALITIES)
|
|
+
|
|
+#define TPM_TIS_BUFFER_MAX 4096
|
|
+
|
|
+typedef enum {
|
|
+ TPM_TIS_STATE_IDLE = 0,
|
|
+ TPM_TIS_STATE_READY,
|
|
+ TPM_TIS_STATE_COMPLETION,
|
|
+ TPM_TIS_STATE_EXECUTION,
|
|
+ TPM_TIS_STATE_RECEPTION,
|
|
+} TPMTISState;
|
|
+
|
|
+/* locality data -- all fields are persisted */
|
|
+typedef struct TPMLocality {
|
|
+ TPMTISState state;
|
|
+ uint8_t access;
|
|
+ uint32_t sts;
|
|
+ uint32_t iface_id;
|
|
+ uint32_t inte;
|
|
+ uint32_t ints;
|
|
+} TPMLocality;
|
|
+
|
|
+typedef struct TPMState {
|
|
+ MemoryRegion mmio;
|
|
+
|
|
+ unsigned char buffer[TPM_TIS_BUFFER_MAX];
|
|
+ uint16_t rw_offset;
|
|
+
|
|
+ uint8_t active_locty;
|
|
+ uint8_t aborting_locty;
|
|
+ uint8_t next_locty;
|
|
+
|
|
+ TPMLocality loc[TPM_TIS_NUM_LOCALITIES];
|
|
+
|
|
+ qemu_irq irq;
|
|
+ uint32_t irq_num;
|
|
+
|
|
+ TPMBackendCmd cmd;
|
|
+
|
|
+ TPMBackend *be_driver;
|
|
+ TPMVersion be_tpm_version;
|
|
+
|
|
+ size_t be_buffer_size;
|
|
+
|
|
+ bool ppi_enabled;
|
|
+ TPMPPI ppi;
|
|
+} TPMState;
|
|
+
|
|
+extern const VMStateDescription vmstate_locty;
|
|
+extern const MemoryRegionOps tpm_tis_memory_ops;
|
|
+
|
|
+int tpm_tis_pre_save(TPMState *s);
|
|
+void tpm_tis_reset(TPMState *s);
|
|
+enum TPMVersion tpm_tis_get_tpm_version(TPMState *s);
|
|
+void tpm_tis_request_completed(TPMState *s, int ret);
|
|
+
|
|
+#endif /* TPM_TPM_TIS_H */
|
|
diff --git a/hw/tpm/tpm_tis_common.c b/hw/tpm/tpm_tis_common.c
|
|
new file mode 100644
|
|
index 00000000..9a51c71e
|
|
--- /dev/null
|
|
+++ b/hw/tpm/tpm_tis_common.c
|
|
@@ -0,0 +1,869 @@
|
|
+/*
|
|
+ * tpm_tis_common.c - QEMU's TPM TIS interface emulator
|
|
+ * device agnostic functions
|
|
+ *
|
|
+ * Copyright (C) 2006,2010-2013 IBM Corporation
|
|
+ *
|
|
+ * Authors:
|
|
+ * Stefan Berger <stefanb@us.ibm.com>
|
|
+ * David Safford <safford@us.ibm.com>
|
|
+ *
|
|
+ * Xen 4 support: Andrease Niederl <andreas.niederl@iaik.tugraz.at>
|
|
+ *
|
|
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
|
|
+ * See the COPYING file in the top-level directory.
|
|
+ *
|
|
+ * Implementation of the TIS interface according to specs found at
|
|
+ * http://www.trustedcomputinggroup.org. This implementation currently
|
|
+ * supports version 1.3, 21 March 2013
|
|
+ * In the developers menu choose the PC Client section then find the TIS
|
|
+ * specification.
|
|
+ *
|
|
+ * TPM TIS for TPM 2 implementation following TCG PC Client Platform
|
|
+ * TPM Profile (PTP) Specification, Familiy 2.0, Revision 00.43
|
|
+ */
|
|
+#include "qemu/osdep.h"
|
|
+#include "hw/isa/isa.h"
|
|
+#include "qapi/error.h"
|
|
+#include "qemu/module.h"
|
|
+
|
|
+#include "hw/acpi/tpm.h"
|
|
+#include "hw/pci/pci_ids.h"
|
|
+#include "sysemu/tpm_backend.h"
|
|
+#include "tpm_int.h"
|
|
+#include "tpm_util.h"
|
|
+#include "tpm_ppi.h"
|
|
+#include "trace.h"
|
|
+
|
|
+#include "tpm_tis.h"
|
|
+
|
|
+#define DEBUG_TIS 0
|
|
+
|
|
+/* local prototypes */
|
|
+
|
|
+static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr,
|
|
+ unsigned size);
|
|
+
|
|
+/* utility functions */
|
|
+
|
|
+static uint8_t tpm_tis_locality_from_addr(hwaddr addr)
|
|
+{
|
|
+ return (uint8_t)((addr >> TPM_TIS_LOCALITY_SHIFT) & 0x7);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * Set the given flags in the STS register by clearing the register but
|
|
+ * preserving the SELFTEST_DONE and TPM_FAMILY_MASK flags and then setting
|
|
+ * the new flags.
|
|
+ *
|
|
+ * The SELFTEST_DONE flag is acquired from the backend that determines it by
|
|
+ * peeking into TPM commands.
|
|
+ *
|
|
+ * A VM suspend/resume will preserve the flag by storing it into the VM
|
|
+ * device state, but the backend will not remember it when QEMU is started
|
|
+ * again. Therefore, we cache the flag here. Once set, it will not be unset
|
|
+ * except by a reset.
|
|
+ */
|
|
+static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags)
|
|
+{
|
|
+ l->sts &= TPM_TIS_STS_SELFTEST_DONE | TPM_TIS_STS_TPM_FAMILY_MASK;
|
|
+ l->sts |= flags;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Send a request to the TPM.
|
|
+ */
|
|
+static void tpm_tis_tpm_send(TPMState *s, uint8_t locty)
|
|
+{
|
|
+ if (trace_event_get_state_backends(TRACE_TPM_UTIL_SHOW_BUFFER)) {
|
|
+ tpm_util_show_buffer(s->buffer, s->be_buffer_size, "To TPM");
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * rw_offset serves as length indicator for length of data;
|
|
+ * it's reset when the response comes back
|
|
+ */
|
|
+ s->loc[locty].state = TPM_TIS_STATE_EXECUTION;
|
|
+
|
|
+ s->cmd = (TPMBackendCmd) {
|
|
+ .locty = locty,
|
|
+ .in = s->buffer,
|
|
+ .in_len = s->rw_offset,
|
|
+ .out = s->buffer,
|
|
+ .out_len = s->be_buffer_size,
|
|
+ };
|
|
+
|
|
+ tpm_backend_deliver_request(s->be_driver, &s->cmd);
|
|
+}
|
|
+
|
|
+/* raise an interrupt if allowed */
|
|
+static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask)
|
|
+{
|
|
+ if (!TPM_TIS_IS_VALID_LOCTY(locty)) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if ((s->loc[locty].inte & TPM_TIS_INT_ENABLED) &&
|
|
+ (s->loc[locty].inte & irqmask)) {
|
|
+ trace_tpm_tis_raise_irq(irqmask);
|
|
+ qemu_irq_raise(s->irq);
|
|
+ s->loc[locty].ints |= irqmask;
|
|
+ }
|
|
+}
|
|
+
|
|
+static uint32_t tpm_tis_check_request_use_except(TPMState *s, uint8_t locty)
|
|
+{
|
|
+ uint8_t l;
|
|
+
|
|
+ for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
|
|
+ if (l == locty) {
|
|
+ continue;
|
|
+ }
|
|
+ if ((s->loc[l].access & TPM_TIS_ACCESS_REQUEST_USE)) {
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty)
|
|
+{
|
|
+ bool change = (s->active_locty != new_active_locty);
|
|
+ bool is_seize;
|
|
+ uint8_t mask;
|
|
+
|
|
+ if (change && TPM_TIS_IS_VALID_LOCTY(s->active_locty)) {
|
|
+ is_seize = TPM_TIS_IS_VALID_LOCTY(new_active_locty) &&
|
|
+ s->loc[new_active_locty].access & TPM_TIS_ACCESS_SEIZE;
|
|
+
|
|
+ if (is_seize) {
|
|
+ mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY);
|
|
+ } else {
|
|
+ mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY|
|
|
+ TPM_TIS_ACCESS_REQUEST_USE);
|
|
+ }
|
|
+ /* reset flags on the old active locality */
|
|
+ s->loc[s->active_locty].access &= mask;
|
|
+
|
|
+ if (is_seize) {
|
|
+ s->loc[s->active_locty].access |= TPM_TIS_ACCESS_BEEN_SEIZED;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ s->active_locty = new_active_locty;
|
|
+
|
|
+ trace_tpm_tis_new_active_locality(s->active_locty);
|
|
+
|
|
+ if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) {
|
|
+ /* set flags on the new active locality */
|
|
+ s->loc[new_active_locty].access |= TPM_TIS_ACCESS_ACTIVE_LOCALITY;
|
|
+ s->loc[new_active_locty].access &= ~(TPM_TIS_ACCESS_REQUEST_USE |
|
|
+ TPM_TIS_ACCESS_SEIZE);
|
|
+ }
|
|
+
|
|
+ if (change) {
|
|
+ tpm_tis_raise_irq(s, s->active_locty, TPM_TIS_INT_LOCALITY_CHANGED);
|
|
+ }
|
|
+}
|
|
+
|
|
+/* abort -- this function switches the locality */
|
|
+static void tpm_tis_abort(TPMState *s)
|
|
+{
|
|
+ s->rw_offset = 0;
|
|
+
|
|
+ trace_tpm_tis_abort(s->next_locty);
|
|
+
|
|
+ /*
|
|
+ * Need to react differently depending on who's aborting now and
|
|
+ * which locality will become active afterwards.
|
|
+ */
|
|
+ if (s->aborting_locty == s->next_locty) {
|
|
+ s->loc[s->aborting_locty].state = TPM_TIS_STATE_READY;
|
|
+ tpm_tis_sts_set(&s->loc[s->aborting_locty],
|
|
+ TPM_TIS_STS_COMMAND_READY);
|
|
+ tpm_tis_raise_irq(s, s->aborting_locty, TPM_TIS_INT_COMMAND_READY);
|
|
+ }
|
|
+
|
|
+ /* locality after abort is another one than the current one */
|
|
+ tpm_tis_new_active_locality(s, s->next_locty);
|
|
+
|
|
+ s->next_locty = TPM_TIS_NO_LOCALITY;
|
|
+ /* nobody's aborting a command anymore */
|
|
+ s->aborting_locty = TPM_TIS_NO_LOCALITY;
|
|
+}
|
|
+
|
|
+/* prepare aborting current command */
|
|
+static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty)
|
|
+{
|
|
+ uint8_t busy_locty;
|
|
+
|
|
+ assert(TPM_TIS_IS_VALID_LOCTY(newlocty));
|
|
+
|
|
+ s->aborting_locty = locty; /* may also be TPM_TIS_NO_LOCALITY */
|
|
+ s->next_locty = newlocty; /* locality after successful abort */
|
|
+
|
|
+ /*
|
|
+ * only abort a command using an interrupt if currently executing
|
|
+ * a command AND if there's a valid connection to the vTPM.
|
|
+ */
|
|
+ for (busy_locty = 0; busy_locty < TPM_TIS_NUM_LOCALITIES; busy_locty++) {
|
|
+ if (s->loc[busy_locty].state == TPM_TIS_STATE_EXECUTION) {
|
|
+ /*
|
|
+ * request the backend to cancel. Some backends may not
|
|
+ * support it
|
|
+ */
|
|
+ tpm_backend_cancel_cmd(s->be_driver);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ tpm_tis_abort(s);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Callback from the TPM to indicate that the response was received.
|
|
+ */
|
|
+void tpm_tis_request_completed(TPMState *s, int ret)
|
|
+{
|
|
+ uint8_t locty = s->cmd.locty;
|
|
+ uint8_t l;
|
|
+
|
|
+ assert(TPM_TIS_IS_VALID_LOCTY(locty));
|
|
+
|
|
+ if (s->cmd.selftest_done) {
|
|
+ for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
|
|
+ s->loc[l].sts |= TPM_TIS_STS_SELFTEST_DONE;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* FIXME: report error if ret != 0 */
|
|
+ tpm_tis_sts_set(&s->loc[locty],
|
|
+ TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE);
|
|
+ s->loc[locty].state = TPM_TIS_STATE_COMPLETION;
|
|
+ s->rw_offset = 0;
|
|
+
|
|
+ if (trace_event_get_state_backends(TRACE_TPM_UTIL_SHOW_BUFFER)) {
|
|
+ tpm_util_show_buffer(s->buffer, s->be_buffer_size, "From TPM");
|
|
+ }
|
|
+
|
|
+ if (TPM_TIS_IS_VALID_LOCTY(s->next_locty)) {
|
|
+ tpm_tis_abort(s);
|
|
+ }
|
|
+
|
|
+ tpm_tis_raise_irq(s, locty,
|
|
+ TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read a byte of response data
|
|
+ */
|
|
+static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty)
|
|
+{
|
|
+ uint32_t ret = TPM_TIS_NO_DATA_BYTE;
|
|
+ uint16_t len;
|
|
+
|
|
+ if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
|
|
+ len = MIN(tpm_cmd_get_size(&s->buffer),
|
|
+ s->be_buffer_size);
|
|
+
|
|
+ ret = s->buffer[s->rw_offset++];
|
|
+ if (s->rw_offset >= len) {
|
|
+ /* got last byte */
|
|
+ tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID);
|
|
+ tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
|
|
+ }
|
|
+ trace_tpm_tis_data_read(ret, s->rw_offset - 1);
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+#ifdef DEBUG_TIS
|
|
+static void tpm_tis_dump_state(TPMState *s, hwaddr addr)
|
|
+{
|
|
+ static const unsigned regs[] = {
|
|
+ TPM_TIS_REG_ACCESS,
|
|
+ TPM_TIS_REG_INT_ENABLE,
|
|
+ TPM_TIS_REG_INT_VECTOR,
|
|
+ TPM_TIS_REG_INT_STATUS,
|
|
+ TPM_TIS_REG_INTF_CAPABILITY,
|
|
+ TPM_TIS_REG_STS,
|
|
+ TPM_TIS_REG_DID_VID,
|
|
+ TPM_TIS_REG_RID,
|
|
+ 0xfff};
|
|
+ int idx;
|
|
+ uint8_t locty = tpm_tis_locality_from_addr(addr);
|
|
+ hwaddr base = addr & ~0xfff;
|
|
+
|
|
+ printf("tpm_tis: active locality : %d\n"
|
|
+ "tpm_tis: state of locality %d : %d\n"
|
|
+ "tpm_tis: register dump:\n",
|
|
+ s->active_locty,
|
|
+ locty, s->loc[locty].state);
|
|
+
|
|
+ for (idx = 0; regs[idx] != 0xfff; idx++) {
|
|
+ printf("tpm_tis: 0x%04x : 0x%08x\n", regs[idx],
|
|
+ (int)tpm_tis_mmio_read(s, base + regs[idx], 4));
|
|
+ }
|
|
+
|
|
+ printf("tpm_tis: r/w offset : %d\n"
|
|
+ "tpm_tis: result buffer : ",
|
|
+ s->rw_offset);
|
|
+ for (idx = 0;
|
|
+ idx < MIN(tpm_cmd_get_size(&s->buffer), s->be_buffer_size);
|
|
+ idx++) {
|
|
+ printf("%c%02x%s",
|
|
+ s->rw_offset == idx ? '>' : ' ',
|
|
+ s->buffer[idx],
|
|
+ ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : "");
|
|
+ }
|
|
+ printf("\n");
|
|
+}
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * Read a register of the TIS interface
|
|
+ * See specs pages 33-63 for description of the registers
|
|
+ */
|
|
+static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr,
|
|
+ unsigned size)
|
|
+{
|
|
+ TPMState *s = opaque;
|
|
+ uint16_t offset = addr & 0xffc;
|
|
+ uint8_t shift = (addr & 0x3) * 8;
|
|
+ uint32_t val = 0xffffffff;
|
|
+ uint8_t locty = tpm_tis_locality_from_addr(addr);
|
|
+ uint32_t avail;
|
|
+ uint8_t v;
|
|
+
|
|
+ if (tpm_backend_had_startup_error(s->be_driver)) {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ switch (offset) {
|
|
+ case TPM_TIS_REG_ACCESS:
|
|
+ /* never show the SEIZE flag even though we use it internally */
|
|
+ val = s->loc[locty].access & ~TPM_TIS_ACCESS_SEIZE;
|
|
+ /* the pending flag is always calculated */
|
|
+ if (tpm_tis_check_request_use_except(s, locty)) {
|
|
+ val |= TPM_TIS_ACCESS_PENDING_REQUEST;
|
|
+ }
|
|
+ val |= !tpm_backend_get_tpm_established_flag(s->be_driver);
|
|
+ break;
|
|
+ case TPM_TIS_REG_INT_ENABLE:
|
|
+ val = s->loc[locty].inte;
|
|
+ break;
|
|
+ case TPM_TIS_REG_INT_VECTOR:
|
|
+ val = s->irq_num;
|
|
+ break;
|
|
+ case TPM_TIS_REG_INT_STATUS:
|
|
+ val = s->loc[locty].ints;
|
|
+ break;
|
|
+ case TPM_TIS_REG_INTF_CAPABILITY:
|
|
+ switch (s->be_tpm_version) {
|
|
+ case TPM_VERSION_UNSPEC:
|
|
+ val = 0;
|
|
+ break;
|
|
+ case TPM_VERSION_1_2:
|
|
+ val = TPM_TIS_CAPABILITIES_SUPPORTED1_3;
|
|
+ break;
|
|
+ case TPM_VERSION_2_0:
|
|
+ val = TPM_TIS_CAPABILITIES_SUPPORTED2_0;
|
|
+ break;
|
|
+ }
|
|
+ break;
|
|
+ case TPM_TIS_REG_STS:
|
|
+ if (s->active_locty == locty) {
|
|
+ if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
|
|
+ val = TPM_TIS_BURST_COUNT(
|
|
+ MIN(tpm_cmd_get_size(&s->buffer),
|
|
+ s->be_buffer_size)
|
|
+ - s->rw_offset) | s->loc[locty].sts;
|
|
+ } else {
|
|
+ avail = s->be_buffer_size - s->rw_offset;
|
|
+ /*
|
|
+ * byte-sized reads should not return 0x00 for 0x100
|
|
+ * available bytes.
|
|
+ */
|
|
+ if (size == 1 && avail > 0xff) {
|
|
+ avail = 0xff;
|
|
+ }
|
|
+ val = TPM_TIS_BURST_COUNT(avail) | s->loc[locty].sts;
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ case TPM_TIS_REG_DATA_FIFO:
|
|
+ case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END:
|
|
+ if (s->active_locty == locty) {
|
|
+ if (size > 4 - (addr & 0x3)) {
|
|
+ /* prevent access beyond FIFO */
|
|
+ size = 4 - (addr & 0x3);
|
|
+ }
|
|
+ val = 0;
|
|
+ shift = 0;
|
|
+ while (size > 0) {
|
|
+ switch (s->loc[locty].state) {
|
|
+ case TPM_TIS_STATE_COMPLETION:
|
|
+ v = tpm_tis_data_read(s, locty);
|
|
+ break;
|
|
+ default:
|
|
+ v = TPM_TIS_NO_DATA_BYTE;
|
|
+ break;
|
|
+ }
|
|
+ val |= (v << shift);
|
|
+ shift += 8;
|
|
+ size--;
|
|
+ }
|
|
+ shift = 0; /* no more adjustments */
|
|
+ }
|
|
+ break;
|
|
+ case TPM_TIS_REG_INTERFACE_ID:
|
|
+ val = s->loc[locty].iface_id;
|
|
+ break;
|
|
+ case TPM_TIS_REG_DID_VID:
|
|
+ val = (TPM_TIS_TPM_DID << 16) | TPM_TIS_TPM_VID;
|
|
+ break;
|
|
+ case TPM_TIS_REG_RID:
|
|
+ val = TPM_TIS_TPM_RID;
|
|
+ break;
|
|
+#ifdef DEBUG_TIS
|
|
+ case TPM_TIS_REG_DEBUG:
|
|
+ tpm_tis_dump_state(s, addr);
|
|
+ break;
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ if (shift) {
|
|
+ val >>= shift;
|
|
+ }
|
|
+
|
|
+ trace_tpm_tis_mmio_read(size, addr, val);
|
|
+
|
|
+ return val;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Write a value to a register of the TIS interface
|
|
+ * See specs pages 33-63 for description of the registers
|
|
+ */
|
|
+static void tpm_tis_mmio_write(void *opaque, hwaddr addr,
|
|
+ uint64_t val, unsigned size)
|
|
+{
|
|
+ TPMState *s = opaque;
|
|
+ uint16_t off = addr & 0xffc;
|
|
+ uint8_t shift = (addr & 0x3) * 8;
|
|
+ uint8_t locty = tpm_tis_locality_from_addr(addr);
|
|
+ uint8_t active_locty, l;
|
|
+ int c, set_new_locty = 1;
|
|
+ uint16_t len;
|
|
+ uint32_t mask = (size == 1) ? 0xff : ((size == 2) ? 0xffff : ~0);
|
|
+
|
|
+ trace_tpm_tis_mmio_write(size, addr, val);
|
|
+
|
|
+ if (locty == 4) {
|
|
+ trace_tpm_tis_mmio_write_locty4();
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (tpm_backend_had_startup_error(s->be_driver)) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ val &= mask;
|
|
+
|
|
+ if (shift) {
|
|
+ val <<= shift;
|
|
+ mask <<= shift;
|
|
+ }
|
|
+
|
|
+ mask ^= 0xffffffff;
|
|
+
|
|
+ switch (off) {
|
|
+ case TPM_TIS_REG_ACCESS:
|
|
+
|
|
+ if ((val & TPM_TIS_ACCESS_SEIZE)) {
|
|
+ val &= ~(TPM_TIS_ACCESS_REQUEST_USE |
|
|
+ TPM_TIS_ACCESS_ACTIVE_LOCALITY);
|
|
+ }
|
|
+
|
|
+ active_locty = s->active_locty;
|
|
+
|
|
+ if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) {
|
|
+ /* give up locality if currently owned */
|
|
+ if (s->active_locty == locty) {
|
|
+ trace_tpm_tis_mmio_write_release_locty(locty);
|
|
+
|
|
+ uint8_t newlocty = TPM_TIS_NO_LOCALITY;
|
|
+ /* anybody wants the locality ? */
|
|
+ for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) {
|
|
+ if ((s->loc[c].access & TPM_TIS_ACCESS_REQUEST_USE)) {
|
|
+ trace_tpm_tis_mmio_write_locty_req_use(c);
|
|
+ newlocty = c;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ trace_tpm_tis_mmio_write_next_locty(newlocty);
|
|
+
|
|
+ if (TPM_TIS_IS_VALID_LOCTY(newlocty)) {
|
|
+ set_new_locty = 0;
|
|
+ tpm_tis_prep_abort(s, locty, newlocty);
|
|
+ } else {
|
|
+ active_locty = TPM_TIS_NO_LOCALITY;
|
|
+ }
|
|
+ } else {
|
|
+ /* not currently the owner; clear a pending request */
|
|
+ s->loc[locty].access &= ~TPM_TIS_ACCESS_REQUEST_USE;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if ((val & TPM_TIS_ACCESS_BEEN_SEIZED)) {
|
|
+ s->loc[locty].access &= ~TPM_TIS_ACCESS_BEEN_SEIZED;
|
|
+ }
|
|
+
|
|
+ if ((val & TPM_TIS_ACCESS_SEIZE)) {
|
|
+ /*
|
|
+ * allow seize if a locality is active and the requesting
|
|
+ * locality is higher than the one that's active
|
|
+ * OR
|
|
+ * allow seize for requesting locality if no locality is
|
|
+ * active
|
|
+ */
|
|
+ while ((TPM_TIS_IS_VALID_LOCTY(s->active_locty) &&
|
|
+ locty > s->active_locty) ||
|
|
+ !TPM_TIS_IS_VALID_LOCTY(s->active_locty)) {
|
|
+ bool higher_seize = FALSE;
|
|
+
|
|
+ /* already a pending SEIZE ? */
|
|
+ if ((s->loc[locty].access & TPM_TIS_ACCESS_SEIZE)) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* check for ongoing seize by a higher locality */
|
|
+ for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES; l++) {
|
|
+ if ((s->loc[l].access & TPM_TIS_ACCESS_SEIZE)) {
|
|
+ higher_seize = TRUE;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (higher_seize) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* cancel any seize by a lower locality */
|
|
+ for (l = 0; l < locty; l++) {
|
|
+ s->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE;
|
|
+ }
|
|
+
|
|
+ s->loc[locty].access |= TPM_TIS_ACCESS_SEIZE;
|
|
+
|
|
+ trace_tpm_tis_mmio_write_locty_seized(locty, s->active_locty);
|
|
+ trace_tpm_tis_mmio_write_init_abort();
|
|
+
|
|
+ set_new_locty = 0;
|
|
+ tpm_tis_prep_abort(s, s->active_locty, locty);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if ((val & TPM_TIS_ACCESS_REQUEST_USE)) {
|
|
+ if (s->active_locty != locty) {
|
|
+ if (TPM_TIS_IS_VALID_LOCTY(s->active_locty)) {
|
|
+ s->loc[locty].access |= TPM_TIS_ACCESS_REQUEST_USE;
|
|
+ } else {
|
|
+ /* no locality active -> make this one active now */
|
|
+ active_locty = locty;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (set_new_locty) {
|
|
+ tpm_tis_new_active_locality(s, active_locty);
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ case TPM_TIS_REG_INT_ENABLE:
|
|
+ if (s->active_locty != locty) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ s->loc[locty].inte &= mask;
|
|
+ s->loc[locty].inte |= (val & (TPM_TIS_INT_ENABLED |
|
|
+ TPM_TIS_INT_POLARITY_MASK |
|
|
+ TPM_TIS_INTERRUPTS_SUPPORTED));
|
|
+ break;
|
|
+ case TPM_TIS_REG_INT_VECTOR:
|
|
+ /* hard wired -- ignore */
|
|
+ break;
|
|
+ case TPM_TIS_REG_INT_STATUS:
|
|
+ if (s->active_locty != locty) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* clearing of interrupt flags */
|
|
+ if (((val & TPM_TIS_INTERRUPTS_SUPPORTED)) &&
|
|
+ (s->loc[locty].ints & TPM_TIS_INTERRUPTS_SUPPORTED)) {
|
|
+ s->loc[locty].ints &= ~val;
|
|
+ if (s->loc[locty].ints == 0) {
|
|
+ qemu_irq_lower(s->irq);
|
|
+ trace_tpm_tis_mmio_write_lowering_irq();
|
|
+ }
|
|
+ }
|
|
+ s->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED);
|
|
+ break;
|
|
+ case TPM_TIS_REG_STS:
|
|
+ if (s->active_locty != locty) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (s->be_tpm_version == TPM_VERSION_2_0) {
|
|
+ /* some flags that are only supported for TPM 2 */
|
|
+ if (val & TPM_TIS_STS_COMMAND_CANCEL) {
|
|
+ if (s->loc[locty].state == TPM_TIS_STATE_EXECUTION) {
|
|
+ /*
|
|
+ * request the backend to cancel. Some backends may not
|
|
+ * support it
|
|
+ */
|
|
+ tpm_backend_cancel_cmd(s->be_driver);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (val & TPM_TIS_STS_RESET_ESTABLISHMENT_BIT) {
|
|
+ if (locty == 3 || locty == 4) {
|
|
+ tpm_backend_reset_tpm_established_flag(s->be_driver, locty);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ val &= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO |
|
|
+ TPM_TIS_STS_RESPONSE_RETRY);
|
|
+
|
|
+ if (val == TPM_TIS_STS_COMMAND_READY) {
|
|
+ switch (s->loc[locty].state) {
|
|
+
|
|
+ case TPM_TIS_STATE_READY:
|
|
+ s->rw_offset = 0;
|
|
+ break;
|
|
+
|
|
+ case TPM_TIS_STATE_IDLE:
|
|
+ tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_COMMAND_READY);
|
|
+ s->loc[locty].state = TPM_TIS_STATE_READY;
|
|
+ tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
|
|
+ break;
|
|
+
|
|
+ case TPM_TIS_STATE_EXECUTION:
|
|
+ case TPM_TIS_STATE_RECEPTION:
|
|
+ /* abort currently running command */
|
|
+ trace_tpm_tis_mmio_write_init_abort();
|
|
+ tpm_tis_prep_abort(s, locty, locty);
|
|
+ break;
|
|
+
|
|
+ case TPM_TIS_STATE_COMPLETION:
|
|
+ s->rw_offset = 0;
|
|
+ /* shortcut to ready state with C/R set */
|
|
+ s->loc[locty].state = TPM_TIS_STATE_READY;
|
|
+ if (!(s->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) {
|
|
+ tpm_tis_sts_set(&s->loc[locty],
|
|
+ TPM_TIS_STS_COMMAND_READY);
|
|
+ tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
|
|
+ }
|
|
+ s->loc[locty].sts &= ~(TPM_TIS_STS_DATA_AVAILABLE);
|
|
+ break;
|
|
+
|
|
+ }
|
|
+ } else if (val == TPM_TIS_STS_TPM_GO) {
|
|
+ switch (s->loc[locty].state) {
|
|
+ case TPM_TIS_STATE_RECEPTION:
|
|
+ if ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) == 0) {
|
|
+ tpm_tis_tpm_send(s, locty);
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ /* ignore */
|
|
+ break;
|
|
+ }
|
|
+ } else if (val == TPM_TIS_STS_RESPONSE_RETRY) {
|
|
+ switch (s->loc[locty].state) {
|
|
+ case TPM_TIS_STATE_COMPLETION:
|
|
+ s->rw_offset = 0;
|
|
+ tpm_tis_sts_set(&s->loc[locty],
|
|
+ TPM_TIS_STS_VALID|
|
|
+ TPM_TIS_STS_DATA_AVAILABLE);
|
|
+ break;
|
|
+ default:
|
|
+ /* ignore */
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ case TPM_TIS_REG_DATA_FIFO:
|
|
+ case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END:
|
|
+ /* data fifo */
|
|
+ if (s->active_locty != locty) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (s->loc[locty].state == TPM_TIS_STATE_IDLE ||
|
|
+ s->loc[locty].state == TPM_TIS_STATE_EXECUTION ||
|
|
+ s->loc[locty].state == TPM_TIS_STATE_COMPLETION) {
|
|
+ /* drop the byte */
|
|
+ } else {
|
|
+ trace_tpm_tis_mmio_write_data2send(val, size);
|
|
+ if (s->loc[locty].state == TPM_TIS_STATE_READY) {
|
|
+ s->loc[locty].state = TPM_TIS_STATE_RECEPTION;
|
|
+ tpm_tis_sts_set(&s->loc[locty],
|
|
+ TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID);
|
|
+ }
|
|
+
|
|
+ val >>= shift;
|
|
+ if (size > 4 - (addr & 0x3)) {
|
|
+ /* prevent access beyond FIFO */
|
|
+ size = 4 - (addr & 0x3);
|
|
+ }
|
|
+
|
|
+ while ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) && size > 0) {
|
|
+ if (s->rw_offset < s->be_buffer_size) {
|
|
+ s->buffer[s->rw_offset++] =
|
|
+ (uint8_t)val;
|
|
+ val >>= 8;
|
|
+ size--;
|
|
+ } else {
|
|
+ tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* check for complete packet */
|
|
+ if (s->rw_offset > 5 &&
|
|
+ (s->loc[locty].sts & TPM_TIS_STS_EXPECT)) {
|
|
+ /* we have a packet length - see if we have all of it */
|
|
+ bool need_irq = !(s->loc[locty].sts & TPM_TIS_STS_VALID);
|
|
+
|
|
+ len = tpm_cmd_get_size(&s->buffer);
|
|
+ if (len > s->rw_offset) {
|
|
+ tpm_tis_sts_set(&s->loc[locty],
|
|
+ TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID);
|
|
+ } else {
|
|
+ /* packet complete */
|
|
+ tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID);
|
|
+ }
|
|
+ if (need_irq) {
|
|
+ tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ case TPM_TIS_REG_INTERFACE_ID:
|
|
+ if (val & TPM_TIS_IFACE_ID_INT_SEL_LOCK) {
|
|
+ for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
|
|
+ s->loc[l].iface_id |= TPM_TIS_IFACE_ID_INT_SEL_LOCK;
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+const MemoryRegionOps tpm_tis_memory_ops = {
|
|
+ .read = tpm_tis_mmio_read,
|
|
+ .write = tpm_tis_mmio_write,
|
|
+ .endianness = DEVICE_LITTLE_ENDIAN,
|
|
+ .valid = {
|
|
+ .min_access_size = 1,
|
|
+ .max_access_size = 4,
|
|
+ },
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Get the TPMVersion of the backend device being used
|
|
+ */
|
|
+enum TPMVersion tpm_tis_get_tpm_version(TPMState *s)
|
|
+{
|
|
+ if (tpm_backend_had_startup_error(s->be_driver)) {
|
|
+ return TPM_VERSION_UNSPEC;
|
|
+ }
|
|
+
|
|
+ return tpm_backend_get_tpm_version(s->be_driver);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * This function is called when the machine starts, resets or due to
|
|
+ * S3 resume.
|
|
+ */
|
|
+void tpm_tis_reset(TPMState *s)
|
|
+{
|
|
+ int c;
|
|
+
|
|
+ s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver);
|
|
+ s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->be_driver),
|
|
+ TPM_TIS_BUFFER_MAX);
|
|
+
|
|
+ if (s->ppi_enabled) {
|
|
+ tpm_ppi_reset(&s->ppi);
|
|
+ }
|
|
+ tpm_backend_reset(s->be_driver);
|
|
+
|
|
+ s->active_locty = TPM_TIS_NO_LOCALITY;
|
|
+ s->next_locty = TPM_TIS_NO_LOCALITY;
|
|
+ s->aborting_locty = TPM_TIS_NO_LOCALITY;
|
|
+
|
|
+ for (c = 0; c < TPM_TIS_NUM_LOCALITIES; c++) {
|
|
+ s->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS;
|
|
+ switch (s->be_tpm_version) {
|
|
+ case TPM_VERSION_UNSPEC:
|
|
+ break;
|
|
+ case TPM_VERSION_1_2:
|
|
+ s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY1_2;
|
|
+ s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3;
|
|
+ break;
|
|
+ case TPM_VERSION_2_0:
|
|
+ s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY2_0;
|
|
+ s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0;
|
|
+ break;
|
|
+ }
|
|
+ s->loc[c].inte = TPM_TIS_INT_POLARITY_LOW_LEVEL;
|
|
+ s->loc[c].ints = 0;
|
|
+ s->loc[c].state = TPM_TIS_STATE_IDLE;
|
|
+
|
|
+ s->rw_offset = 0;
|
|
+ }
|
|
+
|
|
+ if (tpm_backend_startup_tpm(s->be_driver, s->be_buffer_size) < 0) {
|
|
+ exit(1);
|
|
+ }
|
|
+}
|
|
+
|
|
+/* persistent state handling */
|
|
+
|
|
+int tpm_tis_pre_save(TPMState *s)
|
|
+{
|
|
+ uint8_t locty = s->active_locty;
|
|
+
|
|
+ trace_tpm_tis_pre_save(locty, s->rw_offset);
|
|
+
|
|
+ if (DEBUG_TIS) {
|
|
+ tpm_tis_dump_state(s, 0);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Synchronize with backend completion.
|
|
+ */
|
|
+ tpm_backend_finish_sync(s->be_driver);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+const VMStateDescription vmstate_locty = {
|
|
+ .name = "tpm-tis/locty",
|
|
+ .version_id = 0,
|
|
+ .fields = (VMStateField[]) {
|
|
+ VMSTATE_UINT32(state, TPMLocality),
|
|
+ VMSTATE_UINT32(inte, TPMLocality),
|
|
+ VMSTATE_UINT32(ints, TPMLocality),
|
|
+ VMSTATE_UINT8(access, TPMLocality),
|
|
+ VMSTATE_UINT32(sts, TPMLocality),
|
|
+ VMSTATE_UINT32(iface_id, TPMLocality),
|
|
+ VMSTATE_END_OF_LIST(),
|
|
+ }
|
|
+};
|
|
+
|
|
diff --git a/hw/tpm/tpm_tis_isa.c b/hw/tpm/tpm_tis_isa.c
|
|
new file mode 100644
|
|
index 00000000..45e25c02
|
|
--- /dev/null
|
|
+++ b/hw/tpm/tpm_tis_isa.c
|
|
@@ -0,0 +1,170 @@
|
|
+/*
|
|
+ * tpm_tis_isa.c - QEMU's TPM TIS ISA Device
|
|
+ *
|
|
+ * Copyright (C) 2006,2010-2013 IBM Corporation
|
|
+ *
|
|
+ * Authors:
|
|
+ * Stefan Berger <stefanb@us.ibm.com>
|
|
+ * David Safford <safford@us.ibm.com>
|
|
+ *
|
|
+ * Xen 4 support: Andrease Niederl <andreas.niederl@iaik.tugraz.at>
|
|
+ *
|
|
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
|
|
+ * See the COPYING file in the top-level directory.
|
|
+ *
|
|
+ * Implementation of the TIS interface according to specs found at
|
|
+ * http://www.trustedcomputinggroup.org. This implementation currently
|
|
+ * supports version 1.3, 21 March 2013
|
|
+ * In the developers menu choose the PC Client section then find the TIS
|
|
+ * specification.
|
|
+ *
|
|
+ * TPM TIS for TPM 2 implementation following TCG PC Client Platform
|
|
+ * TPM Profile (PTP) Specification, Familiy 2.0, Revision 00.43
|
|
+ */
|
|
+
|
|
+#include "qemu/osdep.h"
|
|
+#include "hw/isa/isa.h"
|
|
+#include "hw/qdev-properties.h"
|
|
+#include "migration/vmstate.h"
|
|
+#include "tpm_util.h"
|
|
+#include "tpm_tis.h"
|
|
+
|
|
+typedef struct TPMStateISA {
|
|
+ /*< private >*/
|
|
+ ISADevice parent_obj;
|
|
+
|
|
+ /*< public >*/
|
|
+ TPMState state; /* not a QOM object */
|
|
+} TPMStateISA;
|
|
+
|
|
+#define TPM_TIS_ISA(obj) OBJECT_CHECK(TPMStateISA, (obj), TYPE_TPM_TIS_ISA)
|
|
+
|
|
+static int tpm_tis_pre_save_isa(void *opaque)
|
|
+{
|
|
+ TPMStateISA *isadev = opaque;
|
|
+
|
|
+ return tpm_tis_pre_save(&isadev->state);
|
|
+}
|
|
+
|
|
+static const VMStateDescription vmstate_tpm_tis_isa = {
|
|
+ .name = "tpm-tis",
|
|
+ .version_id = 0,
|
|
+ .pre_save = tpm_tis_pre_save_isa,
|
|
+ .fields = (VMStateField[]) {
|
|
+ VMSTATE_BUFFER(state.buffer, TPMStateISA),
|
|
+ VMSTATE_UINT16(state.rw_offset, TPMStateISA),
|
|
+ VMSTATE_UINT8(state.active_locty, TPMStateISA),
|
|
+ VMSTATE_UINT8(state.aborting_locty, TPMStateISA),
|
|
+ VMSTATE_UINT8(state.next_locty, TPMStateISA),
|
|
+
|
|
+ VMSTATE_STRUCT_ARRAY(state.loc, TPMStateISA, TPM_TIS_NUM_LOCALITIES, 0,
|
|
+ vmstate_locty, TPMLocality),
|
|
+
|
|
+ VMSTATE_END_OF_LIST()
|
|
+ }
|
|
+};
|
|
+
|
|
+static void tpm_tis_isa_request_completed(TPMIf *ti, int ret)
|
|
+{
|
|
+ TPMStateISA *isadev = TPM_TIS_ISA(ti);
|
|
+ TPMState *s = &isadev->state;
|
|
+
|
|
+ tpm_tis_request_completed(s, ret);
|
|
+}
|
|
+
|
|
+static enum TPMVersion tpm_tis_isa_get_tpm_version(TPMIf *ti)
|
|
+{
|
|
+ TPMStateISA *isadev = TPM_TIS_ISA(ti);
|
|
+ TPMState *s = &isadev->state;
|
|
+
|
|
+ return tpm_tis_get_tpm_version(s);
|
|
+}
|
|
+
|
|
+static void tpm_tis_isa_reset(DeviceState *dev)
|
|
+{
|
|
+ TPMStateISA *isadev = TPM_TIS_ISA(dev);
|
|
+ TPMState *s = &isadev->state;
|
|
+
|
|
+ return tpm_tis_reset(s);
|
|
+}
|
|
+
|
|
+static Property tpm_tis_isa_properties[] = {
|
|
+ DEFINE_PROP_UINT32("irq", TPMStateISA, state.irq_num, TPM_TIS_IRQ),
|
|
+ DEFINE_PROP_TPMBE("tpmdev", TPMStateISA, state.be_driver),
|
|
+ DEFINE_PROP_BOOL("ppi", TPMStateISA, state.ppi_enabled, true),
|
|
+ DEFINE_PROP_END_OF_LIST(),
|
|
+};
|
|
+
|
|
+static void tpm_tis_isa_initfn(Object *obj)
|
|
+{
|
|
+ TPMStateISA *isadev = TPM_TIS_ISA(obj);
|
|
+ TPMState *s = &isadev->state;
|
|
+
|
|
+ memory_region_init_io(&s->mmio, obj, &tpm_tis_memory_ops,
|
|
+ s, "tpm-tis-mmio",
|
|
+ TPM_TIS_NUM_LOCALITIES << TPM_TIS_LOCALITY_SHIFT);
|
|
+}
|
|
+
|
|
+static void tpm_tis_isa_realizefn(DeviceState *dev, Error **errp)
|
|
+{
|
|
+ TPMStateISA *isadev = TPM_TIS_ISA(dev);
|
|
+ TPMState *s = &isadev->state;
|
|
+
|
|
+ if (!tpm_find()) {
|
|
+ error_setg(errp, "at most one TPM device is permitted");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (!s->be_driver) {
|
|
+ error_setg(errp, "'tpmdev' property is required");
|
|
+ return;
|
|
+ }
|
|
+ if (s->irq_num > 15) {
|
|
+ error_setg(errp, "IRQ %d is outside valid range of 0 to 15",
|
|
+ s->irq_num);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ isa_init_irq(ISA_DEVICE(dev), &s->irq, s->irq_num);
|
|
+
|
|
+ memory_region_add_subregion(isa_address_space(ISA_DEVICE(dev)),
|
|
+ TPM_TIS_ADDR_BASE, &s->mmio);
|
|
+
|
|
+ if (s->ppi_enabled) {
|
|
+ tpm_ppi_init(&s->ppi, isa_address_space(ISA_DEVICE(dev)),
|
|
+ TPM_PPI_ADDR_BASE, OBJECT(dev));
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tpm_tis_isa_class_init(ObjectClass *klass, void *data)
|
|
+{
|
|
+ DeviceClass *dc = DEVICE_CLASS(klass);
|
|
+ TPMIfClass *tc = TPM_IF_CLASS(klass);
|
|
+
|
|
+ dc->props = tpm_tis_isa_properties;
|
|
+ dc->vmsd = &vmstate_tpm_tis_isa;
|
|
+ tc->model = TPM_MODEL_TPM_TIS;
|
|
+ dc->realize = tpm_tis_isa_realizefn;
|
|
+ dc->reset = tpm_tis_isa_reset;
|
|
+ tc->request_completed = tpm_tis_isa_request_completed;
|
|
+ tc->get_version = tpm_tis_isa_get_tpm_version;
|
|
+}
|
|
+
|
|
+static const TypeInfo tpm_tis_isa_info = {
|
|
+ .name = TYPE_TPM_TIS_ISA,
|
|
+ .parent = TYPE_ISA_DEVICE,
|
|
+ .instance_size = sizeof(TPMStateISA),
|
|
+ .instance_init = tpm_tis_isa_initfn,
|
|
+ .class_init = tpm_tis_isa_class_init,
|
|
+ .interfaces = (InterfaceInfo[]) {
|
|
+ { TYPE_TPM_IF },
|
|
+ { }
|
|
+ }
|
|
+};
|
|
+
|
|
+static void tpm_tis_isa_register(void)
|
|
+{
|
|
+ type_register_static(&tpm_tis_isa_info);
|
|
+}
|
|
+
|
|
+type_init(tpm_tis_isa_register)
|
|
--
|
|
2.23.0
|
|
|