mirror of
https://gitlab.com/qemu-project/qemu
synced 2024-11-05 20:35:44 +00:00
added a command line monitor
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@657 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
80cabfad16
commit
9dc39cbae3
2 changed files with 572 additions and 2 deletions
|
@ -204,7 +204,8 @@ ifeq ($(ARCH),alpha)
|
|||
endif
|
||||
|
||||
# must use static linking to avoid leaving stuff in virtual address space
|
||||
VL_OBJS=vl.o osdep.o block.o ide.o ne2000.o pckbd.o vga.o sb16.o dma.o oss.o \
|
||||
VL_OBJS=vl.o osdep.o block.o monitor.o \
|
||||
ide.o ne2000.o pckbd.o vga.o sb16.o dma.o oss.o \
|
||||
fdc.o mc146818rtc.o serial.o i8259.o i8254.o pc.o
|
||||
ifeq ($(TARGET_ARCH), ppc)
|
||||
VL_OBJS+= hw.o
|
||||
|
@ -226,7 +227,7 @@ VL_LDFLAGS+=-Wl,-T,$(SRC_PATH)/i386-vl.ld
|
|||
endif
|
||||
|
||||
$(QEMU_SYSTEM): $(VL_OBJS) libqemu.a
|
||||
$(CC) $(VL_LDFLAGS) -o $@ $^ $(LIBS) $(SDL_LIBS)
|
||||
$(CC) $(VL_LDFLAGS) -o $@ $^ $(LIBS) $(SDL_LIBS) -lutil
|
||||
|
||||
sdl.o: sdl.c
|
||||
$(CC) $(CFLAGS) $(DEFINES) $(SDL_CFLAGS) -c -o $@ $<
|
||||
|
|
569
monitor.c
Normal file
569
monitor.c
Normal file
|
@ -0,0 +1,569 @@
|
|||
/*
|
||||
* QEMU monitor
|
||||
*
|
||||
* Copyright (c) 2003-2004 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <malloc.h>
|
||||
#include <termios.h>
|
||||
#include <sys/poll.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "vl.h"
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
#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
|
||||
|
||||
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;
|
||||
|
||||
typedef struct term_cmd_t {
|
||||
const char *name;
|
||||
void (*handler)(int argc, const char **argv);
|
||||
const char *params;
|
||||
const char *help;
|
||||
} term_cmd_t;
|
||||
|
||||
static term_cmd_t term_cmds[];
|
||||
static term_cmd_t info_cmds[];
|
||||
|
||||
void term_printf(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void term_flush(void)
|
||||
{
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
static int compare_cmd(const char *name, const char *list)
|
||||
{
|
||||
const char *p, *pstart;
|
||||
int len;
|
||||
len = strlen(name);
|
||||
p = list;
|
||||
for(;;) {
|
||||
pstart = p;
|
||||
p = strchr(p, '|');
|
||||
if (!p)
|
||||
p = pstart + strlen(pstart);
|
||||
if ((p - pstart) == len && !memcmp(pstart, name, len))
|
||||
return 1;
|
||||
if (*p == '\0')
|
||||
break;
|
||||
p++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void help_cmd1(term_cmd_t *cmds, const char *prefix, const char *name)
|
||||
{
|
||||
term_cmd_t *cmd;
|
||||
|
||||
for(cmd = cmds; cmd->name != NULL; cmd++) {
|
||||
if (!name || !strcmp(name, cmd->name))
|
||||
term_printf("%s%s %s -- %s\n", prefix, cmd->name, cmd->params, cmd->help);
|
||||
}
|
||||
}
|
||||
|
||||
static void help_cmd(const char *name)
|
||||
{
|
||||
if (name && !strcmp(name, "info")) {
|
||||
help_cmd1(info_cmds, "info ", NULL);
|
||||
} else {
|
||||
help_cmd1(term_cmds, "", name);
|
||||
}
|
||||
}
|
||||
|
||||
static void do_help(int argc, const char **argv)
|
||||
{
|
||||
help_cmd(argv[1]);
|
||||
}
|
||||
|
||||
static void do_commit(int argc, const char **argv)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_DISKS; i++) {
|
||||
if (bs_table[i])
|
||||
bdrv_commit(bs_table[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void do_info(int argc, const char **argv)
|
||||
{
|
||||
term_cmd_t *cmd;
|
||||
const char *item;
|
||||
|
||||
if (argc < 2)
|
||||
goto help;
|
||||
item = argv[1];
|
||||
for(cmd = info_cmds; cmd->name != NULL; cmd++) {
|
||||
if (compare_cmd(argv[1], cmd->name))
|
||||
goto found;
|
||||
}
|
||||
help:
|
||||
help_cmd(argv[0]);
|
||||
return;
|
||||
found:
|
||||
cmd->handler(argc, argv);
|
||||
}
|
||||
|
||||
static void do_info_network(int argc, const char **argv)
|
||||
{
|
||||
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("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void do_info_block(int argc, const char **argv)
|
||||
{
|
||||
bdrv_info();
|
||||
}
|
||||
|
||||
static void do_quit(int argc, const char **argv)
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static int eject_device(BlockDriverState *bs, int force)
|
||||
{
|
||||
if (bdrv_is_inserted(bs)) {
|
||||
if (!force) {
|
||||
if (!bdrv_is_removable(bs)) {
|
||||
term_printf("device is not removable\n");
|
||||
return -1;
|
||||
}
|
||||
if (bdrv_is_locked(bs)) {
|
||||
term_printf("device is locked\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
bdrv_close(bs);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void do_eject(int argc, const char **argv)
|
||||
{
|
||||
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);
|
||||
if (!bs) {
|
||||
term_printf("device not found\n");
|
||||
return;
|
||||
}
|
||||
eject_device(bs, force);
|
||||
}
|
||||
|
||||
static void do_change(int argc, const char **argv)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
|
||||
if (argc != 3) {
|
||||
help_cmd(argv[0]);
|
||||
return;
|
||||
}
|
||||
bs = bdrv_find(argv[1]);
|
||||
if (!bs) {
|
||||
term_printf("device not found\n");
|
||||
return;
|
||||
}
|
||||
if (eject_device(bs, 0) < 0)
|
||||
return;
|
||||
bdrv_open(bs, argv[2], 0);
|
||||
}
|
||||
|
||||
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" },
|
||||
{ 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)
|
||||
{
|
||||
char *p, *pstart;
|
||||
int argc;
|
||||
const char *args[MAX_ARGS + 1];
|
||||
term_cmd_t *cmd;
|
||||
|
||||
#ifdef DEBUG
|
||||
term_printf("command='%s'\n", cmdline);
|
||||
#endif
|
||||
|
||||
/* 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;i<argc;i++) {
|
||||
term_printf(" '%s'", args[i]);
|
||||
}
|
||||
term_printf("\n");
|
||||
#endif
|
||||
if (argc <= 0)
|
||||
return;
|
||||
for(cmd = term_cmds; cmd->name != NULL; cmd++) {
|
||||
if (compare_cmd(args[0], cmd->name))
|
||||
goto found;
|
||||
}
|
||||
term_printf("unknown command: '%s'\n", args[0]);
|
||||
return;
|
||||
found:
|
||||
cmd->handler(argc, args);
|
||||
}
|
||||
|
||||
static void term_show_prompt(void)
|
||||
{
|
||||
term_printf("(qemu) ");
|
||||
fflush(stdout);
|
||||
term_cmd_buf_index = 0;
|
||||
term_cmd_buf_size = 0;
|
||||
term_esc_state = IS_NORM;
|
||||
}
|
||||
|
||||
static void term_insert_char(int ch)
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
static void term_backward_char(void)
|
||||
{
|
||||
if (term_cmd_buf_index > 0) {
|
||||
term_cmd_buf_index--;
|
||||
term_printf("\033[D");
|
||||
term_flush();
|
||||
}
|
||||
}
|
||||
|
||||
static void term_forward_char(void)
|
||||
{
|
||||
if (term_cmd_buf_index < term_cmd_buf_size) {
|
||||
term_cmd_buf_index++;
|
||||
term_printf("\033[C");
|
||||
term_flush();
|
||||
}
|
||||
}
|
||||
|
||||
static void term_delete_char(void)
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
static void term_backspace(void)
|
||||
{
|
||||
if (term_cmd_buf_index > 0) {
|
||||
term_backward_char();
|
||||
term_delete_char();
|
||||
}
|
||||
}
|
||||
|
||||
static void term_bol(void)
|
||||
{
|
||||
while (term_cmd_buf_index > 0)
|
||||
term_backward_char();
|
||||
}
|
||||
|
||||
static void term_eol(void)
|
||||
{
|
||||
while (term_cmd_buf_index < term_cmd_buf_size)
|
||||
term_forward_char();
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
break;
|
||||
case IS_ESC:
|
||||
if (ch == '[') {
|
||||
term_esc_state = IS_CSI;
|
||||
term_esc_param = 0;
|
||||
} else {
|
||||
term_esc_state = IS_NORM;
|
||||
}
|
||||
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) {
|
||||
case 1:
|
||||
term_bol();
|
||||
break;
|
||||
case 3:
|
||||
term_delete_char();
|
||||
break;
|
||||
case 4:
|
||||
term_eol();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
term_esc_state = IS_NORM;
|
||||
the_end:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
/* serial console support */
|
||||
|
||||
#define TERM_ESCAPE 0x01 /* ctrl-a is used for escape */
|
||||
|
||||
static int term_got_escape, term_command;
|
||||
|
||||
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"
|
||||
);
|
||||
}
|
||||
|
||||
/* called when a char is received */
|
||||
static void term_received_byte(int ch)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int term_can_read(void *opaque)
|
||||
{
|
||||
if (serial_console) {
|
||||
return serial_can_receive(serial_console);
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void term_read(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < size; i++)
|
||||
term_received_byte(buf[i]);
|
||||
}
|
||||
|
||||
void monitor_init(void)
|
||||
{
|
||||
if (!serial_console) {
|
||||
term_printf("QEMU %s monitor - type 'help' for more information\n",
|
||||
QEMU_VERSION);
|
||||
term_show_prompt();
|
||||
}
|
||||
add_fd_read_handler(0, term_can_read, term_read, NULL);
|
||||
}
|
Loading…
Reference in a new issue