]> git.proxmox.com Git - mirror_qemu.git/blame - monitor.c
improved monitor: type check, expression evaluator, memory dump, disassembly
[mirror_qemu.git] / monitor.c
CommitLineData
9dc39cba
FB
1/*
2 * QEMU monitor
3 *
4 * Copyright (c) 2003-2004 Fabrice Bellard
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
9dc39cba 24#include "vl.h"
9307c4c1 25#include "disas.h"
9dc39cba
FB
26
27//#define DEBUG
28
9307c4c1
FB
29#ifndef offsetof
30#define offsetof(type, field) ((size_t) &((type *)0)->field)
31#endif
32
9dc39cba 33#define TERM_CMD_BUF_SIZE 4095
9dc39cba
FB
34
35#define IS_NORM 0
36#define IS_ESC 1
37#define IS_CSI 2
38
39#define printf do_not_use_printf
40
41static char term_cmd_buf[TERM_CMD_BUF_SIZE + 1];
42static int term_cmd_buf_index;
43static int term_cmd_buf_size;
44static int term_esc_state;
45static int term_esc_param;
46
9307c4c1
FB
47/*
48 * Supported types:
49 *
50 * 'F' filename
51 * 's' string (accept optional quote)
52 * 'i' integer
53 * '/' optional gdb-like print format (like "/10x")
54 *
55 * '?' optional type (for 'F', 's' and 'i')
56 *
57 */
58
9dc39cba
FB
59typedef struct term_cmd_t {
60 const char *name;
9307c4c1
FB
61 const char *args_type;
62 void (*handler)();
9dc39cba
FB
63 const char *params;
64 const char *help;
65} term_cmd_t;
66
67static term_cmd_t term_cmds[];
68static term_cmd_t info_cmds[];
69
70void term_printf(const char *fmt, ...)
71{
72 va_list ap;
73 va_start(ap, fmt);
74 vprintf(fmt, ap);
75 va_end(ap);
76}
77
78void term_flush(void)
79{
80 fflush(stdout);
81}
82
83static int compare_cmd(const char *name, const char *list)
84{
85 const char *p, *pstart;
86 int len;
87 len = strlen(name);
88 p = list;
89 for(;;) {
90 pstart = p;
91 p = strchr(p, '|');
92 if (!p)
93 p = pstart + strlen(pstart);
94 if ((p - pstart) == len && !memcmp(pstart, name, len))
95 return 1;
96 if (*p == '\0')
97 break;
98 p++;
99 }
100 return 0;
101}
102
103static void help_cmd1(term_cmd_t *cmds, const char *prefix, const char *name)
104{
105 term_cmd_t *cmd;
106
107 for(cmd = cmds; cmd->name != NULL; cmd++) {
108 if (!name || !strcmp(name, cmd->name))
109 term_printf("%s%s %s -- %s\n", prefix, cmd->name, cmd->params, cmd->help);
110 }
111}
112
113static void help_cmd(const char *name)
114{
115 if (name && !strcmp(name, "info")) {
116 help_cmd1(info_cmds, "info ", NULL);
117 } else {
118 help_cmd1(term_cmds, "", name);
f193c797
FB
119 if (name && !strcmp(name, "log")) {
120 CPULogItem *item;
121 term_printf("Log items (comma separated):\n");
122 term_printf("%-10s %s\n", "none", "remove all logs");
123 for(item = cpu_log_items; item->mask != 0; item++) {
124 term_printf("%-10s %s\n", item->name, item->help);
125 }
126 }
9dc39cba
FB
127 }
128}
129
9307c4c1 130static void do_help(const char *name)
9dc39cba 131{
9307c4c1 132 help_cmd(name);
9dc39cba
FB
133}
134
9307c4c1 135static void do_commit(void)
9dc39cba
FB
136{
137 int i;
138
139 for (i = 0; i < MAX_DISKS; i++) {
140 if (bs_table[i])
141 bdrv_commit(bs_table[i]);
142 }
143}
144
9307c4c1 145static void do_info(const char *item)
9dc39cba
FB
146{
147 term_cmd_t *cmd;
9dc39cba 148
9307c4c1 149 if (!item)
9dc39cba 150 goto help;
9dc39cba 151 for(cmd = info_cmds; cmd->name != NULL; cmd++) {
9307c4c1 152 if (compare_cmd(item, cmd->name))
9dc39cba
FB
153 goto found;
154 }
155 help:
9307c4c1 156 help_cmd("info");
9dc39cba
FB
157 return;
158 found:
9307c4c1 159 cmd->handler();
9dc39cba
FB
160}
161
9307c4c1 162static void do_info_network(void)
9dc39cba
FB
163{
164 int i, j;
165 NetDriverState *nd;
166
167 for(i = 0; i < nb_nics; i++) {
168 nd = &nd_table[i];
169 term_printf("%d: ifname=%s macaddr=", i, nd->ifname);
170 for(j = 0; j < 6; j++) {
171 if (j > 0)
172 term_printf(":");
173 term_printf("%02x", nd->macaddr[j]);
174 }
175 term_printf("\n");
176 }
177}
178
9307c4c1 179static void do_info_block(void)
9dc39cba
FB
180{
181 bdrv_info();
182}
183
9307c4c1
FB
184static void do_info_registers(void)
185{
186#ifdef TARGET_I386
187 cpu_dump_state(cpu_single_env, stdout, X86_DUMP_FPU | X86_DUMP_CCOP);
188#else
189 cpu_dump_state(cpu_single_env, stdout, 0);
190#endif
191}
192
193static void do_quit(void)
9dc39cba
FB
194{
195 exit(0);
196}
197
198static int eject_device(BlockDriverState *bs, int force)
199{
200 if (bdrv_is_inserted(bs)) {
201 if (!force) {
202 if (!bdrv_is_removable(bs)) {
203 term_printf("device is not removable\n");
204 return -1;
205 }
206 if (bdrv_is_locked(bs)) {
207 term_printf("device is locked\n");
208 return -1;
209 }
210 }
211 bdrv_close(bs);
212 }
213 return 0;
214}
215
9307c4c1 216static void do_eject(int force, const char *filename)
9dc39cba
FB
217{
218 BlockDriverState *bs;
9dc39cba 219
9307c4c1
FB
220 term_printf("%d %s\n", force, filename);
221
222 bs = bdrv_find(filename);
9dc39cba
FB
223 if (!bs) {
224 term_printf("device not found\n");
225 return;
226 }
227 eject_device(bs, force);
228}
229
9307c4c1 230static void do_change(const char *device, const char *filename)
9dc39cba
FB
231{
232 BlockDriverState *bs;
233
9307c4c1 234 bs = bdrv_find(device);
9dc39cba
FB
235 if (!bs) {
236 term_printf("device not found\n");
237 return;
238 }
239 if (eject_device(bs, 0) < 0)
240 return;
9307c4c1 241 bdrv_open(bs, filename, 0);
9dc39cba
FB
242}
243
9307c4c1 244static void do_screen_dump(const char *filename)
59a983b9 245{
9307c4c1 246 vga_screen_dump(filename);
59a983b9
FB
247}
248
9307c4c1 249static void do_log(const char *items)
f193c797
FB
250{
251 int mask;
252
9307c4c1 253 if (!strcmp(items, "none")) {
f193c797
FB
254 mask = 0;
255 } else {
9307c4c1 256 mask = cpu_str_to_log_mask(items);
f193c797 257 if (!mask) {
9307c4c1 258 help_cmd("log");
f193c797
FB
259 return;
260 }
261 }
262 cpu_set_log(mask);
263}
264
9307c4c1 265static void do_savevm(const char *filename)
8a7ddc38 266{
9307c4c1
FB
267 if (qemu_savevm(filename) < 0)
268 term_printf("I/O error when saving VM to '%s'\n", filename);
8a7ddc38
FB
269}
270
9307c4c1 271static void do_loadvm(const char *filename)
8a7ddc38 272{
9307c4c1
FB
273 if (qemu_loadvm(filename) < 0)
274 term_printf("I/O error when loading VM from '%s'\n", filename);
8a7ddc38
FB
275}
276
9307c4c1 277static void do_stop(void)
8a7ddc38
FB
278{
279 vm_stop(EXCP_INTERRUPT);
280}
281
9307c4c1 282static void do_cont(void)
8a7ddc38
FB
283{
284 vm_start();
285}
286
67b915a5 287#ifdef CONFIG_GDBSTUB
9307c4c1 288static void do_gdbserver(int has_port, int port)
8a7ddc38 289{
9307c4c1
FB
290 if (!has_port)
291 port = DEFAULT_GDBSTUB_PORT;
8a7ddc38
FB
292 if (gdbserver_start(port) < 0) {
293 qemu_printf("Could not open gdbserver socket on port %d\n", port);
294 } else {
295 qemu_printf("Waiting gdb connection on port %d\n", port);
296 }
297}
67b915a5 298#endif
8a7ddc38 299
9307c4c1
FB
300static void term_printc(int c)
301{
302 term_printf("'");
303 switch(c) {
304 case '\'':
305 term_printf("\\'");
306 break;
307 case '\\':
308 term_printf("\\\\");
309 break;
310 case '\n':
311 term_printf("\\n");
312 break;
313 case '\r':
314 term_printf("\\r");
315 break;
316 default:
317 if (c >= 32 && c <= 126) {
318 term_printf("%c", c);
319 } else {
320 term_printf("\\x%02x", c);
321 }
322 break;
323 }
324 term_printf("'");
325}
326
327static void memory_dump(int count, int format, int wsize,
328 target_ulong addr, int is_physical)
329{
330 int nb_per_line, l, line_size, i, max_digits, len;
331 uint8_t buf[16];
332 uint64_t v;
333
334 if (format == 'i') {
335 int flags;
336 flags = 0;
337#ifdef TARGET_I386
338 /* we use the current CS size */
339 if (!(cpu_single_env->segs[R_CS].flags & DESC_B_MASK))
340 flags = 1;
341#endif
342 monitor_disas(addr, count, is_physical, flags);
343 return;
344 }
345
346 len = wsize * count;
347 if (wsize == 1)
348 line_size = 8;
349 else
350 line_size = 16;
351 nb_per_line = line_size / wsize;
352 max_digits = 0;
353
354 switch(format) {
355 case 'o':
356 max_digits = (wsize * 8 + 2) / 3;
357 break;
358 default:
359 case 'x':
360 max_digits = (wsize * 8) / 4;
361 break;
362 case 'u':
363 case 'd':
364 max_digits = (wsize * 8 * 10 + 32) / 33;
365 break;
366 case 'c':
367 wsize = 1;
368 break;
369 }
370
371 while (len > 0) {
372 term_printf("0x%08x:", addr);
373 l = len;
374 if (l > line_size)
375 l = line_size;
376 if (is_physical) {
377 cpu_physical_memory_rw(addr, buf, l, 0);
378 } else {
379 cpu_memory_rw_debug(cpu_single_env, addr, buf, l, 0);
380 }
381 i = 0;
382 while (i < l) {
383 switch(wsize) {
384 default:
385 case 1:
386 v = ldub_raw(buf + i);
387 break;
388 case 2:
389 v = lduw_raw(buf + i);
390 break;
391 case 4:
392 v = ldl_raw(buf + i);
393 break;
394 case 8:
395 v = ldq_raw(buf + i);
396 break;
397 }
398 term_printf(" ");
399 switch(format) {
400 case 'o':
401 term_printf("%#*llo", max_digits, v);
402 break;
403 case 'x':
404 term_printf("0x%0*llx", max_digits, v);
405 break;
406 case 'u':
407 term_printf("%*llu", max_digits, v);
408 break;
409 case 'd':
410 term_printf("%*lld", max_digits, v);
411 break;
412 case 'c':
413 term_printc(v);
414 break;
415 }
416 i += wsize;
417 }
418 term_printf("\n");
419 addr += l;
420 len -= l;
421 }
422}
423
424static void do_memory_dump(int count, int format, int size, int addr)
425{
426 memory_dump(count, format, size, addr, 0);
427}
428
429static void do_physical_memory_dump(int count, int format, int size, int addr)
430{
431 memory_dump(count, format, size, addr, 1);
432}
433
434static void do_print(int count, int format, int size, int val)
435{
436 switch(format) {
437 case 'o':
438 term_printf("%#o", val);
439 break;
440 case 'x':
441 term_printf("%#x", val);
442 break;
443 case 'u':
444 term_printf("%u", val);
445 break;
446 default:
447 case 'd':
448 term_printf("%d", val);
449 break;
450 case 'c':
451 term_printc(val);
452 break;
453 }
454 term_printf("\n");
455}
456
9dc39cba 457static term_cmd_t term_cmds[] = {
9307c4c1 458 { "help|?", "s?", do_help,
9dc39cba 459 "[cmd]", "show the help" },
9307c4c1 460 { "commit", "", do_commit,
9dc39cba 461 "", "commit changes to the disk images (if -snapshot is used)" },
9307c4c1 462 { "info", "s?", do_info,
9dc39cba 463 "subcommand", "show various information about the system state" },
9307c4c1 464 { "q|quit", "", do_quit,
9dc39cba 465 "", "quit the emulator" },
9307c4c1 466 { "eject", "-fs", do_eject,
9dc39cba 467 "[-f] device", "eject a removable media (use -f to force it)" },
9307c4c1 468 { "change", "sF", do_change,
9dc39cba 469 "device filename", "change a removable media" },
9307c4c1 470 { "screendump", "F", do_screen_dump,
59a983b9 471 "filename", "save screen into PPM image 'filename'" },
9307c4c1 472 { "log", "s", do_log,
f193c797 473 "item1[,...]", "activate logging of the specified items to '/tmp/qemu.log'" },
9307c4c1 474 { "savevm", "F", do_savevm,
8a7ddc38 475 "filename", "save the whole virtual machine state to 'filename'" },
9307c4c1 476 { "loadvm", "F", do_loadvm,
8a7ddc38 477 "filename", "restore the whole virtual machine state from 'filename'" },
9307c4c1
FB
478 { "stop", "", do_stop,
479 "", "stop emulation", },
480 { "c|cont", "", do_cont,
481 "", "resume emulation", },
67b915a5 482#ifdef CONFIG_GDBSTUB
9307c4c1
FB
483 { "gdbserver", "i?", do_gdbserver,
484 "[port]", "start gdbserver session (default port=1234)", },
67b915a5 485#endif
9307c4c1
FB
486 { "x", "/i", do_memory_dump,
487 "/fmt addr", "virtual memory dump starting at 'addr'", },
488 { "xp", "/i", do_physical_memory_dump,
489 "/fmt addr", "physical memory dump starting at 'addr'", },
490 { "p|print", "/i", do_print,
491 "/fmt expr", "print expression value (use $reg for CPU register access)", },
f193c797 492 { NULL, NULL, },
9dc39cba
FB
493};
494
495static term_cmd_t info_cmds[] = {
9307c4c1 496 { "network", "", do_info_network,
9dc39cba 497 "", "show the network state" },
9307c4c1 498 { "block", "", do_info_block,
9dc39cba 499 "", "show the block devices" },
9307c4c1
FB
500 { "registers", "", do_info_registers,
501 "", "show the cpu registers" },
9dc39cba
FB
502 { NULL, NULL, },
503};
504
9307c4c1
FB
505/*******************************************************************/
506
507static const char *pch;
508static jmp_buf expr_env;
509
510typedef struct MonitorDef {
511 const char *name;
512 int offset;
513 int (*get_value)(struct MonitorDef *md);
514} MonitorDef;
515
516static MonitorDef monitor_defs[] = {
517#ifdef TARGET_I386
518 { "eax", offsetof(CPUState, regs[0]) },
519 { "ecx", offsetof(CPUState, regs[1]) },
520 { "edx", offsetof(CPUState, regs[2]) },
521 { "ebx", offsetof(CPUState, regs[3]) },
522 { "esp|sp", offsetof(CPUState, regs[4]) },
523 { "ebp|fp", offsetof(CPUState, regs[5]) },
524 { "esi", offsetof(CPUState, regs[6]) },
525 { "esi", offsetof(CPUState, regs[7]) },
526 { "eflags", offsetof(CPUState, eflags) },
527 { "eip|pc", offsetof(CPUState, eip) },
528#endif
529 { NULL },
530};
531
532static void expr_error(const char *fmt)
9dc39cba 533{
9307c4c1
FB
534 term_printf(fmt);
535 term_printf("\n");
536 longjmp(expr_env, 1);
537}
538
539static int get_monitor_def(int *pval, const char *name)
540{
541 MonitorDef *md;
542 for(md = monitor_defs; md->name != NULL; md++) {
543 if (compare_cmd(name, md->name)) {
544 if (md->get_value) {
545 *pval = md->get_value(md);
546 } else {
547 *pval = *(uint32_t *)((uint8_t *)cpu_single_env + md->offset);
548 }
549 return 0;
550 }
551 }
552 return -1;
553}
554
555static void next(void)
556{
557 if (pch != '\0') {
558 pch++;
559 while (isspace(*pch))
560 pch++;
561 }
562}
563
564static int expr_sum(void);
565
566static int expr_unary(void)
567{
568 int n;
569 char *p;
570
571 switch(*pch) {
572 case '+':
573 next();
574 n = expr_unary();
575 break;
576 case '-':
577 next();
578 n = -expr_unary();
579 break;
580 case '~':
581 next();
582 n = ~expr_unary();
583 break;
584 case '(':
585 next();
586 n = expr_sum();
587 if (*pch != ')') {
588 expr_error("')' expected");
589 }
590 next();
591 break;
592 case '$':
593 {
594 char buf[128], *q;
595
596 pch++;
597 q = buf;
598 while ((*pch >= 'a' && *pch <= 'z') ||
599 (*pch >= 'A' && *pch <= 'Z') ||
600 (*pch >= '0' && *pch <= '9') ||
601 *pch == '_') {
602 if ((q - buf) < sizeof(buf) - 1)
603 *q++ = *pch;
604 pch++;
605 }
606 while (isspace(*pch))
607 pch++;
608 *q = 0;
609 if (get_monitor_def(&n, buf))
610 expr_error("unknown register");
611 }
612 break;
613 case '\0':
614 expr_error("unexpected end of expression");
615 n = 0;
616 break;
617 default:
618 n = strtoul(pch, &p, 0);
619 if (pch == p) {
620 expr_error("invalid char in expression");
621 }
622 pch = p;
623 while (isspace(*pch))
624 pch++;
625 break;
626 }
627 return n;
628}
629
630
631static int expr_prod(void)
632{
633 int val, val2, op;
634
635 val = expr_unary();
636 for(;;) {
637 op = *pch;
638 if (op != '*' && op != '/' && op != '%')
639 break;
640 next();
641 val2 = expr_unary();
642 switch(op) {
643 default:
644 case '*':
645 val *= val2;
646 break;
647 case '/':
648 case '%':
649 if (val2 == 0)
650 expr_error("divison by zero");
651 if (op == '/')
652 val /= val2;
653 else
654 val %= val2;
655 break;
656 }
657 }
658 return val;
659}
660
661static int expr_logic(void)
662{
663 int val, val2, op;
664
665 val = expr_prod();
666 for(;;) {
667 op = *pch;
668 if (op != '&' && op != '|' && op != '^')
669 break;
670 next();
671 val2 = expr_prod();
672 switch(op) {
673 default:
674 case '&':
675 val &= val2;
676 break;
677 case '|':
678 val |= val2;
679 break;
680 case '^':
681 val ^= val2;
682 break;
683 }
684 }
685 return val;
686}
687
688static int expr_sum(void)
689{
690 int val, val2, op;
691
692 val = expr_logic();
693 for(;;) {
694 op = *pch;
695 if (op != '+' && op != '-')
696 break;
697 next();
698 val2 = expr_logic();
699 if (op == '+')
700 val += val2;
701 else
702 val -= val2;
703 }
704 return val;
705}
706
707static int get_expr(int *pval, const char **pp)
708{
709 pch = *pp;
710 if (setjmp(expr_env)) {
711 *pp = pch;
712 return -1;
713 }
714 while (isspace(*pch))
715 pch++;
716 *pval = expr_sum();
717 *pp = pch;
718 return 0;
719}
720
721static int get_str(char *buf, int buf_size, const char **pp)
722{
723 const char *p;
724 char *q;
725 int c;
726
727 p = *pp;
728 while (isspace(*p))
729 p++;
730 if (*p == '\0') {
731 fail:
732 *pp = p;
733 return -1;
734 }
735 q = buf;
736 if (*p == '\"') {
737 p++;
738 while (*p != '\0' && *p != '\"') {
739 if (*p == '\\') {
740 p++;
741 c = *p++;
742 switch(c) {
743 case 'n':
744 c = '\n';
745 break;
746 case 'r':
747 c = '\r';
748 break;
749 case '\\':
750 case '\'':
751 case '\"':
752 break;
753 default:
754 qemu_printf("unsupported escape code: '\\%c'\n", c);
755 goto fail;
756 }
757 if ((q - buf) < buf_size - 1) {
758 *q++ = c;
759 }
760 } else {
761 if ((q - buf) < buf_size - 1) {
762 *q++ = *p;
763 }
764 p++;
765 }
766 }
767 if (*p != '\"') {
768 qemu_printf("untermintated string\n");
769 goto fail;
770 }
771 p++;
772 } else {
773 while (*p != '\0' && !isspace(*p)) {
774 if ((q - buf) < buf_size - 1) {
775 *q++ = *p;
776 }
777 p++;
778 }
779 *q = '\0';
780 }
781 *pp = p;
782 return 0;
783}
784
785static int default_fmt_format = 'x';
786static int default_fmt_size = 4;
787
788#define MAX_ARGS 16
789
790static void term_handle_command(const char *cmdline)
791{
792 const char *p, *pstart, *typestr;
793 char *q;
794 int c, nb_args, len, i, has_arg;
9dc39cba 795 term_cmd_t *cmd;
9307c4c1
FB
796 char cmdname[256];
797 char buf[1024];
798 void *str_allocated[MAX_ARGS];
799 void *args[MAX_ARGS];
9dc39cba
FB
800
801#ifdef DEBUG
802 term_printf("command='%s'\n", cmdline);
803#endif
804
9307c4c1 805 /* extract the command name */
9dc39cba 806 p = cmdline;
9307c4c1
FB
807 q = cmdname;
808 while (isspace(*p))
809 p++;
810 if (*p == '\0')
811 return;
812 pstart = p;
813 while (*p != '\0' && *p != '/' && !isspace(*p))
814 p++;
815 len = p - pstart;
816 if (len > sizeof(cmdname) - 1)
817 len = sizeof(cmdname) - 1;
818 memcpy(cmdname, pstart, len);
819 cmdname[len] = '\0';
820
821 /* find the command */
822 for(cmd = term_cmds; cmd->name != NULL; cmd++) {
823 if (compare_cmd(cmdname, cmd->name))
824 goto found;
825 }
826 term_printf("unknown command: '%s'\n", cmdname);
827 return;
828 found:
829
830 for(i = 0; i < MAX_ARGS; i++)
831 str_allocated[i] = NULL;
832
833 /* parse the parameters */
834 typestr = cmd->args_type;
835 nb_args = 0;
9dc39cba 836 for(;;) {
9307c4c1
FB
837 c = *typestr;
838 if (c == '\0')
9dc39cba 839 break;
9307c4c1
FB
840 typestr++;
841 switch(c) {
842 case 'F':
843 case 's':
844 {
845 int ret;
846 char *str;
847
848 while (isspace(*p))
849 p++;
850 if (*typestr == '?') {
851 typestr++;
852 if (*p == '\0') {
853 /* no optional string: NULL argument */
854 str = NULL;
855 goto add_str;
856 }
857 }
858 ret = get_str(buf, sizeof(buf), &p);
859 if (ret < 0) {
860 if (c == 'F')
861 term_printf("%s: filename expected\n", cmdname);
862 else
863 term_printf("%s: string expected\n", cmdname);
864 goto fail;
865 }
866 str = qemu_malloc(strlen(buf) + 1);
867 strcpy(str, buf);
868 str_allocated[nb_args] = str;
869 add_str:
870 if (nb_args >= MAX_ARGS) {
871 error_args:
872 term_printf("%s: too many arguments\n", cmdname);
873 goto fail;
874 }
875 args[nb_args++] = str;
876 }
9dc39cba 877 break;
9307c4c1
FB
878 case '/':
879 {
880 int count, format, size;
881
882 while (isspace(*p))
883 p++;
884 if (*p == '/') {
885 /* format found */
886 p++;
887 count = 1;
888 if (isdigit(*p)) {
889 count = 0;
890 while (isdigit(*p)) {
891 count = count * 10 + (*p - '0');
892 p++;
893 }
894 }
895 size = -1;
896 format = -1;
897 for(;;) {
898 switch(*p) {
899 case 'o':
900 case 'd':
901 case 'u':
902 case 'x':
903 case 'i':
904 case 'c':
905 format = *p++;
906 break;
907 case 'b':
908 size = 1;
909 p++;
910 break;
911 case 'h':
912 size = 2;
913 p++;
914 break;
915 case 'w':
916 size = 4;
917 p++;
918 break;
919 case 'g':
920 case 'L':
921 size = 8;
922 p++;
923 break;
924 default:
925 goto next;
926 }
927 }
928 next:
929 if (*p != '\0' && !isspace(*p)) {
930 term_printf("invalid char in format: '%c'\n", *p);
931 goto fail;
932 }
933 if (size < 0)
934 size = default_fmt_size;
935 if (format < 0)
936 format = default_fmt_format;
937 default_fmt_size = size;
938 default_fmt_format = format;
939 } else {
940 count = 1;
941 format = default_fmt_format;
942 size = default_fmt_size;
943 }
944 if (nb_args + 3 > MAX_ARGS)
945 goto error_args;
946 args[nb_args++] = (void*)count;
947 args[nb_args++] = (void*)format;
948 args[nb_args++] = (void*)size;
949 }
9dc39cba 950 break;
9307c4c1
FB
951 case 'i':
952 {
953 int val;
954 while (isspace(*p))
955 p++;
956 if (*typestr == '?') {
957 typestr++;
958 if (*p == '\0')
959 has_arg = 0;
960 else
961 has_arg = 1;
962 if (nb_args >= MAX_ARGS)
963 goto error_args;
964 args[nb_args++] = (void *)has_arg;
965 if (!has_arg) {
966 if (nb_args >= MAX_ARGS)
967 goto error_args;
968 val = -1;
969 goto add_num;
970 }
971 }
972 if (get_expr(&val, &p))
973 goto fail;
974 add_num:
975 if (nb_args >= MAX_ARGS)
976 goto error_args;
977 args[nb_args++] = (void *)val;
978 }
979 break;
980 case '-':
981 {
982 int has_option;
983 /* option */
984
985 c = *typestr++;
986 if (c == '\0')
987 goto bad_type;
988 while (isspace(*p))
989 p++;
990 has_option = 0;
991 if (*p == '-') {
992 p++;
993 if (*p != c) {
994 term_printf("%s: unsupported option -%c\n",
995 cmdname, *p);
996 goto fail;
997 }
998 p++;
999 has_option = 1;
1000 }
1001 if (nb_args >= MAX_ARGS)
1002 goto error_args;
1003 args[nb_args++] = (void *)has_option;
1004 }
1005 break;
1006 default:
1007 bad_type:
1008 term_printf("%s: unknown type '%c'\n", cmdname, c);
1009 goto fail;
1010 }
9dc39cba 1011 }
9307c4c1
FB
1012 /* check that all arguments were parsed */
1013 while (isspace(*p))
1014 p++;
1015 if (*p != '\0') {
1016 term_printf("%s: extraneous characters at the end of line\n",
1017 cmdname);
1018 goto fail;
9dc39cba 1019 }
9307c4c1
FB
1020
1021 switch(nb_args) {
1022 case 0:
1023 cmd->handler();
1024 break;
1025 case 1:
1026 cmd->handler(args[0]);
1027 break;
1028 case 2:
1029 cmd->handler(args[0], args[1]);
1030 break;
1031 case 3:
1032 cmd->handler(args[0], args[1], args[2]);
1033 break;
1034 case 4:
1035 cmd->handler(args[0], args[1], args[2], args[3]);
1036 break;
1037 case 5:
1038 cmd->handler(args[0], args[1], args[2], args[3], args[4]);
1039 break;
1040 default:
1041 term_printf("unsupported number of arguments: %d\n", nb_args);
1042 goto fail;
9dc39cba 1043 }
9307c4c1
FB
1044 fail:
1045 for(i = 0; i < MAX_ARGS; i++)
1046 qemu_free(str_allocated[i]);
9dc39cba 1047 return;
9dc39cba
FB
1048}
1049
1050static void term_show_prompt(void)
1051{
1052 term_printf("(qemu) ");
1053 fflush(stdout);
1054 term_cmd_buf_index = 0;
1055 term_cmd_buf_size = 0;
1056 term_esc_state = IS_NORM;
1057}
1058
1059static void term_insert_char(int ch)
1060{
1061 if (term_cmd_buf_index < TERM_CMD_BUF_SIZE) {
1062 memmove(term_cmd_buf + term_cmd_buf_index + 1,
1063 term_cmd_buf + term_cmd_buf_index,
1064 term_cmd_buf_size - term_cmd_buf_index);
1065 term_cmd_buf[term_cmd_buf_index] = ch;
1066 term_cmd_buf_size++;
1067 term_printf("\033[@%c", ch);
1068 term_cmd_buf_index++;
1069 term_flush();
1070 }
1071}
1072
1073static void term_backward_char(void)
1074{
1075 if (term_cmd_buf_index > 0) {
1076 term_cmd_buf_index--;
1077 term_printf("\033[D");
1078 term_flush();
1079 }
1080}
1081
1082static void term_forward_char(void)
1083{
1084 if (term_cmd_buf_index < term_cmd_buf_size) {
1085 term_cmd_buf_index++;
1086 term_printf("\033[C");
1087 term_flush();
1088 }
1089}
1090
1091static void term_delete_char(void)
1092{
1093 if (term_cmd_buf_index < term_cmd_buf_size) {
1094 memmove(term_cmd_buf + term_cmd_buf_index,
1095 term_cmd_buf + term_cmd_buf_index + 1,
1096 term_cmd_buf_size - term_cmd_buf_index - 1);
1097 term_printf("\033[P");
1098 term_cmd_buf_size--;
1099 term_flush();
1100 }
1101}
1102
1103static void term_backspace(void)
1104{
1105 if (term_cmd_buf_index > 0) {
1106 term_backward_char();
1107 term_delete_char();
1108 }
1109}
1110
1111static void term_bol(void)
1112{
1113 while (term_cmd_buf_index > 0)
1114 term_backward_char();
1115}
1116
1117static void term_eol(void)
1118{
1119 while (term_cmd_buf_index < term_cmd_buf_size)
1120 term_forward_char();
1121}
1122
1123/* return true if command handled */
1124static void term_handle_byte(int ch)
1125{
1126 switch(term_esc_state) {
1127 case IS_NORM:
1128 switch(ch) {
1129 case 1:
1130 term_bol();
1131 break;
1132 case 5:
1133 term_eol();
1134 break;
1135 case 10:
1136 case 13:
1137 term_cmd_buf[term_cmd_buf_size] = '\0';
1138 term_printf("\n");
1139 term_handle_command(term_cmd_buf);
1140 term_show_prompt();
1141 break;
1142 case 27:
1143 term_esc_state = IS_ESC;
1144 break;
1145 case 127:
1146 case 8:
1147 term_backspace();
1148 break;
1149 default:
1150 if (ch >= 32) {
1151 term_insert_char(ch);
1152 }
1153 break;
1154 }
1155 break;
1156 case IS_ESC:
1157 if (ch == '[') {
1158 term_esc_state = IS_CSI;
1159 term_esc_param = 0;
1160 } else {
1161 term_esc_state = IS_NORM;
1162 }
1163 break;
1164 case IS_CSI:
1165 switch(ch) {
1166 case 'D':
1167 term_backward_char();
1168 break;
1169 case 'C':
1170 term_forward_char();
1171 break;
1172 case '0' ... '9':
1173 term_esc_param = term_esc_param * 10 + (ch - '0');
1174 goto the_end;
1175 case '~':
1176 switch(term_esc_param) {
1177 case 1:
1178 term_bol();
1179 break;
1180 case 3:
1181 term_delete_char();
1182 break;
1183 case 4:
1184 term_eol();
1185 break;
1186 }
1187 break;
1188 default:
1189 break;
1190 }
1191 term_esc_state = IS_NORM;
1192 the_end:
1193 break;
1194 }
1195}
1196
1197/*************************************************************/
1198/* serial console support */
1199
1200#define TERM_ESCAPE 0x01 /* ctrl-a is used for escape */
1201
1202static int term_got_escape, term_command;
1203
1204void term_print_help(void)
1205{
1206 term_printf("\n"
1207 "C-a h print this help\n"
1208 "C-a x exit emulatior\n"
9dc39cba
FB
1209 "C-a s save disk data back to file (if -snapshot)\n"
1210 "C-a b send break (magic sysrq)\n"
1211 "C-a c switch between console and monitor\n"
1212 "C-a C-a send C-a\n"
1213 );
1214}
1215
1216/* called when a char is received */
1217static void term_received_byte(int ch)
1218{
1219 if (!serial_console) {
1220 /* if no serial console, handle every command */
1221 term_handle_byte(ch);
1222 } else {
1223 if (term_got_escape) {
1224 term_got_escape = 0;
1225 switch(ch) {
1226 case 'h':
1227 term_print_help();
1228 break;
1229 case 'x':
1230 exit(0);
1231 break;
1232 case 's':
1233 {
1234 int i;
1235 for (i = 0; i < MAX_DISKS; i++) {
1236 if (bs_table[i])
1237 bdrv_commit(bs_table[i]);
1238 }
1239 }
1240 break;
1241 case 'b':
1242 if (serial_console)
1243 serial_receive_break(serial_console);
1244 break;
1245 case 'c':
1246 if (!term_command) {
1247 term_show_prompt();
1248 term_command = 1;
1249 } else {
1250 term_command = 0;
1251 }
1252 break;
9dc39cba
FB
1253 case TERM_ESCAPE:
1254 goto send_char;
1255 }
1256 } else if (ch == TERM_ESCAPE) {
1257 term_got_escape = 1;
1258 } else {
1259 send_char:
1260 if (term_command) {
1261 term_handle_byte(ch);
1262 } else {
1263 if (serial_console)
1264 serial_receive_byte(serial_console, ch);
1265 }
1266 }
1267 }
1268}
1269
1270static int term_can_read(void *opaque)
1271{
1272 if (serial_console) {
1273 return serial_can_receive(serial_console);
1274 } else {
f193c797 1275 return 128;
9dc39cba
FB
1276 }
1277}
1278
1279static void term_read(void *opaque, const uint8_t *buf, int size)
1280{
1281 int i;
1282 for(i = 0; i < size; i++)
1283 term_received_byte(buf[i]);
1284}
1285
1286void monitor_init(void)
1287{
1288 if (!serial_console) {
1289 term_printf("QEMU %s monitor - type 'help' for more information\n",
1290 QEMU_VERSION);
1291 term_show_prompt();
1292 }
8a7ddc38 1293 qemu_add_fd_read_handler(0, term_can_read, term_read, NULL);
9dc39cba 1294}