For the ABIs in which the syscall return register is not
also the first function argument register, move the errno
value into the correct place.
Fixes: a3310c0397
("linux-user: Move syscall error detection into safe_syscall_base")
Reported-by: Laurent Vivier <laurent@vivier.eu>
Tested-by: Laurent Vivier <laurent@vivier.eu>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Message-Id: <20220104190454.542225-1-richard.henderson@linaro.org>
150 lines
5 KiB
ArmAsm
150 lines
5 KiB
ArmAsm
/*
|
|
* 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 common-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').
|
|
*/
|
|
|
|
#if _MIPS_SIM == _ABIO32
|
|
/* 8 * 4 = 32 for outgoing parameters; 1 * 4 for s0 save; 1 * 4 for align. */
|
|
#define FRAME 40
|
|
#define OFS_S0 32
|
|
#else
|
|
/* 1 * 8 for s0 save; 1 * 8 for align. */
|
|
#define FRAME 16
|
|
#define OFS_S0 0
|
|
#endif
|
|
|
|
|
|
NESTED(safe_syscall_base, FRAME, ra)
|
|
.cfi_startproc
|
|
PTR_ADDIU sp, sp, -FRAME
|
|
.cfi_adjust_cfa_offset FRAME
|
|
REG_S s0, OFS_S0(sp)
|
|
.cfi_rel_offset s0, OFS_S0
|
|
#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 s0, a0 /* signal_pending pointer */
|
|
move v0, a1 /* syscall number */
|
|
move a0, a2 /* syscall arguments */
|
|
move a1, a3
|
|
lw a2, FRAME+16(sp)
|
|
lw a3, FRAME+20(sp)
|
|
lw t4, FRAME+24(sp)
|
|
lw t5, FRAME+28(sp)
|
|
lw t6, FRAME+32(sp)
|
|
lw t7, FRAME+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 s0, 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(s0)
|
|
bnez t1, 2f
|
|
syscall
|
|
safe_syscall_end:
|
|
|
|
/* code path for having successfully executed the syscall */
|
|
REG_L s0, OFS_S0(sp)
|
|
PTR_ADDIU sp, sp, FRAME
|
|
.cfi_remember_state
|
|
.cfi_adjust_cfa_offset -FRAME
|
|
.cfi_restore s0
|
|
bnez a3, 1f
|
|
jr ra
|
|
.cfi_restore_state
|
|
|
|
/* code path when we didn't execute the syscall */
|
|
2: REG_L s0, OFS_S0(sp)
|
|
PTR_ADDIU sp, sp, FRAME
|
|
.cfi_adjust_cfa_offset -FRAME
|
|
.cfi_restore s0
|
|
li v0, QEMU_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)
|
|
move a0, v0
|
|
PTR_LA t9, safe_syscall_set_errno_tail
|
|
jr t9
|
|
|
|
.cfi_endproc
|
|
END(safe_syscall_base)
|