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