The bcm2836 local timer has a basic "counts down, fires at zero, and reloads to programmed value to count down again" functionality, as documented in https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf
The documentation is very sparse and doesn't say what actually happens if the guest programs the reload value to zero. Currently we trip an assert in this case. Instead, log this as a guest error and disable the timer (which seems a reasonable guess -- effectively the timer will stop counting). Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3395 Signed-off-by: Peter Maydell <[email protected]> --- hw/intc/bcm2836_control.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/hw/intc/bcm2836_control.c b/hw/intc/bcm2836_control.c index f1deafaf7a..d6c6057cd2 100644 --- a/hw/intc/bcm2836_control.c +++ b/hw/intc/bcm2836_control.c @@ -197,12 +197,21 @@ static void bcm2836_control_local_timer_set_next(void *opaque) { BCM2836ControlState *s = opaque; uint64_t next_event; + uint64_t reload_value = LOCALTIMER_VALUE(s->local_timer_control); - assert(LOCALTIMER_VALUE(s->local_timer_control) > 0); + if (reload_value == 0) { + /* + * Spec doesn't say what happens in this case; treat as a + * guest error and stop the timer running. + */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: local timer reload value is 0\n", + __func__); + timer_del(&s->timer); + return; + } next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - muldiv64(LOCALTIMER_VALUE(s->local_timer_control), - NANOSECONDS_PER_SECOND, LOCALTIMER_FREQ); + muldiv64(reload_value, NANOSECONDS_PER_SECOND, LOCALTIMER_FREQ); timer_mod(&s->timer, next_event); } -- 2.43.0
