#include "tpm_int.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
-#include "sysemu/tpm_backend_int.h"
-#include "tpm_tis.h"
+#include "qapi/clone-visitor.h"
#include "tpm_util.h"
#define DEBUG_TPM 0
#define TPM_PASSTHROUGH(obj) \
OBJECT_CHECK(TPMPassthruState, (obj), TYPE_TPM_PASSTHROUGH)
-static const TPMDriverOps tpm_passthrough_driver;
-
/* 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;
+ TPMPassthroughOptions *options;
+ const char *tpm_dev;
int tpm_fd;
bool tpm_executing;
bool tpm_op_canceled;
int cancel_fd;
- bool had_startup_error;
TPMVersion tpm_version;
+ size_t tpm_buffersize;
};
typedef struct TPMPassthruState TPMPassthruState;
static void tpm_passthrough_cancel_cmd(TPMBackend *tb);
-static int tpm_passthrough_unix_write(int fd, const uint8_t *buf, uint32_t len)
-{
- int ret, remain;
-
- remain = len;
- while (remain > 0) {
- ret = write(fd, buf, remain);
- if (ret < 0) {
- if (errno != EINTR && errno != EAGAIN) {
- return -1;
- }
- } else if (ret == 0) {
- break;
- } else {
- buf += ret;
- remain -= ret;
- }
- }
- return len - remain;
-}
-
static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
{
int ret;
}
return ret;
}
-
-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);
-}
-
-/*
- * 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);
- }
-}
-
-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;
-}
-
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,
bool *selftest_done)
{
- int ret;
+ ssize_t ret;
bool is_selftest;
const struct tpm_resp_hdr *hdr;
+ /* FIXME: protect shared variables or use other sync mechanism */
tpm_pt->tpm_op_canceled = false;
tpm_pt->tpm_executing = true;
*selftest_done = false;
- is_selftest = tpm_passthrough_is_selftest(in, in_len);
+ is_selftest = tpm_util_is_selftest(in, in_len);
- ret = tpm_passthrough_unix_write(tpm_pt->tpm_fd, in, in_len);
+ ret = qemu_write_full(tpm_pt->tpm_fd, in, in_len);
if (ret != in_len) {
if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) {
error_report("tpm_passthrough: error while transmitting data "
strerror(errno), errno);
}
} else if (ret < sizeof(struct tpm_resp_hdr) ||
- tpm_passthrough_get_size_from_buffer(out) != ret) {
+ be32_to_cpu(((struct tpm_resp_hdr *)out)->len) != ret) {
ret = -1;
error_report("tpm_passthrough: received invalid response "
"packet from TPM");
err_exit:
if (ret < 0) {
- tpm_write_fatal_error_response(out, out_len);
+ tpm_util_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,
- bool *selftest_done)
-{
- 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,
- selftest_done);
-}
-
-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;
- bool selftest_done = false;
-
- 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,
- &selftest_done);
-
- thr_parms->recv_data_callback(thr_parms->tpm_state,
- thr_parms->tpm_state->locty_number,
- selftest_done);
- 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)
+static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd *cmd)
{
TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
- /* terminate a running TPM */
- tpm_backend_thread_end(&tpm_pt->tbt);
+ DPRINTF("tpm_passthrough: processing command %p\n", cmd);
- tpm_backend_thread_create(&tpm_pt->tbt,
- tpm_passthrough_worker_thread,
- &tpm_pt->tpm_thread_params);
-
- return 0;
+ tpm_passthrough_unix_tx_bufs(tpm_pt, cmd->in, cmd->in_len,
+ cmd->out, cmd->out_len, &cmd->selftest_done);
}
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 0;
}
-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);
*/
if (tpm_pt->tpm_executing) {
if (tpm_pt->cancel_fd >= 0) {
+ tpm_pt->tpm_op_canceled = true;
n = write(tpm_pt->cancel_fd, "-", 1);
if (n != 1) {
error_report("Canceling TPM command failed: %s",
strerror(errno));
- } else {
- tpm_pt->tpm_op_canceled = true;
}
} else {
error_report("Cannot cancel TPM command due to missing "
}
}
-static const char *tpm_passthrough_create_desc(void)
+static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb)
{
- return "Passthrough TPM backend driver";
+ TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
+
+ return tpm_pt->tpm_version;
}
-static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb)
+static size_t tpm_passthrough_get_buffer_size(TPMBackend *tb)
{
TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
+ int ret;
- return tpm_pt->tpm_version;
+ ret = tpm_util_get_buffer_size(tpm_pt->tpm_fd, tpm_pt->tpm_version,
+ &tpm_pt->tpm_buffersize);
+ if (ret < 0) {
+ tpm_pt->tpm_buffersize = 4096;
+ }
+ return tpm_pt->tpm_buffersize;
}
/*
* in Documentation/ABI/stable/sysfs-class-tpm.
* From /dev/tpm0 create /sys/class/misc/tpm0/device/cancel
*/
-static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb)
+static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt)
{
- TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
int fd = -1;
char *dev;
char path[PATH_MAX];
- if (tb->cancel_path) {
- fd = qemu_open(tb->cancel_path, O_WRONLY);
+ if (tpm_pt->options->cancel_path) {
+ fd = qemu_open(tpm_pt->options->cancel_path, O_WRONLY);
if (fd < 0) {
error_report("Could not open TPM cancel path : %s",
strerror(errno));
if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel",
dev) < sizeof(path)) {
fd = qemu_open(path, O_WRONLY);
- if (fd >= 0) {
- tb->cancel_path = g_strdup(path);
- } else {
+ if (fd < 0) {
error_report("tpm_passthrough: Could not open TPM cancel "
"path %s : %s", path, strerror(errno));
}
return fd;
}
-static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
+static int
+tpm_passthrough_handle_device_opts(TPMPassthruState *tpm_pt, QemuOpts *opts)
{
- TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
const char *value;
value = qemu_opt_get(opts, "cancel-path");
- tb->cancel_path = g_strdup(value);
+ if (value) {
+ tpm_pt->options->cancel_path = g_strdup(value);
+ tpm_pt->options->has_cancel_path = true;
+ }
value = qemu_opt_get(opts, "path");
- if (!value) {
- value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
+ if (value) {
+ tpm_pt->options->has_path = true;
+ tpm_pt->options->path = g_strdup(value);
}
- tpm_pt->tpm_dev = g_strdup(value);
-
- tb->path = g_strdup(tpm_pt->tpm_dev);
-
+ tpm_pt->tpm_dev = value ? value : TPM_PASSTHROUGH_DEFAULT_DEVICE;
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",
tpm_pt->tpm_dev, strerror(errno));
- goto err_free_parameters;
+ return -1;
}
if (tpm_util_test_tpmdev(tpm_pt->tpm_fd, &tpm_pt->tpm_version)) {
error_report("'%s' is not a TPM device.",
tpm_pt->tpm_dev);
- goto err_close_tpmdev;
+ return -1;
}
- 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;
+ tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tpm_pt);
+ if (tpm_pt->cancel_fd < 0) {
+ return -1;
+ }
- return 1;
+ return 0;
}
-static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
+static TPMBackend *tpm_passthrough_create(QemuOpts *opts)
{
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;
+ if (tpm_passthrough_handle_device_opts(TPM_PASSTHROUGH(obj), opts)) {
+ object_unref(obj);
+ return NULL;
}
- 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;
+ return TPM_BACKEND(obj);
}
-static void tpm_passthrough_destroy(TPMBackend *tb)
+static int tpm_passthrough_startup_tpm(TPMBackend *tb, size_t buffersize)
{
TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
- tpm_passthrough_cancel_cmd(tb);
+ if (buffersize && buffersize < tpm_pt->tpm_buffersize) {
+ error_report("Requested buffer size of %zu is smaller than host TPM's "
+ "fixed buffer size of %zu",
+ buffersize, tpm_pt->tpm_buffersize);
+ return -1;
+ }
- tpm_backend_thread_end(&tpm_pt->tbt);
+ return 0;
+}
- qemu_close(tpm_pt->tpm_fd);
- qemu_close(tpm_pt->cancel_fd);
+static TpmTypeOptions *tpm_passthrough_get_tpm_options(TPMBackend *tb)
+{
+ TpmTypeOptions *options = g_new0(TpmTypeOptions, 1);
+
+ options->type = TPM_TYPE_OPTIONS_KIND_PASSTHROUGH;
+ options->u.passthrough.data = QAPI_CLONE(TPMPassthroughOptions,
+ TPM_PASSTHROUGH(tb)->options);
- g_free(tb->id);
- g_free(tb->path);
- g_free(tb->cancel_path);
- g_free(tpm_pt->tpm_dev);
+ return options;
}
static const QemuOptDesc tpm_passthrough_cmdline_opts[] = {
{ /* end of list */ },
};
-static const TPMDriverOps tpm_passthrough_driver = {
- .type = TPM_TYPE_PASSTHROUGH,
- .opts = tpm_passthrough_cmdline_opts,
- .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,
- .reset_tpm_established_flag = tpm_passthrough_reset_tpm_established_flag,
- .get_tpm_version = tpm_passthrough_get_tpm_version,
-};
-
static void tpm_passthrough_inst_init(Object *obj)
{
+ TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(obj);
+
+ tpm_pt->options = g_new0(TPMPassthroughOptions, 1);
+ tpm_pt->tpm_fd = -1;
+ tpm_pt->cancel_fd = -1;
}
static void tpm_passthrough_inst_finalize(Object *obj)
{
+ TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(obj);
+
+ tpm_passthrough_cancel_cmd(TPM_BACKEND(obj));
+
+ if (tpm_pt->tpm_fd >= 0) {
+ qemu_close(tpm_pt->tpm_fd);
+ }
+ if (tpm_pt->cancel_fd >= 0) {
+ qemu_close(tpm_pt->cancel_fd);
+ }
+ qapi_free_TPMPassthroughOptions(tpm_pt->options);
}
static void tpm_passthrough_class_init(ObjectClass *klass, void *data)
{
TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);
- tbc->ops = &tpm_passthrough_driver;
+ tbc->type = TPM_TYPE_PASSTHROUGH;
+ tbc->opts = tpm_passthrough_cmdline_opts;
+ tbc->desc = "Passthrough TPM backend driver";
+ tbc->create = tpm_passthrough_create;
+ tbc->startup_tpm = tpm_passthrough_startup_tpm;
+ tbc->reset = tpm_passthrough_reset;
+ tbc->cancel_cmd = tpm_passthrough_cancel_cmd;
+ tbc->get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag;
+ tbc->reset_tpm_established_flag =
+ tpm_passthrough_reset_tpm_established_flag;
+ tbc->get_tpm_version = tpm_passthrough_get_tpm_version;
+ tbc->get_buffer_size = tpm_passthrough_get_buffer_size;
+ tbc->get_tpm_options = tpm_passthrough_get_tpm_options;
+ tbc->handle_request = tpm_passthrough_handle_request;
}
static const TypeInfo tpm_passthrough_info = {
static void tpm_passthrough_register(void)
{
type_register_static(&tpm_passthrough_info);
- tpm_register_driver(&tpm_passthrough_driver);
}
type_init(tpm_passthrough_register)