1 |
199 |
simons |
/*
|
2 |
|
|
* linux/arch/arm/kernel/traps.c
|
3 |
|
|
*
|
4 |
|
|
* Copyright (C) 1995, 1996 Russell King
|
5 |
|
|
* Fragments that appear the same as linux/arch/i386/kernel/traps.c (C) Linus Torvalds
|
6 |
|
|
*/
|
7 |
|
|
|
8 |
|
|
/*
|
9 |
|
|
* 'traps.c' handles hardware exceptions after we have saved some state in
|
10 |
|
|
* 'linux/arch/arm/lib/traps.S'. Mostly a debugging aid, but will probably
|
11 |
|
|
* kill the offending process.
|
12 |
|
|
*/
|
13 |
|
|
#include <linux/config.h>
|
14 |
|
|
#include <linux/types.h>
|
15 |
|
|
#include <linux/kernel.h>
|
16 |
|
|
#include <linux/signal.h>
|
17 |
|
|
#include <linux/sched.h>
|
18 |
|
|
#include <linux/mm.h>
|
19 |
|
|
|
20 |
|
|
extern void fpe_save(struct fp_soft_struct *);
|
21 |
|
|
extern void fpe_restore(struct fp_soft_struct *);
|
22 |
|
|
extern void die_if_kernel(char *str, struct pt_regs *regs, int err, int ret);
|
23 |
|
|
extern void c_backtrace (unsigned long fp, int pmode);
|
24 |
|
|
extern int ptrace_cancel_bpt (struct task_struct *);
|
25 |
|
|
|
26 |
|
|
char *processor_modes[]=
|
27 |
|
|
{ "USER_26", "FIQ_26" , "IRQ_26" , "SVC_26" , "UK4_26" , "UK5_26" , "UK6_26" , "UK7_26" ,
|
28 |
|
|
"UK8_26" , "UK9_26" , "UK10_26", "UK11_26", "UK12_26", "UK13_26", "UK14_26", "UK15_26",
|
29 |
|
|
"USER_32", "FIQ_32" , "IRQ_32" , "SVC_32" , "UK4_32" , "UK5_32" , "UK6_32" , "ABT_32" ,
|
30 |
|
|
"UK8_32" , "UK9_32" , "UK10_32", "UND_32" , "UK12_32", "UK13_32", "UK14_32", "SYS_32"
|
31 |
|
|
};
|
32 |
|
|
|
33 |
|
|
static char *handler[]= { "prefetch abort", "data abort", "address exception", "interrupt" };
|
34 |
|
|
|
35 |
|
|
static inline void console_verbose(void)
|
36 |
|
|
{
|
37 |
|
|
extern int console_loglevel;
|
38 |
|
|
console_loglevel = 15;
|
39 |
|
|
}
|
40 |
|
|
|
41 |
|
|
int kstack_depth_to_print = 200;
|
42 |
|
|
|
43 |
|
|
static int verify_stack_pointer (unsigned long stackptr, int size)
|
44 |
|
|
{
|
45 |
|
|
#if defined(CONFIG_CPU_ARM2) || defined(CONFIG_CPU_ARM3)
|
46 |
|
|
if (stackptr < 0x02048000 || stackptr + size > 0x03000000)
|
47 |
|
|
return -EFAULT;
|
48 |
|
|
#else
|
49 |
|
|
if (stackptr < 0xc0000000 || stackptr + size > high_memory)
|
50 |
|
|
return -EFAULT;
|
51 |
|
|
#endif
|
52 |
|
|
return 0;
|
53 |
|
|
}
|
54 |
|
|
|
55 |
|
|
static void dump_stack (unsigned long *start, unsigned long *end, int offset, int max)
|
56 |
|
|
{
|
57 |
|
|
unsigned long *p;
|
58 |
|
|
int i;
|
59 |
|
|
|
60 |
|
|
for (p = start + offset, i = 0; i < max && p < end; i++, p++) {
|
61 |
|
|
if (i && (i & 7) == 0)
|
62 |
|
|
printk ("\n ");
|
63 |
|
|
printk ("%08lx ", *p);
|
64 |
|
|
}
|
65 |
|
|
printk ("\n");
|
66 |
|
|
}
|
67 |
|
|
|
68 |
|
|
/*
|
69 |
|
|
* These constants are for searching for possible module text
|
70 |
|
|
* segments. VMALLOC_OFFSET comes from mm/vmalloc.c; MODULE_RANGE is
|
71 |
|
|
* a guess of how much space is likely to be vmalloced.
|
72 |
|
|
*/
|
73 |
|
|
#define VMALLOC_OFFSET (8*1024*1024)
|
74 |
|
|
#define MODULE_RANGE (8*1024*1024)
|
75 |
|
|
|
76 |
|
|
static void dump_instr (unsigned long pc)
|
77 |
|
|
{
|
78 |
|
|
unsigned long module_start, module_end;
|
79 |
|
|
int pmin = -2, pmax = 3, ok = 0;
|
80 |
|
|
extern char start_kernel, _etext;
|
81 |
|
|
|
82 |
|
|
module_start = ((high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1));
|
83 |
|
|
module_end = module_start + MODULE_RANGE;
|
84 |
|
|
|
85 |
|
|
if ((pc >= (unsigned long) &start_kernel) &&
|
86 |
|
|
(pc <= (unsigned long) &_etext)) {
|
87 |
|
|
if (pc + pmin < (unsigned long) &start_kernel)
|
88 |
|
|
pmin = ((unsigned long) &start_kernel) - pc;
|
89 |
|
|
if (pc + pmax > (unsigned long) &_etext)
|
90 |
|
|
pmax = ((unsigned long) &_etext) - pc;
|
91 |
|
|
ok = 1;
|
92 |
|
|
} else if (pc >= module_start && pc <= module_end) {
|
93 |
|
|
if (pc + pmin < module_start)
|
94 |
|
|
pmin = module_start - pc;
|
95 |
|
|
if (pc + pmax > module_end)
|
96 |
|
|
pmax = module_end - pc;
|
97 |
|
|
ok = 1;
|
98 |
|
|
}
|
99 |
|
|
printk ("Code: ");
|
100 |
|
|
if (ok) {
|
101 |
|
|
int i;
|
102 |
|
|
for (i = pmin; i < pmax; i++)
|
103 |
|
|
printk("%08lx ", ((unsigned long *)pc)[i]);
|
104 |
|
|
printk ("\n");
|
105 |
|
|
} else
|
106 |
|
|
printk ("pc not in code space\n");
|
107 |
|
|
}
|
108 |
|
|
|
109 |
|
|
/*
|
110 |
|
|
* This function is protected against kernel-mode re-entrancy. If it
|
111 |
|
|
* is re-entered it will hang the system since we can't guarantee in
|
112 |
|
|
* this case that any of the functions that it calls are safe any more.
|
113 |
|
|
* Even the panic function could be a problem, but we'll give it a go.
|
114 |
|
|
*/
|
115 |
|
|
void die_if_kernel(char *str, struct pt_regs *regs, int err, int ret)
|
116 |
|
|
{
|
117 |
|
|
static int died = 0;
|
118 |
|
|
unsigned long cstack, sstack, frameptr;
|
119 |
|
|
|
120 |
|
|
if (user_mode(regs))
|
121 |
|
|
return;
|
122 |
|
|
|
123 |
|
|
switch (died) {
|
124 |
|
|
case 2:
|
125 |
|
|
while (1);
|
126 |
|
|
case 1:
|
127 |
|
|
died ++;
|
128 |
|
|
panic ("die_if_kernel re-entered. Major kernel corruption. Please reboot me!");
|
129 |
|
|
break;
|
130 |
|
|
case 0:
|
131 |
|
|
died ++;
|
132 |
|
|
break;
|
133 |
|
|
}
|
134 |
|
|
|
135 |
|
|
console_verbose ();
|
136 |
|
|
printk ("Internal error: %s: %x\n", str, err);
|
137 |
|
|
printk ("CPU: %d", smp_processor_id());
|
138 |
|
|
show_regs (regs);
|
139 |
|
|
printk ("Process %s (pid: %d, stackpage=%08lx)\nStack: ",
|
140 |
|
|
current->comm, current->pid, current->kernel_stack_page);
|
141 |
|
|
|
142 |
|
|
cstack = (unsigned long)(regs + 1);
|
143 |
|
|
sstack = (unsigned long)current->kernel_stack_page;
|
144 |
|
|
|
145 |
|
|
if (*(unsigned long *)sstack != STACK_MAGIC)
|
146 |
|
|
printk ("*** corrupted stack page\n ");
|
147 |
|
|
|
148 |
|
|
if (verify_stack_pointer (cstack, 4))
|
149 |
|
|
printk ("%08lx invalid kernel stack pointer\n", cstack);
|
150 |
|
|
else if(cstack > sstack + 4096)
|
151 |
|
|
printk("(sp overflow)\n");
|
152 |
|
|
else if(cstack < sstack)
|
153 |
|
|
printk("(sp underflow)\n");
|
154 |
|
|
else
|
155 |
|
|
dump_stack ((unsigned long *)sstack, (unsigned long *)sstack + 1024,
|
156 |
|
|
cstack - sstack, kstack_depth_to_print);
|
157 |
|
|
|
158 |
|
|
frameptr = regs->ARM_fp;
|
159 |
|
|
if (frameptr) {
|
160 |
|
|
if (verify_stack_pointer (frameptr, 4))
|
161 |
|
|
printk ("Backtrace: invalid frame pointer\n");
|
162 |
|
|
else {
|
163 |
|
|
printk("Backtrace: \n");
|
164 |
|
|
c_backtrace (frameptr, processor_mode(regs));
|
165 |
|
|
}
|
166 |
|
|
}
|
167 |
|
|
|
168 |
|
|
dump_instr (instruction_pointer(regs));
|
169 |
|
|
died = 0;
|
170 |
|
|
if (ret != -1)
|
171 |
|
|
do_exit (ret);
|
172 |
|
|
else {
|
173 |
|
|
cli ();
|
174 |
|
|
while (1);
|
175 |
|
|
}
|
176 |
|
|
}
|
177 |
|
|
|
178 |
|
|
void bad_user_access_alignment (const void *ptr)
|
179 |
|
|
{
|
180 |
|
|
void *pc;
|
181 |
|
|
__asm__("mov %0, lr\n": "=r" (pc));
|
182 |
|
|
printk ("bad_user_access_alignment called: ptr = %p, pc = %p\n", ptr, pc);
|
183 |
|
|
current->tss.error_code = 0;
|
184 |
|
|
current->tss.trap_no = 11;
|
185 |
|
|
force_sig (SIGBUS, current);
|
186 |
|
|
/* die_if_kernel("Oops - bad user access alignment", regs, mode, SIGBUS);*/
|
187 |
|
|
}
|
188 |
|
|
|
189 |
|
|
asmlinkage void do_undefinstr (int address, struct pt_regs *regs, int mode)
|
190 |
|
|
{
|
191 |
|
|
current->tss.error_code = 0;
|
192 |
|
|
current->tss.trap_no = 6;
|
193 |
|
|
force_sig (SIGILL, current);
|
194 |
|
|
die_if_kernel("Oops - undefined instruction", regs, mode, SIGILL);
|
195 |
|
|
}
|
196 |
|
|
|
197 |
|
|
asmlinkage void do_excpt (int address, struct pt_regs *regs, int mode)
|
198 |
|
|
{
|
199 |
|
|
current->tss.error_code = 0;
|
200 |
|
|
current->tss.trap_no = 11;
|
201 |
|
|
force_sig (SIGBUS, current);
|
202 |
|
|
die_if_kernel("Oops - address exception", regs, mode, SIGBUS);
|
203 |
|
|
}
|
204 |
|
|
|
205 |
|
|
asmlinkage void do_unexp_fiq (struct pt_regs *regs)
|
206 |
|
|
{
|
207 |
|
|
#ifndef CONFIG_IGNORE_FIQ
|
208 |
|
|
printk ("Hmm. Unexpected FIQ received, but trying to continue\n");
|
209 |
|
|
printk ("You may have a hardware problem...\n");
|
210 |
|
|
#endif
|
211 |
|
|
}
|
212 |
|
|
|
213 |
|
|
asmlinkage void bad_mode(struct pt_regs *regs, int reason, int proc_mode)
|
214 |
|
|
{
|
215 |
|
|
printk ("Bad mode in %s handler detected: mode %s\n",
|
216 |
|
|
handler[reason],
|
217 |
|
|
processor_modes[proc_mode]);
|
218 |
|
|
die_if_kernel ("Oops", regs, 0, -1);
|
219 |
|
|
}
|
220 |
|
|
|
221 |
|
|
/*
|
222 |
|
|
* 'math_state_restore()' saves the current math information in the
|
223 |
|
|
* old math state array, and gets the new ones from the current task.
|
224 |
|
|
*
|
225 |
|
|
* We no longer save/restore the math state on every context switch
|
226 |
|
|
* any more. We only do this now if it actually gets used.
|
227 |
|
|
*/
|
228 |
|
|
asmlinkage void math_state_restore (void)
|
229 |
|
|
{
|
230 |
|
|
if (last_task_used_math == current)
|
231 |
|
|
return;
|
232 |
|
|
if (last_task_used_math)
|
233 |
|
|
/*
|
234 |
|
|
* Save current fp state into last_task_used_math->tss.fpe_save
|
235 |
|
|
*/
|
236 |
|
|
fpe_save (&last_task_used_math->tss.fpstate.soft);
|
237 |
|
|
last_task_used_math = current;
|
238 |
|
|
if (current->used_math) {
|
239 |
|
|
/*
|
240 |
|
|
* Restore current fp state from current->tss.fpe_save
|
241 |
|
|
*/
|
242 |
|
|
fpe_restore (¤t->tss.fpstate.soft);
|
243 |
|
|
} else {
|
244 |
|
|
/*
|
245 |
|
|
* initialise fp state
|
246 |
|
|
*/
|
247 |
|
|
fpe_restore (&init_task.tss.fpstate.soft);
|
248 |
|
|
current->used_math = 1;
|
249 |
|
|
}
|
250 |
|
|
}
|
251 |
|
|
|
252 |
|
|
asmlinkage void arm_syscall (int no, struct pt_regs *regs)
|
253 |
|
|
{
|
254 |
|
|
switch (no) {
|
255 |
|
|
case 0: /* branch through 0 */
|
256 |
|
|
printk ("[%d] %s: branch through zero\n", current->pid, current->comm);
|
257 |
|
|
force_sig (SIGILL, current);
|
258 |
|
|
if (user_mode(regs)) {
|
259 |
|
|
show_regs (regs);
|
260 |
|
|
c_backtrace (regs->ARM_fp, processor_mode(regs));
|
261 |
|
|
}
|
262 |
|
|
die_if_kernel ("Oops", regs, 0, SIGILL);
|
263 |
|
|
break;
|
264 |
|
|
|
265 |
|
|
case 1: /* SWI_BREAK_POINT */
|
266 |
|
|
regs->ARM_pc -= 4; /* Decrement PC by one instruction */
|
267 |
|
|
ptrace_cancel_bpt (current);
|
268 |
|
|
force_sig (SIGTRAP, current);
|
269 |
|
|
break;
|
270 |
|
|
|
271 |
|
|
default:
|
272 |
|
|
printk ("[%d] %s: arm syscall %d\n", current->pid, current->comm, no);
|
273 |
|
|
force_sig (SIGILL, current);
|
274 |
|
|
if (user_mode(regs)) {
|
275 |
|
|
show_regs (regs);
|
276 |
|
|
c_backtrace (regs->ARM_fp, processor_mode(regs));
|
277 |
|
|
}
|
278 |
|
|
die_if_kernel ("Oops", regs, no, SIGILL);
|
279 |
|
|
break;
|
280 |
|
|
}
|
281 |
|
|
}
|
282 |
|
|
|
283 |
|
|
asmlinkage void deferred(int n, struct pt_regs *regs)
|
284 |
|
|
{
|
285 |
|
|
printk ("[%d] %s: old system call %X\n", current->pid, current->comm, n);
|
286 |
|
|
show_regs (regs);
|
287 |
|
|
force_sig (SIGILL, current);
|
288 |
|
|
}
|
289 |
|
|
|
290 |
|
|
asmlinkage void arm_malalignedptr(const char *str, void *pc, volatile void *ptr)
|
291 |
|
|
{
|
292 |
|
|
printk ("Mal-aligned pointer in %s: %p (PC=%p)\n", str, ptr, pc);
|
293 |
|
|
}
|
294 |
|
|
|
295 |
|
|
asmlinkage void arm_invalidptr (const char *function, int size)
|
296 |
|
|
{
|
297 |
|
|
printk ("Invalid pointer size in %s (PC=%p) size %d\n",
|
298 |
|
|
function, __builtin_return_address(0), size);
|
299 |
|
|
}
|