diff options
Diffstat (limited to 'tpm/tpm_passthrough.c')
| -rw-r--r-- | tpm/tpm_passthrough.c | 560 |
1 files changed, 0 insertions, 560 deletions
diff --git a/tpm/tpm_passthrough.c b/tpm/tpm_passthrough.c deleted file mode 100644 index 1fdd66d356..0000000000 --- a/tpm/tpm_passthrough.c +++ /dev/null @@ -1,560 +0,0 @@ -/* - * 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 <dirent.h> - -#include "qemu-common.h" -#include "qapi/error.h" -#include "qemu/sockets.h" -#include "backends/tpm.h" -#include "tpm_int.h" -#include "hw/hw.h" -#include "hw/i386/pc.h" -#include "tpm_tis.h" -#include "tpm_backend.h" - -/* #define DEBUG_TPM */ - -#ifdef DEBUG_TPM -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) -#endif - -#define TYPE_TPM_PASSTHROUGH "tpm-passthrough" -#define TPM_PASSTHROUGH(obj) \ - OBJECT_CHECK(TPMPassthruState, (obj), TYPE_TPM_PASSTHROUGH) - -/* data structures */ -typedef struct TPMPassthruThreadParams { - TPMState *tpm_state; - - TPMRecvDataCB *recv_data_callback; - TPMBackend *tb; -} TPMPassthruThreadParams; - -struct TPMPassthruState { - TPMBackend parent; - - TPMBackendThread tbt; - - TPMPassthruThreadParams tpm_thread_params; - - char *tpm_dev; - int tpm_fd; - bool tpm_executing; - bool tpm_op_canceled; - int cancel_fd; - bool had_startup_error; -}; - -typedef struct TPMPassthruState TPMPassthruState; - -#define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0" - -/* functions */ - -static void tpm_passthrough_cancel_cmd(TPMBackend *tb); - -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); -} - -static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt, - const uint8_t *in, uint32_t in_len, - uint8_t *out, uint32_t out_len) -{ - int ret; - - tpm_pt->tpm_op_canceled = false; - tpm_pt->tpm_executing = true; - - ret = tpm_passthrough_unix_write(tpm_pt->tpm_fd, in, in_len); - if (ret != in_len) { - if (!tpm_pt->tpm_op_canceled || - (tpm_pt->tpm_op_canceled && errno != ECANCELED)) { - error_report("tpm_passthrough: error while transmitting data " - "to TPM: %s (%i)\n", - strerror(errno), errno); - } - goto err_exit; - } - - tpm_pt->tpm_executing = false; - - ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len); - if (ret < 0) { - if (!tpm_pt->tpm_op_canceled || - (tpm_pt->tpm_op_canceled && errno != ECANCELED)) { - error_report("tpm_passthrough: error while reading data from " - "TPM: %s (%i)\n", - strerror(errno), errno); - } - } 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 " - "packet from TPM\n"); - } - -err_exit: - if (ret < 0) { - tpm_write_fatal_error_response(out, out_len); - } - - tpm_pt->tpm_executing = false; - - return ret; -} - -static int tpm_passthrough_unix_transfer(TPMPassthruState *tpm_pt, - const TPMLocality *locty_data) -{ - return tpm_passthrough_unix_tx_bufs(tpm_pt, - locty_data->w_buffer.buffer, - locty_data->w_offset, - locty_data->r_buffer.buffer, - locty_data->r_buffer.size); -} - -static void tpm_passthrough_worker_thread(gpointer data, - gpointer user_data) -{ - TPMPassthruThreadParams *thr_parms = user_data; - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(thr_parms->tb); - TPMBackendCmd cmd = (TPMBackendCmd)data; - - DPRINTF("tpm_passthrough: processing command type %d\n", cmd); - - switch (cmd) { - case TPM_BACKEND_CMD_PROCESS_CMD: - tpm_passthrough_unix_transfer(tpm_pt, - thr_parms->tpm_state->locty_data); - - thr_parms->recv_data_callback(thr_parms->tpm_state, - thr_parms->tpm_state->locty_number); - 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) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - - /* terminate a running TPM */ - tpm_backend_thread_end(&tpm_pt->tbt); - - tpm_backend_thread_create(&tpm_pt->tbt, - tpm_passthrough_worker_thread, - &tpm_pt->tpm_thread_params); - - return 0; -} - -static void tpm_passthrough_reset(TPMBackend *tb) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - - DPRINTF("tpm_passthrough: CALL TO TPM_RESET!\n"); - - tpm_passthrough_cancel_cmd(tb); - - 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) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - - 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; -} - -static bool tpm_passthrough_get_startup_error(TPMBackend *tb) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - - 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) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - - tpm_backend_thread_deliver_request(&tpm_pt->tbt); -} - -static void tpm_passthrough_cancel_cmd(TPMBackend *tb) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - 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) { - error_report("Canceling TPM command failed: %s\n", - strerror(errno)); - } else { - tpm_pt->tpm_op_canceled = true; - } - } else { - error_report("Cannot cancel TPM command due to missing " - "TPM sysfs cancel entry"); - } - } -} - -static const char *tpm_passthrough_create_desc(void) -{ - return "Passthrough TPM backend driver"; -} - -/* - * A basic test of a TPM device. We expect a well formatted response header - * (error response is fine) within one second. - */ -static int tpm_passthrough_test_tpmdev(int fd) -{ - struct tpm_req_hdr req = { - .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND), - .len = cpu_to_be32(sizeof(req)), - .ordinal = cpu_to_be32(TPM_ORD_GetTicks), - }; - struct tpm_resp_hdr *resp; - fd_set readfds; - int n; - struct timeval tv = { - .tv_sec = 1, - .tv_usec = 0, - }; - unsigned char buf[1024]; - - n = write(fd, &req, sizeof(req)); - if (n < 0) { - return errno; - } - if (n != sizeof(req)) { - return EFAULT; - } - - FD_ZERO(&readfds); - FD_SET(fd, &readfds); - - /* wait for a second */ - n = select(fd + 1, &readfds, NULL, NULL, &tv); - if (n != 1) { - return errno; - } - - n = read(fd, &buf, sizeof(buf)); - if (n < sizeof(struct tpm_resp_hdr)) { - return EFAULT; - } - - resp = (struct tpm_resp_hdr *)buf; - /* check the header */ - if (be16_to_cpu(resp->tag) != TPM_TAG_RSP_COMMAND || - be32_to_cpu(resp->len) != n) { - return EBADMSG; - } - - return 0; -} - -/* - * Check whether the given base path, e.g., /sys/class/misc/tpm0/device, - * is the sysfs directory of a TPM. A TPM sysfs directory should be uniquely - * recognizable by the file entries 'pcrs' and 'cancel'. - * Upon success 'true' is returned and the basebath buffer has '/cancel' - * appended. - */ -static bool tpm_passthrough_check_sysfs_cancel(char *basepath, size_t bufsz) -{ - char path[PATH_MAX]; - struct stat statbuf; - - snprintf(path, sizeof(path), "%s/pcrs", basepath); - if (stat(path, &statbuf) == -1 || !S_ISREG(statbuf.st_mode)) { - return false; - } - - snprintf(path, sizeof(path), "%s/cancel", basepath); - if (stat(path, &statbuf) == -1 || !S_ISREG(statbuf.st_mode)) { - return false; - } - - strncpy(basepath, path, bufsz); - - return true; -} - -/* - * 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. - */ -static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb) -{ - int fd = -1; - unsigned int idx; - DIR *pnp_dir; - char path[PATH_MAX]; - struct dirent entry, *result; - int len; - - 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; - } - - snprintf(path, sizeof(path), "/sys/class/misc"); - pnp_dir = opendir(path); - if (pnp_dir != NULL) { - while (readdir_r(pnp_dir, &entry, &result) == 0 && - result != NULL) { - /* - * only allow /sys/class/misc/tpm%u type of paths - */ - if (sscanf(entry.d_name, "tpm%u%n", &idx, &len) < 1 || - len <= strlen("tpm") || - len != strlen(entry.d_name)) { - continue; - } - - snprintf(path, sizeof(path), "/sys/class/misc/%s/device", - entry.d_name); - if (!tpm_passthrough_check_sysfs_cancel(path, sizeof(path))) { - continue; - } - - fd = qemu_open(path, O_WRONLY); - break; - } - closedir(pnp_dir); - } - - if (fd >= 0) { - tb->cancel_path = g_strdup(path); - } - - return fd; -} - -static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - const char *value; - - value = qemu_opt_get(opts, "cancel-path"); - if (value) { - tb->cancel_path = g_strdup(value); - } - - value = qemu_opt_get(opts, "path"); - if (!value) { - value = TPM_PASSTHROUGH_DEFAULT_DEVICE; - } - - tpm_pt->tpm_dev = g_strdup(value); - - tb->path = g_strdup(tpm_pt->tpm_dev); - - tpm_pt->tpm_fd = qemu_open(tpm_pt->tpm_dev, O_RDWR); - if (tpm_pt->tpm_fd < 0) { - error_report("Cannot access TPM device using '%s': %s\n", - tpm_pt->tpm_dev, strerror(errno)); - goto err_free_parameters; - } - - if (tpm_passthrough_test_tpmdev(tpm_pt->tpm_fd)) { - error_report("'%s' is not a TPM device.\n", - tpm_pt->tpm_dev); - goto err_close_tpmdev; - } - - return 0; - - err_close_tpmdev: - qemu_close(tpm_pt->tpm_fd); - tpm_pt->tpm_fd = -1; - - err_free_parameters: - g_free(tb->path); - tb->path = NULL; - - g_free(tpm_pt->tpm_dev); - tpm_pt->tpm_dev = NULL; - - return 1; -} - -static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id) -{ - Object *obj = object_new(TYPE_TPM_PASSTHROUGH); - TPMBackend *tb = TPM_BACKEND(obj); - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - - 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; - } - - tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tb); - if (tpm_pt->cancel_fd < 0) { - goto err_exit; - } - - return tb; - -err_exit: - g_free(tb->id); - - return NULL; -} - -static void tpm_passthrough_destroy(TPMBackend *tb) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - - tpm_passthrough_cancel_cmd(tb); - - tpm_backend_thread_end(&tpm_pt->tbt); - - qemu_close(tpm_pt->tpm_fd); - qemu_close(tpm_pt->cancel_fd); - - g_free(tb->id); - g_free(tb->path); - g_free(tb->cancel_path); - g_free(tpm_pt->tpm_dev); -} - -const TPMDriverOps tpm_passthrough_driver = { - .type = TPM_TYPE_PASSTHROUGH, - .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, -}; - -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, -}; - -static void tpm_passthrough_register(void) -{ - type_register_static(&tpm_passthrough_info); - tpm_register_driver(&tpm_passthrough_driver); -} - -type_init(tpm_passthrough_register) |