|
1 /* |
|
2 * qemu user main |
|
3 * |
|
4 * Copyright (c) 2003-2008 Fabrice Bellard |
|
5 * |
|
6 * This program is free software; you can redistribute it and/or modify |
|
7 * it 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 * This program is distributed in the hope that it will be useful, |
|
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
14 * GNU General Public License for more details. |
|
15 * |
|
16 * You should have received a copy of the GNU General Public License |
|
17 * along with this program; if not, write to the Free Software |
|
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
|
19 */ |
|
20 #include <stdlib.h> |
|
21 #include <stdio.h> |
|
22 #include <stdarg.h> |
|
23 #include <string.h> |
|
24 #include <errno.h> |
|
25 #include <unistd.h> |
|
26 #include <machine/trap.h> |
|
27 |
|
28 #include "qemu.h" |
|
29 #include "qemu-common.h" |
|
30 /* For tb_lock */ |
|
31 #include "exec-all.h" |
|
32 |
|
33 #define DEBUG_LOGFILE "/tmp/qemu.log" |
|
34 |
|
35 static const char *interp_prefix = CONFIG_QEMU_PREFIX; |
|
36 const char *qemu_uname_release = CONFIG_UNAME_RELEASE; |
|
37 extern char **environ; |
|
38 |
|
39 /* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so |
|
40 we allocate a bigger stack. Need a better solution, for example |
|
41 by remapping the process stack directly at the right place */ |
|
42 unsigned long x86_stack_size = 512 * 1024; |
|
43 |
|
44 void gemu_log(const char *fmt, ...) |
|
45 { |
|
46 va_list ap; |
|
47 |
|
48 va_start(ap, fmt); |
|
49 vfprintf(stderr, fmt, ap); |
|
50 va_end(ap); |
|
51 } |
|
52 #ifdef TARGET_SPARC |
|
53 #define SPARC64_STACK_BIAS 2047 |
|
54 |
|
55 //#define DEBUG_WIN |
|
56 /* WARNING: dealing with register windows _is_ complicated. More info |
|
57 can be found at http://www.sics.se/~psm/sparcstack.html */ |
|
58 static inline int get_reg_index(CPUSPARCState *env, int cwp, int index) |
|
59 { |
|
60 index = (index + cwp * 16) % (16 * env->nwindows); |
|
61 /* wrap handling : if cwp is on the last window, then we use the |
|
62 registers 'after' the end */ |
|
63 if (index < 8 && env->cwp == env->nwindows - 1) |
|
64 index += 16 * env->nwindows; |
|
65 return index; |
|
66 } |
|
67 |
|
68 /* save the register window 'cwp1' */ |
|
69 static inline void save_window_offset(CPUSPARCState *env, int cwp1) |
|
70 { |
|
71 unsigned int i; |
|
72 abi_ulong sp_ptr; |
|
73 |
|
74 sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)]; |
|
75 #ifdef TARGET_SPARC64 |
|
76 if (sp_ptr & 3) |
|
77 sp_ptr += SPARC64_STACK_BIAS; |
|
78 #endif |
|
79 #if defined(DEBUG_WIN) |
|
80 printf("win_overflow: sp_ptr=0x" TARGET_ABI_FMT_lx " save_cwp=%d\n", |
|
81 sp_ptr, cwp1); |
|
82 #endif |
|
83 for(i = 0; i < 16; i++) { |
|
84 /* FIXME - what to do if put_user() fails? */ |
|
85 put_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr); |
|
86 sp_ptr += sizeof(abi_ulong); |
|
87 } |
|
88 } |
|
89 |
|
90 static void save_window(CPUSPARCState *env) |
|
91 { |
|
92 #ifndef TARGET_SPARC64 |
|
93 unsigned int new_wim; |
|
94 new_wim = ((env->wim >> 1) | (env->wim << (env->nwindows - 1))) & |
|
95 ((1LL << env->nwindows) - 1); |
|
96 save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2)); |
|
97 env->wim = new_wim; |
|
98 #else |
|
99 save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2)); |
|
100 env->cansave++; |
|
101 env->canrestore--; |
|
102 #endif |
|
103 } |
|
104 |
|
105 static void restore_window(CPUSPARCState *env) |
|
106 { |
|
107 #ifndef TARGET_SPARC64 |
|
108 unsigned int new_wim; |
|
109 #endif |
|
110 unsigned int i, cwp1; |
|
111 abi_ulong sp_ptr; |
|
112 |
|
113 #ifndef TARGET_SPARC64 |
|
114 new_wim = ((env->wim << 1) | (env->wim >> (env->nwindows - 1))) & |
|
115 ((1LL << env->nwindows) - 1); |
|
116 #endif |
|
117 |
|
118 /* restore the invalid window */ |
|
119 cwp1 = cpu_cwp_inc(env, env->cwp + 1); |
|
120 sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)]; |
|
121 #ifdef TARGET_SPARC64 |
|
122 if (sp_ptr & 3) |
|
123 sp_ptr += SPARC64_STACK_BIAS; |
|
124 #endif |
|
125 #if defined(DEBUG_WIN) |
|
126 printf("win_underflow: sp_ptr=0x" TARGET_ABI_FMT_lx " load_cwp=%d\n", |
|
127 sp_ptr, cwp1); |
|
128 #endif |
|
129 for(i = 0; i < 16; i++) { |
|
130 /* FIXME - what to do if get_user() fails? */ |
|
131 get_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr); |
|
132 sp_ptr += sizeof(abi_ulong); |
|
133 } |
|
134 #ifdef TARGET_SPARC64 |
|
135 env->canrestore++; |
|
136 if (env->cleanwin < env->nwindows - 1) |
|
137 env->cleanwin++; |
|
138 env->cansave--; |
|
139 #else |
|
140 env->wim = new_wim; |
|
141 #endif |
|
142 } |
|
143 |
|
144 static void flush_windows(CPUSPARCState *env) |
|
145 { |
|
146 int offset, cwp1; |
|
147 |
|
148 offset = 1; |
|
149 for(;;) { |
|
150 /* if restore would invoke restore_window(), then we can stop */ |
|
151 cwp1 = cpu_cwp_inc(env, env->cwp + offset); |
|
152 #ifndef TARGET_SPARC64 |
|
153 if (env->wim & (1 << cwp1)) |
|
154 break; |
|
155 #else |
|
156 if (env->canrestore == 0) |
|
157 break; |
|
158 env->cansave++; |
|
159 env->canrestore--; |
|
160 #endif |
|
161 save_window_offset(env, cwp1); |
|
162 offset++; |
|
163 } |
|
164 cwp1 = cpu_cwp_inc(env, env->cwp + 1); |
|
165 #ifndef TARGET_SPARC64 |
|
166 /* set wim so that restore will reload the registers */ |
|
167 env->wim = 1 << cwp1; |
|
168 #endif |
|
169 #if defined(DEBUG_WIN) |
|
170 printf("flush_windows: nb=%d\n", offset - 1); |
|
171 #endif |
|
172 } |
|
173 |
|
174 void cpu_loop(CPUSPARCState *env, enum BSDType bsd_type) |
|
175 { |
|
176 int trapnr, ret, syscall_nr; |
|
177 //target_siginfo_t info; |
|
178 |
|
179 while (1) { |
|
180 trapnr = cpu_sparc_exec (env); |
|
181 |
|
182 switch (trapnr) { |
|
183 #ifndef TARGET_SPARC64 |
|
184 case 0x80: |
|
185 #else |
|
186 case 0x100: |
|
187 #endif |
|
188 syscall_nr = env->gregs[1]; |
|
189 if (bsd_type == target_freebsd) |
|
190 ret = do_freebsd_syscall(env, syscall_nr, |
|
191 env->regwptr[0], env->regwptr[1], |
|
192 env->regwptr[2], env->regwptr[3], |
|
193 env->regwptr[4], env->regwptr[5]); |
|
194 else if (bsd_type == target_netbsd) |
|
195 ret = do_netbsd_syscall(env, syscall_nr, |
|
196 env->regwptr[0], env->regwptr[1], |
|
197 env->regwptr[2], env->regwptr[3], |
|
198 env->regwptr[4], env->regwptr[5]); |
|
199 else { //if (bsd_type == target_openbsd) |
|
200 #if defined(TARGET_SPARC64) |
|
201 syscall_nr &= ~(TARGET_OPENBSD_SYSCALL_G7RFLAG | |
|
202 TARGET_OPENBSD_SYSCALL_G2RFLAG); |
|
203 #endif |
|
204 ret = do_openbsd_syscall(env, syscall_nr, |
|
205 env->regwptr[0], env->regwptr[1], |
|
206 env->regwptr[2], env->regwptr[3], |
|
207 env->regwptr[4], env->regwptr[5]); |
|
208 } |
|
209 if ((unsigned int)ret >= (unsigned int)(-515)) { |
|
210 #if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) |
|
211 env->xcc |= PSR_CARRY; |
|
212 #else |
|
213 env->psr |= PSR_CARRY; |
|
214 #endif |
|
215 } else { |
|
216 #if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) |
|
217 env->xcc &= ~PSR_CARRY; |
|
218 #else |
|
219 env->psr &= ~PSR_CARRY; |
|
220 #endif |
|
221 } |
|
222 env->regwptr[0] = ret; |
|
223 /* next instruction */ |
|
224 #if defined(TARGET_SPARC64) |
|
225 if (bsd_type == target_openbsd && |
|
226 env->gregs[1] & TARGET_OPENBSD_SYSCALL_G2RFLAG) { |
|
227 env->pc = env->gregs[2]; |
|
228 env->npc = env->pc + 4; |
|
229 } else if (bsd_type == target_openbsd && |
|
230 env->gregs[1] & TARGET_OPENBSD_SYSCALL_G7RFLAG) { |
|
231 env->pc = env->gregs[7]; |
|
232 env->npc = env->pc + 4; |
|
233 } else { |
|
234 env->pc = env->npc; |
|
235 env->npc = env->npc + 4; |
|
236 } |
|
237 #else |
|
238 env->pc = env->npc; |
|
239 env->npc = env->npc + 4; |
|
240 #endif |
|
241 break; |
|
242 case 0x83: /* flush windows */ |
|
243 #ifdef TARGET_ABI32 |
|
244 case 0x103: |
|
245 #endif |
|
246 flush_windows(env); |
|
247 /* next instruction */ |
|
248 env->pc = env->npc; |
|
249 env->npc = env->npc + 4; |
|
250 break; |
|
251 #ifndef TARGET_SPARC64 |
|
252 case TT_WIN_OVF: /* window overflow */ |
|
253 save_window(env); |
|
254 break; |
|
255 case TT_WIN_UNF: /* window underflow */ |
|
256 restore_window(env); |
|
257 break; |
|
258 case TT_TFAULT: |
|
259 case TT_DFAULT: |
|
260 #if 0 |
|
261 { |
|
262 info.si_signo = SIGSEGV; |
|
263 info.si_errno = 0; |
|
264 /* XXX: check env->error_code */ |
|
265 info.si_code = TARGET_SEGV_MAPERR; |
|
266 info._sifields._sigfault._addr = env->mmuregs[4]; |
|
267 queue_signal(env, info.si_signo, &info); |
|
268 } |
|
269 #endif |
|
270 break; |
|
271 #else |
|
272 case TT_SPILL: /* window overflow */ |
|
273 save_window(env); |
|
274 break; |
|
275 case TT_FILL: /* window underflow */ |
|
276 restore_window(env); |
|
277 break; |
|
278 case TT_TFAULT: |
|
279 case TT_DFAULT: |
|
280 #if 0 |
|
281 { |
|
282 info.si_signo = SIGSEGV; |
|
283 info.si_errno = 0; |
|
284 /* XXX: check env->error_code */ |
|
285 info.si_code = TARGET_SEGV_MAPERR; |
|
286 if (trapnr == TT_DFAULT) |
|
287 info._sifields._sigfault._addr = env->dmmuregs[4]; |
|
288 else |
|
289 info._sifields._sigfault._addr = env->tsptr->tpc; |
|
290 //queue_signal(env, info.si_signo, &info); |
|
291 } |
|
292 #endif |
|
293 break; |
|
294 #endif |
|
295 case EXCP_INTERRUPT: |
|
296 /* just indicate that signals should be handled asap */ |
|
297 break; |
|
298 case EXCP_DEBUG: |
|
299 { |
|
300 int sig; |
|
301 |
|
302 sig = gdb_handlesig (env, TARGET_SIGTRAP); |
|
303 #if 0 |
|
304 if (sig) |
|
305 { |
|
306 info.si_signo = sig; |
|
307 info.si_errno = 0; |
|
308 info.si_code = TARGET_TRAP_BRKPT; |
|
309 //queue_signal(env, info.si_signo, &info); |
|
310 } |
|
311 #endif |
|
312 } |
|
313 break; |
|
314 default: |
|
315 printf ("Unhandled trap: 0x%x\n", trapnr); |
|
316 cpu_dump_state(env, stderr, fprintf, 0); |
|
317 exit (1); |
|
318 } |
|
319 process_pending_signals (env); |
|
320 } |
|
321 } |
|
322 |
|
323 #endif |
|
324 |
|
325 static void usage(void) |
|
326 { |
|
327 printf("qemu-" TARGET_ARCH " version " QEMU_VERSION ", Copyright (c) 2003-2008 Fabrice Bellard\n" |
|
328 "usage: qemu-" TARGET_ARCH " [options] program [arguments...]\n" |
|
329 "BSD CPU emulator (compiled for %s emulation)\n" |
|
330 "\n" |
|
331 "Standard options:\n" |
|
332 "-h print this help\n" |
|
333 "-g port wait gdb connection to port\n" |
|
334 "-L path set the elf interpreter prefix (default=%s)\n" |
|
335 "-s size set the stack size in bytes (default=%ld)\n" |
|
336 "-cpu model select CPU (-cpu ? for list)\n" |
|
337 "-drop-ld-preload drop LD_PRELOAD for target process\n" |
|
338 "-bsd type select emulated BSD type FreeBSD/NetBSD/OpenBSD (default)\n" |
|
339 "\n" |
|
340 "Debug options:\n" |
|
341 "-d options activate log (logfile=%s)\n" |
|
342 "-p pagesize set the host page size to 'pagesize'\n" |
|
343 "-strace log system calls\n" |
|
344 "\n" |
|
345 "Environment variables:\n" |
|
346 "QEMU_STRACE Print system calls and arguments similar to the\n" |
|
347 " 'strace' program. Enable by setting to any value.\n" |
|
348 , |
|
349 TARGET_ARCH, |
|
350 interp_prefix, |
|
351 x86_stack_size, |
|
352 DEBUG_LOGFILE); |
|
353 _exit(1); |
|
354 } |
|
355 |
|
356 THREAD CPUState *thread_env; |
|
357 |
|
358 /* Assumes contents are already zeroed. */ |
|
359 void init_task_state(TaskState *ts) |
|
360 { |
|
361 int i; |
|
362 |
|
363 ts->used = 1; |
|
364 ts->first_free = ts->sigqueue_table; |
|
365 for (i = 0; i < MAX_SIGQUEUE_SIZE - 1; i++) { |
|
366 ts->sigqueue_table[i].next = &ts->sigqueue_table[i + 1]; |
|
367 } |
|
368 ts->sigqueue_table[i].next = NULL; |
|
369 } |
|
370 |
|
371 int main(int argc, char **argv) |
|
372 { |
|
373 const char *filename; |
|
374 const char *cpu_model; |
|
375 struct target_pt_regs regs1, *regs = ®s1; |
|
376 struct image_info info1, *info = &info1; |
|
377 TaskState ts1, *ts = &ts1; |
|
378 CPUState *env; |
|
379 int optind; |
|
380 const char *r; |
|
381 int gdbstub_port = 0; |
|
382 int drop_ld_preload = 0, environ_count = 0; |
|
383 char **target_environ, **wrk, **dst; |
|
384 enum BSDType bsd_type = target_openbsd; |
|
385 |
|
386 if (argc <= 1) |
|
387 usage(); |
|
388 |
|
389 /* init debug */ |
|
390 cpu_set_log_filename(DEBUG_LOGFILE); |
|
391 |
|
392 cpu_model = NULL; |
|
393 optind = 1; |
|
394 for(;;) { |
|
395 if (optind >= argc) |
|
396 break; |
|
397 r = argv[optind]; |
|
398 if (r[0] != '-') |
|
399 break; |
|
400 optind++; |
|
401 r++; |
|
402 if (!strcmp(r, "-")) { |
|
403 break; |
|
404 } else if (!strcmp(r, "d")) { |
|
405 int mask; |
|
406 const CPULogItem *item; |
|
407 |
|
408 if (optind >= argc) |
|
409 break; |
|
410 |
|
411 r = argv[optind++]; |
|
412 mask = cpu_str_to_log_mask(r); |
|
413 if (!mask) { |
|
414 printf("Log items (comma separated):\n"); |
|
415 for(item = cpu_log_items; item->mask != 0; item++) { |
|
416 printf("%-10s %s\n", item->name, item->help); |
|
417 } |
|
418 exit(1); |
|
419 } |
|
420 cpu_set_log(mask); |
|
421 } else if (!strcmp(r, "s")) { |
|
422 r = argv[optind++]; |
|
423 x86_stack_size = strtol(r, (char **)&r, 0); |
|
424 if (x86_stack_size <= 0) |
|
425 usage(); |
|
426 if (*r == 'M') |
|
427 x86_stack_size *= 1024 * 1024; |
|
428 else if (*r == 'k' || *r == 'K') |
|
429 x86_stack_size *= 1024; |
|
430 } else if (!strcmp(r, "L")) { |
|
431 interp_prefix = argv[optind++]; |
|
432 } else if (!strcmp(r, "p")) { |
|
433 qemu_host_page_size = atoi(argv[optind++]); |
|
434 if (qemu_host_page_size == 0 || |
|
435 (qemu_host_page_size & (qemu_host_page_size - 1)) != 0) { |
|
436 fprintf(stderr, "page size must be a power of two\n"); |
|
437 exit(1); |
|
438 } |
|
439 } else if (!strcmp(r, "g")) { |
|
440 gdbstub_port = atoi(argv[optind++]); |
|
441 } else if (!strcmp(r, "r")) { |
|
442 qemu_uname_release = argv[optind++]; |
|
443 } else if (!strcmp(r, "cpu")) { |
|
444 cpu_model = argv[optind++]; |
|
445 if (strcmp(cpu_model, "?") == 0) { |
|
446 /* XXX: implement xxx_cpu_list for targets that still miss it */ |
|
447 #if defined(cpu_list) |
|
448 cpu_list(stdout, &fprintf); |
|
449 #endif |
|
450 _exit(1); |
|
451 } |
|
452 } else if (!strcmp(r, "drop-ld-preload")) { |
|
453 drop_ld_preload = 1; |
|
454 } else if (!strcmp(r, "bsd")) { |
|
455 if (!strcasecmp(argv[optind], "freebsd")) { |
|
456 bsd_type = target_freebsd; |
|
457 } else if (!strcasecmp(argv[optind], "netbsd")) { |
|
458 bsd_type = target_netbsd; |
|
459 } else if (!strcasecmp(argv[optind], "openbsd")) { |
|
460 bsd_type = target_openbsd; |
|
461 } else { |
|
462 usage(); |
|
463 } |
|
464 optind++; |
|
465 } else if (!strcmp(r, "strace")) { |
|
466 do_strace = 1; |
|
467 } else |
|
468 { |
|
469 usage(); |
|
470 } |
|
471 } |
|
472 if (optind >= argc) |
|
473 usage(); |
|
474 filename = argv[optind]; |
|
475 |
|
476 /* Zero out regs */ |
|
477 memset(regs, 0, sizeof(struct target_pt_regs)); |
|
478 |
|
479 /* Zero out image_info */ |
|
480 memset(info, 0, sizeof(struct image_info)); |
|
481 |
|
482 /* Scan interp_prefix dir for replacement files. */ |
|
483 init_paths(interp_prefix); |
|
484 |
|
485 if (cpu_model == NULL) { |
|
486 #if defined(TARGET_SPARC) |
|
487 #ifdef TARGET_SPARC64 |
|
488 cpu_model = "TI UltraSparc II"; |
|
489 #else |
|
490 cpu_model = "Fujitsu MB86904"; |
|
491 #endif |
|
492 #else |
|
493 cpu_model = "any"; |
|
494 #endif |
|
495 } |
|
496 cpu_exec_init_all(0); |
|
497 /* NOTE: we need to init the CPU at this stage to get |
|
498 qemu_host_page_size */ |
|
499 env = cpu_init(cpu_model); |
|
500 if (!env) { |
|
501 fprintf(stderr, "Unable to find CPU definition\n"); |
|
502 exit(1); |
|
503 } |
|
504 thread_env = env; |
|
505 |
|
506 if (getenv("QEMU_STRACE")) { |
|
507 do_strace = 1; |
|
508 } |
|
509 |
|
510 wrk = environ; |
|
511 while (*(wrk++)) |
|
512 environ_count++; |
|
513 |
|
514 target_environ = malloc((environ_count + 1) * sizeof(char *)); |
|
515 if (!target_environ) |
|
516 abort(); |
|
517 for (wrk = environ, dst = target_environ; *wrk; wrk++) { |
|
518 if (drop_ld_preload && !strncmp(*wrk, "LD_PRELOAD=", 11)) |
|
519 continue; |
|
520 *(dst++) = strdup(*wrk); |
|
521 } |
|
522 *dst = NULL; /* NULL terminate target_environ */ |
|
523 |
|
524 if (loader_exec(filename, argv+optind, target_environ, regs, info) != 0) { |
|
525 printf("Error loading %s\n", filename); |
|
526 _exit(1); |
|
527 } |
|
528 |
|
529 for (wrk = target_environ; *wrk; wrk++) { |
|
530 free(*wrk); |
|
531 } |
|
532 |
|
533 free(target_environ); |
|
534 |
|
535 if (loglevel) { |
|
536 page_dump(logfile); |
|
537 |
|
538 fprintf(logfile, "start_brk 0x" TARGET_ABI_FMT_lx "\n", info->start_brk); |
|
539 fprintf(logfile, "end_code 0x" TARGET_ABI_FMT_lx "\n", info->end_code); |
|
540 fprintf(logfile, "start_code 0x" TARGET_ABI_FMT_lx "\n", |
|
541 info->start_code); |
|
542 fprintf(logfile, "start_data 0x" TARGET_ABI_FMT_lx "\n", |
|
543 info->start_data); |
|
544 fprintf(logfile, "end_data 0x" TARGET_ABI_FMT_lx "\n", info->end_data); |
|
545 fprintf(logfile, "start_stack 0x" TARGET_ABI_FMT_lx "\n", |
|
546 info->start_stack); |
|
547 fprintf(logfile, "brk 0x" TARGET_ABI_FMT_lx "\n", info->brk); |
|
548 fprintf(logfile, "entry 0x" TARGET_ABI_FMT_lx "\n", info->entry); |
|
549 } |
|
550 |
|
551 target_set_brk(info->brk); |
|
552 syscall_init(); |
|
553 signal_init(); |
|
554 |
|
555 /* build Task State */ |
|
556 memset(ts, 0, sizeof(TaskState)); |
|
557 init_task_state(ts); |
|
558 ts->info = info; |
|
559 env->opaque = ts; |
|
560 env->user_mode_only = 1; |
|
561 |
|
562 #if defined(TARGET_SPARC) |
|
563 { |
|
564 int i; |
|
565 env->pc = regs->pc; |
|
566 env->npc = regs->npc; |
|
567 env->y = regs->y; |
|
568 for(i = 0; i < 8; i++) |
|
569 env->gregs[i] = regs->u_regs[i]; |
|
570 for(i = 0; i < 8; i++) |
|
571 env->regwptr[i] = regs->u_regs[i + 8]; |
|
572 } |
|
573 #else |
|
574 #error unsupported target CPU |
|
575 #endif |
|
576 |
|
577 if (gdbstub_port) { |
|
578 gdbserver_start (gdbstub_port); |
|
579 gdb_handlesig(env, 0); |
|
580 } |
|
581 cpu_loop(env, bsd_type); |
|
582 /* never exits */ |
|
583 return 0; |
|
584 } |