]>
Commit | Line | Data |
---|---|---|
4549a8b7 SB |
1 | /* |
2 | * passthrough TPM driver | |
3 | * | |
4 | * Copyright (c) 2010 - 2013 IBM Corporation | |
5 | * Authors: | |
6 | * Stefan Berger <stefanb@us.ibm.com> | |
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 | ||
92dcc234 SB |
25 | #include <dirent.h> |
26 | ||
4549a8b7 SB |
27 | #include "qemu-common.h" |
28 | #include "qapi/error.h" | |
29 | #include "qemu/sockets.h" | |
dccfcd0e | 30 | #include "sysemu/tpm_backend.h" |
4549a8b7 SB |
31 | #include "tpm_int.h" |
32 | #include "hw/hw.h" | |
0d09e41a | 33 | #include "hw/i386/pc.h" |
bdee56f5 | 34 | #include "sysemu/tpm_backend_int.h" |
4549a8b7 | 35 | #include "tpm_tis.h" |
4549a8b7 SB |
36 | |
37 | /* #define DEBUG_TPM */ | |
38 | ||
39 | #ifdef DEBUG_TPM | |
40 | #define DPRINTF(fmt, ...) \ | |
41 | do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) | |
42 | #else | |
43 | #define DPRINTF(fmt, ...) \ | |
44 | do { } while (0) | |
45 | #endif | |
46 | ||
8f0605cc SB |
47 | #define TYPE_TPM_PASSTHROUGH "tpm-passthrough" |
48 | #define TPM_PASSTHROUGH(obj) \ | |
49 | OBJECT_CHECK(TPMPassthruState, (obj), TYPE_TPM_PASSTHROUGH) | |
4549a8b7 | 50 | |
bdee56f5 PB |
51 | static const TPMDriverOps tpm_passthrough_driver; |
52 | ||
8f0605cc | 53 | /* data structures */ |
4549a8b7 SB |
54 | typedef struct TPMPassthruThreadParams { |
55 | TPMState *tpm_state; | |
56 | ||
57 | TPMRecvDataCB *recv_data_callback; | |
58 | TPMBackend *tb; | |
59 | } TPMPassthruThreadParams; | |
60 | ||
61 | struct TPMPassthruState { | |
8f0605cc SB |
62 | TPMBackend parent; |
63 | ||
4549a8b7 SB |
64 | TPMBackendThread tbt; |
65 | ||
66 | TPMPassthruThreadParams tpm_thread_params; | |
67 | ||
68 | char *tpm_dev; | |
69 | int tpm_fd; | |
92dcc234 SB |
70 | bool tpm_executing; |
71 | bool tpm_op_canceled; | |
72 | int cancel_fd; | |
4549a8b7 SB |
73 | bool had_startup_error; |
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 | { | |
86 | return send_all(fd, buf, len); | |
87 | } | |
88 | ||
89 | static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len) | |
90 | { | |
91 | return recv_all(fd, buf, len, true); | |
92 | } | |
93 | ||
94 | static uint32_t tpm_passthrough_get_size_from_buffer(const uint8_t *buf) | |
95 | { | |
96 | struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)buf; | |
97 | ||
98 | return be32_to_cpu(resp->len); | |
99 | } | |
100 | ||
bdee56f5 PB |
101 | /* |
102 | * Write an error message in the given output buffer. | |
103 | */ | |
104 | static void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len) | |
105 | { | |
106 | if (out_len >= sizeof(struct tpm_resp_hdr)) { | |
107 | struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)out; | |
108 | ||
109 | resp->tag = cpu_to_be16(TPM_TAG_RSP_COMMAND); | |
110 | resp->len = cpu_to_be32(sizeof(struct tpm_resp_hdr)); | |
111 | resp->errcode = cpu_to_be32(TPM_FAIL); | |
112 | } | |
113 | } | |
114 | ||
92dcc234 | 115 | static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt, |
4549a8b7 SB |
116 | const uint8_t *in, uint32_t in_len, |
117 | uint8_t *out, uint32_t out_len) | |
118 | { | |
119 | int ret; | |
120 | ||
92dcc234 SB |
121 | tpm_pt->tpm_op_canceled = false; |
122 | tpm_pt->tpm_executing = true; | |
123 | ||
124 | ret = tpm_passthrough_unix_write(tpm_pt->tpm_fd, in, in_len); | |
4549a8b7 | 125 | if (ret != in_len) { |
92dcc234 SB |
126 | if (!tpm_pt->tpm_op_canceled || |
127 | (tpm_pt->tpm_op_canceled && errno != ECANCELED)) { | |
128 | error_report("tpm_passthrough: error while transmitting data " | |
129 | "to TPM: %s (%i)\n", | |
130 | strerror(errno), errno); | |
131 | } | |
4549a8b7 SB |
132 | goto err_exit; |
133 | } | |
134 | ||
92dcc234 SB |
135 | tpm_pt->tpm_executing = false; |
136 | ||
137 | ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len); | |
4549a8b7 | 138 | if (ret < 0) { |
92dcc234 SB |
139 | if (!tpm_pt->tpm_op_canceled || |
140 | (tpm_pt->tpm_op_canceled && errno != ECANCELED)) { | |
141 | error_report("tpm_passthrough: error while reading data from " | |
142 | "TPM: %s (%i)\n", | |
143 | strerror(errno), errno); | |
144 | } | |
4549a8b7 SB |
145 | } else if (ret < sizeof(struct tpm_resp_hdr) || |
146 | tpm_passthrough_get_size_from_buffer(out) != ret) { | |
147 | ret = -1; | |
148 | error_report("tpm_passthrough: received invalid response " | |
149 | "packet from TPM\n"); | |
150 | } | |
151 | ||
152 | err_exit: | |
153 | if (ret < 0) { | |
154 | tpm_write_fatal_error_response(out, out_len); | |
155 | } | |
156 | ||
92dcc234 SB |
157 | tpm_pt->tpm_executing = false; |
158 | ||
4549a8b7 SB |
159 | return ret; |
160 | } | |
161 | ||
92dcc234 | 162 | static int tpm_passthrough_unix_transfer(TPMPassthruState *tpm_pt, |
4549a8b7 SB |
163 | const TPMLocality *locty_data) |
164 | { | |
92dcc234 | 165 | return tpm_passthrough_unix_tx_bufs(tpm_pt, |
4549a8b7 SB |
166 | locty_data->w_buffer.buffer, |
167 | locty_data->w_offset, | |
168 | locty_data->r_buffer.buffer, | |
169 | locty_data->r_buffer.size); | |
170 | } | |
171 | ||
172 | static void tpm_passthrough_worker_thread(gpointer data, | |
173 | gpointer user_data) | |
174 | { | |
175 | TPMPassthruThreadParams *thr_parms = user_data; | |
8f0605cc | 176 | TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(thr_parms->tb); |
4549a8b7 SB |
177 | TPMBackendCmd cmd = (TPMBackendCmd)data; |
178 | ||
179 | DPRINTF("tpm_passthrough: processing command type %d\n", cmd); | |
180 | ||
181 | switch (cmd) { | |
182 | case TPM_BACKEND_CMD_PROCESS_CMD: | |
92dcc234 | 183 | tpm_passthrough_unix_transfer(tpm_pt, |
4549a8b7 SB |
184 | thr_parms->tpm_state->locty_data); |
185 | ||
186 | thr_parms->recv_data_callback(thr_parms->tpm_state, | |
187 | thr_parms->tpm_state->locty_number); | |
188 | break; | |
189 | case TPM_BACKEND_CMD_INIT: | |
190 | case TPM_BACKEND_CMD_END: | |
191 | case TPM_BACKEND_CMD_TPM_RESET: | |
192 | /* nothing to do */ | |
193 | break; | |
194 | } | |
195 | } | |
196 | ||
197 | /* | |
198 | * Start the TPM (thread). If it had been started before, then terminate | |
199 | * and start it again. | |
200 | */ | |
201 | static int tpm_passthrough_startup_tpm(TPMBackend *tb) | |
202 | { | |
8f0605cc | 203 | TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); |
4549a8b7 SB |
204 | |
205 | /* terminate a running TPM */ | |
206 | tpm_backend_thread_end(&tpm_pt->tbt); | |
207 | ||
208 | tpm_backend_thread_create(&tpm_pt->tbt, | |
209 | tpm_passthrough_worker_thread, | |
8f0605cc | 210 | &tpm_pt->tpm_thread_params); |
4549a8b7 SB |
211 | |
212 | return 0; | |
213 | } | |
214 | ||
215 | static void tpm_passthrough_reset(TPMBackend *tb) | |
216 | { | |
8f0605cc | 217 | TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); |
4549a8b7 SB |
218 | |
219 | DPRINTF("tpm_passthrough: CALL TO TPM_RESET!\n"); | |
220 | ||
92dcc234 SB |
221 | tpm_passthrough_cancel_cmd(tb); |
222 | ||
4549a8b7 SB |
223 | tpm_backend_thread_end(&tpm_pt->tbt); |
224 | ||
225 | tpm_pt->had_startup_error = false; | |
226 | } | |
227 | ||
228 | static int tpm_passthrough_init(TPMBackend *tb, TPMState *s, | |
229 | TPMRecvDataCB *recv_data_cb) | |
230 | { | |
8f0605cc | 231 | TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); |
4549a8b7 SB |
232 | |
233 | tpm_pt->tpm_thread_params.tpm_state = s; | |
234 | tpm_pt->tpm_thread_params.recv_data_callback = recv_data_cb; | |
235 | tpm_pt->tpm_thread_params.tb = tb; | |
236 | ||
237 | return 0; | |
238 | } | |
239 | ||
240 | static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb) | |
241 | { | |
242 | return false; | |
243 | } | |
244 | ||
245 | static bool tpm_passthrough_get_startup_error(TPMBackend *tb) | |
246 | { | |
8f0605cc | 247 | TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); |
4549a8b7 SB |
248 | |
249 | return tpm_pt->had_startup_error; | |
250 | } | |
251 | ||
252 | static size_t tpm_passthrough_realloc_buffer(TPMSizedBuffer *sb) | |
253 | { | |
254 | size_t wanted_size = 4096; /* Linux tpm.c buffer size */ | |
255 | ||
256 | if (sb->size != wanted_size) { | |
257 | sb->buffer = g_realloc(sb->buffer, wanted_size); | |
258 | sb->size = wanted_size; | |
259 | } | |
260 | return sb->size; | |
261 | } | |
262 | ||
263 | static void tpm_passthrough_deliver_request(TPMBackend *tb) | |
264 | { | |
8f0605cc | 265 | TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); |
4549a8b7 SB |
266 | |
267 | tpm_backend_thread_deliver_request(&tpm_pt->tbt); | |
268 | } | |
269 | ||
270 | static void tpm_passthrough_cancel_cmd(TPMBackend *tb) | |
271 | { | |
8f0605cc | 272 | TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); |
92dcc234 SB |
273 | int n; |
274 | ||
275 | /* | |
276 | * As of Linux 3.7 the tpm_tis driver does not properly cancel | |
277 | * commands on all TPM manufacturers' TPMs. | |
278 | * Only cancel if we're busy so we don't cancel someone else's | |
279 | * command, e.g., a command executed on the host. | |
280 | */ | |
281 | if (tpm_pt->tpm_executing) { | |
282 | if (tpm_pt->cancel_fd >= 0) { | |
283 | n = write(tpm_pt->cancel_fd, "-", 1); | |
284 | if (n != 1) { | |
285 | error_report("Canceling TPM command failed: %s\n", | |
286 | strerror(errno)); | |
287 | } else { | |
288 | tpm_pt->tpm_op_canceled = true; | |
289 | } | |
290 | } else { | |
291 | error_report("Cannot cancel TPM command due to missing " | |
292 | "TPM sysfs cancel entry"); | |
293 | } | |
294 | } | |
4549a8b7 SB |
295 | } |
296 | ||
297 | static const char *tpm_passthrough_create_desc(void) | |
298 | { | |
299 | return "Passthrough TPM backend driver"; | |
300 | } | |
301 | ||
302 | /* | |
303 | * A basic test of a TPM device. We expect a well formatted response header | |
304 | * (error response is fine) within one second. | |
305 | */ | |
306 | static int tpm_passthrough_test_tpmdev(int fd) | |
307 | { | |
308 | struct tpm_req_hdr req = { | |
309 | .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND), | |
310 | .len = cpu_to_be32(sizeof(req)), | |
311 | .ordinal = cpu_to_be32(TPM_ORD_GetTicks), | |
312 | }; | |
313 | struct tpm_resp_hdr *resp; | |
314 | fd_set readfds; | |
315 | int n; | |
316 | struct timeval tv = { | |
317 | .tv_sec = 1, | |
318 | .tv_usec = 0, | |
319 | }; | |
320 | unsigned char buf[1024]; | |
321 | ||
322 | n = write(fd, &req, sizeof(req)); | |
323 | if (n < 0) { | |
324 | return errno; | |
325 | } | |
326 | if (n != sizeof(req)) { | |
327 | return EFAULT; | |
328 | } | |
329 | ||
330 | FD_ZERO(&readfds); | |
331 | FD_SET(fd, &readfds); | |
332 | ||
333 | /* wait for a second */ | |
334 | n = select(fd + 1, &readfds, NULL, NULL, &tv); | |
335 | if (n != 1) { | |
336 | return errno; | |
337 | } | |
338 | ||
339 | n = read(fd, &buf, sizeof(buf)); | |
340 | if (n < sizeof(struct tpm_resp_hdr)) { | |
341 | return EFAULT; | |
342 | } | |
343 | ||
344 | resp = (struct tpm_resp_hdr *)buf; | |
345 | /* check the header */ | |
346 | if (be16_to_cpu(resp->tag) != TPM_TAG_RSP_COMMAND || | |
347 | be32_to_cpu(resp->len) != n) { | |
348 | return EBADMSG; | |
349 | } | |
350 | ||
351 | return 0; | |
352 | } | |
353 | ||
92dcc234 SB |
354 | /* |
355 | * Check whether the given base path, e.g., /sys/class/misc/tpm0/device, | |
356 | * is the sysfs directory of a TPM. A TPM sysfs directory should be uniquely | |
357 | * recognizable by the file entries 'pcrs' and 'cancel'. | |
358 | * Upon success 'true' is returned and the basebath buffer has '/cancel' | |
359 | * appended. | |
360 | */ | |
361 | static bool tpm_passthrough_check_sysfs_cancel(char *basepath, size_t bufsz) | |
362 | { | |
363 | char path[PATH_MAX]; | |
364 | struct stat statbuf; | |
365 | ||
366 | snprintf(path, sizeof(path), "%s/pcrs", basepath); | |
367 | if (stat(path, &statbuf) == -1 || !S_ISREG(statbuf.st_mode)) { | |
368 | return false; | |
369 | } | |
370 | ||
371 | snprintf(path, sizeof(path), "%s/cancel", basepath); | |
372 | if (stat(path, &statbuf) == -1 || !S_ISREG(statbuf.st_mode)) { | |
373 | return false; | |
374 | } | |
375 | ||
376 | strncpy(basepath, path, bufsz); | |
377 | ||
378 | return true; | |
379 | } | |
380 | ||
381 | /* | |
382 | * Unless path or file descriptor set has been provided by user, | |
383 | * determine the sysfs cancel file following kernel documentation | |
384 | * in Documentation/ABI/stable/sysfs-class-tpm. | |
385 | */ | |
386 | static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb) | |
387 | { | |
388 | int fd = -1; | |
389 | unsigned int idx; | |
390 | DIR *pnp_dir; | |
391 | char path[PATH_MAX]; | |
392 | struct dirent entry, *result; | |
393 | int len; | |
394 | ||
395 | if (tb->cancel_path) { | |
396 | fd = qemu_open(tb->cancel_path, O_WRONLY); | |
397 | if (fd < 0) { | |
398 | error_report("Could not open TPM cancel path : %s", | |
399 | strerror(errno)); | |
400 | } | |
401 | return fd; | |
402 | } | |
403 | ||
404 | snprintf(path, sizeof(path), "/sys/class/misc"); | |
405 | pnp_dir = opendir(path); | |
406 | if (pnp_dir != NULL) { | |
407 | while (readdir_r(pnp_dir, &entry, &result) == 0 && | |
408 | result != NULL) { | |
409 | /* | |
410 | * only allow /sys/class/misc/tpm%u type of paths | |
411 | */ | |
412 | if (sscanf(entry.d_name, "tpm%u%n", &idx, &len) < 1 || | |
413 | len <= strlen("tpm") || | |
414 | len != strlen(entry.d_name)) { | |
415 | continue; | |
416 | } | |
417 | ||
418 | snprintf(path, sizeof(path), "/sys/class/misc/%s/device", | |
419 | entry.d_name); | |
420 | if (!tpm_passthrough_check_sysfs_cancel(path, sizeof(path))) { | |
421 | continue; | |
422 | } | |
423 | ||
424 | fd = qemu_open(path, O_WRONLY); | |
425 | break; | |
426 | } | |
427 | closedir(pnp_dir); | |
428 | } | |
429 | ||
430 | if (fd >= 0) { | |
431 | tb->cancel_path = g_strdup(path); | |
432 | } | |
433 | ||
434 | return fd; | |
435 | } | |
436 | ||
4549a8b7 SB |
437 | static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb) |
438 | { | |
8f0605cc | 439 | TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); |
4549a8b7 SB |
440 | const char *value; |
441 | ||
92dcc234 SB |
442 | value = qemu_opt_get(opts, "cancel-path"); |
443 | if (value) { | |
444 | tb->cancel_path = g_strdup(value); | |
445 | } | |
446 | ||
4549a8b7 SB |
447 | value = qemu_opt_get(opts, "path"); |
448 | if (!value) { | |
449 | value = TPM_PASSTHROUGH_DEFAULT_DEVICE; | |
450 | } | |
451 | ||
8f0605cc | 452 | tpm_pt->tpm_dev = g_strdup(value); |
4549a8b7 | 453 | |
8f0605cc | 454 | tb->path = g_strdup(tpm_pt->tpm_dev); |
4549a8b7 | 455 | |
8f0605cc SB |
456 | tpm_pt->tpm_fd = qemu_open(tpm_pt->tpm_dev, O_RDWR); |
457 | if (tpm_pt->tpm_fd < 0) { | |
4549a8b7 | 458 | error_report("Cannot access TPM device using '%s': %s\n", |
8f0605cc | 459 | tpm_pt->tpm_dev, strerror(errno)); |
4549a8b7 SB |
460 | goto err_free_parameters; |
461 | } | |
462 | ||
8f0605cc | 463 | if (tpm_passthrough_test_tpmdev(tpm_pt->tpm_fd)) { |
4549a8b7 | 464 | error_report("'%s' is not a TPM device.\n", |
8f0605cc | 465 | tpm_pt->tpm_dev); |
4549a8b7 SB |
466 | goto err_close_tpmdev; |
467 | } | |
468 | ||
469 | return 0; | |
470 | ||
471 | err_close_tpmdev: | |
8f0605cc SB |
472 | qemu_close(tpm_pt->tpm_fd); |
473 | tpm_pt->tpm_fd = -1; | |
4549a8b7 SB |
474 | |
475 | err_free_parameters: | |
476 | g_free(tb->path); | |
477 | tb->path = NULL; | |
478 | ||
8f0605cc SB |
479 | g_free(tpm_pt->tpm_dev); |
480 | tpm_pt->tpm_dev = NULL; | |
4549a8b7 SB |
481 | |
482 | return 1; | |
483 | } | |
484 | ||
485 | static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id) | |
486 | { | |
8f0605cc SB |
487 | Object *obj = object_new(TYPE_TPM_PASSTHROUGH); |
488 | TPMBackend *tb = TPM_BACKEND(obj); | |
489 | TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); | |
4549a8b7 | 490 | |
4549a8b7 SB |
491 | tb->id = g_strdup(id); |
492 | /* let frontend set the fe_model to proper value */ | |
493 | tb->fe_model = -1; | |
494 | ||
495 | tb->ops = &tpm_passthrough_driver; | |
496 | ||
497 | if (tpm_passthrough_handle_device_opts(opts, tb)) { | |
498 | goto err_exit; | |
499 | } | |
500 | ||
8f0605cc SB |
501 | tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tb); |
502 | if (tpm_pt->cancel_fd < 0) { | |
92dcc234 SB |
503 | goto err_exit; |
504 | } | |
505 | ||
4549a8b7 SB |
506 | return tb; |
507 | ||
508 | err_exit: | |
509 | g_free(tb->id); | |
4549a8b7 SB |
510 | |
511 | return NULL; | |
512 | } | |
513 | ||
514 | static void tpm_passthrough_destroy(TPMBackend *tb) | |
515 | { | |
8f0605cc | 516 | TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); |
4549a8b7 | 517 | |
92dcc234 SB |
518 | tpm_passthrough_cancel_cmd(tb); |
519 | ||
4549a8b7 SB |
520 | tpm_backend_thread_end(&tpm_pt->tbt); |
521 | ||
522 | qemu_close(tpm_pt->tpm_fd); | |
8f0605cc | 523 | qemu_close(tpm_pt->cancel_fd); |
4549a8b7 SB |
524 | |
525 | g_free(tb->id); | |
526 | g_free(tb->path); | |
92dcc234 | 527 | g_free(tb->cancel_path); |
8f0605cc | 528 | g_free(tpm_pt->tpm_dev); |
4549a8b7 SB |
529 | } |
530 | ||
bdee56f5 | 531 | static const TPMDriverOps tpm_passthrough_driver = { |
4549a8b7 SB |
532 | .type = TPM_TYPE_PASSTHROUGH, |
533 | .desc = tpm_passthrough_create_desc, | |
534 | .create = tpm_passthrough_create, | |
535 | .destroy = tpm_passthrough_destroy, | |
536 | .init = tpm_passthrough_init, | |
537 | .startup_tpm = tpm_passthrough_startup_tpm, | |
538 | .realloc_buffer = tpm_passthrough_realloc_buffer, | |
539 | .reset = tpm_passthrough_reset, | |
540 | .had_startup_error = tpm_passthrough_get_startup_error, | |
541 | .deliver_request = tpm_passthrough_deliver_request, | |
542 | .cancel_cmd = tpm_passthrough_cancel_cmd, | |
543 | .get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag, | |
544 | }; | |
545 | ||
8f0605cc SB |
546 | static void tpm_passthrough_inst_init(Object *obj) |
547 | { | |
548 | } | |
549 | ||
550 | static void tpm_passthrough_inst_finalize(Object *obj) | |
551 | { | |
552 | } | |
553 | ||
554 | static void tpm_passthrough_class_init(ObjectClass *klass, void *data) | |
555 | { | |
556 | TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass); | |
557 | ||
558 | tbc->ops = &tpm_passthrough_driver; | |
559 | } | |
560 | ||
561 | static const TypeInfo tpm_passthrough_info = { | |
562 | .name = TYPE_TPM_PASSTHROUGH, | |
563 | .parent = TYPE_TPM_BACKEND, | |
564 | .instance_size = sizeof(TPMPassthruState), | |
565 | .class_init = tpm_passthrough_class_init, | |
566 | .instance_init = tpm_passthrough_inst_init, | |
567 | .instance_finalize = tpm_passthrough_inst_finalize, | |
568 | }; | |
569 | ||
4549a8b7 SB |
570 | static void tpm_passthrough_register(void) |
571 | { | |
8f0605cc | 572 | type_register_static(&tpm_passthrough_info); |
4549a8b7 SB |
573 | tpm_register_driver(&tpm_passthrough_driver); |
574 | } | |
575 | ||
576 | type_init(tpm_passthrough_register) |