diff --git a/exec.c b/exec.c
index d6e5d3c..8b8bea5 100644
--- a/exec.c
+++ b/exec.c
@@ -41,6 +41,9 @@
 #include "kvm.h"
 #if defined(CONFIG_USER_ONLY)
 #include <qemu.h>
+#ifdef TARGET_X86_64
+#include "vsyscall.h"
+#endif
 #endif
 
 //#define DEBUG_TB_INVALIDATE
@@ -908,6 +911,13 @@ TranslationBlock *tb_gen_code(CPUState *env,
     cpu_gen_code(env, tb, &code_gen_size);
     code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1));
 
+#if defined(CONFIG_USER_ONLY) && defined(TARGET_X86_64)
+    /* if we are doing vsyscall don't link the page as it lies in high memory
+       and tb_alloc_page will abort due to page_l1_map returning NULL */
+    if (unlikely(phys_pc >= TARGET_VSYSCALL_START
+                 && phys_pc < TARGET_VSYSCALL_END))
+        return tb;
+#endif
     /* check next page if needed */
     virt_page2 = (pc + tb->size - 1) & TARGET_PAGE_MASK;
     phys_page2 = -1;
diff --git a/linux-user/main.c b/linux-user/main.c
index e331d65..761f1cc 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -30,6 +30,9 @@
 #include "qemu.h"
 #include "qemu-common.h"
 #include "cache-utils.h"
+#if defined(CONFIG_USER_ONLY) && defined(TARGET_X86_64)
+#include "vsyscall.h"
+#endif
 /* For tb_lock */
 #include "exec-all.h"
 
@@ -327,6 +330,9 @@ void cpu_loop(CPUX86State *env)
     int trapnr;
     abi_ulong pc;
     target_siginfo_t info;
+#ifdef TARGET_X86_64
+    int syscall_num;
+#endif
 
     for(;;) {
         trapnr = cpu_x86_exec(env);
@@ -356,6 +362,37 @@ void cpu_loop(CPUX86State *env)
             env->eip = env->exception_next_eip;
             break;
 #endif
+#ifdef TARGET_X86_64
+        case EXCP_VSYSCALL:
+            switch (env->eip) {
+            case TARGET_VSYSCALL_ADDR(__NR_vgettimeofday):
+                syscall_num = __NR_gettimeofday;
+                break;
+            case TARGET_VSYSCALL_ADDR(__NR_vtime):
+                syscall_num = __NR_time;
+                break;
+            case TARGET_VSYSCALL_ADDR(__NR_vgetcpu):
+                /* XXX: not yet implemented */
+                cpu_abort(env, "Unimplemented vsyscall vgetcpu");
+                break;
+            default:
+                cpu_abort(env,
+                          "Invalid vsyscall to address " TARGET_FMT_lx "\n",
+                          env->eip);
+            }
+            env->regs[R_EAX] = do_syscall(env,
+                                          syscall_num,
+                                          env->regs[R_EDI],
+                                          env->regs[R_ESI],
+                                          env->regs[R_EDX],
+                                          env->regs[10],
+                                          env->regs[8],
+                                          env->regs[9]);
+            /* simulate a ret */
+            env->eip = ldq(env->regs[R_ESP]);
+            env->regs[R_ESP] += 8;
+            break;
+#endif
         case EXCP0B_NOSEG:
         case EXCP0C_STACK:
             info.si_signo = SIGBUS;
diff --git a/linux-user/x86_64/vsyscall.h b/linux-user/x86_64/vsyscall.h
new file mode 100644
index 0000000..11dcc07
--- /dev/null
+++ b/linux-user/x86_64/vsyscall.h
@@ -0,0 +1,19 @@
+#ifndef VSYSCALL_H
+#define VSYSCALL_H
+
+/* This is based on asm/syscall.h in kernel 2.6.29. */
+
+enum vsyscall_num {
+    __NR_vgettimeofday,
+    __NR_vtime,
+    __NR_vgetcpu,
+};
+
+#define TARGET_VSYSCALL_START (-10UL << 20)
+#define TARGET_VSYSCALL_SIZE 1024
+#define TARGET_VSYSCALL_END (-2UL << 20)
+#define TARGET_VSYSCALL_MAPPED_PAGES 1
+#define TARGET_VSYSCALL_ADDR(vsyscall_nr) \
+    (TARGET_VSYSCALL_START+TARGET_VSYSCALL_SIZE*(vsyscall_nr))
+
+#endif /* !VSYSCALL_H */
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index 6f7478a..a4281ae 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -437,6 +437,8 @@
 
 #define EXCP_SYSCALL    0x100 /* only happens in user only emulation
                                  for syscall instruction */
+#define EXCP_VSYSCALL   0x101 /* only happens in user only emulation
+                                 on x86_64 */
 
 enum {
     CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */
diff --git a/target-i386/helper.h b/target-i386/helper.h
index 68d57b1..3d39d7b 100644
--- a/target-i386/helper.h
+++ b/target-i386/helper.h
@@ -57,6 +57,7 @@ DEF_HELPER_1(sysexit, void, int)
 #ifdef TARGET_X86_64
 DEF_HELPER_1(syscall, void, int)
 DEF_HELPER_1(sysret, void, int)
+DEF_HELPER_0(vsyscall, void)
 #endif
 DEF_HELPER_1(hlt, void, int)
 DEF_HELPER_1(monitor, void, tl)
diff --git a/target-i386/op_helper.c b/target-i386/op_helper.c
index ed22c7a..6b558bf 100644
--- a/target-i386/op_helper.c
+++ b/target-i386/op_helper.c
@@ -1004,6 +1004,12 @@ void helper_syscall(int next_eip_addend)
     env->exception_next_eip = env->eip + next_eip_addend;
     cpu_loop_exit();
 }
+
+void helper_vsyscall(void)
+{
+    env->exception_index = EXCP_VSYSCALL;
+    cpu_loop_exit();
+}
 #else
 void helper_syscall(int next_eip_addend)
 {
diff --git a/target-i386/translate.c b/target-i386/translate.c
index 4726009..d6daef4 100644
--- a/target-i386/translate.c
+++ b/target-i386/translate.c
@@ -33,6 +33,10 @@
 #define GEN_HELPER 1
 #include "helper.h"
 
+#if defined(CONFIG_USER_ONLY) && defined(TARGET_X86_64)
+#include "vsyscall.h"
+#endif
+
 #define PREFIX_REPZ   0x01
 #define PREFIX_REPNZ  0x02
 #define PREFIX_LOCK   0x04
@@ -7688,6 +7692,15 @@ static inline void gen_intermediate_code_internal(CPUState *env,
 
     gen_icount_start();
     for(;;) {
+#if defined(CONFIG_USER_ONLY) && defined(TARGET_X86_64)
+        /* Detect vsyscall's */
+        if (unlikely(pc_ptr >= TARGET_VSYSCALL_START
+                     && pc_ptr < TARGET_VSYSCALL_END)) {
+            gen_helper_vsyscall();
+            break;
+        }
+#endif
+
         if (unlikely(!TAILQ_EMPTY(&env->breakpoints))) {
             TAILQ_FOREACH(bp, &env->breakpoints, entry) {
                 if (bp->pc == pc_ptr &&
