diff options
Diffstat (limited to 'arch/arm64/mm/fault.c')
-rw-r--r-- | arch/arm64/mm/fault.c | 33 |
1 files changed, 33 insertions, 0 deletions
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index cfd65b63f36f..9808da29a653 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c | |||
@@ -8,6 +8,7 @@ | |||
8 | */ | 8 | */ |
9 | 9 | ||
10 | #include <linux/acpi.h> | 10 | #include <linux/acpi.h> |
11 | #include <linux/bitfield.h> | ||
11 | #include <linux/extable.h> | 12 | #include <linux/extable.h> |
12 | #include <linux/signal.h> | 13 | #include <linux/signal.h> |
13 | #include <linux/mm.h> | 14 | #include <linux/mm.h> |
@@ -242,6 +243,34 @@ static inline bool is_el1_permission_fault(unsigned long addr, unsigned int esr, | |||
242 | return false; | 243 | return false; |
243 | } | 244 | } |
244 | 245 | ||
246 | static bool __kprobes is_spurious_el1_translation_fault(unsigned long addr, | ||
247 | unsigned int esr, | ||
248 | struct pt_regs *regs) | ||
249 | { | ||
250 | unsigned long flags; | ||
251 | u64 par, dfsc; | ||
252 | |||
253 | if (ESR_ELx_EC(esr) != ESR_ELx_EC_DABT_CUR || | ||
254 | (esr & ESR_ELx_FSC_TYPE) != ESR_ELx_FSC_FAULT) | ||
255 | return false; | ||
256 | |||
257 | local_irq_save(flags); | ||
258 | asm volatile("at s1e1r, %0" :: "r" (addr)); | ||
259 | isb(); | ||
260 | par = read_sysreg(par_el1); | ||
261 | local_irq_restore(flags); | ||
262 | |||
263 | if (!(par & SYS_PAR_EL1_F)) | ||
264 | return false; | ||
265 | |||
266 | /* | ||
267 | * If we got a different type of fault from the AT instruction, | ||
268 | * treat the translation fault as spurious. | ||
269 | */ | ||
270 | dfsc = FIELD_PREP(SYS_PAR_EL1_FST, par); | ||
271 | return (dfsc & ESR_ELx_FSC_TYPE) != ESR_ELx_FSC_FAULT; | ||
272 | } | ||
273 | |||
245 | static void die_kernel_fault(const char *msg, unsigned long addr, | 274 | static void die_kernel_fault(const char *msg, unsigned long addr, |
246 | unsigned int esr, struct pt_regs *regs) | 275 | unsigned int esr, struct pt_regs *regs) |
247 | { | 276 | { |
@@ -270,6 +299,10 @@ static void __do_kernel_fault(unsigned long addr, unsigned int esr, | |||
270 | if (!is_el1_instruction_abort(esr) && fixup_exception(regs)) | 299 | if (!is_el1_instruction_abort(esr) && fixup_exception(regs)) |
271 | return; | 300 | return; |
272 | 301 | ||
302 | if (WARN_RATELIMIT(is_spurious_el1_translation_fault(addr, esr, regs), | ||
303 | "Ignoring spurious kernel translation fault at virtual address %016lx\n", addr)) | ||
304 | return; | ||
305 | |||
273 | if (is_el1_permission_fault(addr, esr, regs)) { | 306 | if (is_el1_permission_fault(addr, esr, regs)) { |
274 | if (esr & ESR_ELx_WNR) | 307 | if (esr & ESR_ELx_WNR) |
275 | msg = "write to read-only memory"; | 308 | msg = "write to read-only memory"; |