summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWill Deacon <will@kernel.org>2019-08-08 11:51:00 -0400
committerRussell King <rmk+kernel@armlinux.org.uk>2019-08-23 06:39:34 -0400
commit834020366da9ab3fb87d1eb9a3160eb22dbed63a (patch)
treea8c17ef8b5517a5f5b72fcc5df42954e8122da7c
parent3e07590e7248db951fed6a2039403b5a39010be7 (diff)
ARM: 8898/1: mm: Don't treat faults reported from cache maintenance as writes
Translation faults arising from cache maintenance instructions are rather unhelpfully reported with an FSR value where the WnR field is set to 1, indicating that the faulting access was a write. Since cache maintenance instructions on 32-bit ARM do not require any particular permissions, this can cause our private 'cacheflush' system call to fail spuriously if a translation fault is generated due to page aging when targetting a read-only VMA. In this situation, we will return -EFAULT to userspace, although this is unfortunately suppressed by the popular '__builtin___clear_cache()' intrinsic provided by GCC, which returns void. Although it's tempting to write this off as a userspace issue, we can actually do a little bit better on CPUs that support LPAE, even if the short-descriptor format is in use. On these CPUs, cache maintenance faults additionally set the CM field in the FSR, which we can use to suppress the write permission checks in the page fault handler and succeed in performing cache maintenance to read-only areas even in the presence of a translation fault. Reported-by: Orion Hodson <oth@google.com> Signed-off-by: Will Deacon <will@kernel.org> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
-rw-r--r--arch/arm/mm/fault.c4
-rw-r--r--arch/arm/mm/fault.h1
2 files changed, 3 insertions, 2 deletions
diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c
index 890eeaac3cbb..bd0f4821f7e1 100644
--- a/arch/arm/mm/fault.c
+++ b/arch/arm/mm/fault.c
@@ -191,7 +191,7 @@ static inline bool access_error(unsigned int fsr, struct vm_area_struct *vma)
191{ 191{
192 unsigned int mask = VM_READ | VM_WRITE | VM_EXEC; 192 unsigned int mask = VM_READ | VM_WRITE | VM_EXEC;
193 193
194 if (fsr & FSR_WRITE) 194 if ((fsr & FSR_WRITE) && !(fsr & FSR_CM))
195 mask = VM_WRITE; 195 mask = VM_WRITE;
196 if (fsr & FSR_LNX_PF) 196 if (fsr & FSR_LNX_PF)
197 mask = VM_EXEC; 197 mask = VM_EXEC;
@@ -262,7 +262,7 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
262 262
263 if (user_mode(regs)) 263 if (user_mode(regs))
264 flags |= FAULT_FLAG_USER; 264 flags |= FAULT_FLAG_USER;
265 if (fsr & FSR_WRITE) 265 if ((fsr & FSR_WRITE) && !(fsr & FSR_CM))
266 flags |= FAULT_FLAG_WRITE; 266 flags |= FAULT_FLAG_WRITE;
267 267
268 /* 268 /*
diff --git a/arch/arm/mm/fault.h b/arch/arm/mm/fault.h
index c063708fa503..9ecc2097a87a 100644
--- a/arch/arm/mm/fault.h
+++ b/arch/arm/mm/fault.h
@@ -6,6 +6,7 @@
6 * Fault status register encodings. We steal bit 31 for our own purposes. 6 * Fault status register encodings. We steal bit 31 for our own purposes.
7 */ 7 */
8#define FSR_LNX_PF (1 << 31) 8#define FSR_LNX_PF (1 << 31)
9#define FSR_CM (1 << 13)
9#define FSR_WRITE (1 << 11) 10#define FSR_WRITE (1 << 11)
10#define FSR_FS4 (1 << 10) 11#define FSR_FS4 (1 << 10)
11#define FSR_FS3_0 (15) 12#define FSR_FS3_0 (15)