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:
Linus Torvalds 2010-05-21 11:10:41 -07:00
Родитель 90b9a32d8f 4fe1da4ebc
Коммит ac3ee84c60
12 изменённых файлов: 255 добавлений и 36 удалений

Просмотреть файл

@ -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
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]
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
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.
Requires a tty driver that supports console polling,
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 int sysenter_setup(void);
extern void early_trap_init(void);
/* Defined in head.S */
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
* initialized (naturally) in the bootstrap process, such as the GDT
@ -1174,18 +1188,8 @@ void __cpuinit cpu_init(void)
load_TR_desc();
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();
@ -1239,6 +1243,7 @@ void __cpuinit cpu_init(void)
#endif
clear_all_debug_regs();
dbg_restore_debug_regs();
/*
* 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));
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') {
current_xpos = 0;
current_ypos++;

Просмотреть файл

@ -199,6 +199,8 @@ static struct hw_breakpoint {
struct perf_event **pev;
} breakinfo[4];
static unsigned long early_dr7;
static void kgdb_correct_hw_break(void)
{
int breakno;
@ -210,6 +212,14 @@ static void kgdb_correct_hw_break(void)
int cpu = raw_smp_processor_id();
if (!breakinfo[breakno].enabled)
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);
info = counter_arch_bp(bp);
if (bp->attr.disabled != 1)
@ -224,7 +234,8 @@ static void kgdb_correct_hw_break(void)
if (!val)
bp->attr.disabled = 0;
}
hw_breakpoint_restore();
if (!dbg_is_early)
hw_breakpoint_restore();
}
static int hw_break_reserve_slot(int breakno)
@ -233,6 +244,9 @@ static int hw_break_reserve_slot(int breakno)
int cnt = 0;
struct perf_event **pevent;
if (dbg_is_early)
return 0;
for_each_online_cpu(cpu) {
cnt++;
pevent = per_cpu_ptr(breakinfo[breakno].pev, cpu);
@ -258,6 +272,9 @@ static int hw_break_release_slot(int breakno)
struct perf_event **pevent;
int cpu;
if (dbg_is_early)
return 0;
for_each_online_cpu(cpu) {
pevent = per_cpu_ptr(breakinfo[breakno].pev, cpu);
if (dbg_release_bp_slot(*pevent))
@ -302,7 +319,11 @@ static void kgdb_remove_all_hw_break(void)
bp = *per_cpu_ptr(breakinfo[i].pev, cpu);
if (bp->attr.disabled == 1)
continue;
arch_uninstall_hw_breakpoint(bp);
if (dbg_is_early)
early_dr7 &= ~encode_dr7(i, breakinfo[i].len,
breakinfo[i].type);
else
arch_uninstall_hw_breakpoint(bp);
bp->attr.disabled = 1;
}
}
@ -379,6 +400,11 @@ void kgdb_disable_hw_debug(struct pt_regs *regs)
for (i = 0; i < 4; i++) {
if (!breakinfo[i].enabled)
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);
if (bp->attr.disabled == 1)
continue;
@ -595,15 +621,16 @@ static struct notifier_block kgdb_notifier = {
* specific callbacks.
*/
int kgdb_arch_init(void)
{
return register_die_notifier(&kgdb_notifier);
}
void kgdb_arch_late(void)
{
int i, cpu;
int ret;
struct perf_event_attr attr;
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
* 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.disabled = 1;
for (i = 0; i < 4; i++) {
if (breakinfo[i].pev)
continue;
breakinfo[i].pev = register_wide_hw_breakpoint(&attr, NULL);
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;
kgdb_arch_exit();
return -1;
return;
}
for_each_online_cpu(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_init();
early_trap_init();
early_cpu_init();
early_ioremap_init();

Просмотреть файл

@ -808,6 +808,16 @@ dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code)
}
#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)
{
int i;
@ -821,10 +831,7 @@ void __init trap_init(void)
#endif
set_intr_gate(0, &divide_error);
set_intr_gate_ist(1, &debug, DEBUG_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 */
set_system_intr_gate(4, &overflow);
set_intr_gate(5, &bounds);
@ -840,7 +847,6 @@ void __init trap_init(void)
set_intr_gate(11, &segment_not_present);
set_intr_gate_ist(12, &stack_segment, STACKFAULT_STACK);
set_intr_gate(13, &general_protection);
set_intr_gate(14, &page_fault);
set_intr_gate(15, &spurious_interrupt_bug);
set_intr_gate(16, &coprocessor_error);
set_intr_gate(17, &alignment_check);

Просмотреть файл

@ -223,6 +223,25 @@ static struct kgdb_io kgdboc_io_ops = {
.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_exit(cleanup_kgdboc);
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/ehci_def.h>
#include <linux/delay.h>
#include <linux/serial_core.h>
#include <linux/kgdb.h>
#include <linux/kthread.h>
#include <asm/io.h>
#include <asm/pci-direct.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 int dbgp_not_safe; /* Cannot use debug device during ehci reset */
static unsigned int dbgp_endpoint_out;
static unsigned int dbgp_endpoint_in;
struct ehci_dev {
u32 bus;
@ -91,6 +95,13 @@ static inline u32 dbgp_len_update(u32 x, u32 len)
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)
*/
@ -182,11 +193,10 @@ static void dbgp_breath(void)
/* 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;
int ret;
int loop = DBGP_LOOPS;
retry:
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);
writel(addr, &ehci_debug->address);
writel(pids, &ehci_debug->pids);
ret = dbgp_wait_until_done(ctrl);
ret = dbgp_wait_until_done(ctrl, DBGP_LOOPS);
return ret;
}
static int dbgp_bulk_read(unsigned devnum, unsigned endpoint, void *data,
int size)
int size, int loops)
{
u32 pids, addr, ctrl;
int ret;
@ -302,7 +312,7 @@ static int dbgp_bulk_read(unsigned devnum, unsigned endpoint, void *data,
writel(addr, &ehci_debug->address);
writel(pids, &ehci_debug->pids);
ret = dbgp_wait_until_done(ctrl);
ret = dbgp_wait_until_done(ctrl, loops);
if (ret < 0)
return ret;
@ -343,12 +353,12 @@ static int dbgp_control_msg(unsigned devnum, int requesttype,
dbgp_set_data(&req, sizeof(req));
writel(addr, &ehci_debug->address);
writel(pids, &ehci_debug->pids);
ret = dbgp_wait_until_done(ctrl);
ret = dbgp_wait_until_done(ctrl, DBGP_LOOPS);
if (ret < 0)
return ret;
/* 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 */
@ -559,6 +569,7 @@ try_again:
goto err;
}
dbgp_endpoint_out = dbgp_desc.bDebugOutEndpoint;
dbgp_endpoint_in = dbgp_desc.bDebugInEndpoint;
/* Move the device to 127 if it isn't already there */
if (devnum != USB_DEBUG_DEVNUM) {
@ -968,8 +979,9 @@ int dbgp_reset_prep(void)
if (!ehci_debug)
return 0;
if (early_dbgp_console.index != -1 &&
!(early_dbgp_console.flags & CON_BOOT))
if ((early_dbgp_console.index != -1 &&
!(early_dbgp_console.flags & CON_BOOT)) ||
dbgp_kgdb_mode)
return 1;
/* This means the console is not initialized, or should get
* shutdown so as to allow for reuse of the usb device, which
@ -982,3 +994,93 @@ int dbgp_reset_prep(void)
return 0;
}
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_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.
* @gdb_bpt_instr: The instruction to trigger a breakpoint.
@ -285,7 +296,10 @@ extern int kgdb_single_step;
extern atomic_t kgdb_active;
#define in_dbg_master() \
(raw_smp_processor_id() == atomic_read(&kgdb_active))
extern bool dbg_is_early;
extern void __init dbg_late_init(void);
#else /* ! CONFIG_KGDB */
#define in_dbg_master() (0)
#define dbg_late_init()
#endif /* ! CONFIG_KGDB */
#endif /* _KGDB_H_ */

Просмотреть файл

@ -62,7 +62,7 @@
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/idr.h>
#include <linux/kdb.h>
#include <linux/kgdb.h>
#include <linux/ftrace.h>
#include <linux/async.h>
#include <linux/kmemcheck.h>
@ -676,7 +676,7 @@ asmlinkage void __init start_kernel(void)
buffer_init();
key_init();
security_init();
kdb_init(KDB_INIT_FULL);
dbg_late_init();
vfs_caches_init(totalram_pages);
signals_init();
/* rootfs populating might need page-writeback */

Просмотреть файл

@ -78,6 +78,8 @@ static DEFINE_SPINLOCK(kgdb_registration_lock);
static int kgdb_con_registered;
/* determine if kgdb console output should be used */
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 */
int dbg_switch_cpu;
@ -777,11 +779,25 @@ static struct notifier_block kgdb_panic_event_nb = {
.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)
{
if (!kgdb_io_module_registered) {
kgdb_io_module_registered = 1;
kgdb_arch_init();
if (!dbg_is_early)
kgdb_arch_late();
atomic_notifier_chain_register(&panic_notifier_list,
&kgdb_panic_event_nb);
#ifdef CONFIG_MAGIC_SYSRQ