diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index 93b09cd..c5ba7c3 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -30,8 +30,7 @@
 
 #include "softfloat.h"
 
-#define NB_MMU_MODES 2 // guess
-#define MMU_USER_IDX 0 // guess
+#define NB_MMU_MODES 2
 
 typedef union FPReg {
     struct {
@@ -77,12 +76,28 @@ static inline void cpu_clone_regs(CPUState *env, target_ulong newsp)
 }
 #endif
 
+#define MMU_MODE0_SUFFIX _kernel
+#define MMU_MODE1_SUFFIX _user
+#define MMU_USER_IDX 1
+static inline int cpu_mmu_index (CPUState *env)
+{
+    /* XXX: Currently we don't implement virtual memory */
+    return 0;
+}
+
+
 CPUS390XState *cpu_s390x_init(const char *cpu_model);
 void s390x_translate_init(void);
 int cpu_s390x_exec(CPUS390XState *s);
 void cpu_s390x_close(CPUS390XState *s);
 void do_interrupt (CPUState *env);
 
+#ifndef CONFIG_USER_ONLY
+extern int s390_virtio_hypercall(CPUState *env);
+extern void kvm_s390_virtio_irq(CPUState *env, int config_change, uint64_t token);
+extern CPUState *s390_cpu_addr2state(uint16_t cpu_addr);
+#endif
+
 /* you can call this signal handler from your SIGBUS and SIGSEGV
    signal handlers to inform the virtual CPU of exceptions. non zero
    is returned if the signal was handled by the virtual CPU.  */
@@ -129,4 +144,71 @@ static inline void cpu_get_tb_cpu_state(CPUState* env, target_ulong *pc,
     *cs_base = 0;
     *flags = env->psw.mask; // guess
 }
+
+/* Program Status Word.  */
+#define S390_PSWM_REGNUM 0
+#define S390_PSWA_REGNUM 1
+/* General Purpose Registers.  */
+#define S390_R0_REGNUM 2
+#define S390_R1_REGNUM 3
+#define S390_R2_REGNUM 4
+#define S390_R3_REGNUM 5
+#define S390_R4_REGNUM 6
+#define S390_R5_REGNUM 7
+#define S390_R6_REGNUM 8
+#define S390_R7_REGNUM 9
+#define S390_R8_REGNUM 10
+#define S390_R9_REGNUM 11
+#define S390_R10_REGNUM 12
+#define S390_R11_REGNUM 13
+#define S390_R12_REGNUM 14
+#define S390_R13_REGNUM 15
+#define S390_R14_REGNUM 16
+#define S390_R15_REGNUM 17
+/* Access Registers.  */
+#define S390_A0_REGNUM 18
+#define S390_A1_REGNUM 19
+#define S390_A2_REGNUM 20
+#define S390_A3_REGNUM 21
+#define S390_A4_REGNUM 22
+#define S390_A5_REGNUM 23
+#define S390_A6_REGNUM 24
+#define S390_A7_REGNUM 25
+#define S390_A8_REGNUM 26
+#define S390_A9_REGNUM 27
+#define S390_A10_REGNUM 28
+#define S390_A11_REGNUM 29
+#define S390_A12_REGNUM 30
+#define S390_A13_REGNUM 31
+#define S390_A14_REGNUM 32
+#define S390_A15_REGNUM 33
+/* Floating Point Control Word.  */
+#define S390_FPC_REGNUM 34
+/* Floating Point Registers.  */
+#define S390_F0_REGNUM 35
+#define S390_F1_REGNUM 36
+#define S390_F2_REGNUM 37
+#define S390_F3_REGNUM 38
+#define S390_F4_REGNUM 39
+#define S390_F5_REGNUM 40
+#define S390_F6_REGNUM 41
+#define S390_F7_REGNUM 42
+#define S390_F8_REGNUM 43
+#define S390_F9_REGNUM 44
+#define S390_F10_REGNUM 45
+#define S390_F11_REGNUM 46
+#define S390_F12_REGNUM 47
+#define S390_F13_REGNUM 48
+#define S390_F14_REGNUM 49
+#define S390_F15_REGNUM 50
+/* Total.  */
+#define S390_NUM_REGS 51
+
+/* Pseudo registers -- PC and condition code.  */
+#define S390_PC_REGNUM S390_NUM_REGS
+#define S390_CC_REGNUM (S390_NUM_REGS+1)
+#define S390_NUM_PSEUDO_REGS 2
+#define S390_NUM_TOTAL_REGS (S390_NUM_REGS+2)
+
+
 #endif
diff --git a/target-s390x/exec.h b/target-s390x/exec.h
index 5198359..13dc7dd 100644
--- a/target-s390x/exec.h
+++ b/target-s390x/exec.h
@@ -22,9 +22,14 @@
 
 register struct CPUS390XState *env asm(AREG0);
 
+#include "config.h"
 #include "cpu.h"
 #include "exec-all.h"
 
+#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+#endif /* !defined(CONFIG_USER_ONLY) */
+
 static inline int cpu_has_work(CPUState *env)
 {
     return env->interrupt_request & CPU_INTERRUPT_HARD; // guess
diff --git a/target-s390x/helper.c b/target-s390x/helper.c
index 5407c62..d98d603 100644
--- a/target-s390x/helper.c
+++ b/target-s390x/helper.c
@@ -27,6 +27,9 @@
 #include "gdbstub.h"
 #include "qemu-common.h"
 
+#include <linux/kvm.h>
+#include "kvm.h"
+
 CPUS390XState *cpu_s390x_init(const char *cpu_model)
 {
     CPUS390XState *env;
@@ -61,13 +64,13 @@ int cpu_s390x_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
     return 1;
 }
 
+#endif /* CONFIG_USER_ONLY */
+
 target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
 {
     return addr;
 }
 
-#endif /* CONFIG_USER_ONLY */
-
 void cpu_reset(CPUS390XState *env)
 {
     if (qemu_loglevel_mask(CPU_LOG_RESET)) {
@@ -79,3 +82,22 @@ void cpu_reset(CPUS390XState *env)
     /* FIXME: reset vector? */
     tlb_flush(env, 1);
 }
+
+#ifndef CONFIG_USER_ONLY
+
+int cpu_s390x_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+                                int mmu_idx, int is_softmmu)
+{
+    target_ulong phys;
+    int prot;
+
+    /* XXX: implement mmu */
+
+    phys = address;
+    prot = PAGE_READ | PAGE_WRITE;
+
+    return tlb_set_page(env, address & TARGET_PAGE_MASK,
+                        phys & TARGET_PAGE_MASK, prot,
+                        mmu_idx, is_softmmu);
+}
+#endif /* CONFIG_USER_ONLY */
diff --git a/target-s390x/machine.c b/target-s390x/machine.c
new file mode 100644
index 0000000..3e79be6
--- /dev/null
+++ b/target-s390x/machine.c
@@ -0,0 +1,30 @@
+/*
+ * QEMU S390x machine definitions
+ *
+ * Copyright (c) 2009 Alexander Graf <agraf@suse.de>
+ *
+ * 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 "hw/hw.h"
+#include "hw/boards.h"
+
+void cpu_save(QEMUFile *f, void *opaque)
+{
+}
+
+int cpu_load(QEMUFile *f, void *opaque, int version_id)
+{
+    return 0;
+}
diff --git a/target-s390x/op_helper.c b/target-s390x/op_helper.c
index 5de4d08..7b88225 100644
--- a/target-s390x/op_helper.c
+++ b/target-s390x/op_helper.c
@@ -1717,3 +1717,56 @@ void HELPER(sqdbr)(uint32_t f1, uint32_t f2)
 {
     env->fregs[f1].d = float64_sqrt(env->fregs[f2].d, &env->fpu_status);
 }
+
+/*****************************************************************************/
+/* Softmmu support */
+#if !defined (CONFIG_USER_ONLY)
+
+#define MMUSUFFIX _mmu
+
+#define SHIFT 0
+#include "softmmu_template.h"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+/* try to fill the TLB and return an exception if error. If retaddr is
+   NULL, it means that the function was called in C code (i.e. not
+   from generated code or from helper.c) */
+/* XXX: fix it to restore all registers */
+void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr)
+{
+    TranslationBlock *tb;
+    CPUState *saved_env;
+    unsigned long pc;
+    int ret;
+
+    /* XXX: hack to restore env in all cases, even if not called from
+       generated code */
+    saved_env = env;
+    env = cpu_single_env;
+    ret = cpu_s390x_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
+    if (unlikely(ret != 0)) {
+        if (likely(retaddr)) {
+            /* now we have a real cpu fault */
+            pc = (unsigned long)retaddr;
+            tb = tb_find_pc(pc);
+            if (likely(tb)) {
+                /* the PC is inside the translated code. It means that we have
+                   a virtual CPU fault */
+                cpu_restore_state(tb, env, pc, NULL);
+            }
+        }
+        /* XXX */
+        /* helper_raise_exception_err(env->exception_index, env->error_code); */
+    }
+    env = saved_env;
+}
+
+#endif
