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