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-common.h" 26 #include "qapi/error.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 return send_all(fd, buf, len); 87 } 88 89 static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len) 90 { 91 return recv_all(fd, buf, len, true); 92 } 93 94 static 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 */ 104 static 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 115 static 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 126 static 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 174 err_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 184 static 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 196 static 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 */ 228 static 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 242 static 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 255 static 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 267 static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb) 268 { 269 return false; 270 } 271 272 static int tpm_passthrough_reset_tpm_established_flag(TPMBackend *tb, 273 uint8_t locty) 274 { 275 /* only a TPM 2.0 will support this */ 276 return 0; 277 } 278 279 static bool tpm_passthrough_get_startup_error(TPMBackend *tb) 280 { 281 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); 282 283 return tpm_pt->had_startup_error; 284 } 285 286 static size_t tpm_passthrough_realloc_buffer(TPMSizedBuffer *sb) 287 { 288 size_t wanted_size = 4096; /* Linux tpm.c buffer size */ 289 290 if (sb->size != wanted_size) { 291 sb->buffer = g_realloc(sb->buffer, wanted_size); 292 sb->size = wanted_size; 293 } 294 return sb->size; 295 } 296 297 static void tpm_passthrough_deliver_request(TPMBackend *tb) 298 { 299 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); 300 301 tpm_backend_thread_deliver_request(&tpm_pt->tbt); 302 } 303 304 static void tpm_passthrough_cancel_cmd(TPMBackend *tb) 305 { 306 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); 307 int n; 308 309 /* 310 * As of Linux 3.7 the tpm_tis driver does not properly cancel 311 * commands on all TPM manufacturers' TPMs. 312 * Only cancel if we're busy so we don't cancel someone else's 313 * command, e.g., a command executed on the host. 314 */ 315 if (tpm_pt->tpm_executing) { 316 if (tpm_pt->cancel_fd >= 0) { 317 n = write(tpm_pt->cancel_fd, "-", 1); 318 if (n != 1) { 319 error_report("Canceling TPM command failed: %s", 320 strerror(errno)); 321 } else { 322 tpm_pt->tpm_op_canceled = true; 323 } 324 } else { 325 error_report("Cannot cancel TPM command due to missing " 326 "TPM sysfs cancel entry"); 327 } 328 } 329 } 330 331 static const char *tpm_passthrough_create_desc(void) 332 { 333 return "Passthrough TPM backend driver"; 334 } 335 336 static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb) 337 { 338 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); 339 340 return tpm_pt->tpm_version; 341 } 342 343 /* 344 * Unless path or file descriptor set has been provided by user, 345 * determine the sysfs cancel file following kernel documentation 346 * in Documentation/ABI/stable/sysfs-class-tpm. 347 * From /dev/tpm0 create /sys/class/misc/tpm0/device/cancel 348 */ 349 static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb) 350 { 351 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); 352 int fd = -1; 353 char *dev; 354 char path[PATH_MAX]; 355 356 if (tb->cancel_path) { 357 fd = qemu_open(tb->cancel_path, O_WRONLY); 358 if (fd < 0) { 359 error_report("Could not open TPM cancel path : %s", 360 strerror(errno)); 361 } 362 return fd; 363 } 364 365 dev = strrchr(tpm_pt->tpm_dev, '/'); 366 if (dev) { 367 dev++; 368 if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel", 369 dev) < sizeof(path)) { 370 fd = qemu_open(path, O_WRONLY); 371 if (fd >= 0) { 372 tb->cancel_path = g_strdup(path); 373 } else { 374 error_report("tpm_passthrough: Could not open TPM cancel " 375 "path %s : %s", path, strerror(errno)); 376 } 377 } 378 } else { 379 error_report("tpm_passthrough: Bad TPM device path %s", 380 tpm_pt->tpm_dev); 381 } 382 383 return fd; 384 } 385 386 static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb) 387 { 388 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); 389 const char *value; 390 391 value = qemu_opt_get(opts, "cancel-path"); 392 tb->cancel_path = g_strdup(value); 393 394 value = qemu_opt_get(opts, "path"); 395 if (!value) { 396 value = TPM_PASSTHROUGH_DEFAULT_DEVICE; 397 } 398 399 tpm_pt->tpm_dev = g_strdup(value); 400 401 tb->path = g_strdup(tpm_pt->tpm_dev); 402 403 tpm_pt->tpm_fd = qemu_open(tpm_pt->tpm_dev, O_RDWR); 404 if (tpm_pt->tpm_fd < 0) { 405 error_report("Cannot access TPM device using '%s': %s", 406 tpm_pt->tpm_dev, strerror(errno)); 407 goto err_free_parameters; 408 } 409 410 if (tpm_util_test_tpmdev(tpm_pt->tpm_fd, &tpm_pt->tpm_version)) { 411 error_report("'%s' is not a TPM device.", 412 tpm_pt->tpm_dev); 413 goto err_close_tpmdev; 414 } 415 416 return 0; 417 418 err_close_tpmdev: 419 qemu_close(tpm_pt->tpm_fd); 420 tpm_pt->tpm_fd = -1; 421 422 err_free_parameters: 423 g_free(tb->path); 424 tb->path = NULL; 425 426 g_free(tpm_pt->tpm_dev); 427 tpm_pt->tpm_dev = NULL; 428 429 return 1; 430 } 431 432 static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id) 433 { 434 Object *obj = object_new(TYPE_TPM_PASSTHROUGH); 435 TPMBackend *tb = TPM_BACKEND(obj); 436 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); 437 438 tb->id = g_strdup(id); 439 /* let frontend set the fe_model to proper value */ 440 tb->fe_model = -1; 441 442 tb->ops = &tpm_passthrough_driver; 443 444 if (tpm_passthrough_handle_device_opts(opts, tb)) { 445 goto err_exit; 446 } 447 448 tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tb); 449 if (tpm_pt->cancel_fd < 0) { 450 goto err_exit; 451 } 452 453 return tb; 454 455 err_exit: 456 g_free(tb->id); 457 458 return NULL; 459 } 460 461 static void tpm_passthrough_destroy(TPMBackend *tb) 462 { 463 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); 464 465 tpm_passthrough_cancel_cmd(tb); 466 467 tpm_backend_thread_end(&tpm_pt->tbt); 468 469 qemu_close(tpm_pt->tpm_fd); 470 qemu_close(tpm_pt->cancel_fd); 471 472 g_free(tb->id); 473 g_free(tb->path); 474 g_free(tb->cancel_path); 475 g_free(tpm_pt->tpm_dev); 476 } 477 478 static const QemuOptDesc tpm_passthrough_cmdline_opts[] = { 479 TPM_STANDARD_CMDLINE_OPTS, 480 { 481 .name = "cancel-path", 482 .type = QEMU_OPT_STRING, 483 .help = "Sysfs file entry for canceling TPM commands", 484 }, 485 { 486 .name = "path", 487 .type = QEMU_OPT_STRING, 488 .help = "Path to TPM device on the host", 489 }, 490 { /* end of list */ }, 491 }; 492 493 static const TPMDriverOps tpm_passthrough_driver = { 494 .type = TPM_TYPE_PASSTHROUGH, 495 .opts = tpm_passthrough_cmdline_opts, 496 .desc = tpm_passthrough_create_desc, 497 .create = tpm_passthrough_create, 498 .destroy = tpm_passthrough_destroy, 499 .init = tpm_passthrough_init, 500 .startup_tpm = tpm_passthrough_startup_tpm, 501 .realloc_buffer = tpm_passthrough_realloc_buffer, 502 .reset = tpm_passthrough_reset, 503 .had_startup_error = tpm_passthrough_get_startup_error, 504 .deliver_request = tpm_passthrough_deliver_request, 505 .cancel_cmd = tpm_passthrough_cancel_cmd, 506 .get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag, 507 .reset_tpm_established_flag = tpm_passthrough_reset_tpm_established_flag, 508 .get_tpm_version = tpm_passthrough_get_tpm_version, 509 }; 510 511 static void tpm_passthrough_inst_init(Object *obj) 512 { 513 } 514 515 static void tpm_passthrough_inst_finalize(Object *obj) 516 { 517 } 518 519 static void tpm_passthrough_class_init(ObjectClass *klass, void *data) 520 { 521 TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass); 522 523 tbc->ops = &tpm_passthrough_driver; 524 } 525 526 static const TypeInfo tpm_passthrough_info = { 527 .name = TYPE_TPM_PASSTHROUGH, 528 .parent = TYPE_TPM_BACKEND, 529 .instance_size = sizeof(TPMPassthruState), 530 .class_init = tpm_passthrough_class_init, 531 .instance_init = tpm_passthrough_inst_init, 532 .instance_finalize = tpm_passthrough_inst_finalize, 533 }; 534 535 static void tpm_passthrough_register(void) 536 { 537 type_register_static(&tpm_passthrough_info); 538 tpm_register_driver(&tpm_passthrough_driver); 539 } 540 541 type_init(tpm_passthrough_register) 542