GNU Linux-libre 6.1.90-gnu
[releases.git] / arch / csky / kernel / traps.c
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
3
4 #include <linux/sched.h>
5 #include <linux/signal.h>
6 #include <linux/kernel.h>
7 #include <linux/mm.h>
8 #include <linux/module.h>
9 #include <linux/user.h>
10 #include <linux/string.h>
11 #include <linux/linkage.h>
12 #include <linux/init.h>
13 #include <linux/ptrace.h>
14 #include <linux/kallsyms.h>
15 #include <linux/rtc.h>
16 #include <linux/uaccess.h>
17 #include <linux/kprobes.h>
18 #include <linux/kdebug.h>
19 #include <linux/sched/debug.h>
20
21 #include <asm/setup.h>
22 #include <asm/traps.h>
23 #include <asm/pgalloc.h>
24 #include <asm/siginfo.h>
25
26 #include <asm/mmu_context.h>
27
28 #ifdef CONFIG_CPU_HAS_FPU
29 #include <abi/fpu.h>
30 #endif
31
32 int show_unhandled_signals = 1;
33
34 /* Defined in entry.S */
35 asmlinkage void csky_trap(void);
36
37 asmlinkage void csky_systemcall(void);
38 asmlinkage void csky_cmpxchg(void);
39 asmlinkage void csky_get_tls(void);
40 asmlinkage void csky_irq(void);
41
42 asmlinkage void csky_pagefault(void);
43
44 /* Defined in head.S */
45 asmlinkage void _start_smp_secondary(void);
46
47 void __init pre_trap_init(void)
48 {
49         int i;
50
51         mtcr("vbr", vec_base);
52
53         for (i = 1; i < 128; i++)
54                 VEC_INIT(i, csky_trap);
55 }
56
57 void __init trap_init(void)
58 {
59         VEC_INIT(VEC_AUTOVEC, csky_irq);
60
61         /* setup trap0 trap2 trap3 */
62         VEC_INIT(VEC_TRAP0, csky_systemcall);
63         VEC_INIT(VEC_TRAP2, csky_cmpxchg);
64         VEC_INIT(VEC_TRAP3, csky_get_tls);
65
66         /* setup MMU TLB exception */
67         VEC_INIT(VEC_TLBINVALIDL, csky_pagefault);
68         VEC_INIT(VEC_TLBINVALIDS, csky_pagefault);
69         VEC_INIT(VEC_TLBMODIFIED, csky_pagefault);
70
71 #ifdef CONFIG_CPU_HAS_FPU
72         init_fpu();
73 #endif
74
75 #ifdef CONFIG_SMP
76         mtcr("cr<28, 0>", virt_to_phys(vec_base));
77
78         VEC_INIT(VEC_RESET, (void *)virt_to_phys(_start_smp_secondary));
79 #endif
80 }
81
82 static DEFINE_SPINLOCK(die_lock);
83
84 void die(struct pt_regs *regs, const char *str)
85 {
86         static int die_counter;
87         int ret;
88
89         oops_enter();
90
91         spin_lock_irq(&die_lock);
92         console_verbose();
93         bust_spinlocks(1);
94
95         pr_emerg("%s [#%d]\n", str, ++die_counter);
96         print_modules();
97         show_regs(regs);
98         show_stack(current, (unsigned long *)regs->regs[4], KERN_INFO);
99
100         ret = notify_die(DIE_OOPS, str, regs, 0, trap_no(regs), SIGSEGV);
101
102         bust_spinlocks(0);
103         add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE);
104         spin_unlock_irq(&die_lock);
105         oops_exit();
106
107         if (in_interrupt())
108                 panic("Fatal exception in interrupt");
109         if (panic_on_oops)
110                 panic("Fatal exception");
111         if (ret != NOTIFY_STOP)
112                 make_task_dead(SIGSEGV);
113 }
114
115 void do_trap(struct pt_regs *regs, int signo, int code, unsigned long addr)
116 {
117         struct task_struct *tsk = current;
118
119         if (show_unhandled_signals && unhandled_signal(tsk, signo)
120             && printk_ratelimit()) {
121                 pr_info("%s[%d]: unhandled signal %d code 0x%x at 0x%08lx",
122                         tsk->comm, task_pid_nr(tsk), signo, code, addr);
123                 print_vma_addr(KERN_CONT " in ", instruction_pointer(regs));
124                 pr_cont("\n");
125                 show_regs(regs);
126         }
127
128         force_sig_fault(signo, code, (void __user *)addr);
129 }
130
131 static void do_trap_error(struct pt_regs *regs, int signo, int code,
132         unsigned long addr, const char *str)
133 {
134         current->thread.trap_no = trap_no(regs);
135
136         if (user_mode(regs)) {
137                 do_trap(regs, signo, code, addr);
138         } else {
139                 if (!fixup_exception(regs))
140                         die(regs, str);
141         }
142 }
143
144 #define DO_ERROR_INFO(name, signo, code, str)                           \
145 asmlinkage __visible void name(struct pt_regs *regs)                    \
146 {                                                                       \
147         do_trap_error(regs, signo, code, regs->pc, "Oops - " str);      \
148 }
149
150 DO_ERROR_INFO(do_trap_unknown,
151         SIGILL, ILL_ILLTRP, "unknown exception");
152 DO_ERROR_INFO(do_trap_zdiv,
153         SIGFPE, FPE_INTDIV, "error zero div exception");
154 DO_ERROR_INFO(do_trap_buserr,
155         SIGSEGV, ILL_ILLADR, "error bus error exception");
156
157 asmlinkage void do_trap_misaligned(struct pt_regs *regs)
158 {
159 #ifdef CONFIG_CPU_NEED_SOFTALIGN
160         csky_alignment(regs);
161 #else
162         current->thread.trap_no = trap_no(regs);
163         do_trap_error(regs, SIGBUS, BUS_ADRALN, regs->pc,
164                       "Oops - load/store address misaligned");
165 #endif
166 }
167
168 asmlinkage void do_trap_bkpt(struct pt_regs *regs)
169 {
170 #ifdef CONFIG_KPROBES
171         if (kprobe_single_step_handler(regs))
172                 return;
173 #endif
174 #ifdef CONFIG_UPROBES
175         if (uprobe_single_step_handler(regs))
176                 return;
177 #endif
178         if (user_mode(regs)) {
179                 send_sig(SIGTRAP, current, 0);
180                 return;
181         }
182
183         do_trap_error(regs, SIGILL, ILL_ILLTRP, regs->pc,
184                       "Oops - illegal trap exception");
185 }
186
187 asmlinkage void do_trap_illinsn(struct pt_regs *regs)
188 {
189         current->thread.trap_no = trap_no(regs);
190
191 #ifdef CONFIG_KPROBES
192         if (kprobe_breakpoint_handler(regs))
193                 return;
194 #endif
195 #ifdef CONFIG_UPROBES
196         if (uprobe_breakpoint_handler(regs))
197                 return;
198 #endif
199 #ifndef CONFIG_CPU_NO_USER_BKPT
200         if (*(uint16_t *)instruction_pointer(regs) != USR_BKPT) {
201                 send_sig(SIGTRAP, current, 0);
202                 return;
203         }
204 #endif
205
206         do_trap_error(regs, SIGILL, ILL_ILLOPC, regs->pc,
207                       "Oops - illegal instruction exception");
208 }
209
210 asmlinkage void do_trap_fpe(struct pt_regs *regs)
211 {
212 #ifdef CONFIG_CPU_HAS_FPU
213         return fpu_fpe(regs);
214 #else
215         do_trap_error(regs, SIGILL, ILL_ILLOPC, regs->pc,
216                       "Oops - fpu instruction exception");
217 #endif
218 }
219
220 asmlinkage void do_trap_priv(struct pt_regs *regs)
221 {
222 #ifdef CONFIG_CPU_HAS_FPU
223         if (user_mode(regs) && fpu_libc_helper(regs))
224                 return;
225 #endif
226         do_trap_error(regs, SIGILL, ILL_PRVOPC, regs->pc,
227                       "Oops - illegal privileged exception");
228 }
229
230 asmlinkage void trap_c(struct pt_regs *regs)
231 {
232         switch (trap_no(regs)) {
233         case VEC_ZERODIV:
234                 do_trap_zdiv(regs);
235                 break;
236         case VEC_TRACE:
237                 do_trap_bkpt(regs);
238                 break;
239         case VEC_ILLEGAL:
240                 do_trap_illinsn(regs);
241                 break;
242         case VEC_TRAP1:
243         case VEC_BREAKPOINT:
244                 do_trap_bkpt(regs);
245                 break;
246         case VEC_ACCESS:
247                 do_trap_buserr(regs);
248                 break;
249         case VEC_ALIGN:
250                 do_trap_misaligned(regs);
251                 break;
252         case VEC_FPE:
253                 do_trap_fpe(regs);
254                 break;
255         case VEC_PRIV:
256                 do_trap_priv(regs);
257                 break;
258         default:
259                 do_trap_unknown(regs);
260                 break;
261         }
262 }