-/* $OpenBSD: fault.c,v 1.22 2016/08/19 19:07:37 kettenis Exp $ */
+/* $OpenBSD: fault.c,v 1.23 2016/08/24 13:09:52 kettenis Exp $ */
/* $NetBSD: fault.c,v 1.46 2004/01/21 15:39:21 skrll Exp $ */
/*
struct sigdata *sd);
static int dab_buserr(trapframe_t *, u_int, u_int, struct proc *,
struct sigdata *sd);
+extern int dab_access(trapframe_t *, u_int, u_int, struct proc *,
+ struct sigdata *sd);
static const struct data_abort data_aborts[] = {
#ifndef CPU_ARMv7
{dab_fatal, "V7 fault 00000"},
{dab_align, "Alignment fault"},
{dab_fatal, "Debug event"},
- {NULL, "Access flag fault (L1)"},
+ {dab_fatal, "Access flag fault (L1)"},
{dab_buserr, "Fault on instruction cache maintenance"},
{NULL, "Translation fault (L1)"},
- {NULL, "Access flag fault (L2)"},
+ {dab_access, "Access flag fault (L2)"},
{NULL, "Translation fault (L2)"},
{dab_buserr, "Synchronous external abort"},
{NULL, "Domain fault (L1)"},
ftype = fsr & FAULT_WNR ? PROT_WRITE : PROT_READ;
#endif
+#ifndef CPU_ARMv7
/*
* See if the fault is as a result of ref/mod emulation,
* or domain mismatch.
#endif
goto out;
}
+#endif
if (__predict_false(curcpu()->ci_idepth > 0)) {
if (pcb->pcb_onfault) {
if (__predict_true((tf->tf_spsr & PSR_I) == 0))
enable_interrupts(PSR_I);
- /* Get fault address */
p = curproc;
+
+#ifdef CPU_ARMv7
+ /* Invoke access fault handler if appropriate */
+ if (FAULT_TYPE_V7(fsr) == FAULT_ACCESS_2) {
+ dab_access(tf, fsr, far, p, NULL);
+ goto out;
+ }
+#endif
+
p->p_addr->u_pcb.pcb_tf = tf;
/* Ok validate the address, can only execute in USER space */
map = &p->p_vmspace->vm_map;
va = trunc_page(far);
+#ifndef CPU_ARMv7
/*
* See if the pmap can handle this fault on its own...
*/
#endif
if (pmap_fault_fixup(map->pmap, va, PROT_READ | PROT_EXEC, 1))
goto out;
+#endif
#ifdef DIAGNOSTIC
if (__predict_false(curcpu()->ci_idepth > 0)) {
-/* $OpenBSD: pmap7.c,v 1.46 2016/08/20 21:08:16 kettenis Exp $ */
+/* $OpenBSD: pmap7.c,v 1.47 2016/08/24 13:09:52 kettenis Exp $ */
/* $NetBSD: pmap.c,v 1.147 2004/01/18 13:03:50 scw Exp $ */
/*
*/
/* See <arm/pmap.h> */
+/*
+ * dab_access() handles the following data aborts:
+ *
+ * FAULT_ACCESS_2 - Access flag fault -- Level 2
+ *
+ * Set the Access Flag and mark the page as referenced.
+ */
int
-pmap_fault_fixup(pmap_t pm, vaddr_t va, vm_prot_t ftype, int user)
+dab_access(trapframe_t *tf, u_int fsr, u_int far, struct proc *p)
{
+ struct pmap *pm = p->p_vmspace->vm_map.pmap;
+ vaddr_t va = trunc_page(far);
struct l2_dtable *l2;
struct l2_bucket *l2b;
- pd_entry_t *pl1pd, l1pd;
pt_entry_t *ptep, pte;
+ struct pv_entry *pv;
+ struct vm_page *pg;
paddr_t pa;
u_int l1idx;
- int rv = 0;
l1idx = L1_IDX(va);
/*
* If there is no l2_dtable for this address, then the process
* has no business accessing it.
- *
- * Note: This will catch userland processes trying to access
- * kernel addresses.
*/
l2 = pm->pm_l2[L2_IDX(l1idx)];
- if (l2 == NULL)
- goto out;
+ KASSERT(l2 != NULL);
/*
* Likewise if there is no L2 descriptor table
*/
l2b = &l2->l2_bucket[L2_BUCKET(l1idx)];
- if (l2b->l2b_kva == NULL)
- goto out;
+ KASSERT(l2b->l2b_kva != NULL);
/*
* Check the PTE itself.
*/
ptep = &l2b->l2b_kva[l2pte_index(va)];
pte = *ptep;
- if (pte == L2_TYPE_INV)
- goto out;
+ KASSERT(pte != L2_TYPE_INV);
- if ((ftype & PROT_EXEC) && (pte & L2_V7_S_XN))
- goto out;
+ pa = l2pte_pa(pte);
- /* only if vectors are low ?? */
/*
- * Catch a userland access to the vector page mapped at 0x0
+ * Perform page referenced emulation.
*/
- if (user) {
- if ((pte & L2_V7_AP(0x2)) == 0)
- goto out;
- }
-
- pa = l2pte_pa(pte);
-
- if ((ftype & PROT_WRITE) && !l2pte_is_writeable(pte, pm)) {
- /*
- * This looks like a good candidate for "page modified"
- * emulation...
- */
- struct pv_entry *pv;
- struct vm_page *pg;
-
- /* Extract the physical address of the page */
- if ((pg = PHYS_TO_VM_PAGE(pa)) == NULL)
- goto out;
-
- /* Get the current flags for this page. */
- pv = pmap_find_pv(pg, pm, va);
- if (pv == NULL)
- goto out;
-
- /*
- * Do the flags say this page is writable? If not then it
- * is a genuine write fault. If yes then the write fault is
- * our fault as we did not reflect the write access in the
- * PTE. Now we know a write has occurred we can correct this
- * and also set the modified bit
- */
- if ((pv->pv_flags & PVF_WRITE) == 0)
- goto out;
-
- NPDEBUG(PDB_FOLLOW,
- printf("pmap_fault_fixup: mod emul. pm %p, va 0x%08lx, pa 0x%08lx\n",
- pm, va, pg->phys_addr));
-
- pg->mdpage.pvh_attrs |= PVF_REF | PVF_MOD;
- pv->pv_flags |= PVF_REF | PVF_MOD;
-
- /*
- * Re-enable write permissions for the page.
- * We've already set the cacheable bits based on
- * the assumption that we can write to this page.
- */
- *ptep = (pte & ~L2_V7_AP(0x4));
- PTE_SYNC(ptep);
- rv = 1;
- } else if ((pte & L2_V7_AF) == 0) {
- /*
- * This looks like a good candidate for "page referenced"
- * emulation.
- */
- struct pv_entry *pv;
- struct vm_page *pg;
-
- /* Extract the physical address of the page */
- if ((pg = PHYS_TO_VM_PAGE(pa)) == NULL)
- goto out;
+ KASSERT((pte & L2_V7_AF) == 0);
- /* Get the current flags for this page. */
- pv = pmap_find_pv(pg, pm, va);
- if (pv == NULL)
- goto out;
-
- pg->mdpage.pvh_attrs |= PVF_REF;
- pv->pv_flags |= PVF_REF;
- pte |= L2_V7_AF;
-
- NPDEBUG(PDB_FOLLOW,
- printf("pmap_fault_fixup: ref emul. pm %p, va 0x%08lx, pa 0x%08lx\n",
- pm, va, pg->phys_addr));
-
- *ptep = pte;
- PTE_SYNC(ptep);
- rv = 1;
- } else {
-printf("%s: va %08lx ftype %x %c pte %08x\n", __func__, va, ftype, user ? 'u' : 's', pte);
- goto out;
- }
+ /* Extract the physical address of the page */
+ pg = PHYS_TO_VM_PAGE(pa);
+ KASSERT(pg != NULL);
- /*
- * We know there is a valid mapping here, so simply
- * fix up the L1 if necessary.
- */
- pl1pd = &pm->pm_l1->l1_kva[l1idx];
- l1pd = l2b->l2b_phys | L1_C_DOM(pm->pm_domain) | L1_C_PROTO;
- if (*pl1pd != l1pd) {
- *pl1pd = l1pd;
- PTE_SYNC(pl1pd);
- rv = 1;
- }
+ /* Get the current flags for this page. */
+ pv = pmap_find_pv(pg, pm, va);
+ KASSERT(pv != NULL);
- if (rv) {
- cpu_tlb_flushID_SE(va);
- cpu_cpwait();
- }
+ pg->mdpage.pvh_attrs |= PVF_REF;
+ pv->pv_flags |= PVF_REF;
+ pte |= L2_V7_AF;
-out:
- return (rv);
+ *ptep = pte;
+ PTE_SYNC(ptep);
+ return 0;
}
/*