xref: /qemu/hw/tpm/tpm_tis_common.c (revision b6148757f9baa2de11304c09aaf9f9a276c825cd)
1edff8678SStefan Berger /*
2edff8678SStefan Berger  * tpm_tis.c - QEMU's TPM TIS interface emulator
3edff8678SStefan Berger  *
4edff8678SStefan Berger  * Copyright (C) 2006,2010-2013 IBM Corporation
5edff8678SStefan Berger  *
6edff8678SStefan Berger  * Authors:
7edff8678SStefan Berger  *  Stefan Berger <stefanb@us.ibm.com>
8edff8678SStefan Berger  *  David Safford <safford@us.ibm.com>
9edff8678SStefan Berger  *
10edff8678SStefan Berger  * Xen 4 support: Andrease Niederl <andreas.niederl@iaik.tugraz.at>
11edff8678SStefan Berger  *
12edff8678SStefan Berger  * This work is licensed under the terms of the GNU GPL, version 2 or later.
13edff8678SStefan Berger  * See the COPYING file in the top-level directory.
14edff8678SStefan Berger  *
15edff8678SStefan Berger  * Implementation of the TIS interface according to specs found at
16edff8678SStefan Berger  * http://www.trustedcomputinggroup.org. This implementation currently
179dd5c40dSStefan Berger  * supports version 1.3, 21 March 2013
18edff8678SStefan Berger  * In the developers menu choose the PC Client section then find the TIS
19edff8678SStefan Berger  * specification.
20116694c3SStefan Berger  *
21116694c3SStefan Berger  * TPM TIS for TPM 2 implementation following TCG PC Client Platform
22116694c3SStefan Berger  * TPM Profile (PTP) Specification, Familiy 2.0, Revision 00.43
23edff8678SStefan Berger  */
24edff8678SStefan Berger 
250430891cSPeter Maydell #include "qemu/osdep.h"
26732cd587SMarc-André Lureau #include "hw/isa/isa.h"
27023299d8SMarc-André Lureau #include "qapi/error.h"
28023299d8SMarc-André Lureau 
29023299d8SMarc-André Lureau #include "hw/acpi/tpm.h"
30023299d8SMarc-André Lureau #include "hw/pci/pci_ids.h"
31dccfcd0eSPaolo Bonzini #include "sysemu/tpm_backend.h"
32edff8678SStefan Berger #include "tpm_int.h"
335cf954d0SMarc-André Lureau #include "tpm_util.h"
34fcbed221SStefan Berger #include "trace.h"
35732cd587SMarc-André Lureau 
36732cd587SMarc-André Lureau #define TPM_TIS_NUM_LOCALITIES      5     /* per spec */
37732cd587SMarc-André Lureau #define TPM_TIS_LOCALITY_SHIFT      12
38732cd587SMarc-André Lureau #define TPM_TIS_NO_LOCALITY         0xff
39732cd587SMarc-André Lureau 
40732cd587SMarc-André Lureau #define TPM_TIS_IS_VALID_LOCTY(x)   ((x) < TPM_TIS_NUM_LOCALITIES)
41732cd587SMarc-André Lureau 
42732cd587SMarc-André Lureau #define TPM_TIS_BUFFER_MAX          4096
43732cd587SMarc-André Lureau 
44732cd587SMarc-André Lureau typedef enum {
45732cd587SMarc-André Lureau     TPM_TIS_STATE_IDLE = 0,
46732cd587SMarc-André Lureau     TPM_TIS_STATE_READY,
47732cd587SMarc-André Lureau     TPM_TIS_STATE_COMPLETION,
48732cd587SMarc-André Lureau     TPM_TIS_STATE_EXECUTION,
49732cd587SMarc-André Lureau     TPM_TIS_STATE_RECEPTION,
50732cd587SMarc-André Lureau } TPMTISState;
51732cd587SMarc-André Lureau 
52732cd587SMarc-André Lureau /* locality data  -- all fields are persisted */
53732cd587SMarc-André Lureau typedef struct TPMLocality {
54732cd587SMarc-André Lureau     TPMTISState state;
55732cd587SMarc-André Lureau     uint8_t access;
56732cd587SMarc-André Lureau     uint32_t sts;
57732cd587SMarc-André Lureau     uint32_t iface_id;
58732cd587SMarc-André Lureau     uint32_t inte;
59732cd587SMarc-André Lureau     uint32_t ints;
60732cd587SMarc-André Lureau } TPMLocality;
61732cd587SMarc-André Lureau 
6236e86589SMarc-André Lureau typedef struct TPMState {
633d4960c7SMarc-André Lureau     ISADevice busdev;
643d4960c7SMarc-André Lureau     MemoryRegion mmio;
653d4960c7SMarc-André Lureau 
66c5496b97SStefan Berger     unsigned char buffer[TPM_TIS_BUFFER_MAX];
67f999d81bSStefan Berger     uint16_t rw_offset;
68732cd587SMarc-André Lureau 
69732cd587SMarc-André Lureau     uint8_t active_locty;
70732cd587SMarc-André Lureau     uint8_t aborting_locty;
71732cd587SMarc-André Lureau     uint8_t next_locty;
72732cd587SMarc-André Lureau 
73732cd587SMarc-André Lureau     TPMLocality loc[TPM_TIS_NUM_LOCALITIES];
74732cd587SMarc-André Lureau 
75732cd587SMarc-André Lureau     qemu_irq irq;
76732cd587SMarc-André Lureau     uint32_t irq_num;
77732cd587SMarc-André Lureau 
78732cd587SMarc-André Lureau     TPMBackendCmd cmd;
79732cd587SMarc-André Lureau 
80732cd587SMarc-André Lureau     TPMBackend *be_driver;
81732cd587SMarc-André Lureau     TPMVersion be_tpm_version;
82b21e6aafSStefan Berger 
83b21e6aafSStefan Berger     size_t be_buffer_size;
84*b6148757SMarc-André Lureau 
85*b6148757SMarc-André Lureau     bool ppi_enabled;
8636e86589SMarc-André Lureau } TPMState;
87732cd587SMarc-André Lureau 
88732cd587SMarc-André Lureau #define TPM(obj) OBJECT_CHECK(TPMState, (obj), TYPE_TPM_TIS)
89edff8678SStefan Berger 
904d1ba9c4SStefan Berger #define DEBUG_TIS 0
91edff8678SStefan Berger 
928db7c415SStefan Berger /* local prototypes */
938db7c415SStefan Berger 
948db7c415SStefan Berger static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr,
958db7c415SStefan Berger                                   unsigned size);
968db7c415SStefan Berger 
97edff8678SStefan Berger /* utility functions */
98edff8678SStefan Berger 
99edff8678SStefan Berger static uint8_t tpm_tis_locality_from_addr(hwaddr addr)
100edff8678SStefan Berger {
101edff8678SStefan Berger     return (uint8_t)((addr >> TPM_TIS_LOCALITY_SHIFT) & 0x7);
102edff8678SStefan Berger }
103edff8678SStefan Berger 
104e6b703f6SStefan Berger static void tpm_tis_show_buffer(const unsigned char *buffer,
105e6b703f6SStefan Berger                                 size_t buffer_size, const char *string)
106edff8678SStefan Berger {
107edff8678SStefan Berger     uint32_t len, i;
108edff8678SStefan Berger 
109e6b703f6SStefan Berger     len = MIN(tpm_cmd_get_size(buffer), buffer_size);
110fcbed221SStefan Berger     printf("tpm_tis: %s length = %d\n", string, len);
111edff8678SStefan Berger     for (i = 0; i < len; i++) {
112edff8678SStefan Berger         if (i && !(i % 16)) {
113fcbed221SStefan Berger             printf("\n");
114edff8678SStefan Berger         }
115fcbed221SStefan Berger         printf("%.2X ", buffer[i]);
116edff8678SStefan Berger     }
117fcbed221SStefan Berger     printf("\n");
118edff8678SStefan Berger }
119edff8678SStefan Berger 
120edff8678SStefan Berger /*
121fd859081SStefan Berger  * Set the given flags in the STS register by clearing the register but
122116694c3SStefan Berger  * preserving the SELFTEST_DONE and TPM_FAMILY_MASK flags and then setting
123116694c3SStefan Berger  * the new flags.
124fd859081SStefan Berger  *
125fd859081SStefan Berger  * The SELFTEST_DONE flag is acquired from the backend that determines it by
126fd859081SStefan Berger  * peeking into TPM commands.
127fd859081SStefan Berger  *
128fd859081SStefan Berger  * A VM suspend/resume will preserve the flag by storing it into the VM
129fd859081SStefan Berger  * device state, but the backend will not remember it when QEMU is started
130fd859081SStefan Berger  * again. Therefore, we cache the flag here. Once set, it will not be unset
131fd859081SStefan Berger  * except by a reset.
132fd859081SStefan Berger  */
133fd859081SStefan Berger static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags)
134fd859081SStefan Berger {
135116694c3SStefan Berger     l->sts &= TPM_TIS_STS_SELFTEST_DONE | TPM_TIS_STS_TPM_FAMILY_MASK;
136fd859081SStefan Berger     l->sts |= flags;
137fd859081SStefan Berger }
138fd859081SStefan Berger 
139fd859081SStefan Berger /*
140edff8678SStefan Berger  * Send a request to the TPM.
141edff8678SStefan Berger  */
142edff8678SStefan Berger static void tpm_tis_tpm_send(TPMState *s, uint8_t locty)
143edff8678SStefan Berger {
144fcbed221SStefan Berger     if (DEBUG_TIS) {
145c5496b97SStefan Berger         tpm_tis_show_buffer(s->buffer, s->be_buffer_size,
146e6b703f6SStefan Berger                             "tpm_tis: To TPM");
147fcbed221SStefan Berger     }
148edff8678SStefan Berger 
149edff8678SStefan Berger     /*
150f999d81bSStefan Berger      * rw_offset serves as length indicator for length of data;
151edff8678SStefan Berger      * it's reset when the response comes back
152edff8678SStefan Berger      */
1533d4960c7SMarc-André Lureau     s->loc[locty].state = TPM_TIS_STATE_EXECUTION;
154edff8678SStefan Berger 
1550e43b7e6SMarc-André Lureau     s->cmd = (TPMBackendCmd) {
1560e43b7e6SMarc-André Lureau         .locty = locty,
157c5496b97SStefan Berger         .in = s->buffer,
158f999d81bSStefan Berger         .in_len = s->rw_offset,
159c5496b97SStefan Berger         .out = s->buffer,
160e6b703f6SStefan Berger         .out_len = s->be_buffer_size,
1610e43b7e6SMarc-André Lureau     };
1620e43b7e6SMarc-André Lureau 
1630e43b7e6SMarc-André Lureau     tpm_backend_deliver_request(s->be_driver, &s->cmd);
164edff8678SStefan Berger }
165edff8678SStefan Berger 
166edff8678SStefan Berger /* raise an interrupt if allowed */
167edff8678SStefan Berger static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask)
168edff8678SStefan Berger {
169edff8678SStefan Berger     if (!TPM_TIS_IS_VALID_LOCTY(locty)) {
170edff8678SStefan Berger         return;
171edff8678SStefan Berger     }
172edff8678SStefan Berger 
1733d4960c7SMarc-André Lureau     if ((s->loc[locty].inte & TPM_TIS_INT_ENABLED) &&
1743d4960c7SMarc-André Lureau         (s->loc[locty].inte & irqmask)) {
175fcbed221SStefan Berger         trace_tpm_tis_raise_irq(irqmask);
1763d4960c7SMarc-André Lureau         qemu_irq_raise(s->irq);
1773d4960c7SMarc-André Lureau         s->loc[locty].ints |= irqmask;
178edff8678SStefan Berger     }
179edff8678SStefan Berger }
180edff8678SStefan Berger 
181edff8678SStefan Berger static uint32_t tpm_tis_check_request_use_except(TPMState *s, uint8_t locty)
182edff8678SStefan Berger {
183edff8678SStefan Berger     uint8_t l;
184edff8678SStefan Berger 
185edff8678SStefan Berger     for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
186edff8678SStefan Berger         if (l == locty) {
187edff8678SStefan Berger             continue;
188edff8678SStefan Berger         }
1893d4960c7SMarc-André Lureau         if ((s->loc[l].access & TPM_TIS_ACCESS_REQUEST_USE)) {
190edff8678SStefan Berger             return 1;
191edff8678SStefan Berger         }
192edff8678SStefan Berger     }
193edff8678SStefan Berger 
194edff8678SStefan Berger     return 0;
195edff8678SStefan Berger }
196edff8678SStefan Berger 
197edff8678SStefan Berger static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty)
198edff8678SStefan Berger {
1993d4960c7SMarc-André Lureau     bool change = (s->active_locty != new_active_locty);
200edff8678SStefan Berger     bool is_seize;
201edff8678SStefan Berger     uint8_t mask;
202edff8678SStefan Berger 
2033d4960c7SMarc-André Lureau     if (change && TPM_TIS_IS_VALID_LOCTY(s->active_locty)) {
204edff8678SStefan Berger         is_seize = TPM_TIS_IS_VALID_LOCTY(new_active_locty) &&
2053d4960c7SMarc-André Lureau                    s->loc[new_active_locty].access & TPM_TIS_ACCESS_SEIZE;
206edff8678SStefan Berger 
207edff8678SStefan Berger         if (is_seize) {
208edff8678SStefan Berger             mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY);
209edff8678SStefan Berger         } else {
210edff8678SStefan Berger             mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY|
211edff8678SStefan Berger                      TPM_TIS_ACCESS_REQUEST_USE);
212edff8678SStefan Berger         }
213edff8678SStefan Berger         /* reset flags on the old active locality */
2143d4960c7SMarc-André Lureau         s->loc[s->active_locty].access &= mask;
215edff8678SStefan Berger 
216edff8678SStefan Berger         if (is_seize) {
2173d4960c7SMarc-André Lureau             s->loc[s->active_locty].access |= TPM_TIS_ACCESS_BEEN_SEIZED;
218edff8678SStefan Berger         }
219edff8678SStefan Berger     }
220edff8678SStefan Berger 
2213d4960c7SMarc-André Lureau     s->active_locty = new_active_locty;
222edff8678SStefan Berger 
223fcbed221SStefan Berger     trace_tpm_tis_new_active_locality(s->active_locty);
224edff8678SStefan Berger 
225edff8678SStefan Berger     if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) {
226edff8678SStefan Berger         /* set flags on the new active locality */
2273d4960c7SMarc-André Lureau         s->loc[new_active_locty].access |= TPM_TIS_ACCESS_ACTIVE_LOCALITY;
2283d4960c7SMarc-André Lureau         s->loc[new_active_locty].access &= ~(TPM_TIS_ACCESS_REQUEST_USE |
229edff8678SStefan Berger                                                TPM_TIS_ACCESS_SEIZE);
230edff8678SStefan Berger     }
231edff8678SStefan Berger 
232edff8678SStefan Berger     if (change) {
2333d4960c7SMarc-André Lureau         tpm_tis_raise_irq(s, s->active_locty, TPM_TIS_INT_LOCALITY_CHANGED);
234edff8678SStefan Berger     }
235edff8678SStefan Berger }
236edff8678SStefan Berger 
237edff8678SStefan Berger /* abort -- this function switches the locality */
2380f5faee3SStefan Berger static void tpm_tis_abort(TPMState *s)
239edff8678SStefan Berger {
240f999d81bSStefan Berger     s->rw_offset = 0;
241edff8678SStefan Berger 
242fcbed221SStefan Berger     trace_tpm_tis_abort(s->next_locty);
243edff8678SStefan Berger 
244edff8678SStefan Berger     /*
245edff8678SStefan Berger      * Need to react differently depending on who's aborting now and
246edff8678SStefan Berger      * which locality will become active afterwards.
247edff8678SStefan Berger      */
2483d4960c7SMarc-André Lureau     if (s->aborting_locty == s->next_locty) {
2493d4960c7SMarc-André Lureau         s->loc[s->aborting_locty].state = TPM_TIS_STATE_READY;
2503d4960c7SMarc-André Lureau         tpm_tis_sts_set(&s->loc[s->aborting_locty],
251fd859081SStefan Berger                         TPM_TIS_STS_COMMAND_READY);
2523d4960c7SMarc-André Lureau         tpm_tis_raise_irq(s, s->aborting_locty, TPM_TIS_INT_COMMAND_READY);
253edff8678SStefan Berger     }
254edff8678SStefan Berger 
255edff8678SStefan Berger     /* locality after abort is another one than the current one */
2563d4960c7SMarc-André Lureau     tpm_tis_new_active_locality(s, s->next_locty);
257edff8678SStefan Berger 
2583d4960c7SMarc-André Lureau     s->next_locty = TPM_TIS_NO_LOCALITY;
259edff8678SStefan Berger     /* nobody's aborting a command anymore */
2603d4960c7SMarc-André Lureau     s->aborting_locty = TPM_TIS_NO_LOCALITY;
261edff8678SStefan Berger }
262edff8678SStefan Berger 
263edff8678SStefan Berger /* prepare aborting current command */
264edff8678SStefan Berger static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty)
265edff8678SStefan Berger {
266edff8678SStefan Berger     uint8_t busy_locty;
267edff8678SStefan Berger 
268e92b63eaSStefan Berger     assert(TPM_TIS_IS_VALID_LOCTY(newlocty));
269e92b63eaSStefan Berger 
270e92b63eaSStefan Berger     s->aborting_locty = locty; /* may also be TPM_TIS_NO_LOCALITY */
2713d4960c7SMarc-André Lureau     s->next_locty = newlocty;  /* locality after successful abort */
272edff8678SStefan Berger 
273edff8678SStefan Berger     /*
274edff8678SStefan Berger      * only abort a command using an interrupt if currently executing
275edff8678SStefan Berger      * a command AND if there's a valid connection to the vTPM.
276edff8678SStefan Berger      */
277edff8678SStefan Berger     for (busy_locty = 0; busy_locty < TPM_TIS_NUM_LOCALITIES; busy_locty++) {
2783d4960c7SMarc-André Lureau         if (s->loc[busy_locty].state == TPM_TIS_STATE_EXECUTION) {
279edff8678SStefan Berger             /*
280edff8678SStefan Berger              * request the backend to cancel. Some backends may not
281edff8678SStefan Berger              * support it
282edff8678SStefan Berger              */
2838f0605ccSStefan Berger             tpm_backend_cancel_cmd(s->be_driver);
284edff8678SStefan Berger             return;
285edff8678SStefan Berger         }
286edff8678SStefan Berger     }
287edff8678SStefan Berger 
2880f5faee3SStefan Berger     tpm_tis_abort(s);
289edff8678SStefan Berger }
290edff8678SStefan Berger 
29168999059SMarc-André Lureau /*
29268999059SMarc-André Lureau  * Callback from the TPM to indicate that the response was received.
29368999059SMarc-André Lureau  */
2946a8a2354SMarc-André Lureau static void tpm_tis_request_completed(TPMIf *ti, int ret)
295edff8678SStefan Berger {
29668999059SMarc-André Lureau     TPMState *s = TPM(ti);
2970e43b7e6SMarc-André Lureau     uint8_t locty = s->cmd.locty;
29868999059SMarc-André Lureau     uint8_t l;
29968999059SMarc-André Lureau 
300a639f961SStefan Berger     assert(TPM_TIS_IS_VALID_LOCTY(locty));
301a639f961SStefan Berger 
30268999059SMarc-André Lureau     if (s->cmd.selftest_done) {
30368999059SMarc-André Lureau         for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
3046a50bb98SPrasad J Pandit             s->loc[l].sts |= TPM_TIS_STS_SELFTEST_DONE;
30568999059SMarc-André Lureau         }
30668999059SMarc-André Lureau     }
307edff8678SStefan Berger 
3086a8a2354SMarc-André Lureau     /* FIXME: report error if ret != 0 */
3093d4960c7SMarc-André Lureau     tpm_tis_sts_set(&s->loc[locty],
310fd859081SStefan Berger                     TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE);
3113d4960c7SMarc-André Lureau     s->loc[locty].state = TPM_TIS_STATE_COMPLETION;
312f999d81bSStefan Berger     s->rw_offset = 0;
313edff8678SStefan Berger 
314fcbed221SStefan Berger     if (DEBUG_TIS) {
315c5496b97SStefan Berger         tpm_tis_show_buffer(s->buffer, s->be_buffer_size,
316e6b703f6SStefan Berger                             "tpm_tis: From TPM");
317fcbed221SStefan Berger     }
318298d8b81SStefan Berger 
3193d4960c7SMarc-André Lureau     if (TPM_TIS_IS_VALID_LOCTY(s->next_locty)) {
3200f5faee3SStefan Berger         tpm_tis_abort(s);
321edff8678SStefan Berger     }
322edff8678SStefan Berger 
323edff8678SStefan Berger     tpm_tis_raise_irq(s, locty,
324edff8678SStefan Berger                       TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID);
325edff8678SStefan Berger }
326edff8678SStefan Berger 
327edff8678SStefan Berger /*
328edff8678SStefan Berger  * Read a byte of response data
329edff8678SStefan Berger  */
330edff8678SStefan Berger static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty)
331edff8678SStefan Berger {
332edff8678SStefan Berger     uint32_t ret = TPM_TIS_NO_DATA_BYTE;
333edff8678SStefan Berger     uint16_t len;
334edff8678SStefan Berger 
3353d4960c7SMarc-André Lureau     if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
336c5496b97SStefan Berger         len = MIN(tpm_cmd_get_size(&s->buffer),
337e6b703f6SStefan Berger                   s->be_buffer_size);
338edff8678SStefan Berger 
339f999d81bSStefan Berger         ret = s->buffer[s->rw_offset++];
340f999d81bSStefan Berger         if (s->rw_offset >= len) {
341edff8678SStefan Berger             /* got last byte */
3423d4960c7SMarc-André Lureau             tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID);
343edff8678SStefan Berger             tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
344edff8678SStefan Berger         }
345fcbed221SStefan Berger         trace_tpm_tis_data_read(ret, s->rw_offset - 1);
346edff8678SStefan Berger     }
347edff8678SStefan Berger 
348edff8678SStefan Berger     return ret;
349edff8678SStefan Berger }
350edff8678SStefan Berger 
3518db7c415SStefan Berger #ifdef DEBUG_TIS
3528db7c415SStefan Berger static void tpm_tis_dump_state(void *opaque, hwaddr addr)
3538db7c415SStefan Berger {
3548db7c415SStefan Berger     static const unsigned regs[] = {
3558db7c415SStefan Berger         TPM_TIS_REG_ACCESS,
3568db7c415SStefan Berger         TPM_TIS_REG_INT_ENABLE,
3578db7c415SStefan Berger         TPM_TIS_REG_INT_VECTOR,
3588db7c415SStefan Berger         TPM_TIS_REG_INT_STATUS,
3598db7c415SStefan Berger         TPM_TIS_REG_INTF_CAPABILITY,
3608db7c415SStefan Berger         TPM_TIS_REG_STS,
3618db7c415SStefan Berger         TPM_TIS_REG_DID_VID,
3628db7c415SStefan Berger         TPM_TIS_REG_RID,
3638db7c415SStefan Berger         0xfff};
3648db7c415SStefan Berger     int idx;
3658db7c415SStefan Berger     uint8_t locty = tpm_tis_locality_from_addr(addr);
3668db7c415SStefan Berger     hwaddr base = addr & ~0xfff;
3678db7c415SStefan Berger     TPMState *s = opaque;
3688db7c415SStefan Berger 
369fcbed221SStefan Berger     printf("tpm_tis: active locality      : %d\n"
3708db7c415SStefan Berger            "tpm_tis: state of locality %d : %d\n"
3718db7c415SStefan Berger            "tpm_tis: register dump:\n",
3723d4960c7SMarc-André Lureau            s->active_locty,
3733d4960c7SMarc-André Lureau            locty, s->loc[locty].state);
3748db7c415SStefan Berger 
3758db7c415SStefan Berger     for (idx = 0; regs[idx] != 0xfff; idx++) {
376fcbed221SStefan Berger         printf("tpm_tis: 0x%04x : 0x%08x\n", regs[idx],
377070c7607SStefan Berger                (int)tpm_tis_mmio_read(opaque, base + regs[idx], 4));
3788db7c415SStefan Berger     }
3798db7c415SStefan Berger 
380fcbed221SStefan Berger     printf("tpm_tis: r/w offset    : %d\n"
3818db7c415SStefan Berger            "tpm_tis: result buffer : ",
382f999d81bSStefan Berger            s->rw_offset);
3838db7c415SStefan Berger     for (idx = 0;
384c5496b97SStefan Berger          idx < MIN(tpm_cmd_get_size(&s->buffer), s->be_buffer_size);
3858db7c415SStefan Berger          idx++) {
386fcbed221SStefan Berger         printf("%c%02x%s",
387f999d81bSStefan Berger                s->rw_offset == idx ? '>' : ' ',
388c5496b97SStefan Berger                s->buffer[idx],
3898db7c415SStefan Berger                ((idx & 0xf) == 0xf) ? "\ntpm_tis:                 " : "");
3908db7c415SStefan Berger     }
391fcbed221SStefan Berger     printf("\n");
3928db7c415SStefan Berger }
3938db7c415SStefan Berger #endif
3948db7c415SStefan Berger 
395edff8678SStefan Berger /*
396edff8678SStefan Berger  * Read a register of the TIS interface
397edff8678SStefan Berger  * See specs pages 33-63 for description of the registers
398edff8678SStefan Berger  */
399edff8678SStefan Berger static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr,
400edff8678SStefan Berger                                   unsigned size)
401edff8678SStefan Berger {
402edff8678SStefan Berger     TPMState *s = opaque;
403edff8678SStefan Berger     uint16_t offset = addr & 0xffc;
404edff8678SStefan Berger     uint8_t shift = (addr & 0x3) * 8;
405edff8678SStefan Berger     uint32_t val = 0xffffffff;
406edff8678SStefan Berger     uint8_t locty = tpm_tis_locality_from_addr(addr);
407edff8678SStefan Berger     uint32_t avail;
408feeb755fSStefan Berger     uint8_t v;
409edff8678SStefan Berger 
4108f0605ccSStefan Berger     if (tpm_backend_had_startup_error(s->be_driver)) {
4116cd65969SStefan Berger         return 0;
412edff8678SStefan Berger     }
413edff8678SStefan Berger 
414edff8678SStefan Berger     switch (offset) {
415edff8678SStefan Berger     case TPM_TIS_REG_ACCESS:
416edff8678SStefan Berger         /* never show the SEIZE flag even though we use it internally */
4173d4960c7SMarc-André Lureau         val = s->loc[locty].access & ~TPM_TIS_ACCESS_SEIZE;
418edff8678SStefan Berger         /* the pending flag is always calculated */
419edff8678SStefan Berger         if (tpm_tis_check_request_use_except(s, locty)) {
420edff8678SStefan Berger             val |= TPM_TIS_ACCESS_PENDING_REQUEST;
421edff8678SStefan Berger         }
4228f0605ccSStefan Berger         val |= !tpm_backend_get_tpm_established_flag(s->be_driver);
423edff8678SStefan Berger         break;
424edff8678SStefan Berger     case TPM_TIS_REG_INT_ENABLE:
4253d4960c7SMarc-André Lureau         val = s->loc[locty].inte;
426edff8678SStefan Berger         break;
427edff8678SStefan Berger     case TPM_TIS_REG_INT_VECTOR:
4283d4960c7SMarc-André Lureau         val = s->irq_num;
429edff8678SStefan Berger         break;
430edff8678SStefan Berger     case TPM_TIS_REG_INT_STATUS:
4313d4960c7SMarc-André Lureau         val = s->loc[locty].ints;
432edff8678SStefan Berger         break;
433edff8678SStefan Berger     case TPM_TIS_REG_INTF_CAPABILITY:
434116694c3SStefan Berger         switch (s->be_tpm_version) {
435116694c3SStefan Berger         case TPM_VERSION_UNSPEC:
436116694c3SStefan Berger             val = 0;
437116694c3SStefan Berger             break;
438116694c3SStefan Berger         case TPM_VERSION_1_2:
439116694c3SStefan Berger             val = TPM_TIS_CAPABILITIES_SUPPORTED1_3;
440116694c3SStefan Berger             break;
441116694c3SStefan Berger         case TPM_VERSION_2_0:
442116694c3SStefan Berger             val = TPM_TIS_CAPABILITIES_SUPPORTED2_0;
443116694c3SStefan Berger             break;
444116694c3SStefan Berger         }
445edff8678SStefan Berger         break;
446edff8678SStefan Berger     case TPM_TIS_REG_STS:
4473d4960c7SMarc-André Lureau         if (s->active_locty == locty) {
4483d4960c7SMarc-André Lureau             if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
449edff8678SStefan Berger                 val = TPM_TIS_BURST_COUNT(
450c5496b97SStefan Berger                        MIN(tpm_cmd_get_size(&s->buffer),
451e6b703f6SStefan Berger                            s->be_buffer_size)
452f999d81bSStefan Berger                        - s->rw_offset) | s->loc[locty].sts;
453edff8678SStefan Berger             } else {
454f999d81bSStefan Berger                 avail = s->be_buffer_size - s->rw_offset;
455edff8678SStefan Berger                 /*
456edff8678SStefan Berger                  * byte-sized reads should not return 0x00 for 0x100
457edff8678SStefan Berger                  * available bytes.
458edff8678SStefan Berger                  */
459edff8678SStefan Berger                 if (size == 1 && avail > 0xff) {
460edff8678SStefan Berger                     avail = 0xff;
461edff8678SStefan Berger                 }
4623d4960c7SMarc-André Lureau                 val = TPM_TIS_BURST_COUNT(avail) | s->loc[locty].sts;
463edff8678SStefan Berger             }
464edff8678SStefan Berger         }
465edff8678SStefan Berger         break;
466edff8678SStefan Berger     case TPM_TIS_REG_DATA_FIFO:
4672eae8c75SStefan Berger     case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END:
4683d4960c7SMarc-André Lureau         if (s->active_locty == locty) {
469feeb755fSStefan Berger             if (size > 4 - (addr & 0x3)) {
470feeb755fSStefan Berger                 /* prevent access beyond FIFO */
471feeb755fSStefan Berger                 size = 4 - (addr & 0x3);
472feeb755fSStefan Berger             }
473feeb755fSStefan Berger             val = 0;
474feeb755fSStefan Berger             shift = 0;
475feeb755fSStefan Berger             while (size > 0) {
4763d4960c7SMarc-André Lureau                 switch (s->loc[locty].state) {
477edff8678SStefan Berger                 case TPM_TIS_STATE_COMPLETION:
478feeb755fSStefan Berger                     v = tpm_tis_data_read(s, locty);
479edff8678SStefan Berger                     break;
480edff8678SStefan Berger                 default:
481feeb755fSStefan Berger                     v = TPM_TIS_NO_DATA_BYTE;
482edff8678SStefan Berger                     break;
483edff8678SStefan Berger                 }
484feeb755fSStefan Berger                 val |= (v << shift);
485feeb755fSStefan Berger                 shift += 8;
486feeb755fSStefan Berger                 size--;
487feeb755fSStefan Berger             }
488feeb755fSStefan Berger             shift = 0; /* no more adjustments */
489edff8678SStefan Berger         }
490edff8678SStefan Berger         break;
491116694c3SStefan Berger     case TPM_TIS_REG_INTERFACE_ID:
4923d4960c7SMarc-André Lureau         val = s->loc[locty].iface_id;
493116694c3SStefan Berger         break;
494edff8678SStefan Berger     case TPM_TIS_REG_DID_VID:
495edff8678SStefan Berger         val = (TPM_TIS_TPM_DID << 16) | TPM_TIS_TPM_VID;
496edff8678SStefan Berger         break;
497edff8678SStefan Berger     case TPM_TIS_REG_RID:
498edff8678SStefan Berger         val = TPM_TIS_TPM_RID;
499edff8678SStefan Berger         break;
5008db7c415SStefan Berger #ifdef DEBUG_TIS
5018db7c415SStefan Berger     case TPM_TIS_REG_DEBUG:
5028db7c415SStefan Berger         tpm_tis_dump_state(opaque, addr);
5038db7c415SStefan Berger         break;
5048db7c415SStefan Berger #endif
505edff8678SStefan Berger     }
506edff8678SStefan Berger 
507edff8678SStefan Berger     if (shift) {
508edff8678SStefan Berger         val >>= shift;
509edff8678SStefan Berger     }
510edff8678SStefan Berger 
511fcbed221SStefan Berger     trace_tpm_tis_mmio_read(size, addr, val);
512edff8678SStefan Berger 
513edff8678SStefan Berger     return val;
514edff8678SStefan Berger }
515edff8678SStefan Berger 
516edff8678SStefan Berger /*
517edff8678SStefan Berger  * Write a value to a register of the TIS interface
518edff8678SStefan Berger  * See specs pages 33-63 for description of the registers
519edff8678SStefan Berger  */
520ff2bc0c1SMarc-André Lureau static void tpm_tis_mmio_write(void *opaque, hwaddr addr,
521ff2bc0c1SMarc-André Lureau                                uint64_t val, unsigned size)
522edff8678SStefan Berger {
523edff8678SStefan Berger     TPMState *s = opaque;
524feeb755fSStefan Berger     uint16_t off = addr & 0xffc;
525feeb755fSStefan Berger     uint8_t shift = (addr & 0x3) * 8;
526edff8678SStefan Berger     uint8_t locty = tpm_tis_locality_from_addr(addr);
527edff8678SStefan Berger     uint8_t active_locty, l;
528edff8678SStefan Berger     int c, set_new_locty = 1;
529edff8678SStefan Berger     uint16_t len;
530feeb755fSStefan Berger     uint32_t mask = (size == 1) ? 0xff : ((size == 2) ? 0xffff : ~0);
531edff8678SStefan Berger 
532fcbed221SStefan Berger     trace_tpm_tis_mmio_write(size, addr, val);
533edff8678SStefan Berger 
534ff2bc0c1SMarc-André Lureau     if (locty == 4) {
535fcbed221SStefan Berger         trace_tpm_tis_mmio_write_locty4();
536edff8678SStefan Berger         return;
537edff8678SStefan Berger     }
538edff8678SStefan Berger 
5398f0605ccSStefan Berger     if (tpm_backend_had_startup_error(s->be_driver)) {
540edff8678SStefan Berger         return;
541edff8678SStefan Berger     }
542edff8678SStefan Berger 
543feeb755fSStefan Berger     val &= mask;
544feeb755fSStefan Berger 
545feeb755fSStefan Berger     if (shift) {
546feeb755fSStefan Berger         val <<= shift;
547feeb755fSStefan Berger         mask <<= shift;
548feeb755fSStefan Berger     }
549feeb755fSStefan Berger 
550feeb755fSStefan Berger     mask ^= 0xffffffff;
551feeb755fSStefan Berger 
552edff8678SStefan Berger     switch (off) {
553edff8678SStefan Berger     case TPM_TIS_REG_ACCESS:
554edff8678SStefan Berger 
555edff8678SStefan Berger         if ((val & TPM_TIS_ACCESS_SEIZE)) {
556edff8678SStefan Berger             val &= ~(TPM_TIS_ACCESS_REQUEST_USE |
557edff8678SStefan Berger                      TPM_TIS_ACCESS_ACTIVE_LOCALITY);
558edff8678SStefan Berger         }
559edff8678SStefan Berger 
5603d4960c7SMarc-André Lureau         active_locty = s->active_locty;
561edff8678SStefan Berger 
562edff8678SStefan Berger         if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) {
563edff8678SStefan Berger             /* give up locality if currently owned */
5643d4960c7SMarc-André Lureau             if (s->active_locty == locty) {
565fcbed221SStefan Berger                 trace_tpm_tis_mmio_write_release_locty(locty);
566edff8678SStefan Berger 
567edff8678SStefan Berger                 uint8_t newlocty = TPM_TIS_NO_LOCALITY;
568edff8678SStefan Berger                 /* anybody wants the locality ? */
569edff8678SStefan Berger                 for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) {
5703d4960c7SMarc-André Lureau                     if ((s->loc[c].access & TPM_TIS_ACCESS_REQUEST_USE)) {
571fcbed221SStefan Berger                         trace_tpm_tis_mmio_write_locty_req_use(c);
572edff8678SStefan Berger                         newlocty = c;
573edff8678SStefan Berger                         break;
574edff8678SStefan Berger                     }
575edff8678SStefan Berger                 }
576fcbed221SStefan Berger                 trace_tpm_tis_mmio_write_next_locty(newlocty);
577edff8678SStefan Berger 
578edff8678SStefan Berger                 if (TPM_TIS_IS_VALID_LOCTY(newlocty)) {
579edff8678SStefan Berger                     set_new_locty = 0;
580edff8678SStefan Berger                     tpm_tis_prep_abort(s, locty, newlocty);
581edff8678SStefan Berger                 } else {
582edff8678SStefan Berger                     active_locty = TPM_TIS_NO_LOCALITY;
583edff8678SStefan Berger                 }
584edff8678SStefan Berger             } else {
585edff8678SStefan Berger                 /* not currently the owner; clear a pending request */
5863d4960c7SMarc-André Lureau                 s->loc[locty].access &= ~TPM_TIS_ACCESS_REQUEST_USE;
587edff8678SStefan Berger             }
588edff8678SStefan Berger         }
589edff8678SStefan Berger 
590edff8678SStefan Berger         if ((val & TPM_TIS_ACCESS_BEEN_SEIZED)) {
5913d4960c7SMarc-André Lureau             s->loc[locty].access &= ~TPM_TIS_ACCESS_BEEN_SEIZED;
592edff8678SStefan Berger         }
593edff8678SStefan Berger 
594edff8678SStefan Berger         if ((val & TPM_TIS_ACCESS_SEIZE)) {
595edff8678SStefan Berger             /*
596edff8678SStefan Berger              * allow seize if a locality is active and the requesting
597edff8678SStefan Berger              * locality is higher than the one that's active
598edff8678SStefan Berger              * OR
599edff8678SStefan Berger              * allow seize for requesting locality if no locality is
600edff8678SStefan Berger              * active
601edff8678SStefan Berger              */
6023d4960c7SMarc-André Lureau             while ((TPM_TIS_IS_VALID_LOCTY(s->active_locty) &&
6033d4960c7SMarc-André Lureau                     locty > s->active_locty) ||
6043d4960c7SMarc-André Lureau                     !TPM_TIS_IS_VALID_LOCTY(s->active_locty)) {
605edff8678SStefan Berger                 bool higher_seize = FALSE;
606edff8678SStefan Berger 
607edff8678SStefan Berger                 /* already a pending SEIZE ? */
6083d4960c7SMarc-André Lureau                 if ((s->loc[locty].access & TPM_TIS_ACCESS_SEIZE)) {
609edff8678SStefan Berger                     break;
610edff8678SStefan Berger                 }
611edff8678SStefan Berger 
612edff8678SStefan Berger                 /* check for ongoing seize by a higher locality */
613edff8678SStefan Berger                 for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES; l++) {
6143d4960c7SMarc-André Lureau                     if ((s->loc[l].access & TPM_TIS_ACCESS_SEIZE)) {
615edff8678SStefan Berger                         higher_seize = TRUE;
616edff8678SStefan Berger                         break;
617edff8678SStefan Berger                     }
618edff8678SStefan Berger                 }
619edff8678SStefan Berger 
620edff8678SStefan Berger                 if (higher_seize) {
621edff8678SStefan Berger                     break;
622edff8678SStefan Berger                 }
623edff8678SStefan Berger 
624edff8678SStefan Berger                 /* cancel any seize by a lower locality */
625edff8678SStefan Berger                 for (l = 0; l < locty - 1; l++) {
6263d4960c7SMarc-André Lureau                     s->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE;
627edff8678SStefan Berger                 }
628edff8678SStefan Berger 
6293d4960c7SMarc-André Lureau                 s->loc[locty].access |= TPM_TIS_ACCESS_SEIZE;
630fcbed221SStefan Berger 
631fcbed221SStefan Berger                 trace_tpm_tis_mmio_write_locty_seized(locty, s->active_locty);
632fcbed221SStefan Berger                 trace_tpm_tis_mmio_write_init_abort();
633fcbed221SStefan Berger 
634edff8678SStefan Berger                 set_new_locty = 0;
6353d4960c7SMarc-André Lureau                 tpm_tis_prep_abort(s, s->active_locty, locty);
636edff8678SStefan Berger                 break;
637edff8678SStefan Berger             }
638edff8678SStefan Berger         }
639edff8678SStefan Berger 
640edff8678SStefan Berger         if ((val & TPM_TIS_ACCESS_REQUEST_USE)) {
6413d4960c7SMarc-André Lureau             if (s->active_locty != locty) {
6423d4960c7SMarc-André Lureau                 if (TPM_TIS_IS_VALID_LOCTY(s->active_locty)) {
6433d4960c7SMarc-André Lureau                     s->loc[locty].access |= TPM_TIS_ACCESS_REQUEST_USE;
644edff8678SStefan Berger                 } else {
645edff8678SStefan Berger                     /* no locality active -> make this one active now */
646edff8678SStefan Berger                     active_locty = locty;
647edff8678SStefan Berger                 }
648edff8678SStefan Berger             }
649edff8678SStefan Berger         }
650edff8678SStefan Berger 
651edff8678SStefan Berger         if (set_new_locty) {
652edff8678SStefan Berger             tpm_tis_new_active_locality(s, active_locty);
653edff8678SStefan Berger         }
654edff8678SStefan Berger 
655edff8678SStefan Berger         break;
656edff8678SStefan Berger     case TPM_TIS_REG_INT_ENABLE:
6573d4960c7SMarc-André Lureau         if (s->active_locty != locty) {
658edff8678SStefan Berger             break;
659edff8678SStefan Berger         }
660edff8678SStefan Berger 
6613d4960c7SMarc-André Lureau         s->loc[locty].inte &= mask;
6623d4960c7SMarc-André Lureau         s->loc[locty].inte |= (val & (TPM_TIS_INT_ENABLED |
663edff8678SStefan Berger                                         TPM_TIS_INT_POLARITY_MASK |
664edff8678SStefan Berger                                         TPM_TIS_INTERRUPTS_SUPPORTED));
665edff8678SStefan Berger         break;
666edff8678SStefan Berger     case TPM_TIS_REG_INT_VECTOR:
667edff8678SStefan Berger         /* hard wired -- ignore */
668edff8678SStefan Berger         break;
669edff8678SStefan Berger     case TPM_TIS_REG_INT_STATUS:
6703d4960c7SMarc-André Lureau         if (s->active_locty != locty) {
671edff8678SStefan Berger             break;
672edff8678SStefan Berger         }
673edff8678SStefan Berger 
674edff8678SStefan Berger         /* clearing of interrupt flags */
675edff8678SStefan Berger         if (((val & TPM_TIS_INTERRUPTS_SUPPORTED)) &&
6763d4960c7SMarc-André Lureau             (s->loc[locty].ints & TPM_TIS_INTERRUPTS_SUPPORTED)) {
6773d4960c7SMarc-André Lureau             s->loc[locty].ints &= ~val;
6783d4960c7SMarc-André Lureau             if (s->loc[locty].ints == 0) {
6793d4960c7SMarc-André Lureau                 qemu_irq_lower(s->irq);
680fcbed221SStefan Berger                 trace_tpm_tis_mmio_write_lowering_irq();
681edff8678SStefan Berger             }
682edff8678SStefan Berger         }
6833d4960c7SMarc-André Lureau         s->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED);
684edff8678SStefan Berger         break;
685edff8678SStefan Berger     case TPM_TIS_REG_STS:
6863d4960c7SMarc-André Lureau         if (s->active_locty != locty) {
687edff8678SStefan Berger             break;
688edff8678SStefan Berger         }
689edff8678SStefan Berger 
690116694c3SStefan Berger         if (s->be_tpm_version == TPM_VERSION_2_0) {
691116694c3SStefan Berger             /* some flags that are only supported for TPM 2 */
692116694c3SStefan Berger             if (val & TPM_TIS_STS_COMMAND_CANCEL) {
6933d4960c7SMarc-André Lureau                 if (s->loc[locty].state == TPM_TIS_STATE_EXECUTION) {
694116694c3SStefan Berger                     /*
695116694c3SStefan Berger                      * request the backend to cancel. Some backends may not
696116694c3SStefan Berger                      * support it
697116694c3SStefan Berger                      */
698116694c3SStefan Berger                     tpm_backend_cancel_cmd(s->be_driver);
699116694c3SStefan Berger                 }
700116694c3SStefan Berger             }
701116694c3SStefan Berger 
702116694c3SStefan Berger             if (val & TPM_TIS_STS_RESET_ESTABLISHMENT_BIT) {
703116694c3SStefan Berger                 if (locty == 3 || locty == 4) {
704116694c3SStefan Berger                     tpm_backend_reset_tpm_established_flag(s->be_driver, locty);
705116694c3SStefan Berger                 }
706116694c3SStefan Berger             }
707116694c3SStefan Berger         }
708116694c3SStefan Berger 
709edff8678SStefan Berger         val &= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO |
710edff8678SStefan Berger                 TPM_TIS_STS_RESPONSE_RETRY);
711edff8678SStefan Berger 
712edff8678SStefan Berger         if (val == TPM_TIS_STS_COMMAND_READY) {
7133d4960c7SMarc-André Lureau             switch (s->loc[locty].state) {
714edff8678SStefan Berger 
715edff8678SStefan Berger             case TPM_TIS_STATE_READY:
716f999d81bSStefan Berger                 s->rw_offset = 0;
717edff8678SStefan Berger             break;
718edff8678SStefan Berger 
719edff8678SStefan Berger             case TPM_TIS_STATE_IDLE:
7203d4960c7SMarc-André Lureau                 tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_COMMAND_READY);
7213d4960c7SMarc-André Lureau                 s->loc[locty].state = TPM_TIS_STATE_READY;
722edff8678SStefan Berger                 tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
723edff8678SStefan Berger             break;
724edff8678SStefan Berger 
725edff8678SStefan Berger             case TPM_TIS_STATE_EXECUTION:
726edff8678SStefan Berger             case TPM_TIS_STATE_RECEPTION:
727edff8678SStefan Berger                 /* abort currently running command */
728fcbed221SStefan Berger                 trace_tpm_tis_mmio_write_init_abort();
729edff8678SStefan Berger                 tpm_tis_prep_abort(s, locty, locty);
730edff8678SStefan Berger             break;
731edff8678SStefan Berger 
732edff8678SStefan Berger             case TPM_TIS_STATE_COMPLETION:
733f999d81bSStefan Berger                 s->rw_offset = 0;
734edff8678SStefan Berger                 /* shortcut to ready state with C/R set */
7353d4960c7SMarc-André Lureau                 s->loc[locty].state = TPM_TIS_STATE_READY;
7363d4960c7SMarc-André Lureau                 if (!(s->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) {
7373d4960c7SMarc-André Lureau                     tpm_tis_sts_set(&s->loc[locty],
738fd859081SStefan Berger                                     TPM_TIS_STS_COMMAND_READY);
739edff8678SStefan Berger                     tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
740edff8678SStefan Berger                 }
7413d4960c7SMarc-André Lureau                 s->loc[locty].sts &= ~(TPM_TIS_STS_DATA_AVAILABLE);
742edff8678SStefan Berger             break;
743edff8678SStefan Berger 
744edff8678SStefan Berger             }
745edff8678SStefan Berger         } else if (val == TPM_TIS_STS_TPM_GO) {
7463d4960c7SMarc-André Lureau             switch (s->loc[locty].state) {
747edff8678SStefan Berger             case TPM_TIS_STATE_RECEPTION:
7483d4960c7SMarc-André Lureau                 if ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) == 0) {
749edff8678SStefan Berger                     tpm_tis_tpm_send(s, locty);
750edff8678SStefan Berger                 }
751edff8678SStefan Berger                 break;
752edff8678SStefan Berger             default:
753edff8678SStefan Berger                 /* ignore */
754edff8678SStefan Berger                 break;
755edff8678SStefan Berger             }
756edff8678SStefan Berger         } else if (val == TPM_TIS_STS_RESPONSE_RETRY) {
7573d4960c7SMarc-André Lureau             switch (s->loc[locty].state) {
758edff8678SStefan Berger             case TPM_TIS_STATE_COMPLETION:
759f999d81bSStefan Berger                 s->rw_offset = 0;
7603d4960c7SMarc-André Lureau                 tpm_tis_sts_set(&s->loc[locty],
761fd859081SStefan Berger                                 TPM_TIS_STS_VALID|
762fd859081SStefan Berger                                 TPM_TIS_STS_DATA_AVAILABLE);
763edff8678SStefan Berger                 break;
764edff8678SStefan Berger             default:
765edff8678SStefan Berger                 /* ignore */
766edff8678SStefan Berger                 break;
767edff8678SStefan Berger             }
768edff8678SStefan Berger         }
769edff8678SStefan Berger         break;
770edff8678SStefan Berger     case TPM_TIS_REG_DATA_FIFO:
7712eae8c75SStefan Berger     case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END:
772edff8678SStefan Berger         /* data fifo */
7733d4960c7SMarc-André Lureau         if (s->active_locty != locty) {
774edff8678SStefan Berger             break;
775edff8678SStefan Berger         }
776edff8678SStefan Berger 
7773d4960c7SMarc-André Lureau         if (s->loc[locty].state == TPM_TIS_STATE_IDLE ||
7783d4960c7SMarc-André Lureau             s->loc[locty].state == TPM_TIS_STATE_EXECUTION ||
7793d4960c7SMarc-André Lureau             s->loc[locty].state == TPM_TIS_STATE_COMPLETION) {
780edff8678SStefan Berger             /* drop the byte */
781edff8678SStefan Berger         } else {
782fcbed221SStefan Berger             trace_tpm_tis_mmio_write_data2send(val, size);
7833d4960c7SMarc-André Lureau             if (s->loc[locty].state == TPM_TIS_STATE_READY) {
7843d4960c7SMarc-André Lureau                 s->loc[locty].state = TPM_TIS_STATE_RECEPTION;
7853d4960c7SMarc-André Lureau                 tpm_tis_sts_set(&s->loc[locty],
786fd859081SStefan Berger                                 TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID);
787edff8678SStefan Berger             }
788edff8678SStefan Berger 
789feeb755fSStefan Berger             val >>= shift;
790feeb755fSStefan Berger             if (size > 4 - (addr & 0x3)) {
791feeb755fSStefan Berger                 /* prevent access beyond FIFO */
792feeb755fSStefan Berger                 size = 4 - (addr & 0x3);
793feeb755fSStefan Berger             }
794feeb755fSStefan Berger 
7953d4960c7SMarc-André Lureau             while ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) && size > 0) {
796f999d81bSStefan Berger                 if (s->rw_offset < s->be_buffer_size) {
797f999d81bSStefan Berger                     s->buffer[s->rw_offset++] =
798e6b703f6SStefan Berger                         (uint8_t)val;
799feeb755fSStefan Berger                     val >>= 8;
800feeb755fSStefan Berger                     size--;
801edff8678SStefan Berger                 } else {
8023d4960c7SMarc-André Lureau                     tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID);
803edff8678SStefan Berger                 }
804edff8678SStefan Berger             }
805edff8678SStefan Berger 
806edff8678SStefan Berger             /* check for complete packet */
807f999d81bSStefan Berger             if (s->rw_offset > 5 &&
8083d4960c7SMarc-André Lureau                 (s->loc[locty].sts & TPM_TIS_STS_EXPECT)) {
809edff8678SStefan Berger                 /* we have a packet length - see if we have all of it */
8103d4960c7SMarc-André Lureau                 bool need_irq = !(s->loc[locty].sts & TPM_TIS_STS_VALID);
811d8383d61SMarc-André Lureau 
812c5496b97SStefan Berger                 len = tpm_cmd_get_size(&s->buffer);
813f999d81bSStefan Berger                 if (len > s->rw_offset) {
8143d4960c7SMarc-André Lureau                     tpm_tis_sts_set(&s->loc[locty],
815fd859081SStefan Berger                                     TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID);
816edff8678SStefan Berger                 } else {
817edff8678SStefan Berger                     /* packet complete */
8183d4960c7SMarc-André Lureau                     tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID);
819edff8678SStefan Berger                 }
82029b558d8SStefan Berger                 if (need_irq) {
821edff8678SStefan Berger                     tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
822edff8678SStefan Berger                 }
823edff8678SStefan Berger             }
824edff8678SStefan Berger         }
825edff8678SStefan Berger         break;
826116694c3SStefan Berger     case TPM_TIS_REG_INTERFACE_ID:
827116694c3SStefan Berger         if (val & TPM_TIS_IFACE_ID_INT_SEL_LOCK) {
828116694c3SStefan Berger             for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
8293d4960c7SMarc-André Lureau                 s->loc[l].iface_id |= TPM_TIS_IFACE_ID_INT_SEL_LOCK;
830116694c3SStefan Berger             }
831116694c3SStefan Berger         }
832116694c3SStefan Berger         break;
833edff8678SStefan Berger     }
834edff8678SStefan Berger }
835edff8678SStefan Berger 
836edff8678SStefan Berger static const MemoryRegionOps tpm_tis_memory_ops = {
837edff8678SStefan Berger     .read = tpm_tis_mmio_read,
838edff8678SStefan Berger     .write = tpm_tis_mmio_write,
839edff8678SStefan Berger     .endianness = DEVICE_LITTLE_ENDIAN,
840edff8678SStefan Berger     .valid = {
841edff8678SStefan Berger         .min_access_size = 1,
842edff8678SStefan Berger         .max_access_size = 4,
843edff8678SStefan Berger     },
844edff8678SStefan Berger };
845edff8678SStefan Berger 
846edff8678SStefan Berger /*
8475cb18b3dSStefan Berger  * Get the TPMVersion of the backend device being used
8485cb18b3dSStefan Berger  */
8499af7a721SMarc-André Lureau static enum TPMVersion tpm_tis_get_tpm_version(TPMIf *ti)
8505cb18b3dSStefan Berger {
8519af7a721SMarc-André Lureau     TPMState *s = TPM(ti);
8525cb18b3dSStefan Berger 
853ad4aca69SStefan Berger     if (tpm_backend_had_startup_error(s->be_driver)) {
854ad4aca69SStefan Berger         return TPM_VERSION_UNSPEC;
855ad4aca69SStefan Berger     }
856ad4aca69SStefan Berger 
8575cb18b3dSStefan Berger     return tpm_backend_get_tpm_version(s->be_driver);
8585cb18b3dSStefan Berger }
8595cb18b3dSStefan Berger 
8605cb18b3dSStefan Berger /*
861edff8678SStefan Berger  * This function is called when the machine starts, resets or due to
862edff8678SStefan Berger  * S3 resume.
863edff8678SStefan Berger  */
864edff8678SStefan Berger static void tpm_tis_reset(DeviceState *dev)
865edff8678SStefan Berger {
866edff8678SStefan Berger     TPMState *s = TPM(dev);
867edff8678SStefan Berger     int c;
868edff8678SStefan Berger 
869116694c3SStefan Berger     s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver);
8701af3d63eSStefan Berger     s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->be_driver),
8711af3d63eSStefan Berger                             TPM_TIS_BUFFER_MAX);
872116694c3SStefan Berger 
8738f0605ccSStefan Berger     tpm_backend_reset(s->be_driver);
874edff8678SStefan Berger 
8753d4960c7SMarc-André Lureau     s->active_locty = TPM_TIS_NO_LOCALITY;
8763d4960c7SMarc-André Lureau     s->next_locty = TPM_TIS_NO_LOCALITY;
8773d4960c7SMarc-André Lureau     s->aborting_locty = TPM_TIS_NO_LOCALITY;
878edff8678SStefan Berger 
879edff8678SStefan Berger     for (c = 0; c < TPM_TIS_NUM_LOCALITIES; c++) {
8803d4960c7SMarc-André Lureau         s->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS;
881116694c3SStefan Berger         switch (s->be_tpm_version) {
882116694c3SStefan Berger         case TPM_VERSION_UNSPEC:
883116694c3SStefan Berger             break;
884116694c3SStefan Berger         case TPM_VERSION_1_2:
8853d4960c7SMarc-André Lureau             s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY1_2;
8863d4960c7SMarc-André Lureau             s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3;
887116694c3SStefan Berger             break;
888116694c3SStefan Berger         case TPM_VERSION_2_0:
8893d4960c7SMarc-André Lureau             s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY2_0;
8903d4960c7SMarc-André Lureau             s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0;
891116694c3SStefan Berger             break;
892116694c3SStefan Berger         }
8933d4960c7SMarc-André Lureau         s->loc[c].inte = TPM_TIS_INT_POLARITY_LOW_LEVEL;
8943d4960c7SMarc-André Lureau         s->loc[c].ints = 0;
8953d4960c7SMarc-André Lureau         s->loc[c].state = TPM_TIS_STATE_IDLE;
896edff8678SStefan Berger 
897f999d81bSStefan Berger         s->rw_offset = 0;
898edff8678SStefan Berger     }
899edff8678SStefan Berger 
9003bd9e161SStefan Berger     tpm_backend_startup_tpm(s->be_driver, s->be_buffer_size);
901edff8678SStefan Berger }
902edff8678SStefan Berger 
9039ec08c48SStefan Berger /* persistent state handling */
9049ec08c48SStefan Berger 
9059ec08c48SStefan Berger static int tpm_tis_pre_save(void *opaque)
9069ec08c48SStefan Berger {
9079ec08c48SStefan Berger     TPMState *s = opaque;
9089ec08c48SStefan Berger     uint8_t locty = s->active_locty;
9099ec08c48SStefan Berger 
9109ec08c48SStefan Berger     trace_tpm_tis_pre_save(locty, s->rw_offset);
9119ec08c48SStefan Berger 
9129ec08c48SStefan Berger     if (DEBUG_TIS) {
9139ec08c48SStefan Berger         tpm_tis_dump_state(opaque, 0);
9149ec08c48SStefan Berger     }
9159ec08c48SStefan Berger 
9169ec08c48SStefan Berger     /*
9179ec08c48SStefan Berger      * Synchronize with backend completion.
9189ec08c48SStefan Berger      */
9199ec08c48SStefan Berger     tpm_backend_finish_sync(s->be_driver);
9209ec08c48SStefan Berger 
9219ec08c48SStefan Berger     return 0;
9229ec08c48SStefan Berger }
9239ec08c48SStefan Berger 
9249ec08c48SStefan Berger static const VMStateDescription vmstate_locty = {
9259ec08c48SStefan Berger     .name = "tpm-tis/locty",
9269ec08c48SStefan Berger     .version_id = 0,
9279ec08c48SStefan Berger     .fields      = (VMStateField[]) {
9289ec08c48SStefan Berger         VMSTATE_UINT32(state, TPMLocality),
9299ec08c48SStefan Berger         VMSTATE_UINT32(inte, TPMLocality),
9309ec08c48SStefan Berger         VMSTATE_UINT32(ints, TPMLocality),
9319ec08c48SStefan Berger         VMSTATE_UINT8(access, TPMLocality),
9329ec08c48SStefan Berger         VMSTATE_UINT32(sts, TPMLocality),
9339ec08c48SStefan Berger         VMSTATE_UINT32(iface_id, TPMLocality),
9349ec08c48SStefan Berger         VMSTATE_END_OF_LIST(),
9359ec08c48SStefan Berger     }
9369ec08c48SStefan Berger };
9379ec08c48SStefan Berger 
938edff8678SStefan Berger static const VMStateDescription vmstate_tpm_tis = {
9399ec08c48SStefan Berger     .name = "tpm-tis",
9409ec08c48SStefan Berger     .version_id = 0,
9419ec08c48SStefan Berger     .pre_save  = tpm_tis_pre_save,
9429ec08c48SStefan Berger     .fields = (VMStateField[]) {
9439ec08c48SStefan Berger         VMSTATE_BUFFER(buffer, TPMState),
9449ec08c48SStefan Berger         VMSTATE_UINT16(rw_offset, TPMState),
9459ec08c48SStefan Berger         VMSTATE_UINT8(active_locty, TPMState),
9469ec08c48SStefan Berger         VMSTATE_UINT8(aborting_locty, TPMState),
9479ec08c48SStefan Berger         VMSTATE_UINT8(next_locty, TPMState),
9489ec08c48SStefan Berger 
9499ec08c48SStefan Berger         VMSTATE_STRUCT_ARRAY(loc, TPMState, TPM_TIS_NUM_LOCALITIES, 0,
9509ec08c48SStefan Berger                              vmstate_locty, TPMLocality),
9519ec08c48SStefan Berger 
9529ec08c48SStefan Berger         VMSTATE_END_OF_LIST()
9539ec08c48SStefan Berger     }
954edff8678SStefan Berger };
955edff8678SStefan Berger 
956edff8678SStefan Berger static Property tpm_tis_properties[] = {
9573d4960c7SMarc-André Lureau     DEFINE_PROP_UINT32("irq", TPMState, irq_num, TPM_TIS_IRQ),
958c0378544SMarc-André Lureau     DEFINE_PROP_TPMBE("tpmdev", TPMState, be_driver),
959*b6148757SMarc-André Lureau     DEFINE_PROP_BOOL("ppi", TPMState, ppi_enabled, true),
960edff8678SStefan Berger     DEFINE_PROP_END_OF_LIST(),
961edff8678SStefan Berger };
962edff8678SStefan Berger 
963edff8678SStefan Berger static void tpm_tis_realizefn(DeviceState *dev, Error **errp)
964edff8678SStefan Berger {
965edff8678SStefan Berger     TPMState *s = TPM(dev);
966edff8678SStefan Berger 
96751a837e9SMarc-André Lureau     if (!tpm_find()) {
96851a837e9SMarc-André Lureau         error_setg(errp, "at most one TPM device is permitted");
96951a837e9SMarc-André Lureau         return;
97051a837e9SMarc-André Lureau     }
97151a837e9SMarc-André Lureau 
972edff8678SStefan Berger     if (!s->be_driver) {
973c0378544SMarc-André Lureau         error_setg(errp, "'tpmdev' property is required");
974edff8678SStefan Berger         return;
975edff8678SStefan Berger     }
9763d4960c7SMarc-André Lureau     if (s->irq_num > 15) {
977c87b35faSMarc-André Lureau         error_setg(errp, "IRQ %d is outside valid range of 0 to 15",
978c87b35faSMarc-André Lureau                    s->irq_num);
979edff8678SStefan Berger         return;
980edff8678SStefan Berger     }
981edff8678SStefan Berger 
9823d4960c7SMarc-André Lureau     isa_init_irq(&s->busdev, &s->irq, s->irq_num);
9839dfd24edSStefan Berger 
9849dfd24edSStefan Berger     memory_region_add_subregion(isa_address_space(ISA_DEVICE(dev)),
9859dfd24edSStefan Berger                                 TPM_TIS_ADDR_BASE, &s->mmio);
986edff8678SStefan Berger }
987edff8678SStefan Berger 
988edff8678SStefan Berger static void tpm_tis_initfn(Object *obj)
989edff8678SStefan Berger {
990edff8678SStefan Berger     TPMState *s = TPM(obj);
991edff8678SStefan Berger 
992853dca12SPaolo Bonzini     memory_region_init_io(&s->mmio, OBJECT(s), &tpm_tis_memory_ops,
993853dca12SPaolo Bonzini                           s, "tpm-tis-mmio",
994edff8678SStefan Berger                           TPM_TIS_NUM_LOCALITIES << TPM_TIS_LOCALITY_SHIFT);
995edff8678SStefan Berger }
996edff8678SStefan Berger 
997edff8678SStefan Berger static void tpm_tis_class_init(ObjectClass *klass, void *data)
998edff8678SStefan Berger {
999edff8678SStefan Berger     DeviceClass *dc = DEVICE_CLASS(klass);
100005a69998SMarc-André Lureau     TPMIfClass *tc = TPM_IF_CLASS(klass);
1001edff8678SStefan Berger 
1002edff8678SStefan Berger     dc->realize = tpm_tis_realizefn;
1003edff8678SStefan Berger     dc->props = tpm_tis_properties;
1004edff8678SStefan Berger     dc->reset = tpm_tis_reset;
1005edff8678SStefan Berger     dc->vmsd  = &vmstate_tpm_tis;
1006191adc94SMarc-André Lureau     tc->model = TPM_MODEL_TPM_TIS;
10079af7a721SMarc-André Lureau     tc->get_version = tpm_tis_get_tpm_version;
100805a69998SMarc-André Lureau     tc->request_completed = tpm_tis_request_completed;
1009edff8678SStefan Berger }
1010edff8678SStefan Berger 
1011edff8678SStefan Berger static const TypeInfo tpm_tis_info = {
1012edff8678SStefan Berger     .name = TYPE_TPM_TIS,
1013edff8678SStefan Berger     .parent = TYPE_ISA_DEVICE,
1014edff8678SStefan Berger     .instance_size = sizeof(TPMState),
1015edff8678SStefan Berger     .instance_init = tpm_tis_initfn,
1016edff8678SStefan Berger     .class_init  = tpm_tis_class_init,
1017698f5daaSMarc-André Lureau     .interfaces = (InterfaceInfo[]) {
1018698f5daaSMarc-André Lureau         { TYPE_TPM_IF },
1019698f5daaSMarc-André Lureau         { }
1020698f5daaSMarc-André Lureau     }
1021edff8678SStefan Berger };
1022edff8678SStefan Berger 
1023edff8678SStefan Berger static void tpm_tis_register(void)
1024edff8678SStefan Berger {
1025edff8678SStefan Berger     type_register_static(&tpm_tis_info);
1026edff8678SStefan Berger }
1027edff8678SStefan Berger 
1028edff8678SStefan Berger type_init(tpm_tis_register)
1029