]> git.proxmox.com Git - mirror_qemu.git/blame - hw/tpm/tpm_tis.c
q800: fix coverity warning CID 1412799
[mirror_qemu.git] / hw / tpm / tpm_tis.c
CommitLineData
edff8678
SB
1/*
2 * tpm_tis.c - QEMU's TPM TIS interface emulator
3 *
4 * Copyright (C) 2006,2010-2013 IBM Corporation
5 *
6 * Authors:
7 * Stefan Berger <stefanb@us.ibm.com>
8 * David Safford <safford@us.ibm.com>
9 *
10 * Xen 4 support: Andrease Niederl <andreas.niederl@iaik.tugraz.at>
11 *
12 * This work is licensed under the terms of the GNU GPL, version 2 or later.
13 * See the COPYING file in the top-level directory.
14 *
15 * Implementation of the TIS interface according to specs found at
16 * http://www.trustedcomputinggroup.org. This implementation currently
9dd5c40d 17 * supports version 1.3, 21 March 2013
edff8678
SB
18 * In the developers menu choose the PC Client section then find the TIS
19 * specification.
116694c3
SB
20 *
21 * TPM TIS for TPM 2 implementation following TCG PC Client Platform
22 * TPM Profile (PTP) Specification, Familiy 2.0, Revision 00.43
edff8678
SB
23 */
24
0430891c 25#include "qemu/osdep.h"
64552b6b 26#include "hw/irq.h"
732cd587 27#include "hw/isa/isa.h"
da34e65c 28#include "qapi/error.h"
0b8fa32f 29#include "qemu/module.h"
023299d8 30
732cd587 31#include "hw/acpi/tpm.h"
023299d8 32#include "hw/pci/pci_ids.h"
a27bd6c7 33#include "hw/qdev-properties.h"
d6454270 34#include "migration/vmstate.h"
023299d8
MAL
35#include "sysemu/tpm_backend.h"
36#include "tpm_int.h"
5cf954d0 37#include "tpm_util.h"
3b97c01e 38#include "tpm_ppi.h"
fcbed221 39#include "trace.h"
732cd587
MAL
40
41#define TPM_TIS_NUM_LOCALITIES 5 /* per spec */
42#define TPM_TIS_LOCALITY_SHIFT 12
43#define TPM_TIS_NO_LOCALITY 0xff
44
45#define TPM_TIS_IS_VALID_LOCTY(x) ((x) < TPM_TIS_NUM_LOCALITIES)
46
47#define TPM_TIS_BUFFER_MAX 4096
48
49typedef enum {
50 TPM_TIS_STATE_IDLE = 0,
51 TPM_TIS_STATE_READY,
52 TPM_TIS_STATE_COMPLETION,
53 TPM_TIS_STATE_EXECUTION,
54 TPM_TIS_STATE_RECEPTION,
55} TPMTISState;
56
732cd587
MAL
57/* locality data -- all fields are persisted */
58typedef struct TPMLocality {
59 TPMTISState state;
60 uint8_t access;
61 uint32_t sts;
62 uint32_t iface_id;
63 uint32_t inte;
64 uint32_t ints;
732cd587
MAL
65} TPMLocality;
66
36e86589 67typedef struct TPMState {
3d4960c7
MAL
68 ISADevice busdev;
69 MemoryRegion mmio;
70
c5496b97 71 unsigned char buffer[TPM_TIS_BUFFER_MAX];
f999d81b 72 uint16_t rw_offset;
732cd587
MAL
73
74 uint8_t active_locty;
75 uint8_t aborting_locty;
76 uint8_t next_locty;
77
78 TPMLocality loc[TPM_TIS_NUM_LOCALITIES];
79
80 qemu_irq irq;
81 uint32_t irq_num;
732cd587 82
732cd587
MAL
83 TPMBackendCmd cmd;
84
732cd587
MAL
85 TPMBackend *be_driver;
86 TPMVersion be_tpm_version;
b21e6aaf
SB
87
88 size_t be_buffer_size;
b6148757
MAL
89
90 bool ppi_enabled;
3b97c01e 91 TPMPPI ppi;
36e86589 92} TPMState;
732cd587
MAL
93
94#define TPM(obj) OBJECT_CHECK(TPMState, (obj), TYPE_TPM_TIS)
edff8678 95
4d1ba9c4 96#define DEBUG_TIS 0
edff8678 97
8db7c415
SB
98/* local prototypes */
99
100static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr,
101 unsigned size);
102
edff8678
SB
103/* utility functions */
104
105static uint8_t tpm_tis_locality_from_addr(hwaddr addr)
106{
107 return (uint8_t)((addr >> TPM_TIS_LOCALITY_SHIFT) & 0x7);
108}
109
edff8678 110
fd859081
SB
111/*
112 * Set the given flags in the STS register by clearing the register but
116694c3
SB
113 * preserving the SELFTEST_DONE and TPM_FAMILY_MASK flags and then setting
114 * the new flags.
fd859081
SB
115 *
116 * The SELFTEST_DONE flag is acquired from the backend that determines it by
117 * peeking into TPM commands.
118 *
119 * A VM suspend/resume will preserve the flag by storing it into the VM
120 * device state, but the backend will not remember it when QEMU is started
121 * again. Therefore, we cache the flag here. Once set, it will not be unset
122 * except by a reset.
123 */
124static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags)
125{
116694c3 126 l->sts &= TPM_TIS_STS_SELFTEST_DONE | TPM_TIS_STS_TPM_FAMILY_MASK;
fd859081
SB
127 l->sts |= flags;
128}
129
edff8678
SB
130/*
131 * Send a request to the TPM.
132 */
133static void tpm_tis_tpm_send(TPMState *s, uint8_t locty)
134{
3688d73b
SB
135 if (trace_event_get_state_backends(TRACE_TPM_UTIL_SHOW_BUFFER)) {
136 tpm_util_show_buffer(s->buffer, s->be_buffer_size, "To TPM");
fcbed221 137 }
edff8678 138
edff8678 139 /*
f999d81b 140 * rw_offset serves as length indicator for length of data;
edff8678
SB
141 * it's reset when the response comes back
142 */
3d4960c7 143 s->loc[locty].state = TPM_TIS_STATE_EXECUTION;
edff8678 144
0e43b7e6
MAL
145 s->cmd = (TPMBackendCmd) {
146 .locty = locty,
c5496b97 147 .in = s->buffer,
f999d81b 148 .in_len = s->rw_offset,
c5496b97 149 .out = s->buffer,
e6b703f6 150 .out_len = s->be_buffer_size,
0e43b7e6
MAL
151 };
152
153 tpm_backend_deliver_request(s->be_driver, &s->cmd);
edff8678
SB
154}
155
156/* raise an interrupt if allowed */
157static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask)
158{
edff8678
SB
159 if (!TPM_TIS_IS_VALID_LOCTY(locty)) {
160 return;
161 }
162
3d4960c7
MAL
163 if ((s->loc[locty].inte & TPM_TIS_INT_ENABLED) &&
164 (s->loc[locty].inte & irqmask)) {
fcbed221 165 trace_tpm_tis_raise_irq(irqmask);
3d4960c7
MAL
166 qemu_irq_raise(s->irq);
167 s->loc[locty].ints |= irqmask;
edff8678
SB
168 }
169}
170
171static uint32_t tpm_tis_check_request_use_except(TPMState *s, uint8_t locty)
172{
173 uint8_t l;
174
175 for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
176 if (l == locty) {
177 continue;
178 }
3d4960c7 179 if ((s->loc[l].access & TPM_TIS_ACCESS_REQUEST_USE)) {
edff8678
SB
180 return 1;
181 }
182 }
183
184 return 0;
185}
186
187static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty)
188{
3d4960c7 189 bool change = (s->active_locty != new_active_locty);
edff8678
SB
190 bool is_seize;
191 uint8_t mask;
192
3d4960c7 193 if (change && TPM_TIS_IS_VALID_LOCTY(s->active_locty)) {
edff8678 194 is_seize = TPM_TIS_IS_VALID_LOCTY(new_active_locty) &&
3d4960c7 195 s->loc[new_active_locty].access & TPM_TIS_ACCESS_SEIZE;
edff8678
SB
196
197 if (is_seize) {
198 mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY);
199 } else {
200 mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY|
201 TPM_TIS_ACCESS_REQUEST_USE);
202 }
203 /* reset flags on the old active locality */
3d4960c7 204 s->loc[s->active_locty].access &= mask;
edff8678
SB
205
206 if (is_seize) {
3d4960c7 207 s->loc[s->active_locty].access |= TPM_TIS_ACCESS_BEEN_SEIZED;
edff8678
SB
208 }
209 }
210
3d4960c7 211 s->active_locty = new_active_locty;
edff8678 212
fcbed221 213 trace_tpm_tis_new_active_locality(s->active_locty);
edff8678
SB
214
215 if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) {
216 /* set flags on the new active locality */
3d4960c7
MAL
217 s->loc[new_active_locty].access |= TPM_TIS_ACCESS_ACTIVE_LOCALITY;
218 s->loc[new_active_locty].access &= ~(TPM_TIS_ACCESS_REQUEST_USE |
edff8678
SB
219 TPM_TIS_ACCESS_SEIZE);
220 }
221
222 if (change) {
3d4960c7 223 tpm_tis_raise_irq(s, s->active_locty, TPM_TIS_INT_LOCALITY_CHANGED);
edff8678
SB
224 }
225}
226
227/* abort -- this function switches the locality */
0f5faee3 228static void tpm_tis_abort(TPMState *s)
edff8678 229{
f999d81b 230 s->rw_offset = 0;
edff8678 231
fcbed221 232 trace_tpm_tis_abort(s->next_locty);
edff8678
SB
233
234 /*
235 * Need to react differently depending on who's aborting now and
236 * which locality will become active afterwards.
237 */
3d4960c7
MAL
238 if (s->aborting_locty == s->next_locty) {
239 s->loc[s->aborting_locty].state = TPM_TIS_STATE_READY;
240 tpm_tis_sts_set(&s->loc[s->aborting_locty],
fd859081 241 TPM_TIS_STS_COMMAND_READY);
3d4960c7 242 tpm_tis_raise_irq(s, s->aborting_locty, TPM_TIS_INT_COMMAND_READY);
edff8678
SB
243 }
244
245 /* locality after abort is another one than the current one */
3d4960c7 246 tpm_tis_new_active_locality(s, s->next_locty);
edff8678 247
3d4960c7 248 s->next_locty = TPM_TIS_NO_LOCALITY;
edff8678 249 /* nobody's aborting a command anymore */
3d4960c7 250 s->aborting_locty = TPM_TIS_NO_LOCALITY;
edff8678
SB
251}
252
253/* prepare aborting current command */
254static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty)
255{
edff8678
SB
256 uint8_t busy_locty;
257
e92b63ea
SB
258 assert(TPM_TIS_IS_VALID_LOCTY(newlocty));
259
260 s->aborting_locty = locty; /* may also be TPM_TIS_NO_LOCALITY */
3d4960c7 261 s->next_locty = newlocty; /* locality after successful abort */
edff8678
SB
262
263 /*
264 * only abort a command using an interrupt if currently executing
265 * a command AND if there's a valid connection to the vTPM.
266 */
267 for (busy_locty = 0; busy_locty < TPM_TIS_NUM_LOCALITIES; busy_locty++) {
3d4960c7 268 if (s->loc[busy_locty].state == TPM_TIS_STATE_EXECUTION) {
edff8678
SB
269 /*
270 * request the backend to cancel. Some backends may not
271 * support it
272 */
8f0605cc 273 tpm_backend_cancel_cmd(s->be_driver);
edff8678
SB
274 return;
275 }
276 }
277
0f5faee3 278 tpm_tis_abort(s);
edff8678
SB
279}
280
68999059
MAL
281/*
282 * Callback from the TPM to indicate that the response was received.
283 */
6a8a2354 284static void tpm_tis_request_completed(TPMIf *ti, int ret)
edff8678 285{
68999059 286 TPMState *s = TPM(ti);
0e43b7e6 287 uint8_t locty = s->cmd.locty;
68999059
MAL
288 uint8_t l;
289
a639f961
SB
290 assert(TPM_TIS_IS_VALID_LOCTY(locty));
291
68999059
MAL
292 if (s->cmd.selftest_done) {
293 for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
6a50bb98 294 s->loc[l].sts |= TPM_TIS_STS_SELFTEST_DONE;
68999059
MAL
295 }
296 }
edff8678 297
6a8a2354 298 /* FIXME: report error if ret != 0 */
3d4960c7 299 tpm_tis_sts_set(&s->loc[locty],
fd859081 300 TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE);
3d4960c7 301 s->loc[locty].state = TPM_TIS_STATE_COMPLETION;
f999d81b 302 s->rw_offset = 0;
edff8678 303
3688d73b
SB
304 if (trace_event_get_state_backends(TRACE_TPM_UTIL_SHOW_BUFFER)) {
305 tpm_util_show_buffer(s->buffer, s->be_buffer_size, "From TPM");
fcbed221 306 }
298d8b81 307
3d4960c7 308 if (TPM_TIS_IS_VALID_LOCTY(s->next_locty)) {
0f5faee3 309 tpm_tis_abort(s);
edff8678
SB
310 }
311
edff8678
SB
312 tpm_tis_raise_irq(s, locty,
313 TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID);
edff8678
SB
314}
315
edff8678
SB
316/*
317 * Read a byte of response data
318 */
319static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty)
320{
edff8678
SB
321 uint32_t ret = TPM_TIS_NO_DATA_BYTE;
322 uint16_t len;
323
3d4960c7 324 if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
c5496b97 325 len = MIN(tpm_cmd_get_size(&s->buffer),
e6b703f6 326 s->be_buffer_size);
edff8678 327
f999d81b
SB
328 ret = s->buffer[s->rw_offset++];
329 if (s->rw_offset >= len) {
edff8678 330 /* got last byte */
3d4960c7 331 tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID);
edff8678 332 tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
edff8678 333 }
fcbed221 334 trace_tpm_tis_data_read(ret, s->rw_offset - 1);
edff8678
SB
335 }
336
337 return ret;
338}
339
8db7c415
SB
340#ifdef DEBUG_TIS
341static void tpm_tis_dump_state(void *opaque, hwaddr addr)
342{
343 static const unsigned regs[] = {
344 TPM_TIS_REG_ACCESS,
345 TPM_TIS_REG_INT_ENABLE,
346 TPM_TIS_REG_INT_VECTOR,
347 TPM_TIS_REG_INT_STATUS,
348 TPM_TIS_REG_INTF_CAPABILITY,
349 TPM_TIS_REG_STS,
350 TPM_TIS_REG_DID_VID,
351 TPM_TIS_REG_RID,
352 0xfff};
353 int idx;
354 uint8_t locty = tpm_tis_locality_from_addr(addr);
355 hwaddr base = addr & ~0xfff;
356 TPMState *s = opaque;
8db7c415 357
fcbed221
SB
358 printf("tpm_tis: active locality : %d\n"
359 "tpm_tis: state of locality %d : %d\n"
360 "tpm_tis: register dump:\n",
361 s->active_locty,
362 locty, s->loc[locty].state);
8db7c415
SB
363
364 for (idx = 0; regs[idx] != 0xfff; idx++) {
fcbed221
SB
365 printf("tpm_tis: 0x%04x : 0x%08x\n", regs[idx],
366 (int)tpm_tis_mmio_read(opaque, base + regs[idx], 4));
8db7c415
SB
367 }
368
fcbed221
SB
369 printf("tpm_tis: r/w offset : %d\n"
370 "tpm_tis: result buffer : ",
371 s->rw_offset);
8db7c415 372 for (idx = 0;
c5496b97 373 idx < MIN(tpm_cmd_get_size(&s->buffer), s->be_buffer_size);
8db7c415 374 idx++) {
fcbed221
SB
375 printf("%c%02x%s",
376 s->rw_offset == idx ? '>' : ' ',
377 s->buffer[idx],
378 ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : "");
8db7c415 379 }
fcbed221 380 printf("\n");
8db7c415
SB
381}
382#endif
383
edff8678
SB
384/*
385 * Read a register of the TIS interface
386 * See specs pages 33-63 for description of the registers
387 */
388static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr,
389 unsigned size)
390{
391 TPMState *s = opaque;
edff8678
SB
392 uint16_t offset = addr & 0xffc;
393 uint8_t shift = (addr & 0x3) * 8;
394 uint32_t val = 0xffffffff;
395 uint8_t locty = tpm_tis_locality_from_addr(addr);
396 uint32_t avail;
feeb755f 397 uint8_t v;
edff8678 398
8f0605cc 399 if (tpm_backend_had_startup_error(s->be_driver)) {
6cd65969 400 return 0;
edff8678
SB
401 }
402
403 switch (offset) {
404 case TPM_TIS_REG_ACCESS:
405 /* never show the SEIZE flag even though we use it internally */
3d4960c7 406 val = s->loc[locty].access & ~TPM_TIS_ACCESS_SEIZE;
edff8678
SB
407 /* the pending flag is always calculated */
408 if (tpm_tis_check_request_use_except(s, locty)) {
409 val |= TPM_TIS_ACCESS_PENDING_REQUEST;
410 }
8f0605cc 411 val |= !tpm_backend_get_tpm_established_flag(s->be_driver);
edff8678
SB
412 break;
413 case TPM_TIS_REG_INT_ENABLE:
3d4960c7 414 val = s->loc[locty].inte;
edff8678
SB
415 break;
416 case TPM_TIS_REG_INT_VECTOR:
3d4960c7 417 val = s->irq_num;
edff8678
SB
418 break;
419 case TPM_TIS_REG_INT_STATUS:
3d4960c7 420 val = s->loc[locty].ints;
edff8678
SB
421 break;
422 case TPM_TIS_REG_INTF_CAPABILITY:
116694c3
SB
423 switch (s->be_tpm_version) {
424 case TPM_VERSION_UNSPEC:
425 val = 0;
426 break;
427 case TPM_VERSION_1_2:
428 val = TPM_TIS_CAPABILITIES_SUPPORTED1_3;
429 break;
430 case TPM_VERSION_2_0:
431 val = TPM_TIS_CAPABILITIES_SUPPORTED2_0;
432 break;
433 }
edff8678
SB
434 break;
435 case TPM_TIS_REG_STS:
3d4960c7
MAL
436 if (s->active_locty == locty) {
437 if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
edff8678 438 val = TPM_TIS_BURST_COUNT(
c5496b97 439 MIN(tpm_cmd_get_size(&s->buffer),
e6b703f6 440 s->be_buffer_size)
f999d81b 441 - s->rw_offset) | s->loc[locty].sts;
edff8678 442 } else {
f999d81b 443 avail = s->be_buffer_size - s->rw_offset;
edff8678
SB
444 /*
445 * byte-sized reads should not return 0x00 for 0x100
446 * available bytes.
447 */
448 if (size == 1 && avail > 0xff) {
449 avail = 0xff;
450 }
3d4960c7 451 val = TPM_TIS_BURST_COUNT(avail) | s->loc[locty].sts;
edff8678
SB
452 }
453 }
454 break;
455 case TPM_TIS_REG_DATA_FIFO:
2eae8c75 456 case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END:
3d4960c7 457 if (s->active_locty == locty) {
feeb755f
SB
458 if (size > 4 - (addr & 0x3)) {
459 /* prevent access beyond FIFO */
460 size = 4 - (addr & 0x3);
461 }
462 val = 0;
463 shift = 0;
464 while (size > 0) {
3d4960c7 465 switch (s->loc[locty].state) {
feeb755f
SB
466 case TPM_TIS_STATE_COMPLETION:
467 v = tpm_tis_data_read(s, locty);
468 break;
469 default:
470 v = TPM_TIS_NO_DATA_BYTE;
471 break;
472 }
473 val |= (v << shift);
474 shift += 8;
475 size--;
edff8678 476 }
feeb755f 477 shift = 0; /* no more adjustments */
edff8678
SB
478 }
479 break;
116694c3 480 case TPM_TIS_REG_INTERFACE_ID:
3d4960c7 481 val = s->loc[locty].iface_id;
116694c3 482 break;
edff8678
SB
483 case TPM_TIS_REG_DID_VID:
484 val = (TPM_TIS_TPM_DID << 16) | TPM_TIS_TPM_VID;
485 break;
486 case TPM_TIS_REG_RID:
487 val = TPM_TIS_TPM_RID;
488 break;
8db7c415
SB
489#ifdef DEBUG_TIS
490 case TPM_TIS_REG_DEBUG:
491 tpm_tis_dump_state(opaque, addr);
492 break;
493#endif
edff8678
SB
494 }
495
496 if (shift) {
497 val >>= shift;
498 }
499
fcbed221 500 trace_tpm_tis_mmio_read(size, addr, val);
edff8678
SB
501
502 return val;
503}
504
505/*
506 * Write a value to a register of the TIS interface
507 * See specs pages 33-63 for description of the registers
508 */
ff2bc0c1
MAL
509static void tpm_tis_mmio_write(void *opaque, hwaddr addr,
510 uint64_t val, unsigned size)
edff8678
SB
511{
512 TPMState *s = opaque;
feeb755f
SB
513 uint16_t off = addr & 0xffc;
514 uint8_t shift = (addr & 0x3) * 8;
edff8678
SB
515 uint8_t locty = tpm_tis_locality_from_addr(addr);
516 uint8_t active_locty, l;
517 int c, set_new_locty = 1;
518 uint16_t len;
feeb755f 519 uint32_t mask = (size == 1) ? 0xff : ((size == 2) ? 0xffff : ~0);
edff8678 520
fcbed221 521 trace_tpm_tis_mmio_write(size, addr, val);
edff8678 522
ff2bc0c1 523 if (locty == 4) {
fcbed221 524 trace_tpm_tis_mmio_write_locty4();
edff8678
SB
525 return;
526 }
527
8f0605cc 528 if (tpm_backend_had_startup_error(s->be_driver)) {
edff8678
SB
529 return;
530 }
531
feeb755f
SB
532 val &= mask;
533
534 if (shift) {
535 val <<= shift;
536 mask <<= shift;
537 }
538
539 mask ^= 0xffffffff;
540
edff8678
SB
541 switch (off) {
542 case TPM_TIS_REG_ACCESS:
543
544 if ((val & TPM_TIS_ACCESS_SEIZE)) {
545 val &= ~(TPM_TIS_ACCESS_REQUEST_USE |
546 TPM_TIS_ACCESS_ACTIVE_LOCALITY);
547 }
548
3d4960c7 549 active_locty = s->active_locty;
edff8678
SB
550
551 if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) {
552 /* give up locality if currently owned */
3d4960c7 553 if (s->active_locty == locty) {
fcbed221 554 trace_tpm_tis_mmio_write_release_locty(locty);
edff8678
SB
555
556 uint8_t newlocty = TPM_TIS_NO_LOCALITY;
557 /* anybody wants the locality ? */
558 for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) {
3d4960c7 559 if ((s->loc[c].access & TPM_TIS_ACCESS_REQUEST_USE)) {
fcbed221 560 trace_tpm_tis_mmio_write_locty_req_use(c);
edff8678
SB
561 newlocty = c;
562 break;
563 }
564 }
fcbed221 565 trace_tpm_tis_mmio_write_next_locty(newlocty);
edff8678
SB
566
567 if (TPM_TIS_IS_VALID_LOCTY(newlocty)) {
568 set_new_locty = 0;
569 tpm_tis_prep_abort(s, locty, newlocty);
570 } else {
571 active_locty = TPM_TIS_NO_LOCALITY;
572 }
573 } else {
574 /* not currently the owner; clear a pending request */
3d4960c7 575 s->loc[locty].access &= ~TPM_TIS_ACCESS_REQUEST_USE;
edff8678
SB
576 }
577 }
578
579 if ((val & TPM_TIS_ACCESS_BEEN_SEIZED)) {
3d4960c7 580 s->loc[locty].access &= ~TPM_TIS_ACCESS_BEEN_SEIZED;
edff8678
SB
581 }
582
583 if ((val & TPM_TIS_ACCESS_SEIZE)) {
584 /*
585 * allow seize if a locality is active and the requesting
586 * locality is higher than the one that's active
587 * OR
588 * allow seize for requesting locality if no locality is
589 * active
590 */
3d4960c7
MAL
591 while ((TPM_TIS_IS_VALID_LOCTY(s->active_locty) &&
592 locty > s->active_locty) ||
593 !TPM_TIS_IS_VALID_LOCTY(s->active_locty)) {
edff8678
SB
594 bool higher_seize = FALSE;
595
596 /* already a pending SEIZE ? */
3d4960c7 597 if ((s->loc[locty].access & TPM_TIS_ACCESS_SEIZE)) {
edff8678
SB
598 break;
599 }
600
601 /* check for ongoing seize by a higher locality */
602 for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES; l++) {
3d4960c7 603 if ((s->loc[l].access & TPM_TIS_ACCESS_SEIZE)) {
edff8678
SB
604 higher_seize = TRUE;
605 break;
606 }
607 }
608
609 if (higher_seize) {
610 break;
611 }
612
613 /* cancel any seize by a lower locality */
37b55d67 614 for (l = 0; l < locty; l++) {
3d4960c7 615 s->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE;
edff8678
SB
616 }
617
3d4960c7 618 s->loc[locty].access |= TPM_TIS_ACCESS_SEIZE;
fcbed221
SB
619
620 trace_tpm_tis_mmio_write_locty_seized(locty, s->active_locty);
621 trace_tpm_tis_mmio_write_init_abort();
622
edff8678 623 set_new_locty = 0;
3d4960c7 624 tpm_tis_prep_abort(s, s->active_locty, locty);
edff8678
SB
625 break;
626 }
627 }
628
629 if ((val & TPM_TIS_ACCESS_REQUEST_USE)) {
3d4960c7
MAL
630 if (s->active_locty != locty) {
631 if (TPM_TIS_IS_VALID_LOCTY(s->active_locty)) {
632 s->loc[locty].access |= TPM_TIS_ACCESS_REQUEST_USE;
edff8678
SB
633 } else {
634 /* no locality active -> make this one active now */
635 active_locty = locty;
636 }
637 }
638 }
639
640 if (set_new_locty) {
641 tpm_tis_new_active_locality(s, active_locty);
642 }
643
644 break;
645 case TPM_TIS_REG_INT_ENABLE:
3d4960c7 646 if (s->active_locty != locty) {
edff8678
SB
647 break;
648 }
649
3d4960c7
MAL
650 s->loc[locty].inte &= mask;
651 s->loc[locty].inte |= (val & (TPM_TIS_INT_ENABLED |
feeb755f
SB
652 TPM_TIS_INT_POLARITY_MASK |
653 TPM_TIS_INTERRUPTS_SUPPORTED));
edff8678
SB
654 break;
655 case TPM_TIS_REG_INT_VECTOR:
656 /* hard wired -- ignore */
657 break;
658 case TPM_TIS_REG_INT_STATUS:
3d4960c7 659 if (s->active_locty != locty) {
edff8678
SB
660 break;
661 }
662
663 /* clearing of interrupt flags */
664 if (((val & TPM_TIS_INTERRUPTS_SUPPORTED)) &&
3d4960c7
MAL
665 (s->loc[locty].ints & TPM_TIS_INTERRUPTS_SUPPORTED)) {
666 s->loc[locty].ints &= ~val;
667 if (s->loc[locty].ints == 0) {
668 qemu_irq_lower(s->irq);
fcbed221 669 trace_tpm_tis_mmio_write_lowering_irq();
edff8678
SB
670 }
671 }
3d4960c7 672 s->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED);
edff8678
SB
673 break;
674 case TPM_TIS_REG_STS:
3d4960c7 675 if (s->active_locty != locty) {
edff8678
SB
676 break;
677 }
678
116694c3
SB
679 if (s->be_tpm_version == TPM_VERSION_2_0) {
680 /* some flags that are only supported for TPM 2 */
681 if (val & TPM_TIS_STS_COMMAND_CANCEL) {
3d4960c7 682 if (s->loc[locty].state == TPM_TIS_STATE_EXECUTION) {
116694c3
SB
683 /*
684 * request the backend to cancel. Some backends may not
685 * support it
686 */
687 tpm_backend_cancel_cmd(s->be_driver);
688 }
689 }
690
691 if (val & TPM_TIS_STS_RESET_ESTABLISHMENT_BIT) {
692 if (locty == 3 || locty == 4) {
693 tpm_backend_reset_tpm_established_flag(s->be_driver, locty);
694 }
695 }
696 }
697
edff8678
SB
698 val &= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO |
699 TPM_TIS_STS_RESPONSE_RETRY);
700
701 if (val == TPM_TIS_STS_COMMAND_READY) {
3d4960c7 702 switch (s->loc[locty].state) {
edff8678
SB
703
704 case TPM_TIS_STATE_READY:
f999d81b 705 s->rw_offset = 0;
edff8678
SB
706 break;
707
708 case TPM_TIS_STATE_IDLE:
3d4960c7
MAL
709 tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_COMMAND_READY);
710 s->loc[locty].state = TPM_TIS_STATE_READY;
edff8678
SB
711 tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
712 break;
713
714 case TPM_TIS_STATE_EXECUTION:
715 case TPM_TIS_STATE_RECEPTION:
716 /* abort currently running command */
fcbed221 717 trace_tpm_tis_mmio_write_init_abort();
edff8678
SB
718 tpm_tis_prep_abort(s, locty, locty);
719 break;
720
721 case TPM_TIS_STATE_COMPLETION:
f999d81b 722 s->rw_offset = 0;
edff8678 723 /* shortcut to ready state with C/R set */
3d4960c7
MAL
724 s->loc[locty].state = TPM_TIS_STATE_READY;
725 if (!(s->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) {
726 tpm_tis_sts_set(&s->loc[locty],
fd859081 727 TPM_TIS_STS_COMMAND_READY);
edff8678
SB
728 tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
729 }
3d4960c7 730 s->loc[locty].sts &= ~(TPM_TIS_STS_DATA_AVAILABLE);
edff8678
SB
731 break;
732
733 }
734 } else if (val == TPM_TIS_STS_TPM_GO) {
3d4960c7 735 switch (s->loc[locty].state) {
edff8678 736 case TPM_TIS_STATE_RECEPTION:
3d4960c7 737 if ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) == 0) {
edff8678
SB
738 tpm_tis_tpm_send(s, locty);
739 }
740 break;
741 default:
742 /* ignore */
743 break;
744 }
745 } else if (val == TPM_TIS_STS_RESPONSE_RETRY) {
3d4960c7 746 switch (s->loc[locty].state) {
edff8678 747 case TPM_TIS_STATE_COMPLETION:
f999d81b 748 s->rw_offset = 0;
3d4960c7 749 tpm_tis_sts_set(&s->loc[locty],
fd859081
SB
750 TPM_TIS_STS_VALID|
751 TPM_TIS_STS_DATA_AVAILABLE);
edff8678
SB
752 break;
753 default:
754 /* ignore */
755 break;
756 }
757 }
758 break;
759 case TPM_TIS_REG_DATA_FIFO:
2eae8c75 760 case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END:
edff8678 761 /* data fifo */
3d4960c7 762 if (s->active_locty != locty) {
edff8678
SB
763 break;
764 }
765
3d4960c7
MAL
766 if (s->loc[locty].state == TPM_TIS_STATE_IDLE ||
767 s->loc[locty].state == TPM_TIS_STATE_EXECUTION ||
768 s->loc[locty].state == TPM_TIS_STATE_COMPLETION) {
edff8678
SB
769 /* drop the byte */
770 } else {
fcbed221 771 trace_tpm_tis_mmio_write_data2send(val, size);
3d4960c7
MAL
772 if (s->loc[locty].state == TPM_TIS_STATE_READY) {
773 s->loc[locty].state = TPM_TIS_STATE_RECEPTION;
774 tpm_tis_sts_set(&s->loc[locty],
fd859081 775 TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID);
edff8678
SB
776 }
777
feeb755f
SB
778 val >>= shift;
779 if (size > 4 - (addr & 0x3)) {
780 /* prevent access beyond FIFO */
781 size = 4 - (addr & 0x3);
782 }
783
3d4960c7 784 while ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) && size > 0) {
f999d81b
SB
785 if (s->rw_offset < s->be_buffer_size) {
786 s->buffer[s->rw_offset++] =
e6b703f6 787 (uint8_t)val;
feeb755f
SB
788 val >>= 8;
789 size--;
edff8678 790 } else {
3d4960c7 791 tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID);
edff8678
SB
792 }
793 }
794
795 /* check for complete packet */
f999d81b 796 if (s->rw_offset > 5 &&
3d4960c7 797 (s->loc[locty].sts & TPM_TIS_STS_EXPECT)) {
edff8678 798 /* we have a packet length - see if we have all of it */
3d4960c7 799 bool need_irq = !(s->loc[locty].sts & TPM_TIS_STS_VALID);
d8383d61 800
c5496b97 801 len = tpm_cmd_get_size(&s->buffer);
f999d81b 802 if (len > s->rw_offset) {
3d4960c7 803 tpm_tis_sts_set(&s->loc[locty],
fd859081 804 TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID);
edff8678
SB
805 } else {
806 /* packet complete */
3d4960c7 807 tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID);
edff8678 808 }
29b558d8 809 if (need_irq) {
edff8678
SB
810 tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
811 }
edff8678
SB
812 }
813 }
814 break;
116694c3
SB
815 case TPM_TIS_REG_INTERFACE_ID:
816 if (val & TPM_TIS_IFACE_ID_INT_SEL_LOCK) {
817 for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
3d4960c7 818 s->loc[l].iface_id |= TPM_TIS_IFACE_ID_INT_SEL_LOCK;
116694c3
SB
819 }
820 }
821 break;
edff8678
SB
822 }
823}
824
edff8678
SB
825static const MemoryRegionOps tpm_tis_memory_ops = {
826 .read = tpm_tis_mmio_read,
827 .write = tpm_tis_mmio_write,
828 .endianness = DEVICE_LITTLE_ENDIAN,
829 .valid = {
830 .min_access_size = 1,
831 .max_access_size = 4,
832 },
833};
834
5cb18b3d
SB
835/*
836 * Get the TPMVersion of the backend device being used
837 */
9af7a721 838static enum TPMVersion tpm_tis_get_tpm_version(TPMIf *ti)
5cb18b3d 839{
9af7a721 840 TPMState *s = TPM(ti);
5cb18b3d 841
ad4aca69
SB
842 if (tpm_backend_had_startup_error(s->be_driver)) {
843 return TPM_VERSION_UNSPEC;
844 }
845
5cb18b3d
SB
846 return tpm_backend_get_tpm_version(s->be_driver);
847}
848
edff8678
SB
849/*
850 * This function is called when the machine starts, resets or due to
851 * S3 resume.
852 */
853static void tpm_tis_reset(DeviceState *dev)
854{
855 TPMState *s = TPM(dev);
edff8678
SB
856 int c;
857
116694c3 858 s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver);
1af3d63e
SB
859 s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->be_driver),
860 TPM_TIS_BUFFER_MAX);
116694c3 861
ffab1be7
MAL
862 if (s->ppi_enabled) {
863 tpm_ppi_reset(&s->ppi);
864 }
8f0605cc 865 tpm_backend_reset(s->be_driver);
edff8678 866
3d4960c7
MAL
867 s->active_locty = TPM_TIS_NO_LOCALITY;
868 s->next_locty = TPM_TIS_NO_LOCALITY;
869 s->aborting_locty = TPM_TIS_NO_LOCALITY;
edff8678
SB
870
871 for (c = 0; c < TPM_TIS_NUM_LOCALITIES; c++) {
3d4960c7 872 s->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS;
116694c3
SB
873 switch (s->be_tpm_version) {
874 case TPM_VERSION_UNSPEC:
875 break;
876 case TPM_VERSION_1_2:
3d4960c7
MAL
877 s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY1_2;
878 s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3;
116694c3
SB
879 break;
880 case TPM_VERSION_2_0:
3d4960c7
MAL
881 s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY2_0;
882 s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0;
116694c3
SB
883 break;
884 }
3d4960c7
MAL
885 s->loc[c].inte = TPM_TIS_INT_POLARITY_LOW_LEVEL;
886 s->loc[c].ints = 0;
887 s->loc[c].state = TPM_TIS_STATE_IDLE;
888
f999d81b 889 s->rw_offset = 0;
edff8678
SB
890 }
891
bcfd16fe
SB
892 if (tpm_backend_startup_tpm(s->be_driver, s->be_buffer_size) < 0) {
893 exit(1);
894 }
edff8678
SB
895}
896
9ec08c48
SB
897/* persistent state handling */
898
899static int tpm_tis_pre_save(void *opaque)
900{
901 TPMState *s = opaque;
902 uint8_t locty = s->active_locty;
903
904 trace_tpm_tis_pre_save(locty, s->rw_offset);
905
906 if (DEBUG_TIS) {
907 tpm_tis_dump_state(opaque, 0);
908 }
909
910 /*
911 * Synchronize with backend completion.
912 */
913 tpm_backend_finish_sync(s->be_driver);
914
915 return 0;
916}
917
918static const VMStateDescription vmstate_locty = {
919 .name = "tpm-tis/locty",
920 .version_id = 0,
921 .fields = (VMStateField[]) {
922 VMSTATE_UINT32(state, TPMLocality),
923 VMSTATE_UINT32(inte, TPMLocality),
924 VMSTATE_UINT32(ints, TPMLocality),
925 VMSTATE_UINT8(access, TPMLocality),
926 VMSTATE_UINT32(sts, TPMLocality),
927 VMSTATE_UINT32(iface_id, TPMLocality),
928 VMSTATE_END_OF_LIST(),
929 }
930};
931
edff8678 932static const VMStateDescription vmstate_tpm_tis = {
9ec08c48
SB
933 .name = "tpm-tis",
934 .version_id = 0,
935 .pre_save = tpm_tis_pre_save,
936 .fields = (VMStateField[]) {
937 VMSTATE_BUFFER(buffer, TPMState),
938 VMSTATE_UINT16(rw_offset, TPMState),
939 VMSTATE_UINT8(active_locty, TPMState),
940 VMSTATE_UINT8(aborting_locty, TPMState),
941 VMSTATE_UINT8(next_locty, TPMState),
942
943 VMSTATE_STRUCT_ARRAY(loc, TPMState, TPM_TIS_NUM_LOCALITIES, 0,
944 vmstate_locty, TPMLocality),
945
946 VMSTATE_END_OF_LIST()
947 }
edff8678
SB
948};
949
950static Property tpm_tis_properties[] = {
3d4960c7 951 DEFINE_PROP_UINT32("irq", TPMState, irq_num, TPM_TIS_IRQ),
c0378544 952 DEFINE_PROP_TPMBE("tpmdev", TPMState, be_driver),
b6148757 953 DEFINE_PROP_BOOL("ppi", TPMState, ppi_enabled, true),
edff8678
SB
954 DEFINE_PROP_END_OF_LIST(),
955};
956
957static void tpm_tis_realizefn(DeviceState *dev, Error **errp)
958{
959 TPMState *s = TPM(dev);
edff8678 960
51a837e9
MAL
961 if (!tpm_find()) {
962 error_setg(errp, "at most one TPM device is permitted");
963 return;
964 }
965
edff8678 966 if (!s->be_driver) {
c0378544 967 error_setg(errp, "'tpmdev' property is required");
edff8678
SB
968 return;
969 }
3d4960c7 970 if (s->irq_num > 15) {
c87b35fa
MAL
971 error_setg(errp, "IRQ %d is outside valid range of 0 to 15",
972 s->irq_num);
edff8678
SB
973 return;
974 }
975
3d4960c7 976 isa_init_irq(&s->busdev, &s->irq, s->irq_num);
9dfd24ed
SB
977
978 memory_region_add_subregion(isa_address_space(ISA_DEVICE(dev)),
979 TPM_TIS_ADDR_BASE, &s->mmio);
3b97c01e
SB
980
981 if (s->ppi_enabled) {
982 tpm_ppi_init(&s->ppi, isa_address_space(ISA_DEVICE(dev)),
983 TPM_PPI_ADDR_BASE, OBJECT(s));
984 }
edff8678
SB
985}
986
987static void tpm_tis_initfn(Object *obj)
988{
edff8678
SB
989 TPMState *s = TPM(obj);
990
853dca12
PB
991 memory_region_init_io(&s->mmio, OBJECT(s), &tpm_tis_memory_ops,
992 s, "tpm-tis-mmio",
edff8678 993 TPM_TIS_NUM_LOCALITIES << TPM_TIS_LOCALITY_SHIFT);
edff8678
SB
994}
995
edff8678
SB
996static void tpm_tis_class_init(ObjectClass *klass, void *data)
997{
998 DeviceClass *dc = DEVICE_CLASS(klass);
05a69998 999 TPMIfClass *tc = TPM_IF_CLASS(klass);
edff8678
SB
1000
1001 dc->realize = tpm_tis_realizefn;
4f67d30b 1002 device_class_set_props(dc, tpm_tis_properties);
edff8678
SB
1003 dc->reset = tpm_tis_reset;
1004 dc->vmsd = &vmstate_tpm_tis;
191adc94 1005 tc->model = TPM_MODEL_TPM_TIS;
9af7a721 1006 tc->get_version = tpm_tis_get_tpm_version;
05a69998 1007 tc->request_completed = tpm_tis_request_completed;
edff8678
SB
1008}
1009
1010static const TypeInfo tpm_tis_info = {
1011 .name = TYPE_TPM_TIS,
1012 .parent = TYPE_ISA_DEVICE,
1013 .instance_size = sizeof(TPMState),
1014 .instance_init = tpm_tis_initfn,
edff8678 1015 .class_init = tpm_tis_class_init,
698f5daa
MAL
1016 .interfaces = (InterfaceInfo[]) {
1017 { TYPE_TPM_IF },
1018 { }
1019 }
edff8678
SB
1020};
1021
1022static void tpm_tis_register(void)
1023{
1024 type_register_static(&tpm_tis_info);
edff8678
SB
1025}
1026
1027type_init(tpm_tis_register)