b344e24a8e
We can't assume that if we execute the unwinder code and the unwinder was already running that it has faulted. Clearly two kernel threads can invoke the unwinder at the same time and may be running simultaneously. The previous approach used BUG() and BUG_ON() in the unwinder code to detect whether the unwinder was incapable of unwinding the stack, and that the next available unwinder should be used instead. A better approach is to explicitly invoke a trap handler to switch unwinders when the current unwinder cannot continue. Signed-off-by: Matt Fleming <matt@console-pimps.org>
80 lines
1.5 KiB
C
80 lines
1.5 KiB
C
#include <linux/bug.h>
|
|
#include <linux/io.h>
|
|
#include <linux/types.h>
|
|
#include <linux/kdebug.h>
|
|
#include <linux/signal.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/uaccess.h>
|
|
#include <asm/system.h>
|
|
|
|
#ifdef CONFIG_BUG
|
|
void handle_BUG(struct pt_regs *regs)
|
|
{
|
|
enum bug_trap_type tt;
|
|
tt = report_bug(regs->pc, regs);
|
|
if (tt == BUG_TRAP_TYPE_WARN) {
|
|
regs->pc += instruction_size(regs->pc);
|
|
return;
|
|
}
|
|
|
|
die("Kernel BUG", regs, TRAPA_BUG_OPCODE & 0xff);
|
|
}
|
|
|
|
int is_valid_bugaddr(unsigned long addr)
|
|
{
|
|
insn_size_t opcode;
|
|
|
|
if (addr < PAGE_OFFSET)
|
|
return 0;
|
|
if (probe_kernel_address((insn_size_t *)addr, opcode))
|
|
return 0;
|
|
|
|
if (opcode == TRAPA_BUG_OPCODE || opcode == TRAPA_UNWINDER_BUG_OPCODE)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Generic trap handler.
|
|
*/
|
|
BUILD_TRAP_HANDLER(debug)
|
|
{
|
|
TRAP_HANDLER_DECL;
|
|
|
|
/* Rewind */
|
|
regs->pc -= instruction_size(ctrl_inw(regs->pc - 4));
|
|
|
|
if (notify_die(DIE_TRAP, "debug trap", regs, 0, vec & 0xff,
|
|
SIGTRAP) == NOTIFY_STOP)
|
|
return;
|
|
|
|
force_sig(SIGTRAP, current);
|
|
}
|
|
|
|
/*
|
|
* Special handler for BUG() traps.
|
|
*/
|
|
BUILD_TRAP_HANDLER(bug)
|
|
{
|
|
TRAP_HANDLER_DECL;
|
|
|
|
/* Rewind */
|
|
regs->pc -= instruction_size(ctrl_inw(regs->pc - 4));
|
|
|
|
if (notify_die(DIE_TRAP, "bug trap", regs, 0, TRAPA_BUG_OPCODE & 0xff,
|
|
SIGTRAP) == NOTIFY_STOP)
|
|
return;
|
|
|
|
#ifdef CONFIG_BUG
|
|
if (__kernel_text_address(instruction_pointer(regs))) {
|
|
insn_size_t insn = *(insn_size_t *)instruction_pointer(regs);
|
|
if (insn == TRAPA_BUG_OPCODE)
|
|
handle_BUG(regs);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
force_sig(SIGTRAP, current);
|
|
}
|