2004-03-15 00:38:27 +03:00
|
|
|
/*
|
|
|
|
* QEMU monitor
|
2007-09-17 01:08:06 +04:00
|
|
|
*
|
2004-03-15 00:38:27 +03:00
|
|
|
* Copyright (c) 2003-2004 Fabrice Bellard
|
2007-09-17 01:08:06 +04:00
|
|
|
*
|
2004-03-15 00:38:27 +03:00
|
|
|
* 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.
|
|
|
|
*/
|
2009-03-07 18:32:56 +03:00
|
|
|
#include <dirent.h>
|
2007-11-17 20:14:51 +03:00
|
|
|
#include "hw/hw.h"
|
2009-06-05 18:53:17 +04:00
|
|
|
#include "hw/qdev.h"
|
2007-11-17 20:14:51 +03:00
|
|
|
#include "hw/usb.h"
|
|
|
|
#include "hw/pcmcia.h"
|
|
|
|
#include "hw/pc.h"
|
2012-12-12 16:24:50 +04:00
|
|
|
#include "hw/pci/pci.h"
|
2009-04-25 16:56:19 +04:00
|
|
|
#include "hw/watchdog.h"
|
2009-10-01 18:42:33 +04:00
|
|
|
#include "hw/loader.h"
|
2012-12-17 21:19:49 +04:00
|
|
|
#include "exec/gdbstub.h"
|
2012-10-24 10:43:34 +04:00
|
|
|
#include "net/net.h"
|
2009-11-25 21:48:54 +03:00
|
|
|
#include "net/slirp.h"
|
2012-12-17 21:20:05 +04:00
|
|
|
#include "char/char.h"
|
2010-10-07 14:22:54 +04:00
|
|
|
#include "ui/qemu-spice.h"
|
2012-12-17 21:20:04 +04:00
|
|
|
#include "sysemu/sysemu.h"
|
2012-12-17 21:19:49 +04:00
|
|
|
#include "monitor/monitor.h"
|
|
|
|
#include "monitor/readline.h"
|
2012-11-28 15:06:30 +04:00
|
|
|
#include "ui/console.h"
|
2012-12-17 21:20:04 +04:00
|
|
|
#include "sysemu/blockdev.h"
|
2007-11-17 20:14:51 +03:00
|
|
|
#include "audio/audio.h"
|
2012-10-24 13:12:21 +04:00
|
|
|
#include "disas/disas.h"
|
2012-12-17 21:20:04 +04:00
|
|
|
#include "sysemu/balloon.h"
|
2012-12-17 21:20:00 +04:00
|
|
|
#include "qemu/timer.h"
|
2012-12-17 21:19:50 +04:00
|
|
|
#include "migration/migration.h"
|
2012-12-17 21:20:04 +04:00
|
|
|
#include "sysemu/kvm.h"
|
2012-12-17 21:20:00 +04:00
|
|
|
#include "qemu/acl.h"
|
2012-12-17 21:19:43 +04:00
|
|
|
#include "qapi/qmp/qint.h"
|
|
|
|
#include "qapi/qmp/qfloat.h"
|
|
|
|
#include "qapi/qmp/qlist.h"
|
|
|
|
#include "qapi/qmp/qbool.h"
|
|
|
|
#include "qapi/qmp/qstring.h"
|
|
|
|
#include "qapi/qmp/qjson.h"
|
|
|
|
#include "qapi/qmp/json-streamer.h"
|
|
|
|
#include "qapi/qmp/json-parser.h"
|
2012-12-17 21:20:00 +04:00
|
|
|
#include "qemu/osdep.h"
|
2011-06-20 00:38:22 +04:00
|
|
|
#include "cpu.h"
|
2011-09-23 11:23:06 +04:00
|
|
|
#include "trace.h"
|
2011-08-31 22:31:24 +04:00
|
|
|
#include "trace/control.h"
|
2011-08-31 22:30:43 +04:00
|
|
|
#ifdef CONFIG_TRACE_SIMPLE
|
2011-08-31 22:31:24 +04:00
|
|
|
#include "trace/simple.h"
|
2010-06-24 15:34:53 +04:00
|
|
|
#endif
|
2010-10-11 20:03:51 +04:00
|
|
|
#include "ui/qemu-spice.h"
|
2012-12-17 21:19:49 +04:00
|
|
|
#include "exec/memory.h"
|
2011-09-02 21:34:48 +04:00
|
|
|
#include "qmp-commands.h"
|
|
|
|
#include "hmp.h"
|
2012-12-17 21:20:00 +04:00
|
|
|
#include "qemu/thread.h"
|
2007-12-03 20:05:38 +03:00
|
|
|
|
2011-10-16 13:53:13 +04:00
|
|
|
/* for pic/irq_info */
|
|
|
|
#if defined(TARGET_SPARC)
|
|
|
|
#include "hw/sun4m.h"
|
|
|
|
#endif
|
|
|
|
#include "hw/lm32_pic.h"
|
|
|
|
|
2004-03-15 00:38:27 +03:00
|
|
|
//#define DEBUG
|
2004-07-14 21:21:37 +04:00
|
|
|
//#define DEBUG_COMPLETION
|
2004-03-15 00:38:27 +03:00
|
|
|
|
2004-04-04 16:57:25 +04:00
|
|
|
/*
|
|
|
|
* Supported types:
|
2007-09-17 01:08:06 +04:00
|
|
|
*
|
2004-04-04 16:57:25 +04:00
|
|
|
* 'F' filename
|
2004-07-14 21:21:37 +04:00
|
|
|
* 'B' block device name
|
2004-04-04 16:57:25 +04:00
|
|
|
* 's' string (accept optional quote)
|
2010-02-10 22:24:35 +03:00
|
|
|
* 'O' option string of the form NAME=VALUE,...
|
|
|
|
* parsed according to QemuOptsList given by its name
|
|
|
|
* Example: 'device:O' uses qemu_device_opts.
|
|
|
|
* Restriction: only lists with empty desc are supported
|
|
|
|
* TODO lift the restriction
|
2005-02-11 01:00:52 +03:00
|
|
|
* 'i' 32 bit integer
|
|
|
|
* 'l' target long (32 or 64 bit)
|
2012-04-27 00:34:30 +04:00
|
|
|
* 'M' Non-negative target long (32 or 64 bit), in user mode the
|
|
|
|
* value is multiplied by 2^20 (think Mebibyte)
|
2010-10-21 19:15:47 +04:00
|
|
|
* 'o' octets (aka bytes)
|
|
|
|
* user mode accepts an optional T, t, G, g, M, m, K, k
|
|
|
|
* suffix, which multiplies the value by 2^40 for
|
|
|
|
* suffixes T and t, 2^30 for suffixes G and g, 2^20 for
|
|
|
|
* M and m, 2^10 for K and k
|
2010-01-25 16:23:06 +03:00
|
|
|
* 'T' double
|
|
|
|
* user mode accepts an optional ms, us, ns suffix,
|
|
|
|
* which divides the value by 1e3, 1e6, 1e9, respectively
|
2004-04-04 16:57:25 +04:00
|
|
|
* '/' optional gdb-like print format (like "/10x")
|
|
|
|
*
|
2009-08-28 22:27:27 +04:00
|
|
|
* '?' optional type (for all types, except '/')
|
|
|
|
* '.' other form of optional type (for 'i' and 'l')
|
2010-03-26 11:07:09 +03:00
|
|
|
* 'b' boolean
|
|
|
|
* user mode accepts "on" or "off"
|
2009-08-28 22:27:27 +04:00
|
|
|
* '-' optional parameter (eg. '-f')
|
2004-04-04 16:57:25 +04:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2010-01-25 21:18:44 +03:00
|
|
|
typedef struct MonitorCompletionData MonitorCompletionData;
|
|
|
|
struct MonitorCompletionData {
|
|
|
|
Monitor *mon;
|
|
|
|
void (*user_print)(Monitor *mon, const QObject *data);
|
|
|
|
};
|
|
|
|
|
2009-10-02 01:12:16 +04:00
|
|
|
typedef struct mon_cmd_t {
|
2004-03-15 00:38:27 +03:00
|
|
|
const char *name;
|
2004-04-04 16:57:25 +04:00
|
|
|
const char *args_type;
|
2004-03-15 00:38:27 +03:00
|
|
|
const char *params;
|
|
|
|
const char *help;
|
2009-10-07 20:41:53 +04:00
|
|
|
void (*user_print)(Monitor *mon, const QObject *data);
|
2009-10-07 20:41:51 +04:00
|
|
|
union {
|
|
|
|
void (*info)(Monitor *mon);
|
2009-10-07 20:41:52 +04:00
|
|
|
void (*cmd)(Monitor *mon, const QDict *qdict);
|
2010-02-11 04:50:02 +03:00
|
|
|
int (*cmd_new)(Monitor *mon, const QDict *params, QObject **ret_data);
|
2010-01-25 21:18:44 +03:00
|
|
|
int (*cmd_async)(Monitor *mon, const QDict *params,
|
|
|
|
MonitorCompletion *cb, void *opaque);
|
2009-10-07 20:41:51 +04:00
|
|
|
} mhandler;
|
2010-06-16 02:38:39 +04:00
|
|
|
int flags;
|
2009-10-02 01:12:16 +04:00
|
|
|
} mon_cmd_t;
|
2004-03-15 00:38:27 +03:00
|
|
|
|
2009-07-22 12:11:40 +04:00
|
|
|
/* file descriptors passed via SCM_RIGHTS */
|
2009-10-02 01:12:16 +04:00
|
|
|
typedef struct mon_fd_t mon_fd_t;
|
|
|
|
struct mon_fd_t {
|
2009-07-22 12:11:40 +04:00
|
|
|
char *name;
|
|
|
|
int fd;
|
2009-10-02 01:12:16 +04:00
|
|
|
QLIST_ENTRY(mon_fd_t) next;
|
2009-07-22 12:11:40 +04:00
|
|
|
};
|
|
|
|
|
2012-08-15 00:43:43 +04:00
|
|
|
/* file descriptor associated with a file descriptor set */
|
|
|
|
typedef struct MonFdsetFd MonFdsetFd;
|
|
|
|
struct MonFdsetFd {
|
|
|
|
int fd;
|
|
|
|
bool removed;
|
|
|
|
char *opaque;
|
|
|
|
QLIST_ENTRY(MonFdsetFd) next;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* file descriptor set containing fds passed via SCM_RIGHTS */
|
|
|
|
typedef struct MonFdset MonFdset;
|
|
|
|
struct MonFdset {
|
|
|
|
int64_t id;
|
|
|
|
QLIST_HEAD(, MonFdsetFd) fds;
|
2012-08-15 00:43:47 +04:00
|
|
|
QLIST_HEAD(, MonFdsetFd) dup_fds;
|
2012-08-15 00:43:43 +04:00
|
|
|
QLIST_ENTRY(MonFdset) next;
|
|
|
|
};
|
|
|
|
|
2009-11-27 03:59:01 +03:00
|
|
|
typedef struct MonitorControl {
|
|
|
|
QObject *id;
|
|
|
|
JSONMessageParser parser;
|
2010-02-04 23:10:05 +03:00
|
|
|
int command_mode;
|
2009-11-27 03:59:01 +03:00
|
|
|
} MonitorControl;
|
|
|
|
|
2012-06-14 21:12:57 +04:00
|
|
|
/*
|
|
|
|
* To prevent flooding clients, events can be throttled. The
|
|
|
|
* throttling is calculated globally, rather than per-Monitor
|
|
|
|
* instance.
|
|
|
|
*/
|
|
|
|
typedef struct MonitorEventState {
|
|
|
|
MonitorEvent event; /* Event being tracked */
|
|
|
|
int64_t rate; /* Period over which to throttle. 0 to disable */
|
|
|
|
int64_t last; /* Time at which event was last emitted */
|
|
|
|
QEMUTimer *timer; /* Timer for handling delayed events */
|
|
|
|
QObject *data; /* Event pending delayed dispatch */
|
|
|
|
} MonitorEventState;
|
|
|
|
|
2009-03-06 02:01:29 +03:00
|
|
|
struct Monitor {
|
|
|
|
CharDriverState *chr;
|
2009-09-10 12:58:54 +04:00
|
|
|
int mux_out;
|
|
|
|
int reset_seen;
|
2009-03-06 02:01:42 +03:00
|
|
|
int flags;
|
|
|
|
int suspend_cnt;
|
|
|
|
uint8_t outbuf[1024];
|
|
|
|
int outbuf_index;
|
|
|
|
ReadLineState *rs;
|
2009-11-27 03:59:01 +03:00
|
|
|
MonitorControl *mc;
|
2012-03-14 04:38:32 +04:00
|
|
|
CPUArchState *mon_cpu;
|
2009-03-06 02:01:42 +03:00
|
|
|
BlockDriverCompletionFunc *password_completion_cb;
|
|
|
|
void *password_opaque;
|
2009-11-19 04:05:31 +03:00
|
|
|
QError *error;
|
2009-10-02 01:12:16 +04:00
|
|
|
QLIST_HEAD(,mon_fd_t) fds;
|
2009-09-12 11:36:22 +04:00
|
|
|
QLIST_ENTRY(Monitor) entry;
|
2009-03-06 02:01:29 +03:00
|
|
|
};
|
|
|
|
|
2010-05-26 23:13:09 +04:00
|
|
|
/* QMP checker flags */
|
|
|
|
#define QMP_ACCEPT_UNKNOWNS 1
|
|
|
|
|
2009-09-12 11:36:22 +04:00
|
|
|
static QLIST_HEAD(mon_list, Monitor) mon_list;
|
2012-08-15 00:43:43 +04:00
|
|
|
static QLIST_HEAD(mon_fdsets, MonFdset) mon_fdsets;
|
2012-08-15 00:43:48 +04:00
|
|
|
static int mon_refcount;
|
2004-08-02 01:52:19 +04:00
|
|
|
|
2011-10-12 07:32:41 +04:00
|
|
|
static mon_cmd_t mon_cmds[];
|
|
|
|
static mon_cmd_t info_cmds[];
|
2004-03-15 00:38:27 +03:00
|
|
|
|
2010-09-16 00:17:45 +04:00
|
|
|
static const mon_cmd_t qmp_cmds[];
|
|
|
|
|
2010-02-18 13:41:55 +03:00
|
|
|
Monitor *cur_mon;
|
|
|
|
Monitor *default_mon;
|
2009-03-06 02:01:23 +03:00
|
|
|
|
2009-03-06 02:01:42 +03:00
|
|
|
static void monitor_command_cb(Monitor *mon, const char *cmdline,
|
|
|
|
void *opaque);
|
2008-08-19 18:44:22 +04:00
|
|
|
|
2010-02-04 23:10:06 +03:00
|
|
|
static inline int qmp_cmd_mode(const Monitor *mon)
|
|
|
|
{
|
|
|
|
return (mon->mc ? mon->mc->command_mode : 0);
|
|
|
|
}
|
|
|
|
|
2009-11-27 03:58:51 +03:00
|
|
|
/* Return true if in control mode, false otherwise */
|
|
|
|
static inline int monitor_ctrl_mode(const Monitor *mon)
|
|
|
|
{
|
|
|
|
return (mon->flags & MONITOR_USE_CONTROL);
|
|
|
|
}
|
|
|
|
|
2010-02-11 19:05:43 +03:00
|
|
|
/* Return non-zero iff we have a current monitor, and it is in QMP mode. */
|
|
|
|
int monitor_cur_is_qmp(void)
|
|
|
|
{
|
|
|
|
return cur_mon && monitor_ctrl_mode(cur_mon);
|
|
|
|
}
|
|
|
|
|
2011-09-02 21:34:50 +04:00
|
|
|
void monitor_read_command(Monitor *mon, int show_prompt)
|
2009-03-06 02:01:42 +03:00
|
|
|
{
|
2009-12-14 23:53:23 +03:00
|
|
|
if (!mon->rs)
|
|
|
|
return;
|
|
|
|
|
2009-03-06 02:01:42 +03:00
|
|
|
readline_start(mon->rs, "(qemu) ", 0, monitor_command_cb, NULL);
|
|
|
|
if (show_prompt)
|
|
|
|
readline_show_prompt(mon->rs);
|
|
|
|
}
|
2005-11-22 02:25:50 +03:00
|
|
|
|
2011-09-02 21:34:50 +04:00
|
|
|
int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func,
|
|
|
|
void *opaque)
|
2009-03-06 02:01:15 +03:00
|
|
|
{
|
2009-12-07 23:37:00 +03:00
|
|
|
if (monitor_ctrl_mode(mon)) {
|
2010-03-02 20:15:09 +03:00
|
|
|
qerror_report(QERR_MISSING_PARAMETER, "password");
|
2009-12-07 23:37:00 +03:00
|
|
|
return -EINVAL;
|
|
|
|
} else if (mon->rs) {
|
2009-03-06 02:01:51 +03:00
|
|
|
readline_start(mon->rs, "Password: ", 1, readline_func, opaque);
|
|
|
|
/* prompt is printed on return from the command handler */
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
monitor_printf(mon, "terminal does not support password prompting\n");
|
|
|
|
return -ENOTTY;
|
|
|
|
}
|
2009-03-06 02:01:15 +03:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
void monitor_flush(Monitor *mon)
|
2004-08-02 01:52:19 +04:00
|
|
|
{
|
2009-09-10 12:58:54 +04:00
|
|
|
if (mon && mon->outbuf_index != 0 && !mon->mux_out) {
|
2011-08-15 20:17:28 +04:00
|
|
|
qemu_chr_fe_write(mon->chr, mon->outbuf, mon->outbuf_index);
|
2009-03-06 02:01:42 +03:00
|
|
|
mon->outbuf_index = 0;
|
2004-08-02 01:52:19 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* flush at every end of line or if the buffer is full */
|
2009-03-06 02:01:23 +03:00
|
|
|
static void monitor_puts(Monitor *mon, const char *str)
|
2004-08-02 01:52:19 +04:00
|
|
|
{
|
2007-12-16 06:02:09 +03:00
|
|
|
char c;
|
2009-03-06 02:01:42 +03:00
|
|
|
|
2004-08-02 01:52:19 +04:00
|
|
|
for(;;) {
|
|
|
|
c = *str++;
|
|
|
|
if (c == '\0')
|
|
|
|
break;
|
2006-07-15 00:26:42 +04:00
|
|
|
if (c == '\n')
|
2009-03-06 02:01:42 +03:00
|
|
|
mon->outbuf[mon->outbuf_index++] = '\r';
|
|
|
|
mon->outbuf[mon->outbuf_index++] = c;
|
|
|
|
if (mon->outbuf_index >= (sizeof(mon->outbuf) - 1)
|
|
|
|
|| c == '\n')
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_flush(mon);
|
2004-08-02 01:52:19 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
|
2004-03-15 00:38:27 +03:00
|
|
|
{
|
2010-02-11 04:50:04 +03:00
|
|
|
char buf[4096];
|
|
|
|
|
2009-12-14 23:53:24 +03:00
|
|
|
if (!mon)
|
|
|
|
return;
|
|
|
|
|
2010-02-11 04:50:04 +03:00
|
|
|
if (monitor_ctrl_mode(mon)) {
|
|
|
|
return;
|
2009-11-27 03:59:05 +03:00
|
|
|
}
|
2010-02-11 04:50:04 +03:00
|
|
|
|
|
|
|
vsnprintf(buf, sizeof(buf), fmt, ap);
|
|
|
|
monitor_puts(mon, buf);
|
2004-03-15 00:38:27 +03:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
void monitor_printf(Monitor *mon, const char *fmt, ...)
|
2004-03-15 00:38:27 +03:00
|
|
|
{
|
2004-08-02 01:52:19 +04:00
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_vprintf(mon, fmt, ap);
|
2004-08-02 01:52:19 +04:00
|
|
|
va_end(ap);
|
2004-03-15 00:38:27 +03:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
void monitor_print_filename(Monitor *mon, const char *filename)
|
2006-12-22 17:11:32 +03:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; filename[i]; i++) {
|
2009-03-06 23:27:40 +03:00
|
|
|
switch (filename[i]) {
|
|
|
|
case ' ':
|
|
|
|
case '"':
|
|
|
|
case '\\':
|
|
|
|
monitor_printf(mon, "\\%c", filename[i]);
|
|
|
|
break;
|
|
|
|
case '\t':
|
|
|
|
monitor_printf(mon, "\\t");
|
|
|
|
break;
|
|
|
|
case '\r':
|
|
|
|
monitor_printf(mon, "\\r");
|
|
|
|
break;
|
|
|
|
case '\n':
|
|
|
|
monitor_printf(mon, "\\n");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
monitor_printf(mon, "%c", filename[i]);
|
|
|
|
break;
|
|
|
|
}
|
2006-12-22 17:11:32 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-23 23:28:05 +04:00
|
|
|
static int GCC_FMT_ATTR(2, 3) monitor_fprintf(FILE *stream,
|
|
|
|
const char *fmt, ...)
|
2004-10-09 22:08:01 +04:00
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_vprintf((Monitor *)stream, fmt, ap);
|
2004-10-09 22:08:01 +04:00
|
|
|
va_end(ap);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-10-07 20:41:55 +04:00
|
|
|
static void monitor_user_noop(Monitor *mon, const QObject *data) { }
|
|
|
|
|
2010-09-16 17:58:59 +04:00
|
|
|
static inline int handler_is_qobject(const mon_cmd_t *cmd)
|
2009-10-07 20:41:54 +04:00
|
|
|
{
|
|
|
|
return cmd->user_print != NULL;
|
|
|
|
}
|
|
|
|
|
2010-09-16 18:01:32 +04:00
|
|
|
static inline bool handler_is_async(const mon_cmd_t *cmd)
|
2010-01-25 21:18:44 +03:00
|
|
|
{
|
2010-06-16 02:38:39 +04:00
|
|
|
return cmd->flags & MONITOR_CMD_ASYNC;
|
2010-01-25 21:18:44 +03:00
|
|
|
}
|
|
|
|
|
2009-11-19 04:05:31 +03:00
|
|
|
static inline int monitor_has_error(const Monitor *mon)
|
|
|
|
{
|
|
|
|
return mon->error != NULL;
|
|
|
|
}
|
|
|
|
|
2009-11-27 03:58:58 +03:00
|
|
|
static void monitor_json_emitter(Monitor *mon, const QObject *data)
|
|
|
|
{
|
|
|
|
QString *json;
|
|
|
|
|
2010-11-22 22:10:37 +03:00
|
|
|
json = mon->flags & MONITOR_USE_PRETTY ? qobject_to_json_pretty(data) :
|
|
|
|
qobject_to_json(data);
|
2009-11-27 03:58:58 +03:00
|
|
|
assert(json != NULL);
|
|
|
|
|
2010-02-11 04:50:04 +03:00
|
|
|
qstring_append_chr(json, '\n');
|
|
|
|
monitor_puts(mon, qstring_get_str(json));
|
2009-11-27 03:59:05 +03:00
|
|
|
|
2009-11-27 03:58:58 +03:00
|
|
|
QDECREF(json);
|
|
|
|
}
|
|
|
|
|
2012-07-27 23:18:16 +04:00
|
|
|
static QDict *build_qmp_error_dict(const QError *err)
|
|
|
|
{
|
|
|
|
QObject *obj;
|
|
|
|
|
|
|
|
obj = qobject_from_jsonf("{ 'error': { 'class': %s, 'desc': %p } }",
|
|
|
|
ErrorClass_lookup[err->err_class],
|
|
|
|
qerror_human(err));
|
|
|
|
|
|
|
|
return qobject_to_qdict(obj);
|
|
|
|
}
|
|
|
|
|
2009-11-27 03:58:59 +03:00
|
|
|
static void monitor_protocol_emitter(Monitor *mon, QObject *data)
|
|
|
|
{
|
|
|
|
QDict *qmp;
|
|
|
|
|
2011-09-23 11:23:06 +04:00
|
|
|
trace_monitor_protocol_emitter(mon);
|
|
|
|
|
2009-11-27 03:58:59 +03:00
|
|
|
if (!monitor_has_error(mon)) {
|
|
|
|
/* success response */
|
2012-07-27 23:18:16 +04:00
|
|
|
qmp = qdict_new();
|
2009-11-27 03:58:59 +03:00
|
|
|
if (data) {
|
|
|
|
qobject_incref(data);
|
|
|
|
qdict_put_obj(qmp, "return", data);
|
|
|
|
} else {
|
2009-12-18 18:25:00 +03:00
|
|
|
/* return an empty QDict by default */
|
|
|
|
qdict_put(qmp, "return", qdict_new());
|
2009-11-27 03:58:59 +03:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* error response */
|
2012-07-27 23:18:16 +04:00
|
|
|
qmp = build_qmp_error_dict(mon->error);
|
2009-11-27 03:58:59 +03:00
|
|
|
QDECREF(mon->error);
|
|
|
|
mon->error = NULL;
|
|
|
|
}
|
|
|
|
|
2009-11-27 03:59:01 +03:00
|
|
|
if (mon->mc->id) {
|
|
|
|
qdict_put_obj(qmp, "id", mon->mc->id);
|
|
|
|
mon->mc->id = NULL;
|
|
|
|
}
|
|
|
|
|
2009-11-27 03:58:59 +03:00
|
|
|
monitor_json_emitter(mon, QOBJECT(qmp));
|
|
|
|
QDECREF(qmp);
|
|
|
|
}
|
|
|
|
|
2009-11-27 03:59:03 +03:00
|
|
|
static void timestamp_put(QDict *qdict)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
QObject *obj;
|
2009-12-04 21:06:39 +03:00
|
|
|
qemu_timeval tv;
|
2009-11-27 03:59:03 +03:00
|
|
|
|
2009-12-04 21:06:39 +03:00
|
|
|
err = qemu_gettimeofday(&tv);
|
2009-11-27 03:59:03 +03:00
|
|
|
if (err < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
obj = qobject_from_jsonf("{ 'seconds': %" PRId64 ", "
|
|
|
|
"'microseconds': %" PRId64 " }",
|
|
|
|
(int64_t) tv.tv_sec, (int64_t) tv.tv_usec);
|
|
|
|
qdict_put_obj(qdict, "timestamp", obj);
|
|
|
|
}
|
|
|
|
|
2012-05-21 20:59:51 +04:00
|
|
|
|
|
|
|
static const char *monitor_event_names[] = {
|
|
|
|
[QEVENT_SHUTDOWN] = "SHUTDOWN",
|
|
|
|
[QEVENT_RESET] = "RESET",
|
|
|
|
[QEVENT_POWERDOWN] = "POWERDOWN",
|
|
|
|
[QEVENT_STOP] = "STOP",
|
|
|
|
[QEVENT_RESUME] = "RESUME",
|
|
|
|
[QEVENT_VNC_CONNECTED] = "VNC_CONNECTED",
|
|
|
|
[QEVENT_VNC_INITIALIZED] = "VNC_INITIALIZED",
|
|
|
|
[QEVENT_VNC_DISCONNECTED] = "VNC_DISCONNECTED",
|
|
|
|
[QEVENT_BLOCK_IO_ERROR] = "BLOCK_IO_ERROR",
|
|
|
|
[QEVENT_RTC_CHANGE] = "RTC_CHANGE",
|
|
|
|
[QEVENT_WATCHDOG] = "WATCHDOG",
|
|
|
|
[QEVENT_SPICE_CONNECTED] = "SPICE_CONNECTED",
|
|
|
|
[QEVENT_SPICE_INITIALIZED] = "SPICE_INITIALIZED",
|
|
|
|
[QEVENT_SPICE_DISCONNECTED] = "SPICE_DISCONNECTED",
|
|
|
|
[QEVENT_BLOCK_JOB_COMPLETED] = "BLOCK_JOB_COMPLETED",
|
|
|
|
[QEVENT_BLOCK_JOB_CANCELLED] = "BLOCK_JOB_CANCELLED",
|
block: introduce block job error
The following behaviors are possible:
'report': The behavior is the same as in 1.1. An I/O error,
respectively during a read or a write, will complete the job immediately
with an error code.
'ignore': An I/O error, respectively during a read or a write, will be
ignored. For streaming, the job will complete with an error and the
backing file will be left in place. For mirroring, the sector will be
marked again as dirty and re-examined later.
'stop': The job will be paused and the job iostatus will be set to
failed or nospace, while the VM will keep running. This can only be
specified if the block device has rerror=stop and werror=stop or enospc.
'enospc': Behaves as 'stop' for ENOSPC errors, 'report' for others.
In all cases, even for 'report', the I/O error is reported as a QMP
event BLOCK_JOB_ERROR, with the same arguments as BLOCK_IO_ERROR.
It is possible that while stopping the VM a BLOCK_IO_ERROR event will be
reported and will clobber the event from BLOCK_JOB_ERROR, or vice versa.
This is not really avoidable since stopping the VM completes all pending
I/O requests. In fact, it is already possible now that a series of
BLOCK_IO_ERROR events are reported with rerror=stop, because vm_stop
calls bdrv_drain_all and this can generate further errors.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2012-09-28 19:22:58 +04:00
|
|
|
[QEVENT_BLOCK_JOB_ERROR] = "BLOCK_JOB_ERROR",
|
2012-07-23 17:15:47 +04:00
|
|
|
[QEVENT_BLOCK_JOB_READY] = "BLOCK_JOB_READY",
|
2012-05-21 20:59:51 +04:00
|
|
|
[QEVENT_DEVICE_TRAY_MOVED] = "DEVICE_TRAY_MOVED",
|
|
|
|
[QEVENT_SUSPEND] = "SUSPEND",
|
2012-08-09 00:03:01 +04:00
|
|
|
[QEVENT_SUSPEND_DISK] = "SUSPEND_DISK",
|
2012-05-21 20:59:51 +04:00
|
|
|
[QEVENT_WAKEUP] = "WAKEUP",
|
2012-06-14 21:12:56 +04:00
|
|
|
[QEVENT_BALLOON_CHANGE] = "BALLOON_CHANGE",
|
2012-08-21 12:51:57 +04:00
|
|
|
[QEVENT_SPICE_MIGRATE_COMPLETED] = "SPICE_MIGRATE_COMPLETED",
|
2012-05-21 20:59:51 +04:00
|
|
|
};
|
|
|
|
QEMU_BUILD_BUG_ON(ARRAY_SIZE(monitor_event_names) != QEVENT_MAX)
|
|
|
|
|
2012-06-14 21:12:57 +04:00
|
|
|
MonitorEventState monitor_event_state[QEVENT_MAX];
|
|
|
|
QemuMutex monitor_event_state_lock;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Emits the event to every monitor instance
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
monitor_protocol_event_emit(MonitorEvent event,
|
|
|
|
QObject *data)
|
|
|
|
{
|
|
|
|
Monitor *mon;
|
|
|
|
|
|
|
|
trace_monitor_protocol_event_emit(event, data);
|
|
|
|
QLIST_FOREACH(mon, &mon_list, entry) {
|
|
|
|
if (monitor_ctrl_mode(mon) && qmp_cmd_mode(mon)) {
|
|
|
|
monitor_json_emitter(mon, data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Queue a new event for emission to Monitor instances,
|
|
|
|
* applying any rate limiting if required.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
monitor_protocol_event_queue(MonitorEvent event,
|
|
|
|
QObject *data)
|
|
|
|
{
|
|
|
|
MonitorEventState *evstate;
|
|
|
|
int64_t now = qemu_get_clock_ns(rt_clock);
|
|
|
|
assert(event < QEVENT_MAX);
|
|
|
|
|
|
|
|
qemu_mutex_lock(&monitor_event_state_lock);
|
|
|
|
evstate = &(monitor_event_state[event]);
|
|
|
|
trace_monitor_protocol_event_queue(event,
|
|
|
|
data,
|
|
|
|
evstate->rate,
|
|
|
|
evstate->last,
|
|
|
|
now);
|
|
|
|
|
|
|
|
/* Rate limit of 0 indicates no throttling */
|
|
|
|
if (!evstate->rate) {
|
|
|
|
monitor_protocol_event_emit(event, data);
|
|
|
|
evstate->last = now;
|
|
|
|
} else {
|
|
|
|
int64_t delta = now - evstate->last;
|
|
|
|
if (evstate->data ||
|
|
|
|
delta < evstate->rate) {
|
|
|
|
/* If there's an existing event pending, replace
|
|
|
|
* it with the new event, otherwise schedule a
|
|
|
|
* timer for delayed emission
|
|
|
|
*/
|
|
|
|
if (evstate->data) {
|
|
|
|
qobject_decref(evstate->data);
|
|
|
|
} else {
|
|
|
|
int64_t then = evstate->last + evstate->rate;
|
|
|
|
qemu_mod_timer_ns(evstate->timer, then);
|
|
|
|
}
|
|
|
|
evstate->data = data;
|
|
|
|
qobject_incref(evstate->data);
|
|
|
|
} else {
|
|
|
|
monitor_protocol_event_emit(event, data);
|
|
|
|
evstate->last = now;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
qemu_mutex_unlock(&monitor_event_state_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The callback invoked by QemuTimer when a delayed
|
|
|
|
* event is ready to be emitted
|
|
|
|
*/
|
|
|
|
static void monitor_protocol_event_handler(void *opaque)
|
|
|
|
{
|
|
|
|
MonitorEventState *evstate = opaque;
|
|
|
|
int64_t now = qemu_get_clock_ns(rt_clock);
|
|
|
|
|
|
|
|
qemu_mutex_lock(&monitor_event_state_lock);
|
|
|
|
|
|
|
|
trace_monitor_protocol_event_handler(evstate->event,
|
|
|
|
evstate->data,
|
|
|
|
evstate->last,
|
|
|
|
now);
|
|
|
|
if (evstate->data) {
|
|
|
|
monitor_protocol_event_emit(evstate->event, evstate->data);
|
|
|
|
qobject_decref(evstate->data);
|
|
|
|
evstate->data = NULL;
|
|
|
|
}
|
|
|
|
evstate->last = now;
|
|
|
|
qemu_mutex_unlock(&monitor_event_state_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @event: the event ID to be limited
|
|
|
|
* @rate: the rate limit in milliseconds
|
|
|
|
*
|
|
|
|
* Sets a rate limit on a particular event, so no
|
|
|
|
* more than 1 event will be emitted within @rate
|
|
|
|
* milliseconds
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
monitor_protocol_event_throttle(MonitorEvent event,
|
|
|
|
int64_t rate)
|
|
|
|
{
|
|
|
|
MonitorEventState *evstate;
|
|
|
|
assert(event < QEVENT_MAX);
|
|
|
|
|
|
|
|
evstate = &(monitor_event_state[event]);
|
|
|
|
|
|
|
|
trace_monitor_protocol_event_throttle(event, rate);
|
|
|
|
evstate->event = event;
|
|
|
|
evstate->rate = rate * SCALE_MS;
|
|
|
|
evstate->timer = qemu_new_timer(rt_clock,
|
|
|
|
SCALE_MS,
|
|
|
|
monitor_protocol_event_handler,
|
|
|
|
evstate);
|
|
|
|
evstate->last = 0;
|
|
|
|
evstate->data = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Global, one-time initializer to configure the rate limiting
|
|
|
|
* and initialize state */
|
|
|
|
static void monitor_protocol_event_init(void)
|
|
|
|
{
|
|
|
|
qemu_mutex_init(&monitor_event_state_lock);
|
|
|
|
/* Limit RTC & BALLOON events to 1 per second */
|
|
|
|
monitor_protocol_event_throttle(QEVENT_RTC_CHANGE, 1000);
|
|
|
|
monitor_protocol_event_throttle(QEVENT_BALLOON_CHANGE, 1000);
|
|
|
|
monitor_protocol_event_throttle(QEVENT_WATCHDOG, 1000);
|
|
|
|
}
|
|
|
|
|
2009-11-27 03:59:03 +03:00
|
|
|
/**
|
|
|
|
* monitor_protocol_event(): Generate a Monitor event
|
|
|
|
*
|
|
|
|
* Event-specific data can be emitted through the (optional) 'data' parameter.
|
|
|
|
*/
|
|
|
|
void monitor_protocol_event(MonitorEvent event, QObject *data)
|
|
|
|
{
|
|
|
|
QDict *qmp;
|
|
|
|
const char *event_name;
|
|
|
|
|
2009-12-04 21:05:45 +03:00
|
|
|
assert(event < QEVENT_MAX);
|
2009-11-27 03:59:03 +03:00
|
|
|
|
2012-05-21 20:59:51 +04:00
|
|
|
event_name = monitor_event_names[event];
|
|
|
|
assert(event_name != NULL);
|
2009-11-27 03:59:03 +03:00
|
|
|
|
|
|
|
qmp = qdict_new();
|
|
|
|
timestamp_put(qmp);
|
|
|
|
qdict_put(qmp, "event", qstring_from_str(event_name));
|
2010-01-08 21:45:53 +03:00
|
|
|
if (data) {
|
|
|
|
qobject_incref(data);
|
2009-11-27 03:59:03 +03:00
|
|
|
qdict_put_obj(qmp, "data", data);
|
2010-01-08 21:45:53 +03:00
|
|
|
}
|
2009-11-27 03:59:03 +03:00
|
|
|
|
2012-06-14 21:12:57 +04:00
|
|
|
trace_monitor_protocol_event(event, event_name, qmp);
|
|
|
|
monitor_protocol_event_queue(event, QOBJECT(qmp));
|
2009-11-27 03:59:03 +03:00
|
|
|
QDECREF(qmp);
|
|
|
|
}
|
|
|
|
|
2010-02-11 04:49:48 +03:00
|
|
|
static int do_qmp_capabilities(Monitor *mon, const QDict *params,
|
|
|
|
QObject **ret_data)
|
2010-02-04 23:10:05 +03:00
|
|
|
{
|
|
|
|
/* Will setup QMP capabilities in the future */
|
|
|
|
if (monitor_ctrl_mode(mon)) {
|
|
|
|
mon->mc->command_mode = 1;
|
|
|
|
}
|
2010-02-11 04:49:48 +03:00
|
|
|
|
|
|
|
return 0;
|
2010-02-04 23:10:05 +03:00
|
|
|
}
|
|
|
|
|
2010-10-22 16:08:02 +04:00
|
|
|
static void handle_user_command(Monitor *mon, const char *cmdline);
|
|
|
|
|
2011-11-25 23:52:45 +04:00
|
|
|
char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index,
|
|
|
|
int64_t cpu_index, Error **errp)
|
2010-10-22 16:08:02 +04:00
|
|
|
{
|
2011-11-25 23:52:45 +04:00
|
|
|
char *output = NULL;
|
2010-10-22 16:08:02 +04:00
|
|
|
Monitor *old_mon, hmp;
|
|
|
|
CharDriverState mchar;
|
|
|
|
|
|
|
|
memset(&hmp, 0, sizeof(hmp));
|
|
|
|
qemu_chr_init_mem(&mchar);
|
|
|
|
hmp.chr = &mchar;
|
|
|
|
|
|
|
|
old_mon = cur_mon;
|
|
|
|
cur_mon = &hmp;
|
|
|
|
|
2011-11-25 23:52:45 +04:00
|
|
|
if (has_cpu_index) {
|
|
|
|
int ret = monitor_set_cpu(cpu_index);
|
2010-10-22 16:08:02 +04:00
|
|
|
if (ret < 0) {
|
|
|
|
cur_mon = old_mon;
|
2011-11-25 23:52:45 +04:00
|
|
|
error_set(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index",
|
|
|
|
"a CPU number");
|
2010-10-22 16:08:02 +04:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-25 23:52:45 +04:00
|
|
|
handle_user_command(&hmp, command_line);
|
2010-10-22 16:08:02 +04:00
|
|
|
cur_mon = old_mon;
|
|
|
|
|
|
|
|
if (qemu_chr_mem_osize(hmp.chr) > 0) {
|
2011-11-25 23:52:45 +04:00
|
|
|
QString *str = qemu_chr_mem_to_qs(hmp.chr);
|
|
|
|
output = g_strdup(qstring_get_str(str));
|
|
|
|
QDECREF(str);
|
|
|
|
} else {
|
|
|
|
output = g_strdup("");
|
2010-10-22 16:08:02 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
qemu_chr_close_mem(hmp.chr);
|
2011-11-25 23:52:45 +04:00
|
|
|
return output;
|
2010-10-22 16:08:02 +04:00
|
|
|
}
|
|
|
|
|
2004-03-15 00:38:27 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-10-02 01:12:16 +04:00
|
|
|
static void help_cmd_dump(Monitor *mon, const mon_cmd_t *cmds,
|
2009-03-06 02:01:23 +03:00
|
|
|
const char *prefix, const char *name)
|
2004-03-15 00:38:27 +03:00
|
|
|
{
|
2009-10-02 01:12:16 +04:00
|
|
|
const mon_cmd_t *cmd;
|
2004-03-15 00:38:27 +03:00
|
|
|
|
|
|
|
for(cmd = cmds; cmd->name != NULL; cmd++) {
|
|
|
|
if (!name || !strcmp(name, cmd->name))
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%s%s %s -- %s\n", prefix, cmd->name,
|
|
|
|
cmd->params, cmd->help);
|
2004-03-15 00:38:27 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void help_cmd(Monitor *mon, const char *name)
|
2004-03-15 00:38:27 +03:00
|
|
|
{
|
|
|
|
if (name && !strcmp(name, "info")) {
|
2009-03-06 02:01:23 +03:00
|
|
|
help_cmd_dump(mon, info_cmds, "info ", NULL);
|
2004-03-15 00:38:27 +03:00
|
|
|
} else {
|
2009-03-06 02:01:23 +03:00
|
|
|
help_cmd_dump(mon, mon_cmds, "", name);
|
2004-03-21 20:06:25 +03:00
|
|
|
if (name && !strcmp(name, "log")) {
|
2008-10-02 22:32:44 +04:00
|
|
|
const CPULogItem *item;
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "Log items (comma separated):\n");
|
|
|
|
monitor_printf(mon, "%-10s %s\n", "none", "remove all logs");
|
2004-03-21 20:06:25 +03:00
|
|
|
for(item = cpu_log_items; item->mask != 0; item++) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%-10s %s\n", item->name, item->help);
|
2004-03-21 20:06:25 +03:00
|
|
|
}
|
|
|
|
}
|
2004-03-15 00:38:27 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-28 22:27:13 +04:00
|
|
|
static void do_help_cmd(Monitor *mon, const QDict *qdict)
|
2009-08-28 22:27:08 +04:00
|
|
|
{
|
2009-08-28 22:27:13 +04:00
|
|
|
help_cmd(mon, qdict_get_try_str(qdict, "name"));
|
2009-08-28 22:27:08 +04:00
|
|
|
}
|
|
|
|
|
2011-08-31 22:31:18 +04:00
|
|
|
static void do_trace_event_set_state(Monitor *mon, const QDict *qdict)
|
2010-06-24 15:34:53 +04:00
|
|
|
{
|
|
|
|
const char *tp_name = qdict_get_str(qdict, "name");
|
|
|
|
bool new_state = qdict_get_bool(qdict, "option");
|
2011-08-31 22:31:18 +04:00
|
|
|
int ret = trace_event_set_state(tp_name, new_state);
|
2010-10-13 23:14:29 +04:00
|
|
|
|
|
|
|
if (!ret) {
|
|
|
|
monitor_printf(mon, "unknown event name \"%s\"\n", tp_name);
|
|
|
|
}
|
2010-06-24 15:34:53 +04:00
|
|
|
}
|
2010-07-13 12:26:33 +04:00
|
|
|
|
2011-10-02 17:44:37 +04:00
|
|
|
#ifdef CONFIG_TRACE_SIMPLE
|
2010-07-13 12:26:33 +04:00
|
|
|
static void do_trace_file(Monitor *mon, const QDict *qdict)
|
|
|
|
{
|
|
|
|
const char *op = qdict_get_try_str(qdict, "op");
|
|
|
|
const char *arg = qdict_get_try_str(qdict, "arg");
|
|
|
|
|
|
|
|
if (!op) {
|
|
|
|
st_print_trace_file_status((FILE *)mon, &monitor_fprintf);
|
|
|
|
} else if (!strcmp(op, "on")) {
|
|
|
|
st_set_trace_file_enabled(true);
|
|
|
|
} else if (!strcmp(op, "off")) {
|
|
|
|
st_set_trace_file_enabled(false);
|
|
|
|
} else if (!strcmp(op, "flush")) {
|
|
|
|
st_flush_trace_buffer();
|
|
|
|
} else if (!strcmp(op, "set")) {
|
|
|
|
if (arg) {
|
|
|
|
st_set_trace_file(arg);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
monitor_printf(mon, "unexpected argument \"%s\"\n", op);
|
|
|
|
help_cmd(mon, "trace-file");
|
|
|
|
}
|
|
|
|
}
|
2010-06-24 15:34:53 +04:00
|
|
|
#endif
|
|
|
|
|
2010-01-25 21:18:44 +03:00
|
|
|
static void user_monitor_complete(void *opaque, QObject *ret_data)
|
|
|
|
{
|
|
|
|
MonitorCompletionData *data = (MonitorCompletionData *)opaque;
|
|
|
|
|
|
|
|
if (ret_data) {
|
|
|
|
data->user_print(data->mon, ret_data);
|
|
|
|
}
|
|
|
|
monitor_resume(data->mon);
|
2011-08-21 07:09:37 +04:00
|
|
|
g_free(data);
|
2010-01-25 21:18:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void qmp_monitor_complete(void *opaque, QObject *ret_data)
|
|
|
|
{
|
|
|
|
monitor_protocol_emitter(opaque, ret_data);
|
|
|
|
}
|
|
|
|
|
2010-06-23 02:10:46 +04:00
|
|
|
static int qmp_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd,
|
|
|
|
const QDict *params)
|
2010-01-25 21:18:44 +03:00
|
|
|
{
|
2010-06-23 02:10:46 +04:00
|
|
|
return cmd->mhandler.cmd_async(mon, params, qmp_monitor_complete, mon);
|
2010-01-25 21:18:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void user_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd,
|
|
|
|
const QDict *params)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2011-08-21 07:09:37 +04:00
|
|
|
MonitorCompletionData *cb_data = g_malloc(sizeof(*cb_data));
|
2010-01-25 21:18:44 +03:00
|
|
|
cb_data->mon = mon;
|
|
|
|
cb_data->user_print = cmd->user_print;
|
|
|
|
monitor_suspend(mon);
|
|
|
|
ret = cmd->mhandler.cmd_async(mon, params,
|
|
|
|
user_monitor_complete, cb_data);
|
|
|
|
if (ret < 0) {
|
|
|
|
monitor_resume(mon);
|
2011-08-21 07:09:37 +04:00
|
|
|
g_free(cb_data);
|
2010-01-25 21:18:44 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-13 19:15:26 +04:00
|
|
|
static void do_info(Monitor *mon, const QDict *qdict)
|
2004-03-15 00:38:27 +03:00
|
|
|
{
|
2009-10-02 01:12:16 +04:00
|
|
|
const mon_cmd_t *cmd;
|
2009-08-28 22:27:13 +04:00
|
|
|
const char *item = qdict_get_try_str(qdict, "item");
|
2004-03-15 00:38:27 +03:00
|
|
|
|
2009-11-27 03:59:00 +03:00
|
|
|
if (!item) {
|
2004-03-15 00:38:27 +03:00
|
|
|
goto help;
|
2009-11-27 03:59:00 +03:00
|
|
|
}
|
2009-10-07 20:41:55 +04:00
|
|
|
|
|
|
|
for (cmd = info_cmds; cmd->name != NULL; cmd++) {
|
2007-09-17 01:08:06 +04:00
|
|
|
if (compare_cmd(item, cmd->name))
|
2009-10-07 20:41:55 +04:00
|
|
|
break;
|
2004-03-15 00:38:27 +03:00
|
|
|
}
|
2009-10-07 20:41:55 +04:00
|
|
|
|
2009-11-27 03:59:00 +03:00
|
|
|
if (cmd->name == NULL) {
|
2009-10-07 20:41:55 +04:00
|
|
|
goto help;
|
2009-11-27 03:59:00 +03:00
|
|
|
}
|
2009-10-07 20:41:55 +04:00
|
|
|
|
2011-10-21 22:24:28 +04:00
|
|
|
cmd->mhandler.info(mon);
|
2010-09-13 19:15:26 +04:00
|
|
|
return;
|
2009-10-07 20:41:55 +04:00
|
|
|
|
|
|
|
help:
|
|
|
|
help_cmd(mon, "info");
|
2004-03-15 00:38:27 +03:00
|
|
|
}
|
|
|
|
|
2011-09-21 21:31:51 +04:00
|
|
|
CommandInfoList *qmp_query_commands(Error **errp)
|
2009-11-27 03:58:56 +03:00
|
|
|
{
|
2011-09-21 21:31:51 +04:00
|
|
|
CommandInfoList *info, *cmd_list = NULL;
|
2009-11-27 03:58:56 +03:00
|
|
|
const mon_cmd_t *cmd;
|
|
|
|
|
2010-09-16 00:17:45 +04:00
|
|
|
for (cmd = qmp_cmds; cmd->name != NULL; cmd++) {
|
2011-10-21 22:15:31 +04:00
|
|
|
info = g_malloc0(sizeof(*info));
|
|
|
|
info->value = g_malloc0(sizeof(*info->value));
|
|
|
|
info->value->name = g_strdup(cmd->name);
|
2009-11-27 03:58:56 +03:00
|
|
|
|
2011-09-21 21:31:51 +04:00
|
|
|
info->next = cmd_list;
|
|
|
|
cmd_list = info;
|
2009-11-27 03:58:56 +03:00
|
|
|
}
|
|
|
|
|
2011-09-21 21:31:51 +04:00
|
|
|
return cmd_list;
|
2007-12-02 08:18:19 +03:00
|
|
|
}
|
|
|
|
|
2012-05-21 20:59:51 +04:00
|
|
|
EventInfoList *qmp_query_events(Error **errp)
|
|
|
|
{
|
|
|
|
EventInfoList *info, *ev_list = NULL;
|
|
|
|
MonitorEvent e;
|
|
|
|
|
|
|
|
for (e = 0 ; e < QEVENT_MAX ; e++) {
|
|
|
|
const char *event_name = monitor_event_names[e];
|
|
|
|
assert(event_name != NULL);
|
|
|
|
info = g_malloc0(sizeof(*info));
|
|
|
|
info->value = g_malloc0(sizeof(*info->value));
|
|
|
|
info->value->name = g_strdup(event_name);
|
|
|
|
|
|
|
|
info->next = ev_list;
|
|
|
|
ev_list = info;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ev_list;
|
|
|
|
}
|
|
|
|
|
2011-10-06 21:02:57 +04:00
|
|
|
/* set the current CPU defined by the user */
|
|
|
|
int monitor_set_cpu(int cpu_index)
|
2005-11-22 02:25:50 +03:00
|
|
|
{
|
2012-03-14 04:38:32 +04:00
|
|
|
CPUArchState *env;
|
2005-11-22 02:25:50 +03:00
|
|
|
|
|
|
|
for(env = first_cpu; env != NULL; env = env->next_cpu) {
|
|
|
|
if (env->cpu_index == cpu_index) {
|
2009-03-06 02:01:42 +03:00
|
|
|
cur_mon->mon_cpu = env;
|
2005-11-22 02:25:50 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-03-14 04:38:32 +04:00
|
|
|
static CPUArchState *mon_get_cpu(void)
|
2005-11-22 02:25:50 +03:00
|
|
|
{
|
2009-03-06 02:01:42 +03:00
|
|
|
if (!cur_mon->mon_cpu) {
|
2011-10-06 21:02:57 +04:00
|
|
|
monitor_set_cpu(0);
|
2005-11-22 02:25:50 +03:00
|
|
|
}
|
2009-08-18 00:19:53 +04:00
|
|
|
cpu_synchronize_state(cur_mon->mon_cpu);
|
2009-03-06 02:01:42 +03:00
|
|
|
return cur_mon->mon_cpu;
|
2005-11-22 02:25:50 +03:00
|
|
|
}
|
|
|
|
|
2011-10-24 16:53:44 +04:00
|
|
|
int monitor_get_cpu_index(void)
|
|
|
|
{
|
|
|
|
return mon_get_cpu()->cpu_index;
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_info_registers(Monitor *mon)
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
2012-03-14 04:38:32 +04:00
|
|
|
CPUArchState *env;
|
2005-11-22 02:25:50 +03:00
|
|
|
env = mon_get_cpu();
|
2012-10-05 18:04:43 +04:00
|
|
|
cpu_dump_state(env, (FILE *)mon, monitor_fprintf, CPU_DUMP_FPU);
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_info_jit(Monitor *mon)
|
2005-01-27 01:00:47 +03:00
|
|
|
{
|
2009-03-06 02:01:23 +03:00
|
|
|
dump_exec_info((FILE *)mon, monitor_fprintf);
|
2005-01-27 01:00:47 +03:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_info_history(Monitor *mon)
|
2004-04-04 17:07:25 +04:00
|
|
|
{
|
|
|
|
int i;
|
2004-08-02 01:52:19 +04:00
|
|
|
const char *str;
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2009-03-06 02:01:51 +03:00
|
|
|
if (!mon->rs)
|
|
|
|
return;
|
2004-08-02 01:52:19 +04:00
|
|
|
i = 0;
|
|
|
|
for(;;) {
|
2009-03-06 02:01:42 +03:00
|
|
|
str = readline_get_history(mon->rs, i);
|
2004-08-02 01:52:19 +04:00
|
|
|
if (!str)
|
|
|
|
break;
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%d: '%s'\n", i, str);
|
2004-10-09 21:32:58 +04:00
|
|
|
i++;
|
2004-04-04 17:07:25 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-07 11:32:30 +03:00
|
|
|
#if defined(TARGET_PPC)
|
|
|
|
/* XXX: not implemented in other targets */
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_info_cpu_stats(Monitor *mon)
|
2007-03-07 11:32:30 +03:00
|
|
|
{
|
2012-03-14 04:38:32 +04:00
|
|
|
CPUArchState *env;
|
2007-03-07 11:32:30 +03:00
|
|
|
|
|
|
|
env = mon_get_cpu();
|
2009-03-06 02:01:23 +03:00
|
|
|
cpu_dump_statistics(env, (FILE *)mon, &monitor_fprintf, 0);
|
2007-03-07 11:32:30 +03:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-08-31 22:31:18 +04:00
|
|
|
static void do_trace_print_events(Monitor *mon)
|
2010-06-24 15:34:53 +04:00
|
|
|
{
|
2011-08-31 22:31:18 +04:00
|
|
|
trace_print_events((FILE *)mon, &monitor_fprintf);
|
2010-06-24 15:34:53 +04:00
|
|
|
}
|
|
|
|
|
2011-10-17 12:03:18 +04:00
|
|
|
static int client_migrate_info(Monitor *mon, const QDict *qdict,
|
|
|
|
MonitorCompletion cb, void *opaque)
|
2010-04-23 15:28:21 +04:00
|
|
|
{
|
|
|
|
const char *protocol = qdict_get_str(qdict, "protocol");
|
|
|
|
const char *hostname = qdict_get_str(qdict, "hostname");
|
|
|
|
const char *subject = qdict_get_try_str(qdict, "cert-subject");
|
|
|
|
int port = qdict_get_try_int(qdict, "port", -1);
|
|
|
|
int tls_port = qdict_get_try_int(qdict, "tls-port", -1);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (strcmp(protocol, "spice") == 0) {
|
|
|
|
if (!using_spice) {
|
|
|
|
qerror_report(QERR_DEVICE_NOT_ACTIVE, "spice");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-03-18 11:42:39 +04:00
|
|
|
if (port == -1 && tls_port == -1) {
|
|
|
|
qerror_report(QERR_MISSING_PARAMETER, "port/tls-port");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-10-17 12:03:18 +04:00
|
|
|
ret = qemu_spice_migrate_info(hostname, port, tls_port, subject,
|
|
|
|
cb, opaque);
|
2010-04-23 15:28:21 +04:00
|
|
|
if (ret != 0) {
|
|
|
|
qerror_report(QERR_UNDEFINED_ERROR);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
qerror_report(QERR_INVALID_PARAMETER, "protocol");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-08-28 22:27:13 +04:00
|
|
|
static void do_logfile(Monitor *mon, const QDict *qdict)
|
2007-06-30 17:53:24 +04:00
|
|
|
{
|
2009-08-28 22:27:13 +04:00
|
|
|
cpu_set_log_filename(qdict_get_str(qdict, "filename"));
|
2007-06-30 17:53:24 +04:00
|
|
|
}
|
|
|
|
|
2009-08-28 22:27:13 +04:00
|
|
|
static void do_log(Monitor *mon, const QDict *qdict)
|
2004-03-21 20:06:25 +03:00
|
|
|
{
|
|
|
|
int mask;
|
2009-08-28 22:27:13 +04:00
|
|
|
const char *items = qdict_get_str(qdict, "items");
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2004-04-04 16:57:25 +04:00
|
|
|
if (!strcmp(items, "none")) {
|
2004-03-21 20:06:25 +03:00
|
|
|
mask = 0;
|
|
|
|
} else {
|
2004-04-04 16:57:25 +04:00
|
|
|
mask = cpu_str_to_log_mask(items);
|
2004-03-21 20:06:25 +03:00
|
|
|
if (!mask) {
|
2009-03-06 02:01:23 +03:00
|
|
|
help_cmd(mon, "log");
|
2004-03-21 20:06:25 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cpu_set_log(mask);
|
|
|
|
}
|
|
|
|
|
2009-08-28 22:27:13 +04:00
|
|
|
static void do_singlestep(Monitor *mon, const QDict *qdict)
|
2009-04-06 00:08:59 +04:00
|
|
|
{
|
2009-08-28 22:27:13 +04:00
|
|
|
const char *option = qdict_get_try_str(qdict, "option");
|
2009-04-06 00:08:59 +04:00
|
|
|
if (!option || !strcmp(option, "on")) {
|
|
|
|
singlestep = 1;
|
|
|
|
} else if (!strcmp(option, "off")) {
|
|
|
|
singlestep = 0;
|
|
|
|
} else {
|
|
|
|
monitor_printf(mon, "unexpected option %s\n", option);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-28 22:27:13 +04:00
|
|
|
static void do_gdbserver(Monitor *mon, const QDict *qdict)
|
2009-04-05 22:43:41 +04:00
|
|
|
{
|
2009-08-28 22:27:13 +04:00
|
|
|
const char *device = qdict_get_try_str(qdict, "device");
|
2009-04-05 22:43:41 +04:00
|
|
|
if (!device)
|
|
|
|
device = "tcp::" DEFAULT_GDBSTUB_PORT;
|
|
|
|
if (gdbserver_start(device) < 0) {
|
|
|
|
monitor_printf(mon, "Could not open gdbserver on device '%s'\n",
|
|
|
|
device);
|
|
|
|
} else if (strcmp(device, "none") == 0) {
|
2009-03-28 21:05:53 +03:00
|
|
|
monitor_printf(mon, "Disabled gdbserver\n");
|
2004-03-31 23:00:16 +04:00
|
|
|
} else {
|
2009-04-05 22:43:41 +04:00
|
|
|
monitor_printf(mon, "Waiting for gdb connection on device '%s'\n",
|
|
|
|
device);
|
2004-03-31 23:00:16 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-28 22:27:13 +04:00
|
|
|
static void do_watchdog_action(Monitor *mon, const QDict *qdict)
|
2009-04-25 16:56:19 +04:00
|
|
|
{
|
2009-08-28 22:27:13 +04:00
|
|
|
const char *action = qdict_get_str(qdict, "action");
|
2009-04-25 16:56:19 +04:00
|
|
|
if (select_watchdog_action(action) == -1) {
|
|
|
|
monitor_printf(mon, "Unknown watchdog action '%s'\n", action);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void monitor_printc(Monitor *mon, int c)
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "'");
|
2004-04-04 16:57:25 +04:00
|
|
|
switch(c) {
|
|
|
|
case '\'':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "\\'");
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case '\\':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "\\\\");
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case '\n':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "\\n");
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case '\r':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "\\r");
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (c >= 32 && c <= 126) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%c", c);
|
2004-04-04 16:57:25 +04:00
|
|
|
} else {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "\\x%02x", c);
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "'");
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void memory_dump(Monitor *mon, int count, int format, int wsize,
|
2012-10-23 14:30:10 +04:00
|
|
|
hwaddr addr, int is_physical)
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
2012-03-14 04:38:32 +04:00
|
|
|
CPUArchState *env;
|
2010-01-12 23:27:43 +03:00
|
|
|
int l, line_size, i, max_digits, len;
|
2004-04-04 16:57:25 +04:00
|
|
|
uint8_t buf[16];
|
|
|
|
uint64_t v;
|
|
|
|
|
|
|
|
if (format == 'i') {
|
|
|
|
int flags;
|
|
|
|
flags = 0;
|
2005-11-22 02:25:50 +03:00
|
|
|
env = mon_get_cpu();
|
2004-04-04 16:57:25 +04:00
|
|
|
#ifdef TARGET_I386
|
2004-04-25 22:05:08 +04:00
|
|
|
if (wsize == 2) {
|
2004-04-04 16:57:25 +04:00
|
|
|
flags = 1;
|
2004-04-25 22:05:08 +04:00
|
|
|
} else if (wsize == 4) {
|
|
|
|
flags = 0;
|
|
|
|
} else {
|
2006-04-13 01:07:07 +04:00
|
|
|
/* as default we use the current CS size */
|
2004-04-25 22:05:08 +04:00
|
|
|
flags = 0;
|
2006-04-13 01:07:07 +04:00
|
|
|
if (env) {
|
|
|
|
#ifdef TARGET_X86_64
|
2007-09-17 01:08:06 +04:00
|
|
|
if ((env->efer & MSR_EFER_LMA) &&
|
2006-04-13 01:07:07 +04:00
|
|
|
(env->segs[R_CS].flags & DESC_L_MASK))
|
|
|
|
flags = 2;
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
if (!(env->segs[R_CS].flags & DESC_B_MASK))
|
|
|
|
flags = 1;
|
|
|
|
}
|
2004-04-25 22:05:08 +04:00
|
|
|
}
|
|
|
|
#endif
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_disas(mon, env, addr, count, is_physical, flags);
|
2004-04-04 16:57:25 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = wsize * count;
|
|
|
|
if (wsize == 1)
|
|
|
|
line_size = 8;
|
|
|
|
else
|
|
|
|
line_size = 16;
|
|
|
|
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 'c':
|
|
|
|
wsize = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (len > 0) {
|
2007-09-24 22:39:04 +04:00
|
|
|
if (is_physical)
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, TARGET_FMT_plx ":", addr);
|
2007-09-24 22:39:04 +04:00
|
|
|
else
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, TARGET_FMT_lx ":", (target_ulong)addr);
|
2004-04-04 16:57:25 +04:00
|
|
|
l = len;
|
|
|
|
if (l > line_size)
|
|
|
|
l = line_size;
|
|
|
|
if (is_physical) {
|
2011-04-10 20:23:39 +04:00
|
|
|
cpu_physical_memory_read(addr, buf, l);
|
2004-04-04 16:57:25 +04:00
|
|
|
} else {
|
2005-11-22 02:25:50 +03:00
|
|
|
env = mon_get_cpu();
|
2008-08-18 18:00:20 +04:00
|
|
|
if (cpu_memory_rw_debug(env, addr, buf, l, 0) < 0) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, " Cannot access memory\n");
|
2008-08-18 18:00:20 +04:00
|
|
|
break;
|
|
|
|
}
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
2007-09-17 01:08:06 +04:00
|
|
|
i = 0;
|
2004-04-04 16:57:25 +04:00
|
|
|
while (i < l) {
|
|
|
|
switch(wsize) {
|
|
|
|
default:
|
|
|
|
case 1:
|
|
|
|
v = ldub_raw(buf + i);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
v = lduw_raw(buf + i);
|
|
|
|
break;
|
|
|
|
case 4:
|
2005-02-11 01:00:52 +03:00
|
|
|
v = (uint32_t)ldl_raw(buf + i);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
v = ldq_raw(buf + i);
|
|
|
|
break;
|
|
|
|
}
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, " ");
|
2004-04-04 16:57:25 +04:00
|
|
|
switch(format) {
|
|
|
|
case 'o':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%#*" PRIo64, max_digits, v);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case 'x':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "0x%0*" PRIx64, max_digits, v);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case 'u':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%*" PRIu64, max_digits, v);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case 'd':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%*" PRId64, max_digits, v);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case 'c':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printc(mon, v);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
i += wsize;
|
|
|
|
}
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "\n");
|
2004-04-04 16:57:25 +04:00
|
|
|
addr += l;
|
|
|
|
len -= l;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-28 22:27:17 +04:00
|
|
|
static void do_memory_dump(Monitor *mon, const QDict *qdict)
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
2009-08-28 22:27:17 +04:00
|
|
|
int count = qdict_get_int(qdict, "count");
|
|
|
|
int format = qdict_get_int(qdict, "format");
|
|
|
|
int size = qdict_get_int(qdict, "size");
|
|
|
|
target_long addr = qdict_get_int(qdict, "addr");
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
memory_dump(mon, count, format, size, addr, 0);
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
|
|
|
|
2009-08-28 22:27:17 +04:00
|
|
|
static void do_physical_memory_dump(Monitor *mon, const QDict *qdict)
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
2009-08-28 22:27:17 +04:00
|
|
|
int count = qdict_get_int(qdict, "count");
|
|
|
|
int format = qdict_get_int(qdict, "format");
|
|
|
|
int size = qdict_get_int(qdict, "size");
|
2012-10-23 14:30:10 +04:00
|
|
|
hwaddr addr = qdict_get_int(qdict, "addr");
|
2009-08-28 22:27:17 +04:00
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
memory_dump(mon, count, format, size, addr, 1);
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
|
|
|
|
2009-08-28 22:27:17 +04:00
|
|
|
static void do_print(Monitor *mon, const QDict *qdict)
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
2009-08-28 22:27:17 +04:00
|
|
|
int format = qdict_get_int(qdict, "format");
|
2012-10-23 14:30:10 +04:00
|
|
|
hwaddr val = qdict_get_int(qdict, "val");
|
2009-08-28 22:27:17 +04:00
|
|
|
|
2004-04-04 16:57:25 +04:00
|
|
|
switch(format) {
|
|
|
|
case 'o':
|
2012-10-23 14:30:10 +04:00
|
|
|
monitor_printf(mon, "%#" HWADDR_PRIo, val);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case 'x':
|
2012-10-23 14:30:10 +04:00
|
|
|
monitor_printf(mon, "%#" HWADDR_PRIx, val);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case 'u':
|
2012-10-23 14:30:10 +04:00
|
|
|
monitor_printf(mon, "%" HWADDR_PRIu, val);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
case 'd':
|
2012-10-23 14:30:10 +04:00
|
|
|
monitor_printf(mon, "%" HWADDR_PRId, val);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case 'c':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printc(mon, val);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
}
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "\n");
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
|
|
|
|
2009-08-28 22:27:14 +04:00
|
|
|
static void do_sum(Monitor *mon, const QDict *qdict)
|
2005-06-05 00:15:57 +04:00
|
|
|
{
|
|
|
|
uint32_t addr;
|
|
|
|
uint16_t sum;
|
2009-08-28 22:27:14 +04:00
|
|
|
uint32_t start = qdict_get_int(qdict, "start");
|
|
|
|
uint32_t size = qdict_get_int(qdict, "size");
|
2005-06-05 00:15:57 +04:00
|
|
|
|
|
|
|
sum = 0;
|
|
|
|
for(addr = start; addr < (start + size); addr++) {
|
2011-04-10 20:23:39 +04:00
|
|
|
uint8_t val = ldub_phys(addr);
|
2005-06-05 00:15:57 +04:00
|
|
|
/* BSD sum algorithm ('sum' Unix command) */
|
|
|
|
sum = (sum >> 1) | (sum << 15);
|
2011-04-10 20:23:39 +04:00
|
|
|
sum += val;
|
2005-06-05 00:15:57 +04:00
|
|
|
}
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%05d\n", sum);
|
2005-06-05 00:15:57 +04:00
|
|
|
}
|
|
|
|
|
2006-07-15 02:03:35 +04:00
|
|
|
static int mouse_button_state;
|
|
|
|
|
2009-08-28 22:27:15 +04:00
|
|
|
static void do_mouse_move(Monitor *mon, const QDict *qdict)
|
2006-07-15 02:03:35 +04:00
|
|
|
{
|
|
|
|
int dx, dy, dz;
|
2009-08-28 22:27:15 +04:00
|
|
|
const char *dx_str = qdict_get_str(qdict, "dx_str");
|
|
|
|
const char *dy_str = qdict_get_str(qdict, "dy_str");
|
|
|
|
const char *dz_str = qdict_get_try_str(qdict, "dz_str");
|
2006-07-15 02:03:35 +04:00
|
|
|
dx = strtol(dx_str, NULL, 0);
|
|
|
|
dy = strtol(dy_str, NULL, 0);
|
|
|
|
dz = 0;
|
2007-09-17 01:08:06 +04:00
|
|
|
if (dz_str)
|
2006-07-15 02:03:35 +04:00
|
|
|
dz = strtol(dz_str, NULL, 0);
|
|
|
|
kbd_mouse_event(dx, dy, dz, mouse_button_state);
|
|
|
|
}
|
|
|
|
|
2009-08-28 22:27:13 +04:00
|
|
|
static void do_mouse_button(Monitor *mon, const QDict *qdict)
|
2006-07-15 02:03:35 +04:00
|
|
|
{
|
2009-08-28 22:27:13 +04:00
|
|
|
int button_state = qdict_get_int(qdict, "button_state");
|
2006-07-15 02:03:35 +04:00
|
|
|
mouse_button_state = button_state;
|
|
|
|
kbd_mouse_event(0, 0, 0, mouse_button_state);
|
|
|
|
}
|
|
|
|
|
2009-08-28 22:27:18 +04:00
|
|
|
static void do_ioport_read(Monitor *mon, const QDict *qdict)
|
2004-06-08 04:55:58 +04:00
|
|
|
{
|
2009-08-28 22:27:18 +04:00
|
|
|
int size = qdict_get_int(qdict, "size");
|
|
|
|
int addr = qdict_get_int(qdict, "addr");
|
|
|
|
int has_index = qdict_haskey(qdict, "index");
|
2004-06-08 04:55:58 +04:00
|
|
|
uint32_t val;
|
|
|
|
int suffix;
|
|
|
|
|
|
|
|
if (has_index) {
|
2009-08-28 22:27:18 +04:00
|
|
|
int index = qdict_get_int(qdict, "index");
|
2009-09-20 20:05:47 +04:00
|
|
|
cpu_outb(addr & IOPORTS_MASK, index & 0xff);
|
2004-06-08 04:55:58 +04:00
|
|
|
addr++;
|
|
|
|
}
|
|
|
|
addr &= 0xffff;
|
|
|
|
|
|
|
|
switch(size) {
|
|
|
|
default:
|
|
|
|
case 1:
|
2009-09-20 20:05:47 +04:00
|
|
|
val = cpu_inb(addr);
|
2004-06-08 04:55:58 +04:00
|
|
|
suffix = 'b';
|
|
|
|
break;
|
|
|
|
case 2:
|
2009-09-20 20:05:47 +04:00
|
|
|
val = cpu_inw(addr);
|
2004-06-08 04:55:58 +04:00
|
|
|
suffix = 'w';
|
|
|
|
break;
|
|
|
|
case 4:
|
2009-09-20 20:05:47 +04:00
|
|
|
val = cpu_inl(addr);
|
2004-06-08 04:55:58 +04:00
|
|
|
suffix = 'l';
|
|
|
|
break;
|
|
|
|
}
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "port%c[0x%04x] = %#0*x\n",
|
|
|
|
suffix, addr, size * 2, val);
|
2004-06-08 04:55:58 +04:00
|
|
|
}
|
2004-06-04 15:06:21 +04:00
|
|
|
|
2009-08-28 22:27:17 +04:00
|
|
|
static void do_ioport_write(Monitor *mon, const QDict *qdict)
|
2009-07-14 12:20:11 +04:00
|
|
|
{
|
2009-08-28 22:27:17 +04:00
|
|
|
int size = qdict_get_int(qdict, "size");
|
|
|
|
int addr = qdict_get_int(qdict, "addr");
|
|
|
|
int val = qdict_get_int(qdict, "val");
|
|
|
|
|
2009-07-14 12:20:11 +04:00
|
|
|
addr &= IOPORTS_MASK;
|
|
|
|
|
|
|
|
switch (size) {
|
|
|
|
default:
|
|
|
|
case 1:
|
2009-09-20 20:05:47 +04:00
|
|
|
cpu_outb(addr, val);
|
2009-07-14 12:20:11 +04:00
|
|
|
break;
|
|
|
|
case 2:
|
2009-09-20 20:05:47 +04:00
|
|
|
cpu_outw(addr, val);
|
2009-07-14 12:20:11 +04:00
|
|
|
break;
|
|
|
|
case 4:
|
2009-09-20 20:05:47 +04:00
|
|
|
cpu_outl(addr, val);
|
2009-07-14 12:20:11 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-28 22:27:13 +04:00
|
|
|
static void do_boot_set(Monitor *mon, const QDict *qdict)
|
2008-05-05 00:11:34 +04:00
|
|
|
{
|
|
|
|
int res;
|
2009-08-28 22:27:13 +04:00
|
|
|
const char *bootdevice = qdict_get_str(qdict, "bootdevice");
|
2008-05-05 00:11:34 +04:00
|
|
|
|
2009-07-02 02:19:02 +04:00
|
|
|
res = qemu_boot_set(bootdevice);
|
|
|
|
if (res == 0) {
|
|
|
|
monitor_printf(mon, "boot device list now set to %s\n", bootdevice);
|
|
|
|
} else if (res > 0) {
|
|
|
|
monitor_printf(mon, "setting boot device list failed\n");
|
2008-05-05 00:11:34 +04:00
|
|
|
} else {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "no function defined to set boot device list for "
|
|
|
|
"this architecture\n");
|
2008-05-05 00:11:34 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-09-18 23:32:46 +04:00
|
|
|
#if defined(TARGET_I386)
|
2012-10-23 14:30:10 +04:00
|
|
|
static void print_pte(Monitor *mon, hwaddr addr,
|
|
|
|
hwaddr pte,
|
|
|
|
hwaddr mask)
|
2004-09-18 23:32:46 +04:00
|
|
|
{
|
2010-12-11 21:56:24 +03:00
|
|
|
#ifdef TARGET_X86_64
|
|
|
|
if (addr & (1ULL << 47)) {
|
|
|
|
addr |= -1LL << 48;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
monitor_printf(mon, TARGET_FMT_plx ": " TARGET_FMT_plx
|
|
|
|
" %c%c%c%c%c%c%c%c%c\n",
|
2009-03-06 02:01:23 +03:00
|
|
|
addr,
|
|
|
|
pte & mask,
|
2010-12-11 21:56:24 +03:00
|
|
|
pte & PG_NX_MASK ? 'X' : '-',
|
2009-03-06 02:01:23 +03:00
|
|
|
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' : '-');
|
2004-09-18 23:32:46 +04:00
|
|
|
}
|
|
|
|
|
2012-03-14 04:38:32 +04:00
|
|
|
static void tlb_info_32(Monitor *mon, CPUArchState *env)
|
2004-09-18 23:32:46 +04:00
|
|
|
{
|
2011-08-21 22:49:45 +04:00
|
|
|
unsigned int l1, l2;
|
2004-09-18 23:32:46 +04:00
|
|
|
uint32_t pgd, pde, pte;
|
|
|
|
|
|
|
|
pgd = env->cr[3] & ~0xfff;
|
|
|
|
for(l1 = 0; l1 < 1024; l1++) {
|
2011-03-26 23:11:05 +03:00
|
|
|
cpu_physical_memory_read(pgd + l1 * 4, &pde, 4);
|
2004-09-18 23:32:46 +04:00
|
|
|
pde = le32_to_cpu(pde);
|
|
|
|
if (pde & PG_PRESENT_MASK) {
|
|
|
|
if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) {
|
2010-12-11 21:56:24 +03:00
|
|
|
/* 4M pages */
|
|
|
|
print_pte(mon, (l1 << 22), pde, ~((1 << 21) - 1));
|
2004-09-18 23:32:46 +04:00
|
|
|
} else {
|
|
|
|
for(l2 = 0; l2 < 1024; l2++) {
|
2011-03-26 23:11:05 +03:00
|
|
|
cpu_physical_memory_read((pde & ~0xfff) + l2 * 4, &pte, 4);
|
2004-09-18 23:32:46 +04:00
|
|
|
pte = le32_to_cpu(pte);
|
|
|
|
if (pte & PG_PRESENT_MASK) {
|
2009-03-06 02:01:23 +03:00
|
|
|
print_pte(mon, (l1 << 22) + (l2 << 12),
|
2007-09-17 01:08:06 +04:00
|
|
|
pte & ~PG_PSE_MASK,
|
2004-09-18 23:32:46 +04:00
|
|
|
~0xfff);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-14 04:38:32 +04:00
|
|
|
static void tlb_info_pae32(Monitor *mon, CPUArchState *env)
|
2010-12-11 21:56:24 +03:00
|
|
|
{
|
2011-08-21 22:49:45 +04:00
|
|
|
unsigned int l1, l2, l3;
|
2010-12-11 21:56:24 +03:00
|
|
|
uint64_t pdpe, pde, pte;
|
|
|
|
uint64_t pdp_addr, pd_addr, pt_addr;
|
|
|
|
|
|
|
|
pdp_addr = env->cr[3] & ~0x1f;
|
|
|
|
for (l1 = 0; l1 < 4; l1++) {
|
2011-03-26 23:11:05 +03:00
|
|
|
cpu_physical_memory_read(pdp_addr + l1 * 8, &pdpe, 8);
|
2010-12-11 21:56:24 +03:00
|
|
|
pdpe = le64_to_cpu(pdpe);
|
|
|
|
if (pdpe & PG_PRESENT_MASK) {
|
|
|
|
pd_addr = pdpe & 0x3fffffffff000ULL;
|
|
|
|
for (l2 = 0; l2 < 512; l2++) {
|
2011-03-26 23:11:05 +03:00
|
|
|
cpu_physical_memory_read(pd_addr + l2 * 8, &pde, 8);
|
2010-12-11 21:56:24 +03:00
|
|
|
pde = le64_to_cpu(pde);
|
|
|
|
if (pde & PG_PRESENT_MASK) {
|
|
|
|
if (pde & PG_PSE_MASK) {
|
|
|
|
/* 2M pages with PAE, CR4.PSE is ignored */
|
|
|
|
print_pte(mon, (l1 << 30 ) + (l2 << 21), pde,
|
2012-10-23 14:30:10 +04:00
|
|
|
~((hwaddr)(1 << 20) - 1));
|
2010-12-11 21:56:24 +03:00
|
|
|
} else {
|
|
|
|
pt_addr = pde & 0x3fffffffff000ULL;
|
|
|
|
for (l3 = 0; l3 < 512; l3++) {
|
2011-03-26 23:11:05 +03:00
|
|
|
cpu_physical_memory_read(pt_addr + l3 * 8, &pte, 8);
|
2010-12-11 21:56:24 +03:00
|
|
|
pte = le64_to_cpu(pte);
|
|
|
|
if (pte & PG_PRESENT_MASK) {
|
|
|
|
print_pte(mon, (l1 << 30 ) + (l2 << 21)
|
|
|
|
+ (l3 << 12),
|
|
|
|
pte & ~PG_PSE_MASK,
|
2012-10-23 14:30:10 +04:00
|
|
|
~(hwaddr)0xfff);
|
2010-12-11 21:56:24 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef TARGET_X86_64
|
2012-03-14 04:38:32 +04:00
|
|
|
static void tlb_info_64(Monitor *mon, CPUArchState *env)
|
2010-12-11 21:56:24 +03:00
|
|
|
{
|
|
|
|
uint64_t l1, l2, l3, l4;
|
|
|
|
uint64_t pml4e, pdpe, pde, pte;
|
|
|
|
uint64_t pml4_addr, pdp_addr, pd_addr, pt_addr;
|
|
|
|
|
|
|
|
pml4_addr = env->cr[3] & 0x3fffffffff000ULL;
|
|
|
|
for (l1 = 0; l1 < 512; l1++) {
|
2011-03-26 23:11:05 +03:00
|
|
|
cpu_physical_memory_read(pml4_addr + l1 * 8, &pml4e, 8);
|
2010-12-11 21:56:24 +03:00
|
|
|
pml4e = le64_to_cpu(pml4e);
|
|
|
|
if (pml4e & PG_PRESENT_MASK) {
|
|
|
|
pdp_addr = pml4e & 0x3fffffffff000ULL;
|
|
|
|
for (l2 = 0; l2 < 512; l2++) {
|
2011-03-26 23:11:05 +03:00
|
|
|
cpu_physical_memory_read(pdp_addr + l2 * 8, &pdpe, 8);
|
2010-12-11 21:56:24 +03:00
|
|
|
pdpe = le64_to_cpu(pdpe);
|
|
|
|
if (pdpe & PG_PRESENT_MASK) {
|
|
|
|
if (pdpe & PG_PSE_MASK) {
|
|
|
|
/* 1G pages, CR4.PSE is ignored */
|
|
|
|
print_pte(mon, (l1 << 39) + (l2 << 30), pdpe,
|
|
|
|
0x3ffffc0000000ULL);
|
|
|
|
} else {
|
|
|
|
pd_addr = pdpe & 0x3fffffffff000ULL;
|
|
|
|
for (l3 = 0; l3 < 512; l3++) {
|
2011-03-26 23:11:05 +03:00
|
|
|
cpu_physical_memory_read(pd_addr + l3 * 8, &pde, 8);
|
2010-12-11 21:56:24 +03:00
|
|
|
pde = le64_to_cpu(pde);
|
|
|
|
if (pde & PG_PRESENT_MASK) {
|
|
|
|
if (pde & PG_PSE_MASK) {
|
|
|
|
/* 2M pages, CR4.PSE is ignored */
|
|
|
|
print_pte(mon, (l1 << 39) + (l2 << 30) +
|
|
|
|
(l3 << 21), pde,
|
|
|
|
0x3ffffffe00000ULL);
|
|
|
|
} else {
|
|
|
|
pt_addr = pde & 0x3fffffffff000ULL;
|
|
|
|
for (l4 = 0; l4 < 512; l4++) {
|
|
|
|
cpu_physical_memory_read(pt_addr
|
|
|
|
+ l4 * 8,
|
2011-03-26 23:11:05 +03:00
|
|
|
&pte, 8);
|
2010-12-11 21:56:24 +03:00
|
|
|
pte = le64_to_cpu(pte);
|
|
|
|
if (pte & PG_PRESENT_MASK) {
|
|
|
|
print_pte(mon, (l1 << 39) +
|
|
|
|
(l2 << 30) +
|
|
|
|
(l3 << 21) + (l4 << 12),
|
|
|
|
pte & ~PG_PSE_MASK,
|
|
|
|
0x3fffffffff000ULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void tlb_info(Monitor *mon)
|
|
|
|
{
|
2012-03-14 04:38:32 +04:00
|
|
|
CPUArchState *env;
|
2010-12-11 21:56:24 +03:00
|
|
|
|
|
|
|
env = mon_get_cpu();
|
|
|
|
|
|
|
|
if (!(env->cr[0] & CR0_PG_MASK)) {
|
|
|
|
monitor_printf(mon, "PG disabled\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (env->cr[4] & CR4_PAE_MASK) {
|
|
|
|
#ifdef TARGET_X86_64
|
|
|
|
if (env->hflags & HF_LMA_MASK) {
|
|
|
|
tlb_info_64(mon, env);
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
tlb_info_pae32(mon, env);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
tlb_info_32(mon, env);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-23 14:30:10 +04:00
|
|
|
static void mem_print(Monitor *mon, hwaddr *pstart,
|
2010-12-11 21:56:27 +03:00
|
|
|
int *plast_prot,
|
2012-10-23 14:30:10 +04:00
|
|
|
hwaddr end, int prot)
|
2004-09-18 23:32:46 +04:00
|
|
|
{
|
2004-11-11 21:30:24 +03:00
|
|
|
int prot1;
|
|
|
|
prot1 = *plast_prot;
|
|
|
|
if (prot != prot1) {
|
2004-09-18 23:32:46 +04:00
|
|
|
if (*pstart != -1) {
|
2010-12-11 21:56:27 +03:00
|
|
|
monitor_printf(mon, TARGET_FMT_plx "-" TARGET_FMT_plx " "
|
|
|
|
TARGET_FMT_plx " %c%c%c\n",
|
2009-03-06 02:01:23 +03:00
|
|
|
*pstart, end, end - *pstart,
|
|
|
|
prot1 & PG_USER_MASK ? 'u' : '-',
|
|
|
|
'r',
|
|
|
|
prot1 & PG_RW_MASK ? 'w' : '-');
|
2004-09-18 23:32:46 +04:00
|
|
|
}
|
|
|
|
if (prot != 0)
|
|
|
|
*pstart = end;
|
|
|
|
else
|
|
|
|
*pstart = -1;
|
|
|
|
*plast_prot = prot;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-14 04:38:32 +04:00
|
|
|
static void mem_info_32(Monitor *mon, CPUArchState *env)
|
2004-09-18 23:32:46 +04:00
|
|
|
{
|
2011-08-15 07:19:21 +04:00
|
|
|
unsigned int l1, l2;
|
|
|
|
int prot, last_prot;
|
2010-12-11 21:56:27 +03:00
|
|
|
uint32_t pgd, pde, pte;
|
2012-10-23 14:30:10 +04:00
|
|
|
hwaddr start, end;
|
2005-11-22 02:25:50 +03:00
|
|
|
|
2004-09-18 23:32:46 +04:00
|
|
|
pgd = env->cr[3] & ~0xfff;
|
|
|
|
last_prot = 0;
|
|
|
|
start = -1;
|
|
|
|
for(l1 = 0; l1 < 1024; l1++) {
|
2011-03-26 23:11:05 +03:00
|
|
|
cpu_physical_memory_read(pgd + l1 * 4, &pde, 4);
|
2004-09-18 23:32:46 +04:00
|
|
|
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);
|
2009-03-06 02:01:23 +03:00
|
|
|
mem_print(mon, &start, &last_prot, end, prot);
|
2004-09-18 23:32:46 +04:00
|
|
|
} else {
|
|
|
|
for(l2 = 0; l2 < 1024; l2++) {
|
2011-03-26 23:11:05 +03:00
|
|
|
cpu_physical_memory_read((pde & ~0xfff) + l2 * 4, &pte, 4);
|
2004-09-18 23:32:46 +04:00
|
|
|
pte = le32_to_cpu(pte);
|
|
|
|
end = (l1 << 22) + (l2 << 12);
|
|
|
|
if (pte & PG_PRESENT_MASK) {
|
2011-08-15 07:22:28 +04:00
|
|
|
prot = pte & pde &
|
|
|
|
(PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK);
|
2004-09-18 23:32:46 +04:00
|
|
|
} else {
|
|
|
|
prot = 0;
|
|
|
|
}
|
2009-03-06 02:01:23 +03:00
|
|
|
mem_print(mon, &start, &last_prot, end, prot);
|
2004-09-18 23:32:46 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
prot = 0;
|
2009-03-06 02:01:23 +03:00
|
|
|
mem_print(mon, &start, &last_prot, end, prot);
|
2004-09-18 23:32:46 +04:00
|
|
|
}
|
|
|
|
}
|
2011-08-15 07:22:04 +04:00
|
|
|
/* Flush last range */
|
2012-10-23 14:30:10 +04:00
|
|
|
mem_print(mon, &start, &last_prot, (hwaddr)1 << 32, 0);
|
2004-09-18 23:32:46 +04:00
|
|
|
}
|
2010-12-11 21:56:27 +03:00
|
|
|
|
2012-03-14 04:38:32 +04:00
|
|
|
static void mem_info_pae32(Monitor *mon, CPUArchState *env)
|
2010-12-11 21:56:27 +03:00
|
|
|
{
|
2011-08-15 07:19:21 +04:00
|
|
|
unsigned int l1, l2, l3;
|
|
|
|
int prot, last_prot;
|
2010-12-11 21:56:27 +03:00
|
|
|
uint64_t pdpe, pde, pte;
|
|
|
|
uint64_t pdp_addr, pd_addr, pt_addr;
|
2012-10-23 14:30:10 +04:00
|
|
|
hwaddr start, end;
|
2010-12-11 21:56:27 +03:00
|
|
|
|
|
|
|
pdp_addr = env->cr[3] & ~0x1f;
|
|
|
|
last_prot = 0;
|
|
|
|
start = -1;
|
|
|
|
for (l1 = 0; l1 < 4; l1++) {
|
2011-03-26 23:11:05 +03:00
|
|
|
cpu_physical_memory_read(pdp_addr + l1 * 8, &pdpe, 8);
|
2010-12-11 21:56:27 +03:00
|
|
|
pdpe = le64_to_cpu(pdpe);
|
|
|
|
end = l1 << 30;
|
|
|
|
if (pdpe & PG_PRESENT_MASK) {
|
|
|
|
pd_addr = pdpe & 0x3fffffffff000ULL;
|
|
|
|
for (l2 = 0; l2 < 512; l2++) {
|
2011-03-26 23:11:05 +03:00
|
|
|
cpu_physical_memory_read(pd_addr + l2 * 8, &pde, 8);
|
2010-12-11 21:56:27 +03:00
|
|
|
pde = le64_to_cpu(pde);
|
|
|
|
end = (l1 << 30) + (l2 << 21);
|
|
|
|
if (pde & PG_PRESENT_MASK) {
|
|
|
|
if (pde & PG_PSE_MASK) {
|
|
|
|
prot = pde & (PG_USER_MASK | PG_RW_MASK |
|
|
|
|
PG_PRESENT_MASK);
|
|
|
|
mem_print(mon, &start, &last_prot, end, prot);
|
|
|
|
} else {
|
|
|
|
pt_addr = pde & 0x3fffffffff000ULL;
|
|
|
|
for (l3 = 0; l3 < 512; l3++) {
|
2011-03-26 23:11:05 +03:00
|
|
|
cpu_physical_memory_read(pt_addr + l3 * 8, &pte, 8);
|
2010-12-11 21:56:27 +03:00
|
|
|
pte = le64_to_cpu(pte);
|
|
|
|
end = (l1 << 30) + (l2 << 21) + (l3 << 12);
|
|
|
|
if (pte & PG_PRESENT_MASK) {
|
2011-08-15 07:22:28 +04:00
|
|
|
prot = pte & pde & (PG_USER_MASK | PG_RW_MASK |
|
|
|
|
PG_PRESENT_MASK);
|
2010-12-11 21:56:27 +03:00
|
|
|
} else {
|
|
|
|
prot = 0;
|
|
|
|
}
|
|
|
|
mem_print(mon, &start, &last_prot, end, prot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
prot = 0;
|
|
|
|
mem_print(mon, &start, &last_prot, end, prot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
prot = 0;
|
|
|
|
mem_print(mon, &start, &last_prot, end, prot);
|
|
|
|
}
|
|
|
|
}
|
2011-08-15 07:22:04 +04:00
|
|
|
/* Flush last range */
|
2012-10-23 14:30:10 +04:00
|
|
|
mem_print(mon, &start, &last_prot, (hwaddr)1 << 32, 0);
|
2010-12-11 21:56:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef TARGET_X86_64
|
2012-03-14 04:38:32 +04:00
|
|
|
static void mem_info_64(Monitor *mon, CPUArchState *env)
|
2010-12-11 21:56:27 +03:00
|
|
|
{
|
|
|
|
int prot, last_prot;
|
|
|
|
uint64_t l1, l2, l3, l4;
|
|
|
|
uint64_t pml4e, pdpe, pde, pte;
|
|
|
|
uint64_t pml4_addr, pdp_addr, pd_addr, pt_addr, start, end;
|
|
|
|
|
|
|
|
pml4_addr = env->cr[3] & 0x3fffffffff000ULL;
|
|
|
|
last_prot = 0;
|
|
|
|
start = -1;
|
|
|
|
for (l1 = 0; l1 < 512; l1++) {
|
2011-03-26 23:11:05 +03:00
|
|
|
cpu_physical_memory_read(pml4_addr + l1 * 8, &pml4e, 8);
|
2010-12-11 21:56:27 +03:00
|
|
|
pml4e = le64_to_cpu(pml4e);
|
|
|
|
end = l1 << 39;
|
|
|
|
if (pml4e & PG_PRESENT_MASK) {
|
|
|
|
pdp_addr = pml4e & 0x3fffffffff000ULL;
|
|
|
|
for (l2 = 0; l2 < 512; l2++) {
|
2011-03-26 23:11:05 +03:00
|
|
|
cpu_physical_memory_read(pdp_addr + l2 * 8, &pdpe, 8);
|
2010-12-11 21:56:27 +03:00
|
|
|
pdpe = le64_to_cpu(pdpe);
|
|
|
|
end = (l1 << 39) + (l2 << 30);
|
|
|
|
if (pdpe & PG_PRESENT_MASK) {
|
|
|
|
if (pdpe & PG_PSE_MASK) {
|
2011-01-15 11:31:00 +03:00
|
|
|
prot = pdpe & (PG_USER_MASK | PG_RW_MASK |
|
|
|
|
PG_PRESENT_MASK);
|
2011-08-15 07:22:28 +04:00
|
|
|
prot &= pml4e;
|
2010-12-11 21:56:27 +03:00
|
|
|
mem_print(mon, &start, &last_prot, end, prot);
|
|
|
|
} else {
|
|
|
|
pd_addr = pdpe & 0x3fffffffff000ULL;
|
|
|
|
for (l3 = 0; l3 < 512; l3++) {
|
2011-03-26 23:11:05 +03:00
|
|
|
cpu_physical_memory_read(pd_addr + l3 * 8, &pde, 8);
|
2010-12-11 21:56:27 +03:00
|
|
|
pde = le64_to_cpu(pde);
|
|
|
|
end = (l1 << 39) + (l2 << 30) + (l3 << 21);
|
|
|
|
if (pde & PG_PRESENT_MASK) {
|
|
|
|
if (pde & PG_PSE_MASK) {
|
|
|
|
prot = pde & (PG_USER_MASK | PG_RW_MASK |
|
|
|
|
PG_PRESENT_MASK);
|
2011-08-15 07:22:28 +04:00
|
|
|
prot &= pml4e & pdpe;
|
2010-12-11 21:56:27 +03:00
|
|
|
mem_print(mon, &start, &last_prot, end, prot);
|
|
|
|
} else {
|
|
|
|
pt_addr = pde & 0x3fffffffff000ULL;
|
|
|
|
for (l4 = 0; l4 < 512; l4++) {
|
|
|
|
cpu_physical_memory_read(pt_addr
|
|
|
|
+ l4 * 8,
|
2011-03-26 23:11:05 +03:00
|
|
|
&pte, 8);
|
2010-12-11 21:56:27 +03:00
|
|
|
pte = le64_to_cpu(pte);
|
|
|
|
end = (l1 << 39) + (l2 << 30) +
|
|
|
|
(l3 << 21) + (l4 << 12);
|
|
|
|
if (pte & PG_PRESENT_MASK) {
|
|
|
|
prot = pte & (PG_USER_MASK | PG_RW_MASK |
|
|
|
|
PG_PRESENT_MASK);
|
2011-08-15 07:22:28 +04:00
|
|
|
prot &= pml4e & pdpe & pde;
|
2010-12-11 21:56:27 +03:00
|
|
|
} else {
|
|
|
|
prot = 0;
|
|
|
|
}
|
|
|
|
mem_print(mon, &start, &last_prot, end, prot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
prot = 0;
|
|
|
|
mem_print(mon, &start, &last_prot, end, prot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
prot = 0;
|
|
|
|
mem_print(mon, &start, &last_prot, end, prot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
prot = 0;
|
|
|
|
mem_print(mon, &start, &last_prot, end, prot);
|
|
|
|
}
|
|
|
|
}
|
2011-08-15 07:22:04 +04:00
|
|
|
/* Flush last range */
|
2012-10-23 14:30:10 +04:00
|
|
|
mem_print(mon, &start, &last_prot, (hwaddr)1 << 48, 0);
|
2010-12-11 21:56:27 +03:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void mem_info(Monitor *mon)
|
|
|
|
{
|
2012-03-14 04:38:32 +04:00
|
|
|
CPUArchState *env;
|
2010-12-11 21:56:27 +03:00
|
|
|
|
|
|
|
env = mon_get_cpu();
|
|
|
|
|
|
|
|
if (!(env->cr[0] & CR0_PG_MASK)) {
|
|
|
|
monitor_printf(mon, "PG disabled\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (env->cr[4] & CR4_PAE_MASK) {
|
|
|
|
#ifdef TARGET_X86_64
|
|
|
|
if (env->hflags & HF_LMA_MASK) {
|
|
|
|
mem_info_64(mon, env);
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
mem_info_pae32(mon, env);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mem_info_32(mon, env);
|
|
|
|
}
|
|
|
|
}
|
2004-09-18 23:32:46 +04:00
|
|
|
#endif
|
|
|
|
|
2009-03-03 09:12:22 +03:00
|
|
|
#if defined(TARGET_SH4)
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void print_tlb(Monitor *mon, int idx, tlb_t *tlb)
|
2009-03-03 09:12:22 +03:00
|
|
|
{
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, " tlb%i:\t"
|
|
|
|
"asid=%hhu vpn=%x\tppn=%x\tsz=%hhu size=%u\t"
|
|
|
|
"v=%hhu shared=%hhu cached=%hhu prot=%hhu "
|
|
|
|
"dirty=%hhu writethrough=%hhu\n",
|
|
|
|
idx,
|
|
|
|
tlb->asid, tlb->vpn, tlb->ppn, tlb->sz, tlb->size,
|
|
|
|
tlb->v, tlb->sh, tlb->c, tlb->pr,
|
|
|
|
tlb->d, tlb->wt);
|
2009-03-03 09:12:22 +03:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void tlb_info(Monitor *mon)
|
2009-03-03 09:12:22 +03:00
|
|
|
{
|
2012-03-14 04:38:32 +04:00
|
|
|
CPUArchState *env = mon_get_cpu();
|
2009-03-03 09:12:22 +03:00
|
|
|
int i;
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf (mon, "ITLB:\n");
|
2009-03-03 09:12:22 +03:00
|
|
|
for (i = 0 ; i < ITLB_SIZE ; i++)
|
2009-03-06 02:01:23 +03:00
|
|
|
print_tlb (mon, i, &env->itlb[i]);
|
|
|
|
monitor_printf (mon, "UTLB:\n");
|
2009-03-03 09:12:22 +03:00
|
|
|
for (i = 0 ; i < UTLB_SIZE ; i++)
|
2009-03-06 02:01:23 +03:00
|
|
|
print_tlb (mon, i, &env->utlb[i]);
|
2009-03-03 09:12:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2012-01-07 20:02:40 +04:00
|
|
|
#if defined(TARGET_SPARC) || defined(TARGET_PPC) || defined(TARGET_XTENSA)
|
2010-12-19 16:42:56 +03:00
|
|
|
static void tlb_info(Monitor *mon)
|
|
|
|
{
|
2012-03-14 04:38:32 +04:00
|
|
|
CPUArchState *env1 = mon_get_cpu();
|
2010-12-19 16:42:56 +03:00
|
|
|
|
|
|
|
dump_mmu((FILE*)mon, (fprintf_function)monitor_printf, env1);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-09-12 00:22:05 +04:00
|
|
|
static void do_info_mtree(Monitor *mon)
|
|
|
|
{
|
|
|
|
mtree_info((fprintf_function)monitor_printf, mon);
|
|
|
|
}
|
|
|
|
|
2009-04-22 02:30:47 +04:00
|
|
|
static void do_info_numa(Monitor *mon)
|
|
|
|
{
|
2009-04-23 00:20:29 +04:00
|
|
|
int i;
|
2012-03-14 04:38:32 +04:00
|
|
|
CPUArchState *env;
|
2009-04-22 02:30:47 +04:00
|
|
|
|
|
|
|
monitor_printf(mon, "%d nodes\n", nb_numa_nodes);
|
|
|
|
for (i = 0; i < nb_numa_nodes; i++) {
|
|
|
|
monitor_printf(mon, "node %d cpus:", i);
|
|
|
|
for (env = first_cpu; env != NULL; env = env->next_cpu) {
|
|
|
|
if (env->numa_node == i) {
|
|
|
|
monitor_printf(mon, " %d", env->cpu_index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
monitor_printf(mon, "\n");
|
|
|
|
monitor_printf(mon, "node %d size: %" PRId64 " MB\n", i,
|
|
|
|
node_mem[i] >> 20);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-02-09 01:40:15 +03:00
|
|
|
#ifdef CONFIG_PROFILER
|
|
|
|
|
2009-09-30 16:09:52 +04:00
|
|
|
int64_t qemu_time;
|
|
|
|
int64_t dev_time;
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_info_profile(Monitor *mon)
|
2006-02-09 01:40:15 +03:00
|
|
|
{
|
|
|
|
int64_t total;
|
|
|
|
total = qemu_time;
|
|
|
|
if (total == 0)
|
|
|
|
total = 1;
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "async time %" PRId64 " (%0.3f)\n",
|
2009-09-10 05:04:26 +04:00
|
|
|
dev_time, dev_time / (double)get_ticks_per_sec());
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "qemu time %" PRId64 " (%0.3f)\n",
|
2009-09-10 05:04:26 +04:00
|
|
|
qemu_time, qemu_time / (double)get_ticks_per_sec());
|
2006-02-09 01:40:15 +03:00
|
|
|
qemu_time = 0;
|
|
|
|
dev_time = 0;
|
|
|
|
}
|
|
|
|
#else
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_info_profile(Monitor *mon)
|
2006-02-09 01:40:15 +03:00
|
|
|
{
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "Internal profiler not compiled\n");
|
2006-02-09 01:40:15 +03:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2006-07-16 22:57:03 +04:00
|
|
|
/* Capture support */
|
2009-09-12 11:36:22 +04:00
|
|
|
static QLIST_HEAD (capture_list_head, CaptureState) capture_head;
|
2006-07-16 22:57:03 +04:00
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_info_capture(Monitor *mon)
|
2006-07-16 22:57:03 +04:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
CaptureState *s;
|
|
|
|
|
|
|
|
for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "[%d]: ", i);
|
2006-07-16 22:57:03 +04:00
|
|
|
s->ops.info (s->opaque);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-06 12:22:04 +04:00
|
|
|
#ifdef HAS_AUDIO
|
2009-08-28 22:27:13 +04:00
|
|
|
static void do_stop_capture(Monitor *mon, const QDict *qdict)
|
2006-07-16 22:57:03 +04:00
|
|
|
{
|
|
|
|
int i;
|
2009-08-28 22:27:13 +04:00
|
|
|
int n = qdict_get_int(qdict, "n");
|
2006-07-16 22:57:03 +04:00
|
|
|
CaptureState *s;
|
|
|
|
|
|
|
|
for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) {
|
|
|
|
if (i == n) {
|
|
|
|
s->ops.destroy (s->opaque);
|
2009-09-12 11:36:22 +04:00
|
|
|
QLIST_REMOVE (s, entries);
|
2011-08-21 07:09:37 +04:00
|
|
|
g_free (s);
|
2006-07-16 22:57:03 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-28 22:27:19 +04:00
|
|
|
static void do_wav_capture(Monitor *mon, const QDict *qdict)
|
|
|
|
{
|
|
|
|
const char *path = qdict_get_str(qdict, "path");
|
|
|
|
int has_freq = qdict_haskey(qdict, "freq");
|
|
|
|
int freq = qdict_get_try_int(qdict, "freq", -1);
|
|
|
|
int has_bits = qdict_haskey(qdict, "bits");
|
|
|
|
int bits = qdict_get_try_int(qdict, "bits", -1);
|
|
|
|
int has_channels = qdict_haskey(qdict, "nchannels");
|
|
|
|
int nchannels = qdict_get_try_int(qdict, "nchannels", -1);
|
2006-07-16 22:57:03 +04:00
|
|
|
CaptureState *s;
|
|
|
|
|
2011-08-21 07:09:37 +04:00
|
|
|
s = g_malloc0 (sizeof (*s));
|
2006-07-16 22:57:03 +04:00
|
|
|
|
|
|
|
freq = has_freq ? freq : 44100;
|
|
|
|
bits = has_bits ? bits : 16;
|
|
|
|
nchannels = has_channels ? nchannels : 2;
|
|
|
|
|
|
|
|
if (wav_start_capture (s, path, freq, bits, nchannels)) {
|
2011-01-21 13:53:55 +03:00
|
|
|
monitor_printf(mon, "Failed to add wave capture\n");
|
2011-08-21 07:09:37 +04:00
|
|
|
g_free (s);
|
2011-01-21 13:53:55 +03:00
|
|
|
return;
|
2006-07-16 22:57:03 +04:00
|
|
|
}
|
2009-09-12 11:36:22 +04:00
|
|
|
QLIST_INSERT_HEAD (&capture_head, s, entries);
|
2006-07-16 22:57:03 +04:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-06-25 10:22:08 +04:00
|
|
|
static qemu_acl *find_acl(Monitor *mon, const char *name)
|
Support ACLs for controlling VNC access ("Daniel P. Berrange")
This patch introduces a generic internal API for access control lists
to be used by network servers in QEMU. It adds support for checking
these ACL in the VNC server, in two places. The first ACL is for the
SASL authentication mechanism, checking the SASL username. This ACL
is called 'vnc.username'. The second is for the TLS authentication
mechanism, when x509 client certificates are turned on, checking against
the Distinguished Name of the client. This ACL is called 'vnc.x509dname'
The internal API provides for an ACL with the following characteristics
- A unique name, eg vnc.username, and vnc.x509dname.
- A default policy, allow or deny
- An ordered series of match rules, with allow or deny policy
If none of the match rules apply, then the default policy is
used.
There is a monitor API to manipulate the ACLs, which I'll describe via
examples
(qemu) acl show vnc.username
policy: allow
(qemu) acl policy vnc.username denya
acl: policy set to 'deny'
(qemu) acl allow vnc.username fred
acl: added rule at position 1
(qemu) acl allow vnc.username bob
acl: added rule at position 2
(qemu) acl allow vnc.username joe 1
acl: added rule at position 1
(qemu) acl show vnc.username
policy: deny
0: allow fred
1: allow joe
2: allow bob
(qemu) acl show vnc.x509dname
policy: allow
(qemu) acl policy vnc.x509dname deny
acl: policy set to 'deny'
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=London,CN=*
acl: added rule at position 1
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=Boston,CN=bob
acl: added rule at position 2
(qemu) acl show vnc.x509dname
policy: deny
0: allow C=GB,O=ACME,L=London,CN=*
1: allow C=GB,O=ACME,L=Boston,CN=bob
By default the VNC server will not use any ACLs, allowing access to
the server if the user successfully authenticates. To enable use of
ACLs to restrict user access, the ',acl' flag should be given when
starting QEMU. The initial ACL activated will be a 'deny all' policy
and should be customized using monitor commands.
eg enable SASL auth and ACLs
qemu .... -vnc localhost:1,sasl,acl
The next patch will provide a way to load a pre-defined ACL when
starting up
Makefile | 6 +
b/acl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
b/acl.h | 74 ++++++++++++++++++++++
configure | 18 +++++
monitor.c | 95 ++++++++++++++++++++++++++++
qemu-doc.texi | 49 ++++++++++++++
vnc-auth-sasl.c | 16 +++-
vnc-auth-sasl.h | 7 ++
vnc-tls.c | 19 +++++
vnc-tls.h | 3
vnc.c | 21 ++++++
vnc.h | 3
12 files changed, 491 insertions(+), 5 deletions(-)
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6726 c046a42c-6fe2-441c-8c8c-71466251a162
2009-03-06 23:27:37 +03:00
|
|
|
{
|
2009-06-25 10:22:08 +04:00
|
|
|
qemu_acl *acl = qemu_acl_find(name);
|
Support ACLs for controlling VNC access ("Daniel P. Berrange")
This patch introduces a generic internal API for access control lists
to be used by network servers in QEMU. It adds support for checking
these ACL in the VNC server, in two places. The first ACL is for the
SASL authentication mechanism, checking the SASL username. This ACL
is called 'vnc.username'. The second is for the TLS authentication
mechanism, when x509 client certificates are turned on, checking against
the Distinguished Name of the client. This ACL is called 'vnc.x509dname'
The internal API provides for an ACL with the following characteristics
- A unique name, eg vnc.username, and vnc.x509dname.
- A default policy, allow or deny
- An ordered series of match rules, with allow or deny policy
If none of the match rules apply, then the default policy is
used.
There is a monitor API to manipulate the ACLs, which I'll describe via
examples
(qemu) acl show vnc.username
policy: allow
(qemu) acl policy vnc.username denya
acl: policy set to 'deny'
(qemu) acl allow vnc.username fred
acl: added rule at position 1
(qemu) acl allow vnc.username bob
acl: added rule at position 2
(qemu) acl allow vnc.username joe 1
acl: added rule at position 1
(qemu) acl show vnc.username
policy: deny
0: allow fred
1: allow joe
2: allow bob
(qemu) acl show vnc.x509dname
policy: allow
(qemu) acl policy vnc.x509dname deny
acl: policy set to 'deny'
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=London,CN=*
acl: added rule at position 1
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=Boston,CN=bob
acl: added rule at position 2
(qemu) acl show vnc.x509dname
policy: deny
0: allow C=GB,O=ACME,L=London,CN=*
1: allow C=GB,O=ACME,L=Boston,CN=bob
By default the VNC server will not use any ACLs, allowing access to
the server if the user successfully authenticates. To enable use of
ACLs to restrict user access, the ',acl' flag should be given when
starting QEMU. The initial ACL activated will be a 'deny all' policy
and should be customized using monitor commands.
eg enable SASL auth and ACLs
qemu .... -vnc localhost:1,sasl,acl
The next patch will provide a way to load a pre-defined ACL when
starting up
Makefile | 6 +
b/acl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
b/acl.h | 74 ++++++++++++++++++++++
configure | 18 +++++
monitor.c | 95 ++++++++++++++++++++++++++++
qemu-doc.texi | 49 ++++++++++++++
vnc-auth-sasl.c | 16 +++-
vnc-auth-sasl.h | 7 ++
vnc-tls.c | 19 +++++
vnc-tls.h | 3
vnc.c | 21 ++++++
vnc.h | 3
12 files changed, 491 insertions(+), 5 deletions(-)
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6726 c046a42c-6fe2-441c-8c8c-71466251a162
2009-03-06 23:27:37 +03:00
|
|
|
|
|
|
|
if (!acl) {
|
2009-06-25 10:22:08 +04:00
|
|
|
monitor_printf(mon, "acl: unknown list '%s'\n", name);
|
Support ACLs for controlling VNC access ("Daniel P. Berrange")
This patch introduces a generic internal API for access control lists
to be used by network servers in QEMU. It adds support for checking
these ACL in the VNC server, in two places. The first ACL is for the
SASL authentication mechanism, checking the SASL username. This ACL
is called 'vnc.username'. The second is for the TLS authentication
mechanism, when x509 client certificates are turned on, checking against
the Distinguished Name of the client. This ACL is called 'vnc.x509dname'
The internal API provides for an ACL with the following characteristics
- A unique name, eg vnc.username, and vnc.x509dname.
- A default policy, allow or deny
- An ordered series of match rules, with allow or deny policy
If none of the match rules apply, then the default policy is
used.
There is a monitor API to manipulate the ACLs, which I'll describe via
examples
(qemu) acl show vnc.username
policy: allow
(qemu) acl policy vnc.username denya
acl: policy set to 'deny'
(qemu) acl allow vnc.username fred
acl: added rule at position 1
(qemu) acl allow vnc.username bob
acl: added rule at position 2
(qemu) acl allow vnc.username joe 1
acl: added rule at position 1
(qemu) acl show vnc.username
policy: deny
0: allow fred
1: allow joe
2: allow bob
(qemu) acl show vnc.x509dname
policy: allow
(qemu) acl policy vnc.x509dname deny
acl: policy set to 'deny'
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=London,CN=*
acl: added rule at position 1
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=Boston,CN=bob
acl: added rule at position 2
(qemu) acl show vnc.x509dname
policy: deny
0: allow C=GB,O=ACME,L=London,CN=*
1: allow C=GB,O=ACME,L=Boston,CN=bob
By default the VNC server will not use any ACLs, allowing access to
the server if the user successfully authenticates. To enable use of
ACLs to restrict user access, the ',acl' flag should be given when
starting QEMU. The initial ACL activated will be a 'deny all' policy
and should be customized using monitor commands.
eg enable SASL auth and ACLs
qemu .... -vnc localhost:1,sasl,acl
The next patch will provide a way to load a pre-defined ACL when
starting up
Makefile | 6 +
b/acl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
b/acl.h | 74 ++++++++++++++++++++++
configure | 18 +++++
monitor.c | 95 ++++++++++++++++++++++++++++
qemu-doc.texi | 49 ++++++++++++++
vnc-auth-sasl.c | 16 +++-
vnc-auth-sasl.h | 7 ++
vnc-tls.c | 19 +++++
vnc-tls.h | 3
vnc.c | 21 ++++++
vnc.h | 3
12 files changed, 491 insertions(+), 5 deletions(-)
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6726 c046a42c-6fe2-441c-8c8c-71466251a162
2009-03-06 23:27:37 +03:00
|
|
|
}
|
2009-06-25 10:22:08 +04:00
|
|
|
return acl;
|
|
|
|
}
|
|
|
|
|
2009-08-28 22:27:13 +04:00
|
|
|
static void do_acl_show(Monitor *mon, const QDict *qdict)
|
2009-06-25 10:22:08 +04:00
|
|
|
{
|
2009-08-28 22:27:13 +04:00
|
|
|
const char *aclname = qdict_get_str(qdict, "aclname");
|
2009-06-25 10:22:08 +04:00
|
|
|
qemu_acl *acl = find_acl(mon, aclname);
|
|
|
|
qemu_acl_entry *entry;
|
|
|
|
int i = 0;
|
Support ACLs for controlling VNC access ("Daniel P. Berrange")
This patch introduces a generic internal API for access control lists
to be used by network servers in QEMU. It adds support for checking
these ACL in the VNC server, in two places. The first ACL is for the
SASL authentication mechanism, checking the SASL username. This ACL
is called 'vnc.username'. The second is for the TLS authentication
mechanism, when x509 client certificates are turned on, checking against
the Distinguished Name of the client. This ACL is called 'vnc.x509dname'
The internal API provides for an ACL with the following characteristics
- A unique name, eg vnc.username, and vnc.x509dname.
- A default policy, allow or deny
- An ordered series of match rules, with allow or deny policy
If none of the match rules apply, then the default policy is
used.
There is a monitor API to manipulate the ACLs, which I'll describe via
examples
(qemu) acl show vnc.username
policy: allow
(qemu) acl policy vnc.username denya
acl: policy set to 'deny'
(qemu) acl allow vnc.username fred
acl: added rule at position 1
(qemu) acl allow vnc.username bob
acl: added rule at position 2
(qemu) acl allow vnc.username joe 1
acl: added rule at position 1
(qemu) acl show vnc.username
policy: deny
0: allow fred
1: allow joe
2: allow bob
(qemu) acl show vnc.x509dname
policy: allow
(qemu) acl policy vnc.x509dname deny
acl: policy set to 'deny'
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=London,CN=*
acl: added rule at position 1
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=Boston,CN=bob
acl: added rule at position 2
(qemu) acl show vnc.x509dname
policy: deny
0: allow C=GB,O=ACME,L=London,CN=*
1: allow C=GB,O=ACME,L=Boston,CN=bob
By default the VNC server will not use any ACLs, allowing access to
the server if the user successfully authenticates. To enable use of
ACLs to restrict user access, the ',acl' flag should be given when
starting QEMU. The initial ACL activated will be a 'deny all' policy
and should be customized using monitor commands.
eg enable SASL auth and ACLs
qemu .... -vnc localhost:1,sasl,acl
The next patch will provide a way to load a pre-defined ACL when
starting up
Makefile | 6 +
b/acl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
b/acl.h | 74 ++++++++++++++++++++++
configure | 18 +++++
monitor.c | 95 ++++++++++++++++++++++++++++
qemu-doc.texi | 49 ++++++++++++++
vnc-auth-sasl.c | 16 +++-
vnc-auth-sasl.h | 7 ++
vnc-tls.c | 19 +++++
vnc-tls.h | 3
vnc.c | 21 ++++++
vnc.h | 3
12 files changed, 491 insertions(+), 5 deletions(-)
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6726 c046a42c-6fe2-441c-8c8c-71466251a162
2009-03-06 23:27:37 +03:00
|
|
|
|
2009-06-25 10:22:08 +04:00
|
|
|
if (acl) {
|
2009-03-06 23:27:40 +03:00
|
|
|
monitor_printf(mon, "policy: %s\n",
|
Support ACLs for controlling VNC access ("Daniel P. Berrange")
This patch introduces a generic internal API for access control lists
to be used by network servers in QEMU. It adds support for checking
these ACL in the VNC server, in two places. The first ACL is for the
SASL authentication mechanism, checking the SASL username. This ACL
is called 'vnc.username'. The second is for the TLS authentication
mechanism, when x509 client certificates are turned on, checking against
the Distinguished Name of the client. This ACL is called 'vnc.x509dname'
The internal API provides for an ACL with the following characteristics
- A unique name, eg vnc.username, and vnc.x509dname.
- A default policy, allow or deny
- An ordered series of match rules, with allow or deny policy
If none of the match rules apply, then the default policy is
used.
There is a monitor API to manipulate the ACLs, which I'll describe via
examples
(qemu) acl show vnc.username
policy: allow
(qemu) acl policy vnc.username denya
acl: policy set to 'deny'
(qemu) acl allow vnc.username fred
acl: added rule at position 1
(qemu) acl allow vnc.username bob
acl: added rule at position 2
(qemu) acl allow vnc.username joe 1
acl: added rule at position 1
(qemu) acl show vnc.username
policy: deny
0: allow fred
1: allow joe
2: allow bob
(qemu) acl show vnc.x509dname
policy: allow
(qemu) acl policy vnc.x509dname deny
acl: policy set to 'deny'
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=London,CN=*
acl: added rule at position 1
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=Boston,CN=bob
acl: added rule at position 2
(qemu) acl show vnc.x509dname
policy: deny
0: allow C=GB,O=ACME,L=London,CN=*
1: allow C=GB,O=ACME,L=Boston,CN=bob
By default the VNC server will not use any ACLs, allowing access to
the server if the user successfully authenticates. To enable use of
ACLs to restrict user access, the ',acl' flag should be given when
starting QEMU. The initial ACL activated will be a 'deny all' policy
and should be customized using monitor commands.
eg enable SASL auth and ACLs
qemu .... -vnc localhost:1,sasl,acl
The next patch will provide a way to load a pre-defined ACL when
starting up
Makefile | 6 +
b/acl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
b/acl.h | 74 ++++++++++++++++++++++
configure | 18 +++++
monitor.c | 95 ++++++++++++++++++++++++++++
qemu-doc.texi | 49 ++++++++++++++
vnc-auth-sasl.c | 16 +++-
vnc-auth-sasl.h | 7 ++
vnc-tls.c | 19 +++++
vnc-tls.h | 3
vnc.c | 21 ++++++
vnc.h | 3
12 files changed, 491 insertions(+), 5 deletions(-)
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6726 c046a42c-6fe2-441c-8c8c-71466251a162
2009-03-06 23:27:37 +03:00
|
|
|
acl->defaultDeny ? "deny" : "allow");
|
2009-09-12 11:36:22 +04:00
|
|
|
QTAILQ_FOREACH(entry, &acl->entries, next) {
|
2009-03-06 23:27:40 +03:00
|
|
|
i++;
|
|
|
|
monitor_printf(mon, "%d: %s %s\n", i,
|
2009-06-25 10:22:08 +04:00
|
|
|
entry->deny ? "deny" : "allow", entry->match);
|
2009-03-06 23:27:40 +03:00
|
|
|
}
|
2009-06-25 10:22:08 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-28 22:27:13 +04:00
|
|
|
static void do_acl_reset(Monitor *mon, const QDict *qdict)
|
2009-06-25 10:22:08 +04:00
|
|
|
{
|
2009-08-28 22:27:13 +04:00
|
|
|
const char *aclname = qdict_get_str(qdict, "aclname");
|
2009-06-25 10:22:08 +04:00
|
|
|
qemu_acl *acl = find_acl(mon, aclname);
|
|
|
|
|
|
|
|
if (acl) {
|
2009-03-06 23:27:40 +03:00
|
|
|
qemu_acl_reset(acl);
|
|
|
|
monitor_printf(mon, "acl: removed all rules\n");
|
2009-06-25 10:22:08 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-28 22:27:14 +04:00
|
|
|
static void do_acl_policy(Monitor *mon, const QDict *qdict)
|
2009-06-25 10:22:08 +04:00
|
|
|
{
|
2009-08-28 22:27:14 +04:00
|
|
|
const char *aclname = qdict_get_str(qdict, "aclname");
|
|
|
|
const char *policy = qdict_get_str(qdict, "policy");
|
2009-06-25 10:22:08 +04:00
|
|
|
qemu_acl *acl = find_acl(mon, aclname);
|
2009-03-06 23:27:40 +03:00
|
|
|
|
2009-06-25 10:22:08 +04:00
|
|
|
if (acl) {
|
|
|
|
if (strcmp(policy, "allow") == 0) {
|
2009-03-06 23:27:40 +03:00
|
|
|
acl->defaultDeny = 0;
|
|
|
|
monitor_printf(mon, "acl: policy set to 'allow'\n");
|
2009-06-25 10:22:08 +04:00
|
|
|
} else if (strcmp(policy, "deny") == 0) {
|
2009-03-06 23:27:40 +03:00
|
|
|
acl->defaultDeny = 1;
|
|
|
|
monitor_printf(mon, "acl: policy set to 'deny'\n");
|
|
|
|
} else {
|
2009-06-25 10:22:08 +04:00
|
|
|
monitor_printf(mon, "acl: unknown policy '%s', "
|
|
|
|
"expected 'deny' or 'allow'\n", policy);
|
2009-03-06 23:27:40 +03:00
|
|
|
}
|
2009-06-25 10:22:08 +04:00
|
|
|
}
|
|
|
|
}
|
2009-03-06 23:27:40 +03:00
|
|
|
|
2009-08-28 22:27:17 +04:00
|
|
|
static void do_acl_add(Monitor *mon, const QDict *qdict)
|
2009-06-25 10:22:08 +04:00
|
|
|
{
|
2009-08-28 22:27:17 +04:00
|
|
|
const char *aclname = qdict_get_str(qdict, "aclname");
|
|
|
|
const char *match = qdict_get_str(qdict, "match");
|
|
|
|
const char *policy = qdict_get_str(qdict, "policy");
|
|
|
|
int has_index = qdict_haskey(qdict, "index");
|
|
|
|
int index = qdict_get_try_int(qdict, "index", -1);
|
2009-06-25 10:22:08 +04:00
|
|
|
qemu_acl *acl = find_acl(mon, aclname);
|
|
|
|
int deny, ret;
|
|
|
|
|
|
|
|
if (acl) {
|
|
|
|
if (strcmp(policy, "allow") == 0) {
|
|
|
|
deny = 0;
|
|
|
|
} else if (strcmp(policy, "deny") == 0) {
|
|
|
|
deny = 1;
|
|
|
|
} else {
|
|
|
|
monitor_printf(mon, "acl: unknown policy '%s', "
|
|
|
|
"expected 'deny' or 'allow'\n", policy);
|
2009-03-06 23:27:40 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (has_index)
|
|
|
|
ret = qemu_acl_insert(acl, deny, match, index);
|
|
|
|
else
|
|
|
|
ret = qemu_acl_append(acl, deny, match);
|
|
|
|
if (ret < 0)
|
|
|
|
monitor_printf(mon, "acl: unable to add acl entry\n");
|
|
|
|
else
|
|
|
|
monitor_printf(mon, "acl: added rule at position %d\n", ret);
|
2009-06-25 10:22:08 +04:00
|
|
|
}
|
|
|
|
}
|
2009-03-06 23:27:40 +03:00
|
|
|
|
2009-08-28 22:27:14 +04:00
|
|
|
static void do_acl_remove(Monitor *mon, const QDict *qdict)
|
2009-06-25 10:22:08 +04:00
|
|
|
{
|
2009-08-28 22:27:14 +04:00
|
|
|
const char *aclname = qdict_get_str(qdict, "aclname");
|
|
|
|
const char *match = qdict_get_str(qdict, "match");
|
2009-06-25 10:22:08 +04:00
|
|
|
qemu_acl *acl = find_acl(mon, aclname);
|
|
|
|
int ret;
|
2009-03-06 23:27:40 +03:00
|
|
|
|
2009-06-25 10:22:08 +04:00
|
|
|
if (acl) {
|
2009-03-06 23:27:40 +03:00
|
|
|
ret = qemu_acl_remove(acl, match);
|
|
|
|
if (ret < 0)
|
|
|
|
monitor_printf(mon, "acl: no matching acl entry\n");
|
|
|
|
else
|
|
|
|
monitor_printf(mon, "acl: removed rule at position %d\n", ret);
|
Support ACLs for controlling VNC access ("Daniel P. Berrange")
This patch introduces a generic internal API for access control lists
to be used by network servers in QEMU. It adds support for checking
these ACL in the VNC server, in two places. The first ACL is for the
SASL authentication mechanism, checking the SASL username. This ACL
is called 'vnc.username'. The second is for the TLS authentication
mechanism, when x509 client certificates are turned on, checking against
the Distinguished Name of the client. This ACL is called 'vnc.x509dname'
The internal API provides for an ACL with the following characteristics
- A unique name, eg vnc.username, and vnc.x509dname.
- A default policy, allow or deny
- An ordered series of match rules, with allow or deny policy
If none of the match rules apply, then the default policy is
used.
There is a monitor API to manipulate the ACLs, which I'll describe via
examples
(qemu) acl show vnc.username
policy: allow
(qemu) acl policy vnc.username denya
acl: policy set to 'deny'
(qemu) acl allow vnc.username fred
acl: added rule at position 1
(qemu) acl allow vnc.username bob
acl: added rule at position 2
(qemu) acl allow vnc.username joe 1
acl: added rule at position 1
(qemu) acl show vnc.username
policy: deny
0: allow fred
1: allow joe
2: allow bob
(qemu) acl show vnc.x509dname
policy: allow
(qemu) acl policy vnc.x509dname deny
acl: policy set to 'deny'
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=London,CN=*
acl: added rule at position 1
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=Boston,CN=bob
acl: added rule at position 2
(qemu) acl show vnc.x509dname
policy: deny
0: allow C=GB,O=ACME,L=London,CN=*
1: allow C=GB,O=ACME,L=Boston,CN=bob
By default the VNC server will not use any ACLs, allowing access to
the server if the user successfully authenticates. To enable use of
ACLs to restrict user access, the ',acl' flag should be given when
starting QEMU. The initial ACL activated will be a 'deny all' policy
and should be customized using monitor commands.
eg enable SASL auth and ACLs
qemu .... -vnc localhost:1,sasl,acl
The next patch will provide a way to load a pre-defined ACL when
starting up
Makefile | 6 +
b/acl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
b/acl.h | 74 ++++++++++++++++++++++
configure | 18 +++++
monitor.c | 95 ++++++++++++++++++++++++++++
qemu-doc.texi | 49 ++++++++++++++
vnc-auth-sasl.c | 16 +++-
vnc-auth-sasl.h | 7 ++
vnc-tls.c | 19 +++++
vnc-tls.h | 3
vnc.c | 21 ++++++
vnc.h | 3
12 files changed, 491 insertions(+), 5 deletions(-)
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6726 c046a42c-6fe2-441c-8c8c-71466251a162
2009-03-06 23:27:37 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-23 06:05:14 +04:00
|
|
|
#if defined(TARGET_I386)
|
2009-08-28 22:27:21 +04:00
|
|
|
static void do_inject_mce(Monitor *mon, const QDict *qdict)
|
2009-06-23 06:05:14 +04:00
|
|
|
{
|
2012-05-03 17:22:54 +04:00
|
|
|
X86CPU *cpu;
|
|
|
|
CPUX86State *cenv;
|
2009-08-28 22:27:21 +04:00
|
|
|
int cpu_index = qdict_get_int(qdict, "cpu_index");
|
|
|
|
int bank = qdict_get_int(qdict, "bank");
|
|
|
|
uint64_t status = qdict_get_int(qdict, "status");
|
|
|
|
uint64_t mcg_status = qdict_get_int(qdict, "mcg_status");
|
|
|
|
uint64_t addr = qdict_get_int(qdict, "addr");
|
|
|
|
uint64_t misc = qdict_get_int(qdict, "misc");
|
2011-03-02 10:56:10 +03:00
|
|
|
int flags = MCE_INJECT_UNCOND_AO;
|
2009-06-23 06:05:14 +04:00
|
|
|
|
2011-03-02 10:56:10 +03:00
|
|
|
if (qdict_get_try_bool(qdict, "broadcast", 0)) {
|
|
|
|
flags |= MCE_INJECT_BROADCAST;
|
|
|
|
}
|
2010-12-10 11:21:02 +03:00
|
|
|
for (cenv = first_cpu; cenv != NULL; cenv = cenv->next_cpu) {
|
2012-05-03 17:22:54 +04:00
|
|
|
cpu = x86_env_get_cpu(cenv);
|
2011-03-02 10:56:09 +03:00
|
|
|
if (cenv->cpu_index == cpu_index) {
|
2012-05-03 17:22:54 +04:00
|
|
|
cpu_x86_inject_mce(mon, cpu, bank, status, mcg_status, addr, misc,
|
2011-03-02 10:56:10 +03:00
|
|
|
flags);
|
2009-06-23 06:05:14 +04:00
|
|
|
break;
|
|
|
|
}
|
2010-12-10 11:21:02 +03:00
|
|
|
}
|
2009-06-23 06:05:14 +04:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-06-22 22:36:09 +04:00
|
|
|
void qmp_getfd(const char *fdname, Error **errp)
|
2009-07-22 12:11:40 +04:00
|
|
|
{
|
2009-10-02 01:12:16 +04:00
|
|
|
mon_fd_t *monfd;
|
2009-07-22 12:11:40 +04:00
|
|
|
int fd;
|
|
|
|
|
2012-06-22 22:36:09 +04:00
|
|
|
fd = qemu_chr_fe_get_msgfd(cur_mon->chr);
|
2009-07-22 12:11:40 +04:00
|
|
|
if (fd == -1) {
|
2012-06-22 22:36:09 +04:00
|
|
|
error_set(errp, QERR_FD_NOT_SUPPLIED);
|
|
|
|
return;
|
2009-07-22 12:11:40 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (qemu_isdigit(fdname[0])) {
|
2012-06-22 22:36:09 +04:00
|
|
|
error_set(errp, QERR_INVALID_PARAMETER_VALUE, "fdname",
|
|
|
|
"a name not starting with a digit");
|
|
|
|
return;
|
2009-07-22 12:11:40 +04:00
|
|
|
}
|
|
|
|
|
2012-06-22 22:36:09 +04:00
|
|
|
QLIST_FOREACH(monfd, &cur_mon->fds, next) {
|
2009-07-22 12:11:40 +04:00
|
|
|
if (strcmp(monfd->name, fdname) != 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
close(monfd->fd);
|
|
|
|
monfd->fd = fd;
|
2012-06-22 22:36:09 +04:00
|
|
|
return;
|
2009-07-22 12:11:40 +04:00
|
|
|
}
|
|
|
|
|
2011-08-21 07:09:37 +04:00
|
|
|
monfd = g_malloc0(sizeof(mon_fd_t));
|
|
|
|
monfd->name = g_strdup(fdname);
|
2009-07-22 12:11:40 +04:00
|
|
|
monfd->fd = fd;
|
|
|
|
|
2012-06-22 22:36:09 +04:00
|
|
|
QLIST_INSERT_HEAD(&cur_mon->fds, monfd, next);
|
2009-07-22 12:11:40 +04:00
|
|
|
}
|
|
|
|
|
2012-06-22 22:36:09 +04:00
|
|
|
void qmp_closefd(const char *fdname, Error **errp)
|
2009-07-22 12:11:40 +04:00
|
|
|
{
|
2009-10-02 01:12:16 +04:00
|
|
|
mon_fd_t *monfd;
|
2009-07-22 12:11:40 +04:00
|
|
|
|
2012-06-22 22:36:09 +04:00
|
|
|
QLIST_FOREACH(monfd, &cur_mon->fds, next) {
|
2009-07-22 12:11:40 +04:00
|
|
|
if (strcmp(monfd->name, fdname) != 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-09-12 11:36:22 +04:00
|
|
|
QLIST_REMOVE(monfd, next);
|
2009-07-22 12:11:40 +04:00
|
|
|
close(monfd->fd);
|
2011-08-21 07:09:37 +04:00
|
|
|
g_free(monfd->name);
|
|
|
|
g_free(monfd);
|
2012-06-22 22:36:09 +04:00
|
|
|
return;
|
2009-07-22 12:11:40 +04:00
|
|
|
}
|
|
|
|
|
2012-06-22 22:36:09 +04:00
|
|
|
error_set(errp, QERR_FD_NOT_FOUND, fdname);
|
2009-07-22 12:11:40 +04:00
|
|
|
}
|
|
|
|
|
2009-08-28 22:27:13 +04:00
|
|
|
static void do_loadvm(Monitor *mon, const QDict *qdict)
|
2009-08-20 21:42:21 +04:00
|
|
|
{
|
2011-07-29 22:36:43 +04:00
|
|
|
int saved_vm_running = runstate_is_running();
|
2009-08-28 22:27:13 +04:00
|
|
|
const char *name = qdict_get_str(qdict, "name");
|
2009-08-20 21:42:21 +04:00
|
|
|
|
2011-09-30 21:45:27 +04:00
|
|
|
vm_stop(RUN_STATE_RESTORE_VM);
|
2009-08-20 21:42:21 +04:00
|
|
|
|
2010-07-19 22:25:01 +04:00
|
|
|
if (load_vmstate(name) == 0 && saved_vm_running) {
|
2009-08-20 21:42:21 +04:00
|
|
|
vm_start();
|
2010-07-19 22:25:01 +04:00
|
|
|
}
|
2009-08-20 21:42:21 +04:00
|
|
|
}
|
|
|
|
|
2012-09-20 18:50:32 +04:00
|
|
|
int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp)
|
2009-07-22 12:11:41 +04:00
|
|
|
{
|
2009-10-02 01:12:16 +04:00
|
|
|
mon_fd_t *monfd;
|
2009-07-22 12:11:41 +04:00
|
|
|
|
2009-09-12 11:36:22 +04:00
|
|
|
QLIST_FOREACH(monfd, &mon->fds, next) {
|
2009-07-22 12:11:41 +04:00
|
|
|
int fd;
|
|
|
|
|
|
|
|
if (strcmp(monfd->name, fdname) != 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
fd = monfd->fd;
|
|
|
|
|
|
|
|
/* caller takes ownership of fd */
|
2009-09-12 11:36:22 +04:00
|
|
|
QLIST_REMOVE(monfd, next);
|
2011-08-21 07:09:37 +04:00
|
|
|
g_free(monfd->name);
|
|
|
|
g_free(monfd);
|
2009-07-22 12:11:41 +04:00
|
|
|
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2012-09-20 18:50:32 +04:00
|
|
|
error_setg(errp, "File descriptor named '%s' has not been found", fdname);
|
2009-07-22 12:11:41 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-08-15 00:43:43 +04:00
|
|
|
static void monitor_fdset_cleanup(MonFdset *mon_fdset)
|
|
|
|
{
|
|
|
|
MonFdsetFd *mon_fdset_fd;
|
|
|
|
MonFdsetFd *mon_fdset_fd_next;
|
|
|
|
|
|
|
|
QLIST_FOREACH_SAFE(mon_fdset_fd, &mon_fdset->fds, next, mon_fdset_fd_next) {
|
2012-10-18 23:19:33 +04:00
|
|
|
if ((mon_fdset_fd->removed ||
|
|
|
|
(QLIST_EMPTY(&mon_fdset->dup_fds) && mon_refcount == 0)) &&
|
|
|
|
runstate_is_running()) {
|
2012-08-15 00:43:43 +04:00
|
|
|
close(mon_fdset_fd->fd);
|
|
|
|
g_free(mon_fdset_fd->opaque);
|
|
|
|
QLIST_REMOVE(mon_fdset_fd, next);
|
|
|
|
g_free(mon_fdset_fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-15 00:43:47 +04:00
|
|
|
if (QLIST_EMPTY(&mon_fdset->fds) && QLIST_EMPTY(&mon_fdset->dup_fds)) {
|
2012-08-15 00:43:43 +04:00
|
|
|
QLIST_REMOVE(mon_fdset, next);
|
|
|
|
g_free(mon_fdset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-15 00:43:48 +04:00
|
|
|
static void monitor_fdsets_cleanup(void)
|
|
|
|
{
|
|
|
|
MonFdset *mon_fdset;
|
|
|
|
MonFdset *mon_fdset_next;
|
|
|
|
|
|
|
|
QLIST_FOREACH_SAFE(mon_fdset, &mon_fdsets, next, mon_fdset_next) {
|
|
|
|
monitor_fdset_cleanup(mon_fdset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-15 00:43:43 +04:00
|
|
|
AddfdInfo *qmp_add_fd(bool has_fdset_id, int64_t fdset_id, bool has_opaque,
|
|
|
|
const char *opaque, Error **errp)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
Monitor *mon = cur_mon;
|
|
|
|
AddfdInfo *fdinfo;
|
|
|
|
|
|
|
|
fd = qemu_chr_fe_get_msgfd(mon->chr);
|
|
|
|
if (fd == -1) {
|
|
|
|
error_set(errp, QERR_FD_NOT_SUPPLIED);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2012-10-18 23:19:32 +04:00
|
|
|
fdinfo = monitor_fdset_add_fd(fd, has_fdset_id, fdset_id,
|
|
|
|
has_opaque, opaque, errp);
|
|
|
|
if (fdinfo) {
|
|
|
|
return fdinfo;
|
2012-08-15 00:43:43 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
error:
|
|
|
|
if (fd != -1) {
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void qmp_remove_fd(int64_t fdset_id, bool has_fd, int64_t fd, Error **errp)
|
|
|
|
{
|
|
|
|
MonFdset *mon_fdset;
|
|
|
|
MonFdsetFd *mon_fdset_fd;
|
|
|
|
char fd_str[60];
|
|
|
|
|
|
|
|
QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
|
|
|
|
if (mon_fdset->id != fdset_id) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) {
|
|
|
|
if (has_fd) {
|
|
|
|
if (mon_fdset_fd->fd != fd) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
mon_fdset_fd->removed = true;
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
mon_fdset_fd->removed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (has_fd && !mon_fdset_fd) {
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
monitor_fdset_cleanup(mon_fdset);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
error:
|
|
|
|
if (has_fd) {
|
|
|
|
snprintf(fd_str, sizeof(fd_str), "fdset-id:%" PRId64 ", fd:%" PRId64,
|
|
|
|
fdset_id, fd);
|
|
|
|
} else {
|
|
|
|
snprintf(fd_str, sizeof(fd_str), "fdset-id:%" PRId64, fdset_id);
|
|
|
|
}
|
|
|
|
error_set(errp, QERR_FD_NOT_FOUND, fd_str);
|
|
|
|
}
|
|
|
|
|
|
|
|
FdsetInfoList *qmp_query_fdsets(Error **errp)
|
|
|
|
{
|
|
|
|
MonFdset *mon_fdset;
|
|
|
|
MonFdsetFd *mon_fdset_fd;
|
|
|
|
FdsetInfoList *fdset_list = NULL;
|
|
|
|
|
|
|
|
QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
|
|
|
|
FdsetInfoList *fdset_info = g_malloc0(sizeof(*fdset_info));
|
|
|
|
FdsetFdInfoList *fdsetfd_list = NULL;
|
|
|
|
|
|
|
|
fdset_info->value = g_malloc0(sizeof(*fdset_info->value));
|
|
|
|
fdset_info->value->fdset_id = mon_fdset->id;
|
|
|
|
|
|
|
|
QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) {
|
|
|
|
FdsetFdInfoList *fdsetfd_info;
|
|
|
|
|
|
|
|
fdsetfd_info = g_malloc0(sizeof(*fdsetfd_info));
|
|
|
|
fdsetfd_info->value = g_malloc0(sizeof(*fdsetfd_info->value));
|
|
|
|
fdsetfd_info->value->fd = mon_fdset_fd->fd;
|
|
|
|
if (mon_fdset_fd->opaque) {
|
|
|
|
fdsetfd_info->value->has_opaque = true;
|
|
|
|
fdsetfd_info->value->opaque = g_strdup(mon_fdset_fd->opaque);
|
|
|
|
} else {
|
|
|
|
fdsetfd_info->value->has_opaque = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
fdsetfd_info->next = fdsetfd_list;
|
|
|
|
fdsetfd_list = fdsetfd_info;
|
|
|
|
}
|
|
|
|
|
|
|
|
fdset_info->value->fds = fdsetfd_list;
|
|
|
|
|
|
|
|
fdset_info->next = fdset_list;
|
|
|
|
fdset_list = fdset_info;
|
|
|
|
}
|
|
|
|
|
|
|
|
return fdset_list;
|
|
|
|
}
|
|
|
|
|
2012-10-18 23:19:32 +04:00
|
|
|
AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id,
|
|
|
|
bool has_opaque, const char *opaque,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
MonFdset *mon_fdset = NULL;
|
|
|
|
MonFdsetFd *mon_fdset_fd;
|
|
|
|
AddfdInfo *fdinfo;
|
|
|
|
|
|
|
|
if (has_fdset_id) {
|
|
|
|
QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
|
|
|
|
/* Break if match found or match impossible due to ordering by ID */
|
|
|
|
if (fdset_id <= mon_fdset->id) {
|
|
|
|
if (fdset_id < mon_fdset->id) {
|
|
|
|
mon_fdset = NULL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mon_fdset == NULL) {
|
|
|
|
int64_t fdset_id_prev = -1;
|
|
|
|
MonFdset *mon_fdset_cur = QLIST_FIRST(&mon_fdsets);
|
|
|
|
|
|
|
|
if (has_fdset_id) {
|
|
|
|
if (fdset_id < 0) {
|
|
|
|
error_set(errp, QERR_INVALID_PARAMETER_VALUE, "fdset-id",
|
|
|
|
"a non-negative value");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* Use specified fdset ID */
|
|
|
|
QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
|
|
|
|
mon_fdset_cur = mon_fdset;
|
|
|
|
if (fdset_id < mon_fdset_cur->id) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Use first available fdset ID */
|
|
|
|
QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
|
|
|
|
mon_fdset_cur = mon_fdset;
|
|
|
|
if (fdset_id_prev == mon_fdset_cur->id - 1) {
|
|
|
|
fdset_id_prev = mon_fdset_cur->id;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mon_fdset = g_malloc0(sizeof(*mon_fdset));
|
|
|
|
if (has_fdset_id) {
|
|
|
|
mon_fdset->id = fdset_id;
|
|
|
|
} else {
|
|
|
|
mon_fdset->id = fdset_id_prev + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The fdset list is ordered by fdset ID */
|
|
|
|
if (!mon_fdset_cur) {
|
|
|
|
QLIST_INSERT_HEAD(&mon_fdsets, mon_fdset, next);
|
|
|
|
} else if (mon_fdset->id < mon_fdset_cur->id) {
|
|
|
|
QLIST_INSERT_BEFORE(mon_fdset_cur, mon_fdset, next);
|
|
|
|
} else {
|
|
|
|
QLIST_INSERT_AFTER(mon_fdset_cur, mon_fdset, next);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mon_fdset_fd = g_malloc0(sizeof(*mon_fdset_fd));
|
|
|
|
mon_fdset_fd->fd = fd;
|
|
|
|
mon_fdset_fd->removed = false;
|
|
|
|
if (has_opaque) {
|
|
|
|
mon_fdset_fd->opaque = g_strdup(opaque);
|
|
|
|
}
|
|
|
|
QLIST_INSERT_HEAD(&mon_fdset->fds, mon_fdset_fd, next);
|
|
|
|
|
|
|
|
fdinfo = g_malloc0(sizeof(*fdinfo));
|
|
|
|
fdinfo->fdset_id = mon_fdset->id;
|
|
|
|
fdinfo->fd = mon_fdset_fd->fd;
|
|
|
|
|
|
|
|
return fdinfo;
|
|
|
|
}
|
|
|
|
|
2012-08-15 00:43:47 +04:00
|
|
|
int monitor_fdset_get_fd(int64_t fdset_id, int flags)
|
|
|
|
{
|
2012-08-19 00:14:54 +04:00
|
|
|
#ifndef _WIN32
|
2012-08-15 00:43:47 +04:00
|
|
|
MonFdset *mon_fdset;
|
|
|
|
MonFdsetFd *mon_fdset_fd;
|
|
|
|
int mon_fd_flags;
|
|
|
|
|
|
|
|
QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
|
|
|
|
if (mon_fdset->id != fdset_id) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) {
|
|
|
|
mon_fd_flags = fcntl(mon_fdset_fd->fd, F_GETFL);
|
|
|
|
if (mon_fd_flags == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((flags & O_ACCMODE) == (mon_fd_flags & O_ACCMODE)) {
|
|
|
|
return mon_fdset_fd->fd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
errno = EACCES;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
errno = ENOENT;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int monitor_fdset_dup_fd_add(int64_t fdset_id, int dup_fd)
|
|
|
|
{
|
|
|
|
MonFdset *mon_fdset;
|
|
|
|
MonFdsetFd *mon_fdset_fd_dup;
|
|
|
|
|
|
|
|
QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
|
|
|
|
if (mon_fdset->id != fdset_id) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
QLIST_FOREACH(mon_fdset_fd_dup, &mon_fdset->dup_fds, next) {
|
|
|
|
if (mon_fdset_fd_dup->fd == dup_fd) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mon_fdset_fd_dup = g_malloc0(sizeof(*mon_fdset_fd_dup));
|
|
|
|
mon_fdset_fd_dup->fd = dup_fd;
|
|
|
|
QLIST_INSERT_HEAD(&mon_fdset->dup_fds, mon_fdset_fd_dup, next);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int monitor_fdset_dup_fd_find_remove(int dup_fd, bool remove)
|
|
|
|
{
|
|
|
|
MonFdset *mon_fdset;
|
|
|
|
MonFdsetFd *mon_fdset_fd_dup;
|
|
|
|
|
|
|
|
QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
|
|
|
|
QLIST_FOREACH(mon_fdset_fd_dup, &mon_fdset->dup_fds, next) {
|
|
|
|
if (mon_fdset_fd_dup->fd == dup_fd) {
|
|
|
|
if (remove) {
|
|
|
|
QLIST_REMOVE(mon_fdset_fd_dup, next);
|
|
|
|
if (QLIST_EMPTY(&mon_fdset->dup_fds)) {
|
|
|
|
monitor_fdset_cleanup(mon_fdset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return mon_fdset->id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int monitor_fdset_dup_fd_find(int dup_fd)
|
|
|
|
{
|
|
|
|
return monitor_fdset_dup_fd_find_remove(dup_fd, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
int monitor_fdset_dup_fd_remove(int dup_fd)
|
|
|
|
{
|
|
|
|
return monitor_fdset_dup_fd_find_remove(dup_fd, true);
|
|
|
|
}
|
|
|
|
|
2012-08-22 00:52:07 +04:00
|
|
|
int monitor_handle_fd_param(Monitor *mon, const char *fdname)
|
|
|
|
{
|
|
|
|
int fd;
|
2012-09-20 18:50:32 +04:00
|
|
|
Error *local_err = NULL;
|
2012-08-22 00:52:07 +04:00
|
|
|
|
|
|
|
if (!qemu_isdigit(fdname[0]) && mon) {
|
|
|
|
|
2012-09-20 18:50:32 +04:00
|
|
|
fd = monitor_get_fd(mon, fdname, &local_err);
|
2012-08-22 00:52:07 +04:00
|
|
|
if (fd == -1) {
|
2012-09-20 18:50:32 +04:00
|
|
|
qerror_report_err(local_err);
|
|
|
|
error_free(local_err);
|
2012-08-22 00:52:07 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fd = qemu_parse_fd(fdname);
|
|
|
|
}
|
|
|
|
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2011-10-12 07:32:41 +04:00
|
|
|
/* mon_cmds and info_cmds would be sorted at runtime */
|
|
|
|
static mon_cmd_t mon_cmds[] = {
|
2010-09-30 23:00:22 +04:00
|
|
|
#include "hmp-commands.h"
|
2007-09-17 01:08:06 +04:00
|
|
|
{ NULL, NULL, },
|
2004-03-15 00:38:27 +03:00
|
|
|
};
|
|
|
|
|
2010-09-30 23:00:22 +04:00
|
|
|
/* Please update hmp-commands.hx when adding or changing commands */
|
2011-10-12 07:32:41 +04:00
|
|
|
static mon_cmd_t info_cmds[] = {
|
2009-10-07 20:41:50 +04:00
|
|
|
{
|
|
|
|
.name = "version",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the version of QEMU",
|
2011-08-27 00:38:13 +04:00
|
|
|
.mhandler.info = hmp_info_version,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "network",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the network state",
|
2009-10-07 20:41:51 +04:00
|
|
|
.mhandler.info = do_info_network,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "chardev",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the character devices",
|
2011-09-14 23:05:49 +04:00
|
|
|
.mhandler.info = hmp_info_chardev,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "block",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the block devices",
|
2011-09-22 00:16:47 +04:00
|
|
|
.mhandler.info = hmp_info_block,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "blockstats",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show block device statistics",
|
2011-09-22 22:56:36 +04:00
|
|
|
.mhandler.info = hmp_info_blockstats,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
2012-01-18 18:40:49 +04:00
|
|
|
{
|
|
|
|
.name = "block-jobs",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show progress of ongoing block device operations",
|
|
|
|
.mhandler.info = hmp_info_block_jobs,
|
|
|
|
},
|
2009-10-07 20:41:50 +04:00
|
|
|
{
|
|
|
|
.name = "registers",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the cpu registers",
|
2009-10-07 20:41:51 +04:00
|
|
|
.mhandler.info = do_info_registers,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "cpus",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show infos for each CPU",
|
2011-09-21 23:38:35 +04:00
|
|
|
.mhandler.info = hmp_info_cpus,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "history",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the command line history",
|
2009-10-07 20:41:51 +04:00
|
|
|
.mhandler.info = do_info_history,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
2011-10-16 13:53:13 +04:00
|
|
|
#if defined(TARGET_I386) || defined(TARGET_PPC) || defined(TARGET_MIPS) || \
|
|
|
|
defined(TARGET_LM32) || (defined(TARGET_SPARC) && !defined(TARGET_SPARC64))
|
2009-10-07 20:41:50 +04:00
|
|
|
{
|
|
|
|
.name = "irq",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the interrupts statistics (if available)",
|
2011-10-16 13:53:13 +04:00
|
|
|
#ifdef TARGET_SPARC
|
|
|
|
.mhandler.info = sun4m_irq_info,
|
|
|
|
#elif defined(TARGET_LM32)
|
|
|
|
.mhandler.info = lm32_irq_info,
|
|
|
|
#else
|
2009-10-07 20:41:51 +04:00
|
|
|
.mhandler.info = irq_info,
|
2011-10-16 13:53:13 +04:00
|
|
|
#endif
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "pic",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show i8259 (PIC) state",
|
2011-10-16 13:53:13 +04:00
|
|
|
#ifdef TARGET_SPARC
|
|
|
|
.mhandler.info = sun4m_pic_info,
|
|
|
|
#elif defined(TARGET_LM32)
|
|
|
|
.mhandler.info = lm32_do_pic_info,
|
|
|
|
#else
|
2009-10-07 20:41:51 +04:00
|
|
|
.mhandler.info = pic_info,
|
2011-10-16 13:53:13 +04:00
|
|
|
#endif
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
2011-10-16 13:53:13 +04:00
|
|
|
#endif
|
2009-10-07 20:41:50 +04:00
|
|
|
{
|
|
|
|
.name = "pci",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show PCI info",
|
2011-10-21 20:15:33 +04:00
|
|
|
.mhandler.info = hmp_info_pci,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
2011-08-18 14:38:42 +04:00
|
|
|
#if defined(TARGET_I386) || defined(TARGET_SH4) || defined(TARGET_SPARC) || \
|
2012-01-07 20:02:40 +04:00
|
|
|
defined(TARGET_PPC) || defined(TARGET_XTENSA)
|
2009-10-07 20:41:50 +04:00
|
|
|
{
|
|
|
|
.name = "tlb",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show virtual to physical memory mappings",
|
2009-10-07 20:41:51 +04:00
|
|
|
.mhandler.info = tlb_info,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
2009-03-03 09:12:22 +03:00
|
|
|
#endif
|
|
|
|
#if defined(TARGET_I386)
|
2009-10-07 20:41:50 +04:00
|
|
|
{
|
|
|
|
.name = "mem",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the active virtual memory mappings",
|
2009-10-07 20:41:51 +04:00
|
|
|
.mhandler.info = mem_info,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
2004-09-18 23:32:46 +04:00
|
|
|
#endif
|
2011-09-12 00:22:05 +04:00
|
|
|
{
|
|
|
|
.name = "mtree",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show memory tree",
|
|
|
|
.mhandler.info = do_info_mtree,
|
|
|
|
},
|
2009-10-07 20:41:50 +04:00
|
|
|
{
|
|
|
|
.name = "jit",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show dynamic compiler info",
|
2009-10-07 20:41:51 +04:00
|
|
|
.mhandler.info = do_info_jit,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "kvm",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show KVM information",
|
2011-09-12 22:10:53 +04:00
|
|
|
.mhandler.info = hmp_info_kvm,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "numa",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show NUMA information",
|
2009-10-07 20:41:51 +04:00
|
|
|
.mhandler.info = do_info_numa,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "usb",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show guest USB devices",
|
2009-10-07 20:41:51 +04:00
|
|
|
.mhandler.info = usb_info,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "usbhost",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show host USB devices",
|
2009-10-07 20:41:51 +04:00
|
|
|
.mhandler.info = usb_host_info,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "profile",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show profiling information",
|
2009-10-07 20:41:51 +04:00
|
|
|
.mhandler.info = do_info_profile,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "capture",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show capture information",
|
2009-10-07 20:41:51 +04:00
|
|
|
.mhandler.info = do_info_capture,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "snapshots",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the currently saved VM snapshots",
|
2009-10-07 20:41:51 +04:00
|
|
|
.mhandler.info = do_info_snapshots,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "status",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the current VM status (running|paused)",
|
2011-09-13 00:54:20 +04:00
|
|
|
.mhandler.info = hmp_info_status,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "pcmcia",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show guest PCMCIA status",
|
2009-10-07 20:41:51 +04:00
|
|
|
.mhandler.info = pcmcia_info,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "mice",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show which guest mouse is receiving events",
|
2011-09-21 22:29:55 +04:00
|
|
|
.mhandler.info = hmp_info_mice,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "vnc",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the vnc server status",
|
2011-10-17 22:41:22 +04:00
|
|
|
.mhandler.info = hmp_info_vnc,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
2010-11-30 13:02:51 +03:00
|
|
|
#if defined(CONFIG_SPICE)
|
|
|
|
{
|
|
|
|
.name = "spice",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the spice server status",
|
2011-10-20 23:01:33 +04:00
|
|
|
.mhandler.info = hmp_info_spice,
|
2010-11-30 13:02:51 +03:00
|
|
|
},
|
|
|
|
#endif
|
2009-10-07 20:41:50 +04:00
|
|
|
{
|
|
|
|
.name = "name",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the current VM name",
|
2011-09-02 21:34:48 +04:00
|
|
|
.mhandler.info = hmp_info_name,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "uuid",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the current VM UUID",
|
2011-09-14 00:16:25 +04:00
|
|
|
.mhandler.info = hmp_info_uuid,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
2007-03-07 11:32:30 +03:00
|
|
|
#if defined(TARGET_PPC)
|
2009-10-07 20:41:50 +04:00
|
|
|
{
|
|
|
|
.name = "cpustats",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show CPU statistics",
|
2009-10-07 20:41:51 +04:00
|
|
|
.mhandler.info = do_info_cpu_stats,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
2007-03-07 11:32:30 +03:00
|
|
|
#endif
|
2007-10-26 22:42:59 +04:00
|
|
|
#if defined(CONFIG_SLIRP)
|
2009-10-07 20:41:50 +04:00
|
|
|
{
|
|
|
|
.name = "usernet",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show user network stack connection states",
|
2009-10-07 20:41:51 +04:00
|
|
|
.mhandler.info = do_info_usernet,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
2007-10-26 22:42:59 +04:00
|
|
|
#endif
|
2009-10-07 20:41:50 +04:00
|
|
|
{
|
|
|
|
.name = "migrate",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show migration status",
|
2011-09-14 00:37:16 +04:00
|
|
|
.mhandler.info = hmp_info_migrate,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
2012-08-06 22:42:47 +04:00
|
|
|
{
|
|
|
|
.name = "migrate_capabilities",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show current migration capabilities",
|
|
|
|
.mhandler.info = hmp_info_migrate_capabilities,
|
|
|
|
},
|
2012-08-06 22:42:54 +04:00
|
|
|
{
|
|
|
|
.name = "migrate_cache_size",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show current migration xbzrle cache size",
|
|
|
|
.mhandler.info = hmp_info_migrate_cache_size,
|
|
|
|
},
|
2009-10-07 20:41:50 +04:00
|
|
|
{
|
|
|
|
.name = "balloon",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show balloon information",
|
2011-10-21 17:41:37 +04:00
|
|
|
.mhandler.info = hmp_info_balloon,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "qtree",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show device tree",
|
2009-10-07 20:41:51 +04:00
|
|
|
.mhandler.info = do_info_qtree,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "qdm",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show qdev device model list",
|
2009-10-07 20:41:51 +04:00
|
|
|
.mhandler.info = do_info_qdm,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "roms",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show roms",
|
2009-10-07 20:41:51 +04:00
|
|
|
.mhandler.info = do_info_roms,
|
2009-10-07 20:41:50 +04:00
|
|
|
},
|
2010-06-24 15:34:53 +04:00
|
|
|
{
|
|
|
|
.name = "trace-events",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show available trace-events & their state",
|
2011-08-31 22:31:18 +04:00
|
|
|
.mhandler.info = do_trace_print_events,
|
2010-06-24 15:34:53 +04:00
|
|
|
},
|
2009-10-07 20:41:50 +04:00
|
|
|
{
|
|
|
|
.name = NULL,
|
|
|
|
},
|
2004-03-15 00:38:27 +03:00
|
|
|
};
|
|
|
|
|
2010-09-16 00:17:45 +04:00
|
|
|
static const mon_cmd_t qmp_cmds[] = {
|
2011-09-02 21:34:47 +04:00
|
|
|
#include "qmp-commands-old.h"
|
2010-09-16 00:17:45 +04:00
|
|
|
{ /* NULL */ },
|
|
|
|
};
|
|
|
|
|
2004-04-04 16:57:25 +04:00
|
|
|
/*******************************************************************/
|
|
|
|
|
|
|
|
static const char *pch;
|
|
|
|
static jmp_buf expr_env;
|
|
|
|
|
2005-02-11 01:00:52 +03:00
|
|
|
#define MD_TLONG 0
|
|
|
|
#define MD_I32 1
|
|
|
|
|
2004-04-04 16:57:25 +04:00
|
|
|
typedef struct MonitorDef {
|
|
|
|
const char *name;
|
|
|
|
int offset;
|
2008-10-02 22:32:44 +04:00
|
|
|
target_long (*get_value)(const struct MonitorDef *md, int val);
|
2005-02-11 01:00:52 +03:00
|
|
|
int type;
|
2004-04-04 16:57:25 +04:00
|
|
|
} MonitorDef;
|
|
|
|
|
2004-04-25 22:54:52 +04:00
|
|
|
#if defined(TARGET_I386)
|
2008-10-02 22:32:44 +04:00
|
|
|
static target_long monitor_get_pc (const struct MonitorDef *md, int val)
|
2004-04-25 22:54:52 +04:00
|
|
|
{
|
2012-03-14 04:38:32 +04:00
|
|
|
CPUArchState *env = mon_get_cpu();
|
2005-11-22 02:25:50 +03:00
|
|
|
return env->eip + env->segs[R_CS].base;
|
2004-04-25 22:54:52 +04:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2004-04-13 00:39:29 +04:00
|
|
|
#if defined(TARGET_PPC)
|
2008-10-02 22:32:44 +04:00
|
|
|
static target_long monitor_get_ccr (const struct MonitorDef *md, int val)
|
2004-04-13 00:39:29 +04:00
|
|
|
{
|
2012-03-14 04:38:32 +04:00
|
|
|
CPUArchState *env = mon_get_cpu();
|
2004-04-13 00:39:29 +04:00
|
|
|
unsigned int u;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
u = 0;
|
|
|
|
for (i = 0; i < 8; i++)
|
2009-03-06 23:27:40 +03:00
|
|
|
u |= env->crf[i] << (32 - (4 * i));
|
2004-04-13 00:39:29 +04:00
|
|
|
|
|
|
|
return u;
|
|
|
|
}
|
|
|
|
|
2008-10-02 22:32:44 +04:00
|
|
|
static target_long monitor_get_msr (const struct MonitorDef *md, int val)
|
2004-04-13 00:39:29 +04:00
|
|
|
{
|
2012-03-14 04:38:32 +04:00
|
|
|
CPUArchState *env = mon_get_cpu();
|
2007-10-26 01:35:50 +04:00
|
|
|
return env->msr;
|
2004-04-13 00:39:29 +04:00
|
|
|
}
|
|
|
|
|
2008-10-02 22:32:44 +04:00
|
|
|
static target_long monitor_get_xer (const struct MonitorDef *md, int val)
|
2004-04-13 00:39:29 +04:00
|
|
|
{
|
2012-03-14 04:38:32 +04:00
|
|
|
CPUArchState *env = mon_get_cpu();
|
2008-10-21 15:28:46 +04:00
|
|
|
return env->xer;
|
2004-04-13 00:39:29 +04:00
|
|
|
}
|
2004-05-21 16:59:32 +04:00
|
|
|
|
2008-10-02 22:32:44 +04:00
|
|
|
static target_long monitor_get_decr (const struct MonitorDef *md, int val)
|
2004-05-21 16:59:32 +04:00
|
|
|
{
|
2012-03-14 04:38:32 +04:00
|
|
|
CPUArchState *env = mon_get_cpu();
|
2005-11-22 02:25:50 +03:00
|
|
|
return cpu_ppc_load_decr(env);
|
2004-05-21 16:59:32 +04:00
|
|
|
}
|
|
|
|
|
2008-10-02 22:32:44 +04:00
|
|
|
static target_long monitor_get_tbu (const struct MonitorDef *md, int val)
|
2004-05-21 16:59:32 +04:00
|
|
|
{
|
2012-03-14 04:38:32 +04:00
|
|
|
CPUArchState *env = mon_get_cpu();
|
2005-11-22 02:25:50 +03:00
|
|
|
return cpu_ppc_load_tbu(env);
|
2004-05-21 16:59:32 +04:00
|
|
|
}
|
|
|
|
|
2008-10-02 22:32:44 +04:00
|
|
|
static target_long monitor_get_tbl (const struct MonitorDef *md, int val)
|
2004-05-21 16:59:32 +04:00
|
|
|
{
|
2012-03-14 04:38:32 +04:00
|
|
|
CPUArchState *env = mon_get_cpu();
|
2005-11-22 02:25:50 +03:00
|
|
|
return cpu_ppc_load_tbl(env);
|
2004-05-21 16:59:32 +04:00
|
|
|
}
|
2004-04-13 00:39:29 +04:00
|
|
|
#endif
|
|
|
|
|
2004-10-01 02:22:08 +04:00
|
|
|
#if defined(TARGET_SPARC)
|
2005-10-30 20:05:13 +03:00
|
|
|
#ifndef TARGET_SPARC64
|
2008-10-02 22:32:44 +04:00
|
|
|
static target_long monitor_get_psr (const struct MonitorDef *md, int val)
|
2004-10-01 02:22:08 +04:00
|
|
|
{
|
2012-03-14 04:38:32 +04:00
|
|
|
CPUArchState *env = mon_get_cpu();
|
2010-05-10 00:19:04 +04:00
|
|
|
|
|
|
|
return cpu_get_psr(env);
|
2004-10-01 02:22:08 +04:00
|
|
|
}
|
2005-10-30 20:05:13 +03:00
|
|
|
#endif
|
2004-10-01 02:22:08 +04:00
|
|
|
|
2008-10-02 22:32:44 +04:00
|
|
|
static target_long monitor_get_reg(const struct MonitorDef *md, int val)
|
2004-10-01 02:22:08 +04:00
|
|
|
{
|
2012-03-14 04:38:32 +04:00
|
|
|
CPUArchState *env = mon_get_cpu();
|
2005-11-22 02:25:50 +03:00
|
|
|
return env->regwptr[val];
|
2004-10-01 02:22:08 +04:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-10-02 22:32:44 +04:00
|
|
|
static const MonitorDef monitor_defs[] = {
|
2004-04-04 16:57:25 +04:00
|
|
|
#ifdef TARGET_I386
|
2004-04-25 22:54:52 +04:00
|
|
|
|
|
|
|
#define SEG(name, seg) \
|
2012-02-16 03:40:47 +04:00
|
|
|
{ name, offsetof(CPUX86State, segs[seg].selector), NULL, MD_I32 },\
|
|
|
|
{ name ".base", offsetof(CPUX86State, segs[seg].base) },\
|
|
|
|
{ name ".limit", offsetof(CPUX86State, segs[seg].limit), NULL, MD_I32 },
|
|
|
|
|
|
|
|
{ "eax", offsetof(CPUX86State, regs[0]) },
|
|
|
|
{ "ecx", offsetof(CPUX86State, regs[1]) },
|
|
|
|
{ "edx", offsetof(CPUX86State, regs[2]) },
|
|
|
|
{ "ebx", offsetof(CPUX86State, regs[3]) },
|
|
|
|
{ "esp|sp", offsetof(CPUX86State, regs[4]) },
|
|
|
|
{ "ebp|fp", offsetof(CPUX86State, regs[5]) },
|
|
|
|
{ "esi", offsetof(CPUX86State, regs[6]) },
|
|
|
|
{ "edi", offsetof(CPUX86State, regs[7]) },
|
2005-02-11 01:00:52 +03:00
|
|
|
#ifdef TARGET_X86_64
|
2012-02-16 03:40:47 +04:00
|
|
|
{ "r8", offsetof(CPUX86State, regs[8]) },
|
|
|
|
{ "r9", offsetof(CPUX86State, regs[9]) },
|
|
|
|
{ "r10", offsetof(CPUX86State, regs[10]) },
|
|
|
|
{ "r11", offsetof(CPUX86State, regs[11]) },
|
|
|
|
{ "r12", offsetof(CPUX86State, regs[12]) },
|
|
|
|
{ "r13", offsetof(CPUX86State, regs[13]) },
|
|
|
|
{ "r14", offsetof(CPUX86State, regs[14]) },
|
|
|
|
{ "r15", offsetof(CPUX86State, regs[15]) },
|
2005-02-11 01:00:52 +03:00
|
|
|
#endif
|
2012-02-16 03:40:47 +04:00
|
|
|
{ "eflags", offsetof(CPUX86State, eflags) },
|
|
|
|
{ "eip", offsetof(CPUX86State, eip) },
|
2004-04-25 22:54:52 +04:00
|
|
|
SEG("cs", R_CS)
|
|
|
|
SEG("ds", R_DS)
|
|
|
|
SEG("es", R_ES)
|
2004-09-14 01:36:46 +04:00
|
|
|
SEG("ss", R_SS)
|
2004-04-25 22:54:52 +04:00
|
|
|
SEG("fs", R_FS)
|
|
|
|
SEG("gs", R_GS)
|
|
|
|
{ "pc", 0, monitor_get_pc, },
|
2004-04-13 00:39:29 +04:00
|
|
|
#elif defined(TARGET_PPC)
|
2007-09-19 09:49:13 +04:00
|
|
|
/* General purpose registers */
|
2012-02-16 03:40:47 +04:00
|
|
|
{ "r0", offsetof(CPUPPCState, gpr[0]) },
|
|
|
|
{ "r1", offsetof(CPUPPCState, gpr[1]) },
|
|
|
|
{ "r2", offsetof(CPUPPCState, gpr[2]) },
|
|
|
|
{ "r3", offsetof(CPUPPCState, gpr[3]) },
|
|
|
|
{ "r4", offsetof(CPUPPCState, gpr[4]) },
|
|
|
|
{ "r5", offsetof(CPUPPCState, gpr[5]) },
|
|
|
|
{ "r6", offsetof(CPUPPCState, gpr[6]) },
|
|
|
|
{ "r7", offsetof(CPUPPCState, gpr[7]) },
|
|
|
|
{ "r8", offsetof(CPUPPCState, gpr[8]) },
|
|
|
|
{ "r9", offsetof(CPUPPCState, gpr[9]) },
|
|
|
|
{ "r10", offsetof(CPUPPCState, gpr[10]) },
|
|
|
|
{ "r11", offsetof(CPUPPCState, gpr[11]) },
|
|
|
|
{ "r12", offsetof(CPUPPCState, gpr[12]) },
|
|
|
|
{ "r13", offsetof(CPUPPCState, gpr[13]) },
|
|
|
|
{ "r14", offsetof(CPUPPCState, gpr[14]) },
|
|
|
|
{ "r15", offsetof(CPUPPCState, gpr[15]) },
|
|
|
|
{ "r16", offsetof(CPUPPCState, gpr[16]) },
|
|
|
|
{ "r17", offsetof(CPUPPCState, gpr[17]) },
|
|
|
|
{ "r18", offsetof(CPUPPCState, gpr[18]) },
|
|
|
|
{ "r19", offsetof(CPUPPCState, gpr[19]) },
|
|
|
|
{ "r20", offsetof(CPUPPCState, gpr[20]) },
|
|
|
|
{ "r21", offsetof(CPUPPCState, gpr[21]) },
|
|
|
|
{ "r22", offsetof(CPUPPCState, gpr[22]) },
|
|
|
|
{ "r23", offsetof(CPUPPCState, gpr[23]) },
|
|
|
|
{ "r24", offsetof(CPUPPCState, gpr[24]) },
|
|
|
|
{ "r25", offsetof(CPUPPCState, gpr[25]) },
|
|
|
|
{ "r26", offsetof(CPUPPCState, gpr[26]) },
|
|
|
|
{ "r27", offsetof(CPUPPCState, gpr[27]) },
|
|
|
|
{ "r28", offsetof(CPUPPCState, gpr[28]) },
|
|
|
|
{ "r29", offsetof(CPUPPCState, gpr[29]) },
|
|
|
|
{ "r30", offsetof(CPUPPCState, gpr[30]) },
|
|
|
|
{ "r31", offsetof(CPUPPCState, gpr[31]) },
|
2007-09-19 09:49:13 +04:00
|
|
|
/* Floating point registers */
|
2012-02-16 03:40:47 +04:00
|
|
|
{ "f0", offsetof(CPUPPCState, fpr[0]) },
|
|
|
|
{ "f1", offsetof(CPUPPCState, fpr[1]) },
|
|
|
|
{ "f2", offsetof(CPUPPCState, fpr[2]) },
|
|
|
|
{ "f3", offsetof(CPUPPCState, fpr[3]) },
|
|
|
|
{ "f4", offsetof(CPUPPCState, fpr[4]) },
|
|
|
|
{ "f5", offsetof(CPUPPCState, fpr[5]) },
|
|
|
|
{ "f6", offsetof(CPUPPCState, fpr[6]) },
|
|
|
|
{ "f7", offsetof(CPUPPCState, fpr[7]) },
|
|
|
|
{ "f8", offsetof(CPUPPCState, fpr[8]) },
|
|
|
|
{ "f9", offsetof(CPUPPCState, fpr[9]) },
|
|
|
|
{ "f10", offsetof(CPUPPCState, fpr[10]) },
|
|
|
|
{ "f11", offsetof(CPUPPCState, fpr[11]) },
|
|
|
|
{ "f12", offsetof(CPUPPCState, fpr[12]) },
|
|
|
|
{ "f13", offsetof(CPUPPCState, fpr[13]) },
|
|
|
|
{ "f14", offsetof(CPUPPCState, fpr[14]) },
|
|
|
|
{ "f15", offsetof(CPUPPCState, fpr[15]) },
|
|
|
|
{ "f16", offsetof(CPUPPCState, fpr[16]) },
|
|
|
|
{ "f17", offsetof(CPUPPCState, fpr[17]) },
|
|
|
|
{ "f18", offsetof(CPUPPCState, fpr[18]) },
|
|
|
|
{ "f19", offsetof(CPUPPCState, fpr[19]) },
|
|
|
|
{ "f20", offsetof(CPUPPCState, fpr[20]) },
|
|
|
|
{ "f21", offsetof(CPUPPCState, fpr[21]) },
|
|
|
|
{ "f22", offsetof(CPUPPCState, fpr[22]) },
|
|
|
|
{ "f23", offsetof(CPUPPCState, fpr[23]) },
|
|
|
|
{ "f24", offsetof(CPUPPCState, fpr[24]) },
|
|
|
|
{ "f25", offsetof(CPUPPCState, fpr[25]) },
|
|
|
|
{ "f26", offsetof(CPUPPCState, fpr[26]) },
|
|
|
|
{ "f27", offsetof(CPUPPCState, fpr[27]) },
|
|
|
|
{ "f28", offsetof(CPUPPCState, fpr[28]) },
|
|
|
|
{ "f29", offsetof(CPUPPCState, fpr[29]) },
|
|
|
|
{ "f30", offsetof(CPUPPCState, fpr[30]) },
|
|
|
|
{ "f31", offsetof(CPUPPCState, fpr[31]) },
|
|
|
|
{ "fpscr", offsetof(CPUPPCState, fpscr) },
|
2007-09-19 09:49:13 +04:00
|
|
|
/* Next instruction pointer */
|
2012-02-16 03:40:47 +04:00
|
|
|
{ "nip|pc", offsetof(CPUPPCState, nip) },
|
|
|
|
{ "lr", offsetof(CPUPPCState, lr) },
|
|
|
|
{ "ctr", offsetof(CPUPPCState, ctr) },
|
2004-05-21 16:59:32 +04:00
|
|
|
{ "decr", 0, &monitor_get_decr, },
|
2004-04-13 00:39:29 +04:00
|
|
|
{ "ccr", 0, &monitor_get_ccr, },
|
2007-09-19 09:49:13 +04:00
|
|
|
/* Machine state register */
|
2004-04-13 00:39:29 +04:00
|
|
|
{ "msr", 0, &monitor_get_msr, },
|
|
|
|
{ "xer", 0, &monitor_get_xer, },
|
2004-05-21 16:59:32 +04:00
|
|
|
{ "tbu", 0, &monitor_get_tbu, },
|
|
|
|
{ "tbl", 0, &monitor_get_tbl, },
|
2007-09-19 09:49:13 +04:00
|
|
|
#if defined(TARGET_PPC64)
|
|
|
|
/* Address space register */
|
2012-02-16 03:40:47 +04:00
|
|
|
{ "asr", offsetof(CPUPPCState, asr) },
|
2007-09-19 09:49:13 +04:00
|
|
|
#endif
|
|
|
|
/* Segment registers */
|
2012-02-16 03:40:47 +04:00
|
|
|
{ "sdr1", offsetof(CPUPPCState, spr[SPR_SDR1]) },
|
|
|
|
{ "sr0", offsetof(CPUPPCState, sr[0]) },
|
|
|
|
{ "sr1", offsetof(CPUPPCState, sr[1]) },
|
|
|
|
{ "sr2", offsetof(CPUPPCState, sr[2]) },
|
|
|
|
{ "sr3", offsetof(CPUPPCState, sr[3]) },
|
|
|
|
{ "sr4", offsetof(CPUPPCState, sr[4]) },
|
|
|
|
{ "sr5", offsetof(CPUPPCState, sr[5]) },
|
|
|
|
{ "sr6", offsetof(CPUPPCState, sr[6]) },
|
|
|
|
{ "sr7", offsetof(CPUPPCState, sr[7]) },
|
|
|
|
{ "sr8", offsetof(CPUPPCState, sr[8]) },
|
|
|
|
{ "sr9", offsetof(CPUPPCState, sr[9]) },
|
|
|
|
{ "sr10", offsetof(CPUPPCState, sr[10]) },
|
|
|
|
{ "sr11", offsetof(CPUPPCState, sr[11]) },
|
|
|
|
{ "sr12", offsetof(CPUPPCState, sr[12]) },
|
|
|
|
{ "sr13", offsetof(CPUPPCState, sr[13]) },
|
|
|
|
{ "sr14", offsetof(CPUPPCState, sr[14]) },
|
|
|
|
{ "sr15", offsetof(CPUPPCState, sr[15]) },
|
2011-04-30 02:10:23 +04:00
|
|
|
/* Too lazy to put BATs... */
|
2012-02-16 03:40:47 +04:00
|
|
|
{ "pvr", offsetof(CPUPPCState, spr[SPR_PVR]) },
|
|
|
|
|
|
|
|
{ "srr0", offsetof(CPUPPCState, spr[SPR_SRR0]) },
|
|
|
|
{ "srr1", offsetof(CPUPPCState, spr[SPR_SRR1]) },
|
|
|
|
{ "sprg0", offsetof(CPUPPCState, spr[SPR_SPRG0]) },
|
|
|
|
{ "sprg1", offsetof(CPUPPCState, spr[SPR_SPRG1]) },
|
|
|
|
{ "sprg2", offsetof(CPUPPCState, spr[SPR_SPRG2]) },
|
|
|
|
{ "sprg3", offsetof(CPUPPCState, spr[SPR_SPRG3]) },
|
|
|
|
{ "sprg4", offsetof(CPUPPCState, spr[SPR_SPRG4]) },
|
|
|
|
{ "sprg5", offsetof(CPUPPCState, spr[SPR_SPRG5]) },
|
|
|
|
{ "sprg6", offsetof(CPUPPCState, spr[SPR_SPRG6]) },
|
|
|
|
{ "sprg7", offsetof(CPUPPCState, spr[SPR_SPRG7]) },
|
|
|
|
{ "pid", offsetof(CPUPPCState, spr[SPR_BOOKE_PID]) },
|
|
|
|
{ "csrr0", offsetof(CPUPPCState, spr[SPR_BOOKE_CSRR0]) },
|
|
|
|
{ "csrr1", offsetof(CPUPPCState, spr[SPR_BOOKE_CSRR1]) },
|
|
|
|
{ "esr", offsetof(CPUPPCState, spr[SPR_BOOKE_ESR]) },
|
|
|
|
{ "dear", offsetof(CPUPPCState, spr[SPR_BOOKE_DEAR]) },
|
|
|
|
{ "mcsr", offsetof(CPUPPCState, spr[SPR_BOOKE_MCSR]) },
|
|
|
|
{ "tsr", offsetof(CPUPPCState, spr[SPR_BOOKE_TSR]) },
|
|
|
|
{ "tcr", offsetof(CPUPPCState, spr[SPR_BOOKE_TCR]) },
|
|
|
|
{ "vrsave", offsetof(CPUPPCState, spr[SPR_VRSAVE]) },
|
|
|
|
{ "pir", offsetof(CPUPPCState, spr[SPR_BOOKE_PIR]) },
|
|
|
|
{ "mcsrr0", offsetof(CPUPPCState, spr[SPR_BOOKE_MCSRR0]) },
|
|
|
|
{ "mcsrr1", offsetof(CPUPPCState, spr[SPR_BOOKE_MCSRR1]) },
|
|
|
|
{ "decar", offsetof(CPUPPCState, spr[SPR_BOOKE_DECAR]) },
|
|
|
|
{ "ivpr", offsetof(CPUPPCState, spr[SPR_BOOKE_IVPR]) },
|
|
|
|
{ "epcr", offsetof(CPUPPCState, spr[SPR_BOOKE_EPCR]) },
|
|
|
|
{ "sprg8", offsetof(CPUPPCState, spr[SPR_BOOKE_SPRG8]) },
|
|
|
|
{ "ivor0", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR0]) },
|
|
|
|
{ "ivor1", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR1]) },
|
|
|
|
{ "ivor2", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR2]) },
|
|
|
|
{ "ivor3", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR3]) },
|
|
|
|
{ "ivor4", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR4]) },
|
|
|
|
{ "ivor5", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR5]) },
|
|
|
|
{ "ivor6", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR6]) },
|
|
|
|
{ "ivor7", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR7]) },
|
|
|
|
{ "ivor8", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR8]) },
|
|
|
|
{ "ivor9", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR9]) },
|
|
|
|
{ "ivor10", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR10]) },
|
|
|
|
{ "ivor11", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR11]) },
|
|
|
|
{ "ivor12", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR12]) },
|
|
|
|
{ "ivor13", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR13]) },
|
|
|
|
{ "ivor14", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR14]) },
|
|
|
|
{ "ivor15", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR15]) },
|
|
|
|
{ "ivor32", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR32]) },
|
|
|
|
{ "ivor33", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR33]) },
|
|
|
|
{ "ivor34", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR34]) },
|
|
|
|
{ "ivor35", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR35]) },
|
|
|
|
{ "ivor36", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR36]) },
|
|
|
|
{ "ivor37", offsetof(CPUPPCState, spr[SPR_BOOKE_IVOR37]) },
|
|
|
|
{ "mas0", offsetof(CPUPPCState, spr[SPR_BOOKE_MAS0]) },
|
|
|
|
{ "mas1", offsetof(CPUPPCState, spr[SPR_BOOKE_MAS1]) },
|
|
|
|
{ "mas2", offsetof(CPUPPCState, spr[SPR_BOOKE_MAS2]) },
|
|
|
|
{ "mas3", offsetof(CPUPPCState, spr[SPR_BOOKE_MAS3]) },
|
|
|
|
{ "mas4", offsetof(CPUPPCState, spr[SPR_BOOKE_MAS4]) },
|
|
|
|
{ "mas6", offsetof(CPUPPCState, spr[SPR_BOOKE_MAS6]) },
|
|
|
|
{ "mas7", offsetof(CPUPPCState, spr[SPR_BOOKE_MAS7]) },
|
|
|
|
{ "mmucfg", offsetof(CPUPPCState, spr[SPR_MMUCFG]) },
|
|
|
|
{ "tlb0cfg", offsetof(CPUPPCState, spr[SPR_BOOKE_TLB0CFG]) },
|
|
|
|
{ "tlb1cfg", offsetof(CPUPPCState, spr[SPR_BOOKE_TLB1CFG]) },
|
|
|
|
{ "epr", offsetof(CPUPPCState, spr[SPR_BOOKE_EPR]) },
|
|
|
|
{ "eplc", offsetof(CPUPPCState, spr[SPR_BOOKE_EPLC]) },
|
|
|
|
{ "epsc", offsetof(CPUPPCState, spr[SPR_BOOKE_EPSC]) },
|
|
|
|
{ "svr", offsetof(CPUPPCState, spr[SPR_E500_SVR]) },
|
|
|
|
{ "mcar", offsetof(CPUPPCState, spr[SPR_Exxx_MCAR]) },
|
|
|
|
{ "pid1", offsetof(CPUPPCState, spr[SPR_BOOKE_PID1]) },
|
|
|
|
{ "pid2", offsetof(CPUPPCState, spr[SPR_BOOKE_PID2]) },
|
|
|
|
{ "hid0", offsetof(CPUPPCState, spr[SPR_HID0]) },
|
2011-04-30 02:10:23 +04:00
|
|
|
|
2004-10-01 02:22:08 +04:00
|
|
|
#elif defined(TARGET_SPARC)
|
2012-02-16 03:40:47 +04:00
|
|
|
{ "g0", offsetof(CPUSPARCState, gregs[0]) },
|
|
|
|
{ "g1", offsetof(CPUSPARCState, gregs[1]) },
|
|
|
|
{ "g2", offsetof(CPUSPARCState, gregs[2]) },
|
|
|
|
{ "g3", offsetof(CPUSPARCState, gregs[3]) },
|
|
|
|
{ "g4", offsetof(CPUSPARCState, gregs[4]) },
|
|
|
|
{ "g5", offsetof(CPUSPARCState, gregs[5]) },
|
|
|
|
{ "g6", offsetof(CPUSPARCState, gregs[6]) },
|
|
|
|
{ "g7", offsetof(CPUSPARCState, gregs[7]) },
|
2004-10-01 02:22:08 +04:00
|
|
|
{ "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 },
|
2012-02-16 03:40:47 +04:00
|
|
|
{ "pc", offsetof(CPUSPARCState, pc) },
|
|
|
|
{ "npc", offsetof(CPUSPARCState, npc) },
|
|
|
|
{ "y", offsetof(CPUSPARCState, y) },
|
2005-10-30 20:05:13 +03:00
|
|
|
#ifndef TARGET_SPARC64
|
2004-10-01 02:22:08 +04:00
|
|
|
{ "psr", 0, &monitor_get_psr, },
|
2012-02-16 03:40:47 +04:00
|
|
|
{ "wim", offsetof(CPUSPARCState, wim) },
|
2005-10-30 20:05:13 +03:00
|
|
|
#endif
|
2012-02-16 03:40:47 +04:00
|
|
|
{ "tbr", offsetof(CPUSPARCState, tbr) },
|
|
|
|
{ "fsr", offsetof(CPUSPARCState, fsr) },
|
|
|
|
{ "f0", offsetof(CPUSPARCState, fpr[0].l.upper) },
|
|
|
|
{ "f1", offsetof(CPUSPARCState, fpr[0].l.lower) },
|
|
|
|
{ "f2", offsetof(CPUSPARCState, fpr[1].l.upper) },
|
|
|
|
{ "f3", offsetof(CPUSPARCState, fpr[1].l.lower) },
|
|
|
|
{ "f4", offsetof(CPUSPARCState, fpr[2].l.upper) },
|
|
|
|
{ "f5", offsetof(CPUSPARCState, fpr[2].l.lower) },
|
|
|
|
{ "f6", offsetof(CPUSPARCState, fpr[3].l.upper) },
|
|
|
|
{ "f7", offsetof(CPUSPARCState, fpr[3].l.lower) },
|
|
|
|
{ "f8", offsetof(CPUSPARCState, fpr[4].l.upper) },
|
|
|
|
{ "f9", offsetof(CPUSPARCState, fpr[4].l.lower) },
|
|
|
|
{ "f10", offsetof(CPUSPARCState, fpr[5].l.upper) },
|
|
|
|
{ "f11", offsetof(CPUSPARCState, fpr[5].l.lower) },
|
|
|
|
{ "f12", offsetof(CPUSPARCState, fpr[6].l.upper) },
|
|
|
|
{ "f13", offsetof(CPUSPARCState, fpr[6].l.lower) },
|
|
|
|
{ "f14", offsetof(CPUSPARCState, fpr[7].l.upper) },
|
|
|
|
{ "f15", offsetof(CPUSPARCState, fpr[7].l.lower) },
|
|
|
|
{ "f16", offsetof(CPUSPARCState, fpr[8].l.upper) },
|
|
|
|
{ "f17", offsetof(CPUSPARCState, fpr[8].l.lower) },
|
|
|
|
{ "f18", offsetof(CPUSPARCState, fpr[9].l.upper) },
|
|
|
|
{ "f19", offsetof(CPUSPARCState, fpr[9].l.lower) },
|
|
|
|
{ "f20", offsetof(CPUSPARCState, fpr[10].l.upper) },
|
|
|
|
{ "f21", offsetof(CPUSPARCState, fpr[10].l.lower) },
|
|
|
|
{ "f22", offsetof(CPUSPARCState, fpr[11].l.upper) },
|
|
|
|
{ "f23", offsetof(CPUSPARCState, fpr[11].l.lower) },
|
|
|
|
{ "f24", offsetof(CPUSPARCState, fpr[12].l.upper) },
|
|
|
|
{ "f25", offsetof(CPUSPARCState, fpr[12].l.lower) },
|
|
|
|
{ "f26", offsetof(CPUSPARCState, fpr[13].l.upper) },
|
|
|
|
{ "f27", offsetof(CPUSPARCState, fpr[13].l.lower) },
|
|
|
|
{ "f28", offsetof(CPUSPARCState, fpr[14].l.upper) },
|
|
|
|
{ "f29", offsetof(CPUSPARCState, fpr[14].l.lower) },
|
|
|
|
{ "f30", offsetof(CPUSPARCState, fpr[15].l.upper) },
|
|
|
|
{ "f31", offsetof(CPUSPARCState, fpr[15].l.lower) },
|
2005-10-30 20:05:13 +03:00
|
|
|
#ifdef TARGET_SPARC64
|
2012-02-16 03:40:47 +04:00
|
|
|
{ "f32", offsetof(CPUSPARCState, fpr[16]) },
|
|
|
|
{ "f34", offsetof(CPUSPARCState, fpr[17]) },
|
|
|
|
{ "f36", offsetof(CPUSPARCState, fpr[18]) },
|
|
|
|
{ "f38", offsetof(CPUSPARCState, fpr[19]) },
|
|
|
|
{ "f40", offsetof(CPUSPARCState, fpr[20]) },
|
|
|
|
{ "f42", offsetof(CPUSPARCState, fpr[21]) },
|
|
|
|
{ "f44", offsetof(CPUSPARCState, fpr[22]) },
|
|
|
|
{ "f46", offsetof(CPUSPARCState, fpr[23]) },
|
|
|
|
{ "f48", offsetof(CPUSPARCState, fpr[24]) },
|
|
|
|
{ "f50", offsetof(CPUSPARCState, fpr[25]) },
|
|
|
|
{ "f52", offsetof(CPUSPARCState, fpr[26]) },
|
|
|
|
{ "f54", offsetof(CPUSPARCState, fpr[27]) },
|
|
|
|
{ "f56", offsetof(CPUSPARCState, fpr[28]) },
|
|
|
|
{ "f58", offsetof(CPUSPARCState, fpr[29]) },
|
|
|
|
{ "f60", offsetof(CPUSPARCState, fpr[30]) },
|
|
|
|
{ "f62", offsetof(CPUSPARCState, fpr[31]) },
|
|
|
|
{ "asi", offsetof(CPUSPARCState, asi) },
|
|
|
|
{ "pstate", offsetof(CPUSPARCState, pstate) },
|
|
|
|
{ "cansave", offsetof(CPUSPARCState, cansave) },
|
|
|
|
{ "canrestore", offsetof(CPUSPARCState, canrestore) },
|
|
|
|
{ "otherwin", offsetof(CPUSPARCState, otherwin) },
|
|
|
|
{ "wstate", offsetof(CPUSPARCState, wstate) },
|
|
|
|
{ "cleanwin", offsetof(CPUSPARCState, cleanwin) },
|
|
|
|
{ "fprs", offsetof(CPUSPARCState, fprs) },
|
2005-10-30 20:05:13 +03:00
|
|
|
#endif
|
2004-04-04 16:57:25 +04:00
|
|
|
#endif
|
|
|
|
{ NULL },
|
|
|
|
};
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void expr_error(Monitor *mon, const char *msg)
|
2004-03-15 00:38:27 +03:00
|
|
|
{
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%s\n", msg);
|
2004-04-04 16:57:25 +04:00
|
|
|
longjmp(expr_env, 1);
|
|
|
|
}
|
|
|
|
|
2010-01-20 15:07:30 +03:00
|
|
|
/* return 0 if OK, -1 if not found */
|
2005-02-11 01:00:52 +03:00
|
|
|
static int get_monitor_def(target_long *pval, const char *name)
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
2008-10-02 22:32:44 +04:00
|
|
|
const MonitorDef *md;
|
2005-02-11 01:00:52 +03:00
|
|
|
void *ptr;
|
|
|
|
|
2004-04-04 16:57:25 +04:00
|
|
|
for(md = monitor_defs; md->name != NULL; md++) {
|
|
|
|
if (compare_cmd(name, md->name)) {
|
|
|
|
if (md->get_value) {
|
2004-10-01 02:22:08 +04:00
|
|
|
*pval = md->get_value(md, md->offset);
|
2004-04-04 16:57:25 +04:00
|
|
|
} else {
|
2012-03-14 04:38:32 +04:00
|
|
|
CPUArchState *env = mon_get_cpu();
|
2005-11-22 02:25:50 +03:00
|
|
|
ptr = (uint8_t *)env + md->offset;
|
2005-02-11 01:00:52 +03:00
|
|
|
switch(md->type) {
|
|
|
|
case MD_I32:
|
|
|
|
*pval = *(int32_t *)ptr;
|
|
|
|
break;
|
|
|
|
case MD_TLONG:
|
|
|
|
*pval = *(target_long *)ptr;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
*pval = 0;
|
|
|
|
break;
|
|
|
|
}
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void next(void)
|
|
|
|
{
|
2009-08-01 01:16:51 +04:00
|
|
|
if (*pch != '\0') {
|
2004-04-04 16:57:25 +04:00
|
|
|
pch++;
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isspace(*pch))
|
2004-04-04 16:57:25 +04:00
|
|
|
pch++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static int64_t expr_sum(Monitor *mon);
|
2004-04-04 16:57:25 +04:00
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static int64_t expr_unary(Monitor *mon)
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
2007-09-25 21:28:42 +04:00
|
|
|
int64_t n;
|
2004-04-04 16:57:25 +04:00
|
|
|
char *p;
|
2005-11-22 02:25:50 +03:00
|
|
|
int ret;
|
2004-04-04 16:57:25 +04:00
|
|
|
|
|
|
|
switch(*pch) {
|
|
|
|
case '+':
|
|
|
|
next();
|
2009-03-06 02:01:23 +03:00
|
|
|
n = expr_unary(mon);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case '-':
|
|
|
|
next();
|
2009-03-06 02:01:23 +03:00
|
|
|
n = -expr_unary(mon);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case '~':
|
|
|
|
next();
|
2009-03-06 02:01:23 +03:00
|
|
|
n = ~expr_unary(mon);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case '(':
|
|
|
|
next();
|
2009-03-06 02:01:23 +03:00
|
|
|
n = expr_sum(mon);
|
2004-04-04 16:57:25 +04:00
|
|
|
if (*pch != ')') {
|
2009-03-06 02:01:23 +03:00
|
|
|
expr_error(mon, "')' expected");
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
|
|
|
next();
|
|
|
|
break;
|
2004-07-14 21:21:37 +04:00
|
|
|
case '\'':
|
|
|
|
pch++;
|
|
|
|
if (*pch == '\0')
|
2009-03-06 02:01:23 +03:00
|
|
|
expr_error(mon, "character constant expected");
|
2004-07-14 21:21:37 +04:00
|
|
|
n = *pch;
|
|
|
|
pch++;
|
|
|
|
if (*pch != '\'')
|
2009-03-06 02:01:23 +03:00
|
|
|
expr_error(mon, "missing terminating \' character");
|
2004-07-14 21:21:37 +04:00
|
|
|
next();
|
|
|
|
break;
|
2004-04-04 16:57:25 +04:00
|
|
|
case '$':
|
|
|
|
{
|
|
|
|
char buf[128], *q;
|
2007-12-17 06:15:52 +03:00
|
|
|
target_long reg=0;
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2004-04-04 16:57:25 +04:00
|
|
|
pch++;
|
|
|
|
q = buf;
|
|
|
|
while ((*pch >= 'a' && *pch <= 'z') ||
|
|
|
|
(*pch >= 'A' && *pch <= 'Z') ||
|
|
|
|
(*pch >= '0' && *pch <= '9') ||
|
2004-04-25 22:54:52 +04:00
|
|
|
*pch == '_' || *pch == '.') {
|
2004-04-04 16:57:25 +04:00
|
|
|
if ((q - buf) < sizeof(buf) - 1)
|
|
|
|
*q++ = *pch;
|
|
|
|
pch++;
|
|
|
|
}
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isspace(*pch))
|
2004-04-04 16:57:25 +04:00
|
|
|
pch++;
|
|
|
|
*q = 0;
|
2007-09-24 22:39:04 +04:00
|
|
|
ret = get_monitor_def(®, buf);
|
2010-01-20 15:07:30 +03:00
|
|
|
if (ret < 0)
|
2009-03-06 02:01:23 +03:00
|
|
|
expr_error(mon, "unknown register");
|
2007-09-24 22:39:04 +04:00
|
|
|
n = reg;
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '\0':
|
2009-03-06 02:01:23 +03:00
|
|
|
expr_error(mon, "unexpected end of expression");
|
2004-04-04 16:57:25 +04:00
|
|
|
n = 0;
|
|
|
|
break;
|
|
|
|
default:
|
2012-04-26 23:48:41 +04:00
|
|
|
errno = 0;
|
2006-06-25 22:28:12 +04:00
|
|
|
n = strtoull(pch, &p, 0);
|
2012-04-26 23:48:41 +04:00
|
|
|
if (errno == ERANGE) {
|
|
|
|
expr_error(mon, "number too large");
|
|
|
|
}
|
2004-04-04 16:57:25 +04:00
|
|
|
if (pch == p) {
|
2009-03-06 02:01:23 +03:00
|
|
|
expr_error(mon, "invalid char in expression");
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
|
|
|
pch = p;
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isspace(*pch))
|
2004-04-04 16:57:25 +04:00
|
|
|
pch++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static int64_t expr_prod(Monitor *mon)
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
2007-09-25 21:28:42 +04:00
|
|
|
int64_t val, val2;
|
2005-02-11 01:00:52 +03:00
|
|
|
int op;
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
val = expr_unary(mon);
|
2004-04-04 16:57:25 +04:00
|
|
|
for(;;) {
|
|
|
|
op = *pch;
|
|
|
|
if (op != '*' && op != '/' && op != '%')
|
|
|
|
break;
|
|
|
|
next();
|
2009-03-06 02:01:23 +03:00
|
|
|
val2 = expr_unary(mon);
|
2004-04-04 16:57:25 +04:00
|
|
|
switch(op) {
|
|
|
|
default:
|
|
|
|
case '*':
|
|
|
|
val *= val2;
|
|
|
|
break;
|
|
|
|
case '/':
|
|
|
|
case '%':
|
2007-09-17 01:08:06 +04:00
|
|
|
if (val2 == 0)
|
2009-03-06 02:01:23 +03:00
|
|
|
expr_error(mon, "division by zero");
|
2004-04-04 16:57:25 +04:00
|
|
|
if (op == '/')
|
|
|
|
val /= val2;
|
|
|
|
else
|
|
|
|
val %= val2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static int64_t expr_logic(Monitor *mon)
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
2007-09-25 21:28:42 +04:00
|
|
|
int64_t val, val2;
|
2005-02-11 01:00:52 +03:00
|
|
|
int op;
|
2004-04-04 16:57:25 +04:00
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
val = expr_prod(mon);
|
2004-04-04 16:57:25 +04:00
|
|
|
for(;;) {
|
|
|
|
op = *pch;
|
|
|
|
if (op != '&' && op != '|' && op != '^')
|
|
|
|
break;
|
|
|
|
next();
|
2009-03-06 02:01:23 +03:00
|
|
|
val2 = expr_prod(mon);
|
2004-04-04 16:57:25 +04:00
|
|
|
switch(op) {
|
|
|
|
default:
|
|
|
|
case '&':
|
|
|
|
val &= val2;
|
|
|
|
break;
|
|
|
|
case '|':
|
|
|
|
val |= val2;
|
|
|
|
break;
|
|
|
|
case '^':
|
|
|
|
val ^= val2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static int64_t expr_sum(Monitor *mon)
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
2007-09-25 21:28:42 +04:00
|
|
|
int64_t val, val2;
|
2005-02-11 01:00:52 +03:00
|
|
|
int op;
|
2004-04-04 16:57:25 +04:00
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
val = expr_logic(mon);
|
2004-04-04 16:57:25 +04:00
|
|
|
for(;;) {
|
|
|
|
op = *pch;
|
|
|
|
if (op != '+' && op != '-')
|
|
|
|
break;
|
|
|
|
next();
|
2009-03-06 02:01:23 +03:00
|
|
|
val2 = expr_logic(mon);
|
2004-04-04 16:57:25 +04:00
|
|
|
if (op == '+')
|
|
|
|
val += val2;
|
|
|
|
else
|
|
|
|
val -= val2;
|
|
|
|
}
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static int get_expr(Monitor *mon, int64_t *pval, const char **pp)
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
|
|
|
pch = *pp;
|
|
|
|
if (setjmp(expr_env)) {
|
|
|
|
*pp = pch;
|
|
|
|
return -1;
|
|
|
|
}
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isspace(*pch))
|
2004-04-04 16:57:25 +04:00
|
|
|
pch++;
|
2009-03-06 02:01:23 +03:00
|
|
|
*pval = expr_sum(mon);
|
2004-04-04 16:57:25 +04:00
|
|
|
*pp = pch;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-01-25 16:23:03 +03:00
|
|
|
static int get_double(Monitor *mon, double *pval, const char **pp)
|
|
|
|
{
|
|
|
|
const char *p = *pp;
|
|
|
|
char *tailp;
|
|
|
|
double d;
|
|
|
|
|
|
|
|
d = strtod(p, &tailp);
|
|
|
|
if (tailp == p) {
|
|
|
|
monitor_printf(mon, "Number expected\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (d != d || d - d != 0) {
|
|
|
|
/* NaN or infinity */
|
|
|
|
monitor_printf(mon, "Bad number\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
*pval = d;
|
|
|
|
*pp = tailp;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-04-04 16:57:25 +04:00
|
|
|
static int get_str(char *buf, int buf_size, const char **pp)
|
|
|
|
{
|
|
|
|
const char *p;
|
|
|
|
char *q;
|
|
|
|
int c;
|
|
|
|
|
2004-07-14 21:21:37 +04:00
|
|
|
q = buf;
|
2004-04-04 16:57:25 +04:00
|
|
|
p = *pp;
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isspace(*p))
|
2004-04-04 16:57:25 +04:00
|
|
|
p++;
|
|
|
|
if (*p == '\0') {
|
|
|
|
fail:
|
2004-07-14 21:21:37 +04:00
|
|
|
*q = '\0';
|
2004-04-04 16:57:25 +04:00
|
|
|
*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 != '\"') {
|
2004-05-23 01:41:05 +04:00
|
|
|
qemu_printf("unterminated string\n");
|
2004-04-04 16:57:25 +04:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
p++;
|
|
|
|
} else {
|
2008-11-16 16:53:32 +03:00
|
|
|
while (*p != '\0' && !qemu_isspace(*p)) {
|
2004-04-04 16:57:25 +04:00
|
|
|
if ((q - buf) < buf_size - 1) {
|
|
|
|
*q++ = *p;
|
|
|
|
}
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
2004-07-14 21:21:37 +04:00
|
|
|
*q = '\0';
|
2004-04-04 16:57:25 +04:00
|
|
|
*pp = p;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-06-10 01:21:30 +04:00
|
|
|
/*
|
|
|
|
* Store the command-name in cmdname, and return a pointer to
|
|
|
|
* the remaining of the command string.
|
|
|
|
*/
|
|
|
|
static const char *get_command_name(const char *cmdline,
|
|
|
|
char *cmdname, size_t nlen)
|
|
|
|
{
|
|
|
|
size_t len;
|
|
|
|
const char *p, *pstart;
|
|
|
|
|
|
|
|
p = cmdline;
|
|
|
|
while (qemu_isspace(*p))
|
|
|
|
p++;
|
|
|
|
if (*p == '\0')
|
|
|
|
return NULL;
|
|
|
|
pstart = p;
|
|
|
|
while (*p != '\0' && *p != '/' && !qemu_isspace(*p))
|
|
|
|
p++;
|
|
|
|
len = p - pstart;
|
|
|
|
if (len > nlen - 1)
|
|
|
|
len = nlen - 1;
|
|
|
|
memcpy(cmdname, pstart, len);
|
|
|
|
cmdname[len] = '\0';
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
monitor: New format for handlers argument types
Current handlers argument types, as defined in qemu-monitor.hx file,
are a sequence of chars where each one represents one argument type
of the command handler. The number of chars is also used to know how
many arguments a given handler accepts.
This commit defines a new format, which makes mandatory the use of
a name for each argument.
For example, do_eject() command handler is currently defined as:
{ "eject", "-fB", do_eject, ... }
With the new format it becomes:
{ "eject", "force:-f,filename:B", do_eject, ... }
This way the Monitor will be capable of setting up a dictionary, using
each argument's name as the key and the argument itself as the value.
This commit also adds two new functions: key_get_info() and
next_arg_type(), both are used to parse the new format.
Currently key_get_info() consumes the 'key' part of the new format and
discards it, this way the current parsing code is not affected by this
change.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-08-28 22:27:09 +04:00
|
|
|
/**
|
|
|
|
* Read key of 'type' into 'key' and return the current
|
|
|
|
* 'type' pointer.
|
|
|
|
*/
|
|
|
|
static char *key_get_info(const char *type, char **key)
|
|
|
|
{
|
|
|
|
size_t len;
|
|
|
|
char *p, *str;
|
|
|
|
|
|
|
|
if (*type == ',')
|
|
|
|
type++;
|
|
|
|
|
|
|
|
p = strchr(type, ':');
|
|
|
|
if (!p) {
|
|
|
|
*key = NULL;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
len = p - type;
|
|
|
|
|
2011-08-21 07:09:37 +04:00
|
|
|
str = g_malloc(len + 1);
|
monitor: New format for handlers argument types
Current handlers argument types, as defined in qemu-monitor.hx file,
are a sequence of chars where each one represents one argument type
of the command handler. The number of chars is also used to know how
many arguments a given handler accepts.
This commit defines a new format, which makes mandatory the use of
a name for each argument.
For example, do_eject() command handler is currently defined as:
{ "eject", "-fB", do_eject, ... }
With the new format it becomes:
{ "eject", "force:-f,filename:B", do_eject, ... }
This way the Monitor will be capable of setting up a dictionary, using
each argument's name as the key and the argument itself as the value.
This commit also adds two new functions: key_get_info() and
next_arg_type(), both are used to parse the new format.
Currently key_get_info() consumes the 'key' part of the new format and
discards it, this way the current parsing code is not affected by this
change.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-08-28 22:27:09 +04:00
|
|
|
memcpy(str, type, len);
|
|
|
|
str[len] = '\0';
|
|
|
|
|
|
|
|
*key = str;
|
|
|
|
return ++p;
|
|
|
|
}
|
|
|
|
|
2004-04-04 16:57:25 +04:00
|
|
|
static int default_fmt_format = 'x';
|
|
|
|
static int default_fmt_size = 4;
|
|
|
|
|
|
|
|
#define MAX_ARGS 16
|
|
|
|
|
2009-11-02 16:41:13 +03:00
|
|
|
static int is_valid_option(const char *c, const char *typestr)
|
|
|
|
{
|
|
|
|
char option[3];
|
|
|
|
|
|
|
|
option[0] = '-';
|
|
|
|
option[1] = *c;
|
|
|
|
option[2] = '\0';
|
|
|
|
|
|
|
|
typestr = strstr(typestr, option);
|
|
|
|
return (typestr != NULL);
|
|
|
|
}
|
|
|
|
|
2010-09-13 20:17:58 +04:00
|
|
|
static const mon_cmd_t *search_dispatch_table(const mon_cmd_t *disp_table,
|
|
|
|
const char *cmdname)
|
2009-11-27 03:58:54 +03:00
|
|
|
{
|
|
|
|
const mon_cmd_t *cmd;
|
|
|
|
|
2010-09-13 20:17:58 +04:00
|
|
|
for (cmd = disp_table; cmd->name != NULL; cmd++) {
|
2009-11-27 03:58:54 +03:00
|
|
|
if (compare_cmd(cmdname, cmd->name)) {
|
|
|
|
return cmd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-09-13 20:17:58 +04:00
|
|
|
static const mon_cmd_t *monitor_find_command(const char *cmdname)
|
|
|
|
{
|
|
|
|
return search_dispatch_table(mon_cmds, cmdname);
|
|
|
|
}
|
|
|
|
|
2010-09-16 00:08:39 +04:00
|
|
|
static const mon_cmd_t *qmp_find_cmd(const char *cmdname)
|
|
|
|
{
|
2010-09-16 00:17:45 +04:00
|
|
|
return search_dispatch_table(qmp_cmds, cmdname);
|
2010-09-16 00:08:39 +04:00
|
|
|
}
|
|
|
|
|
2009-10-02 01:12:16 +04:00
|
|
|
static const mon_cmd_t *monitor_parse_command(Monitor *mon,
|
2009-08-28 22:27:22 +04:00
|
|
|
const char *cmdline,
|
|
|
|
QDict *qdict)
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
2009-06-10 01:21:30 +04:00
|
|
|
const char *p, *typestr;
|
2009-08-28 22:27:25 +04:00
|
|
|
int c;
|
2009-10-02 01:12:16 +04:00
|
|
|
const mon_cmd_t *cmd;
|
2004-04-04 16:57:25 +04:00
|
|
|
char cmdname[256];
|
|
|
|
char buf[1024];
|
monitor: New format for handlers argument types
Current handlers argument types, as defined in qemu-monitor.hx file,
are a sequence of chars where each one represents one argument type
of the command handler. The number of chars is also used to know how
many arguments a given handler accepts.
This commit defines a new format, which makes mandatory the use of
a name for each argument.
For example, do_eject() command handler is currently defined as:
{ "eject", "-fB", do_eject, ... }
With the new format it becomes:
{ "eject", "force:-f,filename:B", do_eject, ... }
This way the Monitor will be capable of setting up a dictionary, using
each argument's name as the key and the argument itself as the value.
This commit also adds two new functions: key_get_info() and
next_arg_type(), both are used to parse the new format.
Currently key_get_info() consumes the 'key' part of the new format and
discards it, this way the current parsing code is not affected by this
change.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-08-28 22:27:09 +04:00
|
|
|
char *key;
|
2004-03-15 00:38:27 +03:00
|
|
|
|
|
|
|
#ifdef DEBUG
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "command='%s'\n", cmdline);
|
2004-03-15 00:38:27 +03:00
|
|
|
#endif
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2004-04-04 16:57:25 +04:00
|
|
|
/* extract the command name */
|
2009-06-10 01:21:30 +04:00
|
|
|
p = get_command_name(cmdline, cmdname, sizeof(cmdname));
|
|
|
|
if (!p)
|
2009-08-28 22:27:22 +04:00
|
|
|
return NULL;
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2009-11-27 03:58:54 +03:00
|
|
|
cmd = monitor_find_command(cmdname);
|
|
|
|
if (!cmd) {
|
2009-06-10 01:21:54 +04:00
|
|
|
monitor_printf(mon, "unknown command: '%s'\n", cmdname);
|
2009-08-28 22:27:22 +04:00
|
|
|
return NULL;
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* parse the parameters */
|
|
|
|
typestr = cmd->args_type;
|
2004-03-15 00:38:27 +03:00
|
|
|
for(;;) {
|
monitor: New format for handlers argument types
Current handlers argument types, as defined in qemu-monitor.hx file,
are a sequence of chars where each one represents one argument type
of the command handler. The number of chars is also used to know how
many arguments a given handler accepts.
This commit defines a new format, which makes mandatory the use of
a name for each argument.
For example, do_eject() command handler is currently defined as:
{ "eject", "-fB", do_eject, ... }
With the new format it becomes:
{ "eject", "force:-f,filename:B", do_eject, ... }
This way the Monitor will be capable of setting up a dictionary, using
each argument's name as the key and the argument itself as the value.
This commit also adds two new functions: key_get_info() and
next_arg_type(), both are used to parse the new format.
Currently key_get_info() consumes the 'key' part of the new format and
discards it, this way the current parsing code is not affected by this
change.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-08-28 22:27:09 +04:00
|
|
|
typestr = key_get_info(typestr, &key);
|
|
|
|
if (!typestr)
|
2004-03-15 00:38:27 +03:00
|
|
|
break;
|
monitor: New format for handlers argument types
Current handlers argument types, as defined in qemu-monitor.hx file,
are a sequence of chars where each one represents one argument type
of the command handler. The number of chars is also used to know how
many arguments a given handler accepts.
This commit defines a new format, which makes mandatory the use of
a name for each argument.
For example, do_eject() command handler is currently defined as:
{ "eject", "-fB", do_eject, ... }
With the new format it becomes:
{ "eject", "force:-f,filename:B", do_eject, ... }
This way the Monitor will be capable of setting up a dictionary, using
each argument's name as the key and the argument itself as the value.
This commit also adds two new functions: key_get_info() and
next_arg_type(), both are used to parse the new format.
Currently key_get_info() consumes the 'key' part of the new format and
discards it, this way the current parsing code is not affected by this
change.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-08-28 22:27:09 +04:00
|
|
|
c = *typestr;
|
2004-04-04 16:57:25 +04:00
|
|
|
typestr++;
|
|
|
|
switch(c) {
|
|
|
|
case 'F':
|
2004-07-14 21:21:37 +04:00
|
|
|
case 'B':
|
2004-04-04 16:57:25 +04:00
|
|
|
case 's':
|
|
|
|
{
|
|
|
|
int ret;
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isspace(*p))
|
2004-04-04 16:57:25 +04:00
|
|
|
p++;
|
|
|
|
if (*typestr == '?') {
|
|
|
|
typestr++;
|
|
|
|
if (*p == '\0') {
|
|
|
|
/* no optional string: NULL argument */
|
2009-08-28 22:27:25 +04:00
|
|
|
break;
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ret = get_str(buf, sizeof(buf), &p);
|
|
|
|
if (ret < 0) {
|
2004-07-14 21:21:37 +04:00
|
|
|
switch(c) {
|
|
|
|
case 'F':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%s: filename expected\n",
|
|
|
|
cmdname);
|
2004-07-14 21:21:37 +04:00
|
|
|
break;
|
|
|
|
case 'B':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%s: block device name expected\n",
|
|
|
|
cmdname);
|
2004-07-14 21:21:37 +04:00
|
|
|
break;
|
|
|
|
default:
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%s: string expected\n", cmdname);
|
2004-07-14 21:21:37 +04:00
|
|
|
break;
|
|
|
|
}
|
2004-04-04 16:57:25 +04:00
|
|
|
goto fail;
|
|
|
|
}
|
2009-08-28 22:27:25 +04:00
|
|
|
qdict_put(qdict, key, qstring_from_str(buf));
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
2004-03-15 00:38:27 +03:00
|
|
|
break;
|
2010-02-10 22:24:35 +03:00
|
|
|
case 'O':
|
|
|
|
{
|
|
|
|
QemuOptsList *opts_list;
|
|
|
|
QemuOpts *opts;
|
|
|
|
|
|
|
|
opts_list = qemu_find_opts(key);
|
|
|
|
if (!opts_list || opts_list->desc->name) {
|
|
|
|
goto bad_type;
|
|
|
|
}
|
|
|
|
while (qemu_isspace(*p)) {
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
if (!*p)
|
|
|
|
break;
|
|
|
|
if (get_str(buf, sizeof(buf), &p) < 0) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
opts = qemu_opts_parse(opts_list, buf, 1);
|
|
|
|
if (!opts) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
qemu_opts_to_qdict(opts, qdict);
|
|
|
|
qemu_opts_del(opts);
|
|
|
|
}
|
|
|
|
break;
|
2004-04-04 16:57:25 +04:00
|
|
|
case '/':
|
|
|
|
{
|
|
|
|
int count, format, size;
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isspace(*p))
|
2004-04-04 16:57:25 +04:00
|
|
|
p++;
|
|
|
|
if (*p == '/') {
|
|
|
|
/* format found */
|
|
|
|
p++;
|
|
|
|
count = 1;
|
2008-11-16 16:53:32 +03:00
|
|
|
if (qemu_isdigit(*p)) {
|
2004-04-04 16:57:25 +04:00
|
|
|
count = 0;
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isdigit(*p)) {
|
2004-04-04 16:57:25 +04:00
|
|
|
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:
|
2008-11-16 16:53:32 +03:00
|
|
|
if (*p != '\0' && !qemu_isspace(*p)) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "invalid char in format: '%c'\n",
|
|
|
|
*p);
|
2004-04-04 16:57:25 +04:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
if (format < 0)
|
|
|
|
format = default_fmt_format;
|
2004-04-25 22:05:08 +04:00
|
|
|
if (format != 'i') {
|
|
|
|
/* for 'i', not specifying a size gives -1 as size */
|
|
|
|
if (size < 0)
|
|
|
|
size = default_fmt_size;
|
2008-10-02 01:45:51 +04:00
|
|
|
default_fmt_size = size;
|
2004-04-25 22:05:08 +04:00
|
|
|
}
|
2004-04-04 16:57:25 +04:00
|
|
|
default_fmt_format = format;
|
|
|
|
} else {
|
|
|
|
count = 1;
|
|
|
|
format = default_fmt_format;
|
2004-04-25 22:05:08 +04:00
|
|
|
if (format != 'i') {
|
|
|
|
size = default_fmt_size;
|
|
|
|
} else {
|
|
|
|
size = -1;
|
|
|
|
}
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
2009-08-28 22:27:10 +04:00
|
|
|
qdict_put(qdict, "count", qint_from_int(count));
|
|
|
|
qdict_put(qdict, "format", qint_from_int(format));
|
|
|
|
qdict_put(qdict, "size", qint_from_int(size));
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
2004-03-15 00:38:27 +03:00
|
|
|
break;
|
2004-04-04 16:57:25 +04:00
|
|
|
case 'i':
|
2005-02-11 01:00:52 +03:00
|
|
|
case 'l':
|
2009-12-18 18:25:04 +03:00
|
|
|
case 'M':
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
2007-09-25 21:28:42 +04:00
|
|
|
int64_t val;
|
2007-09-24 22:39:04 +04:00
|
|
|
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isspace(*p))
|
2004-04-04 16:57:25 +04:00
|
|
|
p++;
|
2004-06-08 04:55:58 +04:00
|
|
|
if (*typestr == '?' || *typestr == '.') {
|
|
|
|
if (*typestr == '?') {
|
2009-08-28 22:27:25 +04:00
|
|
|
if (*p == '\0') {
|
|
|
|
typestr++;
|
|
|
|
break;
|
|
|
|
}
|
2004-06-08 04:55:58 +04:00
|
|
|
} else {
|
|
|
|
if (*p == '.') {
|
|
|
|
p++;
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isspace(*p))
|
2004-06-08 04:55:58 +04:00
|
|
|
p++;
|
|
|
|
} else {
|
2009-08-28 22:27:25 +04:00
|
|
|
typestr++;
|
|
|
|
break;
|
2004-06-08 04:55:58 +04:00
|
|
|
}
|
|
|
|
}
|
2006-07-15 02:03:35 +04:00
|
|
|
typestr++;
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
2009-03-06 02:01:23 +03:00
|
|
|
if (get_expr(mon, &val, &p))
|
2004-04-04 16:57:25 +04:00
|
|
|
goto fail;
|
2009-08-28 22:27:26 +04:00
|
|
|
/* Check if 'i' is greater than 32-bit */
|
|
|
|
if ((c == 'i') && ((val >> 32) & 0xffffffff)) {
|
|
|
|
monitor_printf(mon, "\'%s\' has failed: ", cmdname);
|
|
|
|
monitor_printf(mon, "integer is for 32-bit values\n");
|
|
|
|
goto fail;
|
2009-12-18 18:25:04 +03:00
|
|
|
} else if (c == 'M') {
|
2012-04-27 00:34:30 +04:00
|
|
|
if (val < 0) {
|
|
|
|
monitor_printf(mon, "enter a positive value\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
2009-12-18 18:25:04 +03:00
|
|
|
val <<= 20;
|
2009-08-28 22:27:26 +04:00
|
|
|
}
|
2009-08-28 22:27:25 +04:00
|
|
|
qdict_put(qdict, key, qint_from_int(val));
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
|
|
|
break;
|
2010-10-21 19:15:47 +04:00
|
|
|
case 'o':
|
|
|
|
{
|
2011-01-05 13:41:02 +03:00
|
|
|
int64_t val;
|
2010-10-21 19:15:47 +04:00
|
|
|
char *end;
|
|
|
|
|
|
|
|
while (qemu_isspace(*p)) {
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
if (*typestr == '?') {
|
|
|
|
typestr++;
|
|
|
|
if (*p == '\0') {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
val = strtosz(p, &end);
|
|
|
|
if (val < 0) {
|
|
|
|
monitor_printf(mon, "invalid size\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
qdict_put(qdict, key, qint_from_int(val));
|
|
|
|
p = end;
|
|
|
|
}
|
|
|
|
break;
|
2010-01-25 16:23:06 +03:00
|
|
|
case 'T':
|
2010-01-25 16:23:03 +03:00
|
|
|
{
|
|
|
|
double val;
|
|
|
|
|
|
|
|
while (qemu_isspace(*p))
|
|
|
|
p++;
|
|
|
|
if (*typestr == '?') {
|
|
|
|
typestr++;
|
|
|
|
if (*p == '\0') {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (get_double(mon, &val, &p) < 0) {
|
|
|
|
goto fail;
|
|
|
|
}
|
2010-10-21 19:15:49 +04:00
|
|
|
if (p[0] && p[1] == 's') {
|
2010-01-25 16:23:06 +03:00
|
|
|
switch (*p) {
|
|
|
|
case 'm':
|
|
|
|
val /= 1e3; p += 2; break;
|
|
|
|
case 'u':
|
|
|
|
val /= 1e6; p += 2; break;
|
|
|
|
case 'n':
|
|
|
|
val /= 1e9; p += 2; break;
|
|
|
|
}
|
|
|
|
}
|
2010-01-25 16:23:03 +03:00
|
|
|
if (*p && !qemu_isspace(*p)) {
|
|
|
|
monitor_printf(mon, "Unknown unit suffix\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
qdict_put(qdict, key, qfloat_from_double(val));
|
|
|
|
}
|
|
|
|
break;
|
2010-03-26 11:07:09 +03:00
|
|
|
case 'b':
|
|
|
|
{
|
|
|
|
const char *beg;
|
|
|
|
int val;
|
|
|
|
|
|
|
|
while (qemu_isspace(*p)) {
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
beg = p;
|
|
|
|
while (qemu_isgraph(*p)) {
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
if (p - beg == 2 && !memcmp(beg, "on", p - beg)) {
|
|
|
|
val = 1;
|
|
|
|
} else if (p - beg == 3 && !memcmp(beg, "off", p - beg)) {
|
|
|
|
val = 0;
|
|
|
|
} else {
|
|
|
|
monitor_printf(mon, "Expected 'on' or 'off'\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
qdict_put(qdict, key, qbool_from_int(val));
|
|
|
|
}
|
|
|
|
break;
|
2004-04-04 16:57:25 +04:00
|
|
|
case '-':
|
|
|
|
{
|
2009-11-02 16:41:13 +03:00
|
|
|
const char *tmp = p;
|
Monitor: handle optional '-' arg as a bool
Historically, user monitor arguments beginning with '-' (eg. '-f')
were passed as integers down to handlers.
I've maintained this behavior in the new monitor because we didn't
have a boolean type at the very beginning of QMP. Today we have it
and this behavior is causing trouble to QMP's argument checker.
This commit fixes the problem by doing the following changes:
1. User Monitor
Before: the optional arg was represented as a QInt, we'd pass 1
down to handlers if the user specified the argument or
0 otherwise
This commit: the optional arg is represented as a QBool, we pass
true down to handlers if the user specified the
argument, otherwise _nothing_ is passed
2. QMP
Before: the client was required to pass the arg as QBool, but we'd
convert it to QInt internally. If the argument wasn't passed,
we'd pass 0 down
This commit: still require a QBool, but doesn't do any conversion and
doesn't pass any default value
3. Convert existing handlers (do_eject()/do_migrate()) to the new way
Before: Both handlers would expect a QInt value, either 0 or 1
This commit: Change the handlers to accept a QBool, they handle the
following cases:
A) true is passed: the option is enabled
B) false is passed: the option is disabled
C) nothing is passed: option not specified, use
default behavior
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2010-05-28 22:25:24 +04:00
|
|
|
int skip_key = 0;
|
2004-04-04 16:57:25 +04:00
|
|
|
/* option */
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2004-04-04 16:57:25 +04:00
|
|
|
c = *typestr++;
|
|
|
|
if (c == '\0')
|
|
|
|
goto bad_type;
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isspace(*p))
|
2004-04-04 16:57:25 +04:00
|
|
|
p++;
|
|
|
|
if (*p == '-') {
|
|
|
|
p++;
|
2009-11-02 16:41:13 +03:00
|
|
|
if(c != *p) {
|
|
|
|
if(!is_valid_option(p, typestr)) {
|
|
|
|
|
|
|
|
monitor_printf(mon, "%s: unsupported option -%c\n",
|
|
|
|
cmdname, *p);
|
|
|
|
goto fail;
|
|
|
|
} else {
|
|
|
|
skip_key = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(skip_key) {
|
|
|
|
p = tmp;
|
|
|
|
} else {
|
Monitor: handle optional '-' arg as a bool
Historically, user monitor arguments beginning with '-' (eg. '-f')
were passed as integers down to handlers.
I've maintained this behavior in the new monitor because we didn't
have a boolean type at the very beginning of QMP. Today we have it
and this behavior is causing trouble to QMP's argument checker.
This commit fixes the problem by doing the following changes:
1. User Monitor
Before: the optional arg was represented as a QInt, we'd pass 1
down to handlers if the user specified the argument or
0 otherwise
This commit: the optional arg is represented as a QBool, we pass
true down to handlers if the user specified the
argument, otherwise _nothing_ is passed
2. QMP
Before: the client was required to pass the arg as QBool, but we'd
convert it to QInt internally. If the argument wasn't passed,
we'd pass 0 down
This commit: still require a QBool, but doesn't do any conversion and
doesn't pass any default value
3. Convert existing handlers (do_eject()/do_migrate()) to the new way
Before: Both handlers would expect a QInt value, either 0 or 1
This commit: Change the handlers to accept a QBool, they handle the
following cases:
A) true is passed: the option is enabled
B) false is passed: the option is disabled
C) nothing is passed: option not specified, use
default behavior
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2010-05-28 22:25:24 +04:00
|
|
|
/* has option */
|
2009-11-02 16:41:13 +03:00
|
|
|
p++;
|
Monitor: handle optional '-' arg as a bool
Historically, user monitor arguments beginning with '-' (eg. '-f')
were passed as integers down to handlers.
I've maintained this behavior in the new monitor because we didn't
have a boolean type at the very beginning of QMP. Today we have it
and this behavior is causing trouble to QMP's argument checker.
This commit fixes the problem by doing the following changes:
1. User Monitor
Before: the optional arg was represented as a QInt, we'd pass 1
down to handlers if the user specified the argument or
0 otherwise
This commit: the optional arg is represented as a QBool, we pass
true down to handlers if the user specified the
argument, otherwise _nothing_ is passed
2. QMP
Before: the client was required to pass the arg as QBool, but we'd
convert it to QInt internally. If the argument wasn't passed,
we'd pass 0 down
This commit: still require a QBool, but doesn't do any conversion and
doesn't pass any default value
3. Convert existing handlers (do_eject()/do_migrate()) to the new way
Before: Both handlers would expect a QInt value, either 0 or 1
This commit: Change the handlers to accept a QBool, they handle the
following cases:
A) true is passed: the option is enabled
B) false is passed: the option is disabled
C) nothing is passed: option not specified, use
default behavior
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2010-05-28 22:25:24 +04:00
|
|
|
qdict_put(qdict, key, qbool_from_int(1));
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
bad_type:
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%s: unknown type '%c'\n", cmdname, c);
|
2004-04-04 16:57:25 +04:00
|
|
|
goto fail;
|
|
|
|
}
|
2011-08-21 07:09:37 +04:00
|
|
|
g_free(key);
|
monitor: New format for handlers argument types
Current handlers argument types, as defined in qemu-monitor.hx file,
are a sequence of chars where each one represents one argument type
of the command handler. The number of chars is also used to know how
many arguments a given handler accepts.
This commit defines a new format, which makes mandatory the use of
a name for each argument.
For example, do_eject() command handler is currently defined as:
{ "eject", "-fB", do_eject, ... }
With the new format it becomes:
{ "eject", "force:-f,filename:B", do_eject, ... }
This way the Monitor will be capable of setting up a dictionary, using
each argument's name as the key and the argument itself as the value.
This commit also adds two new functions: key_get_info() and
next_arg_type(), both are used to parse the new format.
Currently key_get_info() consumes the 'key' part of the new format and
discards it, this way the current parsing code is not affected by this
change.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-08-28 22:27:09 +04:00
|
|
|
key = NULL;
|
2004-03-15 00:38:27 +03:00
|
|
|
}
|
2004-04-04 16:57:25 +04:00
|
|
|
/* check that all arguments were parsed */
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isspace(*p))
|
2004-04-04 16:57:25 +04:00
|
|
|
p++;
|
|
|
|
if (*p != '\0') {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%s: extraneous characters at the end of line\n",
|
|
|
|
cmdname);
|
2004-04-04 16:57:25 +04:00
|
|
|
goto fail;
|
2004-03-15 00:38:27 +03:00
|
|
|
}
|
2004-04-04 16:57:25 +04:00
|
|
|
|
2009-08-28 22:27:22 +04:00
|
|
|
return cmd;
|
2009-08-14 12:36:06 +04:00
|
|
|
|
2009-08-28 22:27:22 +04:00
|
|
|
fail:
|
2011-08-21 07:09:37 +04:00
|
|
|
g_free(key);
|
2009-08-28 22:27:22 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-02-17 12:52:26 +03:00
|
|
|
void monitor_set_error(Monitor *mon, QError *qerror)
|
|
|
|
{
|
|
|
|
/* report only the first error */
|
|
|
|
if (!mon->error) {
|
|
|
|
mon->error = qerror;
|
|
|
|
} else {
|
|
|
|
QDECREF(qerror);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-11 04:50:05 +03:00
|
|
|
static void handler_audit(Monitor *mon, const mon_cmd_t *cmd, int ret)
|
|
|
|
{
|
2010-11-22 21:35:09 +03:00
|
|
|
if (ret && !monitor_has_error(mon)) {
|
|
|
|
/*
|
|
|
|
* If it returns failure, it must have passed on error.
|
|
|
|
*
|
|
|
|
* Action: Report an internal error to the client if in QMP.
|
|
|
|
*/
|
|
|
|
qerror_report(QERR_UNDEFINED_ERROR);
|
|
|
|
}
|
2010-02-11 04:50:05 +03:00
|
|
|
}
|
|
|
|
|
2009-11-27 03:58:55 +03:00
|
|
|
static void handle_user_command(Monitor *mon, const char *cmdline)
|
2009-08-28 22:27:22 +04:00
|
|
|
{
|
|
|
|
QDict *qdict;
|
2009-10-02 01:12:16 +04:00
|
|
|
const mon_cmd_t *cmd;
|
2009-08-28 22:27:22 +04:00
|
|
|
|
|
|
|
qdict = qdict_new();
|
|
|
|
|
2009-08-28 22:27:24 +04:00
|
|
|
cmd = monitor_parse_command(mon, cmdline, qdict);
|
2009-10-07 20:41:54 +04:00
|
|
|
if (!cmd)
|
|
|
|
goto out;
|
|
|
|
|
2010-09-16 18:01:32 +04:00
|
|
|
if (handler_is_async(cmd)) {
|
2010-01-25 21:18:44 +03:00
|
|
|
user_async_cmd_handler(mon, cmd, qdict);
|
2010-09-16 17:58:59 +04:00
|
|
|
} else if (handler_is_qobject(cmd)) {
|
2010-09-16 18:06:11 +04:00
|
|
|
QObject *data = NULL;
|
|
|
|
|
|
|
|
/* XXX: ignores the error code */
|
|
|
|
cmd->mhandler.cmd_new(mon, qdict, &data);
|
|
|
|
assert(!monitor_has_error(mon));
|
|
|
|
if (data) {
|
|
|
|
cmd->user_print(mon, data);
|
|
|
|
qobject_decref(data);
|
|
|
|
}
|
2009-10-07 20:41:54 +04:00
|
|
|
} else {
|
2009-10-07 20:41:52 +04:00
|
|
|
cmd->mhandler.cmd(mon, qdict);
|
2009-08-28 22:27:22 +04:00
|
|
|
}
|
|
|
|
|
2009-10-07 20:41:54 +04:00
|
|
|
out:
|
2009-08-28 22:27:10 +04:00
|
|
|
QDECREF(qdict);
|
2004-03-15 00:38:27 +03:00
|
|
|
}
|
|
|
|
|
2004-07-14 21:21:37 +04:00
|
|
|
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))) {
|
2009-03-06 02:01:42 +03:00
|
|
|
readline_add_completion(cur_mon->rs, cmd);
|
2004-07-14 21:21:37 +04:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
|
2007-09-17 01:08:06 +04:00
|
|
|
p = strrchr(input, '/');
|
2004-07-14 21:21:37 +04:00
|
|
|
if (!p) {
|
|
|
|
input_path_len = 0;
|
|
|
|
pstrcpy(file_prefix, sizeof(file_prefix), input);
|
2008-08-21 21:58:08 +04:00
|
|
|
pstrcpy(path, sizeof(path), ".");
|
2004-07-14 21:21:37 +04:00
|
|
|
} 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
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(cur_mon, "input='%s' path='%s' prefix='%s'\n",
|
|
|
|
input, path, file_prefix);
|
2004-07-14 21:21:37 +04:00
|
|
|
#endif
|
|
|
|
ffs = opendir(path);
|
|
|
|
if (!ffs)
|
|
|
|
return;
|
|
|
|
for(;;) {
|
|
|
|
struct stat sb;
|
|
|
|
d = readdir(ffs);
|
|
|
|
if (!d)
|
|
|
|
break;
|
2010-10-20 13:00:01 +04:00
|
|
|
|
|
|
|
if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2004-07-14 21:21:37 +04:00
|
|
|
if (strstart(d->d_name, file_prefix, NULL)) {
|
|
|
|
memcpy(file, input, input_path_len);
|
2008-08-21 21:58:08 +04:00
|
|
|
if (input_path_len < sizeof(file))
|
|
|
|
pstrcpy(file + input_path_len, sizeof(file) - input_path_len,
|
|
|
|
d->d_name);
|
2004-07-14 21:21:37 +04:00
|
|
|
/* stat the file to find out if it's a directory.
|
|
|
|
* In that case add a slash to speed up typing long paths
|
|
|
|
*/
|
2011-11-16 18:43:47 +04:00
|
|
|
if (stat(file, &sb) == 0 && S_ISDIR(sb.st_mode)) {
|
2008-08-21 21:58:08 +04:00
|
|
|
pstrcat(file, sizeof(file), "/");
|
2011-11-16 18:43:47 +04:00
|
|
|
}
|
2009-03-06 02:01:42 +03:00
|
|
|
readline_add_completion(cur_mon->rs, file);
|
2004-07-14 21:21:37 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
closedir(ffs);
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:00:43 +03:00
|
|
|
static void block_completion_it(void *opaque, BlockDriverState *bs)
|
2004-07-14 21:21:37 +04:00
|
|
|
{
|
2009-03-06 02:00:43 +03:00
|
|
|
const char *name = bdrv_get_device_name(bs);
|
2004-07-14 21:21:37 +04:00
|
|
|
const char *input = opaque;
|
|
|
|
|
|
|
|
if (input[0] == '\0' ||
|
|
|
|
!strncmp(name, (char *)input, strlen(input))) {
|
2009-03-06 02:01:42 +03:00
|
|
|
readline_add_completion(cur_mon->rs, name);
|
2004-07-14 21:21:37 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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(;;) {
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isspace(*p))
|
2004-07-14 21:21:37 +04:00
|
|
|
p++;
|
|
|
|
if (*p == '\0')
|
|
|
|
break;
|
|
|
|
if (nb_args >= MAX_ARGS)
|
|
|
|
break;
|
|
|
|
ret = get_str(buf, sizeof(buf), &p);
|
2011-08-21 07:09:37 +04:00
|
|
|
args[nb_args] = g_strdup(buf);
|
2004-07-14 21:21:37 +04:00
|
|
|
nb_args++;
|
|
|
|
if (ret < 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
*pnb_args = nb_args;
|
|
|
|
}
|
|
|
|
|
monitor: New format for handlers argument types
Current handlers argument types, as defined in qemu-monitor.hx file,
are a sequence of chars where each one represents one argument type
of the command handler. The number of chars is also used to know how
many arguments a given handler accepts.
This commit defines a new format, which makes mandatory the use of
a name for each argument.
For example, do_eject() command handler is currently defined as:
{ "eject", "-fB", do_eject, ... }
With the new format it becomes:
{ "eject", "force:-f,filename:B", do_eject, ... }
This way the Monitor will be capable of setting up a dictionary, using
each argument's name as the key and the argument itself as the value.
This commit also adds two new functions: key_get_info() and
next_arg_type(), both are used to parse the new format.
Currently key_get_info() consumes the 'key' part of the new format and
discards it, this way the current parsing code is not affected by this
change.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-08-28 22:27:09 +04:00
|
|
|
static const char *next_arg_type(const char *typestr)
|
|
|
|
{
|
|
|
|
const char *p = strchr(typestr, ':');
|
|
|
|
return (p != NULL ? ++p : typestr);
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:37 +03:00
|
|
|
static void monitor_find_completion(const char *cmdline)
|
2004-07-14 21:21:37 +04:00
|
|
|
{
|
|
|
|
const char *cmdname;
|
|
|
|
char *args[MAX_ARGS];
|
|
|
|
int nb_args, i, len;
|
|
|
|
const char *ptype, *str;
|
2009-10-02 01:12:16 +04:00
|
|
|
const mon_cmd_t *cmd;
|
2004-07-14 21:21:37 +04:00
|
|
|
|
|
|
|
parse_cmdline(cmdline, &nb_args, args);
|
|
|
|
#ifdef DEBUG_COMPLETION
|
|
|
|
for(i = 0; i < nb_args; i++) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(cur_mon, "arg%d = '%s'\n", i, (char *)args[i]);
|
2004-07-14 21:21:37 +04:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* if the line ends with a space, it means we want to complete the
|
|
|
|
next arg */
|
|
|
|
len = strlen(cmdline);
|
2008-11-16 16:53:32 +03:00
|
|
|
if (len > 0 && qemu_isspace(cmdline[len - 1])) {
|
2010-06-16 02:38:33 +04:00
|
|
|
if (nb_args >= MAX_ARGS) {
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2011-08-21 07:09:37 +04:00
|
|
|
args[nb_args++] = g_strdup("");
|
2004-07-14 21:21:37 +04:00
|
|
|
}
|
|
|
|
if (nb_args <= 1) {
|
|
|
|
/* command completion */
|
|
|
|
if (nb_args == 0)
|
|
|
|
cmdname = "";
|
|
|
|
else
|
|
|
|
cmdname = args[0];
|
2009-03-06 02:01:42 +03:00
|
|
|
readline_set_completion_index(cur_mon->rs, strlen(cmdname));
|
2009-03-06 02:01:23 +03:00
|
|
|
for(cmd = mon_cmds; cmd->name != NULL; cmd++) {
|
2004-07-14 21:21:37 +04:00
|
|
|
cmd_completion(cmdname, cmd->name);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* find the command */
|
2010-06-16 02:38:33 +04:00
|
|
|
for (cmd = mon_cmds; cmd->name != NULL; cmd++) {
|
|
|
|
if (compare_cmd(args[0], cmd->name)) {
|
|
|
|
break;
|
|
|
|
}
|
2004-07-14 21:21:37 +04:00
|
|
|
}
|
2010-06-16 02:38:33 +04:00
|
|
|
if (!cmd->name) {
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
monitor: New format for handlers argument types
Current handlers argument types, as defined in qemu-monitor.hx file,
are a sequence of chars where each one represents one argument type
of the command handler. The number of chars is also used to know how
many arguments a given handler accepts.
This commit defines a new format, which makes mandatory the use of
a name for each argument.
For example, do_eject() command handler is currently defined as:
{ "eject", "-fB", do_eject, ... }
With the new format it becomes:
{ "eject", "force:-f,filename:B", do_eject, ... }
This way the Monitor will be capable of setting up a dictionary, using
each argument's name as the key and the argument itself as the value.
This commit also adds two new functions: key_get_info() and
next_arg_type(), both are used to parse the new format.
Currently key_get_info() consumes the 'key' part of the new format and
discards it, this way the current parsing code is not affected by this
change.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-08-28 22:27:09 +04:00
|
|
|
ptype = next_arg_type(cmd->args_type);
|
2004-07-14 21:21:37 +04:00
|
|
|
for(i = 0; i < nb_args - 2; i++) {
|
|
|
|
if (*ptype != '\0') {
|
monitor: New format for handlers argument types
Current handlers argument types, as defined in qemu-monitor.hx file,
are a sequence of chars where each one represents one argument type
of the command handler. The number of chars is also used to know how
many arguments a given handler accepts.
This commit defines a new format, which makes mandatory the use of
a name for each argument.
For example, do_eject() command handler is currently defined as:
{ "eject", "-fB", do_eject, ... }
With the new format it becomes:
{ "eject", "force:-f,filename:B", do_eject, ... }
This way the Monitor will be capable of setting up a dictionary, using
each argument's name as the key and the argument itself as the value.
This commit also adds two new functions: key_get_info() and
next_arg_type(), both are used to parse the new format.
Currently key_get_info() consumes the 'key' part of the new format and
discards it, this way the current parsing code is not affected by this
change.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-08-28 22:27:09 +04:00
|
|
|
ptype = next_arg_type(ptype);
|
2004-07-14 21:21:37 +04:00
|
|
|
while (*ptype == '?')
|
monitor: New format for handlers argument types
Current handlers argument types, as defined in qemu-monitor.hx file,
are a sequence of chars where each one represents one argument type
of the command handler. The number of chars is also used to know how
many arguments a given handler accepts.
This commit defines a new format, which makes mandatory the use of
a name for each argument.
For example, do_eject() command handler is currently defined as:
{ "eject", "-fB", do_eject, ... }
With the new format it becomes:
{ "eject", "force:-f,filename:B", do_eject, ... }
This way the Monitor will be capable of setting up a dictionary, using
each argument's name as the key and the argument itself as the value.
This commit also adds two new functions: key_get_info() and
next_arg_type(), both are used to parse the new format.
Currently key_get_info() consumes the 'key' part of the new format and
discards it, this way the current parsing code is not affected by this
change.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-08-28 22:27:09 +04:00
|
|
|
ptype = next_arg_type(ptype);
|
2004-07-14 21:21:37 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
str = args[nb_args - 1];
|
2009-08-24 00:10:28 +04:00
|
|
|
if (*ptype == '-' && ptype[1] != '\0') {
|
2010-06-16 02:38:34 +04:00
|
|
|
ptype = next_arg_type(ptype);
|
2009-08-24 00:10:28 +04:00
|
|
|
}
|
2004-07-14 21:21:37 +04:00
|
|
|
switch(*ptype) {
|
|
|
|
case 'F':
|
|
|
|
/* file completion */
|
2009-03-06 02:01:42 +03:00
|
|
|
readline_set_completion_index(cur_mon->rs, strlen(str));
|
2004-07-14 21:21:37 +04:00
|
|
|
file_completion(str);
|
|
|
|
break;
|
|
|
|
case 'B':
|
|
|
|
/* block device name completion */
|
2009-03-06 02:01:42 +03:00
|
|
|
readline_set_completion_index(cur_mon->rs, strlen(str));
|
2004-07-14 21:21:37 +04:00
|
|
|
bdrv_iterate(block_completion_it, (void *)str);
|
|
|
|
break;
|
2004-10-09 22:08:01 +04:00
|
|
|
case 's':
|
|
|
|
/* XXX: more generic ? */
|
|
|
|
if (!strcmp(cmd->name, "info")) {
|
2009-03-06 02:01:42 +03:00
|
|
|
readline_set_completion_index(cur_mon->rs, strlen(str));
|
2004-10-09 22:08:01 +04:00
|
|
|
for(cmd = info_cmds; cmd->name != NULL; cmd++) {
|
|
|
|
cmd_completion(str, cmd->name);
|
|
|
|
}
|
2006-05-07 22:03:31 +04:00
|
|
|
} else if (!strcmp(cmd->name, "sendkey")) {
|
2009-03-08 20:42:02 +03:00
|
|
|
char *sep = strrchr(str, '-');
|
|
|
|
if (sep)
|
|
|
|
str = sep + 1;
|
2009-03-06 02:01:42 +03:00
|
|
|
readline_set_completion_index(cur_mon->rs, strlen(str));
|
2012-08-31 06:56:25 +04:00
|
|
|
for (i = 0; i < Q_KEY_CODE_MAX; i++) {
|
|
|
|
cmd_completion(str, QKeyCode_lookup[i]);
|
2006-05-07 22:03:31 +04:00
|
|
|
}
|
2009-06-25 10:22:02 +04:00
|
|
|
} else if (!strcmp(cmd->name, "help|?")) {
|
|
|
|
readline_set_completion_index(cur_mon->rs, strlen(str));
|
|
|
|
for (cmd = mon_cmds; cmd->name != NULL; cmd++) {
|
|
|
|
cmd_completion(str, cmd->name);
|
|
|
|
}
|
2004-10-09 22:08:01 +04:00
|
|
|
}
|
|
|
|
break;
|
2004-07-14 21:21:37 +04:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2010-06-16 02:38:33 +04:00
|
|
|
|
|
|
|
cleanup:
|
|
|
|
for (i = 0; i < nb_args; i++) {
|
2011-08-21 07:09:37 +04:00
|
|
|
g_free(args[i]);
|
2010-06-16 02:38:33 +04:00
|
|
|
}
|
2004-07-14 21:21:37 +04:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:42 +03:00
|
|
|
static int monitor_can_read(void *opaque)
|
2004-03-15 00:38:27 +03:00
|
|
|
{
|
2009-03-06 02:01:42 +03:00
|
|
|
Monitor *mon = opaque;
|
|
|
|
|
2009-12-04 16:05:29 +03:00
|
|
|
return (mon->suspend_cnt == 0) ? 1 : 0;
|
2004-03-15 00:38:27 +03:00
|
|
|
}
|
|
|
|
|
2010-02-04 23:10:06 +03:00
|
|
|
static int invalid_qmp_mode(const Monitor *mon, const char *cmd_name)
|
2009-11-27 03:59:01 +03:00
|
|
|
{
|
2010-02-04 23:10:06 +03:00
|
|
|
int is_cap = compare_cmd(cmd_name, "qmp_capabilities");
|
|
|
|
return (qmp_cmd_mode(mon) ? is_cap : !is_cap);
|
2009-11-27 03:59:01 +03:00
|
|
|
}
|
|
|
|
|
2010-06-22 18:44:05 +04:00
|
|
|
/*
|
|
|
|
* Argument validation rules:
|
|
|
|
*
|
|
|
|
* 1. The argument must exist in cmd_args qdict
|
|
|
|
* 2. The argument type must be the expected one
|
|
|
|
*
|
|
|
|
* Special case: If the argument doesn't exist in cmd_args and
|
|
|
|
* the QMP_ACCEPT_UNKNOWNS flag is set, then the
|
|
|
|
* checking is skipped for it.
|
|
|
|
*/
|
|
|
|
static int check_client_args_type(const QDict *client_args,
|
|
|
|
const QDict *cmd_args, int flags)
|
2009-11-27 03:59:01 +03:00
|
|
|
{
|
2010-06-22 18:44:05 +04:00
|
|
|
const QDictEntry *ent;
|
2009-11-27 03:59:01 +03:00
|
|
|
|
2010-06-22 18:44:05 +04:00
|
|
|
for (ent = qdict_first(client_args); ent;ent = qdict_next(client_args,ent)){
|
|
|
|
QObject *obj;
|
|
|
|
QString *arg_type;
|
|
|
|
const QObject *client_arg = qdict_entry_value(ent);
|
|
|
|
const char *client_arg_name = qdict_entry_key(ent);
|
|
|
|
|
|
|
|
obj = qdict_get(cmd_args, client_arg_name);
|
|
|
|
if (!obj) {
|
|
|
|
if (flags & QMP_ACCEPT_UNKNOWNS) {
|
|
|
|
/* handler accepts unknowns */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* client arg doesn't exist */
|
|
|
|
qerror_report(QERR_INVALID_PARAMETER, client_arg_name);
|
|
|
|
return -1;
|
|
|
|
}
|
2009-11-27 03:59:01 +03:00
|
|
|
|
2010-06-22 18:44:05 +04:00
|
|
|
arg_type = qobject_to_qstring(obj);
|
|
|
|
assert(arg_type != NULL);
|
2009-11-27 03:59:01 +03:00
|
|
|
|
2010-06-22 18:44:05 +04:00
|
|
|
/* check if argument's type is correct */
|
|
|
|
switch (qstring_get_str(arg_type)[0]) {
|
2009-11-27 03:59:01 +03:00
|
|
|
case 'F':
|
|
|
|
case 'B':
|
|
|
|
case 's':
|
2010-06-22 18:44:05 +04:00
|
|
|
if (qobject_type(client_arg) != QTYPE_QSTRING) {
|
|
|
|
qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name,
|
|
|
|
"string");
|
2009-11-27 03:59:01 +03:00
|
|
|
return -1;
|
|
|
|
}
|
2010-06-22 18:44:05 +04:00
|
|
|
break;
|
2009-11-27 03:59:01 +03:00
|
|
|
case 'i':
|
|
|
|
case 'l':
|
2009-12-18 18:25:04 +03:00
|
|
|
case 'M':
|
2010-10-21 19:15:47 +04:00
|
|
|
case 'o':
|
2010-06-22 18:44:05 +04:00
|
|
|
if (qobject_type(client_arg) != QTYPE_QINT) {
|
|
|
|
qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name,
|
|
|
|
"int");
|
|
|
|
return -1;
|
2009-11-27 03:59:01 +03:00
|
|
|
}
|
|
|
|
break;
|
2010-01-25 16:23:06 +03:00
|
|
|
case 'T':
|
2010-06-22 18:44:05 +04:00
|
|
|
if (qobject_type(client_arg) != QTYPE_QINT &&
|
|
|
|
qobject_type(client_arg) != QTYPE_QFLOAT) {
|
|
|
|
qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name,
|
|
|
|
"number");
|
|
|
|
return -1;
|
2010-01-25 16:23:03 +03:00
|
|
|
}
|
|
|
|
break;
|
2010-03-26 11:07:09 +03:00
|
|
|
case 'b':
|
2009-11-27 03:59:01 +03:00
|
|
|
case '-':
|
2010-06-22 18:44:05 +04:00
|
|
|
if (qobject_type(client_arg) != QTYPE_QBOOL) {
|
|
|
|
qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name,
|
|
|
|
"bool");
|
|
|
|
return -1;
|
2009-11-27 03:59:01 +03:00
|
|
|
}
|
|
|
|
break;
|
2010-02-10 22:24:35 +03:00
|
|
|
case 'O':
|
2010-06-22 18:44:05 +04:00
|
|
|
assert(flags & QMP_ACCEPT_UNKNOWNS);
|
|
|
|
break;
|
2012-03-22 15:51:11 +04:00
|
|
|
case 'q':
|
|
|
|
/* Any QObject can be passed. */
|
|
|
|
break;
|
2010-06-22 18:44:05 +04:00
|
|
|
case '/':
|
|
|
|
case '.':
|
|
|
|
/*
|
|
|
|
* These types are not supported by QMP and thus are not
|
|
|
|
* handled here. Fall through.
|
|
|
|
*/
|
2009-11-27 03:59:01 +03:00
|
|
|
default:
|
|
|
|
abort();
|
2010-06-22 18:44:05 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-05-26 23:13:09 +04:00
|
|
|
/*
|
|
|
|
* - Check if the client has passed all mandatory args
|
|
|
|
* - Set special flags for argument validation
|
|
|
|
*/
|
|
|
|
static int check_mandatory_args(const QDict *cmd_args,
|
|
|
|
const QDict *client_args, int *flags)
|
|
|
|
{
|
|
|
|
const QDictEntry *ent;
|
|
|
|
|
|
|
|
for (ent = qdict_first(cmd_args); ent; ent = qdict_next(cmd_args, ent)) {
|
|
|
|
const char *cmd_arg_name = qdict_entry_key(ent);
|
|
|
|
QString *type = qobject_to_qstring(qdict_entry_value(ent));
|
|
|
|
assert(type != NULL);
|
|
|
|
|
|
|
|
if (qstring_get_str(type)[0] == 'O') {
|
|
|
|
assert((*flags & QMP_ACCEPT_UNKNOWNS) == 0);
|
|
|
|
*flags |= QMP_ACCEPT_UNKNOWNS;
|
|
|
|
} else if (qstring_get_str(type)[0] != '-' &&
|
|
|
|
qstring_get_str(type)[1] != '?' &&
|
|
|
|
!qdict_haskey(client_args, cmd_arg_name)) {
|
|
|
|
qerror_report(QERR_MISSING_PARAMETER, cmd_arg_name);
|
|
|
|
return -1;
|
|
|
|
}
|
2009-11-27 03:59:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-05-26 23:13:09 +04:00
|
|
|
static QDict *qdict_from_args_type(const char *args_type)
|
2009-11-27 03:59:01 +03:00
|
|
|
{
|
2010-05-26 23:13:09 +04:00
|
|
|
int i;
|
|
|
|
QDict *qdict;
|
|
|
|
QString *key, *type, *cur_qs;
|
|
|
|
|
|
|
|
assert(args_type != NULL);
|
|
|
|
|
|
|
|
qdict = qdict_new();
|
|
|
|
|
|
|
|
if (args_type == NULL || args_type[0] == '\0') {
|
|
|
|
/* no args, empty qdict */
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
key = qstring_new();
|
|
|
|
type = qstring_new();
|
|
|
|
|
|
|
|
cur_qs = key;
|
|
|
|
|
|
|
|
for (i = 0;; i++) {
|
|
|
|
switch (args_type[i]) {
|
|
|
|
case ',':
|
|
|
|
case '\0':
|
|
|
|
qdict_put(qdict, qstring_get_str(key), type);
|
|
|
|
QDECREF(key);
|
|
|
|
if (args_type[i] == '\0') {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
type = qstring_new(); /* qdict has ref */
|
|
|
|
cur_qs = key = qstring_new();
|
|
|
|
break;
|
|
|
|
case ':':
|
|
|
|
cur_qs = type;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
qstring_append_chr(cur_qs, args_type[i]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
return qdict;
|
2009-11-27 03:59:01 +03:00
|
|
|
}
|
|
|
|
|
2010-05-26 23:13:09 +04:00
|
|
|
/*
|
|
|
|
* Client argument checking rules:
|
|
|
|
*
|
|
|
|
* 1. Client must provide all mandatory arguments
|
2010-06-22 18:44:05 +04:00
|
|
|
* 2. Each argument provided by the client must be expected
|
|
|
|
* 3. Each argument provided by the client must have the type expected
|
|
|
|
* by the command
|
2010-05-26 23:13:09 +04:00
|
|
|
*/
|
|
|
|
static int qmp_check_client_args(const mon_cmd_t *cmd, QDict *client_args)
|
2010-02-10 22:24:35 +03:00
|
|
|
{
|
2010-05-26 23:13:09 +04:00
|
|
|
int flags, err;
|
|
|
|
QDict *cmd_args;
|
|
|
|
|
|
|
|
cmd_args = qdict_from_args_type(cmd->args_type);
|
|
|
|
|
|
|
|
flags = 0;
|
|
|
|
err = check_mandatory_args(cmd_args, client_args, &flags);
|
|
|
|
if (err) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-06-22 18:44:05 +04:00
|
|
|
err = check_client_args_type(client_args, cmd_args, flags);
|
2010-05-26 23:13:09 +04:00
|
|
|
|
|
|
|
out:
|
|
|
|
QDECREF(cmd_args);
|
|
|
|
return err;
|
2010-02-10 22:24:35 +03:00
|
|
|
}
|
|
|
|
|
2009-11-27 03:59:01 +03:00
|
|
|
/*
|
2010-06-01 00:28:01 +04:00
|
|
|
* Input object checking rules
|
2009-11-27 03:59:01 +03:00
|
|
|
*
|
2010-06-01 00:28:01 +04:00
|
|
|
* 1. Input object must be a dict
|
|
|
|
* 2. The "execute" key must exist
|
|
|
|
* 3. The "execute" key must be a string
|
|
|
|
* 4. If the "arguments" key exists, it must be a dict
|
|
|
|
* 5. If the "id" key exists, it can be anything (ie. json-value)
|
|
|
|
* 6. Any argument not listed above is considered invalid
|
2009-11-27 03:59:01 +03:00
|
|
|
*/
|
2010-06-01 00:28:01 +04:00
|
|
|
static QDict *qmp_check_input_obj(QObject *input_obj)
|
2009-11-27 03:59:01 +03:00
|
|
|
{
|
2010-06-01 00:28:01 +04:00
|
|
|
const QDictEntry *ent;
|
|
|
|
int has_exec_key = 0;
|
|
|
|
QDict *input_dict;
|
2009-11-27 03:59:01 +03:00
|
|
|
|
2010-06-01 00:28:01 +04:00
|
|
|
if (qobject_type(input_obj) != QTYPE_QDICT) {
|
|
|
|
qerror_report(QERR_QMP_BAD_INPUT_OBJECT, "object");
|
|
|
|
return NULL;
|
2009-11-27 03:59:01 +03:00
|
|
|
}
|
|
|
|
|
2010-06-01 00:28:01 +04:00
|
|
|
input_dict = qobject_to_qdict(input_obj);
|
2009-11-27 03:59:01 +03:00
|
|
|
|
2010-06-01 00:28:01 +04:00
|
|
|
for (ent = qdict_first(input_dict); ent; ent = qdict_next(input_dict, ent)){
|
|
|
|
const char *arg_name = qdict_entry_key(ent);
|
|
|
|
const QObject *arg_obj = qdict_entry_value(ent);
|
2009-11-27 03:59:01 +03:00
|
|
|
|
2010-06-01 00:28:01 +04:00
|
|
|
if (!strcmp(arg_name, "execute")) {
|
|
|
|
if (qobject_type(arg_obj) != QTYPE_QSTRING) {
|
|
|
|
qerror_report(QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "execute",
|
|
|
|
"string");
|
|
|
|
return NULL;
|
2010-02-10 22:24:35 +03:00
|
|
|
}
|
2010-06-01 00:28:01 +04:00
|
|
|
has_exec_key = 1;
|
|
|
|
} else if (!strcmp(arg_name, "arguments")) {
|
|
|
|
if (qobject_type(arg_obj) != QTYPE_QDICT) {
|
|
|
|
qerror_report(QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "arguments",
|
|
|
|
"object");
|
|
|
|
return NULL;
|
2009-11-27 03:59:01 +03:00
|
|
|
}
|
2010-06-01 00:28:01 +04:00
|
|
|
} else if (!strcmp(arg_name, "id")) {
|
|
|
|
/* FIXME: check duplicated IDs for async commands */
|
2009-11-27 03:59:01 +03:00
|
|
|
} else {
|
2010-06-01 00:28:01 +04:00
|
|
|
qerror_report(QERR_QMP_EXTRA_MEMBER, arg_name);
|
|
|
|
return NULL;
|
2009-11-27 03:59:01 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-01 00:28:01 +04:00
|
|
|
if (!has_exec_key) {
|
|
|
|
qerror_report(QERR_QMP_BAD_INPUT_OBJECT, "execute");
|
|
|
|
return NULL;
|
|
|
|
}
|
2009-11-27 03:59:01 +03:00
|
|
|
|
2010-06-01 00:28:01 +04:00
|
|
|
return input_dict;
|
2010-02-04 23:10:06 +03:00
|
|
|
}
|
|
|
|
|
2010-09-16 18:11:18 +04:00
|
|
|
static void qmp_call_cmd(Monitor *mon, const mon_cmd_t *cmd,
|
|
|
|
const QDict *params)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
QObject *data = NULL;
|
|
|
|
|
|
|
|
ret = cmd->mhandler.cmd_new(mon, params, &data);
|
|
|
|
handler_audit(mon, cmd, ret);
|
|
|
|
monitor_protocol_emitter(mon, data);
|
|
|
|
qobject_decref(data);
|
|
|
|
}
|
|
|
|
|
2009-11-27 03:59:01 +03:00
|
|
|
static void handle_qmp_command(JSONMessageParser *parser, QList *tokens)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
QObject *obj;
|
|
|
|
QDict *input, *args;
|
|
|
|
const mon_cmd_t *cmd;
|
2011-10-21 22:15:31 +04:00
|
|
|
const char *cmd_name;
|
2009-11-27 03:59:01 +03:00
|
|
|
Monitor *mon = cur_mon;
|
|
|
|
|
2010-06-25 00:58:20 +04:00
|
|
|
args = input = NULL;
|
2009-11-27 03:59:01 +03:00
|
|
|
|
|
|
|
obj = json_parser_parse(tokens, NULL);
|
|
|
|
if (!obj) {
|
|
|
|
// FIXME: should be triggered in json_parser_parse()
|
2010-03-02 20:15:09 +03:00
|
|
|
qerror_report(QERR_JSON_PARSING);
|
2009-11-27 03:59:01 +03:00
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
|
2010-06-01 00:28:01 +04:00
|
|
|
input = qmp_check_input_obj(obj);
|
|
|
|
if (!input) {
|
2009-11-27 03:59:01 +03:00
|
|
|
qobject_decref(obj);
|
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
mon->mc->id = qdict_get(input, "id");
|
|
|
|
qobject_incref(mon->mc->id);
|
|
|
|
|
2010-06-01 00:32:50 +04:00
|
|
|
cmd_name = qdict_get_str(input, "execute");
|
2011-09-23 11:23:06 +04:00
|
|
|
trace_handle_qmp_command(mon, cmd_name);
|
2010-02-04 23:10:06 +03:00
|
|
|
if (invalid_qmp_mode(mon, cmd_name)) {
|
2010-03-02 20:15:09 +03:00
|
|
|
qerror_report(QERR_COMMAND_NOT_FOUND, cmd_name);
|
2010-06-25 00:58:20 +04:00
|
|
|
goto err_out;
|
2010-02-04 23:10:06 +03:00
|
|
|
}
|
|
|
|
|
2011-09-02 21:34:47 +04:00
|
|
|
cmd = qmp_find_cmd(cmd_name);
|
2010-09-13 21:44:27 +04:00
|
|
|
if (!cmd) {
|
2010-03-02 20:15:09 +03:00
|
|
|
qerror_report(QERR_COMMAND_NOT_FOUND, cmd_name);
|
2010-06-25 00:58:20 +04:00
|
|
|
goto err_out;
|
2009-11-27 03:59:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
obj = qdict_get(input, "arguments");
|
|
|
|
if (!obj) {
|
|
|
|
args = qdict_new();
|
|
|
|
} else {
|
|
|
|
args = qobject_to_qdict(obj);
|
|
|
|
QINCREF(args);
|
|
|
|
}
|
|
|
|
|
2010-05-26 23:13:09 +04:00
|
|
|
err = qmp_check_client_args(cmd, args);
|
2009-11-27 03:59:01 +03:00
|
|
|
if (err < 0) {
|
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
|
2011-10-21 22:15:31 +04:00
|
|
|
if (handler_is_async(cmd)) {
|
2010-06-23 02:10:46 +04:00
|
|
|
err = qmp_async_cmd_handler(mon, cmd, args);
|
|
|
|
if (err) {
|
|
|
|
/* emit the error response */
|
|
|
|
goto err_out;
|
|
|
|
}
|
2010-01-25 21:18:44 +03:00
|
|
|
} else {
|
2010-09-16 18:11:18 +04:00
|
|
|
qmp_call_cmd(mon, cmd, args);
|
2010-01-25 21:18:44 +03:00
|
|
|
}
|
2010-06-25 00:58:20 +04:00
|
|
|
|
2009-11-27 03:59:01 +03:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
err_out:
|
|
|
|
monitor_protocol_emitter(mon, NULL);
|
|
|
|
out:
|
2010-06-25 00:58:20 +04:00
|
|
|
QDECREF(input);
|
2009-11-27 03:59:01 +03:00
|
|
|
QDECREF(args);
|
|
|
|
}
|
|
|
|
|
2009-11-27 03:58:58 +03:00
|
|
|
/**
|
|
|
|
* monitor_control_read(): Read and handle QMP input
|
|
|
|
*/
|
|
|
|
static void monitor_control_read(void *opaque, const uint8_t *buf, int size)
|
|
|
|
{
|
|
|
|
Monitor *old_mon = cur_mon;
|
|
|
|
|
|
|
|
cur_mon = opaque;
|
|
|
|
|
2009-11-27 03:59:01 +03:00
|
|
|
json_message_parser_feed(&cur_mon->mc->parser, (const char *) buf, size);
|
2009-11-27 03:58:58 +03:00
|
|
|
|
|
|
|
cur_mon = old_mon;
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:42 +03:00
|
|
|
static void monitor_read(void *opaque, const uint8_t *buf, int size)
|
2004-03-15 00:38:27 +03:00
|
|
|
{
|
2009-03-06 02:01:42 +03:00
|
|
|
Monitor *old_mon = cur_mon;
|
2004-08-02 01:52:19 +04:00
|
|
|
int i;
|
2009-03-06 02:01:23 +03:00
|
|
|
|
2009-03-06 02:01:42 +03:00
|
|
|
cur_mon = opaque;
|
|
|
|
|
2009-03-06 02:01:51 +03:00
|
|
|
if (cur_mon->rs) {
|
|
|
|
for (i = 0; i < size; i++)
|
|
|
|
readline_handle_byte(cur_mon->rs, buf[i]);
|
|
|
|
} else {
|
|
|
|
if (size == 0 || buf[size - 1] != 0)
|
|
|
|
monitor_printf(cur_mon, "corrupted command\n");
|
|
|
|
else
|
2009-11-27 03:58:55 +03:00
|
|
|
handle_user_command(cur_mon, (char *)buf);
|
2009-03-06 02:01:51 +03:00
|
|
|
}
|
2004-03-15 00:38:27 +03:00
|
|
|
|
2009-03-06 02:01:42 +03:00
|
|
|
cur_mon = old_mon;
|
|
|
|
}
|
2008-10-06 17:52:44 +04:00
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void monitor_command_cb(Monitor *mon, const char *cmdline, void *opaque)
|
2004-04-04 17:07:25 +04:00
|
|
|
{
|
2009-03-06 02:01:42 +03:00
|
|
|
monitor_suspend(mon);
|
2009-11-27 03:58:55 +03:00
|
|
|
handle_user_command(mon, cmdline);
|
2009-03-06 02:01:42 +03:00
|
|
|
monitor_resume(mon);
|
2008-10-06 17:52:44 +04:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:51 +03:00
|
|
|
int monitor_suspend(Monitor *mon)
|
2008-10-06 17:52:44 +04:00
|
|
|
{
|
2009-03-06 02:01:51 +03:00
|
|
|
if (!mon->rs)
|
|
|
|
return -ENOTTY;
|
2009-03-06 02:01:42 +03:00
|
|
|
mon->suspend_cnt++;
|
2009-03-06 02:01:51 +03:00
|
|
|
return 0;
|
2008-10-06 17:52:44 +04:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
void monitor_resume(Monitor *mon)
|
2008-10-06 17:52:44 +04:00
|
|
|
{
|
2009-03-06 02:01:51 +03:00
|
|
|
if (!mon->rs)
|
|
|
|
return;
|
2009-03-06 02:01:42 +03:00
|
|
|
if (--mon->suspend_cnt == 0)
|
|
|
|
readline_show_prompt(mon->rs);
|
2004-04-04 17:07:25 +04:00
|
|
|
}
|
|
|
|
|
2010-02-04 23:10:04 +03:00
|
|
|
static QObject *get_qmp_greeting(void)
|
|
|
|
{
|
2011-08-27 00:38:13 +04:00
|
|
|
QObject *ver = NULL;
|
2010-02-04 23:10:04 +03:00
|
|
|
|
2011-08-27 00:38:13 +04:00
|
|
|
qmp_marshal_input_query_version(NULL, NULL, &ver);
|
2010-02-04 23:10:04 +03:00
|
|
|
return qobject_from_jsonf("{'QMP':{'version': %p,'capabilities': []}}",ver);
|
|
|
|
}
|
|
|
|
|
2009-11-27 03:58:58 +03:00
|
|
|
/**
|
|
|
|
* monitor_control_event(): Print QMP gretting
|
|
|
|
*/
|
|
|
|
static void monitor_control_event(void *opaque, int event)
|
|
|
|
{
|
2010-02-08 22:01:30 +03:00
|
|
|
QObject *data;
|
|
|
|
Monitor *mon = opaque;
|
2009-11-27 03:58:58 +03:00
|
|
|
|
2010-02-08 22:01:30 +03:00
|
|
|
switch (event) {
|
|
|
|
case CHR_EVENT_OPENED:
|
2010-02-04 23:10:05 +03:00
|
|
|
mon->mc->command_mode = 0;
|
2010-02-04 23:10:04 +03:00
|
|
|
data = get_qmp_greeting();
|
2009-11-27 03:58:58 +03:00
|
|
|
monitor_json_emitter(mon, data);
|
|
|
|
qobject_decref(data);
|
2012-08-15 00:43:48 +04:00
|
|
|
mon_refcount++;
|
2010-02-08 22:01:30 +03:00
|
|
|
break;
|
|
|
|
case CHR_EVENT_CLOSED:
|
|
|
|
json_message_parser_destroy(&mon->mc->parser);
|
2012-08-23 17:03:21 +04:00
|
|
|
json_message_parser_init(&mon->mc->parser, handle_qmp_command);
|
2012-08-15 00:43:48 +04:00
|
|
|
mon_refcount--;
|
|
|
|
monitor_fdsets_cleanup();
|
2010-02-08 22:01:30 +03:00
|
|
|
break;
|
2009-11-27 03:58:58 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:42 +03:00
|
|
|
static void monitor_event(void *opaque, int event)
|
2007-01-06 01:01:59 +03:00
|
|
|
{
|
2009-03-06 02:01:23 +03:00
|
|
|
Monitor *mon = opaque;
|
|
|
|
|
2009-03-06 02:01:47 +03:00
|
|
|
switch (event) {
|
|
|
|
case CHR_EVENT_MUX_IN:
|
2009-09-10 12:58:54 +04:00
|
|
|
mon->mux_out = 0;
|
|
|
|
if (mon->reset_seen) {
|
|
|
|
readline_restart(mon->rs);
|
|
|
|
monitor_resume(mon);
|
|
|
|
monitor_flush(mon);
|
|
|
|
} else {
|
|
|
|
mon->suspend_cnt = 0;
|
|
|
|
}
|
2009-03-06 02:01:47 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CHR_EVENT_MUX_OUT:
|
2009-09-10 12:58:54 +04:00
|
|
|
if (mon->reset_seen) {
|
|
|
|
if (mon->suspend_cnt == 0) {
|
|
|
|
monitor_printf(mon, "\n");
|
|
|
|
}
|
|
|
|
monitor_flush(mon);
|
|
|
|
monitor_suspend(mon);
|
|
|
|
} else {
|
|
|
|
mon->suspend_cnt++;
|
|
|
|
}
|
|
|
|
mon->mux_out = 1;
|
2009-03-06 02:01:47 +03:00
|
|
|
break;
|
2007-01-06 01:01:59 +03:00
|
|
|
|
2009-10-07 17:01:16 +04:00
|
|
|
case CHR_EVENT_OPENED:
|
2009-03-06 02:01:47 +03:00
|
|
|
monitor_printf(mon, "QEMU %s monitor - type 'help' for more "
|
|
|
|
"information\n", QEMU_VERSION);
|
2009-09-10 12:58:54 +04:00
|
|
|
if (!mon->mux_out) {
|
2009-03-06 02:01:47 +03:00
|
|
|
readline_show_prompt(mon->rs);
|
2009-09-10 12:58:54 +04:00
|
|
|
}
|
|
|
|
mon->reset_seen = 1;
|
2012-08-15 00:43:48 +04:00
|
|
|
mon_refcount++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CHR_EVENT_CLOSED:
|
|
|
|
mon_refcount--;
|
|
|
|
monitor_fdsets_cleanup();
|
2009-03-06 02:01:47 +03:00
|
|
|
break;
|
|
|
|
}
|
2007-01-06 01:01:59 +03:00
|
|
|
}
|
|
|
|
|
2011-10-12 07:32:41 +04:00
|
|
|
static int
|
|
|
|
compare_mon_cmd(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
return strcmp(((const mon_cmd_t *)a)->name,
|
|
|
|
((const mon_cmd_t *)b)->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sortcmdlist(void)
|
|
|
|
{
|
|
|
|
int array_num;
|
|
|
|
int elem_size = sizeof(mon_cmd_t);
|
|
|
|
|
|
|
|
array_num = sizeof(mon_cmds)/elem_size-1;
|
|
|
|
qsort((void *)mon_cmds, array_num, elem_size, compare_mon_cmd);
|
|
|
|
|
|
|
|
array_num = sizeof(info_cmds)/elem_size-1;
|
|
|
|
qsort((void *)info_cmds, array_num, elem_size, compare_mon_cmd);
|
|
|
|
}
|
|
|
|
|
Support ACLs for controlling VNC access ("Daniel P. Berrange")
This patch introduces a generic internal API for access control lists
to be used by network servers in QEMU. It adds support for checking
these ACL in the VNC server, in two places. The first ACL is for the
SASL authentication mechanism, checking the SASL username. This ACL
is called 'vnc.username'. The second is for the TLS authentication
mechanism, when x509 client certificates are turned on, checking against
the Distinguished Name of the client. This ACL is called 'vnc.x509dname'
The internal API provides for an ACL with the following characteristics
- A unique name, eg vnc.username, and vnc.x509dname.
- A default policy, allow or deny
- An ordered series of match rules, with allow or deny policy
If none of the match rules apply, then the default policy is
used.
There is a monitor API to manipulate the ACLs, which I'll describe via
examples
(qemu) acl show vnc.username
policy: allow
(qemu) acl policy vnc.username denya
acl: policy set to 'deny'
(qemu) acl allow vnc.username fred
acl: added rule at position 1
(qemu) acl allow vnc.username bob
acl: added rule at position 2
(qemu) acl allow vnc.username joe 1
acl: added rule at position 1
(qemu) acl show vnc.username
policy: deny
0: allow fred
1: allow joe
2: allow bob
(qemu) acl show vnc.x509dname
policy: allow
(qemu) acl policy vnc.x509dname deny
acl: policy set to 'deny'
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=London,CN=*
acl: added rule at position 1
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=Boston,CN=bob
acl: added rule at position 2
(qemu) acl show vnc.x509dname
policy: deny
0: allow C=GB,O=ACME,L=London,CN=*
1: allow C=GB,O=ACME,L=Boston,CN=bob
By default the VNC server will not use any ACLs, allowing access to
the server if the user successfully authenticates. To enable use of
ACLs to restrict user access, the ',acl' flag should be given when
starting QEMU. The initial ACL activated will be a 'deny all' policy
and should be customized using monitor commands.
eg enable SASL auth and ACLs
qemu .... -vnc localhost:1,sasl,acl
The next patch will provide a way to load a pre-defined ACL when
starting up
Makefile | 6 +
b/acl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
b/acl.h | 74 ++++++++++++++++++++++
configure | 18 +++++
monitor.c | 95 ++++++++++++++++++++++++++++
qemu-doc.texi | 49 ++++++++++++++
vnc-auth-sasl.c | 16 +++-
vnc-auth-sasl.h | 7 ++
vnc-tls.c | 19 +++++
vnc-tls.h | 3
vnc.c | 21 ++++++
vnc.h | 3
12 files changed, 491 insertions(+), 5 deletions(-)
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6726 c046a42c-6fe2-441c-8c8c-71466251a162
2009-03-06 23:27:37 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Local variables:
|
|
|
|
* c-indent-level: 4
|
|
|
|
* c-basic-offset: 4
|
|
|
|
* tab-width: 8
|
|
|
|
* End:
|
|
|
|
*/
|
|
|
|
|
2009-03-06 02:01:42 +03:00
|
|
|
void monitor_init(CharDriverState *chr, int flags)
|
2004-04-04 17:07:25 +04:00
|
|
|
{
|
2009-03-06 02:01:42 +03:00
|
|
|
static int is_first_init = 1;
|
2009-03-06 02:01:29 +03:00
|
|
|
Monitor *mon;
|
2007-02-18 20:04:49 +03:00
|
|
|
|
|
|
|
if (is_first_init) {
|
2012-06-14 21:12:57 +04:00
|
|
|
monitor_protocol_event_init();
|
2007-02-18 20:04:49 +03:00
|
|
|
is_first_init = 0;
|
|
|
|
}
|
2009-03-06 02:01:29 +03:00
|
|
|
|
2011-08-21 07:09:37 +04:00
|
|
|
mon = g_malloc0(sizeof(*mon));
|
2007-02-18 20:04:49 +03:00
|
|
|
|
2009-03-06 02:01:29 +03:00
|
|
|
mon->chr = chr;
|
2009-03-06 02:01:42 +03:00
|
|
|
mon->flags = flags;
|
2009-03-06 02:01:51 +03:00
|
|
|
if (flags & MONITOR_USE_READLINE) {
|
|
|
|
mon->rs = readline_init(mon, monitor_find_completion);
|
|
|
|
monitor_read_command(mon, 0);
|
|
|
|
}
|
2009-03-06 02:01:29 +03:00
|
|
|
|
2009-11-27 03:58:58 +03:00
|
|
|
if (monitor_ctrl_mode(mon)) {
|
2011-08-21 07:09:37 +04:00
|
|
|
mon->mc = g_malloc0(sizeof(MonitorControl));
|
2009-11-27 03:58:58 +03:00
|
|
|
/* Control mode requires special handlers */
|
|
|
|
qemu_chr_add_handlers(chr, monitor_can_read, monitor_control_read,
|
|
|
|
monitor_control_event, mon);
|
2011-08-15 20:17:35 +04:00
|
|
|
qemu_chr_fe_set_echo(chr, true);
|
2012-08-23 22:49:02 +04:00
|
|
|
|
|
|
|
json_message_parser_init(&mon->mc->parser, handle_qmp_command);
|
2009-11-27 03:58:58 +03:00
|
|
|
} else {
|
|
|
|
qemu_chr_add_handlers(chr, monitor_can_read, monitor_read,
|
|
|
|
monitor_event, mon);
|
|
|
|
}
|
2009-03-06 02:01:29 +03:00
|
|
|
|
2009-09-12 11:36:22 +04:00
|
|
|
QLIST_INSERT_HEAD(&mon_list, mon, entry);
|
2010-02-18 13:41:55 +03:00
|
|
|
if (!default_mon || (flags & MONITOR_IS_DEFAULT))
|
|
|
|
default_mon = mon;
|
2011-10-12 07:32:41 +04:00
|
|
|
|
|
|
|
sortcmdlist();
|
2004-04-04 17:07:25 +04:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void bdrv_password_cb(Monitor *mon, const char *password, void *opaque)
|
2004-07-14 21:21:37 +04:00
|
|
|
{
|
2009-03-06 02:01:15 +03:00
|
|
|
BlockDriverState *bs = opaque;
|
|
|
|
int ret = 0;
|
2004-07-14 21:21:37 +04:00
|
|
|
|
2009-03-06 02:01:15 +03:00
|
|
|
if (bdrv_set_key(bs, password) != 0) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "invalid password\n");
|
2009-03-06 02:01:15 +03:00
|
|
|
ret = -EPERM;
|
2004-03-15 00:38:27 +03:00
|
|
|
}
|
2009-03-06 02:01:42 +03:00
|
|
|
if (mon->password_completion_cb)
|
|
|
|
mon->password_completion_cb(mon->password_opaque, ret);
|
2009-03-06 02:01:15 +03:00
|
|
|
|
2009-03-06 02:01:42 +03:00
|
|
|
monitor_read_command(mon, 1);
|
2004-03-15 00:38:27 +03:00
|
|
|
}
|
2009-03-06 02:01:01 +03:00
|
|
|
|
2011-09-02 21:34:50 +04:00
|
|
|
ReadLineState *monitor_get_rs(Monitor *mon)
|
|
|
|
{
|
|
|
|
return mon->rs;
|
|
|
|
}
|
|
|
|
|
2010-02-11 04:50:01 +03:00
|
|
|
int monitor_read_bdrv_key_start(Monitor *mon, BlockDriverState *bs,
|
|
|
|
BlockDriverCompletionFunc *completion_cb,
|
|
|
|
void *opaque)
|
2009-03-06 02:01:01 +03:00
|
|
|
{
|
2009-03-06 02:01:51 +03:00
|
|
|
int err;
|
|
|
|
|
2009-03-06 02:01:15 +03:00
|
|
|
if (!bdrv_key_required(bs)) {
|
|
|
|
if (completion_cb)
|
|
|
|
completion_cb(opaque, 0);
|
2010-02-11 04:50:01 +03:00
|
|
|
return 0;
|
2009-03-06 02:01:15 +03:00
|
|
|
}
|
2009-03-06 02:01:01 +03:00
|
|
|
|
2009-12-07 23:37:00 +03:00
|
|
|
if (monitor_ctrl_mode(mon)) {
|
2011-12-13 23:18:30 +04:00
|
|
|
qerror_report(QERR_DEVICE_ENCRYPTED, bdrv_get_device_name(bs),
|
|
|
|
bdrv_get_encrypted_filename(bs));
|
2010-02-11 04:50:01 +03:00
|
|
|
return -1;
|
2009-12-07 23:37:00 +03:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%s (%s) is encrypted.\n", bdrv_get_device_name(bs),
|
|
|
|
bdrv_get_encrypted_filename(bs));
|
2009-03-06 02:01:15 +03:00
|
|
|
|
2009-03-06 02:01:42 +03:00
|
|
|
mon->password_completion_cb = completion_cb;
|
|
|
|
mon->password_opaque = opaque;
|
2009-03-06 02:01:15 +03:00
|
|
|
|
2009-03-06 02:01:51 +03:00
|
|
|
err = monitor_read_password(mon, bdrv_password_cb, bs);
|
|
|
|
|
|
|
|
if (err && completion_cb)
|
|
|
|
completion_cb(opaque, err);
|
2010-02-11 04:50:01 +03:00
|
|
|
|
|
|
|
return err;
|
2009-03-06 02:01:01 +03:00
|
|
|
}
|
2011-11-22 23:58:31 +04:00
|
|
|
|
|
|
|
int monitor_read_block_device_key(Monitor *mon, const char *device,
|
|
|
|
BlockDriverCompletionFunc *completion_cb,
|
|
|
|
void *opaque)
|
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
|
|
|
|
bs = bdrv_find(device);
|
|
|
|
if (!bs) {
|
|
|
|
monitor_printf(mon, "Device not found %s\n", device);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return monitor_read_bdrv_key_start(mon, bs, completion_cb, opaque);
|
|
|
|
}
|
2012-11-26 19:03:42 +04:00
|
|
|
|
|
|
|
QemuOptsList qemu_mon_opts = {
|
|
|
|
.name = "mon",
|
|
|
|
.implied_opt_name = "chardev",
|
|
|
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_mon_opts.head),
|
|
|
|
.desc = {
|
|
|
|
{
|
|
|
|
.name = "mode",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
},{
|
|
|
|
.name = "chardev",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
},{
|
|
|
|
.name = "default",
|
|
|
|
.type = QEMU_OPT_BOOL,
|
|
|
|
},{
|
|
|
|
.name = "pretty",
|
|
|
|
.type = QEMU_OPT_BOOL,
|
|
|
|
},
|
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
|
|
|
};
|