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