sh: Enable shared page fault handler for _32/_64.
[deliverable/linux.git] / arch / sh / mm / tlbflush_64.c
index 99c5833036be229132cefaaf966be8f6629c66d5..908167bdfc040a4445edf12b9a4fcce9d90c938e 100644 (file)
 #include <asm/pgalloc.h>
 #include <asm/mmu_context.h>
 
-static pte_t *lookup_pte(struct mm_struct *mm, unsigned long address)
-{
-       pgd_t *dir;
-       pud_t *pud;
-       pmd_t *pmd;
-       pte_t *pte;
-       pte_t entry;
-
-       dir = pgd_offset(mm, address);
-       if (pgd_none(*dir))
-               return NULL;
-
-       pud = pud_offset(dir, address);
-       if (pud_none(*pud))
-               return NULL;
-
-       pmd = pmd_offset(pud, address);
-       if (pmd_none(*pmd))
-               return NULL;
-
-       pte = pte_offset_kernel(pmd, address);
-       entry = *pte;
-       if (pte_none(entry) || !pte_present(entry))
-               return NULL;
-
-       return pte;
-}
-
-/*
- * This routine handles page faults.  It determines the address,
- * and the problem, and then passes it off to one of the appropriate
- * routines.
- */
-asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code,
-                             unsigned long address)
-{
-       struct task_struct *tsk;
-       struct mm_struct *mm;
-       struct vm_area_struct * vma;
-       const struct exception_table_entry *fixup;
-       int write = error_code & FAULT_CODE_WRITE;
-       int textaccess = error_code & FAULT_CODE_ITLB;
-       unsigned int flags = (FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE |
-                             (write ? FAULT_FLAG_WRITE : 0));
-       pte_t *pte;
-       int fault;
-
-       /* SIM
-        * Note this is now called with interrupts still disabled
-        * This is to cope with being called for a missing IO port
-        * address with interrupts disabled. This should be fixed as
-        * soon as we have a better 'fast path' miss handler.
-        *
-        * Plus take care how you try and debug this stuff.
-        * For example, writing debug data to a port which you
-        * have just faulted on is not going to work.
-        */
-
-       tsk = current;
-       mm = tsk->mm;
-
-       /* Not an IO address, so reenable interrupts */
-       local_irq_enable();
-
-       perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
-
-       /*
-        * If we're in an interrupt or have no user
-        * context, we must not take the fault..
-        */
-       if (in_atomic() || !mm)
-               goto no_context;
-
-retry:
-       /* TLB misses upon some cache flushes get done under cli() */
-       down_read(&mm->mmap_sem);
-
-       vma = find_vma(mm, address);
-       if (!vma)
-               goto bad_area;
-       if (vma->vm_start <= address)
-               goto good_area;
-       if (!(vma->vm_flags & VM_GROWSDOWN))
-               goto bad_area;
-       if (expand_stack(vma, address))
-               goto bad_area;
-
-/*
- * Ok, we have a good vm_area for this memory access, so
- * we can handle it..
- */
-good_area:
-       if (textaccess) {
-               if (!(vma->vm_flags & VM_EXEC))
-                       goto bad_area;
-       } else {
-               if (write) {
-                       if (!(vma->vm_flags & VM_WRITE))
-                               goto bad_area;
-               } else {
-                       if (!(vma->vm_flags & VM_READ))
-                               goto bad_area;
-               }
-       }
-
-       /*
-        * If for any reason at all we couldn't handle the fault,
-        * make sure we exit gracefully rather than endlessly redo
-        * the fault.
-        */
-       fault = handle_mm_fault(mm, vma, address, flags);
-
-       if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current))
-               return;
-
-       if (unlikely(fault & VM_FAULT_ERROR)) {
-               if (fault & VM_FAULT_OOM)
-                       goto out_of_memory;
-               else if (fault & VM_FAULT_SIGBUS)
-                       goto do_sigbus;
-               BUG();
-       }
-
-       if (flags & FAULT_FLAG_ALLOW_RETRY) {
-               if (fault & VM_FAULT_MAJOR) {
-                       tsk->maj_flt++;
-                       perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1,
-                                     regs, address);
-               } else {
-                       tsk->min_flt++;
-                       perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1,
-                                     regs, address);
-               }
-
-               if (fault & VM_FAULT_RETRY) {
-                       flags &= ~FAULT_FLAG_ALLOW_RETRY;
-
-                       /*
-                        * No need to up_read(&mm->mmap_sem) as we would
-                        * have already released it in __lock_page_or_retry
-                        * in mm/filemap.c.
-                        */
-                       goto retry;
-               }
-       }
-
-       /* If we get here, the page fault has been handled.  Do the TLB refill
-          now from the newly-setup PTE, to avoid having to fault again right
-          away on the same instruction. */
-       pte = lookup_pte (mm, address);
-       if (!pte) {
-               /* From empirical evidence, we can get here, due to
-                  !pte_present(pte).  (e.g. if a swap-in occurs, and the page
-                  is swapped back out again before the process that wanted it
-                  gets rescheduled?) */
-               goto no_pte;
-       }
-
-       __do_tlb_refill(address, textaccess, pte);
-
-no_pte:
-
-       up_read(&mm->mmap_sem);
-       return;
-
-/*
- * Something tried to access memory that isn't in our memory map..
- * Fix it, but check if it's kernel or user first..
- */
-bad_area:
-       up_read(&mm->mmap_sem);
-
-       if (user_mode(regs)) {
-               static int count=0;
-               siginfo_t info;
-               if (count < 4) {
-                       /* This is really to help debug faults when starting
-                        * usermode, so only need a few */
-                       count++;
-                       printk("user mode bad_area address=%08lx pid=%d (%s) pc=%08lx\n",
-                               address, task_pid_nr(current), current->comm,
-                               (unsigned long) regs->pc);
-               }
-               if (is_global_init(tsk)) {
-                       panic("INIT had user mode bad_area\n");
-               }
-               tsk->thread.address = address;
-               info.si_signo = SIGSEGV;
-               info.si_errno = 0;
-               info.si_addr = (void *) address;
-               force_sig_info(SIGSEGV, &info, tsk);
-               return;
-       }
-
-no_context:
-       /* Are we prepared to handle this kernel fault?  */
-       fixup = search_exception_tables(regs->pc);
-       if (fixup) {
-               regs->pc = fixup->fixup;
-               return;
-       }
-
-/*
- * Oops. The kernel tried to access some bad page. We'll have to
- * terminate things with extreme prejudice.
- *
- */
-       if (address < PAGE_SIZE)
-               printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
-       else
-               printk(KERN_ALERT "Unable to handle kernel paging request");
-       printk(" at virtual address %08lx\n", address);
-       printk(KERN_ALERT "pc = %08Lx%08Lx\n", regs->pc >> 32, regs->pc & 0xffffffff);
-       die("Oops", regs, error_code);
-       do_exit(SIGKILL);
-
-/*
- * We ran out of memory, or some other thing happened to us that made
- * us unable to handle the page fault gracefully.
- */
-out_of_memory:
-       up_read(&mm->mmap_sem);
-       if (!user_mode(regs))
-               goto no_context;
-       pagefault_out_of_memory();
-       return;
-
-do_sigbus:
-       printk("fault:Do sigbus\n");
-       up_read(&mm->mmap_sem);
-
-       /*
-        * Send a sigbus, regardless of whether we were in kernel
-        * or user mode.
-        */
-       tsk->thread.address = address;
-       force_sig(SIGBUS, tsk);
-
-       /* Kernel mode? Handle exceptions or die */
-       if (!user_mode(regs))
-               goto no_context;
-}
-
 void local_flush_tlb_one(unsigned long asid, unsigned long page)
 {
        unsigned long long match, pteh=0, lpage;
This page took 0.028026 seconds and 5 git commands to generate.