Message ID | 1361464413-22515-7-git-send-email-stefanb@linux.vnet.ibm.com |
---|---|
State | New |
Headers | show |
On 02/21/2013 11:33 AM, Stefan Berger wrote: > 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/class/misc/tpm0/device/cancel > > This patch propagates the cancellation of a command inside a VM > 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> > Reviewed-by: Corey Bryant<coreyb@linux.vnet.ibm.com> > --- > qemu-options.hx | 13 +++- > tpm/tpm_passthrough.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++---- > vl.c | 5 ++ > 3 files changed, 168 insertions(+), 16 deletions(-) > > diff --git a/qemu-options.hx b/qemu-options.hx > index a0e789e..9c8a56f 100644 > --- a/qemu-options.hx > +++ b/qemu-options.hx > @@ -2218,8 +2218,10 @@ DEFHEADING() > DEFHEADING(TPM device options:) > > DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \ > - "-tpmdev passthrough,id=id[,path=path]\n" > - " use path to provide path to a character device; default is /dev/tpm0\n", > + "-tpmdev passthrough,id=id[,path=path][,cancel-path=path]\n" > + " use path to provide path to a character device; default 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/misc/tpm?/device\n", > QEMU_ARCH_ALL) > STEXI > > @@ -2241,7 +2243,7 @@ Use 'help' to print all available TPM backend types. > qemu -tpmdev help > @end example > > -@item -tpmdev passthrough, id=@var{id}, path=@var{path} > +@item -tpmdev passthrough, id=@var{id}, path=@var{path}, path=@var{cancel-path} The last path= should be cancel-path= > > (Linux-host only) Enable access to the host's TPM using the passthrough > driver. > @@ -2250,6 +2252,11 @@ driver. > a Linux host this would be @code{/dev/tpm0}. > @option{path} is optional and by default @code{/dev/tpm0} is used. > > +@option{cancel-path} specifies the path to the host TPM device's sysfs > +entry allowing for cancellation of an ongoing TPM command. > +@option{cancel-path} is optional and by default QEMU will search for the > +sysfs entry to use. > + > Some notes about using the host's TPM with the passthrough driver: > > The TPM device accessed by the passthrough driver must not be > diff --git a/tpm/tpm_passthrough.c b/tpm/tpm_passthrough.c > index 4c09938..8556d8a 100644 > --- a/tpm/tpm_passthrough.c > +++ b/tpm/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 "qapi/error.h" > #include "qemu/sockets.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,36 @@ 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); > + tpm_pt->tpm_executing = false; > + > + 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 +130,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 +156,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, > @@ -172,6 +194,8 @@ static void tpm_passthrough_reset(TPMBackend *tb) > > DPRINTF("tpm_passthrough: CALL TO TPM_RESET!\n"); > > + tpm_passthrough_cancel_cmd(tb); > + > tpm_backend_thread_end(&tpm_pt->tbt); > > tpm_pt->had_startup_error = false; > @@ -221,7 +245,29 @@ 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 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->tpm_executing) { > + if (tpm_pt->cancel_fd >= 0) { > + 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; > + } > + } else { > + error_report("Cannot cancel TPM command due to missing " > + "TPM sysfs cancel entry"); > + } > + } > } > > static const char *tpm_passthrough_create_desc(void) > @@ -281,10 +327,98 @@ static int tpm_passthrough_test_tpmdev(int fd) > return 0; > } > > +/* > + * Check whether the given base path, e.g., /sys/class/misc/tpm0/device, > + * is the sysfs directory of a TPM. A TPM sysfs directory 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; > +} > + > +/* > + * Unless path or file descriptor set has been provided by user, > + * determine the sysfs cancel file following kernel documentation > + * in Documentation/ABI/stable/sysfs-class-tpm. > + */ > +static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb) > +{ > + int fd = -1; > + unsigned int idx; > + DIR *pnp_dir; > + char path[PATH_MAX]; > + struct dirent entry, *result; > + int len; > + > + if (tb->cancel_path) { > + fd = qemu_open(tb->cancel_path, O_WRONLY); > + if (fd < 0) { > + error_report("Could not open TPM cancel path : %s", > + strerror(errno)); > + } > + return fd; > + } > + > + snprintf(path, sizeof(path), "/sys/class/misc"); > + pnp_dir = opendir(path); > + if (pnp_dir != NULL) { > + while (readdir_r(pnp_dir, &entry, &result) == 0 && > + result != NULL) { > + /* > + * only allow /sys/class/misc/tpm%u type of paths > + */ > + if (sscanf(entry.d_name, "tpm%u%n", &idx, &len) < 1 || > + len <= strlen("tpm") || > + len != strlen(entry.d_name)) { > + continue; > + } > + > + snprintf(path, sizeof(path), "/sys/class/misc/%s/device", > + entry.d_name); > + if (!tpm_passthrough_check_sysfs_cancel(path, sizeof(path))) { > + continue; > + } > + > + fd = qemu_open(path, O_WRONLY); > + break; > + } > + closedir(pnp_dir); > + } > + > + 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; > > + value = qemu_opt_get(opts, "cancel-path"); > + if (value) { > + tb->cancel_path = g_strdup(value); > + } > + > value = qemu_opt_get(opts, "path"); > if (!value) { > value = TPM_PASSTHROUGH_DEFAULT_DEVICE; > @@ -337,6 +471,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); > + I think this needs a failure path if cancel_fd == -1. I'm able to start a guest with: -tpmdev passthrough,id=tpm0,path=/dev/tpm0,cancel-path=/dev/junk > return tb; > > err_exit: > @@ -351,12 +487,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); > > qemu_close(tpm_pt->tpm_fd); > + qemu_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); > diff --git a/vl.c b/vl.c > index 19065d0..5e1032f 100644 > --- a/vl.c > +++ b/vl.c > @@ -503,6 +503,11 @@ static QemuOptsList qemu_tpmdev_opts = { > .help = "Type of TPM backend", > }, > { > + .name = "cancel-path", > + .type = QEMU_OPT_STRING, > + .help = "Sysfs file entry for canceling TPM commands", > + }, > + { > .name = "path", > .type = QEMU_OPT_STRING, > .help = "Path to TPM device on the host", > -- 1.7.11.7 >
On 02/26/2013 05:04 PM, Corey Bryant wrote: > > > On 02/21/2013 11:33 AM, Stefan Berger wrote: >> >> >> -@item -tpmdev passthrough, id=@var{id}, path=@var{path} >> +@item -tpmdev passthrough, id=@var{id}, path=@var{path}, >> path=@var{cancel-path} > > The last path= should be cancel-path= Fixed >> goto err_exit; >> } >> >> + tb->s.tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tb); >> + > @@ -337,6 +471,8 @@ static TPMBackend *tpm_passthrough_create(QemuOpts > *opts, const char *id) > > I think this needs a failure path if cancel_fd == -1. I'm able to > start a guest with: > > -tpmdev passthrough,id=tpm0,path=/dev/tpm0,cancel-path=/dev/junk Fixed.
diff --git a/qemu-options.hx b/qemu-options.hx index a0e789e..9c8a56f 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2218,8 +2218,10 @@ DEFHEADING() DEFHEADING(TPM device options:) DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \ - "-tpmdev passthrough,id=id[,path=path]\n" - " use path to provide path to a character device; default is /dev/tpm0\n", + "-tpmdev passthrough,id=id[,path=path][,cancel-path=path]\n" + " use path to provide path to a character device; default 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/misc/tpm?/device\n", QEMU_ARCH_ALL) STEXI @@ -2241,7 +2243,7 @@ Use 'help' to print all available TPM backend types. qemu -tpmdev help @end example -@item -tpmdev passthrough, id=@var{id}, path=@var{path} +@item -tpmdev passthrough, id=@var{id}, path=@var{path}, path=@var{cancel-path} (Linux-host only) Enable access to the host's TPM using the passthrough driver. @@ -2250,6 +2252,11 @@ driver. a Linux host this would be @code{/dev/tpm0}. @option{path} is optional and by default @code{/dev/tpm0} is used. +@option{cancel-path} specifies the path to the host TPM device's sysfs +entry allowing for cancellation of an ongoing TPM command. +@option{cancel-path} is optional and by default QEMU will search for the +sysfs entry to use. + Some notes about using the host's TPM with the passthrough driver: The TPM device accessed by the passthrough driver must not be diff --git a/tpm/tpm_passthrough.c b/tpm/tpm_passthrough.c index 4c09938..8556d8a 100644 --- a/tpm/tpm_passthrough.c +++ b/tpm/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 "qapi/error.h" #include "qemu/sockets.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,36 @@ 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); + tpm_pt->tpm_executing = false; + + 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 +130,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 +156,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, @@ -172,6 +194,8 @@ static void tpm_passthrough_reset(TPMBackend *tb) DPRINTF("tpm_passthrough: CALL TO TPM_RESET!\n"); + tpm_passthrough_cancel_cmd(tb); + tpm_backend_thread_end(&tpm_pt->tbt); tpm_pt->had_startup_error = false; @@ -221,7 +245,29 @@ 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 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->tpm_executing) { + if (tpm_pt->cancel_fd >= 0) { + 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; + } + } else { + error_report("Cannot cancel TPM command due to missing " + "TPM sysfs cancel entry"); + } + } } static const char *tpm_passthrough_create_desc(void) @@ -281,10 +327,98 @@ static int tpm_passthrough_test_tpmdev(int fd) return 0; } +/* + * Check whether the given base path, e.g., /sys/class/misc/tpm0/device, + * is the sysfs directory of a TPM. A TPM sysfs directory 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; +} + +/* + * Unless path or file descriptor set has been provided by user, + * determine the sysfs cancel file following kernel documentation + * in Documentation/ABI/stable/sysfs-class-tpm. + */ +static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb) +{ + int fd = -1; + unsigned int idx; + DIR *pnp_dir; + char path[PATH_MAX]; + struct dirent entry, *result; + int len; + + if (tb->cancel_path) { + fd = qemu_open(tb->cancel_path, O_WRONLY); + if (fd < 0) { + error_report("Could not open TPM cancel path : %s", + strerror(errno)); + } + return fd; + } + + snprintf(path, sizeof(path), "/sys/class/misc"); + pnp_dir = opendir(path); + if (pnp_dir != NULL) { + while (readdir_r(pnp_dir, &entry, &result) == 0 && + result != NULL) { + /* + * only allow /sys/class/misc/tpm%u type of paths + */ + if (sscanf(entry.d_name, "tpm%u%n", &idx, &len) < 1 || + len <= strlen("tpm") || + len != strlen(entry.d_name)) { + continue; + } + + snprintf(path, sizeof(path), "/sys/class/misc/%s/device", + entry.d_name); + if (!tpm_passthrough_check_sysfs_cancel(path, sizeof(path))) { + continue; + } + + fd = qemu_open(path, O_WRONLY); + break; + } + closedir(pnp_dir); + } + + 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; + value = qemu_opt_get(opts, "cancel-path"); + if (value) { + tb->cancel_path = g_strdup(value); + } + value = qemu_opt_get(opts, "path"); if (!value) { value = TPM_PASSTHROUGH_DEFAULT_DEVICE; @@ -337,6 +471,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 +487,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); qemu_close(tpm_pt->tpm_fd); + qemu_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); diff --git a/vl.c b/vl.c index 19065d0..5e1032f 100644 --- a/vl.c +++ b/vl.c @@ -503,6 +503,11 @@ static QemuOptsList qemu_tpmdev_opts = { .help = "Type of TPM backend", }, { + .name = "cancel-path", + .type = QEMU_OPT_STRING, + .help = "Sysfs file entry for canceling TPM commands", + }, + { .name = "path", .type = QEMU_OPT_STRING, .help = "Path to TPM device on the host",