diff options
Diffstat (limited to 'arch/i386/mm/fault.c')
-rw-r--r-- | arch/i386/mm/fault.c | 41 |
1 files changed, 23 insertions, 18 deletions
diff --git a/arch/i386/mm/fault.c b/arch/i386/mm/fault.c index 8e90339d6eaa..9edd4485b91e 100644 --- a/arch/i386/mm/fault.c +++ b/arch/i386/mm/fault.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/vt_kern.h> /* For unblank_screen() */ | 21 | #include <linux/vt_kern.h> /* For unblank_screen() */ |
22 | #include <linux/highmem.h> | 22 | #include <linux/highmem.h> |
23 | #include <linux/module.h> | 23 | #include <linux/module.h> |
24 | #include <linux/kprobes.h> | ||
24 | 25 | ||
25 | #include <asm/system.h> | 26 | #include <asm/system.h> |
26 | #include <asm/uaccess.h> | 27 | #include <asm/uaccess.h> |
@@ -199,6 +200,18 @@ static inline int is_prefetch(struct pt_regs *regs, unsigned long addr, | |||
199 | return 0; | 200 | return 0; |
200 | } | 201 | } |
201 | 202 | ||
203 | static noinline void force_sig_info_fault(int si_signo, int si_code, | ||
204 | unsigned long address, struct task_struct *tsk) | ||
205 | { | ||
206 | siginfo_t info; | ||
207 | |||
208 | info.si_signo = si_signo; | ||
209 | info.si_errno = 0; | ||
210 | info.si_code = si_code; | ||
211 | info.si_addr = (void __user *)address; | ||
212 | force_sig_info(si_signo, &info, tsk); | ||
213 | } | ||
214 | |||
202 | fastcall void do_invalid_op(struct pt_regs *, unsigned long); | 215 | fastcall void do_invalid_op(struct pt_regs *, unsigned long); |
203 | 216 | ||
204 | /* | 217 | /* |
@@ -211,18 +224,18 @@ fastcall void do_invalid_op(struct pt_regs *, unsigned long); | |||
211 | * bit 1 == 0 means read, 1 means write | 224 | * bit 1 == 0 means read, 1 means write |
212 | * bit 2 == 0 means kernel, 1 means user-mode | 225 | * bit 2 == 0 means kernel, 1 means user-mode |
213 | */ | 226 | */ |
214 | fastcall void do_page_fault(struct pt_regs *regs, unsigned long error_code) | 227 | fastcall void __kprobes do_page_fault(struct pt_regs *regs, |
228 | unsigned long error_code) | ||
215 | { | 229 | { |
216 | struct task_struct *tsk; | 230 | struct task_struct *tsk; |
217 | struct mm_struct *mm; | 231 | struct mm_struct *mm; |
218 | struct vm_area_struct * vma; | 232 | struct vm_area_struct * vma; |
219 | unsigned long address; | 233 | unsigned long address; |
220 | unsigned long page; | 234 | unsigned long page; |
221 | int write; | 235 | int write, si_code; |
222 | siginfo_t info; | ||
223 | 236 | ||
224 | /* get the address */ | 237 | /* get the address */ |
225 | __asm__("movl %%cr2,%0":"=r" (address)); | 238 | address = read_cr2(); |
226 | 239 | ||
227 | if (notify_die(DIE_PAGE_FAULT, "page fault", regs, error_code, 14, | 240 | if (notify_die(DIE_PAGE_FAULT, "page fault", regs, error_code, 14, |
228 | SIGSEGV) == NOTIFY_STOP) | 241 | SIGSEGV) == NOTIFY_STOP) |
@@ -233,7 +246,7 @@ fastcall void do_page_fault(struct pt_regs *regs, unsigned long error_code) | |||
233 | 246 | ||
234 | tsk = current; | 247 | tsk = current; |
235 | 248 | ||
236 | info.si_code = SEGV_MAPERR; | 249 | si_code = SEGV_MAPERR; |
237 | 250 | ||
238 | /* | 251 | /* |
239 | * We fault-in kernel-space virtual memory on-demand. The | 252 | * We fault-in kernel-space virtual memory on-demand. The |
@@ -313,7 +326,7 @@ fastcall void do_page_fault(struct pt_regs *regs, unsigned long error_code) | |||
313 | * we can handle it.. | 326 | * we can handle it.. |
314 | */ | 327 | */ |
315 | good_area: | 328 | good_area: |
316 | info.si_code = SEGV_ACCERR; | 329 | si_code = SEGV_ACCERR; |
317 | write = 0; | 330 | write = 0; |
318 | switch (error_code & 3) { | 331 | switch (error_code & 3) { |
319 | default: /* 3: write, present */ | 332 | default: /* 3: write, present */ |
@@ -387,11 +400,7 @@ bad_area_nosemaphore: | |||
387 | /* Kernel addresses are always protection faults */ | 400 | /* Kernel addresses are always protection faults */ |
388 | tsk->thread.error_code = error_code | (address >= TASK_SIZE); | 401 | tsk->thread.error_code = error_code | (address >= TASK_SIZE); |
389 | tsk->thread.trap_no = 14; | 402 | tsk->thread.trap_no = 14; |
390 | info.si_signo = SIGSEGV; | 403 | force_sig_info_fault(SIGSEGV, si_code, address, tsk); |
391 | info.si_errno = 0; | ||
392 | /* info.si_code has been set above */ | ||
393 | info.si_addr = (void __user *)address; | ||
394 | force_sig_info(SIGSEGV, &info, tsk); | ||
395 | return; | 404 | return; |
396 | } | 405 | } |
397 | 406 | ||
@@ -446,7 +455,7 @@ no_context: | |||
446 | printk(" at virtual address %08lx\n",address); | 455 | printk(" at virtual address %08lx\n",address); |
447 | printk(KERN_ALERT " printing eip:\n"); | 456 | printk(KERN_ALERT " printing eip:\n"); |
448 | printk("%08lx\n", regs->eip); | 457 | printk("%08lx\n", regs->eip); |
449 | asm("movl %%cr3,%0":"=r" (page)); | 458 | page = read_cr3(); |
450 | page = ((unsigned long *) __va(page))[address >> 22]; | 459 | page = ((unsigned long *) __va(page))[address >> 22]; |
451 | printk(KERN_ALERT "*pde = %08lx\n", page); | 460 | printk(KERN_ALERT "*pde = %08lx\n", page); |
452 | /* | 461 | /* |
@@ -500,11 +509,7 @@ do_sigbus: | |||
500 | tsk->thread.cr2 = address; | 509 | tsk->thread.cr2 = address; |
501 | tsk->thread.error_code = error_code; | 510 | tsk->thread.error_code = error_code; |
502 | tsk->thread.trap_no = 14; | 511 | tsk->thread.trap_no = 14; |
503 | info.si_signo = SIGBUS; | 512 | force_sig_info_fault(SIGBUS, BUS_ADRERR, address, tsk); |
504 | info.si_errno = 0; | ||
505 | info.si_code = BUS_ADRERR; | ||
506 | info.si_addr = (void __user *)address; | ||
507 | force_sig_info(SIGBUS, &info, tsk); | ||
508 | return; | 513 | return; |
509 | 514 | ||
510 | vmalloc_fault: | 515 | vmalloc_fault: |
@@ -523,7 +528,7 @@ vmalloc_fault: | |||
523 | pmd_t *pmd, *pmd_k; | 528 | pmd_t *pmd, *pmd_k; |
524 | pte_t *pte_k; | 529 | pte_t *pte_k; |
525 | 530 | ||
526 | asm("movl %%cr3,%0":"=r" (pgd_paddr)); | 531 | pgd_paddr = read_cr3(); |
527 | pgd = index + (pgd_t *)__va(pgd_paddr); | 532 | pgd = index + (pgd_t *)__va(pgd_paddr); |
528 | pgd_k = init_mm.pgd + index; | 533 | pgd_k = init_mm.pgd + index; |
529 | 534 | ||