traps.c 19.9 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
/*
 *  linux/arch/arm/kernel/traps.c
 *
4
 *  Copyright (C) 1995-2009 Russell King
Linus Torvalds's avatar
Linus Torvalds committed
5
6
7
8
9
10
11
12
13
14
15
16
17
 *  Fragments that appear the same as linux/arch/i386/kernel/traps.c (C) Linus Torvalds
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 *  'traps.c' handles hardware exceptions after we have saved some state in
 *  'linux/arch/arm/lib/traps.S'.  Mostly a debugging aid, but will probably
 *  kill the offending process.
 */
#include <linux/signal.h>
#include <linux/personality.h>
#include <linux/kallsyms.h>
Russell King's avatar
Russell King committed
18
19
#include <linux/spinlock.h>
#include <linux/uaccess.h>
20
#include <linux/hardirq.h>
Russell King's avatar
Russell King committed
21
22
23
24
#include <linux/kdebug.h>
#include <linux/module.h>
#include <linux/kexec.h>
#include <linux/delay.h>
Linus Torvalds's avatar
Linus Torvalds committed
25
#include <linux/init.h>
26
#include <linux/sched.h>
Linus Torvalds's avatar
Linus Torvalds committed
27
28
29
30
31
32

#include <asm/atomic.h>
#include <asm/cacheflush.h>
#include <asm/system.h>
#include <asm/unistd.h>
#include <asm/traps.h>
33
#include <asm/unwind.h>
34
#include <asm/tls.h>
Linus Torvalds's avatar
Linus Torvalds committed
35

36
#include "signal.h"
Linus Torvalds's avatar
Linus Torvalds committed
37
38
39

static const char *handler[]= { "prefetch abort", "data abort", "address exception", "interrupt" };

40
41
void *vectors_page;

Linus Torvalds's avatar
Linus Torvalds committed
42
43
44
45
46
47
48
49
50
51
52
#ifdef CONFIG_DEBUG_USER
unsigned int user_debug;

static int __init user_debug_setup(char *str)
{
	get_option(&str, &user_debug);
	return 1;
}
__setup("user_debug=", user_debug_setup);
#endif

53
static void dump_mem(const char *, const char *, unsigned long, unsigned long);
54
55

void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame)
Linus Torvalds's avatar
Linus Torvalds committed
56
57
{
#ifdef CONFIG_KALLSYMS
58
	printk("[<%08lx>] (%pS) from [<%08lx>] (%pS)\n", where, (void *)where, from, (void *)from);
Linus Torvalds's avatar
Linus Torvalds committed
59
60
61
#else
	printk("Function entered at [<%08lx>] from [<%08lx>]\n", where, from);
#endif
62
63

	if (in_exception_text(where))
64
		dump_mem("", "Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs));
Linus Torvalds's avatar
Linus Torvalds committed
65
66
}

67
#ifndef CONFIG_ARM_UNWIND
Linus Torvalds's avatar
Linus Torvalds committed
68
69
70
71
72
73
74
/*
 * Stack pointers should always be within the kernels view of
 * physical memory.  If it is not there, then we can't dump
 * out any information relating to the stack.
 */
static int verify_stack(unsigned long sp)
{
75
76
	if (sp < PAGE_OFFSET ||
	    (sp > (unsigned long)high_memory && high_memory != NULL))
Linus Torvalds's avatar
Linus Torvalds committed
77
78
79
80
		return -EFAULT;

	return 0;
}
81
#endif
Linus Torvalds's avatar
Linus Torvalds committed
82
83
84
85

/*
 * Dump out the contents of some memory nicely...
 */
86
87
static void dump_mem(const char *lvl, const char *str, unsigned long bottom,
		     unsigned long top)
Linus Torvalds's avatar
Linus Torvalds committed
88
{
89
	unsigned long first;
Linus Torvalds's avatar
Linus Torvalds committed
90
91
92
93
94
95
96
97
98
99
100
	mm_segment_t fs;
	int i;

	/*
	 * We need to switch to kernel mode so that we can use __get_user
	 * to safely read from kernel space.  Note that we now dump the
	 * code first, just in case the backtrace kills us.
	 */
	fs = get_fs();
	set_fs(KERNEL_DS);

101
	printk("%s%s(0x%08lx to 0x%08lx)\n", lvl, str, bottom, top);
Linus Torvalds's avatar
Linus Torvalds committed
102

103
104
105
	for (first = bottom & ~31; first < top; first += 32) {
		unsigned long p;
		char str[sizeof(" 12345678") * 8 + 1];
Linus Torvalds's avatar
Linus Torvalds committed
106

107
108
		memset(str, ' ', sizeof(str));
		str[sizeof(str) - 1] = '\0';
Linus Torvalds's avatar
Linus Torvalds committed
109

110
111
112
113
114
115
116
		for (p = first, i = 0; i < 8 && p < top; i++, p += 4) {
			if (p >= bottom && p < top) {
				unsigned long val;
				if (__get_user(val, (unsigned long *)p) == 0)
					sprintf(str + i * 9, " %08lx", val);
				else
					sprintf(str + i * 9, " ????????");
Linus Torvalds's avatar
Linus Torvalds committed
117
118
			}
		}
119
		printk("%s%04lx:%s\n", lvl, first & 0xffff, str);
Linus Torvalds's avatar
Linus Torvalds committed
120
121
122
123
124
	}

	set_fs(fs);
}

125
static void dump_instr(const char *lvl, struct pt_regs *regs)
Linus Torvalds's avatar
Linus Torvalds committed
126
127
128
129
130
{
	unsigned long addr = instruction_pointer(regs);
	const int thumb = thumb_mode(regs);
	const int width = thumb ? 4 : 8;
	mm_segment_t fs;
131
	char str[sizeof("00000000 ") * 5 + 2 + 1], *p = str;
Linus Torvalds's avatar
Linus Torvalds committed
132
133
134
135
136
137
138
139
140
141
	int i;

	/*
	 * We need to switch to kernel mode so that we can use __get_user
	 * to safely read from kernel space.  Note that we now dump the
	 * code first, just in case the backtrace kills us.
	 */
	fs = get_fs();
	set_fs(KERNEL_DS);

142
	for (i = -4; i < 1 + !!thumb; i++) {
Linus Torvalds's avatar
Linus Torvalds committed
143
144
145
146
147
148
149
150
		unsigned int val, bad;

		if (thumb)
			bad = __get_user(val, &((u16 *)addr)[i]);
		else
			bad = __get_user(val, &((u32 *)addr)[i]);

		if (!bad)
151
152
			p += sprintf(p, i == 0 ? "(%0*x) " : "%0*x ",
					width, val);
Linus Torvalds's avatar
Linus Torvalds committed
153
		else {
154
			p += sprintf(p, "bad PC value");
Linus Torvalds's avatar
Linus Torvalds committed
155
156
157
			break;
		}
	}
158
	printk("%sCode: %s\n", lvl, str);
Linus Torvalds's avatar
Linus Torvalds committed
159
160
161
162

	set_fs(fs);
}

163
164
165
166
167
168
#ifdef CONFIG_ARM_UNWIND
static inline void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
{
	unwind_backtrace(regs, tsk);
}
#else
Linus Torvalds's avatar
Linus Torvalds committed
169
170
static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
{
171
	unsigned int fp, mode;
Linus Torvalds's avatar
Linus Torvalds committed
172
173
174
	int ok = 1;

	printk("Backtrace: ");
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189

	if (!tsk)
		tsk = current;

	if (regs) {
		fp = regs->ARM_fp;
		mode = processor_mode(regs);
	} else if (tsk != current) {
		fp = thread_saved_fp(tsk);
		mode = 0x10;
	} else {
		asm("mov %0, fp" : "=r" (fp) : : "cc");
		mode = 0x10;
	}

Linus Torvalds's avatar
Linus Torvalds committed
190
191
192
193
194
195
	if (!fp) {
		printk("no frame pointer");
		ok = 0;
	} else if (verify_stack(fp)) {
		printk("invalid frame pointer 0x%08x", fp);
		ok = 0;
Al Viro's avatar
Al Viro committed
196
	} else if (fp < (unsigned long)end_of_stack(tsk))
Linus Torvalds's avatar
Linus Torvalds committed
197
198
199
200
		printk("frame pointer underflow");
	printk("\n");

	if (ok)
201
		c_backtrace(fp, mode);
Linus Torvalds's avatar
Linus Torvalds committed
202
}
203
#endif
Linus Torvalds's avatar
Linus Torvalds committed
204
205
206

void dump_stack(void)
{
207
	dump_backtrace(NULL, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
208
209
210
211
212
213
}

EXPORT_SYMBOL(dump_stack);

void show_stack(struct task_struct *tsk, unsigned long *sp)
{
214
	dump_backtrace(NULL, tsk);
Linus Torvalds's avatar
Linus Torvalds committed
215
216
217
	barrier();
}

218
219
220
221
222
223
224
225
226
227
228
#ifdef CONFIG_PREEMPT
#define S_PREEMPT " PREEMPT"
#else
#define S_PREEMPT ""
#endif
#ifdef CONFIG_SMP
#define S_SMP " SMP"
#else
#define S_SMP ""
#endif

Russell King's avatar
Russell King committed
229
static int __die(const char *str, int err, struct thread_info *thread, struct pt_regs *regs)
Linus Torvalds's avatar
Linus Torvalds committed
230
{
Russell King's avatar
Russell King committed
231
	struct task_struct *tsk = thread->task;
Linus Torvalds's avatar
Linus Torvalds committed
232
	static int die_counter;
Russell King's avatar
Russell King committed
233
	int ret;
Linus Torvalds's avatar
Linus Torvalds committed
234

235
	printk(KERN_EMERG "Internal error: %s: %x [#%d]" S_PREEMPT S_SMP "\n",
236
	       str, err, ++die_counter);
Russell King's avatar
Russell King committed
237
238
239
240
241
242

	/* trap and error numbers are mostly meaningless on ARM */
	ret = notify_die(DIE_OOPS, str, regs, err, tsk->thread.trap_no, SIGSEGV);
	if (ret == NOTIFY_STOP)
		return ret;

Linus Torvalds's avatar
Linus Torvalds committed
243
	print_modules();
Russell King's avatar
Russell King committed
244
	__show_regs(regs);
245
246
	printk(KERN_EMERG "Process %.*s (pid: %d, stack limit = 0x%p)\n",
		TASK_COMM_LEN, tsk->comm, task_pid_nr(tsk), thread + 1);
Linus Torvalds's avatar
Linus Torvalds committed
247
248

	if (!user_mode(regs) || in_interrupt()) {
249
		dump_mem(KERN_EMERG, "Stack: ", regs->ARM_sp,
Al Viro's avatar
Al Viro committed
250
			 THREAD_SIZE + (unsigned long)task_stack_page(tsk));
Linus Torvalds's avatar
Linus Torvalds committed
251
		dump_backtrace(regs, tsk);
252
		dump_instr(KERN_EMERG, regs);
Linus Torvalds's avatar
Linus Torvalds committed
253
	}
Russell King's avatar
Russell King committed
254
255

	return ret;
Russell King's avatar
Russell King committed
256
}
Linus Torvalds's avatar
Linus Torvalds committed
257

258
static DEFINE_SPINLOCK(die_lock);
Russell King's avatar
Russell King committed
259
260
261
262

/*
 * This function is protected against re-entrancy.
 */
Russell King's avatar
Russell King committed
263
void die(const char *str, struct pt_regs *regs, int err)
Russell King's avatar
Russell King committed
264
265
{
	struct thread_info *thread = current_thread_info();
Russell King's avatar
Russell King committed
266
	int ret;
Russell King's avatar
Russell King committed
267

268
269
	oops_enter();

Russell King's avatar
Russell King committed
270
	spin_lock_irq(&die_lock);
Russell King's avatar
Russell King committed
271
	console_verbose();
Russell King's avatar
Russell King committed
272
	bust_spinlocks(1);
Russell King's avatar
Russell King committed
273
274
275
276
277
	ret = __die(str, err, thread, regs);

	if (regs && kexec_should_crash(thread->task))
		crash_kexec(regs);

Linus Torvalds's avatar
Linus Torvalds committed
278
	bust_spinlocks(0);
279
	add_taint(TAINT_DIE);
Linus Torvalds's avatar
Linus Torvalds committed
280
	spin_unlock_irq(&die_lock);
Russell King's avatar
Russell King committed
281
	oops_exit();
Russell King's avatar
Russell King committed
282

283
284
	if (in_interrupt())
		panic("Fatal exception in interrupt");
Horms's avatar
Horms committed
285
	if (panic_on_oops)
286
		panic("Fatal exception");
Russell King's avatar
Russell King committed
287
288
	if (ret != NOTIFY_STOP)
		do_exit(SIGSEGV);
Linus Torvalds's avatar
Linus Torvalds committed
289
290
}

291
292
void arm_notify_die(const char *str, struct pt_regs *regs,
		struct siginfo *info, unsigned long err, unsigned long trap)
Linus Torvalds's avatar
Linus Torvalds committed
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
{
	if (user_mode(regs)) {
		current->thread.error_code = err;
		current->thread.trap_no = trap;

		force_sig_info(info->si_signo, info, current);
	} else {
		die(str, regs, err);
	}
}

static LIST_HEAD(undef_hook);
static DEFINE_SPINLOCK(undef_lock);

void register_undef_hook(struct undef_hook *hook)
{
309
310
311
	unsigned long flags;

	spin_lock_irqsave(&undef_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
312
	list_add(&hook->node, &undef_hook);
313
	spin_unlock_irqrestore(&undef_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
314
315
316
317
}

void unregister_undef_hook(struct undef_hook *hook)
{
318
319
320
	unsigned long flags;

	spin_lock_irqsave(&undef_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
321
	list_del(&hook->node);
322
	spin_unlock_irqrestore(&undef_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
323
324
}

325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
static int call_undef_hook(struct pt_regs *regs, unsigned int instr)
{
	struct undef_hook *hook;
	unsigned long flags;
	int (*fn)(struct pt_regs *regs, unsigned int instr) = NULL;

	spin_lock_irqsave(&undef_lock, flags);
	list_for_each_entry(hook, &undef_hook, node)
		if ((instr & hook->instr_mask) == hook->instr_val &&
		    (regs->ARM_cpsr & hook->cpsr_mask) == hook->cpsr_val)
			fn = hook->fn;
	spin_unlock_irqrestore(&undef_lock, flags);

	return fn ? fn(regs, instr) : 1;
}

341
asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
Linus Torvalds's avatar
Linus Torvalds committed
342
343
344
345
346
347
348
349
350
351
352
353
354
355
{
	unsigned int correction = thumb_mode(regs) ? 2 : 4;
	unsigned int instr;
	siginfo_t info;
	void __user *pc;

	/*
	 * According to the ARM ARM, PC is 2 or 4 bytes ahead,
	 * depending whether we're in Thumb mode or not.
	 * Correct this offset.
	 */
	regs->ARM_pc -= correction;

	pc = (void __user *)instruction_pointer(regs);
356
357

	if (processor_mode(regs) == SVC_MODE) {
358
359
360
361
362
363
364
365
366
367
#ifdef CONFIG_THUMB2_KERNEL
		if (thumb_mode(regs)) {
			instr = ((u16 *)pc)[0];
			if (is_wide_instruction(instr)) {
				instr <<= 16;
				instr |= ((u16 *)pc)[1];
			}
		} else
#endif
			instr = *(u32 *) pc;
368
	} else if (thumb_mode(regs)) {
Linus Torvalds's avatar
Linus Torvalds committed
369
		get_user(instr, (u16 __user *)pc);
370
371
372
373
374
375
		if (is_wide_instruction(instr)) {
			unsigned int instr2;
			get_user(instr2, (u16 __user *)pc+1);
			instr <<= 16;
			instr |= instr2;
		}
Linus Torvalds's avatar
Linus Torvalds committed
376
377
378
379
	} else {
		get_user(instr, (u32 __user *)pc);
	}

380
381
	if (call_undef_hook(regs, instr) == 0)
		return;
Linus Torvalds's avatar
Linus Torvalds committed
382
383
384
385

#ifdef CONFIG_DEBUG_USER
	if (user_debug & UDBG_UNDEFINED) {
		printk(KERN_INFO "%s (%d): undefined instruction: pc=%p\n",
386
			current->comm, task_pid_nr(current), pc);
387
		dump_instr(KERN_INFO, regs);
Linus Torvalds's avatar
Linus Torvalds committed
388
389
390
391
392
393
394
395
	}
#endif

	info.si_signo = SIGILL;
	info.si_errno = 0;
	info.si_code  = ILL_ILLOPC;
	info.si_addr  = pc;

396
	arm_notify_die("Oops - undefined instruction", regs, &info, 0, 6);
Linus Torvalds's avatar
Linus Torvalds committed
397
398
399
400
401
402
403
404
405
406
407
408
409
410
}

asmlinkage void do_unexp_fiq (struct pt_regs *regs)
{
	printk("Hmm.  Unexpected FIQ received, but trying to continue\n");
	printk("You may have a hardware problem...\n");
}

/*
 * bad_mode handles the impossible case in the vectors.  If you see one of
 * these, then it's extremely serious, and could mean you have buggy hardware.
 * It never returns, and never tries to sync.  We hope that we can at least
 * dump out some state information...
 */
411
asmlinkage void bad_mode(struct pt_regs *regs, int reason)
Linus Torvalds's avatar
Linus Torvalds committed
412
413
414
{
	console_verbose();

415
	printk(KERN_CRIT "Bad mode in %s handler detected\n", handler[reason]);
Linus Torvalds's avatar
Linus Torvalds committed
416
417
418
419
420
421
422
423
424
425
426

	die("Oops - bad mode", regs, 0);
	local_irq_disable();
	panic("bad mode");
}

static int bad_syscall(int n, struct pt_regs *regs)
{
	struct thread_info *thread = current_thread_info();
	siginfo_t info;

427
	if ((current->personality & PER_MASK) != PER_LINUX &&
428
	    thread->exec_domain->handler) {
Linus Torvalds's avatar
Linus Torvalds committed
429
430
431
432
433
434
435
		thread->exec_domain->handler(n, regs);
		return regs->ARM_r0;
	}

#ifdef CONFIG_DEBUG_USER
	if (user_debug & UDBG_SYSCALL) {
		printk(KERN_ERR "[%d] %s: obsolete system call %08x.\n",
436
			task_pid_nr(current), current->comm, n);
437
		dump_instr(KERN_ERR, regs);
Linus Torvalds's avatar
Linus Torvalds committed
438
439
440
441
442
443
444
445
446
	}
#endif

	info.si_signo = SIGILL;
	info.si_errno = 0;
	info.si_code  = ILL_ILLTRP;
	info.si_addr  = (void __user *)instruction_pointer(regs) -
			 (thumb_mode(regs) ? 2 : 4);

447
	arm_notify_die("Oops - bad syscall", regs, &info, n, 0);
Linus Torvalds's avatar
Linus Torvalds committed
448
449
450
451
452
453
454

	return regs->ARM_r0;
}

static inline void
do_cache_op(unsigned long start, unsigned long end, int flags)
{
455
	struct mm_struct *mm = current->active_mm;
Linus Torvalds's avatar
Linus Torvalds committed
456
457
458
459
460
	struct vm_area_struct *vma;

	if (end < start || flags)
		return;

461
462
	down_read(&mm->mmap_sem);
	vma = find_vma(mm, start);
Linus Torvalds's avatar
Linus Torvalds committed
463
464
465
466
467
468
469
470
	if (vma && vma->vm_start < end) {
		if (start < vma->vm_start)
			start = vma->vm_start;
		if (end > vma->vm_end)
			end = vma->vm_end;

		flush_cache_user_range(vma, start, end);
	}
471
	up_read(&mm->mmap_sem);
Linus Torvalds's avatar
Linus Torvalds committed
472
473
474
475
476
477
478
479
480
481
482
483
}

/*
 * Handle all unrecognised system calls.
 *  0x9f0000 - 0x9fffff are some more esoteric system calls
 */
#define NR(x) ((__ARM_NR_##x) - __ARM_NR_BASE)
asmlinkage int arm_syscall(int no, struct pt_regs *regs)
{
	struct thread_info *thread = current_thread_info();
	siginfo_t info;

484
	if ((no >> 16) != (__ARM_NR_BASE>> 16))
Linus Torvalds's avatar
Linus Torvalds committed
485
486
487
488
489
490
491
492
493
		return bad_syscall(no, regs);

	switch (no & 0xffff) {
	case 0: /* branch through 0 */
		info.si_signo = SIGSEGV;
		info.si_errno = 0;
		info.si_code  = SEGV_MAPERR;
		info.si_addr  = NULL;

494
		arm_notify_die("branch through zero", regs, &info, 0, 0);
Linus Torvalds's avatar
Linus Torvalds committed
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
		return 0;

	case NR(breakpoint): /* SWI BREAK_POINT */
		regs->ARM_pc -= thumb_mode(regs) ? 2 : 4;
		ptrace_break(current, regs);
		return regs->ARM_r0;

	/*
	 * Flush a region from virtual address 'r0' to virtual address 'r1'
	 * _exclusive_.  There is no alignment requirement on either address;
	 * user space does not need to know the hardware cache layout.
	 *
	 * r2 contains flags.  It should ALWAYS be passed as ZERO until it
	 * is defined to be something else.  For now we ignore it, but may
	 * the fires of hell burn in your belly if you break this rule. ;)
	 *
	 * (at a later date, we may want to allow this call to not flush
	 * various aspects of the cache.  Passing '0' will guarantee that
	 * everything necessary gets flushed to maintain consistency in
	 * the specified region).
	 */
	case NR(cacheflush):
		do_cache_op(regs->ARM_r0, regs->ARM_r1, regs->ARM_r2);
		return 0;

	case NR(usr26):
		if (!(elf_hwcap & HWCAP_26BIT))
			break;
		regs->ARM_cpsr &= ~MODE32_BIT;
		return regs->ARM_r0;

	case NR(usr32):
		if (!(elf_hwcap & HWCAP_26BIT))
			break;
		regs->ARM_cpsr |= MODE32_BIT;
		return regs->ARM_r0;

	case NR(set_tls):
		thread->tp_value = regs->ARM_r0;
534
535
536
537
538
539
540
541
542
543
544
545
546
547
		if (tls_emu)
			return 0;
		if (has_tls_reg) {
			asm ("mcr p15, 0, %0, c13, c0, 3"
				: : "r" (regs->ARM_r0));
		} else {
			/*
			 * User space must never try to access this directly.
			 * Expect your app to break eventually if you do so.
			 * The user helper at 0xffff0fe0 must be used instead.
			 * (see entry-armv.S for details)
			 */
			*((unsigned int *)0xffff0ff0) = regs->ARM_r0;
		}
Linus Torvalds's avatar
Linus Torvalds committed
548
549
		return 0;

550
551
552
553
554
555
556
557
558
559
560
561
#ifdef CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG
	/*
	 * Atomically store r1 in *r2 if *r2 is equal to r0 for user space.
	 * Return zero in r0 if *MEM was changed or non-zero if no exchange
	 * happened.  Also set the user C flag accordingly.
	 * If access permissions have to be fixed up then non-zero is
	 * returned and the operation has to be re-attempted.
	 *
	 * *NOTE*: This is a ghost syscall private to the kernel.  Only the
	 * __kuser_cmpxchg code in entry-armv.S should be aware of its
	 * existence.  Don't ever use this from user code.
	 */
562
	case NR(cmpxchg):
563
	for (;;) {
564
565
566
567
568
569
		extern void do_DataAbort(unsigned long addr, unsigned int fsr,
					 struct pt_regs *regs);
		unsigned long val;
		unsigned long addr = regs->ARM_r2;
		struct mm_struct *mm = current->mm;
		pgd_t *pgd; pmd_t *pmd; pte_t *pte;
570
		spinlock_t *ptl;
571
572

		regs->ARM_cpsr &= ~PSR_C_BIT;
573
		down_read(&mm->mmap_sem);
574
575
576
577
578
579
		pgd = pgd_offset(mm, addr);
		if (!pgd_present(*pgd))
			goto bad_access;
		pmd = pmd_offset(pgd, addr);
		if (!pmd_present(*pmd))
			goto bad_access;
580
		pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
581
		if (!pte_present(*pte) || !pte_write(*pte) || !pte_dirty(*pte)) {
582
			pte_unmap_unlock(pte, ptl);
583
			goto bad_access;
584
		}
585
586
587
588
589
590
		val = *(unsigned long *)addr;
		val -= regs->ARM_r0;
		if (val == 0) {
			*(unsigned long *)addr = regs->ARM_r1;
			regs->ARM_cpsr |= PSR_C_BIT;
		}
591
592
		pte_unmap_unlock(pte, ptl);
		up_read(&mm->mmap_sem);
593
594
595
		return val;

		bad_access:
596
		up_read(&mm->mmap_sem);
597
		/* simulate a write access fault */
598
599
600
601
		do_DataAbort(addr, 15 + (1 << 11), regs);
	}
#endif

Linus Torvalds's avatar
Linus Torvalds committed
602
603
604
605
606
	default:
		/* Calls 9f00xx..9f07ff are defined to return -ENOSYS
		   if not implemented, rather than raising SIGILL.  This
		   way the calling program can gracefully determine whether
		   a feature is supported.  */
607
		if ((no & 0xffff) <= 0x7ff)
Linus Torvalds's avatar
Linus Torvalds committed
608
609
610
611
612
613
614
615
616
617
			return -ENOSYS;
		break;
	}
#ifdef CONFIG_DEBUG_USER
	/*
	 * experience shows that these seem to indicate that
	 * something catastrophic has happened
	 */
	if (user_debug & UDBG_SYSCALL) {
		printk("[%d] %s: arm syscall %d\n",
618
		       task_pid_nr(current), current->comm, no);
619
		dump_instr("", regs);
Linus Torvalds's avatar
Linus Torvalds committed
620
		if (user_mode(regs)) {
Russell King's avatar
Russell King committed
621
			__show_regs(regs);
Linus Torvalds's avatar
Linus Torvalds committed
622
623
624
625
626
627
628
629
630
631
			c_backtrace(regs->ARM_fp, processor_mode(regs));
		}
	}
#endif
	info.si_signo = SIGILL;
	info.si_errno = 0;
	info.si_code  = ILL_ILLTRP;
	info.si_addr  = (void __user *)instruction_pointer(regs) -
			 (thumb_mode(regs) ? 2 : 4);

632
	arm_notify_die("Oops - bad syscall(2)", regs, &info, no, 0);
Linus Torvalds's avatar
Linus Torvalds committed
633
634
635
	return 0;
}

636
#ifdef CONFIG_TLS_REG_EMUL
637
638
639

/*
 * We might be running on an ARMv6+ processor which should have the TLS
640
641
642
643
 * register but for some reason we can't use it, or maybe an SMP system
 * using a pre-ARMv6 processor (there are apparently a few prototypes like
 * that in existence) and therefore access to that register must be
 * emulated.
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
 */

static int get_tp_trap(struct pt_regs *regs, unsigned int instr)
{
	int reg = (instr >> 12) & 15;
	if (reg == 15)
		return 1;
	regs->uregs[reg] = current_thread_info()->tp_value;
	regs->ARM_pc += 4;
	return 0;
}

static struct undef_hook arm_mrc_hook = {
	.instr_mask	= 0x0fff0fff,
	.instr_val	= 0x0e1d0f70,
	.cpsr_mask	= PSR_T_BIT,
	.cpsr_val	= 0,
	.fn		= get_tp_trap,
};

static int __init arm_mrc_hook_init(void)
{
	register_undef_hook(&arm_mrc_hook);
	return 0;
}

late_initcall(arm_mrc_hook_init);

#endif

Linus Torvalds's avatar
Linus Torvalds committed
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
void __bad_xchg(volatile void *ptr, int size)
{
	printk("xchg: bad data size: pc 0x%p, ptr 0x%p, size %d\n",
		__builtin_return_address(0), ptr, size);
	BUG();
}
EXPORT_SYMBOL(__bad_xchg);

/*
 * A data abort trap was taken, but we did not handle the instruction.
 * Try to abort the user program, or panic if it was the kernel.
 */
asmlinkage void
baddataabort(int code, unsigned long instr, struct pt_regs *regs)
{
	unsigned long addr = instruction_pointer(regs);
	siginfo_t info;

#ifdef CONFIG_DEBUG_USER
	if (user_debug & UDBG_BADABORT) {
		printk(KERN_ERR "[%d] %s: bad data abort: code %d instr 0x%08lx\n",
695
			task_pid_nr(current), current->comm, code, instr);
696
		dump_instr(KERN_ERR, regs);
Linus Torvalds's avatar
Linus Torvalds committed
697
698
699
700
701
702
703
704
705
		show_pte(current->mm, addr);
	}
#endif

	info.si_signo = SIGILL;
	info.si_errno = 0;
	info.si_code  = ILL_ILLOPC;
	info.si_addr  = (void __user *)addr;

706
	arm_notify_die("unknown data abort code", regs, &info, instr, 0);
Linus Torvalds's avatar
Linus Torvalds committed
707
708
}

709
void __attribute__((noreturn)) __bug(const char *file, int line)
Linus Torvalds's avatar
Linus Torvalds committed
710
{
711
	printk(KERN_CRIT"kernel BUG at %s:%d!\n", file, line);
Linus Torvalds's avatar
Linus Torvalds committed
712
	*(int *)0 = 0;
713
714
715

	/* Avoid "noreturn function does return" */
	for (;;);
Linus Torvalds's avatar
Linus Torvalds committed
716
717
718
719
720
721
722
723
724
725
}
EXPORT_SYMBOL(__bug);

void __readwrite_bug(const char *fn)
{
	printk("%s called, but not implemented\n", fn);
	BUG();
}
EXPORT_SYMBOL(__readwrite_bug);

726
void __pte_error(const char *file, int line, pte_t pte)
Linus Torvalds's avatar
Linus Torvalds committed
727
{
728
	printk("%s:%d: bad pte %08llx.\n", file, line, (long long)pte_val(pte));
Linus Torvalds's avatar
Linus Torvalds committed
729
730
}

731
void __pmd_error(const char *file, int line, pmd_t pmd)
Linus Torvalds's avatar
Linus Torvalds committed
732
{
733
	printk("%s:%d: bad pmd %08llx.\n", file, line, (long long)pmd_val(pmd));
Linus Torvalds's avatar
Linus Torvalds committed
734
735
}

736
void __pgd_error(const char *file, int line, pgd_t pgd)
Linus Torvalds's avatar
Linus Torvalds committed
737
{
738
	printk("%s:%d: bad pgd %08llx.\n", file, line, (long long)pgd_val(pgd));
Linus Torvalds's avatar
Linus Torvalds committed
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
}

asmlinkage void __div0(void)
{
	printk("Division by zero in kernel.\n");
	dump_stack();
}
EXPORT_SYMBOL(__div0);

void abort(void)
{
	BUG();

	/* if that doesn't kill us, halt */
	panic("Oops failed to kill thread");
}
EXPORT_SYMBOL(abort);

void __init trap_init(void)
Jason Wessel's avatar
Jason Wessel committed
758
759
760
761
{
	return;
}

762
763
764
765
766
767
768
769
770
771
static void __init kuser_get_tls_init(unsigned long vectors)
{
	/*
	 * vectors + 0xfe0 = __kuser_get_tls
	 * vectors + 0xfe8 = hardware TLS instruction at 0xffff0fe8
	 */
	if (tls_emu || has_tls_reg)
		memcpy((void *)vectors + 0xfe0, (void *)vectors + 0xfe8, 4);
}

Jason Wessel's avatar
Jason Wessel committed
772
void __init early_trap_init(void)
Linus Torvalds's avatar
Linus Torvalds committed
773
{
774
#if defined(CONFIG_CPU_USE_DOMAINS)
775
	unsigned long vectors = CONFIG_VECTORS_BASE;
776
777
778
#else
	unsigned long vectors = (unsigned long)vectors_page;
#endif
779
780
	extern char __stubs_start[], __stubs_end[];
	extern char __vectors_start[], __vectors_end[];
781
782
	extern char __kuser_helper_start[], __kuser_helper_end[];
	int kuser_sz = __kuser_helper_end - __kuser_helper_start;
Linus Torvalds's avatar
Linus Torvalds committed
783

784
	/*
785
786
787
	 * Copy the vectors, stubs and kuser helpers (in entry-armv.S)
	 * into the vector page, mapped at 0xffff0000, and ensure these
	 * are visible to the instruction stream.
788
	 */
789
790
791
	memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
	memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
	memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);
792

793
794
795
796
797
	/*
	 * Do processor specific fixups for the kuser helpers
	 */
	kuser_get_tls_init(vectors);

798
799
800
801
	/*
	 * Copy signal return handlers into the vector page, and
	 * set sigreturn to be a pointer to these.
	 */
802
803
804
805
	memcpy((void *)(vectors + KERN_SIGRETURN_CODE - CONFIG_VECTORS_BASE),
	       sigreturn_codes, sizeof(sigreturn_codes));
	memcpy((void *)(vectors + KERN_RESTART_CODE - CONFIG_VECTORS_BASE),
	       syscall_restart_code, sizeof(syscall_restart_code));
806

807
	flush_icache_range(vectors, vectors + PAGE_SIZE);
Linus Torvalds's avatar
Linus Torvalds committed
808
809
	modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
}