We have several places across the kernel where we want to access another task's syscall arguments, such as ptrace(2), seccomp(2), etc., by making a call to syscall_get_arguments(). This works for register arguments right away by accessing the task's `regs' member of `struct pt_regs', however for stack arguments seen with 32-bit/o32 kernels things are more complicated. Technically they ought to be obtained from the user stack with calls to an access_remote_vm(), but we have an easier way available already. So as to be able to access syscall stack arguments as regular function arguments following the MIPS calling convention we copy them over from the user stack to the kernel stack in arch/mips/kernel/scall32-o32.S, in handle_sys(), to the current stack frame's outgoing argument space at the top of the stack, which is where the handler called expects to see its incoming arguments. This area is also pointed at by the `pt_regs' pointer obtained by task_pt_regs(). Make the o32 stack argument space a proper member of `struct pt_regs' then, by renaming the existing member from `pad0' to `args' and using generated offsets to access the space. No functional change though. With the change in place the o32 kernel stack frame layout at the entry to a syscall handler invoked by handle_sys() is therefore as follows: $sp + 68 -> | ... | <- pt_regs.regs[9] +---------------------+ $sp + 64 -> | $t0 | <- pt_regs.regs[8] +---------------------+ $sp + 60 -> | $a3/argument #4 | <- pt_regs.regs[7] +---------------------+ $sp + 56 -> | $a2/argument #3 | <- pt_regs.regs[6] +---------------------+ $sp + 52 -> | $a1/argument #2 | <- pt_regs.regs[5] +---------------------+ $sp + 48 -> | $a0/argument #1 | <- pt_regs.regs[4] +---------------------+ $sp + 44 -> | $v1 | <- pt_regs.regs[3] +---------------------+ $sp + 40 -> | $v0 | <- pt_regs.regs[2] +---------------------+ $sp + 36 -> | $at | <- pt_regs.regs[1] +---------------------+ $sp + 32 -> | $zero | <- pt_regs.regs[0] +---------------------+ $sp + 28 -> | stack argument #8 | <- pt_regs.args[7] +---------------------+ $sp + 24 -> | stack argument #7 | <- pt_regs.args[6] +---------------------+ $sp + 20 -> | stack argument #6 | <- pt_regs.args[5] +---------------------+ $sp + 16 -> | stack argument #5 | <- pt_regs.args[4] +---------------------+ $sp + 12 -> | psABI space for $a3 | <- pt_regs.args[3] +---------------------+ $sp + 8 -> | psABI space for $a2 | <- pt_regs.args[2] +---------------------+ $sp + 4 -> | psABI space for $a1 | <- pt_regs.args[1] +---------------------+ $sp + 0 -> | psABI space for $a0 | <- pt_regs.args[0] +---------------------+ holding user data received and with the first 4 frame slots reserved by the psABI for the compiler to spill the incoming arguments from $a0-$a3 registers (which it sometimes does according to its needs) and the next 4 frame slots designated by the psABI for any stack function arguments that follow. This data is also available for other tasks to peek/poke at as reqired and where permitted. Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk> Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
225 lines
5.2 KiB
ArmAsm
225 lines
5.2 KiB
ArmAsm
/*
|
|
* This file is subject to the terms and conditions of the GNU General Public
|
|
* License. See the file "COPYING" in the main directory of this archive
|
|
* for more details.
|
|
*
|
|
* Copyright (C) 1995-99, 2000- 02, 06 Ralf Baechle <ralf@linux-mips.org>
|
|
* Copyright (C) 2001 MIPS Technologies, Inc.
|
|
* Copyright (C) 2004 Thiemo Seufer
|
|
* Copyright (C) 2014 Imagination Technologies Ltd.
|
|
*/
|
|
#include <linux/errno.h>
|
|
#include <asm/asm.h>
|
|
#include <asm/asmmacro.h>
|
|
#include <asm/irqflags.h>
|
|
#include <asm/mipsregs.h>
|
|
#include <asm/regdef.h>
|
|
#include <asm/stackframe.h>
|
|
#include <asm/isadep.h>
|
|
#include <asm/sysmips.h>
|
|
#include <asm/thread_info.h>
|
|
#include <asm/unistd.h>
|
|
#include <asm/asm-offsets.h>
|
|
|
|
.align 5
|
|
NESTED(handle_sys, PT_SIZE, sp)
|
|
.set noat
|
|
SAVE_SOME
|
|
TRACE_IRQS_ON_RELOAD
|
|
STI
|
|
.set at
|
|
|
|
lw t1, PT_EPC(sp) # skip syscall on return
|
|
|
|
addiu t1, 4 # skip to next instruction
|
|
sw t1, PT_EPC(sp)
|
|
|
|
sw a3, PT_R26(sp) # save a3 for syscall restarting
|
|
|
|
/*
|
|
* More than four arguments. Try to deal with it by copying the
|
|
* stack arguments from the user stack to the kernel stack.
|
|
* This Sucks (TM).
|
|
*/
|
|
lw t0, PT_R29(sp) # get old user stack pointer
|
|
|
|
/*
|
|
* We intentionally keep the kernel stack a little below the top of
|
|
* userspace so we don't have to do a slower byte accurate check here.
|
|
*/
|
|
addu t4, t0, 32
|
|
bltz t4, bad_stack # -> sp is bad
|
|
|
|
/*
|
|
* Ok, copy the args from the luser stack to the kernel stack.
|
|
*/
|
|
|
|
.set push
|
|
.set noreorder
|
|
.set nomacro
|
|
|
|
load_a4: user_lw(t5, 16(t0)) # argument #5 from usp
|
|
load_a5: user_lw(t6, 20(t0)) # argument #6 from usp
|
|
load_a6: user_lw(t7, 24(t0)) # argument #7 from usp
|
|
load_a7: user_lw(t8, 28(t0)) # argument #8 from usp
|
|
loads_done:
|
|
|
|
sw t5, PT_ARG4(sp) # argument #5 to ksp
|
|
sw t6, PT_ARG5(sp) # argument #6 to ksp
|
|
sw t7, PT_ARG6(sp) # argument #7 to ksp
|
|
sw t8, PT_ARG7(sp) # argument #8 to ksp
|
|
.set pop
|
|
|
|
.section __ex_table,"a"
|
|
PTR_WD load_a4, bad_stack_a4
|
|
PTR_WD load_a5, bad_stack_a5
|
|
PTR_WD load_a6, bad_stack_a6
|
|
PTR_WD load_a7, bad_stack_a7
|
|
.previous
|
|
|
|
/*
|
|
* syscall number is in v0 unless we called syscall(__NR_###)
|
|
* where the real syscall number is in a0
|
|
*/
|
|
subu t2, v0, __NR_O32_Linux
|
|
bnez t2, 1f /* __NR_syscall at offset 0 */
|
|
LONG_S a0, TI_SYSCALL($28) # Save a0 as syscall number
|
|
b 2f
|
|
1:
|
|
LONG_S v0, TI_SYSCALL($28) # Save v0 as syscall number
|
|
2:
|
|
|
|
lw t0, TI_FLAGS($28) # syscall tracing enabled?
|
|
li t1, _TIF_WORK_SYSCALL_ENTRY
|
|
and t0, t1
|
|
bnez t0, syscall_trace_entry # -> yes
|
|
syscall_common:
|
|
subu v0, v0, __NR_O32_Linux # check syscall number
|
|
sltiu t0, v0, __NR_O32_Linux_syscalls
|
|
beqz t0, illegal_syscall
|
|
|
|
sll t0, v0, 2
|
|
la t1, sys_call_table
|
|
addu t1, t0
|
|
lw t2, (t1) # syscall routine
|
|
|
|
beqz t2, illegal_syscall
|
|
|
|
jalr t2 # Do The Real Thing (TM)
|
|
|
|
li t0, -EMAXERRNO - 1 # error?
|
|
sltu t0, t0, v0
|
|
sw t0, PT_R7(sp) # set error flag
|
|
beqz t0, 1f
|
|
|
|
lw t1, PT_R2(sp) # syscall number
|
|
negu v0 # error
|
|
sw t1, PT_R0(sp) # save it for syscall restarting
|
|
1: sw v0, PT_R2(sp) # result
|
|
|
|
o32_syscall_exit:
|
|
j syscall_exit_partial
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
syscall_trace_entry:
|
|
SAVE_STATIC
|
|
move a0, sp
|
|
|
|
jal syscall_trace_enter
|
|
|
|
bltz v0, 1f # seccomp failed? Skip syscall
|
|
|
|
RESTORE_STATIC
|
|
lw v0, PT_R2(sp) # Restore syscall (maybe modified)
|
|
lw a0, PT_R4(sp) # Restore argument registers
|
|
lw a1, PT_R5(sp)
|
|
lw a2, PT_R6(sp)
|
|
lw a3, PT_R7(sp)
|
|
j syscall_common
|
|
|
|
1: j syscall_exit
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
/*
|
|
* Our open-coded access area sanity test for the stack pointer
|
|
* failed. We probably should handle this case a bit more drastic.
|
|
*/
|
|
bad_stack:
|
|
li v0, EFAULT
|
|
sw v0, PT_R2(sp)
|
|
li t0, 1 # set error flag
|
|
sw t0, PT_R7(sp)
|
|
j o32_syscall_exit
|
|
|
|
bad_stack_a4:
|
|
li t5, 0
|
|
b load_a5
|
|
|
|
bad_stack_a5:
|
|
li t6, 0
|
|
b load_a6
|
|
|
|
bad_stack_a6:
|
|
li t7, 0
|
|
b load_a7
|
|
|
|
bad_stack_a7:
|
|
li t8, 0
|
|
b loads_done
|
|
|
|
/*
|
|
* The system call does not exist in this kernel
|
|
*/
|
|
illegal_syscall:
|
|
li v0, ENOSYS # error
|
|
sw v0, PT_R2(sp)
|
|
li t0, 1 # set error flag
|
|
sw t0, PT_R7(sp)
|
|
j o32_syscall_exit
|
|
END(handle_sys)
|
|
|
|
LEAF(sys_syscall)
|
|
subu t0, a0, __NR_O32_Linux # check syscall number
|
|
sltiu v0, t0, __NR_O32_Linux_syscalls
|
|
beqz t0, einval # do not recurse
|
|
sll t1, t0, 2
|
|
beqz v0, einval
|
|
lw t2, sys_call_table(t1) # syscall routine
|
|
|
|
move a0, a1 # shift argument registers
|
|
move a1, a2
|
|
move a2, a3
|
|
lw a3, 16(sp)
|
|
lw t4, 20(sp)
|
|
lw t5, 24(sp)
|
|
lw t6, 28(sp)
|
|
sw t4, 16(sp)
|
|
sw t5, 20(sp)
|
|
sw t6, 24(sp)
|
|
jr t2
|
|
/* Unreached */
|
|
|
|
einval: li v0, -ENOSYS
|
|
jr ra
|
|
END(sys_syscall)
|
|
|
|
#ifdef CONFIG_MIPS_MT_FPAFF
|
|
/*
|
|
* For FPU affinity scheduling on MIPS MT processors, we need to
|
|
* intercept sys_sched_xxxaffinity() calls until we get a proper hook
|
|
* in kernel/sched/core.c. Considered only temporary we only support
|
|
* these hooks for the 32-bit kernel - there is no MIPS64 MT processor
|
|
* atm.
|
|
*/
|
|
#define sys_sched_setaffinity mipsmt_sys_sched_setaffinity
|
|
#define sys_sched_getaffinity mipsmt_sys_sched_getaffinity
|
|
#endif /* CONFIG_MIPS_MT_FPAFF */
|
|
|
|
#define __SYSCALL_WITH_COMPAT(nr, native, compat) __SYSCALL(nr, native)
|
|
#define __SYSCALL(nr, entry) PTR_WD entry
|
|
.align 2
|
|
.type sys_call_table, @object
|
|
EXPORT(sys_call_table)
|
|
#include <asm/syscall_table_o32.h>
|