1# Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003 Free Software
2# Foundation, Inc.
3#
4# This file is part of DejaGnu.
5#
6# DejaGnu is free software; you can redistribute it and/or modify it
7# under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2 of the License, or
9# (at your option) any later version.
10#
11# DejaGnu is distributed in the hope that it will be useful, but
12# WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14# General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with DejaGnu; if not, write to the Free Software Foundation,
18# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20# This file was written by Michael Snyder <msnyder@cygnus.com>.
21
22#
23# Stub remote run command.
24#
25
26proc gdb_stub_init { dest args } {
27    global gdb_prompt
28    global GDB
29    global tool_root_dir
30
31    if ![info exists GDB] then {
32	set GDB "[lookfor_file ${tool_root_dir} gdb/gdb]"
33	if { $GDB == "" } {
34	    set GDB [transform gdb]
35	}
36    }
37
38    if [board_info $dest exists gdb_prompt] {
39	set gdb_prompt [board_info $dest gdb_prompt]
40    } else {
41	set gdb_prompt "\\(gdb\\)"
42    }
43
44    return 1
45}
46
47proc gdb_stub_restart { dest } {
48    global gdb_prompt
49    global GDB
50
51    gdb_stub_init $dest
52
53    for { set x 1 } { $x < 4 } {incr x} {
54	remote_close $dest
55	sleep 2
56	set command "$GDB -nw -nx"
57	if [host_info exists gdb_opts] {
58	    append command " [host_info gdb_opts]"
59	}
60	set spawn_id [remote_spawn host $command]
61	remote_expect host 30 {
62	    -re "$gdb_prompt" { }
63	}
64	if { $spawn_id >= 0 } {
65	    if [board_info $dest exists baud] {
66		remote_send host "set remotebaud [board_info $dest baud]\n"
67		remote_expect host 5 {
68		    -re "$gdb_prompt" { }
69		    default {
70			warning "Error setting baud rate."
71			return -1
72		    }
73		}
74	    }
75
76
77	    set value [gdb_stub_startup $dest]
78	    if { $value > 0 } {
79		break
80	    }
81	    verbose "got $value from gdb_stub_startup"
82	    remote_send host "quit\n"
83	}
84	remote_reboot $dest
85    }
86    if { ${x} < 4 } {
87	global board_info
88	set name [board_info $dest name]
89
90	set board_info($name,gdb_is_running) 1
91	return 1
92    } else {
93	return 0
94    }
95}
96
97proc gdb_stub_remote_check { dest } {
98    global gdb_prompt
99
100    if [board_info $dest exists gdb_serial] {
101	set serial [board_info $dest gdb_serial]
102    } elseif [board_info $dest exists serial] {
103	set serial [board_info $dest serial]
104    } else {
105	set serial [board_info $dest netport]
106    }
107    remote_send host "target remote $serial\n"
108    remote_expect host 10 {
109	-re "Couldn't establish connection.*$gdb_prompt" {
110	    return 0
111	}
112	-re "Remote debugging.*$gdb_prompt" {
113	    verbose "stub is already running"
114	    return 1
115	}
116	-re "$gdb_prompt" {
117	    return 0
118	}
119	timeout {
120	    remote_send host "\003"
121	    remote_expect host 10 {
122		-re "$gdb_prompt" { }
123	    }
124	    return 0
125	}
126	default {
127	    return 0
128	}
129    }
130}
131
132proc gdb_stub_startup { dest } {
133    global gdb_prompt
134    global GDB
135
136    set is_running_stub 0
137
138    if [gdb_stub_remote_check $dest] {
139	set is_running_stub 1
140    }
141
142    if [board_info $dest exists serial] {
143        set serial [board_info $dest serial]
144    } else {
145	set serial [board_info $dest netport]
146    }
147
148    if { ! $is_running_stub } {
149	set command "target [board_info $dest gdb_protocol] $serial\n"
150	remote_send host $command
151	remote_expect host 5 {
152	    -re "already.*y or n." {
153		remote_send host "y\n"
154		exp_continue
155	    }
156	    -re "appears to be alive.*$gdb_prompt" { }
157	    -re "Remote target.*connected to.*$gdb_prompt" { }
158	    default {
159		return -1
160	    }
161	}
162    }
163    if { $is_running_stub == 0 } {
164	global libdir
165
166	verbose "building loader"
167	set loader "loader"
168	if ![file exists $loader] {
169	    if [board_info $dest exists gdb_stub_offset] {
170		set result [target_compile "${libdir}/stub-loader.c" $loader executable "libs=-Wl,-Ttext,[board_info $dest gdb_stub_offset]"]
171	    } else {
172		set result [target_compile "${libdir}/stub-loader.c" $loader executable "ldscript=[board_info $dest gdb_stub_ldscript]"]
173	    }
174	    verbose "result is $result"
175	    if [is_remote host] {
176		set loader [remote_download host $loader]
177	    }
178	}
179	remote_send host "file $loader\n"
180	remote_expect host 20 {
181	    -re "A program is being debug.*Kill it.*y or n. $" {
182		remote_send host "y\n"
183		exp_continue
184	    }
185	    -re "Load new symbol table.*y or n. $" {
186		remote_send host "y\n"
187		exp_continue
188	    }
189	    -re "Reading symbols from.*done..*$gdb_prompt $" {}
190	    -re "$gdb_prompt $" { warning "GDB couldn't find loader" }
191	    timeout {
192		warning "(timeout) read symbol file"
193		return -1
194	    }
195	}
196
197	if [board_info $dest exists serial] {
198	    set serial [board_info $dest serial]
199	} else {
200	    set serial [board_info $dest netport]
201	}
202	remote_send host "target [board_info $dest gdb_protocol] $serial\n"
203	remote_expect host 60 {
204	    -re "appears to be alive.*$gdb_prompt" { }
205	    -re "Remote target.*connected to.*$gdb_prompt" { }
206	    -re "$gdb_prompt" {
207		warning "Error reconnecting to stub."
208		return -1
209	    }
210	    default {
211		warning "Error reconnecting to stub."
212		return -1
213	    }
214	}
215
216	# We only send the offset if gdb_load_offset is set. Otherwise, we
217	# assume that sending the offset isn't needed.
218	if [board_info $dest exists gdb_load_offset] {
219	    remote_send host "load $loader [board_info $dest gdb_stub_offset]\n"
220	} else {
221	    remote_send host "load $loader\n"
222	}
223	verbose "Loading $loader into $GDB" 2
224	global verbose
225	set no_run_command 0
226	# FIXME: The value 1200 below should be a parameter.
227	remote_expect host 1200 {
228	    -re "Transfer rate:.*Switching to remote protocol.*Remote debugging" {
229		set no_run_command 1
230		remote_send host ""
231		sleep 2
232		remote_send host ""
233		sleep 1
234	    }
235	    -re "Loading.*Starting.*at.*$gdb_prompt $" {
236		verbose "Loaded $loader into $GDB" 1
237		set no_run_command 1
238	    }
239	    -re "Loading.*$gdb_prompt $" {
240		verbose "Loaded $loader into $GDB" 1
241	    }
242	    -re "$gdb_prompt $"     {
243		if $verbose>1 then {
244		    warning "GDB couldn't load."
245		}
246	    }
247	    timeout {
248		if $verbose>1 then {
249		    warning "Timed out trying to load $arg."
250		}
251	    }
252	}
253
254	if { ! $no_run_command } {
255	    remote_send host "run\n"
256	    remote_expect host 60 {
257		-re "A program is being debug.*Kill it.*y or n. $" {
258		    remote_send host "y\n"
259		    exp_continue
260		}
261		-re "The program being debugged .*y or n. $" {
262		    remote_send host "y\n"
263		    exp_continue
264		}
265		-re "Starting program:.*loader.*$" {
266		    verbose "Starting loader succeeded"
267		}
268		timeout {
269		    warning "(timeout) starting the loader"
270		    return -1
271		}
272		default {
273		    warning "error starting the loader"
274		}
275	    }
276	    sleep 2
277	    remote_send host ""
278	    sleep 1
279	    remote_send host ""
280	    verbose "Sent ^C^C"
281	    remote_expect host 30 {
282		-re "Give up .and stop debugging it.*$" {
283		    remote_send host "y\n"
284		    exp_continue
285		}
286		-re "$gdb_prompt $" {
287		    verbose "Running loader succeeded"
288		}
289		timeout {
290		    warning "(timeout) interrupting the loader"
291		    return -1
292		}
293		default {
294		    warning "error interrupting the loader"
295		}
296	    }
297	}
298	remote_send host "quit\n"
299	return [gdb_stub_restart $dest]
300    }
301    return 1
302}
303
304#
305# Delete all breakpoints and verify that they were deleted.  If anything
306# goes wrong we just exit.
307#
308proc gdb_stub_delete_breakpoints {} {
309    global gdb_prompt
310
311    remote_send host "delete breakpoints\n"
312    remote_expect host 10 {
313	-re "Delete all breakpoints.*y or n. $" {
314	    remote_send host "y\n"
315	    exp_continue
316	}
317	-re "$gdb_prompt $" { }
318	timeout { warning "Delete all breakpoints (timeout)" ; return -1}
319    }
320    remote_send host "info breakpoints\n"
321    remote_expect host 10 {
322	-re "No breakpoints or watchpoints..*$gdb_prompt $" {}
323	-re "$gdb_prompt $" { warning "breakpoints not deleted" ; return -1}
324	timeout { warning "info breakpoints (timeout)" ; return -1}
325    }
326    return 0
327}
328
329proc gdb_stub_go_idle { dest } {
330    gdb_stub_delete_breakpoints
331}
332
333proc gdb_stub_add_breakpoint { function args } {
334    global gdb_prompt
335
336    remote_send host "break $function\n"
337    remote_expect host 60 {
338	-re "Breakpoint (\[0-9\]+).*$gdb_prompt $" { return $expect_out(1,string) }
339	-re "Function.*not defined.*$gdb_prompt $" { return "undef" }
340	-re "No symbol table.*$gdb_prompt $" { return "undef" }
341	default {
342	    return "undef"
343	}
344    }
345}
346
347proc gdb_stub_start { dest } {
348    global gdb_prompt
349
350    set exit_brnum [gdb_stub_add_breakpoint _exit]
351    if { $exit_brnum == "undef" || [board_info $dest exists always_break_exit] } {
352	set exit_brnum [gdb_stub_add_breakpoint exit]
353    }
354    set abort_brnum [gdb_stub_add_breakpoint abort]
355
356    upvar #0 gdb_stub_info I
357    set I($dest,exit_brnum) $exit_brnum
358    set I($dest,abort_brnum) $abort_brnum
359
360    remote_send host "set \$fp=0\n"
361    remote_expect host 10 {
362	-re "$gdb_prompt" { }
363    }
364    # This is needed for the SparcLite. Whee.
365    if [board_info $dest exists gdb,start_symbol] {
366	set start_comm "jump *[board_info $dest gdb,start_symbol]\n"
367    } else {
368	set start_comm "jump *start\n"
369    }
370    remote_send host "break copyloop\n"
371    remote_expect host 10 {
372	-re "Breakpoint.*$gdb_prompt $" {
373	    set start_comm "continue\n"
374	}
375	-re "Function.*not defined.*$gdb_prompt $" { }
376	default { }
377    }
378    remote_send host $start_comm
379    remote_expect host 10 {
380	-re "y or n. $" {
381	    remote_send host "y\n"
382	    exp_continue
383	}
384	-re "Breakpoint.*in copyloop.*$gdb_prompt $" {
385	    remote_send host "jump relocd\n"
386	    exp_continue
387	}
388	-re "Continuing at.*\[\r\n\]" { }
389	default {
390	    return { "fail" "" }
391	}
392    }
393    return { "pass" "" }
394}
395
396proc gdb_stub_spawn { dest prog args } {
397    for { set x 0 } { $x < 3 } { incr x } {
398	if { [remote_ld $dest $prog] != 1 } {
399	    return [list "fail" "remote_ld failed"]
400	}
401
402	set result [gdb_stub_start $dest]
403	if { [lindex $result 0] != "pass" } {
404	    remote_reboot target
405	} else {
406	    return 666;		# does anyone use this value?
407	}
408    }
409    return -1
410}
411
412proc gdb_stub_wait { dest timeout } {
413    global gdb_prompt
414
415
416    upvar #0 gdb_stub_info I
417    set exit_brnum $I($dest,exit_brnum)
418    set abort_brnum $I($dest,abort_brnum)
419
420    remote_expect host $timeout {
421	-re "Breakpoint.*exit.*=0.*$gdb_prompt $" {
422	    gdb_stub_go_idle $dest
423	    return [list 0 ""]
424	}
425	-re "Breakpoint.*exit.*=\[1-9\]\[0-9\]*.*$gdb_prompt $" {
426	    gdb_stub_go_idle $dest
427	    return [list 0 ""]
428	}
429	-re "Breakpoint.*exit.*$gdb_prompt $" {
430	    gdb_stub_go_idle $dest
431	    return [list 0 ""]
432	}
433	-re "Breakpoint.*abort.*$gdb_prompt $" {
434	    gdb_stub_go_idle $dest
435	    return [list 1 ""]
436	}
437        -re " EXIT code 0.*$gdb_prompt $" {
438            gdb_stub_go_idle $dest
439            return [list 0 ""]
440        }
441        -re " EXIT code \[1-9]\[0-9]*.*$gdb_prompt $" {
442            gdb_stub_go_idle $dest
443            return [list 0 ""]
444        }
445        -re " EXIT code 4242.*$gdb_prompt $" {
446            gdb_stub_go_idle $dest
447            return [list 1 ""]
448        }
449 	-re "Program received.*$gdb_prompt $" {
450	    gdb_stub_go_idle $dest
451	    return [list 1 ""]
452	}
453	-re "Program exited.*$gdb_prompt $" {
454	    gdb_stub_go_idle $dest
455	    return [list 1 ""]
456	}
457	-re "Breakpoint $exit_brnum.*$gdb_prompt $" {
458	    gdb_stub_go_idle $dest
459	    return [list 0 ""]
460	}
461	-re "Breakpoint $abort_brnum.*$gdb_prompt $" {
462	    gdb_stub_go_idle $dest
463	    return [list 1 ""]
464	}
465	default {
466	    remote_close $dest
467	    remote_reboot $dest
468	    return [list -1 ""]
469	}
470    }
471    return [list -1 ""]
472}
473
474proc gdb_stub_load { dest prog args } {
475    global gdb_prompt
476    set argnames { "command-line arguments" "input file" "output file" }
477
478    for { set x 0 } { $x < [llength $args] } { incr x } {
479	if { [lindex $args $x] != "" } {
480	    return [list "unsupported" "no support for [lindex $argnames $x] on this target"]
481	}
482    }
483
484    set result [remote_spawn $dest $prog]
485
486    if { $result < 0 } {
487	return [list "fail" "remote_spawn failed"]
488    }
489
490    # FIXME: The value 120 should be a parameter.
491    set result [remote_wait $dest 120]
492    set status [lindex $result 0]
493    set output [lindex $result 1]
494
495    if { $status == 0 } {
496	return [list "pass" $output]
497    } else if { $status > 0 } {
498	return [list "fail" $output]
499    } else {
500	global gdb_stub_retry
501
502	if ![info exists gdb_stub_retry] {
503	    set gdb_stub_retry 1
504
505	    set result [eval gdb_stub_load \{$dest\} \{$prog\} $args]
506	    unset gdb_stub_retry
507	    return $result
508	} else {
509	    return [list "fail" $output]
510	}
511    }
512}
513
514
515#
516# gdb_stub_ld -- load PROG into the board
517#             Returns a 0 if there was an error,
518#                       1 if it loaded successfully.
519#
520proc gdb_stub_ld { dest prog } {
521    global gdb_prompt
522    global GDB
523
524    if ![board_info $dest exists gdb_is_running] {
525	if ![gdb_stub_restart $dest] {
526	    return 0
527	}
528    }
529
530    set loadfile [file tail $prog]
531    set loadpath [file dirname $prog]
532
533    remote_send host "file $prog\n"
534    remote_expect host 30 {
535	-re "A program is being debug.*Kill it.*y or n. $" {
536	    remote_send host "y\n"
537	    exp_continue
538	}
539        -re "Load new symbol table.*y or n. $" {
540	    remote_send host "y\n"
541	    exp_continue
542	}
543	-re "Reading symbols from.*done..*$gdb_prompt $" {}
544	-re "$gdb_prompt $" {
545	    # Hmmm...is retrying going to help? I kinda doubt it.
546	    warning "GDB couldn't read file"
547	    return [gdb_stub_retry_ld "$dest" "$prog"]
548	}
549	timeout {
550	    warning "(timeout) read symbol file"
551	    return [gdb_stub_retry_ld "$dest" "$prog"]
552	}
553    }
554
555    # just in case there are old breakpoints lying around.
556    gdb_stub_delete_breakpoints
557
558    if [board_info $dest exists gdb_serial] {
559	set serial [board_info $dest gdb_serial]
560    } elseif [board_info $dest exists serial] {
561	set serial [board_info $dest serial]
562    } else {
563	set serial [board_info $dest netport]
564    }
565
566    remote_send host "target remote $serial\n"
567    remote_expect host 60 {
568	-re "Kill it?.*y or n.*" {
569	    remote_send host "y\n"
570	    exp_continue
571	}
572	-re "$gdb_prompt $"	{
573	    verbose "Set remote target to $serial" 2
574	}
575	timeout {
576	    warning "Couldn't set remote target."
577	    return 0
578	}
579    }
580
581    if [board_info $dest exists gdb_load_offset] {
582	set offset "[board_info $dest gdb_load_offset]"
583    } else {
584	set offset ""
585    }
586    remote_send host "load $prog $offset\n"
587    verbose "Loading $prog into $GDB" 2
588    global verbose
589    remote_expect host 1200 {
590	-re "Loading.*$gdb_prompt $" {
591	    verbose "Loaded $prog into $GDB" 1
592	}
593	-re "$gdb_prompt $"     {
594	    if $verbose>1 then {
595		warning "GDB couldn't load."
596	    }
597	}
598	timeout {
599	    if $verbose>1 then {
600		perror "Timed out trying to load $prog."
601	    }
602	}
603    }
604    return 1
605}
606
607#
608# Retry the ld operation, but only once.
609#
610
611proc gdb_stub_retry_ld { dest prog } {
612    global gdb_stub_retry_ld
613
614    remote_reboot $dest
615    if [info exists gdb_stub_retry_ld] {
616	unset gdb_stub_retry_ld
617	return 0
618    } else {
619	set gdb_stub_retry_ld 1
620    }
621    gdb_stub_restart $dest
622    set status [gdb_stub_ld $dest $prog]
623    if [info exists gdb_stub_retry_ld] {
624	unset gdb_stub_retry_ld
625    }
626    return $status
627}
628
629proc gdb_stub_close { dest } {
630    global board_info
631    set name [board_info $dest name]
632    if [info exists board_info($name,gdb_is_running)] {
633	unset board_info($name,gdb_is_running)
634    }
635    return [remote_close host]
636}
637
638set_board_info protocol  "gdb_stub"
639