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