From nobody Mon Apr 29 14:14:38 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1490972382305646.9042476507593; Fri, 31 Mar 2017 07:59:42 -0700 (PDT) Received: from localhost ([::1]:41453 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cty1c-0005s4-Uv for importer@patchew.org; Fri, 31 Mar 2017 10:59:41 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:40892) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ctwJq-0003ko-O4 for qemu-devel@nongnu.org; Fri, 31 Mar 2017 09:10:24 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ctwJl-0000VZ-LP for qemu-devel@nongnu.org; Fri, 31 Mar 2017 09:10:22 -0400 Received: from mga09.intel.com ([134.134.136.24]:48955) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1ctwJl-0000UK-6R for qemu-devel@nongnu.org; Fri, 31 Mar 2017 09:10:17 -0400 Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 31 Mar 2017 06:10:13 -0700 Received: from avallurigigabyte.fi.intel.com ([10.237.72.170]) by fmsmga001.fm.intel.com with ESMTP; 31 Mar 2017 06:10:12 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=intel.com; i=@intel.com; q=dns/txt; s=intel; t=1490965817; x=1522501817; h=from:to:cc:subject:date:message-id:in-reply-to: references; bh=uZqdYKwALsz8ZcPIXOvS3kJfAqXSrHME+CiK/pztUuA=; b=dfKQilCjWW3ZKPjxLnKg75/BBeLdWuxy4Kseix7kMCI7zk+XFRcgMSNF sHHahPN/wk0VX3c/N72nmuCm31C5fQ==; X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.36,251,1486454400"; d="scan'208";a="1129316548" From: Amarnath Valluri To: qemu-devel@nongnu.org Date: Fri, 31 Mar 2017 16:10:10 +0300 Message-Id: <1490965817-16913-2-git-send-email-amarnath.valluri@intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1490965817-16913-1-git-send-email-amarnath.valluri@intel.com> References: <1490965817-16913-1-git-send-email-amarnath.valluri@intel.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 134.134.136.24 X-Mailman-Approved-At: Fri, 31 Mar 2017 10:56:08 -0400 Subject: [Qemu-devel] [PATCH 1/7] tpm-backend: Remove unneeded member variable from backend class X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Amarnath Valluri , patrick.ohly@intel.com, stefanb@linux.vnet.ibm.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" TPMDriverOps inside TPMBackend is not required, as it is supposed to be a c= lass member. The only possible reason for keeping in TPMBackend was, to get the backend type in tpm.c where dedicated backend api, tpm_backend_get_type() is present. Signed-off-by: Amarnath Valluri Reviewed-by: Marc-Andr=C3=A9 Lureau Reviewed-by: Philippe Mathieu-Daud=C3=A9 --- hw/tpm/tpm_passthrough.c | 4 ---- include/sysemu/tpm_backend.h | 1 - tpm.c | 2 +- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c index 9234eb3..a0baf5f 100644 --- a/hw/tpm/tpm_passthrough.c +++ b/hw/tpm/tpm_passthrough.c @@ -46,8 +46,6 @@ #define TPM_PASSTHROUGH(obj) \ OBJECT_CHECK(TPMPassthruState, (obj), TYPE_TPM_PASSTHROUGH) =20 -static const TPMDriverOps tpm_passthrough_driver; - /* data structures */ typedef struct TPMPassthruThreadParams { TPMState *tpm_state; @@ -462,8 +460,6 @@ static TPMBackend *tpm_passthrough_create(QemuOpts *opt= s, const char *id) /* let frontend set the fe_model to proper value */ tb->fe_model =3D -1; =20 - tb->ops =3D &tpm_passthrough_driver; - if (tpm_passthrough_handle_device_opts(opts, tb)) { goto err_exit; } diff --git a/include/sysemu/tpm_backend.h b/include/sysemu/tpm_backend.h index b58f52d..e7f590d 100644 --- a/include/sysemu/tpm_backend.h +++ b/include/sysemu/tpm_backend.h @@ -50,7 +50,6 @@ struct TPMBackend { enum TpmModel fe_model; char *path; char *cancel_path; - const TPMDriverOps *ops; =20 QLIST_ENTRY(TPMBackend) list; }; diff --git a/tpm.c b/tpm.c index 9a7c711..0ee021a 100644 --- a/tpm.c +++ b/tpm.c @@ -258,7 +258,7 @@ static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv) res->model =3D drv->fe_model; res->options =3D g_new0(TpmTypeOptions, 1); =20 - switch (drv->ops->type) { + switch (tpm_backend_get_type(drv)) { case TPM_TYPE_PASSTHROUGH: res->options->type =3D TPM_TYPE_OPTIONS_KIND_PASSTHROUGH; tpo =3D g_new0(TPMPassthroughOptions, 1); --=20 2.7.4 From nobody Mon Apr 29 14:14:38 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1490972696171900.1301517500029; Fri, 31 Mar 2017 08:04:56 -0700 (PDT) Received: from localhost ([::1]:41479 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cty6g-0001E0-EK for importer@patchew.org; Fri, 31 Mar 2017 11:04:54 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:40896) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ctwJq-0003kq-On for qemu-devel@nongnu.org; Fri, 31 Mar 2017 09:10:24 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ctwJm-0000Vy-8S for qemu-devel@nongnu.org; Fri, 31 Mar 2017 09:10:22 -0400 Received: from mga09.intel.com ([134.134.136.24]:13263) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1ctwJl-0000V4-QG for qemu-devel@nongnu.org; Fri, 31 Mar 2017 09:10:18 -0400 Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 31 Mar 2017 06:10:15 -0700 Received: from avallurigigabyte.fi.intel.com ([10.237.72.170]) by fmsmga001.fm.intel.com with ESMTP; 31 Mar 2017 06:10:13 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=intel.com; i=@intel.com; q=dns/txt; s=intel; t=1490965817; x=1522501817; h=from:to:cc:subject:date:message-id:in-reply-to: references; bh=j3ztiUqvjIc1yu2PZJ0skgr4ftxWWGzCZKk3g6aDqZg=; b=bGMh1UWQsTULFykrj482CiRDcPLDaUS4Msap/H06dGylVYU+oxhK0PHp AcS5SN/PuQqVkE3k5QF+H4j+vY1FJg==; X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.36,251,1486454400"; d="scan'208";a="1129316566" From: Amarnath Valluri To: qemu-devel@nongnu.org Date: Fri, 31 Mar 2017 16:10:11 +0300 Message-Id: <1490965817-16913-3-git-send-email-amarnath.valluri@intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1490965817-16913-1-git-send-email-amarnath.valluri@intel.com> References: <1490965817-16913-1-git-send-email-amarnath.valluri@intel.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 134.134.136.24 X-Mailman-Approved-At: Fri, 31 Mar 2017 10:56:08 -0400 Subject: [Qemu-devel] [PATCH 2/7] tpm-backend: Move thread handling inside TPMBackend X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Amarnath Valluri , patrick.ohly@intel.com, stefanb@linux.vnet.ibm.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Move thread handling inside TPMBackend, this way backend implementations ne= ed not to maintain their own thread life cycle, instead they needs to implement 'handle_request()' class method that always been called from a thread. This change also demands to move TPMBackendClass definition to tpm_backend_int.h. As handle_request() method is internal to TPMBackend and= its derived classes, and shall not be visible for others. Signed-off-by: Amarnath Valluri --- backends/tpm.c | 66 ++++++++++++++++++++++++++----------= ---- hw/tpm/tpm_passthrough.c | 57 +++++----------------------------- include/sysemu/tpm_backend.h | 19 +++--------- include/sysemu/tpm_backend_int.h | 19 ++++++------ 4 files changed, 67 insertions(+), 94 deletions(-) diff --git a/backends/tpm.c b/backends/tpm.c index 536f262..50a7809 100644 --- a/backends/tpm.c +++ b/backends/tpm.c @@ -20,6 +20,25 @@ #include "qemu/thread.h" #include "sysemu/tpm_backend_int.h" =20 +static void tpm_backend_worker_thread(gpointer data, gpointer user_data) +{ + TPMBackend *s =3D TPM_BACKEND(user_data); + TPMBackendClass *k =3D TPM_BACKEND_GET_CLASS(s); + + if (k->handle_request) { + k->handle_request(s, (TPMBackendCmd)data); + } +} + +static void tpm_backend_thread_end(TPMBackend *s) +{ + if (s->thread_pool) { + g_thread_pool_push(s->thread_pool, (gpointer)TPM_BACKEND_CMD_END, = NULL); + g_thread_pool_free(s->thread_pool, FALSE, TRUE); + s->thread_pool =3D NULL; + } +} + enum TpmType tpm_backend_get_type(TPMBackend *s) { TPMBackendClass *k =3D TPM_BACKEND_GET_CLASS(s); @@ -39,6 +58,8 @@ void tpm_backend_destroy(TPMBackend *s) TPMBackendClass *k =3D TPM_BACKEND_GET_CLASS(s); =20 k->ops->destroy(s); + + tpm_backend_thread_end(s); } =20 int tpm_backend_init(TPMBackend *s, TPMState *state, @@ -46,13 +67,26 @@ int tpm_backend_init(TPMBackend *s, TPMState *state, { TPMBackendClass *k =3D TPM_BACKEND_GET_CLASS(s); =20 - return k->ops->init(s, state, datacb); + s->tpm_state =3D state; + s->recv_data_callback =3D datacb; + + return k->ops->init(s); } =20 int tpm_backend_startup_tpm(TPMBackend *s) { TPMBackendClass *k =3D TPM_BACKEND_GET_CLASS(s); =20 + /* terminate a running TPM */ + tpm_backend_thread_end(s); + + if (!s->thread_pool) { + s->thread_pool =3D g_thread_pool_new(tpm_backend_worker_thread, s,= 1, + TRUE, NULL); + g_thread_pool_push(s->thread_pool, (gpointer)TPM_BACKEND_CMD_INIT, + NULL); + } + return k->ops->startup_tpm(s); } =20 @@ -72,9 +106,8 @@ size_t tpm_backend_realloc_buffer(TPMBackend *s, TPMSize= dBuffer *sb) =20 void tpm_backend_deliver_request(TPMBackend *s) { - TPMBackendClass *k =3D TPM_BACKEND_GET_CLASS(s); - - k->ops->deliver_request(s); + g_thread_pool_push(s->thread_pool, (gpointer)TPM_BACKEND_CMD_PROCESS_C= MD, + NULL); } =20 void tpm_backend_reset(TPMBackend *s) @@ -82,6 +115,8 @@ void tpm_backend_reset(TPMBackend *s) TPMBackendClass *k =3D TPM_BACKEND_GET_CLASS(s); =20 k->ops->reset(s); + + tpm_backend_thread_end(s); } =20 void tpm_backend_cancel_cmd(TPMBackend *s) @@ -156,29 +191,15 @@ static void tpm_backend_instance_init(Object *obj) tpm_backend_prop_get_opened, tpm_backend_prop_set_opened, NULL); -} =20 -void tpm_backend_thread_deliver_request(TPMBackendThread *tbt) -{ - g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_PROCESS_CMD, NU= LL); } =20 -void tpm_backend_thread_create(TPMBackendThread *tbt, - GFunc func, gpointer user_data) +static void tpm_backend_instance_finalize(Object *obj) { - if (!tbt->pool) { - tbt->pool =3D g_thread_pool_new(func, user_data, 1, TRUE, NULL); - g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_INIT, NULL= ); - } -} + TPMBackend *s =3D TPM_BACKEND(obj); =20 -void tpm_backend_thread_end(TPMBackendThread *tbt) -{ - if (tbt->pool) { - g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_END, NULL); - g_thread_pool_free(tbt->pool, FALSE, TRUE); - tbt->pool =3D NULL; - } + g_free(s->id); + tpm_backend_thread_end(s); } =20 static const TypeInfo tpm_backend_info =3D { @@ -186,6 +207,7 @@ static const TypeInfo tpm_backend_info =3D { .parent =3D TYPE_OBJECT, .instance_size =3D sizeof(TPMBackend), .instance_init =3D tpm_backend_instance_init, + .instance_finalize =3D tpm_backend_instance_finalize, .class_size =3D sizeof(TPMBackendClass), .abstract =3D true, }; diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c index a0baf5f..606e064 100644 --- a/hw/tpm/tpm_passthrough.c +++ b/hw/tpm/tpm_passthrough.c @@ -47,20 +47,9 @@ OBJECT_CHECK(TPMPassthruState, (obj), TYPE_TPM_PASSTHROUGH) =20 /* data structures */ -typedef struct TPMPassthruThreadParams { - TPMState *tpm_state; - - TPMRecvDataCB *recv_data_callback; - TPMBackend *tb; -} TPMPassthruThreadParams; - struct TPMPassthruState { TPMBackend parent; =20 - TPMBackendThread tbt; - - TPMPassthruThreadParams tpm_thread_params; - char *tpm_dev; int tpm_fd; bool tpm_executing; @@ -214,12 +203,9 @@ static int tpm_passthrough_unix_transfer(TPMPassthruSt= ate *tpm_pt, selftest_done); } =20 -static void tpm_passthrough_worker_thread(gpointer data, - gpointer user_data) +static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd c= md) { - TPMPassthruThreadParams *thr_parms =3D user_data; - TPMPassthruState *tpm_pt =3D TPM_PASSTHROUGH(thr_parms->tb); - TPMBackendCmd cmd =3D (TPMBackendCmd)data; + TPMPassthruState *tpm_pt =3D TPM_PASSTHROUGH(tb); bool selftest_done =3D false; =20 DPRINTF("tpm_passthrough: processing command type %d\n", cmd); @@ -227,12 +213,12 @@ static void tpm_passthrough_worker_thread(gpointer da= ta, switch (cmd) { case TPM_BACKEND_CMD_PROCESS_CMD: tpm_passthrough_unix_transfer(tpm_pt, - thr_parms->tpm_state->locty_data, + tb->tpm_state->locty_data, &selftest_done); =20 - thr_parms->recv_data_callback(thr_parms->tpm_state, - thr_parms->tpm_state->locty_number, - selftest_done); + tb->recv_data_callback(tb->tpm_state, + tb->tpm_state->locty_number, + selftest_done); break; case TPM_BACKEND_CMD_INIT: case TPM_BACKEND_CMD_END: @@ -248,15 +234,6 @@ static void tpm_passthrough_worker_thread(gpointer dat= a, */ static int tpm_passthrough_startup_tpm(TPMBackend *tb) { - TPMPassthruState *tpm_pt =3D 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; } =20 @@ -268,20 +245,11 @@ static void tpm_passthrough_reset(TPMBackend *tb) =20 tpm_passthrough_cancel_cmd(tb); =20 - tpm_backend_thread_end(&tpm_pt->tbt); - tpm_pt->had_startup_error =3D false; } =20 -static int tpm_passthrough_init(TPMBackend *tb, TPMState *s, - TPMRecvDataCB *recv_data_cb) +static int tpm_passthrough_init(TPMBackend *tb) { - TPMPassthruState *tpm_pt =3D TPM_PASSTHROUGH(tb); - - tpm_pt->tpm_thread_params.tpm_state =3D s; - tpm_pt->tpm_thread_params.recv_data_callback =3D recv_data_cb; - tpm_pt->tpm_thread_params.tb =3D tb; - return 0; } =20 @@ -315,13 +283,6 @@ static size_t tpm_passthrough_realloc_buffer(TPMSizedB= uffer *sb) return sb->size; } =20 -static void tpm_passthrough_deliver_request(TPMBackend *tb) -{ - TPMPassthruState *tpm_pt =3D TPM_PASSTHROUGH(tb); - - tpm_backend_thread_deliver_request(&tpm_pt->tbt); -} - static void tpm_passthrough_cancel_cmd(TPMBackend *tb) { TPMPassthruState *tpm_pt =3D TPM_PASSTHROUGH(tb); @@ -483,8 +444,6 @@ static void tpm_passthrough_destroy(TPMBackend *tb) =20 tpm_passthrough_cancel_cmd(tb); =20 - tpm_backend_thread_end(&tpm_pt->tbt); - qemu_close(tpm_pt->tpm_fd); qemu_close(tpm_pt->cancel_fd); =20 @@ -520,7 +479,6 @@ static const TPMDriverOps tpm_passthrough_driver =3D { .realloc_buffer =3D tpm_passthrough_realloc_buffer, .reset =3D tpm_passthrough_reset, .had_startup_error =3D tpm_passthrough_get_startup_error, - .deliver_request =3D tpm_passthrough_deliver_request, .cancel_cmd =3D tpm_passthrough_cancel_cmd, .get_tpm_established_flag =3D tpm_passthrough_get_tpm_established_flag, .reset_tpm_established_flag =3D tpm_passthrough_reset_tpm_established_= flag, @@ -540,6 +498,7 @@ static void tpm_passthrough_class_init(ObjectClass *kla= ss, void *data) TPMBackendClass *tbc =3D TPM_BACKEND_CLASS(klass); =20 tbc->ops =3D &tpm_passthrough_driver; + tbc->handle_request =3D tpm_passthrough_handle_request; } =20 static const TypeInfo tpm_passthrough_info =3D { diff --git a/include/sysemu/tpm_backend.h b/include/sysemu/tpm_backend.h index e7f590d..98b6112 100644 --- a/include/sysemu/tpm_backend.h +++ b/include/sysemu/tpm_backend.h @@ -29,22 +29,17 @@ =20 typedef struct TPMBackendClass TPMBackendClass; typedef struct TPMBackend TPMBackend; - typedef struct TPMDriverOps TPMDriverOps; - -struct TPMBackendClass { - ObjectClass parent_class; - - const TPMDriverOps *ops; - - void (*opened)(TPMBackend *s, Error **errp); -}; +typedef void (TPMRecvDataCB)(TPMState *, uint8_t locty, bool selftest_done= ); =20 struct TPMBackend { Object parent; =20 /*< protected >*/ bool opened; + TPMState *tpm_state; + GThreadPool *thread_pool; + TPMRecvDataCB *recv_data_callback; =20 char *id; enum TpmModel fe_model; @@ -54,8 +49,6 @@ struct TPMBackend { QLIST_ENTRY(TPMBackend) list; }; =20 -typedef void (TPMRecvDataCB)(TPMState *, uint8_t locty, bool selftest_done= ); - typedef struct TPMSizedBuffer { uint32_t size; uint8_t *buffer; @@ -71,7 +64,7 @@ struct TPMDriverOps { void (*destroy)(TPMBackend *t); =20 /* initialize the backend */ - int (*init)(TPMBackend *t, TPMState *s, TPMRecvDataCB *datacb); + int (*init)(TPMBackend *t); /* start up the TPM on the backend */ int (*startup_tpm)(TPMBackend *t); /* returns true if nothing will ever answer TPM requests */ @@ -79,8 +72,6 @@ struct TPMDriverOps { =20 size_t (*realloc_buffer)(TPMSizedBuffer *sb); =20 - void (*deliver_request)(TPMBackend *t); - void (*reset)(TPMBackend *t); =20 void (*cancel_cmd)(TPMBackend *t); diff --git a/include/sysemu/tpm_backend_int.h b/include/sysemu/tpm_backend_= int.h index 00639dd..9b48a35 100644 --- a/include/sysemu/tpm_backend_int.h +++ b/include/sysemu/tpm_backend_int.h @@ -22,15 +22,6 @@ #ifndef TPM_BACKEND_INT_H #define TPM_BACKEND_INT_H =20 -typedef struct TPMBackendThread { - GThreadPool *pool; -} TPMBackendThread; - -void tpm_backend_thread_deliver_request(TPMBackendThread *tbt); -void tpm_backend_thread_create(TPMBackendThread *tbt, - GFunc func, gpointer user_data); -void tpm_backend_thread_end(TPMBackendThread *tbt); - typedef enum TPMBackendCmd { TPM_BACKEND_CMD_INIT =3D 1, TPM_BACKEND_CMD_PROCESS_CMD, @@ -38,4 +29,14 @@ typedef enum TPMBackendCmd { TPM_BACKEND_CMD_TPM_RESET, } TPMBackendCmd; =20 +struct TPMBackendClass { + ObjectClass parent_class; + + const TPMDriverOps *ops; + + void (*opened)(TPMBackend *s, Error **errp); + + void (*handle_request)(TPMBackend *s, TPMBackendCmd cmd); +}; + #endif /* TPM_BACKEND_INT_H */ --=20 2.7.4 From nobody Mon Apr 29 14:14:38 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1490972495033626.8529845781918; Fri, 31 Mar 2017 08:01:35 -0700 (PDT) Received: from localhost ([::1]:41468 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cty3R-0007Y8-Qi for importer@patchew.org; Fri, 31 Mar 2017 11:01:33 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:40894) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ctwJq-0003kp-OZ for qemu-devel@nongnu.org; Fri, 31 Mar 2017 09:10:27 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ctwJm-0000Vs-7Q for qemu-devel@nongnu.org; Fri, 31 Mar 2017 09:10:22 -0400 Received: from mga09.intel.com ([134.134.136.24]:48955) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1ctwJl-0000UK-Te for qemu-devel@nongnu.org; Fri, 31 Mar 2017 09:10:18 -0400 Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 31 Mar 2017 06:10:17 -0700 Received: from avallurigigabyte.fi.intel.com ([10.237.72.170]) by fmsmga001.fm.intel.com with ESMTP; 31 Mar 2017 06:10:15 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=intel.com; i=@intel.com; q=dns/txt; s=intel; t=1490965817; x=1522501817; h=from:to:cc:subject:date:message-id:in-reply-to: references; bh=MTxtwu2Kg2Hh73n8q6u+MxXDCmLKukJh2+l61SFzOpU=; b=K92HqtG2AesFdMzHNGBGPZjnyoPnvgqybPlR0NG+T72i4+YM0U/HjWgF a9IytS1etsKsQaBuNEoU7hlDb6Mm4Q==; X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.36,251,1486454400"; d="scan'208";a="1129316591" From: Amarnath Valluri To: qemu-devel@nongnu.org Date: Fri, 31 Mar 2017 16:10:12 +0300 Message-Id: <1490965817-16913-4-git-send-email-amarnath.valluri@intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1490965817-16913-1-git-send-email-amarnath.valluri@intel.com> References: <1490965817-16913-1-git-send-email-amarnath.valluri@intel.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 134.134.136.24 X-Mailman-Approved-At: Fri, 31 Mar 2017 10:56:08 -0400 Subject: [Qemu-devel] [PATCH 3/7] tpm-backend: Initialize and free data members in it's own methods X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Amarnath Valluri , patrick.ohly@intel.com, stefanb@linux.vnet.ibm.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Initialize and free TPMBackend data members in it's own instance_init() and instance_finalize methods. Took the opportunity to fix object cleanup in tpm_backend_destroy() method Signed-off-by: Amarnath Valluri --- backends/tpm.c | 14 +++++++++++++- hw/tpm/tpm_passthrough.c | 8 +------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/backends/tpm.c b/backends/tpm.c index 50a7809..00c82d7 100644 --- a/backends/tpm.c +++ b/backends/tpm.c @@ -59,7 +59,7 @@ void tpm_backend_destroy(TPMBackend *s) =20 k->ops->destroy(s); =20 - tpm_backend_thread_end(s); + object_unref(OBJECT(s)); } =20 int tpm_backend_init(TPMBackend *s, TPMState *state, @@ -187,10 +187,20 @@ static void tpm_backend_prop_set_opened(Object *obj, = bool value, Error **errp) =20 static void tpm_backend_instance_init(Object *obj) { + TPMBackend *s =3D TPM_BACKEND(obj); + object_property_add_bool(obj, "opened", tpm_backend_prop_get_opened, tpm_backend_prop_set_opened, NULL); + s->id =3D NULL; + s->fe_model =3D -1; + s->opened =3D false; + s->tpm_state =3D NULL; + s->thread_pool =3D NULL; + s->recv_data_callback =3D NULL; + s->path =3D NULL; + s->cancel_path =3D NULL; =20 } =20 @@ -199,6 +209,8 @@ static void tpm_backend_instance_finalize(Object *obj) TPMBackend *s =3D TPM_BACKEND(obj); =20 g_free(s->id); + g_free(s->path); + g_free(s->cancel_path); tpm_backend_thread_end(s); } =20 diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c index 606e064..cb63079 100644 --- a/hw/tpm/tpm_passthrough.c +++ b/hw/tpm/tpm_passthrough.c @@ -418,8 +418,6 @@ static TPMBackend *tpm_passthrough_create(QemuOpts *opt= s, const char *id) TPMPassthruState *tpm_pt =3D TPM_PASSTHROUGH(tb); =20 tb->id =3D g_strdup(id); - /* let frontend set the fe_model to proper value */ - tb->fe_model =3D -1; =20 if (tpm_passthrough_handle_device_opts(opts, tb)) { goto err_exit; @@ -433,7 +431,7 @@ static TPMBackend *tpm_passthrough_create(QemuOpts *opt= s, const char *id) return tb; =20 err_exit: - g_free(tb->id); + object_unref(obj); =20 return NULL; } @@ -446,10 +444,6 @@ static void tpm_passthrough_destroy(TPMBackend *tb) =20 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); } =20 --=20 2.7.4 From nobody Mon Apr 29 14:14:38 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1490972258229265.03263715331207; Fri, 31 Mar 2017 07:57:38 -0700 (PDT) Received: from localhost ([::1]:41445 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ctxzc-0004HS-RV for importer@patchew.org; Fri, 31 Mar 2017 10:57:36 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:40891) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ctwJq-0003kn-Ni for qemu-devel@nongnu.org; Fri, 31 Mar 2017 09:10:23 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ctwJo-0000X0-9C for qemu-devel@nongnu.org; Fri, 31 Mar 2017 09:10:22 -0400 Received: from mga09.intel.com ([134.134.136.24]:16468) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1ctwJn-0000WJ-TE for qemu-devel@nongnu.org; Fri, 31 Mar 2017 09:10:20 -0400 Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 31 Mar 2017 06:10:18 -0700 Received: from avallurigigabyte.fi.intel.com ([10.237.72.170]) by fmsmga001.fm.intel.com with ESMTP; 31 Mar 2017 06:10:17 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=intel.com; i=@intel.com; q=dns/txt; s=intel; t=1490965819; x=1522501819; h=from:to:cc:subject:date:message-id:in-reply-to: references; bh=tfniAnmmgLm+jnRrKCVCn6qmcSsSGwxRQ40m09U8iYE=; b=v82LE1COHdO3pQza7qxRhCqcQQFyRha7VkOoaJg2UOS4CNakvZpQ8mRU 0JQx8nWMVmKUtoMGOHpGVNIEJt5Crg==; X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.36,251,1486454400"; d="scan'208";a="1129316614" From: Amarnath Valluri To: qemu-devel@nongnu.org Date: Fri, 31 Mar 2017 16:10:13 +0300 Message-Id: <1490965817-16913-5-git-send-email-amarnath.valluri@intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1490965817-16913-1-git-send-email-amarnath.valluri@intel.com> References: <1490965817-16913-1-git-send-email-amarnath.valluri@intel.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 134.134.136.24 X-Mailman-Approved-At: Fri, 31 Mar 2017 10:56:08 -0400 Subject: [Qemu-devel] [PATCH 4/7] tpm-backend: Call interface methods only if backend implements them X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Amarnath Valluri , patrick.ohly@intel.com, stefanb@linux.vnet.ibm.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" This allows backend implementations left optional interface methods. Signed-off-by: Amarnath Valluri --- backends/tpm.c | 22 ++++++++++++++-------- hw/tpm/tpm_passthrough.c | 16 ---------------- 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/backends/tpm.c b/backends/tpm.c index 00c82d7..0bdc5af 100644 --- a/backends/tpm.c +++ b/backends/tpm.c @@ -50,14 +50,16 @@ const char *tpm_backend_get_desc(TPMBackend *s) { TPMBackendClass *k =3D TPM_BACKEND_GET_CLASS(s); =20 - return k->ops->desc(); + return k->ops->desc ? k->ops->desc() : ""; } =20 void tpm_backend_destroy(TPMBackend *s) { TPMBackendClass *k =3D TPM_BACKEND_GET_CLASS(s); =20 - k->ops->destroy(s); + if (k->ops->destroy) { + k->ops->destroy(s); + } =20 object_unref(OBJECT(s)); } @@ -70,7 +72,7 @@ int tpm_backend_init(TPMBackend *s, TPMState *state, s->tpm_state =3D state; s->recv_data_callback =3D datacb; =20 - return k->ops->init(s); + return k->ops->init ? k->ops->init(s) : 0; } =20 int tpm_backend_startup_tpm(TPMBackend *s) @@ -87,21 +89,21 @@ int tpm_backend_startup_tpm(TPMBackend *s) NULL); } =20 - return k->ops->startup_tpm(s); + return k->ops->startup_tpm ? k->ops->startup_tpm(s) : 0; } =20 bool tpm_backend_had_startup_error(TPMBackend *s) { TPMBackendClass *k =3D TPM_BACKEND_GET_CLASS(s); =20 - return k->ops->had_startup_error(s); + return k->ops->had_startup_error ? k->ops->had_startup_error(s) : 0; } =20 size_t tpm_backend_realloc_buffer(TPMBackend *s, TPMSizedBuffer *sb) { TPMBackendClass *k =3D TPM_BACKEND_GET_CLASS(s); =20 - return k->ops->realloc_buffer(sb); + return k->ops->realloc_buffer ? k->ops->realloc_buffer(sb) : (size_t)0; } =20 void tpm_backend_deliver_request(TPMBackend *s) @@ -114,7 +116,9 @@ void tpm_backend_reset(TPMBackend *s) { TPMBackendClass *k =3D TPM_BACKEND_GET_CLASS(s); =20 - k->ops->reset(s); + if (k->ops->reset) { + k->ops->reset(s); + } =20 tpm_backend_thread_end(s); } @@ -123,7 +127,9 @@ void tpm_backend_cancel_cmd(TPMBackend *s) { TPMBackendClass *k =3D TPM_BACKEND_GET_CLASS(s); =20 - k->ops->cancel_cmd(s); + if (k->ops->cancel_cmd) { + k->ops->cancel_cmd(s); + } } =20 bool tpm_backend_get_tpm_established_flag(TPMBackend *s) diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c index cb63079..5b3bf1c 100644 --- a/hw/tpm/tpm_passthrough.c +++ b/hw/tpm/tpm_passthrough.c @@ -228,15 +228,6 @@ static void tpm_passthrough_handle_request(TPMBackend = *tb, TPMBackendCmd cmd) } } =20 -/* - * Start the TPM (thread). If it had been started before, then terminate - * and start it again. - */ -static int tpm_passthrough_startup_tpm(TPMBackend *tb) -{ - return 0; -} - static void tpm_passthrough_reset(TPMBackend *tb) { TPMPassthruState *tpm_pt =3D TPM_PASSTHROUGH(tb); @@ -248,11 +239,6 @@ static void tpm_passthrough_reset(TPMBackend *tb) tpm_pt->had_startup_error =3D false; } =20 -static int tpm_passthrough_init(TPMBackend *tb) -{ - return 0; -} - static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb) { return false; @@ -468,8 +454,6 @@ static const TPMDriverOps tpm_passthrough_driver =3D { .desc =3D tpm_passthrough_create_desc, .create =3D tpm_passthrough_create, .destroy =3D tpm_passthrough_destroy, - .init =3D tpm_passthrough_init, - .startup_tpm =3D tpm_passthrough_startup_tpm, .realloc_buffer =3D tpm_passthrough_realloc_buffer, .reset =3D tpm_passthrough_reset, .had_startup_error =3D tpm_passthrough_get_startup_error, --=20 2.7.4 From nobody Mon Apr 29 14:14:38 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 149097238103956.292074580560325; Fri, 31 Mar 2017 07:59:41 -0700 (PDT) Received: from localhost ([::1]:41452 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cty1b-0005r2-Kx for importer@patchew.org; Fri, 31 Mar 2017 10:59:39 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:40942) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ctwJu-0003lD-1l for qemu-devel@nongnu.org; Fri, 31 Mar 2017 09:10:30 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ctwJp-0000Xd-BA for qemu-devel@nongnu.org; Fri, 31 Mar 2017 09:10:26 -0400 Received: from mga09.intel.com ([134.134.136.24]:16468) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1ctwJp-0000WJ-0Q for qemu-devel@nongnu.org; Fri, 31 Mar 2017 09:10:21 -0400 Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 31 Mar 2017 06:10:20 -0700 Received: from avallurigigabyte.fi.intel.com ([10.237.72.170]) by fmsmga001.fm.intel.com with ESMTP; 31 Mar 2017 06:10:19 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=intel.com; i=@intel.com; q=dns/txt; s=intel; t=1490965821; x=1522501821; h=from:to:cc:subject:date:message-id:in-reply-to: references; bh=zllbEPRDmuOGqdMDnPjb4EWa32shxwgKA1DfYaRvUlA=; b=N5ZO5RYVIB0b42b7vwMwCpQqi/1QOTa8AIqe6azID9mHh8CcCEDL7JNt U8hvhvV50kIJyEZXyVziHYbstGDyBQ==; X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.36,251,1486454400"; d="scan'208";a="1129316636" From: Amarnath Valluri To: qemu-devel@nongnu.org Date: Fri, 31 Mar 2017 16:10:14 +0300 Message-Id: <1490965817-16913-6-git-send-email-amarnath.valluri@intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1490965817-16913-1-git-send-email-amarnath.valluri@intel.com> References: <1490965817-16913-1-git-send-email-amarnath.valluri@intel.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 134.134.136.24 X-Mailman-Approved-At: Fri, 31 Mar 2017 10:56:09 -0400 Subject: [Qemu-devel] [PATCH 5/7] tmp backend: Add new api to read backend tpm options X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Amarnath Valluri , patrick.ohly@intel.com, stefanb@linux.vnet.ibm.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" TPM configuration options are backend implementation details and shall not = be part of base TPMBackend object, and these shall not be accessed directly ou= tside of the class, hence added a new tpm backend api, tpm_backend_get_tpm_option= s() to read the backend configured options. Added new method, get_tpm_options() to TPMDriverOps., which shall be implem= ented by the derived classes to return configured tpm options. Signed-off-by: Amarnath Valluri --- backends/tpm.c | 12 ++++++---- hw/tpm/tpm_passthrough.c | 55 +++++++++++++++++++++++++++++++++++-----= ---- include/sysemu/tpm_backend.h | 15 ++++++++++-- qapi-schema.json | 12 ++++++++-- tpm.c | 13 ++--------- 5 files changed, 76 insertions(+), 31 deletions(-) diff --git a/backends/tpm.c b/backends/tpm.c index 0bdc5af..98aafd8 100644 --- a/backends/tpm.c +++ b/backends/tpm.c @@ -153,6 +153,13 @@ TPMVersion tpm_backend_get_tpm_version(TPMBackend *s) return k->ops->get_tpm_version(s); } =20 +TPMOptions *tpm_backend_get_tpm_options(TPMBackend *s) +{ + TPMBackendClass *k =3D TPM_BACKEND_GET_CLASS(s); + + return k->ops->get_tpm_options ? k->ops->get_tpm_options(s) : NULL; +} + static bool tpm_backend_prop_get_opened(Object *obj, Error **errp) { TPMBackend *s =3D TPM_BACKEND(obj); @@ -205,9 +212,6 @@ static void tpm_backend_instance_init(Object *obj) s->tpm_state =3D NULL; s->thread_pool =3D NULL; s->recv_data_callback =3D NULL; - s->path =3D NULL; - s->cancel_path =3D NULL; - } =20 static void tpm_backend_instance_finalize(Object *obj) @@ -215,8 +219,6 @@ static void tpm_backend_instance_finalize(Object *obj) TPMBackend *s =3D TPM_BACKEND(obj); =20 g_free(s->id); - g_free(s->path); - g_free(s->cancel_path); tpm_backend_thread_end(s); } =20 diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c index 5b3bf1c..fce1163 100644 --- a/hw/tpm/tpm_passthrough.c +++ b/hw/tpm/tpm_passthrough.c @@ -50,6 +50,7 @@ struct TPMPassthruState { TPMBackend parent; =20 + TPMPassthroughOptions ops; char *tpm_dev; int tpm_fd; bool tpm_executing; @@ -314,15 +315,14 @@ static TPMVersion tpm_passthrough_get_tpm_version(TPM= Backend *tb) * 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 =3D TPM_PASSTHROUGH(tb); int fd =3D -1; char *dev; char path[PATH_MAX]; =20 - if (tb->cancel_path) { - fd =3D qemu_open(tb->cancel_path, O_WRONLY); + if (tpm_pt->ops.cancel_path) { + fd =3D qemu_open(tpm_pt->ops.cancel_path, O_WRONLY); if (fd < 0) { error_report("Could not open TPM cancel path : %s", strerror(errno)); @@ -337,7 +337,7 @@ static int tpm_passthrough_open_sysfs_cancel(TPMBackend= *tb) dev) < sizeof(path)) { fd =3D qemu_open(path, O_WRONLY); if (fd >=3D 0) { - tb->cancel_path =3D g_strdup(path); + tpm_pt->ops.cancel_path =3D g_strdup(path); } else { error_report("tpm_passthrough: Could not open TPM cancel " "path %s : %s", path, strerror(errno)); @@ -357,17 +357,24 @@ static int tpm_passthrough_handle_device_opts(QemuOpt= s *opts, TPMBackend *tb) const char *value; =20 value =3D qemu_opt_get(opts, "cancel-path"); - tb->cancel_path =3D g_strdup(value); + if (value) { + tpm_pt->ops.cancel_path =3D g_strdup(value); + tpm_pt->ops.has_cancel_path =3D true; + } else { + tpm_pt->ops.has_cancel_path =3D false; + } =20 value =3D qemu_opt_get(opts, "path"); if (!value) { value =3D TPM_PASSTHROUGH_DEFAULT_DEVICE; + tpm_pt->ops.has_path =3D false; + } else { + tpm_pt->ops.has_path =3D true; } =20 + tpm_pt->ops.path =3D g_strdup(value); tpm_pt->tpm_dev =3D g_strdup(value); =20 - tb->path =3D g_strdup(tpm_pt->tpm_dev); - tpm_pt->tpm_fd =3D qemu_open(tpm_pt->tpm_dev, O_RDWR); if (tpm_pt->tpm_fd < 0) { error_report("Cannot access TPM device using '%s': %s", @@ -388,8 +395,8 @@ static int tpm_passthrough_handle_device_opts(QemuOpts = *opts, TPMBackend *tb) tpm_pt->tpm_fd =3D -1; =20 err_free_parameters: - g_free(tb->path); - tb->path =3D NULL; + g_free(tpm_pt->ops.path); + tpm_pt->ops.path =3D NULL; =20 g_free(tpm_pt->tpm_dev); tpm_pt->tpm_dev =3D NULL; @@ -409,7 +416,7 @@ static TPMBackend *tpm_passthrough_create(QemuOpts *opt= s, const char *id) goto err_exit; } =20 - tpm_pt->cancel_fd =3D tpm_passthrough_open_sysfs_cancel(tb); + tpm_pt->cancel_fd =3D tpm_passthrough_open_sysfs_cancel(tpm_pt); if (tpm_pt->cancel_fd < 0) { goto err_exit; } @@ -430,9 +437,34 @@ static void tpm_passthrough_destroy(TPMBackend *tb) =20 qemu_close(tpm_pt->tpm_fd); qemu_close(tpm_pt->cancel_fd); + + g_free(tpm_pt->ops.path); + g_free(tpm_pt->ops.cancel_path); g_free(tpm_pt->tpm_dev); } =20 +static TPMOptions *tpm_passthrough_get_tpm_options(TPMBackend *tb) +{ + TPMPassthruState *tpm_pt =3D TPM_PASSTHROUGH(tb); + TPMPassthroughOptions *ops =3D g_new0(TPMPassthroughOptions, 1); + + if (!ops) { + return NULL; + } + + if (tpm_pt->ops.has_path) { + ops->has_path =3D true; + ops->path =3D g_strdup(tpm_pt->ops.path); + } + + if (tpm_pt->ops.has_cancel_path) { + ops->has_cancel_path =3D true; + ops->cancel_path =3D g_strdup(tpm_pt->ops.cancel_path); + } + + return (TPMOptions *)ops; +} + static const QemuOptDesc tpm_passthrough_cmdline_opts[] =3D { TPM_STANDARD_CMDLINE_OPTS, { @@ -461,6 +493,7 @@ static const TPMDriverOps tpm_passthrough_driver =3D { .get_tpm_established_flag =3D tpm_passthrough_get_tpm_established_flag, .reset_tpm_established_flag =3D tpm_passthrough_reset_tpm_established_= flag, .get_tpm_version =3D tpm_passthrough_get_tpm_version, + .get_tpm_options =3D tpm_passthrough_get_tpm_options, }; =20 static void tpm_passthrough_inst_init(Object *obj) diff --git a/include/sysemu/tpm_backend.h b/include/sysemu/tpm_backend.h index 98b6112..82348a3 100644 --- a/include/sysemu/tpm_backend.h +++ b/include/sysemu/tpm_backend.h @@ -41,10 +41,9 @@ struct TPMBackend { GThreadPool *thread_pool; TPMRecvDataCB *recv_data_callback; =20 + /* */ char *id; enum TpmModel fe_model; - char *path; - char *cancel_path; =20 QLIST_ENTRY(TPMBackend) list; }; @@ -81,6 +80,8 @@ struct TPMDriverOps { int (*reset_tpm_established_flag)(TPMBackend *t, uint8_t locty); =20 TPMVersion (*get_tpm_version)(TPMBackend *t); + + TPMOptions* (*get_tpm_options)(TPMBackend *t); }; =20 =20 @@ -213,6 +214,16 @@ void tpm_backend_open(TPMBackend *s, Error **errp); */ TPMVersion tpm_backend_get_tpm_version(TPMBackend *s); =20 +/** + * tpm_backend_get_tpm_options: + * @s: the backend + * + * Get the backend configuration options + * + * Returns newly allocated TPMOptions + */ +TPMOptions *tpm_backend_get_tpm_options(TPMBackend *s); + TPMBackend *qemu_find_tpm(const char *id); =20 const TPMDriverOps *tpm_get_backend_driver(const char *type); diff --git a/qapi-schema.json b/qapi-schema.json index b921994..5faf1ac 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -5140,6 +5140,14 @@ { 'command': 'query-tpm-types', 'returns': ['TpmType'] } =20 ## +# @TPMOptions: +# +# Base type for TPM options +## +{ 'struct': 'TPMOptions', + 'data': { } } + +## # @TPMPassthroughOptions: # # Information about the TPM passthrough type @@ -5151,8 +5159,8 @@ # # Since: 1.5 ## -{ 'struct': 'TPMPassthroughOptions', 'data': { '*path' : 'str', - '*cancel-path' : 'str'} } +{ 'struct': 'TPMPassthroughOptions', 'base': 'TPMOptions', + 'data': { '*path' : 'str', '*cancel-path' : 'str'} } =20 ## # @TpmTypeOptions: diff --git a/tpm.c b/tpm.c index 0ee021a..c221000 100644 --- a/tpm.c +++ b/tpm.c @@ -252,7 +252,6 @@ static const TPMDriverOps *tpm_driver_find_by_type(enum= TpmType type) static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv) { TPMInfo *res =3D g_new0(TPMInfo, 1); - TPMPassthroughOptions *tpo; =20 res->id =3D g_strdup(drv->id); res->model =3D drv->fe_model; @@ -261,16 +260,8 @@ static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv) switch (tpm_backend_get_type(drv)) { case TPM_TYPE_PASSTHROUGH: res->options->type =3D TPM_TYPE_OPTIONS_KIND_PASSTHROUGH; - tpo =3D g_new0(TPMPassthroughOptions, 1); - res->options->u.passthrough.data =3D tpo; - if (drv->path) { - tpo->path =3D g_strdup(drv->path); - tpo->has_path =3D true; - } - if (drv->cancel_path) { - tpo->cancel_path =3D g_strdup(drv->cancel_path); - tpo->has_cancel_path =3D true; - } + res->options->u.passthrough.data =3D + (TPMPassthroughOptions *)tpm_backend_get_tpm_options(drv); break; case TPM_TYPE__MAX: break; --=20 2.7.4 From nobody Mon Apr 29 14:14:38 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1490972257749474.52807167836784; Fri, 31 Mar 2017 07:57:37 -0700 (PDT) Received: from localhost ([::1]:41444 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ctxzb-0004H9-Vq for importer@patchew.org; Fri, 31 Mar 2017 10:57:36 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:40931) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ctwJs-0003l3-Hr for qemu-devel@nongnu.org; Fri, 31 Mar 2017 09:10:25 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ctwJr-0000Ym-9u for qemu-devel@nongnu.org; Fri, 31 Mar 2017 09:10:24 -0400 Received: from mga09.intel.com ([134.134.136.24]:16468) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1ctwJq-0000WJ-UE for qemu-devel@nongnu.org; Fri, 31 Mar 2017 09:10:23 -0400 Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 31 Mar 2017 06:10:22 -0700 Received: from avallurigigabyte.fi.intel.com ([10.237.72.170]) by fmsmga001.fm.intel.com with ESMTP; 31 Mar 2017 06:10:20 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=intel.com; i=@intel.com; q=dns/txt; s=intel; t=1490965822; x=1522501822; h=from:to:cc:subject:date:message-id:in-reply-to: references; bh=9IrFqSO4Wo0KNDtAgpTS5dn8jpKT69PCwU8rG7pJoTk=; b=OMSCN8IhBoaAtWkRBriJbysgC84EkZ++qfLMubgNjnSdcETukbmT/PYr 15yi3V0w6hqQCV9U0T/T9ViyfMN5yw==; X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.36,251,1486454400"; d="scan'208";a="1129316649" From: Amarnath Valluri To: qemu-devel@nongnu.org Date: Fri, 31 Mar 2017 16:10:15 +0300 Message-Id: <1490965817-16913-7-git-send-email-amarnath.valluri@intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1490965817-16913-1-git-send-email-amarnath.valluri@intel.com> References: <1490965817-16913-1-git-send-email-amarnath.valluri@intel.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 134.134.136.24 X-Mailman-Approved-At: Fri, 31 Mar 2017 10:56:08 -0400 Subject: [Qemu-devel] [PATCH 6/7] tpm-passthrough: move reusable code to utils X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Amarnath Valluri , patrick.ohly@intel.com, stefanb@linux.vnet.ibm.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Signed-off-by: Amarnath Valluri --- hw/tpm/tpm_passthrough.c | 77 ++++----------------------------------------= ---- hw/tpm/tpm_util.c | 60 +++++++++++++++++++++++++++++++++++++ hw/tpm/tpm_util.h | 8 +++++ 3 files changed, 73 insertions(+), 72 deletions(-) diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c index fce1163..fc60914 100644 --- a/hw/tpm/tpm_passthrough.c +++ b/hw/tpm/tpm_passthrough.c @@ -69,73 +69,6 @@ typedef struct TPMPassthruState TPMPassthruState; =20 static void tpm_passthrough_cancel_cmd(TPMBackend *tb); =20 -static int tpm_passthrough_unix_write(int fd, const uint8_t *buf, uint32_t= len) -{ - int ret, remain; - - remain =3D len; - while (remain > 0) { - ret =3D write(fd, buf, remain); - if (ret < 0) { - if (errno !=3D EINTR && errno !=3D EAGAIN) { - return -1; - } - } else if (ret =3D=3D 0) { - break; - } else { - buf +=3D ret; - remain -=3D ret; - } - } - return len - remain; -} - -static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len) -{ - int ret; - reread: - ret =3D read(fd, buf, len); - if (ret < 0) { - if (errno !=3D EINTR && errno !=3D EAGAIN) { - return -1; - } - goto reread; - } - return ret; -} - -static uint32_t tpm_passthrough_get_size_from_buffer(const uint8_t *buf) -{ - struct tpm_resp_hdr *resp =3D (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 >=3D sizeof(struct tpm_resp_hdr)) { - struct tpm_resp_hdr *resp =3D (struct tpm_resp_hdr *)out; - - resp->tag =3D cpu_to_be16(TPM_TAG_RSP_COMMAND); - resp->len =3D cpu_to_be32(sizeof(struct tpm_resp_hdr)); - resp->errcode =3D cpu_to_be32(TPM_FAIL); - } -} - -static bool tpm_passthrough_is_selftest(const uint8_t *in, uint32_t in_len) -{ - struct tpm_req_hdr *hdr =3D (struct tpm_req_hdr *)in; - - if (in_len >=3D sizeof(*hdr)) { - return (be32_to_cpu(hdr->ordinal) =3D=3D 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, @@ -149,9 +82,9 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState= *tpm_pt, tpm_pt->tpm_executing =3D true; *selftest_done =3D false; =20 - is_selftest =3D tpm_passthrough_is_selftest(in, in_len); + is_selftest =3D tpm_util_is_selftest(in, in_len); =20 - ret =3D tpm_passthrough_unix_write(tpm_pt->tpm_fd, in, in_len); + ret =3D tpm_util_unix_write(tpm_pt->tpm_fd, in, in_len); if (ret !=3D in_len) { if (!tpm_pt->tpm_op_canceled || errno !=3D ECANCELED) { error_report("tpm_passthrough: error while transmitting data " @@ -163,7 +96,7 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState= *tpm_pt, =20 tpm_pt->tpm_executing =3D false; =20 - ret =3D tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len); + ret =3D tpm_util_unix_read(tpm_pt->tpm_fd, out, out_len); if (ret < 0) { if (!tpm_pt->tpm_op_canceled || errno !=3D ECANCELED) { error_report("tpm_passthrough: error while reading data from " @@ -171,7 +104,7 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruStat= e *tpm_pt, strerror(errno), errno); } } else if (ret < sizeof(struct tpm_resp_hdr) || - tpm_passthrough_get_size_from_buffer(out) !=3D ret) { + ((struct tpm_resp_hdr *)out)->len !=3D ret) { ret =3D -1; error_report("tpm_passthrough: received invalid response " "packet from TPM"); @@ -184,7 +117,7 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruStat= e *tpm_pt, =20 err_exit: if (ret < 0) { - tpm_write_fatal_error_response(out, out_len); + tpm_util_write_fatal_error_response(out, out_len); } =20 tpm_pt->tpm_executing =3D false; diff --git a/hw/tpm/tpm_util.c b/hw/tpm/tpm_util.c index 7b35429..5475acf 100644 --- a/hw/tpm/tpm_util.c +++ b/hw/tpm/tpm_util.c @@ -23,6 +23,66 @@ #include "tpm_util.h" #include "tpm_int.h" =20 +int tpm_util_unix_write(int fd, const uint8_t *buf, uint32_t len) +{ + int ret, remain; + + remain =3D len; + while (remain > 0) { + ret =3D write(fd, buf, remain); + if (ret < 0) { + if (errno !=3D EINTR && errno !=3D EAGAIN) { + return -1; + } + } else if (ret =3D=3D 0) { + break; + } else { + buf +=3D ret; + remain -=3D ret; + } + } + return len - remain; +} + +int tpm_util_unix_read(int fd, uint8_t *buf, uint32_t len) +{ + int ret; + reread: + ret =3D read(fd, buf, len); + if (ret < 0) { + if (errno !=3D EINTR && errno !=3D EAGAIN) { + return -1; + } + goto reread; + } + return ret; +} + +/* + * Write an error message in the given output buffer. + */ +void tpm_util_write_fatal_error_response(uint8_t *out, uint32_t out_len) +{ + if (out_len >=3D sizeof(struct tpm_resp_hdr)) { + struct tpm_resp_hdr *resp =3D (struct tpm_resp_hdr *)out; + + resp->tag =3D cpu_to_be16(TPM_TAG_RSP_COMMAND); + resp->len =3D cpu_to_be32(sizeof(struct tpm_resp_hdr)); + resp->errcode =3D cpu_to_be32(TPM_FAIL); + } +} + +bool tpm_util_is_selftest(const uint8_t *in, uint32_t in_len) +{ + struct tpm_req_hdr *hdr =3D (struct tpm_req_hdr *)in; + + if (in_len >=3D sizeof(*hdr)) { + return (be32_to_cpu(hdr->ordinal) =3D=3D TPM_ORD_ContinueSelfTest); + } + + return false; +} + /* * A basic test of a TPM device. We expect a well formatted response header * (error response is fine) within one second. diff --git a/hw/tpm/tpm_util.h b/hw/tpm/tpm_util.h index df76245..c2feca7 100644 --- a/hw/tpm/tpm_util.h +++ b/hw/tpm/tpm_util.h @@ -24,6 +24,14 @@ =20 #include "sysemu/tpm_backend.h" =20 +int tpm_util_unix_write(int fd, const uint8_t *buf, uint32_t len); + +int tpm_util_unix_read(int fd, uint8_t *buf, uint32_t len); + +void tpm_util_write_fatal_error_response(uint8_t *out, uint32_t out_len); + +bool tpm_util_is_selftest(const uint8_t *in, uint32_t in_len); + int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version); =20 #endif /* TPM_TPM_UTIL_H */ --=20 2.7.4 From nobody Mon Apr 29 14:14:38 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1490972600800148.1498815265487; Fri, 31 Mar 2017 08:03:20 -0700 (PDT) Received: from localhost ([::1]:41472 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cty58-0000AY-Sy for importer@patchew.org; Fri, 31 Mar 2017 11:03:18 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41030) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ctwK3-0003nP-Om for qemu-devel@nongnu.org; Fri, 31 Mar 2017 09:10:40 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ctwJz-0000dR-BL for qemu-devel@nongnu.org; Fri, 31 Mar 2017 09:10:35 -0400 Received: from mga09.intel.com ([134.134.136.24]:16468) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1ctwJt-0000WJ-5n for qemu-devel@nongnu.org; Fri, 31 Mar 2017 09:10:31 -0400 Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 31 Mar 2017 06:10:24 -0700 Received: from avallurigigabyte.fi.intel.com ([10.237.72.170]) by fmsmga001.fm.intel.com with ESMTP; 31 Mar 2017 06:10:22 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=intel.com; i=@intel.com; q=dns/txt; s=intel; t=1490965830; x=1522501830; h=from:to:cc:subject:date:message-id:in-reply-to: references; bh=39jJDBL1gYqBbKIGQG5BA/IDjupVfvRFJB30dzHBOrA=; b=CSr01ng+1iD48GWnC+W9DvfK1wFzydyeJN59IIt8RiF3xZ43TXAzZORU bg3sgQYU74VCsQJS3tqP9N+2CsaECQ==; X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.36,251,1486454400"; d="scan'208";a="1129316670" From: Amarnath Valluri To: qemu-devel@nongnu.org Date: Fri, 31 Mar 2017 16:10:16 +0300 Message-Id: <1490965817-16913-8-git-send-email-amarnath.valluri@intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1490965817-16913-1-git-send-email-amarnath.valluri@intel.com> References: <1490965817-16913-1-git-send-email-amarnath.valluri@intel.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 134.134.136.24 X-Mailman-Approved-At: Fri, 31 Mar 2017 10:56:09 -0400 Subject: [Qemu-devel] [PATCH 7/7] Added support for TPM emulator X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Amarnath Valluri , patrick.ohly@intel.com, stefanb@linux.vnet.ibm.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" This change introduces a new TPM backend driver that can communicates with swtpm(software TPM emulator) using unix domain socket interface. Swtpm uses two unix sockets, one for plain TPM commands and responses, and = one for out-of-band control messages. The swtpm and associated tools can be found here: https://github.com/stefanberger/swtpm Usage: # setup TPM state directory mkdir /tmp/mytpm chown -R tss:root /tmp/mytpm /usr/bin/swtpm_setup --tpm-state /tmp/mytpm --createek # Ask qeum to use TPM emulator with given tpm state directory qemu-system-x86_64 \ [...] \ -tpmdev emulator,id=3Dtpm0,tpmstatedir=3D/tmp/mytpm,logfile=3D/tmp/= swtpm.log \ -device tpm-tis,tpmdev=3Dtpm0 \ [...] Signed-off-by: Amarnath Valluri --- configure | 6 + hmp.c | 14 + hw/tpm/Makefile.objs | 1 + hw/tpm/tpm_emulator.c | 740 ++++++++++++++++++++++++++++++++++++++++++++++= ++++ hw/tpm/tpm_ioctl.h | 243 +++++++++++++++++ hw/tpm/tpm_util.c | 34 +++ hw/tpm/tpm_util.h | 3 + qapi-schema.json | 22 +- qemu-options.hx | 25 +- tpm.c | 7 +- 10 files changed, 1089 insertions(+), 6 deletions(-) create mode 100644 hw/tpm/tpm_emulator.c create mode 100644 hw/tpm/tpm_ioctl.h diff --git a/configure b/configure index 4901b9a..9089546 100755 --- a/configure +++ b/configure @@ -3349,8 +3349,10 @@ fi =20 if test "$targetos" =3D Linux && test "$cpu" =3D i386 -o "$cpu" =3D x86_64= ; then tpm_passthrough=3D$tpm + tpm_emulator=3D$tpm else tpm_passthrough=3Dno + tpm_emulator=3Dno fi =20 ########################################## @@ -5125,6 +5127,7 @@ echo "gcov enabled $gcov" echo "TPM support $tpm" echo "libssh2 support $libssh2" echo "TPM passthrough $tpm_passthrough" +echo "TPM emulator $tpm_emulator" echo "QOM debugging $qom_cast_debug" echo "lzo support $lzo" echo "snappy support $snappy" @@ -5704,6 +5707,9 @@ if test "$tpm" =3D "yes"; then if test "$tpm_passthrough" =3D "yes"; then echo "CONFIG_TPM_PASSTHROUGH=3Dy" >> $config_host_mak fi + if test "$tpm_emulator" =3D "yes"; then + echo "CONFIG_TPM_EMULATOR=3Dy" >> $config_host_mak + fi fi =20 echo "TRACE_BACKENDS=3D$trace_backends" >> $config_host_mak diff --git a/hmp.c b/hmp.c index edb8970..03a47e2 100644 --- a/hmp.c +++ b/hmp.c @@ -937,6 +937,7 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict) Error *err =3D NULL; unsigned int c =3D 0; TPMPassthroughOptions *tpo; + TPMEmulatorOptions *teo; =20 info_list =3D qmp_query_tpm(&err); if (err) { @@ -966,6 +967,19 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict) tpo->has_cancel_path ? ",cancel-path=3D" : "", tpo->has_cancel_path ? tpo->cancel_path : ""); break; + case TPM_TYPE_OPTIONS_KIND_EMULATOR: + teo =3D ti->options->u.emulator.data; + monitor_printf(mon, ",tmpstatedir=3D%s", teo->tpmstatedir); + if (teo->has_path) { + monitor_printf(mon, ",path=3D%s", teo->path); + } + if (teo->has_logfile) { + monitor_printf(mon, ",logfile=3D%s", teo->logfile); + } + if (teo->has_loglevel) { + monitor_printf(mon, ",loglevel=3D%ld", teo->loglevel); + } + break; case TPM_TYPE_OPTIONS_KIND__MAX: break; } diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs index 64cecc3..41f0b7a 100644 --- a/hw/tpm/Makefile.objs +++ b/hw/tpm/Makefile.objs @@ -1,2 +1,3 @@ common-obj-$(CONFIG_TPM_TIS) +=3D tpm_tis.o common-obj-$(CONFIG_TPM_PASSTHROUGH) +=3D tpm_passthrough.o tpm_util.o +common-obj-$(CONFIG_TPM_EMULATOR) +=3D tpm_emulator.o tpm_util.o diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c new file mode 100644 index 0000000..5b1dcfa --- /dev/null +++ b/hw/tpm/tpm_emulator.c @@ -0,0 +1,740 @@ +/* + * emulator TPM driver + * + * Copyright (c) 2017 Intel Corporation + * Author: Amarnath Valluri + * + * Copyright (c) 2010 - 2013 IBM Corporation + * Authors: + * Stefan Berger + * + * 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 + * + * The origin of the code is from CUSE driver posed by Stefan Berger: + * https://github.com/stefanberger/qemu-tpm + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "sysemu/tpm_backend.h" +#include "tpm_int.h" +#include "hw/hw.h" +#include "hw/i386/pc.h" +#include "sysemu/tpm_backend_int.h" +#include "tpm_util.h" +#include "tpm_ioctl.h" +#include "qapi/error.h" + +#include +#include +#include +#include + +#define DEBUG_TPM 0 + +#define DPRINT(fmt, ...) do { \ + if (DEBUG_TPM) { \ + fprintf(stderr, fmt, ## __VA_ARGS__); \ + } \ +} while (0); + +#define DPRINTF(fmt, ...) DPRINT(fmt"\n", __VA_ARGS__) + +#define TYPE_TPM_EMULATOR "emulator" +#define TPM_EMULATOR(obj) \ + OBJECT_CHECK(TPMEmulator, (obj), TYPE_TPM_EMULATOR) + +static const TPMDriverOps tpm_emulator_driver; + +/* data structures */ +typedef struct TPMEmulator { + TPMBackend parent; + + TPMEmulatorOptions ops; + int tpm_fd; + int tpm_ctrl_fd; + bool op_executing; + bool op_canceled; + bool child_running; + TPMVersion tpm_version; + ptm_cap caps; /* capabilities of the TPM */ + uint8_t cur_locty_number; /* last set locality */ +} TPMEmulator; + +#define TPM_DEFAULT_EMULATOR "swtpm" +#define TPM_DEFAULT_LOGLEVEL 5 +#define TPM_EUMLATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps & (cap)) =3D= =3D (cap)) + +static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_pt, + const uint8_t *in, uint32_t in_len, + uint8_t *out, uint32_t out_len, + bool *selftest_done) +{ + int ret; + bool is_selftest; + const struct tpm_resp_hdr *hdr; + + if (!tpm_pt->child_running) { + return -1; + } + + tpm_pt->op_canceled =3D false; + tpm_pt->op_executing =3D true; + *selftest_done =3D false; + + is_selftest =3D tpm_util_is_selftest(in, in_len); + + ret =3D tpm_util_unix_write(tpm_pt->tpm_fd, in, in_len); + if (ret !=3D in_len) { + if (!tpm_pt->op_canceled || errno !=3D ECANCELED) { + error_report("tpm_emulator: error while transmitting data " + "to TPM: %s (%i)", strerror(errno), errno); + } + goto err_exit; + } + + tpm_pt->op_executing =3D false; + + ret =3D tpm_util_unix_read(tpm_pt->tpm_fd, out, out_len); + if (ret < 0) { + if (!tpm_pt->op_canceled || errno !=3D ECANCELED) { + error_report("tpm_emulator: error while reading data from " + "TPM: %s (%i)", strerror(errno), errno); + } + } else if (ret < sizeof(struct tpm_resp_hdr) || + be32_to_cpu(((struct tpm_resp_hdr *)out)->len) !=3D ret) { + ret =3D -1; + error_report("tpm_emulator: received invalid response " + "packet from TPM"); + } + + if (is_selftest && (ret >=3D sizeof(struct tpm_resp_hdr))) { + hdr =3D (struct tpm_resp_hdr *)out; + *selftest_done =3D (be32_to_cpu(hdr->errcode) =3D=3D 0); + } + +err_exit: + if (ret < 0) { + tpm_util_write_fatal_error_response(out, out_len); + } + + tpm_pt->op_executing =3D false; + + return ret; +} + +static int tpm_emulator_set_locality(TPMEmulator *tpm_pt, + uint8_t locty_number) +{ + ptm_loc loc; + + if (!tpm_pt->child_running) { + return -1; + } + + DPRINTF("tpm_emulator: %s : locality: 0x%x", __func__, locty_number); + + if (tpm_pt->cur_locty_number !=3D locty_number) { + DPRINTF("tpm-emulator: setting locality : 0x%x", locty_number); + loc.u.req.loc =3D cpu_to_be32(locty_number); + if (tpm_util_ctrlcmd(tpm_pt->tpm_ctrl_fd, PTM_SET_LOCALITY, &loc, + sizeof(loc), sizeof(loc)) < 0) { + error_report("tpm-emulator: could not set locality : %s", + strerror(errno)); + return -1; + } + loc.u.resp.tpm_result =3D be32_to_cpu(loc.u.resp.tpm_result); + if (loc.u.resp.tpm_result !=3D 0) { + error_report("tpm-emulator: TPM result for set locality : 0x%x= ", + loc.u.resp.tpm_result); + return -1; + } + tpm_pt->cur_locty_number =3D locty_number; + } + return 0; +} + +static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd cmd) +{ + TPMEmulator *tpm_pt =3D TPM_EMULATOR(tb); + TPMLocality *locty =3D NULL; + bool selftest_done =3D false; + + DPRINTF("tpm_emulator: processing command type %d", cmd); + + switch (cmd) { + case TPM_BACKEND_CMD_PROCESS_CMD: + locty =3D tb->tpm_state->locty_data; + if (tpm_emulator_set_locality(tpm_pt, + tb->tpm_state->locty_number) < 0) { + tpm_util_write_fatal_error_response(locty->r_buffer.buffer, + locty->r_buffer.size); + } else { + tpm_emulator_unix_tx_bufs(tpm_pt, locty->w_buffer.buffer, + locty->w_offset, locty->r_buffer.buffer, + locty->r_buffer.size, &selftest_done); + } + tb->recv_data_callback(tb->tpm_state, tb->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; + } +} + +/* + * Gracefully shut down the external unixio TPM + */ +static void tpm_emulator_shutdown(TPMEmulator *tpm_pt) +{ + ptm_res res; + + if (!tpm_pt->child_running) { + return; + } + + if (tpm_util_ctrlcmd(tpm_pt->tpm_ctrl_fd, PTM_SHUTDOWN, &res, 0, + sizeof(res)) < 0) { + error_report("tpm-emulator: Could not cleanly shutdown the TPM: %s= ", + strerror(errno)); + } else if (res !=3D 0) { + error_report("tpm-emulator: TPM result for sutdown: 0x%x", + be32_to_cpu(res)); + } +} + +static int tpm_emulator_probe_caps(TPMEmulator *tpm_pt) +{ + if (!tpm_pt->child_running) { + return -1; + } + + DPRINTF("tpm_emulator: %s", __func__); + if (tpm_util_ctrlcmd(tpm_pt->tpm_ctrl_fd, PTM_GET_CAPABILITY, + &tpm_pt->caps, 0, sizeof(tpm_pt->caps)) < 0) { + error_report("tpm-emulator: probing failed : %s", strerror(errno)); + return -1; + } + + tpm_pt->caps =3D be64_to_cpu(tpm_pt->caps); + + DPRINTF("tpm-emulator: capbilities : 0x%lx", tpm_pt->caps); + + return 0; +} + +static int tpm_emulator_check_caps(TPMEmulator *tpm_pt) +{ + ptm_cap caps =3D 0; + const char *tpm =3D NULL; + + /* check for min. required capabilities */ + switch (tpm_pt->tpm_version) { + case TPM_VERSION_1_2: + caps =3D PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLIS= HED | + PTM_CAP_SET_LOCALITY; + tpm =3D "1.2"; + break; + case TPM_VERSION_2_0: + caps =3D PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLIS= HED | + PTM_CAP_SET_LOCALITY | PTM_CAP_RESET_TPMESTABLISHED; + tpm =3D "2"; + break; + case TPM_VERSION_UNSPEC: + error_report("tpm-emulator: TPM version has not been set"); + return -1; + } + + if (!TPM_EUMLATOR_IMPLEMENTS_ALL_CAPS(tpm_pt, caps)) { + error_report("tpm-emulator: TPM does not implement minimum set of " + "required capabilities for TPM %s (0x%x)", tpm, (int)= caps); + return -1; + } + + return 0; +} + +static int tpm_emulator_init_tpm(TPMEmulator *tpm_pt, bool is_resume) +{ + ptm_init init; + ptm_res res; + + if (!tpm_pt->child_running) { + return -1; + } + + DPRINTF("tpm_emulator: %s", __func__); + if (is_resume) { + init.u.req.init_flags =3D cpu_to_be32(PTM_INIT_FLAG_DELETE_VOLATIL= E); + } + + if (tpm_util_ctrlcmd(tpm_pt->tpm_ctrl_fd, PTM_INIT, &init, sizeof(init= ), + sizeof(init)) < 0) { + error_report("tpm-emulator: could not send INIT: %s", + strerror(errno)); + return -1; + } + + res =3D be32_to_cpu(init.u.resp.tpm_result); + if (res) { + error_report("tpm-emulator: TPM result for PTM_INIT: 0x%x", res); + return -1; + } + + return 0; +} + +static int tpm_emulator_startup_tpm(TPMBackend *tb) +{ + TPMEmulator *tpm_pt =3D TPM_EMULATOR(tb); + + DPRINTF("tpm_emulator: %s", __func__); + + tpm_emulator_init_tpm(tpm_pt, false) ; + + return 0; +} + +static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb) +{ + TPMEmulator *tpm_pt =3D TPM_EMULATOR(tb); + ptm_est est; + + DPRINTF("tpm_emulator: %s", __func__); + if (tpm_util_ctrlcmd(tpm_pt->tpm_ctrl_fd, PTM_GET_TPMESTABLISHED, &est= , 0, + sizeof(est)) < 0) { + error_report("tpm-emulator: Could not get the TPM established flag= : %s", + strerror(errno)); + return false; + } + DPRINTF("tpm_emulator: established flag: %0x", est.u.resp.bit); + + return (est.u.resp.bit !=3D 0); +} + +static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb, + uint8_t locty) +{ + TPMEmulator *tpm_pt =3D TPM_EMULATOR(tb); + ptm_reset_est reset_est; + ptm_res res; + + /* only a TPM 2.0 will support this */ + if (tpm_pt->tpm_version =3D=3D TPM_VERSION_2_0) { + reset_est.u.req.loc =3D cpu_to_be32(tpm_pt->cur_locty_number); + + if (tpm_util_ctrlcmd(tpm_pt->tpm_ctrl_fd, PTM_RESET_TPMESTABLISHED, + &reset_est, sizeof(reset_est), + sizeof(reset_est)) < 0) { + error_report("tpm-emulator: Could not reset the establishment = bit: " + "%s", strerror(errno)); + return -1; + } + + res =3D be32_to_cpu(reset_est.u.resp.tpm_result); + if (res) { + error_report("tpm-emulator: TPM result for rest establixhed fl= ag: " + "0x%x", res); + return -1; + } + } + + return 0; +} + +static bool tpm_emulator_get_startup_error(TPMBackend *tb) +{ + TPMEmulator *tpm_pt =3D TPM_EMULATOR(tb); + + return !tpm_pt->child_running; +} + +static size_t tpm_emulator_realloc_buffer(TPMSizedBuffer *sb) +{ + size_t wanted_size =3D 4096; /* Linux tpm.c buffer size */ + + if (sb->size !=3D wanted_size) { + sb->buffer =3D g_realloc(sb->buffer, wanted_size); + sb->size =3D wanted_size; + } + return sb->size; +} + +static void tpm_emulator_cancel_cmd(TPMBackend *tb) +{ + TPMEmulator *tpm_pt =3D TPM_EMULATOR(tb); + ptm_res res; + + /* + * 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->op_executing) { + if (TPM_EUMLATOR_IMPLEMENTS_ALL_CAPS(tpm_pt, PTM_CAP_CANCEL_TPM_CM= D)) { + if (tpm_util_ctrlcmd(tpm_pt->tpm_ctrl_fd, PTM_CANCEL_TPM_CMD, = &res, + 0, sizeof(res)) < 0) { + error_report("tpm-emulator: Could not cancel command: %s", + strerror(errno)); + } else if (res !=3D 0) { + error_report("tpm-emulator: Failed to cancel TPM: 0x%x", + be32_to_cpu(res)); + } else { + tpm_pt->op_canceled =3D true; + } + } + } +} + +static void tpm_emulator_reset(TPMBackend *tb) +{ + DPRINTF("tpm_emulator: %s", __func__); + + tpm_emulator_cancel_cmd(tb); +} + +static const char *tpm_emulator_desc(void) +{ + return "TPM emulator backend driver"; +} + +static TPMVersion tpm_emulator_get_tpm_version(TPMBackend *tb) +{ + TPMEmulator *tpm_pt =3D TPM_EMULATOR(tb); + + return tpm_pt->tpm_version; +} + +static void tpm_emulator_fd_handler(void *opaque) +{ + TPMEmulator *tpm_pt =3D opaque; + char val =3D 0; + ssize_t size; + + qemu_set_fd_handler(tpm_pt->tpm_fd, NULL, NULL, NULL); + + size =3D qemu_recv(tpm_pt->tpm_fd, &val, 1, MSG_PEEK); + if (!size) { + error_report("TPM backend disappeared"); + tpm_pt->child_running =3D false; + } else { + DPRINT("tpm-emulator: unexpected data on TPM\n"); + } +} + +static int tpm_emulator_spawn_emulator(TPMEmulator *tpm_pt) +{ + int fds[2]; + int ctrl_fds[2]; + pid_t cpid; + + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) < 0) { + return -1; + } + + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, ctrl_fds) < 0) { + closesocket(fds[0]); + closesocket(fds[1]); + return -1; + } + + cpid =3D fork(); + if (cpid < 0) { + error_report("tpm-emulator: Fork failure: %s", strerror(errno)); + closesocket(fds[0]); closesocket(fds[1]); + closesocket(ctrl_fds[0]); closesocket(ctrl_fds[1]); + return -1; + } + + if (cpid =3D=3D 0) { /* CHILD */ + int i; + char fd_str[128] =3D ""; + char ctrl_fd_str[128] =3D ""; + char tpmstate_str[1024] =3D ""; + char log_str[1024] =3D ""; + const char *params[] =3D { + tpm_pt->ops.path, "socket", + "--fd", fd_str, + "--ctrl", ctrl_fd_str, + "--tpmstate", tpmstate_str, + "--log", log_str, + NULL /* End */ + }; + + /* close all unused inherited sockets */ + closesocket(fds[0]); + closesocket(ctrl_fds[0]); + for (i =3D STDERR_FILENO + 1; i < fds[1]; i++) { + closesocket(i); + } + + sprintf(fd_str, "%d", fds[1]); + sprintf(ctrl_fd_str, "type=3Dunixio,clientfd=3D%d", ctrl_fds[1]); + sprintf(tpmstate_str, "dir=3D%s", tpm_pt->ops.tpmstatedir); + if (tpm_pt->ops.has_logfile) { + sprintf(log_str, "file=3D%s,level=3D%d", tpm_pt->ops.logfile, + (int)tpm_pt->ops.loglevel); + } else { + /* truncate logs */ + params[8] =3D NULL; + } + DPRINT("Running cmd: ") + for (i =3D 0; params[i]; i++) { + DPRINT(" %s", params[i]) + } + DPRINT("\n") + if (execv(tpm_pt->ops.path, (char * const *)params) < 0) { + error_report("execv() failure : %s", strerror(errno)); + } + closesocket(fds[1]); + closesocket(ctrl_fds[1]); + exit(0); + } else { /* self */ + DPRINTF("tpm-emulator: child pid: %d", cpid); + /* FIXME: find better way of finding swtpm ready + maybe write 'ready'bit on socket ? + give some time to child to get ready */ + sleep(1); + + tpm_pt->tpm_fd =3D fds[0]; + tpm_pt->tpm_ctrl_fd =3D ctrl_fds[0]; + tpm_pt->child_running =3D true; + + qemu_add_child_watch(cpid); + + fcntl(tpm_pt->tpm_fd, F_SETFL, O_NONBLOCK); + qemu_set_fd_handler(tpm_pt->tpm_fd, tpm_emulator_fd_handler, NULL, + tpm_pt); + + /* close unsed sockets */ + closesocket(fds[1]); + closesocket(ctrl_fds[1]); + } + + return 0; +} + +static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_pt, QemuOpts *= opts) +{ + const char *value; + + value =3D qemu_opt_get(opts, "tpmstatedir"); + if (!value) { + error_report("tpm-emulator: Missing tpm state directory"); + return -1; + } + tpm_pt->ops.tpmstatedir =3D g_strdup(value); + + value =3D qemu_opt_get(opts, "path"); + if (!value) { + value =3D TPM_DEFAULT_EMULATOR; + tpm_pt->ops.has_path =3D false; + } else { + tpm_pt->ops.has_path =3D true; + if (value[0] =3D=3D '/') { + struct stat st; + if (stat(value, &st) < 0 || !(S_ISREG(st.st_mode) + || S_ISLNK(st.st_mode))) { + error_report("tpm-emulator: Invalid emulator path: %s", va= lue); + return -1; + } + } + } + tpm_pt->ops.path =3D g_strdup(value); + + value =3D qemu_opt_get(opts, "logfile"); + if (value) { + DPRINTF("tpm-emulator: LogFile: %s", value); + tpm_pt->ops.has_logfile =3D true; + tpm_pt->ops.logfile =3D g_strdup(value); + tpm_pt->ops.loglevel =3D qemu_opt_get_number(opts, "loglevel", + TPM_DEFAULT_LOGLEVEL); + tpm_pt->ops.has_loglevel =3D tpm_pt->ops.loglevel !=3D + TPM_DEFAULT_LOGLEVEL; + } + + if (tpm_emulator_spawn_emulator(tpm_pt) < 0) { + goto err_close_dev; + } + + tpm_pt->cur_locty_number =3D ~0; + + if (tpm_emulator_probe_caps(tpm_pt) || + tpm_emulator_init_tpm(tpm_pt, false)) { + goto err_close_dev; + } + + if (tpm_util_test_tpmdev(tpm_pt->tpm_fd, &tpm_pt->tpm_version)) { + error_report("'%s' is not emulating TPM device.", tpm_pt->ops.path= ); + goto err_close_dev; + } + + DPRINTF("tpm_emulator: TPM Version %s", + tpm_pt->tpm_version =3D=3D TPM_VERSION_1_2 ? "1.2" : + (tpm_pt->tpm_version =3D=3D TPM_VERSION_2_0 ? "2.0" : "Unspe= cified")); + + if (tpm_emulator_check_caps(tpm_pt)) { + goto err_close_dev; + } + + return 0; + +err_close_dev: + tpm_emulator_shutdown(tpm_pt); + return -1; +} + +static TPMBackend *tpm_emulator_create(QemuOpts *opts, const char *id) +{ + TPMBackend *tb =3D TPM_BACKEND(object_new(TYPE_TPM_EMULATOR)); + + tb->id =3D g_strdup(id); + + if (tpm_emulator_handle_device_opts(TPM_EMULATOR(tb), opts)) { + goto err_exit; + } + + return tb; + +err_exit: + object_unref(OBJECT(tb)); + + return NULL; +} + +static void tpm_emulator_destroy(TPMBackend *tb) +{ + TPMEmulator *tpm_pt =3D TPM_EMULATOR(tb); + + DPRINTF("tpm_emulator: %s", __func__); + + tpm_emulator_cancel_cmd(tb); + tpm_emulator_shutdown(tpm_pt); + + closesocket(tpm_pt->tpm_fd); + closesocket(tpm_pt->tpm_ctrl_fd); + g_free(tpm_pt->ops.tpmstatedir); + g_free(tpm_pt->ops.path); + g_free(tpm_pt->ops.logfile); +} + +static TPMOptions *tpm_emulator_get_tpm_options(TPMBackend *tb) +{ + TPMEmulator *tpm_pt =3D TPM_EMULATOR(tb); + TPMEmulatorOptions *ops =3D g_new(TPMEmulatorOptions, 1); + + if (!ops) { + return NULL; + } + DPRINTF("tpm_emulator: %s", __func__); + + ops->tpmstatedir =3D g_strdup(tpm_pt->ops.tpmstatedir); + if (tpm_pt->ops.has_path) { + ops->has_path =3D true; + ops->path =3D g_strdup(tpm_pt->ops.path); + } + if (tpm_pt->ops.has_logfile) { + ops->has_logfile =3D true; + ops->logfile =3D g_strdup(tpm_pt->ops.logfile); + } + if (tpm_pt->ops.has_loglevel) { + ops->has_loglevel =3D true; + ops->loglevel =3D tpm_pt->ops.loglevel; + } + + return (TPMOptions *)ops; +} + +static const QemuOptDesc tpm_emulator_cmdline_opts[] =3D { + TPM_STANDARD_CMDLINE_OPTS, + { + .name =3D "tpmstatedir", + .type =3D QEMU_OPT_STRING, + .help =3D "TPM state directroy", + }, + { + .name =3D "path", + .type =3D QEMU_OPT_STRING, + .help =3D "Path to TPM emulator binary", + }, + { + .name =3D "logfile", + .type =3D QEMU_OPT_STRING, + .help =3D "Path to log file", + }, + { + .name =3D "level", + .type =3D QEMU_OPT_STRING, + .help =3D "Log level number", + }, + { /* end of list */ }, +}; + +static const TPMDriverOps tpm_emulator_driver =3D { + .type =3D TPM_TYPE_EMULATOR, + .opts =3D tpm_emulator_cmdline_opts, + .desc =3D tpm_emulator_desc, + .create =3D tpm_emulator_create, + .destroy =3D tpm_emulator_destroy, + .startup_tpm =3D tpm_emulator_startup_tpm, + .realloc_buffer =3D tpm_emulator_realloc_buffer, + .reset =3D tpm_emulator_reset, + .had_startup_error =3D tpm_emulator_get_startup_error, + .cancel_cmd =3D tpm_emulator_cancel_cmd, + .get_tpm_established_flag =3D tpm_emulator_get_tpm_established_flag, + .reset_tpm_established_flag =3D tpm_emulator_reset_tpm_established_fla= g, + .get_tpm_version =3D tpm_emulator_get_tpm_version, + .get_tpm_options =3D tpm_emulator_get_tpm_options, +}; + +static void tpm_emulator_inst_init(Object *obj) +{ + TPMEmulator *tpm_pt =3D TPM_EMULATOR(obj); + + DPRINTF("tpm_emulator: %s", __func__); + tpm_pt->tpm_fd =3D tpm_pt->tpm_ctrl_fd =3D -1; + tpm_pt->op_executing =3D tpm_pt->op_canceled =3D false; + tpm_pt->child_running =3D false; + tpm_pt->cur_locty_number =3D ~0; +} + +static void tpm_emulator_class_init(ObjectClass *klass, void *data) +{ + TPMBackendClass *tbc =3D TPM_BACKEND_CLASS(klass); + tbc->ops =3D &tpm_emulator_driver; + tbc->handle_request =3D tpm_emulator_handle_request; +} + +static const TypeInfo tpm_emulator_info =3D { + .name =3D TYPE_TPM_EMULATOR, + .parent =3D TYPE_TPM_BACKEND, + .instance_size =3D sizeof(TPMEmulator), + .class_init =3D tpm_emulator_class_init, + .instance_init =3D tpm_emulator_inst_init, +}; + +static void tpm_emulator_register(void) +{ + type_register_static(&tpm_emulator_info); + tpm_register_driver(&tpm_emulator_driver); +} + +type_init(tpm_emulator_register) diff --git a/hw/tpm/tpm_ioctl.h b/hw/tpm/tpm_ioctl.h new file mode 100644 index 0000000..af49708 --- /dev/null +++ b/hw/tpm/tpm_ioctl.h @@ -0,0 +1,243 @@ +/* + * tpm_ioctl.h + * + * (c) Copyright IBM Corporation 2014, 2015. + * + * This file is licensed under the terms of the 3-clause BSD license + */ +#ifndef _TPM_IOCTL_H_ +#define _TPM_IOCTL_H_ + +#include +#include +#include +#include + +/* + * Every response from a command involving a TPM command execution must ho= ld + * the ptm_res as the first element. + * ptm_res corresponds to the error code of a command executed by the TPM. + */ + +typedef uint32_t ptm_res; + +/* PTM_GET_TPMESTABLISHED: get the establishment bit */ +struct ptm_est { + union { + struct { + ptm_res tpm_result; + unsigned char bit; /* TPM established bit */ + } resp; /* response */ + } u; +}; + +/* PTM_RESET_TPMESTABLISHED: reset establishment bit */ +struct ptm_reset_est { + union { + struct { + uint8_t loc; /* locality to use */ + } req; /* request */ + struct { + ptm_res tpm_result; + } resp; /* response */ + } u; +}; + +/* PTM_INIT */ +struct ptm_init { + union { + struct { + uint32_t init_flags; /* see definitions below */ + } req; /* request */ + struct { + ptm_res tpm_result; + } resp; /* response */ + } u; +}; + +/* above init_flags */ +#define PTM_INIT_FLAG_DELETE_VOLATILE (1 << 0) + /* delete volatile state file after reading it */ + +/* PTM_SET_LOCALITY */ +struct ptm_loc { + union { + struct { + uint8_t loc; /* locality to set */ + } req; /* request */ + struct { + ptm_res tpm_result; + } resp; /* response */ + } u; +}; + +/* PTM_HASH_DATA: hash given data */ +struct ptm_hdata { + union { + struct { + uint32_t length; + uint8_t data[4096]; + } req; /* request */ + struct { + ptm_res tpm_result; + } resp; /* response */ + } u; +}; + +/* + * size of the TPM state blob to transfer; x86_64 can handle 8k, + * ppc64le only ~7k; keep the response below a 4k page size + */ +#define PTM_STATE_BLOB_SIZE (3 * 1024) + +/* + * The following is the data structure to get state blobs from the TPM. + * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple= reads + * with this ioctl and with adjusted offset are necessary. All bytes + * must be transferred and the transfer is done once the last byte has been + * returned. + * It is possible to use the read() interface for reading the data; howeve= r, the + * first bytes of the state blob will be part of the response to the ioctl= (); a + * subsequent read() is only necessary if the total length (totlength) exc= eeds + * the number of received bytes. seek() is not supported. + */ +struct ptm_getstate { + union { + struct { + uint32_t state_flags; /* may be: PTM_STATE_FLAG_DECRYPTED */ + uint32_t type; /* which blob to pull */ + uint32_t offset; /* offset from where to read */ + } req; /* request */ + struct { + ptm_res tpm_result; + uint32_t state_flags; /* may be: PTM_STATE_FLAG_ENCRYPTED */ + uint32_t totlength; /* total length that will be transferred= */ + uint32_t length; /* number of bytes in following buffer */ + uint8_t data[PTM_STATE_BLOB_SIZE]; + } resp; /* response */ + } u; +}; + +/* TPM state blob types */ +#define PTM_BLOB_TYPE_PERMANENT 1 +#define PTM_BLOB_TYPE_VOLATILE 2 +#define PTM_BLOB_TYPE_SAVESTATE 3 + +/* state_flags above : */ +#define PTM_STATE_FLAG_DECRYPTED 1 /* on input: get decrypted state */ +#define PTM_STATE_FLAG_ENCRYPTED 2 /* on output: state is encrypted */ + +/* + * The following is the data structure to set state blobs in the TPM. + * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple + * 'writes' using this ioctl are necessary. The last packet is indicated + * by the length being smaller than the PTM_STATE_BLOB_SIZE. + * The very first packet may have a length indicator of '0' enabling + * a write() with all the bytes from a buffer. If the write() interface + * is used, a final ioctl with a non-full buffer must be made to indicate + * that all data were transferred (a write with 0 bytes would not work). + */ +struct ptm_setstate { + union { + struct { + uint32_t state_flags; /* may be PTM_STATE_FLAG_ENCRYPTED */ + uint32_t type; /* which blob to set */ + uint32_t length; /* length of the data; + use 0 on the first packet to + transfer using write() */ + uint8_t data[PTM_STATE_BLOB_SIZE]; + } req; /* request */ + struct { + ptm_res tpm_result; + } resp; /* response */ + } u; +}; + +/* + * PTM_GET_CONFIG: Data structure to get runtime configuration information + * such as which keys are applied. + */ +struct ptm_getconfig { + union { + struct { + ptm_res tpm_result; + uint32_t flags; + } resp; /* response */ + } u; +}; + +#define PTM_CONFIG_FLAG_FILE_KEY 0x1 +#define PTM_CONFIG_FLAG_MIGRATION_KEY 0x2 + + +typedef uint64_t ptm_cap; +typedef struct ptm_est ptm_est; +typedef struct ptm_reset_est ptm_reset_est; +typedef struct ptm_loc ptm_loc; +typedef struct ptm_hdata ptm_hdata; +typedef struct ptm_init ptm_init; +typedef struct ptm_getstate ptm_getstate; +typedef struct ptm_setstate ptm_setstate; +typedef struct ptm_getconfig ptm_getconfig; + +/* capability flags returned by PTM_GET_CAPABILITY */ +#define PTM_CAP_INIT (1) +#define PTM_CAP_SHUTDOWN (1 << 1) +#define PTM_CAP_GET_TPMESTABLISHED (1 << 2) +#define PTM_CAP_SET_LOCALITY (1 << 3) +#define PTM_CAP_HASHING (1 << 4) +#define PTM_CAP_CANCEL_TPM_CMD (1 << 5) +#define PTM_CAP_STORE_VOLATILE (1 << 6) +#define PTM_CAP_RESET_TPMESTABLISHED (1 << 7) +#define PTM_CAP_GET_STATEBLOB (1 << 8) +#define PTM_CAP_SET_STATEBLOB (1 << 9) +#define PTM_CAP_STOP (1 << 10) +#define PTM_CAP_GET_CONFIG (1 << 11) + +enum { + PTM_GET_CAPABILITY =3D _IOR('P', 0, ptm_cap), + PTM_INIT =3D _IOWR('P', 1, ptm_init), + PTM_SHUTDOWN =3D _IOR('P', 2, ptm_res), + PTM_GET_TPMESTABLISHED =3D _IOR('P', 3, ptm_est), + PTM_SET_LOCALITY =3D _IOWR('P', 4, ptm_loc), + PTM_HASH_START =3D _IOR('P', 5, ptm_res), + PTM_HASH_DATA =3D _IOWR('P', 6, ptm_hdata), + PTM_HASH_END =3D _IOR('P', 7, ptm_res), + PTM_CANCEL_TPM_CMD =3D _IOR('P', 8, ptm_res), + PTM_STORE_VOLATILE =3D _IOR('P', 9, ptm_res), + PTM_RESET_TPMESTABLISHED =3D _IOWR('P', 10, ptm_reset_est), + PTM_GET_STATEBLOB =3D _IOWR('P', 11, ptm_getstate), + PTM_SET_STATEBLOB =3D _IOWR('P', 12, ptm_setstate), + PTM_STOP =3D _IOR('P', 13, ptm_res), + PTM_GET_CONFIG =3D _IOR('P', 14, ptm_getconfig), +}; + +/* + * Commands used by the non-CUSE TPMs + * + * All messages container big-endian data. + * + * The return messages only contain the 'resp' part of the unions + * in the data structures above. Besides that the limits in the + * buffers above (ptm_hdata:u.req.data and ptm_get_state:u.resp.data + * and ptm_set_state:u.req.data) are 0xffffffff. + */ +enum { + CMD_GET_CAPABILITY =3D 1, + CMD_INIT, + CMD_SHUTDOWN, + CMD_GET_TPMESTABLISHED, + CMD_SET_LOCALITY, + CMD_HASH_START, + CMD_HASH_DATA, + CMD_HASH_END, + CMD_CANCEL_TPM_CMD, + CMD_STORE_VOLATILE, + CMD_RESET_TPMESTABLISHED, + CMD_GET_STATEBLOB, + CMD_SET_STATEBLOB, + CMD_STOP, + CMD_GET_CONFIG, +}; + +#endif /* _TPM_IOCTL_H */ diff --git a/hw/tpm/tpm_util.c b/hw/tpm/tpm_util.c index 5475acf..34b1d59 100644 --- a/hw/tpm/tpm_util.c +++ b/hw/tpm/tpm_util.c @@ -22,6 +22,7 @@ #include "qemu/osdep.h" #include "tpm_util.h" #include "tpm_int.h" +#include "tpm_ioctl.h" =20 int tpm_util_unix_write(int fd, const uint8_t *buf, uint32_t len) { @@ -185,3 +186,36 @@ int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_v= ersion) =20 return 1; } + +static unsigned long ioctl_to_cmd(unsigned long ioctlnum) +{ + /* the ioctl number contains the command number - 1 */ + return ((ioctlnum >> _IOC_NRSHIFT) & _IOC_NRMASK) + 1; +} + +int tpm_util_ctrlcmd(int fd, unsigned long cmd, void *msg, size_t msg_len_= in, + size_t msg_len_out) +{ + int n; + + uint32_t cmd_no =3D cpu_to_be32(ioctl_to_cmd(cmd)); + struct iovec iov[2] =3D { + { .iov_base =3D &cmd_no, .iov_len =3D sizeof(cmd_no), }, + { .iov_base =3D msg, .iov_len =3D msg_len_in, }, + }; + + n =3D writev(fd, iov, 2); + if (n > 0) { + if (msg_len_out > 0) { + n =3D read(fd, msg, msg_len_out); + /* simulate ioctl return value */ + if (n > 0) { + n =3D 0; + } + } else { + n =3D 0; + } + } + return n; +} + diff --git a/hw/tpm/tpm_util.h b/hw/tpm/tpm_util.h index c2feca7..b93d484 100644 --- a/hw/tpm/tpm_util.h +++ b/hw/tpm/tpm_util.h @@ -34,4 +34,7 @@ bool tpm_util_is_selftest(const uint8_t *in, uint32_t in_= len); =20 int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version); =20 +int tpm_util_ctrlcmd(int fd, unsigned long cmd, void *msg, + size_t msg_len_in, size_t msg_len_out); + #endif /* TPM_TPM_UTIL_H */ diff --git a/qapi-schema.json b/qapi-schema.json index 5faf1ac..e8ddbc6 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -5117,10 +5117,11 @@ # An enumeration of TPM types # # @passthrough: TPM passthrough type +# @emulator: Software Emulator TPM type # # Since: 1.5 ## -{ 'enum': 'TpmType', 'data': [ 'passthrough' ] } +{ 'enum': 'TpmType', 'data': [ 'passthrough', 'emulator' ] } =20 ## # @query-tpm-types: @@ -5163,6 +5164,22 @@ 'data': { '*path' : 'str', '*cancel-path' : 'str'} } =20 ## +# @TPMEmulatorOptions: +# +# Information about the TPM emulator +# +# @tpmstatedir: TPM emilator state dir +# @path: TPM emulator binary path to use +# @logfile: file to use to place TPM emulator logs +# @loglevel: log level number +# +# Since: 2.6 +## +{ 'struct': 'TPMEmulatorOptions', 'base': 'TPMOptions', + 'data': { 'tpmstatedir' : 'str', '*path': 'str', + '*logfile' : 'str', '*loglevel' : 'int' } } + +## # @TpmTypeOptions: # # A union referencing different TPM backend types' configuration options @@ -5172,7 +5189,8 @@ # Since: 1.5 ## { 'union': 'TpmTypeOptions', - 'data': { 'passthrough' : 'TPMPassthroughOptions' } } + 'data': { 'passthrough' : 'TPMPassthroughOptions', + 'emulator' : 'TPMEmulatorOptions' } } =20 ## # @TPMInfo: diff --git a/qemu-options.hx b/qemu-options.hx index 99af8ed..5bbf187 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2846,7 +2846,12 @@ DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \ "-tpmdev passthrough,id=3Did[,path=3Dpath][,cancel-path=3Dpath]\n" " use path to provide path to a character device; defau= lt is /dev/tpm0\n" " use cancel-path to provide path to TPM's cancel sysfs= entry; if\n" - " not provided it will be searched for in /sys/class/mi= sc/tpm?/device\n", + " not provided it will be searched for in /sys/class/mi= sc/tpm?/device\n" + "-tpmdev emulator,id=3Did,tpmstatedir=3Ddir[,path=3Demulator-path,logf= ile=3Dpath,loglevel=3Dlevel]\n" + " use tpmstatedir to provide path to the tpm state dirc= tory\n" + " use path to provide the emulator binary to launch; de= fault is 'swtpm'\n" + " use logfile to provide where to place the swtpm logs\= n" + " use loglevel to controls the swtpm log level\n", QEMU_ARCH_ALL) STEXI =20 @@ -2855,8 +2860,8 @@ The general form of a TPM device option is: =20 @item -tpmdev @var{backend} ,id=3D@var{id} [,@var{options}] @findex -tpmdev -Backend type must be: -@option{passthrough}. +Backend type must be either one of the following: +@option{passthrough}, @option{emulator}. =20 The specific backend type will determine the applicable options. The @code{-tpmdev} option creates the TPM backend and requires a @@ -2906,6 +2911,20 @@ To create a passthrough TPM use the following two op= tions: Note that the @code{-tpmdev} id is @code{tpm0} and is referenced by @code{tpmdev=3Dtpm0} in the device option. =20 +@item -tpmdev emulator, id=3D@var{id}, tpmstatedir=3D@var{path}, path=3D@v= ar{emulator-binary-path}, logfile=3D@var{path}, logevel=3D@var{level} + +(Linux-host only) Enable access to a TPM emulator. + +@option{tpmstatedir} specifies the tpm state directory +@option{path} specifies the emulator binary path to use +@option{logfile} optional log file to use to place log messages +@option{loglevel} specifies the log level to use + +To create a TPM emulator backend device: +@example +-tpmdev emulator,id=3Dtpm0,tpmstatedir=3D/tmp/my-tpm,path=3D/usr/local/bin= /swtpm,logfile=3D/tmp/qemu-tpm.log,logevel=3D5 -device tpm-tis,tpmdev=3Dtpm0 +@end example + @end table =20 ETEXI diff --git a/tpm.c b/tpm.c index c221000..ed110d2 100644 --- a/tpm.c +++ b/tpm.c @@ -25,7 +25,7 @@ static QLIST_HEAD(, TPMBackend) tpm_backends =3D =20 =20 #define TPM_MAX_MODELS 1 -#define TPM_MAX_DRIVERS 1 +#define TPM_MAX_DRIVERS 2 =20 static TPMDriverOps const *be_drivers[TPM_MAX_DRIVERS] =3D { NULL, @@ -263,6 +263,11 @@ static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv) res->options->u.passthrough.data =3D (TPMPassthroughOptions *)tpm_backend_get_tpm_options(drv); break; + case TPM_TYPE_EMULATOR: + res->options->type =3D TPM_TYPE_OPTIONS_KIND_EMULATOR; + res->options->u.emulator.data =3D + (TPMEmulatorOptions *) tpm_backend_get_tpm_options(drv); + break; case TPM_TYPE__MAX: break; } --=20 2.7.4 From nobody Mon Apr 29 14:14:38 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1490972600874685.1507595227644; Fri, 31 Mar 2017 08:03:20 -0700 (PDT) Received: from localhost ([::1]:41473 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cty58-0000Ao-NS for importer@patchew.org; Fri, 31 Mar 2017 11:03:18 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41040) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ctwK4-0003nV-VB for qemu-devel@nongnu.org; Fri, 31 Mar 2017 09:10:44 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ctwK0-0000eI-No for qemu-devel@nongnu.org; Fri, 31 Mar 2017 09:10:36 -0400 Received: from mga09.intel.com ([134.134.136.24]:16468) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1ctwK0-0000WJ-6q for qemu-devel@nongnu.org; Fri, 31 Mar 2017 09:10:32 -0400 Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 31 Mar 2017 06:10:30 -0700 Received: from avallurigigabyte.fi.intel.com ([10.237.72.170]) by fmsmga001.fm.intel.com with ESMTP; 31 Mar 2017 06:10:24 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=intel.com; i=@intel.com; q=dns/txt; s=intel; t=1490965832; x=1522501832; h=from:to:cc:subject:date:message-id:in-reply-to: references; bh=39jJDBL1gYqBbKIGQG5BA/IDjupVfvRFJB30dzHBOrA=; b=Sm32u6Ye4CXhdApee9z5Vi8KLqGXLQjfo1uAwzSCfqZJNpDGdTORqGCi +SIm8jg7hp/6IEn5m9KJBQ6VKcQuoQ==; X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.36,251,1486454400"; d="scan'208";a="1129316678" From: Amarnath Valluri To: qemu-devel@nongnu.org Date: Fri, 31 Mar 2017 16:10:17 +0300 Message-Id: <1490965817-16913-9-git-send-email-amarnath.valluri@intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1490965817-16913-1-git-send-email-amarnath.valluri@intel.com> References: <1490965817-16913-1-git-send-email-amarnath.valluri@intel.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 134.134.136.24 X-Mailman-Approved-At: Fri, 31 Mar 2017 10:56:09 -0400 Subject: [Qemu-devel] [PATCH 7/7] tpm: New backend driver to support TPM emulator X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Amarnath Valluri , patrick.ohly@intel.com, stefanb@linux.vnet.ibm.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" This change introduces a new TPM backend driver that can communicates with swtpm(software TPM emulator) using unix domain socket interface. Swtpm uses two unix sockets, one for plain TPM commands and responses, and = one for out-of-band control messages. The swtpm and associated tools can be found here: https://github.com/stefanberger/swtpm Usage: # setup TPM state directory mkdir /tmp/mytpm chown -R tss:root /tmp/mytpm /usr/bin/swtpm_setup --tpm-state /tmp/mytpm --createek # Ask qeum to use TPM emulator with given tpm state directory qemu-system-x86_64 \ [...] \ -tpmdev emulator,id=3Dtpm0,tpmstatedir=3D/tmp/mytpm,logfile=3D/tmp/= swtpm.log \ -device tpm-tis,tpmdev=3Dtpm0 \ [...] Signed-off-by: Amarnath Valluri --- configure | 6 + hmp.c | 14 + hw/tpm/Makefile.objs | 1 + hw/tpm/tpm_emulator.c | 740 ++++++++++++++++++++++++++++++++++++++++++++++= ++++ hw/tpm/tpm_ioctl.h | 243 +++++++++++++++++ hw/tpm/tpm_util.c | 34 +++ hw/tpm/tpm_util.h | 3 + qapi-schema.json | 22 +- qemu-options.hx | 25 +- tpm.c | 7 +- 10 files changed, 1089 insertions(+), 6 deletions(-) create mode 100644 hw/tpm/tpm_emulator.c create mode 100644 hw/tpm/tpm_ioctl.h diff --git a/configure b/configure index 4901b9a..9089546 100755 --- a/configure +++ b/configure @@ -3349,8 +3349,10 @@ fi =20 if test "$targetos" =3D Linux && test "$cpu" =3D i386 -o "$cpu" =3D x86_64= ; then tpm_passthrough=3D$tpm + tpm_emulator=3D$tpm else tpm_passthrough=3Dno + tpm_emulator=3Dno fi =20 ########################################## @@ -5125,6 +5127,7 @@ echo "gcov enabled $gcov" echo "TPM support $tpm" echo "libssh2 support $libssh2" echo "TPM passthrough $tpm_passthrough" +echo "TPM emulator $tpm_emulator" echo "QOM debugging $qom_cast_debug" echo "lzo support $lzo" echo "snappy support $snappy" @@ -5704,6 +5707,9 @@ if test "$tpm" =3D "yes"; then if test "$tpm_passthrough" =3D "yes"; then echo "CONFIG_TPM_PASSTHROUGH=3Dy" >> $config_host_mak fi + if test "$tpm_emulator" =3D "yes"; then + echo "CONFIG_TPM_EMULATOR=3Dy" >> $config_host_mak + fi fi =20 echo "TRACE_BACKENDS=3D$trace_backends" >> $config_host_mak diff --git a/hmp.c b/hmp.c index edb8970..03a47e2 100644 --- a/hmp.c +++ b/hmp.c @@ -937,6 +937,7 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict) Error *err =3D NULL; unsigned int c =3D 0; TPMPassthroughOptions *tpo; + TPMEmulatorOptions *teo; =20 info_list =3D qmp_query_tpm(&err); if (err) { @@ -966,6 +967,19 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict) tpo->has_cancel_path ? ",cancel-path=3D" : "", tpo->has_cancel_path ? tpo->cancel_path : ""); break; + case TPM_TYPE_OPTIONS_KIND_EMULATOR: + teo =3D ti->options->u.emulator.data; + monitor_printf(mon, ",tmpstatedir=3D%s", teo->tpmstatedir); + if (teo->has_path) { + monitor_printf(mon, ",path=3D%s", teo->path); + } + if (teo->has_logfile) { + monitor_printf(mon, ",logfile=3D%s", teo->logfile); + } + if (teo->has_loglevel) { + monitor_printf(mon, ",loglevel=3D%ld", teo->loglevel); + } + break; case TPM_TYPE_OPTIONS_KIND__MAX: break; } diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs index 64cecc3..41f0b7a 100644 --- a/hw/tpm/Makefile.objs +++ b/hw/tpm/Makefile.objs @@ -1,2 +1,3 @@ common-obj-$(CONFIG_TPM_TIS) +=3D tpm_tis.o common-obj-$(CONFIG_TPM_PASSTHROUGH) +=3D tpm_passthrough.o tpm_util.o +common-obj-$(CONFIG_TPM_EMULATOR) +=3D tpm_emulator.o tpm_util.o diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c new file mode 100644 index 0000000..5b1dcfa --- /dev/null +++ b/hw/tpm/tpm_emulator.c @@ -0,0 +1,740 @@ +/* + * emulator TPM driver + * + * Copyright (c) 2017 Intel Corporation + * Author: Amarnath Valluri + * + * Copyright (c) 2010 - 2013 IBM Corporation + * Authors: + * Stefan Berger + * + * 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 + * + * The origin of the code is from CUSE driver posed by Stefan Berger: + * https://github.com/stefanberger/qemu-tpm + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "sysemu/tpm_backend.h" +#include "tpm_int.h" +#include "hw/hw.h" +#include "hw/i386/pc.h" +#include "sysemu/tpm_backend_int.h" +#include "tpm_util.h" +#include "tpm_ioctl.h" +#include "qapi/error.h" + +#include +#include +#include +#include + +#define DEBUG_TPM 0 + +#define DPRINT(fmt, ...) do { \ + if (DEBUG_TPM) { \ + fprintf(stderr, fmt, ## __VA_ARGS__); \ + } \ +} while (0); + +#define DPRINTF(fmt, ...) DPRINT(fmt"\n", __VA_ARGS__) + +#define TYPE_TPM_EMULATOR "emulator" +#define TPM_EMULATOR(obj) \ + OBJECT_CHECK(TPMEmulator, (obj), TYPE_TPM_EMULATOR) + +static const TPMDriverOps tpm_emulator_driver; + +/* data structures */ +typedef struct TPMEmulator { + TPMBackend parent; + + TPMEmulatorOptions ops; + int tpm_fd; + int tpm_ctrl_fd; + bool op_executing; + bool op_canceled; + bool child_running; + TPMVersion tpm_version; + ptm_cap caps; /* capabilities of the TPM */ + uint8_t cur_locty_number; /* last set locality */ +} TPMEmulator; + +#define TPM_DEFAULT_EMULATOR "swtpm" +#define TPM_DEFAULT_LOGLEVEL 5 +#define TPM_EUMLATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps & (cap)) =3D= =3D (cap)) + +static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_pt, + const uint8_t *in, uint32_t in_len, + uint8_t *out, uint32_t out_len, + bool *selftest_done) +{ + int ret; + bool is_selftest; + const struct tpm_resp_hdr *hdr; + + if (!tpm_pt->child_running) { + return -1; + } + + tpm_pt->op_canceled =3D false; + tpm_pt->op_executing =3D true; + *selftest_done =3D false; + + is_selftest =3D tpm_util_is_selftest(in, in_len); + + ret =3D tpm_util_unix_write(tpm_pt->tpm_fd, in, in_len); + if (ret !=3D in_len) { + if (!tpm_pt->op_canceled || errno !=3D ECANCELED) { + error_report("tpm_emulator: error while transmitting data " + "to TPM: %s (%i)", strerror(errno), errno); + } + goto err_exit; + } + + tpm_pt->op_executing =3D false; + + ret =3D tpm_util_unix_read(tpm_pt->tpm_fd, out, out_len); + if (ret < 0) { + if (!tpm_pt->op_canceled || errno !=3D ECANCELED) { + error_report("tpm_emulator: error while reading data from " + "TPM: %s (%i)", strerror(errno), errno); + } + } else if (ret < sizeof(struct tpm_resp_hdr) || + be32_to_cpu(((struct tpm_resp_hdr *)out)->len) !=3D ret) { + ret =3D -1; + error_report("tpm_emulator: received invalid response " + "packet from TPM"); + } + + if (is_selftest && (ret >=3D sizeof(struct tpm_resp_hdr))) { + hdr =3D (struct tpm_resp_hdr *)out; + *selftest_done =3D (be32_to_cpu(hdr->errcode) =3D=3D 0); + } + +err_exit: + if (ret < 0) { + tpm_util_write_fatal_error_response(out, out_len); + } + + tpm_pt->op_executing =3D false; + + return ret; +} + +static int tpm_emulator_set_locality(TPMEmulator *tpm_pt, + uint8_t locty_number) +{ + ptm_loc loc; + + if (!tpm_pt->child_running) { + return -1; + } + + DPRINTF("tpm_emulator: %s : locality: 0x%x", __func__, locty_number); + + if (tpm_pt->cur_locty_number !=3D locty_number) { + DPRINTF("tpm-emulator: setting locality : 0x%x", locty_number); + loc.u.req.loc =3D cpu_to_be32(locty_number); + if (tpm_util_ctrlcmd(tpm_pt->tpm_ctrl_fd, PTM_SET_LOCALITY, &loc, + sizeof(loc), sizeof(loc)) < 0) { + error_report("tpm-emulator: could not set locality : %s", + strerror(errno)); + return -1; + } + loc.u.resp.tpm_result =3D be32_to_cpu(loc.u.resp.tpm_result); + if (loc.u.resp.tpm_result !=3D 0) { + error_report("tpm-emulator: TPM result for set locality : 0x%x= ", + loc.u.resp.tpm_result); + return -1; + } + tpm_pt->cur_locty_number =3D locty_number; + } + return 0; +} + +static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd cmd) +{ + TPMEmulator *tpm_pt =3D TPM_EMULATOR(tb); + TPMLocality *locty =3D NULL; + bool selftest_done =3D false; + + DPRINTF("tpm_emulator: processing command type %d", cmd); + + switch (cmd) { + case TPM_BACKEND_CMD_PROCESS_CMD: + locty =3D tb->tpm_state->locty_data; + if (tpm_emulator_set_locality(tpm_pt, + tb->tpm_state->locty_number) < 0) { + tpm_util_write_fatal_error_response(locty->r_buffer.buffer, + locty->r_buffer.size); + } else { + tpm_emulator_unix_tx_bufs(tpm_pt, locty->w_buffer.buffer, + locty->w_offset, locty->r_buffer.buffer, + locty->r_buffer.size, &selftest_done); + } + tb->recv_data_callback(tb->tpm_state, tb->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; + } +} + +/* + * Gracefully shut down the external unixio TPM + */ +static void tpm_emulator_shutdown(TPMEmulator *tpm_pt) +{ + ptm_res res; + + if (!tpm_pt->child_running) { + return; + } + + if (tpm_util_ctrlcmd(tpm_pt->tpm_ctrl_fd, PTM_SHUTDOWN, &res, 0, + sizeof(res)) < 0) { + error_report("tpm-emulator: Could not cleanly shutdown the TPM: %s= ", + strerror(errno)); + } else if (res !=3D 0) { + error_report("tpm-emulator: TPM result for sutdown: 0x%x", + be32_to_cpu(res)); + } +} + +static int tpm_emulator_probe_caps(TPMEmulator *tpm_pt) +{ + if (!tpm_pt->child_running) { + return -1; + } + + DPRINTF("tpm_emulator: %s", __func__); + if (tpm_util_ctrlcmd(tpm_pt->tpm_ctrl_fd, PTM_GET_CAPABILITY, + &tpm_pt->caps, 0, sizeof(tpm_pt->caps)) < 0) { + error_report("tpm-emulator: probing failed : %s", strerror(errno)); + return -1; + } + + tpm_pt->caps =3D be64_to_cpu(tpm_pt->caps); + + DPRINTF("tpm-emulator: capbilities : 0x%lx", tpm_pt->caps); + + return 0; +} + +static int tpm_emulator_check_caps(TPMEmulator *tpm_pt) +{ + ptm_cap caps =3D 0; + const char *tpm =3D NULL; + + /* check for min. required capabilities */ + switch (tpm_pt->tpm_version) { + case TPM_VERSION_1_2: + caps =3D PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLIS= HED | + PTM_CAP_SET_LOCALITY; + tpm =3D "1.2"; + break; + case TPM_VERSION_2_0: + caps =3D PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLIS= HED | + PTM_CAP_SET_LOCALITY | PTM_CAP_RESET_TPMESTABLISHED; + tpm =3D "2"; + break; + case TPM_VERSION_UNSPEC: + error_report("tpm-emulator: TPM version has not been set"); + return -1; + } + + if (!TPM_EUMLATOR_IMPLEMENTS_ALL_CAPS(tpm_pt, caps)) { + error_report("tpm-emulator: TPM does not implement minimum set of " + "required capabilities for TPM %s (0x%x)", tpm, (int)= caps); + return -1; + } + + return 0; +} + +static int tpm_emulator_init_tpm(TPMEmulator *tpm_pt, bool is_resume) +{ + ptm_init init; + ptm_res res; + + if (!tpm_pt->child_running) { + return -1; + } + + DPRINTF("tpm_emulator: %s", __func__); + if (is_resume) { + init.u.req.init_flags =3D cpu_to_be32(PTM_INIT_FLAG_DELETE_VOLATIL= E); + } + + if (tpm_util_ctrlcmd(tpm_pt->tpm_ctrl_fd, PTM_INIT, &init, sizeof(init= ), + sizeof(init)) < 0) { + error_report("tpm-emulator: could not send INIT: %s", + strerror(errno)); + return -1; + } + + res =3D be32_to_cpu(init.u.resp.tpm_result); + if (res) { + error_report("tpm-emulator: TPM result for PTM_INIT: 0x%x", res); + return -1; + } + + return 0; +} + +static int tpm_emulator_startup_tpm(TPMBackend *tb) +{ + TPMEmulator *tpm_pt =3D TPM_EMULATOR(tb); + + DPRINTF("tpm_emulator: %s", __func__); + + tpm_emulator_init_tpm(tpm_pt, false) ; + + return 0; +} + +static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb) +{ + TPMEmulator *tpm_pt =3D TPM_EMULATOR(tb); + ptm_est est; + + DPRINTF("tpm_emulator: %s", __func__); + if (tpm_util_ctrlcmd(tpm_pt->tpm_ctrl_fd, PTM_GET_TPMESTABLISHED, &est= , 0, + sizeof(est)) < 0) { + error_report("tpm-emulator: Could not get the TPM established flag= : %s", + strerror(errno)); + return false; + } + DPRINTF("tpm_emulator: established flag: %0x", est.u.resp.bit); + + return (est.u.resp.bit !=3D 0); +} + +static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb, + uint8_t locty) +{ + TPMEmulator *tpm_pt =3D TPM_EMULATOR(tb); + ptm_reset_est reset_est; + ptm_res res; + + /* only a TPM 2.0 will support this */ + if (tpm_pt->tpm_version =3D=3D TPM_VERSION_2_0) { + reset_est.u.req.loc =3D cpu_to_be32(tpm_pt->cur_locty_number); + + if (tpm_util_ctrlcmd(tpm_pt->tpm_ctrl_fd, PTM_RESET_TPMESTABLISHED, + &reset_est, sizeof(reset_est), + sizeof(reset_est)) < 0) { + error_report("tpm-emulator: Could not reset the establishment = bit: " + "%s", strerror(errno)); + return -1; + } + + res =3D be32_to_cpu(reset_est.u.resp.tpm_result); + if (res) { + error_report("tpm-emulator: TPM result for rest establixhed fl= ag: " + "0x%x", res); + return -1; + } + } + + return 0; +} + +static bool tpm_emulator_get_startup_error(TPMBackend *tb) +{ + TPMEmulator *tpm_pt =3D TPM_EMULATOR(tb); + + return !tpm_pt->child_running; +} + +static size_t tpm_emulator_realloc_buffer(TPMSizedBuffer *sb) +{ + size_t wanted_size =3D 4096; /* Linux tpm.c buffer size */ + + if (sb->size !=3D wanted_size) { + sb->buffer =3D g_realloc(sb->buffer, wanted_size); + sb->size =3D wanted_size; + } + return sb->size; +} + +static void tpm_emulator_cancel_cmd(TPMBackend *tb) +{ + TPMEmulator *tpm_pt =3D TPM_EMULATOR(tb); + ptm_res res; + + /* + * 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->op_executing) { + if (TPM_EUMLATOR_IMPLEMENTS_ALL_CAPS(tpm_pt, PTM_CAP_CANCEL_TPM_CM= D)) { + if (tpm_util_ctrlcmd(tpm_pt->tpm_ctrl_fd, PTM_CANCEL_TPM_CMD, = &res, + 0, sizeof(res)) < 0) { + error_report("tpm-emulator: Could not cancel command: %s", + strerror(errno)); + } else if (res !=3D 0) { + error_report("tpm-emulator: Failed to cancel TPM: 0x%x", + be32_to_cpu(res)); + } else { + tpm_pt->op_canceled =3D true; + } + } + } +} + +static void tpm_emulator_reset(TPMBackend *tb) +{ + DPRINTF("tpm_emulator: %s", __func__); + + tpm_emulator_cancel_cmd(tb); +} + +static const char *tpm_emulator_desc(void) +{ + return "TPM emulator backend driver"; +} + +static TPMVersion tpm_emulator_get_tpm_version(TPMBackend *tb) +{ + TPMEmulator *tpm_pt =3D TPM_EMULATOR(tb); + + return tpm_pt->tpm_version; +} + +static void tpm_emulator_fd_handler(void *opaque) +{ + TPMEmulator *tpm_pt =3D opaque; + char val =3D 0; + ssize_t size; + + qemu_set_fd_handler(tpm_pt->tpm_fd, NULL, NULL, NULL); + + size =3D qemu_recv(tpm_pt->tpm_fd, &val, 1, MSG_PEEK); + if (!size) { + error_report("TPM backend disappeared"); + tpm_pt->child_running =3D false; + } else { + DPRINT("tpm-emulator: unexpected data on TPM\n"); + } +} + +static int tpm_emulator_spawn_emulator(TPMEmulator *tpm_pt) +{ + int fds[2]; + int ctrl_fds[2]; + pid_t cpid; + + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) < 0) { + return -1; + } + + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, ctrl_fds) < 0) { + closesocket(fds[0]); + closesocket(fds[1]); + return -1; + } + + cpid =3D fork(); + if (cpid < 0) { + error_report("tpm-emulator: Fork failure: %s", strerror(errno)); + closesocket(fds[0]); closesocket(fds[1]); + closesocket(ctrl_fds[0]); closesocket(ctrl_fds[1]); + return -1; + } + + if (cpid =3D=3D 0) { /* CHILD */ + int i; + char fd_str[128] =3D ""; + char ctrl_fd_str[128] =3D ""; + char tpmstate_str[1024] =3D ""; + char log_str[1024] =3D ""; + const char *params[] =3D { + tpm_pt->ops.path, "socket", + "--fd", fd_str, + "--ctrl", ctrl_fd_str, + "--tpmstate", tpmstate_str, + "--log", log_str, + NULL /* End */ + }; + + /* close all unused inherited sockets */ + closesocket(fds[0]); + closesocket(ctrl_fds[0]); + for (i =3D STDERR_FILENO + 1; i < fds[1]; i++) { + closesocket(i); + } + + sprintf(fd_str, "%d", fds[1]); + sprintf(ctrl_fd_str, "type=3Dunixio,clientfd=3D%d", ctrl_fds[1]); + sprintf(tpmstate_str, "dir=3D%s", tpm_pt->ops.tpmstatedir); + if (tpm_pt->ops.has_logfile) { + sprintf(log_str, "file=3D%s,level=3D%d", tpm_pt->ops.logfile, + (int)tpm_pt->ops.loglevel); + } else { + /* truncate logs */ + params[8] =3D NULL; + } + DPRINT("Running cmd: ") + for (i =3D 0; params[i]; i++) { + DPRINT(" %s", params[i]) + } + DPRINT("\n") + if (execv(tpm_pt->ops.path, (char * const *)params) < 0) { + error_report("execv() failure : %s", strerror(errno)); + } + closesocket(fds[1]); + closesocket(ctrl_fds[1]); + exit(0); + } else { /* self */ + DPRINTF("tpm-emulator: child pid: %d", cpid); + /* FIXME: find better way of finding swtpm ready + maybe write 'ready'bit on socket ? + give some time to child to get ready */ + sleep(1); + + tpm_pt->tpm_fd =3D fds[0]; + tpm_pt->tpm_ctrl_fd =3D ctrl_fds[0]; + tpm_pt->child_running =3D true; + + qemu_add_child_watch(cpid); + + fcntl(tpm_pt->tpm_fd, F_SETFL, O_NONBLOCK); + qemu_set_fd_handler(tpm_pt->tpm_fd, tpm_emulator_fd_handler, NULL, + tpm_pt); + + /* close unsed sockets */ + closesocket(fds[1]); + closesocket(ctrl_fds[1]); + } + + return 0; +} + +static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_pt, QemuOpts *= opts) +{ + const char *value; + + value =3D qemu_opt_get(opts, "tpmstatedir"); + if (!value) { + error_report("tpm-emulator: Missing tpm state directory"); + return -1; + } + tpm_pt->ops.tpmstatedir =3D g_strdup(value); + + value =3D qemu_opt_get(opts, "path"); + if (!value) { + value =3D TPM_DEFAULT_EMULATOR; + tpm_pt->ops.has_path =3D false; + } else { + tpm_pt->ops.has_path =3D true; + if (value[0] =3D=3D '/') { + struct stat st; + if (stat(value, &st) < 0 || !(S_ISREG(st.st_mode) + || S_ISLNK(st.st_mode))) { + error_report("tpm-emulator: Invalid emulator path: %s", va= lue); + return -1; + } + } + } + tpm_pt->ops.path =3D g_strdup(value); + + value =3D qemu_opt_get(opts, "logfile"); + if (value) { + DPRINTF("tpm-emulator: LogFile: %s", value); + tpm_pt->ops.has_logfile =3D true; + tpm_pt->ops.logfile =3D g_strdup(value); + tpm_pt->ops.loglevel =3D qemu_opt_get_number(opts, "loglevel", + TPM_DEFAULT_LOGLEVEL); + tpm_pt->ops.has_loglevel =3D tpm_pt->ops.loglevel !=3D + TPM_DEFAULT_LOGLEVEL; + } + + if (tpm_emulator_spawn_emulator(tpm_pt) < 0) { + goto err_close_dev; + } + + tpm_pt->cur_locty_number =3D ~0; + + if (tpm_emulator_probe_caps(tpm_pt) || + tpm_emulator_init_tpm(tpm_pt, false)) { + goto err_close_dev; + } + + if (tpm_util_test_tpmdev(tpm_pt->tpm_fd, &tpm_pt->tpm_version)) { + error_report("'%s' is not emulating TPM device.", tpm_pt->ops.path= ); + goto err_close_dev; + } + + DPRINTF("tpm_emulator: TPM Version %s", + tpm_pt->tpm_version =3D=3D TPM_VERSION_1_2 ? "1.2" : + (tpm_pt->tpm_version =3D=3D TPM_VERSION_2_0 ? "2.0" : "Unspe= cified")); + + if (tpm_emulator_check_caps(tpm_pt)) { + goto err_close_dev; + } + + return 0; + +err_close_dev: + tpm_emulator_shutdown(tpm_pt); + return -1; +} + +static TPMBackend *tpm_emulator_create(QemuOpts *opts, const char *id) +{ + TPMBackend *tb =3D TPM_BACKEND(object_new(TYPE_TPM_EMULATOR)); + + tb->id =3D g_strdup(id); + + if (tpm_emulator_handle_device_opts(TPM_EMULATOR(tb), opts)) { + goto err_exit; + } + + return tb; + +err_exit: + object_unref(OBJECT(tb)); + + return NULL; +} + +static void tpm_emulator_destroy(TPMBackend *tb) +{ + TPMEmulator *tpm_pt =3D TPM_EMULATOR(tb); + + DPRINTF("tpm_emulator: %s", __func__); + + tpm_emulator_cancel_cmd(tb); + tpm_emulator_shutdown(tpm_pt); + + closesocket(tpm_pt->tpm_fd); + closesocket(tpm_pt->tpm_ctrl_fd); + g_free(tpm_pt->ops.tpmstatedir); + g_free(tpm_pt->ops.path); + g_free(tpm_pt->ops.logfile); +} + +static TPMOptions *tpm_emulator_get_tpm_options(TPMBackend *tb) +{ + TPMEmulator *tpm_pt =3D TPM_EMULATOR(tb); + TPMEmulatorOptions *ops =3D g_new(TPMEmulatorOptions, 1); + + if (!ops) { + return NULL; + } + DPRINTF("tpm_emulator: %s", __func__); + + ops->tpmstatedir =3D g_strdup(tpm_pt->ops.tpmstatedir); + if (tpm_pt->ops.has_path) { + ops->has_path =3D true; + ops->path =3D g_strdup(tpm_pt->ops.path); + } + if (tpm_pt->ops.has_logfile) { + ops->has_logfile =3D true; + ops->logfile =3D g_strdup(tpm_pt->ops.logfile); + } + if (tpm_pt->ops.has_loglevel) { + ops->has_loglevel =3D true; + ops->loglevel =3D tpm_pt->ops.loglevel; + } + + return (TPMOptions *)ops; +} + +static const QemuOptDesc tpm_emulator_cmdline_opts[] =3D { + TPM_STANDARD_CMDLINE_OPTS, + { + .name =3D "tpmstatedir", + .type =3D QEMU_OPT_STRING, + .help =3D "TPM state directroy", + }, + { + .name =3D "path", + .type =3D QEMU_OPT_STRING, + .help =3D "Path to TPM emulator binary", + }, + { + .name =3D "logfile", + .type =3D QEMU_OPT_STRING, + .help =3D "Path to log file", + }, + { + .name =3D "level", + .type =3D QEMU_OPT_STRING, + .help =3D "Log level number", + }, + { /* end of list */ }, +}; + +static const TPMDriverOps tpm_emulator_driver =3D { + .type =3D TPM_TYPE_EMULATOR, + .opts =3D tpm_emulator_cmdline_opts, + .desc =3D tpm_emulator_desc, + .create =3D tpm_emulator_create, + .destroy =3D tpm_emulator_destroy, + .startup_tpm =3D tpm_emulator_startup_tpm, + .realloc_buffer =3D tpm_emulator_realloc_buffer, + .reset =3D tpm_emulator_reset, + .had_startup_error =3D tpm_emulator_get_startup_error, + .cancel_cmd =3D tpm_emulator_cancel_cmd, + .get_tpm_established_flag =3D tpm_emulator_get_tpm_established_flag, + .reset_tpm_established_flag =3D tpm_emulator_reset_tpm_established_fla= g, + .get_tpm_version =3D tpm_emulator_get_tpm_version, + .get_tpm_options =3D tpm_emulator_get_tpm_options, +}; + +static void tpm_emulator_inst_init(Object *obj) +{ + TPMEmulator *tpm_pt =3D TPM_EMULATOR(obj); + + DPRINTF("tpm_emulator: %s", __func__); + tpm_pt->tpm_fd =3D tpm_pt->tpm_ctrl_fd =3D -1; + tpm_pt->op_executing =3D tpm_pt->op_canceled =3D false; + tpm_pt->child_running =3D false; + tpm_pt->cur_locty_number =3D ~0; +} + +static void tpm_emulator_class_init(ObjectClass *klass, void *data) +{ + TPMBackendClass *tbc =3D TPM_BACKEND_CLASS(klass); + tbc->ops =3D &tpm_emulator_driver; + tbc->handle_request =3D tpm_emulator_handle_request; +} + +static const TypeInfo tpm_emulator_info =3D { + .name =3D TYPE_TPM_EMULATOR, + .parent =3D TYPE_TPM_BACKEND, + .instance_size =3D sizeof(TPMEmulator), + .class_init =3D tpm_emulator_class_init, + .instance_init =3D tpm_emulator_inst_init, +}; + +static void tpm_emulator_register(void) +{ + type_register_static(&tpm_emulator_info); + tpm_register_driver(&tpm_emulator_driver); +} + +type_init(tpm_emulator_register) diff --git a/hw/tpm/tpm_ioctl.h b/hw/tpm/tpm_ioctl.h new file mode 100644 index 0000000..af49708 --- /dev/null +++ b/hw/tpm/tpm_ioctl.h @@ -0,0 +1,243 @@ +/* + * tpm_ioctl.h + * + * (c) Copyright IBM Corporation 2014, 2015. + * + * This file is licensed under the terms of the 3-clause BSD license + */ +#ifndef _TPM_IOCTL_H_ +#define _TPM_IOCTL_H_ + +#include +#include +#include +#include + +/* + * Every response from a command involving a TPM command execution must ho= ld + * the ptm_res as the first element. + * ptm_res corresponds to the error code of a command executed by the TPM. + */ + +typedef uint32_t ptm_res; + +/* PTM_GET_TPMESTABLISHED: get the establishment bit */ +struct ptm_est { + union { + struct { + ptm_res tpm_result; + unsigned char bit; /* TPM established bit */ + } resp; /* response */ + } u; +}; + +/* PTM_RESET_TPMESTABLISHED: reset establishment bit */ +struct ptm_reset_est { + union { + struct { + uint8_t loc; /* locality to use */ + } req; /* request */ + struct { + ptm_res tpm_result; + } resp; /* response */ + } u; +}; + +/* PTM_INIT */ +struct ptm_init { + union { + struct { + uint32_t init_flags; /* see definitions below */ + } req; /* request */ + struct { + ptm_res tpm_result; + } resp; /* response */ + } u; +}; + +/* above init_flags */ +#define PTM_INIT_FLAG_DELETE_VOLATILE (1 << 0) + /* delete volatile state file after reading it */ + +/* PTM_SET_LOCALITY */ +struct ptm_loc { + union { + struct { + uint8_t loc; /* locality to set */ + } req; /* request */ + struct { + ptm_res tpm_result; + } resp; /* response */ + } u; +}; + +/* PTM_HASH_DATA: hash given data */ +struct ptm_hdata { + union { + struct { + uint32_t length; + uint8_t data[4096]; + } req; /* request */ + struct { + ptm_res tpm_result; + } resp; /* response */ + } u; +}; + +/* + * size of the TPM state blob to transfer; x86_64 can handle 8k, + * ppc64le only ~7k; keep the response below a 4k page size + */ +#define PTM_STATE_BLOB_SIZE (3 * 1024) + +/* + * The following is the data structure to get state blobs from the TPM. + * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple= reads + * with this ioctl and with adjusted offset are necessary. All bytes + * must be transferred and the transfer is done once the last byte has been + * returned. + * It is possible to use the read() interface for reading the data; howeve= r, the + * first bytes of the state blob will be part of the response to the ioctl= (); a + * subsequent read() is only necessary if the total length (totlength) exc= eeds + * the number of received bytes. seek() is not supported. + */ +struct ptm_getstate { + union { + struct { + uint32_t state_flags; /* may be: PTM_STATE_FLAG_DECRYPTED */ + uint32_t type; /* which blob to pull */ + uint32_t offset; /* offset from where to read */ + } req; /* request */ + struct { + ptm_res tpm_result; + uint32_t state_flags; /* may be: PTM_STATE_FLAG_ENCRYPTED */ + uint32_t totlength; /* total length that will be transferred= */ + uint32_t length; /* number of bytes in following buffer */ + uint8_t data[PTM_STATE_BLOB_SIZE]; + } resp; /* response */ + } u; +}; + +/* TPM state blob types */ +#define PTM_BLOB_TYPE_PERMANENT 1 +#define PTM_BLOB_TYPE_VOLATILE 2 +#define PTM_BLOB_TYPE_SAVESTATE 3 + +/* state_flags above : */ +#define PTM_STATE_FLAG_DECRYPTED 1 /* on input: get decrypted state */ +#define PTM_STATE_FLAG_ENCRYPTED 2 /* on output: state is encrypted */ + +/* + * The following is the data structure to set state blobs in the TPM. + * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple + * 'writes' using this ioctl are necessary. The last packet is indicated + * by the length being smaller than the PTM_STATE_BLOB_SIZE. + * The very first packet may have a length indicator of '0' enabling + * a write() with all the bytes from a buffer. If the write() interface + * is used, a final ioctl with a non-full buffer must be made to indicate + * that all data were transferred (a write with 0 bytes would not work). + */ +struct ptm_setstate { + union { + struct { + uint32_t state_flags; /* may be PTM_STATE_FLAG_ENCRYPTED */ + uint32_t type; /* which blob to set */ + uint32_t length; /* length of the data; + use 0 on the first packet to + transfer using write() */ + uint8_t data[PTM_STATE_BLOB_SIZE]; + } req; /* request */ + struct { + ptm_res tpm_result; + } resp; /* response */ + } u; +}; + +/* + * PTM_GET_CONFIG: Data structure to get runtime configuration information + * such as which keys are applied. + */ +struct ptm_getconfig { + union { + struct { + ptm_res tpm_result; + uint32_t flags; + } resp; /* response */ + } u; +}; + +#define PTM_CONFIG_FLAG_FILE_KEY 0x1 +#define PTM_CONFIG_FLAG_MIGRATION_KEY 0x2 + + +typedef uint64_t ptm_cap; +typedef struct ptm_est ptm_est; +typedef struct ptm_reset_est ptm_reset_est; +typedef struct ptm_loc ptm_loc; +typedef struct ptm_hdata ptm_hdata; +typedef struct ptm_init ptm_init; +typedef struct ptm_getstate ptm_getstate; +typedef struct ptm_setstate ptm_setstate; +typedef struct ptm_getconfig ptm_getconfig; + +/* capability flags returned by PTM_GET_CAPABILITY */ +#define PTM_CAP_INIT (1) +#define PTM_CAP_SHUTDOWN (1 << 1) +#define PTM_CAP_GET_TPMESTABLISHED (1 << 2) +#define PTM_CAP_SET_LOCALITY (1 << 3) +#define PTM_CAP_HASHING (1 << 4) +#define PTM_CAP_CANCEL_TPM_CMD (1 << 5) +#define PTM_CAP_STORE_VOLATILE (1 << 6) +#define PTM_CAP_RESET_TPMESTABLISHED (1 << 7) +#define PTM_CAP_GET_STATEBLOB (1 << 8) +#define PTM_CAP_SET_STATEBLOB (1 << 9) +#define PTM_CAP_STOP (1 << 10) +#define PTM_CAP_GET_CONFIG (1 << 11) + +enum { + PTM_GET_CAPABILITY =3D _IOR('P', 0, ptm_cap), + PTM_INIT =3D _IOWR('P', 1, ptm_init), + PTM_SHUTDOWN =3D _IOR('P', 2, ptm_res), + PTM_GET_TPMESTABLISHED =3D _IOR('P', 3, ptm_est), + PTM_SET_LOCALITY =3D _IOWR('P', 4, ptm_loc), + PTM_HASH_START =3D _IOR('P', 5, ptm_res), + PTM_HASH_DATA =3D _IOWR('P', 6, ptm_hdata), + PTM_HASH_END =3D _IOR('P', 7, ptm_res), + PTM_CANCEL_TPM_CMD =3D _IOR('P', 8, ptm_res), + PTM_STORE_VOLATILE =3D _IOR('P', 9, ptm_res), + PTM_RESET_TPMESTABLISHED =3D _IOWR('P', 10, ptm_reset_est), + PTM_GET_STATEBLOB =3D _IOWR('P', 11, ptm_getstate), + PTM_SET_STATEBLOB =3D _IOWR('P', 12, ptm_setstate), + PTM_STOP =3D _IOR('P', 13, ptm_res), + PTM_GET_CONFIG =3D _IOR('P', 14, ptm_getconfig), +}; + +/* + * Commands used by the non-CUSE TPMs + * + * All messages container big-endian data. + * + * The return messages only contain the 'resp' part of the unions + * in the data structures above. Besides that the limits in the + * buffers above (ptm_hdata:u.req.data and ptm_get_state:u.resp.data + * and ptm_set_state:u.req.data) are 0xffffffff. + */ +enum { + CMD_GET_CAPABILITY =3D 1, + CMD_INIT, + CMD_SHUTDOWN, + CMD_GET_TPMESTABLISHED, + CMD_SET_LOCALITY, + CMD_HASH_START, + CMD_HASH_DATA, + CMD_HASH_END, + CMD_CANCEL_TPM_CMD, + CMD_STORE_VOLATILE, + CMD_RESET_TPMESTABLISHED, + CMD_GET_STATEBLOB, + CMD_SET_STATEBLOB, + CMD_STOP, + CMD_GET_CONFIG, +}; + +#endif /* _TPM_IOCTL_H */ diff --git a/hw/tpm/tpm_util.c b/hw/tpm/tpm_util.c index 5475acf..34b1d59 100644 --- a/hw/tpm/tpm_util.c +++ b/hw/tpm/tpm_util.c @@ -22,6 +22,7 @@ #include "qemu/osdep.h" #include "tpm_util.h" #include "tpm_int.h" +#include "tpm_ioctl.h" =20 int tpm_util_unix_write(int fd, const uint8_t *buf, uint32_t len) { @@ -185,3 +186,36 @@ int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_v= ersion) =20 return 1; } + +static unsigned long ioctl_to_cmd(unsigned long ioctlnum) +{ + /* the ioctl number contains the command number - 1 */ + return ((ioctlnum >> _IOC_NRSHIFT) & _IOC_NRMASK) + 1; +} + +int tpm_util_ctrlcmd(int fd, unsigned long cmd, void *msg, size_t msg_len_= in, + size_t msg_len_out) +{ + int n; + + uint32_t cmd_no =3D cpu_to_be32(ioctl_to_cmd(cmd)); + struct iovec iov[2] =3D { + { .iov_base =3D &cmd_no, .iov_len =3D sizeof(cmd_no), }, + { .iov_base =3D msg, .iov_len =3D msg_len_in, }, + }; + + n =3D writev(fd, iov, 2); + if (n > 0) { + if (msg_len_out > 0) { + n =3D read(fd, msg, msg_len_out); + /* simulate ioctl return value */ + if (n > 0) { + n =3D 0; + } + } else { + n =3D 0; + } + } + return n; +} + diff --git a/hw/tpm/tpm_util.h b/hw/tpm/tpm_util.h index c2feca7..b93d484 100644 --- a/hw/tpm/tpm_util.h +++ b/hw/tpm/tpm_util.h @@ -34,4 +34,7 @@ bool tpm_util_is_selftest(const uint8_t *in, uint32_t in_= len); =20 int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version); =20 +int tpm_util_ctrlcmd(int fd, unsigned long cmd, void *msg, + size_t msg_len_in, size_t msg_len_out); + #endif /* TPM_TPM_UTIL_H */ diff --git a/qapi-schema.json b/qapi-schema.json index 5faf1ac..e8ddbc6 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -5117,10 +5117,11 @@ # An enumeration of TPM types # # @passthrough: TPM passthrough type +# @emulator: Software Emulator TPM type # # Since: 1.5 ## -{ 'enum': 'TpmType', 'data': [ 'passthrough' ] } +{ 'enum': 'TpmType', 'data': [ 'passthrough', 'emulator' ] } =20 ## # @query-tpm-types: @@ -5163,6 +5164,22 @@ 'data': { '*path' : 'str', '*cancel-path' : 'str'} } =20 ## +# @TPMEmulatorOptions: +# +# Information about the TPM emulator +# +# @tpmstatedir: TPM emilator state dir +# @path: TPM emulator binary path to use +# @logfile: file to use to place TPM emulator logs +# @loglevel: log level number +# +# Since: 2.6 +## +{ 'struct': 'TPMEmulatorOptions', 'base': 'TPMOptions', + 'data': { 'tpmstatedir' : 'str', '*path': 'str', + '*logfile' : 'str', '*loglevel' : 'int' } } + +## # @TpmTypeOptions: # # A union referencing different TPM backend types' configuration options @@ -5172,7 +5189,8 @@ # Since: 1.5 ## { 'union': 'TpmTypeOptions', - 'data': { 'passthrough' : 'TPMPassthroughOptions' } } + 'data': { 'passthrough' : 'TPMPassthroughOptions', + 'emulator' : 'TPMEmulatorOptions' } } =20 ## # @TPMInfo: diff --git a/qemu-options.hx b/qemu-options.hx index 99af8ed..5bbf187 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2846,7 +2846,12 @@ DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \ "-tpmdev passthrough,id=3Did[,path=3Dpath][,cancel-path=3Dpath]\n" " use path to provide path to a character device; defau= lt is /dev/tpm0\n" " use cancel-path to provide path to TPM's cancel sysfs= entry; if\n" - " not provided it will be searched for in /sys/class/mi= sc/tpm?/device\n", + " not provided it will be searched for in /sys/class/mi= sc/tpm?/device\n" + "-tpmdev emulator,id=3Did,tpmstatedir=3Ddir[,path=3Demulator-path,logf= ile=3Dpath,loglevel=3Dlevel]\n" + " use tpmstatedir to provide path to the tpm state dirc= tory\n" + " use path to provide the emulator binary to launch; de= fault is 'swtpm'\n" + " use logfile to provide where to place the swtpm logs\= n" + " use loglevel to controls the swtpm log level\n", QEMU_ARCH_ALL) STEXI =20 @@ -2855,8 +2860,8 @@ The general form of a TPM device option is: =20 @item -tpmdev @var{backend} ,id=3D@var{id} [,@var{options}] @findex -tpmdev -Backend type must be: -@option{passthrough}. +Backend type must be either one of the following: +@option{passthrough}, @option{emulator}. =20 The specific backend type will determine the applicable options. The @code{-tpmdev} option creates the TPM backend and requires a @@ -2906,6 +2911,20 @@ To create a passthrough TPM use the following two op= tions: Note that the @code{-tpmdev} id is @code{tpm0} and is referenced by @code{tpmdev=3Dtpm0} in the device option. =20 +@item -tpmdev emulator, id=3D@var{id}, tpmstatedir=3D@var{path}, path=3D@v= ar{emulator-binary-path}, logfile=3D@var{path}, logevel=3D@var{level} + +(Linux-host only) Enable access to a TPM emulator. + +@option{tpmstatedir} specifies the tpm state directory +@option{path} specifies the emulator binary path to use +@option{logfile} optional log file to use to place log messages +@option{loglevel} specifies the log level to use + +To create a TPM emulator backend device: +@example +-tpmdev emulator,id=3Dtpm0,tpmstatedir=3D/tmp/my-tpm,path=3D/usr/local/bin= /swtpm,logfile=3D/tmp/qemu-tpm.log,logevel=3D5 -device tpm-tis,tpmdev=3Dtpm0 +@end example + @end table =20 ETEXI diff --git a/tpm.c b/tpm.c index c221000..ed110d2 100644 --- a/tpm.c +++ b/tpm.c @@ -25,7 +25,7 @@ static QLIST_HEAD(, TPMBackend) tpm_backends =3D =20 =20 #define TPM_MAX_MODELS 1 -#define TPM_MAX_DRIVERS 1 +#define TPM_MAX_DRIVERS 2 =20 static TPMDriverOps const *be_drivers[TPM_MAX_DRIVERS] =3D { NULL, @@ -263,6 +263,11 @@ static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv) res->options->u.passthrough.data =3D (TPMPassthroughOptions *)tpm_backend_get_tpm_options(drv); break; + case TPM_TYPE_EMULATOR: + res->options->type =3D TPM_TYPE_OPTIONS_KIND_EMULATOR; + res->options->u.emulator.data =3D + (TPMEmulatorOptions *) tpm_backend_get_tpm_options(drv); + break; case TPM_TYPE__MAX: break; } --=20 2.7.4