2004-03-15 05:38:27 +08:00
|
|
|
/*
|
|
|
|
* QEMU monitor
|
2007-09-17 05:08:06 +08:00
|
|
|
*
|
2004-03-15 05:38:27 +08:00
|
|
|
* Copyright (c) 2003-2004 Fabrice Bellard
|
2007-09-17 05:08:06 +08:00
|
|
|
*
|
2004-03-15 05:38:27 +08: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 23:32:56 +08:00
|
|
|
#include <dirent.h>
|
2007-11-18 01:14:51 +08:00
|
|
|
#include "hw/hw.h"
|
2009-06-05 22:53:17 +08:00
|
|
|
#include "hw/qdev.h"
|
2007-11-18 01:14:51 +08:00
|
|
|
#include "hw/usb.h"
|
|
|
|
#include "hw/pcmcia.h"
|
|
|
|
#include "hw/pc.h"
|
|
|
|
#include "hw/pci.h"
|
2009-04-25 20:56:19 +08:00
|
|
|
#include "hw/watchdog.h"
|
2009-10-01 22:42:33 +08:00
|
|
|
#include "hw/loader.h"
|
2007-11-18 01:14:51 +08:00
|
|
|
#include "gdbstub.h"
|
|
|
|
#include "net.h"
|
2009-11-26 02:48:54 +08:00
|
|
|
#include "net/slirp.h"
|
2007-11-18 01:14:51 +08:00
|
|
|
#include "qemu-char.h"
|
|
|
|
#include "sysemu.h"
|
2009-03-06 07:01:23 +08:00
|
|
|
#include "monitor.h"
|
|
|
|
#include "readline.h"
|
2007-11-18 01:14:51 +08:00
|
|
|
#include "console.h"
|
|
|
|
#include "block.h"
|
|
|
|
#include "audio/audio.h"
|
2004-04-04 20:57:25 +08:00
|
|
|
#include "disas.h"
|
2008-12-05 04:19:35 +08:00
|
|
|
#include "balloon.h"
|
2008-06-09 06:45:01 +08:00
|
|
|
#include "qemu-timer.h"
|
2008-10-13 11:12:02 +08:00
|
|
|
#include "migration.h"
|
2008-11-06 00:04:33 +08:00
|
|
|
#include "kvm.h"
|
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-07 04:27:37 +08:00
|
|
|
#include "acl.h"
|
2009-08-29 02:27:10 +08:00
|
|
|
#include "qint.h"
|
2009-10-08 00:42:04 +08:00
|
|
|
#include "qlist.h"
|
2009-08-29 02:27:10 +08:00
|
|
|
#include "qdict.h"
|
2009-11-27 08:59:01 +08:00
|
|
|
#include "qbool.h"
|
2009-08-29 02:27:10 +08:00
|
|
|
#include "qstring.h"
|
2009-11-19 09:05:31 +08:00
|
|
|
#include "qerror.h"
|
2009-11-27 08:58:58 +08:00
|
|
|
#include "qjson.h"
|
2009-11-27 08:59:01 +08:00
|
|
|
#include "json-streamer.h"
|
|
|
|
#include "json-parser.h"
|
2009-12-05 02:06:39 +08:00
|
|
|
#include "osdep.h"
|
2007-12-04 01:05:38 +08:00
|
|
|
|
2004-03-15 05:38:27 +08:00
|
|
|
//#define DEBUG
|
2004-07-15 01:21:37 +08:00
|
|
|
//#define DEBUG_COMPLETION
|
2004-03-15 05:38:27 +08:00
|
|
|
|
2004-04-04 20:57:25 +08:00
|
|
|
/*
|
|
|
|
* Supported types:
|
2007-09-17 05:08:06 +08:00
|
|
|
*
|
2004-04-04 20:57:25 +08:00
|
|
|
* 'F' filename
|
2004-07-15 01:21:37 +08:00
|
|
|
* 'B' block device name
|
2004-04-04 20:57:25 +08:00
|
|
|
* 's' string (accept optional quote)
|
2005-02-11 06:00:52 +08:00
|
|
|
* 'i' 32 bit integer
|
|
|
|
* 'l' target long (32 or 64 bit)
|
2004-04-04 20:57:25 +08:00
|
|
|
* '/' optional gdb-like print format (like "/10x")
|
|
|
|
*
|
2009-08-29 02:27:27 +08:00
|
|
|
* '?' optional type (for all types, except '/')
|
|
|
|
* '.' other form of optional type (for 'i' and 'l')
|
|
|
|
* '-' optional parameter (eg. '-f')
|
2004-04-04 20:57:25 +08:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2010-01-26 02:18:44 +08:00
|
|
|
typedef struct MonitorCompletionData MonitorCompletionData;
|
|
|
|
struct MonitorCompletionData {
|
|
|
|
Monitor *mon;
|
|
|
|
void (*user_print)(Monitor *mon, const QObject *data);
|
|
|
|
};
|
|
|
|
|
2009-10-02 05:12:16 +08:00
|
|
|
typedef struct mon_cmd_t {
|
2004-03-15 05:38:27 +08:00
|
|
|
const char *name;
|
2004-04-04 20:57:25 +08:00
|
|
|
const char *args_type;
|
2004-03-15 05:38:27 +08:00
|
|
|
const char *params;
|
|
|
|
const char *help;
|
2009-10-08 00:41:53 +08:00
|
|
|
void (*user_print)(Monitor *mon, const QObject *data);
|
2009-10-08 00:41:51 +08:00
|
|
|
union {
|
|
|
|
void (*info)(Monitor *mon);
|
2009-10-08 00:41:55 +08:00
|
|
|
void (*info_new)(Monitor *mon, QObject **ret_data);
|
2010-01-26 02:18:44 +08:00
|
|
|
int (*info_async)(Monitor *mon, MonitorCompletion *cb, void *opaque);
|
2009-10-08 00:41:52 +08:00
|
|
|
void (*cmd)(Monitor *mon, const QDict *qdict);
|
2009-10-08 00:41:54 +08:00
|
|
|
void (*cmd_new)(Monitor *mon, const QDict *params, QObject **ret_data);
|
2010-01-26 02:18:44 +08:00
|
|
|
int (*cmd_async)(Monitor *mon, const QDict *params,
|
|
|
|
MonitorCompletion *cb, void *opaque);
|
2009-10-08 00:41:51 +08:00
|
|
|
} mhandler;
|
2010-01-26 02:18:44 +08:00
|
|
|
int async;
|
2009-10-02 05:12:16 +08:00
|
|
|
} mon_cmd_t;
|
2004-03-15 05:38:27 +08:00
|
|
|
|
2009-07-22 16:11:40 +08:00
|
|
|
/* file descriptors passed via SCM_RIGHTS */
|
2009-10-02 05:12:16 +08:00
|
|
|
typedef struct mon_fd_t mon_fd_t;
|
|
|
|
struct mon_fd_t {
|
2009-07-22 16:11:40 +08:00
|
|
|
char *name;
|
|
|
|
int fd;
|
2009-10-02 05:12:16 +08:00
|
|
|
QLIST_ENTRY(mon_fd_t) next;
|
2009-07-22 16:11:40 +08:00
|
|
|
};
|
|
|
|
|
2009-11-27 08:59:01 +08:00
|
|
|
typedef struct MonitorControl {
|
|
|
|
QObject *id;
|
2009-11-27 08:59:05 +08:00
|
|
|
int print_enabled;
|
2009-11-27 08:59:01 +08:00
|
|
|
JSONMessageParser parser;
|
|
|
|
} MonitorControl;
|
|
|
|
|
2009-03-06 07:01:29 +08:00
|
|
|
struct Monitor {
|
|
|
|
CharDriverState *chr;
|
2009-09-10 16:58:54 +08:00
|
|
|
int mux_out;
|
|
|
|
int reset_seen;
|
2009-03-06 07:01:42 +08:00
|
|
|
int flags;
|
|
|
|
int suspend_cnt;
|
|
|
|
uint8_t outbuf[1024];
|
|
|
|
int outbuf_index;
|
|
|
|
ReadLineState *rs;
|
2009-11-27 08:59:01 +08:00
|
|
|
MonitorControl *mc;
|
2009-03-06 07:01:42 +08:00
|
|
|
CPUState *mon_cpu;
|
|
|
|
BlockDriverCompletionFunc *password_completion_cb;
|
|
|
|
void *password_opaque;
|
2009-11-19 09:05:31 +08:00
|
|
|
QError *error;
|
2009-10-02 05:12:16 +08:00
|
|
|
QLIST_HEAD(,mon_fd_t) fds;
|
2009-09-12 15:36:22 +08:00
|
|
|
QLIST_ENTRY(Monitor) entry;
|
2009-03-06 07:01:29 +08:00
|
|
|
};
|
|
|
|
|
2009-09-12 15:36:22 +08:00
|
|
|
static QLIST_HEAD(mon_list, Monitor) mon_list;
|
2004-08-02 05:52:19 +08:00
|
|
|
|
2009-10-02 05:12:16 +08:00
|
|
|
static const mon_cmd_t mon_cmds[];
|
|
|
|
static const mon_cmd_t info_cmds[];
|
2004-03-15 05:38:27 +08:00
|
|
|
|
2009-03-06 07:01:29 +08:00
|
|
|
Monitor *cur_mon = NULL;
|
2009-03-06 07:01:23 +08:00
|
|
|
|
2009-03-06 07:01:42 +08:00
|
|
|
static void monitor_command_cb(Monitor *mon, const char *cmdline,
|
|
|
|
void *opaque);
|
2008-08-19 22:44:22 +08:00
|
|
|
|
2009-11-27 08:58:51 +08:00
|
|
|
/* Return true if in control mode, false otherwise */
|
|
|
|
static inline int monitor_ctrl_mode(const Monitor *mon)
|
|
|
|
{
|
|
|
|
return (mon->flags & MONITOR_USE_CONTROL);
|
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:42 +08:00
|
|
|
static void monitor_read_command(Monitor *mon, int show_prompt)
|
|
|
|
{
|
2009-12-15 04:53:23 +08:00
|
|
|
if (!mon->rs)
|
|
|
|
return;
|
|
|
|
|
2009-03-06 07:01:42 +08:00
|
|
|
readline_start(mon->rs, "(qemu) ", 0, monitor_command_cb, NULL);
|
|
|
|
if (show_prompt)
|
|
|
|
readline_show_prompt(mon->rs);
|
|
|
|
}
|
2005-11-22 07:25:50 +08:00
|
|
|
|
2009-03-06 07:01:51 +08:00
|
|
|
static int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func,
|
|
|
|
void *opaque)
|
2009-03-06 07:01:15 +08:00
|
|
|
{
|
2009-12-08 04:37:00 +08:00
|
|
|
if (monitor_ctrl_mode(mon)) {
|
|
|
|
qemu_error_new(QERR_MISSING_PARAMETER, "password");
|
|
|
|
return -EINVAL;
|
|
|
|
} else if (mon->rs) {
|
2009-03-06 07:01:51 +08: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 07:01:15 +08:00
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
void monitor_flush(Monitor *mon)
|
2004-08-02 05:52:19 +08:00
|
|
|
{
|
2009-09-10 16:58:54 +08:00
|
|
|
if (mon && mon->outbuf_index != 0 && !mon->mux_out) {
|
2009-03-06 07:01:42 +08:00
|
|
|
qemu_chr_write(mon->chr, mon->outbuf, mon->outbuf_index);
|
|
|
|
mon->outbuf_index = 0;
|
2004-08-02 05:52:19 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* flush at every end of line or if the buffer is full */
|
2009-03-06 07:01:23 +08:00
|
|
|
static void monitor_puts(Monitor *mon, const char *str)
|
2004-08-02 05:52:19 +08:00
|
|
|
{
|
2007-12-16 11:02:09 +08:00
|
|
|
char c;
|
2009-03-06 07:01:42 +08:00
|
|
|
|
2004-08-02 05:52:19 +08:00
|
|
|
for(;;) {
|
|
|
|
c = *str++;
|
|
|
|
if (c == '\0')
|
|
|
|
break;
|
2006-07-15 04:26:42 +08:00
|
|
|
if (c == '\n')
|
2009-03-06 07:01:42 +08: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 07:01:23 +08:00
|
|
|
monitor_flush(mon);
|
2004-08-02 05:52:19 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
|
2004-03-15 05:38:27 +08:00
|
|
|
{
|
2009-12-15 04:53:24 +08:00
|
|
|
if (!mon)
|
|
|
|
return;
|
|
|
|
|
2009-11-27 08:59:05 +08:00
|
|
|
if (mon->mc && !mon->mc->print_enabled) {
|
|
|
|
qemu_error_new(QERR_UNDEFINED_ERROR);
|
|
|
|
} else {
|
|
|
|
char buf[4096];
|
|
|
|
vsnprintf(buf, sizeof(buf), fmt, ap);
|
|
|
|
monitor_puts(mon, buf);
|
|
|
|
}
|
2004-03-15 05:38:27 +08:00
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
void monitor_printf(Monitor *mon, const char *fmt, ...)
|
2004-03-15 05:38:27 +08:00
|
|
|
{
|
2004-08-02 05:52:19 +08:00
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_vprintf(mon, fmt, ap);
|
2004-08-02 05:52:19 +08:00
|
|
|
va_end(ap);
|
2004-03-15 05:38:27 +08:00
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
void monitor_print_filename(Monitor *mon, const char *filename)
|
2006-12-22 22:11:32 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; filename[i]; i++) {
|
2009-03-07 04:27:40 +08: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 22:11:32 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-10-10 02:08:01 +08:00
|
|
|
static int monitor_fprintf(FILE *stream, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_vprintf((Monitor *)stream, fmt, ap);
|
2004-10-10 02:08:01 +08:00
|
|
|
va_end(ap);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-10-08 00:41:55 +08:00
|
|
|
static void monitor_user_noop(Monitor *mon, const QObject *data) { }
|
|
|
|
|
2009-10-08 00:41:54 +08:00
|
|
|
static inline int monitor_handler_ported(const mon_cmd_t *cmd)
|
|
|
|
{
|
|
|
|
return cmd->user_print != NULL;
|
|
|
|
}
|
|
|
|
|
2010-01-26 02:18:44 +08:00
|
|
|
static inline bool monitor_handler_is_async(const mon_cmd_t *cmd)
|
|
|
|
{
|
|
|
|
return cmd->async != 0;
|
|
|
|
}
|
|
|
|
|
2009-11-19 09:05:31 +08:00
|
|
|
static inline int monitor_has_error(const Monitor *mon)
|
|
|
|
{
|
|
|
|
return mon->error != NULL;
|
|
|
|
}
|
|
|
|
|
2009-11-27 08:58:58 +08:00
|
|
|
static void monitor_json_emitter(Monitor *mon, const QObject *data)
|
|
|
|
{
|
|
|
|
QString *json;
|
|
|
|
|
|
|
|
json = qobject_to_json(data);
|
|
|
|
assert(json != NULL);
|
|
|
|
|
2009-11-27 08:59:05 +08:00
|
|
|
mon->mc->print_enabled = 1;
|
2009-11-27 08:58:58 +08:00
|
|
|
monitor_printf(mon, "%s\n", qstring_get_str(json));
|
2009-11-27 08:59:05 +08:00
|
|
|
mon->mc->print_enabled = 0;
|
|
|
|
|
2009-11-27 08:58:58 +08:00
|
|
|
QDECREF(json);
|
|
|
|
}
|
|
|
|
|
2009-11-27 08:58:59 +08:00
|
|
|
static void monitor_protocol_emitter(Monitor *mon, QObject *data)
|
|
|
|
{
|
|
|
|
QDict *qmp;
|
|
|
|
|
|
|
|
qmp = qdict_new();
|
|
|
|
|
|
|
|
if (!monitor_has_error(mon)) {
|
|
|
|
/* success response */
|
|
|
|
if (data) {
|
|
|
|
qobject_incref(data);
|
|
|
|
qdict_put_obj(qmp, "return", data);
|
|
|
|
} else {
|
2009-12-18 23:25:00 +08:00
|
|
|
/* return an empty QDict by default */
|
|
|
|
qdict_put(qmp, "return", qdict_new());
|
2009-11-27 08:58:59 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* error response */
|
2009-12-08 04:37:16 +08:00
|
|
|
qdict_put(mon->error->error, "desc", qerror_human(mon->error));
|
2009-11-27 08:58:59 +08:00
|
|
|
qdict_put(qmp, "error", mon->error->error);
|
|
|
|
QINCREF(mon->error->error);
|
|
|
|
QDECREF(mon->error);
|
|
|
|
mon->error = NULL;
|
|
|
|
}
|
|
|
|
|
2009-11-27 08:59:01 +08:00
|
|
|
if (mon->mc->id) {
|
|
|
|
qdict_put_obj(qmp, "id", mon->mc->id);
|
|
|
|
mon->mc->id = NULL;
|
|
|
|
}
|
|
|
|
|
2009-11-27 08:58:59 +08:00
|
|
|
monitor_json_emitter(mon, QOBJECT(qmp));
|
|
|
|
QDECREF(qmp);
|
|
|
|
}
|
|
|
|
|
2009-11-27 08:59:03 +08:00
|
|
|
static void timestamp_put(QDict *qdict)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
QObject *obj;
|
2009-12-05 02:06:39 +08:00
|
|
|
qemu_timeval tv;
|
2009-11-27 08:59:03 +08:00
|
|
|
|
2009-12-05 02:06:39 +08:00
|
|
|
err = qemu_gettimeofday(&tv);
|
2009-11-27 08:59:03 +08:00
|
|
|
if (err < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
obj = qobject_from_jsonf("{ 'seconds': %" PRId64 ", "
|
|
|
|
"'microseconds': %" PRId64 " }",
|
|
|
|
(int64_t) tv.tv_sec, (int64_t) tv.tv_usec);
|
|
|
|
assert(obj != NULL);
|
|
|
|
|
|
|
|
qdict_put_obj(qdict, "timestamp", obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
2010-01-15 22:34:02 +08:00
|
|
|
Monitor *mon;
|
2009-11-27 08:59:03 +08:00
|
|
|
|
2009-12-05 02:05:45 +08:00
|
|
|
assert(event < QEVENT_MAX);
|
2009-11-27 08:59:03 +08:00
|
|
|
|
|
|
|
switch (event) {
|
2009-12-05 02:05:45 +08:00
|
|
|
case QEVENT_DEBUG:
|
2009-11-27 08:59:04 +08:00
|
|
|
event_name = "DEBUG";
|
|
|
|
break;
|
2009-12-05 02:05:45 +08:00
|
|
|
case QEVENT_SHUTDOWN:
|
2009-11-27 08:59:04 +08:00
|
|
|
event_name = "SHUTDOWN";
|
|
|
|
break;
|
2009-12-05 02:05:45 +08:00
|
|
|
case QEVENT_RESET:
|
2009-11-27 08:59:04 +08:00
|
|
|
event_name = "RESET";
|
|
|
|
break;
|
2009-12-05 02:05:45 +08:00
|
|
|
case QEVENT_POWERDOWN:
|
2009-11-27 08:59:04 +08:00
|
|
|
event_name = "POWERDOWN";
|
|
|
|
break;
|
2009-12-05 02:05:45 +08:00
|
|
|
case QEVENT_STOP:
|
2009-11-27 08:59:04 +08:00
|
|
|
event_name = "STOP";
|
|
|
|
break;
|
QMP: Introduce VNC_CONNECTED event
It's emitted when a VNC client connects to QEMU, client's information
such as port and IP address are provided.
Note that this event is emitted right when the connection is
established. This means that it happens before authentication
procedure and session initialization.
Event example:
{ "event": "VNC_CONNECTED",
"timestamp": { "seconds": 1262976601, "microseconds": 975795 },
"data": {
"server": { "auth": "sasl", "family": "ipv4",
"service": "5901", "host": "0.0.0.0" },
"client": { "family": "ipv4", "service": "58425",
"host": "127.0.0.1" } } }
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2010-01-15 00:50:57 +08:00
|
|
|
case QEVENT_VNC_CONNECTED:
|
|
|
|
event_name = "VNC_CONNECTED";
|
|
|
|
break;
|
QMP: Introduce VNC_INITIALIZED event
It's emitted when a VNC client session is activated by QEMU,
client's information such as port, IP and auth ID (if the
session is authenticated) are provided.
Event example:
{ "event": "VNC_INITIALIZED",
"timestamp": {"seconds": 1263475302, "microseconds": 150772},
"data": {
"server": { "auth": "sasl", "family": "ipv4",
"service": "5901", "host": "0.0.0.0"},
"client": { "family": "ipv4", "service": "46089",
"host": "127.0.0.1", "sasl_username": "lcapitulino" } } }
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2010-01-15 00:50:59 +08:00
|
|
|
case QEVENT_VNC_INITIALIZED:
|
|
|
|
event_name = "VNC_INITIALIZED";
|
|
|
|
break;
|
QMP: Introduce VNC_DISCONNECTED event
It's emitted when a VNC client disconnects from QEMU, client's
information such as port and IP address are provided.
Event example:
{ "event": "VNC_DISCONNECTED",
"timestamp": { "seconds": 1262976601, "microseconds": 975795 },
"data": {
"server": { "auth": "sasl", "family": "ipv4",
"service": "5901", "host": "0.0.0.0" },
"client": { "family": "ipv4", "service": "58425",
"host": "127.0.0.1", "sasl_username": "foo" } } }
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2010-01-15 00:50:58 +08:00
|
|
|
case QEVENT_VNC_DISCONNECTED:
|
|
|
|
event_name = "VNC_DISCONNECTED";
|
|
|
|
break;
|
2009-11-27 08:59:03 +08:00
|
|
|
default:
|
|
|
|
abort();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
qmp = qdict_new();
|
|
|
|
timestamp_put(qmp);
|
|
|
|
qdict_put(qmp, "event", qstring_from_str(event_name));
|
2010-01-09 02:45:53 +08:00
|
|
|
if (data) {
|
|
|
|
qobject_incref(data);
|
2009-11-27 08:59:03 +08:00
|
|
|
qdict_put_obj(qmp, "data", data);
|
2010-01-09 02:45:53 +08:00
|
|
|
}
|
2009-11-27 08:59:03 +08:00
|
|
|
|
2010-01-15 22:34:02 +08:00
|
|
|
QLIST_FOREACH(mon, &mon_list, entry) {
|
2010-01-20 20:37:59 +08:00
|
|
|
if (monitor_ctrl_mode(mon)) {
|
|
|
|
monitor_json_emitter(mon, QOBJECT(qmp));
|
|
|
|
}
|
2010-01-15 22:34:02 +08:00
|
|
|
}
|
2009-11-27 08:59:03 +08:00
|
|
|
QDECREF(qmp);
|
|
|
|
}
|
|
|
|
|
2004-03-15 05:38:27 +08: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 05:12:16 +08:00
|
|
|
static void help_cmd_dump(Monitor *mon, const mon_cmd_t *cmds,
|
2009-03-06 07:01:23 +08:00
|
|
|
const char *prefix, const char *name)
|
2004-03-15 05:38:27 +08:00
|
|
|
{
|
2009-10-02 05:12:16 +08:00
|
|
|
const mon_cmd_t *cmd;
|
2004-03-15 05:38:27 +08:00
|
|
|
|
|
|
|
for(cmd = cmds; cmd->name != NULL; cmd++) {
|
|
|
|
if (!name || !strcmp(name, cmd->name))
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "%s%s %s -- %s\n", prefix, cmd->name,
|
|
|
|
cmd->params, cmd->help);
|
2004-03-15 05:38:27 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
static void help_cmd(Monitor *mon, const char *name)
|
2004-03-15 05:38:27 +08:00
|
|
|
{
|
|
|
|
if (name && !strcmp(name, "info")) {
|
2009-03-06 07:01:23 +08:00
|
|
|
help_cmd_dump(mon, info_cmds, "info ", NULL);
|
2004-03-15 05:38:27 +08:00
|
|
|
} else {
|
2009-03-06 07:01:23 +08:00
|
|
|
help_cmd_dump(mon, mon_cmds, "", name);
|
2004-03-22 01:06:25 +08:00
|
|
|
if (name && !strcmp(name, "log")) {
|
2008-10-03 02:32:44 +08:00
|
|
|
const CPULogItem *item;
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "Log items (comma separated):\n");
|
|
|
|
monitor_printf(mon, "%-10s %s\n", "none", "remove all logs");
|
2004-03-22 01:06:25 +08:00
|
|
|
for(item = cpu_log_items; item->mask != 0; item++) {
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "%-10s %s\n", item->name, item->help);
|
2004-03-22 01:06:25 +08:00
|
|
|
}
|
|
|
|
}
|
2004-03-15 05:38:27 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-29 02:27:13 +08:00
|
|
|
static void do_help_cmd(Monitor *mon, const QDict *qdict)
|
2009-08-29 02:27:08 +08:00
|
|
|
{
|
2009-08-29 02:27:13 +08:00
|
|
|
help_cmd(mon, qdict_get_try_str(qdict, "name"));
|
2009-08-29 02:27:08 +08:00
|
|
|
}
|
|
|
|
|
2009-08-29 02:27:13 +08:00
|
|
|
static void do_commit(Monitor *mon, const QDict *qdict)
|
2004-03-15 05:38:27 +08:00
|
|
|
{
|
2009-07-22 22:42:57 +08:00
|
|
|
int all_devices;
|
|
|
|
DriveInfo *dinfo;
|
2009-08-29 02:27:13 +08:00
|
|
|
const char *device = qdict_get_str(qdict, "device");
|
2007-05-25 02:53:22 +08:00
|
|
|
|
2006-08-01 23:52:40 +08:00
|
|
|
all_devices = !strcmp(device, "all");
|
2009-09-12 15:36:22 +08:00
|
|
|
QTAILQ_FOREACH(dinfo, &drives, next) {
|
2009-07-22 22:42:57 +08:00
|
|
|
if (!all_devices)
|
2009-08-01 02:15:41 +08:00
|
|
|
if (strcmp(bdrv_get_device_name(dinfo->bdrv), device))
|
2009-07-22 22:42:57 +08:00
|
|
|
continue;
|
|
|
|
bdrv_commit(dinfo->bdrv);
|
2004-03-15 05:38:27 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-26 02:18:44 +08: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);
|
|
|
|
qemu_free(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qmp_monitor_complete(void *opaque, QObject *ret_data)
|
|
|
|
{
|
|
|
|
monitor_protocol_emitter(opaque, ret_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qmp_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd,
|
|
|
|
const QDict *params)
|
|
|
|
{
|
|
|
|
cmd->mhandler.cmd_async(mon, params, qmp_monitor_complete, mon);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qmp_async_info_handler(Monitor *mon, const mon_cmd_t *cmd)
|
|
|
|
{
|
|
|
|
cmd->mhandler.info_async(mon, qmp_monitor_complete, mon);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void user_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd,
|
|
|
|
const QDict *params)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
MonitorCompletionData *cb_data = qemu_malloc(sizeof(*cb_data));
|
|
|
|
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);
|
|
|
|
qemu_free(cb_data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void user_async_info_handler(Monitor *mon, const mon_cmd_t *cmd)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
MonitorCompletionData *cb_data = qemu_malloc(sizeof(*cb_data));
|
|
|
|
cb_data->mon = mon;
|
|
|
|
cb_data->user_print = cmd->user_print;
|
|
|
|
monitor_suspend(mon);
|
|
|
|
ret = cmd->mhandler.info_async(mon, user_monitor_complete, cb_data);
|
|
|
|
if (ret < 0) {
|
|
|
|
monitor_resume(mon);
|
|
|
|
qemu_free(cb_data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-08 00:41:55 +08:00
|
|
|
static void do_info(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
2004-03-15 05:38:27 +08:00
|
|
|
{
|
2009-10-02 05:12:16 +08:00
|
|
|
const mon_cmd_t *cmd;
|
2009-08-29 02:27:13 +08:00
|
|
|
const char *item = qdict_get_try_str(qdict, "item");
|
2004-03-15 05:38:27 +08:00
|
|
|
|
2009-11-27 08:59:00 +08:00
|
|
|
if (!item) {
|
|
|
|
assert(monitor_ctrl_mode(mon) == 0);
|
2004-03-15 05:38:27 +08:00
|
|
|
goto help;
|
2009-11-27 08:59:00 +08:00
|
|
|
}
|
2009-10-08 00:41:55 +08:00
|
|
|
|
|
|
|
for (cmd = info_cmds; cmd->name != NULL; cmd++) {
|
2007-09-17 05:08:06 +08:00
|
|
|
if (compare_cmd(item, cmd->name))
|
2009-10-08 00:41:55 +08:00
|
|
|
break;
|
2004-03-15 05:38:27 +08:00
|
|
|
}
|
2009-10-08 00:41:55 +08:00
|
|
|
|
2009-11-27 08:59:00 +08:00
|
|
|
if (cmd->name == NULL) {
|
|
|
|
if (monitor_ctrl_mode(mon)) {
|
|
|
|
qemu_error_new(QERR_COMMAND_NOT_FOUND, item);
|
|
|
|
return;
|
|
|
|
}
|
2009-10-08 00:41:55 +08:00
|
|
|
goto help;
|
2009-11-27 08:59:00 +08:00
|
|
|
}
|
2009-10-08 00:41:55 +08:00
|
|
|
|
2010-01-26 02:18:44 +08:00
|
|
|
if (monitor_handler_is_async(cmd)) {
|
|
|
|
if (monitor_ctrl_mode(mon)) {
|
|
|
|
qmp_async_info_handler(mon, cmd);
|
|
|
|
} else {
|
|
|
|
user_async_info_handler(mon, cmd);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Indicate that this command is asynchronous and will not return any
|
|
|
|
* data (not even empty). Instead, the data will be returned via a
|
|
|
|
* completion callback.
|
|
|
|
*/
|
|
|
|
*ret_data = qobject_from_jsonf("{ '__mon_async': 'return' }");
|
|
|
|
} else if (monitor_handler_ported(cmd)) {
|
2009-10-08 00:41:55 +08:00
|
|
|
cmd->mhandler.info_new(mon, ret_data);
|
2009-11-27 08:58:59 +08:00
|
|
|
|
|
|
|
if (!monitor_ctrl_mode(mon)) {
|
|
|
|
/*
|
|
|
|
* User Protocol function is called here, Monitor Protocol is
|
|
|
|
* handled by monitor_call_handler()
|
|
|
|
*/
|
|
|
|
if (*ret_data)
|
|
|
|
cmd->user_print(mon, *ret_data);
|
|
|
|
}
|
2009-10-08 00:41:55 +08:00
|
|
|
} else {
|
2009-11-27 08:59:00 +08:00
|
|
|
if (monitor_ctrl_mode(mon)) {
|
|
|
|
/* handler not converted yet */
|
|
|
|
qemu_error_new(QERR_COMMAND_NOT_FOUND, item);
|
|
|
|
} else {
|
|
|
|
cmd->mhandler.info(mon);
|
|
|
|
}
|
2009-10-08 00:41:55 +08:00
|
|
|
}
|
|
|
|
|
2004-03-15 05:38:27 +08:00
|
|
|
return;
|
2009-10-08 00:41:55 +08:00
|
|
|
|
|
|
|
help:
|
|
|
|
help_cmd(mon, "info");
|
2004-03-15 05:38:27 +08:00
|
|
|
}
|
|
|
|
|
2009-12-11 03:15:58 +08:00
|
|
|
static void do_info_version_print(Monitor *mon, const QObject *data)
|
|
|
|
{
|
|
|
|
QDict *qdict;
|
|
|
|
|
|
|
|
qdict = qobject_to_qdict(data);
|
|
|
|
|
|
|
|
monitor_printf(mon, "%s%s\n", qdict_get_str(qdict, "qemu"),
|
|
|
|
qdict_get_str(qdict, "package"));
|
|
|
|
}
|
|
|
|
|
2009-10-08 00:42:02 +08:00
|
|
|
/**
|
|
|
|
* do_info_version(): Show QEMU version
|
2009-12-11 03:15:58 +08:00
|
|
|
*
|
|
|
|
* Return a QDict with the following information:
|
|
|
|
*
|
|
|
|
* - "qemu": QEMU's version
|
|
|
|
* - "package": package's version
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
* { "qemu": "0.11.50", "package": "" }
|
2009-10-08 00:42:02 +08:00
|
|
|
*/
|
|
|
|
static void do_info_version(Monitor *mon, QObject **ret_data)
|
2004-10-10 23:15:51 +08:00
|
|
|
{
|
2009-12-11 03:15:58 +08:00
|
|
|
*ret_data = qobject_from_jsonf("{ 'qemu': %s, 'package': %s }",
|
|
|
|
QEMU_VERSION, QEMU_PKGVERSION);
|
2004-10-10 23:15:51 +08:00
|
|
|
}
|
|
|
|
|
2009-12-11 03:16:01 +08:00
|
|
|
static void do_info_name_print(Monitor *mon, const QObject *data)
|
2007-03-19 23:17:08 +08:00
|
|
|
{
|
2009-12-11 03:16:01 +08:00
|
|
|
QDict *qdict;
|
|
|
|
|
|
|
|
qdict = qobject_to_qdict(data);
|
|
|
|
if (qdict_size(qdict) == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
monitor_printf(mon, "%s\n", qdict_get_str(qdict, "name"));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* do_info_name(): Show VM name
|
|
|
|
*
|
|
|
|
* Return a QDict with the following information:
|
|
|
|
*
|
|
|
|
* - "name": VM's name (optional)
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
* { "name": "qemu-name" }
|
|
|
|
*/
|
|
|
|
static void do_info_name(Monitor *mon, QObject **ret_data)
|
|
|
|
{
|
|
|
|
*ret_data = qemu_name ? qobject_from_jsonf("{'name': %s }", qemu_name) :
|
|
|
|
qobject_from_jsonf("{}");
|
2007-03-19 23:17:08 +08:00
|
|
|
}
|
|
|
|
|
2009-12-11 03:15:56 +08:00
|
|
|
static QObject *get_cmd_dict(const char *name)
|
|
|
|
{
|
|
|
|
const char *p;
|
|
|
|
|
|
|
|
/* Remove '|' from some commands */
|
|
|
|
p = strchr(name, '|');
|
|
|
|
if (p) {
|
|
|
|
p++;
|
|
|
|
} else {
|
|
|
|
p = name;
|
|
|
|
}
|
|
|
|
|
|
|
|
return qobject_from_jsonf("{ 'name': %s }", p);
|
|
|
|
}
|
|
|
|
|
2009-11-27 08:58:56 +08:00
|
|
|
/**
|
|
|
|
* do_info_commands(): List QMP available commands
|
|
|
|
*
|
2009-12-11 03:15:56 +08:00
|
|
|
* Each command is represented by a QDict, the returned QObject is a QList
|
|
|
|
* of all commands.
|
|
|
|
*
|
|
|
|
* The QDict contains:
|
|
|
|
*
|
|
|
|
* - "name": command's name
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
* { [ { "name": "query-balloon" }, { "name": "system_powerdown" } ] }
|
2009-11-27 08:58:56 +08:00
|
|
|
*/
|
|
|
|
static void do_info_commands(Monitor *mon, QObject **ret_data)
|
|
|
|
{
|
|
|
|
QList *cmd_list;
|
|
|
|
const mon_cmd_t *cmd;
|
|
|
|
|
|
|
|
cmd_list = qlist_new();
|
|
|
|
|
|
|
|
for (cmd = mon_cmds; cmd->name != NULL; cmd++) {
|
|
|
|
if (monitor_handler_ported(cmd) && !compare_cmd(cmd->name, "info")) {
|
2009-12-11 03:15:56 +08:00
|
|
|
qlist_append_obj(cmd_list, get_cmd_dict(cmd->name));
|
2009-11-27 08:58:56 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (cmd = info_cmds; cmd->name != NULL; cmd++) {
|
|
|
|
if (monitor_handler_ported(cmd)) {
|
|
|
|
char buf[128];
|
|
|
|
snprintf(buf, sizeof(buf), "query-%s", cmd->name);
|
2009-12-11 03:15:56 +08:00
|
|
|
qlist_append_obj(cmd_list, get_cmd_dict(buf));
|
2009-11-27 08:58:56 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*ret_data = QOBJECT(cmd_list);
|
|
|
|
}
|
|
|
|
|
2008-12-19 06:42:34 +08:00
|
|
|
#if defined(TARGET_I386)
|
2009-12-11 03:16:02 +08:00
|
|
|
static void do_info_hpet_print(Monitor *mon, const QObject *data)
|
2008-12-18 07:28:44 +08:00
|
|
|
{
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "HPET is %s by QEMU\n",
|
2009-12-11 03:16:02 +08:00
|
|
|
qdict_get_bool(qobject_to_qdict(data), "enabled") ?
|
|
|
|
"enabled" : "disabled");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* do_info_hpet(): Show HPET state
|
|
|
|
*
|
|
|
|
* Return a QDict with the following information:
|
|
|
|
*
|
|
|
|
* - "enabled": true if hpet if enabled, false otherwise
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
* { "enabled": true }
|
|
|
|
*/
|
|
|
|
static void do_info_hpet(Monitor *mon, QObject **ret_data)
|
|
|
|
{
|
|
|
|
*ret_data = qobject_from_jsonf("{ 'enabled': %i }", !no_hpet);
|
2008-12-18 07:28:44 +08:00
|
|
|
}
|
2008-12-19 06:42:34 +08:00
|
|
|
#endif
|
2008-12-18 07:28:44 +08:00
|
|
|
|
2009-12-11 03:16:03 +08:00
|
|
|
static void do_info_uuid_print(Monitor *mon, const QObject *data)
|
2007-12-02 13:18:19 +08:00
|
|
|
{
|
2009-12-11 03:16:03 +08:00
|
|
|
monitor_printf(mon, "%s\n", qdict_get_str(qobject_to_qdict(data), "UUID"));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* do_info_uuid(): Show VM UUID
|
|
|
|
*
|
|
|
|
* Return a QDict with the following information:
|
|
|
|
*
|
|
|
|
* - "UUID": Universally Unique Identifier
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
* { "UUID": "550e8400-e29b-41d4-a716-446655440000" }
|
|
|
|
*/
|
|
|
|
static void do_info_uuid(Monitor *mon, QObject **ret_data)
|
|
|
|
{
|
|
|
|
char uuid[64];
|
|
|
|
|
|
|
|
snprintf(uuid, sizeof(uuid), UUID_FMT, qemu_uuid[0], qemu_uuid[1],
|
2009-03-06 07:01:23 +08:00
|
|
|
qemu_uuid[2], qemu_uuid[3], qemu_uuid[4], qemu_uuid[5],
|
|
|
|
qemu_uuid[6], qemu_uuid[7], qemu_uuid[8], qemu_uuid[9],
|
|
|
|
qemu_uuid[10], qemu_uuid[11], qemu_uuid[12], qemu_uuid[13],
|
|
|
|
qemu_uuid[14], qemu_uuid[15]);
|
2009-12-11 03:16:03 +08:00
|
|
|
*ret_data = qobject_from_jsonf("{ 'UUID': %s }", uuid);
|
2007-12-02 13:18:19 +08:00
|
|
|
}
|
|
|
|
|
2005-11-22 07:25:50 +08:00
|
|
|
/* get the current CPU defined by the user */
|
2007-11-18 09:44:38 +08:00
|
|
|
static int mon_set_cpu(int cpu_index)
|
2005-11-22 07:25:50 +08:00
|
|
|
{
|
|
|
|
CPUState *env;
|
|
|
|
|
|
|
|
for(env = first_cpu; env != NULL; env = env->next_cpu) {
|
|
|
|
if (env->cpu_index == cpu_index) {
|
2009-03-06 07:01:42 +08:00
|
|
|
cur_mon->mon_cpu = env;
|
2005-11-22 07:25:50 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-11-18 09:44:38 +08:00
|
|
|
static CPUState *mon_get_cpu(void)
|
2005-11-22 07:25:50 +08:00
|
|
|
{
|
2009-03-06 07:01:42 +08:00
|
|
|
if (!cur_mon->mon_cpu) {
|
2005-11-22 07:25:50 +08:00
|
|
|
mon_set_cpu(0);
|
|
|
|
}
|
2009-08-18 04:19:53 +08:00
|
|
|
cpu_synchronize_state(cur_mon->mon_cpu);
|
2009-03-06 07:01:42 +08:00
|
|
|
return cur_mon->mon_cpu;
|
2005-11-22 07:25:50 +08:00
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
static void do_info_registers(Monitor *mon)
|
2004-04-04 20:57:25 +08:00
|
|
|
{
|
2005-11-22 07:25:50 +08:00
|
|
|
CPUState *env;
|
|
|
|
env = mon_get_cpu();
|
2004-04-04 20:57:25 +08:00
|
|
|
#ifdef TARGET_I386
|
2009-03-06 07:01:23 +08:00
|
|
|
cpu_dump_state(env, (FILE *)mon, monitor_fprintf,
|
2005-07-04 05:28:00 +08:00
|
|
|
X86_DUMP_FPU);
|
2004-04-04 20:57:25 +08:00
|
|
|
#else
|
2009-03-06 07:01:23 +08:00
|
|
|
cpu_dump_state(env, (FILE *)mon, monitor_fprintf,
|
2004-10-10 02:08:01 +08:00
|
|
|
0);
|
2004-04-04 20:57:25 +08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2009-10-08 00:42:04 +08:00
|
|
|
static void print_cpu_iter(QObject *obj, void *opaque)
|
|
|
|
{
|
|
|
|
QDict *cpu;
|
|
|
|
int active = ' ';
|
|
|
|
Monitor *mon = opaque;
|
|
|
|
|
|
|
|
assert(qobject_type(obj) == QTYPE_QDICT);
|
|
|
|
cpu = qobject_to_qdict(obj);
|
|
|
|
|
2009-12-11 03:15:57 +08:00
|
|
|
if (qdict_get_bool(cpu, "current")) {
|
2009-10-08 00:42:04 +08:00
|
|
|
active = '*';
|
2009-12-11 03:15:57 +08:00
|
|
|
}
|
2009-10-08 00:42:04 +08:00
|
|
|
|
|
|
|
monitor_printf(mon, "%c CPU #%d: ", active, (int)qdict_get_int(cpu, "CPU"));
|
|
|
|
|
|
|
|
#if defined(TARGET_I386)
|
|
|
|
monitor_printf(mon, "pc=0x" TARGET_FMT_lx,
|
|
|
|
(target_ulong) qdict_get_int(cpu, "pc"));
|
|
|
|
#elif defined(TARGET_PPC)
|
|
|
|
monitor_printf(mon, "nip=0x" TARGET_FMT_lx,
|
|
|
|
(target_long) qdict_get_int(cpu, "nip"));
|
|
|
|
#elif defined(TARGET_SPARC)
|
|
|
|
monitor_printf(mon, "pc=0x " TARGET_FMT_lx,
|
|
|
|
(target_long) qdict_get_int(cpu, "pc"));
|
|
|
|
monitor_printf(mon, "npc=0x" TARGET_FMT_lx,
|
|
|
|
(target_long) qdict_get_int(cpu, "npc"));
|
|
|
|
#elif defined(TARGET_MIPS)
|
|
|
|
monitor_printf(mon, "PC=0x" TARGET_FMT_lx,
|
|
|
|
(target_long) qdict_get_int(cpu, "PC"));
|
|
|
|
#endif
|
|
|
|
|
2009-12-11 03:15:57 +08:00
|
|
|
if (qdict_get_bool(cpu, "halted")) {
|
2009-10-08 00:42:04 +08:00
|
|
|
monitor_printf(mon, " (halted)");
|
2009-12-11 03:15:57 +08:00
|
|
|
}
|
2009-10-08 00:42:04 +08:00
|
|
|
|
|
|
|
monitor_printf(mon, "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void monitor_print_cpus(Monitor *mon, const QObject *data)
|
|
|
|
{
|
|
|
|
QList *cpu_list;
|
|
|
|
|
|
|
|
assert(qobject_type(data) == QTYPE_QLIST);
|
|
|
|
cpu_list = qobject_to_qlist(data);
|
|
|
|
qlist_iter(cpu_list, print_cpu_iter, mon);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* do_info_cpus(): Show CPU information
|
|
|
|
*
|
2009-12-11 03:15:57 +08:00
|
|
|
* Return a QList. Each CPU is represented by a QDict, which contains:
|
2009-10-08 00:42:04 +08:00
|
|
|
*
|
2009-12-11 03:15:57 +08:00
|
|
|
* - "cpu": CPU index
|
|
|
|
* - "current": true if this is the current CPU, false otherwise
|
|
|
|
* - "halted": true if the cpu is halted, false otherwise
|
|
|
|
* - Current program counter. The key's name depends on the architecture:
|
|
|
|
* "pc": i386/x86)64
|
|
|
|
* "nip": PPC
|
|
|
|
* "pc" and "npc": sparc
|
|
|
|
* "PC": mips
|
2009-10-08 00:42:04 +08:00
|
|
|
*
|
2009-12-11 03:15:57 +08:00
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
* [ { "CPU": 0, "current": true, "halted": false, "pc": 3227107138 },
|
|
|
|
* { "CPU": 1, "current": false, "halted": true, "pc": 7108165 } ]
|
2009-10-08 00:42:04 +08:00
|
|
|
*/
|
|
|
|
static void do_info_cpus(Monitor *mon, QObject **ret_data)
|
2005-11-22 07:25:50 +08:00
|
|
|
{
|
|
|
|
CPUState *env;
|
2009-10-08 00:42:04 +08:00
|
|
|
QList *cpu_list;
|
|
|
|
|
|
|
|
cpu_list = qlist_new();
|
2005-11-22 07:25:50 +08:00
|
|
|
|
|
|
|
/* just to set the default cpu if not already done */
|
|
|
|
mon_get_cpu();
|
|
|
|
|
|
|
|
for(env = first_cpu; env != NULL; env = env->next_cpu) {
|
2009-12-11 03:15:57 +08:00
|
|
|
QDict *cpu;
|
|
|
|
QObject *obj;
|
2009-10-08 00:42:04 +08:00
|
|
|
|
2009-08-18 04:19:53 +08:00
|
|
|
cpu_synchronize_state(env);
|
2009-10-08 00:42:04 +08:00
|
|
|
|
2009-12-11 03:15:57 +08:00
|
|
|
obj = qobject_from_jsonf("{ 'CPU': %d, 'current': %i, 'halted': %i }",
|
|
|
|
env->cpu_index, env == mon->mon_cpu,
|
|
|
|
env->halted);
|
|
|
|
assert(obj != NULL);
|
|
|
|
|
|
|
|
cpu = qobject_to_qdict(obj);
|
2009-10-08 00:42:04 +08:00
|
|
|
|
2005-11-22 07:25:50 +08:00
|
|
|
#if defined(TARGET_I386)
|
2009-10-08 00:42:04 +08:00
|
|
|
qdict_put(cpu, "pc", qint_from_int(env->eip + env->segs[R_CS].base));
|
2005-11-24 06:05:28 +08:00
|
|
|
#elif defined(TARGET_PPC)
|
2009-10-08 00:42:04 +08:00
|
|
|
qdict_put(cpu, "nip", qint_from_int(env->nip));
|
2005-12-06 04:31:52 +08:00
|
|
|
#elif defined(TARGET_SPARC)
|
2009-10-08 00:42:04 +08:00
|
|
|
qdict_put(cpu, "pc", qint_from_int(env->pc));
|
|
|
|
qdict_put(cpu, "npc", qint_from_int(env->npc));
|
2007-09-06 08:18:15 +08:00
|
|
|
#elif defined(TARGET_MIPS)
|
2009-10-08 00:42:04 +08:00
|
|
|
qdict_put(cpu, "PC", qint_from_int(env->active_tc.PC));
|
2008-05-29 01:14:10 +08:00
|
|
|
#endif
|
2009-10-08 00:42:04 +08:00
|
|
|
|
|
|
|
qlist_append(cpu_list, cpu);
|
2005-11-22 07:25:50 +08:00
|
|
|
}
|
2009-10-08 00:42:04 +08:00
|
|
|
|
|
|
|
*ret_data = QOBJECT(cpu_list);
|
2005-11-22 07:25:50 +08:00
|
|
|
}
|
|
|
|
|
2010-01-20 20:07:35 +08:00
|
|
|
static void do_cpu_set(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
2005-11-22 07:25:50 +08:00
|
|
|
{
|
2009-08-29 02:27:13 +08:00
|
|
|
int index = qdict_get_int(qdict, "index");
|
2005-11-22 07:25:50 +08:00
|
|
|
if (mon_set_cpu(index) < 0)
|
2010-01-20 20:07:35 +08:00
|
|
|
qemu_error_new(QERR_INVALID_CPU_INDEX);
|
2005-11-22 07:25:50 +08:00
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
static void do_info_jit(Monitor *mon)
|
2005-01-27 06:00:47 +08:00
|
|
|
{
|
2009-03-06 07:01:23 +08:00
|
|
|
dump_exec_info((FILE *)mon, monitor_fprintf);
|
2005-01-27 06:00:47 +08:00
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
static void do_info_history(Monitor *mon)
|
2004-04-04 21:07:25 +08:00
|
|
|
{
|
|
|
|
int i;
|
2004-08-02 05:52:19 +08:00
|
|
|
const char *str;
|
2007-09-17 16:09:54 +08:00
|
|
|
|
2009-03-06 07:01:51 +08:00
|
|
|
if (!mon->rs)
|
|
|
|
return;
|
2004-08-02 05:52:19 +08:00
|
|
|
i = 0;
|
|
|
|
for(;;) {
|
2009-03-06 07:01:42 +08:00
|
|
|
str = readline_get_history(mon->rs, i);
|
2004-08-02 05:52:19 +08:00
|
|
|
if (!str)
|
|
|
|
break;
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "%d: '%s'\n", i, str);
|
2004-10-10 01:32:58 +08:00
|
|
|
i++;
|
2004-04-04 21:07:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-07 16:32:30 +08:00
|
|
|
#if defined(TARGET_PPC)
|
|
|
|
/* XXX: not implemented in other targets */
|
2009-03-06 07:01:23 +08:00
|
|
|
static void do_info_cpu_stats(Monitor *mon)
|
2007-03-07 16:32:30 +08:00
|
|
|
{
|
|
|
|
CPUState *env;
|
|
|
|
|
|
|
|
env = mon_get_cpu();
|
2009-03-06 07:01:23 +08:00
|
|
|
cpu_dump_statistics(env, (FILE *)mon, &monitor_fprintf, 0);
|
2007-03-07 16:32:30 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-10-08 00:41:56 +08:00
|
|
|
/**
|
|
|
|
* do_quit(): Quit QEMU execution
|
|
|
|
*/
|
|
|
|
static void do_quit(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
2004-03-15 05:38:27 +08:00
|
|
|
{
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
static int eject_device(Monitor *mon, BlockDriverState *bs, int force)
|
2004-03-15 05:38:27 +08:00
|
|
|
{
|
|
|
|
if (bdrv_is_inserted(bs)) {
|
|
|
|
if (!force) {
|
|
|
|
if (!bdrv_is_removable(bs)) {
|
2009-12-08 04:37:05 +08:00
|
|
|
qemu_error_new(QERR_DEVICE_NOT_REMOVABLE,
|
|
|
|
bdrv_get_device_name(bs));
|
2004-03-15 05:38:27 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (bdrv_is_locked(bs)) {
|
2009-12-08 04:37:05 +08:00
|
|
|
qemu_error_new(QERR_DEVICE_LOCKED, bdrv_get_device_name(bs));
|
2004-03-15 05:38:27 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bdrv_close(bs);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-10-16 23:23:49 +08:00
|
|
|
static void do_eject(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
2004-03-15 05:38:27 +08:00
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
2009-08-29 02:27:14 +08:00
|
|
|
int force = qdict_get_int(qdict, "force");
|
2009-12-15 04:53:21 +08:00
|
|
|
const char *filename = qdict_get_str(qdict, "device");
|
2004-03-15 05:38:27 +08:00
|
|
|
|
2004-04-04 20:57:25 +08:00
|
|
|
bs = bdrv_find(filename);
|
2004-03-15 05:38:27 +08:00
|
|
|
if (!bs) {
|
2009-12-08 04:37:05 +08:00
|
|
|
qemu_error_new(QERR_DEVICE_NOT_FOUND, filename);
|
2004-03-15 05:38:27 +08:00
|
|
|
return;
|
|
|
|
}
|
2009-03-06 07:01:23 +08:00
|
|
|
eject_device(mon, bs, force);
|
2004-03-15 05:38:27 +08:00
|
|
|
}
|
|
|
|
|
2009-12-05 01:24:09 +08:00
|
|
|
static void do_block_set_passwd(Monitor *mon, const QDict *qdict,
|
|
|
|
QObject **ret_data)
|
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
|
|
|
|
bs = bdrv_find(qdict_get_str(qdict, "device"));
|
|
|
|
if (!bs) {
|
|
|
|
qemu_error_new(QERR_DEVICE_NOT_FOUND, qdict_get_str(qdict, "device"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bdrv_set_key(bs, qdict_get_str(qdict, "password")) < 0) {
|
|
|
|
qemu_error_new(QERR_INVALID_PASSWORD);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
static void do_change_block(Monitor *mon, const char *device,
|
|
|
|
const char *filename, const char *fmt)
|
2004-03-15 05:38:27 +08:00
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
2008-06-19 06:10:01 +08:00
|
|
|
BlockDriver *drv = NULL;
|
2004-03-15 05:38:27 +08:00
|
|
|
|
2004-04-04 20:57:25 +08:00
|
|
|
bs = bdrv_find(device);
|
2004-03-15 05:38:27 +08:00
|
|
|
if (!bs) {
|
2009-12-08 04:37:09 +08:00
|
|
|
qemu_error_new(QERR_DEVICE_NOT_FOUND, device);
|
2004-03-15 05:38:27 +08:00
|
|
|
return;
|
|
|
|
}
|
2008-06-19 06:10:01 +08:00
|
|
|
if (fmt) {
|
2009-10-28 01:41:44 +08:00
|
|
|
drv = bdrv_find_whitelisted_format(fmt);
|
2008-06-19 06:10:01 +08:00
|
|
|
if (!drv) {
|
2009-12-08 04:37:09 +08:00
|
|
|
qemu_error_new(QERR_INVALID_BLOCK_FORMAT, fmt);
|
2008-06-19 06:10:01 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2009-03-06 07:01:23 +08:00
|
|
|
if (eject_device(mon, bs, 0) < 0)
|
2004-03-15 05:38:27 +08:00
|
|
|
return;
|
2010-01-17 22:48:13 +08:00
|
|
|
bdrv_open2(bs, filename, BDRV_O_RDWR, drv);
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_read_bdrv_key_start(mon, bs, NULL, NULL);
|
2009-03-06 07:01:15 +08:00
|
|
|
}
|
|
|
|
|
2009-12-08 04:37:09 +08:00
|
|
|
static void change_vnc_password(const char *password)
|
2009-03-06 07:01:15 +08:00
|
|
|
{
|
|
|
|
if (vnc_display_password(NULL, password) < 0)
|
2009-12-08 04:37:09 +08:00
|
|
|
qemu_error_new(QERR_SET_PASSWD_FAILED);
|
2009-03-06 07:01:15 +08:00
|
|
|
|
2009-12-08 04:37:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void change_vnc_password_cb(Monitor *mon, const char *password,
|
|
|
|
void *opaque)
|
|
|
|
{
|
2009-12-08 04:37:09 +08:00
|
|
|
change_vnc_password(password);
|
2009-03-06 07:01:42 +08:00
|
|
|
monitor_read_command(mon, 1);
|
2004-03-15 05:38:27 +08:00
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
static void do_change_vnc(Monitor *mon, const char *target, const char *arg)
|
2007-08-25 09:36:20 +08:00
|
|
|
{
|
2007-08-25 09:37:05 +08:00
|
|
|
if (strcmp(target, "passwd") == 0 ||
|
2009-03-07 04:27:40 +08:00
|
|
|
strcmp(target, "password") == 0) {
|
|
|
|
if (arg) {
|
2009-03-06 07:01:15 +08:00
|
|
|
char password[9];
|
2009-03-07 04:27:40 +08:00
|
|
|
strncpy(password, arg, sizeof(password));
|
|
|
|
password[sizeof(password) - 1] = '\0';
|
2009-12-08 04:37:09 +08:00
|
|
|
change_vnc_password(password);
|
2009-03-06 07:01:15 +08:00
|
|
|
} else {
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_read_password(mon, change_vnc_password_cb, NULL);
|
2009-03-06 07:01:15 +08:00
|
|
|
}
|
2007-08-25 09:37:05 +08:00
|
|
|
} else {
|
2009-03-07 04:27:40 +08:00
|
|
|
if (vnc_display_open(NULL, target) < 0)
|
2009-12-08 04:37:09 +08:00
|
|
|
qemu_error_new(QERR_VNC_SERVER_FAILED, target);
|
2007-08-25 09:37:05 +08:00
|
|
|
}
|
2007-08-25 09:36:20 +08:00
|
|
|
}
|
|
|
|
|
2009-12-08 04:37:09 +08:00
|
|
|
/**
|
|
|
|
* do_change(): Change a removable medium, or VNC configuration
|
|
|
|
*/
|
|
|
|
static void do_change(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
2007-08-25 09:36:20 +08:00
|
|
|
{
|
2009-08-29 02:27:15 +08:00
|
|
|
const char *device = qdict_get_str(qdict, "device");
|
|
|
|
const char *target = qdict_get_str(qdict, "target");
|
|
|
|
const char *arg = qdict_get_try_str(qdict, "arg");
|
2007-08-25 09:36:20 +08:00
|
|
|
if (strcmp(device, "vnc") == 0) {
|
2009-03-07 04:27:40 +08:00
|
|
|
do_change_vnc(mon, target, arg);
|
2007-08-25 09:36:20 +08:00
|
|
|
} else {
|
2009-03-07 04:27:40 +08:00
|
|
|
do_change_block(mon, device, target, arg);
|
2007-08-25 09:36:20 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-29 02:27:13 +08:00
|
|
|
static void do_screen_dump(Monitor *mon, const QDict *qdict)
|
2004-03-18 07:17:16 +08:00
|
|
|
{
|
2009-08-29 02:27:13 +08:00
|
|
|
vga_hw_screen_dump(qdict_get_str(qdict, "filename"));
|
2004-03-18 07:17:16 +08:00
|
|
|
}
|
|
|
|
|
2009-08-29 02:27:13 +08:00
|
|
|
static void do_logfile(Monitor *mon, const QDict *qdict)
|
2007-06-30 21:53:24 +08:00
|
|
|
{
|
2009-08-29 02:27:13 +08:00
|
|
|
cpu_set_log_filename(qdict_get_str(qdict, "filename"));
|
2007-06-30 21:53:24 +08:00
|
|
|
}
|
|
|
|
|
2009-08-29 02:27:13 +08:00
|
|
|
static void do_log(Monitor *mon, const QDict *qdict)
|
2004-03-22 01:06:25 +08:00
|
|
|
{
|
|
|
|
int mask;
|
2009-08-29 02:27:13 +08:00
|
|
|
const char *items = qdict_get_str(qdict, "items");
|
2007-09-17 16:09:54 +08:00
|
|
|
|
2004-04-04 20:57:25 +08:00
|
|
|
if (!strcmp(items, "none")) {
|
2004-03-22 01:06:25 +08:00
|
|
|
mask = 0;
|
|
|
|
} else {
|
2004-04-04 20:57:25 +08:00
|
|
|
mask = cpu_str_to_log_mask(items);
|
2004-03-22 01:06:25 +08:00
|
|
|
if (!mask) {
|
2009-03-06 07:01:23 +08:00
|
|
|
help_cmd(mon, "log");
|
2004-03-22 01:06:25 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cpu_set_log(mask);
|
|
|
|
}
|
|
|
|
|
2009-08-29 02:27:13 +08:00
|
|
|
static void do_singlestep(Monitor *mon, const QDict *qdict)
|
2009-04-06 04:08:59 +08:00
|
|
|
{
|
2009-08-29 02:27:13 +08:00
|
|
|
const char *option = qdict_get_try_str(qdict, "option");
|
2009-04-06 04:08:59 +08: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-10-08 00:41:57 +08:00
|
|
|
/**
|
|
|
|
* do_stop(): Stop VM execution
|
|
|
|
*/
|
|
|
|
static void do_stop(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
2004-04-01 03:00:16 +08:00
|
|
|
{
|
|
|
|
vm_stop(EXCP_INTERRUPT);
|
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:15 +08:00
|
|
|
static void encrypted_bdrv_it(void *opaque, BlockDriverState *bs);
|
2009-03-06 07:01:01 +08:00
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
struct bdrv_iterate_context {
|
|
|
|
Monitor *mon;
|
|
|
|
int err;
|
|
|
|
};
|
|
|
|
|
2009-10-08 00:42:00 +08:00
|
|
|
/**
|
|
|
|
* do_cont(): Resume emulation.
|
|
|
|
*/
|
|
|
|
static void do_cont(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
2004-04-01 03:00:16 +08:00
|
|
|
{
|
2009-03-06 07:01:23 +08:00
|
|
|
struct bdrv_iterate_context context = { mon, 0 };
|
2009-03-06 07:01:01 +08:00
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
bdrv_iterate(encrypted_bdrv_it, &context);
|
2009-03-06 07:01:01 +08:00
|
|
|
/* only resume the vm if all keys are set and valid */
|
2009-03-06 07:01:23 +08:00
|
|
|
if (!context.err)
|
2009-03-06 07:01:01 +08:00
|
|
|
vm_start();
|
2004-04-01 03:00:16 +08:00
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:15 +08:00
|
|
|
static void bdrv_key_cb(void *opaque, int err)
|
|
|
|
{
|
2009-03-06 07:01:23 +08:00
|
|
|
Monitor *mon = opaque;
|
|
|
|
|
2009-03-06 07:01:15 +08:00
|
|
|
/* another key was set successfully, retry to continue */
|
|
|
|
if (!err)
|
2009-10-08 00:42:00 +08:00
|
|
|
do_cont(mon, NULL, NULL);
|
2009-03-06 07:01:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void encrypted_bdrv_it(void *opaque, BlockDriverState *bs)
|
|
|
|
{
|
2009-03-06 07:01:23 +08:00
|
|
|
struct bdrv_iterate_context *context = opaque;
|
2009-03-06 07:01:15 +08:00
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
if (!context->err && bdrv_key_required(bs)) {
|
|
|
|
context->err = -EBUSY;
|
|
|
|
monitor_read_bdrv_key_start(context->mon, bs, bdrv_key_cb,
|
|
|
|
context->mon);
|
2009-03-06 07:01:15 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-29 02:27:13 +08:00
|
|
|
static void do_gdbserver(Monitor *mon, const QDict *qdict)
|
2009-04-06 02:43:41 +08:00
|
|
|
{
|
2009-08-29 02:27:13 +08:00
|
|
|
const char *device = qdict_get_try_str(qdict, "device");
|
2009-04-06 02:43:41 +08: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-29 02:05:53 +08:00
|
|
|
monitor_printf(mon, "Disabled gdbserver\n");
|
2004-04-01 03:00:16 +08:00
|
|
|
} else {
|
2009-04-06 02:43:41 +08:00
|
|
|
monitor_printf(mon, "Waiting for gdb connection on device '%s'\n",
|
|
|
|
device);
|
2004-04-01 03:00:16 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-29 02:27:13 +08:00
|
|
|
static void do_watchdog_action(Monitor *mon, const QDict *qdict)
|
2009-04-25 20:56:19 +08:00
|
|
|
{
|
2009-08-29 02:27:13 +08:00
|
|
|
const char *action = qdict_get_str(qdict, "action");
|
2009-04-25 20:56:19 +08:00
|
|
|
if (select_watchdog_action(action) == -1) {
|
|
|
|
monitor_printf(mon, "Unknown watchdog action '%s'\n", action);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
static void monitor_printc(Monitor *mon, int c)
|
2004-04-04 20:57:25 +08:00
|
|
|
{
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "'");
|
2004-04-04 20:57:25 +08:00
|
|
|
switch(c) {
|
|
|
|
case '\'':
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "\\'");
|
2004-04-04 20:57:25 +08:00
|
|
|
break;
|
|
|
|
case '\\':
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "\\\\");
|
2004-04-04 20:57:25 +08:00
|
|
|
break;
|
|
|
|
case '\n':
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "\\n");
|
2004-04-04 20:57:25 +08:00
|
|
|
break;
|
|
|
|
case '\r':
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "\\r");
|
2004-04-04 20:57:25 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (c >= 32 && c <= 126) {
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "%c", c);
|
2004-04-04 20:57:25 +08:00
|
|
|
} else {
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "\\x%02x", c);
|
2004-04-04 20:57:25 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "'");
|
2004-04-04 20:57:25 +08:00
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
static void memory_dump(Monitor *mon, int count, int format, int wsize,
|
2009-10-02 05:12:16 +08:00
|
|
|
target_phys_addr_t addr, int is_physical)
|
2004-04-04 20:57:25 +08:00
|
|
|
{
|
2005-11-22 07:25:50 +08:00
|
|
|
CPUState *env;
|
2010-01-13 04:27:43 +08:00
|
|
|
int l, line_size, i, max_digits, len;
|
2004-04-04 20:57:25 +08:00
|
|
|
uint8_t buf[16];
|
|
|
|
uint64_t v;
|
|
|
|
|
|
|
|
if (format == 'i') {
|
|
|
|
int flags;
|
|
|
|
flags = 0;
|
2005-11-22 07:25:50 +08:00
|
|
|
env = mon_get_cpu();
|
2010-01-20 20:07:30 +08:00
|
|
|
if (!is_physical)
|
2005-11-22 07:25:50 +08:00
|
|
|
return;
|
2004-04-04 20:57:25 +08:00
|
|
|
#ifdef TARGET_I386
|
2004-04-26 02:05:08 +08:00
|
|
|
if (wsize == 2) {
|
2004-04-04 20:57:25 +08:00
|
|
|
flags = 1;
|
2004-04-26 02:05:08 +08:00
|
|
|
} else if (wsize == 4) {
|
|
|
|
flags = 0;
|
|
|
|
} else {
|
2006-04-13 05:07:07 +08:00
|
|
|
/* as default we use the current CS size */
|
2004-04-26 02:05:08 +08:00
|
|
|
flags = 0;
|
2006-04-13 05:07:07 +08:00
|
|
|
if (env) {
|
|
|
|
#ifdef TARGET_X86_64
|
2007-09-17 05:08:06 +08:00
|
|
|
if ((env->efer & MSR_EFER_LMA) &&
|
2006-04-13 05:07:07 +08: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-26 02:05:08 +08:00
|
|
|
}
|
|
|
|
#endif
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_disas(mon, env, addr, count, is_physical, flags);
|
2004-04-04 20:57:25 +08: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-25 02:39:04 +08:00
|
|
|
if (is_physical)
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, TARGET_FMT_plx ":", addr);
|
2007-09-25 02:39:04 +08:00
|
|
|
else
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, TARGET_FMT_lx ":", (target_ulong)addr);
|
2004-04-04 20:57:25 +08:00
|
|
|
l = len;
|
|
|
|
if (l > line_size)
|
|
|
|
l = line_size;
|
|
|
|
if (is_physical) {
|
|
|
|
cpu_physical_memory_rw(addr, buf, l, 0);
|
|
|
|
} else {
|
2005-11-22 07:25:50 +08:00
|
|
|
env = mon_get_cpu();
|
2008-08-18 22:00:20 +08:00
|
|
|
if (cpu_memory_rw_debug(env, addr, buf, l, 0) < 0) {
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, " Cannot access memory\n");
|
2008-08-18 22:00:20 +08:00
|
|
|
break;
|
|
|
|
}
|
2004-04-04 20:57:25 +08:00
|
|
|
}
|
2007-09-17 05:08:06 +08:00
|
|
|
i = 0;
|
2004-04-04 20:57:25 +08: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 06:00:52 +08:00
|
|
|
v = (uint32_t)ldl_raw(buf + i);
|
2004-04-04 20:57:25 +08:00
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
v = ldq_raw(buf + i);
|
|
|
|
break;
|
|
|
|
}
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, " ");
|
2004-04-04 20:57:25 +08:00
|
|
|
switch(format) {
|
|
|
|
case 'o':
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "%#*" PRIo64, max_digits, v);
|
2004-04-04 20:57:25 +08:00
|
|
|
break;
|
|
|
|
case 'x':
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "0x%0*" PRIx64, max_digits, v);
|
2004-04-04 20:57:25 +08:00
|
|
|
break;
|
|
|
|
case 'u':
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "%*" PRIu64, max_digits, v);
|
2004-04-04 20:57:25 +08:00
|
|
|
break;
|
|
|
|
case 'd':
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "%*" PRId64, max_digits, v);
|
2004-04-04 20:57:25 +08:00
|
|
|
break;
|
|
|
|
case 'c':
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printc(mon, v);
|
2004-04-04 20:57:25 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
i += wsize;
|
|
|
|
}
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "\n");
|
2004-04-04 20:57:25 +08:00
|
|
|
addr += l;
|
|
|
|
len -= l;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-29 02:27:17 +08:00
|
|
|
static void do_memory_dump(Monitor *mon, const QDict *qdict)
|
2004-04-04 20:57:25 +08:00
|
|
|
{
|
2009-08-29 02:27:17 +08: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 07:01:23 +08:00
|
|
|
memory_dump(mon, count, format, size, addr, 0);
|
2004-04-04 20:57:25 +08:00
|
|
|
}
|
|
|
|
|
2009-08-29 02:27:17 +08:00
|
|
|
static void do_physical_memory_dump(Monitor *mon, const QDict *qdict)
|
2004-04-04 20:57:25 +08:00
|
|
|
{
|
2009-08-29 02:27:17 +08:00
|
|
|
int count = qdict_get_int(qdict, "count");
|
|
|
|
int format = qdict_get_int(qdict, "format");
|
|
|
|
int size = qdict_get_int(qdict, "size");
|
2009-10-02 05:12:16 +08:00
|
|
|
target_phys_addr_t addr = qdict_get_int(qdict, "addr");
|
2009-08-29 02:27:17 +08:00
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
memory_dump(mon, count, format, size, addr, 1);
|
2004-04-04 20:57:25 +08:00
|
|
|
}
|
|
|
|
|
2009-08-29 02:27:17 +08:00
|
|
|
static void do_print(Monitor *mon, const QDict *qdict)
|
2004-04-04 20:57:25 +08:00
|
|
|
{
|
2009-08-29 02:27:17 +08:00
|
|
|
int format = qdict_get_int(qdict, "format");
|
2009-10-02 05:12:16 +08:00
|
|
|
target_phys_addr_t val = qdict_get_int(qdict, "val");
|
2009-08-29 02:27:17 +08:00
|
|
|
|
2007-09-25 02:39:04 +08:00
|
|
|
#if TARGET_PHYS_ADDR_BITS == 32
|
2004-04-04 20:57:25 +08:00
|
|
|
switch(format) {
|
|
|
|
case 'o':
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "%#o", val);
|
2004-04-04 20:57:25 +08:00
|
|
|
break;
|
|
|
|
case 'x':
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "%#x", val);
|
2004-04-04 20:57:25 +08:00
|
|
|
break;
|
|
|
|
case 'u':
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "%u", val);
|
2004-04-04 20:57:25 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
case 'd':
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "%d", val);
|
2004-04-04 20:57:25 +08:00
|
|
|
break;
|
|
|
|
case 'c':
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printc(mon, val);
|
2004-04-04 20:57:25 +08:00
|
|
|
break;
|
|
|
|
}
|
2005-02-11 06:00:52 +08:00
|
|
|
#else
|
|
|
|
switch(format) {
|
|
|
|
case 'o':
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "%#" PRIo64, val);
|
2005-02-11 06:00:52 +08:00
|
|
|
break;
|
|
|
|
case 'x':
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "%#" PRIx64, val);
|
2005-02-11 06:00:52 +08:00
|
|
|
break;
|
|
|
|
case 'u':
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "%" PRIu64, val);
|
2005-02-11 06:00:52 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
case 'd':
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "%" PRId64, val);
|
2005-02-11 06:00:52 +08:00
|
|
|
break;
|
|
|
|
case 'c':
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printc(mon, val);
|
2005-02-11 06:00:52 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "\n");
|
2004-04-04 20:57:25 +08:00
|
|
|
}
|
|
|
|
|
2009-10-16 23:23:43 +08:00
|
|
|
static void do_memory_save(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
2007-01-03 23:20:39 +08:00
|
|
|
{
|
|
|
|
FILE *f;
|
2009-08-29 02:27:16 +08:00
|
|
|
uint32_t size = qdict_get_int(qdict, "size");
|
|
|
|
const char *filename = qdict_get_str(qdict, "filename");
|
|
|
|
target_long addr = qdict_get_int(qdict, "val");
|
2007-01-03 23:20:39 +08:00
|
|
|
uint32_t l;
|
|
|
|
CPUState *env;
|
|
|
|
uint8_t buf[1024];
|
|
|
|
|
|
|
|
env = mon_get_cpu();
|
|
|
|
|
|
|
|
f = fopen(filename, "wb");
|
|
|
|
if (!f) {
|
2010-01-20 20:07:32 +08:00
|
|
|
qemu_error_new(QERR_OPEN_FILE_FAILED, filename);
|
2007-01-03 23:20:39 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
while (size != 0) {
|
|
|
|
l = sizeof(buf);
|
|
|
|
if (l > size)
|
|
|
|
l = size;
|
|
|
|
cpu_memory_rw_debug(env, addr, buf, l, 0);
|
2010-01-20 07:56:19 +08:00
|
|
|
if (fwrite(buf, 1, l, f) != l) {
|
|
|
|
monitor_printf(mon, "fwrite() error in do_memory_save\n");
|
|
|
|
goto exit;
|
|
|
|
}
|
2007-01-03 23:20:39 +08:00
|
|
|
addr += l;
|
|
|
|
size -= l;
|
|
|
|
}
|
2010-01-20 07:56:19 +08:00
|
|
|
exit:
|
2007-01-03 23:20:39 +08:00
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
|
2009-10-16 23:23:44 +08:00
|
|
|
static void do_physical_memory_save(Monitor *mon, const QDict *qdict,
|
|
|
|
QObject **ret_data)
|
2008-04-12 05:36:14 +08:00
|
|
|
{
|
|
|
|
FILE *f;
|
|
|
|
uint32_t l;
|
|
|
|
uint8_t buf[1024];
|
2009-08-29 02:27:16 +08:00
|
|
|
uint32_t size = qdict_get_int(qdict, "size");
|
|
|
|
const char *filename = qdict_get_str(qdict, "filename");
|
2009-10-02 05:12:16 +08:00
|
|
|
target_phys_addr_t addr = qdict_get_int(qdict, "val");
|
2008-04-12 05:36:14 +08:00
|
|
|
|
|
|
|
f = fopen(filename, "wb");
|
|
|
|
if (!f) {
|
2010-01-20 20:07:33 +08:00
|
|
|
qemu_error_new(QERR_OPEN_FILE_FAILED, filename);
|
2008-04-12 05:36:14 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
while (size != 0) {
|
|
|
|
l = sizeof(buf);
|
|
|
|
if (l > size)
|
|
|
|
l = size;
|
|
|
|
cpu_physical_memory_rw(addr, buf, l, 0);
|
2010-01-20 07:56:19 +08:00
|
|
|
if (fwrite(buf, 1, l, f) != l) {
|
|
|
|
monitor_printf(mon, "fwrite() error in do_physical_memory_save\n");
|
|
|
|
goto exit;
|
|
|
|
}
|
2008-04-12 05:36:14 +08:00
|
|
|
fflush(f);
|
|
|
|
addr += l;
|
|
|
|
size -= l;
|
|
|
|
}
|
2010-01-20 07:56:19 +08:00
|
|
|
exit:
|
2008-04-12 05:36:14 +08:00
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
|
2009-08-29 02:27:14 +08:00
|
|
|
static void do_sum(Monitor *mon, const QDict *qdict)
|
2005-06-05 04:15:57 +08:00
|
|
|
{
|
|
|
|
uint32_t addr;
|
|
|
|
uint8_t buf[1];
|
|
|
|
uint16_t sum;
|
2009-08-29 02:27:14 +08:00
|
|
|
uint32_t start = qdict_get_int(qdict, "start");
|
|
|
|
uint32_t size = qdict_get_int(qdict, "size");
|
2005-06-05 04:15:57 +08:00
|
|
|
|
|
|
|
sum = 0;
|
|
|
|
for(addr = start; addr < (start + size); addr++) {
|
|
|
|
cpu_physical_memory_rw(addr, buf, 1, 0);
|
|
|
|
/* BSD sum algorithm ('sum' Unix command) */
|
|
|
|
sum = (sum >> 1) | (sum << 15);
|
|
|
|
sum += buf[0];
|
|
|
|
}
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "%05d\n", sum);
|
2005-06-05 04:15:57 +08:00
|
|
|
}
|
|
|
|
|
2004-06-04 19:06:21 +08:00
|
|
|
typedef struct {
|
|
|
|
int keycode;
|
|
|
|
const char *name;
|
|
|
|
} KeyDef;
|
|
|
|
|
|
|
|
static const KeyDef key_defs[] = {
|
|
|
|
{ 0x2a, "shift" },
|
|
|
|
{ 0x36, "shift_r" },
|
2007-09-17 16:09:54 +08:00
|
|
|
|
2004-06-04 19:06:21 +08:00
|
|
|
{ 0x38, "alt" },
|
|
|
|
{ 0xb8, "alt_r" },
|
2008-08-13 20:54:23 +08:00
|
|
|
{ 0x64, "altgr" },
|
|
|
|
{ 0xe4, "altgr_r" },
|
2004-06-04 19:06:21 +08:00
|
|
|
{ 0x1d, "ctrl" },
|
|
|
|
{ 0x9d, "ctrl_r" },
|
|
|
|
|
|
|
|
{ 0xdd, "menu" },
|
|
|
|
|
|
|
|
{ 0x01, "esc" },
|
|
|
|
|
|
|
|
{ 0x02, "1" },
|
|
|
|
{ 0x03, "2" },
|
|
|
|
{ 0x04, "3" },
|
|
|
|
{ 0x05, "4" },
|
|
|
|
{ 0x06, "5" },
|
|
|
|
{ 0x07, "6" },
|
|
|
|
{ 0x08, "7" },
|
|
|
|
{ 0x09, "8" },
|
|
|
|
{ 0x0a, "9" },
|
|
|
|
{ 0x0b, "0" },
|
2006-05-08 02:03:31 +08:00
|
|
|
{ 0x0c, "minus" },
|
|
|
|
{ 0x0d, "equal" },
|
2004-06-04 19:06:21 +08:00
|
|
|
{ 0x0e, "backspace" },
|
|
|
|
|
|
|
|
{ 0x0f, "tab" },
|
|
|
|
{ 0x10, "q" },
|
|
|
|
{ 0x11, "w" },
|
|
|
|
{ 0x12, "e" },
|
|
|
|
{ 0x13, "r" },
|
|
|
|
{ 0x14, "t" },
|
|
|
|
{ 0x15, "y" },
|
|
|
|
{ 0x16, "u" },
|
|
|
|
{ 0x17, "i" },
|
|
|
|
{ 0x18, "o" },
|
|
|
|
{ 0x19, "p" },
|
|
|
|
|
|
|
|
{ 0x1c, "ret" },
|
|
|
|
|
|
|
|
{ 0x1e, "a" },
|
|
|
|
{ 0x1f, "s" },
|
|
|
|
{ 0x20, "d" },
|
|
|
|
{ 0x21, "f" },
|
|
|
|
{ 0x22, "g" },
|
|
|
|
{ 0x23, "h" },
|
|
|
|
{ 0x24, "j" },
|
|
|
|
{ 0x25, "k" },
|
|
|
|
{ 0x26, "l" },
|
|
|
|
|
|
|
|
{ 0x2c, "z" },
|
|
|
|
{ 0x2d, "x" },
|
|
|
|
{ 0x2e, "c" },
|
|
|
|
{ 0x2f, "v" },
|
|
|
|
{ 0x30, "b" },
|
|
|
|
{ 0x31, "n" },
|
|
|
|
{ 0x32, "m" },
|
2008-10-02 05:46:15 +08:00
|
|
|
{ 0x33, "comma" },
|
|
|
|
{ 0x34, "dot" },
|
|
|
|
{ 0x35, "slash" },
|
2007-09-17 16:09:54 +08:00
|
|
|
|
2008-02-11 00:33:14 +08:00
|
|
|
{ 0x37, "asterisk" },
|
|
|
|
|
2004-06-04 19:06:21 +08:00
|
|
|
{ 0x39, "spc" },
|
2004-06-04 21:25:15 +08:00
|
|
|
{ 0x3a, "caps_lock" },
|
2004-06-04 19:06:21 +08:00
|
|
|
{ 0x3b, "f1" },
|
|
|
|
{ 0x3c, "f2" },
|
|
|
|
{ 0x3d, "f3" },
|
|
|
|
{ 0x3e, "f4" },
|
|
|
|
{ 0x3f, "f5" },
|
|
|
|
{ 0x40, "f6" },
|
|
|
|
{ 0x41, "f7" },
|
|
|
|
{ 0x42, "f8" },
|
|
|
|
{ 0x43, "f9" },
|
|
|
|
{ 0x44, "f10" },
|
2004-06-04 21:25:15 +08:00
|
|
|
{ 0x45, "num_lock" },
|
2004-06-04 19:06:21 +08:00
|
|
|
{ 0x46, "scroll_lock" },
|
|
|
|
|
2006-05-08 02:03:31 +08:00
|
|
|
{ 0xb5, "kp_divide" },
|
|
|
|
{ 0x37, "kp_multiply" },
|
2007-06-24 00:02:43 +08:00
|
|
|
{ 0x4a, "kp_subtract" },
|
2006-05-08 02:03:31 +08:00
|
|
|
{ 0x4e, "kp_add" },
|
|
|
|
{ 0x9c, "kp_enter" },
|
|
|
|
{ 0x53, "kp_decimal" },
|
2008-06-04 18:14:16 +08:00
|
|
|
{ 0x54, "sysrq" },
|
2006-05-08 02:03:31 +08:00
|
|
|
|
|
|
|
{ 0x52, "kp_0" },
|
|
|
|
{ 0x4f, "kp_1" },
|
|
|
|
{ 0x50, "kp_2" },
|
|
|
|
{ 0x51, "kp_3" },
|
|
|
|
{ 0x4b, "kp_4" },
|
|
|
|
{ 0x4c, "kp_5" },
|
|
|
|
{ 0x4d, "kp_6" },
|
|
|
|
{ 0x47, "kp_7" },
|
|
|
|
{ 0x48, "kp_8" },
|
|
|
|
{ 0x49, "kp_9" },
|
2007-09-17 16:09:54 +08:00
|
|
|
|
2004-06-04 19:06:21 +08:00
|
|
|
{ 0x56, "<" },
|
|
|
|
|
|
|
|
{ 0x57, "f11" },
|
|
|
|
{ 0x58, "f12" },
|
|
|
|
|
|
|
|
{ 0xb7, "print" },
|
|
|
|
|
|
|
|
{ 0xc7, "home" },
|
|
|
|
{ 0xc9, "pgup" },
|
|
|
|
{ 0xd1, "pgdn" },
|
|
|
|
{ 0xcf, "end" },
|
|
|
|
|
|
|
|
{ 0xcb, "left" },
|
|
|
|
{ 0xc8, "up" },
|
|
|
|
{ 0xd0, "down" },
|
|
|
|
{ 0xcd, "right" },
|
|
|
|
|
|
|
|
{ 0xd2, "insert" },
|
|
|
|
{ 0xd3, "delete" },
|
2008-06-22 15:45:42 +08:00
|
|
|
#if defined(TARGET_SPARC) && !defined(TARGET_SPARC64)
|
|
|
|
{ 0xf0, "stop" },
|
|
|
|
{ 0xf1, "again" },
|
|
|
|
{ 0xf2, "props" },
|
|
|
|
{ 0xf3, "undo" },
|
|
|
|
{ 0xf4, "front" },
|
|
|
|
{ 0xf5, "copy" },
|
|
|
|
{ 0xf6, "open" },
|
|
|
|
{ 0xf7, "paste" },
|
|
|
|
{ 0xf8, "find" },
|
|
|
|
{ 0xf9, "cut" },
|
|
|
|
{ 0xfa, "lf" },
|
|
|
|
{ 0xfb, "help" },
|
|
|
|
{ 0xfc, "meta_l" },
|
|
|
|
{ 0xfd, "meta_r" },
|
|
|
|
{ 0xfe, "compose" },
|
|
|
|
#endif
|
2004-06-04 19:06:21 +08:00
|
|
|
{ 0, NULL },
|
|
|
|
};
|
|
|
|
|
|
|
|
static int get_keycode(const char *key)
|
|
|
|
{
|
|
|
|
const KeyDef *p;
|
2006-05-08 02:03:31 +08:00
|
|
|
char *endp;
|
|
|
|
int ret;
|
2004-06-04 19:06:21 +08:00
|
|
|
|
|
|
|
for(p = key_defs; p->name != NULL; p++) {
|
|
|
|
if (!strcmp(key, p->name))
|
|
|
|
return p->keycode;
|
|
|
|
}
|
2006-05-08 02:03:31 +08:00
|
|
|
if (strstart(key, "0x", NULL)) {
|
|
|
|
ret = strtoul(key, &endp, 0);
|
|
|
|
if (*endp == '\0' && ret >= 0x01 && ret <= 0xff)
|
|
|
|
return ret;
|
|
|
|
}
|
2004-06-04 19:06:21 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2008-06-09 06:45:01 +08:00
|
|
|
#define MAX_KEYCODES 16
|
|
|
|
static uint8_t keycodes[MAX_KEYCODES];
|
|
|
|
static int nb_pending_keycodes;
|
|
|
|
static QEMUTimer *key_timer;
|
|
|
|
|
|
|
|
static void release_keys(void *opaque)
|
|
|
|
{
|
|
|
|
int keycode;
|
|
|
|
|
|
|
|
while (nb_pending_keycodes > 0) {
|
|
|
|
nb_pending_keycodes--;
|
|
|
|
keycode = keycodes[nb_pending_keycodes];
|
|
|
|
if (keycode & 0x80)
|
|
|
|
kbd_put_keycode(0xe0);
|
|
|
|
kbd_put_keycode(keycode | 0x80);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-29 02:27:15 +08:00
|
|
|
static void do_sendkey(Monitor *mon, const QDict *qdict)
|
2004-06-04 19:06:21 +08:00
|
|
|
{
|
2008-06-04 18:05:59 +08:00
|
|
|
char keyname_buf[16];
|
|
|
|
char *separator;
|
|
|
|
int keyname_len, keycode, i;
|
2009-08-29 02:27:15 +08:00
|
|
|
const char *string = qdict_get_str(qdict, "string");
|
|
|
|
int has_hold_time = qdict_haskey(qdict, "hold_time");
|
|
|
|
int hold_time = qdict_get_try_int(qdict, "hold_time", -1);
|
2008-06-04 18:05:59 +08:00
|
|
|
|
2008-06-09 06:45:01 +08:00
|
|
|
if (nb_pending_keycodes > 0) {
|
|
|
|
qemu_del_timer(key_timer);
|
|
|
|
release_keys(NULL);
|
|
|
|
}
|
|
|
|
if (!has_hold_time)
|
|
|
|
hold_time = 100;
|
|
|
|
i = 0;
|
2008-06-04 18:05:59 +08:00
|
|
|
while (1) {
|
|
|
|
separator = strchr(string, '-');
|
|
|
|
keyname_len = separator ? separator - string : strlen(string);
|
|
|
|
if (keyname_len > 0) {
|
|
|
|
pstrcpy(keyname_buf, sizeof(keyname_buf), string);
|
|
|
|
if (keyname_len > sizeof(keyname_buf) - 1) {
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "invalid key: '%s...'\n", keyname_buf);
|
2008-06-04 18:05:59 +08:00
|
|
|
return;
|
2004-06-04 19:06:21 +08:00
|
|
|
}
|
2008-06-09 06:45:01 +08:00
|
|
|
if (i == MAX_KEYCODES) {
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "too many keys\n");
|
2008-06-04 18:05:59 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
keyname_buf[keyname_len] = 0;
|
|
|
|
keycode = get_keycode(keyname_buf);
|
|
|
|
if (keycode < 0) {
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "unknown key: '%s'\n", keyname_buf);
|
2008-06-04 18:05:59 +08:00
|
|
|
return;
|
|
|
|
}
|
2008-06-09 06:45:01 +08:00
|
|
|
keycodes[i++] = keycode;
|
2004-06-04 19:06:21 +08:00
|
|
|
}
|
2008-06-04 18:05:59 +08:00
|
|
|
if (!separator)
|
2004-06-04 19:06:21 +08:00
|
|
|
break;
|
2008-06-04 18:05:59 +08:00
|
|
|
string = separator + 1;
|
2004-06-04 19:06:21 +08:00
|
|
|
}
|
2008-06-09 06:45:01 +08:00
|
|
|
nb_pending_keycodes = i;
|
2004-06-04 19:06:21 +08:00
|
|
|
/* key down events */
|
2008-06-09 06:45:01 +08:00
|
|
|
for (i = 0; i < nb_pending_keycodes; i++) {
|
2004-06-04 19:06:21 +08:00
|
|
|
keycode = keycodes[i];
|
|
|
|
if (keycode & 0x80)
|
|
|
|
kbd_put_keycode(0xe0);
|
|
|
|
kbd_put_keycode(keycode & 0x7f);
|
|
|
|
}
|
2008-06-09 06:45:01 +08:00
|
|
|
/* delayed key up events */
|
2008-06-09 08:03:47 +08:00
|
|
|
qemu_mod_timer(key_timer, qemu_get_clock(vm_clock) +
|
2009-09-10 09:04:26 +08:00
|
|
|
muldiv64(get_ticks_per_sec(), hold_time, 1000));
|
2004-06-04 19:06:21 +08:00
|
|
|
}
|
|
|
|
|
2006-07-15 06:03:35 +08:00
|
|
|
static int mouse_button_state;
|
|
|
|
|
2009-08-29 02:27:15 +08:00
|
|
|
static void do_mouse_move(Monitor *mon, const QDict *qdict)
|
2006-07-15 06:03:35 +08:00
|
|
|
{
|
|
|
|
int dx, dy, dz;
|
2009-08-29 02:27:15 +08: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 06:03:35 +08:00
|
|
|
dx = strtol(dx_str, NULL, 0);
|
|
|
|
dy = strtol(dy_str, NULL, 0);
|
|
|
|
dz = 0;
|
2007-09-17 05:08:06 +08:00
|
|
|
if (dz_str)
|
2006-07-15 06:03:35 +08:00
|
|
|
dz = strtol(dz_str, NULL, 0);
|
|
|
|
kbd_mouse_event(dx, dy, dz, mouse_button_state);
|
|
|
|
}
|
|
|
|
|
2009-08-29 02:27:13 +08:00
|
|
|
static void do_mouse_button(Monitor *mon, const QDict *qdict)
|
2006-07-15 06:03:35 +08:00
|
|
|
{
|
2009-08-29 02:27:13 +08:00
|
|
|
int button_state = qdict_get_int(qdict, "button_state");
|
2006-07-15 06:03:35 +08:00
|
|
|
mouse_button_state = button_state;
|
|
|
|
kbd_mouse_event(0, 0, 0, mouse_button_state);
|
|
|
|
}
|
|
|
|
|
2009-08-29 02:27:18 +08:00
|
|
|
static void do_ioport_read(Monitor *mon, const QDict *qdict)
|
2004-06-08 08:55:58 +08:00
|
|
|
{
|
2009-08-29 02:27:18 +08: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 08:55:58 +08:00
|
|
|
uint32_t val;
|
|
|
|
int suffix;
|
|
|
|
|
|
|
|
if (has_index) {
|
2009-08-29 02:27:18 +08:00
|
|
|
int index = qdict_get_int(qdict, "index");
|
2009-09-21 00:05:47 +08:00
|
|
|
cpu_outb(addr & IOPORTS_MASK, index & 0xff);
|
2004-06-08 08:55:58 +08:00
|
|
|
addr++;
|
|
|
|
}
|
|
|
|
addr &= 0xffff;
|
|
|
|
|
|
|
|
switch(size) {
|
|
|
|
default:
|
|
|
|
case 1:
|
2009-09-21 00:05:47 +08:00
|
|
|
val = cpu_inb(addr);
|
2004-06-08 08:55:58 +08:00
|
|
|
suffix = 'b';
|
|
|
|
break;
|
|
|
|
case 2:
|
2009-09-21 00:05:47 +08:00
|
|
|
val = cpu_inw(addr);
|
2004-06-08 08:55:58 +08:00
|
|
|
suffix = 'w';
|
|
|
|
break;
|
|
|
|
case 4:
|
2009-09-21 00:05:47 +08:00
|
|
|
val = cpu_inl(addr);
|
2004-06-08 08:55:58 +08:00
|
|
|
suffix = 'l';
|
|
|
|
break;
|
|
|
|
}
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "port%c[0x%04x] = %#0*x\n",
|
|
|
|
suffix, addr, size * 2, val);
|
2004-06-08 08:55:58 +08:00
|
|
|
}
|
2004-06-04 19:06:21 +08:00
|
|
|
|
2009-08-29 02:27:17 +08:00
|
|
|
static void do_ioport_write(Monitor *mon, const QDict *qdict)
|
2009-07-14 16:20:11 +08:00
|
|
|
{
|
2009-08-29 02:27:17 +08: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 16:20:11 +08:00
|
|
|
addr &= IOPORTS_MASK;
|
|
|
|
|
|
|
|
switch (size) {
|
|
|
|
default:
|
|
|
|
case 1:
|
2009-09-21 00:05:47 +08:00
|
|
|
cpu_outb(addr, val);
|
2009-07-14 16:20:11 +08:00
|
|
|
break;
|
|
|
|
case 2:
|
2009-09-21 00:05:47 +08:00
|
|
|
cpu_outw(addr, val);
|
2009-07-14 16:20:11 +08:00
|
|
|
break;
|
|
|
|
case 4:
|
2009-09-21 00:05:47 +08:00
|
|
|
cpu_outl(addr, val);
|
2009-07-14 16:20:11 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-29 02:27:13 +08:00
|
|
|
static void do_boot_set(Monitor *mon, const QDict *qdict)
|
2008-05-05 04:11:34 +08:00
|
|
|
{
|
|
|
|
int res;
|
2009-08-29 02:27:13 +08:00
|
|
|
const char *bootdevice = qdict_get_str(qdict, "bootdevice");
|
2008-05-05 04:11:34 +08:00
|
|
|
|
2009-07-02 06:19:02 +08: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 04:11:34 +08:00
|
|
|
} else {
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "no function defined to set boot device list for "
|
|
|
|
"this architecture\n");
|
2008-05-05 04:11:34 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-08 00:41:58 +08:00
|
|
|
/**
|
|
|
|
* do_system_reset(): Issue a machine reset
|
|
|
|
*/
|
|
|
|
static void do_system_reset(Monitor *mon, const QDict *qdict,
|
|
|
|
QObject **ret_data)
|
2004-06-20 20:35:44 +08:00
|
|
|
{
|
|
|
|
qemu_system_reset_request();
|
|
|
|
}
|
|
|
|
|
2009-10-08 00:41:59 +08:00
|
|
|
/**
|
|
|
|
* do_system_powerdown(): Issue a machine powerdown
|
|
|
|
*/
|
|
|
|
static void do_system_powerdown(Monitor *mon, const QDict *qdict,
|
|
|
|
QObject **ret_data)
|
2005-07-02 22:31:34 +08:00
|
|
|
{
|
|
|
|
qemu_system_powerdown_request();
|
|
|
|
}
|
|
|
|
|
2004-09-19 03:32:46 +08:00
|
|
|
#if defined(TARGET_I386)
|
2009-03-06 07:01:23 +08:00
|
|
|
static void print_pte(Monitor *mon, uint32_t addr, uint32_t pte, uint32_t mask)
|
2004-09-19 03:32:46 +08:00
|
|
|
{
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "%08x: %08x %c%c%c%c%c%c%c%c\n",
|
|
|
|
addr,
|
|
|
|
pte & mask,
|
|
|
|
pte & PG_GLOBAL_MASK ? 'G' : '-',
|
|
|
|
pte & PG_PSE_MASK ? 'P' : '-',
|
|
|
|
pte & PG_DIRTY_MASK ? 'D' : '-',
|
|
|
|
pte & PG_ACCESSED_MASK ? 'A' : '-',
|
|
|
|
pte & PG_PCD_MASK ? 'C' : '-',
|
|
|
|
pte & PG_PWT_MASK ? 'T' : '-',
|
|
|
|
pte & PG_USER_MASK ? 'U' : '-',
|
|
|
|
pte & PG_RW_MASK ? 'W' : '-');
|
2004-09-19 03:32:46 +08:00
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
static void tlb_info(Monitor *mon)
|
2004-09-19 03:32:46 +08:00
|
|
|
{
|
2005-11-22 07:25:50 +08:00
|
|
|
CPUState *env;
|
2004-09-19 03:32:46 +08:00
|
|
|
int l1, l2;
|
|
|
|
uint32_t pgd, pde, pte;
|
|
|
|
|
2005-11-22 07:25:50 +08:00
|
|
|
env = mon_get_cpu();
|
|
|
|
|
2004-09-19 03:32:46 +08:00
|
|
|
if (!(env->cr[0] & CR0_PG_MASK)) {
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "PG disabled\n");
|
2004-09-19 03:32:46 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
pgd = env->cr[3] & ~0xfff;
|
|
|
|
for(l1 = 0; l1 < 1024; l1++) {
|
|
|
|
cpu_physical_memory_read(pgd + l1 * 4, (uint8_t *)&pde, 4);
|
|
|
|
pde = le32_to_cpu(pde);
|
|
|
|
if (pde & PG_PRESENT_MASK) {
|
|
|
|
if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) {
|
2009-03-06 07:01:23 +08:00
|
|
|
print_pte(mon, (l1 << 22), pde, ~((1 << 20) - 1));
|
2004-09-19 03:32:46 +08:00
|
|
|
} else {
|
|
|
|
for(l2 = 0; l2 < 1024; l2++) {
|
2007-09-17 05:08:06 +08:00
|
|
|
cpu_physical_memory_read((pde & ~0xfff) + l2 * 4,
|
2004-09-19 03:32:46 +08:00
|
|
|
(uint8_t *)&pte, 4);
|
|
|
|
pte = le32_to_cpu(pte);
|
|
|
|
if (pte & PG_PRESENT_MASK) {
|
2009-03-06 07:01:23 +08:00
|
|
|
print_pte(mon, (l1 << 22) + (l2 << 12),
|
2007-09-17 05:08:06 +08:00
|
|
|
pte & ~PG_PSE_MASK,
|
2004-09-19 03:32:46 +08:00
|
|
|
~0xfff);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
static void mem_print(Monitor *mon, uint32_t *pstart, int *plast_prot,
|
2004-09-19 03:32:46 +08:00
|
|
|
uint32_t end, int prot)
|
|
|
|
{
|
2004-11-12 02:30:24 +08:00
|
|
|
int prot1;
|
|
|
|
prot1 = *plast_prot;
|
|
|
|
if (prot != prot1) {
|
2004-09-19 03:32:46 +08:00
|
|
|
if (*pstart != -1) {
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "%08x-%08x %08x %c%c%c\n",
|
|
|
|
*pstart, end, end - *pstart,
|
|
|
|
prot1 & PG_USER_MASK ? 'u' : '-',
|
|
|
|
'r',
|
|
|
|
prot1 & PG_RW_MASK ? 'w' : '-');
|
2004-09-19 03:32:46 +08:00
|
|
|
}
|
|
|
|
if (prot != 0)
|
|
|
|
*pstart = end;
|
|
|
|
else
|
|
|
|
*pstart = -1;
|
|
|
|
*plast_prot = prot;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
static void mem_info(Monitor *mon)
|
2004-09-19 03:32:46 +08:00
|
|
|
{
|
2005-11-22 07:25:50 +08:00
|
|
|
CPUState *env;
|
2004-09-19 03:32:46 +08:00
|
|
|
int l1, l2, prot, last_prot;
|
|
|
|
uint32_t pgd, pde, pte, start, end;
|
|
|
|
|
2005-11-22 07:25:50 +08:00
|
|
|
env = mon_get_cpu();
|
|
|
|
|
2004-09-19 03:32:46 +08:00
|
|
|
if (!(env->cr[0] & CR0_PG_MASK)) {
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "PG disabled\n");
|
2004-09-19 03:32:46 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
pgd = env->cr[3] & ~0xfff;
|
|
|
|
last_prot = 0;
|
|
|
|
start = -1;
|
|
|
|
for(l1 = 0; l1 < 1024; l1++) {
|
|
|
|
cpu_physical_memory_read(pgd + l1 * 4, (uint8_t *)&pde, 4);
|
|
|
|
pde = le32_to_cpu(pde);
|
|
|
|
end = l1 << 22;
|
|
|
|
if (pde & PG_PRESENT_MASK) {
|
|
|
|
if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) {
|
|
|
|
prot = pde & (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK);
|
2009-03-06 07:01:23 +08:00
|
|
|
mem_print(mon, &start, &last_prot, end, prot);
|
2004-09-19 03:32:46 +08:00
|
|
|
} else {
|
|
|
|
for(l2 = 0; l2 < 1024; l2++) {
|
2007-09-17 05:08:06 +08:00
|
|
|
cpu_physical_memory_read((pde & ~0xfff) + l2 * 4,
|
2004-09-19 03:32:46 +08:00
|
|
|
(uint8_t *)&pte, 4);
|
|
|
|
pte = le32_to_cpu(pte);
|
|
|
|
end = (l1 << 22) + (l2 << 12);
|
|
|
|
if (pte & PG_PRESENT_MASK) {
|
|
|
|
prot = pte & (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK);
|
|
|
|
} else {
|
|
|
|
prot = 0;
|
|
|
|
}
|
2009-03-06 07:01:23 +08:00
|
|
|
mem_print(mon, &start, &last_prot, end, prot);
|
2004-09-19 03:32:46 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
prot = 0;
|
2009-03-06 07:01:23 +08:00
|
|
|
mem_print(mon, &start, &last_prot, end, prot);
|
2004-09-19 03:32:46 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-03-03 14:12:22 +08:00
|
|
|
#if defined(TARGET_SH4)
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
static void print_tlb(Monitor *mon, int idx, tlb_t *tlb)
|
2009-03-03 14:12:22 +08:00
|
|
|
{
|
2009-03-06 07:01:23 +08: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 14:12:22 +08:00
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
static void tlb_info(Monitor *mon)
|
2009-03-03 14:12:22 +08:00
|
|
|
{
|
|
|
|
CPUState *env = mon_get_cpu();
|
|
|
|
int i;
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf (mon, "ITLB:\n");
|
2009-03-03 14:12:22 +08:00
|
|
|
for (i = 0 ; i < ITLB_SIZE ; i++)
|
2009-03-06 07:01:23 +08:00
|
|
|
print_tlb (mon, i, &env->itlb[i]);
|
|
|
|
monitor_printf (mon, "UTLB:\n");
|
2009-03-03 14:12:22 +08:00
|
|
|
for (i = 0 ; i < UTLB_SIZE ; i++)
|
2009-03-06 07:01:23 +08:00
|
|
|
print_tlb (mon, i, &env->utlb[i]);
|
2009-03-03 14:12:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2009-12-11 03:16:00 +08:00
|
|
|
static void do_info_kvm_print(Monitor *mon, const QObject *data)
|
2008-11-06 00:04:33 +08:00
|
|
|
{
|
2009-12-11 03:16:00 +08:00
|
|
|
QDict *qdict;
|
|
|
|
|
|
|
|
qdict = qobject_to_qdict(data);
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "kvm support: ");
|
2009-12-11 03:16:00 +08:00
|
|
|
if (qdict_get_bool(qdict, "present")) {
|
|
|
|
monitor_printf(mon, "%s\n", qdict_get_bool(qdict, "enabled") ?
|
|
|
|
"enabled" : "disabled");
|
|
|
|
} else {
|
|
|
|
monitor_printf(mon, "not compiled\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* do_info_kvm(): Show KVM information
|
|
|
|
*
|
|
|
|
* Return a QDict with the following information:
|
|
|
|
*
|
|
|
|
* - "enabled": true if KVM support is enabled, false otherwise
|
|
|
|
* - "present": true if QEMU has KVM support, false otherwise
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
* { "enabled": true, "present": true }
|
|
|
|
*/
|
|
|
|
static void do_info_kvm(Monitor *mon, QObject **ret_data)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_KVM
|
|
|
|
*ret_data = qobject_from_jsonf("{ 'enabled': %i, 'present': true }",
|
|
|
|
kvm_enabled());
|
2008-11-06 00:04:33 +08:00
|
|
|
#else
|
2009-12-11 03:16:00 +08:00
|
|
|
*ret_data = qobject_from_jsonf("{ 'enabled': false, 'present': false }");
|
2008-11-06 00:04:33 +08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2009-04-22 06:30:47 +08:00
|
|
|
static void do_info_numa(Monitor *mon)
|
|
|
|
{
|
2009-04-23 04:20:29 +08:00
|
|
|
int i;
|
2009-04-22 06:30:47 +08:00
|
|
|
CPUState *env;
|
|
|
|
|
|
|
|
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 06:40:15 +08:00
|
|
|
#ifdef CONFIG_PROFILER
|
|
|
|
|
2009-09-30 20:09:52 +08:00
|
|
|
int64_t qemu_time;
|
|
|
|
int64_t dev_time;
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
static void do_info_profile(Monitor *mon)
|
2006-02-09 06:40:15 +08:00
|
|
|
{
|
|
|
|
int64_t total;
|
|
|
|
total = qemu_time;
|
|
|
|
if (total == 0)
|
|
|
|
total = 1;
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "async time %" PRId64 " (%0.3f)\n",
|
2009-09-10 09:04:26 +08:00
|
|
|
dev_time, dev_time / (double)get_ticks_per_sec());
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "qemu time %" PRId64 " (%0.3f)\n",
|
2009-09-10 09:04:26 +08:00
|
|
|
qemu_time, qemu_time / (double)get_ticks_per_sec());
|
2006-02-09 06:40:15 +08:00
|
|
|
qemu_time = 0;
|
|
|
|
dev_time = 0;
|
|
|
|
}
|
|
|
|
#else
|
2009-03-06 07:01:23 +08:00
|
|
|
static void do_info_profile(Monitor *mon)
|
2006-02-09 06:40:15 +08:00
|
|
|
{
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "Internal profiler not compiled\n");
|
2006-02-09 06:40:15 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2006-07-17 02:57:03 +08:00
|
|
|
/* Capture support */
|
2009-09-12 15:36:22 +08:00
|
|
|
static QLIST_HEAD (capture_list_head, CaptureState) capture_head;
|
2006-07-17 02:57:03 +08:00
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
static void do_info_capture(Monitor *mon)
|
2006-07-17 02:57:03 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
CaptureState *s;
|
|
|
|
|
|
|
|
for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) {
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "[%d]: ", i);
|
2006-07-17 02:57:03 +08:00
|
|
|
s->ops.info (s->opaque);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-06 16:22:04 +08:00
|
|
|
#ifdef HAS_AUDIO
|
2009-08-29 02:27:13 +08:00
|
|
|
static void do_stop_capture(Monitor *mon, const QDict *qdict)
|
2006-07-17 02:57:03 +08:00
|
|
|
{
|
|
|
|
int i;
|
2009-08-29 02:27:13 +08:00
|
|
|
int n = qdict_get_int(qdict, "n");
|
2006-07-17 02:57:03 +08: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 15:36:22 +08:00
|
|
|
QLIST_REMOVE (s, entries);
|
2006-07-17 02:57:03 +08:00
|
|
|
qemu_free (s);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-29 02:27:19 +08: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-17 02:57:03 +08:00
|
|
|
CaptureState *s;
|
|
|
|
|
|
|
|
s = qemu_mallocz (sizeof (*s));
|
|
|
|
|
|
|
|
freq = has_freq ? freq : 44100;
|
|
|
|
bits = has_bits ? bits : 16;
|
|
|
|
nchannels = has_channels ? nchannels : 2;
|
|
|
|
|
|
|
|
if (wav_start_capture (s, path, freq, bits, nchannels)) {
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "Faied to add wave capture\n");
|
2006-07-17 02:57:03 +08:00
|
|
|
qemu_free (s);
|
|
|
|
}
|
2009-09-12 15:36:22 +08:00
|
|
|
QLIST_INSERT_HEAD (&capture_head, s, entries);
|
2006-07-17 02:57:03 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-04-28 07:52:12 +08:00
|
|
|
#if defined(TARGET_I386)
|
2009-08-29 02:27:13 +08:00
|
|
|
static void do_inject_nmi(Monitor *mon, const QDict *qdict)
|
2008-04-28 07:52:12 +08:00
|
|
|
{
|
|
|
|
CPUState *env;
|
2009-08-29 02:27:13 +08:00
|
|
|
int cpu_index = qdict_get_int(qdict, "cpu_index");
|
2008-04-28 07:52:12 +08:00
|
|
|
|
|
|
|
for (env = first_cpu; env != NULL; env = env->next_cpu)
|
|
|
|
if (env->cpu_index == cpu_index) {
|
|
|
|
cpu_interrupt(env, CPU_INTERRUPT_NMI);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-12-11 03:15:59 +08:00
|
|
|
static void do_info_status_print(Monitor *mon, const QObject *data)
|
2008-12-19 06:43:56 +08:00
|
|
|
{
|
2009-12-11 03:15:59 +08:00
|
|
|
QDict *qdict;
|
|
|
|
|
|
|
|
qdict = qobject_to_qdict(data);
|
|
|
|
|
|
|
|
monitor_printf(mon, "VM status: ");
|
|
|
|
if (qdict_get_bool(qdict, "running")) {
|
|
|
|
monitor_printf(mon, "running");
|
|
|
|
if (qdict_get_bool(qdict, "singlestep")) {
|
|
|
|
monitor_printf(mon, " (single step mode)");
|
2009-04-06 04:08:59 +08:00
|
|
|
}
|
2009-12-11 03:15:59 +08:00
|
|
|
} else {
|
|
|
|
monitor_printf(mon, "paused");
|
|
|
|
}
|
|
|
|
|
|
|
|
monitor_printf(mon, "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* do_info_status(): VM status
|
|
|
|
*
|
|
|
|
* Return a QDict with the following information:
|
|
|
|
*
|
|
|
|
* - "running": true if the VM is running, or false if it is paused
|
|
|
|
* - "singlestep": true if the VM is in single step mode, false otherwise
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
* { "running": true, "singlestep": false }
|
|
|
|
*/
|
|
|
|
static void do_info_status(Monitor *mon, QObject **ret_data)
|
|
|
|
{
|
|
|
|
*ret_data = qobject_from_jsonf("{ 'running': %i, 'singlestep': %i }",
|
|
|
|
vm_running, singlestep);
|
2008-12-19 06:43:56 +08:00
|
|
|
}
|
|
|
|
|
2009-12-15 04:53:22 +08:00
|
|
|
static ram_addr_t balloon_get_value(void)
|
|
|
|
{
|
|
|
|
ram_addr_t actual;
|
|
|
|
|
|
|
|
if (kvm_enabled() && !kvm_has_sync_mmu()) {
|
|
|
|
qemu_error_new(QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
actual = qemu_balloon_status();
|
|
|
|
if (actual == 0) {
|
|
|
|
qemu_error_new(QERR_DEVICE_NOT_ACTIVE, "balloon");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return actual;
|
|
|
|
}
|
|
|
|
|
2009-10-08 00:42:01 +08:00
|
|
|
/**
|
|
|
|
* do_balloon(): Request VM to change its memory allocation
|
|
|
|
*/
|
|
|
|
static void do_balloon(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
2008-12-05 04:19:35 +08:00
|
|
|
{
|
2009-12-15 04:53:22 +08:00
|
|
|
if (balloon_get_value()) {
|
|
|
|
/* ballooning is active */
|
2009-12-18 23:25:05 +08:00
|
|
|
qemu_balloon(qdict_get_int(qdict, "value"));
|
2009-12-15 04:53:22 +08:00
|
|
|
}
|
2008-12-05 04:19:35 +08:00
|
|
|
}
|
|
|
|
|
2009-10-08 00:42:03 +08:00
|
|
|
static void monitor_print_balloon(Monitor *mon, const QObject *data)
|
|
|
|
{
|
2009-12-11 03:15:55 +08:00
|
|
|
QDict *qdict;
|
|
|
|
|
|
|
|
qdict = qobject_to_qdict(data);
|
|
|
|
|
|
|
|
monitor_printf(mon, "balloon: actual=%" PRId64 "\n",
|
|
|
|
qdict_get_int(qdict, "balloon") >> 20);
|
2009-10-08 00:42:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* do_info_balloon(): Balloon information
|
2009-12-11 03:15:55 +08:00
|
|
|
*
|
|
|
|
* Return a QDict with the following information:
|
|
|
|
*
|
|
|
|
* - "balloon": current balloon value in bytes
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
* { "balloon": 1073741824 }
|
2009-10-08 00:42:03 +08:00
|
|
|
*/
|
|
|
|
static void do_info_balloon(Monitor *mon, QObject **ret_data)
|
2008-12-05 04:19:35 +08:00
|
|
|
{
|
2009-10-02 05:12:16 +08:00
|
|
|
ram_addr_t actual;
|
2008-12-05 04:19:35 +08:00
|
|
|
|
2009-12-15 04:53:22 +08:00
|
|
|
actual = balloon_get_value();
|
|
|
|
if (actual != 0) {
|
2009-12-11 03:15:55 +08:00
|
|
|
*ret_data = qobject_from_jsonf("{ 'balloon': %" PRId64 "}",
|
|
|
|
(int64_t) actual);
|
2009-12-15 04:53:22 +08:00
|
|
|
}
|
2008-12-05 04:19:35 +08:00
|
|
|
}
|
|
|
|
|
2009-06-25 14:22:08 +08: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-07 04:27:37 +08:00
|
|
|
{
|
2009-06-25 14:22:08 +08: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-07 04:27:37 +08:00
|
|
|
|
|
|
|
if (!acl) {
|
2009-06-25 14:22:08 +08: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-07 04:27:37 +08:00
|
|
|
}
|
2009-06-25 14:22:08 +08:00
|
|
|
return acl;
|
|
|
|
}
|
|
|
|
|
2009-08-29 02:27:13 +08:00
|
|
|
static void do_acl_show(Monitor *mon, const QDict *qdict)
|
2009-06-25 14:22:08 +08:00
|
|
|
{
|
2009-08-29 02:27:13 +08:00
|
|
|
const char *aclname = qdict_get_str(qdict, "aclname");
|
2009-06-25 14:22:08 +08: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-07 04:27:37 +08:00
|
|
|
|
2009-06-25 14:22:08 +08:00
|
|
|
if (acl) {
|
2009-03-07 04:27:40 +08: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-07 04:27:37 +08:00
|
|
|
acl->defaultDeny ? "deny" : "allow");
|
2009-09-12 15:36:22 +08:00
|
|
|
QTAILQ_FOREACH(entry, &acl->entries, next) {
|
2009-03-07 04:27:40 +08:00
|
|
|
i++;
|
|
|
|
monitor_printf(mon, "%d: %s %s\n", i,
|
2009-06-25 14:22:08 +08:00
|
|
|
entry->deny ? "deny" : "allow", entry->match);
|
2009-03-07 04:27:40 +08:00
|
|
|
}
|
2009-06-25 14:22:08 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-29 02:27:13 +08:00
|
|
|
static void do_acl_reset(Monitor *mon, const QDict *qdict)
|
2009-06-25 14:22:08 +08:00
|
|
|
{
|
2009-08-29 02:27:13 +08:00
|
|
|
const char *aclname = qdict_get_str(qdict, "aclname");
|
2009-06-25 14:22:08 +08:00
|
|
|
qemu_acl *acl = find_acl(mon, aclname);
|
|
|
|
|
|
|
|
if (acl) {
|
2009-03-07 04:27:40 +08:00
|
|
|
qemu_acl_reset(acl);
|
|
|
|
monitor_printf(mon, "acl: removed all rules\n");
|
2009-06-25 14:22:08 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-29 02:27:14 +08:00
|
|
|
static void do_acl_policy(Monitor *mon, const QDict *qdict)
|
2009-06-25 14:22:08 +08:00
|
|
|
{
|
2009-08-29 02:27:14 +08:00
|
|
|
const char *aclname = qdict_get_str(qdict, "aclname");
|
|
|
|
const char *policy = qdict_get_str(qdict, "policy");
|
2009-06-25 14:22:08 +08:00
|
|
|
qemu_acl *acl = find_acl(mon, aclname);
|
2009-03-07 04:27:40 +08:00
|
|
|
|
2009-06-25 14:22:08 +08:00
|
|
|
if (acl) {
|
|
|
|
if (strcmp(policy, "allow") == 0) {
|
2009-03-07 04:27:40 +08:00
|
|
|
acl->defaultDeny = 0;
|
|
|
|
monitor_printf(mon, "acl: policy set to 'allow'\n");
|
2009-06-25 14:22:08 +08:00
|
|
|
} else if (strcmp(policy, "deny") == 0) {
|
2009-03-07 04:27:40 +08:00
|
|
|
acl->defaultDeny = 1;
|
|
|
|
monitor_printf(mon, "acl: policy set to 'deny'\n");
|
|
|
|
} else {
|
2009-06-25 14:22:08 +08:00
|
|
|
monitor_printf(mon, "acl: unknown policy '%s', "
|
|
|
|
"expected 'deny' or 'allow'\n", policy);
|
2009-03-07 04:27:40 +08:00
|
|
|
}
|
2009-06-25 14:22:08 +08:00
|
|
|
}
|
|
|
|
}
|
2009-03-07 04:27:40 +08:00
|
|
|
|
2009-08-29 02:27:17 +08:00
|
|
|
static void do_acl_add(Monitor *mon, const QDict *qdict)
|
2009-06-25 14:22:08 +08:00
|
|
|
{
|
2009-08-29 02:27:17 +08: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 14:22:08 +08: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-07 04:27:40 +08: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 14:22:08 +08:00
|
|
|
}
|
|
|
|
}
|
2009-03-07 04:27:40 +08:00
|
|
|
|
2009-08-29 02:27:14 +08:00
|
|
|
static void do_acl_remove(Monitor *mon, const QDict *qdict)
|
2009-06-25 14:22:08 +08:00
|
|
|
{
|
2009-08-29 02:27:14 +08:00
|
|
|
const char *aclname = qdict_get_str(qdict, "aclname");
|
|
|
|
const char *match = qdict_get_str(qdict, "match");
|
2009-06-25 14:22:08 +08:00
|
|
|
qemu_acl *acl = find_acl(mon, aclname);
|
|
|
|
int ret;
|
2009-03-07 04:27:40 +08:00
|
|
|
|
2009-06-25 14:22:08 +08:00
|
|
|
if (acl) {
|
2009-03-07 04:27:40 +08: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-07 04:27:37 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-23 10:05:14 +08:00
|
|
|
#if defined(TARGET_I386)
|
2009-08-29 02:27:21 +08:00
|
|
|
static void do_inject_mce(Monitor *mon, const QDict *qdict)
|
2009-06-23 10:05:14 +08:00
|
|
|
{
|
|
|
|
CPUState *cenv;
|
2009-08-29 02:27:21 +08: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");
|
2009-06-23 10:05:14 +08:00
|
|
|
|
|
|
|
for (cenv = first_cpu; cenv != NULL; cenv = cenv->next_cpu)
|
|
|
|
if (cenv->cpu_index == cpu_index && cenv->mcg_cap) {
|
|
|
|
cpu_inject_x86_mce(cenv, bank, status, mcg_status, addr, misc);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-10-16 23:23:50 +08:00
|
|
|
static void do_getfd(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
2009-07-22 16:11:40 +08:00
|
|
|
{
|
2009-08-29 02:27:13 +08:00
|
|
|
const char *fdname = qdict_get_str(qdict, "fdname");
|
2009-10-02 05:12:16 +08:00
|
|
|
mon_fd_t *monfd;
|
2009-07-22 16:11:40 +08:00
|
|
|
int fd;
|
|
|
|
|
|
|
|
fd = qemu_chr_get_msgfd(mon->chr);
|
|
|
|
if (fd == -1) {
|
2009-12-08 04:37:15 +08:00
|
|
|
qemu_error_new(QERR_FD_NOT_SUPPLIED);
|
2009-07-22 16:11:40 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (qemu_isdigit(fdname[0])) {
|
2009-12-08 04:37:15 +08:00
|
|
|
qemu_error_new(QERR_INVALID_PARAMETER, "fdname");
|
2009-07-22 16:11:40 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
fd = dup(fd);
|
|
|
|
if (fd == -1) {
|
2009-12-08 04:37:15 +08:00
|
|
|
if (errno == EMFILE)
|
|
|
|
qemu_error_new(QERR_TOO_MANY_FILES);
|
|
|
|
else
|
|
|
|
qemu_error_new(QERR_UNDEFINED_ERROR);
|
2009-07-22 16:11:40 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-09-12 15:36:22 +08:00
|
|
|
QLIST_FOREACH(monfd, &mon->fds, next) {
|
2009-07-22 16:11:40 +08:00
|
|
|
if (strcmp(monfd->name, fdname) != 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
close(monfd->fd);
|
|
|
|
monfd->fd = fd;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-10-02 05:12:16 +08:00
|
|
|
monfd = qemu_mallocz(sizeof(mon_fd_t));
|
2009-07-22 16:11:40 +08:00
|
|
|
monfd->name = qemu_strdup(fdname);
|
|
|
|
monfd->fd = fd;
|
|
|
|
|
2009-09-12 15:36:22 +08:00
|
|
|
QLIST_INSERT_HEAD(&mon->fds, monfd, next);
|
2009-07-22 16:11:40 +08:00
|
|
|
}
|
|
|
|
|
2009-10-16 23:23:51 +08:00
|
|
|
static void do_closefd(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
2009-07-22 16:11:40 +08:00
|
|
|
{
|
2009-08-29 02:27:13 +08:00
|
|
|
const char *fdname = qdict_get_str(qdict, "fdname");
|
2009-10-02 05:12:16 +08:00
|
|
|
mon_fd_t *monfd;
|
2009-07-22 16:11:40 +08:00
|
|
|
|
2009-09-12 15:36:22 +08:00
|
|
|
QLIST_FOREACH(monfd, &mon->fds, next) {
|
2009-07-22 16:11:40 +08:00
|
|
|
if (strcmp(monfd->name, fdname) != 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-09-12 15:36:22 +08:00
|
|
|
QLIST_REMOVE(monfd, next);
|
2009-07-22 16:11:40 +08:00
|
|
|
close(monfd->fd);
|
|
|
|
qemu_free(monfd->name);
|
|
|
|
qemu_free(monfd);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-12-08 04:37:11 +08:00
|
|
|
qemu_error_new(QERR_FD_NOT_FOUND, fdname);
|
2009-07-22 16:11:40 +08:00
|
|
|
}
|
|
|
|
|
2009-08-29 02:27:13 +08:00
|
|
|
static void do_loadvm(Monitor *mon, const QDict *qdict)
|
2009-08-21 01:42:21 +08:00
|
|
|
{
|
|
|
|
int saved_vm_running = vm_running;
|
2009-08-29 02:27:13 +08:00
|
|
|
const char *name = qdict_get_str(qdict, "name");
|
2009-08-21 01:42:21 +08:00
|
|
|
|
|
|
|
vm_stop(0);
|
|
|
|
|
2009-08-21 01:42:22 +08:00
|
|
|
if (load_vmstate(mon, name) >= 0 && saved_vm_running)
|
2009-08-21 01:42:21 +08:00
|
|
|
vm_start();
|
|
|
|
}
|
|
|
|
|
2009-07-22 16:11:41 +08:00
|
|
|
int monitor_get_fd(Monitor *mon, const char *fdname)
|
|
|
|
{
|
2009-10-02 05:12:16 +08:00
|
|
|
mon_fd_t *monfd;
|
2009-07-22 16:11:41 +08:00
|
|
|
|
2009-09-12 15:36:22 +08:00
|
|
|
QLIST_FOREACH(monfd, &mon->fds, next) {
|
2009-07-22 16:11:41 +08:00
|
|
|
int fd;
|
|
|
|
|
|
|
|
if (strcmp(monfd->name, fdname) != 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
fd = monfd->fd;
|
|
|
|
|
|
|
|
/* caller takes ownership of fd */
|
2009-09-12 15:36:22 +08:00
|
|
|
QLIST_REMOVE(monfd, next);
|
2009-07-22 16:11:41 +08:00
|
|
|
qemu_free(monfd->name);
|
|
|
|
qemu_free(monfd);
|
|
|
|
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-10-02 05:12:16 +08:00
|
|
|
static const mon_cmd_t mon_cmds[] = {
|
2009-06-06 16:22:04 +08:00
|
|
|
#include "qemu-monitor.h"
|
2007-09-17 05:08:06 +08:00
|
|
|
{ NULL, NULL, },
|
2004-03-15 05:38:27 +08:00
|
|
|
};
|
|
|
|
|
2009-06-06 16:22:04 +08:00
|
|
|
/* Please update qemu-monitor.hx when adding or changing commands */
|
2009-10-02 05:12:16 +08:00
|
|
|
static const mon_cmd_t info_cmds[] = {
|
2009-10-08 00:41:50 +08:00
|
|
|
{
|
|
|
|
.name = "version",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the version of QEMU",
|
2009-12-11 03:15:58 +08:00
|
|
|
.user_print = do_info_version_print,
|
2009-10-08 00:42:02 +08:00
|
|
|
.mhandler.info_new = do_info_version,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
2009-11-27 08:58:56 +08:00
|
|
|
{
|
|
|
|
.name = "commands",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "list QMP available commands",
|
|
|
|
.user_print = monitor_user_noop,
|
|
|
|
.mhandler.info_new = do_info_commands,
|
|
|
|
},
|
2009-10-08 00:41:50 +08:00
|
|
|
{
|
|
|
|
.name = "network",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the network state",
|
2009-10-08 00:41:51 +08:00
|
|
|
.mhandler.info = do_info_network,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "chardev",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the character devices",
|
2009-12-11 03:16:08 +08:00
|
|
|
.user_print = qemu_chr_info_print,
|
|
|
|
.mhandler.info_new = qemu_chr_info,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "block",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the block devices",
|
2009-12-11 03:16:06 +08:00
|
|
|
.user_print = bdrv_info_print,
|
|
|
|
.mhandler.info_new = bdrv_info,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "blockstats",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show block device statistics",
|
2009-12-11 03:16:07 +08:00
|
|
|
.user_print = bdrv_stats_print,
|
|
|
|
.mhandler.info_new = bdrv_info_stats,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "registers",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the cpu registers",
|
2009-10-08 00:41:51 +08:00
|
|
|
.mhandler.info = do_info_registers,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "cpus",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show infos for each CPU",
|
2009-10-08 00:42:04 +08:00
|
|
|
.user_print = monitor_print_cpus,
|
|
|
|
.mhandler.info_new = do_info_cpus,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "history",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the command line history",
|
2009-10-08 00:41:51 +08:00
|
|
|
.mhandler.info = do_info_history,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "irq",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the interrupts statistics (if available)",
|
2009-10-08 00:41:51 +08:00
|
|
|
.mhandler.info = irq_info,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "pic",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show i8259 (PIC) state",
|
2009-10-08 00:41:51 +08:00
|
|
|
.mhandler.info = pic_info,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "pci",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show PCI info",
|
2010-01-22 05:15:40 +08:00
|
|
|
.user_print = do_pci_info_print,
|
|
|
|
.mhandler.info_new = do_pci_info,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
2009-03-03 14:12:22 +08:00
|
|
|
#if defined(TARGET_I386) || defined(TARGET_SH4)
|
2009-10-08 00:41:50 +08:00
|
|
|
{
|
|
|
|
.name = "tlb",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show virtual to physical memory mappings",
|
2009-10-08 00:41:51 +08:00
|
|
|
.mhandler.info = tlb_info,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
2009-03-03 14:12:22 +08:00
|
|
|
#endif
|
|
|
|
#if defined(TARGET_I386)
|
2009-10-08 00:41:50 +08:00
|
|
|
{
|
|
|
|
.name = "mem",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the active virtual memory mappings",
|
2009-10-08 00:41:51 +08:00
|
|
|
.mhandler.info = mem_info,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "hpet",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show state of HPET",
|
2009-12-11 03:16:02 +08:00
|
|
|
.user_print = do_info_hpet_print,
|
|
|
|
.mhandler.info_new = do_info_hpet,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
2004-09-19 03:32:46 +08:00
|
|
|
#endif
|
2009-10-08 00:41:50 +08:00
|
|
|
{
|
|
|
|
.name = "jit",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show dynamic compiler info",
|
2009-10-08 00:41:51 +08:00
|
|
|
.mhandler.info = do_info_jit,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "kvm",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show KVM information",
|
2009-12-11 03:16:00 +08:00
|
|
|
.user_print = do_info_kvm_print,
|
|
|
|
.mhandler.info_new = do_info_kvm,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "numa",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show NUMA information",
|
2009-10-08 00:41:51 +08:00
|
|
|
.mhandler.info = do_info_numa,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "usb",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show guest USB devices",
|
2009-10-08 00:41:51 +08:00
|
|
|
.mhandler.info = usb_info,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "usbhost",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show host USB devices",
|
2009-10-08 00:41:51 +08:00
|
|
|
.mhandler.info = usb_host_info,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "profile",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show profiling information",
|
2009-10-08 00:41:51 +08:00
|
|
|
.mhandler.info = do_info_profile,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "capture",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show capture information",
|
2009-10-08 00:41:51 +08:00
|
|
|
.mhandler.info = do_info_capture,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "snapshots",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the currently saved VM snapshots",
|
2009-10-08 00:41:51 +08:00
|
|
|
.mhandler.info = do_info_snapshots,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "status",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the current VM status (running|paused)",
|
2009-12-11 03:15:59 +08:00
|
|
|
.user_print = do_info_status_print,
|
|
|
|
.mhandler.info_new = do_info_status,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "pcmcia",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show guest PCMCIA status",
|
2009-10-08 00:41:51 +08:00
|
|
|
.mhandler.info = pcmcia_info,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "mice",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show which guest mouse is receiving events",
|
2009-12-11 03:16:04 +08:00
|
|
|
.user_print = do_info_mice_print,
|
|
|
|
.mhandler.info_new = do_info_mice,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "vnc",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the vnc server status",
|
2009-12-11 03:16:10 +08:00
|
|
|
.user_print = do_info_vnc_print,
|
|
|
|
.mhandler.info_new = do_info_vnc,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "name",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the current VM name",
|
2009-12-11 03:16:01 +08:00
|
|
|
.user_print = do_info_name_print,
|
|
|
|
.mhandler.info_new = do_info_name,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "uuid",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show the current VM UUID",
|
2009-12-11 03:16:03 +08:00
|
|
|
.user_print = do_info_uuid_print,
|
|
|
|
.mhandler.info_new = do_info_uuid,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
2007-03-07 16:32:30 +08:00
|
|
|
#if defined(TARGET_PPC)
|
2009-10-08 00:41:50 +08:00
|
|
|
{
|
|
|
|
.name = "cpustats",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show CPU statistics",
|
2009-10-08 00:41:51 +08:00
|
|
|
.mhandler.info = do_info_cpu_stats,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
2007-03-07 16:32:30 +08:00
|
|
|
#endif
|
2007-10-27 02:42:59 +08:00
|
|
|
#if defined(CONFIG_SLIRP)
|
2009-10-08 00:41:50 +08:00
|
|
|
{
|
|
|
|
.name = "usernet",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show user network stack connection states",
|
2009-10-08 00:41:51 +08:00
|
|
|
.mhandler.info = do_info_usernet,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
2007-10-27 02:42:59 +08:00
|
|
|
#endif
|
2009-10-08 00:41:50 +08:00
|
|
|
{
|
|
|
|
.name = "migrate",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show migration status",
|
2009-12-11 03:16:05 +08:00
|
|
|
.user_print = do_info_migrate_print,
|
|
|
|
.mhandler.info_new = do_info_migrate,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "balloon",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show balloon information",
|
2009-10-08 00:42:03 +08:00
|
|
|
.user_print = monitor_print_balloon,
|
|
|
|
.mhandler.info_new = do_info_balloon,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "qtree",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show device tree",
|
2009-10-08 00:41:51 +08:00
|
|
|
.mhandler.info = do_info_qtree,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "qdm",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show qdev device model list",
|
2009-10-08 00:41:51 +08:00
|
|
|
.mhandler.info = do_info_qdm,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "roms",
|
|
|
|
.args_type = "",
|
|
|
|
.params = "",
|
|
|
|
.help = "show roms",
|
2009-10-08 00:41:51 +08:00
|
|
|
.mhandler.info = do_info_roms,
|
2009-10-08 00:41:50 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = NULL,
|
|
|
|
},
|
2004-03-15 05:38:27 +08:00
|
|
|
};
|
|
|
|
|
2004-04-04 20:57:25 +08:00
|
|
|
/*******************************************************************/
|
|
|
|
|
|
|
|
static const char *pch;
|
|
|
|
static jmp_buf expr_env;
|
|
|
|
|
2005-02-11 06:00:52 +08:00
|
|
|
#define MD_TLONG 0
|
|
|
|
#define MD_I32 1
|
|
|
|
|
2004-04-04 20:57:25 +08:00
|
|
|
typedef struct MonitorDef {
|
|
|
|
const char *name;
|
|
|
|
int offset;
|
2008-10-03 02:32:44 +08:00
|
|
|
target_long (*get_value)(const struct MonitorDef *md, int val);
|
2005-02-11 06:00:52 +08:00
|
|
|
int type;
|
2004-04-04 20:57:25 +08:00
|
|
|
} MonitorDef;
|
|
|
|
|
2004-04-26 02:54:52 +08:00
|
|
|
#if defined(TARGET_I386)
|
2008-10-03 02:32:44 +08:00
|
|
|
static target_long monitor_get_pc (const struct MonitorDef *md, int val)
|
2004-04-26 02:54:52 +08:00
|
|
|
{
|
2005-11-22 07:25:50 +08:00
|
|
|
CPUState *env = mon_get_cpu();
|
|
|
|
return env->eip + env->segs[R_CS].base;
|
2004-04-26 02:54:52 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2004-04-13 04:39:29 +08:00
|
|
|
#if defined(TARGET_PPC)
|
2008-10-03 02:32:44 +08:00
|
|
|
static target_long monitor_get_ccr (const struct MonitorDef *md, int val)
|
2004-04-13 04:39:29 +08:00
|
|
|
{
|
2005-11-22 07:25:50 +08:00
|
|
|
CPUState *env = mon_get_cpu();
|
2004-04-13 04:39:29 +08:00
|
|
|
unsigned int u;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
u = 0;
|
|
|
|
for (i = 0; i < 8; i++)
|
2009-03-07 04:27:40 +08:00
|
|
|
u |= env->crf[i] << (32 - (4 * i));
|
2004-04-13 04:39:29 +08:00
|
|
|
|
|
|
|
return u;
|
|
|
|
}
|
|
|
|
|
2008-10-03 02:32:44 +08:00
|
|
|
static target_long monitor_get_msr (const struct MonitorDef *md, int val)
|
2004-04-13 04:39:29 +08:00
|
|
|
{
|
2005-11-22 07:25:50 +08:00
|
|
|
CPUState *env = mon_get_cpu();
|
2007-10-26 05:35:50 +08:00
|
|
|
return env->msr;
|
2004-04-13 04:39:29 +08:00
|
|
|
}
|
|
|
|
|
2008-10-03 02:32:44 +08:00
|
|
|
static target_long monitor_get_xer (const struct MonitorDef *md, int val)
|
2004-04-13 04:39:29 +08:00
|
|
|
{
|
2005-11-22 07:25:50 +08:00
|
|
|
CPUState *env = mon_get_cpu();
|
2008-10-21 19:28:46 +08:00
|
|
|
return env->xer;
|
2004-04-13 04:39:29 +08:00
|
|
|
}
|
2004-05-21 20:59:32 +08:00
|
|
|
|
2008-10-03 02:32:44 +08:00
|
|
|
static target_long monitor_get_decr (const struct MonitorDef *md, int val)
|
2004-05-21 20:59:32 +08:00
|
|
|
{
|
2005-11-22 07:25:50 +08:00
|
|
|
CPUState *env = mon_get_cpu();
|
|
|
|
return cpu_ppc_load_decr(env);
|
2004-05-21 20:59:32 +08:00
|
|
|
}
|
|
|
|
|
2008-10-03 02:32:44 +08:00
|
|
|
static target_long monitor_get_tbu (const struct MonitorDef *md, int val)
|
2004-05-21 20:59:32 +08:00
|
|
|
{
|
2005-11-22 07:25:50 +08:00
|
|
|
CPUState *env = mon_get_cpu();
|
|
|
|
return cpu_ppc_load_tbu(env);
|
2004-05-21 20:59:32 +08:00
|
|
|
}
|
|
|
|
|
2008-10-03 02:32:44 +08:00
|
|
|
static target_long monitor_get_tbl (const struct MonitorDef *md, int val)
|
2004-05-21 20:59:32 +08:00
|
|
|
{
|
2005-11-22 07:25:50 +08:00
|
|
|
CPUState *env = mon_get_cpu();
|
|
|
|
return cpu_ppc_load_tbl(env);
|
2004-05-21 20:59:32 +08:00
|
|
|
}
|
2004-04-13 04:39:29 +08:00
|
|
|
#endif
|
|
|
|
|
2004-10-01 06:22:08 +08:00
|
|
|
#if defined(TARGET_SPARC)
|
2005-10-31 01:05:13 +08:00
|
|
|
#ifndef TARGET_SPARC64
|
2008-10-03 02:32:44 +08:00
|
|
|
static target_long monitor_get_psr (const struct MonitorDef *md, int val)
|
2004-10-01 06:22:08 +08:00
|
|
|
{
|
2005-11-22 07:25:50 +08:00
|
|
|
CPUState *env = mon_get_cpu();
|
|
|
|
return GET_PSR(env);
|
2004-10-01 06:22:08 +08:00
|
|
|
}
|
2005-10-31 01:05:13 +08:00
|
|
|
#endif
|
2004-10-01 06:22:08 +08:00
|
|
|
|
2008-10-03 02:32:44 +08:00
|
|
|
static target_long monitor_get_reg(const struct MonitorDef *md, int val)
|
2004-10-01 06:22:08 +08:00
|
|
|
{
|
2005-11-22 07:25:50 +08:00
|
|
|
CPUState *env = mon_get_cpu();
|
|
|
|
return env->regwptr[val];
|
2004-10-01 06:22:08 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-10-03 02:32:44 +08:00
|
|
|
static const MonitorDef monitor_defs[] = {
|
2004-04-04 20:57:25 +08:00
|
|
|
#ifdef TARGET_I386
|
2004-04-26 02:54:52 +08:00
|
|
|
|
|
|
|
#define SEG(name, seg) \
|
2005-02-11 06:00:52 +08:00
|
|
|
{ name, offsetof(CPUState, segs[seg].selector), NULL, MD_I32 },\
|
2004-04-26 02:54:52 +08:00
|
|
|
{ name ".base", offsetof(CPUState, segs[seg].base) },\
|
2005-02-11 06:00:52 +08:00
|
|
|
{ name ".limit", offsetof(CPUState, segs[seg].limit), NULL, MD_I32 },
|
2004-04-26 02:54:52 +08:00
|
|
|
|
2004-04-04 20:57:25 +08:00
|
|
|
{ "eax", offsetof(CPUState, regs[0]) },
|
|
|
|
{ "ecx", offsetof(CPUState, regs[1]) },
|
|
|
|
{ "edx", offsetof(CPUState, regs[2]) },
|
|
|
|
{ "ebx", offsetof(CPUState, regs[3]) },
|
|
|
|
{ "esp|sp", offsetof(CPUState, regs[4]) },
|
|
|
|
{ "ebp|fp", offsetof(CPUState, regs[5]) },
|
|
|
|
{ "esi", offsetof(CPUState, regs[6]) },
|
2004-09-14 05:36:46 +08:00
|
|
|
{ "edi", offsetof(CPUState, regs[7]) },
|
2005-02-11 06:00:52 +08:00
|
|
|
#ifdef TARGET_X86_64
|
|
|
|
{ "r8", offsetof(CPUState, regs[8]) },
|
|
|
|
{ "r9", offsetof(CPUState, regs[9]) },
|
|
|
|
{ "r10", offsetof(CPUState, regs[10]) },
|
|
|
|
{ "r11", offsetof(CPUState, regs[11]) },
|
|
|
|
{ "r12", offsetof(CPUState, regs[12]) },
|
|
|
|
{ "r13", offsetof(CPUState, regs[13]) },
|
|
|
|
{ "r14", offsetof(CPUState, regs[14]) },
|
|
|
|
{ "r15", offsetof(CPUState, regs[15]) },
|
|
|
|
#endif
|
2004-04-04 20:57:25 +08:00
|
|
|
{ "eflags", offsetof(CPUState, eflags) },
|
2004-04-26 02:54:52 +08:00
|
|
|
{ "eip", offsetof(CPUState, eip) },
|
|
|
|
SEG("cs", R_CS)
|
|
|
|
SEG("ds", R_DS)
|
|
|
|
SEG("es", R_ES)
|
2004-09-14 05:36:46 +08:00
|
|
|
SEG("ss", R_SS)
|
2004-04-26 02:54:52 +08:00
|
|
|
SEG("fs", R_FS)
|
|
|
|
SEG("gs", R_GS)
|
|
|
|
{ "pc", 0, monitor_get_pc, },
|
2004-04-13 04:39:29 +08:00
|
|
|
#elif defined(TARGET_PPC)
|
2007-09-19 13:49:13 +08:00
|
|
|
/* General purpose registers */
|
2004-04-13 04:39:29 +08:00
|
|
|
{ "r0", offsetof(CPUState, gpr[0]) },
|
|
|
|
{ "r1", offsetof(CPUState, gpr[1]) },
|
|
|
|
{ "r2", offsetof(CPUState, gpr[2]) },
|
|
|
|
{ "r3", offsetof(CPUState, gpr[3]) },
|
|
|
|
{ "r4", offsetof(CPUState, gpr[4]) },
|
|
|
|
{ "r5", offsetof(CPUState, gpr[5]) },
|
|
|
|
{ "r6", offsetof(CPUState, gpr[6]) },
|
|
|
|
{ "r7", offsetof(CPUState, gpr[7]) },
|
|
|
|
{ "r8", offsetof(CPUState, gpr[8]) },
|
|
|
|
{ "r9", offsetof(CPUState, gpr[9]) },
|
|
|
|
{ "r10", offsetof(CPUState, gpr[10]) },
|
|
|
|
{ "r11", offsetof(CPUState, gpr[11]) },
|
|
|
|
{ "r12", offsetof(CPUState, gpr[12]) },
|
|
|
|
{ "r13", offsetof(CPUState, gpr[13]) },
|
|
|
|
{ "r14", offsetof(CPUState, gpr[14]) },
|
|
|
|
{ "r15", offsetof(CPUState, gpr[15]) },
|
|
|
|
{ "r16", offsetof(CPUState, gpr[16]) },
|
|
|
|
{ "r17", offsetof(CPUState, gpr[17]) },
|
|
|
|
{ "r18", offsetof(CPUState, gpr[18]) },
|
|
|
|
{ "r19", offsetof(CPUState, gpr[19]) },
|
|
|
|
{ "r20", offsetof(CPUState, gpr[20]) },
|
|
|
|
{ "r21", offsetof(CPUState, gpr[21]) },
|
|
|
|
{ "r22", offsetof(CPUState, gpr[22]) },
|
|
|
|
{ "r23", offsetof(CPUState, gpr[23]) },
|
|
|
|
{ "r24", offsetof(CPUState, gpr[24]) },
|
|
|
|
{ "r25", offsetof(CPUState, gpr[25]) },
|
|
|
|
{ "r26", offsetof(CPUState, gpr[26]) },
|
|
|
|
{ "r27", offsetof(CPUState, gpr[27]) },
|
|
|
|
{ "r28", offsetof(CPUState, gpr[28]) },
|
|
|
|
{ "r29", offsetof(CPUState, gpr[29]) },
|
|
|
|
{ "r30", offsetof(CPUState, gpr[30]) },
|
|
|
|
{ "r31", offsetof(CPUState, gpr[31]) },
|
2007-09-19 13:49:13 +08:00
|
|
|
/* Floating point registers */
|
|
|
|
{ "f0", offsetof(CPUState, fpr[0]) },
|
|
|
|
{ "f1", offsetof(CPUState, fpr[1]) },
|
|
|
|
{ "f2", offsetof(CPUState, fpr[2]) },
|
|
|
|
{ "f3", offsetof(CPUState, fpr[3]) },
|
|
|
|
{ "f4", offsetof(CPUState, fpr[4]) },
|
|
|
|
{ "f5", offsetof(CPUState, fpr[5]) },
|
|
|
|
{ "f6", offsetof(CPUState, fpr[6]) },
|
|
|
|
{ "f7", offsetof(CPUState, fpr[7]) },
|
|
|
|
{ "f8", offsetof(CPUState, fpr[8]) },
|
|
|
|
{ "f9", offsetof(CPUState, fpr[9]) },
|
|
|
|
{ "f10", offsetof(CPUState, fpr[10]) },
|
|
|
|
{ "f11", offsetof(CPUState, fpr[11]) },
|
|
|
|
{ "f12", offsetof(CPUState, fpr[12]) },
|
|
|
|
{ "f13", offsetof(CPUState, fpr[13]) },
|
|
|
|
{ "f14", offsetof(CPUState, fpr[14]) },
|
|
|
|
{ "f15", offsetof(CPUState, fpr[15]) },
|
|
|
|
{ "f16", offsetof(CPUState, fpr[16]) },
|
|
|
|
{ "f17", offsetof(CPUState, fpr[17]) },
|
|
|
|
{ "f18", offsetof(CPUState, fpr[18]) },
|
|
|
|
{ "f19", offsetof(CPUState, fpr[19]) },
|
|
|
|
{ "f20", offsetof(CPUState, fpr[20]) },
|
|
|
|
{ "f21", offsetof(CPUState, fpr[21]) },
|
|
|
|
{ "f22", offsetof(CPUState, fpr[22]) },
|
|
|
|
{ "f23", offsetof(CPUState, fpr[23]) },
|
|
|
|
{ "f24", offsetof(CPUState, fpr[24]) },
|
|
|
|
{ "f25", offsetof(CPUState, fpr[25]) },
|
|
|
|
{ "f26", offsetof(CPUState, fpr[26]) },
|
|
|
|
{ "f27", offsetof(CPUState, fpr[27]) },
|
|
|
|
{ "f28", offsetof(CPUState, fpr[28]) },
|
|
|
|
{ "f29", offsetof(CPUState, fpr[29]) },
|
|
|
|
{ "f30", offsetof(CPUState, fpr[30]) },
|
|
|
|
{ "f31", offsetof(CPUState, fpr[31]) },
|
|
|
|
{ "fpscr", offsetof(CPUState, fpscr) },
|
|
|
|
/* Next instruction pointer */
|
2004-04-26 02:54:52 +08:00
|
|
|
{ "nip|pc", offsetof(CPUState, nip) },
|
2004-04-13 04:39:29 +08:00
|
|
|
{ "lr", offsetof(CPUState, lr) },
|
|
|
|
{ "ctr", offsetof(CPUState, ctr) },
|
2004-05-21 20:59:32 +08:00
|
|
|
{ "decr", 0, &monitor_get_decr, },
|
2004-04-13 04:39:29 +08:00
|
|
|
{ "ccr", 0, &monitor_get_ccr, },
|
2007-09-19 13:49:13 +08:00
|
|
|
/* Machine state register */
|
2004-04-13 04:39:29 +08:00
|
|
|
{ "msr", 0, &monitor_get_msr, },
|
|
|
|
{ "xer", 0, &monitor_get_xer, },
|
2004-05-21 20:59:32 +08:00
|
|
|
{ "tbu", 0, &monitor_get_tbu, },
|
|
|
|
{ "tbl", 0, &monitor_get_tbl, },
|
2007-09-19 13:49:13 +08:00
|
|
|
#if defined(TARGET_PPC64)
|
|
|
|
/* Address space register */
|
|
|
|
{ "asr", offsetof(CPUState, asr) },
|
|
|
|
#endif
|
|
|
|
/* Segment registers */
|
2004-04-13 04:39:29 +08:00
|
|
|
{ "sdr1", offsetof(CPUState, sdr1) },
|
|
|
|
{ "sr0", offsetof(CPUState, sr[0]) },
|
|
|
|
{ "sr1", offsetof(CPUState, sr[1]) },
|
|
|
|
{ "sr2", offsetof(CPUState, sr[2]) },
|
|
|
|
{ "sr3", offsetof(CPUState, sr[3]) },
|
|
|
|
{ "sr4", offsetof(CPUState, sr[4]) },
|
|
|
|
{ "sr5", offsetof(CPUState, sr[5]) },
|
|
|
|
{ "sr6", offsetof(CPUState, sr[6]) },
|
|
|
|
{ "sr7", offsetof(CPUState, sr[7]) },
|
|
|
|
{ "sr8", offsetof(CPUState, sr[8]) },
|
|
|
|
{ "sr9", offsetof(CPUState, sr[9]) },
|
|
|
|
{ "sr10", offsetof(CPUState, sr[10]) },
|
|
|
|
{ "sr11", offsetof(CPUState, sr[11]) },
|
|
|
|
{ "sr12", offsetof(CPUState, sr[12]) },
|
|
|
|
{ "sr13", offsetof(CPUState, sr[13]) },
|
|
|
|
{ "sr14", offsetof(CPUState, sr[14]) },
|
|
|
|
{ "sr15", offsetof(CPUState, sr[15]) },
|
|
|
|
/* Too lazy to put BATs and SPRs ... */
|
2004-10-01 06:22:08 +08:00
|
|
|
#elif defined(TARGET_SPARC)
|
|
|
|
{ "g0", offsetof(CPUState, gregs[0]) },
|
|
|
|
{ "g1", offsetof(CPUState, gregs[1]) },
|
|
|
|
{ "g2", offsetof(CPUState, gregs[2]) },
|
|
|
|
{ "g3", offsetof(CPUState, gregs[3]) },
|
|
|
|
{ "g4", offsetof(CPUState, gregs[4]) },
|
|
|
|
{ "g5", offsetof(CPUState, gregs[5]) },
|
|
|
|
{ "g6", offsetof(CPUState, gregs[6]) },
|
|
|
|
{ "g7", offsetof(CPUState, gregs[7]) },
|
|
|
|
{ "o0", 0, monitor_get_reg },
|
|
|
|
{ "o1", 1, monitor_get_reg },
|
|
|
|
{ "o2", 2, monitor_get_reg },
|
|
|
|
{ "o3", 3, monitor_get_reg },
|
|
|
|
{ "o4", 4, monitor_get_reg },
|
|
|
|
{ "o5", 5, monitor_get_reg },
|
|
|
|
{ "o6", 6, monitor_get_reg },
|
|
|
|
{ "o7", 7, monitor_get_reg },
|
|
|
|
{ "l0", 8, monitor_get_reg },
|
|
|
|
{ "l1", 9, monitor_get_reg },
|
|
|
|
{ "l2", 10, monitor_get_reg },
|
|
|
|
{ "l3", 11, monitor_get_reg },
|
|
|
|
{ "l4", 12, monitor_get_reg },
|
|
|
|
{ "l5", 13, monitor_get_reg },
|
|
|
|
{ "l6", 14, monitor_get_reg },
|
|
|
|
{ "l7", 15, monitor_get_reg },
|
|
|
|
{ "i0", 16, monitor_get_reg },
|
|
|
|
{ "i1", 17, monitor_get_reg },
|
|
|
|
{ "i2", 18, monitor_get_reg },
|
|
|
|
{ "i3", 19, monitor_get_reg },
|
|
|
|
{ "i4", 20, monitor_get_reg },
|
|
|
|
{ "i5", 21, monitor_get_reg },
|
|
|
|
{ "i6", 22, monitor_get_reg },
|
|
|
|
{ "i7", 23, monitor_get_reg },
|
|
|
|
{ "pc", offsetof(CPUState, pc) },
|
|
|
|
{ "npc", offsetof(CPUState, npc) },
|
|
|
|
{ "y", offsetof(CPUState, y) },
|
2005-10-31 01:05:13 +08:00
|
|
|
#ifndef TARGET_SPARC64
|
2004-10-01 06:22:08 +08:00
|
|
|
{ "psr", 0, &monitor_get_psr, },
|
|
|
|
{ "wim", offsetof(CPUState, wim) },
|
2005-10-31 01:05:13 +08:00
|
|
|
#endif
|
2004-10-01 06:22:08 +08:00
|
|
|
{ "tbr", offsetof(CPUState, tbr) },
|
|
|
|
{ "fsr", offsetof(CPUState, fsr) },
|
|
|
|
{ "f0", offsetof(CPUState, fpr[0]) },
|
|
|
|
{ "f1", offsetof(CPUState, fpr[1]) },
|
|
|
|
{ "f2", offsetof(CPUState, fpr[2]) },
|
|
|
|
{ "f3", offsetof(CPUState, fpr[3]) },
|
|
|
|
{ "f4", offsetof(CPUState, fpr[4]) },
|
|
|
|
{ "f5", offsetof(CPUState, fpr[5]) },
|
|
|
|
{ "f6", offsetof(CPUState, fpr[6]) },
|
|
|
|
{ "f7", offsetof(CPUState, fpr[7]) },
|
|
|
|
{ "f8", offsetof(CPUState, fpr[8]) },
|
|
|
|
{ "f9", offsetof(CPUState, fpr[9]) },
|
|
|
|
{ "f10", offsetof(CPUState, fpr[10]) },
|
|
|
|
{ "f11", offsetof(CPUState, fpr[11]) },
|
|
|
|
{ "f12", offsetof(CPUState, fpr[12]) },
|
|
|
|
{ "f13", offsetof(CPUState, fpr[13]) },
|
|
|
|
{ "f14", offsetof(CPUState, fpr[14]) },
|
|
|
|
{ "f15", offsetof(CPUState, fpr[15]) },
|
|
|
|
{ "f16", offsetof(CPUState, fpr[16]) },
|
|
|
|
{ "f17", offsetof(CPUState, fpr[17]) },
|
|
|
|
{ "f18", offsetof(CPUState, fpr[18]) },
|
|
|
|
{ "f19", offsetof(CPUState, fpr[19]) },
|
|
|
|
{ "f20", offsetof(CPUState, fpr[20]) },
|
|
|
|
{ "f21", offsetof(CPUState, fpr[21]) },
|
|
|
|
{ "f22", offsetof(CPUState, fpr[22]) },
|
|
|
|
{ "f23", offsetof(CPUState, fpr[23]) },
|
|
|
|
{ "f24", offsetof(CPUState, fpr[24]) },
|
|
|
|
{ "f25", offsetof(CPUState, fpr[25]) },
|
|
|
|
{ "f26", offsetof(CPUState, fpr[26]) },
|
|
|
|
{ "f27", offsetof(CPUState, fpr[27]) },
|
|
|
|
{ "f28", offsetof(CPUState, fpr[28]) },
|
|
|
|
{ "f29", offsetof(CPUState, fpr[29]) },
|
|
|
|
{ "f30", offsetof(CPUState, fpr[30]) },
|
|
|
|
{ "f31", offsetof(CPUState, fpr[31]) },
|
2005-10-31 01:05:13 +08:00
|
|
|
#ifdef TARGET_SPARC64
|
|
|
|
{ "f32", offsetof(CPUState, fpr[32]) },
|
|
|
|
{ "f34", offsetof(CPUState, fpr[34]) },
|
|
|
|
{ "f36", offsetof(CPUState, fpr[36]) },
|
|
|
|
{ "f38", offsetof(CPUState, fpr[38]) },
|
|
|
|
{ "f40", offsetof(CPUState, fpr[40]) },
|
|
|
|
{ "f42", offsetof(CPUState, fpr[42]) },
|
|
|
|
{ "f44", offsetof(CPUState, fpr[44]) },
|
|
|
|
{ "f46", offsetof(CPUState, fpr[46]) },
|
|
|
|
{ "f48", offsetof(CPUState, fpr[48]) },
|
|
|
|
{ "f50", offsetof(CPUState, fpr[50]) },
|
|
|
|
{ "f52", offsetof(CPUState, fpr[52]) },
|
|
|
|
{ "f54", offsetof(CPUState, fpr[54]) },
|
|
|
|
{ "f56", offsetof(CPUState, fpr[56]) },
|
|
|
|
{ "f58", offsetof(CPUState, fpr[58]) },
|
|
|
|
{ "f60", offsetof(CPUState, fpr[60]) },
|
|
|
|
{ "f62", offsetof(CPUState, fpr[62]) },
|
|
|
|
{ "asi", offsetof(CPUState, asi) },
|
|
|
|
{ "pstate", offsetof(CPUState, pstate) },
|
|
|
|
{ "cansave", offsetof(CPUState, cansave) },
|
|
|
|
{ "canrestore", offsetof(CPUState, canrestore) },
|
|
|
|
{ "otherwin", offsetof(CPUState, otherwin) },
|
|
|
|
{ "wstate", offsetof(CPUState, wstate) },
|
|
|
|
{ "cleanwin", offsetof(CPUState, cleanwin) },
|
|
|
|
{ "fprs", offsetof(CPUState, fprs) },
|
|
|
|
#endif
|
2004-04-04 20:57:25 +08:00
|
|
|
#endif
|
|
|
|
{ NULL },
|
|
|
|
};
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
static void expr_error(Monitor *mon, const char *msg)
|
2004-03-15 05:38:27 +08:00
|
|
|
{
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "%s\n", msg);
|
2004-04-04 20:57:25 +08:00
|
|
|
longjmp(expr_env, 1);
|
|
|
|
}
|
|
|
|
|
2010-01-20 20:07:30 +08:00
|
|
|
/* return 0 if OK, -1 if not found */
|
2005-02-11 06:00:52 +08:00
|
|
|
static int get_monitor_def(target_long *pval, const char *name)
|
2004-04-04 20:57:25 +08:00
|
|
|
{
|
2008-10-03 02:32:44 +08:00
|
|
|
const MonitorDef *md;
|
2005-02-11 06:00:52 +08:00
|
|
|
void *ptr;
|
|
|
|
|
2004-04-04 20:57:25 +08:00
|
|
|
for(md = monitor_defs; md->name != NULL; md++) {
|
|
|
|
if (compare_cmd(name, md->name)) {
|
|
|
|
if (md->get_value) {
|
2004-10-01 06:22:08 +08:00
|
|
|
*pval = md->get_value(md, md->offset);
|
2004-04-04 20:57:25 +08:00
|
|
|
} else {
|
2005-11-22 07:25:50 +08:00
|
|
|
CPUState *env = mon_get_cpu();
|
|
|
|
ptr = (uint8_t *)env + md->offset;
|
2005-02-11 06:00:52 +08: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 20:57:25 +08:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void next(void)
|
|
|
|
{
|
2009-08-01 05:16:51 +08:00
|
|
|
if (*pch != '\0') {
|
2004-04-04 20:57:25 +08:00
|
|
|
pch++;
|
2008-11-16 21:53:32 +08:00
|
|
|
while (qemu_isspace(*pch))
|
2004-04-04 20:57:25 +08:00
|
|
|
pch++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
static int64_t expr_sum(Monitor *mon);
|
2004-04-04 20:57:25 +08:00
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
static int64_t expr_unary(Monitor *mon)
|
2004-04-04 20:57:25 +08:00
|
|
|
{
|
2007-09-26 01:28:42 +08:00
|
|
|
int64_t n;
|
2004-04-04 20:57:25 +08:00
|
|
|
char *p;
|
2005-11-22 07:25:50 +08:00
|
|
|
int ret;
|
2004-04-04 20:57:25 +08:00
|
|
|
|
|
|
|
switch(*pch) {
|
|
|
|
case '+':
|
|
|
|
next();
|
2009-03-06 07:01:23 +08:00
|
|
|
n = expr_unary(mon);
|
2004-04-04 20:57:25 +08:00
|
|
|
break;
|
|
|
|
case '-':
|
|
|
|
next();
|
2009-03-06 07:01:23 +08:00
|
|
|
n = -expr_unary(mon);
|
2004-04-04 20:57:25 +08:00
|
|
|
break;
|
|
|
|
case '~':
|
|
|
|
next();
|
2009-03-06 07:01:23 +08:00
|
|
|
n = ~expr_unary(mon);
|
2004-04-04 20:57:25 +08:00
|
|
|
break;
|
|
|
|
case '(':
|
|
|
|
next();
|
2009-03-06 07:01:23 +08:00
|
|
|
n = expr_sum(mon);
|
2004-04-04 20:57:25 +08:00
|
|
|
if (*pch != ')') {
|
2009-03-06 07:01:23 +08:00
|
|
|
expr_error(mon, "')' expected");
|
2004-04-04 20:57:25 +08:00
|
|
|
}
|
|
|
|
next();
|
|
|
|
break;
|
2004-07-15 01:21:37 +08:00
|
|
|
case '\'':
|
|
|
|
pch++;
|
|
|
|
if (*pch == '\0')
|
2009-03-06 07:01:23 +08:00
|
|
|
expr_error(mon, "character constant expected");
|
2004-07-15 01:21:37 +08:00
|
|
|
n = *pch;
|
|
|
|
pch++;
|
|
|
|
if (*pch != '\'')
|
2009-03-06 07:01:23 +08:00
|
|
|
expr_error(mon, "missing terminating \' character");
|
2004-07-15 01:21:37 +08:00
|
|
|
next();
|
|
|
|
break;
|
2004-04-04 20:57:25 +08:00
|
|
|
case '$':
|
|
|
|
{
|
|
|
|
char buf[128], *q;
|
2007-12-17 11:15:52 +08:00
|
|
|
target_long reg=0;
|
2007-09-17 16:09:54 +08:00
|
|
|
|
2004-04-04 20:57:25 +08:00
|
|
|
pch++;
|
|
|
|
q = buf;
|
|
|
|
while ((*pch >= 'a' && *pch <= 'z') ||
|
|
|
|
(*pch >= 'A' && *pch <= 'Z') ||
|
|
|
|
(*pch >= '0' && *pch <= '9') ||
|
2004-04-26 02:54:52 +08:00
|
|
|
*pch == '_' || *pch == '.') {
|
2004-04-04 20:57:25 +08:00
|
|
|
if ((q - buf) < sizeof(buf) - 1)
|
|
|
|
*q++ = *pch;
|
|
|
|
pch++;
|
|
|
|
}
|
2008-11-16 21:53:32 +08:00
|
|
|
while (qemu_isspace(*pch))
|
2004-04-04 20:57:25 +08:00
|
|
|
pch++;
|
|
|
|
*q = 0;
|
2007-09-25 02:39:04 +08:00
|
|
|
ret = get_monitor_def(®, buf);
|
2010-01-20 20:07:30 +08:00
|
|
|
if (ret < 0)
|
2009-03-06 07:01:23 +08:00
|
|
|
expr_error(mon, "unknown register");
|
2007-09-25 02:39:04 +08:00
|
|
|
n = reg;
|
2004-04-04 20:57:25 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '\0':
|
2009-03-06 07:01:23 +08:00
|
|
|
expr_error(mon, "unexpected end of expression");
|
2004-04-04 20:57:25 +08:00
|
|
|
n = 0;
|
|
|
|
break;
|
|
|
|
default:
|
2007-09-25 02:39:04 +08:00
|
|
|
#if TARGET_PHYS_ADDR_BITS > 32
|
2006-06-26 02:28:12 +08:00
|
|
|
n = strtoull(pch, &p, 0);
|
|
|
|
#else
|
2004-04-04 20:57:25 +08:00
|
|
|
n = strtoul(pch, &p, 0);
|
2006-06-26 02:28:12 +08:00
|
|
|
#endif
|
2004-04-04 20:57:25 +08:00
|
|
|
if (pch == p) {
|
2009-03-06 07:01:23 +08:00
|
|
|
expr_error(mon, "invalid char in expression");
|
2004-04-04 20:57:25 +08:00
|
|
|
}
|
|
|
|
pch = p;
|
2008-11-16 21:53:32 +08:00
|
|
|
while (qemu_isspace(*pch))
|
2004-04-04 20:57:25 +08:00
|
|
|
pch++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
static int64_t expr_prod(Monitor *mon)
|
2004-04-04 20:57:25 +08:00
|
|
|
{
|
2007-09-26 01:28:42 +08:00
|
|
|
int64_t val, val2;
|
2005-02-11 06:00:52 +08:00
|
|
|
int op;
|
2007-09-17 16:09:54 +08:00
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
val = expr_unary(mon);
|
2004-04-04 20:57:25 +08:00
|
|
|
for(;;) {
|
|
|
|
op = *pch;
|
|
|
|
if (op != '*' && op != '/' && op != '%')
|
|
|
|
break;
|
|
|
|
next();
|
2009-03-06 07:01:23 +08:00
|
|
|
val2 = expr_unary(mon);
|
2004-04-04 20:57:25 +08:00
|
|
|
switch(op) {
|
|
|
|
default:
|
|
|
|
case '*':
|
|
|
|
val *= val2;
|
|
|
|
break;
|
|
|
|
case '/':
|
|
|
|
case '%':
|
2007-09-17 05:08:06 +08:00
|
|
|
if (val2 == 0)
|
2009-03-06 07:01:23 +08:00
|
|
|
expr_error(mon, "division by zero");
|
2004-04-04 20:57:25 +08:00
|
|
|
if (op == '/')
|
|
|
|
val /= val2;
|
|
|
|
else
|
|
|
|
val %= val2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
static int64_t expr_logic(Monitor *mon)
|
2004-04-04 20:57:25 +08:00
|
|
|
{
|
2007-09-26 01:28:42 +08:00
|
|
|
int64_t val, val2;
|
2005-02-11 06:00:52 +08:00
|
|
|
int op;
|
2004-04-04 20:57:25 +08:00
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
val = expr_prod(mon);
|
2004-04-04 20:57:25 +08:00
|
|
|
for(;;) {
|
|
|
|
op = *pch;
|
|
|
|
if (op != '&' && op != '|' && op != '^')
|
|
|
|
break;
|
|
|
|
next();
|
2009-03-06 07:01:23 +08:00
|
|
|
val2 = expr_prod(mon);
|
2004-04-04 20:57:25 +08:00
|
|
|
switch(op) {
|
|
|
|
default:
|
|
|
|
case '&':
|
|
|
|
val &= val2;
|
|
|
|
break;
|
|
|
|
case '|':
|
|
|
|
val |= val2;
|
|
|
|
break;
|
|
|
|
case '^':
|
|
|
|
val ^= val2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
static int64_t expr_sum(Monitor *mon)
|
2004-04-04 20:57:25 +08:00
|
|
|
{
|
2007-09-26 01:28:42 +08:00
|
|
|
int64_t val, val2;
|
2005-02-11 06:00:52 +08:00
|
|
|
int op;
|
2004-04-04 20:57:25 +08:00
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
val = expr_logic(mon);
|
2004-04-04 20:57:25 +08:00
|
|
|
for(;;) {
|
|
|
|
op = *pch;
|
|
|
|
if (op != '+' && op != '-')
|
|
|
|
break;
|
|
|
|
next();
|
2009-03-06 07:01:23 +08:00
|
|
|
val2 = expr_logic(mon);
|
2004-04-04 20:57:25 +08:00
|
|
|
if (op == '+')
|
|
|
|
val += val2;
|
|
|
|
else
|
|
|
|
val -= val2;
|
|
|
|
}
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
static int get_expr(Monitor *mon, int64_t *pval, const char **pp)
|
2004-04-04 20:57:25 +08:00
|
|
|
{
|
|
|
|
pch = *pp;
|
|
|
|
if (setjmp(expr_env)) {
|
|
|
|
*pp = pch;
|
|
|
|
return -1;
|
|
|
|
}
|
2008-11-16 21:53:32 +08:00
|
|
|
while (qemu_isspace(*pch))
|
2004-04-04 20:57:25 +08:00
|
|
|
pch++;
|
2009-03-06 07:01:23 +08:00
|
|
|
*pval = expr_sum(mon);
|
2004-04-04 20:57:25 +08:00
|
|
|
*pp = pch;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int get_str(char *buf, int buf_size, const char **pp)
|
|
|
|
{
|
|
|
|
const char *p;
|
|
|
|
char *q;
|
|
|
|
int c;
|
|
|
|
|
2004-07-15 01:21:37 +08:00
|
|
|
q = buf;
|
2004-04-04 20:57:25 +08:00
|
|
|
p = *pp;
|
2008-11-16 21:53:32 +08:00
|
|
|
while (qemu_isspace(*p))
|
2004-04-04 20:57:25 +08:00
|
|
|
p++;
|
|
|
|
if (*p == '\0') {
|
|
|
|
fail:
|
2004-07-15 01:21:37 +08:00
|
|
|
*q = '\0';
|
2004-04-04 20:57:25 +08: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 05:41:05 +08:00
|
|
|
qemu_printf("unterminated string\n");
|
2004-04-04 20:57:25 +08:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
p++;
|
|
|
|
} else {
|
2008-11-16 21:53:32 +08:00
|
|
|
while (*p != '\0' && !qemu_isspace(*p)) {
|
2004-04-04 20:57:25 +08:00
|
|
|
if ((q - buf) < buf_size - 1) {
|
|
|
|
*q++ = *p;
|
|
|
|
}
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
2004-07-15 01:21:37 +08:00
|
|
|
*q = '\0';
|
2004-04-04 20:57:25 +08:00
|
|
|
*pp = p;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-06-10 05:21:30 +08: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-29 02:27:09 +08: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;
|
|
|
|
|
|
|
|
str = qemu_malloc(len + 1);
|
|
|
|
memcpy(str, type, len);
|
|
|
|
str[len] = '\0';
|
|
|
|
|
|
|
|
*key = str;
|
|
|
|
return ++p;
|
|
|
|
}
|
|
|
|
|
2004-04-04 20:57:25 +08:00
|
|
|
static int default_fmt_format = 'x';
|
|
|
|
static int default_fmt_size = 4;
|
|
|
|
|
|
|
|
#define MAX_ARGS 16
|
|
|
|
|
2009-11-02 21:41:13 +08: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);
|
|
|
|
}
|
|
|
|
|
2009-11-27 08:58:54 +08:00
|
|
|
static const mon_cmd_t *monitor_find_command(const char *cmdname)
|
|
|
|
{
|
|
|
|
const mon_cmd_t *cmd;
|
|
|
|
|
|
|
|
for (cmd = mon_cmds; cmd->name != NULL; cmd++) {
|
|
|
|
if (compare_cmd(cmdname, cmd->name)) {
|
|
|
|
return cmd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-10-02 05:12:16 +08:00
|
|
|
static const mon_cmd_t *monitor_parse_command(Monitor *mon,
|
2009-08-29 02:27:22 +08:00
|
|
|
const char *cmdline,
|
|
|
|
QDict *qdict)
|
2004-04-04 20:57:25 +08:00
|
|
|
{
|
2009-06-10 05:21:30 +08:00
|
|
|
const char *p, *typestr;
|
2009-08-29 02:27:25 +08:00
|
|
|
int c;
|
2009-10-02 05:12:16 +08:00
|
|
|
const mon_cmd_t *cmd;
|
2004-04-04 20:57:25 +08: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-29 02:27:09 +08:00
|
|
|
char *key;
|
2004-03-15 05:38:27 +08:00
|
|
|
|
|
|
|
#ifdef DEBUG
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "command='%s'\n", cmdline);
|
2004-03-15 05:38:27 +08:00
|
|
|
#endif
|
2007-09-17 16:09:54 +08:00
|
|
|
|
2004-04-04 20:57:25 +08:00
|
|
|
/* extract the command name */
|
2009-06-10 05:21:30 +08:00
|
|
|
p = get_command_name(cmdline, cmdname, sizeof(cmdname));
|
|
|
|
if (!p)
|
2009-08-29 02:27:22 +08:00
|
|
|
return NULL;
|
2007-09-17 16:09:54 +08:00
|
|
|
|
2009-11-27 08:58:54 +08:00
|
|
|
cmd = monitor_find_command(cmdname);
|
|
|
|
if (!cmd) {
|
2009-06-10 05:21:54 +08:00
|
|
|
monitor_printf(mon, "unknown command: '%s'\n", cmdname);
|
2009-08-29 02:27:22 +08:00
|
|
|
return NULL;
|
2004-04-04 20:57:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* parse the parameters */
|
|
|
|
typestr = cmd->args_type;
|
2004-03-15 05:38:27 +08: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-29 02:27:09 +08:00
|
|
|
typestr = key_get_info(typestr, &key);
|
|
|
|
if (!typestr)
|
2004-03-15 05:38:27 +08: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-29 02:27:09 +08:00
|
|
|
c = *typestr;
|
2004-04-04 20:57:25 +08:00
|
|
|
typestr++;
|
|
|
|
switch(c) {
|
|
|
|
case 'F':
|
2004-07-15 01:21:37 +08:00
|
|
|
case 'B':
|
2004-04-04 20:57:25 +08:00
|
|
|
case 's':
|
|
|
|
{
|
|
|
|
int ret;
|
2007-09-17 16:09:54 +08:00
|
|
|
|
2008-11-16 21:53:32 +08:00
|
|
|
while (qemu_isspace(*p))
|
2004-04-04 20:57:25 +08:00
|
|
|
p++;
|
|
|
|
if (*typestr == '?') {
|
|
|
|
typestr++;
|
|
|
|
if (*p == '\0') {
|
|
|
|
/* no optional string: NULL argument */
|
2009-08-29 02:27:25 +08:00
|
|
|
break;
|
2004-04-04 20:57:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ret = get_str(buf, sizeof(buf), &p);
|
|
|
|
if (ret < 0) {
|
2004-07-15 01:21:37 +08:00
|
|
|
switch(c) {
|
|
|
|
case 'F':
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "%s: filename expected\n",
|
|
|
|
cmdname);
|
2004-07-15 01:21:37 +08:00
|
|
|
break;
|
|
|
|
case 'B':
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "%s: block device name expected\n",
|
|
|
|
cmdname);
|
2004-07-15 01:21:37 +08:00
|
|
|
break;
|
|
|
|
default:
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "%s: string expected\n", cmdname);
|
2004-07-15 01:21:37 +08:00
|
|
|
break;
|
|
|
|
}
|
2004-04-04 20:57:25 +08:00
|
|
|
goto fail;
|
|
|
|
}
|
2009-08-29 02:27:25 +08:00
|
|
|
qdict_put(qdict, key, qstring_from_str(buf));
|
2004-04-04 20:57:25 +08:00
|
|
|
}
|
2004-03-15 05:38:27 +08:00
|
|
|
break;
|
2004-04-04 20:57:25 +08:00
|
|
|
case '/':
|
|
|
|
{
|
|
|
|
int count, format, size;
|
2007-09-17 16:09:54 +08:00
|
|
|
|
2008-11-16 21:53:32 +08:00
|
|
|
while (qemu_isspace(*p))
|
2004-04-04 20:57:25 +08:00
|
|
|
p++;
|
|
|
|
if (*p == '/') {
|
|
|
|
/* format found */
|
|
|
|
p++;
|
|
|
|
count = 1;
|
2008-11-16 21:53:32 +08:00
|
|
|
if (qemu_isdigit(*p)) {
|
2004-04-04 20:57:25 +08:00
|
|
|
count = 0;
|
2008-11-16 21:53:32 +08:00
|
|
|
while (qemu_isdigit(*p)) {
|
2004-04-04 20:57:25 +08: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 21:53:32 +08:00
|
|
|
if (*p != '\0' && !qemu_isspace(*p)) {
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "invalid char in format: '%c'\n",
|
|
|
|
*p);
|
2004-04-04 20:57:25 +08:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
if (format < 0)
|
|
|
|
format = default_fmt_format;
|
2004-04-26 02:05:08 +08:00
|
|
|
if (format != 'i') {
|
|
|
|
/* for 'i', not specifying a size gives -1 as size */
|
|
|
|
if (size < 0)
|
|
|
|
size = default_fmt_size;
|
2008-10-02 05:45:51 +08:00
|
|
|
default_fmt_size = size;
|
2004-04-26 02:05:08 +08:00
|
|
|
}
|
2004-04-04 20:57:25 +08:00
|
|
|
default_fmt_format = format;
|
|
|
|
} else {
|
|
|
|
count = 1;
|
|
|
|
format = default_fmt_format;
|
2004-04-26 02:05:08 +08:00
|
|
|
if (format != 'i') {
|
|
|
|
size = default_fmt_size;
|
|
|
|
} else {
|
|
|
|
size = -1;
|
|
|
|
}
|
2004-04-04 20:57:25 +08:00
|
|
|
}
|
2009-08-29 02:27:10 +08: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 20:57:25 +08:00
|
|
|
}
|
2004-03-15 05:38:27 +08:00
|
|
|
break;
|
2004-04-04 20:57:25 +08:00
|
|
|
case 'i':
|
2005-02-11 06:00:52 +08:00
|
|
|
case 'l':
|
2009-12-18 23:25:04 +08:00
|
|
|
case 'M':
|
2004-04-04 20:57:25 +08:00
|
|
|
{
|
2007-09-26 01:28:42 +08:00
|
|
|
int64_t val;
|
2007-09-25 02:39:04 +08:00
|
|
|
|
2008-11-16 21:53:32 +08:00
|
|
|
while (qemu_isspace(*p))
|
2004-04-04 20:57:25 +08:00
|
|
|
p++;
|
2004-06-08 08:55:58 +08:00
|
|
|
if (*typestr == '?' || *typestr == '.') {
|
|
|
|
if (*typestr == '?') {
|
2009-08-29 02:27:25 +08:00
|
|
|
if (*p == '\0') {
|
|
|
|
typestr++;
|
|
|
|
break;
|
|
|
|
}
|
2004-06-08 08:55:58 +08:00
|
|
|
} else {
|
|
|
|
if (*p == '.') {
|
|
|
|
p++;
|
2008-11-16 21:53:32 +08:00
|
|
|
while (qemu_isspace(*p))
|
2004-06-08 08:55:58 +08:00
|
|
|
p++;
|
|
|
|
} else {
|
2009-08-29 02:27:25 +08:00
|
|
|
typestr++;
|
|
|
|
break;
|
2004-06-08 08:55:58 +08:00
|
|
|
}
|
|
|
|
}
|
2006-07-15 06:03:35 +08:00
|
|
|
typestr++;
|
2004-04-04 20:57:25 +08:00
|
|
|
}
|
2009-03-06 07:01:23 +08:00
|
|
|
if (get_expr(mon, &val, &p))
|
2004-04-04 20:57:25 +08:00
|
|
|
goto fail;
|
2009-08-29 02:27:26 +08: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 23:25:04 +08:00
|
|
|
} else if (c == 'M') {
|
|
|
|
val <<= 20;
|
2009-08-29 02:27:26 +08:00
|
|
|
}
|
2009-08-29 02:27:25 +08:00
|
|
|
qdict_put(qdict, key, qint_from_int(val));
|
2004-04-04 20:57:25 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '-':
|
|
|
|
{
|
2009-11-02 21:41:13 +08:00
|
|
|
const char *tmp = p;
|
|
|
|
int has_option, skip_key = 0;
|
2004-04-04 20:57:25 +08:00
|
|
|
/* option */
|
2007-09-17 16:09:54 +08:00
|
|
|
|
2004-04-04 20:57:25 +08:00
|
|
|
c = *typestr++;
|
|
|
|
if (c == '\0')
|
|
|
|
goto bad_type;
|
2008-11-16 21:53:32 +08:00
|
|
|
while (qemu_isspace(*p))
|
2004-04-04 20:57:25 +08:00
|
|
|
p++;
|
|
|
|
has_option = 0;
|
|
|
|
if (*p == '-') {
|
|
|
|
p++;
|
2009-11-02 21:41:13 +08: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 {
|
|
|
|
p++;
|
|
|
|
has_option = 1;
|
2004-04-04 20:57:25 +08:00
|
|
|
}
|
|
|
|
}
|
2009-08-29 02:27:10 +08:00
|
|
|
qdict_put(qdict, key, qint_from_int(has_option));
|
2004-04-04 20:57:25 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
bad_type:
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "%s: unknown type '%c'\n", cmdname, c);
|
2004-04-04 20:57:25 +08:00
|
|
|
goto fail;
|
|
|
|
}
|
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-29 02:27:09 +08:00
|
|
|
qemu_free(key);
|
|
|
|
key = NULL;
|
2004-03-15 05:38:27 +08:00
|
|
|
}
|
2004-04-04 20:57:25 +08:00
|
|
|
/* check that all arguments were parsed */
|
2008-11-16 21:53:32 +08:00
|
|
|
while (qemu_isspace(*p))
|
2004-04-04 20:57:25 +08:00
|
|
|
p++;
|
|
|
|
if (*p != '\0') {
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "%s: extraneous characters at the end of line\n",
|
|
|
|
cmdname);
|
2004-04-04 20:57:25 +08:00
|
|
|
goto fail;
|
2004-03-15 05:38:27 +08:00
|
|
|
}
|
2004-04-04 20:57:25 +08:00
|
|
|
|
2009-08-29 02:27:22 +08:00
|
|
|
return cmd;
|
2009-08-14 16:36:06 +08:00
|
|
|
|
2009-08-29 02:27:22 +08:00
|
|
|
fail:
|
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-29 02:27:09 +08:00
|
|
|
qemu_free(key);
|
2009-08-29 02:27:22 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-11-19 09:05:31 +08:00
|
|
|
static void monitor_print_error(Monitor *mon)
|
|
|
|
{
|
|
|
|
qerror_print(mon->error);
|
|
|
|
QDECREF(mon->error);
|
|
|
|
mon->error = NULL;
|
|
|
|
}
|
|
|
|
|
2010-01-26 02:18:44 +08:00
|
|
|
static int is_async_return(const QObject *data)
|
|
|
|
{
|
|
|
|
return data && qdict_haskey(qobject_to_qdict(data), "__mon_async");
|
|
|
|
}
|
|
|
|
|
2009-11-27 08:58:53 +08:00
|
|
|
static void monitor_call_handler(Monitor *mon, const mon_cmd_t *cmd,
|
|
|
|
const QDict *params)
|
|
|
|
{
|
|
|
|
QObject *data = NULL;
|
|
|
|
|
|
|
|
cmd->mhandler.cmd_new(mon, params, &data);
|
2009-11-27 08:58:59 +08:00
|
|
|
|
2010-01-26 02:18:44 +08:00
|
|
|
if (is_async_return(data)) {
|
|
|
|
/*
|
|
|
|
* Asynchronous commands have no initial return data but they can
|
|
|
|
* generate errors. Data is returned via the async completion handler.
|
|
|
|
*/
|
|
|
|
if (monitor_ctrl_mode(mon) && monitor_has_error(mon)) {
|
|
|
|
monitor_protocol_emitter(mon, NULL);
|
|
|
|
}
|
|
|
|
} else if (monitor_ctrl_mode(mon)) {
|
2009-11-27 08:58:59 +08:00
|
|
|
/* Monitor Protocol */
|
|
|
|
monitor_protocol_emitter(mon, data);
|
|
|
|
} else {
|
|
|
|
/* User Protocol */
|
|
|
|
if (data)
|
|
|
|
cmd->user_print(mon, data);
|
|
|
|
}
|
2009-11-27 08:58:53 +08:00
|
|
|
|
|
|
|
qobject_decref(data);
|
|
|
|
}
|
|
|
|
|
2009-11-27 08:58:55 +08:00
|
|
|
static void handle_user_command(Monitor *mon, const char *cmdline)
|
2009-08-29 02:27:22 +08:00
|
|
|
{
|
|
|
|
QDict *qdict;
|
2009-10-02 05:12:16 +08:00
|
|
|
const mon_cmd_t *cmd;
|
2009-08-29 02:27:22 +08:00
|
|
|
|
|
|
|
qdict = qdict_new();
|
|
|
|
|
2009-08-29 02:27:24 +08:00
|
|
|
cmd = monitor_parse_command(mon, cmdline, qdict);
|
2009-10-08 00:41:54 +08:00
|
|
|
if (!cmd)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
qemu_errors_to_mon(mon);
|
|
|
|
|
2010-01-26 02:18:44 +08:00
|
|
|
if (monitor_handler_is_async(cmd)) {
|
|
|
|
user_async_cmd_handler(mon, cmd, qdict);
|
|
|
|
} else if (monitor_handler_ported(cmd)) {
|
2009-11-27 08:58:53 +08:00
|
|
|
monitor_call_handler(mon, cmd, qdict);
|
2009-10-08 00:41:54 +08:00
|
|
|
} else {
|
2009-10-08 00:41:52 +08:00
|
|
|
cmd->mhandler.cmd(mon, qdict);
|
2009-08-29 02:27:22 +08:00
|
|
|
}
|
|
|
|
|
2009-11-19 09:05:31 +08:00
|
|
|
if (monitor_has_error(mon))
|
|
|
|
monitor_print_error(mon);
|
|
|
|
|
|
|
|
qemu_errors_to_previous();
|
2009-10-08 00:41:54 +08:00
|
|
|
|
|
|
|
out:
|
2009-08-29 02:27:10 +08:00
|
|
|
QDECREF(qdict);
|
2004-03-15 05:38:27 +08:00
|
|
|
}
|
|
|
|
|
2004-07-15 01:21:37 +08: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 07:01:42 +08:00
|
|
|
readline_add_completion(cur_mon->rs, cmd);
|
2004-07-15 01:21:37 +08: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 05:08:06 +08:00
|
|
|
p = strrchr(input, '/');
|
2004-07-15 01:21:37 +08:00
|
|
|
if (!p) {
|
|
|
|
input_path_len = 0;
|
|
|
|
pstrcpy(file_prefix, sizeof(file_prefix), input);
|
2008-08-22 01:58:08 +08:00
|
|
|
pstrcpy(path, sizeof(path), ".");
|
2004-07-15 01:21:37 +08: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 07:01:23 +08:00
|
|
|
monitor_printf(cur_mon, "input='%s' path='%s' prefix='%s'\n",
|
|
|
|
input, path, file_prefix);
|
2004-07-15 01:21:37 +08:00
|
|
|
#endif
|
|
|
|
ffs = opendir(path);
|
|
|
|
if (!ffs)
|
|
|
|
return;
|
|
|
|
for(;;) {
|
|
|
|
struct stat sb;
|
|
|
|
d = readdir(ffs);
|
|
|
|
if (!d)
|
|
|
|
break;
|
|
|
|
if (strstart(d->d_name, file_prefix, NULL)) {
|
|
|
|
memcpy(file, input, input_path_len);
|
2008-08-22 01:58:08 +08:00
|
|
|
if (input_path_len < sizeof(file))
|
|
|
|
pstrcpy(file + input_path_len, sizeof(file) - input_path_len,
|
|
|
|
d->d_name);
|
2004-07-15 01:21:37 +08:00
|
|
|
/* stat the file to find out if it's a directory.
|
|
|
|
* In that case add a slash to speed up typing long paths
|
|
|
|
*/
|
|
|
|
stat(file, &sb);
|
|
|
|
if(S_ISDIR(sb.st_mode))
|
2008-08-22 01:58:08 +08:00
|
|
|
pstrcat(file, sizeof(file), "/");
|
2009-03-06 07:01:42 +08:00
|
|
|
readline_add_completion(cur_mon->rs, file);
|
2004-07-15 01:21:37 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
closedir(ffs);
|
|
|
|
}
|
|
|
|
|
2009-03-06 07:00:43 +08:00
|
|
|
static void block_completion_it(void *opaque, BlockDriverState *bs)
|
2004-07-15 01:21:37 +08:00
|
|
|
{
|
2009-03-06 07:00:43 +08:00
|
|
|
const char *name = bdrv_get_device_name(bs);
|
2004-07-15 01:21:37 +08:00
|
|
|
const char *input = opaque;
|
|
|
|
|
|
|
|
if (input[0] == '\0' ||
|
|
|
|
!strncmp(name, (char *)input, strlen(input))) {
|
2009-03-06 07:01:42 +08:00
|
|
|
readline_add_completion(cur_mon->rs, name);
|
2004-07-15 01:21:37 +08: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 21:53:32 +08:00
|
|
|
while (qemu_isspace(*p))
|
2004-07-15 01:21:37 +08:00
|
|
|
p++;
|
|
|
|
if (*p == '\0')
|
|
|
|
break;
|
|
|
|
if (nb_args >= MAX_ARGS)
|
|
|
|
break;
|
|
|
|
ret = get_str(buf, sizeof(buf), &p);
|
|
|
|
args[nb_args] = qemu_strdup(buf);
|
|
|
|
nb_args++;
|
|
|
|
if (ret < 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
*pnb_args = nb_args;
|
|
|
|
}
|
|
|
|
|
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-29 02:27:09 +08:00
|
|
|
static const char *next_arg_type(const char *typestr)
|
|
|
|
{
|
|
|
|
const char *p = strchr(typestr, ':');
|
|
|
|
return (p != NULL ? ++p : typestr);
|
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:37 +08:00
|
|
|
static void monitor_find_completion(const char *cmdline)
|
2004-07-15 01:21:37 +08:00
|
|
|
{
|
|
|
|
const char *cmdname;
|
|
|
|
char *args[MAX_ARGS];
|
|
|
|
int nb_args, i, len;
|
|
|
|
const char *ptype, *str;
|
2009-10-02 05:12:16 +08:00
|
|
|
const mon_cmd_t *cmd;
|
2006-05-08 02:03:31 +08:00
|
|
|
const KeyDef *key;
|
2004-07-15 01:21:37 +08:00
|
|
|
|
|
|
|
parse_cmdline(cmdline, &nb_args, args);
|
|
|
|
#ifdef DEBUG_COMPLETION
|
|
|
|
for(i = 0; i < nb_args; i++) {
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(cur_mon, "arg%d = '%s'\n", i, (char *)args[i]);
|
2004-07-15 01:21:37 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* if the line ends with a space, it means we want to complete the
|
|
|
|
next arg */
|
|
|
|
len = strlen(cmdline);
|
2008-11-16 21:53:32 +08:00
|
|
|
if (len > 0 && qemu_isspace(cmdline[len - 1])) {
|
2004-07-15 01:21:37 +08:00
|
|
|
if (nb_args >= MAX_ARGS)
|
|
|
|
return;
|
|
|
|
args[nb_args++] = qemu_strdup("");
|
|
|
|
}
|
|
|
|
if (nb_args <= 1) {
|
|
|
|
/* command completion */
|
|
|
|
if (nb_args == 0)
|
|
|
|
cmdname = "";
|
|
|
|
else
|
|
|
|
cmdname = args[0];
|
2009-03-06 07:01:42 +08:00
|
|
|
readline_set_completion_index(cur_mon->rs, strlen(cmdname));
|
2009-03-06 07:01:23 +08:00
|
|
|
for(cmd = mon_cmds; cmd->name != NULL; cmd++) {
|
2004-07-15 01:21:37 +08:00
|
|
|
cmd_completion(cmdname, cmd->name);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* find the command */
|
2009-03-06 07:01:23 +08:00
|
|
|
for(cmd = mon_cmds; cmd->name != NULL; cmd++) {
|
2004-07-15 01:21:37 +08:00
|
|
|
if (compare_cmd(args[0], cmd->name))
|
|
|
|
goto found;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
found:
|
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-29 02:27:09 +08:00
|
|
|
ptype = next_arg_type(cmd->args_type);
|
2004-07-15 01:21:37 +08: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-29 02:27:09 +08:00
|
|
|
ptype = next_arg_type(ptype);
|
2004-07-15 01:21:37 +08: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-29 02:27:09 +08:00
|
|
|
ptype = next_arg_type(ptype);
|
2004-07-15 01:21:37 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
str = args[nb_args - 1];
|
2009-08-24 04:10:28 +08:00
|
|
|
if (*ptype == '-' && ptype[1] != '\0') {
|
|
|
|
ptype += 2;
|
|
|
|
}
|
2004-07-15 01:21:37 +08:00
|
|
|
switch(*ptype) {
|
|
|
|
case 'F':
|
|
|
|
/* file completion */
|
2009-03-06 07:01:42 +08:00
|
|
|
readline_set_completion_index(cur_mon->rs, strlen(str));
|
2004-07-15 01:21:37 +08:00
|
|
|
file_completion(str);
|
|
|
|
break;
|
|
|
|
case 'B':
|
|
|
|
/* block device name completion */
|
2009-03-06 07:01:42 +08:00
|
|
|
readline_set_completion_index(cur_mon->rs, strlen(str));
|
2004-07-15 01:21:37 +08:00
|
|
|
bdrv_iterate(block_completion_it, (void *)str);
|
|
|
|
break;
|
2004-10-10 02:08:01 +08:00
|
|
|
case 's':
|
|
|
|
/* XXX: more generic ? */
|
|
|
|
if (!strcmp(cmd->name, "info")) {
|
2009-03-06 07:01:42 +08:00
|
|
|
readline_set_completion_index(cur_mon->rs, strlen(str));
|
2004-10-10 02:08:01 +08:00
|
|
|
for(cmd = info_cmds; cmd->name != NULL; cmd++) {
|
|
|
|
cmd_completion(str, cmd->name);
|
|
|
|
}
|
2006-05-08 02:03:31 +08:00
|
|
|
} else if (!strcmp(cmd->name, "sendkey")) {
|
2009-03-09 01:42:02 +08:00
|
|
|
char *sep = strrchr(str, '-');
|
|
|
|
if (sep)
|
|
|
|
str = sep + 1;
|
2009-03-06 07:01:42 +08:00
|
|
|
readline_set_completion_index(cur_mon->rs, strlen(str));
|
2006-05-08 02:03:31 +08:00
|
|
|
for(key = key_defs; key->name != NULL; key++) {
|
|
|
|
cmd_completion(str, key->name);
|
|
|
|
}
|
2009-06-25 14:22:02 +08: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-10 02:08:01 +08:00
|
|
|
}
|
|
|
|
break;
|
2004-07-15 01:21:37 +08:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for(i = 0; i < nb_args; i++)
|
|
|
|
qemu_free(args[i]);
|
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:42 +08:00
|
|
|
static int monitor_can_read(void *opaque)
|
2004-03-15 05:38:27 +08:00
|
|
|
{
|
2009-03-06 07:01:42 +08:00
|
|
|
Monitor *mon = opaque;
|
|
|
|
|
2009-12-04 21:05:29 +08:00
|
|
|
return (mon->suspend_cnt == 0) ? 1 : 0;
|
2004-03-15 05:38:27 +08:00
|
|
|
}
|
|
|
|
|
2009-11-27 08:59:01 +08:00
|
|
|
typedef struct CmdArgs {
|
|
|
|
QString *name;
|
|
|
|
int type;
|
|
|
|
int flag;
|
|
|
|
int optional;
|
|
|
|
} CmdArgs;
|
|
|
|
|
|
|
|
static int check_opt(const CmdArgs *cmd_args, const char *name, QDict *args)
|
|
|
|
{
|
|
|
|
if (!cmd_args->optional) {
|
|
|
|
qemu_error_new(QERR_MISSING_PARAMETER, name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmd_args->type == '-') {
|
|
|
|
/* handlers expect a value, they need to be changed */
|
|
|
|
qdict_put(args, name, qint_from_int(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int check_arg(const CmdArgs *cmd_args, QDict *args)
|
|
|
|
{
|
|
|
|
QObject *value;
|
|
|
|
const char *name;
|
|
|
|
|
|
|
|
name = qstring_get_str(cmd_args->name);
|
|
|
|
|
|
|
|
if (!args) {
|
|
|
|
return check_opt(cmd_args, name, args);
|
|
|
|
}
|
|
|
|
|
|
|
|
value = qdict_get(args, name);
|
|
|
|
if (!value) {
|
|
|
|
return check_opt(cmd_args, name, args);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (cmd_args->type) {
|
|
|
|
case 'F':
|
|
|
|
case 'B':
|
|
|
|
case 's':
|
|
|
|
if (qobject_type(value) != QTYPE_QSTRING) {
|
|
|
|
qemu_error_new(QERR_INVALID_PARAMETER_TYPE, name, "string");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '/': {
|
|
|
|
int i;
|
|
|
|
const char *keys[] = { "count", "format", "size", NULL };
|
|
|
|
|
|
|
|
for (i = 0; keys[i]; i++) {
|
|
|
|
QObject *obj = qdict_get(args, keys[i]);
|
|
|
|
if (!obj) {
|
|
|
|
qemu_error_new(QERR_MISSING_PARAMETER, name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (qobject_type(obj) != QTYPE_QINT) {
|
|
|
|
qemu_error_new(QERR_INVALID_PARAMETER_TYPE, name, "int");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'i':
|
|
|
|
case 'l':
|
2009-12-18 23:25:04 +08:00
|
|
|
case 'M':
|
2009-11-27 08:59:01 +08:00
|
|
|
if (qobject_type(value) != QTYPE_QINT) {
|
|
|
|
qemu_error_new(QERR_INVALID_PARAMETER_TYPE, name, "int");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '-':
|
|
|
|
if (qobject_type(value) != QTYPE_QINT &&
|
|
|
|
qobject_type(value) != QTYPE_QBOOL) {
|
|
|
|
qemu_error_new(QERR_INVALID_PARAMETER_TYPE, name, "bool");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (qobject_type(value) == QTYPE_QBOOL) {
|
|
|
|
/* handlers expect a QInt, they need to be changed */
|
|
|
|
qdict_put(args, name,
|
|
|
|
qint_from_int(qbool_get_int(qobject_to_qbool(value))));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* impossible */
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void cmd_args_init(CmdArgs *cmd_args)
|
|
|
|
{
|
|
|
|
cmd_args->name = qstring_new();
|
|
|
|
cmd_args->type = cmd_args->flag = cmd_args->optional = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is not trivial, we have to parse Monitor command's argument
|
|
|
|
* type syntax to be able to check the arguments provided by clients.
|
|
|
|
*
|
|
|
|
* In the near future we will be using an array for that and will be
|
|
|
|
* able to drop all this parsing...
|
|
|
|
*/
|
|
|
|
static int monitor_check_qmp_args(const mon_cmd_t *cmd, QDict *args)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
const char *p;
|
|
|
|
CmdArgs cmd_args;
|
|
|
|
|
2009-12-05 04:52:02 +08:00
|
|
|
if (cmd->args_type == NULL) {
|
2009-11-27 08:59:01 +08:00
|
|
|
return (qdict_size(args) == 0 ? 0 : -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
cmd_args_init(&cmd_args);
|
|
|
|
|
|
|
|
for (p = cmd->args_type;; p++) {
|
|
|
|
if (*p == ':') {
|
|
|
|
cmd_args.type = *++p;
|
|
|
|
p++;
|
|
|
|
if (cmd_args.type == '-') {
|
|
|
|
cmd_args.flag = *p++;
|
|
|
|
cmd_args.optional = 1;
|
|
|
|
} else if (*p == '?') {
|
|
|
|
cmd_args.optional = 1;
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(*p == ',' || *p == '\0');
|
|
|
|
err = check_arg(&cmd_args, args);
|
|
|
|
|
|
|
|
QDECREF(cmd_args.name);
|
|
|
|
cmd_args_init(&cmd_args);
|
|
|
|
|
|
|
|
if (err < 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
qstring_append_chr(cmd_args.name, *p);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*p == '\0') {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QDECREF(cmd_args.name);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void handle_qmp_command(JSONMessageParser *parser, QList *tokens)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
QObject *obj;
|
|
|
|
QDict *input, *args;
|
|
|
|
const mon_cmd_t *cmd;
|
|
|
|
Monitor *mon = cur_mon;
|
2009-11-27 08:59:02 +08:00
|
|
|
const char *cmd_name, *info_item;
|
2009-11-27 08:59:01 +08:00
|
|
|
|
|
|
|
args = NULL;
|
|
|
|
qemu_errors_to_mon(mon);
|
|
|
|
|
|
|
|
obj = json_parser_parse(tokens, NULL);
|
|
|
|
if (!obj) {
|
|
|
|
// FIXME: should be triggered in json_parser_parse()
|
|
|
|
qemu_error_new(QERR_JSON_PARSING);
|
|
|
|
goto err_out;
|
|
|
|
} else if (qobject_type(obj) != QTYPE_QDICT) {
|
|
|
|
qemu_error_new(QERR_QMP_BAD_INPUT_OBJECT, "object");
|
|
|
|
qobject_decref(obj);
|
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
input = qobject_to_qdict(obj);
|
|
|
|
|
|
|
|
mon->mc->id = qdict_get(input, "id");
|
|
|
|
qobject_incref(mon->mc->id);
|
|
|
|
|
|
|
|
obj = qdict_get(input, "execute");
|
|
|
|
if (!obj) {
|
|
|
|
qemu_error_new(QERR_QMP_BAD_INPUT_OBJECT, "execute");
|
|
|
|
goto err_input;
|
|
|
|
} else if (qobject_type(obj) != QTYPE_QSTRING) {
|
|
|
|
qemu_error_new(QERR_QMP_BAD_INPUT_OBJECT, "string");
|
|
|
|
goto err_input;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd_name = qstring_get_str(qobject_to_qstring(obj));
|
2009-11-27 08:59:02 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX: We need this special case until we get info handlers
|
|
|
|
* converted into 'query-' commands
|
|
|
|
*/
|
|
|
|
if (compare_cmd(cmd_name, "info")) {
|
2009-11-27 08:59:01 +08:00
|
|
|
qemu_error_new(QERR_COMMAND_NOT_FOUND, cmd_name);
|
|
|
|
goto err_input;
|
2009-11-27 08:59:02 +08:00
|
|
|
} else if (strstart(cmd_name, "query-", &info_item)) {
|
|
|
|
cmd = monitor_find_command("info");
|
|
|
|
qdict_put_obj(input, "arguments",
|
|
|
|
qobject_from_jsonf("{ 'item': %s }", info_item));
|
|
|
|
} else {
|
|
|
|
cmd = monitor_find_command(cmd_name);
|
2009-12-18 23:24:59 +08:00
|
|
|
if (!cmd || !monitor_handler_ported(cmd)) {
|
2009-11-27 08:59:02 +08:00
|
|
|
qemu_error_new(QERR_COMMAND_NOT_FOUND, cmd_name);
|
|
|
|
goto err_input;
|
|
|
|
}
|
2009-11-27 08:59:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
obj = qdict_get(input, "arguments");
|
|
|
|
if (!obj) {
|
|
|
|
args = qdict_new();
|
|
|
|
} else {
|
|
|
|
args = qobject_to_qdict(obj);
|
|
|
|
QINCREF(args);
|
|
|
|
}
|
|
|
|
|
|
|
|
QDECREF(input);
|
|
|
|
|
|
|
|
err = monitor_check_qmp_args(cmd, args);
|
|
|
|
if (err < 0) {
|
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
|
2010-01-26 02:18:44 +08:00
|
|
|
if (monitor_handler_is_async(cmd)) {
|
|
|
|
qmp_async_cmd_handler(mon, cmd, args);
|
|
|
|
} else {
|
|
|
|
monitor_call_handler(mon, cmd, args);
|
|
|
|
}
|
2009-11-27 08:59:01 +08:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
err_input:
|
|
|
|
QDECREF(input);
|
|
|
|
err_out:
|
|
|
|
monitor_protocol_emitter(mon, NULL);
|
|
|
|
out:
|
|
|
|
QDECREF(args);
|
|
|
|
qemu_errors_to_previous();
|
|
|
|
}
|
|
|
|
|
2009-11-27 08:58:58 +08: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 08:59:01 +08:00
|
|
|
json_message_parser_feed(&cur_mon->mc->parser, (const char *) buf, size);
|
2009-11-27 08:58:58 +08:00
|
|
|
|
|
|
|
cur_mon = old_mon;
|
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:42 +08:00
|
|
|
static void monitor_read(void *opaque, const uint8_t *buf, int size)
|
2004-03-15 05:38:27 +08:00
|
|
|
{
|
2009-03-06 07:01:42 +08:00
|
|
|
Monitor *old_mon = cur_mon;
|
2004-08-02 05:52:19 +08:00
|
|
|
int i;
|
2009-03-06 07:01:23 +08:00
|
|
|
|
2009-03-06 07:01:42 +08:00
|
|
|
cur_mon = opaque;
|
|
|
|
|
2009-03-06 07:01:51 +08: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 08:58:55 +08:00
|
|
|
handle_user_command(cur_mon, (char *)buf);
|
2009-03-06 07:01:51 +08:00
|
|
|
}
|
2004-03-15 05:38:27 +08:00
|
|
|
|
2009-03-06 07:01:42 +08:00
|
|
|
cur_mon = old_mon;
|
|
|
|
}
|
2008-10-06 21:52:44 +08:00
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
static void monitor_command_cb(Monitor *mon, const char *cmdline, void *opaque)
|
2004-04-04 21:07:25 +08:00
|
|
|
{
|
2009-03-06 07:01:42 +08:00
|
|
|
monitor_suspend(mon);
|
2009-11-27 08:58:55 +08:00
|
|
|
handle_user_command(mon, cmdline);
|
2009-03-06 07:01:42 +08:00
|
|
|
monitor_resume(mon);
|
2008-10-06 21:52:44 +08:00
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:51 +08:00
|
|
|
int monitor_suspend(Monitor *mon)
|
2008-10-06 21:52:44 +08:00
|
|
|
{
|
2009-03-06 07:01:51 +08:00
|
|
|
if (!mon->rs)
|
|
|
|
return -ENOTTY;
|
2009-03-06 07:01:42 +08:00
|
|
|
mon->suspend_cnt++;
|
2009-03-06 07:01:51 +08:00
|
|
|
return 0;
|
2008-10-06 21:52:44 +08:00
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
void monitor_resume(Monitor *mon)
|
2008-10-06 21:52:44 +08:00
|
|
|
{
|
2009-03-06 07:01:51 +08:00
|
|
|
if (!mon->rs)
|
|
|
|
return;
|
2009-03-06 07:01:42 +08:00
|
|
|
if (--mon->suspend_cnt == 0)
|
|
|
|
readline_show_prompt(mon->rs);
|
2004-04-04 21:07:25 +08:00
|
|
|
}
|
|
|
|
|
2009-11-27 08:58:58 +08:00
|
|
|
/**
|
|
|
|
* monitor_control_event(): Print QMP gretting
|
|
|
|
*/
|
|
|
|
static void monitor_control_event(void *opaque, int event)
|
|
|
|
{
|
|
|
|
if (event == CHR_EVENT_OPENED) {
|
|
|
|
QObject *data;
|
|
|
|
Monitor *mon = opaque;
|
|
|
|
|
2009-11-27 08:59:01 +08:00
|
|
|
json_message_parser_init(&mon->mc->parser, handle_qmp_command);
|
|
|
|
|
2009-11-27 08:58:58 +08:00
|
|
|
data = qobject_from_jsonf("{ 'QMP': { 'capabilities': [] } }");
|
|
|
|
assert(data != NULL);
|
|
|
|
|
|
|
|
monitor_json_emitter(mon, data);
|
|
|
|
qobject_decref(data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:42 +08:00
|
|
|
static void monitor_event(void *opaque, int event)
|
2007-01-06 06:01:59 +08:00
|
|
|
{
|
2009-03-06 07:01:23 +08:00
|
|
|
Monitor *mon = opaque;
|
|
|
|
|
2009-03-06 07:01:47 +08:00
|
|
|
switch (event) {
|
|
|
|
case CHR_EVENT_MUX_IN:
|
2009-09-10 16:58:54 +08: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 07:01:47 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CHR_EVENT_MUX_OUT:
|
2009-09-10 16:58:54 +08: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 07:01:47 +08:00
|
|
|
break;
|
2007-01-06 06:01:59 +08:00
|
|
|
|
2009-10-07 21:01:16 +08:00
|
|
|
case CHR_EVENT_OPENED:
|
2009-03-06 07:01:47 +08:00
|
|
|
monitor_printf(mon, "QEMU %s monitor - type 'help' for more "
|
|
|
|
"information\n", QEMU_VERSION);
|
2009-09-10 16:58:54 +08:00
|
|
|
if (!mon->mux_out) {
|
2009-03-06 07:01:47 +08:00
|
|
|
readline_show_prompt(mon->rs);
|
2009-09-10 16:58:54 +08:00
|
|
|
}
|
|
|
|
mon->reset_seen = 1;
|
2009-03-06 07:01:47 +08:00
|
|
|
break;
|
|
|
|
}
|
2007-01-06 06:01:59 +08:00
|
|
|
}
|
|
|
|
|
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-07 04:27:37 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Local variables:
|
|
|
|
* c-indent-level: 4
|
|
|
|
* c-basic-offset: 4
|
|
|
|
* tab-width: 8
|
|
|
|
* End:
|
|
|
|
*/
|
|
|
|
|
2009-03-06 07:01:42 +08:00
|
|
|
void monitor_init(CharDriverState *chr, int flags)
|
2004-04-04 21:07:25 +08:00
|
|
|
{
|
2009-03-06 07:01:42 +08:00
|
|
|
static int is_first_init = 1;
|
2009-03-06 07:01:29 +08:00
|
|
|
Monitor *mon;
|
2007-02-19 01:04:49 +08:00
|
|
|
|
|
|
|
if (is_first_init) {
|
2008-06-09 06:45:01 +08:00
|
|
|
key_timer = qemu_new_timer(vm_clock, release_keys, NULL);
|
2007-02-19 01:04:49 +08:00
|
|
|
is_first_init = 0;
|
|
|
|
}
|
2009-03-06 07:01:29 +08:00
|
|
|
|
|
|
|
mon = qemu_mallocz(sizeof(*mon));
|
2007-02-19 01:04:49 +08:00
|
|
|
|
2009-03-06 07:01:29 +08:00
|
|
|
mon->chr = chr;
|
2009-03-06 07:01:42 +08:00
|
|
|
mon->flags = flags;
|
2009-03-06 07:01:51 +08:00
|
|
|
if (flags & MONITOR_USE_READLINE) {
|
|
|
|
mon->rs = readline_init(mon, monitor_find_completion);
|
|
|
|
monitor_read_command(mon, 0);
|
|
|
|
}
|
2009-03-06 07:01:29 +08:00
|
|
|
|
2009-11-27 08:58:58 +08:00
|
|
|
if (monitor_ctrl_mode(mon)) {
|
2009-11-27 08:59:01 +08:00
|
|
|
mon->mc = qemu_mallocz(sizeof(MonitorControl));
|
2009-11-27 08:58:58 +08:00
|
|
|
/* Control mode requires special handlers */
|
|
|
|
qemu_chr_add_handlers(chr, monitor_can_read, monitor_control_read,
|
|
|
|
monitor_control_event, mon);
|
|
|
|
} else {
|
|
|
|
qemu_chr_add_handlers(chr, monitor_can_read, monitor_read,
|
|
|
|
monitor_event, mon);
|
|
|
|
}
|
2009-03-06 07:01:29 +08:00
|
|
|
|
2009-09-12 15:36:22 +08:00
|
|
|
QLIST_INSERT_HEAD(&mon_list, mon, entry);
|
2009-03-06 07:01:42 +08:00
|
|
|
if (!cur_mon || (flags & MONITOR_IS_DEFAULT))
|
2009-03-06 07:01:29 +08:00
|
|
|
cur_mon = mon;
|
2004-04-04 21:07:25 +08:00
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
static void bdrv_password_cb(Monitor *mon, const char *password, void *opaque)
|
2004-07-15 01:21:37 +08:00
|
|
|
{
|
2009-03-06 07:01:15 +08:00
|
|
|
BlockDriverState *bs = opaque;
|
|
|
|
int ret = 0;
|
2004-07-15 01:21:37 +08:00
|
|
|
|
2009-03-06 07:01:15 +08:00
|
|
|
if (bdrv_set_key(bs, password) != 0) {
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "invalid password\n");
|
2009-03-06 07:01:15 +08:00
|
|
|
ret = -EPERM;
|
2004-03-15 05:38:27 +08:00
|
|
|
}
|
2009-03-06 07:01:42 +08:00
|
|
|
if (mon->password_completion_cb)
|
|
|
|
mon->password_completion_cb(mon->password_opaque, ret);
|
2009-03-06 07:01:15 +08:00
|
|
|
|
2009-03-06 07:01:42 +08:00
|
|
|
monitor_read_command(mon, 1);
|
2004-03-15 05:38:27 +08:00
|
|
|
}
|
2009-03-06 07:01:01 +08:00
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
void monitor_read_bdrv_key_start(Monitor *mon, BlockDriverState *bs,
|
2009-03-06 07:01:15 +08:00
|
|
|
BlockDriverCompletionFunc *completion_cb,
|
|
|
|
void *opaque)
|
2009-03-06 07:01:01 +08:00
|
|
|
{
|
2009-03-06 07:01:51 +08:00
|
|
|
int err;
|
|
|
|
|
2009-03-06 07:01:15 +08:00
|
|
|
if (!bdrv_key_required(bs)) {
|
|
|
|
if (completion_cb)
|
|
|
|
completion_cb(opaque, 0);
|
|
|
|
return;
|
|
|
|
}
|
2009-03-06 07:01:01 +08:00
|
|
|
|
2009-12-08 04:37:00 +08:00
|
|
|
if (monitor_ctrl_mode(mon)) {
|
|
|
|
qemu_error_new(QERR_DEVICE_ENCRYPTED, bdrv_get_device_name(bs));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "%s (%s) is encrypted.\n", bdrv_get_device_name(bs),
|
|
|
|
bdrv_get_encrypted_filename(bs));
|
2009-03-06 07:01:15 +08:00
|
|
|
|
2009-03-06 07:01:42 +08:00
|
|
|
mon->password_completion_cb = completion_cb;
|
|
|
|
mon->password_opaque = opaque;
|
2009-03-06 07:01:15 +08:00
|
|
|
|
2009-03-06 07:01:51 +08:00
|
|
|
err = monitor_read_password(mon, bdrv_password_cb, bs);
|
|
|
|
|
|
|
|
if (err && completion_cb)
|
|
|
|
completion_cb(opaque, err);
|
2009-03-06 07:01:01 +08:00
|
|
|
}
|
2009-08-14 16:36:06 +08:00
|
|
|
|
|
|
|
typedef struct QemuErrorSink QemuErrorSink;
|
|
|
|
struct QemuErrorSink {
|
|
|
|
enum {
|
|
|
|
ERR_SINK_FILE,
|
|
|
|
ERR_SINK_MONITOR,
|
|
|
|
} dest;
|
|
|
|
union {
|
|
|
|
FILE *fp;
|
|
|
|
Monitor *mon;
|
|
|
|
};
|
|
|
|
QemuErrorSink *previous;
|
|
|
|
};
|
|
|
|
|
2009-08-31 23:14:40 +08:00
|
|
|
static QemuErrorSink *qemu_error_sink;
|
2009-08-14 16:36:06 +08:00
|
|
|
|
|
|
|
void qemu_errors_to_file(FILE *fp)
|
|
|
|
{
|
|
|
|
QemuErrorSink *sink;
|
|
|
|
|
|
|
|
sink = qemu_mallocz(sizeof(*sink));
|
|
|
|
sink->dest = ERR_SINK_FILE;
|
|
|
|
sink->fp = fp;
|
|
|
|
sink->previous = qemu_error_sink;
|
|
|
|
qemu_error_sink = sink;
|
|
|
|
}
|
|
|
|
|
|
|
|
void qemu_errors_to_mon(Monitor *mon)
|
|
|
|
{
|
|
|
|
QemuErrorSink *sink;
|
|
|
|
|
|
|
|
sink = qemu_mallocz(sizeof(*sink));
|
|
|
|
sink->dest = ERR_SINK_MONITOR;
|
|
|
|
sink->mon = mon;
|
|
|
|
sink->previous = qemu_error_sink;
|
|
|
|
qemu_error_sink = sink;
|
|
|
|
}
|
|
|
|
|
|
|
|
void qemu_errors_to_previous(void)
|
|
|
|
{
|
|
|
|
QemuErrorSink *sink;
|
|
|
|
|
|
|
|
assert(qemu_error_sink != NULL);
|
|
|
|
sink = qemu_error_sink;
|
|
|
|
qemu_error_sink = sink->previous;
|
|
|
|
qemu_free(sink);
|
|
|
|
}
|
|
|
|
|
|
|
|
void qemu_error(const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
assert(qemu_error_sink != NULL);
|
|
|
|
switch (qemu_error_sink->dest) {
|
|
|
|
case ERR_SINK_FILE:
|
|
|
|
va_start(args, fmt);
|
|
|
|
vfprintf(qemu_error_sink->fp, fmt, args);
|
|
|
|
va_end(args);
|
|
|
|
break;
|
|
|
|
case ERR_SINK_MONITOR:
|
|
|
|
va_start(args, fmt);
|
|
|
|
monitor_vprintf(qemu_error_sink->mon, fmt, args);
|
|
|
|
va_end(args);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2009-11-19 09:05:31 +08:00
|
|
|
|
|
|
|
void qemu_error_internal(const char *file, int linenr, const char *func,
|
|
|
|
const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list va;
|
|
|
|
QError *qerror;
|
|
|
|
|
|
|
|
assert(qemu_error_sink != NULL);
|
|
|
|
|
|
|
|
va_start(va, fmt);
|
|
|
|
qerror = qerror_from_info(file, linenr, func, fmt, &va);
|
|
|
|
va_end(va);
|
|
|
|
|
|
|
|
switch (qemu_error_sink->dest) {
|
|
|
|
case ERR_SINK_FILE:
|
|
|
|
qerror_print(qerror);
|
|
|
|
QDECREF(qerror);
|
|
|
|
break;
|
|
|
|
case ERR_SINK_MONITOR:
|
|
|
|
assert(qemu_error_sink->mon->error == NULL);
|
|
|
|
qemu_error_sink->mon->error = qerror;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|