linux-bl808/arch/powerpc/include/asm/syscall.h
Rohan McLure 7e92e01b72 powerpc: Provide syscall wrapper
Implement syscall wrapper as per s390, x86, arm64. When enabled
cause handlers to accept parameters from a stack frame rather than
from user scratch register state. This allows for user registers to be
safely cleared in order to reduce caller influence on speculation
within syscall routine. The wrapper is a macro that emits syscall
handler symbols that call into the target handler, obtaining its
parameters from a struct pt_regs on the stack.

As registers are already saved to the stack prior to calling
system_call_exception, it appears that this function is executed more
efficiently with the new stack-pointer convention than with parameters
passed by registers, avoiding the allocation of a stack frame for this
method. On a 32-bit system, we see >20% performance increases on the
null_syscall microbenchmark, and on a Power 8 the performance gains
amortise the cost of clearing and restoring registers which is
implemented at the end of this series, seeing final result of ~5.6%
performance improvement on null_syscall.

Syscalls are wrapped in this fashion on all platforms except for the
Cell processor as this commit does not provide SPU support. This can be
quickly fixed in a successive patch, but requires spu_sys_callback to
allocate a pt_regs structure to satisfy the wrapped calling convention.

Co-developed-by: Andrew Donnellan <ajd@linux.ibm.com>
Signed-off-by: Andrew Donnellan <ajd@linux.ibm.com>
Signed-off-by: Rohan McLure <rmclure@linux.ibm.com>
Reviewed-by: Nicholas Piggin <npiggin@gmai.com>
[mpe: Make incompatible with COMPAT to retain clearing of high bits of args]
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20220921065605.1051927-22-rmclure@linux.ibm.com
2022-09-28 19:22:09 +10:00

122 lines
2.9 KiB
C

/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Access to user system call parameters and results
*
* Copyright (C) 2008 Red Hat, Inc. All rights reserved.
*
* See asm-generic/syscall.h for descriptions of what we must do here.
*/
#ifndef _ASM_SYSCALL_H
#define _ASM_SYSCALL_H 1
#include <uapi/linux/audit.h>
#include <linux/sched.h>
#include <linux/thread_info.h>
#ifdef CONFIG_ARCH_HAS_SYSCALL_WRAPPER
typedef long (*syscall_fn)(const struct pt_regs *);
#else
typedef long (*syscall_fn)(unsigned long, unsigned long, unsigned long,
unsigned long, unsigned long, unsigned long);
#endif
/* ftrace syscalls requires exporting the sys_call_table */
extern const syscall_fn sys_call_table[];
extern const syscall_fn compat_sys_call_table[];
static inline int syscall_get_nr(struct task_struct *task, struct pt_regs *regs)
{
/*
* Note that we are returning an int here. That means 0xffffffff, ie.
* 32-bit negative 1, will be interpreted as -1 on a 64-bit kernel.
* This is important for seccomp so that compat tasks can set r0 = -1
* to reject the syscall.
*/
if (trap_is_syscall(regs))
return regs->gpr[0];
else
return -1;
}
static inline void syscall_rollback(struct task_struct *task,
struct pt_regs *regs)
{
regs->gpr[3] = regs->orig_gpr3;
}
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)
{
return regs->gpr[3];
}
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.
*/
if (error) {
regs->ccr |= 0x10000000L;
regs->gpr[3] = error;
} else {
regs->ccr &= ~0x10000000L;
regs->gpr[3] = val;
}
}
}
static inline void syscall_get_arguments(struct task_struct *task,
struct pt_regs *regs,
unsigned long *args)
{
unsigned long val, mask = -1UL;
unsigned int n = 6;
if (is_tsk_32bit_task(task))
mask = 0xffffffff;
while (n--) {
if (n == 0)
val = regs->orig_gpr3;
else
val = regs->gpr[3 + n];
args[n] = val & mask;
}
}
static inline int syscall_get_arch(struct task_struct *task)
{
if (is_tsk_32bit_task(task))
return AUDIT_ARCH_PPC;
else if (IS_ENABLED(CONFIG_CPU_LITTLE_ENDIAN))
return AUDIT_ARCH_PPC64LE;
else
return AUDIT_ARCH_PPC64;
}
#endif /* _ASM_SYSCALL_H */