diff options
author | Max Filippov <jcmvbkbc@gmail.com> | 2022-05-13 08:11:14 -0700 |
---|---|---|
committer | Max Filippov <jcmvbkbc@gmail.com> | 2022-05-17 03:35:43 -0700 |
commit | d74862007e0849fad8ba86447e6f05928f920640 (patch) | |
tree | 3b4b0e3aa88dbb682fce761e7b4a79fe5f586c0b /arch/xtensa | |
parent | 408b1d3c0ec8e4f36603092573108a6d3daa07a8 (diff) |
xtensa: support artificial division by 0 exception
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>
Diffstat (limited to 'arch/xtensa')
-rw-r--r-- | arch/xtensa/kernel/traps.c | 23 |
1 files changed, 23 insertions, 0 deletions
diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c index 24d11b44fa57..2b75b252b626 100644 --- a/arch/xtensa/kernel/traps.c +++ b/arch/xtensa/kernel/traps.c @@ -293,12 +293,35 @@ static void do_interrupt(struct pt_regs *regs) set_irq_regs(old_regs); } +static bool check_div0(struct pt_regs *regs) +{ + static const u8 pattern[] = {'D', 'I', 'V', '0'}; + const u8 *p; + u8 buf[5]; + + if (user_mode(regs)) { + if (copy_from_user(buf, (void __user *)regs->pc + 2, 5)) + return 0; + p = buf; + } else { + p = (const u8 *)regs->pc + 2; + } + + return memcmp(p, pattern, sizeof(pattern)) == 0 || + memcmp(p + 1, pattern, sizeof(pattern)) == 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. */ |