X-Git-Url: http://vcs.maemo.org/git/?a=blobdiff_plain;f=monitor.c;h=70b473ddbff6ba7b3b3373485a05640675a88d2e;hb=e80e1cc4b18d388227d2fa6b8551929a381d2490;hp=195a0b0071f63f62ef2264eb9b750cf2bf5f2f99;hpb=59a983b921e8318ae8da357aacd496c3ab5bbc5b;p=qemu diff --git a/monitor.c b/monitor.c index 195a0b0..70b473d 100644 --- a/monitor.c +++ b/monitor.c @@ -21,65 +21,96 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cpu.h" #include "vl.h" +#include "disas.h" +#include //#define DEBUG +//#define DEBUG_COMPLETION -#define TERM_CMD_BUF_SIZE 4095 -#define MAX_ARGS 64 - -#define IS_NORM 0 -#define IS_ESC 1 -#define IS_CSI 2 - -#define printf do_not_use_printf +#ifndef offsetof +#define offsetof(type, field) ((size_t) &((type *)0)->field) +#endif -static char term_cmd_buf[TERM_CMD_BUF_SIZE + 1]; -static int term_cmd_buf_index; -static int term_cmd_buf_size; -static int term_esc_state; -static int term_esc_param; +/* + * Supported types: + * + * 'F' filename + * 'B' block device name + * 's' string (accept optional quote) + * 'i' 32 bit integer + * 'l' target long (32 or 64 bit) + * '/' optional gdb-like print format (like "/10x") + * + * '?' optional type (for 'F', 's' and 'i') + * + */ typedef struct term_cmd_t { const char *name; - void (*handler)(int argc, const char **argv); + const char *args_type; + void (*handler)(); const char *params; const char *help; } term_cmd_t; +static CharDriverState *monitor_hd; + static term_cmd_t term_cmds[]; static term_cmd_t info_cmds[]; +static char term_outbuf[1024]; +static int term_outbuf_index; + +static void monitor_start_input(void); + +CPUState *mon_cpu = NULL; + +void term_flush(void) +{ + if (term_outbuf_index > 0) { + qemu_chr_write(monitor_hd, term_outbuf, term_outbuf_index); + term_outbuf_index = 0; + } +} + +/* flush at every end of line or if the buffer is full */ +void term_puts(const char *str) +{ + int c; + for(;;) { + c = *str++; + if (c == '\0') + break; + term_outbuf[term_outbuf_index++] = c; + if (term_outbuf_index >= sizeof(term_outbuf) || + c == '\n') + term_flush(); + } +} + +void term_vprintf(const char *fmt, va_list ap) +{ + char buf[4096]; + vsnprintf(buf, sizeof(buf), fmt, ap); + term_puts(buf); +} + void term_printf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); - vprintf(fmt, ap); + term_vprintf(fmt, ap); va_end(ap); } -void term_flush(void) +static int monitor_fprintf(FILE *stream, const char *fmt, ...) { - fflush(stdout); + va_list ap; + va_start(ap, fmt); + term_vprintf(fmt, ap); + va_end(ap); + return 0; } static int compare_cmd(const char *name, const char *list) @@ -118,67 +149,152 @@ static void help_cmd(const char *name) help_cmd1(info_cmds, "info ", NULL); } else { help_cmd1(term_cmds, "", name); + if (name && !strcmp(name, "log")) { + CPULogItem *item; + term_printf("Log items (comma separated):\n"); + term_printf("%-10s %s\n", "none", "remove all logs"); + for(item = cpu_log_items; item->mask != 0; item++) { + term_printf("%-10s %s\n", item->name, item->help); + } + } } } -static void do_help(int argc, const char **argv) +static void do_help(const char *name) { - help_cmd(argv[1]); + help_cmd(name); } -static void do_commit(int argc, const char **argv) +static void do_commit(void) { int i; for (i = 0; i < MAX_DISKS; i++) { - if (bs_table[i]) + if (bs_table[i]) { bdrv_commit(bs_table[i]); + } } } -static void do_info(int argc, const char **argv) +static void do_info(const char *item) { term_cmd_t *cmd; - const char *item; - if (argc < 2) + if (!item) goto help; - item = argv[1]; for(cmd = info_cmds; cmd->name != NULL; cmd++) { - if (compare_cmd(argv[1], cmd->name)) + if (compare_cmd(item, cmd->name)) goto found; } help: - help_cmd(argv[0]); + help_cmd("info"); return; found: - cmd->handler(argc, argv); + cmd->handler(); } -static void do_info_network(int argc, const char **argv) +static void do_info_version(void) { - int i, j; - NetDriverState *nd; - - for(i = 0; i < nb_nics; i++) { - nd = &nd_table[i]; - term_printf("%d: ifname=%s macaddr=", i, nd->ifname); - for(j = 0; j < 6; j++) { - if (j > 0) - term_printf(":"); - term_printf("%02x", nd->macaddr[j]); + term_printf("%s\n", QEMU_VERSION); +} + +static void do_info_block(void) +{ + bdrv_info(); +} + +/* get the current CPU defined by the user */ +int mon_set_cpu(int cpu_index) +{ + CPUState *env; + + for(env = first_cpu; env != NULL; env = env->next_cpu) { + if (env->cpu_index == cpu_index) { + mon_cpu = env; + return 0; } + } + return -1; +} + +CPUState *mon_get_cpu(void) +{ + if (!mon_cpu) { + mon_set_cpu(0); + } + return mon_cpu; +} + +static void do_info_registers(void) +{ + CPUState *env; + env = mon_get_cpu(); + if (!env) + return; +#ifdef TARGET_I386 + cpu_dump_state(env, NULL, monitor_fprintf, + X86_DUMP_FPU); +#else + cpu_dump_state(env, NULL, monitor_fprintf, + 0); +#endif +} + +static void do_info_cpus(void) +{ + CPUState *env; + + /* just to set the default cpu if not already done */ + mon_get_cpu(); + + for(env = first_cpu; env != NULL; env = env->next_cpu) { + term_printf("%c CPU #%d:", + (env == mon_cpu) ? '*' : ' ', + env->cpu_index); +#if defined(TARGET_I386) + term_printf(" pc=0x" TARGET_FMT_lx, env->eip + env->segs[R_CS].base); + if (env->hflags & HF_HALTED_MASK) + term_printf(" (halted)"); +#elif defined(TARGET_PPC) + term_printf(" nip=0x" TARGET_FMT_lx, env->nip); + if (msr_pow) + term_printf(" (halted)"); +#endif term_printf("\n"); } } - -static void do_info_block(int argc, const char **argv) + +static void do_cpu_set(int index) +{ + if (mon_set_cpu(index) < 0) + term_printf("Invalid CPU index\n"); +} + +static void do_info_jit(void) { - bdrv_info(); + dump_exec_info(NULL, monitor_fprintf); } -static void do_quit(int argc, const char **argv) +static void do_info_history (void) { + int i; + const char *str; + + i = 0; + for(;;) { + str = readline_get_history(i); + if (!str) + break; + term_printf("%d: '%s'\n", i, str); + i++; + } +} + +static void do_quit(void) +{ +#ifdef USE_KQEMU + kqemu_record_dump(); +#endif exit(0); } @@ -200,26 +316,11 @@ static int eject_device(BlockDriverState *bs, int force) return 0; } -static void do_eject(int argc, const char **argv) +static void do_eject(int force, const char *filename) { BlockDriverState *bs; - const char **parg; - int force; - parg = argv + 1; - if (!*parg) { - fail: - help_cmd(argv[0]); - return; - } - force = 0; - if (!strcmp(*parg, "-f")) { - force = 1; - parg++; - } - if (!*parg) - goto fail; - bs = bdrv_find(*parg); + bs = bdrv_find(filename); if (!bs) { term_printf("device not found\n"); return; @@ -227,354 +328,1891 @@ static void do_eject(int argc, const char **argv) eject_device(bs, force); } -static void do_change(int argc, const char **argv) +static void do_change(const char *device, const char *filename) { BlockDriverState *bs; + int i; + char password[256]; - if (argc != 3) { - help_cmd(argv[0]); - return; - } - bs = bdrv_find(argv[1]); + bs = bdrv_find(device); if (!bs) { term_printf("device not found\n"); return; } if (eject_device(bs, 0) < 0) return; - bdrv_open(bs, argv[2], 0); + bdrv_open(bs, filename, 0); + if (bdrv_is_encrypted(bs)) { + term_printf("%s is encrypted.\n", device); + for(i = 0; i < 3; i++) { + monitor_readline("Password: ", 1, password, sizeof(password)); + if (bdrv_set_key(bs, password) == 0) + break; + term_printf("invalid password\n"); + } + } } -static void do_screen_dump(int argc, const char **argv) +static void do_screen_dump(const char *filename) { - if (argc != 2) { - help_cmd(argv[0]); - return; - } - vga_screen_dump(argv[1]); + vga_screen_dump(filename); } -static term_cmd_t term_cmds[] = { - { "help|?", do_help, - "[cmd]", "show the help" }, - { "commit", do_commit, - "", "commit changes to the disk images (if -snapshot is used)" }, - { "info", do_info, - "subcommand", "show various information about the system state" }, - { "q|quit", do_quit, - "", "quit the emulator" }, - { "eject", do_eject, - "[-f] device", "eject a removable media (use -f to force it)" }, - { "change", do_change, - "device filename", "change a removable media" }, - { "screendump", do_screen_dump, - "filename", "save screen into PPM image 'filename'" }, - { NULL, NULL, }, -}; - -static term_cmd_t info_cmds[] = { - { "network", do_info_network, - "", "show the network state" }, - { "block", do_info_block, - "", "show the block devices" }, - { NULL, NULL, }, -}; - -static void term_handle_command(char *cmdline) +static void do_log(const char *items) { - char *p, *pstart; - int argc; - const char *args[MAX_ARGS + 1]; - term_cmd_t *cmd; - -#ifdef DEBUG - term_printf("command='%s'\n", cmdline); -#endif + int mask; - /* split command in words */ - argc = 0; - p = cmdline; - for(;;) { - while (isspace(*p)) - p++; - if (*p == '\0') - break; - pstart = p; - while (*p != '\0' && !isspace(*p)) - p++; - args[argc] = pstart; - argc++; - if (argc >= MAX_ARGS) - break; - if (*p == '\0') - break; - *p++ = '\0'; - } - args[argc] = NULL; -#ifdef DEBUG - for(i=0;iname != NULL; cmd++) { - if (compare_cmd(args[0], cmd->name)) - goto found; + if (!strcmp(items, "none")) { + mask = 0; + } else { + mask = cpu_str_to_log_mask(items); + if (!mask) { + help_cmd("log"); + return; + } } - term_printf("unknown command: '%s'\n", args[0]); - return; - found: - cmd->handler(argc, args); + cpu_set_log(mask); } -static void term_show_prompt(void) +static void do_savevm(const char *filename) { - term_printf("(qemu) "); - fflush(stdout); - term_cmd_buf_index = 0; - term_cmd_buf_size = 0; - term_esc_state = IS_NORM; + if (qemu_savevm(filename) < 0) + term_printf("I/O error when saving VM to '%s'\n", filename); } -static void term_insert_char(int ch) +static void do_loadvm(const char *filename) { - if (term_cmd_buf_index < TERM_CMD_BUF_SIZE) { - memmove(term_cmd_buf + term_cmd_buf_index + 1, - term_cmd_buf + term_cmd_buf_index, - term_cmd_buf_size - term_cmd_buf_index); - term_cmd_buf[term_cmd_buf_index] = ch; - term_cmd_buf_size++; - term_printf("\033[@%c", ch); - term_cmd_buf_index++; - term_flush(); - } + if (qemu_loadvm(filename) < 0) + term_printf("I/O error when loading VM from '%s'\n", filename); } -static void term_backward_char(void) +static void do_stop(void) { - if (term_cmd_buf_index > 0) { - term_cmd_buf_index--; - term_printf("\033[D"); - term_flush(); - } + vm_stop(EXCP_INTERRUPT); } -static void term_forward_char(void) +static void do_cont(void) { - if (term_cmd_buf_index < term_cmd_buf_size) { - term_cmd_buf_index++; - term_printf("\033[C"); - term_flush(); - } + vm_start(); } -static void term_delete_char(void) +#ifdef CONFIG_GDBSTUB +static void do_gdbserver(int has_port, int port) { - if (term_cmd_buf_index < term_cmd_buf_size) { - memmove(term_cmd_buf + term_cmd_buf_index, - term_cmd_buf + term_cmd_buf_index + 1, - term_cmd_buf_size - term_cmd_buf_index - 1); - term_printf("\033[P"); - term_cmd_buf_size--; - term_flush(); + if (!has_port) + port = DEFAULT_GDBSTUB_PORT; + if (gdbserver_start(port) < 0) { + qemu_printf("Could not open gdbserver socket on port %d\n", port); + } else { + qemu_printf("Waiting gdb connection on port %d\n", port); } } +#endif -static void term_backspace(void) +static void term_printc(int c) { - if (term_cmd_buf_index > 0) { - term_backward_char(); - term_delete_char(); + term_printf("'"); + switch(c) { + case '\'': + term_printf("\\'"); + break; + case '\\': + term_printf("\\\\"); + break; + case '\n': + term_printf("\\n"); + break; + case '\r': + term_printf("\\r"); + break; + default: + if (c >= 32 && c <= 126) { + term_printf("%c", c); + } else { + term_printf("\\x%02x", c); + } + break; } + term_printf("'"); } -static void term_bol(void) -{ - while (term_cmd_buf_index > 0) - term_backward_char(); -} - -static void term_eol(void) +static void memory_dump(int count, int format, int wsize, + target_ulong addr, int is_physical) { - while (term_cmd_buf_index < term_cmd_buf_size) - term_forward_char(); -} + CPUState *env; + int nb_per_line, l, line_size, i, max_digits, len; + uint8_t buf[16]; + uint64_t v; -/* return true if command handled */ -static void term_handle_byte(int ch) -{ - switch(term_esc_state) { - case IS_NORM: - switch(ch) { - case 1: - term_bol(); - break; - case 5: - term_eol(); - break; - case 10: - case 13: - term_cmd_buf[term_cmd_buf_size] = '\0'; - term_printf("\n"); - term_handle_command(term_cmd_buf); - term_show_prompt(); - break; - case 27: - term_esc_state = IS_ESC; - break; - case 127: - case 8: - term_backspace(); - break; - default: - if (ch >= 32) { - term_insert_char(ch); - } - break; + if (format == 'i') { + int flags; + flags = 0; + env = mon_get_cpu(); + if (!env && !is_physical) + return; +#ifdef TARGET_I386 + if (wsize == 2) { + flags = 1; + } else if (wsize == 4) { + flags = 0; + } else { + /* as default we use the current CS size */ + flags = 0; + if (env && !(env->segs[R_CS].flags & DESC_B_MASK)) + flags = 1; } +#endif + monitor_disas(env, addr, count, is_physical, flags); + return; + } + + len = wsize * count; + if (wsize == 1) + line_size = 8; + else + line_size = 16; + nb_per_line = line_size / wsize; + max_digits = 0; + + switch(format) { + case 'o': + max_digits = (wsize * 8 + 2) / 3; + break; + default: + case 'x': + max_digits = (wsize * 8) / 4; + break; + case 'u': + case 'd': + max_digits = (wsize * 8 * 10 + 32) / 33; break; - case IS_ESC: - if (ch == '[') { - term_esc_state = IS_CSI; - term_esc_param = 0; + case 'c': + wsize = 1; + break; + } + + while (len > 0) { + term_printf(TARGET_FMT_lx ":", addr); + l = len; + if (l > line_size) + l = line_size; + if (is_physical) { + cpu_physical_memory_rw(addr, buf, l, 0); } else { - term_esc_state = IS_NORM; + env = mon_get_cpu(); + if (!env) + break; + cpu_memory_rw_debug(env, addr, buf, l, 0); } - break; - case IS_CSI: - switch(ch) { - case 'D': - term_backward_char(); - break; - case 'C': - term_forward_char(); - break; - case '0' ... '9': - term_esc_param = term_esc_param * 10 + (ch - '0'); - goto the_end; - case '~': - switch(term_esc_param) { + i = 0; + while (i < l) { + switch(wsize) { + default: case 1: - term_bol(); + v = ldub_raw(buf + i); break; - case 3: - term_delete_char(); + case 2: + v = lduw_raw(buf + i); break; case 4: - term_eol(); + v = (uint32_t)ldl_raw(buf + i); + break; + case 8: + v = ldq_raw(buf + i); break; } - break; - default: - break; + term_printf(" "); + switch(format) { + case 'o': + term_printf("%#*llo", max_digits, v); + break; + case 'x': + term_printf("0x%0*llx", max_digits, v); + break; + case 'u': + term_printf("%*llu", max_digits, v); + break; + case 'd': + term_printf("%*lld", max_digits, v); + break; + case 'c': + term_printc(v); + break; + } + i += wsize; } - term_esc_state = IS_NORM; - the_end: - break; + term_printf("\n"); + addr += l; + len -= l; } } -/*************************************************************/ -/* serial console support */ +#if TARGET_LONG_BITS == 64 +#define GET_TLONG(h, l) (((uint64_t)(h) << 32) | (l)) +#else +#define GET_TLONG(h, l) (l) +#endif -#define TERM_ESCAPE 0x01 /* ctrl-a is used for escape */ +static void do_memory_dump(int count, int format, int size, + uint32_t addrh, uint32_t addrl) +{ + target_long addr = GET_TLONG(addrh, addrl); + memory_dump(count, format, size, addr, 0); +} -static int term_got_escape, term_command; +static void do_physical_memory_dump(int count, int format, int size, + uint32_t addrh, uint32_t addrl) -void term_print_help(void) { - term_printf("\n" - "C-a h print this help\n" - "C-a x exit emulatior\n" - "C-a d switch on/off debug log\n" - "C-a s save disk data back to file (if -snapshot)\n" - "C-a b send break (magic sysrq)\n" - "C-a c switch between console and monitor\n" - "C-a C-a send C-a\n" - ); + target_long addr = GET_TLONG(addrh, addrl); + memory_dump(count, format, size, addr, 1); } -/* called when a char is received */ -static void term_received_byte(int ch) +static void do_print(int count, int format, int size, unsigned int valh, unsigned int vall) { - if (!serial_console) { - /* if no serial console, handle every command */ - term_handle_byte(ch); - } else { - if (term_got_escape) { - term_got_escape = 0; - switch(ch) { - case 'h': - term_print_help(); - break; - case 'x': - exit(0); - break; - case 's': - { - int i; - for (i = 0; i < MAX_DISKS; i++) { - if (bs_table[i]) - bdrv_commit(bs_table[i]); - } - } - break; - case 'b': - if (serial_console) - serial_receive_break(serial_console); - break; - case 'c': - if (!term_command) { - term_show_prompt(); - term_command = 1; - } else { - term_command = 0; - } - break; - case 'd': - cpu_set_log(CPU_LOG_ALL); - break; - case TERM_ESCAPE: - goto send_char; - } - } else if (ch == TERM_ESCAPE) { - term_got_escape = 1; - } else { - send_char: - if (term_command) { - term_handle_byte(ch); - } else { - if (serial_console) - serial_receive_byte(serial_console, ch); - } - } + target_long val = GET_TLONG(valh, vall); +#if TARGET_LONG_BITS == 32 + switch(format) { + case 'o': + term_printf("%#o", val); + break; + case 'x': + term_printf("%#x", val); + break; + case 'u': + term_printf("%u", val); + break; + default: + case 'd': + term_printf("%d", val); + break; + case 'c': + term_printc(val); + break; + } +#else + switch(format) { + case 'o': + term_printf("%#llo", val); + break; + case 'x': + term_printf("%#llx", val); + break; + case 'u': + term_printf("%llu", val); + break; + default: + case 'd': + term_printf("%lld", val); + break; + case 'c': + term_printc(val); + break; } +#endif + term_printf("\n"); } -static int term_can_read(void *opaque) +static void do_sum(uint32_t start, uint32_t size) { - if (serial_console) { - return serial_can_receive(serial_console); - } else { - return 1; + uint32_t addr; + uint8_t buf[1]; + uint16_t sum; + + sum = 0; + for(addr = start; addr < (start + size); addr++) { + cpu_physical_memory_rw(addr, buf, 1, 0); + /* BSD sum algorithm ('sum' Unix command) */ + sum = (sum >> 1) | (sum << 15); + sum += buf[0]; } + term_printf("%05d\n", sum); } -static void term_read(void *opaque, const uint8_t *buf, int size) +typedef struct { + int keycode; + const char *name; +} KeyDef; + +static const KeyDef key_defs[] = { + { 0x2a, "shift" }, + { 0x36, "shift_r" }, + + { 0x38, "alt" }, + { 0xb8, "alt_r" }, + { 0x1d, "ctrl" }, + { 0x9d, "ctrl_r" }, + + { 0xdd, "menu" }, + + { 0x01, "esc" }, + + { 0x02, "1" }, + { 0x03, "2" }, + { 0x04, "3" }, + { 0x05, "4" }, + { 0x06, "5" }, + { 0x07, "6" }, + { 0x08, "7" }, + { 0x09, "8" }, + { 0x0a, "9" }, + { 0x0b, "0" }, + { 0x0e, "backspace" }, + + { 0x0f, "tab" }, + { 0x10, "q" }, + { 0x11, "w" }, + { 0x12, "e" }, + { 0x13, "r" }, + { 0x14, "t" }, + { 0x15, "y" }, + { 0x16, "u" }, + { 0x17, "i" }, + { 0x18, "o" }, + { 0x19, "p" }, + + { 0x1c, "ret" }, + + { 0x1e, "a" }, + { 0x1f, "s" }, + { 0x20, "d" }, + { 0x21, "f" }, + { 0x22, "g" }, + { 0x23, "h" }, + { 0x24, "j" }, + { 0x25, "k" }, + { 0x26, "l" }, + + { 0x2c, "z" }, + { 0x2d, "x" }, + { 0x2e, "c" }, + { 0x2f, "v" }, + { 0x30, "b" }, + { 0x31, "n" }, + { 0x32, "m" }, + + { 0x39, "spc" }, + { 0x3a, "caps_lock" }, + { 0x3b, "f1" }, + { 0x3c, "f2" }, + { 0x3d, "f3" }, + { 0x3e, "f4" }, + { 0x3f, "f5" }, + { 0x40, "f6" }, + { 0x41, "f7" }, + { 0x42, "f8" }, + { 0x43, "f9" }, + { 0x44, "f10" }, + { 0x45, "num_lock" }, + { 0x46, "scroll_lock" }, + + { 0x56, "<" }, + + { 0x57, "f11" }, + { 0x58, "f12" }, + + { 0xb7, "print" }, + + { 0xc7, "home" }, + { 0xc9, "pgup" }, + { 0xd1, "pgdn" }, + { 0xcf, "end" }, + + { 0xcb, "left" }, + { 0xc8, "up" }, + { 0xd0, "down" }, + { 0xcd, "right" }, + + { 0xd2, "insert" }, + { 0xd3, "delete" }, + { 0, NULL }, +}; + +static int get_keycode(const char *key) { - int i; - for(i = 0; i < size; i++) - term_received_byte(buf[i]); + const KeyDef *p; + + for(p = key_defs; p->name != NULL; p++) { + if (!strcmp(key, p->name)) + return p->keycode; + } + return -1; } -void monitor_init(void) +static void do_send_key(const char *string) { - if (!serial_console) { - term_printf("QEMU %s monitor - type 'help' for more information\n", - QEMU_VERSION); - term_show_prompt(); + char keybuf[16], *q; + uint8_t keycodes[16]; + const char *p; + int nb_keycodes, keycode, i; + + nb_keycodes = 0; + p = string; + while (*p != '\0') { + q = keybuf; + while (*p != '\0' && *p != '-') { + if ((q - keybuf) < sizeof(keybuf) - 1) { + *q++ = *p; + } + p++; + } + *q = '\0'; + keycode = get_keycode(keybuf); + if (keycode < 0) { + term_printf("unknown key: '%s'\n", keybuf); + return; + } + keycodes[nb_keycodes++] = keycode; + if (*p == '\0') + break; + p++; + } + /* key down events */ + for(i = 0; i < nb_keycodes; i++) { + keycode = keycodes[i]; + if (keycode & 0x80) + kbd_put_keycode(0xe0); + kbd_put_keycode(keycode & 0x7f); + } + /* key up events */ + for(i = nb_keycodes - 1; i >= 0; i--) { + keycode = keycodes[i]; + if (keycode & 0x80) + kbd_put_keycode(0xe0); + kbd_put_keycode(keycode | 0x80); + } +} + +static void do_ioport_read(int count, int format, int size, int addr, int has_index, int index) +{ + uint32_t val; + int suffix; + + if (has_index) { + cpu_outb(NULL, addr & 0xffff, index & 0xff); + addr++; + } + addr &= 0xffff; + + switch(size) { + default: + case 1: + val = cpu_inb(NULL, addr); + suffix = 'b'; + break; + case 2: + val = cpu_inw(NULL, addr); + suffix = 'w'; + break; + case 4: + val = cpu_inl(NULL, addr); + suffix = 'l'; + break; + } + term_printf("port%c[0x%04x] = %#0*x\n", + suffix, addr, size * 2, val); +} + +static void do_system_reset(void) +{ + qemu_system_reset_request(); +} + +static void do_system_powerdown(void) +{ + qemu_system_powerdown_request(); +} + +#if defined(TARGET_I386) +static void print_pte(uint32_t addr, uint32_t pte, uint32_t mask) +{ + term_printf("%08x: %08x %c%c%c%c%c%c%c%c\n", + addr, + pte & mask, + pte & PG_GLOBAL_MASK ? 'G' : '-', + pte & PG_PSE_MASK ? 'P' : '-', + pte & PG_DIRTY_MASK ? 'D' : '-', + pte & PG_ACCESSED_MASK ? 'A' : '-', + pte & PG_PCD_MASK ? 'C' : '-', + pte & PG_PWT_MASK ? 'T' : '-', + pte & PG_USER_MASK ? 'U' : '-', + pte & PG_RW_MASK ? 'W' : '-'); +} + +static void tlb_info(void) +{ + CPUState *env; + int l1, l2; + uint32_t pgd, pde, pte; + + env = mon_get_cpu(); + if (!env) + return; + + if (!(env->cr[0] & CR0_PG_MASK)) { + term_printf("PG disabled\n"); + return; + } + pgd = env->cr[3] & ~0xfff; + for(l1 = 0; l1 < 1024; l1++) { + cpu_physical_memory_read(pgd + l1 * 4, (uint8_t *)&pde, 4); + pde = le32_to_cpu(pde); + if (pde & PG_PRESENT_MASK) { + if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { + print_pte((l1 << 22), pde, ~((1 << 20) - 1)); + } else { + for(l2 = 0; l2 < 1024; l2++) { + cpu_physical_memory_read((pde & ~0xfff) + l2 * 4, + (uint8_t *)&pte, 4); + pte = le32_to_cpu(pte); + if (pte & PG_PRESENT_MASK) { + print_pte((l1 << 22) + (l2 << 12), + pte & ~PG_PSE_MASK, + ~0xfff); + } + } + } + } + } +} + +static void mem_print(uint32_t *pstart, int *plast_prot, + uint32_t end, int prot) +{ + int prot1; + prot1 = *plast_prot; + if (prot != prot1) { + if (*pstart != -1) { + term_printf("%08x-%08x %08x %c%c%c\n", + *pstart, end, end - *pstart, + prot1 & PG_USER_MASK ? 'u' : '-', + 'r', + prot1 & PG_RW_MASK ? 'w' : '-'); + } + if (prot != 0) + *pstart = end; + else + *pstart = -1; + *plast_prot = prot; + } +} + +static void mem_info(void) +{ + CPUState *env; + int l1, l2, prot, last_prot; + uint32_t pgd, pde, pte, start, end; + + env = mon_get_cpu(); + if (!env) + return; + + if (!(env->cr[0] & CR0_PG_MASK)) { + term_printf("PG disabled\n"); + return; + } + pgd = env->cr[3] & ~0xfff; + last_prot = 0; + start = -1; + for(l1 = 0; l1 < 1024; l1++) { + cpu_physical_memory_read(pgd + l1 * 4, (uint8_t *)&pde, 4); + pde = le32_to_cpu(pde); + end = l1 << 22; + if (pde & PG_PRESENT_MASK) { + if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { + prot = pde & (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK); + mem_print(&start, &last_prot, end, prot); + } else { + for(l2 = 0; l2 < 1024; l2++) { + cpu_physical_memory_read((pde & ~0xfff) + l2 * 4, + (uint8_t *)&pte, 4); + pte = le32_to_cpu(pte); + end = (l1 << 22) + (l2 << 12); + if (pte & PG_PRESENT_MASK) { + prot = pte & (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK); + } else { + prot = 0; + } + mem_print(&start, &last_prot, end, prot); + } + } + } else { + prot = 0; + mem_print(&start, &last_prot, end, prot); + } + } +} +#endif + +static void do_info_kqemu(void) +{ +#ifdef USE_KQEMU + CPUState *env; + int val; + val = 0; + env = mon_get_cpu(); + if (!env) { + term_printf("No cpu initialized yet"); + return; + } + val = env->kqemu_enabled; + term_printf("kqemu is %s\n", val ? "enabled" : "disabled"); +#else + term_printf("kqemu support is not compiled\n"); +#endif +} + +static term_cmd_t term_cmds[] = { + { "help|?", "s?", do_help, + "[cmd]", "show the help" }, + { "commit", "", do_commit, + "", "commit changes to the disk images (if -snapshot is used)" }, + { "info", "s?", do_info, + "subcommand", "show various information about the system state" }, + { "q|quit", "", do_quit, + "", "quit the emulator" }, + { "eject", "-fB", do_eject, + "[-f] device", "eject a removable media (use -f to force it)" }, + { "change", "BF", do_change, + "device filename", "change a removable media" }, + { "screendump", "F", do_screen_dump, + "filename", "save screen into PPM image 'filename'" }, + { "log", "s", do_log, + "item1[,...]", "activate logging of the specified items to '/tmp/qemu.log'" }, + { "savevm", "F", do_savevm, + "filename", "save the whole virtual machine state to 'filename'" }, + { "loadvm", "F", do_loadvm, + "filename", "restore the whole virtual machine state from 'filename'" }, + { "stop", "", do_stop, + "", "stop emulation", }, + { "c|cont", "", do_cont, + "", "resume emulation", }, +#ifdef CONFIG_GDBSTUB + { "gdbserver", "i?", do_gdbserver, + "[port]", "start gdbserver session (default port=1234)", }, +#endif + { "x", "/l", do_memory_dump, + "/fmt addr", "virtual memory dump starting at 'addr'", }, + { "xp", "/l", do_physical_memory_dump, + "/fmt addr", "physical memory dump starting at 'addr'", }, + { "p|print", "/l", do_print, + "/fmt expr", "print expression value (use $reg for CPU register access)", }, + { "i", "/ii.", do_ioport_read, + "/fmt addr", "I/O port read" }, + + { "sendkey", "s", do_send_key, + "keys", "send keys to the VM (e.g. 'sendkey ctrl-alt-f1')" }, + { "system_reset", "", do_system_reset, + "", "reset the system" }, + { "system_powerdown", "", do_system_powerdown, + "", "send system power down event" }, + { "sum", "ii", do_sum, + "addr size", "compute the checksum of a memory region" }, + { "usb_add", "s", do_usb_add, + "device", "add USB device (e.g. 'host:bus.addr' or 'host:vendor_id:product_id')" }, + { "usb_del", "s", do_usb_del, + "device", "remove USB device 'bus.addr'" }, + { "cpu", "i", do_cpu_set, + "index", "set the default CPU" }, + { NULL, NULL, }, +}; + +static term_cmd_t info_cmds[] = { + { "version", "", do_info_version, + "", "show the version of qemu" }, + { "network", "", do_info_network, + "", "show the network state" }, + { "block", "", do_info_block, + "", "show the block devices" }, + { "registers", "", do_info_registers, + "", "show the cpu registers" }, + { "cpus", "", do_info_cpus, + "", "show infos for each CPU" }, + { "history", "", do_info_history, + "", "show the command line history", }, + { "irq", "", irq_info, + "", "show the interrupts statistics (if available)", }, + { "pic", "", pic_info, + "", "show i8259 (PIC) state", }, + { "pci", "", pci_info, + "", "show PCI info", }, +#if defined(TARGET_I386) + { "tlb", "", tlb_info, + "", "show virtual to physical memory mappings", }, + { "mem", "", mem_info, + "", "show the active virtual memory mappings", }, +#endif + { "jit", "", do_info_jit, + "", "show dynamic compiler info", }, + { "kqemu", "", do_info_kqemu, + "", "show kqemu information", }, + { "usb", "", usb_info, + "", "show guest USB devices", }, + { "usbhost", "", usb_host_info, + "", "show host USB devices", }, + { NULL, NULL, }, +}; + +/*******************************************************************/ + +static const char *pch; +static jmp_buf expr_env; + +#define MD_TLONG 0 +#define MD_I32 1 + +typedef struct MonitorDef { + const char *name; + int offset; + target_long (*get_value)(struct MonitorDef *md, int val); + int type; +} MonitorDef; + +#if defined(TARGET_I386) +static target_long monitor_get_pc (struct MonitorDef *md, int val) +{ + CPUState *env = mon_get_cpu(); + if (!env) + return 0; + return env->eip + env->segs[R_CS].base; +} +#endif + +#if defined(TARGET_PPC) +static target_long monitor_get_ccr (struct MonitorDef *md, int val) +{ + CPUState *env = mon_get_cpu(); + unsigned int u; + int i; + + if (!env) + return 0; + + u = 0; + for (i = 0; i < 8; i++) + u |= env->crf[i] << (32 - (4 * i)); + + return u; +} + +static target_long monitor_get_msr (struct MonitorDef *md, int val) +{ + CPUState *env = mon_get_cpu(); + if (!env) + return 0; + return (env->msr[MSR_POW] << MSR_POW) | + (env->msr[MSR_ILE] << MSR_ILE) | + (env->msr[MSR_EE] << MSR_EE) | + (env->msr[MSR_PR] << MSR_PR) | + (env->msr[MSR_FP] << MSR_FP) | + (env->msr[MSR_ME] << MSR_ME) | + (env->msr[MSR_FE0] << MSR_FE0) | + (env->msr[MSR_SE] << MSR_SE) | + (env->msr[MSR_BE] << MSR_BE) | + (env->msr[MSR_FE1] << MSR_FE1) | + (env->msr[MSR_IP] << MSR_IP) | + (env->msr[MSR_IR] << MSR_IR) | + (env->msr[MSR_DR] << MSR_DR) | + (env->msr[MSR_RI] << MSR_RI) | + (env->msr[MSR_LE] << MSR_LE); +} + +static target_long monitor_get_xer (struct MonitorDef *md, int val) +{ + CPUState *env = mon_get_cpu(); + if (!env) + return 0; + return (env->xer[XER_SO] << XER_SO) | + (env->xer[XER_OV] << XER_OV) | + (env->xer[XER_CA] << XER_CA) | + (env->xer[XER_BC] << XER_BC); +} + +static target_long monitor_get_decr (struct MonitorDef *md, int val) +{ + CPUState *env = mon_get_cpu(); + if (!env) + return 0; + return cpu_ppc_load_decr(env); +} + +static target_long monitor_get_tbu (struct MonitorDef *md, int val) +{ + CPUState *env = mon_get_cpu(); + if (!env) + return 0; + return cpu_ppc_load_tbu(env); +} + +static target_long monitor_get_tbl (struct MonitorDef *md, int val) +{ + CPUState *env = mon_get_cpu(); + if (!env) + return 0; + return cpu_ppc_load_tbl(env); +} +#endif + +#if defined(TARGET_SPARC) +#ifndef TARGET_SPARC64 +static target_long monitor_get_psr (struct MonitorDef *md, int val) +{ + CPUState *env = mon_get_cpu(); + if (!env) + return 0; + return GET_PSR(env); +} +#endif + +static target_long monitor_get_reg(struct MonitorDef *md, int val) +{ + CPUState *env = mon_get_cpu(); + if (!env) + return 0; + return env->regwptr[val]; +} +#endif + +static MonitorDef monitor_defs[] = { +#ifdef TARGET_I386 + +#define SEG(name, seg) \ + { name, offsetof(CPUState, segs[seg].selector), NULL, MD_I32 },\ + { name ".base", offsetof(CPUState, segs[seg].base) },\ + { name ".limit", offsetof(CPUState, segs[seg].limit), NULL, MD_I32 }, + + { "eax", offsetof(CPUState, regs[0]) }, + { "ecx", offsetof(CPUState, regs[1]) }, + { "edx", offsetof(CPUState, regs[2]) }, + { "ebx", offsetof(CPUState, regs[3]) }, + { "esp|sp", offsetof(CPUState, regs[4]) }, + { "ebp|fp", offsetof(CPUState, regs[5]) }, + { "esi", offsetof(CPUState, regs[6]) }, + { "edi", offsetof(CPUState, regs[7]) }, +#ifdef TARGET_X86_64 + { "r8", offsetof(CPUState, regs[8]) }, + { "r9", offsetof(CPUState, regs[9]) }, + { "r10", offsetof(CPUState, regs[10]) }, + { "r11", offsetof(CPUState, regs[11]) }, + { "r12", offsetof(CPUState, regs[12]) }, + { "r13", offsetof(CPUState, regs[13]) }, + { "r14", offsetof(CPUState, regs[14]) }, + { "r15", offsetof(CPUState, regs[15]) }, +#endif + { "eflags", offsetof(CPUState, eflags) }, + { "eip", offsetof(CPUState, eip) }, + SEG("cs", R_CS) + SEG("ds", R_DS) + SEG("es", R_ES) + SEG("ss", R_SS) + SEG("fs", R_FS) + SEG("gs", R_GS) + { "pc", 0, monitor_get_pc, }, +#elif defined(TARGET_PPC) + { "r0", offsetof(CPUState, gpr[0]) }, + { "r1", offsetof(CPUState, gpr[1]) }, + { "r2", offsetof(CPUState, gpr[2]) }, + { "r3", offsetof(CPUState, gpr[3]) }, + { "r4", offsetof(CPUState, gpr[4]) }, + { "r5", offsetof(CPUState, gpr[5]) }, + { "r6", offsetof(CPUState, gpr[6]) }, + { "r7", offsetof(CPUState, gpr[7]) }, + { "r8", offsetof(CPUState, gpr[8]) }, + { "r9", offsetof(CPUState, gpr[9]) }, + { "r10", offsetof(CPUState, gpr[10]) }, + { "r11", offsetof(CPUState, gpr[11]) }, + { "r12", offsetof(CPUState, gpr[12]) }, + { "r13", offsetof(CPUState, gpr[13]) }, + { "r14", offsetof(CPUState, gpr[14]) }, + { "r15", offsetof(CPUState, gpr[15]) }, + { "r16", offsetof(CPUState, gpr[16]) }, + { "r17", offsetof(CPUState, gpr[17]) }, + { "r18", offsetof(CPUState, gpr[18]) }, + { "r19", offsetof(CPUState, gpr[19]) }, + { "r20", offsetof(CPUState, gpr[20]) }, + { "r21", offsetof(CPUState, gpr[21]) }, + { "r22", offsetof(CPUState, gpr[22]) }, + { "r23", offsetof(CPUState, gpr[23]) }, + { "r24", offsetof(CPUState, gpr[24]) }, + { "r25", offsetof(CPUState, gpr[25]) }, + { "r26", offsetof(CPUState, gpr[26]) }, + { "r27", offsetof(CPUState, gpr[27]) }, + { "r28", offsetof(CPUState, gpr[28]) }, + { "r29", offsetof(CPUState, gpr[29]) }, + { "r30", offsetof(CPUState, gpr[30]) }, + { "r31", offsetof(CPUState, gpr[31]) }, + { "nip|pc", offsetof(CPUState, nip) }, + { "lr", offsetof(CPUState, lr) }, + { "ctr", offsetof(CPUState, ctr) }, + { "decr", 0, &monitor_get_decr, }, + { "ccr", 0, &monitor_get_ccr, }, + { "msr", 0, &monitor_get_msr, }, + { "xer", 0, &monitor_get_xer, }, + { "tbu", 0, &monitor_get_tbu, }, + { "tbl", 0, &monitor_get_tbl, }, + { "sdr1", offsetof(CPUState, sdr1) }, + { "sr0", offsetof(CPUState, sr[0]) }, + { "sr1", offsetof(CPUState, sr[1]) }, + { "sr2", offsetof(CPUState, sr[2]) }, + { "sr3", offsetof(CPUState, sr[3]) }, + { "sr4", offsetof(CPUState, sr[4]) }, + { "sr5", offsetof(CPUState, sr[5]) }, + { "sr6", offsetof(CPUState, sr[6]) }, + { "sr7", offsetof(CPUState, sr[7]) }, + { "sr8", offsetof(CPUState, sr[8]) }, + { "sr9", offsetof(CPUState, sr[9]) }, + { "sr10", offsetof(CPUState, sr[10]) }, + { "sr11", offsetof(CPUState, sr[11]) }, + { "sr12", offsetof(CPUState, sr[12]) }, + { "sr13", offsetof(CPUState, sr[13]) }, + { "sr14", offsetof(CPUState, sr[14]) }, + { "sr15", offsetof(CPUState, sr[15]) }, + /* Too lazy to put BATs and SPRs ... */ +#elif defined(TARGET_SPARC) + { "g0", offsetof(CPUState, gregs[0]) }, + { "g1", offsetof(CPUState, gregs[1]) }, + { "g2", offsetof(CPUState, gregs[2]) }, + { "g3", offsetof(CPUState, gregs[3]) }, + { "g4", offsetof(CPUState, gregs[4]) }, + { "g5", offsetof(CPUState, gregs[5]) }, + { "g6", offsetof(CPUState, gregs[6]) }, + { "g7", offsetof(CPUState, gregs[7]) }, + { "o0", 0, monitor_get_reg }, + { "o1", 1, monitor_get_reg }, + { "o2", 2, monitor_get_reg }, + { "o3", 3, monitor_get_reg }, + { "o4", 4, monitor_get_reg }, + { "o5", 5, monitor_get_reg }, + { "o6", 6, monitor_get_reg }, + { "o7", 7, monitor_get_reg }, + { "l0", 8, monitor_get_reg }, + { "l1", 9, monitor_get_reg }, + { "l2", 10, monitor_get_reg }, + { "l3", 11, monitor_get_reg }, + { "l4", 12, monitor_get_reg }, + { "l5", 13, monitor_get_reg }, + { "l6", 14, monitor_get_reg }, + { "l7", 15, monitor_get_reg }, + { "i0", 16, monitor_get_reg }, + { "i1", 17, monitor_get_reg }, + { "i2", 18, monitor_get_reg }, + { "i3", 19, monitor_get_reg }, + { "i4", 20, monitor_get_reg }, + { "i5", 21, monitor_get_reg }, + { "i6", 22, monitor_get_reg }, + { "i7", 23, monitor_get_reg }, + { "pc", offsetof(CPUState, pc) }, + { "npc", offsetof(CPUState, npc) }, + { "y", offsetof(CPUState, y) }, +#ifndef TARGET_SPARC64 + { "psr", 0, &monitor_get_psr, }, + { "wim", offsetof(CPUState, wim) }, +#endif + { "tbr", offsetof(CPUState, tbr) }, + { "fsr", offsetof(CPUState, fsr) }, + { "f0", offsetof(CPUState, fpr[0]) }, + { "f1", offsetof(CPUState, fpr[1]) }, + { "f2", offsetof(CPUState, fpr[2]) }, + { "f3", offsetof(CPUState, fpr[3]) }, + { "f4", offsetof(CPUState, fpr[4]) }, + { "f5", offsetof(CPUState, fpr[5]) }, + { "f6", offsetof(CPUState, fpr[6]) }, + { "f7", offsetof(CPUState, fpr[7]) }, + { "f8", offsetof(CPUState, fpr[8]) }, + { "f9", offsetof(CPUState, fpr[9]) }, + { "f10", offsetof(CPUState, fpr[10]) }, + { "f11", offsetof(CPUState, fpr[11]) }, + { "f12", offsetof(CPUState, fpr[12]) }, + { "f13", offsetof(CPUState, fpr[13]) }, + { "f14", offsetof(CPUState, fpr[14]) }, + { "f15", offsetof(CPUState, fpr[15]) }, + { "f16", offsetof(CPUState, fpr[16]) }, + { "f17", offsetof(CPUState, fpr[17]) }, + { "f18", offsetof(CPUState, fpr[18]) }, + { "f19", offsetof(CPUState, fpr[19]) }, + { "f20", offsetof(CPUState, fpr[20]) }, + { "f21", offsetof(CPUState, fpr[21]) }, + { "f22", offsetof(CPUState, fpr[22]) }, + { "f23", offsetof(CPUState, fpr[23]) }, + { "f24", offsetof(CPUState, fpr[24]) }, + { "f25", offsetof(CPUState, fpr[25]) }, + { "f26", offsetof(CPUState, fpr[26]) }, + { "f27", offsetof(CPUState, fpr[27]) }, + { "f28", offsetof(CPUState, fpr[28]) }, + { "f29", offsetof(CPUState, fpr[29]) }, + { "f30", offsetof(CPUState, fpr[30]) }, + { "f31", offsetof(CPUState, fpr[31]) }, +#ifdef TARGET_SPARC64 + { "f32", offsetof(CPUState, fpr[32]) }, + { "f34", offsetof(CPUState, fpr[34]) }, + { "f36", offsetof(CPUState, fpr[36]) }, + { "f38", offsetof(CPUState, fpr[38]) }, + { "f40", offsetof(CPUState, fpr[40]) }, + { "f42", offsetof(CPUState, fpr[42]) }, + { "f44", offsetof(CPUState, fpr[44]) }, + { "f46", offsetof(CPUState, fpr[46]) }, + { "f48", offsetof(CPUState, fpr[48]) }, + { "f50", offsetof(CPUState, fpr[50]) }, + { "f52", offsetof(CPUState, fpr[52]) }, + { "f54", offsetof(CPUState, fpr[54]) }, + { "f56", offsetof(CPUState, fpr[56]) }, + { "f58", offsetof(CPUState, fpr[58]) }, + { "f60", offsetof(CPUState, fpr[60]) }, + { "f62", offsetof(CPUState, fpr[62]) }, + { "asi", offsetof(CPUState, asi) }, + { "pstate", offsetof(CPUState, pstate) }, + { "cansave", offsetof(CPUState, cansave) }, + { "canrestore", offsetof(CPUState, canrestore) }, + { "otherwin", offsetof(CPUState, otherwin) }, + { "wstate", offsetof(CPUState, wstate) }, + { "cleanwin", offsetof(CPUState, cleanwin) }, + { "fprs", offsetof(CPUState, fprs) }, +#endif +#endif + { NULL }, +}; + +static void expr_error(const char *fmt) +{ + term_printf(fmt); + term_printf("\n"); + longjmp(expr_env, 1); +} + +/* return 0 if OK, -1 if not found, -2 if no CPU defined */ +static int get_monitor_def(target_long *pval, const char *name) +{ + MonitorDef *md; + void *ptr; + + for(md = monitor_defs; md->name != NULL; md++) { + if (compare_cmd(name, md->name)) { + if (md->get_value) { + *pval = md->get_value(md, md->offset); + } else { + CPUState *env = mon_get_cpu(); + if (!env) + return -2; + ptr = (uint8_t *)env + md->offset; + switch(md->type) { + case MD_I32: + *pval = *(int32_t *)ptr; + break; + case MD_TLONG: + *pval = *(target_long *)ptr; + break; + default: + *pval = 0; + break; + } + } + return 0; + } + } + return -1; +} + +static void next(void) +{ + if (pch != '\0') { + pch++; + while (isspace(*pch)) + pch++; + } +} + +static target_long expr_sum(void); + +static target_long expr_unary(void) +{ + target_long n; + char *p; + int ret; + + switch(*pch) { + case '+': + next(); + n = expr_unary(); + break; + case '-': + next(); + n = -expr_unary(); + break; + case '~': + next(); + n = ~expr_unary(); + break; + case '(': + next(); + n = expr_sum(); + if (*pch != ')') { + expr_error("')' expected"); + } + next(); + break; + case '\'': + pch++; + if (*pch == '\0') + expr_error("character constant expected"); + n = *pch; + pch++; + if (*pch != '\'') + expr_error("missing terminating \' character"); + next(); + break; + case '$': + { + char buf[128], *q; + + pch++; + q = buf; + while ((*pch >= 'a' && *pch <= 'z') || + (*pch >= 'A' && *pch <= 'Z') || + (*pch >= '0' && *pch <= '9') || + *pch == '_' || *pch == '.') { + if ((q - buf) < sizeof(buf) - 1) + *q++ = *pch; + pch++; + } + while (isspace(*pch)) + pch++; + *q = 0; + ret = get_monitor_def(&n, buf); + if (ret == -1) + expr_error("unknown register"); + else if (ret == -2) + expr_error("no cpu defined"); + } + break; + case '\0': + expr_error("unexpected end of expression"); + n = 0; + break; + default: + n = strtoul(pch, &p, 0); + if (pch == p) { + expr_error("invalid char in expression"); + } + pch = p; + while (isspace(*pch)) + pch++; + break; + } + return n; +} + + +static target_long expr_prod(void) +{ + target_long val, val2; + int op; + + val = expr_unary(); + for(;;) { + op = *pch; + if (op != '*' && op != '/' && op != '%') + break; + next(); + val2 = expr_unary(); + switch(op) { + default: + case '*': + val *= val2; + break; + case '/': + case '%': + if (val2 == 0) + expr_error("division by zero"); + if (op == '/') + val /= val2; + else + val %= val2; + break; + } + } + return val; +} + +static target_long expr_logic(void) +{ + target_long val, val2; + int op; + + val = expr_prod(); + for(;;) { + op = *pch; + if (op != '&' && op != '|' && op != '^') + break; + next(); + val2 = expr_prod(); + switch(op) { + default: + case '&': + val &= val2; + break; + case '|': + val |= val2; + break; + case '^': + val ^= val2; + break; + } + } + return val; +} + +static target_long expr_sum(void) +{ + target_long val, val2; + int op; + + val = expr_logic(); + for(;;) { + op = *pch; + if (op != '+' && op != '-') + break; + next(); + val2 = expr_logic(); + if (op == '+') + val += val2; + else + val -= val2; + } + return val; +} + +static int get_expr(target_long *pval, const char **pp) +{ + pch = *pp; + if (setjmp(expr_env)) { + *pp = pch; + return -1; + } + while (isspace(*pch)) + pch++; + *pval = expr_sum(); + *pp = pch; + return 0; +} + +static int get_str(char *buf, int buf_size, const char **pp) +{ + const char *p; + char *q; + int c; + + q = buf; + p = *pp; + while (isspace(*p)) + p++; + if (*p == '\0') { + fail: + *q = '\0'; + *pp = p; + return -1; + } + if (*p == '\"') { + p++; + while (*p != '\0' && *p != '\"') { + if (*p == '\\') { + p++; + c = *p++; + switch(c) { + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case '\\': + case '\'': + case '\"': + break; + default: + qemu_printf("unsupported escape code: '\\%c'\n", c); + goto fail; + } + if ((q - buf) < buf_size - 1) { + *q++ = c; + } + } else { + if ((q - buf) < buf_size - 1) { + *q++ = *p; + } + p++; + } + } + if (*p != '\"') { + qemu_printf("unterminated string\n"); + goto fail; + } + p++; + } else { + while (*p != '\0' && !isspace(*p)) { + if ((q - buf) < buf_size - 1) { + *q++ = *p; + } + p++; + } + } + *q = '\0'; + *pp = p; + return 0; +} + +static int default_fmt_format = 'x'; +static int default_fmt_size = 4; + +#define MAX_ARGS 16 + +static void monitor_handle_command(const char *cmdline) +{ + const char *p, *pstart, *typestr; + char *q; + int c, nb_args, len, i, has_arg; + term_cmd_t *cmd; + char cmdname[256]; + char buf[1024]; + void *str_allocated[MAX_ARGS]; + void *args[MAX_ARGS]; + +#ifdef DEBUG + term_printf("command='%s'\n", cmdline); +#endif + + /* extract the command name */ + p = cmdline; + q = cmdname; + while (isspace(*p)) + p++; + if (*p == '\0') + return; + pstart = p; + while (*p != '\0' && *p != '/' && !isspace(*p)) + p++; + len = p - pstart; + if (len > sizeof(cmdname) - 1) + len = sizeof(cmdname) - 1; + memcpy(cmdname, pstart, len); + cmdname[len] = '\0'; + + /* find the command */ + for(cmd = term_cmds; cmd->name != NULL; cmd++) { + if (compare_cmd(cmdname, cmd->name)) + goto found; + } + term_printf("unknown command: '%s'\n", cmdname); + return; + found: + + for(i = 0; i < MAX_ARGS; i++) + str_allocated[i] = NULL; + + /* parse the parameters */ + typestr = cmd->args_type; + nb_args = 0; + for(;;) { + c = *typestr; + if (c == '\0') + break; + typestr++; + switch(c) { + case 'F': + case 'B': + case 's': + { + int ret; + char *str; + + while (isspace(*p)) + p++; + if (*typestr == '?') { + typestr++; + if (*p == '\0') { + /* no optional string: NULL argument */ + str = NULL; + goto add_str; + } + } + ret = get_str(buf, sizeof(buf), &p); + if (ret < 0) { + switch(c) { + case 'F': + term_printf("%s: filename expected\n", cmdname); + break; + case 'B': + term_printf("%s: block device name expected\n", cmdname); + break; + default: + term_printf("%s: string expected\n", cmdname); + break; + } + goto fail; + } + str = qemu_malloc(strlen(buf) + 1); + strcpy(str, buf); + str_allocated[nb_args] = str; + add_str: + if (nb_args >= MAX_ARGS) { + error_args: + term_printf("%s: too many arguments\n", cmdname); + goto fail; + } + args[nb_args++] = str; + } + break; + case '/': + { + int count, format, size; + + while (isspace(*p)) + p++; + if (*p == '/') { + /* format found */ + p++; + count = 1; + if (isdigit(*p)) { + count = 0; + while (isdigit(*p)) { + count = count * 10 + (*p - '0'); + p++; + } + } + size = -1; + format = -1; + for(;;) { + switch(*p) { + case 'o': + case 'd': + case 'u': + case 'x': + case 'i': + case 'c': + format = *p++; + break; + case 'b': + size = 1; + p++; + break; + case 'h': + size = 2; + p++; + break; + case 'w': + size = 4; + p++; + break; + case 'g': + case 'L': + size = 8; + p++; + break; + default: + goto next; + } + } + next: + if (*p != '\0' && !isspace(*p)) { + term_printf("invalid char in format: '%c'\n", *p); + goto fail; + } + if (format < 0) + format = default_fmt_format; + if (format != 'i') { + /* for 'i', not specifying a size gives -1 as size */ + if (size < 0) + size = default_fmt_size; + } + default_fmt_size = size; + default_fmt_format = format; + } else { + count = 1; + format = default_fmt_format; + if (format != 'i') { + size = default_fmt_size; + } else { + size = -1; + } + } + if (nb_args + 3 > MAX_ARGS) + goto error_args; + args[nb_args++] = (void*)count; + args[nb_args++] = (void*)format; + args[nb_args++] = (void*)size; + } + break; + case 'i': + case 'l': + { + target_long val; + while (isspace(*p)) + p++; + if (*typestr == '?' || *typestr == '.') { + typestr++; + if (*typestr == '?') { + if (*p == '\0') + has_arg = 0; + else + has_arg = 1; + } else { + if (*p == '.') { + p++; + while (isspace(*p)) + p++; + has_arg = 1; + } else { + has_arg = 0; + } + } + if (nb_args >= MAX_ARGS) + goto error_args; + args[nb_args++] = (void *)has_arg; + if (!has_arg) { + if (nb_args >= MAX_ARGS) + goto error_args; + val = -1; + goto add_num; + } + } + if (get_expr(&val, &p)) + goto fail; + add_num: + if (c == 'i') { + if (nb_args >= MAX_ARGS) + goto error_args; + args[nb_args++] = (void *)(int)val; + } else { + if ((nb_args + 1) >= MAX_ARGS) + goto error_args; +#if TARGET_LONG_BITS == 64 + args[nb_args++] = (void *)(int)((val >> 32) & 0xffffffff); +#else + args[nb_args++] = (void *)0; +#endif + args[nb_args++] = (void *)(int)(val & 0xffffffff); + } + } + break; + case '-': + { + int has_option; + /* option */ + + c = *typestr++; + if (c == '\0') + goto bad_type; + while (isspace(*p)) + p++; + has_option = 0; + if (*p == '-') { + p++; + if (*p != c) { + term_printf("%s: unsupported option -%c\n", + cmdname, *p); + goto fail; + } + p++; + has_option = 1; + } + if (nb_args >= MAX_ARGS) + goto error_args; + args[nb_args++] = (void *)has_option; + } + break; + default: + bad_type: + term_printf("%s: unknown type '%c'\n", cmdname, c); + goto fail; + } + } + /* check that all arguments were parsed */ + while (isspace(*p)) + p++; + if (*p != '\0') { + term_printf("%s: extraneous characters at the end of line\n", + cmdname); + goto fail; + } + + switch(nb_args) { + case 0: + cmd->handler(); + break; + case 1: + cmd->handler(args[0]); + break; + case 2: + cmd->handler(args[0], args[1]); + break; + case 3: + cmd->handler(args[0], args[1], args[2]); + break; + case 4: + cmd->handler(args[0], args[1], args[2], args[3]); + break; + case 5: + cmd->handler(args[0], args[1], args[2], args[3], args[4]); + break; + case 6: + cmd->handler(args[0], args[1], args[2], args[3], args[4], args[5]); + break; + default: + term_printf("unsupported number of arguments: %d\n", nb_args); + goto fail; + } + fail: + for(i = 0; i < MAX_ARGS; i++) + qemu_free(str_allocated[i]); + return; +} + +static void cmd_completion(const char *name, const char *list) +{ + const char *p, *pstart; + char cmd[128]; + int len; + + p = list; + for(;;) { + pstart = p; + p = strchr(p, '|'); + if (!p) + p = pstart + strlen(pstart); + len = p - pstart; + if (len > sizeof(cmd) - 2) + len = sizeof(cmd) - 2; + memcpy(cmd, pstart, len); + cmd[len] = '\0'; + if (name[0] == '\0' || !strncmp(name, cmd, strlen(name))) { + add_completion(cmd); + } + if (*p == '\0') + break; + p++; + } +} + +static void file_completion(const char *input) +{ + DIR *ffs; + struct dirent *d; + char path[1024]; + char file[1024], file_prefix[1024]; + int input_path_len; + const char *p; + + p = strrchr(input, '/'); + if (!p) { + input_path_len = 0; + pstrcpy(file_prefix, sizeof(file_prefix), input); + strcpy(path, "."); + } else { + input_path_len = p - input + 1; + memcpy(path, input, input_path_len); + if (input_path_len > sizeof(path) - 1) + input_path_len = sizeof(path) - 1; + path[input_path_len] = '\0'; + pstrcpy(file_prefix, sizeof(file_prefix), p + 1); + } +#ifdef DEBUG_COMPLETION + term_printf("input='%s' path='%s' prefix='%s'\n", input, path, file_prefix); +#endif + ffs = opendir(path); + if (!ffs) + return; + for(;;) { + struct stat sb; + d = readdir(ffs); + if (!d) + break; + if (strstart(d->d_name, file_prefix, NULL)) { + memcpy(file, input, input_path_len); + strcpy(file + input_path_len, d->d_name); + /* stat the file to find out if it's a directory. + * In that case add a slash to speed up typing long paths + */ + stat(file, &sb); + if(S_ISDIR(sb.st_mode)) + strcat(file, "/"); + add_completion(file); + } + } + closedir(ffs); +} + +static void block_completion_it(void *opaque, const char *name) +{ + const char *input = opaque; + + if (input[0] == '\0' || + !strncmp(name, (char *)input, strlen(input))) { + add_completion(name); + } +} + +/* NOTE: this parser is an approximate form of the real command parser */ +static void parse_cmdline(const char *cmdline, + int *pnb_args, char **args) +{ + const char *p; + int nb_args, ret; + char buf[1024]; + + p = cmdline; + nb_args = 0; + for(;;) { + while (isspace(*p)) + p++; + if (*p == '\0') + break; + if (nb_args >= MAX_ARGS) + break; + ret = get_str(buf, sizeof(buf), &p); + args[nb_args] = qemu_strdup(buf); + nb_args++; + if (ret < 0) + break; + } + *pnb_args = nb_args; +} + +void readline_find_completion(const char *cmdline) +{ + const char *cmdname; + char *args[MAX_ARGS]; + int nb_args, i, len; + const char *ptype, *str; + term_cmd_t *cmd; + + parse_cmdline(cmdline, &nb_args, args); +#ifdef DEBUG_COMPLETION + for(i = 0; i < nb_args; i++) { + term_printf("arg%d = '%s'\n", i, (char *)args[i]); + } +#endif + + /* if the line ends with a space, it means we want to complete the + next arg */ + len = strlen(cmdline); + if (len > 0 && isspace(cmdline[len - 1])) { + if (nb_args >= MAX_ARGS) + return; + args[nb_args++] = qemu_strdup(""); + } + if (nb_args <= 1) { + /* command completion */ + if (nb_args == 0) + cmdname = ""; + else + cmdname = args[0]; + completion_index = strlen(cmdname); + for(cmd = term_cmds; cmd->name != NULL; cmd++) { + cmd_completion(cmdname, cmd->name); + } + } else { + /* find the command */ + for(cmd = term_cmds; cmd->name != NULL; cmd++) { + if (compare_cmd(args[0], cmd->name)) + goto found; + } + return; + found: + ptype = cmd->args_type; + for(i = 0; i < nb_args - 2; i++) { + if (*ptype != '\0') { + ptype++; + while (*ptype == '?') + ptype++; + } + } + str = args[nb_args - 1]; + switch(*ptype) { + case 'F': + /* file completion */ + completion_index = strlen(str); + file_completion(str); + break; + case 'B': + /* block device name completion */ + completion_index = strlen(str); + bdrv_iterate(block_completion_it, (void *)str); + break; + case 's': + /* XXX: more generic ? */ + if (!strcmp(cmd->name, "info")) { + completion_index = strlen(str); + for(cmd = info_cmds; cmd->name != NULL; cmd++) { + cmd_completion(str, cmd->name); + } + } + break; + default: + break; + } + } + for(i = 0; i < nb_args; i++) + qemu_free(args[i]); +} + +static int term_can_read(void *opaque) +{ + return 128; +} + +static void term_read(void *opaque, const uint8_t *buf, int size) +{ + int i; + for(i = 0; i < size; i++) + readline_handle_byte(buf[i]); +} + +static void monitor_start_input(void); + +static void monitor_handle_command1(void *opaque, const char *cmdline) +{ + monitor_handle_command(cmdline); + monitor_start_input(); +} + +static void monitor_start_input(void) +{ + readline_start("(qemu) ", 0, monitor_handle_command1, NULL); +} + +void monitor_init(CharDriverState *hd, int show_banner) +{ + monitor_hd = hd; + if (show_banner) { + term_printf("QEMU %s monitor - type 'help' for more information\n", + QEMU_VERSION); + } + qemu_chr_add_read_handler(hd, term_can_read, term_read, NULL); + monitor_start_input(); +} + +/* XXX: use threads ? */ +/* modal monitor readline */ +static int monitor_readline_started; +static char *monitor_readline_buf; +static int monitor_readline_buf_size; + +static void monitor_readline_cb(void *opaque, const char *input) +{ + pstrcpy(monitor_readline_buf, monitor_readline_buf_size, input); + monitor_readline_started = 0; +} + +void monitor_readline(const char *prompt, int is_password, + char *buf, int buf_size) +{ + if (is_password) { + qemu_chr_send_event(monitor_hd, CHR_EVENT_FOCUS); + } + readline_start(prompt, is_password, monitor_readline_cb, NULL); + monitor_readline_buf = buf; + monitor_readline_buf_size = buf_size; + monitor_readline_started = 1; + while (monitor_readline_started) { + main_loop_wait(10); } - add_fd_read_handler(0, term_can_read, term_read, NULL); }