commit 11a188eea8e973e6ce42b4327f20083e92fa926e Author: Brandon Bergren Date: Tue Dec 8 14:04:10 2020 -0600 bsd-user: freebsd: Fix context handling on powerpc. Due to a mismatch between the qemu CPU model and the FreeBSD trapframe layout, we need to manually handle the extra registers that are stored in the trapframe rather than copying past the end of the gpr array. Doing this properly fixes oddities like signal handlers screwing up condition register fields and causing spurious signal errors. This also stops the signal handling from scribbling in some of the SPE high half of the GPRs, as that's what was next in the struct. Signed-off-by: Brandon Bergren diff --git a/bsd-user/freebsd/target_os_signal.h b/bsd-user/freebsd/target_os_signal.h index d7004c8055..3ed454e086 100644 --- a/bsd-user/freebsd/target_os_signal.h +++ b/bsd-user/freebsd/target_os_signal.h @@ -42,7 +42,6 @@ #define TARGET_SIGLIBRT 33 /* reserved by the real-time library */ #define TARGET_SIGRTMIN 65 #define TARGET_SIGRTMAX 126 -#define TARGET_QEMU_ESIGRETURN 255 /* fake errno value for use by sigreturn */ /* * Language spec says we must list exactly one parameter, even though we diff --git a/bsd-user/ppc/target_arch_cpu.h b/bsd-user/ppc/target_arch_cpu.h index abc15c9fc4..c8c2293de0 100644 --- a/bsd-user/ppc/target_arch_cpu.h +++ b/bsd-user/ppc/target_arch_cpu.h @@ -436,7 +436,7 @@ static inline void target_cpu_loop(CPUPPCState *env) ret = do_freebsd_syscall(env, env->gpr[0], env->gpr[3], env->gpr[4], env->gpr[5], env->gpr[6], env->gpr[7], env->gpr[8], env->gpr[9], env->gpr[10]); - if (ret == (target_ulong)(-TARGET_QEMU_ESIGRETURN)) { + if (ret == (target_ulong)(-TARGET_EJUSTRETURN)) { /* Returning from a successful sigreturn syscall. Avoid corrupting register state. */ break; diff --git a/bsd-user/ppc/target_arch_signal.h b/bsd-user/ppc/target_arch_signal.h index 9f7559c9d8..e181a9f992 100644 --- a/bsd-user/ppc/target_arch_signal.h +++ b/bsd-user/ppc/target_arch_signal.h @@ -142,6 +142,7 @@ static inline abi_long get_mcontext(CPUPPCState *regs, target_mcontext_t *mcp, int flags) { int i, err = 0; + target_ulong ccr = 0; if (flags & TARGET_MC_SET_ONSTACK) { mcp->mc_onstack = tswapal(1); @@ -151,10 +152,38 @@ static inline abi_long get_mcontext(CPUPPCState *regs, target_mcontext_t *mcp, mcp->mc_flags = 0; - for (i = 1; i < 42; i++) { + for (i = 1; i < 32; i++) { mcp->mc_frame[i] = tswapal(regs->gpr[i]); } + /* Convert cr fields back to cr register */ + for (i = 0; i < ARRAY_SIZE(regs->crf); i++) { + ccr |= regs->crf[i] << (32 - ((i + 1) * 4)); + } + + mcp->mc_frame[32] = tswapal(regs->lr); + mcp->mc_frame[33] = tswapal(ccr); + mcp->mc_frame[34] = tswapal(regs->xer); + mcp->mc_frame[35] = tswapal(regs->ctr); + + /* + * Supervisor only section: + * We will not be restoring these, but we do a best-effort update + * here for the benefit of userland threading code. + */ + + /* srr0 */ + /* XXX is this -4 or no? */ + mcp->mc_frame[36] = tswapal(regs->nip); + /* srr1 */ + mcp->mc_frame[37] = tswapal(regs->msr); + + /* Ensure exception section is empty. */ + mcp->mc_frame[38] = 0; /* exc */ + mcp->mc_frame[39] = 0; /* dar */ + mcp->mc_frame[40] = 0; /* dsisr / esr */ + mcp->mc_frame[41] = 0; /* dbcr0 */ + mcp->mc_flags |= TARGET_MC_FP_VALID; for (i = 0; i < 32; i++) { mcp->mc_fpreg[i] = tswapal(regs->fpr[i]); @@ -183,7 +212,7 @@ static inline abi_long get_mcontext(CPUPPCState *regs, target_mcontext_t *mcp, static inline abi_long set_mcontext(CPUPPCState *regs, target_mcontext_t *mcp, int srflag) { - abi_long tls; + abi_long tls, ccr; int i, err = 0; #if defined(TARGET_PPC64) && !defined(TARGET_ABI32) @@ -191,9 +220,21 @@ static inline abi_long set_mcontext(CPUPPCState *regs, target_mcontext_t *mcp, #else tls = regs->gpr[2]; #endif - for (i = 1; i < 42; i++) { + for (i = 1; i < 32; i++) { regs->gpr[i] = tswapal(mcp->mc_frame[i]); } + + /* Restore CR from context. */ + ccr = tswapal(mcp->mc_frame[33]); + for (i = 0; i < ARRAY_SIZE(regs->crf); i++) { + regs->crf[i] = (ccr >> (32 - ((i + 1) * 4))) & 0xf; + } + + regs->lr = tswapal(mcp->mc_frame[32]); + regs->xer = tswapal(mcp->mc_frame[34]); + regs->ctr = tswapal(mcp->mc_frame[35]); + regs->nip = tswapal(mcp->mc_frame[36]); + #if defined(TARGET_PPC64) && !defined(TARGET_ABI32) regs->gpr[13] = tls; #else