0001
0002
0003 #include <linux/compiler.h>
0004 #include <linux/context_tracking.h>
0005 #include <linux/errno.h>
0006 #include <linux/nospec.h>
0007 #include <linux/ptrace.h>
0008 #include <linux/randomize_kstack.h>
0009 #include <linux/syscalls.h>
0010
0011 #include <asm/daifflags.h>
0012 #include <asm/debug-monitors.h>
0013 #include <asm/exception.h>
0014 #include <asm/fpsimd.h>
0015 #include <asm/syscall.h>
0016 #include <asm/thread_info.h>
0017 #include <asm/unistd.h>
0018
0019 long compat_arm_syscall(struct pt_regs *regs, int scno);
0020 long sys_ni_syscall(void);
0021
0022 static long do_ni_syscall(struct pt_regs *regs, int scno)
0023 {
0024 #ifdef CONFIG_COMPAT
0025 long ret;
0026 if (is_compat_task()) {
0027 ret = compat_arm_syscall(regs, scno);
0028 if (ret != -ENOSYS)
0029 return ret;
0030 }
0031 #endif
0032
0033 return sys_ni_syscall();
0034 }
0035
0036 static long __invoke_syscall(struct pt_regs *regs, syscall_fn_t syscall_fn)
0037 {
0038 return syscall_fn(regs);
0039 }
0040
0041 static void invoke_syscall(struct pt_regs *regs, unsigned int scno,
0042 unsigned int sc_nr,
0043 const syscall_fn_t syscall_table[])
0044 {
0045 long ret;
0046
0047 add_random_kstack_offset();
0048
0049 if (scno < sc_nr) {
0050 syscall_fn_t syscall_fn;
0051 syscall_fn = syscall_table[array_index_nospec(scno, sc_nr)];
0052 ret = __invoke_syscall(regs, syscall_fn);
0053 } else {
0054 ret = do_ni_syscall(regs, scno);
0055 }
0056
0057 syscall_set_return_value(current, regs, 0, ret);
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070 choose_random_kstack_offset(get_random_int() & 0x1FF);
0071 }
0072
0073 static inline bool has_syscall_work(unsigned long flags)
0074 {
0075 return unlikely(flags & _TIF_SYSCALL_WORK);
0076 }
0077
0078 int syscall_trace_enter(struct pt_regs *regs);
0079 void syscall_trace_exit(struct pt_regs *regs);
0080
0081 static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
0082 const syscall_fn_t syscall_table[])
0083 {
0084 unsigned long flags = read_thread_flags();
0085
0086 regs->orig_x0 = regs->regs[0];
0087 regs->syscallno = scno;
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107 local_daif_restore(DAIF_PROCCTX);
0108
0109 if (flags & _TIF_MTE_ASYNC_FAULT) {
0110
0111
0112
0113
0114
0115 syscall_set_return_value(current, regs, -ERESTARTNOINTR, 0);
0116 return;
0117 }
0118
0119 if (has_syscall_work(flags)) {
0120
0121
0122
0123
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134
0135 if (scno == NO_SYSCALL)
0136 syscall_set_return_value(current, regs, -ENOSYS, 0);
0137 scno = syscall_trace_enter(regs);
0138 if (scno == NO_SYSCALL)
0139 goto trace_exit;
0140 }
0141
0142 invoke_syscall(regs, scno, sc_nr, syscall_table);
0143
0144
0145
0146
0147
0148
0149 if (!has_syscall_work(flags) && !IS_ENABLED(CONFIG_DEBUG_RSEQ)) {
0150 local_daif_mask();
0151 flags = read_thread_flags();
0152 if (!has_syscall_work(flags) && !(flags & _TIF_SINGLESTEP))
0153 return;
0154 local_daif_restore(DAIF_PROCCTX);
0155 }
0156
0157 trace_exit:
0158 syscall_trace_exit(regs);
0159 }
0160
0161
0162
0163
0164
0165 static inline void fp_user_discard(void)
0166 {
0167
0168
0169
0170
0171
0172
0173
0174
0175
0176 if (system_supports_sme() && test_thread_flag(TIF_SME)) {
0177 u64 svcr = read_sysreg_s(SYS_SVCR);
0178
0179 if (svcr & SVCR_SM_MASK)
0180 sme_smstop_sm();
0181 }
0182
0183 if (!system_supports_sve())
0184 return;
0185
0186
0187
0188
0189
0190
0191 clear_thread_flag(TIF_SVE);
0192
0193
0194
0195
0196
0197
0198
0199
0200 sve_user_disable();
0201 }
0202
0203 void do_el0_svc(struct pt_regs *regs)
0204 {
0205 fp_user_discard();
0206 el0_svc_common(regs, regs->regs[8], __NR_syscalls, sys_call_table);
0207 }
0208
0209 #ifdef CONFIG_COMPAT
0210 void do_el0_svc_compat(struct pt_regs *regs)
0211 {
0212 el0_svc_common(regs, regs->regs[7], __NR_compat_syscalls,
0213 compat_sys_call_table);
0214 }
0215 #endif