aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.objs1
-rwxr-xr-xconfigure28
-rw-r--r--default-configs/i386-softmmu.mak2
-rw-r--r--default-configs/x86_64-softmmu.mak2
-rw-r--r--exec.c13
-rw-r--r--hmp-commands.hx2
-rw-r--r--hmp.c44
-rw-r--r--hmp.h1
-rw-r--r--include/qemu/sockets.h1
-rw-r--r--include/tpm/tpm.h21
-rw-r--r--linux-user/main.c24
-rw-r--r--linux-user/socket.h69
-rw-r--r--linux-user/sparc/syscall_nr.h2
-rw-r--r--linux-user/strace.c103
-rw-r--r--linux-user/syscall.c134
-rw-r--r--monitor.c8
-rw-r--r--qapi-schema.json104
-rw-r--r--qemu-char.c24
-rw-r--r--qemu-options.hx74
-rw-r--r--qga/channel-win32.c2
-rw-r--r--qga/commands-posix.c268
-rw-r--r--qga/commands-win32.c23
-rw-r--r--qga/guest-agent-core.h1
-rw-r--r--qga/main.c185
-rw-r--r--qga/qapi-schema.json111
-rw-r--r--qga/service-win32.c2
-rw-r--r--qmp-commands.hx18
-rw-r--r--tpm/Makefile.objs6
-rw-r--r--tpm/tpm.c357
-rw-r--r--tpm/tpm_backend.c58
-rw-r--r--tpm/tpm_backend.h45
-rw-r--r--tpm/tpm_int.h116
-rw-r--r--tpm/tpm_passthrough.c530
-rw-r--r--tpm/tpm_tis.c929
-rw-r--r--tpm/tpm_tis.h80
-rw-r--r--trace-events1
-rw-r--r--ui/console.c2
-rw-r--r--vl.c78
38 files changed, 3399 insertions, 70 deletions
diff --git a/Makefile.objs b/Makefile.objs
index 8c90b92d01..f99841ce54 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -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/
diff --git a/configure b/configure
index 84317c6826..46a75947cd 100755
--- 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
diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
index 1b23025a98..df9e126c1f 100644
--- a/default-configs/i386-softmmu.mak
+++ b/default-configs/i386-softmmu.mak
@@ -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/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
index 3392f5abd6..ab3cd5fc35 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -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 c5e65a9380..8a6aac36e3 100644
--- 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) {
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 69c707d332..4bda3fea0e 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -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 2f47a8a9dd..b0a861cfbb 100644
--- 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 30b3c20ca4..95fe76e218 100644
--- 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);
diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h
index 6125bf7bdf..c5cee4bf2f 100644
--- a/include/qemu/sockets.h
+++ b/include/qemu/sockets.h
@@ -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
index 0000000000..cc8f20e69e
--- /dev/null
+++ b/include/tpm/tpm.h
@@ -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 */
diff --git a/linux-user/main.c b/linux-user/main.c
index d8b0cd65fa..4e92a0b4c5 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -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);
}
diff --git a/linux-user/socket.h b/linux-user/socket.h
index 93d47823d8..339cae5a16 100644
--- a/linux-user/socket.h
+++ b/linux-user/socket.h
@@ -87,6 +87,75 @@
#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) */
diff --git a/linux-user/sparc/syscall_nr.h b/linux-user/sparc/syscall_nr.h
index 061711cc03..534e6e9963 100644
--- a/linux-user/sparc/syscall_nr.h
+++ b/linux-user/sparc/syscall_nr.h
@@ -200,6 +200,8 @@
#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
diff --git a/linux-user/strace.c b/linux-user/strace.c
index 4e91a6eb9c..0fbae3c2f6 100644
--- a/linux-user/strace.c
+++ b/linux-user/strace.c
@@ -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
@@ -1437,6 +1498,12 @@ if( cmd == val ) { \
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)
print_op(FUTEX_FD)
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 19630eaf20..ee82a2da4e 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -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)
diff --git a/monitor.c b/monitor.c
index ab05c49abb..112e92064d 100644
--- 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"
@@ -2719,6 +2720,13 @@ static mon_cmd_t info_cmds[] = {
.mhandler.cmd = do_trace_print_events,
},
{
+ .name = "tpm",
+ .args_type = "",
+ .params = "",
+ .help = "show the TPM device",
+ .mhandler.cmd = hmp_info_tpm,
+ },
+ {
.name = NULL,
},
};
diff --git a/qapi-schema.json b/qapi-schema.json
index 28b070f16b..4494e53693 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3240,3 +3240,107 @@
# 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'] }
diff --git a/qemu-char.c b/qemu-char.c
index 04aa589c7e..83787c74c4 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -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
diff --git a/qemu-options.hx b/qemu-options.hx
index cd76f2a00c..30fb85d619 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -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
diff --git a/qga/channel-win32.c b/qga/channel-win32.c
index 16bf44a376..7ed98d72fb 100644
--- a/qga/channel-win32.c
+++ b/qga/channel-win32.c
@@ -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;
}
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 7a0202eb2a..d7da850615 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -15,6 +15,10 @@
#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)
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 7e8ecb3b40..b19be9db48 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -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)
{
diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
index 3354598362..624a559d94 100644
--- a/qga/guest-agent-core.h
+++ b/qga/guest-agent-core.h
@@ -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);
diff --git a/qga/main.c b/qga/main.c
index db281a508b..99346e15aa 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -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>
@@ -53,6 +55,11 @@
#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 {
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index d91d903256..dac4e6f95f 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -83,6 +83,45 @@
{ '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:
#
# Information about guest agent commands.
@@ -515,3 +554,75 @@
##
{ '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' }
diff --git a/qga/service-win32.c b/qga/service-win32.c
index 09054565d3..843398a6c6 100644
--- a/qga/service-win32.c
+++ b/qga/service-win32.c
@@ -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;
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 95022e259f..b370060848 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -2716,6 +2716,24 @@ EQMP
},
{
+ .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",
.mhandler.cmd_new = qmp_marshal_input_chardev_add,
diff --git a/tpm/Makefile.objs b/tpm/Makefile.objs
new file mode 100644
index 0000000000..86768244e4
--- /dev/null
+++ b/tpm/Makefile.objs
@@ -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
index 0000000000..ffd24956d7
--- /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
index 0000000000..4144ef7d76
--- /dev/null
+++ b/tpm/tpm_backend.c
@@ -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
index 0000000000..05d94d0f5b
--- /dev/null
+++ b/tpm/tpm_backend.h
@@ -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
index 0000000000..f7056436cc
--- /dev/null
+++ b/tpm/tpm_int.h
@@ -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
index 0000000000..24aff4d69c
--- /dev/null
+++ b/tpm/tpm_passthrough.c
@@ -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
index 0000000000..e93825eec1
--- /dev/null
+++ b/tpm/tpm_tis.c
@@ -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
index 0000000000..0c8df80cce
--- /dev/null
+++ b/tpm/tpm_tis.h
@@ -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 */
diff --git a/trace-events b/trace-events
index 8389d83568..d6a847d18a 100644
--- a/trace-events
+++ b/trace-events
@@ -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"
diff --git a/ui/console.c b/ui/console.c
index 83a6fa3969..0f96177797 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -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 154f7bae6a..a621aec0a4 100644
--- 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;
}