fault.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:6k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  1. /*
  2.  * This file is subject to the terms and conditions of the GNU General Public
  3.  * License.  See the file "COPYING" in the main directory of this archive
  4.  * for more details.
  5.  *
  6.  * Copyright (C) 1995 - 2000 by Ralf Baechle
  7.  */
  8. #include <linux/signal.h>
  9. #include <linux/sched.h>
  10. #include <linux/interrupt.h>
  11. #include <linux/kernel.h>
  12. #include <linux/errno.h>
  13. #include <linux/string.h>
  14. #include <linux/types.h>
  15. #include <linux/ptrace.h>
  16. #include <linux/mman.h>
  17. #include <linux/mm.h>
  18. #include <linux/smp.h>
  19. #include <linux/smp_lock.h>
  20. #include <linux/version.h>
  21. #include <asm/hardirq.h>
  22. #include <asm/pgalloc.h>
  23. #include <asm/mmu_context.h>
  24. #include <asm/softirq.h>
  25. #include <asm/system.h>
  26. #include <asm/uaccess.h>
  27. #define development_version (LINUX_VERSION_CODE & 0x100)
  28. unsigned long asid_cache = ASID_FIRST_VERSION;
  29. /*
  30.  * Macro for exception fixup code to access integer registers.
  31.  */
  32. #define dpf_reg(r) (regs->regs[r])
  33. /*
  34.  * This routine handles page faults.  It determines the address,
  35.  * and the problem, and then passes it off to one of the appropriate
  36.  * routines.
  37.  */
  38. asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write,
  39.       unsigned long address)
  40. {
  41. struct vm_area_struct * vma;
  42. struct task_struct *tsk = current;
  43. struct mm_struct *mm = tsk->mm;
  44. unsigned long fixup;
  45. siginfo_t info;
  46. /*
  47.  * We fault-in kernel-space virtual memory on-demand. The
  48.  * 'reference' page table is init_mm.pgd.
  49.  *
  50.  * NOTE! We MUST NOT take any locks for this case. We may
  51.  * be in an interrupt or a critical region, and should
  52.  * only copy the information from the master page table,
  53.  * nothing more.
  54.  */
  55. if (address >= TASK_SIZE)
  56. goto vmalloc_fault;
  57. info.si_code = SEGV_MAPERR;
  58. /*
  59.  * If we're in an interrupt or have no user
  60.  * context, we must not take the fault..
  61.  */
  62. if (in_interrupt() || !mm)
  63. goto no_context;
  64. #if 0
  65. printk("[%s:%d:%08lx:%ld:%08lx]n", current->comm, current->pid,
  66.        address, write, regs->cp0_epc);
  67. #endif
  68. down_read(&mm->mmap_sem);
  69. vma = find_vma(mm, address);
  70. if (!vma)
  71. goto bad_area;
  72. if (vma->vm_start <= address)
  73. goto good_area;
  74. if (!(vma->vm_flags & VM_GROWSDOWN))
  75. goto bad_area;
  76. if (expand_stack(vma, address))
  77. goto bad_area;
  78. /*
  79.  * Ok, we have a good vm_area for this memory access, so
  80.  * we can handle it..
  81.  */
  82. good_area:
  83. info.si_code = SEGV_ACCERR;
  84. if (write) {
  85. if (!(vma->vm_flags & VM_WRITE))
  86. goto bad_area;
  87. } else {
  88. if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
  89. goto bad_area;
  90. }
  91. /*
  92.  * If for any reason at all we couldn't handle the fault,
  93.  * make sure we exit gracefully rather than endlessly redo
  94.  * the fault.
  95.  */
  96. switch (handle_mm_fault(mm, vma, address, write)) {
  97. case 1:
  98. tsk->min_flt++;
  99. break;
  100. case 2:
  101. tsk->maj_flt++;
  102. break;
  103. case 0:
  104. goto do_sigbus;
  105. default:
  106. goto out_of_memory;
  107. }
  108. up_read(&mm->mmap_sem);
  109. return;
  110. /*
  111.  * Something tried to access memory that isn't in our memory map..
  112.  * Fix it, but check if it's kernel or user first..
  113.  */
  114. bad_area:
  115. up_read(&mm->mmap_sem);
  116. bad_area_nosemaphore:
  117. /* User mode accesses just cause a SIGSEGV */
  118. if (user_mode(regs)) {
  119. tsk->thread.cp0_badvaddr = address;
  120. tsk->thread.error_code = write;
  121. #if 0
  122. printk("do_page_fault() #2: sending SIGSEGV to %s for illegal %sn"
  123.        "%08lx (epc == %08lx, ra == %08lx)n",
  124.        tsk->comm,
  125.        write ? "write access to" : "read access from",
  126.        address,
  127.        (unsigned long) regs->cp0_epc,
  128.        (unsigned long) regs->regs[31]);
  129. #endif
  130. info.si_signo = SIGSEGV;
  131. info.si_errno = 0;
  132. /* info.si_code has been set above */
  133. info.si_addr = (void *) address;
  134. force_sig_info(SIGSEGV, &info, tsk);
  135. return;
  136. }
  137. no_context:
  138. /* Are we prepared to handle this kernel fault?  */
  139. fixup = search_exception_table(regs->cp0_epc);
  140. if (fixup) {
  141. long new_epc;
  142. tsk->thread.cp0_baduaddr = address;
  143. new_epc = fixup_exception(dpf_reg, fixup, regs->cp0_epc);
  144. if (development_version)
  145. printk(KERN_DEBUG "%s: Exception at [<%lx>] (%lx)n",
  146.        tsk->comm, regs->cp0_epc, new_epc);
  147. regs->cp0_epc = new_epc;
  148. return;
  149. }
  150. /*
  151.  * Oops. The kernel tried to access some bad page. We'll have to
  152.  * terminate things with extreme prejudice.
  153.  */
  154. printk(KERN_ALERT "Unable to handle kernel paging request at virtual "
  155.        "address %08lx, epc == %08lx, ra == %08lxn",
  156.        address, regs->cp0_epc, regs->regs[31]);
  157. die("Oops", regs);
  158. do_exit(SIGKILL);
  159. /*
  160.  * We ran out of memory, or some other thing happened to us that made
  161.  * us unable to handle the page fault gracefully.
  162.  */
  163. out_of_memory:
  164. up_read(&mm->mmap_sem);
  165. printk("VM: killing process %sn", tsk->comm);
  166. if (user_mode(regs))
  167. do_exit(SIGKILL);
  168. goto no_context;
  169. do_sigbus:
  170. up_read(&mm->mmap_sem);
  171. /*
  172.  * Send a sigbus, regardless of whether we were in kernel
  173.  * or user mode.
  174.  */
  175. tsk->thread.cp0_badvaddr = address;
  176. info.si_code = SIGBUS;
  177. info.si_errno = 0;
  178. info.si_code = BUS_ADRERR;
  179. info.si_addr = (void *) address;
  180. force_sig_info(SIGBUS, &info, tsk);
  181. /* Kernel mode? Handle exceptions or die */
  182. if (!user_mode(regs))
  183. goto no_context;
  184. return;
  185. vmalloc_fault:
  186. {
  187. /*
  188.  * Synchronize this task's top level page-table
  189.  * with the 'reference' page table.
  190.  */
  191. int offset = pgd_index(address);
  192. pgd_t *pgd, *pgd_k;
  193. pmd_t *pmd, *pmd_k;
  194. pgd = tsk->active_mm->pgd + offset;
  195. pgd_k = init_mm.pgd + offset;
  196. if (!pgd_present(*pgd)) {
  197. if (!pgd_present(*pgd_k))
  198. goto bad_area_nosemaphore;
  199. set_pgd(pgd, *pgd_k);
  200. return;
  201. }
  202. pmd = pmd_offset(pgd, address);
  203. pmd_k = pmd_offset(pgd_k, address);
  204. if (pmd_present(*pmd) || !pmd_present(*pmd_k))
  205. goto bad_area_nosemaphore;
  206. set_pmd(pmd, *pmd_k);
  207. }
  208. }