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" 26dccfcd0eSPaolo Bonzini #include "sysemu/tpm_backend.h" 27edff8678SStefan Berger #include "tpm_int.h" 284be74634SMarkus Armbruster #include "sysemu/block-backend.h" 29edff8678SStefan Berger #include "exec/address-spaces.h" 30edff8678SStefan Berger #include "hw/hw.h" 310d09e41aSPaolo Bonzini #include "hw/i386/pc.h" 32edff8678SStefan Berger #include "hw/pci/pci_ids.h" 33bdee56f5SPaolo Bonzini #include "tpm_tis.h" 34da34e65cSMarkus Armbruster #include "qapi/error.h" 35edff8678SStefan Berger #include "qemu-common.h" 366a1751b7SAlex Bligh #include "qemu/main-loop.h" 37edff8678SStefan Berger 384d1ba9c4SStefan Berger #define DEBUG_TIS 0 39edff8678SStefan Berger 404d1ba9c4SStefan Berger #define DPRINTF(fmt, ...) do { \ 414d1ba9c4SStefan Berger if (DEBUG_TIS) { \ 424d1ba9c4SStefan Berger printf(fmt, ## __VA_ARGS__); \ 434d1ba9c4SStefan Berger } \ 444d1ba9c4SStefan Berger } while (0); 45edff8678SStefan Berger 46edff8678SStefan Berger /* tis registers */ 47edff8678SStefan Berger #define TPM_TIS_REG_ACCESS 0x00 48edff8678SStefan Berger #define TPM_TIS_REG_INT_ENABLE 0x08 49edff8678SStefan Berger #define TPM_TIS_REG_INT_VECTOR 0x0c 50edff8678SStefan Berger #define TPM_TIS_REG_INT_STATUS 0x10 51edff8678SStefan Berger #define TPM_TIS_REG_INTF_CAPABILITY 0x14 52edff8678SStefan Berger #define TPM_TIS_REG_STS 0x18 53edff8678SStefan Berger #define TPM_TIS_REG_DATA_FIFO 0x24 54116694c3SStefan Berger #define TPM_TIS_REG_INTERFACE_ID 0x30 552eae8c75SStefan Berger #define TPM_TIS_REG_DATA_XFIFO 0x80 562eae8c75SStefan Berger #define TPM_TIS_REG_DATA_XFIFO_END 0xbc 57edff8678SStefan Berger #define TPM_TIS_REG_DID_VID 0xf00 58edff8678SStefan Berger #define TPM_TIS_REG_RID 0xf04 59edff8678SStefan Berger 608db7c415SStefan Berger /* vendor-specific registers */ 618db7c415SStefan Berger #define TPM_TIS_REG_DEBUG 0xf90 628db7c415SStefan Berger 63116694c3SStefan Berger #define TPM_TIS_STS_TPM_FAMILY_MASK (0x3 << 26)/* TPM 2.0 */ 64116694c3SStefan Berger #define TPM_TIS_STS_TPM_FAMILY1_2 (0 << 26) /* TPM 2.0 */ 65116694c3SStefan Berger #define TPM_TIS_STS_TPM_FAMILY2_0 (1 << 26) /* TPM 2.0 */ 66116694c3SStefan Berger #define TPM_TIS_STS_RESET_ESTABLISHMENT_BIT (1 << 25) /* TPM 2.0 */ 67116694c3SStefan Berger #define TPM_TIS_STS_COMMAND_CANCEL (1 << 24) /* TPM 2.0 */ 68116694c3SStefan Berger 69edff8678SStefan Berger #define TPM_TIS_STS_VALID (1 << 7) 70edff8678SStefan Berger #define TPM_TIS_STS_COMMAND_READY (1 << 6) 71edff8678SStefan Berger #define TPM_TIS_STS_TPM_GO (1 << 5) 72edff8678SStefan Berger #define TPM_TIS_STS_DATA_AVAILABLE (1 << 4) 73edff8678SStefan Berger #define TPM_TIS_STS_EXPECT (1 << 3) 74fd859081SStefan Berger #define TPM_TIS_STS_SELFTEST_DONE (1 << 2) 75edff8678SStefan Berger #define TPM_TIS_STS_RESPONSE_RETRY (1 << 1) 76edff8678SStefan Berger 77edff8678SStefan Berger #define TPM_TIS_BURST_COUNT_SHIFT 8 78edff8678SStefan Berger #define TPM_TIS_BURST_COUNT(X) \ 79edff8678SStefan Berger ((X) << TPM_TIS_BURST_COUNT_SHIFT) 80edff8678SStefan Berger 81edff8678SStefan Berger #define TPM_TIS_ACCESS_TPM_REG_VALID_STS (1 << 7) 82edff8678SStefan Berger #define TPM_TIS_ACCESS_ACTIVE_LOCALITY (1 << 5) 83edff8678SStefan Berger #define TPM_TIS_ACCESS_BEEN_SEIZED (1 << 4) 84edff8678SStefan Berger #define TPM_TIS_ACCESS_SEIZE (1 << 3) 85edff8678SStefan Berger #define TPM_TIS_ACCESS_PENDING_REQUEST (1 << 2) 86edff8678SStefan Berger #define TPM_TIS_ACCESS_REQUEST_USE (1 << 1) 87edff8678SStefan Berger #define TPM_TIS_ACCESS_TPM_ESTABLISHMENT (1 << 0) 88edff8678SStefan Berger 89edff8678SStefan Berger #define TPM_TIS_INT_ENABLED (1 << 31) 90edff8678SStefan Berger #define TPM_TIS_INT_DATA_AVAILABLE (1 << 0) 91edff8678SStefan Berger #define TPM_TIS_INT_STS_VALID (1 << 1) 92edff8678SStefan Berger #define TPM_TIS_INT_LOCALITY_CHANGED (1 << 2) 93edff8678SStefan Berger #define TPM_TIS_INT_COMMAND_READY (1 << 7) 94edff8678SStefan Berger 95edff8678SStefan Berger #define TPM_TIS_INT_POLARITY_MASK (3 << 3) 96edff8678SStefan Berger #define TPM_TIS_INT_POLARITY_LOW_LEVEL (1 << 3) 97edff8678SStefan Berger 98edff8678SStefan Berger #define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \ 99edff8678SStefan Berger TPM_TIS_INT_DATA_AVAILABLE | \ 100edff8678SStefan Berger TPM_TIS_INT_STS_VALID | \ 101edff8678SStefan Berger TPM_TIS_INT_COMMAND_READY) 102edff8678SStefan Berger 1039dd5c40dSStefan Berger #define TPM_TIS_CAP_INTERFACE_VERSION1_3 (2 << 28) 104116694c3SStefan Berger #define TPM_TIS_CAP_INTERFACE_VERSION1_3_FOR_TPM2_0 (3 << 28) 1059dd5c40dSStefan Berger #define TPM_TIS_CAP_DATA_TRANSFER_64B (3 << 9) 1069dd5c40dSStefan Berger #define TPM_TIS_CAP_DATA_TRANSFER_LEGACY (0 << 9) 1079dd5c40dSStefan Berger #define TPM_TIS_CAP_BURST_COUNT_DYNAMIC (0 << 8) 108edff8678SStefan Berger #define TPM_TIS_CAP_INTERRUPT_LOW_LEVEL (1 << 4) /* support is mandatory */ 109116694c3SStefan Berger #define TPM_TIS_CAPABILITIES_SUPPORTED1_3 \ 110116694c3SStefan Berger (TPM_TIS_CAP_INTERRUPT_LOW_LEVEL | \ 1119dd5c40dSStefan Berger TPM_TIS_CAP_BURST_COUNT_DYNAMIC | \ 1129dd5c40dSStefan Berger TPM_TIS_CAP_DATA_TRANSFER_64B | \ 1139dd5c40dSStefan Berger TPM_TIS_CAP_INTERFACE_VERSION1_3 | \ 114edff8678SStefan Berger TPM_TIS_INTERRUPTS_SUPPORTED) 115edff8678SStefan Berger 116116694c3SStefan Berger #define TPM_TIS_CAPABILITIES_SUPPORTED2_0 \ 117116694c3SStefan Berger (TPM_TIS_CAP_INTERRUPT_LOW_LEVEL | \ 118116694c3SStefan Berger TPM_TIS_CAP_BURST_COUNT_DYNAMIC | \ 119116694c3SStefan Berger TPM_TIS_CAP_DATA_TRANSFER_64B | \ 120116694c3SStefan Berger TPM_TIS_CAP_INTERFACE_VERSION1_3_FOR_TPM2_0 | \ 121116694c3SStefan Berger TPM_TIS_INTERRUPTS_SUPPORTED) 122116694c3SStefan Berger 123116694c3SStefan Berger #define TPM_TIS_IFACE_ID_INTERFACE_TIS1_3 (0xf) /* TPM 2.0 */ 124116694c3SStefan Berger #define TPM_TIS_IFACE_ID_INTERFACE_FIFO (0x0) /* TPM 2.0 */ 125116694c3SStefan Berger #define TPM_TIS_IFACE_ID_INTERFACE_VER_FIFO (0 << 4) /* TPM 2.0 */ 126116694c3SStefan Berger #define TPM_TIS_IFACE_ID_CAP_5_LOCALITIES (1 << 8) /* TPM 2.0 */ 127116694c3SStefan Berger #define TPM_TIS_IFACE_ID_CAP_TIS_SUPPORTED (1 << 13) /* TPM 2.0 */ 128116694c3SStefan Berger #define TPM_TIS_IFACE_ID_INT_SEL_LOCK (1 << 19) /* TPM 2.0 */ 129116694c3SStefan Berger 130116694c3SStefan Berger #define TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3 \ 131116694c3SStefan Berger (TPM_TIS_IFACE_ID_INTERFACE_TIS1_3 | \ 132886ce6f8SStefan Hajnoczi (~0u << 4)/* all of it is don't care */) 133116694c3SStefan Berger 134116694c3SStefan Berger /* if backend was a TPM 2.0: */ 135116694c3SStefan Berger #define TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0 \ 136116694c3SStefan Berger (TPM_TIS_IFACE_ID_INTERFACE_FIFO | \ 137116694c3SStefan Berger TPM_TIS_IFACE_ID_INTERFACE_VER_FIFO | \ 138116694c3SStefan Berger TPM_TIS_IFACE_ID_CAP_5_LOCALITIES | \ 139116694c3SStefan Berger TPM_TIS_IFACE_ID_CAP_TIS_SUPPORTED) 140116694c3SStefan Berger 141edff8678SStefan Berger #define TPM_TIS_TPM_DID 0x0001 142edff8678SStefan Berger #define TPM_TIS_TPM_VID PCI_VENDOR_ID_IBM 143edff8678SStefan Berger #define TPM_TIS_TPM_RID 0x0001 144edff8678SStefan Berger 145edff8678SStefan Berger #define TPM_TIS_NO_DATA_BYTE 0xff 146edff8678SStefan Berger 1478db7c415SStefan Berger /* local prototypes */ 1488db7c415SStefan Berger 1498db7c415SStefan Berger static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, 1508db7c415SStefan Berger unsigned size); 1518db7c415SStefan Berger 152edff8678SStefan Berger /* utility functions */ 153edff8678SStefan Berger 154edff8678SStefan Berger static uint8_t tpm_tis_locality_from_addr(hwaddr addr) 155edff8678SStefan Berger { 156edff8678SStefan Berger return (uint8_t)((addr >> TPM_TIS_LOCALITY_SHIFT) & 0x7); 157edff8678SStefan Berger } 158edff8678SStefan Berger 159edff8678SStefan Berger static uint32_t tpm_tis_get_size_from_buffer(const TPMSizedBuffer *sb) 160edff8678SStefan Berger { 161edff8678SStefan Berger return be32_to_cpu(*(uint32_t *)&sb->buffer[2]); 162edff8678SStefan Berger } 163edff8678SStefan Berger 164edff8678SStefan Berger static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string) 165edff8678SStefan Berger { 166edff8678SStefan Berger #ifdef DEBUG_TIS 167edff8678SStefan Berger uint32_t len, i; 168edff8678SStefan Berger 169edff8678SStefan Berger len = tpm_tis_get_size_from_buffer(sb); 170edff8678SStefan Berger DPRINTF("tpm_tis: %s length = %d\n", string, len); 171edff8678SStefan Berger for (i = 0; i < len; i++) { 172edff8678SStefan Berger if (i && !(i % 16)) { 173edff8678SStefan Berger DPRINTF("\n"); 174edff8678SStefan Berger } 175edff8678SStefan Berger DPRINTF("%.2X ", sb->buffer[i]); 176edff8678SStefan Berger } 177edff8678SStefan Berger DPRINTF("\n"); 178edff8678SStefan Berger #endif 179edff8678SStefan Berger } 180edff8678SStefan Berger 181edff8678SStefan Berger /* 182fd859081SStefan Berger * Set the given flags in the STS register by clearing the register but 183116694c3SStefan Berger * preserving the SELFTEST_DONE and TPM_FAMILY_MASK flags and then setting 184116694c3SStefan Berger * the new flags. 185fd859081SStefan Berger * 186fd859081SStefan Berger * The SELFTEST_DONE flag is acquired from the backend that determines it by 187fd859081SStefan Berger * peeking into TPM commands. 188fd859081SStefan Berger * 189fd859081SStefan Berger * A VM suspend/resume will preserve the flag by storing it into the VM 190fd859081SStefan Berger * device state, but the backend will not remember it when QEMU is started 191fd859081SStefan Berger * again. Therefore, we cache the flag here. Once set, it will not be unset 192fd859081SStefan Berger * except by a reset. 193fd859081SStefan Berger */ 194fd859081SStefan Berger static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags) 195fd859081SStefan Berger { 196116694c3SStefan Berger l->sts &= TPM_TIS_STS_SELFTEST_DONE | TPM_TIS_STS_TPM_FAMILY_MASK; 197fd859081SStefan Berger l->sts |= flags; 198fd859081SStefan Berger } 199fd859081SStefan Berger 200fd859081SStefan Berger /* 201edff8678SStefan Berger * Send a request to the TPM. 202edff8678SStefan Berger */ 203edff8678SStefan Berger static void tpm_tis_tpm_send(TPMState *s, uint8_t locty) 204edff8678SStefan Berger { 205edff8678SStefan Berger TPMTISEmuState *tis = &s->s.tis; 206edff8678SStefan Berger 207edff8678SStefan Berger tpm_tis_show_buffer(&tis->loc[locty].w_buffer, "tpm_tis: To TPM"); 208edff8678SStefan Berger 209edff8678SStefan Berger s->locty_number = locty; 210edff8678SStefan Berger s->locty_data = &tis->loc[locty]; 211edff8678SStefan Berger 212edff8678SStefan Berger /* 213edff8678SStefan Berger * w_offset serves as length indicator for length of data; 214edff8678SStefan Berger * it's reset when the response comes back 215edff8678SStefan Berger */ 216edff8678SStefan Berger tis->loc[locty].state = TPM_TIS_STATE_EXECUTION; 217edff8678SStefan Berger 2188f0605ccSStefan Berger tpm_backend_deliver_request(s->be_driver); 219edff8678SStefan Berger } 220edff8678SStefan Berger 221edff8678SStefan Berger /* raise an interrupt if allowed */ 222edff8678SStefan Berger static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask) 223edff8678SStefan Berger { 224edff8678SStefan Berger TPMTISEmuState *tis = &s->s.tis; 225edff8678SStefan Berger 226edff8678SStefan Berger if (!TPM_TIS_IS_VALID_LOCTY(locty)) { 227edff8678SStefan Berger return; 228edff8678SStefan Berger } 229edff8678SStefan Berger 230edff8678SStefan Berger if ((tis->loc[locty].inte & TPM_TIS_INT_ENABLED) && 231edff8678SStefan Berger (tis->loc[locty].inte & irqmask)) { 232edff8678SStefan Berger DPRINTF("tpm_tis: Raising IRQ for flag %08x\n", irqmask); 233edff8678SStefan Berger qemu_irq_raise(s->s.tis.irq); 234edff8678SStefan Berger tis->loc[locty].ints |= irqmask; 235edff8678SStefan Berger } 236edff8678SStefan Berger } 237edff8678SStefan Berger 238edff8678SStefan Berger static uint32_t tpm_tis_check_request_use_except(TPMState *s, uint8_t locty) 239edff8678SStefan Berger { 240edff8678SStefan Berger uint8_t l; 241edff8678SStefan Berger 242edff8678SStefan Berger for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 243edff8678SStefan Berger if (l == locty) { 244edff8678SStefan Berger continue; 245edff8678SStefan Berger } 246edff8678SStefan Berger if ((s->s.tis.loc[l].access & TPM_TIS_ACCESS_REQUEST_USE)) { 247edff8678SStefan Berger return 1; 248edff8678SStefan Berger } 249edff8678SStefan Berger } 250edff8678SStefan Berger 251edff8678SStefan Berger return 0; 252edff8678SStefan Berger } 253edff8678SStefan Berger 254edff8678SStefan Berger static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty) 255edff8678SStefan Berger { 256edff8678SStefan Berger TPMTISEmuState *tis = &s->s.tis; 257edff8678SStefan Berger bool change = (s->s.tis.active_locty != new_active_locty); 258edff8678SStefan Berger bool is_seize; 259edff8678SStefan Berger uint8_t mask; 260edff8678SStefan Berger 261edff8678SStefan Berger if (change && TPM_TIS_IS_VALID_LOCTY(s->s.tis.active_locty)) { 262edff8678SStefan Berger is_seize = TPM_TIS_IS_VALID_LOCTY(new_active_locty) && 263edff8678SStefan Berger tis->loc[new_active_locty].access & TPM_TIS_ACCESS_SEIZE; 264edff8678SStefan Berger 265edff8678SStefan Berger if (is_seize) { 266edff8678SStefan Berger mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY); 267edff8678SStefan Berger } else { 268edff8678SStefan Berger mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY| 269edff8678SStefan Berger TPM_TIS_ACCESS_REQUEST_USE); 270edff8678SStefan Berger } 271edff8678SStefan Berger /* reset flags on the old active locality */ 272edff8678SStefan Berger tis->loc[s->s.tis.active_locty].access &= mask; 273edff8678SStefan Berger 274edff8678SStefan Berger if (is_seize) { 275edff8678SStefan Berger tis->loc[tis->active_locty].access |= TPM_TIS_ACCESS_BEEN_SEIZED; 276edff8678SStefan Berger } 277edff8678SStefan Berger } 278edff8678SStefan Berger 279edff8678SStefan Berger tis->active_locty = new_active_locty; 280edff8678SStefan Berger 281edff8678SStefan Berger DPRINTF("tpm_tis: Active locality is now %d\n", s->s.tis.active_locty); 282edff8678SStefan Berger 283edff8678SStefan Berger if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) { 284edff8678SStefan Berger /* set flags on the new active locality */ 285edff8678SStefan Berger tis->loc[new_active_locty].access |= TPM_TIS_ACCESS_ACTIVE_LOCALITY; 286edff8678SStefan Berger tis->loc[new_active_locty].access &= ~(TPM_TIS_ACCESS_REQUEST_USE | 287edff8678SStefan Berger TPM_TIS_ACCESS_SEIZE); 288edff8678SStefan Berger } 289edff8678SStefan Berger 290edff8678SStefan Berger if (change) { 291edff8678SStefan Berger tpm_tis_raise_irq(s, tis->active_locty, TPM_TIS_INT_LOCALITY_CHANGED); 292edff8678SStefan Berger } 293edff8678SStefan Berger } 294edff8678SStefan Berger 295edff8678SStefan Berger /* abort -- this function switches the locality */ 296edff8678SStefan Berger static void tpm_tis_abort(TPMState *s, uint8_t locty) 297edff8678SStefan Berger { 298edff8678SStefan Berger TPMTISEmuState *tis = &s->s.tis; 299edff8678SStefan Berger 300edff8678SStefan Berger tis->loc[locty].r_offset = 0; 301edff8678SStefan Berger tis->loc[locty].w_offset = 0; 302edff8678SStefan Berger 303edff8678SStefan Berger DPRINTF("tpm_tis: tis_abort: new active locality is %d\n", tis->next_locty); 304edff8678SStefan Berger 305edff8678SStefan Berger /* 306edff8678SStefan Berger * Need to react differently depending on who's aborting now and 307edff8678SStefan Berger * which locality will become active afterwards. 308edff8678SStefan Berger */ 309edff8678SStefan Berger if (tis->aborting_locty == tis->next_locty) { 310edff8678SStefan Berger tis->loc[tis->aborting_locty].state = TPM_TIS_STATE_READY; 311fd859081SStefan Berger tpm_tis_sts_set(&tis->loc[tis->aborting_locty], 312fd859081SStefan Berger TPM_TIS_STS_COMMAND_READY); 313edff8678SStefan Berger tpm_tis_raise_irq(s, tis->aborting_locty, TPM_TIS_INT_COMMAND_READY); 314edff8678SStefan Berger } 315edff8678SStefan Berger 316edff8678SStefan Berger /* locality after abort is another one than the current one */ 317edff8678SStefan Berger tpm_tis_new_active_locality(s, tis->next_locty); 318edff8678SStefan Berger 319edff8678SStefan Berger tis->next_locty = TPM_TIS_NO_LOCALITY; 320edff8678SStefan Berger /* nobody's aborting a command anymore */ 321edff8678SStefan Berger tis->aborting_locty = TPM_TIS_NO_LOCALITY; 322edff8678SStefan Berger } 323edff8678SStefan Berger 324edff8678SStefan Berger /* prepare aborting current command */ 325edff8678SStefan Berger static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty) 326edff8678SStefan Berger { 327edff8678SStefan Berger TPMTISEmuState *tis = &s->s.tis; 328edff8678SStefan Berger uint8_t busy_locty; 329edff8678SStefan Berger 330edff8678SStefan Berger tis->aborting_locty = locty; 331edff8678SStefan Berger tis->next_locty = newlocty; /* locality after successful abort */ 332edff8678SStefan Berger 333edff8678SStefan Berger /* 334edff8678SStefan Berger * only abort a command using an interrupt if currently executing 335edff8678SStefan Berger * a command AND if there's a valid connection to the vTPM. 336edff8678SStefan Berger */ 337edff8678SStefan Berger for (busy_locty = 0; busy_locty < TPM_TIS_NUM_LOCALITIES; busy_locty++) { 338edff8678SStefan Berger if (tis->loc[busy_locty].state == TPM_TIS_STATE_EXECUTION) { 339edff8678SStefan Berger /* 340edff8678SStefan Berger * request the backend to cancel. Some backends may not 341edff8678SStefan Berger * support it 342edff8678SStefan Berger */ 3438f0605ccSStefan Berger tpm_backend_cancel_cmd(s->be_driver); 344edff8678SStefan Berger return; 345edff8678SStefan Berger } 346edff8678SStefan Berger } 347edff8678SStefan Berger 348edff8678SStefan Berger tpm_tis_abort(s, locty); 349edff8678SStefan Berger } 350edff8678SStefan Berger 351edff8678SStefan Berger static void tpm_tis_receive_bh(void *opaque) 352edff8678SStefan Berger { 353edff8678SStefan Berger TPMState *s = opaque; 354edff8678SStefan Berger TPMTISEmuState *tis = &s->s.tis; 355edff8678SStefan Berger uint8_t locty = s->locty_number; 356edff8678SStefan Berger 357fd859081SStefan Berger tpm_tis_sts_set(&tis->loc[locty], 358fd859081SStefan Berger TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE); 359edff8678SStefan Berger tis->loc[locty].state = TPM_TIS_STATE_COMPLETION; 360edff8678SStefan Berger tis->loc[locty].r_offset = 0; 361edff8678SStefan Berger tis->loc[locty].w_offset = 0; 362edff8678SStefan Berger 363edff8678SStefan Berger if (TPM_TIS_IS_VALID_LOCTY(tis->next_locty)) { 364edff8678SStefan Berger tpm_tis_abort(s, locty); 365edff8678SStefan Berger } 366edff8678SStefan Berger 367edff8678SStefan Berger tpm_tis_raise_irq(s, locty, 368edff8678SStefan Berger TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID); 369edff8678SStefan Berger } 370edff8678SStefan Berger 371edff8678SStefan Berger /* 372edff8678SStefan Berger * Callback from the TPM to indicate that the response was received. 373edff8678SStefan Berger */ 374fd859081SStefan Berger static void tpm_tis_receive_cb(TPMState *s, uint8_t locty, 375fd859081SStefan Berger bool is_selftest_done) 376edff8678SStefan Berger { 377edff8678SStefan Berger TPMTISEmuState *tis = &s->s.tis; 378fd859081SStefan Berger uint8_t l; 379edff8678SStefan Berger 380edff8678SStefan Berger assert(s->locty_number == locty); 381edff8678SStefan Berger 382fd859081SStefan Berger if (is_selftest_done) { 383fd859081SStefan Berger for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 384fd859081SStefan Berger tis->loc[locty].sts |= TPM_TIS_STS_SELFTEST_DONE; 385fd859081SStefan Berger } 386fd859081SStefan Berger } 387fd859081SStefan Berger 388edff8678SStefan Berger qemu_bh_schedule(tis->bh); 389edff8678SStefan Berger } 390edff8678SStefan Berger 391edff8678SStefan Berger /* 392edff8678SStefan Berger * Read a byte of response data 393edff8678SStefan Berger */ 394edff8678SStefan Berger static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty) 395edff8678SStefan Berger { 396edff8678SStefan Berger TPMTISEmuState *tis = &s->s.tis; 397edff8678SStefan Berger uint32_t ret = TPM_TIS_NO_DATA_BYTE; 398edff8678SStefan Berger uint16_t len; 399edff8678SStefan Berger 400edff8678SStefan Berger if ((tis->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) { 401edff8678SStefan Berger len = tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer); 402edff8678SStefan Berger 403edff8678SStefan Berger ret = tis->loc[locty].r_buffer.buffer[tis->loc[locty].r_offset++]; 404edff8678SStefan Berger if (tis->loc[locty].r_offset >= len) { 405edff8678SStefan Berger /* got last byte */ 406fd859081SStefan Berger tpm_tis_sts_set(&tis->loc[locty], TPM_TIS_STS_VALID); 407edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); 408edff8678SStefan Berger } 409edff8678SStefan Berger DPRINTF("tpm_tis: tpm_tis_data_read byte 0x%02x [%d]\n", 410edff8678SStefan Berger ret, tis->loc[locty].r_offset-1); 411edff8678SStefan Berger } 412edff8678SStefan Berger 413edff8678SStefan Berger return ret; 414edff8678SStefan Berger } 415edff8678SStefan Berger 4168db7c415SStefan Berger #ifdef DEBUG_TIS 4178db7c415SStefan Berger static void tpm_tis_dump_state(void *opaque, hwaddr addr) 4188db7c415SStefan Berger { 4198db7c415SStefan Berger static const unsigned regs[] = { 4208db7c415SStefan Berger TPM_TIS_REG_ACCESS, 4218db7c415SStefan Berger TPM_TIS_REG_INT_ENABLE, 4228db7c415SStefan Berger TPM_TIS_REG_INT_VECTOR, 4238db7c415SStefan Berger TPM_TIS_REG_INT_STATUS, 4248db7c415SStefan Berger TPM_TIS_REG_INTF_CAPABILITY, 4258db7c415SStefan Berger TPM_TIS_REG_STS, 4268db7c415SStefan Berger TPM_TIS_REG_DID_VID, 4278db7c415SStefan Berger TPM_TIS_REG_RID, 4288db7c415SStefan Berger 0xfff}; 4298db7c415SStefan Berger int idx; 4308db7c415SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 4318db7c415SStefan Berger hwaddr base = addr & ~0xfff; 4328db7c415SStefan Berger TPMState *s = opaque; 4338db7c415SStefan Berger TPMTISEmuState *tis = &s->s.tis; 4348db7c415SStefan Berger 4358db7c415SStefan Berger DPRINTF("tpm_tis: active locality : %d\n" 4368db7c415SStefan Berger "tpm_tis: state of locality %d : %d\n" 4378db7c415SStefan Berger "tpm_tis: register dump:\n", 4388db7c415SStefan Berger tis->active_locty, 4398db7c415SStefan Berger locty, tis->loc[locty].state); 4408db7c415SStefan Berger 4418db7c415SStefan Berger for (idx = 0; regs[idx] != 0xfff; idx++) { 4428db7c415SStefan Berger DPRINTF("tpm_tis: 0x%04x : 0x%08x\n", regs[idx], 443070c7607SStefan Berger (int)tpm_tis_mmio_read(opaque, base + regs[idx], 4)); 4448db7c415SStefan Berger } 4458db7c415SStefan Berger 4468db7c415SStefan Berger DPRINTF("tpm_tis: read offset : %d\n" 4478db7c415SStefan Berger "tpm_tis: result buffer : ", 4488db7c415SStefan Berger tis->loc[locty].r_offset); 4498db7c415SStefan Berger for (idx = 0; 4508db7c415SStefan Berger idx < tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer); 4518db7c415SStefan Berger idx++) { 4528db7c415SStefan Berger DPRINTF("%c%02x%s", 4538db7c415SStefan Berger tis->loc[locty].r_offset == idx ? '>' : ' ', 4548db7c415SStefan Berger tis->loc[locty].r_buffer.buffer[idx], 4558db7c415SStefan Berger ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : ""); 4568db7c415SStefan Berger } 4578db7c415SStefan Berger DPRINTF("\n" 4588db7c415SStefan Berger "tpm_tis: write offset : %d\n" 4598db7c415SStefan Berger "tpm_tis: request buffer: ", 4608db7c415SStefan Berger tis->loc[locty].w_offset); 4618db7c415SStefan Berger for (idx = 0; 4628db7c415SStefan Berger idx < tpm_tis_get_size_from_buffer(&tis->loc[locty].w_buffer); 4638db7c415SStefan Berger idx++) { 4648db7c415SStefan Berger DPRINTF("%c%02x%s", 4658db7c415SStefan Berger tis->loc[locty].w_offset == idx ? '>' : ' ', 4668db7c415SStefan Berger tis->loc[locty].w_buffer.buffer[idx], 4678db7c415SStefan Berger ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : ""); 4688db7c415SStefan Berger } 4698db7c415SStefan Berger DPRINTF("\n"); 4708db7c415SStefan Berger } 4718db7c415SStefan Berger #endif 4728db7c415SStefan Berger 473edff8678SStefan Berger /* 474edff8678SStefan Berger * Read a register of the TIS interface 475edff8678SStefan Berger * See specs pages 33-63 for description of the registers 476edff8678SStefan Berger */ 477edff8678SStefan Berger static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, 478edff8678SStefan Berger unsigned size) 479edff8678SStefan Berger { 480edff8678SStefan Berger TPMState *s = opaque; 481edff8678SStefan Berger TPMTISEmuState *tis = &s->s.tis; 482edff8678SStefan Berger uint16_t offset = addr & 0xffc; 483edff8678SStefan Berger uint8_t shift = (addr & 0x3) * 8; 484edff8678SStefan Berger uint32_t val = 0xffffffff; 485edff8678SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 486edff8678SStefan Berger uint32_t avail; 487feeb755fSStefan Berger uint8_t v; 488edff8678SStefan Berger 4898f0605ccSStefan Berger if (tpm_backend_had_startup_error(s->be_driver)) { 490edff8678SStefan Berger return val; 491edff8678SStefan Berger } 492edff8678SStefan Berger 493edff8678SStefan Berger switch (offset) { 494edff8678SStefan Berger case TPM_TIS_REG_ACCESS: 495edff8678SStefan Berger /* never show the SEIZE flag even though we use it internally */ 496edff8678SStefan Berger val = tis->loc[locty].access & ~TPM_TIS_ACCESS_SEIZE; 497edff8678SStefan Berger /* the pending flag is always calculated */ 498edff8678SStefan Berger if (tpm_tis_check_request_use_except(s, locty)) { 499edff8678SStefan Berger val |= TPM_TIS_ACCESS_PENDING_REQUEST; 500edff8678SStefan Berger } 5018f0605ccSStefan Berger val |= !tpm_backend_get_tpm_established_flag(s->be_driver); 502edff8678SStefan Berger break; 503edff8678SStefan Berger case TPM_TIS_REG_INT_ENABLE: 504edff8678SStefan Berger val = tis->loc[locty].inte; 505edff8678SStefan Berger break; 506edff8678SStefan Berger case TPM_TIS_REG_INT_VECTOR: 507edff8678SStefan Berger val = tis->irq_num; 508edff8678SStefan Berger break; 509edff8678SStefan Berger case TPM_TIS_REG_INT_STATUS: 510edff8678SStefan Berger val = tis->loc[locty].ints; 511edff8678SStefan Berger break; 512edff8678SStefan Berger case TPM_TIS_REG_INTF_CAPABILITY: 513116694c3SStefan Berger switch (s->be_tpm_version) { 514116694c3SStefan Berger case TPM_VERSION_UNSPEC: 515116694c3SStefan Berger val = 0; 516116694c3SStefan Berger break; 517116694c3SStefan Berger case TPM_VERSION_1_2: 518116694c3SStefan Berger val = TPM_TIS_CAPABILITIES_SUPPORTED1_3; 519116694c3SStefan Berger break; 520116694c3SStefan Berger case TPM_VERSION_2_0: 521116694c3SStefan Berger val = TPM_TIS_CAPABILITIES_SUPPORTED2_0; 522116694c3SStefan Berger break; 523116694c3SStefan Berger } 524edff8678SStefan Berger break; 525edff8678SStefan Berger case TPM_TIS_REG_STS: 526edff8678SStefan Berger if (tis->active_locty == locty) { 527edff8678SStefan Berger if ((tis->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) { 528edff8678SStefan Berger val = TPM_TIS_BURST_COUNT( 529edff8678SStefan Berger tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer) 530edff8678SStefan Berger - tis->loc[locty].r_offset) | tis->loc[locty].sts; 531edff8678SStefan Berger } else { 532edff8678SStefan Berger avail = tis->loc[locty].w_buffer.size 533edff8678SStefan Berger - tis->loc[locty].w_offset; 534edff8678SStefan Berger /* 535edff8678SStefan Berger * byte-sized reads should not return 0x00 for 0x100 536edff8678SStefan Berger * available bytes. 537edff8678SStefan Berger */ 538edff8678SStefan Berger if (size == 1 && avail > 0xff) { 539edff8678SStefan Berger avail = 0xff; 540edff8678SStefan Berger } 541edff8678SStefan Berger val = TPM_TIS_BURST_COUNT(avail) | tis->loc[locty].sts; 542edff8678SStefan Berger } 543edff8678SStefan Berger } 544edff8678SStefan Berger break; 545edff8678SStefan Berger case TPM_TIS_REG_DATA_FIFO: 5462eae8c75SStefan Berger case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END: 547edff8678SStefan Berger if (tis->active_locty == locty) { 548feeb755fSStefan Berger if (size > 4 - (addr & 0x3)) { 549feeb755fSStefan Berger /* prevent access beyond FIFO */ 550feeb755fSStefan Berger size = 4 - (addr & 0x3); 551feeb755fSStefan Berger } 552feeb755fSStefan Berger val = 0; 553feeb755fSStefan Berger shift = 0; 554feeb755fSStefan Berger while (size > 0) { 555edff8678SStefan Berger switch (tis->loc[locty].state) { 556edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 557feeb755fSStefan Berger v = tpm_tis_data_read(s, locty); 558edff8678SStefan Berger break; 559edff8678SStefan Berger default: 560feeb755fSStefan Berger v = TPM_TIS_NO_DATA_BYTE; 561edff8678SStefan Berger break; 562edff8678SStefan Berger } 563feeb755fSStefan Berger val |= (v << shift); 564feeb755fSStefan Berger shift += 8; 565feeb755fSStefan Berger size--; 566feeb755fSStefan Berger } 567feeb755fSStefan Berger shift = 0; /* no more adjustments */ 568edff8678SStefan Berger } 569edff8678SStefan Berger break; 570116694c3SStefan Berger case TPM_TIS_REG_INTERFACE_ID: 571116694c3SStefan Berger val = tis->loc[locty].iface_id; 572116694c3SStefan Berger break; 573edff8678SStefan Berger case TPM_TIS_REG_DID_VID: 574edff8678SStefan Berger val = (TPM_TIS_TPM_DID << 16) | TPM_TIS_TPM_VID; 575edff8678SStefan Berger break; 576edff8678SStefan Berger case TPM_TIS_REG_RID: 577edff8678SStefan Berger val = TPM_TIS_TPM_RID; 578edff8678SStefan Berger break; 5798db7c415SStefan Berger #ifdef DEBUG_TIS 5808db7c415SStefan Berger case TPM_TIS_REG_DEBUG: 5818db7c415SStefan Berger tpm_tis_dump_state(opaque, addr); 5828db7c415SStefan Berger break; 5838db7c415SStefan Berger #endif 584edff8678SStefan Berger } 585edff8678SStefan Berger 586edff8678SStefan Berger if (shift) { 587edff8678SStefan Berger val >>= shift; 588edff8678SStefan Berger } 589edff8678SStefan Berger 590070c7607SStefan Berger DPRINTF("tpm_tis: read.%u(%08x) = %08x\n", size, (int)addr, (int)val); 591edff8678SStefan Berger 592edff8678SStefan Berger return val; 593edff8678SStefan Berger } 594edff8678SStefan Berger 595edff8678SStefan Berger /* 596edff8678SStefan Berger * Write a value to a register of the TIS interface 597edff8678SStefan Berger * See specs pages 33-63 for description of the registers 598edff8678SStefan Berger */ 599ff2bc0c1SMarc-André Lureau static void tpm_tis_mmio_write(void *opaque, hwaddr addr, 600ff2bc0c1SMarc-André Lureau uint64_t val, unsigned size) 601edff8678SStefan Berger { 602edff8678SStefan Berger TPMState *s = opaque; 603edff8678SStefan Berger TPMTISEmuState *tis = &s->s.tis; 604feeb755fSStefan Berger uint16_t off = addr & 0xffc; 605feeb755fSStefan Berger uint8_t shift = (addr & 0x3) * 8; 606edff8678SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 607edff8678SStefan Berger uint8_t active_locty, l; 608edff8678SStefan Berger int c, set_new_locty = 1; 609edff8678SStefan Berger uint16_t len; 610feeb755fSStefan Berger uint32_t mask = (size == 1) ? 0xff : ((size == 2) ? 0xffff : ~0); 611edff8678SStefan Berger 612070c7607SStefan Berger DPRINTF("tpm_tis: write.%u(%08x) = %08x\n", size, (int)addr, (int)val); 613edff8678SStefan Berger 614ff2bc0c1SMarc-André Lureau if (locty == 4) { 615edff8678SStefan Berger DPRINTF("tpm_tis: Access to locality 4 only allowed from hardware\n"); 616edff8678SStefan Berger return; 617edff8678SStefan Berger } 618edff8678SStefan Berger 6198f0605ccSStefan Berger if (tpm_backend_had_startup_error(s->be_driver)) { 620edff8678SStefan Berger return; 621edff8678SStefan Berger } 622edff8678SStefan Berger 623feeb755fSStefan Berger val &= mask; 624feeb755fSStefan Berger 625feeb755fSStefan Berger if (shift) { 626feeb755fSStefan Berger val <<= shift; 627feeb755fSStefan Berger mask <<= shift; 628feeb755fSStefan Berger } 629feeb755fSStefan Berger 630feeb755fSStefan Berger mask ^= 0xffffffff; 631feeb755fSStefan Berger 632edff8678SStefan Berger switch (off) { 633edff8678SStefan Berger case TPM_TIS_REG_ACCESS: 634edff8678SStefan Berger 635edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_SEIZE)) { 636edff8678SStefan Berger val &= ~(TPM_TIS_ACCESS_REQUEST_USE | 637edff8678SStefan Berger TPM_TIS_ACCESS_ACTIVE_LOCALITY); 638edff8678SStefan Berger } 639edff8678SStefan Berger 640edff8678SStefan Berger active_locty = tis->active_locty; 641edff8678SStefan Berger 642edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) { 643edff8678SStefan Berger /* give up locality if currently owned */ 644edff8678SStefan Berger if (tis->active_locty == locty) { 645edff8678SStefan Berger DPRINTF("tpm_tis: Releasing locality %d\n", locty); 646edff8678SStefan Berger 647edff8678SStefan Berger uint8_t newlocty = TPM_TIS_NO_LOCALITY; 648edff8678SStefan Berger /* anybody wants the locality ? */ 649edff8678SStefan Berger for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) { 650edff8678SStefan Berger if ((tis->loc[c].access & TPM_TIS_ACCESS_REQUEST_USE)) { 651edff8678SStefan Berger DPRINTF("tpm_tis: Locality %d requests use.\n", c); 652edff8678SStefan Berger newlocty = c; 653edff8678SStefan Berger break; 654edff8678SStefan Berger } 655edff8678SStefan Berger } 656edff8678SStefan Berger DPRINTF("tpm_tis: TPM_TIS_ACCESS_ACTIVE_LOCALITY: " 657edff8678SStefan Berger "Next active locality: %d\n", 658edff8678SStefan Berger newlocty); 659edff8678SStefan Berger 660edff8678SStefan Berger if (TPM_TIS_IS_VALID_LOCTY(newlocty)) { 661edff8678SStefan Berger set_new_locty = 0; 662edff8678SStefan Berger tpm_tis_prep_abort(s, locty, newlocty); 663edff8678SStefan Berger } else { 664edff8678SStefan Berger active_locty = TPM_TIS_NO_LOCALITY; 665edff8678SStefan Berger } 666edff8678SStefan Berger } else { 667edff8678SStefan Berger /* not currently the owner; clear a pending request */ 668edff8678SStefan Berger tis->loc[locty].access &= ~TPM_TIS_ACCESS_REQUEST_USE; 669edff8678SStefan Berger } 670edff8678SStefan Berger } 671edff8678SStefan Berger 672edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_BEEN_SEIZED)) { 673edff8678SStefan Berger tis->loc[locty].access &= ~TPM_TIS_ACCESS_BEEN_SEIZED; 674edff8678SStefan Berger } 675edff8678SStefan Berger 676edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_SEIZE)) { 677edff8678SStefan Berger /* 678edff8678SStefan Berger * allow seize if a locality is active and the requesting 679edff8678SStefan Berger * locality is higher than the one that's active 680edff8678SStefan Berger * OR 681edff8678SStefan Berger * allow seize for requesting locality if no locality is 682edff8678SStefan Berger * active 683edff8678SStefan Berger */ 684edff8678SStefan Berger while ((TPM_TIS_IS_VALID_LOCTY(tis->active_locty) && 685edff8678SStefan Berger locty > tis->active_locty) || 686edff8678SStefan Berger !TPM_TIS_IS_VALID_LOCTY(tis->active_locty)) { 687edff8678SStefan Berger bool higher_seize = FALSE; 688edff8678SStefan Berger 689edff8678SStefan Berger /* already a pending SEIZE ? */ 690edff8678SStefan Berger if ((tis->loc[locty].access & TPM_TIS_ACCESS_SEIZE)) { 691edff8678SStefan Berger break; 692edff8678SStefan Berger } 693edff8678SStefan Berger 694edff8678SStefan Berger /* check for ongoing seize by a higher locality */ 695edff8678SStefan Berger for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES; l++) { 696edff8678SStefan Berger if ((tis->loc[l].access & TPM_TIS_ACCESS_SEIZE)) { 697edff8678SStefan Berger higher_seize = TRUE; 698edff8678SStefan Berger break; 699edff8678SStefan Berger } 700edff8678SStefan Berger } 701edff8678SStefan Berger 702edff8678SStefan Berger if (higher_seize) { 703edff8678SStefan Berger break; 704edff8678SStefan Berger } 705edff8678SStefan Berger 706edff8678SStefan Berger /* cancel any seize by a lower locality */ 707edff8678SStefan Berger for (l = 0; l < locty - 1; l++) { 708edff8678SStefan Berger tis->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE; 709edff8678SStefan Berger } 710edff8678SStefan Berger 711edff8678SStefan Berger tis->loc[locty].access |= TPM_TIS_ACCESS_SEIZE; 712edff8678SStefan Berger DPRINTF("tpm_tis: TPM_TIS_ACCESS_SEIZE: " 713edff8678SStefan Berger "Locality %d seized from locality %d\n", 714edff8678SStefan Berger locty, tis->active_locty); 715edff8678SStefan Berger DPRINTF("tpm_tis: TPM_TIS_ACCESS_SEIZE: Initiating abort.\n"); 716edff8678SStefan Berger set_new_locty = 0; 717edff8678SStefan Berger tpm_tis_prep_abort(s, tis->active_locty, locty); 718edff8678SStefan Berger break; 719edff8678SStefan Berger } 720edff8678SStefan Berger } 721edff8678SStefan Berger 722edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_REQUEST_USE)) { 723edff8678SStefan Berger if (tis->active_locty != locty) { 724edff8678SStefan Berger if (TPM_TIS_IS_VALID_LOCTY(tis->active_locty)) { 725edff8678SStefan Berger tis->loc[locty].access |= TPM_TIS_ACCESS_REQUEST_USE; 726edff8678SStefan Berger } else { 727edff8678SStefan Berger /* no locality active -> make this one active now */ 728edff8678SStefan Berger active_locty = locty; 729edff8678SStefan Berger } 730edff8678SStefan Berger } 731edff8678SStefan Berger } 732edff8678SStefan Berger 733edff8678SStefan Berger if (set_new_locty) { 734edff8678SStefan Berger tpm_tis_new_active_locality(s, active_locty); 735edff8678SStefan Berger } 736edff8678SStefan Berger 737edff8678SStefan Berger break; 738edff8678SStefan Berger case TPM_TIS_REG_INT_ENABLE: 739edff8678SStefan Berger if (tis->active_locty != locty) { 740edff8678SStefan Berger break; 741edff8678SStefan Berger } 742edff8678SStefan Berger 743feeb755fSStefan Berger tis->loc[locty].inte &= mask; 744feeb755fSStefan Berger tis->loc[locty].inte |= (val & (TPM_TIS_INT_ENABLED | 745edff8678SStefan Berger TPM_TIS_INT_POLARITY_MASK | 746edff8678SStefan Berger TPM_TIS_INTERRUPTS_SUPPORTED)); 747edff8678SStefan Berger break; 748edff8678SStefan Berger case TPM_TIS_REG_INT_VECTOR: 749edff8678SStefan Berger /* hard wired -- ignore */ 750edff8678SStefan Berger break; 751edff8678SStefan Berger case TPM_TIS_REG_INT_STATUS: 752edff8678SStefan Berger if (tis->active_locty != locty) { 753edff8678SStefan Berger break; 754edff8678SStefan Berger } 755edff8678SStefan Berger 756edff8678SStefan Berger /* clearing of interrupt flags */ 757edff8678SStefan Berger if (((val & TPM_TIS_INTERRUPTS_SUPPORTED)) && 758edff8678SStefan Berger (tis->loc[locty].ints & TPM_TIS_INTERRUPTS_SUPPORTED)) { 759edff8678SStefan Berger tis->loc[locty].ints &= ~val; 760edff8678SStefan Berger if (tis->loc[locty].ints == 0) { 761edff8678SStefan Berger qemu_irq_lower(tis->irq); 762edff8678SStefan Berger DPRINTF("tpm_tis: Lowering IRQ\n"); 763edff8678SStefan Berger } 764edff8678SStefan Berger } 765edff8678SStefan Berger tis->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED); 766edff8678SStefan Berger break; 767edff8678SStefan Berger case TPM_TIS_REG_STS: 768edff8678SStefan Berger if (tis->active_locty != locty) { 769edff8678SStefan Berger break; 770edff8678SStefan Berger } 771edff8678SStefan Berger 772116694c3SStefan Berger if (s->be_tpm_version == TPM_VERSION_2_0) { 773116694c3SStefan Berger /* some flags that are only supported for TPM 2 */ 774116694c3SStefan Berger if (val & TPM_TIS_STS_COMMAND_CANCEL) { 775116694c3SStefan Berger if (tis->loc[locty].state == TPM_TIS_STATE_EXECUTION) { 776116694c3SStefan Berger /* 777116694c3SStefan Berger * request the backend to cancel. Some backends may not 778116694c3SStefan Berger * support it 779116694c3SStefan Berger */ 780116694c3SStefan Berger tpm_backend_cancel_cmd(s->be_driver); 781116694c3SStefan Berger } 782116694c3SStefan Berger } 783116694c3SStefan Berger 784116694c3SStefan Berger if (val & TPM_TIS_STS_RESET_ESTABLISHMENT_BIT) { 785116694c3SStefan Berger if (locty == 3 || locty == 4) { 786116694c3SStefan Berger tpm_backend_reset_tpm_established_flag(s->be_driver, locty); 787116694c3SStefan Berger } 788116694c3SStefan Berger } 789116694c3SStefan Berger } 790116694c3SStefan Berger 791edff8678SStefan Berger val &= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO | 792edff8678SStefan Berger TPM_TIS_STS_RESPONSE_RETRY); 793edff8678SStefan Berger 794edff8678SStefan Berger if (val == TPM_TIS_STS_COMMAND_READY) { 795edff8678SStefan Berger switch (tis->loc[locty].state) { 796edff8678SStefan Berger 797edff8678SStefan Berger case TPM_TIS_STATE_READY: 798edff8678SStefan Berger tis->loc[locty].w_offset = 0; 799edff8678SStefan Berger tis->loc[locty].r_offset = 0; 800edff8678SStefan Berger break; 801edff8678SStefan Berger 802edff8678SStefan Berger case TPM_TIS_STATE_IDLE: 803fd859081SStefan Berger tpm_tis_sts_set(&tis->loc[locty], TPM_TIS_STS_COMMAND_READY); 804edff8678SStefan Berger tis->loc[locty].state = TPM_TIS_STATE_READY; 805edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); 806edff8678SStefan Berger break; 807edff8678SStefan Berger 808edff8678SStefan Berger case TPM_TIS_STATE_EXECUTION: 809edff8678SStefan Berger case TPM_TIS_STATE_RECEPTION: 810edff8678SStefan Berger /* abort currently running command */ 811edff8678SStefan Berger DPRINTF("tpm_tis: %s: Initiating abort.\n", 812edff8678SStefan Berger __func__); 813edff8678SStefan Berger tpm_tis_prep_abort(s, locty, locty); 814edff8678SStefan Berger break; 815edff8678SStefan Berger 816edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 817edff8678SStefan Berger tis->loc[locty].w_offset = 0; 818edff8678SStefan Berger tis->loc[locty].r_offset = 0; 819edff8678SStefan Berger /* shortcut to ready state with C/R set */ 820edff8678SStefan Berger tis->loc[locty].state = TPM_TIS_STATE_READY; 821edff8678SStefan Berger if (!(tis->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) { 822fd859081SStefan Berger tpm_tis_sts_set(&tis->loc[locty], 823fd859081SStefan Berger TPM_TIS_STS_COMMAND_READY); 824edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); 825edff8678SStefan Berger } 826edff8678SStefan Berger tis->loc[locty].sts &= ~(TPM_TIS_STS_DATA_AVAILABLE); 827edff8678SStefan Berger break; 828edff8678SStefan Berger 829edff8678SStefan Berger } 830edff8678SStefan Berger } else if (val == TPM_TIS_STS_TPM_GO) { 831edff8678SStefan Berger switch (tis->loc[locty].state) { 832edff8678SStefan Berger case TPM_TIS_STATE_RECEPTION: 833edff8678SStefan Berger if ((tis->loc[locty].sts & TPM_TIS_STS_EXPECT) == 0) { 834edff8678SStefan Berger tpm_tis_tpm_send(s, locty); 835edff8678SStefan Berger } 836edff8678SStefan Berger break; 837edff8678SStefan Berger default: 838edff8678SStefan Berger /* ignore */ 839edff8678SStefan Berger break; 840edff8678SStefan Berger } 841edff8678SStefan Berger } else if (val == TPM_TIS_STS_RESPONSE_RETRY) { 842edff8678SStefan Berger switch (tis->loc[locty].state) { 843edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 844edff8678SStefan Berger tis->loc[locty].r_offset = 0; 845fd859081SStefan Berger tpm_tis_sts_set(&tis->loc[locty], 846fd859081SStefan Berger TPM_TIS_STS_VALID| 847fd859081SStefan Berger TPM_TIS_STS_DATA_AVAILABLE); 848edff8678SStefan Berger break; 849edff8678SStefan Berger default: 850edff8678SStefan Berger /* ignore */ 851edff8678SStefan Berger break; 852edff8678SStefan Berger } 853edff8678SStefan Berger } 854edff8678SStefan Berger break; 855edff8678SStefan Berger case TPM_TIS_REG_DATA_FIFO: 8562eae8c75SStefan Berger case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END: 857edff8678SStefan Berger /* data fifo */ 858edff8678SStefan Berger if (tis->active_locty != locty) { 859edff8678SStefan Berger break; 860edff8678SStefan Berger } 861edff8678SStefan Berger 862edff8678SStefan Berger if (tis->loc[locty].state == TPM_TIS_STATE_IDLE || 863edff8678SStefan Berger tis->loc[locty].state == TPM_TIS_STATE_EXECUTION || 864edff8678SStefan Berger tis->loc[locty].state == TPM_TIS_STATE_COMPLETION) { 865edff8678SStefan Berger /* drop the byte */ 866edff8678SStefan Berger } else { 867feeb755fSStefan Berger DPRINTF("tpm_tis: Data to send to TPM: %08x (size=%d)\n", 868070c7607SStefan Berger (int)val, size); 869edff8678SStefan Berger if (tis->loc[locty].state == TPM_TIS_STATE_READY) { 870edff8678SStefan Berger tis->loc[locty].state = TPM_TIS_STATE_RECEPTION; 871fd859081SStefan Berger tpm_tis_sts_set(&tis->loc[locty], 872fd859081SStefan Berger TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); 873edff8678SStefan Berger } 874edff8678SStefan Berger 875feeb755fSStefan Berger val >>= shift; 876feeb755fSStefan Berger if (size > 4 - (addr & 0x3)) { 877feeb755fSStefan Berger /* prevent access beyond FIFO */ 878feeb755fSStefan Berger size = 4 - (addr & 0x3); 879feeb755fSStefan Berger } 880feeb755fSStefan Berger 881feeb755fSStefan Berger while ((tis->loc[locty].sts & TPM_TIS_STS_EXPECT) && size > 0) { 882edff8678SStefan Berger if (tis->loc[locty].w_offset < tis->loc[locty].w_buffer.size) { 883edff8678SStefan Berger tis->loc[locty].w_buffer. 884edff8678SStefan Berger buffer[tis->loc[locty].w_offset++] = (uint8_t)val; 885feeb755fSStefan Berger val >>= 8; 886feeb755fSStefan Berger size--; 887edff8678SStefan Berger } else { 888fd859081SStefan Berger tpm_tis_sts_set(&tis->loc[locty], TPM_TIS_STS_VALID); 889edff8678SStefan Berger } 890edff8678SStefan Berger } 891edff8678SStefan Berger 892edff8678SStefan Berger /* check for complete packet */ 893edff8678SStefan Berger if (tis->loc[locty].w_offset > 5 && 894edff8678SStefan Berger (tis->loc[locty].sts & TPM_TIS_STS_EXPECT)) { 895edff8678SStefan Berger /* we have a packet length - see if we have all of it */ 89629b558d8SStefan Berger bool need_irq = !(tis->loc[locty].sts & TPM_TIS_STS_VALID); 897*d8383d61SMarc-André Lureau 898edff8678SStefan Berger len = tpm_tis_get_size_from_buffer(&tis->loc[locty].w_buffer); 899edff8678SStefan Berger if (len > tis->loc[locty].w_offset) { 900fd859081SStefan Berger tpm_tis_sts_set(&tis->loc[locty], 901fd859081SStefan Berger TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); 902edff8678SStefan Berger } else { 903edff8678SStefan Berger /* packet complete */ 904fd859081SStefan Berger tpm_tis_sts_set(&tis->loc[locty], TPM_TIS_STS_VALID); 905edff8678SStefan Berger } 90629b558d8SStefan Berger if (need_irq) { 907edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); 908edff8678SStefan Berger } 909edff8678SStefan Berger } 910edff8678SStefan Berger } 911edff8678SStefan Berger break; 912116694c3SStefan Berger case TPM_TIS_REG_INTERFACE_ID: 913116694c3SStefan Berger if (val & TPM_TIS_IFACE_ID_INT_SEL_LOCK) { 914116694c3SStefan Berger for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 915116694c3SStefan Berger tis->loc[l].iface_id |= TPM_TIS_IFACE_ID_INT_SEL_LOCK; 916116694c3SStefan Berger } 917116694c3SStefan Berger } 918116694c3SStefan Berger break; 919edff8678SStefan Berger } 920edff8678SStefan Berger } 921edff8678SStefan Berger 922edff8678SStefan Berger static const MemoryRegionOps tpm_tis_memory_ops = { 923edff8678SStefan Berger .read = tpm_tis_mmio_read, 924edff8678SStefan Berger .write = tpm_tis_mmio_write, 925edff8678SStefan Berger .endianness = DEVICE_LITTLE_ENDIAN, 926edff8678SStefan Berger .valid = { 927edff8678SStefan Berger .min_access_size = 1, 928edff8678SStefan Berger .max_access_size = 4, 929edff8678SStefan Berger }, 930edff8678SStefan Berger }; 931edff8678SStefan Berger 932edff8678SStefan Berger static int tpm_tis_do_startup_tpm(TPMState *s) 933edff8678SStefan Berger { 9348f0605ccSStefan Berger return tpm_backend_startup_tpm(s->be_driver); 935edff8678SStefan Berger } 936edff8678SStefan Berger 937d0c519bdSAmarnath Valluri static void tpm_tis_realloc_buffer(TPMSizedBuffer *sb) 938d0c519bdSAmarnath Valluri { 939d0c519bdSAmarnath Valluri size_t wanted_size = 4096; /* Linux tpm.c buffer size */ 940d0c519bdSAmarnath Valluri 941d0c519bdSAmarnath Valluri if (sb->size != wanted_size) { 942d0c519bdSAmarnath Valluri sb->buffer = g_realloc(sb->buffer, wanted_size); 943d0c519bdSAmarnath Valluri sb->size = wanted_size; 944d0c519bdSAmarnath Valluri } 945d0c519bdSAmarnath Valluri } 946d0c519bdSAmarnath Valluri 947edff8678SStefan Berger /* 9485cb18b3dSStefan Berger * Get the TPMVersion of the backend device being used 9495cb18b3dSStefan Berger */ 9505cb18b3dSStefan Berger TPMVersion tpm_tis_get_tpm_version(Object *obj) 9515cb18b3dSStefan Berger { 9525cb18b3dSStefan Berger TPMState *s = TPM(obj); 9535cb18b3dSStefan Berger 9545cb18b3dSStefan Berger return tpm_backend_get_tpm_version(s->be_driver); 9555cb18b3dSStefan Berger } 9565cb18b3dSStefan Berger 9575cb18b3dSStefan Berger /* 958edff8678SStefan Berger * This function is called when the machine starts, resets or due to 959edff8678SStefan Berger * S3 resume. 960edff8678SStefan Berger */ 961edff8678SStefan Berger static void tpm_tis_reset(DeviceState *dev) 962edff8678SStefan Berger { 963edff8678SStefan Berger TPMState *s = TPM(dev); 964edff8678SStefan Berger TPMTISEmuState *tis = &s->s.tis; 965edff8678SStefan Berger int c; 966edff8678SStefan Berger 967116694c3SStefan Berger s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver); 968116694c3SStefan Berger 9698f0605ccSStefan Berger tpm_backend_reset(s->be_driver); 970edff8678SStefan Berger 971edff8678SStefan Berger tis->active_locty = TPM_TIS_NO_LOCALITY; 972edff8678SStefan Berger tis->next_locty = TPM_TIS_NO_LOCALITY; 973edff8678SStefan Berger tis->aborting_locty = TPM_TIS_NO_LOCALITY; 974edff8678SStefan Berger 975edff8678SStefan Berger for (c = 0; c < TPM_TIS_NUM_LOCALITIES; c++) { 976edff8678SStefan Berger tis->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS; 977116694c3SStefan Berger switch (s->be_tpm_version) { 978116694c3SStefan Berger case TPM_VERSION_UNSPEC: 979116694c3SStefan Berger break; 980116694c3SStefan Berger case TPM_VERSION_1_2: 981116694c3SStefan Berger tis->loc[c].sts = TPM_TIS_STS_TPM_FAMILY1_2; 982116694c3SStefan Berger tis->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3; 983116694c3SStefan Berger break; 984116694c3SStefan Berger case TPM_VERSION_2_0: 985116694c3SStefan Berger tis->loc[c].sts = TPM_TIS_STS_TPM_FAMILY2_0; 986116694c3SStefan Berger tis->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0; 987116694c3SStefan Berger break; 988116694c3SStefan Berger } 989edff8678SStefan Berger tis->loc[c].inte = TPM_TIS_INT_POLARITY_LOW_LEVEL; 990edff8678SStefan Berger tis->loc[c].ints = 0; 991edff8678SStefan Berger tis->loc[c].state = TPM_TIS_STATE_IDLE; 992edff8678SStefan Berger 993edff8678SStefan Berger tis->loc[c].w_offset = 0; 994d0c519bdSAmarnath Valluri tpm_tis_realloc_buffer(&tis->loc[c].w_buffer); 995edff8678SStefan Berger tis->loc[c].r_offset = 0; 996d0c519bdSAmarnath Valluri tpm_tis_realloc_buffer(&tis->loc[c].r_buffer); 997edff8678SStefan Berger } 998edff8678SStefan Berger 999edff8678SStefan Berger tpm_tis_do_startup_tpm(s); 1000edff8678SStefan Berger } 1001edff8678SStefan Berger 1002edff8678SStefan Berger static const VMStateDescription vmstate_tpm_tis = { 1003edff8678SStefan Berger .name = "tpm", 1004edff8678SStefan Berger .unmigratable = 1, 1005edff8678SStefan Berger }; 1006edff8678SStefan Berger 1007edff8678SStefan Berger static Property tpm_tis_properties[] = { 1008edff8678SStefan Berger DEFINE_PROP_UINT32("irq", TPMState, 1009edff8678SStefan Berger s.tis.irq_num, TPM_TIS_IRQ), 1010edff8678SStefan Berger DEFINE_PROP_STRING("tpmdev", TPMState, backend), 1011edff8678SStefan Berger DEFINE_PROP_END_OF_LIST(), 1012edff8678SStefan Berger }; 1013edff8678SStefan Berger 1014edff8678SStefan Berger static void tpm_tis_realizefn(DeviceState *dev, Error **errp) 1015edff8678SStefan Berger { 1016edff8678SStefan Berger TPMState *s = TPM(dev); 1017edff8678SStefan Berger TPMTISEmuState *tis = &s->s.tis; 1018edff8678SStefan Berger 1019edff8678SStefan Berger s->be_driver = qemu_find_tpm(s->backend); 1020edff8678SStefan Berger if (!s->be_driver) { 1021edff8678SStefan Berger error_setg(errp, "tpm_tis: backend driver with id %s could not be " 1022edff8678SStefan Berger "found", s->backend); 1023edff8678SStefan Berger return; 1024edff8678SStefan Berger } 1025edff8678SStefan Berger 1026edff8678SStefan Berger s->be_driver->fe_model = TPM_MODEL_TPM_TIS; 1027edff8678SStefan Berger 10288f0605ccSStefan Berger if (tpm_backend_init(s->be_driver, s, tpm_tis_receive_cb)) { 1029edff8678SStefan Berger error_setg(errp, "tpm_tis: backend driver with id %s could not be " 1030edff8678SStefan Berger "initialized", s->backend); 1031edff8678SStefan Berger return; 1032edff8678SStefan Berger } 1033edff8678SStefan Berger 1034edff8678SStefan Berger if (tis->irq_num > 15) { 1035edff8678SStefan Berger error_setg(errp, "tpm_tis: IRQ %d for TPM TIS is outside valid range " 10369af9e0feSMarkus Armbruster "of 0 to 15", tis->irq_num); 1037edff8678SStefan Berger return; 1038edff8678SStefan Berger } 1039edff8678SStefan Berger 1040edff8678SStefan Berger tis->bh = qemu_bh_new(tpm_tis_receive_bh, s); 1041edff8678SStefan Berger 1042edff8678SStefan Berger isa_init_irq(&s->busdev, &tis->irq, tis->irq_num); 10439dfd24edSStefan Berger 10449dfd24edSStefan Berger memory_region_add_subregion(isa_address_space(ISA_DEVICE(dev)), 10459dfd24edSStefan Berger TPM_TIS_ADDR_BASE, &s->mmio); 1046edff8678SStefan Berger } 1047edff8678SStefan Berger 1048edff8678SStefan Berger static void tpm_tis_initfn(Object *obj) 1049edff8678SStefan Berger { 1050edff8678SStefan Berger TPMState *s = TPM(obj); 1051edff8678SStefan Berger 1052853dca12SPaolo Bonzini memory_region_init_io(&s->mmio, OBJECT(s), &tpm_tis_memory_ops, 1053853dca12SPaolo Bonzini s, "tpm-tis-mmio", 1054edff8678SStefan Berger TPM_TIS_NUM_LOCALITIES << TPM_TIS_LOCALITY_SHIFT); 1055edff8678SStefan Berger } 1056edff8678SStefan Berger 1057edff8678SStefan Berger static void tpm_tis_class_init(ObjectClass *klass, void *data) 1058edff8678SStefan Berger { 1059edff8678SStefan Berger DeviceClass *dc = DEVICE_CLASS(klass); 1060edff8678SStefan Berger 1061edff8678SStefan Berger dc->realize = tpm_tis_realizefn; 1062edff8678SStefan Berger dc->props = tpm_tis_properties; 1063edff8678SStefan Berger dc->reset = tpm_tis_reset; 1064edff8678SStefan Berger dc->vmsd = &vmstate_tpm_tis; 1065edff8678SStefan Berger } 1066edff8678SStefan Berger 1067edff8678SStefan Berger static const TypeInfo tpm_tis_info = { 1068edff8678SStefan Berger .name = TYPE_TPM_TIS, 1069edff8678SStefan Berger .parent = TYPE_ISA_DEVICE, 1070edff8678SStefan Berger .instance_size = sizeof(TPMState), 1071edff8678SStefan Berger .instance_init = tpm_tis_initfn, 1072edff8678SStefan Berger .class_init = tpm_tis_class_init, 1073edff8678SStefan Berger }; 1074edff8678SStefan Berger 1075edff8678SStefan Berger static void tpm_tis_register(void) 1076edff8678SStefan Berger { 1077edff8678SStefan Berger type_register_static(&tpm_tis_info); 1078edff8678SStefan Berger tpm_register_model(TPM_MODEL_TPM_TIS); 1079edff8678SStefan Berger } 1080edff8678SStefan Berger 1081edff8678SStefan Berger type_init(tpm_tis_register) 1082