fault.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:6k
源码类别:

Linux/Unix编程

开发平台:

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