]> git.proxmox.com Git - qemu.git/commitdiff
Merge remote-tracking branch 'afaerber/qom-cpu' into staging
authorAnthony Liguori <aliguori@us.ibm.com>
Thu, 14 Mar 2013 19:50:58 +0000 (14:50 -0500)
committerAnthony Liguori <aliguori@us.ibm.com>
Thu, 14 Mar 2013 19:50:58 +0000 (14:50 -0500)
# By Andreas Färber (16) and Igor Mammedov (1)
# Via Andreas Färber
* afaerber/qom-cpu:
  target-lm32: Update VMStateDescription to LM32CPU
  target-arm: Override do_interrupt for ARMv7-M profile
  cpu: Replace do_interrupt() by CPUClass::do_interrupt method
  cpu: Pass CPUState to cpu_interrupt()
  exec: Pass CPUState to cpu_reset_interrupt()
  cpu: Move halted and interrupt_request fields to CPUState
  target-cris/helper.c: Update Coding Style
  target-i386: Update VMStateDescription to X86CPU
  cpu: Introduce cpu_class_set_vmsd()
  cpu: Register VMStateDescription through CPUState
  stubs: Add a vmstate_dummy struct for CONFIG_USER_ONLY
  vmstate: Make vmstate_register() static inline
  target-sh4: Move PVR/PRR/CVR into SuperHCPUClass
  target-sh4: Introduce SuperHCPU subclasses
  cpus: Replace open-coded CPU loop in qmp_memsave() with qemu_get_cpu()
  monitor: Use qemu_get_cpu() in monitor_set_cpu()
  cpu: Fix qemu_get_cpu() to return NULL if CPU not found

38 files changed:
Makefile.objs
configure
default-configs/i386-softmmu.mak
default-configs/x86_64-softmmu.mak
exec.c
hmp-commands.hx
hmp.c
hmp.h
include/qemu/sockets.h
include/tpm/tpm.h [new file with mode: 0644]
linux-user/main.c
linux-user/socket.h
linux-user/sparc/syscall_nr.h
linux-user/strace.c
linux-user/syscall.c
monitor.c
qapi-schema.json
qemu-char.c
qemu-options.hx
qga/channel-win32.c
qga/commands-posix.c
qga/commands-win32.c
qga/guest-agent-core.h
qga/main.c
qga/qapi-schema.json
qga/service-win32.c
qmp-commands.hx
tpm/Makefile.objs [new file with mode: 0644]
tpm/tpm.c [new file with mode: 0644]
tpm/tpm_backend.c [new file with mode: 0644]
tpm/tpm_backend.h [new file with mode: 0644]
tpm/tpm_int.h [new file with mode: 0644]
tpm/tpm_passthrough.c [new file with mode: 0644]
tpm/tpm_tis.c [new file with mode: 0644]
tpm/tpm_tis.h [new file with mode: 0644]
trace-events
ui/console.c
vl.c

index 8c90b92d01a363f54c20e4972528e7ede52cfc34..f99841ce5495e0b8360f73d17a2d502adeb54b04 100644 (file)
@@ -74,6 +74,7 @@ common-obj-y += bt-host.o bt-vhci.o
 
 common-obj-y += dma-helpers.o
 common-obj-y += vl.o
+common-obj-y += tpm/
 
 common-obj-$(CONFIG_SLIRP) += slirp/
 
index 84317c6826f72ae9758774d30ce8857ccbb771aa..46a75947cd9a5a87ecec73aabe4cf4aa85f16150 100755 (executable)
--- a/configure
+++ b/configure
@@ -228,6 +228,7 @@ glusterfs=""
 virtio_blk_data_plane=""
 gtk=""
 gtkabi="2.0"
+tpm="no"
 
 # parse CC options first
 for opt do
@@ -905,6 +906,8 @@ for opt do
   ;;
   --with-gtkabi=*) gtkabi="$optarg"
   ;;
+  --enable-tpm) tpm="yes"
+  ;;
   *) echo "ERROR: unknown option $opt"; show_help="yes"
   ;;
   esac
@@ -1160,6 +1163,7 @@ echo "  --enable-glusterfs       enable GlusterFS backend"
 echo "  --disable-glusterfs      disable GlusterFS backend"
 echo "  --enable-gcov            enable test coverage analysis with gcov"
 echo "  --gcov=GCOV              use specified gcov [$gcov_tool]"
+echo "  --enable-tpm             enable TPM support"
 echo ""
 echo "NOTE: The object files are built at the place where configure is launched"
 exit 1
@@ -2760,6 +2764,20 @@ if compile_prog "" "" ; then
   epoll_pwait=yes
 fi
 
+# check for sendfile support
+sendfile=no
+cat > $TMPC << EOF
+#include <sys/sendfile.h>
+
+int main(void)
+{
+    return sendfile(0, 0, 0, 0);
+}
+EOF
+if compile_prog "" "" ; then
+  sendfile=yes
+fi
+
 # Check if tools are available to build documentation.
 if test "$docs" != "no" ; then
   if has makeinfo && has pod2man; then
@@ -3420,6 +3438,7 @@ echo "GlusterFS support $glusterfs"
 echo "virtio-blk-data-plane $virtio_blk_data_plane"
 echo "gcov              $gcov_tool"
 echo "gcov enabled      $gcov"
+echo "TPM support       $tpm"
 
 if test "$sdl_too_old" = "yes"; then
 echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -3628,6 +3647,9 @@ fi
 if test "$epoll_pwait" = "yes" ; then
   echo "CONFIG_EPOLL_PWAIT=y" >> $config_host_mak
 fi
+if test "$sendfile" = "yes" ; then
+  echo "CONFIG_SENDFILE=y" >> $config_host_mak
+fi
 if test "$inotify" = "yes" ; then
   echo "CONFIG_INOTIFY=y" >> $config_host_mak
 fi
@@ -4329,6 +4351,12 @@ if test "$gprof" = "yes" ; then
   fi
 fi
 
+if test "$tpm" = "yes"; then
+  if test "$target_softmmu" = "yes" ; then
+    echo "CONFIG_TPM=y" >> $config_host_mak
+  fi
+fi
+
 if test "$ARCH" = "tci"; then
   linker_script=""
 else
index 1b23025a98598e2683ad5fc281b8099b5d56de55..df9e126c1f240093d1a3ac1d5d05ca7828423986 100644 (file)
@@ -26,3 +26,5 @@ CONFIG_HPET=y
 CONFIG_APPLESMC=y
 CONFIG_I8259=y
 CONFIG_PFLASH_CFI01=y
+CONFIG_TPM_TIS=y
+CONFIG_TPM_PASSTHROUGH=y
index 3392f5abd6531a49009a3897ae77280de600e446..ab3cd5fc350be9267d05be9bf6d671d866255459 100644 (file)
@@ -26,3 +26,5 @@ CONFIG_HPET=y
 CONFIG_APPLESMC=y
 CONFIG_I8259=y
 CONFIG_PFLASH_CFI01=y
+CONFIG_TPM_TIS=y
+CONFIG_TPM_PASSTHROUGH=y
diff --git a/exec.c b/exec.c
index c5e65a9380a554faf98c4538faf2c420798d7224..8a6aac36e347399ffa448fac4cab1fae2c135110 100644 (file)
--- a/exec.c
+++ b/exec.c
@@ -846,6 +846,8 @@ static void *file_ram_alloc(RAMBlock *block,
                             const char *path)
 {
     char *filename;
+    char *sanitized_name;
+    char *c;
     void *area;
     int fd;
 #ifdef MAP_POPULATE
@@ -867,7 +869,16 @@ static void *file_ram_alloc(RAMBlock *block,
         return NULL;
     }
 
-    filename = g_strdup_printf("%s/qemu_back_mem.XXXXXX", path);
+    /* Make name safe to use with mkstemp by replacing '/' with '_'. */
+    sanitized_name = g_strdup(block->mr->name);
+    for (c = sanitized_name; *c != '\0'; c++) {
+        if (*c == '/')
+            *c = '_';
+    }
+
+    filename = g_strdup_printf("%s/qemu_back_mem.%s.XXXXXX", path,
+                               sanitized_name);
+    g_free(sanitized_name);
 
     fd = mkstemp(filename);
     if (fd < 0) {
index 69c707d332a19586a92fcb5d1025b9f321ab8a53..4bda3fea0e3b585a96cc02bd748152137b78824c 100644 (file)
@@ -1642,6 +1642,8 @@ show device tree
 show qdev device model list
 @item info roms
 show roms
+@item info tpm
+show the TPM device
 @end table
 ETEXI
 
diff --git a/hmp.c b/hmp.c
index 2f47a8a9dddefcf820aa8795785b971ea74f1f03..b0a861cfbb0eba7c93b8698a1a53da30cd73b9ff 100644 (file)
--- a/hmp.c
+++ b/hmp.c
@@ -607,6 +607,50 @@ void hmp_info_block_jobs(Monitor *mon, const QDict *qdict)
     }
 }
 
+void hmp_info_tpm(Monitor *mon, const QDict *qdict)
+{
+    TPMInfoList *info_list, *info;
+    Error *err = NULL;
+    unsigned int c = 0;
+    TPMPassthroughOptions *tpo;
+
+    info_list = qmp_query_tpm(&err);
+    if (err) {
+        monitor_printf(mon, "TPM device not supported\n");
+        error_free(err);
+        return;
+    }
+
+    if (info_list) {
+        monitor_printf(mon, "TPM device:\n");
+    }
+
+    for (info = info_list; info; info = info->next) {
+        TPMInfo *ti = info->value;
+        monitor_printf(mon, " tpm%d: model=%s\n",
+                       c, TpmModel_lookup[ti->model]);
+
+        monitor_printf(mon, "  \\ %s: type=%s",
+                       ti->id, TpmType_lookup[ti->type]);
+
+        switch (ti->tpm_options->kind) {
+        case TPM_TYPE_OPTIONS_KIND_TPM_PASSTHROUGH_OPTIONS:
+            tpo = ti->tpm_options->tpm_passthrough_options;
+            monitor_printf(mon, "%s%s%s%s",
+                           tpo->has_path ? ",path=" : "",
+                           tpo->has_path ? tpo->path : "",
+                           tpo->has_cancel_path ? ",cancel-path=" : "",
+                           tpo->has_cancel_path ? tpo->cancel_path : "");
+            break;
+        case TPM_TYPE_OPTIONS_KIND_MAX:
+            break;
+        }
+        monitor_printf(mon, "\n");
+        c++;
+    }
+    qapi_free_TPMInfoList(info_list);
+}
+
 void hmp_quit(Monitor *mon, const QDict *qdict)
 {
     monitor_suspend(mon);
diff --git a/hmp.h b/hmp.h
index 30b3c20ca4d847fd6c2ceb6ffaaba8a7f06bc6c2..95fe76e218f48ee6fb9ccb37768a85350deaa1e9 100644 (file)
--- a/hmp.h
+++ b/hmp.h
@@ -36,6 +36,7 @@ void hmp_info_spice(Monitor *mon, const QDict *qdict);
 void hmp_info_balloon(Monitor *mon, const QDict *qdict);
 void hmp_info_pci(Monitor *mon, const QDict *qdict);
 void hmp_info_block_jobs(Monitor *mon, const QDict *qdict);
+void hmp_info_tpm(Monitor *mon, const QDict *qdict);
 void hmp_quit(Monitor *mon, const QDict *qdict);
 void hmp_stop(Monitor *mon, const QDict *qdict);
 void hmp_system_reset(Monitor *mon, const QDict *qdict);
index 6125bf7bdfbd43851be181e28c7b4eb5db39bc89..c5cee4bf2fee06f043a2683daed978fa63b8d6ca 100644 (file)
@@ -38,6 +38,7 @@ int socket_set_nodelay(int fd);
 void socket_set_block(int fd);
 void socket_set_nonblock(int fd);
 int send_all(int fd, const void *buf, int len1);
+int recv_all(int fd, void *buf, int len1, bool single_read);
 
 /* callback function for nonblocking connect
  * valid fd on success, negative error code on failure
diff --git a/include/tpm/tpm.h b/include/tpm/tpm.h
new file mode 100644 (file)
index 0000000..cc8f20e
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Public TPM functions
+ *
+ * Copyright (C) 2011-2013 IBM Corporation
+ *
+ * Authors:
+ *  Stefan Berger    <stefanb@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#ifndef QEMU_TPM_H
+#define QEMU_TPM_H
+
+#include "qemu/option.h"
+
+int tpm_config_parse(QemuOptsList *opts_list, const char *optarg);
+int tpm_init(void);
+void tpm_cleanup(void);
+
+#endif /* QEMU_TPM_H */
index d8b0cd65fa71a48b55c00bb5cbf59c321f065a85..4e92a0b4c5e4c195269df8976b7e5043121740c5 100644 (file)
@@ -3406,27 +3406,35 @@ static void usage(void)
            "Options and associated environment variables:\n"
            "\n");
 
-    maxarglen = maxenvlen = 0;
+    /* Calculate column widths. We must always have at least enough space
+     * for the column header.
+     */
+    maxarglen = strlen("Argument");
+    maxenvlen = strlen("Env-variable");
 
     for (arginfo = arg_table; arginfo->handle_opt != NULL; arginfo++) {
+        int arglen = strlen(arginfo->argv);
+        if (arginfo->has_arg) {
+            arglen += strlen(arginfo->example) + 1;
+        }
         if (strlen(arginfo->env) > maxenvlen) {
             maxenvlen = strlen(arginfo->env);
         }
-        if (strlen(arginfo->argv) > maxarglen) {
-            maxarglen = strlen(arginfo->argv);
+        if (arglen > maxarglen) {
+            maxarglen = arglen;
         }
     }
 
-    printf("%-*s%-*sDescription\n", maxarglen+3, "Argument",
-            maxenvlen+1, "Env-variable");
+    printf("%-*s %-*s Description\n", maxarglen+1, "Argument",
+            maxenvlen, "Env-variable");
 
     for (arginfo = arg_table; arginfo->handle_opt != NULL; arginfo++) {
         if (arginfo->has_arg) {
             printf("-%s %-*s %-*s %s\n", arginfo->argv,
-                    (int)(maxarglen-strlen(arginfo->argv)), arginfo->example,
-                    maxenvlen, arginfo->env, arginfo->help);
+                   (int)(maxarglen - strlen(arginfo->argv) - 1),
+                   arginfo->example, maxenvlen, arginfo->env, arginfo->help);
         } else {
-            printf("-%-*s %-*s %s\n", maxarglen+1, arginfo->argv,
+            printf("-%-*s %-*s %s\n", maxarglen, arginfo->argv,
                     maxenvlen, arginfo->env,
                     arginfo->help);
         }
index 93d47823d8cc6d80ef4e0e4acaf40c9f73a59d72..339cae5a169d47a297a19f988fdf33a363d645d8 100644 (file)
 
        #define TARGET_SOCK_MAX (SOCK_PACKET + 1)
 
+#elif defined(TARGET_ALPHA)
+
+    /* For setsockopt(2) */
+    #define TARGET_SOL_SOCKET   0xffff
+
+    #define TARGET_SO_DEBUG 0x0001
+    #define TARGET_SO_REUSEADDR 0x0004
+    #define TARGET_SO_KEEPALIVE 0x0008
+    #define TARGET_SO_DONTROUTE 0x0010
+    #define TARGET_SO_BROADCAST 0x0020
+    #define TARGET_SO_LINGER    0x0080
+    #define TARGET_SO_OOBINLINE 0x0100
+    /* To add :#define TARGET_SO_REUSEPORT 0x0200 */
+
+    #define TARGET_SO_TYPE      0x1008
+    #define TARGET_SO_ERROR 0x1007
+    #define TARGET_SO_SNDBUF    0x1001
+    #define TARGET_SO_RCVBUF    0x1002
+    #define TARGET_SO_SNDBUFFORCE   0x100a
+    #define TARGET_SO_RCVBUFFORCE   0x100b
+    #define TARGET_SO_RCVLOWAT  0x1010
+    #define TARGET_SO_SNDLOWAT  0x1011
+    #define TARGET_SO_RCVTIMEO  0x1012
+    #define TARGET_SO_SNDTIMEO  0x1013
+    #define TARGET_SO_ACCEPTCONN    0x1014
+    #define TARGET_SO_PROTOCOL  0x1028
+    #define TARGET_SO_DOMAIN    0x1029
+
+    /* linux-specific, might as well be the same as on i386 */
+    #define TARGET_SO_NO_CHECK  11
+    #define TARGET_SO_PRIORITY  12
+    #define TARGET_SO_BSDCOMPAT 14
+
+    #define TARGET_SO_PASSCRED  17
+    #define TARGET_SO_PEERCRED  18
+    #define TARGET_SO_BINDTODEVICE 25
+
+    /* Socket filtering */
+    #define TARGET_SO_ATTACH_FILTER        26
+    #define TARGET_SO_DETACH_FILTER        27
+
+    #define TARGET_SO_PEERNAME      28
+    #define TARGET_SO_TIMESTAMP     29
+    #define TARGET_SCM_TIMESTAMP        TARGET_SO_TIMESTAMP
+
+    #define TARGET_SO_PEERSEC       30
+    #define TARGET_SO_PASSSEC       34
+    #define TARGET_SO_TIMESTAMPNS       35
+    #define TARGET_SCM_TIMESTAMPNS      TARGET_SO_TIMESTAMPNS
+
+    /* Security levels - as per NRL IPv6 - don't actually do anything */
+    #define TARGET_SO_SECURITY_AUTHENTICATION       19
+    #define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 20
+    #define TARGET_SO_SECURITY_ENCRYPTION_NETWORK       21
+
+    #define TARGET_SO_MARK          36
+
+    #define TARGET_SO_TIMESTAMPING      37
+    #define TARGET_SCM_TIMESTAMPING TARGET_SO_TIMESTAMPING
+
+    #define TARGET_SO_RXQ_OVFL             40
+
+    #define TARGET_SO_WIFI_STATUS       41
+    #define TARGET_SCM_WIFI_STATUS      TARGET_SO_WIFI_STATUS
+    #define TARGET_SO_PEEK_OFF      42
+
+    /* Instruct lower device to use last 4-bytes of skb data as FCS */
+    #define TARGET_SO_NOFCS     43
+
 #else
 
        /* For setsockopt(2) */
index 061711cc033907a1d467e6f7ad31b9b77f7623f4..534e6e9963875062c18edf7c4bae88de92b500e9 100644 (file)
 #define TARGET_NR__newselect         230 /* Linux Specific                              */
 #define TARGET_NR_time               231 /* Linux Specific                              */
 #define TARGET_NR_stime              233 /* Linux Specific                              */
+#define TARGET_NR_statfs64           234 /* Linux Specific                              */
+#define TARGET_NR_fstatfs64          235 /* Linux Specific                              */
 #define TARGET_NR__llseek            236 /* Linux Specific                              */
 #define TARGET_NR_mlock              237
 #define TARGET_NR_munlock            238
index 4e91a6eb9ce7a87ceb6cc4ef68e1555f7f2f86cd..0fbae3c2f630897e606165096942a1eb73081aa7 100644 (file)
@@ -462,18 +462,6 @@ UNUSED static struct flags mmap_flags[] = {
     FLAG_END,
 };
 
-UNUSED static struct flags fcntl_flags[] = {
-    FLAG_TARGET(F_DUPFD),
-    FLAG_TARGET(F_GETFD),
-    FLAG_TARGET(F_SETFD),
-    FLAG_TARGET(F_GETFL),
-    FLAG_TARGET(F_SETFL),
-    FLAG_TARGET(F_GETLK),
-    FLAG_TARGET(F_SETLK),
-    FLAG_TARGET(F_SETLKW),
-    FLAG_END,
-};
-
 UNUSED static struct flags clone_flags[] = {
     FLAG_GENERIC(CLONE_VM),
     FLAG_GENERIC(CLONE_FS),
@@ -867,12 +855,85 @@ print_fcntl(const struct syscallname *name,
 {
     print_syscall_prologue(name);
     print_raw_param("%d", arg0, 0);
-    print_flags(fcntl_flags, arg1, 0);
-    /*
-     * TODO: check flags and print following argument only
-     *       when needed.
-     */
-    print_pointer(arg2, 1);
+    switch(arg1) {
+    case TARGET_F_DUPFD:
+        gemu_log("F_DUPFD,");
+        print_raw_param(TARGET_ABI_FMT_ld, arg2, 1);
+        break;
+    case TARGET_F_GETFD:
+        gemu_log("F_GETFD");
+        break;
+    case TARGET_F_SETFD:
+        gemu_log("F_SETFD,");
+        print_raw_param(TARGET_ABI_FMT_ld, arg2, 1);
+        break;
+    case TARGET_F_GETFL:
+        gemu_log("F_GETFL");
+        break;
+    case TARGET_F_SETFL:
+        gemu_log("F_SETFL,");
+        print_open_flags(arg2, 1);
+        break;
+    case TARGET_F_GETLK:
+        gemu_log("F_GETLK,");
+        print_pointer(arg2, 1);
+        break;
+    case TARGET_F_SETLK:
+        gemu_log("F_SETLK,");
+        print_pointer(arg2, 1);
+        break;
+    case TARGET_F_SETLKW:
+        gemu_log("F_SETLKW,");
+        print_pointer(arg2, 1);
+        break;
+    case TARGET_F_GETOWN:
+        gemu_log("F_GETOWN");
+        break;
+    case TARGET_F_SETOWN:
+        gemu_log("F_SETOWN,");
+        print_raw_param(TARGET_ABI_FMT_ld, arg2, 0);
+        break;
+    case TARGET_F_GETSIG:
+        gemu_log("F_GETSIG");
+        break;
+    case TARGET_F_SETSIG:
+        gemu_log("F_SETSIG,");
+        print_raw_param(TARGET_ABI_FMT_ld, arg2, 0);
+        break;
+#if TARGET_ABI_BITS == 32
+    case TARGET_F_GETLK64:
+        gemu_log("F_GETLK64,");
+        print_pointer(arg2, 1);
+        break;
+    case TARGET_F_SETLK64:
+        gemu_log("F_SETLK64,");
+        print_pointer(arg2, 1);
+        break;
+    case TARGET_F_SETLKW64:
+        gemu_log("F_SETLKW64,");
+        print_pointer(arg2, 1);
+        break;
+#endif
+    case TARGET_F_SETLEASE:
+        gemu_log("F_SETLEASE,");
+        print_raw_param(TARGET_ABI_FMT_ld, arg2, 0);
+        break;
+    case TARGET_F_GETLEASE:
+        gemu_log("F_GETLEASE");
+        break;
+    case TARGET_F_DUPFD_CLOEXEC:
+        gemu_log("F_DUPFD_CLOEXEC,");
+        print_raw_param(TARGET_ABI_FMT_ld, arg2, 1);
+        break;
+    case TARGET_F_NOTIFY:
+        gemu_log("F_NOTIFY,");
+        print_raw_param(TARGET_ABI_FMT_ld, arg2, 0);
+        break;
+    default:
+        print_raw_param(TARGET_ABI_FMT_ld, arg1, 0);
+        print_pointer(arg2, 1);
+        break;
+    }
     print_syscall_epilogue(name);
 }
 #define print_fcntl64   print_fcntl
@@ -1436,6 +1497,12 @@ if( cmd == val ) { \
         gemu_log("FUTEX_PRIVATE_FLAG|");
         cmd &= ~FUTEX_PRIVATE_FLAG;
     }
+#endif
+#ifdef FUTEX_CLOCK_REALTIME
+    if (cmd & FUTEX_CLOCK_REALTIME) {
+        gemu_log("FUTEX_CLOCK_REALTIME|");
+        cmd &= ~FUTEX_CLOCK_REALTIME;
+    }
 #endif
     print_op(FUTEX_WAIT)
     print_op(FUTEX_WAKE)
index 19630eaf20771583e40e053e1f723b928349497d..ee82a2da4ef73fb35e70e73bffbaeb3bef9f9d53 100644 (file)
@@ -78,6 +78,9 @@ int __clone2(int (*fn)(void *), void *child_stack_base,
 #ifdef CONFIG_ATTR
 #include "qemu/xattr.h"
 #endif
+#ifdef CONFIG_SENDFILE
+#include <sys/sendfile.h>
+#endif
 
 #define termios host_termios
 #define winsize host_winsize
@@ -1776,7 +1779,7 @@ static struct iovec *lock_iovec(int type, abi_ulong target_addr,
         errno = 0;
         return NULL;
     }
-    if (count > IOV_MAX) {
+    if (count < 0 || count > IOV_MAX) {
         errno = EINVAL;
         return NULL;
     }
@@ -2001,16 +2004,30 @@ out2:
     return ret;
 }
 
-/* do_accept() Must return target values and target errnos. */
-static abi_long do_accept(int fd, abi_ulong target_addr,
-                          abi_ulong target_addrlen_addr)
+/* If we don't have a system accept4() then just call accept.
+ * The callsites to do_accept4() will ensure that they don't
+ * pass a non-zero flags argument in this config.
+ */
+#ifndef CONFIG_ACCEPT4
+static inline int accept4(int sockfd, struct sockaddr *addr,
+                          socklen_t *addrlen, int flags)
+{
+    assert(flags == 0);
+    return accept(sockfd, addr, addrlen);
+}
+#endif
+
+/* do_accept4() Must return target values and target errnos. */
+static abi_long do_accept4(int fd, abi_ulong target_addr,
+                           abi_ulong target_addrlen_addr, int flags)
 {
     socklen_t addrlen;
     void *addr;
     abi_long ret;
 
-    if (target_addr == 0)
-       return get_errno(accept(fd, NULL, NULL));
+    if (target_addr == 0) {
+        return get_errno(accept4(fd, NULL, NULL, flags));
+    }
 
     /* linux returns EINVAL if addrlen pointer is invalid */
     if (get_user_u32(addrlen, target_addrlen_addr))
@@ -2025,7 +2042,7 @@ static abi_long do_accept(int fd, abi_ulong target_addr,
 
     addr = alloca(addrlen);
 
-    ret = get_errno(accept(fd, addr, &addrlen));
+    ret = get_errno(accept4(fd, addr, &addrlen, flags));
     if (!is_error(ret)) {
         host_to_target_sockaddr(target_addr, addr, addrlen);
         if (put_user_u32(addrlen, target_addrlen_addr))
@@ -2251,7 +2268,7 @@ static abi_long do_socketcall(int num, abi_ulong vptr)
                 || get_user_ual(target_addrlen, vptr + 2 * n))
                 return -TARGET_EFAULT;
 
-            ret = do_accept(sockfd, target_addr, target_addrlen);
+            ret = do_accept4(sockfd, target_addr, target_addrlen, 0);
         }
         break;
     case SOCKOP_getsockname:
@@ -4922,6 +4939,7 @@ static int do_futex(target_ulong uaddr, int op, int val, target_ulong timeout,
 #endif
     switch (base_op) {
     case FUTEX_WAIT:
+    case FUTEX_WAIT_BITSET:
         if (timeout) {
             pts = &ts;
             target_to_host_timespec(pts, timeout);
@@ -4929,7 +4947,7 @@ static int do_futex(target_ulong uaddr, int op, int val, target_ulong timeout,
             pts = NULL;
         }
         return get_errno(sys_futex(g2h(uaddr), op, tswap32(val),
-                         pts, NULL, 0));
+                         pts, NULL, val3));
     case FUTEX_WAKE:
         return get_errno(sys_futex(g2h(uaddr), op, val, NULL, NULL, 0));
     case FUTEX_FD:
@@ -6673,7 +6691,16 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
 #endif
 #ifdef TARGET_NR_accept
     case TARGET_NR_accept:
-        ret = do_accept(arg1, arg2, arg3);
+        ret = do_accept4(arg1, arg2, arg3, 0);
+        break;
+#endif
+#ifdef TARGET_NR_accept4
+    case TARGET_NR_accept4:
+#ifdef CONFIG_ACCEPT4
+        ret = do_accept4(arg1, arg2, arg3, arg4);
+#else
+        goto unimplemented;
+#endif
         break;
 #endif
 #ifdef TARGET_NR_bind
@@ -7530,8 +7557,58 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
 #else
         goto unimplemented;
 #endif
+
+#ifdef CONFIG_SENDFILE
+    case TARGET_NR_sendfile:
+    {
+        off_t *offp = NULL;
+        off_t off;
+        if (arg3) {
+            ret = get_user_sal(off, arg3);
+            if (is_error(ret)) {
+                break;
+            }
+            offp = &off;
+        }
+        ret = get_errno(sendfile(arg1, arg2, offp, arg4));
+        if (!is_error(ret) && arg3) {
+            abi_long ret2 = put_user_sal(off, arg3);
+            if (is_error(ret2)) {
+                ret = ret2;
+            }
+        }
+        break;
+    }
+#ifdef TARGET_NR_sendfile64
+    case TARGET_NR_sendfile64:
+    {
+        off_t *offp = NULL;
+        off_t off;
+        if (arg3) {
+            ret = get_user_s64(off, arg3);
+            if (is_error(ret)) {
+                break;
+            }
+            offp = &off;
+        }
+        ret = get_errno(sendfile(arg1, arg2, offp, arg4));
+        if (!is_error(ret) && arg3) {
+            abi_long ret2 = put_user_s64(off, arg3);
+            if (is_error(ret2)) {
+                ret = ret2;
+            }
+        }
+        break;
+    }
+#endif
+#else
     case TARGET_NR_sendfile:
+#ifdef TARGET_NR_sendfile64:
+    case TARGET_NR_sendfile64:
+#endif
         goto unimplemented;
+#endif
+
 #ifdef TARGET_NR_getpmsg
     case TARGET_NR_getpmsg:
         goto unimplemented;
@@ -7679,18 +7756,20 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
         {
             int gidsetsize = arg1;
             target_id *target_grouplist;
-            gid_t *grouplist;
+            gid_t *grouplist = NULL;
             int i;
-
-            grouplist = alloca(gidsetsize * sizeof(gid_t));
-            target_grouplist = lock_user(VERIFY_READ, arg2, gidsetsize * 2, 1);
-            if (!target_grouplist) {
-                ret = -TARGET_EFAULT;
-                goto fail;
+            if (gidsetsize) {
+                grouplist = alloca(gidsetsize * sizeof(gid_t));
+                target_grouplist = lock_user(VERIFY_READ, arg2, gidsetsize * 2, 1);
+                if (!target_grouplist) {
+                    ret = -TARGET_EFAULT;
+                    goto fail;
+                }
+                for (i = 0; i < gidsetsize; i++) {
+                    grouplist[i] = low2highgid(tswapid(target_grouplist[i]));
+                }
+                unlock_user(target_grouplist, arg2, 0);
             }
-            for(i = 0;i < gidsetsize; i++)
-                grouplist[i] = low2highgid(tswapid(target_grouplist[i]));
-            unlock_user(target_grouplist, arg2, 0);
             ret = get_errno(setgroups(gidsetsize, grouplist));
         }
         break;
@@ -8552,7 +8631,20 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
 
 #ifdef TARGET_NR_set_robust_list
     case TARGET_NR_set_robust_list:
-       goto unimplemented_nowarn;
+    case TARGET_NR_get_robust_list:
+        /* The ABI for supporting robust futexes has userspace pass
+         * the kernel a pointer to a linked list which is updated by
+         * userspace after the syscall; the list is walked by the kernel
+         * when the thread exits. Since the linked list in QEMU guest
+         * memory isn't a valid linked list for the host and we have
+         * no way to reliably intercept the thread-death event, we can't
+         * support these. Silently return ENOSYS so that guest userspace
+         * falls back to a non-robust futex implementation (which should
+         * be OK except in the corner case of the guest crashing while
+         * holding a mutex that is shared with another process via
+         * shared memory).
+         */
+        goto unimplemented_nowarn;
 #endif
 
 #if defined(TARGET_NR_utimensat) && defined(__NR_utimensat)
index ab05c49abbb795d65c96158e1ab1326340c4eaa2..112e92064d4cc957805127c67f114100adbfade0 100644 (file)
--- a/monitor.c
+++ b/monitor.c
@@ -47,6 +47,7 @@
 #include "migration/migration.h"
 #include "sysemu/kvm.h"
 #include "qemu/acl.h"
+#include "tpm/tpm.h"
 #include "qapi/qmp/qint.h"
 #include "qapi/qmp/qfloat.h"
 #include "qapi/qmp/qlist.h"
@@ -2718,6 +2719,13 @@ static mon_cmd_t info_cmds[] = {
         .help       = "show available trace-events & their state",
         .mhandler.cmd = do_trace_print_events,
     },
+    {
+        .name       = "tpm",
+        .args_type  = "",
+        .params     = "",
+        .help       = "show the TPM device",
+        .mhandler.cmd = hmp_info_tpm,
+    },
     {
         .name       = NULL,
     },
index 28b070f16bb560d0f64203dc0900e4bb406e516c..4494e53693bd35373538f39ab6198aa5ebce8326 100644 (file)
 # Since: 1.4
 ##
 { 'command': 'chardev-remove', 'data': {'id': 'str'} }
+
+##
+# @TpmModel:
+#
+# An enumeration of TPM models
+#
+# @tpm-tis: TPM TIS model
+#
+# Since: 1.5
+##
+{ 'enum': 'TpmModel', 'data': [ 'tpm-tis' ] }
+
+##
+# @query-tpm-models:
+#
+# Return a list of supported TPM models
+#
+# Returns: a list of TpmModel
+#
+# Since: 1.5
+##
+{ 'command': 'query-tpm-models', 'returns': ['TpmModel'] }
+
+##
+# @TpmType:
+#
+# An enumeration of TPM types
+#
+# @passthrough: TPM passthrough type
+#
+# Since: 1.5
+##
+{ 'enum': 'TpmType', 'data': [ 'passthrough' ] }
+
+##
+# @query-tpm-types:
+#
+# Return a list of supported TPM types
+#
+# Returns: a list of TpmType
+#
+# Since: 1.5
+##
+{ 'command': 'query-tpm-types', 'returns': ['TpmType'] }
+
+##
+# @TPMPassthroughOptions:
+#
+# Information about the TPM passthrough type
+#
+# @path: #optional string describing the path used for accessing the TPM device
+#
+# @cancel-path: #optional string showing the TPM's sysfs cancel file
+#               for cancellation of TPM commands while they are executing
+#
+# Since: 1.5
+##
+{ 'type': 'TPMPassthroughOptions', 'data': { '*path' : 'str',
+                                             '*cancel-path' : 'str'} }
+
+##
+# @TpmTypeOptions:
+#
+# A union referencing different TPM backend types' configuration options
+#
+# @tpm-passthough-options: TPMPassthroughOptions describing the TPM
+#                          passthrough configuration options
+#
+# Since: 1.5
+##
+{ 'union': 'TpmTypeOptions',
+   'data': { 'tpm-passthrough-options' : 'TPMPassthroughOptions' } }
+
+##
+# @TpmInfo:
+#
+# Information about the TPM
+#
+# @id: The Id of the TPM
+#
+# @model: The TPM frontend model
+#
+# @type: The TPM (backend) type being used
+#
+# @tpm-options: The TPM (backend) type configuration options
+#
+# Since: 1.5
+##
+{ 'type': 'TPMInfo',
+  'data': {'id': 'str',
+           'model': 'TpmModel',
+           'type': 'TpmType',
+           'tpm-options': 'TpmTypeOptions' } }
+
+##
+# @query-tpm:
+#
+# Return information about the TPM device
+#
+# Returns: @TPMInfo on success
+#
+# Since: 1.5
+##
+{ 'command': 'query-tpm', 'returns': ['TPMInfo'] }
index 04aa589c7ebd6e70c64ee3c87eb23ce5978946de..83787c74c4f8c869b2e1b0e1f6d5b9e101332be3 100644 (file)
@@ -533,6 +533,30 @@ int send_all(int fd, const void *_buf, int len1)
     }
     return len1 - len;
 }
+
+int recv_all(int fd, void *_buf, int len1, bool single_read)
+{
+    int ret, len;
+    uint8_t *buf = _buf;
+
+    len = len1;
+    while ((len > 0) && (ret = read(fd, buf, len)) != 0) {
+        if (ret < 0) {
+            if (errno != EINTR && errno != EAGAIN) {
+                return -1;
+            }
+            continue;
+        } else {
+            if (single_read) {
+                return ret;
+            }
+            buf += ret;
+            len -= ret;
+        }
+    }
+    return len1 - len;
+}
+
 #endif /* !_WIN32 */
 
 typedef struct IOWatchPoll
index cd76f2a00c44ebfba28ca52a8f9d158dfeb0ffd3..30fb85d6195a96843867ebf0d35b88779fbf355e 100644 (file)
@@ -2217,6 +2217,80 @@ STEXI
 ETEXI
 DEFHEADING()
 
+#ifdef CONFIG_TPM
+DEFHEADING(TPM device options:)
+
+DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \
+    "-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
+
+The general form of a TPM device option is:
+@table @option
+
+@item -tpmdev @var{backend} ,id=@var{id} [,@var{options}]
+@findex -tpmdev
+Backend type must be:
+@option{passthrough}.
+
+The specific backend type will determine the applicable options.
+The @code{-tpmdev} option requires a @code{-device} option.
+
+Options to each backend are described below.
+
+Use 'help' to print all available TPM backend types.
+@example
+qemu -tpmdev help
+@end example
+
+@item -tpmdev passthrough, id=@var{id}, path=@var{path}, cancel-path=@var{cancel-path}
+
+(Linux-host only) Enable access to the host's TPM using the passthrough
+driver.
+
+@option{path} specifies the path to the host's TPM device, i.e., on
+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
+used by any other application on the host.
+
+Since the host's firmware (BIOS/UEFI) has already initialized the TPM,
+the VM's firmware (BIOS/UEFI) will not be able to initialize the
+TPM again and may therefore not show a TPM-specific menu that would
+otherwise allow the user to configure the TPM, e.g., allow the user to
+enable/disable or activate/deactivate the TPM.
+Further, if TPM ownership is released from within a VM then the host's TPM
+will get disabled and deactivated. To enable and activate the
+TPM again afterwards, the host has to be rebooted and the user is
+required to enter the firmware's menu to enable and activate the TPM.
+If the TPM is left disabled and/or deactivated most TPM commands will fail.
+
+To create a passthrough TPM use the following two options:
+@example
+-tpmdev passthrough,id=tpm0 -device tpm-tis,tpmdev=tpm0
+@end example
+Note that the @code{-tpmdev} id is @code{tpm0} and is referenced by
+@code{tpmdev=tpm0} in the device option.
+
+@end table
+
+ETEXI
+
+DEFHEADING()
+
+#endif
+
 DEFHEADING(Linux/Multiboot boot specific:)
 STEXI
 
index 16bf44a376bd5ce096d6e96d7eedd73f09b939d2..7ed98d72fb976df5d25df6a7fadf39b5f8c09efe 100644 (file)
@@ -287,7 +287,7 @@ GIOStatus ga_channel_write_all(GAChannel *c, const char *buf, size_t size)
 static gboolean ga_channel_open(GAChannel *c, GAChannelMethod method,
                                 const gchar *path)
 {
-    if (!method == GA_CHANNEL_VIRTIO_SERIAL) {
+    if (method != GA_CHANNEL_VIRTIO_SERIAL) {
         g_critical("unsupported communication method");
         return false;
     }
index 7a0202eb2a25487d03c81f4cb0e921195db2c81e..d7da8506152b1c90d6cc91f6878eb8e81d53b673 100644 (file)
 #include <sys/types.h>
 #include <sys/ioctl.h>
 #include <sys/wait.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
 #include "qga/guest-agent-core.h"
 #include "qga-qmp-commands.h"
 #include "qapi/qmp/qerror.h"
@@ -119,6 +123,77 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err)
     /* succeded */
 }
 
+int64_t qmp_guest_get_time(Error **errp)
+{
+   int ret;
+   qemu_timeval tq;
+   int64_t time_ns;
+
+   ret = qemu_gettimeofday(&tq);
+   if (ret < 0) {
+       error_setg_errno(errp, errno, "Failed to get time");
+       return -1;
+   }
+
+   time_ns = tq.tv_sec * 1000000000LL + tq.tv_usec * 1000;
+   return time_ns;
+}
+
+void qmp_guest_set_time(int64_t time_ns, Error **errp)
+{
+    int ret;
+    int status;
+    pid_t pid;
+    Error *local_err = NULL;
+    struct timeval tv;
+
+    /* year-2038 will overflow in case time_t is 32bit */
+    if (time_ns / 1000000000 != (time_t)(time_ns / 1000000000)) {
+        error_setg(errp, "Time %" PRId64 " is too large", time_ns);
+        return;
+    }
+
+    tv.tv_sec = time_ns / 1000000000;
+    tv.tv_usec = (time_ns % 1000000000) / 1000;
+
+    ret = settimeofday(&tv, NULL);
+    if (ret < 0) {
+        error_setg_errno(errp, errno, "Failed to set time to guest");
+        return;
+    }
+
+    /* Set the Hardware Clock to the current System Time. */
+    pid = fork();
+    if (pid == 0) {
+        setsid();
+        reopen_fd_to_null(0);
+        reopen_fd_to_null(1);
+        reopen_fd_to_null(2);
+
+        execle("/sbin/hwclock", "hwclock", "-w", NULL, environ);
+        _exit(EXIT_FAILURE);
+    } else if (pid < 0) {
+        error_setg_errno(errp, errno, "failed to create child process");
+        return;
+    }
+
+    ga_wait_child(pid, &status, &local_err);
+    if (error_is_set(&local_err)) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    if (!WIFEXITED(status)) {
+        error_setg(errp, "child process has terminated abnormally");
+        return;
+    }
+
+    if (WEXITSTATUS(status)) {
+        error_setg(errp, "hwclock failed to set hardware clock to system time");
+        return;
+    }
+}
+
 typedef struct GuestFileHandle {
     uint64_t id;
     FILE *fh;
@@ -129,14 +204,22 @@ static struct {
     QTAILQ_HEAD(, GuestFileHandle) filehandles;
 } guest_file_state;
 
-static void guest_file_handle_add(FILE *fh)
+static int64_t guest_file_handle_add(FILE *fh, Error **errp)
 {
     GuestFileHandle *gfh;
+    int64_t handle;
+
+    handle = ga_get_fd_handle(ga_state, errp);
+    if (error_is_set(errp)) {
+        return 0;
+    }
 
     gfh = g_malloc0(sizeof(GuestFileHandle));
-    gfh->id = fileno(fh);
+    gfh->id = handle;
     gfh->fh = fh;
     QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next);
+
+    return handle;
 }
 
 static GuestFileHandle *guest_file_handle_find(int64_t id, Error **err)
@@ -158,7 +241,7 @@ int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, E
 {
     FILE *fh;
     int fd;
-    int64_t ret = -1;
+    int64_t ret = -1, handle;
 
     if (!has_mode) {
         mode = "r";
@@ -184,9 +267,14 @@ int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, E
         return -1;
     }
 
-    guest_file_handle_add(fh);
-    slog("guest-file-open, handle: %d", fd);
-    return fd;
+    handle = guest_file_handle_add(fh, err);
+    if (error_is_set(err)) {
+        fclose(fh);
+        return -1;
+    }
+
+    slog("guest-file-open, handle: %d", handle);
+    return handle;
 }
 
 void qmp_guest_file_close(int64_t handle, Error **err)
@@ -1027,6 +1115,162 @@ error:
     return NULL;
 }
 
+#define SYSCONF_EXACT(name, err) sysconf_exact((name), #name, (err))
+
+static long sysconf_exact(int name, const char *name_str, Error **err)
+{
+    long ret;
+
+    errno = 0;
+    ret = sysconf(name);
+    if (ret == -1) {
+        if (errno == 0) {
+            error_setg(err, "sysconf(%s): value indefinite", name_str);
+        } else {
+            error_setg_errno(err, errno, "sysconf(%s)", name_str);
+        }
+    }
+    return ret;
+}
+
+/* Transfer online/offline status between @vcpu and the guest system.
+ *
+ * On input either @errp or *@errp must be NULL.
+ *
+ * In system-to-@vcpu direction, the following @vcpu fields are accessed:
+ * - R: vcpu->logical_id
+ * - W: vcpu->online
+ * - W: vcpu->can_offline
+ *
+ * In @vcpu-to-system direction, the following @vcpu fields are accessed:
+ * - R: vcpu->logical_id
+ * - R: vcpu->online
+ *
+ * Written members remain unmodified on error.
+ */
+static void transfer_vcpu(GuestLogicalProcessor *vcpu, bool sys2vcpu,
+                          Error **errp)
+{
+    char *dirpath;
+    int dirfd;
+
+    dirpath = g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId64 "/",
+                              vcpu->logical_id);
+    dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
+    if (dirfd == -1) {
+        error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
+    } else {
+        static const char fn[] = "online";
+        int fd;
+        int res;
+
+        fd = openat(dirfd, fn, sys2vcpu ? O_RDONLY : O_RDWR);
+        if (fd == -1) {
+            if (errno != ENOENT) {
+                error_setg_errno(errp, errno, "open(\"%s/%s\")", dirpath, fn);
+            } else if (sys2vcpu) {
+                vcpu->online = true;
+                vcpu->can_offline = false;
+            } else if (!vcpu->online) {
+                error_setg(errp, "logical processor #%" PRId64 " can't be "
+                           "offlined", vcpu->logical_id);
+            } /* otherwise pretend successful re-onlining */
+        } else {
+            unsigned char status;
+
+            res = pread(fd, &status, 1, 0);
+            if (res == -1) {
+                error_setg_errno(errp, errno, "pread(\"%s/%s\")", dirpath, fn);
+            } else if (res == 0) {
+                error_setg(errp, "pread(\"%s/%s\"): unexpected EOF", dirpath,
+                           fn);
+            } else if (sys2vcpu) {
+                vcpu->online = (status != '0');
+                vcpu->can_offline = true;
+            } else if (vcpu->online != (status != '0')) {
+                status = '0' + vcpu->online;
+                if (pwrite(fd, &status, 1, 0) == -1) {
+                    error_setg_errno(errp, errno, "pwrite(\"%s/%s\")", dirpath,
+                                     fn);
+                }
+            } /* otherwise pretend successful re-(on|off)-lining */
+
+            res = close(fd);
+            g_assert(res == 0);
+        }
+
+        res = close(dirfd);
+        g_assert(res == 0);
+    }
+
+    g_free(dirpath);
+}
+
+GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
+{
+    int64_t current;
+    GuestLogicalProcessorList *head, **link;
+    long sc_max;
+    Error *local_err = NULL;
+
+    current = 0;
+    head = NULL;
+    link = &head;
+    sc_max = SYSCONF_EXACT(_SC_NPROCESSORS_CONF, &local_err);
+
+    while (local_err == NULL && current < sc_max) {
+        GuestLogicalProcessor *vcpu;
+        GuestLogicalProcessorList *entry;
+
+        vcpu = g_malloc0(sizeof *vcpu);
+        vcpu->logical_id = current++;
+        vcpu->has_can_offline = true; /* lolspeak ftw */
+        transfer_vcpu(vcpu, true, &local_err);
+
+        entry = g_malloc0(sizeof *entry);
+        entry->value = vcpu;
+
+        *link = entry;
+        link = &entry->next;
+    }
+
+    if (local_err == NULL) {
+        /* there's no guest with zero VCPUs */
+        g_assert(head != NULL);
+        return head;
+    }
+
+    qapi_free_GuestLogicalProcessorList(head);
+    error_propagate(errp, local_err);
+    return NULL;
+}
+
+int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
+{
+    int64_t processed;
+    Error *local_err = NULL;
+
+    processed = 0;
+    while (vcpus != NULL) {
+        transfer_vcpu(vcpus->value, false, &local_err);
+        if (local_err != NULL) {
+            break;
+        }
+        ++processed;
+        vcpus = vcpus->next;
+    }
+
+    if (local_err != NULL) {
+        if (processed == 0) {
+            error_propagate(errp, local_err);
+        } else {
+            error_free(local_err);
+        }
+    }
+
+    return processed;
+}
+
 #else /* defined(__linux__) */
 
 void qmp_guest_suspend_disk(Error **err)
@@ -1050,6 +1294,18 @@ GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
     return NULL;
 }
 
+GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
+{
+    error_set(errp, QERR_UNSUPPORTED);
+    return NULL;
+}
+
+int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
+{
+    error_set(errp, QERR_UNSUPPORTED);
+    return -1;
+}
+
 #endif
 
 #if !defined(CONFIG_FSFREEZE)
index 7e8ecb3b40e84c95c612787181aad132b4235607..b19be9db48501d952be6988b8c6b7e7a99d8b608 100644 (file)
@@ -278,6 +278,29 @@ GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **err)
     return NULL;
 }
 
+int64_t qmp_guest_get_time(Error **errp)
+{
+    error_set(errp, QERR_UNSUPPORTED);
+    return -1;
+}
+
+void qmp_guest_set_time(int64_t time_ns, Error **errp)
+{
+    error_set(errp, QERR_UNSUPPORTED);
+}
+
+GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
+{
+    error_set(errp, QERR_UNSUPPORTED);
+    return NULL;
+}
+
+int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
+{
+    error_set(errp, QERR_UNSUPPORTED);
+    return -1;
+}
+
 /* register init/cleanup routines for stateful command groups */
 void ga_command_state_init(GAState *s, GACommandState *cs)
 {
index 33545983625538d89d9635e1e43e8dbe70ca2436..624a559d941d4f067086061a940bb4ebedab229a 100644 (file)
@@ -35,6 +35,7 @@ bool ga_is_frozen(GAState *s);
 void ga_set_frozen(GAState *s);
 void ga_unset_frozen(GAState *s);
 const char *ga_fsfreeze_hook(GAState *s);
+int64_t ga_get_fd_handle(GAState *s, Error **errp);
 
 #ifndef _WIN32
 void reopen_fd_to_null(int fd);
index db281a508be6d6adb3123a31574d4b4328e84604..99346e15aa59ddeb6cb0f6570da67a7d4b5409c5 100644 (file)
@@ -15,6 +15,7 @@
 #include <stdbool.h>
 #include <glib.h>
 #include <getopt.h>
+#include <glib/gstdio.h>
 #ifndef _WIN32
 #include <syslog.h>
 #include <sys/wait.h>
@@ -30,6 +31,7 @@
 #include "qapi/qmp/qerror.h"
 #include "qapi/qmp/dispatch.h"
 #include "qga/channel.h"
+#include "qemu/bswap.h"
 #ifdef _WIN32
 #include "qga/service-win32.h"
 #include <windows.h>
 #endif
 #define QGA_SENTINEL_BYTE 0xFF
 
+typedef struct GAPersistentState {
+#define QGA_PSTATE_DEFAULT_FD_COUNTER 1000
+    int64_t fd_counter;
+} GAPersistentState;
+
 struct GAState {
     JSONMessageParser parser;
     GMainLoop *main_loop;
@@ -76,6 +83,8 @@ struct GAState {
 #ifdef CONFIG_FSFREEZE
     const char *fsfreeze_hook;
 #endif
+    const gchar *pstate_filepath;
+    GAPersistentState pstate;
 };
 
 struct GAState *ga_state;
@@ -85,6 +94,7 @@ static const char *ga_freeze_whitelist[] = {
     "guest-ping",
     "guest-info",
     "guest-sync",
+    "guest-sync-delimited",
     "guest-fsfreeze-status",
     "guest-fsfreeze-thaw",
     NULL
@@ -724,6 +734,171 @@ VOID WINAPI service_main(DWORD argc, TCHAR *argv[])
 }
 #endif
 
+static void set_persistent_state_defaults(GAPersistentState *pstate)
+{
+    g_assert(pstate);
+    pstate->fd_counter = QGA_PSTATE_DEFAULT_FD_COUNTER;
+}
+
+static void persistent_state_from_keyfile(GAPersistentState *pstate,
+                                          GKeyFile *keyfile)
+{
+    g_assert(pstate);
+    g_assert(keyfile);
+    /* if any fields are missing, either because the file was tampered with
+     * by agents of chaos, or because the field wasn't present at the time the
+     * file was created, the best we can ever do is start over with the default
+     * values. so load them now, and ignore any errors in accessing key-value
+     * pairs
+     */
+    set_persistent_state_defaults(pstate);
+
+    if (g_key_file_has_key(keyfile, "global", "fd_counter", NULL)) {
+        pstate->fd_counter =
+            g_key_file_get_int64(keyfile, "global", "fd_counter", NULL);
+    }
+}
+
+static void persistent_state_to_keyfile(const GAPersistentState *pstate,
+                                        GKeyFile *keyfile)
+{
+    g_assert(pstate);
+    g_assert(keyfile);
+
+    g_key_file_set_int64(keyfile, "global", "fd_counter", pstate->fd_counter);
+}
+
+static gboolean write_persistent_state(const GAPersistentState *pstate,
+                                       const gchar *path)
+{
+    GKeyFile *keyfile = g_key_file_new();
+    GError *gerr = NULL;
+    gboolean ret = true;
+    gchar *data = NULL;
+    gsize data_len;
+
+    g_assert(pstate);
+
+    persistent_state_to_keyfile(pstate, keyfile);
+    data = g_key_file_to_data(keyfile, &data_len, &gerr);
+    if (gerr) {
+        g_critical("failed to convert persistent state to string: %s",
+                   gerr->message);
+        ret = false;
+        goto out;
+    }
+
+    g_file_set_contents(path, data, data_len, &gerr);
+    if (gerr) {
+        g_critical("failed to write persistent state to %s: %s",
+                    path, gerr->message);
+        ret = false;
+        goto out;
+    }
+
+out:
+    if (gerr) {
+        g_error_free(gerr);
+    }
+    if (keyfile) {
+        g_key_file_free(keyfile);
+    }
+    g_free(data);
+    return ret;
+}
+
+static gboolean read_persistent_state(GAPersistentState *pstate,
+                                      const gchar *path, gboolean frozen)
+{
+    GKeyFile *keyfile = NULL;
+    GError *gerr = NULL;
+    struct stat st;
+    gboolean ret = true;
+
+    g_assert(pstate);
+
+    if (stat(path, &st) == -1) {
+        /* it's okay if state file doesn't exist, but any other error
+         * indicates a permissions issue or some other misconfiguration
+         * that we likely won't be able to recover from.
+         */
+        if (errno != ENOENT) {
+            g_critical("unable to access state file at path %s: %s",
+                       path, strerror(errno));
+            ret = false;
+            goto out;
+        }
+
+        /* file doesn't exist. initialize state to default values and
+         * attempt to save now. (we could wait till later when we have
+         * modified state we need to commit, but if there's a problem,
+         * such as a missing parent directory, we want to catch it now)
+         *
+         * there is a potential scenario where someone either managed to
+         * update the agent from a version that didn't use a key store
+         * while qemu-ga thought the filesystem was frozen, or
+         * deleted the key store prior to issuing a fsfreeze, prior
+         * to restarting the agent. in this case we go ahead and defer
+         * initial creation till we actually have modified state to
+         * write, otherwise fail to recover from freeze.
+         */
+        set_persistent_state_defaults(pstate);
+        if (!frozen) {
+            ret = write_persistent_state(pstate, path);
+            if (!ret) {
+                g_critical("unable to create state file at path %s", path);
+                ret = false;
+                goto out;
+            }
+        }
+        ret = true;
+        goto out;
+    }
+
+    keyfile = g_key_file_new();
+    g_key_file_load_from_file(keyfile, path, 0, &gerr);
+    if (gerr) {
+        g_critical("error loading persistent state from path: %s, %s",
+                   path, gerr->message);
+        ret = false;
+        goto out;
+    }
+
+    persistent_state_from_keyfile(pstate, keyfile);
+
+out:
+    if (keyfile) {
+        g_key_file_free(keyfile);
+    }
+    if (gerr) {
+        g_error_free(gerr);
+    }
+
+    return ret;
+}
+
+int64_t ga_get_fd_handle(GAState *s, Error **errp)
+{
+    int64_t handle;
+
+    g_assert(s->pstate_filepath);
+    /* we blacklist commands and avoid operations that potentially require
+     * writing to disk when we're in a frozen state. this includes opening
+     * new files, so we should never get here in that situation
+     */
+    g_assert(!ga_is_frozen(s));
+
+    handle = s->pstate.fd_counter++;
+    if (s->pstate.fd_counter < 0) {
+        s->pstate.fd_counter = 0;
+    }
+    if (!write_persistent_state(&s->pstate, s->pstate_filepath)) {
+        error_setg(errp, "failed to commit persistent state to disk");
+    }
+
+    return handle;
+}
+
 int main(int argc, char **argv)
 {
     const char *sopt = "hVvdm:p:l:f:F::b:s:t:";
@@ -853,7 +1028,9 @@ int main(int argc, char **argv)
     ga_enable_logging(s);
     s->state_filepath_isfrozen = g_strdup_printf("%s/qga.state.isfrozen",
                                                  state_dir);
+    s->pstate_filepath = g_strdup_printf("%s/qga.state", state_dir);
     s->frozen = false;
+
 #ifndef _WIN32
     /* check if a previous instance of qemu-ga exited with filesystems' state
      * marked as frozen. this could be a stale value (a non-qemu-ga process
@@ -910,6 +1087,14 @@ int main(int argc, char **argv)
         }
     }
 
+    /* load persistent state from disk */
+    if (!read_persistent_state(&s->pstate,
+                               s->pstate_filepath,
+                               ga_is_frozen(s))) {
+        g_critical("failed to load persistent state");
+        goto out_bad;
+    }
+
     if (blacklist) {
         s->blacklist = blacklist;
         do {
index d91d903256dc3465bcce639c8c4d1d114962d078..dac4e6f95fd2d35a99949d0dae056f831e8dc85f 100644 (file)
 ##
 { 'command': 'guest-ping' }
 
+##
+# @guest-get-time:
+#
+# Get the information about guest time relative to the Epoch
+# of 1970-01-01 in UTC.
+#
+# Returns: Time in nanoseconds.
+#
+# Since 1.5
+##
+{ 'command': 'guest-get-time',
+  'returns': 'int' }
+
+##
+# @guest-set-time:
+#
+# Set guest time.
+#
+# When a guest is paused or migrated to a file then loaded
+# from that file, the guest OS has no idea that there
+# was a big gap in the time. Depending on how long the
+# gap was, NTP might not be able to resynchronize the
+# guest.
+#
+# This command tries to set guest time to the given value,
+# then sets the Hardware Clock to the current System Time.
+# This will make it easier for a guest to resynchronize
+# without waiting for NTP.
+#
+# @time: time of nanoseconds, relative to the Epoch of
+#        1970-01-01 in UTC.
+#
+# Returns: Nothing on success.
+#
+# Since: 1.5
+##
+{ 'command': 'guest-set-time',
+  'data': { 'time': 'int' } }
+
 ##
 # @GuestAgentCommandInfo:
 #
 ##
 { 'command': 'guest-network-get-interfaces',
   'returns': ['GuestNetworkInterface'] }
+
+##
+# @GuestLogicalProcessor:
+#
+# @logical-id: Arbitrary guest-specific unique identifier of the VCPU.
+#
+# @online: Whether the VCPU is enabled.
+#
+# @can-offline: Whether offlining the VCPU is possible. This member is always
+#               filled in by the guest agent when the structure is returned,
+#               and always ignored on input (hence it can be omitted then).
+#
+# Since: 1.5
+##
+{ 'type': 'GuestLogicalProcessor',
+  'data': {'logical-id': 'int',
+           'online': 'bool',
+           '*can-offline': 'bool'} }
+
+##
+# @guest-get-vcpus:
+#
+# Retrieve the list of the guest's logical processors.
+#
+# This is a read-only operation.
+#
+# Returns: The list of all VCPUs the guest knows about. Each VCPU is put on the
+# list exactly once, but their order is unspecified.
+#
+# Since: 1.5
+##
+{ 'command': 'guest-get-vcpus',
+  'returns': ['GuestLogicalProcessor'] }
+
+##
+# @guest-set-vcpus:
+#
+# Attempt to reconfigure (currently: enable/disable) logical processors inside
+# the guest.
+#
+# The input list is processed node by node in order. In each node @logical-id
+# is used to look up the guest VCPU, for which @online specifies the requested
+# state. The set of distinct @logical-id's is only required to be a subset of
+# the guest-supported identifiers. There's no restriction on list length or on
+# repeating the same @logical-id (with possibly different @online field).
+# Preferably the input list should describe a modified subset of
+# @guest-get-vcpus' return value.
+#
+# Returns: The length of the initial sublist that has been successfully
+#          processed. The guest agent maximizes this value. Possible cases:
+#
+#          0:                if the @vcpus list was empty on input. Guest state
+#                            has not been changed. Otherwise,
+#
+#          Error:            processing the first node of @vcpus failed for the
+#                            reason returned. Guest state has not been changed.
+#                            Otherwise,
+#
+#          < length(@vcpus): more than zero initial nodes have been processed,
+#                            but not the entire @vcpus list. Guest state has
+#                            changed accordingly. To retrieve the error
+#                            (assuming it persists), repeat the call with the
+#                            successfully processed initial sublist removed.
+#                            Otherwise,
+#
+#          length(@vcpus):   call successful.
+#
+# Since: 1.5
+##
+{ 'command': 'guest-set-vcpus',
+  'data':    {'vcpus': ['GuestLogicalProcessor'] },
+  'returns': 'int' }
index 09054565d37aaf5b6aec986a84d5dcf8efcc0b7e..843398a6c673fe0d9dfdc98a242f6773d769bb0d 100644 (file)
@@ -29,7 +29,7 @@ static int printf_win_error(const char *text)
         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
         (char *)&message, 0,
         NULL);
-    n = printf("%s. (Error: %d) %s", text, err, message);
+    n = printf("%s. (Error: %d) %s", text, (int)err, message);
     LocalFree(message);
 
     return n;
index 95022e259ff630ee72da7afbace4fd964ceba89f..b37006084852d6c9fa97c06e74d71c4fa969e7c5 100644 (file)
@@ -2715,6 +2715,24 @@ EQMP
         .mhandler.cmd_new = qmp_marshal_input_query_target,
     },
 
+    {
+        .name       = "query-tpm",
+        .args_type  = "",
+        .mhandler.cmd_new = qmp_marshal_input_query_tpm,
+    },
+
+    {
+        .name       = "query-tpm-models",
+        .args_type  = "",
+        .mhandler.cmd_new = qmp_marshal_input_query_tpm_models,
+    },
+
+    {
+        .name       = "query-tpm-types",
+        .args_type  = "",
+        .mhandler.cmd_new = qmp_marshal_input_query_tpm_types,
+    },
+
     {
         .name       = "chardev-add",
         .args_type  = "id:s,backend:q",
diff --git a/tpm/Makefile.objs b/tpm/Makefile.objs
new file mode 100644 (file)
index 0000000..8676824
--- /dev/null
@@ -0,0 +1,6 @@
+common-obj-y = tpm.o
+ifeq ($(CONFIG_TPM),y)
+common-obj-y += tpm_backend.o
+common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o
+common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
+endif
diff --git a/tpm/tpm.c b/tpm/tpm.c
new file mode 100644 (file)
index 0000000..ffd2495
--- /dev/null
+++ b/tpm/tpm.c
@@ -0,0 +1,357 @@
+/*
+ * TPM configuration
+ *
+ * Copyright (C) 2011-2013 IBM Corporation
+ *
+ * Authors:
+ *  Stefan Berger    <stefanb@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * Based on net.c
+ */
+#include "config-host.h"
+
+#include "monitor/monitor.h"
+#include "qapi/qmp/qerror.h"
+#include "tpm_int.h"
+#include "tpm/tpm.h"
+#include "qemu/config-file.h"
+#include "qmp-commands.h"
+
+static QLIST_HEAD(, TPMBackend) tpm_backends =
+    QLIST_HEAD_INITIALIZER(tpm_backends);
+
+
+#define TPM_MAX_MODELS      1
+#define TPM_MAX_DRIVERS     1
+
+static TPMDriverOps const *be_drivers[TPM_MAX_DRIVERS] = {
+    NULL,
+};
+
+static enum TpmModel tpm_models[TPM_MAX_MODELS] = {
+    -1,
+};
+
+int tpm_register_model(enum TpmModel model)
+{
+    int i;
+
+    for (i = 0; i < TPM_MAX_MODELS; i++) {
+        if (tpm_models[i] == -1) {
+            tpm_models[i] = model;
+            return 0;
+        }
+    }
+    error_report("Could not register TPM model");
+    return 1;
+}
+
+static bool tpm_model_is_registered(enum TpmModel model)
+{
+    int i;
+
+    for (i = 0; i < TPM_MAX_MODELS; i++) {
+        if (tpm_models[i] == model) {
+            return true;
+        }
+    }
+    return false;
+}
+
+/*
+ * Write an error message in the given output buffer.
+ */
+void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len)
+{
+    if (out_len >= sizeof(struct tpm_resp_hdr)) {
+        struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)out;
+
+        resp->tag = cpu_to_be16(TPM_TAG_RSP_COMMAND);
+        resp->len = cpu_to_be32(sizeof(struct tpm_resp_hdr));
+        resp->errcode = cpu_to_be32(TPM_FAIL);
+    }
+}
+
+const TPMDriverOps *tpm_get_backend_driver(const char *type)
+{
+    int i;
+
+    for (i = 0; i < TPM_MAX_DRIVERS && be_drivers[i] != NULL; i++) {
+        if (!strcmp(TpmType_lookup[be_drivers[i]->type], type)) {
+            return be_drivers[i];
+        }
+    }
+
+    return NULL;
+}
+
+#ifdef CONFIG_TPM
+
+int tpm_register_driver(const TPMDriverOps *tdo)
+{
+    int i;
+
+    for (i = 0; i < TPM_MAX_DRIVERS; i++) {
+        if (!be_drivers[i]) {
+            be_drivers[i] = tdo;
+            return 0;
+        }
+    }
+    error_report("Could not register TPM driver");
+    return 1;
+}
+
+/*
+ * Walk the list of available TPM backend drivers and display them on the
+ * screen.
+ */
+void tpm_display_backend_drivers(void)
+{
+    int i;
+
+    fprintf(stderr, "Supported TPM types (choose only one):\n");
+
+    for (i = 0; i < TPM_MAX_DRIVERS && be_drivers[i] != NULL; i++) {
+        fprintf(stderr, "%12s   %s\n",
+                TpmType_lookup[be_drivers[i]->type], be_drivers[i]->desc());
+    }
+    fprintf(stderr, "\n");
+}
+
+/*
+ * Find the TPM with the given Id
+ */
+TPMBackend *qemu_find_tpm(const char *id)
+{
+    TPMBackend *drv;
+
+    if (id) {
+        QLIST_FOREACH(drv, &tpm_backends, list) {
+            if (!strcmp(drv->id, id)) {
+                return drv;
+            }
+        }
+    }
+
+    return NULL;
+}
+
+static int configure_tpm(QemuOpts *opts)
+{
+    const char *value;
+    const char *id;
+    const TPMDriverOps *be;
+    TPMBackend *drv;
+
+    if (!QLIST_EMPTY(&tpm_backends)) {
+        error_report("Only one TPM is allowed.\n");
+        return 1;
+    }
+
+    id = qemu_opts_id(opts);
+    if (id == NULL) {
+        qerror_report(QERR_MISSING_PARAMETER, "id");
+        return 1;
+    }
+
+    value = qemu_opt_get(opts, "type");
+    if (!value) {
+        qerror_report(QERR_MISSING_PARAMETER, "type");
+        tpm_display_backend_drivers();
+        return 1;
+    }
+
+    be = tpm_get_backend_driver(value);
+    if (be == NULL) {
+        qerror_report(QERR_INVALID_PARAMETER_VALUE, "type",
+                      "a TPM backend type");
+        tpm_display_backend_drivers();
+        return 1;
+    }
+
+    drv = be->create(opts, id);
+    if (!drv) {
+        return 1;
+    }
+
+    QLIST_INSERT_HEAD(&tpm_backends, drv, list);
+
+    return 0;
+}
+
+static int tpm_init_tpmdev(QemuOpts *opts, void *dummy)
+{
+    return configure_tpm(opts);
+}
+
+/*
+ * Walk the list of TPM backend drivers that are in use and call their
+ * destroy function to have them cleaned up.
+ */
+void tpm_cleanup(void)
+{
+    TPMBackend *drv, *next;
+
+    QLIST_FOREACH_SAFE(drv, &tpm_backends, list, next) {
+        QLIST_REMOVE(drv, list);
+        drv->ops->destroy(drv);
+    }
+}
+
+/*
+ * Initialize the TPM. Process the tpmdev command line options describing the
+ * TPM backend.
+ */
+int tpm_init(void)
+{
+    if (qemu_opts_foreach(qemu_find_opts("tpmdev"),
+                          tpm_init_tpmdev, NULL, 1) != 0) {
+        return -1;
+    }
+
+    atexit(tpm_cleanup);
+
+    return 0;
+}
+
+/*
+ * Parse the TPM configuration options.
+ * To display all available TPM backends the user may use '-tpmdev help'
+ */
+int tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
+{
+    QemuOpts *opts;
+
+    if (!strcmp(optarg, "help")) {
+        tpm_display_backend_drivers();
+        return -1;
+    }
+    opts = qemu_opts_parse(opts_list, optarg, 1);
+    if (!opts) {
+        return -1;
+    }
+    return 0;
+}
+
+#endif /* CONFIG_TPM */
+
+static const TPMDriverOps *tpm_driver_find_by_type(enum TpmType type)
+{
+    int i;
+
+    for (i = 0; i < TPM_MAX_DRIVERS && be_drivers[i] != NULL; i++) {
+        if (be_drivers[i]->type == type) {
+            return be_drivers[i];
+        }
+    }
+    return NULL;
+}
+
+static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv)
+{
+    TPMInfo *res = g_new0(TPMInfo, 1);
+    TPMPassthroughOptions *tpo;
+
+    res->id = g_strdup(drv->id);
+    res->model = drv->fe_model;
+    res->type = drv->ops->type;
+    res->tpm_options = g_new0(TpmTypeOptions, 1);
+
+    switch (res->type) {
+    case TPM_TYPE_PASSTHROUGH:
+        res->tpm_options->kind = TPM_TYPE_OPTIONS_KIND_TPM_PASSTHROUGH_OPTIONS;
+        tpo = g_new0(TPMPassthroughOptions, 1);
+        res->tpm_options->tpm_passthrough_options = tpo;
+        if (drv->path) {
+            tpo->path = g_strdup(drv->path);
+            tpo->has_path = true;
+        }
+        if (drv->cancel_path) {
+            tpo->cancel_path = g_strdup(drv->cancel_path);
+            tpo->has_cancel_path = true;
+        }
+        break;
+    case TPM_TYPE_MAX:
+        break;
+    }
+
+    return res;
+}
+
+/*
+ * Walk the list of active TPM backends and collect information about them
+ * following the schema description in qapi-schema.json.
+ */
+TPMInfoList *qmp_query_tpm(Error **errp)
+{
+    TPMBackend *drv;
+    TPMInfoList *info, *head = NULL, *cur_item = NULL;
+
+    QLIST_FOREACH(drv, &tpm_backends, list) {
+        if (!tpm_model_is_registered(drv->fe_model)) {
+            continue;
+        }
+        info = g_new0(TPMInfoList, 1);
+        info->value = qmp_query_tpm_inst(drv);
+
+        if (!cur_item) {
+            head = cur_item = info;
+        } else {
+            cur_item->next = info;
+            cur_item = info;
+        }
+    }
+
+    return head;
+}
+
+TpmTypeList *qmp_query_tpm_types(Error **errp)
+{
+    unsigned int i = 0;
+    TpmTypeList *head = NULL, *prev = NULL, *cur_item;
+
+    for (i = 0; i < TPM_TYPE_MAX; i++) {
+        if (!tpm_driver_find_by_type(i)) {
+            continue;
+        }
+        cur_item = g_new0(TpmTypeList, 1);
+        cur_item->value = i;
+
+        if (prev) {
+            prev->next = cur_item;
+        }
+        if (!head) {
+            head = cur_item;
+        }
+        prev = cur_item;
+    }
+
+    return head;
+}
+
+TpmModelList *qmp_query_tpm_models(Error **errp)
+{
+    unsigned int i = 0;
+    TpmModelList *head = NULL, *prev = NULL, *cur_item;
+
+    for (i = 0; i < TPM_MODEL_MAX; i++) {
+        if (!tpm_model_is_registered(i)) {
+            continue;
+        }
+        cur_item = g_new0(TpmModelList, 1);
+        cur_item->value = i;
+
+        if (prev) {
+            prev->next = cur_item;
+        }
+        if (!head) {
+            head = cur_item;
+        }
+        prev = cur_item;
+    }
+
+    return head;
+}
diff --git a/tpm/tpm_backend.c b/tpm/tpm_backend.c
new file mode 100644 (file)
index 0000000..4144ef7
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ *  common TPM backend driver functions
+ *
+ *  Copyright (c) 2012-2013 IBM Corporation
+ *  Authors:
+ *    Stefan Berger <stefanb@us.ibm.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>
+ */
+
+#include "tpm/tpm.h"
+#include "qemu/thread.h"
+#include "tpm_backend.h"
+
+void tpm_backend_thread_deliver_request(TPMBackendThread *tbt)
+{
+   g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_PROCESS_CMD, NULL);
+}
+
+void tpm_backend_thread_create(TPMBackendThread *tbt,
+                               GFunc func, gpointer user_data)
+{
+    if (!tbt->pool) {
+        tbt->pool = g_thread_pool_new(func, user_data, 1, TRUE, NULL);
+        g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_INIT, NULL);
+    }
+}
+
+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 = NULL;
+    }
+}
+
+void tpm_backend_thread_tpm_reset(TPMBackendThread *tbt,
+                                  GFunc func, gpointer user_data)
+{
+    if (!tbt->pool) {
+        tpm_backend_thread_create(tbt, func, user_data);
+    } else {
+        g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_TPM_RESET,
+                           NULL);
+    }
+}
diff --git a/tpm/tpm_backend.h b/tpm/tpm_backend.h
new file mode 100644 (file)
index 0000000..05d94d0
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ *  common TPM backend driver functions
+ *
+ *  Copyright (c) 2012-2013 IBM Corporation
+ *  Authors:
+ *    Stefan Berger <stefanb@us.ibm.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>
+ */
+
+#ifndef TPM_TPM_BACKEND_H
+#define TPM_TPM_BACKEND_H
+
+#include <glib.h>
+
+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);
+void tpm_backend_thread_tpm_reset(TPMBackendThread *tbt,
+                                  GFunc func, gpointer user_data);
+
+typedef enum TPMBackendCmd {
+    TPM_BACKEND_CMD_INIT = 1,
+    TPM_BACKEND_CMD_PROCESS_CMD,
+    TPM_BACKEND_CMD_END,
+    TPM_BACKEND_CMD_TPM_RESET,
+} TPMBackendCmd;
+
+#endif /* TPM_TPM_BACKEND_H */
diff --git a/tpm/tpm_int.h b/tpm/tpm_int.h
new file mode 100644 (file)
index 0000000..f705643
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * TPM configuration
+ *
+ * Copyright (C) 2011-2013 IBM Corporation
+ *
+ * Authors:
+ *  Stefan Berger    <stefanb@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#ifndef TPM_TPM_INT_H
+#define TPM_TPM_INT_H
+
+#include "exec/memory.h"
+#include "tpm/tpm_tis.h"
+
+struct TPMDriverOps;
+typedef struct TPMDriverOps TPMDriverOps;
+
+typedef struct TPMPassthruState TPMPassthruState;
+
+typedef struct TPMBackend {
+    char *id;
+    enum TpmModel fe_model;
+    char *path;
+    char *cancel_path;
+    const TPMDriverOps *ops;
+
+    union {
+        TPMPassthruState *tpm_pt;
+    } s;
+
+    QLIST_ENTRY(TPMBackend) list;
+} TPMBackend;
+
+/* overall state of the TPM interface */
+typedef struct TPMState {
+    ISADevice busdev;
+    MemoryRegion mmio;
+
+    union {
+        TPMTISEmuState tis;
+    } s;
+
+    uint8_t     locty_number;
+    TPMLocality *locty_data;
+
+    char *backend;
+    TPMBackend *be_driver;
+} TPMState;
+
+#define TPM(obj) OBJECT_CHECK(TPMState, (obj), TYPE_TPM_TIS)
+
+typedef void (TPMRecvDataCB)(TPMState *, uint8_t locty);
+
+struct TPMDriverOps {
+    enum TpmType type;
+    /* get a descriptive text of the backend to display to the user */
+    const char *(*desc)(void);
+
+    TPMBackend *(*create)(QemuOpts *opts, const char *id);
+    void (*destroy)(TPMBackend *t);
+
+    /* initialize the backend */
+    int (*init)(TPMBackend *t, TPMState *s, TPMRecvDataCB *datacb);
+    /* start up the TPM on the backend */
+    int (*startup_tpm)(TPMBackend *t);
+    /* returns true if nothing will ever answer TPM requests */
+    bool (*had_startup_error)(TPMBackend *t);
+
+    size_t (*realloc_buffer)(TPMSizedBuffer *sb);
+
+    void (*deliver_request)(TPMBackend *t);
+
+    void (*reset)(TPMBackend *t);
+
+    void (*cancel_cmd)(TPMBackend *t);
+
+    bool (*get_tpm_established_flag)(TPMBackend *t);
+};
+
+struct tpm_req_hdr {
+    uint16_t tag;
+    uint32_t len;
+    uint32_t ordinal;
+} QEMU_PACKED;
+
+struct tpm_resp_hdr {
+    uint16_t tag;
+    uint32_t len;
+    uint32_t errcode;
+} QEMU_PACKED;
+
+#define TPM_TAG_RQU_COMMAND       0xc1
+#define TPM_TAG_RQU_AUTH1_COMMAND 0xc2
+#define TPM_TAG_RQU_AUTH2_COMMAND 0xc3
+
+#define TPM_TAG_RSP_COMMAND       0xc4
+#define TPM_TAG_RSP_AUTH1_COMMAND 0xc5
+#define TPM_TAG_RSP_AUTH2_COMMAND 0xc6
+
+#define TPM_FAIL                  9
+
+#define TPM_ORD_GetTicks          0xf1
+
+TPMBackend *qemu_find_tpm(const char *id);
+int tpm_register_model(enum TpmModel model);
+int tpm_register_driver(const TPMDriverOps *tdo);
+void tpm_display_backend_drivers(void);
+const TPMDriverOps *tpm_get_backend_driver(const char *type);
+void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len);
+
+extern const TPMDriverOps tpm_passthrough_driver;
+
+#endif /* TPM_TPM_INT_H */
diff --git a/tpm/tpm_passthrough.c b/tpm/tpm_passthrough.c
new file mode 100644 (file)
index 0000000..24aff4d
--- /dev/null
@@ -0,0 +1,530 @@
+/*
+ *  passthrough TPM driver
+ *
+ *  Copyright (c) 2010 - 2013 IBM Corporation
+ *  Authors:
+ *    Stefan Berger <stefanb@us.ibm.com>
+ *
+ *  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 <http://www.gnu.org/licenses/>
+ */
+
+#include <dirent.h>
+
+#include "qemu-common.h"
+#include "qapi/error.h"
+#include "qemu/sockets.h"
+#include "tpm_int.h"
+#include "hw/hw.h"
+#include "hw/pc.h"
+#include "tpm_tis.h"
+#include "tpm_backend.h"
+
+/* #define DEBUG_TPM */
+
+#ifdef DEBUG_TPM
+#define DPRINTF(fmt, ...) \
+    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+    do { } while (0)
+#endif
+
+/* data structures */
+
+typedef struct TPMPassthruThreadParams {
+    TPMState *tpm_state;
+
+    TPMRecvDataCB *recv_data_callback;
+    TPMBackend *tb;
+} TPMPassthruThreadParams;
+
+struct TPMPassthruState {
+    TPMBackendThread tbt;
+
+    TPMPassthruThreadParams tpm_thread_params;
+
+    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);
+}
+
+static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
+{
+    return recv_all(fd, buf, len, true);
+}
+
+static uint32_t tpm_passthrough_get_size_from_buffer(const uint8_t *buf)
+{
+    struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)buf;
+
+    return be32_to_cpu(resp->len);
+}
+
+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;
+
+    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) {
+        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;
+    }
+
+    tpm_pt->tpm_executing = false;
+
+    ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len);
+    if (ret < 0) {
+        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;
+        error_report("tpm_passthrough: received invalid response "
+                     "packet from TPM\n");
+    }
+
+err_exit:
+    if (ret < 0) {
+        tpm_write_fatal_error_response(out, out_len);
+    }
+
+    tpm_pt->tpm_executing = false;
+
+    return ret;
+}
+
+static int tpm_passthrough_unix_transfer(TPMPassthruState *tpm_pt,
+                                         const TPMLocality *locty_data)
+{
+    return tpm_passthrough_unix_tx_bufs(tpm_pt,
+                                        locty_data->w_buffer.buffer,
+                                        locty_data->w_offset,
+                                        locty_data->r_buffer.buffer,
+                                        locty_data->r_buffer.size);
+}
+
+static void tpm_passthrough_worker_thread(gpointer data,
+                                          gpointer user_data)
+{
+    TPMPassthruThreadParams *thr_parms = user_data;
+    TPMPassthruState *tpm_pt = thr_parms->tb->s.tpm_pt;
+    TPMBackendCmd cmd = (TPMBackendCmd)data;
+
+    DPRINTF("tpm_passthrough: processing command type %d\n", cmd);
+
+    switch (cmd) {
+    case TPM_BACKEND_CMD_PROCESS_CMD:
+        tpm_passthrough_unix_transfer(tpm_pt,
+                                      thr_parms->tpm_state->locty_data);
+
+        thr_parms->recv_data_callback(thr_parms->tpm_state,
+                                      thr_parms->tpm_state->locty_number);
+        break;
+    case TPM_BACKEND_CMD_INIT:
+    case TPM_BACKEND_CMD_END:
+    case TPM_BACKEND_CMD_TPM_RESET:
+        /* nothing to do */
+        break;
+    }
+}
+
+/*
+ * Start the TPM (thread). If it had been started before, then terminate
+ * and start it again.
+ */
+static int tpm_passthrough_startup_tpm(TPMBackend *tb)
+{
+    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
+
+    /* terminate a running TPM */
+    tpm_backend_thread_end(&tpm_pt->tbt);
+
+    tpm_backend_thread_create(&tpm_pt->tbt,
+                              tpm_passthrough_worker_thread,
+                              &tb->s.tpm_pt->tpm_thread_params);
+
+    return 0;
+}
+
+static void tpm_passthrough_reset(TPMBackend *tb)
+{
+    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
+
+    DPRINTF("tpm_passthrough: CALL TO TPM_RESET!\n");
+
+    tpm_passthrough_cancel_cmd(tb);
+
+    tpm_backend_thread_end(&tpm_pt->tbt);
+
+    tpm_pt->had_startup_error = false;
+}
+
+static int tpm_passthrough_init(TPMBackend *tb, TPMState *s,
+                                TPMRecvDataCB *recv_data_cb)
+{
+    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
+
+    tpm_pt->tpm_thread_params.tpm_state = s;
+    tpm_pt->tpm_thread_params.recv_data_callback = recv_data_cb;
+    tpm_pt->tpm_thread_params.tb = tb;
+
+    return 0;
+}
+
+static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb)
+{
+    return false;
+}
+
+static bool tpm_passthrough_get_startup_error(TPMBackend *tb)
+{
+    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
+
+    return tpm_pt->had_startup_error;
+}
+
+static size_t tpm_passthrough_realloc_buffer(TPMSizedBuffer *sb)
+{
+    size_t wanted_size = 4096; /* Linux tpm.c buffer size */
+
+    if (sb->size != wanted_size) {
+        sb->buffer = g_realloc(sb->buffer, wanted_size);
+        sb->size = wanted_size;
+    }
+    return sb->size;
+}
+
+static void tpm_passthrough_deliver_request(TPMBackend *tb)
+{
+    TPMPassthruState *tpm_pt = tb->s.tpm_pt;
+
+    tpm_backend_thread_deliver_request(&tpm_pt->tbt);
+}
+
+static void tpm_passthrough_cancel_cmd(TPMBackend *tb)
+{
+    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)
+{
+    return "Passthrough TPM backend driver";
+}
+
+/*
+ * A basic test of a TPM device. We expect a well formatted response header
+ * (error response is fine) within one second.
+ */
+static int tpm_passthrough_test_tpmdev(int fd)
+{
+    struct tpm_req_hdr req = {
+        .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND),
+        .len = cpu_to_be32(sizeof(req)),
+        .ordinal = cpu_to_be32(TPM_ORD_GetTicks),
+    };
+    struct tpm_resp_hdr *resp;
+    fd_set readfds;
+    int n;
+    struct timeval tv = {
+        .tv_sec = 1,
+        .tv_usec = 0,
+    };
+    unsigned char buf[1024];
+
+    n = write(fd, &req, sizeof(req));
+    if (n < 0) {
+        return errno;
+    }
+    if (n != sizeof(req)) {
+        return EFAULT;
+    }
+
+    FD_ZERO(&readfds);
+    FD_SET(fd, &readfds);
+
+    /* wait for a second */
+    n = select(fd + 1, &readfds, NULL, NULL, &tv);
+    if (n != 1) {
+        return errno;
+    }
+
+    n = read(fd, &buf, sizeof(buf));
+    if (n < sizeof(struct tpm_resp_hdr)) {
+        return EFAULT;
+    }
+
+    resp = (struct tpm_resp_hdr *)buf;
+    /* check the header */
+    if (be16_to_cpu(resp->tag) != TPM_TAG_RSP_COMMAND ||
+        be32_to_cpu(resp->len) != n) {
+        return EBADMSG;
+    }
+
+    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;
+    }
+
+    tb->s.tpm_pt->tpm_dev = g_strdup(value);
+
+    tb->path = g_strdup(tb->s.tpm_pt->tpm_dev);
+
+    tb->s.tpm_pt->tpm_fd = qemu_open(tb->s.tpm_pt->tpm_dev, O_RDWR);
+    if (tb->s.tpm_pt->tpm_fd < 0) {
+        error_report("Cannot access TPM device using '%s': %s\n",
+                     tb->s.tpm_pt->tpm_dev, strerror(errno));
+        goto err_free_parameters;
+    }
+
+    if (tpm_passthrough_test_tpmdev(tb->s.tpm_pt->tpm_fd)) {
+        error_report("'%s' is not a TPM device.\n",
+                     tb->s.tpm_pt->tpm_dev);
+        goto err_close_tpmdev;
+    }
+
+    return 0;
+
+ err_close_tpmdev:
+    qemu_close(tb->s.tpm_pt->tpm_fd);
+    tb->s.tpm_pt->tpm_fd = -1;
+
+ err_free_parameters:
+    g_free(tb->path);
+    tb->path = NULL;
+
+    g_free(tb->s.tpm_pt->tpm_dev);
+    tb->s.tpm_pt->tpm_dev = NULL;
+
+    return 1;
+}
+
+static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
+{
+    TPMBackend *tb;
+
+    tb = g_new0(TPMBackend, 1);
+    tb->s.tpm_pt = g_new0(TPMPassthruState, 1);
+    tb->id = g_strdup(id);
+    /* let frontend set the fe_model to proper value */
+    tb->fe_model = -1;
+
+    tb->ops = &tpm_passthrough_driver;
+
+    if (tpm_passthrough_handle_device_opts(opts, tb)) {
+        goto err_exit;
+    }
+
+    tb->s.tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tb);
+    if (tb->s.tpm_pt->cancel_fd < 0) {
+        goto err_exit;
+    }
+
+    return tb;
+
+err_exit:
+    g_free(tb->id);
+    g_free(tb->s.tpm_pt);
+    g_free(tb);
+
+    return NULL;
+}
+
+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);
+}
+
+const TPMDriverOps tpm_passthrough_driver = {
+    .type                     = TPM_TYPE_PASSTHROUGH,
+    .desc                     = tpm_passthrough_create_desc,
+    .create                   = tpm_passthrough_create,
+    .destroy                  = tpm_passthrough_destroy,
+    .init                     = tpm_passthrough_init,
+    .startup_tpm              = tpm_passthrough_startup_tpm,
+    .realloc_buffer           = tpm_passthrough_realloc_buffer,
+    .reset                    = tpm_passthrough_reset,
+    .had_startup_error        = tpm_passthrough_get_startup_error,
+    .deliver_request          = tpm_passthrough_deliver_request,
+    .cancel_cmd               = tpm_passthrough_cancel_cmd,
+    .get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag,
+};
+
+static void tpm_passthrough_register(void)
+{
+    tpm_register_driver(&tpm_passthrough_driver);
+}
+
+type_init(tpm_passthrough_register)
diff --git a/tpm/tpm_tis.c b/tpm/tpm_tis.c
new file mode 100644 (file)
index 0000000..e93825e
--- /dev/null
@@ -0,0 +1,929 @@
+/*
+ * tpm_tis.c - QEMU's TPM TIS interface emulator
+ *
+ * Copyright (C) 2006,2010-2013 IBM Corporation
+ *
+ * Authors:
+ *  Stefan Berger <stefanb@us.ibm.com>
+ *  David Safford <safford@us.ibm.com>
+ *
+ * Xen 4 support: Andrease Niederl <andreas.niederl@iaik.tugraz.at>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * Implementation of the TIS interface according to specs found at
+ * http://www.trustedcomputinggroup.org. This implementation currently
+ * supports version 1.21, revision 1.0.
+ * In the developers menu choose the PC Client section then find the TIS
+ * specification.
+ */
+
+#include "tpm_int.h"
+#include "block/block.h"
+#include "exec/address-spaces.h"
+#include "hw/hw.h"
+#include "hw/pc.h"
+#include "hw/pci/pci_ids.h"
+#include "tpm/tpm_tis.h"
+#include "qemu-common.h"
+
+/*#define DEBUG_TIS */
+
+#ifdef DEBUG_TIS
+#define DPRINTF(fmt, ...) \
+    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+    do { } while (0)
+#endif
+
+/* whether the STS interrupt is supported */
+#define RAISE_STS_IRQ
+
+/* tis registers */
+#define TPM_TIS_REG_ACCESS                0x00
+#define TPM_TIS_REG_INT_ENABLE            0x08
+#define TPM_TIS_REG_INT_VECTOR            0x0c
+#define TPM_TIS_REG_INT_STATUS            0x10
+#define TPM_TIS_REG_INTF_CAPABILITY       0x14
+#define TPM_TIS_REG_STS                   0x18
+#define TPM_TIS_REG_DATA_FIFO             0x24
+#define TPM_TIS_REG_DID_VID               0xf00
+#define TPM_TIS_REG_RID                   0xf04
+
+/* vendor-specific registers */
+#define TPM_TIS_REG_DEBUG                 0xf90
+
+#define TPM_TIS_STS_VALID                 (1 << 7)
+#define TPM_TIS_STS_COMMAND_READY         (1 << 6)
+#define TPM_TIS_STS_TPM_GO                (1 << 5)
+#define TPM_TIS_STS_DATA_AVAILABLE        (1 << 4)
+#define TPM_TIS_STS_EXPECT                (1 << 3)
+#define TPM_TIS_STS_RESPONSE_RETRY        (1 << 1)
+
+#define TPM_TIS_BURST_COUNT_SHIFT         8
+#define TPM_TIS_BURST_COUNT(X) \
+    ((X) << TPM_TIS_BURST_COUNT_SHIFT)
+
+#define TPM_TIS_ACCESS_TPM_REG_VALID_STS  (1 << 7)
+#define TPM_TIS_ACCESS_ACTIVE_LOCALITY    (1 << 5)
+#define TPM_TIS_ACCESS_BEEN_SEIZED        (1 << 4)
+#define TPM_TIS_ACCESS_SEIZE              (1 << 3)
+#define TPM_TIS_ACCESS_PENDING_REQUEST    (1 << 2)
+#define TPM_TIS_ACCESS_REQUEST_USE        (1 << 1)
+#define TPM_TIS_ACCESS_TPM_ESTABLISHMENT  (1 << 0)
+
+#define TPM_TIS_INT_ENABLED               (1 << 31)
+#define TPM_TIS_INT_DATA_AVAILABLE        (1 << 0)
+#define TPM_TIS_INT_STS_VALID             (1 << 1)
+#define TPM_TIS_INT_LOCALITY_CHANGED      (1 << 2)
+#define TPM_TIS_INT_COMMAND_READY         (1 << 7)
+
+#define TPM_TIS_INT_POLARITY_MASK         (3 << 3)
+#define TPM_TIS_INT_POLARITY_LOW_LEVEL    (1 << 3)
+
+#ifndef RAISE_STS_IRQ
+
+#define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \
+                                      TPM_TIS_INT_DATA_AVAILABLE   | \
+                                      TPM_TIS_INT_COMMAND_READY)
+
+#else
+
+#define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \
+                                      TPM_TIS_INT_DATA_AVAILABLE   | \
+                                      TPM_TIS_INT_STS_VALID | \
+                                      TPM_TIS_INT_COMMAND_READY)
+
+#endif
+
+#define TPM_TIS_CAP_INTERRUPT_LOW_LEVEL  (1 << 4) /* support is mandatory */
+#define TPM_TIS_CAPABILITIES_SUPPORTED   (TPM_TIS_CAP_INTERRUPT_LOW_LEVEL | \
+                                          TPM_TIS_INTERRUPTS_SUPPORTED)
+
+#define TPM_TIS_TPM_DID       0x0001
+#define TPM_TIS_TPM_VID       PCI_VENDOR_ID_IBM
+#define TPM_TIS_TPM_RID       0x0001
+
+#define TPM_TIS_NO_DATA_BYTE  0xff
+
+/* local prototypes */
+
+static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr,
+                                  unsigned size);
+
+/* utility functions */
+
+static uint8_t tpm_tis_locality_from_addr(hwaddr addr)
+{
+    return (uint8_t)((addr >> TPM_TIS_LOCALITY_SHIFT) & 0x7);
+}
+
+static uint32_t tpm_tis_get_size_from_buffer(const TPMSizedBuffer *sb)
+{
+    return be32_to_cpu(*(uint32_t *)&sb->buffer[2]);
+}
+
+static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string)
+{
+#ifdef DEBUG_TIS
+    uint32_t len, i;
+
+    len = tpm_tis_get_size_from_buffer(sb);
+    DPRINTF("tpm_tis: %s length = %d\n", string, len);
+    for (i = 0; i < len; i++) {
+        if (i && !(i % 16)) {
+            DPRINTF("\n");
+        }
+        DPRINTF("%.2X ", sb->buffer[i]);
+    }
+    DPRINTF("\n");
+#endif
+}
+
+/*
+ * Send a request to the TPM.
+ */
+static void tpm_tis_tpm_send(TPMState *s, uint8_t locty)
+{
+    TPMTISEmuState *tis = &s->s.tis;
+
+    tpm_tis_show_buffer(&tis->loc[locty].w_buffer, "tpm_tis: To TPM");
+
+    s->locty_number = locty;
+    s->locty_data = &tis->loc[locty];
+
+    /*
+     * w_offset serves as length indicator for length of data;
+     * it's reset when the response comes back
+     */
+    tis->loc[locty].state = TPM_TIS_STATE_EXECUTION;
+
+    s->be_driver->ops->deliver_request(s->be_driver);
+}
+
+/* raise an interrupt if allowed */
+static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask)
+{
+    TPMTISEmuState *tis = &s->s.tis;
+
+    if (!TPM_TIS_IS_VALID_LOCTY(locty)) {
+        return;
+    }
+
+    if ((tis->loc[locty].inte & TPM_TIS_INT_ENABLED) &&
+        (tis->loc[locty].inte & irqmask)) {
+        DPRINTF("tpm_tis: Raising IRQ for flag %08x\n", irqmask);
+        qemu_irq_raise(s->s.tis.irq);
+        tis->loc[locty].ints |= irqmask;
+    }
+}
+
+static uint32_t tpm_tis_check_request_use_except(TPMState *s, uint8_t locty)
+{
+    uint8_t l;
+
+    for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
+        if (l == locty) {
+            continue;
+        }
+        if ((s->s.tis.loc[l].access & TPM_TIS_ACCESS_REQUEST_USE)) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty)
+{
+    TPMTISEmuState *tis = &s->s.tis;
+    bool change = (s->s.tis.active_locty != new_active_locty);
+    bool is_seize;
+    uint8_t mask;
+
+    if (change && TPM_TIS_IS_VALID_LOCTY(s->s.tis.active_locty)) {
+        is_seize = TPM_TIS_IS_VALID_LOCTY(new_active_locty) &&
+                   tis->loc[new_active_locty].access & TPM_TIS_ACCESS_SEIZE;
+
+        if (is_seize) {
+            mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY);
+        } else {
+            mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY|
+                     TPM_TIS_ACCESS_REQUEST_USE);
+        }
+        /* reset flags on the old active locality */
+        tis->loc[s->s.tis.active_locty].access &= mask;
+
+        if (is_seize) {
+            tis->loc[tis->active_locty].access |= TPM_TIS_ACCESS_BEEN_SEIZED;
+        }
+    }
+
+    tis->active_locty = new_active_locty;
+
+    DPRINTF("tpm_tis: Active locality is now %d\n", s->s.tis.active_locty);
+
+    if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) {
+        /* set flags on the new active locality */
+        tis->loc[new_active_locty].access |= TPM_TIS_ACCESS_ACTIVE_LOCALITY;
+        tis->loc[new_active_locty].access &= ~(TPM_TIS_ACCESS_REQUEST_USE |
+                                               TPM_TIS_ACCESS_SEIZE);
+    }
+
+    if (change) {
+        tpm_tis_raise_irq(s, tis->active_locty, TPM_TIS_INT_LOCALITY_CHANGED);
+    }
+}
+
+/* abort -- this function switches the locality */
+static void tpm_tis_abort(TPMState *s, uint8_t locty)
+{
+    TPMTISEmuState *tis = &s->s.tis;
+
+    tis->loc[locty].r_offset = 0;
+    tis->loc[locty].w_offset = 0;
+
+    DPRINTF("tpm_tis: tis_abort: new active locality is %d\n", tis->next_locty);
+
+    /*
+     * Need to react differently depending on who's aborting now and
+     * which locality will become active afterwards.
+     */
+    if (tis->aborting_locty == tis->next_locty) {
+        tis->loc[tis->aborting_locty].state = TPM_TIS_STATE_READY;
+        tis->loc[tis->aborting_locty].sts = TPM_TIS_STS_COMMAND_READY;
+        tpm_tis_raise_irq(s, tis->aborting_locty, TPM_TIS_INT_COMMAND_READY);
+    }
+
+    /* locality after abort is another one than the current one */
+    tpm_tis_new_active_locality(s, tis->next_locty);
+
+    tis->next_locty = TPM_TIS_NO_LOCALITY;
+    /* nobody's aborting a command anymore */
+    tis->aborting_locty = TPM_TIS_NO_LOCALITY;
+}
+
+/* prepare aborting current command */
+static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty)
+{
+    TPMTISEmuState *tis = &s->s.tis;
+    uint8_t busy_locty;
+
+    tis->aborting_locty = locty;
+    tis->next_locty = newlocty;  /* locality after successful abort */
+
+    /*
+     * only abort a command using an interrupt if currently executing
+     * a command AND if there's a valid connection to the vTPM.
+     */
+    for (busy_locty = 0; busy_locty < TPM_TIS_NUM_LOCALITIES; busy_locty++) {
+        if (tis->loc[busy_locty].state == TPM_TIS_STATE_EXECUTION) {
+            /*
+             * request the backend to cancel. Some backends may not
+             * support it
+             */
+            s->be_driver->ops->cancel_cmd(s->be_driver);
+            return;
+        }
+    }
+
+    tpm_tis_abort(s, locty);
+}
+
+static void tpm_tis_receive_bh(void *opaque)
+{
+    TPMState *s = opaque;
+    TPMTISEmuState *tis = &s->s.tis;
+    uint8_t locty = s->locty_number;
+
+    tis->loc[locty].sts = TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE;
+    tis->loc[locty].state = TPM_TIS_STATE_COMPLETION;
+    tis->loc[locty].r_offset = 0;
+    tis->loc[locty].w_offset = 0;
+
+    if (TPM_TIS_IS_VALID_LOCTY(tis->next_locty)) {
+        tpm_tis_abort(s, locty);
+    }
+
+#ifndef RAISE_STS_IRQ
+    tpm_tis_raise_irq(s, locty, TPM_TIS_INT_DATA_AVAILABLE);
+#else
+    tpm_tis_raise_irq(s, locty,
+                      TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID);
+#endif
+}
+
+/*
+ * Callback from the TPM to indicate that the response was received.
+ */
+static void tpm_tis_receive_cb(TPMState *s, uint8_t locty)
+{
+    TPMTISEmuState *tis = &s->s.tis;
+
+    assert(s->locty_number == locty);
+
+    qemu_bh_schedule(tis->bh);
+}
+
+/*
+ * Read a byte of response data
+ */
+static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty)
+{
+    TPMTISEmuState *tis = &s->s.tis;
+    uint32_t ret = TPM_TIS_NO_DATA_BYTE;
+    uint16_t len;
+
+    if ((tis->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
+        len = tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer);
+
+        ret = tis->loc[locty].r_buffer.buffer[tis->loc[locty].r_offset++];
+        if (tis->loc[locty].r_offset >= len) {
+            /* got last byte */
+            tis->loc[locty].sts = TPM_TIS_STS_VALID;
+#ifdef RAISE_STS_IRQ
+            tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
+#endif
+        }
+        DPRINTF("tpm_tis: tpm_tis_data_read byte 0x%02x   [%d]\n",
+                ret, tis->loc[locty].r_offset-1);
+    }
+
+    return ret;
+}
+
+#ifdef DEBUG_TIS
+static void tpm_tis_dump_state(void *opaque, hwaddr addr)
+{
+    static const unsigned regs[] = {
+        TPM_TIS_REG_ACCESS,
+        TPM_TIS_REG_INT_ENABLE,
+        TPM_TIS_REG_INT_VECTOR,
+        TPM_TIS_REG_INT_STATUS,
+        TPM_TIS_REG_INTF_CAPABILITY,
+        TPM_TIS_REG_STS,
+        TPM_TIS_REG_DID_VID,
+        TPM_TIS_REG_RID,
+        0xfff};
+    int idx;
+    uint8_t locty = tpm_tis_locality_from_addr(addr);
+    hwaddr base = addr & ~0xfff;
+    TPMState *s = opaque;
+    TPMTISEmuState *tis = &s->s.tis;
+
+    DPRINTF("tpm_tis: active locality      : %d\n"
+            "tpm_tis: state of locality %d : %d\n"
+            "tpm_tis: register dump:\n",
+            tis->active_locty,
+            locty, tis->loc[locty].state);
+
+    for (idx = 0; regs[idx] != 0xfff; idx++) {
+        DPRINTF("tpm_tis: 0x%04x : 0x%08x\n", regs[idx],
+                (uint32_t)tpm_tis_mmio_read(opaque, base + regs[idx], 4));
+    }
+
+    DPRINTF("tpm_tis: read offset   : %d\n"
+            "tpm_tis: result buffer : ",
+            tis->loc[locty].r_offset);
+    for (idx = 0;
+         idx < tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer);
+         idx++) {
+        DPRINTF("%c%02x%s",
+                tis->loc[locty].r_offset == idx ? '>' : ' ',
+                tis->loc[locty].r_buffer.buffer[idx],
+                ((idx & 0xf) == 0xf) ? "\ntpm_tis:                 " : "");
+    }
+    DPRINTF("\n"
+            "tpm_tis: write offset  : %d\n"
+            "tpm_tis: request buffer: ",
+            tis->loc[locty].w_offset);
+    for (idx = 0;
+         idx < tpm_tis_get_size_from_buffer(&tis->loc[locty].w_buffer);
+         idx++) {
+        DPRINTF("%c%02x%s",
+                tis->loc[locty].w_offset == idx ? '>' : ' ',
+                tis->loc[locty].w_buffer.buffer[idx],
+                ((idx & 0xf) == 0xf) ? "\ntpm_tis:                 " : "");
+    }
+    DPRINTF("\n");
+}
+#endif
+
+/*
+ * Read a register of the TIS interface
+ * See specs pages 33-63 for description of the registers
+ */
+static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr,
+                                  unsigned size)
+{
+    TPMState *s = opaque;
+    TPMTISEmuState *tis = &s->s.tis;
+    uint16_t offset = addr & 0xffc;
+    uint8_t shift = (addr & 0x3) * 8;
+    uint32_t val = 0xffffffff;
+    uint8_t locty = tpm_tis_locality_from_addr(addr);
+    uint32_t avail;
+
+    if (s->be_driver->ops->had_startup_error(s->be_driver)) {
+        return val;
+    }
+
+    switch (offset) {
+    case TPM_TIS_REG_ACCESS:
+        /* never show the SEIZE flag even though we use it internally */
+        val = tis->loc[locty].access & ~TPM_TIS_ACCESS_SEIZE;
+        /* the pending flag is always calculated */
+        if (tpm_tis_check_request_use_except(s, locty)) {
+            val |= TPM_TIS_ACCESS_PENDING_REQUEST;
+        }
+        val |= !s->be_driver->ops->get_tpm_established_flag(s->be_driver);
+        break;
+    case TPM_TIS_REG_INT_ENABLE:
+        val = tis->loc[locty].inte;
+        break;
+    case TPM_TIS_REG_INT_VECTOR:
+        val = tis->irq_num;
+        break;
+    case TPM_TIS_REG_INT_STATUS:
+        val = tis->loc[locty].ints;
+        break;
+    case TPM_TIS_REG_INTF_CAPABILITY:
+        val = TPM_TIS_CAPABILITIES_SUPPORTED;
+        break;
+    case TPM_TIS_REG_STS:
+        if (tis->active_locty == locty) {
+            if ((tis->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
+                val = TPM_TIS_BURST_COUNT(
+                       tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer)
+                       - tis->loc[locty].r_offset) | tis->loc[locty].sts;
+            } else {
+                avail = tis->loc[locty].w_buffer.size
+                        - tis->loc[locty].w_offset;
+                /*
+                 * byte-sized reads should not return 0x00 for 0x100
+                 * available bytes.
+                 */
+                if (size == 1 && avail > 0xff) {
+                    avail = 0xff;
+                }
+                val = TPM_TIS_BURST_COUNT(avail) | tis->loc[locty].sts;
+            }
+        }
+        break;
+    case TPM_TIS_REG_DATA_FIFO:
+        if (tis->active_locty == locty) {
+            switch (tis->loc[locty].state) {
+            case TPM_TIS_STATE_COMPLETION:
+                val = tpm_tis_data_read(s, locty);
+                break;
+            default:
+                val = TPM_TIS_NO_DATA_BYTE;
+                break;
+            }
+        }
+        break;
+    case TPM_TIS_REG_DID_VID:
+        val = (TPM_TIS_TPM_DID << 16) | TPM_TIS_TPM_VID;
+        break;
+    case TPM_TIS_REG_RID:
+        val = TPM_TIS_TPM_RID;
+        break;
+#ifdef DEBUG_TIS
+    case TPM_TIS_REG_DEBUG:
+        tpm_tis_dump_state(opaque, addr);
+        break;
+#endif
+    }
+
+    if (shift) {
+        val >>= shift;
+    }
+
+    DPRINTF("tpm_tis:  read.%u(%08x) = %08x\n", size, (int)addr, (uint32_t)val);
+
+    return val;
+}
+
+/*
+ * Write a value to a register of the TIS interface
+ * See specs pages 33-63 for description of the registers
+ */
+static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr,
+                                      uint64_t val, unsigned size,
+                                      bool hw_access)
+{
+    TPMState *s = opaque;
+    TPMTISEmuState *tis = &s->s.tis;
+    uint16_t off = addr & 0xfff;
+    uint8_t locty = tpm_tis_locality_from_addr(addr);
+    uint8_t active_locty, l;
+    int c, set_new_locty = 1;
+    uint16_t len;
+
+    DPRINTF("tpm_tis: write.%u(%08x) = %08x\n", size, (int)addr, (uint32_t)val);
+
+    if (locty == 4 && !hw_access) {
+        DPRINTF("tpm_tis: Access to locality 4 only allowed from hardware\n");
+        return;
+    }
+
+    if (s->be_driver->ops->had_startup_error(s->be_driver)) {
+        return;
+    }
+
+    switch (off) {
+    case TPM_TIS_REG_ACCESS:
+
+        if ((val & TPM_TIS_ACCESS_SEIZE)) {
+            val &= ~(TPM_TIS_ACCESS_REQUEST_USE |
+                     TPM_TIS_ACCESS_ACTIVE_LOCALITY);
+        }
+
+        active_locty = tis->active_locty;
+
+        if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) {
+            /* give up locality if currently owned */
+            if (tis->active_locty == locty) {
+                DPRINTF("tpm_tis: Releasing locality %d\n", locty);
+
+                uint8_t newlocty = TPM_TIS_NO_LOCALITY;
+                /* anybody wants the locality ? */
+                for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) {
+                    if ((tis->loc[c].access & TPM_TIS_ACCESS_REQUEST_USE)) {
+                        DPRINTF("tpm_tis: Locality %d requests use.\n", c);
+                        newlocty = c;
+                        break;
+                    }
+                }
+                DPRINTF("tpm_tis: TPM_TIS_ACCESS_ACTIVE_LOCALITY: "
+                        "Next active locality: %d\n",
+                        newlocty);
+
+                if (TPM_TIS_IS_VALID_LOCTY(newlocty)) {
+                    set_new_locty = 0;
+                    tpm_tis_prep_abort(s, locty, newlocty);
+                } else {
+                    active_locty = TPM_TIS_NO_LOCALITY;
+                }
+            } else {
+                /* not currently the owner; clear a pending request */
+                tis->loc[locty].access &= ~TPM_TIS_ACCESS_REQUEST_USE;
+            }
+        }
+
+        if ((val & TPM_TIS_ACCESS_BEEN_SEIZED)) {
+            tis->loc[locty].access &= ~TPM_TIS_ACCESS_BEEN_SEIZED;
+        }
+
+        if ((val & TPM_TIS_ACCESS_SEIZE)) {
+            /*
+             * allow seize if a locality is active and the requesting
+             * locality is higher than the one that's active
+             * OR
+             * allow seize for requesting locality if no locality is
+             * active
+             */
+            while ((TPM_TIS_IS_VALID_LOCTY(tis->active_locty) &&
+                    locty > tis->active_locty) ||
+                    !TPM_TIS_IS_VALID_LOCTY(tis->active_locty)) {
+                bool higher_seize = FALSE;
+
+                /* already a pending SEIZE ? */
+                if ((tis->loc[locty].access & TPM_TIS_ACCESS_SEIZE)) {
+                    break;
+                }
+
+                /* check for ongoing seize by a higher locality */
+                for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES; l++) {
+                    if ((tis->loc[l].access & TPM_TIS_ACCESS_SEIZE)) {
+                        higher_seize = TRUE;
+                        break;
+                    }
+                }
+
+                if (higher_seize) {
+                    break;
+                }
+
+                /* cancel any seize by a lower locality */
+                for (l = 0; l < locty - 1; l++) {
+                    tis->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE;
+                }
+
+                tis->loc[locty].access |= TPM_TIS_ACCESS_SEIZE;
+                DPRINTF("tpm_tis: TPM_TIS_ACCESS_SEIZE: "
+                        "Locality %d seized from locality %d\n",
+                        locty, tis->active_locty);
+                DPRINTF("tpm_tis: TPM_TIS_ACCESS_SEIZE: Initiating abort.\n");
+                set_new_locty = 0;
+                tpm_tis_prep_abort(s, tis->active_locty, locty);
+                break;
+            }
+        }
+
+        if ((val & TPM_TIS_ACCESS_REQUEST_USE)) {
+            if (tis->active_locty != locty) {
+                if (TPM_TIS_IS_VALID_LOCTY(tis->active_locty)) {
+                    tis->loc[locty].access |= TPM_TIS_ACCESS_REQUEST_USE;
+                } else {
+                    /* no locality active -> make this one active now */
+                    active_locty = locty;
+                }
+            }
+        }
+
+        if (set_new_locty) {
+            tpm_tis_new_active_locality(s, active_locty);
+        }
+
+        break;
+    case TPM_TIS_REG_INT_ENABLE:
+        if (tis->active_locty != locty) {
+            break;
+        }
+
+        tis->loc[locty].inte = (val & (TPM_TIS_INT_ENABLED |
+                                       TPM_TIS_INT_POLARITY_MASK |
+                                       TPM_TIS_INTERRUPTS_SUPPORTED));
+        break;
+    case TPM_TIS_REG_INT_VECTOR:
+        /* hard wired -- ignore */
+        break;
+    case TPM_TIS_REG_INT_STATUS:
+        if (tis->active_locty != locty) {
+            break;
+        }
+
+        /* clearing of interrupt flags */
+        if (((val & TPM_TIS_INTERRUPTS_SUPPORTED)) &&
+            (tis->loc[locty].ints & TPM_TIS_INTERRUPTS_SUPPORTED)) {
+            tis->loc[locty].ints &= ~val;
+            if (tis->loc[locty].ints == 0) {
+                qemu_irq_lower(tis->irq);
+                DPRINTF("tpm_tis: Lowering IRQ\n");
+            }
+        }
+        tis->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED);
+        break;
+    case TPM_TIS_REG_STS:
+        if (tis->active_locty != locty) {
+            break;
+        }
+
+        val &= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO |
+                TPM_TIS_STS_RESPONSE_RETRY);
+
+        if (val == TPM_TIS_STS_COMMAND_READY) {
+            switch (tis->loc[locty].state) {
+
+            case TPM_TIS_STATE_READY:
+                tis->loc[locty].w_offset = 0;
+                tis->loc[locty].r_offset = 0;
+            break;
+
+            case TPM_TIS_STATE_IDLE:
+                tis->loc[locty].sts = TPM_TIS_STS_COMMAND_READY;
+                tis->loc[locty].state = TPM_TIS_STATE_READY;
+                tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
+            break;
+
+            case TPM_TIS_STATE_EXECUTION:
+            case TPM_TIS_STATE_RECEPTION:
+                /* abort currently running command */
+                DPRINTF("tpm_tis: %s: Initiating abort.\n",
+                        __func__);
+                tpm_tis_prep_abort(s, locty, locty);
+            break;
+
+            case TPM_TIS_STATE_COMPLETION:
+                tis->loc[locty].w_offset = 0;
+                tis->loc[locty].r_offset = 0;
+                /* shortcut to ready state with C/R set */
+                tis->loc[locty].state = TPM_TIS_STATE_READY;
+                if (!(tis->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) {
+                    tis->loc[locty].sts   = TPM_TIS_STS_COMMAND_READY;
+                    tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
+                }
+                tis->loc[locty].sts &= ~(TPM_TIS_STS_DATA_AVAILABLE);
+            break;
+
+            }
+        } else if (val == TPM_TIS_STS_TPM_GO) {
+            switch (tis->loc[locty].state) {
+            case TPM_TIS_STATE_RECEPTION:
+                if ((tis->loc[locty].sts & TPM_TIS_STS_EXPECT) == 0) {
+                    tpm_tis_tpm_send(s, locty);
+                }
+                break;
+            default:
+                /* ignore */
+                break;
+            }
+        } else if (val == TPM_TIS_STS_RESPONSE_RETRY) {
+            switch (tis->loc[locty].state) {
+            case TPM_TIS_STATE_COMPLETION:
+                tis->loc[locty].r_offset = 0;
+                tis->loc[locty].sts = TPM_TIS_STS_VALID |
+                                      TPM_TIS_STS_DATA_AVAILABLE;
+                break;
+            default:
+                /* ignore */
+                break;
+            }
+        }
+        break;
+    case TPM_TIS_REG_DATA_FIFO:
+        /* data fifo */
+        if (tis->active_locty != locty) {
+            break;
+        }
+
+        if (tis->loc[locty].state == TPM_TIS_STATE_IDLE ||
+            tis->loc[locty].state == TPM_TIS_STATE_EXECUTION ||
+            tis->loc[locty].state == TPM_TIS_STATE_COMPLETION) {
+            /* drop the byte */
+        } else {
+            DPRINTF("tpm_tis: Byte to send to TPM: %02x\n", (uint8_t)val);
+            if (tis->loc[locty].state == TPM_TIS_STATE_READY) {
+                tis->loc[locty].state = TPM_TIS_STATE_RECEPTION;
+                tis->loc[locty].sts = TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID;
+            }
+
+            if ((tis->loc[locty].sts & TPM_TIS_STS_EXPECT)) {
+                if (tis->loc[locty].w_offset < tis->loc[locty].w_buffer.size) {
+                    tis->loc[locty].w_buffer.
+                        buffer[tis->loc[locty].w_offset++] = (uint8_t)val;
+                } else {
+                    tis->loc[locty].sts = TPM_TIS_STS_VALID;
+                }
+            }
+
+            /* check for complete packet */
+            if (tis->loc[locty].w_offset > 5 &&
+                (tis->loc[locty].sts & TPM_TIS_STS_EXPECT)) {
+                /* we have a packet length - see if we have all of it */
+#ifdef RAISE_STS_IRQ
+                bool needIrq = !(tis->loc[locty].sts & TPM_TIS_STS_VALID);
+#endif
+                len = tpm_tis_get_size_from_buffer(&tis->loc[locty].w_buffer);
+                if (len > tis->loc[locty].w_offset) {
+                    tis->loc[locty].sts = TPM_TIS_STS_EXPECT |
+                                          TPM_TIS_STS_VALID;
+                } else {
+                    /* packet complete */
+                    tis->loc[locty].sts = TPM_TIS_STS_VALID;
+                }
+#ifdef RAISE_STS_IRQ
+                if (needIrq) {
+                    tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
+                }
+#endif
+            }
+        }
+        break;
+    }
+}
+
+static void tpm_tis_mmio_write(void *opaque, hwaddr addr,
+                               uint64_t val, unsigned size)
+{
+    return tpm_tis_mmio_write_intern(opaque, addr, val, size, false);
+}
+
+static const MemoryRegionOps tpm_tis_memory_ops = {
+    .read = tpm_tis_mmio_read,
+    .write = tpm_tis_mmio_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+};
+
+static int tpm_tis_do_startup_tpm(TPMState *s)
+{
+    return s->be_driver->ops->startup_tpm(s->be_driver);
+}
+
+/*
+ * This function is called when the machine starts, resets or due to
+ * S3 resume.
+ */
+static void tpm_tis_reset(DeviceState *dev)
+{
+    TPMState *s = TPM(dev);
+    TPMTISEmuState *tis = &s->s.tis;
+    int c;
+
+    s->be_driver->ops->reset(s->be_driver);
+
+    tis->active_locty = TPM_TIS_NO_LOCALITY;
+    tis->next_locty = TPM_TIS_NO_LOCALITY;
+    tis->aborting_locty = TPM_TIS_NO_LOCALITY;
+
+    for (c = 0; c < TPM_TIS_NUM_LOCALITIES; c++) {
+        tis->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS;
+        tis->loc[c].sts = 0;
+        tis->loc[c].inte = TPM_TIS_INT_POLARITY_LOW_LEVEL;
+        tis->loc[c].ints = 0;
+        tis->loc[c].state = TPM_TIS_STATE_IDLE;
+
+        tis->loc[c].w_offset = 0;
+        s->be_driver->ops->realloc_buffer(&tis->loc[c].w_buffer);
+        tis->loc[c].r_offset = 0;
+        s->be_driver->ops->realloc_buffer(&tis->loc[c].r_buffer);
+    }
+
+    tpm_tis_do_startup_tpm(s);
+}
+
+static const VMStateDescription vmstate_tpm_tis = {
+    .name = "tpm",
+    .unmigratable = 1,
+};
+
+static Property tpm_tis_properties[] = {
+    DEFINE_PROP_UINT32("irq", TPMState,
+                       s.tis.irq_num, TPM_TIS_IRQ),
+    DEFINE_PROP_STRING("tpmdev", TPMState, backend),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void tpm_tis_realizefn(DeviceState *dev, Error **errp)
+{
+    TPMState *s = TPM(dev);
+    TPMTISEmuState *tis = &s->s.tis;
+
+    s->be_driver = qemu_find_tpm(s->backend);
+    if (!s->be_driver) {
+        error_setg(errp, "tpm_tis: backend driver with id %s could not be "
+                   "found", s->backend);
+        return;
+    }
+
+    s->be_driver->fe_model = TPM_MODEL_TPM_TIS;
+
+    if (s->be_driver->ops->init(s->be_driver, s, tpm_tis_receive_cb)) {
+        error_setg(errp, "tpm_tis: backend driver with id %s could not be "
+                   "initialized", s->backend);
+        return;
+    }
+
+    if (tis->irq_num > 15) {
+        error_setg(errp, "tpm_tis: IRQ %d for TPM TIS is outside valid range "
+                   "of 0 to 15.\n", tis->irq_num);
+        return;
+    }
+
+    tis->bh = qemu_bh_new(tpm_tis_receive_bh, s);
+
+    isa_init_irq(&s->busdev, &tis->irq, tis->irq_num);
+}
+
+static void tpm_tis_initfn(Object *obj)
+{
+    ISADevice *dev = ISA_DEVICE(obj);
+    TPMState *s = TPM(obj);
+
+    memory_region_init_io(&s->mmio, &tpm_tis_memory_ops, s, "tpm-tis-mmio",
+                          TPM_TIS_NUM_LOCALITIES << TPM_TIS_LOCALITY_SHIFT);
+    memory_region_add_subregion(isa_address_space(dev), TPM_TIS_ADDR_BASE,
+                                &s->mmio);
+}
+
+static void tpm_tis_uninitfn(Object *obj)
+{
+    TPMState *s = TPM(obj);
+
+    memory_region_del_subregion(get_system_memory(), &s->mmio);
+    memory_region_destroy(&s->mmio);
+}
+
+static void tpm_tis_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = tpm_tis_realizefn;
+    dc->props = tpm_tis_properties;
+    dc->reset = tpm_tis_reset;
+    dc->vmsd  = &vmstate_tpm_tis;
+}
+
+static const TypeInfo tpm_tis_info = {
+    .name = TYPE_TPM_TIS,
+    .parent = TYPE_ISA_DEVICE,
+    .instance_size = sizeof(TPMState),
+    .instance_init = tpm_tis_initfn,
+    .instance_finalize = tpm_tis_uninitfn,
+    .class_init  = tpm_tis_class_init,
+};
+
+static void tpm_tis_register(void)
+{
+    type_register_static(&tpm_tis_info);
+    tpm_register_model(TPM_MODEL_TPM_TIS);
+}
+
+type_init(tpm_tis_register)
diff --git a/tpm/tpm_tis.h b/tpm/tpm_tis.h
new file mode 100644 (file)
index 0000000..0c8df80
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * tpm_tis.h - QEMU's TPM TIS interface emulator
+ *
+ * Copyright (C) 2006, 2010-2013 IBM Corporation
+ *
+ * Authors:
+ *  Stefan Berger <stefanb@us.ibm.com>
+ *  David Safford <safford@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * Implementation of the TIS interface according to specs found at
+ * http://www.trustedcomputinggroup.org
+ *
+ */
+#ifndef TPM_TPM_TIS_H
+#define TPM_TPM_TIS_H
+
+#include "hw/isa.h"
+#include "qemu-common.h"
+
+#define TPM_TIS_ADDR_BASE           0xFED40000
+
+#define TPM_TIS_NUM_LOCALITIES      5     /* per spec */
+#define TPM_TIS_LOCALITY_SHIFT      12
+#define TPM_TIS_NO_LOCALITY         0xff
+
+#define TPM_TIS_IS_VALID_LOCTY(x)   ((x) < TPM_TIS_NUM_LOCALITIES)
+
+#define TPM_TIS_IRQ                 5
+
+#define TPM_TIS_BUFFER_MAX          4096
+
+#define TYPE_TPM_TIS                "tpm-tis"
+
+
+typedef struct TPMSizedBuffer {
+    uint32_t size;
+    uint8_t  *buffer;
+} TPMSizedBuffer;
+
+typedef enum {
+    TPM_TIS_STATE_IDLE = 0,
+    TPM_TIS_STATE_READY,
+    TPM_TIS_STATE_COMPLETION,
+    TPM_TIS_STATE_EXECUTION,
+    TPM_TIS_STATE_RECEPTION,
+} TPMTISState;
+
+/* locality data  -- all fields are persisted */
+typedef struct TPMLocality {
+    TPMTISState state;
+    uint8_t access;
+    uint8_t sts;
+    uint32_t inte;
+    uint32_t ints;
+
+    uint16_t w_offset;
+    uint16_t r_offset;
+    TPMSizedBuffer w_buffer;
+    TPMSizedBuffer r_buffer;
+} TPMLocality;
+
+typedef struct TPMTISEmuState {
+    QEMUBH *bh;
+    uint32_t offset;
+    uint8_t buf[TPM_TIS_BUFFER_MAX];
+
+    uint8_t active_locty;
+    uint8_t aborting_locty;
+    uint8_t next_locty;
+
+    TPMLocality loc[TPM_TIS_NUM_LOCALITIES];
+
+    qemu_irq irq;
+    uint32_t irq_num;
+} TPMTISEmuState;
+
+#endif /* TPM_TPM_TIS_H */
index 8389d83568b541e3c0e5124ad6a1fc84b8215602..d6a847d18aa512b0b78ea75cdcea5341c1742a76 100644 (file)
@@ -473,6 +473,7 @@ scsi_request_sense(int target, int lun, int tag) "target %d lun %d tag %d"
 
 # vl.c
 vm_state_notify(int running, int reason) "running %d reason %d"
+load_file(const char *name, const char *path) "name %s location %s"
 
 # block/qcow2.c
 qcow2_writev_start_req(void *co, int64_t sector, int nb_sectors) "co %p sector %" PRIx64 " nb_sectors %d"
index 83a6fa3969aec6fda8d260a318f50fbfcf265c5b..0f9617779740ba22f424c31f0db869eb8218935e 100644 (file)
@@ -1742,7 +1742,7 @@ PixelFormat qemu_default_pixelformat(int bpp)
 
 static void register_types(void)
 {
-    register_char_driver("vc", text_console_init);
+    register_char_driver("vc", vc_init);
 }
 
 type_init(register_types);
diff --git a/vl.c b/vl.c
index 154f7bae6a691c4a217936343b59e434179dcbe6..a621aec0a42114236b09819072dd41dbc286eeb5 100644 (file)
--- a/vl.c
+++ b/vl.c
@@ -139,6 +139,7 @@ int main(int argc, char **argv)
 #include "sysemu/blockdev.h"
 #include "hw/block-common.h"
 #include "migration/block.h"
+#include "tpm/tpm.h"
 #include "sysemu/dma.h"
 #include "audio/audio.h"
 #include "migration/migration.h"
@@ -178,7 +179,8 @@ int main(int argc, char **argv)
 #define MAX_VIRTIO_CONSOLES 1
 #define MAX_SCLP_CONSOLES 1
 
-static const char *data_dir;
+static const char *data_dir[16];
+static int data_dir_idx;
 const char *bios_name = NULL;
 enum vga_retrace_method vga_retrace_method = VGA_RETRACE_DUMB;
 DisplayType display_type = DT_DEFAULT;
@@ -491,6 +493,30 @@ static QemuOptsList qemu_object_opts = {
     },
 };
 
+static QemuOptsList qemu_tpmdev_opts = {
+    .name = "tpmdev",
+    .implied_opt_name = "type",
+    .head = QTAILQ_HEAD_INITIALIZER(qemu_tpmdev_opts.head),
+    .desc = {
+        {
+            .name = "type",
+            .type = QEMU_OPT_STRING,
+            .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",
+        },
+        { /* end of list */ }
+    },
+};
+
 const char *qemu_get_vm_name(void)
 {
     return qemu_name;
@@ -2251,14 +2277,16 @@ static int balloon_parse(const char *arg)
 
 char *qemu_find_file(int type, const char *name)
 {
-    int len;
+    int i;
     const char *subdir;
     char *buf;
 
     /* Try the name as a straight path first */
     if (access(name, R_OK) == 0) {
+        trace_load_file(name, name);
         return g_strdup(name);
     }
+
     switch (type) {
     case QEMU_FILE_TYPE_BIOS:
         subdir = "";
@@ -2269,14 +2297,16 @@ char *qemu_find_file(int type, const char *name)
     default:
         abort();
     }
-    len = strlen(data_dir) + strlen(name) + strlen(subdir) + 2;
-    buf = g_malloc0(len);
-    snprintf(buf, len, "%s/%s%s", data_dir, subdir, name);
-    if (access(buf, R_OK)) {
+
+    for (i = 0; i < data_dir_idx; i++) {
+        buf = g_strdup_printf("%s/%s%s", data_dir[i], subdir, name);
+        if (access(buf, R_OK) == 0) {
+            trace_load_file(name, buf);
+            return buf;
+        }
         g_free(buf);
-        return NULL;
     }
-    return buf;
+    return NULL;
 }
 
 static int device_help_func(QemuOpts *opts, void *opaque)
@@ -2868,6 +2898,7 @@ int main(int argc, char **argv, char **envp)
     qemu_add_opts(&qemu_sandbox_opts);
     qemu_add_opts(&qemu_add_fd_opts);
     qemu_add_opts(&qemu_object_opts);
+    qemu_add_opts(&qemu_tpmdev_opts);
 
     runstate_init();
 
@@ -3231,6 +3262,13 @@ int main(int argc, char **argv, char **envp)
                 }
                 break;
             }
+#ifdef CONFIG_TPM
+            case QEMU_OPTION_tpmdev:
+                if (tpm_config_parse(qemu_find_opts("tpmdev"), optarg) < 0) {
+                    exit(1);
+                }
+                break;
+#endif
             case QEMU_OPTION_mempath:
                 mem_path = optarg;
                 break;
@@ -3252,7 +3290,9 @@ int main(int argc, char **argv, char **envp)
                 add_device_config(DEV_GDB, optarg);
                 break;
             case QEMU_OPTION_L:
-                data_dir = optarg;
+                if (data_dir_idx < ARRAY_SIZE(data_dir)) {
+                    data_dir[data_dir_idx++] = optarg;
+                }
                 break;
             case QEMU_OPTION_bios:
                 bios_name = optarg;
@@ -3892,12 +3932,15 @@ int main(int argc, char **argv, char **envp)
 
     /* If no data_dir is specified then try to find it relative to the
        executable path.  */
-    if (!data_dir) {
-        data_dir = os_find_datadir(argv[0]);
+    if (data_dir_idx < ARRAY_SIZE(data_dir)) {
+        data_dir[data_dir_idx] = os_find_datadir(argv[0]);
+        if (data_dir[data_dir_idx] != NULL) {
+            data_dir_idx++;
+        }
     }
     /* If all else fails use the install path specified when building. */
-    if (!data_dir) {
-        data_dir = CONFIG_QEMU_DATADIR;
+    if (data_dir_idx < ARRAY_SIZE(data_dir)) {
+        data_dir[data_dir_idx++] = CONFIG_QEMU_DATADIR;
     }
 
     /*
@@ -4108,6 +4151,12 @@ int main(int argc, char **argv, char **envp)
         exit(1);
     }
 
+#ifdef CONFIG_TPM
+    if (tpm_init() < 0) {
+        exit(1);
+    }
+#endif
+
     /* init the bluetooth world */
     if (foreach_device_config(DEV_BT, bt_parse))
         exit(1);
@@ -4353,6 +4402,9 @@ int main(int argc, char **argv, char **envp)
     bdrv_close_all();
     pause_all_vcpus();
     res_free();
+#ifdef CONFIG_TPM
+    tpm_cleanup();
+#endif
 
     return 0;
 }