tpm_tis.c 29.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 * tpm_tis.c - QEMU's TPM TIS interface emulator
 *
 * Copyright (C) 2006,2010-2013 IBM Corporation
 *
 * Authors:
 *  Stefan Berger <stefanb@us.ibm.com>
 *  David Safford <safford@us.ibm.com>
 *
 * Xen 4 support: Andrease Niederl <andreas.niederl@iaik.tugraz.at>
 *
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
 * See the COPYING file in the top-level directory.
 *
 * Implementation of the TIS interface according to specs found at
 * http://www.trustedcomputinggroup.org. This implementation currently
17
 * supports version 1.3, 21 March 2013
18 19
 * In the developers menu choose the PC Client section then find the TIS
 * specification.
20 21 22
 *
 * TPM TIS for TPM 2 implementation following TCG PC Client Platform
 * TPM Profile (PTP) Specification, Familiy 2.0, Revision 00.43
23 24
 */

P
Peter Maydell 已提交
25
#include "qemu/osdep.h"
26
#include "hw/isa/isa.h"
27
#include "qapi/error.h"
28

29
#include "hw/acpi/tpm.h"
30 31 32
#include "hw/pci/pci_ids.h"
#include "sysemu/tpm_backend.h"
#include "tpm_int.h"
33
#include "tpm_util.h"
34
#include "trace.h"
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61

#define TPM_TIS_NUM_LOCALITIES      5     /* per spec */
#define TPM_TIS_LOCALITY_SHIFT      12
#define TPM_TIS_NO_LOCALITY         0xff

#define TPM_TIS_IS_VALID_LOCTY(x)   ((x) < TPM_TIS_NUM_LOCALITIES)

#define TPM_TIS_BUFFER_MAX          4096

typedef enum {
    TPM_TIS_STATE_IDLE = 0,
    TPM_TIS_STATE_READY,
    TPM_TIS_STATE_COMPLETION,
    TPM_TIS_STATE_EXECUTION,
    TPM_TIS_STATE_RECEPTION,
} TPMTISState;

/* locality data  -- all fields are persisted */
typedef struct TPMLocality {
    TPMTISState state;
    uint8_t access;
    uint32_t sts;
    uint32_t iface_id;
    uint32_t inte;
    uint32_t ints;
} TPMLocality;

62
typedef struct TPMState {
63 64 65
    ISADevice busdev;
    MemoryRegion mmio;

66
    unsigned char buffer[TPM_TIS_BUFFER_MAX];
67
    uint16_t rw_offset;
68 69 70 71 72 73 74 75 76 77 78 79 80 81

    uint8_t active_locty;
    uint8_t aborting_locty;
    uint8_t next_locty;

    TPMLocality loc[TPM_TIS_NUM_LOCALITIES];

    qemu_irq irq;
    uint32_t irq_num;

    TPMBackendCmd cmd;

    TPMBackend *be_driver;
    TPMVersion be_tpm_version;
82 83

    size_t be_buffer_size;
84
} TPMState;
85 86

#define TPM(obj) OBJECT_CHECK(TPMState, (obj), TYPE_TPM_TIS)
87

88
#define DEBUG_TIS 0
89

S
Stefan Berger 已提交
90 91 92 93 94
/* local prototypes */

static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr,
                                  unsigned size);

95 96 97 98 99 100 101
/* utility functions */

static uint8_t tpm_tis_locality_from_addr(hwaddr addr)
{
    return (uint8_t)((addr >> TPM_TIS_LOCALITY_SHIFT) & 0x7);
}

102 103
static void tpm_tis_show_buffer(const unsigned char *buffer,
                                size_t buffer_size, const char *string)
104 105 106
{
    uint32_t len, i;

107
    len = MIN(tpm_cmd_get_size(buffer), buffer_size);
108
    printf("tpm_tis: %s length = %d\n", string, len);
109 110
    for (i = 0; i < len; i++) {
        if (i && !(i % 16)) {
111
            printf("\n");
112
        }
113
        printf("%.2X ", buffer[i]);
114
    }
115
    printf("\n");
116 117
}

118 119
/*
 * Set the given flags in the STS register by clearing the register but
120 121
 * preserving the SELFTEST_DONE and TPM_FAMILY_MASK flags and then setting
 * the new flags.
122 123 124 125 126 127 128 129 130 131 132
 *
 * The SELFTEST_DONE flag is acquired from the backend that determines it by
 * peeking into TPM commands.
 *
 * A VM suspend/resume will preserve the flag by storing it into the VM
 * device state, but the backend will not remember it when QEMU is started
 * again. Therefore, we cache the flag here. Once set, it will not be unset
 * except by a reset.
 */
static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags)
{
133
    l->sts &= TPM_TIS_STS_SELFTEST_DONE | TPM_TIS_STS_TPM_FAMILY_MASK;
134 135 136
    l->sts |= flags;
}

137 138 139 140 141
/*
 * Send a request to the TPM.
 */
static void tpm_tis_tpm_send(TPMState *s, uint8_t locty)
{
142 143 144 145
    if (DEBUG_TIS) {
        tpm_tis_show_buffer(s->buffer, s->be_buffer_size,
                            "tpm_tis: To TPM");
    }
146 147

    /*
148
     * rw_offset serves as length indicator for length of data;
149 150
     * it's reset when the response comes back
     */
151
    s->loc[locty].state = TPM_TIS_STATE_EXECUTION;
152

153 154
    s->cmd = (TPMBackendCmd) {
        .locty = locty,
155
        .in = s->buffer,
156
        .in_len = s->rw_offset,
157
        .out = s->buffer,
158
        .out_len = s->be_buffer_size,
159 160 161
    };

    tpm_backend_deliver_request(s->be_driver, &s->cmd);
162 163 164 165 166 167 168 169 170
}

/* raise an interrupt if allowed */
static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask)
{
    if (!TPM_TIS_IS_VALID_LOCTY(locty)) {
        return;
    }

171 172
    if ((s->loc[locty].inte & TPM_TIS_INT_ENABLED) &&
        (s->loc[locty].inte & irqmask)) {
173
        trace_tpm_tis_raise_irq(irqmask);
174 175
        qemu_irq_raise(s->irq);
        s->loc[locty].ints |= irqmask;
176 177 178 179 180 181 182 183 184 185 186
    }
}

static uint32_t tpm_tis_check_request_use_except(TPMState *s, uint8_t locty)
{
    uint8_t l;

    for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
        if (l == locty) {
            continue;
        }
187
        if ((s->loc[l].access & TPM_TIS_ACCESS_REQUEST_USE)) {
188 189 190 191 192 193 194 195 196
            return 1;
        }
    }

    return 0;
}

static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty)
{
197
    bool change = (s->active_locty != new_active_locty);
198 199 200
    bool is_seize;
    uint8_t mask;

201
    if (change && TPM_TIS_IS_VALID_LOCTY(s->active_locty)) {
202
        is_seize = TPM_TIS_IS_VALID_LOCTY(new_active_locty) &&
203
                   s->loc[new_active_locty].access & TPM_TIS_ACCESS_SEIZE;
204 205 206 207 208 209 210 211

        if (is_seize) {
            mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY);
        } else {
            mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY|
                     TPM_TIS_ACCESS_REQUEST_USE);
        }
        /* reset flags on the old active locality */
212
        s->loc[s->active_locty].access &= mask;
213 214

        if (is_seize) {
215
            s->loc[s->active_locty].access |= TPM_TIS_ACCESS_BEEN_SEIZED;
216 217 218
        }
    }

219
    s->active_locty = new_active_locty;
220

221
    trace_tpm_tis_new_active_locality(s->active_locty);
222 223 224

    if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) {
        /* set flags on the new active locality */
225 226
        s->loc[new_active_locty].access |= TPM_TIS_ACCESS_ACTIVE_LOCALITY;
        s->loc[new_active_locty].access &= ~(TPM_TIS_ACCESS_REQUEST_USE |
227 228 229 230
                                               TPM_TIS_ACCESS_SEIZE);
    }

    if (change) {
231
        tpm_tis_raise_irq(s, s->active_locty, TPM_TIS_INT_LOCALITY_CHANGED);
232 233 234 235
    }
}

/* abort -- this function switches the locality */
236
static void tpm_tis_abort(TPMState *s)
237
{
238
    s->rw_offset = 0;
239

240
    trace_tpm_tis_abort(s->next_locty);
241 242 243 244 245

    /*
     * Need to react differently depending on who's aborting now and
     * which locality will become active afterwards.
     */
246 247 248
    if (s->aborting_locty == s->next_locty) {
        s->loc[s->aborting_locty].state = TPM_TIS_STATE_READY;
        tpm_tis_sts_set(&s->loc[s->aborting_locty],
249
                        TPM_TIS_STS_COMMAND_READY);
250
        tpm_tis_raise_irq(s, s->aborting_locty, TPM_TIS_INT_COMMAND_READY);
251 252 253
    }

    /* locality after abort is another one than the current one */
254
    tpm_tis_new_active_locality(s, s->next_locty);
255

256
    s->next_locty = TPM_TIS_NO_LOCALITY;
257
    /* nobody's aborting a command anymore */
258
    s->aborting_locty = TPM_TIS_NO_LOCALITY;
259 260 261 262 263 264 265
}

/* prepare aborting current command */
static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty)
{
    uint8_t busy_locty;

266 267
    s->aborting_locty = locty;
    s->next_locty = newlocty;  /* locality after successful abort */
268 269 270 271 272 273

    /*
     * only abort a command using an interrupt if currently executing
     * a command AND if there's a valid connection to the vTPM.
     */
    for (busy_locty = 0; busy_locty < TPM_TIS_NUM_LOCALITIES; busy_locty++) {
274
        if (s->loc[busy_locty].state == TPM_TIS_STATE_EXECUTION) {
275 276 277 278
            /*
             * request the backend to cancel. Some backends may not
             * support it
             */
S
Stefan Berger 已提交
279
            tpm_backend_cancel_cmd(s->be_driver);
280 281 282 283
            return;
        }
    }

284
    tpm_tis_abort(s);
285 286
}

287 288 289
/*
 * Callback from the TPM to indicate that the response was received.
 */
290
static void tpm_tis_request_completed(TPMIf *ti, int ret)
291
{
292
    TPMState *s = TPM(ti);
293
    uint8_t locty = s->cmd.locty;
294 295 296 297
    uint8_t l;

    if (s->cmd.selftest_done) {
        for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
298
            s->loc[l].sts |= TPM_TIS_STS_SELFTEST_DONE;
299 300
        }
    }
301

302
    /* FIXME: report error if ret != 0 */
303
    tpm_tis_sts_set(&s->loc[locty],
304
                    TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE);
305
    s->loc[locty].state = TPM_TIS_STATE_COMPLETION;
306
    s->rw_offset = 0;
307

308 309 310 311
    if (DEBUG_TIS) {
        tpm_tis_show_buffer(s->buffer, s->be_buffer_size,
                            "tpm_tis: From TPM");
    }
312

313
    if (TPM_TIS_IS_VALID_LOCTY(s->next_locty)) {
314
        tpm_tis_abort(s);
315 316 317 318 319 320 321 322 323 324 325 326 327 328
    }

    tpm_tis_raise_irq(s, locty,
                      TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID);
}

/*
 * Read a byte of response data
 */
static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty)
{
    uint32_t ret = TPM_TIS_NO_DATA_BYTE;
    uint16_t len;

329
    if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
330
        len = MIN(tpm_cmd_get_size(&s->buffer),
331
                  s->be_buffer_size);
332

333 334
        ret = s->buffer[s->rw_offset++];
        if (s->rw_offset >= len) {
335
            /* got last byte */
336
            tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID);
337 338
            tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
        }
339
        trace_tpm_tis_data_read(ret, s->rw_offset - 1);
340 341 342 343 344
    }

    return ret;
}

S
Stefan Berger 已提交
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
#ifdef DEBUG_TIS
static void tpm_tis_dump_state(void *opaque, hwaddr addr)
{
    static const unsigned regs[] = {
        TPM_TIS_REG_ACCESS,
        TPM_TIS_REG_INT_ENABLE,
        TPM_TIS_REG_INT_VECTOR,
        TPM_TIS_REG_INT_STATUS,
        TPM_TIS_REG_INTF_CAPABILITY,
        TPM_TIS_REG_STS,
        TPM_TIS_REG_DID_VID,
        TPM_TIS_REG_RID,
        0xfff};
    int idx;
    uint8_t locty = tpm_tis_locality_from_addr(addr);
    hwaddr base = addr & ~0xfff;
    TPMState *s = opaque;

363 364 365 366 367
    printf("tpm_tis: active locality      : %d\n"
           "tpm_tis: state of locality %d : %d\n"
           "tpm_tis: register dump:\n",
           s->active_locty,
           locty, s->loc[locty].state);
S
Stefan Berger 已提交
368 369

    for (idx = 0; regs[idx] != 0xfff; idx++) {
370 371
        printf("tpm_tis: 0x%04x : 0x%08x\n", regs[idx],
               (int)tpm_tis_mmio_read(opaque, base + regs[idx], 4));
S
Stefan Berger 已提交
372 373
    }

374 375 376
    printf("tpm_tis: r/w offset    : %d\n"
           "tpm_tis: result buffer : ",
           s->rw_offset);
S
Stefan Berger 已提交
377
    for (idx = 0;
378
         idx < MIN(tpm_cmd_get_size(&s->buffer), s->be_buffer_size);
S
Stefan Berger 已提交
379
         idx++) {
380 381 382 383
        printf("%c%02x%s",
               s->rw_offset == idx ? '>' : ' ',
               s->buffer[idx],
               ((idx & 0xf) == 0xf) ? "\ntpm_tis:                 " : "");
S
Stefan Berger 已提交
384
    }
385
    printf("\n");
S
Stefan Berger 已提交
386 387 388
}
#endif

389 390 391 392 393 394 395 396 397 398 399 400 401
/*
 * Read a register of the TIS interface
 * See specs pages 33-63 for description of the registers
 */
static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr,
                                  unsigned size)
{
    TPMState *s = opaque;
    uint16_t offset = addr & 0xffc;
    uint8_t shift = (addr & 0x3) * 8;
    uint32_t val = 0xffffffff;
    uint8_t locty = tpm_tis_locality_from_addr(addr);
    uint32_t avail;
402
    uint8_t v;
403

S
Stefan Berger 已提交
404
    if (tpm_backend_had_startup_error(s->be_driver)) {
405
        return 0;
406 407 408 409 410
    }

    switch (offset) {
    case TPM_TIS_REG_ACCESS:
        /* never show the SEIZE flag even though we use it internally */
411
        val = s->loc[locty].access & ~TPM_TIS_ACCESS_SEIZE;
412 413 414 415
        /* the pending flag is always calculated */
        if (tpm_tis_check_request_use_except(s, locty)) {
            val |= TPM_TIS_ACCESS_PENDING_REQUEST;
        }
S
Stefan Berger 已提交
416
        val |= !tpm_backend_get_tpm_established_flag(s->be_driver);
417 418
        break;
    case TPM_TIS_REG_INT_ENABLE:
419
        val = s->loc[locty].inte;
420 421
        break;
    case TPM_TIS_REG_INT_VECTOR:
422
        val = s->irq_num;
423 424
        break;
    case TPM_TIS_REG_INT_STATUS:
425
        val = s->loc[locty].ints;
426 427
        break;
    case TPM_TIS_REG_INTF_CAPABILITY:
428 429 430 431 432 433 434 435 436 437 438
        switch (s->be_tpm_version) {
        case TPM_VERSION_UNSPEC:
            val = 0;
            break;
        case TPM_VERSION_1_2:
            val = TPM_TIS_CAPABILITIES_SUPPORTED1_3;
            break;
        case TPM_VERSION_2_0:
            val = TPM_TIS_CAPABILITIES_SUPPORTED2_0;
            break;
        }
439 440
        break;
    case TPM_TIS_REG_STS:
441 442
        if (s->active_locty == locty) {
            if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
443
                val = TPM_TIS_BURST_COUNT(
444
                       MIN(tpm_cmd_get_size(&s->buffer),
445
                           s->be_buffer_size)
446
                       - s->rw_offset) | s->loc[locty].sts;
447
            } else {
448
                avail = s->be_buffer_size - s->rw_offset;
449 450 451 452 453 454 455
                /*
                 * byte-sized reads should not return 0x00 for 0x100
                 * available bytes.
                 */
                if (size == 1 && avail > 0xff) {
                    avail = 0xff;
                }
456
                val = TPM_TIS_BURST_COUNT(avail) | s->loc[locty].sts;
457 458 459 460
            }
        }
        break;
    case TPM_TIS_REG_DATA_FIFO:
S
Stefan Berger 已提交
461
    case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END:
462
        if (s->active_locty == locty) {
463 464 465 466 467 468 469
            if (size > 4 - (addr & 0x3)) {
                /* prevent access beyond FIFO */
                size = 4 - (addr & 0x3);
            }
            val = 0;
            shift = 0;
            while (size > 0) {
470
                switch (s->loc[locty].state) {
471 472 473 474 475 476 477 478 479 480
                case TPM_TIS_STATE_COMPLETION:
                    v = tpm_tis_data_read(s, locty);
                    break;
                default:
                    v = TPM_TIS_NO_DATA_BYTE;
                    break;
                }
                val |= (v << shift);
                shift += 8;
                size--;
481
            }
482
            shift = 0; /* no more adjustments */
483 484
        }
        break;
485
    case TPM_TIS_REG_INTERFACE_ID:
486
        val = s->loc[locty].iface_id;
487
        break;
488 489 490 491 492 493
    case TPM_TIS_REG_DID_VID:
        val = (TPM_TIS_TPM_DID << 16) | TPM_TIS_TPM_VID;
        break;
    case TPM_TIS_REG_RID:
        val = TPM_TIS_TPM_RID;
        break;
S
Stefan Berger 已提交
494 495 496 497 498
#ifdef DEBUG_TIS
    case TPM_TIS_REG_DEBUG:
        tpm_tis_dump_state(opaque, addr);
        break;
#endif
499 500 501 502 503 504
    }

    if (shift) {
        val >>= shift;
    }

505
    trace_tpm_tis_mmio_read(size, addr, val);
506 507 508 509 510 511 512 513

    return val;
}

/*
 * Write a value to a register of the TIS interface
 * See specs pages 33-63 for description of the registers
 */
514 515
static void tpm_tis_mmio_write(void *opaque, hwaddr addr,
                               uint64_t val, unsigned size)
516 517
{
    TPMState *s = opaque;
518 519
    uint16_t off = addr & 0xffc;
    uint8_t shift = (addr & 0x3) * 8;
520 521 522 523
    uint8_t locty = tpm_tis_locality_from_addr(addr);
    uint8_t active_locty, l;
    int c, set_new_locty = 1;
    uint16_t len;
524
    uint32_t mask = (size == 1) ? 0xff : ((size == 2) ? 0xffff : ~0);
525

526
    trace_tpm_tis_mmio_write(size, addr, val);
527

528
    if (locty == 4) {
529
        trace_tpm_tis_mmio_write_locty4();
530 531 532
        return;
    }

S
Stefan Berger 已提交
533
    if (tpm_backend_had_startup_error(s->be_driver)) {
534 535 536
        return;
    }

537 538 539 540 541 542 543 544 545
    val &= mask;

    if (shift) {
        val <<= shift;
        mask <<= shift;
    }

    mask ^= 0xffffffff;

546 547 548 549 550 551 552 553
    switch (off) {
    case TPM_TIS_REG_ACCESS:

        if ((val & TPM_TIS_ACCESS_SEIZE)) {
            val &= ~(TPM_TIS_ACCESS_REQUEST_USE |
                     TPM_TIS_ACCESS_ACTIVE_LOCALITY);
        }

554
        active_locty = s->active_locty;
555 556 557

        if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) {
            /* give up locality if currently owned */
558
            if (s->active_locty == locty) {
559
                trace_tpm_tis_mmio_write_release_locty(locty);
560 561 562 563

                uint8_t newlocty = TPM_TIS_NO_LOCALITY;
                /* anybody wants the locality ? */
                for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) {
564
                    if ((s->loc[c].access & TPM_TIS_ACCESS_REQUEST_USE)) {
565
                        trace_tpm_tis_mmio_write_locty_req_use(c);
566 567 568 569
                        newlocty = c;
                        break;
                    }
                }
570
                trace_tpm_tis_mmio_write_next_locty(newlocty);
571 572 573 574 575 576 577 578 579

                if (TPM_TIS_IS_VALID_LOCTY(newlocty)) {
                    set_new_locty = 0;
                    tpm_tis_prep_abort(s, locty, newlocty);
                } else {
                    active_locty = TPM_TIS_NO_LOCALITY;
                }
            } else {
                /* not currently the owner; clear a pending request */
580
                s->loc[locty].access &= ~TPM_TIS_ACCESS_REQUEST_USE;
581 582 583 584
            }
        }

        if ((val & TPM_TIS_ACCESS_BEEN_SEIZED)) {
585
            s->loc[locty].access &= ~TPM_TIS_ACCESS_BEEN_SEIZED;
586 587 588 589 590 591 592 593 594 595
        }

        if ((val & TPM_TIS_ACCESS_SEIZE)) {
            /*
             * allow seize if a locality is active and the requesting
             * locality is higher than the one that's active
             * OR
             * allow seize for requesting locality if no locality is
             * active
             */
596 597 598
            while ((TPM_TIS_IS_VALID_LOCTY(s->active_locty) &&
                    locty > s->active_locty) ||
                    !TPM_TIS_IS_VALID_LOCTY(s->active_locty)) {
599 600 601
                bool higher_seize = FALSE;

                /* already a pending SEIZE ? */
602
                if ((s->loc[locty].access & TPM_TIS_ACCESS_SEIZE)) {
603 604 605 606 607
                    break;
                }

                /* check for ongoing seize by a higher locality */
                for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES; l++) {
608
                    if ((s->loc[l].access & TPM_TIS_ACCESS_SEIZE)) {
609 610 611 612 613 614 615 616 617 618 619
                        higher_seize = TRUE;
                        break;
                    }
                }

                if (higher_seize) {
                    break;
                }

                /* cancel any seize by a lower locality */
                for (l = 0; l < locty - 1; l++) {
620
                    s->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE;
621 622
                }

623
                s->loc[locty].access |= TPM_TIS_ACCESS_SEIZE;
624 625 626 627

                trace_tpm_tis_mmio_write_locty_seized(locty, s->active_locty);
                trace_tpm_tis_mmio_write_init_abort();

628
                set_new_locty = 0;
629
                tpm_tis_prep_abort(s, s->active_locty, locty);
630 631 632 633 634
                break;
            }
        }

        if ((val & TPM_TIS_ACCESS_REQUEST_USE)) {
635 636 637
            if (s->active_locty != locty) {
                if (TPM_TIS_IS_VALID_LOCTY(s->active_locty)) {
                    s->loc[locty].access |= TPM_TIS_ACCESS_REQUEST_USE;
638 639 640 641 642 643 644 645 646 647 648 649 650
                } else {
                    /* no locality active -> make this one active now */
                    active_locty = locty;
                }
            }
        }

        if (set_new_locty) {
            tpm_tis_new_active_locality(s, active_locty);
        }

        break;
    case TPM_TIS_REG_INT_ENABLE:
651
        if (s->active_locty != locty) {
652 653 654
            break;
        }

655 656
        s->loc[locty].inte &= mask;
        s->loc[locty].inte |= (val & (TPM_TIS_INT_ENABLED |
657 658
                                        TPM_TIS_INT_POLARITY_MASK |
                                        TPM_TIS_INTERRUPTS_SUPPORTED));
659 660 661 662 663
        break;
    case TPM_TIS_REG_INT_VECTOR:
        /* hard wired -- ignore */
        break;
    case TPM_TIS_REG_INT_STATUS:
664
        if (s->active_locty != locty) {
665 666 667 668 669
            break;
        }

        /* clearing of interrupt flags */
        if (((val & TPM_TIS_INTERRUPTS_SUPPORTED)) &&
670 671 672 673
            (s->loc[locty].ints & TPM_TIS_INTERRUPTS_SUPPORTED)) {
            s->loc[locty].ints &= ~val;
            if (s->loc[locty].ints == 0) {
                qemu_irq_lower(s->irq);
674
                trace_tpm_tis_mmio_write_lowering_irq();
675 676
            }
        }
677
        s->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED);
678 679
        break;
    case TPM_TIS_REG_STS:
680
        if (s->active_locty != locty) {
681 682 683
            break;
        }

684 685 686
        if (s->be_tpm_version == TPM_VERSION_2_0) {
            /* some flags that are only supported for TPM 2 */
            if (val & TPM_TIS_STS_COMMAND_CANCEL) {
687
                if (s->loc[locty].state == TPM_TIS_STATE_EXECUTION) {
688 689 690 691 692 693 694 695 696 697 698 699 700 701 702
                    /*
                     * request the backend to cancel. Some backends may not
                     * support it
                     */
                    tpm_backend_cancel_cmd(s->be_driver);
                }
            }

            if (val & TPM_TIS_STS_RESET_ESTABLISHMENT_BIT) {
                if (locty == 3 || locty == 4) {
                    tpm_backend_reset_tpm_established_flag(s->be_driver, locty);
                }
            }
        }

703 704 705 706
        val &= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO |
                TPM_TIS_STS_RESPONSE_RETRY);

        if (val == TPM_TIS_STS_COMMAND_READY) {
707
            switch (s->loc[locty].state) {
708 709

            case TPM_TIS_STATE_READY:
710
                s->rw_offset = 0;
711 712 713
            break;

            case TPM_TIS_STATE_IDLE:
714 715
                tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_COMMAND_READY);
                s->loc[locty].state = TPM_TIS_STATE_READY;
716 717 718 719 720 721
                tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
            break;

            case TPM_TIS_STATE_EXECUTION:
            case TPM_TIS_STATE_RECEPTION:
                /* abort currently running command */
722
                trace_tpm_tis_mmio_write_init_abort();
723 724 725 726
                tpm_tis_prep_abort(s, locty, locty);
            break;

            case TPM_TIS_STATE_COMPLETION:
727
                s->rw_offset = 0;
728
                /* shortcut to ready state with C/R set */
729 730 731
                s->loc[locty].state = TPM_TIS_STATE_READY;
                if (!(s->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) {
                    tpm_tis_sts_set(&s->loc[locty],
732
                                    TPM_TIS_STS_COMMAND_READY);
733 734
                    tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
                }
735
                s->loc[locty].sts &= ~(TPM_TIS_STS_DATA_AVAILABLE);
736 737 738 739
            break;

            }
        } else if (val == TPM_TIS_STS_TPM_GO) {
740
            switch (s->loc[locty].state) {
741
            case TPM_TIS_STATE_RECEPTION:
742
                if ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) == 0) {
743 744 745 746 747 748 749 750
                    tpm_tis_tpm_send(s, locty);
                }
                break;
            default:
                /* ignore */
                break;
            }
        } else if (val == TPM_TIS_STS_RESPONSE_RETRY) {
751
            switch (s->loc[locty].state) {
752
            case TPM_TIS_STATE_COMPLETION:
753
                s->rw_offset = 0;
754
                tpm_tis_sts_set(&s->loc[locty],
755 756
                                TPM_TIS_STS_VALID|
                                TPM_TIS_STS_DATA_AVAILABLE);
757 758 759 760 761 762 763 764
                break;
            default:
                /* ignore */
                break;
            }
        }
        break;
    case TPM_TIS_REG_DATA_FIFO:
S
Stefan Berger 已提交
765
    case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END:
766
        /* data fifo */
767
        if (s->active_locty != locty) {
768 769 770
            break;
        }

771 772 773
        if (s->loc[locty].state == TPM_TIS_STATE_IDLE ||
            s->loc[locty].state == TPM_TIS_STATE_EXECUTION ||
            s->loc[locty].state == TPM_TIS_STATE_COMPLETION) {
774 775
            /* drop the byte */
        } else {
776
            trace_tpm_tis_mmio_write_data2send(val, size);
777 778 779
            if (s->loc[locty].state == TPM_TIS_STATE_READY) {
                s->loc[locty].state = TPM_TIS_STATE_RECEPTION;
                tpm_tis_sts_set(&s->loc[locty],
780
                                TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID);
781 782
            }

783 784 785 786 787 788
            val >>= shift;
            if (size > 4 - (addr & 0x3)) {
                /* prevent access beyond FIFO */
                size = 4 - (addr & 0x3);
            }

789
            while ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) && size > 0) {
790 791
                if (s->rw_offset < s->be_buffer_size) {
                    s->buffer[s->rw_offset++] =
792
                        (uint8_t)val;
793 794
                    val >>= 8;
                    size--;
795
                } else {
796
                    tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID);
797 798 799 800
                }
            }

            /* check for complete packet */
801
            if (s->rw_offset > 5 &&
802
                (s->loc[locty].sts & TPM_TIS_STS_EXPECT)) {
803
                /* we have a packet length - see if we have all of it */
804
                bool need_irq = !(s->loc[locty].sts & TPM_TIS_STS_VALID);
M
Marc-André Lureau 已提交
805

806
                len = tpm_cmd_get_size(&s->buffer);
807
                if (len > s->rw_offset) {
808
                    tpm_tis_sts_set(&s->loc[locty],
809
                                    TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID);
810 811
                } else {
                    /* packet complete */
812
                    tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID);
813
                }
S
Stefan Berger 已提交
814
                if (need_irq) {
815 816 817 818 819
                    tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
                }
            }
        }
        break;
820 821 822
    case TPM_TIS_REG_INTERFACE_ID:
        if (val & TPM_TIS_IFACE_ID_INT_SEL_LOCK) {
            for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
823
                s->loc[l].iface_id |= TPM_TIS_IFACE_ID_INT_SEL_LOCK;
824 825 826
            }
        }
        break;
827 828 829 830 831 832 833 834 835 836 837 838 839
    }
}

static const MemoryRegionOps tpm_tis_memory_ops = {
    .read = tpm_tis_mmio_read,
    .write = tpm_tis_mmio_write,
    .endianness = DEVICE_LITTLE_ENDIAN,
    .valid = {
        .min_access_size = 1,
        .max_access_size = 4,
    },
};

S
Stefan Berger 已提交
840 841 842
/*
 * Get the TPMVersion of the backend device being used
 */
843
static enum TPMVersion tpm_tis_get_tpm_version(TPMIf *ti)
S
Stefan Berger 已提交
844
{
845
    TPMState *s = TPM(ti);
S
Stefan Berger 已提交
846

847 848 849 850
    if (tpm_backend_had_startup_error(s->be_driver)) {
        return TPM_VERSION_UNSPEC;
    }

S
Stefan Berger 已提交
851 852 853
    return tpm_backend_get_tpm_version(s->be_driver);
}

854 855 856 857 858 859 860 861 862
/*
 * This function is called when the machine starts, resets or due to
 * S3 resume.
 */
static void tpm_tis_reset(DeviceState *dev)
{
    TPMState *s = TPM(dev);
    int c;

863
    s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver);
864 865
    s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->be_driver),
                            TPM_TIS_BUFFER_MAX);
866

S
Stefan Berger 已提交
867
    tpm_backend_reset(s->be_driver);
868

869 870 871
    s->active_locty = TPM_TIS_NO_LOCALITY;
    s->next_locty = TPM_TIS_NO_LOCALITY;
    s->aborting_locty = TPM_TIS_NO_LOCALITY;
872 873

    for (c = 0; c < TPM_TIS_NUM_LOCALITIES; c++) {
874
        s->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS;
875 876 877 878
        switch (s->be_tpm_version) {
        case TPM_VERSION_UNSPEC:
            break;
        case TPM_VERSION_1_2:
879 880
            s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY1_2;
            s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3;
881 882
            break;
        case TPM_VERSION_2_0:
883 884
            s->loc[c].sts = TPM_TIS_STS_TPM_FAMILY2_0;
            s->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0;
885 886
            break;
        }
887 888 889 890
        s->loc[c].inte = TPM_TIS_INT_POLARITY_LOW_LEVEL;
        s->loc[c].ints = 0;
        s->loc[c].state = TPM_TIS_STATE_IDLE;

891
        s->rw_offset = 0;
892 893
    }

894
    tpm_backend_startup_tpm(s->be_driver, s->be_buffer_size);
895 896
}

897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931
/* persistent state handling */

static int tpm_tis_pre_save(void *opaque)
{
    TPMState *s = opaque;
    uint8_t locty = s->active_locty;

    trace_tpm_tis_pre_save(locty, s->rw_offset);

    if (DEBUG_TIS) {
        tpm_tis_dump_state(opaque, 0);
    }

    /*
     * Synchronize with backend completion.
     */
    tpm_backend_finish_sync(s->be_driver);

    return 0;
}

static const VMStateDescription vmstate_locty = {
    .name = "tpm-tis/locty",
    .version_id = 0,
    .fields      = (VMStateField[]) {
        VMSTATE_UINT32(state, TPMLocality),
        VMSTATE_UINT32(inte, TPMLocality),
        VMSTATE_UINT32(ints, TPMLocality),
        VMSTATE_UINT8(access, TPMLocality),
        VMSTATE_UINT32(sts, TPMLocality),
        VMSTATE_UINT32(iface_id, TPMLocality),
        VMSTATE_END_OF_LIST(),
    }
};

932
static const VMStateDescription vmstate_tpm_tis = {
933 934 935 936 937 938 939 940 941 942 943 944 945 946 947
    .name = "tpm-tis",
    .version_id = 0,
    .pre_save  = tpm_tis_pre_save,
    .fields = (VMStateField[]) {
        VMSTATE_BUFFER(buffer, TPMState),
        VMSTATE_UINT16(rw_offset, TPMState),
        VMSTATE_UINT8(active_locty, TPMState),
        VMSTATE_UINT8(aborting_locty, TPMState),
        VMSTATE_UINT8(next_locty, TPMState),

        VMSTATE_STRUCT_ARRAY(loc, TPMState, TPM_TIS_NUM_LOCALITIES, 0,
                             vmstate_locty, TPMLocality),

        VMSTATE_END_OF_LIST()
    }
948 949 950
};

static Property tpm_tis_properties[] = {
951
    DEFINE_PROP_UINT32("irq", TPMState, irq_num, TPM_TIS_IRQ),
952
    DEFINE_PROP_TPMBE("tpmdev", TPMState, be_driver),
953 954 955 956 957 958 959
    DEFINE_PROP_END_OF_LIST(),
};

static void tpm_tis_realizefn(DeviceState *dev, Error **errp)
{
    TPMState *s = TPM(dev);

960 961 962 963 964
    if (!tpm_find()) {
        error_setg(errp, "at most one TPM device is permitted");
        return;
    }

965
    if (!s->be_driver) {
966
        error_setg(errp, "'tpmdev' property is required");
967 968
        return;
    }
969
    if (s->irq_num > 15) {
970 971
        error_setg(errp, "IRQ %d is outside valid range of 0 to 15",
                   s->irq_num);
972 973 974
        return;
    }

975
    isa_init_irq(&s->busdev, &s->irq, s->irq_num);
976 977 978

    memory_region_add_subregion(isa_address_space(ISA_DEVICE(dev)),
                                TPM_TIS_ADDR_BASE, &s->mmio);
979 980 981 982 983 984
}

static void tpm_tis_initfn(Object *obj)
{
    TPMState *s = TPM(obj);

985 986
    memory_region_init_io(&s->mmio, OBJECT(s), &tpm_tis_memory_ops,
                          s, "tpm-tis-mmio",
987 988 989 990 991 992
                          TPM_TIS_NUM_LOCALITIES << TPM_TIS_LOCALITY_SHIFT);
}

static void tpm_tis_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);
993
    TPMIfClass *tc = TPM_IF_CLASS(klass);
994 995 996 997 998

    dc->realize = tpm_tis_realizefn;
    dc->props = tpm_tis_properties;
    dc->reset = tpm_tis_reset;
    dc->vmsd  = &vmstate_tpm_tis;
999
    tc->model = TPM_MODEL_TPM_TIS;
1000
    tc->get_version = tpm_tis_get_tpm_version;
1001
    tc->request_completed = tpm_tis_request_completed;
1002 1003 1004 1005 1006 1007 1008 1009
}

static const TypeInfo tpm_tis_info = {
    .name = TYPE_TPM_TIS,
    .parent = TYPE_ISA_DEVICE,
    .instance_size = sizeof(TPMState),
    .instance_init = tpm_tis_initfn,
    .class_init  = tpm_tis_class_init,
M
Marc-André Lureau 已提交
1010 1011 1012 1013
    .interfaces = (InterfaceInfo[]) {
        { TYPE_TPM_IF },
        { }
    }
1014 1015 1016 1017 1018 1019 1020 1021
};

static void tpm_tis_register(void)
{
    type_register_static(&tpm_tis_info);
}

type_init(tpm_tis_register)