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:
commit
6521130b0a
@ -19,30 +19,40 @@
|
|||||||
#include "sysemu/tpm.h"
|
#include "sysemu/tpm.h"
|
||||||
#include "qemu/thread.h"
|
#include "qemu/thread.h"
|
||||||
#include "qemu/main-loop.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);
|
TPMBackend *s = TPM_BACKEND(opaque);
|
||||||
TPMIfClass *tic = TPM_IF_GET_CLASS(s->tpmif);
|
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);
|
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) {
|
||||||
qemu_bh_schedule(s->bh);
|
error_report_err(err);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tpm_backend_thread_end(TPMBackend *s)
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tpm_backend_finish_sync(TPMBackend *s)
|
||||||
{
|
{
|
||||||
if (s->thread_pool) {
|
while (s->cmd) {
|
||||||
g_thread_pool_free(s->thread_pool, FALSE, TRUE);
|
aio_poll(qemu_get_aio_context(), true);
|
||||||
s->thread_pool = NULL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,10 +84,7 @@ int tpm_backend_startup_tpm(TPMBackend *s, size_t buffersize)
|
|||||||
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
|
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
|
||||||
|
|
||||||
/* terminate a running TPM */
|
/* terminate a running TPM */
|
||||||
tpm_backend_thread_end(s);
|
tpm_backend_finish_sync(s);
|
||||||
|
|
||||||
s->thread_pool = g_thread_pool_new(tpm_backend_worker_thread, s, 1, TRUE,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
res = k->startup_tpm ? k->startup_tpm(s, buffersize) : 0;
|
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)
|
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)
|
void tpm_backend_reset(TPMBackend *s)
|
||||||
@ -104,7 +121,7 @@ void tpm_backend_reset(TPMBackend *s)
|
|||||||
k->reset(s);
|
k->reset(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
tpm_backend_thread_end(s);
|
tpm_backend_finish_sync(s);
|
||||||
|
|
||||||
s->had_startup_error = false;
|
s->had_startup_error = false;
|
||||||
}
|
}
|
||||||
@ -159,28 +176,18 @@ TPMInfo *tpm_backend_query_tpm(TPMBackend *s)
|
|||||||
return info;
|
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)
|
static void tpm_backend_instance_finalize(Object *obj)
|
||||||
{
|
{
|
||||||
TPMBackend *s = TPM_BACKEND(obj);
|
TPMBackend *s = TPM_BACKEND(obj);
|
||||||
|
|
||||||
object_unref(OBJECT(s->tpmif));
|
object_unref(OBJECT(s->tpmif));
|
||||||
g_free(s->id);
|
g_free(s->id);
|
||||||
tpm_backend_thread_end(s);
|
|
||||||
qemu_bh_delete(s->bh);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo tpm_backend_info = {
|
static const TypeInfo tpm_backend_info = {
|
||||||
.name = TYPE_TPM_BACKEND,
|
.name = TYPE_TPM_BACKEND,
|
||||||
.parent = TYPE_OBJECT,
|
.parent = TYPE_OBJECT,
|
||||||
.instance_size = sizeof(TPMBackend),
|
.instance_size = sizeof(TPMBackend),
|
||||||
.instance_init = tpm_backend_instance_init,
|
|
||||||
.instance_finalize = tpm_backend_instance_finalize,
|
.instance_finalize = tpm_backend_instance_finalize,
|
||||||
.class_size = sizeof(TPMBackendClass),
|
.class_size = sizeof(TPMBackendClass),
|
||||||
.abstract = true,
|
.abstract = true,
|
||||||
|
@ -37,6 +37,7 @@ CONFIG_APPLESMC=y
|
|||||||
CONFIG_I8259=y
|
CONFIG_I8259=y
|
||||||
CONFIG_PFLASH_CFI01=y
|
CONFIG_PFLASH_CFI01=y
|
||||||
CONFIG_TPM_TIS=$(CONFIG_TPM)
|
CONFIG_TPM_TIS=$(CONFIG_TPM)
|
||||||
|
CONFIG_TPM_CRB=$(CONFIG_TPM)
|
||||||
CONFIG_MC146818RTC=y
|
CONFIG_MC146818RTC=y
|
||||||
CONFIG_PCI_PIIX=y
|
CONFIG_PCI_PIIX=y
|
||||||
CONFIG_WDT_IB700=y
|
CONFIG_WDT_IB700=y
|
||||||
|
@ -37,6 +37,7 @@ CONFIG_APPLESMC=y
|
|||||||
CONFIG_I8259=y
|
CONFIG_I8259=y
|
||||||
CONFIG_PFLASH_CFI01=y
|
CONFIG_PFLASH_CFI01=y
|
||||||
CONFIG_TPM_TIS=$(CONFIG_TPM)
|
CONFIG_TPM_TIS=$(CONFIG_TPM)
|
||||||
|
CONFIG_TPM_CRB=$(CONFIG_TPM)
|
||||||
CONFIG_MC146818RTC=y
|
CONFIG_MC146818RTC=y
|
||||||
CONFIG_PCI_PIIX=y
|
CONFIG_PCI_PIIX=y
|
||||||
CONFIG_WDT_IB700=y
|
CONFIG_WDT_IB700=y
|
||||||
|
@ -2224,6 +2224,22 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
|
|||||||
aml_append(sb_scope, scope);
|
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);
|
aml_append(dsdt, sb_scope);
|
||||||
|
|
||||||
/* copy AML table into ACPI tables blob and patch header there */
|
/* 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())) {
|
if (TPM_IS_TIS(tpm_find())) {
|
||||||
tpm2_ptr->control_area_address = cpu_to_le64(0);
|
tpm2_ptr->control_area_address = cpu_to_le64(0);
|
||||||
tpm2_ptr->start_method = cpu_to_le32(TPM2_START_METHOD_MMIO);
|
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 =
|
tpm2_ptr->log_area_minimum_length =
|
||||||
cpu_to_le32(TPM_LOG_AREA_MINIMUM_SIZE);
|
cpu_to_le32(TPM_LOG_AREA_MINIMUM_SIZE);
|
||||||
|
|
||||||
/* log area start address to be filled by Guest linker */
|
/* log area start address to be filled by Guest linker */
|
||||||
bios_linker_loader_add_pointer(linker,
|
bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE,
|
||||||
ACPI_BUILD_TABLE_FILE, log_addr_offset, log_addr_size,
|
log_addr_offset, log_addr_size,
|
||||||
ACPI_BUILD_TPMLOG_FILE, 0);
|
ACPI_BUILD_TPMLOG_FILE, 0);
|
||||||
} else {
|
|
||||||
g_warn_if_reached();
|
|
||||||
}
|
|
||||||
|
|
||||||
build_header(linker, table_data,
|
build_header(linker, table_data,
|
||||||
(void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4, NULL, NULL);
|
(void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
common-obj-y += tpm_util.o
|
common-obj-y += tpm_util.o
|
||||||
common-obj-$(CONFIG_TPM_TIS) += tpm_tis.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_PASSTHROUGH) += tpm_passthrough.o
|
||||||
common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o
|
common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o
|
||||||
|
303
hw/tpm/tpm_crb.c
Normal file
303
hw/tpm/tpm_crb.c
Normal 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)
|
@ -120,7 +120,6 @@ static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu,
|
|||||||
{
|
{
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
bool is_selftest = false;
|
bool is_selftest = false;
|
||||||
const struct tpm_resp_hdr *hdr = NULL;
|
|
||||||
|
|
||||||
if (selftest_done) {
|
if (selftest_done) {
|
||||||
*selftest_done = false;
|
*selftest_done = false;
|
||||||
@ -132,22 +131,21 @@ static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = qio_channel_read_all(tpm_emu->data_ioc, (char *)out, sizeof(*hdr),
|
ret = qio_channel_read_all(tpm_emu->data_ioc, (char *)out,
|
||||||
err);
|
sizeof(struct tpm_resp_hdr), err);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
hdr = (struct tpm_resp_hdr *)out;
|
ret = qio_channel_read_all(tpm_emu->data_ioc,
|
||||||
out += sizeof(*hdr);
|
(char *)out + sizeof(struct tpm_resp_hdr),
|
||||||
ret = qio_channel_read_all(tpm_emu->data_ioc, (char *)out,
|
tpm_cmd_get_size(out) - sizeof(struct tpm_resp_hdr), err);
|
||||||
be32_to_cpu(hdr->len) - sizeof(*hdr) , err);
|
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_selftest) {
|
if (is_selftest) {
|
||||||
*selftest_done = (be32_to_cpu(hdr->errcode) == 0);
|
*selftest_done = tpm_cmd_get_errcode(out) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -185,28 +183,19 @@ static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number,
|
|||||||
return 0;
|
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);
|
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
|
||||||
Error *err = NULL;
|
|
||||||
|
|
||||||
DPRINTF("processing TPM command");
|
DPRINTF("processing TPM command");
|
||||||
|
|
||||||
if (tpm_emulator_set_locality(tpm_emu, cmd->locty, &err) < 0) {
|
if (tpm_emulator_set_locality(tpm_emu, cmd->locty, errp) < 0 ||
|
||||||
goto error;
|
tpm_emulator_unix_tx_bufs(tpm_emu, cmd->in, cmd->in_len,
|
||||||
}
|
|
||||||
|
|
||||||
if (tpm_emulator_unix_tx_bufs(tpm_emu, cmd->in, cmd->in_len,
|
|
||||||
cmd->out, cmd->out_len,
|
cmd->out, cmd->out_len,
|
||||||
&cmd->selftest_done, &err) < 0) {
|
&cmd->selftest_done, errp) < 0) {
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
error:
|
|
||||||
tpm_util_write_fatal_error_response(cmd->out, cmd->out_len);
|
tpm_util_write_fatal_error_response(cmd->out, cmd->out_len);
|
||||||
error_report_err(err);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu)
|
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)
|
static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize)
|
||||||
{
|
{
|
||||||
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
|
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
|
||||||
ptm_init init;
|
ptm_init init = {
|
||||||
|
.u.req.init_flags = 0,
|
||||||
|
};
|
||||||
ptm_res res;
|
ptm_res res;
|
||||||
|
|
||||||
if (buffersize != 0 &&
|
if (buffersize != 0 &&
|
||||||
|
@ -80,14 +80,14 @@ static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
|
|||||||
}
|
}
|
||||||
return ret;
|
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,
|
const uint8_t *in, uint32_t in_len,
|
||||||
uint8_t *out, uint32_t out_len,
|
uint8_t *out, uint32_t out_len,
|
||||||
bool *selftest_done)
|
bool *selftest_done, Error **errp)
|
||||||
{
|
{
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
bool is_selftest;
|
bool is_selftest;
|
||||||
const struct tpm_resp_hdr *hdr;
|
|
||||||
|
|
||||||
/* FIXME: protect shared variables or use other sync mechanism */
|
/* FIXME: protect shared variables or use other sync mechanism */
|
||||||
tpm_pt->tpm_op_canceled = false;
|
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);
|
ret = qemu_write_full(tpm_pt->tpm_fd, in, in_len);
|
||||||
if (ret != in_len) {
|
if (ret != in_len) {
|
||||||
if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) {
|
if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) {
|
||||||
error_report("tpm_passthrough: error while transmitting data "
|
error_setg_errno(errp, errno, "tpm_passthrough: error while "
|
||||||
"to TPM: %s (%i)",
|
"transmitting data to TPM");
|
||||||
strerror(errno), errno);
|
|
||||||
}
|
}
|
||||||
goto err_exit;
|
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);
|
ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) {
|
if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) {
|
||||||
error_report("tpm_passthrough: error while reading data from "
|
error_setg_errno(errp, errno, "tpm_passthrough: error while "
|
||||||
"TPM: %s (%i)",
|
"reading data from TPM");
|
||||||
strerror(errno), errno);
|
|
||||||
}
|
}
|
||||||
} else if (ret < sizeof(struct tpm_resp_hdr) ||
|
} 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;
|
ret = -1;
|
||||||
error_report("tpm_passthrough: received invalid response "
|
error_setg_errno(errp, errno, "tpm_passthrough: received invalid "
|
||||||
"packet from TPM");
|
"response packet from TPM");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_selftest && (ret >= sizeof(struct tpm_resp_hdr))) {
|
if (is_selftest && (ret >= sizeof(struct tpm_resp_hdr))) {
|
||||||
hdr = (struct tpm_resp_hdr *)out;
|
*selftest_done = tpm_cmd_get_errcode(out) == 0;
|
||||||
*selftest_done = (be32_to_cpu(hdr->errcode) == 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err_exit:
|
err_exit:
|
||||||
@ -133,18 +130,18 @@ err_exit:
|
|||||||
}
|
}
|
||||||
|
|
||||||
tpm_pt->tpm_executing = false;
|
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);
|
TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
|
||||||
|
|
||||||
DPRINTF("tpm_passthrough: processing command %p\n", cmd);
|
DPRINTF("tpm_passthrough: processing command %p\n", cmd);
|
||||||
|
|
||||||
tpm_passthrough_unix_tx_bufs(tpm_pt, cmd->in, cmd->in_len,
|
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)
|
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,
|
* Unless path or file descriptor set has been provided by user,
|
||||||
* determine the sysfs cancel file following kernel documentation
|
* determine the sysfs cancel file following kernel documentation
|
||||||
* in Documentation/ABI/stable/sysfs-class-tpm.
|
* 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)
|
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) {
|
if (tpm_pt->options->cancel_path) {
|
||||||
fd = qemu_open(tpm_pt->options->cancel_path, O_WRONLY);
|
fd = qemu_open(tpm_pt->options->cancel_path, O_WRONLY);
|
||||||
if (fd < 0) {
|
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));
|
strerror(errno));
|
||||||
}
|
}
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev = strrchr(tpm_pt->tpm_dev, '/');
|
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++;
|
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)) {
|
dev) < sizeof(path)) {
|
||||||
fd = qemu_open(path, O_WRONLY);
|
fd = qemu_open(path, O_WRONLY);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
error_report("tpm_passthrough: Could not open TPM cancel "
|
if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel",
|
||||||
"path %s : %s", path, strerror(errno));
|
dev) < sizeof(path)) {
|
||||||
|
fd = qemu_open(path, O_WRONLY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fd < 0) {
|
||||||
|
error_report("tpm_passthrough: Could not guess TPM cancel path");
|
||||||
} else {
|
} else {
|
||||||
error_report("tpm_passthrough: Bad TPM device path %s",
|
tpm_pt->options->cancel_path = g_strdup(path);
|
||||||
tpm_pt->tpm_dev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return fd;
|
return fd;
|
||||||
|
@ -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.
|
* 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);
|
TPMState *s = TPM(ti);
|
||||||
uint8_t locty = s->cmd.locty;
|
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_set(&s->loc[locty],
|
||||||
TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE);
|
TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE);
|
||||||
s->loc[locty].state = TPM_TIS_STATE_COMPLETION;
|
s->loc[locty].state = TPM_TIS_STATE_COMPLETION;
|
||||||
|
@ -106,20 +106,16 @@ const PropertyInfo qdev_prop_tpm = {
|
|||||||
void tpm_util_write_fatal_error_response(uint8_t *out, uint32_t out_len)
|
void tpm_util_write_fatal_error_response(uint8_t *out, uint32_t out_len)
|
||||||
{
|
{
|
||||||
if (out_len >= sizeof(struct tpm_resp_hdr)) {
|
if (out_len >= sizeof(struct tpm_resp_hdr)) {
|
||||||
struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)out;
|
stw_be_p(out, TPM_TAG_RSP_COMMAND);
|
||||||
|
stl_be_p(out + 2, sizeof(struct tpm_resp_hdr));
|
||||||
resp->tag = cpu_to_be16(TPM_TAG_RSP_COMMAND);
|
stl_be_p(out + 6, TPM_FAIL);
|
||||||
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)
|
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(struct tpm_req_hdr)) {
|
||||||
|
return tpm_cmd_get_ordinal(in) == TPM_ORD_ContinueSelfTest;
|
||||||
if (in_len >= sizeof(*hdr)) {
|
|
||||||
return (be32_to_cpu(hdr->ordinal) == TPM_ORD_ContinueSelfTest);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
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.
|
* Send request to a TPM device. We expect a response within one second.
|
||||||
*/
|
*/
|
||||||
static int tpm_util_request(int fd,
|
static int tpm_util_request(int fd,
|
||||||
unsigned char *request,
|
const void *request,
|
||||||
size_t requestlen,
|
size_t requestlen,
|
||||||
unsigned char *response,
|
void *response,
|
||||||
size_t responselen)
|
size_t responselen)
|
||||||
{
|
{
|
||||||
struct tpm_resp_hdr *resp;
|
|
||||||
fd_set readfds;
|
fd_set readfds;
|
||||||
int n;
|
int n;
|
||||||
struct timeval tv = {
|
struct timeval tv = {
|
||||||
@ -164,9 +159,8 @@ static int tpm_util_request(int fd,
|
|||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
resp = (struct tpm_resp_hdr *)response;
|
|
||||||
/* check the header */
|
/* check the header */
|
||||||
if (be32_to_cpu(resp->len) != n) {
|
if (tpm_cmd_get_size(response) != n) {
|
||||||
return -EMSGSIZE;
|
return -EMSGSIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,12 +172,11 @@ static int tpm_util_request(int fd,
|
|||||||
* (error response is fine).
|
* (error response is fine).
|
||||||
*/
|
*/
|
||||||
static int tpm_util_test(int fd,
|
static int tpm_util_test(int fd,
|
||||||
unsigned char *request,
|
const void *request,
|
||||||
size_t requestlen,
|
size_t requestlen,
|
||||||
uint16_t *return_tag)
|
uint16_t *return_tag)
|
||||||
{
|
{
|
||||||
struct tpm_resp_hdr *resp;
|
char buf[1024];
|
||||||
unsigned char buf[1024];
|
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
|
|
||||||
ret = tpm_util_request(fd, request, requestlen,
|
ret = tpm_util_request(fd, request, requestlen,
|
||||||
@ -192,8 +185,7 @@ static int tpm_util_test(int fd,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
resp = (struct tpm_resp_hdr *)buf;
|
*return_tag = tpm_cmd_get_tag(buf);
|
||||||
*return_tag = be16_to_cpu(resp->tag);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -228,7 +220,7 @@ int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version)
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Send TPM 2 command */
|
/* 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);
|
sizeof(test_req_tpm2), &return_tag);
|
||||||
/* TPM 2 would respond with a tag of TPM2_ST_NO_SESSIONS */
|
/* TPM 2 would respond with a tag of TPM2_ST_NO_SESSIONS */
|
||||||
if (!ret && return_tag == 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 */
|
/* 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);
|
sizeof(test_req), &return_tag);
|
||||||
if (!ret && return_tag == TPM_TAG_RSP_COMMAND) {
|
if (!ret && return_tag == TPM_TAG_RSP_COMMAND) {
|
||||||
*tpm_version = TPM_VERSION_1_2;
|
*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,
|
int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
|
||||||
size_t *buffersize)
|
size_t *buffersize)
|
||||||
{
|
{
|
||||||
unsigned char buf[1024];
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
switch (tpm_version) {
|
switch (tpm_version) {
|
||||||
@ -277,26 +268,27 @@ int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
|
|||||||
struct tpm_resp_hdr hdr;
|
struct tpm_resp_hdr hdr;
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
uint32_t buffersize;
|
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,
|
ret = tpm_util_request(tpm_fd, &tpm_get_buffer_size,
|
||||||
sizeof(tpm_get_buffer_size), buf, sizeof(buf));
|
sizeof(tpm_get_buffer_size),
|
||||||
|
&tpm_resp, sizeof(tpm_resp));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (be32_to_cpu(tpm_resp->hdr.len) != sizeof(*tpm_resp) ||
|
if (be32_to_cpu(tpm_resp.hdr.len) != sizeof(tpm_resp) ||
|
||||||
be32_to_cpu(tpm_resp->len) != sizeof(uint32_t)) {
|
be32_to_cpu(tpm_resp.len) != sizeof(uint32_t)) {
|
||||||
DPRINTF("tpm_resp->hdr.len = %u, expected = %zu\n",
|
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",
|
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 "
|
error_report("tpm_util: Got unexpected response to "
|
||||||
"TPM_GetCapability; errcode: 0x%x",
|
"TPM_GetCapability; errcode: 0x%x",
|
||||||
be32_to_cpu(tpm_resp->hdr.errcode));
|
be32_to_cpu(tpm_resp.hdr.errcode));
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
*buffersize = be32_to_cpu(tpm_resp->buffersize);
|
*buffersize = be32_to_cpu(tpm_resp.buffersize);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TPM_VERSION_2_0: {
|
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 value1;
|
||||||
uint32_t property2;
|
uint32_t property2;
|
||||||
uint32_t value2;
|
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,
|
ret = tpm_util_request(tpm_fd, &tpm2_get_buffer_size,
|
||||||
sizeof(tpm2_get_buffer_size), buf, sizeof(buf));
|
sizeof(tpm2_get_buffer_size),
|
||||||
|
&tpm2_resp, sizeof(tpm2_resp));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (be32_to_cpu(tpm2_resp->hdr.len) != sizeof(*tpm2_resp) ||
|
if (be32_to_cpu(tpm2_resp.hdr.len) != sizeof(tpm2_resp) ||
|
||||||
be32_to_cpu(tpm2_resp->count) != 2) {
|
be32_to_cpu(tpm2_resp.count) != 2) {
|
||||||
DPRINTF("tpm2_resp->hdr.len = %u, expected = %zu\n",
|
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",
|
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 "
|
error_report("tpm_util: Got unexpected response to "
|
||||||
"TPM2_GetCapability; errcode: 0x%x",
|
"TPM2_GetCapability; errcode: 0x%x",
|
||||||
be32_to_cpu(tpm2_resp->hdr.errcode));
|
be32_to_cpu(tpm2_resp.hdr.errcode));
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
*buffersize = MAX(be32_to_cpu(tpm2_resp->value1),
|
*buffersize = MAX(be32_to_cpu(tpm2_resp.value1),
|
||||||
be32_to_cpu(tpm2_resp->value2));
|
be32_to_cpu(tpm2_resp.value2));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TPM_VERSION_UNSPEC:
|
case TPM_VERSION_UNSPEC:
|
||||||
|
@ -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);
|
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)
|
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,
|
int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
|
||||||
|
@ -16,11 +16,61 @@
|
|||||||
#ifndef HW_ACPI_TPM_H
|
#ifndef HW_ACPI_TPM_H
|
||||||
#define HW_ACPI_TPM_H
|
#define HW_ACPI_TPM_H
|
||||||
|
|
||||||
|
#include "hw/registerfields.h"
|
||||||
|
|
||||||
#define TPM_TIS_ADDR_BASE 0xFED40000
|
#define TPM_TIS_ADDR_BASE 0xFED40000
|
||||||
#define TPM_TIS_ADDR_SIZE 0x5000
|
#define TPM_TIS_ADDR_SIZE 0x5000
|
||||||
|
|
||||||
#define TPM_TIS_IRQ 5
|
#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_LOG_AREA_MINIMUM_SIZE (64 * 1024)
|
||||||
|
|
||||||
#define TPM_TCPA_ACPI_CLASS_CLIENT 0
|
#define TPM_TCPA_ACPI_CLASS_CLIENT 0
|
||||||
@ -30,5 +80,6 @@
|
|||||||
#define TPM2_ACPI_CLASS_SERVER 1
|
#define TPM2_ACPI_CLASS_SERVER 1
|
||||||
|
|
||||||
#define TPM2_START_METHOD_MMIO 6
|
#define TPM2_START_METHOD_MMIO 6
|
||||||
|
#define TPM2_START_METHOD_CRB 7
|
||||||
|
|
||||||
#endif /* HW_ACPI_TPM_H */
|
#endif /* HW_ACPI_TPM_H */
|
||||||
|
@ -41,14 +41,17 @@ typedef struct TPMIfClass {
|
|||||||
InterfaceClass parent_class;
|
InterfaceClass parent_class;
|
||||||
|
|
||||||
enum TpmModel model;
|
enum TpmModel model;
|
||||||
void (*request_completed)(TPMIf *obj);
|
void (*request_completed)(TPMIf *obj, int ret);
|
||||||
enum TPMVersion (*get_version)(TPMIf *obj);
|
enum TPMVersion (*get_version)(TPMIf *obj);
|
||||||
} TPMIfClass;
|
} TPMIfClass;
|
||||||
|
|
||||||
#define TYPE_TPM_TIS "tpm-tis"
|
#define TYPE_TPM_TIS "tpm-tis"
|
||||||
|
#define TYPE_TPM_CRB "tpm-crb"
|
||||||
|
|
||||||
#define TPM_IS_TIS(chr) \
|
#define TPM_IS_TIS(chr) \
|
||||||
object_dynamic_cast(OBJECT(chr), TYPE_TPM_TIS)
|
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 */
|
/* returns NULL unless there is exactly one TPM device */
|
||||||
static inline TPMIf *tpm_find(void)
|
static inline TPMIf *tpm_find(void)
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include "qapi-types.h"
|
#include "qapi-types.h"
|
||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
#include "sysemu/tpm.h"
|
#include "sysemu/tpm.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
|
||||||
#define TYPE_TPM_BACKEND "tpm-backend"
|
#define TYPE_TPM_BACKEND "tpm-backend"
|
||||||
#define TPM_BACKEND(obj) \
|
#define TPM_BACKEND(obj) \
|
||||||
@ -45,9 +46,8 @@ struct TPMBackend {
|
|||||||
/*< protected >*/
|
/*< protected >*/
|
||||||
TPMIf *tpmif;
|
TPMIf *tpmif;
|
||||||
bool opened;
|
bool opened;
|
||||||
GThreadPool *thread_pool;
|
|
||||||
bool had_startup_error;
|
bool had_startup_error;
|
||||||
QEMUBH *bh;
|
TPMBackendCmd *cmd;
|
||||||
|
|
||||||
/* <public> */
|
/* <public> */
|
||||||
char *id;
|
char *id;
|
||||||
@ -85,7 +85,7 @@ struct TPMBackendClass {
|
|||||||
|
|
||||||
TpmTypeOptions *(*get_tpm_options)(TPMBackend *t);
|
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);
|
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:
|
* tpm_backend_query_tpm:
|
||||||
* @s: the backend
|
* @s: the backend
|
||||||
|
@ -11,10 +11,11 @@
|
|||||||
# An enumeration of TPM models
|
# An enumeration of TPM models
|
||||||
#
|
#
|
||||||
# @tpm-tis: TPM TIS model
|
# @tpm-tis: TPM TIS model
|
||||||
|
# @tpm-crb: TPM CRB model (since 2.12)
|
||||||
#
|
#
|
||||||
# Since: 1.5
|
# Since: 1.5
|
||||||
##
|
##
|
||||||
{ 'enum': 'TpmModel', 'data': [ 'tpm-tis' ] }
|
{ 'enum': 'TpmModel', 'data': [ 'tpm-tis', 'tpm-crb' ] }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @query-tpm-models:
|
# @query-tpm-models:
|
||||||
@ -28,7 +29,7 @@
|
|||||||
# Example:
|
# Example:
|
||||||
#
|
#
|
||||||
# -> { "execute": "query-tpm-models" }
|
# -> { "execute": "query-tpm-models" }
|
||||||
# <- { "return": [ "tpm-tis" ] }
|
# <- { "return": [ "tpm-tis", "tpm-crb" ] }
|
||||||
#
|
#
|
||||||
##
|
##
|
||||||
{ 'command': 'query-tpm-models', 'returns': ['TpmModel'] }
|
{ 'command': 'query-tpm-models', 'returns': ['TpmModel'] }
|
||||||
|
@ -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),)
|
ifeq ($(CONFIG_VHOST_USER_NET_TEST_i386),)
|
||||||
check-qtest-x86_64-$(CONFIG_VHOST_USER_NET_TEST_x86_64) += tests/vhost-user-test$(EXESUF)
|
check-qtest-x86_64-$(CONFIG_VHOST_USER_NET_TEST_x86_64) += tests/vhost-user-test$(EXESUF)
|
||||||
endif
|
endif
|
||||||
|
check-qtest-i386-$(CONFIG_TPM) += tests/tpm-crb-test$(EXESUF)
|
||||||
check-qtest-i386-$(CONFIG_SLIRP) += tests/test-netfilter$(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-mirror$(EXESUF)
|
||||||
check-qtest-i386-$(CONFIG_POSIX) += tests/test-filter-redirector$(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-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y)
|
||||||
tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \
|
tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \
|
||||||
tests/io-channel-helpers.o $(test-io-obj-y)
|
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/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \
|
||||||
tests/io-channel-helpers.o $(test-io-obj-y)
|
tests/io-channel-helpers.o $(test-io-obj-y)
|
||||||
tests/test-io-channel-tls$(EXESUF): tests/test-io-channel-tls.o \
|
tests/test-io-channel-tls$(EXESUF): tests/test-io-channel-tls.o \
|
||||||
|
275
tests/tpm-crb-test.c
Normal file
275
tests/tpm-crb-test.c
Normal 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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user