1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25#include "qemu/osdep.h"
26#include "qemu-common.h"
27#include "qemu/error-report.h"
28#include "qemu/sockets.h"
29#include "sysemu/tpm_backend.h"
30#include "tpm_int.h"
31#include "hw/hw.h"
32#include "hw/i386/pc.h"
33#include "qapi/clone-visitor.h"
34#include "tpm_util.h"
35
36#define DEBUG_TPM 0
37
38#define DPRINTF(fmt, ...) do { \
39 if (DEBUG_TPM) { \
40 fprintf(stderr, fmt, ## __VA_ARGS__); \
41 } \
42} while (0);
43
44#define TYPE_TPM_PASSTHROUGH "tpm-passthrough"
45#define TPM_PASSTHROUGH(obj) \
46 OBJECT_CHECK(TPMPassthruState, (obj), TYPE_TPM_PASSTHROUGH)
47
48
49struct TPMPassthruState {
50 TPMBackend parent;
51
52 TPMPassthroughOptions *options;
53 const char *tpm_dev;
54 int tpm_fd;
55 bool tpm_executing;
56 bool tpm_op_canceled;
57 int cancel_fd;
58
59 TPMVersion tpm_version;
60};
61
62typedef struct TPMPassthruState TPMPassthruState;
63
64#define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0"
65
66
67
68static void tpm_passthrough_cancel_cmd(TPMBackend *tb);
69
70static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
71{
72 int ret;
73 reread:
74 ret = read(fd, buf, len);
75 if (ret < 0) {
76 if (errno != EINTR && errno != EAGAIN) {
77 return -1;
78 }
79 goto reread;
80 }
81 return ret;
82}
83static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
84 const uint8_t *in, uint32_t in_len,
85 uint8_t *out, uint32_t out_len,
86 bool *selftest_done)
87{
88 ssize_t ret;
89 bool is_selftest;
90 const struct tpm_resp_hdr *hdr;
91
92 tpm_pt->tpm_op_canceled = false;
93 tpm_pt->tpm_executing = true;
94 *selftest_done = false;
95
96 is_selftest = tpm_util_is_selftest(in, in_len);
97
98 ret = qemu_write_full(tpm_pt->tpm_fd, in, in_len);
99 if (ret != in_len) {
100 if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) {
101 error_report("tpm_passthrough: error while transmitting data "
102 "to TPM: %s (%i)",
103 strerror(errno), errno);
104 }
105 goto err_exit;
106 }
107
108 tpm_pt->tpm_executing = false;
109
110 ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len);
111 if (ret < 0) {
112 if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) {
113 error_report("tpm_passthrough: error while reading data from "
114 "TPM: %s (%i)",
115 strerror(errno), errno);
116 }
117 } else if (ret < sizeof(struct tpm_resp_hdr) ||
118 be32_to_cpu(((struct tpm_resp_hdr *)out)->len) != ret) {
119 ret = -1;
120 error_report("tpm_passthrough: received invalid response "
121 "packet from TPM");
122 }
123
124 if (is_selftest && (ret >= sizeof(struct tpm_resp_hdr))) {
125 hdr = (struct tpm_resp_hdr *)out;
126 *selftest_done = (be32_to_cpu(hdr->errcode) == 0);
127 }
128
129err_exit:
130 if (ret < 0) {
131 tpm_util_write_fatal_error_response(out, out_len);
132 }
133
134 tpm_pt->tpm_executing = false;
135
136 return ret;
137}
138
139static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd *cmd)
140{
141 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
142 TPMIfClass *tic = TPM_IF_GET_CLASS(tb->tpm_state);
143
144 DPRINTF("tpm_passthrough: processing command %p\n", cmd);
145
146 tpm_passthrough_unix_tx_bufs(tpm_pt, cmd->in, cmd->in_len,
147 cmd->out, cmd->out_len, &cmd->selftest_done);
148
149 tic->request_completed(TPM_IF(tb->tpm_state));
150}
151
152static void tpm_passthrough_reset(TPMBackend *tb)
153{
154 DPRINTF("tpm_passthrough: CALL TO TPM_RESET!\n");
155
156 tpm_passthrough_cancel_cmd(tb);
157}
158
159static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb)
160{
161 return false;
162}
163
164static int tpm_passthrough_reset_tpm_established_flag(TPMBackend *tb,
165 uint8_t locty)
166{
167
168 return 0;
169}
170
171static void tpm_passthrough_cancel_cmd(TPMBackend *tb)
172{
173 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
174 int n;
175
176
177
178
179
180
181
182 if (tpm_pt->tpm_executing) {
183 if (tpm_pt->cancel_fd >= 0) {
184 n = write(tpm_pt->cancel_fd, "-", 1);
185 if (n != 1) {
186 error_report("Canceling TPM command failed: %s",
187 strerror(errno));
188 } else {
189 tpm_pt->tpm_op_canceled = true;
190 }
191 } else {
192 error_report("Cannot cancel TPM command due to missing "
193 "TPM sysfs cancel entry");
194 }
195 }
196}
197
198static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb)
199{
200 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
201
202 return tpm_pt->tpm_version;
203}
204
205
206
207
208
209
210
211static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt)
212{
213 int fd = -1;
214 char *dev;
215 char path[PATH_MAX];
216
217 if (tpm_pt->options->cancel_path) {
218 fd = qemu_open(tpm_pt->options->cancel_path, O_WRONLY);
219 if (fd < 0) {
220 error_report("Could not open TPM cancel path : %s",
221 strerror(errno));
222 }
223 return fd;
224 }
225
226 dev = strrchr(tpm_pt->tpm_dev, '/');
227 if (dev) {
228 dev++;
229 if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel",
230 dev) < sizeof(path)) {
231 fd = qemu_open(path, O_WRONLY);
232 if (fd >= 0) {
233 tpm_pt->options->cancel_path = g_strdup(path);
234 } else {
235 error_report("tpm_passthrough: Could not open TPM cancel "
236 "path %s : %s", path, strerror(errno));
237 }
238 }
239 } else {
240 error_report("tpm_passthrough: Bad TPM device path %s",
241 tpm_pt->tpm_dev);
242 }
243
244 return fd;
245}
246
247static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
248{
249 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
250 const char *value;
251
252 value = qemu_opt_get(opts, "cancel-path");
253 if (value) {
254 tpm_pt->options->cancel_path = g_strdup(value);
255 tpm_pt->options->has_cancel_path = true;
256 }
257
258 value = qemu_opt_get(opts, "path");
259 if (value) {
260 tpm_pt->options->has_path = true;
261 tpm_pt->options->path = g_strdup(value);
262 }
263
264 tpm_pt->tpm_dev = value ? value : TPM_PASSTHROUGH_DEFAULT_DEVICE;
265 tpm_pt->tpm_fd = qemu_open(tpm_pt->tpm_dev, O_RDWR);
266 if (tpm_pt->tpm_fd < 0) {
267 error_report("Cannot access TPM device using '%s': %s",
268 tpm_pt->tpm_dev, strerror(errno));
269 goto err_free_parameters;
270 }
271
272 if (tpm_util_test_tpmdev(tpm_pt->tpm_fd, &tpm_pt->tpm_version)) {
273 error_report("'%s' is not a TPM device.",
274 tpm_pt->tpm_dev);
275 goto err_close_tpmdev;
276 }
277
278 return 0;
279
280 err_close_tpmdev:
281 qemu_close(tpm_pt->tpm_fd);
282 tpm_pt->tpm_fd = -1;
283
284 err_free_parameters:
285 qapi_free_TPMPassthroughOptions(tpm_pt->options);
286 tpm_pt->options = NULL;
287 tpm_pt->tpm_dev = NULL;
288
289 return 1;
290}
291
292static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
293{
294 Object *obj = object_new(TYPE_TPM_PASSTHROUGH);
295 TPMBackend *tb = TPM_BACKEND(obj);
296 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
297
298 tb->id = g_strdup(id);
299
300 if (tpm_passthrough_handle_device_opts(opts, tb)) {
301 goto err_exit;
302 }
303
304 tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tpm_pt);
305 if (tpm_pt->cancel_fd < 0) {
306 goto err_exit;
307 }
308
309 return tb;
310
311err_exit:
312 object_unref(obj);
313
314 return NULL;
315}
316
317static TpmTypeOptions *tpm_passthrough_get_tpm_options(TPMBackend *tb)
318{
319 TpmTypeOptions *options = g_new0(TpmTypeOptions, 1);
320
321 options->type = TPM_TYPE_OPTIONS_KIND_PASSTHROUGH;
322 options->u.passthrough.data = QAPI_CLONE(TPMPassthroughOptions,
323 TPM_PASSTHROUGH(tb)->options);
324
325 return options;
326}
327
328static const QemuOptDesc tpm_passthrough_cmdline_opts[] = {
329 TPM_STANDARD_CMDLINE_OPTS,
330 {
331 .name = "cancel-path",
332 .type = QEMU_OPT_STRING,
333 .help = "Sysfs file entry for canceling TPM commands",
334 },
335 {
336 .name = "path",
337 .type = QEMU_OPT_STRING,
338 .help = "Path to TPM device on the host",
339 },
340 { },
341};
342
343static void tpm_passthrough_inst_init(Object *obj)
344{
345 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(obj);
346
347 tpm_pt->options = g_new0(TPMPassthroughOptions, 1);
348 tpm_pt->tpm_fd = -1;
349 tpm_pt->cancel_fd = -1;
350}
351
352static void tpm_passthrough_inst_finalize(Object *obj)
353{
354 TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(obj);
355
356 tpm_passthrough_cancel_cmd(TPM_BACKEND(obj));
357
358 qemu_close(tpm_pt->tpm_fd);
359 qemu_close(tpm_pt->cancel_fd);
360 qapi_free_TPMPassthroughOptions(tpm_pt->options);
361}
362
363static void tpm_passthrough_class_init(ObjectClass *klass, void *data)
364{
365 TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);
366
367 tbc->type = TPM_TYPE_PASSTHROUGH;
368 tbc->opts = tpm_passthrough_cmdline_opts;
369 tbc->desc = "Passthrough TPM backend driver";
370 tbc->create = tpm_passthrough_create;
371 tbc->reset = tpm_passthrough_reset;
372 tbc->cancel_cmd = tpm_passthrough_cancel_cmd;
373 tbc->get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag;
374 tbc->reset_tpm_established_flag =
375 tpm_passthrough_reset_tpm_established_flag;
376 tbc->get_tpm_version = tpm_passthrough_get_tpm_version;
377 tbc->get_tpm_options = tpm_passthrough_get_tpm_options;
378 tbc->handle_request = tpm_passthrough_handle_request;
379}
380
381static const TypeInfo tpm_passthrough_info = {
382 .name = TYPE_TPM_PASSTHROUGH,
383 .parent = TYPE_TPM_BACKEND,
384 .instance_size = sizeof(TPMPassthruState),
385 .class_init = tpm_passthrough_class_init,
386 .instance_init = tpm_passthrough_inst_init,
387 .instance_finalize = tpm_passthrough_inst_finalize,
388};
389
390static void tpm_passthrough_register(void)
391{
392 type_register_static(&tpm_passthrough_info);
393}
394
395type_init(tpm_passthrough_register)
396