target/mips: save CP0 timer in vmstate

The MIPS R4K CP0 timer (env->timer) is not included in vmstate_mips_cpu,
so after loadvm the QEMUTimer has no scheduled expiry. This causes
qemu_poll_ns() to block indefinitely and the guest to freeze until an
external I/O event (e.g. a keypress) wakes the main loop.

Fix by adding an optional vmstate subsection for the timer, following
the same pattern used by ARM (gt_timer), RISC-V (env.stimer), SPARC
(qtimer), and OpenRISC (timer).

The .needed callback returns false when env->timer is NULL (KVM mode),
keeping the subsection optional for backwards compatibility with
existing snapshots.

Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1987
Signed-off-by: Trieu Huynh <vikingtc4@gmail.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Message-ID: <20260329113732.482619-1-vikingtc4@gmail.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
This commit is contained in:
Trieu Huynh
2026-03-29 20:37:32 +09:00
committed by Philippe Mathieu-Daudé
parent 946e4d6506
commit 07ec1a7235

View File

@@ -3,6 +3,7 @@
#include "internal.h"
#include "migration/cpu.h"
#include "fpu_helper.h"
#include "qemu/timer.h"
static int cpu_post_load(void *opaque, int version_id)
{
@@ -219,6 +220,23 @@ static const VMStateDescription vmstate_tlb = {
/* MIPS CPU state */
static bool mips_timer_needed(void *opaque)
{
MIPSCPU *cpu = opaque;
return cpu->env.timer != NULL;
}
static const VMStateDescription mips_vmstate_timer = {
.name = "cpu/timer",
.version_id = 1,
.minimum_version_id = 1,
.needed = mips_timer_needed,
.fields = (const VMStateField[]) {
VMSTATE_TIMER_PTR(env.timer, MIPSCPU),
VMSTATE_END_OF_LIST()
}
};
const VMStateDescription vmstate_mips_cpu = {
.name = "cpu",
.version_id = 21,
@@ -333,4 +351,8 @@ const VMStateDescription vmstate_mips_cpu = {
VMSTATE_END_OF_LIST()
},
.subsections = (const VMStateDescription * const []) {
&mips_vmstate_timer,
NULL
}
};