1 // SPDX-License-Identifier: GPL-2.0-only
3 * linux/arch/unicore32/kernel/traps.c
5 * Code specific to PKUnity SoC and UniCore ISA
7 * Copyright (C) 2001-2010 GUAN Xue-tao
9 * 'traps.c' handles hardware exceptions after we have saved some state.
10 * Mostly a debugging aid, but will probably kill the offending process.
12 #include <linux/module.h>
13 #include <linux/signal.h>
14 #include <linux/sched/signal.h>
15 #include <linux/sched/debug.h>
16 #include <linux/sched/task_stack.h>
17 #include <linux/spinlock.h>
18 #include <linux/personality.h>
19 #include <linux/kallsyms.h>
20 #include <linux/kdebug.h>
21 #include <linux/uaccess.h>
22 #include <linux/delay.h>
23 #include <linux/hardirq.h>
24 #include <linux/init.h>
25 #include <linux/atomic.h>
26 #include <linux/unistd.h>
28 #include <asm/cacheflush.h>
29 #include <asm/traps.h>
33 static void dump_mem(const char *, const char *, unsigned long, unsigned long);
35 void dump_backtrace_entry(unsigned long where
,
36 unsigned long from
, unsigned long frame
)
38 #ifdef CONFIG_KALLSYMS
39 printk(KERN_DEFAULT
"[<%08lx>] (%pS) from [<%08lx>] (%pS)\n",
40 where
, (void *)where
, from
, (void *)from
);
42 printk(KERN_DEFAULT
"Function entered at [<%08lx>] from [<%08lx>]\n",
48 * Stack pointers should always be within the kernels view of
49 * physical memory. If it is not there, then we can't dump
50 * out any information relating to the stack.
52 static int verify_stack(unsigned long sp
)
54 if (sp
< PAGE_OFFSET
||
55 (sp
> (unsigned long)high_memory
&& high_memory
!= NULL
))
62 * Dump out the contents of some memory nicely...
64 static void dump_mem(const char *lvl
, const char *str
, unsigned long bottom
,
72 * We need to switch to kernel mode so that we can use __get_user
73 * to safely read from kernel space. Note that we now dump the
74 * code first, just in case the backtrace kills us.
79 printk(KERN_DEFAULT
"%s%s(0x%08lx to 0x%08lx)\n",
80 lvl
, str
, bottom
, top
);
82 for (first
= bottom
& ~31; first
< top
; first
+= 32) {
84 char str
[sizeof(" 12345678") * 8 + 1];
86 memset(str
, ' ', sizeof(str
));
87 str
[sizeof(str
) - 1] = '\0';
89 for (p
= first
, i
= 0; i
< 8 && p
< top
; i
++, p
+= 4) {
90 if (p
>= bottom
&& p
< top
) {
92 if (__get_user(val
, (unsigned long *)p
) == 0)
93 sprintf(str
+ i
* 9, " %08lx", val
);
95 sprintf(str
+ i
* 9, " ????????");
98 printk(KERN_DEFAULT
"%s%04lx:%s\n", lvl
, first
& 0xffff, str
);
104 static void dump_instr(const char *lvl
, struct pt_regs
*regs
)
106 unsigned long addr
= instruction_pointer(regs
);
109 char str
[sizeof("00000000 ") * 5 + 2 + 1], *p
= str
;
113 * We need to switch to kernel mode so that we can use __get_user
114 * to safely read from kernel space. Note that we now dump the
115 * code first, just in case the backtrace kills us.
120 for (i
= -4; i
< 1; i
++) {
121 unsigned int val
, bad
;
123 bad
= __get_user(val
, &((u32
*)addr
)[i
]);
126 p
+= sprintf(p
, i
== 0 ? "(%0*x) " : "%0*x ",
129 p
+= sprintf(p
, "bad PC value");
133 printk(KERN_DEFAULT
"%sCode: %s\n", lvl
, str
);
138 static void dump_backtrace(struct pt_regs
*regs
, struct task_struct
*tsk
)
140 unsigned int fp
, mode
;
143 printk(KERN_DEFAULT
"Backtrace: ");
150 mode
= processor_mode(regs
);
151 } else if (tsk
!= current
) {
152 fp
= thread_saved_fp(tsk
);
155 asm("mov %0, fp" : "=r" (fp
) : : "cc");
160 printk("no frame pointer");
162 } else if (verify_stack(fp
)) {
163 printk("invalid frame pointer 0x%08x", fp
);
165 } else if (fp
< (unsigned long)end_of_stack(tsk
))
166 printk("frame pointer underflow");
170 c_backtrace(fp
, mode
);
173 void show_stack(struct task_struct
*tsk
, unsigned long *sp
)
175 dump_backtrace(NULL
, tsk
);
179 static int __die(const char *str
, int err
, struct thread_info
*thread
,
180 struct pt_regs
*regs
)
182 struct task_struct
*tsk
= thread
->task
;
183 static int die_counter
;
186 printk(KERN_EMERG
"Internal error: %s: %x [#%d]\n",
187 str
, err
, ++die_counter
);
189 /* trap and error numbers are mostly meaningless on UniCore */
190 ret
= notify_die(DIE_OOPS
, str
, regs
, err
, tsk
->thread
.trap_no
, \
192 if (ret
== NOTIFY_STOP
)
197 printk(KERN_EMERG
"Process %.*s (pid: %d, stack limit = 0x%p)\n",
198 TASK_COMM_LEN
, tsk
->comm
, task_pid_nr(tsk
), thread
+ 1);
200 if (!user_mode(regs
) || in_interrupt()) {
201 dump_mem(KERN_EMERG
, "Stack: ", regs
->UCreg_sp
,
202 THREAD_SIZE
+ (unsigned long)task_stack_page(tsk
));
203 dump_backtrace(regs
, tsk
);
204 dump_instr(KERN_EMERG
, regs
);
210 DEFINE_SPINLOCK(die_lock
);
213 * This function is protected against re-entrancy.
215 void die(const char *str
, struct pt_regs
*regs
, int err
)
217 struct thread_info
*thread
= current_thread_info();
222 spin_lock_irq(&die_lock
);
225 ret
= __die(str
, err
, thread
, regs
);
228 add_taint(TAINT_DIE
, LOCKDEP_NOW_UNRELIABLE
);
229 spin_unlock_irq(&die_lock
);
233 panic("Fatal exception in interrupt");
235 panic("Fatal exception");
236 if (ret
!= NOTIFY_STOP
)
240 void uc32_notify_die(const char *str
, struct pt_regs
*regs
,
241 int sig
, int code
, void __user
*addr
,
242 unsigned long err
, unsigned long trap
)
244 if (user_mode(regs
)) {
245 current
->thread
.error_code
= err
;
246 current
->thread
.trap_no
= trap
;
248 force_sig_fault(sig
, code
, addr
);
254 * bad_mode handles the impossible case in the vectors. If you see one of
255 * these, then it's extremely serious, and could mean you have buggy hardware.
256 * It never returns, and never tries to sync. We hope that we can at least
257 * dump out some state information...
259 asmlinkage
void bad_mode(struct pt_regs
*regs
, unsigned int reason
)
263 printk(KERN_CRIT
"Bad mode detected with reason 0x%x\n", reason
);
265 die("Oops - bad mode", regs
, 0);
270 void __pte_error(const char *file
, int line
, unsigned long val
)
272 printk(KERN_DEFAULT
"%s:%d: bad pte %08lx.\n", file
, line
, val
);
275 void __pmd_error(const char *file
, int line
, unsigned long val
)
277 printk(KERN_DEFAULT
"%s:%d: bad pmd %08lx.\n", file
, line
, val
);
280 void __pgd_error(const char *file
, int line
, unsigned long val
)
282 printk(KERN_DEFAULT
"%s:%d: bad pgd %08lx.\n", file
, line
, val
);
285 asmlinkage
void __div0(void)
287 printk(KERN_DEFAULT
"Division by zero in kernel.\n");
290 EXPORT_SYMBOL(__div0
);
296 /* if that doesn't kill us, halt */
297 panic("Oops failed to kill thread");
300 void __init
trap_init(void)
305 void __init
early_trap_init(void)
307 unsigned long vectors
= VECTORS_BASE
;
310 * Copy the vectors, stubs (in entry-unicore.S)
311 * into the vector page, mapped at 0xffff0000, and ensure these
312 * are visible to the instruction stream.
314 memcpy((void *)vectors
,
316 __vectors_end
- __vectors_start
);
317 memcpy((void *)vectors
+ 0x200,
319 __stubs_end
- __stubs_start
);
323 flush_icache_range(vectors
, vectors
+ PAGE_SIZE
);