@@ -179,4 +179,79 @@ static inline abi_long get_mcontext(CPUARMState *env, target_mcontext_t *mcp,
return err;
}
+/* Compare to arm/arm/exec_machdep.c set_mcontext() */
+static inline abi_long set_mcontext(CPUARMState *env, target_mcontext_t *mcp,
+ int srflag)
+{
+ int err = 0;
+ const uint32_t *gr = mcp->__gregs;
+ uint32_t cpsr, ccpsr = cpsr_read(env);
+ uint32_t fpscr;
+
+ cpsr = tswap32(gr[TARGET_REG_CPSR]);
+ /*
+ * Only allow certain bits to change, reject attempted changes to non-user
+ * bits. In addition, make sure we're headed for user mode and none of the
+ * interrupt bits are set.
+ */
+ if ((ccpsr & ~CPSR_USER) != (cpsr & ~CPSR_USER)) {
+ return -TARGET_EINVAL;
+ }
+ if ((cpsr & CPSR_M) != ARM_CPU_MODE_USR ||
+ (cpsr & (CPSR_I | CPSR_F)) != 0) {
+ return -TARGET_EINVAL;
+ }
+
+ /*
+ * Make sure that we either have no vfp, or it's the correct size.
+ * FreeBSD just ignores it, though, so maybe we'll need to adjust
+ * things below instead.
+ */
+ if (mcp->mc_vfp_size != 0 && mcp->mc_vfp_size != sizeof(target_mcontext_vfp_t)) {
+ return -TARGET_EINVAL;
+ }
+ /*
+ * Make sure T mode matches the PC's notion of thumb mode, although
+ * FreeBSD lets the processor sort this out, so we may need remove
+ * this check, or generate a signal...
+ */
+ if (!!(tswap32(gr[TARGET_REG_PC]) & 1) != !!(cpsr & CPSR_T)) {
+ return -TARGET_EINVAL;
+ }
+
+ env->regs[0] = tswap32(gr[TARGET_REG_R0]);
+ env->regs[1] = tswap32(gr[TARGET_REG_R1]);
+ env->regs[2] = tswap32(gr[TARGET_REG_R2]);
+ env->regs[3] = tswap32(gr[TARGET_REG_R3]);
+ env->regs[4] = tswap32(gr[TARGET_REG_R4]);
+ env->regs[5] = tswap32(gr[TARGET_REG_R5]);
+ env->regs[6] = tswap32(gr[TARGET_REG_R6]);
+ env->regs[7] = tswap32(gr[TARGET_REG_R7]);
+ env->regs[8] = tswap32(gr[TARGET_REG_R8]);
+ env->regs[9] = tswap32(gr[TARGET_REG_R9]);
+ env->regs[10] = tswap32(gr[TARGET_REG_R10]);
+ env->regs[11] = tswap32(gr[TARGET_REG_R11]);
+ env->regs[12] = tswap32(gr[TARGET_REG_R12]);
+
+ env->regs[13] = tswap32(gr[TARGET_REG_SP]);
+ env->regs[14] = tswap32(gr[TARGET_REG_LR]);
+ env->regs[15] = tswap32(gr[TARGET_REG_PC]);
+ if (mcp->mc_vfp_size != 0 && mcp->mc_vfp_ptr != NULL) {
+ /* see set_vfpcontext in sys/arm/arm/exec_machdep.c */
+ target_mcontext_vfp_t *vfp = (target_mcontext_vfp_t *)mcp->mc_vfp_ptr;
+ for (int i = 0; i < 32; i++) {
+ *aa32_vfp_dreg(env, i) = tswap64(vfp->mcv_reg[i]);
+ }
+ fpscr = tswap32(vfp->mcv_fpscr);
+ vfp_set_fpscr(env, fpscr);
+ /*
+ * linux-user sets fpexc, fpinst and fpinst2, but these aren't in
+ * FreeBSD's mcontext, what to do?
+ */
+ }
+ cpsr_write(env, cpsr, CPSR_USER | CPSR_EXEC, CPSRWriteByInstr);
+
+ return err;
+}
+
#endif /* !_TARGET_ARCH_SIGNAL_H_ */