cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

fault.c (6879B)


      1// SPDX-License-Identifier: GPL-2.0
      2// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
      3
      4#include <linux/extable.h>
      5#include <linux/kprobes.h>
      6#include <linux/mmu_context.h>
      7#include <linux/perf_event.h>
      8
      9int fixup_exception(struct pt_regs *regs)
     10{
     11	const struct exception_table_entry *fixup;
     12
     13	fixup = search_exception_tables(instruction_pointer(regs));
     14	if (fixup) {
     15		regs->pc = fixup->fixup;
     16
     17		return 1;
     18	}
     19
     20	return 0;
     21}
     22
     23static inline bool is_write(struct pt_regs *regs)
     24{
     25	switch (trap_no(regs)) {
     26	case VEC_TLBINVALIDS:
     27		return true;
     28	case VEC_TLBMODIFIED:
     29		return true;
     30	}
     31
     32	return false;
     33}
     34
     35#ifdef CONFIG_CPU_HAS_LDSTEX
     36static inline void csky_cmpxchg_fixup(struct pt_regs *regs)
     37{
     38	return;
     39}
     40#else
     41extern unsigned long csky_cmpxchg_ldw;
     42extern unsigned long csky_cmpxchg_stw;
     43static inline void csky_cmpxchg_fixup(struct pt_regs *regs)
     44{
     45	if (trap_no(regs) != VEC_TLBMODIFIED)
     46		return;
     47
     48	if (instruction_pointer(regs) == csky_cmpxchg_stw)
     49		instruction_pointer_set(regs, csky_cmpxchg_ldw);
     50	return;
     51}
     52#endif
     53
     54static inline void no_context(struct pt_regs *regs, unsigned long addr)
     55{
     56	current->thread.trap_no = trap_no(regs);
     57
     58	/* Are we prepared to handle this kernel fault? */
     59	if (fixup_exception(regs))
     60		return;
     61
     62	/*
     63	 * Oops. The kernel tried to access some bad page. We'll have to
     64	 * terminate things with extreme prejudice.
     65	 */
     66	bust_spinlocks(1);
     67	pr_alert("Unable to handle kernel paging request at virtual "
     68		 "addr 0x%08lx, pc: 0x%08lx\n", addr, regs->pc);
     69	die(regs, "Oops");
     70	make_task_dead(SIGKILL);
     71}
     72
     73static inline void mm_fault_error(struct pt_regs *regs, unsigned long addr, vm_fault_t fault)
     74{
     75	current->thread.trap_no = trap_no(regs);
     76
     77	if (fault & VM_FAULT_OOM) {
     78		/*
     79		 * We ran out of memory, call the OOM killer, and return the userspace
     80		 * (which will retry the fault, or kill us if we got oom-killed).
     81		 */
     82		if (!user_mode(regs)) {
     83			no_context(regs, addr);
     84			return;
     85		}
     86		pagefault_out_of_memory();
     87		return;
     88	} else if (fault & VM_FAULT_SIGBUS) {
     89		/* Kernel mode? Handle exceptions or die */
     90		if (!user_mode(regs)) {
     91			no_context(regs, addr);
     92			return;
     93		}
     94		do_trap(regs, SIGBUS, BUS_ADRERR, addr);
     95		return;
     96	}
     97	BUG();
     98}
     99
    100static inline void bad_area(struct pt_regs *regs, struct mm_struct *mm, int code, unsigned long addr)
    101{
    102	/*
    103	 * Something tried to access memory that isn't in our memory map.
    104	 * Fix it, but check if it's kernel or user first.
    105	 */
    106	mmap_read_unlock(mm);
    107	/* User mode accesses just cause a SIGSEGV */
    108	if (user_mode(regs)) {
    109		do_trap(regs, SIGSEGV, code, addr);
    110		return;
    111	}
    112
    113	no_context(regs, addr);
    114}
    115
    116static inline void vmalloc_fault(struct pt_regs *regs, int code, unsigned long addr)
    117{
    118	pgd_t *pgd, *pgd_k;
    119	pud_t *pud, *pud_k;
    120	pmd_t *pmd, *pmd_k;
    121	pte_t *pte_k;
    122	int offset;
    123
    124	/* User mode accesses just cause a SIGSEGV */
    125	if (user_mode(regs)) {
    126		do_trap(regs, SIGSEGV, code, addr);
    127		return;
    128	}
    129
    130	/*
    131	 * Synchronize this task's top level page-table
    132	 * with the 'reference' page table.
    133	 *
    134	 * Do _not_ use "tsk" here. We might be inside
    135	 * an interrupt in the middle of a task switch..
    136	 */
    137	offset = pgd_index(addr);
    138
    139	pgd = get_pgd() + offset;
    140	pgd_k = init_mm.pgd + offset;
    141
    142	if (!pgd_present(*pgd_k)) {
    143		no_context(regs, addr);
    144		return;
    145	}
    146	set_pgd(pgd, *pgd_k);
    147
    148	pud = (pud_t *)pgd;
    149	pud_k = (pud_t *)pgd_k;
    150	if (!pud_present(*pud_k)) {
    151		no_context(regs, addr);
    152		return;
    153	}
    154
    155	pmd = pmd_offset(pud, addr);
    156	pmd_k = pmd_offset(pud_k, addr);
    157	if (!pmd_present(*pmd_k)) {
    158		no_context(regs, addr);
    159		return;
    160	}
    161	set_pmd(pmd, *pmd_k);
    162
    163	pte_k = pte_offset_kernel(pmd_k, addr);
    164	if (!pte_present(*pte_k)) {
    165		no_context(regs, addr);
    166		return;
    167	}
    168
    169	flush_tlb_one(addr);
    170}
    171
    172static inline bool access_error(struct pt_regs *regs, struct vm_area_struct *vma)
    173{
    174	if (is_write(regs)) {
    175		if (!(vma->vm_flags & VM_WRITE))
    176			return true;
    177	} else {
    178		if (unlikely(!vma_is_accessible(vma)))
    179			return true;
    180	}
    181	return false;
    182}
    183
    184/*
    185 * This routine handles page faults.  It determines the address and the
    186 * problem, and then passes it off to one of the appropriate routines.
    187 */
    188asmlinkage void do_page_fault(struct pt_regs *regs)
    189{
    190	struct task_struct *tsk;
    191	struct vm_area_struct *vma;
    192	struct mm_struct *mm;
    193	unsigned long addr = read_mmu_entryhi() & PAGE_MASK;
    194	unsigned int flags = FAULT_FLAG_DEFAULT;
    195	int code = SEGV_MAPERR;
    196	vm_fault_t fault;
    197
    198	tsk = current;
    199	mm = tsk->mm;
    200
    201	csky_cmpxchg_fixup(regs);
    202
    203	if (kprobe_page_fault(regs, tsk->thread.trap_no))
    204		return;
    205
    206	/*
    207	 * Fault-in kernel-space virtual memory on-demand.
    208	 * The 'reference' page table is init_mm.pgd.
    209	 *
    210	 * NOTE! We MUST NOT take any locks for this case. We may
    211	 * be in an interrupt or a critical region, and should
    212	 * only copy the information from the master page table,
    213	 * nothing more.
    214	 */
    215	if (unlikely((addr >= VMALLOC_START) && (addr <= VMALLOC_END))) {
    216		vmalloc_fault(regs, code, addr);
    217		return;
    218	}
    219
    220	/* Enable interrupts if they were enabled in the parent context. */
    221	if (likely(regs->sr & BIT(6)))
    222		local_irq_enable();
    223
    224	/*
    225	 * If we're in an interrupt, have no user context, or are running
    226	 * in an atomic region, then we must not take the fault.
    227	 */
    228	if (unlikely(faulthandler_disabled() || !mm)) {
    229		no_context(regs, addr);
    230		return;
    231	}
    232
    233	if (user_mode(regs))
    234		flags |= FAULT_FLAG_USER;
    235
    236	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, addr);
    237
    238	if (is_write(regs))
    239		flags |= FAULT_FLAG_WRITE;
    240retry:
    241	mmap_read_lock(mm);
    242	vma = find_vma(mm, addr);
    243	if (unlikely(!vma)) {
    244		bad_area(regs, mm, code, addr);
    245		return;
    246	}
    247	if (likely(vma->vm_start <= addr))
    248		goto good_area;
    249	if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) {
    250		bad_area(regs, mm, code, addr);
    251		return;
    252	}
    253	if (unlikely(expand_stack(vma, addr))) {
    254		bad_area(regs, mm, code, addr);
    255		return;
    256	}
    257
    258	/*
    259	 * Ok, we have a good vm_area for this memory access, so
    260	 * we can handle it.
    261	 */
    262good_area:
    263	code = SEGV_ACCERR;
    264
    265	if (unlikely(access_error(regs, vma))) {
    266		bad_area(regs, mm, code, addr);
    267		return;
    268	}
    269
    270	/*
    271	 * If for any reason at all we could not handle the fault,
    272	 * make sure we exit gracefully rather than endlessly redo
    273	 * the fault.
    274	 */
    275	fault = handle_mm_fault(vma, addr, flags, regs);
    276
    277	/*
    278	 * If we need to retry but a fatal signal is pending, handle the
    279	 * signal first. We do not need to release the mmap_lock because it
    280	 * would already be released in __lock_page_or_retry in mm/filemap.c.
    281	 */
    282	if (fault_signal_pending(fault, regs)) {
    283		if (!user_mode(regs))
    284			no_context(regs, addr);
    285		return;
    286	}
    287
    288	if (unlikely((fault & VM_FAULT_RETRY) && (flags & FAULT_FLAG_ALLOW_RETRY))) {
    289		flags |= FAULT_FLAG_TRIED;
    290
    291		/*
    292		 * No need to mmap_read_unlock(mm) as we would
    293		 * have already released it in __lock_page_or_retry
    294		 * in mm/filemap.c.
    295		 */
    296		goto retry;
    297	}
    298
    299	mmap_read_unlock(mm);
    300
    301	if (unlikely(fault & VM_FAULT_ERROR)) {
    302		mm_fault_error(regs, addr, fault);
    303		return;
    304	}
    305	return;
    306}