Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

arm64: Provide read/write fault information in compat signal handlers

For AArch32, bit 11 (WnR) of the FSR/ESR register is set when the fault
was caused by a write access and applications like Qemu rely on such
information being provided in sigcontext. This patch introduces the
ESR_EL1 tracking for the arm64 kernel faults and sets bit 11 accordingly
in compat sigcontext.

Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>

+20 -9
+4 -2
arch/arm64/include/asm/esr.h
··· 18 18 #ifndef __ASM_ESR_H 19 19 #define __ASM_ESR_H 20 20 21 - #define ESR_EL1_EC_SHIFT (26) 22 - #define ESR_EL1_IL (1U << 25) 21 + #define ESR_EL1_WRITE (1 << 6) 22 + #define ESR_EL1_CM (1 << 8) 23 + #define ESR_EL1_IL (1 << 25) 23 24 25 + #define ESR_EL1_EC_SHIFT (26) 24 26 #define ESR_EL1_EC_UNKNOWN (0x00) 25 27 #define ESR_EL1_EC_WFI (0x01) 26 28 #define ESR_EL1_EC_CP15_32 (0x03)
+1
arch/arm64/include/asm/processor.h
··· 79 79 unsigned long tp_value; 80 80 struct fpsimd_state fpsimd_state; 81 81 unsigned long fault_address; /* fault info */ 82 + unsigned long fault_code; /* ESR_EL1 value */ 82 83 struct debug_info debug; /* debugging */ 83 84 }; 84 85
+6 -1
arch/arm64/kernel/signal32.c
··· 23 23 #include <linux/syscalls.h> 24 24 #include <linux/ratelimit.h> 25 25 26 + #include <asm/esr.h> 26 27 #include <asm/fpsimd.h> 27 28 #include <asm/signal32.h> 28 29 #include <asm/uaccess.h> ··· 81 80 82 81 #define VFP_MAGIC 0x56465001 83 82 #define VFP_STORAGE_SIZE sizeof(struct compat_vfp_sigframe) 83 + 84 + #define FSR_WRITE_SHIFT (11) 84 85 85 86 struct compat_aux_sigframe { 86 87 struct compat_vfp_sigframe vfp; ··· 503 500 __put_user_error(regs->pstate, &sf->uc.uc_mcontext.arm_cpsr, err); 504 501 505 502 __put_user_error((compat_ulong_t)0, &sf->uc.uc_mcontext.trap_no, err); 506 - __put_user_error((compat_ulong_t)0, &sf->uc.uc_mcontext.error_code, err); 503 + /* set the compat FSR WnR */ 504 + __put_user_error(!!(current->thread.fault_code & ESR_EL1_WRITE) << 505 + FSR_WRITE_SHIFT, &sf->uc.uc_mcontext.error_code, err); 507 506 __put_user_error(current->thread.fault_address, &sf->uc.uc_mcontext.fault_address, err); 508 507 __put_user_error(set->sig[0], &sf->uc.uc_mcontext.oldmask, err); 509 508
+5 -2
arch/arm64/kernel/traps.c
··· 251 251 void arm64_notify_die(const char *str, struct pt_regs *regs, 252 252 struct siginfo *info, int err) 253 253 { 254 - if (user_mode(regs)) 254 + if (user_mode(regs)) { 255 + current->thread.fault_address = 0; 256 + current->thread.fault_code = err; 255 257 force_sig_info(info->si_signo, info, current); 256 - else 258 + } else { 257 259 die(str, regs, err); 260 + } 258 261 } 259 262 260 263 asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
+4 -4
arch/arm64/mm/fault.c
··· 32 32 33 33 #include <asm/exception.h> 34 34 #include <asm/debug-monitors.h> 35 + #include <asm/esr.h> 35 36 #include <asm/system_misc.h> 36 37 #include <asm/pgtable.h> 37 38 #include <asm/tlbflush.h> ··· 124 123 } 125 124 126 125 tsk->thread.fault_address = addr; 126 + tsk->thread.fault_code = esr; 127 127 si.si_signo = sig; 128 128 si.si_errno = 0; 129 129 si.si_code = code; ··· 150 148 #define VM_FAULT_BADMAP 0x010000 151 149 #define VM_FAULT_BADACCESS 0x020000 152 150 153 - #define ESR_WRITE (1 << 6) 154 - #define ESR_CM (1 << 8) 155 151 #define ESR_LNX_EXEC (1 << 24) 156 152 157 153 static int __do_page_fault(struct mm_struct *mm, unsigned long addr, ··· 218 218 219 219 if (esr & ESR_LNX_EXEC) { 220 220 vm_flags = VM_EXEC; 221 - } else if ((esr & ESR_WRITE) && !(esr & ESR_CM)) { 221 + } else if ((esr & ESR_EL1_WRITE) && !(esr & ESR_EL1_CM)) { 222 222 vm_flags = VM_WRITE; 223 223 mm_flags |= FAULT_FLAG_WRITE; 224 224 } ··· 525 525 info.si_errno = 0; 526 526 info.si_code = inf->code; 527 527 info.si_addr = (void __user *)addr; 528 - arm64_notify_die("", regs, &info, esr); 528 + arm64_notify_die("", regs, &info, 0); 529 529 530 530 return 0; 531 531 }