summaryrefslogtreecommitdiffstats
path: root/arch/sparc64/mm/fault.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sparc64/mm/fault.c')
-rw-r--r--arch/sparc64/mm/fault.c161
1 files changed, 95 insertions, 66 deletions
diff --git a/arch/sparc64/mm/fault.c b/arch/sparc64/mm/fault.c
index 6df923a4b..5e16e1218 100644
--- a/arch/sparc64/mm/fault.c
+++ b/arch/sparc64/mm/fault.c
@@ -1,4 +1,4 @@
-/* $Id: fault.c,v 1.11 1997/06/01 05:46:15 davem Exp $
+/* $Id: fault.c,v 1.18 1997/07/17 02:20:56 davem Exp $
* arch/sparc64/mm/fault.c: Page fault handlers for the 64-bit Sparc.
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -134,44 +134,14 @@ asmlinkage int lookup_fault(unsigned long pc, unsigned long ret_pc,
return 0;
}
-/* #define FAULT_TRACER */
-/* #define FAULT_TRACER_VERBOSE */
+/* #define DEBUG_EXCEPTIONS */
-asmlinkage void do_sparc64_fault(struct pt_regs *regs, int text_fault, int write,
- unsigned long address, unsigned long tag,
- unsigned long sfsr)
+asmlinkage void do_sparc64_fault(struct pt_regs *regs, unsigned long address, int write)
{
+ struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
- struct task_struct *tsk = current;
- struct mm_struct *mm = tsk->mm;
- unsigned long fixup;
- unsigned long g2;
- int from_user = !(regs->tstate & TSTATE_PRIV);
-#ifdef FAULT_TRACER
- static unsigned long last_addr = 0;
- static int rcnt = 0;
-
-#ifdef FAULT_TRACER_VERBOSE
- printk("FAULT(PC[%016lx],t[%d],w[%d],addr[%016lx])...",
- regs->tpc, text_fault, write, address);
-#else
- printk("F[%016lx:%016lx:w(%d)", regs->tpc, address, write);
-#endif
- if(address == last_addr) {
- if(rcnt++ > 15) {
- printk("Wheee lotsa bogus faults, something wrong, spinning\n");
- __asm__ __volatile__("flushw");
- printk("o7[%016lx] i7[%016lx]\n",
- regs->u_regs[UREG_I7],
- ((struct reg_window *)(regs->u_regs[UREG_FP]+STACK_BIAS))->ins[7]);
- sti();
- while(1)
- barrier();
- }
- } else rcnt = 0;
- last_addr = address;
-#endif
- lock_kernel ();
+
+ lock_kernel();
down(&mm->mmap_sem);
vma = find_vma(mm, address);
if(!vma)
@@ -204,40 +174,99 @@ good_area:
*/
bad_area:
up(&mm->mmap_sem);
- /* Is this in ex_table? */
+
+ {
+ unsigned long g2 = regs->u_regs[UREG_G2];
+
+ /* Is this in ex_table? */
+ if (regs->tstate & TSTATE_PRIV) {
+ unsigned char asi = ASI_P;
+ unsigned int insn;
+ unsigned long fixup;
+
+ insn = *(unsigned int *)regs->tpc;
+ if ((insn & 0xc0800000) == 0xc0800000) {
+ if (insn & 0x2000)
+ asi = (regs->tstate >> 24);
+ else
+ asi = (insn >> 5);
+ }
- g2 = regs->u_regs[UREG_G2];
- if (!from_user && (fixup = search_exception_table (regs->tpc, &g2))) {
- printk("Exception: PC<%016lx> faddr<%016lx>\n", regs->tpc, address);
- printk("EX_TABLE: insn<%016lx> fixup<%016lx> g2<%016lx>\n",
- regs->tpc, fixup, g2);
- regs->tpc = fixup;
- regs->tnpc = regs->tpc + 4;
- regs->u_regs[UREG_G2] = g2;
- goto out;
- }
- if(from_user) {
-#if 1
- unsigned long cpc;
- __asm__ __volatile__("mov %%i7, %0" : "=r" (cpc));
- printk("[%s:%d] SIGSEGV pc[%016lx] addr[%016lx] w[%d] sfsr[%016lx] "
- "caller[%016lx]\n", current->comm, current->pid, regs->tpc,
- address, write, sfsr, cpc);
+ /* Look in asi.h: All _S asis have LS bit set */
+ if ((asi & 0x1) &&
+ (fixup = search_exception_table (regs->tpc, &g2))) {
+#ifdef DEBUG_EXCEPTIONS
+ printk("Exception: PC<%016lx> faddr<%016lx>\n",
+ regs->tpc, address);
+ printk("EX_TABLE: insn<%016lx> fixup<%016lx> "
+ "g2<%016lx>\n", regs->tpc, fixup, g2);
#endif
- tsk->tss.sig_address = address;
- tsk->tss.sig_desc = SUBSIG_NOMAPPING;
- send_sig(SIGSEGV, tsk, 1);
- goto out;
+ regs->tpc = fixup;
+ regs->tnpc = regs->tpc + 4;
+ regs->u_regs[UREG_G2] = g2;
+ goto out;
+ }
+ } else {
+ current->tss.sig_address = address;
+ current->tss.sig_desc = SUBSIG_NOMAPPING;
+ send_sig(SIGSEGV, current, 1);
+ goto out;
+ }
+ unhandled_fault (address, current, regs);
}
- unhandled_fault (address, tsk, regs);
out:
unlock_kernel();
-#ifdef FAULT_TRACER
-#ifdef FAULT_TRACER_VERBOSE
- printk(" done\n");
-#else
- printk("]");
-#endif
-#endif
}
+void fixup_dcache_alias(struct vm_area_struct *vma, unsigned long address, pte_t pte)
+{
+ struct vm_area_struct *vmaring;
+ struct inode *inode;
+ unsigned long vaddr, offset, start;
+ pgd_t *pgdp;
+ pmd_t *pmdp;
+ pte_t *ptep;
+ int alias_found = 0;
+
+ inode = vma->vm_dentry->d_inode;
+ if(!inode)
+ return;
+
+ offset = (address & PAGE_MASK) - vma->vm_start;
+ vmaring = inode->i_mmap;
+ do {
+ vaddr = vmaring->vm_start + offset;
+
+ /* This conditional is misleading... */
+ if((vaddr ^ address) & PAGE_SIZE) {
+ alias_found++;
+ start = vmaring->vm_start;
+ while(start < vmaring->vm_end) {
+ pgdp = pgd_offset(vmaring->vm_mm, start);
+ if(!pgdp) goto next;
+ pmdp = pmd_offset(pgdp, start);
+ if(!pmdp) goto next;
+ ptep = pte_offset(pmdp, start);
+ if(!ptep) goto next;
+
+ if(pte_val(*ptep) & _PAGE_PRESENT) {
+ flush_cache_page(vmaring, start);
+ *ptep = __pte(pte_val(*ptep) &
+ ~(_PAGE_CV));
+ flush_tlb_page(vmaring, start);
+ }
+ next:
+ start += PAGE_SIZE;
+ }
+ }
+ } while((vmaring = vmaring->vm_next_share) != NULL);
+
+ if(alias_found && (pte_val(pte) & _PAGE_CV)) {
+ pgdp = pgd_offset(vma->vm_mm, address);
+ pmdp = pmd_offset(pgdp, address);
+ ptep = pte_offset(pmdp, address);
+ flush_cache_page(vma, address);
+ *ptep = __pte(pte_val(*ptep) & ~(_PAGE_CV));
+ flush_tlb_page(vma, address);
+ }
+}