xref: /qemu/hw/tpm/tpm_tis_common.c (revision a976a99a)
1 /*
2  * tpm_tis_common.c - QEMU's TPM TIS interface emulator
3  * device agnostic functions
4  *
5  * Copyright (C) 2006,2010-2013 IBM Corporation
6  *
7  * Authors:
8  *  Stefan Berger <stefanb@us.ibm.com>
9  *  David Safford <safford@us.ibm.com>
10  *
11  * Xen 4 support: Andrease Niederl <andreas.niederl@iaik.tugraz.at>
12  *
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.
15  *
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.
21  *
22  * TPM TIS for TPM 2 implementation following TCG PC Client Platform
23  * TPM Profile (PTP) Specification, Familiy 2.0, Revision 00.43
24  */
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"
30 
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 "sysemu/tpm_util.h"
37 #include "tpm_ppi.h"
38 #include "trace.h"
39 
40 #include "tpm_tis.h"
41 
42 #define DEBUG_TIS 0
43 
44 /* local prototypes */
45 
46 static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr,
47                                   unsigned size);
48 
49 /* utility functions */
50 
51 static uint8_t tpm_tis_locality_from_addr(hwaddr addr)
52 {
53     uint8_t locty;
54 
55     locty = (uint8_t)((addr >> TPM_TIS_LOCALITY_SHIFT) & 0x7);
56     assert(TPM_TIS_IS_VALID_LOCTY(locty));
57 
58     return locty;
59 }
60 
61 
62 /*
63  * Set the given flags in the STS register by clearing the register but
64  * preserving the SELFTEST_DONE and TPM_FAMILY_MASK flags and then setting
65  * the new flags.
66  *
67  * The SELFTEST_DONE flag is acquired from the backend that determines it by
68  * peeking into TPM commands.
69  *
70  * A VM suspend/resume will preserve the flag by storing it into the VM
71  * device state, but the backend will not remember it when QEMU is started
72  * again. Therefore, we cache the flag here. Once set, it will not be unset
73  * except by a reset.
74  */
75 static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags)
76 {
77     l->sts &= TPM_TIS_STS_SELFTEST_DONE | TPM_TIS_STS_TPM_FAMILY_MASK;
78     l->sts |= flags;
79 }
80 
81 /*
82  * Send a request to the TPM.
83  */
84 static void tpm_tis_tpm_send(TPMState *s, uint8_t locty)
85 {
86     tpm_util_show_buffer(s->buffer, s->be_buffer_size, "To TPM");
87 
88     /*
89      * rw_offset serves as length indicator for length of data;
90      * it's reset when the response comes back
91      */
92     s->loc[locty].state = TPM_TIS_STATE_EXECUTION;
93 
94     s->cmd = (TPMBackendCmd) {
95         .locty = locty,
96         .in = s->buffer,
97         .in_len = s->rw_offset,
98         .out = s->buffer,
99         .out_len = s->be_buffer_size,
100     };
101 
102     tpm_backend_deliver_request(s->be_driver, &s->cmd);
103 }
104 
105 /* raise an interrupt if allowed */
106 static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask)
107 {
108     if (!TPM_TIS_IS_VALID_LOCTY(locty)) {
109         return;
110     }
111 
112     if ((s->loc[locty].inte & TPM_TIS_INT_ENABLED) &&
113         (s->loc[locty].inte & irqmask)) {
114         trace_tpm_tis_raise_irq(irqmask);
115         qemu_irq_raise(s->irq);
116         s->loc[locty].ints |= irqmask;
117     }
118 }
119 
120 static uint32_t tpm_tis_check_request_use_except(TPMState *s, uint8_t locty)
121 {
122     uint8_t l;
123 
124     for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
125         if (l == locty) {
126             continue;
127         }
128         if ((s->loc[l].access & TPM_TIS_ACCESS_REQUEST_USE)) {
129             return 1;
130         }
131     }
132 
133     return 0;
134 }
135 
136 static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty)
137 {
138     bool change = (s->active_locty != new_active_locty);
139     bool is_seize;
140     uint8_t mask;
141 
142     if (change && TPM_TIS_IS_VALID_LOCTY(s->active_locty)) {
143         is_seize = TPM_TIS_IS_VALID_LOCTY(new_active_locty) &&
144                    s->loc[new_active_locty].access & TPM_TIS_ACCESS_SEIZE;
145 
146         if (is_seize) {
147             mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY);
148         } else {
149             mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY|
150                      TPM_TIS_ACCESS_REQUEST_USE);
151         }
152         /* reset flags on the old active locality */
153         s->loc[s->active_locty].access &= mask;
154 
155         if (is_seize) {
156             s->loc[s->active_locty].access |= TPM_TIS_ACCESS_BEEN_SEIZED;
157         }
158     }
159 
160     s->active_locty = new_active_locty;
161 
162     trace_tpm_tis_new_active_locality(s->active_locty);
163 
164     if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) {
165         /* set flags on the new active locality */
166         s->loc[new_active_locty].access |= TPM_TIS_ACCESS_ACTIVE_LOCALITY;
167         s->loc[new_active_locty].access &= ~(TPM_TIS_ACCESS_REQUEST_USE |
168                                                TPM_TIS_ACCESS_SEIZE);
169     }
170 
171     if (change) {
172         tpm_tis_raise_irq(s, s->active_locty, TPM_TIS_INT_LOCALITY_CHANGED);
173     }
174 }
175 
176 /* abort -- this function switches the locality */
177 static void tpm_tis_abort(TPMState *s)
178 {
179     s->rw_offset = 0;
180 
181     trace_tpm_tis_abort(s->next_locty);
182 
183     /*
184      * Need to react differently depending on who's aborting now and
185      * which locality will become active afterwards.
186      */
187     if (s->aborting_locty == s->next_locty) {
188         s->loc[s->aborting_locty].state = TPM_TIS_STATE_READY;
189         tpm_tis_sts_set(&s->loc[s->aborting_locty],
190                         TPM_TIS_STS_COMMAND_READY);
191         tpm_tis_raise_irq(s, s->aborting_locty, TPM_TIS_INT_COMMAND_READY);
192     }
193 
194     /* locality after abort is another one than the current one */
195     tpm_tis_new_active_locality(s, s->next_locty);
196 
197     s->next_locty = TPM_TIS_NO_LOCALITY;
198     /* nobody's aborting a command anymore */
199     s->aborting_locty = TPM_TIS_NO_LOCALITY;
200 }
201 
202 /* prepare aborting current command */
203 static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty)
204 {
205     uint8_t busy_locty;
206 
207     assert(TPM_TIS_IS_VALID_LOCTY(newlocty));
208 
209     s->aborting_locty = locty; /* may also be TPM_TIS_NO_LOCALITY */
210     s->next_locty = newlocty;  /* locality after successful abort */
211 
212     /*
213      * only abort a command using an interrupt if currently executing
214      * a command AND if there's a valid connection to the vTPM.
215      */
216     for (busy_locty = 0; busy_locty < TPM_TIS_NUM_LOCALITIES; busy_locty++) {
217         if (s->loc[busy_locty].state == TPM_TIS_STATE_EXECUTION) {
218             /*
219              * request the backend to cancel. Some backends may not
220              * support it
221              */
222             tpm_backend_cancel_cmd(s->be_driver);
223             return;
224         }
225     }
226 
227     tpm_tis_abort(s);
228 }
229 
230 /*
231  * Callback from the TPM to indicate that the response was received.
232  */
233 void tpm_tis_request_completed(TPMState *s, int ret)
234 {
235     uint8_t locty = s->cmd.locty;
236     uint8_t l;
237 
238     assert(TPM_TIS_IS_VALID_LOCTY(locty));
239 
240     if (s->cmd.selftest_done) {
241         for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
242             s->loc[l].sts |= TPM_TIS_STS_SELFTEST_DONE;
243         }
244     }
245 
246     /* FIXME: report error if ret != 0 */
247     tpm_tis_sts_set(&s->loc[locty],
248                     TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE);
249     s->loc[locty].state = TPM_TIS_STATE_COMPLETION;
250     s->rw_offset = 0;
251 
252     tpm_util_show_buffer(s->buffer, s->be_buffer_size, "From TPM");
253 
254     if (TPM_TIS_IS_VALID_LOCTY(s->next_locty)) {
255         tpm_tis_abort(s);
256     }
257 
258     tpm_tis_raise_irq(s, locty,
259                       TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID);
260 }
261 
262 /*
263  * Read a byte of response data
264  */
265 static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty)
266 {
267     uint32_t ret = TPM_TIS_NO_DATA_BYTE;
268     uint16_t len;
269 
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);
273 
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);
279         }
280         trace_tpm_tis_data_read(ret, s->rw_offset - 1);
281     }
282 
283     return ret;
284 }
285 
286 #ifdef DEBUG_TIS
287 static void tpm_tis_dump_state(TPMState *s, hwaddr addr)
288 {
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;
302 
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);
308 
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));
312     }
313 
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:                 " : "");
324     }
325     printf("\n");
326 }
327 #endif
328 
329 /*
330  * Read a register of the TIS interface
331  * See specs pages 33-63 for description of the registers
332  */
333 static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr,
334                                   unsigned size)
335 {
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;
343 
344     if (tpm_backend_had_startup_error(s->be_driver)) {
345         return 0;
346     }
347 
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;
355         }
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;
378         }
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;
389                 /*
390                  * byte-sized reads should not return 0x00 for 0x100
391                  * available bytes.
392                  */
393                 if (size == 1 && avail > 0xff) {
394                     avail = 0xff;
395                 }
396                 val = TPM_TIS_BURST_COUNT(avail) | s->loc[locty].sts;
397             }
398         }
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);
406             }
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;
417                 }
418                 val |= (v << shift);
419                 shift += 8;
420                 size--;
421             }
422             shift = 0; /* no more adjustments */
423         }
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
439     }
440 
441     if (shift) {
442         val >>= shift;
443     }
444 
445     trace_tpm_tis_mmio_read(size, addr, val);
446 
447     return val;
448 }
449 
450 /*
451  * Write a value to a register of the TIS interface
452  * See specs pages 33-63 for description of the registers
453  */
454 static void tpm_tis_mmio_write(void *opaque, hwaddr addr,
455                                uint64_t val, unsigned size)
456 {
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);
465 
466     trace_tpm_tis_mmio_write(size, addr, val);
467 
468     if (locty == 4) {
469         trace_tpm_tis_mmio_write_locty4();
470         return;
471     }
472 
473     if (tpm_backend_had_startup_error(s->be_driver)) {
474         return;
475     }
476 
477     val &= mask;
478 
479     if (shift) {
480         val <<= shift;
481         mask <<= shift;
482     }
483 
484     mask ^= 0xffffffff;
485 
486     switch (off) {
487     case TPM_TIS_REG_ACCESS:
488 
489         if ((val & TPM_TIS_ACCESS_SEIZE)) {
490             val &= ~(TPM_TIS_ACCESS_REQUEST_USE |
491                      TPM_TIS_ACCESS_ACTIVE_LOCALITY);
492         }
493 
494         active_locty = s->active_locty;
495 
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);
500 
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;
508                     }
509                 }
510                 trace_tpm_tis_mmio_write_next_locty(newlocty);
511 
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;
517                 }
518             } else {
519                 /* not currently the owner; clear a pending request */
520                 s->loc[locty].access &= ~TPM_TIS_ACCESS_REQUEST_USE;
521             }
522         }
523 
524         if ((val & TPM_TIS_ACCESS_BEEN_SEIZED)) {
525             s->loc[locty].access &= ~TPM_TIS_ACCESS_BEEN_SEIZED;
526         }
527 
528         if ((val & TPM_TIS_ACCESS_SEIZE)) {
529             /*
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
535              */
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;
540 
541                 /* already a pending SEIZE ? */
542                 if ((s->loc[locty].access & TPM_TIS_ACCESS_SEIZE)) {
543                     break;
544                 }
545 
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;
551                     }
552                 }
553 
554                 if (higher_seize) {
555                     break;
556                 }
557 
558                 /* cancel any seize by a lower locality */
559                 for (l = 0; l < locty; l++) {
560                     s->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE;
561                 }
562 
563                 s->loc[locty].access |= TPM_TIS_ACCESS_SEIZE;
564 
565                 trace_tpm_tis_mmio_write_locty_seized(locty, s->active_locty);
566                 trace_tpm_tis_mmio_write_init_abort();
567 
568                 set_new_locty = 0;
569                 tpm_tis_prep_abort(s, s->active_locty, locty);
570                 break;
571             }
572         }
573 
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;
581                 }
582             }
583         }
584 
585         if (set_new_locty) {
586             tpm_tis_new_active_locality(s, active_locty);
587         }
588 
589         break;
590     case TPM_TIS_REG_INT_ENABLE:
591         if (s->active_locty != locty) {
592             break;
593         }
594 
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;
606         }
607 
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();
615             }
616         }
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;
622         }
623 
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) {
628                     /*
629                      * request the backend to cancel. Some backends may not
630                      * support it
631                      */
632                     tpm_backend_cancel_cmd(s->be_driver);
633                 }
634             }
635 
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);
639                 }
640             }
641         }
642 
643         val &= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO |
644                 TPM_TIS_STS_RESPONSE_RETRY);
645 
646         if (val == TPM_TIS_STS_COMMAND_READY) {
647             switch (s->loc[locty].state) {
648 
649             case TPM_TIS_STATE_READY:
650                 s->rw_offset = 0;
651             break;
652 
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;
658 
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;
665 
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);
674                 }
675                 s->loc[locty].sts &= ~(TPM_TIS_STS_DATA_AVAILABLE);
676             break;
677 
678             }
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);
684                 }
685                 break;
686             default:
687                 /* ignore */
688                 break;
689             }
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;
701             }
702         }
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;
709         }
710 
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);
721             }
722 
723             val >>= shift;
724             if (size > 4 - (addr & 0x3)) {
725                 /* prevent access beyond FIFO */
726                 size = 4 - (addr & 0x3);
727             }
728 
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);
737                 }
738             }
739 
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);
745 
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);
753                 }
754                 if (need_irq) {
755                     tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
756                 }
757             }
758         }
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;
764             }
765         }
766         break;
767     }
768 }
769 
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,
777     },
778 };
779 
780 /*
781  * Get the TPMVersion of the backend device being used
782  */
783 enum TPMVersion tpm_tis_get_tpm_version(TPMState *s)
784 {
785     if (tpm_backend_had_startup_error(s->be_driver)) {
786         return TPM_VERSION_UNSPEC;
787     }
788 
789     return tpm_backend_get_tpm_version(s->be_driver);
790 }
791 
792 /*
793  * This function is called when the machine starts, resets or due to
794  * S3 resume.
795  */
796 void tpm_tis_reset(TPMState *s)
797 {
798     int c;
799 
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);
803 
804     if (s->ppi_enabled) {
805         tpm_ppi_reset(&s->ppi);
806     }
807     tpm_backend_reset(s->be_driver);
808 
809     s->active_locty = TPM_TIS_NO_LOCALITY;
810     s->next_locty = TPM_TIS_NO_LOCALITY;
811     s->aborting_locty = TPM_TIS_NO_LOCALITY;
812 
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;
826         }
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;
830 
831         s->rw_offset = 0;
832     }
833 
834     if (tpm_backend_startup_tpm(s->be_driver, s->be_buffer_size) < 0) {
835         exit(1);
836     }
837 }
838 
839 /* persistent state handling */
840 
841 int tpm_tis_pre_save(TPMState *s)
842 {
843     uint8_t locty = s->active_locty;
844 
845     trace_tpm_tis_pre_save(locty, s->rw_offset);
846 
847     if (DEBUG_TIS) {
848         tpm_tis_dump_state(s, 0);
849     }
850 
851     /*
852      * Synchronize with backend completion.
853      */
854     tpm_backend_finish_sync(s->be_driver);
855 
856     return 0;
857 }
858 
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(),
870     }
871 };
872 
873