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>
This commit is contained in:
Max Filippov 2011-10-10 06:25:04 +04:00 committed by Blue Swirl
parent 9870a5e6cd
commit 890c6333b2
4 changed files with 93 additions and 16 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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);
}

View File

@ -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