/* Storage for the registers, in GDB format. */ staticunsignedlong gdb_regs[(NUMREGBYTES + sizeof(unsignedlong) - 1) / sizeof(unsignedlong)];
/* * GDB remote protocol parser:
*/
#ifdef CONFIG_KGDB_KDB staticint gdbstub_read_wait(void)
{ int ret = -1; int i;
if (unlikely(gdbstub_use_prev_in_buf)) { if (gdbstub_prev_in_buf_pos < gdbstub_use_prev_in_buf) return remcom_in_buffer[gdbstub_prev_in_buf_pos++]; else
gdbstub_use_prev_in_buf = 0;
}
/* poll any additional I/O interfaces that are defined */ while (ret < 0) for (i = 0; kdb_poll_funcs[i] != NULL; i++) {
ret = kdb_poll_funcs[i](); if (ret > 0) break;
} return ret;
} #else staticint gdbstub_read_wait(void)
{ int ret = dbg_io_ops->read_char(); while (ret == NO_POLL_CHAR)
ret = dbg_io_ops->read_char(); return ret;
} #endif /* scan for the sequence $<data>#<checksum> */ staticvoid get_packet(char *buffer)
{ unsignedchar checksum; unsignedchar xmitcsum; int count; char ch;
do { /* * Spin and wait around for the start character, ignore all * other characters:
*/ while ((ch = (gdbstub_read_wait())) != '$') /* nothing */;
kgdb_connected = 1;
checksum = 0;
xmitcsum = -1;
count = 0;
/* * now, read until a # or end of buffer is found:
*/ while (count < (BUFMAX - 1)) {
ch = gdbstub_read_wait(); if (ch == '#') break;
checksum = checksum + ch;
buffer[count] = ch;
count = count + 1;
}
dbg_io_ops->write_char('#');
dbg_io_ops->write_char(hex_asc_hi(checksum));
dbg_io_ops->write_char(hex_asc_lo(checksum)); if (dbg_io_ops->flush)
dbg_io_ops->flush();
/* Now see what we get in reply. */
ch = gdbstub_read_wait();
if (ch == 3)
ch = gdbstub_read_wait();
/* If we get an ACK, we are done. */ if (ch == '+') return;
/* * If we get the start of another packet, this means * that GDB is attempting to reconnect. We will NAK * the packet being sent, and stop trying to send this * packet.
*/ if (ch == '$') {
dbg_io_ops->write_char('-'); if (dbg_io_ops->flush)
dbg_io_ops->flush(); return;
}
}
}
staticchar gdbmsgbuf[BUFMAX + 1];
void gdbstub_msg_write(constchar *s, int len)
{ char *bufptr; int wcount; int i;
if (len == 0)
len = strlen(s);
/* '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 = hex_byte_pack(bufptr, s[i]);
*bufptr = '\0';
/* Move up */
s += wcount;
len -= wcount;
/* Write packet */
put_packet(gdbmsgbuf);
}
}
/* * Convert the memory pointed to by mem into hex, placing result in * buf. Return a pointer to the last char put in buf (null). May * return an error.
*/ char *kgdb_mem2hex(char *mem, char *buf, int count)
{ char *tmp; int err;
/* * We use the upper half of buf as an intermediate buffer for the * raw memory copy. Hex conversion will work against this one.
*/
tmp = buf + count;
/* * Convert the hex array pointed to by buf into binary to be placed in * mem. Return a pointer to the character AFTER the last byte * written. May return an error.
*/ int kgdb_hex2mem(char *buf, char *mem, int count)
{ char *tmp_raw; char *tmp_hex;
/* * We use the upper half of buf as an intermediate buffer for the * raw memory that is converted from hex.
*/
tmp_raw = buf + count * 2;
/* * While we find nice hex chars, build a long_val. * Return number of chars processed.
*/ int kgdb_hex2long(char **ptr, unsignedlong *long_val)
{ int hex_val; int num = 0; int negate = 0;
*long_val = 0;
if (**ptr == '-') {
negate = 1;
(*ptr)++;
} while (**ptr) {
hex_val = hex_to_bin(**ptr); if (hex_val < 0) break;
/* * Copy the binary array pointed to by buf into mem. Fix $, #, and * 0x7d escaped with 0x7d. Return -EFAULT on failure or 0 on success. * The input buf is overwritten with the result to write to mem.
*/ staticint kgdb_ebin2mem(char *buf, char *mem, int count)
{ int size = 0; char *c = buf;
while (count-- > 0) {
c[size] = *buf++; if (c[size] == 0x7d)
c[size] = *buf++ ^ 0x20;
size++;
}
for (i = 0; i < DBG_MAX_REG_NUM; i++) {
dbg_get_reg(i, ptr + idx, regs);
idx += dbg_reg_def[i].size;
}
}
void gdb_regs_to_pt_regs(unsignedlong *gdb_regs, struct pt_regs *regs)
{ int i; int idx = 0; char *ptr = (char *)gdb_regs;
for (i = 0; i < DBG_MAX_REG_NUM; i++) {
dbg_set_reg(i, ptr + idx, regs);
idx += dbg_reg_def[i].size;
}
} #endif/* DBG_MAX_REG_NUM > 0 */
/* Write memory due to an 'M' or 'X' packet. */ staticint write_mem_msg(int binary)
{ char *ptr = &remcom_in_buffer[1]; unsignedlong addr; unsignedlong length; int err;
/* * Thread ID accessors. We represent a flat TID space to GDB, where * the per CPU idle threads (which under Linux all have PID 0) are * remapped to negative TIDs.
*/
limit = id + (BUF_THREAD_ID_SIZE / 2); while (id < limit) { if (!lzero || *id != 0) {
pkt = hex_byte_pack(pkt, *id);
lzero = 0;
}
id++;
}
if (lzero)
pkt = hex_byte_pack(pkt, 0);
return pkt;
}
staticvoid int_to_threadref(unsignedchar *id, int value)
{
put_unaligned_be32(value, id);
}
staticstruct task_struct *getthread(struct pt_regs *regs, int tid)
{ /* * Non-positive TIDs are remapped to the cpu shadow information
*/ if (tid == 0 || tid == -1)
tid = -atomic_read(&kgdb_active) - 2; if (tid < -1 && tid > -NR_CPUS - 2) { if (kgdb_info[-tid - 2].task) return kgdb_info[-tid - 2].task; else return idle_task(-tid - 2);
} if (tid <= 0) {
printk(KERN_ERR "KGDB: Internal thread select error\n");
dump_stack(); return NULL;
}
/* * find_task_by_pid_ns() does not take the tasklist lock anymore * but is nicely RCU locked - hence is a pretty resilient * thing to use:
*/ return find_task_by_pid_ns(tid, &init_pid_ns);
}
/* * Remap normal tasks to their real PID, * CPU shadow threads are mapped to -CPU - 2
*/ staticinlineint shadow_pid(int realpid)
{ if (realpid) return realpid;
return -raw_smp_processor_id() - 2;
}
/* * 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 */ staticvoid 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.
*/
dbg_remove_all_break();
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);
}
}
/* Handle the 'g' get registers request */ staticvoid gdb_cmd_getregs(struct kgdb_state *ks)
{
gdb_get_regs_helper(ks);
kgdb_mem2hex((char *)gdb_regs, remcom_out_buffer, NUMREGBYTES);
}
/* Handle the 'G' set registers request */ staticvoid gdb_cmd_setregs(struct kgdb_state *ks)
{
kgdb_hex2mem(&remcom_in_buffer[1], (char *)gdb_regs, NUMREGBYTES);
/* Handle the 'z' or 'Z' breakpoint remove or set packets */ staticvoid 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]; unsignedlong addr; unsignedlong length; int error = 0;
/* * 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;
} 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;
}
} else {
gdbstub_msg_write("KGDB only knows signal 9 (pass)" " and 15 (pass and disconnect)\n" "Executing a continue without signal passing\n", 0);
remcom_in_buffer[0] = 'c';
}
/* Indicate fall through */ return -1;
}
/* * This function performs all gdbserial command processing
*/ int gdb_serial_stub(struct kgdb_state *ks)
{ int error = 0; int tmp;
/* Initialize comm buffer and globals. */
memset(remcom_out_buffer, 0, sizeof(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;
if (kgdb_connected) { unsignedchar thref[BUF_THREAD_ID_SIZE]; char *ptr;
/* Reply to host that an exception has occurred */
ptr = remcom_out_buffer;
*ptr++ = 'T';
ptr = hex_byte_pack(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);
}
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; #if DBG_MAX_REG_NUM > 0 case'p': /* pXX Return gdb register XX (in hex) */
gdb_cmd_reg_get(ks); break; case'P': /* PXX=aaaa Set gdb register XX to aaaa (in hex) */
gdb_cmd_reg_set(ks); break; #endif/* DBG_MAX_REG_NUM > 0 */ 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; #ifdef CONFIG_KGDB_KDB case'3': /* Escape into back into kdb */ if (remcom_in_buffer[1] == '\0') {
gdb_cmd_detachkill(ks); return DBG_PASS_EVENT;
}
fallthrough; #endif case'C': /* Exception passing */
tmp = gdb_cmd_exception_pass(ks); if (tmp > 0) goto default_handle; if (tmp == 0) break;
fallthrough; /* 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;
}
fallthrough; /* 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;
}
int gdbstub_state(struct kgdb_state *ks, char *cmd)
{ int error;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.