• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/arch/frv/mm/
1/*
2 *  linux/arch/frv/mm/fault.c
3 *
4 * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
5 * - Written by David Howells (dhowells@redhat.com)
6 * - Derived from arch/m68knommu/mm/fault.c
7 *   - Copyright (C) 1998  D. Jeff Dionne <jeff@lineo.ca>,
8 *   - Copyright (C) 2000  Lineo, Inc.  (www.lineo.com)
9 *
10 *  Based on:
11 *
12 *  linux/arch/m68k/mm/fault.c
13 *
14 *  Copyright (C) 1995  Hamish Macdonald
15 */
16
17#include <linux/mman.h>
18#include <linux/mm.h>
19#include <linux/kernel.h>
20#include <linux/ptrace.h>
21#include <linux/hardirq.h>
22
23#include <asm/system.h>
24#include <asm/pgtable.h>
25#include <asm/uaccess.h>
26#include <asm/gdb-stub.h>
27
28/*****************************************************************************/
29/*
30 * This routine handles page faults.  It determines the problem, and
31 * then passes it off to one of the appropriate routines.
32 */
33asmlinkage void do_page_fault(int datammu, unsigned long esr0, unsigned long ear0)
34{
35	struct vm_area_struct *vma;
36	struct mm_struct *mm;
37	unsigned long _pme, lrai, lrad, fixup;
38	siginfo_t info;
39	pgd_t *pge;
40	pud_t *pue;
41	pte_t *pte;
42	int write;
43	int fault;
44
45
46	mm = current->mm;
47
48	/*
49	 * We fault-in kernel-space virtual memory on-demand. The
50	 * 'reference' page table is init_mm.pgd.
51	 *
52	 * NOTE! We MUST NOT take any locks for this case. We may
53	 * be in an interrupt or a critical region, and should
54	 * only copy the information from the master page table,
55	 * nothing more.
56	 *
57	 * This verifies that the fault happens in kernel space
58	 * and that the fault was a page not present (invalid) error
59	 */
60	if (!user_mode(__frame) && (esr0 & ESR0_ATXC) == ESR0_ATXC_AMRTLB_MISS) {
61		if (ear0 >= VMALLOC_START && ear0 < VMALLOC_END)
62			goto kernel_pte_fault;
63		if (ear0 >= PKMAP_BASE && ear0 < PKMAP_END)
64			goto kernel_pte_fault;
65	}
66
67	info.si_code = SEGV_MAPERR;
68
69	/*
70	 * If we're in an interrupt or have no user
71	 * context, we must not take the fault..
72	 */
73	if (in_atomic() || !mm)
74		goto no_context;
75
76	down_read(&mm->mmap_sem);
77
78	vma = find_vma(mm, ear0);
79	if (!vma)
80		goto bad_area;
81	if (vma->vm_start <= ear0)
82		goto good_area;
83	if (!(vma->vm_flags & VM_GROWSDOWN))
84		goto bad_area;
85
86	if (user_mode(__frame)) {
87		/*
88		 * accessing the stack below %esp is always a bug.
89		 * The "+ 32" is there due to some instructions (like
90		 * pusha) doing post-decrement on the stack and that
91		 * doesn't show up until later..
92		 */
93		if ((ear0 & PAGE_MASK) + 2 * PAGE_SIZE < __frame->sp) {
94			goto bad_area;
95		}
96	}
97
98	if (expand_stack(vma, ear0))
99		goto bad_area;
100
101/*
102 * Ok, we have a good vm_area for this memory access, so
103 * we can handle it..
104 */
105 good_area:
106	info.si_code = SEGV_ACCERR;
107	write = 0;
108	switch (esr0 & ESR0_ATXC) {
109	default:
110		/* handle write to write protected page */
111	case ESR0_ATXC_WP_EXCEP:
112#ifdef TEST_VERIFY_AREA
113		if (!(user_mode(__frame)))
114			printk("WP fault at %08lx\n", __frame->pc);
115#endif
116		if (!(vma->vm_flags & VM_WRITE))
117			goto bad_area;
118		write = 1;
119		break;
120
121		 /* handle read from protected page */
122	case ESR0_ATXC_PRIV_EXCEP:
123		goto bad_area;
124
125		 /* handle read, write or exec on absent page
126		  * - can't support write without permitting read
127		  * - don't support execute without permitting read and vice-versa
128		  */
129	case ESR0_ATXC_AMRTLB_MISS:
130		if (!(vma->vm_flags & (VM_READ | VM_WRITE | VM_EXEC)))
131			goto bad_area;
132		break;
133	}
134
135	/*
136	 * If for any reason at all we couldn't handle the fault,
137	 * make sure we exit gracefully rather than endlessly redo
138	 * the fault.
139	 */
140	fault = handle_mm_fault(mm, vma, ear0, write ? FAULT_FLAG_WRITE : 0);
141	if (unlikely(fault & VM_FAULT_ERROR)) {
142		if (fault & VM_FAULT_OOM)
143			goto out_of_memory;
144		else if (fault & VM_FAULT_SIGBUS)
145			goto do_sigbus;
146		BUG();
147	}
148	if (fault & VM_FAULT_MAJOR)
149		current->maj_flt++;
150	else
151		current->min_flt++;
152
153	up_read(&mm->mmap_sem);
154	return;
155
156/*
157 * Something tried to access memory that isn't in our memory map..
158 * Fix it, but check if it's kernel or user first..
159 */
160 bad_area:
161	up_read(&mm->mmap_sem);
162
163	/* User mode accesses just cause a SIGSEGV */
164	if (user_mode(__frame)) {
165		info.si_signo = SIGSEGV;
166		info.si_errno = 0;
167		/* info.si_code has been set above */
168		info.si_addr = (void *) ear0;
169		force_sig_info(SIGSEGV, &info, current);
170		return;
171	}
172
173 no_context:
174	/* are we prepared to handle this kernel fault? */
175	if ((fixup = search_exception_table(__frame->pc)) != 0) {
176		__frame->pc = fixup;
177		return;
178	}
179
180/*
181 * Oops. The kernel tried to access some bad page. We'll have to
182 * terminate things with extreme prejudice.
183 */
184
185	bust_spinlocks(1);
186
187	if (ear0 < PAGE_SIZE)
188		printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
189	else
190		printk(KERN_ALERT "Unable to handle kernel paging request");
191	printk(" at virtual addr %08lx\n", ear0);
192	printk("  PC  : %08lx\n", __frame->pc);
193	printk("  EXC : esr0=%08lx ear0=%08lx\n", esr0, ear0);
194
195	asm("lrai %1,%0,#1,#0,#0" : "=&r"(lrai) : "r"(ear0));
196	asm("lrad %1,%0,#1,#0,#0" : "=&r"(lrad) : "r"(ear0));
197
198	printk(KERN_ALERT "  LRAI: %08lx\n", lrai);
199	printk(KERN_ALERT "  LRAD: %08lx\n", lrad);
200
201	__break_hijack_kernel_event();
202
203	pge = pgd_offset(current->mm, ear0);
204	pue = pud_offset(pge, ear0);
205	_pme = pue->pue[0].ste[0];
206
207	printk(KERN_ALERT "  PGE : %8p { PME %08lx }\n", pge, _pme);
208
209	if (_pme & xAMPRx_V) {
210		unsigned long dampr, damlr, val;
211
212		asm volatile("movsg dampr2,%0 ! movgs %2,dampr2 ! movsg damlr2,%1"
213			     : "=&r"(dampr), "=r"(damlr)
214			     : "r" (_pme | xAMPRx_L|xAMPRx_SS_16Kb|xAMPRx_S|xAMPRx_C|xAMPRx_V)
215			     );
216
217		pte = (pte_t *) damlr + __pte_index(ear0);
218		val = pte_val(*pte);
219
220		asm volatile("movgs %0,dampr2" :: "r" (dampr));
221
222		printk(KERN_ALERT "  PTE : %8p { %08lx }\n", pte, val);
223	}
224
225	die_if_kernel("Oops\n");
226	do_exit(SIGKILL);
227
228/*
229 * We ran out of memory, or some other thing happened to us that made
230 * us unable to handle the page fault gracefully.
231 */
232 out_of_memory:
233	up_read(&mm->mmap_sem);
234	if (!user_mode(__frame))
235		goto no_context;
236	pagefault_out_of_memory();
237	return;
238
239 do_sigbus:
240	up_read(&mm->mmap_sem);
241
242	/*
243	 * Send a sigbus, regardless of whether we were in kernel
244	 * or user mode.
245	 */
246	info.si_signo = SIGBUS;
247	info.si_errno = 0;
248	info.si_code = BUS_ADRERR;
249	info.si_addr = (void *) ear0;
250	force_sig_info(SIGBUS, &info, current);
251
252	/* Kernel mode? Handle exceptions or die */
253	if (!user_mode(__frame))
254		goto no_context;
255	return;
256
257/*
258 * The fault was caused by a kernel PTE (such as installed by vmalloc or kmap)
259 */
260 kernel_pte_fault:
261	{
262		/*
263		 * Synchronize this task's top level page-table
264		 * with the 'reference' page table.
265		 *
266		 * Do _not_ use "tsk" here. We might be inside
267		 * an interrupt in the middle of a task switch..
268		 */
269		int index = pgd_index(ear0);
270		pgd_t *pgd, *pgd_k;
271		pud_t *pud, *pud_k;
272		pmd_t *pmd, *pmd_k;
273		pte_t *pte_k;
274
275		pgd = (pgd_t *) __get_TTBR();
276		pgd = (pgd_t *)__va(pgd) + index;
277		pgd_k = ((pgd_t *)(init_mm.pgd)) + index;
278
279		if (!pgd_present(*pgd_k))
280			goto no_context;
281		//set_pgd(pgd, *pgd_k); /////// gcc ICE's on this line
282
283		pud_k = pud_offset(pgd_k, ear0);
284		if (!pud_present(*pud_k))
285			goto no_context;
286
287		pmd_k = pmd_offset(pud_k, ear0);
288		if (!pmd_present(*pmd_k))
289			goto no_context;
290
291		pud = pud_offset(pgd, ear0);
292		pmd = pmd_offset(pud, ear0);
293		set_pmd(pmd, *pmd_k);
294
295		pte_k = pte_offset_kernel(pmd_k, ear0);
296		if (!pte_present(*pte_k))
297			goto no_context;
298		return;
299	}
300} /* end do_page_fault() */
301