diff options
Diffstat (limited to 'kernel/kgdb.c')
| -rw-r--r-- | kernel/kgdb.c | 94 |
1 files changed, 69 insertions, 25 deletions
diff --git a/kernel/kgdb.c b/kernel/kgdb.c index 3ec23c3ec97f..eaa21fc9ad1d 100644 --- a/kernel/kgdb.c +++ b/kernel/kgdb.c | |||
| @@ -56,12 +56,14 @@ | |||
| 56 | 56 | ||
| 57 | static int kgdb_break_asap; | 57 | static int kgdb_break_asap; |
| 58 | 58 | ||
| 59 | #define KGDB_MAX_THREAD_QUERY 17 | ||
| 59 | struct kgdb_state { | 60 | struct kgdb_state { |
| 60 | int ex_vector; | 61 | int ex_vector; |
| 61 | int signo; | 62 | int signo; |
| 62 | int err_code; | 63 | int err_code; |
| 63 | int cpu; | 64 | int cpu; |
| 64 | int pass_exception; | 65 | int pass_exception; |
| 66 | unsigned long thr_query; | ||
| 65 | unsigned long threadid; | 67 | unsigned long threadid; |
| 66 | long kgdb_usethreadid; | 68 | long kgdb_usethreadid; |
| 67 | struct pt_regs *linux_regs; | 69 | struct pt_regs *linux_regs; |
| @@ -166,13 +168,6 @@ early_param("nokgdbroundup", opt_nokgdbroundup); | |||
| 166 | * Weak aliases for breakpoint management, | 168 | * Weak aliases for breakpoint management, |
| 167 | * can be overriden by architectures when needed: | 169 | * can be overriden by architectures when needed: |
| 168 | */ | 170 | */ |
| 169 | int __weak kgdb_validate_break_address(unsigned long addr) | ||
| 170 | { | ||
| 171 | char tmp_variable[BREAK_INSTR_SIZE]; | ||
| 172 | |||
| 173 | return probe_kernel_read(tmp_variable, (char *)addr, BREAK_INSTR_SIZE); | ||
| 174 | } | ||
| 175 | |||
| 176 | int __weak kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr) | 171 | int __weak kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr) |
| 177 | { | 172 | { |
| 178 | int err; | 173 | int err; |
| @@ -191,6 +186,25 @@ int __weak kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle) | |||
| 191 | (char *)bundle, BREAK_INSTR_SIZE); | 186 | (char *)bundle, BREAK_INSTR_SIZE); |
| 192 | } | 187 | } |
| 193 | 188 | ||
| 189 | int __weak kgdb_validate_break_address(unsigned long addr) | ||
| 190 | { | ||
| 191 | char tmp_variable[BREAK_INSTR_SIZE]; | ||
| 192 | int err; | ||
| 193 | /* Validate setting the breakpoint and then removing it. In the | ||
| 194 | * remove fails, the kernel needs to emit a bad message because we | ||
| 195 | * are deep trouble not being able to put things back the way we | ||
| 196 | * found them. | ||
| 197 | */ | ||
| 198 | err = kgdb_arch_set_breakpoint(addr, tmp_variable); | ||
| 199 | if (err) | ||
| 200 | return err; | ||
| 201 | err = kgdb_arch_remove_breakpoint(addr, tmp_variable); | ||
| 202 | if (err) | ||
| 203 | printk(KERN_ERR "KGDB: Critical breakpoint error, kernel " | ||
| 204 | "memory destroyed at: %lx", addr); | ||
| 205 | return err; | ||
| 206 | } | ||
| 207 | |||
| 194 | unsigned long __weak kgdb_arch_pc(int exception, struct pt_regs *regs) | 208 | unsigned long __weak kgdb_arch_pc(int exception, struct pt_regs *regs) |
| 195 | { | 209 | { |
| 196 | return instruction_pointer(regs); | 210 | return instruction_pointer(regs); |
| @@ -433,9 +447,14 @@ int kgdb_hex2long(char **ptr, unsigned long *long_val) | |||
| 433 | { | 447 | { |
| 434 | int hex_val; | 448 | int hex_val; |
| 435 | int num = 0; | 449 | int num = 0; |
| 450 | int negate = 0; | ||
| 436 | 451 | ||
| 437 | *long_val = 0; | 452 | *long_val = 0; |
| 438 | 453 | ||
| 454 | if (**ptr == '-') { | ||
| 455 | negate = 1; | ||
| 456 | (*ptr)++; | ||
| 457 | } | ||
| 439 | while (**ptr) { | 458 | while (**ptr) { |
| 440 | hex_val = hex(**ptr); | 459 | hex_val = hex(**ptr); |
| 441 | if (hex_val < 0) | 460 | if (hex_val < 0) |
| @@ -446,6 +465,9 @@ int kgdb_hex2long(char **ptr, unsigned long *long_val) | |||
| 446 | (*ptr)++; | 465 | (*ptr)++; |
| 447 | } | 466 | } |
| 448 | 467 | ||
| 468 | if (negate) | ||
| 469 | *long_val = -*long_val; | ||
| 470 | |||
| 449 | return num; | 471 | return num; |
| 450 | } | 472 | } |
| 451 | 473 | ||
| @@ -515,10 +537,16 @@ static void int_to_threadref(unsigned char *id, int value) | |||
| 515 | static struct task_struct *getthread(struct pt_regs *regs, int tid) | 537 | static struct task_struct *getthread(struct pt_regs *regs, int tid) |
| 516 | { | 538 | { |
| 517 | /* | 539 | /* |
| 518 | * Non-positive TIDs are remapped idle tasks: | 540 | * Non-positive TIDs are remapped to the cpu shadow information |
| 519 | */ | 541 | */ |
| 520 | if (tid <= 0) | 542 | if (tid == 0 || tid == -1) |
| 521 | return idle_task(-tid); | 543 | tid = -atomic_read(&kgdb_active) - 2; |
| 544 | if (tid < 0) { | ||
| 545 | if (kgdb_info[-tid - 2].task) | ||
| 546 | return kgdb_info[-tid - 2].task; | ||
| 547 | else | ||
| 548 | return idle_task(-tid - 2); | ||
| 549 | } | ||
| 522 | 550 | ||
| 523 | /* | 551 | /* |
| 524 | * find_task_by_pid_ns() does not take the tasklist lock anymore | 552 | * find_task_by_pid_ns() does not take the tasklist lock anymore |
| @@ -725,14 +753,15 @@ setundefined: | |||
| 725 | } | 753 | } |
| 726 | 754 | ||
| 727 | /* | 755 | /* |
| 728 | * Remap normal tasks to their real PID, idle tasks to -1 ... -NR_CPUs: | 756 | * Remap normal tasks to their real PID, |
| 757 | * CPU shadow threads are mapped to -CPU - 2 | ||
| 729 | */ | 758 | */ |
| 730 | static inline int shadow_pid(int realpid) | 759 | static inline int shadow_pid(int realpid) |
| 731 | { | 760 | { |
| 732 | if (realpid) | 761 | if (realpid) |
| 733 | return realpid; | 762 | return realpid; |
| 734 | 763 | ||
| 735 | return -1-raw_smp_processor_id(); | 764 | return -raw_smp_processor_id() - 2; |
| 736 | } | 765 | } |
| 737 | 766 | ||
| 738 | static char gdbmsgbuf[BUFMAX + 1]; | 767 | static char gdbmsgbuf[BUFMAX + 1]; |
| @@ -826,7 +855,7 @@ static void gdb_cmd_getregs(struct kgdb_state *ks) | |||
| 826 | local_debuggerinfo = kgdb_info[ks->cpu].debuggerinfo; | 855 | local_debuggerinfo = kgdb_info[ks->cpu].debuggerinfo; |
| 827 | } else { | 856 | } else { |
| 828 | local_debuggerinfo = NULL; | 857 | local_debuggerinfo = NULL; |
| 829 | for (i = 0; i < NR_CPUS; i++) { | 858 | for_each_online_cpu(i) { |
| 830 | /* | 859 | /* |
| 831 | * Try to find the task on some other | 860 | * Try to find the task on some other |
| 832 | * or possibly this node if we do not | 861 | * or possibly this node if we do not |
| @@ -960,10 +989,13 @@ static int gdb_cmd_reboot(struct kgdb_state *ks) | |||
| 960 | /* Handle the 'q' query packets */ | 989 | /* Handle the 'q' query packets */ |
| 961 | static void gdb_cmd_query(struct kgdb_state *ks) | 990 | static void gdb_cmd_query(struct kgdb_state *ks) |
| 962 | { | 991 | { |
| 963 | struct task_struct *thread; | 992 | struct task_struct *g; |
| 993 | struct task_struct *p; | ||
| 964 | unsigned char thref[8]; | 994 | unsigned char thref[8]; |
| 965 | char *ptr; | 995 | char *ptr; |
| 966 | int i; | 996 | int i; |
| 997 | int cpu; | ||
| 998 | int finished = 0; | ||
| 967 | 999 | ||
| 968 | switch (remcom_in_buffer[1]) { | 1000 | switch (remcom_in_buffer[1]) { |
| 969 | case 's': | 1001 | case 's': |
| @@ -973,22 +1005,34 @@ static void gdb_cmd_query(struct kgdb_state *ks) | |||
| 973 | break; | 1005 | break; |
| 974 | } | 1006 | } |
| 975 | 1007 | ||
| 976 | if (remcom_in_buffer[1] == 'f') | 1008 | i = 0; |
| 977 | ks->threadid = 1; | ||
| 978 | |||
| 979 | remcom_out_buffer[0] = 'm'; | 1009 | remcom_out_buffer[0] = 'm'; |
| 980 | ptr = remcom_out_buffer + 1; | 1010 | ptr = remcom_out_buffer + 1; |
| 981 | 1011 | if (remcom_in_buffer[1] == 'f') { | |
| 982 | for (i = 0; i < 17; ks->threadid++) { | 1012 | /* Each cpu is a shadow thread */ |
| 983 | thread = getthread(ks->linux_regs, ks->threadid); | 1013 | for_each_online_cpu(cpu) { |
| 984 | if (thread) { | 1014 | ks->thr_query = 0; |
| 985 | int_to_threadref(thref, ks->threadid); | 1015 | int_to_threadref(thref, -cpu - 2); |
| 986 | pack_threadid(ptr, thref); | 1016 | pack_threadid(ptr, thref); |
| 987 | ptr += BUF_THREAD_ID_SIZE; | 1017 | ptr += BUF_THREAD_ID_SIZE; |
| 988 | *(ptr++) = ','; | 1018 | *(ptr++) = ','; |
| 989 | i++; | 1019 | i++; |
| 990 | } | 1020 | } |
| 991 | } | 1021 | } |
| 1022 | |||
| 1023 | do_each_thread(g, p) { | ||
| 1024 | if (i >= ks->thr_query && !finished) { | ||
| 1025 | int_to_threadref(thref, p->pid); | ||
| 1026 | pack_threadid(ptr, thref); | ||
| 1027 | ptr += BUF_THREAD_ID_SIZE; | ||
| 1028 | *(ptr++) = ','; | ||
| 1029 | ks->thr_query++; | ||
| 1030 | if (ks->thr_query % KGDB_MAX_THREAD_QUERY == 0) | ||
| 1031 | finished = 1; | ||
| 1032 | } | ||
| 1033 | i++; | ||
| 1034 | } while_each_thread(g, p); | ||
| 1035 | |||
| 992 | *(--ptr) = '\0'; | 1036 | *(--ptr) = '\0'; |
| 993 | break; | 1037 | break; |
| 994 | 1038 | ||
| @@ -1011,15 +1055,15 @@ static void gdb_cmd_query(struct kgdb_state *ks) | |||
| 1011 | error_packet(remcom_out_buffer, -EINVAL); | 1055 | error_packet(remcom_out_buffer, -EINVAL); |
| 1012 | break; | 1056 | break; |
| 1013 | } | 1057 | } |
| 1014 | if (ks->threadid > 0) { | 1058 | if ((int)ks->threadid > 0) { |
| 1015 | kgdb_mem2hex(getthread(ks->linux_regs, | 1059 | kgdb_mem2hex(getthread(ks->linux_regs, |
| 1016 | ks->threadid)->comm, | 1060 | ks->threadid)->comm, |
| 1017 | remcom_out_buffer, 16); | 1061 | remcom_out_buffer, 16); |
| 1018 | } else { | 1062 | } else { |
| 1019 | static char tmpstr[23 + BUF_THREAD_ID_SIZE]; | 1063 | static char tmpstr[23 + BUF_THREAD_ID_SIZE]; |
| 1020 | 1064 | ||
| 1021 | sprintf(tmpstr, "Shadow task %d for pid 0", | 1065 | sprintf(tmpstr, "shadowCPU%d", |
| 1022 | (int)(-ks->threadid-1)); | 1066 | (int)(-ks->threadid - 2)); |
| 1023 | kgdb_mem2hex(tmpstr, remcom_out_buffer, strlen(tmpstr)); | 1067 | kgdb_mem2hex(tmpstr, remcom_out_buffer, strlen(tmpstr)); |
| 1024 | } | 1068 | } |
| 1025 | break; | 1069 | break; |
