• 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/xtensa/mm/
1
2/*
3 * arch/xtensa/mm/fault.c
4 *
5 * This file is subject to the terms and conditions of the GNU General Public
6 * License.  See the file "COPYING" in the main directory of this archive
7 * for more details.
8 *
9 * Copyright (C) 2001 - 2005 Tensilica Inc.
10 *
11 * Chris Zankel <chris@zankel.net>
12 * Joe Taylor	<joe@tensilica.com, joetylr@yahoo.com>
13 */
14
15#include <linux/mm.h>
16#include <linux/module.h>
17#include <linux/hardirq.h>
18#include <asm/mmu_context.h>
19#include <asm/cacheflush.h>
20#include <asm/hardirq.h>
21#include <asm/uaccess.h>
22#include <asm/system.h>
23#include <asm/pgalloc.h>
24
25unsigned long asid_cache = ASID_USER_FIRST;
26void bad_page_fault(struct pt_regs*, unsigned long, int);
27
28#undef DEBUG_PAGE_FAULT
29
30/*
31 * This routine handles page faults.  It determines the address,
32 * and the problem, and then passes it off to one of the appropriate
33 * routines.
34 *
35 * Note: does not handle Miss and MultiHit.
36 */
37
38void do_page_fault(struct pt_regs *regs)
39{
40	struct vm_area_struct * vma;
41	struct mm_struct *mm = current->mm;
42	unsigned int exccause = regs->exccause;
43	unsigned int address = regs->excvaddr;
44	siginfo_t info;
45
46	int is_write, is_exec;
47	int fault;
48
49	info.si_code = SEGV_MAPERR;
50
51	/* We fault-in kernel-space virtual memory on-demand. The
52	 * 'reference' page table is init_mm.pgd.
53	 */
54	if (address >= TASK_SIZE && !user_mode(regs))
55		goto vmalloc_fault;
56
57	/* If we're in an interrupt or have no user
58	 * context, we must not take the fault..
59	 */
60	if (in_atomic() || !mm) {
61		bad_page_fault(regs, address, SIGSEGV);
62		return;
63	}
64
65	is_write = (exccause == EXCCAUSE_STORE_CACHE_ATTRIBUTE) ? 1 : 0;
66	is_exec =  (exccause == EXCCAUSE_ITLB_PRIVILEGE ||
67		    exccause == EXCCAUSE_ITLB_MISS ||
68		    exccause == EXCCAUSE_FETCH_CACHE_ATTRIBUTE) ? 1 : 0;
69
70#ifdef DEBUG_PAGE_FAULT
71	printk("[%s:%d:%08x:%d:%08x:%s%s]\n", current->comm, current->pid,
72	       address, exccause, regs->pc, is_write? "w":"", is_exec? "x":"");
73#endif
74
75	down_read(&mm->mmap_sem);
76	vma = find_vma(mm, address);
77
78	if (!vma)
79		goto bad_area;
80	if (vma->vm_start <= address)
81		goto good_area;
82	if (!(vma->vm_flags & VM_GROWSDOWN))
83		goto bad_area;
84	if (expand_stack(vma, address))
85		goto bad_area;
86
87	/* Ok, we have a good vm_area for this memory access, so
88	 * we can handle it..
89	 */
90
91good_area:
92	info.si_code = SEGV_ACCERR;
93
94	if (is_write) {
95		if (!(vma->vm_flags & VM_WRITE))
96			goto bad_area;
97	} else if (is_exec) {
98		if (!(vma->vm_flags & VM_EXEC))
99			goto bad_area;
100	} else	/* Allow read even from write-only pages. */
101		if (!(vma->vm_flags & (VM_READ | VM_WRITE)))
102			goto bad_area;
103
104	/* If for any reason at all we couldn't handle the fault,
105	 * make sure we exit gracefully rather than endlessly redo
106	 * the fault.
107	 */
108	fault = handle_mm_fault(mm, vma, address, is_write ? FAULT_FLAG_WRITE : 0);
109	if (unlikely(fault & VM_FAULT_ERROR)) {
110		if (fault & VM_FAULT_OOM)
111			goto out_of_memory;
112		else if (fault & VM_FAULT_SIGBUS)
113			goto do_sigbus;
114		BUG();
115	}
116	if (fault & VM_FAULT_MAJOR)
117		current->maj_flt++;
118	else
119		current->min_flt++;
120
121	up_read(&mm->mmap_sem);
122	return;
123
124	/* Something tried to access memory that isn't in our memory map..
125	 * Fix it, but check if it's kernel or user first..
126	 */
127bad_area:
128	up_read(&mm->mmap_sem);
129	if (user_mode(regs)) {
130		current->thread.bad_vaddr = address;
131		current->thread.error_code = is_write;
132		info.si_signo = SIGSEGV;
133		info.si_errno = 0;
134		/* info.si_code has been set above */
135		info.si_addr = (void *) address;
136		force_sig_info(SIGSEGV, &info, current);
137		return;
138	}
139	bad_page_fault(regs, address, SIGSEGV);
140	return;
141
142
143	/* We ran out of memory, or some other thing happened to us that made
144	 * us unable to handle the page fault gracefully.
145	 */
146out_of_memory:
147	up_read(&mm->mmap_sem);
148	if (!user_mode(regs))
149		bad_page_fault(regs, address, SIGKILL);
150	else
151		pagefault_out_of_memory();
152	return;
153
154do_sigbus:
155	up_read(&mm->mmap_sem);
156
157	/* Send a sigbus, regardless of whether we were in kernel
158	 * or user mode.
159	 */
160	current->thread.bad_vaddr = address;
161	info.si_code = SIGBUS;
162	info.si_errno = 0;
163	info.si_code = BUS_ADRERR;
164	info.si_addr = (void *) address;
165	force_sig_info(SIGBUS, &info, current);
166
167	/* Kernel mode? Handle exceptions or die */
168	if (!user_mode(regs))
169		bad_page_fault(regs, address, SIGBUS);
170
171vmalloc_fault:
172	{
173		/* Synchronize this task's top level page-table
174		 * with the 'reference' page table.
175		 */
176		struct mm_struct *act_mm = current->active_mm;
177		int index = pgd_index(address);
178		pgd_t *pgd, *pgd_k;
179		pmd_t *pmd, *pmd_k;
180		pte_t *pte_k;
181
182		if (act_mm == NULL)
183			goto bad_page_fault;
184
185		pgd = act_mm->pgd + index;
186		pgd_k = init_mm.pgd + index;
187
188		if (!pgd_present(*pgd_k))
189			goto bad_page_fault;
190
191		pgd_val(*pgd) = pgd_val(*pgd_k);
192
193		pmd = pmd_offset(pgd, address);
194		pmd_k = pmd_offset(pgd_k, address);
195		if (!pmd_present(*pmd) || !pmd_present(*pmd_k))
196			goto bad_page_fault;
197
198		pmd_val(*pmd) = pmd_val(*pmd_k);
199		pte_k = pte_offset_kernel(pmd_k, address);
200
201		if (!pte_present(*pte_k))
202			goto bad_page_fault;
203		return;
204	}
205bad_page_fault:
206	bad_page_fault(regs, address, SIGKILL);
207	return;
208}
209
210
211void
212bad_page_fault(struct pt_regs *regs, unsigned long address, int sig)
213{
214	extern void die(const char*, struct pt_regs*, long);
215	const struct exception_table_entry *entry;
216
217	/* Are we prepared to handle this kernel fault?  */
218	if ((entry = search_exception_tables(regs->pc)) != NULL) {
219#ifdef DEBUG_PAGE_FAULT
220		printk(KERN_DEBUG "%s: Exception at pc=%#010lx (%lx)\n",
221				current->comm, regs->pc, entry->fixup);
222#endif
223		current->thread.bad_uaddr = address;
224		regs->pc = entry->fixup;
225		return;
226	}
227
228	/* Oops. The kernel tried to access some bad page. We'll have to
229	 * terminate things with extreme prejudice.
230	 */
231	printk(KERN_ALERT "Unable to handle kernel paging request at virtual "
232	       "address %08lx\n pc = %08lx, ra = %08lx\n",
233	       address, regs->pc, regs->areg[0]);
234	die("Oops", regs, sig);
235	do_exit(sig);
236}
237