⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 fault.c

📁 linux 内核源代码
💻 C
字号:
/* * Copyright (C) 2004-2006 Atmel Corporation * * Based on linux/arch/sh/mm/fault.c: *   Copyright (C) 1999  Niibe Yutaka * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */#include <linux/mm.h>#include <linux/module.h>#include <linux/pagemap.h>#include <linux/kdebug.h>#include <linux/kprobes.h>#include <asm/mmu_context.h>#include <asm/sysreg.h>#include <asm/tlb.h>#include <asm/uaccess.h>#ifdef CONFIG_KPROBESstatic inline int notify_page_fault(struct pt_regs *regs, int trap){	int ret = 0;	if (!user_mode(regs)) {		if (kprobe_running() && kprobe_fault_handler(regs, trap))			ret = 1;	}	return ret;}#elsestatic inline int notify_page_fault(struct pt_regs *regs, int trap){	return 0;}#endifint exception_trace = 1;/* * This routine handles page faults. It determines the address and the * problem, and then passes it off to one of the appropriate routines. * * ecr is the Exception Cause Register. Possible values are: *   6:  Protection fault (instruction access) *   15: Protection fault (read access) *   16: Protection fault (write access) *   20: Page not found (instruction access) *   24: Page not found (read access) *   28: Page not found (write access) */asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs){	struct task_struct *tsk;	struct mm_struct *mm;	struct vm_area_struct *vma;	const struct exception_table_entry *fixup;	unsigned long address;	unsigned long page;	int writeaccess;	long signr;	int code;	int fault;	if (notify_page_fault(regs, ecr))		return;	address = sysreg_read(TLBEAR);	tsk = current;	mm = tsk->mm;	signr = SIGSEGV;	code = SEGV_MAPERR;	/*	 * If we're in an interrupt or have no user context, we must	 * not take the fault...	 */	if (in_atomic() || !mm || regs->sr & SYSREG_BIT(GM))		goto no_context;	local_irq_enable();	down_read(&mm->mmap_sem);	vma = find_vma(mm, address);	if (!vma)		goto bad_area;	if (vma->vm_start <= address)		goto good_area;	if (!(vma->vm_flags & VM_GROWSDOWN))		goto bad_area;	if (expand_stack(vma, address))		goto bad_area;	/*	 * Ok, we have a good vm_area for this memory access, so we	 * can handle it...	 */good_area:	code = SEGV_ACCERR;	writeaccess = 0;	switch (ecr) {	case ECR_PROTECTION_X:	case ECR_TLB_MISS_X:		if (!(vma->vm_flags & VM_EXEC))			goto bad_area;		break;	case ECR_PROTECTION_R:	case ECR_TLB_MISS_R:		if (!(vma->vm_flags & (VM_READ | VM_WRITE | VM_EXEC)))			goto bad_area;		break;	case ECR_PROTECTION_W:	case ECR_TLB_MISS_W:		if (!(vma->vm_flags & VM_WRITE))			goto bad_area;		writeaccess = 1;		break;	default:		panic("Unhandled case %lu in do_page_fault!", ecr);	}	/*	 * If for any reason at all we couldn't handle the fault, make	 * sure we exit gracefully rather than endlessly redo the	 * fault.	 */survive:	fault = handle_mm_fault(mm, vma, address, writeaccess);	if (unlikely(fault & VM_FAULT_ERROR)) {		if (fault & VM_FAULT_OOM)			goto out_of_memory;		else if (fault & VM_FAULT_SIGBUS)			goto do_sigbus;		BUG();	}	if (fault & VM_FAULT_MAJOR)		tsk->maj_flt++;	else		tsk->min_flt++;	up_read(&mm->mmap_sem);	return;	/*	 * Something tried to access memory that isn't in our memory	 * map. Fix it, but check if it's kernel or user first...	 */bad_area:	up_read(&mm->mmap_sem);	if (user_mode(regs)) {		if (exception_trace && printk_ratelimit())			printk("%s%s[%d]: segfault at %08lx pc %08lx "			       "sp %08lx ecr %lu\n",			       is_global_init(tsk) ? KERN_EMERG : KERN_INFO,			       tsk->comm, tsk->pid, address, regs->pc,			       regs->sp, ecr);		_exception(SIGSEGV, regs, code, address);		return;	}no_context:	/* Are we prepared to handle this kernel fault? */	fixup = search_exception_tables(regs->pc);	if (fixup) {		regs->pc = fixup->fixup;		return;	}	/*	 * Oops. The kernel tried to access some bad page. We'll have	 * to terminate things with extreme prejudice.	 */	if (address < PAGE_SIZE)		printk(KERN_ALERT		       "Unable to handle kernel NULL pointer dereference");	else		printk(KERN_ALERT		       "Unable to handle kernel paging request");	printk(" at virtual address %08lx\n", address);	page = sysreg_read(PTBR);	printk(KERN_ALERT "ptbr = %08lx", page);	if (page) {		page = ((unsigned long *)page)[address >> 22];		printk(" pgd = %08lx", page);		if (page & _PAGE_PRESENT) {			page &= PAGE_MASK;			address &= 0x003ff000;			page = ((unsigned long *)__va(page))[address >> PAGE_SHIFT];			printk(" pte = %08lx", page);		}	}	printk("\n");	die("Kernel access of bad area", regs, signr);	return;	/*	 * We ran out of memory, or some other thing happened to us	 * that made us unable to handle the page fault gracefully.	 */out_of_memory:	up_read(&mm->mmap_sem);	if (is_global_init(current)) {		yield();		down_read(&mm->mmap_sem);		goto survive;	}	printk("VM: Killing process %s\n", tsk->comm);	if (user_mode(regs))		do_group_exit(SIGKILL);	goto no_context;do_sigbus:	up_read(&mm->mmap_sem);	/* Kernel mode? Handle exceptions or die */	signr = SIGBUS;	code = BUS_ADRERR;	if (!user_mode(regs))		goto no_context;	if (exception_trace)		printk("%s%s[%d]: bus error at %08lx pc %08lx "		       "sp %08lx ecr %lu\n",		       is_global_init(tsk) ? KERN_EMERG : KERN_INFO,		       tsk->comm, tsk->pid, address, regs->pc,		       regs->sp, ecr);	_exception(SIGBUS, regs, BUS_ADRERR, address);}asmlinkage void do_bus_error(unsigned long addr, int write_access,			     struct pt_regs *regs){	printk(KERN_ALERT	       "Bus error at physical address 0x%08lx (%s access)\n",	       addr, write_access ? "write" : "read");	printk(KERN_INFO "DTLB dump:\n");	dump_dtlb();	die("Bus Error", regs, SIGKILL);}/* * This functionality is currently not possible to implement because * we're using segmentation to ensure a fixed mapping of the kernel * virtual address space. * * It would be possible to implement this, but it would require us to * disable segmentation at startup and load the kernel mappings into * the TLB like any other pages. There will be lots of trickery to * avoid recursive invocation of the TLB miss handler, though... */#ifdef CONFIG_DEBUG_PAGEALLOCvoid kernel_map_pages(struct page *page, int numpages, int enable){}EXPORT_SYMBOL(kernel_map_pages);#endif

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -