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