|
@@ -383,3 +383,162 @@ retry:
|
|
|
SEGV_ACCERR : SEGV_MAPERR;
|
|
|
}
|
|
|
|
|
|
+ __do_user_fault(tsk, addr, fsr, sig, code, regs);
|
|
|
+ return 0;
|
|
|
+
|
|
|
+no_context:
|
|
|
+ __do_kernel_fault(mm, addr, fsr, regs);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+#else /* CONFIG_MMU */
|
|
|
+static int
|
|
|
+do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
|
|
|
+{
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+#endif /* CONFIG_MMU */
|
|
|
+
|
|
|
+/*
|
|
|
+ * First Level Translation Fault Handler
|
|
|
+ *
|
|
|
+ * We enter here because the first level page table doesn't contain
|
|
|
+ * a valid entry for the address.
|
|
|
+ *
|
|
|
+ * If the address is in kernel space (>= TASK_SIZE), then we are
|
|
|
+ * probably faulting in the vmalloc() area.
|
|
|
+ *
|
|
|
+ * If the init_task's first level page tables contains the relevant
|
|
|
+ * entry, we copy the it to this task. If not, we send the process
|
|
|
+ * a signal, fixup the exception, or oops the kernel.
|
|
|
+ *
|
|
|
+ * NOTE! We MUST NOT take any locks for this case. We may be in an
|
|
|
+ * interrupt or a critical region, and should only copy the information
|
|
|
+ * from the master page table, nothing more.
|
|
|
+ */
|
|
|
+#ifdef CONFIG_MMU
|
|
|
+static int __kprobes
|
|
|
+do_translation_fault(unsigned long addr, unsigned int fsr,
|
|
|
+ struct pt_regs *regs)
|
|
|
+{
|
|
|
+ unsigned int index;
|
|
|
+ pgd_t *pgd, *pgd_k;
|
|
|
+ pud_t *pud, *pud_k;
|
|
|
+ pmd_t *pmd, *pmd_k;
|
|
|
+
|
|
|
+ if (addr < TASK_SIZE)
|
|
|
+ return do_page_fault(addr, fsr, regs);
|
|
|
+
|
|
|
+ if (user_mode(regs))
|
|
|
+ goto bad_area;
|
|
|
+
|
|
|
+ index = pgd_index(addr);
|
|
|
+
|
|
|
+ pgd = cpu_get_pgd() + index;
|
|
|
+ pgd_k = init_mm.pgd + index;
|
|
|
+
|
|
|
+ if (pgd_none(*pgd_k))
|
|
|
+ goto bad_area;
|
|
|
+ if (!pgd_present(*pgd))
|
|
|
+ set_pgd(pgd, *pgd_k);
|
|
|
+
|
|
|
+ pud = pud_offset(pgd, addr);
|
|
|
+ pud_k = pud_offset(pgd_k, addr);
|
|
|
+
|
|
|
+ if (pud_none(*pud_k))
|
|
|
+ goto bad_area;
|
|
|
+ if (!pud_present(*pud))
|
|
|
+ set_pud(pud, *pud_k);
|
|
|
+
|
|
|
+ pmd = pmd_offset(pud, addr);
|
|
|
+ pmd_k = pmd_offset(pud_k, addr);
|
|
|
+
|
|
|
+#ifdef CONFIG_ARM_LPAE
|
|
|
+ /*
|
|
|
+ * Only one hardware entry per PMD with LPAE.
|
|
|
+ */
|
|
|
+ index = 0;
|
|
|
+#else
|
|
|
+ /*
|
|
|
+ * On ARM one Linux PGD entry contains two hardware entries (see page
|
|
|
+ * tables layout in pgtable.h). We normally guarantee that we always
|
|
|
+ * fill both L1 entries. But create_mapping() doesn't follow the rule.
|
|
|
+ * It can create inidividual L1 entries, so here we have to call
|
|
|
+ * pmd_none() check for the entry really corresponded to address, not
|
|
|
+ * for the first of pair.
|
|
|
+ */
|
|
|
+ index = (addr >> SECTION_SHIFT) & 1;
|
|
|
+#endif
|
|
|
+ if (pmd_none(pmd_k[index]))
|
|
|
+ goto bad_area;
|
|
|
+
|
|
|
+ copy_pmd(pmd, pmd_k);
|
|
|
+ return 0;
|
|
|
+
|
|
|
+bad_area:
|
|
|
+ do_bad_area(addr, fsr, regs);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+#else /* CONFIG_MMU */
|
|
|
+static int
|
|
|
+do_translation_fault(unsigned long addr, unsigned int fsr,
|
|
|
+ struct pt_regs *regs)
|
|
|
+{
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+#endif /* CONFIG_MMU */
|
|
|
+
|
|
|
+/*
|
|
|
+ * Some section permission faults need to be handled gracefully.
|
|
|
+ * They can happen due to a __{get,put}_user during an oops.
|
|
|
+ */
|
|
|
+static int
|
|
|
+do_sect_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
|
|
|
+{
|
|
|
+ do_bad_area(addr, fsr, regs);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * This abort handler always returns "fault".
|
|
|
+ */
|
|
|
+static int
|
|
|
+do_bad(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
|
|
|
+{
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+struct fsr_info {
|
|
|
+ int (*fn)(unsigned long addr, unsigned int fsr, struct pt_regs *regs);
|
|
|
+ int sig;
|
|
|
+ int code;
|
|
|
+ const char *name;
|
|
|
+};
|
|
|
+
|
|
|
+/* FSR definition */
|
|
|
+#ifdef CONFIG_ARM_LPAE
|
|
|
+#include "fsr-3level.c"
|
|
|
+#else
|
|
|
+#include "fsr-2level.c"
|
|
|
+#endif
|
|
|
+
|
|
|
+void __init
|
|
|
+hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *),
|
|
|
+ int sig, int code, const char *name)
|
|
|
+{
|
|
|
+ if (nr < 0 || nr >= ARRAY_SIZE(fsr_info))
|
|
|
+ BUG();
|
|
|
+
|
|
|
+ fsr_info[nr].fn = fn;
|
|
|
+ fsr_info[nr].sig = sig;
|
|
|
+ fsr_info[nr].code = code;
|
|
|
+ fsr_info[nr].name = name;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Dispatch a data abort to the relevant handler.
|
|
|
+ */
|
|
|
+asmlinkage void __exception
|
|
|
+do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
|
|
|
+{
|
|
|
+ const struct fsr_info *inf = fsr_info + fsr_fs(fsr);
|
|
|
+ struct siginfo info;
|