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