162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * MMU fault handling support.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 1998-2002 Hewlett-Packard Co
662306a36Sopenharmony_ci *	David Mosberger-Tang <davidm@hpl.hp.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#include <linux/sched/signal.h>
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/mm.h>
1162306a36Sopenharmony_ci#include <linux/extable.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/kprobes.h>
1462306a36Sopenharmony_ci#include <linux/kdebug.h>
1562306a36Sopenharmony_ci#include <linux/prefetch.h>
1662306a36Sopenharmony_ci#include <linux/uaccess.h>
1762306a36Sopenharmony_ci#include <linux/perf_event.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <asm/processor.h>
2062306a36Sopenharmony_ci#include <asm/exception.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ciextern int die(char *, struct pt_regs *, long);
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/*
2562306a36Sopenharmony_ci * Return TRUE if ADDRESS points at a page in the kernel's mapped segment
2662306a36Sopenharmony_ci * (inside region 5, on ia64) and that page is present.
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_cistatic int
2962306a36Sopenharmony_cimapped_kernel_page_is_present (unsigned long address)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	pgd_t *pgd;
3262306a36Sopenharmony_ci	p4d_t *p4d;
3362306a36Sopenharmony_ci	pud_t *pud;
3462306a36Sopenharmony_ci	pmd_t *pmd;
3562306a36Sopenharmony_ci	pte_t *ptep, pte;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	pgd = pgd_offset_k(address);
3862306a36Sopenharmony_ci	if (pgd_none(*pgd) || pgd_bad(*pgd))
3962306a36Sopenharmony_ci		return 0;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	p4d = p4d_offset(pgd, address);
4262306a36Sopenharmony_ci	if (p4d_none(*p4d) || p4d_bad(*p4d))
4362306a36Sopenharmony_ci		return 0;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	pud = pud_offset(p4d, address);
4662306a36Sopenharmony_ci	if (pud_none(*pud) || pud_bad(*pud))
4762306a36Sopenharmony_ci		return 0;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	pmd = pmd_offset(pud, address);
5062306a36Sopenharmony_ci	if (pmd_none(*pmd) || pmd_bad(*pmd))
5162306a36Sopenharmony_ci		return 0;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	ptep = pte_offset_kernel(pmd, address);
5462306a36Sopenharmony_ci	if (!ptep)
5562306a36Sopenharmony_ci		return 0;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	pte = *ptep;
5862306a36Sopenharmony_ci	return pte_present(pte);
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#	define VM_READ_BIT	0
6262306a36Sopenharmony_ci#	define VM_WRITE_BIT	1
6362306a36Sopenharmony_ci#	define VM_EXEC_BIT	2
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_civoid __kprobes
6662306a36Sopenharmony_ciia64_do_page_fault (unsigned long address, unsigned long isr, struct pt_regs *regs)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	int signal = SIGSEGV, code = SEGV_MAPERR;
6962306a36Sopenharmony_ci	struct vm_area_struct *vma, *prev_vma;
7062306a36Sopenharmony_ci	struct mm_struct *mm = current->mm;
7162306a36Sopenharmony_ci	unsigned long mask;
7262306a36Sopenharmony_ci	vm_fault_t fault;
7362306a36Sopenharmony_ci	unsigned int flags = FAULT_FLAG_DEFAULT;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	mask = ((((isr >> IA64_ISR_X_BIT) & 1UL) << VM_EXEC_BIT)
7662306a36Sopenharmony_ci		| (((isr >> IA64_ISR_W_BIT) & 1UL) << VM_WRITE_BIT));
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	/* mmap_lock is performance critical.... */
7962306a36Sopenharmony_ci	prefetchw(&mm->mmap_lock);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	/*
8262306a36Sopenharmony_ci	 * If we're in an interrupt or have no user context, we must not take the fault..
8362306a36Sopenharmony_ci	 */
8462306a36Sopenharmony_ci	if (faulthandler_disabled() || !mm)
8562306a36Sopenharmony_ci		goto no_context;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	/*
8862306a36Sopenharmony_ci	 * This is to handle the kprobes on user space access instructions
8962306a36Sopenharmony_ci	 */
9062306a36Sopenharmony_ci	if (kprobe_page_fault(regs, TRAP_BRKPT))
9162306a36Sopenharmony_ci		return;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (user_mode(regs))
9462306a36Sopenharmony_ci		flags |= FAULT_FLAG_USER;
9562306a36Sopenharmony_ci	if (mask & VM_WRITE)
9662306a36Sopenharmony_ci		flags |= FAULT_FLAG_WRITE;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
9962306a36Sopenharmony_ciretry:
10062306a36Sopenharmony_ci	mmap_read_lock(mm);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	vma = find_vma_prev(mm, address, &prev_vma);
10362306a36Sopenharmony_ci	if (!vma && !prev_vma )
10462306a36Sopenharmony_ci		goto bad_area;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci        /*
10762306a36Sopenharmony_ci         * find_vma_prev() returns vma such that address < vma->vm_end or NULL
10862306a36Sopenharmony_ci         *
10962306a36Sopenharmony_ci         * May find no vma, but could be that the last vm area is the
11062306a36Sopenharmony_ci         * register backing store that needs to expand upwards, in
11162306a36Sopenharmony_ci         * this case vma will be null, but prev_vma will ne non-null
11262306a36Sopenharmony_ci         */
11362306a36Sopenharmony_ci        if (( !vma && prev_vma ) || (address < vma->vm_start) ) {
11462306a36Sopenharmony_ci		vma = expand_stack(mm, address);
11562306a36Sopenharmony_ci		if (!vma)
11662306a36Sopenharmony_ci			goto bad_area_nosemaphore;
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	code = SEGV_ACCERR;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	/* OK, we've got a good vm_area for this memory area.  Check the access permissions: */
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci#	if (((1 << VM_READ_BIT) != VM_READ || (1 << VM_WRITE_BIT) != VM_WRITE) \
12462306a36Sopenharmony_ci	    || (1 << VM_EXEC_BIT) != VM_EXEC)
12562306a36Sopenharmony_ci#		error File is out of sync with <linux/mm.h>.  Please update.
12662306a36Sopenharmony_ci#	endif
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	if (((isr >> IA64_ISR_R_BIT) & 1UL) && (!(vma->vm_flags & (VM_READ | VM_WRITE))))
12962306a36Sopenharmony_ci		goto bad_area;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if ((vma->vm_flags & mask) != mask)
13262306a36Sopenharmony_ci		goto bad_area;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	/*
13562306a36Sopenharmony_ci	 * If for any reason at all we couldn't handle the fault, make
13662306a36Sopenharmony_ci	 * sure we exit gracefully rather than endlessly redo the
13762306a36Sopenharmony_ci	 * fault.
13862306a36Sopenharmony_ci	 */
13962306a36Sopenharmony_ci	fault = handle_mm_fault(vma, address, flags, regs);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	if (fault_signal_pending(fault, regs)) {
14262306a36Sopenharmony_ci		if (!user_mode(regs))
14362306a36Sopenharmony_ci			goto no_context;
14462306a36Sopenharmony_ci		return;
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	/* The fault is fully completed (including releasing mmap lock) */
14862306a36Sopenharmony_ci	if (fault & VM_FAULT_COMPLETED)
14962306a36Sopenharmony_ci		return;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (unlikely(fault & VM_FAULT_ERROR)) {
15262306a36Sopenharmony_ci		/*
15362306a36Sopenharmony_ci		 * We ran out of memory, or some other thing happened
15462306a36Sopenharmony_ci		 * to us that made us unable to handle the page fault
15562306a36Sopenharmony_ci		 * gracefully.
15662306a36Sopenharmony_ci		 */
15762306a36Sopenharmony_ci		if (fault & VM_FAULT_OOM) {
15862306a36Sopenharmony_ci			goto out_of_memory;
15962306a36Sopenharmony_ci		} else if (fault & VM_FAULT_SIGSEGV) {
16062306a36Sopenharmony_ci			goto bad_area;
16162306a36Sopenharmony_ci		} else if (fault & VM_FAULT_SIGBUS) {
16262306a36Sopenharmony_ci			signal = SIGBUS;
16362306a36Sopenharmony_ci			goto bad_area;
16462306a36Sopenharmony_ci		}
16562306a36Sopenharmony_ci		BUG();
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if (fault & VM_FAULT_RETRY) {
16962306a36Sopenharmony_ci		flags |= FAULT_FLAG_TRIED;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci		/* No need to mmap_read_unlock(mm) as we would
17262306a36Sopenharmony_ci		 * have already released it in __lock_page_or_retry
17362306a36Sopenharmony_ci		 * in mm/filemap.c.
17462306a36Sopenharmony_ci		 */
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci		goto retry;
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	mmap_read_unlock(mm);
18062306a36Sopenharmony_ci	return;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci  bad_area:
18362306a36Sopenharmony_ci	mmap_read_unlock(mm);
18462306a36Sopenharmony_ci  bad_area_nosemaphore:
18562306a36Sopenharmony_ci	if ((isr & IA64_ISR_SP)
18662306a36Sopenharmony_ci	    || ((isr & IA64_ISR_NA) && (isr & IA64_ISR_CODE_MASK) == IA64_ISR_CODE_LFETCH))
18762306a36Sopenharmony_ci	{
18862306a36Sopenharmony_ci		/*
18962306a36Sopenharmony_ci		 * This fault was due to a speculative load or lfetch.fault, set the "ed"
19062306a36Sopenharmony_ci		 * bit in the psr to ensure forward progress.  (Target register will get a
19162306a36Sopenharmony_ci		 * NaT for ld.s, lfetch will be canceled.)
19262306a36Sopenharmony_ci		 */
19362306a36Sopenharmony_ci		ia64_psr(regs)->ed = 1;
19462306a36Sopenharmony_ci		return;
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci	if (user_mode(regs)) {
19762306a36Sopenharmony_ci		force_sig_fault(signal, code, (void __user *) address,
19862306a36Sopenharmony_ci				0, __ISR_VALID, isr);
19962306a36Sopenharmony_ci		return;
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci  no_context:
20362306a36Sopenharmony_ci	if ((isr & IA64_ISR_SP)
20462306a36Sopenharmony_ci	    || ((isr & IA64_ISR_NA) && (isr & IA64_ISR_CODE_MASK) == IA64_ISR_CODE_LFETCH))
20562306a36Sopenharmony_ci	{
20662306a36Sopenharmony_ci		/*
20762306a36Sopenharmony_ci		 * This fault was due to a speculative load or lfetch.fault, set the "ed"
20862306a36Sopenharmony_ci		 * bit in the psr to ensure forward progress.  (Target register will get a
20962306a36Sopenharmony_ci		 * NaT for ld.s, lfetch will be canceled.)
21062306a36Sopenharmony_ci		 */
21162306a36Sopenharmony_ci		ia64_psr(regs)->ed = 1;
21262306a36Sopenharmony_ci		return;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/*
21662306a36Sopenharmony_ci	 * Since we have no vma's for region 5, we might get here even if the address is
21762306a36Sopenharmony_ci	 * valid, due to the VHPT walker inserting a non present translation that becomes
21862306a36Sopenharmony_ci	 * stale. If that happens, the non present fault handler already purged the stale
21962306a36Sopenharmony_ci	 * translation, which fixed the problem. So, we check to see if the translation is
22062306a36Sopenharmony_ci	 * valid, and return if it is.
22162306a36Sopenharmony_ci	 */
22262306a36Sopenharmony_ci	if (REGION_NUMBER(address) == 5 && mapped_kernel_page_is_present(address))
22362306a36Sopenharmony_ci		return;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	if (ia64_done_with_exception(regs))
22662306a36Sopenharmony_ci		return;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	/*
22962306a36Sopenharmony_ci	 * Oops. The kernel tried to access some bad page. We'll have to terminate things
23062306a36Sopenharmony_ci	 * with extreme prejudice.
23162306a36Sopenharmony_ci	 */
23262306a36Sopenharmony_ci	bust_spinlocks(1);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (address < PAGE_SIZE)
23562306a36Sopenharmony_ci		printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference (address %016lx)\n", address);
23662306a36Sopenharmony_ci	else
23762306a36Sopenharmony_ci		printk(KERN_ALERT "Unable to handle kernel paging request at "
23862306a36Sopenharmony_ci		       "virtual address %016lx\n", address);
23962306a36Sopenharmony_ci	if (die("Oops", regs, isr))
24062306a36Sopenharmony_ci		regs = NULL;
24162306a36Sopenharmony_ci	bust_spinlocks(0);
24262306a36Sopenharmony_ci	if (regs)
24362306a36Sopenharmony_ci		make_task_dead(SIGKILL);
24462306a36Sopenharmony_ci	return;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci  out_of_memory:
24762306a36Sopenharmony_ci	mmap_read_unlock(mm);
24862306a36Sopenharmony_ci	if (!user_mode(regs))
24962306a36Sopenharmony_ci		goto no_context;
25062306a36Sopenharmony_ci	pagefault_out_of_memory();
25162306a36Sopenharmony_ci}
252