Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F149527759
D3617.1790351092.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
8 KB
Referenced Files
None
Subscribers
None
D3617.1790351092.diff
View Options
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 */
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Sep 25, 3:44 PM (11 h, 30 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29925906
Default Alt Text
D3617.1790351092.diff (8 KB)
Attached To
Mode
D3617: ARMv6 - fix aborts for copyin/out kind of functions
Attached
Detach File
Event Timeline
Log In to Comment