diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index 5563042..c0016b6 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -885,7 +885,7 @@ struct sysib_322 {
 
 void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr);
 int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc,
-                  target_ulong *raddr, int *flags);
+                  target_ulong *raddr, int *flags, bool exc);
 int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code);
 uint32_t calc_cc(CPUS390XState *env, uint32_t cc_op, uint64_t src, uint64_t dst,
                  uint64_t vr);
diff --git a/target-s390x/helper.c b/target-s390x/helper.c
index 76b4b36..f8a3c5a 100644
--- a/target-s390x/helper.c
+++ b/target-s390x/helper.c
@@ -127,7 +127,7 @@ int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr orig_vaddr,
         vaddr &= 0x7fffffff;
     }
 
-    if (mmu_translate(env, vaddr, rw, asc, &raddr, &prot)) {
+    if (mmu_translate(env, vaddr, rw, asc, &raddr, &prot, true)) {
         /* Translation ended in exception */
         return 1;
     }
@@ -154,8 +154,7 @@ hwaddr s390_cpu_get_phys_page_debug(CPUState *cs, vaddr vaddr)
     S390CPU *cpu = S390_CPU(cs);
     CPUS390XState *env = &cpu->env;
     target_ulong raddr;
-    int prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
-    int old_exc = cs->exception_index;
+    int prot;
     uint64_t asc = env->psw.mask & PSW_MASK_ASC;
 
     /* 31-Bit mode */
@@ -163,8 +162,7 @@ hwaddr s390_cpu_get_phys_page_debug(CPUState *cs, vaddr vaddr)
         vaddr &= 0x7fffffff;
     }
 
-    mmu_translate(env, vaddr, 2, asc, &raddr, &prot);
-    cs->exception_index = old_exc;
+    mmu_translate(env, vaddr, 2, asc, &raddr, &prot, false);
 
     return raddr;
 }
diff --git a/target-s390x/mem_helper.c b/target-s390x/mem_helper.c
index d67b345..0e8cd0f 100644
--- a/target-s390x/mem_helper.c
+++ b/target-s390x/mem_helper.c
@@ -65,7 +65,7 @@ static void mvc_fast_memset(CPUS390XState *env, uint32_t l, uint64_t dest,
     uint64_t asc = env->psw.mask & PSW_MASK_ASC;
     int flags;
 
-    if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags)) {
+    if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags, true)) {
         cpu_stb_data(env, dest, byte);
         cpu_abort(CPU(cpu), "should never reach here");
     }
@@ -90,13 +90,13 @@ static void mvc_fast_memmove(CPUS390XState *env, uint32_t l, uint64_t dest,
     uint64_t asc = env->psw.mask & PSW_MASK_ASC;
     int flags;
 
-    if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags)) {
+    if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags, true)) {
         cpu_stb_data(env, dest, 0);
         cpu_abort(CPU(cpu), "should never reach here");
     }
     dest_phys |= dest & ~TARGET_PAGE_MASK;
 
-    if (mmu_translate(env, src, 0, asc, &src_phys, &flags)) {
+    if (mmu_translate(env, src, 0, asc, &src_phys, &flags, true)) {
         cpu_ldub_data(env, src);
         cpu_abort(CPU(cpu), "should never reach here");
     }
@@ -967,12 +967,12 @@ static uint32_t mvc_asc(CPUS390XState *env, int64_t l, uint64_t a1,
         cc = 3;
     }
 
-    if (mmu_translate(env, a1 & TARGET_PAGE_MASK, 1, mode1, &dest, &flags)) {
+    if (mmu_translate(env, a1, 1, mode1, &dest, &flags, true)) {
         cpu_loop_exit(CPU(s390_env_get_cpu(env)));
     }
     dest |= a1 & ~TARGET_PAGE_MASK;
 
-    if (mmu_translate(env, a2 & TARGET_PAGE_MASK, 0, mode2, &src, &flags)) {
+    if (mmu_translate(env, a2, 0, mode2, &src, &flags, true)) {
         cpu_loop_exit(CPU(s390_env_get_cpu(env)));
     }
     src |= a2 & ~TARGET_PAGE_MASK;
@@ -1088,7 +1088,7 @@ uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr)
     }
 
     cs->exception_index = old_exc;
-    if (mmu_translate(env, addr, 0, asc, &ret, &flags)) {
+    if (mmu_translate(env, addr, 0, asc, &ret, &flags, true)) {
         cc = 3;
     }
     if (cs->exception_index == EXCP_PGM) {
diff --git a/target-s390x/mmu_helper.c b/target-s390x/mmu_helper.c
index d4087ba..67ad3cc 100644
--- a/target-s390x/mmu_helper.c
+++ b/target-s390x/mmu_helper.c
@@ -66,7 +66,7 @@ static int trans_bits(CPUS390XState *env, uint64_t mode)
 }
 
 static void trigger_prot_fault(CPUS390XState *env, target_ulong vaddr,
-                               uint64_t mode)
+                               uint64_t mode, bool exc)
 {
     CPUState *cs = CPU(s390_env_get_cpu(env));
     int ilen = ILEN_LATER_INC;
@@ -74,25 +74,33 @@ static void trigger_prot_fault(CPUS390XState *env, target_ulong vaddr,
 
     DPRINTF("%s: vaddr=%016" PRIx64 " bits=%d\n", __func__, vaddr, bits);
 
+    if (!exc) {
+        return;
+    }
+
     stq_phys(cs->as,
              env->psa + offsetof(LowCore, trans_exc_code), vaddr | bits);
     trigger_pgm_exception(env, PGM_PROTECTION, ilen);
 }
 
 static void trigger_page_fault(CPUS390XState *env, target_ulong vaddr,
-                               uint32_t type, uint64_t asc, int rw)
+                               uint32_t type, uint64_t asc, int rw, bool exc)
 {
     CPUState *cs = CPU(s390_env_get_cpu(env));
     int ilen = ILEN_LATER;
     int bits = trans_bits(env, asc);
 
+    DPRINTF("%s: vaddr=%016" PRIx64 " bits=%d\n", __func__, vaddr, bits);
+
+    if (!exc) {
+        return;
+    }
+
     /* Code accesses have an undefined ilc.  */
     if (rw == 2) {
         ilen = 2;
     }
 
-    DPRINTF("%s: vaddr=%016" PRIx64 " bits=%d\n", __func__, vaddr, bits);
-
     stq_phys(cs->as,
              env->psa + offsetof(LowCore, trans_exc_code), vaddr | bits);
     trigger_pgm_exception(env, type, ilen);
@@ -115,11 +123,11 @@ static target_ulong mmu_real2abs(CPUS390XState *env, target_ulong raddr)
 /* Decode page table entry (normal 4KB page) */
 static int mmu_translate_pte(CPUS390XState *env, target_ulong vaddr,
                              uint64_t asc, uint64_t asce,
-                             target_ulong *raddr, int *flags, int rw)
+                             target_ulong *raddr, int *flags, int rw, bool exc)
 {
     if (asce & _PAGE_INVALID) {
         DPRINTF("%s: PTE=0x%" PRIx64 " invalid\n", __func__, asce);
-        trigger_page_fault(env, vaddr, PGM_PAGE_TRANS, asc, rw);
+        trigger_page_fault(env, vaddr, PGM_PAGE_TRANS, asc, rw, exc);
         return -1;
     }
 
@@ -139,7 +147,8 @@ static int mmu_translate_pte(CPUS390XState *env, target_ulong vaddr,
 /* Decode segment table entry */
 static int mmu_translate_segment(CPUS390XState *env, target_ulong vaddr,
                                  uint64_t asc, uint64_t st_entry,
-                                 target_ulong *raddr, int *flags, int rw)
+                                 target_ulong *raddr, int *flags, int rw,
+                                 bool exc)
 {
     CPUState *cs = CPU(s390_env_get_cpu(env));
     uint64_t origin, offs, pt_entry;
@@ -161,13 +170,14 @@ static int mmu_translate_segment(CPUS390XState *env, target_ulong vaddr,
     pt_entry = ldq_phys(cs->as, origin + offs);
     PTE_DPRINTF("%s: 0x%" PRIx64 " + 0x%" PRIx64 " => 0x%016" PRIx64 "\n",
                 __func__, origin, offs, pt_entry);
-    return mmu_translate_pte(env, vaddr, asc, pt_entry, raddr, flags, rw);
+    return mmu_translate_pte(env, vaddr, asc, pt_entry, raddr, flags, rw, exc);
 }
 
 /* Decode region table entries */
 static int mmu_translate_region(CPUS390XState *env, target_ulong vaddr,
                                 uint64_t asc, uint64_t entry, int level,
-                                target_ulong *raddr, int *flags, int rw)
+                                target_ulong *raddr, int *flags, int rw,
+                                bool exc)
 {
     CPUState *cs = CPU(s390_env_get_cpu(env));
     uint64_t origin, offs, new_entry;
@@ -188,12 +198,12 @@ static int mmu_translate_region(CPUS390XState *env, target_ulong vaddr,
     if ((new_entry & _REGION_ENTRY_INV) != 0) {
         /* XXX different regions have different faults */
         DPRINTF("%s: invalid region\n", __func__);
-        trigger_page_fault(env, vaddr, PGM_SEGMENT_TRANS, asc, rw);
+        trigger_page_fault(env, vaddr, PGM_SEGMENT_TRANS, asc, rw, exc);
         return -1;
     }
 
     if ((new_entry & _REGION_ENTRY_TYPE_MASK) != level) {
-        trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw);
+        trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw, exc);
         return -1;
     }
 
@@ -202,7 +212,7 @@ static int mmu_translate_region(CPUS390XState *env, target_ulong vaddr,
 
     if (level == _ASCE_TYPE_SEGMENT) {
         return mmu_translate_segment(env, vaddr, asc, new_entry, raddr, flags,
-                                     rw);
+                                     rw, exc);
     }
 
     /* Check region table offset and length */
@@ -210,18 +220,18 @@ static int mmu_translate_region(CPUS390XState *env, target_ulong vaddr,
     if (offs < ((new_entry & _REGION_ENTRY_TF) >> 6)
         || offs > (new_entry & _REGION_ENTRY_LENGTH)) {
         DPRINTF("%s: invalid offset or len (%lx)\n", __func__, new_entry);
-        trigger_page_fault(env, vaddr, pchks[level / 4 - 1], asc, rw);
+        trigger_page_fault(env, vaddr, pchks[level / 4 - 1], asc, rw, exc);
         return -1;
     }
 
     /* yet another region */
     return mmu_translate_region(env, vaddr, asc, new_entry, level - 4,
-                                raddr, flags, rw);
+                                raddr, flags, rw, exc);
 }
 
 static int mmu_translate_asc(CPUS390XState *env, target_ulong vaddr,
                              uint64_t asc, target_ulong *raddr, int *flags,
-                             int rw)
+                             int rw, bool exc)
 {
     uint64_t asce = 0;
     int level;
@@ -252,7 +262,7 @@ static int mmu_translate_asc(CPUS390XState *env, target_ulong vaddr,
     switch (level) {
     case _ASCE_TYPE_REGION1:
         if ((vaddr >> 62) > (asce & _ASCE_TABLE_LENGTH)) {
-            trigger_page_fault(env, vaddr, PGM_REG_FIRST_TRANS, asc, rw);
+            trigger_page_fault(env, vaddr, PGM_REG_FIRST_TRANS, asc, rw, exc);
             return -1;
         }
         break;
@@ -260,11 +270,11 @@ static int mmu_translate_asc(CPUS390XState *env, target_ulong vaddr,
         if (vaddr & 0xffe0000000000000ULL) {
             DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64
                     " 0xffe0000000000000ULL\n", __func__, vaddr);
-            trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw);
+            trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw, exc);
             return -1;
         }
         if ((vaddr >> 51 & 3) > (asce & _ASCE_TABLE_LENGTH)) {
-            trigger_page_fault(env, vaddr, PGM_REG_SEC_TRANS, asc, rw);
+            trigger_page_fault(env, vaddr, PGM_REG_SEC_TRANS, asc, rw, exc);
             return -1;
         }
         break;
@@ -272,11 +282,11 @@ static int mmu_translate_asc(CPUS390XState *env, target_ulong vaddr,
         if (vaddr & 0xfffffc0000000000ULL) {
             DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64
                     " 0xfffffc0000000000ULL\n", __func__, vaddr);
-            trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw);
+            trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw, exc);
             return -1;
         }
         if ((vaddr >> 40 & 3) > (asce & _ASCE_TABLE_LENGTH)) {
-            trigger_page_fault(env, vaddr, PGM_REG_THIRD_TRANS, asc, rw);
+            trigger_page_fault(env, vaddr, PGM_REG_THIRD_TRANS, asc, rw, exc);
             return -1;
         }
         break;
@@ -284,27 +294,38 @@ static int mmu_translate_asc(CPUS390XState *env, target_ulong vaddr,
         if (vaddr & 0xffffffff80000000ULL) {
             DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64
                     " 0xffffffff80000000ULL\n", __func__, vaddr);
-            trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw);
+            trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw, exc);
             return -1;
         }
         if ((vaddr >> 29 & 3) > (asce & _ASCE_TABLE_LENGTH)) {
-            trigger_page_fault(env, vaddr, PGM_SEGMENT_TRANS, asc, rw);
+            trigger_page_fault(env, vaddr, PGM_SEGMENT_TRANS, asc, rw, exc);
             return -1;
         }
         break;
     }
 
-    r = mmu_translate_region(env, vaddr, asc, asce, level, raddr, flags, rw);
+    r = mmu_translate_region(env, vaddr, asc, asce, level, raddr, flags, rw,
+                             exc);
     if ((rw == 1) && !(*flags & PAGE_WRITE)) {
-        trigger_prot_fault(env, vaddr, asc);
+        trigger_prot_fault(env, vaddr, asc, exc);
         return -1;
     }
 
     return r;
 }
 
+/**
+ * Translate a virtual (logical) address into a physical (absolute) address.
+ * @param vaddr  the virtual address
+ * @param rw     0 = read, 1 = write, 2 = code fetch
+ * @param asc    address space control (one of the PSW_ASC_* modes)
+ * @param raddr  the translated address is stored to this pointer
+ * @param flags  the PAGE_READ/WRITE/EXEC flags are stored to this pointer
+ * @param exc    true = inject a program check if a fault occured
+ * @return       0 if the translation was successfull, -1 if a fault occured
+ */
 int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc,
-                  target_ulong *raddr, int *flags)
+                  target_ulong *raddr, int *flags, bool exc)
 {
     int r = -1;
     uint8_t *sk;
@@ -321,7 +342,7 @@ int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc,
     switch (asc) {
     case PSW_ASC_PRIMARY:
     case PSW_ASC_HOME:
-        r = mmu_translate_asc(env, vaddr, asc, raddr, flags, rw);
+        r = mmu_translate_asc(env, vaddr, asc, raddr, flags, rw, exc);
         break;
     case PSW_ASC_SECONDARY:
         /*
@@ -330,11 +351,11 @@ int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc,
          */
         if (rw == 2) {
             r = mmu_translate_asc(env, vaddr, PSW_ASC_PRIMARY, raddr, flags,
-                                  rw);
+                                  rw, exc);
             *flags &= ~(PAGE_READ | PAGE_WRITE);
         } else {
             r = mmu_translate_asc(env, vaddr, PSW_ASC_SECONDARY, raddr, flags,
-                                  rw);
+                                  rw, exc);
             *flags &= ~(PAGE_EXEC);
         }
         break;
