linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / arch / i386 / mm / fault.c
index f714896..cf572d9 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/vt_kern.h>             /* For unblank_screen() */
 #include <linux/highmem.h>
 #include <linux/module.h>
+#include <linux/kprobes.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -107,14 +108,12 @@ static inline unsigned long get_segment_eip(struct pt_regs *regs,
                desc = (void *)desc + (seg & ~7);
        } else {
                /* Must disable preemption while reading the GDT. */
-               desc = (u32 *)&per_cpu(cpu_gdt_table, get_cpu());
+               desc = (u32 *)get_cpu_gdt_table(get_cpu());
                desc = (void *)desc + (seg & ~7);
        }
 
        /* Decode the code segment base from the descriptor */
-       base =   (desc[0] >> 16) |
-               ((desc[1] & 0xff) << 16) |
-                (desc[1] & 0xff000000);
+       base = get_desc_base((unsigned long *)desc);
 
        if (seg & (1<<2)) { 
                up(&current->mm->context.sem);
@@ -148,7 +147,7 @@ static int __is_prefetch(struct pt_regs *regs, unsigned long addr)
 
                if (instr > limit)
                        break;
-               if (__get_user(opcode, (unsigned char *) instr))
+               if (__get_user(opcode, (unsigned char __user *) instr))
                        break; 
 
                instr_hi = opcode & 0xf0; 
@@ -175,7 +174,7 @@ static int __is_prefetch(struct pt_regs *regs, unsigned long addr)
                        scan_more = 0;
                        if (instr > limit)
                                break;
-                       if (__get_user(opcode, (unsigned char *) instr)) 
+                       if (__get_user(opcode, (unsigned char __user *) instr))
                                break;
                        prefetch = (instr_lo == 0xF) &&
                                (opcode == 0x0D || opcode == 0x18);
@@ -201,6 +200,18 @@ static inline int is_prefetch(struct pt_regs *regs, unsigned long addr,
        return 0;
 } 
 
+static noinline void force_sig_info_fault(int si_signo, int si_code,
+       unsigned long address, struct task_struct *tsk)
+{
+       siginfo_t info;
+
+       info.si_signo = si_signo;
+       info.si_errno = 0;
+       info.si_code = si_code;
+       info.si_addr = (void __user *)address;
+       force_sig_info(si_signo, &info, tsk);
+}
+
 fastcall void do_invalid_op(struct pt_regs *, unsigned long);
 
 /*
@@ -213,18 +224,18 @@ fastcall void do_invalid_op(struct pt_regs *, unsigned long);
  *     bit 1 == 0 means read, 1 means write
  *     bit 2 == 0 means kernel, 1 means user-mode
  */
-fastcall void do_page_fault(struct pt_regs *regs, unsigned long error_code)
+fastcall void __kprobes do_page_fault(struct pt_regs *regs,
+                                     unsigned long error_code)
 {
        struct task_struct *tsk;
        struct mm_struct *mm;
        struct vm_area_struct * vma;
        unsigned long address;
        unsigned long page;
-       int write;
-       siginfo_t info;
+       int write, si_code;
 
        /* get the address */
-       __asm__("movl %%cr2,%0":"=r" (address));
+        address = read_cr2();
 
        if (notify_die(DIE_PAGE_FAULT, "page fault", regs, error_code, 14,
                                        SIGSEGV) == NOTIFY_STOP)
@@ -235,7 +246,7 @@ fastcall void do_page_fault(struct pt_regs *regs, unsigned long error_code)
 
        tsk = current;
 
-       info.si_code = SEGV_MAPERR;
+       si_code = SEGV_MAPERR;
 
        /*
         * We fault-in kernel-space virtual memory on-demand. The
@@ -315,7 +326,7 @@ fastcall void do_page_fault(struct pt_regs *regs, unsigned long error_code)
  * we can handle it..
  */
 good_area:
-       info.si_code = SEGV_ACCERR;
+       si_code = SEGV_ACCERR;
        write = 0;
        switch (error_code & 3) {
                default:        /* 3: write, present */
@@ -389,11 +400,7 @@ bad_area_nosemaphore:
                /* Kernel addresses are always protection faults */
                tsk->thread.error_code = error_code | (address >= TASK_SIZE);
                tsk->thread.trap_no = 14;
-               info.si_signo = SIGSEGV;
-               info.si_errno = 0;
-               /* info.si_code has been set above */
-               info.si_addr = (void __user *)address;
-               force_sig_info(SIGSEGV, &info, tsk);
+               force_sig_info_fault(SIGSEGV, si_code, address, tsk);
                return;
        }
 
@@ -448,7 +455,7 @@ no_context:
        printk(" at virtual address %08lx\n",address);
        printk(KERN_ALERT " printing eip:\n");
        printk("%08lx\n", regs->eip);
-       asm("movl %%cr3,%0":"=r" (page));
+       page = read_cr3();
        page = ((unsigned long *) __va(page))[address >> 22];
        printk(KERN_ALERT "*pde = %08lx\n", page);
        /*
@@ -465,6 +472,9 @@ no_context:
                printk(KERN_ALERT "*pte = %08lx\n", page);
        }
 #endif
+       tsk->thread.cr2 = address;
+       tsk->thread.trap_no = 14;
+       tsk->thread.error_code = error_code;
        die("Oops", regs, error_code);
        bust_spinlocks(0);
        do_exit(SIGKILL);
@@ -499,11 +509,7 @@ do_sigbus:
        tsk->thread.cr2 = address;
        tsk->thread.error_code = error_code;
        tsk->thread.trap_no = 14;
-       info.si_signo = SIGBUS;
-       info.si_errno = 0;
-       info.si_code = BUS_ADRERR;
-       info.si_addr = (void __user *)address;
-       force_sig_info(SIGBUS, &info, tsk);
+       force_sig_info_fault(SIGBUS, BUS_ADRERR, address, tsk);
        return;
 
 vmalloc_fault:
@@ -518,10 +524,11 @@ vmalloc_fault:
                int index = pgd_index(address);
                unsigned long pgd_paddr;
                pgd_t *pgd, *pgd_k;
+               pud_t *pud, *pud_k;
                pmd_t *pmd, *pmd_k;
                pte_t *pte_k;
 
-               asm("movl %%cr3,%0":"=r" (pgd_paddr));
+               pgd_paddr = read_cr3();
                pgd = index + (pgd_t *)__va(pgd_paddr);
                pgd_k = init_mm.pgd + index;
 
@@ -530,11 +537,17 @@ vmalloc_fault:
 
                /*
                 * set_pgd(pgd, *pgd_k); here would be useless on PAE
-                * and redundant with the set_pmd() on non-PAE.
+                * and redundant with the set_pmd() on non-PAE. As would
+                * set_pud.
                 */
 
-               pmd = pmd_offset(pgd, address);
-               pmd_k = pmd_offset(pgd_k, address);
+               pud = pud_offset(pgd, address);
+               pud_k = pud_offset(pgd_k, address);
+               if (!pud_present(*pud_k))
+                       goto no_context;
+               
+               pmd = pmd_offset(pud, address);
+               pmd_k = pmd_offset(pud_k, address);
                if (!pmd_present(*pmd_k))
                        goto no_context;
                set_pmd(pmd, *pmd_k);