diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-08-01 14:45:09 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-08-01 14:45:09 -0400 |
commit | 31582b094d640fdab3fd29237b348a4c7c8646fb (patch) | |
tree | 52ee679e7dab9d6627d69d87470d62fef7a651d6 /kernel/kgdb.c | |
parent | df1efe6f871e2d3f83e6ad7b7a1d2b728b478fc2 (diff) | |
parent | 25fc999913839a45cbb48ac7872e67f7521e7ed9 (diff) |
Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jwessel/linux-2.6-kgdb
* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jwessel/linux-2.6-kgdb:
kgdb: fix gdb serial thread queries
kgdb: fix kgdb_validate_break_address to perform a mem write
kgdb: remove the requirement for CONFIG_FRAME_POINTER
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; |