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