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" 34fcbed221SStefan Berger #include "trace.h" 35732cd587SMarc-André Lureau 36732cd587SMarc-André Lureau #define TPM_TIS_NUM_LOCALITIES 5 /* per spec */ 37732cd587SMarc-André Lureau #define TPM_TIS_LOCALITY_SHIFT 12 38732cd587SMarc-André Lureau #define TPM_TIS_NO_LOCALITY 0xff 39732cd587SMarc-André Lureau 40732cd587SMarc-André Lureau #define TPM_TIS_IS_VALID_LOCTY(x) ((x) < TPM_TIS_NUM_LOCALITIES) 41732cd587SMarc-André Lureau 42732cd587SMarc-André Lureau #define TPM_TIS_BUFFER_MAX 4096 43732cd587SMarc-André Lureau 44732cd587SMarc-André Lureau typedef enum { 45732cd587SMarc-André Lureau TPM_TIS_STATE_IDLE = 0, 46732cd587SMarc-André Lureau TPM_TIS_STATE_READY, 47732cd587SMarc-André Lureau TPM_TIS_STATE_COMPLETION, 48732cd587SMarc-André Lureau TPM_TIS_STATE_EXECUTION, 49732cd587SMarc-André Lureau TPM_TIS_STATE_RECEPTION, 50732cd587SMarc-André Lureau } TPMTISState; 51732cd587SMarc-André Lureau 52732cd587SMarc-André Lureau /* locality data -- all fields are persisted */ 53732cd587SMarc-André Lureau typedef struct TPMLocality { 54732cd587SMarc-André Lureau TPMTISState state; 55732cd587SMarc-André Lureau uint8_t access; 56732cd587SMarc-André Lureau uint32_t sts; 57732cd587SMarc-André Lureau uint32_t iface_id; 58732cd587SMarc-André Lureau uint32_t inte; 59732cd587SMarc-André Lureau uint32_t ints; 60732cd587SMarc-André Lureau } TPMLocality; 61732cd587SMarc-André Lureau 6236e86589SMarc-André Lureau typedef struct TPMState { 633d4960c7SMarc-André Lureau ISADevice busdev; 643d4960c7SMarc-André Lureau MemoryRegion mmio; 653d4960c7SMarc-André Lureau 66c5496b97SStefan Berger unsigned char buffer[TPM_TIS_BUFFER_MAX]; 67f999d81bSStefan Berger uint16_t rw_offset; 68732cd587SMarc-André Lureau 69732cd587SMarc-André Lureau uint8_t active_locty; 70732cd587SMarc-André Lureau uint8_t aborting_locty; 71732cd587SMarc-André Lureau uint8_t next_locty; 72732cd587SMarc-André Lureau 73732cd587SMarc-André Lureau TPMLocality loc[TPM_TIS_NUM_LOCALITIES]; 74732cd587SMarc-André Lureau 75732cd587SMarc-André Lureau qemu_irq irq; 76732cd587SMarc-André Lureau uint32_t irq_num; 77732cd587SMarc-André Lureau 78732cd587SMarc-André Lureau TPMBackendCmd cmd; 79732cd587SMarc-André Lureau 80732cd587SMarc-André Lureau TPMBackend *be_driver; 81732cd587SMarc-André Lureau TPMVersion be_tpm_version; 82b21e6aafSStefan Berger 83b21e6aafSStefan Berger size_t be_buffer_size; 8436e86589SMarc-André Lureau } TPMState; 85732cd587SMarc-André Lureau 86732cd587SMarc-André Lureau #define TPM(obj) OBJECT_CHECK(TPMState, (obj), TYPE_TPM_TIS) 87edff8678SStefan Berger 884d1ba9c4SStefan Berger #define DEBUG_TIS 0 89edff8678SStefan Berger 908db7c415SStefan Berger /* local prototypes */ 918db7c415SStefan Berger 928db7c415SStefan Berger static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, 938db7c415SStefan Berger unsigned size); 948db7c415SStefan Berger 95edff8678SStefan Berger /* utility functions */ 96edff8678SStefan Berger 97edff8678SStefan Berger static uint8_t tpm_tis_locality_from_addr(hwaddr addr) 98edff8678SStefan Berger { 99edff8678SStefan Berger return (uint8_t)((addr >> TPM_TIS_LOCALITY_SHIFT) & 0x7); 100edff8678SStefan Berger } 101edff8678SStefan Berger 102e6b703f6SStefan Berger static void tpm_tis_show_buffer(const unsigned char *buffer, 103e6b703f6SStefan Berger size_t buffer_size, const char *string) 104edff8678SStefan Berger { 105edff8678SStefan Berger uint32_t len, i; 106edff8678SStefan Berger 107e6b703f6SStefan Berger len = MIN(tpm_cmd_get_size(buffer), buffer_size); 108fcbed221SStefan Berger printf("tpm_tis: %s length = %d\n", string, len); 109edff8678SStefan Berger for (i = 0; i < len; i++) { 110edff8678SStefan Berger if (i && !(i % 16)) { 111fcbed221SStefan Berger printf("\n"); 112edff8678SStefan Berger } 113fcbed221SStefan Berger printf("%.2X ", buffer[i]); 114edff8678SStefan Berger } 115fcbed221SStefan Berger printf("\n"); 116edff8678SStefan Berger } 117edff8678SStefan Berger 118edff8678SStefan Berger /* 119fd859081SStefan Berger * Set the given flags in the STS register by clearing the register but 120116694c3SStefan Berger * preserving the SELFTEST_DONE and TPM_FAMILY_MASK flags and then setting 121116694c3SStefan Berger * the new flags. 122fd859081SStefan Berger * 123fd859081SStefan Berger * The SELFTEST_DONE flag is acquired from the backend that determines it by 124fd859081SStefan Berger * peeking into TPM commands. 125fd859081SStefan Berger * 126fd859081SStefan Berger * A VM suspend/resume will preserve the flag by storing it into the VM 127fd859081SStefan Berger * device state, but the backend will not remember it when QEMU is started 128fd859081SStefan Berger * again. Therefore, we cache the flag here. Once set, it will not be unset 129fd859081SStefan Berger * except by a reset. 130fd859081SStefan Berger */ 131fd859081SStefan Berger static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags) 132fd859081SStefan Berger { 133116694c3SStefan Berger l->sts &= TPM_TIS_STS_SELFTEST_DONE | TPM_TIS_STS_TPM_FAMILY_MASK; 134fd859081SStefan Berger l->sts |= flags; 135fd859081SStefan Berger } 136fd859081SStefan Berger 137fd859081SStefan Berger /* 138edff8678SStefan Berger * Send a request to the TPM. 139edff8678SStefan Berger */ 140edff8678SStefan Berger static void tpm_tis_tpm_send(TPMState *s, uint8_t locty) 141edff8678SStefan Berger { 142fcbed221SStefan Berger if (DEBUG_TIS) { 143c5496b97SStefan Berger tpm_tis_show_buffer(s->buffer, s->be_buffer_size, 144e6b703f6SStefan Berger "tpm_tis: To TPM"); 145fcbed221SStefan Berger } 146edff8678SStefan Berger 147edff8678SStefan Berger /* 148f999d81bSStefan Berger * rw_offset serves as length indicator for length of data; 149edff8678SStefan Berger * it's reset when the response comes back 150edff8678SStefan Berger */ 1513d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_EXECUTION; 152edff8678SStefan Berger 1530e43b7e6SMarc-André Lureau s->cmd = (TPMBackendCmd) { 1540e43b7e6SMarc-André Lureau .locty = locty, 155c5496b97SStefan Berger .in = s->buffer, 156f999d81bSStefan Berger .in_len = s->rw_offset, 157c5496b97SStefan Berger .out = s->buffer, 158e6b703f6SStefan Berger .out_len = s->be_buffer_size, 1590e43b7e6SMarc-André Lureau }; 1600e43b7e6SMarc-André Lureau 1610e43b7e6SMarc-André Lureau tpm_backend_deliver_request(s->be_driver, &s->cmd); 162edff8678SStefan Berger } 163edff8678SStefan Berger 164edff8678SStefan Berger /* raise an interrupt if allowed */ 165edff8678SStefan Berger static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask) 166edff8678SStefan Berger { 167edff8678SStefan Berger if (!TPM_TIS_IS_VALID_LOCTY(locty)) { 168edff8678SStefan Berger return; 169edff8678SStefan Berger } 170edff8678SStefan Berger 1713d4960c7SMarc-André Lureau if ((s->loc[locty].inte & TPM_TIS_INT_ENABLED) && 1723d4960c7SMarc-André Lureau (s->loc[locty].inte & irqmask)) { 173fcbed221SStefan Berger trace_tpm_tis_raise_irq(irqmask); 1743d4960c7SMarc-André Lureau qemu_irq_raise(s->irq); 1753d4960c7SMarc-André Lureau s->loc[locty].ints |= irqmask; 176edff8678SStefan Berger } 177edff8678SStefan Berger } 178edff8678SStefan Berger 179edff8678SStefan Berger static uint32_t tpm_tis_check_request_use_except(TPMState *s, uint8_t locty) 180edff8678SStefan Berger { 181edff8678SStefan Berger uint8_t l; 182edff8678SStefan Berger 183edff8678SStefan Berger for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 184edff8678SStefan Berger if (l == locty) { 185edff8678SStefan Berger continue; 186edff8678SStefan Berger } 1873d4960c7SMarc-André Lureau if ((s->loc[l].access & TPM_TIS_ACCESS_REQUEST_USE)) { 188edff8678SStefan Berger return 1; 189edff8678SStefan Berger } 190edff8678SStefan Berger } 191edff8678SStefan Berger 192edff8678SStefan Berger return 0; 193edff8678SStefan Berger } 194edff8678SStefan Berger 195edff8678SStefan Berger static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty) 196edff8678SStefan Berger { 1973d4960c7SMarc-André Lureau bool change = (s->active_locty != new_active_locty); 198edff8678SStefan Berger bool is_seize; 199edff8678SStefan Berger uint8_t mask; 200edff8678SStefan Berger 2013d4960c7SMarc-André Lureau if (change && TPM_TIS_IS_VALID_LOCTY(s->active_locty)) { 202edff8678SStefan Berger is_seize = TPM_TIS_IS_VALID_LOCTY(new_active_locty) && 2033d4960c7SMarc-André Lureau s->loc[new_active_locty].access & TPM_TIS_ACCESS_SEIZE; 204edff8678SStefan Berger 205edff8678SStefan Berger if (is_seize) { 206edff8678SStefan Berger mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY); 207edff8678SStefan Berger } else { 208edff8678SStefan Berger mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY| 209edff8678SStefan Berger TPM_TIS_ACCESS_REQUEST_USE); 210edff8678SStefan Berger } 211edff8678SStefan Berger /* reset flags on the old active locality */ 2123d4960c7SMarc-André Lureau s->loc[s->active_locty].access &= mask; 213edff8678SStefan Berger 214edff8678SStefan Berger if (is_seize) { 2153d4960c7SMarc-André Lureau s->loc[s->active_locty].access |= TPM_TIS_ACCESS_BEEN_SEIZED; 216edff8678SStefan Berger } 217edff8678SStefan Berger } 218edff8678SStefan Berger 2193d4960c7SMarc-André Lureau s->active_locty = new_active_locty; 220edff8678SStefan Berger 221fcbed221SStefan Berger trace_tpm_tis_new_active_locality(s->active_locty); 222edff8678SStefan Berger 223edff8678SStefan Berger if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) { 224edff8678SStefan Berger /* set flags on the new active locality */ 2253d4960c7SMarc-André Lureau s->loc[new_active_locty].access |= TPM_TIS_ACCESS_ACTIVE_LOCALITY; 2263d4960c7SMarc-André Lureau s->loc[new_active_locty].access &= ~(TPM_TIS_ACCESS_REQUEST_USE | 227edff8678SStefan Berger TPM_TIS_ACCESS_SEIZE); 228edff8678SStefan Berger } 229edff8678SStefan Berger 230edff8678SStefan Berger if (change) { 2313d4960c7SMarc-André Lureau tpm_tis_raise_irq(s, s->active_locty, TPM_TIS_INT_LOCALITY_CHANGED); 232edff8678SStefan Berger } 233edff8678SStefan Berger } 234edff8678SStefan Berger 235edff8678SStefan Berger /* abort -- this function switches the locality */ 236*0f5faee3SStefan Berger static void tpm_tis_abort(TPMState *s) 237edff8678SStefan Berger { 238f999d81bSStefan Berger s->rw_offset = 0; 239edff8678SStefan Berger 240fcbed221SStefan Berger trace_tpm_tis_abort(s->next_locty); 241edff8678SStefan Berger 242edff8678SStefan Berger /* 243edff8678SStefan Berger * Need to react differently depending on who's aborting now and 244edff8678SStefan Berger * which locality will become active afterwards. 245edff8678SStefan Berger */ 2463d4960c7SMarc-André Lureau if (s->aborting_locty == s->next_locty) { 2473d4960c7SMarc-André Lureau s->loc[s->aborting_locty].state = TPM_TIS_STATE_READY; 2483d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[s->aborting_locty], 249fd859081SStefan Berger TPM_TIS_STS_COMMAND_READY); 2503d4960c7SMarc-André Lureau tpm_tis_raise_irq(s, s->aborting_locty, TPM_TIS_INT_COMMAND_READY); 251edff8678SStefan Berger } 252edff8678SStefan Berger 253edff8678SStefan Berger /* locality after abort is another one than the current one */ 2543d4960c7SMarc-André Lureau tpm_tis_new_active_locality(s, s->next_locty); 255edff8678SStefan Berger 2563d4960c7SMarc-André Lureau s->next_locty = TPM_TIS_NO_LOCALITY; 257edff8678SStefan Berger /* nobody's aborting a command anymore */ 2583d4960c7SMarc-André Lureau s->aborting_locty = TPM_TIS_NO_LOCALITY; 259edff8678SStefan Berger } 260edff8678SStefan Berger 261edff8678SStefan Berger /* prepare aborting current command */ 262edff8678SStefan Berger static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty) 263edff8678SStefan Berger { 264edff8678SStefan Berger uint8_t busy_locty; 265edff8678SStefan Berger 2663d4960c7SMarc-André Lureau s->aborting_locty = locty; 2673d4960c7SMarc-André Lureau s->next_locty = newlocty; /* locality after successful abort */ 268edff8678SStefan Berger 269edff8678SStefan Berger /* 270edff8678SStefan Berger * only abort a command using an interrupt if currently executing 271edff8678SStefan Berger * a command AND if there's a valid connection to the vTPM. 272edff8678SStefan Berger */ 273edff8678SStefan Berger for (busy_locty = 0; busy_locty < TPM_TIS_NUM_LOCALITIES; busy_locty++) { 2743d4960c7SMarc-André Lureau if (s->loc[busy_locty].state == TPM_TIS_STATE_EXECUTION) { 275edff8678SStefan Berger /* 276edff8678SStefan Berger * request the backend to cancel. Some backends may not 277edff8678SStefan Berger * support it 278edff8678SStefan Berger */ 2798f0605ccSStefan Berger tpm_backend_cancel_cmd(s->be_driver); 280edff8678SStefan Berger return; 281edff8678SStefan Berger } 282edff8678SStefan Berger } 283edff8678SStefan Berger 284*0f5faee3SStefan Berger tpm_tis_abort(s); 285edff8678SStefan Berger } 286edff8678SStefan Berger 28768999059SMarc-André Lureau /* 28868999059SMarc-André Lureau * Callback from the TPM to indicate that the response was received. 28968999059SMarc-André Lureau */ 2906a8a2354SMarc-André Lureau static void tpm_tis_request_completed(TPMIf *ti, int ret) 291edff8678SStefan Berger { 29268999059SMarc-André Lureau TPMState *s = TPM(ti); 2930e43b7e6SMarc-André Lureau uint8_t locty = s->cmd.locty; 29468999059SMarc-André Lureau uint8_t l; 29568999059SMarc-André Lureau 29668999059SMarc-André Lureau if (s->cmd.selftest_done) { 29768999059SMarc-André Lureau for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 2986a50bb98SPrasad J Pandit s->loc[l].sts |= TPM_TIS_STS_SELFTEST_DONE; 29968999059SMarc-André Lureau } 30068999059SMarc-André Lureau } 301edff8678SStefan Berger 3026a8a2354SMarc-André Lureau /* FIXME: report error if ret != 0 */ 3033d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 304fd859081SStefan Berger TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE); 3053d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_COMPLETION; 306f999d81bSStefan Berger s->rw_offset = 0; 307edff8678SStefan Berger 308fcbed221SStefan Berger if (DEBUG_TIS) { 309c5496b97SStefan Berger tpm_tis_show_buffer(s->buffer, s->be_buffer_size, 310e6b703f6SStefan Berger "tpm_tis: From TPM"); 311fcbed221SStefan Berger } 312298d8b81SStefan Berger 3133d4960c7SMarc-André Lureau if (TPM_TIS_IS_VALID_LOCTY(s->next_locty)) { 314*0f5faee3SStefan Berger tpm_tis_abort(s); 315edff8678SStefan Berger } 316edff8678SStefan Berger 317edff8678SStefan Berger tpm_tis_raise_irq(s, locty, 318edff8678SStefan Berger TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID); 319edff8678SStefan Berger } 320edff8678SStefan Berger 321edff8678SStefan Berger /* 322edff8678SStefan Berger * Read a byte of response data 323edff8678SStefan Berger */ 324edff8678SStefan Berger static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty) 325edff8678SStefan Berger { 326edff8678SStefan Berger uint32_t ret = TPM_TIS_NO_DATA_BYTE; 327edff8678SStefan Berger uint16_t len; 328edff8678SStefan Berger 3293d4960c7SMarc-André Lureau if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) { 330c5496b97SStefan Berger len = MIN(tpm_cmd_get_size(&s->buffer), 331e6b703f6SStefan Berger s->be_buffer_size); 332edff8678SStefan Berger 333f999d81bSStefan Berger ret = s->buffer[s->rw_offset++]; 334f999d81bSStefan Berger if (s->rw_offset >= len) { 335edff8678SStefan Berger /* got last byte */ 3363d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 337edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); 338edff8678SStefan Berger } 339fcbed221SStefan Berger trace_tpm_tis_data_read(ret, s->rw_offset - 1); 340edff8678SStefan Berger } 341edff8678SStefan Berger 342edff8678SStefan Berger return ret; 343edff8678SStefan Berger } 344edff8678SStefan Berger 3458db7c415SStefan Berger #ifdef DEBUG_TIS 3468db7c415SStefan Berger static void tpm_tis_dump_state(void *opaque, hwaddr addr) 3478db7c415SStefan Berger { 3488db7c415SStefan Berger static const unsigned regs[] = { 3498db7c415SStefan Berger TPM_TIS_REG_ACCESS, 3508db7c415SStefan Berger TPM_TIS_REG_INT_ENABLE, 3518db7c415SStefan Berger TPM_TIS_REG_INT_VECTOR, 3528db7c415SStefan Berger TPM_TIS_REG_INT_STATUS, 3538db7c415SStefan Berger TPM_TIS_REG_INTF_CAPABILITY, 3548db7c415SStefan Berger TPM_TIS_REG_STS, 3558db7c415SStefan Berger TPM_TIS_REG_DID_VID, 3568db7c415SStefan Berger TPM_TIS_REG_RID, 3578db7c415SStefan Berger 0xfff}; 3588db7c415SStefan Berger int idx; 3598db7c415SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 3608db7c415SStefan Berger hwaddr base = addr & ~0xfff; 3618db7c415SStefan Berger TPMState *s = opaque; 3628db7c415SStefan Berger 363fcbed221SStefan Berger printf("tpm_tis: active locality : %d\n" 3648db7c415SStefan Berger "tpm_tis: state of locality %d : %d\n" 3658db7c415SStefan Berger "tpm_tis: register dump:\n", 3663d4960c7SMarc-André Lureau s->active_locty, 3673d4960c7SMarc-André Lureau locty, s->loc[locty].state); 3688db7c415SStefan Berger 3698db7c415SStefan Berger for (idx = 0; regs[idx] != 0xfff; idx++) { 370fcbed221SStefan Berger printf("tpm_tis: 0x%04x : 0x%08x\n", regs[idx], 371070c7607SStefan Berger (int)tpm_tis_mmio_read(opaque, base + regs[idx], 4)); 3728db7c415SStefan Berger } 3738db7c415SStefan Berger 374fcbed221SStefan Berger printf("tpm_tis: r/w offset : %d\n" 3758db7c415SStefan Berger "tpm_tis: result buffer : ", 376f999d81bSStefan Berger s->rw_offset); 3778db7c415SStefan Berger for (idx = 0; 378c5496b97SStefan Berger idx < MIN(tpm_cmd_get_size(&s->buffer), s->be_buffer_size); 3798db7c415SStefan Berger idx++) { 380fcbed221SStefan Berger printf("%c%02x%s", 381f999d81bSStefan Berger s->rw_offset == idx ? '>' : ' ', 382c5496b97SStefan Berger s->buffer[idx], 3838db7c415SStefan Berger ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : ""); 3848db7c415SStefan Berger } 385fcbed221SStefan Berger printf("\n"); 3868db7c415SStefan Berger } 3878db7c415SStefan Berger #endif 3888db7c415SStefan Berger 389edff8678SStefan Berger /* 390edff8678SStefan Berger * Read a register of the TIS interface 391edff8678SStefan Berger * See specs pages 33-63 for description of the registers 392edff8678SStefan Berger */ 393edff8678SStefan Berger static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, 394edff8678SStefan Berger unsigned size) 395edff8678SStefan Berger { 396edff8678SStefan Berger TPMState *s = opaque; 397edff8678SStefan Berger uint16_t offset = addr & 0xffc; 398edff8678SStefan Berger uint8_t shift = (addr & 0x3) * 8; 399edff8678SStefan Berger uint32_t val = 0xffffffff; 400edff8678SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 401edff8678SStefan Berger uint32_t avail; 402feeb755fSStefan Berger uint8_t v; 403edff8678SStefan Berger 4048f0605ccSStefan Berger if (tpm_backend_had_startup_error(s->be_driver)) { 4056cd65969SStefan Berger return 0; 406edff8678SStefan Berger } 407edff8678SStefan Berger 408edff8678SStefan Berger switch (offset) { 409edff8678SStefan Berger case TPM_TIS_REG_ACCESS: 410edff8678SStefan Berger /* never show the SEIZE flag even though we use it internally */ 4113d4960c7SMarc-André Lureau val = s->loc[locty].access & ~TPM_TIS_ACCESS_SEIZE; 412edff8678SStefan Berger /* the pending flag is always calculated */ 413edff8678SStefan Berger if (tpm_tis_check_request_use_except(s, locty)) { 414edff8678SStefan Berger val |= TPM_TIS_ACCESS_PENDING_REQUEST; 415edff8678SStefan Berger } 4168f0605ccSStefan Berger val |= !tpm_backend_get_tpm_established_flag(s->be_driver); 417edff8678SStefan Berger break; 418edff8678SStefan Berger case TPM_TIS_REG_INT_ENABLE: 4193d4960c7SMarc-André Lureau val = s->loc[locty].inte; 420edff8678SStefan Berger break; 421edff8678SStefan Berger case TPM_TIS_REG_INT_VECTOR: 4223d4960c7SMarc-André Lureau val = s->irq_num; 423edff8678SStefan Berger break; 424edff8678SStefan Berger case TPM_TIS_REG_INT_STATUS: 4253d4960c7SMarc-André Lureau val = s->loc[locty].ints; 426edff8678SStefan Berger break; 427edff8678SStefan Berger case TPM_TIS_REG_INTF_CAPABILITY: 428116694c3SStefan Berger switch (s->be_tpm_version) { 429116694c3SStefan Berger case TPM_VERSION_UNSPEC: 430116694c3SStefan Berger val = 0; 431116694c3SStefan Berger break; 432116694c3SStefan Berger case TPM_VERSION_1_2: 433116694c3SStefan Berger val = TPM_TIS_CAPABILITIES_SUPPORTED1_3; 434116694c3SStefan Berger break; 435116694c3SStefan Berger case TPM_VERSION_2_0: 436116694c3SStefan Berger val = TPM_TIS_CAPABILITIES_SUPPORTED2_0; 437116694c3SStefan Berger break; 438116694c3SStefan Berger } 439edff8678SStefan Berger break; 440edff8678SStefan Berger case TPM_TIS_REG_STS: 4413d4960c7SMarc-André Lureau if (s->active_locty == locty) { 4423d4960c7SMarc-André Lureau if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) { 443edff8678SStefan Berger val = TPM_TIS_BURST_COUNT( 444c5496b97SStefan Berger MIN(tpm_cmd_get_size(&s->buffer), 445e6b703f6SStefan Berger s->be_buffer_size) 446f999d81bSStefan Berger - s->rw_offset) | s->loc[locty].sts; 447edff8678SStefan Berger } else { 448f999d81bSStefan Berger avail = s->be_buffer_size - s->rw_offset; 449edff8678SStefan Berger /* 450edff8678SStefan Berger * byte-sized reads should not return 0x00 for 0x100 451edff8678SStefan Berger * available bytes. 452edff8678SStefan Berger */ 453edff8678SStefan Berger if (size == 1 && avail > 0xff) { 454edff8678SStefan Berger avail = 0xff; 455edff8678SStefan Berger } 4563d4960c7SMarc-André Lureau val = TPM_TIS_BURST_COUNT(avail) | s->loc[locty].sts; 457edff8678SStefan Berger } 458edff8678SStefan Berger } 459edff8678SStefan Berger break; 460edff8678SStefan Berger case TPM_TIS_REG_DATA_FIFO: 4612eae8c75SStefan Berger case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END: 4623d4960c7SMarc-André Lureau if (s->active_locty == locty) { 463feeb755fSStefan Berger if (size > 4 - (addr & 0x3)) { 464feeb755fSStefan Berger /* prevent access beyond FIFO */ 465feeb755fSStefan Berger size = 4 - (addr & 0x3); 466feeb755fSStefan Berger } 467feeb755fSStefan Berger val = 0; 468feeb755fSStefan Berger shift = 0; 469feeb755fSStefan Berger while (size > 0) { 4703d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 471edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 472feeb755fSStefan Berger v = tpm_tis_data_read(s, locty); 473edff8678SStefan Berger break; 474edff8678SStefan Berger default: 475feeb755fSStefan Berger v = TPM_TIS_NO_DATA_BYTE; 476edff8678SStefan Berger break; 477edff8678SStefan Berger } 478feeb755fSStefan Berger val |= (v << shift); 479feeb755fSStefan Berger shift += 8; 480feeb755fSStefan Berger size--; 481feeb755fSStefan Berger } 482feeb755fSStefan Berger shift = 0; /* no more adjustments */ 483edff8678SStefan Berger } 484edff8678SStefan Berger break; 485116694c3SStefan Berger case TPM_TIS_REG_INTERFACE_ID: 4863d4960c7SMarc-André Lureau val = s->loc[locty].iface_id; 487116694c3SStefan Berger break; 488edff8678SStefan Berger case TPM_TIS_REG_DID_VID: 489edff8678SStefan Berger val = (TPM_TIS_TPM_DID << 16) | TPM_TIS_TPM_VID; 490edff8678SStefan Berger break; 491edff8678SStefan Berger case TPM_TIS_REG_RID: 492edff8678SStefan Berger val = TPM_TIS_TPM_RID; 493edff8678SStefan Berger break; 4948db7c415SStefan Berger #ifdef DEBUG_TIS 4958db7c415SStefan Berger case TPM_TIS_REG_DEBUG: 4968db7c415SStefan Berger tpm_tis_dump_state(opaque, addr); 4978db7c415SStefan Berger break; 4988db7c415SStefan Berger #endif 499edff8678SStefan Berger } 500edff8678SStefan Berger 501edff8678SStefan Berger if (shift) { 502edff8678SStefan Berger val >>= shift; 503edff8678SStefan Berger } 504edff8678SStefan Berger 505fcbed221SStefan Berger trace_tpm_tis_mmio_read(size, addr, val); 506edff8678SStefan Berger 507edff8678SStefan Berger return val; 508edff8678SStefan Berger } 509edff8678SStefan Berger 510edff8678SStefan Berger /* 511edff8678SStefan Berger * Write a value to a register of the TIS interface 512edff8678SStefan Berger * See specs pages 33-63 for description of the registers 513edff8678SStefan Berger */ 514ff2bc0c1SMarc-André Lureau static void tpm_tis_mmio_write(void *opaque, hwaddr addr, 515ff2bc0c1SMarc-André Lureau uint64_t val, unsigned size) 516edff8678SStefan Berger { 517edff8678SStefan Berger TPMState *s = opaque; 518feeb755fSStefan Berger uint16_t off = addr & 0xffc; 519feeb755fSStefan Berger uint8_t shift = (addr & 0x3) * 8; 520edff8678SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 521edff8678SStefan Berger uint8_t active_locty, l; 522edff8678SStefan Berger int c, set_new_locty = 1; 523edff8678SStefan Berger uint16_t len; 524feeb755fSStefan Berger uint32_t mask = (size == 1) ? 0xff : ((size == 2) ? 0xffff : ~0); 525edff8678SStefan Berger 526fcbed221SStefan Berger trace_tpm_tis_mmio_write(size, addr, val); 527edff8678SStefan Berger 528ff2bc0c1SMarc-André Lureau if (locty == 4) { 529fcbed221SStefan Berger trace_tpm_tis_mmio_write_locty4(); 530edff8678SStefan Berger return; 531edff8678SStefan Berger } 532edff8678SStefan Berger 5338f0605ccSStefan Berger if (tpm_backend_had_startup_error(s->be_driver)) { 534edff8678SStefan Berger return; 535edff8678SStefan Berger } 536edff8678SStefan Berger 537feeb755fSStefan Berger val &= mask; 538feeb755fSStefan Berger 539feeb755fSStefan Berger if (shift) { 540feeb755fSStefan Berger val <<= shift; 541feeb755fSStefan Berger mask <<= shift; 542feeb755fSStefan Berger } 543feeb755fSStefan Berger 544feeb755fSStefan Berger mask ^= 0xffffffff; 545feeb755fSStefan Berger 546edff8678SStefan Berger switch (off) { 547edff8678SStefan Berger case TPM_TIS_REG_ACCESS: 548edff8678SStefan Berger 549edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_SEIZE)) { 550edff8678SStefan Berger val &= ~(TPM_TIS_ACCESS_REQUEST_USE | 551edff8678SStefan Berger TPM_TIS_ACCESS_ACTIVE_LOCALITY); 552edff8678SStefan Berger } 553edff8678SStefan Berger 5543d4960c7SMarc-André Lureau active_locty = s->active_locty; 555edff8678SStefan Berger 556edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) { 557edff8678SStefan Berger /* give up locality if currently owned */ 5583d4960c7SMarc-André Lureau if (s->active_locty == locty) { 559fcbed221SStefan Berger trace_tpm_tis_mmio_write_release_locty(locty); 560edff8678SStefan Berger 561edff8678SStefan Berger uint8_t newlocty = TPM_TIS_NO_LOCALITY; 562edff8678SStefan Berger /* anybody wants the locality ? */ 563edff8678SStefan Berger for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) { 5643d4960c7SMarc-André Lureau if ((s->loc[c].access & TPM_TIS_ACCESS_REQUEST_USE)) { 565fcbed221SStefan Berger trace_tpm_tis_mmio_write_locty_req_use(c); 566edff8678SStefan Berger newlocty = c; 567edff8678SStefan Berger break; 568edff8678SStefan Berger } 569edff8678SStefan Berger } 570fcbed221SStefan Berger trace_tpm_tis_mmio_write_next_locty(newlocty); 571edff8678SStefan Berger 572edff8678SStefan Berger if (TPM_TIS_IS_VALID_LOCTY(newlocty)) { 573edff8678SStefan Berger set_new_locty = 0; 574edff8678SStefan Berger tpm_tis_prep_abort(s, locty, newlocty); 575edff8678SStefan Berger } else { 576edff8678SStefan Berger active_locty = TPM_TIS_NO_LOCALITY; 577edff8678SStefan Berger } 578edff8678SStefan Berger } else { 579edff8678SStefan Berger /* not currently the owner; clear a pending request */ 5803d4960c7SMarc-André Lureau s->loc[locty].access &= ~TPM_TIS_ACCESS_REQUEST_USE; 581edff8678SStefan Berger } 582edff8678SStefan Berger } 583edff8678SStefan Berger 584edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_BEEN_SEIZED)) { 5853d4960c7SMarc-André Lureau s->loc[locty].access &= ~TPM_TIS_ACCESS_BEEN_SEIZED; 586edff8678SStefan Berger } 587edff8678SStefan Berger 588edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_SEIZE)) { 589edff8678SStefan Berger /* 590edff8678SStefan Berger * allow seize if a locality is active and the requesting 591edff8678SStefan Berger * locality is higher than the one that's active 592edff8678SStefan Berger * OR 593edff8678SStefan Berger * allow seize for requesting locality if no locality is 594edff8678SStefan Berger * active 595edff8678SStefan Berger */ 5963d4960c7SMarc-André Lureau while ((TPM_TIS_IS_VALID_LOCTY(s->active_locty) && 5973d4960c7SMarc-André Lureau locty > s->active_locty) || 5983d4960c7SMarc-André Lureau !TPM_TIS_IS_VALID_LOCTY(s->active_locty)) { 599edff8678SStefan Berger bool higher_seize = FALSE; 600edff8678SStefan Berger 601edff8678SStefan Berger /* already a pending SEIZE ? */ 6023d4960c7SMarc-André Lureau if ((s->loc[locty].access & TPM_TIS_ACCESS_SEIZE)) { 603edff8678SStefan Berger break; 604edff8678SStefan Berger } 605edff8678SStefan Berger 606edff8678SStefan Berger /* check for ongoing seize by a higher locality */ 607edff8678SStefan Berger for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES; l++) { 6083d4960c7SMarc-André Lureau if ((s->loc[l].access & TPM_TIS_ACCESS_SEIZE)) { 609edff8678SStefan Berger higher_seize = TRUE; 610edff8678SStefan Berger break; 611edff8678SStefan Berger } 612edff8678SStefan Berger } 613edff8678SStefan Berger 614edff8678SStefan Berger if (higher_seize) { 615edff8678SStefan Berger break; 616edff8678SStefan Berger } 617edff8678SStefan Berger 618edff8678SStefan Berger /* cancel any seize by a lower locality */ 619edff8678SStefan Berger for (l = 0; l < locty - 1; l++) { 6203d4960c7SMarc-André Lureau s->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE; 621edff8678SStefan Berger } 622edff8678SStefan Berger 6233d4960c7SMarc-André Lureau s->loc[locty].access |= TPM_TIS_ACCESS_SEIZE; 624fcbed221SStefan Berger 625fcbed221SStefan Berger trace_tpm_tis_mmio_write_locty_seized(locty, s->active_locty); 626fcbed221SStefan Berger trace_tpm_tis_mmio_write_init_abort(); 627fcbed221SStefan Berger 628edff8678SStefan Berger set_new_locty = 0; 6293d4960c7SMarc-André Lureau tpm_tis_prep_abort(s, s->active_locty, locty); 630edff8678SStefan Berger break; 631edff8678SStefan Berger } 632edff8678SStefan Berger } 633edff8678SStefan Berger 634edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_REQUEST_USE)) { 6353d4960c7SMarc-André Lureau if (s->active_locty != locty) { 6363d4960c7SMarc-André Lureau if (TPM_TIS_IS_VALID_LOCTY(s->active_locty)) { 6373d4960c7SMarc-André Lureau s->loc[locty].access |= TPM_TIS_ACCESS_REQUEST_USE; 638edff8678SStefan Berger } else { 639edff8678SStefan Berger /* no locality active -> make this one active now */ 640edff8678SStefan Berger active_locty = locty; 641edff8678SStefan Berger } 642edff8678SStefan Berger } 643edff8678SStefan Berger } 644edff8678SStefan Berger 645edff8678SStefan Berger if (set_new_locty) { 646edff8678SStefan Berger tpm_tis_new_active_locality(s, active_locty); 647edff8678SStefan Berger } 648edff8678SStefan Berger 649edff8678SStefan Berger break; 650edff8678SStefan Berger case TPM_TIS_REG_INT_ENABLE: 6513d4960c7SMarc-André Lureau if (s->active_locty != locty) { 652edff8678SStefan Berger break; 653edff8678SStefan Berger } 654edff8678SStefan Berger 6553d4960c7SMarc-André Lureau s->loc[locty].inte &= mask; 6563d4960c7SMarc-André Lureau s->loc[locty].inte |= (val & (TPM_TIS_INT_ENABLED | 657edff8678SStefan Berger TPM_TIS_INT_POLARITY_MASK | 658edff8678SStefan Berger TPM_TIS_INTERRUPTS_SUPPORTED)); 659edff8678SStefan Berger break; 660edff8678SStefan Berger case TPM_TIS_REG_INT_VECTOR: 661edff8678SStefan Berger /* hard wired -- ignore */ 662edff8678SStefan Berger break; 663edff8678SStefan Berger case TPM_TIS_REG_INT_STATUS: 6643d4960c7SMarc-André Lureau if (s->active_locty != locty) { 665edff8678SStefan Berger break; 666edff8678SStefan Berger } 667edff8678SStefan Berger 668edff8678SStefan Berger /* clearing of interrupt flags */ 669edff8678SStefan Berger if (((val & TPM_TIS_INTERRUPTS_SUPPORTED)) && 6703d4960c7SMarc-André Lureau (s->loc[locty].ints & TPM_TIS_INTERRUPTS_SUPPORTED)) { 6713d4960c7SMarc-André Lureau s->loc[locty].ints &= ~val; 6723d4960c7SMarc-André Lureau if (s->loc[locty].ints == 0) { 6733d4960c7SMarc-André Lureau qemu_irq_lower(s->irq); 674fcbed221SStefan Berger trace_tpm_tis_mmio_write_lowering_irq(); 675edff8678SStefan Berger } 676edff8678SStefan Berger } 6773d4960c7SMarc-André Lureau s->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED); 678edff8678SStefan Berger break; 679edff8678SStefan Berger case TPM_TIS_REG_STS: 6803d4960c7SMarc-André Lureau if (s->active_locty != locty) { 681edff8678SStefan Berger break; 682edff8678SStefan Berger } 683edff8678SStefan Berger 684116694c3SStefan Berger if (s->be_tpm_version == TPM_VERSION_2_0) { 685116694c3SStefan Berger /* some flags that are only supported for TPM 2 */ 686116694c3SStefan Berger if (val & TPM_TIS_STS_COMMAND_CANCEL) { 6873d4960c7SMarc-André Lureau if (s->loc[locty].state == TPM_TIS_STATE_EXECUTION) { 688116694c3SStefan Berger /* 689116694c3SStefan Berger * request the backend to cancel. Some backends may not 690116694c3SStefan Berger * support it 691116694c3SStefan Berger */ 692116694c3SStefan Berger tpm_backend_cancel_cmd(s->be_driver); 693116694c3SStefan Berger } 694116694c3SStefan Berger } 695116694c3SStefan Berger 696116694c3SStefan Berger if (val & TPM_TIS_STS_RESET_ESTABLISHMENT_BIT) { 697116694c3SStefan Berger if (locty == 3 || locty == 4) { 698116694c3SStefan Berger tpm_backend_reset_tpm_established_flag(s->be_driver, locty); 699116694c3SStefan Berger } 700116694c3SStefan Berger } 701116694c3SStefan Berger } 702116694c3SStefan Berger 703edff8678SStefan Berger val &= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO | 704edff8678SStefan Berger TPM_TIS_STS_RESPONSE_RETRY); 705edff8678SStefan Berger 706edff8678SStefan Berger if (val == TPM_TIS_STS_COMMAND_READY) { 7073d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 708edff8678SStefan Berger 709edff8678SStefan Berger case TPM_TIS_STATE_READY: 710f999d81bSStefan Berger s->rw_offset = 0; 711edff8678SStefan Berger break; 712edff8678SStefan Berger 713edff8678SStefan Berger case TPM_TIS_STATE_IDLE: 7143d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_COMMAND_READY); 7153d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_READY; 716edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); 717edff8678SStefan Berger break; 718edff8678SStefan Berger 719edff8678SStefan Berger case TPM_TIS_STATE_EXECUTION: 720edff8678SStefan Berger case TPM_TIS_STATE_RECEPTION: 721edff8678SStefan Berger /* abort currently running command */ 722fcbed221SStefan Berger trace_tpm_tis_mmio_write_init_abort(); 723edff8678SStefan Berger tpm_tis_prep_abort(s, locty, locty); 724edff8678SStefan Berger break; 725edff8678SStefan Berger 726edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 727f999d81bSStefan Berger s->rw_offset = 0; 728edff8678SStefan Berger /* shortcut to ready state with C/R set */ 7293d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_READY; 7303d4960c7SMarc-André Lureau if (!(s->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) { 7313d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 732fd859081SStefan Berger TPM_TIS_STS_COMMAND_READY); 733edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); 734edff8678SStefan Berger } 7353d4960c7SMarc-André Lureau s->loc[locty].sts &= ~(TPM_TIS_STS_DATA_AVAILABLE); 736edff8678SStefan Berger break; 737edff8678SStefan Berger 738edff8678SStefan Berger } 739edff8678SStefan Berger } else if (val == TPM_TIS_STS_TPM_GO) { 7403d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 741edff8678SStefan Berger case TPM_TIS_STATE_RECEPTION: 7423d4960c7SMarc-André Lureau if ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) == 0) { 743edff8678SStefan Berger tpm_tis_tpm_send(s, locty); 744edff8678SStefan Berger } 745edff8678SStefan Berger break; 746edff8678SStefan Berger default: 747edff8678SStefan Berger /* ignore */ 748edff8678SStefan Berger break; 749edff8678SStefan Berger } 750edff8678SStefan Berger } else if (val == TPM_TIS_STS_RESPONSE_RETRY) { 7513d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 752edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 753f999d81bSStefan Berger s->rw_offset = 0; 7543d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 755fd859081SStefan Berger TPM_TIS_STS_VALID| 756fd859081SStefan Berger TPM_TIS_STS_DATA_AVAILABLE); 757edff8678SStefan Berger break; 758edff8678SStefan Berger default: 759edff8678SStefan Berger /* ignore */ 760edff8678SStefan Berger break; 761edff8678SStefan Berger } 762edff8678SStefan Berger } 763edff8678SStefan Berger break; 764edff8678SStefan Berger case TPM_TIS_REG_DATA_FIFO: 7652eae8c75SStefan Berger case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END: 766edff8678SStefan Berger /* data fifo */ 7673d4960c7SMarc-André Lureau if (s->active_locty != locty) { 768edff8678SStefan Berger break; 769edff8678SStefan Berger } 770edff8678SStefan Berger 7713d4960c7SMarc-André Lureau if (s->loc[locty].state == TPM_TIS_STATE_IDLE || 7723d4960c7SMarc-André Lureau s->loc[locty].state == TPM_TIS_STATE_EXECUTION || 7733d4960c7SMarc-André Lureau s->loc[locty].state == TPM_TIS_STATE_COMPLETION) { 774edff8678SStefan Berger /* drop the byte */ 775edff8678SStefan Berger } else { 776fcbed221SStefan Berger trace_tpm_tis_mmio_write_data2send(val, size); 7773d4960c7SMarc-André Lureau if (s->loc[locty].state == TPM_TIS_STATE_READY) { 7783d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_RECEPTION; 7793d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 780fd859081SStefan Berger TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); 781edff8678SStefan Berger } 782edff8678SStefan Berger 783feeb755fSStefan Berger val >>= shift; 784feeb755fSStefan Berger if (size > 4 - (addr & 0x3)) { 785feeb755fSStefan Berger /* prevent access beyond FIFO */ 786feeb755fSStefan Berger size = 4 - (addr & 0x3); 787feeb755fSStefan Berger } 788feeb755fSStefan Berger 7893d4960c7SMarc-André Lureau while ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) && size > 0) { 790f999d81bSStefan Berger if (s->rw_offset < s->be_buffer_size) { 791f999d81bSStefan Berger s->buffer[s->rw_offset++] = 792e6b703f6SStefan Berger (uint8_t)val; 793feeb755fSStefan Berger val >>= 8; 794feeb755fSStefan Berger size--; 795edff8678SStefan Berger } else { 7963d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 797edff8678SStefan Berger } 798edff8678SStefan Berger } 799edff8678SStefan Berger 800edff8678SStefan Berger /* check for complete packet */ 801f999d81bSStefan Berger if (s->rw_offset > 5 && 8023d4960c7SMarc-André Lureau (s->loc[locty].sts & TPM_TIS_STS_EXPECT)) { 803edff8678SStefan Berger /* we have a packet length - see if we have all of it */ 8043d4960c7SMarc-André Lureau bool need_irq = !(s->loc[locty].sts & TPM_TIS_STS_VALID); 805d8383d61SMarc-André Lureau 806c5496b97SStefan Berger len = tpm_cmd_get_size(&s->buffer); 807f999d81bSStefan Berger if (len > s->rw_offset) { 8083d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 809fd859081SStefan Berger TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); 810edff8678SStefan Berger } else { 811edff8678SStefan Berger /* packet complete */ 8123d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 813edff8678SStefan Berger } 81429b558d8SStefan Berger if (need_irq) { 815edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); 816edff8678SStefan Berger } 817edff8678SStefan Berger } 818edff8678SStefan Berger } 819edff8678SStefan Berger break; 820116694c3SStefan Berger case TPM_TIS_REG_INTERFACE_ID: 821116694c3SStefan Berger if (val & TPM_TIS_IFACE_ID_INT_SEL_LOCK) { 822116694c3SStefan Berger for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 8233d4960c7SMarc-André Lureau s->loc[l].iface_id |= TPM_TIS_IFACE_ID_INT_SEL_LOCK; 824116694c3SStefan Berger } 825116694c3SStefan Berger } 826116694c3SStefan Berger break; 827edff8678SStefan Berger } 828edff8678SStefan Berger } 829edff8678SStefan Berger 830edff8678SStefan Berger static const MemoryRegionOps tpm_tis_memory_ops = { 831edff8678SStefan Berger .read = tpm_tis_mmio_read, 832edff8678SStefan Berger .write = tpm_tis_mmio_write, 833edff8678SStefan Berger .endianness = DEVICE_LITTLE_ENDIAN, 834edff8678SStefan Berger .valid = { 835edff8678SStefan Berger .min_access_size = 1, 836edff8678SStefan Berger .max_access_size = 4, 837edff8678SStefan Berger }, 838edff8678SStefan Berger }; 839edff8678SStefan Berger 840edff8678SStefan Berger /* 8415cb18b3dSStefan Berger * Get the TPMVersion of the backend device being used 8425cb18b3dSStefan Berger */ 8439af7a721SMarc-André Lureau static enum TPMVersion tpm_tis_get_tpm_version(TPMIf *ti) 8445cb18b3dSStefan Berger { 8459af7a721SMarc-André Lureau TPMState *s = TPM(ti); 8465cb18b3dSStefan Berger 847ad4aca69SStefan Berger if (tpm_backend_had_startup_error(s->be_driver)) { 848ad4aca69SStefan Berger return TPM_VERSION_UNSPEC; 849ad4aca69SStefan Berger } 850ad4aca69SStefan Berger 8515cb18b3dSStefan Berger return tpm_backend_get_tpm_version(s->be_driver); 8525cb18b3dSStefan Berger } 8535cb18b3dSStefan Berger 8545cb18b3dSStefan Berger /* 855edff8678SStefan Berger * This function is called when the machine starts, resets or due to 856edff8678SStefan Berger * S3 resume. 857edff8678SStefan Berger */ 858edff8678SStefan Berger static void tpm_tis_reset(DeviceState *dev) 859edff8678SStefan Berger { 860edff8678SStefan Berger TPMState *s = TPM(dev); 861edff8678SStefan Berger int c; 862edff8678SStefan Berger 863116694c3SStefan Berger s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver); 8641af3d63eSStefan Berger s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->be_driver), 8651af3d63eSStefan Berger TPM_TIS_BUFFER_MAX); 866116694c3SStefan Berger 8678f0605ccSStefan Berger tpm_backend_reset(s->be_driver); 868edff8678SStefan Berger 8693d4960c7SMarc-André Lureau s->active_locty = TPM_TIS_NO_LOCALITY; 8703d4960c7SMarc-André Lureau s->next_locty = TPM_TIS_NO_LOCALITY; 8713d4960c7SMarc-André Lureau s->aborting_locty = TPM_TIS_NO_LOCALITY; 872edff8678SStefan Berger 873edff8678SStefan Berger for (c = 0; c < TPM_TIS_NUM_LOCALITIES; c++) { 8743d4960c7SMarc-André Lureau s->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS; 875116694c3SStefan Berger switch (s->be_tpm_version) { 876116694c3SStefan Berger case TPM_VERSION_UNSPEC: 877116694c3SStefan Berger break; 878116694c3SStefan Berger case TPM_VERSION_1_2: 8793d4960c7SMarc-André Lureau s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY1_2; 8803d4960c7SMarc-André Lureau s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3; 881116694c3SStefan Berger break; 882116694c3SStefan Berger case TPM_VERSION_2_0: 8833d4960c7SMarc-André Lureau s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY2_0; 8843d4960c7SMarc-André Lureau s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0; 885116694c3SStefan Berger break; 886116694c3SStefan Berger } 8873d4960c7SMarc-André Lureau s->loc[c].inte = TPM_TIS_INT_POLARITY_LOW_LEVEL; 8883d4960c7SMarc-André Lureau s->loc[c].ints = 0; 8893d4960c7SMarc-André Lureau s->loc[c].state = TPM_TIS_STATE_IDLE; 890edff8678SStefan Berger 891f999d81bSStefan Berger s->rw_offset = 0; 892edff8678SStefan Berger } 893edff8678SStefan Berger 8943bd9e161SStefan Berger tpm_backend_startup_tpm(s->be_driver, s->be_buffer_size); 895edff8678SStefan Berger } 896edff8678SStefan Berger 8979ec08c48SStefan Berger /* persistent state handling */ 8989ec08c48SStefan Berger 8999ec08c48SStefan Berger static int tpm_tis_pre_save(void *opaque) 9009ec08c48SStefan Berger { 9019ec08c48SStefan Berger TPMState *s = opaque; 9029ec08c48SStefan Berger uint8_t locty = s->active_locty; 9039ec08c48SStefan Berger 9049ec08c48SStefan Berger trace_tpm_tis_pre_save(locty, s->rw_offset); 9059ec08c48SStefan Berger 9069ec08c48SStefan Berger if (DEBUG_TIS) { 9079ec08c48SStefan Berger tpm_tis_dump_state(opaque, 0); 9089ec08c48SStefan Berger } 9099ec08c48SStefan Berger 9109ec08c48SStefan Berger /* 9119ec08c48SStefan Berger * Synchronize with backend completion. 9129ec08c48SStefan Berger */ 9139ec08c48SStefan Berger tpm_backend_finish_sync(s->be_driver); 9149ec08c48SStefan Berger 9159ec08c48SStefan Berger return 0; 9169ec08c48SStefan Berger } 9179ec08c48SStefan Berger 9189ec08c48SStefan Berger static const VMStateDescription vmstate_locty = { 9199ec08c48SStefan Berger .name = "tpm-tis/locty", 9209ec08c48SStefan Berger .version_id = 0, 9219ec08c48SStefan Berger .fields = (VMStateField[]) { 9229ec08c48SStefan Berger VMSTATE_UINT32(state, TPMLocality), 9239ec08c48SStefan Berger VMSTATE_UINT32(inte, TPMLocality), 9249ec08c48SStefan Berger VMSTATE_UINT32(ints, TPMLocality), 9259ec08c48SStefan Berger VMSTATE_UINT8(access, TPMLocality), 9269ec08c48SStefan Berger VMSTATE_UINT32(sts, TPMLocality), 9279ec08c48SStefan Berger VMSTATE_UINT32(iface_id, TPMLocality), 9289ec08c48SStefan Berger VMSTATE_END_OF_LIST(), 9299ec08c48SStefan Berger } 9309ec08c48SStefan Berger }; 9319ec08c48SStefan Berger 932edff8678SStefan Berger static const VMStateDescription vmstate_tpm_tis = { 9339ec08c48SStefan Berger .name = "tpm-tis", 9349ec08c48SStefan Berger .version_id = 0, 9359ec08c48SStefan Berger .pre_save = tpm_tis_pre_save, 9369ec08c48SStefan Berger .fields = (VMStateField[]) { 9379ec08c48SStefan Berger VMSTATE_BUFFER(buffer, TPMState), 9389ec08c48SStefan Berger VMSTATE_UINT16(rw_offset, TPMState), 9399ec08c48SStefan Berger VMSTATE_UINT8(active_locty, TPMState), 9409ec08c48SStefan Berger VMSTATE_UINT8(aborting_locty, TPMState), 9419ec08c48SStefan Berger VMSTATE_UINT8(next_locty, TPMState), 9429ec08c48SStefan Berger 9439ec08c48SStefan Berger VMSTATE_STRUCT_ARRAY(loc, TPMState, TPM_TIS_NUM_LOCALITIES, 0, 9449ec08c48SStefan Berger vmstate_locty, TPMLocality), 9459ec08c48SStefan Berger 9469ec08c48SStefan Berger VMSTATE_END_OF_LIST() 9479ec08c48SStefan Berger } 948edff8678SStefan Berger }; 949edff8678SStefan Berger 950edff8678SStefan Berger static Property tpm_tis_properties[] = { 9513d4960c7SMarc-André Lureau DEFINE_PROP_UINT32("irq", TPMState, irq_num, TPM_TIS_IRQ), 952c0378544SMarc-André Lureau DEFINE_PROP_TPMBE("tpmdev", TPMState, be_driver), 953edff8678SStefan Berger DEFINE_PROP_END_OF_LIST(), 954edff8678SStefan Berger }; 955edff8678SStefan Berger 956edff8678SStefan Berger static void tpm_tis_realizefn(DeviceState *dev, Error **errp) 957edff8678SStefan Berger { 958edff8678SStefan Berger TPMState *s = TPM(dev); 959edff8678SStefan Berger 96051a837e9SMarc-André Lureau if (!tpm_find()) { 96151a837e9SMarc-André Lureau error_setg(errp, "at most one TPM device is permitted"); 96251a837e9SMarc-André Lureau return; 96351a837e9SMarc-André Lureau } 96451a837e9SMarc-André Lureau 965edff8678SStefan Berger if (!s->be_driver) { 966c0378544SMarc-André Lureau error_setg(errp, "'tpmdev' property is required"); 967edff8678SStefan Berger return; 968edff8678SStefan Berger } 9693d4960c7SMarc-André Lureau if (s->irq_num > 15) { 970c87b35faSMarc-André Lureau error_setg(errp, "IRQ %d is outside valid range of 0 to 15", 971c87b35faSMarc-André Lureau s->irq_num); 972edff8678SStefan Berger return; 973edff8678SStefan Berger } 974edff8678SStefan Berger 9753d4960c7SMarc-André Lureau isa_init_irq(&s->busdev, &s->irq, s->irq_num); 9769dfd24edSStefan Berger 9779dfd24edSStefan Berger memory_region_add_subregion(isa_address_space(ISA_DEVICE(dev)), 9789dfd24edSStefan Berger TPM_TIS_ADDR_BASE, &s->mmio); 979edff8678SStefan Berger } 980edff8678SStefan Berger 981edff8678SStefan Berger static void tpm_tis_initfn(Object *obj) 982edff8678SStefan Berger { 983edff8678SStefan Berger TPMState *s = TPM(obj); 984edff8678SStefan Berger 985853dca12SPaolo Bonzini memory_region_init_io(&s->mmio, OBJECT(s), &tpm_tis_memory_ops, 986853dca12SPaolo Bonzini s, "tpm-tis-mmio", 987edff8678SStefan Berger TPM_TIS_NUM_LOCALITIES << TPM_TIS_LOCALITY_SHIFT); 988edff8678SStefan Berger } 989edff8678SStefan Berger 990edff8678SStefan Berger static void tpm_tis_class_init(ObjectClass *klass, void *data) 991edff8678SStefan Berger { 992edff8678SStefan Berger DeviceClass *dc = DEVICE_CLASS(klass); 99305a69998SMarc-André Lureau TPMIfClass *tc = TPM_IF_CLASS(klass); 994edff8678SStefan Berger 995edff8678SStefan Berger dc->realize = tpm_tis_realizefn; 996edff8678SStefan Berger dc->props = tpm_tis_properties; 997edff8678SStefan Berger dc->reset = tpm_tis_reset; 998edff8678SStefan Berger dc->vmsd = &vmstate_tpm_tis; 999191adc94SMarc-André Lureau tc->model = TPM_MODEL_TPM_TIS; 10009af7a721SMarc-André Lureau tc->get_version = tpm_tis_get_tpm_version; 100105a69998SMarc-André Lureau tc->request_completed = tpm_tis_request_completed; 1002edff8678SStefan Berger } 1003edff8678SStefan Berger 1004edff8678SStefan Berger static const TypeInfo tpm_tis_info = { 1005edff8678SStefan Berger .name = TYPE_TPM_TIS, 1006edff8678SStefan Berger .parent = TYPE_ISA_DEVICE, 1007edff8678SStefan Berger .instance_size = sizeof(TPMState), 1008edff8678SStefan Berger .instance_init = tpm_tis_initfn, 1009edff8678SStefan Berger .class_init = tpm_tis_class_init, 1010698f5daaSMarc-André Lureau .interfaces = (InterfaceInfo[]) { 1011698f5daaSMarc-André Lureau { TYPE_TPM_IF }, 1012698f5daaSMarc-André Lureau { } 1013698f5daaSMarc-André Lureau } 1014edff8678SStefan Berger }; 1015edff8678SStefan Berger 1016edff8678SStefan Berger static void tpm_tis_register(void) 1017edff8678SStefan Berger { 1018edff8678SStefan Berger type_register_static(&tpm_tis_info); 1019edff8678SStefan Berger } 1020edff8678SStefan Berger 1021edff8678SStefan Berger type_init(tpm_tis_register) 1022