Blame view

arch/avr32/mm/fault.c 5.7 KB
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
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>
1eeb66a1b   Christoph Hellwig   move die notifier...
15
  #include <linux/kdebug.h>
9caebec7b   Christoph Hellwig   [AVR32] optimize ...
16
  #include <linux/kprobes.h>
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
17
18
  #include <asm/mmu_context.h>
  #include <asm/sysreg.h>
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
19
  #include <asm/tlb.h>
623b0355d   Haavard Skinnemoen   [AVR32] Clean up ...
20
  #include <asm/uaccess.h>
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
21
22
  
  #ifdef CONFIG_KPROBES
9caebec7b   Christoph Hellwig   [AVR32] optimize ...
23
  static inline int notify_page_fault(struct pt_regs *regs, int trap)
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
24
  {
9caebec7b   Christoph Hellwig   [AVR32] optimize ...
25
  	int ret = 0;
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
26

9caebec7b   Christoph Hellwig   [AVR32] optimize ...
27
28
29
30
  	if (!user_mode(regs)) {
  		if (kprobe_running() && kprobe_fault_handler(regs, trap))
  			ret = 1;
  	}
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
31

9caebec7b   Christoph Hellwig   [AVR32] optimize ...
32
  	return ret;
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
33
34
  }
  #else
9caebec7b   Christoph Hellwig   [AVR32] optimize ...
35
  static inline int notify_page_fault(struct pt_regs *regs, int trap)
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
36
  {
9caebec7b   Christoph Hellwig   [AVR32] optimize ...
37
  	return 0;
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
38
39
  }
  #endif
623b0355d   Haavard Skinnemoen   [AVR32] Clean up ...
40
  int exception_trace = 1;
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
41
42
43
44
45
  /*
   * 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:
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
46
   *   6:  Protection fault (instruction access)
623b0355d   Haavard Skinnemoen   [AVR32] Clean up ...
47
48
49
50
51
   *   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)
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
52
53
54
55
56
57
58
59
60
   */
  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;
623b0355d   Haavard Skinnemoen   [AVR32] Clean up ...
61
62
63
  	int writeaccess;
  	long signr;
  	int code;
83c54070e   Nick Piggin   mm: fault feedbac...
64
  	int fault;
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
65

9caebec7b   Christoph Hellwig   [AVR32] optimize ...
66
  	if (notify_page_fault(regs, ecr))
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
67
68
69
70
71
72
  		return;
  
  	address = sysreg_read(TLBEAR);
  
  	tsk = current;
  	mm = tsk->mm;
623b0355d   Haavard Skinnemoen   [AVR32] Clean up ...
73
74
  	signr = SIGSEGV;
  	code = SEGV_MAPERR;
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
  	/*
  	 * 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();
  
  	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:
623b0355d   Haavard Skinnemoen   [AVR32] Clean up ...
101
102
  	code = SEGV_ACCERR;
  	writeaccess = 0;
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
  	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;
  		writeaccess = 1;
  		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.
  	 */
d06063cc2   Linus Torvalds   Move FAULT_FLAG_x...
129
  	fault = handle_mm_fault(mm, vma, address, writeaccess ? FAULT_FLAG_WRITE : 0);
83c54070e   Nick Piggin   mm: fault feedbac...
130
131
132
133
134
  	if (unlikely(fault & VM_FAULT_ERROR)) {
  		if (fault & VM_FAULT_OOM)
  			goto out_of_memory;
  		else if (fault & VM_FAULT_SIGBUS)
  			goto do_sigbus;
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
135
136
  		BUG();
  	}
83c54070e   Nick Piggin   mm: fault feedbac...
137
138
139
140
  	if (fault & VM_FAULT_MAJOR)
  		tsk->maj_flt++;
  	else
  		tsk->min_flt++;
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
141
142
143
144
145
146
147
148
149
  
  	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:
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
150
151
152
  	up_read(&mm->mmap_sem);
  
  	if (user_mode(regs)) {
126187f1e   Andrea Righi   [AVR32] ratelimit...
153
  		if (exception_trace && printk_ratelimit())
623b0355d   Haavard Skinnemoen   [AVR32] Clean up ...
154
155
156
  			printk("%s%s[%d]: segfault at %08lx pc %08lx "
  			       "sp %08lx ecr %lu
  ",
b460cbc58   Serge E. Hallyn   pid namespaces: d...
157
  			       is_global_init(tsk) ? KERN_EMERG : KERN_INFO,
623b0355d   Haavard Skinnemoen   [AVR32] Clean up ...
158
159
160
  			       tsk->comm, tsk->pid, address, regs->pc,
  			       regs->sp, ecr);
  		_exception(SIGSEGV, regs, code, address);
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
161
162
163
164
  		return;
  	}
  
  no_context:
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
165
166
167
168
  	/* Are we prepared to handle this kernel fault? */
  	fixup = search_exception_tables(regs->pc);
  	if (fixup) {
  		regs->pc = fixup->fixup;
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
  		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
  ", address);
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
184
185
186
  
  	page = sysreg_read(PTBR);
  	printk(KERN_ALERT "ptbr = %08lx", page);
32019828d   Haavard Skinnemoen   avr32: Fix broken...
187
188
  	if (address >= TASK_SIZE)
  		page = (unsigned long)swapper_pg_dir;
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
189
190
191
192
193
194
195
  	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];
623b0355d   Haavard Skinnemoen   [AVR32] Clean up ...
196
  			printk(" pte = %08lx", page);
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
197
198
  		}
  	}
623b0355d   Haavard Skinnemoen   [AVR32] Clean up ...
199
200
201
202
  	printk("
  ");
  	die("Kernel access of bad area", regs, signr);
  	return;
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
203
204
205
206
207
208
  
  	/*
  	 * 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:
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
209
  	up_read(&mm->mmap_sem);
67a8a20fe   Nick Piggin   avr32: invoke oom...
210
211
212
213
  	pagefault_out_of_memory();
  	if (!user_mode(regs))
  		goto no_context;
  	return;
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
214
215
216
  
  do_sigbus:
  	up_read(&mm->mmap_sem);
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
217
  	/* Kernel mode? Handle exceptions or die */
623b0355d   Haavard Skinnemoen   [AVR32] Clean up ...
218
219
  	signr = SIGBUS;
  	code = BUS_ADRERR;
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
220
221
  	if (!user_mode(regs))
  		goto no_context;
623b0355d   Haavard Skinnemoen   [AVR32] Clean up ...
222
223
224
225
226
  
  	if (exception_trace)
  		printk("%s%s[%d]: bus error at %08lx pc %08lx "
  		       "sp %08lx ecr %lu
  ",
b460cbc58   Serge E. Hallyn   pid namespaces: d...
227
  		       is_global_init(tsk) ? KERN_EMERG : KERN_INFO,
623b0355d   Haavard Skinnemoen   [AVR32] Clean up ...
228
229
230
231
  		       tsk->comm, tsk->pid, address, regs->pc,
  		       regs->sp, ecr);
  
  	_exception(SIGBUS, regs, BUS_ADRERR, address);
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
232
233
234
235
236
237
238
239
240
241
242
243
  }
  
  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)
  ",
  	       addr, write_access ? "write" : "read");
  	printk(KERN_INFO "DTLB dump:
  ");
  	dump_dtlb();
623b0355d   Haavard Skinnemoen   [AVR32] Clean up ...
244
  	die("Bus Error", regs, SIGKILL);
5f97f7f94   Haavard Skinnemoen   [PATCH] avr32 arc...
245
  }