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 */ 2360f5faee3SStefan 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 266*e92b63eaSStefan Berger assert(TPM_TIS_IS_VALID_LOCTY(newlocty)); 267*e92b63eaSStefan Berger 268*e92b63eaSStefan Berger s->aborting_locty = locty; /* may also be TPM_TIS_NO_LOCALITY */ 2693d4960c7SMarc-André Lureau s->next_locty = newlocty; /* locality after successful abort */ 270edff8678SStefan Berger 271edff8678SStefan Berger /* 272edff8678SStefan Berger * only abort a command using an interrupt if currently executing 273edff8678SStefan Berger * a command AND if there's a valid connection to the vTPM. 274edff8678SStefan Berger */ 275edff8678SStefan Berger for (busy_locty = 0; busy_locty < TPM_TIS_NUM_LOCALITIES; busy_locty++) { 2763d4960c7SMarc-André Lureau if (s->loc[busy_locty].state == TPM_TIS_STATE_EXECUTION) { 277edff8678SStefan Berger /* 278edff8678SStefan Berger * request the backend to cancel. Some backends may not 279edff8678SStefan Berger * support it 280edff8678SStefan Berger */ 2818f0605ccSStefan Berger tpm_backend_cancel_cmd(s->be_driver); 282edff8678SStefan Berger return; 283edff8678SStefan Berger } 284edff8678SStefan Berger } 285edff8678SStefan Berger 2860f5faee3SStefan Berger tpm_tis_abort(s); 287edff8678SStefan Berger } 288edff8678SStefan Berger 28968999059SMarc-André Lureau /* 29068999059SMarc-André Lureau * Callback from the TPM to indicate that the response was received. 29168999059SMarc-André Lureau */ 2926a8a2354SMarc-André Lureau static void tpm_tis_request_completed(TPMIf *ti, int ret) 293edff8678SStefan Berger { 29468999059SMarc-André Lureau TPMState *s = TPM(ti); 2950e43b7e6SMarc-André Lureau uint8_t locty = s->cmd.locty; 29668999059SMarc-André Lureau uint8_t l; 29768999059SMarc-André Lureau 29868999059SMarc-André Lureau if (s->cmd.selftest_done) { 29968999059SMarc-André Lureau for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 3006a50bb98SPrasad J Pandit s->loc[l].sts |= TPM_TIS_STS_SELFTEST_DONE; 30168999059SMarc-André Lureau } 30268999059SMarc-André Lureau } 303edff8678SStefan Berger 3046a8a2354SMarc-André Lureau /* FIXME: report error if ret != 0 */ 3053d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 306fd859081SStefan Berger TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE); 3073d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_COMPLETION; 308f999d81bSStefan Berger s->rw_offset = 0; 309edff8678SStefan Berger 310fcbed221SStefan Berger if (DEBUG_TIS) { 311c5496b97SStefan Berger tpm_tis_show_buffer(s->buffer, s->be_buffer_size, 312e6b703f6SStefan Berger "tpm_tis: From TPM"); 313fcbed221SStefan Berger } 314298d8b81SStefan Berger 3153d4960c7SMarc-André Lureau if (TPM_TIS_IS_VALID_LOCTY(s->next_locty)) { 3160f5faee3SStefan Berger tpm_tis_abort(s); 317edff8678SStefan Berger } 318edff8678SStefan Berger 319edff8678SStefan Berger tpm_tis_raise_irq(s, locty, 320edff8678SStefan Berger TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID); 321edff8678SStefan Berger } 322edff8678SStefan Berger 323edff8678SStefan Berger /* 324edff8678SStefan Berger * Read a byte of response data 325edff8678SStefan Berger */ 326edff8678SStefan Berger static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty) 327edff8678SStefan Berger { 328edff8678SStefan Berger uint32_t ret = TPM_TIS_NO_DATA_BYTE; 329edff8678SStefan Berger uint16_t len; 330edff8678SStefan Berger 3313d4960c7SMarc-André Lureau if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) { 332c5496b97SStefan Berger len = MIN(tpm_cmd_get_size(&s->buffer), 333e6b703f6SStefan Berger s->be_buffer_size); 334edff8678SStefan Berger 335f999d81bSStefan Berger ret = s->buffer[s->rw_offset++]; 336f999d81bSStefan Berger if (s->rw_offset >= len) { 337edff8678SStefan Berger /* got last byte */ 3383d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 339edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); 340edff8678SStefan Berger } 341fcbed221SStefan Berger trace_tpm_tis_data_read(ret, s->rw_offset - 1); 342edff8678SStefan Berger } 343edff8678SStefan Berger 344edff8678SStefan Berger return ret; 345edff8678SStefan Berger } 346edff8678SStefan Berger 3478db7c415SStefan Berger #ifdef DEBUG_TIS 3488db7c415SStefan Berger static void tpm_tis_dump_state(void *opaque, hwaddr addr) 3498db7c415SStefan Berger { 3508db7c415SStefan Berger static const unsigned regs[] = { 3518db7c415SStefan Berger TPM_TIS_REG_ACCESS, 3528db7c415SStefan Berger TPM_TIS_REG_INT_ENABLE, 3538db7c415SStefan Berger TPM_TIS_REG_INT_VECTOR, 3548db7c415SStefan Berger TPM_TIS_REG_INT_STATUS, 3558db7c415SStefan Berger TPM_TIS_REG_INTF_CAPABILITY, 3568db7c415SStefan Berger TPM_TIS_REG_STS, 3578db7c415SStefan Berger TPM_TIS_REG_DID_VID, 3588db7c415SStefan Berger TPM_TIS_REG_RID, 3598db7c415SStefan Berger 0xfff}; 3608db7c415SStefan Berger int idx; 3618db7c415SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 3628db7c415SStefan Berger hwaddr base = addr & ~0xfff; 3638db7c415SStefan Berger TPMState *s = opaque; 3648db7c415SStefan Berger 365fcbed221SStefan Berger printf("tpm_tis: active locality : %d\n" 3668db7c415SStefan Berger "tpm_tis: state of locality %d : %d\n" 3678db7c415SStefan Berger "tpm_tis: register dump:\n", 3683d4960c7SMarc-André Lureau s->active_locty, 3693d4960c7SMarc-André Lureau locty, s->loc[locty].state); 3708db7c415SStefan Berger 3718db7c415SStefan Berger for (idx = 0; regs[idx] != 0xfff; idx++) { 372fcbed221SStefan Berger printf("tpm_tis: 0x%04x : 0x%08x\n", regs[idx], 373070c7607SStefan Berger (int)tpm_tis_mmio_read(opaque, base + regs[idx], 4)); 3748db7c415SStefan Berger } 3758db7c415SStefan Berger 376fcbed221SStefan Berger printf("tpm_tis: r/w offset : %d\n" 3778db7c415SStefan Berger "tpm_tis: result buffer : ", 378f999d81bSStefan Berger s->rw_offset); 3798db7c415SStefan Berger for (idx = 0; 380c5496b97SStefan Berger idx < MIN(tpm_cmd_get_size(&s->buffer), s->be_buffer_size); 3818db7c415SStefan Berger idx++) { 382fcbed221SStefan Berger printf("%c%02x%s", 383f999d81bSStefan Berger s->rw_offset == idx ? '>' : ' ', 384c5496b97SStefan Berger s->buffer[idx], 3858db7c415SStefan Berger ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : ""); 3868db7c415SStefan Berger } 387fcbed221SStefan Berger printf("\n"); 3888db7c415SStefan Berger } 3898db7c415SStefan Berger #endif 3908db7c415SStefan Berger 391edff8678SStefan Berger /* 392edff8678SStefan Berger * Read a register of the TIS interface 393edff8678SStefan Berger * See specs pages 33-63 for description of the registers 394edff8678SStefan Berger */ 395edff8678SStefan Berger static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, 396edff8678SStefan Berger unsigned size) 397edff8678SStefan Berger { 398edff8678SStefan Berger TPMState *s = opaque; 399edff8678SStefan Berger uint16_t offset = addr & 0xffc; 400edff8678SStefan Berger uint8_t shift = (addr & 0x3) * 8; 401edff8678SStefan Berger uint32_t val = 0xffffffff; 402edff8678SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 403edff8678SStefan Berger uint32_t avail; 404feeb755fSStefan Berger uint8_t v; 405edff8678SStefan Berger 4068f0605ccSStefan Berger if (tpm_backend_had_startup_error(s->be_driver)) { 4076cd65969SStefan Berger return 0; 408edff8678SStefan Berger } 409edff8678SStefan Berger 410edff8678SStefan Berger switch (offset) { 411edff8678SStefan Berger case TPM_TIS_REG_ACCESS: 412edff8678SStefan Berger /* never show the SEIZE flag even though we use it internally */ 4133d4960c7SMarc-André Lureau val = s->loc[locty].access & ~TPM_TIS_ACCESS_SEIZE; 414edff8678SStefan Berger /* the pending flag is always calculated */ 415edff8678SStefan Berger if (tpm_tis_check_request_use_except(s, locty)) { 416edff8678SStefan Berger val |= TPM_TIS_ACCESS_PENDING_REQUEST; 417edff8678SStefan Berger } 4188f0605ccSStefan Berger val |= !tpm_backend_get_tpm_established_flag(s->be_driver); 419edff8678SStefan Berger break; 420edff8678SStefan Berger case TPM_TIS_REG_INT_ENABLE: 4213d4960c7SMarc-André Lureau val = s->loc[locty].inte; 422edff8678SStefan Berger break; 423edff8678SStefan Berger case TPM_TIS_REG_INT_VECTOR: 4243d4960c7SMarc-André Lureau val = s->irq_num; 425edff8678SStefan Berger break; 426edff8678SStefan Berger case TPM_TIS_REG_INT_STATUS: 4273d4960c7SMarc-André Lureau val = s->loc[locty].ints; 428edff8678SStefan Berger break; 429edff8678SStefan Berger case TPM_TIS_REG_INTF_CAPABILITY: 430116694c3SStefan Berger switch (s->be_tpm_version) { 431116694c3SStefan Berger case TPM_VERSION_UNSPEC: 432116694c3SStefan Berger val = 0; 433116694c3SStefan Berger break; 434116694c3SStefan Berger case TPM_VERSION_1_2: 435116694c3SStefan Berger val = TPM_TIS_CAPABILITIES_SUPPORTED1_3; 436116694c3SStefan Berger break; 437116694c3SStefan Berger case TPM_VERSION_2_0: 438116694c3SStefan Berger val = TPM_TIS_CAPABILITIES_SUPPORTED2_0; 439116694c3SStefan Berger break; 440116694c3SStefan Berger } 441edff8678SStefan Berger break; 442edff8678SStefan Berger case TPM_TIS_REG_STS: 4433d4960c7SMarc-André Lureau if (s->active_locty == locty) { 4443d4960c7SMarc-André Lureau if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) { 445edff8678SStefan Berger val = TPM_TIS_BURST_COUNT( 446c5496b97SStefan Berger MIN(tpm_cmd_get_size(&s->buffer), 447e6b703f6SStefan Berger s->be_buffer_size) 448f999d81bSStefan Berger - s->rw_offset) | s->loc[locty].sts; 449edff8678SStefan Berger } else { 450f999d81bSStefan Berger avail = s->be_buffer_size - s->rw_offset; 451edff8678SStefan Berger /* 452edff8678SStefan Berger * byte-sized reads should not return 0x00 for 0x100 453edff8678SStefan Berger * available bytes. 454edff8678SStefan Berger */ 455edff8678SStefan Berger if (size == 1 && avail > 0xff) { 456edff8678SStefan Berger avail = 0xff; 457edff8678SStefan Berger } 4583d4960c7SMarc-André Lureau val = TPM_TIS_BURST_COUNT(avail) | s->loc[locty].sts; 459edff8678SStefan Berger } 460edff8678SStefan Berger } 461edff8678SStefan Berger break; 462edff8678SStefan Berger case TPM_TIS_REG_DATA_FIFO: 4632eae8c75SStefan Berger case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END: 4643d4960c7SMarc-André Lureau if (s->active_locty == locty) { 465feeb755fSStefan Berger if (size > 4 - (addr & 0x3)) { 466feeb755fSStefan Berger /* prevent access beyond FIFO */ 467feeb755fSStefan Berger size = 4 - (addr & 0x3); 468feeb755fSStefan Berger } 469feeb755fSStefan Berger val = 0; 470feeb755fSStefan Berger shift = 0; 471feeb755fSStefan Berger while (size > 0) { 4723d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 473edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 474feeb755fSStefan Berger v = tpm_tis_data_read(s, locty); 475edff8678SStefan Berger break; 476edff8678SStefan Berger default: 477feeb755fSStefan Berger v = TPM_TIS_NO_DATA_BYTE; 478edff8678SStefan Berger break; 479edff8678SStefan Berger } 480feeb755fSStefan Berger val |= (v << shift); 481feeb755fSStefan Berger shift += 8; 482feeb755fSStefan Berger size--; 483feeb755fSStefan Berger } 484feeb755fSStefan Berger shift = 0; /* no more adjustments */ 485edff8678SStefan Berger } 486edff8678SStefan Berger break; 487116694c3SStefan Berger case TPM_TIS_REG_INTERFACE_ID: 4883d4960c7SMarc-André Lureau val = s->loc[locty].iface_id; 489116694c3SStefan Berger break; 490edff8678SStefan Berger case TPM_TIS_REG_DID_VID: 491edff8678SStefan Berger val = (TPM_TIS_TPM_DID << 16) | TPM_TIS_TPM_VID; 492edff8678SStefan Berger break; 493edff8678SStefan Berger case TPM_TIS_REG_RID: 494edff8678SStefan Berger val = TPM_TIS_TPM_RID; 495edff8678SStefan Berger break; 4968db7c415SStefan Berger #ifdef DEBUG_TIS 4978db7c415SStefan Berger case TPM_TIS_REG_DEBUG: 4988db7c415SStefan Berger tpm_tis_dump_state(opaque, addr); 4998db7c415SStefan Berger break; 5008db7c415SStefan Berger #endif 501edff8678SStefan Berger } 502edff8678SStefan Berger 503edff8678SStefan Berger if (shift) { 504edff8678SStefan Berger val >>= shift; 505edff8678SStefan Berger } 506edff8678SStefan Berger 507fcbed221SStefan Berger trace_tpm_tis_mmio_read(size, addr, val); 508edff8678SStefan Berger 509edff8678SStefan Berger return val; 510edff8678SStefan Berger } 511edff8678SStefan Berger 512edff8678SStefan Berger /* 513edff8678SStefan Berger * Write a value to a register of the TIS interface 514edff8678SStefan Berger * See specs pages 33-63 for description of the registers 515edff8678SStefan Berger */ 516ff2bc0c1SMarc-André Lureau static void tpm_tis_mmio_write(void *opaque, hwaddr addr, 517ff2bc0c1SMarc-André Lureau uint64_t val, unsigned size) 518edff8678SStefan Berger { 519edff8678SStefan Berger TPMState *s = opaque; 520feeb755fSStefan Berger uint16_t off = addr & 0xffc; 521feeb755fSStefan Berger uint8_t shift = (addr & 0x3) * 8; 522edff8678SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 523edff8678SStefan Berger uint8_t active_locty, l; 524edff8678SStefan Berger int c, set_new_locty = 1; 525edff8678SStefan Berger uint16_t len; 526feeb755fSStefan Berger uint32_t mask = (size == 1) ? 0xff : ((size == 2) ? 0xffff : ~0); 527edff8678SStefan Berger 528fcbed221SStefan Berger trace_tpm_tis_mmio_write(size, addr, val); 529edff8678SStefan Berger 530ff2bc0c1SMarc-André Lureau if (locty == 4) { 531fcbed221SStefan Berger trace_tpm_tis_mmio_write_locty4(); 532edff8678SStefan Berger return; 533edff8678SStefan Berger } 534edff8678SStefan Berger 5358f0605ccSStefan Berger if (tpm_backend_had_startup_error(s->be_driver)) { 536edff8678SStefan Berger return; 537edff8678SStefan Berger } 538edff8678SStefan Berger 539feeb755fSStefan Berger val &= mask; 540feeb755fSStefan Berger 541feeb755fSStefan Berger if (shift) { 542feeb755fSStefan Berger val <<= shift; 543feeb755fSStefan Berger mask <<= shift; 544feeb755fSStefan Berger } 545feeb755fSStefan Berger 546feeb755fSStefan Berger mask ^= 0xffffffff; 547feeb755fSStefan Berger 548edff8678SStefan Berger switch (off) { 549edff8678SStefan Berger case TPM_TIS_REG_ACCESS: 550edff8678SStefan Berger 551edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_SEIZE)) { 552edff8678SStefan Berger val &= ~(TPM_TIS_ACCESS_REQUEST_USE | 553edff8678SStefan Berger TPM_TIS_ACCESS_ACTIVE_LOCALITY); 554edff8678SStefan Berger } 555edff8678SStefan Berger 5563d4960c7SMarc-André Lureau active_locty = s->active_locty; 557edff8678SStefan Berger 558edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) { 559edff8678SStefan Berger /* give up locality if currently owned */ 5603d4960c7SMarc-André Lureau if (s->active_locty == locty) { 561fcbed221SStefan Berger trace_tpm_tis_mmio_write_release_locty(locty); 562edff8678SStefan Berger 563edff8678SStefan Berger uint8_t newlocty = TPM_TIS_NO_LOCALITY; 564edff8678SStefan Berger /* anybody wants the locality ? */ 565edff8678SStefan Berger for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) { 5663d4960c7SMarc-André Lureau if ((s->loc[c].access & TPM_TIS_ACCESS_REQUEST_USE)) { 567fcbed221SStefan Berger trace_tpm_tis_mmio_write_locty_req_use(c); 568edff8678SStefan Berger newlocty = c; 569edff8678SStefan Berger break; 570edff8678SStefan Berger } 571edff8678SStefan Berger } 572fcbed221SStefan Berger trace_tpm_tis_mmio_write_next_locty(newlocty); 573edff8678SStefan Berger 574edff8678SStefan Berger if (TPM_TIS_IS_VALID_LOCTY(newlocty)) { 575edff8678SStefan Berger set_new_locty = 0; 576edff8678SStefan Berger tpm_tis_prep_abort(s, locty, newlocty); 577edff8678SStefan Berger } else { 578edff8678SStefan Berger active_locty = TPM_TIS_NO_LOCALITY; 579edff8678SStefan Berger } 580edff8678SStefan Berger } else { 581edff8678SStefan Berger /* not currently the owner; clear a pending request */ 5823d4960c7SMarc-André Lureau s->loc[locty].access &= ~TPM_TIS_ACCESS_REQUEST_USE; 583edff8678SStefan Berger } 584edff8678SStefan Berger } 585edff8678SStefan Berger 586edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_BEEN_SEIZED)) { 5873d4960c7SMarc-André Lureau s->loc[locty].access &= ~TPM_TIS_ACCESS_BEEN_SEIZED; 588edff8678SStefan Berger } 589edff8678SStefan Berger 590edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_SEIZE)) { 591edff8678SStefan Berger /* 592edff8678SStefan Berger * allow seize if a locality is active and the requesting 593edff8678SStefan Berger * locality is higher than the one that's active 594edff8678SStefan Berger * OR 595edff8678SStefan Berger * allow seize for requesting locality if no locality is 596edff8678SStefan Berger * active 597edff8678SStefan Berger */ 5983d4960c7SMarc-André Lureau while ((TPM_TIS_IS_VALID_LOCTY(s->active_locty) && 5993d4960c7SMarc-André Lureau locty > s->active_locty) || 6003d4960c7SMarc-André Lureau !TPM_TIS_IS_VALID_LOCTY(s->active_locty)) { 601edff8678SStefan Berger bool higher_seize = FALSE; 602edff8678SStefan Berger 603edff8678SStefan Berger /* already a pending SEIZE ? */ 6043d4960c7SMarc-André Lureau if ((s->loc[locty].access & TPM_TIS_ACCESS_SEIZE)) { 605edff8678SStefan Berger break; 606edff8678SStefan Berger } 607edff8678SStefan Berger 608edff8678SStefan Berger /* check for ongoing seize by a higher locality */ 609edff8678SStefan Berger for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES; l++) { 6103d4960c7SMarc-André Lureau if ((s->loc[l].access & TPM_TIS_ACCESS_SEIZE)) { 611edff8678SStefan Berger higher_seize = TRUE; 612edff8678SStefan Berger break; 613edff8678SStefan Berger } 614edff8678SStefan Berger } 615edff8678SStefan Berger 616edff8678SStefan Berger if (higher_seize) { 617edff8678SStefan Berger break; 618edff8678SStefan Berger } 619edff8678SStefan Berger 620edff8678SStefan Berger /* cancel any seize by a lower locality */ 621edff8678SStefan Berger for (l = 0; l < locty - 1; l++) { 6223d4960c7SMarc-André Lureau s->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE; 623edff8678SStefan Berger } 624edff8678SStefan Berger 6253d4960c7SMarc-André Lureau s->loc[locty].access |= TPM_TIS_ACCESS_SEIZE; 626fcbed221SStefan Berger 627fcbed221SStefan Berger trace_tpm_tis_mmio_write_locty_seized(locty, s->active_locty); 628fcbed221SStefan Berger trace_tpm_tis_mmio_write_init_abort(); 629fcbed221SStefan Berger 630edff8678SStefan Berger set_new_locty = 0; 6313d4960c7SMarc-André Lureau tpm_tis_prep_abort(s, s->active_locty, locty); 632edff8678SStefan Berger break; 633edff8678SStefan Berger } 634edff8678SStefan Berger } 635edff8678SStefan Berger 636edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_REQUEST_USE)) { 6373d4960c7SMarc-André Lureau if (s->active_locty != locty) { 6383d4960c7SMarc-André Lureau if (TPM_TIS_IS_VALID_LOCTY(s->active_locty)) { 6393d4960c7SMarc-André Lureau s->loc[locty].access |= TPM_TIS_ACCESS_REQUEST_USE; 640edff8678SStefan Berger } else { 641edff8678SStefan Berger /* no locality active -> make this one active now */ 642edff8678SStefan Berger active_locty = locty; 643edff8678SStefan Berger } 644edff8678SStefan Berger } 645edff8678SStefan Berger } 646edff8678SStefan Berger 647edff8678SStefan Berger if (set_new_locty) { 648edff8678SStefan Berger tpm_tis_new_active_locality(s, active_locty); 649edff8678SStefan Berger } 650edff8678SStefan Berger 651edff8678SStefan Berger break; 652edff8678SStefan Berger case TPM_TIS_REG_INT_ENABLE: 6533d4960c7SMarc-André Lureau if (s->active_locty != locty) { 654edff8678SStefan Berger break; 655edff8678SStefan Berger } 656edff8678SStefan Berger 6573d4960c7SMarc-André Lureau s->loc[locty].inte &= mask; 6583d4960c7SMarc-André Lureau s->loc[locty].inte |= (val & (TPM_TIS_INT_ENABLED | 659edff8678SStefan Berger TPM_TIS_INT_POLARITY_MASK | 660edff8678SStefan Berger TPM_TIS_INTERRUPTS_SUPPORTED)); 661edff8678SStefan Berger break; 662edff8678SStefan Berger case TPM_TIS_REG_INT_VECTOR: 663edff8678SStefan Berger /* hard wired -- ignore */ 664edff8678SStefan Berger break; 665edff8678SStefan Berger case TPM_TIS_REG_INT_STATUS: 6663d4960c7SMarc-André Lureau if (s->active_locty != locty) { 667edff8678SStefan Berger break; 668edff8678SStefan Berger } 669edff8678SStefan Berger 670edff8678SStefan Berger /* clearing of interrupt flags */ 671edff8678SStefan Berger if (((val & TPM_TIS_INTERRUPTS_SUPPORTED)) && 6723d4960c7SMarc-André Lureau (s->loc[locty].ints & TPM_TIS_INTERRUPTS_SUPPORTED)) { 6733d4960c7SMarc-André Lureau s->loc[locty].ints &= ~val; 6743d4960c7SMarc-André Lureau if (s->loc[locty].ints == 0) { 6753d4960c7SMarc-André Lureau qemu_irq_lower(s->irq); 676fcbed221SStefan Berger trace_tpm_tis_mmio_write_lowering_irq(); 677edff8678SStefan Berger } 678edff8678SStefan Berger } 6793d4960c7SMarc-André Lureau s->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED); 680edff8678SStefan Berger break; 681edff8678SStefan Berger case TPM_TIS_REG_STS: 6823d4960c7SMarc-André Lureau if (s->active_locty != locty) { 683edff8678SStefan Berger break; 684edff8678SStefan Berger } 685edff8678SStefan Berger 686116694c3SStefan Berger if (s->be_tpm_version == TPM_VERSION_2_0) { 687116694c3SStefan Berger /* some flags that are only supported for TPM 2 */ 688116694c3SStefan Berger if (val & TPM_TIS_STS_COMMAND_CANCEL) { 6893d4960c7SMarc-André Lureau if (s->loc[locty].state == TPM_TIS_STATE_EXECUTION) { 690116694c3SStefan Berger /* 691116694c3SStefan Berger * request the backend to cancel. Some backends may not 692116694c3SStefan Berger * support it 693116694c3SStefan Berger */ 694116694c3SStefan Berger tpm_backend_cancel_cmd(s->be_driver); 695116694c3SStefan Berger } 696116694c3SStefan Berger } 697116694c3SStefan Berger 698116694c3SStefan Berger if (val & TPM_TIS_STS_RESET_ESTABLISHMENT_BIT) { 699116694c3SStefan Berger if (locty == 3 || locty == 4) { 700116694c3SStefan Berger tpm_backend_reset_tpm_established_flag(s->be_driver, locty); 701116694c3SStefan Berger } 702116694c3SStefan Berger } 703116694c3SStefan Berger } 704116694c3SStefan Berger 705edff8678SStefan Berger val &= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO | 706edff8678SStefan Berger TPM_TIS_STS_RESPONSE_RETRY); 707edff8678SStefan Berger 708edff8678SStefan Berger if (val == TPM_TIS_STS_COMMAND_READY) { 7093d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 710edff8678SStefan Berger 711edff8678SStefan Berger case TPM_TIS_STATE_READY: 712f999d81bSStefan Berger s->rw_offset = 0; 713edff8678SStefan Berger break; 714edff8678SStefan Berger 715edff8678SStefan Berger case TPM_TIS_STATE_IDLE: 7163d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_COMMAND_READY); 7173d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_READY; 718edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); 719edff8678SStefan Berger break; 720edff8678SStefan Berger 721edff8678SStefan Berger case TPM_TIS_STATE_EXECUTION: 722edff8678SStefan Berger case TPM_TIS_STATE_RECEPTION: 723edff8678SStefan Berger /* abort currently running command */ 724fcbed221SStefan Berger trace_tpm_tis_mmio_write_init_abort(); 725edff8678SStefan Berger tpm_tis_prep_abort(s, locty, locty); 726edff8678SStefan Berger break; 727edff8678SStefan Berger 728edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 729f999d81bSStefan Berger s->rw_offset = 0; 730edff8678SStefan Berger /* shortcut to ready state with C/R set */ 7313d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_READY; 7323d4960c7SMarc-André Lureau if (!(s->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) { 7333d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 734fd859081SStefan Berger TPM_TIS_STS_COMMAND_READY); 735edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); 736edff8678SStefan Berger } 7373d4960c7SMarc-André Lureau s->loc[locty].sts &= ~(TPM_TIS_STS_DATA_AVAILABLE); 738edff8678SStefan Berger break; 739edff8678SStefan Berger 740edff8678SStefan Berger } 741edff8678SStefan Berger } else if (val == TPM_TIS_STS_TPM_GO) { 7423d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 743edff8678SStefan Berger case TPM_TIS_STATE_RECEPTION: 7443d4960c7SMarc-André Lureau if ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) == 0) { 745edff8678SStefan Berger tpm_tis_tpm_send(s, locty); 746edff8678SStefan Berger } 747edff8678SStefan Berger break; 748edff8678SStefan Berger default: 749edff8678SStefan Berger /* ignore */ 750edff8678SStefan Berger break; 751edff8678SStefan Berger } 752edff8678SStefan Berger } else if (val == TPM_TIS_STS_RESPONSE_RETRY) { 7533d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 754edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 755f999d81bSStefan Berger s->rw_offset = 0; 7563d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 757fd859081SStefan Berger TPM_TIS_STS_VALID| 758fd859081SStefan Berger TPM_TIS_STS_DATA_AVAILABLE); 759edff8678SStefan Berger break; 760edff8678SStefan Berger default: 761edff8678SStefan Berger /* ignore */ 762edff8678SStefan Berger break; 763edff8678SStefan Berger } 764edff8678SStefan Berger } 765edff8678SStefan Berger break; 766edff8678SStefan Berger case TPM_TIS_REG_DATA_FIFO: 7672eae8c75SStefan Berger case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END: 768edff8678SStefan Berger /* data fifo */ 7693d4960c7SMarc-André Lureau if (s->active_locty != locty) { 770edff8678SStefan Berger break; 771edff8678SStefan Berger } 772edff8678SStefan Berger 7733d4960c7SMarc-André Lureau if (s->loc[locty].state == TPM_TIS_STATE_IDLE || 7743d4960c7SMarc-André Lureau s->loc[locty].state == TPM_TIS_STATE_EXECUTION || 7753d4960c7SMarc-André Lureau s->loc[locty].state == TPM_TIS_STATE_COMPLETION) { 776edff8678SStefan Berger /* drop the byte */ 777edff8678SStefan Berger } else { 778fcbed221SStefan Berger trace_tpm_tis_mmio_write_data2send(val, size); 7793d4960c7SMarc-André Lureau if (s->loc[locty].state == TPM_TIS_STATE_READY) { 7803d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_RECEPTION; 7813d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 782fd859081SStefan Berger TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); 783edff8678SStefan Berger } 784edff8678SStefan Berger 785feeb755fSStefan Berger val >>= shift; 786feeb755fSStefan Berger if (size > 4 - (addr & 0x3)) { 787feeb755fSStefan Berger /* prevent access beyond FIFO */ 788feeb755fSStefan Berger size = 4 - (addr & 0x3); 789feeb755fSStefan Berger } 790feeb755fSStefan Berger 7913d4960c7SMarc-André Lureau while ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) && size > 0) { 792f999d81bSStefan Berger if (s->rw_offset < s->be_buffer_size) { 793f999d81bSStefan Berger s->buffer[s->rw_offset++] = 794e6b703f6SStefan Berger (uint8_t)val; 795feeb755fSStefan Berger val >>= 8; 796feeb755fSStefan Berger size--; 797edff8678SStefan Berger } else { 7983d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 799edff8678SStefan Berger } 800edff8678SStefan Berger } 801edff8678SStefan Berger 802edff8678SStefan Berger /* check for complete packet */ 803f999d81bSStefan Berger if (s->rw_offset > 5 && 8043d4960c7SMarc-André Lureau (s->loc[locty].sts & TPM_TIS_STS_EXPECT)) { 805edff8678SStefan Berger /* we have a packet length - see if we have all of it */ 8063d4960c7SMarc-André Lureau bool need_irq = !(s->loc[locty].sts & TPM_TIS_STS_VALID); 807d8383d61SMarc-André Lureau 808c5496b97SStefan Berger len = tpm_cmd_get_size(&s->buffer); 809f999d81bSStefan Berger if (len > s->rw_offset) { 8103d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 811fd859081SStefan Berger TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); 812edff8678SStefan Berger } else { 813edff8678SStefan Berger /* packet complete */ 8143d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 815edff8678SStefan Berger } 81629b558d8SStefan Berger if (need_irq) { 817edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); 818edff8678SStefan Berger } 819edff8678SStefan Berger } 820edff8678SStefan Berger } 821edff8678SStefan Berger break; 822116694c3SStefan Berger case TPM_TIS_REG_INTERFACE_ID: 823116694c3SStefan Berger if (val & TPM_TIS_IFACE_ID_INT_SEL_LOCK) { 824116694c3SStefan Berger for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 8253d4960c7SMarc-André Lureau s->loc[l].iface_id |= TPM_TIS_IFACE_ID_INT_SEL_LOCK; 826116694c3SStefan Berger } 827116694c3SStefan Berger } 828116694c3SStefan Berger break; 829edff8678SStefan Berger } 830edff8678SStefan Berger } 831edff8678SStefan Berger 832edff8678SStefan Berger static const MemoryRegionOps tpm_tis_memory_ops = { 833edff8678SStefan Berger .read = tpm_tis_mmio_read, 834edff8678SStefan Berger .write = tpm_tis_mmio_write, 835edff8678SStefan Berger .endianness = DEVICE_LITTLE_ENDIAN, 836edff8678SStefan Berger .valid = { 837edff8678SStefan Berger .min_access_size = 1, 838edff8678SStefan Berger .max_access_size = 4, 839edff8678SStefan Berger }, 840edff8678SStefan Berger }; 841edff8678SStefan Berger 842edff8678SStefan Berger /* 8435cb18b3dSStefan Berger * Get the TPMVersion of the backend device being used 8445cb18b3dSStefan Berger */ 8459af7a721SMarc-André Lureau static enum TPMVersion tpm_tis_get_tpm_version(TPMIf *ti) 8465cb18b3dSStefan Berger { 8479af7a721SMarc-André Lureau TPMState *s = TPM(ti); 8485cb18b3dSStefan Berger 849ad4aca69SStefan Berger if (tpm_backend_had_startup_error(s->be_driver)) { 850ad4aca69SStefan Berger return TPM_VERSION_UNSPEC; 851ad4aca69SStefan Berger } 852ad4aca69SStefan Berger 8535cb18b3dSStefan Berger return tpm_backend_get_tpm_version(s->be_driver); 8545cb18b3dSStefan Berger } 8555cb18b3dSStefan Berger 8565cb18b3dSStefan Berger /* 857edff8678SStefan Berger * This function is called when the machine starts, resets or due to 858edff8678SStefan Berger * S3 resume. 859edff8678SStefan Berger */ 860edff8678SStefan Berger static void tpm_tis_reset(DeviceState *dev) 861edff8678SStefan Berger { 862edff8678SStefan Berger TPMState *s = TPM(dev); 863edff8678SStefan Berger int c; 864edff8678SStefan Berger 865116694c3SStefan Berger s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver); 8661af3d63eSStefan Berger s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->be_driver), 8671af3d63eSStefan Berger TPM_TIS_BUFFER_MAX); 868116694c3SStefan Berger 8698f0605ccSStefan Berger tpm_backend_reset(s->be_driver); 870edff8678SStefan Berger 8713d4960c7SMarc-André Lureau s->active_locty = TPM_TIS_NO_LOCALITY; 8723d4960c7SMarc-André Lureau s->next_locty = TPM_TIS_NO_LOCALITY; 8733d4960c7SMarc-André Lureau s->aborting_locty = TPM_TIS_NO_LOCALITY; 874edff8678SStefan Berger 875edff8678SStefan Berger for (c = 0; c < TPM_TIS_NUM_LOCALITIES; c++) { 8763d4960c7SMarc-André Lureau s->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS; 877116694c3SStefan Berger switch (s->be_tpm_version) { 878116694c3SStefan Berger case TPM_VERSION_UNSPEC: 879116694c3SStefan Berger break; 880116694c3SStefan Berger case TPM_VERSION_1_2: 8813d4960c7SMarc-André Lureau s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY1_2; 8823d4960c7SMarc-André Lureau s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3; 883116694c3SStefan Berger break; 884116694c3SStefan Berger case TPM_VERSION_2_0: 8853d4960c7SMarc-André Lureau s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY2_0; 8863d4960c7SMarc-André Lureau s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0; 887116694c3SStefan Berger break; 888116694c3SStefan Berger } 8893d4960c7SMarc-André Lureau s->loc[c].inte = TPM_TIS_INT_POLARITY_LOW_LEVEL; 8903d4960c7SMarc-André Lureau s->loc[c].ints = 0; 8913d4960c7SMarc-André Lureau s->loc[c].state = TPM_TIS_STATE_IDLE; 892edff8678SStefan Berger 893f999d81bSStefan Berger s->rw_offset = 0; 894edff8678SStefan Berger } 895edff8678SStefan Berger 8963bd9e161SStefan Berger tpm_backend_startup_tpm(s->be_driver, s->be_buffer_size); 897edff8678SStefan Berger } 898edff8678SStefan Berger 8999ec08c48SStefan Berger /* persistent state handling */ 9009ec08c48SStefan Berger 9019ec08c48SStefan Berger static int tpm_tis_pre_save(void *opaque) 9029ec08c48SStefan Berger { 9039ec08c48SStefan Berger TPMState *s = opaque; 9049ec08c48SStefan Berger uint8_t locty = s->active_locty; 9059ec08c48SStefan Berger 9069ec08c48SStefan Berger trace_tpm_tis_pre_save(locty, s->rw_offset); 9079ec08c48SStefan Berger 9089ec08c48SStefan Berger if (DEBUG_TIS) { 9099ec08c48SStefan Berger tpm_tis_dump_state(opaque, 0); 9109ec08c48SStefan Berger } 9119ec08c48SStefan Berger 9129ec08c48SStefan Berger /* 9139ec08c48SStefan Berger * Synchronize with backend completion. 9149ec08c48SStefan Berger */ 9159ec08c48SStefan Berger tpm_backend_finish_sync(s->be_driver); 9169ec08c48SStefan Berger 9179ec08c48SStefan Berger return 0; 9189ec08c48SStefan Berger } 9199ec08c48SStefan Berger 9209ec08c48SStefan Berger static const VMStateDescription vmstate_locty = { 9219ec08c48SStefan Berger .name = "tpm-tis/locty", 9229ec08c48SStefan Berger .version_id = 0, 9239ec08c48SStefan Berger .fields = (VMStateField[]) { 9249ec08c48SStefan Berger VMSTATE_UINT32(state, TPMLocality), 9259ec08c48SStefan Berger VMSTATE_UINT32(inte, TPMLocality), 9269ec08c48SStefan Berger VMSTATE_UINT32(ints, TPMLocality), 9279ec08c48SStefan Berger VMSTATE_UINT8(access, TPMLocality), 9289ec08c48SStefan Berger VMSTATE_UINT32(sts, TPMLocality), 9299ec08c48SStefan Berger VMSTATE_UINT32(iface_id, TPMLocality), 9309ec08c48SStefan Berger VMSTATE_END_OF_LIST(), 9319ec08c48SStefan Berger } 9329ec08c48SStefan Berger }; 9339ec08c48SStefan Berger 934edff8678SStefan Berger static const VMStateDescription vmstate_tpm_tis = { 9359ec08c48SStefan Berger .name = "tpm-tis", 9369ec08c48SStefan Berger .version_id = 0, 9379ec08c48SStefan Berger .pre_save = tpm_tis_pre_save, 9389ec08c48SStefan Berger .fields = (VMStateField[]) { 9399ec08c48SStefan Berger VMSTATE_BUFFER(buffer, TPMState), 9409ec08c48SStefan Berger VMSTATE_UINT16(rw_offset, TPMState), 9419ec08c48SStefan Berger VMSTATE_UINT8(active_locty, TPMState), 9429ec08c48SStefan Berger VMSTATE_UINT8(aborting_locty, TPMState), 9439ec08c48SStefan Berger VMSTATE_UINT8(next_locty, TPMState), 9449ec08c48SStefan Berger 9459ec08c48SStefan Berger VMSTATE_STRUCT_ARRAY(loc, TPMState, TPM_TIS_NUM_LOCALITIES, 0, 9469ec08c48SStefan Berger vmstate_locty, TPMLocality), 9479ec08c48SStefan Berger 9489ec08c48SStefan Berger VMSTATE_END_OF_LIST() 9499ec08c48SStefan Berger } 950edff8678SStefan Berger }; 951edff8678SStefan Berger 952edff8678SStefan Berger static Property tpm_tis_properties[] = { 9533d4960c7SMarc-André Lureau DEFINE_PROP_UINT32("irq", TPMState, irq_num, TPM_TIS_IRQ), 954c0378544SMarc-André Lureau DEFINE_PROP_TPMBE("tpmdev", TPMState, be_driver), 955edff8678SStefan Berger DEFINE_PROP_END_OF_LIST(), 956edff8678SStefan Berger }; 957edff8678SStefan Berger 958edff8678SStefan Berger static void tpm_tis_realizefn(DeviceState *dev, Error **errp) 959edff8678SStefan Berger { 960edff8678SStefan Berger TPMState *s = TPM(dev); 961edff8678SStefan Berger 96251a837e9SMarc-André Lureau if (!tpm_find()) { 96351a837e9SMarc-André Lureau error_setg(errp, "at most one TPM device is permitted"); 96451a837e9SMarc-André Lureau return; 96551a837e9SMarc-André Lureau } 96651a837e9SMarc-André Lureau 967edff8678SStefan Berger if (!s->be_driver) { 968c0378544SMarc-André Lureau error_setg(errp, "'tpmdev' property is required"); 969edff8678SStefan Berger return; 970edff8678SStefan Berger } 9713d4960c7SMarc-André Lureau if (s->irq_num > 15) { 972c87b35faSMarc-André Lureau error_setg(errp, "IRQ %d is outside valid range of 0 to 15", 973c87b35faSMarc-André Lureau s->irq_num); 974edff8678SStefan Berger return; 975edff8678SStefan Berger } 976edff8678SStefan Berger 9773d4960c7SMarc-André Lureau isa_init_irq(&s->busdev, &s->irq, s->irq_num); 9789dfd24edSStefan Berger 9799dfd24edSStefan Berger memory_region_add_subregion(isa_address_space(ISA_DEVICE(dev)), 9809dfd24edSStefan Berger TPM_TIS_ADDR_BASE, &s->mmio); 981edff8678SStefan Berger } 982edff8678SStefan Berger 983edff8678SStefan Berger static void tpm_tis_initfn(Object *obj) 984edff8678SStefan Berger { 985edff8678SStefan Berger TPMState *s = TPM(obj); 986edff8678SStefan Berger 987853dca12SPaolo Bonzini memory_region_init_io(&s->mmio, OBJECT(s), &tpm_tis_memory_ops, 988853dca12SPaolo Bonzini s, "tpm-tis-mmio", 989edff8678SStefan Berger TPM_TIS_NUM_LOCALITIES << TPM_TIS_LOCALITY_SHIFT); 990edff8678SStefan Berger } 991edff8678SStefan Berger 992edff8678SStefan Berger static void tpm_tis_class_init(ObjectClass *klass, void *data) 993edff8678SStefan Berger { 994edff8678SStefan Berger DeviceClass *dc = DEVICE_CLASS(klass); 99505a69998SMarc-André Lureau TPMIfClass *tc = TPM_IF_CLASS(klass); 996edff8678SStefan Berger 997edff8678SStefan Berger dc->realize = tpm_tis_realizefn; 998edff8678SStefan Berger dc->props = tpm_tis_properties; 999edff8678SStefan Berger dc->reset = tpm_tis_reset; 1000edff8678SStefan Berger dc->vmsd = &vmstate_tpm_tis; 1001191adc94SMarc-André Lureau tc->model = TPM_MODEL_TPM_TIS; 10029af7a721SMarc-André Lureau tc->get_version = tpm_tis_get_tpm_version; 100305a69998SMarc-André Lureau tc->request_completed = tpm_tis_request_completed; 1004edff8678SStefan Berger } 1005edff8678SStefan Berger 1006edff8678SStefan Berger static const TypeInfo tpm_tis_info = { 1007edff8678SStefan Berger .name = TYPE_TPM_TIS, 1008edff8678SStefan Berger .parent = TYPE_ISA_DEVICE, 1009edff8678SStefan Berger .instance_size = sizeof(TPMState), 1010edff8678SStefan Berger .instance_init = tpm_tis_initfn, 1011edff8678SStefan Berger .class_init = tpm_tis_class_init, 1012698f5daaSMarc-André Lureau .interfaces = (InterfaceInfo[]) { 1013698f5daaSMarc-André Lureau { TYPE_TPM_IF }, 1014698f5daaSMarc-André Lureau { } 1015698f5daaSMarc-André Lureau } 1016edff8678SStefan Berger }; 1017edff8678SStefan Berger 1018edff8678SStefan Berger static void tpm_tis_register(void) 1019edff8678SStefan Berger { 1020edff8678SStefan Berger type_register_static(&tpm_tis_info); 1021edff8678SStefan Berger } 1022edff8678SStefan Berger 1023edff8678SStefan Berger type_init(tpm_tis_register) 1024