]> git.proxmox.com Git - qemu.git/blob - tpm/tpm_passthrough.c
Add a TPM Passthrough backend driver implementation
[qemu.git] / tpm / tpm_passthrough.c
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 "qemu-common.h"
26 #include "qapi/error.h"
27 #include "qemu/sockets.h"
28 #include "tpm_int.h"
29 #include "hw/hw.h"
30 #include "hw/pc.h"
31 #include "tpm_tis.h"
32 #include "tpm_backend.h"
33
34 /* #define DEBUG_TPM */
35
36 #ifdef DEBUG_TPM
37 #define DPRINTF(fmt, ...) \
38 do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
39 #else
40 #define DPRINTF(fmt, ...) \
41 do { } while (0)
42 #endif
43
44 /* data structures */
45
46 typedef struct TPMPassthruThreadParams {
47 TPMState *tpm_state;
48
49 TPMRecvDataCB *recv_data_callback;
50 TPMBackend *tb;
51 } TPMPassthruThreadParams;
52
53 struct TPMPassthruState {
54 TPMBackendThread tbt;
55
56 TPMPassthruThreadParams tpm_thread_params;
57
58 char *tpm_dev;
59 int tpm_fd;
60 bool had_startup_error;
61 };
62
63 #define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0"
64
65 static int tpm_passthrough_unix_write(int fd, const uint8_t *buf, uint32_t len)
66 {
67 return send_all(fd, buf, len);
68 }
69
70 static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
71 {
72 return recv_all(fd, buf, len, true);
73 }
74
75 static uint32_t tpm_passthrough_get_size_from_buffer(const uint8_t *buf)
76 {
77 struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)buf;
78
79 return be32_to_cpu(resp->len);
80 }
81
82 static int tpm_passthrough_unix_tx_bufs(int tpm_fd,
83 const uint8_t *in, uint32_t in_len,
84 uint8_t *out, uint32_t out_len)
85 {
86 int ret;
87
88 ret = tpm_passthrough_unix_write(tpm_fd, in, in_len);
89 if (ret != in_len) {
90 error_report("tpm_passthrough: error while transmitting data "
91 "to TPM: %s (%i)\n",
92 strerror(errno), errno);
93 goto err_exit;
94 }
95
96 ret = tpm_passthrough_unix_read(tpm_fd, out, out_len);
97 if (ret < 0) {
98 error_report("tpm_passthrough: error while reading data from "
99 "TPM: %s (%i)\n",
100 strerror(errno), errno);
101 } else if (ret < sizeof(struct tpm_resp_hdr) ||
102 tpm_passthrough_get_size_from_buffer(out) != ret) {
103 ret = -1;
104 error_report("tpm_passthrough: received invalid response "
105 "packet from TPM\n");
106 }
107
108 err_exit:
109 if (ret < 0) {
110 tpm_write_fatal_error_response(out, out_len);
111 }
112
113 return ret;
114 }
115
116 static int tpm_passthrough_unix_transfer(int tpm_fd,
117 const TPMLocality *locty_data)
118 {
119 return tpm_passthrough_unix_tx_bufs(tpm_fd,
120 locty_data->w_buffer.buffer,
121 locty_data->w_offset,
122 locty_data->r_buffer.buffer,
123 locty_data->r_buffer.size);
124 }
125
126 static void tpm_passthrough_worker_thread(gpointer data,
127 gpointer user_data)
128 {
129 TPMPassthruThreadParams *thr_parms = user_data;
130 TPMPassthruState *tpm_pt = thr_parms->tb->s.tpm_pt;
131 TPMBackendCmd cmd = (TPMBackendCmd)data;
132
133 DPRINTF("tpm_passthrough: processing command type %d\n", cmd);
134
135 switch (cmd) {
136 case TPM_BACKEND_CMD_PROCESS_CMD:
137 tpm_passthrough_unix_transfer(tpm_pt->tpm_fd,
138 thr_parms->tpm_state->locty_data);
139
140 thr_parms->recv_data_callback(thr_parms->tpm_state,
141 thr_parms->tpm_state->locty_number);
142 break;
143 case TPM_BACKEND_CMD_INIT:
144 case TPM_BACKEND_CMD_END:
145 case TPM_BACKEND_CMD_TPM_RESET:
146 /* nothing to do */
147 break;
148 }
149 }
150
151 /*
152 * Start the TPM (thread). If it had been started before, then terminate
153 * and start it again.
154 */
155 static int tpm_passthrough_startup_tpm(TPMBackend *tb)
156 {
157 TPMPassthruState *tpm_pt = tb->s.tpm_pt;
158
159 /* terminate a running TPM */
160 tpm_backend_thread_end(&tpm_pt->tbt);
161
162 tpm_backend_thread_create(&tpm_pt->tbt,
163 tpm_passthrough_worker_thread,
164 &tb->s.tpm_pt->tpm_thread_params);
165
166 return 0;
167 }
168
169 static void tpm_passthrough_reset(TPMBackend *tb)
170 {
171 TPMPassthruState *tpm_pt = tb->s.tpm_pt;
172
173 DPRINTF("tpm_passthrough: CALL TO TPM_RESET!\n");
174
175 tpm_backend_thread_end(&tpm_pt->tbt);
176
177 tpm_pt->had_startup_error = false;
178 }
179
180 static int tpm_passthrough_init(TPMBackend *tb, TPMState *s,
181 TPMRecvDataCB *recv_data_cb)
182 {
183 TPMPassthruState *tpm_pt = tb->s.tpm_pt;
184
185 tpm_pt->tpm_thread_params.tpm_state = s;
186 tpm_pt->tpm_thread_params.recv_data_callback = recv_data_cb;
187 tpm_pt->tpm_thread_params.tb = tb;
188
189 return 0;
190 }
191
192 static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb)
193 {
194 return false;
195 }
196
197 static bool tpm_passthrough_get_startup_error(TPMBackend *tb)
198 {
199 TPMPassthruState *tpm_pt = tb->s.tpm_pt;
200
201 return tpm_pt->had_startup_error;
202 }
203
204 static size_t tpm_passthrough_realloc_buffer(TPMSizedBuffer *sb)
205 {
206 size_t wanted_size = 4096; /* Linux tpm.c buffer size */
207
208 if (sb->size != wanted_size) {
209 sb->buffer = g_realloc(sb->buffer, wanted_size);
210 sb->size = wanted_size;
211 }
212 return sb->size;
213 }
214
215 static void tpm_passthrough_deliver_request(TPMBackend *tb)
216 {
217 TPMPassthruState *tpm_pt = tb->s.tpm_pt;
218
219 tpm_backend_thread_deliver_request(&tpm_pt->tbt);
220 }
221
222 static void tpm_passthrough_cancel_cmd(TPMBackend *tb)
223 {
224 /* cancelling an ongoing command is known not to work with some TPMs */
225 }
226
227 static const char *tpm_passthrough_create_desc(void)
228 {
229 return "Passthrough TPM backend driver";
230 }
231
232 /*
233 * A basic test of a TPM device. We expect a well formatted response header
234 * (error response is fine) within one second.
235 */
236 static int tpm_passthrough_test_tpmdev(int fd)
237 {
238 struct tpm_req_hdr req = {
239 .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND),
240 .len = cpu_to_be32(sizeof(req)),
241 .ordinal = cpu_to_be32(TPM_ORD_GetTicks),
242 };
243 struct tpm_resp_hdr *resp;
244 fd_set readfds;
245 int n;
246 struct timeval tv = {
247 .tv_sec = 1,
248 .tv_usec = 0,
249 };
250 unsigned char buf[1024];
251
252 n = write(fd, &req, sizeof(req));
253 if (n < 0) {
254 return errno;
255 }
256 if (n != sizeof(req)) {
257 return EFAULT;
258 }
259
260 FD_ZERO(&readfds);
261 FD_SET(fd, &readfds);
262
263 /* wait for a second */
264 n = select(fd + 1, &readfds, NULL, NULL, &tv);
265 if (n != 1) {
266 return errno;
267 }
268
269 n = read(fd, &buf, sizeof(buf));
270 if (n < sizeof(struct tpm_resp_hdr)) {
271 return EFAULT;
272 }
273
274 resp = (struct tpm_resp_hdr *)buf;
275 /* check the header */
276 if (be16_to_cpu(resp->tag) != TPM_TAG_RSP_COMMAND ||
277 be32_to_cpu(resp->len) != n) {
278 return EBADMSG;
279 }
280
281 return 0;
282 }
283
284 static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
285 {
286 const char *value;
287
288 value = qemu_opt_get(opts, "path");
289 if (!value) {
290 value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
291 }
292
293 tb->s.tpm_pt->tpm_dev = g_strdup(value);
294
295 tb->path = g_strdup(tb->s.tpm_pt->tpm_dev);
296
297 tb->s.tpm_pt->tpm_fd = qemu_open(tb->s.tpm_pt->tpm_dev, O_RDWR);
298 if (tb->s.tpm_pt->tpm_fd < 0) {
299 error_report("Cannot access TPM device using '%s': %s\n",
300 tb->s.tpm_pt->tpm_dev, strerror(errno));
301 goto err_free_parameters;
302 }
303
304 if (tpm_passthrough_test_tpmdev(tb->s.tpm_pt->tpm_fd)) {
305 error_report("'%s' is not a TPM device.\n",
306 tb->s.tpm_pt->tpm_dev);
307 goto err_close_tpmdev;
308 }
309
310 return 0;
311
312 err_close_tpmdev:
313 qemu_close(tb->s.tpm_pt->tpm_fd);
314 tb->s.tpm_pt->tpm_fd = -1;
315
316 err_free_parameters:
317 g_free(tb->path);
318 tb->path = NULL;
319
320 g_free(tb->s.tpm_pt->tpm_dev);
321 tb->s.tpm_pt->tpm_dev = NULL;
322
323 return 1;
324 }
325
326 static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
327 {
328 TPMBackend *tb;
329
330 tb = g_new0(TPMBackend, 1);
331 tb->s.tpm_pt = g_new0(TPMPassthruState, 1);
332 tb->id = g_strdup(id);
333 /* let frontend set the fe_model to proper value */
334 tb->fe_model = -1;
335
336 tb->ops = &tpm_passthrough_driver;
337
338 if (tpm_passthrough_handle_device_opts(opts, tb)) {
339 goto err_exit;
340 }
341
342 return tb;
343
344 err_exit:
345 g_free(tb->id);
346 g_free(tb->s.tpm_pt);
347 g_free(tb);
348
349 return NULL;
350 }
351
352 static void tpm_passthrough_destroy(TPMBackend *tb)
353 {
354 TPMPassthruState *tpm_pt = tb->s.tpm_pt;
355
356 tpm_backend_thread_end(&tpm_pt->tbt);
357
358 qemu_close(tpm_pt->tpm_fd);
359
360 g_free(tb->id);
361 g_free(tb->path);
362 g_free(tb->s.tpm_pt->tpm_dev);
363 g_free(tb->s.tpm_pt);
364 g_free(tb);
365 }
366
367 const TPMDriverOps tpm_passthrough_driver = {
368 .type = TPM_TYPE_PASSTHROUGH,
369 .desc = tpm_passthrough_create_desc,
370 .create = tpm_passthrough_create,
371 .destroy = tpm_passthrough_destroy,
372 .init = tpm_passthrough_init,
373 .startup_tpm = tpm_passthrough_startup_tpm,
374 .realloc_buffer = tpm_passthrough_realloc_buffer,
375 .reset = tpm_passthrough_reset,
376 .had_startup_error = tpm_passthrough_get_startup_error,
377 .deliver_request = tpm_passthrough_deliver_request,
378 .cancel_cmd = tpm_passthrough_cancel_cmd,
379 .get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag,
380 };
381
382 static void tpm_passthrough_register(void)
383 {
384 tpm_register_driver(&tpm_passthrough_driver);
385 }
386
387 type_init(tpm_passthrough_register)