xref: /qemu/backends/tpm/tpm_passthrough.c (revision ced29396)
1ca64b086SPhilippe Mathieu-Daudé /*
2ca64b086SPhilippe Mathieu-Daudé  *  passthrough TPM driver
3ca64b086SPhilippe Mathieu-Daudé  *
4ca64b086SPhilippe Mathieu-Daudé  *  Copyright (c) 2010 - 2013 IBM Corporation
5ca64b086SPhilippe Mathieu-Daudé  *  Authors:
6ca64b086SPhilippe Mathieu-Daudé  *    Stefan Berger <stefanb@us.ibm.com>
7ca64b086SPhilippe Mathieu-Daudé  *
8ca64b086SPhilippe Mathieu-Daudé  *  Copyright (C) 2011 IAIK, Graz University of Technology
9ca64b086SPhilippe Mathieu-Daudé  *    Author: Andreas Niederl
10ca64b086SPhilippe Mathieu-Daudé  *
11ca64b086SPhilippe Mathieu-Daudé  * This library is free software; you can redistribute it and/or
12ca64b086SPhilippe Mathieu-Daudé  * modify it under the terms of the GNU Lesser General Public
13ca64b086SPhilippe Mathieu-Daudé  * License as published by the Free Software Foundation; either
14eac2fce9SChetan Pant  * version 2.1 of the License, or (at your option) any later version.
15ca64b086SPhilippe Mathieu-Daudé  *
16ca64b086SPhilippe Mathieu-Daudé  * This library is distributed in the hope that it will be useful,
17ca64b086SPhilippe Mathieu-Daudé  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18ca64b086SPhilippe Mathieu-Daudé  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19ca64b086SPhilippe Mathieu-Daudé  * Lesser General Public License for more details.
20ca64b086SPhilippe Mathieu-Daudé  *
21ca64b086SPhilippe Mathieu-Daudé  * You should have received a copy of the GNU Lesser General Public
22ca64b086SPhilippe Mathieu-Daudé  * License along with this library; if not, see <http://www.gnu.org/licenses/>
23ca64b086SPhilippe Mathieu-Daudé  */
24ca64b086SPhilippe Mathieu-Daudé 
25ca64b086SPhilippe Mathieu-Daudé #include "qemu/osdep.h"
26ca64b086SPhilippe Mathieu-Daudé #include "qemu/error-report.h"
27ca64b086SPhilippe Mathieu-Daudé #include "qemu/module.h"
28ca64b086SPhilippe Mathieu-Daudé #include "qemu/sockets.h"
29ca64b086SPhilippe Mathieu-Daudé #include "sysemu/tpm_backend.h"
30ca64b086SPhilippe Mathieu-Daudé #include "sysemu/tpm_util.h"
31ca64b086SPhilippe Mathieu-Daudé #include "tpm_int.h"
32ca64b086SPhilippe Mathieu-Daudé #include "qapi/clone-visitor.h"
33ca64b086SPhilippe Mathieu-Daudé #include "qapi/qapi-visit-tpm.h"
34ca64b086SPhilippe Mathieu-Daudé #include "trace.h"
35db1015e9SEduardo Habkost #include "qom/object.h"
36ca64b086SPhilippe Mathieu-Daudé 
37ca64b086SPhilippe Mathieu-Daudé #define TYPE_TPM_PASSTHROUGH "tpm-passthrough"
388063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(TPMPassthruState, TPM_PASSTHROUGH)
39ca64b086SPhilippe Mathieu-Daudé 
40ca64b086SPhilippe Mathieu-Daudé /* data structures */
41ca64b086SPhilippe Mathieu-Daudé struct TPMPassthruState {
42ca64b086SPhilippe Mathieu-Daudé     TPMBackend parent;
43ca64b086SPhilippe Mathieu-Daudé 
44ca64b086SPhilippe Mathieu-Daudé     TPMPassthroughOptions *options;
45ca64b086SPhilippe Mathieu-Daudé     const char *tpm_dev;
46ca64b086SPhilippe Mathieu-Daudé     int tpm_fd;
47ca64b086SPhilippe Mathieu-Daudé     bool tpm_executing;
48ca64b086SPhilippe Mathieu-Daudé     bool tpm_op_canceled;
49ca64b086SPhilippe Mathieu-Daudé     int cancel_fd;
50ca64b086SPhilippe Mathieu-Daudé 
51ca64b086SPhilippe Mathieu-Daudé     TPMVersion tpm_version;
52ca64b086SPhilippe Mathieu-Daudé     size_t tpm_buffersize;
53ca64b086SPhilippe Mathieu-Daudé };
54ca64b086SPhilippe Mathieu-Daudé 
55ca64b086SPhilippe Mathieu-Daudé 
56ca64b086SPhilippe Mathieu-Daudé #define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0"
57ca64b086SPhilippe Mathieu-Daudé 
58ca64b086SPhilippe Mathieu-Daudé /* functions */
59ca64b086SPhilippe Mathieu-Daudé 
60ca64b086SPhilippe Mathieu-Daudé static void tpm_passthrough_cancel_cmd(TPMBackend *tb);
61ca64b086SPhilippe Mathieu-Daudé 
tpm_passthrough_unix_read(int fd,uint8_t * buf,uint32_t len)62ca64b086SPhilippe Mathieu-Daudé static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
63ca64b086SPhilippe Mathieu-Daudé {
64ca64b086SPhilippe Mathieu-Daudé     int ret;
65ca64b086SPhilippe Mathieu-Daudé  reread:
66ca64b086SPhilippe Mathieu-Daudé     ret = read(fd, buf, len);
67ca64b086SPhilippe Mathieu-Daudé     if (ret < 0) {
68ca64b086SPhilippe Mathieu-Daudé         if (errno != EINTR && errno != EAGAIN) {
69ca64b086SPhilippe Mathieu-Daudé             return -1;
70ca64b086SPhilippe Mathieu-Daudé         }
71ca64b086SPhilippe Mathieu-Daudé         goto reread;
72ca64b086SPhilippe Mathieu-Daudé     }
73ca64b086SPhilippe Mathieu-Daudé     return ret;
74ca64b086SPhilippe Mathieu-Daudé }
75ca64b086SPhilippe Mathieu-Daudé 
tpm_passthrough_unix_tx_bufs(TPMPassthruState * tpm_pt,const uint8_t * in,uint32_t in_len,uint8_t * out,uint32_t out_len,bool * selftest_done,Error ** errp)76ca64b086SPhilippe Mathieu-Daudé static void tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
77ca64b086SPhilippe Mathieu-Daudé                                          const uint8_t *in, uint32_t in_len,
78ca64b086SPhilippe Mathieu-Daudé                                          uint8_t *out, uint32_t out_len,
79ca64b086SPhilippe Mathieu-Daudé                                          bool *selftest_done, Error **errp)
80ca64b086SPhilippe Mathieu-Daudé {
81ca64b086SPhilippe Mathieu-Daudé     ssize_t ret;
82ca64b086SPhilippe Mathieu-Daudé     bool is_selftest;
83ca64b086SPhilippe Mathieu-Daudé 
84ca64b086SPhilippe Mathieu-Daudé     /* FIXME: protect shared variables or use other sync mechanism */
85ca64b086SPhilippe Mathieu-Daudé     tpm_pt->tpm_op_canceled = false;
86ca64b086SPhilippe Mathieu-Daudé     tpm_pt->tpm_executing = true;
87ca64b086SPhilippe Mathieu-Daudé     *selftest_done = false;
88ca64b086SPhilippe Mathieu-Daudé 
89ca64b086SPhilippe Mathieu-Daudé     is_selftest = tpm_util_is_selftest(in, in_len);
90ca64b086SPhilippe Mathieu-Daudé 
91ca64b086SPhilippe Mathieu-Daudé     ret = qemu_write_full(tpm_pt->tpm_fd, in, in_len);
92ca64b086SPhilippe Mathieu-Daudé     if (ret != in_len) {
93ca64b086SPhilippe Mathieu-Daudé         if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) {
94ca64b086SPhilippe Mathieu-Daudé             error_setg_errno(errp, errno, "tpm_passthrough: error while "
95ca64b086SPhilippe Mathieu-Daudé                              "transmitting data to TPM");
96ca64b086SPhilippe Mathieu-Daudé         }
97ca64b086SPhilippe Mathieu-Daudé         goto err_exit;
98ca64b086SPhilippe Mathieu-Daudé     }
99ca64b086SPhilippe Mathieu-Daudé 
100ca64b086SPhilippe Mathieu-Daudé     tpm_pt->tpm_executing = false;
101ca64b086SPhilippe Mathieu-Daudé 
102ca64b086SPhilippe Mathieu-Daudé     ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len);
103ca64b086SPhilippe Mathieu-Daudé     if (ret < 0) {
104ca64b086SPhilippe Mathieu-Daudé         if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) {
105ca64b086SPhilippe Mathieu-Daudé             error_setg_errno(errp, errno, "tpm_passthrough: error while "
106ca64b086SPhilippe Mathieu-Daudé                              "reading data from TPM");
107ca64b086SPhilippe Mathieu-Daudé         }
108ca64b086SPhilippe Mathieu-Daudé     } else if (ret < sizeof(struct tpm_resp_hdr) ||
109ca64b086SPhilippe Mathieu-Daudé                tpm_cmd_get_size(out) != ret) {
110ca64b086SPhilippe Mathieu-Daudé         ret = -1;
111ca64b086SPhilippe Mathieu-Daudé         error_setg_errno(errp, errno, "tpm_passthrough: received invalid "
112ca64b086SPhilippe Mathieu-Daudé                      "response packet from TPM");
113ca64b086SPhilippe Mathieu-Daudé     }
114ca64b086SPhilippe Mathieu-Daudé 
115ca64b086SPhilippe Mathieu-Daudé     if (is_selftest && (ret >= sizeof(struct tpm_resp_hdr))) {
116ca64b086SPhilippe Mathieu-Daudé         *selftest_done = tpm_cmd_get_errcode(out) == 0;
117ca64b086SPhilippe Mathieu-Daudé     }
118ca64b086SPhilippe Mathieu-Daudé 
119ca64b086SPhilippe Mathieu-Daudé err_exit:
120ca64b086SPhilippe Mathieu-Daudé     if (ret < 0) {
121ca64b086SPhilippe Mathieu-Daudé         tpm_util_write_fatal_error_response(out, out_len);
122ca64b086SPhilippe Mathieu-Daudé     }
123ca64b086SPhilippe Mathieu-Daudé 
124ca64b086SPhilippe Mathieu-Daudé     tpm_pt->tpm_executing = false;
125ca64b086SPhilippe Mathieu-Daudé }
126ca64b086SPhilippe Mathieu-Daudé 
tpm_passthrough_handle_request(TPMBackend * tb,TPMBackendCmd * cmd,Error ** errp)127ca64b086SPhilippe Mathieu-Daudé static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd *cmd,
128ca64b086SPhilippe Mathieu-Daudé                                            Error **errp)
129ca64b086SPhilippe Mathieu-Daudé {
130ca64b086SPhilippe Mathieu-Daudé     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
131ca64b086SPhilippe Mathieu-Daudé 
132ca64b086SPhilippe Mathieu-Daudé     trace_tpm_passthrough_handle_request(cmd);
133ca64b086SPhilippe Mathieu-Daudé 
134ca64b086SPhilippe Mathieu-Daudé     tpm_passthrough_unix_tx_bufs(tpm_pt, cmd->in, cmd->in_len,
135ca64b086SPhilippe Mathieu-Daudé                                  cmd->out, cmd->out_len, &cmd->selftest_done,
136ca64b086SPhilippe Mathieu-Daudé                                  errp);
137ca64b086SPhilippe Mathieu-Daudé }
138ca64b086SPhilippe Mathieu-Daudé 
tpm_passthrough_reset(TPMBackend * tb)139ca64b086SPhilippe Mathieu-Daudé static void tpm_passthrough_reset(TPMBackend *tb)
140ca64b086SPhilippe Mathieu-Daudé {
141ca64b086SPhilippe Mathieu-Daudé     trace_tpm_passthrough_reset();
142ca64b086SPhilippe Mathieu-Daudé 
143ca64b086SPhilippe Mathieu-Daudé     tpm_passthrough_cancel_cmd(tb);
144ca64b086SPhilippe Mathieu-Daudé }
145ca64b086SPhilippe Mathieu-Daudé 
tpm_passthrough_get_tpm_established_flag(TPMBackend * tb)146ca64b086SPhilippe Mathieu-Daudé static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb)
147ca64b086SPhilippe Mathieu-Daudé {
148ca64b086SPhilippe Mathieu-Daudé     return false;
149ca64b086SPhilippe Mathieu-Daudé }
150ca64b086SPhilippe Mathieu-Daudé 
tpm_passthrough_reset_tpm_established_flag(TPMBackend * tb,uint8_t locty)151ca64b086SPhilippe Mathieu-Daudé static int tpm_passthrough_reset_tpm_established_flag(TPMBackend *tb,
152ca64b086SPhilippe Mathieu-Daudé                                                       uint8_t locty)
153ca64b086SPhilippe Mathieu-Daudé {
154ca64b086SPhilippe Mathieu-Daudé     /* only a TPM 2.0 will support this */
155ca64b086SPhilippe Mathieu-Daudé     return 0;
156ca64b086SPhilippe Mathieu-Daudé }
157ca64b086SPhilippe Mathieu-Daudé 
tpm_passthrough_cancel_cmd(TPMBackend * tb)158ca64b086SPhilippe Mathieu-Daudé static void tpm_passthrough_cancel_cmd(TPMBackend *tb)
159ca64b086SPhilippe Mathieu-Daudé {
160ca64b086SPhilippe Mathieu-Daudé     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
161ca64b086SPhilippe Mathieu-Daudé     int n;
162ca64b086SPhilippe Mathieu-Daudé 
163ca64b086SPhilippe Mathieu-Daudé     /*
164ca64b086SPhilippe Mathieu-Daudé      * As of Linux 3.7 the tpm_tis driver does not properly cancel
165ca64b086SPhilippe Mathieu-Daudé      * commands on all TPM manufacturers' TPMs.
166ca64b086SPhilippe Mathieu-Daudé      * Only cancel if we're busy so we don't cancel someone else's
167ca64b086SPhilippe Mathieu-Daudé      * command, e.g., a command executed on the host.
168ca64b086SPhilippe Mathieu-Daudé      */
169ca64b086SPhilippe Mathieu-Daudé     if (tpm_pt->tpm_executing) {
170ca64b086SPhilippe Mathieu-Daudé         if (tpm_pt->cancel_fd >= 0) {
171ca64b086SPhilippe Mathieu-Daudé             tpm_pt->tpm_op_canceled = true;
172ca64b086SPhilippe Mathieu-Daudé             n = write(tpm_pt->cancel_fd, "-", 1);
173ca64b086SPhilippe Mathieu-Daudé             if (n != 1) {
174ca64b086SPhilippe Mathieu-Daudé                 error_report("Canceling TPM command failed: %s",
175ca64b086SPhilippe Mathieu-Daudé                              strerror(errno));
176ca64b086SPhilippe Mathieu-Daudé             }
177ca64b086SPhilippe Mathieu-Daudé         } else {
178ca64b086SPhilippe Mathieu-Daudé             error_report("Cannot cancel TPM command due to missing "
179ca64b086SPhilippe Mathieu-Daudé                          "TPM sysfs cancel entry");
180ca64b086SPhilippe Mathieu-Daudé         }
181ca64b086SPhilippe Mathieu-Daudé     }
182ca64b086SPhilippe Mathieu-Daudé }
183ca64b086SPhilippe Mathieu-Daudé 
tpm_passthrough_get_tpm_version(TPMBackend * tb)184ca64b086SPhilippe Mathieu-Daudé static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb)
185ca64b086SPhilippe Mathieu-Daudé {
186ca64b086SPhilippe Mathieu-Daudé     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
187ca64b086SPhilippe Mathieu-Daudé 
188ca64b086SPhilippe Mathieu-Daudé     return tpm_pt->tpm_version;
189ca64b086SPhilippe Mathieu-Daudé }
190ca64b086SPhilippe Mathieu-Daudé 
tpm_passthrough_get_buffer_size(TPMBackend * tb)191ca64b086SPhilippe Mathieu-Daudé static size_t tpm_passthrough_get_buffer_size(TPMBackend *tb)
192ca64b086SPhilippe Mathieu-Daudé {
193ca64b086SPhilippe Mathieu-Daudé     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
194ca64b086SPhilippe Mathieu-Daudé     int ret;
195ca64b086SPhilippe Mathieu-Daudé 
196ca64b086SPhilippe Mathieu-Daudé     ret = tpm_util_get_buffer_size(tpm_pt->tpm_fd, tpm_pt->tpm_version,
197ca64b086SPhilippe Mathieu-Daudé                                    &tpm_pt->tpm_buffersize);
198ca64b086SPhilippe Mathieu-Daudé     if (ret < 0) {
199ca64b086SPhilippe Mathieu-Daudé         tpm_pt->tpm_buffersize = 4096;
200ca64b086SPhilippe Mathieu-Daudé     }
201ca64b086SPhilippe Mathieu-Daudé     return tpm_pt->tpm_buffersize;
202ca64b086SPhilippe Mathieu-Daudé }
203ca64b086SPhilippe Mathieu-Daudé 
204ca64b086SPhilippe Mathieu-Daudé /*
205ca64b086SPhilippe Mathieu-Daudé  * Unless path or file descriptor set has been provided by user,
206ca64b086SPhilippe Mathieu-Daudé  * determine the sysfs cancel file following kernel documentation
207ca64b086SPhilippe Mathieu-Daudé  * in Documentation/ABI/stable/sysfs-class-tpm.
208ca64b086SPhilippe Mathieu-Daudé  * From /dev/tpm0 create /sys/class/tpm/tpm0/device/cancel
209ca64b086SPhilippe Mathieu-Daudé  * before 4.0: /sys/class/misc/tpm0/device/cancel
210ca64b086SPhilippe Mathieu-Daudé  */
tpm_passthrough_open_sysfs_cancel(TPMPassthruState * tpm_pt)211ca64b086SPhilippe Mathieu-Daudé static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt)
212ca64b086SPhilippe Mathieu-Daudé {
213ca64b086SPhilippe Mathieu-Daudé     int fd = -1;
214ca64b086SPhilippe Mathieu-Daudé     char *dev;
215ca64b086SPhilippe Mathieu-Daudé     char path[PATH_MAX];
216ca64b086SPhilippe Mathieu-Daudé 
217ca64b086SPhilippe Mathieu-Daudé     if (tpm_pt->options->cancel_path) {
218448058aaSDaniel P. Berrangé         fd = qemu_open_old(tpm_pt->options->cancel_path, O_WRONLY);
219ca64b086SPhilippe Mathieu-Daudé         if (fd < 0) {
220ca64b086SPhilippe Mathieu-Daudé             error_report("tpm_passthrough: Could not open TPM cancel path: %s",
221ca64b086SPhilippe Mathieu-Daudé                          strerror(errno));
222ca64b086SPhilippe Mathieu-Daudé         }
223ca64b086SPhilippe Mathieu-Daudé         return fd;
224ca64b086SPhilippe Mathieu-Daudé     }
225ca64b086SPhilippe Mathieu-Daudé 
226ca64b086SPhilippe Mathieu-Daudé     dev = strrchr(tpm_pt->tpm_dev, '/');
227ca64b086SPhilippe Mathieu-Daudé     if (!dev) {
228ca64b086SPhilippe Mathieu-Daudé         error_report("tpm_passthrough: Bad TPM device path %s",
229ca64b086SPhilippe Mathieu-Daudé                      tpm_pt->tpm_dev);
230ca64b086SPhilippe Mathieu-Daudé         return -1;
231ca64b086SPhilippe Mathieu-Daudé     }
232ca64b086SPhilippe Mathieu-Daudé 
233ca64b086SPhilippe Mathieu-Daudé     dev++;
234ca64b086SPhilippe Mathieu-Daudé     if (snprintf(path, sizeof(path), "/sys/class/tpm/%s/device/cancel",
235ca64b086SPhilippe Mathieu-Daudé                  dev) < sizeof(path)) {
236448058aaSDaniel P. Berrangé         fd = qemu_open_old(path, O_WRONLY);
237ca64b086SPhilippe Mathieu-Daudé         if (fd < 0) {
238ca64b086SPhilippe Mathieu-Daudé             if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel",
239ca64b086SPhilippe Mathieu-Daudé                          dev) < sizeof(path)) {
240448058aaSDaniel P. Berrangé                 fd = qemu_open_old(path, O_WRONLY);
241ca64b086SPhilippe Mathieu-Daudé             }
242ca64b086SPhilippe Mathieu-Daudé         }
243ca64b086SPhilippe Mathieu-Daudé     }
244ca64b086SPhilippe Mathieu-Daudé 
245ca64b086SPhilippe Mathieu-Daudé     if (fd < 0) {
246ca64b086SPhilippe Mathieu-Daudé         error_report("tpm_passthrough: Could not guess TPM cancel path");
247ca64b086SPhilippe Mathieu-Daudé     } else {
248ca64b086SPhilippe Mathieu-Daudé         tpm_pt->options->cancel_path = g_strdup(path);
249ca64b086SPhilippe Mathieu-Daudé     }
250ca64b086SPhilippe Mathieu-Daudé 
251ca64b086SPhilippe Mathieu-Daudé     return fd;
252ca64b086SPhilippe Mathieu-Daudé }
253ca64b086SPhilippe Mathieu-Daudé 
254ca64b086SPhilippe Mathieu-Daudé static int
tpm_passthrough_handle_device_opts(TPMPassthruState * tpm_pt,QemuOpts * opts)255ca64b086SPhilippe Mathieu-Daudé tpm_passthrough_handle_device_opts(TPMPassthruState *tpm_pt, QemuOpts *opts)
256ca64b086SPhilippe Mathieu-Daudé {
257ca64b086SPhilippe Mathieu-Daudé     const char *value;
258ca64b086SPhilippe Mathieu-Daudé 
259ca64b086SPhilippe Mathieu-Daudé     value = qemu_opt_get(opts, "cancel-path");
260ca64b086SPhilippe Mathieu-Daudé     if (value) {
261ca64b086SPhilippe Mathieu-Daudé         tpm_pt->options->cancel_path = g_strdup(value);
262ca64b086SPhilippe Mathieu-Daudé     }
263ca64b086SPhilippe Mathieu-Daudé 
264ca64b086SPhilippe Mathieu-Daudé     value = qemu_opt_get(opts, "path");
265ca64b086SPhilippe Mathieu-Daudé     if (value) {
266ca64b086SPhilippe Mathieu-Daudé         tpm_pt->options->path = g_strdup(value);
267ca64b086SPhilippe Mathieu-Daudé     }
268ca64b086SPhilippe Mathieu-Daudé 
269ca64b086SPhilippe Mathieu-Daudé     tpm_pt->tpm_dev = value ? value : TPM_PASSTHROUGH_DEFAULT_DEVICE;
270448058aaSDaniel P. Berrangé     tpm_pt->tpm_fd = qemu_open_old(tpm_pt->tpm_dev, O_RDWR);
271ca64b086SPhilippe Mathieu-Daudé     if (tpm_pt->tpm_fd < 0) {
272ca64b086SPhilippe Mathieu-Daudé         error_report("Cannot access TPM device using '%s': %s",
273ca64b086SPhilippe Mathieu-Daudé                      tpm_pt->tpm_dev, strerror(errno));
274ca64b086SPhilippe Mathieu-Daudé         return -1;
275ca64b086SPhilippe Mathieu-Daudé     }
276ca64b086SPhilippe Mathieu-Daudé 
277ca64b086SPhilippe Mathieu-Daudé     if (tpm_util_test_tpmdev(tpm_pt->tpm_fd, &tpm_pt->tpm_version)) {
278ca64b086SPhilippe Mathieu-Daudé         error_report("'%s' is not a TPM device.",
279ca64b086SPhilippe Mathieu-Daudé                      tpm_pt->tpm_dev);
280ca64b086SPhilippe Mathieu-Daudé         return -1;
281ca64b086SPhilippe Mathieu-Daudé     }
282ca64b086SPhilippe Mathieu-Daudé 
283ca64b086SPhilippe Mathieu-Daudé     tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tpm_pt);
284ca64b086SPhilippe Mathieu-Daudé     if (tpm_pt->cancel_fd < 0) {
285ca64b086SPhilippe Mathieu-Daudé         return -1;
286ca64b086SPhilippe Mathieu-Daudé     }
287ca64b086SPhilippe Mathieu-Daudé 
288ca64b086SPhilippe Mathieu-Daudé     return 0;
289ca64b086SPhilippe Mathieu-Daudé }
290ca64b086SPhilippe Mathieu-Daudé 
tpm_passthrough_create(QemuOpts * opts)291ca64b086SPhilippe Mathieu-Daudé static TPMBackend *tpm_passthrough_create(QemuOpts *opts)
292ca64b086SPhilippe Mathieu-Daudé {
293ca64b086SPhilippe Mathieu-Daudé     Object *obj = object_new(TYPE_TPM_PASSTHROUGH);
294ca64b086SPhilippe Mathieu-Daudé 
295ca64b086SPhilippe Mathieu-Daudé     if (tpm_passthrough_handle_device_opts(TPM_PASSTHROUGH(obj), opts)) {
296ca64b086SPhilippe Mathieu-Daudé         object_unref(obj);
297ca64b086SPhilippe Mathieu-Daudé         return NULL;
298ca64b086SPhilippe Mathieu-Daudé     }
299ca64b086SPhilippe Mathieu-Daudé 
300ca64b086SPhilippe Mathieu-Daudé     return TPM_BACKEND(obj);
301ca64b086SPhilippe Mathieu-Daudé }
302ca64b086SPhilippe Mathieu-Daudé 
tpm_passthrough_startup_tpm(TPMBackend * tb,size_t buffersize)303ca64b086SPhilippe Mathieu-Daudé static int tpm_passthrough_startup_tpm(TPMBackend *tb, size_t buffersize)
304ca64b086SPhilippe Mathieu-Daudé {
305ca64b086SPhilippe Mathieu-Daudé     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
306ca64b086SPhilippe Mathieu-Daudé 
307ca64b086SPhilippe Mathieu-Daudé     if (buffersize && buffersize < tpm_pt->tpm_buffersize) {
308ca64b086SPhilippe Mathieu-Daudé         error_report("Requested buffer size of %zu is smaller than host TPM's "
309ca64b086SPhilippe Mathieu-Daudé                      "fixed buffer size of %zu",
310ca64b086SPhilippe Mathieu-Daudé                      buffersize, tpm_pt->tpm_buffersize);
311ca64b086SPhilippe Mathieu-Daudé         return -1;
312ca64b086SPhilippe Mathieu-Daudé     }
313ca64b086SPhilippe Mathieu-Daudé 
314ca64b086SPhilippe Mathieu-Daudé     return 0;
315ca64b086SPhilippe Mathieu-Daudé }
316ca64b086SPhilippe Mathieu-Daudé 
tpm_passthrough_get_tpm_options(TPMBackend * tb)317ca64b086SPhilippe Mathieu-Daudé static TpmTypeOptions *tpm_passthrough_get_tpm_options(TPMBackend *tb)
318ca64b086SPhilippe Mathieu-Daudé {
319ca64b086SPhilippe Mathieu-Daudé     TpmTypeOptions *options = g_new0(TpmTypeOptions, 1);
320ca64b086SPhilippe Mathieu-Daudé 
321*39dc3e4aSMarkus Armbruster     options->type = TPM_TYPE_PASSTHROUGH;
322ca64b086SPhilippe Mathieu-Daudé     options->u.passthrough.data = QAPI_CLONE(TPMPassthroughOptions,
323ca64b086SPhilippe Mathieu-Daudé                                              TPM_PASSTHROUGH(tb)->options);
324ca64b086SPhilippe Mathieu-Daudé 
325ca64b086SPhilippe Mathieu-Daudé     return options;
326ca64b086SPhilippe Mathieu-Daudé }
327ca64b086SPhilippe Mathieu-Daudé 
328ca64b086SPhilippe Mathieu-Daudé static const QemuOptDesc tpm_passthrough_cmdline_opts[] = {
329ca64b086SPhilippe Mathieu-Daudé     TPM_STANDARD_CMDLINE_OPTS,
330ca64b086SPhilippe Mathieu-Daudé     {
331ca64b086SPhilippe Mathieu-Daudé         .name = "cancel-path",
332ca64b086SPhilippe Mathieu-Daudé         .type = QEMU_OPT_STRING,
333ca64b086SPhilippe Mathieu-Daudé         .help = "Sysfs file entry for canceling TPM commands",
334ca64b086SPhilippe Mathieu-Daudé     },
335ca64b086SPhilippe Mathieu-Daudé     {
336ca64b086SPhilippe Mathieu-Daudé         .name = "path",
337ca64b086SPhilippe Mathieu-Daudé         .type = QEMU_OPT_STRING,
338ca64b086SPhilippe Mathieu-Daudé         .help = "Path to TPM device on the host",
339ca64b086SPhilippe Mathieu-Daudé     },
340ca64b086SPhilippe Mathieu-Daudé     { /* end of list */ },
341ca64b086SPhilippe Mathieu-Daudé };
342ca64b086SPhilippe Mathieu-Daudé 
tpm_passthrough_inst_init(Object * obj)343ca64b086SPhilippe Mathieu-Daudé static void tpm_passthrough_inst_init(Object *obj)
344ca64b086SPhilippe Mathieu-Daudé {
345ca64b086SPhilippe Mathieu-Daudé     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(obj);
346ca64b086SPhilippe Mathieu-Daudé 
347ca64b086SPhilippe Mathieu-Daudé     tpm_pt->options = g_new0(TPMPassthroughOptions, 1);
348ca64b086SPhilippe Mathieu-Daudé     tpm_pt->tpm_fd = -1;
349ca64b086SPhilippe Mathieu-Daudé     tpm_pt->cancel_fd = -1;
350ca64b086SPhilippe Mathieu-Daudé }
351ca64b086SPhilippe Mathieu-Daudé 
tpm_passthrough_inst_finalize(Object * obj)352ca64b086SPhilippe Mathieu-Daudé static void tpm_passthrough_inst_finalize(Object *obj)
353ca64b086SPhilippe Mathieu-Daudé {
354ca64b086SPhilippe Mathieu-Daudé     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(obj);
355ca64b086SPhilippe Mathieu-Daudé 
356ca64b086SPhilippe Mathieu-Daudé     tpm_passthrough_cancel_cmd(TPM_BACKEND(obj));
357ca64b086SPhilippe Mathieu-Daudé 
358ca64b086SPhilippe Mathieu-Daudé     if (tpm_pt->tpm_fd >= 0) {
359ca64b086SPhilippe Mathieu-Daudé         qemu_close(tpm_pt->tpm_fd);
360ca64b086SPhilippe Mathieu-Daudé     }
361ca64b086SPhilippe Mathieu-Daudé     if (tpm_pt->cancel_fd >= 0) {
362ca64b086SPhilippe Mathieu-Daudé         qemu_close(tpm_pt->cancel_fd);
363ca64b086SPhilippe Mathieu-Daudé     }
364ca64b086SPhilippe Mathieu-Daudé     qapi_free_TPMPassthroughOptions(tpm_pt->options);
365ca64b086SPhilippe Mathieu-Daudé }
366ca64b086SPhilippe Mathieu-Daudé 
tpm_passthrough_class_init(ObjectClass * klass,void * data)367ca64b086SPhilippe Mathieu-Daudé static void tpm_passthrough_class_init(ObjectClass *klass, void *data)
368ca64b086SPhilippe Mathieu-Daudé {
369ca64b086SPhilippe Mathieu-Daudé     TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);
370ca64b086SPhilippe Mathieu-Daudé 
371ca64b086SPhilippe Mathieu-Daudé     tbc->type = TPM_TYPE_PASSTHROUGH;
372ca64b086SPhilippe Mathieu-Daudé     tbc->opts = tpm_passthrough_cmdline_opts;
373ca64b086SPhilippe Mathieu-Daudé     tbc->desc = "Passthrough TPM backend driver";
374ca64b086SPhilippe Mathieu-Daudé     tbc->create = tpm_passthrough_create;
375ca64b086SPhilippe Mathieu-Daudé     tbc->startup_tpm = tpm_passthrough_startup_tpm;
376ca64b086SPhilippe Mathieu-Daudé     tbc->reset = tpm_passthrough_reset;
377ca64b086SPhilippe Mathieu-Daudé     tbc->cancel_cmd = tpm_passthrough_cancel_cmd;
378ca64b086SPhilippe Mathieu-Daudé     tbc->get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag;
379ca64b086SPhilippe Mathieu-Daudé     tbc->reset_tpm_established_flag =
380ca64b086SPhilippe Mathieu-Daudé         tpm_passthrough_reset_tpm_established_flag;
381ca64b086SPhilippe Mathieu-Daudé     tbc->get_tpm_version = tpm_passthrough_get_tpm_version;
382ca64b086SPhilippe Mathieu-Daudé     tbc->get_buffer_size = tpm_passthrough_get_buffer_size;
383ca64b086SPhilippe Mathieu-Daudé     tbc->get_tpm_options = tpm_passthrough_get_tpm_options;
384ca64b086SPhilippe Mathieu-Daudé     tbc->handle_request = tpm_passthrough_handle_request;
385ca64b086SPhilippe Mathieu-Daudé }
386ca64b086SPhilippe Mathieu-Daudé 
387ca64b086SPhilippe Mathieu-Daudé static const TypeInfo tpm_passthrough_info = {
388ca64b086SPhilippe Mathieu-Daudé     .name = TYPE_TPM_PASSTHROUGH,
389ca64b086SPhilippe Mathieu-Daudé     .parent = TYPE_TPM_BACKEND,
390ca64b086SPhilippe Mathieu-Daudé     .instance_size = sizeof(TPMPassthruState),
391ca64b086SPhilippe Mathieu-Daudé     .class_init = tpm_passthrough_class_init,
392ca64b086SPhilippe Mathieu-Daudé     .instance_init = tpm_passthrough_inst_init,
393ca64b086SPhilippe Mathieu-Daudé     .instance_finalize = tpm_passthrough_inst_finalize,
394ca64b086SPhilippe Mathieu-Daudé };
395ca64b086SPhilippe Mathieu-Daudé 
tpm_passthrough_register(void)396ca64b086SPhilippe Mathieu-Daudé static void tpm_passthrough_register(void)
397ca64b086SPhilippe Mathieu-Daudé {
398ca64b086SPhilippe Mathieu-Daudé     type_register_static(&tpm_passthrough_info);
399ca64b086SPhilippe Mathieu-Daudé }
400ca64b086SPhilippe Mathieu-Daudé 
401ca64b086SPhilippe Mathieu-Daudé type_init(tpm_passthrough_register)
402