fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / arch / mips / mm / fault.c
index 820c3af..4bcbbd2 100644 (file)
 #include <linux/module.h>
 
 #include <asm/branch.h>
-#include <asm/hardirq.h>
 #include <asm/mmu_context.h>
 #include <asm/system.h>
 #include <asm/uaccess.h>
 #include <asm/ptrace.h>
+#include <asm/highmem.h>               /* For VMALLOC_END */
 
 /*
  * This routine handles page faults.  It determines the address,
@@ -58,8 +58,12 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write,
         * only copy the information from the master page table,
         * nothing more.
         */
-       if (unlikely(address >= VMALLOC_START))
+       if (unlikely(address >= VMALLOC_START && address <= VMALLOC_END))
                goto vmalloc_fault;
+#ifdef MODULE_START
+       if (unlikely(address >= MODULE_START && address < MODULE_END))
+               goto vmalloc_fault;
+#endif
 
        /*
         * If we're in an interrupt or have no user
@@ -89,7 +93,7 @@ good_area:
                if (!(vma->vm_flags & VM_WRITE))
                        goto bad_area;
        } else {
-               if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
+               if (!(vma->vm_flags & (VM_READ | VM_WRITE | VM_EXEC)))
                        goto bad_area;
        }
 
@@ -141,7 +145,7 @@ bad_area_nosemaphore:
                info.si_signo = SIGSEGV;
                info.si_errno = 0;
                /* info.si_code has been set above */
-               info.si_addr = (void *) address;
+               info.si_addr = (void __user *) address;
                force_sig_info(SIGSEGV, &info, tsk);
                return;
        }
@@ -157,7 +161,6 @@ no_context:
         * Oops. The kernel tried to access some bad page. We'll have to
         * terminate things with extreme prejudice.
         */
-
        bust_spinlocks(1);
 
        printk(KERN_ALERT "CPU %d Unable to handle kernel paging request at "
@@ -172,12 +175,13 @@ no_context:
  */
 out_of_memory:
        up_read(&mm->mmap_sem);
-       if (tsk->pid == 1) {
+       if (is_init(tsk)) {
                yield();
                down_read(&mm->mmap_sem);
                goto survive;
        }
-       printk("VM: killing process %s\n", tsk->comm);
+       printk("VM: killing process %s(%d:#%u)\n",
+               tsk->comm, tsk->pid, tsk->xid);
        if (user_mode(regs))
                do_exit(SIGKILL);
        goto no_context;
@@ -188,20 +192,28 @@ do_sigbus:
        /* Kernel mode? Handle exceptions or die */
        if (!user_mode(regs))
                goto no_context;
-
+       else
        /*
         * Send a sigbus, regardless of whether we were in kernel
         * or user mode.
         */
+#if 0
+               printk("do_page_fault() #3: sending SIGBUS to %s for "
+                      "invalid %s\n%0*lx (epc == %0*lx, ra == %0*lx)\n",
+                      tsk->comm,
+                      write ? "write access to" : "read access from",
+                      field, address,
+                      field, (unsigned long) regs->cp0_epc,
+                      field, (unsigned long) regs->regs[31]);
+#endif
        tsk->thread.cp0_badvaddr = address;
        info.si_signo = SIGBUS;
        info.si_errno = 0;
        info.si_code = BUS_ADRERR;
-       info.si_addr = (void *) address;
+       info.si_addr = (void __user *) address;
        force_sig_info(SIGBUS, &info, tsk);
 
        return;
-
 vmalloc_fault:
        {
                /*
@@ -213,6 +225,7 @@ vmalloc_fault:
                 */
                int offset = __pgd_offset(address);
                pgd_t *pgd, *pgd_k;
+               pud_t *pud, *pud_k;
                pmd_t *pmd, *pmd_k;
                pte_t *pte_k;
 
@@ -223,8 +236,13 @@ vmalloc_fault:
                        goto no_context;
                set_pgd(pgd, *pgd_k);
 
-               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);