| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *  passthrough TPM driver | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  Copyright (c) 2010 - 2013 IBM Corporation | 
					
						
							|  |  |  |  *  Authors: | 
					
						
							|  |  |  |  *    Stefan Berger <stefanb@us.ibm.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  Copyright (C) 2011 IAIK, Graz University of Technology | 
					
						
							|  |  |  |  *    Author: Andreas Niederl | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This library is free software; you can redistribute it and/or | 
					
						
							|  |  |  |  * modify it under the terms of the GNU Lesser General Public | 
					
						
							|  |  |  |  * License as published by the Free Software Foundation; either | 
					
						
							|  |  |  |  * version 2 of the License, or (at your option) any later version. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This library is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
					
						
							|  |  |  |  * Lesser General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * You should have received a copy of the GNU Lesser General Public | 
					
						
							|  |  |  |  * License along with this library; if not, see <http://www.gnu.org/licenses/>
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "qemu-common.h"
 | 
					
						
							|  |  |  | #include "qapi/error.h"
 | 
					
						
							| 
									
										
										
										
											2015-03-17 18:29:20 +01:00
										 |  |  | #include "qemu/error-report.h"
 | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | #include "qemu/sockets.h"
 | 
					
						
							| 
									
										
										
										
											2013-04-08 16:55:25 +02:00
										 |  |  | #include "sysemu/tpm_backend.h"
 | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | #include "tpm_int.h"
 | 
					
						
							|  |  |  | #include "hw/hw.h"
 | 
					
						
							| 
									
										
										
										
											2013-02-05 17:06:20 +01:00
										 |  |  | #include "hw/i386/pc.h"
 | 
					
						
							| 
									
										
										
										
											2013-04-02 18:28:41 +02:00
										 |  |  | #include "sysemu/tpm_backend_int.h"
 | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | #include "tpm_tis.h"
 | 
					
						
							| 
									
										
										
										
											2015-05-26 16:51:06 -04:00
										 |  |  | #include "tpm_util.h"
 | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-31 14:49:10 -04:00
										 |  |  | #define DEBUG_TPM 0
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define DPRINTF(fmt, ...) do { \
 | 
					
						
							|  |  |  |     if (DEBUG_TPM) { \ | 
					
						
							|  |  |  |         fprintf(stderr, fmt, ## __VA_ARGS__); \ | 
					
						
							|  |  |  |     } \ | 
					
						
							|  |  |  | } while (0); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  | #define TYPE_TPM_PASSTHROUGH "tpm-passthrough"
 | 
					
						
							|  |  |  | #define TPM_PASSTHROUGH(obj) \
 | 
					
						
							|  |  |  |     OBJECT_CHECK(TPMPassthruState, (obj), TYPE_TPM_PASSTHROUGH) | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-02 18:28:41 +02:00
										 |  |  | static const TPMDriverOps tpm_passthrough_driver; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  | /* data structures */ | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | typedef struct TPMPassthruThreadParams { | 
					
						
							|  |  |  |     TPMState *tpm_state; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     TPMRecvDataCB *recv_data_callback; | 
					
						
							|  |  |  |     TPMBackend *tb; | 
					
						
							|  |  |  | } TPMPassthruThreadParams; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct TPMPassthruState { | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  |     TPMBackend parent; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  |     TPMBackendThread tbt; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     TPMPassthruThreadParams tpm_thread_params; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     char *tpm_dev; | 
					
						
							|  |  |  |     int tpm_fd; | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  |     bool tpm_executing; | 
					
						
							|  |  |  |     bool tpm_op_canceled; | 
					
						
							|  |  |  |     int cancel_fd; | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  |     bool had_startup_error; | 
					
						
							| 
									
										
										
										
											2015-05-26 16:51:06 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     TPMVersion tpm_version; | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  | typedef struct TPMPassthruState TPMPassthruState; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | #define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  | /* functions */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void tpm_passthrough_cancel_cmd(TPMBackend *tb); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | static int tpm_passthrough_unix_write(int fd, const uint8_t *buf, uint32_t len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return send_all(fd, buf, len); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return recv_all(fd, buf, len, true); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static uint32_t tpm_passthrough_get_size_from_buffer(const uint8_t *buf) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)buf; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return be32_to_cpu(resp->len); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-02 18:28:41 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Write an error message in the given output buffer. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (out_len >= sizeof(struct tpm_resp_hdr)) { | 
					
						
							|  |  |  |         struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)out; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         resp->tag = cpu_to_be16(TPM_TAG_RSP_COMMAND); | 
					
						
							|  |  |  |         resp->len = cpu_to_be32(sizeof(struct tpm_resp_hdr)); | 
					
						
							|  |  |  |         resp->errcode = cpu_to_be32(TPM_FAIL); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-23 09:27:19 -05:00
										 |  |  | static bool tpm_passthrough_is_selftest(const uint8_t *in, uint32_t in_len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct tpm_req_hdr *hdr = (struct tpm_req_hdr *)in; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (in_len >= sizeof(*hdr)) { | 
					
						
							|  |  |  |         return (be32_to_cpu(hdr->ordinal) == TPM_ORD_ContinueSelfTest); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  | static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt, | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  |                                         const uint8_t *in, uint32_t in_len, | 
					
						
							| 
									
										
										
										
											2015-02-23 09:27:19 -05:00
										 |  |  |                                         uint8_t *out, uint32_t out_len, | 
					
						
							|  |  |  |                                         bool *selftest_done) | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | { | 
					
						
							|  |  |  |     int ret; | 
					
						
							| 
									
										
										
										
											2015-02-23 09:27:19 -05:00
										 |  |  |     bool is_selftest; | 
					
						
							|  |  |  |     const struct tpm_resp_hdr *hdr; | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  |     tpm_pt->tpm_op_canceled = false; | 
					
						
							|  |  |  |     tpm_pt->tpm_executing = true; | 
					
						
							| 
									
										
										
										
											2015-02-23 09:27:19 -05:00
										 |  |  |     *selftest_done = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     is_selftest = tpm_passthrough_is_selftest(in, in_len); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     ret = tpm_passthrough_unix_write(tpm_pt->tpm_fd, in, in_len); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  |     if (ret != in_len) { | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  |         if (!tpm_pt->tpm_op_canceled || | 
					
						
							|  |  |  |             (tpm_pt->tpm_op_canceled && errno != ECANCELED)) { | 
					
						
							|  |  |  |             error_report("tpm_passthrough: error while transmitting data " | 
					
						
							| 
									
										
										
										
											2015-02-25 12:22:35 +08:00
										 |  |  |                          "to TPM: %s (%i)", | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  |                          strerror(errno), errno); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  |         goto err_exit; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  |     tpm_pt->tpm_executing = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  |     if (ret < 0) { | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  |         if (!tpm_pt->tpm_op_canceled || | 
					
						
							|  |  |  |             (tpm_pt->tpm_op_canceled && errno != ECANCELED)) { | 
					
						
							|  |  |  |             error_report("tpm_passthrough: error while reading data from " | 
					
						
							| 
									
										
										
										
											2015-02-25 12:22:35 +08:00
										 |  |  |                          "TPM: %s (%i)", | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  |                          strerror(errno), errno); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  |     } else if (ret < sizeof(struct tpm_resp_hdr) || | 
					
						
							|  |  |  |                tpm_passthrough_get_size_from_buffer(out) != ret) { | 
					
						
							|  |  |  |         ret = -1; | 
					
						
							|  |  |  |         error_report("tpm_passthrough: received invalid response " | 
					
						
							| 
									
										
										
										
											2015-02-25 12:22:35 +08:00
										 |  |  |                      "packet from TPM"); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-23 09:27:19 -05:00
										 |  |  |     if (is_selftest && (ret >= sizeof(struct tpm_resp_hdr))) { | 
					
						
							|  |  |  |         hdr = (struct tpm_resp_hdr *)out; | 
					
						
							|  |  |  |         *selftest_done = (be32_to_cpu(hdr->errcode) == 0); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | err_exit: | 
					
						
							|  |  |  |     if (ret < 0) { | 
					
						
							|  |  |  |         tpm_write_fatal_error_response(out, out_len); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  |     tpm_pt->tpm_executing = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  |     return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  | static int tpm_passthrough_unix_transfer(TPMPassthruState *tpm_pt, | 
					
						
							| 
									
										
										
										
											2015-02-23 09:27:19 -05:00
										 |  |  |                                          const TPMLocality *locty_data, | 
					
						
							|  |  |  |                                          bool *selftest_done) | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  |     return tpm_passthrough_unix_tx_bufs(tpm_pt, | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  |                                         locty_data->w_buffer.buffer, | 
					
						
							|  |  |  |                                         locty_data->w_offset, | 
					
						
							|  |  |  |                                         locty_data->r_buffer.buffer, | 
					
						
							| 
									
										
										
										
											2015-02-23 09:27:19 -05:00
										 |  |  |                                         locty_data->r_buffer.size, | 
					
						
							|  |  |  |                                         selftest_done); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void tpm_passthrough_worker_thread(gpointer data, | 
					
						
							|  |  |  |                                           gpointer user_data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     TPMPassthruThreadParams *thr_parms = user_data; | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  |     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(thr_parms->tb); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  |     TPMBackendCmd cmd = (TPMBackendCmd)data; | 
					
						
							| 
									
										
										
										
											2015-02-23 09:27:19 -05:00
										 |  |  |     bool selftest_done = false; | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     DPRINTF("tpm_passthrough: processing command type %d\n", cmd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (cmd) { | 
					
						
							|  |  |  |     case TPM_BACKEND_CMD_PROCESS_CMD: | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  |         tpm_passthrough_unix_transfer(tpm_pt, | 
					
						
							| 
									
										
										
										
											2015-02-23 09:27:19 -05:00
										 |  |  |                                       thr_parms->tpm_state->locty_data, | 
					
						
							|  |  |  |                                       &selftest_done); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |         thr_parms->recv_data_callback(thr_parms->tpm_state, | 
					
						
							| 
									
										
										
										
											2015-02-23 09:27:19 -05:00
										 |  |  |                                       thr_parms->tpm_state->locty_number, | 
					
						
							|  |  |  |                                       selftest_done); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  |         break; | 
					
						
							|  |  |  |     case TPM_BACKEND_CMD_INIT: | 
					
						
							|  |  |  |     case TPM_BACKEND_CMD_END: | 
					
						
							|  |  |  |     case TPM_BACKEND_CMD_TPM_RESET: | 
					
						
							|  |  |  |         /* nothing to do */ | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Start the TPM (thread). If it had been started before, then terminate | 
					
						
							|  |  |  |  * and start it again. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int tpm_passthrough_startup_tpm(TPMBackend *tb) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  |     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /* terminate a running TPM */ | 
					
						
							|  |  |  |     tpm_backend_thread_end(&tpm_pt->tbt); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     tpm_backend_thread_create(&tpm_pt->tbt, | 
					
						
							|  |  |  |                               tpm_passthrough_worker_thread, | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  |                               &tpm_pt->tpm_thread_params); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void tpm_passthrough_reset(TPMBackend *tb) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  |     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     DPRINTF("tpm_passthrough: CALL TO TPM_RESET!\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  |     tpm_passthrough_cancel_cmd(tb); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  |     tpm_backend_thread_end(&tpm_pt->tbt); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     tpm_pt->had_startup_error = false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int tpm_passthrough_init(TPMBackend *tb, TPMState *s, | 
					
						
							|  |  |  |                                 TPMRecvDataCB *recv_data_cb) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  |     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     tpm_pt->tpm_thread_params.tpm_state = s; | 
					
						
							|  |  |  |     tpm_pt->tpm_thread_params.recv_data_callback = recv_data_cb; | 
					
						
							|  |  |  |     tpm_pt->tpm_thread_params.tb = tb; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-26 16:51:05 -04:00
										 |  |  | static int tpm_passthrough_reset_tpm_established_flag(TPMBackend *tb, | 
					
						
							|  |  |  |                                                       uint8_t locty) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     /* only a TPM 2.0 will support this */ | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | static bool tpm_passthrough_get_startup_error(TPMBackend *tb) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  |     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return tpm_pt->had_startup_error; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static size_t tpm_passthrough_realloc_buffer(TPMSizedBuffer *sb) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     size_t wanted_size = 4096; /* Linux tpm.c buffer size */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (sb->size != wanted_size) { | 
					
						
							|  |  |  |         sb->buffer = g_realloc(sb->buffer, wanted_size); | 
					
						
							|  |  |  |         sb->size = wanted_size; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return sb->size; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void tpm_passthrough_deliver_request(TPMBackend *tb) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  |     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     tpm_backend_thread_deliver_request(&tpm_pt->tbt); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void tpm_passthrough_cancel_cmd(TPMBackend *tb) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  |     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  |     int n; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /*
 | 
					
						
							|  |  |  |      * As of Linux 3.7 the tpm_tis driver does not properly cancel | 
					
						
							|  |  |  |      * commands on all TPM manufacturers' TPMs. | 
					
						
							|  |  |  |      * Only cancel if we're busy so we don't cancel someone else's | 
					
						
							|  |  |  |      * command, e.g., a command executed on the host. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     if (tpm_pt->tpm_executing) { | 
					
						
							|  |  |  |         if (tpm_pt->cancel_fd >= 0) { | 
					
						
							|  |  |  |             n = write(tpm_pt->cancel_fd, "-", 1); | 
					
						
							|  |  |  |             if (n != 1) { | 
					
						
							| 
									
										
										
										
											2015-02-25 12:22:35 +08:00
										 |  |  |                 error_report("Canceling TPM command failed: %s", | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  |                              strerror(errno)); | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 tpm_pt->tpm_op_canceled = true; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             error_report("Cannot cancel TPM command due to missing " | 
					
						
							|  |  |  |                          "TPM sysfs cancel entry"); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const char *tpm_passthrough_create_desc(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return "Passthrough TPM backend driver"; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-26 16:51:05 -04:00
										 |  |  | static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-05-26 16:51:06 -04:00
										 |  |  |     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-26 16:51:06 -04:00
										 |  |  |     return tpm_pt->tpm_version; | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Unless path or file descriptor set has been provided by user, | 
					
						
							|  |  |  |  * determine the sysfs cancel file following kernel documentation | 
					
						
							|  |  |  |  * in Documentation/ABI/stable/sysfs-class-tpm. | 
					
						
							| 
									
										
										
										
											2013-04-16 17:08:36 -04:00
										 |  |  |  * From /dev/tpm0 create /sys/class/misc/tpm0/device/cancel | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  |  */ | 
					
						
							|  |  |  | static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-04-16 17:08:36 -04:00
										 |  |  |     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  |     int fd = -1; | 
					
						
							| 
									
										
										
										
											2013-04-16 17:08:36 -04:00
										 |  |  |     char *dev; | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  |     char path[PATH_MAX]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (tb->cancel_path) { | 
					
						
							|  |  |  |         fd = qemu_open(tb->cancel_path, O_WRONLY); | 
					
						
							|  |  |  |         if (fd < 0) { | 
					
						
							|  |  |  |             error_report("Could not open TPM cancel path : %s", | 
					
						
							|  |  |  |                          strerror(errno)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return fd; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-16 17:08:36 -04:00
										 |  |  |     dev = strrchr(tpm_pt->tpm_dev, '/'); | 
					
						
							|  |  |  |     if (dev) { | 
					
						
							|  |  |  |         dev++; | 
					
						
							|  |  |  |         if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel", | 
					
						
							|  |  |  |                      dev) < sizeof(path)) { | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  |             fd = qemu_open(path, O_WRONLY); | 
					
						
							| 
									
										
										
										
											2013-04-16 17:08:36 -04:00
										 |  |  |             if (fd >= 0) { | 
					
						
							|  |  |  |                 tb->cancel_path = g_strdup(path); | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 error_report("tpm_passthrough: Could not open TPM cancel " | 
					
						
							|  |  |  |                              "path %s : %s", path, strerror(errno)); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2013-04-16 17:08:36 -04:00
										 |  |  |     } else { | 
					
						
							|  |  |  |        error_report("tpm_passthrough: Bad TPM device path %s", | 
					
						
							|  |  |  |                     tpm_pt->tpm_dev); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return fd; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  |     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  |     const char *value; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  |     value = qemu_opt_get(opts, "cancel-path"); | 
					
						
							| 
									
										
										
										
											2014-12-04 10:26:55 +01:00
										 |  |  |     tb->cancel_path = g_strdup(value); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  |     value = qemu_opt_get(opts, "path"); | 
					
						
							|  |  |  |     if (!value) { | 
					
						
							|  |  |  |         value = TPM_PASSTHROUGH_DEFAULT_DEVICE; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  |     tpm_pt->tpm_dev = g_strdup(value); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  |     tb->path = g_strdup(tpm_pt->tpm_dev); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  |     tpm_pt->tpm_fd = qemu_open(tpm_pt->tpm_dev, O_RDWR); | 
					
						
							|  |  |  |     if (tpm_pt->tpm_fd < 0) { | 
					
						
							| 
									
										
										
										
											2015-02-25 12:22:35 +08:00
										 |  |  |         error_report("Cannot access TPM device using '%s': %s", | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  |                      tpm_pt->tpm_dev, strerror(errno)); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  |         goto err_free_parameters; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-26 16:51:06 -04:00
										 |  |  |     if (tpm_util_test_tpmdev(tpm_pt->tpm_fd, &tpm_pt->tpm_version)) { | 
					
						
							| 
									
										
										
										
											2015-02-25 12:22:35 +08:00
										 |  |  |         error_report("'%s' is not a TPM device.", | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  |                      tpm_pt->tpm_dev); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  |         goto err_close_tpmdev; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  err_close_tpmdev: | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  |     qemu_close(tpm_pt->tpm_fd); | 
					
						
							|  |  |  |     tpm_pt->tpm_fd = -1; | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |  err_free_parameters: | 
					
						
							|  |  |  |     g_free(tb->path); | 
					
						
							|  |  |  |     tb->path = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  |     g_free(tpm_pt->tpm_dev); | 
					
						
							|  |  |  |     tpm_pt->tpm_dev = NULL; | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  |     Object *obj = object_new(TYPE_TPM_PASSTHROUGH); | 
					
						
							|  |  |  |     TPMBackend *tb = TPM_BACKEND(obj); | 
					
						
							|  |  |  |     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     tb->id = g_strdup(id); | 
					
						
							|  |  |  |     /* let frontend set the fe_model to proper value */ | 
					
						
							|  |  |  |     tb->fe_model = -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     tb->ops = &tpm_passthrough_driver; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (tpm_passthrough_handle_device_opts(opts, tb)) { | 
					
						
							|  |  |  |         goto err_exit; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  |     tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tb); | 
					
						
							|  |  |  |     if (tpm_pt->cancel_fd < 0) { | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  |         goto err_exit; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  |     return tb; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | err_exit: | 
					
						
							|  |  |  |     g_free(tb->id); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void tpm_passthrough_destroy(TPMBackend *tb) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  |     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  |     tpm_passthrough_cancel_cmd(tb); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  |     tpm_backend_thread_end(&tpm_pt->tbt); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     qemu_close(tpm_pt->tpm_fd); | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  |     qemu_close(tpm_pt->cancel_fd); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     g_free(tb->id); | 
					
						
							|  |  |  |     g_free(tb->path); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:54 -05:00
										 |  |  |     g_free(tb->cancel_path); | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  |     g_free(tpm_pt->tpm_dev); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-22 10:41:39 -04:00
										 |  |  | static const QemuOptDesc tpm_passthrough_cmdline_opts[] = { | 
					
						
							|  |  |  |     TPM_STANDARD_CMDLINE_OPTS, | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         .name = "cancel-path", | 
					
						
							|  |  |  |         .type = QEMU_OPT_STRING, | 
					
						
							|  |  |  |         .help = "Sysfs file entry for canceling TPM commands", | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         .name = "path", | 
					
						
							|  |  |  |         .type = QEMU_OPT_STRING, | 
					
						
							|  |  |  |         .help = "Path to TPM device on the host", | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     { /* end of list */ }, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-02 18:28:41 +02:00
										 |  |  | static const TPMDriverOps tpm_passthrough_driver = { | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  |     .type                     = TPM_TYPE_PASSTHROUGH, | 
					
						
							| 
									
										
										
										
											2013-04-22 10:41:39 -04:00
										 |  |  |     .opts                     = tpm_passthrough_cmdline_opts, | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  |     .desc                     = tpm_passthrough_create_desc, | 
					
						
							|  |  |  |     .create                   = tpm_passthrough_create, | 
					
						
							|  |  |  |     .destroy                  = tpm_passthrough_destroy, | 
					
						
							|  |  |  |     .init                     = tpm_passthrough_init, | 
					
						
							|  |  |  |     .startup_tpm              = tpm_passthrough_startup_tpm, | 
					
						
							|  |  |  |     .realloc_buffer           = tpm_passthrough_realloc_buffer, | 
					
						
							|  |  |  |     .reset                    = tpm_passthrough_reset, | 
					
						
							|  |  |  |     .had_startup_error        = tpm_passthrough_get_startup_error, | 
					
						
							|  |  |  |     .deliver_request          = tpm_passthrough_deliver_request, | 
					
						
							|  |  |  |     .cancel_cmd               = tpm_passthrough_cancel_cmd, | 
					
						
							|  |  |  |     .get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag, | 
					
						
							| 
									
										
										
										
											2015-05-26 16:51:05 -04:00
										 |  |  |     .reset_tpm_established_flag = tpm_passthrough_reset_tpm_established_flag, | 
					
						
							|  |  |  |     .get_tpm_version          = tpm_passthrough_get_tpm_version, | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  | static void tpm_passthrough_inst_init(Object *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void tpm_passthrough_inst_finalize(Object *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void tpm_passthrough_class_init(ObjectClass *klass, void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     tbc->ops = &tpm_passthrough_driver; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const TypeInfo tpm_passthrough_info = { | 
					
						
							|  |  |  |     .name = TYPE_TPM_PASSTHROUGH, | 
					
						
							|  |  |  |     .parent = TYPE_TPM_BACKEND, | 
					
						
							|  |  |  |     .instance_size = sizeof(TPMPassthruState), | 
					
						
							|  |  |  |     .class_init = tpm_passthrough_class_init, | 
					
						
							|  |  |  |     .instance_init = tpm_passthrough_inst_init, | 
					
						
							|  |  |  |     .instance_finalize = tpm_passthrough_inst_finalize, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  | static void tpm_passthrough_register(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-28 07:26:21 -04:00
										 |  |  |     type_register_static(&tpm_passthrough_info); | 
					
						
							| 
									
										
										
										
											2013-02-27 12:47:53 -05:00
										 |  |  |     tpm_register_driver(&tpm_passthrough_driver); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type_init(tpm_passthrough_register) |