Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/avr32/mm/fault.c
10817 views
1
/*
2
* Copyright (C) 2004-2006 Atmel Corporation
3
*
4
* Based on linux/arch/sh/mm/fault.c:
5
* Copyright (C) 1999 Niibe Yutaka
6
*
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License version 2 as
9
* published by the Free Software Foundation.
10
*/
11
12
#include <linux/mm.h>
13
#include <linux/module.h>
14
#include <linux/pagemap.h>
15
#include <linux/kdebug.h>
16
#include <linux/kprobes.h>
17
18
#include <asm/mmu_context.h>
19
#include <asm/sysreg.h>
20
#include <asm/tlb.h>
21
#include <asm/uaccess.h>
22
23
#ifdef CONFIG_KPROBES
24
static inline int notify_page_fault(struct pt_regs *regs, int trap)
25
{
26
int ret = 0;
27
28
if (!user_mode(regs)) {
29
if (kprobe_running() && kprobe_fault_handler(regs, trap))
30
ret = 1;
31
}
32
33
return ret;
34
}
35
#else
36
static inline int notify_page_fault(struct pt_regs *regs, int trap)
37
{
38
return 0;
39
}
40
#endif
41
42
int exception_trace = 1;
43
44
/*
45
* This routine handles page faults. It determines the address and the
46
* problem, and then passes it off to one of the appropriate routines.
47
*
48
* ecr is the Exception Cause Register. Possible values are:
49
* 6: Protection fault (instruction access)
50
* 15: Protection fault (read access)
51
* 16: Protection fault (write access)
52
* 20: Page not found (instruction access)
53
* 24: Page not found (read access)
54
* 28: Page not found (write access)
55
*/
56
asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)
57
{
58
struct task_struct *tsk;
59
struct mm_struct *mm;
60
struct vm_area_struct *vma;
61
const struct exception_table_entry *fixup;
62
unsigned long address;
63
unsigned long page;
64
int writeaccess;
65
long signr;
66
int code;
67
int fault;
68
69
if (notify_page_fault(regs, ecr))
70
return;
71
72
address = sysreg_read(TLBEAR);
73
74
tsk = current;
75
mm = tsk->mm;
76
77
signr = SIGSEGV;
78
code = SEGV_MAPERR;
79
80
/*
81
* If we're in an interrupt or have no user context, we must
82
* not take the fault...
83
*/
84
if (in_atomic() || !mm || regs->sr & SYSREG_BIT(GM))
85
goto no_context;
86
87
local_irq_enable();
88
89
down_read(&mm->mmap_sem);
90
91
vma = find_vma(mm, address);
92
if (!vma)
93
goto bad_area;
94
if (vma->vm_start <= address)
95
goto good_area;
96
if (!(vma->vm_flags & VM_GROWSDOWN))
97
goto bad_area;
98
if (expand_stack(vma, address))
99
goto bad_area;
100
101
/*
102
* Ok, we have a good vm_area for this memory access, so we
103
* can handle it...
104
*/
105
good_area:
106
code = SEGV_ACCERR;
107
writeaccess = 0;
108
109
switch (ecr) {
110
case ECR_PROTECTION_X:
111
case ECR_TLB_MISS_X:
112
if (!(vma->vm_flags & VM_EXEC))
113
goto bad_area;
114
break;
115
case ECR_PROTECTION_R:
116
case ECR_TLB_MISS_R:
117
if (!(vma->vm_flags & (VM_READ | VM_WRITE | VM_EXEC)))
118
goto bad_area;
119
break;
120
case ECR_PROTECTION_W:
121
case ECR_TLB_MISS_W:
122
if (!(vma->vm_flags & VM_WRITE))
123
goto bad_area;
124
writeaccess = 1;
125
break;
126
default:
127
panic("Unhandled case %lu in do_page_fault!", ecr);
128
}
129
130
/*
131
* If for any reason at all we couldn't handle the fault, make
132
* sure we exit gracefully rather than endlessly redo the
133
* fault.
134
*/
135
fault = handle_mm_fault(mm, vma, address, writeaccess ? FAULT_FLAG_WRITE : 0);
136
if (unlikely(fault & VM_FAULT_ERROR)) {
137
if (fault & VM_FAULT_OOM)
138
goto out_of_memory;
139
else if (fault & VM_FAULT_SIGBUS)
140
goto do_sigbus;
141
BUG();
142
}
143
if (fault & VM_FAULT_MAJOR)
144
tsk->maj_flt++;
145
else
146
tsk->min_flt++;
147
148
up_read(&mm->mmap_sem);
149
return;
150
151
/*
152
* Something tried to access memory that isn't in our memory
153
* map. Fix it, but check if it's kernel or user first...
154
*/
155
bad_area:
156
up_read(&mm->mmap_sem);
157
158
if (user_mode(regs)) {
159
if (exception_trace && printk_ratelimit())
160
printk("%s%s[%d]: segfault at %08lx pc %08lx "
161
"sp %08lx ecr %lu\n",
162
is_global_init(tsk) ? KERN_EMERG : KERN_INFO,
163
tsk->comm, tsk->pid, address, regs->pc,
164
regs->sp, ecr);
165
_exception(SIGSEGV, regs, code, address);
166
return;
167
}
168
169
no_context:
170
/* Are we prepared to handle this kernel fault? */
171
fixup = search_exception_tables(regs->pc);
172
if (fixup) {
173
regs->pc = fixup->fixup;
174
return;
175
}
176
177
/*
178
* Oops. The kernel tried to access some bad page. We'll have
179
* to terminate things with extreme prejudice.
180
*/
181
if (address < PAGE_SIZE)
182
printk(KERN_ALERT
183
"Unable to handle kernel NULL pointer dereference");
184
else
185
printk(KERN_ALERT
186
"Unable to handle kernel paging request");
187
printk(" at virtual address %08lx\n", address);
188
189
page = sysreg_read(PTBR);
190
printk(KERN_ALERT "ptbr = %08lx", page);
191
if (address >= TASK_SIZE)
192
page = (unsigned long)swapper_pg_dir;
193
if (page) {
194
page = ((unsigned long *)page)[address >> 22];
195
printk(" pgd = %08lx", page);
196
if (page & _PAGE_PRESENT) {
197
page &= PAGE_MASK;
198
address &= 0x003ff000;
199
page = ((unsigned long *)__va(page))[address >> PAGE_SHIFT];
200
printk(" pte = %08lx", page);
201
}
202
}
203
printk("\n");
204
die("Kernel access of bad area", regs, signr);
205
return;
206
207
/*
208
* We ran out of memory, or some other thing happened to us
209
* that made us unable to handle the page fault gracefully.
210
*/
211
out_of_memory:
212
up_read(&mm->mmap_sem);
213
pagefault_out_of_memory();
214
if (!user_mode(regs))
215
goto no_context;
216
return;
217
218
do_sigbus:
219
up_read(&mm->mmap_sem);
220
221
/* Kernel mode? Handle exceptions or die */
222
signr = SIGBUS;
223
code = BUS_ADRERR;
224
if (!user_mode(regs))
225
goto no_context;
226
227
if (exception_trace)
228
printk("%s%s[%d]: bus error at %08lx pc %08lx "
229
"sp %08lx ecr %lu\n",
230
is_global_init(tsk) ? KERN_EMERG : KERN_INFO,
231
tsk->comm, tsk->pid, address, regs->pc,
232
regs->sp, ecr);
233
234
_exception(SIGBUS, regs, BUS_ADRERR, address);
235
}
236
237
asmlinkage void do_bus_error(unsigned long addr, int write_access,
238
struct pt_regs *regs)
239
{
240
printk(KERN_ALERT
241
"Bus error at physical address 0x%08lx (%s access)\n",
242
addr, write_access ? "write" : "read");
243
printk(KERN_INFO "DTLB dump:\n");
244
dump_dtlb();
245
die("Bus Error", regs, SIGKILL);
246
}
247
248