/* * QEMU TPM Backend * * Copyright IBM, Corp. 2013 * * Authors: * Stefan Berger * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. * * Based on backends/rng.c by Anthony Liguori */ #include "qemu/osdep.h" #include "sysemu/tpm_backend.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "sysemu/tpm.h" #include "qemu/thread.h" #include "qemu/main-loop.h" #include "block/thread-pool.h" #include "qemu/error-report.h" static void tpm_backend_request_completed(void *opaque, int ret) { TPMBackend *s = TPM_BACKEND(opaque); TPMIfClass *tic = TPM_IF_GET_CLASS(s->tpmif); tic->request_completed(s->tpmif); /* no need for atomic, as long the BQL is taken */ s->cmd = NULL; object_unref(OBJECT(s)); } static int tpm_backend_worker_thread(gpointer data) { TPMBackend *s = TPM_BACKEND(data); TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); k->handle_request(s, s->cmd); return 0; } void tpm_backend_finish_sync(TPMBackend *s) { while (s->cmd) { aio_poll(qemu_get_aio_context(), true); } } enum TpmType tpm_backend_get_type(TPMBackend *s) { TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); return k->type; } int tpm_backend_init(TPMBackend *s, TPMIf *tpmif, Error **errp) { if (s->tpmif) { error_setg(errp, "TPM backend '%s' is already initialized", s->id); return -1; } s->tpmif = tpmif; object_ref(OBJECT(tpmif)); s->had_startup_error = false; return 0; } int tpm_backend_startup_tpm(TPMBackend *s, size_t buffersize) { int res = 0; TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); /* terminate a running TPM */ tpm_backend_finish_sync(s); res = k->startup_tpm ? k->startup_tpm(s, buffersize) : 0; s->had_startup_error = (res != 0); return res; } bool tpm_backend_had_startup_error(TPMBackend *s) { return s->had_startup_error; } void tpm_backend_deliver_request(TPMBackend *s, TPMBackendCmd *cmd) { ThreadPool *pool = aio_get_thread_pool(qemu_get_aio_context()); if (s->cmd != NULL) { error_report("There is a TPM request pending"); return; } s->cmd = cmd; object_ref(OBJECT(s)); thread_pool_submit_aio(pool, tpm_backend_worker_thread, s, tpm_backend_request_completed, s); } void tpm_backend_reset(TPMBackend *s) { TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); if (k->reset) { k->reset(s); } tpm_backend_finish_sync(s); s->had_startup_error = false; } void tpm_backend_cancel_cmd(TPMBackend *s) { TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); k->cancel_cmd(s); } bool tpm_backend_get_tpm_established_flag(TPMBackend *s) { TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); return k->get_tpm_established_flag ? k->get_tpm_established_flag(s) : false; } int tpm_backend_reset_tpm_established_flag(TPMBackend *s, uint8_t locty) { TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); return k->reset_tpm_established_flag ? k->reset_tpm_established_flag(s, locty) : 0; } TPMVersion tpm_backend_get_tpm_version(TPMBackend *s) { TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); return k->get_tpm_version(s); } size_t tpm_backend_get_buffer_size(TPMBackend *s) { TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); return k->get_buffer_size(s); } TPMInfo *tpm_backend_query_tpm(TPMBackend *s) { TPMInfo *info = g_new0(TPMInfo, 1); TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); TPMIfClass *tic = TPM_IF_GET_CLASS(s->tpmif); info->id = g_strdup(s->id); info->model = tic->model; info->options = k->get_tpm_options(s); return info; } static void tpm_backend_instance_finalize(Object *obj) { TPMBackend *s = TPM_BACKEND(obj); object_unref(OBJECT(s->tpmif)); g_free(s->id); } static const TypeInfo tpm_backend_info = { .name = TYPE_TPM_BACKEND, .parent = TYPE_OBJECT, .instance_size = sizeof(TPMBackend), .instance_finalize = tpm_backend_instance_finalize, .class_size = sizeof(TPMBackendClass), .abstract = true, }; static const TypeInfo tpm_if_info = { .name = TYPE_TPM_IF, .parent = TYPE_INTERFACE, .class_size = sizeof(TPMIfClass), }; static void register_types(void) { type_register_static(&tpm_backend_info); type_register_static(&tpm_if_info); } type_init(register_types);