summaryrefslogtreecommitdiffstats
path: root/arch/ppc/mm/fault.c
diff options
context:
space:
mode:
authorMiguel de Icaza <miguel@nuclecu.unam.mx>1997-08-06 19:14:48 +0000
committerMiguel de Icaza <miguel@nuclecu.unam.mx>1997-08-06 19:14:48 +0000
commite2819e52a162873ff5061de81bb749831bdb5de9 (patch)
tree6067ea700202750ba335a423696f2972700e5f76 /arch/ppc/mm/fault.c
parent17a005074429bbf143e40401f405ae4363e56828 (diff)
Merge to 2.1.38.
IMPORTANT NOTE: I could not figure out what information is the one that should be used for the following files (ie, those that were in our tree, or those that came from Linus' patch), please, check these: include/asm-mips/jazz.h include/asm-mips/jazzdma.h include/asm-mips/ioctls.h
Diffstat (limited to 'arch/ppc/mm/fault.c')
-rw-r--r--arch/ppc/mm/fault.c264
1 files changed, 154 insertions, 110 deletions
diff --git a/arch/ppc/mm/fault.c b/arch/ppc/mm/fault.c
index 7104d6bbb..fea722780 100644
--- a/arch/ppc/mm/fault.c
+++ b/arch/ppc/mm/fault.c
@@ -1,9 +1,14 @@
/*
- * ARCH/ppc/mm/fault.c
+ * arch/ppc/mm/fault.c
*
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
* Ported to PPC by Gary Thomas
- * Modified by Cort Dougan (cort@cs.nmt.edu)
+ * Modified by Cort Dougan and Paul Mackerras.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
*/
#include <linux/config.h>
@@ -17,132 +22,151 @@
#include <linux/ptrace.h>
#include <linux/mman.h>
#include <linux/mm.h>
+#include <linux/interrupt.h>
#include <asm/page.h>
#include <asm/pgtable.h>
+#include <asm/mmu_context.h>
-extern void die_if_kernel(char *, struct pt_regs *, long);
-extern void do_page_fault(struct pt_regs *, unsigned long, unsigned long);
-void new_page_fault(unsigned long address, unsigned long code, unsigned long text,
- struct pt_regs *regs);
-
+#ifdef CONFIG_PMAC
+extern void (*xmon_fault_handler)(void);
+#endif
-#undef SHOW_FAULTS
-#undef NOISY_INSTRFAULT
-#undef NOISY_DATAFAULT
+/* the linux norm for the function name is show_regs() so
+ make it call dump_regs() on the mac -- Cort */
+#ifdef CONFIG_PMAC
+#define show_regs dump_regs
+#endif
-unsigned int probingmem = 0;
-#define NEWMM 1
+extern void die_if_kernel(char *, struct pt_regs *, long);
+void bad_page_fault(struct pt_regs *, unsigned long);
+void do_page_fault(struct pt_regs *, unsigned long, unsigned long);
+void print_pte(struct _PTE);
-void new_page_fault(unsigned long address, unsigned long ppc_code,
- unsigned long text, struct pt_regs *regs)
+void do_page_fault(struct pt_regs *regs, unsigned long address, unsigned long error_code)
{
- struct vm_area_struct * vma;
- struct mm_struct *mm = current->mm;
-
- int intel_code = 0;
- pgd_t *dir;
- pmd_t *pmd;
- pte_t *pte;
-
- /*
- * bit 0 == 0 means no page found, 1 means protection fault
- * bit 1 == 0 means read, 1 means write
- * bit 2 == 0 means kernel, 1 means user-mode
- */
- if (user_mode(regs)) intel_code |= 0x04;
- if (!text && (ppc_code & 0x02000000)) intel_code |= 0x02; /* Load/store */
- if (!text && (ppc_code & 0x08000000))
- {
- intel_code |= 0x01; /* prot viol */
- goto do_page;
- }
-
- dir = pgd_offset(mm, address & PAGE_MASK);
- if (dir)
- {
- pmd = pmd_offset(dir, address & PAGE_MASK);
- if (pmd && pmd_present(*pmd))
- {
- pte = pte_offset(pmd, address & PAGE_MASK);
- if (pte && pte_present(*pte))
- {
- MMU_hash_page(&current->tss, address & PAGE_MASK, pte);
- return;
- }
- }
- }
-
+ struct task_struct *tsk = current;
+ extern unsigned _end[];
+ struct vm_area_struct * vma;
+ struct mm_struct *mm = current->mm;
+ pgd_t *dir;
+ pmd_t *pmd;
+ pte_t *pte;
+
+ /*printk("do_page_fault() %s/%d addr %x nip %x regs %x error %x\n",
+ current->comm,current->pid,address,regs->nip,regs,error_code);*/
+#ifdef CONFIG_PMAC
+ if (xmon_fault_handler && regs->trap == 0x300) {
+ xmon_fault_handler();
+ return;
+ }
+#endif
+ if (in_interrupt()) {
+ static int complained;
+ if (complained < 20) {
+ ++complained;
+ printk("page fault in interrupt handler, addr=%lx\n",
+ address);
+ show_regs(regs);
+ }
+ }
+ if (current == NULL)
+ goto bad_area;
+
do_page:
- down(&mm->mmap_sem);
- vma = find_vma(current->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;
+ down(&mm->mmap_sem);
+ vma = find_vma(tsk->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;
good_area:
- /* a write */
- if (intel_code & 2) {
- if (!(vma->vm_flags & VM_WRITE))
- {
- goto bad_area;
- }
- /* a read */
- } else {
- /* protection fault */
- if (intel_code & 1)
- {
- printk("prot fault\n");
- goto bad_area;
- }
- if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
- {
- printk("no read or exec\n");
- goto bad_area;
- }
- }
- handle_mm_fault(vma, address, intel_code & 2);
- up(&mm->mmap_sem); flush_page(address); /* Flush & Invalidate cache - note: address is OK now */
- return;
+ if (error_code & 0xb5700000)
+ /* an error such as lwarx to I/O controller space,
+ address matching DABR, eciwx, etc. */
+ goto bad_area;
+
+ /* a write */
+ if (error_code & 0x02000000) {
+ if (!(vma->vm_flags & VM_WRITE))
+ goto bad_area;
+ /* a read */
+ } else {
+ /* protection fault */
+ if ( error_code & 0x08000000 )
+ goto bad_area;
+ if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
+ goto bad_area;
+ }
+ handle_mm_fault(current, vma, address, error_code & 0x02000000);
+ up(&mm->mmap_sem);
+ /*printk("do_page_fault() return %s/%d addr %x msr %x\n",
+ current->comm,current->pid,address,regs->msr);*/
+ /* not needed since flush_page_to_ram() works */
+#if 0
+ flush_page(address);
+#endif
+ return;
bad_area:
- up(&mm->mmap_sem);
+ up(&current->mm->mmap_sem);
+ bad_page_fault(regs, address);
+}
+
+
+void
+bad_page_fault(struct pt_regs *regs, unsigned long address)
+{
+ extern unsigned int probingmem;
+ struct task_struct *tsk = current;
+ unsigned long fixup;
+
+
+ /* Are we prepared to handle this fault? */
+ if ((fixup = search_exception_table(regs->nip)) != 0) {
+ if ( user_mode(regs) )
+ printk("Exception from user mode\n");
+#if 0
+ printk(KERN_DEBUG "Exception at %lx (%lx)\n", regs->nip, fixup);
+#endif
+ regs->nip = fixup;
+ return;
+ }
- /* Did we have an exception handler installed? */
- if(current->tss.excount != 0) {
- if(user_mode(regs)) {
- printk("Exception signalled from user mode!\n");
- } else {
-#if 0
- printk("Exception from kernel mode. pc %x expc %x count %d\n",
- regs->nip,current->tss.expc,current->tss.excount);
-#endif
- current->tss.excount = 0;
- regs->gpr[3] = -EFAULT;
- regs->nip = current->tss.expc;
- return;
- }
- }
+ if ( user_mode(regs) )
+ {
+ force_sig(SIGSEGV, tsk);
+ return;
+ }
- if (user_mode(regs))
- {
- force_sig(SIGSEGV, current);
- return;
- }
- panic("KERNEL access of bad area PC %x address %x vm_flags %x\n",
- regs->nip,address,vma->vm_flags);
+bad_kernel_access:
+ /* make sure it's not a bootup probe test */
+ if ( probingmem )
+ {
+ probingmem = 0;
+ return;
+ }
+ /* kernel has accessed a bad area */
+ show_regs(regs);
+ print_backtrace( regs->gpr[1] );
+#ifdef CONFIG_PMAC
+ xmon(regs);
+#endif
+ panic("kernel access of bad area\n pc %x address %X tsk %s/%d",
+ regs->nip,address,tsk->comm,tsk->pid);
}
-va_to_phys(unsigned long address)
+unsigned long va_to_phys(unsigned long address)
{
pgd_t *dir;
pmd_t *pmd;
pte_t *pte;
+
dir = pgd_offset(current->mm, address & PAGE_MASK);
if (dir)
{
@@ -165,8 +189,28 @@ va_to_phys(unsigned long address)
return (0);
}
-inline void
-update_mmu_cache(struct vm_area_struct * vma, unsigned long address, pte_t _pte)
+void print_pte(struct _PTE p)
{
- MMU_hash_page(&current->tss, address & PAGE_MASK, (pte *)&_pte);
+ printk(
+"%08x %08x vsid: %06x h: %01x api: %02x rpn: %05x rcwimg: %d%d%d%d%d%d pp: %02x\n",
+ *((unsigned long *)(&p)), *((long *)&p+1),
+ p.vsid, p.h, p.api, p.rpn,
+ p.r,p.c,p.w,p.i,p.m,p.g,p.pp);
+}
+
+/*
+ * Search the hw hash table for a mapping to the given physical
+ * address. -- Cort
+ */
+unsigned long htab_phys_to_va(unsigned long address)
+{
+ extern PTE *Hash, *Hash_end;
+ PTE *ptr;
+
+ for ( ptr = Hash ; ptr < Hash_end ; ptr++ )
+ {
+ if ( ptr->rpn == (address>>12) )
+ printk("phys %08X -> va ???\n",
+ address);
+ }
}