1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * OpenRISC traps.c |
4 | * |
5 | * Linux architectural port borrowing liberally from similar works of |
6 | * others. All original copyrights apply as per the original source |
7 | * declaration. |
8 | * |
9 | * Modifications for the OpenRISC architecture: |
10 | * Copyright (C) 2003 Matjaz Breskvar <phoenix@bsemi.com> |
11 | * Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se> |
12 | * |
13 | * Here we handle the break vectors not used by the system call |
14 | * mechanism, as well as some general stack/register dumping |
15 | * things. |
16 | */ |
17 | |
18 | #include <linux/init.h> |
19 | #include <linux/sched.h> |
20 | #include <linux/sched/debug.h> |
21 | #include <linux/sched/task_stack.h> |
22 | #include <linux/kernel.h> |
23 | #include <linux/extable.h> |
24 | #include <linux/kmod.h> |
25 | #include <linux/string.h> |
26 | #include <linux/errno.h> |
27 | #include <linux/ptrace.h> |
28 | #include <linux/timer.h> |
29 | #include <linux/mm.h> |
30 | #include <linux/kallsyms.h> |
31 | #include <linux/uaccess.h> |
32 | |
33 | #include <asm/bug.h> |
34 | #include <asm/io.h> |
35 | #include <asm/processor.h> |
36 | #include <asm/unwinder.h> |
37 | #include <asm/sections.h> |
38 | |
39 | int lwa_flag; |
40 | static unsigned long __user *lwa_addr; |
41 | |
42 | asmlinkage void unhandled_exception(struct pt_regs *regs, int ea, int vector); |
43 | asmlinkage void do_trap(struct pt_regs *regs, unsigned long address); |
44 | asmlinkage void do_fpe_trap(struct pt_regs *regs, unsigned long address); |
45 | asmlinkage void do_unaligned_access(struct pt_regs *regs, unsigned long address); |
46 | asmlinkage void do_bus_fault(struct pt_regs *regs, unsigned long address); |
47 | asmlinkage void do_illegal_instruction(struct pt_regs *regs, |
48 | unsigned long address); |
49 | |
50 | static void print_trace(void *data, unsigned long addr, int reliable) |
51 | { |
52 | const char *loglvl = data; |
53 | |
54 | printk("%s[<%p>] %s%pS\n" , loglvl, (void *) addr, reliable ? "" : "? " , |
55 | (void *) addr); |
56 | } |
57 | |
58 | static void print_data(unsigned long base_addr, unsigned long word, int i) |
59 | { |
60 | if (i == 0) |
61 | printk("(%08lx:)\t%08lx" , base_addr + (i * 4), word); |
62 | else |
63 | printk(" %08lx:\t%08lx" , base_addr + (i * 4), word); |
64 | } |
65 | |
66 | /* displays a short stack trace */ |
67 | void show_stack(struct task_struct *task, unsigned long *esp, const char *loglvl) |
68 | { |
69 | if (esp == NULL) |
70 | esp = (unsigned long *)&esp; |
71 | |
72 | printk("%sCall trace:\n" , loglvl); |
73 | unwind_stack((void *)loglvl, esp, print_trace); |
74 | } |
75 | |
76 | void show_registers(struct pt_regs *regs) |
77 | { |
78 | int i; |
79 | int in_kernel = 1; |
80 | unsigned long esp; |
81 | |
82 | esp = (unsigned long)(regs->sp); |
83 | if (user_mode(regs)) |
84 | in_kernel = 0; |
85 | |
86 | printk("CPU #: %d\n" |
87 | " PC: %08lx SR: %08lx SP: %08lx FPCSR: %08lx\n" , |
88 | smp_processor_id(), regs->pc, regs->sr, regs->sp, |
89 | regs->fpcsr); |
90 | printk("GPR00: %08lx GPR01: %08lx GPR02: %08lx GPR03: %08lx\n" , |
91 | 0L, regs->gpr[1], regs->gpr[2], regs->gpr[3]); |
92 | printk("GPR04: %08lx GPR05: %08lx GPR06: %08lx GPR07: %08lx\n" , |
93 | regs->gpr[4], regs->gpr[5], regs->gpr[6], regs->gpr[7]); |
94 | printk("GPR08: %08lx GPR09: %08lx GPR10: %08lx GPR11: %08lx\n" , |
95 | regs->gpr[8], regs->gpr[9], regs->gpr[10], regs->gpr[11]); |
96 | printk("GPR12: %08lx GPR13: %08lx GPR14: %08lx GPR15: %08lx\n" , |
97 | regs->gpr[12], regs->gpr[13], regs->gpr[14], regs->gpr[15]); |
98 | printk("GPR16: %08lx GPR17: %08lx GPR18: %08lx GPR19: %08lx\n" , |
99 | regs->gpr[16], regs->gpr[17], regs->gpr[18], regs->gpr[19]); |
100 | printk("GPR20: %08lx GPR21: %08lx GPR22: %08lx GPR23: %08lx\n" , |
101 | regs->gpr[20], regs->gpr[21], regs->gpr[22], regs->gpr[23]); |
102 | printk("GPR24: %08lx GPR25: %08lx GPR26: %08lx GPR27: %08lx\n" , |
103 | regs->gpr[24], regs->gpr[25], regs->gpr[26], regs->gpr[27]); |
104 | printk("GPR28: %08lx GPR29: %08lx GPR30: %08lx GPR31: %08lx\n" , |
105 | regs->gpr[28], regs->gpr[29], regs->gpr[30], regs->gpr[31]); |
106 | printk(" RES: %08lx oGPR11: %08lx\n" , |
107 | regs->gpr[11], regs->orig_gpr11); |
108 | |
109 | printk("Process %s (pid: %d, stackpage=%08lx)\n" , |
110 | current->comm, current->pid, (unsigned long)current); |
111 | /* |
112 | * When in-kernel, we also print out the stack and code at the |
113 | * time of the fault.. |
114 | */ |
115 | if (in_kernel) { |
116 | |
117 | printk("\nStack: " ); |
118 | show_stack(NULL, esp: (unsigned long *)esp, KERN_EMERG); |
119 | |
120 | if (esp < PAGE_OFFSET) |
121 | goto bad_stack; |
122 | |
123 | printk("\n" ); |
124 | for (i = -8; i < 24; i += 1) { |
125 | unsigned long word; |
126 | |
127 | if (__get_user(word, &((unsigned long *)esp)[i])) { |
128 | bad_stack: |
129 | printk(" Bad Stack value." ); |
130 | break; |
131 | } |
132 | |
133 | print_data(base_addr: esp, word, i); |
134 | } |
135 | |
136 | printk("\nCode: " ); |
137 | if (regs->pc < PAGE_OFFSET) |
138 | goto bad; |
139 | |
140 | for (i = -6; i < 6; i += 1) { |
141 | unsigned long word; |
142 | |
143 | if (__get_user(word, &((unsigned long *)regs->pc)[i])) { |
144 | bad: |
145 | printk(" Bad PC value." ); |
146 | break; |
147 | } |
148 | |
149 | print_data(base_addr: regs->pc, word, i); |
150 | } |
151 | } |
152 | printk("\n" ); |
153 | } |
154 | |
155 | /* This is normally the 'Oops' routine */ |
156 | void __noreturn die(const char *str, struct pt_regs *regs, long err) |
157 | { |
158 | |
159 | console_verbose(); |
160 | printk("\n%s#: %04lx\n" , str, err & 0xffff); |
161 | show_registers(regs); |
162 | #ifdef CONFIG_JUMP_UPON_UNHANDLED_EXCEPTION |
163 | printk("\n\nUNHANDLED_EXCEPTION: entering infinite loop\n" ); |
164 | |
165 | /* shut down interrupts */ |
166 | local_irq_disable(); |
167 | |
168 | __asm__ __volatile__("l.nop 1" ); |
169 | do {} while (1); |
170 | #endif |
171 | make_task_dead(SIGSEGV); |
172 | } |
173 | |
174 | asmlinkage void unhandled_exception(struct pt_regs *regs, int ea, int vector) |
175 | { |
176 | printk("Unable to handle exception at EA =0x%x, vector 0x%x" , |
177 | ea, vector); |
178 | die(str: "Oops" , regs, err: 9); |
179 | } |
180 | |
181 | asmlinkage void do_fpe_trap(struct pt_regs *regs, unsigned long address) |
182 | { |
183 | int code = FPE_FLTUNK; |
184 | unsigned long fpcsr = regs->fpcsr; |
185 | |
186 | if (fpcsr & SPR_FPCSR_IVF) |
187 | code = FPE_FLTINV; |
188 | else if (fpcsr & SPR_FPCSR_OVF) |
189 | code = FPE_FLTOVF; |
190 | else if (fpcsr & SPR_FPCSR_UNF) |
191 | code = FPE_FLTUND; |
192 | else if (fpcsr & SPR_FPCSR_DZF) |
193 | code = FPE_FLTDIV; |
194 | else if (fpcsr & SPR_FPCSR_IXF) |
195 | code = FPE_FLTRES; |
196 | |
197 | /* Clear all flags */ |
198 | regs->fpcsr &= ~SPR_FPCSR_ALLF; |
199 | |
200 | force_sig_fault(SIGFPE, code, addr: (void __user *)regs->pc); |
201 | } |
202 | |
203 | asmlinkage void do_trap(struct pt_regs *regs, unsigned long address) |
204 | { |
205 | force_sig_fault(SIGTRAP, TRAP_BRKPT, addr: (void __user *)regs->pc); |
206 | } |
207 | |
208 | asmlinkage void do_unaligned_access(struct pt_regs *regs, unsigned long address) |
209 | { |
210 | if (user_mode(regs)) { |
211 | /* Send a SIGBUS */ |
212 | force_sig_fault(SIGBUS, BUS_ADRALN, addr: (void __user *)address); |
213 | } else { |
214 | printk("KERNEL: Unaligned Access 0x%.8lx\n" , address); |
215 | show_registers(regs); |
216 | die(str: "Die:" , regs, err: address); |
217 | } |
218 | |
219 | } |
220 | |
221 | asmlinkage void do_bus_fault(struct pt_regs *regs, unsigned long address) |
222 | { |
223 | if (user_mode(regs)) { |
224 | /* Send a SIGBUS */ |
225 | force_sig_fault(SIGBUS, BUS_ADRERR, addr: (void __user *)address); |
226 | } else { /* Kernel mode */ |
227 | printk("KERNEL: Bus error (SIGBUS) 0x%.8lx\n" , address); |
228 | show_registers(regs); |
229 | die(str: "Die:" , regs, err: address); |
230 | } |
231 | } |
232 | |
233 | static inline int in_delay_slot(struct pt_regs *regs) |
234 | { |
235 | #ifdef CONFIG_OPENRISC_NO_SPR_SR_DSX |
236 | /* No delay slot flag, do the old way */ |
237 | unsigned int op, insn; |
238 | |
239 | insn = *((unsigned int *)regs->pc); |
240 | op = insn >> 26; |
241 | switch (op) { |
242 | case 0x00: /* l.j */ |
243 | case 0x01: /* l.jal */ |
244 | case 0x03: /* l.bnf */ |
245 | case 0x04: /* l.bf */ |
246 | case 0x11: /* l.jr */ |
247 | case 0x12: /* l.jalr */ |
248 | return 1; |
249 | default: |
250 | return 0; |
251 | } |
252 | #else |
253 | return mfspr(SPR_SR) & SPR_SR_DSX; |
254 | #endif |
255 | } |
256 | |
257 | static inline void adjust_pc(struct pt_regs *regs, unsigned long address) |
258 | { |
259 | int displacement; |
260 | unsigned int rb, op, jmp; |
261 | |
262 | if (unlikely(in_delay_slot(regs))) { |
263 | /* In delay slot, instruction at pc is a branch, simulate it */ |
264 | jmp = *((unsigned int *)regs->pc); |
265 | |
266 | displacement = sign_extend32(value: ((jmp) & 0x3ffffff) << 2, index: 27); |
267 | rb = (jmp & 0x0000ffff) >> 11; |
268 | op = jmp >> 26; |
269 | |
270 | switch (op) { |
271 | case 0x00: /* l.j */ |
272 | regs->pc += displacement; |
273 | return; |
274 | case 0x01: /* l.jal */ |
275 | regs->pc += displacement; |
276 | regs->gpr[9] = regs->pc + 8; |
277 | return; |
278 | case 0x03: /* l.bnf */ |
279 | if (regs->sr & SPR_SR_F) |
280 | regs->pc += 8; |
281 | else |
282 | regs->pc += displacement; |
283 | return; |
284 | case 0x04: /* l.bf */ |
285 | if (regs->sr & SPR_SR_F) |
286 | regs->pc += displacement; |
287 | else |
288 | regs->pc += 8; |
289 | return; |
290 | case 0x11: /* l.jr */ |
291 | regs->pc = regs->gpr[rb]; |
292 | return; |
293 | case 0x12: /* l.jalr */ |
294 | regs->pc = regs->gpr[rb]; |
295 | regs->gpr[9] = regs->pc + 8; |
296 | return; |
297 | default: |
298 | break; |
299 | } |
300 | } else { |
301 | regs->pc += 4; |
302 | } |
303 | } |
304 | |
305 | static inline void simulate_lwa(struct pt_regs *regs, unsigned long address, |
306 | unsigned int insn) |
307 | { |
308 | unsigned int ra, rd; |
309 | unsigned long value; |
310 | unsigned long orig_pc; |
311 | long imm; |
312 | |
313 | const struct exception_table_entry *entry; |
314 | |
315 | orig_pc = regs->pc; |
316 | adjust_pc(regs, address); |
317 | |
318 | ra = (insn >> 16) & 0x1f; |
319 | rd = (insn >> 21) & 0x1f; |
320 | imm = (short)insn; |
321 | lwa_addr = (unsigned long __user *)(regs->gpr[ra] + imm); |
322 | |
323 | if ((unsigned long)lwa_addr & 0x3) { |
324 | do_unaligned_access(regs, address); |
325 | return; |
326 | } |
327 | |
328 | if (get_user(value, lwa_addr)) { |
329 | if (user_mode(regs)) { |
330 | force_sig(SIGSEGV); |
331 | return; |
332 | } |
333 | |
334 | if ((entry = search_exception_tables(add: orig_pc))) { |
335 | regs->pc = entry->fixup; |
336 | return; |
337 | } |
338 | |
339 | /* kernel access in kernel space, load it directly */ |
340 | value = *((unsigned long *)lwa_addr); |
341 | } |
342 | |
343 | lwa_flag = 1; |
344 | regs->gpr[rd] = value; |
345 | } |
346 | |
347 | static inline void simulate_swa(struct pt_regs *regs, unsigned long address, |
348 | unsigned int insn) |
349 | { |
350 | unsigned long __user *vaddr; |
351 | unsigned long orig_pc; |
352 | unsigned int ra, rb; |
353 | long imm; |
354 | |
355 | const struct exception_table_entry *entry; |
356 | |
357 | orig_pc = regs->pc; |
358 | adjust_pc(regs, address); |
359 | |
360 | ra = (insn >> 16) & 0x1f; |
361 | rb = (insn >> 11) & 0x1f; |
362 | imm = (short)(((insn & 0x2200000) >> 10) | (insn & 0x7ff)); |
363 | vaddr = (unsigned long __user *)(regs->gpr[ra] + imm); |
364 | |
365 | if (!lwa_flag || vaddr != lwa_addr) { |
366 | regs->sr &= ~SPR_SR_F; |
367 | return; |
368 | } |
369 | |
370 | if ((unsigned long)vaddr & 0x3) { |
371 | do_unaligned_access(regs, address); |
372 | return; |
373 | } |
374 | |
375 | if (put_user(regs->gpr[rb], vaddr)) { |
376 | if (user_mode(regs)) { |
377 | force_sig(SIGSEGV); |
378 | return; |
379 | } |
380 | |
381 | if ((entry = search_exception_tables(add: orig_pc))) { |
382 | regs->pc = entry->fixup; |
383 | return; |
384 | } |
385 | |
386 | /* kernel access in kernel space, store it directly */ |
387 | *((unsigned long *)vaddr) = regs->gpr[rb]; |
388 | } |
389 | |
390 | lwa_flag = 0; |
391 | regs->sr |= SPR_SR_F; |
392 | } |
393 | |
394 | #define INSN_LWA 0x1b |
395 | #define INSN_SWA 0x33 |
396 | |
397 | asmlinkage void do_illegal_instruction(struct pt_regs *regs, |
398 | unsigned long address) |
399 | { |
400 | unsigned int op; |
401 | unsigned int insn = *((unsigned int *)address); |
402 | |
403 | op = insn >> 26; |
404 | |
405 | switch (op) { |
406 | case INSN_LWA: |
407 | simulate_lwa(regs, address, insn); |
408 | return; |
409 | |
410 | case INSN_SWA: |
411 | simulate_swa(regs, address, insn); |
412 | return; |
413 | |
414 | default: |
415 | break; |
416 | } |
417 | |
418 | if (user_mode(regs)) { |
419 | /* Send a SIGILL */ |
420 | force_sig_fault(SIGILL, ILL_ILLOPC, addr: (void __user *)address); |
421 | } else { /* Kernel mode */ |
422 | printk("KERNEL: Illegal instruction (SIGILL) 0x%.8lx\n" , |
423 | address); |
424 | show_registers(regs); |
425 | die(str: "Die:" , regs, err: address); |
426 | } |
427 | } |
428 | |