entry: Provide generic syscall entry functionality
[linux-2.6-microblaze.git] / kernel / entry / common.c
1 // SPDX-License-Identifier: GPL-2.0
2
3 #include <linux/context_tracking.h>
4 #include <linux/entry-common.h>
5
6 #define CREATE_TRACE_POINTS
7 #include <trace/events/syscalls.h>
8
9 /**
10  * enter_from_user_mode - Establish state when coming from user mode
11  *
12  * Syscall/interrupt entry disables interrupts, but user mode is traced as
13  * interrupts enabled. Also with NO_HZ_FULL RCU might be idle.
14  *
15  * 1) Tell lockdep that interrupts are disabled
16  * 2) Invoke context tracking if enabled to reactivate RCU
17  * 3) Trace interrupts off state
18  */
19 static __always_inline void enter_from_user_mode(struct pt_regs *regs)
20 {
21         arch_check_user_regs(regs);
22         lockdep_hardirqs_off(CALLER_ADDR0);
23
24         CT_WARN_ON(ct_state() != CONTEXT_USER);
25         user_exit_irqoff();
26
27         instrumentation_begin();
28         trace_hardirqs_off_finish();
29         instrumentation_end();
30 }
31
32 static inline void syscall_enter_audit(struct pt_regs *regs, long syscall)
33 {
34         if (unlikely(audit_context())) {
35                 unsigned long args[6];
36
37                 syscall_get_arguments(current, regs, args);
38                 audit_syscall_entry(syscall, args[0], args[1], args[2], args[3]);
39         }
40 }
41
42 static long syscall_trace_enter(struct pt_regs *regs, long syscall,
43                                 unsigned long ti_work)
44 {
45         long ret = 0;
46
47         /* Handle ptrace */
48         if (ti_work & (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_EMU)) {
49                 ret = arch_syscall_enter_tracehook(regs);
50                 if (ret || (ti_work & _TIF_SYSCALL_EMU))
51                         return -1L;
52         }
53
54         /* Do seccomp after ptrace, to catch any tracer changes. */
55         if (ti_work & _TIF_SECCOMP) {
56                 ret = __secure_computing(NULL);
57                 if (ret == -1L)
58                         return ret;
59         }
60
61         if (unlikely(ti_work & _TIF_SYSCALL_TRACEPOINT))
62                 trace_sys_enter(regs, syscall);
63
64         syscall_enter_audit(regs, syscall);
65
66         return ret ? : syscall;
67 }
68
69 noinstr long syscall_enter_from_user_mode(struct pt_regs *regs, long syscall)
70 {
71         unsigned long ti_work;
72
73         enter_from_user_mode(regs);
74         instrumentation_begin();
75
76         local_irq_enable();
77         ti_work = READ_ONCE(current_thread_info()->flags);
78         if (ti_work & SYSCALL_ENTER_WORK)
79                 syscall = syscall_trace_enter(regs, syscall, ti_work);
80         instrumentation_end();
81
82         return syscall;
83 }
84
85 noinstr void irqentry_enter_from_user_mode(struct pt_regs *regs)
86 {
87         enter_from_user_mode(regs);
88 }