diff mbox series

[v2,14/30] bsd-user/arm/target_arch_thread.h: Routines to create and switch to a thread

Message ID 20211102225248.52999-15-imp@bsdimp.com
State New
Headers show
Series bsd-user: arm (32-bit) support | expand

Commit Message

Warner Losh Nov. 2, 2021, 10:52 p.m. UTC
Implement target_thread_init (to create a thread) and target_set_upcall
(to switch to a thread) for arm.

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: Kyle Evans <kevans@FreeBSD.org>
---
 bsd-user/arm/target_arch_thread.h | 80 +++++++++++++++++++++++++++++++
 1 file changed, 80 insertions(+)
 create mode 100644 bsd-user/arm/target_arch_thread.h

Comments

Richard Henderson Nov. 3, 2021, 3:31 a.m. UTC | #1
On 11/2/21 6:52 PM, Warner Losh wrote:
> +    /*
> +     * Thumb mode is encoded by the low bit in the entry point (since ARM can't
> +     * execute at odd addresses). When it's set, set the Thumb bit (T) in the
> +     * CPSR.
> +     */
> +    if (entry & 0x1) {
> +        cpsr_write(env, cpsr_read(env) | CPSR_T, CPSR_T, CPSRWriteByInstr);
> +    }

This should be

   cpsr_write(env, (entry & 1) * CPSR_T, CPSR_T, CPSRWriteByInstr);

because you need to clear T for arm mode as well.

> +    /* FIXME - what to for failure of get_user()? */
> +    get_user_ual(regs->ARM_r2, stack + 8); /* envp */
> +    get_user_ual(regs->ARM_r1, stack + 4); /* envp */

Surely these values are present in image_info anyway?


r~
Warner Losh Nov. 3, 2021, 6:27 p.m. UTC | #2
[[ Adding Olivier Houchard to confirm my reading of the ARM init twisty
maze of code ]]
On Tue, Nov 2, 2021 at 9:31 PM Richard Henderson <
richard.henderson@linaro.org> wrote:

> On 11/2/21 6:52 PM, Warner Losh wrote:
> > +    /*
> > +     * Thumb mode is encoded by the low bit in the entry point (since
> ARM can't
> > +     * execute at odd addresses). When it's set, set the Thumb bit (T)
> in the
> > +     * CPSR.
> > +     */
> > +    if (entry & 0x1) {
> > +        cpsr_write(env, cpsr_read(env) | CPSR_T, CPSR_T,
> CPSRWriteByInstr);
> > +    }
>
> This should be
>
>    cpsr_write(env, (entry & 1) * CPSR_T, CPSR_T, CPSRWriteByInstr);
>
> because you need to clear T for arm mode as well.
>

Ah. Right. I'd intended to fix this, but it slipped my mind (along with the
other T bit thing you told me about).


> > +    /* FIXME - what to for failure of get_user()? */
> > +    get_user_ual(regs->ARM_r2, stack + 8); /* envp */
> > +    get_user_ual(regs->ARM_r1, stack + 4); /* envp */
>
> Surely these values are present in image_info anyway?
>

The host versions are in image_info, but the target versions are not.
Linux-user does a similar
thing without the #define sugar form ARM_rX. I didn't see where the current
bsd-user squirrels
this information away (it's computed and stored in local variables), nor
did my much more
brief look at linux-user.

Looking at the FreeBSD kernel, though, we don't set r1 or r2. r0 and r1 are
set to 0 explicitly,
and r2 is set to 0 because the first user registers are all cleared. In the
static case, they
are ignored (since r0 = ps_strings, r1 = obj_main (unused) and r2 = cleanup
(also
unused in the static case). If we're entering via the dynamic loader, it
saves r0 and generates
r1 (though it's ultimately unused) and r2 (which rtld sets to its cleanup
routine). r0 is the ps
strings that ps displays, so isn't relevant to emulation.

tl;dr: I'll add a comment to that effect and make it simpler (assuming my
analysis survives)

Warner
diff mbox series

Patch

diff --git a/bsd-user/arm/target_arch_thread.h b/bsd-user/arm/target_arch_thread.h
new file mode 100644
index 0000000000..ae5b0d6c38
--- /dev/null
+++ b/bsd-user/arm/target_arch_thread.h
@@ -0,0 +1,80 @@ 
+/*
+ *  arm thread support
+ *
+ *  Copyright (c) 2013 Stacey D. Son
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _TARGET_ARCH_THREAD_H_
+#define _TARGET_ARCH_THREAD_H_
+
+/* Compare to arm/arm/vm_machdep.c cpu_set_upcall_kse() */
+static inline void target_thread_set_upcall(CPUARMState *env, abi_ulong entry,
+    abi_ulong arg, abi_ulong stack_base, abi_ulong stack_size)
+{
+    abi_ulong sp;
+
+    /*
+     * Make sure the stack is properly aligned.
+     * arm/include/param.h (STACKLIGN() macro)
+     */
+    sp = (u_int)(stack_base + stack_size) & ~0x7;
+
+    /* sp = stack base */
+    env->regs[13] = sp;
+    /* pc = start function entry */
+    env->regs[15] = entry & 0xfffffffe;
+    /* r0 = arg */
+    env->regs[0] = arg;
+    env->spsr = ARM_CPU_MODE_USR;
+    /*
+     * Thumb mode is encoded by the low bit in the entry point (since ARM can't
+     * execute at odd addresses). When it's set, set the Thumb bit (T) in the
+     * CPSR.
+     */
+    if (entry & 0x1) {
+        cpsr_write(env, cpsr_read(env) | CPSR_T, CPSR_T, CPSRWriteByInstr);
+    }
+}
+
+static inline void target_thread_init(struct target_pt_regs *regs,
+        struct image_info *infop)
+{
+    abi_long stack = infop->start_stack;
+    memset(regs, 0, sizeof(*regs));
+    regs->ARM_cpsr = 0x10;
+    /*
+     * Thumb mode is encoded by the low bit in the entry point (since ARM can't
+     * execute at odd addresses). When it's set, set the Thumb bit (T) in the
+     * CPSR.
+     */
+    if (infop->entry & 1) {
+        regs->ARM_cpsr |= CPSR_T;
+    }
+    regs->ARM_pc = infop->entry & 0xfffffffe;
+    regs->ARM_sp = infop->start_stack;
+    if (bsd_type == target_freebsd) {
+        regs->ARM_lr = infop->entry & 0xfffffffe;
+    }
+    /* FIXME - what to for failure of get_user()? */
+    get_user_ual(regs->ARM_r2, stack + 8); /* envp */
+    get_user_ual(regs->ARM_r1, stack + 4); /* envp */
+    /* XXX: it seems that r0 is zeroed after ! */
+    regs->ARM_r0 = 0;
+    /* For uClinux PIC binaries.  */
+    /* XXX: Linux does this only on ARM with no MMU (do we care ?) */
+    regs->ARM_r10 = infop->start_data;
+}
+
+#endif /* !_TARGET_ARCH_THREAD_H_ */