Fix iotest 153
[qemu/ar7.git] / hw / tpm / tpm_tis_common.c
blob9ce64d483655b4f25abce1a168cd98529e6f1603
1 /*
2 * tpm_tis_common.c - QEMU's TPM TIS interface emulator
3 * device agnostic functions
5 * Copyright (C) 2006,2010-2013 IBM Corporation
7 * Authors:
8 * Stefan Berger <stefanb@us.ibm.com>
9 * David Safford <safford@us.ibm.com>
11 * Xen 4 support: Andrease Niederl <andreas.niederl@iaik.tugraz.at>
13 * This work is licensed under the terms of the GNU GPL, version 2 or later.
14 * See the COPYING file in the top-level directory.
16 * Implementation of the TIS interface according to specs found at
17 * http://www.trustedcomputinggroup.org. This implementation currently
18 * supports version 1.3, 21 March 2013
19 * In the developers menu choose the PC Client section then find the TIS
20 * specification.
22 * TPM TIS for TPM 2 implementation following TCG PC Client Platform
23 * TPM Profile (PTP) Specification, Familiy 2.0, Revision 00.43
25 #include "qemu/osdep.h"
26 #include "hw/irq.h"
27 #include "hw/isa/isa.h"
28 #include "qapi/error.h"
29 #include "qemu/module.h"
31 #include "hw/acpi/tpm.h"
32 #include "hw/pci/pci_ids.h"
33 #include "hw/qdev-properties.h"
34 #include "migration/vmstate.h"
35 #include "sysemu/tpm_backend.h"
36 #include "tpm_int.h"
37 #include "tpm_util.h"
38 #include "tpm_ppi.h"
39 #include "trace.h"
41 #include "tpm_tis.h"
43 #define DEBUG_TIS 0
45 /* local prototypes */
47 static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr,
48 unsigned size);
50 /* utility functions */
52 static uint8_t tpm_tis_locality_from_addr(hwaddr addr)
54 return (uint8_t)((addr >> TPM_TIS_LOCALITY_SHIFT) & 0x7);
59 * Set the given flags in the STS register by clearing the register but
60 * preserving the SELFTEST_DONE and TPM_FAMILY_MASK flags and then setting
61 * the new flags.
63 * The SELFTEST_DONE flag is acquired from the backend that determines it by
64 * peeking into TPM commands.
66 * A VM suspend/resume will preserve the flag by storing it into the VM
67 * device state, but the backend will not remember it when QEMU is started
68 * again. Therefore, we cache the flag here. Once set, it will not be unset
69 * except by a reset.
71 static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags)
73 l->sts &= TPM_TIS_STS_SELFTEST_DONE | TPM_TIS_STS_TPM_FAMILY_MASK;
74 l->sts |= flags;
78 * Send a request to the TPM.
80 static void tpm_tis_tpm_send(TPMState *s, uint8_t locty)
82 if (trace_event_get_state_backends(TRACE_TPM_UTIL_SHOW_BUFFER)) {
83 tpm_util_show_buffer(s->buffer, s->be_buffer_size, "To TPM");
87 * rw_offset serves as length indicator for length of data;
88 * it's reset when the response comes back
90 s->loc[locty].state = TPM_TIS_STATE_EXECUTION;
92 s->cmd = (TPMBackendCmd) {
93 .locty = locty,
94 .in = s->buffer,
95 .in_len = s->rw_offset,
96 .out = s->buffer,
97 .out_len = s->be_buffer_size,
100 tpm_backend_deliver_request(s->be_driver, &s->cmd);
103 /* raise an interrupt if allowed */
104 static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask)
106 if (!TPM_TIS_IS_VALID_LOCTY(locty)) {
107 return;
110 if ((s->loc[locty].inte & TPM_TIS_INT_ENABLED) &&
111 (s->loc[locty].inte & irqmask)) {
112 trace_tpm_tis_raise_irq(irqmask);
113 qemu_irq_raise(s->irq);
114 s->loc[locty].ints |= irqmask;
118 static uint32_t tpm_tis_check_request_use_except(TPMState *s, uint8_t locty)
120 uint8_t l;
122 for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
123 if (l == locty) {
124 continue;
126 if ((s->loc[l].access & TPM_TIS_ACCESS_REQUEST_USE)) {
127 return 1;
131 return 0;
134 static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty)
136 bool change = (s->active_locty != new_active_locty);
137 bool is_seize;
138 uint8_t mask;
140 if (change && TPM_TIS_IS_VALID_LOCTY(s->active_locty)) {
141 is_seize = TPM_TIS_IS_VALID_LOCTY(new_active_locty) &&
142 s->loc[new_active_locty].access & TPM_TIS_ACCESS_SEIZE;
144 if (is_seize) {
145 mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY);
146 } else {
147 mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY|
148 TPM_TIS_ACCESS_REQUEST_USE);
150 /* reset flags on the old active locality */
151 s->loc[s->active_locty].access &= mask;
153 if (is_seize) {
154 s->loc[s->active_locty].access |= TPM_TIS_ACCESS_BEEN_SEIZED;
158 s->active_locty = new_active_locty;
160 trace_tpm_tis_new_active_locality(s->active_locty);
162 if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) {
163 /* set flags on the new active locality */
164 s->loc[new_active_locty].access |= TPM_TIS_ACCESS_ACTIVE_LOCALITY;
165 s->loc[new_active_locty].access &= ~(TPM_TIS_ACCESS_REQUEST_USE |
166 TPM_TIS_ACCESS_SEIZE);
169 if (change) {
170 tpm_tis_raise_irq(s, s->active_locty, TPM_TIS_INT_LOCALITY_CHANGED);
174 /* abort -- this function switches the locality */
175 static void tpm_tis_abort(TPMState *s)
177 s->rw_offset = 0;
179 trace_tpm_tis_abort(s->next_locty);
182 * Need to react differently depending on who's aborting now and
183 * which locality will become active afterwards.
185 if (s->aborting_locty == s->next_locty) {
186 s->loc[s->aborting_locty].state = TPM_TIS_STATE_READY;
187 tpm_tis_sts_set(&s->loc[s->aborting_locty],
188 TPM_TIS_STS_COMMAND_READY);
189 tpm_tis_raise_irq(s, s->aborting_locty, TPM_TIS_INT_COMMAND_READY);
192 /* locality after abort is another one than the current one */
193 tpm_tis_new_active_locality(s, s->next_locty);
195 s->next_locty = TPM_TIS_NO_LOCALITY;
196 /* nobody's aborting a command anymore */
197 s->aborting_locty = TPM_TIS_NO_LOCALITY;
200 /* prepare aborting current command */
201 static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty)
203 uint8_t busy_locty;
205 assert(TPM_TIS_IS_VALID_LOCTY(newlocty));
207 s->aborting_locty = locty; /* may also be TPM_TIS_NO_LOCALITY */
208 s->next_locty = newlocty; /* locality after successful abort */
211 * only abort a command using an interrupt if currently executing
212 * a command AND if there's a valid connection to the vTPM.
214 for (busy_locty = 0; busy_locty < TPM_TIS_NUM_LOCALITIES; busy_locty++) {
215 if (s->loc[busy_locty].state == TPM_TIS_STATE_EXECUTION) {
217 * request the backend to cancel. Some backends may not
218 * support it
220 tpm_backend_cancel_cmd(s->be_driver);
221 return;
225 tpm_tis_abort(s);
229 * Callback from the TPM to indicate that the response was received.
231 void tpm_tis_request_completed(TPMState *s, int ret)
233 uint8_t locty = s->cmd.locty;
234 uint8_t l;
236 assert(TPM_TIS_IS_VALID_LOCTY(locty));
238 if (s->cmd.selftest_done) {
239 for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
240 s->loc[l].sts |= TPM_TIS_STS_SELFTEST_DONE;
244 /* FIXME: report error if ret != 0 */
245 tpm_tis_sts_set(&s->loc[locty],
246 TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE);
247 s->loc[locty].state = TPM_TIS_STATE_COMPLETION;
248 s->rw_offset = 0;
250 if (trace_event_get_state_backends(TRACE_TPM_UTIL_SHOW_BUFFER)) {
251 tpm_util_show_buffer(s->buffer, s->be_buffer_size, "From TPM");
254 if (TPM_TIS_IS_VALID_LOCTY(s->next_locty)) {
255 tpm_tis_abort(s);
258 tpm_tis_raise_irq(s, locty,
259 TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID);
263 * Read a byte of response data
265 static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty)
267 uint32_t ret = TPM_TIS_NO_DATA_BYTE;
268 uint16_t len;
270 if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
271 len = MIN(tpm_cmd_get_size(&s->buffer),
272 s->be_buffer_size);
274 ret = s->buffer[s->rw_offset++];
275 if (s->rw_offset >= len) {
276 /* got last byte */
277 tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID);
278 tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
280 trace_tpm_tis_data_read(ret, s->rw_offset - 1);
283 return ret;
286 #ifdef DEBUG_TIS
287 static void tpm_tis_dump_state(TPMState *s, hwaddr addr)
289 static const unsigned regs[] = {
290 TPM_TIS_REG_ACCESS,
291 TPM_TIS_REG_INT_ENABLE,
292 TPM_TIS_REG_INT_VECTOR,
293 TPM_TIS_REG_INT_STATUS,
294 TPM_TIS_REG_INTF_CAPABILITY,
295 TPM_TIS_REG_STS,
296 TPM_TIS_REG_DID_VID,
297 TPM_TIS_REG_RID,
298 0xfff};
299 int idx;
300 uint8_t locty = tpm_tis_locality_from_addr(addr);
301 hwaddr base = addr & ~0xfff;
303 printf("tpm_tis: active locality : %d\n"
304 "tpm_tis: state of locality %d : %d\n"
305 "tpm_tis: register dump:\n",
306 s->active_locty,
307 locty, s->loc[locty].state);
309 for (idx = 0; regs[idx] != 0xfff; idx++) {
310 printf("tpm_tis: 0x%04x : 0x%08x\n", regs[idx],
311 (int)tpm_tis_mmio_read(s, base + regs[idx], 4));
314 printf("tpm_tis: r/w offset : %d\n"
315 "tpm_tis: result buffer : ",
316 s->rw_offset);
317 for (idx = 0;
318 idx < MIN(tpm_cmd_get_size(&s->buffer), s->be_buffer_size);
319 idx++) {
320 printf("%c%02x%s",
321 s->rw_offset == idx ? '>' : ' ',
322 s->buffer[idx],
323 ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : "");
325 printf("\n");
327 #endif
330 * Read a register of the TIS interface
331 * See specs pages 33-63 for description of the registers
333 static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr,
334 unsigned size)
336 TPMState *s = opaque;
337 uint16_t offset = addr & 0xffc;
338 uint8_t shift = (addr & 0x3) * 8;
339 uint32_t val = 0xffffffff;
340 uint8_t locty = tpm_tis_locality_from_addr(addr);
341 uint32_t avail;
342 uint8_t v;
344 if (tpm_backend_had_startup_error(s->be_driver)) {
345 return 0;
348 switch (offset) {
349 case TPM_TIS_REG_ACCESS:
350 /* never show the SEIZE flag even though we use it internally */
351 val = s->loc[locty].access & ~TPM_TIS_ACCESS_SEIZE;
352 /* the pending flag is always calculated */
353 if (tpm_tis_check_request_use_except(s, locty)) {
354 val |= TPM_TIS_ACCESS_PENDING_REQUEST;
356 val |= !tpm_backend_get_tpm_established_flag(s->be_driver);
357 break;
358 case TPM_TIS_REG_INT_ENABLE:
359 val = s->loc[locty].inte;
360 break;
361 case TPM_TIS_REG_INT_VECTOR:
362 val = s->irq_num;
363 break;
364 case TPM_TIS_REG_INT_STATUS:
365 val = s->loc[locty].ints;
366 break;
367 case TPM_TIS_REG_INTF_CAPABILITY:
368 switch (s->be_tpm_version) {
369 case TPM_VERSION_UNSPEC:
370 val = 0;
371 break;
372 case TPM_VERSION_1_2:
373 val = TPM_TIS_CAPABILITIES_SUPPORTED1_3;
374 break;
375 case TPM_VERSION_2_0:
376 val = TPM_TIS_CAPABILITIES_SUPPORTED2_0;
377 break;
379 break;
380 case TPM_TIS_REG_STS:
381 if (s->active_locty == locty) {
382 if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
383 val = TPM_TIS_BURST_COUNT(
384 MIN(tpm_cmd_get_size(&s->buffer),
385 s->be_buffer_size)
386 - s->rw_offset) | s->loc[locty].sts;
387 } else {
388 avail = s->be_buffer_size - s->rw_offset;
390 * byte-sized reads should not return 0x00 for 0x100
391 * available bytes.
393 if (size == 1 && avail > 0xff) {
394 avail = 0xff;
396 val = TPM_TIS_BURST_COUNT(avail) | s->loc[locty].sts;
399 break;
400 case TPM_TIS_REG_DATA_FIFO:
401 case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END:
402 if (s->active_locty == locty) {
403 if (size > 4 - (addr & 0x3)) {
404 /* prevent access beyond FIFO */
405 size = 4 - (addr & 0x3);
407 val = 0;
408 shift = 0;
409 while (size > 0) {
410 switch (s->loc[locty].state) {
411 case TPM_TIS_STATE_COMPLETION:
412 v = tpm_tis_data_read(s, locty);
413 break;
414 default:
415 v = TPM_TIS_NO_DATA_BYTE;
416 break;
418 val |= (v << shift);
419 shift += 8;
420 size--;
422 shift = 0; /* no more adjustments */
424 break;
425 case TPM_TIS_REG_INTERFACE_ID:
426 val = s->loc[locty].iface_id;
427 break;
428 case TPM_TIS_REG_DID_VID:
429 val = (TPM_TIS_TPM_DID << 16) | TPM_TIS_TPM_VID;
430 break;
431 case TPM_TIS_REG_RID:
432 val = TPM_TIS_TPM_RID;
433 break;
434 #ifdef DEBUG_TIS
435 case TPM_TIS_REG_DEBUG:
436 tpm_tis_dump_state(s, addr);
437 break;
438 #endif
441 if (shift) {
442 val >>= shift;
445 trace_tpm_tis_mmio_read(size, addr, val);
447 return val;
451 * Write a value to a register of the TIS interface
452 * See specs pages 33-63 for description of the registers
454 static void tpm_tis_mmio_write(void *opaque, hwaddr addr,
455 uint64_t val, unsigned size)
457 TPMState *s = opaque;
458 uint16_t off = addr & 0xffc;
459 uint8_t shift = (addr & 0x3) * 8;
460 uint8_t locty = tpm_tis_locality_from_addr(addr);
461 uint8_t active_locty, l;
462 int c, set_new_locty = 1;
463 uint16_t len;
464 uint32_t mask = (size == 1) ? 0xff : ((size == 2) ? 0xffff : ~0);
466 trace_tpm_tis_mmio_write(size, addr, val);
468 if (locty == 4) {
469 trace_tpm_tis_mmio_write_locty4();
470 return;
473 if (tpm_backend_had_startup_error(s->be_driver)) {
474 return;
477 val &= mask;
479 if (shift) {
480 val <<= shift;
481 mask <<= shift;
484 mask ^= 0xffffffff;
486 switch (off) {
487 case TPM_TIS_REG_ACCESS:
489 if ((val & TPM_TIS_ACCESS_SEIZE)) {
490 val &= ~(TPM_TIS_ACCESS_REQUEST_USE |
491 TPM_TIS_ACCESS_ACTIVE_LOCALITY);
494 active_locty = s->active_locty;
496 if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) {
497 /* give up locality if currently owned */
498 if (s->active_locty == locty) {
499 trace_tpm_tis_mmio_write_release_locty(locty);
501 uint8_t newlocty = TPM_TIS_NO_LOCALITY;
502 /* anybody wants the locality ? */
503 for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) {
504 if ((s->loc[c].access & TPM_TIS_ACCESS_REQUEST_USE)) {
505 trace_tpm_tis_mmio_write_locty_req_use(c);
506 newlocty = c;
507 break;
510 trace_tpm_tis_mmio_write_next_locty(newlocty);
512 if (TPM_TIS_IS_VALID_LOCTY(newlocty)) {
513 set_new_locty = 0;
514 tpm_tis_prep_abort(s, locty, newlocty);
515 } else {
516 active_locty = TPM_TIS_NO_LOCALITY;
518 } else {
519 /* not currently the owner; clear a pending request */
520 s->loc[locty].access &= ~TPM_TIS_ACCESS_REQUEST_USE;
524 if ((val & TPM_TIS_ACCESS_BEEN_SEIZED)) {
525 s->loc[locty].access &= ~TPM_TIS_ACCESS_BEEN_SEIZED;
528 if ((val & TPM_TIS_ACCESS_SEIZE)) {
530 * allow seize if a locality is active and the requesting
531 * locality is higher than the one that's active
532 * OR
533 * allow seize for requesting locality if no locality is
534 * active
536 while ((TPM_TIS_IS_VALID_LOCTY(s->active_locty) &&
537 locty > s->active_locty) ||
538 !TPM_TIS_IS_VALID_LOCTY(s->active_locty)) {
539 bool higher_seize = FALSE;
541 /* already a pending SEIZE ? */
542 if ((s->loc[locty].access & TPM_TIS_ACCESS_SEIZE)) {
543 break;
546 /* check for ongoing seize by a higher locality */
547 for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES; l++) {
548 if ((s->loc[l].access & TPM_TIS_ACCESS_SEIZE)) {
549 higher_seize = TRUE;
550 break;
554 if (higher_seize) {
555 break;
558 /* cancel any seize by a lower locality */
559 for (l = 0; l < locty; l++) {
560 s->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE;
563 s->loc[locty].access |= TPM_TIS_ACCESS_SEIZE;
565 trace_tpm_tis_mmio_write_locty_seized(locty, s->active_locty);
566 trace_tpm_tis_mmio_write_init_abort();
568 set_new_locty = 0;
569 tpm_tis_prep_abort(s, s->active_locty, locty);
570 break;
574 if ((val & TPM_TIS_ACCESS_REQUEST_USE)) {
575 if (s->active_locty != locty) {
576 if (TPM_TIS_IS_VALID_LOCTY(s->active_locty)) {
577 s->loc[locty].access |= TPM_TIS_ACCESS_REQUEST_USE;
578 } else {
579 /* no locality active -> make this one active now */
580 active_locty = locty;
585 if (set_new_locty) {
586 tpm_tis_new_active_locality(s, active_locty);
589 break;
590 case TPM_TIS_REG_INT_ENABLE:
591 if (s->active_locty != locty) {
592 break;
595 s->loc[locty].inte &= mask;
596 s->loc[locty].inte |= (val & (TPM_TIS_INT_ENABLED |
597 TPM_TIS_INT_POLARITY_MASK |
598 TPM_TIS_INTERRUPTS_SUPPORTED));
599 break;
600 case TPM_TIS_REG_INT_VECTOR:
601 /* hard wired -- ignore */
602 break;
603 case TPM_TIS_REG_INT_STATUS:
604 if (s->active_locty != locty) {
605 break;
608 /* clearing of interrupt flags */
609 if (((val & TPM_TIS_INTERRUPTS_SUPPORTED)) &&
610 (s->loc[locty].ints & TPM_TIS_INTERRUPTS_SUPPORTED)) {
611 s->loc[locty].ints &= ~val;
612 if (s->loc[locty].ints == 0) {
613 qemu_irq_lower(s->irq);
614 trace_tpm_tis_mmio_write_lowering_irq();
617 s->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED);
618 break;
619 case TPM_TIS_REG_STS:
620 if (s->active_locty != locty) {
621 break;
624 if (s->be_tpm_version == TPM_VERSION_2_0) {
625 /* some flags that are only supported for TPM 2 */
626 if (val & TPM_TIS_STS_COMMAND_CANCEL) {
627 if (s->loc[locty].state == TPM_TIS_STATE_EXECUTION) {
629 * request the backend to cancel. Some backends may not
630 * support it
632 tpm_backend_cancel_cmd(s->be_driver);
636 if (val & TPM_TIS_STS_RESET_ESTABLISHMENT_BIT) {
637 if (locty == 3 || locty == 4) {
638 tpm_backend_reset_tpm_established_flag(s->be_driver, locty);
643 val &= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO |
644 TPM_TIS_STS_RESPONSE_RETRY);
646 if (val == TPM_TIS_STS_COMMAND_READY) {
647 switch (s->loc[locty].state) {
649 case TPM_TIS_STATE_READY:
650 s->rw_offset = 0;
651 break;
653 case TPM_TIS_STATE_IDLE:
654 tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_COMMAND_READY);
655 s->loc[locty].state = TPM_TIS_STATE_READY;
656 tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
657 break;
659 case TPM_TIS_STATE_EXECUTION:
660 case TPM_TIS_STATE_RECEPTION:
661 /* abort currently running command */
662 trace_tpm_tis_mmio_write_init_abort();
663 tpm_tis_prep_abort(s, locty, locty);
664 break;
666 case TPM_TIS_STATE_COMPLETION:
667 s->rw_offset = 0;
668 /* shortcut to ready state with C/R set */
669 s->loc[locty].state = TPM_TIS_STATE_READY;
670 if (!(s->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) {
671 tpm_tis_sts_set(&s->loc[locty],
672 TPM_TIS_STS_COMMAND_READY);
673 tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
675 s->loc[locty].sts &= ~(TPM_TIS_STS_DATA_AVAILABLE);
676 break;
679 } else if (val == TPM_TIS_STS_TPM_GO) {
680 switch (s->loc[locty].state) {
681 case TPM_TIS_STATE_RECEPTION:
682 if ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) == 0) {
683 tpm_tis_tpm_send(s, locty);
685 break;
686 default:
687 /* ignore */
688 break;
690 } else if (val == TPM_TIS_STS_RESPONSE_RETRY) {
691 switch (s->loc[locty].state) {
692 case TPM_TIS_STATE_COMPLETION:
693 s->rw_offset = 0;
694 tpm_tis_sts_set(&s->loc[locty],
695 TPM_TIS_STS_VALID|
696 TPM_TIS_STS_DATA_AVAILABLE);
697 break;
698 default:
699 /* ignore */
700 break;
703 break;
704 case TPM_TIS_REG_DATA_FIFO:
705 case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END:
706 /* data fifo */
707 if (s->active_locty != locty) {
708 break;
711 if (s->loc[locty].state == TPM_TIS_STATE_IDLE ||
712 s->loc[locty].state == TPM_TIS_STATE_EXECUTION ||
713 s->loc[locty].state == TPM_TIS_STATE_COMPLETION) {
714 /* drop the byte */
715 } else {
716 trace_tpm_tis_mmio_write_data2send(val, size);
717 if (s->loc[locty].state == TPM_TIS_STATE_READY) {
718 s->loc[locty].state = TPM_TIS_STATE_RECEPTION;
719 tpm_tis_sts_set(&s->loc[locty],
720 TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID);
723 val >>= shift;
724 if (size > 4 - (addr & 0x3)) {
725 /* prevent access beyond FIFO */
726 size = 4 - (addr & 0x3);
729 while ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) && size > 0) {
730 if (s->rw_offset < s->be_buffer_size) {
731 s->buffer[s->rw_offset++] =
732 (uint8_t)val;
733 val >>= 8;
734 size--;
735 } else {
736 tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID);
740 /* check for complete packet */
741 if (s->rw_offset > 5 &&
742 (s->loc[locty].sts & TPM_TIS_STS_EXPECT)) {
743 /* we have a packet length - see if we have all of it */
744 bool need_irq = !(s->loc[locty].sts & TPM_TIS_STS_VALID);
746 len = tpm_cmd_get_size(&s->buffer);
747 if (len > s->rw_offset) {
748 tpm_tis_sts_set(&s->loc[locty],
749 TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID);
750 } else {
751 /* packet complete */
752 tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID);
754 if (need_irq) {
755 tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
759 break;
760 case TPM_TIS_REG_INTERFACE_ID:
761 if (val & TPM_TIS_IFACE_ID_INT_SEL_LOCK) {
762 for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
763 s->loc[l].iface_id |= TPM_TIS_IFACE_ID_INT_SEL_LOCK;
766 break;
770 const MemoryRegionOps tpm_tis_memory_ops = {
771 .read = tpm_tis_mmio_read,
772 .write = tpm_tis_mmio_write,
773 .endianness = DEVICE_LITTLE_ENDIAN,
774 .valid = {
775 .min_access_size = 1,
776 .max_access_size = 4,
781 * Get the TPMVersion of the backend device being used
783 enum TPMVersion tpm_tis_get_tpm_version(TPMState *s)
785 if (tpm_backend_had_startup_error(s->be_driver)) {
786 return TPM_VERSION_UNSPEC;
789 return tpm_backend_get_tpm_version(s->be_driver);
793 * This function is called when the machine starts, resets or due to
794 * S3 resume.
796 void tpm_tis_reset(TPMState *s)
798 int c;
800 s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver);
801 s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->be_driver),
802 TPM_TIS_BUFFER_MAX);
804 if (s->ppi_enabled) {
805 tpm_ppi_reset(&s->ppi);
807 tpm_backend_reset(s->be_driver);
809 s->active_locty = TPM_TIS_NO_LOCALITY;
810 s->next_locty = TPM_TIS_NO_LOCALITY;
811 s->aborting_locty = TPM_TIS_NO_LOCALITY;
813 for (c = 0; c < TPM_TIS_NUM_LOCALITIES; c++) {
814 s->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS;
815 switch (s->be_tpm_version) {
816 case TPM_VERSION_UNSPEC:
817 break;
818 case TPM_VERSION_1_2:
819 s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY1_2;
820 s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3;
821 break;
822 case TPM_VERSION_2_0:
823 s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY2_0;
824 s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0;
825 break;
827 s->loc[c].inte = TPM_TIS_INT_POLARITY_LOW_LEVEL;
828 s->loc[c].ints = 0;
829 s->loc[c].state = TPM_TIS_STATE_IDLE;
831 s->rw_offset = 0;
834 if (tpm_backend_startup_tpm(s->be_driver, s->be_buffer_size) < 0) {
835 exit(1);
839 /* persistent state handling */
841 int tpm_tis_pre_save(TPMState *s)
843 uint8_t locty = s->active_locty;
845 trace_tpm_tis_pre_save(locty, s->rw_offset);
847 if (DEBUG_TIS) {
848 tpm_tis_dump_state(s, 0);
852 * Synchronize with backend completion.
854 tpm_backend_finish_sync(s->be_driver);
856 return 0;
859 const VMStateDescription vmstate_locty = {
860 .name = "tpm-tis/locty",
861 .version_id = 0,
862 .fields = (VMStateField[]) {
863 VMSTATE_UINT32(state, TPMLocality),
864 VMSTATE_UINT32(inte, TPMLocality),
865 VMSTATE_UINT32(ints, TPMLocality),
866 VMSTATE_UINT8(access, TPMLocality),
867 VMSTATE_UINT32(sts, TPMLocality),
868 VMSTATE_UINT32(iface_id, TPMLocality),
869 VMSTATE_END_OF_LIST(),