RISC-V: Setting mtimecmp to -1 immediately triggers an interrupt
Host environment
- Operating system: Arch Linux
- OS/kernel version: 5.12.14-arch1-1
- Architecture: x86
- QEMU flavor: qemu-system-riscv32, qemu-system-riscv64
- QEMU version: 6.0.50
Emulated/Virtualized environment
- Operating system: Bare metal
- Architecture: RISC-V
Description of problem
When setting mtimecmp to -1, which should set a timer infinitely far in the future, a timer interrupt is triggered immediately. This happens for most values over 2^61. It is the same for both 32-bit and 64-bit, and for M-mode writing to mtimecmp directly and S-mode using OpenSBI.
I have looked through the source code, and the problem is in the function sifive_clint_write_mtimecmp
, in the file /hw/intc/sifive_clint.c
. First, the muldiv64 multiplies diff with 100, causing an overflow (at least for -M virt, other machines might use a different timebase_freq). Then, the unsigned next
is passed to timer_mod
, which takes a signed integer. In timer_mod_ns_locked
the value is set to MAX(next, 0)
, which means that if the MSB of next
was set, the interrupt happens immediately. This means that it is impossible to set timers more than 2^63 nanoseconds in the future.
This problem basically only affects programs which disable timer interrupts by setting the next one infinitely far in the future. However, the SBI doc specifically says that this is a valid approach, so it should be supported. Using the MSB doesn't work without changing code functionality in QEMU, but it should be sufficient to cap next
at the maximum signed value.