mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-24 21:24:00 +08:00
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: 7fa95f9ada
("powerpc/64s: system call support for scv/rfscv instructions")
Cc: stable@vger.kernel.org # v5.9+
Reported-by: "Dmitry V. Levin" <ldv@altlinux.org>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Reviewed-by: Dmitry V. Levin <ldv@altlinux.org>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20210520111931.2597127-2-npiggin@gmail.com
This commit is contained in:
parent
5665bc35c1
commit
d72500f992
@ -19,6 +19,7 @@
|
||||
#ifndef _ASM_POWERPC_PTRACE_H
|
||||
#define _ASM_POWERPC_PTRACE_H
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <uapi/asm/ptrace.h>
|
||||
#include <asm/asm-const.h>
|
||||
|
||||
@ -152,25 +153,6 @@ extern unsigned long profile_pc(struct pt_regs *regs);
|
||||
long do_syscall_trace_enter(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__
|
||||
#define user_mode(regs) ((((regs)->msr) >> MSR_PR_LG) & 0x1)
|
||||
#else
|
||||
@ -235,6 +217,31 @@ static __always_inline void set_trap_norestart(struct pt_regs *regs)
|
||||
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_block_step() (true)
|
||||
#define ARCH_HAS_USER_SINGLE_STEP_REPORT
|
||||
|
@ -41,11 +41,17 @@ static inline void syscall_rollback(struct task_struct *task,
|
||||
static inline long syscall_get_error(struct task_struct *task,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
/*
|
||||
* If the system call failed,
|
||||
* regs->gpr[3] contains a positive ERRORCODE.
|
||||
*/
|
||||
return (regs->ccr & 0x10000000UL) ? -regs->gpr[3] : 0;
|
||||
if (trap_is_scv(regs)) {
|
||||
unsigned long error = regs->gpr[3];
|
||||
|
||||
return IS_ERR_VALUE(error) ? error : 0;
|
||||
} else {
|
||||
/*
|
||||
* If the system call failed,
|
||||
* regs->gpr[3] contains a positive ERRORCODE.
|
||||
*/
|
||||
return (regs->ccr & 0x10000000UL) ? -regs->gpr[3] : 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline long syscall_get_return_value(struct task_struct *task,
|
||||
@ -58,18 +64,22 @@ static inline void syscall_set_return_value(struct task_struct *task,
|
||||
struct pt_regs *regs,
|
||||
int error, long val)
|
||||
{
|
||||
/*
|
||||
* In the general case it's not obvious that we must deal with CCR
|
||||
* here, as the syscall exit path will also do that for us. However
|
||||
* there are some places, eg. the signal code, which check ccr to
|
||||
* decide if the value in r3 is actually an error.
|
||||
*/
|
||||
if (error) {
|
||||
regs->ccr |= 0x10000000L;
|
||||
regs->gpr[3] = error;
|
||||
if (trap_is_scv(regs)) {
|
||||
regs->gpr[3] = (long) error ?: val;
|
||||
} else {
|
||||
regs->ccr &= ~0x10000000L;
|
||||
regs->gpr[3] = val;
|
||||
/*
|
||||
* In the general case it's not obvious that we must deal with
|
||||
* CCR here, as the syscall exit path will also do that for us.
|
||||
* However there are some places, eg. the signal code, which
|
||||
* check ccr to decide if the value in r3 is actually an error.
|
||||
*/
|
||||
if (error) {
|
||||
regs->ccr |= 0x10000000L;
|
||||
regs->gpr[3] = error;
|
||||
} else {
|
||||
regs->ccr &= ~0x10000000L;
|
||||
regs->gpr[3] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user