qemu/hw/tpm/tpm_passthrough.c
<<
>>
Prefs
   1/*
   2 *  passthrough TPM driver
   3 *
   4 *  Copyright (c) 2010 - 2013 IBM Corporation
   5 *  Authors:
   6 *    Stefan Berger <stefanb@us.ibm.com>
   7 *
   8 *  Copyright (C) 2011 IAIK, Graz University of Technology
   9 *    Author: Andreas Niederl
  10 *
  11 * This library is free software; you can redistribute it and/or
  12 * modify it under the terms of the GNU Lesser General Public
  13 * License as published by the Free Software Foundation; either
  14 * version 2 of the License, or (at your option) any later version.
  15 *
  16 * This library is distributed in the hope that it will be useful,
  17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  19 * Lesser General Public License for more details.
  20 *
  21 * You should have received a copy of the GNU Lesser General Public
  22 * License along with this library; if not, see <http://www.gnu.org/licenses/>
  23 */
  24
  25#include "qemu/osdep.h"
  26#include "qemu-common.h"
  27#include "qemu/error-report.h"
  28#include "qemu/sockets.h"
  29#include "sysemu/tpm_backend.h"
  30#include "tpm_int.h"
  31#include "hw/hw.h"
  32#include "hw/i386/pc.h"
  33#include "sysemu/tpm_backend_int.h"
  34#include "tpm_tis.h"
  35#include "tpm_util.h"
  36
  37#define DEBUG_TPM 0
  38
  39#define DPRINTF(fmt, ...) do { \
  40    if (DEBUG_TPM) { \
  41        fprintf(stderr, fmt, ## __VA_ARGS__); \
  42    } \
  43} while (0);
  44
  45#define TYPE_TPM_PASSTHROUGH "tpm-passthrough"
  46#define TPM_PASSTHROUGH(obj) \
  47    OBJECT_CHECK(TPMPassthruState, (obj), TYPE_TPM_PASSTHROUGH)
  48
  49static const TPMDriverOps tpm_passthrough_driver;
  50
  51/* data structures */
  52typedef struct TPMPassthruThreadParams {
  53    TPMState *tpm_state;
  54
  55    TPMRecvDataCB *recv_data_callback;
  56    TPMBackend *tb;
  57} TPMPassthruThreadParams;
  58
  59struct TPMPassthruState {
  60    TPMBackend parent;
  61
  62    TPMBackendThread tbt;
  63
  64    TPMPassthruThreadParams tpm_thread_params;
  65
  66    char *tpm_dev;
  67    int tpm_fd;
  68    bool tpm_executing;
  69    bool tpm_op_canceled;
  70    int cancel_fd;
  71    bool had_startup_error;
  72
  73    TPMVersion tpm_version;
  74};
  75
  76typedef struct TPMPassthruState TPMPassthruState;
  77
  78#define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0"
  79
  80/* functions */
  81
  82static void tpm_passthrough_cancel_cmd(TPMBackend *tb);
  83
  84static int tpm_passthrough_unix_write(int fd, const uint8_t *buf, uint32_t len)
  85{
  86    int ret, remain;
  87
  88    remain = len;
  89    while (remain > 0) {
  90        ret = write(fd, buf, remain);
  91        if (ret < 0) {
  92            if (errno != EINTR && errno != EAGAIN) {
  93                return -1;
  94            }
  95        } else if (ret == 0) {
  96            break;
  97        } else {
  98            buf += ret;
  99            remain -= ret;
 100        }
 101    }
 102    return len - remain;
 103}
 104
 105static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
 106{
 107    int ret;
 108 reread:
 109    ret = read(fd, buf, len);
 110    if (ret < 0) {
 111        if (errno != EINTR && errno != EAGAIN) {
 112            return -1;
 113        }
 114        goto reread;
 115    }
 116    return ret;
 117}
 118
 119static uint32_t tpm_passthrough_get_size_from_buffer(const uint8_t *buf)
 120{
 121    struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)buf;
 122
 123    return be32_to_cpu(resp->len);
 124}
 125
 126/*
 127 * Write an error message in the given output buffer.
 128 */
 129static void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len)
 130{
 131    if (out_len >= sizeof(struct tpm_resp_hdr)) {
 132        struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)out;
 133
 134        resp->tag = cpu_to_be16(TPM_TAG_RSP_COMMAND);
 135        resp->len = cpu_to_be32(sizeof(struct tpm_resp_hdr));
 136        resp->errcode = cpu_to_be32(TPM_FAIL);
 137    }
 138}
 139
 140static bool tpm_passthrough_is_selftest(const uint8_t *in, uint32_t in_len)
 141{
 142    struct tpm_req_hdr *hdr = (struct tpm_req_hdr *)in;
 143
 144    if (in_len >= sizeof(*hdr)) {
 145        return (be32_to_cpu(hdr->ordinal) == TPM_ORD_ContinueSelfTest);
 146    }
 147
 148    return false;
 149}
 150
 151static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
 152                                        const uint8_t *in, uint32_t in_len,
 153                                        uint8_t *out, uint32_t out_len,
 154                                        bool *selftest_done)
 155{
 156    int ret;
 157    bool is_selftest;
 158    const struct tpm_resp_hdr *hdr;
 159
 160    tpm_pt->tpm_op_canceled = false;
 161    tpm_pt->tpm_executing = true;
 162    *selftest_done = false;
 163
 164    is_selftest = tpm_passthrough_is_selftest(in, in_len);
 165
 166    ret = tpm_passthrough_unix_write(tpm_pt->tpm_fd, in, in_len);
 167    if (ret != in_len) {
 168        if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) {
 169            error_report("tpm_passthrough: error while transmitting data "
 170                         "to TPM: %s (%i)",
 171                         strerror(errno), errno);
 172        }
 173        goto err_exit;
 174    }
 175
 176    tpm_pt->tpm_executing = false;
 177
 178    ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len);
 179    if (ret < 0) {
 180        if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) {
 181            error_report("tpm_passthrough: error while reading data from "
 182                         "TPM: %s (%i)",
 183                         strerror(errno), errno);
 184        }
 185    } else if (ret < sizeof(struct tpm_resp_hdr) ||
 186               tpm_passthrough_get_size_from_buffer(out) != ret) {
 187        ret = -1;
 188        error_report("tpm_passthrough: received invalid response "
 189                     "packet from TPM");
 190    }
 191
 192    if (is_selftest && (ret >= sizeof(struct tpm_resp_hdr))) {
 193        hdr = (struct tpm_resp_hdr *)out;
 194        *selftest_done = (be32_to_cpu(hdr->errcode) == 0);
 195    }
 196
 197err_exit:
 198    if (ret < 0) {
 199        tpm_write_fatal_error_response(out, out_len);
 200    }
 201
 202    tpm_pt->tpm_executing = false;
 203
 204    return ret;
 205}
 206
 207static int tpm_passthrough_unix_transfer(TPMPassthruState *tpm_pt,
 208                                         const TPMLocality *locty_data,
 209                                         bool *selftest_done)
 210{
 211    return tpm_passthrough_unix_tx_bufs(tpm_pt,
 212                                        locty_data->w_buffer.buffer,
 213                                        locty_data->w_offset,
 214                                        locty_data->r_buffer.buffer,
 215                                        locty_data->r_buffer.size,
 216                                        selftest_done);
 217}
 218
 219static void tpm_passthrough_worker_thread(gpointer data,
 220                                          gpointer user_data)
 221{
 222    TPMPassthruThreadParams *thr_parms = user_data;
 223    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(thr_parms->tb);
 224    TPMBackendCmd cmd = (TPMBackendCmd)data;
 225    bool selftest_done = false;
 226
 227    DPRINTF("tpm_passthrough: processing command type %d\n", cmd);
 228
 229    switch (cmd) {
 230    case TPM_BACKEND_CMD_PROCESS_CMD:
 231        tpm_passthrough_unix_transfer(tpm_pt,
 232                                      thr_parms->tpm_state->locty_data,
 233                                      &selftest_done);
 234
 235        thr_parms->recv_data_callback(thr_parms->tpm_state,
 236                                      thr_parms->tpm_state->locty_number,
 237                                      selftest_done);
 238        break;
 239    case TPM_BACKEND_CMD_INIT:
 240    case TPM_BACKEND_CMD_END:
 241    case TPM_BACKEND_CMD_TPM_RESET:
 242        /* nothing to do */
 243        break;
 244    }
 245}
 246
 247/*
 248 * Start the TPM (thread). If it had been started before, then terminate
 249 * and start it again.
 250 */
 251static int tpm_passthrough_startup_tpm(TPMBackend *tb)
 252{
 253    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 254
 255    /* terminate a running TPM */
 256    tpm_backend_thread_end(&tpm_pt->tbt);
 257
 258    tpm_backend_thread_create(&tpm_pt->tbt,
 259                              tpm_passthrough_worker_thread,
 260                              &tpm_pt->tpm_thread_params);
 261
 262    return 0;
 263}
 264
 265static void tpm_passthrough_reset(TPMBackend *tb)
 266{
 267    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 268
 269    DPRINTF("tpm_passthrough: CALL TO TPM_RESET!\n");
 270
 271    tpm_passthrough_cancel_cmd(tb);
 272
 273    tpm_backend_thread_end(&tpm_pt->tbt);
 274
 275    tpm_pt->had_startup_error = false;
 276}
 277
 278static int tpm_passthrough_init(TPMBackend *tb, TPMState *s,
 279                                TPMRecvDataCB *recv_data_cb)
 280{
 281    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 282
 283    tpm_pt->tpm_thread_params.tpm_state = s;
 284    tpm_pt->tpm_thread_params.recv_data_callback = recv_data_cb;
 285    tpm_pt->tpm_thread_params.tb = tb;
 286
 287    return 0;
 288}
 289
 290static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb)
 291{
 292    return false;
 293}
 294
 295static int tpm_passthrough_reset_tpm_established_flag(TPMBackend *tb,
 296                                                      uint8_t locty)
 297{
 298    /* only a TPM 2.0 will support this */
 299    return 0;
 300}
 301
 302static bool tpm_passthrough_get_startup_error(TPMBackend *tb)
 303{
 304    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 305
 306    return tpm_pt->had_startup_error;
 307}
 308
 309static size_t tpm_passthrough_realloc_buffer(TPMSizedBuffer *sb)
 310{
 311    size_t wanted_size = 4096; /* Linux tpm.c buffer size */
 312
 313    if (sb->size != wanted_size) {
 314        sb->buffer = g_realloc(sb->buffer, wanted_size);
 315        sb->size = wanted_size;
 316    }
 317    return sb->size;
 318}
 319
 320static void tpm_passthrough_deliver_request(TPMBackend *tb)
 321{
 322    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 323
 324    tpm_backend_thread_deliver_request(&tpm_pt->tbt);
 325}
 326
 327static void tpm_passthrough_cancel_cmd(TPMBackend *tb)
 328{
 329    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 330    int n;
 331
 332    /*
 333     * As of Linux 3.7 the tpm_tis driver does not properly cancel
 334     * commands on all TPM manufacturers' TPMs.
 335     * Only cancel if we're busy so we don't cancel someone else's
 336     * command, e.g., a command executed on the host.
 337     */
 338    if (tpm_pt->tpm_executing) {
 339        if (tpm_pt->cancel_fd >= 0) {
 340            n = write(tpm_pt->cancel_fd, "-", 1);
 341            if (n != 1) {
 342                error_report("Canceling TPM command failed: %s",
 343                             strerror(errno));
 344            } else {
 345                tpm_pt->tpm_op_canceled = true;
 346            }
 347        } else {
 348            error_report("Cannot cancel TPM command due to missing "
 349                         "TPM sysfs cancel entry");
 350        }
 351    }
 352}
 353
 354static const char *tpm_passthrough_create_desc(void)
 355{
 356    return "Passthrough TPM backend driver";
 357}
 358
 359static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb)
 360{
 361    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 362
 363    return tpm_pt->tpm_version;
 364}
 365
 366/*
 367 * Unless path or file descriptor set has been provided by user,
 368 * determine the sysfs cancel file following kernel documentation
 369 * in Documentation/ABI/stable/sysfs-class-tpm.
 370 * From /dev/tpm0 create /sys/class/misc/tpm0/device/cancel
 371 */
 372static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb)
 373{
 374    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 375    int fd = -1;
 376    char *dev;
 377    char path[PATH_MAX];
 378
 379    if (tb->cancel_path) {
 380        fd = qemu_open(tb->cancel_path, O_WRONLY);
 381        if (fd < 0) {
 382            error_report("Could not open TPM cancel path : %s",
 383                         strerror(errno));
 384        }
 385        return fd;
 386    }
 387
 388    dev = strrchr(tpm_pt->tpm_dev, '/');
 389    if (dev) {
 390        dev++;
 391        if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel",
 392                     dev) < sizeof(path)) {
 393            fd = qemu_open(path, O_WRONLY);
 394            if (fd >= 0) {
 395                tb->cancel_path = g_strdup(path);
 396            } else {
 397                error_report("tpm_passthrough: Could not open TPM cancel "
 398                             "path %s : %s", path, strerror(errno));
 399            }
 400        }
 401    } else {
 402       error_report("tpm_passthrough: Bad TPM device path %s",
 403                    tpm_pt->tpm_dev);
 404    }
 405
 406    return fd;
 407}
 408
 409static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
 410{
 411    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 412    const char *value;
 413
 414    value = qemu_opt_get(opts, "cancel-path");
 415    tb->cancel_path = g_strdup(value);
 416
 417    value = qemu_opt_get(opts, "path");
 418    if (!value) {
 419        value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
 420    }
 421
 422    tpm_pt->tpm_dev = g_strdup(value);
 423
 424    tb->path = g_strdup(tpm_pt->tpm_dev);
 425
 426    tpm_pt->tpm_fd = qemu_open(tpm_pt->tpm_dev, O_RDWR);
 427    if (tpm_pt->tpm_fd < 0) {
 428        error_report("Cannot access TPM device using '%s': %s",
 429                     tpm_pt->tpm_dev, strerror(errno));
 430        goto err_free_parameters;
 431    }
 432
 433    if (tpm_util_test_tpmdev(tpm_pt->tpm_fd, &tpm_pt->tpm_version)) {
 434        error_report("'%s' is not a TPM device.",
 435                     tpm_pt->tpm_dev);
 436        goto err_close_tpmdev;
 437    }
 438
 439    return 0;
 440
 441 err_close_tpmdev:
 442    qemu_close(tpm_pt->tpm_fd);
 443    tpm_pt->tpm_fd = -1;
 444
 445 err_free_parameters:
 446    g_free(tb->path);
 447    tb->path = NULL;
 448
 449    g_free(tpm_pt->tpm_dev);
 450    tpm_pt->tpm_dev = NULL;
 451
 452    return 1;
 453}
 454
 455static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
 456{
 457    Object *obj = object_new(TYPE_TPM_PASSTHROUGH);
 458    TPMBackend *tb = TPM_BACKEND(obj);
 459    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 460
 461    tb->id = g_strdup(id);
 462    /* let frontend set the fe_model to proper value */
 463    tb->fe_model = -1;
 464
 465    tb->ops = &tpm_passthrough_driver;
 466
 467    if (tpm_passthrough_handle_device_opts(opts, tb)) {
 468        goto err_exit;
 469    }
 470
 471    tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tb);
 472    if (tpm_pt->cancel_fd < 0) {
 473        goto err_exit;
 474    }
 475
 476    return tb;
 477
 478err_exit:
 479    g_free(tb->id);
 480
 481    return NULL;
 482}
 483
 484static void tpm_passthrough_destroy(TPMBackend *tb)
 485{
 486    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 487
 488    tpm_passthrough_cancel_cmd(tb);
 489
 490    tpm_backend_thread_end(&tpm_pt->tbt);
 491
 492    qemu_close(tpm_pt->tpm_fd);
 493    qemu_close(tpm_pt->cancel_fd);
 494
 495    g_free(tb->id);
 496    g_free(tb->path);
 497    g_free(tb->cancel_path);
 498    g_free(tpm_pt->tpm_dev);
 499}
 500
 501static const QemuOptDesc tpm_passthrough_cmdline_opts[] = {
 502    TPM_STANDARD_CMDLINE_OPTS,
 503    {
 504        .name = "cancel-path",
 505        .type = QEMU_OPT_STRING,
 506        .help = "Sysfs file entry for canceling TPM commands",
 507    },
 508    {
 509        .name = "path",
 510        .type = QEMU_OPT_STRING,
 511        .help = "Path to TPM device on the host",
 512    },
 513    { /* end of list */ },
 514};
 515
 516static const TPMDriverOps tpm_passthrough_driver = {
 517    .type                     = TPM_TYPE_PASSTHROUGH,
 518    .opts                     = tpm_passthrough_cmdline_opts,
 519    .desc                     = tpm_passthrough_create_desc,
 520    .create                   = tpm_passthrough_create,
 521    .destroy                  = tpm_passthrough_destroy,
 522    .init                     = tpm_passthrough_init,
 523    .startup_tpm              = tpm_passthrough_startup_tpm,
 524    .realloc_buffer           = tpm_passthrough_realloc_buffer,
 525    .reset                    = tpm_passthrough_reset,
 526    .had_startup_error        = tpm_passthrough_get_startup_error,
 527    .deliver_request          = tpm_passthrough_deliver_request,
 528    .cancel_cmd               = tpm_passthrough_cancel_cmd,
 529    .get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag,
 530    .reset_tpm_established_flag = tpm_passthrough_reset_tpm_established_flag,
 531    .get_tpm_version          = tpm_passthrough_get_tpm_version,
 532};
 533
 534static void tpm_passthrough_inst_init(Object *obj)
 535{
 536}
 537
 538static void tpm_passthrough_inst_finalize(Object *obj)
 539{
 540}
 541
 542static void tpm_passthrough_class_init(ObjectClass *klass, void *data)
 543{
 544    TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);
 545
 546    tbc->ops = &tpm_passthrough_driver;
 547}
 548
 549static const TypeInfo tpm_passthrough_info = {
 550    .name = TYPE_TPM_PASSTHROUGH,
 551    .parent = TYPE_TPM_BACKEND,
 552    .instance_size = sizeof(TPMPassthruState),
 553    .class_init = tpm_passthrough_class_init,
 554    .instance_init = tpm_passthrough_inst_init,
 555    .instance_finalize = tpm_passthrough_inst_finalize,
 556};
 557
 558static void tpm_passthrough_register(void)
 559{
 560    type_register_static(&tpm_passthrough_info);
 561    tpm_register_driver(&tpm_passthrough_driver);
 562}
 563
 564type_init(tpm_passthrough_register)
 565