static int qemu_parse_fdset(const char *param) { return qemu_parse_fd(param); }
/* * 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; } static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb) { const char *value; struct stat statbuf; value = qemu_opt_get(opts, "fd"); if (value) { if (qemu_opt_get(opts, "path")) { error_report("fd= is invalid with path="); goto err_exit; } tb->s.tpm_pt->tpm_fd = qemu_parse_fd(value); if (tb->s.tpm_pt->tpm_fd < 0) { error_report("Illegal file descriptor for TPM device.\n"); goto err_exit; } tb->tpm_fd = &tb->s.tpm_pt->tpm_fd; } else { value = qemu_opt_get(opts, "path"); if (!value) { value = TPM_PASSTHROUGH_DEFAULT_DEVICE; } tb->s.tpm_pt->tpm_dev = g_strdup(value); tb->path = g_strdup(value); tb->s.tpm_pt->tpm_fd = open(tb->s.tpm_pt->tpm_dev, O_RDWR); if (tb->s.tpm_pt->tpm_fd < 0) { error_report("Cannot access TPM device using '%s'.\n", tb->s.tpm_pt->tpm_dev); goto err_free_parameters; } } if (fstat(tb->s.tpm_pt->tpm_fd, &statbuf) != 0) { error_report("Cannot determine file descriptor type for TPM " "device: %s", strerror(errno)); goto err_close_tpmdev; } /* only allow character devices for now */ if (!S_ISCHR(statbuf.st_mode)) { error_report("TPM file descriptor is not a character device"); goto err_free_parameters; } if (tpm_passthrough_test_tpmdev(tb->s.tpm_pt->tpm_fd)) { error_report("Device is not a TPM.\n"); goto err_close_tpmdev; } return 0; err_close_tpmdev: close(tb->s.tpm_pt->tpm_fd); tb->s.tpm_pt->tpm_fd = -1; err_free_parameters: g_free(tb->path); tb->path = NULL; g_free(tb->s.tpm_pt->tpm_dev); tb->s.tpm_pt->tpm_dev = NULL; err_exit: return 1; } static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id) { TPMBackend *tb; tb = g_new0(TPMBackend, 1); tb->s.tpm_pt = g_new0(TPMPassthruState, 1); tb->id = g_strdup(id); tb->ops = &tpm_passthrough_driver; if (tpm_passthrough_handle_device_opts(opts, tb)) { goto err_exit; } return tb; err_exit: g_free(tb->id); g_free(tb->s.tpm_pt); g_free(tb); return NULL; } static void tpm_passthrough_destroy(TPMBackend *tb) { TPMPassthruState *tpm_pt = tb->s.tpm_pt; tpm_backend_thread_end(&tpm_pt->tbt); close(tpm_pt->tpm_fd); g_free(tb->id); g_free(tb->path); g_free(tb->s.tpm_pt->tpm_dev); g_free(tb->s.tpm_pt); g_free(tb); }