1ac90053dSEric Auger /* 2ac90053dSEric Auger * tpm_tis_common.c - QEMU's TPM TIS interface emulator 3ac90053dSEric Auger * device agnostic functions 4ac90053dSEric Auger * 5ac90053dSEric Auger * Copyright (C) 2006,2010-2013 IBM Corporation 6ac90053dSEric Auger * 7ac90053dSEric Auger * Authors: 8ac90053dSEric Auger * Stefan Berger <stefanb@us.ibm.com> 9ac90053dSEric Auger * David Safford <safford@us.ibm.com> 10ac90053dSEric Auger * 11ac90053dSEric Auger * Xen 4 support: Andrease Niederl <andreas.niederl@iaik.tugraz.at> 12ac90053dSEric Auger * 13ac90053dSEric Auger * This work is licensed under the terms of the GNU GPL, version 2 or later. 14ac90053dSEric Auger * See the COPYING file in the top-level directory. 15ac90053dSEric Auger * 16ac90053dSEric Auger * Implementation of the TIS interface according to specs found at 17ac90053dSEric Auger * http://www.trustedcomputinggroup.org. This implementation currently 18ac90053dSEric Auger * supports version 1.3, 21 March 2013 19ac90053dSEric Auger * In the developers menu choose the PC Client section then find the TIS 20ac90053dSEric Auger * specification. 21ac90053dSEric Auger * 22ac90053dSEric Auger * TPM TIS for TPM 2 implementation following TCG PC Client Platform 23ac90053dSEric Auger * TPM Profile (PTP) Specification, Familiy 2.0, Revision 00.43 24ac90053dSEric Auger */ 25ac90053dSEric Auger #include "qemu/osdep.h" 26ac90053dSEric Auger #include "hw/irq.h" 27ac90053dSEric Auger #include "hw/isa/isa.h" 28ac90053dSEric Auger #include "qapi/error.h" 29bbadfb2eSNinad Palsule #include "qemu/bswap.h" 30bbadfb2eSNinad Palsule #include "qemu/crc-ccitt.h" 31ac90053dSEric Auger #include "qemu/module.h" 32ac90053dSEric Auger 33ac90053dSEric Auger #include "hw/acpi/tpm.h" 34ac90053dSEric Auger #include "hw/pci/pci_ids.h" 35ac90053dSEric Auger #include "hw/qdev-properties.h" 36ac90053dSEric Auger #include "migration/vmstate.h" 37ac90053dSEric Auger #include "sysemu/tpm_backend.h" 380f7d2148SPhilippe Mathieu-Daudé #include "sysemu/tpm_util.h" 39ac90053dSEric Auger #include "tpm_ppi.h" 40ac90053dSEric Auger #include "trace.h" 41ac90053dSEric Auger 42ac90053dSEric Auger #include "tpm_tis.h" 43ac90053dSEric Auger 44ac90053dSEric Auger #define DEBUG_TIS 0 45ac90053dSEric Auger 46ac90053dSEric Auger /* local prototypes */ 47ac90053dSEric Auger 48ac90053dSEric Auger static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, 49ac90053dSEric Auger unsigned size); 50ac90053dSEric Auger 51ac90053dSEric Auger /* utility functions */ 52ac90053dSEric Auger 53ac90053dSEric Auger static uint8_t tpm_tis_locality_from_addr(hwaddr addr) 54ac90053dSEric Auger { 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; 61ac90053dSEric Auger } 62ac90053dSEric Auger 63ac90053dSEric Auger 64ac90053dSEric Auger /* 65ac90053dSEric Auger * Set the given flags in the STS register by clearing the register but 66ac90053dSEric Auger * preserving the SELFTEST_DONE and TPM_FAMILY_MASK flags and then setting 67ac90053dSEric Auger * the new flags. 68ac90053dSEric Auger * 69ac90053dSEric Auger * The SELFTEST_DONE flag is acquired from the backend that determines it by 70ac90053dSEric Auger * peeking into TPM commands. 71ac90053dSEric Auger * 72ac90053dSEric Auger * A VM suspend/resume will preserve the flag by storing it into the VM 73ac90053dSEric Auger * device state, but the backend will not remember it when QEMU is started 74ac90053dSEric Auger * again. Therefore, we cache the flag here. Once set, it will not be unset 75ac90053dSEric Auger * except by a reset. 76ac90053dSEric Auger */ 77ac90053dSEric Auger static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags) 78ac90053dSEric Auger { 79ac90053dSEric Auger l->sts &= TPM_TIS_STS_SELFTEST_DONE | TPM_TIS_STS_TPM_FAMILY_MASK; 80ac90053dSEric Auger l->sts |= flags; 81ac90053dSEric Auger } 82ac90053dSEric Auger 83ac90053dSEric Auger /* 84ac90053dSEric Auger * Send a request to the TPM. 85ac90053dSEric Auger */ 86ac90053dSEric Auger static void tpm_tis_tpm_send(TPMState *s, uint8_t locty) 87ac90053dSEric Auger { 88ac90053dSEric Auger tpm_util_show_buffer(s->buffer, s->be_buffer_size, "To TPM"); 89ac90053dSEric Auger 90ac90053dSEric Auger /* 91ac90053dSEric Auger * rw_offset serves as length indicator for length of data; 92ac90053dSEric Auger * it's reset when the response comes back 93ac90053dSEric Auger */ 94ac90053dSEric Auger s->loc[locty].state = TPM_TIS_STATE_EXECUTION; 95ac90053dSEric Auger 96ac90053dSEric Auger s->cmd = (TPMBackendCmd) { 97ac90053dSEric Auger .locty = locty, 98ac90053dSEric Auger .in = s->buffer, 99ac90053dSEric Auger .in_len = s->rw_offset, 100ac90053dSEric Auger .out = s->buffer, 101ac90053dSEric Auger .out_len = s->be_buffer_size, 102ac90053dSEric Auger }; 103ac90053dSEric Auger 104ac90053dSEric Auger tpm_backend_deliver_request(s->be_driver, &s->cmd); 105ac90053dSEric Auger } 106ac90053dSEric Auger 107ac90053dSEric Auger /* raise an interrupt if allowed */ 108ac90053dSEric Auger static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask) 109ac90053dSEric Auger { 110ac90053dSEric Auger if (!TPM_TIS_IS_VALID_LOCTY(locty)) { 111ac90053dSEric Auger return; 112ac90053dSEric Auger } 113ac90053dSEric Auger 114ac90053dSEric Auger if ((s->loc[locty].inte & TPM_TIS_INT_ENABLED) && 115ac90053dSEric Auger (s->loc[locty].inte & irqmask)) { 116ac90053dSEric Auger trace_tpm_tis_raise_irq(irqmask); 117ac90053dSEric Auger qemu_irq_raise(s->irq); 118ac90053dSEric Auger s->loc[locty].ints |= irqmask; 119ac90053dSEric Auger } 120ac90053dSEric Auger } 121ac90053dSEric Auger 122ac90053dSEric Auger static uint32_t tpm_tis_check_request_use_except(TPMState *s, uint8_t locty) 123ac90053dSEric Auger { 124ac90053dSEric Auger uint8_t l; 125ac90053dSEric Auger 126ac90053dSEric Auger for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 127ac90053dSEric Auger if (l == locty) { 128ac90053dSEric Auger continue; 129ac90053dSEric Auger } 130ac90053dSEric Auger if ((s->loc[l].access & TPM_TIS_ACCESS_REQUEST_USE)) { 131ac90053dSEric Auger return 1; 132ac90053dSEric Auger } 133ac90053dSEric Auger } 134ac90053dSEric Auger 135ac90053dSEric Auger return 0; 136ac90053dSEric Auger } 137ac90053dSEric Auger 138ac90053dSEric Auger static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty) 139ac90053dSEric Auger { 140ac90053dSEric Auger bool change = (s->active_locty != new_active_locty); 141ac90053dSEric Auger bool is_seize; 142ac90053dSEric Auger uint8_t mask; 143ac90053dSEric Auger 144ac90053dSEric Auger if (change && TPM_TIS_IS_VALID_LOCTY(s->active_locty)) { 145ac90053dSEric Auger is_seize = TPM_TIS_IS_VALID_LOCTY(new_active_locty) && 146ac90053dSEric Auger s->loc[new_active_locty].access & TPM_TIS_ACCESS_SEIZE; 147ac90053dSEric Auger 148ac90053dSEric Auger if (is_seize) { 149ac90053dSEric Auger mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY); 150ac90053dSEric Auger } else { 151ac90053dSEric Auger mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY| 152ac90053dSEric Auger TPM_TIS_ACCESS_REQUEST_USE); 153ac90053dSEric Auger } 154ac90053dSEric Auger /* reset flags on the old active locality */ 155ac90053dSEric Auger s->loc[s->active_locty].access &= mask; 156ac90053dSEric Auger 157ac90053dSEric Auger if (is_seize) { 158ac90053dSEric Auger s->loc[s->active_locty].access |= TPM_TIS_ACCESS_BEEN_SEIZED; 159ac90053dSEric Auger } 160ac90053dSEric Auger } 161ac90053dSEric Auger 162ac90053dSEric Auger s->active_locty = new_active_locty; 163ac90053dSEric Auger 164ac90053dSEric Auger trace_tpm_tis_new_active_locality(s->active_locty); 165ac90053dSEric Auger 166ac90053dSEric Auger if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) { 167ac90053dSEric Auger /* set flags on the new active locality */ 168ac90053dSEric Auger s->loc[new_active_locty].access |= TPM_TIS_ACCESS_ACTIVE_LOCALITY; 169ac90053dSEric Auger s->loc[new_active_locty].access &= ~(TPM_TIS_ACCESS_REQUEST_USE | 170ac90053dSEric Auger TPM_TIS_ACCESS_SEIZE); 171ac90053dSEric Auger } 172ac90053dSEric Auger 173ac90053dSEric Auger if (change) { 174ac90053dSEric Auger tpm_tis_raise_irq(s, s->active_locty, TPM_TIS_INT_LOCALITY_CHANGED); 175ac90053dSEric Auger } 176ac90053dSEric Auger } 177ac90053dSEric Auger 178ac90053dSEric Auger /* abort -- this function switches the locality */ 179ac90053dSEric Auger static void tpm_tis_abort(TPMState *s) 180ac90053dSEric Auger { 181ac90053dSEric Auger s->rw_offset = 0; 182ac90053dSEric Auger 183ac90053dSEric Auger trace_tpm_tis_abort(s->next_locty); 184ac90053dSEric Auger 185ac90053dSEric Auger /* 186ac90053dSEric Auger * Need to react differently depending on who's aborting now and 187ac90053dSEric Auger * which locality will become active afterwards. 188ac90053dSEric Auger */ 189ac90053dSEric Auger if (s->aborting_locty == s->next_locty) { 190ac90053dSEric Auger s->loc[s->aborting_locty].state = TPM_TIS_STATE_READY; 191ac90053dSEric Auger tpm_tis_sts_set(&s->loc[s->aborting_locty], 192ac90053dSEric Auger TPM_TIS_STS_COMMAND_READY); 193ac90053dSEric Auger tpm_tis_raise_irq(s, s->aborting_locty, TPM_TIS_INT_COMMAND_READY); 194ac90053dSEric Auger } 195ac90053dSEric Auger 196ac90053dSEric Auger /* locality after abort is another one than the current one */ 197ac90053dSEric Auger tpm_tis_new_active_locality(s, s->next_locty); 198ac90053dSEric Auger 199ac90053dSEric Auger s->next_locty = TPM_TIS_NO_LOCALITY; 200ac90053dSEric Auger /* nobody's aborting a command anymore */ 201ac90053dSEric Auger s->aborting_locty = TPM_TIS_NO_LOCALITY; 202ac90053dSEric Auger } 203ac90053dSEric Auger 204ac90053dSEric Auger /* prepare aborting current command */ 205ac90053dSEric Auger static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty) 206ac90053dSEric Auger { 207ac90053dSEric Auger uint8_t busy_locty; 208ac90053dSEric Auger 209ac90053dSEric Auger assert(TPM_TIS_IS_VALID_LOCTY(newlocty)); 210ac90053dSEric Auger 211ac90053dSEric Auger s->aborting_locty = locty; /* may also be TPM_TIS_NO_LOCALITY */ 212ac90053dSEric Auger s->next_locty = newlocty; /* locality after successful abort */ 213ac90053dSEric Auger 214ac90053dSEric Auger /* 215ac90053dSEric Auger * only abort a command using an interrupt if currently executing 216ac90053dSEric Auger * a command AND if there's a valid connection to the vTPM. 217ac90053dSEric Auger */ 218ac90053dSEric Auger for (busy_locty = 0; busy_locty < TPM_TIS_NUM_LOCALITIES; busy_locty++) { 219ac90053dSEric Auger if (s->loc[busy_locty].state == TPM_TIS_STATE_EXECUTION) { 220ac90053dSEric Auger /* 221ac90053dSEric Auger * request the backend to cancel. Some backends may not 222ac90053dSEric Auger * support it 223ac90053dSEric Auger */ 224ac90053dSEric Auger tpm_backend_cancel_cmd(s->be_driver); 225ac90053dSEric Auger return; 226ac90053dSEric Auger } 227ac90053dSEric Auger } 228ac90053dSEric Auger 229ac90053dSEric Auger tpm_tis_abort(s); 230ac90053dSEric Auger } 231ac90053dSEric Auger 232ac90053dSEric Auger /* 233ac90053dSEric Auger * Callback from the TPM to indicate that the response was received. 234ac90053dSEric Auger */ 235ac90053dSEric Auger void tpm_tis_request_completed(TPMState *s, int ret) 236ac90053dSEric Auger { 237ac90053dSEric Auger uint8_t locty = s->cmd.locty; 238ac90053dSEric Auger uint8_t l; 239ac90053dSEric Auger 240ac90053dSEric Auger assert(TPM_TIS_IS_VALID_LOCTY(locty)); 241ac90053dSEric Auger 242ac90053dSEric Auger if (s->cmd.selftest_done) { 243ac90053dSEric Auger for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 244ac90053dSEric Auger s->loc[l].sts |= TPM_TIS_STS_SELFTEST_DONE; 245ac90053dSEric Auger } 246ac90053dSEric Auger } 247ac90053dSEric Auger 248ac90053dSEric Auger /* FIXME: report error if ret != 0 */ 249ac90053dSEric Auger tpm_tis_sts_set(&s->loc[locty], 250ac90053dSEric Auger TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE); 251ac90053dSEric Auger s->loc[locty].state = TPM_TIS_STATE_COMPLETION; 252ac90053dSEric Auger s->rw_offset = 0; 253ac90053dSEric Auger 254ac90053dSEric Auger tpm_util_show_buffer(s->buffer, s->be_buffer_size, "From TPM"); 255ac90053dSEric Auger 256ac90053dSEric Auger if (TPM_TIS_IS_VALID_LOCTY(s->next_locty)) { 257ac90053dSEric Auger tpm_tis_abort(s); 258ac90053dSEric Auger } 259ac90053dSEric Auger 260ac90053dSEric Auger tpm_tis_raise_irq(s, locty, 261ac90053dSEric Auger TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID); 262ac90053dSEric Auger } 263ac90053dSEric Auger 264ac90053dSEric Auger /* 265ac90053dSEric Auger * Read a byte of response data 266ac90053dSEric Auger */ 267ac90053dSEric Auger static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty) 268ac90053dSEric Auger { 269ac90053dSEric Auger uint32_t ret = TPM_TIS_NO_DATA_BYTE; 270ac90053dSEric Auger uint16_t len; 271ac90053dSEric Auger 272ac90053dSEric Auger if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) { 273ac90053dSEric Auger len = MIN(tpm_cmd_get_size(&s->buffer), 274ac90053dSEric Auger s->be_buffer_size); 275ac90053dSEric Auger 276ac90053dSEric Auger ret = s->buffer[s->rw_offset++]; 277ac90053dSEric Auger if (s->rw_offset >= len) { 278ac90053dSEric Auger /* got last byte */ 279ac90053dSEric Auger tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 280ac90053dSEric Auger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); 281ac90053dSEric Auger } 282ac90053dSEric Auger trace_tpm_tis_data_read(ret, s->rw_offset - 1); 283ac90053dSEric Auger } 284ac90053dSEric Auger 285ac90053dSEric Auger return ret; 286ac90053dSEric Auger } 287ac90053dSEric Auger 288ac90053dSEric Auger #ifdef DEBUG_TIS 289ac90053dSEric Auger static void tpm_tis_dump_state(TPMState *s, hwaddr addr) 290ac90053dSEric Auger { 291ac90053dSEric Auger static const unsigned regs[] = { 292ac90053dSEric Auger TPM_TIS_REG_ACCESS, 293ac90053dSEric Auger TPM_TIS_REG_INT_ENABLE, 294ac90053dSEric Auger TPM_TIS_REG_INT_VECTOR, 295ac90053dSEric Auger TPM_TIS_REG_INT_STATUS, 296ac90053dSEric Auger TPM_TIS_REG_INTF_CAPABILITY, 297ac90053dSEric Auger TPM_TIS_REG_STS, 298ac90053dSEric Auger TPM_TIS_REG_DID_VID, 299ac90053dSEric Auger TPM_TIS_REG_RID, 300ac90053dSEric Auger 0xfff}; 301ac90053dSEric Auger int idx; 302ac90053dSEric Auger uint8_t locty = tpm_tis_locality_from_addr(addr); 303ac90053dSEric Auger hwaddr base = addr & ~0xfff; 304ac90053dSEric Auger 305ac90053dSEric Auger printf("tpm_tis: active locality : %d\n" 306ac90053dSEric Auger "tpm_tis: state of locality %d : %d\n" 307ac90053dSEric Auger "tpm_tis: register dump:\n", 308ac90053dSEric Auger s->active_locty, 309ac90053dSEric Auger locty, s->loc[locty].state); 310ac90053dSEric Auger 311ac90053dSEric Auger for (idx = 0; regs[idx] != 0xfff; idx++) { 312ac90053dSEric Auger printf("tpm_tis: 0x%04x : 0x%08x\n", regs[idx], 313ac90053dSEric Auger (int)tpm_tis_mmio_read(s, base + regs[idx], 4)); 314ac90053dSEric Auger } 315ac90053dSEric Auger 316ac90053dSEric Auger printf("tpm_tis: r/w offset : %d\n" 317ac90053dSEric Auger "tpm_tis: result buffer : ", 318ac90053dSEric Auger s->rw_offset); 319ac90053dSEric Auger for (idx = 0; 320ac90053dSEric Auger idx < MIN(tpm_cmd_get_size(&s->buffer), s->be_buffer_size); 321ac90053dSEric Auger idx++) { 322ac90053dSEric Auger printf("%c%02x%s", 323ac90053dSEric Auger s->rw_offset == idx ? '>' : ' ', 324ac90053dSEric Auger s->buffer[idx], 325ac90053dSEric Auger ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : ""); 326ac90053dSEric Auger } 327ac90053dSEric Auger printf("\n"); 328ac90053dSEric Auger } 329ac90053dSEric Auger #endif 330ac90053dSEric Auger 331ac90053dSEric Auger /* 332ac90053dSEric Auger * Read a register of the TIS interface 333ac90053dSEric Auger * See specs pages 33-63 for description of the registers 334ac90053dSEric Auger */ 335ac90053dSEric Auger static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, 336ac90053dSEric Auger unsigned size) 337ac90053dSEric Auger { 338ac90053dSEric Auger TPMState *s = opaque; 339ac90053dSEric Auger uint16_t offset = addr & 0xffc; 340ac90053dSEric Auger uint8_t shift = (addr & 0x3) * 8; 341ac90053dSEric Auger uint32_t val = 0xffffffff; 342ac90053dSEric Auger uint8_t locty = tpm_tis_locality_from_addr(addr); 343ac90053dSEric Auger uint32_t avail; 344ac90053dSEric Auger uint8_t v; 345ac90053dSEric Auger 346ac90053dSEric Auger if (tpm_backend_had_startup_error(s->be_driver)) { 347ac90053dSEric Auger return 0; 348ac90053dSEric Auger } 349ac90053dSEric Auger 350ac90053dSEric Auger switch (offset) { 351ac90053dSEric Auger case TPM_TIS_REG_ACCESS: 352ac90053dSEric Auger /* never show the SEIZE flag even though we use it internally */ 353ac90053dSEric Auger val = s->loc[locty].access & ~TPM_TIS_ACCESS_SEIZE; 354ac90053dSEric Auger /* the pending flag is always calculated */ 355ac90053dSEric Auger if (tpm_tis_check_request_use_except(s, locty)) { 356ac90053dSEric Auger val |= TPM_TIS_ACCESS_PENDING_REQUEST; 357ac90053dSEric Auger } 358ac90053dSEric Auger val |= !tpm_backend_get_tpm_established_flag(s->be_driver); 359ac90053dSEric Auger break; 360ac90053dSEric Auger case TPM_TIS_REG_INT_ENABLE: 361ac90053dSEric Auger val = s->loc[locty].inte; 362ac90053dSEric Auger break; 363ac90053dSEric Auger case TPM_TIS_REG_INT_VECTOR: 364ac90053dSEric Auger val = s->irq_num; 365ac90053dSEric Auger break; 366ac90053dSEric Auger case TPM_TIS_REG_INT_STATUS: 367ac90053dSEric Auger val = s->loc[locty].ints; 368ac90053dSEric Auger break; 369ac90053dSEric Auger case TPM_TIS_REG_INTF_CAPABILITY: 370ac90053dSEric Auger switch (s->be_tpm_version) { 371ac90053dSEric Auger case TPM_VERSION_UNSPEC: 372ac90053dSEric Auger val = 0; 373ac90053dSEric Auger break; 374ac90053dSEric Auger case TPM_VERSION_1_2: 375ac90053dSEric Auger val = TPM_TIS_CAPABILITIES_SUPPORTED1_3; 376ac90053dSEric Auger break; 377ac90053dSEric Auger case TPM_VERSION_2_0: 378ac90053dSEric Auger val = TPM_TIS_CAPABILITIES_SUPPORTED2_0; 379ac90053dSEric Auger break; 380ac90053dSEric Auger } 381ac90053dSEric Auger break; 382ac90053dSEric Auger case TPM_TIS_REG_STS: 383ac90053dSEric Auger if (s->active_locty == locty) { 384ac90053dSEric Auger if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) { 385ac90053dSEric Auger val = TPM_TIS_BURST_COUNT( 386ac90053dSEric Auger MIN(tpm_cmd_get_size(&s->buffer), 387ac90053dSEric Auger s->be_buffer_size) 388ac90053dSEric Auger - s->rw_offset) | s->loc[locty].sts; 389ac90053dSEric Auger } else { 390ac90053dSEric Auger avail = s->be_buffer_size - s->rw_offset; 391ac90053dSEric Auger /* 392ac90053dSEric Auger * byte-sized reads should not return 0x00 for 0x100 393ac90053dSEric Auger * available bytes. 394ac90053dSEric Auger */ 395ac90053dSEric Auger if (size == 1 && avail > 0xff) { 396ac90053dSEric Auger avail = 0xff; 397ac90053dSEric Auger } 398ac90053dSEric Auger val = TPM_TIS_BURST_COUNT(avail) | s->loc[locty].sts; 399ac90053dSEric Auger } 400ac90053dSEric Auger } 401ac90053dSEric Auger break; 402ac90053dSEric Auger case TPM_TIS_REG_DATA_FIFO: 403ac90053dSEric Auger case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END: 404ac90053dSEric Auger if (s->active_locty == locty) { 405ac90053dSEric Auger if (size > 4 - (addr & 0x3)) { 406ac90053dSEric Auger /* prevent access beyond FIFO */ 407ac90053dSEric Auger size = 4 - (addr & 0x3); 408ac90053dSEric Auger } 409ac90053dSEric Auger val = 0; 410ac90053dSEric Auger shift = 0; 411ac90053dSEric Auger while (size > 0) { 412ac90053dSEric Auger switch (s->loc[locty].state) { 413ac90053dSEric Auger case TPM_TIS_STATE_COMPLETION: 414ac90053dSEric Auger v = tpm_tis_data_read(s, locty); 415ac90053dSEric Auger break; 416ac90053dSEric Auger default: 417ac90053dSEric Auger v = TPM_TIS_NO_DATA_BYTE; 418ac90053dSEric Auger break; 419ac90053dSEric Auger } 420ac90053dSEric Auger val |= (v << shift); 421ac90053dSEric Auger shift += 8; 422ac90053dSEric Auger size--; 423ac90053dSEric Auger } 424ac90053dSEric Auger shift = 0; /* no more adjustments */ 425ac90053dSEric Auger } 426ac90053dSEric Auger break; 427ac90053dSEric Auger case TPM_TIS_REG_INTERFACE_ID: 428ac90053dSEric Auger val = s->loc[locty].iface_id; 429ac90053dSEric Auger break; 430ac90053dSEric Auger case TPM_TIS_REG_DID_VID: 431ac90053dSEric Auger val = (TPM_TIS_TPM_DID << 16) | TPM_TIS_TPM_VID; 432ac90053dSEric Auger break; 433ac90053dSEric Auger case TPM_TIS_REG_RID: 434ac90053dSEric Auger val = TPM_TIS_TPM_RID; 435ac90053dSEric Auger break; 436ac90053dSEric Auger #ifdef DEBUG_TIS 437ac90053dSEric Auger case TPM_TIS_REG_DEBUG: 438ac90053dSEric Auger tpm_tis_dump_state(s, addr); 439ac90053dSEric Auger break; 440ac90053dSEric Auger #endif 441ac90053dSEric Auger } 442ac90053dSEric Auger 443ac90053dSEric Auger if (shift) { 444ac90053dSEric Auger val >>= shift; 445ac90053dSEric Auger } 446ac90053dSEric Auger 447ac90053dSEric Auger trace_tpm_tis_mmio_read(size, addr, val); 448ac90053dSEric Auger 449ac90053dSEric Auger return val; 450ac90053dSEric Auger } 451ac90053dSEric Auger 452ac90053dSEric Auger /* 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 /* 470ac90053dSEric Auger * Write a value to a register of the TIS interface 471ac90053dSEric Auger * See specs pages 33-63 for description of the registers 472ac90053dSEric Auger */ 473ac90053dSEric Auger static void tpm_tis_mmio_write(void *opaque, hwaddr addr, 474ac90053dSEric Auger uint64_t val, unsigned size) 475ac90053dSEric Auger { 476ac90053dSEric Auger TPMState *s = opaque; 477ac90053dSEric Auger uint16_t off = addr & 0xffc; 478ac90053dSEric Auger uint8_t shift = (addr & 0x3) * 8; 479ac90053dSEric Auger uint8_t locty = tpm_tis_locality_from_addr(addr); 480ac90053dSEric Auger uint8_t active_locty, l; 481ac90053dSEric Auger int c, set_new_locty = 1; 482ac90053dSEric Auger uint16_t len; 483ac90053dSEric Auger uint32_t mask = (size == 1) ? 0xff : ((size == 2) ? 0xffff : ~0); 484ac90053dSEric Auger 485ac90053dSEric Auger trace_tpm_tis_mmio_write(size, addr, val); 486ac90053dSEric Auger 487ac90053dSEric Auger if (locty == 4) { 488ac90053dSEric Auger trace_tpm_tis_mmio_write_locty4(); 489ac90053dSEric Auger return; 490ac90053dSEric Auger } 491ac90053dSEric Auger 492ac90053dSEric Auger if (tpm_backend_had_startup_error(s->be_driver)) { 493ac90053dSEric Auger return; 494ac90053dSEric Auger } 495ac90053dSEric Auger 496ac90053dSEric Auger val &= mask; 497ac90053dSEric Auger 498ac90053dSEric Auger if (shift) { 499ac90053dSEric Auger val <<= shift; 500ac90053dSEric Auger mask <<= shift; 501ac90053dSEric Auger } 502ac90053dSEric Auger 503ac90053dSEric Auger mask ^= 0xffffffff; 504ac90053dSEric Auger 505ac90053dSEric Auger switch (off) { 506ac90053dSEric Auger case TPM_TIS_REG_ACCESS: 507ac90053dSEric Auger 508ac90053dSEric Auger if ((val & TPM_TIS_ACCESS_SEIZE)) { 509ac90053dSEric Auger val &= ~(TPM_TIS_ACCESS_REQUEST_USE | 510ac90053dSEric Auger TPM_TIS_ACCESS_ACTIVE_LOCALITY); 511ac90053dSEric Auger } 512ac90053dSEric Auger 513ac90053dSEric Auger active_locty = s->active_locty; 514ac90053dSEric Auger 515ac90053dSEric Auger if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) { 516ac90053dSEric Auger /* give up locality if currently owned */ 517ac90053dSEric Auger if (s->active_locty == locty) { 518ac90053dSEric Auger trace_tpm_tis_mmio_write_release_locty(locty); 519ac90053dSEric Auger 520ac90053dSEric Auger uint8_t newlocty = TPM_TIS_NO_LOCALITY; 521ac90053dSEric Auger /* anybody wants the locality ? */ 522ac90053dSEric Auger for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) { 523ac90053dSEric Auger if ((s->loc[c].access & TPM_TIS_ACCESS_REQUEST_USE)) { 524ac90053dSEric Auger trace_tpm_tis_mmio_write_locty_req_use(c); 525ac90053dSEric Auger newlocty = c; 526ac90053dSEric Auger break; 527ac90053dSEric Auger } 528ac90053dSEric Auger } 529ac90053dSEric Auger trace_tpm_tis_mmio_write_next_locty(newlocty); 530ac90053dSEric Auger 531ac90053dSEric Auger if (TPM_TIS_IS_VALID_LOCTY(newlocty)) { 532ac90053dSEric Auger set_new_locty = 0; 533ac90053dSEric Auger tpm_tis_prep_abort(s, locty, newlocty); 534ac90053dSEric Auger } else { 535ac90053dSEric Auger active_locty = TPM_TIS_NO_LOCALITY; 536ac90053dSEric Auger } 537ac90053dSEric Auger } else { 538ac90053dSEric Auger /* not currently the owner; clear a pending request */ 539ac90053dSEric Auger s->loc[locty].access &= ~TPM_TIS_ACCESS_REQUEST_USE; 540ac90053dSEric Auger } 541ac90053dSEric Auger } 542ac90053dSEric Auger 543ac90053dSEric Auger if ((val & TPM_TIS_ACCESS_BEEN_SEIZED)) { 544ac90053dSEric Auger s->loc[locty].access &= ~TPM_TIS_ACCESS_BEEN_SEIZED; 545ac90053dSEric Auger } 546ac90053dSEric Auger 547ac90053dSEric Auger if ((val & TPM_TIS_ACCESS_SEIZE)) { 548ac90053dSEric Auger /* 549ac90053dSEric Auger * allow seize if a locality is active and the requesting 550ac90053dSEric Auger * locality is higher than the one that's active 551ac90053dSEric Auger * OR 552ac90053dSEric Auger * allow seize for requesting locality if no locality is 553ac90053dSEric Auger * active 554ac90053dSEric Auger */ 555ac90053dSEric Auger while ((TPM_TIS_IS_VALID_LOCTY(s->active_locty) && 556ac90053dSEric Auger locty > s->active_locty) || 557ac90053dSEric Auger !TPM_TIS_IS_VALID_LOCTY(s->active_locty)) { 558aadad398SJafar Abdi bool higher_seize = false; 559ac90053dSEric Auger 560ac90053dSEric Auger /* already a pending SEIZE ? */ 561ac90053dSEric Auger if ((s->loc[locty].access & TPM_TIS_ACCESS_SEIZE)) { 562ac90053dSEric Auger break; 563ac90053dSEric Auger } 564ac90053dSEric Auger 565ac90053dSEric Auger /* check for ongoing seize by a higher locality */ 566ac90053dSEric Auger for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES; l++) { 567ac90053dSEric Auger if ((s->loc[l].access & TPM_TIS_ACCESS_SEIZE)) { 568aadad398SJafar Abdi higher_seize = true; 569ac90053dSEric Auger break; 570ac90053dSEric Auger } 571ac90053dSEric Auger } 572ac90053dSEric Auger 573ac90053dSEric Auger if (higher_seize) { 574ac90053dSEric Auger break; 575ac90053dSEric Auger } 576ac90053dSEric Auger 577ac90053dSEric Auger /* cancel any seize by a lower locality */ 578ac90053dSEric Auger for (l = 0; l < locty; l++) { 579ac90053dSEric Auger s->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE; 580ac90053dSEric Auger } 581ac90053dSEric Auger 582ac90053dSEric Auger s->loc[locty].access |= TPM_TIS_ACCESS_SEIZE; 583ac90053dSEric Auger 584ac90053dSEric Auger trace_tpm_tis_mmio_write_locty_seized(locty, s->active_locty); 585ac90053dSEric Auger trace_tpm_tis_mmio_write_init_abort(); 586ac90053dSEric Auger 587ac90053dSEric Auger set_new_locty = 0; 588ac90053dSEric Auger tpm_tis_prep_abort(s, s->active_locty, locty); 589ac90053dSEric Auger break; 590ac90053dSEric Auger } 591ac90053dSEric Auger } 592ac90053dSEric Auger 593ac90053dSEric Auger if ((val & TPM_TIS_ACCESS_REQUEST_USE)) { 594ac90053dSEric Auger if (s->active_locty != locty) { 595ac90053dSEric Auger if (TPM_TIS_IS_VALID_LOCTY(s->active_locty)) { 596ac90053dSEric Auger s->loc[locty].access |= TPM_TIS_ACCESS_REQUEST_USE; 597ac90053dSEric Auger } else { 598ac90053dSEric Auger /* no locality active -> make this one active now */ 599ac90053dSEric Auger active_locty = locty; 600ac90053dSEric Auger } 601ac90053dSEric Auger } 602ac90053dSEric Auger } 603ac90053dSEric Auger 604ac90053dSEric Auger if (set_new_locty) { 605ac90053dSEric Auger tpm_tis_new_active_locality(s, active_locty); 606ac90053dSEric Auger } 607ac90053dSEric Auger 608ac90053dSEric Auger break; 609ac90053dSEric Auger case TPM_TIS_REG_INT_ENABLE: 610ac90053dSEric Auger s->loc[locty].inte &= mask; 611ac90053dSEric Auger s->loc[locty].inte |= (val & (TPM_TIS_INT_ENABLED | 612ac90053dSEric Auger TPM_TIS_INT_POLARITY_MASK | 613ac90053dSEric Auger TPM_TIS_INTERRUPTS_SUPPORTED)); 614ac90053dSEric Auger break; 615ac90053dSEric Auger case TPM_TIS_REG_INT_VECTOR: 616ac90053dSEric Auger /* hard wired -- ignore */ 617ac90053dSEric Auger break; 618ac90053dSEric Auger case TPM_TIS_REG_INT_STATUS: 619ac90053dSEric Auger /* clearing of interrupt flags */ 620ac90053dSEric Auger if (((val & TPM_TIS_INTERRUPTS_SUPPORTED)) && 621ac90053dSEric Auger (s->loc[locty].ints & TPM_TIS_INTERRUPTS_SUPPORTED)) { 622ac90053dSEric Auger s->loc[locty].ints &= ~val; 623ac90053dSEric Auger if (s->loc[locty].ints == 0) { 624ac90053dSEric Auger qemu_irq_lower(s->irq); 625ac90053dSEric Auger trace_tpm_tis_mmio_write_lowering_irq(); 626ac90053dSEric Auger } 627ac90053dSEric Auger } 628ac90053dSEric Auger s->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED); 629ac90053dSEric Auger break; 630ac90053dSEric Auger case TPM_TIS_REG_STS: 631ac90053dSEric Auger if (s->active_locty != locty) { 632ac90053dSEric Auger break; 633ac90053dSEric Auger } 634ac90053dSEric Auger 635ac90053dSEric Auger if (s->be_tpm_version == TPM_VERSION_2_0) { 636ac90053dSEric Auger /* some flags that are only supported for TPM 2 */ 637ac90053dSEric Auger if (val & TPM_TIS_STS_COMMAND_CANCEL) { 638ac90053dSEric Auger if (s->loc[locty].state == TPM_TIS_STATE_EXECUTION) { 639ac90053dSEric Auger /* 640ac90053dSEric Auger * request the backend to cancel. Some backends may not 641ac90053dSEric Auger * support it 642ac90053dSEric Auger */ 643ac90053dSEric Auger tpm_backend_cancel_cmd(s->be_driver); 644ac90053dSEric Auger } 645ac90053dSEric Auger } 646ac90053dSEric Auger 647ac90053dSEric Auger if (val & TPM_TIS_STS_RESET_ESTABLISHMENT_BIT) { 648ac90053dSEric Auger if (locty == 3 || locty == 4) { 649ac90053dSEric Auger tpm_backend_reset_tpm_established_flag(s->be_driver, locty); 650ac90053dSEric Auger } 651ac90053dSEric Auger } 652ac90053dSEric Auger } 653ac90053dSEric Auger 654ac90053dSEric Auger val &= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO | 655ac90053dSEric Auger TPM_TIS_STS_RESPONSE_RETRY); 656ac90053dSEric Auger 657ac90053dSEric Auger if (val == TPM_TIS_STS_COMMAND_READY) { 658ac90053dSEric Auger switch (s->loc[locty].state) { 659ac90053dSEric Auger 660ac90053dSEric Auger case TPM_TIS_STATE_READY: 661ac90053dSEric Auger s->rw_offset = 0; 662ac90053dSEric Auger break; 663ac90053dSEric Auger 664ac90053dSEric Auger case TPM_TIS_STATE_IDLE: 665ac90053dSEric Auger tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_COMMAND_READY); 666ac90053dSEric Auger s->loc[locty].state = TPM_TIS_STATE_READY; 667ac90053dSEric Auger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); 668ac90053dSEric Auger break; 669ac90053dSEric Auger 670ac90053dSEric Auger case TPM_TIS_STATE_EXECUTION: 671ac90053dSEric Auger case TPM_TIS_STATE_RECEPTION: 672ac90053dSEric Auger /* abort currently running command */ 673ac90053dSEric Auger trace_tpm_tis_mmio_write_init_abort(); 674ac90053dSEric Auger tpm_tis_prep_abort(s, locty, locty); 675ac90053dSEric Auger break; 676ac90053dSEric Auger 677ac90053dSEric Auger case TPM_TIS_STATE_COMPLETION: 678ac90053dSEric Auger s->rw_offset = 0; 679ac90053dSEric Auger /* shortcut to ready state with C/R set */ 680ac90053dSEric Auger s->loc[locty].state = TPM_TIS_STATE_READY; 681ac90053dSEric Auger if (!(s->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) { 682ac90053dSEric Auger tpm_tis_sts_set(&s->loc[locty], 683ac90053dSEric Auger TPM_TIS_STS_COMMAND_READY); 684ac90053dSEric Auger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); 685ac90053dSEric Auger } 686ac90053dSEric Auger s->loc[locty].sts &= ~(TPM_TIS_STS_DATA_AVAILABLE); 687ac90053dSEric Auger break; 688ac90053dSEric Auger 689ac90053dSEric Auger } 690ac90053dSEric Auger } else if (val == TPM_TIS_STS_TPM_GO) { 691ac90053dSEric Auger switch (s->loc[locty].state) { 692ac90053dSEric Auger case TPM_TIS_STATE_RECEPTION: 693ac90053dSEric Auger if ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) == 0) { 694ac90053dSEric Auger tpm_tis_tpm_send(s, locty); 695ac90053dSEric Auger } 696ac90053dSEric Auger break; 697ac90053dSEric Auger default: 698ac90053dSEric Auger /* ignore */ 699ac90053dSEric Auger break; 700ac90053dSEric Auger } 701ac90053dSEric Auger } else if (val == TPM_TIS_STS_RESPONSE_RETRY) { 702ac90053dSEric Auger switch (s->loc[locty].state) { 703ac90053dSEric Auger case TPM_TIS_STATE_COMPLETION: 704ac90053dSEric Auger s->rw_offset = 0; 705ac90053dSEric Auger tpm_tis_sts_set(&s->loc[locty], 706ac90053dSEric Auger TPM_TIS_STS_VALID| 707ac90053dSEric Auger TPM_TIS_STS_DATA_AVAILABLE); 708ac90053dSEric Auger break; 709ac90053dSEric Auger default: 710ac90053dSEric Auger /* ignore */ 711ac90053dSEric Auger break; 712ac90053dSEric Auger } 713ac90053dSEric Auger } 714ac90053dSEric Auger break; 715ac90053dSEric Auger case TPM_TIS_REG_DATA_FIFO: 716ac90053dSEric Auger case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END: 717ac90053dSEric Auger /* data fifo */ 718ac90053dSEric Auger if (s->active_locty != locty) { 719ac90053dSEric Auger break; 720ac90053dSEric Auger } 721ac90053dSEric Auger 722ac90053dSEric Auger if (s->loc[locty].state == TPM_TIS_STATE_IDLE || 723ac90053dSEric Auger s->loc[locty].state == TPM_TIS_STATE_EXECUTION || 724ac90053dSEric Auger s->loc[locty].state == TPM_TIS_STATE_COMPLETION) { 725ac90053dSEric Auger /* drop the byte */ 726ac90053dSEric Auger } else { 727ac90053dSEric Auger trace_tpm_tis_mmio_write_data2send(val, size); 728ac90053dSEric Auger if (s->loc[locty].state == TPM_TIS_STATE_READY) { 729ac90053dSEric Auger s->loc[locty].state = TPM_TIS_STATE_RECEPTION; 730ac90053dSEric Auger tpm_tis_sts_set(&s->loc[locty], 731ac90053dSEric Auger TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); 732ac90053dSEric Auger } 733ac90053dSEric Auger 734ac90053dSEric Auger val >>= shift; 735ac90053dSEric Auger if (size > 4 - (addr & 0x3)) { 736ac90053dSEric Auger /* prevent access beyond FIFO */ 737ac90053dSEric Auger size = 4 - (addr & 0x3); 738ac90053dSEric Auger } 739ac90053dSEric Auger 740ac90053dSEric Auger while ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) && size > 0) { 741ac90053dSEric Auger if (s->rw_offset < s->be_buffer_size) { 742ac90053dSEric Auger s->buffer[s->rw_offset++] = 743ac90053dSEric Auger (uint8_t)val; 744ac90053dSEric Auger val >>= 8; 745ac90053dSEric Auger size--; 746ac90053dSEric Auger } else { 747ac90053dSEric Auger tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 748ac90053dSEric Auger } 749ac90053dSEric Auger } 750ac90053dSEric Auger 751ac90053dSEric Auger /* check for complete packet */ 752ac90053dSEric Auger if (s->rw_offset > 5 && 753ac90053dSEric Auger (s->loc[locty].sts & TPM_TIS_STS_EXPECT)) { 754ac90053dSEric Auger /* we have a packet length - see if we have all of it */ 755ac90053dSEric Auger bool need_irq = !(s->loc[locty].sts & TPM_TIS_STS_VALID); 756ac90053dSEric Auger 757ac90053dSEric Auger len = tpm_cmd_get_size(&s->buffer); 758ac90053dSEric Auger if (len > s->rw_offset) { 759ac90053dSEric Auger tpm_tis_sts_set(&s->loc[locty], 760ac90053dSEric Auger TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); 761ac90053dSEric Auger } else { 762ac90053dSEric Auger /* packet complete */ 763ac90053dSEric Auger tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); 764ac90053dSEric Auger } 765ac90053dSEric Auger if (need_irq) { 766ac90053dSEric Auger tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); 767ac90053dSEric Auger } 768ac90053dSEric Auger } 769ac90053dSEric Auger } 770ac90053dSEric Auger break; 771ac90053dSEric Auger case TPM_TIS_REG_INTERFACE_ID: 772ac90053dSEric Auger if (val & TPM_TIS_IFACE_ID_INT_SEL_LOCK) { 773ac90053dSEric Auger for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { 774ac90053dSEric Auger s->loc[l].iface_id |= TPM_TIS_IFACE_ID_INT_SEL_LOCK; 775ac90053dSEric Auger } 776ac90053dSEric Auger } 777ac90053dSEric Auger break; 778ac90053dSEric Auger } 779ac90053dSEric Auger } 780ac90053dSEric Auger 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 = { 791ac90053dSEric Auger .read = tpm_tis_mmio_read, 792ac90053dSEric Auger .write = tpm_tis_mmio_write, 793ac90053dSEric Auger .endianness = DEVICE_LITTLE_ENDIAN, 794ac90053dSEric Auger .valid = { 795ac90053dSEric Auger .min_access_size = 1, 796ac90053dSEric Auger .max_access_size = 4, 797ac90053dSEric Auger }, 798ac90053dSEric Auger }; 799ac90053dSEric Auger 800ac90053dSEric Auger /* 801ac90053dSEric Auger * Get the TPMVersion of the backend device being used 802ac90053dSEric Auger */ 803ac90053dSEric Auger enum TPMVersion tpm_tis_get_tpm_version(TPMState *s) 804ac90053dSEric Auger { 805ac90053dSEric Auger if (tpm_backend_had_startup_error(s->be_driver)) { 806ac90053dSEric Auger return TPM_VERSION_UNSPEC; 807ac90053dSEric Auger } 808ac90053dSEric Auger 809ac90053dSEric Auger return tpm_backend_get_tpm_version(s->be_driver); 810ac90053dSEric Auger } 811ac90053dSEric Auger 812ac90053dSEric Auger /* 813ac90053dSEric Auger * This function is called when the machine starts, resets or due to 814ac90053dSEric Auger * S3 resume. 815ac90053dSEric Auger */ 816ac90053dSEric Auger void tpm_tis_reset(TPMState *s) 817ac90053dSEric Auger { 818ac90053dSEric Auger int c; 819ac90053dSEric Auger 820ac90053dSEric Auger s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver); 821ac90053dSEric Auger s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->be_driver), 822ac90053dSEric Auger TPM_TIS_BUFFER_MAX); 823ac90053dSEric Auger 824ac90053dSEric Auger if (s->ppi_enabled) { 825ac90053dSEric Auger tpm_ppi_reset(&s->ppi); 826ac90053dSEric Auger } 827ac90053dSEric Auger tpm_backend_reset(s->be_driver); 828ac90053dSEric Auger 829ac90053dSEric Auger s->active_locty = TPM_TIS_NO_LOCALITY; 830ac90053dSEric Auger s->next_locty = TPM_TIS_NO_LOCALITY; 831ac90053dSEric Auger s->aborting_locty = TPM_TIS_NO_LOCALITY; 832ac90053dSEric Auger 833ac90053dSEric Auger for (c = 0; c < TPM_TIS_NUM_LOCALITIES; c++) { 834ac90053dSEric Auger s->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS; 835ac90053dSEric Auger switch (s->be_tpm_version) { 836ac90053dSEric Auger case TPM_VERSION_UNSPEC: 837ac90053dSEric Auger break; 838ac90053dSEric Auger case TPM_VERSION_1_2: 839ac90053dSEric Auger s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY1_2; 840ac90053dSEric Auger s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3; 841ac90053dSEric Auger break; 842ac90053dSEric Auger case TPM_VERSION_2_0: 843ac90053dSEric Auger s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY2_0; 844ac90053dSEric Auger s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0; 845ac90053dSEric Auger break; 846ac90053dSEric Auger } 847ac90053dSEric Auger s->loc[c].inte = TPM_TIS_INT_POLARITY_LOW_LEVEL; 848ac90053dSEric Auger s->loc[c].ints = 0; 849ac90053dSEric Auger s->loc[c].state = TPM_TIS_STATE_IDLE; 850ac90053dSEric Auger 851ac90053dSEric Auger s->rw_offset = 0; 852ac90053dSEric Auger } 853ac90053dSEric Auger 854ac90053dSEric Auger if (tpm_backend_startup_tpm(s->be_driver, s->be_buffer_size) < 0) { 855ac90053dSEric Auger exit(1); 856ac90053dSEric Auger } 857ac90053dSEric Auger } 858ac90053dSEric Auger 859ac90053dSEric Auger /* persistent state handling */ 860ac90053dSEric Auger 861ac90053dSEric Auger int tpm_tis_pre_save(TPMState *s) 862ac90053dSEric Auger { 863ac90053dSEric Auger uint8_t locty = s->active_locty; 864ac90053dSEric Auger 865ac90053dSEric Auger trace_tpm_tis_pre_save(locty, s->rw_offset); 866ac90053dSEric Auger 867ac90053dSEric Auger if (DEBUG_TIS) { 868ac90053dSEric Auger tpm_tis_dump_state(s, 0); 869ac90053dSEric Auger } 870ac90053dSEric Auger 871ac90053dSEric Auger /* 872ac90053dSEric Auger * Synchronize with backend completion. 873ac90053dSEric Auger */ 874ac90053dSEric Auger tpm_backend_finish_sync(s->be_driver); 875ac90053dSEric Auger 876ac90053dSEric Auger return 0; 877ac90053dSEric Auger } 878ac90053dSEric Auger 879ac90053dSEric Auger const VMStateDescription vmstate_locty = { 880ac90053dSEric Auger .name = "tpm-tis/locty", 881ac90053dSEric Auger .version_id = 0, 882ac90053dSEric Auger .fields = (VMStateField[]) { 883ac90053dSEric Auger VMSTATE_UINT32(state, TPMLocality), 884ac90053dSEric Auger VMSTATE_UINT32(inte, TPMLocality), 885ac90053dSEric Auger VMSTATE_UINT32(ints, TPMLocality), 886ac90053dSEric Auger VMSTATE_UINT8(access, TPMLocality), 887ac90053dSEric Auger VMSTATE_UINT32(sts, TPMLocality), 888ac90053dSEric Auger VMSTATE_UINT32(iface_id, TPMLocality), 889ac90053dSEric Auger VMSTATE_END_OF_LIST(), 890ac90053dSEric Auger } 891ac90053dSEric Auger }; 892ac90053dSEric Auger 893