1edff8678SStefan Berger /* 2ac90053dSEric Auger * tpm_tis_common.c - QEMU's TPM TIS interface emulator 3ac90053dSEric Auger * device agnostic functions 4edff8678SStefan Berger * 5edff8678SStefan Berger * Copyright (C) 2006,2010-2013 IBM Corporation 6edff8678SStefan Berger * 7edff8678SStefan Berger * Authors: 8edff8678SStefan Berger * Stefan Berger <stefanb@us.ibm.com> 9edff8678SStefan Berger * David Safford <safford@us.ibm.com> 10edff8678SStefan Berger * 11edff8678SStefan Berger * Xen 4 support: Andrease Niederl <andreas.niederl@iaik.tugraz.at> 12edff8678SStefan Berger * 13edff8678SStefan Berger * This work is licensed under the terms of the GNU GPL, version 2 or later. 14edff8678SStefan Berger * See the COPYING file in the top-level directory. 15edff8678SStefan Berger * 16edff8678SStefan Berger * Implementation of the TIS interface according to specs found at 17edff8678SStefan Berger * http://www.trustedcomputinggroup.org. This implementation currently 189dd5c40dSStefan Berger * supports version 1.3, 21 March 2013 19edff8678SStefan Berger * In the developers menu choose the PC Client section then find the TIS 20edff8678SStefan Berger * specification. 21116694c3SStefan Berger * 22116694c3SStefan Berger * TPM TIS for TPM 2 implementation following TCG PC Client Platform 23116694c3SStefan Berger * TPM Profile (PTP) Specification, Familiy 2.0, Revision 00.43 24edff8678SStefan Berger */ 250430891cSPeter Maydell #include "qemu/osdep.h" 2664552b6bSMarkus Armbruster #include "hw/irq.h" 27732cd587SMarc-André Lureau #include "hw/isa/isa.h" 28023299d8SMarc-André Lureau #include "qapi/error.h" 290b8fa32fSMarkus Armbruster #include "qemu/module.h" 30023299d8SMarc-André Lureau 31023299d8SMarc-André Lureau #include "hw/acpi/tpm.h" 32023299d8SMarc-André Lureau #include "hw/pci/pci_ids.h" 33a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 34d6454270SMarkus Armbruster #include "migration/vmstate.h" 35dccfcd0eSPaolo Bonzini #include "sysemu/tpm_backend.h" 36edff8678SStefan Berger #include "tpm_int.h" 375cf954d0SMarc-André Lureau #include "tpm_util.h" 383b97c01eSStefan Berger #include "tpm_ppi.h" 39fcbed221SStefan Berger #include "trace.h" 40732cd587SMarc-André Lureau 41ac90053dSEric Auger #include "tpm_tis.h" 42edff8678SStefan Berger 434d1ba9c4SStefan Berger #define DEBUG_TIS 0 44edff8678SStefan Berger 458db7c415SStefan Berger /* local prototypes */ 468db7c415SStefan Berger 478db7c415SStefan Berger static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, 488db7c415SStefan Berger unsigned size); 498db7c415SStefan Berger 50edff8678SStefan Berger /* utility functions */ 51edff8678SStefan Berger 52edff8678SStefan Berger static uint8_t tpm_tis_locality_from_addr(hwaddr addr) 53edff8678SStefan Berger { 54edff8678SStefan Berger return (uint8_t)((addr >> TPM_TIS_LOCALITY_SHIFT) & 0x7); 55edff8678SStefan Berger } 56edff8678SStefan Berger 57edff8678SStefan Berger 58edff8678SStefan Berger /* 59fd859081SStefan Berger * Set the given flags in the STS register by clearing the register but 60116694c3SStefan Berger * preserving the SELFTEST_DONE and TPM_FAMILY_MASK flags and then setting 61116694c3SStefan Berger * the new flags. 62fd859081SStefan Berger * 63fd859081SStefan Berger * The SELFTEST_DONE flag is acquired from the backend that determines it by 64fd859081SStefan Berger * peeking into TPM commands. 65fd859081SStefan Berger * 66fd859081SStefan Berger * A VM suspend/resume will preserve the flag by storing it into the VM 67fd859081SStefan Berger * device state, but the backend will not remember it when QEMU is started 68fd859081SStefan Berger * again. Therefore, we cache the flag here. Once set, it will not be unset 69fd859081SStefan Berger * except by a reset. 70fd859081SStefan Berger */ 71fd859081SStefan Berger static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags) 72fd859081SStefan Berger { 73116694c3SStefan Berger l->sts &= TPM_TIS_STS_SELFTEST_DONE | TPM_TIS_STS_TPM_FAMILY_MASK; 74fd859081SStefan Berger l->sts |= flags; 75fd859081SStefan Berger } 76fd859081SStefan Berger 77fd859081SStefan Berger /* 78edff8678SStefan Berger * Send a request to the TPM. 79edff8678SStefan Berger */ 80edff8678SStefan Berger static void tpm_tis_tpm_send(TPMState *s, uint8_t locty) 81edff8678SStefan Berger { 823688d73bSStefan Berger if (trace_event_get_state_backends(TRACE_TPM_UTIL_SHOW_BUFFER)) { 833688d73bSStefan Berger tpm_util_show_buffer(s->buffer, s->be_buffer_size, "To TPM"); 84fcbed221SStefan Berger } 85edff8678SStefan Berger 86edff8678SStefan Berger /* 87f999d81bSStefan Berger * rw_offset serves as length indicator for length of data; 88edff8678SStefan Berger * it's reset when the response comes back 89edff8678SStefan Berger */ 903d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_EXECUTION; 91edff8678SStefan Berger 920e43b7e6SMarc-André Lureau s->cmd = (TPMBackendCmd) { 930e43b7e6SMarc-André Lureau .locty = locty, 94c5496b97SStefan Berger .in = s->buffer, 95f999d81bSStefan Berger .in_len = s->rw_offset, 96c5496b97SStefan Berger .out = s->buffer, 97e6b703f6SStefan Berger .out_len = s->be_buffer_size, 980e43b7e6SMarc-André Lureau }; 990e43b7e6SMarc-André Lureau 1000e43b7e6SMarc-André Lureau tpm_backend_deliver_request(s->be_driver, &s->cmd); 101edff8678SStefan Berger } 102edff8678SStefan Berger 103edff8678SStefan Berger /* raise an interrupt if allowed */ 104edff8678SStefan Berger static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask) 105edff8678SStefan Berger { 106edff8678SStefan Berger if (!TPM_TIS_IS_VALID_LOCTY(locty)) { 107edff8678SStefan Berger return; 108edff8678SStefan Berger } 109edff8678SStefan Berger 1103d4960c7SMarc-André Lureau if ((s->loc[locty].inte & TPM_TIS_INT_ENABLED) && 1113d4960c7SMarc-André Lureau (s->loc[locty].inte & irqmask)) { 112fcbed221SStefan Berger trace_tpm_tis_raise_irq(irqmask); 1133d4960c7SMarc-André Lureau qemu_irq_raise(s->irq); 1143d4960c7SMarc-André Lureau s->loc[locty].ints |= irqmask; 115edff8678SStefan Berger } 116edff8678SStefan Berger } 117edff8678SStefan Berger 118edff8678SStefan Berger static uint32_t tpm_tis_check_request_use_except(TPMState *s, uint8_t locty) 119edff8678SStefan Berger { 120edff8678SStefan Berger uint8_t l; 121edff8678SStefan Berger 122edff8678SStefan Berger for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 123edff8678SStefan Berger if (l == locty) { 124edff8678SStefan Berger continue; 125edff8678SStefan Berger } 1263d4960c7SMarc-André Lureau if ((s->loc[l].access & TPM_TIS_ACCESS_REQUEST_USE)) { 127edff8678SStefan Berger return 1; 128edff8678SStefan Berger } 129edff8678SStefan Berger } 130edff8678SStefan Berger 131edff8678SStefan Berger return 0; 132edff8678SStefan Berger } 133edff8678SStefan Berger 134edff8678SStefan Berger static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty) 135edff8678SStefan Berger { 1363d4960c7SMarc-André Lureau bool change = (s->active_locty != new_active_locty); 137edff8678SStefan Berger bool is_seize; 138edff8678SStefan Berger uint8_t mask; 139edff8678SStefan Berger 1403d4960c7SMarc-André Lureau if (change && TPM_TIS_IS_VALID_LOCTY(s->active_locty)) { 141edff8678SStefan Berger is_seize = TPM_TIS_IS_VALID_LOCTY(new_active_locty) && 1423d4960c7SMarc-André Lureau s->loc[new_active_locty].access & TPM_TIS_ACCESS_SEIZE; 143edff8678SStefan Berger 144edff8678SStefan Berger if (is_seize) { 145edff8678SStefan Berger mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY); 146edff8678SStefan Berger } else { 147edff8678SStefan Berger mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY| 148edff8678SStefan Berger TPM_TIS_ACCESS_REQUEST_USE); 149edff8678SStefan Berger } 150edff8678SStefan Berger /* reset flags on the old active locality */ 1513d4960c7SMarc-André Lureau s->loc[s->active_locty].access &= mask; 152edff8678SStefan Berger 153edff8678SStefan Berger if (is_seize) { 1543d4960c7SMarc-André Lureau s->loc[s->active_locty].access |= TPM_TIS_ACCESS_BEEN_SEIZED; 155edff8678SStefan Berger } 156edff8678SStefan Berger } 157edff8678SStefan Berger 1583d4960c7SMarc-André Lureau s->active_locty = new_active_locty; 159edff8678SStefan Berger 160fcbed221SStefan Berger trace_tpm_tis_new_active_locality(s->active_locty); 161edff8678SStefan Berger 162edff8678SStefan Berger if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) { 163edff8678SStefan Berger /* set flags on the new active locality */ 1643d4960c7SMarc-André Lureau s->loc[new_active_locty].access |= TPM_TIS_ACCESS_ACTIVE_LOCALITY; 1653d4960c7SMarc-André Lureau s->loc[new_active_locty].access &= ~(TPM_TIS_ACCESS_REQUEST_USE | 166edff8678SStefan Berger TPM_TIS_ACCESS_SEIZE); 167edff8678SStefan Berger } 168edff8678SStefan Berger 169edff8678SStefan Berger if (change) { 1703d4960c7SMarc-André Lureau tpm_tis_raise_irq(s, s->active_locty, TPM_TIS_INT_LOCALITY_CHANGED); 171edff8678SStefan Berger } 172edff8678SStefan Berger } 173edff8678SStefan Berger 174edff8678SStefan Berger /* abort -- this function switches the locality */ 1750f5faee3SStefan Berger static void tpm_tis_abort(TPMState *s) 176edff8678SStefan Berger { 177f999d81bSStefan Berger s->rw_offset = 0; 178edff8678SStefan Berger 179fcbed221SStefan Berger trace_tpm_tis_abort(s->next_locty); 180edff8678SStefan Berger 181edff8678SStefan Berger /* 182edff8678SStefan Berger * Need to react differently depending on who's aborting now and 183edff8678SStefan Berger * which locality will become active afterwards. 184edff8678SStefan Berger */ 1853d4960c7SMarc-André Lureau if (s->aborting_locty == s->next_locty) { 1863d4960c7SMarc-André Lureau s->loc[s->aborting_locty].state = TPM_TIS_STATE_READY; 1873d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[s->aborting_locty], 188fd859081SStefan Berger TPM_TIS_STS_COMMAND_READY); 1893d4960c7SMarc-André Lureau tpm_tis_raise_irq(s, s->aborting_locty, TPM_TIS_INT_COMMAND_READY); 190edff8678SStefan Berger } 191edff8678SStefan Berger 192edff8678SStefan Berger /* locality after abort is another one than the current one */ 1933d4960c7SMarc-André Lureau tpm_tis_new_active_locality(s, s->next_locty); 194edff8678SStefan Berger 1953d4960c7SMarc-André Lureau s->next_locty = TPM_TIS_NO_LOCALITY; 196edff8678SStefan Berger /* nobody's aborting a command anymore */ 1973d4960c7SMarc-André Lureau s->aborting_locty = TPM_TIS_NO_LOCALITY; 198edff8678SStefan Berger } 199edff8678SStefan Berger 200edff8678SStefan Berger /* prepare aborting current command */ 201edff8678SStefan Berger static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty) 202edff8678SStefan Berger { 203edff8678SStefan Berger uint8_t busy_locty; 204edff8678SStefan Berger 205e92b63eaSStefan Berger assert(TPM_TIS_IS_VALID_LOCTY(newlocty)); 206e92b63eaSStefan Berger 207e92b63eaSStefan Berger s->aborting_locty = locty; /* may also be TPM_TIS_NO_LOCALITY */ 2083d4960c7SMarc-André Lureau s->next_locty = newlocty; /* locality after successful abort */ 209edff8678SStefan Berger 210edff8678SStefan Berger /* 211edff8678SStefan Berger * only abort a command using an interrupt if currently executing 212edff8678SStefan Berger * a command AND if there's a valid connection to the vTPM. 213edff8678SStefan Berger */ 214edff8678SStefan Berger for (busy_locty = 0; busy_locty < TPM_TIS_NUM_LOCALITIES; busy_locty++) { 2153d4960c7SMarc-André Lureau if (s->loc[busy_locty].state == TPM_TIS_STATE_EXECUTION) { 216edff8678SStefan Berger /* 217edff8678SStefan Berger * request the backend to cancel. Some backends may not 218edff8678SStefan Berger * support it 219edff8678SStefan Berger */ 2208f0605ccSStefan Berger tpm_backend_cancel_cmd(s->be_driver); 221edff8678SStefan Berger return; 222edff8678SStefan Berger } 223edff8678SStefan Berger } 224edff8678SStefan Berger 2250f5faee3SStefan Berger tpm_tis_abort(s); 226edff8678SStefan Berger } 227edff8678SStefan Berger 22868999059SMarc-André Lureau /* 22968999059SMarc-André Lureau * Callback from the TPM to indicate that the response was received. 23068999059SMarc-André Lureau */ 231ac90053dSEric Auger void tpm_tis_request_completed(TPMState *s, int ret) 232edff8678SStefan Berger { 2330e43b7e6SMarc-André Lureau uint8_t locty = s->cmd.locty; 23468999059SMarc-André Lureau uint8_t l; 23568999059SMarc-André Lureau 236a639f961SStefan Berger assert(TPM_TIS_IS_VALID_LOCTY(locty)); 237a639f961SStefan Berger 23868999059SMarc-André Lureau if (s->cmd.selftest_done) { 23968999059SMarc-André Lureau for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 2406a50bb98SPrasad J Pandit s->loc[l].sts |= TPM_TIS_STS_SELFTEST_DONE; 24168999059SMarc-André Lureau } 24268999059SMarc-André Lureau } 243edff8678SStefan Berger 2446a8a2354SMarc-André Lureau /* FIXME: report error if ret != 0 */ 2453d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 246fd859081SStefan Berger TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE); 2473d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_COMPLETION; 248f999d81bSStefan Berger s->rw_offset = 0; 249edff8678SStefan Berger 2503688d73bSStefan Berger if (trace_event_get_state_backends(TRACE_TPM_UTIL_SHOW_BUFFER)) { 2513688d73bSStefan Berger tpm_util_show_buffer(s->buffer, s->be_buffer_size, "From TPM"); 252fcbed221SStefan Berger } 253298d8b81SStefan Berger 2543d4960c7SMarc-André Lureau if (TPM_TIS_IS_VALID_LOCTY(s->next_locty)) { 2550f5faee3SStefan Berger tpm_tis_abort(s); 256edff8678SStefan Berger } 257edff8678SStefan Berger 258edff8678SStefan Berger tpm_tis_raise_irq(s, locty, 259edff8678SStefan Berger TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID); 260edff8678SStefan Berger } 261edff8678SStefan Berger 262edff8678SStefan Berger /* 263edff8678SStefan Berger * Read a byte of response data 264edff8678SStefan Berger */ 265edff8678SStefan Berger static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty) 266edff8678SStefan Berger { 267edff8678SStefan Berger uint32_t ret = TPM_TIS_NO_DATA_BYTE; 268edff8678SStefan Berger uint16_t len; 269edff8678SStefan Berger 2703d4960c7SMarc-André Lureau if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) { 271c5496b97SStefan Berger len = MIN(tpm_cmd_get_size(&s->buffer), 272e6b703f6SStefan Berger s->be_buffer_size); 273edff8678SStefan Berger 274f999d81bSStefan Berger ret = s->buffer[s->rw_offset++]; 275f999d81bSStefan Berger if (s->rw_offset >= len) { 276edff8678SStefan Berger /* got last byte */ 2773d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 278edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); 279edff8678SStefan Berger } 280fcbed221SStefan Berger trace_tpm_tis_data_read(ret, s->rw_offset - 1); 281edff8678SStefan Berger } 282edff8678SStefan Berger 283edff8678SStefan Berger return ret; 284edff8678SStefan Berger } 285edff8678SStefan Berger 2868db7c415SStefan Berger #ifdef DEBUG_TIS 287ca75c421SEric Auger static void tpm_tis_dump_state(TPMState *s, hwaddr addr) 2888db7c415SStefan Berger { 2898db7c415SStefan Berger static const unsigned regs[] = { 2908db7c415SStefan Berger TPM_TIS_REG_ACCESS, 2918db7c415SStefan Berger TPM_TIS_REG_INT_ENABLE, 2928db7c415SStefan Berger TPM_TIS_REG_INT_VECTOR, 2938db7c415SStefan Berger TPM_TIS_REG_INT_STATUS, 2948db7c415SStefan Berger TPM_TIS_REG_INTF_CAPABILITY, 2958db7c415SStefan Berger TPM_TIS_REG_STS, 2968db7c415SStefan Berger TPM_TIS_REG_DID_VID, 2978db7c415SStefan Berger TPM_TIS_REG_RID, 2988db7c415SStefan Berger 0xfff}; 2998db7c415SStefan Berger int idx; 3008db7c415SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 3018db7c415SStefan Berger hwaddr base = addr & ~0xfff; 3028db7c415SStefan Berger 303fcbed221SStefan Berger printf("tpm_tis: active locality : %d\n" 3048db7c415SStefan Berger "tpm_tis: state of locality %d : %d\n" 3058db7c415SStefan Berger "tpm_tis: register dump:\n", 3063d4960c7SMarc-André Lureau s->active_locty, 3073d4960c7SMarc-André Lureau locty, s->loc[locty].state); 3088db7c415SStefan Berger 3098db7c415SStefan Berger for (idx = 0; regs[idx] != 0xfff; idx++) { 310fcbed221SStefan Berger printf("tpm_tis: 0x%04x : 0x%08x\n", regs[idx], 311ca75c421SEric Auger (int)tpm_tis_mmio_read(s, base + regs[idx], 4)); 3128db7c415SStefan Berger } 3138db7c415SStefan Berger 314fcbed221SStefan Berger printf("tpm_tis: r/w offset : %d\n" 3158db7c415SStefan Berger "tpm_tis: result buffer : ", 316f999d81bSStefan Berger s->rw_offset); 3178db7c415SStefan Berger for (idx = 0; 318c5496b97SStefan Berger idx < MIN(tpm_cmd_get_size(&s->buffer), s->be_buffer_size); 3198db7c415SStefan Berger idx++) { 320fcbed221SStefan Berger printf("%c%02x%s", 321f999d81bSStefan Berger s->rw_offset == idx ? '>' : ' ', 322c5496b97SStefan Berger s->buffer[idx], 3238db7c415SStefan Berger ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : ""); 3248db7c415SStefan Berger } 325fcbed221SStefan Berger printf("\n"); 3268db7c415SStefan Berger } 3278db7c415SStefan Berger #endif 3288db7c415SStefan Berger 329edff8678SStefan Berger /* 330edff8678SStefan Berger * Read a register of the TIS interface 331edff8678SStefan Berger * See specs pages 33-63 for description of the registers 332edff8678SStefan Berger */ 333edff8678SStefan Berger static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, 334edff8678SStefan Berger unsigned size) 335edff8678SStefan Berger { 336edff8678SStefan Berger TPMState *s = opaque; 337edff8678SStefan Berger uint16_t offset = addr & 0xffc; 338edff8678SStefan Berger uint8_t shift = (addr & 0x3) * 8; 339edff8678SStefan Berger uint32_t val = 0xffffffff; 340edff8678SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 341edff8678SStefan Berger uint32_t avail; 342feeb755fSStefan Berger uint8_t v; 343edff8678SStefan Berger 3448f0605ccSStefan Berger if (tpm_backend_had_startup_error(s->be_driver)) { 3456cd65969SStefan Berger return 0; 346edff8678SStefan Berger } 347edff8678SStefan Berger 348edff8678SStefan Berger switch (offset) { 349edff8678SStefan Berger case TPM_TIS_REG_ACCESS: 350edff8678SStefan Berger /* never show the SEIZE flag even though we use it internally */ 3513d4960c7SMarc-André Lureau val = s->loc[locty].access & ~TPM_TIS_ACCESS_SEIZE; 352edff8678SStefan Berger /* the pending flag is always calculated */ 353edff8678SStefan Berger if (tpm_tis_check_request_use_except(s, locty)) { 354edff8678SStefan Berger val |= TPM_TIS_ACCESS_PENDING_REQUEST; 355edff8678SStefan Berger } 3568f0605ccSStefan Berger val |= !tpm_backend_get_tpm_established_flag(s->be_driver); 357edff8678SStefan Berger break; 358edff8678SStefan Berger case TPM_TIS_REG_INT_ENABLE: 3593d4960c7SMarc-André Lureau val = s->loc[locty].inte; 360edff8678SStefan Berger break; 361edff8678SStefan Berger case TPM_TIS_REG_INT_VECTOR: 3623d4960c7SMarc-André Lureau val = s->irq_num; 363edff8678SStefan Berger break; 364edff8678SStefan Berger case TPM_TIS_REG_INT_STATUS: 3653d4960c7SMarc-André Lureau val = s->loc[locty].ints; 366edff8678SStefan Berger break; 367edff8678SStefan Berger case TPM_TIS_REG_INTF_CAPABILITY: 368116694c3SStefan Berger switch (s->be_tpm_version) { 369116694c3SStefan Berger case TPM_VERSION_UNSPEC: 370116694c3SStefan Berger val = 0; 371116694c3SStefan Berger break; 372116694c3SStefan Berger case TPM_VERSION_1_2: 373116694c3SStefan Berger val = TPM_TIS_CAPABILITIES_SUPPORTED1_3; 374116694c3SStefan Berger break; 375116694c3SStefan Berger case TPM_VERSION_2_0: 376116694c3SStefan Berger val = TPM_TIS_CAPABILITIES_SUPPORTED2_0; 377116694c3SStefan Berger break; 378116694c3SStefan Berger } 379edff8678SStefan Berger break; 380edff8678SStefan Berger case TPM_TIS_REG_STS: 3813d4960c7SMarc-André Lureau if (s->active_locty == locty) { 3823d4960c7SMarc-André Lureau if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) { 383edff8678SStefan Berger val = TPM_TIS_BURST_COUNT( 384c5496b97SStefan Berger MIN(tpm_cmd_get_size(&s->buffer), 385e6b703f6SStefan Berger s->be_buffer_size) 386f999d81bSStefan Berger - s->rw_offset) | s->loc[locty].sts; 387edff8678SStefan Berger } else { 388f999d81bSStefan Berger avail = s->be_buffer_size - s->rw_offset; 389edff8678SStefan Berger /* 390edff8678SStefan Berger * byte-sized reads should not return 0x00 for 0x100 391edff8678SStefan Berger * available bytes. 392edff8678SStefan Berger */ 393edff8678SStefan Berger if (size == 1 && avail > 0xff) { 394edff8678SStefan Berger avail = 0xff; 395edff8678SStefan Berger } 3963d4960c7SMarc-André Lureau val = TPM_TIS_BURST_COUNT(avail) | s->loc[locty].sts; 397edff8678SStefan Berger } 398edff8678SStefan Berger } 399edff8678SStefan Berger break; 400edff8678SStefan Berger case TPM_TIS_REG_DATA_FIFO: 4012eae8c75SStefan Berger case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END: 4023d4960c7SMarc-André Lureau if (s->active_locty == locty) { 403feeb755fSStefan Berger if (size > 4 - (addr & 0x3)) { 404feeb755fSStefan Berger /* prevent access beyond FIFO */ 405feeb755fSStefan Berger size = 4 - (addr & 0x3); 406feeb755fSStefan Berger } 407feeb755fSStefan Berger val = 0; 408feeb755fSStefan Berger shift = 0; 409feeb755fSStefan Berger while (size > 0) { 4103d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 411edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 412feeb755fSStefan Berger v = tpm_tis_data_read(s, locty); 413edff8678SStefan Berger break; 414edff8678SStefan Berger default: 415feeb755fSStefan Berger v = TPM_TIS_NO_DATA_BYTE; 416edff8678SStefan Berger break; 417edff8678SStefan Berger } 418feeb755fSStefan Berger val |= (v << shift); 419feeb755fSStefan Berger shift += 8; 420feeb755fSStefan Berger size--; 421feeb755fSStefan Berger } 422feeb755fSStefan Berger shift = 0; /* no more adjustments */ 423edff8678SStefan Berger } 424edff8678SStefan Berger break; 425116694c3SStefan Berger case TPM_TIS_REG_INTERFACE_ID: 4263d4960c7SMarc-André Lureau val = s->loc[locty].iface_id; 427116694c3SStefan Berger break; 428edff8678SStefan Berger case TPM_TIS_REG_DID_VID: 429edff8678SStefan Berger val = (TPM_TIS_TPM_DID << 16) | TPM_TIS_TPM_VID; 430edff8678SStefan Berger break; 431edff8678SStefan Berger case TPM_TIS_REG_RID: 432edff8678SStefan Berger val = TPM_TIS_TPM_RID; 433edff8678SStefan Berger break; 4348db7c415SStefan Berger #ifdef DEBUG_TIS 4358db7c415SStefan Berger case TPM_TIS_REG_DEBUG: 436ca75c421SEric Auger tpm_tis_dump_state(s, addr); 4378db7c415SStefan Berger break; 4388db7c415SStefan Berger #endif 439edff8678SStefan Berger } 440edff8678SStefan Berger 441edff8678SStefan Berger if (shift) { 442edff8678SStefan Berger val >>= shift; 443edff8678SStefan Berger } 444edff8678SStefan Berger 445fcbed221SStefan Berger trace_tpm_tis_mmio_read(size, addr, val); 446edff8678SStefan Berger 447edff8678SStefan Berger return val; 448edff8678SStefan Berger } 449edff8678SStefan Berger 450edff8678SStefan Berger /* 451edff8678SStefan Berger * Write a value to a register of the TIS interface 452edff8678SStefan Berger * See specs pages 33-63 for description of the registers 453edff8678SStefan Berger */ 454ff2bc0c1SMarc-André Lureau static void tpm_tis_mmio_write(void *opaque, hwaddr addr, 455ff2bc0c1SMarc-André Lureau uint64_t val, unsigned size) 456edff8678SStefan Berger { 457edff8678SStefan Berger TPMState *s = opaque; 458feeb755fSStefan Berger uint16_t off = addr & 0xffc; 459feeb755fSStefan Berger uint8_t shift = (addr & 0x3) * 8; 460edff8678SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 461edff8678SStefan Berger uint8_t active_locty, l; 462edff8678SStefan Berger int c, set_new_locty = 1; 463edff8678SStefan Berger uint16_t len; 464feeb755fSStefan Berger uint32_t mask = (size == 1) ? 0xff : ((size == 2) ? 0xffff : ~0); 465edff8678SStefan Berger 466fcbed221SStefan Berger trace_tpm_tis_mmio_write(size, addr, val); 467edff8678SStefan Berger 468ff2bc0c1SMarc-André Lureau if (locty == 4) { 469fcbed221SStefan Berger trace_tpm_tis_mmio_write_locty4(); 470edff8678SStefan Berger return; 471edff8678SStefan Berger } 472edff8678SStefan Berger 4738f0605ccSStefan Berger if (tpm_backend_had_startup_error(s->be_driver)) { 474edff8678SStefan Berger return; 475edff8678SStefan Berger } 476edff8678SStefan Berger 477feeb755fSStefan Berger val &= mask; 478feeb755fSStefan Berger 479feeb755fSStefan Berger if (shift) { 480feeb755fSStefan Berger val <<= shift; 481feeb755fSStefan Berger mask <<= shift; 482feeb755fSStefan Berger } 483feeb755fSStefan Berger 484feeb755fSStefan Berger mask ^= 0xffffffff; 485feeb755fSStefan Berger 486edff8678SStefan Berger switch (off) { 487edff8678SStefan Berger case TPM_TIS_REG_ACCESS: 488edff8678SStefan Berger 489edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_SEIZE)) { 490edff8678SStefan Berger val &= ~(TPM_TIS_ACCESS_REQUEST_USE | 491edff8678SStefan Berger TPM_TIS_ACCESS_ACTIVE_LOCALITY); 492edff8678SStefan Berger } 493edff8678SStefan Berger 4943d4960c7SMarc-André Lureau active_locty = s->active_locty; 495edff8678SStefan Berger 496edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) { 497edff8678SStefan Berger /* give up locality if currently owned */ 4983d4960c7SMarc-André Lureau if (s->active_locty == locty) { 499fcbed221SStefan Berger trace_tpm_tis_mmio_write_release_locty(locty); 500edff8678SStefan Berger 501edff8678SStefan Berger uint8_t newlocty = TPM_TIS_NO_LOCALITY; 502edff8678SStefan Berger /* anybody wants the locality ? */ 503edff8678SStefan Berger for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) { 5043d4960c7SMarc-André Lureau if ((s->loc[c].access & TPM_TIS_ACCESS_REQUEST_USE)) { 505fcbed221SStefan Berger trace_tpm_tis_mmio_write_locty_req_use(c); 506edff8678SStefan Berger newlocty = c; 507edff8678SStefan Berger break; 508edff8678SStefan Berger } 509edff8678SStefan Berger } 510fcbed221SStefan Berger trace_tpm_tis_mmio_write_next_locty(newlocty); 511edff8678SStefan Berger 512edff8678SStefan Berger if (TPM_TIS_IS_VALID_LOCTY(newlocty)) { 513edff8678SStefan Berger set_new_locty = 0; 514edff8678SStefan Berger tpm_tis_prep_abort(s, locty, newlocty); 515edff8678SStefan Berger } else { 516edff8678SStefan Berger active_locty = TPM_TIS_NO_LOCALITY; 517edff8678SStefan Berger } 518edff8678SStefan Berger } else { 519edff8678SStefan Berger /* not currently the owner; clear a pending request */ 5203d4960c7SMarc-André Lureau s->loc[locty].access &= ~TPM_TIS_ACCESS_REQUEST_USE; 521edff8678SStefan Berger } 522edff8678SStefan Berger } 523edff8678SStefan Berger 524edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_BEEN_SEIZED)) { 5253d4960c7SMarc-André Lureau s->loc[locty].access &= ~TPM_TIS_ACCESS_BEEN_SEIZED; 526edff8678SStefan Berger } 527edff8678SStefan Berger 528edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_SEIZE)) { 529edff8678SStefan Berger /* 530edff8678SStefan Berger * allow seize if a locality is active and the requesting 531edff8678SStefan Berger * locality is higher than the one that's active 532edff8678SStefan Berger * OR 533edff8678SStefan Berger * allow seize for requesting locality if no locality is 534edff8678SStefan Berger * active 535edff8678SStefan Berger */ 5363d4960c7SMarc-André Lureau while ((TPM_TIS_IS_VALID_LOCTY(s->active_locty) && 5373d4960c7SMarc-André Lureau locty > s->active_locty) || 5383d4960c7SMarc-André Lureau !TPM_TIS_IS_VALID_LOCTY(s->active_locty)) { 539*aadad398SJafar Abdi bool higher_seize = false; 540edff8678SStefan Berger 541edff8678SStefan Berger /* already a pending SEIZE ? */ 5423d4960c7SMarc-André Lureau if ((s->loc[locty].access & TPM_TIS_ACCESS_SEIZE)) { 543edff8678SStefan Berger break; 544edff8678SStefan Berger } 545edff8678SStefan Berger 546edff8678SStefan Berger /* check for ongoing seize by a higher locality */ 547edff8678SStefan Berger for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES; l++) { 5483d4960c7SMarc-André Lureau if ((s->loc[l].access & TPM_TIS_ACCESS_SEIZE)) { 549*aadad398SJafar Abdi higher_seize = true; 550edff8678SStefan Berger break; 551edff8678SStefan Berger } 552edff8678SStefan Berger } 553edff8678SStefan Berger 554edff8678SStefan Berger if (higher_seize) { 555edff8678SStefan Berger break; 556edff8678SStefan Berger } 557edff8678SStefan Berger 558edff8678SStefan Berger /* cancel any seize by a lower locality */ 55937b55d67SLiam Merwick for (l = 0; l < locty; l++) { 5603d4960c7SMarc-André Lureau s->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE; 561edff8678SStefan Berger } 562edff8678SStefan Berger 5633d4960c7SMarc-André Lureau s->loc[locty].access |= TPM_TIS_ACCESS_SEIZE; 564fcbed221SStefan Berger 565fcbed221SStefan Berger trace_tpm_tis_mmio_write_locty_seized(locty, s->active_locty); 566fcbed221SStefan Berger trace_tpm_tis_mmio_write_init_abort(); 567fcbed221SStefan Berger 568edff8678SStefan Berger set_new_locty = 0; 5693d4960c7SMarc-André Lureau tpm_tis_prep_abort(s, s->active_locty, locty); 570edff8678SStefan Berger break; 571edff8678SStefan Berger } 572edff8678SStefan Berger } 573edff8678SStefan Berger 574edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_REQUEST_USE)) { 5753d4960c7SMarc-André Lureau if (s->active_locty != locty) { 5763d4960c7SMarc-André Lureau if (TPM_TIS_IS_VALID_LOCTY(s->active_locty)) { 5773d4960c7SMarc-André Lureau s->loc[locty].access |= TPM_TIS_ACCESS_REQUEST_USE; 578edff8678SStefan Berger } else { 579edff8678SStefan Berger /* no locality active -> make this one active now */ 580edff8678SStefan Berger active_locty = locty; 581edff8678SStefan Berger } 582edff8678SStefan Berger } 583edff8678SStefan Berger } 584edff8678SStefan Berger 585edff8678SStefan Berger if (set_new_locty) { 586edff8678SStefan Berger tpm_tis_new_active_locality(s, active_locty); 587edff8678SStefan Berger } 588edff8678SStefan Berger 589edff8678SStefan Berger break; 590edff8678SStefan Berger case TPM_TIS_REG_INT_ENABLE: 5913d4960c7SMarc-André Lureau if (s->active_locty != locty) { 592edff8678SStefan Berger break; 593edff8678SStefan Berger } 594edff8678SStefan Berger 5953d4960c7SMarc-André Lureau s->loc[locty].inte &= mask; 5963d4960c7SMarc-André Lureau s->loc[locty].inte |= (val & (TPM_TIS_INT_ENABLED | 597edff8678SStefan Berger TPM_TIS_INT_POLARITY_MASK | 598edff8678SStefan Berger TPM_TIS_INTERRUPTS_SUPPORTED)); 599edff8678SStefan Berger break; 600edff8678SStefan Berger case TPM_TIS_REG_INT_VECTOR: 601edff8678SStefan Berger /* hard wired -- ignore */ 602edff8678SStefan Berger break; 603edff8678SStefan Berger case TPM_TIS_REG_INT_STATUS: 6043d4960c7SMarc-André Lureau if (s->active_locty != locty) { 605edff8678SStefan Berger break; 606edff8678SStefan Berger } 607edff8678SStefan Berger 608edff8678SStefan Berger /* clearing of interrupt flags */ 609edff8678SStefan Berger if (((val & TPM_TIS_INTERRUPTS_SUPPORTED)) && 6103d4960c7SMarc-André Lureau (s->loc[locty].ints & TPM_TIS_INTERRUPTS_SUPPORTED)) { 6113d4960c7SMarc-André Lureau s->loc[locty].ints &= ~val; 6123d4960c7SMarc-André Lureau if (s->loc[locty].ints == 0) { 6133d4960c7SMarc-André Lureau qemu_irq_lower(s->irq); 614fcbed221SStefan Berger trace_tpm_tis_mmio_write_lowering_irq(); 615edff8678SStefan Berger } 616edff8678SStefan Berger } 6173d4960c7SMarc-André Lureau s->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED); 618edff8678SStefan Berger break; 619edff8678SStefan Berger case TPM_TIS_REG_STS: 6203d4960c7SMarc-André Lureau if (s->active_locty != locty) { 621edff8678SStefan Berger break; 622edff8678SStefan Berger } 623edff8678SStefan Berger 624116694c3SStefan Berger if (s->be_tpm_version == TPM_VERSION_2_0) { 625116694c3SStefan Berger /* some flags that are only supported for TPM 2 */ 626116694c3SStefan Berger if (val & TPM_TIS_STS_COMMAND_CANCEL) { 6273d4960c7SMarc-André Lureau if (s->loc[locty].state == TPM_TIS_STATE_EXECUTION) { 628116694c3SStefan Berger /* 629116694c3SStefan Berger * request the backend to cancel. Some backends may not 630116694c3SStefan Berger * support it 631116694c3SStefan Berger */ 632116694c3SStefan Berger tpm_backend_cancel_cmd(s->be_driver); 633116694c3SStefan Berger } 634116694c3SStefan Berger } 635116694c3SStefan Berger 636116694c3SStefan Berger if (val & TPM_TIS_STS_RESET_ESTABLISHMENT_BIT) { 637116694c3SStefan Berger if (locty == 3 || locty == 4) { 638116694c3SStefan Berger tpm_backend_reset_tpm_established_flag(s->be_driver, locty); 639116694c3SStefan Berger } 640116694c3SStefan Berger } 641116694c3SStefan Berger } 642116694c3SStefan Berger 643edff8678SStefan Berger val &= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO | 644edff8678SStefan Berger TPM_TIS_STS_RESPONSE_RETRY); 645edff8678SStefan Berger 646edff8678SStefan Berger if (val == TPM_TIS_STS_COMMAND_READY) { 6473d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 648edff8678SStefan Berger 649edff8678SStefan Berger case TPM_TIS_STATE_READY: 650f999d81bSStefan Berger s->rw_offset = 0; 651edff8678SStefan Berger break; 652edff8678SStefan Berger 653edff8678SStefan Berger case TPM_TIS_STATE_IDLE: 6543d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_COMMAND_READY); 6553d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_READY; 656edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); 657edff8678SStefan Berger break; 658edff8678SStefan Berger 659edff8678SStefan Berger case TPM_TIS_STATE_EXECUTION: 660edff8678SStefan Berger case TPM_TIS_STATE_RECEPTION: 661edff8678SStefan Berger /* abort currently running command */ 662fcbed221SStefan Berger trace_tpm_tis_mmio_write_init_abort(); 663edff8678SStefan Berger tpm_tis_prep_abort(s, locty, locty); 664edff8678SStefan Berger break; 665edff8678SStefan Berger 666edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 667f999d81bSStefan Berger s->rw_offset = 0; 668edff8678SStefan Berger /* shortcut to ready state with C/R set */ 6693d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_READY; 6703d4960c7SMarc-André Lureau if (!(s->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) { 6713d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 672fd859081SStefan Berger TPM_TIS_STS_COMMAND_READY); 673edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); 674edff8678SStefan Berger } 6753d4960c7SMarc-André Lureau s->loc[locty].sts &= ~(TPM_TIS_STS_DATA_AVAILABLE); 676edff8678SStefan Berger break; 677edff8678SStefan Berger 678edff8678SStefan Berger } 679edff8678SStefan Berger } else if (val == TPM_TIS_STS_TPM_GO) { 6803d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 681edff8678SStefan Berger case TPM_TIS_STATE_RECEPTION: 6823d4960c7SMarc-André Lureau if ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) == 0) { 683edff8678SStefan Berger tpm_tis_tpm_send(s, locty); 684edff8678SStefan Berger } 685edff8678SStefan Berger break; 686edff8678SStefan Berger default: 687edff8678SStefan Berger /* ignore */ 688edff8678SStefan Berger break; 689edff8678SStefan Berger } 690edff8678SStefan Berger } else if (val == TPM_TIS_STS_RESPONSE_RETRY) { 6913d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 692edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 693f999d81bSStefan Berger s->rw_offset = 0; 6943d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 695fd859081SStefan Berger TPM_TIS_STS_VALID| 696fd859081SStefan Berger TPM_TIS_STS_DATA_AVAILABLE); 697edff8678SStefan Berger break; 698edff8678SStefan Berger default: 699edff8678SStefan Berger /* ignore */ 700edff8678SStefan Berger break; 701edff8678SStefan Berger } 702edff8678SStefan Berger } 703edff8678SStefan Berger break; 704edff8678SStefan Berger case TPM_TIS_REG_DATA_FIFO: 7052eae8c75SStefan Berger case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END: 706edff8678SStefan Berger /* data fifo */ 7073d4960c7SMarc-André Lureau if (s->active_locty != locty) { 708edff8678SStefan Berger break; 709edff8678SStefan Berger } 710edff8678SStefan Berger 7113d4960c7SMarc-André Lureau if (s->loc[locty].state == TPM_TIS_STATE_IDLE || 7123d4960c7SMarc-André Lureau s->loc[locty].state == TPM_TIS_STATE_EXECUTION || 7133d4960c7SMarc-André Lureau s->loc[locty].state == TPM_TIS_STATE_COMPLETION) { 714edff8678SStefan Berger /* drop the byte */ 715edff8678SStefan Berger } else { 716fcbed221SStefan Berger trace_tpm_tis_mmio_write_data2send(val, size); 7173d4960c7SMarc-André Lureau if (s->loc[locty].state == TPM_TIS_STATE_READY) { 7183d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_RECEPTION; 7193d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 720fd859081SStefan Berger TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); 721edff8678SStefan Berger } 722edff8678SStefan Berger 723feeb755fSStefan Berger val >>= shift; 724feeb755fSStefan Berger if (size > 4 - (addr & 0x3)) { 725feeb755fSStefan Berger /* prevent access beyond FIFO */ 726feeb755fSStefan Berger size = 4 - (addr & 0x3); 727feeb755fSStefan Berger } 728feeb755fSStefan Berger 7293d4960c7SMarc-André Lureau while ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) && size > 0) { 730f999d81bSStefan Berger if (s->rw_offset < s->be_buffer_size) { 731f999d81bSStefan Berger s->buffer[s->rw_offset++] = 732e6b703f6SStefan Berger (uint8_t)val; 733feeb755fSStefan Berger val >>= 8; 734feeb755fSStefan Berger size--; 735edff8678SStefan Berger } else { 7363d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 737edff8678SStefan Berger } 738edff8678SStefan Berger } 739edff8678SStefan Berger 740edff8678SStefan Berger /* check for complete packet */ 741f999d81bSStefan Berger if (s->rw_offset > 5 && 7423d4960c7SMarc-André Lureau (s->loc[locty].sts & TPM_TIS_STS_EXPECT)) { 743edff8678SStefan Berger /* we have a packet length - see if we have all of it */ 7443d4960c7SMarc-André Lureau bool need_irq = !(s->loc[locty].sts & TPM_TIS_STS_VALID); 745d8383d61SMarc-André Lureau 746c5496b97SStefan Berger len = tpm_cmd_get_size(&s->buffer); 747f999d81bSStefan Berger if (len > s->rw_offset) { 7483d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 749fd859081SStefan Berger TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); 750edff8678SStefan Berger } else { 751edff8678SStefan Berger /* packet complete */ 7523d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 753edff8678SStefan Berger } 75429b558d8SStefan Berger if (need_irq) { 755edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); 756edff8678SStefan Berger } 757edff8678SStefan Berger } 758edff8678SStefan Berger } 759edff8678SStefan Berger break; 760116694c3SStefan Berger case TPM_TIS_REG_INTERFACE_ID: 761116694c3SStefan Berger if (val & TPM_TIS_IFACE_ID_INT_SEL_LOCK) { 762116694c3SStefan Berger for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 7633d4960c7SMarc-André Lureau s->loc[l].iface_id |= TPM_TIS_IFACE_ID_INT_SEL_LOCK; 764116694c3SStefan Berger } 765116694c3SStefan Berger } 766116694c3SStefan Berger break; 767edff8678SStefan Berger } 768edff8678SStefan Berger } 769edff8678SStefan Berger 770ac90053dSEric Auger const MemoryRegionOps tpm_tis_memory_ops = { 771edff8678SStefan Berger .read = tpm_tis_mmio_read, 772edff8678SStefan Berger .write = tpm_tis_mmio_write, 773edff8678SStefan Berger .endianness = DEVICE_LITTLE_ENDIAN, 774edff8678SStefan Berger .valid = { 775edff8678SStefan Berger .min_access_size = 1, 776edff8678SStefan Berger .max_access_size = 4, 777edff8678SStefan Berger }, 778edff8678SStefan Berger }; 779edff8678SStefan Berger 780edff8678SStefan Berger /* 7815cb18b3dSStefan Berger * Get the TPMVersion of the backend device being used 7825cb18b3dSStefan Berger */ 783ac90053dSEric Auger enum TPMVersion tpm_tis_get_tpm_version(TPMState *s) 7845cb18b3dSStefan Berger { 785ad4aca69SStefan Berger if (tpm_backend_had_startup_error(s->be_driver)) { 786ad4aca69SStefan Berger return TPM_VERSION_UNSPEC; 787ad4aca69SStefan Berger } 788ad4aca69SStefan Berger 7895cb18b3dSStefan Berger return tpm_backend_get_tpm_version(s->be_driver); 7905cb18b3dSStefan Berger } 7915cb18b3dSStefan Berger 7925cb18b3dSStefan Berger /* 793edff8678SStefan Berger * This function is called when the machine starts, resets or due to 794edff8678SStefan Berger * S3 resume. 795edff8678SStefan Berger */ 796ac90053dSEric Auger void tpm_tis_reset(TPMState *s) 797edff8678SStefan Berger { 798edff8678SStefan Berger int c; 799edff8678SStefan Berger 800116694c3SStefan Berger s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver); 8011af3d63eSStefan Berger s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->be_driver), 8021af3d63eSStefan Berger TPM_TIS_BUFFER_MAX); 803116694c3SStefan Berger 804ffab1be7SMarc-André Lureau if (s->ppi_enabled) { 805ffab1be7SMarc-André Lureau tpm_ppi_reset(&s->ppi); 806ffab1be7SMarc-André Lureau } 8078f0605ccSStefan Berger tpm_backend_reset(s->be_driver); 808edff8678SStefan Berger 8093d4960c7SMarc-André Lureau s->active_locty = TPM_TIS_NO_LOCALITY; 8103d4960c7SMarc-André Lureau s->next_locty = TPM_TIS_NO_LOCALITY; 8113d4960c7SMarc-André Lureau s->aborting_locty = TPM_TIS_NO_LOCALITY; 812edff8678SStefan Berger 813edff8678SStefan Berger for (c = 0; c < TPM_TIS_NUM_LOCALITIES; c++) { 8143d4960c7SMarc-André Lureau s->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS; 815116694c3SStefan Berger switch (s->be_tpm_version) { 816116694c3SStefan Berger case TPM_VERSION_UNSPEC: 817116694c3SStefan Berger break; 818116694c3SStefan Berger case TPM_VERSION_1_2: 8193d4960c7SMarc-André Lureau s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY1_2; 8203d4960c7SMarc-André Lureau s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3; 821116694c3SStefan Berger break; 822116694c3SStefan Berger case TPM_VERSION_2_0: 8233d4960c7SMarc-André Lureau s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY2_0; 8243d4960c7SMarc-André Lureau s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0; 825116694c3SStefan Berger break; 826116694c3SStefan Berger } 8273d4960c7SMarc-André Lureau s->loc[c].inte = TPM_TIS_INT_POLARITY_LOW_LEVEL; 8283d4960c7SMarc-André Lureau s->loc[c].ints = 0; 8293d4960c7SMarc-André Lureau s->loc[c].state = TPM_TIS_STATE_IDLE; 830edff8678SStefan Berger 831f999d81bSStefan Berger s->rw_offset = 0; 832edff8678SStefan Berger } 833edff8678SStefan Berger 834bcfd16feSStefan Berger if (tpm_backend_startup_tpm(s->be_driver, s->be_buffer_size) < 0) { 835bcfd16feSStefan Berger exit(1); 836bcfd16feSStefan Berger } 837edff8678SStefan Berger } 838edff8678SStefan Berger 8399ec08c48SStefan Berger /* persistent state handling */ 8409ec08c48SStefan Berger 841ac90053dSEric Auger int tpm_tis_pre_save(TPMState *s) 8429ec08c48SStefan Berger { 8439ec08c48SStefan Berger uint8_t locty = s->active_locty; 8449ec08c48SStefan Berger 8459ec08c48SStefan Berger trace_tpm_tis_pre_save(locty, s->rw_offset); 8469ec08c48SStefan Berger 8479ec08c48SStefan Berger if (DEBUG_TIS) { 848ca75c421SEric Auger tpm_tis_dump_state(s, 0); 8499ec08c48SStefan Berger } 8509ec08c48SStefan Berger 8519ec08c48SStefan Berger /* 8529ec08c48SStefan Berger * Synchronize with backend completion. 8539ec08c48SStefan Berger */ 8549ec08c48SStefan Berger tpm_backend_finish_sync(s->be_driver); 8559ec08c48SStefan Berger 8569ec08c48SStefan Berger return 0; 8579ec08c48SStefan Berger } 8589ec08c48SStefan Berger 859ac90053dSEric Auger const VMStateDescription vmstate_locty = { 8609ec08c48SStefan Berger .name = "tpm-tis/locty", 8619ec08c48SStefan Berger .version_id = 0, 8629ec08c48SStefan Berger .fields = (VMStateField[]) { 8639ec08c48SStefan Berger VMSTATE_UINT32(state, TPMLocality), 8649ec08c48SStefan Berger VMSTATE_UINT32(inte, TPMLocality), 8659ec08c48SStefan Berger VMSTATE_UINT32(ints, TPMLocality), 8669ec08c48SStefan Berger VMSTATE_UINT8(access, TPMLocality), 8679ec08c48SStefan Berger VMSTATE_UINT32(sts, TPMLocality), 8689ec08c48SStefan Berger VMSTATE_UINT32(iface_id, TPMLocality), 8699ec08c48SStefan Berger VMSTATE_END_OF_LIST(), 8709ec08c48SStefan Berger } 8719ec08c48SStefan Berger }; 8729ec08c48SStefan Berger 873