mirror of
https://github.com/Fishwaldo/linux-bl808.git
synced 2025-03-30 02:45:55 +00:00
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
122 lines
2.9 KiB
C
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 */
|