diff options
Diffstat (limited to 'kernel/debug/gdbstub.c')
| -rw-r--r-- | kernel/debug/gdbstub.c | 191 | 
1 files changed, 136 insertions, 55 deletions
| diff --git a/kernel/debug/gdbstub.c b/kernel/debug/gdbstub.c index e8fd6868682d..481a7bd2dfe7 100644 --- a/kernel/debug/gdbstub.c +++ b/kernel/debug/gdbstub.c | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | * Copyright (C) 2000-2001 VERITAS Software Corporation. | 6 | * Copyright (C) 2000-2001 VERITAS Software Corporation. | 
| 7 | * Copyright (C) 2002-2004 Timesys Corporation | 7 | * Copyright (C) 2002-2004 Timesys Corporation | 
| 8 | * Copyright (C) 2003-2004 Amit S. Kale <amitkale@linsyssoft.com> | 8 | * Copyright (C) 2003-2004 Amit S. Kale <amitkale@linsyssoft.com> | 
| 9 | * Copyright (C) 2004 Pavel Machek <pavel@suse.cz> | 9 | * Copyright (C) 2004 Pavel Machek <pavel@ucw.cz> | 
| 10 | * Copyright (C) 2004-2006 Tom Rini <trini@kernel.crashing.org> | 10 | * Copyright (C) 2004-2006 Tom Rini <trini@kernel.crashing.org> | 
| 11 | * Copyright (C) 2004-2006 LinSysSoft Technologies Pvt. Ltd. | 11 | * Copyright (C) 2004-2006 LinSysSoft Technologies Pvt. Ltd. | 
| 12 | * Copyright (C) 2005-2009 Wind River Systems, Inc. | 12 | * Copyright (C) 2005-2009 Wind River Systems, Inc. | 
| @@ -52,17 +52,6 @@ static unsigned long gdb_regs[(NUMREGBYTES + | |||
| 52 | * GDB remote protocol parser: | 52 | * GDB remote protocol parser: | 
| 53 | */ | 53 | */ | 
| 54 | 54 | ||
| 55 | static int hex(char ch) | ||
| 56 | { | ||
| 57 | if ((ch >= 'a') && (ch <= 'f')) | ||
| 58 | return ch - 'a' + 10; | ||
| 59 | if ((ch >= '0') && (ch <= '9')) | ||
| 60 | return ch - '0'; | ||
| 61 | if ((ch >= 'A') && (ch <= 'F')) | ||
| 62 | return ch - 'A' + 10; | ||
| 63 | return -1; | ||
| 64 | } | ||
| 65 | |||
| 66 | #ifdef CONFIG_KGDB_KDB | 55 | #ifdef CONFIG_KGDB_KDB | 
| 67 | static int gdbstub_read_wait(void) | 56 | static int gdbstub_read_wait(void) | 
| 68 | { | 57 | { | 
| @@ -123,8 +112,8 @@ static void get_packet(char *buffer) | |||
| 123 | buffer[count] = 0; | 112 | buffer[count] = 0; | 
| 124 | 113 | ||
| 125 | if (ch == '#') { | 114 | if (ch == '#') { | 
| 126 | xmitcsum = hex(gdbstub_read_wait()) << 4; | 115 | xmitcsum = hex_to_bin(gdbstub_read_wait()) << 4; | 
| 127 | xmitcsum += hex(gdbstub_read_wait()); | 116 | xmitcsum += hex_to_bin(gdbstub_read_wait()); | 
| 128 | 117 | ||
| 129 | if (checksum != xmitcsum) | 118 | if (checksum != xmitcsum) | 
| 130 | /* failed checksum */ | 119 | /* failed checksum */ | 
| @@ -236,7 +225,7 @@ void gdbstub_msg_write(const char *s, int len) | |||
| 236 | * buf. Return a pointer to the last char put in buf (null). May | 225 | * buf. Return a pointer to the last char put in buf (null). May | 
| 237 | * return an error. | 226 | * return an error. | 
| 238 | */ | 227 | */ | 
| 239 | int kgdb_mem2hex(char *mem, char *buf, int count) | 228 | char *kgdb_mem2hex(char *mem, char *buf, int count) | 
| 240 | { | 229 | { | 
| 241 | char *tmp; | 230 | char *tmp; | 
| 242 | int err; | 231 | int err; | 
| @@ -248,17 +237,16 @@ int kgdb_mem2hex(char *mem, char *buf, int count) | |||
| 248 | tmp = buf + count; | 237 | tmp = buf + count; | 
| 249 | 238 | ||
| 250 | err = probe_kernel_read(tmp, mem, count); | 239 | err = probe_kernel_read(tmp, mem, count); | 
| 251 | if (!err) { | 240 | if (err) | 
| 252 | while (count > 0) { | 241 | return NULL; | 
| 253 | buf = pack_hex_byte(buf, *tmp); | 242 | while (count > 0) { | 
| 254 | tmp++; | 243 | buf = pack_hex_byte(buf, *tmp); | 
| 255 | count--; | 244 | tmp++; | 
| 256 | } | 245 | count--; | 
| 257 | |||
| 258 | *buf = 0; | ||
| 259 | } | 246 | } | 
| 247 | *buf = 0; | ||
| 260 | 248 | ||
| 261 | return err; | 249 | return buf; | 
| 262 | } | 250 | } | 
| 263 | 251 | ||
| 264 | /* | 252 | /* | 
| @@ -280,8 +268,8 @@ int kgdb_hex2mem(char *buf, char *mem, int count) | |||
| 280 | tmp_hex = tmp_raw - 1; | 268 | tmp_hex = tmp_raw - 1; | 
| 281 | while (tmp_hex >= buf) { | 269 | while (tmp_hex >= buf) { | 
| 282 | tmp_raw--; | 270 | tmp_raw--; | 
| 283 | *tmp_raw = hex(*tmp_hex--); | 271 | *tmp_raw = hex_to_bin(*tmp_hex--); | 
| 284 | *tmp_raw |= hex(*tmp_hex--) << 4; | 272 | *tmp_raw |= hex_to_bin(*tmp_hex--) << 4; | 
| 285 | } | 273 | } | 
| 286 | 274 | ||
| 287 | return probe_kernel_write(mem, tmp_raw, count); | 275 | return probe_kernel_write(mem, tmp_raw, count); | 
| @@ -304,7 +292,7 @@ int kgdb_hex2long(char **ptr, unsigned long *long_val) | |||
| 304 | (*ptr)++; | 292 | (*ptr)++; | 
| 305 | } | 293 | } | 
| 306 | while (**ptr) { | 294 | while (**ptr) { | 
| 307 | hex_val = hex(**ptr); | 295 | hex_val = hex_to_bin(**ptr); | 
| 308 | if (hex_val < 0) | 296 | if (hex_val < 0) | 
| 309 | break; | 297 | break; | 
| 310 | 298 | ||
| @@ -339,6 +327,32 @@ static int kgdb_ebin2mem(char *buf, char *mem, int count) | |||
| 339 | return probe_kernel_write(mem, c, size); | 327 | return probe_kernel_write(mem, c, size); | 
| 340 | } | 328 | } | 
| 341 | 329 | ||
| 330 | #if DBG_MAX_REG_NUM > 0 | ||
| 331 | void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) | ||
| 332 | { | ||
| 333 | int i; | ||
| 334 | int idx = 0; | ||
| 335 | char *ptr = (char *)gdb_regs; | ||
| 336 | |||
| 337 | for (i = 0; i < DBG_MAX_REG_NUM; i++) { | ||
| 338 | dbg_get_reg(i, ptr + idx, regs); | ||
| 339 | idx += dbg_reg_def[i].size; | ||
| 340 | } | ||
| 341 | } | ||
| 342 | |||
| 343 | void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) | ||
| 344 | { | ||
| 345 | int i; | ||
| 346 | int idx = 0; | ||
| 347 | char *ptr = (char *)gdb_regs; | ||
| 348 | |||
| 349 | for (i = 0; i < DBG_MAX_REG_NUM; i++) { | ||
| 350 | dbg_set_reg(i, ptr + idx, regs); | ||
| 351 | idx += dbg_reg_def[i].size; | ||
| 352 | } | ||
| 353 | } | ||
| 354 | #endif /* DBG_MAX_REG_NUM > 0 */ | ||
| 355 | |||
| 342 | /* Write memory due to an 'M' or 'X' packet. */ | 356 | /* Write memory due to an 'M' or 'X' packet. */ | 
| 343 | static int write_mem_msg(int binary) | 357 | static int write_mem_msg(int binary) | 
| 344 | { | 358 | { | 
| @@ -378,28 +392,31 @@ static void error_packet(char *pkt, int error) | |||
| 378 | * remapped to negative TIDs. | 392 | * remapped to negative TIDs. | 
| 379 | */ | 393 | */ | 
| 380 | 394 | ||
| 381 | #define BUF_THREAD_ID_SIZE 16 | 395 | #define BUF_THREAD_ID_SIZE 8 | 
| 382 | 396 | ||
| 383 | static char *pack_threadid(char *pkt, unsigned char *id) | 397 | static char *pack_threadid(char *pkt, unsigned char *id) | 
| 384 | { | 398 | { | 
| 385 | char *limit; | 399 | unsigned char *limit; | 
| 400 | int lzero = 1; | ||
| 401 | |||
| 402 | limit = id + (BUF_THREAD_ID_SIZE / 2); | ||
| 403 | while (id < limit) { | ||
| 404 | if (!lzero || *id != 0) { | ||
| 405 | pkt = pack_hex_byte(pkt, *id); | ||
| 406 | lzero = 0; | ||
| 407 | } | ||
| 408 | id++; | ||
| 409 | } | ||
| 386 | 410 | ||
| 387 | limit = pkt + BUF_THREAD_ID_SIZE; | 411 | if (lzero) | 
| 388 | while (pkt < limit) | 412 | pkt = pack_hex_byte(pkt, 0); | 
| 389 | pkt = pack_hex_byte(pkt, *id++); | ||
| 390 | 413 | ||
| 391 | return pkt; | 414 | return pkt; | 
| 392 | } | 415 | } | 
| 393 | 416 | ||
| 394 | static void int_to_threadref(unsigned char *id, int value) | 417 | static void int_to_threadref(unsigned char *id, int value) | 
| 395 | { | 418 | { | 
| 396 | unsigned char *scan; | 419 | put_unaligned_be32(value, id); | 
| 397 | int i = 4; | ||
| 398 | |||
| 399 | scan = (unsigned char *)id; | ||
| 400 | while (i--) | ||
| 401 | *scan++ = 0; | ||
| 402 | put_unaligned_be32(value, scan); | ||
| 403 | } | 420 | } | 
| 404 | 421 | ||
| 405 | static struct task_struct *getthread(struct pt_regs *regs, int tid) | 422 | static struct task_struct *getthread(struct pt_regs *regs, int tid) | 
| @@ -463,8 +480,7 @@ static void gdb_cmd_status(struct kgdb_state *ks) | |||
| 463 | pack_hex_byte(&remcom_out_buffer[1], ks->signo); | 480 | pack_hex_byte(&remcom_out_buffer[1], ks->signo); | 
| 464 | } | 481 | } | 
| 465 | 482 | ||
| 466 | /* Handle the 'g' get registers request */ | 483 | static void gdb_get_regs_helper(struct kgdb_state *ks) | 
| 467 | static void gdb_cmd_getregs(struct kgdb_state *ks) | ||
| 468 | { | 484 | { | 
| 469 | struct task_struct *thread; | 485 | struct task_struct *thread; | 
| 470 | void *local_debuggerinfo; | 486 | void *local_debuggerinfo; | 
| @@ -505,6 +521,12 @@ static void gdb_cmd_getregs(struct kgdb_state *ks) | |||
| 505 | */ | 521 | */ | 
| 506 | sleeping_thread_to_gdb_regs(gdb_regs, thread); | 522 | sleeping_thread_to_gdb_regs(gdb_regs, thread); | 
| 507 | } | 523 | } | 
| 524 | } | ||
| 525 | |||
| 526 | /* Handle the 'g' get registers request */ | ||
| 527 | static void gdb_cmd_getregs(struct kgdb_state *ks) | ||
| 528 | { | ||
| 529 | gdb_get_regs_helper(ks); | ||
| 508 | kgdb_mem2hex((char *)gdb_regs, remcom_out_buffer, NUMREGBYTES); | 530 | kgdb_mem2hex((char *)gdb_regs, remcom_out_buffer, NUMREGBYTES); | 
| 509 | } | 531 | } | 
| 510 | 532 | ||
| @@ -527,13 +549,13 @@ static void gdb_cmd_memread(struct kgdb_state *ks) | |||
| 527 | char *ptr = &remcom_in_buffer[1]; | 549 | char *ptr = &remcom_in_buffer[1]; | 
| 528 | unsigned long length; | 550 | unsigned long length; | 
| 529 | unsigned long addr; | 551 | unsigned long addr; | 
| 530 | int err; | 552 | char *err; | 
| 531 | 553 | ||
| 532 | if (kgdb_hex2long(&ptr, &addr) > 0 && *ptr++ == ',' && | 554 | if (kgdb_hex2long(&ptr, &addr) > 0 && *ptr++ == ',' && | 
| 533 | kgdb_hex2long(&ptr, &length) > 0) { | 555 | kgdb_hex2long(&ptr, &length) > 0) { | 
| 534 | err = kgdb_mem2hex((char *)addr, remcom_out_buffer, length); | 556 | err = kgdb_mem2hex((char *)addr, remcom_out_buffer, length); | 
| 535 | if (err) | 557 | if (!err) | 
| 536 | error_packet(remcom_out_buffer, err); | 558 | error_packet(remcom_out_buffer, -EINVAL); | 
| 537 | } else { | 559 | } else { | 
| 538 | error_packet(remcom_out_buffer, -EINVAL); | 560 | error_packet(remcom_out_buffer, -EINVAL); | 
| 539 | } | 561 | } | 
| @@ -550,6 +572,60 @@ static void gdb_cmd_memwrite(struct kgdb_state *ks) | |||
| 550 | strcpy(remcom_out_buffer, "OK"); | 572 | strcpy(remcom_out_buffer, "OK"); | 
| 551 | } | 573 | } | 
| 552 | 574 | ||
| 575 | #if DBG_MAX_REG_NUM > 0 | ||
| 576 | static char *gdb_hex_reg_helper(int regnum, char *out) | ||
| 577 | { | ||
| 578 | int i; | ||
| 579 | int offset = 0; | ||
| 580 | |||
| 581 | for (i = 0; i < regnum; i++) | ||
| 582 | offset += dbg_reg_def[i].size; | ||
| 583 | return kgdb_mem2hex((char *)gdb_regs + offset, out, | ||
| 584 | dbg_reg_def[i].size); | ||
| 585 | } | ||
| 586 | |||
| 587 | /* Handle the 'p' individual regster get */ | ||
| 588 | static void gdb_cmd_reg_get(struct kgdb_state *ks) | ||
| 589 | { | ||
| 590 | unsigned long regnum; | ||
| 591 | char *ptr = &remcom_in_buffer[1]; | ||
| 592 | |||
| 593 | kgdb_hex2long(&ptr, ®num); | ||
| 594 | if (regnum >= DBG_MAX_REG_NUM) { | ||
| 595 | error_packet(remcom_out_buffer, -EINVAL); | ||
| 596 | return; | ||
| 597 | } | ||
| 598 | gdb_get_regs_helper(ks); | ||
| 599 | gdb_hex_reg_helper(regnum, remcom_out_buffer); | ||
| 600 | } | ||
| 601 | |||
| 602 | /* Handle the 'P' individual regster set */ | ||
| 603 | static void gdb_cmd_reg_set(struct kgdb_state *ks) | ||
| 604 | { | ||
| 605 | unsigned long regnum; | ||
| 606 | char *ptr = &remcom_in_buffer[1]; | ||
| 607 | int i = 0; | ||
| 608 | |||
| 609 | kgdb_hex2long(&ptr, ®num); | ||
| 610 | if (*ptr++ != '=' || | ||
| 611 | !(!kgdb_usethread || kgdb_usethread == current) || | ||
| 612 | !dbg_get_reg(regnum, gdb_regs, ks->linux_regs)) { | ||
| 613 | error_packet(remcom_out_buffer, -EINVAL); | ||
| 614 | return; | ||
| 615 | } | ||
| 616 | memset(gdb_regs, 0, sizeof(gdb_regs)); | ||
| 617 | while (i < sizeof(gdb_regs) * 2) | ||
| 618 | if (hex_to_bin(ptr[i]) >= 0) | ||
| 619 | i++; | ||
| 620 | else | ||
| 621 | break; | ||
| 622 | i = i / 2; | ||
| 623 | kgdb_hex2mem(ptr, (char *)gdb_regs, i); | ||
| 624 | dbg_set_reg(regnum, gdb_regs, ks->linux_regs); | ||
| 625 | strcpy(remcom_out_buffer, "OK"); | ||
| 626 | } | ||
| 627 | #endif /* DBG_MAX_REG_NUM > 0 */ | ||
| 628 | |||
| 553 | /* Handle the 'X' memory binary write bytes */ | 629 | /* Handle the 'X' memory binary write bytes */ | 
| 554 | static void gdb_cmd_binwrite(struct kgdb_state *ks) | 630 | static void gdb_cmd_binwrite(struct kgdb_state *ks) | 
| 555 | { | 631 | { | 
| @@ -612,7 +688,7 @@ static void gdb_cmd_query(struct kgdb_state *ks) | |||
| 612 | { | 688 | { | 
| 613 | struct task_struct *g; | 689 | struct task_struct *g; | 
| 614 | struct task_struct *p; | 690 | struct task_struct *p; | 
| 615 | unsigned char thref[8]; | 691 | unsigned char thref[BUF_THREAD_ID_SIZE]; | 
| 616 | char *ptr; | 692 | char *ptr; | 
| 617 | int i; | 693 | int i; | 
| 618 | int cpu; | 694 | int cpu; | 
| @@ -632,8 +708,7 @@ static void gdb_cmd_query(struct kgdb_state *ks) | |||
| 632 | for_each_online_cpu(cpu) { | 708 | for_each_online_cpu(cpu) { | 
| 633 | ks->thr_query = 0; | 709 | ks->thr_query = 0; | 
| 634 | int_to_threadref(thref, -cpu - 2); | 710 | int_to_threadref(thref, -cpu - 2); | 
| 635 | pack_threadid(ptr, thref); | 711 | ptr = pack_threadid(ptr, thref); | 
| 636 | ptr += BUF_THREAD_ID_SIZE; | ||
| 637 | *(ptr++) = ','; | 712 | *(ptr++) = ','; | 
| 638 | i++; | 713 | i++; | 
| 639 | } | 714 | } | 
| @@ -642,8 +717,7 @@ static void gdb_cmd_query(struct kgdb_state *ks) | |||
| 642 | do_each_thread(g, p) { | 717 | do_each_thread(g, p) { | 
| 643 | if (i >= ks->thr_query && !finished) { | 718 | if (i >= ks->thr_query && !finished) { | 
| 644 | int_to_threadref(thref, p->pid); | 719 | int_to_threadref(thref, p->pid); | 
| 645 | pack_threadid(ptr, thref); | 720 | ptr = pack_threadid(ptr, thref); | 
| 646 | ptr += BUF_THREAD_ID_SIZE; | ||
| 647 | *(ptr++) = ','; | 721 | *(ptr++) = ','; | 
| 648 | ks->thr_query++; | 722 | ks->thr_query++; | 
| 649 | if (ks->thr_query % KGDB_MAX_THREAD_QUERY == 0) | 723 | if (ks->thr_query % KGDB_MAX_THREAD_QUERY == 0) | 
| @@ -858,11 +932,14 @@ int gdb_serial_stub(struct kgdb_state *ks) | |||
| 858 | int error = 0; | 932 | int error = 0; | 
| 859 | int tmp; | 933 | int tmp; | 
| 860 | 934 | ||
| 861 | /* Clear the out buffer. */ | 935 | /* Initialize comm buffer and globals. */ | 
| 862 | memset(remcom_out_buffer, 0, sizeof(remcom_out_buffer)); | 936 | memset(remcom_out_buffer, 0, sizeof(remcom_out_buffer)); | 
| 937 | kgdb_usethread = kgdb_info[ks->cpu].task; | ||
| 938 | ks->kgdb_usethreadid = shadow_pid(kgdb_info[ks->cpu].task->pid); | ||
| 939 | ks->pass_exception = 0; | ||
| 863 | 940 | ||
| 864 | if (kgdb_connected) { | 941 | if (kgdb_connected) { | 
| 865 | unsigned char thref[8]; | 942 | unsigned char thref[BUF_THREAD_ID_SIZE]; | 
| 866 | char *ptr; | 943 | char *ptr; | 
| 867 | 944 | ||
| 868 | /* Reply to host that an exception has occurred */ | 945 | /* Reply to host that an exception has occurred */ | 
| @@ -876,10 +953,6 @@ int gdb_serial_stub(struct kgdb_state *ks) | |||
| 876 | put_packet(remcom_out_buffer); | 953 | put_packet(remcom_out_buffer); | 
| 877 | } | 954 | } | 
| 878 | 955 | ||
| 879 | kgdb_usethread = kgdb_info[ks->cpu].task; | ||
| 880 | ks->kgdb_usethreadid = shadow_pid(kgdb_info[ks->cpu].task->pid); | ||
| 881 | ks->pass_exception = 0; | ||
| 882 | |||
| 883 | while (1) { | 956 | while (1) { | 
| 884 | error = 0; | 957 | error = 0; | 
| 885 | 958 | ||
| @@ -904,6 +977,14 @@ int gdb_serial_stub(struct kgdb_state *ks) | |||
| 904 | case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA..AA */ | 977 | case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA..AA */ | 
| 905 | gdb_cmd_memwrite(ks); | 978 | gdb_cmd_memwrite(ks); | 
| 906 | break; | 979 | break; | 
| 980 | #if DBG_MAX_REG_NUM > 0 | ||
| 981 | case 'p': /* pXX Return gdb register XX (in hex) */ | ||
| 982 | gdb_cmd_reg_get(ks); | ||
| 983 | break; | ||
| 984 | case 'P': /* PXX=aaaa Set gdb register XX to aaaa (in hex) */ | ||
| 985 | gdb_cmd_reg_set(ks); | ||
| 986 | break; | ||
| 987 | #endif /* DBG_MAX_REG_NUM > 0 */ | ||
| 907 | case 'X': /* XAA..AA,LLLL: Write LLLL bytes at address AA..AA */ | 988 | case 'X': /* XAA..AA,LLLL: Write LLLL bytes at address AA..AA */ | 
| 908 | gdb_cmd_binwrite(ks); | 989 | gdb_cmd_binwrite(ks); | 
| 909 | break; | 990 | break; | 
