Patchwork target-xtensa: fix guest hang on masked CCOMPARE interrupt

login
register
mail settings
Submitter Max Filippov
Date Oct. 10, 2011, 2:25 a.m.
Message ID <1318213505-3168-1-git-send-email-jcmvbkbc@gmail.com>
Download mbox | patch
Permalink /patch/118631/
State New
Headers show

Comments

Max Filippov - Oct. 10, 2011, 2:25 a.m.
QEMU timer is used to post CCOMPARE interrupt when the core is halted.
If that CCOMPARE interrupt is masked off then the timer must be rearmed
in the callback, otherwise it will be rearmed next time the core goes to
halt by the waiti instruction.

Add test case into timer testsuite.

Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
---
 hw/xtensa_pic.c           |   27 ++++++++++++++++++-
 target-xtensa/cpu.h       |    1 +
 target-xtensa/op_helper.c |   18 ++----------
 tests/xtensa/test_timer.S |   63 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 93 insertions(+), 16 deletions(-)
Blue Swirl - Oct. 15, 2011, 9:36 p.m.
Thanks, applied.

On Mon, Oct 10, 2011 at 2:25 AM, Max Filippov <jcmvbkbc@gmail.com> wrote:
> QEMU timer is used to post CCOMPARE interrupt when the core is halted.
> If that CCOMPARE interrupt is masked off then the timer must be rearmed
> in the callback, otherwise it will be rearmed next time the core goes to
> halt by the waiti instruction.
>
> Add test case into timer testsuite.
>
> Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
> ---
>  hw/xtensa_pic.c           |   27 ++++++++++++++++++-
>  target-xtensa/cpu.h       |    1 +
>  target-xtensa/op_helper.c |   18 ++----------
>  tests/xtensa/test_timer.S |   63 +++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 93 insertions(+), 16 deletions(-)
>
> diff --git a/hw/xtensa_pic.c b/hw/xtensa_pic.c
> index 3033ae2..e5085ea 100644
> --- a/hw/xtensa_pic.c
> +++ b/hw/xtensa_pic.c
> @@ -116,10 +116,35 @@ void xtensa_timer_irq(CPUState *env, uint32_t id, uint32_t active)
>     qemu_set_irq(env->irq_inputs[env->config->timerint[id]], active);
>  }
>
> +void xtensa_rearm_ccompare_timer(CPUState *env)
> +{
> +    int i;
> +    uint32_t wake_ccount = env->sregs[CCOUNT] - 1;
> +
> +    for (i = 0; i < env->config->nccompare; ++i) {
> +        if (env->sregs[CCOMPARE + i] - env->sregs[CCOUNT] <
> +                wake_ccount - env->sregs[CCOUNT]) {
> +            wake_ccount = env->sregs[CCOMPARE + i];
> +        }
> +    }
> +    env->wake_ccount = wake_ccount;
> +    qemu_mod_timer(env->ccompare_timer, env->halt_clock +
> +            muldiv64(wake_ccount - env->sregs[CCOUNT],
> +                1000000, env->config->clock_freq_khz));
> +}
> +
>  static void xtensa_ccompare_cb(void *opaque)
>  {
>     CPUState *env = opaque;
> -    xtensa_advance_ccount(env, env->wake_ccount - env->sregs[CCOUNT]);
> +
> +    if (env->halted) {
> +        env->halt_clock = qemu_get_clock_ns(vm_clock);
> +        xtensa_advance_ccount(env, env->wake_ccount - env->sregs[CCOUNT]);
> +        if (!cpu_has_work(env)) {
> +            env->sregs[CCOUNT] = env->wake_ccount + 1;
> +            xtensa_rearm_ccompare_timer(env);
> +        }
> +    }
>  }
>
>  void xtensa_irq_init(CPUState *env)
> diff --git a/target-xtensa/cpu.h b/target-xtensa/cpu.h
> index 339075d..966f515 100644
> --- a/target-xtensa/cpu.h
> +++ b/target-xtensa/cpu.h
> @@ -313,6 +313,7 @@ void check_interrupts(CPUXtensaState *s);
>  void xtensa_irq_init(CPUState *env);
>  void xtensa_advance_ccount(CPUState *env, uint32_t d);
>  void xtensa_timer_irq(CPUState *env, uint32_t id, uint32_t active);
> +void xtensa_rearm_ccompare_timer(CPUState *env);
>  int cpu_xtensa_signal_handler(int host_signum, void *pinfo, void *puc);
>  void xtensa_cpu_list(FILE *f, fprintf_function cpu_fprintf);
>  void xtensa_sync_window_from_phys(CPUState *env);
> diff --git a/target-xtensa/op_helper.c b/target-xtensa/op_helper.c
> index 64847fc..0605611 100644
> --- a/target-xtensa/op_helper.c
> +++ b/target-xtensa/op_helper.c
> @@ -370,23 +370,11 @@ void HELPER(waiti)(uint32_t pc, uint32_t intlevel)
>         return;
>     }
>
> -    if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT)) {
> -        int i;
> -        uint32_t wake_ccount = env->sregs[CCOUNT] - 1;
> -
> -        for (i = 0; i < env->config->nccompare; ++i) {
> -            if (env->sregs[CCOMPARE + i] - env->sregs[CCOUNT] <
> -                    wake_ccount - env->sregs[CCOUNT]) {
> -                wake_ccount = env->sregs[CCOMPARE + i];
> -            }
> -        }
> -        env->wake_ccount = wake_ccount;
> -        qemu_mod_timer(env->ccompare_timer, qemu_get_clock_ns(vm_clock) +
> -                muldiv64(wake_ccount - env->sregs[CCOUNT],
> -                    1000000, env->config->clock_freq_khz));
> -    }
>     env->halt_clock = qemu_get_clock_ns(vm_clock);
>     env->halted = 1;
> +    if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT)) {
> +        xtensa_rearm_ccompare_timer(env);
> +    }
>     HELPER(exception)(EXCP_HLT);
>  }
>
> diff --git a/tests/xtensa/test_timer.S b/tests/xtensa/test_timer.S
> index ede6395..1041cc6 100644
> --- a/tests/xtensa/test_timer.S
> +++ b/tests/xtensa/test_timer.S
> @@ -14,6 +14,7 @@ test ccompare
>     wsr     a2, intenable
>     rsr     a2, interrupt
>     wsr     a2, intclear
> +    movi    a2, 0
>     wsr     a2, ccompare1
>     wsr     a2, ccompare2
>
> @@ -37,6 +38,7 @@ test ccompare0_interrupt
>     wsr     a2, intenable
>     rsr     a2, interrupt
>     wsr     a2, intclear
> +    movi    a2, 0
>     wsr     a2, ccompare1
>     wsr     a2, ccompare2
>
> @@ -66,6 +68,7 @@ test ccompare1_interrupt
>     wsr     a2, intenable
>     rsr     a2, interrupt
>     wsr     a2, intclear
> +    movi    a2, 0
>     wsr     a2, ccompare0
>     wsr     a2, ccompare2
>
> @@ -92,6 +95,7 @@ test ccompare2_interrupt
>     wsr     a2, intenable
>     rsr     a2, interrupt
>     wsr     a2, intclear
> +    movi    a2, 0
>     wsr     a2, ccompare0
>     wsr     a2, ccompare1
>
> @@ -112,4 +116,63 @@ test ccompare2_interrupt
>  2:
>  test_end
>
> +test ccompare_interrupt_masked
> +    set_vector kernel, 2f
> +    movi    a2, 0
> +    wsr     a2, intenable
> +    rsr     a2, interrupt
> +    wsr     a2, intclear
> +    movi    a2, 0
> +    wsr     a2, ccompare2
> +
> +    movi    a3, 40
> +    rsr     a2, ccount
> +    addi    a2, a2, 20
> +    wsr     a2, ccompare1
> +    addi    a2, a2, 20
> +    wsr     a2, ccompare0
> +    rsync
> +    rsr     a2, interrupt
> +    assert  eqi, a2, 0
> +
> +    movi    a2, 0x40
> +    wsr     a2, intenable
> +    rsil    a2, 0
> +    loop    a3, 1f
> +    nop
> +1:
> +    test_fail
> +2:
> +    rsr     a2, exccause
> +    assert  eqi, a2, 4 /* LEVEL1_INTERRUPT_CAUSE */
> +test_end
> +
> +test ccompare_interrupt_masked_waiti
> +    set_vector kernel, 2f
> +    movi    a2, 0
> +    wsr     a2, intenable
> +    rsr     a2, interrupt
> +    wsr     a2, intclear
> +    movi    a2, 0
> +    wsr     a2, ccompare2
> +
> +    movi    a3, 40
> +    rsr     a2, ccount
> +    addi    a2, a2, 20
> +    wsr     a2, ccompare1
> +    addi    a2, a2, 20
> +    wsr     a2, ccompare0
> +    rsync
> +    rsr     a2, interrupt
> +    assert  eqi, a2, 0
> +
> +    movi    a2, 0x40
> +    wsr     a2, intenable
> +    waiti   0
> +    test_fail
> +2:
> +    rsr     a2, exccause
> +    assert  eqi, a2, 4 /* LEVEL1_INTERRUPT_CAUSE */
> +test_end
> +
>  test_suite_end
> --
> 1.7.6.4
>
>
>

Patch

diff --git a/hw/xtensa_pic.c b/hw/xtensa_pic.c
index 3033ae2..e5085ea 100644
--- a/hw/xtensa_pic.c
+++ b/hw/xtensa_pic.c
@@ -116,10 +116,35 @@  void xtensa_timer_irq(CPUState *env, uint32_t id, uint32_t active)
     qemu_set_irq(env->irq_inputs[env->config->timerint[id]], active);
 }
 
+void xtensa_rearm_ccompare_timer(CPUState *env)
+{
+    int i;
+    uint32_t wake_ccount = env->sregs[CCOUNT] - 1;
+
+    for (i = 0; i < env->config->nccompare; ++i) {
+        if (env->sregs[CCOMPARE + i] - env->sregs[CCOUNT] <
+                wake_ccount - env->sregs[CCOUNT]) {
+            wake_ccount = env->sregs[CCOMPARE + i];
+        }
+    }
+    env->wake_ccount = wake_ccount;
+    qemu_mod_timer(env->ccompare_timer, env->halt_clock +
+            muldiv64(wake_ccount - env->sregs[CCOUNT],
+                1000000, env->config->clock_freq_khz));
+}
+
 static void xtensa_ccompare_cb(void *opaque)
 {
     CPUState *env = opaque;
-    xtensa_advance_ccount(env, env->wake_ccount - env->sregs[CCOUNT]);
+
+    if (env->halted) {
+        env->halt_clock = qemu_get_clock_ns(vm_clock);
+        xtensa_advance_ccount(env, env->wake_ccount - env->sregs[CCOUNT]);
+        if (!cpu_has_work(env)) {
+            env->sregs[CCOUNT] = env->wake_ccount + 1;
+            xtensa_rearm_ccompare_timer(env);
+        }
+    }
 }
 
 void xtensa_irq_init(CPUState *env)
diff --git a/target-xtensa/cpu.h b/target-xtensa/cpu.h
index 339075d..966f515 100644
--- a/target-xtensa/cpu.h
+++ b/target-xtensa/cpu.h
@@ -313,6 +313,7 @@  void check_interrupts(CPUXtensaState *s);
 void xtensa_irq_init(CPUState *env);
 void xtensa_advance_ccount(CPUState *env, uint32_t d);
 void xtensa_timer_irq(CPUState *env, uint32_t id, uint32_t active);
+void xtensa_rearm_ccompare_timer(CPUState *env);
 int cpu_xtensa_signal_handler(int host_signum, void *pinfo, void *puc);
 void xtensa_cpu_list(FILE *f, fprintf_function cpu_fprintf);
 void xtensa_sync_window_from_phys(CPUState *env);
diff --git a/target-xtensa/op_helper.c b/target-xtensa/op_helper.c
index 64847fc..0605611 100644
--- a/target-xtensa/op_helper.c
+++ b/target-xtensa/op_helper.c
@@ -370,23 +370,11 @@  void HELPER(waiti)(uint32_t pc, uint32_t intlevel)
         return;
     }
 
-    if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT)) {
-        int i;
-        uint32_t wake_ccount = env->sregs[CCOUNT] - 1;
-
-        for (i = 0; i < env->config->nccompare; ++i) {
-            if (env->sregs[CCOMPARE + i] - env->sregs[CCOUNT] <
-                    wake_ccount - env->sregs[CCOUNT]) {
-                wake_ccount = env->sregs[CCOMPARE + i];
-            }
-        }
-        env->wake_ccount = wake_ccount;
-        qemu_mod_timer(env->ccompare_timer, qemu_get_clock_ns(vm_clock) +
-                muldiv64(wake_ccount - env->sregs[CCOUNT],
-                    1000000, env->config->clock_freq_khz));
-    }
     env->halt_clock = qemu_get_clock_ns(vm_clock);
     env->halted = 1;
+    if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT)) {
+        xtensa_rearm_ccompare_timer(env);
+    }
     HELPER(exception)(EXCP_HLT);
 }
 
diff --git a/tests/xtensa/test_timer.S b/tests/xtensa/test_timer.S
index ede6395..1041cc6 100644
--- a/tests/xtensa/test_timer.S
+++ b/tests/xtensa/test_timer.S
@@ -14,6 +14,7 @@  test ccompare
     wsr     a2, intenable
     rsr     a2, interrupt
     wsr     a2, intclear
+    movi    a2, 0
     wsr     a2, ccompare1
     wsr     a2, ccompare2
 
@@ -37,6 +38,7 @@  test ccompare0_interrupt
     wsr     a2, intenable
     rsr     a2, interrupt
     wsr     a2, intclear
+    movi    a2, 0
     wsr     a2, ccompare1
     wsr     a2, ccompare2
 
@@ -66,6 +68,7 @@  test ccompare1_interrupt
     wsr     a2, intenable
     rsr     a2, interrupt
     wsr     a2, intclear
+    movi    a2, 0
     wsr     a2, ccompare0
     wsr     a2, ccompare2
 
@@ -92,6 +95,7 @@  test ccompare2_interrupt
     wsr     a2, intenable
     rsr     a2, interrupt
     wsr     a2, intclear
+    movi    a2, 0
     wsr     a2, ccompare0
     wsr     a2, ccompare1
 
@@ -112,4 +116,63 @@  test ccompare2_interrupt
 2:
 test_end
 
+test ccompare_interrupt_masked
+    set_vector kernel, 2f
+    movi    a2, 0
+    wsr     a2, intenable
+    rsr     a2, interrupt
+    wsr     a2, intclear
+    movi    a2, 0
+    wsr     a2, ccompare2
+
+    movi    a3, 40
+    rsr     a2, ccount
+    addi    a2, a2, 20
+    wsr     a2, ccompare1
+    addi    a2, a2, 20
+    wsr     a2, ccompare0
+    rsync
+    rsr     a2, interrupt
+    assert  eqi, a2, 0
+
+    movi    a2, 0x40
+    wsr     a2, intenable
+    rsil    a2, 0
+    loop    a3, 1f
+    nop
+1:
+    test_fail
+2:
+    rsr     a2, exccause
+    assert  eqi, a2, 4 /* LEVEL1_INTERRUPT_CAUSE */
+test_end
+
+test ccompare_interrupt_masked_waiti
+    set_vector kernel, 2f
+    movi    a2, 0
+    wsr     a2, intenable
+    rsr     a2, interrupt
+    wsr     a2, intclear
+    movi    a2, 0
+    wsr     a2, ccompare2
+
+    movi    a3, 40
+    rsr     a2, ccount
+    addi    a2, a2, 20
+    wsr     a2, ccompare1
+    addi    a2, a2, 20
+    wsr     a2, ccompare0
+    rsync
+    rsr     a2, interrupt
+    assert  eqi, a2, 0
+
+    movi    a2, 0x40
+    wsr     a2, intenable
+    waiti   0
+    test_fail
+2:
+    rsr     a2, exccause
+    assert  eqi, a2, 4 /* LEVEL1_INTERRUPT_CAUSE */
+test_end
+
 test_suite_end