1edff8678SStefan Berger /* 2edff8678SStefan Berger * tpm_tis.c - QEMU's TPM TIS interface emulator 3edff8678SStefan Berger * 4edff8678SStefan Berger * Copyright (C) 2006,2010-2013 IBM Corporation 5edff8678SStefan Berger * 6edff8678SStefan Berger * Authors: 7edff8678SStefan Berger * Stefan Berger <stefanb@us.ibm.com> 8edff8678SStefan Berger * David Safford <safford@us.ibm.com> 9edff8678SStefan Berger * 10edff8678SStefan Berger * Xen 4 support: Andrease Niederl <andreas.niederl@iaik.tugraz.at> 11edff8678SStefan Berger * 12edff8678SStefan Berger * This work is licensed under the terms of the GNU GPL, version 2 or later. 13edff8678SStefan Berger * See the COPYING file in the top-level directory. 14edff8678SStefan Berger * 15edff8678SStefan Berger * Implementation of the TIS interface according to specs found at 16edff8678SStefan Berger * http://www.trustedcomputinggroup.org. This implementation currently 179dd5c40dSStefan Berger * supports version 1.3, 21 March 2013 18edff8678SStefan Berger * In the developers menu choose the PC Client section then find the TIS 19edff8678SStefan Berger * specification. 20116694c3SStefan Berger * 21116694c3SStefan Berger * TPM TIS for TPM 2 implementation following TCG PC Client Platform 22116694c3SStefan Berger * TPM Profile (PTP) Specification, Familiy 2.0, Revision 00.43 23edff8678SStefan Berger */ 24edff8678SStefan Berger 250430891cSPeter Maydell #include "qemu/osdep.h" 26732cd587SMarc-André Lureau #include "hw/isa/isa.h" 27023299d8SMarc-André Lureau #include "qapi/error.h" 28023299d8SMarc-André Lureau 29023299d8SMarc-André Lureau #include "hw/acpi/tpm.h" 30023299d8SMarc-André Lureau #include "hw/pci/pci_ids.h" 31dccfcd0eSPaolo Bonzini #include "sysemu/tpm_backend.h" 32edff8678SStefan Berger #include "tpm_int.h" 335cf954d0SMarc-André Lureau #include "tpm_util.h" 34fcbed221SStefan Berger #include "trace.h" 35732cd587SMarc-André Lureau 36732cd587SMarc-André Lureau #define TPM_TIS_NUM_LOCALITIES 5 /* per spec */ 37732cd587SMarc-André Lureau #define TPM_TIS_LOCALITY_SHIFT 12 38732cd587SMarc-André Lureau #define TPM_TIS_NO_LOCALITY 0xff 39732cd587SMarc-André Lureau 40732cd587SMarc-André Lureau #define TPM_TIS_IS_VALID_LOCTY(x) ((x) < TPM_TIS_NUM_LOCALITIES) 41732cd587SMarc-André Lureau 42732cd587SMarc-André Lureau #define TPM_TIS_BUFFER_MAX 4096 43732cd587SMarc-André Lureau 44732cd587SMarc-André Lureau typedef enum { 45732cd587SMarc-André Lureau TPM_TIS_STATE_IDLE = 0, 46732cd587SMarc-André Lureau TPM_TIS_STATE_READY, 47732cd587SMarc-André Lureau TPM_TIS_STATE_COMPLETION, 48732cd587SMarc-André Lureau TPM_TIS_STATE_EXECUTION, 49732cd587SMarc-André Lureau TPM_TIS_STATE_RECEPTION, 50732cd587SMarc-André Lureau } TPMTISState; 51732cd587SMarc-André Lureau 52732cd587SMarc-André Lureau /* locality data -- all fields are persisted */ 53732cd587SMarc-André Lureau typedef struct TPMLocality { 54732cd587SMarc-André Lureau TPMTISState state; 55732cd587SMarc-André Lureau uint8_t access; 56732cd587SMarc-André Lureau uint32_t sts; 57732cd587SMarc-André Lureau uint32_t iface_id; 58732cd587SMarc-André Lureau uint32_t inte; 59732cd587SMarc-André Lureau uint32_t ints; 60732cd587SMarc-André Lureau } TPMLocality; 61732cd587SMarc-André Lureau 6236e86589SMarc-André Lureau typedef struct TPMState { 633d4960c7SMarc-André Lureau ISADevice busdev; 643d4960c7SMarc-André Lureau MemoryRegion mmio; 653d4960c7SMarc-André Lureau 66c5496b97SStefan Berger unsigned char buffer[TPM_TIS_BUFFER_MAX]; 67f999d81bSStefan Berger uint16_t rw_offset; 68732cd587SMarc-André Lureau 69732cd587SMarc-André Lureau uint8_t active_locty; 70732cd587SMarc-André Lureau uint8_t aborting_locty; 71732cd587SMarc-André Lureau uint8_t next_locty; 72732cd587SMarc-André Lureau 73732cd587SMarc-André Lureau TPMLocality loc[TPM_TIS_NUM_LOCALITIES]; 74732cd587SMarc-André Lureau 75732cd587SMarc-André Lureau qemu_irq irq; 76732cd587SMarc-André Lureau uint32_t irq_num; 77732cd587SMarc-André Lureau 78732cd587SMarc-André Lureau TPMBackendCmd cmd; 79732cd587SMarc-André Lureau 80732cd587SMarc-André Lureau TPMBackend *be_driver; 81732cd587SMarc-André Lureau TPMVersion be_tpm_version; 82b21e6aafSStefan Berger 83b21e6aafSStefan Berger size_t be_buffer_size; 84*b6148757SMarc-André Lureau 85*b6148757SMarc-André Lureau bool ppi_enabled; 8636e86589SMarc-André Lureau } TPMState; 87732cd587SMarc-André Lureau 88732cd587SMarc-André Lureau #define TPM(obj) OBJECT_CHECK(TPMState, (obj), TYPE_TPM_TIS) 89edff8678SStefan Berger 904d1ba9c4SStefan Berger #define DEBUG_TIS 0 91edff8678SStefan Berger 928db7c415SStefan Berger /* local prototypes */ 938db7c415SStefan Berger 948db7c415SStefan Berger static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, 958db7c415SStefan Berger unsigned size); 968db7c415SStefan Berger 97edff8678SStefan Berger /* utility functions */ 98edff8678SStefan Berger 99edff8678SStefan Berger static uint8_t tpm_tis_locality_from_addr(hwaddr addr) 100edff8678SStefan Berger { 101edff8678SStefan Berger return (uint8_t)((addr >> TPM_TIS_LOCALITY_SHIFT) & 0x7); 102edff8678SStefan Berger } 103edff8678SStefan Berger 104e6b703f6SStefan Berger static void tpm_tis_show_buffer(const unsigned char *buffer, 105e6b703f6SStefan Berger size_t buffer_size, const char *string) 106edff8678SStefan Berger { 107edff8678SStefan Berger uint32_t len, i; 108edff8678SStefan Berger 109e6b703f6SStefan Berger len = MIN(tpm_cmd_get_size(buffer), buffer_size); 110fcbed221SStefan Berger printf("tpm_tis: %s length = %d\n", string, len); 111edff8678SStefan Berger for (i = 0; i < len; i++) { 112edff8678SStefan Berger if (i && !(i % 16)) { 113fcbed221SStefan Berger printf("\n"); 114edff8678SStefan Berger } 115fcbed221SStefan Berger printf("%.2X ", buffer[i]); 116edff8678SStefan Berger } 117fcbed221SStefan Berger printf("\n"); 118edff8678SStefan Berger } 119edff8678SStefan Berger 120edff8678SStefan Berger /* 121fd859081SStefan Berger * Set the given flags in the STS register by clearing the register but 122116694c3SStefan Berger * preserving the SELFTEST_DONE and TPM_FAMILY_MASK flags and then setting 123116694c3SStefan Berger * the new flags. 124fd859081SStefan Berger * 125fd859081SStefan Berger * The SELFTEST_DONE flag is acquired from the backend that determines it by 126fd859081SStefan Berger * peeking into TPM commands. 127fd859081SStefan Berger * 128fd859081SStefan Berger * A VM suspend/resume will preserve the flag by storing it into the VM 129fd859081SStefan Berger * device state, but the backend will not remember it when QEMU is started 130fd859081SStefan Berger * again. Therefore, we cache the flag here. Once set, it will not be unset 131fd859081SStefan Berger * except by a reset. 132fd859081SStefan Berger */ 133fd859081SStefan Berger static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags) 134fd859081SStefan Berger { 135116694c3SStefan Berger l->sts &= TPM_TIS_STS_SELFTEST_DONE | TPM_TIS_STS_TPM_FAMILY_MASK; 136fd859081SStefan Berger l->sts |= flags; 137fd859081SStefan Berger } 138fd859081SStefan Berger 139fd859081SStefan Berger /* 140edff8678SStefan Berger * Send a request to the TPM. 141edff8678SStefan Berger */ 142edff8678SStefan Berger static void tpm_tis_tpm_send(TPMState *s, uint8_t locty) 143edff8678SStefan Berger { 144fcbed221SStefan Berger if (DEBUG_TIS) { 145c5496b97SStefan Berger tpm_tis_show_buffer(s->buffer, s->be_buffer_size, 146e6b703f6SStefan Berger "tpm_tis: To TPM"); 147fcbed221SStefan Berger } 148edff8678SStefan Berger 149edff8678SStefan Berger /* 150f999d81bSStefan Berger * rw_offset serves as length indicator for length of data; 151edff8678SStefan Berger * it's reset when the response comes back 152edff8678SStefan Berger */ 1533d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_EXECUTION; 154edff8678SStefan Berger 1550e43b7e6SMarc-André Lureau s->cmd = (TPMBackendCmd) { 1560e43b7e6SMarc-André Lureau .locty = locty, 157c5496b97SStefan Berger .in = s->buffer, 158f999d81bSStefan Berger .in_len = s->rw_offset, 159c5496b97SStefan Berger .out = s->buffer, 160e6b703f6SStefan Berger .out_len = s->be_buffer_size, 1610e43b7e6SMarc-André Lureau }; 1620e43b7e6SMarc-André Lureau 1630e43b7e6SMarc-André Lureau tpm_backend_deliver_request(s->be_driver, &s->cmd); 164edff8678SStefan Berger } 165edff8678SStefan Berger 166edff8678SStefan Berger /* raise an interrupt if allowed */ 167edff8678SStefan Berger static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask) 168edff8678SStefan Berger { 169edff8678SStefan Berger if (!TPM_TIS_IS_VALID_LOCTY(locty)) { 170edff8678SStefan Berger return; 171edff8678SStefan Berger } 172edff8678SStefan Berger 1733d4960c7SMarc-André Lureau if ((s->loc[locty].inte & TPM_TIS_INT_ENABLED) && 1743d4960c7SMarc-André Lureau (s->loc[locty].inte & irqmask)) { 175fcbed221SStefan Berger trace_tpm_tis_raise_irq(irqmask); 1763d4960c7SMarc-André Lureau qemu_irq_raise(s->irq); 1773d4960c7SMarc-André Lureau s->loc[locty].ints |= irqmask; 178edff8678SStefan Berger } 179edff8678SStefan Berger } 180edff8678SStefan Berger 181edff8678SStefan Berger static uint32_t tpm_tis_check_request_use_except(TPMState *s, uint8_t locty) 182edff8678SStefan Berger { 183edff8678SStefan Berger uint8_t l; 184edff8678SStefan Berger 185edff8678SStefan Berger for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 186edff8678SStefan Berger if (l == locty) { 187edff8678SStefan Berger continue; 188edff8678SStefan Berger } 1893d4960c7SMarc-André Lureau if ((s->loc[l].access & TPM_TIS_ACCESS_REQUEST_USE)) { 190edff8678SStefan Berger return 1; 191edff8678SStefan Berger } 192edff8678SStefan Berger } 193edff8678SStefan Berger 194edff8678SStefan Berger return 0; 195edff8678SStefan Berger } 196edff8678SStefan Berger 197edff8678SStefan Berger static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty) 198edff8678SStefan Berger { 1993d4960c7SMarc-André Lureau bool change = (s->active_locty != new_active_locty); 200edff8678SStefan Berger bool is_seize; 201edff8678SStefan Berger uint8_t mask; 202edff8678SStefan Berger 2033d4960c7SMarc-André Lureau if (change && TPM_TIS_IS_VALID_LOCTY(s->active_locty)) { 204edff8678SStefan Berger is_seize = TPM_TIS_IS_VALID_LOCTY(new_active_locty) && 2053d4960c7SMarc-André Lureau s->loc[new_active_locty].access & TPM_TIS_ACCESS_SEIZE; 206edff8678SStefan Berger 207edff8678SStefan Berger if (is_seize) { 208edff8678SStefan Berger mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY); 209edff8678SStefan Berger } else { 210edff8678SStefan Berger mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY| 211edff8678SStefan Berger TPM_TIS_ACCESS_REQUEST_USE); 212edff8678SStefan Berger } 213edff8678SStefan Berger /* reset flags on the old active locality */ 2143d4960c7SMarc-André Lureau s->loc[s->active_locty].access &= mask; 215edff8678SStefan Berger 216edff8678SStefan Berger if (is_seize) { 2173d4960c7SMarc-André Lureau s->loc[s->active_locty].access |= TPM_TIS_ACCESS_BEEN_SEIZED; 218edff8678SStefan Berger } 219edff8678SStefan Berger } 220edff8678SStefan Berger 2213d4960c7SMarc-André Lureau s->active_locty = new_active_locty; 222edff8678SStefan Berger 223fcbed221SStefan Berger trace_tpm_tis_new_active_locality(s->active_locty); 224edff8678SStefan Berger 225edff8678SStefan Berger if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) { 226edff8678SStefan Berger /* set flags on the new active locality */ 2273d4960c7SMarc-André Lureau s->loc[new_active_locty].access |= TPM_TIS_ACCESS_ACTIVE_LOCALITY; 2283d4960c7SMarc-André Lureau s->loc[new_active_locty].access &= ~(TPM_TIS_ACCESS_REQUEST_USE | 229edff8678SStefan Berger TPM_TIS_ACCESS_SEIZE); 230edff8678SStefan Berger } 231edff8678SStefan Berger 232edff8678SStefan Berger if (change) { 2333d4960c7SMarc-André Lureau tpm_tis_raise_irq(s, s->active_locty, TPM_TIS_INT_LOCALITY_CHANGED); 234edff8678SStefan Berger } 235edff8678SStefan Berger } 236edff8678SStefan Berger 237edff8678SStefan Berger /* abort -- this function switches the locality */ 2380f5faee3SStefan Berger static void tpm_tis_abort(TPMState *s) 239edff8678SStefan Berger { 240f999d81bSStefan Berger s->rw_offset = 0; 241edff8678SStefan Berger 242fcbed221SStefan Berger trace_tpm_tis_abort(s->next_locty); 243edff8678SStefan Berger 244edff8678SStefan Berger /* 245edff8678SStefan Berger * Need to react differently depending on who's aborting now and 246edff8678SStefan Berger * which locality will become active afterwards. 247edff8678SStefan Berger */ 2483d4960c7SMarc-André Lureau if (s->aborting_locty == s->next_locty) { 2493d4960c7SMarc-André Lureau s->loc[s->aborting_locty].state = TPM_TIS_STATE_READY; 2503d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[s->aborting_locty], 251fd859081SStefan Berger TPM_TIS_STS_COMMAND_READY); 2523d4960c7SMarc-André Lureau tpm_tis_raise_irq(s, s->aborting_locty, TPM_TIS_INT_COMMAND_READY); 253edff8678SStefan Berger } 254edff8678SStefan Berger 255edff8678SStefan Berger /* locality after abort is another one than the current one */ 2563d4960c7SMarc-André Lureau tpm_tis_new_active_locality(s, s->next_locty); 257edff8678SStefan Berger 2583d4960c7SMarc-André Lureau s->next_locty = TPM_TIS_NO_LOCALITY; 259edff8678SStefan Berger /* nobody's aborting a command anymore */ 2603d4960c7SMarc-André Lureau s->aborting_locty = TPM_TIS_NO_LOCALITY; 261edff8678SStefan Berger } 262edff8678SStefan Berger 263edff8678SStefan Berger /* prepare aborting current command */ 264edff8678SStefan Berger static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty) 265edff8678SStefan Berger { 266edff8678SStefan Berger uint8_t busy_locty; 267edff8678SStefan Berger 268e92b63eaSStefan Berger assert(TPM_TIS_IS_VALID_LOCTY(newlocty)); 269e92b63eaSStefan Berger 270e92b63eaSStefan Berger s->aborting_locty = locty; /* may also be TPM_TIS_NO_LOCALITY */ 2713d4960c7SMarc-André Lureau s->next_locty = newlocty; /* locality after successful abort */ 272edff8678SStefan Berger 273edff8678SStefan Berger /* 274edff8678SStefan Berger * only abort a command using an interrupt if currently executing 275edff8678SStefan Berger * a command AND if there's a valid connection to the vTPM. 276edff8678SStefan Berger */ 277edff8678SStefan Berger for (busy_locty = 0; busy_locty < TPM_TIS_NUM_LOCALITIES; busy_locty++) { 2783d4960c7SMarc-André Lureau if (s->loc[busy_locty].state == TPM_TIS_STATE_EXECUTION) { 279edff8678SStefan Berger /* 280edff8678SStefan Berger * request the backend to cancel. Some backends may not 281edff8678SStefan Berger * support it 282edff8678SStefan Berger */ 2838f0605ccSStefan Berger tpm_backend_cancel_cmd(s->be_driver); 284edff8678SStefan Berger return; 285edff8678SStefan Berger } 286edff8678SStefan Berger } 287edff8678SStefan Berger 2880f5faee3SStefan Berger tpm_tis_abort(s); 289edff8678SStefan Berger } 290edff8678SStefan Berger 29168999059SMarc-André Lureau /* 29268999059SMarc-André Lureau * Callback from the TPM to indicate that the response was received. 29368999059SMarc-André Lureau */ 2946a8a2354SMarc-André Lureau static void tpm_tis_request_completed(TPMIf *ti, int ret) 295edff8678SStefan Berger { 29668999059SMarc-André Lureau TPMState *s = TPM(ti); 2970e43b7e6SMarc-André Lureau uint8_t locty = s->cmd.locty; 29868999059SMarc-André Lureau uint8_t l; 29968999059SMarc-André Lureau 300a639f961SStefan Berger assert(TPM_TIS_IS_VALID_LOCTY(locty)); 301a639f961SStefan Berger 30268999059SMarc-André Lureau if (s->cmd.selftest_done) { 30368999059SMarc-André Lureau for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 3046a50bb98SPrasad J Pandit s->loc[l].sts |= TPM_TIS_STS_SELFTEST_DONE; 30568999059SMarc-André Lureau } 30668999059SMarc-André Lureau } 307edff8678SStefan Berger 3086a8a2354SMarc-André Lureau /* FIXME: report error if ret != 0 */ 3093d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 310fd859081SStefan Berger TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE); 3113d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_COMPLETION; 312f999d81bSStefan Berger s->rw_offset = 0; 313edff8678SStefan Berger 314fcbed221SStefan Berger if (DEBUG_TIS) { 315c5496b97SStefan Berger tpm_tis_show_buffer(s->buffer, s->be_buffer_size, 316e6b703f6SStefan Berger "tpm_tis: From TPM"); 317fcbed221SStefan Berger } 318298d8b81SStefan Berger 3193d4960c7SMarc-André Lureau if (TPM_TIS_IS_VALID_LOCTY(s->next_locty)) { 3200f5faee3SStefan Berger tpm_tis_abort(s); 321edff8678SStefan Berger } 322edff8678SStefan Berger 323edff8678SStefan Berger tpm_tis_raise_irq(s, locty, 324edff8678SStefan Berger TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID); 325edff8678SStefan Berger } 326edff8678SStefan Berger 327edff8678SStefan Berger /* 328edff8678SStefan Berger * Read a byte of response data 329edff8678SStefan Berger */ 330edff8678SStefan Berger static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty) 331edff8678SStefan Berger { 332edff8678SStefan Berger uint32_t ret = TPM_TIS_NO_DATA_BYTE; 333edff8678SStefan Berger uint16_t len; 334edff8678SStefan Berger 3353d4960c7SMarc-André Lureau if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) { 336c5496b97SStefan Berger len = MIN(tpm_cmd_get_size(&s->buffer), 337e6b703f6SStefan Berger s->be_buffer_size); 338edff8678SStefan Berger 339f999d81bSStefan Berger ret = s->buffer[s->rw_offset++]; 340f999d81bSStefan Berger if (s->rw_offset >= len) { 341edff8678SStefan Berger /* got last byte */ 3423d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 343edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); 344edff8678SStefan Berger } 345fcbed221SStefan Berger trace_tpm_tis_data_read(ret, s->rw_offset - 1); 346edff8678SStefan Berger } 347edff8678SStefan Berger 348edff8678SStefan Berger return ret; 349edff8678SStefan Berger } 350edff8678SStefan Berger 3518db7c415SStefan Berger #ifdef DEBUG_TIS 3528db7c415SStefan Berger static void tpm_tis_dump_state(void *opaque, hwaddr addr) 3538db7c415SStefan Berger { 3548db7c415SStefan Berger static const unsigned regs[] = { 3558db7c415SStefan Berger TPM_TIS_REG_ACCESS, 3568db7c415SStefan Berger TPM_TIS_REG_INT_ENABLE, 3578db7c415SStefan Berger TPM_TIS_REG_INT_VECTOR, 3588db7c415SStefan Berger TPM_TIS_REG_INT_STATUS, 3598db7c415SStefan Berger TPM_TIS_REG_INTF_CAPABILITY, 3608db7c415SStefan Berger TPM_TIS_REG_STS, 3618db7c415SStefan Berger TPM_TIS_REG_DID_VID, 3628db7c415SStefan Berger TPM_TIS_REG_RID, 3638db7c415SStefan Berger 0xfff}; 3648db7c415SStefan Berger int idx; 3658db7c415SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 3668db7c415SStefan Berger hwaddr base = addr & ~0xfff; 3678db7c415SStefan Berger TPMState *s = opaque; 3688db7c415SStefan Berger 369fcbed221SStefan Berger printf("tpm_tis: active locality : %d\n" 3708db7c415SStefan Berger "tpm_tis: state of locality %d : %d\n" 3718db7c415SStefan Berger "tpm_tis: register dump:\n", 3723d4960c7SMarc-André Lureau s->active_locty, 3733d4960c7SMarc-André Lureau locty, s->loc[locty].state); 3748db7c415SStefan Berger 3758db7c415SStefan Berger for (idx = 0; regs[idx] != 0xfff; idx++) { 376fcbed221SStefan Berger printf("tpm_tis: 0x%04x : 0x%08x\n", regs[idx], 377070c7607SStefan Berger (int)tpm_tis_mmio_read(opaque, base + regs[idx], 4)); 3788db7c415SStefan Berger } 3798db7c415SStefan Berger 380fcbed221SStefan Berger printf("tpm_tis: r/w offset : %d\n" 3818db7c415SStefan Berger "tpm_tis: result buffer : ", 382f999d81bSStefan Berger s->rw_offset); 3838db7c415SStefan Berger for (idx = 0; 384c5496b97SStefan Berger idx < MIN(tpm_cmd_get_size(&s->buffer), s->be_buffer_size); 3858db7c415SStefan Berger idx++) { 386fcbed221SStefan Berger printf("%c%02x%s", 387f999d81bSStefan Berger s->rw_offset == idx ? '>' : ' ', 388c5496b97SStefan Berger s->buffer[idx], 3898db7c415SStefan Berger ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : ""); 3908db7c415SStefan Berger } 391fcbed221SStefan Berger printf("\n"); 3928db7c415SStefan Berger } 3938db7c415SStefan Berger #endif 3948db7c415SStefan Berger 395edff8678SStefan Berger /* 396edff8678SStefan Berger * Read a register of the TIS interface 397edff8678SStefan Berger * See specs pages 33-63 for description of the registers 398edff8678SStefan Berger */ 399edff8678SStefan Berger static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, 400edff8678SStefan Berger unsigned size) 401edff8678SStefan Berger { 402edff8678SStefan Berger TPMState *s = opaque; 403edff8678SStefan Berger uint16_t offset = addr & 0xffc; 404edff8678SStefan Berger uint8_t shift = (addr & 0x3) * 8; 405edff8678SStefan Berger uint32_t val = 0xffffffff; 406edff8678SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 407edff8678SStefan Berger uint32_t avail; 408feeb755fSStefan Berger uint8_t v; 409edff8678SStefan Berger 4108f0605ccSStefan Berger if (tpm_backend_had_startup_error(s->be_driver)) { 4116cd65969SStefan Berger return 0; 412edff8678SStefan Berger } 413edff8678SStefan Berger 414edff8678SStefan Berger switch (offset) { 415edff8678SStefan Berger case TPM_TIS_REG_ACCESS: 416edff8678SStefan Berger /* never show the SEIZE flag even though we use it internally */ 4173d4960c7SMarc-André Lureau val = s->loc[locty].access & ~TPM_TIS_ACCESS_SEIZE; 418edff8678SStefan Berger /* the pending flag is always calculated */ 419edff8678SStefan Berger if (tpm_tis_check_request_use_except(s, locty)) { 420edff8678SStefan Berger val |= TPM_TIS_ACCESS_PENDING_REQUEST; 421edff8678SStefan Berger } 4228f0605ccSStefan Berger val |= !tpm_backend_get_tpm_established_flag(s->be_driver); 423edff8678SStefan Berger break; 424edff8678SStefan Berger case TPM_TIS_REG_INT_ENABLE: 4253d4960c7SMarc-André Lureau val = s->loc[locty].inte; 426edff8678SStefan Berger break; 427edff8678SStefan Berger case TPM_TIS_REG_INT_VECTOR: 4283d4960c7SMarc-André Lureau val = s->irq_num; 429edff8678SStefan Berger break; 430edff8678SStefan Berger case TPM_TIS_REG_INT_STATUS: 4313d4960c7SMarc-André Lureau val = s->loc[locty].ints; 432edff8678SStefan Berger break; 433edff8678SStefan Berger case TPM_TIS_REG_INTF_CAPABILITY: 434116694c3SStefan Berger switch (s->be_tpm_version) { 435116694c3SStefan Berger case TPM_VERSION_UNSPEC: 436116694c3SStefan Berger val = 0; 437116694c3SStefan Berger break; 438116694c3SStefan Berger case TPM_VERSION_1_2: 439116694c3SStefan Berger val = TPM_TIS_CAPABILITIES_SUPPORTED1_3; 440116694c3SStefan Berger break; 441116694c3SStefan Berger case TPM_VERSION_2_0: 442116694c3SStefan Berger val = TPM_TIS_CAPABILITIES_SUPPORTED2_0; 443116694c3SStefan Berger break; 444116694c3SStefan Berger } 445edff8678SStefan Berger break; 446edff8678SStefan Berger case TPM_TIS_REG_STS: 4473d4960c7SMarc-André Lureau if (s->active_locty == locty) { 4483d4960c7SMarc-André Lureau if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) { 449edff8678SStefan Berger val = TPM_TIS_BURST_COUNT( 450c5496b97SStefan Berger MIN(tpm_cmd_get_size(&s->buffer), 451e6b703f6SStefan Berger s->be_buffer_size) 452f999d81bSStefan Berger - s->rw_offset) | s->loc[locty].sts; 453edff8678SStefan Berger } else { 454f999d81bSStefan Berger avail = s->be_buffer_size - s->rw_offset; 455edff8678SStefan Berger /* 456edff8678SStefan Berger * byte-sized reads should not return 0x00 for 0x100 457edff8678SStefan Berger * available bytes. 458edff8678SStefan Berger */ 459edff8678SStefan Berger if (size == 1 && avail > 0xff) { 460edff8678SStefan Berger avail = 0xff; 461edff8678SStefan Berger } 4623d4960c7SMarc-André Lureau val = TPM_TIS_BURST_COUNT(avail) | s->loc[locty].sts; 463edff8678SStefan Berger } 464edff8678SStefan Berger } 465edff8678SStefan Berger break; 466edff8678SStefan Berger case TPM_TIS_REG_DATA_FIFO: 4672eae8c75SStefan Berger case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END: 4683d4960c7SMarc-André Lureau if (s->active_locty == locty) { 469feeb755fSStefan Berger if (size > 4 - (addr & 0x3)) { 470feeb755fSStefan Berger /* prevent access beyond FIFO */ 471feeb755fSStefan Berger size = 4 - (addr & 0x3); 472feeb755fSStefan Berger } 473feeb755fSStefan Berger val = 0; 474feeb755fSStefan Berger shift = 0; 475feeb755fSStefan Berger while (size > 0) { 4763d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 477edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 478feeb755fSStefan Berger v = tpm_tis_data_read(s, locty); 479edff8678SStefan Berger break; 480edff8678SStefan Berger default: 481feeb755fSStefan Berger v = TPM_TIS_NO_DATA_BYTE; 482edff8678SStefan Berger break; 483edff8678SStefan Berger } 484feeb755fSStefan Berger val |= (v << shift); 485feeb755fSStefan Berger shift += 8; 486feeb755fSStefan Berger size--; 487feeb755fSStefan Berger } 488feeb755fSStefan Berger shift = 0; /* no more adjustments */ 489edff8678SStefan Berger } 490edff8678SStefan Berger break; 491116694c3SStefan Berger case TPM_TIS_REG_INTERFACE_ID: 4923d4960c7SMarc-André Lureau val = s->loc[locty].iface_id; 493116694c3SStefan Berger break; 494edff8678SStefan Berger case TPM_TIS_REG_DID_VID: 495edff8678SStefan Berger val = (TPM_TIS_TPM_DID << 16) | TPM_TIS_TPM_VID; 496edff8678SStefan Berger break; 497edff8678SStefan Berger case TPM_TIS_REG_RID: 498edff8678SStefan Berger val = TPM_TIS_TPM_RID; 499edff8678SStefan Berger break; 5008db7c415SStefan Berger #ifdef DEBUG_TIS 5018db7c415SStefan Berger case TPM_TIS_REG_DEBUG: 5028db7c415SStefan Berger tpm_tis_dump_state(opaque, addr); 5038db7c415SStefan Berger break; 5048db7c415SStefan Berger #endif 505edff8678SStefan Berger } 506edff8678SStefan Berger 507edff8678SStefan Berger if (shift) { 508edff8678SStefan Berger val >>= shift; 509edff8678SStefan Berger } 510edff8678SStefan Berger 511fcbed221SStefan Berger trace_tpm_tis_mmio_read(size, addr, val); 512edff8678SStefan Berger 513edff8678SStefan Berger return val; 514edff8678SStefan Berger } 515edff8678SStefan Berger 516edff8678SStefan Berger /* 517edff8678SStefan Berger * Write a value to a register of the TIS interface 518edff8678SStefan Berger * See specs pages 33-63 for description of the registers 519edff8678SStefan Berger */ 520ff2bc0c1SMarc-André Lureau static void tpm_tis_mmio_write(void *opaque, hwaddr addr, 521ff2bc0c1SMarc-André Lureau uint64_t val, unsigned size) 522edff8678SStefan Berger { 523edff8678SStefan Berger TPMState *s = opaque; 524feeb755fSStefan Berger uint16_t off = addr & 0xffc; 525feeb755fSStefan Berger uint8_t shift = (addr & 0x3) * 8; 526edff8678SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 527edff8678SStefan Berger uint8_t active_locty, l; 528edff8678SStefan Berger int c, set_new_locty = 1; 529edff8678SStefan Berger uint16_t len; 530feeb755fSStefan Berger uint32_t mask = (size == 1) ? 0xff : ((size == 2) ? 0xffff : ~0); 531edff8678SStefan Berger 532fcbed221SStefan Berger trace_tpm_tis_mmio_write(size, addr, val); 533edff8678SStefan Berger 534ff2bc0c1SMarc-André Lureau if (locty == 4) { 535fcbed221SStefan Berger trace_tpm_tis_mmio_write_locty4(); 536edff8678SStefan Berger return; 537edff8678SStefan Berger } 538edff8678SStefan Berger 5398f0605ccSStefan Berger if (tpm_backend_had_startup_error(s->be_driver)) { 540edff8678SStefan Berger return; 541edff8678SStefan Berger } 542edff8678SStefan Berger 543feeb755fSStefan Berger val &= mask; 544feeb755fSStefan Berger 545feeb755fSStefan Berger if (shift) { 546feeb755fSStefan Berger val <<= shift; 547feeb755fSStefan Berger mask <<= shift; 548feeb755fSStefan Berger } 549feeb755fSStefan Berger 550feeb755fSStefan Berger mask ^= 0xffffffff; 551feeb755fSStefan Berger 552edff8678SStefan Berger switch (off) { 553edff8678SStefan Berger case TPM_TIS_REG_ACCESS: 554edff8678SStefan Berger 555edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_SEIZE)) { 556edff8678SStefan Berger val &= ~(TPM_TIS_ACCESS_REQUEST_USE | 557edff8678SStefan Berger TPM_TIS_ACCESS_ACTIVE_LOCALITY); 558edff8678SStefan Berger } 559edff8678SStefan Berger 5603d4960c7SMarc-André Lureau active_locty = s->active_locty; 561edff8678SStefan Berger 562edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) { 563edff8678SStefan Berger /* give up locality if currently owned */ 5643d4960c7SMarc-André Lureau if (s->active_locty == locty) { 565fcbed221SStefan Berger trace_tpm_tis_mmio_write_release_locty(locty); 566edff8678SStefan Berger 567edff8678SStefan Berger uint8_t newlocty = TPM_TIS_NO_LOCALITY; 568edff8678SStefan Berger /* anybody wants the locality ? */ 569edff8678SStefan Berger for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) { 5703d4960c7SMarc-André Lureau if ((s->loc[c].access & TPM_TIS_ACCESS_REQUEST_USE)) { 571fcbed221SStefan Berger trace_tpm_tis_mmio_write_locty_req_use(c); 572edff8678SStefan Berger newlocty = c; 573edff8678SStefan Berger break; 574edff8678SStefan Berger } 575edff8678SStefan Berger } 576fcbed221SStefan Berger trace_tpm_tis_mmio_write_next_locty(newlocty); 577edff8678SStefan Berger 578edff8678SStefan Berger if (TPM_TIS_IS_VALID_LOCTY(newlocty)) { 579edff8678SStefan Berger set_new_locty = 0; 580edff8678SStefan Berger tpm_tis_prep_abort(s, locty, newlocty); 581edff8678SStefan Berger } else { 582edff8678SStefan Berger active_locty = TPM_TIS_NO_LOCALITY; 583edff8678SStefan Berger } 584edff8678SStefan Berger } else { 585edff8678SStefan Berger /* not currently the owner; clear a pending request */ 5863d4960c7SMarc-André Lureau s->loc[locty].access &= ~TPM_TIS_ACCESS_REQUEST_USE; 587edff8678SStefan Berger } 588edff8678SStefan Berger } 589edff8678SStefan Berger 590edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_BEEN_SEIZED)) { 5913d4960c7SMarc-André Lureau s->loc[locty].access &= ~TPM_TIS_ACCESS_BEEN_SEIZED; 592edff8678SStefan Berger } 593edff8678SStefan Berger 594edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_SEIZE)) { 595edff8678SStefan Berger /* 596edff8678SStefan Berger * allow seize if a locality is active and the requesting 597edff8678SStefan Berger * locality is higher than the one that's active 598edff8678SStefan Berger * OR 599edff8678SStefan Berger * allow seize for requesting locality if no locality is 600edff8678SStefan Berger * active 601edff8678SStefan Berger */ 6023d4960c7SMarc-André Lureau while ((TPM_TIS_IS_VALID_LOCTY(s->active_locty) && 6033d4960c7SMarc-André Lureau locty > s->active_locty) || 6043d4960c7SMarc-André Lureau !TPM_TIS_IS_VALID_LOCTY(s->active_locty)) { 605edff8678SStefan Berger bool higher_seize = FALSE; 606edff8678SStefan Berger 607edff8678SStefan Berger /* already a pending SEIZE ? */ 6083d4960c7SMarc-André Lureau if ((s->loc[locty].access & TPM_TIS_ACCESS_SEIZE)) { 609edff8678SStefan Berger break; 610edff8678SStefan Berger } 611edff8678SStefan Berger 612edff8678SStefan Berger /* check for ongoing seize by a higher locality */ 613edff8678SStefan Berger for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES; l++) { 6143d4960c7SMarc-André Lureau if ((s->loc[l].access & TPM_TIS_ACCESS_SEIZE)) { 615edff8678SStefan Berger higher_seize = TRUE; 616edff8678SStefan Berger break; 617edff8678SStefan Berger } 618edff8678SStefan Berger } 619edff8678SStefan Berger 620edff8678SStefan Berger if (higher_seize) { 621edff8678SStefan Berger break; 622edff8678SStefan Berger } 623edff8678SStefan Berger 624edff8678SStefan Berger /* cancel any seize by a lower locality */ 625edff8678SStefan Berger for (l = 0; l < locty - 1; l++) { 6263d4960c7SMarc-André Lureau s->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE; 627edff8678SStefan Berger } 628edff8678SStefan Berger 6293d4960c7SMarc-André Lureau s->loc[locty].access |= TPM_TIS_ACCESS_SEIZE; 630fcbed221SStefan Berger 631fcbed221SStefan Berger trace_tpm_tis_mmio_write_locty_seized(locty, s->active_locty); 632fcbed221SStefan Berger trace_tpm_tis_mmio_write_init_abort(); 633fcbed221SStefan Berger 634edff8678SStefan Berger set_new_locty = 0; 6353d4960c7SMarc-André Lureau tpm_tis_prep_abort(s, s->active_locty, locty); 636edff8678SStefan Berger break; 637edff8678SStefan Berger } 638edff8678SStefan Berger } 639edff8678SStefan Berger 640edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_REQUEST_USE)) { 6413d4960c7SMarc-André Lureau if (s->active_locty != locty) { 6423d4960c7SMarc-André Lureau if (TPM_TIS_IS_VALID_LOCTY(s->active_locty)) { 6433d4960c7SMarc-André Lureau s->loc[locty].access |= TPM_TIS_ACCESS_REQUEST_USE; 644edff8678SStefan Berger } else { 645edff8678SStefan Berger /* no locality active -> make this one active now */ 646edff8678SStefan Berger active_locty = locty; 647edff8678SStefan Berger } 648edff8678SStefan Berger } 649edff8678SStefan Berger } 650edff8678SStefan Berger 651edff8678SStefan Berger if (set_new_locty) { 652edff8678SStefan Berger tpm_tis_new_active_locality(s, active_locty); 653edff8678SStefan Berger } 654edff8678SStefan Berger 655edff8678SStefan Berger break; 656edff8678SStefan Berger case TPM_TIS_REG_INT_ENABLE: 6573d4960c7SMarc-André Lureau if (s->active_locty != locty) { 658edff8678SStefan Berger break; 659edff8678SStefan Berger } 660edff8678SStefan Berger 6613d4960c7SMarc-André Lureau s->loc[locty].inte &= mask; 6623d4960c7SMarc-André Lureau s->loc[locty].inte |= (val & (TPM_TIS_INT_ENABLED | 663edff8678SStefan Berger TPM_TIS_INT_POLARITY_MASK | 664edff8678SStefan Berger TPM_TIS_INTERRUPTS_SUPPORTED)); 665edff8678SStefan Berger break; 666edff8678SStefan Berger case TPM_TIS_REG_INT_VECTOR: 667edff8678SStefan Berger /* hard wired -- ignore */ 668edff8678SStefan Berger break; 669edff8678SStefan Berger case TPM_TIS_REG_INT_STATUS: 6703d4960c7SMarc-André Lureau if (s->active_locty != locty) { 671edff8678SStefan Berger break; 672edff8678SStefan Berger } 673edff8678SStefan Berger 674edff8678SStefan Berger /* clearing of interrupt flags */ 675edff8678SStefan Berger if (((val & TPM_TIS_INTERRUPTS_SUPPORTED)) && 6763d4960c7SMarc-André Lureau (s->loc[locty].ints & TPM_TIS_INTERRUPTS_SUPPORTED)) { 6773d4960c7SMarc-André Lureau s->loc[locty].ints &= ~val; 6783d4960c7SMarc-André Lureau if (s->loc[locty].ints == 0) { 6793d4960c7SMarc-André Lureau qemu_irq_lower(s->irq); 680fcbed221SStefan Berger trace_tpm_tis_mmio_write_lowering_irq(); 681edff8678SStefan Berger } 682edff8678SStefan Berger } 6833d4960c7SMarc-André Lureau s->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED); 684edff8678SStefan Berger break; 685edff8678SStefan Berger case TPM_TIS_REG_STS: 6863d4960c7SMarc-André Lureau if (s->active_locty != locty) { 687edff8678SStefan Berger break; 688edff8678SStefan Berger } 689edff8678SStefan Berger 690116694c3SStefan Berger if (s->be_tpm_version == TPM_VERSION_2_0) { 691116694c3SStefan Berger /* some flags that are only supported for TPM 2 */ 692116694c3SStefan Berger if (val & TPM_TIS_STS_COMMAND_CANCEL) { 6933d4960c7SMarc-André Lureau if (s->loc[locty].state == TPM_TIS_STATE_EXECUTION) { 694116694c3SStefan Berger /* 695116694c3SStefan Berger * request the backend to cancel. Some backends may not 696116694c3SStefan Berger * support it 697116694c3SStefan Berger */ 698116694c3SStefan Berger tpm_backend_cancel_cmd(s->be_driver); 699116694c3SStefan Berger } 700116694c3SStefan Berger } 701116694c3SStefan Berger 702116694c3SStefan Berger if (val & TPM_TIS_STS_RESET_ESTABLISHMENT_BIT) { 703116694c3SStefan Berger if (locty == 3 || locty == 4) { 704116694c3SStefan Berger tpm_backend_reset_tpm_established_flag(s->be_driver, locty); 705116694c3SStefan Berger } 706116694c3SStefan Berger } 707116694c3SStefan Berger } 708116694c3SStefan Berger 709edff8678SStefan Berger val &= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO | 710edff8678SStefan Berger TPM_TIS_STS_RESPONSE_RETRY); 711edff8678SStefan Berger 712edff8678SStefan Berger if (val == TPM_TIS_STS_COMMAND_READY) { 7133d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 714edff8678SStefan Berger 715edff8678SStefan Berger case TPM_TIS_STATE_READY: 716f999d81bSStefan Berger s->rw_offset = 0; 717edff8678SStefan Berger break; 718edff8678SStefan Berger 719edff8678SStefan Berger case TPM_TIS_STATE_IDLE: 7203d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_COMMAND_READY); 7213d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_READY; 722edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); 723edff8678SStefan Berger break; 724edff8678SStefan Berger 725edff8678SStefan Berger case TPM_TIS_STATE_EXECUTION: 726edff8678SStefan Berger case TPM_TIS_STATE_RECEPTION: 727edff8678SStefan Berger /* abort currently running command */ 728fcbed221SStefan Berger trace_tpm_tis_mmio_write_init_abort(); 729edff8678SStefan Berger tpm_tis_prep_abort(s, locty, locty); 730edff8678SStefan Berger break; 731edff8678SStefan Berger 732edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 733f999d81bSStefan Berger s->rw_offset = 0; 734edff8678SStefan Berger /* shortcut to ready state with C/R set */ 7353d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_READY; 7363d4960c7SMarc-André Lureau if (!(s->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) { 7373d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 738fd859081SStefan Berger TPM_TIS_STS_COMMAND_READY); 739edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); 740edff8678SStefan Berger } 7413d4960c7SMarc-André Lureau s->loc[locty].sts &= ~(TPM_TIS_STS_DATA_AVAILABLE); 742edff8678SStefan Berger break; 743edff8678SStefan Berger 744edff8678SStefan Berger } 745edff8678SStefan Berger } else if (val == TPM_TIS_STS_TPM_GO) { 7463d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 747edff8678SStefan Berger case TPM_TIS_STATE_RECEPTION: 7483d4960c7SMarc-André Lureau if ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) == 0) { 749edff8678SStefan Berger tpm_tis_tpm_send(s, locty); 750edff8678SStefan Berger } 751edff8678SStefan Berger break; 752edff8678SStefan Berger default: 753edff8678SStefan Berger /* ignore */ 754edff8678SStefan Berger break; 755edff8678SStefan Berger } 756edff8678SStefan Berger } else if (val == TPM_TIS_STS_RESPONSE_RETRY) { 7573d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 758edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 759f999d81bSStefan Berger s->rw_offset = 0; 7603d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 761fd859081SStefan Berger TPM_TIS_STS_VALID| 762fd859081SStefan Berger TPM_TIS_STS_DATA_AVAILABLE); 763edff8678SStefan Berger break; 764edff8678SStefan Berger default: 765edff8678SStefan Berger /* ignore */ 766edff8678SStefan Berger break; 767edff8678SStefan Berger } 768edff8678SStefan Berger } 769edff8678SStefan Berger break; 770edff8678SStefan Berger case TPM_TIS_REG_DATA_FIFO: 7712eae8c75SStefan Berger case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END: 772edff8678SStefan Berger /* data fifo */ 7733d4960c7SMarc-André Lureau if (s->active_locty != locty) { 774edff8678SStefan Berger break; 775edff8678SStefan Berger } 776edff8678SStefan Berger 7773d4960c7SMarc-André Lureau if (s->loc[locty].state == TPM_TIS_STATE_IDLE || 7783d4960c7SMarc-André Lureau s->loc[locty].state == TPM_TIS_STATE_EXECUTION || 7793d4960c7SMarc-André Lureau s->loc[locty].state == TPM_TIS_STATE_COMPLETION) { 780edff8678SStefan Berger /* drop the byte */ 781edff8678SStefan Berger } else { 782fcbed221SStefan Berger trace_tpm_tis_mmio_write_data2send(val, size); 7833d4960c7SMarc-André Lureau if (s->loc[locty].state == TPM_TIS_STATE_READY) { 7843d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_RECEPTION; 7853d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 786fd859081SStefan Berger TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); 787edff8678SStefan Berger } 788edff8678SStefan Berger 789feeb755fSStefan Berger val >>= shift; 790feeb755fSStefan Berger if (size > 4 - (addr & 0x3)) { 791feeb755fSStefan Berger /* prevent access beyond FIFO */ 792feeb755fSStefan Berger size = 4 - (addr & 0x3); 793feeb755fSStefan Berger } 794feeb755fSStefan Berger 7953d4960c7SMarc-André Lureau while ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) && size > 0) { 796f999d81bSStefan Berger if (s->rw_offset < s->be_buffer_size) { 797f999d81bSStefan Berger s->buffer[s->rw_offset++] = 798e6b703f6SStefan Berger (uint8_t)val; 799feeb755fSStefan Berger val >>= 8; 800feeb755fSStefan Berger size--; 801edff8678SStefan Berger } else { 8023d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 803edff8678SStefan Berger } 804edff8678SStefan Berger } 805edff8678SStefan Berger 806edff8678SStefan Berger /* check for complete packet */ 807f999d81bSStefan Berger if (s->rw_offset > 5 && 8083d4960c7SMarc-André Lureau (s->loc[locty].sts & TPM_TIS_STS_EXPECT)) { 809edff8678SStefan Berger /* we have a packet length - see if we have all of it */ 8103d4960c7SMarc-André Lureau bool need_irq = !(s->loc[locty].sts & TPM_TIS_STS_VALID); 811d8383d61SMarc-André Lureau 812c5496b97SStefan Berger len = tpm_cmd_get_size(&s->buffer); 813f999d81bSStefan Berger if (len > s->rw_offset) { 8143d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 815fd859081SStefan Berger TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); 816edff8678SStefan Berger } else { 817edff8678SStefan Berger /* packet complete */ 8183d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 819edff8678SStefan Berger } 82029b558d8SStefan Berger if (need_irq) { 821edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); 822edff8678SStefan Berger } 823edff8678SStefan Berger } 824edff8678SStefan Berger } 825edff8678SStefan Berger break; 826116694c3SStefan Berger case TPM_TIS_REG_INTERFACE_ID: 827116694c3SStefan Berger if (val & TPM_TIS_IFACE_ID_INT_SEL_LOCK) { 828116694c3SStefan Berger for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 8293d4960c7SMarc-André Lureau s->loc[l].iface_id |= TPM_TIS_IFACE_ID_INT_SEL_LOCK; 830116694c3SStefan Berger } 831116694c3SStefan Berger } 832116694c3SStefan Berger break; 833edff8678SStefan Berger } 834edff8678SStefan Berger } 835edff8678SStefan Berger 836edff8678SStefan Berger static const MemoryRegionOps tpm_tis_memory_ops = { 837edff8678SStefan Berger .read = tpm_tis_mmio_read, 838edff8678SStefan Berger .write = tpm_tis_mmio_write, 839edff8678SStefan Berger .endianness = DEVICE_LITTLE_ENDIAN, 840edff8678SStefan Berger .valid = { 841edff8678SStefan Berger .min_access_size = 1, 842edff8678SStefan Berger .max_access_size = 4, 843edff8678SStefan Berger }, 844edff8678SStefan Berger }; 845edff8678SStefan Berger 846edff8678SStefan Berger /* 8475cb18b3dSStefan Berger * Get the TPMVersion of the backend device being used 8485cb18b3dSStefan Berger */ 8499af7a721SMarc-André Lureau static enum TPMVersion tpm_tis_get_tpm_version(TPMIf *ti) 8505cb18b3dSStefan Berger { 8519af7a721SMarc-André Lureau TPMState *s = TPM(ti); 8525cb18b3dSStefan Berger 853ad4aca69SStefan Berger if (tpm_backend_had_startup_error(s->be_driver)) { 854ad4aca69SStefan Berger return TPM_VERSION_UNSPEC; 855ad4aca69SStefan Berger } 856ad4aca69SStefan Berger 8575cb18b3dSStefan Berger return tpm_backend_get_tpm_version(s->be_driver); 8585cb18b3dSStefan Berger } 8595cb18b3dSStefan Berger 8605cb18b3dSStefan Berger /* 861edff8678SStefan Berger * This function is called when the machine starts, resets or due to 862edff8678SStefan Berger * S3 resume. 863edff8678SStefan Berger */ 864edff8678SStefan Berger static void tpm_tis_reset(DeviceState *dev) 865edff8678SStefan Berger { 866edff8678SStefan Berger TPMState *s = TPM(dev); 867edff8678SStefan Berger int c; 868edff8678SStefan Berger 869116694c3SStefan Berger s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver); 8701af3d63eSStefan Berger s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->be_driver), 8711af3d63eSStefan Berger TPM_TIS_BUFFER_MAX); 872116694c3SStefan Berger 8738f0605ccSStefan Berger tpm_backend_reset(s->be_driver); 874edff8678SStefan Berger 8753d4960c7SMarc-André Lureau s->active_locty = TPM_TIS_NO_LOCALITY; 8763d4960c7SMarc-André Lureau s->next_locty = TPM_TIS_NO_LOCALITY; 8773d4960c7SMarc-André Lureau s->aborting_locty = TPM_TIS_NO_LOCALITY; 878edff8678SStefan Berger 879edff8678SStefan Berger for (c = 0; c < TPM_TIS_NUM_LOCALITIES; c++) { 8803d4960c7SMarc-André Lureau s->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS; 881116694c3SStefan Berger switch (s->be_tpm_version) { 882116694c3SStefan Berger case TPM_VERSION_UNSPEC: 883116694c3SStefan Berger break; 884116694c3SStefan Berger case TPM_VERSION_1_2: 8853d4960c7SMarc-André Lureau s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY1_2; 8863d4960c7SMarc-André Lureau s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3; 887116694c3SStefan Berger break; 888116694c3SStefan Berger case TPM_VERSION_2_0: 8893d4960c7SMarc-André Lureau s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY2_0; 8903d4960c7SMarc-André Lureau s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0; 891116694c3SStefan Berger break; 892116694c3SStefan Berger } 8933d4960c7SMarc-André Lureau s->loc[c].inte = TPM_TIS_INT_POLARITY_LOW_LEVEL; 8943d4960c7SMarc-André Lureau s->loc[c].ints = 0; 8953d4960c7SMarc-André Lureau s->loc[c].state = TPM_TIS_STATE_IDLE; 896edff8678SStefan Berger 897f999d81bSStefan Berger s->rw_offset = 0; 898edff8678SStefan Berger } 899edff8678SStefan Berger 9003bd9e161SStefan Berger tpm_backend_startup_tpm(s->be_driver, s->be_buffer_size); 901edff8678SStefan Berger } 902edff8678SStefan Berger 9039ec08c48SStefan Berger /* persistent state handling */ 9049ec08c48SStefan Berger 9059ec08c48SStefan Berger static int tpm_tis_pre_save(void *opaque) 9069ec08c48SStefan Berger { 9079ec08c48SStefan Berger TPMState *s = opaque; 9089ec08c48SStefan Berger uint8_t locty = s->active_locty; 9099ec08c48SStefan Berger 9109ec08c48SStefan Berger trace_tpm_tis_pre_save(locty, s->rw_offset); 9119ec08c48SStefan Berger 9129ec08c48SStefan Berger if (DEBUG_TIS) { 9139ec08c48SStefan Berger tpm_tis_dump_state(opaque, 0); 9149ec08c48SStefan Berger } 9159ec08c48SStefan Berger 9169ec08c48SStefan Berger /* 9179ec08c48SStefan Berger * Synchronize with backend completion. 9189ec08c48SStefan Berger */ 9199ec08c48SStefan Berger tpm_backend_finish_sync(s->be_driver); 9209ec08c48SStefan Berger 9219ec08c48SStefan Berger return 0; 9229ec08c48SStefan Berger } 9239ec08c48SStefan Berger 9249ec08c48SStefan Berger static const VMStateDescription vmstate_locty = { 9259ec08c48SStefan Berger .name = "tpm-tis/locty", 9269ec08c48SStefan Berger .version_id = 0, 9279ec08c48SStefan Berger .fields = (VMStateField[]) { 9289ec08c48SStefan Berger VMSTATE_UINT32(state, TPMLocality), 9299ec08c48SStefan Berger VMSTATE_UINT32(inte, TPMLocality), 9309ec08c48SStefan Berger VMSTATE_UINT32(ints, TPMLocality), 9319ec08c48SStefan Berger VMSTATE_UINT8(access, TPMLocality), 9329ec08c48SStefan Berger VMSTATE_UINT32(sts, TPMLocality), 9339ec08c48SStefan Berger VMSTATE_UINT32(iface_id, TPMLocality), 9349ec08c48SStefan Berger VMSTATE_END_OF_LIST(), 9359ec08c48SStefan Berger } 9369ec08c48SStefan Berger }; 9379ec08c48SStefan Berger 938edff8678SStefan Berger static const VMStateDescription vmstate_tpm_tis = { 9399ec08c48SStefan Berger .name = "tpm-tis", 9409ec08c48SStefan Berger .version_id = 0, 9419ec08c48SStefan Berger .pre_save = tpm_tis_pre_save, 9429ec08c48SStefan Berger .fields = (VMStateField[]) { 9439ec08c48SStefan Berger VMSTATE_BUFFER(buffer, TPMState), 9449ec08c48SStefan Berger VMSTATE_UINT16(rw_offset, TPMState), 9459ec08c48SStefan Berger VMSTATE_UINT8(active_locty, TPMState), 9469ec08c48SStefan Berger VMSTATE_UINT8(aborting_locty, TPMState), 9479ec08c48SStefan Berger VMSTATE_UINT8(next_locty, TPMState), 9489ec08c48SStefan Berger 9499ec08c48SStefan Berger VMSTATE_STRUCT_ARRAY(loc, TPMState, TPM_TIS_NUM_LOCALITIES, 0, 9509ec08c48SStefan Berger vmstate_locty, TPMLocality), 9519ec08c48SStefan Berger 9529ec08c48SStefan Berger VMSTATE_END_OF_LIST() 9539ec08c48SStefan Berger } 954edff8678SStefan Berger }; 955edff8678SStefan Berger 956edff8678SStefan Berger static Property tpm_tis_properties[] = { 9573d4960c7SMarc-André Lureau DEFINE_PROP_UINT32("irq", TPMState, irq_num, TPM_TIS_IRQ), 958c0378544SMarc-André Lureau DEFINE_PROP_TPMBE("tpmdev", TPMState, be_driver), 959*b6148757SMarc-André Lureau DEFINE_PROP_BOOL("ppi", TPMState, ppi_enabled, true), 960edff8678SStefan Berger DEFINE_PROP_END_OF_LIST(), 961edff8678SStefan Berger }; 962edff8678SStefan Berger 963edff8678SStefan Berger static void tpm_tis_realizefn(DeviceState *dev, Error **errp) 964edff8678SStefan Berger { 965edff8678SStefan Berger TPMState *s = TPM(dev); 966edff8678SStefan Berger 96751a837e9SMarc-André Lureau if (!tpm_find()) { 96851a837e9SMarc-André Lureau error_setg(errp, "at most one TPM device is permitted"); 96951a837e9SMarc-André Lureau return; 97051a837e9SMarc-André Lureau } 97151a837e9SMarc-André Lureau 972edff8678SStefan Berger if (!s->be_driver) { 973c0378544SMarc-André Lureau error_setg(errp, "'tpmdev' property is required"); 974edff8678SStefan Berger return; 975edff8678SStefan Berger } 9763d4960c7SMarc-André Lureau if (s->irq_num > 15) { 977c87b35faSMarc-André Lureau error_setg(errp, "IRQ %d is outside valid range of 0 to 15", 978c87b35faSMarc-André Lureau s->irq_num); 979edff8678SStefan Berger return; 980edff8678SStefan Berger } 981edff8678SStefan Berger 9823d4960c7SMarc-André Lureau isa_init_irq(&s->busdev, &s->irq, s->irq_num); 9839dfd24edSStefan Berger 9849dfd24edSStefan Berger memory_region_add_subregion(isa_address_space(ISA_DEVICE(dev)), 9859dfd24edSStefan Berger TPM_TIS_ADDR_BASE, &s->mmio); 986edff8678SStefan Berger } 987edff8678SStefan Berger 988edff8678SStefan Berger static void tpm_tis_initfn(Object *obj) 989edff8678SStefan Berger { 990edff8678SStefan Berger TPMState *s = TPM(obj); 991edff8678SStefan Berger 992853dca12SPaolo Bonzini memory_region_init_io(&s->mmio, OBJECT(s), &tpm_tis_memory_ops, 993853dca12SPaolo Bonzini s, "tpm-tis-mmio", 994edff8678SStefan Berger TPM_TIS_NUM_LOCALITIES << TPM_TIS_LOCALITY_SHIFT); 995edff8678SStefan Berger } 996edff8678SStefan Berger 997edff8678SStefan Berger static void tpm_tis_class_init(ObjectClass *klass, void *data) 998edff8678SStefan Berger { 999edff8678SStefan Berger DeviceClass *dc = DEVICE_CLASS(klass); 100005a69998SMarc-André Lureau TPMIfClass *tc = TPM_IF_CLASS(klass); 1001edff8678SStefan Berger 1002edff8678SStefan Berger dc->realize = tpm_tis_realizefn; 1003edff8678SStefan Berger dc->props = tpm_tis_properties; 1004edff8678SStefan Berger dc->reset = tpm_tis_reset; 1005edff8678SStefan Berger dc->vmsd = &vmstate_tpm_tis; 1006191adc94SMarc-André Lureau tc->model = TPM_MODEL_TPM_TIS; 10079af7a721SMarc-André Lureau tc->get_version = tpm_tis_get_tpm_version; 100805a69998SMarc-André Lureau tc->request_completed = tpm_tis_request_completed; 1009edff8678SStefan Berger } 1010edff8678SStefan Berger 1011edff8678SStefan Berger static const TypeInfo tpm_tis_info = { 1012edff8678SStefan Berger .name = TYPE_TPM_TIS, 1013edff8678SStefan Berger .parent = TYPE_ISA_DEVICE, 1014edff8678SStefan Berger .instance_size = sizeof(TPMState), 1015edff8678SStefan Berger .instance_init = tpm_tis_initfn, 1016edff8678SStefan Berger .class_init = tpm_tis_class_init, 1017698f5daaSMarc-André Lureau .interfaces = (InterfaceInfo[]) { 1018698f5daaSMarc-André Lureau { TYPE_TPM_IF }, 1019698f5daaSMarc-André Lureau { } 1020698f5daaSMarc-André Lureau } 1021edff8678SStefan Berger }; 1022edff8678SStefan Berger 1023edff8678SStefan Berger static void tpm_tis_register(void) 1024edff8678SStefan Berger { 1025edff8678SStefan Berger type_register_static(&tpm_tis_info); 1026edff8678SStefan Berger } 1027edff8678SStefan Berger 1028edff8678SStefan Berger type_init(tpm_tis_register) 1029