aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2017-12-17 15:27:41 +0000
committerPeter Maydell <peter.maydell@linaro.org>2017-12-17 15:27:41 +0000
commit411ad78115ebeb3411cf4b7622784b93dfabe259 (patch)
tree00d85d7c41e5d0eaae7bebb969f0271c627ad517 /hw
parent38d1b31e0501f938db39c5b2e508328530410246 (diff)
parent683c4b775355cc7acd301e8efe7d4c1c9acdafd8 (diff)
Merge remote-tracking branch 'remotes/stefanberger/tags/pull-tpm-2017-12-15-1' into staging
Merge tpm 2017/12/15 v1 # gpg: Signature made Fri 15 Dec 2017 04:44:15 GMT # gpg: using RSA key 0x75AD65802A0B4211 # gpg: Good signature from "Stefan Berger <stefanb@linux.vnet.ibm.com>" # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: B818 B9CA DF90 89C2 D5CE C66B 75AD 6580 2A0B 4211 * remotes/stefanberger/tags/pull-tpm-2017-12-15-1: (32 commits) tpm: tpm_passthrough: Fail startup if FE buffer size < BE buffer size tpm: tpm_emulator: get and set buffer size of device tpm: tpm_passthrough: Read the buffer size from the host device tpm: pull tpm_util_request() out of tpm_util_test() tpm: Move getting TPM buffer size to backends tpm: remove tpm_register_model() tpm-tis: use DEFINE_PROP_TPMBE qdev: add DEFINE_PROP_TPMBE tpm-tis: check that at most one TPM device exists tpm-tis: remove redundant 'tpm_tis:' in error messages tpm-emulator: add a FIXME comment about blocking cancel acpi: change TPM TIS data conditions tpm: add tpm_cmd_get_size() to tpm_util tpm: add TPM interface to lookup TPM version tpm: lookup the the TPM interface instead of TIS device tpm: rename qemu_find_tpm() -> qemu_find_tpm_be() tpm-tis: simplify header inclusion tpm-passthrough: workaround a possible race tpm-passthrough: simplify create() tpm-passthrough: make it safer to destroy after creation ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw')
-rw-r--r--hw/core/qdev-properties-system.c64
-rw-r--r--hw/i386/acpi-build.c14
-rw-r--r--hw/tpm/tpm_emulator.c98
-rw-r--r--hw/tpm/tpm_int.h31
-rw-r--r--hw/tpm/tpm_ioctl.h28
-rw-r--r--hw/tpm/tpm_passthrough.c90
-rw-r--r--hw/tpm/tpm_tis.c101
-rw-r--r--hw/tpm/tpm_util.c155
-rw-r--r--hw/tpm/tpm_util.h11
9 files changed, 441 insertions, 151 deletions
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index ec10da7424..c17364655c 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -21,6 +21,7 @@
#include "net/hub.h"
#include "qapi/visitor.h"
#include "chardev/char-fe.h"
+#include "sysemu/tpm_backend.h"
#include "sysemu/iothread.h"
static void get_pointer(Object *obj, Visitor *v, Property *prop,
@@ -236,6 +237,69 @@ const PropertyInfo qdev_prop_chr = {
.release = release_chr,
};
+/* --- character device --- */
+
+static void get_tpm(Object *obj, Visitor *v, const char *name, void *opaque,
+ Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ TPMBackend **be = qdev_get_prop_ptr(dev, opaque);
+ char *p;
+
+ p = g_strdup(*be ? (*be)->id : "");
+ visit_type_str(v, name, &p, errp);
+ g_free(p);
+}
+
+static void set_tpm(Object *obj, Visitor *v, const char *name, void *opaque,
+ Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Error *local_err = NULL;
+ Property *prop = opaque;
+ TPMBackend *s, **be = qdev_get_prop_ptr(dev, prop);
+ char *str;
+
+ if (dev->realized) {
+ qdev_prop_set_after_realize(dev, name, errp);
+ return;
+ }
+
+ visit_type_str(v, name, &str, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ s = qemu_find_tpm_be(str);
+ if (s == NULL) {
+ error_setg(errp, "Property '%s.%s' can't find value '%s'",
+ object_get_typename(obj), prop->name, str);
+ } else if (tpm_backend_init(s, TPM_IF(obj), errp) == 0) {
+ *be = s; /* weak reference, avoid cyclic ref */
+ }
+ g_free(str);
+}
+
+static void release_tpm(Object *obj, const char *name, void *opaque)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ TPMBackend **be = qdev_get_prop_ptr(dev, prop);
+
+ if (*be) {
+ tpm_backend_reset(*be);
+ }
+}
+
+const PropertyInfo qdev_prop_tpm = {
+ .name = "str",
+ .description = "ID of a tpm to use as a backend",
+ .get = get_tpm,
+ .set = set_tpm,
+ .release = release_tpm,
+};
+
/* --- netdev device --- */
static void get_netdev(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 73519ab3ac..dd1420b410 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -208,7 +208,7 @@ static void acpi_get_misc_info(AcpiMiscInfo *info)
}
info->has_hpet = hpet_find();
- info->tpm_version = tpm_get_version();
+ info->tpm_version = tpm_get_version(tpm_find());
info->pvpanic_port = pvpanic_port();
info->applesmc_io_base = applesmc_port();
}
@@ -2038,7 +2038,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
}
}
- if (misc->tpm_version != TPM_VERSION_UNSPEC) {
+ if (TPM_IS_TIS(tpm_find())) {
aml_append(crs, aml_memory32_fixed(TPM_TIS_ADDR_BASE,
TPM_TIS_ADDR_SIZE, AML_READ_WRITE));
}
@@ -2204,7 +2204,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
/* Scan all PCI buses. Generate tables to support hotplug. */
build_append_pci_bus_devices(scope, bus, pm->pcihp_bridge_en);
- if (misc->tpm_version != TPM_VERSION_UNSPEC) {
+ if (TPM_IS_TIS(tpm_find())) {
dev = aml_device("ISA.TPM");
aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C31")));
aml_append(dev, aml_name_decl("_STA", aml_int(0xF)));
@@ -2281,8 +2281,12 @@ build_tpm2(GArray *table_data, BIOSLinker *linker)
tpm2_ptr = acpi_data_push(table_data, sizeof *tpm2_ptr);
tpm2_ptr->platform_class = cpu_to_le16(TPM2_ACPI_CLASS_CLIENT);
- tpm2_ptr->control_area_address = cpu_to_le64(0);
- tpm2_ptr->start_method = cpu_to_le32(TPM2_START_METHOD_MMIO);
+ if (TPM_IS_TIS(tpm_find())) {
+ tpm2_ptr->control_area_address = cpu_to_le64(0);
+ tpm2_ptr->start_method = cpu_to_le32(TPM2_START_METHOD_MMIO);
+ } else {
+ g_warn_if_reached();
+ }
build_header(linker, table_data,
(void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4, NULL, NULL);
diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c
index e1a68104d6..3ae8bf6c5a 100644
--- a/hw/tpm/tpm_emulator.c
+++ b/hw/tpm/tpm_emulator.c
@@ -186,7 +186,6 @@ static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number,
static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd)
{
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
- TPMIfClass *tic = TPM_IF_GET_CLASS(tb->tpm_state);
Error *err = NULL;
DPRINTF("processing TPM command");
@@ -201,7 +200,6 @@ static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd)
goto error;
}
- tic->request_completed(TPM_IF(tb->tpm_state));
return;
error:
@@ -234,13 +232,14 @@ static int tpm_emulator_check_caps(TPMEmulator *tpm_emu)
switch (tpm_emu->tpm_version) {
case TPM_VERSION_1_2:
caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED |
- PTM_CAP_SET_LOCALITY | PTM_CAP_SET_DATAFD;
+ PTM_CAP_SET_LOCALITY | PTM_CAP_SET_DATAFD | PTM_CAP_STOP |
+ PTM_CAP_SET_BUFFERSIZE;
tpm = "1.2";
break;
case TPM_VERSION_2_0:
caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED |
PTM_CAP_SET_LOCALITY | PTM_CAP_RESET_TPMESTABLISHED |
- PTM_CAP_SET_DATAFD;
+ PTM_CAP_SET_DATAFD | PTM_CAP_STOP | PTM_CAP_SET_BUFFERSIZE;
tpm = "2";
break;
case TPM_VERSION_UNSPEC:
@@ -257,12 +256,76 @@ static int tpm_emulator_check_caps(TPMEmulator *tpm_emu)
return 0;
}
-static int tpm_emulator_startup_tpm(TPMBackend *tb)
+static int tpm_emulator_stop_tpm(TPMBackend *tb)
+{
+ TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
+ ptm_res res;
+
+ if (tpm_emulator_ctrlcmd(tpm_emu, CMD_STOP, &res, 0, sizeof(res)) < 0) {
+ error_report("tpm-emulator: Could not stop TPM: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ res = be32_to_cpu(res);
+ if (res) {
+ error_report("tpm-emulator: TPM result for CMD_STOP: 0x%x", res);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int tpm_emulator_set_buffer_size(TPMBackend *tb,
+ size_t wanted_size,
+ size_t *actual_size)
+{
+ TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
+ ptm_setbuffersize psbs;
+
+ if (tpm_emulator_stop_tpm(tb) < 0) {
+ return -1;
+ }
+
+ psbs.u.req.buffersize = cpu_to_be32(wanted_size);
+
+ if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_BUFFERSIZE, &psbs,
+ sizeof(psbs.u.req), sizeof(psbs.u.resp)) < 0) {
+ error_report("tpm-emulator: Could not set buffer size: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ psbs.u.resp.tpm_result = be32_to_cpu(psbs.u.resp.tpm_result);
+ if (psbs.u.resp.tpm_result != 0) {
+ error_report("tpm-emulator: TPM result for set buffer size : 0x%x",
+ psbs.u.resp.tpm_result);
+ return -1;
+ }
+
+ if (actual_size) {
+ *actual_size = be32_to_cpu(psbs.u.resp.buffersize);
+ }
+
+ DPRINTF("buffer size: %u, min: %u, max: %u\n",
+ be32_to_cpu(psbs.u.resp.buffersize),
+ be32_to_cpu(psbs.u.resp.minsize),
+ be32_to_cpu(psbs.u.resp.maxsize));
+
+ return 0;
+}
+
+static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize)
{
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
ptm_init init;
ptm_res res;
+ if (buffersize != 0 &&
+ tpm_emulator_set_buffer_size(tb, buffersize, NULL) < 0) {
+ goto err_exit;
+ }
+
DPRINTF("%s", __func__);
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_INIT, &init, sizeof(init),
sizeof(init)) < 0) {
@@ -340,6 +403,7 @@ static void tpm_emulator_cancel_cmd(TPMBackend *tb)
return;
}
+ /* FIXME: make the function non-blocking, or it may block a VCPU */
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_CANCEL_TPM_CMD, &res, 0,
sizeof(res)) < 0) {
error_report("tpm-emulator: Could not cancel command: %s",
@@ -357,6 +421,17 @@ static TPMVersion tpm_emulator_get_tpm_version(TPMBackend *tb)
return tpm_emu->tpm_version;
}
+static size_t tpm_emulator_get_buffer_size(TPMBackend *tb)
+{
+ size_t actual_size;
+
+ if (tpm_emulator_set_buffer_size(tb, 0, &actual_size) < 0) {
+ return 4096;
+ }
+
+ return actual_size;
+}
+
static int tpm_emulator_block_migration(TPMEmulator *tpm_emu)
{
Error *err = NULL;
@@ -465,22 +540,16 @@ err:
return -1;
}
-static TPMBackend *tpm_emulator_create(QemuOpts *opts, const char *id)
+static TPMBackend *tpm_emulator_create(QemuOpts *opts)
{
TPMBackend *tb = TPM_BACKEND(object_new(TYPE_TPM_EMULATOR));
- tb->id = g_strdup(id);
-
if (tpm_emulator_handle_device_opts(TPM_EMULATOR(tb), opts)) {
- goto err_exit;
+ object_unref(OBJECT(tb));
+ return NULL;
}
return tb;
-
-err_exit:
- object_unref(OBJECT(tb));
-
- return NULL;
}
static TpmTypeOptions *tpm_emulator_get_tpm_options(TPMBackend *tb)
@@ -563,6 +632,7 @@ static void tpm_emulator_class_init(ObjectClass *klass, void *data)
tbc->get_tpm_established_flag = tpm_emulator_get_tpm_established_flag;
tbc->reset_tpm_established_flag = tpm_emulator_reset_tpm_established_flag;
tbc->get_tpm_version = tpm_emulator_get_tpm_version;
+ tbc->get_buffer_size = tpm_emulator_get_buffer_size;
tbc->get_tpm_options = tpm_emulator_get_tpm_options;
tbc->handle_request = tpm_emulator_handle_request;
diff --git a/hw/tpm/tpm_int.h b/hw/tpm/tpm_int.h
index 9c045b6691..abbca5191a 100644
--- a/hw/tpm/tpm_int.h
+++ b/hw/tpm/tpm_int.h
@@ -13,28 +13,8 @@
#define TPM_TPM_INT_H
#include "qemu/osdep.h"
-#include "qom/object.h"
-#define TYPE_TPM_IF "tpm-if"
-#define TPM_IF_CLASS(klass) \
- OBJECT_CLASS_CHECK(TPMIfClass, (klass), TYPE_TPM_IF)
-#define TPM_IF_GET_CLASS(obj) \
- OBJECT_GET_CLASS(TPMIfClass, (obj), TYPE_TPM_IF)
-#define TPM_IF(obj) \
- INTERFACE_CHECK(TPMIf, (obj), TYPE_TPM_IF)
-
-typedef struct TPMIf {
- Object parent_obj;
-} TPMIf;
-
-typedef struct TPMIfClass {
- InterfaceClass parent_class;
-
- /* run in thread pool by backend */
- void (*request_completed)(TPMIf *obj);
-} TPMIfClass;
-
-#define TPM_STANDARD_CMDLINE_OPTS \
+#define TPM_STANDARD_CMDLINE_OPTS \
{ \
.name = "type", \
.type = QEMU_OPT_STRING, \
@@ -65,11 +45,20 @@ struct tpm_resp_hdr {
#define TPM_ORD_ContinueSelfTest 0x53
#define TPM_ORD_GetTicks 0xf1
+#define TPM_ORD_GetCapability 0x65
+#define TPM_CAP_PROPERTY 0x05
+
+#define TPM_CAP_PROP_INPUT_BUFFER 0x124
/* TPM2 defines */
#define TPM2_ST_NO_SESSIONS 0x8001
#define TPM2_CC_ReadClock 0x00000181
+#define TPM2_CC_GetCapability 0x0000017a
+
+#define TPM2_CAP_TPM_PROPERTIES 0x6
+
+#define TPM2_PT_MAX_COMMAND_SIZE 0x11e
#endif /* TPM_TPM_INT_H */
diff --git a/hw/tpm/tpm_ioctl.h b/hw/tpm/tpm_ioctl.h
index 33564b11de..54c8d345ad 100644
--- a/hw/tpm/tpm_ioctl.h
+++ b/hw/tpm/tpm_ioctl.h
@@ -169,6 +169,28 @@ struct ptm_getconfig {
#define PTM_CONFIG_FLAG_FILE_KEY 0x1
#define PTM_CONFIG_FLAG_MIGRATION_KEY 0x2
+/*
+ * PTM_SET_BUFFERSIZE: Set the buffer size to be used by the TPM.
+ * A 0 on input queries for the current buffer size. Any other
+ * number will try to set the buffer size. The returned number is
+ * the buffer size that will be used, which can be larger than the
+ * requested one, if it was below the minimum, or smaller than the
+ * requested one, if it was above the maximum.
+ */
+struct ptm_setbuffersize {
+ union {
+ struct {
+ uint32_t buffersize; /* 0 to query for current buffer size */
+ } req; /* request */
+ struct {
+ ptm_res tpm_result;
+ uint32_t buffersize; /* buffer size in use */
+ uint32_t minsize; /* min. supported buffer size */
+ uint32_t maxsize; /* max. supported buffer size */
+ } resp; /* response */
+ } u;
+};
+
typedef uint64_t ptm_cap;
typedef struct ptm_est ptm_est;
@@ -179,6 +201,7 @@ typedef struct ptm_init ptm_init;
typedef struct ptm_getstate ptm_getstate;
typedef struct ptm_setstate ptm_setstate;
typedef struct ptm_getconfig ptm_getconfig;
+typedef struct ptm_setbuffersize ptm_setbuffersize;
/* capability flags returned by PTM_GET_CAPABILITY */
#define PTM_CAP_INIT (1)
@@ -194,6 +217,7 @@ typedef struct ptm_getconfig ptm_getconfig;
#define PTM_CAP_STOP (1 << 10)
#define PTM_CAP_GET_CONFIG (1 << 11)
#define PTM_CAP_SET_DATAFD (1 << 12)
+#define PTM_CAP_SET_BUFFERSIZE (1 << 13)
enum {
PTM_GET_CAPABILITY = _IOR('P', 0, ptm_cap),
@@ -212,6 +236,7 @@ enum {
PTM_STOP = _IOR('P', 13, ptm_res),
PTM_GET_CONFIG = _IOR('P', 14, ptm_getconfig),
PTM_SET_DATAFD = _IOR('P', 15, ptm_res),
+ PTM_SET_BUFFERSIZE = _IOWR('P', 16, ptm_setbuffersize),
};
/*
@@ -240,7 +265,8 @@ enum {
CMD_SET_STATEBLOB,
CMD_STOP,
CMD_GET_CONFIG,
- CMD_SET_DATAFD
+ CMD_SET_DATAFD,
+ CMD_SET_BUFFERSIZE,
};
#endif /* _TPM_IOCTL_H */
diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c
index c440aff4b2..487aae2043 100644
--- a/hw/tpm/tpm_passthrough.c
+++ b/hw/tpm/tpm_passthrough.c
@@ -57,6 +57,7 @@ struct TPMPassthruState {
int cancel_fd;
TPMVersion tpm_version;
+ size_t tpm_buffersize;
};
typedef struct TPMPassthruState TPMPassthruState;
@@ -89,6 +90,7 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
bool is_selftest;
const struct tpm_resp_hdr *hdr;
+ /* FIXME: protect shared variables or use other sync mechanism */
tpm_pt->tpm_op_canceled = false;
tpm_pt->tpm_executing = true;
*selftest_done = false;
@@ -139,14 +141,11 @@ err_exit:
static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd *cmd)
{
TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
- TPMIfClass *tic = TPM_IF_GET_CLASS(tb->tpm_state);
DPRINTF("tpm_passthrough: processing command %p\n", cmd);
tpm_passthrough_unix_tx_bufs(tpm_pt, cmd->in, cmd->in_len,
cmd->out, cmd->out_len, &cmd->selftest_done);
-
- tic->request_completed(TPM_IF(tb->tpm_state));
}
static void tpm_passthrough_reset(TPMBackend *tb)
@@ -181,12 +180,11 @@ static void tpm_passthrough_cancel_cmd(TPMBackend *tb)
*/
if (tpm_pt->tpm_executing) {
if (tpm_pt->cancel_fd >= 0) {
+ tpm_pt->tpm_op_canceled = true;
n = write(tpm_pt->cancel_fd, "-", 1);
if (n != 1) {
error_report("Canceling TPM command failed: %s",
strerror(errno));
- } else {
- tpm_pt->tpm_op_canceled = true;
}
} else {
error_report("Cannot cancel TPM command due to missing "
@@ -202,6 +200,19 @@ static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb)
return tpm_pt->tpm_version;
}
+static size_t tpm_passthrough_get_buffer_size(TPMBackend *tb)
+{
+ TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
+ int ret;
+
+ ret = tpm_util_get_buffer_size(tpm_pt->tpm_fd, tpm_pt->tpm_version,
+ &tpm_pt->tpm_buffersize);
+ if (ret < 0) {
+ tpm_pt->tpm_buffersize = 4096;
+ }
+ return tpm_pt->tpm_buffersize;
+}
+
/*
* Unless path or file descriptor set has been provided by user,
* determine the sysfs cancel file following kernel documentation
@@ -229,9 +240,7 @@ static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt)
if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel",
dev) < sizeof(path)) {
fd = qemu_open(path, O_WRONLY);
- if (fd >= 0) {
- tpm_pt->options->cancel_path = g_strdup(path);
- } else {
+ if (fd < 0) {
error_report("tpm_passthrough: Could not open TPM cancel "
"path %s : %s", path, strerror(errno));
}
@@ -244,9 +253,9 @@ static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt)
return fd;
}
-static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
+static int
+tpm_passthrough_handle_device_opts(TPMPassthruState *tpm_pt, QemuOpts *opts)
{
- TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
const char *value;
value = qemu_opt_get(opts, "cancel-path");
@@ -266,52 +275,47 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
if (tpm_pt->tpm_fd < 0) {
error_report("Cannot access TPM device using '%s': %s",
tpm_pt->tpm_dev, strerror(errno));
- goto err_free_parameters;
+ return -1;
}
if (tpm_util_test_tpmdev(tpm_pt->tpm_fd, &tpm_pt->tpm_version)) {
error_report("'%s' is not a TPM device.",
tpm_pt->tpm_dev);
- goto err_close_tpmdev;
+ return -1;
}
- return 0;
-
- err_close_tpmdev:
- qemu_close(tpm_pt->tpm_fd);
- tpm_pt->tpm_fd = -1;
-
- err_free_parameters:
- qapi_free_TPMPassthroughOptions(tpm_pt->options);
- tpm_pt->options = NULL;
- tpm_pt->tpm_dev = NULL;
+ tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tpm_pt);
+ if (tpm_pt->cancel_fd < 0) {
+ return -1;
+ }
- return 1;
+ return 0;
}
-static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
+static TPMBackend *tpm_passthrough_create(QemuOpts *opts)
{
Object *obj = object_new(TYPE_TPM_PASSTHROUGH);
- TPMBackend *tb = TPM_BACKEND(obj);
- TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
- tb->id = g_strdup(id);
-
- if (tpm_passthrough_handle_device_opts(opts, tb)) {
- goto err_exit;
+ if (tpm_passthrough_handle_device_opts(TPM_PASSTHROUGH(obj), opts)) {
+ object_unref(obj);
+ return NULL;
}
- tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tpm_pt);
- if (tpm_pt->cancel_fd < 0) {
- goto err_exit;
- }
+ return TPM_BACKEND(obj);
+}
- return tb;
+static int tpm_passthrough_startup_tpm(TPMBackend *tb, size_t buffersize)
+{
+ TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
-err_exit:
- object_unref(obj);
+ if (buffersize && buffersize < tpm_pt->tpm_buffersize) {
+ error_report("Requested buffer size of %zu is smaller than host TPM's "
+ "fixed buffer size of %zu",
+ buffersize, tpm_pt->tpm_buffersize);
+ return -1;
+ }
- return NULL;
+ return 0;
}
static TpmTypeOptions *tpm_passthrough_get_tpm_options(TPMBackend *tb)
@@ -355,8 +359,12 @@ static void tpm_passthrough_inst_finalize(Object *obj)
tpm_passthrough_cancel_cmd(TPM_BACKEND(obj));
- qemu_close(tpm_pt->tpm_fd);
- qemu_close(tpm_pt->cancel_fd);
+ if (tpm_pt->tpm_fd >= 0) {
+ qemu_close(tpm_pt->tpm_fd);
+ }
+ if (tpm_pt->cancel_fd >= 0) {
+ qemu_close(tpm_pt->cancel_fd);
+ }
qapi_free_TPMPassthroughOptions(tpm_pt->options);
}
@@ -368,12 +376,14 @@ static void tpm_passthrough_class_init(ObjectClass *klass, void *data)
tbc->opts = tpm_passthrough_cmdline_opts;
tbc->desc = "Passthrough TPM backend driver";
tbc->create = tpm_passthrough_create;
+ tbc->startup_tpm = tpm_passthrough_startup_tpm;
tbc->reset = tpm_passthrough_reset;
tbc->cancel_cmd = tpm_passthrough_cancel_cmd;
tbc->get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag;
tbc->reset_tpm_established_flag =
tpm_passthrough_reset_tpm_established_flag;
tbc->get_tpm_version = tpm_passthrough_get_tpm_version;
+ tbc->get_buffer_size = tpm_passthrough_get_buffer_size;
tbc->get_tpm_options = tpm_passthrough_get_tpm_options;
tbc->handle_request = tpm_passthrough_handle_request;
}
diff --git a/hw/tpm/tpm_tis.c b/hw/tpm/tpm_tis.c
index 42d647d363..b8e811b086 100644
--- a/hw/tpm/tpm_tis.c
+++ b/hw/tpm/tpm_tis.c
@@ -24,17 +24,13 @@
#include "qemu/osdep.h"
#include "hw/isa/isa.h"
-#include "sysemu/tpm_backend.h"
-#include "tpm_int.h"
-#include "sysemu/block-backend.h"
-#include "exec/address-spaces.h"
-#include "hw/hw.h"
-#include "hw/i386/pc.h"
-#include "hw/pci/pci_ids.h"
#include "qapi/error.h"
-#include "qemu-common.h"
-#include "qemu/main-loop.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"
#define TPM_TIS_NUM_LOCALITIES 5 /* per spec */
#define TPM_TIS_LOCALITY_SHIFT 12
@@ -72,11 +68,10 @@ typedef struct TPMLocality {
TPMSizedBuffer r_buffer;
} TPMLocality;
-struct TPMState {
+typedef struct TPMState {
ISADevice busdev;
MemoryRegion mmio;
- QEMUBH *bh;
uint32_t offset;
uint8_t buf[TPM_TIS_BUFFER_MAX];
@@ -89,13 +84,13 @@ struct TPMState {
qemu_irq irq;
uint32_t irq_num;
- uint8_t locty_number;
TPMBackendCmd cmd;
- char *backend;
TPMBackend *be_driver;
TPMVersion be_tpm_version;
-};
+
+ size_t be_buffer_size;
+} TPMState;
#define TPM(obj) OBJECT_CHECK(TPMState, (obj), TYPE_TPM_TIS)
@@ -222,7 +217,7 @@ static uint8_t tpm_tis_locality_from_addr(hwaddr addr)
static uint32_t tpm_tis_get_size_from_buffer(const TPMSizedBuffer *sb)
{
- return be32_to_cpu(*(uint32_t *)&sb->buffer[2]);
+ return tpm_cmd_get_size(sb->buffer);
}
static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string)
@@ -411,10 +406,20 @@ static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty)
tpm_tis_abort(s, locty);
}
-static void tpm_tis_receive_bh(void *opaque)
+/*
+ * Callback from the TPM to indicate that the response was received.
+ */
+static void tpm_tis_request_completed(TPMIf *ti)
{
- TPMState *s = opaque;
+ TPMState *s = TPM(ti);
uint8_t locty = s->cmd.locty;
+ uint8_t l;
+
+ if (s->cmd.selftest_done) {
+ for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
+ s->loc[locty].sts |= TPM_TIS_STS_SELFTEST_DONE;
+ }
+ }
tpm_tis_sts_set(&s->loc[locty],
TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE);
@@ -432,23 +437,6 @@ static void tpm_tis_receive_bh(void *opaque)
TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID);
}
-static void tpm_tis_request_completed(TPMIf *ti)
-{
- TPMState *s = TPM(ti);
-
- bool is_selftest_done = s->cmd.selftest_done;
- uint8_t locty = s->cmd.locty;
- uint8_t l;
-
- if (is_selftest_done) {
- for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
- s->loc[locty].sts |= TPM_TIS_STS_SELFTEST_DONE;
- }
- }
-
- qemu_bh_schedule(s->bh);
-}
-
/*
* Read a byte of response data
*/
@@ -986,15 +974,14 @@ static const MemoryRegionOps tpm_tis_memory_ops = {
},
};
-static int tpm_tis_do_startup_tpm(TPMState *s)
+static int tpm_tis_do_startup_tpm(TPMState *s, uint32_t buffersize)
{
- return tpm_backend_startup_tpm(s->be_driver);
+ return tpm_backend_startup_tpm(s->be_driver, buffersize);
}
-static void tpm_tis_realloc_buffer(TPMSizedBuffer *sb)
+static void tpm_tis_realloc_buffer(TPMSizedBuffer *sb,
+ size_t wanted_size)
{
- size_t wanted_size = 4096; /* Linux tpm.c buffer size */
-
if (sb->size != wanted_size) {
sb->buffer = g_realloc(sb->buffer, wanted_size);
sb->size = wanted_size;
@@ -1004,9 +991,9 @@ static void tpm_tis_realloc_buffer(TPMSizedBuffer *sb)
/*
* Get the TPMVersion of the backend device being used
*/
-TPMVersion tpm_tis_get_tpm_version(Object *obj)
+static enum TPMVersion tpm_tis_get_tpm_version(TPMIf *ti)
{
- TPMState *s = TPM(obj);
+ TPMState *s = TPM(ti);
if (tpm_backend_had_startup_error(s->be_driver)) {
return TPM_VERSION_UNSPEC;
@@ -1025,6 +1012,7 @@ static void tpm_tis_reset(DeviceState *dev)
int c;
s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver);
+ s->be_buffer_size = tpm_backend_get_buffer_size(s->be_driver);
tpm_backend_reset(s->be_driver);
@@ -1051,12 +1039,12 @@ static void tpm_tis_reset(DeviceState *dev)
s->loc[c].state = TPM_TIS_STATE_IDLE;
s->loc[c].w_offset = 0;
- tpm_tis_realloc_buffer(&s->loc[c].w_buffer);
+ tpm_tis_realloc_buffer(&s->loc[c].w_buffer, s->be_buffer_size);
s->loc[c].r_offset = 0;
- tpm_tis_realloc_buffer(&s->loc[c].r_buffer);
+ tpm_tis_realloc_buffer(&s->loc[c].r_buffer, s->be_buffer_size);
}
- tpm_tis_do_startup_tpm(s);
+ tpm_tis_do_startup_tpm(s, 0);
}
static const VMStateDescription vmstate_tpm_tis = {
@@ -1066,7 +1054,7 @@ static const VMStateDescription vmstate_tpm_tis = {
static Property tpm_tis_properties[] = {
DEFINE_PROP_UINT32("irq", TPMState, irq_num, TPM_TIS_IRQ),
- DEFINE_PROP_STRING("tpmdev", TPMState, backend),
+ DEFINE_PROP_TPMBE("tpmdev", TPMState, be_driver),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1074,29 +1062,21 @@ static void tpm_tis_realizefn(DeviceState *dev, Error **errp)
{
TPMState *s = TPM(dev);
- s->be_driver = qemu_find_tpm(s->backend);
- if (!s->be_driver) {
- error_setg(errp, "tpm_tis: backend driver with id %s could not be "
- "found", s->backend);
+ if (!tpm_find()) {
+ error_setg(errp, "at most one TPM device is permitted");
return;
}
- s->be_driver->fe_model = TPM_MODEL_TPM_TIS;
-
- if (tpm_backend_init(s->be_driver, s)) {
- error_setg(errp, "tpm_tis: backend driver with id %s could not be "
- "initialized", s->backend);
+ if (!s->be_driver) {
+ error_setg(errp, "'tpmdev' property is required");
return;
}
-
if (s->irq_num > 15) {
- error_setg(errp, "tpm_tis: IRQ %d for TPM TIS is outside valid range "
- "of 0 to 15", s->irq_num);
+ error_setg(errp, "IRQ %d is outside valid range of 0 to 15",
+ s->irq_num);
return;
}
- s->bh = qemu_bh_new(tpm_tis_receive_bh, s);
-
isa_init_irq(&s->busdev, &s->irq, s->irq_num);
memory_region_add_subregion(isa_address_space(ISA_DEVICE(dev)),
@@ -1121,6 +1101,8 @@ static void tpm_tis_class_init(ObjectClass *klass, void *data)
dc->props = tpm_tis_properties;
dc->reset = tpm_tis_reset;
dc->vmsd = &vmstate_tpm_tis;
+ tc->model = TPM_MODEL_TPM_TIS;
+ tc->get_version = tpm_tis_get_tpm_version;
tc->request_completed = tpm_tis_request_completed;
}
@@ -1139,7 +1121,6 @@ static const TypeInfo tpm_tis_info = {
static void tpm_tis_register(void)
{
type_register_static(&tpm_tis_info);
- tpm_register_model(TPM_MODEL_TPM_TIS);
}
type_init(tpm_tis_register)
diff --git a/hw/tpm/tpm_util.c b/hw/tpm/tpm_util.c
index daf1faa63d..a317243a7e 100644
--- a/hw/tpm/tpm_util.c
+++ b/hw/tpm/tpm_util.c
@@ -20,10 +20,19 @@
*/
#include "qemu/osdep.h"
+#include "qemu/error-report.h"
#include "tpm_util.h"
#include "tpm_int.h"
#include "exec/memory.h"
+#define DEBUG_TPM 0
+
+#define DPRINTF(fmt, ...) do { \
+ if (DEBUG_TPM) { \
+ fprintf(stderr, "tpm-util:"fmt"\n", ## __VA_ARGS__); \
+ } \
+} while (0)
+
/*
* Write an error message in the given output buffer.
*/
@@ -50,13 +59,13 @@ bool tpm_util_is_selftest(const uint8_t *in, uint32_t in_len)
}
/*
- * A basic test of a TPM device. We expect a well formatted response header
- * (error response is fine) within one second.
+ * Send request to a TPM device. We expect a response within one second.
*/
-static int tpm_util_test(int fd,
- unsigned char *request,
- size_t requestlen,
- uint16_t *return_tag)
+static int tpm_util_request(int fd,
+ unsigned char *request,
+ size_t requestlen,
+ unsigned char *response,
+ size_t responselen)
{
struct tpm_resp_hdr *resp;
fd_set readfds;
@@ -65,7 +74,6 @@ static int tpm_util_test(int fd,
.tv_sec = 1,
.tv_usec = 0,
};
- unsigned char buf[1024];
n = write(fd, request, requestlen);
if (n < 0) {
@@ -84,17 +92,40 @@ static int tpm_util_test(int fd,
return -errno;
}
- n = read(fd, &buf, sizeof(buf));
+ n = read(fd, response, responselen);
if (n < sizeof(struct tpm_resp_hdr)) {
return -EFAULT;
}
- resp = (struct tpm_resp_hdr *)buf;
+ resp = (struct tpm_resp_hdr *)response;
/* check the header */
if (be32_to_cpu(resp->len) != n) {
return -EMSGSIZE;
}
+ return 0;
+}
+
+/*
+ * A basic test of a TPM device. We expect a well formatted response header
+ * (error response is fine).
+ */
+static int tpm_util_test(int fd,
+ unsigned char *request,
+ size_t requestlen,
+ uint16_t *return_tag)
+{
+ struct tpm_resp_hdr *resp;
+ unsigned char buf[1024];
+ ssize_t ret;
+
+ ret = tpm_util_request(fd, request, requestlen,
+ buf, sizeof(buf));
+ if (ret < 0) {
+ return ret;
+ }
+
+ resp = (struct tpm_resp_hdr *)buf;
*return_tag = be16_to_cpu(resp->tag);
return 0;
@@ -151,3 +182,109 @@ int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version)
return 1;
}
+
+int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
+ size_t *buffersize)
+{
+ unsigned char buf[1024];
+ int ret;
+
+ switch (tpm_version) {
+ case TPM_VERSION_1_2: {
+ const struct tpm_req_get_buffer_size {
+ struct tpm_req_hdr hdr;
+ uint32_t capability;
+ uint32_t len;
+ uint32_t subcap;
+ } QEMU_PACKED tpm_get_buffer_size = {
+ .hdr = {
+ .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND),
+ .len = cpu_to_be32(sizeof(tpm_get_buffer_size)),
+ .ordinal = cpu_to_be32(TPM_ORD_GetCapability),
+ },
+ .capability = cpu_to_be32(TPM_CAP_PROPERTY),
+ .len = cpu_to_be32(sizeof(uint32_t)),
+ .subcap = cpu_to_be32(TPM_CAP_PROP_INPUT_BUFFER),
+ };
+ struct tpm_resp_get_buffer_size {
+ struct tpm_resp_hdr hdr;
+ uint32_t len;
+ uint32_t buffersize;
+ } QEMU_PACKED *tpm_resp = (struct tpm_resp_get_buffer_size *)buf;
+
+ ret = tpm_util_request(tpm_fd, (unsigned char *)&tpm_get_buffer_size,
+ sizeof(tpm_get_buffer_size), buf, sizeof(buf));
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (be32_to_cpu(tpm_resp->hdr.len) != sizeof(*tpm_resp) ||
+ be32_to_cpu(tpm_resp->len) != sizeof(uint32_t)) {
+ DPRINTF("tpm_resp->hdr.len = %u, expected = %zu\n",
+ be32_to_cpu(tpm_resp->hdr.len), sizeof(*tpm_resp));
+ DPRINTF("tpm_resp->len = %u, expected = %zu\n",
+ be32_to_cpu(tpm_resp->len), sizeof(uint32_t));
+ error_report("tpm_util: Got unexpected response to "
+ "TPM_GetCapability; errcode: 0x%x",
+ be32_to_cpu(tpm_resp->hdr.errcode));
+ return -EFAULT;
+ }
+ *buffersize = be32_to_cpu(tpm_resp->buffersize);
+ break;
+ }
+ case TPM_VERSION_2_0: {
+ const struct tpm2_req_get_buffer_size {
+ struct tpm_req_hdr hdr;
+ uint32_t capability;
+ uint32_t property;
+ uint32_t count;
+ } QEMU_PACKED tpm2_get_buffer_size = {
+ .hdr = {
+ .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
+ .len = cpu_to_be32(sizeof(tpm2_get_buffer_size)),
+ .ordinal = cpu_to_be32(TPM2_CC_GetCapability),
+ },
+ .capability = cpu_to_be32(TPM2_CAP_TPM_PROPERTIES),
+ .property = cpu_to_be32(TPM2_PT_MAX_COMMAND_SIZE),
+ .count = cpu_to_be32(2), /* also get TPM2_PT_MAX_RESPONSE_SIZE */
+ };
+ struct tpm2_resp_get_buffer_size {
+ struct tpm_resp_hdr hdr;
+ uint8_t more;
+ uint32_t capability;
+ uint32_t count;
+ uint32_t property1;
+ uint32_t value1;
+ uint32_t property2;
+ uint32_t value2;
+ } QEMU_PACKED *tpm2_resp = (struct tpm2_resp_get_buffer_size *)buf;
+
+ ret = tpm_util_request(tpm_fd, (unsigned char *)&tpm2_get_buffer_size,
+ sizeof(tpm2_get_buffer_size), buf, sizeof(buf));
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (be32_to_cpu(tpm2_resp->hdr.len) != sizeof(*tpm2_resp) ||
+ be32_to_cpu(tpm2_resp->count) != 2) {
+ DPRINTF("tpm2_resp->hdr.len = %u, expected = %zu\n",
+ be32_to_cpu(tpm2_resp->hdr.len), sizeof(*tpm2_resp));
+ DPRINTF("tpm2_resp->len = %u, expected = %u\n",
+ be32_to_cpu(tpm2_resp->count), 2);
+ error_report("tpm_util: Got unexpected response to "
+ "TPM2_GetCapability; errcode: 0x%x",
+ be32_to_cpu(tpm2_resp->hdr.errcode));
+ return -EFAULT;
+ }
+ *buffersize = MAX(be32_to_cpu(tpm2_resp->value1),
+ be32_to_cpu(tpm2_resp->value2));
+ break;
+ }
+ case TPM_VERSION_UNSPEC:
+ return -EFAULT;
+ }
+
+ DPRINTF("buffersize of device: %zu\n", *buffersize);
+
+ return 0;
+}
diff --git a/hw/tpm/tpm_util.h b/hw/tpm/tpm_util.h
index 2f7c96146d..1c17e3913b 100644
--- a/hw/tpm/tpm_util.h
+++ b/hw/tpm/tpm_util.h
@@ -22,7 +22,8 @@
#ifndef TPM_TPM_UTIL_H
#define TPM_TPM_UTIL_H
-#include "sysemu/tpm_backend.h"
+#include "sysemu/tpm.h"
+#include "qemu/bswap.h"
void tpm_util_write_fatal_error_response(uint8_t *out, uint32_t out_len);
@@ -30,4 +31,12 @@ bool tpm_util_is_selftest(const uint8_t *in, uint32_t in_len);
int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version);
+static inline uint32_t tpm_cmd_get_size(const void *b)
+{
+ return be32_to_cpu(*(const uint32_t *)(b + 2));
+}
+
+int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
+ size_t *buffersize);
+
#endif /* TPM_TPM_UTIL_H */