target/openrisc: Use cpu_unwind_state_data for mfspr

Since we do not plan to exit, use cpu_unwind_state_data
and extract exactly the data requested.

This is a bug fix, in that we no longer clobber dflag.

Consider:

        l.j       L2         // branch
        l.mfspr   r1, ppc    // delay

L1:     boom
L2:     l.lwa     r3, (r4)

Here, dflag would be set by cpu_restore_state (because that is the current
state of the cpu), but but not cleared by tb_stop on exiting the TB
(because DisasContext has recorded the current value as zero).

The next TB begins at L2 with dflag incorrectly set.  If the load has a
tlb miss, then the exception will be delivered as per a delay slot:
with DSX set in the status register and PC decremented (delay slots
restart by re-executing the branch). This will cause the return from
interrupt to go to L1, and boom!

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2022-10-24 22:55:26 +10:00
parent 5813c5c74a
commit cc30dc441b

View File

@ -199,6 +199,7 @@ target_ulong HELPER(mfspr)(CPUOpenRISCState *env, target_ulong rd,
target_ulong spr)
{
#ifndef CONFIG_USER_ONLY
uint64_t data[TARGET_INSN_START_WORDS];
MachineState *ms = MACHINE(qdev_get_machine());
OpenRISCCPU *cpu = env_archcpu(env);
CPUState *cs = env_cpu(env);
@ -232,14 +233,20 @@ target_ulong HELPER(mfspr)(CPUOpenRISCState *env, target_ulong rd,
return env->evbar;
case TO_SPR(0, 16): /* NPC (equals PC) */
cpu_restore_state(cs, GETPC(), false);
if (cpu_unwind_state_data(cs, GETPC(), data)) {
return data[0];
}
return env->pc;
case TO_SPR(0, 17): /* SR */
return cpu_get_sr(env);
case TO_SPR(0, 18): /* PPC */
cpu_restore_state(cs, GETPC(), false);
if (cpu_unwind_state_data(cs, GETPC(), data)) {
if (data[1] & 2) {
return data[0] - 4;
}
}
return env->ppc;
case TO_SPR(0, 32): /* EPCR */