diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2018-01-30 15:20:01 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2018-01-30 15:20:01 +0000 |
commit | 6521130b0a7f699fdb82446d57df5627bfa7ed3c (patch) | |
tree | 4133f36786798486d7222df1dbcc9d1919708116 | |
parent | 8ebb314b957403c1c9a3f1cf995f73c6ae9d5d10 (diff) | |
parent | 4ab6cb4c62273bb46102e5ae1d6af691b47cbcd8 (diff) |
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>
-rw-r--r-- | backends/tpm.c | 59 | ||||
-rw-r--r-- | default-configs/i386-softmmu.mak | 1 | ||||
-rw-r--r-- | default-configs/x86_64-softmmu.mak | 1 | ||||
-rw-r--r-- | hw/i386/acpi-build.c | 34 | ||||
-rw-r--r-- | hw/tpm/Makefile.objs | 1 | ||||
-rw-r--r-- | hw/tpm/tpm_crb.c | 303 | ||||
-rw-r--r-- | hw/tpm/tpm_emulator.c | 39 | ||||
-rw-r--r-- | hw/tpm/tpm_passthrough.c | 71 | ||||
-rw-r--r-- | hw/tpm/tpm_tis.c | 3 | ||||
-rw-r--r-- | hw/tpm/tpm_util.c | 75 | ||||
-rw-r--r-- | hw/tpm/tpm_util.h | 17 | ||||
-rw-r--r-- | include/hw/acpi/tpm.h | 51 | ||||
-rw-r--r-- | include/sysemu/tpm.h | 5 | ||||
-rw-r--r-- | include/sysemu/tpm_backend.h | 15 | ||||
-rw-r--r-- | qapi/tpm.json | 5 | ||||
-rw-r--r-- | tests/Makefile.include | 2 | ||||
-rw-r--r-- | tests/tpm-crb-test.c | 275 |
17 files changed, 818 insertions, 139 deletions
diff --git a/backends/tpm.c b/backends/tpm.c index 91222c5164..d617ba7c52 100644 --- a/backends/tpm.c +++ b/backends/tpm.c @@ -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, diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index 95ac4b464a..ac27700e79 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -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 diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak index 0221236825..b2104ade19 100644 --- a/default-configs/x86_64-softmmu.mak +++ b/default-configs/x86_64-softmmu.mak @@ -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 diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index dc4b2b9ffe..ed78c4ed9f 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -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); - - 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, - ACPI_BUILD_TPMLOG_FILE, 0); + } 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, + ACPI_BUILD_TPMLOG_FILE, 0); build_header(linker, table_data, (void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4, NULL, NULL); } diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs index 7a93b24636..1dc9f8bf2c 100644 --- a/hw/tpm/Makefile.objs +++ b/hw/tpm/Makefile.objs @@ -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 diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c new file mode 100644 index 0000000000..687d2557b7 --- /dev/null +++ b/hw/tpm/tpm_crb.c @@ -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) diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c index 35c78de5a9..710a9ec718 100644 --- a/hw/tpm/tpm_emulator.c +++ b/hw/tpm/tpm_emulator.c @@ -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; + &cmd->selftest_done, errp) < 0) { + tpm_util_write_fatal_error_response(cmd->out, cmd->out_len); } - - return; - -error: - 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 && diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c index 29142f38bb..a495fe07f4 100644 --- a/hw/tpm/tpm_passthrough.c +++ b/hw/tpm/tpm_passthrough.c @@ -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, - const uint8_t *in, uint32_t in_len, - uint8_t *out, uint32_t out_len, - bool *selftest_done) + +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, 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) { - dev++; - 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 open TPM cancel " - "path %s : %s", path, strerror(errno)); + 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/tpm/%s/device/cancel", + dev) < sizeof(path)) { + fd = qemu_open(path, O_WRONLY); + if (fd < 0) { + 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; diff --git a/hw/tpm/tpm_tis.c b/hw/tpm/tpm_tis.c index 8b5eb01a2c..08f41d2707 100644 --- a/hw/tpm/tpm_tis.c +++ b/hw/tpm/tpm_tis.c @@ -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; diff --git a/hw/tpm/tpm_util.c b/hw/tpm/tpm_util.c index 747075e244..8abde59915 100644 --- a/hw/tpm/tpm_util.c +++ b/hw/tpm/tpm_util.c @@ -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: diff --git a/hw/tpm/tpm_util.h b/hw/tpm/tpm_util.h index 19b28474ae..f003d15615 100644 --- a/hw/tpm/tpm_util.h +++ b/hw/tpm/tpm_util.h @@ -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, diff --git a/include/hw/acpi/tpm.h b/include/hw/acpi/tpm.h index 6d516c6a7f..96fd3a92f7 100644 --- a/include/hw/acpi/tpm.h +++ b/include/hw/acpi/tpm.h @@ -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 */ diff --git a/include/sysemu/tpm.h b/include/sysemu/tpm.h index 852e02687c..233b1a3fc3 100644 --- a/include/sysemu/tpm.h +++ b/include/sysemu/tpm.h @@ -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) diff --git a/include/sysemu/tpm_backend.h b/include/sysemu/tpm_backend.h index 0d6c994a62..7e166ef954 100644 --- a/include/sysemu/tpm_backend.h +++ b/include/sysemu/tpm_backend.h @@ -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); }; /** @@ -197,6 +197,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 * diff --git a/qapi/tpm.json b/qapi/tpm.json index 7093f268fb..d50deef5e9 100644 --- a/qapi/tpm.json +++ b/qapi/tpm.json @@ -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'] } diff --git a/tests/Makefile.include b/tests/Makefile.include index 851aafe9d1..ca82e0c0cc 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -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 \ diff --git a/tests/tpm-crb-test.c b/tests/tpm-crb-test.c new file mode 100644 index 0000000000..8bf1507e00 --- /dev/null +++ b/tests/tpm-crb-test.c @@ -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; +} |