Merge tpm 2017/10/04 v3

-----BEGIN PGP SIGNATURE-----
 
 iQEcBAABAgAGBQJZ4KVjAAoJEHWtZYAqC0IRiAoIAK1JYsChRXR7T0jbQAuHglcQ
 AfU6gLrejB4iq6u7jjR/z5Z3PO35YMF4/bwr4T9m1uRcqplggsyin93p5SmLKMtb
 7d76i24am7oDveho1APe3vvoPd8ghmombsi7xVx1Yu80Ukcx39AA0ncCWSqtFjMk
 DT8otWwg4ltTSuAtNVAQl0Drgm63ptAM4m/GekCBdWFK+Gl3fAJhkZchLUCdedJf
 ePEwZc3TCSnWmNjJBAPmjIeZeyUG841xOLc5Y+V+7TRj28QO7G0PM3PV6d1rwqYX
 otP1NmmMYKuCqdB1YUFUoQ3Z4yymDBcvAoNlYcjpKcz4ypbUlcd57aiWn0sATWc=
 =2Z/c
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/stefanberger/tags/pull-tpm-2017-10-04-3' into staging

Merge tpm 2017/10/04 v3

# gpg: Signature made Fri 13 Oct 2017 12:37:07 BST
# 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-10-04-3:
  specs: Describe the TPM support in QEMU
  tpm: Move tpm_cleanup() to right place
  tpm: Added support for TPM emulator
  tpm-passthrough: move reusable code to utils
  tpm-backend: Move realloc_buffer() implementation to tpm-tis model
  tpm-backend: Add new API to read backend TpmInfo
  tpm-backend: Made few interface methods optional
  tpm-backend: Initialize and free data members in it's own methods
  tpm-backend: Move thread handling inside TPMBackend
  tpm-backend: Remove unneeded member variable from backend class
  tpm: Use EMSGSIZE instead of EBADMSG to compile on OpenBSD

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2017-10-16 13:04:43 +01:00
commit 78b62d3766
17 changed files with 1229 additions and 384 deletions

View File

@ -18,7 +18,24 @@
#include "qapi/qmp/qerror.h"
#include "sysemu/tpm.h"
#include "qemu/thread.h"
#include "sysemu/tpm_backend_int.h"
static void tpm_backend_worker_thread(gpointer data, gpointer user_data)
{
TPMBackend *s = TPM_BACKEND(user_data);
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
assert(k->handle_request != NULL);
k->handle_request(s, (TPMBackendCmd)data);
}
static void tpm_backend_thread_end(TPMBackend *s)
{
if (s->thread_pool) {
g_thread_pool_push(s->thread_pool, (gpointer)TPM_BACKEND_CMD_END, NULL);
g_thread_pool_free(s->thread_pool, FALSE, TRUE);
s->thread_pool = NULL;
}
}
enum TpmType tpm_backend_get_type(TPMBackend *s)
{
@ -27,67 +44,67 @@ enum TpmType tpm_backend_get_type(TPMBackend *s)
return k->ops->type;
}
const char *tpm_backend_get_desc(TPMBackend *s)
{
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
return k->ops->desc();
}
void tpm_backend_destroy(TPMBackend *s)
{
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
k->ops->destroy(s);
}
int tpm_backend_init(TPMBackend *s, TPMState *state,
TPMRecvDataCB *datacb)
{
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
return k->ops->init(s, state, datacb);
s->tpm_state = state;
s->recv_data_callback = datacb;
s->had_startup_error = false;
return k->ops->init ? k->ops->init(s) : 0;
}
int tpm_backend_startup_tpm(TPMBackend *s)
{
int res = 0;
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
return k->ops->startup_tpm(s);
/* terminate a running TPM */
tpm_backend_thread_end(s);
s->thread_pool = g_thread_pool_new(tpm_backend_worker_thread, s, 1, TRUE,
NULL);
g_thread_pool_push(s->thread_pool, (gpointer)TPM_BACKEND_CMD_INIT, NULL);
res = k->ops->startup_tpm ? k->ops->startup_tpm(s) : 0;
s->had_startup_error = (res != 0);
return res;
}
bool tpm_backend_had_startup_error(TPMBackend *s)
{
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
return k->ops->had_startup_error(s);
}
size_t tpm_backend_realloc_buffer(TPMBackend *s, TPMSizedBuffer *sb)
{
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
return k->ops->realloc_buffer(sb);
return s->had_startup_error;
}
void tpm_backend_deliver_request(TPMBackend *s)
{
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
k->ops->deliver_request(s);
g_thread_pool_push(s->thread_pool, (gpointer)TPM_BACKEND_CMD_PROCESS_CMD,
NULL);
}
void tpm_backend_reset(TPMBackend *s)
{
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
k->ops->reset(s);
if (k->ops->reset) {
k->ops->reset(s);
}
tpm_backend_thread_end(s);
s->had_startup_error = false;
}
void tpm_backend_cancel_cmd(TPMBackend *s)
{
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
assert(k->ops->cancel_cmd);
k->ops->cancel_cmd(s);
}
@ -95,23 +112,40 @@ bool tpm_backend_get_tpm_established_flag(TPMBackend *s)
{
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
return k->ops->get_tpm_established_flag(s);
return k->ops->get_tpm_established_flag ?
k->ops->get_tpm_established_flag(s) : false;
}
int tpm_backend_reset_tpm_established_flag(TPMBackend *s, uint8_t locty)
{
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
return k->ops->reset_tpm_established_flag(s, locty);
return k->ops->reset_tpm_established_flag ?
k->ops->reset_tpm_established_flag(s, locty) : 0;
}
TPMVersion tpm_backend_get_tpm_version(TPMBackend *s)
{
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
assert(k->ops->get_tpm_version);
return k->ops->get_tpm_version(s);
}
TPMInfo *tpm_backend_query_tpm(TPMBackend *s)
{
TPMInfo *info = g_new0(TPMInfo, 1);
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
info->id = g_strdup(s->id);
info->model = s->fe_model;
info->options = k->ops->get_tpm_options ?
k->ops->get_tpm_options(s) : NULL;
return info;
}
static bool tpm_backend_prop_get_opened(Object *obj, Error **errp)
{
TPMBackend *s = TPM_BACKEND(obj);
@ -152,33 +186,21 @@ static void tpm_backend_prop_set_opened(Object *obj, bool value, Error **errp)
static void tpm_backend_instance_init(Object *obj)
{
TPMBackend *s = TPM_BACKEND(obj);
object_property_add_bool(obj, "opened",
tpm_backend_prop_get_opened,
tpm_backend_prop_set_opened,
NULL);
s->fe_model = -1;
}
void tpm_backend_thread_deliver_request(TPMBackendThread *tbt)
static void tpm_backend_instance_finalize(Object *obj)
{
g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_PROCESS_CMD, NULL);
}
TPMBackend *s = TPM_BACKEND(obj);
void tpm_backend_thread_create(TPMBackendThread *tbt,
GFunc func, gpointer user_data)
{
if (!tbt->pool) {
tbt->pool = g_thread_pool_new(func, user_data, 1, TRUE, NULL);
g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_INIT, NULL);
}
}
void tpm_backend_thread_end(TPMBackendThread *tbt)
{
if (tbt->pool) {
g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_END, NULL);
g_thread_pool_free(tbt->pool, FALSE, TRUE);
tbt->pool = NULL;
}
g_free(s->id);
tpm_backend_thread_end(s);
}
static const TypeInfo tpm_backend_info = {
@ -186,6 +208,7 @@ static const TypeInfo tpm_backend_info = {
.parent = TYPE_OBJECT,
.instance_size = sizeof(TPMBackend),
.instance_init = tpm_backend_instance_init,
.instance_finalize = tpm_backend_instance_finalize,
.class_size = sizeof(TPMBackendClass),
.abstract = true,
};

13
configure vendored
View File

@ -3495,6 +3495,12 @@ else
tpm_passthrough=no
fi
# TPM emulator is for all posix systems
if test "$mingw32" != "yes"; then
tpm_emulator=$tpm
else
tpm_emulator=no
fi
##########################################
# attr probe
@ -5412,6 +5418,7 @@ echo "gcov enabled $gcov"
echo "TPM support $tpm"
echo "libssh2 support $libssh2"
echo "TPM passthrough $tpm_passthrough"
echo "TPM emulator $tpm_emulator"
echo "QOM debugging $qom_cast_debug"
echo "Live block migration $live_block_migration"
echo "lzo support $lzo"
@ -6011,12 +6018,16 @@ if test "$live_block_migration" = "yes" ; then
echo "CONFIG_LIVE_BLOCK_MIGRATION=y" >> $config_host_mak
fi
# TPM passthrough support?
if test "$tpm" = "yes"; then
echo 'CONFIG_TPM=$(CONFIG_SOFTMMU)' >> $config_host_mak
# TPM passthrough support?
if test "$tpm_passthrough" = "yes"; then
echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_host_mak
fi
# TPM emulator support?
if test "$tpm_emulator" = "yes"; then
echo "CONFIG_TPM_EMULATOR=y" >> $config_host_mak
fi
fi
echo "TRACE_BACKENDS=$trace_backends" >> $config_host_mak

123
docs/specs/tpm.txt Normal file
View File

@ -0,0 +1,123 @@
QEMU TPM Device
===============
= Guest-side Hardware Interface =
The QEMU TPM emulation implements a TPM TIS hardware interface following the
Trusted Computing Group's specification "TCG PC Client Specific TPM Interface
Specification (TIS)", Specification Version 1.3, 21 March 2013. This
specification, or a later version of it, can be accessed from the following
URL:
https://trustedcomputinggroup.org/pc-client-work-group-pc-client-specific-tpm-interface-specification-tis/
The TIS interface makes a memory mapped IO region in the area 0xfed40000 -
0xfed44fff available to the guest operating system.
QEMU files related to TPM TIS interface:
- hw/tpm/tpm_tis.c
- hw/tpm/tpm_tis.h
= ACPI Interface =
The TPM device is defined with ACPI ID "PNP0C31". QEMU builds a SSDT and passes
it into the guest through the fw_cfg device. The device description contains
the base address of the TIS interface 0xfed40000 and the size of the MMIO area
(0x5000). In case a TPM2 is used by QEMU, a TPM2 ACPI table is also provided.
The device is described to be used in polling mode rather than interrupt mode
primarily because no unused IRQ could be found.
To support measurement logs to be written by the firmware, e.g. SeaBIOS, a TCPA
table is implemented. This table provides a 64kb buffer where the firmware can
write its log into. For TPM 2 only a more recent version of the TPM2 table
provides support for measurements logs and a TCPA table does not need to be
created.
The TCPA and TPM2 ACPI tables follow the Trusted Computing Group specification
"TCG ACPI Specification" Family "1.2" and "2.0", Level 00 Revision 00.37. This
specification, or a later version of it, can be accessed from the following
URL:
https://trustedcomputinggroup.org/tcg-acpi-specification/
QEMU files related to TPM ACPI tables:
- hw/i386/acpi-build.c
- include/hw/acpi/tpm.h
= TPM backend devices =
The TPM implementation is split into two parts, frontend and backend. The
frontend part is the hardware interface, such as the TPM TIS interface
described earlier, and the other part is the TPM backend interface. The backend
interfaces implement the interaction with a TPM device, which may be a physical
or an emulated device. The split between the front- and backend devices allows
a frontend to be connected with any available backend. This enables the TIS
interface to be used with the passthrough backend or the (future) swtpm backend.
QEMU files related to TPM backends:
- backends/tpm.c
- include/sysemu/tpm_backend.h
- include/sysemu/tpm_backend_int.h
== The QEMU TPM passthrough device ==
In case QEMU is run on Linux as the host operating system it is possible to
make the hardware TPM device available to a single QEMU guest. In this case the
user must make sure that no other program is using the device, e.g., /dev/tpm0,
before trying to start QEMU with it.
The passthrough driver uses the host's TPM device for sending TPM commands
and receiving responses from. Besides that it accesses the TPM device's sysfs
entry for support of command cancellation. Since none of the state of a
hardware TPM can be migrated between hosts, virtual machine migration is
disabled when the TPM passthrough driver is used.
Since the host's TPM device will already be initialized by the host's firmware,
certain commands, e.g. TPM_Startup(), sent by the virtual firmware for device
initialization, will fail. In this case the firmware should not use the TPM.
Sharing the device with the host is generally not a recommended usage scenario
for a TPM device. The primary reason for this is that two operating systems can
then access the device's single set of resources, such as platform configuration
registers (PCRs). Applications or kernel security subsystems, such as the
Linux Integrity Measurement Architecture (IMA), are not expecting to share PCRs.
QEMU files related to the TPM passthrough device:
- hw/tpm/tpm_passthrough.c
- hw/tpm/tpm_util.c
- hw/tpm/tpm_util.h
Command line to start QEMU with the TPM passthrough device using the host's
hardware TPM /dev/tpm0:
qemu-system-x86_64 -display sdl -enable-kvm \
-m 1024 -boot d -bios bios-256k.bin -boot menu=on \
-tpmdev passthrough,id=tpm0,path=/dev/tpm0 \
-device tpm-tis,tpmdev=tpm0 test.img
The following commands should result in similar output inside the VM with a
Linux kernel that either has the TPM TIS driver built-in or available as a
module:
#> dmesg | grep -i tpm
[ 0.711310] tpm_tis 00:06: 1.2 TPM (device=id 0x1, rev-id 1)
#> dmesg | grep TCPA
[ 0.000000] ACPI: TCPA 0x0000000003FFD191C 000032 (v02 BOCHS \
BXPCTCPA 0000001 BXPC 00000001)
#> ls -l /dev/tpm*
crw-------. 1 root root 10, 224 Jul 11 10:11 /dev/tpm0
#> find /sys/devices/ | grep pcrs$ | xargs cat
PCR-00: 35 4E 3B CE 23 9F 38 59 ...
...
PCR-23: 00 00 00 00 00 00 00 00 ...

5
hmp.c
View File

@ -1000,6 +1000,7 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)
Error *err = NULL;
unsigned int c = 0;
TPMPassthroughOptions *tpo;
TPMEmulatorOptions *teo;
info_list = qmp_query_tpm(&err);
if (err) {
@ -1029,6 +1030,10 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)
tpo->has_cancel_path ? ",cancel-path=" : "",
tpo->has_cancel_path ? tpo->cancel_path : "");
break;
case TPM_TYPE_OPTIONS_KIND_EMULATOR:
teo = ti->options->u.emulator.data;
monitor_printf(mon, ",chardev=%s", teo->chardev);
break;
case TPM_TYPE_OPTIONS_KIND__MAX:
break;
}

View File

@ -1,2 +1,3 @@
common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o
common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o tpm_util.o
common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o tpm_util.o

587
hw/tpm/tpm_emulator.c Normal file
View File

@ -0,0 +1,587 @@
/*
* Emulator TPM driver
*
* Copyright (c) 2017 Intel Corporation
* Author: Amarnath Valluri <amarnath.valluri@intel.com>
*
* Copyright (c) 2010 - 2013 IBM Corporation
* Authors:
* Stefan Berger <stefanb@us.ibm.com>
*
* Copyright (C) 2011 IAIK, Graz University of Technology
* Author: Andreas Niederl
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>
*
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "qemu/sockets.h"
#include "io/channel-socket.h"
#include "sysemu/tpm_backend.h"
#include "tpm_int.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "tpm_util.h"
#include "tpm_ioctl.h"
#include "migration/blocker.h"
#include "qapi/error.h"
#include "qapi/clone-visitor.h"
#include "chardev/char-fe.h"
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#define DEBUG_TPM 0
#define DPRINTF(fmt, ...) do { \
if (DEBUG_TPM) { \
fprintf(stderr, "tpm-emulator:"fmt"\n", ## __VA_ARGS__); \
} \
} while (0)
#define TYPE_TPM_EMULATOR "tpm-emulator"
#define TPM_EMULATOR(obj) \
OBJECT_CHECK(TPMEmulator, (obj), TYPE_TPM_EMULATOR)
#define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps & (cap)) == (cap))
static const TPMDriverOps tpm_emulator_driver;
/* data structures */
typedef struct TPMEmulator {
TPMBackend parent;
TPMEmulatorOptions *options;
CharBackend ctrl_chr;
QIOChannel *data_ioc;
TPMVersion tpm_version;
ptm_cap caps; /* capabilities of the TPM */
uint8_t cur_locty_number; /* last set locality */
Error *migration_blocker;
} TPMEmulator;
static int tpm_emulator_ctrlcmd(CharBackend *dev, unsigned long cmd, void *msg,
size_t msg_len_in, size_t msg_len_out)
{
uint32_t cmd_no = cpu_to_be32(cmd);
ssize_t n = sizeof(uint32_t) + msg_len_in;
uint8_t *buf = NULL;
buf = g_alloca(n);
memcpy(buf, &cmd_no, sizeof(cmd_no));
memcpy(buf + sizeof(cmd_no), msg, msg_len_in);
n = qemu_chr_fe_write_all(dev, buf, n);
if (n <= 0) {
return -1;
}
if (msg_len_out != 0) {
n = qemu_chr_fe_read_all(dev, msg, msg_len_out);
if (n <= 0) {
return -1;
}
}
return 0;
}
static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu,
const uint8_t *in, uint32_t in_len,
uint8_t *out, uint32_t out_len,
bool *selftest_done,
Error **err)
{
ssize_t ret;
bool is_selftest = false;
const struct tpm_resp_hdr *hdr = NULL;
if (selftest_done) {
*selftest_done = false;
is_selftest = tpm_util_is_selftest(in, in_len);
}
ret = qio_channel_write_all(tpm_emu->data_ioc, (char *)in, in_len, err);
if (ret != 0) {
return -1;
}
ret = qio_channel_read_all(tpm_emu->data_ioc, (char *)out, sizeof(*hdr),
err);
if (ret != 0) {
return -1;
}
hdr = (struct tpm_resp_hdr *)out;
out += sizeof(*hdr);
ret = qio_channel_read_all(tpm_emu->data_ioc, (char *)out,
be32_to_cpu(hdr->len) - sizeof(*hdr) , err);
if (ret != 0) {
return -1;
}
if (is_selftest) {
*selftest_done = (be32_to_cpu(hdr->errcode) == 0);
}
return 0;
}
static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number)
{
ptm_loc loc;
DPRINTF("%s : locality: 0x%x", __func__, locty_number);
if (tpm_emu->cur_locty_number == locty_number) {
return 0;
}
DPRINTF("setting locality : 0x%x", locty_number);
loc.u.req.loc = locty_number;
if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_SET_LOCALITY, &loc,
sizeof(loc), sizeof(loc)) < 0) {
error_report("tpm-emulator: could not set locality : %s",
strerror(errno));
return -1;
}
loc.u.resp.tpm_result = be32_to_cpu(loc.u.resp.tpm_result);
if (loc.u.resp.tpm_result != 0) {
error_report("tpm-emulator: TPM result for set locality : 0x%x",
loc.u.resp.tpm_result);
return -1;
}
tpm_emu->cur_locty_number = locty_number;
return 0;
}
static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd cmd)
{
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
TPMLocality *locty = NULL;
bool selftest_done = false;
Error *err = NULL;
DPRINTF("processing command type %d", cmd);
switch (cmd) {
case TPM_BACKEND_CMD_PROCESS_CMD:
locty = tb->tpm_state->locty_data;
if (tpm_emulator_set_locality(tpm_emu,
tb->tpm_state->locty_number) < 0 ||
tpm_emulator_unix_tx_bufs(tpm_emu, locty->w_buffer.buffer,
locty->w_offset, locty->r_buffer.buffer,
locty->r_buffer.size, &selftest_done,
&err) < 0) {
tpm_util_write_fatal_error_response(locty->r_buffer.buffer,
locty->r_buffer.size);
error_report_err(err);
}
tb->recv_data_callback(tb->tpm_state, tb->tpm_state->locty_number,
selftest_done);
break;
case TPM_BACKEND_CMD_INIT:
case TPM_BACKEND_CMD_END:
case TPM_BACKEND_CMD_TPM_RESET:
/* nothing to do */
break;
}
}
static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu)
{
DPRINTF("%s", __func__);
if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_GET_CAPABILITY,
&tpm_emu->caps, 0, sizeof(tpm_emu->caps)) < 0) {
error_report("tpm-emulator: probing failed : %s", strerror(errno));
return -1;
}
tpm_emu->caps = be64_to_cpu(tpm_emu->caps);
DPRINTF("capabilities : 0x%"PRIx64, tpm_emu->caps);
return 0;
}
static int tpm_emulator_check_caps(TPMEmulator *tpm_emu)
{
ptm_cap caps = 0;
const char *tpm = NULL;
/* check for min. required capabilities */
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;
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;
tpm = "2";
break;
case TPM_VERSION_UNSPEC:
error_report("tpm-emulator: TPM version has not been set");
return -1;
}
if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, caps)) {
error_report("tpm-emulator: TPM does not implement minimum set of "
"required capabilities for TPM %s (0x%x)", tpm, (int)caps);
return -1;
}
return 0;
}
static int tpm_emulator_startup_tpm(TPMBackend *tb)
{
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
ptm_init init;
ptm_res res;
DPRINTF("%s", __func__);
if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_INIT, &init, sizeof(init),
sizeof(init)) < 0) {
error_report("tpm-emulator: could not send INIT: %s",
strerror(errno));
goto err_exit;
}
res = be32_to_cpu(init.u.resp.tpm_result);
if (res) {
error_report("tpm-emulator: TPM result for CMD_INIT: 0x%x", res);
goto err_exit;
}
return 0;
err_exit:
return -1;
}
static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb)
{
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
ptm_est est;
DPRINTF("%s", __func__);
if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_GET_TPMESTABLISHED, &est,
0, sizeof(est)) < 0) {
error_report("tpm-emulator: Could not get the TPM established flag: %s",
strerror(errno));
return false;
}
DPRINTF("established flag: %0x", est.u.resp.bit);
return (est.u.resp.bit != 0);
}
static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb,
uint8_t locty)
{
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
ptm_reset_est reset_est;
ptm_res res;
/* only a TPM 2.0 will support this */
if (tpm_emu->tpm_version != TPM_VERSION_2_0) {
return 0;
}
reset_est.u.req.loc = tpm_emu->cur_locty_number;
if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_RESET_TPMESTABLISHED,
&reset_est, sizeof(reset_est),
sizeof(reset_est)) < 0) {
error_report("tpm-emulator: Could not reset the establishment bit: %s",
strerror(errno));
return -1;
}
res = be32_to_cpu(reset_est.u.resp.tpm_result);
if (res) {
error_report("tpm-emulator: TPM result for rest establixhed flag: 0x%x",
res);
return -1;
}
return 0;
}
static void tpm_emulator_cancel_cmd(TPMBackend *tb)
{
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
ptm_res res;
if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, PTM_CAP_CANCEL_TPM_CMD)) {
DPRINTF("Backend does not support CANCEL_TPM_CMD");
return;
}
if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_CANCEL_TPM_CMD, &res, 0,
sizeof(res)) < 0) {
error_report("tpm-emulator: Could not cancel command: %s",
strerror(errno));
} else if (res != 0) {
error_report("tpm-emulator: Failed to cancel TPM: 0x%x",
be32_to_cpu(res));
}
}
static TPMVersion tpm_emulator_get_tpm_version(TPMBackend *tb)
{
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
return tpm_emu->tpm_version;
}
static int tpm_emulator_block_migration(TPMEmulator *tpm_emu)
{
Error *err = NULL;
error_setg(&tpm_emu->migration_blocker,
"Migration disabled: TPM emulator not yet migratable");
migrate_add_blocker(tpm_emu->migration_blocker, &err);
if (err) {
error_report_err(err);
error_free(tpm_emu->migration_blocker);
tpm_emu->migration_blocker = NULL;
return -1;
}
return 0;
}
static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_emu)
{
ptm_res res;
Error *err = NULL;
int fds[2] = { -1, -1 };
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
error_report("tpm-emulator: Failed to create socketpair");
return -1;
}
qemu_chr_fe_set_msgfds(&tpm_emu->ctrl_chr, fds + 1, 1);
if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_SET_DATAFD, &res, 0,
sizeof(res)) || res != 0) {
error_report("tpm-emulator: Failed to send CMD_SET_DATAFD: %s",
strerror(errno));
goto err_exit;
}
tpm_emu->data_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fds[0], &err));
if (err) {
error_prepend(&err, "tpm-emulator: Failed to create io channel: ");
error_report_err(err);
goto err_exit;
}
closesocket(fds[1]);
return 0;
err_exit:
closesocket(fds[0]);
closesocket(fds[1]);
return -1;
}
static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_emu, QemuOpts *opts)
{
const char *value;
value = qemu_opt_get(opts, "chardev");
if (value) {
Error *err = NULL;
Chardev *dev = qemu_chr_find(value);
if (!dev) {
error_report("tpm-emulator: tpm chardev '%s' not found.", value);
goto err;
}
if (!qemu_chr_fe_init(&tpm_emu->ctrl_chr, dev, &err)) {
error_prepend(&err, "tpm-emulator: No valid chardev found at '%s':",
value);
error_report_err(err);
goto err;
}
tpm_emu->options->chardev = g_strdup(value);
}
if (tpm_emulator_prepare_data_fd(tpm_emu) < 0) {
goto err;
}
/* FIXME: tpm_util_test_tpmdev() accepts only on socket fd, as it also used
* by passthrough driver, which not yet using GIOChannel.
*/
if (tpm_util_test_tpmdev(QIO_CHANNEL_SOCKET(tpm_emu->data_ioc)->fd,
&tpm_emu->tpm_version)) {
error_report("'%s' is not emulating TPM device. Error: %s",
tpm_emu->options->chardev, strerror(errno));
goto err;
}
DPRINTF("TPM Version %s", tpm_emu->tpm_version == TPM_VERSION_1_2 ? "1.2" :
(tpm_emu->tpm_version == TPM_VERSION_2_0 ? "2.0" : "Unspecified"));
if (tpm_emulator_probe_caps(tpm_emu) ||
tpm_emulator_check_caps(tpm_emu)) {
goto err;
}
return tpm_emulator_block_migration(tpm_emu);
err:
DPRINTF("Startup error");
return -1;
}
static TPMBackend *tpm_emulator_create(QemuOpts *opts, const char *id)
{
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;
}
return tb;
err_exit:
object_unref(OBJECT(tb));
return NULL;
}
static TpmTypeOptions *tpm_emulator_get_tpm_options(TPMBackend *tb)
{
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
TpmTypeOptions *options = g_new0(TpmTypeOptions, 1);
options->type = TPM_TYPE_OPTIONS_KIND_EMULATOR;
options->u.emulator.data = QAPI_CLONE(TPMEmulatorOptions, tpm_emu->options);
return options;
}
static const QemuOptDesc tpm_emulator_cmdline_opts[] = {
TPM_STANDARD_CMDLINE_OPTS,
{
.name = "chardev",
.type = QEMU_OPT_STRING,
.help = "Character device to use for out-of-band control messages",
},
{ /* end of list */ },
};
static const TPMDriverOps tpm_emulator_driver = {
.type = TPM_TYPE_EMULATOR,
.opts = tpm_emulator_cmdline_opts,
.desc = "TPM emulator backend driver",
.create = tpm_emulator_create,
.startup_tpm = tpm_emulator_startup_tpm,
.cancel_cmd = tpm_emulator_cancel_cmd,
.get_tpm_established_flag = tpm_emulator_get_tpm_established_flag,
.reset_tpm_established_flag = tpm_emulator_reset_tpm_established_flag,
.get_tpm_version = tpm_emulator_get_tpm_version,
.get_tpm_options = tpm_emulator_get_tpm_options,
};
static void tpm_emulator_inst_init(Object *obj)
{
TPMEmulator *tpm_emu = TPM_EMULATOR(obj);
DPRINTF("%s", __func__);
tpm_emu->options = g_new0(TPMEmulatorOptions, 1);
tpm_emu->cur_locty_number = ~0;
}
/*
* Gracefully shut down the external TPM
*/
static void tpm_emulator_shutdown(TPMEmulator *tpm_emu)
{
ptm_res res;
if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_SHUTDOWN, &res, 0,
sizeof(res)) < 0) {
error_report("tpm-emulator: Could not cleanly shutdown the TPM: %s",
strerror(errno));
} else if (res != 0) {
error_report("tpm-emulator: TPM result for sutdown: 0x%x",
be32_to_cpu(res));
}
}
static void tpm_emulator_inst_finalize(Object *obj)
{
TPMEmulator *tpm_emu = TPM_EMULATOR(obj);
tpm_emulator_shutdown(tpm_emu);
object_unref(OBJECT(tpm_emu->data_ioc));
qemu_chr_fe_deinit(&tpm_emu->ctrl_chr, false);
qapi_free_TPMEmulatorOptions(tpm_emu->options);
if (tpm_emu->migration_blocker) {
migrate_del_blocker(tpm_emu->migration_blocker);
error_free(tpm_emu->migration_blocker);
}
}
static void tpm_emulator_class_init(ObjectClass *klass, void *data)
{
TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);
tbc->ops = &tpm_emulator_driver;
tbc->handle_request = tpm_emulator_handle_request;
}
static const TypeInfo tpm_emulator_info = {
.name = TYPE_TPM_EMULATOR,
.parent = TYPE_TPM_BACKEND,
.instance_size = sizeof(TPMEmulator),
.class_init = tpm_emulator_class_init,
.instance_init = tpm_emulator_inst_init,
.instance_finalize = tpm_emulator_inst_finalize,
};
static void tpm_emulator_register(void)
{
type_register_static(&tpm_emulator_info);
tpm_register_driver(&tpm_emulator_driver);
}
type_init(tpm_emulator_register)

246
hw/tpm/tpm_ioctl.h Normal file
View File

@ -0,0 +1,246 @@
/*
* tpm_ioctl.h
*
* (c) Copyright IBM Corporation 2014, 2015.
*
* This file is licensed under the terms of the 3-clause BSD license
*/
#ifndef _TPM_IOCTL_H_
#define _TPM_IOCTL_H_
#include <stdint.h>
#include <sys/uio.h>
#include <sys/types.h>
#include <sys/ioctl.h>
/*
* Every response from a command involving a TPM command execution must hold
* the ptm_res as the first element.
* ptm_res corresponds to the error code of a command executed by the TPM.
*/
typedef uint32_t ptm_res;
/* PTM_GET_TPMESTABLISHED: get the establishment bit */
struct ptm_est {
union {
struct {
ptm_res tpm_result;
unsigned char bit; /* TPM established bit */
} resp; /* response */
} u;
};
/* PTM_RESET_TPMESTABLISHED: reset establishment bit */
struct ptm_reset_est {
union {
struct {
uint8_t loc; /* locality to use */
} req; /* request */
struct {
ptm_res tpm_result;
} resp; /* response */
} u;
};
/* PTM_INIT */
struct ptm_init {
union {
struct {
uint32_t init_flags; /* see definitions below */
} req; /* request */
struct {
ptm_res tpm_result;
} resp; /* response */
} u;
};
/* above init_flags */
#define PTM_INIT_FLAG_DELETE_VOLATILE (1 << 0)
/* delete volatile state file after reading it */
/* PTM_SET_LOCALITY */
struct ptm_loc {
union {
struct {
uint8_t loc; /* locality to set */
} req; /* request */
struct {
ptm_res tpm_result;
} resp; /* response */
} u;
};
/* PTM_HASH_DATA: hash given data */
struct ptm_hdata {
union {
struct {
uint32_t length;
uint8_t data[4096];
} req; /* request */
struct {
ptm_res tpm_result;
} resp; /* response */
} u;
};
/*
* size of the TPM state blob to transfer; x86_64 can handle 8k,
* ppc64le only ~7k; keep the response below a 4k page size
*/
#define PTM_STATE_BLOB_SIZE (3 * 1024)
/*
* The following is the data structure to get state blobs from the TPM.
* If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple reads
* with this ioctl and with adjusted offset are necessary. All bytes
* must be transferred and the transfer is done once the last byte has been
* returned.
* It is possible to use the read() interface for reading the data; however, the
* first bytes of the state blob will be part of the response to the ioctl(); a
* subsequent read() is only necessary if the total length (totlength) exceeds
* the number of received bytes. seek() is not supported.
*/
struct ptm_getstate {
union {
struct {
uint32_t state_flags; /* may be: PTM_STATE_FLAG_DECRYPTED */
uint32_t type; /* which blob to pull */
uint32_t offset; /* offset from where to read */
} req; /* request */
struct {
ptm_res tpm_result;
uint32_t state_flags; /* may be: PTM_STATE_FLAG_ENCRYPTED */
uint32_t totlength; /* total length that will be transferred */
uint32_t length; /* number of bytes in following buffer */
uint8_t data[PTM_STATE_BLOB_SIZE];
} resp; /* response */
} u;
};
/* TPM state blob types */
#define PTM_BLOB_TYPE_PERMANENT 1
#define PTM_BLOB_TYPE_VOLATILE 2
#define PTM_BLOB_TYPE_SAVESTATE 3
/* state_flags above : */
#define PTM_STATE_FLAG_DECRYPTED 1 /* on input: get decrypted state */
#define PTM_STATE_FLAG_ENCRYPTED 2 /* on output: state is encrypted */
/*
* The following is the data structure to set state blobs in the TPM.
* If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple
* 'writes' using this ioctl are necessary. The last packet is indicated
* by the length being smaller than the PTM_STATE_BLOB_SIZE.
* The very first packet may have a length indicator of '0' enabling
* a write() with all the bytes from a buffer. If the write() interface
* is used, a final ioctl with a non-full buffer must be made to indicate
* that all data were transferred (a write with 0 bytes would not work).
*/
struct ptm_setstate {
union {
struct {
uint32_t state_flags; /* may be PTM_STATE_FLAG_ENCRYPTED */
uint32_t type; /* which blob to set */
uint32_t length; /* length of the data;
use 0 on the first packet to
transfer using write() */
uint8_t data[PTM_STATE_BLOB_SIZE];
} req; /* request */
struct {
ptm_res tpm_result;
} resp; /* response */
} u;
};
/*
* PTM_GET_CONFIG: Data structure to get runtime configuration information
* such as which keys are applied.
*/
struct ptm_getconfig {
union {
struct {
ptm_res tpm_result;
uint32_t flags;
} resp; /* response */
} u;
};
#define PTM_CONFIG_FLAG_FILE_KEY 0x1
#define PTM_CONFIG_FLAG_MIGRATION_KEY 0x2
typedef uint64_t ptm_cap;
typedef struct ptm_est ptm_est;
typedef struct ptm_reset_est ptm_reset_est;
typedef struct ptm_loc ptm_loc;
typedef struct ptm_hdata ptm_hdata;
typedef struct ptm_init ptm_init;
typedef struct ptm_getstate ptm_getstate;
typedef struct ptm_setstate ptm_setstate;
typedef struct ptm_getconfig ptm_getconfig;
/* capability flags returned by PTM_GET_CAPABILITY */
#define PTM_CAP_INIT (1)
#define PTM_CAP_SHUTDOWN (1 << 1)
#define PTM_CAP_GET_TPMESTABLISHED (1 << 2)
#define PTM_CAP_SET_LOCALITY (1 << 3)
#define PTM_CAP_HASHING (1 << 4)
#define PTM_CAP_CANCEL_TPM_CMD (1 << 5)
#define PTM_CAP_STORE_VOLATILE (1 << 6)
#define PTM_CAP_RESET_TPMESTABLISHED (1 << 7)
#define PTM_CAP_GET_STATEBLOB (1 << 8)
#define PTM_CAP_SET_STATEBLOB (1 << 9)
#define PTM_CAP_STOP (1 << 10)
#define PTM_CAP_GET_CONFIG (1 << 11)
#define PTM_CAP_SET_DATAFD (1 << 12)
enum {
PTM_GET_CAPABILITY = _IOR('P', 0, ptm_cap),
PTM_INIT = _IOWR('P', 1, ptm_init),
PTM_SHUTDOWN = _IOR('P', 2, ptm_res),
PTM_GET_TPMESTABLISHED = _IOR('P', 3, ptm_est),
PTM_SET_LOCALITY = _IOWR('P', 4, ptm_loc),
PTM_HASH_START = _IOR('P', 5, ptm_res),
PTM_HASH_DATA = _IOWR('P', 6, ptm_hdata),
PTM_HASH_END = _IOR('P', 7, ptm_res),
PTM_CANCEL_TPM_CMD = _IOR('P', 8, ptm_res),
PTM_STORE_VOLATILE = _IOR('P', 9, ptm_res),
PTM_RESET_TPMESTABLISHED = _IOWR('P', 10, ptm_reset_est),
PTM_GET_STATEBLOB = _IOWR('P', 11, ptm_getstate),
PTM_SET_STATEBLOB = _IOWR('P', 12, ptm_setstate),
PTM_STOP = _IOR('P', 13, ptm_res),
PTM_GET_CONFIG = _IOR('P', 14, ptm_getconfig),
PTM_SET_DATAFD = _IOR('P', 15, ptm_res),
};
/*
* Commands used by the non-CUSE TPMs
*
* All messages container big-endian data.
*
* The return messages only contain the 'resp' part of the unions
* in the data structures above. Besides that the limits in the
* buffers above (ptm_hdata:u.req.data and ptm_get_state:u.resp.data
* and ptm_set_state:u.req.data) are 0xffffffff.
*/
enum {
CMD_GET_CAPABILITY = 1,
CMD_INIT,
CMD_SHUTDOWN,
CMD_GET_TPMESTABLISHED,
CMD_SET_LOCALITY,
CMD_HASH_START,
CMD_HASH_DATA,
CMD_HASH_END,
CMD_CANCEL_TPM_CMD,
CMD_STORE_VOLATILE,
CMD_RESET_TPMESTABLISHED,
CMD_GET_STATEBLOB,
CMD_SET_STATEBLOB,
CMD_STOP,
CMD_GET_CONFIG,
CMD_SET_DATAFD
};
#endif /* _TPM_IOCTL_H */

View File

@ -30,7 +30,7 @@
#include "tpm_int.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "sysemu/tpm_backend_int.h"
#include "qapi/clone-visitor.h"
#include "tpm_tis.h"
#include "tpm_util.h"
@ -46,29 +46,16 @@
#define TPM_PASSTHROUGH(obj) \
OBJECT_CHECK(TPMPassthruState, (obj), TYPE_TPM_PASSTHROUGH)
static const TPMDriverOps tpm_passthrough_driver;
/* data structures */
typedef struct TPMPassthruThreadParams {
TPMState *tpm_state;
TPMRecvDataCB *recv_data_callback;
TPMBackend *tb;
} TPMPassthruThreadParams;
struct TPMPassthruState {
TPMBackend parent;
TPMBackendThread tbt;
TPMPassthruThreadParams tpm_thread_params;
char *tpm_dev;
TPMPassthroughOptions *options;
const char *tpm_dev;
int tpm_fd;
bool tpm_executing;
bool tpm_op_canceled;
int cancel_fd;
bool had_startup_error;
TPMVersion tpm_version;
};
@ -81,27 +68,6 @@ typedef struct TPMPassthruState TPMPassthruState;
static void tpm_passthrough_cancel_cmd(TPMBackend *tb);
static int tpm_passthrough_unix_write(int fd, const uint8_t *buf, uint32_t len)
{
int ret, remain;
remain = len;
while (remain > 0) {
ret = write(fd, buf, remain);
if (ret < 0) {
if (errno != EINTR && errno != EAGAIN) {
return -1;
}
} else if (ret == 0) {
break;
} else {
buf += ret;
remain -= ret;
}
}
return len - remain;
}
static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
{
int ret;
@ -115,45 +81,12 @@ static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
}
return ret;
}
static uint32_t tpm_passthrough_get_size_from_buffer(const uint8_t *buf)
{
struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)buf;
return be32_to_cpu(resp->len);
}
/*
* Write an error message in the given output buffer.
*/
static void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len)
{
if (out_len >= sizeof(struct tpm_resp_hdr)) {
struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)out;
resp->tag = cpu_to_be16(TPM_TAG_RSP_COMMAND);
resp->len = cpu_to_be32(sizeof(struct tpm_resp_hdr));
resp->errcode = cpu_to_be32(TPM_FAIL);
}
}
static bool tpm_passthrough_is_selftest(const uint8_t *in, uint32_t in_len)
{
struct tpm_req_hdr *hdr = (struct tpm_req_hdr *)in;
if (in_len >= sizeof(*hdr)) {
return (be32_to_cpu(hdr->ordinal) == TPM_ORD_ContinueSelfTest);
}
return false;
}
static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
const uint8_t *in, uint32_t in_len,
uint8_t *out, uint32_t out_len,
bool *selftest_done)
{
int ret;
ssize_t ret;
bool is_selftest;
const struct tpm_resp_hdr *hdr;
@ -161,9 +94,9 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
tpm_pt->tpm_executing = true;
*selftest_done = false;
is_selftest = tpm_passthrough_is_selftest(in, in_len);
is_selftest = tpm_util_is_selftest(in, in_len);
ret = tpm_passthrough_unix_write(tpm_pt->tpm_fd, in, in_len);
ret = qemu_write_full(tpm_pt->tpm_fd, (const void *)in, (size_t)in_len);
if (ret != in_len) {
if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) {
error_report("tpm_passthrough: error while transmitting data "
@ -183,7 +116,7 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
strerror(errno), errno);
}
} else if (ret < sizeof(struct tpm_resp_hdr) ||
tpm_passthrough_get_size_from_buffer(out) != ret) {
be32_to_cpu(((struct tpm_resp_hdr *)out)->len) != ret) {
ret = -1;
error_report("tpm_passthrough: received invalid response "
"packet from TPM");
@ -196,7 +129,7 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
err_exit:
if (ret < 0) {
tpm_write_fatal_error_response(out, out_len);
tpm_util_write_fatal_error_response(out, out_len);
}
tpm_pt->tpm_executing = false;
@ -216,12 +149,9 @@ static int tpm_passthrough_unix_transfer(TPMPassthruState *tpm_pt,
selftest_done);
}
static void tpm_passthrough_worker_thread(gpointer data,
gpointer user_data)
static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd cmd)
{
TPMPassthruThreadParams *thr_parms = user_data;
TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(thr_parms->tb);
TPMBackendCmd cmd = (TPMBackendCmd)data;
TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
bool selftest_done = false;
DPRINTF("tpm_passthrough: processing command type %d\n", cmd);
@ -229,12 +159,12 @@ static void tpm_passthrough_worker_thread(gpointer data,
switch (cmd) {
case TPM_BACKEND_CMD_PROCESS_CMD:
tpm_passthrough_unix_transfer(tpm_pt,
thr_parms->tpm_state->locty_data,
tb->tpm_state->locty_data,
&selftest_done);
thr_parms->recv_data_callback(thr_parms->tpm_state,
thr_parms->tpm_state->locty_number,
selftest_done);
tb->recv_data_callback(tb->tpm_state,
tb->tpm_state->locty_number,
selftest_done);
break;
case TPM_BACKEND_CMD_INIT:
case TPM_BACKEND_CMD_END:
@ -244,47 +174,11 @@ static void tpm_passthrough_worker_thread(gpointer data,
}
}
/*
* Start the TPM (thread). If it had been started before, then terminate
* and start it again.
*/
static int tpm_passthrough_startup_tpm(TPMBackend *tb)
{
TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
/* terminate a running TPM */
tpm_backend_thread_end(&tpm_pt->tbt);
tpm_backend_thread_create(&tpm_pt->tbt,
tpm_passthrough_worker_thread,
&tpm_pt->tpm_thread_params);
return 0;
}
static void tpm_passthrough_reset(TPMBackend *tb)
{
TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
DPRINTF("tpm_passthrough: CALL TO TPM_RESET!\n");
tpm_passthrough_cancel_cmd(tb);
tpm_backend_thread_end(&tpm_pt->tbt);
tpm_pt->had_startup_error = false;
}
static int tpm_passthrough_init(TPMBackend *tb, TPMState *s,
TPMRecvDataCB *recv_data_cb)
{
TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
tpm_pt->tpm_thread_params.tpm_state = s;
tpm_pt->tpm_thread_params.recv_data_callback = recv_data_cb;
tpm_pt->tpm_thread_params.tb = tb;
return 0;
}
static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb)
@ -299,31 +193,6 @@ static int tpm_passthrough_reset_tpm_established_flag(TPMBackend *tb,
return 0;
}
static bool tpm_passthrough_get_startup_error(TPMBackend *tb)
{
TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
return tpm_pt->had_startup_error;
}
static size_t tpm_passthrough_realloc_buffer(TPMSizedBuffer *sb)
{
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;
}
return sb->size;
}
static void tpm_passthrough_deliver_request(TPMBackend *tb)
{
TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
tpm_backend_thread_deliver_request(&tpm_pt->tbt);
}
static void tpm_passthrough_cancel_cmd(TPMBackend *tb)
{
TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
@ -351,11 +220,6 @@ static void tpm_passthrough_cancel_cmd(TPMBackend *tb)
}
}
static const char *tpm_passthrough_create_desc(void)
{
return "Passthrough TPM backend driver";
}
static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb)
{
TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
@ -369,15 +233,14 @@ static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb)
* in Documentation/ABI/stable/sysfs-class-tpm.
* From /dev/tpm0 create /sys/class/misc/tpm0/device/cancel
*/
static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb)
static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt)
{
TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
int fd = -1;
char *dev;
char path[PATH_MAX];
if (tb->cancel_path) {
fd = qemu_open(tb->cancel_path, O_WRONLY);
if (tpm_pt->options->cancel_path) {
fd = qemu_open(tpm_pt->options->cancel_path, O_WRONLY);
if (fd < 0) {
error_report("Could not open TPM cancel path : %s",
strerror(errno));
@ -392,7 +255,7 @@ static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb)
dev) < sizeof(path)) {
fd = qemu_open(path, O_WRONLY);
if (fd >= 0) {
tb->cancel_path = g_strdup(path);
tpm_pt->options->cancel_path = g_strdup(path);
} else {
error_report("tpm_passthrough: Could not open TPM cancel "
"path %s : %s", path, strerror(errno));
@ -412,17 +275,18 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
const char *value;
value = qemu_opt_get(opts, "cancel-path");
tb->cancel_path = g_strdup(value);
value = qemu_opt_get(opts, "path");
if (!value) {
value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
if (value) {
tpm_pt->options->cancel_path = g_strdup(value);
tpm_pt->options->has_cancel_path = true;
}
tpm_pt->tpm_dev = g_strdup(value);
tb->path = g_strdup(tpm_pt->tpm_dev);
value = qemu_opt_get(opts, "path");
if (value) {
tpm_pt->options->has_path = true;
tpm_pt->options->path = g_strdup(value);
}
tpm_pt->tpm_dev = value ? value : TPM_PASSTHROUGH_DEFAULT_DEVICE;
tpm_pt->tpm_fd = qemu_open(tpm_pt->tpm_dev, O_RDWR);
if (tpm_pt->tpm_fd < 0) {
error_report("Cannot access TPM device using '%s': %s",
@ -443,10 +307,8 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
tpm_pt->tpm_fd = -1;
err_free_parameters:
g_free(tb->path);
tb->path = NULL;
g_free(tpm_pt->tpm_dev);
qapi_free_TPMPassthroughOptions(tpm_pt->options);
tpm_pt->options = NULL;
tpm_pt->tpm_dev = NULL;
return 1;
@ -459,16 +321,12 @@ static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
tb->id = g_strdup(id);
/* let frontend set the fe_model to proper value */
tb->fe_model = -1;
tb->ops = &tpm_passthrough_driver;
if (tpm_passthrough_handle_device_opts(opts, tb)) {
goto err_exit;
}
tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tb);
tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tpm_pt);
if (tpm_pt->cancel_fd < 0) {
goto err_exit;
}
@ -476,26 +334,20 @@ static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
return tb;
err_exit:
g_free(tb->id);
object_unref(obj);
return NULL;
}
static void tpm_passthrough_destroy(TPMBackend *tb)
static TpmTypeOptions *tpm_passthrough_get_tpm_options(TPMBackend *tb)
{
TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
TpmTypeOptions *options = g_new0(TpmTypeOptions, 1);
tpm_passthrough_cancel_cmd(tb);
options->type = TPM_TYPE_OPTIONS_KIND_PASSTHROUGH;
options->u.passthrough.data = QAPI_CLONE(TPMPassthroughOptions,
TPM_PASSTHROUGH(tb)->options);
tpm_backend_thread_end(&tpm_pt->tbt);
qemu_close(tpm_pt->tpm_fd);
qemu_close(tpm_pt->cancel_fd);
g_free(tb->id);
g_free(tb->path);
g_free(tb->cancel_path);
g_free(tpm_pt->tpm_dev);
return options;
}
static const QemuOptDesc tpm_passthrough_cmdline_opts[] = {
@ -516,27 +368,34 @@ static const QemuOptDesc tpm_passthrough_cmdline_opts[] = {
static const TPMDriverOps tpm_passthrough_driver = {
.type = TPM_TYPE_PASSTHROUGH,
.opts = tpm_passthrough_cmdline_opts,
.desc = tpm_passthrough_create_desc,
.desc = "Passthrough TPM backend driver",
.create = tpm_passthrough_create,
.destroy = tpm_passthrough_destroy,
.init = tpm_passthrough_init,
.startup_tpm = tpm_passthrough_startup_tpm,
.realloc_buffer = tpm_passthrough_realloc_buffer,
.reset = tpm_passthrough_reset,
.had_startup_error = tpm_passthrough_get_startup_error,
.deliver_request = tpm_passthrough_deliver_request,
.cancel_cmd = tpm_passthrough_cancel_cmd,
.get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag,
.reset_tpm_established_flag = tpm_passthrough_reset_tpm_established_flag,
.get_tpm_version = tpm_passthrough_get_tpm_version,
.get_tpm_options = tpm_passthrough_get_tpm_options,
};
static void tpm_passthrough_inst_init(Object *obj)
{
TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(obj);
tpm_pt->options = g_new0(TPMPassthroughOptions, 1);
tpm_pt->tpm_fd = -1;
tpm_pt->cancel_fd = -1;
}
static void tpm_passthrough_inst_finalize(Object *obj)
{
TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(obj);
tpm_passthrough_cancel_cmd(TPM_BACKEND(obj));
qemu_close(tpm_pt->tpm_fd);
qemu_close(tpm_pt->cancel_fd);
qapi_free_TPMPassthroughOptions(tpm_pt->options);
}
static void tpm_passthrough_class_init(ObjectClass *klass, void *data)
@ -544,6 +403,7 @@ static void tpm_passthrough_class_init(ObjectClass *klass, void *data)
TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);
tbc->ops = &tpm_passthrough_driver;
tbc->handle_request = tpm_passthrough_handle_request;
}
static const TypeInfo tpm_passthrough_info = {

View File

@ -963,6 +963,16 @@ static int tpm_tis_do_startup_tpm(TPMState *s)
return tpm_backend_startup_tpm(s->be_driver);
}
static void tpm_tis_realloc_buffer(TPMSizedBuffer *sb)
{
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;
}
}
/*
* Get the TPMVersion of the backend device being used
*/
@ -1010,9 +1020,9 @@ static void tpm_tis_reset(DeviceState *dev)
tis->loc[c].state = TPM_TIS_STATE_IDLE;
tis->loc[c].w_offset = 0;
tpm_backend_realloc_buffer(s->be_driver, &tis->loc[c].w_buffer);
tpm_tis_realloc_buffer(&tis->loc[c].w_buffer);
tis->loc[c].r_offset = 0;
tpm_backend_realloc_buffer(s->be_driver, &tis->loc[c].r_buffer);
tpm_tis_realloc_buffer(&tis->loc[c].r_buffer);
}
tpm_tis_do_startup_tpm(s);

View File

@ -23,6 +23,31 @@
#include "tpm_util.h"
#include "tpm_int.h"
/*
* Write an error message in the given output buffer.
*/
void tpm_util_write_fatal_error_response(uint8_t *out, uint32_t out_len)
{
if (out_len >= sizeof(struct tpm_resp_hdr)) {
struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)out;
resp->tag = cpu_to_be16(TPM_TAG_RSP_COMMAND);
resp->len = cpu_to_be32(sizeof(struct tpm_resp_hdr));
resp->errcode = cpu_to_be32(TPM_FAIL);
}
}
bool tpm_util_is_selftest(const uint8_t *in, uint32_t in_len)
{
struct tpm_req_hdr *hdr = (struct tpm_req_hdr *)in;
if (in_len >= sizeof(*hdr)) {
return (be32_to_cpu(hdr->ordinal) == TPM_ORD_ContinueSelfTest);
}
return false;
}
/*
* A basic test of a TPM device. We expect a well formatted response header
* (error response is fine) within one second.
@ -43,10 +68,10 @@ static int tpm_util_test(int fd,
n = write(fd, request, requestlen);
if (n < 0) {
return errno;
return -errno;
}
if (n != requestlen) {
return EFAULT;
return -EFAULT;
}
FD_ZERO(&readfds);
@ -55,18 +80,18 @@ static int tpm_util_test(int fd,
/* wait for a second */
n = select(fd + 1, &readfds, NULL, NULL, &tv);
if (n != 1) {
return errno;
return -errno;
}
n = read(fd, &buf, sizeof(buf));
if (n < sizeof(struct tpm_resp_hdr)) {
return EFAULT;
return -EFAULT;
}
resp = (struct tpm_resp_hdr *)buf;
/* check the header */
if (be32_to_cpu(resp->len) != n) {
return EBADMSG;
return -EMSGSIZE;
}
*return_tag = be16_to_cpu(resp->tag);

View File

@ -24,6 +24,10 @@
#include "sysemu/tpm_backend.h"
void tpm_util_write_fatal_error_response(uint8_t *out, uint32_t out_len);
bool tpm_util_is_selftest(const uint8_t *in, uint32_t in_len);
int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version);
#endif /* TPM_TPM_UTIL_H */

View File

@ -29,8 +29,32 @@
typedef struct TPMBackendClass TPMBackendClass;
typedef struct TPMBackend TPMBackend;
typedef struct TPMDriverOps TPMDriverOps;
typedef void (TPMRecvDataCB)(TPMState *, uint8_t locty, bool selftest_done);
typedef enum TPMBackendCmd {
TPM_BACKEND_CMD_INIT = 1,
TPM_BACKEND_CMD_PROCESS_CMD,
TPM_BACKEND_CMD_END,
TPM_BACKEND_CMD_TPM_RESET,
} TPMBackendCmd;
struct TPMBackend {
Object parent;
/*< protected >*/
bool opened;
TPMState *tpm_state;
GThreadPool *thread_pool;
TPMRecvDataCB *recv_data_callback;
bool had_startup_error;
/* <public> */
char *id;
enum TpmModel fe_model;
QLIST_ENTRY(TPMBackend) list;
};
struct TPMBackendClass {
ObjectClass parent_class;
@ -38,25 +62,10 @@ struct TPMBackendClass {
const TPMDriverOps *ops;
void (*opened)(TPMBackend *s, Error **errp);
void (*handle_request)(TPMBackend *s, TPMBackendCmd cmd);
};
struct TPMBackend {
Object parent;
/*< protected >*/
bool opened;
char *id;
enum TpmModel fe_model;
char *path;
char *cancel_path;
const TPMDriverOps *ops;
QLIST_ENTRY(TPMBackend) list;
};
typedef void (TPMRecvDataCB)(TPMState *, uint8_t locty, bool selftest_done);
typedef struct TPMSizedBuffer {
uint32_t size;
uint8_t *buffer;
@ -66,21 +75,14 @@ struct TPMDriverOps {
enum TpmType type;
const QemuOptDesc *opts;
/* get a descriptive text of the backend to display to the user */
const char *(*desc)(void);
const char *desc;
TPMBackend *(*create)(QemuOpts *opts, const char *id);
void (*destroy)(TPMBackend *t);
/* initialize the backend */
int (*init)(TPMBackend *t, TPMState *s, TPMRecvDataCB *datacb);
int (*init)(TPMBackend *t);
/* start up the TPM on the backend */
int (*startup_tpm)(TPMBackend *t);
/* returns true if nothing will ever answer TPM requests */
bool (*had_startup_error)(TPMBackend *t);
size_t (*realloc_buffer)(TPMSizedBuffer *sb);
void (*deliver_request)(TPMBackend *t);
void (*reset)(TPMBackend *t);
@ -91,6 +93,8 @@ struct TPMDriverOps {
int (*reset_tpm_established_flag)(TPMBackend *t, uint8_t locty);
TPMVersion (*get_tpm_version)(TPMBackend *t);
TpmTypeOptions *(*get_tpm_options)(TPMBackend *t);
};
@ -102,20 +106,6 @@ struct TPMDriverOps {
*/
enum TpmType tpm_backend_get_type(TPMBackend *s);
/**
* tpm_backend_get_desc:
* @s: the backend
*
* Returns a human readable description of the backend.
*/
const char *tpm_backend_get_desc(TPMBackend *s);
/**
* tpm_backend_destroy:
* @s: the backend to destroy
*/
void tpm_backend_destroy(TPMBackend *s);
/**
* tpm_backend_init:
* @s: the backend to initialized
@ -147,16 +137,6 @@ int tpm_backend_startup_tpm(TPMBackend *s);
*/
bool tpm_backend_had_startup_error(TPMBackend *s);
/**
* tpm_backend_realloc_buffer:
* @s: the backend
* @sb: the TPMSizedBuffer to re-allocated to the size suitable for the
* backend.
*
* This function returns the size of the allocated buffer
*/
size_t tpm_backend_realloc_buffer(TPMBackend *s, TPMSizedBuffer *sb);
/**
* tpm_backend_deliver_request:
* @s: the backend to send the request to
@ -223,6 +203,16 @@ void tpm_backend_open(TPMBackend *s, Error **errp);
*/
TPMVersion tpm_backend_get_tpm_version(TPMBackend *s);
/**
* tpm_backend_query_tpm:
* @s: the backend
*
* Query backend tpm info
*
* Returns newly allocated TPMInfo
*/
TPMInfo *tpm_backend_query_tpm(TPMBackend *s);
TPMBackend *qemu_find_tpm(const char *id);
const TPMDriverOps *tpm_get_backend_driver(const char *type);

View File

@ -1,41 +0,0 @@
/*
* common TPM backend driver functions
*
* Copyright (c) 2012-2013 IBM Corporation
* Authors:
* Stefan Berger <stefanb@us.ibm.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>
*/
#ifndef TPM_BACKEND_INT_H
#define TPM_BACKEND_INT_H
typedef struct TPMBackendThread {
GThreadPool *pool;
} TPMBackendThread;
void tpm_backend_thread_deliver_request(TPMBackendThread *tbt);
void tpm_backend_thread_create(TPMBackendThread *tbt,
GFunc func, gpointer user_data);
void tpm_backend_thread_end(TPMBackendThread *tbt);
typedef enum TPMBackendCmd {
TPM_BACKEND_CMD_INIT = 1,
TPM_BACKEND_CMD_PROCESS_CMD,
TPM_BACKEND_CMD_END,
TPM_BACKEND_CMD_TPM_RESET,
} TPMBackendCmd;
#endif /* TPM_BACKEND_INT_H */

View File

@ -39,10 +39,12 @@
# An enumeration of TPM types
#
# @passthrough: TPM passthrough type
# @emulator: Software Emulator TPM type
# Since: 2.11
#
# Since: 1.5
##
{ 'enum': 'TpmType', 'data': [ 'passthrough' ] }
{ 'enum': 'TpmType', 'data': [ 'passthrough', 'emulator' ] }
##
# @query-tpm-types:
@ -56,7 +58,7 @@
# Example:
#
# -> { "execute": "query-tpm-types" }
# <- { "return": [ "passthrough" ] }
# <- { "return": [ "passthrough", "emulator" ] }
#
##
{ 'command': 'query-tpm-types', 'returns': ['TpmType'] }
@ -76,17 +78,30 @@
{ 'struct': 'TPMPassthroughOptions', 'data': { '*path' : 'str',
'*cancel-path' : 'str'} }
##
# @TPMEmulatorOptions:
#
# Information about the TPM emulator type
#
# @chardev: Name of a unix socket chardev
#
# Since: 2.11
##
{ 'struct': 'TPMEmulatorOptions', 'data': { 'chardev' : 'str' } }
##
# @TpmTypeOptions:
#
# A union referencing different TPM backend types' configuration options
#
# @type: 'passthrough' The configuration options for the TPM passthrough type
# 'emulator' The configuration options for TPM emulator backend type
#
# Since: 1.5
##
{ 'union': 'TpmTypeOptions',
'data': { 'passthrough' : 'TPMPassthroughOptions' } }
'data': { 'passthrough' : 'TPMPassthroughOptions',
'emulator': 'TPMEmulatorOptions' } }
##
# @TPMInfo:

View File

@ -3121,7 +3121,9 @@ DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \
"-tpmdev passthrough,id=id[,path=path][,cancel-path=path]\n"
" use path to provide path to a character device; default is /dev/tpm0\n"
" use cancel-path to provide path to TPM's cancel sysfs entry; if\n"
" not provided it will be searched for in /sys/class/misc/tpm?/device\n",
" not provided it will be searched for in /sys/class/misc/tpm?/device\n"
"-tpmdev emulator,id=id,chardev=dev\n"
" configure the TPM device using chardev backend\n",
QEMU_ARCH_ALL)
STEXI
@ -3130,8 +3132,8 @@ The general form of a TPM device option is:
@item -tpmdev @var{backend} ,id=@var{id} [,@var{options}]
@findex -tpmdev
Backend type must be:
@option{passthrough}.
Backend type must be either one of the following:
@option{passthrough}, @option{emulator}.
The specific backend type will determine the applicable options.
The @code{-tpmdev} option creates the TPM backend and requires a
@ -3181,6 +3183,20 @@ To create a passthrough TPM use the following two options:
Note that the @code{-tpmdev} id is @code{tpm0} and is referenced by
@code{tpmdev=tpm0} in the device option.
@item -tpmdev emulator, id=@var{id}, chardev=@var{dev}
(Linux-host only) Enable access to a TPM emulator using Unix domain socket based
chardev backend.
@option{chardev} specifies the unique ID of a character device backend that provides connection to the software TPM server.
To create a TPM emulator backend device with chardev socket backend:
@example
-chardev socket,id=chrtpm,path=/tmp/swtpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0
@end example
@end table
ETEXI

37
tpm.c
View File

@ -62,7 +62,7 @@ static void tpm_display_backend_drivers(void)
continue;
}
fprintf(stderr, "%12s %s\n",
TpmType_str(i), be_drivers[i]->desc());
TpmType_str(i), be_drivers[i]->desc);
}
fprintf(stderr, "\n");
}
@ -157,7 +157,7 @@ void tpm_cleanup(void)
QLIST_FOREACH_SAFE(drv, &tpm_backends, list, next) {
QLIST_REMOVE(drv, list);
tpm_backend_destroy(drv);
object_unref(OBJECT(drv));
}
}
@ -172,7 +172,6 @@ int tpm_init(void)
return -1;
}
atexit(tpm_cleanup);
return 0;
}
@ -202,36 +201,6 @@ static const TPMDriverOps *tpm_driver_find_by_type(enum TpmType type)
return be_drivers[type];
}
static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv)
{
TPMInfo *res = g_new0(TPMInfo, 1);
TPMPassthroughOptions *tpo;
res->id = g_strdup(drv->id);
res->model = drv->fe_model;
res->options = g_new0(TpmTypeOptions, 1);
switch (drv->ops->type) {
case TPM_TYPE_PASSTHROUGH:
res->options->type = TPM_TYPE_OPTIONS_KIND_PASSTHROUGH;
tpo = g_new0(TPMPassthroughOptions, 1);
res->options->u.passthrough.data = tpo;
if (drv->path) {
tpo->path = g_strdup(drv->path);
tpo->has_path = true;
}
if (drv->cancel_path) {
tpo->cancel_path = g_strdup(drv->cancel_path);
tpo->has_cancel_path = true;
}
break;
case TPM_TYPE__MAX:
break;
}
return res;
}
/*
* Walk the list of active TPM backends and collect information about them
* following the schema description in qapi-schema.json.
@ -246,7 +215,7 @@ TPMInfoList *qmp_query_tpm(Error **errp)
continue;
}
info = g_new0(TPMInfoList, 1);
info->value = qmp_query_tpm_inst(drv);
info->value = tpm_backend_query_tpm(drv);
if (!cur_item) {
head = cur_item = info;

1
vl.c
View File

@ -4905,6 +4905,7 @@ int main(int argc, char **argv, char **envp)
res_free();
/* vhost-user must be cleaned up before chardevs. */
tpm_cleanup();
net_cleanup();
audio_cleanup();
monitor_cleanup();