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