aboutsummaryrefslogtreecommitdiff
path: root/hw/xtensa_pic.c
diff options
context:
space:
mode:
authorMax Filippov <jcmvbkbc@gmail.com>2011-10-10 06:25:04 +0400
committerBlue Swirl <blauwirbel@gmail.com>2011-10-15 21:03:03 +0000
commit890c6333b28a4dcbf2a26a5a17b0e71304d5a851 (patch)
treed9243ab3e1a3eab793184eb49134b9c1d9c6fa87 /hw/xtensa_pic.c
parent9870a5e6cd3df584328728b3d822b23cee28c561 (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.c27
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)