diff --git a/arch/mips/include/asm/syscall.h b/arch/mips/include/asm/syscall.h index 7c713025b23f..0170602a1e4e 100644 --- a/arch/mips/include/asm/syscall.h +++ b/arch/mips/include/asm/syscall.h @@ -26,12 +26,34 @@ #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; } +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 unsigned long mips_get_syscall_arg(unsigned long *arg, struct task_struct *task, struct pt_regs *regs, unsigned int n) { @@ -98,10 +120,9 @@ static inline void syscall_get_arguments(struct task_struct *task, 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)) + + /* O32 ABI syscall() */ + if (mips_syscall_is_indirect(task, regs)) i++; while (n--) diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 011993e0cce2..efbd8df8b665 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -144,6 +144,9 @@ int ptrace_setregs(struct task_struct *child, struct user_pt_regs __user *data) /* badvaddr, status, and cause may not be written. */ + /* System call number may have been changed */ + mips_syscall_update_nr(child, regs); + return 0; } @@ -345,6 +348,9 @@ static int gpr32_set(struct task_struct *target, } } + /* System call number may have been changed */ + mips_syscall_update_nr(target, regs); + return 0; } @@ -405,6 +411,9 @@ static int gpr64_set(struct task_struct *target, } } + /* System call number may have been changed */ + mips_syscall_update_nr(target, regs); + return 0; } @@ -770,6 +779,12 @@ long arch_ptrace(struct task_struct *child, long request, switch (addr) { case 0 ... 31: regs->regs[addr] = data; + /* System call number may have been changed */ + if (addr == 2) + mips_syscall_update_nr(child, regs); + else if (addr == 4 && + mips_syscall_is_indirect(child, regs)) + mips_syscall_update_nr(child, regs); break; case FPR_BASE ... FPR_BASE + 31: { union fpureg *fregs = get_fpu_regs(child); diff --git a/arch/mips/kernel/ptrace32.c b/arch/mips/kernel/ptrace32.c index 40e212d6b26b..2b9260f92ccd 100644 --- a/arch/mips/kernel/ptrace32.c +++ b/arch/mips/kernel/ptrace32.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -195,6 +196,12 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, switch (addr) { case 0 ... 31: regs->regs[addr] = data; + /* System call number may have been changed */ + if (addr == 2) + mips_syscall_update_nr(child, regs); + else if (addr == 4 && + mips_syscall_is_indirect(child, regs)) + mips_syscall_update_nr(child, regs); break; case FPR_BASE ... FPR_BASE + 31: { union fpureg *fregs = get_fpu_regs(child);