14549a8b7SStefan Berger /* 24549a8b7SStefan Berger * passthrough TPM driver 34549a8b7SStefan Berger * 44549a8b7SStefan Berger * Copyright (c) 2010 - 2013 IBM Corporation 54549a8b7SStefan Berger * Authors: 64549a8b7SStefan Berger * Stefan Berger <stefanb@us.ibm.com> 74549a8b7SStefan Berger * 84549a8b7SStefan Berger * Copyright (C) 2011 IAIK, Graz University of Technology 94549a8b7SStefan Berger * Author: Andreas Niederl 104549a8b7SStefan Berger * 114549a8b7SStefan Berger * This library is free software; you can redistribute it and/or 124549a8b7SStefan Berger * modify it under the terms of the GNU Lesser General Public 134549a8b7SStefan Berger * License as published by the Free Software Foundation; either 144549a8b7SStefan Berger * version 2 of the License, or (at your option) any later version. 154549a8b7SStefan Berger * 164549a8b7SStefan Berger * This library is distributed in the hope that it will be useful, 174549a8b7SStefan Berger * but WITHOUT ANY WARRANTY; without even the implied warranty of 184549a8b7SStefan Berger * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 194549a8b7SStefan Berger * Lesser General Public License for more details. 204549a8b7SStefan Berger * 214549a8b7SStefan Berger * You should have received a copy of the GNU Lesser General Public 224549a8b7SStefan Berger * License along with this library; if not, see <http://www.gnu.org/licenses/> 234549a8b7SStefan Berger */ 244549a8b7SStefan Berger 25*0430891cSPeter Maydell #include "qemu/osdep.h" 264549a8b7SStefan Berger #include "qemu-common.h" 27d49b6836SMarkus Armbruster #include "qemu/error-report.h" 284549a8b7SStefan Berger #include "qemu/sockets.h" 29dccfcd0eSPaolo Bonzini #include "sysemu/tpm_backend.h" 304549a8b7SStefan Berger #include "tpm_int.h" 314549a8b7SStefan Berger #include "hw/hw.h" 320d09e41aSPaolo Bonzini #include "hw/i386/pc.h" 33bdee56f5SPaolo Bonzini #include "sysemu/tpm_backend_int.h" 344549a8b7SStefan Berger #include "tpm_tis.h" 3556a3c24fSStefan Berger #include "tpm_util.h" 364549a8b7SStefan Berger 374d1ba9c4SStefan Berger #define DEBUG_TPM 0 384549a8b7SStefan Berger 394d1ba9c4SStefan Berger #define DPRINTF(fmt, ...) do { \ 404d1ba9c4SStefan Berger if (DEBUG_TPM) { \ 414d1ba9c4SStefan Berger fprintf(stderr, fmt, ## __VA_ARGS__); \ 424d1ba9c4SStefan Berger } \ 434d1ba9c4SStefan Berger } while (0); 444549a8b7SStefan Berger 458f0605ccSStefan Berger #define TYPE_TPM_PASSTHROUGH "tpm-passthrough" 468f0605ccSStefan Berger #define TPM_PASSTHROUGH(obj) \ 478f0605ccSStefan Berger OBJECT_CHECK(TPMPassthruState, (obj), TYPE_TPM_PASSTHROUGH) 484549a8b7SStefan Berger 49bdee56f5SPaolo Bonzini static const TPMDriverOps tpm_passthrough_driver; 50bdee56f5SPaolo Bonzini 518f0605ccSStefan Berger /* data structures */ 524549a8b7SStefan Berger typedef struct TPMPassthruThreadParams { 534549a8b7SStefan Berger TPMState *tpm_state; 544549a8b7SStefan Berger 554549a8b7SStefan Berger TPMRecvDataCB *recv_data_callback; 564549a8b7SStefan Berger TPMBackend *tb; 574549a8b7SStefan Berger } TPMPassthruThreadParams; 584549a8b7SStefan Berger 594549a8b7SStefan Berger struct TPMPassthruState { 608f0605ccSStefan Berger TPMBackend parent; 618f0605ccSStefan Berger 624549a8b7SStefan Berger TPMBackendThread tbt; 634549a8b7SStefan Berger 644549a8b7SStefan Berger TPMPassthruThreadParams tpm_thread_params; 654549a8b7SStefan Berger 664549a8b7SStefan Berger char *tpm_dev; 674549a8b7SStefan Berger int tpm_fd; 6892dcc234SStefan Berger bool tpm_executing; 6992dcc234SStefan Berger bool tpm_op_canceled; 7092dcc234SStefan Berger int cancel_fd; 714549a8b7SStefan Berger bool had_startup_error; 7256a3c24fSStefan Berger 7356a3c24fSStefan Berger TPMVersion tpm_version; 744549a8b7SStefan Berger }; 754549a8b7SStefan Berger 768f0605ccSStefan Berger typedef struct TPMPassthruState TPMPassthruState; 778f0605ccSStefan Berger 784549a8b7SStefan Berger #define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0" 794549a8b7SStefan Berger 8092dcc234SStefan Berger /* functions */ 8192dcc234SStefan Berger 8292dcc234SStefan Berger static void tpm_passthrough_cancel_cmd(TPMBackend *tb); 8392dcc234SStefan Berger 844549a8b7SStefan Berger static int tpm_passthrough_unix_write(int fd, const uint8_t *buf, uint32_t len) 854549a8b7SStefan Berger { 8646f296cdSDaniel P. Berrange int ret, remain; 8746f296cdSDaniel P. Berrange 8846f296cdSDaniel P. Berrange remain = len; 8946f296cdSDaniel P. Berrange while (len > 0) { 9046f296cdSDaniel P. Berrange ret = write(fd, buf, remain); 9146f296cdSDaniel P. Berrange if (ret < 0) { 9246f296cdSDaniel P. Berrange if (errno != EINTR && errno != EAGAIN) { 9346f296cdSDaniel P. Berrange return -1; 9446f296cdSDaniel P. Berrange } 9546f296cdSDaniel P. Berrange } else if (ret == 0) { 9646f296cdSDaniel P. Berrange break; 9746f296cdSDaniel P. Berrange } else { 9846f296cdSDaniel P. Berrange buf += ret; 9946f296cdSDaniel P. Berrange remain -= ret; 10046f296cdSDaniel P. Berrange } 10146f296cdSDaniel P. Berrange } 10246f296cdSDaniel P. Berrange return len - remain; 1034549a8b7SStefan Berger } 1044549a8b7SStefan Berger 1054549a8b7SStefan Berger static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len) 1064549a8b7SStefan Berger { 10746f296cdSDaniel P. Berrange int ret; 10846f296cdSDaniel P. Berrange reread: 10946f296cdSDaniel P. Berrange ret = read(fd, buf, len); 11046f296cdSDaniel P. Berrange if (ret < 0) { 11146f296cdSDaniel P. Berrange if (errno != EINTR && errno != EAGAIN) { 11246f296cdSDaniel P. Berrange return -1; 11346f296cdSDaniel P. Berrange } 11446f296cdSDaniel P. Berrange goto reread; 11546f296cdSDaniel P. Berrange } 11646f296cdSDaniel P. Berrange return ret; 1174549a8b7SStefan Berger } 1184549a8b7SStefan Berger 1194549a8b7SStefan Berger static uint32_t tpm_passthrough_get_size_from_buffer(const uint8_t *buf) 1204549a8b7SStefan Berger { 1214549a8b7SStefan Berger struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)buf; 1224549a8b7SStefan Berger 1234549a8b7SStefan Berger return be32_to_cpu(resp->len); 1244549a8b7SStefan Berger } 1254549a8b7SStefan Berger 126bdee56f5SPaolo Bonzini /* 127bdee56f5SPaolo Bonzini * Write an error message in the given output buffer. 128bdee56f5SPaolo Bonzini */ 129bdee56f5SPaolo Bonzini static void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len) 130bdee56f5SPaolo Bonzini { 131bdee56f5SPaolo Bonzini if (out_len >= sizeof(struct tpm_resp_hdr)) { 132bdee56f5SPaolo Bonzini struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)out; 133bdee56f5SPaolo Bonzini 134bdee56f5SPaolo Bonzini resp->tag = cpu_to_be16(TPM_TAG_RSP_COMMAND); 135bdee56f5SPaolo Bonzini resp->len = cpu_to_be32(sizeof(struct tpm_resp_hdr)); 136bdee56f5SPaolo Bonzini resp->errcode = cpu_to_be32(TPM_FAIL); 137bdee56f5SPaolo Bonzini } 138bdee56f5SPaolo Bonzini } 139bdee56f5SPaolo Bonzini 140fd859081SStefan Berger static bool tpm_passthrough_is_selftest(const uint8_t *in, uint32_t in_len) 141fd859081SStefan Berger { 142fd859081SStefan Berger struct tpm_req_hdr *hdr = (struct tpm_req_hdr *)in; 143fd859081SStefan Berger 144fd859081SStefan Berger if (in_len >= sizeof(*hdr)) { 145fd859081SStefan Berger return (be32_to_cpu(hdr->ordinal) == TPM_ORD_ContinueSelfTest); 146fd859081SStefan Berger } 147fd859081SStefan Berger 148fd859081SStefan Berger return false; 149fd859081SStefan Berger } 150fd859081SStefan Berger 15192dcc234SStefan Berger static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt, 1524549a8b7SStefan Berger const uint8_t *in, uint32_t in_len, 153fd859081SStefan Berger uint8_t *out, uint32_t out_len, 154fd859081SStefan Berger bool *selftest_done) 1554549a8b7SStefan Berger { 1564549a8b7SStefan Berger int ret; 157fd859081SStefan Berger bool is_selftest; 158fd859081SStefan Berger const struct tpm_resp_hdr *hdr; 1594549a8b7SStefan Berger 16092dcc234SStefan Berger tpm_pt->tpm_op_canceled = false; 16192dcc234SStefan Berger tpm_pt->tpm_executing = true; 162fd859081SStefan Berger *selftest_done = false; 163fd859081SStefan Berger 164fd859081SStefan Berger is_selftest = tpm_passthrough_is_selftest(in, in_len); 16592dcc234SStefan Berger 16692dcc234SStefan Berger ret = tpm_passthrough_unix_write(tpm_pt->tpm_fd, in, in_len); 1674549a8b7SStefan Berger if (ret != in_len) { 16892dcc234SStefan Berger if (!tpm_pt->tpm_op_canceled || 16992dcc234SStefan Berger (tpm_pt->tpm_op_canceled && errno != ECANCELED)) { 1704549a8b7SStefan Berger error_report("tpm_passthrough: error while transmitting data " 17127215a22SGonglei "to TPM: %s (%i)", 1724549a8b7SStefan Berger strerror(errno), errno); 17392dcc234SStefan Berger } 1744549a8b7SStefan Berger goto err_exit; 1754549a8b7SStefan Berger } 1764549a8b7SStefan Berger 17792dcc234SStefan Berger tpm_pt->tpm_executing = false; 17892dcc234SStefan Berger 17992dcc234SStefan Berger ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len); 1804549a8b7SStefan Berger if (ret < 0) { 18192dcc234SStefan Berger if (!tpm_pt->tpm_op_canceled || 18292dcc234SStefan Berger (tpm_pt->tpm_op_canceled && errno != ECANCELED)) { 1834549a8b7SStefan Berger error_report("tpm_passthrough: error while reading data from " 18427215a22SGonglei "TPM: %s (%i)", 1854549a8b7SStefan Berger strerror(errno), errno); 18692dcc234SStefan Berger } 1874549a8b7SStefan Berger } else if (ret < sizeof(struct tpm_resp_hdr) || 1884549a8b7SStefan Berger tpm_passthrough_get_size_from_buffer(out) != ret) { 1894549a8b7SStefan Berger ret = -1; 1904549a8b7SStefan Berger error_report("tpm_passthrough: received invalid response " 19127215a22SGonglei "packet from TPM"); 1924549a8b7SStefan Berger } 1934549a8b7SStefan Berger 194fd859081SStefan Berger if (is_selftest && (ret >= sizeof(struct tpm_resp_hdr))) { 195fd859081SStefan Berger hdr = (struct tpm_resp_hdr *)out; 196fd859081SStefan Berger *selftest_done = (be32_to_cpu(hdr->errcode) == 0); 197fd859081SStefan Berger } 198fd859081SStefan Berger 1994549a8b7SStefan Berger err_exit: 2004549a8b7SStefan Berger if (ret < 0) { 2014549a8b7SStefan Berger tpm_write_fatal_error_response(out, out_len); 2024549a8b7SStefan Berger } 2034549a8b7SStefan Berger 20492dcc234SStefan Berger tpm_pt->tpm_executing = false; 20592dcc234SStefan Berger 2064549a8b7SStefan Berger return ret; 2074549a8b7SStefan Berger } 2084549a8b7SStefan Berger 20992dcc234SStefan Berger static int tpm_passthrough_unix_transfer(TPMPassthruState *tpm_pt, 210fd859081SStefan Berger const TPMLocality *locty_data, 211fd859081SStefan Berger bool *selftest_done) 2124549a8b7SStefan Berger { 21392dcc234SStefan Berger return tpm_passthrough_unix_tx_bufs(tpm_pt, 2144549a8b7SStefan Berger locty_data->w_buffer.buffer, 2154549a8b7SStefan Berger locty_data->w_offset, 2164549a8b7SStefan Berger locty_data->r_buffer.buffer, 217fd859081SStefan Berger locty_data->r_buffer.size, 218fd859081SStefan Berger selftest_done); 2194549a8b7SStefan Berger } 2204549a8b7SStefan Berger 2214549a8b7SStefan Berger static void tpm_passthrough_worker_thread(gpointer data, 2224549a8b7SStefan Berger gpointer user_data) 2234549a8b7SStefan Berger { 2244549a8b7SStefan Berger TPMPassthruThreadParams *thr_parms = user_data; 2258f0605ccSStefan Berger TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(thr_parms->tb); 2264549a8b7SStefan Berger TPMBackendCmd cmd = (TPMBackendCmd)data; 227fd859081SStefan Berger bool selftest_done = false; 2284549a8b7SStefan Berger 2294549a8b7SStefan Berger DPRINTF("tpm_passthrough: processing command type %d\n", cmd); 2304549a8b7SStefan Berger 2314549a8b7SStefan Berger switch (cmd) { 2324549a8b7SStefan Berger case TPM_BACKEND_CMD_PROCESS_CMD: 23392dcc234SStefan Berger tpm_passthrough_unix_transfer(tpm_pt, 234fd859081SStefan Berger thr_parms->tpm_state->locty_data, 235fd859081SStefan Berger &selftest_done); 2364549a8b7SStefan Berger 2374549a8b7SStefan Berger thr_parms->recv_data_callback(thr_parms->tpm_state, 238fd859081SStefan Berger thr_parms->tpm_state->locty_number, 239fd859081SStefan Berger selftest_done); 2404549a8b7SStefan Berger break; 2414549a8b7SStefan Berger case TPM_BACKEND_CMD_INIT: 2424549a8b7SStefan Berger case TPM_BACKEND_CMD_END: 2434549a8b7SStefan Berger case TPM_BACKEND_CMD_TPM_RESET: 2444549a8b7SStefan Berger /* nothing to do */ 2454549a8b7SStefan Berger break; 2464549a8b7SStefan Berger } 2474549a8b7SStefan Berger } 2484549a8b7SStefan Berger 2494549a8b7SStefan Berger /* 2504549a8b7SStefan Berger * Start the TPM (thread). If it had been started before, then terminate 2514549a8b7SStefan Berger * and start it again. 2524549a8b7SStefan Berger */ 2534549a8b7SStefan Berger static int tpm_passthrough_startup_tpm(TPMBackend *tb) 2544549a8b7SStefan Berger { 2558f0605ccSStefan Berger TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); 2564549a8b7SStefan Berger 2574549a8b7SStefan Berger /* terminate a running TPM */ 2584549a8b7SStefan Berger tpm_backend_thread_end(&tpm_pt->tbt); 2594549a8b7SStefan Berger 2604549a8b7SStefan Berger tpm_backend_thread_create(&tpm_pt->tbt, 2614549a8b7SStefan Berger tpm_passthrough_worker_thread, 2628f0605ccSStefan Berger &tpm_pt->tpm_thread_params); 2634549a8b7SStefan Berger 2644549a8b7SStefan Berger return 0; 2654549a8b7SStefan Berger } 2664549a8b7SStefan Berger 2674549a8b7SStefan Berger static void tpm_passthrough_reset(TPMBackend *tb) 2684549a8b7SStefan Berger { 2698f0605ccSStefan Berger TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); 2704549a8b7SStefan Berger 2714549a8b7SStefan Berger DPRINTF("tpm_passthrough: CALL TO TPM_RESET!\n"); 2724549a8b7SStefan Berger 27392dcc234SStefan Berger tpm_passthrough_cancel_cmd(tb); 27492dcc234SStefan Berger 2754549a8b7SStefan Berger tpm_backend_thread_end(&tpm_pt->tbt); 2764549a8b7SStefan Berger 2774549a8b7SStefan Berger tpm_pt->had_startup_error = false; 2784549a8b7SStefan Berger } 2794549a8b7SStefan Berger 2804549a8b7SStefan Berger static int tpm_passthrough_init(TPMBackend *tb, TPMState *s, 2814549a8b7SStefan Berger TPMRecvDataCB *recv_data_cb) 2824549a8b7SStefan Berger { 2838f0605ccSStefan Berger TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); 2844549a8b7SStefan Berger 2854549a8b7SStefan Berger tpm_pt->tpm_thread_params.tpm_state = s; 2864549a8b7SStefan Berger tpm_pt->tpm_thread_params.recv_data_callback = recv_data_cb; 2874549a8b7SStefan Berger tpm_pt->tpm_thread_params.tb = tb; 2884549a8b7SStefan Berger 2894549a8b7SStefan Berger return 0; 2904549a8b7SStefan Berger } 2914549a8b7SStefan Berger 2924549a8b7SStefan Berger static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb) 2934549a8b7SStefan Berger { 2944549a8b7SStefan Berger return false; 2954549a8b7SStefan Berger } 2964549a8b7SStefan Berger 297116694c3SStefan Berger static int tpm_passthrough_reset_tpm_established_flag(TPMBackend *tb, 298116694c3SStefan Berger uint8_t locty) 299116694c3SStefan Berger { 300116694c3SStefan Berger /* only a TPM 2.0 will support this */ 301116694c3SStefan Berger return 0; 302116694c3SStefan Berger } 303116694c3SStefan Berger 3044549a8b7SStefan Berger static bool tpm_passthrough_get_startup_error(TPMBackend *tb) 3054549a8b7SStefan Berger { 3068f0605ccSStefan Berger TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); 3074549a8b7SStefan Berger 3084549a8b7SStefan Berger return tpm_pt->had_startup_error; 3094549a8b7SStefan Berger } 3104549a8b7SStefan Berger 3114549a8b7SStefan Berger static size_t tpm_passthrough_realloc_buffer(TPMSizedBuffer *sb) 3124549a8b7SStefan Berger { 3134549a8b7SStefan Berger size_t wanted_size = 4096; /* Linux tpm.c buffer size */ 3144549a8b7SStefan Berger 3154549a8b7SStefan Berger if (sb->size != wanted_size) { 3164549a8b7SStefan Berger sb->buffer = g_realloc(sb->buffer, wanted_size); 3174549a8b7SStefan Berger sb->size = wanted_size; 3184549a8b7SStefan Berger } 3194549a8b7SStefan Berger return sb->size; 3204549a8b7SStefan Berger } 3214549a8b7SStefan Berger 3224549a8b7SStefan Berger static void tpm_passthrough_deliver_request(TPMBackend *tb) 3234549a8b7SStefan Berger { 3248f0605ccSStefan Berger TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); 3254549a8b7SStefan Berger 3264549a8b7SStefan Berger tpm_backend_thread_deliver_request(&tpm_pt->tbt); 3274549a8b7SStefan Berger } 3284549a8b7SStefan Berger 3294549a8b7SStefan Berger static void tpm_passthrough_cancel_cmd(TPMBackend *tb) 3304549a8b7SStefan Berger { 3318f0605ccSStefan Berger TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); 33292dcc234SStefan Berger int n; 33392dcc234SStefan Berger 33492dcc234SStefan Berger /* 33592dcc234SStefan Berger * As of Linux 3.7 the tpm_tis driver does not properly cancel 33692dcc234SStefan Berger * commands on all TPM manufacturers' TPMs. 33792dcc234SStefan Berger * Only cancel if we're busy so we don't cancel someone else's 33892dcc234SStefan Berger * command, e.g., a command executed on the host. 33992dcc234SStefan Berger */ 34092dcc234SStefan Berger if (tpm_pt->tpm_executing) { 34192dcc234SStefan Berger if (tpm_pt->cancel_fd >= 0) { 34292dcc234SStefan Berger n = write(tpm_pt->cancel_fd, "-", 1); 34392dcc234SStefan Berger if (n != 1) { 34427215a22SGonglei error_report("Canceling TPM command failed: %s", 34592dcc234SStefan Berger strerror(errno)); 34692dcc234SStefan Berger } else { 34792dcc234SStefan Berger tpm_pt->tpm_op_canceled = true; 34892dcc234SStefan Berger } 34992dcc234SStefan Berger } else { 35092dcc234SStefan Berger error_report("Cannot cancel TPM command due to missing " 35192dcc234SStefan Berger "TPM sysfs cancel entry"); 35292dcc234SStefan Berger } 35392dcc234SStefan Berger } 3544549a8b7SStefan Berger } 3554549a8b7SStefan Berger 3564549a8b7SStefan Berger static const char *tpm_passthrough_create_desc(void) 3574549a8b7SStefan Berger { 3584549a8b7SStefan Berger return "Passthrough TPM backend driver"; 3594549a8b7SStefan Berger } 3604549a8b7SStefan Berger 361116694c3SStefan Berger static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb) 362116694c3SStefan Berger { 36356a3c24fSStefan Berger TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); 364116694c3SStefan Berger 36556a3c24fSStefan Berger return tpm_pt->tpm_version; 3664549a8b7SStefan Berger } 3674549a8b7SStefan Berger 36892dcc234SStefan Berger /* 36992dcc234SStefan Berger * Unless path or file descriptor set has been provided by user, 37092dcc234SStefan Berger * determine the sysfs cancel file following kernel documentation 37192dcc234SStefan Berger * in Documentation/ABI/stable/sysfs-class-tpm. 3728e36d6caSStefan Berger * From /dev/tpm0 create /sys/class/misc/tpm0/device/cancel 37392dcc234SStefan Berger */ 37492dcc234SStefan Berger static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb) 37592dcc234SStefan Berger { 3768e36d6caSStefan Berger TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); 37792dcc234SStefan Berger int fd = -1; 3788e36d6caSStefan Berger char *dev; 37992dcc234SStefan Berger char path[PATH_MAX]; 38092dcc234SStefan Berger 38192dcc234SStefan Berger if (tb->cancel_path) { 38292dcc234SStefan Berger fd = qemu_open(tb->cancel_path, O_WRONLY); 38392dcc234SStefan Berger if (fd < 0) { 38492dcc234SStefan Berger error_report("Could not open TPM cancel path : %s", 38592dcc234SStefan Berger strerror(errno)); 38692dcc234SStefan Berger } 38792dcc234SStefan Berger return fd; 38892dcc234SStefan Berger } 38992dcc234SStefan Berger 3908e36d6caSStefan Berger dev = strrchr(tpm_pt->tpm_dev, '/'); 3918e36d6caSStefan Berger if (dev) { 3928e36d6caSStefan Berger dev++; 3938e36d6caSStefan Berger if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel", 3948e36d6caSStefan Berger dev) < sizeof(path)) { 39592dcc234SStefan Berger fd = qemu_open(path, O_WRONLY); 39692dcc234SStefan Berger if (fd >= 0) { 39792dcc234SStefan Berger tb->cancel_path = g_strdup(path); 3988e36d6caSStefan Berger } else { 3998e36d6caSStefan Berger error_report("tpm_passthrough: Could not open TPM cancel " 4008e36d6caSStefan Berger "path %s : %s", path, strerror(errno)); 4018e36d6caSStefan Berger } 4028e36d6caSStefan Berger } 4038e36d6caSStefan Berger } else { 4048e36d6caSStefan Berger error_report("tpm_passthrough: Bad TPM device path %s", 4058e36d6caSStefan Berger tpm_pt->tpm_dev); 40692dcc234SStefan Berger } 40792dcc234SStefan Berger 40892dcc234SStefan Berger return fd; 40992dcc234SStefan Berger } 41092dcc234SStefan Berger 4114549a8b7SStefan Berger static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb) 4124549a8b7SStefan Berger { 4138f0605ccSStefan Berger TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); 4144549a8b7SStefan Berger const char *value; 4154549a8b7SStefan Berger 41692dcc234SStefan Berger value = qemu_opt_get(opts, "cancel-path"); 41792dcc234SStefan Berger tb->cancel_path = g_strdup(value); 41892dcc234SStefan Berger 4194549a8b7SStefan Berger value = qemu_opt_get(opts, "path"); 4204549a8b7SStefan Berger if (!value) { 4214549a8b7SStefan Berger value = TPM_PASSTHROUGH_DEFAULT_DEVICE; 4224549a8b7SStefan Berger } 4234549a8b7SStefan Berger 4248f0605ccSStefan Berger tpm_pt->tpm_dev = g_strdup(value); 4254549a8b7SStefan Berger 4268f0605ccSStefan Berger tb->path = g_strdup(tpm_pt->tpm_dev); 4274549a8b7SStefan Berger 4288f0605ccSStefan Berger tpm_pt->tpm_fd = qemu_open(tpm_pt->tpm_dev, O_RDWR); 4298f0605ccSStefan Berger if (tpm_pt->tpm_fd < 0) { 43027215a22SGonglei error_report("Cannot access TPM device using '%s': %s", 4318f0605ccSStefan Berger tpm_pt->tpm_dev, strerror(errno)); 4324549a8b7SStefan Berger goto err_free_parameters; 4334549a8b7SStefan Berger } 4344549a8b7SStefan Berger 43556a3c24fSStefan Berger if (tpm_util_test_tpmdev(tpm_pt->tpm_fd, &tpm_pt->tpm_version)) { 43627215a22SGonglei error_report("'%s' is not a TPM device.", 4378f0605ccSStefan Berger tpm_pt->tpm_dev); 4384549a8b7SStefan Berger goto err_close_tpmdev; 4394549a8b7SStefan Berger } 4404549a8b7SStefan Berger 4414549a8b7SStefan Berger return 0; 4424549a8b7SStefan Berger 4434549a8b7SStefan Berger err_close_tpmdev: 4448f0605ccSStefan Berger qemu_close(tpm_pt->tpm_fd); 4458f0605ccSStefan Berger tpm_pt->tpm_fd = -1; 4464549a8b7SStefan Berger 4474549a8b7SStefan Berger err_free_parameters: 4484549a8b7SStefan Berger g_free(tb->path); 4494549a8b7SStefan Berger tb->path = NULL; 4504549a8b7SStefan Berger 4518f0605ccSStefan Berger g_free(tpm_pt->tpm_dev); 4528f0605ccSStefan Berger tpm_pt->tpm_dev = NULL; 4534549a8b7SStefan Berger 4544549a8b7SStefan Berger return 1; 4554549a8b7SStefan Berger } 4564549a8b7SStefan Berger 4574549a8b7SStefan Berger static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id) 4584549a8b7SStefan Berger { 4598f0605ccSStefan Berger Object *obj = object_new(TYPE_TPM_PASSTHROUGH); 4608f0605ccSStefan Berger TPMBackend *tb = TPM_BACKEND(obj); 4618f0605ccSStefan Berger TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); 4624549a8b7SStefan Berger 4634549a8b7SStefan Berger tb->id = g_strdup(id); 4644549a8b7SStefan Berger /* let frontend set the fe_model to proper value */ 4654549a8b7SStefan Berger tb->fe_model = -1; 4664549a8b7SStefan Berger 4674549a8b7SStefan Berger tb->ops = &tpm_passthrough_driver; 4684549a8b7SStefan Berger 4694549a8b7SStefan Berger if (tpm_passthrough_handle_device_opts(opts, tb)) { 4704549a8b7SStefan Berger goto err_exit; 4714549a8b7SStefan Berger } 4724549a8b7SStefan Berger 4738f0605ccSStefan Berger tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tb); 4748f0605ccSStefan Berger if (tpm_pt->cancel_fd < 0) { 47592dcc234SStefan Berger goto err_exit; 47692dcc234SStefan Berger } 47792dcc234SStefan Berger 4784549a8b7SStefan Berger return tb; 4794549a8b7SStefan Berger 4804549a8b7SStefan Berger err_exit: 4814549a8b7SStefan Berger g_free(tb->id); 4824549a8b7SStefan Berger 4834549a8b7SStefan Berger return NULL; 4844549a8b7SStefan Berger } 4854549a8b7SStefan Berger 4864549a8b7SStefan Berger static void tpm_passthrough_destroy(TPMBackend *tb) 4874549a8b7SStefan Berger { 4888f0605ccSStefan Berger TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); 4894549a8b7SStefan Berger 49092dcc234SStefan Berger tpm_passthrough_cancel_cmd(tb); 49192dcc234SStefan Berger 4924549a8b7SStefan Berger tpm_backend_thread_end(&tpm_pt->tbt); 4934549a8b7SStefan Berger 4944549a8b7SStefan Berger qemu_close(tpm_pt->tpm_fd); 4958f0605ccSStefan Berger qemu_close(tpm_pt->cancel_fd); 4964549a8b7SStefan Berger 4974549a8b7SStefan Berger g_free(tb->id); 4984549a8b7SStefan Berger g_free(tb->path); 49992dcc234SStefan Berger g_free(tb->cancel_path); 5008f0605ccSStefan Berger g_free(tpm_pt->tpm_dev); 5014549a8b7SStefan Berger } 5024549a8b7SStefan Berger 503bb716238SStefan Berger static const QemuOptDesc tpm_passthrough_cmdline_opts[] = { 504bb716238SStefan Berger TPM_STANDARD_CMDLINE_OPTS, 505bb716238SStefan Berger { 506bb716238SStefan Berger .name = "cancel-path", 507bb716238SStefan Berger .type = QEMU_OPT_STRING, 508bb716238SStefan Berger .help = "Sysfs file entry for canceling TPM commands", 509bb716238SStefan Berger }, 510bb716238SStefan Berger { 511bb716238SStefan Berger .name = "path", 512bb716238SStefan Berger .type = QEMU_OPT_STRING, 513bb716238SStefan Berger .help = "Path to TPM device on the host", 514bb716238SStefan Berger }, 515bb716238SStefan Berger { /* end of list */ }, 516bb716238SStefan Berger }; 517bb716238SStefan Berger 518bdee56f5SPaolo Bonzini static const TPMDriverOps tpm_passthrough_driver = { 5194549a8b7SStefan Berger .type = TPM_TYPE_PASSTHROUGH, 520bb716238SStefan Berger .opts = tpm_passthrough_cmdline_opts, 5214549a8b7SStefan Berger .desc = tpm_passthrough_create_desc, 5224549a8b7SStefan Berger .create = tpm_passthrough_create, 5234549a8b7SStefan Berger .destroy = tpm_passthrough_destroy, 5244549a8b7SStefan Berger .init = tpm_passthrough_init, 5254549a8b7SStefan Berger .startup_tpm = tpm_passthrough_startup_tpm, 5264549a8b7SStefan Berger .realloc_buffer = tpm_passthrough_realloc_buffer, 5274549a8b7SStefan Berger .reset = tpm_passthrough_reset, 5284549a8b7SStefan Berger .had_startup_error = tpm_passthrough_get_startup_error, 5294549a8b7SStefan Berger .deliver_request = tpm_passthrough_deliver_request, 5304549a8b7SStefan Berger .cancel_cmd = tpm_passthrough_cancel_cmd, 5314549a8b7SStefan Berger .get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag, 532116694c3SStefan Berger .reset_tpm_established_flag = tpm_passthrough_reset_tpm_established_flag, 533116694c3SStefan Berger .get_tpm_version = tpm_passthrough_get_tpm_version, 5344549a8b7SStefan Berger }; 5354549a8b7SStefan Berger 5368f0605ccSStefan Berger static void tpm_passthrough_inst_init(Object *obj) 5378f0605ccSStefan Berger { 5388f0605ccSStefan Berger } 5398f0605ccSStefan Berger 5408f0605ccSStefan Berger static void tpm_passthrough_inst_finalize(Object *obj) 5418f0605ccSStefan Berger { 5428f0605ccSStefan Berger } 5438f0605ccSStefan Berger 5448f0605ccSStefan Berger static void tpm_passthrough_class_init(ObjectClass *klass, void *data) 5458f0605ccSStefan Berger { 5468f0605ccSStefan Berger TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass); 5478f0605ccSStefan Berger 5488f0605ccSStefan Berger tbc->ops = &tpm_passthrough_driver; 5498f0605ccSStefan Berger } 5508f0605ccSStefan Berger 5518f0605ccSStefan Berger static const TypeInfo tpm_passthrough_info = { 5528f0605ccSStefan Berger .name = TYPE_TPM_PASSTHROUGH, 5538f0605ccSStefan Berger .parent = TYPE_TPM_BACKEND, 5548f0605ccSStefan Berger .instance_size = sizeof(TPMPassthruState), 5558f0605ccSStefan Berger .class_init = tpm_passthrough_class_init, 5568f0605ccSStefan Berger .instance_init = tpm_passthrough_inst_init, 5578f0605ccSStefan Berger .instance_finalize = tpm_passthrough_inst_finalize, 5588f0605ccSStefan Berger }; 5598f0605ccSStefan Berger 5604549a8b7SStefan Berger static void tpm_passthrough_register(void) 5614549a8b7SStefan Berger { 5628f0605ccSStefan Berger type_register_static(&tpm_passthrough_info); 5634549a8b7SStefan Berger tpm_register_driver(&tpm_passthrough_driver); 5644549a8b7SStefan Berger } 5654549a8b7SStefan Berger 5664549a8b7SStefan Berger type_init(tpm_passthrough_register) 567