arm64: Provide read/write fault information in compat signal handlers
authorCatalin Marinas <catalin.marinas@arm.com>
Sun, 6 Apr 2014 22:04:12 +0000 (23:04 +0100)
committerCatalin Marinas <catalin.marinas@arm.com>
Fri, 9 May 2014 14:47:47 +0000 (15:47 +0100)
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>
arch/arm64/include/asm/esr.h
arch/arm64/include/asm/processor.h
arch/arm64/kernel/signal32.c
arch/arm64/kernel/traps.c
arch/arm64/mm/fault.c

index c4a7f940b3870c13fbeb0f3f63a191e44a6c5657..72674f4c3871502f1079674395eaf2921a50bc10 100644 (file)
 #ifndef __ASM_ESR_H
 #define __ASM_ESR_H
 
-#define ESR_EL1_EC_SHIFT       (26)
-#define ESR_EL1_IL             (1U << 25)
+#define ESR_EL1_WRITE          (1 << 6)
+#define ESR_EL1_CM             (1 << 8)
+#define ESR_EL1_IL             (1 << 25)
 
+#define ESR_EL1_EC_SHIFT       (26)
 #define ESR_EL1_EC_UNKNOWN     (0x00)
 #define ESR_EL1_EC_WFI         (0x01)
 #define ESR_EL1_EC_CP15_32     (0x03)
index 45b20cd6cbca3ff1bc33df88f46858be36df8e15..34de2a8f7d9340c8e1ecb7cd78be568ad81147f9 100644 (file)
@@ -79,6 +79,7 @@ struct thread_struct {
        unsigned long           tp_value;
        struct fpsimd_state     fpsimd_state;
        unsigned long           fault_address;  /* fault info */
+       unsigned long           fault_code;     /* ESR_EL1 value */
        struct debug_info       debug;          /* debugging */
 };
 
index b3fc9f5ec6d34b423d70bc5f0fd75836fc307c95..050c1c2af777212f2b0eaab0150348b12b561ebc 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/syscalls.h>
 #include <linux/ratelimit.h>
 
+#include <asm/esr.h>
 #include <asm/fpsimd.h>
 #include <asm/signal32.h>
 #include <asm/uaccess.h>
@@ -81,6 +82,8 @@ struct compat_vfp_sigframe {
 #define VFP_MAGIC              0x56465001
 #define VFP_STORAGE_SIZE       sizeof(struct compat_vfp_sigframe)
 
+#define FSR_WRITE_SHIFT                (11)
+
 struct compat_aux_sigframe {
        struct compat_vfp_sigframe      vfp;
 
@@ -500,7 +503,9 @@ static int compat_setup_sigframe(struct compat_sigframe __user *sf,
        __put_user_error(regs->pstate, &sf->uc.uc_mcontext.arm_cpsr, err);
 
        __put_user_error((compat_ulong_t)0, &sf->uc.uc_mcontext.trap_no, err);
-       __put_user_error((compat_ulong_t)0, &sf->uc.uc_mcontext.error_code, err);
+       /* set the compat FSR WnR */
+       __put_user_error(!!(current->thread.fault_code & ESR_EL1_WRITE) <<
+                        FSR_WRITE_SHIFT, &sf->uc.uc_mcontext.error_code, err);
        __put_user_error(current->thread.fault_address, &sf->uc.uc_mcontext.fault_address, err);
        __put_user_error(set->sig[0], &sf->uc.uc_mcontext.oldmask, err);
 
index 7ffadddb645d32cda5fd54a0c2a5a0c5456b4018..c43cfa9b83041f332a72086a8ad628877a23701f 100644 (file)
@@ -251,10 +251,13 @@ void die(const char *str, struct pt_regs *regs, int err)
 void arm64_notify_die(const char *str, struct pt_regs *regs,
                      struct siginfo *info, int err)
 {
-       if (user_mode(regs))
+       if (user_mode(regs)) {
+               current->thread.fault_address = 0;
+               current->thread.fault_code = err;
                force_sig_info(info->si_signo, info, current);
-       else
+       } else {
                die(str, regs, err);
+       }
 }
 
 asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
index c23751b0612033f56533955788eabefa6c5fd39e..bcc965e2cce1fefca8d0791c2792973d6ab3832b 100644 (file)
@@ -32,6 +32,7 @@
 
 #include <asm/exception.h>
 #include <asm/debug-monitors.h>
+#include <asm/esr.h>
 #include <asm/system_misc.h>
 #include <asm/pgtable.h>
 #include <asm/tlbflush.h>
@@ -123,6 +124,7 @@ static void __do_user_fault(struct task_struct *tsk, unsigned long addr,
        }
 
        tsk->thread.fault_address = addr;
+       tsk->thread.fault_code = esr;
        si.si_signo = sig;
        si.si_errno = 0;
        si.si_code = code;
@@ -148,8 +150,6 @@ static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *re
 #define VM_FAULT_BADMAP                0x010000
 #define VM_FAULT_BADACCESS     0x020000
 
-#define ESR_WRITE              (1 << 6)
-#define ESR_CM                 (1 << 8)
 #define ESR_LNX_EXEC           (1 << 24)
 
 static int __do_page_fault(struct mm_struct *mm, unsigned long addr,
@@ -218,7 +218,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
 
        if (esr & ESR_LNX_EXEC) {
                vm_flags = VM_EXEC;
-       } else if ((esr & ESR_WRITE) && !(esr & ESR_CM)) {
+       } else if ((esr & ESR_EL1_WRITE) && !(esr & ESR_EL1_CM)) {
                vm_flags = VM_WRITE;
                mm_flags |= FAULT_FLAG_WRITE;
        }
@@ -525,7 +525,7 @@ asmlinkage int __exception do_debug_exception(unsigned long addr,
        info.si_errno = 0;
        info.si_code  = inf->code;
        info.si_addr  = (void __user *)addr;
-       arm64_notify_die("", regs, &info, esr);
+       arm64_notify_die("", regs, &info, 0);
 
        return 0;
 }