Commit d72500f9 authored by Nicholas Piggin's avatar Nicholas Piggin Committed by Michael Ellerman

powerpc/64s/syscall: Fix ptrace syscall info with scv syscalls

The scv implementation missed updating syscall return value and error
value get/set functions to deal with the changed register ABI. This
broke ptrace PTRACE_GET_SYSCALL_INFO as well as some kernel auditing
and tracing functions.

Fix. tools/testing/selftests/ptrace/get_syscall_info now passes when
scv is used.

Fixes: 7fa95f9a ("powerpc/64s: system call support for scv/rfscv instructions")
Cc: stable@vger.kernel.org # v5.9+
Reported-by: default avatar"Dmitry V. Levin" <ldv@altlinux.org>
Signed-off-by: default avatarNicholas Piggin <npiggin@gmail.com>
Reviewed-by: default avatarDmitry V. Levin <ldv@altlinux.org>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20210520111931.2597127-2-npiggin@gmail.com
parent 5665bc35
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#ifndef _ASM_POWERPC_PTRACE_H #ifndef _ASM_POWERPC_PTRACE_H
#define _ASM_POWERPC_PTRACE_H #define _ASM_POWERPC_PTRACE_H
#include <linux/err.h>
#include <uapi/asm/ptrace.h> #include <uapi/asm/ptrace.h>
#include <asm/asm-const.h> #include <asm/asm-const.h>
...@@ -152,25 +153,6 @@ extern unsigned long profile_pc(struct pt_regs *regs); ...@@ -152,25 +153,6 @@ extern unsigned long profile_pc(struct pt_regs *regs);
long do_syscall_trace_enter(struct pt_regs *regs); long do_syscall_trace_enter(struct pt_regs *regs);
void do_syscall_trace_leave(struct pt_regs *regs); void do_syscall_trace_leave(struct pt_regs *regs);
#define kernel_stack_pointer(regs) ((regs)->gpr[1])
static inline int is_syscall_success(struct pt_regs *regs)
{
return !(regs->ccr & 0x10000000);
}
static inline long regs_return_value(struct pt_regs *regs)
{
if (is_syscall_success(regs))
return regs->gpr[3];
else
return -regs->gpr[3];
}
static inline void regs_set_return_value(struct pt_regs *regs, unsigned long rc)
{
regs->gpr[3] = rc;
}
#ifdef __powerpc64__ #ifdef __powerpc64__
#define user_mode(regs) ((((regs)->msr) >> MSR_PR_LG) & 0x1) #define user_mode(regs) ((((regs)->msr) >> MSR_PR_LG) & 0x1)
#else #else
...@@ -235,6 +217,31 @@ static __always_inline void set_trap_norestart(struct pt_regs *regs) ...@@ -235,6 +217,31 @@ static __always_inline void set_trap_norestart(struct pt_regs *regs)
regs->trap |= 0x1; regs->trap |= 0x1;
} }
#define kernel_stack_pointer(regs) ((regs)->gpr[1])
static inline int is_syscall_success(struct pt_regs *regs)
{
if (trap_is_scv(regs))
return !IS_ERR_VALUE((unsigned long)regs->gpr[3]);
else
return !(regs->ccr & 0x10000000);
}
static inline long regs_return_value(struct pt_regs *regs)
{
if (trap_is_scv(regs))
return regs->gpr[3];
if (is_syscall_success(regs))
return regs->gpr[3];
else
return -regs->gpr[3];
}
static inline void regs_set_return_value(struct pt_regs *regs, unsigned long rc)
{
regs->gpr[3] = rc;
}
#define arch_has_single_step() (1) #define arch_has_single_step() (1)
#define arch_has_block_step() (true) #define arch_has_block_step() (true)
#define ARCH_HAS_USER_SINGLE_STEP_REPORT #define ARCH_HAS_USER_SINGLE_STEP_REPORT
......
...@@ -41,11 +41,17 @@ static inline void syscall_rollback(struct task_struct *task, ...@@ -41,11 +41,17 @@ static inline void syscall_rollback(struct task_struct *task,
static inline long syscall_get_error(struct task_struct *task, static inline long syscall_get_error(struct task_struct *task,
struct pt_regs *regs) struct pt_regs *regs)
{ {
if (trap_is_scv(regs)) {
unsigned long error = regs->gpr[3];
return IS_ERR_VALUE(error) ? error : 0;
} else {
/* /*
* If the system call failed, * If the system call failed,
* regs->gpr[3] contains a positive ERRORCODE. * regs->gpr[3] contains a positive ERRORCODE.
*/ */
return (regs->ccr & 0x10000000UL) ? -regs->gpr[3] : 0; return (regs->ccr & 0x10000000UL) ? -regs->gpr[3] : 0;
}
} }
static inline long syscall_get_return_value(struct task_struct *task, static inline long syscall_get_return_value(struct task_struct *task,
...@@ -58,11 +64,14 @@ static inline void syscall_set_return_value(struct task_struct *task, ...@@ -58,11 +64,14 @@ static inline void syscall_set_return_value(struct task_struct *task,
struct pt_regs *regs, struct pt_regs *regs,
int error, long val) int error, long val)
{ {
if (trap_is_scv(regs)) {
regs->gpr[3] = (long) error ?: val;
} else {
/* /*
* In the general case it's not obvious that we must deal with CCR * In the general case it's not obvious that we must deal with
* here, as the syscall exit path will also do that for us. However * CCR here, as the syscall exit path will also do that for us.
* there are some places, eg. the signal code, which check ccr to * However there are some places, eg. the signal code, which
* decide if the value in r3 is actually an error. * check ccr to decide if the value in r3 is actually an error.
*/ */
if (error) { if (error) {
regs->ccr |= 0x10000000L; regs->ccr |= 0x10000000L;
...@@ -71,6 +80,7 @@ static inline void syscall_set_return_value(struct task_struct *task, ...@@ -71,6 +80,7 @@ static inline void syscall_set_return_value(struct task_struct *task,
regs->ccr &= ~0x10000000L; regs->ccr &= ~0x10000000L;
regs->gpr[3] = val; regs->gpr[3] = val;
} }
}
} }
static inline void syscall_get_arguments(struct task_struct *task, static inline void syscall_get_arguments(struct task_struct *task,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment