diff options
author | Max Filippov <jcmvbkbc@gmail.com> | 2011-10-10 06:25:04 +0400 |
---|---|---|
committer | Blue Swirl <blauwirbel@gmail.com> | 2011-10-15 21:03:03 +0000 |
commit | 890c6333b28a4dcbf2a26a5a17b0e71304d5a851 (patch) | |
tree | d9243ab3e1a3eab793184eb49134b9c1d9c6fa87 /hw/xtensa_pic.c | |
parent | 9870a5e6cd3df584328728b3d822b23cee28c561 (diff) |
target-xtensa: fix guest hang on masked CCOMPARE interrupt
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>
Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
Diffstat (limited to 'hw/xtensa_pic.c')
-rw-r--r-- | hw/xtensa_pic.c | 27 |
1 files changed, 26 insertions, 1 deletions
diff --git a/hw/xtensa_pic.c b/hw/xtensa_pic.c index 3033ae214a..e5085ea9e7 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) |