aboutsummaryrefslogtreecommitdiffstats
path: root/arch/parisc/mm/fault.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/parisc/mm/fault.c')
-rw-r--r--arch/parisc/mm/fault.c89
1 files changed, 89 insertions, 0 deletions
diff --git a/arch/parisc/mm/fault.c b/arch/parisc/mm/fault.c
index e9eabf8f14d7..f114e102aaf2 100644
--- a/arch/parisc/mm/fault.c
+++ b/arch/parisc/mm/fault.c
@@ -425,3 +425,92 @@ out_of_memory:
}
pagefault_out_of_memory();
}
+
+/* Handle non-access data TLB miss faults.
+ *
+ * For probe instructions, accesses to userspace are considered allowed
+ * if they lie in a valid VMA and the access type matches. We are not
+ * allowed to handle MM faults here so there may be situations where an
+ * actual access would fail even though a probe was successful.
+ */
+int
+handle_nadtlb_fault(struct pt_regs *regs)
+{
+ unsigned long insn = regs->iir;
+ int breg, treg, xreg, val = 0;
+ struct vm_area_struct *vma, *prev_vma;
+ struct task_struct *tsk;
+ struct mm_struct *mm;
+ unsigned long address;
+ unsigned long acc_type;
+
+ switch (insn & 0x380) {
+ case 0x280:
+ /* FDC instruction */
+ fallthrough;
+ case 0x380:
+ /* PDC and FIC instructions */
+ if (printk_ratelimit()) {
+ pr_warn("BUG: nullifying cache flush/purge instruction\n");
+ show_regs(regs);
+ }
+ if (insn & 0x20) {
+ /* Base modification */
+ breg = (insn >> 21) & 0x1f;
+ xreg = (insn >> 16) & 0x1f;
+ if (breg && xreg)
+ regs->gr[breg] += regs->gr[xreg];
+ }
+ regs->gr[0] |= PSW_N;
+ return 1;
+
+ case 0x180:
+ /* PROBE instruction */
+ treg = insn & 0x1f;
+ if (regs->isr) {
+ tsk = current;
+ mm = tsk->mm;
+ if (mm) {
+ /* Search for VMA */
+ address = regs->ior;
+ mmap_read_lock(mm);
+ vma = find_vma_prev(mm, address, &prev_vma);
+ mmap_read_unlock(mm);
+
+ /*
+ * Check if access to the VMA is okay.
+ * We don't allow for stack expansion.
+ */
+ acc_type = (insn & 0x40) ? VM_WRITE : VM_READ;
+ if (vma
+ && address >= vma->vm_start
+ && (vma->vm_flags & acc_type) == acc_type)
+ val = 1;
+ }
+ }
+ if (treg)
+ regs->gr[treg] = val;
+ regs->gr[0] |= PSW_N;
+ return 1;
+
+ case 0x300:
+ /* LPA instruction */
+ if (insn & 0x20) {
+ /* Base modification */
+ breg = (insn >> 21) & 0x1f;
+ xreg = (insn >> 16) & 0x1f;
+ if (breg && xreg)
+ regs->gr[breg] += regs->gr[xreg];
+ }
+ treg = insn & 0x1f;
+ if (treg)
+ regs->gr[treg] = 0;
+ regs->gr[0] |= PSW_N;
+ return 1;
+
+ default:
+ break;
+ }
+
+ return 0;
+}