Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 196339
b: refs/heads/master
c: dcc7871
h: refs/heads/master
i:
  196337: 9ce3656
  196335: 4520f81
v: v3
  • Loading branch information
Jason Wessel committed May 21, 2010
1 parent b455938 commit c4ca9da
Show file tree
Hide file tree
Showing 10 changed files with 187 additions and 21 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 67fc4e0cb931d6b4ccf21248e4199b154478ecea
refs/heads/master: dcc7871128e99458ca86186b7bc8bf27ff0c47b5
5 changes: 5 additions & 0 deletions trunk/arch/arm/kernel/kgdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *task)
gdb_regs[_CPSR] = thread_regs->ARM_cpsr;
}

void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc)
{
regs->ARM_pc = pc;
}

static int compiled_break;

int kgdb_arch_handle_exception(int exception_vector, int signo,
Expand Down
5 changes: 5 additions & 0 deletions trunk/arch/mips/kernel/kgdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,11 @@ void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
*(ptr++) = regs->cp0_epc;
}

void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc)
{
regs->cp0_epc = pc;
}

/*
* Calls linux_debug_hook before the kernel dies. If KGDB is enabled,
* then try to fall into the debugger
Expand Down
5 changes: 5 additions & 0 deletions trunk/arch/powerpc/kernel/kgdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,11 @@ void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
(unsigned long)(((void *)gdb_regs) + NUMREGBYTES));
}

void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc)
{
regs->nip = pc;
}

/*
* This function does PowerPC specific procesing for interfacing to gdb.
*/
Expand Down
5 changes: 5 additions & 0 deletions trunk/arch/x86/kernel/kgdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,11 @@ unsigned long kgdb_arch_pc(int exception, struct pt_regs *regs)
return instruction_pointer(regs);
}

void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip)
{
regs->ip = ip;
}

struct kgdb_arch arch_kgdb_ops = {
/* Breakpoint instruction: */
.gdb_bpt_instr = { 0xcc },
Expand Down
11 changes: 9 additions & 2 deletions trunk/include/linux/kgdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
#include <linux/serial_8250.h>
#include <linux/linkage.h>
#include <linux/init.h>

#include <asm/atomic.h>
#ifdef CONFIG_HAVE_ARCH_KGDB
#include <asm/kgdb.h>
#endif

#ifdef CONFIG_KGDB
struct pt_regs;

/**
Expand Down Expand Up @@ -262,6 +264,7 @@ extern struct kgdb_arch arch_kgdb_ops;

extern unsigned long __weak kgdb_arch_pc(int exception, struct pt_regs *regs);

extern void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc);
extern int kgdb_register_io_module(struct kgdb_io *local_kgdb_io_ops);
extern void kgdb_unregister_io_module(struct kgdb_io *local_kgdb_io_ops);
extern struct kgdb_io *dbg_io_ops;
Expand All @@ -279,5 +282,9 @@ extern int kgdb_nmicallback(int cpu, void *regs);

extern int kgdb_single_step;
extern atomic_t kgdb_active;

#define in_dbg_master() \
(raw_smp_processor_id() == atomic_read(&kgdb_active))
#else /* ! CONFIG_KGDB */
#define in_dbg_master() (0)
#endif /* ! CONFIG_KGDB */
#endif /* _KGDB_H_ */
107 changes: 90 additions & 17 deletions trunk/kernel/debug/debug_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include <linux/sysrq.h>
#include <linux/init.h>
#include <linux/kgdb.h>
#include <linux/kdb.h>
#include <linux/pid.h>
#include <linux/smp.h>
#include <linux/mm.h>
Expand Down Expand Up @@ -77,6 +78,11 @@ static DEFINE_SPINLOCK(kgdb_registration_lock);
static int kgdb_con_registered;
/* determine if kgdb console output should be used */
static int kgdb_use_con;
/* Next cpu to become the master debug core */
int dbg_switch_cpu;

/* Use kdb or gdbserver mode */
static int dbg_kdb_mode = 1;

static int __init opt_kgdb_con(char *str)
{
Expand All @@ -100,6 +106,7 @@ static struct kgdb_bkpt kgdb_break[KGDB_MAX_BREAKPOINTS] = {
* The CPU# of the active CPU, or -1 if none:
*/
atomic_t kgdb_active = ATOMIC_INIT(-1);
EXPORT_SYMBOL_GPL(kgdb_active);

/*
* We use NR_CPUs not PERCPU, in case kgdb is used to debug early
Expand Down Expand Up @@ -301,7 +308,7 @@ int dbg_set_sw_break(unsigned long addr)
return 0;
}

static int kgdb_deactivate_sw_breakpoints(void)
int dbg_deactivate_sw_breakpoints(void)
{
unsigned long addr;
int error;
Expand Down Expand Up @@ -395,8 +402,14 @@ static int kgdb_io_ready(int print_wait)
return 1;
if (atomic_read(&kgdb_setting_breakpoint))
return 1;
if (print_wait)
if (print_wait) {
#ifdef CONFIG_KGDB_KDB
if (!dbg_kdb_mode)
printk(KERN_CRIT "KGDB: waiting... or $3#33 for KDB\n");
#else
printk(KERN_CRIT "KGDB: Waiting for remote debugger\n");
#endif
}
return 1;
}

Expand All @@ -410,7 +423,7 @@ static int kgdb_reenter_check(struct kgdb_state *ks)
/* Panic on recursive debugger calls: */
exception_level++;
addr = kgdb_arch_pc(ks->ex_vector, ks->linux_regs);
kgdb_deactivate_sw_breakpoints();
dbg_deactivate_sw_breakpoints();

/*
* If the break point removed ok at the place exception
Expand Down Expand Up @@ -443,11 +456,24 @@ static int kgdb_reenter_check(struct kgdb_state *ks)
return 1;
}

static void dbg_cpu_switch(int cpu, int next_cpu)
{
/* Mark the cpu we are switching away from as a slave when it
* holds the kgdb_active token. This must be done so that the
* that all the cpus wait in for the debug core will not enter
* again as the master. */
if (cpu == atomic_read(&kgdb_active)) {
kgdb_info[cpu].exception_state |= DCPU_IS_SLAVE;
kgdb_info[cpu].exception_state &= ~DCPU_WANT_MASTER;
}
kgdb_info[next_cpu].exception_state |= DCPU_NEXT_MASTER;
}

static int kgdb_cpu_enter(struct kgdb_state *ks, struct pt_regs *regs)
{
unsigned long flags;
int sstep_tries = 100;
int error = 0;
int error;
int i, cpu;
int trace_on = 0;
acquirelock:
Expand All @@ -460,6 +486,8 @@ static int kgdb_cpu_enter(struct kgdb_state *ks, struct pt_regs *regs)
cpu = ks->cpu;
kgdb_info[cpu].debuggerinfo = regs;
kgdb_info[cpu].task = current;
kgdb_info[cpu].ret_state = 0;
kgdb_info[cpu].irq_depth = hardirq_count() >> HARDIRQ_SHIFT;
/*
* Make sure the above info reaches the primary CPU before
* our cpu_in_kgdb[] flag setting does:
Expand All @@ -471,7 +499,11 @@ static int kgdb_cpu_enter(struct kgdb_state *ks, struct pt_regs *regs)
* master cpu and acquire the kgdb_active lock:
*/
while (1) {
if (kgdb_info[cpu].exception_state & DCPU_WANT_MASTER) {
cpu_loop:
if (kgdb_info[cpu].exception_state & DCPU_NEXT_MASTER) {
kgdb_info[cpu].exception_state &= ~DCPU_NEXT_MASTER;
goto cpu_master_loop;
} else if (kgdb_info[cpu].exception_state & DCPU_WANT_MASTER) {
if (atomic_cmpxchg(&kgdb_active, -1, cpu) == cpu)
break;
} else if (kgdb_info[cpu].exception_state & DCPU_IS_SLAVE) {
Expand Down Expand Up @@ -513,7 +545,7 @@ static int kgdb_cpu_enter(struct kgdb_state *ks, struct pt_regs *regs)
}

if (!kgdb_io_ready(1)) {
error = 1;
kgdb_info[cpu].ret_state = 1;
goto kgdb_restore; /* No I/O connection, resume the system */
}

Expand Down Expand Up @@ -548,7 +580,7 @@ static int kgdb_cpu_enter(struct kgdb_state *ks, struct pt_regs *regs)
* Wait for the other CPUs to be notified and be waiting for us:
*/
for_each_online_cpu(i) {
while (!atomic_read(&cpu_in_kgdb[i]))
while (kgdb_do_roundup && !atomic_read(&cpu_in_kgdb[i]))
cpu_relax();
}

Expand All @@ -557,16 +589,34 @@ static int kgdb_cpu_enter(struct kgdb_state *ks, struct pt_regs *regs)
* in the debugger and all secondary CPUs are quiescent
*/
kgdb_post_primary_code(ks->linux_regs, ks->ex_vector, ks->err_code);
kgdb_deactivate_sw_breakpoints();
dbg_deactivate_sw_breakpoints();
kgdb_single_step = 0;
kgdb_contthread = current;
exception_level = 0;
trace_on = tracing_is_on();
if (trace_on)
tracing_off();

/* Talk to debugger with gdbserial protocol */
error = gdb_serial_stub(ks);
while (1) {
cpu_master_loop:
if (dbg_kdb_mode) {
kgdb_connected = 1;
error = kdb_stub(ks);
} else {
error = gdb_serial_stub(ks);
}

if (error == DBG_PASS_EVENT) {
dbg_kdb_mode = !dbg_kdb_mode;
kgdb_connected = 0;
} else if (error == DBG_SWITCH_CPU_EVENT) {
dbg_cpu_switch(cpu, dbg_switch_cpu);
goto cpu_loop;
} else {
kgdb_info[cpu].ret_state = error;
break;
}
}

/* Call the I/O driver's post_exception routine */
if (dbg_io_ops->post_exception)
Expand All @@ -578,11 +628,16 @@ static int kgdb_cpu_enter(struct kgdb_state *ks, struct pt_regs *regs)
for (i = NR_CPUS-1; i >= 0; i--)
atomic_dec(&passive_cpu_wait[i]);
/*
* Wait till all the CPUs have quit
* from the debugger.
* Wait till all the CPUs have quit from the debugger,
* but allow a CPU that hit an exception and is
* waiting to become the master to remain in the debug
* core.
*/
for_each_online_cpu(i) {
while (atomic_read(&cpu_in_kgdb[i]))
while (kgdb_do_roundup &&
atomic_read(&cpu_in_kgdb[i]) &&
!(kgdb_info[i].exception_state &
DCPU_WANT_MASTER))
cpu_relax();
}
}
Expand All @@ -603,7 +658,7 @@ static int kgdb_cpu_enter(struct kgdb_state *ks, struct pt_regs *regs)
clocksource_touch_watchdog();
local_irq_restore(flags);

return error;
return kgdb_info[cpu].ret_state;
}

/*
Expand Down Expand Up @@ -632,7 +687,8 @@ kgdb_handle_exception(int evector, int signo, int ecode, struct pt_regs *regs)
return 0; /* Ouch, double exception ! */
kgdb_info[ks->cpu].exception_state |= DCPU_WANT_MASTER;
ret = kgdb_cpu_enter(ks, regs);
kgdb_info[ks->cpu].exception_state &= ~DCPU_WANT_MASTER;
kgdb_info[ks->cpu].exception_state &= ~(DCPU_WANT_MASTER |
DCPU_IS_SLAVE);
return ret;
}

Expand Down Expand Up @@ -665,7 +721,7 @@ static void kgdb_console_write(struct console *co, const char *s,

/* If we're debugging, or KGDB has not connected, don't try
* and print. */
if (!kgdb_connected || atomic_read(&kgdb_active) != -1)
if (!kgdb_connected || atomic_read(&kgdb_active) != -1 || dbg_kdb_mode)
return;

local_irq_save(flags);
Expand All @@ -687,8 +743,14 @@ static void sysrq_handle_dbg(int key, struct tty_struct *tty)
printk(KERN_CRIT "ERROR: No KGDB I/O module available\n");
return;
}
if (!kgdb_connected)
if (!kgdb_connected) {
#ifdef CONFIG_KGDB_KDB
if (!dbg_kdb_mode)
printk(KERN_CRIT "KGDB or $3#33 for KDB\n");
#else
printk(KERN_CRIT "Entering KGDB\n");
#endif
}

kgdb_breakpoint();
}
Expand Down Expand Up @@ -817,6 +879,16 @@ void kgdb_unregister_io_module(struct kgdb_io *old_dbg_io_ops)
}
EXPORT_SYMBOL_GPL(kgdb_unregister_io_module);

int dbg_io_get_char(void)
{
int ret = dbg_io_ops->read_char();
if (!dbg_kdb_mode)
return ret;
if (ret == 127)
return 8;
return ret;
}

/**
* kgdb_breakpoint - generate breakpoint exception
*
Expand All @@ -839,6 +911,7 @@ static int __init opt_kgdb_wait(char *str)
{
kgdb_break_asap = 1;

kdb_init(KDB_INIT_EARLY);
if (kgdb_io_module_registered)
kgdb_initial_breakpoint();

Expand Down
24 changes: 24 additions & 0 deletions trunk/kernel/debug/debug_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ struct debuggerinfo_struct {
void *debuggerinfo;
struct task_struct *task;
int exception_state;
int ret_state;
int irq_depth;
};

extern struct debuggerinfo_struct kgdb_info[];
Expand All @@ -47,9 +49,31 @@ extern int dbg_remove_all_break(void);
extern int dbg_set_sw_break(unsigned long addr);
extern int dbg_remove_sw_break(unsigned long addr);
extern int dbg_activate_sw_breakpoints(void);
extern int dbg_deactivate_sw_breakpoints(void);

/* polled character access to i/o module */
extern int dbg_io_get_char(void);

/* stub return value for switching between the gdbstub and kdb */
#define DBG_PASS_EVENT -12345
/* Switch from one cpu to another */
#define DBG_SWITCH_CPU_EVENT -123456
extern int dbg_switch_cpu;

/* gdbstub interface functions */
extern int gdb_serial_stub(struct kgdb_state *ks);
extern void gdbstub_msg_write(const char *s, int len);

/* gdbstub functions used for kdb <-> gdbstub transition */
extern int gdbstub_state(struct kgdb_state *ks, char *cmd);

#ifdef CONFIG_KGDB_KDB
extern int kdb_stub(struct kgdb_state *ks);
#else /* ! CONFIG_KGDB_KDB */
static inline int kdb_stub(struct kgdb_state *ks)
{
return DBG_PASS_EVENT;
}
#endif /* CONFIG_KGDB_KDB */

#endif /* _DEBUG_CORE_H_ */
Loading

0 comments on commit c4ca9da

Please sign in to comment.