diff options
Diffstat (limited to 'arch/parisc/mm/fault.c')
-rw-r--r-- | arch/parisc/mm/fault.c | 42 |
1 files changed, 32 insertions, 10 deletions
diff --git a/arch/parisc/mm/fault.c b/arch/parisc/mm/fault.c index d10d27a720c..7584a5df0fa 100644 --- a/arch/parisc/mm/fault.c +++ b/arch/parisc/mm/fault.c @@ -142,6 +142,12 @@ int fixup_exception(struct pt_regs *regs) { const struct exception_table_entry *fix; + /* If we only stored 32bit addresses in the exception table we can drop + * out if we faulted on a 64bit address. */ + if ((sizeof(regs->iaoq[0]) > sizeof(fix->insn)) + && (regs->iaoq[0] >> 32)) + return 0; + fix = search_exception_tables(regs->iaoq[0]); if (fix) { struct exception_data *d; @@ -171,17 +177,25 @@ void do_page_fault(struct pt_regs *regs, unsigned long code, unsigned long address) { struct vm_area_struct *vma, *prev_vma; - struct task_struct *tsk = current; - struct mm_struct *mm = tsk->mm; + struct task_struct *tsk; + struct mm_struct *mm; unsigned long acc_type; int fault; - unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE; + unsigned int flags; + + if (in_atomic()) + goto no_context; - if (in_atomic() || !mm) + tsk = current; + mm = tsk->mm; + if (!mm) goto no_context; + flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE; if (user_mode(regs)) flags |= FAULT_FLAG_USER; + + acc_type = parisc_acctyp(code, regs->iir); if (acc_type & VM_WRITE) flags |= FAULT_FLAG_WRITE; retry: @@ -196,8 +210,6 @@ retry: good_area: - acc_type = parisc_acctyp(code,regs->iir); - if ((vma->vm_flags & acc_type) != acc_type) goto bad_area; @@ -268,12 +280,22 @@ bad_area: } show_regs(regs); #endif - /* FIXME: actually we need to get the signo and code correct */ - si.si_signo = SIGSEGV; + switch (code) { + case 15: /* Data TLB miss fault/Data page fault */ + case 17: /* NA data TLB miss / page fault */ + case 18: /* Unaligned access - PCXS only */ + si.si_signo = SIGBUS; + si.si_code = BUS_ADRERR; + break; + case 16: /* Non-access instruction TLB miss fault */ + case 26: /* PCXL: Data memory access rights trap */ + default: + si.si_signo = SIGSEGV; + si.si_code = SEGV_MAPERR; + } si.si_errno = 0; - si.si_code = SEGV_MAPERR; si.si_addr = (void __user *) address; - force_sig_info(SIGSEGV, &si, current); + force_sig_info(si.si_signo, &si, current); return; } |