| /* |
| * 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) 1997, 1998, 1999, 2000, 2001 by Ralf Baechle |
| * Copyright (C) 2001 MIPS Technologies, Inc. |
| */ |
| #include <linux/config.h> |
| #include <linux/errno.h> |
| #include <asm/asm.h> |
| #include <asm/current.h> |
| #include <asm/mipsregs.h> |
| #include <asm/regdef.h> |
| #include <asm/stackframe.h> |
| #include <asm/isadep.h> |
| #include <asm/sysmips.h> |
| #include <asm/unistd.h> |
| |
| /* Highest syscall used of any syscall flavour */ |
| #define MAX_SYSCALL_NO __NR_O32_Linux + __NR_O32_Linux_syscalls |
| |
| .align 5 |
| NESTED(handle_sys, PT_SIZE, sp) |
| .set noat |
| SAVE_SOME |
| STI |
| .set at |
| |
| lw t1, PT_EPC(sp) # skip syscall on return |
| |
| sltiu t0, v0, MAX_SYSCALL_NO + 1 # check syscall number |
| addiu t1, 4 # skip to next instruction |
| sw t1, PT_EPC(sp) |
| beqz t0, illegal_syscall |
| |
| /* XXX Put both in one cacheline, should save a bit. */ |
| sll t0, v0, 2 |
| lw t2, sys_call_table(t0) # syscall routine |
| lbu t3, sys_narg_table(v0) # number of arguments |
| beqz t2, illegal_syscall |
| |
| subu t0, t3, 5 # 5 or more arguments? |
| sw a3, PT_R26(sp) # save a3 for syscall restarting |
| bgez t0, stackargs |
| |
| stack_done: |
| sw a3, PT_R26(sp) # save for syscall restart |
| lw t0, TASK_PTRACE($28) # syscall tracing enabled? |
| andi t0, _PT_TRACESYS |
| bnez t0, trace_a_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 |
| |
| negu v0 # error |
| sw v0, PT_R0(sp) # set flag for syscall restarting |
| 1: sw v0, PT_R2(sp) # result |
| |
| fast_ret_from_sys_call: |
| ret_from_schedule: |
| mfc0 t0, CP0_STATUS # need_resched and signals atomic test |
| ori t0, t0, 1 |
| xori t0, t0, 1 |
| mtc0 t0, CP0_STATUS |
| SSNOP; SSNOP; SSNOP |
| |
| lw t2, TASK_NEED_RESCHED($28) |
| lw v0, TASK_SIGPENDING($28) |
| bnez t2, reschedule |
| bnez v0, signal_return |
| restore_all: |
| RESTORE_SOME |
| RESTORE_SP_AND_RET |
| |
| /* ------------------------------------------------------------------------ */ |
| |
| FEXPORT(ret_from_fork) |
| move a0, v0 # prev |
| jal schedule_tail |
| lw t0, TASK_PTRACE($28) # syscall tracing enabled? |
| andi t0, _PT_TRACESYS |
| bnez t0, tracesys_exit |
| |
| static_ret_from_sys_call: |
| RESTORE_STATIC |
| j fast_ret_from_sys_call |
| |
| /* ------------------------------------------------------------------------ */ |
| |
| /* ret_from_sys_call should be here but is in entry.S. */ |
| |
| /* ------------------------------------------------------------------------ */ |
| |
| /* Put this behind restore_all for the sake of the branch prediction. */ |
| signal_return: |
| .type signal_return, @function |
| |
| mfc0 t0, CP0_STATUS |
| ori t0, t0, 1 |
| mtc0 t0, CP0_STATUS |
| |
| SAVE_STATIC |
| move a0, zero |
| move a1, sp |
| jal do_signal |
| RESTORE_STATIC |
| b restore_all |
| |
| /* ------------------------------------------------------------------------ */ |
| |
| reschedule: |
| jal schedule |
| b ret_from_schedule |
| |
| /* ------------------------------------------------------------------------ */ |
| |
| trace_a_syscall: |
| SAVE_STATIC |
| sw t2, PT_R1(sp) |
| jal syscall_trace |
| lw t2, PT_R1(sp) |
| |
| lw a0, PT_R4(sp) # Restore argument registers |
| lw a1, PT_R5(sp) |
| lw a2, PT_R6(sp) |
| lw a3, PT_R7(sp) |
| jalr t2 |
| |
| li t0, -EMAXERRNO - 1 # error? |
| sltu t0, t0, v0 |
| sw t0, PT_R7(sp) # set error flag |
| beqz t0, 1f |
| |
| negu v0 # error |
| sw v0, PT_R0(sp) # set flag for syscall restarting |
| 1: sw v0, PT_R2(sp) # result |
| |
| tracesys_exit: |
| jal syscall_trace |
| j static_ret_from_sys_call |
| |
| /* ------------------------------------------------------------------------ */ |
| |
| /* |
| * 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). |
| */ |
| stackargs: |
| lw t0, PT_R29(sp) # get old user stack pointer |
| subu t3, 4 |
| sll t1, t3, 2 # stack valid? |
| |
| addu t1, t0 # end address |
| or t0, t1 |
| bltz t0, bad_stack # -> sp is bad |
| |
| lw t0, PT_R29(sp) # get old user stack pointer |
| PTR_LA t1, 3f # copy 1 to 2 arguments |
| sll t3, t3, 4 |
| subu t1, t3 |
| jr t1 |
| |
| /* Ok, copy the args from the luser stack to the kernel stack */ |
| /* |
| * I know Ralf doesn't like nops but this avoids code |
| * duplication for R3000 targets (and this is the |
| * only place where ".set reorder" doesn't help). |
| * Harald. |
| */ |
| .set push |
| .set noreorder |
| .set nomacro |
| 1: lw t1, 20(t0) # argument #6 from usp |
| nop |
| sw t1, 20(sp) |
| nop |
| 2: lw t1, 16(t0) # argument #5 from usp |
| nop |
| sw t1, 16(sp) |
| nop |
| 3: .set pop |
| |
| j stack_done # go back |
| |
| .section __ex_table,"a" |
| PTR 1b,bad_stack |
| PTR 2b,bad_stack |
| .previous |
| |
| /* ------------------------------------------------------------------------ */ |
| |
| /* |
| * The stackpointer for a call with more than 4 arguments is bad. |
| * We probably should handle this case a bit more drastic. |
| */ |
| bad_stack: |
| negu v0 # error |
| sw v0, PT_R0(sp) |
| sw v0, PT_R2(sp) |
| li t0, 1 # set error flag |
| sw t0, PT_R7(sp) |
| j fast_ret_from_sys_call |
| |
| /* ------------------------------------------------------------------------ */ |
| |
| /* |
| * The system call does not exist in this kernel |
| */ |
| illegal_syscall: |
| lw t0, TASK_PTRACE($28) # syscall tracing enabled? |
| andi t0, _PT_TRACESYS |
| beqz t0, 1f |
| |
| SAVE_STATIC |
| jal syscall_trace |
| li t0, _PT_TRACESYS |
| |
| 1: li v0, ENOSYS # error |
| sw v0, PT_R0(sp) # set flag for syscall restarting |
| sw v0, PT_R2(sp) |
| li t1, 1 # set error flag |
| sw t1, PT_R7(sp) |
| bnez t0, tracesys_exit |
| |
| j fast_ret_from_sys_call |
| END(handle_sys) |
| |
| LEAF(mips_atomic_set) |
| andi v0, a1, 3 # must be word aligned |
| bnez v0, bad_alignment |
| |
| lw v1, THREAD_CURDS($28) # in legal address range? |
| addiu a0, a1, 4 |
| or a0, a0, a1 |
| and a0, a0, v1 |
| bltz a0, bad_address |
| |
| #ifdef CONFIG_CPU_HAS_LLSC |
| /* Ok, this is the ll/sc case. World is sane :-) */ |
| 1: ll v0, (a1) |
| move a0, a2 |
| 2: sc a0, (a1) |
| beqz a0, 1b |
| |
| .section __ex_table,"a" |
| PTR 1b, bad_stack |
| PTR 2b, bad_stack |
| .previous |
| #else |
| sw a1, 16(sp) |
| sw a2, 20(sp) |
| |
| move a0, sp |
| move a2, a1 |
| li a1, 1 |
| jal do_page_fault |
| |
| lw a1, 16(sp) |
| lw a2, 20(sp) |
| |
| /* |
| * At this point the page should be readable and writable unless |
| * there was no more memory available. |
| */ |
| 1: lw v0, (a1) |
| 2: sw a2, (a1) |
| |
| .section __ex_table,"a" |
| PTR 1b, no_mem |
| PTR 2b, no_mem |
| .previous |
| #endif |
| |
| sw zero, PT_R7(sp) # success |
| sw v0, PT_R2(sp) # result |
| |
| /* Success, so skip usual error handling garbage. */ |
| lw t0, TASK_PTRACE($28) # syscall tracing enabled? |
| andi t0, _PT_TRACESYS |
| beqz t0, fast_ret_from_sys_call |
| |
| SAVE_STATIC |
| jal syscall_trace |
| j static_ret_from_sys_call |
| |
| no_mem: li v0, -ENOMEM |
| jr ra |
| |
| bad_address: |
| li v0, -EFAULT |
| jr ra |
| |
| bad_alignment: |
| li v0, -EINVAL |
| jr ra |
| END(mips_atomic_set) |
| |
| LEAF(sys_sysmips) |
| beq a0, MIPS_ATOMIC_SET, mips_atomic_set |
| j _sys_sysmips |
| END(sys_sysmips) |
| |
| LEAF(sys_syscall) |
| lw t0, PT_R29(sp) # user sp |
| |
| sltu v0, a0, __NR_O32_Linux + __NR_O32_Linux_syscalls + 1 |
| beqz v0, enosys |
| |
| sll v0, a0, 2 |
| la v1, sys_syscall |
| lw t2, sys_call_table(v0) # function pointer |
| lbu t4, sys_narg_table(a0) # number of arguments |
| |
| li v0, -EINVAL |
| beq t2, v1, out # do not recurse |
| |
| beqz t2, enosys # null function pointer? |
| |
| andi v0, t0, 0x3 # unaligned stack pointer? |
| bnez v0, sigsegv |
| |
| addu v0, t0, 16 # v0 = usp + 16 |
| addu t1, v0, 12 # 3 32-bit arguments |
| lw v1, THREAD_CURDS($28) |
| or v0, v0, t1 |
| and v1, v1, v0 |
| bltz v1, efault |
| |
| move a0, a1 # shift argument registers |
| move a1, a2 |
| move a2, a3 |
| |
| 1: lw a3, 16(t0) |
| 2: lw t3, 20(t0) |
| 3: lw t4, 24(t0) |
| |
| .section __ex_table, "a" |
| .word 1b, efault |
| .word 2b, efault |
| .word 3b, efault |
| .previous |
| |
| sw t3, 16(sp) # put into new stackframe |
| sw t4, 20(sp) |
| |
| bnez t4, 1f # zero arguments? |
| addu a0, sp, 32 # then pass sp in a0 |
| 1: |
| |
| sw t3, 16(sp) |
| sw v1, 20(sp) |
| jr t2 |
| /* Unreached */ |
| |
| enosys: li v0, -ENOSYS |
| b out |
| |
| sigsegv: |
| li a0, _SIGSEGV |
| move a1, $28 |
| jal force_sig |
| /* Fall through */ |
| |
| efault: li v0, -EFAULT |
| |
| out: jr ra |
| END(sys_syscall) |