@@ -400,6 +400,8 @@ $(trace-obj-y): $(GENERATED_HEADERS)
######################################################################
# backdoor
+backdoor-nested-$(CONFIG_USER_ONLY) += user.o
+
backdoor-obj-y += $(addprefix backdoor/qemu/, $(backdoor-nested-y))
ifdef CONFIG_BACKDOOR
new file mode 100644
@@ -0,0 +1,202 @@
+/*
+ * QEMU-side management of backdoor channels in user-level emulation.
+ *
+ * Copyright (C) 2011 Lluís Vilanova <vilanova@ac.upc.edu>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "backdoor/qemu/user.h"
+
+#include <string.h>
+#include <sys/mman.h>
+
+#include "qemu-common.h"
+#include "backdoor/qemu/qemu-backdoor.h"
+
+
+static char *data_path = NULL;
+static char *control_path = NULL;
+static int data_fd = -1;
+static int control_fd = -1;
+
+static void *data = NULL;
+static void *qemu_control_0 = NULL;
+static void *qemu_control_1 = NULL;
+
+static struct stat control_fd_stat;
+
+struct sigaction segv_next;
+static void segv_handler(int signum, siginfo_t *siginfo, void *sigctxt);
+
+
+static void init_channel(const char *base, const char *suffix, size_t size,
+ char ** path, int *fd, void **addr)
+{
+ *path = g_malloc(strlen(base) + strlen(suffix) + 1);
+ sprintf(*path, "%s%s", base, suffix);
+
+ *fd = open(*path, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
+ if (*fd == -1) {
+ fprintf(stderr, "error: open(%s): %s\n", *path, strerror(errno));
+ abort();
+ }
+
+ off_t lres = lseek(*fd, size - 1, SEEK_SET);
+ if (lres == (off_t)-1) {
+ fprintf(stderr, "error: lseek(%s): %s\n", *path, strerror(errno));
+ abort();
+ }
+
+ char tmp;
+ ssize_t wres = write(*fd, &tmp, 1);
+ if (wres == -1) {
+ fprintf(stderr, "error: write(%s): %s\n", *path, strerror(errno));
+ abort();
+ }
+
+ if (addr) {
+ *addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0);
+ if (*addr == MAP_FAILED) {
+ fprintf(stderr, "error: mmap(%s): %s\n", *path, strerror(errno));
+ abort();
+ }
+ }
+}
+
+void backdoor_init(const char *base, uint64_t data_size)
+{
+ if (base == NULL) {
+ return;
+ }
+
+ init_channel(base, "-data", data_size, &data_path, &data_fd, &data);
+ void *control;
+ init_channel(base, "-control", getpagesize() * 2, &control_path, &control_fd, &control);
+
+ /* store channel size in first 64bits (target endianess); command goes into
+ * 2nd slot
+ */
+ *(uint64_t*)control = tswap64(data_size);
+
+ if (fstat(control_fd, &control_fd_stat) == -1) {
+ fprintf(stderr, "error: fstat(backdoor_control): %s\n", strerror(errno));
+ abort();
+ }
+
+ struct sigaction segv;
+ memset(&segv, 0, sizeof(segv));
+ segv.sa_sigaction = segv_handler;
+ segv.sa_flags = SA_SIGINFO | SA_RESTART;
+ sigemptyset(&segv.sa_mask);
+
+ if (sigaction(SIGSEGV, &segv, &segv_next) != 0) {
+ fprintf(stderr, "error: sigaction(SIGSEGV): %s\n", strerror(errno));
+ abort();
+ }
+
+ qemu_backdoor_init(data_size);
+}
+
+
+static void fini_channel(int *fd, char **path)
+{
+ if (*fd != -1) {
+ if (close(*fd) == -1) {
+ fprintf(stderr, "error: close: %s\n", strerror(errno));
+ abort();
+ }
+ if (unlink(*path) == -1) {
+ fprintf(stderr, "error: unlink(%s): %s\n", *path, strerror(errno));
+ abort();
+ }
+ *fd = -1;
+ }
+ if (*path != NULL) {
+ g_free(path);
+ *path = NULL;
+ }
+}
+
+void backdoor_fini(void)
+{
+ static bool atexit_in = false;
+ if (atexit_in) {
+ return;
+ }
+ atexit_in = true;
+
+ if (sigaction(SIGSEGV, &segv_next, NULL) != 0) {
+ fprintf(stderr, "error: sigaction(SIGSEGV): %s\n", strerror(errno));
+ abort();
+ }
+ fini_channel(&data_fd, &data_path);
+ fini_channel(&control_fd, &control_path);
+}
+
+
+void backdoor_guest_mmap(int fd, void *qemu_addr)
+{
+ struct stat s;
+ if (fstat(fd, &s) != 0) {
+ return;
+ }
+
+ if (s.st_dev != control_fd_stat.st_dev ||
+ s.st_ino != control_fd_stat.st_ino) {
+ return;
+ }
+
+ /* it's an mmap of the control channel; split it in two and mprotect it to
+ * detect writes (cmd is written once on each part)
+ */
+ qemu_control_0 = qemu_addr;
+ qemu_control_1 = qemu_control_0 + getpagesize();
+ if (mprotect(qemu_control_0, getpagesize(), PROT_READ) == -1) {
+ fprintf(stderr, "error: mprotect(backdoor_control): %s\n",
+ strerror(errno));
+ abort();
+ }
+}
+
+static void swap_control(void *from, void *to)
+{
+ if (mprotect(from, getpagesize(), PROT_READ | PROT_WRITE) == -1) {
+ fprintf(stderr, "error: mprotect(from): %s\n",
+ strerror(errno));
+ abort();
+ }
+ if (mprotect(to, getpagesize(), PROT_READ) == -1) {
+ fprintf(stderr, "error: mprotect(to): %s\n",
+ strerror(errno));
+ abort();
+ }
+}
+
+static void segv_handler(int signum, siginfo_t *siginfo, void *sigctxt)
+{
+ if (qemu_control_0 <= siginfo->si_addr &&
+ siginfo->si_addr < qemu_control_1) {
+
+ /* 1st fault (guest will write cmd) */
+ assert(((target_ulong)siginfo->si_addr % getpagesize()) == sizeof(uint64_t));
+ swap_control(qemu_control_0, qemu_control_1);
+
+ } else if (qemu_control_1 <= siginfo->si_addr &&
+ siginfo->si_addr < qemu_control_1 + getpagesize()) {
+
+ /* 2nd fault (invoke) */
+ assert(((target_ulong)siginfo->si_addr % getpagesize()) == sizeof(uint64_t));
+ qemu_backdoor(((uint64_t*)qemu_control_0)[1], data);
+ swap_control(qemu_control_1, qemu_control_0);
+
+ } else {
+ /* proxy to next handler */
+ if (segv_next.sa_sigaction != NULL) {
+ segv_next.sa_sigaction(signum, siginfo, sigctxt);
+ } else if (segv_next.sa_handler != NULL) {
+ segv_next.sa_handler(signum);
+ }
+ }
+}
new file mode 100644
@@ -0,0 +1,30 @@
+/*
+ * QEMU-side management of backdoor channels in user-level emulation.
+ *
+ * Copyright (C) 2011 Lluís Vilanova <vilanova@ac.upc.edu>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+
+
+/**
+ * Initialize the backing files for the backdoor channel.
+ *
+ * @param base Base path for the backdoor channel files.
+ * @param data_size Length in bytes for the data channel.
+ */
+void backdoor_init(const char *base, uint64_t data_size);
+
+/**
+ * Check if this mmap is for the control channel and act accordingly.
+ */
+void backdoor_guest_mmap(int fd, void *qemu_addr);
+
+/**
+ * Remove the backing files for the backdoor channel.
+ */
+void backdoor_fini(void);
@@ -34,6 +34,10 @@
#include "qemu-timer.h"
#include "envlist.h"
+#if defined(CONFIG_BACKDOOR)
+#include "backdoor/qemu/user.h"
+#endif
+
#define DEBUG_LOGFILE "/tmp/qemu.log"
int singlestep;
@@ -688,6 +692,11 @@ static void usage(void)
"-B address set guest_base address to address\n"
#endif
"-bsd type select emulated BSD type FreeBSD/NetBSD/OpenBSD (default)\n"
+#if defined(CONFIG_BACKDOOR)
+ "-backdoor path base path to backdoor channel\n"
+ "-backdoor-pages value\n"
+ " number of pages for the backdoor data channel (default: 1)\n"
+#endif
"\n"
"Debug options:\n"
"-d options activate log (default logfile=%s)\n"
@@ -744,6 +753,10 @@ int main(int argc, char **argv)
char **target_environ, **wrk;
envlist_t *envlist = NULL;
bsd_type = target_openbsd;
+#if defined(CONFIG_BACKDOOR)
+ char *backdoor_base = NULL;
+ uint64_t backdoor_pages = 1;
+#endif
if (argc <= 1)
usage();
@@ -851,6 +864,12 @@ int main(int argc, char **argv)
singlestep = 1;
} else if (!strcmp(r, "strace")) {
do_strace = 1;
+#if defined(CONFIG_BACKDOOR)
+ } else if (!strcmp(r, "backdoor")) {
+ backdoor_base = argv[optind++];
+ } else if (!strcmp(r, "backdoor-pages")) {
+ backdoor_pages = atoi(argv[optind++]);
+#endif
} else
{
usage();
@@ -988,6 +1007,13 @@ int main(int argc, char **argv)
target_set_brk(info->brk);
syscall_init();
signal_init();
+#if defined(CONFIG_BACKDOOR)
+ if (atexit(backdoor_fini) != 0) {
+ fprintf(stderr, "error: atexit: %s\n", strerror(errno));
+ abort();
+ }
+ backdoor_init(backdoor_base, backdoor_pages * getpagesize());
+#endif
#if defined(CONFIG_USE_GUEST_BASE)
/* Now that we've loaded the binary, GUEST_BASE is fixed. Delay
@@ -28,6 +28,10 @@
#include "qemu-common.h"
#include "bsd-mman.h"
+#if defined(CONFIG_BACKDOOR)
+#include "backdoor/qemu/user.h"
+#endif
+
//#define DEBUG_MMAP
#if defined(CONFIG_USE_NPTL)
@@ -473,6 +477,9 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
}
}
the_end1:
+#if defined(CONFIG_BACKDOOR)
+ backdoor_guest_mmap(fd, (void *)g2h(start));
+#endif
page_set_flags(start, start + len, prot | PAGE_VALID);
the_end:
#ifdef DEBUG_MMAP
@@ -36,6 +36,10 @@
#include "qemu.h"
#include "qemu-common.h"
+#if defined(CONFIG_BACKDOOR)
+#include "backdoor/qemu/user.h"
+#endif
+
//#define DEBUG
static abi_ulong target_brk;
@@ -334,6 +338,9 @@ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
_mcleanup();
#endif
gdb_exit(cpu_env, arg1);
+#if defined(CONFIG_BACKDOOR)
+ backdoor_fini();
+#endif
/* XXX: should free thread stack and CPU env */
_exit(arg1);
ret = 0; /* avoid warning */
@@ -429,6 +436,9 @@ abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1,
_mcleanup();
#endif
gdb_exit(cpu_env, arg1);
+#if defined(CONFIG_BACKDOOR)
+ backdoor_fini();
+#endif
/* XXX: should free thread stack and CPU env */
_exit(arg1);
ret = 0; /* avoid warning */
@@ -501,6 +511,9 @@ abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1,
_mcleanup();
#endif
gdb_exit(cpu_env, arg1);
+#if defined(CONFIG_BACKDOOR)
+ backdoor_fini();
+#endif
/* XXX: should free thread stack and CPU env */
_exit(arg1);
ret = 0; /* avoid warning */
@@ -3373,6 +3373,7 @@ symlink $source_path/Makefile.target $target_dir/Makefile
if test -n "$backdoor"; then
mkdir -p $target_dir/libbackdoor
symlink $backdoor/Makefile $target_dir/libbackdoor/Makefile
+ mkdir -p $target_dir/backdoor/qemu
fi
@@ -28,6 +28,9 @@
#include <sys/mman.h>
#include "qemu.h"
+#if defined(CONFIG_BACKDOOR)
+#include "backdoor/qemu/user.h"
+#endif
#define DEBUG_LOGFILE "/tmp/qemu.log"
@@ -713,6 +716,12 @@ static void usage(void)
"-h print this help\n"
"-L path set the %s library path (default='%s')\n"
"-s size set the stack size in bytes (default=%ld)\n"
+#if defined(CONFIG_BACKDOOR)
+ "-backdoor path\n"
+ " base path to backdoor channel\n"
+ "-backdoor-pages value\n"
+ " number of pages for the backdoor data channel (default: 1)\n"
+#endif
"\n"
"debug options:\n"
"-d options activate log (logfile='%s')\n"
@@ -745,6 +754,10 @@ int main(int argc, char **argv)
short use_gdbstub = 0;
const char *r;
const char *cpu_model;
+#if defined(CONFIG_BACKDOOR)
+ char *backdoor_base = NULL;
+ uint64_t backdoor_pages = 1;
+#endif
if (argc <= 1)
usage();
@@ -802,6 +815,12 @@ int main(int argc, char **argv)
}
} else if (!strcmp(r, "singlestep")) {
singlestep = 1;
+#if defined(CONFIG_BACKDOOR)
+ } else if (!strcmp(r, "backdoor")) {
+ backdoor_base = argv[optind++];
+ } else if (!strcmp(r, "backdoor-pages")) {
+ backdoor_pages = atoi(argv[optind++]);
+#endif
} else
{
usage();
@@ -868,6 +887,13 @@ int main(int argc, char **argv)
syscall_init();
signal_init();
+#if defined(CONFIG_BACKDOOR)
+ if (atexit(backdoor_fini) != 0) {
+ fprintf(stderr, "error: atexit: %s\n", strerror(errno));
+ abort();
+ }
+ backdoor_init(backdoor_base, backdoor_pages * getpagesize());
+#endif
global_env = env;
/* build Task State */
@@ -26,6 +26,10 @@
#include "qemu.h"
+#if defined(CONFIG_BACKDOOR)
+#include "backdoor/qemu/user.h"
+#endif
+
//#define DEBUG_MMAP
/* NOTE: all the constants are the HOST ones */
@@ -308,6 +312,9 @@ long target_mmap(unsigned long start, unsigned long len, int prot,
return ret;
}
the_end1:
+#if defined(CONFIG_BACKDOOR)
+ backdoor_guest_mmap(fd, (void *)g2h(start));
+#endif
page_set_flags(start, start + len, prot | PAGE_VALID);
the_end:
#ifdef DEBUG_MMAP
@@ -33,6 +33,9 @@
#include "tcg.h"
#include "qemu-timer.h"
#include "envlist.h"
+#if defined(CONFIG_BACKDOOR)
+#include "backdoor/qemu/user.h"
+#endif
#define DEBUG_LOGFILE "/tmp/qemu.log"
@@ -50,6 +53,10 @@ unsigned long guest_base;
int have_guest_base;
unsigned long reserved_va;
#endif
+#if defined(CONFIG_BACKDOOR)
+const char *backdoor_base = NULL;
+uint64_t backdoor_pages = 1;
+#endif
static void usage(void);
@@ -3082,6 +3089,18 @@ static void handle_arg_strace(const char *arg)
do_strace = 1;
}
+#if defined(CONFIG_BACKDOOR)
+static void handle_arg_backdoor(const char *arg)
+{
+ backdoor_base = arg;
+}
+
+static void handle_arg_backdoor_pages(const char *arg)
+{
+ backdoor_pages = atoi(arg);
+}
+#endif
+
static void handle_arg_version(const char *arg)
{
printf("qemu-" TARGET_ARCH " version " QEMU_VERSION QEMU_PKGVERSION
@@ -3123,6 +3142,12 @@ struct qemu_argument arg_table[] = {
{"R", "QEMU_RESERVED_VA", true, handle_arg_reserved_va,
"size", "reserve 'size' bytes for guest virtual address space"},
#endif
+#if defined(CONFIG_BACKDOOR)
+ {"backdoor", "QEMU_BACKDOOR", true, handle_arg_backdoor,
+ "path", "base path to backdoor channel"},
+ {"backdoor-pages", "QEMU_BACKDOOR_PAGES", true, handle_arg_backdoor_pages,
+ "num", "number of pages for the backdoor data channel (default: 1)"},
+#endif
{"d", "QEMU_LOG", true, handle_arg_log,
"options", "activate log"},
{"p", "QEMU_PAGESIZE", true, handle_arg_pagesize,
@@ -3510,6 +3535,13 @@ int main(int argc, char **argv, char **envp)
target_set_brk(info->brk);
syscall_init();
signal_init();
+#if defined(CONFIG_BACKDOOR)
+ if (atexit(backdoor_fini)) {
+ fprintf(stderr, "error: atexit: %s\n", strerror(errno));
+ abort();
+ }
+ backdoor_init(backdoor_base, backdoor_pages * getpagesize());
+#endif
#if defined(CONFIG_USE_GUEST_BASE)
/* Now that we've loaded the binary, GUEST_BASE is fixed. Delay
@@ -31,6 +31,10 @@
#include "qemu.h"
#include "qemu-common.h"
+#if defined(CONFIG_BACKDOOR)
+#include "backdoor/qemu/user.h"
+#endif
+
//#define DEBUG_MMAP
#if defined(CONFIG_USE_NPTL)
@@ -553,6 +557,9 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
}
}
the_end1:
+#if defined(CONFIG_BACKDOOR)
+ backdoor_guest_mmap(fd, (void *)g2h(start));
+#endif
page_set_flags(start, start + len, prot | PAGE_VALID);
the_end:
#ifdef DEBUG_MMAP
@@ -100,6 +100,10 @@ int __clone2(int (*fn)(void *), void *child_stack_base,
#include "qemu.h"
+#if defined(CONFIG_BACKDOOR)
+#include "backdoor/qemu/user.h"
+#endif
+
#if defined(CONFIG_USE_NPTL)
#define CLONE_NPTL_FLAGS2 (CLONE_SETTLS | \
CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)
@@ -4663,6 +4667,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
_mcleanup();
#endif
gdb_exit(cpu_env, arg1);
+#if defined(CONFIG_BACKDOOR)
+ backdoor_fini();
+#endif
_exit(arg1);
ret = 0; /* avoid warning */
break;
@@ -6397,6 +6404,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
_mcleanup();
#endif
gdb_exit(cpu_env, arg1);
+#if defined(CONFIG_BACKDOOR)
+ backdoor_fini();
+#endif
ret = get_errno(exit_group(arg1));
break;
#endif
QEMU detects when the guest uses 'mmap' on the control channel file, and then uses 'mprotect' to detect accesses to it, which are redirected to the user-provided "libbackdoor.a". Signed-off-by: Lluís Vilanova <vilanova@ac.upc.edu> --- Makefile.objs | 2 backdoor/qemu/user.c | 202 ++++++++++++++++++++++++++++++++++++++++++++++++++ backdoor/qemu/user.h | 30 +++++++ bsd-user/main.c | 26 ++++++ bsd-user/mmap.c | 7 ++ bsd-user/syscall.c | 13 +++ configure | 1 darwin-user/main.c | 26 ++++++ darwin-user/mmap.c | 7 ++ linux-user/main.c | 32 ++++++++ linux-user/mmap.c | 7 ++ linux-user/syscall.c | 10 ++ 12 files changed, 363 insertions(+), 0 deletions(-) create mode 100644 backdoor/qemu/user.c create mode 100644 backdoor/qemu/user.h