Skip to content

Commit

Permalink
gdbstub: Implement gdbserial 'p' and 'P' packets
Browse files Browse the repository at this point in the history
The gdbserial 'p' and 'P' packets allow gdb to individually get and
set registers instead of querying for all the available registers.

Signed-off-by: Jason Wessel <[email protected]>
  • Loading branch information
jwessel committed Aug 5, 2010
1 parent 22eeef4 commit 5575114
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 21 deletions.
2 changes: 1 addition & 1 deletion include/linux/kgdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ extern void kgdb_unregister_io_module(struct kgdb_io *local_kgdb_io_ops);
extern struct kgdb_io *dbg_io_ops;

extern int kgdb_hex2long(char **ptr, unsigned long *long_val);
extern int kgdb_mem2hex(char *mem, char *buf, int count);
extern char *kgdb_mem2hex(char *mem, char *buf, int count);
extern int kgdb_hex2mem(char *buf, char *mem, int count);

extern int kgdb_isremovedbreak(unsigned long addr);
Expand Down
97 changes: 77 additions & 20 deletions kernel/debug/gdbstub.c
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ void gdbstub_msg_write(const char *s, int len)
* buf. Return a pointer to the last char put in buf (null). May
* return an error.
*/
int kgdb_mem2hex(char *mem, char *buf, int count)
char *kgdb_mem2hex(char *mem, char *buf, int count)
{
char *tmp;
int err;
Expand All @@ -237,17 +237,16 @@ int kgdb_mem2hex(char *mem, char *buf, int count)
tmp = buf + count;

err = probe_kernel_read(tmp, mem, count);
if (!err) {
while (count > 0) {
buf = pack_hex_byte(buf, *tmp);
tmp++;
count--;
}

*buf = 0;
if (err)
return NULL;
while (count > 0) {
buf = pack_hex_byte(buf, *tmp);
tmp++;
count--;
}
*buf = 0;

return err;
return buf;
}

/*
Expand Down Expand Up @@ -481,8 +480,7 @@ static void gdb_cmd_status(struct kgdb_state *ks)
pack_hex_byte(&remcom_out_buffer[1], ks->signo);
}

/* Handle the 'g' get registers request */
static void gdb_cmd_getregs(struct kgdb_state *ks)
static void gdb_get_regs_helper(struct kgdb_state *ks)
{
struct task_struct *thread;
void *local_debuggerinfo;
Expand Down Expand Up @@ -523,6 +521,12 @@ static void gdb_cmd_getregs(struct kgdb_state *ks)
*/
sleeping_thread_to_gdb_regs(gdb_regs, thread);
}
}

/* Handle the 'g' get registers request */
static void gdb_cmd_getregs(struct kgdb_state *ks)
{
gdb_get_regs_helper(ks);
kgdb_mem2hex((char *)gdb_regs, remcom_out_buffer, NUMREGBYTES);
}

Expand All @@ -545,13 +549,13 @@ static void gdb_cmd_memread(struct kgdb_state *ks)
char *ptr = &remcom_in_buffer[1];
unsigned long length;
unsigned long addr;
int err;
char *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);
if (!err)
error_packet(remcom_out_buffer, -EINVAL);
} else {
error_packet(remcom_out_buffer, -EINVAL);
}
Expand All @@ -568,6 +572,52 @@ static void gdb_cmd_memwrite(struct kgdb_state *ks)
strcpy(remcom_out_buffer, "OK");
}

#if DBG_MAX_REG_NUM > 0
static char *gdb_hex_reg_helper(int regnum, char *out)
{
int i;
int offset = 0;

for (i = 0; i < regnum; i++)
offset += dbg_reg_def[i].size;
return kgdb_mem2hex((char *)gdb_regs + offset, out,
dbg_reg_def[i].size);
}

/* Handle the 'p' individual regster get */
static void gdb_cmd_reg_get(struct kgdb_state *ks)
{
unsigned long regnum;
char *ptr = &remcom_in_buffer[1];

kgdb_hex2long(&ptr, &regnum);
if (regnum >= DBG_MAX_REG_NUM) {
error_packet(remcom_out_buffer, -EINVAL);
return;
}
gdb_get_regs_helper(ks);
gdb_hex_reg_helper(regnum, remcom_out_buffer);
}

/* Handle the 'P' individual regster set */
static void gdb_cmd_reg_set(struct kgdb_state *ks)
{
unsigned long regnum;
char *ptr = &remcom_in_buffer[1];

kgdb_hex2long(&ptr, &regnum);
if (*ptr++ != '=' ||
!(!kgdb_usethread || kgdb_usethread == current) ||
!dbg_get_reg(regnum, gdb_regs, ks->linux_regs)) {
error_packet(remcom_out_buffer, -EINVAL);
return;
}
kgdb_hex2mem(ptr, (char *)gdb_regs, dbg_reg_def[regnum].size);
dbg_set_reg(regnum, gdb_regs, ks->linux_regs);
strcpy(remcom_out_buffer, "OK");
}
#endif /* DBG_MAX_REG_NUM > 0 */

/* Handle the 'X' memory binary write bytes */
static void gdb_cmd_binwrite(struct kgdb_state *ks)
{
Expand Down Expand Up @@ -874,8 +924,11 @@ int gdb_serial_stub(struct kgdb_state *ks)
int error = 0;
int tmp;

/* Clear the out buffer. */
/* 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) {
unsigned char thref[BUF_THREAD_ID_SIZE];
Expand All @@ -892,10 +945,6 @@ int gdb_serial_stub(struct kgdb_state *ks)
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;

Expand All @@ -920,6 +969,14 @@ int gdb_serial_stub(struct kgdb_state *ks)
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;
Expand Down

0 comments on commit 5575114

Please sign in to comment.