162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  linux/arch/m68k/mm/fault.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 1995  Hamish Macdonald
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/mman.h>
962306a36Sopenharmony_ci#include <linux/mm.h>
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/ptrace.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/uaccess.h>
1562306a36Sopenharmony_ci#include <linux/perf_event.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <asm/setup.h>
1862306a36Sopenharmony_ci#include <asm/traps.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ciextern void die_if_kernel(char *, struct pt_regs *, long);
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ciint send_fault_sig(struct pt_regs *regs)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	int signo, si_code;
2562306a36Sopenharmony_ci	void __user *addr;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	signo = current->thread.signo;
2862306a36Sopenharmony_ci	si_code = current->thread.code;
2962306a36Sopenharmony_ci	addr = (void __user *)current->thread.faddr;
3062306a36Sopenharmony_ci	pr_debug("send_fault_sig: %p,%d,%d\n", addr, signo, si_code);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	if (user_mode(regs)) {
3362306a36Sopenharmony_ci		force_sig_fault(signo, si_code, addr);
3462306a36Sopenharmony_ci	} else {
3562306a36Sopenharmony_ci		if (fixup_exception(regs))
3662306a36Sopenharmony_ci			return -1;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci		//if (signo == SIGBUS)
3962306a36Sopenharmony_ci		//	force_sig_fault(si_signo, si_code, addr);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci		/*
4262306a36Sopenharmony_ci		 * Oops. The kernel tried to access some bad page. We'll have to
4362306a36Sopenharmony_ci		 * terminate things with extreme prejudice.
4462306a36Sopenharmony_ci		 */
4562306a36Sopenharmony_ci		if ((unsigned long)addr < PAGE_SIZE)
4662306a36Sopenharmony_ci			pr_alert("Unable to handle kernel NULL pointer dereference");
4762306a36Sopenharmony_ci		else
4862306a36Sopenharmony_ci			pr_alert("Unable to handle kernel access");
4962306a36Sopenharmony_ci		pr_cont(" at virtual address %p\n", addr);
5062306a36Sopenharmony_ci		die_if_kernel("Oops", regs, 0 /*error_code*/);
5162306a36Sopenharmony_ci		make_task_dead(SIGKILL);
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	return 1;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/*
5862306a36Sopenharmony_ci * This routine handles page faults.  It determines the problem, and
5962306a36Sopenharmony_ci * then passes it off to one of the appropriate routines.
6062306a36Sopenharmony_ci *
6162306a36Sopenharmony_ci * error_code:
6262306a36Sopenharmony_ci *	bit 0 == 0 means no page found, 1 means protection fault
6362306a36Sopenharmony_ci *	bit 1 == 0 means read, 1 means write
6462306a36Sopenharmony_ci *
6562306a36Sopenharmony_ci * If this routine detects a bad access, it returns 1, otherwise it
6662306a36Sopenharmony_ci * returns 0.
6762306a36Sopenharmony_ci */
6862306a36Sopenharmony_ciint do_page_fault(struct pt_regs *regs, unsigned long address,
6962306a36Sopenharmony_ci			      unsigned long error_code)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	struct mm_struct *mm = current->mm;
7262306a36Sopenharmony_ci	struct vm_area_struct * vma;
7362306a36Sopenharmony_ci	vm_fault_t fault;
7462306a36Sopenharmony_ci	unsigned int flags = FAULT_FLAG_DEFAULT;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	pr_debug("do page fault:\nregs->sr=%#x, regs->pc=%#lx, address=%#lx, %ld, %p\n",
7762306a36Sopenharmony_ci		regs->sr, regs->pc, address, error_code, mm ? mm->pgd : NULL);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	/*
8062306a36Sopenharmony_ci	 * If we're in an interrupt or have no user
8162306a36Sopenharmony_ci	 * context, we must not take the fault..
8262306a36Sopenharmony_ci	 */
8362306a36Sopenharmony_ci	if (faulthandler_disabled() || !mm)
8462306a36Sopenharmony_ci		goto no_context;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	if (user_mode(regs))
8762306a36Sopenharmony_ci		flags |= FAULT_FLAG_USER;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
9062306a36Sopenharmony_ciretry:
9162306a36Sopenharmony_ci	mmap_read_lock(mm);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	vma = find_vma(mm, address);
9462306a36Sopenharmony_ci	if (!vma)
9562306a36Sopenharmony_ci		goto map_err;
9662306a36Sopenharmony_ci	if (vma->vm_start <= address)
9762306a36Sopenharmony_ci		goto good_area;
9862306a36Sopenharmony_ci	if (!(vma->vm_flags & VM_GROWSDOWN))
9962306a36Sopenharmony_ci		goto map_err;
10062306a36Sopenharmony_ci	if (user_mode(regs)) {
10162306a36Sopenharmony_ci		/* Accessing the stack below usp is always a bug.  The
10262306a36Sopenharmony_ci		   "+ 256" is there due to some instructions doing
10362306a36Sopenharmony_ci		   pre-decrement on the stack and that doesn't show up
10462306a36Sopenharmony_ci		   until later.  */
10562306a36Sopenharmony_ci		if (address + 256 < rdusp())
10662306a36Sopenharmony_ci			goto map_err;
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci	vma = expand_stack(mm, address);
10962306a36Sopenharmony_ci	if (!vma)
11062306a36Sopenharmony_ci		goto map_err_nosemaphore;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/*
11362306a36Sopenharmony_ci * Ok, we have a good vm_area for this memory access, so
11462306a36Sopenharmony_ci * we can handle it..
11562306a36Sopenharmony_ci */
11662306a36Sopenharmony_cigood_area:
11762306a36Sopenharmony_ci	pr_debug("do_page_fault: good_area\n");
11862306a36Sopenharmony_ci	switch (error_code & 3) {
11962306a36Sopenharmony_ci		default:	/* 3: write, present */
12062306a36Sopenharmony_ci			fallthrough;
12162306a36Sopenharmony_ci		case 2:		/* write, not present */
12262306a36Sopenharmony_ci			if (!(vma->vm_flags & VM_WRITE))
12362306a36Sopenharmony_ci				goto acc_err;
12462306a36Sopenharmony_ci			flags |= FAULT_FLAG_WRITE;
12562306a36Sopenharmony_ci			break;
12662306a36Sopenharmony_ci		case 1:		/* read, present */
12762306a36Sopenharmony_ci			goto acc_err;
12862306a36Sopenharmony_ci		case 0:		/* read, not present */
12962306a36Sopenharmony_ci			if (unlikely(!vma_is_accessible(vma)))
13062306a36Sopenharmony_ci				goto acc_err;
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	/*
13462306a36Sopenharmony_ci	 * If for any reason at all we couldn't handle the fault,
13562306a36Sopenharmony_ci	 * make sure we exit gracefully rather than endlessly redo
13662306a36Sopenharmony_ci	 * the fault.
13762306a36Sopenharmony_ci	 */
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	fault = handle_mm_fault(vma, address, flags, regs);
14062306a36Sopenharmony_ci	pr_debug("handle_mm_fault returns %x\n", fault);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	if (fault_signal_pending(fault, regs)) {
14362306a36Sopenharmony_ci		if (!user_mode(regs))
14462306a36Sopenharmony_ci			goto no_context;
14562306a36Sopenharmony_ci		return 0;
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	/* The fault is fully completed (including releasing mmap lock) */
14962306a36Sopenharmony_ci	if (fault & VM_FAULT_COMPLETED)
15062306a36Sopenharmony_ci		return 0;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (unlikely(fault & VM_FAULT_ERROR)) {
15362306a36Sopenharmony_ci		if (fault & VM_FAULT_OOM)
15462306a36Sopenharmony_ci			goto out_of_memory;
15562306a36Sopenharmony_ci		else if (fault & VM_FAULT_SIGSEGV)
15662306a36Sopenharmony_ci			goto map_err;
15762306a36Sopenharmony_ci		else if (fault & VM_FAULT_SIGBUS)
15862306a36Sopenharmony_ci			goto bus_err;
15962306a36Sopenharmony_ci		BUG();
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	if (fault & VM_FAULT_RETRY) {
16362306a36Sopenharmony_ci		flags |= FAULT_FLAG_TRIED;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci		/*
16662306a36Sopenharmony_ci		 * No need to mmap_read_unlock(mm) as we would
16762306a36Sopenharmony_ci		 * have already released it in __lock_page_or_retry
16862306a36Sopenharmony_ci		 * in mm/filemap.c.
16962306a36Sopenharmony_ci		 */
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci		goto retry;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	mmap_read_unlock(mm);
17562306a36Sopenharmony_ci	return 0;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci/*
17862306a36Sopenharmony_ci * We ran out of memory, or some other thing happened to us that made
17962306a36Sopenharmony_ci * us unable to handle the page fault gracefully.
18062306a36Sopenharmony_ci */
18162306a36Sopenharmony_ciout_of_memory:
18262306a36Sopenharmony_ci	mmap_read_unlock(mm);
18362306a36Sopenharmony_ci	if (!user_mode(regs))
18462306a36Sopenharmony_ci		goto no_context;
18562306a36Sopenharmony_ci	pagefault_out_of_memory();
18662306a36Sopenharmony_ci	return 0;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cino_context:
18962306a36Sopenharmony_ci	current->thread.signo = SIGBUS;
19062306a36Sopenharmony_ci	current->thread.faddr = address;
19162306a36Sopenharmony_ci	return send_fault_sig(regs);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cibus_err:
19462306a36Sopenharmony_ci	current->thread.signo = SIGBUS;
19562306a36Sopenharmony_ci	current->thread.code = BUS_ADRERR;
19662306a36Sopenharmony_ci	current->thread.faddr = address;
19762306a36Sopenharmony_ci	goto send_sig;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cimap_err:
20062306a36Sopenharmony_ci	mmap_read_unlock(mm);
20162306a36Sopenharmony_cimap_err_nosemaphore:
20262306a36Sopenharmony_ci	current->thread.signo = SIGSEGV;
20362306a36Sopenharmony_ci	current->thread.code = SEGV_MAPERR;
20462306a36Sopenharmony_ci	current->thread.faddr = address;
20562306a36Sopenharmony_ci	return send_fault_sig(regs);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ciacc_err:
20862306a36Sopenharmony_ci	current->thread.signo = SIGSEGV;
20962306a36Sopenharmony_ci	current->thread.code = SEGV_ACCERR;
21062306a36Sopenharmony_ci	current->thread.faddr = address;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cisend_sig:
21362306a36Sopenharmony_ci	mmap_read_unlock(mm);
21462306a36Sopenharmony_ci	return send_fault_sig(regs);
21562306a36Sopenharmony_ci}
216