diff options
author | Catalin Marinas <catalin.marinas@arm.com> | 2014-04-06 18:04:12 -0400 |
---|---|---|
committer | Catalin Marinas <catalin.marinas@arm.com> | 2014-05-09 10:47:47 -0400 |
commit | 9141300a5884b57cea6d32c4e3fd16a337cfc99a (patch) | |
tree | d717d0ae66c7458c922b885c689b9c192b0f4083 | |
parent | 6400111399e16a535231ebd76389c894ea1837ff (diff) |
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>
-rw-r--r-- | arch/arm64/include/asm/esr.h | 6 | ||||
-rw-r--r-- | arch/arm64/include/asm/processor.h | 1 | ||||
-rw-r--r-- | arch/arm64/kernel/signal32.c | 7 | ||||
-rw-r--r-- | arch/arm64/kernel/traps.c | 7 | ||||
-rw-r--r-- | arch/arm64/mm/fault.c | 8 |
5 files changed, 20 insertions, 9 deletions
diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h index c4a7f940b387..72674f4c3871 100644 --- a/arch/arm64/include/asm/esr.h +++ b/arch/arm64/include/asm/esr.h | |||
@@ -18,9 +18,11 @@ | |||
18 | #ifndef __ASM_ESR_H | 18 | #ifndef __ASM_ESR_H |
19 | #define __ASM_ESR_H | 19 | #define __ASM_ESR_H |
20 | 20 | ||
21 | #define ESR_EL1_EC_SHIFT (26) | 21 | #define ESR_EL1_WRITE (1 << 6) |
22 | #define ESR_EL1_IL (1U << 25) | 22 | #define ESR_EL1_CM (1 << 8) |
23 | #define ESR_EL1_IL (1 << 25) | ||
23 | 24 | ||
25 | #define ESR_EL1_EC_SHIFT (26) | ||
24 | #define ESR_EL1_EC_UNKNOWN (0x00) | 26 | #define ESR_EL1_EC_UNKNOWN (0x00) |
25 | #define ESR_EL1_EC_WFI (0x01) | 27 | #define ESR_EL1_EC_WFI (0x01) |
26 | #define ESR_EL1_EC_CP15_32 (0x03) | 28 | #define ESR_EL1_EC_CP15_32 (0x03) |
diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h index 45b20cd6cbca..34de2a8f7d93 100644 --- a/arch/arm64/include/asm/processor.h +++ b/arch/arm64/include/asm/processor.h | |||
@@ -79,6 +79,7 @@ struct thread_struct { | |||
79 | unsigned long tp_value; | 79 | unsigned long tp_value; |
80 | struct fpsimd_state fpsimd_state; | 80 | struct fpsimd_state fpsimd_state; |
81 | unsigned long fault_address; /* fault info */ | 81 | unsigned long fault_address; /* fault info */ |
82 | unsigned long fault_code; /* ESR_EL1 value */ | ||
82 | struct debug_info debug; /* debugging */ | 83 | struct debug_info debug; /* debugging */ |
83 | }; | 84 | }; |
84 | 85 | ||
diff --git a/arch/arm64/kernel/signal32.c b/arch/arm64/kernel/signal32.c index b3fc9f5ec6d3..050c1c2af777 100644 --- a/arch/arm64/kernel/signal32.c +++ b/arch/arm64/kernel/signal32.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/syscalls.h> | 23 | #include <linux/syscalls.h> |
24 | #include <linux/ratelimit.h> | 24 | #include <linux/ratelimit.h> |
25 | 25 | ||
26 | #include <asm/esr.h> | ||
26 | #include <asm/fpsimd.h> | 27 | #include <asm/fpsimd.h> |
27 | #include <asm/signal32.h> | 28 | #include <asm/signal32.h> |
28 | #include <asm/uaccess.h> | 29 | #include <asm/uaccess.h> |
@@ -81,6 +82,8 @@ struct compat_vfp_sigframe { | |||
81 | #define VFP_MAGIC 0x56465001 | 82 | #define VFP_MAGIC 0x56465001 |
82 | #define VFP_STORAGE_SIZE sizeof(struct compat_vfp_sigframe) | 83 | #define VFP_STORAGE_SIZE sizeof(struct compat_vfp_sigframe) |
83 | 84 | ||
85 | #define FSR_WRITE_SHIFT (11) | ||
86 | |||
84 | struct compat_aux_sigframe { | 87 | struct compat_aux_sigframe { |
85 | struct compat_vfp_sigframe vfp; | 88 | struct compat_vfp_sigframe vfp; |
86 | 89 | ||
@@ -500,7 +503,9 @@ static int compat_setup_sigframe(struct compat_sigframe __user *sf, | |||
500 | __put_user_error(regs->pstate, &sf->uc.uc_mcontext.arm_cpsr, err); | 503 | __put_user_error(regs->pstate, &sf->uc.uc_mcontext.arm_cpsr, err); |
501 | 504 | ||
502 | __put_user_error((compat_ulong_t)0, &sf->uc.uc_mcontext.trap_no, err); | 505 | __put_user_error((compat_ulong_t)0, &sf->uc.uc_mcontext.trap_no, err); |
503 | __put_user_error((compat_ulong_t)0, &sf->uc.uc_mcontext.error_code, err); | 506 | /* set the compat FSR WnR */ |
507 | __put_user_error(!!(current->thread.fault_code & ESR_EL1_WRITE) << | ||
508 | FSR_WRITE_SHIFT, &sf->uc.uc_mcontext.error_code, err); | ||
504 | __put_user_error(current->thread.fault_address, &sf->uc.uc_mcontext.fault_address, err); | 509 | __put_user_error(current->thread.fault_address, &sf->uc.uc_mcontext.fault_address, err); |
505 | __put_user_error(set->sig[0], &sf->uc.uc_mcontext.oldmask, err); | 510 | __put_user_error(set->sig[0], &sf->uc.uc_mcontext.oldmask, err); |
506 | 511 | ||
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index 7ffadddb645d..c43cfa9b8304 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c | |||
@@ -251,10 +251,13 @@ void die(const char *str, struct pt_regs *regs, int err) | |||
251 | void arm64_notify_die(const char *str, struct pt_regs *regs, | 251 | void arm64_notify_die(const char *str, struct pt_regs *regs, |
252 | struct siginfo *info, int err) | 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 | force_sig_info(info->si_signo, info, current); | 257 | force_sig_info(info->si_signo, info, current); |
256 | else | 258 | } else { |
257 | die(str, regs, err); | 259 | die(str, regs, err); |
260 | } | ||
258 | } | 261 | } |
259 | 262 | ||
260 | asmlinkage void __exception do_undefinstr(struct pt_regs *regs) | 263 | asmlinkage void __exception do_undefinstr(struct pt_regs *regs) |
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index c23751b06120..bcc965e2cce1 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c | |||
@@ -32,6 +32,7 @@ | |||
32 | 32 | ||
33 | #include <asm/exception.h> | 33 | #include <asm/exception.h> |
34 | #include <asm/debug-monitors.h> | 34 | #include <asm/debug-monitors.h> |
35 | #include <asm/esr.h> | ||
35 | #include <asm/system_misc.h> | 36 | #include <asm/system_misc.h> |
36 | #include <asm/pgtable.h> | 37 | #include <asm/pgtable.h> |
37 | #include <asm/tlbflush.h> | 38 | #include <asm/tlbflush.h> |
@@ -123,6 +124,7 @@ static void __do_user_fault(struct task_struct *tsk, unsigned long addr, | |||
123 | } | 124 | } |
124 | 125 | ||
125 | tsk->thread.fault_address = addr; | 126 | tsk->thread.fault_address = addr; |
127 | tsk->thread.fault_code = esr; | ||
126 | si.si_signo = sig; | 128 | si.si_signo = sig; |
127 | si.si_errno = 0; | 129 | si.si_errno = 0; |
128 | si.si_code = code; | 130 | si.si_code = code; |
@@ -148,8 +150,6 @@ static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *re | |||
148 | #define VM_FAULT_BADMAP 0x010000 | 150 | #define VM_FAULT_BADMAP 0x010000 |
149 | #define VM_FAULT_BADACCESS 0x020000 | 151 | #define VM_FAULT_BADACCESS 0x020000 |
150 | 152 | ||
151 | #define ESR_WRITE (1 << 6) | ||
152 | #define ESR_CM (1 << 8) | ||
153 | #define ESR_LNX_EXEC (1 << 24) | 153 | #define ESR_LNX_EXEC (1 << 24) |
154 | 154 | ||
155 | static int __do_page_fault(struct mm_struct *mm, unsigned long addr, | 155 | 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, | |||
218 | 218 | ||
219 | if (esr & ESR_LNX_EXEC) { | 219 | if (esr & ESR_LNX_EXEC) { |
220 | vm_flags = VM_EXEC; | 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 | vm_flags = VM_WRITE; | 222 | vm_flags = VM_WRITE; |
223 | mm_flags |= FAULT_FLAG_WRITE; | 223 | mm_flags |= FAULT_FLAG_WRITE; |
224 | } | 224 | } |
@@ -525,7 +525,7 @@ asmlinkage int __exception do_debug_exception(unsigned long addr, | |||
525 | info.si_errno = 0; | 525 | info.si_errno = 0; |
526 | info.si_code = inf->code; | 526 | info.si_code = inf->code; |
527 | info.si_addr = (void __user *)addr; | 527 | info.si_addr = (void __user *)addr; |
528 | arm64_notify_die("", regs, &info, esr); | 528 | arm64_notify_die("", regs, &info, 0); |
529 | 529 | ||
530 | return 0; | 530 | return 0; |
531 | } | 531 | } |