In the SPARC CPU state, env->regwptr points into the env->regbase
array at wherever the architectural CWP (current window pointer) says
we are in the register windows. We don't migrate this directly,
since it's a host pointer, so we must ensure it is set up again
after migration load.
We also have to deal with a special case when CWP is (nwindows - 1).
In this case, while running we keep the "in" register data for this
window in a temporary location at the end of the regbase[] array, so
that generated code doesn't have to special case this "wrap around"
case. In cpu_pre_save() we call cpu_set_cwp() to force a copy of the
wrapped data from its temporary location into the architectural
location in window 0's "out" registers. We then migrate only
(nwindows * 16) entries in the regbase[] array. So on the
destination we need to copy the "in" register data back to its
temporary location again.
For 32-bit SPARC we get this right, because the CWP is in the PSR.
The get_psr() function does:
env->cwp = 0;
cpu_put_psr_raw(env, val);
which causes cpu_put_psr_raw() to call cpu_set_cwp() in a way that
sets up both regwptr and the wrapped-register data.
However, for 64-bit SPARC the CWP is not in the PSR, and
cpu_put_psr_raw() will not call cpu_set_cwp(). This leaves the guest
register state in a corrupted state, and the guest will likely crash
on the destination if it didn't happen to be executing with CWP == 0.
Fix this by adding a custom vmstate_cwp VMStateInfo with corresponding
get_cwp() and put_cwp() helpers which does the same for the 64-bit
case.
Cc: qemu-stable@nongnu.org
Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
---
target/sparc/machine.c | 40 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 39 insertions(+), 1 deletion(-)
Thanks to Peter for his original patch and excellent analysis on the
cause of the asan failure whilst running the "sparc64-migration"
functional test.
diff --git a/target/sparc/machine.c b/target/sparc/machine.c
index 5f402e098cf..0d5d79b5e7b 100644
--- a/target/sparc/machine.c
+++ b/target/sparc/machine.c
@@ -151,6 +151,37 @@ static const VMStateInfo vmstate_xcc = {
.get = get_xcc,
.put = put_xcc,
};
+
+static int get_cwp(QEMUFile *f, void *opaque, size_t size,
+ const VMStateField *field)
+{
+ SPARCCPU *cpu = opaque;
+ CPUSPARCState *env = &cpu->env;
+ uint32_t val = qemu_get_be32(f);
+
+ /* needed to ensure that the wrapping registers are correctly updated */
+ env->cwp = 0;
+ cpu_set_cwp(env, val);
+
+ return 0;
+}
+
+static int put_cwp(QEMUFile *f, void *opaque, size_t size,
+ const VMStateField *field, JSONWriter *vmdesc)
+{
+ SPARCCPU *cpu = opaque;
+ CPUSPARCState *env = &cpu->env;
+ uint32_t val = env->cwp;
+
+ qemu_put_be32(f, val);
+ return 0;
+}
+
+static const VMStateInfo vmstate_cwp = {
+ .name = "uint32",
+ .get = get_cwp,
+ .put = put_cwp,
+};
#else
static bool fq_needed(void *opaque)
{
@@ -286,7 +317,14 @@ const VMStateDescription vmstate_sparc_cpu = {
VMSTATE_CPU_TIMER(env.hstick, SPARCCPU),
/* On SPARC32 env.psrpil and env.cwp are migrated as part of the PSR */
VMSTATE_UINT32(env.psrpil, SPARCCPU),
- VMSTATE_UINT32(env.cwp, SPARCCPU),
+ {
+ .name = "env.cwp",
+ .version_id = 0,
+ .size = sizeof(uint32_t),
+ .info = &vmstate_cwp,
+ .flags = VMS_SINGLE,
+ .offset = 0,
+ },
#endif
VMSTATE_END_OF_LIST()
},
--
2.47.3