xref: /openbmc/linux/arch/sparc/mm/fault_32.c (revision 7235db26)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2c37ddd93SSam Ravnborg /*
3c37ddd93SSam Ravnborg  * fault.c:  Page fault handlers for the Sparc.
4c37ddd93SSam Ravnborg  *
5c37ddd93SSam Ravnborg  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
6c37ddd93SSam Ravnborg  * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
7c37ddd93SSam Ravnborg  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
8c37ddd93SSam Ravnborg  */
9c37ddd93SSam Ravnborg 
10c37ddd93SSam Ravnborg #include <asm/head.h>
11c37ddd93SSam Ravnborg 
12c37ddd93SSam Ravnborg #include <linux/string.h>
13c37ddd93SSam Ravnborg #include <linux/types.h>
14c37ddd93SSam Ravnborg #include <linux/sched.h>
15c37ddd93SSam Ravnborg #include <linux/ptrace.h>
16c37ddd93SSam Ravnborg #include <linux/mman.h>
17c37ddd93SSam Ravnborg #include <linux/threads.h>
18c37ddd93SSam Ravnborg #include <linux/kernel.h>
19c37ddd93SSam Ravnborg #include <linux/signal.h>
20c37ddd93SSam Ravnborg #include <linux/mm.h>
21c37ddd93SSam Ravnborg #include <linux/smp.h>
22a084b667SDavid S. Miller #include <linux/perf_event.h>
23c37ddd93SSam Ravnborg #include <linux/interrupt.h>
24c37ddd93SSam Ravnborg #include <linux/kdebug.h>
2570ffdb93SDavid Hildenbrand #include <linux/uaccess.h>
26c37ddd93SSam Ravnborg 
27c37ddd93SSam Ravnborg #include <asm/page.h>
28c37ddd93SSam Ravnborg #include <asm/pgtable.h>
29c37ddd93SSam Ravnborg #include <asm/openprom.h>
30c37ddd93SSam Ravnborg #include <asm/oplib.h>
319edfae3fSSam Ravnborg #include <asm/setup.h>
32c37ddd93SSam Ravnborg #include <asm/smp.h>
33c37ddd93SSam Ravnborg #include <asm/traps.h>
34c37ddd93SSam Ravnborg 
35e1b2f134SSam Ravnborg #include "mm_32.h"
364b177647SDavid S. Miller 
37e1b2f134SSam Ravnborg int show_unhandled_signals = 1;
38c37ddd93SSam Ravnborg 
3970168dfaSSam Ravnborg static void __noreturn unhandled_fault(unsigned long address,
4070168dfaSSam Ravnborg 				       struct task_struct *tsk,
41c37ddd93SSam Ravnborg 				       struct pt_regs *regs)
42c37ddd93SSam Ravnborg {
43c37ddd93SSam Ravnborg 	if ((unsigned long) address < PAGE_SIZE) {
44c37ddd93SSam Ravnborg 		printk(KERN_ALERT
45c37ddd93SSam Ravnborg 		    "Unable to handle kernel NULL pointer dereference\n");
46c37ddd93SSam Ravnborg 	} else {
4770168dfaSSam Ravnborg 		printk(KERN_ALERT "Unable to handle kernel paging request at virtual address %08lx\n",
4870168dfaSSam Ravnborg 		       address);
49c37ddd93SSam Ravnborg 	}
50c37ddd93SSam Ravnborg 	printk(KERN_ALERT "tsk->{mm,active_mm}->context = %08lx\n",
51c37ddd93SSam Ravnborg 		(tsk->mm ? tsk->mm->context : tsk->active_mm->context));
52c37ddd93SSam Ravnborg 	printk(KERN_ALERT "tsk->{mm,active_mm}->pgd = %08lx\n",
53c37ddd93SSam Ravnborg 		(tsk->mm ? (unsigned long) tsk->mm->pgd :
54c37ddd93SSam Ravnborg 			(unsigned long) tsk->active_mm->pgd));
55c37ddd93SSam Ravnborg 	die_if_kernel("Oops", regs);
56c37ddd93SSam Ravnborg }
57c37ddd93SSam Ravnborg 
58c37ddd93SSam Ravnborg asmlinkage int lookup_fault(unsigned long pc, unsigned long ret_pc,
59c37ddd93SSam Ravnborg 			    unsigned long address)
60c37ddd93SSam Ravnborg {
61c37ddd93SSam Ravnborg 	struct pt_regs regs;
62c37ddd93SSam Ravnborg 	unsigned long g2;
63c37ddd93SSam Ravnborg 	unsigned int insn;
64c37ddd93SSam Ravnborg 	int i;
65c37ddd93SSam Ravnborg 
66c37ddd93SSam Ravnborg 	i = search_extables_range(ret_pc, &g2);
67c37ddd93SSam Ravnborg 	switch (i) {
68c37ddd93SSam Ravnborg 	case 3:
69c37ddd93SSam Ravnborg 		/* load & store will be handled by fixup */
70c37ddd93SSam Ravnborg 		return 3;
71c37ddd93SSam Ravnborg 
72c37ddd93SSam Ravnborg 	case 1:
73c37ddd93SSam Ravnborg 		/* store will be handled by fixup, load will bump out */
74c37ddd93SSam Ravnborg 		/* for _to_ macros */
75c37ddd93SSam Ravnborg 		insn = *((unsigned int *) pc);
76c37ddd93SSam Ravnborg 		if ((insn >> 21) & 1)
77c37ddd93SSam Ravnborg 			return 1;
78c37ddd93SSam Ravnborg 		break;
79c37ddd93SSam Ravnborg 
80c37ddd93SSam Ravnborg 	case 2:
81c37ddd93SSam Ravnborg 		/* load will be handled by fixup, store will bump out */
82c37ddd93SSam Ravnborg 		/* for _from_ macros */
83c37ddd93SSam Ravnborg 		insn = *((unsigned int *) pc);
84c37ddd93SSam Ravnborg 		if (!((insn >> 21) & 1) || ((insn>>19)&0x3f) == 15)
85c37ddd93SSam Ravnborg 			return 2;
86c37ddd93SSam Ravnborg 		break;
87c37ddd93SSam Ravnborg 
88c37ddd93SSam Ravnborg 	default:
89c37ddd93SSam Ravnborg 		break;
906cb79b3fSJoe Perches 	}
91c37ddd93SSam Ravnborg 
92c37ddd93SSam Ravnborg 	memset(&regs, 0, sizeof(regs));
93c37ddd93SSam Ravnborg 	regs.pc = pc;
94c37ddd93SSam Ravnborg 	regs.npc = pc + 4;
95c37ddd93SSam Ravnborg 	__asm__ __volatile__(
96c37ddd93SSam Ravnborg 		"rd %%psr, %0\n\t"
97c37ddd93SSam Ravnborg 		"nop\n\t"
98c37ddd93SSam Ravnborg 		"nop\n\t"
99c37ddd93SSam Ravnborg 		"nop\n" : "=r" (regs.psr));
100c37ddd93SSam Ravnborg 	unhandled_fault(address, current, &regs);
101c37ddd93SSam Ravnborg 
102c37ddd93SSam Ravnborg 	/* Not reached */
103c37ddd93SSam Ravnborg 	return 0;
104c37ddd93SSam Ravnborg }
105c37ddd93SSam Ravnborg 
1064b177647SDavid S. Miller static inline void
1074b177647SDavid S. Miller show_signal_msg(struct pt_regs *regs, int sig, int code,
1084b177647SDavid S. Miller 		unsigned long address, struct task_struct *tsk)
1094b177647SDavid S. Miller {
1104b177647SDavid S. Miller 	if (!unhandled_signal(tsk, sig))
1114b177647SDavid S. Miller 		return;
1124b177647SDavid S. Miller 
1134b177647SDavid S. Miller 	if (!printk_ratelimit())
1144b177647SDavid S. Miller 		return;
1154b177647SDavid S. Miller 
11610a7e9d8SKees Cook 	printk("%s%s[%d]: segfault at %lx ip %px (rpc %px) sp %px error %x",
1174b177647SDavid S. Miller 	       task_pid_nr(tsk) > 1 ? KERN_INFO : KERN_EMERG,
1184b177647SDavid S. Miller 	       tsk->comm, task_pid_nr(tsk), address,
1194b177647SDavid S. Miller 	       (void *)regs->pc, (void *)regs->u_regs[UREG_I7],
1204b177647SDavid S. Miller 	       (void *)regs->u_regs[UREG_FP], code);
1214b177647SDavid S. Miller 
1224b177647SDavid S. Miller 	print_vma_addr(KERN_CONT " in ", regs->pc);
1234b177647SDavid S. Miller 
1244b177647SDavid S. Miller 	printk(KERN_CONT "\n");
1254b177647SDavid S. Miller }
1264b177647SDavid S. Miller 
1274b177647SDavid S. Miller static void __do_fault_siginfo(int code, int sig, struct pt_regs *regs,
1284b177647SDavid S. Miller 			       unsigned long addr)
1294b177647SDavid S. Miller {
1304b177647SDavid S. Miller 	if (unlikely(show_unhandled_signals))
131d1f5bef6SEric W. Biederman 		show_signal_msg(regs, sig, code,
1324b177647SDavid S. Miller 				addr, current);
1334b177647SDavid S. Miller 
1342e1661d2SEric W. Biederman 	force_sig_fault(sig, code, (void __user *) addr, 0);
1354b177647SDavid S. Miller }
1364b177647SDavid S. Miller 
137c37ddd93SSam Ravnborg static unsigned long compute_si_addr(struct pt_regs *regs, int text_fault)
138c37ddd93SSam Ravnborg {
139c37ddd93SSam Ravnborg 	unsigned int insn;
140c37ddd93SSam Ravnborg 
141c37ddd93SSam Ravnborg 	if (text_fault)
142c37ddd93SSam Ravnborg 		return regs->pc;
143c37ddd93SSam Ravnborg 
14470168dfaSSam Ravnborg 	if (regs->psr & PSR_PS)
145c37ddd93SSam Ravnborg 		insn = *(unsigned int *) regs->pc;
14670168dfaSSam Ravnborg 	else
147c37ddd93SSam Ravnborg 		__get_user(insn, (unsigned int *) regs->pc);
148c37ddd93SSam Ravnborg 
149c37ddd93SSam Ravnborg 	return safe_compute_effective_address(regs, insn);
150c37ddd93SSam Ravnborg }
151c37ddd93SSam Ravnborg 
1524b177647SDavid S. Miller static noinline void do_fault_siginfo(int code, int sig, struct pt_regs *regs,
1534b177647SDavid S. Miller 				      int text_fault)
1544b177647SDavid S. Miller {
1554b177647SDavid S. Miller 	unsigned long addr = compute_si_addr(regs, text_fault);
1564b177647SDavid S. Miller 
1574b177647SDavid S. Miller 	__do_fault_siginfo(code, sig, regs, addr);
1584b177647SDavid S. Miller }
1594b177647SDavid S. Miller 
160c37ddd93SSam Ravnborg asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write,
161c37ddd93SSam Ravnborg 			       unsigned long address)
162c37ddd93SSam Ravnborg {
163c37ddd93SSam Ravnborg 	struct vm_area_struct *vma;
164c37ddd93SSam Ravnborg 	struct task_struct *tsk = current;
165c37ddd93SSam Ravnborg 	struct mm_struct *mm = tsk->mm;
166c37ddd93SSam Ravnborg 	unsigned int fixup;
167c37ddd93SSam Ravnborg 	unsigned long g2;
168c37ddd93SSam Ravnborg 	int from_user = !(regs->psr & PSR_PS);
16950a7ca3cSSouptick Joarder 	int code;
17050a7ca3cSSouptick Joarder 	vm_fault_t fault;
171759496baSJohannes Weiner 	unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
172c37ddd93SSam Ravnborg 
173c37ddd93SSam Ravnborg 	if (text_fault)
174c37ddd93SSam Ravnborg 		address = regs->pc;
175c37ddd93SSam Ravnborg 
176c37ddd93SSam Ravnborg 	/*
177c37ddd93SSam Ravnborg 	 * We fault-in kernel-space virtual memory on-demand. The
178c37ddd93SSam Ravnborg 	 * 'reference' page table is init_mm.pgd.
179c37ddd93SSam Ravnborg 	 *
180c37ddd93SSam Ravnborg 	 * NOTE! We MUST NOT take any locks for this case. We may
181c37ddd93SSam Ravnborg 	 * be in an interrupt or a critical region, and should
182c37ddd93SSam Ravnborg 	 * only copy the information from the master page table,
183c37ddd93SSam Ravnborg 	 * nothing more.
184c37ddd93SSam Ravnborg 	 */
185c816be7bSDavid S. Miller 	code = SEGV_MAPERR;
186582a0baeSSam Ravnborg 	if (address >= TASK_SIZE)
187c37ddd93SSam Ravnborg 		goto vmalloc_fault;
188c37ddd93SSam Ravnborg 
189c37ddd93SSam Ravnborg 	/*
190c37ddd93SSam Ravnborg 	 * If we're in an interrupt or have no user
191c37ddd93SSam Ravnborg 	 * context, we must not take the fault..
192c37ddd93SSam Ravnborg 	 */
19370ffdb93SDavid Hildenbrand 	if (pagefault_disabled() || !mm)
194c37ddd93SSam Ravnborg 		goto no_context;
195c37ddd93SSam Ravnborg 
196a8b0ca17SPeter Zijlstra 	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
197a084b667SDavid S. Miller 
198c29554f5SKautuk Consul retry:
199c37ddd93SSam Ravnborg 	down_read(&mm->mmap_sem);
200c37ddd93SSam Ravnborg 
201c37ddd93SSam Ravnborg 	if (!from_user && address >= PAGE_OFFSET)
202c37ddd93SSam Ravnborg 		goto bad_area;
203c37ddd93SSam Ravnborg 
204c37ddd93SSam Ravnborg 	vma = find_vma(mm, address);
205c37ddd93SSam Ravnborg 	if (!vma)
206c37ddd93SSam Ravnborg 		goto bad_area;
207c37ddd93SSam Ravnborg 	if (vma->vm_start <= address)
208c37ddd93SSam Ravnborg 		goto good_area;
209c37ddd93SSam Ravnborg 	if (!(vma->vm_flags & VM_GROWSDOWN))
210c37ddd93SSam Ravnborg 		goto bad_area;
211c37ddd93SSam Ravnborg 	if (expand_stack(vma, address))
212c37ddd93SSam Ravnborg 		goto bad_area;
213c37ddd93SSam Ravnborg 	/*
214c37ddd93SSam Ravnborg 	 * Ok, we have a good vm_area for this memory access, so
215c37ddd93SSam Ravnborg 	 * we can handle it..
216c37ddd93SSam Ravnborg 	 */
217c37ddd93SSam Ravnborg good_area:
2184b177647SDavid S. Miller 	code = SEGV_ACCERR;
219c37ddd93SSam Ravnborg 	if (write) {
220c37ddd93SSam Ravnborg 		if (!(vma->vm_flags & VM_WRITE))
221c37ddd93SSam Ravnborg 			goto bad_area;
222c37ddd93SSam Ravnborg 	} else {
223c37ddd93SSam Ravnborg 		/* Allow reads even for write-only mappings */
224c37ddd93SSam Ravnborg 		if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
225c37ddd93SSam Ravnborg 			goto bad_area;
226c37ddd93SSam Ravnborg 	}
227c37ddd93SSam Ravnborg 
228759496baSJohannes Weiner 	if (from_user)
229759496baSJohannes Weiner 		flags |= FAULT_FLAG_USER;
230759496baSJohannes Weiner 	if (write)
231759496baSJohannes Weiner 		flags |= FAULT_FLAG_WRITE;
232759496baSJohannes Weiner 
233c37ddd93SSam Ravnborg 	/*
234c37ddd93SSam Ravnborg 	 * If for any reason at all we couldn't handle the fault,
235c37ddd93SSam Ravnborg 	 * make sure we exit gracefully rather than endlessly redo
236c37ddd93SSam Ravnborg 	 * the fault.
237c37ddd93SSam Ravnborg 	 */
238dcddffd4SKirill A. Shutemov 	fault = handle_mm_fault(vma, address, flags);
239c29554f5SKautuk Consul 
240c29554f5SKautuk Consul 	if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current))
241c29554f5SKautuk Consul 		return;
242c29554f5SKautuk Consul 
243c37ddd93SSam Ravnborg 	if (unlikely(fault & VM_FAULT_ERROR)) {
244c37ddd93SSam Ravnborg 		if (fault & VM_FAULT_OOM)
245c37ddd93SSam Ravnborg 			goto out_of_memory;
24633692f27SLinus Torvalds 		else if (fault & VM_FAULT_SIGSEGV)
24733692f27SLinus Torvalds 			goto bad_area;
248c37ddd93SSam Ravnborg 		else if (fault & VM_FAULT_SIGBUS)
249c37ddd93SSam Ravnborg 			goto do_sigbus;
250c37ddd93SSam Ravnborg 		BUG();
251c37ddd93SSam Ravnborg 	}
252c29554f5SKautuk Consul 
253c29554f5SKautuk Consul 	if (flags & FAULT_FLAG_ALLOW_RETRY) {
254a084b667SDavid S. Miller 		if (fault & VM_FAULT_MAJOR) {
255c37ddd93SSam Ravnborg 			current->maj_flt++;
256c29554f5SKautuk Consul 			perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ,
257c29554f5SKautuk Consul 				      1, regs, address);
258a084b667SDavid S. Miller 		} else {
259c37ddd93SSam Ravnborg 			current->min_flt++;
260c29554f5SKautuk Consul 			perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN,
261c29554f5SKautuk Consul 				      1, regs, address);
262a084b667SDavid S. Miller 		}
263c29554f5SKautuk Consul 		if (fault & VM_FAULT_RETRY) {
264c29554f5SKautuk Consul 			flags &= ~FAULT_FLAG_ALLOW_RETRY;
26545cac65bSShaohua Li 			flags |= FAULT_FLAG_TRIED;
266c29554f5SKautuk Consul 
267c29554f5SKautuk Consul 			/* No need to up_read(&mm->mmap_sem) as we would
268c29554f5SKautuk Consul 			 * have already released it in __lock_page_or_retry
269c29554f5SKautuk Consul 			 * in mm/filemap.c.
270c29554f5SKautuk Consul 			 */
271c29554f5SKautuk Consul 
272c29554f5SKautuk Consul 			goto retry;
273c29554f5SKautuk Consul 		}
274c29554f5SKautuk Consul 	}
275c29554f5SKautuk Consul 
276c37ddd93SSam Ravnborg 	up_read(&mm->mmap_sem);
277c37ddd93SSam Ravnborg 	return;
278c37ddd93SSam Ravnborg 
279c37ddd93SSam Ravnborg 	/*
280c37ddd93SSam Ravnborg 	 * Something tried to access memory that isn't in our memory map..
281c37ddd93SSam Ravnborg 	 * Fix it, but check if it's kernel or user first..
282c37ddd93SSam Ravnborg 	 */
283c37ddd93SSam Ravnborg bad_area:
284c37ddd93SSam Ravnborg 	up_read(&mm->mmap_sem);
285c37ddd93SSam Ravnborg 
286c37ddd93SSam Ravnborg bad_area_nosemaphore:
287c37ddd93SSam Ravnborg 	/* User mode accesses just cause a SIGSEGV */
288c37ddd93SSam Ravnborg 	if (from_user) {
2894b177647SDavid S. Miller 		do_fault_siginfo(code, SIGSEGV, regs, text_fault);
290c37ddd93SSam Ravnborg 		return;
291c37ddd93SSam Ravnborg 	}
292c37ddd93SSam Ravnborg 
293c37ddd93SSam Ravnborg 	/* Is this in ex_table? */
294c37ddd93SSam Ravnborg no_context:
295c37ddd93SSam Ravnborg 	g2 = regs->u_regs[UREG_G2];
2960157141aSSam Ravnborg 	if (!from_user) {
2970157141aSSam Ravnborg 		fixup = search_extables_range(regs->pc, &g2);
29870168dfaSSam Ravnborg 		/* Values below 10 are reserved for other things */
29970168dfaSSam Ravnborg 		if (fixup > 10) {
3009ef595d8SJoe Perches 			extern const unsigned int __memset_start[];
3019ef595d8SJoe Perches 			extern const unsigned int __memset_end[];
3029ef595d8SJoe Perches 			extern const unsigned int __csum_partial_copy_start[];
3039ef595d8SJoe Perches 			extern const unsigned int __csum_partial_copy_end[];
304c37ddd93SSam Ravnborg 
305c37ddd93SSam Ravnborg #ifdef DEBUG_EXCEPTIONS
30670168dfaSSam Ravnborg 			printk("Exception: PC<%08lx> faddr<%08lx>\n",
30770168dfaSSam Ravnborg 			       regs->pc, address);
308c37ddd93SSam Ravnborg 			printk("EX_TABLE: insn<%08lx> fixup<%08x> g2<%08lx>\n",
309c37ddd93SSam Ravnborg 				regs->pc, fixup, g2);
310c37ddd93SSam Ravnborg #endif
311c37ddd93SSam Ravnborg 			if ((regs->pc >= (unsigned long)__memset_start &&
312c37ddd93SSam Ravnborg 			     regs->pc < (unsigned long)__memset_end) ||
313c37ddd93SSam Ravnborg 			    (regs->pc >= (unsigned long)__csum_partial_copy_start &&
314c37ddd93SSam Ravnborg 			     regs->pc < (unsigned long)__csum_partial_copy_end)) {
315c37ddd93SSam Ravnborg 				regs->u_regs[UREG_I4] = address;
316c37ddd93SSam Ravnborg 				regs->u_regs[UREG_I5] = regs->pc;
317c37ddd93SSam Ravnborg 			}
318c37ddd93SSam Ravnborg 			regs->u_regs[UREG_G2] = g2;
319c37ddd93SSam Ravnborg 			regs->pc = fixup;
320c37ddd93SSam Ravnborg 			regs->npc = regs->pc + 4;
321c37ddd93SSam Ravnborg 			return;
322c37ddd93SSam Ravnborg 		}
323c37ddd93SSam Ravnborg 	}
324c37ddd93SSam Ravnborg 
325c37ddd93SSam Ravnborg 	unhandled_fault(address, tsk, regs);
326c37ddd93SSam Ravnborg 	do_exit(SIGKILL);
327c37ddd93SSam Ravnborg 
328c37ddd93SSam Ravnborg /*
329c37ddd93SSam Ravnborg  * We ran out of memory, or some other thing happened to us that made
330c37ddd93SSam Ravnborg  * us unable to handle the page fault gracefully.
331c37ddd93SSam Ravnborg  */
332c37ddd93SSam Ravnborg out_of_memory:
333c37ddd93SSam Ravnborg 	up_read(&mm->mmap_sem);
334a923c28fSDavid S. Miller 	if (from_user) {
335a923c28fSDavid S. Miller 		pagefault_out_of_memory();
336a923c28fSDavid S. Miller 		return;
337a923c28fSDavid S. Miller 	}
338c37ddd93SSam Ravnborg 	goto no_context;
339c37ddd93SSam Ravnborg 
340c37ddd93SSam Ravnborg do_sigbus:
341c37ddd93SSam Ravnborg 	up_read(&mm->mmap_sem);
3424b177647SDavid S. Miller 	do_fault_siginfo(BUS_ADRERR, SIGBUS, regs, text_fault);
343c37ddd93SSam Ravnborg 	if (!from_user)
344c37ddd93SSam Ravnborg 		goto no_context;
345c37ddd93SSam Ravnborg 
346c37ddd93SSam Ravnborg vmalloc_fault:
347c37ddd93SSam Ravnborg 	{
348c37ddd93SSam Ravnborg 		/*
349c37ddd93SSam Ravnborg 		 * Synchronize this task's top level page-table
350c37ddd93SSam Ravnborg 		 * with the 'reference' page table.
351c37ddd93SSam Ravnborg 		 */
352c37ddd93SSam Ravnborg 		int offset = pgd_index(address);
353c37ddd93SSam Ravnborg 		pgd_t *pgd, *pgd_k;
3547235db26SMike Rapoport 		p4d_t *p4d, *p4d_k;
3557235db26SMike Rapoport 		pud_t *pud, *pud_k;
356c37ddd93SSam Ravnborg 		pmd_t *pmd, *pmd_k;
357c37ddd93SSam Ravnborg 
358c37ddd93SSam Ravnborg 		pgd = tsk->active_mm->pgd + offset;
359c37ddd93SSam Ravnborg 		pgd_k = init_mm.pgd + offset;
360c37ddd93SSam Ravnborg 
361c37ddd93SSam Ravnborg 		if (!pgd_present(*pgd)) {
362c37ddd93SSam Ravnborg 			if (!pgd_present(*pgd_k))
363c37ddd93SSam Ravnborg 				goto bad_area_nosemaphore;
364c37ddd93SSam Ravnborg 			pgd_val(*pgd) = pgd_val(*pgd_k);
365c37ddd93SSam Ravnborg 			return;
366c37ddd93SSam Ravnborg 		}
367c37ddd93SSam Ravnborg 
3687235db26SMike Rapoport 		p4d = p4d_offset(pgd, address);
3697235db26SMike Rapoport 		pud = pud_offset(p4d, address);
3707235db26SMike Rapoport 		pmd = pmd_offset(pud, address);
3717235db26SMike Rapoport 
3727235db26SMike Rapoport 		p4d_k = p4d_offset(pgd_k, address);
3737235db26SMike Rapoport 		pud_k = pud_offset(p4d_k, address);
3747235db26SMike Rapoport 		pmd_k = pmd_offset(pud_k, address);
375c37ddd93SSam Ravnborg 
376c37ddd93SSam Ravnborg 		if (pmd_present(*pmd) || !pmd_present(*pmd_k))
377c37ddd93SSam Ravnborg 			goto bad_area_nosemaphore;
37870168dfaSSam Ravnborg 
379c37ddd93SSam Ravnborg 		*pmd = *pmd_k;
380c37ddd93SSam Ravnborg 		return;
381c37ddd93SSam Ravnborg 	}
382c37ddd93SSam Ravnborg }
383c37ddd93SSam Ravnborg 
384c37ddd93SSam Ravnborg /* This always deals with user addresses. */
385c37ddd93SSam Ravnborg static void force_user_fault(unsigned long address, int write)
386c37ddd93SSam Ravnborg {
387c37ddd93SSam Ravnborg 	struct vm_area_struct *vma;
388c37ddd93SSam Ravnborg 	struct task_struct *tsk = current;
389c37ddd93SSam Ravnborg 	struct mm_struct *mm = tsk->mm;
390759496baSJohannes Weiner 	unsigned int flags = FAULT_FLAG_USER;
3914b177647SDavid S. Miller 	int code;
392c37ddd93SSam Ravnborg 
3934b177647SDavid S. Miller 	code = SEGV_MAPERR;
394c37ddd93SSam Ravnborg 
395c37ddd93SSam Ravnborg 	down_read(&mm->mmap_sem);
396c37ddd93SSam Ravnborg 	vma = find_vma(mm, address);
397c37ddd93SSam Ravnborg 	if (!vma)
398c37ddd93SSam Ravnborg 		goto bad_area;
399c37ddd93SSam Ravnborg 	if (vma->vm_start <= address)
400c37ddd93SSam Ravnborg 		goto good_area;
401c37ddd93SSam Ravnborg 	if (!(vma->vm_flags & VM_GROWSDOWN))
402c37ddd93SSam Ravnborg 		goto bad_area;
403c37ddd93SSam Ravnborg 	if (expand_stack(vma, address))
404c37ddd93SSam Ravnborg 		goto bad_area;
405c37ddd93SSam Ravnborg good_area:
4064b177647SDavid S. Miller 	code = SEGV_ACCERR;
407c37ddd93SSam Ravnborg 	if (write) {
408c37ddd93SSam Ravnborg 		if (!(vma->vm_flags & VM_WRITE))
409c37ddd93SSam Ravnborg 			goto bad_area;
410759496baSJohannes Weiner 		flags |= FAULT_FLAG_WRITE;
411c37ddd93SSam Ravnborg 	} else {
412c37ddd93SSam Ravnborg 		if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
413c37ddd93SSam Ravnborg 			goto bad_area;
414c37ddd93SSam Ravnborg 	}
415dcddffd4SKirill A. Shutemov 	switch (handle_mm_fault(vma, address, flags)) {
416c37ddd93SSam Ravnborg 	case VM_FAULT_SIGBUS:
417c37ddd93SSam Ravnborg 	case VM_FAULT_OOM:
418c37ddd93SSam Ravnborg 		goto do_sigbus;
419c37ddd93SSam Ravnborg 	}
420c37ddd93SSam Ravnborg 	up_read(&mm->mmap_sem);
421c37ddd93SSam Ravnborg 	return;
422c37ddd93SSam Ravnborg bad_area:
423c37ddd93SSam Ravnborg 	up_read(&mm->mmap_sem);
4244b177647SDavid S. Miller 	__do_fault_siginfo(code, SIGSEGV, tsk->thread.kregs, address);
425c37ddd93SSam Ravnborg 	return;
426c37ddd93SSam Ravnborg 
427c37ddd93SSam Ravnborg do_sigbus:
428c37ddd93SSam Ravnborg 	up_read(&mm->mmap_sem);
4294b177647SDavid S. Miller 	__do_fault_siginfo(BUS_ADRERR, SIGBUS, tsk->thread.kregs, address);
430c37ddd93SSam Ravnborg }
431c37ddd93SSam Ravnborg 
4329088333eSDavid S. Miller static void check_stack_aligned(unsigned long sp)
4339088333eSDavid S. Miller {
4349088333eSDavid S. Miller 	if (sp & 0x7UL)
4353cf5d076SEric W. Biederman 		force_sig(SIGILL);
4369088333eSDavid S. Miller }
4379088333eSDavid S. Miller 
438c37ddd93SSam Ravnborg void window_overflow_fault(void)
439c37ddd93SSam Ravnborg {
440c37ddd93SSam Ravnborg 	unsigned long sp;
441c37ddd93SSam Ravnborg 
442c37ddd93SSam Ravnborg 	sp = current_thread_info()->rwbuf_stkptrs[0];
443c37ddd93SSam Ravnborg 	if (((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK))
444c37ddd93SSam Ravnborg 		force_user_fault(sp + 0x38, 1);
445c37ddd93SSam Ravnborg 	force_user_fault(sp, 1);
4469088333eSDavid S. Miller 
4479088333eSDavid S. Miller 	check_stack_aligned(sp);
448c37ddd93SSam Ravnborg }
449c37ddd93SSam Ravnborg 
450c37ddd93SSam Ravnborg void window_underflow_fault(unsigned long sp)
451c37ddd93SSam Ravnborg {
452c37ddd93SSam Ravnborg 	if (((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK))
453c37ddd93SSam Ravnborg 		force_user_fault(sp + 0x38, 0);
454c37ddd93SSam Ravnborg 	force_user_fault(sp, 0);
4559088333eSDavid S. Miller 
4569088333eSDavid S. Miller 	check_stack_aligned(sp);
457c37ddd93SSam Ravnborg }
458c37ddd93SSam Ravnborg 
459c37ddd93SSam Ravnborg void window_ret_fault(struct pt_regs *regs)
460c37ddd93SSam Ravnborg {
461c37ddd93SSam Ravnborg 	unsigned long sp;
462c37ddd93SSam Ravnborg 
463c37ddd93SSam Ravnborg 	sp = regs->u_regs[UREG_FP];
464c37ddd93SSam Ravnborg 	if (((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK))
465c37ddd93SSam Ravnborg 		force_user_fault(sp + 0x38, 0);
466c37ddd93SSam Ravnborg 	force_user_fault(sp, 0);
4679088333eSDavid S. Miller 
4689088333eSDavid S. Miller 	check_stack_aligned(sp);
469c37ddd93SSam Ravnborg }
470