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