diff options
Diffstat (limited to 'meta/recipes-devtools/qemu/qemu/0010-tpm-Added-support-for-TPM-emulator.patch')
-rw-r--r-- | meta/recipes-devtools/qemu/qemu/0010-tpm-Added-support-for-TPM-emulator.patch | 1059 |
1 files changed, 0 insertions, 1059 deletions
diff --git a/meta/recipes-devtools/qemu/qemu/0010-tpm-Added-support-for-TPM-emulator.patch b/meta/recipes-devtools/qemu/qemu/0010-tpm-Added-support-for-TPM-emulator.patch deleted file mode 100644 index 968e12e88a..0000000000 --- a/meta/recipes-devtools/qemu/qemu/0010-tpm-Added-support-for-TPM-emulator.patch +++ /dev/null | |||
@@ -1,1059 +0,0 @@ | |||
1 | From 70e73b7c6c7cf982d645db9c81c74588e6b10a2b Mon Sep 17 00:00:00 2001 | ||
2 | From: Amarnath Valluri <amarnath.valluri@intel.com> | ||
3 | Date: Wed, 29 Mar 2017 15:39:41 +0300 | ||
4 | Subject: [PATCH 10/12] tpm: Added support for TPM emulator | ||
5 | |||
6 | This change introduces a new TPM backend driver that can communicate with | ||
7 | swtpm(software TPM emulator) using unix domain socket interface. QEMU talks to | ||
8 | TPM emulator using QEMU's socket-based chardev backend device. | ||
9 | |||
10 | Swtpm uses two Unix sockets for communications, one for plain TPM commands and | ||
11 | responses, and one for out-of-band control messages. QEMU passes data socket to | ||
12 | be used over the control channel. | ||
13 | |||
14 | The swtpm and associated tools can be found here: | ||
15 | https://github.com/stefanberger/swtpm | ||
16 | |||
17 | The swtpm's control channel protocol specification can be found here: | ||
18 | https://github.com/stefanberger/swtpm/wiki/Control-Channel-Specification | ||
19 | |||
20 | Usage: | ||
21 | # setup TPM state directory | ||
22 | mkdir /tmp/mytpm | ||
23 | chown -R tss:root /tmp/mytpm | ||
24 | /usr/bin/swtpm_setup --tpm-state /tmp/mytpm --createek | ||
25 | |||
26 | # Ask qemu to use TPM emulator with given tpm state directory | ||
27 | qemu-system-x86_64 \ | ||
28 | [...] \ | ||
29 | -chardev socket,id=chrtpm,path=/tmp/swtpm-sock \ | ||
30 | -tpmdev emulator,id=tpm0,chardev=chrtpm \ | ||
31 | -device tpm-tis,tpmdev=tpm0 \ | ||
32 | [...] | ||
33 | |||
34 | Signed-off-by: Amarnath Valluri <amarnath.valluri@intel.com> | ||
35 | |||
36 | Upstream-Status: Backport [f4ede81eed29e6140374177d1f2808248c5b5650] | ||
37 | --- | ||
38 | configure | 13 +- | ||
39 | hmp.c | 5 + | ||
40 | hw/tpm/Makefile.objs | 1 + | ||
41 | hw/tpm/tpm_emulator.c | 583 ++++++++++++++++++++++++++++++++++++++++++++++++++ | ||
42 | hw/tpm/tpm_ioctl.h | 246 +++++++++++++++++++++ | ||
43 | qapi-schema.json | 18 +- | ||
44 | qemu-options.hx | 22 +- | ||
45 | 7 files changed, 882 insertions(+), 6 deletions(-) | ||
46 | create mode 100644 hw/tpm/tpm_emulator.c | ||
47 | create mode 100644 hw/tpm/tpm_ioctl.h | ||
48 | |||
49 | diff --git a/configure b/configure | ||
50 | index dd73cce62f..9a25537096 100755 | ||
51 | --- a/configure | ||
52 | +++ b/configure | ||
53 | @@ -3503,6 +3503,12 @@ else | ||
54 | tpm_passthrough=no | ||
55 | fi | ||
56 | |||
57 | +# TPM emulator is for all posix systems | ||
58 | +if test "$mingw32" != "yes"; then | ||
59 | + tpm_emulator=$tpm | ||
60 | +else | ||
61 | + tpm_emulator=no | ||
62 | +fi | ||
63 | ########################################## | ||
64 | # attr probe | ||
65 | |||
66 | @@ -5396,6 +5402,7 @@ echo "gcov enabled $gcov" | ||
67 | echo "TPM support $tpm" | ||
68 | echo "libssh2 support $libssh2" | ||
69 | echo "TPM passthrough $tpm_passthrough" | ||
70 | +echo "TPM emulator $tpm_emulator" | ||
71 | echo "QOM debugging $qom_cast_debug" | ||
72 | echo "Live block migration $live_block_migration" | ||
73 | echo "lzo support $lzo" | ||
74 | @@ -5983,12 +5990,16 @@ else | ||
75 | echo "HOST_USB=stub" >> $config_host_mak | ||
76 | fi | ||
77 | |||
78 | -# TPM passthrough support? | ||
79 | if test "$tpm" = "yes"; then | ||
80 | echo 'CONFIG_TPM=$(CONFIG_SOFTMMU)' >> $config_host_mak | ||
81 | + # TPM passthrough support? | ||
82 | if test "$tpm_passthrough" = "yes"; then | ||
83 | echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_host_mak | ||
84 | fi | ||
85 | + # TPM emulator support? | ||
86 | + if test "$tpm_emulator" = "yes"; then | ||
87 | + echo "CONFIG_TPM_EMULATOR=y" >> $config_host_mak | ||
88 | + fi | ||
89 | fi | ||
90 | |||
91 | echo "TRACE_BACKENDS=$trace_backends" >> $config_host_mak | ||
92 | diff --git a/hmp.c b/hmp.c | ||
93 | index fd80dce758..820aa8f002 100644 | ||
94 | --- a/hmp.c | ||
95 | +++ b/hmp.c | ||
96 | @@ -995,6 +995,7 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict) | ||
97 | Error *err = NULL; | ||
98 | unsigned int c = 0; | ||
99 | TPMPassthroughOptions *tpo; | ||
100 | + TPMEmulatorOptions *teo; | ||
101 | |||
102 | info_list = qmp_query_tpm(&err); | ||
103 | if (err) { | ||
104 | @@ -1024,6 +1025,10 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict) | ||
105 | tpo->has_cancel_path ? ",cancel-path=" : "", | ||
106 | tpo->has_cancel_path ? tpo->cancel_path : ""); | ||
107 | break; | ||
108 | + case TPM_TYPE_OPTIONS_KIND_EMULATOR: | ||
109 | + teo = ti->options->u.emulator.data; | ||
110 | + monitor_printf(mon, ",chardev=%s", teo->chardev); | ||
111 | + break; | ||
112 | case TPM_TYPE_OPTIONS_KIND__MAX: | ||
113 | break; | ||
114 | } | ||
115 | diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs | ||
116 | index 64cecc3b67..41f0b7a590 100644 | ||
117 | --- a/hw/tpm/Makefile.objs | ||
118 | +++ b/hw/tpm/Makefile.objs | ||
119 | @@ -1,2 +1,3 @@ | ||
120 | common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o | ||
121 | common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o tpm_util.o | ||
122 | +common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o tpm_util.o | ||
123 | diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c | ||
124 | new file mode 100644 | ||
125 | index 0000000000..433bc4fa8a | ||
126 | --- /dev/null | ||
127 | +++ b/hw/tpm/tpm_emulator.c | ||
128 | @@ -0,0 +1,583 @@ | ||
129 | +/* | ||
130 | + * Emulator TPM driver | ||
131 | + * | ||
132 | + * Copyright (c) 2017 Intel Corporation | ||
133 | + * Author: Amarnath Valluri <amarnath.valluri@intel.com> | ||
134 | + * | ||
135 | + * Copyright (c) 2010 - 2013 IBM Corporation | ||
136 | + * Authors: | ||
137 | + * Stefan Berger <stefanb@us.ibm.com> | ||
138 | + * | ||
139 | + * Copyright (C) 2011 IAIK, Graz University of Technology | ||
140 | + * Author: Andreas Niederl | ||
141 | + * | ||
142 | + * This library is free software; you can redistribute it and/or | ||
143 | + * modify it under the terms of the GNU Lesser General Public | ||
144 | + * License as published by the Free Software Foundation; either | ||
145 | + * version 2 of the License, or (at your option) any later version. | ||
146 | + * | ||
147 | + * This library is distributed in the hope that it will be useful, | ||
148 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
149 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
150 | + * Lesser General Public License for more details. | ||
151 | + * | ||
152 | + * You should have received a copy of the GNU Lesser General Public | ||
153 | + * License along with this library; if not, see <http://www.gnu.org/licenses/> | ||
154 | + * | ||
155 | + */ | ||
156 | + | ||
157 | +#include "qemu/osdep.h" | ||
158 | +#include "qemu/error-report.h" | ||
159 | +#include "qemu/sockets.h" | ||
160 | +#include "io/channel-socket.h" | ||
161 | +#include "sysemu/tpm_backend.h" | ||
162 | +#include "tpm_int.h" | ||
163 | +#include "hw/hw.h" | ||
164 | +#include "hw/i386/pc.h" | ||
165 | +#include "tpm_util.h" | ||
166 | +#include "tpm_ioctl.h" | ||
167 | +#include "migration/blocker.h" | ||
168 | +#include "qapi/error.h" | ||
169 | +#include "qapi/clone-visitor.h" | ||
170 | +#include "chardev/char-fe.h" | ||
171 | + | ||
172 | +#include <fcntl.h> | ||
173 | +#include <sys/types.h> | ||
174 | +#include <sys/stat.h> | ||
175 | +#include <stdio.h> | ||
176 | + | ||
177 | +#define DEBUG_TPM 0 | ||
178 | + | ||
179 | +#define DPRINTF(fmt, ...) do { \ | ||
180 | + if (DEBUG_TPM) { \ | ||
181 | + fprintf(stderr, "tpm-emulator:"fmt"\n", ## __VA_ARGS__); \ | ||
182 | + } \ | ||
183 | +} while (0) | ||
184 | + | ||
185 | +#define TYPE_TPM_EMULATOR "tpm-emulator" | ||
186 | +#define TPM_EMULATOR(obj) \ | ||
187 | + OBJECT_CHECK(TPMEmulator, (obj), TYPE_TPM_EMULATOR) | ||
188 | + | ||
189 | +#define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps & (cap)) == (cap)) | ||
190 | + | ||
191 | +static const TPMDriverOps tpm_emulator_driver; | ||
192 | + | ||
193 | +/* data structures */ | ||
194 | +typedef struct TPMEmulator { | ||
195 | + TPMBackend parent; | ||
196 | + | ||
197 | + TPMEmulatorOptions *options; | ||
198 | + CharBackend ctrl_chr; | ||
199 | + QIOChannel *data_ioc; | ||
200 | + TPMVersion tpm_version; | ||
201 | + ptm_cap caps; /* capabilities of the TPM */ | ||
202 | + uint8_t cur_locty_number; /* last set locality */ | ||
203 | + Error *migration_blocker; | ||
204 | +} TPMEmulator; | ||
205 | + | ||
206 | + | ||
207 | +static int tpm_emulator_ctrlcmd(CharBackend *dev, unsigned long cmd, void *msg, | ||
208 | + size_t msg_len_in, size_t msg_len_out) | ||
209 | +{ | ||
210 | + uint32_t cmd_no = cpu_to_be32(cmd); | ||
211 | + ssize_t n = sizeof(uint32_t) + msg_len_in; | ||
212 | + uint8_t *buf = NULL; | ||
213 | + | ||
214 | + buf = g_alloca(n); | ||
215 | + memcpy(buf, &cmd_no, sizeof(cmd_no)); | ||
216 | + memcpy(buf + sizeof(cmd_no), msg, msg_len_in); | ||
217 | + | ||
218 | + n = qemu_chr_fe_write_all(dev, buf, n); | ||
219 | + if (n <= 0) { | ||
220 | + return -1; | ||
221 | + } | ||
222 | + | ||
223 | + if (msg_len_out != 0) { | ||
224 | + n = qemu_chr_fe_read_all(dev, msg, msg_len_out); | ||
225 | + if (n <= 0) { | ||
226 | + return -1; | ||
227 | + } | ||
228 | + } | ||
229 | + | ||
230 | + return 0; | ||
231 | +} | ||
232 | + | ||
233 | +static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu, | ||
234 | + const uint8_t *in, uint32_t in_len, | ||
235 | + uint8_t *out, uint32_t out_len, | ||
236 | + bool *selftest_done, | ||
237 | + Error **err) | ||
238 | +{ | ||
239 | + ssize_t ret; | ||
240 | + bool is_selftest = false; | ||
241 | + const struct tpm_resp_hdr *hdr = NULL; | ||
242 | + | ||
243 | + if (selftest_done) { | ||
244 | + *selftest_done = false; | ||
245 | + is_selftest = tpm_util_is_selftest(in, in_len); | ||
246 | + } | ||
247 | + | ||
248 | + ret = qio_channel_write(tpm_emu->data_ioc, (char *)in, in_len, err); | ||
249 | + if (ret != in_len) { | ||
250 | + return -1; | ||
251 | + } | ||
252 | + | ||
253 | + ret = qio_channel_read(tpm_emu->data_ioc, (char *)out, out_len, err); | ||
254 | + if (ret <= 0 || ret < sizeof(*hdr)) { | ||
255 | + return -1; | ||
256 | + } | ||
257 | + | ||
258 | + hdr = (struct tpm_resp_hdr *)out; | ||
259 | + if (be32_to_cpu(hdr->len) != ret) { | ||
260 | + return -1; | ||
261 | + } | ||
262 | + | ||
263 | + if (is_selftest) { | ||
264 | + *selftest_done = (be32_to_cpu(hdr->errcode) == 0); | ||
265 | + } | ||
266 | + | ||
267 | + return 0; | ||
268 | +} | ||
269 | + | ||
270 | +static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number) | ||
271 | +{ | ||
272 | + ptm_loc loc; | ||
273 | + | ||
274 | + DPRINTF("%s : locality: 0x%x", __func__, locty_number); | ||
275 | + | ||
276 | + if (tpm_emu->cur_locty_number == locty_number) { | ||
277 | + return 0; | ||
278 | + } | ||
279 | + | ||
280 | + DPRINTF("setting locality : 0x%x", locty_number); | ||
281 | + loc.u.req.loc = locty_number; | ||
282 | + if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_SET_LOCALITY, &loc, | ||
283 | + sizeof(loc), sizeof(loc)) < 0) { | ||
284 | + error_report("tpm-emulator: could not set locality : %s", | ||
285 | + strerror(errno)); | ||
286 | + return -1; | ||
287 | + } | ||
288 | + | ||
289 | + loc.u.resp.tpm_result = be32_to_cpu(loc.u.resp.tpm_result); | ||
290 | + if (loc.u.resp.tpm_result != 0) { | ||
291 | + error_report("tpm-emulator: TPM result for set locality : 0x%x", | ||
292 | + loc.u.resp.tpm_result); | ||
293 | + return -1; | ||
294 | + } | ||
295 | + | ||
296 | + tpm_emu->cur_locty_number = locty_number; | ||
297 | + | ||
298 | + return 0; | ||
299 | +} | ||
300 | + | ||
301 | +static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd cmd) | ||
302 | +{ | ||
303 | + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | ||
304 | + TPMLocality *locty = NULL; | ||
305 | + bool selftest_done = false; | ||
306 | + Error *err = NULL; | ||
307 | + | ||
308 | + DPRINTF("processing command type %d", cmd); | ||
309 | + | ||
310 | + switch (cmd) { | ||
311 | + case TPM_BACKEND_CMD_PROCESS_CMD: | ||
312 | + locty = tb->tpm_state->locty_data; | ||
313 | + if (tpm_emulator_set_locality(tpm_emu, | ||
314 | + tb->tpm_state->locty_number) < 0 || | ||
315 | + tpm_emulator_unix_tx_bufs(tpm_emu, locty->w_buffer.buffer, | ||
316 | + locty->w_offset, locty->r_buffer.buffer, | ||
317 | + locty->r_buffer.size, &selftest_done, | ||
318 | + &err) < 0) { | ||
319 | + tpm_util_write_fatal_error_response(locty->r_buffer.buffer, | ||
320 | + locty->r_buffer.size); | ||
321 | + error_report_err(err); | ||
322 | + } | ||
323 | + | ||
324 | + tb->recv_data_callback(tb->tpm_state, tb->tpm_state->locty_number, | ||
325 | + selftest_done); | ||
326 | + | ||
327 | + break; | ||
328 | + case TPM_BACKEND_CMD_INIT: | ||
329 | + case TPM_BACKEND_CMD_END: | ||
330 | + case TPM_BACKEND_CMD_TPM_RESET: | ||
331 | + /* nothing to do */ | ||
332 | + break; | ||
333 | + } | ||
334 | +} | ||
335 | + | ||
336 | +static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu) | ||
337 | +{ | ||
338 | + DPRINTF("%s", __func__); | ||
339 | + if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_GET_CAPABILITY, | ||
340 | + &tpm_emu->caps, 0, sizeof(tpm_emu->caps)) < 0) { | ||
341 | + error_report("tpm-emulator: probing failed : %s", strerror(errno)); | ||
342 | + return -1; | ||
343 | + } | ||
344 | + | ||
345 | + tpm_emu->caps = be64_to_cpu(tpm_emu->caps); | ||
346 | + | ||
347 | + DPRINTF("capbilities : 0x%lx", tpm_emu->caps); | ||
348 | + | ||
349 | + return 0; | ||
350 | +} | ||
351 | + | ||
352 | +static int tpm_emulator_check_caps(TPMEmulator *tpm_emu) | ||
353 | +{ | ||
354 | + ptm_cap caps = 0; | ||
355 | + const char *tpm = NULL; | ||
356 | + | ||
357 | + /* check for min. required capabilities */ | ||
358 | + switch (tpm_emu->tpm_version) { | ||
359 | + case TPM_VERSION_1_2: | ||
360 | + caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED | | ||
361 | + PTM_CAP_SET_LOCALITY | PTM_CAP_SET_DATAFD; | ||
362 | + tpm = "1.2"; | ||
363 | + break; | ||
364 | + case TPM_VERSION_2_0: | ||
365 | + caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED | | ||
366 | + PTM_CAP_SET_LOCALITY | PTM_CAP_RESET_TPMESTABLISHED | | ||
367 | + PTM_CAP_SET_DATAFD; | ||
368 | + tpm = "2"; | ||
369 | + break; | ||
370 | + case TPM_VERSION_UNSPEC: | ||
371 | + error_report("tpm-emulator: TPM version has not been set"); | ||
372 | + return -1; | ||
373 | + } | ||
374 | + | ||
375 | + if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, caps)) { | ||
376 | + error_report("tpm-emulator: TPM does not implement minimum set of " | ||
377 | + "required capabilities for TPM %s (0x%x)", tpm, (int)caps); | ||
378 | + return -1; | ||
379 | + } | ||
380 | + | ||
381 | + return 0; | ||
382 | +} | ||
383 | + | ||
384 | +static int tpm_emulator_startup_tpm(TPMBackend *tb) | ||
385 | +{ | ||
386 | + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | ||
387 | + ptm_init init; | ||
388 | + ptm_res res; | ||
389 | + | ||
390 | + DPRINTF("%s", __func__); | ||
391 | + if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_INIT, &init, sizeof(init), | ||
392 | + sizeof(init)) < 0) { | ||
393 | + error_report("tpm-emulator: could not send INIT: %s", | ||
394 | + strerror(errno)); | ||
395 | + goto err_exit; | ||
396 | + } | ||
397 | + | ||
398 | + res = be32_to_cpu(init.u.resp.tpm_result); | ||
399 | + if (res) { | ||
400 | + error_report("tpm-emulator: TPM result for CMD_INIT: 0x%x", res); | ||
401 | + goto err_exit; | ||
402 | + } | ||
403 | + return 0; | ||
404 | + | ||
405 | +err_exit: | ||
406 | + return -1; | ||
407 | +} | ||
408 | + | ||
409 | +static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb) | ||
410 | +{ | ||
411 | + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | ||
412 | + ptm_est est; | ||
413 | + | ||
414 | + DPRINTF("%s", __func__); | ||
415 | + if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_GET_TPMESTABLISHED, &est, | ||
416 | + 0, sizeof(est)) < 0) { | ||
417 | + error_report("tpm-emulator: Could not get the TPM established flag: %s", | ||
418 | + strerror(errno)); | ||
419 | + return false; | ||
420 | + } | ||
421 | + DPRINTF("established flag: %0x", est.u.resp.bit); | ||
422 | + | ||
423 | + return (est.u.resp.bit != 0); | ||
424 | +} | ||
425 | + | ||
426 | +static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb, | ||
427 | + uint8_t locty) | ||
428 | +{ | ||
429 | + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | ||
430 | + ptm_reset_est reset_est; | ||
431 | + ptm_res res; | ||
432 | + | ||
433 | + /* only a TPM 2.0 will support this */ | ||
434 | + if (tpm_emu->tpm_version != TPM_VERSION_2_0) { | ||
435 | + return 0; | ||
436 | + } | ||
437 | + | ||
438 | + reset_est.u.req.loc = tpm_emu->cur_locty_number; | ||
439 | + if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_RESET_TPMESTABLISHED, | ||
440 | + &reset_est, sizeof(reset_est), | ||
441 | + sizeof(reset_est)) < 0) { | ||
442 | + error_report("tpm-emulator: Could not reset the establishment bit: %s", | ||
443 | + strerror(errno)); | ||
444 | + return -1; | ||
445 | + } | ||
446 | + | ||
447 | + res = be32_to_cpu(reset_est.u.resp.tpm_result); | ||
448 | + if (res) { | ||
449 | + error_report("tpm-emulator: TPM result for rest establixhed flag: 0x%x", | ||
450 | + res); | ||
451 | + return -1; | ||
452 | + } | ||
453 | + | ||
454 | + return 0; | ||
455 | +} | ||
456 | + | ||
457 | +static void tpm_emulator_cancel_cmd(TPMBackend *tb) | ||
458 | +{ | ||
459 | + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | ||
460 | + ptm_res res; | ||
461 | + | ||
462 | + if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, PTM_CAP_CANCEL_TPM_CMD)) { | ||
463 | + DPRINTF("Backend does not support CANCEL_TPM_CMD"); | ||
464 | + return; | ||
465 | + } | ||
466 | + | ||
467 | + if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_CANCEL_TPM_CMD, &res, 0, | ||
468 | + sizeof(res)) < 0) { | ||
469 | + error_report("tpm-emulator: Could not cancel command: %s", | ||
470 | + strerror(errno)); | ||
471 | + } else if (res != 0) { | ||
472 | + error_report("tpm-emulator: Failed to cancel TPM: 0x%x", | ||
473 | + be32_to_cpu(res)); | ||
474 | + } | ||
475 | +} | ||
476 | + | ||
477 | +static TPMVersion tpm_emulator_get_tpm_version(TPMBackend *tb) | ||
478 | +{ | ||
479 | + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | ||
480 | + | ||
481 | + return tpm_emu->tpm_version; | ||
482 | +} | ||
483 | + | ||
484 | +static int tpm_emulator_block_migration(TPMEmulator *tpm_emu) | ||
485 | +{ | ||
486 | + Error *err = NULL; | ||
487 | + | ||
488 | + error_setg(&tpm_emu->migration_blocker, | ||
489 | + "Migration disabled: TPM emulator not yet migratable"); | ||
490 | + migrate_add_blocker(tpm_emu->migration_blocker, &err); | ||
491 | + if (err) { | ||
492 | + error_report_err(err); | ||
493 | + error_free(tpm_emu->migration_blocker); | ||
494 | + tpm_emu->migration_blocker = NULL; | ||
495 | + | ||
496 | + return -1; | ||
497 | + } | ||
498 | + | ||
499 | + return 0; | ||
500 | +} | ||
501 | + | ||
502 | +static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_emu) | ||
503 | +{ | ||
504 | + ptm_res res; | ||
505 | + Error *err = NULL; | ||
506 | + int fds[2] = { -1, -1 }; | ||
507 | + | ||
508 | + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { | ||
509 | + error_report("tpm-emulator: Failed to create socketpair"); | ||
510 | + return -1; | ||
511 | + } | ||
512 | + | ||
513 | + qemu_chr_fe_set_msgfds(&tpm_emu->ctrl_chr, fds + 1, 1); | ||
514 | + | ||
515 | + if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_SET_DATAFD, &res, 0, | ||
516 | + sizeof(res)) || res != 0) { | ||
517 | + error_report("tpm-emulator: Failed to send CMD_SET_DATAFD: %s", | ||
518 | + strerror(errno)); | ||
519 | + goto err_exit; | ||
520 | + } | ||
521 | + | ||
522 | + tpm_emu->data_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fds[0], &err)); | ||
523 | + if (err) { | ||
524 | + error_prepend(&err, "tpm-emulator: Failed to create io channel: "); | ||
525 | + error_report_err(err); | ||
526 | + goto err_exit; | ||
527 | + } | ||
528 | + | ||
529 | + closesocket(fds[1]); | ||
530 | + | ||
531 | + return 0; | ||
532 | + | ||
533 | +err_exit: | ||
534 | + closesocket(fds[0]); | ||
535 | + closesocket(fds[1]); | ||
536 | + return -1; | ||
537 | +} | ||
538 | + | ||
539 | +static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_emu, QemuOpts *opts) | ||
540 | +{ | ||
541 | + const char *value; | ||
542 | + | ||
543 | + value = qemu_opt_get(opts, "chardev"); | ||
544 | + if (value) { | ||
545 | + Error *err = NULL; | ||
546 | + Chardev *dev = qemu_chr_find(value); | ||
547 | + | ||
548 | + if (!dev) { | ||
549 | + error_report("tpm-emulator: tpm chardev '%s' not found.", value); | ||
550 | + goto err; | ||
551 | + } | ||
552 | + | ||
553 | + if (!qemu_chr_fe_init(&tpm_emu->ctrl_chr, dev, &err)) { | ||
554 | + error_prepend(&err, "tpm-emulator: No valid chardev found at '%s':", | ||
555 | + value); | ||
556 | + error_report_err(err); | ||
557 | + goto err; | ||
558 | + } | ||
559 | + | ||
560 | + tpm_emu->options->chardev = g_strdup(value); | ||
561 | + } | ||
562 | + | ||
563 | + if (tpm_emulator_prepare_data_fd(tpm_emu) < 0) { | ||
564 | + goto err; | ||
565 | + } | ||
566 | + | ||
567 | + /* FIXME: tpm_util_test_tpmdev() accepts only on socket fd, as it also used | ||
568 | + * by passthrough driver, which not yet using GIOChannel. | ||
569 | + */ | ||
570 | + if (tpm_util_test_tpmdev(QIO_CHANNEL_SOCKET(tpm_emu->data_ioc)->fd, | ||
571 | + &tpm_emu->tpm_version)) { | ||
572 | + error_report("'%s' is not emulating TPM device. Error: %s", | ||
573 | + tpm_emu->options->chardev, strerror(errno)); | ||
574 | + goto err; | ||
575 | + } | ||
576 | + | ||
577 | + DPRINTF("TPM Version %s", tpm_emu->tpm_version == TPM_VERSION_1_2 ? "1.2" : | ||
578 | + (tpm_emu->tpm_version == TPM_VERSION_2_0 ? "2.0" : "Unspecified")); | ||
579 | + | ||
580 | + if (tpm_emulator_probe_caps(tpm_emu) || | ||
581 | + tpm_emulator_check_caps(tpm_emu)) { | ||
582 | + goto err; | ||
583 | + } | ||
584 | + | ||
585 | + return tpm_emulator_block_migration(tpm_emu); | ||
586 | + | ||
587 | +err: | ||
588 | + DPRINTF("Startup error"); | ||
589 | + return -1; | ||
590 | +} | ||
591 | + | ||
592 | +static TPMBackend *tpm_emulator_create(QemuOpts *opts, const char *id) | ||
593 | +{ | ||
594 | + TPMBackend *tb = TPM_BACKEND(object_new(TYPE_TPM_EMULATOR)); | ||
595 | + | ||
596 | + tb->id = g_strdup(id); | ||
597 | + | ||
598 | + if (tpm_emulator_handle_device_opts(TPM_EMULATOR(tb), opts)) { | ||
599 | + goto err_exit; | ||
600 | + } | ||
601 | + | ||
602 | + return tb; | ||
603 | + | ||
604 | +err_exit: | ||
605 | + object_unref(OBJECT(tb)); | ||
606 | + | ||
607 | + return NULL; | ||
608 | +} | ||
609 | + | ||
610 | +static TpmTypeOptions *tpm_emulator_get_tpm_options(TPMBackend *tb) | ||
611 | +{ | ||
612 | + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); | ||
613 | + TpmTypeOptions *options = g_new0(TpmTypeOptions, 1); | ||
614 | + | ||
615 | + options->type = TPM_TYPE_OPTIONS_KIND_EMULATOR; | ||
616 | + options->u.emulator.data = QAPI_CLONE(TPMEmulatorOptions, tpm_emu->options); | ||
617 | + | ||
618 | + return options; | ||
619 | +} | ||
620 | + | ||
621 | +static const QemuOptDesc tpm_emulator_cmdline_opts[] = { | ||
622 | + TPM_STANDARD_CMDLINE_OPTS, | ||
623 | + { | ||
624 | + .name = "chardev", | ||
625 | + .type = QEMU_OPT_STRING, | ||
626 | + .help = "Character device to use for out-of-band control messages", | ||
627 | + }, | ||
628 | + { /* end of list */ }, | ||
629 | +}; | ||
630 | + | ||
631 | +static const TPMDriverOps tpm_emulator_driver = { | ||
632 | + .type = TPM_TYPE_EMULATOR, | ||
633 | + .opts = tpm_emulator_cmdline_opts, | ||
634 | + .desc = "TPM emulator backend driver", | ||
635 | + | ||
636 | + .create = tpm_emulator_create, | ||
637 | + .startup_tpm = tpm_emulator_startup_tpm, | ||
638 | + .cancel_cmd = tpm_emulator_cancel_cmd, | ||
639 | + .get_tpm_established_flag = tpm_emulator_get_tpm_established_flag, | ||
640 | + .reset_tpm_established_flag = tpm_emulator_reset_tpm_established_flag, | ||
641 | + .get_tpm_version = tpm_emulator_get_tpm_version, | ||
642 | + .get_tpm_options = tpm_emulator_get_tpm_options, | ||
643 | +}; | ||
644 | + | ||
645 | +static void tpm_emulator_inst_init(Object *obj) | ||
646 | +{ | ||
647 | + TPMEmulator *tpm_emu = TPM_EMULATOR(obj); | ||
648 | + | ||
649 | + DPRINTF("%s", __func__); | ||
650 | + tpm_emu->options = g_new0(TPMEmulatorOptions, 1); | ||
651 | + tpm_emu->cur_locty_number = ~0; | ||
652 | +} | ||
653 | + | ||
654 | +/* | ||
655 | + * Gracefully shut down the external TPM | ||
656 | + */ | ||
657 | +static void tpm_emulator_shutdown(TPMEmulator *tpm_emu) | ||
658 | +{ | ||
659 | + ptm_res res; | ||
660 | + | ||
661 | + if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_SHUTDOWN, &res, 0, | ||
662 | + sizeof(res)) < 0) { | ||
663 | + error_report("tpm-emulator: Could not cleanly shutdown the TPM: %s", | ||
664 | + strerror(errno)); | ||
665 | + } else if (res != 0) { | ||
666 | + error_report("tpm-emulator: TPM result for sutdown: 0x%x", | ||
667 | + be32_to_cpu(res)); | ||
668 | + } | ||
669 | +} | ||
670 | + | ||
671 | +static void tpm_emulator_inst_finalize(Object *obj) | ||
672 | +{ | ||
673 | + TPMEmulator *tpm_emu = TPM_EMULATOR(obj); | ||
674 | + | ||
675 | + tpm_emulator_shutdown(tpm_emu); | ||
676 | + | ||
677 | + object_unref(OBJECT(tpm_emu->data_ioc)); | ||
678 | + | ||
679 | + qemu_chr_fe_deinit(&tpm_emu->ctrl_chr, false); | ||
680 | + | ||
681 | + qapi_free_TPMEmulatorOptions(tpm_emu->options); | ||
682 | + | ||
683 | + if (tpm_emu->migration_blocker) { | ||
684 | + migrate_del_blocker(tpm_emu->migration_blocker); | ||
685 | + error_free(tpm_emu->migration_blocker); | ||
686 | + } | ||
687 | +} | ||
688 | + | ||
689 | +static void tpm_emulator_class_init(ObjectClass *klass, void *data) | ||
690 | +{ | ||
691 | + TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass); | ||
692 | + tbc->ops = &tpm_emulator_driver; | ||
693 | + tbc->handle_request = tpm_emulator_handle_request; | ||
694 | +} | ||
695 | + | ||
696 | +static const TypeInfo tpm_emulator_info = { | ||
697 | + .name = TYPE_TPM_EMULATOR, | ||
698 | + .parent = TYPE_TPM_BACKEND, | ||
699 | + .instance_size = sizeof(TPMEmulator), | ||
700 | + .class_init = tpm_emulator_class_init, | ||
701 | + .instance_init = tpm_emulator_inst_init, | ||
702 | + .instance_finalize = tpm_emulator_inst_finalize, | ||
703 | +}; | ||
704 | + | ||
705 | +static void tpm_emulator_register(void) | ||
706 | +{ | ||
707 | + type_register_static(&tpm_emulator_info); | ||
708 | + tpm_register_driver(&tpm_emulator_driver); | ||
709 | +} | ||
710 | + | ||
711 | +type_init(tpm_emulator_register) | ||
712 | diff --git a/hw/tpm/tpm_ioctl.h b/hw/tpm/tpm_ioctl.h | ||
713 | new file mode 100644 | ||
714 | index 0000000000..33564b11de | ||
715 | --- /dev/null | ||
716 | +++ b/hw/tpm/tpm_ioctl.h | ||
717 | @@ -0,0 +1,246 @@ | ||
718 | +/* | ||
719 | + * tpm_ioctl.h | ||
720 | + * | ||
721 | + * (c) Copyright IBM Corporation 2014, 2015. | ||
722 | + * | ||
723 | + * This file is licensed under the terms of the 3-clause BSD license | ||
724 | + */ | ||
725 | +#ifndef _TPM_IOCTL_H_ | ||
726 | +#define _TPM_IOCTL_H_ | ||
727 | + | ||
728 | +#include <stdint.h> | ||
729 | +#include <sys/uio.h> | ||
730 | +#include <sys/types.h> | ||
731 | +#include <sys/ioctl.h> | ||
732 | + | ||
733 | +/* | ||
734 | + * Every response from a command involving a TPM command execution must hold | ||
735 | + * the ptm_res as the first element. | ||
736 | + * ptm_res corresponds to the error code of a command executed by the TPM. | ||
737 | + */ | ||
738 | + | ||
739 | +typedef uint32_t ptm_res; | ||
740 | + | ||
741 | +/* PTM_GET_TPMESTABLISHED: get the establishment bit */ | ||
742 | +struct ptm_est { | ||
743 | + union { | ||
744 | + struct { | ||
745 | + ptm_res tpm_result; | ||
746 | + unsigned char bit; /* TPM established bit */ | ||
747 | + } resp; /* response */ | ||
748 | + } u; | ||
749 | +}; | ||
750 | + | ||
751 | +/* PTM_RESET_TPMESTABLISHED: reset establishment bit */ | ||
752 | +struct ptm_reset_est { | ||
753 | + union { | ||
754 | + struct { | ||
755 | + uint8_t loc; /* locality to use */ | ||
756 | + } req; /* request */ | ||
757 | + struct { | ||
758 | + ptm_res tpm_result; | ||
759 | + } resp; /* response */ | ||
760 | + } u; | ||
761 | +}; | ||
762 | + | ||
763 | +/* PTM_INIT */ | ||
764 | +struct ptm_init { | ||
765 | + union { | ||
766 | + struct { | ||
767 | + uint32_t init_flags; /* see definitions below */ | ||
768 | + } req; /* request */ | ||
769 | + struct { | ||
770 | + ptm_res tpm_result; | ||
771 | + } resp; /* response */ | ||
772 | + } u; | ||
773 | +}; | ||
774 | + | ||
775 | +/* above init_flags */ | ||
776 | +#define PTM_INIT_FLAG_DELETE_VOLATILE (1 << 0) | ||
777 | + /* delete volatile state file after reading it */ | ||
778 | + | ||
779 | +/* PTM_SET_LOCALITY */ | ||
780 | +struct ptm_loc { | ||
781 | + union { | ||
782 | + struct { | ||
783 | + uint8_t loc; /* locality to set */ | ||
784 | + } req; /* request */ | ||
785 | + struct { | ||
786 | + ptm_res tpm_result; | ||
787 | + } resp; /* response */ | ||
788 | + } u; | ||
789 | +}; | ||
790 | + | ||
791 | +/* PTM_HASH_DATA: hash given data */ | ||
792 | +struct ptm_hdata { | ||
793 | + union { | ||
794 | + struct { | ||
795 | + uint32_t length; | ||
796 | + uint8_t data[4096]; | ||
797 | + } req; /* request */ | ||
798 | + struct { | ||
799 | + ptm_res tpm_result; | ||
800 | + } resp; /* response */ | ||
801 | + } u; | ||
802 | +}; | ||
803 | + | ||
804 | +/* | ||
805 | + * size of the TPM state blob to transfer; x86_64 can handle 8k, | ||
806 | + * ppc64le only ~7k; keep the response below a 4k page size | ||
807 | + */ | ||
808 | +#define PTM_STATE_BLOB_SIZE (3 * 1024) | ||
809 | + | ||
810 | +/* | ||
811 | + * The following is the data structure to get state blobs from the TPM. | ||
812 | + * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple reads | ||
813 | + * with this ioctl and with adjusted offset are necessary. All bytes | ||
814 | + * must be transferred and the transfer is done once the last byte has been | ||
815 | + * returned. | ||
816 | + * It is possible to use the read() interface for reading the data; however, the | ||
817 | + * first bytes of the state blob will be part of the response to the ioctl(); a | ||
818 | + * subsequent read() is only necessary if the total length (totlength) exceeds | ||
819 | + * the number of received bytes. seek() is not supported. | ||
820 | + */ | ||
821 | +struct ptm_getstate { | ||
822 | + union { | ||
823 | + struct { | ||
824 | + uint32_t state_flags; /* may be: PTM_STATE_FLAG_DECRYPTED */ | ||
825 | + uint32_t type; /* which blob to pull */ | ||
826 | + uint32_t offset; /* offset from where to read */ | ||
827 | + } req; /* request */ | ||
828 | + struct { | ||
829 | + ptm_res tpm_result; | ||
830 | + uint32_t state_flags; /* may be: PTM_STATE_FLAG_ENCRYPTED */ | ||
831 | + uint32_t totlength; /* total length that will be transferred */ | ||
832 | + uint32_t length; /* number of bytes in following buffer */ | ||
833 | + uint8_t data[PTM_STATE_BLOB_SIZE]; | ||
834 | + } resp; /* response */ | ||
835 | + } u; | ||
836 | +}; | ||
837 | + | ||
838 | +/* TPM state blob types */ | ||
839 | +#define PTM_BLOB_TYPE_PERMANENT 1 | ||
840 | +#define PTM_BLOB_TYPE_VOLATILE 2 | ||
841 | +#define PTM_BLOB_TYPE_SAVESTATE 3 | ||
842 | + | ||
843 | +/* state_flags above : */ | ||
844 | +#define PTM_STATE_FLAG_DECRYPTED 1 /* on input: get decrypted state */ | ||
845 | +#define PTM_STATE_FLAG_ENCRYPTED 2 /* on output: state is encrypted */ | ||
846 | + | ||
847 | +/* | ||
848 | + * The following is the data structure to set state blobs in the TPM. | ||
849 | + * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple | ||
850 | + * 'writes' using this ioctl are necessary. The last packet is indicated | ||
851 | + * by the length being smaller than the PTM_STATE_BLOB_SIZE. | ||
852 | + * The very first packet may have a length indicator of '0' enabling | ||
853 | + * a write() with all the bytes from a buffer. If the write() interface | ||
854 | + * is used, a final ioctl with a non-full buffer must be made to indicate | ||
855 | + * that all data were transferred (a write with 0 bytes would not work). | ||
856 | + */ | ||
857 | +struct ptm_setstate { | ||
858 | + union { | ||
859 | + struct { | ||
860 | + uint32_t state_flags; /* may be PTM_STATE_FLAG_ENCRYPTED */ | ||
861 | + uint32_t type; /* which blob to set */ | ||
862 | + uint32_t length; /* length of the data; | ||
863 | + use 0 on the first packet to | ||
864 | + transfer using write() */ | ||
865 | + uint8_t data[PTM_STATE_BLOB_SIZE]; | ||
866 | + } req; /* request */ | ||
867 | + struct { | ||
868 | + ptm_res tpm_result; | ||
869 | + } resp; /* response */ | ||
870 | + } u; | ||
871 | +}; | ||
872 | + | ||
873 | +/* | ||
874 | + * PTM_GET_CONFIG: Data structure to get runtime configuration information | ||
875 | + * such as which keys are applied. | ||
876 | + */ | ||
877 | +struct ptm_getconfig { | ||
878 | + union { | ||
879 | + struct { | ||
880 | + ptm_res tpm_result; | ||
881 | + uint32_t flags; | ||
882 | + } resp; /* response */ | ||
883 | + } u; | ||
884 | +}; | ||
885 | + | ||
886 | +#define PTM_CONFIG_FLAG_FILE_KEY 0x1 | ||
887 | +#define PTM_CONFIG_FLAG_MIGRATION_KEY 0x2 | ||
888 | + | ||
889 | + | ||
890 | +typedef uint64_t ptm_cap; | ||
891 | +typedef struct ptm_est ptm_est; | ||
892 | +typedef struct ptm_reset_est ptm_reset_est; | ||
893 | +typedef struct ptm_loc ptm_loc; | ||
894 | +typedef struct ptm_hdata ptm_hdata; | ||
895 | +typedef struct ptm_init ptm_init; | ||
896 | +typedef struct ptm_getstate ptm_getstate; | ||
897 | +typedef struct ptm_setstate ptm_setstate; | ||
898 | +typedef struct ptm_getconfig ptm_getconfig; | ||
899 | + | ||
900 | +/* capability flags returned by PTM_GET_CAPABILITY */ | ||
901 | +#define PTM_CAP_INIT (1) | ||
902 | +#define PTM_CAP_SHUTDOWN (1 << 1) | ||
903 | +#define PTM_CAP_GET_TPMESTABLISHED (1 << 2) | ||
904 | +#define PTM_CAP_SET_LOCALITY (1 << 3) | ||
905 | +#define PTM_CAP_HASHING (1 << 4) | ||
906 | +#define PTM_CAP_CANCEL_TPM_CMD (1 << 5) | ||
907 | +#define PTM_CAP_STORE_VOLATILE (1 << 6) | ||
908 | +#define PTM_CAP_RESET_TPMESTABLISHED (1 << 7) | ||
909 | +#define PTM_CAP_GET_STATEBLOB (1 << 8) | ||
910 | +#define PTM_CAP_SET_STATEBLOB (1 << 9) | ||
911 | +#define PTM_CAP_STOP (1 << 10) | ||
912 | +#define PTM_CAP_GET_CONFIG (1 << 11) | ||
913 | +#define PTM_CAP_SET_DATAFD (1 << 12) | ||
914 | + | ||
915 | +enum { | ||
916 | + PTM_GET_CAPABILITY = _IOR('P', 0, ptm_cap), | ||
917 | + PTM_INIT = _IOWR('P', 1, ptm_init), | ||
918 | + PTM_SHUTDOWN = _IOR('P', 2, ptm_res), | ||
919 | + PTM_GET_TPMESTABLISHED = _IOR('P', 3, ptm_est), | ||
920 | + PTM_SET_LOCALITY = _IOWR('P', 4, ptm_loc), | ||
921 | + PTM_HASH_START = _IOR('P', 5, ptm_res), | ||
922 | + PTM_HASH_DATA = _IOWR('P', 6, ptm_hdata), | ||
923 | + PTM_HASH_END = _IOR('P', 7, ptm_res), | ||
924 | + PTM_CANCEL_TPM_CMD = _IOR('P', 8, ptm_res), | ||
925 | + PTM_STORE_VOLATILE = _IOR('P', 9, ptm_res), | ||
926 | + PTM_RESET_TPMESTABLISHED = _IOWR('P', 10, ptm_reset_est), | ||
927 | + PTM_GET_STATEBLOB = _IOWR('P', 11, ptm_getstate), | ||
928 | + PTM_SET_STATEBLOB = _IOWR('P', 12, ptm_setstate), | ||
929 | + PTM_STOP = _IOR('P', 13, ptm_res), | ||
930 | + PTM_GET_CONFIG = _IOR('P', 14, ptm_getconfig), | ||
931 | + PTM_SET_DATAFD = _IOR('P', 15, ptm_res), | ||
932 | +}; | ||
933 | + | ||
934 | +/* | ||
935 | + * Commands used by the non-CUSE TPMs | ||
936 | + * | ||
937 | + * All messages container big-endian data. | ||
938 | + * | ||
939 | + * The return messages only contain the 'resp' part of the unions | ||
940 | + * in the data structures above. Besides that the limits in the | ||
941 | + * buffers above (ptm_hdata:u.req.data and ptm_get_state:u.resp.data | ||
942 | + * and ptm_set_state:u.req.data) are 0xffffffff. | ||
943 | + */ | ||
944 | +enum { | ||
945 | + CMD_GET_CAPABILITY = 1, | ||
946 | + CMD_INIT, | ||
947 | + CMD_SHUTDOWN, | ||
948 | + CMD_GET_TPMESTABLISHED, | ||
949 | + CMD_SET_LOCALITY, | ||
950 | + CMD_HASH_START, | ||
951 | + CMD_HASH_DATA, | ||
952 | + CMD_HASH_END, | ||
953 | + CMD_CANCEL_TPM_CMD, | ||
954 | + CMD_STORE_VOLATILE, | ||
955 | + CMD_RESET_TPMESTABLISHED, | ||
956 | + CMD_GET_STATEBLOB, | ||
957 | + CMD_SET_STATEBLOB, | ||
958 | + CMD_STOP, | ||
959 | + CMD_GET_CONFIG, | ||
960 | + CMD_SET_DATAFD | ||
961 | +}; | ||
962 | + | ||
963 | +#endif /* _TPM_IOCTL_H */ | ||
964 | diff --git a/qapi-schema.json b/qapi-schema.json | ||
965 | index 802ea53d00..78a00bc868 100644 | ||
966 | --- a/qapi-schema.json | ||
967 | +++ b/qapi-schema.json | ||
968 | @@ -5314,10 +5314,12 @@ | ||
969 | # An enumeration of TPM types | ||
970 | # | ||
971 | # @passthrough: TPM passthrough type | ||
972 | +# @emulator: Software Emulator TPM type | ||
973 | +# Since: 2.11 | ||
974 | # | ||
975 | # Since: 1.5 | ||
976 | ## | ||
977 | -{ 'enum': 'TpmType', 'data': [ 'passthrough' ] } | ||
978 | +{ 'enum': 'TpmType', 'data': [ 'passthrough', 'emulator' ] } | ||
979 | |||
980 | ## | ||
981 | # @query-tpm-types: | ||
982 | @@ -5352,6 +5354,17 @@ | ||
983 | '*cancel-path' : 'str'} } | ||
984 | |||
985 | ## | ||
986 | +# @TPMEmulatorOptions: | ||
987 | +# | ||
988 | +# Information about the TPM emulator type | ||
989 | +# | ||
990 | +# @chardev: Name of a unix socket chardev | ||
991 | +# | ||
992 | +# Since: 2.11 | ||
993 | +## | ||
994 | +{ 'struct': 'TPMEmulatorOptions', 'data': { 'chardev' : 'str' } } | ||
995 | + | ||
996 | +## | ||
997 | # @TpmTypeOptions: | ||
998 | # | ||
999 | # A union referencing different TPM backend types' configuration options | ||
1000 | @@ -5361,7 +5374,8 @@ | ||
1001 | # Since: 1.5 | ||
1002 | ## | ||
1003 | { 'union': 'TpmTypeOptions', | ||
1004 | - 'data': { 'passthrough' : 'TPMPassthroughOptions' } } | ||
1005 | + 'data': { 'passthrough' : 'TPMPassthroughOptions', | ||
1006 | + 'emulator': 'TPMEmulatorOptions'} } | ||
1007 | |||
1008 | ## | ||
1009 | # @TPMInfo: | ||
1010 | diff --git a/qemu-options.hx b/qemu-options.hx | ||
1011 | index 9f6e2adfff..60eb193c23 100644 | ||
1012 | --- a/qemu-options.hx | ||
1013 | +++ b/qemu-options.hx | ||
1014 | @@ -3121,7 +3121,9 @@ DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \ | ||
1015 | "-tpmdev passthrough,id=id[,path=path][,cancel-path=path]\n" | ||
1016 | " use path to provide path to a character device; default is /dev/tpm0\n" | ||
1017 | " use cancel-path to provide path to TPM's cancel sysfs entry; if\n" | ||
1018 | - " not provided it will be searched for in /sys/class/misc/tpm?/device\n", | ||
1019 | + " not provided it will be searched for in /sys/class/misc/tpm?/device\n" | ||
1020 | + "-tpmdev emulator,id=id,chardev=dev\n" | ||
1021 | + " configure the TPM device using chardev backend\n", | ||
1022 | QEMU_ARCH_ALL) | ||
1023 | STEXI | ||
1024 | |||
1025 | @@ -3130,8 +3132,8 @@ The general form of a TPM device option is: | ||
1026 | |||
1027 | @item -tpmdev @var{backend} ,id=@var{id} [,@var{options}] | ||
1028 | @findex -tpmdev | ||
1029 | -Backend type must be: | ||
1030 | -@option{passthrough}. | ||
1031 | +Backend type must be either one of the following: | ||
1032 | +@option{passthrough}, @option{emulator}. | ||
1033 | |||
1034 | The specific backend type will determine the applicable options. | ||
1035 | The @code{-tpmdev} option creates the TPM backend and requires a | ||
1036 | @@ -3181,6 +3183,20 @@ To create a passthrough TPM use the following two options: | ||
1037 | Note that the @code{-tpmdev} id is @code{tpm0} and is referenced by | ||
1038 | @code{tpmdev=tpm0} in the device option. | ||
1039 | |||
1040 | +@item -tpmdev emulator, id=@var{id}, chardev=@var{dev} | ||
1041 | + | ||
1042 | +(Linux-host only) Enable access to a TPM emulator using Unix domain socket based | ||
1043 | +chardev backend. | ||
1044 | + | ||
1045 | +@option{chardev} specifies the unique ID of a character device backend that provides connection to the software TPM server. | ||
1046 | + | ||
1047 | +To create a TPM emulator backend device with chardev socket backend: | ||
1048 | +@example | ||
1049 | + | ||
1050 | +-chardev socket,id=chrtpm,path=/tmp/swtpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0 | ||
1051 | + | ||
1052 | +@end example | ||
1053 | + | ||
1054 | @end table | ||
1055 | |||
1056 | ETEXI | ||
1057 | -- | ||
1058 | 2.11.0 | ||
1059 | |||