Blame view

arch/sh/kernel/traps_32.c 21.3 KB
6b0022305   Paul Mundt   sh: Proper show_s...
1
2
3
  /*
   * 'traps.c' handles hardware traps and faults after we have saved some
   * state in 'entry.S'.
1da177e4c   Linus Torvalds   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   Paul Mundt   sh: wire up perf ...
8
   *                  Copyright (C) 2002 - 2010 Paul Mundt
6b0022305   Paul Mundt   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   Linus Torvalds   Linux-2.6.12-rc2
13
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
  #include <linux/ptrace.h>
ba84be233   Russell King   remove linux/hard...
16
  #include <linux/hardirq.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
  #include <linux/init.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
19
20
  #include <linux/spinlock.h>
  #include <linux/module.h>
  #include <linux/kallsyms.h>
1f666587d   Paul Mundt   sh: Fix exception...
21
  #include <linux/io.h>
fa6915117   Paul Mundt   sh: generic BUG()...
22
  #include <linux/bug.h>
9b8c90eb0   Paul Mundt   sh: show held loc...
23
  #include <linux/debug_locks.h>
b118ca572   Paul Mundt   sh: Convert to co...
24
  #include <linux/kdebug.h>
e11327662   Paul Mundt   sh: Wire up kdump...
25
  #include <linux/kexec.h>
dc34d312c   Paul Mundt   sh: BUG() handlin...
26
  #include <linux/limits.h>
af67c3a9e   Paul Mundt   sh: update die() ...
27
  #include <linux/sysfs.h>
a99eae541   Paul Mundt   sh: Split out the...
28
  #include <linux/uaccess.h>
ace2dc7d1   Paul Mundt   sh: wire up perf ...
29
  #include <linux/perf_event.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
  #include <asm/system.h>
a99eae541   Paul Mundt   sh: Split out the...
31
  #include <asm/alignment.h>
fad0f9013   Andrew Morton   (fad0f90134197259...
32
  #include <asm/fpu.h>
d39f54501   Chris Smith   sh: Add kprobes s...
33
  #include <asm/kprobes.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35
  #ifdef CONFIG_CPU_SH2
0983b3184   Yoshinori Sato   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   Peter Griffin   sh: Add UBC trap ...
40
  #  define TRAP_UBC		12
6e80f5e8c   Yoshinori Sato   sh2(A) exception ...
41
  #  define TRAP_FPU_ERROR	13
0983b3184   Yoshinori Sato   sh: Wire up divis...
42
43
44
  #  define TRAP_DIVZERO_ERROR	17
  #  define TRAP_DIVOVF_ERROR	18
  # endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
46
47
48
  #else
  #define TRAP_RESERVED_INST	12
  #define TRAP_ILLEGAL_SLOT_INST	13
  #endif
6b0022305   Paul Mundt   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   Linus Torvalds   Linux-2.6.12-rc2
78

3a2e117e2   Paul Mundt   sh: Add die chain...
79
  static DEFINE_SPINLOCK(die_lock);
1da177e4c   Linus Torvalds   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   Paul Mundt   sh: oops_enter()/...
84
  	oops_enter();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
  	spin_lock_irq(&die_lock);
af67c3a9e   Paul Mundt   sh: update die() ...
86
  	console_verbose();
6b0022305   Paul Mundt   sh: Proper show_s...
87
  	bust_spinlocks(1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
89
  	printk("%s: %04lx [#%d]
  ", str, err & 0xffff, ++die_counter);
6b0022305   Paul Mundt   sh: Proper show_s...
90
  	print_modules();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
  	show_regs(regs);
6b0022305   Paul Mundt   sh: Proper show_s...
92

19c5870c0   Alexey Dobriyan   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   Paul Mundt   sh: Proper show_s...
96
97
98
  
  	if (!user_mode(regs) || in_interrupt())
  		dump_mem("Stack: ", regs->regs[15], THREAD_SIZE +
b5a1bcbee   Stuart Menefy   sh: Set up correc...
99
  			 (unsigned long)task_stack_page(current));
6b0022305   Paul Mundt   sh: Proper show_s...
100

c9306f0ef   Paul Mundt   sh: Wire up oops ...
101
  	notify_die(DIE_OOPS, str, regs, err, 255, SIGSEGV);
6b0022305   Paul Mundt   sh: Proper show_s...
102
  	bust_spinlocks(0);
bcdcd8e72   Pavel Emelianov   Report that kerne...
103
  	add_taint(TAINT_DIE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104
  	spin_unlock_irq(&die_lock);
af67c3a9e   Paul Mundt   sh: update die() ...
105
  	oops_exit();
e11327662   Paul Mundt   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   Linus Torvalds   Linux-2.6.12-rc2
115
116
  	do_exit(SIGSEGV);
  }
6b0022305   Paul Mundt   sh: Proper show_s...
117
118
  static inline void die_if_kernel(const char *str, struct pt_regs *regs,
  				 long err)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
119
120
121
122
  {
  	if (!user_mode(regs))
  		die(str, regs, err);
  }
1da177e4c   Linus Torvalds   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   Linus Torvalds   Linux-2.6.12-rc2
128
   */
2afb447f3   SUGIOKA Toshinobu   sh: fix unaligned...
129
  static void die_if_no_fixup(const char * str, struct pt_regs * regs, long err)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130
  {
6b0022305   Paul Mundt   sh: Proper show_s...
131
  	if (!user_mode(regs)) {
1da177e4c   Linus Torvalds   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   SUGIOKA Toshinobu   sh: fix unaligned...
136
  			return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
  		}
b344e24a8   Matt Fleming   sh: unwinder: Int...
138

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
139
140
  		die(str, regs, err);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
  }
86c0179c9   Magnus Damm   sh: break out una...
142
143
144
  static inline void sign_extend(unsigned int count, unsigned char *dst)
  {
  #ifdef __LITTLE_ENDIAN__
4252c659a   Magnus Damm   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   Magnus Damm   sh: break out una...
150
151
152
153
154
  	if ((count == 2) && dst[1] & 0x80) {
  		dst[2] = 0xff;
  		dst[3] = 0xff;
  	}
  #else
4252c659a   Magnus Damm   sh: add byte supp...
155
156
157
  	if ((count == 1) && dst[3] & 0x80) {
  		dst[2] = 0xff;
  		dst[1] = 0xff;
86c0179c9   Magnus Damm   sh: break out una...
158
  		dst[0] = 0xff;
4252c659a   Magnus Damm   sh: add byte supp...
159
160
  	}
  	if ((count == 2) && dst[2] & 0x80) {
86c0179c9   Magnus Damm   sh: break out una...
161
  		dst[1] = 0xff;
4252c659a   Magnus Damm   sh: add byte supp...
162
  		dst[0] = 0xff;
86c0179c9   Magnus Damm   sh: break out una...
163
164
165
  	}
  #endif
  }
e7cc9a734   Magnus Damm   sh: trapped io su...
166
167
168
169
  static struct mem_access user_mem_access = {
  	copy_from_user,
  	copy_to_user,
  };
1da177e4c   Linus Torvalds   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   Paul Mundt   sh: Rename opcode...
177
  static int handle_unaligned_ins(insn_size_t instruction, struct pt_regs *regs,
e7cc9a734   Magnus Damm   sh: trapped io su...
178
  				struct mem_access *ma)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
179
180
181
182
  {
  	int ret, index, count;
  	unsigned long *rm, *rn;
  	unsigned char *src, *dst;
fa43972fa   Paul Mundt   sh: fixup many sp...
183
  	unsigned char __user *srcu, *dstu;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
185
186
187
188
189
190
191
  
  	index = (instruction>>8)&15;	/* 0x0F00 */
  	rn = &regs->regs[index];
  
  	index = (instruction>>4)&15;	/* 0x00F0 */
  	rm = &regs->regs[index];
  
  	count = 1<<(instruction&3);
7436cde6b   Andre Draszik   sh: Allow user co...
192
  	switch (count) {
a99eae541   Paul Mundt   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   Andre Draszik   sh: Allow user co...
197
  	}
1da177e4c   Linus Torvalds   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   Paul Mundt   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   Linus Torvalds   Linux-2.6.12-rc2
207

86c0179c9   Magnus Damm   sh: break out una...
208
  #if !defined(__LITTLE_ENDIAN__)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
  			dst += 4-count;
86c0179c9   Magnus Damm   sh: break out una...
210
  #endif
fa43972fa   Paul Mundt   sh: fixup many sp...
211
  			if (ma->from(dst, srcu, count))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
212
  				goto fetch_fault;
86c0179c9   Magnus Damm   sh: break out una...
213
  			sign_extend(count, dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
215
  		} else {
  			/* to memory */
fa43972fa   Paul Mundt   sh: fixup many sp...
216
  			src = (unsigned char *)rm;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
218
219
  #if !defined(__LITTLE_ENDIAN__)
  			src += 4-count;
  #endif
fa43972fa   Paul Mundt   sh: fixup many sp...
220
221
  			dstu = (unsigned char __user *)*rn;
  			dstu += regs->regs[0];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222

fa43972fa   Paul Mundt   sh: fixup many sp...
223
  			if (ma->to(dstu, src, count))
1da177e4c   Linus Torvalds   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   Paul Mundt   sh: fixup many sp...
231
232
  		dstu = (unsigned char __user *)*rn;
  		dstu += (instruction&0x000F)<<2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233

fa43972fa   Paul Mundt   sh: fixup many sp...
234
  		if (ma->to(dstu, src, 4))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
235
236
  			goto fetch_fault;
  		ret = 0;
b5a1bcbee   Stuart Menefy   sh: Set up correc...
237
  		break;
1da177e4c   Linus Torvalds   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   Paul Mundt   sh: fixup many sp...
243
  		dstu = (unsigned char __user *)*rn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
245
246
  #if !defined(__LITTLE_ENDIAN__)
  		src += 4-count;
  #endif
fa43972fa   Paul Mundt   sh: fixup many sp...
247
  		if (ma->to(dstu, src, count))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
248
249
250
251
252
  			goto fetch_fault;
  		ret = 0;
  		break;
  
  	case 5: /* mov.l @(disp,Rm),Rn */
fa43972fa   Paul Mundt   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   Linus Torvalds   Linux-2.6.12-rc2
257

fa43972fa   Paul Mundt   sh: fixup many sp...
258
  		if (ma->from(dst, srcu, 4))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
260
  			goto fetch_fault;
  		ret = 0;
b5a1bcbee   Stuart Menefy   sh: Set up correc...
261
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
262
263
  
  	case 6:	/* mov.[bwl] from memory, possibly with post-increment */
fa43972fa   Paul Mundt   sh: fixup many sp...
264
  		srcu = (unsigned char __user *)*rm;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
266
267
268
  		if (instruction & 4)
  			*rm += count;
  		dst = (unsigned char*) rn;
  		*(unsigned long*)dst = 0;
b5a1bcbee   Stuart Menefy   sh: Set up correc...
269

86c0179c9   Magnus Damm   sh: break out una...
270
  #if !defined(__LITTLE_ENDIAN__)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271
  		dst += 4-count;
86c0179c9   Magnus Damm   sh: break out una...
272
  #endif
fa43972fa   Paul Mundt   sh: fixup many sp...
273
  		if (ma->from(dst, srcu, count))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274
  			goto fetch_fault;
86c0179c9   Magnus Damm   sh: break out una...
275
  		sign_extend(count, dst);
1da177e4c   Linus Torvalds   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   Paul Mundt   sh: fixup many sp...
282
  			src = (unsigned char *) &regs->regs[0];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283
284
285
  #if !defined(__LITTLE_ENDIAN__)
  			src += 2;
  #endif
fa43972fa   Paul Mundt   sh: fixup many sp...
286
287
  			dstu = (unsigned char __user *)*rm; /* called Rn in the spec */
  			dstu += (instruction & 0x000F) << 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288

fa43972fa   Paul Mundt   sh: fixup many sp...
289
  			if (ma->to(dstu, src, 2))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290
291
292
293
294
  				goto fetch_fault;
  			ret = 0;
  			break;
  
  		case 0x85: /* mov.w @(disp,Rm),R0 */
fa43972fa   Paul Mundt   sh: fixup many sp...
295
296
297
298
  			srcu = (unsigned char __user *)*rm;
  			srcu += (instruction & 0x000F) << 1;
  			dst = (unsigned char *) &regs->regs[0];
  			*(unsigned long *)dst = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
299
300
301
302
  
  #if !defined(__LITTLE_ENDIAN__)
  			dst += 2;
  #endif
fa43972fa   Paul Mundt   sh: fixup many sp...
303
  			if (ma->from(dst, srcu, 2))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
  				goto fetch_fault;
86c0179c9   Magnus Damm   sh: break out una...
305
  			sign_extend(2, dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
307
308
309
  			ret = 0;
  			break;
  		}
  		break;
34f7145a6   Phil Edworthy   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   Linus Torvalds   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   SUGIOKA Toshinobu   sh: fix unaligned...
346
347
  	die_if_no_fixup("Fault in unaligned fixup", regs, 0);
  	return -EFAULT;
1da177e4c   Linus Torvalds   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   Magnus Damm   sh: trapped io su...
354
  static inline int handle_delayslot(struct pt_regs *regs,
2bcfffa42   Paul Mundt   sh: Rename opcode...
355
  				   insn_size_t old_instruction,
e7cc9a734   Magnus Damm   sh: trapped io su...
356
  				   struct mem_access *ma)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
  {
2bcfffa42   Paul Mundt   sh: Rename opcode...
358
  	insn_size_t instruction;
fa43972fa   Paul Mundt   sh: fixup many sp...
359
360
  	void __user *addr = (void __user *)(regs->pc +
  		instruction_size(old_instruction));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361

4b5a9ef52   Magnus Damm   sh: use opcode_t ...
362
  	if (copy_from_user(&instruction, addr, sizeof(instruction))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
364
365
366
367
  		/* the instruction-fetch faulted */
  		if (user_mode(regs))
  			return -EFAULT;
  
  		/* kernel */
b5a1bcbee   Stuart Menefy   sh: Set up correc...
368
369
  		die("delay-slot-insn faulting in handle_unaligned_delayslot",
  		    regs, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
370
  	}
e7cc9a734   Magnus Damm   sh: trapped io su...
371
  	return handle_unaligned_ins(instruction, regs, ma);
1da177e4c   Linus Torvalds   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   Paul Mundt   sh: Rename opcode...
389
  int handle_unaligned_access(insn_size_t instruction, struct pt_regs *regs,
ace2dc7d1   Paul Mundt   sh: wire up perf ...
390
391
  			    struct mem_access *ma, int expected,
  			    unsigned long address)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
392
393
394
  {
  	u_int rm;
  	int ret, index;
23c4c8217   Paul Mundt   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   Linus Torvalds   Linux-2.6.12-rc2
400
401
  	index = (instruction>>8)&15;	/* 0x0F00 */
  	rm = regs->regs[index];
ace2dc7d1   Paul Mundt   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   Paul Mundt   sh: Split out the...
410
  		unaligned_fixups_notify(current, instruction, regs);
a8b0ca17b   Peter Zijlstra   perf: Remove the ...
411
  		perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1,
ace2dc7d1   Paul Mundt   sh: wire up perf ...
412
413
  			      regs, address);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
414
415
416
417
418
419
  
  	ret = -EFAULT;
  	switch (instruction&0xF000) {
  	case 0x0000:
  		if (instruction==0x000B) {
  			/* rts */
e7cc9a734   Magnus Damm   sh: trapped io su...
420
  			ret = handle_delayslot(regs, instruction, ma);
1da177e4c   Linus Torvalds   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   Magnus Damm   sh: trapped io su...
426
  			ret = handle_delayslot(regs, instruction, ma);
1da177e4c   Linus Torvalds   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   Magnus Damm   sh: trapped io su...
432
  			ret = handle_delayslot(regs, instruction, ma);
1da177e4c   Linus Torvalds   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   Magnus Damm   sh: trapped io su...
453
  			ret = handle_delayslot(regs, instruction, ma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454
455
456
457
458
  			if (ret==0)
  				regs->pc = rm;
  		}
  		else if ((instruction&0x00FF)==0x000B) {
  			/* jsr @Rm */
e7cc9a734   Magnus Damm   sh: trapped io su...
459
  			ret = handle_delayslot(regs, instruction, ma);
1da177e4c   Linus Torvalds   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   Phil Edworthy   sh: Fix unaligned...
484
  			ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
485
486
  			break;
  		case 0x0F00: /* bf/s lab */
e7cc9a734   Magnus Damm   sh: trapped io su...
487
  			ret = handle_delayslot(regs, instruction, ma);
1da177e4c   Linus Torvalds   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   Phil Edworthy   sh: Fix unaligned...
498
  			ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
499
500
  			break;
  		case 0x0D00: /* bt/s lab */
e7cc9a734   Magnus Damm   sh: trapped io su...
501
  			ret = handle_delayslot(regs, instruction, ma);
1da177e4c   Linus Torvalds   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   Phil Edworthy   sh: Add unaligned...
513
514
  	case 0x9000: /* mov.w @(disp,Rm),Rn */
  		goto simple;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
515
  	case 0xA000: /* bra label */
e7cc9a734   Magnus Damm   sh: trapped io su...
516
  		ret = handle_delayslot(regs, instruction, ma);
1da177e4c   Linus Torvalds   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   Magnus Damm   sh: trapped io su...
522
  		ret = handle_delayslot(regs, instruction, ma);
1da177e4c   Linus Torvalds   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   Phil Edworthy   sh: Add unaligned...
528
529
530
  
  	case 0xD000: /* mov.l @(disp,Rm),Rn */
  		goto simple;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
531
532
533
534
535
  	}
  	return ret;
  
  	/* handle non-delay-slot instruction */
   simple:
e7cc9a734   Magnus Damm   sh: trapped io su...
536
  	ret = handle_unaligned_ins(instruction, regs, ma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
537
  	if (ret==0)
53f983a90   Paul Mundt   sh: Fix PC adjust...
538
  		regs->pc += instruction_size(instruction);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
539
540
541
542
  	return ret;
  }
  
  /*
b5a1bcbee   Stuart Menefy   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   Simon Arlott   spelling fixes: a...
551
   * and data address errors caused by read accesses.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
552
   */
f0bc814cf   Stuart Menefy   sh: gcc4 support.
553
  asmlinkage void do_address_error(struct pt_regs *regs,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
554
555
556
  				 unsigned long writeaccess,
  				 unsigned long address)
  {
0983b3184   Yoshinori Sato   sh: Wire up divis...
557
  	unsigned long error_code = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
558
  	mm_segment_t oldfs;
b5a1bcbee   Stuart Menefy   sh: Set up correc...
559
  	siginfo_t info;
2bcfffa42   Paul Mundt   sh: Rename opcode...
560
  	insn_size_t instruction;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
561
  	int tmp;
0983b3184   Yoshinori Sato   sh: Wire up divis...
562
563
  	/* Intentional ifdef */
  #ifdef CONFIG_CPU_HAS_SR_RB
4c59e2942   Paul Mundt   sh: Move lookup_e...
564
  	error_code = lookup_exception_vector();
0983b3184   Yoshinori Sato   sh: Wire up divis...
565
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
566
567
568
569
  
  	oldfs = get_fs();
  
  	if (user_mode(regs)) {
b5a1bcbee   Stuart Menefy   sh: Set up correc...
570
  		int si_code = BUS_ADRERR;
a99eae541   Paul Mundt   sh: Split out the...
571
  		unsigned int user_action;
b5a1bcbee   Stuart Menefy   sh: Set up correc...
572

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
573
  		local_irq_enable();
a99eae541   Paul Mundt   sh: Split out the...
574
  		inc_unaligned_user_access();
7436cde6b   Andre Draszik   sh: Allow user co...
575

5a0ab35e4   Andre Draszik   sh: cleanup of do...
576
  		set_fs(USER_DS);
23c4c8217   Paul Mundt   sh: Handle unalig...
577
578
  		if (copy_from_user(&instruction, (insn_size_t *)(regs->pc & ~1),
  				   sizeof(instruction))) {
5a0ab35e4   Andre Draszik   sh: cleanup of do...
579
580
581
582
  			set_fs(oldfs);
  			goto uspace_segv;
  		}
  		set_fs(oldfs);
7436cde6b   Andre Draszik   sh: Allow user co...
583
  		/* shout about userspace fixups */
a99eae541   Paul Mundt   sh: Split out the...
584
  		unaligned_fixups_notify(current, instruction, regs);
7436cde6b   Andre Draszik   sh: Allow user co...
585

a99eae541   Paul Mundt   sh: Split out the...
586
587
  		user_action = unaligned_user_action();
  		if (user_action & UM_FIXUP)
7436cde6b   Andre Draszik   sh: Allow user co...
588
  			goto fixup;
a99eae541   Paul Mundt   sh: Split out the...
589
  		if (user_action & UM_SIGNAL)
7436cde6b   Andre Draszik   sh: Allow user co...
590
591
592
  			goto uspace_segv;
  		else {
  			/* ignore */
5a0ab35e4   Andre Draszik   sh: cleanup of do...
593
  			regs->pc += instruction_size(instruction);
7436cde6b   Andre Draszik   sh: Allow user co...
594
595
596
597
  			return;
  		}
  
  fixup:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
598
  		/* bad PC is not something we can fix */
b5a1bcbee   Stuart Menefy   sh: Set up correc...
599
600
  		if (regs->pc & 1) {
  			si_code = BUS_ADRALN;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
601
  			goto uspace_segv;
b5a1bcbee   Stuart Menefy   sh: Set up correc...
602
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
603
604
  
  		set_fs(USER_DS);
e7cc9a734   Magnus Damm   sh: trapped io su...
605
  		tmp = handle_unaligned_access(instruction, regs,
ace2dc7d1   Paul Mundt   sh: wire up perf ...
606
607
  					      &user_mem_access, 0,
  					      address);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
608
  		set_fs(oldfs);
a99eae541   Paul Mundt   sh: Split out the...
609
  		if (tmp == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
610
  			return; /* sorted */
b5a1bcbee   Stuart Menefy   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   Paul Mundt   sh: __user annota...
620
  		info.si_addr = (void __user *)address;
b5a1bcbee   Stuart Menefy   sh: Set up correc...
621
  		force_sig_info(SIGBUS, &info, current);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
622
  	} else {
a99eae541   Paul Mundt   sh: Split out the...
623
  		inc_unaligned_kernel_access();
7436cde6b   Andre Draszik   sh: Allow user co...
624

1da177e4c   Linus Torvalds   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   Paul Mundt   sh: fixup many sp...
629
  		if (copy_from_user(&instruction, (void __user *)(regs->pc),
4b5a9ef52   Magnus Damm   sh: use opcode_t ...
630
  				   sizeof(instruction))) {
1da177e4c   Linus Torvalds   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   Paul Mundt   sh: Split out the...
637
  		unaligned_fixups_notify(current, instruction, regs);
40258ee97   Paul Mundt   sh: Fix up uninit...
638

ace2dc7d1   Paul Mundt   sh: wire up perf ...
639
640
  		handle_unaligned_access(instruction, regs, &user_mem_access,
  					0, address);
1da177e4c   Linus Torvalds   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   Paul Mundt   sh: Shut up SH2-D...
651
  	unsigned short inst = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
652

f0bc814cf   Stuart Menefy   sh: gcc4 support.
653
  	/*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
654
655
656
  	 * Safe guard if DSP mode is already enabled or we're lacking
  	 * the DSP altogether.
  	 */
11c196568   Paul Mundt   sh: Fixup cpu_dat...
657
  	if (!(current_cpu_data.flags & CPU_HAS_DSP) || (regs->sr & SR_DSP))
1da177e4c   Linus Torvalds   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   Yoshinori Sato   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   Stuart Menefy   sh: gcc4 support.
676
  				struct pt_regs __regs)
0983b3184   Yoshinori Sato   sh: Wire up divis...
677
678
  {
  	siginfo_t info;
0983b3184   Yoshinori Sato   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   Takashi YOSHII   sh: math-emu support
691
692
  asmlinkage void do_reserved_inst(unsigned long r4, unsigned long r5,
  				unsigned long r6, unsigned long r7,
f0bc814cf   Stuart Menefy   sh: gcc4 support.
693
  				struct pt_regs __regs)
4b565680d   Takashi YOSHII   sh: math-emu support
694
  {
f0bc814cf   Stuart Menefy   sh: gcc4 support.
695
  	struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
4b565680d   Takashi YOSHII   sh: math-emu support
696
697
698
699
  	unsigned long error_code;
  	struct task_struct *tsk = current;
  
  #ifdef CONFIG_SH_FPU_EMU
0983b3184   Yoshinori Sato   sh: Wire up divis...
700
  	unsigned short inst = 0;
4b565680d   Takashi YOSHII   sh: math-emu support
701
  	int err;
f0bc814cf   Stuart Menefy   sh: gcc4 support.
702
  	get_user(inst, (unsigned short*)regs->pc);
4b565680d   Takashi YOSHII   sh: math-emu support
703

f0bc814cf   Stuart Menefy   sh: gcc4 support.
704
  	err = do_fpu_inst(inst, regs);
4b565680d   Takashi YOSHII   sh: math-emu support
705
  	if (!err) {
53f983a90   Paul Mundt   sh: Fix PC adjust...
706
  		regs->pc += instruction_size(inst);
4b565680d   Takashi YOSHII   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   Stuart Menefy   sh: Set up correc...
714
  	if (is_dsp_inst(regs)) {
4b565680d   Takashi YOSHII   sh: math-emu support
715
  		/* Enable DSP mode, and restart instruction. */
f0bc814cf   Stuart Menefy   sh: gcc4 support.
716
  		regs->sr |= SR_DSP;
01ab10393   Michael Trimarchi   sh: Fix up DSP co...
717
718
  		/* Save DSP mode */
  		tsk->thread.dsp_status.status |= SR_DSP;
4b565680d   Takashi YOSHII   sh: math-emu support
719
720
721
  		return;
  	}
  #endif
4c59e2942   Paul Mundt   sh: Move lookup_e...
722
  	error_code = lookup_exception_vector();
0983b3184   Yoshinori Sato   sh: Wire up divis...
723

4b565680d   Takashi YOSHII   sh: math-emu support
724
  	local_irq_enable();
4b565680d   Takashi YOSHII   sh: math-emu support
725
  	force_sig(SIGILL, tsk);
f0bc814cf   Stuart Menefy   sh: gcc4 support.
726
  	die_if_no_fixup("reserved instruction", regs, error_code);
4b565680d   Takashi YOSHII   sh: math-emu support
727
728
729
  }
  
  #ifdef CONFIG_SH_FPU_EMU
edfd6da04   Paul Mundt   sh: Add a few mor...
730
  static int emulate_branch(unsigned short inst, struct pt_regs *regs)
4b565680d   Takashi YOSHII   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   Paul Mundt   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   Takashi YOSHII   sh: math-emu support
749
750
751
  		regs->pc += SH_PC_8BIT_OFFSET(inst);
  		return 0;
  	}
edfd6da04   Paul Mundt   sh: Add a few mor...
752
  	if ((inst & 0xe000) == 0xa000) {	/* bra, bsr */
4b565680d   Takashi YOSHII   sh: math-emu support
753
754
755
  		regs->pc += SH_PC_12BIT_OFFSET(inst);
  		return 0;
  	}
edfd6da04   Paul Mundt   sh: Add a few mor...
756
  	if ((inst & 0xf0df) == 0x0003) {	/* braf, bsrf */
4b565680d   Takashi YOSHII   sh: math-emu support
757
758
759
  		regs->pc += regs->regs[(inst & 0x0f00) >> 8] + 4;
  		return 0;
  	}
edfd6da04   Paul Mundt   sh: Add a few mor...
760
  	if ((inst & 0xf0df) == 0x400b) {	/* jmp, jsr */
4b565680d   Takashi YOSHII   sh: math-emu support
761
762
763
  		regs->pc = regs->regs[(inst & 0x0f00) >> 8];
  		return 0;
  	}
edfd6da04   Paul Mundt   sh: Add a few mor...
764
  	if ((inst & 0xffff) == 0x000b) {	/* rts */
4b565680d   Takashi YOSHII   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   Stuart Menefy   sh: gcc4 support.
775
  				struct pt_regs __regs)
4b565680d   Takashi YOSHII   sh: math-emu support
776
  {
f0bc814cf   Stuart Menefy   sh: gcc4 support.
777
  	struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
b3d765f5d   Paul Mundt   sh: Fix up fpu em...
778
  	unsigned long inst;
4b565680d   Takashi YOSHII   sh: math-emu support
779
  	struct task_struct *tsk = current;
d39f54501   Chris Smith   sh: Add kprobes s...
780
781
782
  
  	if (kprobe_handle_illslot(regs->pc) == 0)
  		return;
4b565680d   Takashi YOSHII   sh: math-emu support
783
  #ifdef CONFIG_SH_FPU_EMU
f0bc814cf   Stuart Menefy   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   Takashi YOSHII   sh: math-emu support
788
789
790
791
792
  			return;
  		/* fault in branch.*/
  	}
  	/* not a FPU inst. */
  #endif
4c59e2942   Paul Mundt   sh: Move lookup_e...
793
  	inst = lookup_exception_vector();
0983b3184   Yoshinori Sato   sh: Wire up divis...
794

4b565680d   Takashi YOSHII   sh: math-emu support
795
  	local_irq_enable();
4b565680d   Takashi YOSHII   sh: math-emu support
796
  	force_sig(SIGILL, tsk);
b3d765f5d   Paul Mundt   sh: Fix up fpu em...
797
  	die_if_no_fixup("illegal slot instruction", regs, inst);
4b565680d   Takashi YOSHII   sh: math-emu support
798
  }
1da177e4c   Linus Torvalds   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   Stuart Menefy   sh: gcc4 support.
802
  				   struct pt_regs __regs)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
803
  {
f0bc814cf   Stuart Menefy   sh: gcc4 support.
804
  	struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
805
  	long ex;
0983b3184   Yoshinori Sato   sh: Wire up divis...
806

4c59e2942   Paul Mundt   sh: Move lookup_e...
807
  	ex = lookup_exception_vector();
f0bc814cf   Stuart Menefy   sh: gcc4 support.
808
  	die_if_kernel("exception", regs, ex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
809
  }
aba1030a7   Paul Mundt   sh: Bring SMP sup...
810
  void __cpuinit per_cpu_trap_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
811
812
  {
  	extern void *vbr_base;
1da177e4c   Linus Torvalds   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   Magnus Damm   sh: boot kernel w...
821
822
823
  
  	/* disable exception blocking now when the vbr has been setup */
  	clear_bl_bit();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
824
  }
1f666587d   Paul Mundt   sh: Fix exception...
825
  void *set_exception_table_vec(unsigned int vec, void *handler)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
826
827
  {
  	extern void *exception_handling_table[];
1f666587d   Paul Mundt   sh: Fix exception...
828
  	void *old_handler;
b5a1bcbee   Stuart Menefy   sh: Set up correc...
829

1f666587d   Paul Mundt   sh: Fix exception...
830
831
832
833
  	old_handler = exception_handling_table[vec];
  	exception_handling_table[vec] = handler;
  	return old_handler;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
834

1f666587d   Paul Mundt   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   Linus Torvalds   Linux-2.6.12-rc2
839

4b565680d   Takashi YOSHII   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   Paul Mundt   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   Paul Mundt   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   Linus Torvalds   Linux-2.6.12-rc2
852
  #endif
0983b3184   Yoshinori Sato   sh: Wire up divis...
853
854
  
  #ifdef CONFIG_CPU_SH2
5a4f7c66b   Paul Mundt   sh: Share bug/deb...
855
  	set_exception_table_vec(TRAP_ADDRESS_ERROR, address_error_trap_handler);
0983b3184   Yoshinori Sato   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   Yoshinori Sato   sh2(A) exception ...
860
861
862
  #ifdef CONFIG_SH_FPU
  	set_exception_table_vec(TRAP_FPU_ERROR, fpu_error_trap_handler);
  #endif
0983b3184   Yoshinori Sato   sh: Wire up divis...
863
  #endif
b5a1bcbee   Stuart Menefy   sh: Set up correc...
864

cd89436e5   Peter Griffin   sh: Add UBC trap ...
865
  #ifdef TRAP_UBC
c4761815a   Paul Mundt   sh: Fix up breakp...
866
  	set_exception_table_vec(TRAP_UBC, breakpoint_trap_handler);
cd89436e5   Peter Griffin   sh: Add UBC trap ...
867
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
868
  }
6b0022305   Paul Mundt   sh: Proper show_s...
869
  void show_stack(struct task_struct *tsk, unsigned long *sp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
870
  {
6b0022305   Paul Mundt   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   Linus Torvalds   Linux-2.6.12-rc2
884
885
886
887
888
889
890
  }
  
  void dump_stack(void)
  {
  	show_stack(NULL, NULL);
  }
  EXPORT_SYMBOL(dump_stack);