MN10300: Create generic kernel debugger hooks
Create generic kernel debugger hooks in the MN10300 arch and make gdbstub use them. This is a preparation for KGDB support. Signed-off-by: David Howells <dhowells@redhat.com>
This commit is contained in:
parent
7f386ac327
commit
67ddb4052d
@ -401,8 +401,8 @@ comment "[!] NOTE: A lower number/level indicates a higher priority (0 is highes
|
||||
comment "____Non-maskable interrupt levels____"
|
||||
comment "The following must be set to a higher priority than local_irq_disable() and on-chip serial"
|
||||
|
||||
config GDBSTUB_IRQ_LEVEL
|
||||
int "GDBSTUB interrupt priority"
|
||||
config DEBUGGER_IRQ_LEVEL
|
||||
int "DEBUGGER interrupt priority"
|
||||
depends on KERNEL_DEBUGGER
|
||||
range 0 1 if LINUX_CLI_LEVEL = 2
|
||||
range 0 2 if LINUX_CLI_LEVEL = 3
|
||||
@ -437,7 +437,7 @@ config LINUX_CLI_LEVEL
|
||||
EPSW.IM from 7. Any interrupt is permitted for which the level is
|
||||
lower than EPSW.IM.
|
||||
|
||||
Certain interrupts, such as GDBSTUB and virtual MN10300 on-chip
|
||||
Certain interrupts, such as DEBUGGER and virtual MN10300 on-chip
|
||||
serial DMA interrupts are allowed to interrupt normal disabled
|
||||
sections.
|
||||
|
||||
|
@ -14,6 +14,9 @@
|
||||
|
||||
#if defined(CONFIG_KERNEL_DEBUGGER)
|
||||
|
||||
extern int debugger_intercept(enum exception_code, int, int, struct pt_regs *);
|
||||
extern int at_debugger_breakpoint(struct pt_regs *);
|
||||
|
||||
#ifndef CONFIG_MN10300_DEBUGGER_CACHE_NO_FLUSH
|
||||
extern void debugger_local_cache_flushinv(void);
|
||||
extern void debugger_local_cache_flushinv_one(u8 *);
|
||||
@ -24,5 +27,17 @@ static inline void debugger_local_cache_flushinv_one(u8 *addr) {}
|
||||
|
||||
#else /* CONFIG_KERNEL_DEBUGGER */
|
||||
|
||||
static inline int debugger_intercept(enum exception_code excep,
|
||||
int signo, int si_code,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int at_debugger_breakpoint(struct pt_regs *regs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_KERNEL_DEBUGGER */
|
||||
#endif /* _ASM_DEBUGGER_H */
|
||||
|
@ -55,7 +55,6 @@ static inline void clear_using_fpu(struct task_struct *tsk)
|
||||
|
||||
extern asmlinkage void fpu_kill_state(struct task_struct *);
|
||||
extern asmlinkage void fpu_exception(struct pt_regs *, enum exception_code);
|
||||
extern asmlinkage void fpu_invalid_op(struct pt_regs *, enum exception_code);
|
||||
extern asmlinkage void fpu_init_state(void);
|
||||
extern asmlinkage void fpu_save(struct fpu_state_struct *);
|
||||
extern int fpu_setup_sigcontext(struct fpucontext *buf);
|
||||
@ -113,7 +112,6 @@ static inline void flush_fpu(void)
|
||||
|
||||
extern asmlinkage
|
||||
void unexpected_fpu_exception(struct pt_regs *, enum exception_code);
|
||||
#define fpu_invalid_op unexpected_fpu_exception
|
||||
#define fpu_exception unexpected_fpu_exception
|
||||
|
||||
struct task_struct;
|
||||
|
@ -20,7 +20,7 @@
|
||||
/*
|
||||
* interrupt control
|
||||
* - "disabled": run in IM1/2
|
||||
* - level 0 - GDB stub
|
||||
* - level 0 - kernel debugger
|
||||
* - level 1 - virtual serial DMA (if present)
|
||||
* - level 5 - normal interrupt priority
|
||||
* - level 6 - timer interrupt
|
||||
|
@ -34,7 +34,7 @@
|
||||
#define LOCAL_TIMER_IPI 193
|
||||
#define FLUSH_CACHE_IPI 194
|
||||
#define CALL_FUNCTION_NMI_IPI 195
|
||||
#define GDB_NMI_IPI 196
|
||||
#define DEBUGGER_NMI_IPI 196
|
||||
|
||||
#define SMP_BOOT_IRQ 195
|
||||
|
||||
@ -43,6 +43,7 @@
|
||||
#define LOCAL_TIMER_GxICR_LV GxICR_LEVEL_4
|
||||
#define FLUSH_CACHE_GxICR_LV GxICR_LEVEL_0
|
||||
#define SMP_BOOT_GxICR_LV GxICR_LEVEL_0
|
||||
#define DEBUGGER_GxICR_LV CONFIG_DEBUGGER_IRQ_LEVEL
|
||||
|
||||
#define TIME_OUT_COUNT_BOOT_IPI 100
|
||||
#define DELAY_TIME_BOOT_IPI 75000
|
||||
|
@ -266,7 +266,11 @@ ENTRY(raw_bus_error)
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Miscellaneous exception entry points
|
||||
# NMI exception entry points
|
||||
#
|
||||
# This is used by ordinary interrupt channels that have the GxICR_NMI bit set
|
||||
# in addition to the main NMI and Watchdog channels. SMP NMI IPIs use this
|
||||
# facility.
|
||||
#
|
||||
###############################################################################
|
||||
ENTRY(nmi_handler)
|
||||
@ -281,7 +285,7 @@ ENTRY(nmi_handler)
|
||||
and NMIAGR_GN,d0
|
||||
lsr 0x2,d0
|
||||
cmp CALL_FUNCTION_NMI_IPI,d0
|
||||
bne 5f # if not call function, jump
|
||||
bne nmi_not_smp_callfunc # if not call function, jump
|
||||
|
||||
# function call nmi ipi
|
||||
add 4,sp # no need to store TBR
|
||||
@ -295,30 +299,38 @@ ENTRY(nmi_handler)
|
||||
call smp_nmi_call_function_interrupt[],0
|
||||
RESTORE_ALL
|
||||
|
||||
5:
|
||||
#ifdef CONFIG_GDBSTUB
|
||||
cmp GDB_NMI_IPI,d0
|
||||
bne 3f # if not gdb nmi ipi, jump
|
||||
nmi_not_smp_callfunc:
|
||||
#ifdef CONFIG_KERNEL_DEBUGGER
|
||||
cmp DEBUGGER_NMI_IPI,d0
|
||||
bne nmi_not_debugger # if not kernel debugger NMI IPI, jump
|
||||
|
||||
# gdb nmi ipi
|
||||
# kernel debugger NMI IPI
|
||||
add 4,sp # no need to store TBR
|
||||
mov GxICR_DETECT,d0 # clear NMI
|
||||
movbu d0,(GxICR(GDB_NMI_IPI))
|
||||
movhu (GxICR(GDB_NMI_IPI)),d0
|
||||
movbu d0,(GxICR(DEBUGGER_NMI_IPI))
|
||||
movhu (GxICR(DEBUGGER_NMI_IPI)),d0
|
||||
and ~EPSW_NMID,epsw # enable NMI
|
||||
|
||||
mov (sp),d0
|
||||
SAVE_ALL
|
||||
call gdbstub_nmi_wait[],0
|
||||
mov fp,d0 # arg 0: stacked register file
|
||||
mov a2,d1 # arg 1: exception number
|
||||
call debugger_nmi_interrupt[],0
|
||||
RESTORE_ALL
|
||||
3:
|
||||
#endif /* CONFIG_GDBSTUB */
|
||||
|
||||
nmi_not_debugger:
|
||||
#endif /* CONFIG_KERNEL_DEBUGGER */
|
||||
mov (sp),d0 # restore TBR to d0
|
||||
add 4,sp
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
bra __common_exception_nonmi
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# General exception entry point
|
||||
#
|
||||
###############################################################################
|
||||
ENTRY(__common_exception)
|
||||
add -4,sp
|
||||
mov d0,(sp)
|
||||
|
@ -69,24 +69,6 @@ asmlinkage void fpu_exception(struct pt_regs *regs, enum exception_code code)
|
||||
force_sig_info(SIGFPE, &info, tsk);
|
||||
}
|
||||
|
||||
/*
|
||||
* handle an FPU invalid_op exception
|
||||
* - Derived from DO_EINFO() macro in arch/mn10300/kernel/traps.c
|
||||
*/
|
||||
asmlinkage void fpu_invalid_op(struct pt_regs *regs, enum exception_code code)
|
||||
{
|
||||
siginfo_t info;
|
||||
|
||||
if (!user_mode(regs))
|
||||
die_if_no_fixup("FPU invalid opcode", regs, code);
|
||||
|
||||
info.si_signo = SIGILL;
|
||||
info.si_errno = 0;
|
||||
info.si_code = ILL_COPROC;
|
||||
info.si_addr = (void *) regs->pc;
|
||||
force_sig_info(info.si_signo, &info, current);
|
||||
}
|
||||
|
||||
/*
|
||||
* save the FPU state to a signal context
|
||||
*/
|
||||
|
@ -59,10 +59,10 @@ void __init gdbstub_io_init(void)
|
||||
|
||||
/* we want to get serial receive interrupts */
|
||||
set_intr_level(gdbstub_port->rx_irq,
|
||||
NUM2GxICR_LEVEL(CONFIG_GDBSTUB_IRQ_LEVEL));
|
||||
NUM2GxICR_LEVEL(CONFIG_DEBUGGER_IRQ_LEVEL));
|
||||
set_intr_level(gdbstub_port->tx_irq,
|
||||
NUM2GxICR_LEVEL(CONFIG_GDBSTUB_IRQ_LEVEL));
|
||||
set_intr_stub(NUM2EXCEP_IRQ_LEVEL(CONFIG_GDBSTUB_IRQ_LEVEL),
|
||||
NUM2GxICR_LEVEL(CONFIG_DEBUGGER_IRQ_LEVEL));
|
||||
set_intr_stub(NUM2EXCEP_IRQ_LEVEL(CONFIG_DEBUGGER_IRQ_LEVEL),
|
||||
gdbstub_io_rx_handler);
|
||||
|
||||
*gdbstub_port->rx_icr |= GxICR_ENABLE;
|
||||
@ -88,7 +88,7 @@ void __init gdbstub_io_init(void)
|
||||
|
||||
/* permit level 0 IRQs only */
|
||||
arch_local_change_intr_mask_level(
|
||||
NUM2EPSW_IM(CONFIG_GDBSTUB_IRQ_LEVEL + 1));
|
||||
NUM2EPSW_IM(CONFIG_DEBUGGER_IRQ_LEVEL + 1));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1173,7 +1173,7 @@ int gdbstub_clear_breakpoint(u8 *addr, int len)
|
||||
|
||||
/*
|
||||
* This function does all command processing for interfacing to gdb
|
||||
* - returns 1 if the exception should be skipped, 0 otherwise.
|
||||
* - returns 0 if the exception should be skipped, -ERROR otherwise.
|
||||
*/
|
||||
static int gdbstub(struct pt_regs *regs, enum exception_code excep)
|
||||
{
|
||||
@ -1188,7 +1188,7 @@ static int gdbstub(struct pt_regs *regs, enum exception_code excep)
|
||||
int loop;
|
||||
|
||||
if (excep == EXCEP_FPU_DISABLED)
|
||||
return 0;
|
||||
return -ENOTSUPP;
|
||||
|
||||
gdbstub_flush_caches = 0;
|
||||
|
||||
@ -1197,7 +1197,7 @@ static int gdbstub(struct pt_regs *regs, enum exception_code excep)
|
||||
asm volatile("mov mdr,%0" : "=d"(mdr));
|
||||
local_save_flags(epsw);
|
||||
arch_local_change_intr_mask_level(
|
||||
NUM2EPSW_IM(CONFIG_GDBSTUB_IRQ_LEVEL + 1));
|
||||
NUM2EPSW_IM(CONFIG_DEBUGGER_IRQ_LEVEL + 1));
|
||||
|
||||
gdbstub_store_fpu();
|
||||
|
||||
@ -1675,14 +1675,23 @@ done:
|
||||
touch_softlockup_watchdog();
|
||||
|
||||
local_irq_restore(epsw);
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine if we hit a debugger special breakpoint that needs skipping over
|
||||
* automatically.
|
||||
*/
|
||||
int at_debugger_breakpoint(struct pt_regs *regs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* handle event interception
|
||||
*/
|
||||
asmlinkage int gdbstub_intercept(struct pt_regs *regs,
|
||||
enum exception_code excep)
|
||||
asmlinkage int debugger_intercept(enum exception_code excep,
|
||||
int signo, int si_code, struct pt_regs *regs)
|
||||
{
|
||||
static u8 notfirst = 1;
|
||||
int ret;
|
||||
@ -1696,7 +1705,7 @@ asmlinkage int gdbstub_intercept(struct pt_regs *regs,
|
||||
asm("mov mdr,%0" : "=d"(mdr));
|
||||
|
||||
gdbstub_entry(
|
||||
"--> gdbstub_intercept(%p,%04x) [MDR=%lx PC=%lx]\n",
|
||||
"--> debugger_intercept(%p,%04x) [MDR=%lx PC=%lx]\n",
|
||||
regs, excep, mdr, regs->pc);
|
||||
|
||||
gdbstub_entry(
|
||||
@ -1730,7 +1739,7 @@ asmlinkage int gdbstub_intercept(struct pt_regs *regs,
|
||||
|
||||
ret = gdbstub(regs, excep);
|
||||
|
||||
gdbstub_entry("<-- gdbstub_intercept()\n");
|
||||
gdbstub_entry("<-- debugger_intercept()\n");
|
||||
gdbstub_busy = 0;
|
||||
return ret;
|
||||
}
|
||||
|
@ -29,6 +29,13 @@ extern void ret_from_fork(struct task_struct *) __attribute__((noreturn));
|
||||
extern void mn10300_low_ipi_handler(void);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* smp.c
|
||||
*/
|
||||
#ifdef CONFIG_SMP
|
||||
extern void smp_jump_to_debugger(void);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* time.c
|
||||
*/
|
||||
|
@ -153,7 +153,7 @@ mn10300_cpupic_setaffinity(struct irq_data *d, const struct cpumask *mask,
|
||||
case LOCAL_TIMER_IPI:
|
||||
case FLUSH_CACHE_IPI:
|
||||
case CALL_FUNCTION_NMI_IPI:
|
||||
case GDB_NMI_IPI:
|
||||
case DEBUGGER_NMI_IPI:
|
||||
#ifdef CONFIG_MN10300_TTYSM0
|
||||
case SC0RXIRQ:
|
||||
case SC0TXIRQ:
|
||||
|
@ -439,6 +439,22 @@ int smp_nmi_call_function(smp_call_func_t func, void *info, int wait)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* smp_jump_to_debugger - Make other CPUs enter the debugger by sending an IPI
|
||||
*
|
||||
* Send a non-maskable request to all other CPUs in the system, instructing
|
||||
* them to jump into the debugger. The caller is responsible for checking that
|
||||
* the other CPUs responded to the instruction.
|
||||
*
|
||||
* The caller should make sure that this CPU's debugger IPI is disabled.
|
||||
*/
|
||||
void smp_jump_to_debugger(void)
|
||||
{
|
||||
if (num_online_cpus() > 1)
|
||||
/* Send a message to all other CPUs */
|
||||
send_IPI_allbutself(DEBUGGER_NMI_IPI);
|
||||
}
|
||||
|
||||
/**
|
||||
* stop_this_cpu - Callback to stop a CPU.
|
||||
* @unused: Callback context (ignored).
|
||||
@ -603,7 +619,7 @@ static void __init smp_cpu_init(void)
|
||||
/**
|
||||
* smp_prepare_cpu_init - Initialise CPU in startup_secondary
|
||||
*
|
||||
* Set interrupt level 0-6 setting and init ICR of gdbstub.
|
||||
* Set interrupt level 0-6 setting and init ICR of the kernel debugger.
|
||||
*/
|
||||
void smp_prepare_cpu_init(void)
|
||||
{
|
||||
@ -622,15 +638,15 @@ void smp_prepare_cpu_init(void)
|
||||
for (loop = 0; loop < GxICR_NUM_IRQS; loop++)
|
||||
GxICR(loop) = GxICR_LEVEL_6 | GxICR_DETECT;
|
||||
|
||||
#ifdef CONFIG_GDBSTUB
|
||||
/* initialise GDB-stub */
|
||||
#ifdef CONFIG_KERNEL_DEBUGGER
|
||||
/* initialise the kernel debugger interrupt */
|
||||
do {
|
||||
unsigned long flags;
|
||||
u16 tmp16;
|
||||
|
||||
flags = arch_local_cli_save();
|
||||
GxICR(GDB_NMI_IPI) = GxICR_NMI | GxICR_ENABLE | GxICR_DETECT;
|
||||
tmp16 = GxICR(GDB_NMI_IPI);
|
||||
GxICR(DEBUGGER_NMI_IPI) = GxICR_NMI | GxICR_ENABLE | GxICR_DETECT;
|
||||
tmp16 = GxICR(DEBUGGER_NMI_IPI);
|
||||
arch_local_irq_restore(flags);
|
||||
} while (0);
|
||||
#endif
|
||||
|
@ -38,8 +38,9 @@
|
||||
#include <asm/busctl-regs.h>
|
||||
#include <unit/leds.h>
|
||||
#include <asm/fpu.h>
|
||||
#include <asm/gdb-stub.h>
|
||||
#include <asm/sections.h>
|
||||
#include <asm/debugger.h>
|
||||
#include "internal.h"
|
||||
|
||||
#if (CONFIG_INTERRUPT_VECTOR_BASE & 0xffffff)
|
||||
#error "INTERRUPT_VECTOR_BASE not aligned to 16MiB boundary!"
|
||||
@ -49,63 +50,169 @@ int kstack_depth_to_print = 24;
|
||||
|
||||
spinlock_t die_lock = __SPIN_LOCK_UNLOCKED(die_lock);
|
||||
|
||||
ATOMIC_NOTIFIER_HEAD(mn10300_die_chain);
|
||||
struct exception_to_signal_map {
|
||||
u8 signo;
|
||||
u32 si_code;
|
||||
};
|
||||
|
||||
static const struct exception_to_signal_map exception_to_signal_map[256] = {
|
||||
/* MMU exceptions */
|
||||
[EXCEP_ITLBMISS >> 3] = { 0, 0 },
|
||||
[EXCEP_DTLBMISS >> 3] = { 0, 0 },
|
||||
[EXCEP_IAERROR >> 3] = { 0, 0 },
|
||||
[EXCEP_DAERROR >> 3] = { 0, 0 },
|
||||
|
||||
/* system exceptions */
|
||||
[EXCEP_TRAP >> 3] = { SIGTRAP, TRAP_BRKPT },
|
||||
[EXCEP_ISTEP >> 3] = { SIGTRAP, TRAP_TRACE }, /* Monitor */
|
||||
[EXCEP_IBREAK >> 3] = { SIGTRAP, TRAP_HWBKPT }, /* Monitor */
|
||||
[EXCEP_OBREAK >> 3] = { SIGTRAP, TRAP_HWBKPT }, /* Monitor */
|
||||
[EXCEP_PRIVINS >> 3] = { SIGILL, ILL_PRVOPC },
|
||||
[EXCEP_UNIMPINS >> 3] = { SIGILL, ILL_ILLOPC },
|
||||
[EXCEP_UNIMPEXINS >> 3] = { SIGILL, ILL_ILLOPC },
|
||||
[EXCEP_MEMERR >> 3] = { SIGSEGV, SEGV_ACCERR },
|
||||
[EXCEP_MISALIGN >> 3] = { SIGBUS, BUS_ADRALN },
|
||||
[EXCEP_BUSERROR >> 3] = { SIGBUS, BUS_ADRERR },
|
||||
[EXCEP_ILLINSACC >> 3] = { SIGSEGV, SEGV_ACCERR },
|
||||
[EXCEP_ILLDATACC >> 3] = { SIGSEGV, SEGV_ACCERR },
|
||||
[EXCEP_IOINSACC >> 3] = { SIGSEGV, SEGV_ACCERR },
|
||||
[EXCEP_PRIVINSACC >> 3] = { SIGSEGV, SEGV_ACCERR }, /* userspace */
|
||||
[EXCEP_PRIVDATACC >> 3] = { SIGSEGV, SEGV_ACCERR }, /* userspace */
|
||||
[EXCEP_DATINSACC >> 3] = { SIGSEGV, SEGV_ACCERR },
|
||||
[EXCEP_DOUBLE_FAULT >> 3] = { SIGILL, ILL_BADSTK },
|
||||
|
||||
/* FPU exceptions */
|
||||
[EXCEP_FPU_DISABLED >> 3] = { SIGILL, ILL_COPROC },
|
||||
[EXCEP_FPU_UNIMPINS >> 3] = { SIGILL, ILL_COPROC },
|
||||
[EXCEP_FPU_OPERATION >> 3] = { SIGFPE, FPE_INTDIV },
|
||||
|
||||
/* interrupts */
|
||||
[EXCEP_WDT >> 3] = { SIGALRM, 0 },
|
||||
[EXCEP_NMI >> 3] = { SIGQUIT, 0 },
|
||||
[EXCEP_IRQ_LEVEL0 >> 3] = { SIGINT, 0 },
|
||||
[EXCEP_IRQ_LEVEL1 >> 3] = { 0, 0 },
|
||||
[EXCEP_IRQ_LEVEL2 >> 3] = { 0, 0 },
|
||||
[EXCEP_IRQ_LEVEL3 >> 3] = { 0, 0 },
|
||||
[EXCEP_IRQ_LEVEL4 >> 3] = { 0, 0 },
|
||||
[EXCEP_IRQ_LEVEL5 >> 3] = { 0, 0 },
|
||||
[EXCEP_IRQ_LEVEL6 >> 3] = { 0, 0 },
|
||||
|
||||
/* system calls */
|
||||
[EXCEP_SYSCALL0 >> 3] = { 0, 0 },
|
||||
[EXCEP_SYSCALL1 >> 3] = { SIGILL, ILL_ILLTRP },
|
||||
[EXCEP_SYSCALL2 >> 3] = { SIGILL, ILL_ILLTRP },
|
||||
[EXCEP_SYSCALL3 >> 3] = { SIGILL, ILL_ILLTRP },
|
||||
[EXCEP_SYSCALL4 >> 3] = { SIGILL, ILL_ILLTRP },
|
||||
[EXCEP_SYSCALL5 >> 3] = { SIGILL, ILL_ILLTRP },
|
||||
[EXCEP_SYSCALL6 >> 3] = { SIGILL, ILL_ILLTRP },
|
||||
[EXCEP_SYSCALL7 >> 3] = { SIGILL, ILL_ILLTRP },
|
||||
[EXCEP_SYSCALL8 >> 3] = { SIGILL, ILL_ILLTRP },
|
||||
[EXCEP_SYSCALL9 >> 3] = { SIGILL, ILL_ILLTRP },
|
||||
[EXCEP_SYSCALL10 >> 3] = { SIGILL, ILL_ILLTRP },
|
||||
[EXCEP_SYSCALL11 >> 3] = { SIGILL, ILL_ILLTRP },
|
||||
[EXCEP_SYSCALL12 >> 3] = { SIGILL, ILL_ILLTRP },
|
||||
[EXCEP_SYSCALL13 >> 3] = { SIGILL, ILL_ILLTRP },
|
||||
[EXCEP_SYSCALL14 >> 3] = { SIGILL, ILL_ILLTRP },
|
||||
[EXCEP_SYSCALL15 >> 3] = { SIGABRT, 0 },
|
||||
};
|
||||
|
||||
/*
|
||||
* These constants are for searching for possible module text
|
||||
* segments. MODULE_RANGE is a guess of how much space is likely
|
||||
* to be vmalloced.
|
||||
* Handle kernel exceptions.
|
||||
*
|
||||
* See if there's a fixup handler we can force a jump to when an exception
|
||||
* happens due to something kernel code did
|
||||
*/
|
||||
#define MODULE_RANGE (8 * 1024 * 1024)
|
||||
int die_if_no_fixup(const char *str, struct pt_regs *regs,
|
||||
enum exception_code code)
|
||||
{
|
||||
u8 opcode;
|
||||
int signo, si_code;
|
||||
|
||||
#define DO_ERROR(signr, prologue, str, name) \
|
||||
asmlinkage void name(struct pt_regs *regs, u32 intcode) \
|
||||
{ \
|
||||
prologue; \
|
||||
if (die_if_no_fixup(str, regs, intcode)) \
|
||||
return; \
|
||||
force_sig(signr, current); \
|
||||
if (user_mode(regs))
|
||||
return 0;
|
||||
|
||||
peripheral_leds_display_exception(code);
|
||||
|
||||
signo = exception_to_signal_map[code >> 3].signo;
|
||||
si_code = exception_to_signal_map[code >> 3].si_code;
|
||||
|
||||
switch (code) {
|
||||
/* see if we can fixup the kernel accessing memory */
|
||||
case EXCEP_ITLBMISS:
|
||||
case EXCEP_DTLBMISS:
|
||||
case EXCEP_IAERROR:
|
||||
case EXCEP_DAERROR:
|
||||
case EXCEP_MEMERR:
|
||||
case EXCEP_MISALIGN:
|
||||
case EXCEP_BUSERROR:
|
||||
case EXCEP_ILLDATACC:
|
||||
case EXCEP_IOINSACC:
|
||||
case EXCEP_PRIVINSACC:
|
||||
case EXCEP_PRIVDATACC:
|
||||
case EXCEP_DATINSACC:
|
||||
if (fixup_exception(regs))
|
||||
return 1;
|
||||
break;
|
||||
|
||||
case EXCEP_TRAP:
|
||||
case EXCEP_UNIMPINS:
|
||||
if (get_user(opcode, (uint8_t __user *)regs->pc) != 0)
|
||||
break;
|
||||
if (opcode == 0xff) {
|
||||
if (notify_die(DIE_BREAKPOINT, str, regs, code, 0, 0))
|
||||
return 1;
|
||||
if (at_debugger_breakpoint(regs))
|
||||
regs->pc++;
|
||||
signo = SIGTRAP;
|
||||
si_code = TRAP_BRKPT;
|
||||
}
|
||||
break;
|
||||
|
||||
case EXCEP_SYSCALL1 ... EXCEP_SYSCALL14:
|
||||
/* syscall return addr is _after_ the instruction */
|
||||
regs->pc -= 2;
|
||||
break;
|
||||
|
||||
case EXCEP_SYSCALL15:
|
||||
if (report_bug(regs->pc, regs) == BUG_TRAP_TYPE_WARN)
|
||||
return 1;
|
||||
|
||||
/* syscall return addr is _after_ the instruction */
|
||||
regs->pc -= 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
#define DO_EINFO(signr, prologue, str, name, sicode) \
|
||||
asmlinkage void name(struct pt_regs *regs, u32 intcode) \
|
||||
{ \
|
||||
siginfo_t info; \
|
||||
prologue; \
|
||||
if (die_if_no_fixup(str, regs, intcode)) \
|
||||
return; \
|
||||
info.si_signo = signr; \
|
||||
if (signr == SIGILL && sicode == ILL_ILLOPC) { \
|
||||
uint8_t opcode; \
|
||||
if (get_user(opcode, (uint8_t __user *)regs->pc) == 0) \
|
||||
if (opcode == 0xff) \
|
||||
info.si_signo = SIGTRAP; \
|
||||
} \
|
||||
info.si_errno = 0; \
|
||||
info.si_code = sicode; \
|
||||
info.si_addr = (void *) regs->pc; \
|
||||
force_sig_info(info.si_signo, &info, current); \
|
||||
if (debugger_intercept(code, signo, si_code, regs) == 0)
|
||||
return 1;
|
||||
|
||||
if (notify_die(DIE_GPF, str, regs, code, 0, 0))
|
||||
return 1;
|
||||
|
||||
/* make the process die as the last resort */
|
||||
die(str, regs, code);
|
||||
}
|
||||
|
||||
DO_ERROR(SIGTRAP, {}, "trap", trap);
|
||||
DO_ERROR(SIGSEGV, {}, "ibreak", ibreak);
|
||||
DO_ERROR(SIGSEGV, {}, "obreak", obreak);
|
||||
DO_EINFO(SIGSEGV, {}, "access error", access_error, SEGV_ACCERR);
|
||||
DO_EINFO(SIGSEGV, {}, "insn access error", insn_acc_error, SEGV_ACCERR);
|
||||
DO_EINFO(SIGSEGV, {}, "data access error", data_acc_error, SEGV_ACCERR);
|
||||
DO_EINFO(SIGILL, {}, "privileged opcode", priv_op, ILL_PRVOPC);
|
||||
DO_EINFO(SIGILL, {}, "invalid opcode", invalid_op, ILL_ILLOPC);
|
||||
DO_EINFO(SIGILL, {}, "invalid ex opcode", invalid_exop, ILL_ILLOPC);
|
||||
DO_EINFO(SIGBUS, {}, "invalid address", mem_error, BUS_ADRERR);
|
||||
DO_EINFO(SIGBUS, {}, "bus error", bus_error, BUS_ADRERR);
|
||||
/*
|
||||
* General exception handler
|
||||
*/
|
||||
asmlinkage void handle_exception(struct pt_regs *regs, u32 intcode)
|
||||
{
|
||||
siginfo_t info;
|
||||
|
||||
DO_ERROR(SIGTRAP,
|
||||
#ifndef CONFIG_MN10300_USING_JTAG
|
||||
DCR &= ~0x0001,
|
||||
#else
|
||||
{},
|
||||
#endif
|
||||
"single step", istep);
|
||||
/* deal with kernel exceptions here */
|
||||
if (die_if_no_fixup(NULL, regs, intcode))
|
||||
return;
|
||||
|
||||
/* otherwise it's a userspace exception */
|
||||
info.si_signo = exception_to_signal_map[intcode >> 3].signo;
|
||||
info.si_code = exception_to_signal_map[intcode >> 3].si_code;
|
||||
info.si_errno = 0;
|
||||
info.si_addr = (void *) regs->pc;
|
||||
force_sig_info(info.si_signo, &info, current);
|
||||
}
|
||||
|
||||
/*
|
||||
* handle NMI
|
||||
@ -113,10 +220,8 @@ DO_ERROR(SIGTRAP,
|
||||
asmlinkage void nmi(struct pt_regs *regs, enum exception_code code)
|
||||
{
|
||||
/* see if gdbstub wants to deal with it */
|
||||
#ifdef CONFIG_GDBSTUB
|
||||
if (gdbstub_intercept(regs, code))
|
||||
if (debugger_intercept(code, SIGQUIT, 0, regs))
|
||||
return;
|
||||
#endif
|
||||
|
||||
printk(KERN_WARNING "--- Register Dump ---\n");
|
||||
show_registers(regs);
|
||||
@ -128,29 +233,36 @@ asmlinkage void nmi(struct pt_regs *regs, enum exception_code code)
|
||||
*/
|
||||
void show_trace(unsigned long *sp)
|
||||
{
|
||||
unsigned long *stack, addr, module_start, module_end;
|
||||
int i;
|
||||
unsigned long bottom, stack, addr, fp, raslot;
|
||||
|
||||
printk(KERN_EMERG "\nCall Trace:");
|
||||
printk(KERN_EMERG "\nCall Trace:\n");
|
||||
|
||||
stack = sp;
|
||||
i = 0;
|
||||
module_start = VMALLOC_START;
|
||||
module_end = VMALLOC_END;
|
||||
//stack = (unsigned long)sp;
|
||||
asm("mov sp,%0" : "=a"(stack));
|
||||
asm("mov a3,%0" : "=r"(fp));
|
||||
|
||||
raslot = ULONG_MAX;
|
||||
bottom = (stack + THREAD_SIZE) & ~(THREAD_SIZE - 1);
|
||||
for (; stack < bottom; stack += sizeof(addr)) {
|
||||
addr = *(unsigned long *)stack;
|
||||
if (stack == fp) {
|
||||
if (addr > stack && addr < bottom) {
|
||||
fp = addr;
|
||||
raslot = stack + sizeof(addr);
|
||||
continue;
|
||||
}
|
||||
fp = 0;
|
||||
raslot = ULONG_MAX;
|
||||
}
|
||||
|
||||
while (((long) stack & (THREAD_SIZE - 1)) != 0) {
|
||||
addr = *stack++;
|
||||
if (__kernel_text_address(addr)) {
|
||||
#if 1
|
||||
printk(" [<%08lx>]", addr);
|
||||
if (stack >= raslot)
|
||||
raslot = ULONG_MAX;
|
||||
else
|
||||
printk(" ?");
|
||||
print_symbol(" %s", addr);
|
||||
printk("\n");
|
||||
#else
|
||||
if ((i % 6) == 0)
|
||||
printk(KERN_EMERG " ");
|
||||
printk("[<%08lx>] ", addr);
|
||||
i++;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -322,86 +434,6 @@ void die(const char *str, struct pt_regs *regs, enum exception_code code)
|
||||
do_exit(SIGSEGV);
|
||||
}
|
||||
|
||||
/*
|
||||
* see if there's a fixup handler we can force a jump to when an exception
|
||||
* happens due to something kernel code did
|
||||
*/
|
||||
int die_if_no_fixup(const char *str, struct pt_regs *regs,
|
||||
enum exception_code code)
|
||||
{
|
||||
if (user_mode(regs))
|
||||
return 0;
|
||||
|
||||
peripheral_leds_display_exception(code);
|
||||
|
||||
switch (code) {
|
||||
/* see if we can fixup the kernel accessing memory */
|
||||
case EXCEP_ITLBMISS:
|
||||
case EXCEP_DTLBMISS:
|
||||
case EXCEP_IAERROR:
|
||||
case EXCEP_DAERROR:
|
||||
case EXCEP_MEMERR:
|
||||
case EXCEP_MISALIGN:
|
||||
case EXCEP_BUSERROR:
|
||||
case EXCEP_ILLDATACC:
|
||||
case EXCEP_IOINSACC:
|
||||
case EXCEP_PRIVINSACC:
|
||||
case EXCEP_PRIVDATACC:
|
||||
case EXCEP_DATINSACC:
|
||||
if (fixup_exception(regs))
|
||||
return 1;
|
||||
case EXCEP_UNIMPINS:
|
||||
if (regs->pc && *(uint8_t *)regs->pc == 0xff)
|
||||
if (notify_die(DIE_BREAKPOINT, str, regs, code, 0, 0))
|
||||
return 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* see if gdbstub wants to deal with it */
|
||||
#ifdef CONFIG_GDBSTUB
|
||||
if (gdbstub_intercept(regs, code))
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
if (notify_die(DIE_GPF, str, regs, code, 0, 0))
|
||||
return 1;
|
||||
|
||||
/* make the process die as the last resort */
|
||||
die(str, regs, code);
|
||||
}
|
||||
|
||||
/*
|
||||
* handle unsupported syscall instructions (syscall 1-15)
|
||||
*/
|
||||
static asmlinkage void unsupported_syscall(struct pt_regs *regs,
|
||||
enum exception_code code)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
siginfo_t info;
|
||||
|
||||
/* catch a kernel BUG() */
|
||||
if (code == EXCEP_SYSCALL15 && !user_mode(regs)) {
|
||||
if (report_bug(regs->pc, regs) == BUG_TRAP_TYPE_BUG) {
|
||||
#ifdef CONFIG_GDBSTUB
|
||||
gdbstub_intercept(regs, code);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
regs->pc -= 2; /* syscall return addr is _after_ the instruction */
|
||||
|
||||
die_if_no_fixup("An unsupported syscall insn was used by the kernel\n",
|
||||
regs, code);
|
||||
|
||||
info.si_signo = SIGILL;
|
||||
info.si_errno = ENOSYS;
|
||||
info.si_code = ILL_ILLTRP;
|
||||
info.si_addr = (void *) regs->pc;
|
||||
force_sig_info(SIGILL, &info, tsk);
|
||||
}
|
||||
|
||||
/*
|
||||
* display the register file when the stack pointer gets clobbered
|
||||
*/
|
||||
@ -481,10 +513,8 @@ asmlinkage void uninitialised_exception(struct pt_regs *regs,
|
||||
{
|
||||
|
||||
/* see if gdbstub wants to deal with it */
|
||||
#ifdef CONFIG_GDBSTUB
|
||||
if (gdbstub_intercept(regs, code))
|
||||
if (debugger_intercept(code, SIGSYS, 0, regs) == 0)
|
||||
return;
|
||||
#endif
|
||||
|
||||
peripheral_leds_display_exception(code);
|
||||
printk(KERN_EMERG "Uninitialised Exception 0x%04x\n", code & 0xFFFF);
|
||||
@ -549,43 +579,43 @@ void __init set_intr_stub(enum exception_code code, void *handler)
|
||||
*/
|
||||
void __init trap_init(void)
|
||||
{
|
||||
set_excp_vector(EXCEP_TRAP, trap);
|
||||
set_excp_vector(EXCEP_ISTEP, istep);
|
||||
set_excp_vector(EXCEP_IBREAK, ibreak);
|
||||
set_excp_vector(EXCEP_OBREAK, obreak);
|
||||
set_excp_vector(EXCEP_TRAP, handle_exception);
|
||||
set_excp_vector(EXCEP_ISTEP, handle_exception);
|
||||
set_excp_vector(EXCEP_IBREAK, handle_exception);
|
||||
set_excp_vector(EXCEP_OBREAK, handle_exception);
|
||||
|
||||
set_excp_vector(EXCEP_PRIVINS, priv_op);
|
||||
set_excp_vector(EXCEP_UNIMPINS, invalid_op);
|
||||
set_excp_vector(EXCEP_UNIMPEXINS, invalid_exop);
|
||||
set_excp_vector(EXCEP_MEMERR, mem_error);
|
||||
set_excp_vector(EXCEP_PRIVINS, handle_exception);
|
||||
set_excp_vector(EXCEP_UNIMPINS, handle_exception);
|
||||
set_excp_vector(EXCEP_UNIMPEXINS, handle_exception);
|
||||
set_excp_vector(EXCEP_MEMERR, handle_exception);
|
||||
set_excp_vector(EXCEP_MISALIGN, misalignment);
|
||||
set_excp_vector(EXCEP_BUSERROR, bus_error);
|
||||
set_excp_vector(EXCEP_ILLINSACC, insn_acc_error);
|
||||
set_excp_vector(EXCEP_ILLDATACC, data_acc_error);
|
||||
set_excp_vector(EXCEP_IOINSACC, insn_acc_error);
|
||||
set_excp_vector(EXCEP_PRIVINSACC, insn_acc_error);
|
||||
set_excp_vector(EXCEP_PRIVDATACC, data_acc_error);
|
||||
set_excp_vector(EXCEP_DATINSACC, insn_acc_error);
|
||||
set_excp_vector(EXCEP_FPU_UNIMPINS, fpu_invalid_op);
|
||||
set_excp_vector(EXCEP_BUSERROR, handle_exception);
|
||||
set_excp_vector(EXCEP_ILLINSACC, handle_exception);
|
||||
set_excp_vector(EXCEP_ILLDATACC, handle_exception);
|
||||
set_excp_vector(EXCEP_IOINSACC, handle_exception);
|
||||
set_excp_vector(EXCEP_PRIVINSACC, handle_exception);
|
||||
set_excp_vector(EXCEP_PRIVDATACC, handle_exception);
|
||||
set_excp_vector(EXCEP_DATINSACC, handle_exception);
|
||||
set_excp_vector(EXCEP_FPU_UNIMPINS, handle_exception);
|
||||
set_excp_vector(EXCEP_FPU_OPERATION, fpu_exception);
|
||||
|
||||
set_excp_vector(EXCEP_NMI, nmi);
|
||||
|
||||
set_excp_vector(EXCEP_SYSCALL1, unsupported_syscall);
|
||||
set_excp_vector(EXCEP_SYSCALL2, unsupported_syscall);
|
||||
set_excp_vector(EXCEP_SYSCALL3, unsupported_syscall);
|
||||
set_excp_vector(EXCEP_SYSCALL4, unsupported_syscall);
|
||||
set_excp_vector(EXCEP_SYSCALL5, unsupported_syscall);
|
||||
set_excp_vector(EXCEP_SYSCALL6, unsupported_syscall);
|
||||
set_excp_vector(EXCEP_SYSCALL7, unsupported_syscall);
|
||||
set_excp_vector(EXCEP_SYSCALL8, unsupported_syscall);
|
||||
set_excp_vector(EXCEP_SYSCALL9, unsupported_syscall);
|
||||
set_excp_vector(EXCEP_SYSCALL10, unsupported_syscall);
|
||||
set_excp_vector(EXCEP_SYSCALL11, unsupported_syscall);
|
||||
set_excp_vector(EXCEP_SYSCALL12, unsupported_syscall);
|
||||
set_excp_vector(EXCEP_SYSCALL13, unsupported_syscall);
|
||||
set_excp_vector(EXCEP_SYSCALL14, unsupported_syscall);
|
||||
set_excp_vector(EXCEP_SYSCALL15, unsupported_syscall);
|
||||
set_excp_vector(EXCEP_SYSCALL1, handle_exception);
|
||||
set_excp_vector(EXCEP_SYSCALL2, handle_exception);
|
||||
set_excp_vector(EXCEP_SYSCALL3, handle_exception);
|
||||
set_excp_vector(EXCEP_SYSCALL4, handle_exception);
|
||||
set_excp_vector(EXCEP_SYSCALL5, handle_exception);
|
||||
set_excp_vector(EXCEP_SYSCALL6, handle_exception);
|
||||
set_excp_vector(EXCEP_SYSCALL7, handle_exception);
|
||||
set_excp_vector(EXCEP_SYSCALL8, handle_exception);
|
||||
set_excp_vector(EXCEP_SYSCALL9, handle_exception);
|
||||
set_excp_vector(EXCEP_SYSCALL10, handle_exception);
|
||||
set_excp_vector(EXCEP_SYSCALL11, handle_exception);
|
||||
set_excp_vector(EXCEP_SYSCALL12, handle_exception);
|
||||
set_excp_vector(EXCEP_SYSCALL13, handle_exception);
|
||||
set_excp_vector(EXCEP_SYSCALL14, handle_exception);
|
||||
set_excp_vector(EXCEP_SYSCALL15, handle_exception);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -28,8 +28,9 @@
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/pgalloc.h>
|
||||
#include <asm/hardirq.h>
|
||||
#include <asm/gdb-stub.h>
|
||||
#include <asm/cpu-regs.h>
|
||||
#include <asm/debugger.h>
|
||||
#include <asm/gdb-stub.h>
|
||||
|
||||
/*
|
||||
* Unlock any spinlocks which will prevent us from getting the
|
||||
@ -306,10 +307,8 @@ no_context:
|
||||
printk(" printing pc:\n");
|
||||
printk(KERN_ALERT "%08lx\n", regs->pc);
|
||||
|
||||
#ifdef CONFIG_GDBSTUB
|
||||
gdbstub_intercept(
|
||||
regs, fault_code & 0x00010000 ? EXCEP_IAERROR : EXCEP_DAERROR);
|
||||
#endif
|
||||
debugger_intercept(fault_code & 0x00010000 ? EXCEP_IAERROR : EXCEP_DAERROR,
|
||||
SIGSEGV, SEGV_ACCERR, regs);
|
||||
|
||||
page = PTBR;
|
||||
page = ((unsigned long *) __va(page))[address >> 22];
|
||||
|
Loading…
Reference in New Issue
Block a user