mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-29 15:43:59 +08:00
834020366d
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>
35 lines
735 B
C
35 lines
735 B
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef __ARCH_ARM_FAULT_H
|
|
#define __ARCH_ARM_FAULT_H
|
|
|
|
/*
|
|
* Fault status register encodings. We steal bit 31 for our own purposes.
|
|
*/
|
|
#define FSR_LNX_PF (1 << 31)
|
|
#define FSR_CM (1 << 13)
|
|
#define FSR_WRITE (1 << 11)
|
|
#define FSR_FS4 (1 << 10)
|
|
#define FSR_FS3_0 (15)
|
|
#define FSR_FS5_0 (0x3f)
|
|
|
|
#ifdef CONFIG_ARM_LPAE
|
|
#define FSR_FS_AEA 17
|
|
|
|
static inline int fsr_fs(unsigned int fsr)
|
|
{
|
|
return fsr & FSR_FS5_0;
|
|
}
|
|
#else
|
|
#define FSR_FS_AEA 22
|
|
|
|
static inline int fsr_fs(unsigned int fsr)
|
|
{
|
|
return (fsr & FSR_FS3_0) | (fsr & FSR_FS4) >> 6;
|
|
}
|
|
#endif
|
|
|
|
void do_bad_area(unsigned long addr, unsigned int fsr, struct pt_regs *regs);
|
|
void early_abt_enable(void);
|
|
|
|
#endif /* __ARCH_ARM_FAULT_H */
|