object: make some funcs static
[qemu/ar7.git] / hw / tpm / tpm_passthrough.c
blob9234eb3459962c6ed702da46403b49bebece63cf
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 "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 "hw/i386/pc.h"
33 #include "sysemu/tpm_backend_int.h"
34 #include "tpm_tis.h"
35 #include "tpm_util.h"
37 #define DEBUG_TPM 0
39 #define DPRINTF(fmt, ...) do { \
40 if (DEBUG_TPM) { \
41 fprintf(stderr, fmt, ## __VA_ARGS__); \
42 } \
43 } while (0);
45 #define TYPE_TPM_PASSTHROUGH "tpm-passthrough"
46 #define TPM_PASSTHROUGH(obj) \
47 OBJECT_CHECK(TPMPassthruState, (obj), TYPE_TPM_PASSTHROUGH)
49 static const TPMDriverOps tpm_passthrough_driver;
51 /* data structures */
52 typedef struct TPMPassthruThreadParams {
53 TPMState *tpm_state;
55 TPMRecvDataCB *recv_data_callback;
56 TPMBackend *tb;
57 } TPMPassthruThreadParams;
59 struct TPMPassthruState {
60 TPMBackend parent;
62 TPMBackendThread tbt;
64 TPMPassthruThreadParams tpm_thread_params;
66 char *tpm_dev;
67 int tpm_fd;
68 bool tpm_executing;
69 bool tpm_op_canceled;
70 int cancel_fd;
71 bool had_startup_error;
73 TPMVersion tpm_version;
76 typedef struct TPMPassthruState TPMPassthruState;
78 #define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0"
80 /* functions */
82 static void tpm_passthrough_cancel_cmd(TPMBackend *tb);
84 static int tpm_passthrough_unix_write(int fd, const uint8_t *buf, uint32_t len)
86 int ret, remain;
88 remain = len;
89 while (remain > 0) {
90 ret = write(fd, buf, remain);
91 if (ret < 0) {
92 if (errno != EINTR && errno != EAGAIN) {
93 return -1;
95 } else if (ret == 0) {
96 break;
97 } else {
98 buf += ret;
99 remain -= ret;
102 return len - remain;
105 static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
107 int ret;
108 reread:
109 ret = read(fd, buf, len);
110 if (ret < 0) {
111 if (errno != EINTR && errno != EAGAIN) {
112 return -1;
114 goto reread;
116 return ret;
119 static uint32_t tpm_passthrough_get_size_from_buffer(const uint8_t *buf)
121 struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)buf;
123 return be32_to_cpu(resp->len);
127 * Write an error message in the given output buffer.
129 static void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len)
131 if (out_len >= sizeof(struct tpm_resp_hdr)) {
132 struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)out;
134 resp->tag = cpu_to_be16(TPM_TAG_RSP_COMMAND);
135 resp->len = cpu_to_be32(sizeof(struct tpm_resp_hdr));
136 resp->errcode = cpu_to_be32(TPM_FAIL);
140 static bool tpm_passthrough_is_selftest(const uint8_t *in, uint32_t in_len)
142 struct tpm_req_hdr *hdr = (struct tpm_req_hdr *)in;
144 if (in_len >= sizeof(*hdr)) {
145 return (be32_to_cpu(hdr->ordinal) == TPM_ORD_ContinueSelfTest);
148 return false;
151 static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
152 const uint8_t *in, uint32_t in_len,
153 uint8_t *out, uint32_t out_len,
154 bool *selftest_done)
156 int ret;
157 bool is_selftest;
158 const struct tpm_resp_hdr *hdr;
160 tpm_pt->tpm_op_canceled = false;
161 tpm_pt->tpm_executing = true;
162 *selftest_done = false;
164 is_selftest = tpm_passthrough_is_selftest(in, in_len);
166 ret = tpm_passthrough_unix_write(tpm_pt->tpm_fd, in, in_len);
167 if (ret != in_len) {
168 if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) {
169 error_report("tpm_passthrough: error while transmitting data "
170 "to TPM: %s (%i)",
171 strerror(errno), errno);
173 goto err_exit;
176 tpm_pt->tpm_executing = false;
178 ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len);
179 if (ret < 0) {
180 if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) {
181 error_report("tpm_passthrough: error while reading data from "
182 "TPM: %s (%i)",
183 strerror(errno), errno);
185 } else if (ret < sizeof(struct tpm_resp_hdr) ||
186 tpm_passthrough_get_size_from_buffer(out) != ret) {
187 ret = -1;
188 error_report("tpm_passthrough: received invalid response "
189 "packet from TPM");
192 if (is_selftest && (ret >= sizeof(struct tpm_resp_hdr))) {
193 hdr = (struct tpm_resp_hdr *)out;
194 *selftest_done = (be32_to_cpu(hdr->errcode) == 0);
197 err_exit:
198 if (ret < 0) {
199 tpm_write_fatal_error_response(out, out_len);
202 tpm_pt->tpm_executing = false;
204 return ret;
207 static int tpm_passthrough_unix_transfer(TPMPassthruState *tpm_pt,
208 const TPMLocality *locty_data,
209 bool *selftest_done)
211 return tpm_passthrough_unix_tx_bufs(tpm_pt,
212 locty_data->w_buffer.buffer,
213 locty_data->w_offset,
214 locty_data->r_buffer.buffer,
215 locty_data->r_buffer.size,
216 selftest_done);
219 static void tpm_passthrough_worker_thread(gpointer data,
220 gpointer user_data)
222 TPMPassthruThreadParams *thr_parms = user_data;
223 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(thr_parms->tb);
224 TPMBackendCmd cmd = (TPMBackendCmd)data;
225 bool selftest_done = false;
227 DPRINTF("tpm_passthrough: processing command type %d\n", cmd);
229 switch (cmd) {
230 case TPM_BACKEND_CMD_PROCESS_CMD:
231 tpm_passthrough_unix_transfer(tpm_pt,
232 thr_parms->tpm_state->locty_data,
233 &selftest_done);
235 thr_parms->recv_data_callback(thr_parms->tpm_state,
236 thr_parms->tpm_state->locty_number,
237 selftest_done);
238 break;
239 case TPM_BACKEND_CMD_INIT:
240 case TPM_BACKEND_CMD_END:
241 case TPM_BACKEND_CMD_TPM_RESET:
242 /* nothing to do */
243 break;
248 * Start the TPM (thread). If it had been started before, then terminate
249 * and start it again.
251 static int tpm_passthrough_startup_tpm(TPMBackend *tb)
253 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
255 /* terminate a running TPM */
256 tpm_backend_thread_end(&tpm_pt->tbt);
258 tpm_backend_thread_create(&tpm_pt->tbt,
259 tpm_passthrough_worker_thread,
260 &tpm_pt->tpm_thread_params);
262 return 0;
265 static void tpm_passthrough_reset(TPMBackend *tb)
267 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
269 DPRINTF("tpm_passthrough: CALL TO TPM_RESET!\n");
271 tpm_passthrough_cancel_cmd(tb);
273 tpm_backend_thread_end(&tpm_pt->tbt);
275 tpm_pt->had_startup_error = false;
278 static int tpm_passthrough_init(TPMBackend *tb, TPMState *s,
279 TPMRecvDataCB *recv_data_cb)
281 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
283 tpm_pt->tpm_thread_params.tpm_state = s;
284 tpm_pt->tpm_thread_params.recv_data_callback = recv_data_cb;
285 tpm_pt->tpm_thread_params.tb = tb;
287 return 0;
290 static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb)
292 return false;
295 static int tpm_passthrough_reset_tpm_established_flag(TPMBackend *tb,
296 uint8_t locty)
298 /* only a TPM 2.0 will support this */
299 return 0;
302 static bool tpm_passthrough_get_startup_error(TPMBackend *tb)
304 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
306 return tpm_pt->had_startup_error;
309 static size_t tpm_passthrough_realloc_buffer(TPMSizedBuffer *sb)
311 size_t wanted_size = 4096; /* Linux tpm.c buffer size */
313 if (sb->size != wanted_size) {
314 sb->buffer = g_realloc(sb->buffer, wanted_size);
315 sb->size = wanted_size;
317 return sb->size;
320 static void tpm_passthrough_deliver_request(TPMBackend *tb)
322 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
324 tpm_backend_thread_deliver_request(&tpm_pt->tbt);
327 static void tpm_passthrough_cancel_cmd(TPMBackend *tb)
329 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
330 int n;
333 * As of Linux 3.7 the tpm_tis driver does not properly cancel
334 * commands on all TPM manufacturers' TPMs.
335 * Only cancel if we're busy so we don't cancel someone else's
336 * command, e.g., a command executed on the host.
338 if (tpm_pt->tpm_executing) {
339 if (tpm_pt->cancel_fd >= 0) {
340 n = write(tpm_pt->cancel_fd, "-", 1);
341 if (n != 1) {
342 error_report("Canceling TPM command failed: %s",
343 strerror(errno));
344 } else {
345 tpm_pt->tpm_op_canceled = true;
347 } else {
348 error_report("Cannot cancel TPM command due to missing "
349 "TPM sysfs cancel entry");
354 static const char *tpm_passthrough_create_desc(void)
356 return "Passthrough TPM backend driver";
359 static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb)
361 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
363 return tpm_pt->tpm_version;
367 * Unless path or file descriptor set has been provided by user,
368 * determine the sysfs cancel file following kernel documentation
369 * in Documentation/ABI/stable/sysfs-class-tpm.
370 * From /dev/tpm0 create /sys/class/misc/tpm0/device/cancel
372 static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb)
374 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
375 int fd = -1;
376 char *dev;
377 char path[PATH_MAX];
379 if (tb->cancel_path) {
380 fd = qemu_open(tb->cancel_path, O_WRONLY);
381 if (fd < 0) {
382 error_report("Could not open TPM cancel path : %s",
383 strerror(errno));
385 return fd;
388 dev = strrchr(tpm_pt->tpm_dev, '/');
389 if (dev) {
390 dev++;
391 if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel",
392 dev) < sizeof(path)) {
393 fd = qemu_open(path, O_WRONLY);
394 if (fd >= 0) {
395 tb->cancel_path = g_strdup(path);
396 } else {
397 error_report("tpm_passthrough: Could not open TPM cancel "
398 "path %s : %s", path, strerror(errno));
401 } else {
402 error_report("tpm_passthrough: Bad TPM device path %s",
403 tpm_pt->tpm_dev);
406 return fd;
409 static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
411 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
412 const char *value;
414 value = qemu_opt_get(opts, "cancel-path");
415 tb->cancel_path = g_strdup(value);
417 value = qemu_opt_get(opts, "path");
418 if (!value) {
419 value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
422 tpm_pt->tpm_dev = g_strdup(value);
424 tb->path = g_strdup(tpm_pt->tpm_dev);
426 tpm_pt->tpm_fd = qemu_open(tpm_pt->tpm_dev, O_RDWR);
427 if (tpm_pt->tpm_fd < 0) {
428 error_report("Cannot access TPM device using '%s': %s",
429 tpm_pt->tpm_dev, strerror(errno));
430 goto err_free_parameters;
433 if (tpm_util_test_tpmdev(tpm_pt->tpm_fd, &tpm_pt->tpm_version)) {
434 error_report("'%s' is not a TPM device.",
435 tpm_pt->tpm_dev);
436 goto err_close_tpmdev;
439 return 0;
441 err_close_tpmdev:
442 qemu_close(tpm_pt->tpm_fd);
443 tpm_pt->tpm_fd = -1;
445 err_free_parameters:
446 g_free(tb->path);
447 tb->path = NULL;
449 g_free(tpm_pt->tpm_dev);
450 tpm_pt->tpm_dev = NULL;
452 return 1;
455 static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
457 Object *obj = object_new(TYPE_TPM_PASSTHROUGH);
458 TPMBackend *tb = TPM_BACKEND(obj);
459 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
461 tb->id = g_strdup(id);
462 /* let frontend set the fe_model to proper value */
463 tb->fe_model = -1;
465 tb->ops = &tpm_passthrough_driver;
467 if (tpm_passthrough_handle_device_opts(opts, tb)) {
468 goto err_exit;
471 tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tb);
472 if (tpm_pt->cancel_fd < 0) {
473 goto err_exit;
476 return tb;
478 err_exit:
479 g_free(tb->id);
481 return NULL;
484 static void tpm_passthrough_destroy(TPMBackend *tb)
486 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
488 tpm_passthrough_cancel_cmd(tb);
490 tpm_backend_thread_end(&tpm_pt->tbt);
492 qemu_close(tpm_pt->tpm_fd);
493 qemu_close(tpm_pt->cancel_fd);
495 g_free(tb->id);
496 g_free(tb->path);
497 g_free(tb->cancel_path);
498 g_free(tpm_pt->tpm_dev);
501 static const QemuOptDesc tpm_passthrough_cmdline_opts[] = {
502 TPM_STANDARD_CMDLINE_OPTS,
504 .name = "cancel-path",
505 .type = QEMU_OPT_STRING,
506 .help = "Sysfs file entry for canceling TPM commands",
509 .name = "path",
510 .type = QEMU_OPT_STRING,
511 .help = "Path to TPM device on the host",
513 { /* end of list */ },
516 static const TPMDriverOps tpm_passthrough_driver = {
517 .type = TPM_TYPE_PASSTHROUGH,
518 .opts = tpm_passthrough_cmdline_opts,
519 .desc = tpm_passthrough_create_desc,
520 .create = tpm_passthrough_create,
521 .destroy = tpm_passthrough_destroy,
522 .init = tpm_passthrough_init,
523 .startup_tpm = tpm_passthrough_startup_tpm,
524 .realloc_buffer = tpm_passthrough_realloc_buffer,
525 .reset = tpm_passthrough_reset,
526 .had_startup_error = tpm_passthrough_get_startup_error,
527 .deliver_request = tpm_passthrough_deliver_request,
528 .cancel_cmd = tpm_passthrough_cancel_cmd,
529 .get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag,
530 .reset_tpm_established_flag = tpm_passthrough_reset_tpm_established_flag,
531 .get_tpm_version = tpm_passthrough_get_tpm_version,
534 static void tpm_passthrough_inst_init(Object *obj)
538 static void tpm_passthrough_inst_finalize(Object *obj)
542 static void tpm_passthrough_class_init(ObjectClass *klass, void *data)
544 TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);
546 tbc->ops = &tpm_passthrough_driver;
549 static const TypeInfo tpm_passthrough_info = {
550 .name = TYPE_TPM_PASSTHROUGH,
551 .parent = TYPE_TPM_BACKEND,
552 .instance_size = sizeof(TPMPassthruState),
553 .class_init = tpm_passthrough_class_init,
554 .instance_init = tpm_passthrough_inst_init,
555 .instance_finalize = tpm_passthrough_inst_finalize,
558 static void tpm_passthrough_register(void)
560 type_register_static(&tpm_passthrough_info);
561 tpm_register_driver(&tpm_passthrough_driver);
564 type_init(tpm_passthrough_register)