mips jazz: compile only in 64 bit
[qemu/ar7.git] / hw / tpm / tpm_passthrough.c
blob8d8523a535789d441513a5b369c7cbac000f8b52
1 /*
2 * passthrough TPM driver
4 * Copyright (c) 2010 - 2013 IBM Corporation
5 * Authors:
6 * Stefan Berger <stefanb@us.ibm.com>
8 * Copyright (C) 2011 IAIK, Graz University of Technology
9 * Author: Andreas Niederl
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.
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.
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/>
25 #include <dirent.h>
27 #include "qemu-common.h"
28 #include "qapi/error.h"
29 #include "qemu/sockets.h"
30 #include "sysemu/tpm_backend.h"
31 #include "tpm_int.h"
32 #include "hw/hw.h"
33 #include "hw/i386/pc.h"
34 #include "sysemu/tpm_backend_int.h"
35 #include "tpm_tis.h"
36 #include "tpm_util.h"
38 #define DEBUG_TPM 0
40 #define DPRINTF(fmt, ...) do { \
41 if (DEBUG_TPM) { \
42 fprintf(stderr, fmt, ## __VA_ARGS__); \
43 } \
44 } while (0);
46 #define TYPE_TPM_PASSTHROUGH "tpm-passthrough"
47 #define TPM_PASSTHROUGH(obj) \
48 OBJECT_CHECK(TPMPassthruState, (obj), TYPE_TPM_PASSTHROUGH)
50 static const TPMDriverOps tpm_passthrough_driver;
52 /* data structures */
53 typedef struct TPMPassthruThreadParams {
54 TPMState *tpm_state;
56 TPMRecvDataCB *recv_data_callback;
57 TPMBackend *tb;
58 } TPMPassthruThreadParams;
60 struct TPMPassthruState {
61 TPMBackend parent;
63 TPMBackendThread tbt;
65 TPMPassthruThreadParams tpm_thread_params;
67 char *tpm_dev;
68 int tpm_fd;
69 bool tpm_executing;
70 bool tpm_op_canceled;
71 int cancel_fd;
72 bool had_startup_error;
74 TPMVersion tpm_version;
77 typedef struct TPMPassthruState TPMPassthruState;
79 #define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0"
81 /* functions */
83 static void tpm_passthrough_cancel_cmd(TPMBackend *tb);
85 static int tpm_passthrough_unix_write(int fd, const uint8_t *buf, uint32_t len)
87 return send_all(fd, buf, len);
90 static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
92 return recv_all(fd, buf, len, true);
95 static uint32_t tpm_passthrough_get_size_from_buffer(const uint8_t *buf)
97 struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)buf;
99 return be32_to_cpu(resp->len);
103 * Write an error message in the given output buffer.
105 static void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len)
107 if (out_len >= sizeof(struct tpm_resp_hdr)) {
108 struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)out;
110 resp->tag = cpu_to_be16(TPM_TAG_RSP_COMMAND);
111 resp->len = cpu_to_be32(sizeof(struct tpm_resp_hdr));
112 resp->errcode = cpu_to_be32(TPM_FAIL);
116 static bool tpm_passthrough_is_selftest(const uint8_t *in, uint32_t in_len)
118 struct tpm_req_hdr *hdr = (struct tpm_req_hdr *)in;
120 if (in_len >= sizeof(*hdr)) {
121 return (be32_to_cpu(hdr->ordinal) == TPM_ORD_ContinueSelfTest);
124 return false;
127 static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
128 const uint8_t *in, uint32_t in_len,
129 uint8_t *out, uint32_t out_len,
130 bool *selftest_done)
132 int ret;
133 bool is_selftest;
134 const struct tpm_resp_hdr *hdr;
136 tpm_pt->tpm_op_canceled = false;
137 tpm_pt->tpm_executing = true;
138 *selftest_done = false;
140 is_selftest = tpm_passthrough_is_selftest(in, in_len);
142 ret = tpm_passthrough_unix_write(tpm_pt->tpm_fd, in, in_len);
143 if (ret != in_len) {
144 if (!tpm_pt->tpm_op_canceled ||
145 (tpm_pt->tpm_op_canceled && errno != ECANCELED)) {
146 error_report("tpm_passthrough: error while transmitting data "
147 "to TPM: %s (%i)",
148 strerror(errno), errno);
150 goto err_exit;
153 tpm_pt->tpm_executing = false;
155 ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len);
156 if (ret < 0) {
157 if (!tpm_pt->tpm_op_canceled ||
158 (tpm_pt->tpm_op_canceled && errno != ECANCELED)) {
159 error_report("tpm_passthrough: error while reading data from "
160 "TPM: %s (%i)",
161 strerror(errno), errno);
163 } else if (ret < sizeof(struct tpm_resp_hdr) ||
164 tpm_passthrough_get_size_from_buffer(out) != ret) {
165 ret = -1;
166 error_report("tpm_passthrough: received invalid response "
167 "packet from TPM");
170 if (is_selftest && (ret >= sizeof(struct tpm_resp_hdr))) {
171 hdr = (struct tpm_resp_hdr *)out;
172 *selftest_done = (be32_to_cpu(hdr->errcode) == 0);
175 err_exit:
176 if (ret < 0) {
177 tpm_write_fatal_error_response(out, out_len);
180 tpm_pt->tpm_executing = false;
182 return ret;
185 static int tpm_passthrough_unix_transfer(TPMPassthruState *tpm_pt,
186 const TPMLocality *locty_data,
187 bool *selftest_done)
189 return tpm_passthrough_unix_tx_bufs(tpm_pt,
190 locty_data->w_buffer.buffer,
191 locty_data->w_offset,
192 locty_data->r_buffer.buffer,
193 locty_data->r_buffer.size,
194 selftest_done);
197 static void tpm_passthrough_worker_thread(gpointer data,
198 gpointer user_data)
200 TPMPassthruThreadParams *thr_parms = user_data;
201 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(thr_parms->tb);
202 TPMBackendCmd cmd = (TPMBackendCmd)data;
203 bool selftest_done = false;
205 DPRINTF("tpm_passthrough: processing command type %d\n", cmd);
207 switch (cmd) {
208 case TPM_BACKEND_CMD_PROCESS_CMD:
209 tpm_passthrough_unix_transfer(tpm_pt,
210 thr_parms->tpm_state->locty_data,
211 &selftest_done);
213 thr_parms->recv_data_callback(thr_parms->tpm_state,
214 thr_parms->tpm_state->locty_number,
215 selftest_done);
216 break;
217 case TPM_BACKEND_CMD_INIT:
218 case TPM_BACKEND_CMD_END:
219 case TPM_BACKEND_CMD_TPM_RESET:
220 /* nothing to do */
221 break;
226 * Start the TPM (thread). If it had been started before, then terminate
227 * and start it again.
229 static int tpm_passthrough_startup_tpm(TPMBackend *tb)
231 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
233 /* terminate a running TPM */
234 tpm_backend_thread_end(&tpm_pt->tbt);
236 tpm_backend_thread_create(&tpm_pt->tbt,
237 tpm_passthrough_worker_thread,
238 &tpm_pt->tpm_thread_params);
240 return 0;
243 static void tpm_passthrough_reset(TPMBackend *tb)
245 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
247 DPRINTF("tpm_passthrough: CALL TO TPM_RESET!\n");
249 tpm_passthrough_cancel_cmd(tb);
251 tpm_backend_thread_end(&tpm_pt->tbt);
253 tpm_pt->had_startup_error = false;
256 static int tpm_passthrough_init(TPMBackend *tb, TPMState *s,
257 TPMRecvDataCB *recv_data_cb)
259 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
261 tpm_pt->tpm_thread_params.tpm_state = s;
262 tpm_pt->tpm_thread_params.recv_data_callback = recv_data_cb;
263 tpm_pt->tpm_thread_params.tb = tb;
265 return 0;
268 static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb)
270 return false;
273 static int tpm_passthrough_reset_tpm_established_flag(TPMBackend *tb,
274 uint8_t locty)
276 /* only a TPM 2.0 will support this */
277 return 0;
280 static bool tpm_passthrough_get_startup_error(TPMBackend *tb)
282 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
284 return tpm_pt->had_startup_error;
287 static size_t tpm_passthrough_realloc_buffer(TPMSizedBuffer *sb)
289 size_t wanted_size = 4096; /* Linux tpm.c buffer size */
291 if (sb->size != wanted_size) {
292 sb->buffer = g_realloc(sb->buffer, wanted_size);
293 sb->size = wanted_size;
295 return sb->size;
298 static void tpm_passthrough_deliver_request(TPMBackend *tb)
300 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
302 tpm_backend_thread_deliver_request(&tpm_pt->tbt);
305 static void tpm_passthrough_cancel_cmd(TPMBackend *tb)
307 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
308 int n;
311 * As of Linux 3.7 the tpm_tis driver does not properly cancel
312 * commands on all TPM manufacturers' TPMs.
313 * Only cancel if we're busy so we don't cancel someone else's
314 * command, e.g., a command executed on the host.
316 if (tpm_pt->tpm_executing) {
317 if (tpm_pt->cancel_fd >= 0) {
318 n = write(tpm_pt->cancel_fd, "-", 1);
319 if (n != 1) {
320 error_report("Canceling TPM command failed: %s",
321 strerror(errno));
322 } else {
323 tpm_pt->tpm_op_canceled = true;
325 } else {
326 error_report("Cannot cancel TPM command due to missing "
327 "TPM sysfs cancel entry");
332 static const char *tpm_passthrough_create_desc(void)
334 return "Passthrough TPM backend driver";
337 static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb)
339 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
341 return tpm_pt->tpm_version;
345 * Unless path or file descriptor set has been provided by user,
346 * determine the sysfs cancel file following kernel documentation
347 * in Documentation/ABI/stable/sysfs-class-tpm.
348 * From /dev/tpm0 create /sys/class/misc/tpm0/device/cancel
350 static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb)
352 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
353 int fd = -1;
354 char *dev;
355 char path[PATH_MAX];
357 if (tb->cancel_path) {
358 fd = qemu_open(tb->cancel_path, O_WRONLY);
359 if (fd < 0) {
360 error_report("Could not open TPM cancel path : %s",
361 strerror(errno));
363 return fd;
366 dev = strrchr(tpm_pt->tpm_dev, '/');
367 if (dev) {
368 dev++;
369 if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel",
370 dev) < sizeof(path)) {
371 fd = qemu_open(path, O_WRONLY);
372 if (fd >= 0) {
373 tb->cancel_path = g_strdup(path);
374 } else {
375 error_report("tpm_passthrough: Could not open TPM cancel "
376 "path %s : %s", path, strerror(errno));
379 } else {
380 error_report("tpm_passthrough: Bad TPM device path %s",
381 tpm_pt->tpm_dev);
384 return fd;
387 static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
389 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
390 const char *value;
392 value = qemu_opt_get(opts, "cancel-path");
393 tb->cancel_path = g_strdup(value);
395 value = qemu_opt_get(opts, "path");
396 if (!value) {
397 value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
400 tpm_pt->tpm_dev = g_strdup(value);
402 tb->path = g_strdup(tpm_pt->tpm_dev);
404 tpm_pt->tpm_fd = qemu_open(tpm_pt->tpm_dev, O_RDWR);
405 if (tpm_pt->tpm_fd < 0) {
406 error_report("Cannot access TPM device using '%s': %s",
407 tpm_pt->tpm_dev, strerror(errno));
408 goto err_free_parameters;
411 if (tpm_util_test_tpmdev(tpm_pt->tpm_fd, &tpm_pt->tpm_version)) {
412 error_report("'%s' is not a TPM device.",
413 tpm_pt->tpm_dev);
414 goto err_close_tpmdev;
417 return 0;
419 err_close_tpmdev:
420 qemu_close(tpm_pt->tpm_fd);
421 tpm_pt->tpm_fd = -1;
423 err_free_parameters:
424 g_free(tb->path);
425 tb->path = NULL;
427 g_free(tpm_pt->tpm_dev);
428 tpm_pt->tpm_dev = NULL;
430 return 1;
433 static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
435 Object *obj = object_new(TYPE_TPM_PASSTHROUGH);
436 TPMBackend *tb = TPM_BACKEND(obj);
437 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
439 tb->id = g_strdup(id);
440 /* let frontend set the fe_model to proper value */
441 tb->fe_model = -1;
443 tb->ops = &tpm_passthrough_driver;
445 if (tpm_passthrough_handle_device_opts(opts, tb)) {
446 goto err_exit;
449 tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tb);
450 if (tpm_pt->cancel_fd < 0) {
451 goto err_exit;
454 return tb;
456 err_exit:
457 g_free(tb->id);
459 return NULL;
462 static void tpm_passthrough_destroy(TPMBackend *tb)
464 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
466 tpm_passthrough_cancel_cmd(tb);
468 tpm_backend_thread_end(&tpm_pt->tbt);
470 qemu_close(tpm_pt->tpm_fd);
471 qemu_close(tpm_pt->cancel_fd);
473 g_free(tb->id);
474 g_free(tb->path);
475 g_free(tb->cancel_path);
476 g_free(tpm_pt->tpm_dev);
479 static const QemuOptDesc tpm_passthrough_cmdline_opts[] = {
480 TPM_STANDARD_CMDLINE_OPTS,
482 .name = "cancel-path",
483 .type = QEMU_OPT_STRING,
484 .help = "Sysfs file entry for canceling TPM commands",
487 .name = "path",
488 .type = QEMU_OPT_STRING,
489 .help = "Path to TPM device on the host",
491 { /* end of list */ },
494 static const TPMDriverOps tpm_passthrough_driver = {
495 .type = TPM_TYPE_PASSTHROUGH,
496 .opts = tpm_passthrough_cmdline_opts,
497 .desc = tpm_passthrough_create_desc,
498 .create = tpm_passthrough_create,
499 .destroy = tpm_passthrough_destroy,
500 .init = tpm_passthrough_init,
501 .startup_tpm = tpm_passthrough_startup_tpm,
502 .realloc_buffer = tpm_passthrough_realloc_buffer,
503 .reset = tpm_passthrough_reset,
504 .had_startup_error = tpm_passthrough_get_startup_error,
505 .deliver_request = tpm_passthrough_deliver_request,
506 .cancel_cmd = tpm_passthrough_cancel_cmd,
507 .get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag,
508 .reset_tpm_established_flag = tpm_passthrough_reset_tpm_established_flag,
509 .get_tpm_version = tpm_passthrough_get_tpm_version,
512 static void tpm_passthrough_inst_init(Object *obj)
516 static void tpm_passthrough_inst_finalize(Object *obj)
520 static void tpm_passthrough_class_init(ObjectClass *klass, void *data)
522 TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);
524 tbc->ops = &tpm_passthrough_driver;
527 static const TypeInfo tpm_passthrough_info = {
528 .name = TYPE_TPM_PASSTHROUGH,
529 .parent = TYPE_TPM_BACKEND,
530 .instance_size = sizeof(TPMPassthruState),
531 .class_init = tpm_passthrough_class_init,
532 .instance_init = tpm_passthrough_inst_init,
533 .instance_finalize = tpm_passthrough_inst_finalize,
536 static void tpm_passthrough_register(void)
538 type_register_static(&tpm_passthrough_info);
539 tpm_register_driver(&tpm_passthrough_driver);
542 type_init(tpm_passthrough_register)