fault.c
来自「linux-2.4.29操作系统的源码」· C语言 代码 · 共 696 行 · 第 1/2 页
C
696 行
/* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * arch/sh64/mm/fault.c * * Copyright (C) 2000, 2001 Paolo Alberelli * Copyright (C) 2003 <Richard.Curnow@superh.com> (/proc/tlb, audit_mm, bug fixes) * Copyright (C) 2003 Paul Mundt * */#include <linux/signal.h>#include <linux/sched.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/types.h>#include <linux/ptrace.h>#include <linux/mman.h>#include <linux/mm.h>#include <linux/smp.h>#include <linux/smp_lock.h>#include <linux/interrupt.h>#include <asm/system.h>#include <asm/io.h>#include <asm/tlb.h>#include <asm/uaccess.h>#include <asm/pgalloc.h>#include <asm/hardirq.h>#include <asm/mmu_context.h>#include <asm/registers.h> /* required by inline asm statements */#if defined(CONFIG_SH64_PROC_TLB)#include <linux/init.h>#include <linux/proc_fs.h>/* Count numbers of tlb refills in each region */static unsigned long long calls_to_update_mmu_cache = 0ULL;static unsigned long long calls_to_flush_tlb_page = 0ULL;static unsigned long long calls_to_flush_tlb_range = 0ULL;static unsigned long long calls_to_flush_tlb_mm = 0ULL;static unsigned long long calls_to_flush_tlb_all = 0ULL;unsigned long long calls_to_do_slow_page_fault = 0ULL;unsigned long long calls_to_do_fast_page_fault = 0ULL;/* Count size of ranges for flush_tlb_range */static unsigned long long flush_tlb_range_1 = 0ULL;static unsigned long long flush_tlb_range_2 = 0ULL;static unsigned long long flush_tlb_range_3_4 = 0ULL;static unsigned long long flush_tlb_range_5_7 = 0ULL;static unsigned long long flush_tlb_range_8_11 = 0ULL;static unsigned long long flush_tlb_range_12_15 = 0ULL;static unsigned long long flush_tlb_range_16_up = 0ULL;static unsigned long long page_not_present = 0ULL;#endifextern void die(const char *,struct pt_regs *,long);#define PFLAG(val,flag) (( (val) & (flag) ) ? #flag : "" )#define PPROT(flag) PFLAG(pgprot_val(prot),flag)static __inline__ void print_prots(pgprot_t prot){ printk("prot is 0x%08lx\n",pgprot_val(prot)); printk("%s %s %s %s %s\n",PPROT(_PAGE_SHARED),PPROT(_PAGE_READ), PPROT(_PAGE_EXECUTE),PPROT(_PAGE_WRITE),PPROT(_PAGE_USER));}static __inline__ void print_vma(struct vm_area_struct *vma){ printk("vma start 0x%08lx\n",vma->vm_start); printk("vma end 0x%08lx\n",vma->vm_end); print_prots(vma->vm_page_prot); printk("vm_flags 0x%08lx\n",vma->vm_flags);} static __inline__ void print_task(struct task_struct *tsk){ printk("Task pid %d\n",tsk->pid);}static pte_t *lookup_pte(struct mm_struct *mm, unsigned long address){ pgd_t *dir; pmd_t *pmd; pte_t *pte; pte_t entry; dir = pgd_offset(mm, address); if (pgd_none(*dir)) { return NULL; } pmd = pmd_offset(dir, address); if (pmd_none(*pmd)) { return NULL; } pte = pte_offset(pmd, address); entry = *pte; if (pte_none(entry)) { return NULL; } if (!pte_present(entry)) { return NULL; } return pte;}/* * This routine handles page faults. It determines the address, * and the problem, and then passes it off to one of the appropriate * routines. */asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long writeaccess, unsigned long textaccess, unsigned long address){ struct task_struct *tsk; struct mm_struct *mm; struct vm_area_struct * vma; unsigned long page; unsigned long long lpage; unsigned long fixup; pte_t *pte;#if defined(CONFIG_SH64_PROC_TLB) ++calls_to_do_slow_page_fault;#endif /* SIM * Note this is now called with interrupts still disabled * This is to cope with being called for a missing IO port * address with interupts disabled. This should be fixed as * soon as we have a better 'fast path' miss handler. * * Plus take care how you try and debug this stuff. * For example, writing debug data to a port which you * have just faulted on is not going to work. */ tsk = current; mm = tsk->mm; /* Not an IO address, so reenable interrupts */ sti(); /* * If we're in an interrupt or have no user * context, we must not take the fault.. */ if (in_interrupt() || !mm) goto no_context; /* TLB misses upon some cache flushes get done under cli() */ down_read(&mm->mmap_sem); vma = find_vma(mm, address); if (!vma) {#ifdef DEBUG_FAULT print_task(tsk); printk("%s:%d fault, address is 0x%08x PC %016Lx textaccess %d writeaccess %d\n", __FUNCTION__,__LINE__, address,regs->pc,textaccess,writeaccess); show_regs(regs);#endif goto bad_area; } if (vma->vm_start <= address) { goto good_area; } if (!(vma->vm_flags & VM_GROWSDOWN)) {#ifdef DEBUG_FAULT print_task(tsk); printk("%s:%d fault, address is 0x%08x PC %016Lx textaccess %d writeaccess %d\n", __FUNCTION__,__LINE__, address,regs->pc,textaccess,writeaccess); show_regs(regs); print_vma(vma);#endif goto bad_area; } if (expand_stack(vma, address)) {#ifdef DEBUG_FAULT print_task(tsk); printk("%s:%d fault, address is 0x%08x PC %016Lx textaccess %d writeaccess %d\n", __FUNCTION__,__LINE__, address,regs->pc,textaccess,writeaccess); show_regs(regs);#endif goto bad_area; }/* * Ok, we have a good vm_area for this memory access, so * we can handle it.. */good_area: if (writeaccess) { if (!(vma->vm_flags & VM_WRITE)) goto bad_area; } else { if (!(vma->vm_flags & (VM_READ | VM_EXEC))) goto bad_area; } if (textaccess) { if (!(vma->vm_flags & VM_EXEC)) goto bad_area; } /* * If for any reason at all we couldn't handle the fault, * make sure we exit gracefully rather than endlessly redo * the fault. */survive: switch (handle_mm_fault(mm, vma, address, writeaccess)) { case 1: tsk->min_flt++; break; case 2: tsk->maj_flt++; break; case 0: goto do_sigbus; default: goto out_of_memory; } /* If we get here, the page fault has been handled. Do the TLB refill now from the newly-setup PTE, to avoid having to fault again right away on the same instruction. */ pte = lookup_pte (mm, address); if (!pte) { /* From empirical evidence, we can get here, due to !pte_present(pte). (e.g. if a swap-in occurs, and the page is swapped back out again before the process that wanted it gets rescheduled?) */ goto no_pte; } __do_tlb_refill(address, textaccess, pte);no_pte: 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:#ifdef DEBUG_FAULT printk("fault:bad area\n");#endif up_read(&mm->mmap_sem); if (user_mode(regs)) { tsk->thread.address = address; tsk->thread.error_code = writeaccess; force_sig(SIGSEGV, tsk); return; }no_context:#ifdef DEBUG_FAULT printk("fault:No context\n");#endif /* Are we prepared to handle this kernel fault? */ fixup = search_exception_table(regs->pc); if (fixup != 0) { regs->pc = 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); printk(KERN_ALERT "pc = %08Lx%08Lx\n", regs->pc >> 32, regs->pc & 0xffffffff); die("Oops", regs, writeaccess); do_exit(SIGKILL);/* * 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: if (current->pid == 1) { yield(); goto survive; } printk("fault:Out of memory\n"); up_read(&mm->mmap_sem); printk("VM: killing process %s\n", tsk->comm); if (user_mode(regs)) do_exit(SIGKILL); goto no_context;do_sigbus: printk("fault:Do sigbus\n"); up_read(&mm->mmap_sem); /* * Send a sigbus, regardless of whether we were in kernel * or user mode. */ tsk->thread.address = address; tsk->thread.error_code = writeaccess; tsk->thread.trap_no = 14; force_sig(SIGBUS, tsk); /* Kernel mode? Handle exceptions or die */ if (!user_mode(regs)) goto no_context;}void flush_tlb_all(void);void update_mmu_cache(struct vm_area_struct * vma, unsigned long address, pte_t pte){#if defined(CONFIG_SH64_PROC_TLB) ++calls_to_update_mmu_cache;#endif /* This appears to get called once for every pte entry that gets established => I don't think it's efficient to try refilling the TLBs with the pages - some may not get accessed even. Also, for executable pages, it is impossible to determine reliably here which TLB they should be mapped into (or both even).
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?