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