fault.c 6.2 KB
Newer Older
H
Haavard Skinnemoen 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*
 * Copyright (C) 2004-2006 Atmel Corporation
 *
 * Based on linux/arch/sh/mm/fault.c:
 *   Copyright (C) 1999  Niibe Yutaka
 *
 * 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.
 */

#include <linux/mm.h>
#include <linux/module.h>
#include <linux/pagemap.h>
15
#include <linux/kdebug.h>
16 17
#include <linux/kprobes.h>

H
Haavard Skinnemoen 已提交
18 19 20
#include <asm/mmu_context.h>
#include <asm/sysreg.h>
#include <asm/tlb.h>
21
#include <asm/uaccess.h>
H
Haavard Skinnemoen 已提交
22 23

#ifdef CONFIG_KPROBES
24
static inline int notify_page_fault(struct pt_regs *regs, int trap)
H
Haavard Skinnemoen 已提交
25
{
26
	int ret = 0;
H
Haavard Skinnemoen 已提交
27

28 29 30 31
	if (!user_mode(regs)) {
		if (kprobe_running() && kprobe_fault_handler(regs, trap))
			ret = 1;
	}
H
Haavard Skinnemoen 已提交
32

33
	return ret;
H
Haavard Skinnemoen 已提交
34 35
}
#else
36
static inline int notify_page_fault(struct pt_regs *regs, int trap)
H
Haavard Skinnemoen 已提交
37
{
38
	return 0;
H
Haavard Skinnemoen 已提交
39 40 41
}
#endif

42 43
int exception_trace = 1;

H
Haavard Skinnemoen 已提交
44 45 46 47 48 49
/*
 * This routine handles page faults. It determines the address and the
 * problem, and then passes it off to one of the appropriate routines.
 *
 * ecr is the Exception Cause Register. Possible values are:
 *   6:  Protection fault (instruction access)
50 51 52 53 54
 *   15: Protection fault (read access)
 *   16: Protection fault (write access)
 *   20: Page not found (instruction access)
 *   24: Page not found (read access)
 *   28: Page not found (write access)
H
Haavard Skinnemoen 已提交
55 56 57 58 59 60 61 62 63
 */
asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)
{
	struct task_struct *tsk;
	struct mm_struct *mm;
	struct vm_area_struct *vma;
	const struct exception_table_entry *fixup;
	unsigned long address;
	unsigned long page;
64 65
	long signr;
	int code;
N
Nick Piggin 已提交
66
	int fault;
67
	unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
H
Haavard Skinnemoen 已提交
68

69
	if (notify_page_fault(regs, ecr))
H
Haavard Skinnemoen 已提交
70 71 72 73 74 75 76
		return;

	address = sysreg_read(TLBEAR);

	tsk = current;
	mm = tsk->mm;

77 78 79
	signr = SIGSEGV;
	code = SEGV_MAPERR;

H
Haavard Skinnemoen 已提交
80 81 82 83 84 85 86 87 88
	/*
	 * If we're in an interrupt or have no user context, we must
	 * not take the fault...
	 */
	if (in_atomic() || !mm || regs->sr & SYSREG_BIT(GM))
		goto no_context;

	local_irq_enable();

89 90
	if (user_mode(regs))
		flags |= FAULT_FLAG_USER;
91
retry:
H
Haavard Skinnemoen 已提交
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
	down_read(&mm->mmap_sem);

	vma = find_vma(mm, address);
	if (!vma)
		goto bad_area;
	if (vma->vm_start <= address)
		goto good_area;
	if (!(vma->vm_flags & VM_GROWSDOWN))
		goto bad_area;
	if (expand_stack(vma, address))
		goto bad_area;

	/*
	 * Ok, we have a good vm_area for this memory access, so we
	 * can handle it...
	 */
good_area:
109 110
	code = SEGV_ACCERR;

H
Haavard Skinnemoen 已提交
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
	switch (ecr) {
	case ECR_PROTECTION_X:
	case ECR_TLB_MISS_X:
		if (!(vma->vm_flags & VM_EXEC))
			goto bad_area;
		break;
	case ECR_PROTECTION_R:
	case ECR_TLB_MISS_R:
		if (!(vma->vm_flags & (VM_READ | VM_WRITE | VM_EXEC)))
			goto bad_area;
		break;
	case ECR_PROTECTION_W:
	case ECR_TLB_MISS_W:
		if (!(vma->vm_flags & VM_WRITE))
			goto bad_area;
126
		flags |= FAULT_FLAG_WRITE;
H
Haavard Skinnemoen 已提交
127 128 129 130 131 132 133 134 135 136
		break;
	default:
		panic("Unhandled case %lu in do_page_fault!", ecr);
	}

	/*
	 * If for any reason at all we couldn't handle the fault, make
	 * sure we exit gracefully rather than endlessly redo the
	 * fault.
	 */
137 138 139 140 141
	fault = handle_mm_fault(mm, vma, address, flags);

	if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current))
		return;

N
Nick Piggin 已提交
142 143 144 145 146
	if (unlikely(fault & VM_FAULT_ERROR)) {
		if (fault & VM_FAULT_OOM)
			goto out_of_memory;
		else if (fault & VM_FAULT_SIGBUS)
			goto do_sigbus;
H
Haavard Skinnemoen 已提交
147 148
		BUG();
	}
149 150 151 152 153 154 155 156

	if (flags & FAULT_FLAG_ALLOW_RETRY) {
		if (fault & VM_FAULT_MAJOR)
			tsk->maj_flt++;
		else
			tsk->min_flt++;
		if (fault & VM_FAULT_RETRY) {
			flags &= ~FAULT_FLAG_ALLOW_RETRY;
157
			flags |= FAULT_FLAG_TRIED;
158 159 160 161 162 163 164 165 166

			/*
			 * No need to up_read(&mm->mmap_sem) as we would have
			 * already released it in __lock_page_or_retry() in
			 * mm/filemap.c.
			 */
			goto retry;
		}
	}
H
Haavard Skinnemoen 已提交
167 168 169 170 171 172 173 174 175 176 177 178

	up_read(&mm->mmap_sem);
	return;

	/*
	 * Something tried to access memory that isn't in our memory
	 * map. Fix it, but check if it's kernel or user first...
	 */
bad_area:
	up_read(&mm->mmap_sem);

	if (user_mode(regs)) {
179
		if (exception_trace && printk_ratelimit())
180 181
			printk("%s%s[%d]: segfault at %08lx pc %08lx "
			       "sp %08lx ecr %lu\n",
182
			       is_global_init(tsk) ? KERN_EMERG : KERN_INFO,
183 184 185
			       tsk->comm, tsk->pid, address, regs->pc,
			       regs->sp, ecr);
		_exception(SIGSEGV, regs, code, address);
H
Haavard Skinnemoen 已提交
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
		return;
	}

no_context:
	/* Are we prepared to handle this kernel fault? */
	fixup = search_exception_tables(regs->pc);
	if (fixup) {
		regs->pc = fixup->fixup;
		return;
	}

	/*
	 * Oops. The kernel tried to access some bad page. We'll have
	 * to terminate things with extreme prejudice.
	 */
	if (address < PAGE_SIZE)
		printk(KERN_ALERT
		       "Unable to handle kernel NULL pointer dereference");
	else
		printk(KERN_ALERT
		       "Unable to handle kernel paging request");
	printk(" at virtual address %08lx\n", address);

	page = sysreg_read(PTBR);
	printk(KERN_ALERT "ptbr = %08lx", page);
211 212
	if (address >= TASK_SIZE)
		page = (unsigned long)swapper_pg_dir;
H
Haavard Skinnemoen 已提交
213 214 215 216 217 218 219
	if (page) {
		page = ((unsigned long *)page)[address >> 22];
		printk(" pgd = %08lx", page);
		if (page & _PAGE_PRESENT) {
			page &= PAGE_MASK;
			address &= 0x003ff000;
			page = ((unsigned long *)__va(page))[address >> PAGE_SHIFT];
220
			printk(" pte = %08lx", page);
H
Haavard Skinnemoen 已提交
221 222
		}
	}
223 224 225
	printk("\n");
	die("Kernel access of bad area", regs, signr);
	return;
H
Haavard Skinnemoen 已提交
226 227 228 229 230 231 232

	/*
	 * We ran out of memory, or some other thing happened to us
	 * that made us unable to handle the page fault gracefully.
	 */
out_of_memory:
	up_read(&mm->mmap_sem);
233 234
	if (!user_mode(regs))
		goto no_context;
235
	pagefault_out_of_memory();
236
	return;
H
Haavard Skinnemoen 已提交
237 238 239 240 241

do_sigbus:
	up_read(&mm->mmap_sem);

	/* Kernel mode? Handle exceptions or die */
242 243
	signr = SIGBUS;
	code = BUS_ADRERR;
H
Haavard Skinnemoen 已提交
244 245
	if (!user_mode(regs))
		goto no_context;
246 247 248 249

	if (exception_trace)
		printk("%s%s[%d]: bus error at %08lx pc %08lx "
		       "sp %08lx ecr %lu\n",
250
		       is_global_init(tsk) ? KERN_EMERG : KERN_INFO,
251 252 253 254
		       tsk->comm, tsk->pid, address, regs->pc,
		       regs->sp, ecr);

	_exception(SIGBUS, regs, BUS_ADRERR, address);
H
Haavard Skinnemoen 已提交
255 256 257 258 259 260 261 262 263 264
}

asmlinkage void do_bus_error(unsigned long addr, int write_access,
			     struct pt_regs *regs)
{
	printk(KERN_ALERT
	       "Bus error at physical address 0x%08lx (%s access)\n",
	       addr, write_access ? "write" : "read");
	printk(KERN_INFO "DTLB dump:\n");
	dump_dtlb();
265
	die("Bus Error", regs, SIGKILL);
H
Haavard Skinnemoen 已提交
266
}