:p
atchew
Login
Hello, this series adds handlers for hardware and artificial division by zero exceptions. Max Filippov (2): xtensa: add trap handler for division by zero xtensa: support artificial division by 0 exception arch/xtensa/include/asm/thread_info.h | 4 ++ arch/xtensa/kernel/asm-offsets.c | 3 ++ arch/xtensa/kernel/entry.S | 5 +++ arch/xtensa/kernel/traps.c | 53 ++++++++++++++++++++++++++- 4 files changed, 64 insertions(+), 1 deletion(-) -- 2.30.2
Add c-level handler for the division by zero exception and kill the task if it was thrown from the kernel space or send SIGFPE otherwise. Signed-off-by: Max Filippov <jcmvbkbc@gmail.com> --- arch/xtensa/kernel/traps.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c index XXXXXXX..XXXXXXX 100644 --- a/arch/xtensa/kernel/traps.c +++ b/arch/xtensa/kernel/traps.c @@ -XXX,XX +XXX,XX @@ */ static void do_illegal_instruction(struct pt_regs *regs); +static void do_div0(struct pt_regs *regs); static void do_interrupt(struct pt_regs *regs); #if XTENSA_FAKE_NMI static void do_nmi(struct pt_regs *regs); @@ -XXX,XX +XXX,XX @@ static dispatch_init_table_t __initdata dispatch_init_table[] = { #ifdef SUPPORT_WINDOWED { EXCCAUSE_ALLOCA, USER|KRNL, fast_alloca }, #endif -/* EXCCAUSE_INTEGER_DIVIDE_BY_ZERO unhandled */ +{ EXCCAUSE_INTEGER_DIVIDE_BY_ZERO, 0, do_div0 }, /* EXCCAUSE_PRIVILEGED unhandled */ #if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION #ifdef CONFIG_XTENSA_UNALIGNED_USER @@ -XXX,XX +XXX,XX @@ static void do_illegal_instruction(struct pt_regs *regs) force_sig(SIGILL); } +static void do_div0(struct pt_regs *regs) +{ + __die_if_kernel("Unhandled division by 0 in kernel", regs, SIGKILL); + force_sig_fault(SIGFPE, FPE_INTDIV, (void __user *)regs->pc); +} /* * Handle unaligned memory accesses from user space. Kill task. -- 2.30.2
On xtensa cores wihout hardware division option division support functions from libgcc react to division by 0 attempt by executing illegal instruction followed by the characters 'DIV0'. Recognize this pattern in illegal instruction exception handler and convert it to division by 0. When call0 userspace ABI support by probing is enabled instructions that cause illegal instruction exception when PS.WOE is clear are retried with PS.WOE set before calling c-level exception handler. Record user pc where PS.WOE was set in the fast exception handler and clear PS.WOE in the c-level exception handler if it was due to artificial division by 0. Signed-off-by: Max Filippov <jcmvbkbc@gmail.com> --- arch/xtensa/include/asm/thread_info.h | 4 +++ arch/xtensa/kernel/asm-offsets.c | 3 ++ arch/xtensa/kernel/entry.S | 5 +++ arch/xtensa/kernel/traps.c | 45 +++++++++++++++++++++++++++ 4 files changed, 57 insertions(+) diff --git a/arch/xtensa/include/asm/thread_info.h b/arch/xtensa/include/asm/thread_info.h index XXXXXXX..XXXXXXX 100644 --- a/arch/xtensa/include/asm/thread_info.h +++ b/arch/xtensa/include/asm/thread_info.h @@ -XXX,XX +XXX,XX @@ struct thread_info { /* result of the most recent exclusive store */ unsigned long atomctl8; #endif +#ifdef CONFIG_USER_ABI_CALL0_PROBE + /* Address where PS.WOE was enabled by the ABI probing code */ + unsigned long ps_woe_fix_addr; +#endif /* * If i-th bit is set then coprocessor state is loaded into the diff --git a/arch/xtensa/kernel/asm-offsets.c b/arch/xtensa/kernel/asm-offsets.c index XXXXXXX..XXXXXXX 100644 --- a/arch/xtensa/kernel/asm-offsets.c +++ b/arch/xtensa/kernel/asm-offsets.c @@ -XXX,XX +XXX,XX @@ int main(void) OFFSET(TI_STSTUS, thread_info, status); OFFSET(TI_CPU, thread_info, cpu); OFFSET(TI_PRE_COUNT, thread_info, preempt_count); +#ifdef CONFIG_USER_ABI_CALL0_PROBE + OFFSET(TI_PS_WOE_FIX_ADDR, thread_info, ps_woe_fix_addr); +#endif /* struct thread_info (offset from start_struct) */ DEFINE(THREAD_RA, offsetof (struct task_struct, thread.ra)); diff --git a/arch/xtensa/kernel/entry.S b/arch/xtensa/kernel/entry.S index XXXXXXX..XXXXXXX 100644 --- a/arch/xtensa/kernel/entry.S +++ b/arch/xtensa/kernel/entry.S @@ -XXX,XX +XXX,XX @@ ENTRY(fast_illegal_instruction_user) movi a3, PS_WOE_MASK or a0, a0, a3 wsr a0, ps +#ifdef CONFIG_USER_ABI_CALL0_PROBE + GET_THREAD_INFO(a3, a2) + rsr a0, epc1 + s32i a0, a3, TI_PS_WOE_FIX_ADDR +#endif l32i a3, a2, PT_AREG3 l32i a0, a2, PT_AREG0 rsr a2, depc diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c index XXXXXXX..XXXXXXX 100644 --- a/arch/xtensa/kernel/traps.c +++ b/arch/xtensa/kernel/traps.c @@ -XXX,XX +XXX,XX @@ static void do_interrupt(struct pt_regs *regs) set_irq_regs(old_regs); } +static int check_div0(struct pt_regs *regs) +{ +#if !XCHAL_HAVE_DIV32 + u8 buf[7]; + void *p; + static const u8 pattern1[] = {0, 0, 0, 'D', 'I', 'V', '0'}; +#if defined(__XTENSA_EB__) + static const u8 pattern2[] = {0xd6, 0x0f, 'D', 'I', 'V', '0'}; +#elif defined(__XTENSA_EL__) + static const u8 pattern2[] = {0x6d, 0xf0, 'D', 'I', 'V', '0'}; +#else +#error Unsupported Xtensa endianness +#endif + + if (user_mode(regs)) { + if (copy_from_user(buf, (void __user *)regs->pc, 7)) + return 0; + p = buf; + } else { + p = (void *)regs->pc; + } + + return memcmp(p, pattern1, sizeof(pattern1)) == 0 || + memcmp(p, pattern2, sizeof(pattern2)) == 0; +#else + return 0; +#endif +} + /* * Illegal instruction. Fatal if in kernel space. */ static void do_illegal_instruction(struct pt_regs *regs) { + if (check_div0(regs)) { +#ifdef CONFIG_USER_ABI_CALL0_PROBE + /* + * When call0 application generates artificial division by 0 + * exception fast illegal instruction exception handler will + * attempt to set PS.WOE and retry failing instruction. + * We know that that illegal instruction was not related to + * windowed option so we clear PS.WOE here. + */ + if (regs->pc == current_thread_info()->ps_woe_fix_addr) + regs->ps &= ~PS_WOE_MASK; +#endif + do_div0(regs); + return; + } + __die_if_kernel("Illegal instruction in kernel", regs, SIGKILL); /* If in user mode, send SIGILL signal to current process. */ -- 2.30.2
Hello, this series adds handlers for hardware and artificial division by zero exceptions and improves call0 ABI probing in the presence of unrelated illegal instructions. Changes v1->v2: - split ABI probing improvement from the artificial division by 0 - don't limit artificial division by 0 pattern detection to configurations without HW division opcodes, do it always Max Filippov (3): xtensa: add trap handler for division by zero xtensa: support artificial division by 0 exception xtensa: improve call0 ABI probing arch/xtensa/include/asm/thread_info.h | 4 +++ arch/xtensa/kernel/asm-offsets.c | 3 ++ arch/xtensa/kernel/entry.S | 5 +++ arch/xtensa/kernel/traps.c | 50 ++++++++++++++++++++++++++- 4 files changed, 61 insertions(+), 1 deletion(-) -- 2.30.2
Add c-level handler for the division by zero exception and kill the task if it was thrown from the kernel space or send SIGFPE otherwise. Signed-off-by: Max Filippov <jcmvbkbc@gmail.com> --- arch/xtensa/kernel/traps.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c index XXXXXXX..XXXXXXX 100644 --- a/arch/xtensa/kernel/traps.c +++ b/arch/xtensa/kernel/traps.c @@ -XXX,XX +XXX,XX @@ */ static void do_illegal_instruction(struct pt_regs *regs); +static void do_div0(struct pt_regs *regs); static void do_interrupt(struct pt_regs *regs); #if XTENSA_FAKE_NMI static void do_nmi(struct pt_regs *regs); @@ -XXX,XX +XXX,XX @@ static dispatch_init_table_t __initdata dispatch_init_table[] = { #ifdef SUPPORT_WINDOWED { EXCCAUSE_ALLOCA, USER|KRNL, fast_alloca }, #endif -/* EXCCAUSE_INTEGER_DIVIDE_BY_ZERO unhandled */ +{ EXCCAUSE_INTEGER_DIVIDE_BY_ZERO, 0, do_div0 }, /* EXCCAUSE_PRIVILEGED unhandled */ #if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION #ifdef CONFIG_XTENSA_UNALIGNED_USER @@ -XXX,XX +XXX,XX @@ static void do_illegal_instruction(struct pt_regs *regs) force_sig(SIGILL); } +static void do_div0(struct pt_regs *regs) +{ + __die_if_kernel("Unhandled division by 0 in kernel", regs, SIGKILL); + force_sig_fault(SIGFPE, FPE_INTDIV, (void __user *)regs->pc); +} /* * Handle unaligned memory accesses from user space. Kill task. -- 2.30.2
On xtensa cores wihout hardware division option division support functions from libgcc react to division by 0 attempt by executing illegal instruction followed by the characters 'DIV0'. Recognize this pattern in illegal instruction exception handler and convert it to division by 0. Signed-off-by: Max Filippov <jcmvbkbc@gmail.com> --- Changes v1->v2: - split ABI probing improvement from the artificial division by 0 - don't limit artificial division by 0 pattern detection to configurations without HW division opcodes, do it always arch/xtensa/kernel/traps.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c index XXXXXXX..XXXXXXX 100644 --- a/arch/xtensa/kernel/traps.c +++ b/arch/xtensa/kernel/traps.c @@ -XXX,XX +XXX,XX @@ static void do_interrupt(struct pt_regs *regs) set_irq_regs(old_regs); } +static int check_div0(struct pt_regs *regs) +{ + u8 buf[7]; + void *p; + static const u8 pattern1[] = {0, 0, 0, 'D', 'I', 'V', '0'}; +#if defined(__XTENSA_EB__) + static const u8 pattern2[] = {0xd6, 0x0f, 'D', 'I', 'V', '0'}; +#elif defined(__XTENSA_EL__) + static const u8 pattern2[] = {0x6d, 0xf0, 'D', 'I', 'V', '0'}; +#else +#error Unsupported Xtensa endianness +#endif + + if (user_mode(regs)) { + if (copy_from_user(buf, (void __user *)regs->pc, 7)) + return 0; + p = buf; + } else { + p = (void *)regs->pc; + } + + return memcmp(p, pattern1, sizeof(pattern1)) == 0 || + memcmp(p, pattern2, sizeof(pattern2)) == 0; +} + /* * Illegal instruction. Fatal if in kernel space. */ static void do_illegal_instruction(struct pt_regs *regs) { + if (check_div0(regs)) { + do_div0(regs); + return; + } + __die_if_kernel("Illegal instruction in kernel", regs, SIGKILL); /* If in user mode, send SIGILL signal to current process. */ -- 2.30.2
When call0 userspace ABI support by probing is enabled instructions that cause illegal instruction exception when PS.WOE is clear are retried with PS.WOE set before calling c-level exception handler. Record user pc at which PS.WOE was set in the fast exception handler and clear PS.WOE in the c-level exception handler if we get there from the same address. Signed-off-by: Max Filippov <jcmvbkbc@gmail.com> --- Changes v1->v2: - split ABI probing improvement from the artificial division by 0 arch/xtensa/include/asm/thread_info.h | 4 ++++ arch/xtensa/kernel/asm-offsets.c | 3 +++ arch/xtensa/kernel/entry.S | 5 +++++ arch/xtensa/kernel/traps.c | 12 ++++++++++++ 4 files changed, 24 insertions(+) diff --git a/arch/xtensa/include/asm/thread_info.h b/arch/xtensa/include/asm/thread_info.h index XXXXXXX..XXXXXXX 100644 --- a/arch/xtensa/include/asm/thread_info.h +++ b/arch/xtensa/include/asm/thread_info.h @@ -XXX,XX +XXX,XX @@ struct thread_info { /* result of the most recent exclusive store */ unsigned long atomctl8; #endif +#ifdef CONFIG_USER_ABI_CALL0_PROBE + /* Address where PS.WOE was enabled by the ABI probing code */ + unsigned long ps_woe_fix_addr; +#endif /* * If i-th bit is set then coprocessor state is loaded into the diff --git a/arch/xtensa/kernel/asm-offsets.c b/arch/xtensa/kernel/asm-offsets.c index XXXXXXX..XXXXXXX 100644 --- a/arch/xtensa/kernel/asm-offsets.c +++ b/arch/xtensa/kernel/asm-offsets.c @@ -XXX,XX +XXX,XX @@ int main(void) OFFSET(TI_STSTUS, thread_info, status); OFFSET(TI_CPU, thread_info, cpu); OFFSET(TI_PRE_COUNT, thread_info, preempt_count); +#ifdef CONFIG_USER_ABI_CALL0_PROBE + OFFSET(TI_PS_WOE_FIX_ADDR, thread_info, ps_woe_fix_addr); +#endif /* struct thread_info (offset from start_struct) */ DEFINE(THREAD_RA, offsetof (struct task_struct, thread.ra)); diff --git a/arch/xtensa/kernel/entry.S b/arch/xtensa/kernel/entry.S index XXXXXXX..XXXXXXX 100644 --- a/arch/xtensa/kernel/entry.S +++ b/arch/xtensa/kernel/entry.S @@ -XXX,XX +XXX,XX @@ ENTRY(fast_illegal_instruction_user) movi a3, PS_WOE_MASK or a0, a0, a3 wsr a0, ps +#ifdef CONFIG_USER_ABI_CALL0_PROBE + GET_THREAD_INFO(a3, a2) + rsr a0, epc1 + s32i a0, a3, TI_PS_WOE_FIX_ADDR +#endif l32i a3, a2, PT_AREG3 l32i a0, a2, PT_AREG0 rsr a2, depc diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c index XXXXXXX..XXXXXXX 100644 --- a/arch/xtensa/kernel/traps.c +++ b/arch/xtensa/kernel/traps.c @@ -XXX,XX +XXX,XX @@ static int check_div0(struct pt_regs *regs) static void do_illegal_instruction(struct pt_regs *regs) { +#ifdef CONFIG_USER_ABI_CALL0_PROBE + /* + * When call0 application encounters an illegal instruction fast + * exception handler will attempt to set PS.WOE and retry failing + * instruction. + * If we get here we know that that instruction is also illegal + * with PS.WOE set, so it's not related to the windowed option + * hence PS.WOE may be cleared. + */ + if (regs->pc == current_thread_info()->ps_woe_fix_addr) + regs->ps &= ~PS_WOE_MASK; +#endif if (check_div0(regs)) { do_div0(regs); return; -- 2.30.2