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" 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 41732cd587SMarc-André Lureau #define TPM_TIS_NUM_LOCALITIES 5 /* per spec */ 42732cd587SMarc-André Lureau #define TPM_TIS_LOCALITY_SHIFT 12 43732cd587SMarc-André Lureau #define TPM_TIS_NO_LOCALITY 0xff 44732cd587SMarc-André Lureau 45732cd587SMarc-André Lureau #define TPM_TIS_IS_VALID_LOCTY(x) ((x) < TPM_TIS_NUM_LOCALITIES) 46732cd587SMarc-André Lureau 47732cd587SMarc-André Lureau #define TPM_TIS_BUFFER_MAX 4096 48732cd587SMarc-André Lureau 49732cd587SMarc-André Lureau typedef enum { 50732cd587SMarc-André Lureau TPM_TIS_STATE_IDLE = 0, 51732cd587SMarc-André Lureau TPM_TIS_STATE_READY, 52732cd587SMarc-André Lureau TPM_TIS_STATE_COMPLETION, 53732cd587SMarc-André Lureau TPM_TIS_STATE_EXECUTION, 54732cd587SMarc-André Lureau TPM_TIS_STATE_RECEPTION, 55732cd587SMarc-André Lureau } TPMTISState; 56732cd587SMarc-André Lureau 57732cd587SMarc-André Lureau /* locality data -- all fields are persisted */ 58732cd587SMarc-André Lureau typedef struct TPMLocality { 59732cd587SMarc-André Lureau TPMTISState state; 60732cd587SMarc-André Lureau uint8_t access; 61732cd587SMarc-André Lureau uint32_t sts; 62732cd587SMarc-André Lureau uint32_t iface_id; 63732cd587SMarc-André Lureau uint32_t inte; 64732cd587SMarc-André Lureau uint32_t ints; 65732cd587SMarc-André Lureau } TPMLocality; 66732cd587SMarc-André Lureau 6736e86589SMarc-André Lureau typedef struct TPMState { 683d4960c7SMarc-André Lureau ISADevice busdev; 693d4960c7SMarc-André Lureau MemoryRegion mmio; 703d4960c7SMarc-André Lureau 71c5496b97SStefan Berger unsigned char buffer[TPM_TIS_BUFFER_MAX]; 72f999d81bSStefan Berger uint16_t rw_offset; 73732cd587SMarc-André Lureau 74732cd587SMarc-André Lureau uint8_t active_locty; 75732cd587SMarc-André Lureau uint8_t aborting_locty; 76732cd587SMarc-André Lureau uint8_t next_locty; 77732cd587SMarc-André Lureau 78732cd587SMarc-André Lureau TPMLocality loc[TPM_TIS_NUM_LOCALITIES]; 79732cd587SMarc-André Lureau 80732cd587SMarc-André Lureau qemu_irq irq; 81732cd587SMarc-André Lureau uint32_t irq_num; 82732cd587SMarc-André Lureau 83732cd587SMarc-André Lureau TPMBackendCmd cmd; 84732cd587SMarc-André Lureau 85732cd587SMarc-André Lureau TPMBackend *be_driver; 86732cd587SMarc-André Lureau TPMVersion be_tpm_version; 87b21e6aafSStefan Berger 88b21e6aafSStefan Berger size_t be_buffer_size; 89b6148757SMarc-André Lureau 90b6148757SMarc-André Lureau bool ppi_enabled; 913b97c01eSStefan Berger TPMPPI ppi; 9236e86589SMarc-André Lureau } TPMState; 93732cd587SMarc-André Lureau 94732cd587SMarc-André Lureau #define TPM(obj) OBJECT_CHECK(TPMState, (obj), TYPE_TPM_TIS) 95edff8678SStefan Berger 964d1ba9c4SStefan Berger #define DEBUG_TIS 0 97edff8678SStefan Berger 988db7c415SStefan Berger /* local prototypes */ 998db7c415SStefan Berger 1008db7c415SStefan Berger static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, 1018db7c415SStefan Berger unsigned size); 1028db7c415SStefan Berger 103edff8678SStefan Berger /* utility functions */ 104edff8678SStefan Berger 105edff8678SStefan Berger static uint8_t tpm_tis_locality_from_addr(hwaddr addr) 106edff8678SStefan Berger { 107edff8678SStefan Berger return (uint8_t)((addr >> TPM_TIS_LOCALITY_SHIFT) & 0x7); 108edff8678SStefan Berger } 109edff8678SStefan Berger 110edff8678SStefan Berger 111edff8678SStefan Berger /* 112fd859081SStefan Berger * Set the given flags in the STS register by clearing the register but 113116694c3SStefan Berger * preserving the SELFTEST_DONE and TPM_FAMILY_MASK flags and then setting 114116694c3SStefan Berger * the new flags. 115fd859081SStefan Berger * 116fd859081SStefan Berger * The SELFTEST_DONE flag is acquired from the backend that determines it by 117fd859081SStefan Berger * peeking into TPM commands. 118fd859081SStefan Berger * 119fd859081SStefan Berger * A VM suspend/resume will preserve the flag by storing it into the VM 120fd859081SStefan Berger * device state, but the backend will not remember it when QEMU is started 121fd859081SStefan Berger * again. Therefore, we cache the flag here. Once set, it will not be unset 122fd859081SStefan Berger * except by a reset. 123fd859081SStefan Berger */ 124fd859081SStefan Berger static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags) 125fd859081SStefan Berger { 126116694c3SStefan Berger l->sts &= TPM_TIS_STS_SELFTEST_DONE | TPM_TIS_STS_TPM_FAMILY_MASK; 127fd859081SStefan Berger l->sts |= flags; 128fd859081SStefan Berger } 129fd859081SStefan Berger 130fd859081SStefan Berger /* 131edff8678SStefan Berger * Send a request to the TPM. 132edff8678SStefan Berger */ 133edff8678SStefan Berger static void tpm_tis_tpm_send(TPMState *s, uint8_t locty) 134edff8678SStefan Berger { 135*3688d73bSStefan Berger if (trace_event_get_state_backends(TRACE_TPM_UTIL_SHOW_BUFFER)) { 136*3688d73bSStefan Berger tpm_util_show_buffer(s->buffer, s->be_buffer_size, "To TPM"); 137fcbed221SStefan Berger } 138edff8678SStefan Berger 139edff8678SStefan Berger /* 140f999d81bSStefan Berger * rw_offset serves as length indicator for length of data; 141edff8678SStefan Berger * it's reset when the response comes back 142edff8678SStefan Berger */ 1433d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_EXECUTION; 144edff8678SStefan Berger 1450e43b7e6SMarc-André Lureau s->cmd = (TPMBackendCmd) { 1460e43b7e6SMarc-André Lureau .locty = locty, 147c5496b97SStefan Berger .in = s->buffer, 148f999d81bSStefan Berger .in_len = s->rw_offset, 149c5496b97SStefan Berger .out = s->buffer, 150e6b703f6SStefan Berger .out_len = s->be_buffer_size, 1510e43b7e6SMarc-André Lureau }; 1520e43b7e6SMarc-André Lureau 1530e43b7e6SMarc-André Lureau tpm_backend_deliver_request(s->be_driver, &s->cmd); 154edff8678SStefan Berger } 155edff8678SStefan Berger 156edff8678SStefan Berger /* raise an interrupt if allowed */ 157edff8678SStefan Berger static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask) 158edff8678SStefan Berger { 159edff8678SStefan Berger if (!TPM_TIS_IS_VALID_LOCTY(locty)) { 160edff8678SStefan Berger return; 161edff8678SStefan Berger } 162edff8678SStefan Berger 1633d4960c7SMarc-André Lureau if ((s->loc[locty].inte & TPM_TIS_INT_ENABLED) && 1643d4960c7SMarc-André Lureau (s->loc[locty].inte & irqmask)) { 165fcbed221SStefan Berger trace_tpm_tis_raise_irq(irqmask); 1663d4960c7SMarc-André Lureau qemu_irq_raise(s->irq); 1673d4960c7SMarc-André Lureau s->loc[locty].ints |= irqmask; 168edff8678SStefan Berger } 169edff8678SStefan Berger } 170edff8678SStefan Berger 171edff8678SStefan Berger static uint32_t tpm_tis_check_request_use_except(TPMState *s, uint8_t locty) 172edff8678SStefan Berger { 173edff8678SStefan Berger uint8_t l; 174edff8678SStefan Berger 175edff8678SStefan Berger for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 176edff8678SStefan Berger if (l == locty) { 177edff8678SStefan Berger continue; 178edff8678SStefan Berger } 1793d4960c7SMarc-André Lureau if ((s->loc[l].access & TPM_TIS_ACCESS_REQUEST_USE)) { 180edff8678SStefan Berger return 1; 181edff8678SStefan Berger } 182edff8678SStefan Berger } 183edff8678SStefan Berger 184edff8678SStefan Berger return 0; 185edff8678SStefan Berger } 186edff8678SStefan Berger 187edff8678SStefan Berger static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty) 188edff8678SStefan Berger { 1893d4960c7SMarc-André Lureau bool change = (s->active_locty != new_active_locty); 190edff8678SStefan Berger bool is_seize; 191edff8678SStefan Berger uint8_t mask; 192edff8678SStefan Berger 1933d4960c7SMarc-André Lureau if (change && TPM_TIS_IS_VALID_LOCTY(s->active_locty)) { 194edff8678SStefan Berger is_seize = TPM_TIS_IS_VALID_LOCTY(new_active_locty) && 1953d4960c7SMarc-André Lureau s->loc[new_active_locty].access & TPM_TIS_ACCESS_SEIZE; 196edff8678SStefan Berger 197edff8678SStefan Berger if (is_seize) { 198edff8678SStefan Berger mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY); 199edff8678SStefan Berger } else { 200edff8678SStefan Berger mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY| 201edff8678SStefan Berger TPM_TIS_ACCESS_REQUEST_USE); 202edff8678SStefan Berger } 203edff8678SStefan Berger /* reset flags on the old active locality */ 2043d4960c7SMarc-André Lureau s->loc[s->active_locty].access &= mask; 205edff8678SStefan Berger 206edff8678SStefan Berger if (is_seize) { 2073d4960c7SMarc-André Lureau s->loc[s->active_locty].access |= TPM_TIS_ACCESS_BEEN_SEIZED; 208edff8678SStefan Berger } 209edff8678SStefan Berger } 210edff8678SStefan Berger 2113d4960c7SMarc-André Lureau s->active_locty = new_active_locty; 212edff8678SStefan Berger 213fcbed221SStefan Berger trace_tpm_tis_new_active_locality(s->active_locty); 214edff8678SStefan Berger 215edff8678SStefan Berger if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) { 216edff8678SStefan Berger /* set flags on the new active locality */ 2173d4960c7SMarc-André Lureau s->loc[new_active_locty].access |= TPM_TIS_ACCESS_ACTIVE_LOCALITY; 2183d4960c7SMarc-André Lureau s->loc[new_active_locty].access &= ~(TPM_TIS_ACCESS_REQUEST_USE | 219edff8678SStefan Berger TPM_TIS_ACCESS_SEIZE); 220edff8678SStefan Berger } 221edff8678SStefan Berger 222edff8678SStefan Berger if (change) { 2233d4960c7SMarc-André Lureau tpm_tis_raise_irq(s, s->active_locty, TPM_TIS_INT_LOCALITY_CHANGED); 224edff8678SStefan Berger } 225edff8678SStefan Berger } 226edff8678SStefan Berger 227edff8678SStefan Berger /* abort -- this function switches the locality */ 2280f5faee3SStefan Berger static void tpm_tis_abort(TPMState *s) 229edff8678SStefan Berger { 230f999d81bSStefan Berger s->rw_offset = 0; 231edff8678SStefan Berger 232fcbed221SStefan Berger trace_tpm_tis_abort(s->next_locty); 233edff8678SStefan Berger 234edff8678SStefan Berger /* 235edff8678SStefan Berger * Need to react differently depending on who's aborting now and 236edff8678SStefan Berger * which locality will become active afterwards. 237edff8678SStefan Berger */ 2383d4960c7SMarc-André Lureau if (s->aborting_locty == s->next_locty) { 2393d4960c7SMarc-André Lureau s->loc[s->aborting_locty].state = TPM_TIS_STATE_READY; 2403d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[s->aborting_locty], 241fd859081SStefan Berger TPM_TIS_STS_COMMAND_READY); 2423d4960c7SMarc-André Lureau tpm_tis_raise_irq(s, s->aborting_locty, TPM_TIS_INT_COMMAND_READY); 243edff8678SStefan Berger } 244edff8678SStefan Berger 245edff8678SStefan Berger /* locality after abort is another one than the current one */ 2463d4960c7SMarc-André Lureau tpm_tis_new_active_locality(s, s->next_locty); 247edff8678SStefan Berger 2483d4960c7SMarc-André Lureau s->next_locty = TPM_TIS_NO_LOCALITY; 249edff8678SStefan Berger /* nobody's aborting a command anymore */ 2503d4960c7SMarc-André Lureau s->aborting_locty = TPM_TIS_NO_LOCALITY; 251edff8678SStefan Berger } 252edff8678SStefan Berger 253edff8678SStefan Berger /* prepare aborting current command */ 254edff8678SStefan Berger static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty) 255edff8678SStefan Berger { 256edff8678SStefan Berger uint8_t busy_locty; 257edff8678SStefan Berger 258e92b63eaSStefan Berger assert(TPM_TIS_IS_VALID_LOCTY(newlocty)); 259e92b63eaSStefan Berger 260e92b63eaSStefan Berger s->aborting_locty = locty; /* may also be TPM_TIS_NO_LOCALITY */ 2613d4960c7SMarc-André Lureau s->next_locty = newlocty; /* locality after successful abort */ 262edff8678SStefan Berger 263edff8678SStefan Berger /* 264edff8678SStefan Berger * only abort a command using an interrupt if currently executing 265edff8678SStefan Berger * a command AND if there's a valid connection to the vTPM. 266edff8678SStefan Berger */ 267edff8678SStefan Berger for (busy_locty = 0; busy_locty < TPM_TIS_NUM_LOCALITIES; busy_locty++) { 2683d4960c7SMarc-André Lureau if (s->loc[busy_locty].state == TPM_TIS_STATE_EXECUTION) { 269edff8678SStefan Berger /* 270edff8678SStefan Berger * request the backend to cancel. Some backends may not 271edff8678SStefan Berger * support it 272edff8678SStefan Berger */ 2738f0605ccSStefan Berger tpm_backend_cancel_cmd(s->be_driver); 274edff8678SStefan Berger return; 275edff8678SStefan Berger } 276edff8678SStefan Berger } 277edff8678SStefan Berger 2780f5faee3SStefan Berger tpm_tis_abort(s); 279edff8678SStefan Berger } 280edff8678SStefan Berger 28168999059SMarc-André Lureau /* 28268999059SMarc-André Lureau * Callback from the TPM to indicate that the response was received. 28368999059SMarc-André Lureau */ 2846a8a2354SMarc-André Lureau static void tpm_tis_request_completed(TPMIf *ti, int ret) 285edff8678SStefan Berger { 28668999059SMarc-André Lureau TPMState *s = TPM(ti); 2870e43b7e6SMarc-André Lureau uint8_t locty = s->cmd.locty; 28868999059SMarc-André Lureau uint8_t l; 28968999059SMarc-André Lureau 290a639f961SStefan Berger assert(TPM_TIS_IS_VALID_LOCTY(locty)); 291a639f961SStefan Berger 29268999059SMarc-André Lureau if (s->cmd.selftest_done) { 29368999059SMarc-André Lureau for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 2946a50bb98SPrasad J Pandit s->loc[l].sts |= TPM_TIS_STS_SELFTEST_DONE; 29568999059SMarc-André Lureau } 29668999059SMarc-André Lureau } 297edff8678SStefan Berger 2986a8a2354SMarc-André Lureau /* FIXME: report error if ret != 0 */ 2993d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 300fd859081SStefan Berger TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE); 3013d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_COMPLETION; 302f999d81bSStefan Berger s->rw_offset = 0; 303edff8678SStefan Berger 304*3688d73bSStefan Berger if (trace_event_get_state_backends(TRACE_TPM_UTIL_SHOW_BUFFER)) { 305*3688d73bSStefan Berger tpm_util_show_buffer(s->buffer, s->be_buffer_size, "From TPM"); 306fcbed221SStefan Berger } 307298d8b81SStefan Berger 3083d4960c7SMarc-André Lureau if (TPM_TIS_IS_VALID_LOCTY(s->next_locty)) { 3090f5faee3SStefan Berger tpm_tis_abort(s); 310edff8678SStefan Berger } 311edff8678SStefan Berger 312edff8678SStefan Berger tpm_tis_raise_irq(s, locty, 313edff8678SStefan Berger TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID); 314edff8678SStefan Berger } 315edff8678SStefan Berger 316edff8678SStefan Berger /* 317edff8678SStefan Berger * Read a byte of response data 318edff8678SStefan Berger */ 319edff8678SStefan Berger static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty) 320edff8678SStefan Berger { 321edff8678SStefan Berger uint32_t ret = TPM_TIS_NO_DATA_BYTE; 322edff8678SStefan Berger uint16_t len; 323edff8678SStefan Berger 3243d4960c7SMarc-André Lureau if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) { 325c5496b97SStefan Berger len = MIN(tpm_cmd_get_size(&s->buffer), 326e6b703f6SStefan Berger s->be_buffer_size); 327edff8678SStefan Berger 328f999d81bSStefan Berger ret = s->buffer[s->rw_offset++]; 329f999d81bSStefan Berger if (s->rw_offset >= len) { 330edff8678SStefan Berger /* got last byte */ 3313d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 332edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); 333edff8678SStefan Berger } 334fcbed221SStefan Berger trace_tpm_tis_data_read(ret, s->rw_offset - 1); 335edff8678SStefan Berger } 336edff8678SStefan Berger 337edff8678SStefan Berger return ret; 338edff8678SStefan Berger } 339edff8678SStefan Berger 3408db7c415SStefan Berger #ifdef DEBUG_TIS 3418db7c415SStefan Berger static void tpm_tis_dump_state(void *opaque, hwaddr addr) 3428db7c415SStefan Berger { 3438db7c415SStefan Berger static const unsigned regs[] = { 3448db7c415SStefan Berger TPM_TIS_REG_ACCESS, 3458db7c415SStefan Berger TPM_TIS_REG_INT_ENABLE, 3468db7c415SStefan Berger TPM_TIS_REG_INT_VECTOR, 3478db7c415SStefan Berger TPM_TIS_REG_INT_STATUS, 3488db7c415SStefan Berger TPM_TIS_REG_INTF_CAPABILITY, 3498db7c415SStefan Berger TPM_TIS_REG_STS, 3508db7c415SStefan Berger TPM_TIS_REG_DID_VID, 3518db7c415SStefan Berger TPM_TIS_REG_RID, 3528db7c415SStefan Berger 0xfff}; 3538db7c415SStefan Berger int idx; 3548db7c415SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 3558db7c415SStefan Berger hwaddr base = addr & ~0xfff; 3568db7c415SStefan Berger TPMState *s = opaque; 3578db7c415SStefan Berger 358fcbed221SStefan Berger printf("tpm_tis: active locality : %d\n" 3598db7c415SStefan Berger "tpm_tis: state of locality %d : %d\n" 3608db7c415SStefan Berger "tpm_tis: register dump:\n", 3613d4960c7SMarc-André Lureau s->active_locty, 3623d4960c7SMarc-André Lureau locty, s->loc[locty].state); 3638db7c415SStefan Berger 3648db7c415SStefan Berger for (idx = 0; regs[idx] != 0xfff; idx++) { 365fcbed221SStefan Berger printf("tpm_tis: 0x%04x : 0x%08x\n", regs[idx], 366070c7607SStefan Berger (int)tpm_tis_mmio_read(opaque, base + regs[idx], 4)); 3678db7c415SStefan Berger } 3688db7c415SStefan Berger 369fcbed221SStefan Berger printf("tpm_tis: r/w offset : %d\n" 3708db7c415SStefan Berger "tpm_tis: result buffer : ", 371f999d81bSStefan Berger s->rw_offset); 3728db7c415SStefan Berger for (idx = 0; 373c5496b97SStefan Berger idx < MIN(tpm_cmd_get_size(&s->buffer), s->be_buffer_size); 3748db7c415SStefan Berger idx++) { 375fcbed221SStefan Berger printf("%c%02x%s", 376f999d81bSStefan Berger s->rw_offset == idx ? '>' : ' ', 377c5496b97SStefan Berger s->buffer[idx], 3788db7c415SStefan Berger ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : ""); 3798db7c415SStefan Berger } 380fcbed221SStefan Berger printf("\n"); 3818db7c415SStefan Berger } 3828db7c415SStefan Berger #endif 3838db7c415SStefan Berger 384edff8678SStefan Berger /* 385edff8678SStefan Berger * Read a register of the TIS interface 386edff8678SStefan Berger * See specs pages 33-63 for description of the registers 387edff8678SStefan Berger */ 388edff8678SStefan Berger static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, 389edff8678SStefan Berger unsigned size) 390edff8678SStefan Berger { 391edff8678SStefan Berger TPMState *s = opaque; 392edff8678SStefan Berger uint16_t offset = addr & 0xffc; 393edff8678SStefan Berger uint8_t shift = (addr & 0x3) * 8; 394edff8678SStefan Berger uint32_t val = 0xffffffff; 395edff8678SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 396edff8678SStefan Berger uint32_t avail; 397feeb755fSStefan Berger uint8_t v; 398edff8678SStefan Berger 3998f0605ccSStefan Berger if (tpm_backend_had_startup_error(s->be_driver)) { 4006cd65969SStefan Berger return 0; 401edff8678SStefan Berger } 402edff8678SStefan Berger 403edff8678SStefan Berger switch (offset) { 404edff8678SStefan Berger case TPM_TIS_REG_ACCESS: 405edff8678SStefan Berger /* never show the SEIZE flag even though we use it internally */ 4063d4960c7SMarc-André Lureau val = s->loc[locty].access & ~TPM_TIS_ACCESS_SEIZE; 407edff8678SStefan Berger /* the pending flag is always calculated */ 408edff8678SStefan Berger if (tpm_tis_check_request_use_except(s, locty)) { 409edff8678SStefan Berger val |= TPM_TIS_ACCESS_PENDING_REQUEST; 410edff8678SStefan Berger } 4118f0605ccSStefan Berger val |= !tpm_backend_get_tpm_established_flag(s->be_driver); 412edff8678SStefan Berger break; 413edff8678SStefan Berger case TPM_TIS_REG_INT_ENABLE: 4143d4960c7SMarc-André Lureau val = s->loc[locty].inte; 415edff8678SStefan Berger break; 416edff8678SStefan Berger case TPM_TIS_REG_INT_VECTOR: 4173d4960c7SMarc-André Lureau val = s->irq_num; 418edff8678SStefan Berger break; 419edff8678SStefan Berger case TPM_TIS_REG_INT_STATUS: 4203d4960c7SMarc-André Lureau val = s->loc[locty].ints; 421edff8678SStefan Berger break; 422edff8678SStefan Berger case TPM_TIS_REG_INTF_CAPABILITY: 423116694c3SStefan Berger switch (s->be_tpm_version) { 424116694c3SStefan Berger case TPM_VERSION_UNSPEC: 425116694c3SStefan Berger val = 0; 426116694c3SStefan Berger break; 427116694c3SStefan Berger case TPM_VERSION_1_2: 428116694c3SStefan Berger val = TPM_TIS_CAPABILITIES_SUPPORTED1_3; 429116694c3SStefan Berger break; 430116694c3SStefan Berger case TPM_VERSION_2_0: 431116694c3SStefan Berger val = TPM_TIS_CAPABILITIES_SUPPORTED2_0; 432116694c3SStefan Berger break; 433116694c3SStefan Berger } 434edff8678SStefan Berger break; 435edff8678SStefan Berger case TPM_TIS_REG_STS: 4363d4960c7SMarc-André Lureau if (s->active_locty == locty) { 4373d4960c7SMarc-André Lureau if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) { 438edff8678SStefan Berger val = TPM_TIS_BURST_COUNT( 439c5496b97SStefan Berger MIN(tpm_cmd_get_size(&s->buffer), 440e6b703f6SStefan Berger s->be_buffer_size) 441f999d81bSStefan Berger - s->rw_offset) | s->loc[locty].sts; 442edff8678SStefan Berger } else { 443f999d81bSStefan Berger avail = s->be_buffer_size - s->rw_offset; 444edff8678SStefan Berger /* 445edff8678SStefan Berger * byte-sized reads should not return 0x00 for 0x100 446edff8678SStefan Berger * available bytes. 447edff8678SStefan Berger */ 448edff8678SStefan Berger if (size == 1 && avail > 0xff) { 449edff8678SStefan Berger avail = 0xff; 450edff8678SStefan Berger } 4513d4960c7SMarc-André Lureau val = TPM_TIS_BURST_COUNT(avail) | s->loc[locty].sts; 452edff8678SStefan Berger } 453edff8678SStefan Berger } 454edff8678SStefan Berger break; 455edff8678SStefan Berger case TPM_TIS_REG_DATA_FIFO: 4562eae8c75SStefan Berger case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END: 4573d4960c7SMarc-André Lureau if (s->active_locty == locty) { 458feeb755fSStefan Berger if (size > 4 - (addr & 0x3)) { 459feeb755fSStefan Berger /* prevent access beyond FIFO */ 460feeb755fSStefan Berger size = 4 - (addr & 0x3); 461feeb755fSStefan Berger } 462feeb755fSStefan Berger val = 0; 463feeb755fSStefan Berger shift = 0; 464feeb755fSStefan Berger while (size > 0) { 4653d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 466edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 467feeb755fSStefan Berger v = tpm_tis_data_read(s, locty); 468edff8678SStefan Berger break; 469edff8678SStefan Berger default: 470feeb755fSStefan Berger v = TPM_TIS_NO_DATA_BYTE; 471edff8678SStefan Berger break; 472edff8678SStefan Berger } 473feeb755fSStefan Berger val |= (v << shift); 474feeb755fSStefan Berger shift += 8; 475feeb755fSStefan Berger size--; 476feeb755fSStefan Berger } 477feeb755fSStefan Berger shift = 0; /* no more adjustments */ 478edff8678SStefan Berger } 479edff8678SStefan Berger break; 480116694c3SStefan Berger case TPM_TIS_REG_INTERFACE_ID: 4813d4960c7SMarc-André Lureau val = s->loc[locty].iface_id; 482116694c3SStefan Berger break; 483edff8678SStefan Berger case TPM_TIS_REG_DID_VID: 484edff8678SStefan Berger val = (TPM_TIS_TPM_DID << 16) | TPM_TIS_TPM_VID; 485edff8678SStefan Berger break; 486edff8678SStefan Berger case TPM_TIS_REG_RID: 487edff8678SStefan Berger val = TPM_TIS_TPM_RID; 488edff8678SStefan Berger break; 4898db7c415SStefan Berger #ifdef DEBUG_TIS 4908db7c415SStefan Berger case TPM_TIS_REG_DEBUG: 4918db7c415SStefan Berger tpm_tis_dump_state(opaque, addr); 4928db7c415SStefan Berger break; 4938db7c415SStefan Berger #endif 494edff8678SStefan Berger } 495edff8678SStefan Berger 496edff8678SStefan Berger if (shift) { 497edff8678SStefan Berger val >>= shift; 498edff8678SStefan Berger } 499edff8678SStefan Berger 500fcbed221SStefan Berger trace_tpm_tis_mmio_read(size, addr, val); 501edff8678SStefan Berger 502edff8678SStefan Berger return val; 503edff8678SStefan Berger } 504edff8678SStefan Berger 505edff8678SStefan Berger /* 506edff8678SStefan Berger * Write a value to a register of the TIS interface 507edff8678SStefan Berger * See specs pages 33-63 for description of the registers 508edff8678SStefan Berger */ 509ff2bc0c1SMarc-André Lureau static void tpm_tis_mmio_write(void *opaque, hwaddr addr, 510ff2bc0c1SMarc-André Lureau uint64_t val, unsigned size) 511edff8678SStefan Berger { 512edff8678SStefan Berger TPMState *s = opaque; 513feeb755fSStefan Berger uint16_t off = addr & 0xffc; 514feeb755fSStefan Berger uint8_t shift = (addr & 0x3) * 8; 515edff8678SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 516edff8678SStefan Berger uint8_t active_locty, l; 517edff8678SStefan Berger int c, set_new_locty = 1; 518edff8678SStefan Berger uint16_t len; 519feeb755fSStefan Berger uint32_t mask = (size == 1) ? 0xff : ((size == 2) ? 0xffff : ~0); 520edff8678SStefan Berger 521fcbed221SStefan Berger trace_tpm_tis_mmio_write(size, addr, val); 522edff8678SStefan Berger 523ff2bc0c1SMarc-André Lureau if (locty == 4) { 524fcbed221SStefan Berger trace_tpm_tis_mmio_write_locty4(); 525edff8678SStefan Berger return; 526edff8678SStefan Berger } 527edff8678SStefan Berger 5288f0605ccSStefan Berger if (tpm_backend_had_startup_error(s->be_driver)) { 529edff8678SStefan Berger return; 530edff8678SStefan Berger } 531edff8678SStefan Berger 532feeb755fSStefan Berger val &= mask; 533feeb755fSStefan Berger 534feeb755fSStefan Berger if (shift) { 535feeb755fSStefan Berger val <<= shift; 536feeb755fSStefan Berger mask <<= shift; 537feeb755fSStefan Berger } 538feeb755fSStefan Berger 539feeb755fSStefan Berger mask ^= 0xffffffff; 540feeb755fSStefan Berger 541edff8678SStefan Berger switch (off) { 542edff8678SStefan Berger case TPM_TIS_REG_ACCESS: 543edff8678SStefan Berger 544edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_SEIZE)) { 545edff8678SStefan Berger val &= ~(TPM_TIS_ACCESS_REQUEST_USE | 546edff8678SStefan Berger TPM_TIS_ACCESS_ACTIVE_LOCALITY); 547edff8678SStefan Berger } 548edff8678SStefan Berger 5493d4960c7SMarc-André Lureau active_locty = s->active_locty; 550edff8678SStefan Berger 551edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) { 552edff8678SStefan Berger /* give up locality if currently owned */ 5533d4960c7SMarc-André Lureau if (s->active_locty == locty) { 554fcbed221SStefan Berger trace_tpm_tis_mmio_write_release_locty(locty); 555edff8678SStefan Berger 556edff8678SStefan Berger uint8_t newlocty = TPM_TIS_NO_LOCALITY; 557edff8678SStefan Berger /* anybody wants the locality ? */ 558edff8678SStefan Berger for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) { 5593d4960c7SMarc-André Lureau if ((s->loc[c].access & TPM_TIS_ACCESS_REQUEST_USE)) { 560fcbed221SStefan Berger trace_tpm_tis_mmio_write_locty_req_use(c); 561edff8678SStefan Berger newlocty = c; 562edff8678SStefan Berger break; 563edff8678SStefan Berger } 564edff8678SStefan Berger } 565fcbed221SStefan Berger trace_tpm_tis_mmio_write_next_locty(newlocty); 566edff8678SStefan Berger 567edff8678SStefan Berger if (TPM_TIS_IS_VALID_LOCTY(newlocty)) { 568edff8678SStefan Berger set_new_locty = 0; 569edff8678SStefan Berger tpm_tis_prep_abort(s, locty, newlocty); 570edff8678SStefan Berger } else { 571edff8678SStefan Berger active_locty = TPM_TIS_NO_LOCALITY; 572edff8678SStefan Berger } 573edff8678SStefan Berger } else { 574edff8678SStefan Berger /* not currently the owner; clear a pending request */ 5753d4960c7SMarc-André Lureau s->loc[locty].access &= ~TPM_TIS_ACCESS_REQUEST_USE; 576edff8678SStefan Berger } 577edff8678SStefan Berger } 578edff8678SStefan Berger 579edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_BEEN_SEIZED)) { 5803d4960c7SMarc-André Lureau s->loc[locty].access &= ~TPM_TIS_ACCESS_BEEN_SEIZED; 581edff8678SStefan Berger } 582edff8678SStefan Berger 583edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_SEIZE)) { 584edff8678SStefan Berger /* 585edff8678SStefan Berger * allow seize if a locality is active and the requesting 586edff8678SStefan Berger * locality is higher than the one that's active 587edff8678SStefan Berger * OR 588edff8678SStefan Berger * allow seize for requesting locality if no locality is 589edff8678SStefan Berger * active 590edff8678SStefan Berger */ 5913d4960c7SMarc-André Lureau while ((TPM_TIS_IS_VALID_LOCTY(s->active_locty) && 5923d4960c7SMarc-André Lureau locty > s->active_locty) || 5933d4960c7SMarc-André Lureau !TPM_TIS_IS_VALID_LOCTY(s->active_locty)) { 594edff8678SStefan Berger bool higher_seize = FALSE; 595edff8678SStefan Berger 596edff8678SStefan Berger /* already a pending SEIZE ? */ 5973d4960c7SMarc-André Lureau if ((s->loc[locty].access & TPM_TIS_ACCESS_SEIZE)) { 598edff8678SStefan Berger break; 599edff8678SStefan Berger } 600edff8678SStefan Berger 601edff8678SStefan Berger /* check for ongoing seize by a higher locality */ 602edff8678SStefan Berger for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES; l++) { 6033d4960c7SMarc-André Lureau if ((s->loc[l].access & TPM_TIS_ACCESS_SEIZE)) { 604edff8678SStefan Berger higher_seize = TRUE; 605edff8678SStefan Berger break; 606edff8678SStefan Berger } 607edff8678SStefan Berger } 608edff8678SStefan Berger 609edff8678SStefan Berger if (higher_seize) { 610edff8678SStefan Berger break; 611edff8678SStefan Berger } 612edff8678SStefan Berger 613edff8678SStefan Berger /* cancel any seize by a lower locality */ 61437b55d67SLiam Merwick for (l = 0; l < locty; l++) { 6153d4960c7SMarc-André Lureau s->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE; 616edff8678SStefan Berger } 617edff8678SStefan Berger 6183d4960c7SMarc-André Lureau s->loc[locty].access |= TPM_TIS_ACCESS_SEIZE; 619fcbed221SStefan Berger 620fcbed221SStefan Berger trace_tpm_tis_mmio_write_locty_seized(locty, s->active_locty); 621fcbed221SStefan Berger trace_tpm_tis_mmio_write_init_abort(); 622fcbed221SStefan Berger 623edff8678SStefan Berger set_new_locty = 0; 6243d4960c7SMarc-André Lureau tpm_tis_prep_abort(s, s->active_locty, locty); 625edff8678SStefan Berger break; 626edff8678SStefan Berger } 627edff8678SStefan Berger } 628edff8678SStefan Berger 629edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_REQUEST_USE)) { 6303d4960c7SMarc-André Lureau if (s->active_locty != locty) { 6313d4960c7SMarc-André Lureau if (TPM_TIS_IS_VALID_LOCTY(s->active_locty)) { 6323d4960c7SMarc-André Lureau s->loc[locty].access |= TPM_TIS_ACCESS_REQUEST_USE; 633edff8678SStefan Berger } else { 634edff8678SStefan Berger /* no locality active -> make this one active now */ 635edff8678SStefan Berger active_locty = locty; 636edff8678SStefan Berger } 637edff8678SStefan Berger } 638edff8678SStefan Berger } 639edff8678SStefan Berger 640edff8678SStefan Berger if (set_new_locty) { 641edff8678SStefan Berger tpm_tis_new_active_locality(s, active_locty); 642edff8678SStefan Berger } 643edff8678SStefan Berger 644edff8678SStefan Berger break; 645edff8678SStefan Berger case TPM_TIS_REG_INT_ENABLE: 6463d4960c7SMarc-André Lureau if (s->active_locty != locty) { 647edff8678SStefan Berger break; 648edff8678SStefan Berger } 649edff8678SStefan Berger 6503d4960c7SMarc-André Lureau s->loc[locty].inte &= mask; 6513d4960c7SMarc-André Lureau s->loc[locty].inte |= (val & (TPM_TIS_INT_ENABLED | 652edff8678SStefan Berger TPM_TIS_INT_POLARITY_MASK | 653edff8678SStefan Berger TPM_TIS_INTERRUPTS_SUPPORTED)); 654edff8678SStefan Berger break; 655edff8678SStefan Berger case TPM_TIS_REG_INT_VECTOR: 656edff8678SStefan Berger /* hard wired -- ignore */ 657edff8678SStefan Berger break; 658edff8678SStefan Berger case TPM_TIS_REG_INT_STATUS: 6593d4960c7SMarc-André Lureau if (s->active_locty != locty) { 660edff8678SStefan Berger break; 661edff8678SStefan Berger } 662edff8678SStefan Berger 663edff8678SStefan Berger /* clearing of interrupt flags */ 664edff8678SStefan Berger if (((val & TPM_TIS_INTERRUPTS_SUPPORTED)) && 6653d4960c7SMarc-André Lureau (s->loc[locty].ints & TPM_TIS_INTERRUPTS_SUPPORTED)) { 6663d4960c7SMarc-André Lureau s->loc[locty].ints &= ~val; 6673d4960c7SMarc-André Lureau if (s->loc[locty].ints == 0) { 6683d4960c7SMarc-André Lureau qemu_irq_lower(s->irq); 669fcbed221SStefan Berger trace_tpm_tis_mmio_write_lowering_irq(); 670edff8678SStefan Berger } 671edff8678SStefan Berger } 6723d4960c7SMarc-André Lureau s->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED); 673edff8678SStefan Berger break; 674edff8678SStefan Berger case TPM_TIS_REG_STS: 6753d4960c7SMarc-André Lureau if (s->active_locty != locty) { 676edff8678SStefan Berger break; 677edff8678SStefan Berger } 678edff8678SStefan Berger 679116694c3SStefan Berger if (s->be_tpm_version == TPM_VERSION_2_0) { 680116694c3SStefan Berger /* some flags that are only supported for TPM 2 */ 681116694c3SStefan Berger if (val & TPM_TIS_STS_COMMAND_CANCEL) { 6823d4960c7SMarc-André Lureau if (s->loc[locty].state == TPM_TIS_STATE_EXECUTION) { 683116694c3SStefan Berger /* 684116694c3SStefan Berger * request the backend to cancel. Some backends may not 685116694c3SStefan Berger * support it 686116694c3SStefan Berger */ 687116694c3SStefan Berger tpm_backend_cancel_cmd(s->be_driver); 688116694c3SStefan Berger } 689116694c3SStefan Berger } 690116694c3SStefan Berger 691116694c3SStefan Berger if (val & TPM_TIS_STS_RESET_ESTABLISHMENT_BIT) { 692116694c3SStefan Berger if (locty == 3 || locty == 4) { 693116694c3SStefan Berger tpm_backend_reset_tpm_established_flag(s->be_driver, locty); 694116694c3SStefan Berger } 695116694c3SStefan Berger } 696116694c3SStefan Berger } 697116694c3SStefan Berger 698edff8678SStefan Berger val &= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO | 699edff8678SStefan Berger TPM_TIS_STS_RESPONSE_RETRY); 700edff8678SStefan Berger 701edff8678SStefan Berger if (val == TPM_TIS_STS_COMMAND_READY) { 7023d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 703edff8678SStefan Berger 704edff8678SStefan Berger case TPM_TIS_STATE_READY: 705f999d81bSStefan Berger s->rw_offset = 0; 706edff8678SStefan Berger break; 707edff8678SStefan Berger 708edff8678SStefan Berger case TPM_TIS_STATE_IDLE: 7093d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_COMMAND_READY); 7103d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_READY; 711edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); 712edff8678SStefan Berger break; 713edff8678SStefan Berger 714edff8678SStefan Berger case TPM_TIS_STATE_EXECUTION: 715edff8678SStefan Berger case TPM_TIS_STATE_RECEPTION: 716edff8678SStefan Berger /* abort currently running command */ 717fcbed221SStefan Berger trace_tpm_tis_mmio_write_init_abort(); 718edff8678SStefan Berger tpm_tis_prep_abort(s, locty, locty); 719edff8678SStefan Berger break; 720edff8678SStefan Berger 721edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 722f999d81bSStefan Berger s->rw_offset = 0; 723edff8678SStefan Berger /* shortcut to ready state with C/R set */ 7243d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_READY; 7253d4960c7SMarc-André Lureau if (!(s->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) { 7263d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 727fd859081SStefan Berger TPM_TIS_STS_COMMAND_READY); 728edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); 729edff8678SStefan Berger } 7303d4960c7SMarc-André Lureau s->loc[locty].sts &= ~(TPM_TIS_STS_DATA_AVAILABLE); 731edff8678SStefan Berger break; 732edff8678SStefan Berger 733edff8678SStefan Berger } 734edff8678SStefan Berger } else if (val == TPM_TIS_STS_TPM_GO) { 7353d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 736edff8678SStefan Berger case TPM_TIS_STATE_RECEPTION: 7373d4960c7SMarc-André Lureau if ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) == 0) { 738edff8678SStefan Berger tpm_tis_tpm_send(s, locty); 739edff8678SStefan Berger } 740edff8678SStefan Berger break; 741edff8678SStefan Berger default: 742edff8678SStefan Berger /* ignore */ 743edff8678SStefan Berger break; 744edff8678SStefan Berger } 745edff8678SStefan Berger } else if (val == TPM_TIS_STS_RESPONSE_RETRY) { 7463d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 747edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 748f999d81bSStefan Berger s->rw_offset = 0; 7493d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 750fd859081SStefan Berger TPM_TIS_STS_VALID| 751fd859081SStefan Berger TPM_TIS_STS_DATA_AVAILABLE); 752edff8678SStefan Berger break; 753edff8678SStefan Berger default: 754edff8678SStefan Berger /* ignore */ 755edff8678SStefan Berger break; 756edff8678SStefan Berger } 757edff8678SStefan Berger } 758edff8678SStefan Berger break; 759edff8678SStefan Berger case TPM_TIS_REG_DATA_FIFO: 7602eae8c75SStefan Berger case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END: 761edff8678SStefan Berger /* data fifo */ 7623d4960c7SMarc-André Lureau if (s->active_locty != locty) { 763edff8678SStefan Berger break; 764edff8678SStefan Berger } 765edff8678SStefan Berger 7663d4960c7SMarc-André Lureau if (s->loc[locty].state == TPM_TIS_STATE_IDLE || 7673d4960c7SMarc-André Lureau s->loc[locty].state == TPM_TIS_STATE_EXECUTION || 7683d4960c7SMarc-André Lureau s->loc[locty].state == TPM_TIS_STATE_COMPLETION) { 769edff8678SStefan Berger /* drop the byte */ 770edff8678SStefan Berger } else { 771fcbed221SStefan Berger trace_tpm_tis_mmio_write_data2send(val, size); 7723d4960c7SMarc-André Lureau if (s->loc[locty].state == TPM_TIS_STATE_READY) { 7733d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_RECEPTION; 7743d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 775fd859081SStefan Berger TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); 776edff8678SStefan Berger } 777edff8678SStefan Berger 778feeb755fSStefan Berger val >>= shift; 779feeb755fSStefan Berger if (size > 4 - (addr & 0x3)) { 780feeb755fSStefan Berger /* prevent access beyond FIFO */ 781feeb755fSStefan Berger size = 4 - (addr & 0x3); 782feeb755fSStefan Berger } 783feeb755fSStefan Berger 7843d4960c7SMarc-André Lureau while ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) && size > 0) { 785f999d81bSStefan Berger if (s->rw_offset < s->be_buffer_size) { 786f999d81bSStefan Berger s->buffer[s->rw_offset++] = 787e6b703f6SStefan Berger (uint8_t)val; 788feeb755fSStefan Berger val >>= 8; 789feeb755fSStefan Berger size--; 790edff8678SStefan Berger } else { 7913d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 792edff8678SStefan Berger } 793edff8678SStefan Berger } 794edff8678SStefan Berger 795edff8678SStefan Berger /* check for complete packet */ 796f999d81bSStefan Berger if (s->rw_offset > 5 && 7973d4960c7SMarc-André Lureau (s->loc[locty].sts & TPM_TIS_STS_EXPECT)) { 798edff8678SStefan Berger /* we have a packet length - see if we have all of it */ 7993d4960c7SMarc-André Lureau bool need_irq = !(s->loc[locty].sts & TPM_TIS_STS_VALID); 800d8383d61SMarc-André Lureau 801c5496b97SStefan Berger len = tpm_cmd_get_size(&s->buffer); 802f999d81bSStefan Berger if (len > s->rw_offset) { 8033d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 804fd859081SStefan Berger TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); 805edff8678SStefan Berger } else { 806edff8678SStefan Berger /* packet complete */ 8073d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 808edff8678SStefan Berger } 80929b558d8SStefan Berger if (need_irq) { 810edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); 811edff8678SStefan Berger } 812edff8678SStefan Berger } 813edff8678SStefan Berger } 814edff8678SStefan Berger break; 815116694c3SStefan Berger case TPM_TIS_REG_INTERFACE_ID: 816116694c3SStefan Berger if (val & TPM_TIS_IFACE_ID_INT_SEL_LOCK) { 817116694c3SStefan Berger for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 8183d4960c7SMarc-André Lureau s->loc[l].iface_id |= TPM_TIS_IFACE_ID_INT_SEL_LOCK; 819116694c3SStefan Berger } 820116694c3SStefan Berger } 821116694c3SStefan Berger break; 822edff8678SStefan Berger } 823edff8678SStefan Berger } 824edff8678SStefan Berger 825edff8678SStefan Berger static const MemoryRegionOps tpm_tis_memory_ops = { 826edff8678SStefan Berger .read = tpm_tis_mmio_read, 827edff8678SStefan Berger .write = tpm_tis_mmio_write, 828edff8678SStefan Berger .endianness = DEVICE_LITTLE_ENDIAN, 829edff8678SStefan Berger .valid = { 830edff8678SStefan Berger .min_access_size = 1, 831edff8678SStefan Berger .max_access_size = 4, 832edff8678SStefan Berger }, 833edff8678SStefan Berger }; 834edff8678SStefan Berger 835edff8678SStefan Berger /* 8365cb18b3dSStefan Berger * Get the TPMVersion of the backend device being used 8375cb18b3dSStefan Berger */ 8389af7a721SMarc-André Lureau static enum TPMVersion tpm_tis_get_tpm_version(TPMIf *ti) 8395cb18b3dSStefan Berger { 8409af7a721SMarc-André Lureau TPMState *s = TPM(ti); 8415cb18b3dSStefan Berger 842ad4aca69SStefan Berger if (tpm_backend_had_startup_error(s->be_driver)) { 843ad4aca69SStefan Berger return TPM_VERSION_UNSPEC; 844ad4aca69SStefan Berger } 845ad4aca69SStefan Berger 8465cb18b3dSStefan Berger return tpm_backend_get_tpm_version(s->be_driver); 8475cb18b3dSStefan Berger } 8485cb18b3dSStefan Berger 8495cb18b3dSStefan Berger /* 850edff8678SStefan Berger * This function is called when the machine starts, resets or due to 851edff8678SStefan Berger * S3 resume. 852edff8678SStefan Berger */ 853edff8678SStefan Berger static void tpm_tis_reset(DeviceState *dev) 854edff8678SStefan Berger { 855edff8678SStefan Berger TPMState *s = TPM(dev); 856edff8678SStefan Berger int c; 857edff8678SStefan Berger 858116694c3SStefan Berger s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver); 8591af3d63eSStefan Berger s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->be_driver), 8601af3d63eSStefan Berger TPM_TIS_BUFFER_MAX); 861116694c3SStefan Berger 862ffab1be7SMarc-André Lureau if (s->ppi_enabled) { 863ffab1be7SMarc-André Lureau tpm_ppi_reset(&s->ppi); 864ffab1be7SMarc-André Lureau } 8658f0605ccSStefan Berger tpm_backend_reset(s->be_driver); 866edff8678SStefan Berger 8673d4960c7SMarc-André Lureau s->active_locty = TPM_TIS_NO_LOCALITY; 8683d4960c7SMarc-André Lureau s->next_locty = TPM_TIS_NO_LOCALITY; 8693d4960c7SMarc-André Lureau s->aborting_locty = TPM_TIS_NO_LOCALITY; 870edff8678SStefan Berger 871edff8678SStefan Berger for (c = 0; c < TPM_TIS_NUM_LOCALITIES; c++) { 8723d4960c7SMarc-André Lureau s->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS; 873116694c3SStefan Berger switch (s->be_tpm_version) { 874116694c3SStefan Berger case TPM_VERSION_UNSPEC: 875116694c3SStefan Berger break; 876116694c3SStefan Berger case TPM_VERSION_1_2: 8773d4960c7SMarc-André Lureau s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY1_2; 8783d4960c7SMarc-André Lureau s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3; 879116694c3SStefan Berger break; 880116694c3SStefan Berger case TPM_VERSION_2_0: 8813d4960c7SMarc-André Lureau s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY2_0; 8823d4960c7SMarc-André Lureau s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0; 883116694c3SStefan Berger break; 884116694c3SStefan Berger } 8853d4960c7SMarc-André Lureau s->loc[c].inte = TPM_TIS_INT_POLARITY_LOW_LEVEL; 8863d4960c7SMarc-André Lureau s->loc[c].ints = 0; 8873d4960c7SMarc-André Lureau s->loc[c].state = TPM_TIS_STATE_IDLE; 888edff8678SStefan Berger 889f999d81bSStefan Berger s->rw_offset = 0; 890edff8678SStefan Berger } 891edff8678SStefan Berger 892bcfd16feSStefan Berger if (tpm_backend_startup_tpm(s->be_driver, s->be_buffer_size) < 0) { 893bcfd16feSStefan Berger exit(1); 894bcfd16feSStefan Berger } 895edff8678SStefan Berger } 896edff8678SStefan Berger 8979ec08c48SStefan Berger /* persistent state handling */ 8989ec08c48SStefan Berger 8999ec08c48SStefan Berger static int tpm_tis_pre_save(void *opaque) 9009ec08c48SStefan Berger { 9019ec08c48SStefan Berger TPMState *s = opaque; 9029ec08c48SStefan Berger uint8_t locty = s->active_locty; 9039ec08c48SStefan Berger 9049ec08c48SStefan Berger trace_tpm_tis_pre_save(locty, s->rw_offset); 9059ec08c48SStefan Berger 9069ec08c48SStefan Berger if (DEBUG_TIS) { 9079ec08c48SStefan Berger tpm_tis_dump_state(opaque, 0); 9089ec08c48SStefan Berger } 9099ec08c48SStefan Berger 9109ec08c48SStefan Berger /* 9119ec08c48SStefan Berger * Synchronize with backend completion. 9129ec08c48SStefan Berger */ 9139ec08c48SStefan Berger tpm_backend_finish_sync(s->be_driver); 9149ec08c48SStefan Berger 9159ec08c48SStefan Berger return 0; 9169ec08c48SStefan Berger } 9179ec08c48SStefan Berger 9189ec08c48SStefan Berger static const VMStateDescription vmstate_locty = { 9199ec08c48SStefan Berger .name = "tpm-tis/locty", 9209ec08c48SStefan Berger .version_id = 0, 9219ec08c48SStefan Berger .fields = (VMStateField[]) { 9229ec08c48SStefan Berger VMSTATE_UINT32(state, TPMLocality), 9239ec08c48SStefan Berger VMSTATE_UINT32(inte, TPMLocality), 9249ec08c48SStefan Berger VMSTATE_UINT32(ints, TPMLocality), 9259ec08c48SStefan Berger VMSTATE_UINT8(access, TPMLocality), 9269ec08c48SStefan Berger VMSTATE_UINT32(sts, TPMLocality), 9279ec08c48SStefan Berger VMSTATE_UINT32(iface_id, TPMLocality), 9289ec08c48SStefan Berger VMSTATE_END_OF_LIST(), 9299ec08c48SStefan Berger } 9309ec08c48SStefan Berger }; 9319ec08c48SStefan Berger 932edff8678SStefan Berger static const VMStateDescription vmstate_tpm_tis = { 9339ec08c48SStefan Berger .name = "tpm-tis", 9349ec08c48SStefan Berger .version_id = 0, 9359ec08c48SStefan Berger .pre_save = tpm_tis_pre_save, 9369ec08c48SStefan Berger .fields = (VMStateField[]) { 9379ec08c48SStefan Berger VMSTATE_BUFFER(buffer, TPMState), 9389ec08c48SStefan Berger VMSTATE_UINT16(rw_offset, TPMState), 9399ec08c48SStefan Berger VMSTATE_UINT8(active_locty, TPMState), 9409ec08c48SStefan Berger VMSTATE_UINT8(aborting_locty, TPMState), 9419ec08c48SStefan Berger VMSTATE_UINT8(next_locty, TPMState), 9429ec08c48SStefan Berger 9439ec08c48SStefan Berger VMSTATE_STRUCT_ARRAY(loc, TPMState, TPM_TIS_NUM_LOCALITIES, 0, 9449ec08c48SStefan Berger vmstate_locty, TPMLocality), 9459ec08c48SStefan Berger 9469ec08c48SStefan Berger VMSTATE_END_OF_LIST() 9479ec08c48SStefan Berger } 948edff8678SStefan Berger }; 949edff8678SStefan Berger 950edff8678SStefan Berger static Property tpm_tis_properties[] = { 9513d4960c7SMarc-André Lureau DEFINE_PROP_UINT32("irq", TPMState, irq_num, TPM_TIS_IRQ), 952c0378544SMarc-André Lureau DEFINE_PROP_TPMBE("tpmdev", TPMState, be_driver), 953b6148757SMarc-André Lureau DEFINE_PROP_BOOL("ppi", TPMState, ppi_enabled, true), 954edff8678SStefan Berger DEFINE_PROP_END_OF_LIST(), 955edff8678SStefan Berger }; 956edff8678SStefan Berger 957edff8678SStefan Berger static void tpm_tis_realizefn(DeviceState *dev, Error **errp) 958edff8678SStefan Berger { 959edff8678SStefan Berger TPMState *s = TPM(dev); 960edff8678SStefan Berger 96151a837e9SMarc-André Lureau if (!tpm_find()) { 96251a837e9SMarc-André Lureau error_setg(errp, "at most one TPM device is permitted"); 96351a837e9SMarc-André Lureau return; 96451a837e9SMarc-André Lureau } 96551a837e9SMarc-André Lureau 966edff8678SStefan Berger if (!s->be_driver) { 967c0378544SMarc-André Lureau error_setg(errp, "'tpmdev' property is required"); 968edff8678SStefan Berger return; 969edff8678SStefan Berger } 9703d4960c7SMarc-André Lureau if (s->irq_num > 15) { 971c87b35faSMarc-André Lureau error_setg(errp, "IRQ %d is outside valid range of 0 to 15", 972c87b35faSMarc-André Lureau s->irq_num); 973edff8678SStefan Berger return; 974edff8678SStefan Berger } 975edff8678SStefan Berger 9763d4960c7SMarc-André Lureau isa_init_irq(&s->busdev, &s->irq, s->irq_num); 9779dfd24edSStefan Berger 9789dfd24edSStefan Berger memory_region_add_subregion(isa_address_space(ISA_DEVICE(dev)), 9799dfd24edSStefan Berger TPM_TIS_ADDR_BASE, &s->mmio); 9803b97c01eSStefan Berger 9813b97c01eSStefan Berger if (s->ppi_enabled) { 9823b97c01eSStefan Berger tpm_ppi_init(&s->ppi, isa_address_space(ISA_DEVICE(dev)), 9833b97c01eSStefan Berger TPM_PPI_ADDR_BASE, OBJECT(s)); 9843b97c01eSStefan Berger } 985edff8678SStefan Berger } 986edff8678SStefan Berger 987edff8678SStefan Berger static void tpm_tis_initfn(Object *obj) 988edff8678SStefan Berger { 989edff8678SStefan Berger TPMState *s = TPM(obj); 990edff8678SStefan Berger 991853dca12SPaolo Bonzini memory_region_init_io(&s->mmio, OBJECT(s), &tpm_tis_memory_ops, 992853dca12SPaolo Bonzini s, "tpm-tis-mmio", 993edff8678SStefan Berger TPM_TIS_NUM_LOCALITIES << TPM_TIS_LOCALITY_SHIFT); 994edff8678SStefan Berger } 995edff8678SStefan Berger 996edff8678SStefan Berger static void tpm_tis_class_init(ObjectClass *klass, void *data) 997edff8678SStefan Berger { 998edff8678SStefan Berger DeviceClass *dc = DEVICE_CLASS(klass); 99905a69998SMarc-André Lureau TPMIfClass *tc = TPM_IF_CLASS(klass); 1000edff8678SStefan Berger 1001edff8678SStefan Berger dc->realize = tpm_tis_realizefn; 10024f67d30bSMarc-André Lureau device_class_set_props(dc, tpm_tis_properties); 1003edff8678SStefan Berger dc->reset = tpm_tis_reset; 1004edff8678SStefan Berger dc->vmsd = &vmstate_tpm_tis; 1005191adc94SMarc-André Lureau tc->model = TPM_MODEL_TPM_TIS; 10069af7a721SMarc-André Lureau tc->get_version = tpm_tis_get_tpm_version; 100705a69998SMarc-André Lureau tc->request_completed = tpm_tis_request_completed; 1008edff8678SStefan Berger } 1009edff8678SStefan Berger 1010edff8678SStefan Berger static const TypeInfo tpm_tis_info = { 1011edff8678SStefan Berger .name = TYPE_TPM_TIS, 1012edff8678SStefan Berger .parent = TYPE_ISA_DEVICE, 1013edff8678SStefan Berger .instance_size = sizeof(TPMState), 1014edff8678SStefan Berger .instance_init = tpm_tis_initfn, 1015edff8678SStefan Berger .class_init = tpm_tis_class_init, 1016698f5daaSMarc-André Lureau .interfaces = (InterfaceInfo[]) { 1017698f5daaSMarc-André Lureau { TYPE_TPM_IF }, 1018698f5daaSMarc-André Lureau { } 1019698f5daaSMarc-André Lureau } 1020edff8678SStefan Berger }; 1021edff8678SStefan Berger 1022edff8678SStefan Berger static void tpm_tis_register(void) 1023edff8678SStefan Berger { 1024edff8678SStefan Berger type_register_static(&tpm_tis_info); 1025edff8678SStefan Berger } 1026edff8678SStefan Berger 1027edff8678SStefan Berger type_init(tpm_tis_register) 1028