]>
Commit | Line | Data |
---|---|---|
4549a8b7 SB |
1 | /* |
2 | * passthrough TPM driver | |
3 | * | |
4 | * Copyright (c) 2010 - 2013 IBM Corporation | |
5 | * Authors: | |
6 | * Stefan Berger <[email protected]> | |
7 | * | |
8 | * Copyright (C) 2011 IAIK, Graz University of Technology | |
9 | * Author: Andreas Niederl | |
10 | * | |
11 | * This library is free software; you can redistribute it and/or | |
12 | * modify it under the terms of the GNU Lesser General Public | |
13 | * License as published by the Free Software Foundation; either | |
14 | * version 2 of the License, or (at your option) any later version. | |
15 | * | |
16 | * This library is distributed in the hope that it will be useful, | |
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
19 | * Lesser General Public License for more details. | |
20 | * | |
21 | * You should have received a copy of the GNU Lesser General Public | |
22 | * License along with this library; if not, see <http://www.gnu.org/licenses/> | |
23 | */ | |
24 | ||
0430891c | 25 | #include "qemu/osdep.h" |
4549a8b7 | 26 | #include "qemu-common.h" |
d49b6836 | 27 | #include "qemu/error-report.h" |
4549a8b7 | 28 | #include "qemu/sockets.h" |
dccfcd0e | 29 | #include "sysemu/tpm_backend.h" |
4549a8b7 SB |
30 | #include "tpm_int.h" |
31 | #include "hw/hw.h" | |
0d09e41a | 32 | #include "hw/i386/pc.h" |
bdee56f5 | 33 | #include "sysemu/tpm_backend_int.h" |
4549a8b7 | 34 | #include "tpm_tis.h" |
56a3c24f | 35 | #include "tpm_util.h" |
4549a8b7 | 36 | |
4d1ba9c4 SB |
37 | #define DEBUG_TPM 0 |
38 | ||
39 | #define DPRINTF(fmt, ...) do { \ | |
40 | if (DEBUG_TPM) { \ | |
41 | fprintf(stderr, fmt, ## __VA_ARGS__); \ | |
42 | } \ | |
43 | } while (0); | |
4549a8b7 | 44 | |
8f0605cc SB |
45 | #define TYPE_TPM_PASSTHROUGH "tpm-passthrough" |
46 | #define TPM_PASSTHROUGH(obj) \ | |
47 | OBJECT_CHECK(TPMPassthruState, (obj), TYPE_TPM_PASSTHROUGH) | |
4549a8b7 | 48 | |
bdee56f5 PB |
49 | static const TPMDriverOps tpm_passthrough_driver; |
50 | ||
8f0605cc | 51 | /* data structures */ |
4549a8b7 SB |
52 | typedef struct TPMPassthruThreadParams { |
53 | TPMState *tpm_state; | |
54 | ||
55 | TPMRecvDataCB *recv_data_callback; | |
56 | TPMBackend *tb; | |
57 | } TPMPassthruThreadParams; | |
58 | ||
59 | struct TPMPassthruState { | |
8f0605cc SB |
60 | TPMBackend parent; |
61 | ||
4549a8b7 SB |
62 | TPMBackendThread tbt; |
63 | ||
64 | TPMPassthruThreadParams tpm_thread_params; | |
65 | ||
66 | char *tpm_dev; | |
67 | int tpm_fd; | |
92dcc234 SB |
68 | bool tpm_executing; |
69 | bool tpm_op_canceled; | |
70 | int cancel_fd; | |
4549a8b7 | 71 | bool had_startup_error; |
56a3c24f SB |
72 | |
73 | TPMVersion tpm_version; | |
4549a8b7 SB |
74 | }; |
75 | ||
8f0605cc SB |
76 | typedef struct TPMPassthruState TPMPassthruState; |
77 | ||
4549a8b7 SB |
78 | #define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0" |
79 | ||
92dcc234 SB |
80 | /* functions */ |
81 | ||
82 | static void tpm_passthrough_cancel_cmd(TPMBackend *tb); | |
83 | ||
4549a8b7 SB |
84 | static int tpm_passthrough_unix_write(int fd, const uint8_t *buf, uint32_t len) |
85 | { | |
46f296cd DB |
86 | int ret, remain; |
87 | ||
88 | remain = len; | |
e7658fcc | 89 | while (remain > 0) { |
46f296cd DB |
90 | ret = write(fd, buf, remain); |
91 | if (ret < 0) { | |
92 | if (errno != EINTR && errno != EAGAIN) { | |
93 | return -1; | |
94 | } | |
95 | } else if (ret == 0) { | |
96 | break; | |
97 | } else { | |
98 | buf += ret; | |
99 | remain -= ret; | |
100 | } | |
101 | } | |
102 | return len - remain; | |
4549a8b7 SB |
103 | } |
104 | ||
105 | static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len) | |
106 | { | |
46f296cd DB |
107 | int ret; |
108 | reread: | |
109 | ret = read(fd, buf, len); | |
110 | if (ret < 0) { | |
111 | if (errno != EINTR && errno != EAGAIN) { | |
112 | return -1; | |
113 | } | |
114 | goto reread; | |
115 | } | |
116 | return ret; | |
4549a8b7 SB |
117 | } |
118 | ||
119 | static uint32_t tpm_passthrough_get_size_from_buffer(const uint8_t *buf) | |
120 | { | |
121 | struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)buf; | |
122 | ||
123 | return be32_to_cpu(resp->len); | |
124 | } | |
125 | ||
bdee56f5 PB |
126 | /* |
127 | * Write an error message in the given output buffer. | |
128 | */ | |
129 | static void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len) | |
130 | { | |
131 | if (out_len >= sizeof(struct tpm_resp_hdr)) { | |
132 | struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)out; | |
133 | ||
134 | resp->tag = cpu_to_be16(TPM_TAG_RSP_COMMAND); | |
135 | resp->len = cpu_to_be32(sizeof(struct tpm_resp_hdr)); | |
136 | resp->errcode = cpu_to_be32(TPM_FAIL); | |
137 | } | |
138 | } | |
139 | ||
fd859081 SB |
140 | static bool tpm_passthrough_is_selftest(const uint8_t *in, uint32_t in_len) |
141 | { | |
142 | struct tpm_req_hdr *hdr = (struct tpm_req_hdr *)in; | |
143 | ||
144 | if (in_len >= sizeof(*hdr)) { | |
145 | return (be32_to_cpu(hdr->ordinal) == TPM_ORD_ContinueSelfTest); | |
146 | } | |
147 | ||
148 | return false; | |
149 | } | |
150 | ||
92dcc234 | 151 | static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt, |
4549a8b7 | 152 | const uint8_t *in, uint32_t in_len, |
fd859081 SB |
153 | uint8_t *out, uint32_t out_len, |
154 | bool *selftest_done) | |
4549a8b7 SB |
155 | { |
156 | int ret; | |
fd859081 SB |
157 | bool is_selftest; |
158 | const struct tpm_resp_hdr *hdr; | |
4549a8b7 | 159 | |
92dcc234 SB |
160 | tpm_pt->tpm_op_canceled = false; |
161 | tpm_pt->tpm_executing = true; | |
fd859081 SB |
162 | *selftest_done = false; |
163 | ||
164 | is_selftest = tpm_passthrough_is_selftest(in, in_len); | |
92dcc234 SB |
165 | |
166 | ret = tpm_passthrough_unix_write(tpm_pt->tpm_fd, in, in_len); | |
4549a8b7 | 167 | if (ret != in_len) { |
5f333d79 | 168 | if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) { |
92dcc234 | 169 | error_report("tpm_passthrough: error while transmitting data " |
27215a22 | 170 | "to TPM: %s (%i)", |
92dcc234 SB |
171 | strerror(errno), errno); |
172 | } | |
4549a8b7 SB |
173 | goto err_exit; |
174 | } | |
175 | ||
92dcc234 SB |
176 | tpm_pt->tpm_executing = false; |
177 | ||
178 | ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len); | |
4549a8b7 | 179 | if (ret < 0) { |
5f333d79 | 180 | if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) { |
92dcc234 | 181 | error_report("tpm_passthrough: error while reading data from " |
27215a22 | 182 | "TPM: %s (%i)", |
92dcc234 SB |
183 | strerror(errno), errno); |
184 | } | |
4549a8b7 SB |
185 | } else if (ret < sizeof(struct tpm_resp_hdr) || |
186 | tpm_passthrough_get_size_from_buffer(out) != ret) { | |
187 | ret = -1; | |
188 | error_report("tpm_passthrough: received invalid response " | |
27215a22 | 189 | "packet from TPM"); |
4549a8b7 SB |
190 | } |
191 | ||
fd859081 SB |
192 | if (is_selftest && (ret >= sizeof(struct tpm_resp_hdr))) { |
193 | hdr = (struct tpm_resp_hdr *)out; | |
194 | *selftest_done = (be32_to_cpu(hdr->errcode) == 0); | |
195 | } | |
196 | ||
4549a8b7 SB |
197 | err_exit: |
198 | if (ret < 0) { | |
199 | tpm_write_fatal_error_response(out, out_len); | |
200 | } | |
201 | ||
92dcc234 SB |
202 | tpm_pt->tpm_executing = false; |
203 | ||
4549a8b7 SB |
204 | return ret; |
205 | } | |
206 | ||
92dcc234 | 207 | static int tpm_passthrough_unix_transfer(TPMPassthruState *tpm_pt, |
fd859081 SB |
208 | const TPMLocality *locty_data, |
209 | bool *selftest_done) | |
4549a8b7 | 210 | { |
92dcc234 | 211 | return tpm_passthrough_unix_tx_bufs(tpm_pt, |
4549a8b7 SB |
212 | locty_data->w_buffer.buffer, |
213 | locty_data->w_offset, | |
214 | locty_data->r_buffer.buffer, | |
fd859081 SB |
215 | locty_data->r_buffer.size, |
216 | selftest_done); | |
4549a8b7 SB |
217 | } |
218 | ||
219 | static void tpm_passthrough_worker_thread(gpointer data, | |
220 | gpointer user_data) | |
221 | { | |
222 | TPMPassthruThreadParams *thr_parms = user_data; | |
8f0605cc | 223 | TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(thr_parms->tb); |
4549a8b7 | 224 | TPMBackendCmd cmd = (TPMBackendCmd)data; |
fd859081 | 225 | bool selftest_done = false; |
4549a8b7 SB |
226 | |
227 | DPRINTF("tpm_passthrough: processing command type %d\n", cmd); | |
228 | ||
229 | switch (cmd) { | |
230 | case TPM_BACKEND_CMD_PROCESS_CMD: | |
92dcc234 | 231 | tpm_passthrough_unix_transfer(tpm_pt, |
fd859081 SB |
232 | thr_parms->tpm_state->locty_data, |
233 | &selftest_done); | |
4549a8b7 SB |
234 | |
235 | thr_parms->recv_data_callback(thr_parms->tpm_state, | |
fd859081 SB |
236 | thr_parms->tpm_state->locty_number, |
237 | selftest_done); | |
4549a8b7 SB |
238 | break; |
239 | case TPM_BACKEND_CMD_INIT: | |
240 | case TPM_BACKEND_CMD_END: | |
241 | case TPM_BACKEND_CMD_TPM_RESET: | |
242 | /* nothing to do */ | |
243 | break; | |
244 | } | |
245 | } | |
246 | ||
247 | /* | |
248 | * Start the TPM (thread). If it had been started before, then terminate | |
249 | * and start it again. | |
250 | */ | |
251 | static int tpm_passthrough_startup_tpm(TPMBackend *tb) | |
252 | { | |
8f0605cc | 253 | TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); |
4549a8b7 SB |
254 | |
255 | /* terminate a running TPM */ | |
256 | tpm_backend_thread_end(&tpm_pt->tbt); | |
257 | ||
258 | tpm_backend_thread_create(&tpm_pt->tbt, | |
259 | tpm_passthrough_worker_thread, | |
8f0605cc | 260 | &tpm_pt->tpm_thread_params); |
4549a8b7 SB |
261 | |
262 | return 0; | |
263 | } | |
264 | ||
265 | static void tpm_passthrough_reset(TPMBackend *tb) | |
266 | { | |
8f0605cc | 267 | TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); |
4549a8b7 SB |
268 | |
269 | DPRINTF("tpm_passthrough: CALL TO TPM_RESET!\n"); | |
270 | ||
92dcc234 SB |
271 | tpm_passthrough_cancel_cmd(tb); |
272 | ||
4549a8b7 SB |
273 | tpm_backend_thread_end(&tpm_pt->tbt); |
274 | ||
275 | tpm_pt->had_startup_error = false; | |
276 | } | |
277 | ||
278 | static int tpm_passthrough_init(TPMBackend *tb, TPMState *s, | |
279 | TPMRecvDataCB *recv_data_cb) | |
280 | { | |
8f0605cc | 281 | TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); |
4549a8b7 SB |
282 | |
283 | tpm_pt->tpm_thread_params.tpm_state = s; | |
284 | tpm_pt->tpm_thread_params.recv_data_callback = recv_data_cb; | |
285 | tpm_pt->tpm_thread_params.tb = tb; | |
286 | ||
287 | return 0; | |
288 | } | |
289 | ||
290 | static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb) | |
291 | { | |
292 | return false; | |
293 | } | |
294 | ||
116694c3 SB |
295 | static int tpm_passthrough_reset_tpm_established_flag(TPMBackend *tb, |
296 | uint8_t locty) | |
297 | { | |
298 | /* only a TPM 2.0 will support this */ | |
299 | return 0; | |
300 | } | |
301 | ||
4549a8b7 SB |
302 | static bool tpm_passthrough_get_startup_error(TPMBackend *tb) |
303 | { | |
8f0605cc | 304 | TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); |
4549a8b7 SB |
305 | |
306 | return tpm_pt->had_startup_error; | |
307 | } | |
308 | ||
309 | static size_t tpm_passthrough_realloc_buffer(TPMSizedBuffer *sb) | |
310 | { | |
311 | size_t wanted_size = 4096; /* Linux tpm.c buffer size */ | |
312 | ||
313 | if (sb->size != wanted_size) { | |
314 | sb->buffer = g_realloc(sb->buffer, wanted_size); | |
315 | sb->size = wanted_size; | |
316 | } | |
317 | return sb->size; | |
318 | } | |
319 | ||
320 | static void tpm_passthrough_deliver_request(TPMBackend *tb) | |
321 | { | |
8f0605cc | 322 | TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); |
4549a8b7 SB |
323 | |
324 | tpm_backend_thread_deliver_request(&tpm_pt->tbt); | |
325 | } | |
326 | ||
327 | static void tpm_passthrough_cancel_cmd(TPMBackend *tb) | |
328 | { | |
8f0605cc | 329 | TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); |
92dcc234 SB |
330 | int n; |
331 | ||
332 | /* | |
333 | * As of Linux 3.7 the tpm_tis driver does not properly cancel | |
334 | * commands on all TPM manufacturers' TPMs. | |
335 | * Only cancel if we're busy so we don't cancel someone else's | |
336 | * command, e.g., a command executed on the host. | |
337 | */ | |
338 | if (tpm_pt->tpm_executing) { | |
339 | if (tpm_pt->cancel_fd >= 0) { | |
340 | n = write(tpm_pt->cancel_fd, "-", 1); | |
341 | if (n != 1) { | |
27215a22 | 342 | error_report("Canceling TPM command failed: %s", |
92dcc234 SB |
343 | strerror(errno)); |
344 | } else { | |
345 | tpm_pt->tpm_op_canceled = true; | |
346 | } | |
347 | } else { | |
348 | error_report("Cannot cancel TPM command due to missing " | |
349 | "TPM sysfs cancel entry"); | |
350 | } | |
351 | } | |
4549a8b7 SB |
352 | } |
353 | ||
354 | static const char *tpm_passthrough_create_desc(void) | |
355 | { | |
356 | return "Passthrough TPM backend driver"; | |
357 | } | |
358 | ||
116694c3 SB |
359 | static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb) |
360 | { | |
56a3c24f | 361 | TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); |
4549a8b7 | 362 | |
56a3c24f | 363 | return tpm_pt->tpm_version; |
4549a8b7 SB |
364 | } |
365 | ||
92dcc234 SB |
366 | /* |
367 | * Unless path or file descriptor set has been provided by user, | |
368 | * determine the sysfs cancel file following kernel documentation | |
369 | * in Documentation/ABI/stable/sysfs-class-tpm. | |
8e36d6ca | 370 | * From /dev/tpm0 create /sys/class/misc/tpm0/device/cancel |
92dcc234 SB |
371 | */ |
372 | static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb) | |
373 | { | |
8e36d6ca | 374 | TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); |
92dcc234 | 375 | int fd = -1; |
8e36d6ca | 376 | char *dev; |
92dcc234 | 377 | char path[PATH_MAX]; |
92dcc234 SB |
378 | |
379 | if (tb->cancel_path) { | |
380 | fd = qemu_open(tb->cancel_path, O_WRONLY); | |
381 | if (fd < 0) { | |
382 | error_report("Could not open TPM cancel path : %s", | |
383 | strerror(errno)); | |
384 | } | |
385 | return fd; | |
386 | } | |
387 | ||
8e36d6ca SB |
388 | dev = strrchr(tpm_pt->tpm_dev, '/'); |
389 | if (dev) { | |
390 | dev++; | |
391 | if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel", | |
392 | dev) < sizeof(path)) { | |
92dcc234 | 393 | fd = qemu_open(path, O_WRONLY); |
8e36d6ca SB |
394 | if (fd >= 0) { |
395 | tb->cancel_path = g_strdup(path); | |
396 | } else { | |
397 | error_report("tpm_passthrough: Could not open TPM cancel " | |
398 | "path %s : %s", path, strerror(errno)); | |
399 | } | |
92dcc234 | 400 | } |
8e36d6ca SB |
401 | } else { |
402 | error_report("tpm_passthrough: Bad TPM device path %s", | |
403 | tpm_pt->tpm_dev); | |
92dcc234 SB |
404 | } |
405 | ||
406 | return fd; | |
407 | } | |
408 | ||
4549a8b7 SB |
409 | static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb) |
410 | { | |
8f0605cc | 411 | TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); |
4549a8b7 SB |
412 | const char *value; |
413 | ||
92dcc234 | 414 | value = qemu_opt_get(opts, "cancel-path"); |
24588100 | 415 | tb->cancel_path = g_strdup(value); |
92dcc234 | 416 | |
4549a8b7 SB |
417 | value = qemu_opt_get(opts, "path"); |
418 | if (!value) { | |
419 | value = TPM_PASSTHROUGH_DEFAULT_DEVICE; | |
420 | } | |
421 | ||
8f0605cc | 422 | tpm_pt->tpm_dev = g_strdup(value); |
4549a8b7 | 423 | |
8f0605cc | 424 | tb->path = g_strdup(tpm_pt->tpm_dev); |
4549a8b7 | 425 | |
8f0605cc SB |
426 | tpm_pt->tpm_fd = qemu_open(tpm_pt->tpm_dev, O_RDWR); |
427 | if (tpm_pt->tpm_fd < 0) { | |
27215a22 | 428 | error_report("Cannot access TPM device using '%s': %s", |
8f0605cc | 429 | tpm_pt->tpm_dev, strerror(errno)); |
4549a8b7 SB |
430 | goto err_free_parameters; |
431 | } | |
432 | ||
56a3c24f | 433 | if (tpm_util_test_tpmdev(tpm_pt->tpm_fd, &tpm_pt->tpm_version)) { |
27215a22 | 434 | error_report("'%s' is not a TPM device.", |
8f0605cc | 435 | tpm_pt->tpm_dev); |
4549a8b7 SB |
436 | goto err_close_tpmdev; |
437 | } | |
438 | ||
439 | return 0; | |
440 | ||
441 | err_close_tpmdev: | |
8f0605cc SB |
442 | qemu_close(tpm_pt->tpm_fd); |
443 | tpm_pt->tpm_fd = -1; | |
4549a8b7 SB |
444 | |
445 | err_free_parameters: | |
446 | g_free(tb->path); | |
447 | tb->path = NULL; | |
448 | ||
8f0605cc SB |
449 | g_free(tpm_pt->tpm_dev); |
450 | tpm_pt->tpm_dev = NULL; | |
4549a8b7 SB |
451 | |
452 | return 1; | |
453 | } | |
454 | ||
455 | static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id) | |
456 | { | |
8f0605cc SB |
457 | Object *obj = object_new(TYPE_TPM_PASSTHROUGH); |
458 | TPMBackend *tb = TPM_BACKEND(obj); | |
459 | TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); | |
4549a8b7 | 460 | |
4549a8b7 SB |
461 | tb->id = g_strdup(id); |
462 | /* let frontend set the fe_model to proper value */ | |
463 | tb->fe_model = -1; | |
464 | ||
465 | tb->ops = &tpm_passthrough_driver; | |
466 | ||
467 | if (tpm_passthrough_handle_device_opts(opts, tb)) { | |
468 | goto err_exit; | |
469 | } | |
470 | ||
8f0605cc SB |
471 | tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tb); |
472 | if (tpm_pt->cancel_fd < 0) { | |
92dcc234 SB |
473 | goto err_exit; |
474 | } | |
475 | ||
4549a8b7 SB |
476 | return tb; |
477 | ||
478 | err_exit: | |
479 | g_free(tb->id); | |
4549a8b7 SB |
480 | |
481 | return NULL; | |
482 | } | |
483 | ||
484 | static void tpm_passthrough_destroy(TPMBackend *tb) | |
485 | { | |
8f0605cc | 486 | TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); |
4549a8b7 | 487 | |
92dcc234 SB |
488 | tpm_passthrough_cancel_cmd(tb); |
489 | ||
4549a8b7 SB |
490 | tpm_backend_thread_end(&tpm_pt->tbt); |
491 | ||
492 | qemu_close(tpm_pt->tpm_fd); | |
8f0605cc | 493 | qemu_close(tpm_pt->cancel_fd); |
4549a8b7 SB |
494 | |
495 | g_free(tb->id); | |
496 | g_free(tb->path); | |
92dcc234 | 497 | g_free(tb->cancel_path); |
8f0605cc | 498 | g_free(tpm_pt->tpm_dev); |
4549a8b7 SB |
499 | } |
500 | ||
bb716238 SB |
501 | static const QemuOptDesc tpm_passthrough_cmdline_opts[] = { |
502 | TPM_STANDARD_CMDLINE_OPTS, | |
503 | { | |
504 | .name = "cancel-path", | |
505 | .type = QEMU_OPT_STRING, | |
506 | .help = "Sysfs file entry for canceling TPM commands", | |
507 | }, | |
508 | { | |
509 | .name = "path", | |
510 | .type = QEMU_OPT_STRING, | |
511 | .help = "Path to TPM device on the host", | |
512 | }, | |
513 | { /* end of list */ }, | |
514 | }; | |
515 | ||
bdee56f5 | 516 | static const TPMDriverOps tpm_passthrough_driver = { |
4549a8b7 | 517 | .type = TPM_TYPE_PASSTHROUGH, |
bb716238 | 518 | .opts = tpm_passthrough_cmdline_opts, |
4549a8b7 SB |
519 | .desc = tpm_passthrough_create_desc, |
520 | .create = tpm_passthrough_create, | |
521 | .destroy = tpm_passthrough_destroy, | |
522 | .init = tpm_passthrough_init, | |
523 | .startup_tpm = tpm_passthrough_startup_tpm, | |
524 | .realloc_buffer = tpm_passthrough_realloc_buffer, | |
525 | .reset = tpm_passthrough_reset, | |
526 | .had_startup_error = tpm_passthrough_get_startup_error, | |
527 | .deliver_request = tpm_passthrough_deliver_request, | |
528 | .cancel_cmd = tpm_passthrough_cancel_cmd, | |
529 | .get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag, | |
116694c3 SB |
530 | .reset_tpm_established_flag = tpm_passthrough_reset_tpm_established_flag, |
531 | .get_tpm_version = tpm_passthrough_get_tpm_version, | |
4549a8b7 SB |
532 | }; |
533 | ||
8f0605cc SB |
534 | static void tpm_passthrough_inst_init(Object *obj) |
535 | { | |
536 | } | |
537 | ||
538 | static void tpm_passthrough_inst_finalize(Object *obj) | |
539 | { | |
540 | } | |
541 | ||
542 | static void tpm_passthrough_class_init(ObjectClass *klass, void *data) | |
543 | { | |
544 | TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass); | |
545 | ||
546 | tbc->ops = &tpm_passthrough_driver; | |
547 | } | |
548 | ||
549 | static const TypeInfo tpm_passthrough_info = { | |
550 | .name = TYPE_TPM_PASSTHROUGH, | |
551 | .parent = TYPE_TPM_BACKEND, | |
552 | .instance_size = sizeof(TPMPassthruState), | |
553 | .class_init = tpm_passthrough_class_init, | |
554 | .instance_init = tpm_passthrough_inst_init, | |
555 | .instance_finalize = tpm_passthrough_inst_finalize, | |
556 | }; | |
557 | ||
4549a8b7 SB |
558 | static void tpm_passthrough_register(void) |
559 | { | |
8f0605cc | 560 | type_register_static(&tpm_passthrough_info); |
4549a8b7 SB |
561 | tpm_register_driver(&tpm_passthrough_driver); |
562 | } | |
563 | ||
564 | type_init(tpm_passthrough_register) |