]>
Commit | Line | Data |
---|---|---|
f4ede81e AV |
1 | /* |
2 | * Emulator TPM driver | |
3 | * | |
4 | * Copyright (c) 2017 Intel Corporation | |
5 | * Author: Amarnath Valluri <amarnath.valluri@intel.com> | |
6 | * | |
38ab74e7 | 7 | * Copyright (c) 2010 - 2013, 2018 IBM Corporation |
f4ede81e AV |
8 | * Authors: |
9 | * Stefan Berger <stefanb@us.ibm.com> | |
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 | |
eac2fce9 | 17 | * version 2.1 of the License, or (at your option) any later version. |
f4ede81e AV |
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" | |
0b8fa32f | 31 | #include "qemu/module.h" |
f4ede81e | 32 | #include "qemu/sockets.h" |
bf5dcf8f | 33 | #include "qemu/lockable.h" |
f4ede81e | 34 | #include "io/channel-socket.h" |
a0bcec03 | 35 | #include "sysemu/runstate.h" |
f4ede81e | 36 | #include "sysemu/tpm_backend.h" |
0f7d2148 | 37 | #include "sysemu/tpm_util.h" |
f4ede81e | 38 | #include "tpm_int.h" |
f4ede81e AV |
39 | #include "tpm_ioctl.h" |
40 | #include "migration/blocker.h" | |
d6454270 | 41 | #include "migration/vmstate.h" |
f4ede81e AV |
42 | #include "qapi/error.h" |
43 | #include "qapi/clone-visitor.h" | |
9af23989 | 44 | #include "qapi/qapi-visit-tpm.h" |
f4ede81e | 45 | #include "chardev/char-fe.h" |
9d9dcd96 | 46 | #include "trace.h" |
db1015e9 | 47 | #include "qom/object.h" |
f4ede81e AV |
48 | |
49 | #define TYPE_TPM_EMULATOR "tpm-emulator" | |
8063396b | 50 | OBJECT_DECLARE_SIMPLE_TYPE(TPMEmulator, TPM_EMULATOR) |
f4ede81e AV |
51 | |
52 | #define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps & (cap)) == (cap)) | |
53 | ||
f4ede81e | 54 | /* data structures */ |
38ab74e7 SB |
55 | |
56 | /* blobs from the TPM; part of VM state when migrating */ | |
57 | typedef struct TPMBlobBuffers { | |
58 | uint32_t permanent_flags; | |
59 | TPMSizedBuffer permanent; | |
60 | ||
61 | uint32_t volatil_flags; | |
62 | TPMSizedBuffer volatil; | |
63 | ||
64 | uint32_t savestate_flags; | |
65 | TPMSizedBuffer savestate; | |
66 | } TPMBlobBuffers; | |
67 | ||
db1015e9 | 68 | struct TPMEmulator { |
f4ede81e AV |
69 | TPMBackend parent; |
70 | ||
71 | TPMEmulatorOptions *options; | |
72 | CharBackend ctrl_chr; | |
73 | QIOChannel *data_ioc; | |
74 | TPMVersion tpm_version; | |
75 | ptm_cap caps; /* capabilities of the TPM */ | |
76 | uint8_t cur_locty_number; /* last set locality */ | |
77 | Error *migration_blocker; | |
17b1af77 MAL |
78 | |
79 | QemuMutex mutex; | |
0b4c7c65 SB |
80 | |
81 | unsigned int established_flag:1; | |
82 | unsigned int established_flag_cached:1; | |
38ab74e7 SB |
83 | |
84 | TPMBlobBuffers state_blobs; | |
99bdcd2c SB |
85 | |
86 | bool relock_storage; | |
87 | VMChangeStateEntry *vmstate; | |
db1015e9 | 88 | }; |
f4ede81e | 89 | |
7e095e84 SB |
90 | struct tpm_error { |
91 | uint32_t tpm_result; | |
92 | const char *string; | |
93 | }; | |
94 | ||
95 | static const struct tpm_error tpm_errors[] = { | |
96 | /* TPM 1.2 error codes */ | |
97 | { TPM_BAD_PARAMETER , "a parameter is bad" }, | |
98 | { TPM_FAIL , "operation failed" }, | |
99 | { TPM_KEYNOTFOUND , "key could not be found" }, | |
100 | { TPM_BAD_PARAM_SIZE , "bad parameter size"}, | |
101 | { TPM_ENCRYPT_ERROR , "encryption error" }, | |
102 | { TPM_DECRYPT_ERROR , "decryption error" }, | |
103 | { TPM_BAD_KEY_PROPERTY, "bad key property" }, | |
104 | { TPM_BAD_MODE , "bad (encryption) mode" }, | |
105 | { TPM_BAD_VERSION , "bad version identifier" }, | |
106 | { TPM_BAD_LOCALITY , "bad locality" }, | |
107 | /* TPM 2 error codes */ | |
108 | { TPM_RC_FAILURE , "operation failed" }, | |
109 | { TPM_RC_LOCALITY , "bad locality" }, | |
110 | { TPM_RC_INSUFFICIENT, "insufficient amount of data" }, | |
111 | }; | |
112 | ||
113 | static const char *tpm_emulator_strerror(uint32_t tpm_result) | |
114 | { | |
115 | size_t i; | |
116 | ||
117 | for (i = 0; i < ARRAY_SIZE(tpm_errors); i++) { | |
118 | if (tpm_errors[i].tpm_result == tpm_result) { | |
119 | return tpm_errors[i].string; | |
120 | } | |
121 | } | |
122 | return ""; | |
123 | } | |
f4ede81e | 124 | |
17b1af77 | 125 | static int tpm_emulator_ctrlcmd(TPMEmulator *tpm, unsigned long cmd, void *msg, |
f4ede81e AV |
126 | size_t msg_len_in, size_t msg_len_out) |
127 | { | |
17b1af77 | 128 | CharBackend *dev = &tpm->ctrl_chr; |
f4ede81e AV |
129 | uint32_t cmd_no = cpu_to_be32(cmd); |
130 | ssize_t n = sizeof(uint32_t) + msg_len_in; | |
131 | uint8_t *buf = NULL; | |
17b1af77 | 132 | |
bf5dcf8f PMD |
133 | WITH_QEMU_LOCK_GUARD(&tpm->mutex) { |
134 | buf = g_alloca(n); | |
135 | memcpy(buf, &cmd_no, sizeof(cmd_no)); | |
136 | memcpy(buf + sizeof(cmd_no), msg, msg_len_in); | |
f4ede81e | 137 | |
bf5dcf8f | 138 | n = qemu_chr_fe_write_all(dev, buf, n); |
f4ede81e | 139 | if (n <= 0) { |
bf5dcf8f | 140 | return -1; |
f4ede81e | 141 | } |
f4ede81e | 142 | |
bf5dcf8f PMD |
143 | if (msg_len_out != 0) { |
144 | n = qemu_chr_fe_read_all(dev, msg, msg_len_out); | |
145 | if (n <= 0) { | |
146 | return -1; | |
147 | } | |
148 | } | |
149 | } | |
17b1af77 | 150 | |
bf5dcf8f | 151 | return 0; |
f4ede81e AV |
152 | } |
153 | ||
154 | static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu, | |
155 | const uint8_t *in, uint32_t in_len, | |
156 | uint8_t *out, uint32_t out_len, | |
157 | bool *selftest_done, | |
e04e3321 | 158 | Error **errp) |
f4ede81e AV |
159 | { |
160 | ssize_t ret; | |
161 | bool is_selftest = false; | |
f4ede81e AV |
162 | |
163 | if (selftest_done) { | |
164 | *selftest_done = false; | |
165 | is_selftest = tpm_util_is_selftest(in, in_len); | |
166 | } | |
167 | ||
e04e3321 | 168 | ret = qio_channel_write_all(tpm_emu->data_ioc, (char *)in, in_len, errp); |
f4ede81e AV |
169 | if (ret != 0) { |
170 | return -1; | |
171 | } | |
172 | ||
cc1b6c55 | 173 | ret = qio_channel_read_all(tpm_emu->data_ioc, (char *)out, |
e04e3321 | 174 | sizeof(struct tpm_resp_hdr), errp); |
f4ede81e AV |
175 | if (ret != 0) { |
176 | return -1; | |
177 | } | |
178 | ||
cc1b6c55 MAL |
179 | ret = qio_channel_read_all(tpm_emu->data_ioc, |
180 | (char *)out + sizeof(struct tpm_resp_hdr), | |
e04e3321 | 181 | tpm_cmd_get_size(out) - sizeof(struct tpm_resp_hdr), errp); |
f4ede81e AV |
182 | if (ret != 0) { |
183 | return -1; | |
184 | } | |
185 | ||
186 | if (is_selftest) { | |
cc1b6c55 | 187 | *selftest_done = tpm_cmd_get_errcode(out) == 0; |
f4ede81e AV |
188 | } |
189 | ||
190 | return 0; | |
191 | } | |
192 | ||
c106ede9 MAL |
193 | static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number, |
194 | Error **errp) | |
f4ede81e AV |
195 | { |
196 | ptm_loc loc; | |
197 | ||
f4ede81e AV |
198 | if (tpm_emu->cur_locty_number == locty_number) { |
199 | return 0; | |
200 | } | |
201 | ||
9d9dcd96 SB |
202 | trace_tpm_emulator_set_locality(locty_number); |
203 | ||
eff1fe9f | 204 | memset(&loc, 0, sizeof(loc)); |
f4ede81e | 205 | loc.u.req.loc = locty_number; |
17b1af77 | 206 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_LOCALITY, &loc, |
f4ede81e | 207 | sizeof(loc), sizeof(loc)) < 0) { |
c106ede9 MAL |
208 | error_setg(errp, "tpm-emulator: could not set locality : %s", |
209 | strerror(errno)); | |
f4ede81e AV |
210 | return -1; |
211 | } | |
212 | ||
213 | loc.u.resp.tpm_result = be32_to_cpu(loc.u.resp.tpm_result); | |
214 | if (loc.u.resp.tpm_result != 0) { | |
c106ede9 MAL |
215 | error_setg(errp, "tpm-emulator: TPM result for set locality : 0x%x", |
216 | loc.u.resp.tpm_result); | |
f4ede81e AV |
217 | return -1; |
218 | } | |
219 | ||
220 | tpm_emu->cur_locty_number = locty_number; | |
221 | ||
222 | return 0; | |
223 | } | |
224 | ||
6a8a2354 MAL |
225 | static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd, |
226 | Error **errp) | |
f4ede81e AV |
227 | { |
228 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
f4ede81e | 229 | |
9d9dcd96 | 230 | trace_tpm_emulator_handle_request(); |
905e78ba | 231 | |
6a8a2354 MAL |
232 | if (tpm_emulator_set_locality(tpm_emu, cmd->locty, errp) < 0 || |
233 | tpm_emulator_unix_tx_bufs(tpm_emu, cmd->in, cmd->in_len, | |
0e43b7e6 | 234 | cmd->out, cmd->out_len, |
6a8a2354 MAL |
235 | &cmd->selftest_done, errp) < 0) { |
236 | tpm_util_write_fatal_error_response(cmd->out, cmd->out_len); | |
0e43b7e6 | 237 | } |
f4ede81e AV |
238 | } |
239 | ||
240 | static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu) | |
241 | { | |
17b1af77 MAL |
242 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_CAPABILITY, |
243 | &tpm_emu->caps, 0, sizeof(tpm_emu->caps)) < 0) { | |
f4ede81e AV |
244 | error_report("tpm-emulator: probing failed : %s", strerror(errno)); |
245 | return -1; | |
246 | } | |
247 | ||
248 | tpm_emu->caps = be64_to_cpu(tpm_emu->caps); | |
249 | ||
9d9dcd96 | 250 | trace_tpm_emulator_probe_caps(tpm_emu->caps); |
f4ede81e AV |
251 | |
252 | return 0; | |
253 | } | |
254 | ||
255 | static int tpm_emulator_check_caps(TPMEmulator *tpm_emu) | |
256 | { | |
257 | ptm_cap caps = 0; | |
258 | const char *tpm = NULL; | |
259 | ||
260 | /* check for min. required capabilities */ | |
261 | switch (tpm_emu->tpm_version) { | |
262 | case TPM_VERSION_1_2: | |
263 | caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED | | |
9375c44f SB |
264 | PTM_CAP_SET_LOCALITY | PTM_CAP_SET_DATAFD | PTM_CAP_STOP | |
265 | PTM_CAP_SET_BUFFERSIZE; | |
f4ede81e AV |
266 | tpm = "1.2"; |
267 | break; | |
268 | case TPM_VERSION_2_0: | |
269 | caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED | | |
270 | PTM_CAP_SET_LOCALITY | PTM_CAP_RESET_TPMESTABLISHED | | |
9375c44f | 271 | PTM_CAP_SET_DATAFD | PTM_CAP_STOP | PTM_CAP_SET_BUFFERSIZE; |
f4ede81e AV |
272 | tpm = "2"; |
273 | break; | |
274 | case TPM_VERSION_UNSPEC: | |
275 | error_report("tpm-emulator: TPM version has not been set"); | |
276 | return -1; | |
277 | } | |
278 | ||
279 | if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, caps)) { | |
280 | error_report("tpm-emulator: TPM does not implement minimum set of " | |
281 | "required capabilities for TPM %s (0x%x)", tpm, (int)caps); | |
282 | return -1; | |
283 | } | |
284 | ||
285 | return 0; | |
286 | } | |
287 | ||
9375c44f SB |
288 | static int tpm_emulator_stop_tpm(TPMBackend *tb) |
289 | { | |
290 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
291 | ptm_res res; | |
292 | ||
293 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_STOP, &res, 0, sizeof(res)) < 0) { | |
294 | error_report("tpm-emulator: Could not stop TPM: %s", | |
295 | strerror(errno)); | |
296 | return -1; | |
297 | } | |
298 | ||
299 | res = be32_to_cpu(res); | |
300 | if (res) { | |
7e095e84 SB |
301 | error_report("tpm-emulator: TPM result for CMD_STOP: 0x%x %s", res, |
302 | tpm_emulator_strerror(res)); | |
9375c44f SB |
303 | return -1; |
304 | } | |
305 | ||
306 | return 0; | |
307 | } | |
308 | ||
99bdcd2c SB |
309 | static int tpm_emulator_lock_storage(TPMEmulator *tpm_emu) |
310 | { | |
311 | ptm_lockstorage pls; | |
312 | ||
313 | if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, PTM_CAP_LOCK_STORAGE)) { | |
314 | trace_tpm_emulator_lock_storage_cmd_not_supt(); | |
315 | return 0; | |
316 | } | |
317 | ||
318 | /* give failing side 300 * 10ms time to release lock */ | |
319 | pls.u.req.retries = cpu_to_be32(300); | |
320 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_LOCK_STORAGE, &pls, | |
321 | sizeof(pls.u.req), sizeof(pls.u.resp)) < 0) { | |
322 | error_report("tpm-emulator: Could not lock storage within 3 seconds: " | |
323 | "%s", strerror(errno)); | |
324 | return -1; | |
325 | } | |
326 | ||
327 | pls.u.resp.tpm_result = be32_to_cpu(pls.u.resp.tpm_result); | |
328 | if (pls.u.resp.tpm_result != 0) { | |
329 | error_report("tpm-emulator: TPM result for CMD_LOCK_STORAGE: 0x%x %s", | |
330 | pls.u.resp.tpm_result, | |
331 | tpm_emulator_strerror(pls.u.resp.tpm_result)); | |
332 | return -1; | |
333 | } | |
334 | ||
335 | return 0; | |
336 | } | |
337 | ||
9375c44f SB |
338 | static int tpm_emulator_set_buffer_size(TPMBackend *tb, |
339 | size_t wanted_size, | |
340 | size_t *actual_size) | |
341 | { | |
342 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
343 | ptm_setbuffersize psbs; | |
344 | ||
345 | if (tpm_emulator_stop_tpm(tb) < 0) { | |
346 | return -1; | |
347 | } | |
348 | ||
349 | psbs.u.req.buffersize = cpu_to_be32(wanted_size); | |
350 | ||
351 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_BUFFERSIZE, &psbs, | |
352 | sizeof(psbs.u.req), sizeof(psbs.u.resp)) < 0) { | |
353 | error_report("tpm-emulator: Could not set buffer size: %s", | |
354 | strerror(errno)); | |
355 | return -1; | |
356 | } | |
357 | ||
358 | psbs.u.resp.tpm_result = be32_to_cpu(psbs.u.resp.tpm_result); | |
359 | if (psbs.u.resp.tpm_result != 0) { | |
7e095e84 SB |
360 | error_report("tpm-emulator: TPM result for set buffer size : 0x%x %s", |
361 | psbs.u.resp.tpm_result, | |
362 | tpm_emulator_strerror(psbs.u.resp.tpm_result)); | |
9375c44f SB |
363 | return -1; |
364 | } | |
365 | ||
366 | if (actual_size) { | |
367 | *actual_size = be32_to_cpu(psbs.u.resp.buffersize); | |
368 | } | |
369 | ||
9d9dcd96 | 370 | trace_tpm_emulator_set_buffer_size( |
9375c44f SB |
371 | be32_to_cpu(psbs.u.resp.buffersize), |
372 | be32_to_cpu(psbs.u.resp.minsize), | |
373 | be32_to_cpu(psbs.u.resp.maxsize)); | |
374 | ||
375 | return 0; | |
376 | } | |
377 | ||
38ab74e7 SB |
378 | static int tpm_emulator_startup_tpm_resume(TPMBackend *tb, size_t buffersize, |
379 | bool is_resume) | |
f4ede81e AV |
380 | { |
381 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
30270587 SB |
382 | ptm_init init = { |
383 | .u.req.init_flags = 0, | |
384 | }; | |
f4ede81e AV |
385 | ptm_res res; |
386 | ||
38ab74e7 SB |
387 | trace_tpm_emulator_startup_tpm_resume(is_resume, buffersize); |
388 | ||
9375c44f SB |
389 | if (buffersize != 0 && |
390 | tpm_emulator_set_buffer_size(tb, buffersize, NULL) < 0) { | |
391 | goto err_exit; | |
392 | } | |
393 | ||
38ab74e7 SB |
394 | if (is_resume) { |
395 | init.u.req.init_flags |= cpu_to_be32(PTM_INIT_FLAG_DELETE_VOLATILE); | |
396 | } | |
397 | ||
17b1af77 MAL |
398 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_INIT, &init, sizeof(init), |
399 | sizeof(init)) < 0) { | |
f4ede81e AV |
400 | error_report("tpm-emulator: could not send INIT: %s", |
401 | strerror(errno)); | |
402 | goto err_exit; | |
403 | } | |
404 | ||
405 | res = be32_to_cpu(init.u.resp.tpm_result); | |
406 | if (res) { | |
7e095e84 SB |
407 | error_report("tpm-emulator: TPM result for CMD_INIT: 0x%x %s", res, |
408 | tpm_emulator_strerror(res)); | |
f4ede81e AV |
409 | goto err_exit; |
410 | } | |
411 | return 0; | |
412 | ||
413 | err_exit: | |
414 | return -1; | |
415 | } | |
416 | ||
38ab74e7 SB |
417 | static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize) |
418 | { | |
a0bcec03 RL |
419 | /* TPM startup will be done from post_load hook */ |
420 | if (runstate_check(RUN_STATE_INMIGRATE)) { | |
421 | if (buffersize != 0) { | |
422 | return tpm_emulator_set_buffer_size(tb, buffersize, NULL); | |
423 | } | |
424 | ||
425 | return 0; | |
426 | } | |
427 | ||
38ab74e7 SB |
428 | return tpm_emulator_startup_tpm_resume(tb, buffersize, false); |
429 | } | |
430 | ||
f4ede81e AV |
431 | static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb) |
432 | { | |
433 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
434 | ptm_est est; | |
435 | ||
0b4c7c65 SB |
436 | if (tpm_emu->established_flag_cached) { |
437 | return tpm_emu->established_flag; | |
438 | } | |
439 | ||
17b1af77 | 440 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_TPMESTABLISHED, &est, |
f4ede81e AV |
441 | 0, sizeof(est)) < 0) { |
442 | error_report("tpm-emulator: Could not get the TPM established flag: %s", | |
443 | strerror(errno)); | |
444 | return false; | |
445 | } | |
9d9dcd96 | 446 | trace_tpm_emulator_get_tpm_established_flag(est.u.resp.bit); |
0b4c7c65 SB |
447 | |
448 | tpm_emu->established_flag_cached = 1; | |
449 | tpm_emu->established_flag = (est.u.resp.bit != 0); | |
f4ede81e | 450 | |
0b4c7c65 | 451 | return tpm_emu->established_flag; |
f4ede81e AV |
452 | } |
453 | ||
454 | static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb, | |
455 | uint8_t locty) | |
456 | { | |
457 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
458 | ptm_reset_est reset_est; | |
459 | ptm_res res; | |
460 | ||
461 | /* only a TPM 2.0 will support this */ | |
462 | if (tpm_emu->tpm_version != TPM_VERSION_2_0) { | |
463 | return 0; | |
464 | } | |
465 | ||
466 | reset_est.u.req.loc = tpm_emu->cur_locty_number; | |
17b1af77 | 467 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_RESET_TPMESTABLISHED, |
f4ede81e AV |
468 | &reset_est, sizeof(reset_est), |
469 | sizeof(reset_est)) < 0) { | |
470 | error_report("tpm-emulator: Could not reset the establishment bit: %s", | |
471 | strerror(errno)); | |
472 | return -1; | |
473 | } | |
474 | ||
475 | res = be32_to_cpu(reset_est.u.resp.tpm_result); | |
476 | if (res) { | |
7e095e84 SB |
477 | error_report( |
478 | "tpm-emulator: TPM result for rest established flag: 0x%x %s", | |
479 | res, tpm_emulator_strerror(res)); | |
f4ede81e AV |
480 | return -1; |
481 | } | |
482 | ||
0b4c7c65 SB |
483 | tpm_emu->established_flag_cached = 0; |
484 | ||
f4ede81e AV |
485 | return 0; |
486 | } | |
487 | ||
488 | static void tpm_emulator_cancel_cmd(TPMBackend *tb) | |
489 | { | |
490 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
491 | ptm_res res; | |
492 | ||
493 | if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, PTM_CAP_CANCEL_TPM_CMD)) { | |
9d9dcd96 | 494 | trace_tpm_emulator_cancel_cmd_not_supt(); |
f4ede81e AV |
495 | return; |
496 | } | |
497 | ||
3d011411 | 498 | /* FIXME: make the function non-blocking, or it may block a VCPU */ |
17b1af77 | 499 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_CANCEL_TPM_CMD, &res, 0, |
f4ede81e AV |
500 | sizeof(res)) < 0) { |
501 | error_report("tpm-emulator: Could not cancel command: %s", | |
502 | strerror(errno)); | |
503 | } else if (res != 0) { | |
504 | error_report("tpm-emulator: Failed to cancel TPM: 0x%x", | |
505 | be32_to_cpu(res)); | |
506 | } | |
507 | } | |
508 | ||
509 | static TPMVersion tpm_emulator_get_tpm_version(TPMBackend *tb) | |
510 | { | |
511 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
512 | ||
513 | return tpm_emu->tpm_version; | |
514 | } | |
515 | ||
b21e6aaf SB |
516 | static size_t tpm_emulator_get_buffer_size(TPMBackend *tb) |
517 | { | |
9375c44f SB |
518 | size_t actual_size; |
519 | ||
520 | if (tpm_emulator_set_buffer_size(tb, 0, &actual_size) < 0) { | |
521 | return 4096; | |
522 | } | |
523 | ||
524 | return actual_size; | |
b21e6aaf SB |
525 | } |
526 | ||
f4ede81e AV |
527 | static int tpm_emulator_block_migration(TPMEmulator *tpm_emu) |
528 | { | |
529 | Error *err = NULL; | |
38ab74e7 SB |
530 | ptm_cap caps = PTM_CAP_GET_STATEBLOB | PTM_CAP_SET_STATEBLOB | |
531 | PTM_CAP_STOP; | |
f4ede81e | 532 | |
38ab74e7 SB |
533 | if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, caps)) { |
534 | error_setg(&tpm_emu->migration_blocker, | |
535 | "Migration disabled: TPM emulator does not support " | |
536 | "migration"); | |
c8a7fc51 | 537 | if (migrate_add_blocker(&tpm_emu->migration_blocker, &err) < 0) { |
38ab74e7 | 538 | error_report_err(err); |
38ab74e7 SB |
539 | return -1; |
540 | } | |
f4ede81e AV |
541 | } |
542 | ||
543 | return 0; | |
544 | } | |
545 | ||
546 | static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_emu) | |
547 | { | |
548 | ptm_res res; | |
549 | Error *err = NULL; | |
550 | int fds[2] = { -1, -1 }; | |
551 | ||
0038e9a2 | 552 | if (qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { |
f4ede81e AV |
553 | error_report("tpm-emulator: Failed to create socketpair"); |
554 | return -1; | |
555 | } | |
556 | ||
557 | qemu_chr_fe_set_msgfds(&tpm_emu->ctrl_chr, fds + 1, 1); | |
558 | ||
17b1af77 MAL |
559 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_DATAFD, &res, 0, |
560 | sizeof(res)) < 0 || res != 0) { | |
f4ede81e AV |
561 | error_report("tpm-emulator: Failed to send CMD_SET_DATAFD: %s", |
562 | strerror(errno)); | |
563 | goto err_exit; | |
564 | } | |
565 | ||
566 | tpm_emu->data_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fds[0], &err)); | |
567 | if (err) { | |
568 | error_prepend(&err, "tpm-emulator: Failed to create io channel: "); | |
569 | error_report_err(err); | |
570 | goto err_exit; | |
571 | } | |
572 | ||
25657fc6 | 573 | close(fds[1]); |
f4ede81e AV |
574 | |
575 | return 0; | |
576 | ||
577 | err_exit: | |
25657fc6 MAL |
578 | close(fds[0]); |
579 | close(fds[1]); | |
f4ede81e AV |
580 | return -1; |
581 | } | |
582 | ||
583 | static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_emu, QemuOpts *opts) | |
584 | { | |
585 | const char *value; | |
88f83074 SB |
586 | Error *err = NULL; |
587 | Chardev *dev; | |
f4ede81e AV |
588 | |
589 | value = qemu_opt_get(opts, "chardev"); | |
88f83074 SB |
590 | if (!value) { |
591 | error_report("tpm-emulator: parameter 'chardev' is missing"); | |
592 | goto err; | |
593 | } | |
f4ede81e | 594 | |
88f83074 SB |
595 | dev = qemu_chr_find(value); |
596 | if (!dev) { | |
597 | error_report("tpm-emulator: tpm chardev '%s' not found", value); | |
598 | goto err; | |
599 | } | |
f4ede81e | 600 | |
88f83074 SB |
601 | if (!qemu_chr_fe_init(&tpm_emu->ctrl_chr, dev, &err)) { |
602 | error_prepend(&err, "tpm-emulator: No valid chardev found at '%s':", | |
603 | value); | |
604 | error_report_err(err); | |
605 | goto err; | |
f4ede81e AV |
606 | } |
607 | ||
88f83074 SB |
608 | tpm_emu->options->chardev = g_strdup(value); |
609 | ||
f4ede81e AV |
610 | if (tpm_emulator_prepare_data_fd(tpm_emu) < 0) { |
611 | goto err; | |
612 | } | |
613 | ||
614 | /* FIXME: tpm_util_test_tpmdev() accepts only on socket fd, as it also used | |
615 | * by passthrough driver, which not yet using GIOChannel. | |
616 | */ | |
617 | if (tpm_util_test_tpmdev(QIO_CHANNEL_SOCKET(tpm_emu->data_ioc)->fd, | |
618 | &tpm_emu->tpm_version)) { | |
619 | error_report("'%s' is not emulating TPM device. Error: %s", | |
620 | tpm_emu->options->chardev, strerror(errno)); | |
621 | goto err; | |
622 | } | |
623 | ||
9d9dcd96 SB |
624 | switch (tpm_emu->tpm_version) { |
625 | case TPM_VERSION_1_2: | |
626 | trace_tpm_emulator_handle_device_opts_tpm12(); | |
627 | break; | |
628 | case TPM_VERSION_2_0: | |
629 | trace_tpm_emulator_handle_device_opts_tpm2(); | |
630 | break; | |
631 | default: | |
632 | trace_tpm_emulator_handle_device_opts_unspec(); | |
633 | } | |
f4ede81e AV |
634 | |
635 | if (tpm_emulator_probe_caps(tpm_emu) || | |
636 | tpm_emulator_check_caps(tpm_emu)) { | |
637 | goto err; | |
638 | } | |
639 | ||
640 | return tpm_emulator_block_migration(tpm_emu); | |
641 | ||
642 | err: | |
9d9dcd96 SB |
643 | trace_tpm_emulator_handle_device_opts_startup_error(); |
644 | ||
f4ede81e AV |
645 | return -1; |
646 | } | |
647 | ||
9f7c0ef2 | 648 | static TPMBackend *tpm_emulator_create(QemuOpts *opts) |
f4ede81e AV |
649 | { |
650 | TPMBackend *tb = TPM_BACKEND(object_new(TYPE_TPM_EMULATOR)); | |
651 | ||
f4ede81e | 652 | if (tpm_emulator_handle_device_opts(TPM_EMULATOR(tb), opts)) { |
9f7c0ef2 MAL |
653 | object_unref(OBJECT(tb)); |
654 | return NULL; | |
f4ede81e AV |
655 | } |
656 | ||
657 | return tb; | |
f4ede81e AV |
658 | } |
659 | ||
660 | static TpmTypeOptions *tpm_emulator_get_tpm_options(TPMBackend *tb) | |
661 | { | |
662 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
663 | TpmTypeOptions *options = g_new0(TpmTypeOptions, 1); | |
664 | ||
39dc3e4a | 665 | options->type = TPM_TYPE_EMULATOR; |
f4ede81e AV |
666 | options->u.emulator.data = QAPI_CLONE(TPMEmulatorOptions, tpm_emu->options); |
667 | ||
668 | return options; | |
669 | } | |
670 | ||
671 | static const QemuOptDesc tpm_emulator_cmdline_opts[] = { | |
672 | TPM_STANDARD_CMDLINE_OPTS, | |
673 | { | |
674 | .name = "chardev", | |
675 | .type = QEMU_OPT_STRING, | |
676 | .help = "Character device to use for out-of-band control messages", | |
677 | }, | |
678 | { /* end of list */ }, | |
679 | }; | |
680 | ||
38ab74e7 SB |
681 | /* |
682 | * Transfer a TPM state blob from the TPM into a provided buffer. | |
683 | * | |
684 | * @tpm_emu: TPMEmulator | |
685 | * @type: the type of blob to transfer | |
686 | * @tsb: the TPMSizeBuffer to fill with the blob | |
687 | * @flags: the flags to return to the caller | |
688 | */ | |
689 | static int tpm_emulator_get_state_blob(TPMEmulator *tpm_emu, | |
690 | uint8_t type, | |
691 | TPMSizedBuffer *tsb, | |
692 | uint32_t *flags) | |
693 | { | |
694 | ptm_getstate pgs; | |
695 | ptm_res res; | |
696 | ssize_t n; | |
697 | uint32_t totlength, length; | |
698 | ||
699 | tpm_sized_buffer_reset(tsb); | |
700 | ||
701 | pgs.u.req.state_flags = cpu_to_be32(PTM_STATE_FLAG_DECRYPTED); | |
702 | pgs.u.req.type = cpu_to_be32(type); | |
703 | pgs.u.req.offset = 0; | |
704 | ||
705 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_STATEBLOB, | |
706 | &pgs, sizeof(pgs.u.req), | |
707 | offsetof(ptm_getstate, u.resp.data)) < 0) { | |
708 | error_report("tpm-emulator: could not get state blob type %d : %s", | |
709 | type, strerror(errno)); | |
710 | return -1; | |
711 | } | |
712 | ||
713 | res = be32_to_cpu(pgs.u.resp.tpm_result); | |
714 | if (res != 0 && (res & 0x800) == 0) { | |
715 | error_report("tpm-emulator: Getting the stateblob (type %d) failed " | |
7e095e84 SB |
716 | "with a TPM error 0x%x %s", type, res, |
717 | tpm_emulator_strerror(res)); | |
38ab74e7 SB |
718 | return -1; |
719 | } | |
720 | ||
721 | totlength = be32_to_cpu(pgs.u.resp.totlength); | |
722 | length = be32_to_cpu(pgs.u.resp.length); | |
723 | if (totlength != length) { | |
724 | error_report("tpm-emulator: Expecting to read %u bytes " | |
725 | "but would get %u", totlength, length); | |
726 | return -1; | |
727 | } | |
728 | ||
729 | *flags = be32_to_cpu(pgs.u.resp.state_flags); | |
730 | ||
731 | if (totlength > 0) { | |
732 | tsb->buffer = g_try_malloc(totlength); | |
733 | if (!tsb->buffer) { | |
734 | error_report("tpm-emulator: Out of memory allocating %u bytes", | |
735 | totlength); | |
736 | return -1; | |
737 | } | |
738 | ||
739 | n = qemu_chr_fe_read_all(&tpm_emu->ctrl_chr, tsb->buffer, totlength); | |
740 | if (n != totlength) { | |
741 | error_report("tpm-emulator: Could not read stateblob (type %d); " | |
742 | "expected %u bytes, got %zd", | |
743 | type, totlength, n); | |
744 | return -1; | |
745 | } | |
746 | } | |
747 | tsb->size = totlength; | |
748 | ||
749 | trace_tpm_emulator_get_state_blob(type, tsb->size, *flags); | |
750 | ||
751 | return 0; | |
752 | } | |
753 | ||
754 | static int tpm_emulator_get_state_blobs(TPMEmulator *tpm_emu) | |
755 | { | |
756 | TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs; | |
757 | ||
758 | if (tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_PERMANENT, | |
759 | &state_blobs->permanent, | |
760 | &state_blobs->permanent_flags) < 0 || | |
761 | tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_VOLATILE, | |
762 | &state_blobs->volatil, | |
763 | &state_blobs->volatil_flags) < 0 || | |
764 | tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_SAVESTATE, | |
765 | &state_blobs->savestate, | |
766 | &state_blobs->savestate_flags) < 0) { | |
767 | goto err_exit; | |
768 | } | |
769 | ||
770 | return 0; | |
771 | ||
772 | err_exit: | |
773 | tpm_sized_buffer_reset(&state_blobs->volatil); | |
774 | tpm_sized_buffer_reset(&state_blobs->permanent); | |
775 | tpm_sized_buffer_reset(&state_blobs->savestate); | |
776 | ||
777 | return -1; | |
778 | } | |
779 | ||
780 | /* | |
781 | * Transfer a TPM state blob to the TPM emulator. | |
782 | * | |
783 | * @tpm_emu: TPMEmulator | |
784 | * @type: the type of TPM state blob to transfer | |
785 | * @tsb: TPMSizedBuffer containing the TPM state blob | |
786 | * @flags: Flags describing the (encryption) state of the TPM state blob | |
787 | */ | |
788 | static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu, | |
789 | uint32_t type, | |
790 | TPMSizedBuffer *tsb, | |
791 | uint32_t flags) | |
792 | { | |
793 | ssize_t n; | |
794 | ptm_setstate pss; | |
795 | ptm_res tpm_result; | |
796 | ||
797 | if (tsb->size == 0) { | |
798 | return 0; | |
799 | } | |
800 | ||
801 | pss = (ptm_setstate) { | |
802 | .u.req.state_flags = cpu_to_be32(flags), | |
803 | .u.req.type = cpu_to_be32(type), | |
804 | .u.req.length = cpu_to_be32(tsb->size), | |
805 | }; | |
806 | ||
807 | /* write the header only */ | |
808 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_STATEBLOB, &pss, | |
809 | offsetof(ptm_setstate, u.req.data), 0) < 0) { | |
810 | error_report("tpm-emulator: could not set state blob type %d : %s", | |
811 | type, strerror(errno)); | |
812 | return -1; | |
813 | } | |
814 | ||
815 | /* now the body */ | |
816 | n = qemu_chr_fe_write_all(&tpm_emu->ctrl_chr, tsb->buffer, tsb->size); | |
817 | if (n != tsb->size) { | |
818 | error_report("tpm-emulator: Writing the stateblob (type %d) " | |
819 | "failed; could not write %u bytes, but only %zd", | |
820 | type, tsb->size, n); | |
821 | return -1; | |
822 | } | |
823 | ||
824 | /* now get the result */ | |
825 | n = qemu_chr_fe_read_all(&tpm_emu->ctrl_chr, | |
826 | (uint8_t *)&pss, sizeof(pss.u.resp)); | |
827 | if (n != sizeof(pss.u.resp)) { | |
828 | error_report("tpm-emulator: Reading response from writing stateblob " | |
829 | "(type %d) failed; expected %zu bytes, got %zd", type, | |
830 | sizeof(pss.u.resp), n); | |
831 | return -1; | |
832 | } | |
833 | ||
834 | tpm_result = be32_to_cpu(pss.u.resp.tpm_result); | |
835 | if (tpm_result != 0) { | |
836 | error_report("tpm-emulator: Setting the stateblob (type %d) failed " | |
7e095e84 SB |
837 | "with a TPM error 0x%x %s", type, tpm_result, |
838 | tpm_emulator_strerror(tpm_result)); | |
38ab74e7 SB |
839 | return -1; |
840 | } | |
841 | ||
842 | trace_tpm_emulator_set_state_blob(type, tsb->size, flags); | |
843 | ||
844 | return 0; | |
845 | } | |
846 | ||
847 | /* | |
848 | * Set all the TPM state blobs. | |
849 | * | |
850 | * Returns a negative errno code in case of error. | |
851 | */ | |
852 | static int tpm_emulator_set_state_blobs(TPMBackend *tb) | |
853 | { | |
854 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
855 | TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs; | |
856 | ||
857 | trace_tpm_emulator_set_state_blobs(); | |
858 | ||
859 | if (tpm_emulator_stop_tpm(tb) < 0) { | |
860 | trace_tpm_emulator_set_state_blobs_error("Could not stop TPM"); | |
861 | return -EIO; | |
862 | } | |
863 | ||
864 | if (tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_PERMANENT, | |
865 | &state_blobs->permanent, | |
866 | state_blobs->permanent_flags) < 0 || | |
867 | tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_VOLATILE, | |
868 | &state_blobs->volatil, | |
869 | state_blobs->volatil_flags) < 0 || | |
870 | tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_SAVESTATE, | |
871 | &state_blobs->savestate, | |
872 | state_blobs->savestate_flags) < 0) { | |
873 | return -EIO; | |
874 | } | |
875 | ||
876 | trace_tpm_emulator_set_state_blobs_done(); | |
877 | ||
878 | return 0; | |
879 | } | |
880 | ||
881 | static int tpm_emulator_pre_save(void *opaque) | |
882 | { | |
883 | TPMBackend *tb = opaque; | |
884 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
99bdcd2c | 885 | int ret; |
38ab74e7 SB |
886 | |
887 | trace_tpm_emulator_pre_save(); | |
888 | ||
889 | tpm_backend_finish_sync(tb); | |
890 | ||
891 | /* get the state blobs from the TPM */ | |
99bdcd2c SB |
892 | ret = tpm_emulator_get_state_blobs(tpm_emu); |
893 | ||
894 | tpm_emu->relock_storage = ret == 0; | |
895 | ||
896 | return ret; | |
897 | } | |
898 | ||
899 | static void tpm_emulator_vm_state_change(void *opaque, bool running, | |
900 | RunState state) | |
901 | { | |
902 | TPMBackend *tb = opaque; | |
903 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
904 | ||
905 | trace_tpm_emulator_vm_state_change(running, state); | |
906 | ||
907 | if (!running || state != RUN_STATE_RUNNING || !tpm_emu->relock_storage) { | |
908 | return; | |
909 | } | |
910 | ||
911 | /* lock storage after migration fall-back */ | |
912 | tpm_emulator_lock_storage(tpm_emu); | |
38ab74e7 SB |
913 | } |
914 | ||
915 | /* | |
916 | * Load the TPM state blobs into the TPM. | |
917 | * | |
918 | * Returns negative errno codes in case of error. | |
919 | */ | |
920 | static int tpm_emulator_post_load(void *opaque, int version_id) | |
921 | { | |
922 | TPMBackend *tb = opaque; | |
923 | int ret; | |
924 | ||
925 | ret = tpm_emulator_set_state_blobs(tb); | |
926 | if (ret < 0) { | |
927 | return ret; | |
928 | } | |
929 | ||
930 | if (tpm_emulator_startup_tpm_resume(tb, 0, true) < 0) { | |
931 | return -EIO; | |
932 | } | |
933 | ||
934 | return 0; | |
935 | } | |
936 | ||
937 | static const VMStateDescription vmstate_tpm_emulator = { | |
938 | .name = "tpm-emulator", | |
939 | .version_id = 0, | |
940 | .pre_save = tpm_emulator_pre_save, | |
941 | .post_load = tpm_emulator_post_load, | |
942 | .fields = (VMStateField[]) { | |
943 | VMSTATE_UINT32(state_blobs.permanent_flags, TPMEmulator), | |
944 | VMSTATE_UINT32(state_blobs.permanent.size, TPMEmulator), | |
945 | VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.permanent.buffer, | |
946 | TPMEmulator, 0, 0, | |
947 | state_blobs.permanent.size), | |
948 | ||
949 | VMSTATE_UINT32(state_blobs.volatil_flags, TPMEmulator), | |
950 | VMSTATE_UINT32(state_blobs.volatil.size, TPMEmulator), | |
951 | VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.volatil.buffer, | |
952 | TPMEmulator, 0, 0, | |
953 | state_blobs.volatil.size), | |
954 | ||
955 | VMSTATE_UINT32(state_blobs.savestate_flags, TPMEmulator), | |
956 | VMSTATE_UINT32(state_blobs.savestate.size, TPMEmulator), | |
957 | VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.savestate.buffer, | |
958 | TPMEmulator, 0, 0, | |
959 | state_blobs.savestate.size), | |
960 | ||
961 | VMSTATE_END_OF_LIST() | |
962 | } | |
963 | }; | |
964 | ||
f4ede81e AV |
965 | static void tpm_emulator_inst_init(Object *obj) |
966 | { | |
967 | TPMEmulator *tpm_emu = TPM_EMULATOR(obj); | |
968 | ||
9d9dcd96 SB |
969 | trace_tpm_emulator_inst_init(); |
970 | ||
f4ede81e AV |
971 | tpm_emu->options = g_new0(TPMEmulatorOptions, 1); |
972 | tpm_emu->cur_locty_number = ~0; | |
17b1af77 | 973 | qemu_mutex_init(&tpm_emu->mutex); |
99bdcd2c SB |
974 | tpm_emu->vmstate = |
975 | qemu_add_vm_change_state_handler(tpm_emulator_vm_state_change, | |
976 | tpm_emu); | |
38ab74e7 | 977 | |
99b16e8e | 978 | vmstate_register_any(NULL, &vmstate_tpm_emulator, obj); |
f4ede81e AV |
979 | } |
980 | ||
981 | /* | |
982 | * Gracefully shut down the external TPM | |
983 | */ | |
984 | static void tpm_emulator_shutdown(TPMEmulator *tpm_emu) | |
985 | { | |
986 | ptm_res res; | |
987 | ||
88f83074 SB |
988 | if (!tpm_emu->options->chardev) { |
989 | /* was never properly initialized */ | |
990 | return; | |
991 | } | |
992 | ||
17b1af77 | 993 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SHUTDOWN, &res, 0, sizeof(res)) < 0) { |
f4ede81e AV |
994 | error_report("tpm-emulator: Could not cleanly shutdown the TPM: %s", |
995 | strerror(errno)); | |
996 | } else if (res != 0) { | |
7e095e84 SB |
997 | error_report("tpm-emulator: TPM result for shutdown: 0x%x %s", |
998 | be32_to_cpu(res), tpm_emulator_strerror(be32_to_cpu(res))); | |
f4ede81e AV |
999 | } |
1000 | } | |
1001 | ||
1002 | static void tpm_emulator_inst_finalize(Object *obj) | |
1003 | { | |
1004 | TPMEmulator *tpm_emu = TPM_EMULATOR(obj); | |
38ab74e7 | 1005 | TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs; |
f4ede81e AV |
1006 | |
1007 | tpm_emulator_shutdown(tpm_emu); | |
1008 | ||
1009 | object_unref(OBJECT(tpm_emu->data_ioc)); | |
1010 | ||
1011 | qemu_chr_fe_deinit(&tpm_emu->ctrl_chr, false); | |
1012 | ||
1013 | qapi_free_TPMEmulatorOptions(tpm_emu->options); | |
1014 | ||
c8a7fc51 | 1015 | migrate_del_blocker(&tpm_emu->migration_blocker); |
17b1af77 | 1016 | |
38ab74e7 SB |
1017 | tpm_sized_buffer_reset(&state_blobs->volatil); |
1018 | tpm_sized_buffer_reset(&state_blobs->permanent); | |
1019 | tpm_sized_buffer_reset(&state_blobs->savestate); | |
1020 | ||
17b1af77 | 1021 | qemu_mutex_destroy(&tpm_emu->mutex); |
99bdcd2c | 1022 | qemu_del_vm_change_state_handler(tpm_emu->vmstate); |
38ab74e7 SB |
1023 | |
1024 | vmstate_unregister(NULL, &vmstate_tpm_emulator, obj); | |
f4ede81e AV |
1025 | } |
1026 | ||
1027 | static void tpm_emulator_class_init(ObjectClass *klass, void *data) | |
1028 | { | |
1029 | TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass); | |
d31076ba MAL |
1030 | |
1031 | tbc->type = TPM_TYPE_EMULATOR; | |
1032 | tbc->opts = tpm_emulator_cmdline_opts; | |
1033 | tbc->desc = "TPM emulator backend driver"; | |
1034 | tbc->create = tpm_emulator_create; | |
1035 | tbc->startup_tpm = tpm_emulator_startup_tpm; | |
1036 | tbc->cancel_cmd = tpm_emulator_cancel_cmd; | |
1037 | tbc->get_tpm_established_flag = tpm_emulator_get_tpm_established_flag; | |
1038 | tbc->reset_tpm_established_flag = tpm_emulator_reset_tpm_established_flag; | |
1039 | tbc->get_tpm_version = tpm_emulator_get_tpm_version; | |
b21e6aaf | 1040 | tbc->get_buffer_size = tpm_emulator_get_buffer_size; |
d31076ba MAL |
1041 | tbc->get_tpm_options = tpm_emulator_get_tpm_options; |
1042 | ||
f4ede81e AV |
1043 | tbc->handle_request = tpm_emulator_handle_request; |
1044 | } | |
1045 | ||
1046 | static const TypeInfo tpm_emulator_info = { | |
1047 | .name = TYPE_TPM_EMULATOR, | |
1048 | .parent = TYPE_TPM_BACKEND, | |
1049 | .instance_size = sizeof(TPMEmulator), | |
1050 | .class_init = tpm_emulator_class_init, | |
1051 | .instance_init = tpm_emulator_inst_init, | |
1052 | .instance_finalize = tpm_emulator_inst_finalize, | |
1053 | }; | |
1054 | ||
1055 | static void tpm_emulator_register(void) | |
1056 | { | |
1057 | type_register_static(&tpm_emulator_info); | |
f4ede81e AV |
1058 | } |
1059 | ||
1060 | type_init(tpm_emulator_register) |