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

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
Loading
Loading
Loading
Loading
+26 −19
Original line number Diff line number Diff line
@@ -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
+26 −16
Original line number Diff line number Diff line
@@ -41,12 +41,18 @@ static inline void syscall_rollback(struct task_struct *task,
static inline long syscall_get_error(struct task_struct *task,
				     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,
		 * 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,
					    struct pt_regs *regs)
@@ -58,11 +64,14 @@ static inline void syscall_set_return_value(struct task_struct *task,
					    struct pt_regs *regs,
					    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
	 * 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.
		 * 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;
@@ -72,6 +81,7 @@ static inline void syscall_set_return_value(struct task_struct *task,
			regs->gpr[3] = val;
		}
	}
}

static inline void syscall_get_arguments(struct task_struct *task,
					 struct pt_regs *regs,