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 "qapi/clone-visitor.h" 33 #include "qapi/qapi-visit-tpm.h" 34 #include "tpm_util.h" 35 36 #define DEBUG_TPM 0 37 38 #define DPRINTF(fmt, ...) do { \ 39 if (DEBUG_TPM) { \ 40 fprintf(stderr, fmt, ## __VA_ARGS__); \ 41 } \ 42 } while (0) 43 44 #define TYPE_TPM_PASSTHROUGH "tpm-passthrough" 45 #define TPM_PASSTHROUGH(obj) \ 46 OBJECT_CHECK(TPMPassthruState, (obj), TYPE_TPM_PASSTHROUGH) 47 48 /* data structures */ 49 struct TPMPassthruState { 50 TPMBackend parent; 51 52 TPMPassthroughOptions *options; 53 const char *tpm_dev; 54 int tpm_fd; 55 bool tpm_executing; 56 bool tpm_op_canceled; 57 int cancel_fd; 58 59 TPMVersion tpm_version; 60 size_t tpm_buffersize; 61 }; 62 63 typedef struct TPMPassthruState TPMPassthruState; 64 65 #define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0" 66 67 /* functions */ 68 69 static void tpm_passthrough_cancel_cmd(TPMBackend *tb); 70 71 static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len) 72 { 73 int ret; 74 reread: 75 ret = read(fd, buf, len); 76 if (ret < 0) { 77 if (errno != EINTR && errno != EAGAIN) { 78 return -1; 79 } 80 goto reread; 81 } 82 return ret; 83 } 84 85 static void tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt, 86 const uint8_t *in, uint32_t in_len, 87 uint8_t *out, uint32_t out_len, 88 bool *selftest_done, Error **errp) 89 { 90 ssize_t ret; 91 bool is_selftest; 92 93 /* FIXME: protect shared variables or use other sync mechanism */ 94 tpm_pt->tpm_op_canceled = false; 95 tpm_pt->tpm_executing = true; 96 *selftest_done = false; 97 98 is_selftest = tpm_util_is_selftest(in, in_len); 99 100 ret = qemu_write_full(tpm_pt->tpm_fd, in, in_len); 101 if (ret != in_len) { 102 if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) { 103 error_setg_errno(errp, errno, "tpm_passthrough: error while " 104 "transmitting data to TPM"); 105 } 106 goto err_exit; 107 } 108 109 tpm_pt->tpm_executing = false; 110 111 ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len); 112 if (ret < 0) { 113 if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) { 114 error_setg_errno(errp, errno, "tpm_passthrough: error while " 115 "reading data from TPM"); 116 } 117 } else if (ret < sizeof(struct tpm_resp_hdr) || 118 tpm_cmd_get_size(out) != ret) { 119 ret = -1; 120 error_setg_errno(errp, errno, "tpm_passthrough: received invalid " 121 "response packet from TPM"); 122 } 123 124 if (is_selftest && (ret >= sizeof(struct tpm_resp_hdr))) { 125 *selftest_done = tpm_cmd_get_errcode(out) == 0; 126 } 127 128 err_exit: 129 if (ret < 0) { 130 tpm_util_write_fatal_error_response(out, out_len); 131 } 132 133 tpm_pt->tpm_executing = false; 134 } 135 136 static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd *cmd, 137 Error **errp) 138 { 139 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); 140 141 DPRINTF("tpm_passthrough: processing command %p\n", cmd); 142 143 tpm_passthrough_unix_tx_bufs(tpm_pt, cmd->in, cmd->in_len, 144 cmd->out, cmd->out_len, &cmd->selftest_done, 145 errp); 146 } 147 148 static void tpm_passthrough_reset(TPMBackend *tb) 149 { 150 DPRINTF("tpm_passthrough: CALL TO TPM_RESET!\n"); 151 152 tpm_passthrough_cancel_cmd(tb); 153 } 154 155 static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb) 156 { 157 return false; 158 } 159 160 static int tpm_passthrough_reset_tpm_established_flag(TPMBackend *tb, 161 uint8_t locty) 162 { 163 /* only a TPM 2.0 will support this */ 164 return 0; 165 } 166 167 static void tpm_passthrough_cancel_cmd(TPMBackend *tb) 168 { 169 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); 170 int n; 171 172 /* 173 * As of Linux 3.7 the tpm_tis driver does not properly cancel 174 * commands on all TPM manufacturers' TPMs. 175 * Only cancel if we're busy so we don't cancel someone else's 176 * command, e.g., a command executed on the host. 177 */ 178 if (tpm_pt->tpm_executing) { 179 if (tpm_pt->cancel_fd >= 0) { 180 tpm_pt->tpm_op_canceled = true; 181 n = write(tpm_pt->cancel_fd, "-", 1); 182 if (n != 1) { 183 error_report("Canceling TPM command failed: %s", 184 strerror(errno)); 185 } 186 } else { 187 error_report("Cannot cancel TPM command due to missing " 188 "TPM sysfs cancel entry"); 189 } 190 } 191 } 192 193 static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb) 194 { 195 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); 196 197 return tpm_pt->tpm_version; 198 } 199 200 static size_t tpm_passthrough_get_buffer_size(TPMBackend *tb) 201 { 202 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); 203 int ret; 204 205 ret = tpm_util_get_buffer_size(tpm_pt->tpm_fd, tpm_pt->tpm_version, 206 &tpm_pt->tpm_buffersize); 207 if (ret < 0) { 208 tpm_pt->tpm_buffersize = 4096; 209 } 210 return tpm_pt->tpm_buffersize; 211 } 212 213 /* 214 * Unless path or file descriptor set has been provided by user, 215 * determine the sysfs cancel file following kernel documentation 216 * in Documentation/ABI/stable/sysfs-class-tpm. 217 * From /dev/tpm0 create /sys/class/tpm/tpm0/device/cancel 218 * before 4.0: /sys/class/misc/tpm0/device/cancel 219 */ 220 static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt) 221 { 222 int fd = -1; 223 char *dev; 224 char path[PATH_MAX]; 225 226 if (tpm_pt->options->cancel_path) { 227 fd = qemu_open(tpm_pt->options->cancel_path, O_WRONLY); 228 if (fd < 0) { 229 error_report("tpm_passthrough: Could not open TPM cancel path: %s", 230 strerror(errno)); 231 } 232 return fd; 233 } 234 235 dev = strrchr(tpm_pt->tpm_dev, '/'); 236 if (!dev) { 237 error_report("tpm_passthrough: Bad TPM device path %s", 238 tpm_pt->tpm_dev); 239 return -1; 240 } 241 242 dev++; 243 if (snprintf(path, sizeof(path), "/sys/class/tpm/%s/device/cancel", 244 dev) < sizeof(path)) { 245 fd = qemu_open(path, O_WRONLY); 246 if (fd < 0) { 247 if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel", 248 dev) < sizeof(path)) { 249 fd = qemu_open(path, O_WRONLY); 250 } 251 } 252 } 253 254 if (fd < 0) { 255 error_report("tpm_passthrough: Could not guess TPM cancel path"); 256 } else { 257 tpm_pt->options->cancel_path = g_strdup(path); 258 } 259 260 return fd; 261 } 262 263 static int 264 tpm_passthrough_handle_device_opts(TPMPassthruState *tpm_pt, QemuOpts *opts) 265 { 266 const char *value; 267 268 value = qemu_opt_get(opts, "cancel-path"); 269 if (value) { 270 tpm_pt->options->cancel_path = g_strdup(value); 271 tpm_pt->options->has_cancel_path = true; 272 } 273 274 value = qemu_opt_get(opts, "path"); 275 if (value) { 276 tpm_pt->options->has_path = true; 277 tpm_pt->options->path = g_strdup(value); 278 } 279 280 tpm_pt->tpm_dev = value ? value : TPM_PASSTHROUGH_DEFAULT_DEVICE; 281 tpm_pt->tpm_fd = qemu_open(tpm_pt->tpm_dev, O_RDWR); 282 if (tpm_pt->tpm_fd < 0) { 283 error_report("Cannot access TPM device using '%s': %s", 284 tpm_pt->tpm_dev, strerror(errno)); 285 return -1; 286 } 287 288 if (tpm_util_test_tpmdev(tpm_pt->tpm_fd, &tpm_pt->tpm_version)) { 289 error_report("'%s' is not a TPM device.", 290 tpm_pt->tpm_dev); 291 return -1; 292 } 293 294 tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tpm_pt); 295 if (tpm_pt->cancel_fd < 0) { 296 return -1; 297 } 298 299 return 0; 300 } 301 302 static TPMBackend *tpm_passthrough_create(QemuOpts *opts) 303 { 304 Object *obj = object_new(TYPE_TPM_PASSTHROUGH); 305 306 if (tpm_passthrough_handle_device_opts(TPM_PASSTHROUGH(obj), opts)) { 307 object_unref(obj); 308 return NULL; 309 } 310 311 return TPM_BACKEND(obj); 312 } 313 314 static int tpm_passthrough_startup_tpm(TPMBackend *tb, size_t buffersize) 315 { 316 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); 317 318 if (buffersize && buffersize < tpm_pt->tpm_buffersize) { 319 error_report("Requested buffer size of %zu is smaller than host TPM's " 320 "fixed buffer size of %zu", 321 buffersize, tpm_pt->tpm_buffersize); 322 return -1; 323 } 324 325 return 0; 326 } 327 328 static TpmTypeOptions *tpm_passthrough_get_tpm_options(TPMBackend *tb) 329 { 330 TpmTypeOptions *options = g_new0(TpmTypeOptions, 1); 331 332 options->type = TPM_TYPE_OPTIONS_KIND_PASSTHROUGH; 333 options->u.passthrough.data = QAPI_CLONE(TPMPassthroughOptions, 334 TPM_PASSTHROUGH(tb)->options); 335 336 return options; 337 } 338 339 static const QemuOptDesc tpm_passthrough_cmdline_opts[] = { 340 TPM_STANDARD_CMDLINE_OPTS, 341 { 342 .name = "cancel-path", 343 .type = QEMU_OPT_STRING, 344 .help = "Sysfs file entry for canceling TPM commands", 345 }, 346 { 347 .name = "path", 348 .type = QEMU_OPT_STRING, 349 .help = "Path to TPM device on the host", 350 }, 351 { /* end of list */ }, 352 }; 353 354 static void tpm_passthrough_inst_init(Object *obj) 355 { 356 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(obj); 357 358 tpm_pt->options = g_new0(TPMPassthroughOptions, 1); 359 tpm_pt->tpm_fd = -1; 360 tpm_pt->cancel_fd = -1; 361 } 362 363 static void tpm_passthrough_inst_finalize(Object *obj) 364 { 365 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(obj); 366 367 tpm_passthrough_cancel_cmd(TPM_BACKEND(obj)); 368 369 if (tpm_pt->tpm_fd >= 0) { 370 qemu_close(tpm_pt->tpm_fd); 371 } 372 if (tpm_pt->cancel_fd >= 0) { 373 qemu_close(tpm_pt->cancel_fd); 374 } 375 qapi_free_TPMPassthroughOptions(tpm_pt->options); 376 } 377 378 static void tpm_passthrough_class_init(ObjectClass *klass, void *data) 379 { 380 TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass); 381 382 tbc->type = TPM_TYPE_PASSTHROUGH; 383 tbc->opts = tpm_passthrough_cmdline_opts; 384 tbc->desc = "Passthrough TPM backend driver"; 385 tbc->create = tpm_passthrough_create; 386 tbc->startup_tpm = tpm_passthrough_startup_tpm; 387 tbc->reset = tpm_passthrough_reset; 388 tbc->cancel_cmd = tpm_passthrough_cancel_cmd; 389 tbc->get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag; 390 tbc->reset_tpm_established_flag = 391 tpm_passthrough_reset_tpm_established_flag; 392 tbc->get_tpm_version = tpm_passthrough_get_tpm_version; 393 tbc->get_buffer_size = tpm_passthrough_get_buffer_size; 394 tbc->get_tpm_options = tpm_passthrough_get_tpm_options; 395 tbc->handle_request = tpm_passthrough_handle_request; 396 } 397 398 static const TypeInfo tpm_passthrough_info = { 399 .name = TYPE_TPM_PASSTHROUGH, 400 .parent = TYPE_TPM_BACKEND, 401 .instance_size = sizeof(TPMPassthruState), 402 .class_init = tpm_passthrough_class_init, 403 .instance_init = tpm_passthrough_inst_init, 404 .instance_finalize = tpm_passthrough_inst_finalize, 405 }; 406 407 static void tpm_passthrough_register(void) 408 { 409 type_register_static(&tpm_passthrough_info); 410 } 411 412 type_init(tpm_passthrough_register) 413