18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * fault.c:  Page fault handlers for the Sparc.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
68c2ecf20Sopenharmony_ci * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
78c2ecf20Sopenharmony_ci * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <asm/head.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/string.h>
138c2ecf20Sopenharmony_ci#include <linux/types.h>
148c2ecf20Sopenharmony_ci#include <linux/sched.h>
158c2ecf20Sopenharmony_ci#include <linux/ptrace.h>
168c2ecf20Sopenharmony_ci#include <linux/mman.h>
178c2ecf20Sopenharmony_ci#include <linux/threads.h>
188c2ecf20Sopenharmony_ci#include <linux/kernel.h>
198c2ecf20Sopenharmony_ci#include <linux/signal.h>
208c2ecf20Sopenharmony_ci#include <linux/mm.h>
218c2ecf20Sopenharmony_ci#include <linux/smp.h>
228c2ecf20Sopenharmony_ci#include <linux/perf_event.h>
238c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
248c2ecf20Sopenharmony_ci#include <linux/kdebug.h>
258c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <asm/page.h>
288c2ecf20Sopenharmony_ci#include <asm/openprom.h>
298c2ecf20Sopenharmony_ci#include <asm/oplib.h>
308c2ecf20Sopenharmony_ci#include <asm/setup.h>
318c2ecf20Sopenharmony_ci#include <asm/smp.h>
328c2ecf20Sopenharmony_ci#include <asm/traps.h>
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include "mm_32.h"
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ciint show_unhandled_signals = 1;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic void __noreturn unhandled_fault(unsigned long address,
398c2ecf20Sopenharmony_ci				       struct task_struct *tsk,
408c2ecf20Sopenharmony_ci				       struct pt_regs *regs)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	if ((unsigned long) address < PAGE_SIZE) {
438c2ecf20Sopenharmony_ci		printk(KERN_ALERT
448c2ecf20Sopenharmony_ci		    "Unable to handle kernel NULL pointer dereference\n");
458c2ecf20Sopenharmony_ci	} else {
468c2ecf20Sopenharmony_ci		printk(KERN_ALERT "Unable to handle kernel paging request at virtual address %08lx\n",
478c2ecf20Sopenharmony_ci		       address);
488c2ecf20Sopenharmony_ci	}
498c2ecf20Sopenharmony_ci	printk(KERN_ALERT "tsk->{mm,active_mm}->context = %08lx\n",
508c2ecf20Sopenharmony_ci		(tsk->mm ? tsk->mm->context : tsk->active_mm->context));
518c2ecf20Sopenharmony_ci	printk(KERN_ALERT "tsk->{mm,active_mm}->pgd = %08lx\n",
528c2ecf20Sopenharmony_ci		(tsk->mm ? (unsigned long) tsk->mm->pgd :
538c2ecf20Sopenharmony_ci			(unsigned long) tsk->active_mm->pgd));
548c2ecf20Sopenharmony_ci	die_if_kernel("Oops", regs);
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ciasmlinkage int lookup_fault(unsigned long pc, unsigned long ret_pc,
588c2ecf20Sopenharmony_ci			    unsigned long address)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	struct pt_regs regs;
618c2ecf20Sopenharmony_ci	unsigned long g2;
628c2ecf20Sopenharmony_ci	unsigned int insn;
638c2ecf20Sopenharmony_ci	int i;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	i = search_extables_range(ret_pc, &g2);
668c2ecf20Sopenharmony_ci	switch (i) {
678c2ecf20Sopenharmony_ci	case 3:
688c2ecf20Sopenharmony_ci		/* load & store will be handled by fixup */
698c2ecf20Sopenharmony_ci		return 3;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	case 1:
728c2ecf20Sopenharmony_ci		/* store will be handled by fixup, load will bump out */
738c2ecf20Sopenharmony_ci		/* for _to_ macros */
748c2ecf20Sopenharmony_ci		insn = *((unsigned int *) pc);
758c2ecf20Sopenharmony_ci		if ((insn >> 21) & 1)
768c2ecf20Sopenharmony_ci			return 1;
778c2ecf20Sopenharmony_ci		break;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	case 2:
808c2ecf20Sopenharmony_ci		/* load will be handled by fixup, store will bump out */
818c2ecf20Sopenharmony_ci		/* for _from_ macros */
828c2ecf20Sopenharmony_ci		insn = *((unsigned int *) pc);
838c2ecf20Sopenharmony_ci		if (!((insn >> 21) & 1) || ((insn>>19)&0x3f) == 15)
848c2ecf20Sopenharmony_ci			return 2;
858c2ecf20Sopenharmony_ci		break;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	default:
888c2ecf20Sopenharmony_ci		break;
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	memset(&regs, 0, sizeof(regs));
928c2ecf20Sopenharmony_ci	regs.pc = pc;
938c2ecf20Sopenharmony_ci	regs.npc = pc + 4;
948c2ecf20Sopenharmony_ci	__asm__ __volatile__(
958c2ecf20Sopenharmony_ci		"rd %%psr, %0\n\t"
968c2ecf20Sopenharmony_ci		"nop\n\t"
978c2ecf20Sopenharmony_ci		"nop\n\t"
988c2ecf20Sopenharmony_ci		"nop\n" : "=r" (regs.psr));
998c2ecf20Sopenharmony_ci	unhandled_fault(address, current, &regs);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	/* Not reached */
1028c2ecf20Sopenharmony_ci	return 0;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic inline void
1068c2ecf20Sopenharmony_cishow_signal_msg(struct pt_regs *regs, int sig, int code,
1078c2ecf20Sopenharmony_ci		unsigned long address, struct task_struct *tsk)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	if (!unhandled_signal(tsk, sig))
1108c2ecf20Sopenharmony_ci		return;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	if (!printk_ratelimit())
1138c2ecf20Sopenharmony_ci		return;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	printk("%s%s[%d]: segfault at %lx ip %px (rpc %px) sp %px error %x",
1168c2ecf20Sopenharmony_ci	       task_pid_nr(tsk) > 1 ? KERN_INFO : KERN_EMERG,
1178c2ecf20Sopenharmony_ci	       tsk->comm, task_pid_nr(tsk), address,
1188c2ecf20Sopenharmony_ci	       (void *)regs->pc, (void *)regs->u_regs[UREG_I7],
1198c2ecf20Sopenharmony_ci	       (void *)regs->u_regs[UREG_FP], code);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	print_vma_addr(KERN_CONT " in ", regs->pc);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	printk(KERN_CONT "\n");
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic void __do_fault_siginfo(int code, int sig, struct pt_regs *regs,
1278c2ecf20Sopenharmony_ci			       unsigned long addr)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	if (unlikely(show_unhandled_signals))
1308c2ecf20Sopenharmony_ci		show_signal_msg(regs, sig, code,
1318c2ecf20Sopenharmony_ci				addr, current);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	force_sig_fault(sig, code, (void __user *) addr, 0);
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic unsigned long compute_si_addr(struct pt_regs *regs, int text_fault)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	unsigned int insn;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	if (text_fault)
1418c2ecf20Sopenharmony_ci		return regs->pc;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	if (regs->psr & PSR_PS)
1448c2ecf20Sopenharmony_ci		insn = *(unsigned int *) regs->pc;
1458c2ecf20Sopenharmony_ci	else
1468c2ecf20Sopenharmony_ci		__get_user(insn, (unsigned int *) regs->pc);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	return safe_compute_effective_address(regs, insn);
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic noinline void do_fault_siginfo(int code, int sig, struct pt_regs *regs,
1528c2ecf20Sopenharmony_ci				      int text_fault)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	unsigned long addr = compute_si_addr(regs, text_fault);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	__do_fault_siginfo(code, sig, regs, addr);
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ciasmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write,
1608c2ecf20Sopenharmony_ci			       unsigned long address)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	struct vm_area_struct *vma;
1638c2ecf20Sopenharmony_ci	struct task_struct *tsk = current;
1648c2ecf20Sopenharmony_ci	struct mm_struct *mm = tsk->mm;
1658c2ecf20Sopenharmony_ci	unsigned int fixup;
1668c2ecf20Sopenharmony_ci	unsigned long g2;
1678c2ecf20Sopenharmony_ci	int from_user = !(regs->psr & PSR_PS);
1688c2ecf20Sopenharmony_ci	int code;
1698c2ecf20Sopenharmony_ci	vm_fault_t fault;
1708c2ecf20Sopenharmony_ci	unsigned int flags = FAULT_FLAG_DEFAULT;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	if (text_fault)
1738c2ecf20Sopenharmony_ci		address = regs->pc;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	/*
1768c2ecf20Sopenharmony_ci	 * We fault-in kernel-space virtual memory on-demand. The
1778c2ecf20Sopenharmony_ci	 * 'reference' page table is init_mm.pgd.
1788c2ecf20Sopenharmony_ci	 *
1798c2ecf20Sopenharmony_ci	 * NOTE! We MUST NOT take any locks for this case. We may
1808c2ecf20Sopenharmony_ci	 * be in an interrupt or a critical region, and should
1818c2ecf20Sopenharmony_ci	 * only copy the information from the master page table,
1828c2ecf20Sopenharmony_ci	 * nothing more.
1838c2ecf20Sopenharmony_ci	 */
1848c2ecf20Sopenharmony_ci	code = SEGV_MAPERR;
1858c2ecf20Sopenharmony_ci	if (address >= TASK_SIZE)
1868c2ecf20Sopenharmony_ci		goto vmalloc_fault;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/*
1898c2ecf20Sopenharmony_ci	 * If we're in an interrupt or have no user
1908c2ecf20Sopenharmony_ci	 * context, we must not take the fault..
1918c2ecf20Sopenharmony_ci	 */
1928c2ecf20Sopenharmony_ci	if (pagefault_disabled() || !mm)
1938c2ecf20Sopenharmony_ci		goto no_context;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ciretry:
1988c2ecf20Sopenharmony_ci	mmap_read_lock(mm);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	if (!from_user && address >= PAGE_OFFSET)
2018c2ecf20Sopenharmony_ci		goto bad_area;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	vma = find_vma(mm, address);
2048c2ecf20Sopenharmony_ci	if (!vma)
2058c2ecf20Sopenharmony_ci		goto bad_area;
2068c2ecf20Sopenharmony_ci	if (vma->vm_start <= address)
2078c2ecf20Sopenharmony_ci		goto good_area;
2088c2ecf20Sopenharmony_ci	if (!(vma->vm_flags & VM_GROWSDOWN))
2098c2ecf20Sopenharmony_ci		goto bad_area;
2108c2ecf20Sopenharmony_ci	if (expand_stack(vma, address))
2118c2ecf20Sopenharmony_ci		goto bad_area;
2128c2ecf20Sopenharmony_ci	/*
2138c2ecf20Sopenharmony_ci	 * Ok, we have a good vm_area for this memory access, so
2148c2ecf20Sopenharmony_ci	 * we can handle it..
2158c2ecf20Sopenharmony_ci	 */
2168c2ecf20Sopenharmony_cigood_area:
2178c2ecf20Sopenharmony_ci	code = SEGV_ACCERR;
2188c2ecf20Sopenharmony_ci	if (write) {
2198c2ecf20Sopenharmony_ci		if (!(vma->vm_flags & VM_WRITE))
2208c2ecf20Sopenharmony_ci			goto bad_area;
2218c2ecf20Sopenharmony_ci	} else {
2228c2ecf20Sopenharmony_ci		/* Allow reads even for write-only mappings */
2238c2ecf20Sopenharmony_ci		if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
2248c2ecf20Sopenharmony_ci			goto bad_area;
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (from_user)
2288c2ecf20Sopenharmony_ci		flags |= FAULT_FLAG_USER;
2298c2ecf20Sopenharmony_ci	if (write)
2308c2ecf20Sopenharmony_ci		flags |= FAULT_FLAG_WRITE;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	/*
2338c2ecf20Sopenharmony_ci	 * If for any reason at all we couldn't handle the fault,
2348c2ecf20Sopenharmony_ci	 * make sure we exit gracefully rather than endlessly redo
2358c2ecf20Sopenharmony_ci	 * the fault.
2368c2ecf20Sopenharmony_ci	 */
2378c2ecf20Sopenharmony_ci	fault = handle_mm_fault(vma, address, flags, regs);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	if (fault_signal_pending(fault, regs))
2408c2ecf20Sopenharmony_ci		return;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	if (unlikely(fault & VM_FAULT_ERROR)) {
2438c2ecf20Sopenharmony_ci		if (fault & VM_FAULT_OOM)
2448c2ecf20Sopenharmony_ci			goto out_of_memory;
2458c2ecf20Sopenharmony_ci		else if (fault & VM_FAULT_SIGSEGV)
2468c2ecf20Sopenharmony_ci			goto bad_area;
2478c2ecf20Sopenharmony_ci		else if (fault & VM_FAULT_SIGBUS)
2488c2ecf20Sopenharmony_ci			goto do_sigbus;
2498c2ecf20Sopenharmony_ci		BUG();
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	if (flags & FAULT_FLAG_ALLOW_RETRY) {
2538c2ecf20Sopenharmony_ci		if (fault & VM_FAULT_RETRY) {
2548c2ecf20Sopenharmony_ci			flags |= FAULT_FLAG_TRIED;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci			/* No need to mmap_read_unlock(mm) as we would
2578c2ecf20Sopenharmony_ci			 * have already released it in __lock_page_or_retry
2588c2ecf20Sopenharmony_ci			 * in mm/filemap.c.
2598c2ecf20Sopenharmony_ci			 */
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci			goto retry;
2628c2ecf20Sopenharmony_ci		}
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	mmap_read_unlock(mm);
2668c2ecf20Sopenharmony_ci	return;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	/*
2698c2ecf20Sopenharmony_ci	 * Something tried to access memory that isn't in our memory map..
2708c2ecf20Sopenharmony_ci	 * Fix it, but check if it's kernel or user first..
2718c2ecf20Sopenharmony_ci	 */
2728c2ecf20Sopenharmony_cibad_area:
2738c2ecf20Sopenharmony_ci	mmap_read_unlock(mm);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cibad_area_nosemaphore:
2768c2ecf20Sopenharmony_ci	/* User mode accesses just cause a SIGSEGV */
2778c2ecf20Sopenharmony_ci	if (from_user) {
2788c2ecf20Sopenharmony_ci		do_fault_siginfo(code, SIGSEGV, regs, text_fault);
2798c2ecf20Sopenharmony_ci		return;
2808c2ecf20Sopenharmony_ci	}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	/* Is this in ex_table? */
2838c2ecf20Sopenharmony_cino_context:
2848c2ecf20Sopenharmony_ci	g2 = regs->u_regs[UREG_G2];
2858c2ecf20Sopenharmony_ci	if (!from_user) {
2868c2ecf20Sopenharmony_ci		fixup = search_extables_range(regs->pc, &g2);
2878c2ecf20Sopenharmony_ci		/* Values below 10 are reserved for other things */
2888c2ecf20Sopenharmony_ci		if (fixup > 10) {
2898c2ecf20Sopenharmony_ci			extern const unsigned int __memset_start[];
2908c2ecf20Sopenharmony_ci			extern const unsigned int __memset_end[];
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci#ifdef DEBUG_EXCEPTIONS
2938c2ecf20Sopenharmony_ci			printk("Exception: PC<%08lx> faddr<%08lx>\n",
2948c2ecf20Sopenharmony_ci			       regs->pc, address);
2958c2ecf20Sopenharmony_ci			printk("EX_TABLE: insn<%08lx> fixup<%08x> g2<%08lx>\n",
2968c2ecf20Sopenharmony_ci				regs->pc, fixup, g2);
2978c2ecf20Sopenharmony_ci#endif
2988c2ecf20Sopenharmony_ci			if ((regs->pc >= (unsigned long)__memset_start &&
2998c2ecf20Sopenharmony_ci			     regs->pc < (unsigned long)__memset_end)) {
3008c2ecf20Sopenharmony_ci				regs->u_regs[UREG_I4] = address;
3018c2ecf20Sopenharmony_ci				regs->u_regs[UREG_I5] = regs->pc;
3028c2ecf20Sopenharmony_ci			}
3038c2ecf20Sopenharmony_ci			regs->u_regs[UREG_G2] = g2;
3048c2ecf20Sopenharmony_ci			regs->pc = fixup;
3058c2ecf20Sopenharmony_ci			regs->npc = regs->pc + 4;
3068c2ecf20Sopenharmony_ci			return;
3078c2ecf20Sopenharmony_ci		}
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	unhandled_fault(address, tsk, regs);
3118c2ecf20Sopenharmony_ci	do_exit(SIGKILL);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci/*
3148c2ecf20Sopenharmony_ci * We ran out of memory, or some other thing happened to us that made
3158c2ecf20Sopenharmony_ci * us unable to handle the page fault gracefully.
3168c2ecf20Sopenharmony_ci */
3178c2ecf20Sopenharmony_ciout_of_memory:
3188c2ecf20Sopenharmony_ci	mmap_read_unlock(mm);
3198c2ecf20Sopenharmony_ci	if (from_user) {
3208c2ecf20Sopenharmony_ci		pagefault_out_of_memory();
3218c2ecf20Sopenharmony_ci		return;
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci	goto no_context;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cido_sigbus:
3268c2ecf20Sopenharmony_ci	mmap_read_unlock(mm);
3278c2ecf20Sopenharmony_ci	do_fault_siginfo(BUS_ADRERR, SIGBUS, regs, text_fault);
3288c2ecf20Sopenharmony_ci	if (!from_user)
3298c2ecf20Sopenharmony_ci		goto no_context;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_civmalloc_fault:
3328c2ecf20Sopenharmony_ci	{
3338c2ecf20Sopenharmony_ci		/*
3348c2ecf20Sopenharmony_ci		 * Synchronize this task's top level page-table
3358c2ecf20Sopenharmony_ci		 * with the 'reference' page table.
3368c2ecf20Sopenharmony_ci		 */
3378c2ecf20Sopenharmony_ci		int offset = pgd_index(address);
3388c2ecf20Sopenharmony_ci		pgd_t *pgd, *pgd_k;
3398c2ecf20Sopenharmony_ci		p4d_t *p4d, *p4d_k;
3408c2ecf20Sopenharmony_ci		pud_t *pud, *pud_k;
3418c2ecf20Sopenharmony_ci		pmd_t *pmd, *pmd_k;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci		pgd = tsk->active_mm->pgd + offset;
3448c2ecf20Sopenharmony_ci		pgd_k = init_mm.pgd + offset;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci		if (!pgd_present(*pgd)) {
3478c2ecf20Sopenharmony_ci			if (!pgd_present(*pgd_k))
3488c2ecf20Sopenharmony_ci				goto bad_area_nosemaphore;
3498c2ecf20Sopenharmony_ci			pgd_val(*pgd) = pgd_val(*pgd_k);
3508c2ecf20Sopenharmony_ci			return;
3518c2ecf20Sopenharmony_ci		}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci		p4d = p4d_offset(pgd, address);
3548c2ecf20Sopenharmony_ci		pud = pud_offset(p4d, address);
3558c2ecf20Sopenharmony_ci		pmd = pmd_offset(pud, address);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci		p4d_k = p4d_offset(pgd_k, address);
3588c2ecf20Sopenharmony_ci		pud_k = pud_offset(p4d_k, address);
3598c2ecf20Sopenharmony_ci		pmd_k = pmd_offset(pud_k, address);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci		if (pmd_present(*pmd) || !pmd_present(*pmd_k))
3628c2ecf20Sopenharmony_ci			goto bad_area_nosemaphore;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci		*pmd = *pmd_k;
3658c2ecf20Sopenharmony_ci		return;
3668c2ecf20Sopenharmony_ci	}
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci/* This always deals with user addresses. */
3708c2ecf20Sopenharmony_cistatic void force_user_fault(unsigned long address, int write)
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci	struct vm_area_struct *vma;
3738c2ecf20Sopenharmony_ci	struct task_struct *tsk = current;
3748c2ecf20Sopenharmony_ci	struct mm_struct *mm = tsk->mm;
3758c2ecf20Sopenharmony_ci	unsigned int flags = FAULT_FLAG_USER;
3768c2ecf20Sopenharmony_ci	int code;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	code = SEGV_MAPERR;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	mmap_read_lock(mm);
3818c2ecf20Sopenharmony_ci	vma = find_vma(mm, address);
3828c2ecf20Sopenharmony_ci	if (!vma)
3838c2ecf20Sopenharmony_ci		goto bad_area;
3848c2ecf20Sopenharmony_ci	if (vma->vm_start <= address)
3858c2ecf20Sopenharmony_ci		goto good_area;
3868c2ecf20Sopenharmony_ci	if (!(vma->vm_flags & VM_GROWSDOWN))
3878c2ecf20Sopenharmony_ci		goto bad_area;
3888c2ecf20Sopenharmony_ci	if (expand_stack(vma, address))
3898c2ecf20Sopenharmony_ci		goto bad_area;
3908c2ecf20Sopenharmony_cigood_area:
3918c2ecf20Sopenharmony_ci	code = SEGV_ACCERR;
3928c2ecf20Sopenharmony_ci	if (write) {
3938c2ecf20Sopenharmony_ci		if (!(vma->vm_flags & VM_WRITE))
3948c2ecf20Sopenharmony_ci			goto bad_area;
3958c2ecf20Sopenharmony_ci		flags |= FAULT_FLAG_WRITE;
3968c2ecf20Sopenharmony_ci	} else {
3978c2ecf20Sopenharmony_ci		if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
3988c2ecf20Sopenharmony_ci			goto bad_area;
3998c2ecf20Sopenharmony_ci	}
4008c2ecf20Sopenharmony_ci	switch (handle_mm_fault(vma, address, flags, NULL)) {
4018c2ecf20Sopenharmony_ci	case VM_FAULT_SIGBUS:
4028c2ecf20Sopenharmony_ci	case VM_FAULT_OOM:
4038c2ecf20Sopenharmony_ci		goto do_sigbus;
4048c2ecf20Sopenharmony_ci	}
4058c2ecf20Sopenharmony_ci	mmap_read_unlock(mm);
4068c2ecf20Sopenharmony_ci	return;
4078c2ecf20Sopenharmony_cibad_area:
4088c2ecf20Sopenharmony_ci	mmap_read_unlock(mm);
4098c2ecf20Sopenharmony_ci	__do_fault_siginfo(code, SIGSEGV, tsk->thread.kregs, address);
4108c2ecf20Sopenharmony_ci	return;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cido_sigbus:
4138c2ecf20Sopenharmony_ci	mmap_read_unlock(mm);
4148c2ecf20Sopenharmony_ci	__do_fault_siginfo(BUS_ADRERR, SIGBUS, tsk->thread.kregs, address);
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic void check_stack_aligned(unsigned long sp)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	if (sp & 0x7UL)
4208c2ecf20Sopenharmony_ci		force_sig(SIGILL);
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_civoid window_overflow_fault(void)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	unsigned long sp;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	sp = current_thread_info()->rwbuf_stkptrs[0];
4288c2ecf20Sopenharmony_ci	if (((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK))
4298c2ecf20Sopenharmony_ci		force_user_fault(sp + 0x38, 1);
4308c2ecf20Sopenharmony_ci	force_user_fault(sp, 1);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	check_stack_aligned(sp);
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_civoid window_underflow_fault(unsigned long sp)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	if (((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK))
4388c2ecf20Sopenharmony_ci		force_user_fault(sp + 0x38, 0);
4398c2ecf20Sopenharmony_ci	force_user_fault(sp, 0);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	check_stack_aligned(sp);
4428c2ecf20Sopenharmony_ci}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_civoid window_ret_fault(struct pt_regs *regs)
4458c2ecf20Sopenharmony_ci{
4468c2ecf20Sopenharmony_ci	unsigned long sp;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	sp = regs->u_regs[UREG_FP];
4498c2ecf20Sopenharmony_ci	if (((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK))
4508c2ecf20Sopenharmony_ci		force_user_fault(sp + 0x38, 0);
4518c2ecf20Sopenharmony_ci	force_user_fault(sp, 0);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	check_stack_aligned(sp);
4548c2ecf20Sopenharmony_ci}
455