1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29#include "qemu/osdep.h"
30#include "qemu/error-report.h"
31#include "qemu/sockets.h"
32#include "io/channel-socket.h"
33#include "sysemu/tpm_backend.h"
34#include "tpm_int.h"
35#include "hw/hw.h"
36#include "hw/i386/pc.h"
37#include "tpm_util.h"
38#include "tpm_ioctl.h"
39#include "migration/blocker.h"
40#include "qapi/error.h"
41#include "qapi/clone-visitor.h"
42#include "chardev/char-fe.h"
43
44#include <fcntl.h>
45#include <sys/types.h>
46#include <sys/stat.h>
47#include <stdio.h>
48
49#define DEBUG_TPM 0
50
51#define DPRINTF(fmt, ...) do { \
52 if (DEBUG_TPM) { \
53 fprintf(stderr, "tpm-emulator:"fmt"\n", ## __VA_ARGS__); \
54 } \
55} while (0)
56
57#define TYPE_TPM_EMULATOR "tpm-emulator"
58#define TPM_EMULATOR(obj) \
59 OBJECT_CHECK(TPMEmulator, (obj), TYPE_TPM_EMULATOR)
60
61#define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps & (cap)) == (cap))
62
63
64typedef struct TPMEmulator {
65 TPMBackend parent;
66
67 TPMEmulatorOptions *options;
68 CharBackend ctrl_chr;
69 QIOChannel *data_ioc;
70 TPMVersion tpm_version;
71 ptm_cap caps;
72 uint8_t cur_locty_number;
73 Error *migration_blocker;
74
75 QemuMutex mutex;
76} TPMEmulator;
77
78
79static int tpm_emulator_ctrlcmd(TPMEmulator *tpm, unsigned long cmd, void *msg,
80 size_t msg_len_in, size_t msg_len_out)
81{
82 CharBackend *dev = &tpm->ctrl_chr;
83 uint32_t cmd_no = cpu_to_be32(cmd);
84 ssize_t n = sizeof(uint32_t) + msg_len_in;
85 uint8_t *buf = NULL;
86 int ret = -1;
87
88 qemu_mutex_lock(&tpm->mutex);
89
90 buf = g_alloca(n);
91 memcpy(buf, &cmd_no, sizeof(cmd_no));
92 memcpy(buf + sizeof(cmd_no), msg, msg_len_in);
93
94 n = qemu_chr_fe_write_all(dev, buf, n);
95 if (n <= 0) {
96 goto end;
97 }
98
99 if (msg_len_out != 0) {
100 n = qemu_chr_fe_read_all(dev, msg, msg_len_out);
101 if (n <= 0) {
102 goto end;
103 }
104 }
105
106 ret = 0;
107
108end:
109 qemu_mutex_unlock(&tpm->mutex);
110 return ret;
111}
112
113static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu,
114 const uint8_t *in, uint32_t in_len,
115 uint8_t *out, uint32_t out_len,
116 bool *selftest_done,
117 Error **err)
118{
119 ssize_t ret;
120 bool is_selftest = false;
121 const struct tpm_resp_hdr *hdr = NULL;
122
123 if (selftest_done) {
124 *selftest_done = false;
125 is_selftest = tpm_util_is_selftest(in, in_len);
126 }
127
128 ret = qio_channel_write_all(tpm_emu->data_ioc, (char *)in, in_len, err);
129 if (ret != 0) {
130 return -1;
131 }
132
133 ret = qio_channel_read_all(tpm_emu->data_ioc, (char *)out, sizeof(*hdr),
134 err);
135 if (ret != 0) {
136 return -1;
137 }
138
139 hdr = (struct tpm_resp_hdr *)out;
140 out += sizeof(*hdr);
141 ret = qio_channel_read_all(tpm_emu->data_ioc, (char *)out,
142 be32_to_cpu(hdr->len) - sizeof(*hdr) , err);
143 if (ret != 0) {
144 return -1;
145 }
146
147 if (is_selftest) {
148 *selftest_done = (be32_to_cpu(hdr->errcode) == 0);
149 }
150
151 return 0;
152}
153
154static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number,
155 Error **errp)
156{
157 ptm_loc loc;
158
159 DPRINTF("%s : locality: 0x%x", __func__, locty_number);
160
161 if (tpm_emu->cur_locty_number == locty_number) {
162 return 0;
163 }
164
165 DPRINTF("setting locality : 0x%x", locty_number);
166 loc.u.req.loc = locty_number;
167 if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_LOCALITY, &loc,
168 sizeof(loc), sizeof(loc)) < 0) {
169 error_setg(errp, "tpm-emulator: could not set locality : %s",
170 strerror(errno));
171 return -1;
172 }
173
174 loc.u.resp.tpm_result = be32_to_cpu(loc.u.resp.tpm_result);
175 if (loc.u.resp.tpm_result != 0) {
176 error_setg(errp, "tpm-emulator: TPM result for set locality : 0x%x",
177 loc.u.resp.tpm_result);
178 return -1;
179 }
180
181 tpm_emu->cur_locty_number = locty_number;
182
183 return 0;
184}
185
186static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd)
187{
188 TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
189 TPMIfClass *tic = TPM_IF_GET_CLASS(tb->tpm_state);
190 Error *err = NULL;
191
192 DPRINTF("processing TPM command");
193
194 if (tpm_emulator_set_locality(tpm_emu, cmd->locty, &err) < 0) {
195 goto error;
196 }
197
198 if (tpm_emulator_unix_tx_bufs(tpm_emu, cmd->in, cmd->in_len,
199 cmd->out, cmd->out_len,
200 &cmd->selftest_done, &err) < 0) {
201 goto error;
202 }
203
204 tic->request_completed(TPM_IF(tb->tpm_state));
205 return;
206
207error:
208 tpm_util_write_fatal_error_response(cmd->out, cmd->out_len);
209 error_report_err(err);
210}
211
212static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu)
213{
214 DPRINTF("%s", __func__);
215 if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_CAPABILITY,
216 &tpm_emu->caps, 0, sizeof(tpm_emu->caps)) < 0) {
217 error_report("tpm-emulator: probing failed : %s", strerror(errno));
218 return -1;
219 }
220
221 tpm_emu->caps = be64_to_cpu(tpm_emu->caps);
222
223 DPRINTF("capabilities : 0x%"PRIx64, tpm_emu->caps);
224
225 return 0;
226}
227
228static int tpm_emulator_check_caps(TPMEmulator *tpm_emu)
229{
230 ptm_cap caps = 0;
231 const char *tpm = NULL;
232
233
234 switch (tpm_emu->tpm_version) {
235 case TPM_VERSION_1_2:
236 caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED |
237 PTM_CAP_SET_LOCALITY | PTM_CAP_SET_DATAFD;
238 tpm = "1.2";
239 break;
240 case TPM_VERSION_2_0:
241 caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED |
242 PTM_CAP_SET_LOCALITY | PTM_CAP_RESET_TPMESTABLISHED |
243 PTM_CAP_SET_DATAFD;
244 tpm = "2";
245 break;
246 case TPM_VERSION_UNSPEC:
247 error_report("tpm-emulator: TPM version has not been set");
248 return -1;
249 }
250
251 if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, caps)) {
252 error_report("tpm-emulator: TPM does not implement minimum set of "
253 "required capabilities for TPM %s (0x%x)", tpm, (int)caps);
254 return -1;
255 }
256
257 return 0;
258}
259
260static int tpm_emulator_startup_tpm(TPMBackend *tb)
261{
262 TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
263 ptm_init init;
264 ptm_res res;
265
266 DPRINTF("%s", __func__);
267 if (tpm_emulator_ctrlcmd(tpm_emu, CMD_INIT, &init, sizeof(init),
268 sizeof(init)) < 0) {
269 error_report("tpm-emulator: could not send INIT: %s",
270 strerror(errno));
271 goto err_exit;
272 }
273
274 res = be32_to_cpu(init.u.resp.tpm_result);
275 if (res) {
276 error_report("tpm-emulator: TPM result for CMD_INIT: 0x%x", res);
277 goto err_exit;
278 }
279 return 0;
280
281err_exit:
282 return -1;
283}
284
285static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb)
286{
287 TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
288 ptm_est est;
289
290 DPRINTF("%s", __func__);
291 if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_TPMESTABLISHED, &est,
292 0, sizeof(est)) < 0) {
293 error_report("tpm-emulator: Could not get the TPM established flag: %s",
294 strerror(errno));
295 return false;
296 }
297 DPRINTF("established flag: %0x", est.u.resp.bit);
298
299 return (est.u.resp.bit != 0);
300}
301
302static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb,
303 uint8_t locty)
304{
305 TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
306 ptm_reset_est reset_est;
307 ptm_res res;
308
309
310 if (tpm_emu->tpm_version != TPM_VERSION_2_0) {
311 return 0;
312 }
313
314 reset_est.u.req.loc = tpm_emu->cur_locty_number;
315 if (tpm_emulator_ctrlcmd(tpm_emu, CMD_RESET_TPMESTABLISHED,
316 &reset_est, sizeof(reset_est),
317 sizeof(reset_est)) < 0) {
318 error_report("tpm-emulator: Could not reset the establishment bit: %s",
319 strerror(errno));
320 return -1;
321 }
322
323 res = be32_to_cpu(reset_est.u.resp.tpm_result);
324 if (res) {
325 error_report("tpm-emulator: TPM result for rest establixhed flag: 0x%x",
326 res);
327 return -1;
328 }
329
330 return 0;
331}
332
333static void tpm_emulator_cancel_cmd(TPMBackend *tb)
334{
335 TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
336 ptm_res res;
337
338 if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, PTM_CAP_CANCEL_TPM_CMD)) {
339 DPRINTF("Backend does not support CANCEL_TPM_CMD");
340 return;
341 }
342
343 if (tpm_emulator_ctrlcmd(tpm_emu, CMD_CANCEL_TPM_CMD, &res, 0,
344 sizeof(res)) < 0) {
345 error_report("tpm-emulator: Could not cancel command: %s",
346 strerror(errno));
347 } else if (res != 0) {
348 error_report("tpm-emulator: Failed to cancel TPM: 0x%x",
349 be32_to_cpu(res));
350 }
351}
352
353static TPMVersion tpm_emulator_get_tpm_version(TPMBackend *tb)
354{
355 TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
356
357 return tpm_emu->tpm_version;
358}
359
360static int tpm_emulator_block_migration(TPMEmulator *tpm_emu)
361{
362 Error *err = NULL;
363
364 error_setg(&tpm_emu->migration_blocker,
365 "Migration disabled: TPM emulator not yet migratable");
366 migrate_add_blocker(tpm_emu->migration_blocker, &err);
367 if (err) {
368 error_report_err(err);
369 error_free(tpm_emu->migration_blocker);
370 tpm_emu->migration_blocker = NULL;
371
372 return -1;
373 }
374
375 return 0;
376}
377
378static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_emu)
379{
380 ptm_res res;
381 Error *err = NULL;
382 int fds[2] = { -1, -1 };
383
384 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
385 error_report("tpm-emulator: Failed to create socketpair");
386 return -1;
387 }
388
389 qemu_chr_fe_set_msgfds(&tpm_emu->ctrl_chr, fds + 1, 1);
390
391 if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_DATAFD, &res, 0,
392 sizeof(res)) < 0 || res != 0) {
393 error_report("tpm-emulator: Failed to send CMD_SET_DATAFD: %s",
394 strerror(errno));
395 goto err_exit;
396 }
397
398 tpm_emu->data_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fds[0], &err));
399 if (err) {
400 error_prepend(&err, "tpm-emulator: Failed to create io channel: ");
401 error_report_err(err);
402 goto err_exit;
403 }
404
405 closesocket(fds[1]);
406
407 return 0;
408
409err_exit:
410 closesocket(fds[0]);
411 closesocket(fds[1]);
412 return -1;
413}
414
415static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_emu, QemuOpts *opts)
416{
417 const char *value;
418
419 value = qemu_opt_get(opts, "chardev");
420 if (value) {
421 Error *err = NULL;
422 Chardev *dev = qemu_chr_find(value);
423
424 if (!dev) {
425 error_report("tpm-emulator: tpm chardev '%s' not found.", value);
426 goto err;
427 }
428
429 if (!qemu_chr_fe_init(&tpm_emu->ctrl_chr, dev, &err)) {
430 error_prepend(&err, "tpm-emulator: No valid chardev found at '%s':",
431 value);
432 error_report_err(err);
433 goto err;
434 }
435
436 tpm_emu->options->chardev = g_strdup(value);
437 }
438
439 if (tpm_emulator_prepare_data_fd(tpm_emu) < 0) {
440 goto err;
441 }
442
443
444
445
446 if (tpm_util_test_tpmdev(QIO_CHANNEL_SOCKET(tpm_emu->data_ioc)->fd,
447 &tpm_emu->tpm_version)) {
448 error_report("'%s' is not emulating TPM device. Error: %s",
449 tpm_emu->options->chardev, strerror(errno));
450 goto err;
451 }
452
453 DPRINTF("TPM Version %s", tpm_emu->tpm_version == TPM_VERSION_1_2 ? "1.2" :
454 (tpm_emu->tpm_version == TPM_VERSION_2_0 ? "2.0" : "Unspecified"));
455
456 if (tpm_emulator_probe_caps(tpm_emu) ||
457 tpm_emulator_check_caps(tpm_emu)) {
458 goto err;
459 }
460
461 return tpm_emulator_block_migration(tpm_emu);
462
463err:
464 DPRINTF("Startup error");
465 return -1;
466}
467
468static TPMBackend *tpm_emulator_create(QemuOpts *opts, const char *id)
469{
470 TPMBackend *tb = TPM_BACKEND(object_new(TYPE_TPM_EMULATOR));
471
472 tb->id = g_strdup(id);
473
474 if (tpm_emulator_handle_device_opts(TPM_EMULATOR(tb), opts)) {
475 goto err_exit;
476 }
477
478 return tb;
479
480err_exit:
481 object_unref(OBJECT(tb));
482
483 return NULL;
484}
485
486static TpmTypeOptions *tpm_emulator_get_tpm_options(TPMBackend *tb)
487{
488 TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
489 TpmTypeOptions *options = g_new0(TpmTypeOptions, 1);
490
491 options->type = TPM_TYPE_OPTIONS_KIND_EMULATOR;
492 options->u.emulator.data = QAPI_CLONE(TPMEmulatorOptions, tpm_emu->options);
493
494 return options;
495}
496
497static const QemuOptDesc tpm_emulator_cmdline_opts[] = {
498 TPM_STANDARD_CMDLINE_OPTS,
499 {
500 .name = "chardev",
501 .type = QEMU_OPT_STRING,
502 .help = "Character device to use for out-of-band control messages",
503 },
504 { },
505};
506
507static void tpm_emulator_inst_init(Object *obj)
508{
509 TPMEmulator *tpm_emu = TPM_EMULATOR(obj);
510
511 DPRINTF("%s", __func__);
512 tpm_emu->options = g_new0(TPMEmulatorOptions, 1);
513 tpm_emu->cur_locty_number = ~0;
514 qemu_mutex_init(&tpm_emu->mutex);
515}
516
517
518
519
520static void tpm_emulator_shutdown(TPMEmulator *tpm_emu)
521{
522 ptm_res res;
523
524 if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SHUTDOWN, &res, 0, sizeof(res)) < 0) {
525 error_report("tpm-emulator: Could not cleanly shutdown the TPM: %s",
526 strerror(errno));
527 } else if (res != 0) {
528 error_report("tpm-emulator: TPM result for sutdown: 0x%x",
529 be32_to_cpu(res));
530 }
531}
532
533static void tpm_emulator_inst_finalize(Object *obj)
534{
535 TPMEmulator *tpm_emu = TPM_EMULATOR(obj);
536
537 tpm_emulator_shutdown(tpm_emu);
538
539 object_unref(OBJECT(tpm_emu->data_ioc));
540
541 qemu_chr_fe_deinit(&tpm_emu->ctrl_chr, false);
542
543 qapi_free_TPMEmulatorOptions(tpm_emu->options);
544
545 if (tpm_emu->migration_blocker) {
546 migrate_del_blocker(tpm_emu->migration_blocker);
547 error_free(tpm_emu->migration_blocker);
548 }
549
550 qemu_mutex_destroy(&tpm_emu->mutex);
551}
552
553static void tpm_emulator_class_init(ObjectClass *klass, void *data)
554{
555 TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);
556
557 tbc->type = TPM_TYPE_EMULATOR;
558 tbc->opts = tpm_emulator_cmdline_opts;
559 tbc->desc = "TPM emulator backend driver";
560 tbc->create = tpm_emulator_create;
561 tbc->startup_tpm = tpm_emulator_startup_tpm;
562 tbc->cancel_cmd = tpm_emulator_cancel_cmd;
563 tbc->get_tpm_established_flag = tpm_emulator_get_tpm_established_flag;
564 tbc->reset_tpm_established_flag = tpm_emulator_reset_tpm_established_flag;
565 tbc->get_tpm_version = tpm_emulator_get_tpm_version;
566 tbc->get_tpm_options = tpm_emulator_get_tpm_options;
567
568 tbc->handle_request = tpm_emulator_handle_request;
569}
570
571static const TypeInfo tpm_emulator_info = {
572 .name = TYPE_TPM_EMULATOR,
573 .parent = TYPE_TPM_BACKEND,
574 .instance_size = sizeof(TPMEmulator),
575 .class_init = tpm_emulator_class_init,
576 .instance_init = tpm_emulator_inst_init,
577 .instance_finalize = tpm_emulator_inst_finalize,
578};
579
580static void tpm_emulator_register(void)
581{
582 type_register_static(&tpm_emulator_info);
583}
584
585type_init(tpm_emulator_register)
586