Merge branch 'dbg-early-merge' of git://git.kernel.org/pub/scm/linux/kernel/git/jwessel/linux-2.6-kgdb
* 'dbg-early-merge' of git://git.kernel.org/pub/scm/linux/kernel/git/jwessel/linux-2.6-kgdb: echi-dbgp: Add kernel debugger support for the usb debug port earlyprintk,vga,kdb: Fix \b and \r for earlyprintk=vga with kdb kgdboc: Add ekgdboc for early use of the kernel debugger x86,early dr regs,kgdb: Allow kernel debugger early dr register access x86,kgdb: Implement early hardware breakpoint debugging x86, kgdb, init: Add early and late debug states x86, kgdb: early trap init for early debug
This commit is contained in:
@@ -713,6 +713,12 @@ and is between 256 and 4096 characters. It is defined in the file
|
|||||||
The VGA output is eventually overwritten by the real
|
The VGA output is eventually overwritten by the real
|
||||||
console.
|
console.
|
||||||
|
|
||||||
|
ekgdboc= [X86,KGDB] Allow early kernel console debugging
|
||||||
|
ekgdboc=kbd
|
||||||
|
|
||||||
|
This is desgined to be used in conjunction with
|
||||||
|
the boot argument: earlyprintk=vga
|
||||||
|
|
||||||
eata= [HW,SCSI]
|
eata= [HW,SCSI]
|
||||||
|
|
||||||
edd= [EDD]
|
edd= [EDD]
|
||||||
@@ -1121,6 +1127,17 @@ and is between 256 and 4096 characters. It is defined in the file
|
|||||||
use the HighMem zone if it exists, and the Normal
|
use the HighMem zone if it exists, and the Normal
|
||||||
zone if it does not.
|
zone if it does not.
|
||||||
|
|
||||||
|
kgdbdbgp= [KGDB,HW] kgdb over EHCI usb debug port.
|
||||||
|
Format: <Controller#>[,poll interval]
|
||||||
|
The controller # is the number of the ehci usb debug
|
||||||
|
port as it is probed via PCI. The poll interval is
|
||||||
|
optional and is the number seconds in between
|
||||||
|
each poll cycle to the debug port in case you need
|
||||||
|
the functionality for interrupting the kernel with
|
||||||
|
gdb or control-c on the dbgp connection. When
|
||||||
|
not using this parameter you use sysrq-g to break into
|
||||||
|
the kernel debugger.
|
||||||
|
|
||||||
kgdboc= [KGDB,HW] kgdb over consoles.
|
kgdboc= [KGDB,HW] kgdb over consoles.
|
||||||
Requires a tty driver that supports console polling,
|
Requires a tty driver that supports console polling,
|
||||||
or a supported polling keyboard driver (non-usb).
|
or a supported polling keyboard driver (non-usb).
|
||||||
|
@@ -789,6 +789,8 @@ static inline void wbinvd_halt(void)
|
|||||||
extern void enable_sep_cpu(void);
|
extern void enable_sep_cpu(void);
|
||||||
extern int sysenter_setup(void);
|
extern int sysenter_setup(void);
|
||||||
|
|
||||||
|
extern void early_trap_init(void);
|
||||||
|
|
||||||
/* Defined in head.S */
|
/* Defined in head.S */
|
||||||
extern struct desc_ptr early_gdt_descr;
|
extern struct desc_ptr early_gdt_descr;
|
||||||
|
|
||||||
|
@@ -1084,6 +1084,20 @@ static void clear_all_debug_regs(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_KGDB
|
||||||
|
/*
|
||||||
|
* Restore debug regs if using kgdbwait and you have a kernel debugger
|
||||||
|
* connection established.
|
||||||
|
*/
|
||||||
|
static void dbg_restore_debug_regs(void)
|
||||||
|
{
|
||||||
|
if (unlikely(kgdb_connected && arch_kgdb_ops.correct_hw_break))
|
||||||
|
arch_kgdb_ops.correct_hw_break();
|
||||||
|
}
|
||||||
|
#else /* ! CONFIG_KGDB */
|
||||||
|
#define dbg_restore_debug_regs()
|
||||||
|
#endif /* ! CONFIG_KGDB */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* cpu_init() initializes state that is per-CPU. Some data is already
|
* cpu_init() initializes state that is per-CPU. Some data is already
|
||||||
* initialized (naturally) in the bootstrap process, such as the GDT
|
* initialized (naturally) in the bootstrap process, such as the GDT
|
||||||
@@ -1174,18 +1188,8 @@ void __cpuinit cpu_init(void)
|
|||||||
load_TR_desc();
|
load_TR_desc();
|
||||||
load_LDT(&init_mm.context);
|
load_LDT(&init_mm.context);
|
||||||
|
|
||||||
#ifdef CONFIG_KGDB
|
|
||||||
/*
|
|
||||||
* If the kgdb is connected no debug regs should be altered. This
|
|
||||||
* is only applicable when KGDB and a KGDB I/O module are built
|
|
||||||
* into the kernel and you are using early debugging with
|
|
||||||
* kgdbwait. KGDB will control the kernel HW breakpoint registers.
|
|
||||||
*/
|
|
||||||
if (kgdb_connected && arch_kgdb_ops.correct_hw_break)
|
|
||||||
arch_kgdb_ops.correct_hw_break();
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
clear_all_debug_regs();
|
clear_all_debug_regs();
|
||||||
|
dbg_restore_debug_regs();
|
||||||
|
|
||||||
fpu_init();
|
fpu_init();
|
||||||
|
|
||||||
@@ -1239,6 +1243,7 @@ void __cpuinit cpu_init(void)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
clear_all_debug_regs();
|
clear_all_debug_regs();
|
||||||
|
dbg_restore_debug_regs();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Force FPU initialization:
|
* Force FPU initialization:
|
||||||
|
@@ -41,6 +41,14 @@ static void early_vga_write(struct console *con, const char *str, unsigned n)
|
|||||||
writew(0x720, VGABASE + 2*(max_xpos*j + i));
|
writew(0x720, VGABASE + 2*(max_xpos*j + i));
|
||||||
current_ypos = max_ypos-1;
|
current_ypos = max_ypos-1;
|
||||||
}
|
}
|
||||||
|
#ifdef CONFIG_KGDB_KDB
|
||||||
|
if (c == '\b') {
|
||||||
|
if (current_xpos > 0)
|
||||||
|
current_xpos--;
|
||||||
|
} else if (c == '\r') {
|
||||||
|
current_xpos = 0;
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
if (c == '\n') {
|
if (c == '\n') {
|
||||||
current_xpos = 0;
|
current_xpos = 0;
|
||||||
current_ypos++;
|
current_ypos++;
|
||||||
|
@@ -199,6 +199,8 @@ static struct hw_breakpoint {
|
|||||||
struct perf_event **pev;
|
struct perf_event **pev;
|
||||||
} breakinfo[4];
|
} breakinfo[4];
|
||||||
|
|
||||||
|
static unsigned long early_dr7;
|
||||||
|
|
||||||
static void kgdb_correct_hw_break(void)
|
static void kgdb_correct_hw_break(void)
|
||||||
{
|
{
|
||||||
int breakno;
|
int breakno;
|
||||||
@@ -210,6 +212,14 @@ static void kgdb_correct_hw_break(void)
|
|||||||
int cpu = raw_smp_processor_id();
|
int cpu = raw_smp_processor_id();
|
||||||
if (!breakinfo[breakno].enabled)
|
if (!breakinfo[breakno].enabled)
|
||||||
continue;
|
continue;
|
||||||
|
if (dbg_is_early) {
|
||||||
|
set_debugreg(breakinfo[breakno].addr, breakno);
|
||||||
|
early_dr7 |= encode_dr7(breakno,
|
||||||
|
breakinfo[breakno].len,
|
||||||
|
breakinfo[breakno].type);
|
||||||
|
set_debugreg(early_dr7, 7);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
bp = *per_cpu_ptr(breakinfo[breakno].pev, cpu);
|
bp = *per_cpu_ptr(breakinfo[breakno].pev, cpu);
|
||||||
info = counter_arch_bp(bp);
|
info = counter_arch_bp(bp);
|
||||||
if (bp->attr.disabled != 1)
|
if (bp->attr.disabled != 1)
|
||||||
@@ -224,6 +234,7 @@ static void kgdb_correct_hw_break(void)
|
|||||||
if (!val)
|
if (!val)
|
||||||
bp->attr.disabled = 0;
|
bp->attr.disabled = 0;
|
||||||
}
|
}
|
||||||
|
if (!dbg_is_early)
|
||||||
hw_breakpoint_restore();
|
hw_breakpoint_restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,6 +244,9 @@ static int hw_break_reserve_slot(int breakno)
|
|||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
struct perf_event **pevent;
|
struct perf_event **pevent;
|
||||||
|
|
||||||
|
if (dbg_is_early)
|
||||||
|
return 0;
|
||||||
|
|
||||||
for_each_online_cpu(cpu) {
|
for_each_online_cpu(cpu) {
|
||||||
cnt++;
|
cnt++;
|
||||||
pevent = per_cpu_ptr(breakinfo[breakno].pev, cpu);
|
pevent = per_cpu_ptr(breakinfo[breakno].pev, cpu);
|
||||||
@@ -258,6 +272,9 @@ static int hw_break_release_slot(int breakno)
|
|||||||
struct perf_event **pevent;
|
struct perf_event **pevent;
|
||||||
int cpu;
|
int cpu;
|
||||||
|
|
||||||
|
if (dbg_is_early)
|
||||||
|
return 0;
|
||||||
|
|
||||||
for_each_online_cpu(cpu) {
|
for_each_online_cpu(cpu) {
|
||||||
pevent = per_cpu_ptr(breakinfo[breakno].pev, cpu);
|
pevent = per_cpu_ptr(breakinfo[breakno].pev, cpu);
|
||||||
if (dbg_release_bp_slot(*pevent))
|
if (dbg_release_bp_slot(*pevent))
|
||||||
@@ -302,6 +319,10 @@ static void kgdb_remove_all_hw_break(void)
|
|||||||
bp = *per_cpu_ptr(breakinfo[i].pev, cpu);
|
bp = *per_cpu_ptr(breakinfo[i].pev, cpu);
|
||||||
if (bp->attr.disabled == 1)
|
if (bp->attr.disabled == 1)
|
||||||
continue;
|
continue;
|
||||||
|
if (dbg_is_early)
|
||||||
|
early_dr7 &= ~encode_dr7(i, breakinfo[i].len,
|
||||||
|
breakinfo[i].type);
|
||||||
|
else
|
||||||
arch_uninstall_hw_breakpoint(bp);
|
arch_uninstall_hw_breakpoint(bp);
|
||||||
bp->attr.disabled = 1;
|
bp->attr.disabled = 1;
|
||||||
}
|
}
|
||||||
@@ -379,6 +400,11 @@ void kgdb_disable_hw_debug(struct pt_regs *regs)
|
|||||||
for (i = 0; i < 4; i++) {
|
for (i = 0; i < 4; i++) {
|
||||||
if (!breakinfo[i].enabled)
|
if (!breakinfo[i].enabled)
|
||||||
continue;
|
continue;
|
||||||
|
if (dbg_is_early) {
|
||||||
|
early_dr7 &= ~encode_dr7(i, breakinfo[i].len,
|
||||||
|
breakinfo[i].type);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
bp = *per_cpu_ptr(breakinfo[i].pev, cpu);
|
bp = *per_cpu_ptr(breakinfo[i].pev, cpu);
|
||||||
if (bp->attr.disabled == 1)
|
if (bp->attr.disabled == 1)
|
||||||
continue;
|
continue;
|
||||||
@@ -595,15 +621,16 @@ static struct notifier_block kgdb_notifier = {
|
|||||||
* specific callbacks.
|
* specific callbacks.
|
||||||
*/
|
*/
|
||||||
int kgdb_arch_init(void)
|
int kgdb_arch_init(void)
|
||||||
|
{
|
||||||
|
return register_die_notifier(&kgdb_notifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kgdb_arch_late(void)
|
||||||
{
|
{
|
||||||
int i, cpu;
|
int i, cpu;
|
||||||
int ret;
|
|
||||||
struct perf_event_attr attr;
|
struct perf_event_attr attr;
|
||||||
struct perf_event **pevent;
|
struct perf_event **pevent;
|
||||||
|
|
||||||
ret = register_die_notifier(&kgdb_notifier);
|
|
||||||
if (ret != 0)
|
|
||||||
return ret;
|
|
||||||
/*
|
/*
|
||||||
* Pre-allocate the hw breakpoint structions in the non-atomic
|
* Pre-allocate the hw breakpoint structions in the non-atomic
|
||||||
* portion of kgdb because this operation requires mutexs to
|
* portion of kgdb because this operation requires mutexs to
|
||||||
@@ -615,12 +642,15 @@ int kgdb_arch_init(void)
|
|||||||
attr.bp_type = HW_BREAKPOINT_W;
|
attr.bp_type = HW_BREAKPOINT_W;
|
||||||
attr.disabled = 1;
|
attr.disabled = 1;
|
||||||
for (i = 0; i < 4; i++) {
|
for (i = 0; i < 4; i++) {
|
||||||
|
if (breakinfo[i].pev)
|
||||||
|
continue;
|
||||||
breakinfo[i].pev = register_wide_hw_breakpoint(&attr, NULL);
|
breakinfo[i].pev = register_wide_hw_breakpoint(&attr, NULL);
|
||||||
if (IS_ERR(breakinfo[i].pev)) {
|
if (IS_ERR(breakinfo[i].pev)) {
|
||||||
printk(KERN_ERR "kgdb: Could not allocate hw breakpoints\n");
|
printk(KERN_ERR "kgdb: Could not allocate hw"
|
||||||
|
"breakpoints\nDisabling the kernel debugger\n");
|
||||||
breakinfo[i].pev = NULL;
|
breakinfo[i].pev = NULL;
|
||||||
kgdb_arch_exit();
|
kgdb_arch_exit();
|
||||||
return -1;
|
return;
|
||||||
}
|
}
|
||||||
for_each_online_cpu(cpu) {
|
for_each_online_cpu(cpu) {
|
||||||
pevent = per_cpu_ptr(breakinfo[i].pev, cpu);
|
pevent = per_cpu_ptr(breakinfo[i].pev, cpu);
|
||||||
@@ -631,7 +661,6 @@ int kgdb_arch_init(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -725,6 +725,7 @@ void __init setup_arch(char **cmdline_p)
|
|||||||
/* VMI may relocate the fixmap; do this before touching ioremap area */
|
/* VMI may relocate the fixmap; do this before touching ioremap area */
|
||||||
vmi_init();
|
vmi_init();
|
||||||
|
|
||||||
|
early_trap_init();
|
||||||
early_cpu_init();
|
early_cpu_init();
|
||||||
early_ioremap_init();
|
early_ioremap_init();
|
||||||
|
|
||||||
|
@@ -808,6 +808,16 @@ dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Set of traps needed for early debugging. */
|
||||||
|
void __init early_trap_init(void)
|
||||||
|
{
|
||||||
|
set_intr_gate_ist(1, &debug, DEBUG_STACK);
|
||||||
|
/* int3 can be called from all */
|
||||||
|
set_system_intr_gate_ist(3, &int3, DEBUG_STACK);
|
||||||
|
set_intr_gate(14, &page_fault);
|
||||||
|
load_idt(&idt_descr);
|
||||||
|
}
|
||||||
|
|
||||||
void __init trap_init(void)
|
void __init trap_init(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@@ -821,10 +831,7 @@ void __init trap_init(void)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
set_intr_gate(0, ÷_error);
|
set_intr_gate(0, ÷_error);
|
||||||
set_intr_gate_ist(1, &debug, DEBUG_STACK);
|
|
||||||
set_intr_gate_ist(2, &nmi, NMI_STACK);
|
set_intr_gate_ist(2, &nmi, NMI_STACK);
|
||||||
/* int3 can be called from all */
|
|
||||||
set_system_intr_gate_ist(3, &int3, DEBUG_STACK);
|
|
||||||
/* int4 can be called from all */
|
/* int4 can be called from all */
|
||||||
set_system_intr_gate(4, &overflow);
|
set_system_intr_gate(4, &overflow);
|
||||||
set_intr_gate(5, &bounds);
|
set_intr_gate(5, &bounds);
|
||||||
@@ -840,7 +847,6 @@ void __init trap_init(void)
|
|||||||
set_intr_gate(11, &segment_not_present);
|
set_intr_gate(11, &segment_not_present);
|
||||||
set_intr_gate_ist(12, &stack_segment, STACKFAULT_STACK);
|
set_intr_gate_ist(12, &stack_segment, STACKFAULT_STACK);
|
||||||
set_intr_gate(13, &general_protection);
|
set_intr_gate(13, &general_protection);
|
||||||
set_intr_gate(14, &page_fault);
|
|
||||||
set_intr_gate(15, &spurious_interrupt_bug);
|
set_intr_gate(15, &spurious_interrupt_bug);
|
||||||
set_intr_gate(16, &coprocessor_error);
|
set_intr_gate(16, &coprocessor_error);
|
||||||
set_intr_gate(17, &alignment_check);
|
set_intr_gate(17, &alignment_check);
|
||||||
|
@@ -223,6 +223,25 @@ static struct kgdb_io kgdboc_io_ops = {
|
|||||||
.post_exception = kgdboc_post_exp_handler,
|
.post_exception = kgdboc_post_exp_handler,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_KGDB_SERIAL_CONSOLE
|
||||||
|
/* This is only available if kgdboc is a built in for early debugging */
|
||||||
|
int __init kgdboc_early_init(char *opt)
|
||||||
|
{
|
||||||
|
/* save the first character of the config string because the
|
||||||
|
* init routine can destroy it.
|
||||||
|
*/
|
||||||
|
char save_ch;
|
||||||
|
|
||||||
|
kgdboc_option_setup(opt);
|
||||||
|
save_ch = config[0];
|
||||||
|
init_kgdboc();
|
||||||
|
config[0] = save_ch;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
early_param("ekgdboc", kgdboc_early_init);
|
||||||
|
#endif /* CONFIG_KGDB_SERIAL_CONSOLE */
|
||||||
|
|
||||||
module_init(init_kgdboc);
|
module_init(init_kgdboc);
|
||||||
module_exit(cleanup_kgdboc);
|
module_exit(cleanup_kgdboc);
|
||||||
module_param_call(kgdboc, param_set_kgdboc_var, param_get_string, &kps, 0644);
|
module_param_call(kgdboc, param_set_kgdboc_var, param_get_string, &kps, 0644);
|
||||||
|
@@ -19,6 +19,9 @@
|
|||||||
#include <linux/usb/ch9.h>
|
#include <linux/usb/ch9.h>
|
||||||
#include <linux/usb/ehci_def.h>
|
#include <linux/usb/ehci_def.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
#include <linux/serial_core.h>
|
||||||
|
#include <linux/kgdb.h>
|
||||||
|
#include <linux/kthread.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
#include <asm/pci-direct.h>
|
#include <asm/pci-direct.h>
|
||||||
#include <asm/fixmap.h>
|
#include <asm/fixmap.h>
|
||||||
@@ -55,6 +58,7 @@ static struct ehci_regs __iomem *ehci_regs;
|
|||||||
static struct ehci_dbg_port __iomem *ehci_debug;
|
static struct ehci_dbg_port __iomem *ehci_debug;
|
||||||
static int dbgp_not_safe; /* Cannot use debug device during ehci reset */
|
static int dbgp_not_safe; /* Cannot use debug device during ehci reset */
|
||||||
static unsigned int dbgp_endpoint_out;
|
static unsigned int dbgp_endpoint_out;
|
||||||
|
static unsigned int dbgp_endpoint_in;
|
||||||
|
|
||||||
struct ehci_dev {
|
struct ehci_dev {
|
||||||
u32 bus;
|
u32 bus;
|
||||||
@@ -91,6 +95,13 @@ static inline u32 dbgp_len_update(u32 x, u32 len)
|
|||||||
return (x & ~0x0f) | (len & 0x0f);
|
return (x & ~0x0f) | (len & 0x0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_KGDB
|
||||||
|
static struct kgdb_io kgdbdbgp_io_ops;
|
||||||
|
#define dbgp_kgdb_mode (dbg_io_ops == &kgdbdbgp_io_ops)
|
||||||
|
#else
|
||||||
|
#define dbgp_kgdb_mode (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* USB Packet IDs (PIDs)
|
* USB Packet IDs (PIDs)
|
||||||
*/
|
*/
|
||||||
@@ -182,11 +193,10 @@ static void dbgp_breath(void)
|
|||||||
/* Sleep to give the debug port a chance to breathe */
|
/* Sleep to give the debug port a chance to breathe */
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dbgp_wait_until_done(unsigned ctrl)
|
static int dbgp_wait_until_done(unsigned ctrl, int loop)
|
||||||
{
|
{
|
||||||
u32 pids, lpid;
|
u32 pids, lpid;
|
||||||
int ret;
|
int ret;
|
||||||
int loop = DBGP_LOOPS;
|
|
||||||
|
|
||||||
retry:
|
retry:
|
||||||
writel(ctrl | DBGP_GO, &ehci_debug->control);
|
writel(ctrl | DBGP_GO, &ehci_debug->control);
|
||||||
@@ -276,13 +286,13 @@ static int dbgp_bulk_write(unsigned devnum, unsigned endpoint,
|
|||||||
dbgp_set_data(bytes, size);
|
dbgp_set_data(bytes, size);
|
||||||
writel(addr, &ehci_debug->address);
|
writel(addr, &ehci_debug->address);
|
||||||
writel(pids, &ehci_debug->pids);
|
writel(pids, &ehci_debug->pids);
|
||||||
ret = dbgp_wait_until_done(ctrl);
|
ret = dbgp_wait_until_done(ctrl, DBGP_LOOPS);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dbgp_bulk_read(unsigned devnum, unsigned endpoint, void *data,
|
static int dbgp_bulk_read(unsigned devnum, unsigned endpoint, void *data,
|
||||||
int size)
|
int size, int loops)
|
||||||
{
|
{
|
||||||
u32 pids, addr, ctrl;
|
u32 pids, addr, ctrl;
|
||||||
int ret;
|
int ret;
|
||||||
@@ -302,7 +312,7 @@ static int dbgp_bulk_read(unsigned devnum, unsigned endpoint, void *data,
|
|||||||
|
|
||||||
writel(addr, &ehci_debug->address);
|
writel(addr, &ehci_debug->address);
|
||||||
writel(pids, &ehci_debug->pids);
|
writel(pids, &ehci_debug->pids);
|
||||||
ret = dbgp_wait_until_done(ctrl);
|
ret = dbgp_wait_until_done(ctrl, loops);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@@ -343,12 +353,12 @@ static int dbgp_control_msg(unsigned devnum, int requesttype,
|
|||||||
dbgp_set_data(&req, sizeof(req));
|
dbgp_set_data(&req, sizeof(req));
|
||||||
writel(addr, &ehci_debug->address);
|
writel(addr, &ehci_debug->address);
|
||||||
writel(pids, &ehci_debug->pids);
|
writel(pids, &ehci_debug->pids);
|
||||||
ret = dbgp_wait_until_done(ctrl);
|
ret = dbgp_wait_until_done(ctrl, DBGP_LOOPS);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* Read the result */
|
/* Read the result */
|
||||||
return dbgp_bulk_read(devnum, 0, data, size);
|
return dbgp_bulk_read(devnum, 0, data, size, DBGP_LOOPS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find a PCI capability */
|
/* Find a PCI capability */
|
||||||
@@ -559,6 +569,7 @@ try_again:
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
dbgp_endpoint_out = dbgp_desc.bDebugOutEndpoint;
|
dbgp_endpoint_out = dbgp_desc.bDebugOutEndpoint;
|
||||||
|
dbgp_endpoint_in = dbgp_desc.bDebugInEndpoint;
|
||||||
|
|
||||||
/* Move the device to 127 if it isn't already there */
|
/* Move the device to 127 if it isn't already there */
|
||||||
if (devnum != USB_DEBUG_DEVNUM) {
|
if (devnum != USB_DEBUG_DEVNUM) {
|
||||||
@@ -968,8 +979,9 @@ int dbgp_reset_prep(void)
|
|||||||
if (!ehci_debug)
|
if (!ehci_debug)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (early_dbgp_console.index != -1 &&
|
if ((early_dbgp_console.index != -1 &&
|
||||||
!(early_dbgp_console.flags & CON_BOOT))
|
!(early_dbgp_console.flags & CON_BOOT)) ||
|
||||||
|
dbgp_kgdb_mode)
|
||||||
return 1;
|
return 1;
|
||||||
/* This means the console is not initialized, or should get
|
/* This means the console is not initialized, or should get
|
||||||
* shutdown so as to allow for reuse of the usb device, which
|
* shutdown so as to allow for reuse of the usb device, which
|
||||||
@@ -982,3 +994,93 @@ int dbgp_reset_prep(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(dbgp_reset_prep);
|
EXPORT_SYMBOL_GPL(dbgp_reset_prep);
|
||||||
|
|
||||||
|
#ifdef CONFIG_KGDB
|
||||||
|
|
||||||
|
static char kgdbdbgp_buf[DBGP_MAX_PACKET];
|
||||||
|
static int kgdbdbgp_buf_sz;
|
||||||
|
static int kgdbdbgp_buf_idx;
|
||||||
|
static int kgdbdbgp_loop_cnt = DBGP_LOOPS;
|
||||||
|
|
||||||
|
static int kgdbdbgp_read_char(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (kgdbdbgp_buf_idx < kgdbdbgp_buf_sz) {
|
||||||
|
char ch = kgdbdbgp_buf[kgdbdbgp_buf_idx++];
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = dbgp_bulk_read(USB_DEBUG_DEVNUM, dbgp_endpoint_in,
|
||||||
|
&kgdbdbgp_buf, DBGP_MAX_PACKET,
|
||||||
|
kgdbdbgp_loop_cnt);
|
||||||
|
if (ret <= 0)
|
||||||
|
return NO_POLL_CHAR;
|
||||||
|
kgdbdbgp_buf_sz = ret;
|
||||||
|
kgdbdbgp_buf_idx = 1;
|
||||||
|
return kgdbdbgp_buf[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kgdbdbgp_write_char(u8 chr)
|
||||||
|
{
|
||||||
|
early_dbgp_write(NULL, &chr, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct kgdb_io kgdbdbgp_io_ops = {
|
||||||
|
.name = "kgdbdbgp",
|
||||||
|
.read_char = kgdbdbgp_read_char,
|
||||||
|
.write_char = kgdbdbgp_write_char,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int kgdbdbgp_wait_time;
|
||||||
|
|
||||||
|
static int __init kgdbdbgp_parse_config(char *str)
|
||||||
|
{
|
||||||
|
char *ptr;
|
||||||
|
|
||||||
|
if (!ehci_debug) {
|
||||||
|
if (early_dbgp_init(str))
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ptr = strchr(str, ',');
|
||||||
|
if (ptr) {
|
||||||
|
ptr++;
|
||||||
|
kgdbdbgp_wait_time = simple_strtoul(ptr, &ptr, 10);
|
||||||
|
}
|
||||||
|
kgdb_register_io_module(&kgdbdbgp_io_ops);
|
||||||
|
kgdbdbgp_io_ops.is_console = early_dbgp_console.index != -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
early_param("kgdbdbgp", kgdbdbgp_parse_config);
|
||||||
|
|
||||||
|
static int kgdbdbgp_reader_thread(void *ptr)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
while (readl(&ehci_debug->control) & DBGP_ENABLED) {
|
||||||
|
kgdbdbgp_loop_cnt = 1;
|
||||||
|
ret = kgdbdbgp_read_char();
|
||||||
|
kgdbdbgp_loop_cnt = DBGP_LOOPS;
|
||||||
|
if (ret != NO_POLL_CHAR) {
|
||||||
|
if (ret == 0x3 || ret == '$') {
|
||||||
|
if (ret == '$')
|
||||||
|
kgdbdbgp_buf_idx--;
|
||||||
|
kgdb_breakpoint();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
schedule_timeout_interruptible(kgdbdbgp_wait_time * HZ);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __init kgdbdbgp_start_thread(void)
|
||||||
|
{
|
||||||
|
if (dbgp_kgdb_mode && kgdbdbgp_wait_time)
|
||||||
|
kthread_run(kgdbdbgp_reader_thread, NULL, "%s", "dbgp");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
module_init(kgdbdbgp_start_thread);
|
||||||
|
#endif /* CONFIG_KGDB */
|
||||||
|
@@ -207,6 +207,17 @@ extern int kgdb_validate_break_address(unsigned long addr);
|
|||||||
extern int kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr);
|
extern int kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr);
|
||||||
extern int kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle);
|
extern int kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kgdb_arch_late - Perform any architecture specific initalization.
|
||||||
|
*
|
||||||
|
* This function will handle the late initalization of any
|
||||||
|
* architecture specific callbacks. This is an optional function for
|
||||||
|
* handling things like late initialization of hw breakpoints. The
|
||||||
|
* default implementation does nothing.
|
||||||
|
*/
|
||||||
|
extern void kgdb_arch_late(void);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct kgdb_arch - Describe architecture specific values.
|
* struct kgdb_arch - Describe architecture specific values.
|
||||||
* @gdb_bpt_instr: The instruction to trigger a breakpoint.
|
* @gdb_bpt_instr: The instruction to trigger a breakpoint.
|
||||||
@@ -285,7 +296,10 @@ extern int kgdb_single_step;
|
|||||||
extern atomic_t kgdb_active;
|
extern atomic_t kgdb_active;
|
||||||
#define in_dbg_master() \
|
#define in_dbg_master() \
|
||||||
(raw_smp_processor_id() == atomic_read(&kgdb_active))
|
(raw_smp_processor_id() == atomic_read(&kgdb_active))
|
||||||
|
extern bool dbg_is_early;
|
||||||
|
extern void __init dbg_late_init(void);
|
||||||
#else /* ! CONFIG_KGDB */
|
#else /* ! CONFIG_KGDB */
|
||||||
#define in_dbg_master() (0)
|
#define in_dbg_master() (0)
|
||||||
|
#define dbg_late_init()
|
||||||
#endif /* ! CONFIG_KGDB */
|
#endif /* ! CONFIG_KGDB */
|
||||||
#endif /* _KGDB_H_ */
|
#endif /* _KGDB_H_ */
|
||||||
|
@@ -62,7 +62,7 @@
|
|||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/signal.h>
|
#include <linux/signal.h>
|
||||||
#include <linux/idr.h>
|
#include <linux/idr.h>
|
||||||
#include <linux/kdb.h>
|
#include <linux/kgdb.h>
|
||||||
#include <linux/ftrace.h>
|
#include <linux/ftrace.h>
|
||||||
#include <linux/async.h>
|
#include <linux/async.h>
|
||||||
#include <linux/kmemcheck.h>
|
#include <linux/kmemcheck.h>
|
||||||
@@ -676,7 +676,7 @@ asmlinkage void __init start_kernel(void)
|
|||||||
buffer_init();
|
buffer_init();
|
||||||
key_init();
|
key_init();
|
||||||
security_init();
|
security_init();
|
||||||
kdb_init(KDB_INIT_FULL);
|
dbg_late_init();
|
||||||
vfs_caches_init(totalram_pages);
|
vfs_caches_init(totalram_pages);
|
||||||
signals_init();
|
signals_init();
|
||||||
/* rootfs populating might need page-writeback */
|
/* rootfs populating might need page-writeback */
|
||||||
|
@@ -78,6 +78,8 @@ static DEFINE_SPINLOCK(kgdb_registration_lock);
|
|||||||
static int kgdb_con_registered;
|
static int kgdb_con_registered;
|
||||||
/* determine if kgdb console output should be used */
|
/* determine if kgdb console output should be used */
|
||||||
static int kgdb_use_con;
|
static int kgdb_use_con;
|
||||||
|
/* Flag for alternate operations for early debugging */
|
||||||
|
bool dbg_is_early = true;
|
||||||
/* Next cpu to become the master debug core */
|
/* Next cpu to become the master debug core */
|
||||||
int dbg_switch_cpu;
|
int dbg_switch_cpu;
|
||||||
|
|
||||||
@@ -777,11 +779,25 @@ static struct notifier_block kgdb_panic_event_nb = {
|
|||||||
.priority = INT_MAX,
|
.priority = INT_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void __weak kgdb_arch_late(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void __init dbg_late_init(void)
|
||||||
|
{
|
||||||
|
dbg_is_early = false;
|
||||||
|
if (kgdb_io_module_registered)
|
||||||
|
kgdb_arch_late();
|
||||||
|
kdb_init(KDB_INIT_FULL);
|
||||||
|
}
|
||||||
|
|
||||||
static void kgdb_register_callbacks(void)
|
static void kgdb_register_callbacks(void)
|
||||||
{
|
{
|
||||||
if (!kgdb_io_module_registered) {
|
if (!kgdb_io_module_registered) {
|
||||||
kgdb_io_module_registered = 1;
|
kgdb_io_module_registered = 1;
|
||||||
kgdb_arch_init();
|
kgdb_arch_init();
|
||||||
|
if (!dbg_is_early)
|
||||||
|
kgdb_arch_late();
|
||||||
atomic_notifier_chain_register(&panic_notifier_list,
|
atomic_notifier_chain_register(&panic_notifier_list,
|
||||||
&kgdb_panic_event_nb);
|
&kgdb_panic_event_nb);
|
||||||
#ifdef CONFIG_MAGIC_SYSRQ
|
#ifdef CONFIG_MAGIC_SYSRQ
|
||||||
|
Reference in New Issue
Block a user