@@ -4,6 +4,9 @@
struct pt_regs;
+void arm_und_handler(struct pt_regs *regs);
+void arm_dabt_handler(unsigned long addr, unsigned int fsr, struct pt_regs *regs);
+void arm_pabt_handler(unsigned long addr, unsigned int ifsr, struct pt_regs *regs);
void arm_irq_handler(struct pt_regs *regs, int mode);
void arm_fiq_handler(struct pt_regs *regs);
void arm_exit_to_user_mode(struct pt_regs *regs);
@@ -37,7 +37,7 @@ extern void ptrace_break(struct pt_regs *regs);
extern void *vectors_page;
asmlinkage void dump_backtrace_stm(u32 *stack, u32 instruction, const char *loglvl);
-asmlinkage void do_undefinstr(struct pt_regs *regs);
+void do_undefinstr(struct pt_regs *regs);
asmlinkage void bad_mode(struct pt_regs *regs, int reason);
int arm_syscall(int no, struct pt_regs *regs);
asmlinkage void baddataabort(int code, unsigned long instr, struct pt_regs *regs);
@@ -230,7 +230,7 @@ __und_fault:
ldr r2, [r0, #S_PC]
sub r2, r2, r1
str r2, [r0, #S_PC]
- b do_undefinstr
+ b arm_und_handler
ENDPROC(__und_fault)
.align 5
@@ -449,9 +449,8 @@ __pabt_usr:
ENTRY(ret_from_exception)
UNWIND(.fnstart )
UNWIND(.cantunwind )
- get_thread_info tsk
- mov why, #0
- b ret_to_user
+ disable_irq_notrace
+ b ret_to_user_from_irq
UNWIND(.fnend )
ENDPROC(__pabt_usr)
ENDPROC(ret_from_exception)
@@ -7,8 +7,37 @@
#include <linux/irqflags.h>
#include <linux/percpu.h>
#include <linux/rseq.h>
+#include <asm/traps.h>
#include "irq.h"
+#include "../mm/fault.h"
+
+noinstr asmlinkage void arm_und_handler(struct pt_regs *regs)
+{
+ irqentry_state_t state = irqentry_enter(regs);
+
+ do_undefinstr(regs);
+
+ irqentry_exit(regs, state);
+}
+
+noinstr asmlinkage void arm_dabt_handler(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
+{
+ irqentry_state_t state = irqentry_enter(regs);
+
+ do_DataAbort(addr, fsr, regs);
+
+ irqentry_exit(regs, state);
+}
+
+noinstr asmlinkage void arm_pabt_handler(unsigned long addr, unsigned int ifsr, struct pt_regs *regs)
+{
+ irqentry_state_t state = irqentry_enter(regs);
+
+ do_PrefetchAbort(addr, ifsr, regs);
+
+ irqentry_exit(regs, state);
+}
static void noinstr handle_arm_irq(void *data)
{
@@ -449,7 +449,7 @@ int call_undef_hook(struct pt_regs *regs, unsigned int instr)
return fn ? fn(regs, instr) : 1;
}
-asmlinkage void do_undefinstr(struct pt_regs *regs)
+noinstr void do_undefinstr(struct pt_regs *regs)
{
unsigned int instr;
void __user *pc;
@@ -24,4 +24,4 @@ ENTRY(v4_early_abort)
bic r1, r1, #1 << 11 | 1 << 10 @ clear bits 11 and 10 of FSR
tst r3, #1 << 20 @ L = 1 -> write?
orreq r1, r1, #1 << 11 @ yes.
- b do_DataAbort
+ b arm_dabt_handler
@@ -25,4 +25,4 @@ ENTRY(v4t_early_abort)
bic r1, r1, #1 << 11 | 1 << 10 @ clear bits 11 and 10 of FSR
tst r3, #1 << 20 @ check write
orreq r1, r1, #1 << 11
- b do_DataAbort
+ b arm_dabt_handler
@@ -25,7 +25,7 @@ ENTRY(v5t_early_abort)
uaccess_disable ip @ disable user access
bic r1, r1, #1 << 11 @ clear bits 11 of FSR
teq_ldrd tmp=ip, insn=r3 @ insn was LDRD?
- beq do_DataAbort @ yes
+ beq arm_dabt_handler @ yes
tst r3, #1 << 20 @ check write
orreq r1, r1, #1 << 11
- b do_DataAbort
+ b arm_dabt_handler
@@ -22,12 +22,12 @@ ENTRY(v5tj_early_abort)
mrc p15, 0, r0, c6, c0, 0 @ get FAR
bic r1, r1, #1 << 11 | 1 << 10 @ clear bits 11 and 10 of FSR
tst r5, #PSR_J_BIT @ Java?
- bne do_DataAbort
+ bne arm_dabt_handler
do_thumb_abort fsr=r1, pc=r4, psr=r5, tmp=r3
ldreq r3, [r4] @ read aborted ARM instruction
uaccess_disable ip @ disable userspace access
teq_ldrd tmp=ip, insn=r3 @ insn was LDRD?
- beq do_DataAbort @ yes
+ beq arm_dabt_handler @ yes
tst r3, #1 << 20 @ L = 0 -> write
orreq r1, r1, #1 << 11 @ yes.
- b do_DataAbort
+ b arm_dabt_handler
@@ -42,4 +42,4 @@ ENTRY(v6_early_abort)
orreq r1, r1, #1 << 11 @ yes.
#endif
1: uaccess_disable ip @ disable userspace access
- b do_DataAbort
+ b arm_dabt_handler
@@ -18,5 +18,5 @@ ENTRY(v7_early_abort)
mrc p15, 0, r1, c5, c0, 0 @ get FSR
mrc p15, 0, r0, c6, c0, 0 @ get FAR
uaccess_disable ip @ disable userspace access
- b do_DataAbort
+ b arm_dabt_handler
ENDPROC(v7_early_abort)
@@ -46,8 +46,8 @@ ENTRY(v4t_late_abort)
/* 9 */ b .data_arm_ldmstm @ ldm*b rn, <rlist>
/* a */ b .data_unknown
/* b */ b .data_unknown
-/* c */ b do_DataAbort @ ldc rd, [rn], #m @ Same as ldr rd, [rn], #m
-/* d */ b do_DataAbort @ ldc rd, [rn, #m]
+/* c */ b arm_dabt_handler @ ldc rd, [rn], #m @ Same as ldr rd, [rn], #m
+/* d */ b arm_dabt_handler @ ldc rd, [rn, #m]
/* e */ b .data_unknown
/* f */ b .data_unknown
@@ -60,7 +60,7 @@ ENTRY(v4t_late_abort)
.data_arm_ldmstm:
tst r8, #1 << 21 @ check writeback bit
- beq do_DataAbort @ no writeback -> no fixup
+ beq arm_dabt_handler @ no writeback -> no fixup
str r9, [sp, #-4]!
mov r7, #0x11
orr r7, r7, #0x1100
@@ -81,11 +81,11 @@ ENTRY(v4t_late_abort)
addeq r7, r7, r6, lsl #2 @ Undo decrement
str r7, [r2, r9, lsr #14] @ Put register 'Rn'
ldr r9, [sp], #4
- b do_DataAbort
+ b arm_dabt_handler
.data_arm_lateldrhpre:
tst r8, #1 << 21 @ Check writeback bit
- beq do_DataAbort @ No writeback -> no fixup
+ beq arm_dabt_handler @ No writeback -> no fixup
.data_arm_lateldrhpost:
str r9, [sp, #-4]!
and r9, r8, #0x00f @ get Rm / low nibble of immediate value
@@ -101,14 +101,14 @@ ENTRY(v4t_late_abort)
addeq r7, r7, r6 @ Undo decrement
str r7, [r2, r9, lsr #14] @ Put register 'Rn'
ldr r9, [sp], #4
- b do_DataAbort
+ b arm_dabt_handler
.data_arm_lateldrpreconst:
tst r8, #1 << 21 @ check writeback bit
- beq do_DataAbort @ no writeback -> no fixup
+ beq arm_dabt_handler @ no writeback -> no fixup
.data_arm_lateldrpostconst:
movs r6, r8, lsl #20 @ Get offset
- beq do_DataAbort @ zero -> no fixup
+ beq arm_dabt_handler @ zero -> no fixup
str r9, [sp, #-4]!
and r9, r8, #15 << 16 @ Extract 'n' from instruction
ldr r7, [r2, r9, lsr #14] @ Get register 'Rn'
@@ -117,11 +117,11 @@ ENTRY(v4t_late_abort)
addeq r7, r7, r6, lsr #20 @ Undo decrement
str r7, [r2, r9, lsr #14] @ Put register 'Rn'
ldr r9, [sp], #4
- b do_DataAbort
+ b arm_dabt_handler
.data_arm_lateldrprereg:
tst r8, #1 << 21 @ check writeback bit
- beq do_DataAbort @ no writeback -> no fixup
+ beq arm_dabt_handler @ no writeback -> no fixup
.data_arm_lateldrpostreg:
and r7, r8, #15 @ Extract 'm' from instruction
ldr r6, [r2, r7, lsl #2] @ Get register 'Rm'
@@ -180,10 +180,10 @@ ENTRY(v4t_late_abort)
/* 3 */ b .data_unknown
/* 4 */ b .data_unknown
/* 5 */ b .data_thumb_reg
-/* 6 */ b do_DataAbort
-/* 7 */ b do_DataAbort
-/* 8 */ b do_DataAbort
-/* 9 */ b do_DataAbort
+/* 6 */ b arm_dabt_handler
+/* 7 */ b arm_dabt_handler
+/* 8 */ b arm_dabt_handler
+/* 9 */ b arm_dabt_handler
/* A */ b .data_unknown
/* B */ b .data_thumb_pushpop
/* C */ b .data_thumb_ldmstm
@@ -193,10 +193,10 @@ ENTRY(v4t_late_abort)
.data_thumb_reg:
tst r8, #1 << 9
- beq do_DataAbort
+ beq arm_dabt_handler
tst r8, #1 << 10 @ If 'S' (signed) bit is set
movne r1, #0 @ it must be a load instr
- b do_DataAbort
+ b arm_dabt_handler
.data_thumb_pushpop:
tst r8, #1 << 10
@@ -217,7 +217,7 @@ ENTRY(v4t_late_abort)
subne r7, r7, r6, lsl #2 @ decrement SP if POP
str r7, [r2, #13 << 2]
ldr r9, [sp], #4
- b do_DataAbort
+ b arm_dabt_handler
.data_thumb_ldmstm:
str r9, [sp, #-4]!
@@ -234,4 +234,4 @@ ENTRY(v4t_late_abort)
sub r7, r7, r6, lsl #2 @ always decrement
str r7, [r2, r9, lsr #6]
ldr r9, [sp], #4
- b do_DataAbort
+ b arm_dabt_handler
@@ -20,7 +20,7 @@
orreq \tmp, \tmp, #1 << 11 @ Set L-bit if yes
tst \tmp, #1 << 11 @ L = 0 -> write
orreq \fsr, \fsr, #1 << 11 @ yes.
- b do_DataAbort
+ b arm_dabt_handler
not_thumb:
.endm
@@ -17,5 +17,5 @@
ENTRY(nommu_early_abort)
mov r0, #0 @ clear r0, r1 (no FSR/FAR)
mov r1, #0
- b do_DataAbort
+ b arm_dabt_handler
ENDPROC(nommu_early_abort)
@@ -588,7 +588,7 @@ hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *)
/*
* Dispatch a data abort to the relevant handler.
*/
-asmlinkage void
+void
do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
{
const struct fsr_info *inf = fsr_info + fsr_fs(fsr);
@@ -618,7 +618,7 @@ hook_ifault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *
ifsr_info[nr].name = name;
}
-asmlinkage void
+void
do_PrefetchAbort(unsigned long addr, unsigned int ifsr, struct pt_regs *regs)
{
const struct fsr_info *inf = ifsr_info + fsr_fs(ifsr);
@@ -37,9 +37,9 @@ static inline int fsr_fs(unsigned int fsr)
void do_bad_area(unsigned long addr, unsigned int fsr, struct pt_regs *regs);
void early_abt_enable(void);
-asmlinkage void do_DataAbort(unsigned long addr, unsigned int fsr,
- struct pt_regs *regs);
-asmlinkage void do_PrefetchAbort(unsigned long addr, unsigned int ifsr,
- struct pt_regs *regs);
+void do_DataAbort(unsigned long addr, unsigned int fsr,
+ struct pt_regs *regs);
+void do_PrefetchAbort(unsigned long addr, unsigned int ifsr,
+ struct pt_regs *regs);
#endif /* __ARCH_ARM_FAULT_H */
@@ -18,5 +18,5 @@
ENTRY(legacy_pabort)
mov r0, r4
mov r1, #5
- b do_PrefetchAbort
+ b arm_pabt_handler
ENDPROC(legacy_pabort)
@@ -18,5 +18,5 @@
ENTRY(v6_pabort)
mov r0, r4
mrc p15, 0, r1, c5, c0, 1 @ get IFSR
- b do_PrefetchAbort
+ b arm_pabt_handler
ENDPROC(v6_pabort)
@@ -18,5 +18,5 @@
ENTRY(v7_pabort)
mrc p15, 0, r0, c6, c0, 2 @ get IFAR
mrc p15, 0, r1, c5, c0, 1 @ get IFSR
- b do_PrefetchAbort
+ b arm_pabt_handler
ENDPROC(v7_pabort)
While it isn't entirely intuitive, it appears that any kind of exception such as data or prefetch abort ("page faults") need to be handled as some kind of "interrupts" when using generic entry. At least this is what other platforms are doing. The same goes for undefined instruction handling, i.e. floating point emulation in the kernel (und-exceptions). This is necessary for the context checking to pass: without this patch, a whole slew of warnings start to trigger from syscall_exit_to_user_mode_prepare() CT_WARN_ON(ct_state() != CT_STATE_KERNEL), i.e. syscalls seems to exit from user mode to user mode (not good), because the page faults screws up the context tracker. This patch restores the order. If this seems like the previous patch introduces a regression that is then fixed in this patch, it can simply be squashed into the former: having this rewrite separately surely makes development and review easier. Signed-off-by: Linus Walleij <linus.walleij@linaro.org> --- arch/arm/include/asm/entry.h | 3 +++ arch/arm/include/asm/traps.h | 2 +- arch/arm/kernel/entry-armv.S | 7 +++---- arch/arm/kernel/entry.c | 29 +++++++++++++++++++++++++++++ arch/arm/kernel/traps.c | 2 +- arch/arm/mm/abort-ev4.S | 2 +- arch/arm/mm/abort-ev4t.S | 2 +- arch/arm/mm/abort-ev5t.S | 4 ++-- arch/arm/mm/abort-ev5tj.S | 6 +++--- arch/arm/mm/abort-ev6.S | 2 +- arch/arm/mm/abort-ev7.S | 2 +- arch/arm/mm/abort-lv4t.S | 36 ++++++++++++++++++------------------ arch/arm/mm/abort-macro.S | 2 +- arch/arm/mm/abort-nommu.S | 2 +- arch/arm/mm/fault.c | 4 ++-- arch/arm/mm/fault.h | 8 ++++---- arch/arm/mm/pabort-legacy.S | 2 +- arch/arm/mm/pabort-v6.S | 2 +- arch/arm/mm/pabort-v7.S | 2 +- 19 files changed, 75 insertions(+), 44 deletions(-)