diff mbox

MIPS interrupts and -icount

Message ID 20100725044441.GD22478@hall.aurel32.net
State New
Headers show

Commit Message

Aurelien Jarno July 25, 2010, 4:44 a.m. UTC
On Sun, Jul 25, 2010 at 05:08:07AM +0200, Aurelien Jarno wrote:
> On Sun, Jul 25, 2010 at 02:07:54AM +0200, Edgar E. Iglesias wrote:
> > On Sun, Jul 25, 2010 at 12:55:45AM +0200, Aurelien Jarno wrote:
> > > On Thu, Jul 22, 2010 at 01:32:18PM +0200, Edgar E. Iglesias wrote:
> > > > Hi,
> > > > 
> > > > I'm seeing an error when emulating MIPS guests with -icount.
> > > > In cpu_interrupt:
> > > >   cpu_abort(env, "Raised interrupt while not in I/O function");
> > > 
> > > I am not able to reproduce the issue. Do you have more details how to
> > > reproduce the problem?
> > 
> > You need a machine with devices that raise the hw interrupts. I didn't
> > see the error on the images on the wiki though. But I've got a machine
> > here that trigs it easily. Will check if I can publish it and an image.
> > 
> 
> That would be nice if you can share it.
> 
> > > > It seems to me like the MIPS interrupt glue logic between interrupt
> > > > controllers and the MIPS core is not modeled correctly.
> > > 
> > > It seems indeed that sometimes interrupt are triggered while not in 
> > > I/O functions, your patch addresses part of the problem.
> > > 
> > > > When hw interrupt pending bits in CP0_Cause are set, the CPU should
> > > > see the hw interrupt line as active. The CPU may or may not take the
> > > > interrupt based on internal state (global irq mask etc) but the glue
> > > > logic shouldn't care about that. Am I missing something here?
> > > 
> > > I don't think it is correct. On the real hardware, the interrupt line is
> > > actually active only when all conditions are fulfilled.
> > > 
> > > The thing to remember is that the interrupts are level triggered. So if
> > > an interrupt is masked, it should be rejected by the CPU, but could be
> > > triggered again as soon as the interrupt mask is changed.
> > 
> > Agreed, that behaviour is what I'm trying to acheive. The problem now
> > is that the the level triggered line, prior to CPU masking is beeing masked
> > by external logic. IMO it shouldnt. See hw/mips_int.c and cpu-exec.c prior
> > to the patch.
> 
> Actually all depends if you consider the MIPS interrupt controller part
> of the CPU or not. It could be entirely modeled in the CPU, that is in 
> cpu-exec.c or entirely modeled as a separate controller, that is in 
> mips_int.c.
>

If we choose having the interrupt controller as part of the CPU, which
seems to be what you have chosen, the following patch should fix the
remaining issues (and prepare the work for vector interrupt support):

Comments

Edgar E. Iglesias July 25, 2010, 5:52 a.m. UTC | #1
On Sun, Jul 25, 2010 at 06:44:41AM +0200, Aurelien Jarno wrote:
> On Sun, Jul 25, 2010 at 05:08:07AM +0200, Aurelien Jarno wrote:
> > On Sun, Jul 25, 2010 at 02:07:54AM +0200, Edgar E. Iglesias wrote:
> > > On Sun, Jul 25, 2010 at 12:55:45AM +0200, Aurelien Jarno wrote:
> > > > On Thu, Jul 22, 2010 at 01:32:18PM +0200, Edgar E. Iglesias wrote:
> > > > > Hi,
> > > > > 
> > > > > I'm seeing an error when emulating MIPS guests with -icount.
> > > > > In cpu_interrupt:
> > > > >   cpu_abort(env, "Raised interrupt while not in I/O function");
> > > > 
> > > > I am not able to reproduce the issue. Do you have more details how to
> > > > reproduce the problem?
> > > 
> > > You need a machine with devices that raise the hw interrupts. I didn't
> > > see the error on the images on the wiki though. But I've got a machine
> > > here that trigs it easily. Will check if I can publish it and an image.
> > > 
> > 
> > That would be nice if you can share it.
> > 
> > > > > It seems to me like the MIPS interrupt glue logic between interrupt
> > > > > controllers and the MIPS core is not modeled correctly.
> > > > 
> > > > It seems indeed that sometimes interrupt are triggered while not in 
> > > > I/O functions, your patch addresses part of the problem.
> > > > 
> > > > > When hw interrupt pending bits in CP0_Cause are set, the CPU should
> > > > > see the hw interrupt line as active. The CPU may or may not take the
> > > > > interrupt based on internal state (global irq mask etc) but the glue
> > > > > logic shouldn't care about that. Am I missing something here?
> > > > 
> > > > I don't think it is correct. On the real hardware, the interrupt line is
> > > > actually active only when all conditions are fulfilled.
> > > > 
> > > > The thing to remember is that the interrupts are level triggered. So if
> > > > an interrupt is masked, it should be rejected by the CPU, but could be
> > > > triggered again as soon as the interrupt mask is changed.
> > > 
> > > Agreed, that behaviour is what I'm trying to acheive. The problem now
> > > is that the the level triggered line, prior to CPU masking is beeing masked
> > > by external logic. IMO it shouldnt. See hw/mips_int.c and cpu-exec.c prior
> > > to the patch.
> > 
> > Actually all depends if you consider the MIPS interrupt controller part
> > of the CPU or not. It could be entirely modeled in the CPU, that is in 
> > cpu-exec.c or entirely modeled as a separate controller, that is in 
> > mips_int.c.
> >
> 
> If we choose having the interrupt controller as part of the CPU, which
> seems to be what you have chosen, the following patch should fix the
> remaining issues (and prepare the work for vector interrupt support):

Thanks,

That looks nice, specially the removing of the helper_interrupt_restart
parts :)

I'll test it on my side and send you an ACK.

Thanks for your patience Aurelien,
Edgar


> 
> 
> diff --git a/hw/mips_int.c b/hw/mips_int.c
> index 80488ba..477f6ab 100644
> --- a/hw/mips_int.c
> +++ b/hw/mips_int.c
> @@ -54,3 +54,12 @@ void cpu_mips_irq_init_cpu(CPUState *env)
>          env->irq[i] = qi[i];
>      }
>  }
> +
> +void cpu_mips_soft_irq(CPUState *env, int irq, int level)
> +{
> +    if (irq < 0 || irq > 2) {
> +        return;
> +    }
> +
> +    qemu_set_irq(env->irq[irq], level);
> +}
> diff --git a/target-mips/cpu.h b/target-mips/cpu.h
> index 1578850..b8e6fee 100644
> --- a/target-mips/cpu.h
> +++ b/target-mips/cpu.h
> @@ -597,6 +597,9 @@ void cpu_mips_store_compare (CPUState *env, uint32_t value);
>  void cpu_mips_start_count(CPUState *env);
>  void cpu_mips_stop_count(CPUState *env);
>  
> +/* mips_int.c */
> +void cpu_mips_soft_irq(CPUState *env, int irq, int level);
> +
>  /* helper.c */
>  int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
>                                 int mmu_idx, int is_softmmu);
> diff --git a/target-mips/helper.h b/target-mips/helper.h
> index a6ba75d..cb13fb2 100644
> --- a/target-mips/helper.h
> +++ b/target-mips/helper.h
> @@ -2,7 +2,6 @@
>  
>  DEF_HELPER_2(raise_exception_err, void, i32, int)
>  DEF_HELPER_1(raise_exception, void, i32)
> -DEF_HELPER_0(interrupt_restart, void)
>  
>  #ifdef TARGET_MIPS64
>  DEF_HELPER_3(ldl, tl, tl, tl, int)
> diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c
> index c963224..8482e69 100644
> --- a/target-mips/op_helper.c
> +++ b/target-mips/op_helper.c
> @@ -46,18 +46,6 @@ void helper_raise_exception (uint32_t exception)
>      helper_raise_exception_err(exception, 0);
>  }
>  
> -void helper_interrupt_restart (void)
> -{
> -    if (!(env->CP0_Status & (1 << CP0St_EXL)) &&
> -        !(env->CP0_Status & (1 << CP0St_ERL)) &&
> -        !(env->hflags & MIPS_HFLAG_DM) &&
> -        (env->CP0_Status & (1 << CP0St_IE)) &&
> -        (env->CP0_Status & env->CP0_Cause & CP0Ca_IP_mask)) {
> -        env->CP0_Cause &= ~(0x1f << CP0Ca_EC);
> -        helper_raise_exception(EXCP_EXT_INTERRUPT);
> -    }
> -}
> -
>  #if !defined(CONFIG_USER_ONLY)
>  static void do_restore_state (void *pc_ptr)
>  {
> @@ -1346,6 +1334,7 @@ void helper_mtc0_cause (target_ulong arg1)
>  {
>      uint32_t mask = 0x00C00300;
>      uint32_t old = env->CP0_Cause;
> +    int i;
>  
>      if (env->insn_flags & ISA_MIPS32R2)
>          mask |= 1 << CP0Ca_DC;
> @@ -1358,6 +1347,13 @@ void helper_mtc0_cause (target_ulong arg1)
>          else
>              cpu_mips_start_count(env);
>      }
> +
> +    /* Set/reset software interrupts */
> +    for (i = 0 ; i < 2 ; i++) {
> +        if ((old ^ env->CP0_Cause) & (1 << (CP0Ca_IP + i))) {
> +            cpu_mips_soft_irq(env, i, env->CP0_Cause & (1 << (CP0Ca_IP + i)));
> +        }
> +    }
>  }
>  
>  void helper_mtc0_ebase (target_ulong arg1)
> diff --git a/target-mips/translate.c b/target-mips/translate.c
> index 7168273..6c72dee 100644
> --- a/target-mips/translate.c
> +++ b/target-mips/translate.c
> @@ -5190,7 +5190,17 @@ static void gen_dmtc0 (CPUState *env, DisasContext *ctx, TCGv arg, int reg, int
>          switch (sel) {
>          case 0:
>              save_cpu_state(ctx, 1);
> +            /* Mark as an IO operation because we may trigger a software
> +               interrupt.  */
> +            if (use_icount) {
> +                gen_io_start();
> +            }
>              gen_helper_mtc0_cause(arg);
> +            if (use_icount) {
> +                gen_io_end();
> +            }
> +            /* Stop translation as we may have triggered an intetrupt */
> +            ctx->bstate = BS_STOP;
>              rn = "Cause";
>              break;
>          default:
> @@ -12365,7 +12375,6 @@ gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
>      } else {
>          switch (ctx.bstate) {
>          case BS_STOP:
> -            gen_helper_interrupt_restart();
>              gen_goto_tb(&ctx, 0, ctx.pc);
>              break;
>          case BS_NONE:
> @@ -12373,7 +12382,6 @@ gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
>              gen_goto_tb(&ctx, 0, ctx.pc);
>              break;
>          case BS_EXCP:
> -            gen_helper_interrupt_restart();
>              tcg_gen_exit_tb(0);
>              break;
>          case BS_BRANCH:
> 
> -- 
> Aurelien Jarno	                        GPG: 1024D/F1BCDB73
> aurelien@aurel32.net                 http://www.aurel32.net
Edgar E. Iglesias July 25, 2010, 7:21 a.m. UTC | #2
On Sun, Jul 25, 2010 at 07:52:18AM +0200, Edgar E. Iglesias wrote:
> On Sun, Jul 25, 2010 at 06:44:41AM +0200, Aurelien Jarno wrote:
> > On Sun, Jul 25, 2010 at 05:08:07AM +0200, Aurelien Jarno wrote:
> > > On Sun, Jul 25, 2010 at 02:07:54AM +0200, Edgar E. Iglesias wrote:
> > > > On Sun, Jul 25, 2010 at 12:55:45AM +0200, Aurelien Jarno wrote:
> > > > > On Thu, Jul 22, 2010 at 01:32:18PM +0200, Edgar E. Iglesias wrote:
> > > > > > Hi,
> > > > > > 
> > > > > > I'm seeing an error when emulating MIPS guests with -icount.
> > > > > > In cpu_interrupt:
> > > > > >   cpu_abort(env, "Raised interrupt while not in I/O function");
> > > > > 
> > > > > I am not able to reproduce the issue. Do you have more details how to
> > > > > reproduce the problem?
> > > > 
> > > > You need a machine with devices that raise the hw interrupts. I didn't
> > > > see the error on the images on the wiki though. But I've got a machine
> > > > here that trigs it easily. Will check if I can publish it and an image.
> > > > 
> > > 
> > > That would be nice if you can share it.
> > > 
> > > > > > It seems to me like the MIPS interrupt glue logic between interrupt
> > > > > > controllers and the MIPS core is not modeled correctly.
> > > > > 
> > > > > It seems indeed that sometimes interrupt are triggered while not in 
> > > > > I/O functions, your patch addresses part of the problem.
> > > > > 
> > > > > > When hw interrupt pending bits in CP0_Cause are set, the CPU should
> > > > > > see the hw interrupt line as active. The CPU may or may not take the
> > > > > > interrupt based on internal state (global irq mask etc) but the glue
> > > > > > logic shouldn't care about that. Am I missing something here?
> > > > > 
> > > > > I don't think it is correct. On the real hardware, the interrupt line is
> > > > > actually active only when all conditions are fulfilled.
> > > > > 
> > > > > The thing to remember is that the interrupts are level triggered. So if
> > > > > an interrupt is masked, it should be rejected by the CPU, but could be
> > > > > triggered again as soon as the interrupt mask is changed.
> > > > 
> > > > Agreed, that behaviour is what I'm trying to acheive. The problem now
> > > > is that the the level triggered line, prior to CPU masking is beeing masked
> > > > by external logic. IMO it shouldnt. See hw/mips_int.c and cpu-exec.c prior
> > > > to the patch.
> > > 
> > > Actually all depends if you consider the MIPS interrupt controller part
> > > of the CPU or not. It could be entirely modeled in the CPU, that is in 
> > > cpu-exec.c or entirely modeled as a separate controller, that is in 
> > > mips_int.c.
> > >
> > 
> > If we choose having the interrupt controller as part of the CPU, which
> > seems to be what you have chosen, the following patch should fix the
> > remaining issues (and prepare the work for vector interrupt support):
> 
> Thanks,
> 
> That looks nice, specially the removing of the helper_interrupt_restart
> parts :)
> 
> I'll test it on my side and send you an ACK.


I've tested this with the same images as before, they still work.
I also created a synthetic testcase for the 2 software interrupt
lines and they seem to behave OK. Our linux port was not so
happy about seeing those raised though, so I had to hack a bit
to see them in action :)

Acked-by: Edgar E. Iglesias <edgar@axis.com>
Tested-by: Edgar E. Iglesias <edgar@axis.com>

Would you mind applying your patch on top of mine?

Thanks alot,
Edgar


> 
> Thanks for your patience Aurelien,
> Edgar
> 
> 
> > 
> > 
> > diff --git a/hw/mips_int.c b/hw/mips_int.c
> > index 80488ba..477f6ab 100644
> > --- a/hw/mips_int.c
> > +++ b/hw/mips_int.c
> > @@ -54,3 +54,12 @@ void cpu_mips_irq_init_cpu(CPUState *env)
> >          env->irq[i] = qi[i];
> >      }
> >  }
> > +
> > +void cpu_mips_soft_irq(CPUState *env, int irq, int level)
> > +{
> > +    if (irq < 0 || irq > 2) {
> > +        return;
> > +    }
> > +
> > +    qemu_set_irq(env->irq[irq], level);
> > +}
> > diff --git a/target-mips/cpu.h b/target-mips/cpu.h
> > index 1578850..b8e6fee 100644
> > --- a/target-mips/cpu.h
> > +++ b/target-mips/cpu.h
> > @@ -597,6 +597,9 @@ void cpu_mips_store_compare (CPUState *env, uint32_t value);
> >  void cpu_mips_start_count(CPUState *env);
> >  void cpu_mips_stop_count(CPUState *env);
> >  
> > +/* mips_int.c */
> > +void cpu_mips_soft_irq(CPUState *env, int irq, int level);
> > +
> >  /* helper.c */
> >  int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
> >                                 int mmu_idx, int is_softmmu);
> > diff --git a/target-mips/helper.h b/target-mips/helper.h
> > index a6ba75d..cb13fb2 100644
> > --- a/target-mips/helper.h
> > +++ b/target-mips/helper.h
> > @@ -2,7 +2,6 @@
> >  
> >  DEF_HELPER_2(raise_exception_err, void, i32, int)
> >  DEF_HELPER_1(raise_exception, void, i32)
> > -DEF_HELPER_0(interrupt_restart, void)
> >  
> >  #ifdef TARGET_MIPS64
> >  DEF_HELPER_3(ldl, tl, tl, tl, int)
> > diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c
> > index c963224..8482e69 100644
> > --- a/target-mips/op_helper.c
> > +++ b/target-mips/op_helper.c
> > @@ -46,18 +46,6 @@ void helper_raise_exception (uint32_t exception)
> >      helper_raise_exception_err(exception, 0);
> >  }
> >  
> > -void helper_interrupt_restart (void)
> > -{
> > -    if (!(env->CP0_Status & (1 << CP0St_EXL)) &&
> > -        !(env->CP0_Status & (1 << CP0St_ERL)) &&
> > -        !(env->hflags & MIPS_HFLAG_DM) &&
> > -        (env->CP0_Status & (1 << CP0St_IE)) &&
> > -        (env->CP0_Status & env->CP0_Cause & CP0Ca_IP_mask)) {
> > -        env->CP0_Cause &= ~(0x1f << CP0Ca_EC);
> > -        helper_raise_exception(EXCP_EXT_INTERRUPT);
> > -    }
> > -}
> > -
> >  #if !defined(CONFIG_USER_ONLY)
> >  static void do_restore_state (void *pc_ptr)
> >  {
> > @@ -1346,6 +1334,7 @@ void helper_mtc0_cause (target_ulong arg1)
> >  {
> >      uint32_t mask = 0x00C00300;
> >      uint32_t old = env->CP0_Cause;
> > +    int i;
> >  
> >      if (env->insn_flags & ISA_MIPS32R2)
> >          mask |= 1 << CP0Ca_DC;
> > @@ -1358,6 +1347,13 @@ void helper_mtc0_cause (target_ulong arg1)
> >          else
> >              cpu_mips_start_count(env);
> >      }
> > +
> > +    /* Set/reset software interrupts */
> > +    for (i = 0 ; i < 2 ; i++) {
> > +        if ((old ^ env->CP0_Cause) & (1 << (CP0Ca_IP + i))) {
> > +            cpu_mips_soft_irq(env, i, env->CP0_Cause & (1 << (CP0Ca_IP + i)));
> > +        }
> > +    }
> >  }
> >  
> >  void helper_mtc0_ebase (target_ulong arg1)
> > diff --git a/target-mips/translate.c b/target-mips/translate.c
> > index 7168273..6c72dee 100644
> > --- a/target-mips/translate.c
> > +++ b/target-mips/translate.c
> > @@ -5190,7 +5190,17 @@ static void gen_dmtc0 (CPUState *env, DisasContext *ctx, TCGv arg, int reg, int
> >          switch (sel) {
> >          case 0:
> >              save_cpu_state(ctx, 1);
> > +            /* Mark as an IO operation because we may trigger a software
> > +               interrupt.  */
> > +            if (use_icount) {
> > +                gen_io_start();
> > +            }
> >              gen_helper_mtc0_cause(arg);
> > +            if (use_icount) {
> > +                gen_io_end();
> > +            }
> > +            /* Stop translation as we may have triggered an intetrupt */
> > +            ctx->bstate = BS_STOP;
> >              rn = "Cause";
> >              break;
> >          default:
> > @@ -12365,7 +12375,6 @@ gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
> >      } else {
> >          switch (ctx.bstate) {
> >          case BS_STOP:
> > -            gen_helper_interrupt_restart();
> >              gen_goto_tb(&ctx, 0, ctx.pc);
> >              break;
> >          case BS_NONE:
> > @@ -12373,7 +12382,6 @@ gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
> >              gen_goto_tb(&ctx, 0, ctx.pc);
> >              break;
> >          case BS_EXCP:
> > -            gen_helper_interrupt_restart();
> >              tcg_gen_exit_tb(0);
> >              break;
> >          case BS_BRANCH:
> > 
> > -- 
> > Aurelien Jarno	                        GPG: 1024D/F1BCDB73
> > aurelien@aurel32.net                 http://www.aurel32.net
Aurelien Jarno July 25, 2010, 2:58 p.m. UTC | #3
On Sun, Jul 25, 2010 at 09:21:48AM +0200, Edgar E. Iglesias wrote:
> On Sun, Jul 25, 2010 at 07:52:18AM +0200, Edgar E. Iglesias wrote:
> > On Sun, Jul 25, 2010 at 06:44:41AM +0200, Aurelien Jarno wrote:
> > > On Sun, Jul 25, 2010 at 05:08:07AM +0200, Aurelien Jarno wrote:
> > > > On Sun, Jul 25, 2010 at 02:07:54AM +0200, Edgar E. Iglesias wrote:
> > > > > On Sun, Jul 25, 2010 at 12:55:45AM +0200, Aurelien Jarno wrote:
> > > > > > On Thu, Jul 22, 2010 at 01:32:18PM +0200, Edgar E. Iglesias wrote:
> > > > > > > Hi,
> > > > > > > 
> > > > > > > I'm seeing an error when emulating MIPS guests with -icount.
> > > > > > > In cpu_interrupt:
> > > > > > >   cpu_abort(env, "Raised interrupt while not in I/O function");
> > > > > > 
> > > > > > I am not able to reproduce the issue. Do you have more details how to
> > > > > > reproduce the problem?
> > > > > 
> > > > > You need a machine with devices that raise the hw interrupts. I didn't
> > > > > see the error on the images on the wiki though. But I've got a machine
> > > > > here that trigs it easily. Will check if I can publish it and an image.
> > > > > 
> > > > 
> > > > That would be nice if you can share it.
> > > > 
> > > > > > > It seems to me like the MIPS interrupt glue logic between interrupt
> > > > > > > controllers and the MIPS core is not modeled correctly.
> > > > > > 
> > > > > > It seems indeed that sometimes interrupt are triggered while not in 
> > > > > > I/O functions, your patch addresses part of the problem.
> > > > > > 
> > > > > > > When hw interrupt pending bits in CP0_Cause are set, the CPU should
> > > > > > > see the hw interrupt line as active. The CPU may or may not take the
> > > > > > > interrupt based on internal state (global irq mask etc) but the glue
> > > > > > > logic shouldn't care about that. Am I missing something here?
> > > > > > 
> > > > > > I don't think it is correct. On the real hardware, the interrupt line is
> > > > > > actually active only when all conditions are fulfilled.
> > > > > > 
> > > > > > The thing to remember is that the interrupts are level triggered. So if
> > > > > > an interrupt is masked, it should be rejected by the CPU, but could be
> > > > > > triggered again as soon as the interrupt mask is changed.
> > > > > 
> > > > > Agreed, that behaviour is what I'm trying to acheive. The problem now
> > > > > is that the the level triggered line, prior to CPU masking is beeing masked
> > > > > by external logic. IMO it shouldnt. See hw/mips_int.c and cpu-exec.c prior
> > > > > to the patch.
> > > > 
> > > > Actually all depends if you consider the MIPS interrupt controller part
> > > > of the CPU or not. It could be entirely modeled in the CPU, that is in 
> > > > cpu-exec.c or entirely modeled as a separate controller, that is in 
> > > > mips_int.c.
> > > >
> > > 
> > > If we choose having the interrupt controller as part of the CPU, which
> > > seems to be what you have chosen, the following patch should fix the
> > > remaining issues (and prepare the work for vector interrupt support):
> > 
> > Thanks,
> > 
> > That looks nice, specially the removing of the helper_interrupt_restart
> > parts :)
> > 
> > I'll test it on my side and send you an ACK.
> 
> 
> I've tested this with the same images as before, they still work.
> I also created a synthetic testcase for the 2 software interrupt
> lines and they seem to behave OK. Our linux port was not so
> happy about seeing those raised though, so I had to hack a bit
> to see them in action :)
> 
> Acked-by: Edgar E. Iglesias <edgar@axis.com>
> Tested-by: Edgar E. Iglesias <edgar@axis.com>
> 
> Would you mind applying your patch on top of mine?
> 

Thanks for the tests, I have just applied it.
diff mbox

Patch

diff --git a/hw/mips_int.c b/hw/mips_int.c
index 80488ba..477f6ab 100644
--- a/hw/mips_int.c
+++ b/hw/mips_int.c
@@ -54,3 +54,12 @@  void cpu_mips_irq_init_cpu(CPUState *env)
         env->irq[i] = qi[i];
     }
 }
+
+void cpu_mips_soft_irq(CPUState *env, int irq, int level)
+{
+    if (irq < 0 || irq > 2) {
+        return;
+    }
+
+    qemu_set_irq(env->irq[irq], level);
+}
diff --git a/target-mips/cpu.h b/target-mips/cpu.h
index 1578850..b8e6fee 100644
--- a/target-mips/cpu.h
+++ b/target-mips/cpu.h
@@ -597,6 +597,9 @@  void cpu_mips_store_compare (CPUState *env, uint32_t value);
 void cpu_mips_start_count(CPUState *env);
 void cpu_mips_stop_count(CPUState *env);
 
+/* mips_int.c */
+void cpu_mips_soft_irq(CPUState *env, int irq, int level);
+
 /* helper.c */
 int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
                                int mmu_idx, int is_softmmu);
diff --git a/target-mips/helper.h b/target-mips/helper.h
index a6ba75d..cb13fb2 100644
--- a/target-mips/helper.h
+++ b/target-mips/helper.h
@@ -2,7 +2,6 @@ 
 
 DEF_HELPER_2(raise_exception_err, void, i32, int)
 DEF_HELPER_1(raise_exception, void, i32)
-DEF_HELPER_0(interrupt_restart, void)
 
 #ifdef TARGET_MIPS64
 DEF_HELPER_3(ldl, tl, tl, tl, int)
diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c
index c963224..8482e69 100644
--- a/target-mips/op_helper.c
+++ b/target-mips/op_helper.c
@@ -46,18 +46,6 @@  void helper_raise_exception (uint32_t exception)
     helper_raise_exception_err(exception, 0);
 }
 
-void helper_interrupt_restart (void)
-{
-    if (!(env->CP0_Status & (1 << CP0St_EXL)) &&
-        !(env->CP0_Status & (1 << CP0St_ERL)) &&
-        !(env->hflags & MIPS_HFLAG_DM) &&
-        (env->CP0_Status & (1 << CP0St_IE)) &&
-        (env->CP0_Status & env->CP0_Cause & CP0Ca_IP_mask)) {
-        env->CP0_Cause &= ~(0x1f << CP0Ca_EC);
-        helper_raise_exception(EXCP_EXT_INTERRUPT);
-    }
-}
-
 #if !defined(CONFIG_USER_ONLY)
 static void do_restore_state (void *pc_ptr)
 {
@@ -1346,6 +1334,7 @@  void helper_mtc0_cause (target_ulong arg1)
 {
     uint32_t mask = 0x00C00300;
     uint32_t old = env->CP0_Cause;
+    int i;
 
     if (env->insn_flags & ISA_MIPS32R2)
         mask |= 1 << CP0Ca_DC;
@@ -1358,6 +1347,13 @@  void helper_mtc0_cause (target_ulong arg1)
         else
             cpu_mips_start_count(env);
     }
+
+    /* Set/reset software interrupts */
+    for (i = 0 ; i < 2 ; i++) {
+        if ((old ^ env->CP0_Cause) & (1 << (CP0Ca_IP + i))) {
+            cpu_mips_soft_irq(env, i, env->CP0_Cause & (1 << (CP0Ca_IP + i)));
+        }
+    }
 }
 
 void helper_mtc0_ebase (target_ulong arg1)
diff --git a/target-mips/translate.c b/target-mips/translate.c
index 7168273..6c72dee 100644
--- a/target-mips/translate.c
+++ b/target-mips/translate.c
@@ -5190,7 +5190,17 @@  static void gen_dmtc0 (CPUState *env, DisasContext *ctx, TCGv arg, int reg, int
         switch (sel) {
         case 0:
             save_cpu_state(ctx, 1);
+            /* Mark as an IO operation because we may trigger a software
+               interrupt.  */
+            if (use_icount) {
+                gen_io_start();
+            }
             gen_helper_mtc0_cause(arg);
+            if (use_icount) {
+                gen_io_end();
+            }
+            /* Stop translation as we may have triggered an intetrupt */
+            ctx->bstate = BS_STOP;
             rn = "Cause";
             break;
         default:
@@ -12365,7 +12375,6 @@  gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
     } else {
         switch (ctx.bstate) {
         case BS_STOP:
-            gen_helper_interrupt_restart();
             gen_goto_tb(&ctx, 0, ctx.pc);
             break;
         case BS_NONE:
@@ -12373,7 +12382,6 @@  gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
             gen_goto_tb(&ctx, 0, ctx.pc);
             break;
         case BS_EXCP:
-            gen_helper_interrupt_restart();
             tcg_gen_exit_tb(0);
             break;
         case BS_BRANCH: