📄 kgdb.c
字号:
} if (!kgdb_hex2long(&ptr, &addr)) { error_packet(remcom_out_buffer, -EINVAL); return; } if (*(ptr++) != ',' || !kgdb_hex2long(&ptr, &length)) { error_packet(remcom_out_buffer, -EINVAL); return; } if (remcom_in_buffer[0] == 'Z' && *bpt_type == '0') error = kgdb_set_sw_break(addr); else if (remcom_in_buffer[0] == 'z' && *bpt_type == '0') error = kgdb_remove_sw_break(addr); else if (remcom_in_buffer[0] == 'Z') error = arch_kgdb_ops.set_hw_breakpoint(addr, (int)length, *bpt_type - '0'); else if (remcom_in_buffer[0] == 'z') error = arch_kgdb_ops.remove_hw_breakpoint(addr, (int) length, *bpt_type - '0'); if (error == 0) strcpy(remcom_out_buffer, "OK"); else error_packet(remcom_out_buffer, error);}/* Handle the 'C' signal / exception passing packets */static int gdb_cmd_exception_pass(struct kgdb_state *ks){ /* C09 == pass exception * C15 == detach kgdb, pass exception */ if (remcom_in_buffer[1] == '0' && remcom_in_buffer[2] == '9') { ks->pass_exception = 1; remcom_in_buffer[0] = 'c'; } else if (remcom_in_buffer[1] == '1' && remcom_in_buffer[2] == '5') { ks->pass_exception = 1; remcom_in_buffer[0] = 'D'; remove_all_break(); kgdb_connected = 0; return 1; } else { error_packet(remcom_out_buffer, -EINVAL); return 0; } /* Indicate fall through */ return -1;}/* * This function performs all gdbserial command procesing */static int gdb_serial_stub(struct kgdb_state *ks){ int error = 0; int tmp; /* Clear the out buffer. */ memset(remcom_out_buffer, 0, sizeof(remcom_out_buffer)); if (kgdb_connected) { unsigned char thref[8]; char *ptr; /* Reply to host that an exception has occurred */ ptr = remcom_out_buffer; *ptr++ = 'T'; ptr = pack_hex_byte(ptr, ks->signo); ptr += strlen(strcpy(ptr, "thread:")); int_to_threadref(thref, shadow_pid(current->pid)); ptr = pack_threadid(ptr, thref); *ptr++ = ';'; put_packet(remcom_out_buffer); } kgdb_usethread = kgdb_info[ks->cpu].task; ks->kgdb_usethreadid = shadow_pid(kgdb_info[ks->cpu].task->pid); ks->pass_exception = 0; while (1) { error = 0; /* Clear the out buffer. */ memset(remcom_out_buffer, 0, sizeof(remcom_out_buffer)); get_packet(remcom_in_buffer); switch (remcom_in_buffer[0]) { case '?': /* gdbserial status */ gdb_cmd_status(ks); break; case 'g': /* return the value of the CPU registers */ gdb_cmd_getregs(ks); break; case 'G': /* set the value of the CPU registers - return OK */ gdb_cmd_setregs(ks); break; case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ gdb_cmd_memread(ks); break; case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA..AA */ gdb_cmd_memwrite(ks); break; case 'X': /* XAA..AA,LLLL: Write LLLL bytes at address AA..AA */ gdb_cmd_binwrite(ks); break; /* kill or detach. KGDB should treat this like a * continue. */ case 'D': /* Debugger detach */ case 'k': /* Debugger detach via kill */ gdb_cmd_detachkill(ks); goto default_handle; case 'R': /* Reboot */ if (gdb_cmd_reboot(ks)) goto default_handle; break; case 'q': /* query command */ gdb_cmd_query(ks); break; case 'H': /* task related */ gdb_cmd_task(ks); break; case 'T': /* Query thread status */ gdb_cmd_thread(ks); break; case 'z': /* Break point remove */ case 'Z': /* Break point set */ gdb_cmd_break(ks); break; case 'C': /* Exception passing */ tmp = gdb_cmd_exception_pass(ks); if (tmp > 0) goto default_handle; if (tmp == 0) break; /* Fall through on tmp < 0 */ case 'c': /* Continue packet */ case 's': /* Single step packet */ if (kgdb_contthread && kgdb_contthread != current) { /* Can't switch threads in kgdb */ error_packet(remcom_out_buffer, -EINVAL); break; } kgdb_activate_sw_breakpoints(); /* Fall through to default processing */ default:default_handle: error = kgdb_arch_handle_exception(ks->ex_vector, ks->signo, ks->err_code, remcom_in_buffer, remcom_out_buffer, ks->linux_regs); /* * Leave cmd processing on error, detach, * kill, continue, or single step. */ if (error >= 0 || remcom_in_buffer[0] == 'D' || remcom_in_buffer[0] == 'k') { error = 0; goto kgdb_exit; } } /* reply to the request */ put_packet(remcom_out_buffer); }kgdb_exit: if (ks->pass_exception) error = 1; return error;}static int kgdb_reenter_check(struct kgdb_state *ks){ unsigned long addr; if (atomic_read(&kgdb_active) != raw_smp_processor_id()) return 0; /* Panic on recursive debugger calls: */ exception_level++; addr = kgdb_arch_pc(ks->ex_vector, ks->linux_regs); kgdb_deactivate_sw_breakpoints(); /* * If the break point removed ok at the place exception * occurred, try to recover and print a warning to the end * user because the user planted a breakpoint in a place that * KGDB needs in order to function. */ if (kgdb_remove_sw_break(addr) == 0) { exception_level = 0; kgdb_skipexception(ks->ex_vector, ks->linux_regs); kgdb_activate_sw_breakpoints(); printk(KERN_CRIT "KGDB: re-enter error: breakpoint removed %lx\n", addr); WARN_ON_ONCE(1); return 1; } remove_all_break(); kgdb_skipexception(ks->ex_vector, ks->linux_regs); if (exception_level > 1) { dump_stack(); panic("Recursive entry to debugger"); } printk(KERN_CRIT "KGDB: re-enter exception: ALL breakpoints killed\n"); dump_stack(); panic("Recursive entry to debugger"); return 1;}/* * kgdb_handle_exception() - main entry point from a kernel exception * * Locking hierarchy: * interface locks, if any (begin_session) * kgdb lock (kgdb_active) */intkgdb_handle_exception(int evector, int signo, int ecode, struct pt_regs *regs){ struct kgdb_state kgdb_var; struct kgdb_state *ks = &kgdb_var; unsigned long flags; int error = 0; int i, cpu; ks->cpu = raw_smp_processor_id(); ks->ex_vector = evector; ks->signo = signo; ks->ex_vector = evector; ks->err_code = ecode; ks->kgdb_usethreadid = 0; ks->linux_regs = regs; if (kgdb_reenter_check(ks)) return 0; /* Ouch, double exception ! */acquirelock: /* * Interrupts will be restored by the 'trap return' code, except when * single stepping. */ local_irq_save(flags); cpu = raw_smp_processor_id(); /* * Acquire the kgdb_active lock: */ while (atomic_cmpxchg(&kgdb_active, -1, cpu) != -1) cpu_relax(); /* * Do not start the debugger connection on this CPU if the last * instance of the exception handler wanted to come into the * debugger on a different CPU via a single step */ if (atomic_read(&kgdb_cpu_doing_single_step) != -1 && atomic_read(&kgdb_cpu_doing_single_step) != cpu) { atomic_set(&kgdb_active, -1); touch_softlockup_watchdog(); clocksource_touch_watchdog(); local_irq_restore(flags); goto acquirelock; } if (!kgdb_io_ready(1)) { error = 1; goto kgdb_restore; /* No I/O connection, so resume the system */ } /* * Don't enter if we have hit a removed breakpoint. */ if (kgdb_skipexception(ks->ex_vector, ks->linux_regs)) goto kgdb_restore; /* Call the I/O driver's pre_exception routine */ if (kgdb_io_ops->pre_exception) kgdb_io_ops->pre_exception(); kgdb_info[ks->cpu].debuggerinfo = ks->linux_regs; kgdb_info[ks->cpu].task = current; kgdb_disable_hw_debug(ks->linux_regs); /* * Get the passive CPU lock which will hold all the non-primary * CPU in a spin state while the debugger is active */ if (!kgdb_single_step) { for (i = 0; i < NR_CPUS; i++) atomic_set(&passive_cpu_wait[i], 1); } /* * spin_lock code is good enough as a barrier so we don't * need one here: */ atomic_set(&cpu_in_kgdb[ks->cpu], 1);#ifdef CONFIG_SMP /* Signal the other CPUs to enter kgdb_wait() */ if ((!kgdb_single_step) && kgdb_do_roundup) kgdb_roundup_cpus(flags);#endif /* * 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])) cpu_relax(); } /* * At this point the primary processor is completely * 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(); kgdb_single_step = 0; kgdb_contthread = current; exception_level = 0; /* Talk to debugger with gdbserial protocol */ error = gdb_serial_stub(ks); /* Call the I/O driver's post_exception routine */ if (kgdb_io_ops->post_exception) kgdb_io_ops->post_exception(); kgdb_info[ks->cpu].debuggerinfo = NULL; kgdb_info[ks->cpu].task = NULL; atomic_set(&cpu_in_kgdb[ks->cpu], 0); if (!kgdb_single_step) { for (i = NR_CPUS-1; i >= 0; i--) atomic_set(&passive_cpu_wait[i], 0); /* * Wait till all the CPUs have quit * from the debugger. */ for_each_online_cpu(i) { while (atomic_read(&cpu_in_kgdb[i])) cpu_relax(); } }kgdb_restore: /* Free kgdb_active */ atomic_set(&kgdb_active, -1); touch_softlockup_watchdog(); clocksource_touch_watchdog(); local_irq_restore(flags); return error;}int kgdb_nmicallback(int cpu, void *regs){#ifdef CONFIG_SMP if (!atomic_read(&cpu_in_kgdb[cpu]) && atomic_read(&kgdb_active) != cpu && atomic_read(&cpu_in_kgdb[atomic_read(&kgdb_active)])) { kgdb_wait((struct pt_regs *)regs); return 0; }#endif return 1;}static void kgdb_console_write(struct console *co, const char *s, unsigned count){ unsigned long flags; /* If we're debugging, or KGDB has not connected, don't try * and print. */ if (!kgdb_connected || atomic_read(&kgdb_active) != -1) return; local_irq_save(flags); kgdb_msg_write(s, count); local_irq_restore(flags);}static struct console kgdbcons = { .name = "kgdb", .write = kgdb_console_write, .flags = CON_PRINTBUFFER | CON_ENABLED, .index = -1,};#ifdef CONFIG_MAGIC_SYSRQstatic void sysrq_handle_gdb(int key, struct tty_struct *tty){ if (!kgdb_io_ops) { printk(KERN_CRIT "ERROR: No KGDB I/O module available\n"); return; } if (!kgdb_connected) printk(KERN_CRIT "Entering KGDB\n"); kgdb_breakpoint();}static struct sysrq_key_op sysrq_gdb_op = { .handler = sysrq_handle_gdb, .help_msg = "Gdb", .action_msg = "GDB",};#endifstatic void kgdb_register_callbacks(void){ if (!kgdb_io_module_registered) { kgdb_io_module_registered = 1; kgdb_arch_init();#ifdef CONFIG_MAGIC_SYSRQ register_sysrq_key('g', &sysrq_gdb_op);#endif if (kgdb_use_con && !kgdb_con_registered) { register_console(&kgdbcons); kgdb_con_registered = 1; } }}static void kgdb_unregister_callbacks(void){ /* * When this routine is called KGDB should unregister from the * panic handler and clean up, making sure it is not handling any * break exceptions at the time. */ if (kgdb_io_module_registered) { kgdb_io_module_registered = 0; kgdb_arch_exit();#ifdef CONFIG_MAGIC_SYSRQ unregister_sysrq_key('g', &sysrq_gdb_op);#endif if (kgdb_con_registered) { unregister_console(&kgdbcons); kgdb_con_registered = 0; } }}static void kgdb_initial_breakpoint(void){ kgdb_break_asap = 0; printk(KERN_CRIT "kgdb: Waiting for connection from remote gdb...\n"); kgdb_breakpoint();}/** * kgdb_register_io_module - register KGDB IO module * @new_kgdb_io_ops: the io ops vector * * Register it with the KGDB core. */int kgdb_register_io_module(struct kgdb_io *new_kgdb_io_ops){ int err; spin_lock(&kgdb_registration_lock); if (kgdb_io_ops) { spin_unlock(&kgdb_registration_lock); printk(KERN_ERR "kgdb: Another I/O driver is already " "registered with KGDB.\n"); return -EBUSY; } if (new_kgdb_io_ops->init) { err = new_kgdb_io_ops->init(); if (err) { spin_unlock(&kgdb_registration_lock); return err; } } kgdb_io_ops = new_kgdb_io_ops; spin_unlock(&kgdb_registration_lock); printk(KERN_INFO "kgdb: Registered I/O driver %s.\n", new_kgdb_io_ops->name); /* Arm KGDB now. */ kgdb_register_callbacks(); if (kgdb_break_asap) kgdb_initial_breakpoint(); return 0;}EXPORT_SYMBOL_GPL(kgdb_register_io_module);/** * kkgdb_unregister_io_module - unregister KGDB IO module * @old_kgdb_io_ops: the io ops vector * * Unregister it with the KGDB core. */void kgdb_unregister_io_module(struct kgdb_io *old_kgdb_io_ops){ BUG_ON(kgdb_connected); /* * KGDB is no longer able to communicate out, so * unregister our callbacks and reset state. */ kgdb_unregister_callbacks(); spin_lock(&kgdb_registration_lock); WARN_ON_ONCE(kgdb_io_ops != old_kgdb_io_ops); kgdb_io_ops = NULL; spin_unlock(&kgdb_registration_lock); printk(KERN_INFO "kgdb: Unregistered I/O driver %s, debugger disabled.\n", old_kgdb_io_ops->name);}EXPORT_SYMBOL_GPL(kgdb_unregister_io_module);/** * kgdb_breakpoint - generate breakpoint exception * * This function will generate a breakpoint exception. It is used at the * beginning of a program to sync up with a debugger and can be used * otherwise as a quick means to stop program execution and "break" into * the debugger. */void kgdb_breakpoint(void){ atomic_set(&kgdb_setting_breakpoint, 1); wmb(); /* Sync point before breakpoint */ arch_kgdb_breakpoint(); wmb(); /* Sync point after breakpoint */ atomic_set(&kgdb_setting_breakpoint, 0);}EXPORT_SYMBOL_GPL(kgdb_breakpoint);static int __init opt_kgdb_wait(char *str){ kgdb_break_asap = 1; if (kgdb_io_module_registered) kgdb_initial_breakpoint(); return 0;}early_param("kgdbwait", opt_kgdb_wait);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -