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 49 static const TPMDriverOps tpm_passthrough_driver; 50 51 /* data structures */ 52 typedef struct TPMPassthruThreadParams { 53 TPMState *tpm_state; 54 55 TPMRecvDataCB *recv_data_callback; 56 TPMBackend *tb; 57 } TPMPassthruThreadParams; 58 59 struct 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 76 typedef struct TPMPassthruState TPMPassthruState; 77 78 #define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0" 79 80 /* functions */ 81 82 static void tpm_passthrough_cancel_cmd(TPMBackend *tb); 83 84 static 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 105 static 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 119 static 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 */ 129 static 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 140 static 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 151 static 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 199 err_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 209 static 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 221 static 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 */ 253 static 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 267 static 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 280 static 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 292 static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb) 293 { 294 return false; 295 } 296 297 static 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 304 static 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 311 static 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 322 static 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 329 static 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 356 static const char *tpm_passthrough_create_desc(void) 357 { 358 return "Passthrough TPM backend driver"; 359 } 360 361 static 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 */ 374 static 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 411 static 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 457 static 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 480 err_exit: 481 g_free(tb->id); 482 483 return NULL; 484 } 485 486 static 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 503 static 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 518 static 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 536 static void tpm_passthrough_inst_init(Object *obj) 537 { 538 } 539 540 static void tpm_passthrough_inst_finalize(Object *obj) 541 { 542 } 543 544 static 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 551 static 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 560 static void tpm_passthrough_register(void) 561 { 562 type_register_static(&tpm_passthrough_info); 563 tpm_register_driver(&tpm_passthrough_driver); 564 } 565 566 type_init(tpm_passthrough_register) 567