2018-07-11 16:56:43 +03:00
// SPDX-License-Identifier: GPL-2.0
2018-07-11 16:56:44 +03:00
# include <linux/compiler.h>
# include <linux/context_tracking.h>
2018-07-11 16:56:43 +03:00
# include <linux/errno.h>
# include <linux/nospec.h>
# include <linux/ptrace.h>
# include <linux/syscalls.h>
2018-07-11 16:56:44 +03:00
# include <asm/daifflags.h>
2018-07-11 16:56:43 +03:00
# include <asm/syscall.h>
2018-07-11 16:56:44 +03:00
# include <asm/thread_info.h>
2018-07-11 16:56:43 +03:00
long compat_arm_syscall ( struct pt_regs * regs ) ;
asmlinkage long do_ni_syscall ( struct pt_regs * regs )
{
# ifdef CONFIG_COMPAT
long ret ;
if ( is_compat_task ( ) ) {
ret = compat_arm_syscall ( regs ) ;
if ( ret ! = - ENOSYS )
return ret ;
}
# endif
return sys_ni_syscall ( ) ;
}
static long __invoke_syscall ( struct pt_regs * regs , syscall_fn_t syscall_fn )
{
return syscall_fn ( regs - > regs [ 0 ] , regs - > regs [ 1 ] , regs - > regs [ 2 ] ,
regs - > regs [ 3 ] , regs - > regs [ 4 ] , regs - > regs [ 5 ] ) ;
}
2018-07-11 16:56:44 +03:00
static void invoke_syscall ( struct pt_regs * regs , unsigned int scno ,
unsigned int sc_nr ,
const syscall_fn_t syscall_table [ ] )
2018-07-11 16:56:43 +03:00
{
long ret ;
if ( scno < sc_nr ) {
syscall_fn_t syscall_fn ;
syscall_fn = syscall_table [ array_index_nospec ( scno , sc_nr ) ] ;
ret = __invoke_syscall ( regs , syscall_fn ) ;
} else {
ret = do_ni_syscall ( regs ) ;
}
regs - > regs [ 0 ] = ret ;
}
2018-07-11 16:56:44 +03:00
static inline bool has_syscall_work ( unsigned long flags )
{
return unlikely ( flags & _TIF_SYSCALL_WORK ) ;
}
int syscall_trace_enter ( struct pt_regs * regs ) ;
void syscall_trace_exit ( struct pt_regs * regs ) ;
asmlinkage void el0_svc_common ( struct pt_regs * regs , int scno , int sc_nr ,
const syscall_fn_t syscall_table [ ] )
{
unsigned long flags = current_thread_info ( ) - > flags ;
regs - > orig_x0 = regs - > regs [ 0 ] ;
regs - > syscallno = scno ;
local_daif_restore ( DAIF_PROCCTX ) ;
user_exit ( ) ;
if ( has_syscall_work ( flags ) ) {
/* set default errno for user-issued syscall(-1) */
if ( scno = = NO_SYSCALL )
regs - > regs [ 0 ] = - ENOSYS ;
scno = syscall_trace_enter ( regs ) ;
if ( scno = = NO_SYSCALL )
goto trace_exit ;
}
invoke_syscall ( regs , scno , sc_nr , syscall_table ) ;
/*
* The tracing status may have changed under our feet , so we have to
* check again . However , if we were tracing entry , then we always trace
* exit regardless , as the old entry assembly did .
*/
if ( ! has_syscall_work ( flags ) & & ! IS_ENABLED ( CONFIG_DEBUG_RSEQ ) ) {
local_daif_mask ( ) ;
flags = current_thread_info ( ) - > flags ;
if ( ! has_syscall_work ( flags ) )
return ;
local_daif_restore ( DAIF_PROCCTX ) ;
}
trace_exit :
syscall_trace_exit ( regs ) ;
}