diff options
author | Mark Rutland <mark.rutland@arm.com> | 2018-07-11 14:56:44 +0100 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2018-07-12 14:49:47 +0100 |
commit | f37099b6992a0b818c7b51b899e435f4006a9f90 (patch) | |
tree | a33df4aaa94fe37221ed960361dc9799ae7a8d22 /arch/arm64/kernel/syscall.c | |
parent | 4141c857fd09dbed480f021b3eece4f46c653161 (diff) | |
download | lwn-f37099b6992a0b818c7b51b899e435f4006a9f90.tar.gz lwn-f37099b6992a0b818c7b51b899e435f4006a9f90.zip |
arm64: convert syscall trace logic to C
Currently syscall tracing is a tricky assembly state machine, which can
be rather difficult to follow, and even harder to modify. Before we
start fiddling with it for pt_regs syscalls, let's convert it to C.
This is not intended to have any functional change.
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Diffstat (limited to 'arch/arm64/kernel/syscall.c')
-rw-r--r-- | arch/arm64/kernel/syscall.c | 57 |
1 files changed, 54 insertions, 3 deletions
diff --git a/arch/arm64/kernel/syscall.c b/arch/arm64/kernel/syscall.c index 93d36f22647e..82098e6f6aa3 100644 --- a/arch/arm64/kernel/syscall.c +++ b/arch/arm64/kernel/syscall.c @@ -1,11 +1,15 @@ // SPDX-License-Identifier: GPL-2.0 +#include <linux/compiler.h> +#include <linux/context_tracking.h> #include <linux/errno.h> #include <linux/nospec.h> #include <linux/ptrace.h> #include <linux/syscalls.h> +#include <asm/daifflags.h> #include <asm/syscall.h> +#include <asm/thread_info.h> long compat_arm_syscall(struct pt_regs *regs); @@ -29,9 +33,9 @@ static long __invoke_syscall(struct pt_regs *regs, syscall_fn_t syscall_fn) regs->regs[3], regs->regs[4], regs->regs[5]); } -asmlinkage void invoke_syscall(struct pt_regs *regs, unsigned int scno, - unsigned int sc_nr, - const syscall_fn_t syscall_table[]) +static void invoke_syscall(struct pt_regs *regs, unsigned int scno, + unsigned int sc_nr, + const syscall_fn_t syscall_table[]) { long ret; @@ -45,3 +49,50 @@ asmlinkage void invoke_syscall(struct pt_regs *regs, unsigned int scno, regs->regs[0] = ret; } + +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); +} |