Patchwork [015/147] target-s390: Reorg exception handling

login
register
mail settings
Submitter Richard Henderson
Date Sept. 27, 2012, 10:39 p.m.
Message ID <1348785610-23418-16-git-send-email-rth@twiddle.net>
Download mbox | patch
Permalink /patch/187561/
State New
Headers show

Comments

Richard Henderson - Sept. 27, 2012, 10:39 p.m.
Make the user path more like the system path.  Prepare for more kinds
of runtime exceptions.  Rename ILC to ILEN to make it clear that we
want to pass around a full instruction length, rather than a "code"
that happens to be stored one bit left in a larger field.

Signed-off-by: Richard Henderson <rth@twiddle.net>
---
 linux-user/main.c          | 140 +++++++++++++++++---------
 linux-user/s390x/syscall.h |   2 +-
 target-s390x/cpu.h         |  49 +++++----
 target-s390x/helper.c      |  58 ++++++-----
 target-s390x/mem_helper.c  |   2 +-
 target-s390x/misc_helper.c |   8 +-
 target-s390x/translate.c   | 242 +++++++++++++++++----------------------------
 7 files changed, 241 insertions(+), 260 deletions(-)

Patch

diff --git a/linux-user/main.c b/linux-user/main.c
index 9f3476b..05011af 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -2922,71 +2922,115 @@  void cpu_loop(CPUAlphaState *env)
 #ifdef TARGET_S390X
 void cpu_loop(CPUS390XState *env)
 {
-    int trapnr;
+    int trapnr, n, sig;
     target_siginfo_t info;
+    target_ulong addr;
 
     while (1) {
-        trapnr = cpu_s390x_exec (env);
-
+        trapnr = cpu_s390x_exec(env);
         switch (trapnr) {
         case EXCP_INTERRUPT:
-            /* just indicate that signals should be handled asap */
+            /* Just indicate that signals should be handled asap.  */
             break;
-        case EXCP_DEBUG:
-            {
-                int sig;
 
-                sig = gdb_handlesig (env, TARGET_SIGTRAP);
-                if (sig) {
-                    info.si_signo = sig;
-                    info.si_errno = 0;
-                    info.si_code = TARGET_TRAP_BRKPT;
-                    queue_signal(env, info.si_signo, &info);
-                }
+        case EXCP_SVC:
+            n = env->int_svc_code;
+            if (!n) {
+                /* syscalls > 255 */
+                n = env->regs[1];
             }
+            env->psw.addr += env->int_svc_ilen;
+            env->regs[2] = do_syscall(env, n, env->regs[2], env->regs[3],
+                                      env->regs[4], env->regs[5],
+                                      env->regs[6], env->regs[7], 0, 0);
             break;
-        case EXCP_SVC:
-            {
-                int n = env->int_svc_code;
-                if (!n) {
-                    /* syscalls > 255 */
-                    n = env->regs[1];
-                }
-                env->psw.addr += env->int_svc_ilc;
-                env->regs[2] = do_syscall(env, n,
-                           env->regs[2],
-                           env->regs[3],
-                           env->regs[4],
-                           env->regs[5],
-                           env->regs[6],
-                           env->regs[7],
-                           0, 0);
+
+        case EXCP_DEBUG:
+            sig = gdb_handlesig(env, TARGET_SIGTRAP);
+            if (sig) {
+                n = TARGET_TRAP_BRKPT;
+                goto do_signal_pc;
             }
             break;
-        case EXCP_ADDR:
-            {
-                info.si_signo = SIGSEGV;
-                info.si_errno = 0;
+        case EXCP_PGM:
+            n = env->int_pgm_code;
+            switch (n) {
+            case PGM_OPERATION:
+            case PGM_PRIVILEGED:
+                sig = SIGILL;
+                n = TARGET_ILL_ILLOPC;
+                goto do_signal_pc;
+            case PGM_PROTECTION:
+            case PGM_ADDRESSING:
+                sig = SIGSEGV;
                 /* XXX: check env->error_code */
-                info.si_code = TARGET_SEGV_MAPERR;
-                info._sifields._sigfault._addr = env->__excp_addr;
-                queue_signal(env, info.si_signo, &info);
+                n = TARGET_SEGV_MAPERR;
+                addr = env->__excp_addr;
+                goto do_signal;
+            case PGM_EXECUTE:
+            case PGM_SPECIFICATION:
+            case PGM_SPECIAL_OP:
+            case PGM_OPERAND:
+            do_sigill_opn:
+                sig = SIGILL;
+                n = TARGET_ILL_ILLOPN;
+                goto do_signal_pc;
+
+            case PGM_FIXPT_OVERFLOW:
+                sig = SIGFPE;
+                n = TARGET_FPE_INTOVF;
+                goto do_signal_pc;
+            case PGM_FIXPT_DIVIDE:
+                sig = SIGFPE;
+                n = TARGET_FPE_INTDIV;
+                goto do_signal_pc;
+
+            case PGM_DATA:
+                n = (env->fpc >> 8) & 0xff;
+                if (n == 0xff) {
+                    /* compare-and-trap */
+                    goto do_sigill_opn;
+                } else {
+                    /* An IEEE exception, simulated or otherwise.  */
+                    if (n & 0x80) {
+                        n = TARGET_FPE_FLTINV;
+                    } else if (n & 0x40) {
+                        n = TARGET_FPE_FLTDIV;
+                    } else if (n & 0x20) {
+                        n = TARGET_FPE_FLTOVF;
+                    } else if (n & 0x10) {
+                        n = TARGET_FPE_FLTUND;
+                    } else if (n & 0x08) {
+                        n = TARGET_FPE_FLTRES;
+                    } else {
+                        /* ??? Quantum exception; BFP, DFP error.  */
+                        goto do_sigill_opn;
+                    }
+                    sig = SIGFPE;
+                    goto do_signal_pc;
+                }
+
+            default:
+                fprintf(stderr, "Unhandled program exception: %#x\n", n);
+                cpu_dump_state(env, stderr, fprintf, 0);
+                exit(1);
             }
             break;
-        case EXCP_SPEC:
-            {
-                fprintf(stderr,"specification exception insn 0x%08x%04x\n", ldl(env->psw.addr), lduw(env->psw.addr + 4));
-                info.si_signo = SIGILL;
-                info.si_errno = 0;
-                info.si_code = TARGET_ILL_ILLOPC;
-                info._sifields._sigfault._addr = env->__excp_addr;
-                queue_signal(env, info.si_signo, &info);
-            }
+
+        do_signal_pc:
+            addr = env->psw.addr;
+        do_signal:
+            info.si_signo = sig;
+            info.si_errno = 0;
+            info.si_code = n;
+            info._sifields._sigfault._addr = addr;
+            queue_signal(env, info.si_signo, &info);
             break;
+
         default:
-            printf ("Unhandled trap: 0x%x\n", trapnr);
+            fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr);
             cpu_dump_state(env, stderr, fprintf, 0);
-            exit (1);
+            exit(1);
         }
         process_pending_signals (env);
     }
diff --git a/linux-user/s390x/syscall.h b/linux-user/s390x/syscall.h
index c2ea151..e4603b7 100644
--- a/linux-user/s390x/syscall.h
+++ b/linux-user/s390x/syscall.h
@@ -16,7 +16,7 @@  struct target_pt_regs {
     target_psw_t psw;
     abi_ulong gprs[TARGET_NUM_GPRS];
     abi_ulong orig_gpr2;
-    unsigned short ilc;
+    unsigned short ilen;
     unsigned short trap;
 };
 
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index 471fb91..2c5a743 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -79,10 +79,10 @@  typedef struct CPUS390XState {
     uint64_t psa;
 
     uint32_t int_pgm_code;
-    uint32_t int_pgm_ilc;
+    uint32_t int_pgm_ilen;
 
     uint32_t int_svc_code;
-    uint32_t int_svc_ilc;
+    uint32_t int_svc_ilen;
 
     uint64_t cregs[16]; /* control registers */
 
@@ -253,25 +253,31 @@  static inline void cpu_get_tb_cpu_state(CPUS390XState* env, target_ulong *pc,
              ((env->psw.mask & PSW_MASK_32) ? FLAG_MASK_32 : 0);
 }
 
-static inline int get_ilc(uint8_t opc)
+/* While the PoO talks about ILC (a number between 1-3) what is actually
+   stored in LowCore is shifted left one bit (an even between 2-6).  As
+   this is the actual length of the insn and therefore more useful, that
+   is what we want to pass around and manipulate.  To make sure that we
+   have applied this distinction universally, rename the "ILC" to "ILEN".  */
+static inline int get_ilen(uint8_t opc)
 {
     switch (opc >> 6) {
     case 0:
-        return 1;
+        return 2;
     case 1:
     case 2:
-        return 2;
-    case 3:
-        return 3;
+        return 4;
+    default:
+        return 6;
     }
-
-    return 0;
 }
 
-#define ILC_LATER       0x20
-#define ILC_LATER_INC   0x21
-#define ILC_LATER_INC_2 0x22
-
+#ifndef CONFIG_USER_ONLY
+/* In several cases of runtime exceptions, we havn't recorded the true
+   instruction length.  Use these codes when raising exceptions in order
+   to re-compute the length by examining the insn in memory.  */
+#define ILEN_LATER       0x20
+#define ILEN_LATER_INC   0x21
+#endif
 
 S390CPU *cpu_s390x_init(const char *cpu_model);
 void s390x_translate_init(void);
@@ -352,21 +358,10 @@  static inline void cpu_set_tls(CPUS390XState *env, target_ulong newtls)
 
 #include "exec-all.h"
 
-#ifdef CONFIG_USER_ONLY
-
-#define EXCP_OPEX 1 /* operation exception (sigill) */
-#define EXCP_SVC 2 /* supervisor call (syscall) */
-#define EXCP_ADDR 5 /* addressing exception */
-#define EXCP_SPEC 6 /* specification exception */
-
-#else
-
 #define EXCP_EXT 1 /* external interrupt */
 #define EXCP_SVC 2 /* supervisor call (syscall) */
 #define EXCP_PGM 3 /* program interruption */
 
-#endif /* CONFIG_USER_ONLY */
-
 #define INTERRUPT_EXT        (1 << 0)
 #define INTERRUPT_TOD        (1 << 1)
 #define INTERRUPT_CPUTIMER   (1 << 2)
@@ -543,9 +538,9 @@  typedef struct LowCore
     uint32_t        ext_params;               /* 0x080 */
     uint16_t        cpu_addr;                 /* 0x084 */
     uint16_t        ext_int_code;             /* 0x086 */
-    uint16_t        svc_ilc;                  /* 0x088 */
+    uint16_t        svc_ilen;                 /* 0x088 */
     uint16_t        svc_code;                 /* 0x08a */
-    uint16_t        pgm_ilc;                  /* 0x08c */
+    uint16_t        pgm_ilen;                 /* 0x08c */
     uint16_t        pgm_code;                 /* 0x08e */
     uint32_t        data_exc_code;            /* 0x090 */
     uint16_t        mon_class_num;            /* 0x094 */
@@ -933,6 +928,6 @@  uint32_t set_cc_nz_f32(float32 v);
 uint32_t set_cc_nz_f64(float64 v);
 
 /* misc_helper.c */
-void program_interrupt(CPUS390XState *env, uint32_t code, int ilc);
+void program_interrupt(CPUS390XState *env, uint32_t code, int ilen);
 
 #endif
diff --git a/target-s390x/helper.c b/target-s390x/helper.c
index 27c3123..d813dad 100644
--- a/target-s390x/helper.c
+++ b/target-s390x/helper.c
@@ -99,10 +99,10 @@  void do_interrupt(CPUS390XState *env)
 int cpu_s390x_handle_mmu_fault(CPUS390XState *env, target_ulong address,
                                int rw, int mmu_idx)
 {
-    /* fprintf(stderr, "%s: address 0x%lx rw %d mmu_idx %d\n",
-       __func__, address, rw, mmu_idx); */
-    env->exception_index = EXCP_ADDR;
-    /* FIXME: find out how this works on a real machine */
+    env->exception_index = EXCP_PGM;
+    env->int_pgm_code = PGM_ADDRESSING;
+    /* On real machines this value is dropped into LowMem.  Since this
+       is userland, simply put this someplace that cpu_loop can find it.  */
     env->__excp_addr = address;
     return 1;
 }
@@ -111,11 +111,11 @@  int cpu_s390x_handle_mmu_fault(CPUS390XState *env, target_ulong address,
 
 /* Ensure to exit the TB after this call! */
 static void trigger_pgm_exception(CPUS390XState *env, uint32_t code,
-                                  uint32_t ilc)
+                                  uint32_t ilen)
 {
     env->exception_index = EXCP_PGM;
     env->int_pgm_code = code;
-    env->int_pgm_ilc = ilc;
+    env->int_pgm_ilen = ilen;
 }
 
 static int trans_bits(CPUS390XState *env, uint64_t mode)
@@ -143,30 +143,30 @@  static int trans_bits(CPUS390XState *env, uint64_t mode)
 static void trigger_prot_fault(CPUS390XState *env, target_ulong vaddr,
                                uint64_t mode)
 {
-    int ilc = ILC_LATER_INC_2;
+    int ilen = ILEN_LATER_INC;
     int bits = trans_bits(env, mode) | 4;
 
     DPRINTF("%s: vaddr=%016" PRIx64 " bits=%d\n", __func__, vaddr, bits);
 
     stq_phys(env->psa + offsetof(LowCore, trans_exc_code), vaddr | bits);
-    trigger_pgm_exception(env, PGM_PROTECTION, ilc);
+    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)
 {
-    int ilc = ILC_LATER;
+    int ilen = ILEN_LATER;
     int bits = trans_bits(env, asc);
 
+    /* Code accesses have an undefined ilc.  */
     if (rw == 2) {
-        /* code has is undefined ilc */
-        ilc = 2;
+        ilen = 2;
     }
 
     DPRINTF("%s: vaddr=%016" PRIx64 " bits=%d\n", __func__, vaddr, bits);
 
     stq_phys(env->psa + offsetof(LowCore, trans_exc_code), vaddr | bits);
-    trigger_pgm_exception(env, type, ilc);
+    trigger_pgm_exception(env, type, ilen);
 }
 
 static int mmu_translate_asce(CPUS390XState *env, target_ulong vaddr,
@@ -406,7 +406,7 @@  int cpu_s390x_handle_mmu_fault(CPUS390XState *env, target_ulong orig_vaddr,
     if (raddr > (ram_size + virtio_size)) {
         DPRINTF("%s: aaddr %" PRIx64 " > ram_size %" PRIx64 "\n", __func__,
                 (uint64_t)aaddr, (uint64_t)ram_size);
-        trigger_pgm_exception(env, PGM_ADDRESSING, ILC_LATER);
+        trigger_pgm_exception(env, PGM_ADDRESSING, ILEN_LATER);
         return 1;
     }
 
@@ -480,9 +480,9 @@  static void do_svc_interrupt(CPUS390XState *env)
     lowcore = cpu_physical_memory_map(env->psa, &len, 1);
 
     lowcore->svc_code = cpu_to_be16(env->int_svc_code);
-    lowcore->svc_ilc = cpu_to_be16(env->int_svc_ilc);
+    lowcore->svc_ilen = cpu_to_be16(env->int_svc_ilen);
     lowcore->svc_old_psw.mask = cpu_to_be64(get_psw_mask(env));
-    lowcore->svc_old_psw.addr = cpu_to_be64(env->psw.addr + (env->int_svc_ilc));
+    lowcore->svc_old_psw.addr = cpu_to_be64(env->psw.addr + env->int_svc_ilen);
     mask = be64_to_cpu(lowcore->svc_new_psw.mask);
     addr = be64_to_cpu(lowcore->svc_new_psw.addr);
 
@@ -496,28 +496,26 @@  static void do_program_interrupt(CPUS390XState *env)
     uint64_t mask, addr;
     LowCore *lowcore;
     target_phys_addr_t len = TARGET_PAGE_SIZE;
-    int ilc = env->int_pgm_ilc;
+    int ilen = env->int_pgm_ilen;
 
-    switch (ilc) {
-    case ILC_LATER:
-        ilc = get_ilc(cpu_ldub_code(env, env->psw.addr));
-        break;
-    case ILC_LATER_INC:
-        ilc = get_ilc(cpu_ldub_code(env, env->psw.addr));
-        env->psw.addr += ilc * 2;
+    switch (ilen) {
+    case ILEN_LATER:
+        ilen = get_ilen(cpu_ldub_code(env, env->psw.addr));
         break;
-    case ILC_LATER_INC_2:
-        ilc = get_ilc(cpu_ldub_code(env, env->psw.addr)) * 2;
-        env->psw.addr += ilc;
+    case ILEN_LATER_INC:
+        ilen = get_ilen(cpu_ldub_code(env, env->psw.addr));
+        env->psw.addr += ilen;
         break;
+    default:
+        assert(ilen == 2 || ilen == 4 || ilen == 6);
     }
 
-    qemu_log_mask(CPU_LOG_INT, "%s: code=0x%x ilc=%d\n",
-                  __func__, env->int_pgm_code, ilc);
+    qemu_log_mask(CPU_LOG_INT, "%s: code=0x%x ilen=%d\n",
+                  __func__, env->int_pgm_code, ilen);
 
     lowcore = cpu_physical_memory_map(env->psa, &len, 1);
 
-    lowcore->pgm_ilc = cpu_to_be16(ilc);
+    lowcore->pgm_ilen = cpu_to_be16(ilen);
     lowcore->pgm_code = cpu_to_be16(env->int_pgm_code);
     lowcore->program_old_psw.mask = cpu_to_be64(get_psw_mask(env));
     lowcore->program_old_psw.addr = cpu_to_be64(env->psw.addr);
@@ -527,7 +525,7 @@  static void do_program_interrupt(CPUS390XState *env)
     cpu_physical_memory_unmap(lowcore, len, 1, len);
 
     DPRINTF("%s: %x %x %" PRIx64 " %" PRIx64 "\n", __func__,
-            env->int_pgm_code, ilc, env->psw.mask,
+            env->int_pgm_code, ilen, env->psw.mask,
             env->psw.addr);
 
     load_psw(env, mask, addr);
diff --git a/target-s390x/mem_helper.c b/target-s390x/mem_helper.c
index b21b37c..0a06df8 100644
--- a/target-s390x/mem_helper.c
+++ b/target-s390x/mem_helper.c
@@ -600,7 +600,7 @@  uint32_t HELPER(ex)(CPUS390XState *env, uint32_t cc, uint64_t v1,
         HELPER_LOG("%s: svc %ld via execute\n", __func__, (insn | v1) & 0xff);
         env->psw.addr = ret - 4;
         env->int_svc_code = (insn | v1) & 0xff;
-        env->int_svc_ilc = 4;
+        env->int_svc_ilen = 4;
         helper_exception(env, EXCP_SVC);
     } else if ((insn & 0xff00) == 0xbf00) {
         uint32_t insn2, r1, r3, b2, d2;
diff --git a/target-s390x/misc_helper.c b/target-s390x/misc_helper.c
index e9b3cae..bd5fd20 100644
--- a/target-s390x/misc_helper.c
+++ b/target-s390x/misc_helper.c
@@ -42,7 +42,7 @@ 
 #define HELPER_LOG(x...)
 #endif
 
-/* raise an exception */
+/* Raise an exception statically from a TB.  */
 void HELPER(exception)(CPUS390XState *env, uint32_t excp)
 {
     HELPER_LOG("%s: exception %d\n", __func__, excp);
@@ -51,7 +51,7 @@  void HELPER(exception)(CPUS390XState *env, uint32_t excp)
 }
 
 #ifndef CONFIG_USER_ONLY
-void program_interrupt(CPUS390XState *env, uint32_t code, int ilc)
+void program_interrupt(CPUS390XState *env, uint32_t code, int ilen)
 {
     qemu_log_mask(CPU_LOG_INT, "program interrupt at %#" PRIx64 "\n",
                   env->psw.addr);
@@ -62,7 +62,7 @@  void program_interrupt(CPUS390XState *env, uint32_t code, int ilc)
 #endif
     } else {
         env->int_pgm_code = code;
-        env->int_pgm_ilc = ilc;
+        env->int_pgm_ilen = ilen;
         env->exception_index = EXCP_PGM;
         cpu_loop_exit(env);
     }
@@ -149,7 +149,7 @@  uint64_t HELPER(diag)(CPUS390XState *env, uint32_t num, uint64_t mem,
     }
 
     if (r) {
-        program_interrupt(env, PGM_OPERATION, ILC_LATER_INC);
+        program_interrupt(env, PGM_OPERATION, ILEN_LATER_INC);
     }
 
     return r;
diff --git a/target-s390x/translate.c b/target-s390x/translate.c
index 2dbb969..cf455b5 100644
--- a/target-s390x/translate.c
+++ b/target-s390x/translate.c
@@ -18,7 +18,6 @@ 
  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  */
 
-/* #define DEBUG_ILLEGAL_INSTRUCTIONS */
 /* #define DEBUG_INLINE_BRANCHES */
 #define S390X_DEBUG_DISAS
 /* #define S390X_DEBUG_DISAS_VERBOSE */
@@ -338,105 +337,52 @@  static inline int get_mem_index(DisasContext *s)
     }
 }
 
-static inline void gen_debug(DisasContext *s)
+static void gen_exception(int excp)
 {
-    TCGv_i32 tmp = tcg_const_i32(EXCP_DEBUG);
-    update_psw_addr(s);
-    gen_op_calc_cc(s);
-    gen_helper_exception(cpu_env, tmp);
-    tcg_temp_free_i32(tmp);
-    s->is_jmp = DISAS_EXCP;
-}
-
-#ifdef CONFIG_USER_ONLY
-
-static void gen_illegal_opcode(CPUS390XState *env, DisasContext *s, int ilc)
-{
-    TCGv_i32 tmp = tcg_const_i32(EXCP_SPEC);
-    update_psw_addr(s);
-    gen_op_calc_cc(s);
+    TCGv_i32 tmp = tcg_const_i32(excp);
     gen_helper_exception(cpu_env, tmp);
     tcg_temp_free_i32(tmp);
-    s->is_jmp = DISAS_EXCP;
 }
 
-#else /* CONFIG_USER_ONLY */
-
-static void debug_print_inst(CPUS390XState *env, DisasContext *s, int ilc)
-{
-#ifdef DEBUG_ILLEGAL_INSTRUCTIONS
-    uint64_t inst = 0;
-
-    switch (ilc & 3) {
-    case 1:
-        inst = ld_code2(env, s->pc);
-        break;
-    case 2:
-        inst = ld_code4(env, s->pc);
-        break;
-    case 3:
-        inst = ld_code6(env, s->pc);
-        break;
-    }
-
-    fprintf(stderr, "Illegal instruction [%d at %016" PRIx64 "]: 0x%016"
-            PRIx64 "\n", ilc, s->pc, inst);
-#endif
-}
-
-static void gen_program_exception(CPUS390XState *env, DisasContext *s, int ilc,
-                                  int code)
+static void gen_program_exception(DisasContext *s, int code)
 {
     TCGv_i32 tmp;
 
-    debug_print_inst(env, s, ilc);
-
-    /* remember what pgm exeption this was */
+    /* Remember what pgm exeption this was.  */
     tmp = tcg_const_i32(code);
     tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUS390XState, int_pgm_code));
     tcg_temp_free_i32(tmp);
 
-    tmp = tcg_const_i32(ilc);
-    tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUS390XState, int_pgm_ilc));
+    tmp = tcg_const_i32(s->next_pc - s->pc);
+    tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUS390XState, int_pgm_ilen));
     tcg_temp_free_i32(tmp);
 
-    /* advance past instruction */
-    s->pc += (ilc * 2);
+    /* Advance past instruction.  */
+    s->pc = s->next_pc;
     update_psw_addr(s);
 
-    /* save off cc */
+    /* Save off cc.  */
     gen_op_calc_cc(s);
 
-    /* trigger exception */
-    tmp = tcg_const_i32(EXCP_PGM);
-    gen_helper_exception(cpu_env, tmp);
-    tcg_temp_free_i32(tmp);
+    /* Trigger exception.  */
+    gen_exception(EXCP_PGM);
 
-    /* end TB here */
+    /* End TB here.  */
     s->is_jmp = DISAS_EXCP;
 }
 
-
-static void gen_illegal_opcode(CPUS390XState *env, DisasContext *s, int ilc)
+static inline void gen_illegal_opcode(DisasContext *s)
 {
-    gen_program_exception(env, s, ilc, PGM_SPECIFICATION);
+    gen_program_exception(s, PGM_SPECIFICATION);
 }
 
-static void gen_privileged_exception(CPUS390XState *env, DisasContext *s,
-                                     int ilc)
-{
-    gen_program_exception(env, s, ilc, PGM_PRIVILEGED);
-}
-
-static void check_privileged(CPUS390XState *env, DisasContext *s, int ilc)
+static inline void check_privileged(DisasContext *s)
 {
     if (s->tb->flags & (PSW_MASK_PSTATE >> 32)) {
-        gen_privileged_exception(env, s, ilc);
+        gen_program_exception(s, PGM_PRIVILEGED);
     }
 }
 
-#endif /* CONFIG_USER_ONLY */
-
 static TCGv_i64 get_address(DisasContext *s, int x2, int b2, int d2)
 {
     TCGv_i64 tmp;
@@ -1769,7 +1715,7 @@  static void disas_e3(CPUS390XState *env, DisasContext* s, int op, int r1,
         break;
     default:
         LOG_DISAS("illegal e3 operation 0x%x\n", op);
-        gen_illegal_opcode(env, s, 3);
+        gen_illegal_opcode(s);
         break;
     }
     tcg_temp_free_i64(addr);
@@ -1794,7 +1740,7 @@  static void disas_e5(CPUS390XState *env, DisasContext* s, uint64_t insn)
         break;
     default:
         LOG_DISAS("illegal e5 operation 0x%x\n", op);
-        gen_illegal_opcode(env, s, 3);
+        gen_illegal_opcode(s);
         break;
     }
 
@@ -1809,7 +1755,6 @@  static void disas_eb(CPUS390XState *env, DisasContext *s, int op, int r1,
     TCGv_i64 tmp, tmp2, tmp3, tmp4;
     TCGv_i32 tmp32_1, tmp32_2;
     int i, stm_len;
-    int ilc = 3;
 
     LOG_DISAS("disas_eb: op 0x%x r1 %d r3 %d b2 %d d2 0x%x\n",
               op, r1, r3, b2, d2);
@@ -1947,7 +1892,7 @@  do_mh:
 #ifndef CONFIG_USER_ONLY
     case 0x2f: /* LCTLG     R1,R3,D2(B2)     [RSE] */
         /* Load Control */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         tmp = get_address(s, 0, b2, d2);
         tmp32_1 = tcg_const_i32(r1);
         tmp32_2 = tcg_const_i32(r3);
@@ -1959,7 +1904,7 @@  do_mh:
         break;
     case 0x25: /* STCTG     R1,R3,D2(B2)     [RSE] */
         /* Store Control */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         tmp = get_address(s, 0, b2, d2);
         tmp32_1 = tcg_const_i32(r1);
         tmp32_2 = tcg_const_i32(r3);
@@ -2036,7 +1981,7 @@  do_mh:
         break;
     default:
         LOG_DISAS("illegal eb operation 0x%x\n", op);
-        gen_illegal_opcode(env, s, ilc);
+        gen_illegal_opcode(s);
         break;
     }
 }
@@ -2156,7 +2101,7 @@  static void disas_ed(CPUS390XState *env, DisasContext *s, int op, int r1,
         break;
     default:
         LOG_DISAS("illegal ed operation 0x%x\n", op);
-        gen_illegal_opcode(env, s, 3);
+        gen_illegal_opcode(s);
         return;
     }
     tcg_temp_free_i32(tmp_r1);
@@ -2313,7 +2258,7 @@  static void disas_a5(CPUS390XState *env, DisasContext *s, int op, int r1,
         break;
     default:
         LOG_DISAS("illegal a5 operation 0x%x\n", op);
-        gen_illegal_opcode(env, s, 2);
+        gen_illegal_opcode(s);
         return;
     }
 }
@@ -2451,7 +2396,7 @@  static void disas_a7(CPUS390XState *env, DisasContext *s, int op, int r1,
         break;
     default:
         LOG_DISAS("illegal a7 operation 0x%x\n", op);
-        gen_illegal_opcode(env, s, 2);
+        gen_illegal_opcode(s);
         return;
     }
 }
@@ -2462,7 +2407,6 @@  static void disas_b2(CPUS390XState *env, DisasContext *s, int op,
     TCGv_i64 tmp, tmp2, tmp3;
     TCGv_i32 tmp32_1, tmp32_2, tmp32_3;
     int r1, r2;
-    int ilc = 2;
 #ifndef CONFIG_USER_ONLY
     int r3, d2, b2;
 #endif
@@ -2556,7 +2500,7 @@  static void disas_b2(CPUS390XState *env, DisasContext *s, int op,
 #ifndef CONFIG_USER_ONLY
     case 0x02: /* STIDP     D2(B2)     [S] */
         /* Store CPU ID */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
         potential_page_fault(s);
@@ -2565,7 +2509,7 @@  static void disas_b2(CPUS390XState *env, DisasContext *s, int op,
         break;
     case 0x04: /* SCK       D2(B2)     [S] */
         /* Set Clock */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
         potential_page_fault(s);
@@ -2584,7 +2528,7 @@  static void disas_b2(CPUS390XState *env, DisasContext *s, int op,
         break;
     case 0x06: /* SCKC     D2(B2)     [S] */
         /* Set Clock Comparator */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
         potential_page_fault(s);
@@ -2593,7 +2537,7 @@  static void disas_b2(CPUS390XState *env, DisasContext *s, int op,
         break;
     case 0x07: /* STCKC    D2(B2)     [S] */
         /* Store Clock Comparator */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
         potential_page_fault(s);
@@ -2602,7 +2546,7 @@  static void disas_b2(CPUS390XState *env, DisasContext *s, int op,
         break;
     case 0x08: /* SPT      D2(B2)     [S] */
         /* Set CPU Timer */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
         potential_page_fault(s);
@@ -2611,7 +2555,7 @@  static void disas_b2(CPUS390XState *env, DisasContext *s, int op,
         break;
     case 0x09: /* STPT     D2(B2)     [S] */
         /* Store CPU Timer */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
         potential_page_fault(s);
@@ -2620,7 +2564,7 @@  static void disas_b2(CPUS390XState *env, DisasContext *s, int op,
         break;
     case 0x0a: /* SPKA     D2(B2)     [S] */
         /* Set PSW Key from Address */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
         tmp2 = tcg_temp_new_i64();
@@ -2632,12 +2576,12 @@  static void disas_b2(CPUS390XState *env, DisasContext *s, int op,
         break;
     case 0x0d: /* PTLB                [S] */
         /* Purge TLB */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         gen_helper_ptlb(cpu_env);
         break;
     case 0x10: /* SPX      D2(B2)     [S] */
         /* Set Prefix Register */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
         potential_page_fault(s);
@@ -2646,7 +2590,7 @@  static void disas_b2(CPUS390XState *env, DisasContext *s, int op,
         break;
     case 0x11: /* STPX     D2(B2)     [S] */
         /* Store Prefix */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
         tmp2 = tcg_temp_new_i64();
@@ -2657,7 +2601,7 @@  static void disas_b2(CPUS390XState *env, DisasContext *s, int op,
         break;
     case 0x12: /* STAP     D2(B2)     [S] */
         /* Store CPU Address */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
         tmp2 = tcg_temp_new_i64();
@@ -2671,7 +2615,7 @@  static void disas_b2(CPUS390XState *env, DisasContext *s, int op,
         break;
     case 0x21: /* IPTE     R1,R2      [RRE] */
         /* Invalidate PTE */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         r1 = (insn >> 4) & 0xf;
         r2 = insn & 0xf;
         tmp = load_reg(r1);
@@ -2682,7 +2626,7 @@  static void disas_b2(CPUS390XState *env, DisasContext *s, int op,
         break;
     case 0x29: /* ISKE     R1,R2      [RRE] */
         /* Insert Storage Key Extended */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         r1 = (insn >> 4) & 0xf;
         r2 = insn & 0xf;
         tmp = load_reg(r2);
@@ -2694,7 +2638,7 @@  static void disas_b2(CPUS390XState *env, DisasContext *s, int op,
         break;
     case 0x2a: /* RRBE     R1,R2      [RRE] */
         /* Set Storage Key Extended */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         r1 = (insn >> 4) & 0xf;
         r2 = insn & 0xf;
         tmp32_1 = load_reg32(r1);
@@ -2706,7 +2650,7 @@  static void disas_b2(CPUS390XState *env, DisasContext *s, int op,
         break;
     case 0x2b: /* SSKE     R1,R2      [RRE] */
         /* Set Storage Key Extended */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         r1 = (insn >> 4) & 0xf;
         r2 = insn & 0xf;
         tmp32_1 = load_reg32(r1);
@@ -2717,12 +2661,12 @@  static void disas_b2(CPUS390XState *env, DisasContext *s, int op,
         break;
     case 0x34: /* STCH ? */
         /* Store Subchannel */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         gen_op_movi_cc(s, 3);
         break;
     case 0x46: /* STURA    R1,R2      [RRE] */
         /* Store Using Real Address */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         r1 = (insn >> 4) & 0xf;
         r2 = insn & 0xf;
         tmp32_1 = load_reg32(r1);
@@ -2734,7 +2678,7 @@  static void disas_b2(CPUS390XState *env, DisasContext *s, int op,
         break;
     case 0x50: /* CSP      R1,R2      [RRE] */
         /* Compare And Swap And Purge */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         r1 = (insn >> 4) & 0xf;
         r2 = insn & 0xf;
         tmp32_1 = tcg_const_i32(r1);
@@ -2746,7 +2690,7 @@  static void disas_b2(CPUS390XState *env, DisasContext *s, int op,
         break;
     case 0x5f: /* CHSC ? */
         /* Channel Subsystem Call */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         gen_op_movi_cc(s, 3);
         break;
     case 0x78: /* STCKE    D2(B2)     [S] */
@@ -2760,19 +2704,19 @@  static void disas_b2(CPUS390XState *env, DisasContext *s, int op,
         break;
     case 0x79: /* SACF    D2(B2)     [S] */
         /* Set Address Space Control Fast */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
         potential_page_fault(s);
         gen_helper_sacf(cpu_env, tmp);
         tcg_temp_free_i64(tmp);
         /* addressing mode has changed, so end the block */
-        s->pc += ilc * 2;
+        s->pc = s->next_pc;
         update_psw_addr(s);
         s->is_jmp = DISAS_JUMP;
         break;
     case 0x7d: /* STSI     D2,(B2)     [S] */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
         tmp32_1 = load_reg32(0);
@@ -2798,7 +2742,7 @@  static void disas_b2(CPUS390XState *env, DisasContext *s, int op,
         break;
     case 0xb1: /* STFL     D2(B2)     [S] */
         /* Store Facility List (CPU features) at 200 */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         tmp2 = tcg_const_i64(0xc0000000);
         tmp = tcg_const_i64(200);
         tcg_gen_qemu_st32(tmp2, tmp, get_mem_index(s));
@@ -2807,7 +2751,7 @@  static void disas_b2(CPUS390XState *env, DisasContext *s, int op,
         break;
     case 0xb2: /* LPSWE    D2(B2)     [S] */
         /* Load PSW Extended */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
         tmp2 = tcg_temp_new_i64();
@@ -2824,7 +2768,7 @@  static void disas_b2(CPUS390XState *env, DisasContext *s, int op,
         break;
     case 0x20: /* SERVC     R1,R2     [RRE] */
         /* SCLP Service call (PV hypercall) */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         potential_page_fault(s);
         tmp32_1 = load_reg32(r2);
         tmp = load_reg(r1);
@@ -2836,7 +2780,7 @@  static void disas_b2(CPUS390XState *env, DisasContext *s, int op,
 #endif
     default:
         LOG_DISAS("illegal b2 operation 0x%x\n", op);
-        gen_illegal_opcode(env, s, ilc);
+        gen_illegal_opcode(s);
         break;
     }
 }
@@ -3112,7 +3056,7 @@  static void disas_b3(CPUS390XState *env, DisasContext *s, int op, int m3,
         break;
     default:
         LOG_DISAS("illegal b3 operation 0x%x\n", op);
-        gen_illegal_opcode(env, s, 2);
+        gen_illegal_opcode(s);
         break;
     }
 
@@ -3419,7 +3363,7 @@  static void disas_b9(CPUS390XState *env, DisasContext *s, int op, int r1,
         break;
     default:
         LOG_DISAS("illegal b9 operation 0x%x\n", op);
-        gen_illegal_opcode(env, s, 2);
+        gen_illegal_opcode(s);
         break;
     }
 }
@@ -3525,7 +3469,7 @@  static void disas_c0(CPUS390XState *env, DisasContext *s, int op, int r1, int i2
         break;
     default:
         LOG_DISAS("illegal c0 operation 0x%x\n", op);
-        gen_illegal_opcode(env, s, 3);
+        gen_illegal_opcode(s);
         break;
     }
 }
@@ -3559,7 +3503,7 @@  static void disas_c2(CPUS390XState *env, DisasContext *s, int op, int r1,
         break;
     default:
         LOG_DISAS("illegal c2 operation 0x%x\n", op);
-        gen_illegal_opcode(env, s, 3);
+        gen_illegal_opcode(s);
         break;
     }
 }
@@ -3589,14 +3533,11 @@  static void disas_s390_insn(CPUS390XState *env, DisasContext *s)
     uint64_t insn;
     int op, r1, r2, r3, d1, d2, x2, b1, b2, i, i2, r1b;
     TCGv_i32 vl;
-    int ilc;
     int l1;
 
     opc = cpu_ldub_code(env, s->pc);
     LOG_DISAS("opc 0x%x\n", opc);
 
-    ilc = get_ilc(opc);
-
     switch (opc) {
 #ifndef CONFIG_USER_ONLY
     case 0x01: /* SAM */
@@ -3649,15 +3590,13 @@  static void disas_s390_insn(CPUS390XState *env, DisasContext *s)
         update_psw_addr(s);
         gen_op_calc_cc(s);
         tmp32_1 = tcg_const_i32(i);
-        tmp32_2 = tcg_const_i32(ilc * 2);
-        tmp32_3 = tcg_const_i32(EXCP_SVC);
+        tmp32_2 = tcg_const_i32(s->next_pc - s->pc);
         tcg_gen_st_i32(tmp32_1, cpu_env, offsetof(CPUS390XState, int_svc_code));
-        tcg_gen_st_i32(tmp32_2, cpu_env, offsetof(CPUS390XState, int_svc_ilc));
-        gen_helper_exception(cpu_env, tmp32_3);
+        tcg_gen_st_i32(tmp32_2, cpu_env, offsetof(CPUS390XState, int_svc_ilen));
+        gen_exception(EXCP_SVC);
         s->is_jmp = DISAS_EXCP;
         tcg_temp_free_i32(tmp32_1);
         tcg_temp_free_i32(tmp32_2);
-        tcg_temp_free_i32(tmp32_3);
         break;
     case 0xd: /* BASR   R1,R2     [RR] */
         insn = ld_code2(env, s->pc);
@@ -4148,7 +4087,7 @@  static void disas_s390_insn(CPUS390XState *env, DisasContext *s)
 #ifndef CONFIG_USER_ONLY
     case 0x80: /* SSM      D2(B2)       [S] */
         /* Set System Mask */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         insn = ld_code4(env, s->pc);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
@@ -4164,7 +4103,7 @@  static void disas_s390_insn(CPUS390XState *env, DisasContext *s)
         break;
     case 0x82: /* LPSW     D2(B2)       [S] */
         /* Load PSW */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         insn = ld_code4(env, s->pc);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
@@ -4184,7 +4123,7 @@  static void disas_s390_insn(CPUS390XState *env, DisasContext *s)
         break;
     case 0x83: /* DIAG     R1,R3,D2     [RS] */
         /* Diagnose call (KVM hypercall) */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         potential_page_fault(s);
         insn = ld_code4(env, s->pc);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
@@ -4402,7 +4341,7 @@  static void disas_s390_insn(CPUS390XState *env, DisasContext *s)
 #ifndef CONFIG_USER_ONLY
     case 0xac: /* STNSM   D1(B1),I2     [SI] */
     case 0xad: /* STOSM   D1(B1),I2     [SI] */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         insn = ld_code4(env, s->pc);
         tmp = decode_si(s, insn, &i2, &b1, &d1);
         tmp2 = tcg_temp_new_i64();
@@ -4418,7 +4357,7 @@  static void disas_s390_insn(CPUS390XState *env, DisasContext *s)
         tcg_temp_free_i64(tmp2);
         break;
     case 0xae: /* SIGP   R1,R3,D2(B2)     [RS] */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         insn = ld_code4(env, s->pc);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
@@ -4432,7 +4371,7 @@  static void disas_s390_insn(CPUS390XState *env, DisasContext *s)
         tcg_temp_free_i32(tmp32_1);
         break;
     case 0xb1: /* LRA    R1,D2(X2, B2)     [RX] */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         insn = ld_code4(env, s->pc);
         tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
         tmp32_1 = tcg_const_i32(r1);
@@ -4476,7 +4415,7 @@  static void disas_s390_insn(CPUS390XState *env, DisasContext *s)
 #ifndef CONFIG_USER_ONLY
     case 0xb6: /* STCTL     R1,R3,D2(B2)     [RS] */
         /* Store Control */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         insn = ld_code4(env, s->pc);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
@@ -4490,7 +4429,7 @@  static void disas_s390_insn(CPUS390XState *env, DisasContext *s)
         break;
     case 0xb7: /* LCTL      R1,R3,D2(B2)     [RS] */
         /* Load Control */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         insn = ld_code4(env, s->pc);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
@@ -4674,7 +4613,7 @@  static void disas_s390_insn(CPUS390XState *env, DisasContext *s)
 #ifndef CONFIG_USER_ONLY
     case 0xda: /* MVCP     D1(R1,B1),D2(B2),R3   [SS] */
     case 0xdb: /* MVCS     D1(R1,B1),D2(B2),R3   [SS] */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         potential_page_fault(s);
         insn = ld_code6(env, s->pc);
         r1 = (insn >> 36) & 0xf;
@@ -4712,7 +4651,7 @@  static void disas_s390_insn(CPUS390XState *env, DisasContext *s)
 #ifndef CONFIG_USER_ONLY
     case 0xe5:
         /* Test Protection */
-        check_privileged(env, s, ilc);
+        check_privileged(s);
         insn = ld_code6(env, s->pc);
         debug_insn(insn);
         disas_e5(env, s, insn);
@@ -4742,7 +4681,7 @@  static void disas_s390_insn(CPUS390XState *env, DisasContext *s)
         break;
     default:
         qemu_log_mask(LOG_UNIMP, "unimplemented opcode 0x%x\n", opc);
-        gen_illegal_opcode(env, s, ilc);
+        gen_illegal_opcode(s);
         break;
     }
 }
@@ -5273,19 +5212,22 @@  static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s,
                                      DisasFields *f)
 {
     uint64_t insn, pc = s->pc;
-    int op, op2;
+    int op, op2, ilen;
     const DisasInsn *info;
 
     insn = ld_code2(env, pc);
     op = (insn >> 8) & 0xff;
-    switch (get_ilc(op)) {
-    case 1:
+    ilen = get_ilen(op);
+    s->next_pc = s->pc + ilen;
+
+    switch (ilen) {
+    case 2:
         insn = insn << 48;
         break;
-    case 2:
+    case 4:
         insn = ld_code4(env, pc) << 32;
         break;
-    case 3:
+    case 6:
         insn = (insn << 48) | (ld_code4(env, pc + 2) << 16);
         break;
     default:
@@ -5361,9 +5303,6 @@  static ExitStatus translate_one(CPUS390XState *env, DisasContext *s)
 
     insn = extract_insn(env, s, &f);
 
-    /* Instruction length is encoded in the opcode */
-    s->next_pc = s->pc + get_ilc(f.op) * 2;
-
     /* If not found, try the old interpreter.  This includes ILLOPC.  */
     if (insn == NULL) {
         disas_s390_insn(env, s);
@@ -5452,6 +5391,7 @@  static inline void gen_intermediate_code_internal(CPUS390XState *env,
     int num_insns, max_insns;
     CPUBreakpoint *bp;
     ExitStatus status;
+    bool do_debug;
 
     pc_start = tb->pc;
 
@@ -5463,7 +5403,7 @@  static inline void gen_intermediate_code_internal(CPUS390XState *env,
     dc.tb = tb;
     dc.pc = pc_start;
     dc.cc_op = CC_OP_DYNAMIC;
-    dc.singlestep_enabled = env->singlestep_enabled;
+    do_debug = dc.singlestep_enabled = env->singlestep_enabled;
     dc.is_jmp = DISAS_NEXT;
 
     gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
@@ -5479,14 +5419,6 @@  static inline void gen_intermediate_code_internal(CPUS390XState *env,
     gen_icount_start();
 
     do {
-        if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) {
-            QTAILQ_FOREACH(bp, &env->breakpoints, entry) {
-                if (bp->pc == dc.pc) {
-                    gen_debug(&dc);
-                    break;
-                }
-            }
-        }
         if (search_pc) {
             j = gen_opc_ptr - gen_opc_buf;
             if (lj < j) {
@@ -5508,7 +5440,19 @@  static inline void gen_intermediate_code_internal(CPUS390XState *env,
             tcg_gen_debug_insn_start(dc.pc);
         }
 
-        status = translate_one(env, &dc);
+        status = NO_EXIT;
+        if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) {
+            QTAILQ_FOREACH(bp, &env->breakpoints, entry) {
+                if (bp->pc == dc.pc) {
+                    status = EXIT_PC_STALE;
+                    do_debug = true;
+                    break;
+                }
+            }
+        }
+        if (status == NO_EXIT) {
+            status = translate_one(env, &dc);
+        }
 
         /* If we reach a page boundary, are single stepping,
            or exhaust instruction count, stop generation.  */
@@ -5541,8 +5485,8 @@  static inline void gen_intermediate_code_internal(CPUS390XState *env,
                so make sure the cc op type is in env */
             gen_op_set_cc_op(&dc);
         }
-        if (env->singlestep_enabled) {
-            gen_debug(&dc);
+        if (do_debug) {
+            gen_exception(EXCP_DEBUG);
         } else {
             /* Generate the return instruction */
             tcg_gen_exit_tb(0);