diff mbox series

[v6,04/16] linux-user/host/mips: Add safe-syscall.inc.S

Message ID 20211123173759.1383510-5-richard.henderson@linaro.org
State Superseded
Headers show
Series linux-user: simplify safe signal handling | expand

Commit Message

Richard Henderson Nov. 23, 2021, 5:37 p.m. UTC
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 linux-user/host/mips/hostdep.h          |   3 +
 linux-user/host/mips/safe-syscall.inc.S | 123 ++++++++++++++++++++++++
 2 files changed, 126 insertions(+)
 create mode 100644 linux-user/host/mips/safe-syscall.inc.S

Comments

Warner Losh Nov. 25, 2021, 10:22 a.m. UTC | #1
On Tue, Nov 23, 2021 at 10:38 AM Richard Henderson <
richard.henderson@linaro.org> wrote:

> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
> ---
>  linux-user/host/mips/hostdep.h          |   3 +
>  linux-user/host/mips/safe-syscall.inc.S | 123 ++++++++++++++++++++++++
>  2 files changed, 126 insertions(+)
>  create mode 100644 linux-user/host/mips/safe-syscall.inc.S
>

Reviewed-by: Warner Losh <imp@bsdimp.com>
Peter Maydell Nov. 29, 2021, 4:40 p.m. UTC | #2
On Tue, 23 Nov 2021 at 17:44, Richard Henderson
<richard.henderson@linaro.org> wrote:
>
> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
> ---
>  linux-user/host/mips/hostdep.h          |   3 +
>  linux-user/host/mips/safe-syscall.inc.S | 123 ++++++++++++++++++++++++
>  2 files changed, 126 insertions(+)
>  create mode 100644 linux-user/host/mips/safe-syscall.inc.S


> +LEAF(safe_syscall_base)
> +        .cfi_startproc
> +#if _MIPS_SIM == _ABIO32
> +        /*
> +         * The syscall calling convention is nearly the same as C:
> +         * we enter with a0 == &signal_pending
> +         *               a1 == syscall number
> +         *               a2, a3, stack == syscall arguments
> +         *               and return the result in a0
> +         * and the syscall instruction needs
> +         *               v0 == syscall number
> +         *               a0 ... a3, stack == syscall arguments
> +         *               and returns the result in v0
> +         * Shuffle everything around appropriately.
> +         */
> +        move    t0, a0          /* signal_pending pointer */
> +        move    v0, a1          /* syscall number */
> +        move    a0, a2          /* syscall arguments */
> +        move    a1, a3
> +        lw      a2, 16(sp)
> +        lw      a3, 20(sp)
> +        lw      t4, 24(sp)
> +        lw      t5, 28(sp)
> +        lw      t6, 32(sp)
> +        lw      t7, 40(sp)
> +        sw      t4, 16(sp)
> +        sw      t5, 20(sp)
> +        sw      t6, 24(sp)
> +        sw      t7, 28(sp)

This is a varargs call, so (unless I'm confused, which is
quite possible) the caller will only allocate enough stack
space for the arguments we're actually passed, right? That
means that unless the syscall actually has 3 or more arguments
the memory at 16(sp) will be whatever the caller had on the
stack above the argument-passing area, and we can't write to
it. I think we need to actually move sp down here so we have
some space we know we can scribble on.

-- PMM
Richard Henderson Nov. 29, 2021, 4:51 p.m. UTC | #3
On 11/29/21 5:40 PM, Peter Maydell wrote:
>> +        lw      a2, 16(sp)
>> +        lw      a3, 20(sp)
>> +        lw      t4, 24(sp)
>> +        lw      t5, 28(sp)
>> +        lw      t6, 32(sp)
>> +        lw      t7, 40(sp)
>> +        sw      t4, 16(sp)
>> +        sw      t5, 20(sp)
>> +        sw      t6, 24(sp)
>> +        sw      t7, 28(sp)
> 
> This is a varargs call, so (unless I'm confused, which is
> quite possible) the caller will only allocate enough stack
> space for the arguments we're actually passed, right? That
> means that unless the syscall actually has 3 or more arguments
> the memory at 16(sp) will be whatever the caller had on the
> stack above the argument-passing area, and we can't write to
> it. I think we need to actually move sp down here so we have
> some space we know we can scribble on.

Yep, good catch.


r~
diff mbox series

Patch

diff --git a/linux-user/host/mips/hostdep.h b/linux-user/host/mips/hostdep.h
index ba111d75c3..d9e90a096b 100644
--- a/linux-user/host/mips/hostdep.h
+++ b/linux-user/host/mips/hostdep.h
@@ -12,4 +12,7 @@ 
 #ifndef MIPS_HOSTDEP_H
 #define MIPS_HOSTDEP_H
 
+/* We have a safe-syscall.inc.S */
+#define HAVE_SAFE_SYSCALL
+
 #endif
diff --git a/linux-user/host/mips/safe-syscall.inc.S b/linux-user/host/mips/safe-syscall.inc.S
new file mode 100644
index 0000000000..c6dd726001
--- /dev/null
+++ b/linux-user/host/mips/safe-syscall.inc.S
@@ -0,0 +1,123 @@ 
+/*
+ * safe-syscall.inc.S : host-specific assembly fragment
+ * to handle signals occurring at the same time as system calls.
+ * This is intended to be included by linux-user/safe-syscall.S
+ *
+ * Written by Richard Henderson <richard.henderson@linaro.org>
+ * Copyright (C) 2021 Linaro, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "sys/regdef.h"
+#include "sys/asm.h"
+
+        .text
+        .set    nomips16
+        .set    reorder
+
+        .global safe_syscall_start
+        .global safe_syscall_end
+        .type   safe_syscall_start, @function
+        .type   safe_syscall_end, @function
+
+        /*
+         * This is the entry point for making a system call. The calling
+         * convention here is that of a C varargs function with the
+         * first argument an 'int *' to the signal_pending flag, the
+         * second one the system call number (as a 'long'), and all further
+         * arguments being syscall arguments (also 'long').
+         */
+
+LEAF(safe_syscall_base)
+        .cfi_startproc
+#if _MIPS_SIM == _ABIO32
+        /*
+         * The syscall calling convention is nearly the same as C:
+         * we enter with a0 == &signal_pending
+         *               a1 == syscall number
+         *               a2, a3, stack == syscall arguments
+         *               and return the result in a0
+         * and the syscall instruction needs
+         *               v0 == syscall number
+         *               a0 ... a3, stack == syscall arguments
+         *               and returns the result in v0
+         * Shuffle everything around appropriately.
+         */
+        move    t0, a0          /* signal_pending pointer */
+        move    v0, a1          /* syscall number */
+        move    a0, a2          /* syscall arguments */
+        move    a1, a3
+        lw      a2, 16(sp)
+        lw      a3, 20(sp)
+        lw      t4, 24(sp)
+        lw      t5, 28(sp)
+        lw      t6, 32(sp)
+        lw      t7, 40(sp)
+        sw      t4, 16(sp)
+        sw      t5, 20(sp)
+        sw      t6, 24(sp)
+        sw      t7, 28(sp)
+#else
+        /*
+         * The syscall calling convention is nearly the same as C:
+         * we enter with a0 == &signal_pending
+         *               a1 == syscall number
+         *               a2 ... a7 == syscall arguments
+         *               and return the result in a0
+         * and the syscall instruction needs
+         *               v0 == syscall number
+         *               a0 ... a5 == syscall arguments
+         *               and returns the result in v0
+         * Shuffle everything around appropriately.
+         */
+        move    t0, a0          /* signal_pending pointer */
+        move    v0, a1          /* syscall number */
+        move    a0, a2          /* syscall arguments */
+        move    a1, a3
+        move    a2, a4
+        move    a3, a5
+        move    a4, a6
+        move    a5, a7
+#endif
+
+        /*
+         * This next sequence of code works in conjunction with the
+         * rewind_if_safe_syscall_function(). If a signal is taken
+         * and the interrupted PC is anywhere between 'safe_syscall_start'
+         * and 'safe_syscall_end' then we rewind it to 'safe_syscall_start'.
+         * The code sequence must therefore be able to cope with this, and
+         * the syscall instruction must be the final one in the sequence.
+         */
+safe_syscall_start:
+        /* If signal_pending is non-zero, don't do the call */
+        lw      t1, 0(t0)
+        bnez    t1, 2f
+        syscall
+safe_syscall_end:
+
+        /* code path for having successfully executed the syscall */
+        bnez    a3, 1f
+        jr      ra
+
+        /* code path when we didn't execute the syscall */
+2:      li      v0, TARGET_ERESTARTSYS
+
+        /* code path setting errno */
+        /*
+         * We didn't setup GP on entry, optimistic of the syscall success.
+         * We must do so now to load the address of the helper, as required
+         * by the ABI, into t9.
+         *
+         * Note that SETUP_GPX and SETUP_GPX64 are themselves conditional,
+         * so we can simply let the one that's not empty succeed.
+         */
+1:      USE_ALT_CP(t0)
+        SETUP_GPX(t1)
+        SETUP_GPX64(t0, t1)
+        PTR_LA  t9, safe_syscall_set_errno_tail
+        jr      t9
+
+        .cfi_endproc
+END(safe_syscall_base)