root / hw / tpm / tpm_tis.c @ 6a1751b7
History | View | Annotate | Download (29 kB)
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
|
17 |
* supports version 1.21, revision 1.0.
|
18 |
* In the developers menu choose the PC Client section then find the TIS
|
19 |
* specification.
|
20 |
*/
|
21 |
|
22 |
#include "sysemu/tpm_backend.h" |
23 |
#include "tpm_int.h" |
24 |
#include "block/block.h" |
25 |
#include "exec/address-spaces.h" |
26 |
#include "hw/hw.h" |
27 |
#include "hw/i386/pc.h" |
28 |
#include "hw/pci/pci_ids.h" |
29 |
#include "tpm_tis.h" |
30 |
#include "qemu-common.h" |
31 |
#include "qemu/main-loop.h" |
32 |
|
33 |
/*#define DEBUG_TIS */
|
34 |
|
35 |
#ifdef DEBUG_TIS
|
36 |
#define DPRINTF(fmt, ...) \
|
37 |
do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) |
38 |
#else
|
39 |
#define DPRINTF(fmt, ...) \
|
40 |
do { } while (0) |
41 |
#endif
|
42 |
|
43 |
/* whether the STS interrupt is supported */
|
44 |
#define RAISE_STS_IRQ
|
45 |
|
46 |
/* tis registers */
|
47 |
#define TPM_TIS_REG_ACCESS 0x00 |
48 |
#define TPM_TIS_REG_INT_ENABLE 0x08 |
49 |
#define TPM_TIS_REG_INT_VECTOR 0x0c |
50 |
#define TPM_TIS_REG_INT_STATUS 0x10 |
51 |
#define TPM_TIS_REG_INTF_CAPABILITY 0x14 |
52 |
#define TPM_TIS_REG_STS 0x18 |
53 |
#define TPM_TIS_REG_DATA_FIFO 0x24 |
54 |
#define TPM_TIS_REG_DID_VID 0xf00 |
55 |
#define TPM_TIS_REG_RID 0xf04 |
56 |
|
57 |
/* vendor-specific registers */
|
58 |
#define TPM_TIS_REG_DEBUG 0xf90 |
59 |
|
60 |
#define TPM_TIS_STS_VALID (1 << 7) |
61 |
#define TPM_TIS_STS_COMMAND_READY (1 << 6) |
62 |
#define TPM_TIS_STS_TPM_GO (1 << 5) |
63 |
#define TPM_TIS_STS_DATA_AVAILABLE (1 << 4) |
64 |
#define TPM_TIS_STS_EXPECT (1 << 3) |
65 |
#define TPM_TIS_STS_RESPONSE_RETRY (1 << 1) |
66 |
|
67 |
#define TPM_TIS_BURST_COUNT_SHIFT 8 |
68 |
#define TPM_TIS_BURST_COUNT(X) \
|
69 |
((X) << TPM_TIS_BURST_COUNT_SHIFT) |
70 |
|
71 |
#define TPM_TIS_ACCESS_TPM_REG_VALID_STS (1 << 7) |
72 |
#define TPM_TIS_ACCESS_ACTIVE_LOCALITY (1 << 5) |
73 |
#define TPM_TIS_ACCESS_BEEN_SEIZED (1 << 4) |
74 |
#define TPM_TIS_ACCESS_SEIZE (1 << 3) |
75 |
#define TPM_TIS_ACCESS_PENDING_REQUEST (1 << 2) |
76 |
#define TPM_TIS_ACCESS_REQUEST_USE (1 << 1) |
77 |
#define TPM_TIS_ACCESS_TPM_ESTABLISHMENT (1 << 0) |
78 |
|
79 |
#define TPM_TIS_INT_ENABLED (1 << 31) |
80 |
#define TPM_TIS_INT_DATA_AVAILABLE (1 << 0) |
81 |
#define TPM_TIS_INT_STS_VALID (1 << 1) |
82 |
#define TPM_TIS_INT_LOCALITY_CHANGED (1 << 2) |
83 |
#define TPM_TIS_INT_COMMAND_READY (1 << 7) |
84 |
|
85 |
#define TPM_TIS_INT_POLARITY_MASK (3 << 3) |
86 |
#define TPM_TIS_INT_POLARITY_LOW_LEVEL (1 << 3) |
87 |
|
88 |
#ifndef RAISE_STS_IRQ
|
89 |
|
90 |
#define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \
|
91 |
TPM_TIS_INT_DATA_AVAILABLE | \ |
92 |
TPM_TIS_INT_COMMAND_READY) |
93 |
|
94 |
#else
|
95 |
|
96 |
#define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \
|
97 |
TPM_TIS_INT_DATA_AVAILABLE | \ |
98 |
TPM_TIS_INT_STS_VALID | \ |
99 |
TPM_TIS_INT_COMMAND_READY) |
100 |
|
101 |
#endif
|
102 |
|
103 |
#define TPM_TIS_CAP_INTERRUPT_LOW_LEVEL (1 << 4) /* support is mandatory */ |
104 |
#define TPM_TIS_CAPABILITIES_SUPPORTED (TPM_TIS_CAP_INTERRUPT_LOW_LEVEL | \
|
105 |
TPM_TIS_INTERRUPTS_SUPPORTED) |
106 |
|
107 |
#define TPM_TIS_TPM_DID 0x0001 |
108 |
#define TPM_TIS_TPM_VID PCI_VENDOR_ID_IBM
|
109 |
#define TPM_TIS_TPM_RID 0x0001 |
110 |
|
111 |
#define TPM_TIS_NO_DATA_BYTE 0xff |
112 |
|
113 |
/* local prototypes */
|
114 |
|
115 |
static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, |
116 |
unsigned size);
|
117 |
|
118 |
/* utility functions */
|
119 |
|
120 |
static uint8_t tpm_tis_locality_from_addr(hwaddr addr)
|
121 |
{ |
122 |
return (uint8_t)((addr >> TPM_TIS_LOCALITY_SHIFT) & 0x7); |
123 |
} |
124 |
|
125 |
static uint32_t tpm_tis_get_size_from_buffer(const TPMSizedBuffer *sb) |
126 |
{ |
127 |
return be32_to_cpu(*(uint32_t *)&sb->buffer[2]); |
128 |
} |
129 |
|
130 |
static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string) |
131 |
{ |
132 |
#ifdef DEBUG_TIS
|
133 |
uint32_t len, i; |
134 |
|
135 |
len = tpm_tis_get_size_from_buffer(sb); |
136 |
DPRINTF("tpm_tis: %s length = %d\n", string, len);
|
137 |
for (i = 0; i < len; i++) { |
138 |
if (i && !(i % 16)) { |
139 |
DPRINTF("\n");
|
140 |
} |
141 |
DPRINTF("%.2X ", sb->buffer[i]);
|
142 |
} |
143 |
DPRINTF("\n");
|
144 |
#endif
|
145 |
} |
146 |
|
147 |
/*
|
148 |
* Send a request to the TPM.
|
149 |
*/
|
150 |
static void tpm_tis_tpm_send(TPMState *s, uint8_t locty) |
151 |
{ |
152 |
TPMTISEmuState *tis = &s->s.tis; |
153 |
|
154 |
tpm_tis_show_buffer(&tis->loc[locty].w_buffer, "tpm_tis: To TPM");
|
155 |
|
156 |
s->locty_number = locty; |
157 |
s->locty_data = &tis->loc[locty]; |
158 |
|
159 |
/*
|
160 |
* w_offset serves as length indicator for length of data;
|
161 |
* it's reset when the response comes back
|
162 |
*/
|
163 |
tis->loc[locty].state = TPM_TIS_STATE_EXECUTION; |
164 |
|
165 |
tpm_backend_deliver_request(s->be_driver); |
166 |
} |
167 |
|
168 |
/* raise an interrupt if allowed */
|
169 |
static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask) |
170 |
{ |
171 |
TPMTISEmuState *tis = &s->s.tis; |
172 |
|
173 |
if (!TPM_TIS_IS_VALID_LOCTY(locty)) {
|
174 |
return;
|
175 |
} |
176 |
|
177 |
if ((tis->loc[locty].inte & TPM_TIS_INT_ENABLED) &&
|
178 |
(tis->loc[locty].inte & irqmask)) { |
179 |
DPRINTF("tpm_tis: Raising IRQ for flag %08x\n", irqmask);
|
180 |
qemu_irq_raise(s->s.tis.irq); |
181 |
tis->loc[locty].ints |= irqmask; |
182 |
} |
183 |
} |
184 |
|
185 |
static uint32_t tpm_tis_check_request_use_except(TPMState *s, uint8_t locty)
|
186 |
{ |
187 |
uint8_t l; |
188 |
|
189 |
for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { |
190 |
if (l == locty) {
|
191 |
continue;
|
192 |
} |
193 |
if ((s->s.tis.loc[l].access & TPM_TIS_ACCESS_REQUEST_USE)) {
|
194 |
return 1; |
195 |
} |
196 |
} |
197 |
|
198 |
return 0; |
199 |
} |
200 |
|
201 |
static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty) |
202 |
{ |
203 |
TPMTISEmuState *tis = &s->s.tis; |
204 |
bool change = (s->s.tis.active_locty != new_active_locty);
|
205 |
bool is_seize;
|
206 |
uint8_t mask; |
207 |
|
208 |
if (change && TPM_TIS_IS_VALID_LOCTY(s->s.tis.active_locty)) {
|
209 |
is_seize = TPM_TIS_IS_VALID_LOCTY(new_active_locty) && |
210 |
tis->loc[new_active_locty].access & TPM_TIS_ACCESS_SEIZE; |
211 |
|
212 |
if (is_seize) {
|
213 |
mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY); |
214 |
} else {
|
215 |
mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY| |
216 |
TPM_TIS_ACCESS_REQUEST_USE); |
217 |
} |
218 |
/* reset flags on the old active locality */
|
219 |
tis->loc[s->s.tis.active_locty].access &= mask; |
220 |
|
221 |
if (is_seize) {
|
222 |
tis->loc[tis->active_locty].access |= TPM_TIS_ACCESS_BEEN_SEIZED; |
223 |
} |
224 |
} |
225 |
|
226 |
tis->active_locty = new_active_locty; |
227 |
|
228 |
DPRINTF("tpm_tis: Active locality is now %d\n", s->s.tis.active_locty);
|
229 |
|
230 |
if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) {
|
231 |
/* set flags on the new active locality */
|
232 |
tis->loc[new_active_locty].access |= TPM_TIS_ACCESS_ACTIVE_LOCALITY; |
233 |
tis->loc[new_active_locty].access &= ~(TPM_TIS_ACCESS_REQUEST_USE | |
234 |
TPM_TIS_ACCESS_SEIZE); |
235 |
} |
236 |
|
237 |
if (change) {
|
238 |
tpm_tis_raise_irq(s, tis->active_locty, TPM_TIS_INT_LOCALITY_CHANGED); |
239 |
} |
240 |
} |
241 |
|
242 |
/* abort -- this function switches the locality */
|
243 |
static void tpm_tis_abort(TPMState *s, uint8_t locty) |
244 |
{ |
245 |
TPMTISEmuState *tis = &s->s.tis; |
246 |
|
247 |
tis->loc[locty].r_offset = 0;
|
248 |
tis->loc[locty].w_offset = 0;
|
249 |
|
250 |
DPRINTF("tpm_tis: tis_abort: new active locality is %d\n", tis->next_locty);
|
251 |
|
252 |
/*
|
253 |
* Need to react differently depending on who's aborting now and
|
254 |
* which locality will become active afterwards.
|
255 |
*/
|
256 |
if (tis->aborting_locty == tis->next_locty) {
|
257 |
tis->loc[tis->aborting_locty].state = TPM_TIS_STATE_READY; |
258 |
tis->loc[tis->aborting_locty].sts = TPM_TIS_STS_COMMAND_READY; |
259 |
tpm_tis_raise_irq(s, tis->aborting_locty, TPM_TIS_INT_COMMAND_READY); |
260 |
} |
261 |
|
262 |
/* locality after abort is another one than the current one */
|
263 |
tpm_tis_new_active_locality(s, tis->next_locty); |
264 |
|
265 |
tis->next_locty = TPM_TIS_NO_LOCALITY; |
266 |
/* nobody's aborting a command anymore */
|
267 |
tis->aborting_locty = TPM_TIS_NO_LOCALITY; |
268 |
} |
269 |
|
270 |
/* prepare aborting current command */
|
271 |
static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty) |
272 |
{ |
273 |
TPMTISEmuState *tis = &s->s.tis; |
274 |
uint8_t busy_locty; |
275 |
|
276 |
tis->aborting_locty = locty; |
277 |
tis->next_locty = newlocty; /* locality after successful abort */
|
278 |
|
279 |
/*
|
280 |
* only abort a command using an interrupt if currently executing
|
281 |
* a command AND if there's a valid connection to the vTPM.
|
282 |
*/
|
283 |
for (busy_locty = 0; busy_locty < TPM_TIS_NUM_LOCALITIES; busy_locty++) { |
284 |
if (tis->loc[busy_locty].state == TPM_TIS_STATE_EXECUTION) {
|
285 |
/*
|
286 |
* request the backend to cancel. Some backends may not
|
287 |
* support it
|
288 |
*/
|
289 |
tpm_backend_cancel_cmd(s->be_driver); |
290 |
return;
|
291 |
} |
292 |
} |
293 |
|
294 |
tpm_tis_abort(s, locty); |
295 |
} |
296 |
|
297 |
static void tpm_tis_receive_bh(void *opaque) |
298 |
{ |
299 |
TPMState *s = opaque; |
300 |
TPMTISEmuState *tis = &s->s.tis; |
301 |
uint8_t locty = s->locty_number; |
302 |
|
303 |
tis->loc[locty].sts = TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE; |
304 |
tis->loc[locty].state = TPM_TIS_STATE_COMPLETION; |
305 |
tis->loc[locty].r_offset = 0;
|
306 |
tis->loc[locty].w_offset = 0;
|
307 |
|
308 |
if (TPM_TIS_IS_VALID_LOCTY(tis->next_locty)) {
|
309 |
tpm_tis_abort(s, locty); |
310 |
} |
311 |
|
312 |
#ifndef RAISE_STS_IRQ
|
313 |
tpm_tis_raise_irq(s, locty, TPM_TIS_INT_DATA_AVAILABLE); |
314 |
#else
|
315 |
tpm_tis_raise_irq(s, locty, |
316 |
TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID); |
317 |
#endif
|
318 |
} |
319 |
|
320 |
/*
|
321 |
* Callback from the TPM to indicate that the response was received.
|
322 |
*/
|
323 |
static void tpm_tis_receive_cb(TPMState *s, uint8_t locty) |
324 |
{ |
325 |
TPMTISEmuState *tis = &s->s.tis; |
326 |
|
327 |
assert(s->locty_number == locty); |
328 |
|
329 |
qemu_bh_schedule(tis->bh); |
330 |
} |
331 |
|
332 |
/*
|
333 |
* Read a byte of response data
|
334 |
*/
|
335 |
static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty)
|
336 |
{ |
337 |
TPMTISEmuState *tis = &s->s.tis; |
338 |
uint32_t ret = TPM_TIS_NO_DATA_BYTE; |
339 |
uint16_t len; |
340 |
|
341 |
if ((tis->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
|
342 |
len = tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer); |
343 |
|
344 |
ret = tis->loc[locty].r_buffer.buffer[tis->loc[locty].r_offset++]; |
345 |
if (tis->loc[locty].r_offset >= len) {
|
346 |
/* got last byte */
|
347 |
tis->loc[locty].sts = TPM_TIS_STS_VALID; |
348 |
#ifdef RAISE_STS_IRQ
|
349 |
tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); |
350 |
#endif
|
351 |
} |
352 |
DPRINTF("tpm_tis: tpm_tis_data_read byte 0x%02x [%d]\n",
|
353 |
ret, tis->loc[locty].r_offset-1);
|
354 |
} |
355 |
|
356 |
return ret;
|
357 |
} |
358 |
|
359 |
#ifdef DEBUG_TIS
|
360 |
static void tpm_tis_dump_state(void *opaque, hwaddr addr) |
361 |
{ |
362 |
static const unsigned regs[] = { |
363 |
TPM_TIS_REG_ACCESS, |
364 |
TPM_TIS_REG_INT_ENABLE, |
365 |
TPM_TIS_REG_INT_VECTOR, |
366 |
TPM_TIS_REG_INT_STATUS, |
367 |
TPM_TIS_REG_INTF_CAPABILITY, |
368 |
TPM_TIS_REG_STS, |
369 |
TPM_TIS_REG_DID_VID, |
370 |
TPM_TIS_REG_RID, |
371 |
0xfff};
|
372 |
int idx;
|
373 |
uint8_t locty = tpm_tis_locality_from_addr(addr); |
374 |
hwaddr base = addr & ~0xfff;
|
375 |
TPMState *s = opaque; |
376 |
TPMTISEmuState *tis = &s->s.tis; |
377 |
|
378 |
DPRINTF("tpm_tis: active locality : %d\n"
|
379 |
"tpm_tis: state of locality %d : %d\n"
|
380 |
"tpm_tis: register dump:\n",
|
381 |
tis->active_locty, |
382 |
locty, tis->loc[locty].state); |
383 |
|
384 |
for (idx = 0; regs[idx] != 0xfff; idx++) { |
385 |
DPRINTF("tpm_tis: 0x%04x : 0x%08x\n", regs[idx],
|
386 |
(uint32_t)tpm_tis_mmio_read(opaque, base + regs[idx], 4));
|
387 |
} |
388 |
|
389 |
DPRINTF("tpm_tis: read offset : %d\n"
|
390 |
"tpm_tis: result buffer : ",
|
391 |
tis->loc[locty].r_offset); |
392 |
for (idx = 0; |
393 |
idx < tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer); |
394 |
idx++) { |
395 |
DPRINTF("%c%02x%s",
|
396 |
tis->loc[locty].r_offset == idx ? '>' : ' ', |
397 |
tis->loc[locty].r_buffer.buffer[idx], |
398 |
((idx & 0xf) == 0xf) ? "\ntpm_tis: " : ""); |
399 |
} |
400 |
DPRINTF("\n"
|
401 |
"tpm_tis: write offset : %d\n"
|
402 |
"tpm_tis: request buffer: ",
|
403 |
tis->loc[locty].w_offset); |
404 |
for (idx = 0; |
405 |
idx < tpm_tis_get_size_from_buffer(&tis->loc[locty].w_buffer); |
406 |
idx++) { |
407 |
DPRINTF("%c%02x%s",
|
408 |
tis->loc[locty].w_offset == idx ? '>' : ' ', |
409 |
tis->loc[locty].w_buffer.buffer[idx], |
410 |
((idx & 0xf) == 0xf) ? "\ntpm_tis: " : ""); |
411 |
} |
412 |
DPRINTF("\n");
|
413 |
} |
414 |
#endif
|
415 |
|
416 |
/*
|
417 |
* Read a register of the TIS interface
|
418 |
* See specs pages 33-63 for description of the registers
|
419 |
*/
|
420 |
static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, |
421 |
unsigned size)
|
422 |
{ |
423 |
TPMState *s = opaque; |
424 |
TPMTISEmuState *tis = &s->s.tis; |
425 |
uint16_t offset = addr & 0xffc;
|
426 |
uint8_t shift = (addr & 0x3) * 8; |
427 |
uint32_t val = 0xffffffff;
|
428 |
uint8_t locty = tpm_tis_locality_from_addr(addr); |
429 |
uint32_t avail; |
430 |
|
431 |
if (tpm_backend_had_startup_error(s->be_driver)) {
|
432 |
return val;
|
433 |
} |
434 |
|
435 |
switch (offset) {
|
436 |
case TPM_TIS_REG_ACCESS:
|
437 |
/* never show the SEIZE flag even though we use it internally */
|
438 |
val = tis->loc[locty].access & ~TPM_TIS_ACCESS_SEIZE; |
439 |
/* the pending flag is always calculated */
|
440 |
if (tpm_tis_check_request_use_except(s, locty)) {
|
441 |
val |= TPM_TIS_ACCESS_PENDING_REQUEST; |
442 |
} |
443 |
val |= !tpm_backend_get_tpm_established_flag(s->be_driver); |
444 |
break;
|
445 |
case TPM_TIS_REG_INT_ENABLE:
|
446 |
val = tis->loc[locty].inte; |
447 |
break;
|
448 |
case TPM_TIS_REG_INT_VECTOR:
|
449 |
val = tis->irq_num; |
450 |
break;
|
451 |
case TPM_TIS_REG_INT_STATUS:
|
452 |
val = tis->loc[locty].ints; |
453 |
break;
|
454 |
case TPM_TIS_REG_INTF_CAPABILITY:
|
455 |
val = TPM_TIS_CAPABILITIES_SUPPORTED; |
456 |
break;
|
457 |
case TPM_TIS_REG_STS:
|
458 |
if (tis->active_locty == locty) {
|
459 |
if ((tis->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
|
460 |
val = TPM_TIS_BURST_COUNT( |
461 |
tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer) |
462 |
- tis->loc[locty].r_offset) | tis->loc[locty].sts; |
463 |
} else {
|
464 |
avail = tis->loc[locty].w_buffer.size |
465 |
- tis->loc[locty].w_offset; |
466 |
/*
|
467 |
* byte-sized reads should not return 0x00 for 0x100
|
468 |
* available bytes.
|
469 |
*/
|
470 |
if (size == 1 && avail > 0xff) { |
471 |
avail = 0xff;
|
472 |
} |
473 |
val = TPM_TIS_BURST_COUNT(avail) | tis->loc[locty].sts; |
474 |
} |
475 |
} |
476 |
break;
|
477 |
case TPM_TIS_REG_DATA_FIFO:
|
478 |
if (tis->active_locty == locty) {
|
479 |
switch (tis->loc[locty].state) {
|
480 |
case TPM_TIS_STATE_COMPLETION:
|
481 |
val = tpm_tis_data_read(s, locty); |
482 |
break;
|
483 |
default:
|
484 |
val = TPM_TIS_NO_DATA_BYTE; |
485 |
break;
|
486 |
} |
487 |
} |
488 |
break;
|
489 |
case TPM_TIS_REG_DID_VID:
|
490 |
val = (TPM_TIS_TPM_DID << 16) | TPM_TIS_TPM_VID;
|
491 |
break;
|
492 |
case TPM_TIS_REG_RID:
|
493 |
val = TPM_TIS_TPM_RID; |
494 |
break;
|
495 |
#ifdef DEBUG_TIS
|
496 |
case TPM_TIS_REG_DEBUG:
|
497 |
tpm_tis_dump_state(opaque, addr); |
498 |
break;
|
499 |
#endif
|
500 |
} |
501 |
|
502 |
if (shift) {
|
503 |
val >>= shift; |
504 |
} |
505 |
|
506 |
DPRINTF("tpm_tis: read.%u(%08x) = %08x\n", size, (int)addr, (uint32_t)val); |
507 |
|
508 |
return val;
|
509 |
} |
510 |
|
511 |
/*
|
512 |
* Write a value to a register of the TIS interface
|
513 |
* See specs pages 33-63 for description of the registers
|
514 |
*/
|
515 |
static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr, |
516 |
uint64_t val, unsigned size,
|
517 |
bool hw_access)
|
518 |
{ |
519 |
TPMState *s = opaque; |
520 |
TPMTISEmuState *tis = &s->s.tis; |
521 |
uint16_t off = addr & 0xfff;
|
522 |
uint8_t locty = tpm_tis_locality_from_addr(addr); |
523 |
uint8_t active_locty, l; |
524 |
int c, set_new_locty = 1; |
525 |
uint16_t len; |
526 |
|
527 |
DPRINTF("tpm_tis: write.%u(%08x) = %08x\n", size, (int)addr, (uint32_t)val); |
528 |
|
529 |
if (locty == 4 && !hw_access) { |
530 |
DPRINTF("tpm_tis: Access to locality 4 only allowed from hardware\n");
|
531 |
return;
|
532 |
} |
533 |
|
534 |
if (tpm_backend_had_startup_error(s->be_driver)) {
|
535 |
return;
|
536 |
} |
537 |
|
538 |
switch (off) {
|
539 |
case TPM_TIS_REG_ACCESS:
|
540 |
|
541 |
if ((val & TPM_TIS_ACCESS_SEIZE)) {
|
542 |
val &= ~(TPM_TIS_ACCESS_REQUEST_USE | |
543 |
TPM_TIS_ACCESS_ACTIVE_LOCALITY); |
544 |
} |
545 |
|
546 |
active_locty = tis->active_locty; |
547 |
|
548 |
if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) {
|
549 |
/* give up locality if currently owned */
|
550 |
if (tis->active_locty == locty) {
|
551 |
DPRINTF("tpm_tis: Releasing locality %d\n", locty);
|
552 |
|
553 |
uint8_t newlocty = TPM_TIS_NO_LOCALITY; |
554 |
/* anybody wants the locality ? */
|
555 |
for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) { |
556 |
if ((tis->loc[c].access & TPM_TIS_ACCESS_REQUEST_USE)) {
|
557 |
DPRINTF("tpm_tis: Locality %d requests use.\n", c);
|
558 |
newlocty = c; |
559 |
break;
|
560 |
} |
561 |
} |
562 |
DPRINTF("tpm_tis: TPM_TIS_ACCESS_ACTIVE_LOCALITY: "
|
563 |
"Next active locality: %d\n",
|
564 |
newlocty); |
565 |
|
566 |
if (TPM_TIS_IS_VALID_LOCTY(newlocty)) {
|
567 |
set_new_locty = 0;
|
568 |
tpm_tis_prep_abort(s, locty, newlocty); |
569 |
} else {
|
570 |
active_locty = TPM_TIS_NO_LOCALITY; |
571 |
} |
572 |
} else {
|
573 |
/* not currently the owner; clear a pending request */
|
574 |
tis->loc[locty].access &= ~TPM_TIS_ACCESS_REQUEST_USE; |
575 |
} |
576 |
} |
577 |
|
578 |
if ((val & TPM_TIS_ACCESS_BEEN_SEIZED)) {
|
579 |
tis->loc[locty].access &= ~TPM_TIS_ACCESS_BEEN_SEIZED; |
580 |
} |
581 |
|
582 |
if ((val & TPM_TIS_ACCESS_SEIZE)) {
|
583 |
/*
|
584 |
* allow seize if a locality is active and the requesting
|
585 |
* locality is higher than the one that's active
|
586 |
* OR
|
587 |
* allow seize for requesting locality if no locality is
|
588 |
* active
|
589 |
*/
|
590 |
while ((TPM_TIS_IS_VALID_LOCTY(tis->active_locty) &&
|
591 |
locty > tis->active_locty) || |
592 |
!TPM_TIS_IS_VALID_LOCTY(tis->active_locty)) { |
593 |
bool higher_seize = FALSE;
|
594 |
|
595 |
/* already a pending SEIZE ? */
|
596 |
if ((tis->loc[locty].access & TPM_TIS_ACCESS_SEIZE)) {
|
597 |
break;
|
598 |
} |
599 |
|
600 |
/* check for ongoing seize by a higher locality */
|
601 |
for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES; l++) { |
602 |
if ((tis->loc[l].access & TPM_TIS_ACCESS_SEIZE)) {
|
603 |
higher_seize = TRUE; |
604 |
break;
|
605 |
} |
606 |
} |
607 |
|
608 |
if (higher_seize) {
|
609 |
break;
|
610 |
} |
611 |
|
612 |
/* cancel any seize by a lower locality */
|
613 |
for (l = 0; l < locty - 1; l++) { |
614 |
tis->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE; |
615 |
} |
616 |
|
617 |
tis->loc[locty].access |= TPM_TIS_ACCESS_SEIZE; |
618 |
DPRINTF("tpm_tis: TPM_TIS_ACCESS_SEIZE: "
|
619 |
"Locality %d seized from locality %d\n",
|
620 |
locty, tis->active_locty); |
621 |
DPRINTF("tpm_tis: TPM_TIS_ACCESS_SEIZE: Initiating abort.\n");
|
622 |
set_new_locty = 0;
|
623 |
tpm_tis_prep_abort(s, tis->active_locty, locty); |
624 |
break;
|
625 |
} |
626 |
} |
627 |
|
628 |
if ((val & TPM_TIS_ACCESS_REQUEST_USE)) {
|
629 |
if (tis->active_locty != locty) {
|
630 |
if (TPM_TIS_IS_VALID_LOCTY(tis->active_locty)) {
|
631 |
tis->loc[locty].access |= TPM_TIS_ACCESS_REQUEST_USE; |
632 |
} else {
|
633 |
/* no locality active -> make this one active now */
|
634 |
active_locty = locty; |
635 |
} |
636 |
} |
637 |
} |
638 |
|
639 |
if (set_new_locty) {
|
640 |
tpm_tis_new_active_locality(s, active_locty); |
641 |
} |
642 |
|
643 |
break;
|
644 |
case TPM_TIS_REG_INT_ENABLE:
|
645 |
if (tis->active_locty != locty) {
|
646 |
break;
|
647 |
} |
648 |
|
649 |
tis->loc[locty].inte = (val & (TPM_TIS_INT_ENABLED | |
650 |
TPM_TIS_INT_POLARITY_MASK | |
651 |
TPM_TIS_INTERRUPTS_SUPPORTED)); |
652 |
break;
|
653 |
case TPM_TIS_REG_INT_VECTOR:
|
654 |
/* hard wired -- ignore */
|
655 |
break;
|
656 |
case TPM_TIS_REG_INT_STATUS:
|
657 |
if (tis->active_locty != locty) {
|
658 |
break;
|
659 |
} |
660 |
|
661 |
/* clearing of interrupt flags */
|
662 |
if (((val & TPM_TIS_INTERRUPTS_SUPPORTED)) &&
|
663 |
(tis->loc[locty].ints & TPM_TIS_INTERRUPTS_SUPPORTED)) { |
664 |
tis->loc[locty].ints &= ~val; |
665 |
if (tis->loc[locty].ints == 0) { |
666 |
qemu_irq_lower(tis->irq); |
667 |
DPRINTF("tpm_tis: Lowering IRQ\n");
|
668 |
} |
669 |
} |
670 |
tis->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED); |
671 |
break;
|
672 |
case TPM_TIS_REG_STS:
|
673 |
if (tis->active_locty != locty) {
|
674 |
break;
|
675 |
} |
676 |
|
677 |
val &= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO | |
678 |
TPM_TIS_STS_RESPONSE_RETRY); |
679 |
|
680 |
if (val == TPM_TIS_STS_COMMAND_READY) {
|
681 |
switch (tis->loc[locty].state) {
|
682 |
|
683 |
case TPM_TIS_STATE_READY:
|
684 |
tis->loc[locty].w_offset = 0;
|
685 |
tis->loc[locty].r_offset = 0;
|
686 |
break;
|
687 |
|
688 |
case TPM_TIS_STATE_IDLE:
|
689 |
tis->loc[locty].sts = TPM_TIS_STS_COMMAND_READY; |
690 |
tis->loc[locty].state = TPM_TIS_STATE_READY; |
691 |
tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); |
692 |
break;
|
693 |
|
694 |
case TPM_TIS_STATE_EXECUTION:
|
695 |
case TPM_TIS_STATE_RECEPTION:
|
696 |
/* abort currently running command */
|
697 |
DPRINTF("tpm_tis: %s: Initiating abort.\n",
|
698 |
__func__); |
699 |
tpm_tis_prep_abort(s, locty, locty); |
700 |
break;
|
701 |
|
702 |
case TPM_TIS_STATE_COMPLETION:
|
703 |
tis->loc[locty].w_offset = 0;
|
704 |
tis->loc[locty].r_offset = 0;
|
705 |
/* shortcut to ready state with C/R set */
|
706 |
tis->loc[locty].state = TPM_TIS_STATE_READY; |
707 |
if (!(tis->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) {
|
708 |
tis->loc[locty].sts = TPM_TIS_STS_COMMAND_READY; |
709 |
tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); |
710 |
} |
711 |
tis->loc[locty].sts &= ~(TPM_TIS_STS_DATA_AVAILABLE); |
712 |
break;
|
713 |
|
714 |
} |
715 |
} else if (val == TPM_TIS_STS_TPM_GO) { |
716 |
switch (tis->loc[locty].state) {
|
717 |
case TPM_TIS_STATE_RECEPTION:
|
718 |
if ((tis->loc[locty].sts & TPM_TIS_STS_EXPECT) == 0) { |
719 |
tpm_tis_tpm_send(s, locty); |
720 |
} |
721 |
break;
|
722 |
default:
|
723 |
/* ignore */
|
724 |
break;
|
725 |
} |
726 |
} else if (val == TPM_TIS_STS_RESPONSE_RETRY) { |
727 |
switch (tis->loc[locty].state) {
|
728 |
case TPM_TIS_STATE_COMPLETION:
|
729 |
tis->loc[locty].r_offset = 0;
|
730 |
tis->loc[locty].sts = TPM_TIS_STS_VALID | |
731 |
TPM_TIS_STS_DATA_AVAILABLE; |
732 |
break;
|
733 |
default:
|
734 |
/* ignore */
|
735 |
break;
|
736 |
} |
737 |
} |
738 |
break;
|
739 |
case TPM_TIS_REG_DATA_FIFO:
|
740 |
/* data fifo */
|
741 |
if (tis->active_locty != locty) {
|
742 |
break;
|
743 |
} |
744 |
|
745 |
if (tis->loc[locty].state == TPM_TIS_STATE_IDLE ||
|
746 |
tis->loc[locty].state == TPM_TIS_STATE_EXECUTION || |
747 |
tis->loc[locty].state == TPM_TIS_STATE_COMPLETION) { |
748 |
/* drop the byte */
|
749 |
} else {
|
750 |
DPRINTF("tpm_tis: Byte to send to TPM: %02x\n", (uint8_t)val);
|
751 |
if (tis->loc[locty].state == TPM_TIS_STATE_READY) {
|
752 |
tis->loc[locty].state = TPM_TIS_STATE_RECEPTION; |
753 |
tis->loc[locty].sts = TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID; |
754 |
} |
755 |
|
756 |
if ((tis->loc[locty].sts & TPM_TIS_STS_EXPECT)) {
|
757 |
if (tis->loc[locty].w_offset < tis->loc[locty].w_buffer.size) {
|
758 |
tis->loc[locty].w_buffer. |
759 |
buffer[tis->loc[locty].w_offset++] = (uint8_t)val; |
760 |
} else {
|
761 |
tis->loc[locty].sts = TPM_TIS_STS_VALID; |
762 |
} |
763 |
} |
764 |
|
765 |
/* check for complete packet */
|
766 |
if (tis->loc[locty].w_offset > 5 && |
767 |
(tis->loc[locty].sts & TPM_TIS_STS_EXPECT)) { |
768 |
/* we have a packet length - see if we have all of it */
|
769 |
#ifdef RAISE_STS_IRQ
|
770 |
bool needIrq = !(tis->loc[locty].sts & TPM_TIS_STS_VALID);
|
771 |
#endif
|
772 |
len = tpm_tis_get_size_from_buffer(&tis->loc[locty].w_buffer); |
773 |
if (len > tis->loc[locty].w_offset) {
|
774 |
tis->loc[locty].sts = TPM_TIS_STS_EXPECT | |
775 |
TPM_TIS_STS_VALID; |
776 |
} else {
|
777 |
/* packet complete */
|
778 |
tis->loc[locty].sts = TPM_TIS_STS_VALID; |
779 |
} |
780 |
#ifdef RAISE_STS_IRQ
|
781 |
if (needIrq) {
|
782 |
tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); |
783 |
} |
784 |
#endif
|
785 |
} |
786 |
} |
787 |
break;
|
788 |
} |
789 |
} |
790 |
|
791 |
static void tpm_tis_mmio_write(void *opaque, hwaddr addr, |
792 |
uint64_t val, unsigned size)
|
793 |
{ |
794 |
return tpm_tis_mmio_write_intern(opaque, addr, val, size, false); |
795 |
} |
796 |
|
797 |
static const MemoryRegionOps tpm_tis_memory_ops = { |
798 |
.read = tpm_tis_mmio_read, |
799 |
.write = tpm_tis_mmio_write, |
800 |
.endianness = DEVICE_LITTLE_ENDIAN, |
801 |
.valid = { |
802 |
.min_access_size = 1,
|
803 |
.max_access_size = 4,
|
804 |
}, |
805 |
}; |
806 |
|
807 |
static int tpm_tis_do_startup_tpm(TPMState *s) |
808 |
{ |
809 |
return tpm_backend_startup_tpm(s->be_driver);
|
810 |
} |
811 |
|
812 |
/*
|
813 |
* This function is called when the machine starts, resets or due to
|
814 |
* S3 resume.
|
815 |
*/
|
816 |
static void tpm_tis_reset(DeviceState *dev) |
817 |
{ |
818 |
TPMState *s = TPM(dev); |
819 |
TPMTISEmuState *tis = &s->s.tis; |
820 |
int c;
|
821 |
|
822 |
tpm_backend_reset(s->be_driver); |
823 |
|
824 |
tis->active_locty = TPM_TIS_NO_LOCALITY; |
825 |
tis->next_locty = TPM_TIS_NO_LOCALITY; |
826 |
tis->aborting_locty = TPM_TIS_NO_LOCALITY; |
827 |
|
828 |
for (c = 0; c < TPM_TIS_NUM_LOCALITIES; c++) { |
829 |
tis->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS; |
830 |
tis->loc[c].sts = 0;
|
831 |
tis->loc[c].inte = TPM_TIS_INT_POLARITY_LOW_LEVEL; |
832 |
tis->loc[c].ints = 0;
|
833 |
tis->loc[c].state = TPM_TIS_STATE_IDLE; |
834 |
|
835 |
tis->loc[c].w_offset = 0;
|
836 |
tpm_backend_realloc_buffer(s->be_driver, &tis->loc[c].w_buffer); |
837 |
tis->loc[c].r_offset = 0;
|
838 |
tpm_backend_realloc_buffer(s->be_driver, &tis->loc[c].r_buffer); |
839 |
} |
840 |
|
841 |
tpm_tis_do_startup_tpm(s); |
842 |
} |
843 |
|
844 |
static const VMStateDescription vmstate_tpm_tis = { |
845 |
.name = "tpm",
|
846 |
.unmigratable = 1,
|
847 |
}; |
848 |
|
849 |
static Property tpm_tis_properties[] = {
|
850 |
DEFINE_PROP_UINT32("irq", TPMState,
|
851 |
s.tis.irq_num, TPM_TIS_IRQ), |
852 |
DEFINE_PROP_STRING("tpmdev", TPMState, backend),
|
853 |
DEFINE_PROP_END_OF_LIST(), |
854 |
}; |
855 |
|
856 |
static void tpm_tis_realizefn(DeviceState *dev, Error **errp) |
857 |
{ |
858 |
TPMState *s = TPM(dev); |
859 |
TPMTISEmuState *tis = &s->s.tis; |
860 |
|
861 |
s->be_driver = qemu_find_tpm(s->backend); |
862 |
if (!s->be_driver) {
|
863 |
error_setg(errp, "tpm_tis: backend driver with id %s could not be "
|
864 |
"found", s->backend);
|
865 |
return;
|
866 |
} |
867 |
|
868 |
s->be_driver->fe_model = TPM_MODEL_TPM_TIS; |
869 |
|
870 |
if (tpm_backend_init(s->be_driver, s, tpm_tis_receive_cb)) {
|
871 |
error_setg(errp, "tpm_tis: backend driver with id %s could not be "
|
872 |
"initialized", s->backend);
|
873 |
return;
|
874 |
} |
875 |
|
876 |
if (tis->irq_num > 15) { |
877 |
error_setg(errp, "tpm_tis: IRQ %d for TPM TIS is outside valid range "
|
878 |
"of 0 to 15.\n", tis->irq_num);
|
879 |
return;
|
880 |
} |
881 |
|
882 |
tis->bh = qemu_bh_new(tpm_tis_receive_bh, s); |
883 |
|
884 |
isa_init_irq(&s->busdev, &tis->irq, tis->irq_num); |
885 |
} |
886 |
|
887 |
static void tpm_tis_initfn(Object *obj) |
888 |
{ |
889 |
ISADevice *dev = ISA_DEVICE(obj); |
890 |
TPMState *s = TPM(obj); |
891 |
|
892 |
memory_region_init_io(&s->mmio, OBJECT(s), &tpm_tis_memory_ops, |
893 |
s, "tpm-tis-mmio",
|
894 |
TPM_TIS_NUM_LOCALITIES << TPM_TIS_LOCALITY_SHIFT); |
895 |
memory_region_add_subregion(isa_address_space(dev), TPM_TIS_ADDR_BASE, |
896 |
&s->mmio); |
897 |
} |
898 |
|
899 |
static void tpm_tis_uninitfn(Object *obj) |
900 |
{ |
901 |
TPMState *s = TPM(obj); |
902 |
|
903 |
memory_region_del_subregion(get_system_memory(), &s->mmio); |
904 |
memory_region_destroy(&s->mmio); |
905 |
} |
906 |
|
907 |
static void tpm_tis_class_init(ObjectClass *klass, void *data) |
908 |
{ |
909 |
DeviceClass *dc = DEVICE_CLASS(klass); |
910 |
|
911 |
dc->realize = tpm_tis_realizefn; |
912 |
dc->props = tpm_tis_properties; |
913 |
dc->reset = tpm_tis_reset; |
914 |
dc->vmsd = &vmstate_tpm_tis; |
915 |
} |
916 |
|
917 |
static const TypeInfo tpm_tis_info = { |
918 |
.name = TYPE_TPM_TIS, |
919 |
.parent = TYPE_ISA_DEVICE, |
920 |
.instance_size = sizeof(TPMState),
|
921 |
.instance_init = tpm_tis_initfn, |
922 |
.instance_finalize = tpm_tis_uninitfn, |
923 |
.class_init = tpm_tis_class_init, |
924 |
}; |
925 |
|
926 |
static void tpm_tis_register(void) |
927 |
{ |
928 |
type_register_static(&tpm_tis_info); |
929 |
tpm_register_model(TPM_MODEL_TPM_TIS); |
930 |
} |
931 |
|
932 |
type_init(tpm_tis_register) |