diff options
Diffstat (limited to 'arch/mips/include/asm/syscall.h')
| -rw-r--r-- | arch/mips/include/asm/syscall.h | 126 |
1 files changed, 87 insertions, 39 deletions
diff --git a/arch/mips/include/asm/syscall.h b/arch/mips/include/asm/syscall.h index 7c713025b23f..d19e67e2aa6a 100644 --- a/arch/mips/include/asm/syscall.h +++ b/arch/mips/include/asm/syscall.h @@ -26,45 +26,90 @@ #define __NR_syscall 4000 #endif +static inline bool mips_syscall_is_indirect(struct task_struct *task, + struct pt_regs *regs) +{ + /* O32 ABI syscall() - Either 64-bit with O32 or 32-bit */ + return (IS_ENABLED(CONFIG_32BIT) || + test_tsk_thread_flag(task, TIF_32BIT_REGS)) && + (regs->regs[2] == __NR_syscall); +} + static inline long syscall_get_nr(struct task_struct *task, struct pt_regs *regs) { - return current_thread_info()->syscall; + return task_thread_info(task)->syscall; } -static inline unsigned long mips_get_syscall_arg(unsigned long *arg, - struct task_struct *task, struct pt_regs *regs, unsigned int n) +static inline void syscall_set_nr(struct task_struct *task, + struct pt_regs *regs, + int nr) { - unsigned long usp __maybe_unused = regs->regs[29]; + /* + * New syscall number has to be assigned to regs[2] because + * it is loaded from there unconditionally after return from + * syscall_trace_enter() invocation. + * + * Consequently, if the syscall was indirect and nr != __NR_syscall, + * then after this assignment the syscall will cease to be indirect. + */ + task_thread_info(task)->syscall = regs->regs[2] = nr; +} +static inline void mips_syscall_update_nr(struct task_struct *task, + struct pt_regs *regs) +{ + /* + * v0 is the system call number, except for O32 ABI syscall(), where it + * ends up in a0. + */ + if (mips_syscall_is_indirect(task, regs)) + task_thread_info(task)->syscall = regs->regs[4]; + else + task_thread_info(task)->syscall = regs->regs[2]; +} + +static inline void mips_get_syscall_arg(unsigned long *arg, + struct task_struct *task, struct pt_regs *regs, unsigned int n) +{ +#ifdef CONFIG_32BIT switch (n) { case 0: case 1: case 2: case 3: *arg = regs->regs[4 + n]; - - return 0; - -#ifdef CONFIG_32BIT + return; case 4: case 5: case 6: case 7: - return get_user(*arg, (int *)usp + n); + *arg = regs->args[n]; + return; + } +#else + *arg = regs->regs[4 + n]; + if ((IS_ENABLED(CONFIG_MIPS32_O32) && + test_tsk_thread_flag(task, TIF_32BIT_REGS))) + *arg = (unsigned int)*arg; #endif +} -#ifdef CONFIG_64BIT +static inline void mips_set_syscall_arg(unsigned long *arg, + struct task_struct *task, struct pt_regs *regs, unsigned int n) +{ +#ifdef CONFIG_32BIT + switch (n) { + case 0: case 1: case 2: case 3: + regs->regs[4 + n] = *arg; + return; case 4: case 5: case 6: case 7: -#ifdef CONFIG_MIPS32_O32 - if (test_thread_flag(TIF_32BIT_REGS)) - return get_user(*arg, (int *)usp + n); - else -#endif - *arg = regs->regs[4 + n]; - - return 0; -#endif - - default: - BUG(); + *arg = regs->args[n] = *arg; + return; } +#else + regs->regs[4 + n] = *arg; +#endif +} - unreachable(); +static inline long syscall_get_error(struct task_struct *task, + struct pt_regs *regs) +{ + return regs->regs[7] ? -regs->regs[2] : 0; } static inline long syscall_get_return_value(struct task_struct *task, @@ -94,39 +139,42 @@ static inline void syscall_set_return_value(struct task_struct *task, static inline void syscall_get_arguments(struct task_struct *task, struct pt_regs *regs, - unsigned int i, unsigned int n, unsigned long *args) { - int ret; - /* O32 ABI syscall() - Either 64-bit with O32 or 32-bit */ - if ((IS_ENABLED(CONFIG_32BIT) || - test_tsk_thread_flag(task, TIF_32BIT_REGS)) && - (regs->regs[2] == __NR_syscall)) + unsigned int i = 0; + unsigned int n = 6; + + /* O32 ABI syscall() */ + if (mips_syscall_is_indirect(task, regs)) i++; while (n--) - ret |= mips_get_syscall_arg(args++, task, regs, i++); + mips_get_syscall_arg(args++, task, regs, i++); +} - /* - * No way to communicate an error because this is a void function. - */ -#if 0 - return ret; -#endif +static inline void syscall_set_arguments(struct task_struct *task, + struct pt_regs *regs, + unsigned long *args) +{ + unsigned int i = 0; + unsigned int n = 6; + + while (n--) + mips_set_syscall_arg(args++, task, regs, i++); } extern const unsigned long sys_call_table[]; extern const unsigned long sys32_call_table[]; extern const unsigned long sysn32_call_table[]; -static inline int syscall_get_arch(void) +static inline int syscall_get_arch(struct task_struct *task) { int arch = AUDIT_ARCH_MIPS; #ifdef CONFIG_64BIT - if (!test_thread_flag(TIF_32BIT_REGS)) { + if (!test_tsk_thread_flag(task, TIF_32BIT_REGS)) { arch |= __AUDIT_ARCH_64BIT; /* N32 sets only TIF_32BIT_ADDR */ - if (test_thread_flag(TIF_32BIT_ADDR)) + if (test_tsk_thread_flag(task, TIF_32BIT_ADDR)) arch |= __AUDIT_ARCH_CONVENTION_MIPS64_N32; } #endif |
