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 ||
 169            (tpm_pt->tpm_op_canceled && errno != ECANCELED)) {
 170            error_report("tpm_passthrough: error while transmitting data "
 171                         "to TPM: %s (%i)",
 172                         strerror(errno), errno);
 173        }
 174        goto err_exit;
 175    }
 176
 177    tpm_pt->tpm_executing = false;
 178
 179    ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len);
 180    if (ret < 0) {
 181        if (!tpm_pt->tpm_op_canceled ||
 182            (tpm_pt->tpm_op_canceled && errno != ECANCELED)) {
 183            error_report("tpm_passthrough: error while reading data from "
 184                         "TPM: %s (%i)",
 185                         strerror(errno), errno);
 186        }
 187    } else if (ret < sizeof(struct tpm_resp_hdr) ||
 188               tpm_passthrough_get_size_from_buffer(out) != ret) {
 189        ret = -1;
 190        error_report("tpm_passthrough: received invalid response "
 191                     "packet from TPM");
 192    }
 193
 194    if (is_selftest && (ret >= sizeof(struct tpm_resp_hdr))) {
 195        hdr = (struct tpm_resp_hdr *)out;
 196        *selftest_done = (be32_to_cpu(hdr->errcode) == 0);
 197    }
 198
 199err_exit:
 200    if (ret < 0) {
 201        tpm_write_fatal_error_response(out, out_len);
 202    }
 203
 204    tpm_pt->tpm_executing = false;
 205
 206    return ret;
 207}
 208
 209static int tpm_passthrough_unix_transfer(TPMPassthruState *tpm_pt,
 210                                         const TPMLocality *locty_data,
 211                                         bool *selftest_done)
 212{
 213    return tpm_passthrough_unix_tx_bufs(tpm_pt,
 214                                        locty_data->w_buffer.buffer,
 215                                        locty_data->w_offset,
 216                                        locty_data->r_buffer.buffer,
 217                                        locty_data->r_buffer.size,
 218                                        selftest_done);
 219}
 220
 221static void tpm_passthrough_worker_thread(gpointer data,
 222                                          gpointer user_data)
 223{
 224    TPMPassthruThreadParams *thr_parms = user_data;
 225    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(thr_parms->tb);
 226    TPMBackendCmd cmd = (TPMBackendCmd)data;
 227    bool selftest_done = false;
 228
 229    DPRINTF("tpm_passthrough: processing command type %d\n", cmd);
 230
 231    switch (cmd) {
 232    case TPM_BACKEND_CMD_PROCESS_CMD:
 233        tpm_passthrough_unix_transfer(tpm_pt,
 234                                      thr_parms->tpm_state->locty_data,
 235                                      &selftest_done);
 236
 237        thr_parms->recv_data_callback(thr_parms->tpm_state,
 238                                      thr_parms->tpm_state->locty_number,
 239                                      selftest_done);
 240        break;
 241    case TPM_BACKEND_CMD_INIT:
 242    case TPM_BACKEND_CMD_END:
 243    case TPM_BACKEND_CMD_TPM_RESET:
 244        /* nothing to do */
 245        break;
 246    }
 247}
 248
 249/*
 250 * Start the TPM (thread). If it had been started before, then terminate
 251 * and start it again.
 252 */
 253static int tpm_passthrough_startup_tpm(TPMBackend *tb)
 254{
 255    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 256
 257    /* terminate a running TPM */
 258    tpm_backend_thread_end(&tpm_pt->tbt);
 259
 260    tpm_backend_thread_create(&tpm_pt->tbt,
 261                              tpm_passthrough_worker_thread,
 262                              &tpm_pt->tpm_thread_params);
 263
 264    return 0;
 265}
 266
 267static void tpm_passthrough_reset(TPMBackend *tb)
 268{
 269    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 270
 271    DPRINTF("tpm_passthrough: CALL TO TPM_RESET!\n");
 272
 273    tpm_passthrough_cancel_cmd(tb);
 274
 275    tpm_backend_thread_end(&tpm_pt->tbt);
 276
 277    tpm_pt->had_startup_error = false;
 278}
 279
 280static int tpm_passthrough_init(TPMBackend *tb, TPMState *s,
 281                                TPMRecvDataCB *recv_data_cb)
 282{
 283    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 284
 285    tpm_pt->tpm_thread_params.tpm_state = s;
 286    tpm_pt->tpm_thread_params.recv_data_callback = recv_data_cb;
 287    tpm_pt->tpm_thread_params.tb = tb;
 288
 289    return 0;
 290}
 291
 292static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb)
 293{
 294    return false;
 295}
 296
 297static int tpm_passthrough_reset_tpm_established_flag(TPMBackend *tb,
 298                                                      uint8_t locty)
 299{
 300    /* only a TPM 2.0 will support this */
 301    return 0;
 302}
 303
 304static bool tpm_passthrough_get_startup_error(TPMBackend *tb)
 305{
 306    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 307
 308    return tpm_pt->had_startup_error;
 309}
 310
 311static size_t tpm_passthrough_realloc_buffer(TPMSizedBuffer *sb)
 312{
 313    size_t wanted_size = 4096; /* Linux tpm.c buffer size */
 314
 315    if (sb->size != wanted_size) {
 316        sb->buffer = g_realloc(sb->buffer, wanted_size);
 317        sb->size = wanted_size;
 318    }
 319    return sb->size;
 320}
 321
 322static void tpm_passthrough_deliver_request(TPMBackend *tb)
 323{
 324    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 325
 326    tpm_backend_thread_deliver_request(&tpm_pt->tbt);
 327}
 328
 329static void tpm_passthrough_cancel_cmd(TPMBackend *tb)
 330{
 331    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 332    int n;
 333
 334    /*
 335     * As of Linux 3.7 the tpm_tis driver does not properly cancel
 336     * commands on all TPM manufacturers' TPMs.
 337     * Only cancel if we're busy so we don't cancel someone else's
 338     * command, e.g., a command executed on the host.
 339     */
 340    if (tpm_pt->tpm_executing) {
 341        if (tpm_pt->cancel_fd >= 0) {
 342            n = write(tpm_pt->cancel_fd, "-", 1);
 343            if (n != 1) {
 344                error_report("Canceling TPM command failed: %s",
 345                             strerror(errno));
 346            } else {
 347                tpm_pt->tpm_op_canceled = true;
 348            }
 349        } else {
 350            error_report("Cannot cancel TPM command due to missing "
 351                         "TPM sysfs cancel entry");
 352        }
 353    }
 354}
 355
 356static const char *tpm_passthrough_create_desc(void)
 357{
 358    return "Passthrough TPM backend driver";
 359}
 360
 361static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb)
 362{
 363    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 364
 365    return tpm_pt->tpm_version;
 366}
 367
 368/*
 369 * Unless path or file descriptor set has been provided by user,
 370 * determine the sysfs cancel file following kernel documentation
 371 * in Documentation/ABI/stable/sysfs-class-tpm.
 372 * From /dev/tpm0 create /sys/class/misc/tpm0/device/cancel
 373 */
 374static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb)
 375{
 376    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 377    int fd = -1;
 378    char *dev;
 379    char path[PATH_MAX];
 380
 381    if (tb->cancel_path) {
 382        fd = qemu_open(tb->cancel_path, O_WRONLY);
 383        if (fd < 0) {
 384            error_report("Could not open TPM cancel path : %s",
 385                         strerror(errno));
 386        }
 387        return fd;
 388    }
 389
 390    dev = strrchr(tpm_pt->tpm_dev, '/');
 391    if (dev) {
 392        dev++;
 393        if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel",
 394                     dev) < sizeof(path)) {
 395            fd = qemu_open(path, O_WRONLY);
 396            if (fd >= 0) {
 397                tb->cancel_path = g_strdup(path);
 398            } else {
 399                error_report("tpm_passthrough: Could not open TPM cancel "
 400                             "path %s : %s", path, strerror(errno));
 401            }
 402        }
 403    } else {
 404       error_report("tpm_passthrough: Bad TPM device path %s",
 405                    tpm_pt->tpm_dev);
 406    }
 407
 408    return fd;
 409}
 410
 411static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
 412{
 413    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 414    const char *value;
 415
 416    value = qemu_opt_get(opts, "cancel-path");
 417    tb->cancel_path = g_strdup(value);
 418
 419    value = qemu_opt_get(opts, "path");
 420    if (!value) {
 421        value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
 422    }
 423
 424    tpm_pt->tpm_dev = g_strdup(value);
 425
 426    tb->path = g_strdup(tpm_pt->tpm_dev);
 427
 428    tpm_pt->tpm_fd = qemu_open(tpm_pt->tpm_dev, O_RDWR);
 429    if (tpm_pt->tpm_fd < 0) {
 430        error_report("Cannot access TPM device using '%s': %s",
 431                     tpm_pt->tpm_dev, strerror(errno));
 432        goto err_free_parameters;
 433    }
 434
 435    if (tpm_util_test_tpmdev(tpm_pt->tpm_fd, &tpm_pt->tpm_version)) {
 436        error_report("'%s' is not a TPM device.",
 437                     tpm_pt->tpm_dev);
 438        goto err_close_tpmdev;
 439    }
 440
 441    return 0;
 442
 443 err_close_tpmdev:
 444    qemu_close(tpm_pt->tpm_fd);
 445    tpm_pt->tpm_fd = -1;
 446
 447 err_free_parameters:
 448    g_free(tb->path);
 449    tb->path = NULL;
 450
 451    g_free(tpm_pt->tpm_dev);
 452    tpm_pt->tpm_dev = NULL;
 453
 454    return 1;
 455}
 456
 457static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
 458{
 459    Object *obj = object_new(TYPE_TPM_PASSTHROUGH);
 460    TPMBackend *tb = TPM_BACKEND(obj);
 461    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 462
 463    tb->id = g_strdup(id);
 464    /* let frontend set the fe_model to proper value */
 465    tb->fe_model = -1;
 466
 467    tb->ops = &tpm_passthrough_driver;
 468
 469    if (tpm_passthrough_handle_device_opts(opts, tb)) {
 470        goto err_exit;
 471    }
 472
 473    tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tb);
 474    if (tpm_pt->cancel_fd < 0) {
 475        goto err_exit;
 476    }
 477
 478    return tb;
 479
 480err_exit:
 481    g_free(tb->id);
 482
 483    return NULL;
 484}
 485
 486static void tpm_passthrough_destroy(TPMBackend *tb)
 487{
 488    TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 489
 490    tpm_passthrough_cancel_cmd(tb);
 491
 492    tpm_backend_thread_end(&tpm_pt->tbt);
 493
 494    qemu_close(tpm_pt->tpm_fd);
 495    qemu_close(tpm_pt->cancel_fd);
 496
 497    g_free(tb->id);
 498    g_free(tb->path);
 499    g_free(tb->cancel_path);
 500    g_free(tpm_pt->tpm_dev);
 501}
 502
 503static const QemuOptDesc tpm_passthrough_cmdline_opts[] = {
 504    TPM_STANDARD_CMDLINE_OPTS,
 505    {
 506        .name = "cancel-path",
 507        .type = QEMU_OPT_STRING,
 508        .help = "Sysfs file entry for canceling TPM commands",
 509    },
 510    {
 511        .name = "path",
 512        .type = QEMU_OPT_STRING,
 513        .help = "Path to TPM device on the host",
 514    },
 515    { /* end of list */ },
 516};
 517
 518static const TPMDriverOps tpm_passthrough_driver = {
 519    .type                     = TPM_TYPE_PASSTHROUGH,
 520    .opts                     = tpm_passthrough_cmdline_opts,
 521    .desc                     = tpm_passthrough_create_desc,
 522    .create                   = tpm_passthrough_create,
 523    .destroy                  = tpm_passthrough_destroy,
 524    .init                     = tpm_passthrough_init,
 525    .startup_tpm              = tpm_passthrough_startup_tpm,
 526    .realloc_buffer           = tpm_passthrough_realloc_buffer,
 527    .reset                    = tpm_passthrough_reset,
 528    .had_startup_error        = tpm_passthrough_get_startup_error,
 529    .deliver_request          = tpm_passthrough_deliver_request,
 530    .cancel_cmd               = tpm_passthrough_cancel_cmd,
 531    .get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag,
 532    .reset_tpm_established_flag = tpm_passthrough_reset_tpm_established_flag,
 533    .get_tpm_version          = tpm_passthrough_get_tpm_version,
 534};
 535
 536static void tpm_passthrough_inst_init(Object *obj)
 537{
 538}
 539
 540static void tpm_passthrough_inst_finalize(Object *obj)
 541{
 542}
 543
 544static void tpm_passthrough_class_init(ObjectClass *klass, void *data)
 545{
 546    TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);
 547
 548    tbc->ops = &tpm_passthrough_driver;
 549}
 550
 551static const TypeInfo tpm_passthrough_info = {
 552    .name = TYPE_TPM_PASSTHROUGH,
 553    .parent = TYPE_TPM_BACKEND,
 554    .instance_size = sizeof(TPMPassthruState),
 555    .class_init = tpm_passthrough_class_init,
 556    .instance_init = tpm_passthrough_inst_init,
 557    .instance_finalize = tpm_passthrough_inst_finalize,
 558};
 559
 560static void tpm_passthrough_register(void)
 561{
 562    type_register_static(&tpm_passthrough_info);
 563    tpm_register_driver(&tpm_passthrough_driver);
 564}
 565
 566type_init(tpm_passthrough_register)
 567