RISC-V: Setting mtimecmp to -1 immediately triggers an interrupt
- 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
- 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.