]> git.proxmox.com Git - mirror_qemu.git/blame - hw/tpm/tpm_tis.c
vl: remove unnecessary #ifdef CONFIG_TPM
[mirror_qemu.git] / hw / tpm / tpm_tis.c
CommitLineData
edff8678
SB
1/*
2 * tpm_tis.c - QEMU's TPM TIS interface emulator
3 *
4 * Copyright (C) 2006,2010-2013 IBM Corporation
5 *
6 * Authors:
7 * Stefan Berger <stefanb@us.ibm.com>
8 * David Safford <safford@us.ibm.com>
9 *
10 * Xen 4 support: Andrease Niederl <andreas.niederl@iaik.tugraz.at>
11 *
12 * This work is licensed under the terms of the GNU GPL, version 2 or later.
13 * See the COPYING file in the top-level directory.
14 *
15 * Implementation of the TIS interface according to specs found at
16 * http://www.trustedcomputinggroup.org. This implementation currently
9dd5c40d 17 * supports version 1.3, 21 March 2013
edff8678
SB
18 * In the developers menu choose the PC Client section then find the TIS
19 * specification.
116694c3
SB
20 *
21 * TPM TIS for TPM 2 implementation following TCG PC Client Platform
22 * TPM Profile (PTP) Specification, Familiy 2.0, Revision 00.43
edff8678
SB
23 */
24
0430891c 25#include "qemu/osdep.h"
732cd587 26#include "hw/isa/isa.h"
dccfcd0e 27#include "sysemu/tpm_backend.h"
edff8678 28#include "tpm_int.h"
4be74634 29#include "sysemu/block-backend.h"
edff8678
SB
30#include "exec/address-spaces.h"
31#include "hw/hw.h"
0d09e41a 32#include "hw/i386/pc.h"
edff8678 33#include "hw/pci/pci_ids.h"
da34e65c 34#include "qapi/error.h"
edff8678 35#include "qemu-common.h"
6a1751b7 36#include "qemu/main-loop.h"
732cd587
MAL
37#include "hw/acpi/tpm.h"
38
39#define TPM_TIS_NUM_LOCALITIES 5 /* per spec */
40#define TPM_TIS_LOCALITY_SHIFT 12
41#define TPM_TIS_NO_LOCALITY 0xff
42
43#define TPM_TIS_IS_VALID_LOCTY(x) ((x) < TPM_TIS_NUM_LOCALITIES)
44
45#define TPM_TIS_BUFFER_MAX 4096
46
47typedef enum {
48 TPM_TIS_STATE_IDLE = 0,
49 TPM_TIS_STATE_READY,
50 TPM_TIS_STATE_COMPLETION,
51 TPM_TIS_STATE_EXECUTION,
52 TPM_TIS_STATE_RECEPTION,
53} TPMTISState;
54
55typedef struct TPMSizedBuffer {
56 uint32_t size;
57 uint8_t *buffer;
58} TPMSizedBuffer;
59
60/* locality data -- all fields are persisted */
61typedef struct TPMLocality {
62 TPMTISState state;
63 uint8_t access;
64 uint32_t sts;
65 uint32_t iface_id;
66 uint32_t inte;
67 uint32_t ints;
68
69 uint16_t w_offset;
70 uint16_t r_offset;
71 TPMSizedBuffer w_buffer;
72 TPMSizedBuffer r_buffer;
73} TPMLocality;
74
3d4960c7
MAL
75struct TPMState {
76 ISADevice busdev;
77 MemoryRegion mmio;
78
732cd587
MAL
79 QEMUBH *bh;
80 uint32_t offset;
81 uint8_t buf[TPM_TIS_BUFFER_MAX];
82
83 uint8_t active_locty;
84 uint8_t aborting_locty;
85 uint8_t next_locty;
86
87 TPMLocality loc[TPM_TIS_NUM_LOCALITIES];
88
89 qemu_irq irq;
90 uint32_t irq_num;
732cd587
MAL
91
92 uint8_t locty_number;
93 TPMBackendCmd cmd;
94
95 char *backend;
96 TPMBackend *be_driver;
97 TPMVersion be_tpm_version;
98};
99
100#define TPM(obj) OBJECT_CHECK(TPMState, (obj), TYPE_TPM_TIS)
edff8678 101
4d1ba9c4 102#define DEBUG_TIS 0
edff8678 103
4d1ba9c4
SB
104#define DPRINTF(fmt, ...) do { \
105 if (DEBUG_TIS) { \
106 printf(fmt, ## __VA_ARGS__); \
107 } \
108} while (0);
edff8678 109
edff8678
SB
110/* tis registers */
111#define TPM_TIS_REG_ACCESS 0x00
112#define TPM_TIS_REG_INT_ENABLE 0x08
113#define TPM_TIS_REG_INT_VECTOR 0x0c
114#define TPM_TIS_REG_INT_STATUS 0x10
115#define TPM_TIS_REG_INTF_CAPABILITY 0x14
116#define TPM_TIS_REG_STS 0x18
117#define TPM_TIS_REG_DATA_FIFO 0x24
116694c3 118#define TPM_TIS_REG_INTERFACE_ID 0x30
2eae8c75
SB
119#define TPM_TIS_REG_DATA_XFIFO 0x80
120#define TPM_TIS_REG_DATA_XFIFO_END 0xbc
edff8678
SB
121#define TPM_TIS_REG_DID_VID 0xf00
122#define TPM_TIS_REG_RID 0xf04
123
8db7c415
SB
124/* vendor-specific registers */
125#define TPM_TIS_REG_DEBUG 0xf90
126
116694c3
SB
127#define TPM_TIS_STS_TPM_FAMILY_MASK (0x3 << 26)/* TPM 2.0 */
128#define TPM_TIS_STS_TPM_FAMILY1_2 (0 << 26) /* TPM 2.0 */
129#define TPM_TIS_STS_TPM_FAMILY2_0 (1 << 26) /* TPM 2.0 */
130#define TPM_TIS_STS_RESET_ESTABLISHMENT_BIT (1 << 25) /* TPM 2.0 */
131#define TPM_TIS_STS_COMMAND_CANCEL (1 << 24) /* TPM 2.0 */
132
edff8678
SB
133#define TPM_TIS_STS_VALID (1 << 7)
134#define TPM_TIS_STS_COMMAND_READY (1 << 6)
135#define TPM_TIS_STS_TPM_GO (1 << 5)
136#define TPM_TIS_STS_DATA_AVAILABLE (1 << 4)
137#define TPM_TIS_STS_EXPECT (1 << 3)
fd859081 138#define TPM_TIS_STS_SELFTEST_DONE (1 << 2)
edff8678
SB
139#define TPM_TIS_STS_RESPONSE_RETRY (1 << 1)
140
141#define TPM_TIS_BURST_COUNT_SHIFT 8
142#define TPM_TIS_BURST_COUNT(X) \
143 ((X) << TPM_TIS_BURST_COUNT_SHIFT)
144
145#define TPM_TIS_ACCESS_TPM_REG_VALID_STS (1 << 7)
146#define TPM_TIS_ACCESS_ACTIVE_LOCALITY (1 << 5)
147#define TPM_TIS_ACCESS_BEEN_SEIZED (1 << 4)
148#define TPM_TIS_ACCESS_SEIZE (1 << 3)
149#define TPM_TIS_ACCESS_PENDING_REQUEST (1 << 2)
150#define TPM_TIS_ACCESS_REQUEST_USE (1 << 1)
151#define TPM_TIS_ACCESS_TPM_ESTABLISHMENT (1 << 0)
152
153#define TPM_TIS_INT_ENABLED (1 << 31)
154#define TPM_TIS_INT_DATA_AVAILABLE (1 << 0)
155#define TPM_TIS_INT_STS_VALID (1 << 1)
156#define TPM_TIS_INT_LOCALITY_CHANGED (1 << 2)
157#define TPM_TIS_INT_COMMAND_READY (1 << 7)
158
159#define TPM_TIS_INT_POLARITY_MASK (3 << 3)
160#define TPM_TIS_INT_POLARITY_LOW_LEVEL (1 << 3)
161
edff8678
SB
162#define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \
163 TPM_TIS_INT_DATA_AVAILABLE | \
164 TPM_TIS_INT_STS_VALID | \
165 TPM_TIS_INT_COMMAND_READY)
166
9dd5c40d 167#define TPM_TIS_CAP_INTERFACE_VERSION1_3 (2 << 28)
116694c3 168#define TPM_TIS_CAP_INTERFACE_VERSION1_3_FOR_TPM2_0 (3 << 28)
9dd5c40d
SB
169#define TPM_TIS_CAP_DATA_TRANSFER_64B (3 << 9)
170#define TPM_TIS_CAP_DATA_TRANSFER_LEGACY (0 << 9)
171#define TPM_TIS_CAP_BURST_COUNT_DYNAMIC (0 << 8)
edff8678 172#define TPM_TIS_CAP_INTERRUPT_LOW_LEVEL (1 << 4) /* support is mandatory */
116694c3
SB
173#define TPM_TIS_CAPABILITIES_SUPPORTED1_3 \
174 (TPM_TIS_CAP_INTERRUPT_LOW_LEVEL | \
175 TPM_TIS_CAP_BURST_COUNT_DYNAMIC | \
176 TPM_TIS_CAP_DATA_TRANSFER_64B | \
177 TPM_TIS_CAP_INTERFACE_VERSION1_3 | \
178 TPM_TIS_INTERRUPTS_SUPPORTED)
179
180#define TPM_TIS_CAPABILITIES_SUPPORTED2_0 \
181 (TPM_TIS_CAP_INTERRUPT_LOW_LEVEL | \
182 TPM_TIS_CAP_BURST_COUNT_DYNAMIC | \
183 TPM_TIS_CAP_DATA_TRANSFER_64B | \
184 TPM_TIS_CAP_INTERFACE_VERSION1_3_FOR_TPM2_0 | \
185 TPM_TIS_INTERRUPTS_SUPPORTED)
186
187#define TPM_TIS_IFACE_ID_INTERFACE_TIS1_3 (0xf) /* TPM 2.0 */
188#define TPM_TIS_IFACE_ID_INTERFACE_FIFO (0x0) /* TPM 2.0 */
189#define TPM_TIS_IFACE_ID_INTERFACE_VER_FIFO (0 << 4) /* TPM 2.0 */
190#define TPM_TIS_IFACE_ID_CAP_5_LOCALITIES (1 << 8) /* TPM 2.0 */
191#define TPM_TIS_IFACE_ID_CAP_TIS_SUPPORTED (1 << 13) /* TPM 2.0 */
192#define TPM_TIS_IFACE_ID_INT_SEL_LOCK (1 << 19) /* TPM 2.0 */
193
194#define TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3 \
195 (TPM_TIS_IFACE_ID_INTERFACE_TIS1_3 | \
886ce6f8 196 (~0u << 4)/* all of it is don't care */)
116694c3
SB
197
198/* if backend was a TPM 2.0: */
199#define TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0 \
200 (TPM_TIS_IFACE_ID_INTERFACE_FIFO | \
201 TPM_TIS_IFACE_ID_INTERFACE_VER_FIFO | \
202 TPM_TIS_IFACE_ID_CAP_5_LOCALITIES | \
203 TPM_TIS_IFACE_ID_CAP_TIS_SUPPORTED)
edff8678
SB
204
205#define TPM_TIS_TPM_DID 0x0001
206#define TPM_TIS_TPM_VID PCI_VENDOR_ID_IBM
207#define TPM_TIS_TPM_RID 0x0001
208
209#define TPM_TIS_NO_DATA_BYTE 0xff
210
8db7c415
SB
211/* local prototypes */
212
213static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr,
214 unsigned size);
215
edff8678
SB
216/* utility functions */
217
218static uint8_t tpm_tis_locality_from_addr(hwaddr addr)
219{
220 return (uint8_t)((addr >> TPM_TIS_LOCALITY_SHIFT) & 0x7);
221}
222
223static uint32_t tpm_tis_get_size_from_buffer(const TPMSizedBuffer *sb)
224{
225 return be32_to_cpu(*(uint32_t *)&sb->buffer[2]);
226}
227
228static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string)
229{
230#ifdef DEBUG_TIS
231 uint32_t len, i;
232
233 len = tpm_tis_get_size_from_buffer(sb);
234 DPRINTF("tpm_tis: %s length = %d\n", string, len);
235 for (i = 0; i < len; i++) {
236 if (i && !(i % 16)) {
237 DPRINTF("\n");
238 }
239 DPRINTF("%.2X ", sb->buffer[i]);
240 }
241 DPRINTF("\n");
242#endif
243}
244
fd859081
SB
245/*
246 * Set the given flags in the STS register by clearing the register but
116694c3
SB
247 * preserving the SELFTEST_DONE and TPM_FAMILY_MASK flags and then setting
248 * the new flags.
fd859081
SB
249 *
250 * The SELFTEST_DONE flag is acquired from the backend that determines it by
251 * peeking into TPM commands.
252 *
253 * A VM suspend/resume will preserve the flag by storing it into the VM
254 * device state, but the backend will not remember it when QEMU is started
255 * again. Therefore, we cache the flag here. Once set, it will not be unset
256 * except by a reset.
257 */
258static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags)
259{
116694c3 260 l->sts &= TPM_TIS_STS_SELFTEST_DONE | TPM_TIS_STS_TPM_FAMILY_MASK;
fd859081
SB
261 l->sts |= flags;
262}
263
edff8678
SB
264/*
265 * Send a request to the TPM.
266 */
267static void tpm_tis_tpm_send(TPMState *s, uint8_t locty)
268{
3d4960c7 269 TPMLocality *locty_data = &s->loc[locty];
edff8678 270
3d4960c7 271 tpm_tis_show_buffer(&s->loc[locty].w_buffer, "tpm_tis: To TPM");
edff8678 272
edff8678
SB
273 /*
274 * w_offset serves as length indicator for length of data;
275 * it's reset when the response comes back
276 */
3d4960c7 277 s->loc[locty].state = TPM_TIS_STATE_EXECUTION;
edff8678 278
0e43b7e6
MAL
279 s->cmd = (TPMBackendCmd) {
280 .locty = locty,
d2809766
MAL
281 .in = locty_data->w_buffer.buffer,
282 .in_len = locty_data->w_offset,
283 .out = locty_data->r_buffer.buffer,
284 .out_len = locty_data->r_buffer.size
0e43b7e6
MAL
285 };
286
287 tpm_backend_deliver_request(s->be_driver, &s->cmd);
edff8678
SB
288}
289
290/* raise an interrupt if allowed */
291static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask)
292{
edff8678
SB
293 if (!TPM_TIS_IS_VALID_LOCTY(locty)) {
294 return;
295 }
296
3d4960c7
MAL
297 if ((s->loc[locty].inte & TPM_TIS_INT_ENABLED) &&
298 (s->loc[locty].inte & irqmask)) {
edff8678 299 DPRINTF("tpm_tis: Raising IRQ for flag %08x\n", irqmask);
3d4960c7
MAL
300 qemu_irq_raise(s->irq);
301 s->loc[locty].ints |= irqmask;
edff8678
SB
302 }
303}
304
305static uint32_t tpm_tis_check_request_use_except(TPMState *s, uint8_t locty)
306{
307 uint8_t l;
308
309 for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
310 if (l == locty) {
311 continue;
312 }
3d4960c7 313 if ((s->loc[l].access & TPM_TIS_ACCESS_REQUEST_USE)) {
edff8678
SB
314 return 1;
315 }
316 }
317
318 return 0;
319}
320
321static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty)
322{
3d4960c7 323 bool change = (s->active_locty != new_active_locty);
edff8678
SB
324 bool is_seize;
325 uint8_t mask;
326
3d4960c7 327 if (change && TPM_TIS_IS_VALID_LOCTY(s->active_locty)) {
edff8678 328 is_seize = TPM_TIS_IS_VALID_LOCTY(new_active_locty) &&
3d4960c7 329 s->loc[new_active_locty].access & TPM_TIS_ACCESS_SEIZE;
edff8678
SB
330
331 if (is_seize) {
332 mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY);
333 } else {
334 mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY|
335 TPM_TIS_ACCESS_REQUEST_USE);
336 }
337 /* reset flags on the old active locality */
3d4960c7 338 s->loc[s->active_locty].access &= mask;
edff8678
SB
339
340 if (is_seize) {
3d4960c7 341 s->loc[s->active_locty].access |= TPM_TIS_ACCESS_BEEN_SEIZED;
edff8678
SB
342 }
343 }
344
3d4960c7 345 s->active_locty = new_active_locty;
edff8678 346
3d4960c7 347 DPRINTF("tpm_tis: Active locality is now %d\n", s->active_locty);
edff8678
SB
348
349 if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) {
350 /* set flags on the new active locality */
3d4960c7
MAL
351 s->loc[new_active_locty].access |= TPM_TIS_ACCESS_ACTIVE_LOCALITY;
352 s->loc[new_active_locty].access &= ~(TPM_TIS_ACCESS_REQUEST_USE |
edff8678
SB
353 TPM_TIS_ACCESS_SEIZE);
354 }
355
356 if (change) {
3d4960c7 357 tpm_tis_raise_irq(s, s->active_locty, TPM_TIS_INT_LOCALITY_CHANGED);
edff8678
SB
358 }
359}
360
361/* abort -- this function switches the locality */
362static void tpm_tis_abort(TPMState *s, uint8_t locty)
363{
3d4960c7
MAL
364 s->loc[locty].r_offset = 0;
365 s->loc[locty].w_offset = 0;
edff8678 366
3d4960c7 367 DPRINTF("tpm_tis: tis_abort: new active locality is %d\n", s->next_locty);
edff8678
SB
368
369 /*
370 * Need to react differently depending on who's aborting now and
371 * which locality will become active afterwards.
372 */
3d4960c7
MAL
373 if (s->aborting_locty == s->next_locty) {
374 s->loc[s->aborting_locty].state = TPM_TIS_STATE_READY;
375 tpm_tis_sts_set(&s->loc[s->aborting_locty],
fd859081 376 TPM_TIS_STS_COMMAND_READY);
3d4960c7 377 tpm_tis_raise_irq(s, s->aborting_locty, TPM_TIS_INT_COMMAND_READY);
edff8678
SB
378 }
379
380 /* locality after abort is another one than the current one */
3d4960c7 381 tpm_tis_new_active_locality(s, s->next_locty);
edff8678 382
3d4960c7 383 s->next_locty = TPM_TIS_NO_LOCALITY;
edff8678 384 /* nobody's aborting a command anymore */
3d4960c7 385 s->aborting_locty = TPM_TIS_NO_LOCALITY;
edff8678
SB
386}
387
388/* prepare aborting current command */
389static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty)
390{
edff8678
SB
391 uint8_t busy_locty;
392
3d4960c7
MAL
393 s->aborting_locty = locty;
394 s->next_locty = newlocty; /* locality after successful abort */
edff8678
SB
395
396 /*
397 * only abort a command using an interrupt if currently executing
398 * a command AND if there's a valid connection to the vTPM.
399 */
400 for (busy_locty = 0; busy_locty < TPM_TIS_NUM_LOCALITIES; busy_locty++) {
3d4960c7 401 if (s->loc[busy_locty].state == TPM_TIS_STATE_EXECUTION) {
edff8678
SB
402 /*
403 * request the backend to cancel. Some backends may not
404 * support it
405 */
8f0605cc 406 tpm_backend_cancel_cmd(s->be_driver);
edff8678
SB
407 return;
408 }
409 }
410
411 tpm_tis_abort(s, locty);
412}
413
414static void tpm_tis_receive_bh(void *opaque)
415{
416 TPMState *s = opaque;
0e43b7e6 417 uint8_t locty = s->cmd.locty;
edff8678 418
3d4960c7 419 tpm_tis_sts_set(&s->loc[locty],
fd859081 420 TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE);
3d4960c7
MAL
421 s->loc[locty].state = TPM_TIS_STATE_COMPLETION;
422 s->loc[locty].r_offset = 0;
423 s->loc[locty].w_offset = 0;
edff8678 424
3d4960c7 425 if (TPM_TIS_IS_VALID_LOCTY(s->next_locty)) {
edff8678
SB
426 tpm_tis_abort(s, locty);
427 }
428
edff8678
SB
429 tpm_tis_raise_irq(s, locty,
430 TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID);
edff8678
SB
431}
432
05a69998 433static void tpm_tis_request_completed(TPMIf *ti)
edff8678 434{
05a69998
MAL
435 TPMState *s = TPM(ti);
436
0e43b7e6
MAL
437 bool is_selftest_done = s->cmd.selftest_done;
438 uint8_t locty = s->cmd.locty;
fd859081 439 uint8_t l;
edff8678 440
fd859081
SB
441 if (is_selftest_done) {
442 for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
3d4960c7 443 s->loc[locty].sts |= TPM_TIS_STS_SELFTEST_DONE;
fd859081
SB
444 }
445 }
446
3d4960c7 447 qemu_bh_schedule(s->bh);
edff8678
SB
448}
449
450/*
451 * Read a byte of response data
452 */
453static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty)
454{
edff8678
SB
455 uint32_t ret = TPM_TIS_NO_DATA_BYTE;
456 uint16_t len;
457
3d4960c7
MAL
458 if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
459 len = tpm_tis_get_size_from_buffer(&s->loc[locty].r_buffer);
edff8678 460
3d4960c7
MAL
461 ret = s->loc[locty].r_buffer.buffer[s->loc[locty].r_offset++];
462 if (s->loc[locty].r_offset >= len) {
edff8678 463 /* got last byte */
3d4960c7 464 tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID);
edff8678 465 tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
edff8678
SB
466 }
467 DPRINTF("tpm_tis: tpm_tis_data_read byte 0x%02x [%d]\n",
3d4960c7 468 ret, s->loc[locty].r_offset - 1);
edff8678
SB
469 }
470
471 return ret;
472}
473
8db7c415
SB
474#ifdef DEBUG_TIS
475static void tpm_tis_dump_state(void *opaque, hwaddr addr)
476{
477 static const unsigned regs[] = {
478 TPM_TIS_REG_ACCESS,
479 TPM_TIS_REG_INT_ENABLE,
480 TPM_TIS_REG_INT_VECTOR,
481 TPM_TIS_REG_INT_STATUS,
482 TPM_TIS_REG_INTF_CAPABILITY,
483 TPM_TIS_REG_STS,
484 TPM_TIS_REG_DID_VID,
485 TPM_TIS_REG_RID,
486 0xfff};
487 int idx;
488 uint8_t locty = tpm_tis_locality_from_addr(addr);
489 hwaddr base = addr & ~0xfff;
490 TPMState *s = opaque;
8db7c415
SB
491
492 DPRINTF("tpm_tis: active locality : %d\n"
493 "tpm_tis: state of locality %d : %d\n"
494 "tpm_tis: register dump:\n",
3d4960c7
MAL
495 s->active_locty,
496 locty, s->loc[locty].state);
8db7c415
SB
497
498 for (idx = 0; regs[idx] != 0xfff; idx++) {
499 DPRINTF("tpm_tis: 0x%04x : 0x%08x\n", regs[idx],
070c7607 500 (int)tpm_tis_mmio_read(opaque, base + regs[idx], 4));
8db7c415
SB
501 }
502
503 DPRINTF("tpm_tis: read offset : %d\n"
504 "tpm_tis: result buffer : ",
3d4960c7 505 s->loc[locty].r_offset);
8db7c415 506 for (idx = 0;
3d4960c7 507 idx < tpm_tis_get_size_from_buffer(&s->loc[locty].r_buffer);
8db7c415
SB
508 idx++) {
509 DPRINTF("%c%02x%s",
3d4960c7
MAL
510 s->loc[locty].r_offset == idx ? '>' : ' ',
511 s->loc[locty].r_buffer.buffer[idx],
8db7c415
SB
512 ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : "");
513 }
514 DPRINTF("\n"
515 "tpm_tis: write offset : %d\n"
516 "tpm_tis: request buffer: ",
3d4960c7 517 s->loc[locty].w_offset);
8db7c415 518 for (idx = 0;
3d4960c7 519 idx < tpm_tis_get_size_from_buffer(&s->loc[locty].w_buffer);
8db7c415
SB
520 idx++) {
521 DPRINTF("%c%02x%s",
3d4960c7
MAL
522 s->loc[locty].w_offset == idx ? '>' : ' ',
523 s->loc[locty].w_buffer.buffer[idx],
8db7c415
SB
524 ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : "");
525 }
526 DPRINTF("\n");
527}
528#endif
529
edff8678
SB
530/*
531 * Read a register of the TIS interface
532 * See specs pages 33-63 for description of the registers
533 */
534static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr,
535 unsigned size)
536{
537 TPMState *s = opaque;
edff8678
SB
538 uint16_t offset = addr & 0xffc;
539 uint8_t shift = (addr & 0x3) * 8;
540 uint32_t val = 0xffffffff;
541 uint8_t locty = tpm_tis_locality_from_addr(addr);
542 uint32_t avail;
feeb755f 543 uint8_t v;
edff8678 544
8f0605cc 545 if (tpm_backend_had_startup_error(s->be_driver)) {
edff8678
SB
546 return val;
547 }
548
549 switch (offset) {
550 case TPM_TIS_REG_ACCESS:
551 /* never show the SEIZE flag even though we use it internally */
3d4960c7 552 val = s->loc[locty].access & ~TPM_TIS_ACCESS_SEIZE;
edff8678
SB
553 /* the pending flag is always calculated */
554 if (tpm_tis_check_request_use_except(s, locty)) {
555 val |= TPM_TIS_ACCESS_PENDING_REQUEST;
556 }
8f0605cc 557 val |= !tpm_backend_get_tpm_established_flag(s->be_driver);
edff8678
SB
558 break;
559 case TPM_TIS_REG_INT_ENABLE:
3d4960c7 560 val = s->loc[locty].inte;
edff8678
SB
561 break;
562 case TPM_TIS_REG_INT_VECTOR:
3d4960c7 563 val = s->irq_num;
edff8678
SB
564 break;
565 case TPM_TIS_REG_INT_STATUS:
3d4960c7 566 val = s->loc[locty].ints;
edff8678
SB
567 break;
568 case TPM_TIS_REG_INTF_CAPABILITY:
116694c3
SB
569 switch (s->be_tpm_version) {
570 case TPM_VERSION_UNSPEC:
571 val = 0;
572 break;
573 case TPM_VERSION_1_2:
574 val = TPM_TIS_CAPABILITIES_SUPPORTED1_3;
575 break;
576 case TPM_VERSION_2_0:
577 val = TPM_TIS_CAPABILITIES_SUPPORTED2_0;
578 break;
579 }
edff8678
SB
580 break;
581 case TPM_TIS_REG_STS:
3d4960c7
MAL
582 if (s->active_locty == locty) {
583 if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
edff8678 584 val = TPM_TIS_BURST_COUNT(
3d4960c7
MAL
585 tpm_tis_get_size_from_buffer(&s->loc[locty].r_buffer)
586 - s->loc[locty].r_offset) | s->loc[locty].sts;
edff8678 587 } else {
3d4960c7
MAL
588 avail = s->loc[locty].w_buffer.size
589 - s->loc[locty].w_offset;
edff8678
SB
590 /*
591 * byte-sized reads should not return 0x00 for 0x100
592 * available bytes.
593 */
594 if (size == 1 && avail > 0xff) {
595 avail = 0xff;
596 }
3d4960c7 597 val = TPM_TIS_BURST_COUNT(avail) | s->loc[locty].sts;
edff8678
SB
598 }
599 }
600 break;
601 case TPM_TIS_REG_DATA_FIFO:
2eae8c75 602 case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END:
3d4960c7 603 if (s->active_locty == locty) {
feeb755f
SB
604 if (size > 4 - (addr & 0x3)) {
605 /* prevent access beyond FIFO */
606 size = 4 - (addr & 0x3);
607 }
608 val = 0;
609 shift = 0;
610 while (size > 0) {
3d4960c7 611 switch (s->loc[locty].state) {
feeb755f
SB
612 case TPM_TIS_STATE_COMPLETION:
613 v = tpm_tis_data_read(s, locty);
614 break;
615 default:
616 v = TPM_TIS_NO_DATA_BYTE;
617 break;
618 }
619 val |= (v << shift);
620 shift += 8;
621 size--;
edff8678 622 }
feeb755f 623 shift = 0; /* no more adjustments */
edff8678
SB
624 }
625 break;
116694c3 626 case TPM_TIS_REG_INTERFACE_ID:
3d4960c7 627 val = s->loc[locty].iface_id;
116694c3 628 break;
edff8678
SB
629 case TPM_TIS_REG_DID_VID:
630 val = (TPM_TIS_TPM_DID << 16) | TPM_TIS_TPM_VID;
631 break;
632 case TPM_TIS_REG_RID:
633 val = TPM_TIS_TPM_RID;
634 break;
8db7c415
SB
635#ifdef DEBUG_TIS
636 case TPM_TIS_REG_DEBUG:
637 tpm_tis_dump_state(opaque, addr);
638 break;
639#endif
edff8678
SB
640 }
641
642 if (shift) {
643 val >>= shift;
644 }
645
070c7607 646 DPRINTF("tpm_tis: read.%u(%08x) = %08x\n", size, (int)addr, (int)val);
edff8678
SB
647
648 return val;
649}
650
651/*
652 * Write a value to a register of the TIS interface
653 * See specs pages 33-63 for description of the registers
654 */
ff2bc0c1
MAL
655static void tpm_tis_mmio_write(void *opaque, hwaddr addr,
656 uint64_t val, unsigned size)
edff8678
SB
657{
658 TPMState *s = opaque;
feeb755f
SB
659 uint16_t off = addr & 0xffc;
660 uint8_t shift = (addr & 0x3) * 8;
edff8678
SB
661 uint8_t locty = tpm_tis_locality_from_addr(addr);
662 uint8_t active_locty, l;
663 int c, set_new_locty = 1;
664 uint16_t len;
feeb755f 665 uint32_t mask = (size == 1) ? 0xff : ((size == 2) ? 0xffff : ~0);
edff8678 666
070c7607 667 DPRINTF("tpm_tis: write.%u(%08x) = %08x\n", size, (int)addr, (int)val);
edff8678 668
ff2bc0c1 669 if (locty == 4) {
edff8678
SB
670 DPRINTF("tpm_tis: Access to locality 4 only allowed from hardware\n");
671 return;
672 }
673
8f0605cc 674 if (tpm_backend_had_startup_error(s->be_driver)) {
edff8678
SB
675 return;
676 }
677
feeb755f
SB
678 val &= mask;
679
680 if (shift) {
681 val <<= shift;
682 mask <<= shift;
683 }
684
685 mask ^= 0xffffffff;
686
edff8678
SB
687 switch (off) {
688 case TPM_TIS_REG_ACCESS:
689
690 if ((val & TPM_TIS_ACCESS_SEIZE)) {
691 val &= ~(TPM_TIS_ACCESS_REQUEST_USE |
692 TPM_TIS_ACCESS_ACTIVE_LOCALITY);
693 }
694
3d4960c7 695 active_locty = s->active_locty;
edff8678
SB
696
697 if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) {
698 /* give up locality if currently owned */
3d4960c7 699 if (s->active_locty == locty) {
edff8678
SB
700 DPRINTF("tpm_tis: Releasing locality %d\n", locty);
701
702 uint8_t newlocty = TPM_TIS_NO_LOCALITY;
703 /* anybody wants the locality ? */
704 for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) {
3d4960c7 705 if ((s->loc[c].access & TPM_TIS_ACCESS_REQUEST_USE)) {
edff8678
SB
706 DPRINTF("tpm_tis: Locality %d requests use.\n", c);
707 newlocty = c;
708 break;
709 }
710 }
711 DPRINTF("tpm_tis: TPM_TIS_ACCESS_ACTIVE_LOCALITY: "
712 "Next active locality: %d\n",
713 newlocty);
714
715 if (TPM_TIS_IS_VALID_LOCTY(newlocty)) {
716 set_new_locty = 0;
717 tpm_tis_prep_abort(s, locty, newlocty);
718 } else {
719 active_locty = TPM_TIS_NO_LOCALITY;
720 }
721 } else {
722 /* not currently the owner; clear a pending request */
3d4960c7 723 s->loc[locty].access &= ~TPM_TIS_ACCESS_REQUEST_USE;
edff8678
SB
724 }
725 }
726
727 if ((val & TPM_TIS_ACCESS_BEEN_SEIZED)) {
3d4960c7 728 s->loc[locty].access &= ~TPM_TIS_ACCESS_BEEN_SEIZED;
edff8678
SB
729 }
730
731 if ((val & TPM_TIS_ACCESS_SEIZE)) {
732 /*
733 * allow seize if a locality is active and the requesting
734 * locality is higher than the one that's active
735 * OR
736 * allow seize for requesting locality if no locality is
737 * active
738 */
3d4960c7
MAL
739 while ((TPM_TIS_IS_VALID_LOCTY(s->active_locty) &&
740 locty > s->active_locty) ||
741 !TPM_TIS_IS_VALID_LOCTY(s->active_locty)) {
edff8678
SB
742 bool higher_seize = FALSE;
743
744 /* already a pending SEIZE ? */
3d4960c7 745 if ((s->loc[locty].access & TPM_TIS_ACCESS_SEIZE)) {
edff8678
SB
746 break;
747 }
748
749 /* check for ongoing seize by a higher locality */
750 for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES; l++) {
3d4960c7 751 if ((s->loc[l].access & TPM_TIS_ACCESS_SEIZE)) {
edff8678
SB
752 higher_seize = TRUE;
753 break;
754 }
755 }
756
757 if (higher_seize) {
758 break;
759 }
760
761 /* cancel any seize by a lower locality */
762 for (l = 0; l < locty - 1; l++) {
3d4960c7 763 s->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE;
edff8678
SB
764 }
765
3d4960c7 766 s->loc[locty].access |= TPM_TIS_ACCESS_SEIZE;
edff8678
SB
767 DPRINTF("tpm_tis: TPM_TIS_ACCESS_SEIZE: "
768 "Locality %d seized from locality %d\n",
3d4960c7 769 locty, s->active_locty);
edff8678
SB
770 DPRINTF("tpm_tis: TPM_TIS_ACCESS_SEIZE: Initiating abort.\n");
771 set_new_locty = 0;
3d4960c7 772 tpm_tis_prep_abort(s, s->active_locty, locty);
edff8678
SB
773 break;
774 }
775 }
776
777 if ((val & TPM_TIS_ACCESS_REQUEST_USE)) {
3d4960c7
MAL
778 if (s->active_locty != locty) {
779 if (TPM_TIS_IS_VALID_LOCTY(s->active_locty)) {
780 s->loc[locty].access |= TPM_TIS_ACCESS_REQUEST_USE;
edff8678
SB
781 } else {
782 /* no locality active -> make this one active now */
783 active_locty = locty;
784 }
785 }
786 }
787
788 if (set_new_locty) {
789 tpm_tis_new_active_locality(s, active_locty);
790 }
791
792 break;
793 case TPM_TIS_REG_INT_ENABLE:
3d4960c7 794 if (s->active_locty != locty) {
edff8678
SB
795 break;
796 }
797
3d4960c7
MAL
798 s->loc[locty].inte &= mask;
799 s->loc[locty].inte |= (val & (TPM_TIS_INT_ENABLED |
feeb755f
SB
800 TPM_TIS_INT_POLARITY_MASK |
801 TPM_TIS_INTERRUPTS_SUPPORTED));
edff8678
SB
802 break;
803 case TPM_TIS_REG_INT_VECTOR:
804 /* hard wired -- ignore */
805 break;
806 case TPM_TIS_REG_INT_STATUS:
3d4960c7 807 if (s->active_locty != locty) {
edff8678
SB
808 break;
809 }
810
811 /* clearing of interrupt flags */
812 if (((val & TPM_TIS_INTERRUPTS_SUPPORTED)) &&
3d4960c7
MAL
813 (s->loc[locty].ints & TPM_TIS_INTERRUPTS_SUPPORTED)) {
814 s->loc[locty].ints &= ~val;
815 if (s->loc[locty].ints == 0) {
816 qemu_irq_lower(s->irq);
edff8678
SB
817 DPRINTF("tpm_tis: Lowering IRQ\n");
818 }
819 }
3d4960c7 820 s->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED);
edff8678
SB
821 break;
822 case TPM_TIS_REG_STS:
3d4960c7 823 if (s->active_locty != locty) {
edff8678
SB
824 break;
825 }
826
116694c3
SB
827 if (s->be_tpm_version == TPM_VERSION_2_0) {
828 /* some flags that are only supported for TPM 2 */
829 if (val & TPM_TIS_STS_COMMAND_CANCEL) {
3d4960c7 830 if (s->loc[locty].state == TPM_TIS_STATE_EXECUTION) {
116694c3
SB
831 /*
832 * request the backend to cancel. Some backends may not
833 * support it
834 */
835 tpm_backend_cancel_cmd(s->be_driver);
836 }
837 }
838
839 if (val & TPM_TIS_STS_RESET_ESTABLISHMENT_BIT) {
840 if (locty == 3 || locty == 4) {
841 tpm_backend_reset_tpm_established_flag(s->be_driver, locty);
842 }
843 }
844 }
845
edff8678
SB
846 val &= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO |
847 TPM_TIS_STS_RESPONSE_RETRY);
848
849 if (val == TPM_TIS_STS_COMMAND_READY) {
3d4960c7 850 switch (s->loc[locty].state) {
edff8678
SB
851
852 case TPM_TIS_STATE_READY:
3d4960c7
MAL
853 s->loc[locty].w_offset = 0;
854 s->loc[locty].r_offset = 0;
edff8678
SB
855 break;
856
857 case TPM_TIS_STATE_IDLE:
3d4960c7
MAL
858 tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_COMMAND_READY);
859 s->loc[locty].state = TPM_TIS_STATE_READY;
edff8678
SB
860 tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
861 break;
862
863 case TPM_TIS_STATE_EXECUTION:
864 case TPM_TIS_STATE_RECEPTION:
865 /* abort currently running command */
866 DPRINTF("tpm_tis: %s: Initiating abort.\n",
867 __func__);
868 tpm_tis_prep_abort(s, locty, locty);
869 break;
870
871 case TPM_TIS_STATE_COMPLETION:
3d4960c7
MAL
872 s->loc[locty].w_offset = 0;
873 s->loc[locty].r_offset = 0;
edff8678 874 /* shortcut to ready state with C/R set */
3d4960c7
MAL
875 s->loc[locty].state = TPM_TIS_STATE_READY;
876 if (!(s->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) {
877 tpm_tis_sts_set(&s->loc[locty],
fd859081 878 TPM_TIS_STS_COMMAND_READY);
edff8678
SB
879 tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
880 }
3d4960c7 881 s->loc[locty].sts &= ~(TPM_TIS_STS_DATA_AVAILABLE);
edff8678
SB
882 break;
883
884 }
885 } else if (val == TPM_TIS_STS_TPM_GO) {
3d4960c7 886 switch (s->loc[locty].state) {
edff8678 887 case TPM_TIS_STATE_RECEPTION:
3d4960c7 888 if ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) == 0) {
edff8678
SB
889 tpm_tis_tpm_send(s, locty);
890 }
891 break;
892 default:
893 /* ignore */
894 break;
895 }
896 } else if (val == TPM_TIS_STS_RESPONSE_RETRY) {
3d4960c7 897 switch (s->loc[locty].state) {
edff8678 898 case TPM_TIS_STATE_COMPLETION:
3d4960c7
MAL
899 s->loc[locty].r_offset = 0;
900 tpm_tis_sts_set(&s->loc[locty],
fd859081
SB
901 TPM_TIS_STS_VALID|
902 TPM_TIS_STS_DATA_AVAILABLE);
edff8678
SB
903 break;
904 default:
905 /* ignore */
906 break;
907 }
908 }
909 break;
910 case TPM_TIS_REG_DATA_FIFO:
2eae8c75 911 case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END:
edff8678 912 /* data fifo */
3d4960c7 913 if (s->active_locty != locty) {
edff8678
SB
914 break;
915 }
916
3d4960c7
MAL
917 if (s->loc[locty].state == TPM_TIS_STATE_IDLE ||
918 s->loc[locty].state == TPM_TIS_STATE_EXECUTION ||
919 s->loc[locty].state == TPM_TIS_STATE_COMPLETION) {
edff8678
SB
920 /* drop the byte */
921 } else {
feeb755f 922 DPRINTF("tpm_tis: Data to send to TPM: %08x (size=%d)\n",
070c7607 923 (int)val, size);
3d4960c7
MAL
924 if (s->loc[locty].state == TPM_TIS_STATE_READY) {
925 s->loc[locty].state = TPM_TIS_STATE_RECEPTION;
926 tpm_tis_sts_set(&s->loc[locty],
fd859081 927 TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID);
edff8678
SB
928 }
929
feeb755f
SB
930 val >>= shift;
931 if (size > 4 - (addr & 0x3)) {
932 /* prevent access beyond FIFO */
933 size = 4 - (addr & 0x3);
934 }
935
3d4960c7
MAL
936 while ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) && size > 0) {
937 if (s->loc[locty].w_offset < s->loc[locty].w_buffer.size) {
938 s->loc[locty].w_buffer.
939 buffer[s->loc[locty].w_offset++] = (uint8_t)val;
feeb755f
SB
940 val >>= 8;
941 size--;
edff8678 942 } else {
3d4960c7 943 tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID);
edff8678
SB
944 }
945 }
946
947 /* check for complete packet */
3d4960c7
MAL
948 if (s->loc[locty].w_offset > 5 &&
949 (s->loc[locty].sts & TPM_TIS_STS_EXPECT)) {
edff8678 950 /* we have a packet length - see if we have all of it */
3d4960c7 951 bool need_irq = !(s->loc[locty].sts & TPM_TIS_STS_VALID);
d8383d61 952
3d4960c7
MAL
953 len = tpm_tis_get_size_from_buffer(&s->loc[locty].w_buffer);
954 if (len > s->loc[locty].w_offset) {
955 tpm_tis_sts_set(&s->loc[locty],
fd859081 956 TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID);
edff8678
SB
957 } else {
958 /* packet complete */
3d4960c7 959 tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID);
edff8678 960 }
29b558d8 961 if (need_irq) {
edff8678
SB
962 tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
963 }
edff8678
SB
964 }
965 }
966 break;
116694c3
SB
967 case TPM_TIS_REG_INTERFACE_ID:
968 if (val & TPM_TIS_IFACE_ID_INT_SEL_LOCK) {
969 for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
3d4960c7 970 s->loc[l].iface_id |= TPM_TIS_IFACE_ID_INT_SEL_LOCK;
116694c3
SB
971 }
972 }
973 break;
edff8678
SB
974 }
975}
976
edff8678
SB
977static const MemoryRegionOps tpm_tis_memory_ops = {
978 .read = tpm_tis_mmio_read,
979 .write = tpm_tis_mmio_write,
980 .endianness = DEVICE_LITTLE_ENDIAN,
981 .valid = {
982 .min_access_size = 1,
983 .max_access_size = 4,
984 },
985};
986
987static int tpm_tis_do_startup_tpm(TPMState *s)
988{
8f0605cc 989 return tpm_backend_startup_tpm(s->be_driver);
edff8678
SB
990}
991
d0c519bd
AV
992static void tpm_tis_realloc_buffer(TPMSizedBuffer *sb)
993{
994 size_t wanted_size = 4096; /* Linux tpm.c buffer size */
995
996 if (sb->size != wanted_size) {
997 sb->buffer = g_realloc(sb->buffer, wanted_size);
998 sb->size = wanted_size;
999 }
1000}
1001
5cb18b3d
SB
1002/*
1003 * Get the TPMVersion of the backend device being used
1004 */
1005TPMVersion tpm_tis_get_tpm_version(Object *obj)
1006{
1007 TPMState *s = TPM(obj);
1008
1009 return tpm_backend_get_tpm_version(s->be_driver);
1010}
1011
edff8678
SB
1012/*
1013 * This function is called when the machine starts, resets or due to
1014 * S3 resume.
1015 */
1016static void tpm_tis_reset(DeviceState *dev)
1017{
1018 TPMState *s = TPM(dev);
edff8678
SB
1019 int c;
1020
116694c3
SB
1021 s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver);
1022
8f0605cc 1023 tpm_backend_reset(s->be_driver);
edff8678 1024
3d4960c7
MAL
1025 s->active_locty = TPM_TIS_NO_LOCALITY;
1026 s->next_locty = TPM_TIS_NO_LOCALITY;
1027 s->aborting_locty = TPM_TIS_NO_LOCALITY;
edff8678
SB
1028
1029 for (c = 0; c < TPM_TIS_NUM_LOCALITIES; c++) {
3d4960c7 1030 s->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS;
116694c3
SB
1031 switch (s->be_tpm_version) {
1032 case TPM_VERSION_UNSPEC:
1033 break;
1034 case TPM_VERSION_1_2:
3d4960c7
MAL
1035 s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY1_2;
1036 s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3;
116694c3
SB
1037 break;
1038 case TPM_VERSION_2_0:
3d4960c7
MAL
1039 s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY2_0;
1040 s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0;
116694c3
SB
1041 break;
1042 }
3d4960c7
MAL
1043 s->loc[c].inte = TPM_TIS_INT_POLARITY_LOW_LEVEL;
1044 s->loc[c].ints = 0;
1045 s->loc[c].state = TPM_TIS_STATE_IDLE;
1046
1047 s->loc[c].w_offset = 0;
1048 tpm_tis_realloc_buffer(&s->loc[c].w_buffer);
1049 s->loc[c].r_offset = 0;
1050 tpm_tis_realloc_buffer(&s->loc[c].r_buffer);
edff8678
SB
1051 }
1052
1053 tpm_tis_do_startup_tpm(s);
1054}
1055
1056static const VMStateDescription vmstate_tpm_tis = {
1057 .name = "tpm",
1058 .unmigratable = 1,
1059};
1060
1061static Property tpm_tis_properties[] = {
3d4960c7 1062 DEFINE_PROP_UINT32("irq", TPMState, irq_num, TPM_TIS_IRQ),
edff8678
SB
1063 DEFINE_PROP_STRING("tpmdev", TPMState, backend),
1064 DEFINE_PROP_END_OF_LIST(),
1065};
1066
1067static void tpm_tis_realizefn(DeviceState *dev, Error **errp)
1068{
1069 TPMState *s = TPM(dev);
edff8678
SB
1070
1071 s->be_driver = qemu_find_tpm(s->backend);
1072 if (!s->be_driver) {
1073 error_setg(errp, "tpm_tis: backend driver with id %s could not be "
1074 "found", s->backend);
1075 return;
1076 }
1077
1078 s->be_driver->fe_model = TPM_MODEL_TPM_TIS;
1079
05a69998 1080 if (tpm_backend_init(s->be_driver, s)) {
edff8678
SB
1081 error_setg(errp, "tpm_tis: backend driver with id %s could not be "
1082 "initialized", s->backend);
1083 return;
1084 }
1085
3d4960c7 1086 if (s->irq_num > 15) {
edff8678 1087 error_setg(errp, "tpm_tis: IRQ %d for TPM TIS is outside valid range "
3d4960c7 1088 "of 0 to 15", s->irq_num);
edff8678
SB
1089 return;
1090 }
1091
3d4960c7 1092 s->bh = qemu_bh_new(tpm_tis_receive_bh, s);
edff8678 1093
3d4960c7 1094 isa_init_irq(&s->busdev, &s->irq, s->irq_num);
9dfd24ed
SB
1095
1096 memory_region_add_subregion(isa_address_space(ISA_DEVICE(dev)),
1097 TPM_TIS_ADDR_BASE, &s->mmio);
edff8678
SB
1098}
1099
1100static void tpm_tis_initfn(Object *obj)
1101{
edff8678
SB
1102 TPMState *s = TPM(obj);
1103
853dca12
PB
1104 memory_region_init_io(&s->mmio, OBJECT(s), &tpm_tis_memory_ops,
1105 s, "tpm-tis-mmio",
edff8678 1106 TPM_TIS_NUM_LOCALITIES << TPM_TIS_LOCALITY_SHIFT);
edff8678
SB
1107}
1108
edff8678
SB
1109static void tpm_tis_class_init(ObjectClass *klass, void *data)
1110{
1111 DeviceClass *dc = DEVICE_CLASS(klass);
05a69998 1112 TPMIfClass *tc = TPM_IF_CLASS(klass);
edff8678
SB
1113
1114 dc->realize = tpm_tis_realizefn;
1115 dc->props = tpm_tis_properties;
1116 dc->reset = tpm_tis_reset;
1117 dc->vmsd = &vmstate_tpm_tis;
05a69998 1118 tc->request_completed = tpm_tis_request_completed;
edff8678
SB
1119}
1120
1121static const TypeInfo tpm_tis_info = {
1122 .name = TYPE_TPM_TIS,
1123 .parent = TYPE_ISA_DEVICE,
1124 .instance_size = sizeof(TPMState),
1125 .instance_init = tpm_tis_initfn,
edff8678 1126 .class_init = tpm_tis_class_init,
698f5daa
MAL
1127 .interfaces = (InterfaceInfo[]) {
1128 { TYPE_TPM_IF },
1129 { }
1130 }
edff8678
SB
1131};
1132
1133static void tpm_tis_register(void)
1134{
1135 type_register_static(&tpm_tis_info);
1136 tpm_register_model(TPM_MODEL_TPM_TIS);
1137}
1138
1139type_init(tpm_tis_register)