📄 kgdb.c
字号:
/* Wait till primary CPU is done with debugging */ while (atomic_read(&passive_cpu_wait[cpu])) cpu_relax(); kgdb_info[cpu].debuggerinfo = NULL; kgdb_info[cpu].task = NULL; /* fix up hardware debug registers on local cpu */ if (arch_kgdb_ops.correct_hw_break) arch_kgdb_ops.correct_hw_break(); /* Signal the primary CPU that we are done: */ atomic_set(&cpu_in_kgdb[cpu], 0); touch_softlockup_watchdog(); clocksource_touch_watchdog(); local_irq_restore(flags);}#endif/* * Some architectures need cache flushes when we set/clear a * breakpoint: */static void kgdb_flush_swbreak_addr(unsigned long addr){ if (!CACHE_FLUSH_IS_SAFE) return; if (current->mm && current->mm->mmap_cache) { flush_cache_range(current->mm->mmap_cache, addr, addr + BREAK_INSTR_SIZE); } /* Force flush instruction cache if it was outside the mm */ flush_icache_range(addr, addr + BREAK_INSTR_SIZE);}/* * SW breakpoint management: */static int kgdb_activate_sw_breakpoints(void){ unsigned long addr; int error = 0; int i; for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) { if (kgdb_break[i].state != BP_SET) continue; addr = kgdb_break[i].bpt_addr; error = kgdb_arch_set_breakpoint(addr, kgdb_break[i].saved_instr); if (error) return error; kgdb_flush_swbreak_addr(addr); kgdb_break[i].state = BP_ACTIVE; } return 0;}static int kgdb_set_sw_break(unsigned long addr){ int err = kgdb_validate_break_address(addr); int breakno = -1; int i; if (err) return err; for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) { if ((kgdb_break[i].state == BP_SET) && (kgdb_break[i].bpt_addr == addr)) return -EEXIST; } for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) { if (kgdb_break[i].state == BP_REMOVED && kgdb_break[i].bpt_addr == addr) { breakno = i; break; } } if (breakno == -1) { for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) { if (kgdb_break[i].state == BP_UNDEFINED) { breakno = i; break; } } } if (breakno == -1) return -E2BIG; kgdb_break[breakno].state = BP_SET; kgdb_break[breakno].type = BP_BREAKPOINT; kgdb_break[breakno].bpt_addr = addr; return 0;}static int kgdb_deactivate_sw_breakpoints(void){ unsigned long addr; int error = 0; int i; for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) { if (kgdb_break[i].state != BP_ACTIVE) continue; addr = kgdb_break[i].bpt_addr; error = kgdb_arch_remove_breakpoint(addr, kgdb_break[i].saved_instr); if (error) return error; kgdb_flush_swbreak_addr(addr); kgdb_break[i].state = BP_SET; } return 0;}static int kgdb_remove_sw_break(unsigned long addr){ int i; for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) { if ((kgdb_break[i].state == BP_SET) && (kgdb_break[i].bpt_addr == addr)) { kgdb_break[i].state = BP_REMOVED; return 0; } } return -ENOENT;}int kgdb_isremovedbreak(unsigned long addr){ int i; for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) { if ((kgdb_break[i].state == BP_REMOVED) && (kgdb_break[i].bpt_addr == addr)) return 1; } return 0;}static int remove_all_break(void){ unsigned long addr; int error; int i; /* Clear memory breakpoints. */ for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) { if (kgdb_break[i].state != BP_ACTIVE) goto setundefined; addr = kgdb_break[i].bpt_addr; error = kgdb_arch_remove_breakpoint(addr, kgdb_break[i].saved_instr); if (error) printk(KERN_ERR "KGDB: breakpoint remove failed: %lx\n", addr);setundefined: kgdb_break[i].state = BP_UNDEFINED; } /* Clear hardware breakpoints. */ if (arch_kgdb_ops.remove_all_hw_break) arch_kgdb_ops.remove_all_hw_break(); return 0;}/* * Remap normal tasks to their real PID, * CPU shadow threads are mapped to -CPU - 2 */static inline int shadow_pid(int realpid){ if (realpid) return realpid; return -raw_smp_processor_id() - 2;}static char gdbmsgbuf[BUFMAX + 1];static void kgdb_msg_write(const char *s, int len){ char *bufptr; int wcount; int i; /* 'O'utput */ gdbmsgbuf[0] = 'O'; /* Fill and send buffers... */ while (len > 0) { bufptr = gdbmsgbuf + 1; /* Calculate how many this time */ if ((len << 1) > (BUFMAX - 2)) wcount = (BUFMAX - 2) >> 1; else wcount = len; /* Pack in hex chars */ for (i = 0; i < wcount; i++) bufptr = pack_hex_byte(bufptr, s[i]); *bufptr = '\0'; /* Move up */ s += wcount; len -= wcount; /* Write packet */ put_packet(gdbmsgbuf); }}/* * Return true if there is a valid kgdb I/O module. Also if no * debugger is attached a message can be printed to the console about * waiting for the debugger to attach. * * The print_wait argument is only to be true when called from inside * the core kgdb_handle_exception, because it will wait for the * debugger to attach. */static int kgdb_io_ready(int print_wait){ if (!kgdb_io_ops) return 0; if (kgdb_connected) return 1; if (atomic_read(&kgdb_setting_breakpoint)) return 1; if (print_wait) printk(KERN_CRIT "KGDB: Waiting for remote debugger\n"); return 1;}/* * All the functions that start with gdb_cmd are the various * operations to implement the handlers for the gdbserial protocol * where KGDB is communicating with an external debugger *//* Handle the '?' status packets */static void gdb_cmd_status(struct kgdb_state *ks){ /* * We know that this packet is only sent * during initial connect. So to be safe, * we clear out our breakpoints now in case * GDB is reconnecting. */ remove_all_break(); remcom_out_buffer[0] = 'S'; pack_hex_byte(&remcom_out_buffer[1], ks->signo);}/* Handle the 'g' get registers request */static void gdb_cmd_getregs(struct kgdb_state *ks){ struct task_struct *thread; void *local_debuggerinfo; int i; thread = kgdb_usethread; if (!thread) { thread = kgdb_info[ks->cpu].task; local_debuggerinfo = kgdb_info[ks->cpu].debuggerinfo; } else { local_debuggerinfo = NULL; for_each_online_cpu(i) { /* * Try to find the task on some other * or possibly this node if we do not * find the matching task then we try * to approximate the results. */ if (thread == kgdb_info[i].task) local_debuggerinfo = kgdb_info[i].debuggerinfo; } } /* * All threads that don't have debuggerinfo should be * in __schedule() sleeping, since all other CPUs * are in kgdb_wait, and thus have debuggerinfo. */ if (local_debuggerinfo) { pt_regs_to_gdb_regs(gdb_regs, local_debuggerinfo); } else { /* * Pull stuff saved during switch_to; nothing * else is accessible (or even particularly * relevant). * * This should be enough for a stack trace. */ sleeping_thread_to_gdb_regs(gdb_regs, thread); } kgdb_mem2hex((char *)gdb_regs, remcom_out_buffer, NUMREGBYTES);}/* Handle the 'G' set registers request */static void gdb_cmd_setregs(struct kgdb_state *ks){ kgdb_hex2mem(&remcom_in_buffer[1], (char *)gdb_regs, NUMREGBYTES); if (kgdb_usethread && kgdb_usethread != current) { error_packet(remcom_out_buffer, -EINVAL); } else { gdb_regs_to_pt_regs(gdb_regs, ks->linux_regs); strcpy(remcom_out_buffer, "OK"); }}/* Handle the 'm' memory read bytes */static void gdb_cmd_memread(struct kgdb_state *ks){ char *ptr = &remcom_in_buffer[1]; unsigned long length; unsigned long addr; int err; if (kgdb_hex2long(&ptr, &addr) > 0 && *ptr++ == ',' && kgdb_hex2long(&ptr, &length) > 0) { err = kgdb_mem2hex((char *)addr, remcom_out_buffer, length); if (err) error_packet(remcom_out_buffer, err); } else { error_packet(remcom_out_buffer, -EINVAL); }}/* Handle the 'M' memory write bytes */static void gdb_cmd_memwrite(struct kgdb_state *ks){ int err = write_mem_msg(0); if (err) error_packet(remcom_out_buffer, err); else strcpy(remcom_out_buffer, "OK");}/* Handle the 'X' memory binary write bytes */static void gdb_cmd_binwrite(struct kgdb_state *ks){ int err = write_mem_msg(1); if (err) error_packet(remcom_out_buffer, err); else strcpy(remcom_out_buffer, "OK");}/* Handle the 'D' or 'k', detach or kill packets */static void gdb_cmd_detachkill(struct kgdb_state *ks){ int error; /* The detach case */ if (remcom_in_buffer[0] == 'D') { error = remove_all_break(); if (error < 0) { error_packet(remcom_out_buffer, error); } else { strcpy(remcom_out_buffer, "OK"); kgdb_connected = 0; } put_packet(remcom_out_buffer); } else { /* * Assume the kill case, with no exit code checking, * trying to force detach the debugger: */ remove_all_break(); kgdb_connected = 0; }}/* Handle the 'R' reboot packets */static int gdb_cmd_reboot(struct kgdb_state *ks){ /* For now, only honor R0 */ if (strcmp(remcom_in_buffer, "R0") == 0) { printk(KERN_CRIT "Executing emergency reboot\n"); strcpy(remcom_out_buffer, "OK"); put_packet(remcom_out_buffer); /* * Execution should not return from * machine_emergency_restart() */ machine_emergency_restart(); kgdb_connected = 0; return 1; } return 0;}/* Handle the 'q' query packets */static void gdb_cmd_query(struct kgdb_state *ks){ struct task_struct *g; struct task_struct *p; unsigned char thref[8]; char *ptr; int i; int cpu; int finished = 0; switch (remcom_in_buffer[1]) { case 's': case 'f': if (memcmp(remcom_in_buffer + 2, "ThreadInfo", 10)) { error_packet(remcom_out_buffer, -EINVAL); break; } i = 0; remcom_out_buffer[0] = 'm'; ptr = remcom_out_buffer + 1; if (remcom_in_buffer[1] == 'f') { /* Each cpu is a shadow thread */ for_each_online_cpu(cpu) { ks->thr_query = 0; int_to_threadref(thref, -cpu - 2); pack_threadid(ptr, thref); ptr += BUF_THREAD_ID_SIZE; *(ptr++) = ','; i++; } } do_each_thread(g, p) { if (i >= ks->thr_query && !finished) { int_to_threadref(thref, p->pid); pack_threadid(ptr, thref); ptr += BUF_THREAD_ID_SIZE; *(ptr++) = ','; ks->thr_query++; if (ks->thr_query % KGDB_MAX_THREAD_QUERY == 0) finished = 1; } i++; } while_each_thread(g, p); *(--ptr) = '\0'; break; case 'C': /* Current thread id */ strcpy(remcom_out_buffer, "QC"); ks->threadid = shadow_pid(current->pid); int_to_threadref(thref, ks->threadid); pack_threadid(remcom_out_buffer + 2, thref); break; case 'T': if (memcmp(remcom_in_buffer + 1, "ThreadExtraInfo,", 16)) { error_packet(remcom_out_buffer, -EINVAL); break; } ks->threadid = 0; ptr = remcom_in_buffer + 17; kgdb_hex2long(&ptr, &ks->threadid); if (!getthread(ks->linux_regs, ks->threadid)) { error_packet(remcom_out_buffer, -EINVAL); break; } if ((int)ks->threadid > 0) { kgdb_mem2hex(getthread(ks->linux_regs, ks->threadid)->comm, remcom_out_buffer, 16); } else { static char tmpstr[23 + BUF_THREAD_ID_SIZE]; sprintf(tmpstr, "shadowCPU%d", (int)(-ks->threadid - 2)); kgdb_mem2hex(tmpstr, remcom_out_buffer, strlen(tmpstr)); } break; }}/* Handle the 'H' task query packets */static void gdb_cmd_task(struct kgdb_state *ks){ struct task_struct *thread; char *ptr; switch (remcom_in_buffer[1]) { case 'g': ptr = &remcom_in_buffer[2]; kgdb_hex2long(&ptr, &ks->threadid); thread = getthread(ks->linux_regs, ks->threadid); if (!thread && ks->threadid > 0) { error_packet(remcom_out_buffer, -EINVAL); break; } kgdb_usethread = thread; ks->kgdb_usethreadid = ks->threadid; strcpy(remcom_out_buffer, "OK"); break; case 'c': ptr = &remcom_in_buffer[2]; kgdb_hex2long(&ptr, &ks->threadid); if (!ks->threadid) { kgdb_contthread = NULL; } else { thread = getthread(ks->linux_regs, ks->threadid); if (!thread && ks->threadid > 0) { error_packet(remcom_out_buffer, -EINVAL); break; } kgdb_contthread = thread; } strcpy(remcom_out_buffer, "OK"); break; }}/* Handle the 'T' thread query packets */static void gdb_cmd_thread(struct kgdb_state *ks){ char *ptr = &remcom_in_buffer[1]; struct task_struct *thread; kgdb_hex2long(&ptr, &ks->threadid); thread = getthread(ks->linux_regs, ks->threadid); if (thread) strcpy(remcom_out_buffer, "OK"); else error_packet(remcom_out_buffer, -EINVAL);}/* Handle the 'z' or 'Z' breakpoint remove or set packets */static void gdb_cmd_break(struct kgdb_state *ks){ /* * Since GDB-5.3, it's been drafted that '0' is a software * breakpoint, '1' is a hardware breakpoint, so let's do that. */ char *bpt_type = &remcom_in_buffer[1]; char *ptr = &remcom_in_buffer[2]; unsigned long addr; unsigned long length; int error = 0; if (arch_kgdb_ops.set_hw_breakpoint && *bpt_type >= '1') { /* Unsupported */ if (*bpt_type > '4') return; } else { if (*bpt_type != '0' && *bpt_type != '1') /* Unsupported. */ return; } /* * Test if this is a hardware breakpoint, and * if we support it: */ if (*bpt_type == '1' && !(arch_kgdb_ops.flags & KGDB_HW_BREAKPOINT)) /* Unsupported. */ return; if (*(ptr++) != ',') { error_packet(remcom_out_buffer, -EINVAL); return;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -