On Thu, Sep 11, 2025 at 7:58 PM TANG Tiancheng <[email protected]> wrote:
>
> In QEMU's RISC-V ACLINT timer model, 'mtime' is not stored directly as a
> state variable. It is computed on demand as:
>
>     mtime = rtc_r + time_delta
>
> where:
> - 'rtc_r' is the current VM virtual time (in ticks) obtained via
>   cpu_riscv_read_rtc_raw() from QEMU_CLOCK_VIRTUAL.
> - 'time_delta' is an offset applied when the guest writes a new 'mtime'
>   value via riscv_aclint_mtimer_write():
>
>     time_delta = value - rtc_r
>
> Under this design, 'rtc_r' is assumed to be monotonically increasing
> during VM execution. Even if the guest writes an 'mtime' value smaller
> than the current one (making 'time_delta' negative in signed arithmetic,
> or underflow in unsigned arithmetic), the computed 'mtime' remains
> correct because 'rtc_r_new > rtc_r_old':
>
>     mtime_new = rtc_r_new + (value - rtc_r_old)
>
> However, this monotonicity assumption breaks on snapshot load.
>
> Before restoring a snapshot, QEMU resets the guest, which calls
> riscv_aclint_mtimer_reset_enter() to set 'mtime' to 0 and recompute
> 'time_delta' as:
>
>     time_delta = 0 - rtc_r_reset
>
> Here, the time_delta differs from the value that was present when the
> snapshot was saved. As a result, subsequent reads produce a fixed offset
> from the true mtime.
>
> This can be observed with the 'date' command inside the guest: after loading
> a snapshot, the reported time appears "frozen" at the save point, and only
> resumes correctly after the guest has run long enough to compensate for the
> erroneous offset.
>
> The fix is to treat 'time_delta' as part of the device's migratable
> state and save/restore it via vmstate. This preserves the correct
> relation between 'rtc_r' and 'mtime' across snapshot save/load, ensuring
> 'mtime' continues incrementing from the precise saved value after
> restore.
>
> Reviewed-by: LIU Zhiwei <[email protected]>
> Reviewed-by: Daniel Henrique Barboza <[email protected]>
> Signed-off-by: TANG Tiancheng <[email protected]>

Reviewed-by: Alistair Francis <[email protected]>

Alistair

> ---
>  hw/intc/riscv_aclint.c | 5 +++--
>  1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/hw/intc/riscv_aclint.c b/hw/intc/riscv_aclint.c
> index 
> 4623cfa029365c6cbdead4bd4a9f0d8b9e88b939..318a9c8248432a8cd4c3f3fa990739917ecf7ca1
>  100644
> --- a/hw/intc/riscv_aclint.c
> +++ b/hw/intc/riscv_aclint.c
> @@ -323,9 +323,10 @@ static void riscv_aclint_mtimer_reset_enter(Object *obj, 
> ResetType type)
>
>  static const VMStateDescription vmstate_riscv_mtimer = {
>      .name = "riscv_mtimer",
> -    .version_id = 1,
> -    .minimum_version_id = 1,
> +    .version_id = 2,
> +    .minimum_version_id = 2,
>      .fields = (const VMStateField[]) {
> +            VMSTATE_UINT64(time_delta, RISCVAclintMTimerState),
>              VMSTATE_VARRAY_UINT32(timecmp, RISCVAclintMTimerState,
>                                    num_harts, 0,
>                                    vmstate_info_uint64, uint64_t),
>
> --
> 2.43.0
>
>

Reply via email to