Patchwork [010/126] target-s390: Reorg exception handling

login
register
mail settings
Submitter Richard Henderson
Date Sept. 9, 2012, 9:04 p.m.
Message ID <1347224784-19472-11-git-send-email-rth@twiddle.net>
Download mbox | patch
Permalink /patch/182682/
State New
Headers show

Comments

Richard Henderson - Sept. 9, 2012, 9:04 p.m.
Make the user path more like the system path.  Prepare for more kinds
of runtime exceptions.  Compute ILC from S->NEXT_PC, rather than
passing it around.

Signed-off-by: Richard Henderson <rth@twiddle.net>
---
 linux-user/main.c          | 133 +++++++++++++++++----------
 target-s390x/cpu.h         |  11 ---
 target-s390x/misc_helper.c |  10 ++-
 target-s390x/translate.c   | 220 +++++++++++++++++----------------------------
 4 files changed, 174 insertions(+), 200 deletions(-)
Alexander Graf - Sept. 18, 2012, 8:18 p.m.
On 09/09/2012 11:04 PM, Richard Henderson wrote:
> Make the user path more like the system path.  Prepare for more kinds
> of runtime exceptions.  Compute ILC from S->NEXT_PC, rather than
> passing it around.
>
> Signed-off-by: Richard Henderson <rth@twiddle.net>
> ---
>   linux-user/main.c          | 133 +++++++++++++++++----------
>   target-s390x/cpu.h         |  11 ---
>   target-s390x/misc_helper.c |  10 ++-
>   target-s390x/translate.c   | 220 +++++++++++++++++----------------------------
>   4 files changed, 174 insertions(+), 200 deletions(-)
>
> diff --git a/linux-user/main.c b/linux-user/main.c
> index 1a1c661..8f6f39b 100644
> --- a/linux-user/main.c
> +++ b/linux-user/main.c
> @@ -2935,71 +2935,108 @@ 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_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);
>               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_DATA:
> +            case PGM_SPECIAL_OP:
> +            case PGM_OPERAND:
> +                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_HFP_EXP_OVERFLOW:
> +                sig = SIGFPE;
> +                n = TARGET_FPE_FLTOVF;
> +                goto do_signal_pc;
> +            case PGM_HFP_EXP_UNDERFLOW:
> +                sig = SIGFPE;
> +                n = TARGET_FPE_FLTUND;
> +                goto do_signal_pc;
> +            case PGM_HFP_SIGNIFICANCE:
> +                sig = SIGFPE;
> +                n = TARGET_FPE_FLTRES;
> +                goto do_signal_pc;
> +            case PGM_HFP_DIVIDE:
> +                sig = SIGFPE;
> +                n = TARGET_FPE_FLTDIV;
> +                goto do_signal_pc;
> +            case PGM_HFP_SQRT:
> +                sig = SIGFPE;
> +                n = TARGET_FPE_FLTINV;
> +                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/target-s390x/cpu.h b/target-s390x/cpu.h
> index 471fb91..17829b5 100644
> --- a/target-s390x/cpu.h
> +++ b/target-s390x/cpu.h
> @@ -352,21 +352,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)
> diff --git a/target-s390x/misc_helper.c b/target-s390x/misc_helper.c
> index f405b97..1502f15 100644
> --- a/target-s390x/misc_helper.c
> +++ b/target-s390x/misc_helper.c
> @@ -86,10 +86,12 @@ 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_PROTECTION;
> +    /* A real machine puts the address in LowCore, which the kernel
> +       helpfully interprets for us.  Since we've nothing similar
> +       within the userland address space, invent something for use
> +       within cpu_loop.  */
>       env->__excp_addr = address;
>       return 1;
>   }
> diff --git a/target-s390x/translate.c b/target-s390x/translate.c
> index 996d786..70e5d87 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 */
> @@ -336,103 +335,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(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(DisasContext *s, int ilc)
> -{
> -#ifdef DEBUG_ILLEGAL_INSTRUCTIONS
> -    uint64_t inst = 0;
> -
> -    switch (ilc & 3) {
> -    case 1:
> -        inst = ld_code2(s->pc);
> -        break;
> -    case 2:
> -        inst = ld_code4(s->pc);
> -        break;
> -    case 3:
> -        inst = ld_code6(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(DisasContext *s, int ilc, int code)
> +static void gen_program_exception(DisasContext *s, int code)
>   {
>       TCGv_i32 tmp;
>   
> -    debug_print_inst(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);
> +    tmp = tcg_const_i32(s->next_pc - s->pc);

Mind to explain this one?


>       tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUS390XState, int_pgm_ilc));
>       tcg_temp_free_i32(tmp);


Alex
Richard Henderson - Sept. 19, 2012, 12:14 a.m.
On 09/18/2012 01:18 PM, Alexander Graf wrote:
>> -    /* 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);
>> +    tmp = tcg_const_i32(s->next_pc - s->pc);
> 
> Mind to explain this one?

ILC = the size of the insn.  Rather than passing ILC around into
gen_program_exception, get it back from the s->next_pc value that
we stored into DisasContext.


r~
Alexander Graf - Sept. 19, 2012, 11:07 a.m.
On 19.09.2012, at 02:14, Richard Henderson wrote:

> On 09/18/2012 01:18 PM, Alexander Graf wrote:
>>> -    /* 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);
>>> +    tmp = tcg_const_i32(s->next_pc - s->pc);
>> 
>> Mind to explain this one?
> 
> ILC = the size of the insn.  Rather than passing ILC around into
> gen_program_exception, get it back from the s->next_pc value that
> we stored into DisasContext.

Ah, makes sense. Maybe create a small helper that makes it more obvious:

static int current_ilc(DisasContext *s)
{
    /* Next pc - current pc = current instruction length. */
    return s->next_pc - s->pc;
}


Alex
Peter Maydell - Sept. 19, 2012, 11:29 a.m.
On 19 September 2012 01:14, Richard Henderson <rth@twiddle.net> wrote:
> On 09/18/2012 01:18 PM, Alexander Graf wrote:
>>> -    /* remember what pgm exeption this was */
>>> +    /* Remember what pgm exeption this was.  */

...if you're changing this comment then at least fix the typo
("exception")...

>>>       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);
>>> +    tmp = tcg_const_i32(s->next_pc - s->pc);
>>
>> Mind to explain this one?
>
> ILC = the size of the insn.  Rather than passing ILC around into
> gen_program_exception, get it back from the s->next_pc value that
> we stored into DisasContext.

...but isn't (s-next_pc - s->pc) ilc*2, not ilc? Compare this hunk
from elsewhere in the patch:

-        tmp32_2 = tcg_const_i32(ilc * 2);
-        tmp32_3 = tcg_const_i32(EXCP_SVC);
+        tmp32_2 = tcg_const_i32(s->next_pc - s->pc);

-- PMM
Alexander Graf - Sept. 19, 2012, 11:34 a.m.
On 19.09.2012, at 13:29, Peter Maydell wrote:

> On 19 September 2012 01:14, Richard Henderson <rth@twiddle.net> wrote:
>> On 09/18/2012 01:18 PM, Alexander Graf wrote:
>>>> -    /* remember what pgm exeption this was */
>>>> +    /* Remember what pgm exeption this was.  */
> 
> ...if you're changing this comment then at least fix the typo
> ("exception")...
> 
>>>>      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);
>>>> +    tmp = tcg_const_i32(s->next_pc - s->pc);
>>> 
>>> Mind to explain this one?
>> 
>> ILC = the size of the insn.  Rather than passing ILC around into
>> gen_program_exception, get it back from the s->next_pc value that
>> we stored into DisasContext.
> 
> ...but isn't (s-next_pc - s->pc) ilc*2, not ilc? Compare this hunk
> from elsewhere in the patch:
> 
> -        tmp32_2 = tcg_const_i32(ilc * 2);
> -        tmp32_3 = tcg_const_i32(EXCP_SVC);
> +        tmp32_2 = tcg_const_i32(s->next_pc - s->pc);

Yes, it is, and hence it's correct. The exception field encodes the real instruction length, not the ILC bits in the opcode. Maybe the naming is unfortunate...


Alex
Alexander Graf - Sept. 19, 2012, 1:02 p.m.
On 19.09.2012, at 13:34, Alexander Graf wrote:

> 
> On 19.09.2012, at 13:29, Peter Maydell wrote:
> 
>> On 19 September 2012 01:14, Richard Henderson <rth@twiddle.net> wrote:
>>> On 09/18/2012 01:18 PM, Alexander Graf wrote:
>>>>> -    /* remember what pgm exeption this was */
>>>>> +    /* Remember what pgm exeption this was.  */
>> 
>> ...if you're changing this comment then at least fix the typo
>> ("exception")...
>> 
>>>>>     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);
>>>>> +    tmp = tcg_const_i32(s->next_pc - s->pc);
>>>> 
>>>> Mind to explain this one?
>>> 
>>> ILC = the size of the insn.  Rather than passing ILC around into
>>> gen_program_exception, get it back from the s->next_pc value that
>>> we stored into DisasContext.
>> 
>> ...but isn't (s-next_pc - s->pc) ilc*2, not ilc? Compare this hunk
>> from elsewhere in the patch:
>> 
>> -        tmp32_2 = tcg_const_i32(ilc * 2);
>> -        tmp32_3 = tcg_const_i32(EXCP_SVC);
>> +        tmp32_2 = tcg_const_i32(s->next_pc - s->pc);
> 
> Yes, it is, and hence it's correct. The exception field encodes the real instruction length, not the ILC bits in the opcode. Maybe the naming is unfortunate...

Or maybe not. For SVC we seem to pass in the full instruction length, while PGM only gets ILC. So the above hunk is wrong.

Richard, please double-check all the ilc passing again with the spec.


Alex
Richard Henderson - Sept. 19, 2012, 2:34 p.m.
On 09/19/2012 06:02 AM, Alexander Graf wrote:
> Richard, please double-check all the ilc passing again with the spec.

I'll look again.

But I do recall that the spec is consistent in placing a 3-bit field (values 1-3)
shifted one bit left into the larger 32-bit field, so that in essence the value
is always the real instruction length.

However, qemu was not consistent in performing that multiplication.  So there
will be some place that gets changed because of this.  Frankly I think it would
be least confusing to always manipulate the full instruction length and never
explicitly perform the multiply.


r~
Alexander Graf - Sept. 19, 2012, 2:40 p.m.
On 19.09.2012, at 16:34, Richard Henderson wrote:

> On 09/19/2012 06:02 AM, Alexander Graf wrote:
>> Richard, please double-check all the ilc passing again with the spec.
> 
> I'll look again.
> 
> But I do recall that the spec is consistent in placing a 3-bit field (values 1-3)
> shifted one bit left into the larger 32-bit field, so that in essence the value
> is always the real instruction length.
> 
> However, qemu was not consistent in performing that multiplication.  So there
> will be some place that gets changed because of this.  Frankly I think it would
> be least confusing to always manipulate the full instruction length and never
> explicitly perform the multiply.

I agree. How about we rename the whole thing to inst_len and then just modify it accordingly when we move it into the exception area?


Alex

Patch

diff --git a/linux-user/main.c b/linux-user/main.c
index 1a1c661..8f6f39b 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -2935,71 +2935,108 @@  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_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);
             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_DATA:
+            case PGM_SPECIAL_OP:
+            case PGM_OPERAND:
+                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_HFP_EXP_OVERFLOW:
+                sig = SIGFPE;
+                n = TARGET_FPE_FLTOVF;
+                goto do_signal_pc;
+            case PGM_HFP_EXP_UNDERFLOW:
+                sig = SIGFPE;
+                n = TARGET_FPE_FLTUND;
+                goto do_signal_pc;
+            case PGM_HFP_SIGNIFICANCE:
+                sig = SIGFPE;
+                n = TARGET_FPE_FLTRES;
+                goto do_signal_pc;
+            case PGM_HFP_DIVIDE:
+                sig = SIGFPE;
+                n = TARGET_FPE_FLTDIV;
+                goto do_signal_pc;
+            case PGM_HFP_SQRT:
+                sig = SIGFPE;
+                n = TARGET_FPE_FLTINV;
+                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/target-s390x/cpu.h b/target-s390x/cpu.h
index 471fb91..17829b5 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -352,21 +352,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)
diff --git a/target-s390x/misc_helper.c b/target-s390x/misc_helper.c
index f405b97..1502f15 100644
--- a/target-s390x/misc_helper.c
+++ b/target-s390x/misc_helper.c
@@ -86,10 +86,12 @@  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_PROTECTION;
+    /* A real machine puts the address in LowCore, which the kernel
+       helpfully interprets for us.  Since we've nothing similar
+       within the userland address space, invent something for use
+       within cpu_loop.  */
     env->__excp_addr = address;
     return 1;
 }
diff --git a/target-s390x/translate.c b/target-s390x/translate.c
index 996d786..70e5d87 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 */
@@ -336,103 +335,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(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(DisasContext *s, int ilc)
-{
-#ifdef DEBUG_ILLEGAL_INSTRUCTIONS
-    uint64_t inst = 0;
-
-    switch (ilc & 3) {
-    case 1:
-        inst = ld_code2(s->pc);
-        break;
-    case 2:
-        inst = ld_code4(s->pc);
-        break;
-    case 3:
-        inst = ld_code6(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(DisasContext *s, int ilc, int code)
+static void gen_program_exception(DisasContext *s, int code)
 {
     TCGv_i32 tmp;
 
-    debug_print_inst(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);
+    tmp = tcg_const_i32(s->next_pc - s->pc);
     tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUS390XState, int_pgm_ilc));
     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(DisasContext *s, int ilc)
-{
-    gen_program_exception(s, ilc, PGM_SPECIFICATION);
-}
-
-static void gen_privileged_exception(DisasContext *s, int ilc)
+static inline void gen_illegal_opcode(DisasContext *s)
 {
-    gen_program_exception(s, ilc, PGM_PRIVILEGED);
+    gen_program_exception(s, PGM_SPECIFICATION);
 }
 
-static void check_privileged(DisasContext *s, int ilc)
+static inline void check_privileged(DisasContext *s)
 {
     if (s->tb->flags & (PSW_MASK_PSTATE >> 32)) {
-        gen_privileged_exception(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;
@@ -1762,7 +1710,7 @@  static void disas_e3(DisasContext* s, int op, int r1, int x2, int b2, int d2)
         break;
     default:
         LOG_DISAS("illegal e3 operation 0x%x\n", op);
-        gen_illegal_opcode(s, 3);
+        gen_illegal_opcode(s);
         break;
     }
     tcg_temp_free_i64(addr);
@@ -1787,7 +1735,7 @@  static void disas_e5(DisasContext* s, uint64_t insn)
         break;
     default:
         LOG_DISAS("illegal e5 operation 0x%x\n", op);
-        gen_illegal_opcode(s, 3);
+        gen_illegal_opcode(s);
         break;
     }
 
@@ -1801,7 +1749,6 @@  static void disas_eb(DisasContext *s, int op, int r1, int r3, int b2, int d2)
     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);
@@ -1939,7 +1886,7 @@  do_mh:
 #ifndef CONFIG_USER_ONLY
     case 0x2f: /* LCTLG     R1,R3,D2(B2)     [RSE] */
         /* Load Control */
-        check_privileged(s, ilc);
+        check_privileged(s);
         tmp = get_address(s, 0, b2, d2);
         tmp32_1 = tcg_const_i32(r1);
         tmp32_2 = tcg_const_i32(r3);
@@ -1951,7 +1898,7 @@  do_mh:
         break;
     case 0x25: /* STCTG     R1,R3,D2(B2)     [RSE] */
         /* Store Control */
-        check_privileged(s, ilc);
+        check_privileged(s);
         tmp = get_address(s, 0, b2, d2);
         tmp32_1 = tcg_const_i32(r1);
         tmp32_2 = tcg_const_i32(r3);
@@ -2028,7 +1975,7 @@  do_mh:
         break;
     default:
         LOG_DISAS("illegal eb operation 0x%x\n", op);
-        gen_illegal_opcode(s, ilc);
+        gen_illegal_opcode(s);
         break;
     }
 }
@@ -2148,7 +2095,7 @@  static void disas_ed(DisasContext *s, int op, int r1, int x2, int b2, int d2,
         break;
     default:
         LOG_DISAS("illegal ed operation 0x%x\n", op);
-        gen_illegal_opcode(s, 3);
+        gen_illegal_opcode(s);
         return;
     }
     tcg_temp_free_i32(tmp_r1);
@@ -2304,7 +2251,7 @@  static void disas_a5(DisasContext *s, int op, int r1, int i2)
         break;
     default:
         LOG_DISAS("illegal a5 operation 0x%x\n", op);
-        gen_illegal_opcode(s, 2);
+        gen_illegal_opcode(s);
         return;
     }
 }
@@ -2441,7 +2388,7 @@  static void disas_a7(DisasContext *s, int op, int r1, int i2)
         break;
     default:
         LOG_DISAS("illegal a7 operation 0x%x\n", op);
-        gen_illegal_opcode(s, 2);
+        gen_illegal_opcode(s);
         return;
     }
 }
@@ -2451,7 +2398,6 @@  static void disas_b2(DisasContext *s, int op, uint32_t insn)
     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
@@ -2545,7 +2491,7 @@  static void disas_b2(DisasContext *s, int op, uint32_t insn)
 #ifndef CONFIG_USER_ONLY
     case 0x02: /* STIDP     D2(B2)     [S] */
         /* Store CPU ID */
-        check_privileged(s, ilc);
+        check_privileged(s);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
         potential_page_fault(s);
@@ -2554,7 +2500,7 @@  static void disas_b2(DisasContext *s, int op, uint32_t insn)
         break;
     case 0x04: /* SCK       D2(B2)     [S] */
         /* Set Clock */
-        check_privileged(s, ilc);
+        check_privileged(s);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
         potential_page_fault(s);
@@ -2573,7 +2519,7 @@  static void disas_b2(DisasContext *s, int op, uint32_t insn)
         break;
     case 0x06: /* SCKC     D2(B2)     [S] */
         /* Set Clock Comparator */
-        check_privileged(s, ilc);
+        check_privileged(s);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
         potential_page_fault(s);
@@ -2582,7 +2528,7 @@  static void disas_b2(DisasContext *s, int op, uint32_t insn)
         break;
     case 0x07: /* STCKC    D2(B2)     [S] */
         /* Store Clock Comparator */
-        check_privileged(s, ilc);
+        check_privileged(s);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
         potential_page_fault(s);
@@ -2591,7 +2537,7 @@  static void disas_b2(DisasContext *s, int op, uint32_t insn)
         break;
     case 0x08: /* SPT      D2(B2)     [S] */
         /* Set CPU Timer */
-        check_privileged(s, ilc);
+        check_privileged(s);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
         potential_page_fault(s);
@@ -2600,7 +2546,7 @@  static void disas_b2(DisasContext *s, int op, uint32_t insn)
         break;
     case 0x09: /* STPT     D2(B2)     [S] */
         /* Store CPU Timer */
-        check_privileged(s, ilc);
+        check_privileged(s);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
         potential_page_fault(s);
@@ -2609,7 +2555,7 @@  static void disas_b2(DisasContext *s, int op, uint32_t insn)
         break;
     case 0x0a: /* SPKA     D2(B2)     [S] */
         /* Set PSW Key from Address */
-        check_privileged(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();
@@ -2621,12 +2567,12 @@  static void disas_b2(DisasContext *s, int op, uint32_t insn)
         break;
     case 0x0d: /* PTLB                [S] */
         /* Purge TLB */
-        check_privileged(s, ilc);
+        check_privileged(s);
         gen_helper_ptlb(cpu_env);
         break;
     case 0x10: /* SPX      D2(B2)     [S] */
         /* Set Prefix Register */
-        check_privileged(s, ilc);
+        check_privileged(s);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
         potential_page_fault(s);
@@ -2635,7 +2581,7 @@  static void disas_b2(DisasContext *s, int op, uint32_t insn)
         break;
     case 0x11: /* STPX     D2(B2)     [S] */
         /* Store Prefix */
-        check_privileged(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();
@@ -2646,7 +2592,7 @@  static void disas_b2(DisasContext *s, int op, uint32_t insn)
         break;
     case 0x12: /* STAP     D2(B2)     [S] */
         /* Store CPU Address */
-        check_privileged(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();
@@ -2660,7 +2606,7 @@  static void disas_b2(DisasContext *s, int op, uint32_t insn)
         break;
     case 0x21: /* IPTE     R1,R2      [RRE] */
         /* Invalidate PTE */
-        check_privileged(s, ilc);
+        check_privileged(s);
         r1 = (insn >> 4) & 0xf;
         r2 = insn & 0xf;
         tmp = load_reg(r1);
@@ -2671,7 +2617,7 @@  static void disas_b2(DisasContext *s, int op, uint32_t insn)
         break;
     case 0x29: /* ISKE     R1,R2      [RRE] */
         /* Insert Storage Key Extended */
-        check_privileged(s, ilc);
+        check_privileged(s);
         r1 = (insn >> 4) & 0xf;
         r2 = insn & 0xf;
         tmp = load_reg(r2);
@@ -2683,7 +2629,7 @@  static void disas_b2(DisasContext *s, int op, uint32_t insn)
         break;
     case 0x2a: /* RRBE     R1,R2      [RRE] */
         /* Set Storage Key Extended */
-        check_privileged(s, ilc);
+        check_privileged(s);
         r1 = (insn >> 4) & 0xf;
         r2 = insn & 0xf;
         tmp32_1 = load_reg32(r1);
@@ -2695,7 +2641,7 @@  static void disas_b2(DisasContext *s, int op, uint32_t insn)
         break;
     case 0x2b: /* SSKE     R1,R2      [RRE] */
         /* Set Storage Key Extended */
-        check_privileged(s, ilc);
+        check_privileged(s);
         r1 = (insn >> 4) & 0xf;
         r2 = insn & 0xf;
         tmp32_1 = load_reg32(r1);
@@ -2706,12 +2652,12 @@  static void disas_b2(DisasContext *s, int op, uint32_t insn)
         break;
     case 0x34: /* STCH ? */
         /* Store Subchannel */
-        check_privileged(s, ilc);
+        check_privileged(s);
         gen_op_movi_cc(s, 3);
         break;
     case 0x46: /* STURA    R1,R2      [RRE] */
         /* Store Using Real Address */
-        check_privileged(s, ilc);
+        check_privileged(s);
         r1 = (insn >> 4) & 0xf;
         r2 = insn & 0xf;
         tmp32_1 = load_reg32(r1);
@@ -2723,7 +2669,7 @@  static void disas_b2(DisasContext *s, int op, uint32_t insn)
         break;
     case 0x50: /* CSP      R1,R2      [RRE] */
         /* Compare And Swap And Purge */
-        check_privileged(s, ilc);
+        check_privileged(s);
         r1 = (insn >> 4) & 0xf;
         r2 = insn & 0xf;
         tmp32_1 = tcg_const_i32(r1);
@@ -2735,7 +2681,7 @@  static void disas_b2(DisasContext *s, int op, uint32_t insn)
         break;
     case 0x5f: /* CHSC ? */
         /* Channel Subsystem Call */
-        check_privileged(s, ilc);
+        check_privileged(s);
         gen_op_movi_cc(s, 3);
         break;
     case 0x78: /* STCKE    D2(B2)     [S] */
@@ -2749,19 +2695,19 @@  static void disas_b2(DisasContext *s, int op, uint32_t insn)
         break;
     case 0x79: /* SACF    D2(B2)     [S] */
         /* Store Clock Extended */
-        check_privileged(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_EXCP;
         break;
     case 0x7d: /* STSI     D2,(B2)     [S] */
-        check_privileged(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);
@@ -2787,7 +2733,7 @@  static void disas_b2(DisasContext *s, int op, uint32_t insn)
         break;
     case 0xb1: /* STFL     D2(B2)     [S] */
         /* Store Facility List (CPU features) at 200 */
-        check_privileged(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));
@@ -2796,7 +2742,7 @@  static void disas_b2(DisasContext *s, int op, uint32_t insn)
         break;
     case 0xb2: /* LPSWE    D2(B2)     [S] */
         /* Load PSW Extended */
-        check_privileged(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();
@@ -2813,7 +2759,7 @@  static void disas_b2(DisasContext *s, int op, uint32_t insn)
         break;
     case 0x20: /* SERVC     R1,R2     [RRE] */
         /* SCLP Service call (PV hypercall) */
-        check_privileged(s, ilc);
+        check_privileged(s);
         potential_page_fault(s);
         tmp32_1 = load_reg32(r2);
         tmp = load_reg(r1);
@@ -2825,7 +2771,7 @@  static void disas_b2(DisasContext *s, int op, uint32_t insn)
 #endif
     default:
         LOG_DISAS("illegal b2 operation 0x%x\n", op);
-        gen_illegal_opcode(s, ilc);
+        gen_illegal_opcode(s);
         break;
     }
 }
@@ -3100,7 +3046,7 @@  static void disas_b3(DisasContext *s, int op, int m3, int r1, int r2)
         break;
     default:
         LOG_DISAS("illegal b3 operation 0x%x\n", op);
-        gen_illegal_opcode(s, 2);
+        gen_illegal_opcode(s);
         break;
     }
 
@@ -3406,7 +3352,7 @@  static void disas_b9(DisasContext *s, int op, int r1, int r2)
         break;
     default:
         LOG_DISAS("illegal b9 operation 0x%x\n", op);
-        gen_illegal_opcode(s, 2);
+        gen_illegal_opcode(s);
         break;
     }
 }
@@ -3507,7 +3453,7 @@  static void disas_c0(DisasContext *s, int op, int r1, int i2)
         break;
     default:
         LOG_DISAS("illegal c0 operation 0x%x\n", op);
-        gen_illegal_opcode(s, 3);
+        gen_illegal_opcode(s);
         break;
     }
 }
@@ -3540,7 +3486,7 @@  static void disas_c2(DisasContext *s, int op, int r1, int i2)
         break;
     default:
         LOG_DISAS("illegal c2 operation 0x%x\n", op);
-        gen_illegal_opcode(s, 3);
+        gen_illegal_opcode(s);
         break;
     }
 }
@@ -3570,14 +3516,11 @@  static void disas_s390_insn(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(cpu_single_env, s->pc);
     LOG_DISAS("opc 0x%x\n", opc);
 
-    ilc = get_ilc(opc);
-
     switch (opc) {
 #ifndef CONFIG_USER_ONLY
     case 0x01: /* SAM */
@@ -3630,15 +3573,13 @@  static void disas_s390_insn(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);
+        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(s->pc);
@@ -4129,7 +4070,7 @@  static void disas_s390_insn(DisasContext *s)
 #ifndef CONFIG_USER_ONLY
     case 0x80: /* SSM      D2(B2)       [S] */
         /* Set System Mask */
-        check_privileged(s, ilc);
+        check_privileged(s);
         insn = ld_code4(s->pc);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
@@ -4145,7 +4086,7 @@  static void disas_s390_insn(DisasContext *s)
         break;
     case 0x82: /* LPSW     D2(B2)       [S] */
         /* Load PSW */
-        check_privileged(s, ilc);
+        check_privileged(s);
         insn = ld_code4(s->pc);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
@@ -4163,7 +4104,7 @@  static void disas_s390_insn(DisasContext *s)
         break;
     case 0x83: /* DIAG     R1,R3,D2     [RS] */
         /* Diagnose call (KVM hypercall) */
-        check_privileged(s, ilc);
+        check_privileged(s);
         potential_page_fault(s);
         insn = ld_code4(s->pc);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
@@ -4381,7 +4322,7 @@  static void disas_s390_insn(DisasContext *s)
 #ifndef CONFIG_USER_ONLY
     case 0xac: /* STNSM   D1(B1),I2     [SI] */
     case 0xad: /* STOSM   D1(B1),I2     [SI] */
-        check_privileged(s, ilc);
+        check_privileged(s);
         insn = ld_code4(s->pc);
         tmp = decode_si(s, insn, &i2, &b1, &d1);
         tmp2 = tcg_temp_new_i64();
@@ -4397,7 +4338,7 @@  static void disas_s390_insn(DisasContext *s)
         tcg_temp_free_i64(tmp2);
         break;
     case 0xae: /* SIGP   R1,R3,D2(B2)     [RS] */
-        check_privileged(s, ilc);
+        check_privileged(s);
         insn = ld_code4(s->pc);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
@@ -4411,7 +4352,7 @@  static void disas_s390_insn(DisasContext *s)
         tcg_temp_free_i32(tmp32_1);
         break;
     case 0xb1: /* LRA    R1,D2(X2, B2)     [RX] */
-        check_privileged(s, ilc);
+        check_privileged(s);
         insn = ld_code4(s->pc);
         tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
         tmp32_1 = tcg_const_i32(r1);
@@ -4455,7 +4396,7 @@  static void disas_s390_insn(DisasContext *s)
 #ifndef CONFIG_USER_ONLY
     case 0xb6: /* STCTL     R1,R3,D2(B2)     [RS] */
         /* Store Control */
-        check_privileged(s, ilc);
+        check_privileged(s);
         insn = ld_code4(s->pc);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
@@ -4469,7 +4410,7 @@  static void disas_s390_insn(DisasContext *s)
         break;
     case 0xb7: /* LCTL      R1,R3,D2(B2)     [RS] */
         /* Load Control */
-        check_privileged(s, ilc);
+        check_privileged(s);
         insn = ld_code4(s->pc);
         decode_rs(s, insn, &r1, &r3, &b2, &d2);
         tmp = get_address(s, 0, b2, d2);
@@ -4653,7 +4594,7 @@  static void disas_s390_insn(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(s, ilc);
+        check_privileged(s);
         potential_page_fault(s);
         insn = ld_code6(s->pc);
         r1 = (insn >> 36) & 0xf;
@@ -4691,7 +4632,7 @@  static void disas_s390_insn(DisasContext *s)
 #ifndef CONFIG_USER_ONLY
     case 0xe5:
         /* Test Protection */
-        check_privileged(s, ilc);
+        check_privileged(s);
         insn = ld_code6(s->pc);
         debug_insn(insn);
         disas_e5(s, insn);
@@ -4721,7 +4662,7 @@  static void disas_s390_insn(DisasContext *s)
         break;
     default:
         qemu_log_mask(LOG_UNIMP, "unimplemented opcode 0x%x\n", opc);
-        gen_illegal_opcode(s, ilc);
+        gen_illegal_opcode(s);
         break;
     }
 }
@@ -5430,6 +5371,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;
 
@@ -5441,7 +5383,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;
@@ -5457,14 +5399,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) {
@@ -5486,7 +5420,19 @@  static inline void gen_intermediate_code_internal(CPUS390XState *env,
             tcg_gen_debug_insn_start(dc.pc);
         }
 
-        status = translate_one(&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(&dc);
+        }
 
         /* If we reach a page boundary, are single stepping,
            or exhaust instruction count, stop generation.  */
@@ -5519,8 +5465,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);