Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/sh/kernel/traps_32.c
26424 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* 'traps.c' handles hardware traps and faults after we have saved some
4
* state in 'entry.S'.
5
*
6
* SuperH version: Copyright (C) 1999 Niibe Yutaka
7
* Copyright (C) 2000 Philipp Rumpf
8
* Copyright (C) 2000 David Howells
9
* Copyright (C) 2002 - 2010 Paul Mundt
10
*/
11
#include <linux/kernel.h>
12
#include <linux/ptrace.h>
13
#include <linux/hardirq.h>
14
#include <linux/init.h>
15
#include <linux/spinlock.h>
16
#include <linux/kallsyms.h>
17
#include <linux/io.h>
18
#include <linux/bug.h>
19
#include <linux/debug_locks.h>
20
#include <linux/kdebug.h>
21
#include <linux/limits.h>
22
#include <linux/sysfs.h>
23
#include <linux/uaccess.h>
24
#include <linux/perf_event.h>
25
#include <linux/sched/task_stack.h>
26
27
#include <asm/alignment.h>
28
#include <asm/fpu.h>
29
#include <asm/kprobes.h>
30
#include <asm/setup.h>
31
#include <asm/traps.h>
32
#include <asm/bl_bit.h>
33
34
#ifdef CONFIG_CPU_SH2
35
# define TRAP_RESERVED_INST 4
36
# define TRAP_ILLEGAL_SLOT_INST 6
37
# define TRAP_ADDRESS_ERROR 9
38
# ifdef CONFIG_CPU_SH2A
39
# define TRAP_UBC 12
40
# define TRAP_FPU_ERROR 13
41
# define TRAP_DIVZERO_ERROR 17
42
# define TRAP_DIVOVF_ERROR 18
43
# endif
44
#else
45
#define TRAP_RESERVED_INST 12
46
#define TRAP_ILLEGAL_SLOT_INST 13
47
#endif
48
49
static inline void sign_extend(unsigned int count, unsigned char *dst)
50
{
51
#ifdef __LITTLE_ENDIAN__
52
if ((count == 1) && dst[0] & 0x80) {
53
dst[1] = 0xff;
54
dst[2] = 0xff;
55
dst[3] = 0xff;
56
}
57
if ((count == 2) && dst[1] & 0x80) {
58
dst[2] = 0xff;
59
dst[3] = 0xff;
60
}
61
#else
62
if ((count == 1) && dst[3] & 0x80) {
63
dst[2] = 0xff;
64
dst[1] = 0xff;
65
dst[0] = 0xff;
66
}
67
if ((count == 2) && dst[2] & 0x80) {
68
dst[1] = 0xff;
69
dst[0] = 0xff;
70
}
71
#endif
72
}
73
74
static struct mem_access user_mem_access = {
75
copy_from_user,
76
copy_to_user,
77
};
78
79
static unsigned long copy_from_kernel_wrapper(void *dst, const void __user *src,
80
unsigned long cnt)
81
{
82
return copy_from_kernel_nofault(dst, (const void __force *)src, cnt);
83
}
84
85
static unsigned long copy_to_kernel_wrapper(void __user *dst, const void *src,
86
unsigned long cnt)
87
{
88
return copy_to_kernel_nofault((void __force *)dst, src, cnt);
89
}
90
91
static struct mem_access kernel_mem_access = {
92
copy_from_kernel_wrapper,
93
copy_to_kernel_wrapper,
94
};
95
96
/*
97
* handle an instruction that does an unaligned memory access by emulating the
98
* desired behaviour
99
* - note that PC _may not_ point to the faulting instruction
100
* (if that instruction is in a branch delay slot)
101
* - return 0 if emulation okay, -EFAULT on existential error
102
*/
103
static int handle_unaligned_ins(insn_size_t instruction, struct pt_regs *regs,
104
struct mem_access *ma)
105
{
106
int ret, index, count;
107
unsigned long *rm, *rn;
108
unsigned char *src, *dst;
109
unsigned char __user *srcu, *dstu;
110
111
index = (instruction>>8)&15; /* 0x0F00 */
112
rn = &regs->regs[index];
113
114
index = (instruction>>4)&15; /* 0x00F0 */
115
rm = &regs->regs[index];
116
117
count = 1<<(instruction&3);
118
119
switch (count) {
120
case 1: inc_unaligned_byte_access(); break;
121
case 2: inc_unaligned_word_access(); break;
122
case 4: inc_unaligned_dword_access(); break;
123
case 8: inc_unaligned_multi_access(); break;
124
}
125
126
ret = -EFAULT;
127
switch (instruction>>12) {
128
case 0: /* mov.[bwl] to/from memory via r0+rn */
129
if (instruction & 8) {
130
/* from memory */
131
srcu = (unsigned char __user *)*rm;
132
srcu += regs->regs[0];
133
dst = (unsigned char *)rn;
134
*(unsigned long *)dst = 0;
135
136
#if !defined(__LITTLE_ENDIAN__)
137
dst += 4-count;
138
#endif
139
if (ma->from(dst, srcu, count))
140
goto fetch_fault;
141
142
sign_extend(count, dst);
143
} else {
144
/* to memory */
145
src = (unsigned char *)rm;
146
#if !defined(__LITTLE_ENDIAN__)
147
src += 4-count;
148
#endif
149
dstu = (unsigned char __user *)*rn;
150
dstu += regs->regs[0];
151
152
if (ma->to(dstu, src, count))
153
goto fetch_fault;
154
}
155
ret = 0;
156
break;
157
158
case 1: /* mov.l Rm,@(disp,Rn) */
159
src = (unsigned char*) rm;
160
dstu = (unsigned char __user *)*rn;
161
dstu += (instruction&0x000F)<<2;
162
163
if (ma->to(dstu, src, 4))
164
goto fetch_fault;
165
ret = 0;
166
break;
167
168
case 2: /* mov.[bwl] to memory, possibly with pre-decrement */
169
if (instruction & 4)
170
*rn -= count;
171
src = (unsigned char*) rm;
172
dstu = (unsigned char __user *)*rn;
173
#if !defined(__LITTLE_ENDIAN__)
174
src += 4-count;
175
#endif
176
if (ma->to(dstu, src, count))
177
goto fetch_fault;
178
ret = 0;
179
break;
180
181
case 5: /* mov.l @(disp,Rm),Rn */
182
srcu = (unsigned char __user *)*rm;
183
srcu += (instruction & 0x000F) << 2;
184
dst = (unsigned char *)rn;
185
*(unsigned long *)dst = 0;
186
187
if (ma->from(dst, srcu, 4))
188
goto fetch_fault;
189
ret = 0;
190
break;
191
192
case 6: /* mov.[bwl] from memory, possibly with post-increment */
193
srcu = (unsigned char __user *)*rm;
194
if (instruction & 4)
195
*rm += count;
196
dst = (unsigned char*) rn;
197
*(unsigned long*)dst = 0;
198
199
#if !defined(__LITTLE_ENDIAN__)
200
dst += 4-count;
201
#endif
202
if (ma->from(dst, srcu, count))
203
goto fetch_fault;
204
sign_extend(count, dst);
205
ret = 0;
206
break;
207
208
case 8:
209
switch ((instruction&0xFF00)>>8) {
210
case 0x81: /* mov.w R0,@(disp,Rn) */
211
src = (unsigned char *) &regs->regs[0];
212
#if !defined(__LITTLE_ENDIAN__)
213
src += 2;
214
#endif
215
dstu = (unsigned char __user *)*rm; /* called Rn in the spec */
216
dstu += (instruction & 0x000F) << 1;
217
218
if (ma->to(dstu, src, 2))
219
goto fetch_fault;
220
ret = 0;
221
break;
222
223
case 0x85: /* mov.w @(disp,Rm),R0 */
224
srcu = (unsigned char __user *)*rm;
225
srcu += (instruction & 0x000F) << 1;
226
dst = (unsigned char *) &regs->regs[0];
227
*(unsigned long *)dst = 0;
228
229
#if !defined(__LITTLE_ENDIAN__)
230
dst += 2;
231
#endif
232
if (ma->from(dst, srcu, 2))
233
goto fetch_fault;
234
sign_extend(2, dst);
235
ret = 0;
236
break;
237
}
238
break;
239
240
case 9: /* mov.w @(disp,PC),Rn */
241
srcu = (unsigned char __user *)regs->pc;
242
srcu += 4;
243
srcu += (instruction & 0x00FF) << 1;
244
dst = (unsigned char *)rn;
245
*(unsigned long *)dst = 0;
246
247
#if !defined(__LITTLE_ENDIAN__)
248
dst += 2;
249
#endif
250
251
if (ma->from(dst, srcu, 2))
252
goto fetch_fault;
253
sign_extend(2, dst);
254
ret = 0;
255
break;
256
257
case 0xd: /* mov.l @(disp,PC),Rn */
258
srcu = (unsigned char __user *)(regs->pc & ~0x3);
259
srcu += 4;
260
srcu += (instruction & 0x00FF) << 2;
261
dst = (unsigned char *)rn;
262
*(unsigned long *)dst = 0;
263
264
if (ma->from(dst, srcu, 4))
265
goto fetch_fault;
266
ret = 0;
267
break;
268
}
269
return ret;
270
271
fetch_fault:
272
/* Argh. Address not only misaligned but also non-existent.
273
* Raise an EFAULT and see if it's trapped
274
*/
275
die_if_no_fixup("Fault in unaligned fixup", regs, 0);
276
return -EFAULT;
277
}
278
279
/*
280
* emulate the instruction in the delay slot
281
* - fetches the instruction from PC+2
282
*/
283
static inline int handle_delayslot(struct pt_regs *regs,
284
insn_size_t old_instruction,
285
struct mem_access *ma)
286
{
287
insn_size_t instruction;
288
void __user *addr = (void __user *)(regs->pc +
289
instruction_size(old_instruction));
290
291
if (copy_from_user(&instruction, addr, sizeof(instruction))) {
292
/* the instruction-fetch faulted */
293
if (user_mode(regs))
294
return -EFAULT;
295
296
/* kernel */
297
die("delay-slot-insn faulting in handle_unaligned_delayslot",
298
regs, 0);
299
}
300
301
return handle_unaligned_ins(instruction, regs, ma);
302
}
303
304
/*
305
* handle an instruction that does an unaligned memory access
306
* - have to be careful of branch delay-slot instructions that fault
307
* SH3:
308
* - if the branch would be taken PC points to the branch
309
* - if the branch would not be taken, PC points to delay-slot
310
* SH4:
311
* - PC always points to delayed branch
312
* - return 0 if handled, -EFAULT if failed (may not return if in kernel)
313
*/
314
315
/* Macros to determine offset from current PC for branch instructions */
316
/* Explicit type coercion is used to force sign extension where needed */
317
#define SH_PC_8BIT_OFFSET(instr) ((((signed char)(instr))*2) + 4)
318
#define SH_PC_12BIT_OFFSET(instr) ((((signed short)(instr<<4))>>3) + 4)
319
320
int handle_unaligned_access(insn_size_t instruction, struct pt_regs *regs,
321
struct mem_access *ma, int expected,
322
unsigned long address)
323
{
324
u_int rm;
325
int ret, index;
326
327
/*
328
* XXX: We can't handle mixed 16/32-bit instructions yet
329
*/
330
if (instruction_size(instruction) != 2)
331
return -EINVAL;
332
333
index = (instruction>>8)&15; /* 0x0F00 */
334
rm = regs->regs[index];
335
336
/*
337
* Log the unexpected fixups, and then pass them on to perf.
338
*
339
* We intentionally don't report the expected cases to perf as
340
* otherwise the trapped I/O case will skew the results too much
341
* to be useful.
342
*/
343
if (!expected) {
344
unaligned_fixups_notify(current, instruction, regs);
345
perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1,
346
regs, address);
347
}
348
349
ret = -EFAULT;
350
switch (instruction&0xF000) {
351
case 0x0000:
352
if (instruction==0x000B) {
353
/* rts */
354
ret = handle_delayslot(regs, instruction, ma);
355
if (ret==0)
356
regs->pc = regs->pr;
357
}
358
else if ((instruction&0x00FF)==0x0023) {
359
/* braf @Rm */
360
ret = handle_delayslot(regs, instruction, ma);
361
if (ret==0)
362
regs->pc += rm + 4;
363
}
364
else if ((instruction&0x00FF)==0x0003) {
365
/* bsrf @Rm */
366
ret = handle_delayslot(regs, instruction, ma);
367
if (ret==0) {
368
regs->pr = regs->pc + 4;
369
regs->pc += rm + 4;
370
}
371
}
372
else {
373
/* mov.[bwl] to/from memory via r0+rn */
374
goto simple;
375
}
376
break;
377
378
case 0x1000: /* mov.l Rm,@(disp,Rn) */
379
goto simple;
380
381
case 0x2000: /* mov.[bwl] to memory, possibly with pre-decrement */
382
goto simple;
383
384
case 0x4000:
385
if ((instruction&0x00FF)==0x002B) {
386
/* jmp @Rm */
387
ret = handle_delayslot(regs, instruction, ma);
388
if (ret==0)
389
regs->pc = rm;
390
}
391
else if ((instruction&0x00FF)==0x000B) {
392
/* jsr @Rm */
393
ret = handle_delayslot(regs, instruction, ma);
394
if (ret==0) {
395
regs->pr = regs->pc + 4;
396
regs->pc = rm;
397
}
398
}
399
else {
400
/* mov.[bwl] to/from memory via r0+rn */
401
goto simple;
402
}
403
break;
404
405
case 0x5000: /* mov.l @(disp,Rm),Rn */
406
goto simple;
407
408
case 0x6000: /* mov.[bwl] from memory, possibly with post-increment */
409
goto simple;
410
411
case 0x8000: /* bf lab, bf/s lab, bt lab, bt/s lab */
412
switch (instruction&0x0F00) {
413
case 0x0100: /* mov.w R0,@(disp,Rm) */
414
goto simple;
415
case 0x0500: /* mov.w @(disp,Rm),R0 */
416
goto simple;
417
case 0x0B00: /* bf lab - no delayslot*/
418
ret = 0;
419
break;
420
case 0x0F00: /* bf/s lab */
421
ret = handle_delayslot(regs, instruction, ma);
422
if (ret==0) {
423
#if defined(CONFIG_CPU_SH4) || defined(CONFIG_SH7705_CACHE_32KB)
424
if ((regs->sr & 0x00000001) != 0)
425
regs->pc += 4; /* next after slot */
426
else
427
#endif
428
regs->pc += SH_PC_8BIT_OFFSET(instruction);
429
}
430
break;
431
case 0x0900: /* bt lab - no delayslot */
432
ret = 0;
433
break;
434
case 0x0D00: /* bt/s lab */
435
ret = handle_delayslot(regs, instruction, ma);
436
if (ret==0) {
437
#if defined(CONFIG_CPU_SH4) || defined(CONFIG_SH7705_CACHE_32KB)
438
if ((regs->sr & 0x00000001) == 0)
439
regs->pc += 4; /* next after slot */
440
else
441
#endif
442
regs->pc += SH_PC_8BIT_OFFSET(instruction);
443
}
444
break;
445
}
446
break;
447
448
case 0x9000: /* mov.w @(disp,Rm),Rn */
449
goto simple;
450
451
case 0xA000: /* bra label */
452
ret = handle_delayslot(regs, instruction, ma);
453
if (ret==0)
454
regs->pc += SH_PC_12BIT_OFFSET(instruction);
455
break;
456
457
case 0xB000: /* bsr label */
458
ret = handle_delayslot(regs, instruction, ma);
459
if (ret==0) {
460
regs->pr = regs->pc + 4;
461
regs->pc += SH_PC_12BIT_OFFSET(instruction);
462
}
463
break;
464
465
case 0xD000: /* mov.l @(disp,Rm),Rn */
466
goto simple;
467
}
468
return ret;
469
470
/* handle non-delay-slot instruction */
471
simple:
472
ret = handle_unaligned_ins(instruction, regs, ma);
473
if (ret==0)
474
regs->pc += instruction_size(instruction);
475
return ret;
476
}
477
478
/*
479
* Handle various address error exceptions:
480
* - instruction address error:
481
* misaligned PC
482
* PC >= 0x80000000 in user mode
483
* - data address error (read and write)
484
* misaligned data access
485
* access to >= 0x80000000 is user mode
486
* Unfortuntaly we can't distinguish between instruction address error
487
* and data address errors caused by read accesses.
488
*/
489
asmlinkage void do_address_error(struct pt_regs *regs,
490
unsigned long writeaccess,
491
unsigned long address)
492
{
493
unsigned long error_code = 0;
494
insn_size_t instruction;
495
int tmp;
496
497
/* Intentional ifdef */
498
#ifdef CONFIG_CPU_HAS_SR_RB
499
error_code = lookup_exception_vector();
500
#endif
501
502
if (user_mode(regs)) {
503
int si_code = BUS_ADRERR;
504
unsigned int user_action;
505
506
local_irq_enable();
507
inc_unaligned_user_access();
508
509
if (copy_from_user(&instruction, (insn_size_t __user *)(regs->pc & ~1),
510
sizeof(instruction))) {
511
goto uspace_segv;
512
}
513
514
/* shout about userspace fixups */
515
unaligned_fixups_notify(current, instruction, regs);
516
517
user_action = unaligned_user_action();
518
if (user_action & UM_FIXUP)
519
goto fixup;
520
if (user_action & UM_SIGNAL)
521
goto uspace_segv;
522
else {
523
/* ignore */
524
regs->pc += instruction_size(instruction);
525
return;
526
}
527
528
fixup:
529
/* bad PC is not something we can fix */
530
if (regs->pc & 1) {
531
si_code = BUS_ADRALN;
532
goto uspace_segv;
533
}
534
535
tmp = handle_unaligned_access(instruction, regs,
536
&user_mem_access, 0,
537
address);
538
539
if (tmp == 0)
540
return; /* sorted */
541
uspace_segv:
542
printk(KERN_NOTICE "Sending SIGBUS to \"%s\" due to unaligned "
543
"access (PC %lx PR %lx)\n", current->comm, regs->pc,
544
regs->pr);
545
546
force_sig_fault(SIGBUS, si_code, (void __user *)address);
547
} else {
548
inc_unaligned_kernel_access();
549
550
if (regs->pc & 1)
551
die("unaligned program counter", regs, error_code);
552
553
if (copy_from_kernel_nofault(&instruction, (void *)(regs->pc),
554
sizeof(instruction))) {
555
/* Argh. Fault on the instruction itself.
556
This should never happen non-SMP
557
*/
558
die("insn faulting in do_address_error", regs, 0);
559
}
560
561
unaligned_fixups_notify(current, instruction, regs);
562
563
handle_unaligned_access(instruction, regs, &kernel_mem_access,
564
0, address);
565
}
566
}
567
568
#ifdef CONFIG_SH_DSP
569
/*
570
* SH-DSP support [email protected].
571
*/
572
static int is_dsp_inst(struct pt_regs *regs)
573
{
574
unsigned short inst = 0;
575
576
/*
577
* Safe guard if DSP mode is already enabled or we're lacking
578
* the DSP altogether.
579
*/
580
if (!(current_cpu_data.flags & CPU_HAS_DSP) || (regs->sr & SR_DSP))
581
return 0;
582
583
get_user(inst, ((unsigned short *) regs->pc));
584
585
inst &= 0xf000;
586
587
/* Check for any type of DSP or support instruction */
588
if ((inst == 0xf000) || (inst == 0x4000))
589
return 1;
590
591
return 0;
592
}
593
#else
594
static inline int is_dsp_inst(struct pt_regs *regs) { return 0; }
595
#endif /* CONFIG_SH_DSP */
596
597
#ifdef CONFIG_CPU_SH2A
598
asmlinkage void do_divide_error(unsigned long r4)
599
{
600
int code;
601
602
switch (r4) {
603
case TRAP_DIVZERO_ERROR:
604
code = FPE_INTDIV;
605
break;
606
case TRAP_DIVOVF_ERROR:
607
code = FPE_INTOVF;
608
break;
609
default:
610
/* Let gcc know unhandled cases don't make it past here */
611
return;
612
}
613
force_sig_fault(SIGFPE, code, NULL);
614
}
615
#endif
616
617
asmlinkage void do_reserved_inst(void)
618
{
619
struct pt_regs *regs = current_pt_regs();
620
unsigned long error_code;
621
622
#ifdef CONFIG_SH_FPU_EMU
623
unsigned short inst = 0;
624
int err;
625
626
get_user(inst, (unsigned short __user *)regs->pc);
627
628
err = do_fpu_inst(inst, regs);
629
if (!err) {
630
regs->pc += instruction_size(inst);
631
return;
632
}
633
/* not a FPU inst. */
634
#endif
635
636
#ifdef CONFIG_SH_DSP
637
/* Check if it's a DSP instruction */
638
if (is_dsp_inst(regs)) {
639
/* Enable DSP mode, and restart instruction. */
640
regs->sr |= SR_DSP;
641
/* Save DSP mode */
642
current->thread.dsp_status.status |= SR_DSP;
643
return;
644
}
645
#endif
646
647
error_code = lookup_exception_vector();
648
649
local_irq_enable();
650
force_sig(SIGILL);
651
die_if_no_fixup("reserved instruction", regs, error_code);
652
}
653
654
#ifdef CONFIG_SH_FPU_EMU
655
static int emulate_branch(unsigned short inst, struct pt_regs *regs)
656
{
657
/*
658
* bfs: 8fxx: PC+=d*2+4;
659
* bts: 8dxx: PC+=d*2+4;
660
* bra: axxx: PC+=D*2+4;
661
* bsr: bxxx: PC+=D*2+4 after PR=PC+4;
662
* braf:0x23: PC+=Rn*2+4;
663
* bsrf:0x03: PC+=Rn*2+4 after PR=PC+4;
664
* jmp: 4x2b: PC=Rn;
665
* jsr: 4x0b: PC=Rn after PR=PC+4;
666
* rts: 000b: PC=PR;
667
*/
668
if (((inst & 0xf000) == 0xb000) || /* bsr */
669
((inst & 0xf0ff) == 0x0003) || /* bsrf */
670
((inst & 0xf0ff) == 0x400b)) /* jsr */
671
regs->pr = regs->pc + 4;
672
673
if ((inst & 0xfd00) == 0x8d00) { /* bfs, bts */
674
regs->pc += SH_PC_8BIT_OFFSET(inst);
675
return 0;
676
}
677
678
if ((inst & 0xe000) == 0xa000) { /* bra, bsr */
679
regs->pc += SH_PC_12BIT_OFFSET(inst);
680
return 0;
681
}
682
683
if ((inst & 0xf0df) == 0x0003) { /* braf, bsrf */
684
regs->pc += regs->regs[(inst & 0x0f00) >> 8] + 4;
685
return 0;
686
}
687
688
if ((inst & 0xf0df) == 0x400b) { /* jmp, jsr */
689
regs->pc = regs->regs[(inst & 0x0f00) >> 8];
690
return 0;
691
}
692
693
if ((inst & 0xffff) == 0x000b) { /* rts */
694
regs->pc = regs->pr;
695
return 0;
696
}
697
698
return 1;
699
}
700
#endif
701
702
asmlinkage void do_illegal_slot_inst(void)
703
{
704
struct pt_regs *regs = current_pt_regs();
705
unsigned long inst;
706
707
if (kprobe_handle_illslot(regs->pc) == 0)
708
return;
709
710
#ifdef CONFIG_SH_FPU_EMU
711
get_user(inst, (unsigned short __user *)regs->pc + 1);
712
if (!do_fpu_inst(inst, regs)) {
713
get_user(inst, (unsigned short __user *)regs->pc);
714
if (!emulate_branch(inst, regs))
715
return;
716
/* fault in branch.*/
717
}
718
/* not a FPU inst. */
719
#endif
720
721
inst = lookup_exception_vector();
722
723
local_irq_enable();
724
force_sig(SIGILL);
725
die_if_no_fixup("illegal slot instruction", regs, inst);
726
}
727
728
asmlinkage void do_exception_error(void)
729
{
730
long ex;
731
732
ex = lookup_exception_vector();
733
die_if_kernel("exception", current_pt_regs(), ex);
734
}
735
736
void per_cpu_trap_init(void)
737
{
738
extern void *vbr_base;
739
740
/* NOTE: The VBR value should be at P1
741
(or P2, virtural "fixed" address space).
742
It's definitely should not in physical address. */
743
744
asm volatile("ldc %0, vbr"
745
: /* no output */
746
: "r" (&vbr_base)
747
: "memory");
748
749
/* disable exception blocking now when the vbr has been setup */
750
clear_bl_bit();
751
}
752
753
void *set_exception_table_vec(unsigned int vec, void *handler)
754
{
755
extern void *exception_handling_table[];
756
void *old_handler;
757
758
old_handler = exception_handling_table[vec];
759
exception_handling_table[vec] = handler;
760
return old_handler;
761
}
762
763
void __init trap_init(void)
764
{
765
set_exception_table_vec(TRAP_RESERVED_INST, do_reserved_inst);
766
set_exception_table_vec(TRAP_ILLEGAL_SLOT_INST, do_illegal_slot_inst);
767
768
#if defined(CONFIG_CPU_SH4) && !defined(CONFIG_SH_FPU) || \
769
defined(CONFIG_SH_FPU_EMU)
770
/*
771
* For SH-4 lacking an FPU, treat floating point instructions as
772
* reserved. They'll be handled in the math-emu case, or faulted on
773
* otherwise.
774
*/
775
set_exception_table_evt(0x800, do_reserved_inst);
776
set_exception_table_evt(0x820, do_illegal_slot_inst);
777
#elif defined(CONFIG_SH_FPU)
778
set_exception_table_evt(0x800, fpu_state_restore_trap_handler);
779
set_exception_table_evt(0x820, fpu_state_restore_trap_handler);
780
#endif
781
782
#ifdef CONFIG_CPU_SH2
783
set_exception_table_vec(TRAP_ADDRESS_ERROR, address_error_trap_handler);
784
#endif
785
#ifdef CONFIG_CPU_SH2A
786
set_exception_table_vec(TRAP_DIVZERO_ERROR, do_divide_error);
787
set_exception_table_vec(TRAP_DIVOVF_ERROR, do_divide_error);
788
#ifdef CONFIG_SH_FPU
789
set_exception_table_vec(TRAP_FPU_ERROR, fpu_error_trap_handler);
790
#endif
791
#endif
792
793
#ifdef TRAP_UBC
794
set_exception_table_vec(TRAP_UBC, breakpoint_trap_handler);
795
#endif
796
}
797
798