1edff8678SStefan Berger /* 2ac90053dSEric Auger * tpm_tis_common.c - QEMU's TPM TIS interface emulator 3ac90053dSEric Auger * device agnostic functions 4edff8678SStefan Berger * 5edff8678SStefan Berger * Copyright (C) 2006,2010-2013 IBM Corporation 6edff8678SStefan Berger * 7edff8678SStefan Berger * Authors: 8edff8678SStefan Berger * Stefan Berger <stefanb@us.ibm.com> 9edff8678SStefan Berger * David Safford <safford@us.ibm.com> 10edff8678SStefan Berger * 11edff8678SStefan Berger * Xen 4 support: Andrease Niederl <andreas.niederl@iaik.tugraz.at> 12edff8678SStefan Berger * 13edff8678SStefan Berger * This work is licensed under the terms of the GNU GPL, version 2 or later. 14edff8678SStefan Berger * See the COPYING file in the top-level directory. 15edff8678SStefan Berger * 16edff8678SStefan Berger * Implementation of the TIS interface according to specs found at 17edff8678SStefan Berger * http://www.trustedcomputinggroup.org. This implementation currently 189dd5c40dSStefan Berger * supports version 1.3, 21 March 2013 19edff8678SStefan Berger * In the developers menu choose the PC Client section then find the TIS 20edff8678SStefan Berger * specification. 21116694c3SStefan Berger * 22116694c3SStefan Berger * TPM TIS for TPM 2 implementation following TCG PC Client Platform 23*6eedbb5bSMichael Tokarev * TPM Profile (PTP) Specification, Family 2.0, Revision 00.43 24edff8678SStefan Berger */ 250430891cSPeter Maydell #include "qemu/osdep.h" 2664552b6bSMarkus Armbruster #include "hw/irq.h" 27732cd587SMarc-André Lureau #include "hw/isa/isa.h" 28023299d8SMarc-André Lureau #include "qapi/error.h" 29bbadfb2eSNinad Palsule #include "qemu/bswap.h" 30bbadfb2eSNinad Palsule #include "qemu/crc-ccitt.h" 310b8fa32fSMarkus Armbruster #include "qemu/module.h" 32023299d8SMarc-André Lureau 33023299d8SMarc-André Lureau #include "hw/acpi/tpm.h" 34023299d8SMarc-André Lureau #include "hw/pci/pci_ids.h" 35a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 36d6454270SMarkus Armbruster #include "migration/vmstate.h" 37dccfcd0eSPaolo Bonzini #include "sysemu/tpm_backend.h" 380f7d2148SPhilippe Mathieu-Daudé #include "sysemu/tpm_util.h" 393b97c01eSStefan Berger #include "tpm_ppi.h" 40fcbed221SStefan Berger #include "trace.h" 41732cd587SMarc-André Lureau 42ac90053dSEric Auger #include "tpm_tis.h" 43edff8678SStefan Berger 444d1ba9c4SStefan Berger #define DEBUG_TIS 0 45edff8678SStefan Berger 468db7c415SStefan Berger /* local prototypes */ 478db7c415SStefan Berger 488db7c415SStefan Berger static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, 498db7c415SStefan Berger unsigned size); 508db7c415SStefan Berger 51edff8678SStefan Berger /* utility functions */ 52edff8678SStefan Berger 53edff8678SStefan Berger static uint8_t tpm_tis_locality_from_addr(hwaddr addr) 54edff8678SStefan Berger { 554d84bb6cSPeter Maydell uint8_t locty; 564d84bb6cSPeter Maydell 574d84bb6cSPeter Maydell locty = (uint8_t)((addr >> TPM_TIS_LOCALITY_SHIFT) & 0x7); 584d84bb6cSPeter Maydell assert(TPM_TIS_IS_VALID_LOCTY(locty)); 594d84bb6cSPeter Maydell 604d84bb6cSPeter Maydell return locty; 61edff8678SStefan Berger } 62edff8678SStefan Berger 63edff8678SStefan Berger 64edff8678SStefan Berger /* 65fd859081SStefan Berger * Set the given flags in the STS register by clearing the register but 66116694c3SStefan Berger * preserving the SELFTEST_DONE and TPM_FAMILY_MASK flags and then setting 67116694c3SStefan Berger * the new flags. 68fd859081SStefan Berger * 69fd859081SStefan Berger * The SELFTEST_DONE flag is acquired from the backend that determines it by 70fd859081SStefan Berger * peeking into TPM commands. 71fd859081SStefan Berger * 72fd859081SStefan Berger * A VM suspend/resume will preserve the flag by storing it into the VM 73fd859081SStefan Berger * device state, but the backend will not remember it when QEMU is started 74fd859081SStefan Berger * again. Therefore, we cache the flag here. Once set, it will not be unset 75fd859081SStefan Berger * except by a reset. 76fd859081SStefan Berger */ 77fd859081SStefan Berger static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags) 78fd859081SStefan Berger { 79116694c3SStefan Berger l->sts &= TPM_TIS_STS_SELFTEST_DONE | TPM_TIS_STS_TPM_FAMILY_MASK; 80fd859081SStefan Berger l->sts |= flags; 81fd859081SStefan Berger } 82fd859081SStefan Berger 83fd859081SStefan Berger /* 84edff8678SStefan Berger * Send a request to the TPM. 85edff8678SStefan Berger */ 86edff8678SStefan Berger static void tpm_tis_tpm_send(TPMState *s, uint8_t locty) 87edff8678SStefan Berger { 883688d73bSStefan Berger tpm_util_show_buffer(s->buffer, s->be_buffer_size, "To TPM"); 89edff8678SStefan Berger 90edff8678SStefan Berger /* 91f999d81bSStefan Berger * rw_offset serves as length indicator for length of data; 92edff8678SStefan Berger * it's reset when the response comes back 93edff8678SStefan Berger */ 943d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_EXECUTION; 95edff8678SStefan Berger 960e43b7e6SMarc-André Lureau s->cmd = (TPMBackendCmd) { 970e43b7e6SMarc-André Lureau .locty = locty, 98c5496b97SStefan Berger .in = s->buffer, 99f999d81bSStefan Berger .in_len = s->rw_offset, 100c5496b97SStefan Berger .out = s->buffer, 101e6b703f6SStefan Berger .out_len = s->be_buffer_size, 1020e43b7e6SMarc-André Lureau }; 1030e43b7e6SMarc-André Lureau 1040e43b7e6SMarc-André Lureau tpm_backend_deliver_request(s->be_driver, &s->cmd); 105edff8678SStefan Berger } 106edff8678SStefan Berger 107edff8678SStefan Berger /* raise an interrupt if allowed */ 108edff8678SStefan Berger static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask) 109edff8678SStefan Berger { 110edff8678SStefan Berger if (!TPM_TIS_IS_VALID_LOCTY(locty)) { 111edff8678SStefan Berger return; 112edff8678SStefan Berger } 113edff8678SStefan Berger 1143d4960c7SMarc-André Lureau if ((s->loc[locty].inte & TPM_TIS_INT_ENABLED) && 1153d4960c7SMarc-André Lureau (s->loc[locty].inte & irqmask)) { 116fcbed221SStefan Berger trace_tpm_tis_raise_irq(irqmask); 1173d4960c7SMarc-André Lureau qemu_irq_raise(s->irq); 1183d4960c7SMarc-André Lureau s->loc[locty].ints |= irqmask; 119edff8678SStefan Berger } 120edff8678SStefan Berger } 121edff8678SStefan Berger 122edff8678SStefan Berger static uint32_t tpm_tis_check_request_use_except(TPMState *s, uint8_t locty) 123edff8678SStefan Berger { 124edff8678SStefan Berger uint8_t l; 125edff8678SStefan Berger 126edff8678SStefan Berger for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 127edff8678SStefan Berger if (l == locty) { 128edff8678SStefan Berger continue; 129edff8678SStefan Berger } 1303d4960c7SMarc-André Lureau if ((s->loc[l].access & TPM_TIS_ACCESS_REQUEST_USE)) { 131edff8678SStefan Berger return 1; 132edff8678SStefan Berger } 133edff8678SStefan Berger } 134edff8678SStefan Berger 135edff8678SStefan Berger return 0; 136edff8678SStefan Berger } 137edff8678SStefan Berger 138edff8678SStefan Berger static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty) 139edff8678SStefan Berger { 1403d4960c7SMarc-André Lureau bool change = (s->active_locty != new_active_locty); 141edff8678SStefan Berger bool is_seize; 142edff8678SStefan Berger uint8_t mask; 143edff8678SStefan Berger 1443d4960c7SMarc-André Lureau if (change && TPM_TIS_IS_VALID_LOCTY(s->active_locty)) { 145edff8678SStefan Berger is_seize = TPM_TIS_IS_VALID_LOCTY(new_active_locty) && 1463d4960c7SMarc-André Lureau s->loc[new_active_locty].access & TPM_TIS_ACCESS_SEIZE; 147edff8678SStefan Berger 148edff8678SStefan Berger if (is_seize) { 149edff8678SStefan Berger mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY); 150edff8678SStefan Berger } else { 151edff8678SStefan Berger mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY| 152edff8678SStefan Berger TPM_TIS_ACCESS_REQUEST_USE); 153edff8678SStefan Berger } 154edff8678SStefan Berger /* reset flags on the old active locality */ 1553d4960c7SMarc-André Lureau s->loc[s->active_locty].access &= mask; 156edff8678SStefan Berger 157edff8678SStefan Berger if (is_seize) { 1583d4960c7SMarc-André Lureau s->loc[s->active_locty].access |= TPM_TIS_ACCESS_BEEN_SEIZED; 159edff8678SStefan Berger } 160edff8678SStefan Berger } 161edff8678SStefan Berger 1623d4960c7SMarc-André Lureau s->active_locty = new_active_locty; 163edff8678SStefan Berger 164fcbed221SStefan Berger trace_tpm_tis_new_active_locality(s->active_locty); 165edff8678SStefan Berger 166edff8678SStefan Berger if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) { 167edff8678SStefan Berger /* set flags on the new active locality */ 1683d4960c7SMarc-André Lureau s->loc[new_active_locty].access |= TPM_TIS_ACCESS_ACTIVE_LOCALITY; 1693d4960c7SMarc-André Lureau s->loc[new_active_locty].access &= ~(TPM_TIS_ACCESS_REQUEST_USE | 170edff8678SStefan Berger TPM_TIS_ACCESS_SEIZE); 171edff8678SStefan Berger } 172edff8678SStefan Berger 173edff8678SStefan Berger if (change) { 1743d4960c7SMarc-André Lureau tpm_tis_raise_irq(s, s->active_locty, TPM_TIS_INT_LOCALITY_CHANGED); 175edff8678SStefan Berger } 176edff8678SStefan Berger } 177edff8678SStefan Berger 178edff8678SStefan Berger /* abort -- this function switches the locality */ 1790f5faee3SStefan Berger static void tpm_tis_abort(TPMState *s) 180edff8678SStefan Berger { 181f999d81bSStefan Berger s->rw_offset = 0; 182edff8678SStefan Berger 183fcbed221SStefan Berger trace_tpm_tis_abort(s->next_locty); 184edff8678SStefan Berger 185edff8678SStefan Berger /* 186edff8678SStefan Berger * Need to react differently depending on who's aborting now and 187edff8678SStefan Berger * which locality will become active afterwards. 188edff8678SStefan Berger */ 1893d4960c7SMarc-André Lureau if (s->aborting_locty == s->next_locty) { 1903d4960c7SMarc-André Lureau s->loc[s->aborting_locty].state = TPM_TIS_STATE_READY; 1913d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[s->aborting_locty], 192fd859081SStefan Berger TPM_TIS_STS_COMMAND_READY); 1933d4960c7SMarc-André Lureau tpm_tis_raise_irq(s, s->aborting_locty, TPM_TIS_INT_COMMAND_READY); 194edff8678SStefan Berger } 195edff8678SStefan Berger 196edff8678SStefan Berger /* locality after abort is another one than the current one */ 1973d4960c7SMarc-André Lureau tpm_tis_new_active_locality(s, s->next_locty); 198edff8678SStefan Berger 1993d4960c7SMarc-André Lureau s->next_locty = TPM_TIS_NO_LOCALITY; 200edff8678SStefan Berger /* nobody's aborting a command anymore */ 2013d4960c7SMarc-André Lureau s->aborting_locty = TPM_TIS_NO_LOCALITY; 202edff8678SStefan Berger } 203edff8678SStefan Berger 204edff8678SStefan Berger /* prepare aborting current command */ 205edff8678SStefan Berger static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty) 206edff8678SStefan Berger { 207edff8678SStefan Berger uint8_t busy_locty; 208edff8678SStefan Berger 209e92b63eaSStefan Berger assert(TPM_TIS_IS_VALID_LOCTY(newlocty)); 210e92b63eaSStefan Berger 211e92b63eaSStefan Berger s->aborting_locty = locty; /* may also be TPM_TIS_NO_LOCALITY */ 2123d4960c7SMarc-André Lureau s->next_locty = newlocty; /* locality after successful abort */ 213edff8678SStefan Berger 214edff8678SStefan Berger /* 215edff8678SStefan Berger * only abort a command using an interrupt if currently executing 216edff8678SStefan Berger * a command AND if there's a valid connection to the vTPM. 217edff8678SStefan Berger */ 218edff8678SStefan Berger for (busy_locty = 0; busy_locty < TPM_TIS_NUM_LOCALITIES; busy_locty++) { 2193d4960c7SMarc-André Lureau if (s->loc[busy_locty].state == TPM_TIS_STATE_EXECUTION) { 220edff8678SStefan Berger /* 221edff8678SStefan Berger * request the backend to cancel. Some backends may not 222edff8678SStefan Berger * support it 223edff8678SStefan Berger */ 2248f0605ccSStefan Berger tpm_backend_cancel_cmd(s->be_driver); 225edff8678SStefan Berger return; 226edff8678SStefan Berger } 227edff8678SStefan Berger } 228edff8678SStefan Berger 2290f5faee3SStefan Berger tpm_tis_abort(s); 230edff8678SStefan Berger } 231edff8678SStefan Berger 23268999059SMarc-André Lureau /* 23368999059SMarc-André Lureau * Callback from the TPM to indicate that the response was received. 23468999059SMarc-André Lureau */ 235ac90053dSEric Auger void tpm_tis_request_completed(TPMState *s, int ret) 236edff8678SStefan Berger { 2370e43b7e6SMarc-André Lureau uint8_t locty = s->cmd.locty; 23868999059SMarc-André Lureau uint8_t l; 23968999059SMarc-André Lureau 240a639f961SStefan Berger assert(TPM_TIS_IS_VALID_LOCTY(locty)); 241a639f961SStefan Berger 24268999059SMarc-André Lureau if (s->cmd.selftest_done) { 24368999059SMarc-André Lureau for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 2446a50bb98SPrasad J Pandit s->loc[l].sts |= TPM_TIS_STS_SELFTEST_DONE; 24568999059SMarc-André Lureau } 24668999059SMarc-André Lureau } 247edff8678SStefan Berger 2486a8a2354SMarc-André Lureau /* FIXME: report error if ret != 0 */ 2493d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 250fd859081SStefan Berger TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE); 2513d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_COMPLETION; 252f999d81bSStefan Berger s->rw_offset = 0; 253edff8678SStefan Berger 2543688d73bSStefan Berger tpm_util_show_buffer(s->buffer, s->be_buffer_size, "From TPM"); 255298d8b81SStefan Berger 2563d4960c7SMarc-André Lureau if (TPM_TIS_IS_VALID_LOCTY(s->next_locty)) { 2570f5faee3SStefan Berger tpm_tis_abort(s); 258edff8678SStefan Berger } 259edff8678SStefan Berger 260edff8678SStefan Berger tpm_tis_raise_irq(s, locty, 261edff8678SStefan Berger TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID); 262edff8678SStefan Berger } 263edff8678SStefan Berger 264edff8678SStefan Berger /* 265edff8678SStefan Berger * Read a byte of response data 266edff8678SStefan Berger */ 267edff8678SStefan Berger static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty) 268edff8678SStefan Berger { 269edff8678SStefan Berger uint32_t ret = TPM_TIS_NO_DATA_BYTE; 270edff8678SStefan Berger uint16_t len; 271edff8678SStefan Berger 2723d4960c7SMarc-André Lureau if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) { 273c5496b97SStefan Berger len = MIN(tpm_cmd_get_size(&s->buffer), 274e6b703f6SStefan Berger s->be_buffer_size); 275edff8678SStefan Berger 276f999d81bSStefan Berger ret = s->buffer[s->rw_offset++]; 277f999d81bSStefan Berger if (s->rw_offset >= len) { 278edff8678SStefan Berger /* got last byte */ 2793d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 280edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); 281edff8678SStefan Berger } 282fcbed221SStefan Berger trace_tpm_tis_data_read(ret, s->rw_offset - 1); 283edff8678SStefan Berger } 284edff8678SStefan Berger 285edff8678SStefan Berger return ret; 286edff8678SStefan Berger } 287edff8678SStefan Berger 2888db7c415SStefan Berger #ifdef DEBUG_TIS 289ca75c421SEric Auger static void tpm_tis_dump_state(TPMState *s, hwaddr addr) 2908db7c415SStefan Berger { 2918db7c415SStefan Berger static const unsigned regs[] = { 2928db7c415SStefan Berger TPM_TIS_REG_ACCESS, 2938db7c415SStefan Berger TPM_TIS_REG_INT_ENABLE, 2948db7c415SStefan Berger TPM_TIS_REG_INT_VECTOR, 2958db7c415SStefan Berger TPM_TIS_REG_INT_STATUS, 2968db7c415SStefan Berger TPM_TIS_REG_INTF_CAPABILITY, 2978db7c415SStefan Berger TPM_TIS_REG_STS, 2988db7c415SStefan Berger TPM_TIS_REG_DID_VID, 2998db7c415SStefan Berger TPM_TIS_REG_RID, 3008db7c415SStefan Berger 0xfff}; 3018db7c415SStefan Berger int idx; 3028db7c415SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 3038db7c415SStefan Berger hwaddr base = addr & ~0xfff; 3048db7c415SStefan Berger 305fcbed221SStefan Berger printf("tpm_tis: active locality : %d\n" 3068db7c415SStefan Berger "tpm_tis: state of locality %d : %d\n" 3078db7c415SStefan Berger "tpm_tis: register dump:\n", 3083d4960c7SMarc-André Lureau s->active_locty, 3093d4960c7SMarc-André Lureau locty, s->loc[locty].state); 3108db7c415SStefan Berger 3118db7c415SStefan Berger for (idx = 0; regs[idx] != 0xfff; idx++) { 312fcbed221SStefan Berger printf("tpm_tis: 0x%04x : 0x%08x\n", regs[idx], 313ca75c421SEric Auger (int)tpm_tis_mmio_read(s, base + regs[idx], 4)); 3148db7c415SStefan Berger } 3158db7c415SStefan Berger 316fcbed221SStefan Berger printf("tpm_tis: r/w offset : %d\n" 3178db7c415SStefan Berger "tpm_tis: result buffer : ", 318f999d81bSStefan Berger s->rw_offset); 3198db7c415SStefan Berger for (idx = 0; 320c5496b97SStefan Berger idx < MIN(tpm_cmd_get_size(&s->buffer), s->be_buffer_size); 3218db7c415SStefan Berger idx++) { 322fcbed221SStefan Berger printf("%c%02x%s", 323f999d81bSStefan Berger s->rw_offset == idx ? '>' : ' ', 324c5496b97SStefan Berger s->buffer[idx], 3258db7c415SStefan Berger ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : ""); 3268db7c415SStefan Berger } 327fcbed221SStefan Berger printf("\n"); 3288db7c415SStefan Berger } 3298db7c415SStefan Berger #endif 3308db7c415SStefan Berger 331edff8678SStefan Berger /* 332edff8678SStefan Berger * Read a register of the TIS interface 333edff8678SStefan Berger * See specs pages 33-63 for description of the registers 334edff8678SStefan Berger */ 335edff8678SStefan Berger static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, 336edff8678SStefan Berger unsigned size) 337edff8678SStefan Berger { 338edff8678SStefan Berger TPMState *s = opaque; 339edff8678SStefan Berger uint16_t offset = addr & 0xffc; 340edff8678SStefan Berger uint8_t shift = (addr & 0x3) * 8; 341edff8678SStefan Berger uint32_t val = 0xffffffff; 342edff8678SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 343edff8678SStefan Berger uint32_t avail; 344feeb755fSStefan Berger uint8_t v; 345edff8678SStefan Berger 3468f0605ccSStefan Berger if (tpm_backend_had_startup_error(s->be_driver)) { 3476cd65969SStefan Berger return 0; 348edff8678SStefan Berger } 349edff8678SStefan Berger 350edff8678SStefan Berger switch (offset) { 351edff8678SStefan Berger case TPM_TIS_REG_ACCESS: 352edff8678SStefan Berger /* never show the SEIZE flag even though we use it internally */ 3533d4960c7SMarc-André Lureau val = s->loc[locty].access & ~TPM_TIS_ACCESS_SEIZE; 354edff8678SStefan Berger /* the pending flag is always calculated */ 355edff8678SStefan Berger if (tpm_tis_check_request_use_except(s, locty)) { 356edff8678SStefan Berger val |= TPM_TIS_ACCESS_PENDING_REQUEST; 357edff8678SStefan Berger } 3588f0605ccSStefan Berger val |= !tpm_backend_get_tpm_established_flag(s->be_driver); 359edff8678SStefan Berger break; 360edff8678SStefan Berger case TPM_TIS_REG_INT_ENABLE: 3613d4960c7SMarc-André Lureau val = s->loc[locty].inte; 362edff8678SStefan Berger break; 363edff8678SStefan Berger case TPM_TIS_REG_INT_VECTOR: 3643d4960c7SMarc-André Lureau val = s->irq_num; 365edff8678SStefan Berger break; 366edff8678SStefan Berger case TPM_TIS_REG_INT_STATUS: 3673d4960c7SMarc-André Lureau val = s->loc[locty].ints; 368edff8678SStefan Berger break; 369edff8678SStefan Berger case TPM_TIS_REG_INTF_CAPABILITY: 370116694c3SStefan Berger switch (s->be_tpm_version) { 371116694c3SStefan Berger case TPM_VERSION_UNSPEC: 372116694c3SStefan Berger val = 0; 373116694c3SStefan Berger break; 374116694c3SStefan Berger case TPM_VERSION_1_2: 375116694c3SStefan Berger val = TPM_TIS_CAPABILITIES_SUPPORTED1_3; 376116694c3SStefan Berger break; 377116694c3SStefan Berger case TPM_VERSION_2_0: 378116694c3SStefan Berger val = TPM_TIS_CAPABILITIES_SUPPORTED2_0; 379116694c3SStefan Berger break; 380116694c3SStefan Berger } 381edff8678SStefan Berger break; 382edff8678SStefan Berger case TPM_TIS_REG_STS: 3833d4960c7SMarc-André Lureau if (s->active_locty == locty) { 3843d4960c7SMarc-André Lureau if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) { 385edff8678SStefan Berger val = TPM_TIS_BURST_COUNT( 386c5496b97SStefan Berger MIN(tpm_cmd_get_size(&s->buffer), 387e6b703f6SStefan Berger s->be_buffer_size) 388f999d81bSStefan Berger - s->rw_offset) | s->loc[locty].sts; 389edff8678SStefan Berger } else { 390f999d81bSStefan Berger avail = s->be_buffer_size - s->rw_offset; 391edff8678SStefan Berger /* 392edff8678SStefan Berger * byte-sized reads should not return 0x00 for 0x100 393edff8678SStefan Berger * available bytes. 394edff8678SStefan Berger */ 395edff8678SStefan Berger if (size == 1 && avail > 0xff) { 396edff8678SStefan Berger avail = 0xff; 397edff8678SStefan Berger } 3983d4960c7SMarc-André Lureau val = TPM_TIS_BURST_COUNT(avail) | s->loc[locty].sts; 399edff8678SStefan Berger } 400edff8678SStefan Berger } 401edff8678SStefan Berger break; 402edff8678SStefan Berger case TPM_TIS_REG_DATA_FIFO: 4032eae8c75SStefan Berger case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END: 4043d4960c7SMarc-André Lureau if (s->active_locty == locty) { 405feeb755fSStefan Berger if (size > 4 - (addr & 0x3)) { 406feeb755fSStefan Berger /* prevent access beyond FIFO */ 407feeb755fSStefan Berger size = 4 - (addr & 0x3); 408feeb755fSStefan Berger } 409feeb755fSStefan Berger val = 0; 410feeb755fSStefan Berger shift = 0; 411feeb755fSStefan Berger while (size > 0) { 4123d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 413edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 414feeb755fSStefan Berger v = tpm_tis_data_read(s, locty); 415edff8678SStefan Berger break; 416edff8678SStefan Berger default: 417feeb755fSStefan Berger v = TPM_TIS_NO_DATA_BYTE; 418edff8678SStefan Berger break; 419edff8678SStefan Berger } 420feeb755fSStefan Berger val |= (v << shift); 421feeb755fSStefan Berger shift += 8; 422feeb755fSStefan Berger size--; 423feeb755fSStefan Berger } 424feeb755fSStefan Berger shift = 0; /* no more adjustments */ 425edff8678SStefan Berger } 426edff8678SStefan Berger break; 427116694c3SStefan Berger case TPM_TIS_REG_INTERFACE_ID: 4283d4960c7SMarc-André Lureau val = s->loc[locty].iface_id; 429116694c3SStefan Berger break; 430edff8678SStefan Berger case TPM_TIS_REG_DID_VID: 431edff8678SStefan Berger val = (TPM_TIS_TPM_DID << 16) | TPM_TIS_TPM_VID; 432edff8678SStefan Berger break; 433edff8678SStefan Berger case TPM_TIS_REG_RID: 434edff8678SStefan Berger val = TPM_TIS_TPM_RID; 435edff8678SStefan Berger break; 4368db7c415SStefan Berger #ifdef DEBUG_TIS 4378db7c415SStefan Berger case TPM_TIS_REG_DEBUG: 438ca75c421SEric Auger tpm_tis_dump_state(s, addr); 4398db7c415SStefan Berger break; 4408db7c415SStefan Berger #endif 441edff8678SStefan Berger } 442edff8678SStefan Berger 443edff8678SStefan Berger if (shift) { 444edff8678SStefan Berger val >>= shift; 445edff8678SStefan Berger } 446edff8678SStefan Berger 447fcbed221SStefan Berger trace_tpm_tis_mmio_read(size, addr, val); 448edff8678SStefan Berger 449edff8678SStefan Berger return val; 450edff8678SStefan Berger } 451edff8678SStefan Berger 452edff8678SStefan Berger /* 453bbadfb2eSNinad Palsule * A wrapper read function so that it can be directly called without 454bbadfb2eSNinad Palsule * mmio. 455bbadfb2eSNinad Palsule */ 456bbadfb2eSNinad Palsule uint32_t tpm_tis_read_data(TPMState *s, hwaddr addr, unsigned size) 457bbadfb2eSNinad Palsule { 458bbadfb2eSNinad Palsule return tpm_tis_mmio_read(s, addr, size); 459bbadfb2eSNinad Palsule } 460bbadfb2eSNinad Palsule 461bbadfb2eSNinad Palsule /* 462bbadfb2eSNinad Palsule * Calculate current data buffer checksum 463bbadfb2eSNinad Palsule */ 464bbadfb2eSNinad Palsule uint16_t tpm_tis_get_checksum(TPMState *s) 465bbadfb2eSNinad Palsule { 466bbadfb2eSNinad Palsule return bswap16(crc_ccitt(0, s->buffer, s->rw_offset)); 467bbadfb2eSNinad Palsule } 468bbadfb2eSNinad Palsule 469bbadfb2eSNinad Palsule /* 470edff8678SStefan Berger * Write a value to a register of the TIS interface 471edff8678SStefan Berger * See specs pages 33-63 for description of the registers 472edff8678SStefan Berger */ 473ff2bc0c1SMarc-André Lureau static void tpm_tis_mmio_write(void *opaque, hwaddr addr, 474ff2bc0c1SMarc-André Lureau uint64_t val, unsigned size) 475edff8678SStefan Berger { 476edff8678SStefan Berger TPMState *s = opaque; 477feeb755fSStefan Berger uint16_t off = addr & 0xffc; 478feeb755fSStefan Berger uint8_t shift = (addr & 0x3) * 8; 479edff8678SStefan Berger uint8_t locty = tpm_tis_locality_from_addr(addr); 480edff8678SStefan Berger uint8_t active_locty, l; 481edff8678SStefan Berger int c, set_new_locty = 1; 482edff8678SStefan Berger uint16_t len; 483feeb755fSStefan Berger uint32_t mask = (size == 1) ? 0xff : ((size == 2) ? 0xffff : ~0); 484edff8678SStefan Berger 485fcbed221SStefan Berger trace_tpm_tis_mmio_write(size, addr, val); 486edff8678SStefan Berger 487ff2bc0c1SMarc-André Lureau if (locty == 4) { 488fcbed221SStefan Berger trace_tpm_tis_mmio_write_locty4(); 489edff8678SStefan Berger return; 490edff8678SStefan Berger } 491edff8678SStefan Berger 4928f0605ccSStefan Berger if (tpm_backend_had_startup_error(s->be_driver)) { 493edff8678SStefan Berger return; 494edff8678SStefan Berger } 495edff8678SStefan Berger 496feeb755fSStefan Berger val &= mask; 497feeb755fSStefan Berger 498feeb755fSStefan Berger if (shift) { 499feeb755fSStefan Berger val <<= shift; 500feeb755fSStefan Berger mask <<= shift; 501feeb755fSStefan Berger } 502feeb755fSStefan Berger 503feeb755fSStefan Berger mask ^= 0xffffffff; 504feeb755fSStefan Berger 505edff8678SStefan Berger switch (off) { 506edff8678SStefan Berger case TPM_TIS_REG_ACCESS: 507edff8678SStefan Berger 508edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_SEIZE)) { 509edff8678SStefan Berger val &= ~(TPM_TIS_ACCESS_REQUEST_USE | 510edff8678SStefan Berger TPM_TIS_ACCESS_ACTIVE_LOCALITY); 511edff8678SStefan Berger } 512edff8678SStefan Berger 5133d4960c7SMarc-André Lureau active_locty = s->active_locty; 514edff8678SStefan Berger 515edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) { 516edff8678SStefan Berger /* give up locality if currently owned */ 5173d4960c7SMarc-André Lureau if (s->active_locty == locty) { 518fcbed221SStefan Berger trace_tpm_tis_mmio_write_release_locty(locty); 519edff8678SStefan Berger 520edff8678SStefan Berger uint8_t newlocty = TPM_TIS_NO_LOCALITY; 521edff8678SStefan Berger /* anybody wants the locality ? */ 522edff8678SStefan Berger for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) { 5233d4960c7SMarc-André Lureau if ((s->loc[c].access & TPM_TIS_ACCESS_REQUEST_USE)) { 524fcbed221SStefan Berger trace_tpm_tis_mmio_write_locty_req_use(c); 525edff8678SStefan Berger newlocty = c; 526edff8678SStefan Berger break; 527edff8678SStefan Berger } 528edff8678SStefan Berger } 529fcbed221SStefan Berger trace_tpm_tis_mmio_write_next_locty(newlocty); 530edff8678SStefan Berger 531edff8678SStefan Berger if (TPM_TIS_IS_VALID_LOCTY(newlocty)) { 532edff8678SStefan Berger set_new_locty = 0; 533edff8678SStefan Berger tpm_tis_prep_abort(s, locty, newlocty); 534edff8678SStefan Berger } else { 535edff8678SStefan Berger active_locty = TPM_TIS_NO_LOCALITY; 536edff8678SStefan Berger } 537edff8678SStefan Berger } else { 538edff8678SStefan Berger /* not currently the owner; clear a pending request */ 5393d4960c7SMarc-André Lureau s->loc[locty].access &= ~TPM_TIS_ACCESS_REQUEST_USE; 540edff8678SStefan Berger } 541edff8678SStefan Berger } 542edff8678SStefan Berger 543edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_BEEN_SEIZED)) { 5443d4960c7SMarc-André Lureau s->loc[locty].access &= ~TPM_TIS_ACCESS_BEEN_SEIZED; 545edff8678SStefan Berger } 546edff8678SStefan Berger 547edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_SEIZE)) { 548edff8678SStefan Berger /* 549edff8678SStefan Berger * allow seize if a locality is active and the requesting 550edff8678SStefan Berger * locality is higher than the one that's active 551edff8678SStefan Berger * OR 552edff8678SStefan Berger * allow seize for requesting locality if no locality is 553edff8678SStefan Berger * active 554edff8678SStefan Berger */ 5553d4960c7SMarc-André Lureau while ((TPM_TIS_IS_VALID_LOCTY(s->active_locty) && 5563d4960c7SMarc-André Lureau locty > s->active_locty) || 5573d4960c7SMarc-André Lureau !TPM_TIS_IS_VALID_LOCTY(s->active_locty)) { 558aadad398SJafar Abdi bool higher_seize = false; 559edff8678SStefan Berger 560edff8678SStefan Berger /* already a pending SEIZE ? */ 5613d4960c7SMarc-André Lureau if ((s->loc[locty].access & TPM_TIS_ACCESS_SEIZE)) { 562edff8678SStefan Berger break; 563edff8678SStefan Berger } 564edff8678SStefan Berger 565edff8678SStefan Berger /* check for ongoing seize by a higher locality */ 566edff8678SStefan Berger for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES; l++) { 5673d4960c7SMarc-André Lureau if ((s->loc[l].access & TPM_TIS_ACCESS_SEIZE)) { 568aadad398SJafar Abdi higher_seize = true; 569edff8678SStefan Berger break; 570edff8678SStefan Berger } 571edff8678SStefan Berger } 572edff8678SStefan Berger 573edff8678SStefan Berger if (higher_seize) { 574edff8678SStefan Berger break; 575edff8678SStefan Berger } 576edff8678SStefan Berger 577edff8678SStefan Berger /* cancel any seize by a lower locality */ 57837b55d67SLiam Merwick for (l = 0; l < locty; l++) { 5793d4960c7SMarc-André Lureau s->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE; 580edff8678SStefan Berger } 581edff8678SStefan Berger 5823d4960c7SMarc-André Lureau s->loc[locty].access |= TPM_TIS_ACCESS_SEIZE; 583fcbed221SStefan Berger 584fcbed221SStefan Berger trace_tpm_tis_mmio_write_locty_seized(locty, s->active_locty); 585fcbed221SStefan Berger trace_tpm_tis_mmio_write_init_abort(); 586fcbed221SStefan Berger 587edff8678SStefan Berger set_new_locty = 0; 5883d4960c7SMarc-André Lureau tpm_tis_prep_abort(s, s->active_locty, locty); 589edff8678SStefan Berger break; 590edff8678SStefan Berger } 591edff8678SStefan Berger } 592edff8678SStefan Berger 593edff8678SStefan Berger if ((val & TPM_TIS_ACCESS_REQUEST_USE)) { 5943d4960c7SMarc-André Lureau if (s->active_locty != locty) { 5953d4960c7SMarc-André Lureau if (TPM_TIS_IS_VALID_LOCTY(s->active_locty)) { 5963d4960c7SMarc-André Lureau s->loc[locty].access |= TPM_TIS_ACCESS_REQUEST_USE; 597edff8678SStefan Berger } else { 598edff8678SStefan Berger /* no locality active -> make this one active now */ 599edff8678SStefan Berger active_locty = locty; 600edff8678SStefan Berger } 601edff8678SStefan Berger } 602edff8678SStefan Berger } 603edff8678SStefan Berger 604edff8678SStefan Berger if (set_new_locty) { 605edff8678SStefan Berger tpm_tis_new_active_locality(s, active_locty); 606edff8678SStefan Berger } 607edff8678SStefan Berger 608edff8678SStefan Berger break; 609edff8678SStefan Berger case TPM_TIS_REG_INT_ENABLE: 6103d4960c7SMarc-André Lureau s->loc[locty].inte &= mask; 6113d4960c7SMarc-André Lureau s->loc[locty].inte |= (val & (TPM_TIS_INT_ENABLED | 612edff8678SStefan Berger TPM_TIS_INT_POLARITY_MASK | 613edff8678SStefan Berger TPM_TIS_INTERRUPTS_SUPPORTED)); 614edff8678SStefan Berger break; 615edff8678SStefan Berger case TPM_TIS_REG_INT_VECTOR: 616edff8678SStefan Berger /* hard wired -- ignore */ 617edff8678SStefan Berger break; 618edff8678SStefan Berger case TPM_TIS_REG_INT_STATUS: 619edff8678SStefan Berger /* clearing of interrupt flags */ 620edff8678SStefan Berger if (((val & TPM_TIS_INTERRUPTS_SUPPORTED)) && 6213d4960c7SMarc-André Lureau (s->loc[locty].ints & TPM_TIS_INTERRUPTS_SUPPORTED)) { 6223d4960c7SMarc-André Lureau s->loc[locty].ints &= ~val; 6233d4960c7SMarc-André Lureau if (s->loc[locty].ints == 0) { 6243d4960c7SMarc-André Lureau qemu_irq_lower(s->irq); 625fcbed221SStefan Berger trace_tpm_tis_mmio_write_lowering_irq(); 626edff8678SStefan Berger } 627edff8678SStefan Berger } 6283d4960c7SMarc-André Lureau s->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED); 629edff8678SStefan Berger break; 630edff8678SStefan Berger case TPM_TIS_REG_STS: 6313d4960c7SMarc-André Lureau if (s->active_locty != locty) { 632edff8678SStefan Berger break; 633edff8678SStefan Berger } 634edff8678SStefan Berger 635116694c3SStefan Berger if (s->be_tpm_version == TPM_VERSION_2_0) { 636116694c3SStefan Berger /* some flags that are only supported for TPM 2 */ 637116694c3SStefan Berger if (val & TPM_TIS_STS_COMMAND_CANCEL) { 6383d4960c7SMarc-André Lureau if (s->loc[locty].state == TPM_TIS_STATE_EXECUTION) { 639116694c3SStefan Berger /* 640116694c3SStefan Berger * request the backend to cancel. Some backends may not 641116694c3SStefan Berger * support it 642116694c3SStefan Berger */ 643116694c3SStefan Berger tpm_backend_cancel_cmd(s->be_driver); 644116694c3SStefan Berger } 645116694c3SStefan Berger } 646116694c3SStefan Berger 647116694c3SStefan Berger if (val & TPM_TIS_STS_RESET_ESTABLISHMENT_BIT) { 648116694c3SStefan Berger if (locty == 3 || locty == 4) { 649116694c3SStefan Berger tpm_backend_reset_tpm_established_flag(s->be_driver, locty); 650116694c3SStefan Berger } 651116694c3SStefan Berger } 652116694c3SStefan Berger } 653116694c3SStefan Berger 654edff8678SStefan Berger val &= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO | 655edff8678SStefan Berger TPM_TIS_STS_RESPONSE_RETRY); 656edff8678SStefan Berger 657edff8678SStefan Berger if (val == TPM_TIS_STS_COMMAND_READY) { 6583d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 659edff8678SStefan Berger 660edff8678SStefan Berger case TPM_TIS_STATE_READY: 661f999d81bSStefan Berger s->rw_offset = 0; 662edff8678SStefan Berger break; 663edff8678SStefan Berger 664edff8678SStefan Berger case TPM_TIS_STATE_IDLE: 6653d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_COMMAND_READY); 6663d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_READY; 667edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); 668edff8678SStefan Berger break; 669edff8678SStefan Berger 670edff8678SStefan Berger case TPM_TIS_STATE_EXECUTION: 671edff8678SStefan Berger case TPM_TIS_STATE_RECEPTION: 672edff8678SStefan Berger /* abort currently running command */ 673fcbed221SStefan Berger trace_tpm_tis_mmio_write_init_abort(); 674edff8678SStefan Berger tpm_tis_prep_abort(s, locty, locty); 675edff8678SStefan Berger break; 676edff8678SStefan Berger 677edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 678f999d81bSStefan Berger s->rw_offset = 0; 679edff8678SStefan Berger /* shortcut to ready state with C/R set */ 6803d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_READY; 6813d4960c7SMarc-André Lureau if (!(s->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) { 6823d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 683fd859081SStefan Berger TPM_TIS_STS_COMMAND_READY); 684edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); 685edff8678SStefan Berger } 6863d4960c7SMarc-André Lureau s->loc[locty].sts &= ~(TPM_TIS_STS_DATA_AVAILABLE); 687edff8678SStefan Berger break; 688edff8678SStefan Berger 689edff8678SStefan Berger } 690edff8678SStefan Berger } else if (val == TPM_TIS_STS_TPM_GO) { 6913d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 692edff8678SStefan Berger case TPM_TIS_STATE_RECEPTION: 6933d4960c7SMarc-André Lureau if ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) == 0) { 694edff8678SStefan Berger tpm_tis_tpm_send(s, locty); 695edff8678SStefan Berger } 696edff8678SStefan Berger break; 697edff8678SStefan Berger default: 698edff8678SStefan Berger /* ignore */ 699edff8678SStefan Berger break; 700edff8678SStefan Berger } 701edff8678SStefan Berger } else if (val == TPM_TIS_STS_RESPONSE_RETRY) { 7023d4960c7SMarc-André Lureau switch (s->loc[locty].state) { 703edff8678SStefan Berger case TPM_TIS_STATE_COMPLETION: 704f999d81bSStefan Berger s->rw_offset = 0; 7053d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 706fd859081SStefan Berger TPM_TIS_STS_VALID| 707fd859081SStefan Berger TPM_TIS_STS_DATA_AVAILABLE); 708edff8678SStefan Berger break; 709edff8678SStefan Berger default: 710edff8678SStefan Berger /* ignore */ 711edff8678SStefan Berger break; 712edff8678SStefan Berger } 713edff8678SStefan Berger } 714edff8678SStefan Berger break; 715edff8678SStefan Berger case TPM_TIS_REG_DATA_FIFO: 7162eae8c75SStefan Berger case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END: 717edff8678SStefan Berger /* data fifo */ 7183d4960c7SMarc-André Lureau if (s->active_locty != locty) { 719edff8678SStefan Berger break; 720edff8678SStefan Berger } 721edff8678SStefan Berger 7223d4960c7SMarc-André Lureau if (s->loc[locty].state == TPM_TIS_STATE_IDLE || 7233d4960c7SMarc-André Lureau s->loc[locty].state == TPM_TIS_STATE_EXECUTION || 7243d4960c7SMarc-André Lureau s->loc[locty].state == TPM_TIS_STATE_COMPLETION) { 725edff8678SStefan Berger /* drop the byte */ 726edff8678SStefan Berger } else { 727fcbed221SStefan Berger trace_tpm_tis_mmio_write_data2send(val, size); 7283d4960c7SMarc-André Lureau if (s->loc[locty].state == TPM_TIS_STATE_READY) { 7293d4960c7SMarc-André Lureau s->loc[locty].state = TPM_TIS_STATE_RECEPTION; 7303d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 731fd859081SStefan Berger TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); 732edff8678SStefan Berger } 733edff8678SStefan Berger 734feeb755fSStefan Berger val >>= shift; 735feeb755fSStefan Berger if (size > 4 - (addr & 0x3)) { 736feeb755fSStefan Berger /* prevent access beyond FIFO */ 737feeb755fSStefan Berger size = 4 - (addr & 0x3); 738feeb755fSStefan Berger } 739feeb755fSStefan Berger 7403d4960c7SMarc-André Lureau while ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) && size > 0) { 741f999d81bSStefan Berger if (s->rw_offset < s->be_buffer_size) { 742f999d81bSStefan Berger s->buffer[s->rw_offset++] = 743e6b703f6SStefan Berger (uint8_t)val; 744feeb755fSStefan Berger val >>= 8; 745feeb755fSStefan Berger size--; 746edff8678SStefan Berger } else { 7473d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 748edff8678SStefan Berger } 749edff8678SStefan Berger } 750edff8678SStefan Berger 751edff8678SStefan Berger /* check for complete packet */ 752f999d81bSStefan Berger if (s->rw_offset > 5 && 7533d4960c7SMarc-André Lureau (s->loc[locty].sts & TPM_TIS_STS_EXPECT)) { 754edff8678SStefan Berger /* we have a packet length - see if we have all of it */ 7553d4960c7SMarc-André Lureau bool need_irq = !(s->loc[locty].sts & TPM_TIS_STS_VALID); 756d8383d61SMarc-André Lureau 757c5496b97SStefan Berger len = tpm_cmd_get_size(&s->buffer); 758f999d81bSStefan Berger if (len > s->rw_offset) { 7593d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], 760fd859081SStefan Berger TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); 761edff8678SStefan Berger } else { 762edff8678SStefan Berger /* packet complete */ 7633d4960c7SMarc-André Lureau tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 764edff8678SStefan Berger } 76529b558d8SStefan Berger if (need_irq) { 766edff8678SStefan Berger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); 767edff8678SStefan Berger } 768edff8678SStefan Berger } 769edff8678SStefan Berger } 770edff8678SStefan Berger break; 771116694c3SStefan Berger case TPM_TIS_REG_INTERFACE_ID: 772116694c3SStefan Berger if (val & TPM_TIS_IFACE_ID_INT_SEL_LOCK) { 773116694c3SStefan Berger for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 7743d4960c7SMarc-André Lureau s->loc[l].iface_id |= TPM_TIS_IFACE_ID_INT_SEL_LOCK; 775116694c3SStefan Berger } 776116694c3SStefan Berger } 777116694c3SStefan Berger break; 778edff8678SStefan Berger } 779edff8678SStefan Berger } 780edff8678SStefan Berger 781bbadfb2eSNinad Palsule /* 782bbadfb2eSNinad Palsule * A wrapper write function so that it can be directly called without 783bbadfb2eSNinad Palsule * mmio. 784bbadfb2eSNinad Palsule */ 785bbadfb2eSNinad Palsule void tpm_tis_write_data(TPMState *s, hwaddr addr, uint64_t val, uint32_t size) 786bbadfb2eSNinad Palsule { 787bbadfb2eSNinad Palsule tpm_tis_mmio_write(s, addr, val, size); 788bbadfb2eSNinad Palsule } 789bbadfb2eSNinad Palsule 790ac90053dSEric Auger const MemoryRegionOps tpm_tis_memory_ops = { 791edff8678SStefan Berger .read = tpm_tis_mmio_read, 792edff8678SStefan Berger .write = tpm_tis_mmio_write, 793edff8678SStefan Berger .endianness = DEVICE_LITTLE_ENDIAN, 794edff8678SStefan Berger .valid = { 795edff8678SStefan Berger .min_access_size = 1, 796edff8678SStefan Berger .max_access_size = 4, 797edff8678SStefan Berger }, 798edff8678SStefan Berger }; 799edff8678SStefan Berger 800edff8678SStefan Berger /* 8015cb18b3dSStefan Berger * Get the TPMVersion of the backend device being used 8025cb18b3dSStefan Berger */ 803ac90053dSEric Auger enum TPMVersion tpm_tis_get_tpm_version(TPMState *s) 8045cb18b3dSStefan Berger { 805ad4aca69SStefan Berger if (tpm_backend_had_startup_error(s->be_driver)) { 806ad4aca69SStefan Berger return TPM_VERSION_UNSPEC; 807ad4aca69SStefan Berger } 808ad4aca69SStefan Berger 8095cb18b3dSStefan Berger return tpm_backend_get_tpm_version(s->be_driver); 8105cb18b3dSStefan Berger } 8115cb18b3dSStefan Berger 8125cb18b3dSStefan Berger /* 813edff8678SStefan Berger * This function is called when the machine starts, resets or due to 814edff8678SStefan Berger * S3 resume. 815edff8678SStefan Berger */ 816ac90053dSEric Auger void tpm_tis_reset(TPMState *s) 817edff8678SStefan Berger { 818edff8678SStefan Berger int c; 819edff8678SStefan Berger 820116694c3SStefan Berger s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver); 8211af3d63eSStefan Berger s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->be_driver), 8221af3d63eSStefan Berger TPM_TIS_BUFFER_MAX); 823116694c3SStefan Berger 824ffab1be7SMarc-André Lureau if (s->ppi_enabled) { 825ffab1be7SMarc-André Lureau tpm_ppi_reset(&s->ppi); 826ffab1be7SMarc-André Lureau } 8278f0605ccSStefan Berger tpm_backend_reset(s->be_driver); 828edff8678SStefan Berger 8293d4960c7SMarc-André Lureau s->active_locty = TPM_TIS_NO_LOCALITY; 8303d4960c7SMarc-André Lureau s->next_locty = TPM_TIS_NO_LOCALITY; 8313d4960c7SMarc-André Lureau s->aborting_locty = TPM_TIS_NO_LOCALITY; 832edff8678SStefan Berger 833edff8678SStefan Berger for (c = 0; c < TPM_TIS_NUM_LOCALITIES; c++) { 8343d4960c7SMarc-André Lureau s->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS; 835116694c3SStefan Berger switch (s->be_tpm_version) { 836116694c3SStefan Berger case TPM_VERSION_UNSPEC: 837116694c3SStefan Berger break; 838116694c3SStefan Berger case TPM_VERSION_1_2: 8393d4960c7SMarc-André Lureau s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY1_2; 8403d4960c7SMarc-André Lureau s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3; 841116694c3SStefan Berger break; 842116694c3SStefan Berger case TPM_VERSION_2_0: 8433d4960c7SMarc-André Lureau s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY2_0; 8443d4960c7SMarc-André Lureau s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0; 845116694c3SStefan Berger break; 846116694c3SStefan Berger } 8473d4960c7SMarc-André Lureau s->loc[c].inte = TPM_TIS_INT_POLARITY_LOW_LEVEL; 8483d4960c7SMarc-André Lureau s->loc[c].ints = 0; 8493d4960c7SMarc-André Lureau s->loc[c].state = TPM_TIS_STATE_IDLE; 850edff8678SStefan Berger 851f999d81bSStefan Berger s->rw_offset = 0; 852edff8678SStefan Berger } 853edff8678SStefan Berger 854bcfd16feSStefan Berger if (tpm_backend_startup_tpm(s->be_driver, s->be_buffer_size) < 0) { 855bcfd16feSStefan Berger exit(1); 856bcfd16feSStefan Berger } 857edff8678SStefan Berger } 858edff8678SStefan Berger 8599ec08c48SStefan Berger /* persistent state handling */ 8609ec08c48SStefan Berger 861ac90053dSEric Auger int tpm_tis_pre_save(TPMState *s) 8629ec08c48SStefan Berger { 8639ec08c48SStefan Berger uint8_t locty = s->active_locty; 8649ec08c48SStefan Berger 8659ec08c48SStefan Berger trace_tpm_tis_pre_save(locty, s->rw_offset); 8669ec08c48SStefan Berger 8679ec08c48SStefan Berger if (DEBUG_TIS) { 868ca75c421SEric Auger tpm_tis_dump_state(s, 0); 8699ec08c48SStefan Berger } 8709ec08c48SStefan Berger 8719ec08c48SStefan Berger /* 8729ec08c48SStefan Berger * Synchronize with backend completion. 8739ec08c48SStefan Berger */ 8749ec08c48SStefan Berger tpm_backend_finish_sync(s->be_driver); 8759ec08c48SStefan Berger 8769ec08c48SStefan Berger return 0; 8779ec08c48SStefan Berger } 8789ec08c48SStefan Berger 879ac90053dSEric Auger const VMStateDescription vmstate_locty = { 8809ec08c48SStefan Berger .name = "tpm-tis/locty", 8819ec08c48SStefan Berger .version_id = 0, 8829ec08c48SStefan Berger .fields = (VMStateField[]) { 8839ec08c48SStefan Berger VMSTATE_UINT32(state, TPMLocality), 8849ec08c48SStefan Berger VMSTATE_UINT32(inte, TPMLocality), 8859ec08c48SStefan Berger VMSTATE_UINT32(ints, TPMLocality), 8869ec08c48SStefan Berger VMSTATE_UINT8(access, TPMLocality), 8879ec08c48SStefan Berger VMSTATE_UINT32(sts, TPMLocality), 8889ec08c48SStefan Berger VMSTATE_UINT32(iface_id, TPMLocality), 8899ec08c48SStefan Berger VMSTATE_END_OF_LIST(), 8909ec08c48SStefan Berger } 8919ec08c48SStefan Berger }; 8929ec08c48SStefan Berger 893