2018-07-11 14:56:43 +01:00
// SPDX-License-Identifier: GPL-2.0
2018-07-11 14:56:44 +01:00
# include <linux/compiler.h>
# include <linux/context_tracking.h>
2018-07-11 14:56:43 +01:00
# include <linux/errno.h>
# include <linux/nospec.h>
# include <linux/ptrace.h>
# include <linux/syscalls.h>
2018-07-11 14:56:44 +01:00
# include <asm/daifflags.h>
2019-04-29 13:03:57 +01:00
# include <asm/debug-monitors.h>
2018-07-11 14:56:45 +01:00
# include <asm/fpsimd.h>
2018-07-11 14:56:43 +01:00
# include <asm/syscall.h>
2018-07-11 14:56:44 +01:00
# include <asm/thread_info.h>
2018-07-11 14:56:45 +01:00
# include <asm/unistd.h>
2018-07-11 14:56:43 +01:00
2019-01-03 18:00:39 +00:00
long compat_arm_syscall ( struct pt_regs * regs , int scno ) ;
arm64: implement syscall wrappers
To minimize the risk of userspace-controlled values being used under
speculation, this patch adds pt_regs based syscall wrappers for arm64,
which pass the minimum set of required userspace values to syscall
implementations. For each syscall, a wrapper which takes a pt_regs
argument is automatically generated, and this extracts the arguments
before calling the "real" syscall implementation.
Each syscall has three functions generated:
* __do_<compat_>sys_<name> is the "real" syscall implementation, with
the expected prototype.
* __se_<compat_>sys_<name> is the sign-extension/narrowing wrapper,
inherited from common code. This takes a series of long parameters,
casting each to the requisite types required by the "real" syscall
implementation in __do_<compat_>sys_<name>.
This wrapper *may* not be necessary on arm64 given the AAPCS rules on
unused register bits, but it seemed safer to keep the wrapper for now.
* __arm64_<compat_>_sys_<name> takes a struct pt_regs pointer, and
extracts *only* the relevant register values, passing these on to the
__se_<compat_>sys_<name> wrapper.
The syscall invocation code is updated to handle the calling convention
required by __arm64_<compat_>_sys_<name>, and passes a single struct
pt_regs pointer.
The compiler can fold the syscall implementation and its wrappers, such
that the overhead of this approach is minimized.
Note that we play games with sys_ni_syscall(). It can't be defined with
SYSCALL_DEFINE0() because we must avoid the possibility of error
injection. Additionally, there are a couple of locations where we need
to call it from C code, and we don't (currently) have a
ksys_ni_syscall(). While it has no wrapper, passing in a redundant
pt_regs pointer is benign per the AAPCS.
When ARCH_HAS_SYSCALL_WRAPPER is selected, no prototype is defines for
sys_ni_syscall(). Since we need to treat it differently for in-kernel
calls and the syscall tables, the prototype is defined as-required.
The wrappers are largely the same as their x86 counterparts, but
simplified as we don't have a variety of compat calling conventions that
require separate stubs. Unlike x86, we have some zero-argument compat
syscalls, and must define COMPAT_SYSCALL_DEFINE0() to ensure that these
are also given an __arm64_compat_sys_ prefix.
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Reviewed-by: Dominik Brodowski <linux@dominikbrodowski.net>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2018-07-11 14:56:56 +01:00
long sys_ni_syscall ( void ) ;
2019-01-03 18:00:39 +00:00
static long do_ni_syscall ( struct pt_regs * regs , int scno )
2018-07-11 14:56:43 +01:00
{
# ifdef CONFIG_COMPAT
long ret ;
if ( is_compat_task ( ) ) {
2019-01-03 18:00:39 +00:00
ret = compat_arm_syscall ( regs , scno ) ;
2018-07-11 14:56:43 +01:00
if ( ret ! = - ENOSYS )
return ret ;
}
# endif
return sys_ni_syscall ( ) ;
}
static long __invoke_syscall ( struct pt_regs * regs , syscall_fn_t syscall_fn )
{
arm64: implement syscall wrappers
To minimize the risk of userspace-controlled values being used under
speculation, this patch adds pt_regs based syscall wrappers for arm64,
which pass the minimum set of required userspace values to syscall
implementations. For each syscall, a wrapper which takes a pt_regs
argument is automatically generated, and this extracts the arguments
before calling the "real" syscall implementation.
Each syscall has three functions generated:
* __do_<compat_>sys_<name> is the "real" syscall implementation, with
the expected prototype.
* __se_<compat_>sys_<name> is the sign-extension/narrowing wrapper,
inherited from common code. This takes a series of long parameters,
casting each to the requisite types required by the "real" syscall
implementation in __do_<compat_>sys_<name>.
This wrapper *may* not be necessary on arm64 given the AAPCS rules on
unused register bits, but it seemed safer to keep the wrapper for now.
* __arm64_<compat_>_sys_<name> takes a struct pt_regs pointer, and
extracts *only* the relevant register values, passing these on to the
__se_<compat_>sys_<name> wrapper.
The syscall invocation code is updated to handle the calling convention
required by __arm64_<compat_>_sys_<name>, and passes a single struct
pt_regs pointer.
The compiler can fold the syscall implementation and its wrappers, such
that the overhead of this approach is minimized.
Note that we play games with sys_ni_syscall(). It can't be defined with
SYSCALL_DEFINE0() because we must avoid the possibility of error
injection. Additionally, there are a couple of locations where we need
to call it from C code, and we don't (currently) have a
ksys_ni_syscall(). While it has no wrapper, passing in a redundant
pt_regs pointer is benign per the AAPCS.
When ARCH_HAS_SYSCALL_WRAPPER is selected, no prototype is defines for
sys_ni_syscall(). Since we need to treat it differently for in-kernel
calls and the syscall tables, the prototype is defined as-required.
The wrappers are largely the same as their x86 counterparts, but
simplified as we don't have a variety of compat calling conventions that
require separate stubs. Unlike x86, we have some zero-argument compat
syscalls, and must define COMPAT_SYSCALL_DEFINE0() to ensure that these
are also given an __arm64_compat_sys_ prefix.
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Reviewed-by: Dominik Brodowski <linux@dominikbrodowski.net>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2018-07-11 14:56:56 +01:00
return syscall_fn ( regs ) ;
2018-07-11 14:56:43 +01:00
}
2018-07-11 14:56:44 +01: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 14:56:43 +01: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 {
2019-01-03 18:00:39 +00:00
ret = do_ni_syscall ( regs , scno ) ;
2018-07-11 14:56:43 +01:00
}
regs - > regs [ 0 ] = ret ;
}
2018-07-11 14:56:44 +01: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 ) ;
2019-04-29 13:03:57 +01:00
# ifdef CONFIG_ARM64_ERRATUM_1463225
DECLARE_PER_CPU ( int , __in_cortex_a76_erratum_1463225_wa ) ;
static void cortex_a76_erratum_1463225_svc_handler ( void )
{
u32 reg , val ;
if ( ! unlikely ( test_thread_flag ( TIF_SINGLESTEP ) ) )
return ;
if ( ! unlikely ( this_cpu_has_cap ( ARM64_WORKAROUND_1463225 ) ) )
return ;
__this_cpu_write ( __in_cortex_a76_erratum_1463225_wa , 1 ) ;
reg = read_sysreg ( mdscr_el1 ) ;
val = reg | DBG_MDSCR_SS | DBG_MDSCR_KDE ;
write_sysreg ( val , mdscr_el1 ) ;
asm volatile ( " msr daifclr, #8 " ) ;
isb ( ) ;
/* We will have taken a single-step exception by this point */
write_sysreg ( reg , mdscr_el1 ) ;
__this_cpu_write ( __in_cortex_a76_erratum_1463225_wa , 0 ) ;
}
# else
static void cortex_a76_erratum_1463225_svc_handler ( void ) { }
# endif /* CONFIG_ARM64_ERRATUM_1463225 */
2018-07-11 14:56:45 +01:00
static void el0_svc_common ( struct pt_regs * regs , int scno , int sc_nr ,
const syscall_fn_t syscall_table [ ] )
2018-07-11 14:56:44 +01:00
{
unsigned long flags = current_thread_info ( ) - > flags ;
regs - > orig_x0 = regs - > regs [ 0 ] ;
regs - > syscallno = scno ;
2019-04-29 13:03:57 +01:00
cortex_a76_erratum_1463225_svc_handler ( ) ;
2018-07-11 14:56:44 +01:00
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 ;
2018-07-30 17:43:39 +01:00
if ( ! has_syscall_work ( flags ) ) {
/*
* We ' re off to userspace , where interrupts are
* always enabled after we restore the flags from
* the SPSR .
*/
trace_hardirqs_on ( ) ;
2018-07-11 14:56:44 +01:00
return ;
2018-07-30 17:43:39 +01:00
}
2018-07-11 14:56:44 +01:00
local_daif_restore ( DAIF_PROCCTX ) ;
}
trace_exit :
syscall_trace_exit ( regs ) ;
}
2018-07-11 14:56:45 +01:00
static inline void sve_user_discard ( void )
{
if ( ! system_supports_sve ( ) )
return ;
clear_thread_flag ( TIF_SVE ) ;
/*
* task_fpsimd_load ( ) won ' t be called to update CPACR_EL1 in
* ret_to_user unless TIF_FOREIGN_FPSTATE is still set , which only
* happens if a context switch or kernel_neon_begin ( ) or context
* modification ( sigreturn , ptrace ) intervenes .
* So , ensure that CPACR_EL1 is already correct for the fast - path case .
*/
sve_user_disable ( ) ;
}
asmlinkage void el0_svc_handler ( struct pt_regs * regs )
{
sve_user_discard ( ) ;
el0_svc_common ( regs , regs - > regs [ 8 ] , __NR_syscalls , sys_call_table ) ;
}
# ifdef CONFIG_COMPAT
asmlinkage void el0_svc_compat_handler ( struct pt_regs * regs )
{
el0_svc_common ( regs , regs - > regs [ 7 ] , __NR_compat_syscalls ,
compat_sys_call_table ) ;
}
# endif