Merge tpm 2018/01/26 v2

-----BEGIN PGP SIGNATURE-----
 
 iQEcBAABAgAGBQJab54VAAoJEHWtZYAqC0IRZ+IH+QFtVX3R9fVxlSmFtPs7L9+s
 a+WbbVbYf0toiTg1taRoYgyGkryc8Gtw8VJrN2iowM8KFjEx+h2cZ3qoRd15GqP6
 jFAGb0lc6tjOk0O5pDiJU8hErSrIda8biBp/I0QDz3RkXeGrAZ7FrQemj0FXQjEG
 0o+xGstCYKrVfGxrnDysfvyGSDOad0HnBqwc0rerbVjBJe5p8UErP8DSPsNCaj6W
 qbSSgySeMnTeXGOwIXgCW43eTEJG13eBQ/rNJRqrcoIXiBd/txPb+c+E1iBBAmrF
 XZHxS4v8vP+8rVRgBut4sIr2psx1DZvktHRThJDgu+Cyv6h7c6okQ0wxmo0+9bo=
 =k7Fh
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/stefanberger/tags/pull-tpm-2018-01-26-2' into staging

Merge tpm 2018/01/26 v2

# gpg: Signature made Mon 29 Jan 2018 22:20:05 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-2018-01-26-2:
  tpm: add CRB device
  tpm: report backend request error
  tpm: replace GThreadPool with AIO threadpool
  tpm: lookup cancel path under tpm device class
  tpm: fix alignment issues
  tpm: Set the flags of the CMD_INIT command to 0

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2018-01-30 15:20:01 +00:00
commit 6521130b0a
17 changed files with 818 additions and 139 deletions

View File

@ -19,30 +19,40 @@
#include "sysemu/tpm.h"
#include "qemu/thread.h"
#include "qemu/main-loop.h"
#include "block/thread-pool.h"
#include "qemu/error-report.h"
static void tpm_backend_request_completed_bh(void *opaque)
static void tpm_backend_request_completed(void *opaque, int ret)
{
TPMBackend *s = TPM_BACKEND(opaque);
TPMIfClass *tic = TPM_IF_GET_CLASS(s->tpmif);
tic->request_completed(s->tpmif);
tic->request_completed(s->tpmif, ret);
/* no need for atomic, as long the BQL is taken */
s->cmd = NULL;
object_unref(OBJECT(s));
}
static void tpm_backend_worker_thread(gpointer data, gpointer user_data)
static int tpm_backend_worker_thread(gpointer data)
{
TPMBackend *s = TPM_BACKEND(user_data);
TPMBackend *s = TPM_BACKEND(data);
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
Error *err = NULL;
k->handle_request(s, (TPMBackendCmd *)data);
k->handle_request(s, s->cmd, &err);
if (err) {
error_report_err(err);
return -1;
}
qemu_bh_schedule(s->bh);
return 0;
}
static void tpm_backend_thread_end(TPMBackend *s)
void tpm_backend_finish_sync(TPMBackend *s)
{
if (s->thread_pool) {
g_thread_pool_free(s->thread_pool, FALSE, TRUE);
s->thread_pool = NULL;
while (s->cmd) {
aio_poll(qemu_get_aio_context(), true);
}
}
@ -74,10 +84,7 @@ int tpm_backend_startup_tpm(TPMBackend *s, size_t buffersize)
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(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);
tpm_backend_finish_sync(s);
res = k->startup_tpm ? k->startup_tpm(s, buffersize) : 0;
@ -93,7 +100,17 @@ bool tpm_backend_had_startup_error(TPMBackend *s)
void tpm_backend_deliver_request(TPMBackend *s, TPMBackendCmd *cmd)
{
g_thread_pool_push(s->thread_pool, cmd, NULL);
ThreadPool *pool = aio_get_thread_pool(qemu_get_aio_context());
if (s->cmd != NULL) {
error_report("There is a TPM request pending");
return;
}
s->cmd = cmd;
object_ref(OBJECT(s));
thread_pool_submit_aio(pool, tpm_backend_worker_thread, s,
tpm_backend_request_completed, s);
}
void tpm_backend_reset(TPMBackend *s)
@ -104,7 +121,7 @@ void tpm_backend_reset(TPMBackend *s)
k->reset(s);
}
tpm_backend_thread_end(s);
tpm_backend_finish_sync(s);
s->had_startup_error = false;
}
@ -159,28 +176,18 @@ TPMInfo *tpm_backend_query_tpm(TPMBackend *s)
return info;
}
static void tpm_backend_instance_init(Object *obj)
{
TPMBackend *s = TPM_BACKEND(obj);
s->bh = qemu_bh_new(tpm_backend_request_completed_bh, s);
}
static void tpm_backend_instance_finalize(Object *obj)
{
TPMBackend *s = TPM_BACKEND(obj);
object_unref(OBJECT(s->tpmif));
g_free(s->id);
tpm_backend_thread_end(s);
qemu_bh_delete(s->bh);
}
static const TypeInfo tpm_backend_info = {
.name = TYPE_TPM_BACKEND,
.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,

View File

@ -37,6 +37,7 @@ CONFIG_APPLESMC=y
CONFIG_I8259=y
CONFIG_PFLASH_CFI01=y
CONFIG_TPM_TIS=$(CONFIG_TPM)
CONFIG_TPM_CRB=$(CONFIG_TPM)
CONFIG_MC146818RTC=y
CONFIG_PCI_PIIX=y
CONFIG_WDT_IB700=y

View File

@ -37,6 +37,7 @@ CONFIG_APPLESMC=y
CONFIG_I8259=y
CONFIG_PFLASH_CFI01=y
CONFIG_TPM_TIS=$(CONFIG_TPM)
CONFIG_TPM_CRB=$(CONFIG_TPM)
CONFIG_MC146818RTC=y
CONFIG_PCI_PIIX=y
CONFIG_WDT_IB700=y

View File

@ -2224,6 +2224,22 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
aml_append(sb_scope, scope);
}
}
if (TPM_IS_CRB(tpm_find())) {
dev = aml_device("TPM");
aml_append(dev, aml_name_decl("_HID", aml_string("MSFT0101")));
crs = aml_resource_template();
aml_append(crs, aml_memory32_fixed(TPM_CRB_ADDR_BASE,
TPM_CRB_ADDR_SIZE, AML_READ_WRITE));
aml_append(dev, aml_name_decl("_CRS", crs));
method = aml_method("_STA", 0, AML_NOTSERIALIZED);
aml_append(method, aml_return(aml_int(0x0f)));
aml_append(dev, method);
aml_append(sb_scope, dev);
}
aml_append(dsdt, sb_scope);
/* copy AML table into ACPI tables blob and patch header there */
@ -2285,18 +2301,20 @@ build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog)
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 if (TPM_IS_CRB(tpm_find())) {
tpm2_ptr->control_area_address = cpu_to_le64(TPM_CRB_ADDR_CTRL);
tpm2_ptr->start_method = cpu_to_le32(TPM2_START_METHOD_CRB);
} else {
g_warn_if_reached();
}
tpm2_ptr->log_area_minimum_length =
cpu_to_le32(TPM_LOG_AREA_MINIMUM_SIZE);
/* log area start address to be filled by Guest linker */
bios_linker_loader_add_pointer(linker,
ACPI_BUILD_TABLE_FILE, log_addr_offset, log_addr_size,
bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE,
log_addr_offset, log_addr_size,
ACPI_BUILD_TPMLOG_FILE, 0);
} else {
g_warn_if_reached();
}
build_header(linker, table_data,
(void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4, NULL, NULL);
}

View File

@ -1,4 +1,5 @@
common-obj-y += tpm_util.o
common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o
common-obj-$(CONFIG_TPM_CRB) += tpm_crb.o
common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o

303
hw/tpm/tpm_crb.c Normal file
View File

@ -0,0 +1,303 @@
/*
* tpm_crb.c - QEMU's TPM CRB interface emulator
*
* Copyright (c) 2018 Red Hat, Inc.
*
* Authors:
* Marc-André Lureau <marcandre.lureau@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
* tpm_crb is a device for TPM 2.0 Command Response Buffer (CRB) Interface
* as defined in TCG PC Client Platform TPM Profile (PTP) Specification
* Family 2.0 Level 00 Revision 01.03 v22
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qapi/error.h"
#include "exec/address-spaces.h"
#include "hw/qdev-core.h"
#include "hw/qdev-properties.h"
#include "hw/pci/pci_ids.h"
#include "hw/acpi/tpm.h"
#include "migration/vmstate.h"
#include "sysemu/tpm_backend.h"
#include "tpm_int.h"
#include "tpm_util.h"
typedef struct CRBState {
DeviceState parent_obj;
TPMBackend *tpmbe;
TPMBackendCmd cmd;
uint32_t regs[TPM_CRB_R_MAX];
MemoryRegion mmio;
MemoryRegion cmdmem;
size_t be_buffer_size;
} CRBState;
#define CRB(obj) OBJECT_CHECK(CRBState, (obj), TYPE_TPM_CRB)
#define DEBUG_CRB 0
#define DPRINTF(fmt, ...) do { \
if (DEBUG_CRB) { \
printf(fmt, ## __VA_ARGS__); \
} \
} while (0)
#define CRB_INTF_TYPE_CRB_ACTIVE 0b1
#define CRB_INTF_VERSION_CRB 0b1
#define CRB_INTF_CAP_LOCALITY_0_ONLY 0b0
#define CRB_INTF_CAP_IDLE_FAST 0b0
#define CRB_INTF_CAP_XFER_SIZE_64 0b11
#define CRB_INTF_CAP_FIFO_NOT_SUPPORTED 0b0
#define CRB_INTF_CAP_CRB_SUPPORTED 0b1
#define CRB_INTF_IF_SELECTOR_CRB 0b1
#define CRB_CTRL_CMD_SIZE (TPM_CRB_ADDR_SIZE - A_CRB_DATA_BUFFER)
enum crb_loc_ctrl {
CRB_LOC_CTRL_REQUEST_ACCESS = BIT(0),
CRB_LOC_CTRL_RELINQUISH = BIT(1),
CRB_LOC_CTRL_SEIZE = BIT(2),
CRB_LOC_CTRL_RESET_ESTABLISHMENT_BIT = BIT(3),
};
enum crb_ctrl_req {
CRB_CTRL_REQ_CMD_READY = BIT(0),
CRB_CTRL_REQ_GO_IDLE = BIT(1),
};
enum crb_start {
CRB_START_INVOKE = BIT(0),
};
enum crb_cancel {
CRB_CANCEL_INVOKE = BIT(0),
};
static uint64_t tpm_crb_mmio_read(void *opaque, hwaddr addr,
unsigned size)
{
CRBState *s = CRB(opaque);
void *regs = (void *)&s->regs + (addr & ~3);
unsigned offset = addr & 3;
uint32_t val = *(uint32_t *)regs >> (8 * offset);
DPRINTF("CRB read 0x" TARGET_FMT_plx " len:%u val: 0x%" PRIx32 "\n",
addr, size, val);
return val;
}
static void tpm_crb_mmio_write(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
CRBState *s = CRB(opaque);
DPRINTF("CRB write 0x" TARGET_FMT_plx " len:%u val: 0x%" PRIx64 "\n",
addr, size, val);
switch (addr) {
case A_CRB_CTRL_REQ:
switch (val) {
case CRB_CTRL_REQ_CMD_READY:
ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS,
tpmIdle, 0);
break;
case CRB_CTRL_REQ_GO_IDLE:
ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS,
tpmIdle, 1);
break;
}
break;
case A_CRB_CTRL_CANCEL:
if (val == CRB_CANCEL_INVOKE &&
s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE) {
tpm_backend_cancel_cmd(s->tpmbe);
}
break;
case A_CRB_CTRL_START:
if (val == CRB_START_INVOKE &&
!(s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE)) {
void *mem = memory_region_get_ram_ptr(&s->cmdmem);
s->regs[R_CRB_CTRL_START] |= CRB_START_INVOKE;
s->cmd = (TPMBackendCmd) {
.in = mem,
.in_len = MIN(tpm_cmd_get_size(mem), s->be_buffer_size),
.out = mem,
.out_len = s->be_buffer_size,
};
tpm_backend_deliver_request(s->tpmbe, &s->cmd);
}
break;
case A_CRB_LOC_CTRL:
switch (val) {
case CRB_LOC_CTRL_RESET_ESTABLISHMENT_BIT:
/* not loc 3 or 4 */
break;
case CRB_LOC_CTRL_RELINQUISH:
break;
case CRB_LOC_CTRL_REQUEST_ACCESS:
ARRAY_FIELD_DP32(s->regs, CRB_LOC_STS,
Granted, 1);
ARRAY_FIELD_DP32(s->regs, CRB_LOC_STS,
beenSeized, 0);
ARRAY_FIELD_DP32(s->regs, CRB_LOC_STATE,
locAssigned, 1);
ARRAY_FIELD_DP32(s->regs, CRB_LOC_STATE,
tpmRegValidSts, 1);
break;
}
break;
}
}
static const MemoryRegionOps tpm_crb_memory_ops = {
.read = tpm_crb_mmio_read,
.write = tpm_crb_mmio_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 1,
.max_access_size = 4,
},
};
static void tpm_crb_request_completed(TPMIf *ti, int ret)
{
CRBState *s = CRB(ti);
s->regs[R_CRB_CTRL_START] &= ~CRB_START_INVOKE;
if (ret != 0) {
ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS,
tpmSts, 1); /* fatal error */
}
}
static enum TPMVersion tpm_crb_get_version(TPMIf *ti)
{
CRBState *s = CRB(ti);
return tpm_backend_get_tpm_version(s->tpmbe);
}
static int tpm_crb_pre_save(void *opaque)
{
CRBState *s = opaque;
tpm_backend_finish_sync(s->tpmbe);
return 0;
}
static const VMStateDescription vmstate_tpm_crb = {
.name = "tpm-crb",
.pre_save = tpm_crb_pre_save,
.fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(regs, CRBState, TPM_CRB_R_MAX),
VMSTATE_END_OF_LIST(),
}
};
static Property tpm_crb_properties[] = {
DEFINE_PROP_TPMBE("tpmdev", CRBState, tpmbe),
DEFINE_PROP_END_OF_LIST(),
};
static void tpm_crb_realize(DeviceState *dev, Error **errp)
{
CRBState *s = CRB(dev);
if (!tpm_find()) {
error_setg(errp, "at most one TPM device is permitted");
return;
}
if (!s->tpmbe) {
error_setg(errp, "'tpmdev' property is required");
return;
}
memory_region_init_io(&s->mmio, OBJECT(s), &tpm_crb_memory_ops, s,
"tpm-crb-mmio", sizeof(s->regs));
memory_region_init_ram(&s->cmdmem, OBJECT(s),
"tpm-crb-cmd", CRB_CTRL_CMD_SIZE, errp);
memory_region_add_subregion(get_system_memory(),
TPM_CRB_ADDR_BASE, &s->mmio);
memory_region_add_subregion(get_system_memory(),
TPM_CRB_ADDR_BASE + sizeof(s->regs), &s->cmdmem);
tpm_backend_reset(s->tpmbe);
ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
InterfaceType, CRB_INTF_TYPE_CRB_ACTIVE);
ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
InterfaceVersion, CRB_INTF_VERSION_CRB);
ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
CapLocality, CRB_INTF_CAP_LOCALITY_0_ONLY);
ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
CapCRBIdleBypass, CRB_INTF_CAP_IDLE_FAST);
ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
CapDataXferSizeSupport, CRB_INTF_CAP_XFER_SIZE_64);
ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
CapFIFO, CRB_INTF_CAP_FIFO_NOT_SUPPORTED);
ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
CapCRB, CRB_INTF_CAP_CRB_SUPPORTED);
ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
InterfaceSelector, CRB_INTF_IF_SELECTOR_CRB);
ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
RID, 0b0000);
ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID2,
VID, PCI_VENDOR_ID_IBM);
s->regs[R_CRB_CTRL_CMD_SIZE] = CRB_CTRL_CMD_SIZE;
s->regs[R_CRB_CTRL_CMD_LADDR] = TPM_CRB_ADDR_BASE + A_CRB_DATA_BUFFER;
s->regs[R_CRB_CTRL_RSP_SIZE] = CRB_CTRL_CMD_SIZE;
s->regs[R_CRB_CTRL_RSP_ADDR] = TPM_CRB_ADDR_BASE + A_CRB_DATA_BUFFER;
s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->tpmbe),
CRB_CTRL_CMD_SIZE);
tpm_backend_startup_tpm(s->tpmbe, s->be_buffer_size);
}
static void tpm_crb_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
TPMIfClass *tc = TPM_IF_CLASS(klass);
dc->realize = tpm_crb_realize;
dc->props = tpm_crb_properties;
dc->vmsd = &vmstate_tpm_crb;
dc->user_creatable = true;
tc->model = TPM_MODEL_TPM_CRB;
tc->get_version = tpm_crb_get_version;
tc->request_completed = tpm_crb_request_completed;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
}
static const TypeInfo tpm_crb_info = {
.name = TYPE_TPM_CRB,
/* could be TYPE_SYS_BUS_DEVICE (or LPC etc) */
.parent = TYPE_DEVICE,
.instance_size = sizeof(CRBState),
.class_init = tpm_crb_class_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_TPM_IF },
{ }
}
};
static void tpm_crb_register(void)
{
type_register_static(&tpm_crb_info);
}
type_init(tpm_crb_register)

View File

@ -120,7 +120,6 @@ static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu,
{
ssize_t ret;
bool is_selftest = false;
const struct tpm_resp_hdr *hdr = NULL;
if (selftest_done) {
*selftest_done = false;
@ -132,22 +131,21 @@ static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu,
return -1;
}
ret = qio_channel_read_all(tpm_emu->data_ioc, (char *)out, sizeof(*hdr),
err);
ret = qio_channel_read_all(tpm_emu->data_ioc, (char *)out,
sizeof(struct tpm_resp_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);
ret = qio_channel_read_all(tpm_emu->data_ioc,
(char *)out + sizeof(struct tpm_resp_hdr),
tpm_cmd_get_size(out) - sizeof(struct tpm_resp_hdr), err);
if (ret != 0) {
return -1;
}
if (is_selftest) {
*selftest_done = (be32_to_cpu(hdr->errcode) == 0);
*selftest_done = tpm_cmd_get_errcode(out) == 0;
}
return 0;
@ -185,28 +183,19 @@ static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number,
return 0;
}
static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd)
static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd,
Error **errp)
{
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
Error *err = NULL;
DPRINTF("processing TPM command");
if (tpm_emulator_set_locality(tpm_emu, cmd->locty, &err) < 0) {
goto error;
}
if (tpm_emulator_unix_tx_bufs(tpm_emu, cmd->in, cmd->in_len,
if (tpm_emulator_set_locality(tpm_emu, cmd->locty, errp) < 0 ||
tpm_emulator_unix_tx_bufs(tpm_emu, cmd->in, cmd->in_len,
cmd->out, cmd->out_len,
&cmd->selftest_done, &err) < 0) {
goto error;
}
return;
error:
&cmd->selftest_done, errp) < 0) {
tpm_util_write_fatal_error_response(cmd->out, cmd->out_len);
error_report_err(err);
}
}
static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu)
@ -320,7 +309,9 @@ static int tpm_emulator_set_buffer_size(TPMBackend *tb,
static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize)
{
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
ptm_init init;
ptm_init init = {
.u.req.init_flags = 0,
};
ptm_res res;
if (buffersize != 0 &&

View File

@ -80,14 +80,14 @@ static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
}
return ret;
}
static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
static void 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)
bool *selftest_done, Error **errp)
{
ssize_t ret;
bool is_selftest;
const struct tpm_resp_hdr *hdr;
/* FIXME: protect shared variables or use other sync mechanism */
tpm_pt->tpm_op_canceled = false;
@ -99,9 +99,8 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
ret = qemu_write_full(tpm_pt->tpm_fd, in, in_len);
if (ret != in_len) {
if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) {
error_report("tpm_passthrough: error while transmitting data "
"to TPM: %s (%i)",
strerror(errno), errno);
error_setg_errno(errp, errno, "tpm_passthrough: error while "
"transmitting data to TPM");
}
goto err_exit;
}
@ -111,20 +110,18 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len);
if (ret < 0) {
if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) {
error_report("tpm_passthrough: error while reading data from "
"TPM: %s (%i)",
strerror(errno), errno);
error_setg_errno(errp, errno, "tpm_passthrough: error while "
"reading data from TPM");
}
} else if (ret < sizeof(struct tpm_resp_hdr) ||
be32_to_cpu(((struct tpm_resp_hdr *)out)->len) != ret) {
tpm_cmd_get_size(out) != ret) {
ret = -1;
error_report("tpm_passthrough: received invalid response "
"packet from TPM");
error_setg_errno(errp, errno, "tpm_passthrough: received invalid "
"response packet from TPM");
}
if (is_selftest && (ret >= sizeof(struct tpm_resp_hdr))) {
hdr = (struct tpm_resp_hdr *)out;
*selftest_done = (be32_to_cpu(hdr->errcode) == 0);
*selftest_done = tpm_cmd_get_errcode(out) == 0;
}
err_exit:
@ -133,18 +130,18 @@ err_exit:
}
tpm_pt->tpm_executing = false;
return ret;
}
static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd *cmd)
static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd *cmd,
Error **errp)
{
TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
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);
cmd->out, cmd->out_len, &cmd->selftest_done,
errp);
}
static void tpm_passthrough_reset(TPMBackend *tb)
@ -216,7 +213,8 @@ static size_t tpm_passthrough_get_buffer_size(TPMBackend *tb)
* Unless path or file descriptor set has been provided by user,
* determine the sysfs cancel file following kernel documentation
* in Documentation/ABI/stable/sysfs-class-tpm.
* From /dev/tpm0 create /sys/class/misc/tpm0/device/cancel
* From /dev/tpm0 create /sys/class/tpm/tpm0/device/cancel
* before 4.0: /sys/class/misc/tpm0/device/cancel
*/
static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt)
{
@ -227,26 +225,35 @@ static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt)
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",
error_report("tpm_passthrough: Could not open TPM cancel path: %s",
strerror(errno));
}
return fd;
}
dev = strrchr(tpm_pt->tpm_dev, '/');
if (dev) {
if (!dev) {
error_report("tpm_passthrough: Bad TPM device path %s",
tpm_pt->tpm_dev);
return -1;
}
dev++;
if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel",
if (snprintf(path, sizeof(path), "/sys/class/tpm/%s/device/cancel",
dev) < sizeof(path)) {
fd = qemu_open(path, O_WRONLY);
if (fd < 0) {
error_report("tpm_passthrough: Could not open TPM cancel "
"path %s : %s", path, strerror(errno));
if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel",
dev) < sizeof(path)) {
fd = qemu_open(path, O_WRONLY);
}
}
}
if (fd < 0) {
error_report("tpm_passthrough: Could not guess TPM cancel path");
} else {
error_report("tpm_passthrough: Bad TPM device path %s",
tpm_pt->tpm_dev);
tpm_pt->options->cancel_path = g_strdup(path);
}
return fd;

View File

@ -393,7 +393,7 @@ static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty)
/*
* Callback from the TPM to indicate that the response was received.
*/
static void tpm_tis_request_completed(TPMIf *ti)
static void tpm_tis_request_completed(TPMIf *ti, int ret)
{
TPMState *s = TPM(ti);
uint8_t locty = s->cmd.locty;
@ -405,6 +405,7 @@ static void tpm_tis_request_completed(TPMIf *ti)
}
}
/* 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;

View File

@ -106,20 +106,16 @@ const PropertyInfo qdev_prop_tpm = {
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);
stw_be_p(out, TPM_TAG_RSP_COMMAND);
stl_be_p(out + 2, sizeof(struct tpm_resp_hdr));
stl_be_p(out + 6, 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);
if (in_len >= sizeof(struct tpm_req_hdr)) {
return tpm_cmd_get_ordinal(in) == TPM_ORD_ContinueSelfTest;
}
return false;
@ -129,12 +125,11 @@ bool tpm_util_is_selftest(const uint8_t *in, uint32_t in_len)
* Send request to a TPM device. We expect a response within one second.
*/
static int tpm_util_request(int fd,
unsigned char *request,
const void *request,
size_t requestlen,
unsigned char *response,
void *response,
size_t responselen)
{
struct tpm_resp_hdr *resp;
fd_set readfds;
int n;
struct timeval tv = {
@ -164,9 +159,8 @@ static int tpm_util_request(int fd,
return -EFAULT;
}
resp = (struct tpm_resp_hdr *)response;
/* check the header */
if (be32_to_cpu(resp->len) != n) {
if (tpm_cmd_get_size(response) != n) {
return -EMSGSIZE;
}
@ -178,12 +172,11 @@ static int tpm_util_request(int fd,
* (error response is fine).
*/
static int tpm_util_test(int fd,
unsigned char *request,
const void *request,
size_t requestlen,
uint16_t *return_tag)
{
struct tpm_resp_hdr *resp;
unsigned char buf[1024];
char buf[1024];
ssize_t ret;
ret = tpm_util_request(fd, request, requestlen,
@ -192,8 +185,7 @@ static int tpm_util_test(int fd,
return ret;
}
resp = (struct tpm_resp_hdr *)buf;
*return_tag = be16_to_cpu(resp->tag);
*return_tag = tpm_cmd_get_tag(buf);
return 0;
}
@ -228,7 +220,7 @@ int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version)
int ret;
/* Send TPM 2 command */
ret = tpm_util_test(tpm_fd, (unsigned char *)&test_req_tpm2,
ret = tpm_util_test(tpm_fd, &test_req_tpm2,
sizeof(test_req_tpm2), &return_tag);
/* TPM 2 would respond with a tag of TPM2_ST_NO_SESSIONS */
if (!ret && return_tag == TPM2_ST_NO_SESSIONS) {
@ -237,7 +229,7 @@ int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version)
}
/* Send TPM 1.2 command */
ret = tpm_util_test(tpm_fd, (unsigned char *)&test_req,
ret = tpm_util_test(tpm_fd, &test_req,
sizeof(test_req), &return_tag);
if (!ret && return_tag == TPM_TAG_RSP_COMMAND) {
*tpm_version = TPM_VERSION_1_2;
@ -253,7 +245,6 @@ int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version)
int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
size_t *buffersize)
{
unsigned char buf[1024];
int ret;
switch (tpm_version) {
@ -277,26 +268,27 @@ int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
struct tpm_resp_hdr hdr;
uint32_t len;
uint32_t buffersize;
} QEMU_PACKED *tpm_resp = (struct tpm_resp_get_buffer_size *)buf;
} QEMU_PACKED tpm_resp;
ret = tpm_util_request(tpm_fd, (unsigned char *)&tpm_get_buffer_size,
sizeof(tpm_get_buffer_size), buf, sizeof(buf));
ret = tpm_util_request(tpm_fd, &tpm_get_buffer_size,
sizeof(tpm_get_buffer_size),
&tpm_resp, sizeof(tpm_resp));
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)) {
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));
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));
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));
be32_to_cpu(tpm_resp.hdr.errcode));
return -EFAULT;
}
*buffersize = be32_to_cpu(tpm_resp->buffersize);
*buffersize = be32_to_cpu(tpm_resp.buffersize);
break;
}
case TPM_VERSION_2_0: {
@ -324,27 +316,28 @@ int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
uint32_t value1;
uint32_t property2;
uint32_t value2;
} QEMU_PACKED *tpm2_resp = (struct tpm2_resp_get_buffer_size *)buf;
} QEMU_PACKED tpm2_resp;
ret = tpm_util_request(tpm_fd, (unsigned char *)&tpm2_get_buffer_size,
sizeof(tpm2_get_buffer_size), buf, sizeof(buf));
ret = tpm_util_request(tpm_fd, &tpm2_get_buffer_size,
sizeof(tpm2_get_buffer_size),
&tpm2_resp, sizeof(tpm2_resp));
if (ret < 0) {
return ret;
}
if (be32_to_cpu(tpm2_resp->hdr.len) != sizeof(*tpm2_resp) ||
be32_to_cpu(tpm2_resp->count) != 2) {
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));
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);
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));
be32_to_cpu(tpm2_resp.hdr.errcode));
return -EFAULT;
}
*buffersize = MAX(be32_to_cpu(tpm2_resp->value1),
be32_to_cpu(tpm2_resp->value2));
*buffersize = MAX(be32_to_cpu(tpm2_resp.value1),
be32_to_cpu(tpm2_resp.value2));
break;
}
case TPM_VERSION_UNSPEC:

View File

@ -31,9 +31,24 @@ 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 uint16_t tpm_cmd_get_tag(const void *b)
{
return lduw_be_p(b);
}
static inline uint32_t tpm_cmd_get_size(const void *b)
{
return be32_to_cpu(*(const uint32_t *)(b + 2));
return ldl_be_p(b + 2);
}
static inline uint32_t tpm_cmd_get_ordinal(const void *b)
{
return ldl_be_p(b + 6);
}
static inline uint32_t tpm_cmd_get_errcode(const void *b)
{
return ldl_be_p(b + 6);
}
int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,

View File

@ -16,11 +16,61 @@
#ifndef HW_ACPI_TPM_H
#define HW_ACPI_TPM_H
#include "hw/registerfields.h"
#define TPM_TIS_ADDR_BASE 0xFED40000
#define TPM_TIS_ADDR_SIZE 0x5000
#define TPM_TIS_IRQ 5
REG32(CRB_LOC_STATE, 0x00)
FIELD(CRB_LOC_STATE, tpmEstablished, 0, 1)
FIELD(CRB_LOC_STATE, locAssigned, 1, 1)
FIELD(CRB_LOC_STATE, activeLocality, 2, 3)
FIELD(CRB_LOC_STATE, reserved, 5, 2)
FIELD(CRB_LOC_STATE, tpmRegValidSts, 7, 1)
REG32(CRB_LOC_CTRL, 0x08)
REG32(CRB_LOC_STS, 0x0C)
FIELD(CRB_LOC_STS, Granted, 0, 1)
FIELD(CRB_LOC_STS, beenSeized, 1, 1)
REG32(CRB_INTF_ID, 0x30)
FIELD(CRB_INTF_ID, InterfaceType, 0, 4)
FIELD(CRB_INTF_ID, InterfaceVersion, 4, 4)
FIELD(CRB_INTF_ID, CapLocality, 8, 1)
FIELD(CRB_INTF_ID, CapCRBIdleBypass, 9, 1)
FIELD(CRB_INTF_ID, Reserved1, 10, 1)
FIELD(CRB_INTF_ID, CapDataXferSizeSupport, 11, 2)
FIELD(CRB_INTF_ID, CapFIFO, 13, 1)
FIELD(CRB_INTF_ID, CapCRB, 14, 1)
FIELD(CRB_INTF_ID, CapIFRes, 15, 2)
FIELD(CRB_INTF_ID, InterfaceSelector, 17, 2)
FIELD(CRB_INTF_ID, IntfSelLock, 19, 1)
FIELD(CRB_INTF_ID, Reserved2, 20, 4)
FIELD(CRB_INTF_ID, RID, 24, 8)
REG32(CRB_INTF_ID2, 0x34)
FIELD(CRB_INTF_ID2, VID, 0, 16)
FIELD(CRB_INTF_ID2, DID, 16, 16)
REG32(CRB_CTRL_EXT, 0x38)
REG32(CRB_CTRL_REQ, 0x40)
REG32(CRB_CTRL_STS, 0x44)
FIELD(CRB_CTRL_STS, tpmSts, 0, 1)
FIELD(CRB_CTRL_STS, tpmIdle, 1, 1)
REG32(CRB_CTRL_CANCEL, 0x48)
REG32(CRB_CTRL_START, 0x4C)
REG32(CRB_INT_ENABLED, 0x50)
REG32(CRB_INT_STS, 0x54)
REG32(CRB_CTRL_CMD_SIZE, 0x58)
REG32(CRB_CTRL_CMD_LADDR, 0x5C)
REG32(CRB_CTRL_CMD_HADDR, 0x60)
REG32(CRB_CTRL_RSP_SIZE, 0x64)
REG32(CRB_CTRL_RSP_ADDR, 0x68)
REG32(CRB_DATA_BUFFER, 0x80)
#define TPM_CRB_ADDR_BASE 0xFED40000
#define TPM_CRB_ADDR_SIZE 0x1000
#define TPM_CRB_ADDR_CTRL (TPM_CRB_ADDR_BASE + A_CRB_CTRL_REQ)
#define TPM_CRB_R_MAX R_CRB_DATA_BUFFER
#define TPM_LOG_AREA_MINIMUM_SIZE (64 * 1024)
#define TPM_TCPA_ACPI_CLASS_CLIENT 0
@ -30,5 +80,6 @@
#define TPM2_ACPI_CLASS_SERVER 1
#define TPM2_START_METHOD_MMIO 6
#define TPM2_START_METHOD_CRB 7
#endif /* HW_ACPI_TPM_H */

View File

@ -41,14 +41,17 @@ typedef struct TPMIfClass {
InterfaceClass parent_class;
enum TpmModel model;
void (*request_completed)(TPMIf *obj);
void (*request_completed)(TPMIf *obj, int ret);
enum TPMVersion (*get_version)(TPMIf *obj);
} TPMIfClass;
#define TYPE_TPM_TIS "tpm-tis"
#define TYPE_TPM_CRB "tpm-crb"
#define TPM_IS_TIS(chr) \
object_dynamic_cast(OBJECT(chr), TYPE_TPM_TIS)
#define TPM_IS_CRB(chr) \
object_dynamic_cast(OBJECT(chr), TYPE_TPM_CRB)
/* returns NULL unless there is exactly one TPM device */
static inline TPMIf *tpm_find(void)

View File

@ -18,6 +18,7 @@
#include "qapi-types.h"
#include "qemu/option.h"
#include "sysemu/tpm.h"
#include "qapi/error.h"
#define TYPE_TPM_BACKEND "tpm-backend"
#define TPM_BACKEND(obj) \
@ -45,9 +46,8 @@ struct TPMBackend {
/*< protected >*/
TPMIf *tpmif;
bool opened;
GThreadPool *thread_pool;
bool had_startup_error;
QEMUBH *bh;
TPMBackendCmd *cmd;
/* <public> */
char *id;
@ -85,7 +85,7 @@ struct TPMBackendClass {
TpmTypeOptions *(*get_tpm_options)(TPMBackend *t);
void (*handle_request)(TPMBackend *s, TPMBackendCmd *cmd);
void (*handle_request)(TPMBackend *s, TPMBackendCmd *cmd, Error **errp);
};
/**
@ -196,6 +196,15 @@ TPMVersion tpm_backend_get_tpm_version(TPMBackend *s);
*/
size_t tpm_backend_get_buffer_size(TPMBackend *s);
/**
* tpm_backend_finish_sync:
* @s: the backend to call into
*
* Finish the pending command synchronously (this will call aio_poll()
* on qemu main AIOContext until it ends)
*/
void tpm_backend_finish_sync(TPMBackend *s);
/**
* tpm_backend_query_tpm:
* @s: the backend

View File

@ -11,10 +11,11 @@
# An enumeration of TPM models
#
# @tpm-tis: TPM TIS model
# @tpm-crb: TPM CRB model (since 2.12)
#
# Since: 1.5
##
{ 'enum': 'TpmModel', 'data': [ 'tpm-tis' ] }
{ 'enum': 'TpmModel', 'data': [ 'tpm-tis', 'tpm-crb' ] }
##
# @query-tpm-models:
@ -28,7 +29,7 @@
# Example:
#
# -> { "execute": "query-tpm-models" }
# <- { "return": [ "tpm-tis" ] }
# <- { "return": [ "tpm-tis", "tpm-crb" ] }
#
##
{ 'command': 'query-tpm-models', 'returns': ['TpmModel'] }

View File

@ -286,6 +286,7 @@ check-qtest-i386-$(CONFIG_VHOST_USER_NET_TEST_i386) += tests/vhost-user-test$(EX
ifeq ($(CONFIG_VHOST_USER_NET_TEST_i386),)
check-qtest-x86_64-$(CONFIG_VHOST_USER_NET_TEST_x86_64) += tests/vhost-user-test$(EXESUF)
endif
check-qtest-i386-$(CONFIG_TPM) += tests/tpm-crb-test$(EXESUF)
check-qtest-i386-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF)
check-qtest-i386-$(CONFIG_POSIX) += tests/test-filter-mirror$(EXESUF)
check-qtest-i386-$(CONFIG_POSIX) += tests/test-filter-redirector$(EXESUF)
@ -708,6 +709,7 @@ tests/test-crypto-tlssession$(EXESUF): tests/test-crypto-tlssession.o \
tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y)
tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \
tests/io-channel-helpers.o $(test-io-obj-y)
tests/tpm-crb-test$(EXESUF): tests/tpm-crb-test.o $(test-io-obj-y)
tests/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \
tests/io-channel-helpers.o $(test-io-obj-y)
tests/test-io-channel-tls$(EXESUF): tests/test-io-channel-tls.o \

275
tests/tpm-crb-test.c Normal file
View File

@ -0,0 +1,275 @@
/*
* QTest testcase for TPM CRB
*
* Copyright (c) 2018 Red Hat, Inc.
*
* Authors:
* Marc-André Lureau <marcandre.lureau@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include <glib/gstdio.h>
#include "hw/acpi/tpm.h"
#include "hw/tpm/tpm_ioctl.h"
#include "io/channel-socket.h"
#include "libqtest.h"
#include "qapi/error.h"
#define TPM_RC_FAILURE 0x101
#define TPM2_ST_NO_SESSIONS 0x8001
struct tpm_hdr {
uint16_t tag;
uint32_t len;
uint32_t code; /*ordinal/error */
char buffer[];
} QEMU_PACKED;
typedef struct TestState {
CompatGMutex data_mutex;
CompatGCond data_cond;
SocketAddress *addr;
QIOChannel *tpm_ioc;
GThread *emu_tpm_thread;
struct tpm_hdr *tpm_msg;
} TestState;
static void test_wait_cond(TestState *s)
{
gint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
g_mutex_lock(&s->data_mutex);
if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) {
g_assert_not_reached();
}
g_mutex_unlock(&s->data_mutex);
}
static void *emu_tpm_thread(void *data)
{
TestState *s = data;
QIOChannel *ioc = s->tpm_ioc;
s->tpm_msg = g_new(struct tpm_hdr, 1);
while (true) {
int minhlen = sizeof(s->tpm_msg->tag) + sizeof(s->tpm_msg->len);
if (!qio_channel_read(ioc, (char *)s->tpm_msg, minhlen, &error_abort)) {
break;
}
s->tpm_msg->tag = be16_to_cpu(s->tpm_msg->tag);
s->tpm_msg->len = be32_to_cpu(s->tpm_msg->len);
g_assert_cmpint(s->tpm_msg->len, >=, minhlen);
g_assert_cmpint(s->tpm_msg->tag, ==, TPM2_ST_NO_SESSIONS);
s->tpm_msg = g_realloc(s->tpm_msg, s->tpm_msg->len);
qio_channel_read(ioc, (char *)&s->tpm_msg->code,
s->tpm_msg->len - minhlen, &error_abort);
s->tpm_msg->code = be32_to_cpu(s->tpm_msg->code);
/* reply error */
s->tpm_msg->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
s->tpm_msg->len = cpu_to_be32(sizeof(struct tpm_hdr));
s->tpm_msg->code = cpu_to_be32(TPM_RC_FAILURE);
qio_channel_write(ioc, (char *)s->tpm_msg, be32_to_cpu(s->tpm_msg->len),
&error_abort);
}
g_free(s->tpm_msg);
s->tpm_msg = NULL;
object_unref(OBJECT(s->tpm_ioc));
return NULL;
}
static void *emu_ctrl_thread(void *data)
{
TestState *s = data;
QIOChannelSocket *lioc = qio_channel_socket_new();
QIOChannel *ioc;
qio_channel_socket_listen_sync(lioc, s->addr, &error_abort);
g_cond_signal(&s->data_cond);
qio_channel_wait(QIO_CHANNEL(lioc), G_IO_IN);
ioc = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort));
g_assert(ioc);
{
uint32_t cmd = 0;
struct iovec iov = { .iov_base = &cmd, .iov_len = sizeof(cmd) };
int *pfd = NULL;
size_t nfd = 0;
qio_channel_readv_full(ioc, &iov, 1, &pfd, &nfd, &error_abort);
cmd = be32_to_cpu(cmd);
g_assert_cmpint(cmd, ==, CMD_SET_DATAFD);
g_assert_cmpint(nfd, ==, 1);
s->tpm_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(*pfd, &error_abort));
g_free(pfd);
cmd = 0;
qio_channel_write(ioc, (char *)&cmd, sizeof(cmd), &error_abort);
s->emu_tpm_thread = g_thread_new(NULL, emu_tpm_thread, s);
}
while (true) {
uint32_t cmd;
ssize_t ret;
ret = qio_channel_read(ioc, (char *)&cmd, sizeof(cmd), NULL);
if (ret <= 0) {
break;
}
cmd = be32_to_cpu(cmd);
switch (cmd) {
case CMD_GET_CAPABILITY: {
ptm_cap cap = cpu_to_be64(0x3fff);
qio_channel_write(ioc, (char *)&cap, sizeof(cap), &error_abort);
break;
}
case CMD_INIT: {
ptm_init init;
qio_channel_read(ioc, (char *)&init.u.req, sizeof(init.u.req),
&error_abort);
init.u.resp.tpm_result = 0;
qio_channel_write(ioc, (char *)&init.u.resp, sizeof(init.u.resp),
&error_abort);
break;
}
case CMD_SHUTDOWN: {
ptm_res res = 0;
qio_channel_write(ioc, (char *)&res, sizeof(res), &error_abort);
qio_channel_close(s->tpm_ioc, &error_abort);
g_thread_join(s->emu_tpm_thread);
break;
}
case CMD_STOP: {
ptm_res res = 0;
qio_channel_write(ioc, (char *)&res, sizeof(res), &error_abort);
break;
}
case CMD_SET_BUFFERSIZE: {
ptm_setbuffersize sbs;
qio_channel_read(ioc, (char *)&sbs.u.req, sizeof(sbs.u.req),
&error_abort);
sbs.u.resp.buffersize = sbs.u.req.buffersize ?: cpu_to_be32(4096);
sbs.u.resp.tpm_result = 0;
sbs.u.resp.minsize = cpu_to_be32(128);
sbs.u.resp.maxsize = cpu_to_be32(4096);
qio_channel_write(ioc, (char *)&sbs.u.resp, sizeof(sbs.u.resp),
&error_abort);
break;
}
case CMD_SET_LOCALITY: {
ptm_loc loc;
/* Note: this time it's not u.req / u.resp... */
qio_channel_read(ioc, (char *)&loc, sizeof(loc), &error_abort);
g_assert_cmpint(loc.u.req.loc, ==, 0);
loc.u.resp.tpm_result = 0;
qio_channel_write(ioc, (char *)&loc, sizeof(loc), &error_abort);
break;
}
default:
g_debug("unimplemented %u", cmd);
g_assert_not_reached();
}
}
object_unref(OBJECT(ioc));
object_unref(OBJECT(lioc));
return NULL;
}
#define TPM_CMD "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00"
static void tpm_crb_test(const void *data)
{
const TestState *s = data;
uint32_t intfid = readl(TPM_CRB_ADDR_BASE + A_CRB_INTF_ID);
uint32_t csize = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_SIZE);
uint64_t caddr = readq(TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR);
uint32_t rsize = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_SIZE);
uint64_t raddr = readq(TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR);
g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, InterfaceType), ==, 1);
g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, InterfaceVersion), ==, 1);
g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapLocality), ==, 0);
g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapCRBIdleBypass), ==, 0);
g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapDataXferSizeSupport),
==, 3);
g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapFIFO), ==, 0);
g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapCRB), ==, 1);
g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, InterfaceSelector), ==, 1);
g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, RID), ==, 0);
g_assert_cmpint(csize, >=, 128);
g_assert_cmpint(rsize, >=, 128);
g_assert_cmpint(caddr, >, TPM_CRB_ADDR_BASE);
g_assert_cmpint(raddr, >, TPM_CRB_ADDR_BASE);
memwrite(caddr, TPM_CMD, sizeof(TPM_CMD));
uint32_t sts, start = 1;
uint64_t end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
writel(TPM_CRB_ADDR_BASE + A_CRB_CTRL_START, start);
do {
start = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_START);
if ((start & 1) == 0) {
break;
}
} while (g_get_monotonic_time() < end_time);
start = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_START);
g_assert_cmpint(start & 1, ==, 0);
sts = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS);
g_assert_cmpint(sts & 1, ==, 0);
struct tpm_hdr tpm_msg;
memread(raddr, &tpm_msg, sizeof(tpm_msg));
g_assert_cmpmem(&tpm_msg, sizeof(tpm_msg), s->tpm_msg, sizeof(*s->tpm_msg));
}
int main(int argc, char **argv)
{
int ret;
char *args, *tmp_path = g_dir_make_tmp("qemu-tpm-crb-test.XXXXXX", NULL);
GThread *thread;
TestState test;
module_call_init(MODULE_INIT_QOM);
g_test_init(&argc, &argv, NULL);
test.addr = g_new0(SocketAddress, 1);
test.addr->type = SOCKET_ADDRESS_TYPE_UNIX;
test.addr->u.q_unix.path = g_build_filename(tmp_path, "sock", NULL);
g_mutex_init(&test.data_mutex);
g_cond_init(&test.data_cond);
thread = g_thread_new(NULL, emu_ctrl_thread, &test);
test_wait_cond(&test);
args = g_strdup_printf(
"-chardev socket,id=chr,path=%s "
"-tpmdev emulator,id=dev,chardev=chr "
"-device tpm-crb,tpmdev=dev",
test.addr->u.q_unix.path);
qtest_start(args);
qtest_add_data_func("/tpm-crb/test", &test, tpm_crb_test);
ret = g_test_run();
qtest_end();
g_thread_join(thread);
g_unlink(test.addr->u.q_unix.path);
qapi_free_SocketAddress(test.addr);
g_rmdir(tmp_path);
g_free(tmp_path);
g_free(args);
return ret;
}