]>
Commit | Line | Data |
---|---|---|
f4ede81e AV |
1 | /* |
2 | * Emulator TPM driver | |
3 | * | |
4 | * Copyright (c) 2017 Intel Corporation | |
5 | * Author: Amarnath Valluri <[email protected]> | |
6 | * | |
7 | * Copyright (c) 2010 - 2013 IBM Corporation | |
8 | * Authors: | |
9 | * Stefan Berger <[email protected]> | |
10 | * | |
11 | * Copyright (C) 2011 IAIK, Graz University of Technology | |
12 | * Author: Andreas Niederl | |
13 | * | |
14 | * This library is free software; you can redistribute it and/or | |
15 | * modify it under the terms of the GNU Lesser General Public | |
16 | * License as published by the Free Software Foundation; either | |
17 | * version 2 of the License, or (at your option) any later version. | |
18 | * | |
19 | * This library is distributed in the hope that it will be useful, | |
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
22 | * Lesser General Public License for more details. | |
23 | * | |
24 | * You should have received a copy of the GNU Lesser General Public | |
25 | * License along with this library; if not, see <http://www.gnu.org/licenses/> | |
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" | |
f4ede81e AV |
36 | #include "tpm_util.h" |
37 | #include "tpm_ioctl.h" | |
38 | #include "migration/blocker.h" | |
39 | #include "qapi/error.h" | |
40 | #include "qapi/clone-visitor.h" | |
41 | #include "chardev/char-fe.h" | |
42 | ||
43 | #include <fcntl.h> | |
44 | #include <sys/types.h> | |
45 | #include <sys/stat.h> | |
46 | #include <stdio.h> | |
47 | ||
48 | #define DEBUG_TPM 0 | |
49 | ||
50 | #define DPRINTF(fmt, ...) do { \ | |
51 | if (DEBUG_TPM) { \ | |
52 | fprintf(stderr, "tpm-emulator:"fmt"\n", ## __VA_ARGS__); \ | |
53 | } \ | |
54 | } while (0) | |
55 | ||
56 | #define TYPE_TPM_EMULATOR "tpm-emulator" | |
57 | #define TPM_EMULATOR(obj) \ | |
58 | OBJECT_CHECK(TPMEmulator, (obj), TYPE_TPM_EMULATOR) | |
59 | ||
60 | #define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps & (cap)) == (cap)) | |
61 | ||
f4ede81e AV |
62 | /* data structures */ |
63 | typedef struct TPMEmulator { | |
64 | TPMBackend parent; | |
65 | ||
66 | TPMEmulatorOptions *options; | |
67 | CharBackend ctrl_chr; | |
68 | QIOChannel *data_ioc; | |
69 | TPMVersion tpm_version; | |
70 | ptm_cap caps; /* capabilities of the TPM */ | |
71 | uint8_t cur_locty_number; /* last set locality */ | |
72 | Error *migration_blocker; | |
17b1af77 MAL |
73 | |
74 | QemuMutex mutex; | |
0b4c7c65 SB |
75 | |
76 | unsigned int established_flag:1; | |
77 | unsigned int established_flag_cached:1; | |
f4ede81e AV |
78 | } TPMEmulator; |
79 | ||
80 | ||
17b1af77 | 81 | static int tpm_emulator_ctrlcmd(TPMEmulator *tpm, unsigned long cmd, void *msg, |
f4ede81e AV |
82 | size_t msg_len_in, size_t msg_len_out) |
83 | { | |
17b1af77 | 84 | CharBackend *dev = &tpm->ctrl_chr; |
f4ede81e AV |
85 | uint32_t cmd_no = cpu_to_be32(cmd); |
86 | ssize_t n = sizeof(uint32_t) + msg_len_in; | |
87 | uint8_t *buf = NULL; | |
17b1af77 MAL |
88 | int ret = -1; |
89 | ||
90 | qemu_mutex_lock(&tpm->mutex); | |
f4ede81e AV |
91 | |
92 | buf = g_alloca(n); | |
93 | memcpy(buf, &cmd_no, sizeof(cmd_no)); | |
94 | memcpy(buf + sizeof(cmd_no), msg, msg_len_in); | |
95 | ||
96 | n = qemu_chr_fe_write_all(dev, buf, n); | |
97 | if (n <= 0) { | |
17b1af77 | 98 | goto end; |
f4ede81e AV |
99 | } |
100 | ||
101 | if (msg_len_out != 0) { | |
102 | n = qemu_chr_fe_read_all(dev, msg, msg_len_out); | |
103 | if (n <= 0) { | |
17b1af77 | 104 | goto end; |
f4ede81e AV |
105 | } |
106 | } | |
107 | ||
17b1af77 MAL |
108 | ret = 0; |
109 | ||
110 | end: | |
111 | qemu_mutex_unlock(&tpm->mutex); | |
112 | return ret; | |
f4ede81e AV |
113 | } |
114 | ||
115 | static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu, | |
116 | const uint8_t *in, uint32_t in_len, | |
117 | uint8_t *out, uint32_t out_len, | |
118 | bool *selftest_done, | |
119 | Error **err) | |
120 | { | |
121 | ssize_t ret; | |
122 | bool is_selftest = false; | |
f4ede81e AV |
123 | |
124 | if (selftest_done) { | |
125 | *selftest_done = false; | |
126 | is_selftest = tpm_util_is_selftest(in, in_len); | |
127 | } | |
128 | ||
129 | ret = qio_channel_write_all(tpm_emu->data_ioc, (char *)in, in_len, err); | |
130 | if (ret != 0) { | |
131 | return -1; | |
132 | } | |
133 | ||
cc1b6c55 MAL |
134 | ret = qio_channel_read_all(tpm_emu->data_ioc, (char *)out, |
135 | sizeof(struct tpm_resp_hdr), err); | |
f4ede81e AV |
136 | if (ret != 0) { |
137 | return -1; | |
138 | } | |
139 | ||
cc1b6c55 MAL |
140 | ret = qio_channel_read_all(tpm_emu->data_ioc, |
141 | (char *)out + sizeof(struct tpm_resp_hdr), | |
142 | tpm_cmd_get_size(out) - sizeof(struct tpm_resp_hdr), err); | |
f4ede81e AV |
143 | if (ret != 0) { |
144 | return -1; | |
145 | } | |
146 | ||
147 | if (is_selftest) { | |
cc1b6c55 | 148 | *selftest_done = tpm_cmd_get_errcode(out) == 0; |
f4ede81e AV |
149 | } |
150 | ||
151 | return 0; | |
152 | } | |
153 | ||
c106ede9 MAL |
154 | static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number, |
155 | Error **errp) | |
f4ede81e AV |
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; | |
17b1af77 | 167 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_LOCALITY, &loc, |
f4ede81e | 168 | sizeof(loc), sizeof(loc)) < 0) { |
c106ede9 MAL |
169 | error_setg(errp, "tpm-emulator: could not set locality : %s", |
170 | strerror(errno)); | |
f4ede81e AV |
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) { | |
c106ede9 MAL |
176 | error_setg(errp, "tpm-emulator: TPM result for set locality : 0x%x", |
177 | loc.u.resp.tpm_result); | |
f4ede81e AV |
178 | return -1; |
179 | } | |
180 | ||
181 | tpm_emu->cur_locty_number = locty_number; | |
182 | ||
183 | return 0; | |
184 | } | |
185 | ||
0e43b7e6 | 186 | static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd) |
f4ede81e AV |
187 | { |
188 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
f4ede81e AV |
189 | Error *err = NULL; |
190 | ||
905e78ba MAL |
191 | DPRINTF("processing TPM command"); |
192 | ||
d2809766 | 193 | if (tpm_emulator_set_locality(tpm_emu, cmd->locty, &err) < 0) { |
0e43b7e6 | 194 | goto error; |
f4ede81e | 195 | } |
905e78ba | 196 | |
0e43b7e6 MAL |
197 | if (tpm_emulator_unix_tx_bufs(tpm_emu, cmd->in, cmd->in_len, |
198 | cmd->out, cmd->out_len, | |
199 | &cmd->selftest_done, &err) < 0) { | |
200 | goto error; | |
201 | } | |
202 | ||
0e43b7e6 MAL |
203 | return; |
204 | ||
205 | error: | |
206 | tpm_util_write_fatal_error_response(cmd->out, cmd->out_len); | |
207 | error_report_err(err); | |
f4ede81e AV |
208 | } |
209 | ||
210 | static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu) | |
211 | { | |
212 | DPRINTF("%s", __func__); | |
17b1af77 MAL |
213 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_CAPABILITY, |
214 | &tpm_emu->caps, 0, sizeof(tpm_emu->caps)) < 0) { | |
f4ede81e AV |
215 | error_report("tpm-emulator: probing failed : %s", strerror(errno)); |
216 | return -1; | |
217 | } | |
218 | ||
219 | tpm_emu->caps = be64_to_cpu(tpm_emu->caps); | |
220 | ||
221 | DPRINTF("capabilities : 0x%"PRIx64, tpm_emu->caps); | |
222 | ||
223 | return 0; | |
224 | } | |
225 | ||
226 | static int tpm_emulator_check_caps(TPMEmulator *tpm_emu) | |
227 | { | |
228 | ptm_cap caps = 0; | |
229 | const char *tpm = NULL; | |
230 | ||
231 | /* check for min. required capabilities */ | |
232 | switch (tpm_emu->tpm_version) { | |
233 | case TPM_VERSION_1_2: | |
234 | caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED | | |
9375c44f SB |
235 | PTM_CAP_SET_LOCALITY | PTM_CAP_SET_DATAFD | PTM_CAP_STOP | |
236 | PTM_CAP_SET_BUFFERSIZE; | |
f4ede81e AV |
237 | tpm = "1.2"; |
238 | break; | |
239 | case TPM_VERSION_2_0: | |
240 | caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED | | |
241 | PTM_CAP_SET_LOCALITY | PTM_CAP_RESET_TPMESTABLISHED | | |
9375c44f | 242 | PTM_CAP_SET_DATAFD | PTM_CAP_STOP | PTM_CAP_SET_BUFFERSIZE; |
f4ede81e AV |
243 | tpm = "2"; |
244 | break; | |
245 | case TPM_VERSION_UNSPEC: | |
246 | error_report("tpm-emulator: TPM version has not been set"); | |
247 | return -1; | |
248 | } | |
249 | ||
250 | if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, caps)) { | |
251 | error_report("tpm-emulator: TPM does not implement minimum set of " | |
252 | "required capabilities for TPM %s (0x%x)", tpm, (int)caps); | |
253 | return -1; | |
254 | } | |
255 | ||
256 | return 0; | |
257 | } | |
258 | ||
9375c44f SB |
259 | static int tpm_emulator_stop_tpm(TPMBackend *tb) |
260 | { | |
261 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
262 | ptm_res res; | |
263 | ||
264 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_STOP, &res, 0, sizeof(res)) < 0) { | |
265 | error_report("tpm-emulator: Could not stop TPM: %s", | |
266 | strerror(errno)); | |
267 | return -1; | |
268 | } | |
269 | ||
270 | res = be32_to_cpu(res); | |
271 | if (res) { | |
272 | error_report("tpm-emulator: TPM result for CMD_STOP: 0x%x", res); | |
273 | return -1; | |
274 | } | |
275 | ||
276 | return 0; | |
277 | } | |
278 | ||
279 | static int tpm_emulator_set_buffer_size(TPMBackend *tb, | |
280 | size_t wanted_size, | |
281 | size_t *actual_size) | |
282 | { | |
283 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
284 | ptm_setbuffersize psbs; | |
285 | ||
286 | if (tpm_emulator_stop_tpm(tb) < 0) { | |
287 | return -1; | |
288 | } | |
289 | ||
290 | psbs.u.req.buffersize = cpu_to_be32(wanted_size); | |
291 | ||
292 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_BUFFERSIZE, &psbs, | |
293 | sizeof(psbs.u.req), sizeof(psbs.u.resp)) < 0) { | |
294 | error_report("tpm-emulator: Could not set buffer size: %s", | |
295 | strerror(errno)); | |
296 | return -1; | |
297 | } | |
298 | ||
299 | psbs.u.resp.tpm_result = be32_to_cpu(psbs.u.resp.tpm_result); | |
300 | if (psbs.u.resp.tpm_result != 0) { | |
301 | error_report("tpm-emulator: TPM result for set buffer size : 0x%x", | |
302 | psbs.u.resp.tpm_result); | |
303 | return -1; | |
304 | } | |
305 | ||
306 | if (actual_size) { | |
307 | *actual_size = be32_to_cpu(psbs.u.resp.buffersize); | |
308 | } | |
309 | ||
310 | DPRINTF("buffer size: %u, min: %u, max: %u\n", | |
311 | be32_to_cpu(psbs.u.resp.buffersize), | |
312 | be32_to_cpu(psbs.u.resp.minsize), | |
313 | be32_to_cpu(psbs.u.resp.maxsize)); | |
314 | ||
315 | return 0; | |
316 | } | |
317 | ||
318 | static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize) | |
f4ede81e AV |
319 | { |
320 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
30270587 SB |
321 | ptm_init init = { |
322 | .u.req.init_flags = 0, | |
323 | }; | |
f4ede81e AV |
324 | ptm_res res; |
325 | ||
9375c44f SB |
326 | if (buffersize != 0 && |
327 | tpm_emulator_set_buffer_size(tb, buffersize, NULL) < 0) { | |
328 | goto err_exit; | |
329 | } | |
330 | ||
f4ede81e | 331 | DPRINTF("%s", __func__); |
17b1af77 MAL |
332 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_INIT, &init, sizeof(init), |
333 | sizeof(init)) < 0) { | |
f4ede81e AV |
334 | error_report("tpm-emulator: could not send INIT: %s", |
335 | strerror(errno)); | |
336 | goto err_exit; | |
337 | } | |
338 | ||
339 | res = be32_to_cpu(init.u.resp.tpm_result); | |
340 | if (res) { | |
341 | error_report("tpm-emulator: TPM result for CMD_INIT: 0x%x", res); | |
342 | goto err_exit; | |
343 | } | |
344 | return 0; | |
345 | ||
346 | err_exit: | |
347 | return -1; | |
348 | } | |
349 | ||
350 | static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb) | |
351 | { | |
352 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
353 | ptm_est est; | |
354 | ||
0b4c7c65 SB |
355 | if (tpm_emu->established_flag_cached) { |
356 | return tpm_emu->established_flag; | |
357 | } | |
358 | ||
17b1af77 | 359 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_TPMESTABLISHED, &est, |
f4ede81e AV |
360 | 0, sizeof(est)) < 0) { |
361 | error_report("tpm-emulator: Could not get the TPM established flag: %s", | |
362 | strerror(errno)); | |
363 | return false; | |
364 | } | |
0b4c7c65 SB |
365 | DPRINTF("got established flag: %0x", est.u.resp.bit); |
366 | ||
367 | tpm_emu->established_flag_cached = 1; | |
368 | tpm_emu->established_flag = (est.u.resp.bit != 0); | |
f4ede81e | 369 | |
0b4c7c65 | 370 | return tpm_emu->established_flag; |
f4ede81e AV |
371 | } |
372 | ||
373 | static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb, | |
374 | uint8_t locty) | |
375 | { | |
376 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
377 | ptm_reset_est reset_est; | |
378 | ptm_res res; | |
379 | ||
380 | /* only a TPM 2.0 will support this */ | |
381 | if (tpm_emu->tpm_version != TPM_VERSION_2_0) { | |
382 | return 0; | |
383 | } | |
384 | ||
385 | reset_est.u.req.loc = tpm_emu->cur_locty_number; | |
17b1af77 | 386 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_RESET_TPMESTABLISHED, |
f4ede81e AV |
387 | &reset_est, sizeof(reset_est), |
388 | sizeof(reset_est)) < 0) { | |
389 | error_report("tpm-emulator: Could not reset the establishment bit: %s", | |
390 | strerror(errno)); | |
391 | return -1; | |
392 | } | |
393 | ||
394 | res = be32_to_cpu(reset_est.u.resp.tpm_result); | |
395 | if (res) { | |
396 | error_report("tpm-emulator: TPM result for rest establixhed flag: 0x%x", | |
397 | res); | |
398 | return -1; | |
399 | } | |
400 | ||
0b4c7c65 SB |
401 | tpm_emu->established_flag_cached = 0; |
402 | ||
f4ede81e AV |
403 | return 0; |
404 | } | |
405 | ||
406 | static void tpm_emulator_cancel_cmd(TPMBackend *tb) | |
407 | { | |
408 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
409 | ptm_res res; | |
410 | ||
411 | if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, PTM_CAP_CANCEL_TPM_CMD)) { | |
412 | DPRINTF("Backend does not support CANCEL_TPM_CMD"); | |
413 | return; | |
414 | } | |
415 | ||
3d011411 | 416 | /* FIXME: make the function non-blocking, or it may block a VCPU */ |
17b1af77 | 417 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_CANCEL_TPM_CMD, &res, 0, |
f4ede81e AV |
418 | sizeof(res)) < 0) { |
419 | error_report("tpm-emulator: Could not cancel command: %s", | |
420 | strerror(errno)); | |
421 | } else if (res != 0) { | |
422 | error_report("tpm-emulator: Failed to cancel TPM: 0x%x", | |
423 | be32_to_cpu(res)); | |
424 | } | |
425 | } | |
426 | ||
427 | static TPMVersion tpm_emulator_get_tpm_version(TPMBackend *tb) | |
428 | { | |
429 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
430 | ||
431 | return tpm_emu->tpm_version; | |
432 | } | |
433 | ||
b21e6aaf SB |
434 | static size_t tpm_emulator_get_buffer_size(TPMBackend *tb) |
435 | { | |
9375c44f SB |
436 | size_t actual_size; |
437 | ||
438 | if (tpm_emulator_set_buffer_size(tb, 0, &actual_size) < 0) { | |
439 | return 4096; | |
440 | } | |
441 | ||
442 | return actual_size; | |
b21e6aaf SB |
443 | } |
444 | ||
f4ede81e AV |
445 | static int tpm_emulator_block_migration(TPMEmulator *tpm_emu) |
446 | { | |
447 | Error *err = NULL; | |
448 | ||
449 | error_setg(&tpm_emu->migration_blocker, | |
450 | "Migration disabled: TPM emulator not yet migratable"); | |
451 | migrate_add_blocker(tpm_emu->migration_blocker, &err); | |
452 | if (err) { | |
453 | error_report_err(err); | |
454 | error_free(tpm_emu->migration_blocker); | |
455 | tpm_emu->migration_blocker = NULL; | |
456 | ||
457 | return -1; | |
458 | } | |
459 | ||
460 | return 0; | |
461 | } | |
462 | ||
463 | static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_emu) | |
464 | { | |
465 | ptm_res res; | |
466 | Error *err = NULL; | |
467 | int fds[2] = { -1, -1 }; | |
468 | ||
469 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { | |
470 | error_report("tpm-emulator: Failed to create socketpair"); | |
471 | return -1; | |
472 | } | |
473 | ||
474 | qemu_chr_fe_set_msgfds(&tpm_emu->ctrl_chr, fds + 1, 1); | |
475 | ||
17b1af77 MAL |
476 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_DATAFD, &res, 0, |
477 | sizeof(res)) < 0 || res != 0) { | |
f4ede81e AV |
478 | error_report("tpm-emulator: Failed to send CMD_SET_DATAFD: %s", |
479 | strerror(errno)); | |
480 | goto err_exit; | |
481 | } | |
482 | ||
483 | tpm_emu->data_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fds[0], &err)); | |
484 | if (err) { | |
485 | error_prepend(&err, "tpm-emulator: Failed to create io channel: "); | |
486 | error_report_err(err); | |
487 | goto err_exit; | |
488 | } | |
489 | ||
490 | closesocket(fds[1]); | |
491 | ||
492 | return 0; | |
493 | ||
494 | err_exit: | |
495 | closesocket(fds[0]); | |
496 | closesocket(fds[1]); | |
497 | return -1; | |
498 | } | |
499 | ||
500 | static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_emu, QemuOpts *opts) | |
501 | { | |
502 | const char *value; | |
503 | ||
504 | value = qemu_opt_get(opts, "chardev"); | |
505 | if (value) { | |
506 | Error *err = NULL; | |
507 | Chardev *dev = qemu_chr_find(value); | |
508 | ||
509 | if (!dev) { | |
510 | error_report("tpm-emulator: tpm chardev '%s' not found.", value); | |
511 | goto err; | |
512 | } | |
513 | ||
514 | if (!qemu_chr_fe_init(&tpm_emu->ctrl_chr, dev, &err)) { | |
515 | error_prepend(&err, "tpm-emulator: No valid chardev found at '%s':", | |
516 | value); | |
517 | error_report_err(err); | |
518 | goto err; | |
519 | } | |
520 | ||
521 | tpm_emu->options->chardev = g_strdup(value); | |
522 | } | |
523 | ||
524 | if (tpm_emulator_prepare_data_fd(tpm_emu) < 0) { | |
525 | goto err; | |
526 | } | |
527 | ||
528 | /* FIXME: tpm_util_test_tpmdev() accepts only on socket fd, as it also used | |
529 | * by passthrough driver, which not yet using GIOChannel. | |
530 | */ | |
531 | if (tpm_util_test_tpmdev(QIO_CHANNEL_SOCKET(tpm_emu->data_ioc)->fd, | |
532 | &tpm_emu->tpm_version)) { | |
533 | error_report("'%s' is not emulating TPM device. Error: %s", | |
534 | tpm_emu->options->chardev, strerror(errno)); | |
535 | goto err; | |
536 | } | |
537 | ||
538 | DPRINTF("TPM Version %s", tpm_emu->tpm_version == TPM_VERSION_1_2 ? "1.2" : | |
539 | (tpm_emu->tpm_version == TPM_VERSION_2_0 ? "2.0" : "Unspecified")); | |
540 | ||
541 | if (tpm_emulator_probe_caps(tpm_emu) || | |
542 | tpm_emulator_check_caps(tpm_emu)) { | |
543 | goto err; | |
544 | } | |
545 | ||
546 | return tpm_emulator_block_migration(tpm_emu); | |
547 | ||
548 | err: | |
549 | DPRINTF("Startup error"); | |
550 | return -1; | |
551 | } | |
552 | ||
9f7c0ef2 | 553 | static TPMBackend *tpm_emulator_create(QemuOpts *opts) |
f4ede81e AV |
554 | { |
555 | TPMBackend *tb = TPM_BACKEND(object_new(TYPE_TPM_EMULATOR)); | |
556 | ||
f4ede81e | 557 | if (tpm_emulator_handle_device_opts(TPM_EMULATOR(tb), opts)) { |
9f7c0ef2 MAL |
558 | object_unref(OBJECT(tb)); |
559 | return NULL; | |
f4ede81e AV |
560 | } |
561 | ||
562 | return tb; | |
f4ede81e AV |
563 | } |
564 | ||
565 | static TpmTypeOptions *tpm_emulator_get_tpm_options(TPMBackend *tb) | |
566 | { | |
567 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
568 | TpmTypeOptions *options = g_new0(TpmTypeOptions, 1); | |
569 | ||
570 | options->type = TPM_TYPE_OPTIONS_KIND_EMULATOR; | |
571 | options->u.emulator.data = QAPI_CLONE(TPMEmulatorOptions, tpm_emu->options); | |
572 | ||
573 | return options; | |
574 | } | |
575 | ||
576 | static const QemuOptDesc tpm_emulator_cmdline_opts[] = { | |
577 | TPM_STANDARD_CMDLINE_OPTS, | |
578 | { | |
579 | .name = "chardev", | |
580 | .type = QEMU_OPT_STRING, | |
581 | .help = "Character device to use for out-of-band control messages", | |
582 | }, | |
583 | { /* end of list */ }, | |
584 | }; | |
585 | ||
f4ede81e AV |
586 | static void tpm_emulator_inst_init(Object *obj) |
587 | { | |
588 | TPMEmulator *tpm_emu = TPM_EMULATOR(obj); | |
589 | ||
590 | DPRINTF("%s", __func__); | |
591 | tpm_emu->options = g_new0(TPMEmulatorOptions, 1); | |
592 | tpm_emu->cur_locty_number = ~0; | |
17b1af77 | 593 | qemu_mutex_init(&tpm_emu->mutex); |
f4ede81e AV |
594 | } |
595 | ||
596 | /* | |
597 | * Gracefully shut down the external TPM | |
598 | */ | |
599 | static void tpm_emulator_shutdown(TPMEmulator *tpm_emu) | |
600 | { | |
601 | ptm_res res; | |
602 | ||
17b1af77 | 603 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SHUTDOWN, &res, 0, sizeof(res)) < 0) { |
f4ede81e AV |
604 | error_report("tpm-emulator: Could not cleanly shutdown the TPM: %s", |
605 | strerror(errno)); | |
606 | } else if (res != 0) { | |
607 | error_report("tpm-emulator: TPM result for sutdown: 0x%x", | |
608 | be32_to_cpu(res)); | |
609 | } | |
610 | } | |
611 | ||
612 | static void tpm_emulator_inst_finalize(Object *obj) | |
613 | { | |
614 | TPMEmulator *tpm_emu = TPM_EMULATOR(obj); | |
615 | ||
616 | tpm_emulator_shutdown(tpm_emu); | |
617 | ||
618 | object_unref(OBJECT(tpm_emu->data_ioc)); | |
619 | ||
620 | qemu_chr_fe_deinit(&tpm_emu->ctrl_chr, false); | |
621 | ||
622 | qapi_free_TPMEmulatorOptions(tpm_emu->options); | |
623 | ||
624 | if (tpm_emu->migration_blocker) { | |
625 | migrate_del_blocker(tpm_emu->migration_blocker); | |
626 | error_free(tpm_emu->migration_blocker); | |
627 | } | |
17b1af77 MAL |
628 | |
629 | qemu_mutex_destroy(&tpm_emu->mutex); | |
f4ede81e AV |
630 | } |
631 | ||
632 | static void tpm_emulator_class_init(ObjectClass *klass, void *data) | |
633 | { | |
634 | TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass); | |
d31076ba MAL |
635 | |
636 | tbc->type = TPM_TYPE_EMULATOR; | |
637 | tbc->opts = tpm_emulator_cmdline_opts; | |
638 | tbc->desc = "TPM emulator backend driver"; | |
639 | tbc->create = tpm_emulator_create; | |
640 | tbc->startup_tpm = tpm_emulator_startup_tpm; | |
641 | tbc->cancel_cmd = tpm_emulator_cancel_cmd; | |
642 | tbc->get_tpm_established_flag = tpm_emulator_get_tpm_established_flag; | |
643 | tbc->reset_tpm_established_flag = tpm_emulator_reset_tpm_established_flag; | |
644 | tbc->get_tpm_version = tpm_emulator_get_tpm_version; | |
b21e6aaf | 645 | tbc->get_buffer_size = tpm_emulator_get_buffer_size; |
d31076ba MAL |
646 | tbc->get_tpm_options = tpm_emulator_get_tpm_options; |
647 | ||
f4ede81e AV |
648 | tbc->handle_request = tpm_emulator_handle_request; |
649 | } | |
650 | ||
651 | static const TypeInfo tpm_emulator_info = { | |
652 | .name = TYPE_TPM_EMULATOR, | |
653 | .parent = TYPE_TPM_BACKEND, | |
654 | .instance_size = sizeof(TPMEmulator), | |
655 | .class_init = tpm_emulator_class_init, | |
656 | .instance_init = tpm_emulator_inst_init, | |
657 | .instance_finalize = tpm_emulator_inst_finalize, | |
658 | }; | |
659 | ||
660 | static void tpm_emulator_register(void) | |
661 | { | |
662 | type_register_static(&tpm_emulator_info); | |
f4ede81e AV |
663 | } |
664 | ||
665 | type_init(tpm_emulator_register) |