Blame view
arch/sh/kernel/traps_32.c
21.3 KB
6b0022305 sh: Proper show_s... |
1 2 3 |
/* * 'traps.c' handles hardware traps and faults after we have saved some * state in 'entry.S'. |
1da177e4c Linux-2.6.12-rc2 |
4 5 6 7 |
* * SuperH version: Copyright (C) 1999 Niibe Yutaka * Copyright (C) 2000 Philipp Rumpf * Copyright (C) 2000 David Howells |
ace2dc7d1 sh: wire up perf ... |
8 |
* Copyright (C) 2002 - 2010 Paul Mundt |
6b0022305 sh: Proper show_s... |
9 10 11 12 |
* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. |
1da177e4c Linux-2.6.12-rc2 |
13 |
*/ |
1da177e4c Linux-2.6.12-rc2 |
14 |
#include <linux/kernel.h> |
1da177e4c Linux-2.6.12-rc2 |
15 |
#include <linux/ptrace.h> |
ba84be233 remove linux/hard... |
16 |
#include <linux/hardirq.h> |
1da177e4c Linux-2.6.12-rc2 |
17 |
#include <linux/init.h> |
1da177e4c Linux-2.6.12-rc2 |
18 19 20 |
#include <linux/spinlock.h> #include <linux/module.h> #include <linux/kallsyms.h> |
1f666587d sh: Fix exception... |
21 |
#include <linux/io.h> |
fa6915117 sh: generic BUG()... |
22 |
#include <linux/bug.h> |
9b8c90eb0 sh: show held loc... |
23 |
#include <linux/debug_locks.h> |
b118ca572 sh: Convert to co... |
24 |
#include <linux/kdebug.h> |
e11327662 sh: Wire up kdump... |
25 |
#include <linux/kexec.h> |
dc34d312c sh: BUG() handlin... |
26 |
#include <linux/limits.h> |
af67c3a9e sh: update die() ... |
27 |
#include <linux/sysfs.h> |
a99eae541 sh: Split out the... |
28 |
#include <linux/uaccess.h> |
ace2dc7d1 sh: wire up perf ... |
29 |
#include <linux/perf_event.h> |
1da177e4c Linux-2.6.12-rc2 |
30 |
#include <asm/system.h> |
a99eae541 sh: Split out the... |
31 |
#include <asm/alignment.h> |
fad0f9013 (fad0f90134197259... |
32 |
#include <asm/fpu.h> |
d39f54501 sh: Add kprobes s... |
33 |
#include <asm/kprobes.h> |
1da177e4c Linux-2.6.12-rc2 |
34 |
|
1da177e4c Linux-2.6.12-rc2 |
35 |
#ifdef CONFIG_CPU_SH2 |
0983b3184 sh: Wire up divis... |
36 37 38 39 |
# define TRAP_RESERVED_INST 4 # define TRAP_ILLEGAL_SLOT_INST 6 # define TRAP_ADDRESS_ERROR 9 # ifdef CONFIG_CPU_SH2A |
cd89436e5 sh: Add UBC trap ... |
40 |
# define TRAP_UBC 12 |
6e80f5e8c sh2(A) exception ... |
41 |
# define TRAP_FPU_ERROR 13 |
0983b3184 sh: Wire up divis... |
42 43 44 |
# define TRAP_DIVZERO_ERROR 17 # define TRAP_DIVOVF_ERROR 18 # endif |
1da177e4c Linux-2.6.12-rc2 |
45 46 47 48 |
#else #define TRAP_RESERVED_INST 12 #define TRAP_ILLEGAL_SLOT_INST 13 #endif |
6b0022305 sh: Proper show_s... |
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
static void dump_mem(const char *str, unsigned long bottom, unsigned long top) { unsigned long p; int i; printk("%s(0x%08lx to 0x%08lx) ", str, bottom, top); for (p = bottom & ~31; p < top; ) { printk("%04lx: ", p & 0xffff); for (i = 0; i < 8; i++, p += 4) { unsigned int val; if (p < bottom || p >= top) printk(" "); else { if (__get_user(val, (unsigned int __user *)p)) { printk(" "); return; } printk("%08x ", val); } } printk(" "); } } |
1da177e4c Linux-2.6.12-rc2 |
78 |
|
3a2e117e2 sh: Add die chain... |
79 |
static DEFINE_SPINLOCK(die_lock); |
1da177e4c Linux-2.6.12-rc2 |
80 81 82 83 |
void die(const char * str, struct pt_regs * regs, long err) { static int die_counter; |
552739821 sh: oops_enter()/... |
84 |
oops_enter(); |
1da177e4c Linux-2.6.12-rc2 |
85 |
spin_lock_irq(&die_lock); |
af67c3a9e sh: update die() ... |
86 |
console_verbose(); |
6b0022305 sh: Proper show_s... |
87 |
bust_spinlocks(1); |
1da177e4c Linux-2.6.12-rc2 |
88 89 |
printk("%s: %04lx [#%d] ", str, err & 0xffff, ++die_counter); |
6b0022305 sh: Proper show_s... |
90 |
print_modules(); |
1da177e4c Linux-2.6.12-rc2 |
91 |
show_regs(regs); |
6b0022305 sh: Proper show_s... |
92 |
|
19c5870c0 Use helpers to ob... |
93 94 95 |
printk("Process: %s (pid: %d, stack limit = %p) ", current->comm, task_pid_nr(current), task_stack_page(current) + 1); |
6b0022305 sh: Proper show_s... |
96 97 98 |
if (!user_mode(regs) || in_interrupt()) dump_mem("Stack: ", regs->regs[15], THREAD_SIZE + |
b5a1bcbee sh: Set up correc... |
99 |
(unsigned long)task_stack_page(current)); |
6b0022305 sh: Proper show_s... |
100 |
|
c9306f0ef sh: Wire up oops ... |
101 |
notify_die(DIE_OOPS, str, regs, err, 255, SIGSEGV); |
6b0022305 sh: Proper show_s... |
102 |
bust_spinlocks(0); |
bcdcd8e72 Report that kerne... |
103 |
add_taint(TAINT_DIE); |
1da177e4c Linux-2.6.12-rc2 |
104 |
spin_unlock_irq(&die_lock); |
af67c3a9e sh: update die() ... |
105 |
oops_exit(); |
e11327662 sh: Wire up kdump... |
106 107 108 109 110 111 112 113 114 |
if (kexec_should_crash(current)) crash_kexec(regs); if (in_interrupt()) panic("Fatal exception in interrupt"); if (panic_on_oops) panic("Fatal exception"); |
1da177e4c Linux-2.6.12-rc2 |
115 116 |
do_exit(SIGSEGV); } |
6b0022305 sh: Proper show_s... |
117 118 |
static inline void die_if_kernel(const char *str, struct pt_regs *regs, long err) |
1da177e4c Linux-2.6.12-rc2 |
119 120 121 122 |
{ if (!user_mode(regs)) die(str, regs, err); } |
1da177e4c Linux-2.6.12-rc2 |
123 124 125 126 127 |
/* * try and fix up kernelspace address errors * - userspace errors just cause EFAULT to be returned, resulting in SEGV * - kernel/userspace interfaces cause a jump to an appropriate handler * - other kernel errors are bad |
1da177e4c Linux-2.6.12-rc2 |
128 |
*/ |
2afb447f3 sh: fix unaligned... |
129 |
static void die_if_no_fixup(const char * str, struct pt_regs * regs, long err) |
1da177e4c Linux-2.6.12-rc2 |
130 |
{ |
6b0022305 sh: Proper show_s... |
131 |
if (!user_mode(regs)) { |
1da177e4c Linux-2.6.12-rc2 |
132 133 134 135 |
const struct exception_table_entry *fixup; fixup = search_exception_tables(regs->pc); if (fixup) { regs->pc = fixup->fixup; |
2afb447f3 sh: fix unaligned... |
136 |
return; |
1da177e4c Linux-2.6.12-rc2 |
137 |
} |
b344e24a8 sh: unwinder: Int... |
138 |
|
1da177e4c Linux-2.6.12-rc2 |
139 140 |
die(str, regs, err); } |
1da177e4c Linux-2.6.12-rc2 |
141 |
} |
86c0179c9 sh: break out una... |
142 143 144 |
static inline void sign_extend(unsigned int count, unsigned char *dst) { #ifdef __LITTLE_ENDIAN__ |
4252c659a sh: add byte supp... |
145 146 147 148 149 |
if ((count == 1) && dst[0] & 0x80) { dst[1] = 0xff; dst[2] = 0xff; dst[3] = 0xff; } |
86c0179c9 sh: break out una... |
150 151 152 153 154 |
if ((count == 2) && dst[1] & 0x80) { dst[2] = 0xff; dst[3] = 0xff; } #else |
4252c659a sh: add byte supp... |
155 156 157 |
if ((count == 1) && dst[3] & 0x80) { dst[2] = 0xff; dst[1] = 0xff; |
86c0179c9 sh: break out una... |
158 |
dst[0] = 0xff; |
4252c659a sh: add byte supp... |
159 160 |
} if ((count == 2) && dst[2] & 0x80) { |
86c0179c9 sh: break out una... |
161 |
dst[1] = 0xff; |
4252c659a sh: add byte supp... |
162 |
dst[0] = 0xff; |
86c0179c9 sh: break out una... |
163 164 165 |
} #endif } |
e7cc9a734 sh: trapped io su... |
166 167 168 169 |
static struct mem_access user_mem_access = { copy_from_user, copy_to_user, }; |
1da177e4c Linux-2.6.12-rc2 |
170 171 172 173 174 175 176 |
/* * handle an instruction that does an unaligned memory access by emulating the * desired behaviour * - note that PC _may not_ point to the faulting instruction * (if that instruction is in a branch delay slot) * - return 0 if emulation okay, -EFAULT on existential error */ |
2bcfffa42 sh: Rename opcode... |
177 |
static int handle_unaligned_ins(insn_size_t instruction, struct pt_regs *regs, |
e7cc9a734 sh: trapped io su... |
178 |
struct mem_access *ma) |
1da177e4c Linux-2.6.12-rc2 |
179 180 181 182 |
{ int ret, index, count; unsigned long *rm, *rn; unsigned char *src, *dst; |
fa43972fa sh: fixup many sp... |
183 |
unsigned char __user *srcu, *dstu; |
1da177e4c Linux-2.6.12-rc2 |
184 185 186 187 188 189 190 191 |
index = (instruction>>8)&15; /* 0x0F00 */ rn = ®s->regs[index]; index = (instruction>>4)&15; /* 0x00F0 */ rm = ®s->regs[index]; count = 1<<(instruction&3); |
7436cde6b sh: Allow user co... |
192 |
switch (count) { |
a99eae541 sh: Split out the... |
193 194 195 196 |
case 1: inc_unaligned_byte_access(); break; case 2: inc_unaligned_word_access(); break; case 4: inc_unaligned_dword_access(); break; case 8: inc_unaligned_multi_access(); break; |
7436cde6b sh: Allow user co... |
197 |
} |
1da177e4c Linux-2.6.12-rc2 |
198 199 200 201 202 |
ret = -EFAULT; switch (instruction>>12) { case 0: /* mov.[bwl] to/from memory via r0+rn */ if (instruction & 8) { /* from memory */ |
fa43972fa sh: fixup many sp... |
203 204 205 206 |
srcu = (unsigned char __user *)*rm; srcu += regs->regs[0]; dst = (unsigned char *)rn; *(unsigned long *)dst = 0; |
1da177e4c Linux-2.6.12-rc2 |
207 |
|
86c0179c9 sh: break out una... |
208 |
#if !defined(__LITTLE_ENDIAN__) |
1da177e4c Linux-2.6.12-rc2 |
209 |
dst += 4-count; |
86c0179c9 sh: break out una... |
210 |
#endif |
fa43972fa sh: fixup many sp... |
211 |
if (ma->from(dst, srcu, count)) |
1da177e4c Linux-2.6.12-rc2 |
212 |
goto fetch_fault; |
86c0179c9 sh: break out una... |
213 |
sign_extend(count, dst); |
1da177e4c Linux-2.6.12-rc2 |
214 215 |
} else { /* to memory */ |
fa43972fa sh: fixup many sp... |
216 |
src = (unsigned char *)rm; |
1da177e4c Linux-2.6.12-rc2 |
217 218 219 |
#if !defined(__LITTLE_ENDIAN__) src += 4-count; #endif |
fa43972fa sh: fixup many sp... |
220 221 |
dstu = (unsigned char __user *)*rn; dstu += regs->regs[0]; |
1da177e4c Linux-2.6.12-rc2 |
222 |
|
fa43972fa sh: fixup many sp... |
223 |
if (ma->to(dstu, src, count)) |
1da177e4c Linux-2.6.12-rc2 |
224 225 226 227 228 229 230 |
goto fetch_fault; } ret = 0; break; case 1: /* mov.l Rm,@(disp,Rn) */ src = (unsigned char*) rm; |
fa43972fa sh: fixup many sp... |
231 232 |
dstu = (unsigned char __user *)*rn; dstu += (instruction&0x000F)<<2; |
1da177e4c Linux-2.6.12-rc2 |
233 |
|
fa43972fa sh: fixup many sp... |
234 |
if (ma->to(dstu, src, 4)) |
1da177e4c Linux-2.6.12-rc2 |
235 236 |
goto fetch_fault; ret = 0; |
b5a1bcbee sh: Set up correc... |
237 |
break; |
1da177e4c Linux-2.6.12-rc2 |
238 239 240 241 242 |
case 2: /* mov.[bwl] to memory, possibly with pre-decrement */ if (instruction & 4) *rn -= count; src = (unsigned char*) rm; |
fa43972fa sh: fixup many sp... |
243 |
dstu = (unsigned char __user *)*rn; |
1da177e4c Linux-2.6.12-rc2 |
244 245 246 |
#if !defined(__LITTLE_ENDIAN__) src += 4-count; #endif |
fa43972fa sh: fixup many sp... |
247 |
if (ma->to(dstu, src, count)) |
1da177e4c Linux-2.6.12-rc2 |
248 249 250 251 252 |
goto fetch_fault; ret = 0; break; case 5: /* mov.l @(disp,Rm),Rn */ |
fa43972fa sh: fixup many sp... |
253 254 255 256 |
srcu = (unsigned char __user *)*rm; srcu += (instruction & 0x000F) << 2; dst = (unsigned char *)rn; *(unsigned long *)dst = 0; |
1da177e4c Linux-2.6.12-rc2 |
257 |
|
fa43972fa sh: fixup many sp... |
258 |
if (ma->from(dst, srcu, 4)) |
1da177e4c Linux-2.6.12-rc2 |
259 260 |
goto fetch_fault; ret = 0; |
b5a1bcbee sh: Set up correc... |
261 |
break; |
1da177e4c Linux-2.6.12-rc2 |
262 263 |
case 6: /* mov.[bwl] from memory, possibly with post-increment */ |
fa43972fa sh: fixup many sp... |
264 |
srcu = (unsigned char __user *)*rm; |
1da177e4c Linux-2.6.12-rc2 |
265 266 267 268 |
if (instruction & 4) *rm += count; dst = (unsigned char*) rn; *(unsigned long*)dst = 0; |
b5a1bcbee sh: Set up correc... |
269 |
|
86c0179c9 sh: break out una... |
270 |
#if !defined(__LITTLE_ENDIAN__) |
1da177e4c Linux-2.6.12-rc2 |
271 |
dst += 4-count; |
86c0179c9 sh: break out una... |
272 |
#endif |
fa43972fa sh: fixup many sp... |
273 |
if (ma->from(dst, srcu, count)) |
1da177e4c Linux-2.6.12-rc2 |
274 |
goto fetch_fault; |
86c0179c9 sh: break out una... |
275 |
sign_extend(count, dst); |
1da177e4c Linux-2.6.12-rc2 |
276 277 278 279 280 281 |
ret = 0; break; case 8: switch ((instruction&0xFF00)>>8) { case 0x81: /* mov.w R0,@(disp,Rn) */ |
fa43972fa sh: fixup many sp... |
282 |
src = (unsigned char *) ®s->regs[0]; |
1da177e4c Linux-2.6.12-rc2 |
283 284 285 |
#if !defined(__LITTLE_ENDIAN__) src += 2; #endif |
fa43972fa sh: fixup many sp... |
286 287 |
dstu = (unsigned char __user *)*rm; /* called Rn in the spec */ dstu += (instruction & 0x000F) << 1; |
1da177e4c Linux-2.6.12-rc2 |
288 |
|
fa43972fa sh: fixup many sp... |
289 |
if (ma->to(dstu, src, 2)) |
1da177e4c Linux-2.6.12-rc2 |
290 291 292 293 294 |
goto fetch_fault; ret = 0; break; case 0x85: /* mov.w @(disp,Rm),R0 */ |
fa43972fa sh: fixup many sp... |
295 296 297 298 |
srcu = (unsigned char __user *)*rm; srcu += (instruction & 0x000F) << 1; dst = (unsigned char *) ®s->regs[0]; *(unsigned long *)dst = 0; |
1da177e4c Linux-2.6.12-rc2 |
299 300 301 302 |
#if !defined(__LITTLE_ENDIAN__) dst += 2; #endif |
fa43972fa sh: fixup many sp... |
303 |
if (ma->from(dst, srcu, 2)) |
1da177e4c Linux-2.6.12-rc2 |
304 |
goto fetch_fault; |
86c0179c9 sh: break out una... |
305 |
sign_extend(2, dst); |
1da177e4c Linux-2.6.12-rc2 |
306 307 308 309 |
ret = 0; break; } break; |
34f7145a6 sh: Add unaligned... |
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 |
case 9: /* mov.w @(disp,PC),Rn */ srcu = (unsigned char __user *)regs->pc; srcu += 4; srcu += (instruction & 0x00FF) << 1; dst = (unsigned char *)rn; *(unsigned long *)dst = 0; #if !defined(__LITTLE_ENDIAN__) dst += 2; #endif if (ma->from(dst, srcu, 2)) goto fetch_fault; sign_extend(2, dst); ret = 0; break; case 0xd: /* mov.l @(disp,PC),Rn */ srcu = (unsigned char __user *)(regs->pc & ~0x3); srcu += 4; srcu += (instruction & 0x00FF) << 2; dst = (unsigned char *)rn; *(unsigned long *)dst = 0; if (ma->from(dst, srcu, 4)) goto fetch_fault; ret = 0; break; |
1da177e4c Linux-2.6.12-rc2 |
339 340 341 342 343 344 345 |
} return ret; fetch_fault: /* Argh. Address not only misaligned but also non-existent. * Raise an EFAULT and see if it's trapped */ |
2afb447f3 sh: fix unaligned... |
346 347 |
die_if_no_fixup("Fault in unaligned fixup", regs, 0); return -EFAULT; |
1da177e4c Linux-2.6.12-rc2 |
348 349 350 351 352 353 |
} /* * emulate the instruction in the delay slot * - fetches the instruction from PC+2 */ |
e7cc9a734 sh: trapped io su... |
354 |
static inline int handle_delayslot(struct pt_regs *regs, |
2bcfffa42 sh: Rename opcode... |
355 |
insn_size_t old_instruction, |
e7cc9a734 sh: trapped io su... |
356 |
struct mem_access *ma) |
1da177e4c Linux-2.6.12-rc2 |
357 |
{ |
2bcfffa42 sh: Rename opcode... |
358 |
insn_size_t instruction; |
fa43972fa sh: fixup many sp... |
359 360 |
void __user *addr = (void __user *)(regs->pc + instruction_size(old_instruction)); |
1da177e4c Linux-2.6.12-rc2 |
361 |
|
4b5a9ef52 sh: use opcode_t ... |
362 |
if (copy_from_user(&instruction, addr, sizeof(instruction))) { |
1da177e4c Linux-2.6.12-rc2 |
363 364 365 366 367 |
/* the instruction-fetch faulted */ if (user_mode(regs)) return -EFAULT; /* kernel */ |
b5a1bcbee sh: Set up correc... |
368 369 |
die("delay-slot-insn faulting in handle_unaligned_delayslot", regs, 0); |
1da177e4c Linux-2.6.12-rc2 |
370 |
} |
e7cc9a734 sh: trapped io su... |
371 |
return handle_unaligned_ins(instruction, regs, ma); |
1da177e4c Linux-2.6.12-rc2 |
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 |
} /* * handle an instruction that does an unaligned memory access * - have to be careful of branch delay-slot instructions that fault * SH3: * - if the branch would be taken PC points to the branch * - if the branch would not be taken, PC points to delay-slot * SH4: * - PC always points to delayed branch * - return 0 if handled, -EFAULT if failed (may not return if in kernel) */ /* Macros to determine offset from current PC for branch instructions */ /* Explicit type coercion is used to force sign extension where needed */ #define SH_PC_8BIT_OFFSET(instr) ((((signed char)(instr))*2) + 4) #define SH_PC_12BIT_OFFSET(instr) ((((signed short)(instr<<4))>>3) + 4) |
2bcfffa42 sh: Rename opcode... |
389 |
int handle_unaligned_access(insn_size_t instruction, struct pt_regs *regs, |
ace2dc7d1 sh: wire up perf ... |
390 391 |
struct mem_access *ma, int expected, unsigned long address) |
1da177e4c Linux-2.6.12-rc2 |
392 393 394 |
{ u_int rm; int ret, index; |
23c4c8217 sh: Handle unalig... |
395 396 397 398 399 |
/* * XXX: We can't handle mixed 16/32-bit instructions yet */ if (instruction_size(instruction) != 2) return -EINVAL; |
1da177e4c Linux-2.6.12-rc2 |
400 401 |
index = (instruction>>8)&15; /* 0x0F00 */ rm = regs->regs[index]; |
ace2dc7d1 sh: wire up perf ... |
402 403 404 405 406 407 408 409 |
/* * Log the unexpected fixups, and then pass them on to perf. * * We intentionally don't report the expected cases to perf as * otherwise the trapped I/O case will skew the results too much * to be useful. */ if (!expected) { |
a99eae541 sh: Split out the... |
410 |
unaligned_fixups_notify(current, instruction, regs); |
a8b0ca17b perf: Remove the ... |
411 |
perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, |
ace2dc7d1 sh: wire up perf ... |
412 413 |
regs, address); } |
1da177e4c Linux-2.6.12-rc2 |
414 415 416 417 418 419 |
ret = -EFAULT; switch (instruction&0xF000) { case 0x0000: if (instruction==0x000B) { /* rts */ |
e7cc9a734 sh: trapped io su... |
420 |
ret = handle_delayslot(regs, instruction, ma); |
1da177e4c Linux-2.6.12-rc2 |
421 422 423 424 425 |
if (ret==0) regs->pc = regs->pr; } else if ((instruction&0x00FF)==0x0023) { /* braf @Rm */ |
e7cc9a734 sh: trapped io su... |
426 |
ret = handle_delayslot(regs, instruction, ma); |
1da177e4c Linux-2.6.12-rc2 |
427 428 429 430 431 |
if (ret==0) regs->pc += rm + 4; } else if ((instruction&0x00FF)==0x0003) { /* bsrf @Rm */ |
e7cc9a734 sh: trapped io su... |
432 |
ret = handle_delayslot(regs, instruction, ma); |
1da177e4c Linux-2.6.12-rc2 |
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 |
if (ret==0) { regs->pr = regs->pc + 4; regs->pc += rm + 4; } } else { /* mov.[bwl] to/from memory via r0+rn */ goto simple; } break; case 0x1000: /* mov.l Rm,@(disp,Rn) */ goto simple; case 0x2000: /* mov.[bwl] to memory, possibly with pre-decrement */ goto simple; case 0x4000: if ((instruction&0x00FF)==0x002B) { /* jmp @Rm */ |
e7cc9a734 sh: trapped io su... |
453 |
ret = handle_delayslot(regs, instruction, ma); |
1da177e4c Linux-2.6.12-rc2 |
454 455 456 457 458 |
if (ret==0) regs->pc = rm; } else if ((instruction&0x00FF)==0x000B) { /* jsr @Rm */ |
e7cc9a734 sh: trapped io su... |
459 |
ret = handle_delayslot(regs, instruction, ma); |
1da177e4c Linux-2.6.12-rc2 |
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 |
if (ret==0) { regs->pr = regs->pc + 4; regs->pc = rm; } } else { /* mov.[bwl] to/from memory via r0+rn */ goto simple; } break; case 0x5000: /* mov.l @(disp,Rm),Rn */ goto simple; case 0x6000: /* mov.[bwl] from memory, possibly with post-increment */ goto simple; case 0x8000: /* bf lab, bf/s lab, bt lab, bt/s lab */ switch (instruction&0x0F00) { case 0x0100: /* mov.w R0,@(disp,Rm) */ goto simple; case 0x0500: /* mov.w @(disp,Rm),R0 */ goto simple; case 0x0B00: /* bf lab - no delayslot*/ |
0710b91c5 sh: Fix unaligned... |
484 |
ret = 0; |
1da177e4c Linux-2.6.12-rc2 |
485 486 |
break; case 0x0F00: /* bf/s lab */ |
e7cc9a734 sh: trapped io su... |
487 |
ret = handle_delayslot(regs, instruction, ma); |
1da177e4c Linux-2.6.12-rc2 |
488 489 490 491 492 493 494 495 496 497 |
if (ret==0) { #if defined(CONFIG_CPU_SH4) || defined(CONFIG_SH7705_CACHE_32KB) if ((regs->sr & 0x00000001) != 0) regs->pc += 4; /* next after slot */ else #endif regs->pc += SH_PC_8BIT_OFFSET(instruction); } break; case 0x0900: /* bt lab - no delayslot */ |
0710b91c5 sh: Fix unaligned... |
498 |
ret = 0; |
1da177e4c Linux-2.6.12-rc2 |
499 500 |
break; case 0x0D00: /* bt/s lab */ |
e7cc9a734 sh: trapped io su... |
501 |
ret = handle_delayslot(regs, instruction, ma); |
1da177e4c Linux-2.6.12-rc2 |
502 503 504 505 506 507 508 509 510 511 512 |
if (ret==0) { #if defined(CONFIG_CPU_SH4) || defined(CONFIG_SH7705_CACHE_32KB) if ((regs->sr & 0x00000001) == 0) regs->pc += 4; /* next after slot */ else #endif regs->pc += SH_PC_8BIT_OFFSET(instruction); } break; } break; |
34f7145a6 sh: Add unaligned... |
513 514 |
case 0x9000: /* mov.w @(disp,Rm),Rn */ goto simple; |
1da177e4c Linux-2.6.12-rc2 |
515 |
case 0xA000: /* bra label */ |
e7cc9a734 sh: trapped io su... |
516 |
ret = handle_delayslot(regs, instruction, ma); |
1da177e4c Linux-2.6.12-rc2 |
517 518 519 520 521 |
if (ret==0) regs->pc += SH_PC_12BIT_OFFSET(instruction); break; case 0xB000: /* bsr label */ |
e7cc9a734 sh: trapped io su... |
522 |
ret = handle_delayslot(regs, instruction, ma); |
1da177e4c Linux-2.6.12-rc2 |
523 524 525 526 527 |
if (ret==0) { regs->pr = regs->pc + 4; regs->pc += SH_PC_12BIT_OFFSET(instruction); } break; |
34f7145a6 sh: Add unaligned... |
528 529 530 |
case 0xD000: /* mov.l @(disp,Rm),Rn */ goto simple; |
1da177e4c Linux-2.6.12-rc2 |
531 532 533 534 535 |
} return ret; /* handle non-delay-slot instruction */ simple: |
e7cc9a734 sh: trapped io su... |
536 |
ret = handle_unaligned_ins(instruction, regs, ma); |
1da177e4c Linux-2.6.12-rc2 |
537 |
if (ret==0) |
53f983a90 sh: Fix PC adjust... |
538 |
regs->pc += instruction_size(instruction); |
1da177e4c Linux-2.6.12-rc2 |
539 540 541 542 |
return ret; } /* |
b5a1bcbee sh: Set up correc... |
543 544 545 546 547 548 549 550 |
* Handle various address error exceptions: * - instruction address error: * misaligned PC * PC >= 0x80000000 in user mode * - data address error (read and write) * misaligned data access * access to >= 0x80000000 is user mode * Unfortuntaly we can't distinguish between instruction address error |
e868d6127 spelling fixes: a... |
551 |
* and data address errors caused by read accesses. |
1da177e4c Linux-2.6.12-rc2 |
552 |
*/ |
f0bc814cf sh: gcc4 support. |
553 |
asmlinkage void do_address_error(struct pt_regs *regs, |
1da177e4c Linux-2.6.12-rc2 |
554 555 556 |
unsigned long writeaccess, unsigned long address) { |
0983b3184 sh: Wire up divis... |
557 |
unsigned long error_code = 0; |
1da177e4c Linux-2.6.12-rc2 |
558 |
mm_segment_t oldfs; |
b5a1bcbee sh: Set up correc... |
559 |
siginfo_t info; |
2bcfffa42 sh: Rename opcode... |
560 |
insn_size_t instruction; |
1da177e4c Linux-2.6.12-rc2 |
561 |
int tmp; |
0983b3184 sh: Wire up divis... |
562 563 |
/* Intentional ifdef */ #ifdef CONFIG_CPU_HAS_SR_RB |
4c59e2942 sh: Move lookup_e... |
564 |
error_code = lookup_exception_vector(); |
0983b3184 sh: Wire up divis... |
565 |
#endif |
1da177e4c Linux-2.6.12-rc2 |
566 567 568 569 |
oldfs = get_fs(); if (user_mode(regs)) { |
b5a1bcbee sh: Set up correc... |
570 |
int si_code = BUS_ADRERR; |
a99eae541 sh: Split out the... |
571 |
unsigned int user_action; |
b5a1bcbee sh: Set up correc... |
572 |
|
1da177e4c Linux-2.6.12-rc2 |
573 |
local_irq_enable(); |
a99eae541 sh: Split out the... |
574 |
inc_unaligned_user_access(); |
7436cde6b sh: Allow user co... |
575 |
|
5a0ab35e4 sh: cleanup of do... |
576 |
set_fs(USER_DS); |
23c4c8217 sh: Handle unalig... |
577 578 |
if (copy_from_user(&instruction, (insn_size_t *)(regs->pc & ~1), sizeof(instruction))) { |
5a0ab35e4 sh: cleanup of do... |
579 580 581 582 |
set_fs(oldfs); goto uspace_segv; } set_fs(oldfs); |
7436cde6b sh: Allow user co... |
583 |
/* shout about userspace fixups */ |
a99eae541 sh: Split out the... |
584 |
unaligned_fixups_notify(current, instruction, regs); |
7436cde6b sh: Allow user co... |
585 |
|
a99eae541 sh: Split out the... |
586 587 |
user_action = unaligned_user_action(); if (user_action & UM_FIXUP) |
7436cde6b sh: Allow user co... |
588 |
goto fixup; |
a99eae541 sh: Split out the... |
589 |
if (user_action & UM_SIGNAL) |
7436cde6b sh: Allow user co... |
590 591 592 |
goto uspace_segv; else { /* ignore */ |
5a0ab35e4 sh: cleanup of do... |
593 |
regs->pc += instruction_size(instruction); |
7436cde6b sh: Allow user co... |
594 595 596 597 |
return; } fixup: |
1da177e4c Linux-2.6.12-rc2 |
598 |
/* bad PC is not something we can fix */ |
b5a1bcbee sh: Set up correc... |
599 600 |
if (regs->pc & 1) { si_code = BUS_ADRALN; |
1da177e4c Linux-2.6.12-rc2 |
601 |
goto uspace_segv; |
b5a1bcbee sh: Set up correc... |
602 |
} |
1da177e4c Linux-2.6.12-rc2 |
603 604 |
set_fs(USER_DS); |
e7cc9a734 sh: trapped io su... |
605 |
tmp = handle_unaligned_access(instruction, regs, |
ace2dc7d1 sh: wire up perf ... |
606 607 |
&user_mem_access, 0, address); |
1da177e4c Linux-2.6.12-rc2 |
608 |
set_fs(oldfs); |
a99eae541 sh: Split out the... |
609 |
if (tmp == 0) |
1da177e4c Linux-2.6.12-rc2 |
610 |
return; /* sorted */ |
b5a1bcbee sh: Set up correc... |
611 612 613 614 615 616 617 618 619 |
uspace_segv: printk(KERN_NOTICE "Sending SIGBUS to \"%s\" due to unaligned " "access (PC %lx PR %lx) ", current->comm, regs->pc, regs->pr); info.si_signo = SIGBUS; info.si_errno = 0; info.si_code = si_code; |
e08f457c7 sh: __user annota... |
620 |
info.si_addr = (void __user *)address; |
b5a1bcbee sh: Set up correc... |
621 |
force_sig_info(SIGBUS, &info, current); |
1da177e4c Linux-2.6.12-rc2 |
622 |
} else { |
a99eae541 sh: Split out the... |
623 |
inc_unaligned_kernel_access(); |
7436cde6b sh: Allow user co... |
624 |
|
1da177e4c Linux-2.6.12-rc2 |
625 626 627 628 |
if (regs->pc & 1) die("unaligned program counter", regs, error_code); set_fs(KERNEL_DS); |
fa43972fa sh: fixup many sp... |
629 |
if (copy_from_user(&instruction, (void __user *)(regs->pc), |
4b5a9ef52 sh: use opcode_t ... |
630 |
sizeof(instruction))) { |
1da177e4c Linux-2.6.12-rc2 |
631 632 633 634 635 636 |
/* Argh. Fault on the instruction itself. This should never happen non-SMP */ set_fs(oldfs); die("insn faulting in do_address_error", regs, 0); } |
a99eae541 sh: Split out the... |
637 |
unaligned_fixups_notify(current, instruction, regs); |
40258ee97 sh: Fix up uninit... |
638 |
|
ace2dc7d1 sh: wire up perf ... |
639 640 |
handle_unaligned_access(instruction, regs, &user_mem_access, 0, address); |
1da177e4c Linux-2.6.12-rc2 |
641 642 643 644 645 646 647 648 649 650 |
set_fs(oldfs); } } #ifdef CONFIG_SH_DSP /* * SH-DSP support gerg@snapgear.com. */ int is_dsp_inst(struct pt_regs *regs) { |
882c12c4e sh: Shut up SH2-D... |
651 |
unsigned short inst = 0; |
1da177e4c Linux-2.6.12-rc2 |
652 |
|
f0bc814cf sh: gcc4 support. |
653 |
/* |
1da177e4c Linux-2.6.12-rc2 |
654 655 656 |
* Safe guard if DSP mode is already enabled or we're lacking * the DSP altogether. */ |
11c196568 sh: Fixup cpu_dat... |
657 |
if (!(current_cpu_data.flags & CPU_HAS_DSP) || (regs->sr & SR_DSP)) |
1da177e4c Linux-2.6.12-rc2 |
658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 |
return 0; get_user(inst, ((unsigned short *) regs->pc)); inst &= 0xf000; /* Check for any type of DSP or support instruction */ if ((inst == 0xf000) || (inst == 0x4000)) return 1; return 0; } #else #define is_dsp_inst(regs) (0) #endif /* CONFIG_SH_DSP */ |
0983b3184 sh: Wire up divis... |
673 674 675 |
#ifdef CONFIG_CPU_SH2A asmlinkage void do_divide_error(unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7, |
f0bc814cf sh: gcc4 support. |
676 |
struct pt_regs __regs) |
0983b3184 sh: Wire up divis... |
677 678 |
{ siginfo_t info; |
0983b3184 sh: Wire up divis... |
679 680 681 682 683 684 685 686 687 688 689 690 |
switch (r4) { case TRAP_DIVZERO_ERROR: info.si_code = FPE_INTDIV; break; case TRAP_DIVOVF_ERROR: info.si_code = FPE_INTOVF; break; } force_sig_info(SIGFPE, &info, current); } #endif |
4b565680d sh: math-emu support |
691 692 |
asmlinkage void do_reserved_inst(unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7, |
f0bc814cf sh: gcc4 support. |
693 |
struct pt_regs __regs) |
4b565680d sh: math-emu support |
694 |
{ |
f0bc814cf sh: gcc4 support. |
695 |
struct pt_regs *regs = RELOC_HIDE(&__regs, 0); |
4b565680d sh: math-emu support |
696 697 698 699 |
unsigned long error_code; struct task_struct *tsk = current; #ifdef CONFIG_SH_FPU_EMU |
0983b3184 sh: Wire up divis... |
700 |
unsigned short inst = 0; |
4b565680d sh: math-emu support |
701 |
int err; |
f0bc814cf sh: gcc4 support. |
702 |
get_user(inst, (unsigned short*)regs->pc); |
4b565680d sh: math-emu support |
703 |
|
f0bc814cf sh: gcc4 support. |
704 |
err = do_fpu_inst(inst, regs); |
4b565680d sh: math-emu support |
705 |
if (!err) { |
53f983a90 sh: Fix PC adjust... |
706 |
regs->pc += instruction_size(inst); |
4b565680d sh: math-emu support |
707 708 709 710 711 712 713 |
return; } /* not a FPU inst. */ #endif #ifdef CONFIG_SH_DSP /* Check if it's a DSP instruction */ |
b5a1bcbee sh: Set up correc... |
714 |
if (is_dsp_inst(regs)) { |
4b565680d sh: math-emu support |
715 |
/* Enable DSP mode, and restart instruction. */ |
f0bc814cf sh: gcc4 support. |
716 |
regs->sr |= SR_DSP; |
01ab10393 sh: Fix up DSP co... |
717 718 |
/* Save DSP mode */ tsk->thread.dsp_status.status |= SR_DSP; |
4b565680d sh: math-emu support |
719 720 721 |
return; } #endif |
4c59e2942 sh: Move lookup_e... |
722 |
error_code = lookup_exception_vector(); |
0983b3184 sh: Wire up divis... |
723 |
|
4b565680d sh: math-emu support |
724 |
local_irq_enable(); |
4b565680d sh: math-emu support |
725 |
force_sig(SIGILL, tsk); |
f0bc814cf sh: gcc4 support. |
726 |
die_if_no_fixup("reserved instruction", regs, error_code); |
4b565680d sh: math-emu support |
727 728 729 |
} #ifdef CONFIG_SH_FPU_EMU |
edfd6da04 sh: Add a few mor... |
730 |
static int emulate_branch(unsigned short inst, struct pt_regs *regs) |
4b565680d sh: math-emu support |
731 732 733 734 735 736 737 738 739 740 741 742 |
{ /* * bfs: 8fxx: PC+=d*2+4; * bts: 8dxx: PC+=d*2+4; * bra: axxx: PC+=D*2+4; * bsr: bxxx: PC+=D*2+4 after PR=PC+4; * braf:0x23: PC+=Rn*2+4; * bsrf:0x03: PC+=Rn*2+4 after PR=PC+4; * jmp: 4x2b: PC=Rn; * jsr: 4x0b: PC=Rn after PR=PC+4; * rts: 000b: PC=PR; */ |
edfd6da04 sh: Add a few mor... |
743 744 745 746 747 748 |
if (((inst & 0xf000) == 0xb000) || /* bsr */ ((inst & 0xf0ff) == 0x0003) || /* bsrf */ ((inst & 0xf0ff) == 0x400b)) /* jsr */ regs->pr = regs->pc + 4; if ((inst & 0xfd00) == 0x8d00) { /* bfs, bts */ |
4b565680d sh: math-emu support |
749 750 751 |
regs->pc += SH_PC_8BIT_OFFSET(inst); return 0; } |
edfd6da04 sh: Add a few mor... |
752 |
if ((inst & 0xe000) == 0xa000) { /* bra, bsr */ |
4b565680d sh: math-emu support |
753 754 755 |
regs->pc += SH_PC_12BIT_OFFSET(inst); return 0; } |
edfd6da04 sh: Add a few mor... |
756 |
if ((inst & 0xf0df) == 0x0003) { /* braf, bsrf */ |
4b565680d sh: math-emu support |
757 758 759 |
regs->pc += regs->regs[(inst & 0x0f00) >> 8] + 4; return 0; } |
edfd6da04 sh: Add a few mor... |
760 |
if ((inst & 0xf0df) == 0x400b) { /* jmp, jsr */ |
4b565680d sh: math-emu support |
761 762 763 |
regs->pc = regs->regs[(inst & 0x0f00) >> 8]; return 0; } |
edfd6da04 sh: Add a few mor... |
764 |
if ((inst & 0xffff) == 0x000b) { /* rts */ |
4b565680d sh: math-emu support |
765 766 767 768 769 770 771 772 773 774 |
regs->pc = regs->pr; return 0; } return 1; } #endif asmlinkage void do_illegal_slot_inst(unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7, |
f0bc814cf sh: gcc4 support. |
775 |
struct pt_regs __regs) |
4b565680d sh: math-emu support |
776 |
{ |
f0bc814cf sh: gcc4 support. |
777 |
struct pt_regs *regs = RELOC_HIDE(&__regs, 0); |
b3d765f5d sh: Fix up fpu em... |
778 |
unsigned long inst; |
4b565680d sh: math-emu support |
779 |
struct task_struct *tsk = current; |
d39f54501 sh: Add kprobes s... |
780 781 782 |
if (kprobe_handle_illslot(regs->pc) == 0) return; |
4b565680d sh: math-emu support |
783 |
#ifdef CONFIG_SH_FPU_EMU |
f0bc814cf sh: gcc4 support. |
784 785 786 787 |
get_user(inst, (unsigned short *)regs->pc + 1); if (!do_fpu_inst(inst, regs)) { get_user(inst, (unsigned short *)regs->pc); if (!emulate_branch(inst, regs)) |
4b565680d sh: math-emu support |
788 789 790 791 792 |
return; /* fault in branch.*/ } /* not a FPU inst. */ #endif |
4c59e2942 sh: Move lookup_e... |
793 |
inst = lookup_exception_vector(); |
0983b3184 sh: Wire up divis... |
794 |
|
4b565680d sh: math-emu support |
795 |
local_irq_enable(); |
4b565680d sh: math-emu support |
796 |
force_sig(SIGILL, tsk); |
b3d765f5d sh: Fix up fpu em... |
797 |
die_if_no_fixup("illegal slot instruction", regs, inst); |
4b565680d sh: math-emu support |
798 |
} |
1da177e4c Linux-2.6.12-rc2 |
799 800 801 |
asmlinkage void do_exception_error(unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7, |
f0bc814cf sh: gcc4 support. |
802 |
struct pt_regs __regs) |
1da177e4c Linux-2.6.12-rc2 |
803 |
{ |
f0bc814cf sh: gcc4 support. |
804 |
struct pt_regs *regs = RELOC_HIDE(&__regs, 0); |
1da177e4c Linux-2.6.12-rc2 |
805 |
long ex; |
0983b3184 sh: Wire up divis... |
806 |
|
4c59e2942 sh: Move lookup_e... |
807 |
ex = lookup_exception_vector(); |
f0bc814cf sh: gcc4 support. |
808 |
die_if_kernel("exception", regs, ex); |
1da177e4c Linux-2.6.12-rc2 |
809 |
} |
aba1030a7 sh: Bring SMP sup... |
810 |
void __cpuinit per_cpu_trap_init(void) |
1da177e4c Linux-2.6.12-rc2 |
811 812 |
{ extern void *vbr_base; |
1da177e4c Linux-2.6.12-rc2 |
813 814 815 816 817 818 819 820 |
/* NOTE: The VBR value should be at P1 (or P2, virtural "fixed" address space). It's definitely should not in physical address. */ asm volatile("ldc %0, vbr" : /* no output */ : "r" (&vbr_base) : "memory"); |
68a1aed70 sh: boot kernel w... |
821 822 823 |
/* disable exception blocking now when the vbr has been setup */ clear_bl_bit(); |
1da177e4c Linux-2.6.12-rc2 |
824 |
} |
1f666587d sh: Fix exception... |
825 |
void *set_exception_table_vec(unsigned int vec, void *handler) |
1da177e4c Linux-2.6.12-rc2 |
826 827 |
{ extern void *exception_handling_table[]; |
1f666587d sh: Fix exception... |
828 |
void *old_handler; |
b5a1bcbee sh: Set up correc... |
829 |
|
1f666587d sh: Fix exception... |
830 831 832 833 |
old_handler = exception_handling_table[vec]; exception_handling_table[vec] = handler; return old_handler; } |
1da177e4c Linux-2.6.12-rc2 |
834 |
|
1f666587d sh: Fix exception... |
835 836 837 838 |
void __init trap_init(void) { set_exception_table_vec(TRAP_RESERVED_INST, do_reserved_inst); set_exception_table_vec(TRAP_ILLEGAL_SLOT_INST, do_illegal_slot_inst); |
1da177e4c Linux-2.6.12-rc2 |
839 |
|
4b565680d sh: math-emu support |
840 841 842 843 844 845 846 |
#if defined(CONFIG_CPU_SH4) && !defined(CONFIG_SH_FPU) || \ defined(CONFIG_SH_FPU_EMU) /* * For SH-4 lacking an FPU, treat floating point instructions as * reserved. They'll be handled in the math-emu case, or faulted on * otherwise. */ |
1f666587d sh: Fix exception... |
847 848 849 |
set_exception_table_evt(0x800, do_reserved_inst); set_exception_table_evt(0x820, do_illegal_slot_inst); #elif defined(CONFIG_SH_FPU) |
74d99a5e2 sh: SH-2A FPU sup... |
850 851 |
set_exception_table_evt(0x800, fpu_state_restore_trap_handler); set_exception_table_evt(0x820, fpu_state_restore_trap_handler); |
1da177e4c Linux-2.6.12-rc2 |
852 |
#endif |
0983b3184 sh: Wire up divis... |
853 854 |
#ifdef CONFIG_CPU_SH2 |
5a4f7c66b sh: Share bug/deb... |
855 |
set_exception_table_vec(TRAP_ADDRESS_ERROR, address_error_trap_handler); |
0983b3184 sh: Wire up divis... |
856 857 858 859 |
#endif #ifdef CONFIG_CPU_SH2A set_exception_table_vec(TRAP_DIVZERO_ERROR, do_divide_error); set_exception_table_vec(TRAP_DIVOVF_ERROR, do_divide_error); |
6e80f5e8c sh2(A) exception ... |
860 861 862 |
#ifdef CONFIG_SH_FPU set_exception_table_vec(TRAP_FPU_ERROR, fpu_error_trap_handler); #endif |
0983b3184 sh: Wire up divis... |
863 |
#endif |
b5a1bcbee sh: Set up correc... |
864 |
|
cd89436e5 sh: Add UBC trap ... |
865 |
#ifdef TRAP_UBC |
c4761815a sh: Fix up breakp... |
866 |
set_exception_table_vec(TRAP_UBC, breakpoint_trap_handler); |
cd89436e5 sh: Add UBC trap ... |
867 |
#endif |
1da177e4c Linux-2.6.12-rc2 |
868 |
} |
6b0022305 sh: Proper show_s... |
869 |
void show_stack(struct task_struct *tsk, unsigned long *sp) |
1da177e4c Linux-2.6.12-rc2 |
870 |
{ |
6b0022305 sh: Proper show_s... |
871 872 873 874 875 876 877 878 879 880 881 882 883 |
unsigned long stack; if (!tsk) tsk = current; if (tsk == current) sp = (unsigned long *)current_stack_pointer; else sp = (unsigned long *)tsk->thread.sp; stack = (unsigned long)sp; dump_mem("Stack: ", stack, THREAD_SIZE + (unsigned long)task_stack_page(tsk)); show_trace(tsk, sp, NULL); |
1da177e4c Linux-2.6.12-rc2 |
884 885 886 887 888 889 890 |
} void dump_stack(void) { show_stack(NULL, NULL); } EXPORT_SYMBOL(dump_stack); |