sm501: Add missing arbitration control register
[qemu.git] / hw / tpm / tpm_passthrough.c
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/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"
36
37 #define DEBUG_TPM 0
38
39 #define DPRINTF(fmt, ...) do { \
40 if (DEBUG_TPM) { \
41 fprintf(stderr, fmt, ## __VA_ARGS__); \
42 } \
43 } while (0);
44
45 #define TYPE_TPM_PASSTHROUGH "tpm-passthrough"
46 #define TPM_PASSTHROUGH(obj) \
47 OBJECT_CHECK(TPMPassthruState, (obj), TYPE_TPM_PASSTHROUGH)
48
49 static const TPMDriverOps tpm_passthrough_driver;
50
51 /* data structures */
52 typedef struct TPMPassthruThreadParams {
53 TPMState *tpm_state;
54
55 TPMRecvDataCB *recv_data_callback;
56 TPMBackend *tb;
57 } TPMPassthruThreadParams;
58
59 struct TPMPassthruState {
60 TPMBackend parent;
61
62 TPMBackendThread tbt;
63
64 TPMPassthruThreadParams tpm_thread_params;
65
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;
72
73 TPMVersion tpm_version;
74 };
75
76 typedef struct TPMPassthruState TPMPassthruState;
77
78 #define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0"
79
80 /* functions */
81
82 static void tpm_passthrough_cancel_cmd(TPMBackend *tb);
83
84 static int tpm_passthrough_unix_write(int fd, const uint8_t *buf, uint32_t len)
85 {
86 int ret, remain;
87
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;
94 }
95 } else if (ret == 0) {
96 break;
97 } else {
98 buf += ret;
99 remain -= ret;
100 }
101 }
102 return len - remain;
103 }
104
105 static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
106 {
107 int ret;
108 reread:
109 ret = read(fd, buf, len);
110 if (ret < 0) {
111 if (errno != EINTR && errno != EAGAIN) {
112 return -1;
113 }
114 goto reread;
115 }
116 return ret;
117 }
118
119 static uint32_t tpm_passthrough_get_size_from_buffer(const uint8_t *buf)
120 {
121 struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)buf;
122
123 return be32_to_cpu(resp->len);
124 }
125
126 /*
127 * Write an error message in the given output buffer.
128 */
129 static void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len)
130 {
131 if (out_len >= sizeof(struct tpm_resp_hdr)) {
132 struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)out;
133
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);
137 }
138 }
139
140 static bool tpm_passthrough_is_selftest(const uint8_t *in, uint32_t in_len)
141 {
142 struct tpm_req_hdr *hdr = (struct tpm_req_hdr *)in;
143
144 if (in_len >= sizeof(*hdr)) {
145 return (be32_to_cpu(hdr->ordinal) == TPM_ORD_ContinueSelfTest);
146 }
147
148 return false;
149 }
150
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)
155 {
156 int ret;
157 bool is_selftest;
158 const struct tpm_resp_hdr *hdr;
159
160 tpm_pt->tpm_op_canceled = false;
161 tpm_pt->tpm_executing = true;
162 *selftest_done = false;
163
164 is_selftest = tpm_passthrough_is_selftest(in, in_len);
165
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);
172 }
173 goto err_exit;
174 }
175
176 tpm_pt->tpm_executing = false;
177
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);
184 }
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");
190 }
191
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);
195 }
196
197 err_exit:
198 if (ret < 0) {
199 tpm_write_fatal_error_response(out, out_len);
200 }
201
202 tpm_pt->tpm_executing = false;
203
204 return ret;
205 }
206
207 static int tpm_passthrough_unix_transfer(TPMPassthruState *tpm_pt,
208 const TPMLocality *locty_data,
209 bool *selftest_done)
210 {
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);
217 }
218
219 static void tpm_passthrough_worker_thread(gpointer data,
220 gpointer user_data)
221 {
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;
226
227 DPRINTF("tpm_passthrough: processing command type %d\n", cmd);
228
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);
234
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;
244 }
245 }
246
247 /*
248 * Start the TPM (thread). If it had been started before, then terminate
249 * and start it again.
250 */
251 static int tpm_passthrough_startup_tpm(TPMBackend *tb)
252 {
253 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
254
255 /* terminate a running TPM */
256 tpm_backend_thread_end(&tpm_pt->tbt);
257
258 tpm_backend_thread_create(&tpm_pt->tbt,
259 tpm_passthrough_worker_thread,
260 &tpm_pt->tpm_thread_params);
261
262 return 0;
263 }
264
265 static void tpm_passthrough_reset(TPMBackend *tb)
266 {
267 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
268
269 DPRINTF("tpm_passthrough: CALL TO TPM_RESET!\n");
270
271 tpm_passthrough_cancel_cmd(tb);
272
273 tpm_backend_thread_end(&tpm_pt->tbt);
274
275 tpm_pt->had_startup_error = false;
276 }
277
278 static int tpm_passthrough_init(TPMBackend *tb, TPMState *s,
279 TPMRecvDataCB *recv_data_cb)
280 {
281 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
282
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;
286
287 return 0;
288 }
289
290 static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb)
291 {
292 return false;
293 }
294
295 static int tpm_passthrough_reset_tpm_established_flag(TPMBackend *tb,
296 uint8_t locty)
297 {
298 /* only a TPM 2.0 will support this */
299 return 0;
300 }
301
302 static bool tpm_passthrough_get_startup_error(TPMBackend *tb)
303 {
304 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
305
306 return tpm_pt->had_startup_error;
307 }
308
309 static size_t tpm_passthrough_realloc_buffer(TPMSizedBuffer *sb)
310 {
311 size_t wanted_size = 4096; /* Linux tpm.c buffer size */
312
313 if (sb->size != wanted_size) {
314 sb->buffer = g_realloc(sb->buffer, wanted_size);
315 sb->size = wanted_size;
316 }
317 return sb->size;
318 }
319
320 static void tpm_passthrough_deliver_request(TPMBackend *tb)
321 {
322 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
323
324 tpm_backend_thread_deliver_request(&tpm_pt->tbt);
325 }
326
327 static void tpm_passthrough_cancel_cmd(TPMBackend *tb)
328 {
329 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
330 int n;
331
332 /*
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.
337 */
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;
346 }
347 } else {
348 error_report("Cannot cancel TPM command due to missing "
349 "TPM sysfs cancel entry");
350 }
351 }
352 }
353
354 static const char *tpm_passthrough_create_desc(void)
355 {
356 return "Passthrough TPM backend driver";
357 }
358
359 static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb)
360 {
361 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
362
363 return tpm_pt->tpm_version;
364 }
365
366 /*
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
371 */
372 static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb)
373 {
374 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
375 int fd = -1;
376 char *dev;
377 char path[PATH_MAX];
378
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));
384 }
385 return fd;
386 }
387
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));
399 }
400 }
401 } else {
402 error_report("tpm_passthrough: Bad TPM device path %s",
403 tpm_pt->tpm_dev);
404 }
405
406 return fd;
407 }
408
409 static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
410 {
411 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
412 const char *value;
413
414 value = qemu_opt_get(opts, "cancel-path");
415 tb->cancel_path = g_strdup(value);
416
417 value = qemu_opt_get(opts, "path");
418 if (!value) {
419 value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
420 }
421
422 tpm_pt->tpm_dev = g_strdup(value);
423
424 tb->path = g_strdup(tpm_pt->tpm_dev);
425
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;
431 }
432
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;
437 }
438
439 return 0;
440
441 err_close_tpmdev:
442 qemu_close(tpm_pt->tpm_fd);
443 tpm_pt->tpm_fd = -1;
444
445 err_free_parameters:
446 g_free(tb->path);
447 tb->path = NULL;
448
449 g_free(tpm_pt->tpm_dev);
450 tpm_pt->tpm_dev = NULL;
451
452 return 1;
453 }
454
455 static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
456 {
457 Object *obj = object_new(TYPE_TPM_PASSTHROUGH);
458 TPMBackend *tb = TPM_BACKEND(obj);
459 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
460
461 tb->id = g_strdup(id);
462 /* let frontend set the fe_model to proper value */
463 tb->fe_model = -1;
464
465 tb->ops = &tpm_passthrough_driver;
466
467 if (tpm_passthrough_handle_device_opts(opts, tb)) {
468 goto err_exit;
469 }
470
471 tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tb);
472 if (tpm_pt->cancel_fd < 0) {
473 goto err_exit;
474 }
475
476 return tb;
477
478 err_exit:
479 g_free(tb->id);
480
481 return NULL;
482 }
483
484 static void tpm_passthrough_destroy(TPMBackend *tb)
485 {
486 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
487
488 tpm_passthrough_cancel_cmd(tb);
489
490 tpm_backend_thread_end(&tpm_pt->tbt);
491
492 qemu_close(tpm_pt->tpm_fd);
493 qemu_close(tpm_pt->cancel_fd);
494
495 g_free(tb->id);
496 g_free(tb->path);
497 g_free(tb->cancel_path);
498 g_free(tpm_pt->tpm_dev);
499 }
500
501 static const QemuOptDesc tpm_passthrough_cmdline_opts[] = {
502 TPM_STANDARD_CMDLINE_OPTS,
503 {
504 .name = "cancel-path",
505 .type = QEMU_OPT_STRING,
506 .help = "Sysfs file entry for canceling TPM commands",
507 },
508 {
509 .name = "path",
510 .type = QEMU_OPT_STRING,
511 .help = "Path to TPM device on the host",
512 },
513 { /* end of list */ },
514 };
515
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,
532 };
533
534 static void tpm_passthrough_inst_init(Object *obj)
535 {
536 }
537
538 static void tpm_passthrough_inst_finalize(Object *obj)
539 {
540 }
541
542 static void tpm_passthrough_class_init(ObjectClass *klass, void *data)
543 {
544 TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);
545
546 tbc->ops = &tpm_passthrough_driver;
547 }
548
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,
556 };
557
558 static void tpm_passthrough_register(void)
559 {
560 type_register_static(&tpm_passthrough_info);
561 tpm_register_driver(&tpm_passthrough_driver);
562 }
563
564 type_init(tpm_passthrough_register)