Index: sys/arm/arm/pmap-v6-new.c =================================================================== --- sys/arm/arm/pmap-v6-new.c +++ sys/arm/arm/pmap-v6-new.c @@ -6158,8 +6158,9 @@ * All L1 tables should always be mapped and present. * However, we check only current one herein. For user mode, * only permission abort from malicious user is not fatal. + * And alignment fault as it may have higher priority. */ - if (!usermode || (idx != FAULT_PERM_L2)) { + if (!usermode || (idx != FAULT_ALIGN && idx != FAULT_PERM_L2)) { CTR4(KTR_PMAP, "%s: pmap %#x pm_pt1 %#x far %#x", __func__, pmap, pmap->pm_pt1, far); panic("%s: pm_pt1 abort", __func__); @@ -6172,9 +6173,10 @@ * L1 table. However, only existing L2 tables are mapped * in PT2MAP. For user mode, only L2 translation abort and * permission abort from malicious user is not fatal. + * And alignment fault as it may have higher priority. */ - if (!usermode || - (idx != FAULT_TRAN_L2 && idx != FAULT_PERM_L2)) { + if (!usermode || (idx != FAULT_ALIGN && + idx != FAULT_TRAN_L2 && idx != FAULT_PERM_L2)) { CTR4(KTR_PMAP, "%s: pmap %#x PT2MAP %#x far %#x", __func__, pmap, PT2MAP, far); panic("%s: PT2MAP abort", __func__); Index: sys/arm/arm/trap-v6.c =================================================================== --- sys/arm/arm/trap-v6.c +++ sys/arm/arm/trap-v6.c @@ -168,6 +168,86 @@ }; +static __inline int +read_instruction_nofault(vm_offset_t addr, uint32_t *inst) +{ + + if (!ALIGNED_POINTER(addr, uint32_t) || cp15_ats1cpr_check(addr) != 0) + return (EFAULT); + + *inst = *((uint32_t*)addr); + return (0); +} + +/* + * There are eight load/store unpriviledged instructions and each of them has + * two 32-bits encodings. And one more 16-bits (thumb) encoding, but not used + * in kernel. + * + * bit 27-20 bit 7-4 + * + * STRT A1 01 00 x 010 x x x x + * A2 01 10 x 010 x x x 0 + * STRHT A1 00 00 x 110 1 0 1 1 + * A2 00 00 x 010 1 0 1 1 + * STRBT A1 01 00 x 110 x x x x + * A2 01 10 x 110 x x x 0 + * LDRT A1 01 00 x 011 x x x x + * A2 01 10 x 011 x x x 0 + * LDRHT A1 00 00 x 111 1 0 1 1 + * A2 00 00 x 011 1 0 1 1 + * LDRSHT A1 00 00 x 111 1 1 1 1 + * A2 00 00 x 011 1 1 1 1 + * LDRBT A1 01 00 x 111 x x x x + * A2 01 10 x 111 x x x 0 + * LDRSBT A1 00 00 x 111 1 1 0 1 + * A2 00 00 x 011 1 1 0 1 + * + * The bit x and other bits in these encodings are arbitrary (conditions, + * registers, immediate values, ... ). + * + * We can group these encodings into four sets: + * + * STRT A1 01 00 x 010 x x x x + * STRBT A1 01 00 x 110 x x x x + * LDRT A1 01 00 x 011 x x x x + * LDRBT A1 01 00 x 111 x x x x + * + * (inst & 0x0f200000) == 0x04200000 + * + * STRT A2 01 10 x 010 x x x 0 + * STRBT A2 01 10 x 110 x x x 0 + * LDRT A2 01 10 x 011 x x x 0 + * LDRBT A2 01 10 x 111 x x x 0 + * + * (inst & 0x0f200010) == 0x06200000 + * + * STRHT A1 00 00 x 110 1 0 1 1 + * A2 00 00 x 010 1 0 1 1 + * LDRHT A1 00 00 x 111 1 0 1 1 + * A2 00 00 x 011 1 0 1 1 + * + * (inst & 0x0f2000f0) == 0x002000b0 + * + * LDRSHT A1 00 00 x 111 1 1 1 1 + * A2 00 00 x 011 1 1 1 1 + * LDRSBT A1 00 00 x 111 1 1 0 1 + * A2 00 00 x 011 1 1 0 1 + * + * (inst & 0x0f3000d0) == 0x003000d0 + */ +static __inline bool +is_instruction_unprivileged(uint32_t inst) +{ + + if ((inst & 0x0f200000) == 0x04200000 || + (inst & 0x0f200010) == 0x06200000 || + (inst & 0x0f2000f0) == 0x002000b0 || + (inst & 0x0f3000d0) == 0x003000d0) + return (true); + return (false); +} + static __inline void call_trapsignal(struct thread *td, int sig, int code, vm_offset_t addr) { @@ -275,7 +355,7 @@ { struct thread *td; vm_offset_t far, va; - int idx, usermode; + int idx, usermode, usr_copy_fault; uint32_t fsr; struct ksig ksig; struct proc *p; @@ -300,8 +380,8 @@ if (usermode) td->td_frame = tf; - CTR4(KTR_TRAP, "abort_handler: fsr %#x (idx %u) far %#x prefetch %u", - fsr, idx, far, prefetch); + CTR6(KTR_TRAP, "%s: fsr %#x (idx %u) far %#x prefetch %u user %u", + __func__, fsr, idx, far, prefetch, usermode); /* * Firstly, handle aborts that are not directly related to mapping. @@ -316,16 +396,39 @@ return; } -#ifdef ARM_NEW_PMAP - rv = pmap_fault(PCPU_GET(curpmap), far, fsr, idx, usermode); - if (rv == 0) { - return; - } else if (rv == EFAULT) { + /* + * ARM has a set of unprivileged load and store instructions + * (LDRT/LDRBT/STRT/STRBT ...) which are supposed to be used in other + * than user mode and OS should recognize their aborts and behave + * appropriately. However, there is no way how to do that reasonably + * in general unless we restrict the handling somehow. + * + * Thus, we only handle such cases when these instructions are used + * while pcb_onfault is not NULL. This is quite reasonable restriction + * as the pcb_onfault is used for quick error handling of access to + * user address space done in kernel. + */ + usr_copy_fault = 0; + pcb = td->td_pcb; + if (__predict_false(!usermode && pcb->pcb_onfault != NULL)) { + uint32_t inst; - call_trapsignal(td, SIGSEGV, SEGV_MAPERR, far); - userret(td, tf); - return; + /* Read instruction safely to not double abort. */ + if (read_instruction_nofault(TRAPF_PC(tf), &inst) != 0) + panic("%s: bad PC %x", __func__, TRAPF_PC(tf)); + + /* Is this load/store unprivileged instruction? */ + if (is_instruction_unprivileged(inst)) + usr_copy_fault = 1; } + +#ifdef ARM_NEW_PMAP + rv = pmap_fault(PCPU_GET(curpmap), far, fsr, idx, usermode || + usr_copy_fault); + if (rv == 0) + return; + if (rv == EFAULT) + goto nogo; #endif /* * Now, when we handled imprecise and debug aborts, the rest of @@ -410,7 +513,6 @@ * Don't pass faulting cache operation to vm_fault(). We don't want * to handle all vm stuff at this moment. */ - pcb = td->td_pcb; if (__predict_false(pcb->pcb_onfault == cachebailout)) { tf->tf_r0 = far; /* return failing address */ tf->tf_pc = (register_t)pcb->pcb_onfault; @@ -435,32 +537,18 @@ */ /* fusubailout is used by [fs]uswintr to avoid page faulting */ - pcb = td->td_pcb; if (__predict_false(pcb->pcb_onfault == fusubailout)) { tf->tf_r0 = EFAULT; tf->tf_pc = (register_t)pcb->pcb_onfault; return; } - /* - * QQQ: ARM has a set of unprivileged load and store instructions - * (LDRT/LDRBT/STRT/STRBT ...) which are supposed to be used - * in other than user mode and OS should recognize their - * aborts and behaved appropriately. However, there is no way - * how to do that reasonably in general unless we restrict - * the handling somehow. One way is to limit the handling for - * aborts which come from undefined mode only. - * - * Anyhow, we do not use these instructions and do not implement - * any special handling for them. - */ - va = trunc_page(far); if (va >= KERNBASE) { /* * Don't allow user-mode faults in kernel address space. */ - if (usermode) + if (usermode || usr_copy_fault) goto nogo; map = kernel_map; Index: sys/arm/include/cpu-v6.h =================================================================== --- sys/arm/include/cpu-v6.h +++ sys/arm/include/cpu-v6.h @@ -151,14 +151,12 @@ _RF0(cp15_ifar_get, CP15_IFAR(%0)) _RF0(cp15_l2ctlr_get, CP15_L2CTLR(%0)) #endif -/* ARMv6+ and XScale */ _RF0(cp15_actlr_get, CP15_ACTLR(%0)) _WF1(cp15_actlr_set, CP15_ACTLR(%0)) -#if __ARM_ARCH >= 6 -_WF1(cp15_ats1cpr_set, CP15_ATS1CPR(%0)); -_RF0(cp15_par_get, CP15_PAR); +_WF1(cp15_ats1cpr_set, CP15_ATS1CPR(%0)) +_WF1(cp15_ats1cpw_set, CP15_ATS1CPW(%0)) +_RF0(cp15_par_get, CP15_PAR(%0)) _RF0(cp15_sctlr_get, CP15_SCTLR(%0)) -#endif /*CPU id registers */ _RF0(cp15_midr_get, CP15_MIDR(%0)) @@ -531,6 +529,34 @@ tlb_flush_all_ng_local(); } +/* Check address for privileged (PL1) read access. */ +static __inline int +cp15_ats1cpr_check(vm_offset_t addr) +{ + register_t par, s; + + s = intr_disable(); + cp15_ats1cpr_set(addr); + isb(); + par = cp15_par_get(); + intr_restore(s); + return (par & 0x01 ? EFAULT : 0); +} + +/* Check address for privileged (PL1) write access. */ +static __inline int +cp15_ats1cpw_check(vm_offset_t addr) +{ + register_t par, s; + + s = intr_disable(); + cp15_ats1cpw_set(addr); + isb(); + par = cp15_par_get(); + intr_restore(s); + return (par & 0x01 ? EFAULT : 0); +} + #endif /* _KERNEL */ #endif /* !MACHINE_CPU_V6_H */ Index: sys/arm/include/sysreg.h =================================================================== --- sys/arm/include/sysreg.h +++ sys/arm/include/sysreg.h @@ -130,7 +130,7 @@ #define CP15_BPIALLIS p15, 0, r0, c7, c1, 6 /* Branch predictor invalidate all IS */ #endif -#define CP15_PAR p15, 0, r0, c7, c4, 0 /* Physical Address Register */ +#define CP15_PAR(rr) p15, 0, rr, c7, c4, 0 /* Physical Address Register */ #define CP15_ICIALLU p15, 0, r0, c7, c5, 0 /* Instruction cache invalidate all PoU */ #define CP15_ICIMVAU(rr) p15, 0, rr, c7, c5, 1 /* Instruction cache invalidate */