[PATCH v2 2/7] mips: fix mips_get_syscall_arg() for O32 and N32

Dmitry V. Levin
2025-01-13T17:11:15Z

Fix the following get_syscall_info test assertion on mips O32:
# get_syscall_info.c:218:get_syscall_info:Expected exp_args[5] (3134521044) == info.entry.args[4] (4911432)
# get_syscall_info.c:219:get_syscall_info:wait #1: entry stop mismatch

Fix the following get_syscall_info test assertion on mips64 O32 and mips64 N32:
# get_syscall_info.c:209:get_syscall_info:Expected exp_args[2] (3134324433) == info.entry.args[1] (18446744072548908753)
# get_syscall_info.c:210:get_syscall_info:wait #1: entry stop mismatch

This makes ptrace/get_syscall_info selftest pass on mips O32,
mips64 O32, and mips64 N32.

Signed-off-by: Dmitry V. Levin <ldv@strace.io>
---

Note that I'm not a MIPS expert, so I cannot tell why the get_user()
approach doesn't work for O32. Also, during experiments I discovered that
regs->pad0 approach works for O32, but why it works remains a mystery.

arch/mips/include/asm/syscall.h | 34 ++++++++++-----------------------
1 file changed, 10 insertions(+), 24 deletions(-)

diff --git a/arch/mips/include/asm/syscall.h b/arch/mips/include/asm/syscall.h
index ebdf4d910af2..2f85f2d8f754 100644
--- a/arch/mips/include/asm/syscall.h
+++ b/arch/mips/include/asm/syscall.h
@@ -57,37 +57,23 @@ static inline void mips_syscall_update_nr(struct task_struct *task,
static inline void mips_get_syscall_arg(unsigned long *arg,
struct task_struct *task, struct pt_regs *regs, unsigned int n)
{
- unsigned long usp __maybe_unused = regs->regs[29];
-
+#ifdef CONFIG_32BIT
switch (n) {
case 0: case 1: case 2: case 3:
*arg = regs->regs[4 + n];
-
- return;
-
-#ifdef CONFIG_32BIT
- case 4: case 5: case 6: case 7:
- get_user(*arg, (int *)usp + n);
return;
-#endif
-
-#ifdef CONFIG_64BIT
case 4: case 5: case 6: case 7:
-#ifdef CONFIG_MIPS32_O32
- if (test_tsk_thread_flag(task, TIF_32BIT_REGS))
- get_user(*arg, (int *)usp + n);
- else
-#endif
- *arg = regs->regs[4 + n];
-
+ *arg = regs->pad0[n];
return;
-#endif
-
- default:
- BUG();
}
-
- unreachable();
+#else
+ *arg = regs->regs[4 + n];
+ if ((IS_ENABLED(CONFIG_MIPS32_O32) &&
+ test_tsk_thread_flag(task, TIF_32BIT_REGS)) ||
+ (IS_ENABLED(CONFIG_MIPS32_N32) &&
+ test_tsk_thread_flag(task, TIF_32BIT_ADDR)))
+ *arg = (unsigned int)*arg;
+#endif
}

static inline long syscall_get_error(struct task_struct *task,
--
ldv