1edff8678SStefan Berger /* 2edff8678SStefan Berger * tpm_tis.c - QEMU's TPM TIS interface emulator 3edff8678SStefan Berger * 4edff8678SStefan Berger * Copyright (C) 2006,2010-2013 IBM Corporation 5edff8678SStefan Berger * 6edff8678SStefan Berger * Authors: 7edff8678SStefan Berger * Stefan Berger <stefanb@us.ibm.com> 8edff8678SStefan Berger * David Safford <safford@us.ibm.com> 9edff8678SStefan Berger * 10edff8678SStefan Berger * Xen 4 support: Andrease Niederl <andreas.niederl@iaik.tugraz.at> 11edff8678SStefan Berger * 12edff8678SStefan Berger * This work is licensed under the terms of the GNU GPL, version 2 or later. 13edff8678SStefan Berger * See the COPYING file in the top-level directory. 14edff8678SStefan Berger * 15edff8678SStefan Berger * Implementation of the TIS interface according to specs found at 16edff8678SStefan Berger * http://www.trustedcomputinggroup.org. This implementation currently 179dd5c40dSStefan Berger * supports version 1.3, 21 March 2013 18edff8678SStefan Berger * In the developers menu choose the PC Client section then find the TIS 19edff8678SStefan Berger * specification. 20116694c3SStefan Berger * 21116694c3SStefan Berger * TPM TIS for TPM 2 implementation following TCG PC Client Platform 22116694c3SStefan Berger * TPM Profile (PTP) Specification, Familiy 2.0, Revision 00.43 23edff8678SStefan Berger */ 24edff8678SStefan Berger 250430891cSPeter Maydell #include "qemu/osdep.h" 26732cd587SMarc-André Lureau #include "hw/isa/isa.h" 27023299d8SMarc-André Lureau #include "qapi/error.h" 28023299d8SMarc-André Lureau 29023299d8SMarc-André Lureau #include "hw/acpi/tpm.h" 30023299d8SMarc-André Lureau #include "hw/pci/pci_ids.h" 31dccfcd0eSPaolo Bonzini #include "sysemu/tpm_backend.h" 32edff8678SStefan Berger #include "tpm_int.h" 335cf954d0SMarc-André Lureau #include "tpm_util.h" 34732cd587SMarc-André Lureau 35732cd587SMarc-André Lureau #define TPM_TIS_NUM_LOCALITIES 5 /* per spec */ 36732cd587SMarc-André Lureau #define TPM_TIS_LOCALITY_SHIFT 12 37732cd587SMarc-André Lureau #define TPM_TIS_NO_LOCALITY 0xff 38732cd587SMarc-André Lureau 39732cd587SMarc-André Lureau #define TPM_TIS_IS_VALID_LOCTY(x) ((x) < TPM_TIS_NUM_LOCALITIES) 40732cd587SMarc-André Lureau 41732cd587SMarc-André Lureau #define TPM_TIS_BUFFER_MAX 4096 42732cd587SMarc-André Lureau 43732cd587SMarc-André Lureau typedef enum { 44732cd587SMarc-André Lureau TPM_TIS_STATE_IDLE = 0, 45732cd587SMarc-André Lureau TPM_TIS_STATE_READY, 46732cd587SMarc-André Lureau TPM_TIS_STATE_COMPLETION, 47732cd587SMarc-André Lureau TPM_TIS_STATE_EXECUTION, 48732cd587SMarc-André Lureau TPM_TIS_STATE_RECEPTION, 49732cd587SMarc-André Lureau } TPMTISState; 50732cd587SMarc-André Lureau 51732cd587SMarc-André Lureau typedef struct TPMSizedBuffer { 52732cd587SMarc-André Lureau uint32_t size; 53732cd587SMarc-André Lureau uint8_t *buffer; 54732cd587SMarc-André Lureau } TPMSizedBuffer; 55732cd587SMarc-André Lureau 56732cd587SMarc-André Lureau /* locality data -- all fields are persisted */ 57732cd587SMarc-André Lureau typedef struct TPMLocality { 58732cd587SMarc-André Lureau TPMTISState state; 59732cd587SMarc-André Lureau uint8_t access; 60732cd587SMarc-André Lureau uint32_t sts; 61732cd587SMarc-André Lureau uint32_t iface_id; 62732cd587SMarc-André Lureau uint32_t inte; 63732cd587SMarc-André Lureau uint32_t ints; 64732cd587SMarc-André Lureau 65732cd587SMarc-André Lureau uint16_t w_offset; 66732cd587SMarc-André Lureau uint16_t r_offset; 67*e6b703f6SStefan Berger unsigned char w_buffer[TPM_TIS_BUFFER_MAX]; 68*e6b703f6SStefan Berger unsigned char r_buffer[TPM_TIS_BUFFER_MAX]; 69732cd587SMarc-André Lureau } TPMLocality; 70732cd587SMarc-André Lureau 7136e86589SMarc-André Lureau typedef struct TPMState { 723d4960c7SMarc-André Lureau ISADevice busdev; 733d4960c7SMarc-André Lureau MemoryRegion mmio; 743d4960c7SMarc-André Lureau 75732cd587SMarc-André Lureau uint32_t offset; 76732cd587SMarc-André Lureau uint8_t buf[TPM_TIS_BUFFER_MAX]; 77732cd587SMarc-André Lureau 78732cd587SMarc-André Lureau uint8_t active_locty; 79732cd587SMarc-André Lureau uint8_t aborting_locty; 80732cd587SMarc-André Lureau uint8_t next_locty; 81732cd587SMarc-André Lureau 82732cd587SMarc-André Lureau TPMLocality loc[TPM_TIS_NUM_LOCALITIES]; 83732cd587SMarc-André Lureau 84732cd587SMarc-André Lureau qemu_irq irq; 85732cd587SMarc-André Lureau uint32_t irq_num; 86732cd587SMarc-André Lureau 87732cd587SMarc-André Lureau TPMBackendCmd cmd; 88732cd587SMarc-André Lureau 89732cd587SMarc-André Lureau TPMBackend *be_driver; 90732cd587SMarc-André Lureau TPMVersion be_tpm_version; 91b21e6aafSStefan Berger 92b21e6aafSStefan Berger size_t be_buffer_size; 9336e86589SMarc-André Lureau } TPMState; 94732cd587SMarc-André Lureau 95732cd587SMarc-André Lureau #define TPM(obj) OBJECT_CHECK(TPMState, (obj), TYPE_TPM_TIS) 96edff8678SStefan Berger 974d1ba9c4SStefan Berger #define DEBUG_TIS 0 98edff8678SStefan Berger 994d1ba9c4SStefan Berger #define DPRINTF(fmt, ...) do { \ 1004d1ba9c4SStefan Berger if (DEBUG_TIS) { \ 1014d1ba9c4SStefan Berger printf(fmt, ## __VA_ARGS__); \ 1024d1ba9c4SStefan Berger } \ 1034d1ba9c4SStefan Berger } while (0); 104edff8678SStefan Berger 105edff8678SStefan Berger /* tis registers */ 106edff8678SStefan Berger #define TPM_TIS_REG_ACCESS 0x00 107edff8678SStefan Berger #define TPM_TIS_REG_INT_ENABLE 0x08 108edff8678SStefan Berger #define TPM_TIS_REG_INT_VECTOR 0x0c 109edff8678SStefan Berger #define TPM_TIS_REG_INT_STATUS 0x10 110edff8678SStefan Berger #define TPM_TIS_REG_INTF_CAPABILITY 0x14 111edff8678SStefan Berger #define TPM_TIS_REG_STS 0x18 112edff8678SStefan Berger #define TPM_TIS_REG_DATA_FIFO 0x24 113116694c3SStefan Berger #define TPM_TIS_REG_INTERFACE_ID 0x30 1142eae8c75SStefan Berger #define TPM_TIS_REG_DATA_XFIFO 0x80 1152eae8c75SStefan Berger #define TPM_TIS_REG_DATA_XFIFO_END 0xbc 116edff8678SStefan Berger #define TPM_TIS_REG_DID_VID 0xf00 117edff8678SStefan Berger #define TPM_TIS_REG_RID 0xf04 118edff8678SStefan Berger 1198db7c415SStefan Berger /* vendor-specific registers */ 1208db7c415SStefan Berger #define TPM_TIS_REG_DEBUG 0xf90 1218db7c415SStefan Berger 122116694c3SStefan Berger #define TPM_TIS_STS_TPM_FAMILY_MASK (0x3 << 26)/* TPM 2.0 */ 123116694c3SStefan Berger #define TPM_TIS_STS_TPM_FAMILY1_2 (0 << 26) /* TPM 2.0 */ 124116694c3SStefan Berger #define TPM_TIS_STS_TPM_FAMILY2_0 (1 << 26) /* TPM 2.0 */ 125116694c3SStefan Berger #define TPM_TIS_STS_RESET_ESTABLISHMENT_BIT (1 << 25) /* TPM 2.0 */ 126116694c3SStefan Berger #define TPM_TIS_STS_COMMAND_CANCEL (1 << 24) /* TPM 2.0 */ 127116694c3SStefan Berger 128edff8678SStefan Berger #define TPM_TIS_STS_VALID (1 << 7) 129edff8678SStefan Berger #define TPM_TIS_STS_COMMAND_READY (1 << 6) 130edff8678SStefan Berger #define TPM_TIS_STS_TPM_GO (1 << 5) 131edff8678SStefan Berger #define TPM_TIS_STS_DATA_AVAILABLE (1 << 4) 132edff8678SStefan Berger #define TPM_TIS_STS_EXPECT (1 << 3) 133fd859081SStefan Berger #define TPM_TIS_STS_SELFTEST_DONE (1 << 2) 134edff8678SStefan Berger #define TPM_TIS_STS_RESPONSE_RETRY (1 << 1) 135edff8678SStefan Berger 136edff8678SStefan Berger #define TPM_TIS_BURST_COUNT_SHIFT 8 137edff8678SStefan Berger #define TPM_TIS_BURST_COUNT(X) \ 138edff8678SStefan Berger ((X) << TPM_TIS_BURST_COUNT_SHIFT) 139edff8678SStefan Berger 140edff8678SStefan Berger #define TPM_TIS_ACCESS_TPM_REG_VALID_STS (1 << 7) 141edff8678SStefan Berger #define TPM_TIS_ACCESS_ACTIVE_LOCALITY (1 << 5) 142edff8678SStefan Berger #define TPM_TIS_ACCESS_BEEN_SEIZED (1 << 4) 143edff8678SStefan Berger #define TPM_TIS_ACCESS_SEIZE (1 << 3) 144edff8678SStefan Berger #define TPM_TIS_ACCESS_PENDING_REQUEST (1 << 2) 145edff8678SStefan Berger #define TPM_TIS_ACCESS_REQUEST_USE (1 << 1) 146edff8678SStefan Berger #define TPM_TIS_ACCESS_TPM_ESTABLISHMENT (1 << 0) 147edff8678SStefan Berger 148edff8678SStefan Berger #define TPM_TIS_INT_ENABLED (1 << 31) 149edff8678SStefan Berger #define TPM_TIS_INT_DATA_AVAILABLE (1 << 0) 150edff8678SStefan Berger #define TPM_TIS_INT_STS_VALID (1 << 1) 151edff8678SStefan Berger #define TPM_TIS_INT_LOCALITY_CHANGED (1 << 2) 152edff8678SStefan Berger #define TPM_TIS_INT_COMMAND_READY (1 << 7) 153edff8678SStefan Berger 154edff8678SStefan Berger #define TPM_TIS_INT_POLARITY_MASK (3 << 3) 155edff8678SStefan Berger #define TPM_TIS_INT_POLARITY_LOW_LEVEL (1 << 3) 156edff8678SStefan Berger 157edff8678SStefan Berger #define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \ 158edff8678SStefan Berger TPM_TIS_INT_DATA_AVAILABLE | \ 159edff8678SStefan Berger TPM_TIS_INT_STS_VALID | \ 160edff8678SStefan Berger TPM_TIS_INT_COMMAND_READY) 161edff8678SStefan Berger 1629dd5c40dSStefan Berger #define TPM_TIS_CAP_INTERFACE_VERSION1_3 (2 << 28) 163116694c3SStefan Berger #define TPM_TIS_CAP_INTERFACE_VERSION1_3_FOR_TPM2_0 (3 << 28) 1649dd5c40dSStefan Berger #define TPM_TIS_CAP_DATA_TRANSFER_64B (3 << 9) 1659dd5c40dSStefan Berger #define TPM_TIS_CAP_DATA_TRANSFER_LEGACY (0 << 9) 1669dd5c40dSStefan Berger #define TPM_TIS_CAP_BURST_COUNT_DYNAMIC (0 << 8) 167edff8678SStefan Berger #define TPM_TIS_CAP_INTERRUPT_LOW_LEVEL (1 << 4) /* support is mandatory */ 168116694c3SStefan Berger #define TPM_TIS_CAPABILITIES_SUPPORTED1_3 \ 169116694c3SStefan Berger (TPM_TIS_CAP_INTERRUPT_LOW_LEVEL | \ 1709dd5c40dSStefan Berger TPM_TIS_CAP_BURST_COUNT_DYNAMIC | \ 1719dd5c40dSStefan Berger TPM_TIS_CAP_DATA_TRANSFER_64B | \ 1729dd5c40dSStefan Berger TPM_TIS_CAP_INTERFACE_VERSION1_3 | \ 173edff8678SStefan Berger TPM_TIS_INTERRUPTS_SUPPORTED) 174edff8678SStefan Berger 175116694c3SStefan Berger #define TPM_TIS_CAPABILITIES_SUPPORTED2_0 \ 176116694c3SStefan Berger (TPM_TIS_CAP_INTERRUPT_LOW_LEVEL | \ 177116694c3SStefan Berger TPM_TIS_CAP_BURST_COUNT_DYNAMIC | \ 178116694c3SStefan Berger TPM_TIS_CAP_DATA_TRANSFER_64B | \ 179116694c3SStefan Berger TPM_TIS_CAP_INTERFACE_VERSION1_3_FOR_TPM2_0 | \ 180116694c3SStefan Berger TPM_TIS_INTERRUPTS_SUPPORTED) 181116694c3SStefan Berger 182116694c3SStefan Berger #define TPM_TIS_IFACE_ID_INTERFACE_TIS1_3 (0xf) /* TPM 2.0 */ 183116694c3SStefan Berger #define TPM_TIS_IFACE_ID_INTERFACE_FIFO (0x0) /* TPM 2.0 */ 184116694c3SStefan Berger #define TPM_TIS_IFACE_ID_INTERFACE_VER_FIFO (0 << 4) /* TPM 2.0 */ 185116694c3SStefan Berger #define TPM_TIS_IFACE_ID_CAP_5_LOCALITIES (1 << 8) /* TPM 2.0 */ 186116694c3SStefan Berger #define TPM_TIS_IFACE_ID_CAP_TIS_SUPPORTED (1 << 13) /* TPM 2.0 */ 187116694c3SStefan Berger #define TPM_TIS_IFACE_ID_INT_SEL_LOCK (1 << 19) /* TPM 2.0 */ 188116694c3SStefan Berger 189116694c3SStefan Berger #define TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3 \ 190116694c3SStefan Berger (TPM_TIS_IFACE_ID_INTERFACE_TIS1_3 | \ 191886ce6f8SStefan Hajnoczi (~0u << 4)/* all of it is don't care */) 192116694c3SStefan Berger 193116694c3SStefan Berger /* if backend was a TPM 2.0: */ 194116694c3SStefan Berger #define TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0 \ 195116694c3SStefan Berger (TPM_TIS_IFACE_ID_INTERFACE_FIFO | \ 196116694c3SStefan Berger TPM_TIS_IFACE_ID_INTERFACE_VER_FIFO | \ 197116694c3SStefan Berger TPM_TIS_IFACE_ID_CAP_5_LOCALITIES | \ 198116694c3SStefan Berger TPM_TIS_IFACE_ID_CAP_TIS_SUPPORTED) 199116694c3SStefan Berger 200edff8678SStefan Berger #define TPM_TIS_TPM_DID 0x0001 201edff8678SStefan Berger #define TPM_TIS_TPM_VID PCI_VENDOR_ID_IBM 202edff8678SStefan Berger #define TPM_TIS_TPM_RID 0x0001 203edff8678SStefan Berger 204edff8678SStefan Berger #define TPM_TIS_NO_DATA_BYTE 0xff 205edff8678SStefan Berger 2068db7c415SStefan Berger /* local prototypes */ 2078db7c415SStefan Berger 2088db7c415SStefan Berger static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, 2098db7c415SStefan Berger unsigned size); 2108db7c415SStefan Berger 211edff8678SStefan Berger /* utility functions */ 212edff8678SStefan Berger 213edff8678SStefan Berger static uint8_t tpm_tis_locality_from_addr(hwaddr addr) 214edff8678SStefan Berger { 215edff8678SStefan Berger return (uint8_t)((addr >> TPM_TIS_LOCALITY_SHIFT) & 0x7); 216edff8678SStefan Berger } 217edff8678SStefan Berger 218*e6b703f6SStefan Berger static void tpm_tis_show_buffer(const unsigned char *buffer, 219*e6b703f6SStefan Berger size_t buffer_size, const char *string) 220edff8678SStefan Berger { 221edff8678SStefan Berger #ifdef DEBUG_TIS 222edff8678SStefan Berger uint32_t len, i; 223edff8678SStefan Berger 224*e6b703f6SStefan Berger len = MIN(tpm_cmd_get_size(buffer), buffer_size); 225edff8678SStefan Berger DPRINTF("tpm_tis: %s length = %d\n", string, len); 226edff8678SStefan Berger for (i = 0; i < len; i++) { 227edff8678SStefan Berger if (i && !(i % 16)) { 228edff8678SStefan Berger DPRINTF("\n"); 229edff8678SStefan Berger } 230*e6b703f6SStefan Berger DPRINTF("%.2X ", buffer[i]); 231edff8678SStefan Berger } 232edff8678SStefan Berger DPRINTF("\n"); 233edff8678SStefan Berger #endif 234edff8678SStefan Berger } 235edff8678SStefan Berger 236edff8678SStefan Berger /* 237fd859081SStefan Berger * Set the given flags in the STS register by clearing the register but 238116694c3SStefan Berger * preserving the SELFTEST_DONE and TPM_FAMILY_MASK flags and then setting 239116694c3SStefan Berger * the new flags. 240fd859081SStefan Berger * 241fd859081SStefan Berger * The SELFTEST_DONE flag is acquired from the backend that determines it by 242fd859081SStefan Berger * peeking into TPM commands. 243fd859081SStefan Berger * 244fd859081SStefan Berger * A VM suspend/resume will preserve the flag by storing it into the VM 245fd859081SStefan Berger * device state, but the backend will not remember it when QEMU is started 246fd859081SStefan Berger * again. Therefore, we cache the flag here. Once set, it will not be unset 247fd859081SStefan Berger * except by a reset. 248fd859081SStefan Berger */ 249fd859081SStefan Berger static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags) 250fd859081SStefan Berger { 251116694c3SStefan Berger l->sts &= TPM_TIS_STS_SELFTEST_DONE | TPM_TIS_STS_TPM_FAMILY_MASK; 252fd859081SStefan Berger l->sts |= flags; 253fd859081SStefan Berger } 254fd859081SStefan Berger 255fd859081SStefan Berger /* 256edff8678SStefan Berger * Send a request to the TPM. 257edff8678SStefan Berger */ 258edff8678SStefan Berger static void tpm_tis_tpm_send(TPMState *s, uint8_t locty) 259edff8678SStefan Berger { 2603d4960c7SMarc-André Lureau TPMLocality *locty_data = &s->loc[locty]; 261edff8678SStefan Berger 262*e6b703f6SStefan Berger tpm_tis_show_buffer(s->loc[locty].w_buffer, s->be_buffer_size, 263*e6b703f6SStefan Berger "tpm_tis: To TPM"); 264edff8678SStefan Berger 265edff8678SStefan Berger /* 266edff8678SStefan Berger * w_offset serves as length indicator for length of data; 267edff8678SStefan Berger * it's reset when the response comes back 268edff8678SStefan Berger */ 2693d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_EXECUTION; 270edff8678SStefan Berger 2710e43b7e6SMarc-André Lureau s->cmd = (TPMBackendCmd) { 2720e43b7e6SMarc-André Lureau .locty = locty, 273*e6b703f6SStefan Berger .in = locty_data->w_buffer, 274d2809766SMarc-André Lureau .in_len = locty_data->w_offset, 275*e6b703f6SStefan Berger .out = locty_data->r_buffer, 276*e6b703f6SStefan Berger .out_len = s->be_buffer_size, 2770e43b7e6SMarc-André Lureau }; 2780e43b7e6SMarc-André Lureau 2790e43b7e6SMarc-André Lureau tpm_backend_deliver_request(s->be_driver, &s->cmd); 280edff8678SStefan Berger } 281edff8678SStefan Berger 282edff8678SStefan Berger /* raise an interrupt if allowed */ 283edff8678SStefan Berger static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask) 284edff8678SStefan Berger { 285edff8678SStefan Berger if (!TPM_TIS_IS_VALID_LOCTY(locty)) { 286edff8678SStefan Berger return; 287edff8678SStefan Berger } 288edff8678SStefan Berger 2893d4960c7SMarc-André Lureau if ((s->loc[locty].inte & TPM_TIS_INT_ENABLED) && 2903d4960c7SMarc-André Lureau (s->loc[locty].inte & irqmask)) { 291edff8678SStefan Berger DPRINTF("tpm_tis: Raising IRQ for flag %08x\n", irqmask); 2923d4960c7SMarc-André Lureau qemu_irq_raise(s->irq); 2933d4960c7SMarc-André Lureau s->loc[locty].ints |= irqmask; 294edff8678SStefan Berger } 295edff8678SStefan Berger } 296edff8678SStefan Berger 297edff8678SStefan Berger static uint32_t tpm_tis_check_request_use_except(TPMState *s, uint8_t locty) 298edff8678SStefan Berger { 299edff8678SStefan Berger uint8_t l; 300edff8678SStefan Berger 301edff8678SStefan Berger for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 302edff8678SStefan Berger if (l == locty) { 303edff8678SStefan Berger continue; 304edff8678SStefan Berger } 3053d4960c7SMarc-André Lureau if ((s->loc[l].access & TPM_TIS_ACCESS_REQUEST_USE)) { 306edff8678SStefan Berger return 1; 307edff8678SStefan Berger } 308edff8678SStefan Berger } 309edff8678SStefan Berger 310edff8678SStefan Berger return 0; 311edff8678SStefan Berger } 312edff8678SStefan Berger 313edff8678SStefan Berger static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty) 314edff8678SStefan Berger { 3153d4960c7SMarc-André Lureau bool change = (s->active_locty != new_active_locty); 316edff8678SStefan Berger bool is_seize; 317edff8678SStefan Berger uint8_t mask; 318edff8678SStefan Berger 3193d4960c7SMarc-André Lureau if (change && TPM_TIS_IS_VALID_LOCTY(s->active_locty)) { 320edff8678SStefan Berger is_seize = TPM_TIS_IS_VALID_LOCTY(new_active_locty) && 3213d4960c7SMarc-André Lureau s->loc[new_active_locty].access & TPM_TIS_ACCESS_SEIZE; 322edff8678SStefan Berger 323edff8678SStefan Berger if (is_seize) { 324edff8678SStefan Berger mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY); 325edff8678SStefan Berger } else { 326edff8678SStefan Berger mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY| 327edff8678SStefan Berger TPM_TIS_ACCESS_REQUEST_USE); 328edff8678SStefan Berger } 329edff8678SStefan Berger /* reset flags on the old active locality */ 3303d4960c7SMarc-André Lureau s->loc[s->active_locty].access &= mask; 331edff8678SStefan Berger 332edff8678SStefan Berger if (is_seize) { 3333d4960c7SMarc-André Lureau s->loc[s->active_locty].access |= TPM_TIS_ACCESS_BEEN_SEIZED; 334edff8678SStefan Berger } 335edff8678SStefan Berger } 336edff8678SStefan Berger 3373d4960c7SMarc-André Lureau s->active_locty = new_active_locty; 338edff8678SStefan Berger 3393d4960c7SMarc-André Lureau DPRINTF("tpm_tis: Active locality is now %d\n", s->active_locty); 340edff8678SStefan Berger 341edff8678SStefan Berger if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) { 342edff8678SStefan Berger /* set flags on the new active locality */ 3433d4960c7SMarc-André Lureau s->loc[new_active_locty].access |= TPM_TIS_ACCESS_ACTIVE_LOCALITY; 3443d4960c7SMarc-André Lureau s->loc[new_active_locty].access &= ~(TPM_TIS_ACCESS_REQUEST_USE | 345edff8678SStefan Berger TPM_TIS_ACCESS_SEIZE); 346edff8678SStefan Berger } 347edff8678SStefan Berger 348edff8678SStefan Berger if (change) { 3493d4960c7SMarc-André Lureau tpm_tis_raise_irq(s, s->active_locty, TPM_TIS_INT_LOCALITY_CHANGED); 350edff8678SStefan Berger } 351edff8678SStefan Berger } 352edff8678SStefan Berger 353edff8678SStefan Berger /* abort -- this function switches the locality */ 354edff8678SStefan Berger static void tpm_tis_abort(TPMState *s, uint8_t locty) 355edff8678SStefan Berger { 3563d4960c7SMarc-André Lureau s->loc[locty].r_offset = 0; 3573d4960c7SMarc-André Lureau s->loc[locty].w_offset = 0; 358edff8678SStefan Berger 3593d4960c7SMarc-André Lureau DPRINTF("tpm_tis: tis_abort: new active locality is %d\n", s->next_locty); 360edff8678SStefan Berger 361edff8678SStefan Berger /* 362edff8678SStefan Berger * Need to react differently depending on who's aborting now and 363edff8678SStefan Berger * which locality will become active afterwards. 364edff8678SStefan Berger */ 3653d4960c7SMarc-André Lureau if (s->aborting_locty == s->next_locty) { 3663d4960c7SMarc-André Lureau s->loc[s->aborting_locty].state = TPM_TIS_STATE_READY; 3673d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[s->aborting_locty], 368fd859081SStefan Berger TPM_TIS_STS_COMMAND_READY); 3693d4960c7SMarc-André Lureau tpm_tis_raise_irq(s, s->aborting_locty, TPM_TIS_INT_COMMAND_READY); 370edff8678SStefan Berger } 371edff8678SStefan Berger 372edff8678SStefan Berger /* locality after abort is another one than the current one */ 3733d4960c7SMarc-André Lureau tpm_tis_new_active_locality(s, s->next_locty); 374edff8678SStefan Berger 3753d4960c7SMarc-André Lureau s->next_locty = TPM_TIS_NO_LOCALITY; 376edff8678SStefan Berger /* nobody's aborting a command anymore */ 3773d4960c7SMarc-André Lureau s->aborting_locty = TPM_TIS_NO_LOCALITY; 378edff8678SStefan Berger } 379edff8678SStefan Berger 380edff8678SStefan Berger /* prepare aborting current command */ 381edff8678SStefan Berger static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty) 382edff8678SStefan Berger { 383edff8678SStefan Berger uint8_t busy_locty; 384edff8678SStefan Berger 3853d4960c7SMarc-André Lureau s->aborting_locty = locty; 3863d4960c7SMarc-André Lureau s->next_locty = newlocty; /* locality after successful abort */ 387edff8678SStefan Berger 388edff8678SStefan Berger /* 389edff8678SStefan Berger * only abort a command using an interrupt if currently executing 390edff8678SStefan Berger * a command AND if there's a valid connection to the vTPM. 391edff8678SStefan Berger */ 392edff8678SStefan Berger for (busy_locty = 0; busy_locty < TPM_TIS_NUM_LOCALITIES; busy_locty++) { 3933d4960c7SMarc-André Lureau if (s->loc[busy_locty].state == TPM_TIS_STATE_EXECUTION) { 394edff8678SStefan Berger /* 395edff8678SStefan Berger * request the backend to cancel. Some backends may not 396edff8678SStefan Berger * support it 397edff8678SStefan Berger */ 3988f0605ccSStefan Berger tpm_backend_cancel_cmd(s->be_driver); 399edff8678SStefan Berger return; 400edff8678SStefan Berger } 401edff8678SStefan Berger } 402edff8678SStefan Berger 403edff8678SStefan Berger tpm_tis_abort(s, locty); 404edff8678SStefan Berger } 405edff8678SStefan Berger 40668999059SMarc-André Lureau /* 40768999059SMarc-André Lureau * Callback from the TPM to indicate that the response was received. 40868999059SMarc-André Lureau */ 40968999059SMarc-André Lureau static void tpm_tis_request_completed(TPMIf *ti) 410edff8678SStefan Berger { 41168999059SMarc-André Lureau TPMState *s = TPM(ti); 4120e43b7e6SMarc-André Lureau uint8_t locty = s->cmd.locty; 41368999059SMarc-André Lureau uint8_t l; 41468999059SMarc-André Lureau 41568999059SMarc-André Lureau if (s->cmd.selftest_done) { 41668999059SMarc-André Lureau for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 41768999059SMarc-André Lureau s->loc[locty].sts |= TPM_TIS_STS_SELFTEST_DONE; 41868999059SMarc-André Lureau } 41968999059SMarc-André Lureau } 420edff8678SStefan Berger 4213d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 422fd859081SStefan Berger TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE); 4233d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_COMPLETION; 4243d4960c7SMarc-André Lureau s->loc[locty].r_offset = 0; 4253d4960c7SMarc-André Lureau s->loc[locty].w_offset = 0; 426edff8678SStefan Berger 427*e6b703f6SStefan Berger tpm_tis_show_buffer(s->loc[locty].r_buffer, s->be_buffer_size, 428*e6b703f6SStefan Berger "tpm_tis: From TPM"); 429298d8b81SStefan Berger 4303d4960c7SMarc-André Lureau if (TPM_TIS_IS_VALID_LOCTY(s->next_locty)) { 431edff8678SStefan Berger tpm_tis_abort(s, locty); 432edff8678SStefan Berger } 433edff8678SStefan Berger 434edff8678SStefan Berger tpm_tis_raise_irq(s, locty, 435edff8678SStefan Berger TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID); 436edff8678SStefan Berger } 437edff8678SStefan Berger 438edff8678SStefan Berger /* 439edff8678SStefan Berger * Read a byte of response data 440edff8678SStefan Berger */ 441edff8678SStefan Berger static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty) 442edff8678SStefan Berger { 443edff8678SStefan Berger uint32_t ret = TPM_TIS_NO_DATA_BYTE; 444edff8678SStefan Berger uint16_t len; 445edff8678SStefan Berger 4463d4960c7SMarc-André Lureau if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) { 447*e6b703f6SStefan Berger len = MIN(tpm_cmd_get_size(&s->loc[locty].r_buffer), 448*e6b703f6SStefan Berger s->be_buffer_size); 449edff8678SStefan Berger 450*e6b703f6SStefan Berger ret = s->loc[locty].r_buffer[s->loc[locty].r_offset++]; 4513d4960c7SMarc-André Lureau if (s->loc[locty].r_offset >= len) { 452edff8678SStefan Berger /* got last byte */ 4533d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 454edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); 455edff8678SStefan Berger } 456edff8678SStefan Berger DPRINTF("tpm_tis: tpm_tis_data_read byte 0x%02x [%d]\n", 4573d4960c7SMarc-André Lureau ret, s->loc[locty].r_offset - 1); 458edff8678SStefan Berger } 459edff8678SStefan Berger 460edff8678SStefan Berger return ret; 461edff8678SStefan Berger } 462edff8678SStefan Berger 4638db7c415SStefan Berger #ifdef DEBUG_TIS 4648db7c415SStefan Berger static void tpm_tis_dump_state(void *opaque, hwaddr addr) 4658db7c415SStefan Berger { 4668db7c415SStefan Berger static const unsigned regs[] = { 4678db7c415SStefan Berger TPM_TIS_REG_ACCESS, 4688db7c415SStefan Berger TPM_TIS_REG_INT_ENABLE, 4698db7c415SStefan Berger TPM_TIS_REG_INT_VECTOR, 4708db7c415SStefan Berger TPM_TIS_REG_INT_STATUS, 4718db7c415SStefan Berger TPM_TIS_REG_INTF_CAPABILITY, 4728db7c415SStefan Berger TPM_TIS_REG_STS, 4738db7c415SStefan Berger TPM_TIS_REG_DID_VID, 4748db7c415SStefan Berger TPM_TIS_REG_RID, 4758db7c415SStefan Berger 0xfff}; 4768db7c415SStefan Berger int idx; 4778db7c415SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 4788db7c415SStefan Berger hwaddr base = addr & ~0xfff; 4798db7c415SStefan Berger TPMState *s = opaque; 4808db7c415SStefan Berger 4818db7c415SStefan Berger DPRINTF("tpm_tis: active locality : %d\n" 4828db7c415SStefan Berger "tpm_tis: state of locality %d : %d\n" 4838db7c415SStefan Berger "tpm_tis: register dump:\n", 4843d4960c7SMarc-André Lureau s->active_locty, 4853d4960c7SMarc-André Lureau locty, s->loc[locty].state); 4868db7c415SStefan Berger 4878db7c415SStefan Berger for (idx = 0; regs[idx] != 0xfff; idx++) { 4888db7c415SStefan Berger DPRINTF("tpm_tis: 0x%04x : 0x%08x\n", regs[idx], 489070c7607SStefan Berger (int)tpm_tis_mmio_read(opaque, base + regs[idx], 4)); 4908db7c415SStefan Berger } 4918db7c415SStefan Berger 4928db7c415SStefan Berger DPRINTF("tpm_tis: read offset : %d\n" 4938db7c415SStefan Berger "tpm_tis: result buffer : ", 4943d4960c7SMarc-André Lureau s->loc[locty].r_offset); 4958db7c415SStefan Berger for (idx = 0; 496*e6b703f6SStefan Berger idx < MIN(tpm_cmd_get_size(&s->loc[locty].r_buffer), 497*e6b703f6SStefan Berger s->be_buffer_size); 4988db7c415SStefan Berger idx++) { 4998db7c415SStefan Berger DPRINTF("%c%02x%s", 5003d4960c7SMarc-André Lureau s->loc[locty].r_offset == idx ? '>' : ' ', 501*e6b703f6SStefan Berger s->loc[locty].r_buffer[idx], 5028db7c415SStefan Berger ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : ""); 5038db7c415SStefan Berger } 5048db7c415SStefan Berger DPRINTF("\n" 5058db7c415SStefan Berger "tpm_tis: write offset : %d\n" 5068db7c415SStefan Berger "tpm_tis: request buffer: ", 5073d4960c7SMarc-André Lureau s->loc[locty].w_offset); 5088db7c415SStefan Berger for (idx = 0; 509*e6b703f6SStefan Berger idx < MIN(tpm_cmd_get_size(s->loc[locty].w_buffer), 510*e6b703f6SStefan Berger s->be_buffer_size); 5118db7c415SStefan Berger idx++) { 5128db7c415SStefan Berger DPRINTF("%c%02x%s", 5133d4960c7SMarc-André Lureau s->loc[locty].w_offset == idx ? '>' : ' ', 514*e6b703f6SStefan Berger s->loc[locty].w_buffer[idx], 5158db7c415SStefan Berger ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : ""); 5168db7c415SStefan Berger } 5178db7c415SStefan Berger DPRINTF("\n"); 5188db7c415SStefan Berger } 5198db7c415SStefan Berger #endif 5208db7c415SStefan Berger 521edff8678SStefan Berger /* 522edff8678SStefan Berger * Read a register of the TIS interface 523edff8678SStefan Berger * See specs pages 33-63 for description of the registers 524edff8678SStefan Berger */ 525edff8678SStefan Berger static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, 526edff8678SStefan Berger unsigned size) 527edff8678SStefan Berger { 528edff8678SStefan Berger TPMState *s = opaque; 529edff8678SStefan Berger uint16_t offset = addr & 0xffc; 530edff8678SStefan Berger uint8_t shift = (addr & 0x3) * 8; 531edff8678SStefan Berger uint32_t val = 0xffffffff; 532edff8678SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 533edff8678SStefan Berger uint32_t avail; 534feeb755fSStefan Berger uint8_t v; 535edff8678SStefan Berger 5368f0605ccSStefan Berger if (tpm_backend_had_startup_error(s->be_driver)) { 5376cd65969SStefan Berger return 0; 538edff8678SStefan Berger } 539edff8678SStefan Berger 540edff8678SStefan Berger switch (offset) { 541edff8678SStefan Berger case TPM_TIS_REG_ACCESS: 542edff8678SStefan Berger /* never show the SEIZE flag even though we use it internally */ 5433d4960c7SMarc-André Lureau val = s->loc[locty].access & ~TPM_TIS_ACCESS_SEIZE; 544edff8678SStefan Berger /* the pending flag is always calculated */ 545edff8678SStefan Berger if (tpm_tis_check_request_use_except(s, locty)) { 546edff8678SStefan Berger val |= TPM_TIS_ACCESS_PENDING_REQUEST; 547edff8678SStefan Berger } 5488f0605ccSStefan Berger val |= !tpm_backend_get_tpm_established_flag(s->be_driver); 549edff8678SStefan Berger break; 550edff8678SStefan Berger case TPM_TIS_REG_INT_ENABLE: 5513d4960c7SMarc-André Lureau val = s->loc[locty].inte; 552edff8678SStefan Berger break; 553edff8678SStefan Berger case TPM_TIS_REG_INT_VECTOR: 5543d4960c7SMarc-André Lureau val = s->irq_num; 555edff8678SStefan Berger break; 556edff8678SStefan Berger case TPM_TIS_REG_INT_STATUS: 5573d4960c7SMarc-André Lureau val = s->loc[locty].ints; 558edff8678SStefan Berger break; 559edff8678SStefan Berger case TPM_TIS_REG_INTF_CAPABILITY: 560116694c3SStefan Berger switch (s->be_tpm_version) { 561116694c3SStefan Berger case TPM_VERSION_UNSPEC: 562116694c3SStefan Berger val = 0; 563116694c3SStefan Berger break; 564116694c3SStefan Berger case TPM_VERSION_1_2: 565116694c3SStefan Berger val = TPM_TIS_CAPABILITIES_SUPPORTED1_3; 566116694c3SStefan Berger break; 567116694c3SStefan Berger case TPM_VERSION_2_0: 568116694c3SStefan Berger val = TPM_TIS_CAPABILITIES_SUPPORTED2_0; 569116694c3SStefan Berger break; 570116694c3SStefan Berger } 571edff8678SStefan Berger break; 572edff8678SStefan Berger case TPM_TIS_REG_STS: 5733d4960c7SMarc-André Lureau if (s->active_locty == locty) { 5743d4960c7SMarc-André Lureau if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) { 575edff8678SStefan Berger val = TPM_TIS_BURST_COUNT( 576*e6b703f6SStefan Berger MIN(tpm_cmd_get_size(&s->loc[locty].r_buffer), 577*e6b703f6SStefan Berger s->be_buffer_size) 5783d4960c7SMarc-André Lureau - s->loc[locty].r_offset) | s->loc[locty].sts; 579edff8678SStefan Berger } else { 580*e6b703f6SStefan Berger avail = s->be_buffer_size - s->loc[locty].w_offset; 581edff8678SStefan Berger /* 582edff8678SStefan Berger * byte-sized reads should not return 0x00 for 0x100 583edff8678SStefan Berger * available bytes. 584edff8678SStefan Berger */ 585edff8678SStefan Berger if (size == 1 && avail > 0xff) { 586edff8678SStefan Berger avail = 0xff; 587edff8678SStefan Berger } 5883d4960c7SMarc-André Lureau val = TPM_TIS_BURST_COUNT(avail) | s->loc[locty].sts; 589edff8678SStefan Berger } 590edff8678SStefan Berger } 591edff8678SStefan Berger break; 592edff8678SStefan Berger case TPM_TIS_REG_DATA_FIFO: 5932eae8c75SStefan Berger case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END: 5943d4960c7SMarc-André Lureau if (s->active_locty == locty) { 595feeb755fSStefan Berger if (size > 4 - (addr & 0x3)) { 596feeb755fSStefan Berger /* prevent access beyond FIFO */ 597feeb755fSStefan Berger size = 4 - (addr & 0x3); 598feeb755fSStefan Berger } 599feeb755fSStefan Berger val = 0; 600feeb755fSStefan Berger shift = 0; 601feeb755fSStefan Berger while (size > 0) { 6023d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 603edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 604feeb755fSStefan Berger v = tpm_tis_data_read(s, locty); 605edff8678SStefan Berger break; 606edff8678SStefan Berger default: 607feeb755fSStefan Berger v = TPM_TIS_NO_DATA_BYTE; 608edff8678SStefan Berger break; 609edff8678SStefan Berger } 610feeb755fSStefan Berger val |= (v << shift); 611feeb755fSStefan Berger shift += 8; 612feeb755fSStefan Berger size--; 613feeb755fSStefan Berger } 614feeb755fSStefan Berger shift = 0; /* no more adjustments */ 615edff8678SStefan Berger } 616edff8678SStefan Berger break; 617116694c3SStefan Berger case TPM_TIS_REG_INTERFACE_ID: 6183d4960c7SMarc-André Lureau val = s->loc[locty].iface_id; 619116694c3SStefan Berger break; 620edff8678SStefan Berger case TPM_TIS_REG_DID_VID: 621edff8678SStefan Berger val = (TPM_TIS_TPM_DID << 16) | TPM_TIS_TPM_VID; 622edff8678SStefan Berger break; 623edff8678SStefan Berger case TPM_TIS_REG_RID: 624edff8678SStefan Berger val = TPM_TIS_TPM_RID; 625edff8678SStefan Berger break; 6268db7c415SStefan Berger #ifdef DEBUG_TIS 6278db7c415SStefan Berger case TPM_TIS_REG_DEBUG: 6288db7c415SStefan Berger tpm_tis_dump_state(opaque, addr); 6298db7c415SStefan Berger break; 6308db7c415SStefan Berger #endif 631edff8678SStefan Berger } 632edff8678SStefan Berger 633edff8678SStefan Berger if (shift) { 634edff8678SStefan Berger val >>= shift; 635edff8678SStefan Berger } 636edff8678SStefan Berger 637070c7607SStefan Berger DPRINTF("tpm_tis: read.%u(%08x) = %08x\n", size, (int)addr, (int)val); 638edff8678SStefan Berger 639edff8678SStefan Berger return val; 640edff8678SStefan Berger } 641edff8678SStefan Berger 642edff8678SStefan Berger /* 643edff8678SStefan Berger * Write a value to a register of the TIS interface 644edff8678SStefan Berger * See specs pages 33-63 for description of the registers 645edff8678SStefan Berger */ 646ff2bc0c1SMarc-André Lureau static void tpm_tis_mmio_write(void *opaque, hwaddr addr, 647ff2bc0c1SMarc-André Lureau uint64_t val, unsigned size) 648edff8678SStefan Berger { 649edff8678SStefan Berger TPMState *s = opaque; 650feeb755fSStefan Berger uint16_t off = addr & 0xffc; 651feeb755fSStefan Berger uint8_t shift = (addr & 0x3) * 8; 652edff8678SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 653edff8678SStefan Berger uint8_t active_locty, l; 654edff8678SStefan Berger int c, set_new_locty = 1; 655edff8678SStefan Berger uint16_t len; 656feeb755fSStefan Berger uint32_t mask = (size == 1) ? 0xff : ((size == 2) ? 0xffff : ~0); 657edff8678SStefan Berger 658070c7607SStefan Berger DPRINTF("tpm_tis: write.%u(%08x) = %08x\n", size, (int)addr, (int)val); 659edff8678SStefan Berger 660ff2bc0c1SMarc-André Lureau if (locty == 4) { 661edff8678SStefan Berger DPRINTF("tpm_tis: Access to locality 4 only allowed from hardware\n"); 662edff8678SStefan Berger return; 663edff8678SStefan Berger } 664edff8678SStefan Berger 6658f0605ccSStefan Berger if (tpm_backend_had_startup_error(s->be_driver)) { 666edff8678SStefan Berger return; 667edff8678SStefan Berger } 668edff8678SStefan Berger 669feeb755fSStefan Berger val &= mask; 670feeb755fSStefan Berger 671feeb755fSStefan Berger if (shift) { 672feeb755fSStefan Berger val <<= shift; 673feeb755fSStefan Berger mask <<= shift; 674feeb755fSStefan Berger } 675feeb755fSStefan Berger 676feeb755fSStefan Berger mask ^= 0xffffffff; 677feeb755fSStefan Berger 678edff8678SStefan Berger switch (off) { 679edff8678SStefan Berger case TPM_TIS_REG_ACCESS: 680edff8678SStefan Berger 681edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_SEIZE)) { 682edff8678SStefan Berger val &= ~(TPM_TIS_ACCESS_REQUEST_USE | 683edff8678SStefan Berger TPM_TIS_ACCESS_ACTIVE_LOCALITY); 684edff8678SStefan Berger } 685edff8678SStefan Berger 6863d4960c7SMarc-André Lureau active_locty = s->active_locty; 687edff8678SStefan Berger 688edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) { 689edff8678SStefan Berger /* give up locality if currently owned */ 6903d4960c7SMarc-André Lureau if (s->active_locty == locty) { 691edff8678SStefan Berger DPRINTF("tpm_tis: Releasing locality %d\n", locty); 692edff8678SStefan Berger 693edff8678SStefan Berger uint8_t newlocty = TPM_TIS_NO_LOCALITY; 694edff8678SStefan Berger /* anybody wants the locality ? */ 695edff8678SStefan Berger for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) { 6963d4960c7SMarc-André Lureau if ((s->loc[c].access & TPM_TIS_ACCESS_REQUEST_USE)) { 697edff8678SStefan Berger DPRINTF("tpm_tis: Locality %d requests use.\n", c); 698edff8678SStefan Berger newlocty = c; 699edff8678SStefan Berger break; 700edff8678SStefan Berger } 701edff8678SStefan Berger } 702edff8678SStefan Berger DPRINTF("tpm_tis: TPM_TIS_ACCESS_ACTIVE_LOCALITY: " 703edff8678SStefan Berger "Next active locality: %d\n", 704edff8678SStefan Berger newlocty); 705edff8678SStefan Berger 706edff8678SStefan Berger if (TPM_TIS_IS_VALID_LOCTY(newlocty)) { 707edff8678SStefan Berger set_new_locty = 0; 708edff8678SStefan Berger tpm_tis_prep_abort(s, locty, newlocty); 709edff8678SStefan Berger } else { 710edff8678SStefan Berger active_locty = TPM_TIS_NO_LOCALITY; 711edff8678SStefan Berger } 712edff8678SStefan Berger } else { 713edff8678SStefan Berger /* not currently the owner; clear a pending request */ 7143d4960c7SMarc-André Lureau s->loc[locty].access &= ~TPM_TIS_ACCESS_REQUEST_USE; 715edff8678SStefan Berger } 716edff8678SStefan Berger } 717edff8678SStefan Berger 718edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_BEEN_SEIZED)) { 7193d4960c7SMarc-André Lureau s->loc[locty].access &= ~TPM_TIS_ACCESS_BEEN_SEIZED; 720edff8678SStefan Berger } 721edff8678SStefan Berger 722edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_SEIZE)) { 723edff8678SStefan Berger /* 724edff8678SStefan Berger * allow seize if a locality is active and the requesting 725edff8678SStefan Berger * locality is higher than the one that's active 726edff8678SStefan Berger * OR 727edff8678SStefan Berger * allow seize for requesting locality if no locality is 728edff8678SStefan Berger * active 729edff8678SStefan Berger */ 7303d4960c7SMarc-André Lureau while ((TPM_TIS_IS_VALID_LOCTY(s->active_locty) && 7313d4960c7SMarc-André Lureau locty > s->active_locty) || 7323d4960c7SMarc-André Lureau !TPM_TIS_IS_VALID_LOCTY(s->active_locty)) { 733edff8678SStefan Berger bool higher_seize = FALSE; 734edff8678SStefan Berger 735edff8678SStefan Berger /* already a pending SEIZE ? */ 7363d4960c7SMarc-André Lureau if ((s->loc[locty].access & TPM_TIS_ACCESS_SEIZE)) { 737edff8678SStefan Berger break; 738edff8678SStefan Berger } 739edff8678SStefan Berger 740edff8678SStefan Berger /* check for ongoing seize by a higher locality */ 741edff8678SStefan Berger for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES; l++) { 7423d4960c7SMarc-André Lureau if ((s->loc[l].access & TPM_TIS_ACCESS_SEIZE)) { 743edff8678SStefan Berger higher_seize = TRUE; 744edff8678SStefan Berger break; 745edff8678SStefan Berger } 746edff8678SStefan Berger } 747edff8678SStefan Berger 748edff8678SStefan Berger if (higher_seize) { 749edff8678SStefan Berger break; 750edff8678SStefan Berger } 751edff8678SStefan Berger 752edff8678SStefan Berger /* cancel any seize by a lower locality */ 753edff8678SStefan Berger for (l = 0; l < locty - 1; l++) { 7543d4960c7SMarc-André Lureau s->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE; 755edff8678SStefan Berger } 756edff8678SStefan Berger 7573d4960c7SMarc-André Lureau s->loc[locty].access |= TPM_TIS_ACCESS_SEIZE; 758edff8678SStefan Berger DPRINTF("tpm_tis: TPM_TIS_ACCESS_SEIZE: " 759edff8678SStefan Berger "Locality %d seized from locality %d\n", 7603d4960c7SMarc-André Lureau locty, s->active_locty); 761edff8678SStefan Berger DPRINTF("tpm_tis: TPM_TIS_ACCESS_SEIZE: Initiating abort.\n"); 762edff8678SStefan Berger set_new_locty = 0; 7633d4960c7SMarc-André Lureau tpm_tis_prep_abort(s, s->active_locty, locty); 764edff8678SStefan Berger break; 765edff8678SStefan Berger } 766edff8678SStefan Berger } 767edff8678SStefan Berger 768edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_REQUEST_USE)) { 7693d4960c7SMarc-André Lureau if (s->active_locty != locty) { 7703d4960c7SMarc-André Lureau if (TPM_TIS_IS_VALID_LOCTY(s->active_locty)) { 7713d4960c7SMarc-André Lureau s->loc[locty].access |= TPM_TIS_ACCESS_REQUEST_USE; 772edff8678SStefan Berger } else { 773edff8678SStefan Berger /* no locality active -> make this one active now */ 774edff8678SStefan Berger active_locty = locty; 775edff8678SStefan Berger } 776edff8678SStefan Berger } 777edff8678SStefan Berger } 778edff8678SStefan Berger 779edff8678SStefan Berger if (set_new_locty) { 780edff8678SStefan Berger tpm_tis_new_active_locality(s, active_locty); 781edff8678SStefan Berger } 782edff8678SStefan Berger 783edff8678SStefan Berger break; 784edff8678SStefan Berger case TPM_TIS_REG_INT_ENABLE: 7853d4960c7SMarc-André Lureau if (s->active_locty != locty) { 786edff8678SStefan Berger break; 787edff8678SStefan Berger } 788edff8678SStefan Berger 7893d4960c7SMarc-André Lureau s->loc[locty].inte &= mask; 7903d4960c7SMarc-André Lureau s->loc[locty].inte |= (val & (TPM_TIS_INT_ENABLED | 791edff8678SStefan Berger TPM_TIS_INT_POLARITY_MASK | 792edff8678SStefan Berger TPM_TIS_INTERRUPTS_SUPPORTED)); 793edff8678SStefan Berger break; 794edff8678SStefan Berger case TPM_TIS_REG_INT_VECTOR: 795edff8678SStefan Berger /* hard wired -- ignore */ 796edff8678SStefan Berger break; 797edff8678SStefan Berger case TPM_TIS_REG_INT_STATUS: 7983d4960c7SMarc-André Lureau if (s->active_locty != locty) { 799edff8678SStefan Berger break; 800edff8678SStefan Berger } 801edff8678SStefan Berger 802edff8678SStefan Berger /* clearing of interrupt flags */ 803edff8678SStefan Berger if (((val & TPM_TIS_INTERRUPTS_SUPPORTED)) && 8043d4960c7SMarc-André Lureau (s->loc[locty].ints & TPM_TIS_INTERRUPTS_SUPPORTED)) { 8053d4960c7SMarc-André Lureau s->loc[locty].ints &= ~val; 8063d4960c7SMarc-André Lureau if (s->loc[locty].ints == 0) { 8073d4960c7SMarc-André Lureau qemu_irq_lower(s->irq); 808edff8678SStefan Berger DPRINTF("tpm_tis: Lowering IRQ\n"); 809edff8678SStefan Berger } 810edff8678SStefan Berger } 8113d4960c7SMarc-André Lureau s->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED); 812edff8678SStefan Berger break; 813edff8678SStefan Berger case TPM_TIS_REG_STS: 8143d4960c7SMarc-André Lureau if (s->active_locty != locty) { 815edff8678SStefan Berger break; 816edff8678SStefan Berger } 817edff8678SStefan Berger 818116694c3SStefan Berger if (s->be_tpm_version == TPM_VERSION_2_0) { 819116694c3SStefan Berger /* some flags that are only supported for TPM 2 */ 820116694c3SStefan Berger if (val & TPM_TIS_STS_COMMAND_CANCEL) { 8213d4960c7SMarc-André Lureau if (s->loc[locty].state == TPM_TIS_STATE_EXECUTION) { 822116694c3SStefan Berger /* 823116694c3SStefan Berger * request the backend to cancel. Some backends may not 824116694c3SStefan Berger * support it 825116694c3SStefan Berger */ 826116694c3SStefan Berger tpm_backend_cancel_cmd(s->be_driver); 827116694c3SStefan Berger } 828116694c3SStefan Berger } 829116694c3SStefan Berger 830116694c3SStefan Berger if (val & TPM_TIS_STS_RESET_ESTABLISHMENT_BIT) { 831116694c3SStefan Berger if (locty == 3 || locty == 4) { 832116694c3SStefan Berger tpm_backend_reset_tpm_established_flag(s->be_driver, locty); 833116694c3SStefan Berger } 834116694c3SStefan Berger } 835116694c3SStefan Berger } 836116694c3SStefan Berger 837edff8678SStefan Berger val &= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO | 838edff8678SStefan Berger TPM_TIS_STS_RESPONSE_RETRY); 839edff8678SStefan Berger 840edff8678SStefan Berger if (val == TPM_TIS_STS_COMMAND_READY) { 8413d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 842edff8678SStefan Berger 843edff8678SStefan Berger case TPM_TIS_STATE_READY: 8443d4960c7SMarc-André Lureau s->loc[locty].w_offset = 0; 8453d4960c7SMarc-André Lureau s->loc[locty].r_offset = 0; 846edff8678SStefan Berger break; 847edff8678SStefan Berger 848edff8678SStefan Berger case TPM_TIS_STATE_IDLE: 8493d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_COMMAND_READY); 8503d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_READY; 851edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); 852edff8678SStefan Berger break; 853edff8678SStefan Berger 854edff8678SStefan Berger case TPM_TIS_STATE_EXECUTION: 855edff8678SStefan Berger case TPM_TIS_STATE_RECEPTION: 856edff8678SStefan Berger /* abort currently running command */ 857edff8678SStefan Berger DPRINTF("tpm_tis: %s: Initiating abort.\n", 858edff8678SStefan Berger __func__); 859edff8678SStefan Berger tpm_tis_prep_abort(s, locty, locty); 860edff8678SStefan Berger break; 861edff8678SStefan Berger 862edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 8633d4960c7SMarc-André Lureau s->loc[locty].w_offset = 0; 8643d4960c7SMarc-André Lureau s->loc[locty].r_offset = 0; 865edff8678SStefan Berger /* shortcut to ready state with C/R set */ 8663d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_READY; 8673d4960c7SMarc-André Lureau if (!(s->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) { 8683d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 869fd859081SStefan Berger TPM_TIS_STS_COMMAND_READY); 870edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); 871edff8678SStefan Berger } 8723d4960c7SMarc-André Lureau s->loc[locty].sts &= ~(TPM_TIS_STS_DATA_AVAILABLE); 873edff8678SStefan Berger break; 874edff8678SStefan Berger 875edff8678SStefan Berger } 876edff8678SStefan Berger } else if (val == TPM_TIS_STS_TPM_GO) { 8773d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 878edff8678SStefan Berger case TPM_TIS_STATE_RECEPTION: 8793d4960c7SMarc-André Lureau if ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) == 0) { 880edff8678SStefan Berger tpm_tis_tpm_send(s, locty); 881edff8678SStefan Berger } 882edff8678SStefan Berger break; 883edff8678SStefan Berger default: 884edff8678SStefan Berger /* ignore */ 885edff8678SStefan Berger break; 886edff8678SStefan Berger } 887edff8678SStefan Berger } else if (val == TPM_TIS_STS_RESPONSE_RETRY) { 8883d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 889edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 8903d4960c7SMarc-André Lureau s->loc[locty].r_offset = 0; 8913d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 892fd859081SStefan Berger TPM_TIS_STS_VALID| 893fd859081SStefan Berger TPM_TIS_STS_DATA_AVAILABLE); 894edff8678SStefan Berger break; 895edff8678SStefan Berger default: 896edff8678SStefan Berger /* ignore */ 897edff8678SStefan Berger break; 898edff8678SStefan Berger } 899edff8678SStefan Berger } 900edff8678SStefan Berger break; 901edff8678SStefan Berger case TPM_TIS_REG_DATA_FIFO: 9022eae8c75SStefan Berger case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END: 903edff8678SStefan Berger /* data fifo */ 9043d4960c7SMarc-André Lureau if (s->active_locty != locty) { 905edff8678SStefan Berger break; 906edff8678SStefan Berger } 907edff8678SStefan Berger 9083d4960c7SMarc-André Lureau if (s->loc[locty].state == TPM_TIS_STATE_IDLE || 9093d4960c7SMarc-André Lureau s->loc[locty].state == TPM_TIS_STATE_EXECUTION || 9103d4960c7SMarc-André Lureau s->loc[locty].state == TPM_TIS_STATE_COMPLETION) { 911edff8678SStefan Berger /* drop the byte */ 912edff8678SStefan Berger } else { 913feeb755fSStefan Berger DPRINTF("tpm_tis: Data to send to TPM: %08x (size=%d)\n", 914070c7607SStefan Berger (int)val, size); 9153d4960c7SMarc-André Lureau if (s->loc[locty].state == TPM_TIS_STATE_READY) { 9163d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_RECEPTION; 9173d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 918fd859081SStefan Berger TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); 919edff8678SStefan Berger } 920edff8678SStefan Berger 921feeb755fSStefan Berger val >>= shift; 922feeb755fSStefan Berger if (size > 4 - (addr & 0x3)) { 923feeb755fSStefan Berger /* prevent access beyond FIFO */ 924feeb755fSStefan Berger size = 4 - (addr & 0x3); 925feeb755fSStefan Berger } 926feeb755fSStefan Berger 9273d4960c7SMarc-André Lureau while ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) && size > 0) { 928*e6b703f6SStefan Berger if (s->loc[locty].w_offset < s->be_buffer_size) { 929*e6b703f6SStefan Berger s->loc[locty].w_buffer[s->loc[locty].w_offset++] = 930*e6b703f6SStefan Berger (uint8_t)val; 931feeb755fSStefan Berger val >>= 8; 932feeb755fSStefan Berger size--; 933edff8678SStefan Berger } else { 9343d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 935edff8678SStefan Berger } 936edff8678SStefan Berger } 937edff8678SStefan Berger 938edff8678SStefan Berger /* check for complete packet */ 9393d4960c7SMarc-André Lureau if (s->loc[locty].w_offset > 5 && 9403d4960c7SMarc-André Lureau (s->loc[locty].sts & TPM_TIS_STS_EXPECT)) { 941edff8678SStefan Berger /* we have a packet length - see if we have all of it */ 9423d4960c7SMarc-André Lureau bool need_irq = !(s->loc[locty].sts & TPM_TIS_STS_VALID); 943d8383d61SMarc-André Lureau 944*e6b703f6SStefan Berger len = tpm_cmd_get_size(&s->loc[locty].w_buffer); 9453d4960c7SMarc-André Lureau if (len > s->loc[locty].w_offset) { 9463d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 947fd859081SStefan Berger TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); 948edff8678SStefan Berger } else { 949edff8678SStefan Berger /* packet complete */ 9503d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 951edff8678SStefan Berger } 95229b558d8SStefan Berger if (need_irq) { 953edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); 954edff8678SStefan Berger } 955edff8678SStefan Berger } 956edff8678SStefan Berger } 957edff8678SStefan Berger break; 958116694c3SStefan Berger case TPM_TIS_REG_INTERFACE_ID: 959116694c3SStefan Berger if (val & TPM_TIS_IFACE_ID_INT_SEL_LOCK) { 960116694c3SStefan Berger for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 9613d4960c7SMarc-André Lureau s->loc[l].iface_id |= TPM_TIS_IFACE_ID_INT_SEL_LOCK; 962116694c3SStefan Berger } 963116694c3SStefan Berger } 964116694c3SStefan Berger break; 965edff8678SStefan Berger } 966edff8678SStefan Berger } 967edff8678SStefan Berger 968edff8678SStefan Berger static const MemoryRegionOps tpm_tis_memory_ops = { 969edff8678SStefan Berger .read = tpm_tis_mmio_read, 970edff8678SStefan Berger .write = tpm_tis_mmio_write, 971edff8678SStefan Berger .endianness = DEVICE_LITTLE_ENDIAN, 972edff8678SStefan Berger .valid = { 973edff8678SStefan Berger .min_access_size = 1, 974edff8678SStefan Berger .max_access_size = 4, 975edff8678SStefan Berger }, 976edff8678SStefan Berger }; 977edff8678SStefan Berger 9788a2306c7SStefan Berger static int tpm_tis_do_startup_tpm(TPMState *s, size_t buffersize) 979edff8678SStefan Berger { 9809375c44fSStefan Berger return tpm_backend_startup_tpm(s->be_driver, buffersize); 981edff8678SStefan Berger } 982edff8678SStefan Berger 983edff8678SStefan Berger /* 9845cb18b3dSStefan Berger * Get the TPMVersion of the backend device being used 9855cb18b3dSStefan Berger */ 9869af7a721SMarc-André Lureau static enum TPMVersion tpm_tis_get_tpm_version(TPMIf *ti) 9875cb18b3dSStefan Berger { 9889af7a721SMarc-André Lureau TPMState *s = TPM(ti); 9895cb18b3dSStefan Berger 990ad4aca69SStefan Berger if (tpm_backend_had_startup_error(s->be_driver)) { 991ad4aca69SStefan Berger return TPM_VERSION_UNSPEC; 992ad4aca69SStefan Berger } 993ad4aca69SStefan Berger 9945cb18b3dSStefan Berger return tpm_backend_get_tpm_version(s->be_driver); 9955cb18b3dSStefan Berger } 9965cb18b3dSStefan Berger 9975cb18b3dSStefan Berger /* 998edff8678SStefan Berger * This function is called when the machine starts, resets or due to 999edff8678SStefan Berger * S3 resume. 1000edff8678SStefan Berger */ 1001edff8678SStefan Berger static void tpm_tis_reset(DeviceState *dev) 1002edff8678SStefan Berger { 1003edff8678SStefan Berger TPMState *s = TPM(dev); 1004edff8678SStefan Berger int c; 1005edff8678SStefan Berger 1006116694c3SStefan Berger s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver); 10071af3d63eSStefan Berger s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->be_driver), 10081af3d63eSStefan Berger TPM_TIS_BUFFER_MAX); 1009116694c3SStefan Berger 10108f0605ccSStefan Berger tpm_backend_reset(s->be_driver); 1011edff8678SStefan Berger 10123d4960c7SMarc-André Lureau s->active_locty = TPM_TIS_NO_LOCALITY; 10133d4960c7SMarc-André Lureau s->next_locty = TPM_TIS_NO_LOCALITY; 10143d4960c7SMarc-André Lureau s->aborting_locty = TPM_TIS_NO_LOCALITY; 1015edff8678SStefan Berger 1016edff8678SStefan Berger for (c = 0; c < TPM_TIS_NUM_LOCALITIES; c++) { 10173d4960c7SMarc-André Lureau s->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS; 1018116694c3SStefan Berger switch (s->be_tpm_version) { 1019116694c3SStefan Berger case TPM_VERSION_UNSPEC: 1020116694c3SStefan Berger break; 1021116694c3SStefan Berger case TPM_VERSION_1_2: 10223d4960c7SMarc-André Lureau s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY1_2; 10233d4960c7SMarc-André Lureau s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3; 1024116694c3SStefan Berger break; 1025116694c3SStefan Berger case TPM_VERSION_2_0: 10263d4960c7SMarc-André Lureau s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY2_0; 10273d4960c7SMarc-André Lureau s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0; 1028116694c3SStefan Berger break; 1029116694c3SStefan Berger } 10303d4960c7SMarc-André Lureau s->loc[c].inte = TPM_TIS_INT_POLARITY_LOW_LEVEL; 10313d4960c7SMarc-André Lureau s->loc[c].ints = 0; 10323d4960c7SMarc-André Lureau s->loc[c].state = TPM_TIS_STATE_IDLE; 1033edff8678SStefan Berger 10343d4960c7SMarc-André Lureau s->loc[c].w_offset = 0; 10353d4960c7SMarc-André Lureau s->loc[c].r_offset = 0; 1036edff8678SStefan Berger } 1037edff8678SStefan Berger 10381af3d63eSStefan Berger tpm_tis_do_startup_tpm(s, s->be_buffer_size); 1039edff8678SStefan Berger } 1040edff8678SStefan Berger 1041edff8678SStefan Berger static const VMStateDescription vmstate_tpm_tis = { 1042edff8678SStefan Berger .name = "tpm", 1043edff8678SStefan Berger .unmigratable = 1, 1044edff8678SStefan Berger }; 1045edff8678SStefan Berger 1046edff8678SStefan Berger static Property tpm_tis_properties[] = { 10473d4960c7SMarc-André Lureau DEFINE_PROP_UINT32("irq", TPMState, irq_num, TPM_TIS_IRQ), 1048c0378544SMarc-André Lureau DEFINE_PROP_TPMBE("tpmdev", TPMState, be_driver), 1049edff8678SStefan Berger DEFINE_PROP_END_OF_LIST(), 1050edff8678SStefan Berger }; 1051edff8678SStefan Berger 1052edff8678SStefan Berger static void tpm_tis_realizefn(DeviceState *dev, Error **errp) 1053edff8678SStefan Berger { 1054edff8678SStefan Berger TPMState *s = TPM(dev); 1055edff8678SStefan Berger 105651a837e9SMarc-André Lureau if (!tpm_find()) { 105751a837e9SMarc-André Lureau error_setg(errp, "at most one TPM device is permitted"); 105851a837e9SMarc-André Lureau return; 105951a837e9SMarc-André Lureau } 106051a837e9SMarc-André Lureau 1061edff8678SStefan Berger if (!s->be_driver) { 1062c0378544SMarc-André Lureau error_setg(errp, "'tpmdev' property is required"); 1063edff8678SStefan Berger return; 1064edff8678SStefan Berger } 10653d4960c7SMarc-André Lureau if (s->irq_num > 15) { 1066c87b35faSMarc-André Lureau error_setg(errp, "IRQ %d is outside valid range of 0 to 15", 1067c87b35faSMarc-André Lureau s->irq_num); 1068edff8678SStefan Berger return; 1069edff8678SStefan Berger } 1070edff8678SStefan Berger 10713d4960c7SMarc-André Lureau isa_init_irq(&s->busdev, &s->irq, s->irq_num); 10729dfd24edSStefan Berger 10739dfd24edSStefan Berger memory_region_add_subregion(isa_address_space(ISA_DEVICE(dev)), 10749dfd24edSStefan Berger TPM_TIS_ADDR_BASE, &s->mmio); 1075edff8678SStefan Berger } 1076edff8678SStefan Berger 1077edff8678SStefan Berger static void tpm_tis_initfn(Object *obj) 1078edff8678SStefan Berger { 1079edff8678SStefan Berger TPMState *s = TPM(obj); 1080edff8678SStefan Berger 1081853dca12SPaolo Bonzini memory_region_init_io(&s->mmio, OBJECT(s), &tpm_tis_memory_ops, 1082853dca12SPaolo Bonzini s, "tpm-tis-mmio", 1083edff8678SStefan Berger TPM_TIS_NUM_LOCALITIES << TPM_TIS_LOCALITY_SHIFT); 1084edff8678SStefan Berger } 1085edff8678SStefan Berger 1086edff8678SStefan Berger static void tpm_tis_class_init(ObjectClass *klass, void *data) 1087edff8678SStefan Berger { 1088edff8678SStefan Berger DeviceClass *dc = DEVICE_CLASS(klass); 108905a69998SMarc-André Lureau TPMIfClass *tc = TPM_IF_CLASS(klass); 1090edff8678SStefan Berger 1091edff8678SStefan Berger dc->realize = tpm_tis_realizefn; 1092edff8678SStefan Berger dc->props = tpm_tis_properties; 1093edff8678SStefan Berger dc->reset = tpm_tis_reset; 1094edff8678SStefan Berger dc->vmsd = &vmstate_tpm_tis; 1095191adc94SMarc-André Lureau tc->model = TPM_MODEL_TPM_TIS; 10969af7a721SMarc-André Lureau tc->get_version = tpm_tis_get_tpm_version; 109705a69998SMarc-André Lureau tc->request_completed = tpm_tis_request_completed; 1098edff8678SStefan Berger } 1099edff8678SStefan Berger 1100edff8678SStefan Berger static const TypeInfo tpm_tis_info = { 1101edff8678SStefan Berger .name = TYPE_TPM_TIS, 1102edff8678SStefan Berger .parent = TYPE_ISA_DEVICE, 1103edff8678SStefan Berger .instance_size = sizeof(TPMState), 1104edff8678SStefan Berger .instance_init = tpm_tis_initfn, 1105edff8678SStefan Berger .class_init = tpm_tis_class_init, 1106698f5daaSMarc-André Lureau .interfaces = (InterfaceInfo[]) { 1107698f5daaSMarc-André Lureau { TYPE_TPM_IF }, 1108698f5daaSMarc-André Lureau { } 1109698f5daaSMarc-André Lureau } 1110edff8678SStefan Berger }; 1111edff8678SStefan Berger 1112edff8678SStefan Berger static void tpm_tis_register(void) 1113edff8678SStefan Berger { 1114edff8678SStefan Berger type_register_static(&tpm_tis_info); 1115edff8678SStefan Berger } 1116edff8678SStefan Berger 1117edff8678SStefan Berger type_init(tpm_tis_register) 1118