diff mbox series

[v3,23/29] bsd-user/arm/target_arch_signal.h: arm set_mcontext

Message ID 20211104140536.42573-24-imp@bsdimp.com (mailing list archive)
State New, archived
Headers show
Series bsd-user: arm (32-bit) support | expand

Commit Message

Warner Losh Nov. 4, 2021, 2:05 p.m. UTC
Move the machine context to the CPU state.

Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Kyle Evans <kevans@FreeBSD.org>
Signed-off-by: Warner Losh <imp@bsdimp.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
---
 bsd-user/arm/target_arch_signal.h | 75 +++++++++++++++++++++++++++++++
 1 file changed, 75 insertions(+)

Comments

Richard Henderson Nov. 4, 2021, 6:41 p.m. UTC | #1
On 11/4/21 10:05 AM, Warner Losh wrote:
> +    /*
> +     * 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;
> +    }

I've had a read through the Arm ARM for "movs pc, lr", which is how swi_exit returns to 
userspace:

     CPSRWriteByInstr(SPSR[], '1111', TRUE);
     ...
     BranchWritePC(result);

So the CPSR gets written first, which sets the T bit, and thus the result of 
CurrentInstrSet(), then

BranchWritePC(bits(32) address)
   if CurrentInstrSet() == InstrSet_ARM then
     if ArchVersion() < 6 && address<1:0> != '00' then UNPREDICTABLE;
     BranchTo(address<31:2>:'00');
   ...
   else
     BranchTo(address<31:1>:'0');

> +    env->regs[15] = tswap32(gr[TARGET_REG_PC]);

So this should mask the low 1 or 2 bits depending on cpsr & CPSR_T.


r~
Warner Losh Nov. 4, 2021, 11:59 p.m. UTC | #2
On Thu, Nov 4, 2021 at 12:43 PM Richard Henderson <
richard.henderson@linaro.org> wrote:

> On 11/4/21 10:05 AM, Warner Losh wrote:
> > +    /*
> > +     * 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;
> > +    }
>
> I've had a read through the Arm ARM for "movs pc, lr", which is how
> swi_exit returns to
> userspace:
>
>      CPSRWriteByInstr(SPSR[], '1111', TRUE);
>      ...
>      BranchWritePC(result);
>
> So the CPSR gets written first, which sets the T bit, and thus the result
> of
> CurrentInstrSet(), then
>
> BranchWritePC(bits(32) address)
>    if CurrentInstrSet() == InstrSet_ARM then
>      if ArchVersion() < 6 && address<1:0> != '00' then UNPREDICTABLE;
>      BranchTo(address<31:2>:'00');
>    ...
>    else
>      BranchTo(address<31:1>:'0');
>
> > +    env->regs[15] = tswap32(gr[TARGET_REG_PC]);
>
> So this should mask the low 1 or 2 bits depending on cpsr & CPSR_T.
>

Will do. Thanks for all the patient explanations.

Warner


> r~
>
diff mbox series

Patch

diff --git a/bsd-user/arm/target_arch_signal.h b/bsd-user/arm/target_arch_signal.h
index ebb44d10dc..177617ea8b 100644
--- a/bsd-user/arm/target_arch_signal.h
+++ b/bsd-user/arm/target_arch_signal.h
@@ -171,4 +171,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_ */