]>
Commit | Line | Data |
---|---|---|
f4ede81e AV |
1 | /* |
2 | * Emulator TPM driver | |
3 | * | |
4 | * Copyright (c) 2017 Intel Corporation | |
5 | * Author: Amarnath Valluri <[email protected]> | |
6 | * | |
38ab74e7 | 7 | * Copyright (c) 2010 - 2013, 2018 IBM Corporation |
f4ede81e AV |
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" | |
0b8fa32f | 31 | #include "qemu/module.h" |
f4ede81e AV |
32 | #include "qemu/sockets.h" |
33 | #include "io/channel-socket.h" | |
34 | #include "sysemu/tpm_backend.h" | |
0f7d2148 | 35 | #include "sysemu/tpm_util.h" |
f4ede81e | 36 | #include "tpm_int.h" |
f4ede81e AV |
37 | #include "tpm_ioctl.h" |
38 | #include "migration/blocker.h" | |
d6454270 | 39 | #include "migration/vmstate.h" |
f4ede81e AV |
40 | #include "qapi/error.h" |
41 | #include "qapi/clone-visitor.h" | |
9af23989 | 42 | #include "qapi/qapi-visit-tpm.h" |
f4ede81e | 43 | #include "chardev/char-fe.h" |
9d9dcd96 | 44 | #include "trace.h" |
f4ede81e AV |
45 | |
46 | #define TYPE_TPM_EMULATOR "tpm-emulator" | |
47 | #define TPM_EMULATOR(obj) \ | |
48 | OBJECT_CHECK(TPMEmulator, (obj), TYPE_TPM_EMULATOR) | |
49 | ||
50 | #define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps & (cap)) == (cap)) | |
51 | ||
f4ede81e | 52 | /* data structures */ |
38ab74e7 SB |
53 | |
54 | /* blobs from the TPM; part of VM state when migrating */ | |
55 | typedef struct TPMBlobBuffers { | |
56 | uint32_t permanent_flags; | |
57 | TPMSizedBuffer permanent; | |
58 | ||
59 | uint32_t volatil_flags; | |
60 | TPMSizedBuffer volatil; | |
61 | ||
62 | uint32_t savestate_flags; | |
63 | TPMSizedBuffer savestate; | |
64 | } TPMBlobBuffers; | |
65 | ||
f4ede81e AV |
66 | typedef struct TPMEmulator { |
67 | TPMBackend parent; | |
68 | ||
69 | TPMEmulatorOptions *options; | |
70 | CharBackend ctrl_chr; | |
71 | QIOChannel *data_ioc; | |
72 | TPMVersion tpm_version; | |
73 | ptm_cap caps; /* capabilities of the TPM */ | |
74 | uint8_t cur_locty_number; /* last set locality */ | |
75 | Error *migration_blocker; | |
17b1af77 MAL |
76 | |
77 | QemuMutex mutex; | |
0b4c7c65 SB |
78 | |
79 | unsigned int established_flag:1; | |
80 | unsigned int established_flag_cached:1; | |
38ab74e7 SB |
81 | |
82 | TPMBlobBuffers state_blobs; | |
f4ede81e AV |
83 | } TPMEmulator; |
84 | ||
7e095e84 SB |
85 | struct tpm_error { |
86 | uint32_t tpm_result; | |
87 | const char *string; | |
88 | }; | |
89 | ||
90 | static const struct tpm_error tpm_errors[] = { | |
91 | /* TPM 1.2 error codes */ | |
92 | { TPM_BAD_PARAMETER , "a parameter is bad" }, | |
93 | { TPM_FAIL , "operation failed" }, | |
94 | { TPM_KEYNOTFOUND , "key could not be found" }, | |
95 | { TPM_BAD_PARAM_SIZE , "bad parameter size"}, | |
96 | { TPM_ENCRYPT_ERROR , "encryption error" }, | |
97 | { TPM_DECRYPT_ERROR , "decryption error" }, | |
98 | { TPM_BAD_KEY_PROPERTY, "bad key property" }, | |
99 | { TPM_BAD_MODE , "bad (encryption) mode" }, | |
100 | { TPM_BAD_VERSION , "bad version identifier" }, | |
101 | { TPM_BAD_LOCALITY , "bad locality" }, | |
102 | /* TPM 2 error codes */ | |
103 | { TPM_RC_FAILURE , "operation failed" }, | |
104 | { TPM_RC_LOCALITY , "bad locality" }, | |
105 | { TPM_RC_INSUFFICIENT, "insufficient amount of data" }, | |
106 | }; | |
107 | ||
108 | static const char *tpm_emulator_strerror(uint32_t tpm_result) | |
109 | { | |
110 | size_t i; | |
111 | ||
112 | for (i = 0; i < ARRAY_SIZE(tpm_errors); i++) { | |
113 | if (tpm_errors[i].tpm_result == tpm_result) { | |
114 | return tpm_errors[i].string; | |
115 | } | |
116 | } | |
117 | return ""; | |
118 | } | |
f4ede81e | 119 | |
17b1af77 | 120 | static int tpm_emulator_ctrlcmd(TPMEmulator *tpm, unsigned long cmd, void *msg, |
f4ede81e AV |
121 | size_t msg_len_in, size_t msg_len_out) |
122 | { | |
17b1af77 | 123 | CharBackend *dev = &tpm->ctrl_chr; |
f4ede81e AV |
124 | uint32_t cmd_no = cpu_to_be32(cmd); |
125 | ssize_t n = sizeof(uint32_t) + msg_len_in; | |
126 | uint8_t *buf = NULL; | |
17b1af77 MAL |
127 | int ret = -1; |
128 | ||
129 | qemu_mutex_lock(&tpm->mutex); | |
f4ede81e AV |
130 | |
131 | buf = g_alloca(n); | |
132 | memcpy(buf, &cmd_no, sizeof(cmd_no)); | |
133 | memcpy(buf + sizeof(cmd_no), msg, msg_len_in); | |
134 | ||
135 | n = qemu_chr_fe_write_all(dev, buf, n); | |
136 | if (n <= 0) { | |
17b1af77 | 137 | goto end; |
f4ede81e AV |
138 | } |
139 | ||
140 | if (msg_len_out != 0) { | |
141 | n = qemu_chr_fe_read_all(dev, msg, msg_len_out); | |
142 | if (n <= 0) { | |
17b1af77 | 143 | goto end; |
f4ede81e AV |
144 | } |
145 | } | |
146 | ||
17b1af77 MAL |
147 | ret = 0; |
148 | ||
149 | end: | |
150 | qemu_mutex_unlock(&tpm->mutex); | |
151 | return ret; | |
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 | ||
309 | static int tpm_emulator_set_buffer_size(TPMBackend *tb, | |
310 | size_t wanted_size, | |
311 | size_t *actual_size) | |
312 | { | |
313 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
314 | ptm_setbuffersize psbs; | |
315 | ||
316 | if (tpm_emulator_stop_tpm(tb) < 0) { | |
317 | return -1; | |
318 | } | |
319 | ||
320 | psbs.u.req.buffersize = cpu_to_be32(wanted_size); | |
321 | ||
322 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_BUFFERSIZE, &psbs, | |
323 | sizeof(psbs.u.req), sizeof(psbs.u.resp)) < 0) { | |
324 | error_report("tpm-emulator: Could not set buffer size: %s", | |
325 | strerror(errno)); | |
326 | return -1; | |
327 | } | |
328 | ||
329 | psbs.u.resp.tpm_result = be32_to_cpu(psbs.u.resp.tpm_result); | |
330 | if (psbs.u.resp.tpm_result != 0) { | |
7e095e84 SB |
331 | error_report("tpm-emulator: TPM result for set buffer size : 0x%x %s", |
332 | psbs.u.resp.tpm_result, | |
333 | tpm_emulator_strerror(psbs.u.resp.tpm_result)); | |
9375c44f SB |
334 | return -1; |
335 | } | |
336 | ||
337 | if (actual_size) { | |
338 | *actual_size = be32_to_cpu(psbs.u.resp.buffersize); | |
339 | } | |
340 | ||
9d9dcd96 | 341 | trace_tpm_emulator_set_buffer_size( |
9375c44f SB |
342 | be32_to_cpu(psbs.u.resp.buffersize), |
343 | be32_to_cpu(psbs.u.resp.minsize), | |
344 | be32_to_cpu(psbs.u.resp.maxsize)); | |
345 | ||
346 | return 0; | |
347 | } | |
348 | ||
38ab74e7 SB |
349 | static int tpm_emulator_startup_tpm_resume(TPMBackend *tb, size_t buffersize, |
350 | bool is_resume) | |
f4ede81e AV |
351 | { |
352 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
30270587 SB |
353 | ptm_init init = { |
354 | .u.req.init_flags = 0, | |
355 | }; | |
f4ede81e AV |
356 | ptm_res res; |
357 | ||
38ab74e7 SB |
358 | trace_tpm_emulator_startup_tpm_resume(is_resume, buffersize); |
359 | ||
9375c44f SB |
360 | if (buffersize != 0 && |
361 | tpm_emulator_set_buffer_size(tb, buffersize, NULL) < 0) { | |
362 | goto err_exit; | |
363 | } | |
364 | ||
38ab74e7 SB |
365 | if (is_resume) { |
366 | init.u.req.init_flags |= cpu_to_be32(PTM_INIT_FLAG_DELETE_VOLATILE); | |
367 | } | |
368 | ||
17b1af77 MAL |
369 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_INIT, &init, sizeof(init), |
370 | sizeof(init)) < 0) { | |
f4ede81e AV |
371 | error_report("tpm-emulator: could not send INIT: %s", |
372 | strerror(errno)); | |
373 | goto err_exit; | |
374 | } | |
375 | ||
376 | res = be32_to_cpu(init.u.resp.tpm_result); | |
377 | if (res) { | |
7e095e84 SB |
378 | error_report("tpm-emulator: TPM result for CMD_INIT: 0x%x %s", res, |
379 | tpm_emulator_strerror(res)); | |
f4ede81e AV |
380 | goto err_exit; |
381 | } | |
382 | return 0; | |
383 | ||
384 | err_exit: | |
385 | return -1; | |
386 | } | |
387 | ||
38ab74e7 SB |
388 | static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize) |
389 | { | |
390 | return tpm_emulator_startup_tpm_resume(tb, buffersize, false); | |
391 | } | |
392 | ||
f4ede81e AV |
393 | static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb) |
394 | { | |
395 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
396 | ptm_est est; | |
397 | ||
0b4c7c65 SB |
398 | if (tpm_emu->established_flag_cached) { |
399 | return tpm_emu->established_flag; | |
400 | } | |
401 | ||
17b1af77 | 402 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_TPMESTABLISHED, &est, |
f4ede81e AV |
403 | 0, sizeof(est)) < 0) { |
404 | error_report("tpm-emulator: Could not get the TPM established flag: %s", | |
405 | strerror(errno)); | |
406 | return false; | |
407 | } | |
9d9dcd96 | 408 | trace_tpm_emulator_get_tpm_established_flag(est.u.resp.bit); |
0b4c7c65 SB |
409 | |
410 | tpm_emu->established_flag_cached = 1; | |
411 | tpm_emu->established_flag = (est.u.resp.bit != 0); | |
f4ede81e | 412 | |
0b4c7c65 | 413 | return tpm_emu->established_flag; |
f4ede81e AV |
414 | } |
415 | ||
416 | static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb, | |
417 | uint8_t locty) | |
418 | { | |
419 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
420 | ptm_reset_est reset_est; | |
421 | ptm_res res; | |
422 | ||
423 | /* only a TPM 2.0 will support this */ | |
424 | if (tpm_emu->tpm_version != TPM_VERSION_2_0) { | |
425 | return 0; | |
426 | } | |
427 | ||
428 | reset_est.u.req.loc = tpm_emu->cur_locty_number; | |
17b1af77 | 429 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_RESET_TPMESTABLISHED, |
f4ede81e AV |
430 | &reset_est, sizeof(reset_est), |
431 | sizeof(reset_est)) < 0) { | |
432 | error_report("tpm-emulator: Could not reset the establishment bit: %s", | |
433 | strerror(errno)); | |
434 | return -1; | |
435 | } | |
436 | ||
437 | res = be32_to_cpu(reset_est.u.resp.tpm_result); | |
438 | if (res) { | |
7e095e84 SB |
439 | error_report( |
440 | "tpm-emulator: TPM result for rest established flag: 0x%x %s", | |
441 | res, tpm_emulator_strerror(res)); | |
f4ede81e AV |
442 | return -1; |
443 | } | |
444 | ||
0b4c7c65 SB |
445 | tpm_emu->established_flag_cached = 0; |
446 | ||
f4ede81e AV |
447 | return 0; |
448 | } | |
449 | ||
450 | static void tpm_emulator_cancel_cmd(TPMBackend *tb) | |
451 | { | |
452 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
453 | ptm_res res; | |
454 | ||
455 | if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, PTM_CAP_CANCEL_TPM_CMD)) { | |
9d9dcd96 | 456 | trace_tpm_emulator_cancel_cmd_not_supt(); |
f4ede81e AV |
457 | return; |
458 | } | |
459 | ||
3d011411 | 460 | /* FIXME: make the function non-blocking, or it may block a VCPU */ |
17b1af77 | 461 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_CANCEL_TPM_CMD, &res, 0, |
f4ede81e AV |
462 | sizeof(res)) < 0) { |
463 | error_report("tpm-emulator: Could not cancel command: %s", | |
464 | strerror(errno)); | |
465 | } else if (res != 0) { | |
466 | error_report("tpm-emulator: Failed to cancel TPM: 0x%x", | |
467 | be32_to_cpu(res)); | |
468 | } | |
469 | } | |
470 | ||
471 | static TPMVersion tpm_emulator_get_tpm_version(TPMBackend *tb) | |
472 | { | |
473 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
474 | ||
475 | return tpm_emu->tpm_version; | |
476 | } | |
477 | ||
b21e6aaf SB |
478 | static size_t tpm_emulator_get_buffer_size(TPMBackend *tb) |
479 | { | |
9375c44f SB |
480 | size_t actual_size; |
481 | ||
482 | if (tpm_emulator_set_buffer_size(tb, 0, &actual_size) < 0) { | |
483 | return 4096; | |
484 | } | |
485 | ||
486 | return actual_size; | |
b21e6aaf SB |
487 | } |
488 | ||
f4ede81e AV |
489 | static int tpm_emulator_block_migration(TPMEmulator *tpm_emu) |
490 | { | |
491 | Error *err = NULL; | |
38ab74e7 SB |
492 | ptm_cap caps = PTM_CAP_GET_STATEBLOB | PTM_CAP_SET_STATEBLOB | |
493 | PTM_CAP_STOP; | |
f4ede81e | 494 | |
38ab74e7 SB |
495 | if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, caps)) { |
496 | error_setg(&tpm_emu->migration_blocker, | |
497 | "Migration disabled: TPM emulator does not support " | |
498 | "migration"); | |
499 | migrate_add_blocker(tpm_emu->migration_blocker, &err); | |
500 | if (err) { | |
501 | error_report_err(err); | |
502 | error_free(tpm_emu->migration_blocker); | |
503 | tpm_emu->migration_blocker = NULL; | |
f4ede81e | 504 | |
38ab74e7 SB |
505 | return -1; |
506 | } | |
f4ede81e AV |
507 | } |
508 | ||
509 | return 0; | |
510 | } | |
511 | ||
512 | static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_emu) | |
513 | { | |
514 | ptm_res res; | |
515 | Error *err = NULL; | |
516 | int fds[2] = { -1, -1 }; | |
517 | ||
518 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { | |
519 | error_report("tpm-emulator: Failed to create socketpair"); | |
520 | return -1; | |
521 | } | |
522 | ||
523 | qemu_chr_fe_set_msgfds(&tpm_emu->ctrl_chr, fds + 1, 1); | |
524 | ||
17b1af77 MAL |
525 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_DATAFD, &res, 0, |
526 | sizeof(res)) < 0 || res != 0) { | |
f4ede81e AV |
527 | error_report("tpm-emulator: Failed to send CMD_SET_DATAFD: %s", |
528 | strerror(errno)); | |
529 | goto err_exit; | |
530 | } | |
531 | ||
532 | tpm_emu->data_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fds[0], &err)); | |
533 | if (err) { | |
534 | error_prepend(&err, "tpm-emulator: Failed to create io channel: "); | |
535 | error_report_err(err); | |
536 | goto err_exit; | |
537 | } | |
538 | ||
539 | closesocket(fds[1]); | |
540 | ||
541 | return 0; | |
542 | ||
543 | err_exit: | |
544 | closesocket(fds[0]); | |
545 | closesocket(fds[1]); | |
546 | return -1; | |
547 | } | |
548 | ||
549 | static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_emu, QemuOpts *opts) | |
550 | { | |
551 | const char *value; | |
552 | ||
553 | value = qemu_opt_get(opts, "chardev"); | |
554 | if (value) { | |
555 | Error *err = NULL; | |
556 | Chardev *dev = qemu_chr_find(value); | |
557 | ||
558 | if (!dev) { | |
559 | error_report("tpm-emulator: tpm chardev '%s' not found.", value); | |
560 | goto err; | |
561 | } | |
562 | ||
563 | if (!qemu_chr_fe_init(&tpm_emu->ctrl_chr, dev, &err)) { | |
564 | error_prepend(&err, "tpm-emulator: No valid chardev found at '%s':", | |
565 | value); | |
566 | error_report_err(err); | |
567 | goto err; | |
568 | } | |
569 | ||
570 | tpm_emu->options->chardev = g_strdup(value); | |
571 | } | |
572 | ||
573 | if (tpm_emulator_prepare_data_fd(tpm_emu) < 0) { | |
574 | goto err; | |
575 | } | |
576 | ||
577 | /* FIXME: tpm_util_test_tpmdev() accepts only on socket fd, as it also used | |
578 | * by passthrough driver, which not yet using GIOChannel. | |
579 | */ | |
580 | if (tpm_util_test_tpmdev(QIO_CHANNEL_SOCKET(tpm_emu->data_ioc)->fd, | |
581 | &tpm_emu->tpm_version)) { | |
582 | error_report("'%s' is not emulating TPM device. Error: %s", | |
583 | tpm_emu->options->chardev, strerror(errno)); | |
584 | goto err; | |
585 | } | |
586 | ||
9d9dcd96 SB |
587 | switch (tpm_emu->tpm_version) { |
588 | case TPM_VERSION_1_2: | |
589 | trace_tpm_emulator_handle_device_opts_tpm12(); | |
590 | break; | |
591 | case TPM_VERSION_2_0: | |
592 | trace_tpm_emulator_handle_device_opts_tpm2(); | |
593 | break; | |
594 | default: | |
595 | trace_tpm_emulator_handle_device_opts_unspec(); | |
596 | } | |
f4ede81e AV |
597 | |
598 | if (tpm_emulator_probe_caps(tpm_emu) || | |
599 | tpm_emulator_check_caps(tpm_emu)) { | |
600 | goto err; | |
601 | } | |
602 | ||
603 | return tpm_emulator_block_migration(tpm_emu); | |
604 | ||
605 | err: | |
9d9dcd96 SB |
606 | trace_tpm_emulator_handle_device_opts_startup_error(); |
607 | ||
f4ede81e AV |
608 | return -1; |
609 | } | |
610 | ||
9f7c0ef2 | 611 | static TPMBackend *tpm_emulator_create(QemuOpts *opts) |
f4ede81e AV |
612 | { |
613 | TPMBackend *tb = TPM_BACKEND(object_new(TYPE_TPM_EMULATOR)); | |
614 | ||
f4ede81e | 615 | if (tpm_emulator_handle_device_opts(TPM_EMULATOR(tb), opts)) { |
9f7c0ef2 MAL |
616 | object_unref(OBJECT(tb)); |
617 | return NULL; | |
f4ede81e AV |
618 | } |
619 | ||
620 | return tb; | |
f4ede81e AV |
621 | } |
622 | ||
623 | static TpmTypeOptions *tpm_emulator_get_tpm_options(TPMBackend *tb) | |
624 | { | |
625 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
626 | TpmTypeOptions *options = g_new0(TpmTypeOptions, 1); | |
627 | ||
628 | options->type = TPM_TYPE_OPTIONS_KIND_EMULATOR; | |
629 | options->u.emulator.data = QAPI_CLONE(TPMEmulatorOptions, tpm_emu->options); | |
630 | ||
631 | return options; | |
632 | } | |
633 | ||
634 | static const QemuOptDesc tpm_emulator_cmdline_opts[] = { | |
635 | TPM_STANDARD_CMDLINE_OPTS, | |
636 | { | |
637 | .name = "chardev", | |
638 | .type = QEMU_OPT_STRING, | |
639 | .help = "Character device to use for out-of-band control messages", | |
640 | }, | |
641 | { /* end of list */ }, | |
642 | }; | |
643 | ||
38ab74e7 SB |
644 | /* |
645 | * Transfer a TPM state blob from the TPM into a provided buffer. | |
646 | * | |
647 | * @tpm_emu: TPMEmulator | |
648 | * @type: the type of blob to transfer | |
649 | * @tsb: the TPMSizeBuffer to fill with the blob | |
650 | * @flags: the flags to return to the caller | |
651 | */ | |
652 | static int tpm_emulator_get_state_blob(TPMEmulator *tpm_emu, | |
653 | uint8_t type, | |
654 | TPMSizedBuffer *tsb, | |
655 | uint32_t *flags) | |
656 | { | |
657 | ptm_getstate pgs; | |
658 | ptm_res res; | |
659 | ssize_t n; | |
660 | uint32_t totlength, length; | |
661 | ||
662 | tpm_sized_buffer_reset(tsb); | |
663 | ||
664 | pgs.u.req.state_flags = cpu_to_be32(PTM_STATE_FLAG_DECRYPTED); | |
665 | pgs.u.req.type = cpu_to_be32(type); | |
666 | pgs.u.req.offset = 0; | |
667 | ||
668 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_STATEBLOB, | |
669 | &pgs, sizeof(pgs.u.req), | |
670 | offsetof(ptm_getstate, u.resp.data)) < 0) { | |
671 | error_report("tpm-emulator: could not get state blob type %d : %s", | |
672 | type, strerror(errno)); | |
673 | return -1; | |
674 | } | |
675 | ||
676 | res = be32_to_cpu(pgs.u.resp.tpm_result); | |
677 | if (res != 0 && (res & 0x800) == 0) { | |
678 | error_report("tpm-emulator: Getting the stateblob (type %d) failed " | |
7e095e84 SB |
679 | "with a TPM error 0x%x %s", type, res, |
680 | tpm_emulator_strerror(res)); | |
38ab74e7 SB |
681 | return -1; |
682 | } | |
683 | ||
684 | totlength = be32_to_cpu(pgs.u.resp.totlength); | |
685 | length = be32_to_cpu(pgs.u.resp.length); | |
686 | if (totlength != length) { | |
687 | error_report("tpm-emulator: Expecting to read %u bytes " | |
688 | "but would get %u", totlength, length); | |
689 | return -1; | |
690 | } | |
691 | ||
692 | *flags = be32_to_cpu(pgs.u.resp.state_flags); | |
693 | ||
694 | if (totlength > 0) { | |
695 | tsb->buffer = g_try_malloc(totlength); | |
696 | if (!tsb->buffer) { | |
697 | error_report("tpm-emulator: Out of memory allocating %u bytes", | |
698 | totlength); | |
699 | return -1; | |
700 | } | |
701 | ||
702 | n = qemu_chr_fe_read_all(&tpm_emu->ctrl_chr, tsb->buffer, totlength); | |
703 | if (n != totlength) { | |
704 | error_report("tpm-emulator: Could not read stateblob (type %d); " | |
705 | "expected %u bytes, got %zd", | |
706 | type, totlength, n); | |
707 | return -1; | |
708 | } | |
709 | } | |
710 | tsb->size = totlength; | |
711 | ||
712 | trace_tpm_emulator_get_state_blob(type, tsb->size, *flags); | |
713 | ||
714 | return 0; | |
715 | } | |
716 | ||
717 | static int tpm_emulator_get_state_blobs(TPMEmulator *tpm_emu) | |
718 | { | |
719 | TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs; | |
720 | ||
721 | if (tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_PERMANENT, | |
722 | &state_blobs->permanent, | |
723 | &state_blobs->permanent_flags) < 0 || | |
724 | tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_VOLATILE, | |
725 | &state_blobs->volatil, | |
726 | &state_blobs->volatil_flags) < 0 || | |
727 | tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_SAVESTATE, | |
728 | &state_blobs->savestate, | |
729 | &state_blobs->savestate_flags) < 0) { | |
730 | goto err_exit; | |
731 | } | |
732 | ||
733 | return 0; | |
734 | ||
735 | err_exit: | |
736 | tpm_sized_buffer_reset(&state_blobs->volatil); | |
737 | tpm_sized_buffer_reset(&state_blobs->permanent); | |
738 | tpm_sized_buffer_reset(&state_blobs->savestate); | |
739 | ||
740 | return -1; | |
741 | } | |
742 | ||
743 | /* | |
744 | * Transfer a TPM state blob to the TPM emulator. | |
745 | * | |
746 | * @tpm_emu: TPMEmulator | |
747 | * @type: the type of TPM state blob to transfer | |
748 | * @tsb: TPMSizedBuffer containing the TPM state blob | |
749 | * @flags: Flags describing the (encryption) state of the TPM state blob | |
750 | */ | |
751 | static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu, | |
752 | uint32_t type, | |
753 | TPMSizedBuffer *tsb, | |
754 | uint32_t flags) | |
755 | { | |
756 | ssize_t n; | |
757 | ptm_setstate pss; | |
758 | ptm_res tpm_result; | |
759 | ||
760 | if (tsb->size == 0) { | |
761 | return 0; | |
762 | } | |
763 | ||
764 | pss = (ptm_setstate) { | |
765 | .u.req.state_flags = cpu_to_be32(flags), | |
766 | .u.req.type = cpu_to_be32(type), | |
767 | .u.req.length = cpu_to_be32(tsb->size), | |
768 | }; | |
769 | ||
770 | /* write the header only */ | |
771 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_STATEBLOB, &pss, | |
772 | offsetof(ptm_setstate, u.req.data), 0) < 0) { | |
773 | error_report("tpm-emulator: could not set state blob type %d : %s", | |
774 | type, strerror(errno)); | |
775 | return -1; | |
776 | } | |
777 | ||
778 | /* now the body */ | |
779 | n = qemu_chr_fe_write_all(&tpm_emu->ctrl_chr, tsb->buffer, tsb->size); | |
780 | if (n != tsb->size) { | |
781 | error_report("tpm-emulator: Writing the stateblob (type %d) " | |
782 | "failed; could not write %u bytes, but only %zd", | |
783 | type, tsb->size, n); | |
784 | return -1; | |
785 | } | |
786 | ||
787 | /* now get the result */ | |
788 | n = qemu_chr_fe_read_all(&tpm_emu->ctrl_chr, | |
789 | (uint8_t *)&pss, sizeof(pss.u.resp)); | |
790 | if (n != sizeof(pss.u.resp)) { | |
791 | error_report("tpm-emulator: Reading response from writing stateblob " | |
792 | "(type %d) failed; expected %zu bytes, got %zd", type, | |
793 | sizeof(pss.u.resp), n); | |
794 | return -1; | |
795 | } | |
796 | ||
797 | tpm_result = be32_to_cpu(pss.u.resp.tpm_result); | |
798 | if (tpm_result != 0) { | |
799 | error_report("tpm-emulator: Setting the stateblob (type %d) failed " | |
7e095e84 SB |
800 | "with a TPM error 0x%x %s", type, tpm_result, |
801 | tpm_emulator_strerror(tpm_result)); | |
38ab74e7 SB |
802 | return -1; |
803 | } | |
804 | ||
805 | trace_tpm_emulator_set_state_blob(type, tsb->size, flags); | |
806 | ||
807 | return 0; | |
808 | } | |
809 | ||
810 | /* | |
811 | * Set all the TPM state blobs. | |
812 | * | |
813 | * Returns a negative errno code in case of error. | |
814 | */ | |
815 | static int tpm_emulator_set_state_blobs(TPMBackend *tb) | |
816 | { | |
817 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
818 | TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs; | |
819 | ||
820 | trace_tpm_emulator_set_state_blobs(); | |
821 | ||
822 | if (tpm_emulator_stop_tpm(tb) < 0) { | |
823 | trace_tpm_emulator_set_state_blobs_error("Could not stop TPM"); | |
824 | return -EIO; | |
825 | } | |
826 | ||
827 | if (tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_PERMANENT, | |
828 | &state_blobs->permanent, | |
829 | state_blobs->permanent_flags) < 0 || | |
830 | tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_VOLATILE, | |
831 | &state_blobs->volatil, | |
832 | state_blobs->volatil_flags) < 0 || | |
833 | tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_SAVESTATE, | |
834 | &state_blobs->savestate, | |
835 | state_blobs->savestate_flags) < 0) { | |
836 | return -EIO; | |
837 | } | |
838 | ||
839 | trace_tpm_emulator_set_state_blobs_done(); | |
840 | ||
841 | return 0; | |
842 | } | |
843 | ||
844 | static int tpm_emulator_pre_save(void *opaque) | |
845 | { | |
846 | TPMBackend *tb = opaque; | |
847 | TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | |
848 | ||
849 | trace_tpm_emulator_pre_save(); | |
850 | ||
851 | tpm_backend_finish_sync(tb); | |
852 | ||
853 | /* get the state blobs from the TPM */ | |
854 | return tpm_emulator_get_state_blobs(tpm_emu); | |
855 | } | |
856 | ||
857 | /* | |
858 | * Load the TPM state blobs into the TPM. | |
859 | * | |
860 | * Returns negative errno codes in case of error. | |
861 | */ | |
862 | static int tpm_emulator_post_load(void *opaque, int version_id) | |
863 | { | |
864 | TPMBackend *tb = opaque; | |
865 | int ret; | |
866 | ||
867 | ret = tpm_emulator_set_state_blobs(tb); | |
868 | if (ret < 0) { | |
869 | return ret; | |
870 | } | |
871 | ||
872 | if (tpm_emulator_startup_tpm_resume(tb, 0, true) < 0) { | |
873 | return -EIO; | |
874 | } | |
875 | ||
876 | return 0; | |
877 | } | |
878 | ||
879 | static const VMStateDescription vmstate_tpm_emulator = { | |
880 | .name = "tpm-emulator", | |
881 | .version_id = 0, | |
882 | .pre_save = tpm_emulator_pre_save, | |
883 | .post_load = tpm_emulator_post_load, | |
884 | .fields = (VMStateField[]) { | |
885 | VMSTATE_UINT32(state_blobs.permanent_flags, TPMEmulator), | |
886 | VMSTATE_UINT32(state_blobs.permanent.size, TPMEmulator), | |
887 | VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.permanent.buffer, | |
888 | TPMEmulator, 0, 0, | |
889 | state_blobs.permanent.size), | |
890 | ||
891 | VMSTATE_UINT32(state_blobs.volatil_flags, TPMEmulator), | |
892 | VMSTATE_UINT32(state_blobs.volatil.size, TPMEmulator), | |
893 | VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.volatil.buffer, | |
894 | TPMEmulator, 0, 0, | |
895 | state_blobs.volatil.size), | |
896 | ||
897 | VMSTATE_UINT32(state_blobs.savestate_flags, TPMEmulator), | |
898 | VMSTATE_UINT32(state_blobs.savestate.size, TPMEmulator), | |
899 | VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.savestate.buffer, | |
900 | TPMEmulator, 0, 0, | |
901 | state_blobs.savestate.size), | |
902 | ||
903 | VMSTATE_END_OF_LIST() | |
904 | } | |
905 | }; | |
906 | ||
f4ede81e AV |
907 | static void tpm_emulator_inst_init(Object *obj) |
908 | { | |
909 | TPMEmulator *tpm_emu = TPM_EMULATOR(obj); | |
910 | ||
9d9dcd96 SB |
911 | trace_tpm_emulator_inst_init(); |
912 | ||
f4ede81e AV |
913 | tpm_emu->options = g_new0(TPMEmulatorOptions, 1); |
914 | tpm_emu->cur_locty_number = ~0; | |
17b1af77 | 915 | qemu_mutex_init(&tpm_emu->mutex); |
38ab74e7 | 916 | |
1df2c9a2 PX |
917 | vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, |
918 | &vmstate_tpm_emulator, obj); | |
f4ede81e AV |
919 | } |
920 | ||
921 | /* | |
922 | * Gracefully shut down the external TPM | |
923 | */ | |
924 | static void tpm_emulator_shutdown(TPMEmulator *tpm_emu) | |
925 | { | |
926 | ptm_res res; | |
927 | ||
17b1af77 | 928 | if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SHUTDOWN, &res, 0, sizeof(res)) < 0) { |
f4ede81e AV |
929 | error_report("tpm-emulator: Could not cleanly shutdown the TPM: %s", |
930 | strerror(errno)); | |
931 | } else if (res != 0) { | |
7e095e84 SB |
932 | error_report("tpm-emulator: TPM result for shutdown: 0x%x %s", |
933 | be32_to_cpu(res), tpm_emulator_strerror(be32_to_cpu(res))); | |
f4ede81e AV |
934 | } |
935 | } | |
936 | ||
937 | static void tpm_emulator_inst_finalize(Object *obj) | |
938 | { | |
939 | TPMEmulator *tpm_emu = TPM_EMULATOR(obj); | |
38ab74e7 | 940 | TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs; |
f4ede81e AV |
941 | |
942 | tpm_emulator_shutdown(tpm_emu); | |
943 | ||
944 | object_unref(OBJECT(tpm_emu->data_ioc)); | |
945 | ||
946 | qemu_chr_fe_deinit(&tpm_emu->ctrl_chr, false); | |
947 | ||
948 | qapi_free_TPMEmulatorOptions(tpm_emu->options); | |
949 | ||
950 | if (tpm_emu->migration_blocker) { | |
951 | migrate_del_blocker(tpm_emu->migration_blocker); | |
952 | error_free(tpm_emu->migration_blocker); | |
953 | } | |
17b1af77 | 954 | |
38ab74e7 SB |
955 | tpm_sized_buffer_reset(&state_blobs->volatil); |
956 | tpm_sized_buffer_reset(&state_blobs->permanent); | |
957 | tpm_sized_buffer_reset(&state_blobs->savestate); | |
958 | ||
17b1af77 | 959 | qemu_mutex_destroy(&tpm_emu->mutex); |
38ab74e7 SB |
960 | |
961 | vmstate_unregister(NULL, &vmstate_tpm_emulator, obj); | |
f4ede81e AV |
962 | } |
963 | ||
964 | static void tpm_emulator_class_init(ObjectClass *klass, void *data) | |
965 | { | |
966 | TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass); | |
d31076ba MAL |
967 | |
968 | tbc->type = TPM_TYPE_EMULATOR; | |
969 | tbc->opts = tpm_emulator_cmdline_opts; | |
970 | tbc->desc = "TPM emulator backend driver"; | |
971 | tbc->create = tpm_emulator_create; | |
972 | tbc->startup_tpm = tpm_emulator_startup_tpm; | |
973 | tbc->cancel_cmd = tpm_emulator_cancel_cmd; | |
974 | tbc->get_tpm_established_flag = tpm_emulator_get_tpm_established_flag; | |
975 | tbc->reset_tpm_established_flag = tpm_emulator_reset_tpm_established_flag; | |
976 | tbc->get_tpm_version = tpm_emulator_get_tpm_version; | |
b21e6aaf | 977 | tbc->get_buffer_size = tpm_emulator_get_buffer_size; |
d31076ba MAL |
978 | tbc->get_tpm_options = tpm_emulator_get_tpm_options; |
979 | ||
f4ede81e AV |
980 | tbc->handle_request = tpm_emulator_handle_request; |
981 | } | |
982 | ||
983 | static const TypeInfo tpm_emulator_info = { | |
984 | .name = TYPE_TPM_EMULATOR, | |
985 | .parent = TYPE_TPM_BACKEND, | |
986 | .instance_size = sizeof(TPMEmulator), | |
987 | .class_init = tpm_emulator_class_init, | |
988 | .instance_init = tpm_emulator_inst_init, | |
989 | .instance_finalize = tpm_emulator_inst_finalize, | |
990 | }; | |
991 | ||
992 | static void tpm_emulator_register(void) | |
993 | { | |
994 | type_register_static(&tpm_emulator_info); | |
f4ede81e AV |
995 | } |
996 | ||
997 | type_init(tpm_emulator_register) |