diff options
author | Paul Mundt <lethal@linux-sh.org> | 2007-03-08 03:27:37 -0500 |
---|---|---|
committer | Paul Mundt <lethal@hera.kernel.org> | 2007-05-06 22:10:51 -0400 |
commit | fa5da2f7bdcf885efe65a37df13907c7d72296f6 (patch) | |
tree | 54104d5f660a1ec824505b28540eb2c5e4be390a /arch/sh/kernel/kgdb_stub.c | |
parent | 15700770ef7c5d12e2f1659d2ddbeb3f658d9f37 (diff) |
sh: Bring kgdb back from the dead.
This code has suffered quite a bit of bitrot, do some basic
tidying to get it to a reasonably functional state again.
This gets the basic support and the console working again.
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/sh/kernel/kgdb_stub.c')
-rw-r--r-- | arch/sh/kernel/kgdb_stub.c | 106 |
1 files changed, 54 insertions, 52 deletions
diff --git a/arch/sh/kernel/kgdb_stub.c b/arch/sh/kernel/kgdb_stub.c index d8927d85492e..737eadc8ce0f 100644 --- a/arch/sh/kernel/kgdb_stub.c +++ b/arch/sh/kernel/kgdb_stub.c | |||
@@ -6,11 +6,11 @@ | |||
6 | * David Grothe <dave@gcom.com>, Tigran Aivazian <tigran@sco.com>, | 6 | * David Grothe <dave@gcom.com>, Tigran Aivazian <tigran@sco.com>, |
7 | * Amit S. Kale <akale@veritas.com>, William Gatliff <bgat@open-widgets.com>, | 7 | * Amit S. Kale <akale@veritas.com>, William Gatliff <bgat@open-widgets.com>, |
8 | * Ben Lee, Steve Chamberlain and Benoit Miller <fulg@iname.com>. | 8 | * Ben Lee, Steve Chamberlain and Benoit Miller <fulg@iname.com>. |
9 | * | 9 | * |
10 | * This version by Henry Bell <henry.bell@st.com> | 10 | * This version by Henry Bell <henry.bell@st.com> |
11 | * Minor modifications by Jeremy Siegel <jsiegel@mvista.com> | 11 | * Minor modifications by Jeremy Siegel <jsiegel@mvista.com> |
12 | * | 12 | * |
13 | * Contains low-level support for remote debug using GDB. | 13 | * Contains low-level support for remote debug using GDB. |
14 | * | 14 | * |
15 | * To enable debugger support, two things need to happen. A call to | 15 | * To enable debugger support, two things need to happen. A call to |
16 | * set_debug_traps() is necessary in order to allow any breakpoints | 16 | * set_debug_traps() is necessary in order to allow any breakpoints |
@@ -48,7 +48,7 @@ | |||
48 | * k kill (Detach GDB) | 48 | * k kill (Detach GDB) |
49 | * | 49 | * |
50 | * d Toggle debug flag | 50 | * d Toggle debug flag |
51 | * D Detach GDB | 51 | * D Detach GDB |
52 | * | 52 | * |
53 | * Hct Set thread t for operations, OK or ENN | 53 | * Hct Set thread t for operations, OK or ENN |
54 | * c = 'c' (step, cont), c = 'g' (other | 54 | * c = 'c' (step, cont), c = 'g' (other |
@@ -58,7 +58,7 @@ | |||
58 | * qfThreadInfo Get list of current threads (first) m<id> | 58 | * qfThreadInfo Get list of current threads (first) m<id> |
59 | * qsThreadInfo " " " " " (subsequent) | 59 | * qsThreadInfo " " " " " (subsequent) |
60 | * qOffsets Get section offsets Text=x;Data=y;Bss=z | 60 | * qOffsets Get section offsets Text=x;Data=y;Bss=z |
61 | * | 61 | * |
62 | * TXX Find if thread XX is alive OK or ENN | 62 | * TXX Find if thread XX is alive OK or ENN |
63 | * ? What was the last sigval ? SNN (signal NN) | 63 | * ? What was the last sigval ? SNN (signal NN) |
64 | * O Output to GDB console | 64 | * O Output to GDB console |
@@ -74,7 +74,7 @@ | |||
74 | * '$' or '#'. If <data> starts with two characters followed by | 74 | * '$' or '#'. If <data> starts with two characters followed by |
75 | * ':', then the existing stubs interpret this as a sequence number. | 75 | * ':', then the existing stubs interpret this as a sequence number. |
76 | * | 76 | * |
77 | * CSUM1 and CSUM2 are ascii hex representation of an 8-bit | 77 | * CSUM1 and CSUM2 are ascii hex representation of an 8-bit |
78 | * checksum of <data>, the most significant nibble is sent first. | 78 | * checksum of <data>, the most significant nibble is sent first. |
79 | * the hex digits 0-9,a-f are used. | 79 | * the hex digits 0-9,a-f are used. |
80 | * | 80 | * |
@@ -86,8 +86,8 @@ | |||
86 | * Responses can be run-length encoded to save space. A '*' means that | 86 | * Responses can be run-length encoded to save space. A '*' means that |
87 | * the next character is an ASCII encoding giving a repeat count which | 87 | * the next character is an ASCII encoding giving a repeat count which |
88 | * stands for that many repititions of the character preceding the '*'. | 88 | * stands for that many repititions of the character preceding the '*'. |
89 | * The encoding is n+29, yielding a printable character where n >=3 | 89 | * The encoding is n+29, yielding a printable character where n >=3 |
90 | * (which is where RLE starts to win). Don't use an n > 126. | 90 | * (which is where RLE starts to win). Don't use an n > 126. |
91 | * | 91 | * |
92 | * So "0* " means the same as "0000". | 92 | * So "0* " means the same as "0000". |
93 | */ | 93 | */ |
@@ -100,12 +100,10 @@ | |||
100 | #include <linux/delay.h> | 100 | #include <linux/delay.h> |
101 | #include <linux/linkage.h> | 101 | #include <linux/linkage.h> |
102 | #include <linux/init.h> | 102 | #include <linux/init.h> |
103 | |||
104 | #ifdef CONFIG_SH_KGDB_CONSOLE | ||
105 | #include <linux/console.h> | 103 | #include <linux/console.h> |
106 | #endif | 104 | #include <linux/sysrq.h> |
107 | |||
108 | #include <asm/system.h> | 105 | #include <asm/system.h> |
106 | #include <asm/cacheflush.h> | ||
109 | #include <asm/current.h> | 107 | #include <asm/current.h> |
110 | #include <asm/signal.h> | 108 | #include <asm/signal.h> |
111 | #include <asm/pgtable.h> | 109 | #include <asm/pgtable.h> |
@@ -153,7 +151,6 @@ char kgdb_in_gdb_mode; | |||
153 | char in_nmi; /* Set during NMI to prevent reentry */ | 151 | char in_nmi; /* Set during NMI to prevent reentry */ |
154 | int kgdb_nofault; /* Boolean to ignore bus errs (i.e. in GDB) */ | 152 | int kgdb_nofault; /* Boolean to ignore bus errs (i.e. in GDB) */ |
155 | int kgdb_enabled = 1; /* Default to enabled, cmdline can disable */ | 153 | int kgdb_enabled = 1; /* Default to enabled, cmdline can disable */ |
156 | int kgdb_halt; | ||
157 | 154 | ||
158 | /* Exposed for user access */ | 155 | /* Exposed for user access */ |
159 | struct task_struct *kgdb_current; | 156 | struct task_struct *kgdb_current; |
@@ -328,7 +325,7 @@ static int hex_to_int(char **ptr, int *int_value) | |||
328 | } | 325 | } |
329 | 326 | ||
330 | /* Copy the binary array pointed to by buf into mem. Fix $, #, | 327 | /* Copy the binary array pointed to by buf into mem. Fix $, #, |
331 | and 0x7d escaped with 0x7d. Return a pointer to the character | 328 | and 0x7d escaped with 0x7d. Return a pointer to the character |
332 | after the last byte written. */ | 329 | after the last byte written. */ |
333 | static char *ebin_to_mem(const char *buf, char *mem, int count) | 330 | static char *ebin_to_mem(const char *buf, char *mem, int count) |
334 | { | 331 | { |
@@ -452,7 +449,7 @@ static void get_packet(char *buffer, int buflen) | |||
452 | /* Ack successful transfer */ | 449 | /* Ack successful transfer */ |
453 | put_debug_char('+'); | 450 | put_debug_char('+'); |
454 | 451 | ||
455 | /* If a sequence char is present, reply | 452 | /* If a sequence char is present, reply |
456 | the sequence ID */ | 453 | the sequence ID */ |
457 | if (buffer[2] == ':') { | 454 | if (buffer[2] == ':') { |
458 | put_debug_char(buffer[0]); | 455 | put_debug_char(buffer[0]); |
@@ -759,7 +756,7 @@ static short *get_step_address(void) | |||
759 | return (short *) addr; | 756 | return (short *) addr; |
760 | } | 757 | } |
761 | 758 | ||
762 | /* Set up a single-step. Replace the instruction immediately after the | 759 | /* Set up a single-step. Replace the instruction immediately after the |
763 | current instruction (i.e. next in the expected flow of control) with a | 760 | current instruction (i.e. next in the expected flow of control) with a |
764 | trap instruction, so that returning will cause only a single instruction | 761 | trap instruction, so that returning will cause only a single instruction |
765 | to be executed. Note that this model is slightly broken for instructions | 762 | to be executed. Note that this model is slightly broken for instructions |
@@ -1002,10 +999,8 @@ void set_thread_msg(void) | |||
1002 | char *ptr; | 999 | char *ptr; |
1003 | 1000 | ||
1004 | switch (in_buffer[1]) { | 1001 | switch (in_buffer[1]) { |
1005 | 1002 | /* To select which thread for gG etc messages, i.e. supported */ | |
1006 | /* To select which thread for gG etc messages, i.e. supported */ | ||
1007 | case 'g': | 1003 | case 'g': |
1008 | |||
1009 | ptr = &in_buffer[2]; | 1004 | ptr = &in_buffer[2]; |
1010 | hex_to_int(&ptr, &threadid); | 1005 | hex_to_int(&ptr, &threadid); |
1011 | thread = get_thread(threadid); | 1006 | thread = get_thread(threadid); |
@@ -1173,6 +1168,7 @@ static void query_msg(void) | |||
1173 | } | 1168 | } |
1174 | #endif /* CONFIG_KGDB_THREAD */ | 1169 | #endif /* CONFIG_KGDB_THREAD */ |
1175 | 1170 | ||
1171 | #ifdef CONFIG_SH_KGDB_CONSOLE | ||
1176 | /* | 1172 | /* |
1177 | * Bring up the ports.. | 1173 | * Bring up the ports.. |
1178 | */ | 1174 | */ |
@@ -1185,6 +1181,9 @@ static int kgdb_serial_setup(void) | |||
1185 | 1181 | ||
1186 | return 0; | 1182 | return 0; |
1187 | } | 1183 | } |
1184 | #else | ||
1185 | #define kgdb_serial_setup() 0 | ||
1186 | #endif | ||
1188 | 1187 | ||
1189 | /* The command loop, read and act on requests */ | 1188 | /* The command loop, read and act on requests */ |
1190 | static void kgdb_command_loop(const int excep_code, const int trapa_value) | 1189 | static void kgdb_command_loop(const int excep_code, const int trapa_value) |
@@ -1193,7 +1192,7 @@ static void kgdb_command_loop(const int excep_code, const int trapa_value) | |||
1193 | 1192 | ||
1194 | if (excep_code == NMI_VEC) { | 1193 | if (excep_code == NMI_VEC) { |
1195 | #ifndef CONFIG_KGDB_NMI | 1194 | #ifndef CONFIG_KGDB_NMI |
1196 | KGDB_PRINTK("Ignoring unexpected NMI?\n"); | 1195 | printk(KERN_NOTICE "KGDB: Ignoring unexpected NMI?\n"); |
1197 | return; | 1196 | return; |
1198 | #else /* CONFIG_KGDB_NMI */ | 1197 | #else /* CONFIG_KGDB_NMI */ |
1199 | if (!kgdb_enabled) { | 1198 | if (!kgdb_enabled) { |
@@ -1216,10 +1215,7 @@ static void kgdb_command_loop(const int excep_code, const int trapa_value) | |||
1216 | /* Enter GDB mode (e.g. after detach) */ | 1215 | /* Enter GDB mode (e.g. after detach) */ |
1217 | if (!kgdb_in_gdb_mode) { | 1216 | if (!kgdb_in_gdb_mode) { |
1218 | /* Do serial setup, notify user, issue preemptive ack */ | 1217 | /* Do serial setup, notify user, issue preemptive ack */ |
1219 | kgdb_serial_setup(); | 1218 | printk(KERN_NOTICE "KGDB: Waiting for GDB\n"); |
1220 | KGDB_PRINTK("Waiting for GDB (on %s%d at %d baud)\n", | ||
1221 | (kgdb_porttype ? kgdb_porttype->name : ""), | ||
1222 | kgdb_portnum, kgdb_baud); | ||
1223 | kgdb_in_gdb_mode = 1; | 1219 | kgdb_in_gdb_mode = 1; |
1224 | put_debug_char('+'); | 1220 | put_debug_char('+'); |
1225 | } | 1221 | } |
@@ -1233,21 +1229,18 @@ static void kgdb_command_loop(const int excep_code, const int trapa_value) | |||
1233 | will later be replaced by its original one. Do NOT do this for | 1229 | will later be replaced by its original one. Do NOT do this for |
1234 | trap 0xff, since that indicates a compiled-in breakpoint which | 1230 | trap 0xff, since that indicates a compiled-in breakpoint which |
1235 | will not be replaced (and we would retake the trap forever) */ | 1231 | will not be replaced (and we would retake the trap forever) */ |
1236 | if ((excep_code == TRAP_VEC) && (trapa_value != (0xff << 2))) { | 1232 | if ((excep_code == TRAP_VEC) && (trapa_value != (0x3c << 2))) |
1237 | trap_registers.pc -= 2; | 1233 | trap_registers.pc -= 2; |
1238 | } | ||
1239 | 1234 | ||
1240 | /* Undo any stepping we may have done */ | 1235 | /* Undo any stepping we may have done */ |
1241 | undo_single_step(); | 1236 | undo_single_step(); |
1242 | 1237 | ||
1243 | while (1) { | 1238 | while (1) { |
1244 | |||
1245 | out_buffer[0] = 0; | 1239 | out_buffer[0] = 0; |
1246 | get_packet(in_buffer, BUFMAX); | 1240 | get_packet(in_buffer, BUFMAX); |
1247 | 1241 | ||
1248 | /* Examine first char of buffer to see what we need to do */ | 1242 | /* Examine first char of buffer to see what we need to do */ |
1249 | switch (in_buffer[0]) { | 1243 | switch (in_buffer[0]) { |
1250 | |||
1251 | case '?': /* Send which signal we've received */ | 1244 | case '?': /* Send which signal we've received */ |
1252 | send_signal_msg(sigval); | 1245 | send_signal_msg(sigval); |
1253 | break; | 1246 | break; |
@@ -1323,11 +1316,8 @@ static void kgdb_command_loop(const int excep_code, const int trapa_value) | |||
1323 | } | 1316 | } |
1324 | 1317 | ||
1325 | /* There has been an exception, most likely a breakpoint. */ | 1318 | /* There has been an exception, most likely a breakpoint. */ |
1326 | asmlinkage void kgdb_handle_exception(unsigned long r4, unsigned long r5, | 1319 | static void handle_exception(struct pt_regs *regs) |
1327 | unsigned long r6, unsigned long r7, | ||
1328 | struct pt_regs __regs) | ||
1329 | { | 1320 | { |
1330 | struct pt_regs *regs = RELOC_HIDE(&__regs, 0); | ||
1331 | int excep_code, vbr_val; | 1321 | int excep_code, vbr_val; |
1332 | int count; | 1322 | int count; |
1333 | int trapa_value = ctrl_inl(TRA); | 1323 | int trapa_value = ctrl_inl(TRA); |
@@ -1355,7 +1345,7 @@ asmlinkage void kgdb_handle_exception(unsigned long r4, unsigned long r5, | |||
1355 | kgdb_trapa_val = trapa_value; | 1345 | kgdb_trapa_val = trapa_value; |
1356 | 1346 | ||
1357 | /* Act on the exception */ | 1347 | /* Act on the exception */ |
1358 | kgdb_command_loop(excep_code >> 5, trapa_value); | 1348 | kgdb_command_loop(excep_code, trapa_value); |
1359 | 1349 | ||
1360 | kgdb_current = NULL; | 1350 | kgdb_current = NULL; |
1361 | 1351 | ||
@@ -1373,14 +1363,12 @@ asmlinkage void kgdb_handle_exception(unsigned long r4, unsigned long r5, | |||
1373 | asm("ldc %0, vbr": :"r"(vbr_val)); | 1363 | asm("ldc %0, vbr": :"r"(vbr_val)); |
1374 | } | 1364 | } |
1375 | 1365 | ||
1376 | /* Trigger a breakpoint by function */ | 1366 | asmlinkage void kgdb_handle_exception(unsigned long r4, unsigned long r5, |
1377 | void breakpoint(void) | 1367 | unsigned long r6, unsigned long r7, |
1368 | struct pt_regs __regs) | ||
1378 | { | 1369 | { |
1379 | if (!kgdb_enabled) { | 1370 | struct pt_regs *regs = RELOC_HIDE(&__regs, 0); |
1380 | kgdb_enabled = 1; | 1371 | handle_exception(regs); |
1381 | kgdb_init(); | ||
1382 | } | ||
1383 | BREAKPOINT(); | ||
1384 | } | 1372 | } |
1385 | 1373 | ||
1386 | /* Initialise the KGDB data structures and serial configuration */ | 1374 | /* Initialise the KGDB data structures and serial configuration */ |
@@ -1395,24 +1383,16 @@ int kgdb_init(void) | |||
1395 | kgdb_in_gdb_mode = 0; | 1383 | kgdb_in_gdb_mode = 0; |
1396 | 1384 | ||
1397 | if (kgdb_serial_setup() != 0) { | 1385 | if (kgdb_serial_setup() != 0) { |
1398 | KGDB_PRINTK("serial setup error\n"); | 1386 | printk(KERN_NOTICE "KGDB: serial setup error\n"); |
1399 | return -1; | 1387 | return -1; |
1400 | } | 1388 | } |
1401 | 1389 | ||
1402 | /* Init ptr to exception handler */ | 1390 | /* Init ptr to exception handler */ |
1403 | kgdb_debug_hook = kgdb_handle_exception; | 1391 | kgdb_debug_hook = handle_exception; |
1404 | kgdb_bus_err_hook = kgdb_handle_bus_error; | 1392 | kgdb_bus_err_hook = kgdb_handle_bus_error; |
1405 | 1393 | ||
1406 | /* Enter kgdb now if requested, or just report init done */ | 1394 | /* Enter kgdb now if requested, or just report init done */ |
1407 | if (kgdb_halt) { | 1395 | printk(KERN_NOTICE "KGDB: stub is initialized.\n"); |
1408 | kgdb_in_gdb_mode = 1; | ||
1409 | put_debug_char('+'); | ||
1410 | breakpoint(); | ||
1411 | } | ||
1412 | else | ||
1413 | { | ||
1414 | KGDB_PRINTK("stub is initialized.\n"); | ||
1415 | } | ||
1416 | 1396 | ||
1417 | return 0; | 1397 | return 0; |
1418 | } | 1398 | } |
@@ -1437,7 +1417,7 @@ static void kgdb_msg_write(const char *s, unsigned count) | |||
1437 | 1417 | ||
1438 | /* Calculate how many this time */ | 1418 | /* Calculate how many this time */ |
1439 | wcount = (count > MAXOUT) ? MAXOUT : count; | 1419 | wcount = (count > MAXOUT) ? MAXOUT : count; |
1440 | 1420 | ||
1441 | /* Pack in hex chars */ | 1421 | /* Pack in hex chars */ |
1442 | for (i = 0; i < wcount; i++) | 1422 | for (i = 0; i < wcount; i++) |
1443 | bufptr = pack_hex_byte(bufptr, s[i]); | 1423 | bufptr = pack_hex_byte(bufptr, s[i]); |
@@ -1467,3 +1447,25 @@ void kgdb_console_write(struct console *co, const char *s, unsigned count) | |||
1467 | kgdb_msg_write(s, count); | 1447 | kgdb_msg_write(s, count); |
1468 | } | 1448 | } |
1469 | #endif | 1449 | #endif |
1450 | |||
1451 | #ifdef CONFIG_KGDB_SYSRQ | ||
1452 | static void sysrq_handle_gdb(int key, struct tty_struct *tty) | ||
1453 | { | ||
1454 | printk("Entering GDB stub\n"); | ||
1455 | breakpoint(); | ||
1456 | } | ||
1457 | |||
1458 | static struct sysrq_key_op sysrq_gdb_op = { | ||
1459 | .handler = sysrq_handle_gdb, | ||
1460 | .help_msg = "Gdb", | ||
1461 | .action_msg = "GDB", | ||
1462 | }; | ||
1463 | |||
1464 | static int gdb_register_sysrq(void) | ||
1465 | { | ||
1466 | printk("Registering GDB sysrq handler\n"); | ||
1467 | register_sysrq_key('g', &sysrq_gdb_op); | ||
1468 | return 0; | ||
1469 | } | ||
1470 | module_init(gdb_register_sysrq); | ||
1471 | #endif | ||