diff options
Diffstat (limited to 'arch/alpha/kernel/traps.c')
-rw-r--r-- | arch/alpha/kernel/traps.c | 1092 |
1 files changed, 1092 insertions, 0 deletions
diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c new file mode 100644 index 000000000000..fd7bd17cc960 --- /dev/null +++ b/arch/alpha/kernel/traps.c | |||
@@ -0,0 +1,1092 @@ | |||
1 | /* | ||
2 | * arch/alpha/kernel/traps.c | ||
3 | * | ||
4 | * (C) Copyright 1994 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * This file initializes the trap entry points | ||
9 | */ | ||
10 | |||
11 | #include <linux/config.h> | ||
12 | #include <linux/mm.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/tty.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <linux/smp_lock.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/kallsyms.h> | ||
20 | |||
21 | #include <asm/gentrap.h> | ||
22 | #include <asm/uaccess.h> | ||
23 | #include <asm/unaligned.h> | ||
24 | #include <asm/sysinfo.h> | ||
25 | #include <asm/hwrpb.h> | ||
26 | #include <asm/mmu_context.h> | ||
27 | |||
28 | #include "proto.h" | ||
29 | |||
30 | /* Work-around for some SRMs which mishandle opDEC faults. */ | ||
31 | |||
32 | static int opDEC_fix; | ||
33 | |||
34 | static void __init | ||
35 | opDEC_check(void) | ||
36 | { | ||
37 | __asm__ __volatile__ ( | ||
38 | /* Load the address of... */ | ||
39 | " br $16, 1f\n" | ||
40 | /* A stub instruction fault handler. Just add 4 to the | ||
41 | pc and continue. */ | ||
42 | " ldq $16, 8($sp)\n" | ||
43 | " addq $16, 4, $16\n" | ||
44 | " stq $16, 8($sp)\n" | ||
45 | " call_pal %[rti]\n" | ||
46 | /* Install the instruction fault handler. */ | ||
47 | "1: lda $17, 3\n" | ||
48 | " call_pal %[wrent]\n" | ||
49 | /* With that in place, the fault from the round-to-minf fp | ||
50 | insn will arrive either at the "lda 4" insn (bad) or one | ||
51 | past that (good). This places the correct fixup in %0. */ | ||
52 | " lda %[fix], 0\n" | ||
53 | " cvttq/svm $f31,$f31\n" | ||
54 | " lda %[fix], 4" | ||
55 | : [fix] "=r" (opDEC_fix) | ||
56 | : [rti] "n" (PAL_rti), [wrent] "n" (PAL_wrent) | ||
57 | : "$0", "$1", "$16", "$17", "$22", "$23", "$24", "$25"); | ||
58 | |||
59 | if (opDEC_fix) | ||
60 | printk("opDEC fixup enabled.\n"); | ||
61 | } | ||
62 | |||
63 | void | ||
64 | dik_show_regs(struct pt_regs *regs, unsigned long *r9_15) | ||
65 | { | ||
66 | printk("pc = [<%016lx>] ra = [<%016lx>] ps = %04lx %s\n", | ||
67 | regs->pc, regs->r26, regs->ps, print_tainted()); | ||
68 | print_symbol("pc is at %s\n", regs->pc); | ||
69 | print_symbol("ra is at %s\n", regs->r26 ); | ||
70 | printk("v0 = %016lx t0 = %016lx t1 = %016lx\n", | ||
71 | regs->r0, regs->r1, regs->r2); | ||
72 | printk("t2 = %016lx t3 = %016lx t4 = %016lx\n", | ||
73 | regs->r3, regs->r4, regs->r5); | ||
74 | printk("t5 = %016lx t6 = %016lx t7 = %016lx\n", | ||
75 | regs->r6, regs->r7, regs->r8); | ||
76 | |||
77 | if (r9_15) { | ||
78 | printk("s0 = %016lx s1 = %016lx s2 = %016lx\n", | ||
79 | r9_15[9], r9_15[10], r9_15[11]); | ||
80 | printk("s3 = %016lx s4 = %016lx s5 = %016lx\n", | ||
81 | r9_15[12], r9_15[13], r9_15[14]); | ||
82 | printk("s6 = %016lx\n", r9_15[15]); | ||
83 | } | ||
84 | |||
85 | printk("a0 = %016lx a1 = %016lx a2 = %016lx\n", | ||
86 | regs->r16, regs->r17, regs->r18); | ||
87 | printk("a3 = %016lx a4 = %016lx a5 = %016lx\n", | ||
88 | regs->r19, regs->r20, regs->r21); | ||
89 | printk("t8 = %016lx t9 = %016lx t10= %016lx\n", | ||
90 | regs->r22, regs->r23, regs->r24); | ||
91 | printk("t11= %016lx pv = %016lx at = %016lx\n", | ||
92 | regs->r25, regs->r27, regs->r28); | ||
93 | printk("gp = %016lx sp = %p\n", regs->gp, regs+1); | ||
94 | #if 0 | ||
95 | __halt(); | ||
96 | #endif | ||
97 | } | ||
98 | |||
99 | #if 0 | ||
100 | static char * ireg_name[] = {"v0", "t0", "t1", "t2", "t3", "t4", "t5", "t6", | ||
101 | "t7", "s0", "s1", "s2", "s3", "s4", "s5", "s6", | ||
102 | "a0", "a1", "a2", "a3", "a4", "a5", "t8", "t9", | ||
103 | "t10", "t11", "ra", "pv", "at", "gp", "sp", "zero"}; | ||
104 | #endif | ||
105 | |||
106 | static void | ||
107 | dik_show_code(unsigned int *pc) | ||
108 | { | ||
109 | long i; | ||
110 | |||
111 | printk("Code:"); | ||
112 | for (i = -6; i < 2; i++) { | ||
113 | unsigned int insn; | ||
114 | if (__get_user(insn, (unsigned int __user *)pc + i)) | ||
115 | break; | ||
116 | printk("%c%08x%c", i ? ' ' : '<', insn, i ? ' ' : '>'); | ||
117 | } | ||
118 | printk("\n"); | ||
119 | } | ||
120 | |||
121 | static void | ||
122 | dik_show_trace(unsigned long *sp) | ||
123 | { | ||
124 | long i = 0; | ||
125 | printk("Trace:\n"); | ||
126 | while (0x1ff8 & (unsigned long) sp) { | ||
127 | extern char _stext[], _etext[]; | ||
128 | unsigned long tmp = *sp; | ||
129 | sp++; | ||
130 | if (tmp < (unsigned long) &_stext) | ||
131 | continue; | ||
132 | if (tmp >= (unsigned long) &_etext) | ||
133 | continue; | ||
134 | printk("[<%lx>]", tmp); | ||
135 | print_symbol(" %s", tmp); | ||
136 | printk("\n"); | ||
137 | if (i > 40) { | ||
138 | printk(" ..."); | ||
139 | break; | ||
140 | } | ||
141 | } | ||
142 | printk("\n"); | ||
143 | } | ||
144 | |||
145 | static int kstack_depth_to_print = 24; | ||
146 | |||
147 | void show_stack(struct task_struct *task, unsigned long *sp) | ||
148 | { | ||
149 | unsigned long *stack; | ||
150 | int i; | ||
151 | |||
152 | /* | ||
153 | * debugging aid: "show_stack(NULL);" prints the | ||
154 | * back trace for this cpu. | ||
155 | */ | ||
156 | if(sp==NULL) | ||
157 | sp=(unsigned long*)&sp; | ||
158 | |||
159 | stack = sp; | ||
160 | for(i=0; i < kstack_depth_to_print; i++) { | ||
161 | if (((long) stack & (THREAD_SIZE-1)) == 0) | ||
162 | break; | ||
163 | if (i && ((i % 4) == 0)) | ||
164 | printk("\n "); | ||
165 | printk("%016lx ", *stack++); | ||
166 | } | ||
167 | printk("\n"); | ||
168 | dik_show_trace(sp); | ||
169 | } | ||
170 | |||
171 | void dump_stack(void) | ||
172 | { | ||
173 | show_stack(NULL, NULL); | ||
174 | } | ||
175 | |||
176 | EXPORT_SYMBOL(dump_stack); | ||
177 | |||
178 | void | ||
179 | die_if_kernel(char * str, struct pt_regs *regs, long err, unsigned long *r9_15) | ||
180 | { | ||
181 | if (regs->ps & 8) | ||
182 | return; | ||
183 | #ifdef CONFIG_SMP | ||
184 | printk("CPU %d ", hard_smp_processor_id()); | ||
185 | #endif | ||
186 | printk("%s(%d): %s %ld\n", current->comm, current->pid, str, err); | ||
187 | dik_show_regs(regs, r9_15); | ||
188 | dik_show_trace((unsigned long *)(regs+1)); | ||
189 | dik_show_code((unsigned int *)regs->pc); | ||
190 | |||
191 | if (test_and_set_thread_flag (TIF_DIE_IF_KERNEL)) { | ||
192 | printk("die_if_kernel recursion detected.\n"); | ||
193 | local_irq_enable(); | ||
194 | while (1); | ||
195 | } | ||
196 | do_exit(SIGSEGV); | ||
197 | } | ||
198 | |||
199 | #ifndef CONFIG_MATHEMU | ||
200 | static long dummy_emul(void) { return 0; } | ||
201 | long (*alpha_fp_emul_imprecise)(struct pt_regs *regs, unsigned long writemask) | ||
202 | = (void *)dummy_emul; | ||
203 | long (*alpha_fp_emul) (unsigned long pc) | ||
204 | = (void *)dummy_emul; | ||
205 | #else | ||
206 | long alpha_fp_emul_imprecise(struct pt_regs *regs, unsigned long writemask); | ||
207 | long alpha_fp_emul (unsigned long pc); | ||
208 | #endif | ||
209 | |||
210 | asmlinkage void | ||
211 | do_entArith(unsigned long summary, unsigned long write_mask, | ||
212 | struct pt_regs *regs) | ||
213 | { | ||
214 | long si_code = FPE_FLTINV; | ||
215 | siginfo_t info; | ||
216 | |||
217 | if (summary & 1) { | ||
218 | /* Software-completion summary bit is set, so try to | ||
219 | emulate the instruction. If the processor supports | ||
220 | precise exceptions, we don't have to search. */ | ||
221 | if (!amask(AMASK_PRECISE_TRAP)) | ||
222 | si_code = alpha_fp_emul(regs->pc - 4); | ||
223 | else | ||
224 | si_code = alpha_fp_emul_imprecise(regs, write_mask); | ||
225 | if (si_code == 0) | ||
226 | return; | ||
227 | } | ||
228 | die_if_kernel("Arithmetic fault", regs, 0, NULL); | ||
229 | |||
230 | info.si_signo = SIGFPE; | ||
231 | info.si_errno = 0; | ||
232 | info.si_code = si_code; | ||
233 | info.si_addr = (void __user *) regs->pc; | ||
234 | send_sig_info(SIGFPE, &info, current); | ||
235 | } | ||
236 | |||
237 | asmlinkage void | ||
238 | do_entIF(unsigned long type, struct pt_regs *regs) | ||
239 | { | ||
240 | siginfo_t info; | ||
241 | int signo, code; | ||
242 | |||
243 | if (regs->ps == 0) { | ||
244 | if (type == 1) { | ||
245 | const unsigned int *data | ||
246 | = (const unsigned int *) regs->pc; | ||
247 | printk("Kernel bug at %s:%d\n", | ||
248 | (const char *)(data[1] | (long)data[2] << 32), | ||
249 | data[0]); | ||
250 | } | ||
251 | die_if_kernel((type == 1 ? "Kernel Bug" : "Instruction fault"), | ||
252 | regs, type, NULL); | ||
253 | } | ||
254 | |||
255 | switch (type) { | ||
256 | case 0: /* breakpoint */ | ||
257 | info.si_signo = SIGTRAP; | ||
258 | info.si_errno = 0; | ||
259 | info.si_code = TRAP_BRKPT; | ||
260 | info.si_trapno = 0; | ||
261 | info.si_addr = (void __user *) regs->pc; | ||
262 | |||
263 | if (ptrace_cancel_bpt(current)) { | ||
264 | regs->pc -= 4; /* make pc point to former bpt */ | ||
265 | } | ||
266 | |||
267 | send_sig_info(SIGTRAP, &info, current); | ||
268 | return; | ||
269 | |||
270 | case 1: /* bugcheck */ | ||
271 | info.si_signo = SIGTRAP; | ||
272 | info.si_errno = 0; | ||
273 | info.si_code = __SI_FAULT; | ||
274 | info.si_addr = (void __user *) regs->pc; | ||
275 | info.si_trapno = 0; | ||
276 | send_sig_info(SIGTRAP, &info, current); | ||
277 | return; | ||
278 | |||
279 | case 2: /* gentrap */ | ||
280 | info.si_addr = (void __user *) regs->pc; | ||
281 | info.si_trapno = regs->r16; | ||
282 | switch ((long) regs->r16) { | ||
283 | case GEN_INTOVF: | ||
284 | signo = SIGFPE; | ||
285 | code = FPE_INTOVF; | ||
286 | break; | ||
287 | case GEN_INTDIV: | ||
288 | signo = SIGFPE; | ||
289 | code = FPE_INTDIV; | ||
290 | break; | ||
291 | case GEN_FLTOVF: | ||
292 | signo = SIGFPE; | ||
293 | code = FPE_FLTOVF; | ||
294 | break; | ||
295 | case GEN_FLTDIV: | ||
296 | signo = SIGFPE; | ||
297 | code = FPE_FLTDIV; | ||
298 | break; | ||
299 | case GEN_FLTUND: | ||
300 | signo = SIGFPE; | ||
301 | code = FPE_FLTUND; | ||
302 | break; | ||
303 | case GEN_FLTINV: | ||
304 | signo = SIGFPE; | ||
305 | code = FPE_FLTINV; | ||
306 | break; | ||
307 | case GEN_FLTINE: | ||
308 | signo = SIGFPE; | ||
309 | code = FPE_FLTRES; | ||
310 | break; | ||
311 | case GEN_ROPRAND: | ||
312 | signo = SIGFPE; | ||
313 | code = __SI_FAULT; | ||
314 | break; | ||
315 | |||
316 | case GEN_DECOVF: | ||
317 | case GEN_DECDIV: | ||
318 | case GEN_DECINV: | ||
319 | case GEN_ASSERTERR: | ||
320 | case GEN_NULPTRERR: | ||
321 | case GEN_STKOVF: | ||
322 | case GEN_STRLENERR: | ||
323 | case GEN_SUBSTRERR: | ||
324 | case GEN_RANGERR: | ||
325 | case GEN_SUBRNG: | ||
326 | case GEN_SUBRNG1: | ||
327 | case GEN_SUBRNG2: | ||
328 | case GEN_SUBRNG3: | ||
329 | case GEN_SUBRNG4: | ||
330 | case GEN_SUBRNG5: | ||
331 | case GEN_SUBRNG6: | ||
332 | case GEN_SUBRNG7: | ||
333 | default: | ||
334 | signo = SIGTRAP; | ||
335 | code = __SI_FAULT; | ||
336 | break; | ||
337 | } | ||
338 | |||
339 | info.si_signo = signo; | ||
340 | info.si_errno = 0; | ||
341 | info.si_code = code; | ||
342 | info.si_addr = (void __user *) regs->pc; | ||
343 | send_sig_info(signo, &info, current); | ||
344 | return; | ||
345 | |||
346 | case 4: /* opDEC */ | ||
347 | if (implver() == IMPLVER_EV4) { | ||
348 | long si_code; | ||
349 | |||
350 | /* The some versions of SRM do not handle | ||
351 | the opDEC properly - they return the PC of the | ||
352 | opDEC fault, not the instruction after as the | ||
353 | Alpha architecture requires. Here we fix it up. | ||
354 | We do this by intentionally causing an opDEC | ||
355 | fault during the boot sequence and testing if | ||
356 | we get the correct PC. If not, we set a flag | ||
357 | to correct it every time through. */ | ||
358 | regs->pc += opDEC_fix; | ||
359 | |||
360 | /* EV4 does not implement anything except normal | ||
361 | rounding. Everything else will come here as | ||
362 | an illegal instruction. Emulate them. */ | ||
363 | si_code = alpha_fp_emul(regs->pc - 4); | ||
364 | if (si_code == 0) | ||
365 | return; | ||
366 | if (si_code > 0) { | ||
367 | info.si_signo = SIGFPE; | ||
368 | info.si_errno = 0; | ||
369 | info.si_code = si_code; | ||
370 | info.si_addr = (void __user *) regs->pc; | ||
371 | send_sig_info(SIGFPE, &info, current); | ||
372 | return; | ||
373 | } | ||
374 | } | ||
375 | break; | ||
376 | |||
377 | case 3: /* FEN fault */ | ||
378 | /* Irritating users can call PAL_clrfen to disable the | ||
379 | FPU for the process. The kernel will then trap in | ||
380 | do_switch_stack and undo_switch_stack when we try | ||
381 | to save and restore the FP registers. | ||
382 | |||
383 | Given that GCC by default generates code that uses the | ||
384 | FP registers, PAL_clrfen is not useful except for DoS | ||
385 | attacks. So turn the bleeding FPU back on and be done | ||
386 | with it. */ | ||
387 | current_thread_info()->pcb.flags |= 1; | ||
388 | __reload_thread(¤t_thread_info()->pcb); | ||
389 | return; | ||
390 | |||
391 | case 5: /* illoc */ | ||
392 | default: /* unexpected instruction-fault type */ | ||
393 | ; | ||
394 | } | ||
395 | |||
396 | info.si_signo = SIGILL; | ||
397 | info.si_errno = 0; | ||
398 | info.si_code = ILL_ILLOPC; | ||
399 | info.si_addr = (void __user *) regs->pc; | ||
400 | send_sig_info(SIGILL, &info, current); | ||
401 | } | ||
402 | |||
403 | /* There is an ifdef in the PALcode in MILO that enables a | ||
404 | "kernel debugging entry point" as an unprivileged call_pal. | ||
405 | |||
406 | We don't want to have anything to do with it, but unfortunately | ||
407 | several versions of MILO included in distributions have it enabled, | ||
408 | and if we don't put something on the entry point we'll oops. */ | ||
409 | |||
410 | asmlinkage void | ||
411 | do_entDbg(struct pt_regs *regs) | ||
412 | { | ||
413 | siginfo_t info; | ||
414 | |||
415 | die_if_kernel("Instruction fault", regs, 0, NULL); | ||
416 | |||
417 | info.si_signo = SIGILL; | ||
418 | info.si_errno = 0; | ||
419 | info.si_code = ILL_ILLOPC; | ||
420 | info.si_addr = (void __user *) regs->pc; | ||
421 | force_sig_info(SIGILL, &info, current); | ||
422 | } | ||
423 | |||
424 | |||
425 | /* | ||
426 | * entUna has a different register layout to be reasonably simple. It | ||
427 | * needs access to all the integer registers (the kernel doesn't use | ||
428 | * fp-regs), and it needs to have them in order for simpler access. | ||
429 | * | ||
430 | * Due to the non-standard register layout (and because we don't want | ||
431 | * to handle floating-point regs), user-mode unaligned accesses are | ||
432 | * handled separately by do_entUnaUser below. | ||
433 | * | ||
434 | * Oh, btw, we don't handle the "gp" register correctly, but if we fault | ||
435 | * on a gp-register unaligned load/store, something is _very_ wrong | ||
436 | * in the kernel anyway.. | ||
437 | */ | ||
438 | struct allregs { | ||
439 | unsigned long regs[32]; | ||
440 | unsigned long ps, pc, gp, a0, a1, a2; | ||
441 | }; | ||
442 | |||
443 | struct unaligned_stat { | ||
444 | unsigned long count, va, pc; | ||
445 | } unaligned[2]; | ||
446 | |||
447 | |||
448 | /* Macro for exception fixup code to access integer registers. */ | ||
449 | #define una_reg(r) (regs.regs[(r) >= 16 && (r) <= 18 ? (r)+19 : (r)]) | ||
450 | |||
451 | |||
452 | asmlinkage void | ||
453 | do_entUna(void * va, unsigned long opcode, unsigned long reg, | ||
454 | unsigned long a3, unsigned long a4, unsigned long a5, | ||
455 | struct allregs regs) | ||
456 | { | ||
457 | long error, tmp1, tmp2, tmp3, tmp4; | ||
458 | unsigned long pc = regs.pc - 4; | ||
459 | const struct exception_table_entry *fixup; | ||
460 | |||
461 | unaligned[0].count++; | ||
462 | unaligned[0].va = (unsigned long) va; | ||
463 | unaligned[0].pc = pc; | ||
464 | |||
465 | /* We don't want to use the generic get/put unaligned macros as | ||
466 | we want to trap exceptions. Only if we actually get an | ||
467 | exception will we decide whether we should have caught it. */ | ||
468 | |||
469 | switch (opcode) { | ||
470 | case 0x0c: /* ldwu */ | ||
471 | __asm__ __volatile__( | ||
472 | "1: ldq_u %1,0(%3)\n" | ||
473 | "2: ldq_u %2,1(%3)\n" | ||
474 | " extwl %1,%3,%1\n" | ||
475 | " extwh %2,%3,%2\n" | ||
476 | "3:\n" | ||
477 | ".section __ex_table,\"a\"\n" | ||
478 | " .long 1b - .\n" | ||
479 | " lda %1,3b-1b(%0)\n" | ||
480 | " .long 2b - .\n" | ||
481 | " lda %2,3b-2b(%0)\n" | ||
482 | ".previous" | ||
483 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2) | ||
484 | : "r"(va), "0"(0)); | ||
485 | if (error) | ||
486 | goto got_exception; | ||
487 | una_reg(reg) = tmp1|tmp2; | ||
488 | return; | ||
489 | |||
490 | case 0x28: /* ldl */ | ||
491 | __asm__ __volatile__( | ||
492 | "1: ldq_u %1,0(%3)\n" | ||
493 | "2: ldq_u %2,3(%3)\n" | ||
494 | " extll %1,%3,%1\n" | ||
495 | " extlh %2,%3,%2\n" | ||
496 | "3:\n" | ||
497 | ".section __ex_table,\"a\"\n" | ||
498 | " .long 1b - .\n" | ||
499 | " lda %1,3b-1b(%0)\n" | ||
500 | " .long 2b - .\n" | ||
501 | " lda %2,3b-2b(%0)\n" | ||
502 | ".previous" | ||
503 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2) | ||
504 | : "r"(va), "0"(0)); | ||
505 | if (error) | ||
506 | goto got_exception; | ||
507 | una_reg(reg) = (int)(tmp1|tmp2); | ||
508 | return; | ||
509 | |||
510 | case 0x29: /* ldq */ | ||
511 | __asm__ __volatile__( | ||
512 | "1: ldq_u %1,0(%3)\n" | ||
513 | "2: ldq_u %2,7(%3)\n" | ||
514 | " extql %1,%3,%1\n" | ||
515 | " extqh %2,%3,%2\n" | ||
516 | "3:\n" | ||
517 | ".section __ex_table,\"a\"\n" | ||
518 | " .long 1b - .\n" | ||
519 | " lda %1,3b-1b(%0)\n" | ||
520 | " .long 2b - .\n" | ||
521 | " lda %2,3b-2b(%0)\n" | ||
522 | ".previous" | ||
523 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2) | ||
524 | : "r"(va), "0"(0)); | ||
525 | if (error) | ||
526 | goto got_exception; | ||
527 | una_reg(reg) = tmp1|tmp2; | ||
528 | return; | ||
529 | |||
530 | /* Note that the store sequences do not indicate that they change | ||
531 | memory because it _should_ be affecting nothing in this context. | ||
532 | (Otherwise we have other, much larger, problems.) */ | ||
533 | case 0x0d: /* stw */ | ||
534 | __asm__ __volatile__( | ||
535 | "1: ldq_u %2,1(%5)\n" | ||
536 | "2: ldq_u %1,0(%5)\n" | ||
537 | " inswh %6,%5,%4\n" | ||
538 | " inswl %6,%5,%3\n" | ||
539 | " mskwh %2,%5,%2\n" | ||
540 | " mskwl %1,%5,%1\n" | ||
541 | " or %2,%4,%2\n" | ||
542 | " or %1,%3,%1\n" | ||
543 | "3: stq_u %2,1(%5)\n" | ||
544 | "4: stq_u %1,0(%5)\n" | ||
545 | "5:\n" | ||
546 | ".section __ex_table,\"a\"\n" | ||
547 | " .long 1b - .\n" | ||
548 | " lda %2,5b-1b(%0)\n" | ||
549 | " .long 2b - .\n" | ||
550 | " lda %1,5b-2b(%0)\n" | ||
551 | " .long 3b - .\n" | ||
552 | " lda $31,5b-3b(%0)\n" | ||
553 | " .long 4b - .\n" | ||
554 | " lda $31,5b-4b(%0)\n" | ||
555 | ".previous" | ||
556 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2), | ||
557 | "=&r"(tmp3), "=&r"(tmp4) | ||
558 | : "r"(va), "r"(una_reg(reg)), "0"(0)); | ||
559 | if (error) | ||
560 | goto got_exception; | ||
561 | return; | ||
562 | |||
563 | case 0x2c: /* stl */ | ||
564 | __asm__ __volatile__( | ||
565 | "1: ldq_u %2,3(%5)\n" | ||
566 | "2: ldq_u %1,0(%5)\n" | ||
567 | " inslh %6,%5,%4\n" | ||
568 | " insll %6,%5,%3\n" | ||
569 | " msklh %2,%5,%2\n" | ||
570 | " mskll %1,%5,%1\n" | ||
571 | " or %2,%4,%2\n" | ||
572 | " or %1,%3,%1\n" | ||
573 | "3: stq_u %2,3(%5)\n" | ||
574 | "4: stq_u %1,0(%5)\n" | ||
575 | "5:\n" | ||
576 | ".section __ex_table,\"a\"\n" | ||
577 | " .long 1b - .\n" | ||
578 | " lda %2,5b-1b(%0)\n" | ||
579 | " .long 2b - .\n" | ||
580 | " lda %1,5b-2b(%0)\n" | ||
581 | " .long 3b - .\n" | ||
582 | " lda $31,5b-3b(%0)\n" | ||
583 | " .long 4b - .\n" | ||
584 | " lda $31,5b-4b(%0)\n" | ||
585 | ".previous" | ||
586 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2), | ||
587 | "=&r"(tmp3), "=&r"(tmp4) | ||
588 | : "r"(va), "r"(una_reg(reg)), "0"(0)); | ||
589 | if (error) | ||
590 | goto got_exception; | ||
591 | return; | ||
592 | |||
593 | case 0x2d: /* stq */ | ||
594 | __asm__ __volatile__( | ||
595 | "1: ldq_u %2,7(%5)\n" | ||
596 | "2: ldq_u %1,0(%5)\n" | ||
597 | " insqh %6,%5,%4\n" | ||
598 | " insql %6,%5,%3\n" | ||
599 | " mskqh %2,%5,%2\n" | ||
600 | " mskql %1,%5,%1\n" | ||
601 | " or %2,%4,%2\n" | ||
602 | " or %1,%3,%1\n" | ||
603 | "3: stq_u %2,7(%5)\n" | ||
604 | "4: stq_u %1,0(%5)\n" | ||
605 | "5:\n" | ||
606 | ".section __ex_table,\"a\"\n\t" | ||
607 | " .long 1b - .\n" | ||
608 | " lda %2,5b-1b(%0)\n" | ||
609 | " .long 2b - .\n" | ||
610 | " lda %1,5b-2b(%0)\n" | ||
611 | " .long 3b - .\n" | ||
612 | " lda $31,5b-3b(%0)\n" | ||
613 | " .long 4b - .\n" | ||
614 | " lda $31,5b-4b(%0)\n" | ||
615 | ".previous" | ||
616 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2), | ||
617 | "=&r"(tmp3), "=&r"(tmp4) | ||
618 | : "r"(va), "r"(una_reg(reg)), "0"(0)); | ||
619 | if (error) | ||
620 | goto got_exception; | ||
621 | return; | ||
622 | } | ||
623 | |||
624 | lock_kernel(); | ||
625 | printk("Bad unaligned kernel access at %016lx: %p %lx %ld\n", | ||
626 | pc, va, opcode, reg); | ||
627 | do_exit(SIGSEGV); | ||
628 | |||
629 | got_exception: | ||
630 | /* Ok, we caught the exception, but we don't want it. Is there | ||
631 | someone to pass it along to? */ | ||
632 | if ((fixup = search_exception_tables(pc)) != 0) { | ||
633 | unsigned long newpc; | ||
634 | newpc = fixup_exception(una_reg, fixup, pc); | ||
635 | |||
636 | printk("Forwarding unaligned exception at %lx (%lx)\n", | ||
637 | pc, newpc); | ||
638 | |||
639 | (®s)->pc = newpc; | ||
640 | return; | ||
641 | } | ||
642 | |||
643 | /* | ||
644 | * Yikes! No one to forward the exception to. | ||
645 | * Since the registers are in a weird format, dump them ourselves. | ||
646 | */ | ||
647 | lock_kernel(); | ||
648 | |||
649 | printk("%s(%d): unhandled unaligned exception\n", | ||
650 | current->comm, current->pid); | ||
651 | |||
652 | printk("pc = [<%016lx>] ra = [<%016lx>] ps = %04lx\n", | ||
653 | pc, una_reg(26), regs.ps); | ||
654 | printk("r0 = %016lx r1 = %016lx r2 = %016lx\n", | ||
655 | una_reg(0), una_reg(1), una_reg(2)); | ||
656 | printk("r3 = %016lx r4 = %016lx r5 = %016lx\n", | ||
657 | una_reg(3), una_reg(4), una_reg(5)); | ||
658 | printk("r6 = %016lx r7 = %016lx r8 = %016lx\n", | ||
659 | una_reg(6), una_reg(7), una_reg(8)); | ||
660 | printk("r9 = %016lx r10= %016lx r11= %016lx\n", | ||
661 | una_reg(9), una_reg(10), una_reg(11)); | ||
662 | printk("r12= %016lx r13= %016lx r14= %016lx\n", | ||
663 | una_reg(12), una_reg(13), una_reg(14)); | ||
664 | printk("r15= %016lx\n", una_reg(15)); | ||
665 | printk("r16= %016lx r17= %016lx r18= %016lx\n", | ||
666 | una_reg(16), una_reg(17), una_reg(18)); | ||
667 | printk("r19= %016lx r20= %016lx r21= %016lx\n", | ||
668 | una_reg(19), una_reg(20), una_reg(21)); | ||
669 | printk("r22= %016lx r23= %016lx r24= %016lx\n", | ||
670 | una_reg(22), una_reg(23), una_reg(24)); | ||
671 | printk("r25= %016lx r27= %016lx r28= %016lx\n", | ||
672 | una_reg(25), una_reg(27), una_reg(28)); | ||
673 | printk("gp = %016lx sp = %p\n", regs.gp, ®s+1); | ||
674 | |||
675 | dik_show_code((unsigned int *)pc); | ||
676 | dik_show_trace((unsigned long *)(®s+1)); | ||
677 | |||
678 | if (test_and_set_thread_flag (TIF_DIE_IF_KERNEL)) { | ||
679 | printk("die_if_kernel recursion detected.\n"); | ||
680 | local_irq_enable(); | ||
681 | while (1); | ||
682 | } | ||
683 | do_exit(SIGSEGV); | ||
684 | } | ||
685 | |||
686 | /* | ||
687 | * Convert an s-floating point value in memory format to the | ||
688 | * corresponding value in register format. The exponent | ||
689 | * needs to be remapped to preserve non-finite values | ||
690 | * (infinities, not-a-numbers, denormals). | ||
691 | */ | ||
692 | static inline unsigned long | ||
693 | s_mem_to_reg (unsigned long s_mem) | ||
694 | { | ||
695 | unsigned long frac = (s_mem >> 0) & 0x7fffff; | ||
696 | unsigned long sign = (s_mem >> 31) & 0x1; | ||
697 | unsigned long exp_msb = (s_mem >> 30) & 0x1; | ||
698 | unsigned long exp_low = (s_mem >> 23) & 0x7f; | ||
699 | unsigned long exp; | ||
700 | |||
701 | exp = (exp_msb << 10) | exp_low; /* common case */ | ||
702 | if (exp_msb) { | ||
703 | if (exp_low == 0x7f) { | ||
704 | exp = 0x7ff; | ||
705 | } | ||
706 | } else { | ||
707 | if (exp_low == 0x00) { | ||
708 | exp = 0x000; | ||
709 | } else { | ||
710 | exp |= (0x7 << 7); | ||
711 | } | ||
712 | } | ||
713 | return (sign << 63) | (exp << 52) | (frac << 29); | ||
714 | } | ||
715 | |||
716 | /* | ||
717 | * Convert an s-floating point value in register format to the | ||
718 | * corresponding value in memory format. | ||
719 | */ | ||
720 | static inline unsigned long | ||
721 | s_reg_to_mem (unsigned long s_reg) | ||
722 | { | ||
723 | return ((s_reg >> 62) << 30) | ((s_reg << 5) >> 34); | ||
724 | } | ||
725 | |||
726 | /* | ||
727 | * Handle user-level unaligned fault. Handling user-level unaligned | ||
728 | * faults is *extremely* slow and produces nasty messages. A user | ||
729 | * program *should* fix unaligned faults ASAP. | ||
730 | * | ||
731 | * Notice that we have (almost) the regular kernel stack layout here, | ||
732 | * so finding the appropriate registers is a little more difficult | ||
733 | * than in the kernel case. | ||
734 | * | ||
735 | * Finally, we handle regular integer load/stores only. In | ||
736 | * particular, load-linked/store-conditionally and floating point | ||
737 | * load/stores are not supported. The former make no sense with | ||
738 | * unaligned faults (they are guaranteed to fail) and I don't think | ||
739 | * the latter will occur in any decent program. | ||
740 | * | ||
741 | * Sigh. We *do* have to handle some FP operations, because GCC will | ||
742 | * uses them as temporary storage for integer memory to memory copies. | ||
743 | * However, we need to deal with stt/ldt and sts/lds only. | ||
744 | */ | ||
745 | |||
746 | #define OP_INT_MASK ( 1L << 0x28 | 1L << 0x2c /* ldl stl */ \ | ||
747 | | 1L << 0x29 | 1L << 0x2d /* ldq stq */ \ | ||
748 | | 1L << 0x0c | 1L << 0x0d /* ldwu stw */ \ | ||
749 | | 1L << 0x0a | 1L << 0x0e ) /* ldbu stb */ | ||
750 | |||
751 | #define OP_WRITE_MASK ( 1L << 0x26 | 1L << 0x27 /* sts stt */ \ | ||
752 | | 1L << 0x2c | 1L << 0x2d /* stl stq */ \ | ||
753 | | 1L << 0x0d | 1L << 0x0e ) /* stw stb */ | ||
754 | |||
755 | #define R(x) ((size_t) &((struct pt_regs *)0)->x) | ||
756 | |||
757 | static int unauser_reg_offsets[32] = { | ||
758 | R(r0), R(r1), R(r2), R(r3), R(r4), R(r5), R(r6), R(r7), R(r8), | ||
759 | /* r9 ... r15 are stored in front of regs. */ | ||
760 | -56, -48, -40, -32, -24, -16, -8, | ||
761 | R(r16), R(r17), R(r18), | ||
762 | R(r19), R(r20), R(r21), R(r22), R(r23), R(r24), R(r25), R(r26), | ||
763 | R(r27), R(r28), R(gp), | ||
764 | 0, 0 | ||
765 | }; | ||
766 | |||
767 | #undef R | ||
768 | |||
769 | asmlinkage void | ||
770 | do_entUnaUser(void __user * va, unsigned long opcode, | ||
771 | unsigned long reg, struct pt_regs *regs) | ||
772 | { | ||
773 | static int cnt = 0; | ||
774 | static long last_time = 0; | ||
775 | |||
776 | unsigned long tmp1, tmp2, tmp3, tmp4; | ||
777 | unsigned long fake_reg, *reg_addr = &fake_reg; | ||
778 | siginfo_t info; | ||
779 | long error; | ||
780 | |||
781 | /* Check the UAC bits to decide what the user wants us to do | ||
782 | with the unaliged access. */ | ||
783 | |||
784 | if (!test_thread_flag (TIF_UAC_NOPRINT)) { | ||
785 | if (cnt >= 5 && jiffies - last_time > 5*HZ) { | ||
786 | cnt = 0; | ||
787 | } | ||
788 | if (++cnt < 5) { | ||
789 | printk("%s(%d): unaligned trap at %016lx: %p %lx %ld\n", | ||
790 | current->comm, current->pid, | ||
791 | regs->pc - 4, va, opcode, reg); | ||
792 | } | ||
793 | last_time = jiffies; | ||
794 | } | ||
795 | if (test_thread_flag (TIF_UAC_SIGBUS)) | ||
796 | goto give_sigbus; | ||
797 | /* Not sure why you'd want to use this, but... */ | ||
798 | if (test_thread_flag (TIF_UAC_NOFIX)) | ||
799 | return; | ||
800 | |||
801 | /* Don't bother reading ds in the access check since we already | ||
802 | know that this came from the user. Also rely on the fact that | ||
803 | the page at TASK_SIZE is unmapped and so can't be touched anyway. */ | ||
804 | if (!__access_ok((unsigned long)va, 0, USER_DS)) | ||
805 | goto give_sigsegv; | ||
806 | |||
807 | ++unaligned[1].count; | ||
808 | unaligned[1].va = (unsigned long)va; | ||
809 | unaligned[1].pc = regs->pc - 4; | ||
810 | |||
811 | if ((1L << opcode) & OP_INT_MASK) { | ||
812 | /* it's an integer load/store */ | ||
813 | if (reg < 30) { | ||
814 | reg_addr = (unsigned long *) | ||
815 | ((char *)regs + unauser_reg_offsets[reg]); | ||
816 | } else if (reg == 30) { | ||
817 | /* usp in PAL regs */ | ||
818 | fake_reg = rdusp(); | ||
819 | } else { | ||
820 | /* zero "register" */ | ||
821 | fake_reg = 0; | ||
822 | } | ||
823 | } | ||
824 | |||
825 | /* We don't want to use the generic get/put unaligned macros as | ||
826 | we want to trap exceptions. Only if we actually get an | ||
827 | exception will we decide whether we should have caught it. */ | ||
828 | |||
829 | switch (opcode) { | ||
830 | case 0x0c: /* ldwu */ | ||
831 | __asm__ __volatile__( | ||
832 | "1: ldq_u %1,0(%3)\n" | ||
833 | "2: ldq_u %2,1(%3)\n" | ||
834 | " extwl %1,%3,%1\n" | ||
835 | " extwh %2,%3,%2\n" | ||
836 | "3:\n" | ||
837 | ".section __ex_table,\"a\"\n" | ||
838 | " .long 1b - .\n" | ||
839 | " lda %1,3b-1b(%0)\n" | ||
840 | " .long 2b - .\n" | ||
841 | " lda %2,3b-2b(%0)\n" | ||
842 | ".previous" | ||
843 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2) | ||
844 | : "r"(va), "0"(0)); | ||
845 | if (error) | ||
846 | goto give_sigsegv; | ||
847 | *reg_addr = tmp1|tmp2; | ||
848 | break; | ||
849 | |||
850 | case 0x22: /* lds */ | ||
851 | __asm__ __volatile__( | ||
852 | "1: ldq_u %1,0(%3)\n" | ||
853 | "2: ldq_u %2,3(%3)\n" | ||
854 | " extll %1,%3,%1\n" | ||
855 | " extlh %2,%3,%2\n" | ||
856 | "3:\n" | ||
857 | ".section __ex_table,\"a\"\n" | ||
858 | " .long 1b - .\n" | ||
859 | " lda %1,3b-1b(%0)\n" | ||
860 | " .long 2b - .\n" | ||
861 | " lda %2,3b-2b(%0)\n" | ||
862 | ".previous" | ||
863 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2) | ||
864 | : "r"(va), "0"(0)); | ||
865 | if (error) | ||
866 | goto give_sigsegv; | ||
867 | alpha_write_fp_reg(reg, s_mem_to_reg((int)(tmp1|tmp2))); | ||
868 | return; | ||
869 | |||
870 | case 0x23: /* ldt */ | ||
871 | __asm__ __volatile__( | ||
872 | "1: ldq_u %1,0(%3)\n" | ||
873 | "2: ldq_u %2,7(%3)\n" | ||
874 | " extql %1,%3,%1\n" | ||
875 | " extqh %2,%3,%2\n" | ||
876 | "3:\n" | ||
877 | ".section __ex_table,\"a\"\n" | ||
878 | " .long 1b - .\n" | ||
879 | " lda %1,3b-1b(%0)\n" | ||
880 | " .long 2b - .\n" | ||
881 | " lda %2,3b-2b(%0)\n" | ||
882 | ".previous" | ||
883 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2) | ||
884 | : "r"(va), "0"(0)); | ||
885 | if (error) | ||
886 | goto give_sigsegv; | ||
887 | alpha_write_fp_reg(reg, tmp1|tmp2); | ||
888 | return; | ||
889 | |||
890 | case 0x28: /* ldl */ | ||
891 | __asm__ __volatile__( | ||
892 | "1: ldq_u %1,0(%3)\n" | ||
893 | "2: ldq_u %2,3(%3)\n" | ||
894 | " extll %1,%3,%1\n" | ||
895 | " extlh %2,%3,%2\n" | ||
896 | "3:\n" | ||
897 | ".section __ex_table,\"a\"\n" | ||
898 | " .long 1b - .\n" | ||
899 | " lda %1,3b-1b(%0)\n" | ||
900 | " .long 2b - .\n" | ||
901 | " lda %2,3b-2b(%0)\n" | ||
902 | ".previous" | ||
903 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2) | ||
904 | : "r"(va), "0"(0)); | ||
905 | if (error) | ||
906 | goto give_sigsegv; | ||
907 | *reg_addr = (int)(tmp1|tmp2); | ||
908 | break; | ||
909 | |||
910 | case 0x29: /* ldq */ | ||
911 | __asm__ __volatile__( | ||
912 | "1: ldq_u %1,0(%3)\n" | ||
913 | "2: ldq_u %2,7(%3)\n" | ||
914 | " extql %1,%3,%1\n" | ||
915 | " extqh %2,%3,%2\n" | ||
916 | "3:\n" | ||
917 | ".section __ex_table,\"a\"\n" | ||
918 | " .long 1b - .\n" | ||
919 | " lda %1,3b-1b(%0)\n" | ||
920 | " .long 2b - .\n" | ||
921 | " lda %2,3b-2b(%0)\n" | ||
922 | ".previous" | ||
923 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2) | ||
924 | : "r"(va), "0"(0)); | ||
925 | if (error) | ||
926 | goto give_sigsegv; | ||
927 | *reg_addr = tmp1|tmp2; | ||
928 | break; | ||
929 | |||
930 | /* Note that the store sequences do not indicate that they change | ||
931 | memory because it _should_ be affecting nothing in this context. | ||
932 | (Otherwise we have other, much larger, problems.) */ | ||
933 | case 0x0d: /* stw */ | ||
934 | __asm__ __volatile__( | ||
935 | "1: ldq_u %2,1(%5)\n" | ||
936 | "2: ldq_u %1,0(%5)\n" | ||
937 | " inswh %6,%5,%4\n" | ||
938 | " inswl %6,%5,%3\n" | ||
939 | " mskwh %2,%5,%2\n" | ||
940 | " mskwl %1,%5,%1\n" | ||
941 | " or %2,%4,%2\n" | ||
942 | " or %1,%3,%1\n" | ||
943 | "3: stq_u %2,1(%5)\n" | ||
944 | "4: stq_u %1,0(%5)\n" | ||
945 | "5:\n" | ||
946 | ".section __ex_table,\"a\"\n" | ||
947 | " .long 1b - .\n" | ||
948 | " lda %2,5b-1b(%0)\n" | ||
949 | " .long 2b - .\n" | ||
950 | " lda %1,5b-2b(%0)\n" | ||
951 | " .long 3b - .\n" | ||
952 | " lda $31,5b-3b(%0)\n" | ||
953 | " .long 4b - .\n" | ||
954 | " lda $31,5b-4b(%0)\n" | ||
955 | ".previous" | ||
956 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2), | ||
957 | "=&r"(tmp3), "=&r"(tmp4) | ||
958 | : "r"(va), "r"(*reg_addr), "0"(0)); | ||
959 | if (error) | ||
960 | goto give_sigsegv; | ||
961 | return; | ||
962 | |||
963 | case 0x26: /* sts */ | ||
964 | fake_reg = s_reg_to_mem(alpha_read_fp_reg(reg)); | ||
965 | /* FALLTHRU */ | ||
966 | |||
967 | case 0x2c: /* stl */ | ||
968 | __asm__ __volatile__( | ||
969 | "1: ldq_u %2,3(%5)\n" | ||
970 | "2: ldq_u %1,0(%5)\n" | ||
971 | " inslh %6,%5,%4\n" | ||
972 | " insll %6,%5,%3\n" | ||
973 | " msklh %2,%5,%2\n" | ||
974 | " mskll %1,%5,%1\n" | ||
975 | " or %2,%4,%2\n" | ||
976 | " or %1,%3,%1\n" | ||
977 | "3: stq_u %2,3(%5)\n" | ||
978 | "4: stq_u %1,0(%5)\n" | ||
979 | "5:\n" | ||
980 | ".section __ex_table,\"a\"\n" | ||
981 | " .long 1b - .\n" | ||
982 | " lda %2,5b-1b(%0)\n" | ||
983 | " .long 2b - .\n" | ||
984 | " lda %1,5b-2b(%0)\n" | ||
985 | " .long 3b - .\n" | ||
986 | " lda $31,5b-3b(%0)\n" | ||
987 | " .long 4b - .\n" | ||
988 | " lda $31,5b-4b(%0)\n" | ||
989 | ".previous" | ||
990 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2), | ||
991 | "=&r"(tmp3), "=&r"(tmp4) | ||
992 | : "r"(va), "r"(*reg_addr), "0"(0)); | ||
993 | if (error) | ||
994 | goto give_sigsegv; | ||
995 | return; | ||
996 | |||
997 | case 0x27: /* stt */ | ||
998 | fake_reg = alpha_read_fp_reg(reg); | ||
999 | /* FALLTHRU */ | ||
1000 | |||
1001 | case 0x2d: /* stq */ | ||
1002 | __asm__ __volatile__( | ||
1003 | "1: ldq_u %2,7(%5)\n" | ||
1004 | "2: ldq_u %1,0(%5)\n" | ||
1005 | " insqh %6,%5,%4\n" | ||
1006 | " insql %6,%5,%3\n" | ||
1007 | " mskqh %2,%5,%2\n" | ||
1008 | " mskql %1,%5,%1\n" | ||
1009 | " or %2,%4,%2\n" | ||
1010 | " or %1,%3,%1\n" | ||
1011 | "3: stq_u %2,7(%5)\n" | ||
1012 | "4: stq_u %1,0(%5)\n" | ||
1013 | "5:\n" | ||
1014 | ".section __ex_table,\"a\"\n\t" | ||
1015 | " .long 1b - .\n" | ||
1016 | " lda %2,5b-1b(%0)\n" | ||
1017 | " .long 2b - .\n" | ||
1018 | " lda %1,5b-2b(%0)\n" | ||
1019 | " .long 3b - .\n" | ||
1020 | " lda $31,5b-3b(%0)\n" | ||
1021 | " .long 4b - .\n" | ||
1022 | " lda $31,5b-4b(%0)\n" | ||
1023 | ".previous" | ||
1024 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2), | ||
1025 | "=&r"(tmp3), "=&r"(tmp4) | ||
1026 | : "r"(va), "r"(*reg_addr), "0"(0)); | ||
1027 | if (error) | ||
1028 | goto give_sigsegv; | ||
1029 | return; | ||
1030 | |||
1031 | default: | ||
1032 | /* What instruction were you trying to use, exactly? */ | ||
1033 | goto give_sigbus; | ||
1034 | } | ||
1035 | |||
1036 | /* Only integer loads should get here; everyone else returns early. */ | ||
1037 | if (reg == 30) | ||
1038 | wrusp(fake_reg); | ||
1039 | return; | ||
1040 | |||
1041 | give_sigsegv: | ||
1042 | regs->pc -= 4; /* make pc point to faulting insn */ | ||
1043 | info.si_signo = SIGSEGV; | ||
1044 | info.si_errno = 0; | ||
1045 | |||
1046 | /* We need to replicate some of the logic in mm/fault.c, | ||
1047 | since we don't have access to the fault code in the | ||
1048 | exception handling return path. */ | ||
1049 | if (!__access_ok((unsigned long)va, 0, USER_DS)) | ||
1050 | info.si_code = SEGV_ACCERR; | ||
1051 | else { | ||
1052 | struct mm_struct *mm = current->mm; | ||
1053 | down_read(&mm->mmap_sem); | ||
1054 | if (find_vma(mm, (unsigned long)va)) | ||
1055 | info.si_code = SEGV_ACCERR; | ||
1056 | else | ||
1057 | info.si_code = SEGV_MAPERR; | ||
1058 | up_read(&mm->mmap_sem); | ||
1059 | } | ||
1060 | info.si_addr = va; | ||
1061 | send_sig_info(SIGSEGV, &info, current); | ||
1062 | return; | ||
1063 | |||
1064 | give_sigbus: | ||
1065 | regs->pc -= 4; | ||
1066 | info.si_signo = SIGBUS; | ||
1067 | info.si_errno = 0; | ||
1068 | info.si_code = BUS_ADRALN; | ||
1069 | info.si_addr = va; | ||
1070 | send_sig_info(SIGBUS, &info, current); | ||
1071 | return; | ||
1072 | } | ||
1073 | |||
1074 | void __init | ||
1075 | trap_init(void) | ||
1076 | { | ||
1077 | /* Tell PAL-code what global pointer we want in the kernel. */ | ||
1078 | register unsigned long gptr __asm__("$29"); | ||
1079 | wrkgp(gptr); | ||
1080 | |||
1081 | /* Hack for Multia (UDB) and JENSEN: some of their SRMs have | ||
1082 | a bug in the handling of the opDEC fault. Fix it up if so. */ | ||
1083 | if (implver() == IMPLVER_EV4) | ||
1084 | opDEC_check(); | ||
1085 | |||
1086 | wrent(entArith, 1); | ||
1087 | wrent(entMM, 2); | ||
1088 | wrent(entIF, 3); | ||
1089 | wrent(entUna, 4); | ||
1090 | wrent(entSys, 5); | ||
1091 | wrent(entDbg, 6); | ||
1092 | } | ||