xref: /qemu/backends/tpm/tpm_passthrough.c (revision 8e36d6ca34243fdc9f48f4bdbe5fca2b19162bfa)
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 
25 #include <dirent.h>
26 
27 #include "qemu-common.h"
28 #include "qapi/error.h"
29 #include "qemu/sockets.h"
30 #include "sysemu/tpm_backend.h"
31 #include "tpm_int.h"
32 #include "hw/hw.h"
33 #include "hw/i386/pc.h"
34 #include "sysemu/tpm_backend_int.h"
35 #include "tpm_tis.h"
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 
47 #define TYPE_TPM_PASSTHROUGH "tpm-passthrough"
48 #define TPM_PASSTHROUGH(obj) \
49     OBJECT_CHECK(TPMPassthruState, (obj), TYPE_TPM_PASSTHROUGH)
50 
51 static const TPMDriverOps tpm_passthrough_driver;
52 
53 /* data structures */
54 typedef struct TPMPassthruThreadParams {
55     TPMState *tpm_state;
56 
57     TPMRecvDataCB *recv_data_callback;
58     TPMBackend *tb;
59 } TPMPassthruThreadParams;
60 
61 struct TPMPassthruState {
62     TPMBackend parent;
63 
64     TPMBackendThread tbt;
65 
66     TPMPassthruThreadParams tpm_thread_params;
67 
68     char *tpm_dev;
69     int tpm_fd;
70     bool tpm_executing;
71     bool tpm_op_canceled;
72     int cancel_fd;
73     bool had_startup_error;
74 };
75 
76 typedef struct TPMPassthruState TPMPassthruState;
77 
78 #define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0"
79 
80 /* functions */
81 
82 static void tpm_passthrough_cancel_cmd(TPMBackend *tb);
83 
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 
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 
115 static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
116                                         const uint8_t *in, uint32_t in_len,
117                                         uint8_t *out, uint32_t out_len)
118 {
119     int ret;
120 
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);
125     if (ret != in_len) {
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         }
132         goto err_exit;
133     }
134 
135     tpm_pt->tpm_executing = false;
136 
137     ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len);
138     if (ret < 0) {
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         }
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 
157     tpm_pt->tpm_executing = false;
158 
159     return ret;
160 }
161 
162 static int tpm_passthrough_unix_transfer(TPMPassthruState *tpm_pt,
163                                          const TPMLocality *locty_data)
164 {
165     return tpm_passthrough_unix_tx_bufs(tpm_pt,
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;
176     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(thr_parms->tb);
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:
183         tpm_passthrough_unix_transfer(tpm_pt,
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 {
203     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
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,
210                               &tpm_pt->tpm_thread_params);
211 
212     return 0;
213 }
214 
215 static void tpm_passthrough_reset(TPMBackend *tb)
216 {
217     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
218 
219     DPRINTF("tpm_passthrough: CALL TO TPM_RESET!\n");
220 
221     tpm_passthrough_cancel_cmd(tb);
222 
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 {
231     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
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 {
247     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
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 {
265     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
266 
267     tpm_backend_thread_deliver_request(&tpm_pt->tbt);
268 }
269 
270 static void tpm_passthrough_cancel_cmd(TPMBackend *tb)
271 {
272     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
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     }
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 
354 /*
355  * Unless path or file descriptor set has been provided by user,
356  * determine the sysfs cancel file following kernel documentation
357  * in Documentation/ABI/stable/sysfs-class-tpm.
358  * From /dev/tpm0 create /sys/class/misc/tpm0/device/cancel
359  */
360 static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb)
361 {
362     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
363     int fd = -1;
364     char *dev;
365     char path[PATH_MAX];
366 
367     if (tb->cancel_path) {
368         fd = qemu_open(tb->cancel_path, O_WRONLY);
369         if (fd < 0) {
370             error_report("Could not open TPM cancel path : %s",
371                          strerror(errno));
372         }
373         return fd;
374     }
375 
376     dev = strrchr(tpm_pt->tpm_dev, '/');
377     if (dev) {
378         dev++;
379         if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel",
380                      dev) < sizeof(path)) {
381             fd = qemu_open(path, O_WRONLY);
382             if (fd >= 0) {
383                 tb->cancel_path = g_strdup(path);
384             } else {
385                 error_report("tpm_passthrough: Could not open TPM cancel "
386                              "path %s : %s", path, strerror(errno));
387             }
388         }
389     } else {
390        error_report("tpm_passthrough: Bad TPM device path %s",
391                     tpm_pt->tpm_dev);
392     }
393 
394     return fd;
395 }
396 
397 static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
398 {
399     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
400     const char *value;
401 
402     value = qemu_opt_get(opts, "cancel-path");
403     if (value) {
404         tb->cancel_path = g_strdup(value);
405     }
406 
407     value = qemu_opt_get(opts, "path");
408     if (!value) {
409         value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
410     }
411 
412     tpm_pt->tpm_dev = g_strdup(value);
413 
414     tb->path = g_strdup(tpm_pt->tpm_dev);
415 
416     tpm_pt->tpm_fd = qemu_open(tpm_pt->tpm_dev, O_RDWR);
417     if (tpm_pt->tpm_fd < 0) {
418         error_report("Cannot access TPM device using '%s': %s\n",
419                      tpm_pt->tpm_dev, strerror(errno));
420         goto err_free_parameters;
421     }
422 
423     if (tpm_passthrough_test_tpmdev(tpm_pt->tpm_fd)) {
424         error_report("'%s' is not a TPM device.\n",
425                      tpm_pt->tpm_dev);
426         goto err_close_tpmdev;
427     }
428 
429     return 0;
430 
431  err_close_tpmdev:
432     qemu_close(tpm_pt->tpm_fd);
433     tpm_pt->tpm_fd = -1;
434 
435  err_free_parameters:
436     g_free(tb->path);
437     tb->path = NULL;
438 
439     g_free(tpm_pt->tpm_dev);
440     tpm_pt->tpm_dev = NULL;
441 
442     return 1;
443 }
444 
445 static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
446 {
447     Object *obj = object_new(TYPE_TPM_PASSTHROUGH);
448     TPMBackend *tb = TPM_BACKEND(obj);
449     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
450 
451     tb->id = g_strdup(id);
452     /* let frontend set the fe_model to proper value */
453     tb->fe_model = -1;
454 
455     tb->ops = &tpm_passthrough_driver;
456 
457     if (tpm_passthrough_handle_device_opts(opts, tb)) {
458         goto err_exit;
459     }
460 
461     tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tb);
462     if (tpm_pt->cancel_fd < 0) {
463         goto err_exit;
464     }
465 
466     return tb;
467 
468 err_exit:
469     g_free(tb->id);
470 
471     return NULL;
472 }
473 
474 static void tpm_passthrough_destroy(TPMBackend *tb)
475 {
476     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
477 
478     tpm_passthrough_cancel_cmd(tb);
479 
480     tpm_backend_thread_end(&tpm_pt->tbt);
481 
482     qemu_close(tpm_pt->tpm_fd);
483     qemu_close(tpm_pt->cancel_fd);
484 
485     g_free(tb->id);
486     g_free(tb->path);
487     g_free(tb->cancel_path);
488     g_free(tpm_pt->tpm_dev);
489 }
490 
491 static const TPMDriverOps tpm_passthrough_driver = {
492     .type                     = TPM_TYPE_PASSTHROUGH,
493     .desc                     = tpm_passthrough_create_desc,
494     .create                   = tpm_passthrough_create,
495     .destroy                  = tpm_passthrough_destroy,
496     .init                     = tpm_passthrough_init,
497     .startup_tpm              = tpm_passthrough_startup_tpm,
498     .realloc_buffer           = tpm_passthrough_realloc_buffer,
499     .reset                    = tpm_passthrough_reset,
500     .had_startup_error        = tpm_passthrough_get_startup_error,
501     .deliver_request          = tpm_passthrough_deliver_request,
502     .cancel_cmd               = tpm_passthrough_cancel_cmd,
503     .get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag,
504 };
505 
506 static void tpm_passthrough_inst_init(Object *obj)
507 {
508 }
509 
510 static void tpm_passthrough_inst_finalize(Object *obj)
511 {
512 }
513 
514 static void tpm_passthrough_class_init(ObjectClass *klass, void *data)
515 {
516     TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);
517 
518     tbc->ops = &tpm_passthrough_driver;
519 }
520 
521 static const TypeInfo tpm_passthrough_info = {
522     .name = TYPE_TPM_PASSTHROUGH,
523     .parent = TYPE_TPM_BACKEND,
524     .instance_size = sizeof(TPMPassthruState),
525     .class_init = tpm_passthrough_class_init,
526     .instance_init = tpm_passthrough_inst_init,
527     .instance_finalize = tpm_passthrough_inst_finalize,
528 };
529 
530 static void tpm_passthrough_register(void)
531 {
532     type_register_static(&tpm_passthrough_info);
533     tpm_register_driver(&tpm_passthrough_driver);
534 }
535 
536 type_init(tpm_passthrough_register)
537