qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Stefan Berger <stefanb@linux.vnet.ibm.com>
To: stefanb@linux.vnet.ibm.com, qemu-devel@nongnu.org, anthony@codemonkey.ws
Cc: andreas.niederl@iaik.tugraz.at, mst@redhat.com
Subject: [Qemu-devel] [PATCH V20 6/8] Add support for cancelling of a TPM command
Date: Fri, 18 Jan 2013 11:02:46 -0500	[thread overview]
Message-ID: <1358524968-22297-7-git-send-email-stefanb@linux.vnet.ibm.com> (raw)
In-Reply-To: <1358524968-22297-1-git-send-email-stefanb@linux.vnet.ibm.com>

This patch adds support for cancelling an executing TPM command.
In Linux for example a user can cancel a command through the TPM's
sysfs 'cancel' entry using

echo "1" > /sysfs/.../cancel

This patch propagates the cancellation to the host TPM's sysfs entry.
It also uses the possibility to cancel the command before QEMU VM
shutdown or reboot, which helps in preventing QEMU from hanging while
waiting for the completion of the command.
To relieve higher layers or users from having to determine the TPM's
cancel sysfs entry, the driver searches for the entry in well known
locations.

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
---
 hw/tpm_passthrough.c | 168 +++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 155 insertions(+), 13 deletions(-)

diff --git a/hw/tpm_passthrough.c b/hw/tpm_passthrough.c
index f9e6923..1b17c30 100644
--- a/hw/tpm_passthrough.c
+++ b/hw/tpm_passthrough.c
@@ -22,6 +22,8 @@
  * License along with this library; if not, see <http://www.gnu.org/licenses/>
  */
 
+#include <dirent.h>
+
 #include "qemu-common.h"
 #include "qemu-error.h"
 #include "qemu_socket.h"
@@ -57,11 +59,18 @@ struct TPMPassthruState {
 
     char *tpm_dev;
     int tpm_fd;
+    bool tpm_executing;
+    bool tpm_op_canceled;
+    int cancel_fd;
     bool had_startup_error;
 };
 
 #define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0"
 
+/* functions */
+
+static void tpm_passthrough_cancel_cmd(TPMBackend *tb);
+
 static int tpm_passthrough_unix_write(int fd, const uint8_t *buf, uint32_t len)
 {
     return send_all(fd, buf, len);
@@ -79,25 +88,34 @@ static uint32_t tpm_passthrough_get_size_from_buffer(const uint8_t *buf)
     return be32_to_cpu(resp->len);
 }
 
-static int tpm_passthrough_unix_tx_bufs(int tpm_fd,
+static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
                                         const uint8_t *in, uint32_t in_len,
                                         uint8_t *out, uint32_t out_len)
 {
     int ret;
 
-    ret = tpm_passthrough_unix_write(tpm_fd, in, in_len);
+    tpm_pt->tpm_op_canceled = false;
+    tpm_pt->tpm_executing = true;
+
+    ret = tpm_passthrough_unix_write(tpm_pt->tpm_fd, in, in_len);
     if (ret != in_len) {
-        error_report("tpm_passthrough: error while transmitting data "
-                     "to TPM: %s (%i)\n",
-                     strerror(errno), errno);
+        if (!tpm_pt->tpm_op_canceled ||
+            (tpm_pt->tpm_op_canceled && errno != ECANCELED)) {
+            error_report("tpm_passthrough: error while transmitting data "
+                         "to TPM: %s (%i)\n",
+                         strerror(errno), errno);
+        }
         goto err_exit;
     }
 
-    ret = tpm_passthrough_unix_read(tpm_fd, out, out_len);
+    ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len);
     if (ret < 0) {
-        error_report("tpm_passthrough: error while reading data from "
-                     "TPM: %s (%i)\n",
-                     strerror(errno), errno);
+        if (!tpm_pt->tpm_op_canceled ||
+            (tpm_pt->tpm_op_canceled && errno != ECANCELED)) {
+            error_report("tpm_passthrough: error while reading data from "
+                         "TPM: %s (%i)\n",
+                         strerror(errno), errno);
+        }
     } else if (ret < sizeof(struct tpm_resp_hdr) ||
                tpm_passthrough_get_size_from_buffer(out) != ret) {
         ret = -1;
@@ -110,13 +128,15 @@ err_exit:
         tpm_write_fatal_error_response(out, out_len);
     }
 
+    tpm_pt->tpm_executing = false;
+
     return ret;
 }
 
-static int tpm_passthrough_unix_transfer(int tpm_fd,
+static int tpm_passthrough_unix_transfer(TPMPassthruState *tpm_pt,
                                          const TPMLocality *locty_data)
 {
-    return tpm_passthrough_unix_tx_bufs(tpm_fd,
+    return tpm_passthrough_unix_tx_bufs(tpm_pt,
                                         locty_data->w_buffer.buffer,
                                         locty_data->w_offset,
                                         locty_data->r_buffer.buffer,
@@ -134,7 +154,7 @@ static void tpm_passthrough_worker_thread(gpointer data,
 
     switch (cmd) {
     case TPM_BACKEND_CMD_PROCESS_CMD:
-        tpm_passthrough_unix_transfer(tpm_pt->tpm_fd,
+        tpm_passthrough_unix_transfer(tpm_pt,
                                       thr_parms->tpm_state->locty_data);
 
         thr_parms->recv_data_callback(thr_parms->tpm_state,
@@ -174,6 +194,8 @@ static void tpm_passthrough_reset(TPMBackend *tb)
 
     tpm_backend_thread_end(&tpm_pt->tbt);
 
+    tpm_passthrough_cancel_cmd(tb);
+
     tpm_pt->had_startup_error = false;
 }
 
@@ -221,7 +243,24 @@ static void tpm_passthrough_deliver_request(TPMBackend *tb)
 
 static void tpm_passthrough_cancel_cmd(TPMBackend *tb)
 {
-    /* cancelling an ongoing command is known not to work with some TPMs */
+    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
+    int n;
+
+    /*
+     * As of Linux 3.7 the tpm_tis driver does not properly cancel
+     * commands for all 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->cancel_fd >= 0 && tpm_pt->tpm_executing) {
+        n = write(tpm_pt->cancel_fd, "-", 1);
+        if (n != 1) {
+            error_report("Canceling TPM command failed: %s\n",
+                         strerror(errno));
+        } else {
+            tpm_pt->tpm_op_canceled = true;
+        }
+    }
 }
 
 static const char *tpm_passthrough_create_desc(void)
@@ -281,6 +320,103 @@ static int tpm_passthrough_test_tpmdev(int fd)
     return 0;
 }
 
+/*
+ * Check whether the given base path, e.g.,  /sys/devices/platfrom/tpm_tis,
+ * is the sysfs entry of a TPM. A TPM sysfs entry should be uniquely
+ * recognizable by the file entries 'pcrs' and 'cancel'.
+ * Upon success 'true' is returned and the basebath buffer has '/cancel'
+ * appended.
+ */
+static bool tpm_passthrough_check_sysfs_cancel(char *basepath, size_t bufsz)
+{
+    char path[PATH_MAX];
+    struct stat statbuf;
+
+    snprintf(path, sizeof(path), "%s/pcrs", basepath);
+    if (stat(path, &statbuf) == -1 || !S_ISREG(statbuf.st_mode)) {
+        return false;
+    }
+
+    snprintf(path, sizeof(path), "%s/cancel", basepath);
+    if (stat(path, &statbuf) == -1 || !S_ISREG(statbuf.st_mode)) {
+        return false;
+    }
+
+    strncpy(basepath, path, bufsz);
+
+    return true;
+}
+
+static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb)
+{
+    int fd = -1;
+    unsigned int i = 0;
+    DIR *pnp_dir;
+    char path[PATH_MAX];
+    struct dirent entry, *result;
+    bool found = false;
+
+    while (!found) {
+        snprintf(path, sizeof(path), "/sys/devices/pnp%u", i);
+        pnp_dir = opendir(path);
+        if (pnp_dir != NULL) {
+            while (readdir_r(pnp_dir, &entry, &result) == 0 &&
+                   result != NULL) {
+                if (!strcmp(entry.d_name, ".") ||
+                    !strcmp(entry.d_name, "..")) {
+                    continue;
+                }
+                snprintf(path, sizeof(path), "/sys/devices/pnp%u/%s",
+                         i, entry.d_name);
+                if (!tpm_passthrough_check_sysfs_cancel(path, sizeof(path))) {
+                    continue;
+                }
+
+                fd = open(path, O_WRONLY);
+                found = true;
+                break;
+            }
+            closedir(pnp_dir);
+        } else {
+            break;
+        }
+        i++;
+    }
+
+    if (fd >= 0) {
+        goto exit;
+    }
+
+    /*
+     * alternative path if ACPI for TPM was not available:
+     *   modprobe tpm_tis force=1
+     */
+    snprintf(path, sizeof(path), "/sys/devices/platform/tpm_tis");
+    if (tpm_passthrough_check_sysfs_cancel(path, sizeof(path))) {
+        fd = open(path, O_WRONLY);
+        goto exit;
+    }
+
+    /*
+     * one more thing to try: /sys/class/misc/tpm%u/device/cancel
+     */
+    for (i = 0; i < 9; i++) {
+        snprintf(path, sizeof(path), "/sys/class/misc/tpm%u/device", i);
+        if (!tpm_passthrough_check_sysfs_cancel(path, sizeof(path))) {
+            continue;
+        }
+        fd = open(path, O_WRONLY);
+        break;
+    }
+
+exit:
+    if (fd >= 0) {
+        tb->cancel_path = g_strdup(path);
+    }
+
+    return fd;
+}
+
 static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
 {
     const char *value;
@@ -337,6 +473,8 @@ static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
         goto err_exit;
     }
 
+    tb->s.tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tb);
+
     return tb;
 
 err_exit:
@@ -351,12 +489,16 @@ static void tpm_passthrough_destroy(TPMBackend *tb)
 {
     TPMPassthruState *tpm_pt = tb->s.tpm_pt;
 
+    tpm_passthrough_cancel_cmd(tb);
+
     tpm_backend_thread_end(&tpm_pt->tbt);
 
     close(tpm_pt->tpm_fd);
+    close(tb->s.tpm_pt->cancel_fd);
 
     g_free(tb->id);
     g_free(tb->path);
+    g_free(tb->cancel_path);
     g_free(tb->s.tpm_pt->tpm_dev);
     g_free(tb->s.tpm_pt);
     g_free(tb);
-- 
1.7.11.7

  parent reply	other threads:[~2013-01-18 16:09 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-01-18 16:02 [Qemu-devel] [PATCH V20 0/8] Qemu Trusted Platform Module (TPM) integration Stefan Berger
2013-01-18 16:02 ` [Qemu-devel] [PATCH V20 1/8] Support for TPM command line options Stefan Berger
2013-02-01 15:33   ` Corey Bryant
2013-01-18 16:02 ` [Qemu-devel] [PATCH V20 2/8] Add TPM (frontend) hardware interface (TPM TIS) to QEMU Stefan Berger
2013-02-01 17:02   ` Corey Bryant
2013-01-18 16:02 ` [Qemu-devel] [PATCH V20 3/8] Add a debug register Stefan Berger
2013-02-01 17:07   ` Corey Bryant
2013-01-18 16:02 ` [Qemu-devel] [PATCH V20 4/8] Build the TPM frontend code Stefan Berger
2013-02-01 17:08   ` Corey Bryant
2013-01-18 16:02 ` [Qemu-devel] [PATCH V20 5/8] Add a TPM Passthrough backend driver implementation Stefan Berger
2013-01-19  9:18   ` Blue Swirl
2013-01-19 14:29     ` Stefan Berger
2013-02-01 19:03   ` Corey Bryant
2013-01-18 16:02 ` Stefan Berger [this message]
2013-01-18 16:02 ` [Qemu-devel] [PATCH V20 7/8] Introduce --enable-tpm-passthrough configure option Stefan Berger
2013-02-01 19:21   ` Corey Bryant
2013-01-18 16:02 ` [Qemu-devel] [PATCH V20 8/8] Add fd parameter for TPM passthrough driver Stefan Berger
     [not found]   ` <50F991FE.3000901@redhat.com>
2013-01-19  0:14     ` Stefan Berger
2013-01-19  0:55       ` Stefan Berger
2013-01-19 15:31         ` Eric Blake
2013-01-19 18:37           ` Stefan Berger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1358524968-22297-7-git-send-email-stefanb@linux.vnet.ibm.com \
    --to=stefanb@linux.vnet.ibm.com \
    --cc=andreas.niederl@iaik.tugraz.at \
    --cc=anthony@codemonkey.ws \
    --cc=mst@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).