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