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" 34*3b97c01eSStefan Berger #include "tpm_ppi.h" 35fcbed221SStefan Berger #include "trace.h" 36732cd587SMarc-André Lureau 37732cd587SMarc-André Lureau #define TPM_TIS_NUM_LOCALITIES 5 /* per spec */ 38732cd587SMarc-André Lureau #define TPM_TIS_LOCALITY_SHIFT 12 39732cd587SMarc-André Lureau #define TPM_TIS_NO_LOCALITY 0xff 40732cd587SMarc-André Lureau 41732cd587SMarc-André Lureau #define TPM_TIS_IS_VALID_LOCTY(x) ((x) < TPM_TIS_NUM_LOCALITIES) 42732cd587SMarc-André Lureau 43732cd587SMarc-André Lureau #define TPM_TIS_BUFFER_MAX 4096 44732cd587SMarc-André Lureau 45732cd587SMarc-André Lureau typedef enum { 46732cd587SMarc-André Lureau TPM_TIS_STATE_IDLE = 0, 47732cd587SMarc-André Lureau TPM_TIS_STATE_READY, 48732cd587SMarc-André Lureau TPM_TIS_STATE_COMPLETION, 49732cd587SMarc-André Lureau TPM_TIS_STATE_EXECUTION, 50732cd587SMarc-André Lureau TPM_TIS_STATE_RECEPTION, 51732cd587SMarc-André Lureau } TPMTISState; 52732cd587SMarc-André Lureau 53732cd587SMarc-André Lureau /* locality data -- all fields are persisted */ 54732cd587SMarc-André Lureau typedef struct TPMLocality { 55732cd587SMarc-André Lureau TPMTISState state; 56732cd587SMarc-André Lureau uint8_t access; 57732cd587SMarc-André Lureau uint32_t sts; 58732cd587SMarc-André Lureau uint32_t iface_id; 59732cd587SMarc-André Lureau uint32_t inte; 60732cd587SMarc-André Lureau uint32_t ints; 61732cd587SMarc-André Lureau } TPMLocality; 62732cd587SMarc-André Lureau 6336e86589SMarc-André Lureau typedef struct TPMState { 643d4960c7SMarc-André Lureau ISADevice busdev; 653d4960c7SMarc-André Lureau MemoryRegion mmio; 663d4960c7SMarc-André Lureau 67c5496b97SStefan Berger unsigned char buffer[TPM_TIS_BUFFER_MAX]; 68f999d81bSStefan Berger uint16_t rw_offset; 69732cd587SMarc-André Lureau 70732cd587SMarc-André Lureau uint8_t active_locty; 71732cd587SMarc-André Lureau uint8_t aborting_locty; 72732cd587SMarc-André Lureau uint8_t next_locty; 73732cd587SMarc-André Lureau 74732cd587SMarc-André Lureau TPMLocality loc[TPM_TIS_NUM_LOCALITIES]; 75732cd587SMarc-André Lureau 76732cd587SMarc-André Lureau qemu_irq irq; 77732cd587SMarc-André Lureau uint32_t irq_num; 78732cd587SMarc-André Lureau 79732cd587SMarc-André Lureau TPMBackendCmd cmd; 80732cd587SMarc-André Lureau 81732cd587SMarc-André Lureau TPMBackend *be_driver; 82732cd587SMarc-André Lureau TPMVersion be_tpm_version; 83b21e6aafSStefan Berger 84b21e6aafSStefan Berger size_t be_buffer_size; 85b6148757SMarc-André Lureau 86b6148757SMarc-André Lureau bool ppi_enabled; 87*3b97c01eSStefan Berger TPMPPI ppi; 8836e86589SMarc-André Lureau } TPMState; 89732cd587SMarc-André Lureau 90732cd587SMarc-André Lureau #define TPM(obj) OBJECT_CHECK(TPMState, (obj), TYPE_TPM_TIS) 91edff8678SStefan Berger 924d1ba9c4SStefan Berger #define DEBUG_TIS 0 93edff8678SStefan Berger 948db7c415SStefan Berger /* local prototypes */ 958db7c415SStefan Berger 968db7c415SStefan Berger static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, 978db7c415SStefan Berger unsigned size); 988db7c415SStefan Berger 99edff8678SStefan Berger /* utility functions */ 100edff8678SStefan Berger 101edff8678SStefan Berger static uint8_t tpm_tis_locality_from_addr(hwaddr addr) 102edff8678SStefan Berger { 103edff8678SStefan Berger return (uint8_t)((addr >> TPM_TIS_LOCALITY_SHIFT) & 0x7); 104edff8678SStefan Berger } 105edff8678SStefan Berger 106e6b703f6SStefan Berger static void tpm_tis_show_buffer(const unsigned char *buffer, 107e6b703f6SStefan Berger size_t buffer_size, const char *string) 108edff8678SStefan Berger { 109edff8678SStefan Berger uint32_t len, i; 110edff8678SStefan Berger 111e6b703f6SStefan Berger len = MIN(tpm_cmd_get_size(buffer), buffer_size); 112fcbed221SStefan Berger printf("tpm_tis: %s length = %d\n", string, len); 113edff8678SStefan Berger for (i = 0; i < len; i++) { 114edff8678SStefan Berger if (i && !(i % 16)) { 115fcbed221SStefan Berger printf("\n"); 116edff8678SStefan Berger } 117fcbed221SStefan Berger printf("%.2X ", buffer[i]); 118edff8678SStefan Berger } 119fcbed221SStefan Berger printf("\n"); 120edff8678SStefan Berger } 121edff8678SStefan Berger 122edff8678SStefan Berger /* 123fd859081SStefan Berger * Set the given flags in the STS register by clearing the register but 124116694c3SStefan Berger * preserving the SELFTEST_DONE and TPM_FAMILY_MASK flags and then setting 125116694c3SStefan Berger * the new flags. 126fd859081SStefan Berger * 127fd859081SStefan Berger * The SELFTEST_DONE flag is acquired from the backend that determines it by 128fd859081SStefan Berger * peeking into TPM commands. 129fd859081SStefan Berger * 130fd859081SStefan Berger * A VM suspend/resume will preserve the flag by storing it into the VM 131fd859081SStefan Berger * device state, but the backend will not remember it when QEMU is started 132fd859081SStefan Berger * again. Therefore, we cache the flag here. Once set, it will not be unset 133fd859081SStefan Berger * except by a reset. 134fd859081SStefan Berger */ 135fd859081SStefan Berger static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags) 136fd859081SStefan Berger { 137116694c3SStefan Berger l->sts &= TPM_TIS_STS_SELFTEST_DONE | TPM_TIS_STS_TPM_FAMILY_MASK; 138fd859081SStefan Berger l->sts |= flags; 139fd859081SStefan Berger } 140fd859081SStefan Berger 141fd859081SStefan Berger /* 142edff8678SStefan Berger * Send a request to the TPM. 143edff8678SStefan Berger */ 144edff8678SStefan Berger static void tpm_tis_tpm_send(TPMState *s, uint8_t locty) 145edff8678SStefan Berger { 146fcbed221SStefan Berger if (DEBUG_TIS) { 147c5496b97SStefan Berger tpm_tis_show_buffer(s->buffer, s->be_buffer_size, 148e6b703f6SStefan Berger "tpm_tis: To TPM"); 149fcbed221SStefan Berger } 150edff8678SStefan Berger 151edff8678SStefan Berger /* 152f999d81bSStefan Berger * rw_offset serves as length indicator for length of data; 153edff8678SStefan Berger * it's reset when the response comes back 154edff8678SStefan Berger */ 1553d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_EXECUTION; 156edff8678SStefan Berger 1570e43b7e6SMarc-André Lureau s->cmd = (TPMBackendCmd) { 1580e43b7e6SMarc-André Lureau .locty = locty, 159c5496b97SStefan Berger .in = s->buffer, 160f999d81bSStefan Berger .in_len = s->rw_offset, 161c5496b97SStefan Berger .out = s->buffer, 162e6b703f6SStefan Berger .out_len = s->be_buffer_size, 1630e43b7e6SMarc-André Lureau }; 1640e43b7e6SMarc-André Lureau 1650e43b7e6SMarc-André Lureau tpm_backend_deliver_request(s->be_driver, &s->cmd); 166edff8678SStefan Berger } 167edff8678SStefan Berger 168edff8678SStefan Berger /* raise an interrupt if allowed */ 169edff8678SStefan Berger static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask) 170edff8678SStefan Berger { 171edff8678SStefan Berger if (!TPM_TIS_IS_VALID_LOCTY(locty)) { 172edff8678SStefan Berger return; 173edff8678SStefan Berger } 174edff8678SStefan Berger 1753d4960c7SMarc-André Lureau if ((s->loc[locty].inte & TPM_TIS_INT_ENABLED) && 1763d4960c7SMarc-André Lureau (s->loc[locty].inte & irqmask)) { 177fcbed221SStefan Berger trace_tpm_tis_raise_irq(irqmask); 1783d4960c7SMarc-André Lureau qemu_irq_raise(s->irq); 1793d4960c7SMarc-André Lureau s->loc[locty].ints |= irqmask; 180edff8678SStefan Berger } 181edff8678SStefan Berger } 182edff8678SStefan Berger 183edff8678SStefan Berger static uint32_t tpm_tis_check_request_use_except(TPMState *s, uint8_t locty) 184edff8678SStefan Berger { 185edff8678SStefan Berger uint8_t l; 186edff8678SStefan Berger 187edff8678SStefan Berger for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 188edff8678SStefan Berger if (l == locty) { 189edff8678SStefan Berger continue; 190edff8678SStefan Berger } 1913d4960c7SMarc-André Lureau if ((s->loc[l].access & TPM_TIS_ACCESS_REQUEST_USE)) { 192edff8678SStefan Berger return 1; 193edff8678SStefan Berger } 194edff8678SStefan Berger } 195edff8678SStefan Berger 196edff8678SStefan Berger return 0; 197edff8678SStefan Berger } 198edff8678SStefan Berger 199edff8678SStefan Berger static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty) 200edff8678SStefan Berger { 2013d4960c7SMarc-André Lureau bool change = (s->active_locty != new_active_locty); 202edff8678SStefan Berger bool is_seize; 203edff8678SStefan Berger uint8_t mask; 204edff8678SStefan Berger 2053d4960c7SMarc-André Lureau if (change && TPM_TIS_IS_VALID_LOCTY(s->active_locty)) { 206edff8678SStefan Berger is_seize = TPM_TIS_IS_VALID_LOCTY(new_active_locty) && 2073d4960c7SMarc-André Lureau s->loc[new_active_locty].access & TPM_TIS_ACCESS_SEIZE; 208edff8678SStefan Berger 209edff8678SStefan Berger if (is_seize) { 210edff8678SStefan Berger mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY); 211edff8678SStefan Berger } else { 212edff8678SStefan Berger mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY| 213edff8678SStefan Berger TPM_TIS_ACCESS_REQUEST_USE); 214edff8678SStefan Berger } 215edff8678SStefan Berger /* reset flags on the old active locality */ 2163d4960c7SMarc-André Lureau s->loc[s->active_locty].access &= mask; 217edff8678SStefan Berger 218edff8678SStefan Berger if (is_seize) { 2193d4960c7SMarc-André Lureau s->loc[s->active_locty].access |= TPM_TIS_ACCESS_BEEN_SEIZED; 220edff8678SStefan Berger } 221edff8678SStefan Berger } 222edff8678SStefan Berger 2233d4960c7SMarc-André Lureau s->active_locty = new_active_locty; 224edff8678SStefan Berger 225fcbed221SStefan Berger trace_tpm_tis_new_active_locality(s->active_locty); 226edff8678SStefan Berger 227edff8678SStefan Berger if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) { 228edff8678SStefan Berger /* set flags on the new active locality */ 2293d4960c7SMarc-André Lureau s->loc[new_active_locty].access |= TPM_TIS_ACCESS_ACTIVE_LOCALITY; 2303d4960c7SMarc-André Lureau s->loc[new_active_locty].access &= ~(TPM_TIS_ACCESS_REQUEST_USE | 231edff8678SStefan Berger TPM_TIS_ACCESS_SEIZE); 232edff8678SStefan Berger } 233edff8678SStefan Berger 234edff8678SStefan Berger if (change) { 2353d4960c7SMarc-André Lureau tpm_tis_raise_irq(s, s->active_locty, TPM_TIS_INT_LOCALITY_CHANGED); 236edff8678SStefan Berger } 237edff8678SStefan Berger } 238edff8678SStefan Berger 239edff8678SStefan Berger /* abort -- this function switches the locality */ 2400f5faee3SStefan Berger static void tpm_tis_abort(TPMState *s) 241edff8678SStefan Berger { 242f999d81bSStefan Berger s->rw_offset = 0; 243edff8678SStefan Berger 244fcbed221SStefan Berger trace_tpm_tis_abort(s->next_locty); 245edff8678SStefan Berger 246edff8678SStefan Berger /* 247edff8678SStefan Berger * Need to react differently depending on who's aborting now and 248edff8678SStefan Berger * which locality will become active afterwards. 249edff8678SStefan Berger */ 2503d4960c7SMarc-André Lureau if (s->aborting_locty == s->next_locty) { 2513d4960c7SMarc-André Lureau s->loc[s->aborting_locty].state = TPM_TIS_STATE_READY; 2523d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[s->aborting_locty], 253fd859081SStefan Berger TPM_TIS_STS_COMMAND_READY); 2543d4960c7SMarc-André Lureau tpm_tis_raise_irq(s, s->aborting_locty, TPM_TIS_INT_COMMAND_READY); 255edff8678SStefan Berger } 256edff8678SStefan Berger 257edff8678SStefan Berger /* locality after abort is another one than the current one */ 2583d4960c7SMarc-André Lureau tpm_tis_new_active_locality(s, s->next_locty); 259edff8678SStefan Berger 2603d4960c7SMarc-André Lureau s->next_locty = TPM_TIS_NO_LOCALITY; 261edff8678SStefan Berger /* nobody's aborting a command anymore */ 2623d4960c7SMarc-André Lureau s->aborting_locty = TPM_TIS_NO_LOCALITY; 263edff8678SStefan Berger } 264edff8678SStefan Berger 265edff8678SStefan Berger /* prepare aborting current command */ 266edff8678SStefan Berger static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty) 267edff8678SStefan Berger { 268edff8678SStefan Berger uint8_t busy_locty; 269edff8678SStefan Berger 270e92b63eaSStefan Berger assert(TPM_TIS_IS_VALID_LOCTY(newlocty)); 271e92b63eaSStefan Berger 272e92b63eaSStefan Berger s->aborting_locty = locty; /* may also be TPM_TIS_NO_LOCALITY */ 2733d4960c7SMarc-André Lureau s->next_locty = newlocty; /* locality after successful abort */ 274edff8678SStefan Berger 275edff8678SStefan Berger /* 276edff8678SStefan Berger * only abort a command using an interrupt if currently executing 277edff8678SStefan Berger * a command AND if there's a valid connection to the vTPM. 278edff8678SStefan Berger */ 279edff8678SStefan Berger for (busy_locty = 0; busy_locty < TPM_TIS_NUM_LOCALITIES; busy_locty++) { 2803d4960c7SMarc-André Lureau if (s->loc[busy_locty].state == TPM_TIS_STATE_EXECUTION) { 281edff8678SStefan Berger /* 282edff8678SStefan Berger * request the backend to cancel. Some backends may not 283edff8678SStefan Berger * support it 284edff8678SStefan Berger */ 2858f0605ccSStefan Berger tpm_backend_cancel_cmd(s->be_driver); 286edff8678SStefan Berger return; 287edff8678SStefan Berger } 288edff8678SStefan Berger } 289edff8678SStefan Berger 2900f5faee3SStefan Berger tpm_tis_abort(s); 291edff8678SStefan Berger } 292edff8678SStefan Berger 29368999059SMarc-André Lureau /* 29468999059SMarc-André Lureau * Callback from the TPM to indicate that the response was received. 29568999059SMarc-André Lureau */ 2966a8a2354SMarc-André Lureau static void tpm_tis_request_completed(TPMIf *ti, int ret) 297edff8678SStefan Berger { 29868999059SMarc-André Lureau TPMState *s = TPM(ti); 2990e43b7e6SMarc-André Lureau uint8_t locty = s->cmd.locty; 30068999059SMarc-André Lureau uint8_t l; 30168999059SMarc-André Lureau 302a639f961SStefan Berger assert(TPM_TIS_IS_VALID_LOCTY(locty)); 303a639f961SStefan Berger 30468999059SMarc-André Lureau if (s->cmd.selftest_done) { 30568999059SMarc-André Lureau for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 3066a50bb98SPrasad J Pandit s->loc[l].sts |= TPM_TIS_STS_SELFTEST_DONE; 30768999059SMarc-André Lureau } 30868999059SMarc-André Lureau } 309edff8678SStefan Berger 3106a8a2354SMarc-André Lureau /* FIXME: report error if ret != 0 */ 3113d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 312fd859081SStefan Berger TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE); 3133d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_COMPLETION; 314f999d81bSStefan Berger s->rw_offset = 0; 315edff8678SStefan Berger 316fcbed221SStefan Berger if (DEBUG_TIS) { 317c5496b97SStefan Berger tpm_tis_show_buffer(s->buffer, s->be_buffer_size, 318e6b703f6SStefan Berger "tpm_tis: From TPM"); 319fcbed221SStefan Berger } 320298d8b81SStefan Berger 3213d4960c7SMarc-André Lureau if (TPM_TIS_IS_VALID_LOCTY(s->next_locty)) { 3220f5faee3SStefan Berger tpm_tis_abort(s); 323edff8678SStefan Berger } 324edff8678SStefan Berger 325edff8678SStefan Berger tpm_tis_raise_irq(s, locty, 326edff8678SStefan Berger TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID); 327edff8678SStefan Berger } 328edff8678SStefan Berger 329edff8678SStefan Berger /* 330edff8678SStefan Berger * Read a byte of response data 331edff8678SStefan Berger */ 332edff8678SStefan Berger static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty) 333edff8678SStefan Berger { 334edff8678SStefan Berger uint32_t ret = TPM_TIS_NO_DATA_BYTE; 335edff8678SStefan Berger uint16_t len; 336edff8678SStefan Berger 3373d4960c7SMarc-André Lureau if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) { 338c5496b97SStefan Berger len = MIN(tpm_cmd_get_size(&s->buffer), 339e6b703f6SStefan Berger s->be_buffer_size); 340edff8678SStefan Berger 341f999d81bSStefan Berger ret = s->buffer[s->rw_offset++]; 342f999d81bSStefan Berger if (s->rw_offset >= len) { 343edff8678SStefan Berger /* got last byte */ 3443d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 345edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); 346edff8678SStefan Berger } 347fcbed221SStefan Berger trace_tpm_tis_data_read(ret, s->rw_offset - 1); 348edff8678SStefan Berger } 349edff8678SStefan Berger 350edff8678SStefan Berger return ret; 351edff8678SStefan Berger } 352edff8678SStefan Berger 3538db7c415SStefan Berger #ifdef DEBUG_TIS 3548db7c415SStefan Berger static void tpm_tis_dump_state(void *opaque, hwaddr addr) 3558db7c415SStefan Berger { 3568db7c415SStefan Berger static const unsigned regs[] = { 3578db7c415SStefan Berger TPM_TIS_REG_ACCESS, 3588db7c415SStefan Berger TPM_TIS_REG_INT_ENABLE, 3598db7c415SStefan Berger TPM_TIS_REG_INT_VECTOR, 3608db7c415SStefan Berger TPM_TIS_REG_INT_STATUS, 3618db7c415SStefan Berger TPM_TIS_REG_INTF_CAPABILITY, 3628db7c415SStefan Berger TPM_TIS_REG_STS, 3638db7c415SStefan Berger TPM_TIS_REG_DID_VID, 3648db7c415SStefan Berger TPM_TIS_REG_RID, 3658db7c415SStefan Berger 0xfff}; 3668db7c415SStefan Berger int idx; 3678db7c415SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 3688db7c415SStefan Berger hwaddr base = addr & ~0xfff; 3698db7c415SStefan Berger TPMState *s = opaque; 3708db7c415SStefan Berger 371fcbed221SStefan Berger printf("tpm_tis: active locality : %d\n" 3728db7c415SStefan Berger "tpm_tis: state of locality %d : %d\n" 3738db7c415SStefan Berger "tpm_tis: register dump:\n", 3743d4960c7SMarc-André Lureau s->active_locty, 3753d4960c7SMarc-André Lureau locty, s->loc[locty].state); 3768db7c415SStefan Berger 3778db7c415SStefan Berger for (idx = 0; regs[idx] != 0xfff; idx++) { 378fcbed221SStefan Berger printf("tpm_tis: 0x%04x : 0x%08x\n", regs[idx], 379070c7607SStefan Berger (int)tpm_tis_mmio_read(opaque, base + regs[idx], 4)); 3808db7c415SStefan Berger } 3818db7c415SStefan Berger 382fcbed221SStefan Berger printf("tpm_tis: r/w offset : %d\n" 3838db7c415SStefan Berger "tpm_tis: result buffer : ", 384f999d81bSStefan Berger s->rw_offset); 3858db7c415SStefan Berger for (idx = 0; 386c5496b97SStefan Berger idx < MIN(tpm_cmd_get_size(&s->buffer), s->be_buffer_size); 3878db7c415SStefan Berger idx++) { 388fcbed221SStefan Berger printf("%c%02x%s", 389f999d81bSStefan Berger s->rw_offset == idx ? '>' : ' ', 390c5496b97SStefan Berger s->buffer[idx], 3918db7c415SStefan Berger ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : ""); 3928db7c415SStefan Berger } 393fcbed221SStefan Berger printf("\n"); 3948db7c415SStefan Berger } 3958db7c415SStefan Berger #endif 3968db7c415SStefan Berger 397edff8678SStefan Berger /* 398edff8678SStefan Berger * Read a register of the TIS interface 399edff8678SStefan Berger * See specs pages 33-63 for description of the registers 400edff8678SStefan Berger */ 401edff8678SStefan Berger static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, 402edff8678SStefan Berger unsigned size) 403edff8678SStefan Berger { 404edff8678SStefan Berger TPMState *s = opaque; 405edff8678SStefan Berger uint16_t offset = addr & 0xffc; 406edff8678SStefan Berger uint8_t shift = (addr & 0x3) * 8; 407edff8678SStefan Berger uint32_t val = 0xffffffff; 408edff8678SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 409edff8678SStefan Berger uint32_t avail; 410feeb755fSStefan Berger uint8_t v; 411edff8678SStefan Berger 4128f0605ccSStefan Berger if (tpm_backend_had_startup_error(s->be_driver)) { 4136cd65969SStefan Berger return 0; 414edff8678SStefan Berger } 415edff8678SStefan Berger 416edff8678SStefan Berger switch (offset) { 417edff8678SStefan Berger case TPM_TIS_REG_ACCESS: 418edff8678SStefan Berger /* never show the SEIZE flag even though we use it internally */ 4193d4960c7SMarc-André Lureau val = s->loc[locty].access & ~TPM_TIS_ACCESS_SEIZE; 420edff8678SStefan Berger /* the pending flag is always calculated */ 421edff8678SStefan Berger if (tpm_tis_check_request_use_except(s, locty)) { 422edff8678SStefan Berger val |= TPM_TIS_ACCESS_PENDING_REQUEST; 423edff8678SStefan Berger } 4248f0605ccSStefan Berger val |= !tpm_backend_get_tpm_established_flag(s->be_driver); 425edff8678SStefan Berger break; 426edff8678SStefan Berger case TPM_TIS_REG_INT_ENABLE: 4273d4960c7SMarc-André Lureau val = s->loc[locty].inte; 428edff8678SStefan Berger break; 429edff8678SStefan Berger case TPM_TIS_REG_INT_VECTOR: 4303d4960c7SMarc-André Lureau val = s->irq_num; 431edff8678SStefan Berger break; 432edff8678SStefan Berger case TPM_TIS_REG_INT_STATUS: 4333d4960c7SMarc-André Lureau val = s->loc[locty].ints; 434edff8678SStefan Berger break; 435edff8678SStefan Berger case TPM_TIS_REG_INTF_CAPABILITY: 436116694c3SStefan Berger switch (s->be_tpm_version) { 437116694c3SStefan Berger case TPM_VERSION_UNSPEC: 438116694c3SStefan Berger val = 0; 439116694c3SStefan Berger break; 440116694c3SStefan Berger case TPM_VERSION_1_2: 441116694c3SStefan Berger val = TPM_TIS_CAPABILITIES_SUPPORTED1_3; 442116694c3SStefan Berger break; 443116694c3SStefan Berger case TPM_VERSION_2_0: 444116694c3SStefan Berger val = TPM_TIS_CAPABILITIES_SUPPORTED2_0; 445116694c3SStefan Berger break; 446116694c3SStefan Berger } 447edff8678SStefan Berger break; 448edff8678SStefan Berger case TPM_TIS_REG_STS: 4493d4960c7SMarc-André Lureau if (s->active_locty == locty) { 4503d4960c7SMarc-André Lureau if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) { 451edff8678SStefan Berger val = TPM_TIS_BURST_COUNT( 452c5496b97SStefan Berger MIN(tpm_cmd_get_size(&s->buffer), 453e6b703f6SStefan Berger s->be_buffer_size) 454f999d81bSStefan Berger - s->rw_offset) | s->loc[locty].sts; 455edff8678SStefan Berger } else { 456f999d81bSStefan Berger avail = s->be_buffer_size - s->rw_offset; 457edff8678SStefan Berger /* 458edff8678SStefan Berger * byte-sized reads should not return 0x00 for 0x100 459edff8678SStefan Berger * available bytes. 460edff8678SStefan Berger */ 461edff8678SStefan Berger if (size == 1 && avail > 0xff) { 462edff8678SStefan Berger avail = 0xff; 463edff8678SStefan Berger } 4643d4960c7SMarc-André Lureau val = TPM_TIS_BURST_COUNT(avail) | s->loc[locty].sts; 465edff8678SStefan Berger } 466edff8678SStefan Berger } 467edff8678SStefan Berger break; 468edff8678SStefan Berger case TPM_TIS_REG_DATA_FIFO: 4692eae8c75SStefan Berger case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END: 4703d4960c7SMarc-André Lureau if (s->active_locty == locty) { 471feeb755fSStefan Berger if (size > 4 - (addr & 0x3)) { 472feeb755fSStefan Berger /* prevent access beyond FIFO */ 473feeb755fSStefan Berger size = 4 - (addr & 0x3); 474feeb755fSStefan Berger } 475feeb755fSStefan Berger val = 0; 476feeb755fSStefan Berger shift = 0; 477feeb755fSStefan Berger while (size > 0) { 4783d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 479edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 480feeb755fSStefan Berger v = tpm_tis_data_read(s, locty); 481edff8678SStefan Berger break; 482edff8678SStefan Berger default: 483feeb755fSStefan Berger v = TPM_TIS_NO_DATA_BYTE; 484edff8678SStefan Berger break; 485edff8678SStefan Berger } 486feeb755fSStefan Berger val |= (v << shift); 487feeb755fSStefan Berger shift += 8; 488feeb755fSStefan Berger size--; 489feeb755fSStefan Berger } 490feeb755fSStefan Berger shift = 0; /* no more adjustments */ 491edff8678SStefan Berger } 492edff8678SStefan Berger break; 493116694c3SStefan Berger case TPM_TIS_REG_INTERFACE_ID: 4943d4960c7SMarc-André Lureau val = s->loc[locty].iface_id; 495116694c3SStefan Berger break; 496edff8678SStefan Berger case TPM_TIS_REG_DID_VID: 497edff8678SStefan Berger val = (TPM_TIS_TPM_DID << 16) | TPM_TIS_TPM_VID; 498edff8678SStefan Berger break; 499edff8678SStefan Berger case TPM_TIS_REG_RID: 500edff8678SStefan Berger val = TPM_TIS_TPM_RID; 501edff8678SStefan Berger break; 5028db7c415SStefan Berger #ifdef DEBUG_TIS 5038db7c415SStefan Berger case TPM_TIS_REG_DEBUG: 5048db7c415SStefan Berger tpm_tis_dump_state(opaque, addr); 5058db7c415SStefan Berger break; 5068db7c415SStefan Berger #endif 507edff8678SStefan Berger } 508edff8678SStefan Berger 509edff8678SStefan Berger if (shift) { 510edff8678SStefan Berger val >>= shift; 511edff8678SStefan Berger } 512edff8678SStefan Berger 513fcbed221SStefan Berger trace_tpm_tis_mmio_read(size, addr, val); 514edff8678SStefan Berger 515edff8678SStefan Berger return val; 516edff8678SStefan Berger } 517edff8678SStefan Berger 518edff8678SStefan Berger /* 519edff8678SStefan Berger * Write a value to a register of the TIS interface 520edff8678SStefan Berger * See specs pages 33-63 for description of the registers 521edff8678SStefan Berger */ 522ff2bc0c1SMarc-André Lureau static void tpm_tis_mmio_write(void *opaque, hwaddr addr, 523ff2bc0c1SMarc-André Lureau uint64_t val, unsigned size) 524edff8678SStefan Berger { 525edff8678SStefan Berger TPMState *s = opaque; 526feeb755fSStefan Berger uint16_t off = addr & 0xffc; 527feeb755fSStefan Berger uint8_t shift = (addr & 0x3) * 8; 528edff8678SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 529edff8678SStefan Berger uint8_t active_locty, l; 530edff8678SStefan Berger int c, set_new_locty = 1; 531edff8678SStefan Berger uint16_t len; 532feeb755fSStefan Berger uint32_t mask = (size == 1) ? 0xff : ((size == 2) ? 0xffff : ~0); 533edff8678SStefan Berger 534fcbed221SStefan Berger trace_tpm_tis_mmio_write(size, addr, val); 535edff8678SStefan Berger 536ff2bc0c1SMarc-André Lureau if (locty == 4) { 537fcbed221SStefan Berger trace_tpm_tis_mmio_write_locty4(); 538edff8678SStefan Berger return; 539edff8678SStefan Berger } 540edff8678SStefan Berger 5418f0605ccSStefan Berger if (tpm_backend_had_startup_error(s->be_driver)) { 542edff8678SStefan Berger return; 543edff8678SStefan Berger } 544edff8678SStefan Berger 545feeb755fSStefan Berger val &= mask; 546feeb755fSStefan Berger 547feeb755fSStefan Berger if (shift) { 548feeb755fSStefan Berger val <<= shift; 549feeb755fSStefan Berger mask <<= shift; 550feeb755fSStefan Berger } 551feeb755fSStefan Berger 552feeb755fSStefan Berger mask ^= 0xffffffff; 553feeb755fSStefan Berger 554edff8678SStefan Berger switch (off) { 555edff8678SStefan Berger case TPM_TIS_REG_ACCESS: 556edff8678SStefan Berger 557edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_SEIZE)) { 558edff8678SStefan Berger val &= ~(TPM_TIS_ACCESS_REQUEST_USE | 559edff8678SStefan Berger TPM_TIS_ACCESS_ACTIVE_LOCALITY); 560edff8678SStefan Berger } 561edff8678SStefan Berger 5623d4960c7SMarc-André Lureau active_locty = s->active_locty; 563edff8678SStefan Berger 564edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) { 565edff8678SStefan Berger /* give up locality if currently owned */ 5663d4960c7SMarc-André Lureau if (s->active_locty == locty) { 567fcbed221SStefan Berger trace_tpm_tis_mmio_write_release_locty(locty); 568edff8678SStefan Berger 569edff8678SStefan Berger uint8_t newlocty = TPM_TIS_NO_LOCALITY; 570edff8678SStefan Berger /* anybody wants the locality ? */ 571edff8678SStefan Berger for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) { 5723d4960c7SMarc-André Lureau if ((s->loc[c].access & TPM_TIS_ACCESS_REQUEST_USE)) { 573fcbed221SStefan Berger trace_tpm_tis_mmio_write_locty_req_use(c); 574edff8678SStefan Berger newlocty = c; 575edff8678SStefan Berger break; 576edff8678SStefan Berger } 577edff8678SStefan Berger } 578fcbed221SStefan Berger trace_tpm_tis_mmio_write_next_locty(newlocty); 579edff8678SStefan Berger 580edff8678SStefan Berger if (TPM_TIS_IS_VALID_LOCTY(newlocty)) { 581edff8678SStefan Berger set_new_locty = 0; 582edff8678SStefan Berger tpm_tis_prep_abort(s, locty, newlocty); 583edff8678SStefan Berger } else { 584edff8678SStefan Berger active_locty = TPM_TIS_NO_LOCALITY; 585edff8678SStefan Berger } 586edff8678SStefan Berger } else { 587edff8678SStefan Berger /* not currently the owner; clear a pending request */ 5883d4960c7SMarc-André Lureau s->loc[locty].access &= ~TPM_TIS_ACCESS_REQUEST_USE; 589edff8678SStefan Berger } 590edff8678SStefan Berger } 591edff8678SStefan Berger 592edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_BEEN_SEIZED)) { 5933d4960c7SMarc-André Lureau s->loc[locty].access &= ~TPM_TIS_ACCESS_BEEN_SEIZED; 594edff8678SStefan Berger } 595edff8678SStefan Berger 596edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_SEIZE)) { 597edff8678SStefan Berger /* 598edff8678SStefan Berger * allow seize if a locality is active and the requesting 599edff8678SStefan Berger * locality is higher than the one that's active 600edff8678SStefan Berger * OR 601edff8678SStefan Berger * allow seize for requesting locality if no locality is 602edff8678SStefan Berger * active 603edff8678SStefan Berger */ 6043d4960c7SMarc-André Lureau while ((TPM_TIS_IS_VALID_LOCTY(s->active_locty) && 6053d4960c7SMarc-André Lureau locty > s->active_locty) || 6063d4960c7SMarc-André Lureau !TPM_TIS_IS_VALID_LOCTY(s->active_locty)) { 607edff8678SStefan Berger bool higher_seize = FALSE; 608edff8678SStefan Berger 609edff8678SStefan Berger /* already a pending SEIZE ? */ 6103d4960c7SMarc-André Lureau if ((s->loc[locty].access & TPM_TIS_ACCESS_SEIZE)) { 611edff8678SStefan Berger break; 612edff8678SStefan Berger } 613edff8678SStefan Berger 614edff8678SStefan Berger /* check for ongoing seize by a higher locality */ 615edff8678SStefan Berger for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES; l++) { 6163d4960c7SMarc-André Lureau if ((s->loc[l].access & TPM_TIS_ACCESS_SEIZE)) { 617edff8678SStefan Berger higher_seize = TRUE; 618edff8678SStefan Berger break; 619edff8678SStefan Berger } 620edff8678SStefan Berger } 621edff8678SStefan Berger 622edff8678SStefan Berger if (higher_seize) { 623edff8678SStefan Berger break; 624edff8678SStefan Berger } 625edff8678SStefan Berger 626edff8678SStefan Berger /* cancel any seize by a lower locality */ 627edff8678SStefan Berger for (l = 0; l < locty - 1; l++) { 6283d4960c7SMarc-André Lureau s->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE; 629edff8678SStefan Berger } 630edff8678SStefan Berger 6313d4960c7SMarc-André Lureau s->loc[locty].access |= TPM_TIS_ACCESS_SEIZE; 632fcbed221SStefan Berger 633fcbed221SStefan Berger trace_tpm_tis_mmio_write_locty_seized(locty, s->active_locty); 634fcbed221SStefan Berger trace_tpm_tis_mmio_write_init_abort(); 635fcbed221SStefan Berger 636edff8678SStefan Berger set_new_locty = 0; 6373d4960c7SMarc-André Lureau tpm_tis_prep_abort(s, s->active_locty, locty); 638edff8678SStefan Berger break; 639edff8678SStefan Berger } 640edff8678SStefan Berger } 641edff8678SStefan Berger 642edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_REQUEST_USE)) { 6433d4960c7SMarc-André Lureau if (s->active_locty != locty) { 6443d4960c7SMarc-André Lureau if (TPM_TIS_IS_VALID_LOCTY(s->active_locty)) { 6453d4960c7SMarc-André Lureau s->loc[locty].access |= TPM_TIS_ACCESS_REQUEST_USE; 646edff8678SStefan Berger } else { 647edff8678SStefan Berger /* no locality active -> make this one active now */ 648edff8678SStefan Berger active_locty = locty; 649edff8678SStefan Berger } 650edff8678SStefan Berger } 651edff8678SStefan Berger } 652edff8678SStefan Berger 653edff8678SStefan Berger if (set_new_locty) { 654edff8678SStefan Berger tpm_tis_new_active_locality(s, active_locty); 655edff8678SStefan Berger } 656edff8678SStefan Berger 657edff8678SStefan Berger break; 658edff8678SStefan Berger case TPM_TIS_REG_INT_ENABLE: 6593d4960c7SMarc-André Lureau if (s->active_locty != locty) { 660edff8678SStefan Berger break; 661edff8678SStefan Berger } 662edff8678SStefan Berger 6633d4960c7SMarc-André Lureau s->loc[locty].inte &= mask; 6643d4960c7SMarc-André Lureau s->loc[locty].inte |= (val & (TPM_TIS_INT_ENABLED | 665edff8678SStefan Berger TPM_TIS_INT_POLARITY_MASK | 666edff8678SStefan Berger TPM_TIS_INTERRUPTS_SUPPORTED)); 667edff8678SStefan Berger break; 668edff8678SStefan Berger case TPM_TIS_REG_INT_VECTOR: 669edff8678SStefan Berger /* hard wired -- ignore */ 670edff8678SStefan Berger break; 671edff8678SStefan Berger case TPM_TIS_REG_INT_STATUS: 6723d4960c7SMarc-André Lureau if (s->active_locty != locty) { 673edff8678SStefan Berger break; 674edff8678SStefan Berger } 675edff8678SStefan Berger 676edff8678SStefan Berger /* clearing of interrupt flags */ 677edff8678SStefan Berger if (((val & TPM_TIS_INTERRUPTS_SUPPORTED)) && 6783d4960c7SMarc-André Lureau (s->loc[locty].ints & TPM_TIS_INTERRUPTS_SUPPORTED)) { 6793d4960c7SMarc-André Lureau s->loc[locty].ints &= ~val; 6803d4960c7SMarc-André Lureau if (s->loc[locty].ints == 0) { 6813d4960c7SMarc-André Lureau qemu_irq_lower(s->irq); 682fcbed221SStefan Berger trace_tpm_tis_mmio_write_lowering_irq(); 683edff8678SStefan Berger } 684edff8678SStefan Berger } 6853d4960c7SMarc-André Lureau s->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED); 686edff8678SStefan Berger break; 687edff8678SStefan Berger case TPM_TIS_REG_STS: 6883d4960c7SMarc-André Lureau if (s->active_locty != locty) { 689edff8678SStefan Berger break; 690edff8678SStefan Berger } 691edff8678SStefan Berger 692116694c3SStefan Berger if (s->be_tpm_version == TPM_VERSION_2_0) { 693116694c3SStefan Berger /* some flags that are only supported for TPM 2 */ 694116694c3SStefan Berger if (val & TPM_TIS_STS_COMMAND_CANCEL) { 6953d4960c7SMarc-André Lureau if (s->loc[locty].state == TPM_TIS_STATE_EXECUTION) { 696116694c3SStefan Berger /* 697116694c3SStefan Berger * request the backend to cancel. Some backends may not 698116694c3SStefan Berger * support it 699116694c3SStefan Berger */ 700116694c3SStefan Berger tpm_backend_cancel_cmd(s->be_driver); 701116694c3SStefan Berger } 702116694c3SStefan Berger } 703116694c3SStefan Berger 704116694c3SStefan Berger if (val & TPM_TIS_STS_RESET_ESTABLISHMENT_BIT) { 705116694c3SStefan Berger if (locty == 3 || locty == 4) { 706116694c3SStefan Berger tpm_backend_reset_tpm_established_flag(s->be_driver, locty); 707116694c3SStefan Berger } 708116694c3SStefan Berger } 709116694c3SStefan Berger } 710116694c3SStefan Berger 711edff8678SStefan Berger val &= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO | 712edff8678SStefan Berger TPM_TIS_STS_RESPONSE_RETRY); 713edff8678SStefan Berger 714edff8678SStefan Berger if (val == TPM_TIS_STS_COMMAND_READY) { 7153d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 716edff8678SStefan Berger 717edff8678SStefan Berger case TPM_TIS_STATE_READY: 718f999d81bSStefan Berger s->rw_offset = 0; 719edff8678SStefan Berger break; 720edff8678SStefan Berger 721edff8678SStefan Berger case TPM_TIS_STATE_IDLE: 7223d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_COMMAND_READY); 7233d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_READY; 724edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); 725edff8678SStefan Berger break; 726edff8678SStefan Berger 727edff8678SStefan Berger case TPM_TIS_STATE_EXECUTION: 728edff8678SStefan Berger case TPM_TIS_STATE_RECEPTION: 729edff8678SStefan Berger /* abort currently running command */ 730fcbed221SStefan Berger trace_tpm_tis_mmio_write_init_abort(); 731edff8678SStefan Berger tpm_tis_prep_abort(s, locty, locty); 732edff8678SStefan Berger break; 733edff8678SStefan Berger 734edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 735f999d81bSStefan Berger s->rw_offset = 0; 736edff8678SStefan Berger /* shortcut to ready state with C/R set */ 7373d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_READY; 7383d4960c7SMarc-André Lureau if (!(s->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) { 7393d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 740fd859081SStefan Berger TPM_TIS_STS_COMMAND_READY); 741edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); 742edff8678SStefan Berger } 7433d4960c7SMarc-André Lureau s->loc[locty].sts &= ~(TPM_TIS_STS_DATA_AVAILABLE); 744edff8678SStefan Berger break; 745edff8678SStefan Berger 746edff8678SStefan Berger } 747edff8678SStefan Berger } else if (val == TPM_TIS_STS_TPM_GO) { 7483d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 749edff8678SStefan Berger case TPM_TIS_STATE_RECEPTION: 7503d4960c7SMarc-André Lureau if ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) == 0) { 751edff8678SStefan Berger tpm_tis_tpm_send(s, locty); 752edff8678SStefan Berger } 753edff8678SStefan Berger break; 754edff8678SStefan Berger default: 755edff8678SStefan Berger /* ignore */ 756edff8678SStefan Berger break; 757edff8678SStefan Berger } 758edff8678SStefan Berger } else if (val == TPM_TIS_STS_RESPONSE_RETRY) { 7593d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 760edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 761f999d81bSStefan Berger s->rw_offset = 0; 7623d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 763fd859081SStefan Berger TPM_TIS_STS_VALID| 764fd859081SStefan Berger TPM_TIS_STS_DATA_AVAILABLE); 765edff8678SStefan Berger break; 766edff8678SStefan Berger default: 767edff8678SStefan Berger /* ignore */ 768edff8678SStefan Berger break; 769edff8678SStefan Berger } 770edff8678SStefan Berger } 771edff8678SStefan Berger break; 772edff8678SStefan Berger case TPM_TIS_REG_DATA_FIFO: 7732eae8c75SStefan Berger case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END: 774edff8678SStefan Berger /* data fifo */ 7753d4960c7SMarc-André Lureau if (s->active_locty != locty) { 776edff8678SStefan Berger break; 777edff8678SStefan Berger } 778edff8678SStefan Berger 7793d4960c7SMarc-André Lureau if (s->loc[locty].state == TPM_TIS_STATE_IDLE || 7803d4960c7SMarc-André Lureau s->loc[locty].state == TPM_TIS_STATE_EXECUTION || 7813d4960c7SMarc-André Lureau s->loc[locty].state == TPM_TIS_STATE_COMPLETION) { 782edff8678SStefan Berger /* drop the byte */ 783edff8678SStefan Berger } else { 784fcbed221SStefan Berger trace_tpm_tis_mmio_write_data2send(val, size); 7853d4960c7SMarc-André Lureau if (s->loc[locty].state == TPM_TIS_STATE_READY) { 7863d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_RECEPTION; 7873d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 788fd859081SStefan Berger TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); 789edff8678SStefan Berger } 790edff8678SStefan Berger 791feeb755fSStefan Berger val >>= shift; 792feeb755fSStefan Berger if (size > 4 - (addr & 0x3)) { 793feeb755fSStefan Berger /* prevent access beyond FIFO */ 794feeb755fSStefan Berger size = 4 - (addr & 0x3); 795feeb755fSStefan Berger } 796feeb755fSStefan Berger 7973d4960c7SMarc-André Lureau while ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) && size > 0) { 798f999d81bSStefan Berger if (s->rw_offset < s->be_buffer_size) { 799f999d81bSStefan Berger s->buffer[s->rw_offset++] = 800e6b703f6SStefan Berger (uint8_t)val; 801feeb755fSStefan Berger val >>= 8; 802feeb755fSStefan Berger size--; 803edff8678SStefan Berger } else { 8043d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 805edff8678SStefan Berger } 806edff8678SStefan Berger } 807edff8678SStefan Berger 808edff8678SStefan Berger /* check for complete packet */ 809f999d81bSStefan Berger if (s->rw_offset > 5 && 8103d4960c7SMarc-André Lureau (s->loc[locty].sts & TPM_TIS_STS_EXPECT)) { 811edff8678SStefan Berger /* we have a packet length - see if we have all of it */ 8123d4960c7SMarc-André Lureau bool need_irq = !(s->loc[locty].sts & TPM_TIS_STS_VALID); 813d8383d61SMarc-André Lureau 814c5496b97SStefan Berger len = tpm_cmd_get_size(&s->buffer); 815f999d81bSStefan Berger if (len > s->rw_offset) { 8163d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 817fd859081SStefan Berger TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); 818edff8678SStefan Berger } else { 819edff8678SStefan Berger /* packet complete */ 8203d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 821edff8678SStefan Berger } 82229b558d8SStefan Berger if (need_irq) { 823edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); 824edff8678SStefan Berger } 825edff8678SStefan Berger } 826edff8678SStefan Berger } 827edff8678SStefan Berger break; 828116694c3SStefan Berger case TPM_TIS_REG_INTERFACE_ID: 829116694c3SStefan Berger if (val & TPM_TIS_IFACE_ID_INT_SEL_LOCK) { 830116694c3SStefan Berger for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 8313d4960c7SMarc-André Lureau s->loc[l].iface_id |= TPM_TIS_IFACE_ID_INT_SEL_LOCK; 832116694c3SStefan Berger } 833116694c3SStefan Berger } 834116694c3SStefan Berger break; 835edff8678SStefan Berger } 836edff8678SStefan Berger } 837edff8678SStefan Berger 838edff8678SStefan Berger static const MemoryRegionOps tpm_tis_memory_ops = { 839edff8678SStefan Berger .read = tpm_tis_mmio_read, 840edff8678SStefan Berger .write = tpm_tis_mmio_write, 841edff8678SStefan Berger .endianness = DEVICE_LITTLE_ENDIAN, 842edff8678SStefan Berger .valid = { 843edff8678SStefan Berger .min_access_size = 1, 844edff8678SStefan Berger .max_access_size = 4, 845edff8678SStefan Berger }, 846edff8678SStefan Berger }; 847edff8678SStefan Berger 848edff8678SStefan Berger /* 8495cb18b3dSStefan Berger * Get the TPMVersion of the backend device being used 8505cb18b3dSStefan Berger */ 8519af7a721SMarc-André Lureau static enum TPMVersion tpm_tis_get_tpm_version(TPMIf *ti) 8525cb18b3dSStefan Berger { 8539af7a721SMarc-André Lureau TPMState *s = TPM(ti); 8545cb18b3dSStefan Berger 855ad4aca69SStefan Berger if (tpm_backend_had_startup_error(s->be_driver)) { 856ad4aca69SStefan Berger return TPM_VERSION_UNSPEC; 857ad4aca69SStefan Berger } 858ad4aca69SStefan Berger 8595cb18b3dSStefan Berger return tpm_backend_get_tpm_version(s->be_driver); 8605cb18b3dSStefan Berger } 8615cb18b3dSStefan Berger 8625cb18b3dSStefan Berger /* 863edff8678SStefan Berger * This function is called when the machine starts, resets or due to 864edff8678SStefan Berger * S3 resume. 865edff8678SStefan Berger */ 866edff8678SStefan Berger static void tpm_tis_reset(DeviceState *dev) 867edff8678SStefan Berger { 868edff8678SStefan Berger TPMState *s = TPM(dev); 869edff8678SStefan Berger int c; 870edff8678SStefan Berger 871116694c3SStefan Berger s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver); 8721af3d63eSStefan Berger s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->be_driver), 8731af3d63eSStefan Berger TPM_TIS_BUFFER_MAX); 874116694c3SStefan Berger 8758f0605ccSStefan Berger tpm_backend_reset(s->be_driver); 876edff8678SStefan Berger 8773d4960c7SMarc-André Lureau s->active_locty = TPM_TIS_NO_LOCALITY; 8783d4960c7SMarc-André Lureau s->next_locty = TPM_TIS_NO_LOCALITY; 8793d4960c7SMarc-André Lureau s->aborting_locty = TPM_TIS_NO_LOCALITY; 880edff8678SStefan Berger 881edff8678SStefan Berger for (c = 0; c < TPM_TIS_NUM_LOCALITIES; c++) { 8823d4960c7SMarc-André Lureau s->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS; 883116694c3SStefan Berger switch (s->be_tpm_version) { 884116694c3SStefan Berger case TPM_VERSION_UNSPEC: 885116694c3SStefan Berger break; 886116694c3SStefan Berger case TPM_VERSION_1_2: 8873d4960c7SMarc-André Lureau s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY1_2; 8883d4960c7SMarc-André Lureau s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3; 889116694c3SStefan Berger break; 890116694c3SStefan Berger case TPM_VERSION_2_0: 8913d4960c7SMarc-André Lureau s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY2_0; 8923d4960c7SMarc-André Lureau s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0; 893116694c3SStefan Berger break; 894116694c3SStefan Berger } 8953d4960c7SMarc-André Lureau s->loc[c].inte = TPM_TIS_INT_POLARITY_LOW_LEVEL; 8963d4960c7SMarc-André Lureau s->loc[c].ints = 0; 8973d4960c7SMarc-André Lureau s->loc[c].state = TPM_TIS_STATE_IDLE; 898edff8678SStefan Berger 899f999d81bSStefan Berger s->rw_offset = 0; 900edff8678SStefan Berger } 901edff8678SStefan Berger 9023bd9e161SStefan Berger tpm_backend_startup_tpm(s->be_driver, s->be_buffer_size); 903edff8678SStefan Berger } 904edff8678SStefan Berger 9059ec08c48SStefan Berger /* persistent state handling */ 9069ec08c48SStefan Berger 9079ec08c48SStefan Berger static int tpm_tis_pre_save(void *opaque) 9089ec08c48SStefan Berger { 9099ec08c48SStefan Berger TPMState *s = opaque; 9109ec08c48SStefan Berger uint8_t locty = s->active_locty; 9119ec08c48SStefan Berger 9129ec08c48SStefan Berger trace_tpm_tis_pre_save(locty, s->rw_offset); 9139ec08c48SStefan Berger 9149ec08c48SStefan Berger if (DEBUG_TIS) { 9159ec08c48SStefan Berger tpm_tis_dump_state(opaque, 0); 9169ec08c48SStefan Berger } 9179ec08c48SStefan Berger 9189ec08c48SStefan Berger /* 9199ec08c48SStefan Berger * Synchronize with backend completion. 9209ec08c48SStefan Berger */ 9219ec08c48SStefan Berger tpm_backend_finish_sync(s->be_driver); 9229ec08c48SStefan Berger 9239ec08c48SStefan Berger return 0; 9249ec08c48SStefan Berger } 9259ec08c48SStefan Berger 9269ec08c48SStefan Berger static const VMStateDescription vmstate_locty = { 9279ec08c48SStefan Berger .name = "tpm-tis/locty", 9289ec08c48SStefan Berger .version_id = 0, 9299ec08c48SStefan Berger .fields = (VMStateField[]) { 9309ec08c48SStefan Berger VMSTATE_UINT32(state, TPMLocality), 9319ec08c48SStefan Berger VMSTATE_UINT32(inte, TPMLocality), 9329ec08c48SStefan Berger VMSTATE_UINT32(ints, TPMLocality), 9339ec08c48SStefan Berger VMSTATE_UINT8(access, TPMLocality), 9349ec08c48SStefan Berger VMSTATE_UINT32(sts, TPMLocality), 9359ec08c48SStefan Berger VMSTATE_UINT32(iface_id, TPMLocality), 9369ec08c48SStefan Berger VMSTATE_END_OF_LIST(), 9379ec08c48SStefan Berger } 9389ec08c48SStefan Berger }; 9399ec08c48SStefan Berger 940edff8678SStefan Berger static const VMStateDescription vmstate_tpm_tis = { 9419ec08c48SStefan Berger .name = "tpm-tis", 9429ec08c48SStefan Berger .version_id = 0, 9439ec08c48SStefan Berger .pre_save = tpm_tis_pre_save, 9449ec08c48SStefan Berger .fields = (VMStateField[]) { 9459ec08c48SStefan Berger VMSTATE_BUFFER(buffer, TPMState), 9469ec08c48SStefan Berger VMSTATE_UINT16(rw_offset, TPMState), 9479ec08c48SStefan Berger VMSTATE_UINT8(active_locty, TPMState), 9489ec08c48SStefan Berger VMSTATE_UINT8(aborting_locty, TPMState), 9499ec08c48SStefan Berger VMSTATE_UINT8(next_locty, TPMState), 9509ec08c48SStefan Berger 9519ec08c48SStefan Berger VMSTATE_STRUCT_ARRAY(loc, TPMState, TPM_TIS_NUM_LOCALITIES, 0, 9529ec08c48SStefan Berger vmstate_locty, TPMLocality), 9539ec08c48SStefan Berger 9549ec08c48SStefan Berger VMSTATE_END_OF_LIST() 9559ec08c48SStefan Berger } 956edff8678SStefan Berger }; 957edff8678SStefan Berger 958edff8678SStefan Berger static Property tpm_tis_properties[] = { 9593d4960c7SMarc-André Lureau DEFINE_PROP_UINT32("irq", TPMState, irq_num, TPM_TIS_IRQ), 960c0378544SMarc-André Lureau DEFINE_PROP_TPMBE("tpmdev", TPMState, be_driver), 961b6148757SMarc-André Lureau DEFINE_PROP_BOOL("ppi", TPMState, ppi_enabled, true), 962edff8678SStefan Berger DEFINE_PROP_END_OF_LIST(), 963edff8678SStefan Berger }; 964edff8678SStefan Berger 965edff8678SStefan Berger static void tpm_tis_realizefn(DeviceState *dev, Error **errp) 966edff8678SStefan Berger { 967edff8678SStefan Berger TPMState *s = TPM(dev); 968edff8678SStefan Berger 96951a837e9SMarc-André Lureau if (!tpm_find()) { 97051a837e9SMarc-André Lureau error_setg(errp, "at most one TPM device is permitted"); 97151a837e9SMarc-André Lureau return; 97251a837e9SMarc-André Lureau } 97351a837e9SMarc-André Lureau 974edff8678SStefan Berger if (!s->be_driver) { 975c0378544SMarc-André Lureau error_setg(errp, "'tpmdev' property is required"); 976edff8678SStefan Berger return; 977edff8678SStefan Berger } 9783d4960c7SMarc-André Lureau if (s->irq_num > 15) { 979c87b35faSMarc-André Lureau error_setg(errp, "IRQ %d is outside valid range of 0 to 15", 980c87b35faSMarc-André Lureau s->irq_num); 981edff8678SStefan Berger return; 982edff8678SStefan Berger } 983edff8678SStefan Berger 9843d4960c7SMarc-André Lureau isa_init_irq(&s->busdev, &s->irq, s->irq_num); 9859dfd24edSStefan Berger 9869dfd24edSStefan Berger memory_region_add_subregion(isa_address_space(ISA_DEVICE(dev)), 9879dfd24edSStefan Berger TPM_TIS_ADDR_BASE, &s->mmio); 988*3b97c01eSStefan Berger 989*3b97c01eSStefan Berger if (s->ppi_enabled) { 990*3b97c01eSStefan Berger tpm_ppi_init(&s->ppi, isa_address_space(ISA_DEVICE(dev)), 991*3b97c01eSStefan Berger TPM_PPI_ADDR_BASE, OBJECT(s)); 992*3b97c01eSStefan Berger } 993edff8678SStefan Berger } 994edff8678SStefan Berger 995edff8678SStefan Berger static void tpm_tis_initfn(Object *obj) 996edff8678SStefan Berger { 997edff8678SStefan Berger TPMState *s = TPM(obj); 998edff8678SStefan Berger 999853dca12SPaolo Bonzini memory_region_init_io(&s->mmio, OBJECT(s), &tpm_tis_memory_ops, 1000853dca12SPaolo Bonzini s, "tpm-tis-mmio", 1001edff8678SStefan Berger TPM_TIS_NUM_LOCALITIES << TPM_TIS_LOCALITY_SHIFT); 1002edff8678SStefan Berger } 1003edff8678SStefan Berger 1004edff8678SStefan Berger static void tpm_tis_class_init(ObjectClass *klass, void *data) 1005edff8678SStefan Berger { 1006edff8678SStefan Berger DeviceClass *dc = DEVICE_CLASS(klass); 100705a69998SMarc-André Lureau TPMIfClass *tc = TPM_IF_CLASS(klass); 1008edff8678SStefan Berger 1009edff8678SStefan Berger dc->realize = tpm_tis_realizefn; 1010edff8678SStefan Berger dc->props = tpm_tis_properties; 1011edff8678SStefan Berger dc->reset = tpm_tis_reset; 1012edff8678SStefan Berger dc->vmsd = &vmstate_tpm_tis; 1013191adc94SMarc-André Lureau tc->model = TPM_MODEL_TPM_TIS; 10149af7a721SMarc-André Lureau tc->get_version = tpm_tis_get_tpm_version; 101505a69998SMarc-André Lureau tc->request_completed = tpm_tis_request_completed; 1016edff8678SStefan Berger } 1017edff8678SStefan Berger 1018edff8678SStefan Berger static const TypeInfo tpm_tis_info = { 1019edff8678SStefan Berger .name = TYPE_TPM_TIS, 1020edff8678SStefan Berger .parent = TYPE_ISA_DEVICE, 1021edff8678SStefan Berger .instance_size = sizeof(TPMState), 1022edff8678SStefan Berger .instance_init = tpm_tis_initfn, 1023edff8678SStefan Berger .class_init = tpm_tis_class_init, 1024698f5daaSMarc-André Lureau .interfaces = (InterfaceInfo[]) { 1025698f5daaSMarc-André Lureau { TYPE_TPM_IF }, 1026698f5daaSMarc-André Lureau { } 1027698f5daaSMarc-André Lureau } 1028edff8678SStefan Berger }; 1029edff8678SStefan Berger 1030edff8678SStefan Berger static void tpm_tis_register(void) 1031edff8678SStefan Berger { 1032edff8678SStefan Berger type_register_static(&tpm_tis_info); 1033edff8678SStefan Berger } 1034edff8678SStefan Berger 1035edff8678SStefan Berger type_init(tpm_tis_register) 1036