patch-2.1.48 linux/arch/ppc/mm/fault.c

Next file: linux/arch/ppc/mm/init.c
Previous file: linux/arch/ppc/mm/extable.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.47/linux/arch/ppc/mm/fault.c linux/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
+
+/* 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
 
-#undef SHOW_FAULTS
-#undef NOISY_INSTRFAULT
-#undef NOISY_DATAFAULT
-
-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);
+}
+
 
-  /* 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;
-    }
-  }
+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;
+	}
+
+	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 @@
 	return (0);
 }
 
-inline void
-update_mmu_cache(struct vm_area_struct * vma, unsigned long address, pte_t _pte)
+void print_pte(struct _PTE p)
+{
+	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)
 {
-   MMU_hash_page(&current->tss, address & PAGE_MASK, (pte *)&_pte);
+	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);
+	}
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov