2004-03-15 00:38:27 +03:00
|
|
|
/*
|
|
|
|
* QEMU monitor
|
2007-09-17 01:08:06 +04:00
|
|
|
*
|
2004-03-15 00:38:27 +03:00
|
|
|
* Copyright (c) 2003-2004 Fabrice Bellard
|
2007-09-17 01:08:06 +04:00
|
|
|
*
|
2004-03-15 00:38:27 +03:00
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
|
|
* in the Software without restriction, including without limitation the rights
|
|
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
* THE SOFTWARE.
|
|
|
|
*/
|
2009-03-07 18:32:56 +03:00
|
|
|
#include <dirent.h>
|
2007-11-17 20:14:51 +03:00
|
|
|
#include "hw/hw.h"
|
|
|
|
#include "hw/usb.h"
|
|
|
|
#include "hw/pcmcia.h"
|
|
|
|
#include "hw/pc.h"
|
|
|
|
#include "hw/pci.h"
|
|
|
|
#include "gdbstub.h"
|
|
|
|
#include "net.h"
|
|
|
|
#include "qemu-char.h"
|
|
|
|
#include "sysemu.h"
|
2009-03-06 02:01:23 +03:00
|
|
|
#include "monitor.h"
|
|
|
|
#include "readline.h"
|
2007-11-17 20:14:51 +03:00
|
|
|
#include "console.h"
|
|
|
|
#include "block.h"
|
|
|
|
#include "audio/audio.h"
|
2004-04-04 16:57:25 +04:00
|
|
|
#include "disas.h"
|
2008-12-04 23:19:35 +03:00
|
|
|
#include "balloon.h"
|
2008-06-09 02:45:01 +04:00
|
|
|
#include "qemu-timer.h"
|
2008-10-13 07:12:02 +04:00
|
|
|
#include "migration.h"
|
2008-11-05 19:04:33 +03: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-06 23:27:37 +03:00
|
|
|
#include "acl.h"
|
2007-12-03 20:05:38 +03:00
|
|
|
|
2004-03-15 00:38:27 +03:00
|
|
|
//#define DEBUG
|
2004-07-14 21:21:37 +04:00
|
|
|
//#define DEBUG_COMPLETION
|
2004-03-15 00:38:27 +03:00
|
|
|
|
2004-04-04 16:57:25 +04:00
|
|
|
/*
|
|
|
|
* Supported types:
|
2007-09-17 01:08:06 +04:00
|
|
|
*
|
2004-04-04 16:57:25 +04:00
|
|
|
* 'F' filename
|
2004-07-14 21:21:37 +04:00
|
|
|
* 'B' block device name
|
2004-04-04 16:57:25 +04:00
|
|
|
* 's' string (accept optional quote)
|
2005-02-11 01:00:52 +03:00
|
|
|
* 'i' 32 bit integer
|
|
|
|
* 'l' target long (32 or 64 bit)
|
2004-04-04 16:57:25 +04:00
|
|
|
* '/' optional gdb-like print format (like "/10x")
|
|
|
|
*
|
|
|
|
* '?' optional type (for 'F', 's' and 'i')
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
typedef struct mon_cmd_t {
|
2004-03-15 00:38:27 +03:00
|
|
|
const char *name;
|
2004-04-04 16:57:25 +04:00
|
|
|
const char *args_type;
|
2008-08-18 00:21:51 +04:00
|
|
|
void *handler;
|
2004-03-15 00:38:27 +03:00
|
|
|
const char *params;
|
|
|
|
const char *help;
|
2009-03-06 02:01:23 +03:00
|
|
|
} mon_cmd_t;
|
2004-03-15 00:38:27 +03:00
|
|
|
|
2009-03-06 02:01:29 +03:00
|
|
|
struct Monitor {
|
|
|
|
CharDriverState *chr;
|
2009-03-06 02:01:42 +03:00
|
|
|
int flags;
|
|
|
|
int suspend_cnt;
|
|
|
|
uint8_t outbuf[1024];
|
|
|
|
int outbuf_index;
|
|
|
|
ReadLineState *rs;
|
|
|
|
CPUState *mon_cpu;
|
|
|
|
BlockDriverCompletionFunc *password_completion_cb;
|
|
|
|
void *password_opaque;
|
2009-03-06 02:01:29 +03:00
|
|
|
LIST_ENTRY(Monitor) entry;
|
|
|
|
};
|
|
|
|
|
|
|
|
static LIST_HEAD(mon_list, Monitor) mon_list;
|
2004-08-02 01:52:19 +04:00
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static const mon_cmd_t mon_cmds[];
|
|
|
|
static const mon_cmd_t info_cmds[];
|
2004-03-15 00:38:27 +03:00
|
|
|
|
2009-03-06 02:01:29 +03:00
|
|
|
Monitor *cur_mon = NULL;
|
2009-03-06 02:01:23 +03:00
|
|
|
|
2009-03-06 02:01:42 +03:00
|
|
|
static void monitor_command_cb(Monitor *mon, const char *cmdline,
|
|
|
|
void *opaque);
|
2008-08-19 18:44:22 +04:00
|
|
|
|
2009-03-06 02:01:42 +03:00
|
|
|
static void monitor_read_command(Monitor *mon, int show_prompt)
|
|
|
|
{
|
|
|
|
readline_start(mon->rs, "(qemu) ", 0, monitor_command_cb, NULL);
|
|
|
|
if (show_prompt)
|
|
|
|
readline_show_prompt(mon->rs);
|
|
|
|
}
|
2005-11-22 02:25:50 +03:00
|
|
|
|
2009-03-06 02:01:51 +03:00
|
|
|
static int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func,
|
|
|
|
void *opaque)
|
2009-03-06 02:01:15 +03:00
|
|
|
{
|
2009-03-06 02:01:51 +03:00
|
|
|
if (mon->rs) {
|
|
|
|
readline_start(mon->rs, "Password: ", 1, readline_func, opaque);
|
|
|
|
/* prompt is printed on return from the command handler */
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
monitor_printf(mon, "terminal does not support password prompting\n");
|
|
|
|
return -ENOTTY;
|
|
|
|
}
|
2009-03-06 02:01:15 +03:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
void monitor_flush(Monitor *mon)
|
2004-08-02 01:52:19 +04:00
|
|
|
{
|
2009-03-06 02:01:42 +03:00
|
|
|
if (mon && mon->outbuf_index != 0 && mon->chr->focus == 0) {
|
|
|
|
qemu_chr_write(mon->chr, mon->outbuf, mon->outbuf_index);
|
|
|
|
mon->outbuf_index = 0;
|
2004-08-02 01:52:19 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* flush at every end of line or if the buffer is full */
|
2009-03-06 02:01:23 +03:00
|
|
|
static void monitor_puts(Monitor *mon, const char *str)
|
2004-08-02 01:52:19 +04:00
|
|
|
{
|
2007-12-16 06:02:09 +03:00
|
|
|
char c;
|
2009-03-06 02:01:42 +03:00
|
|
|
|
|
|
|
if (!mon)
|
|
|
|
return;
|
|
|
|
|
2004-08-02 01:52:19 +04:00
|
|
|
for(;;) {
|
|
|
|
c = *str++;
|
|
|
|
if (c == '\0')
|
|
|
|
break;
|
2006-07-15 00:26:42 +04:00
|
|
|
if (c == '\n')
|
2009-03-06 02:01:42 +03:00
|
|
|
mon->outbuf[mon->outbuf_index++] = '\r';
|
|
|
|
mon->outbuf[mon->outbuf_index++] = c;
|
|
|
|
if (mon->outbuf_index >= (sizeof(mon->outbuf) - 1)
|
|
|
|
|| c == '\n')
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_flush(mon);
|
2004-08-02 01:52:19 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
|
2004-03-15 00:38:27 +03:00
|
|
|
{
|
2004-07-14 21:21:37 +04:00
|
|
|
char buf[4096];
|
|
|
|
vsnprintf(buf, sizeof(buf), fmt, ap);
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_puts(mon, buf);
|
2004-03-15 00:38:27 +03:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
void monitor_printf(Monitor *mon, const char *fmt, ...)
|
2004-03-15 00:38:27 +03:00
|
|
|
{
|
2004-08-02 01:52:19 +04:00
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_vprintf(mon, fmt, ap);
|
2004-08-02 01:52:19 +04:00
|
|
|
va_end(ap);
|
2004-03-15 00:38:27 +03:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
void monitor_print_filename(Monitor *mon, const char *filename)
|
2006-12-22 17:11:32 +03:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; filename[i]; i++) {
|
2009-03-06 23:27:40 +03:00
|
|
|
switch (filename[i]) {
|
|
|
|
case ' ':
|
|
|
|
case '"':
|
|
|
|
case '\\':
|
|
|
|
monitor_printf(mon, "\\%c", filename[i]);
|
|
|
|
break;
|
|
|
|
case '\t':
|
|
|
|
monitor_printf(mon, "\\t");
|
|
|
|
break;
|
|
|
|
case '\r':
|
|
|
|
monitor_printf(mon, "\\r");
|
|
|
|
break;
|
|
|
|
case '\n':
|
|
|
|
monitor_printf(mon, "\\n");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
monitor_printf(mon, "%c", filename[i]);
|
|
|
|
break;
|
|
|
|
}
|
2006-12-22 17:11:32 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-10-09 22:08:01 +04:00
|
|
|
static int monitor_fprintf(FILE *stream, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_vprintf((Monitor *)stream, fmt, ap);
|
2004-10-09 22:08:01 +04:00
|
|
|
va_end(ap);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-03-15 00:38:27 +03:00
|
|
|
static int compare_cmd(const char *name, const char *list)
|
|
|
|
{
|
|
|
|
const char *p, *pstart;
|
|
|
|
int len;
|
|
|
|
len = strlen(name);
|
|
|
|
p = list;
|
|
|
|
for(;;) {
|
|
|
|
pstart = p;
|
|
|
|
p = strchr(p, '|');
|
|
|
|
if (!p)
|
|
|
|
p = pstart + strlen(pstart);
|
|
|
|
if ((p - pstart) == len && !memcmp(pstart, name, len))
|
|
|
|
return 1;
|
|
|
|
if (*p == '\0')
|
|
|
|
break;
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void help_cmd_dump(Monitor *mon, const mon_cmd_t *cmds,
|
|
|
|
const char *prefix, const char *name)
|
2004-03-15 00:38:27 +03:00
|
|
|
{
|
2009-03-06 02:01:23 +03:00
|
|
|
const mon_cmd_t *cmd;
|
2004-03-15 00:38:27 +03:00
|
|
|
|
|
|
|
for(cmd = cmds; cmd->name != NULL; cmd++) {
|
|
|
|
if (!name || !strcmp(name, cmd->name))
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%s%s %s -- %s\n", prefix, cmd->name,
|
|
|
|
cmd->params, cmd->help);
|
2004-03-15 00:38:27 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void help_cmd(Monitor *mon, const char *name)
|
2004-03-15 00:38:27 +03:00
|
|
|
{
|
|
|
|
if (name && !strcmp(name, "info")) {
|
2009-03-06 02:01:23 +03:00
|
|
|
help_cmd_dump(mon, info_cmds, "info ", NULL);
|
2004-03-15 00:38:27 +03:00
|
|
|
} else {
|
2009-03-06 02:01:23 +03:00
|
|
|
help_cmd_dump(mon, mon_cmds, "", name);
|
2004-03-21 20:06:25 +03:00
|
|
|
if (name && !strcmp(name, "log")) {
|
2008-10-02 22:32:44 +04:00
|
|
|
const CPULogItem *item;
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "Log items (comma separated):\n");
|
|
|
|
monitor_printf(mon, "%-10s %s\n", "none", "remove all logs");
|
2004-03-21 20:06:25 +03:00
|
|
|
for(item = cpu_log_items; item->mask != 0; item++) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%-10s %s\n", item->name, item->help);
|
2004-03-21 20:06:25 +03:00
|
|
|
}
|
|
|
|
}
|
2004-03-15 00:38:27 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_commit(Monitor *mon, const char *device)
|
2004-03-15 00:38:27 +03:00
|
|
|
{
|
2006-08-01 19:52:40 +04:00
|
|
|
int i, all_devices;
|
2007-05-24 22:53:22 +04:00
|
|
|
|
2006-08-01 19:52:40 +04:00
|
|
|
all_devices = !strcmp(device, "all");
|
2007-12-02 07:51:10 +03:00
|
|
|
for (i = 0; i < nb_drives; i++) {
|
2007-09-17 01:08:06 +04:00
|
|
|
if (all_devices ||
|
2007-12-02 07:51:10 +03:00
|
|
|
!strcmp(bdrv_get_device_name(drives_table[i].bdrv), device))
|
|
|
|
bdrv_commit(drives_table[i].bdrv);
|
2004-03-15 00:38:27 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_info(Monitor *mon, const char *item)
|
2004-03-15 00:38:27 +03:00
|
|
|
{
|
2009-03-06 02:01:23 +03:00
|
|
|
const mon_cmd_t *cmd;
|
|
|
|
void (*handler)(Monitor *);
|
2004-03-15 00:38:27 +03:00
|
|
|
|
2004-04-04 16:57:25 +04:00
|
|
|
if (!item)
|
2004-03-15 00:38:27 +03:00
|
|
|
goto help;
|
|
|
|
for(cmd = info_cmds; cmd->name != NULL; cmd++) {
|
2007-09-17 01:08:06 +04:00
|
|
|
if (compare_cmd(item, cmd->name))
|
2004-03-15 00:38:27 +03:00
|
|
|
goto found;
|
|
|
|
}
|
|
|
|
help:
|
2009-03-06 02:01:23 +03:00
|
|
|
help_cmd(mon, "info");
|
2004-03-15 00:38:27 +03:00
|
|
|
return;
|
|
|
|
found:
|
2008-08-18 00:21:51 +04:00
|
|
|
handler = cmd->handler;
|
2009-03-06 02:01:23 +03:00
|
|
|
handler(mon);
|
2004-03-15 00:38:27 +03:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_info_version(Monitor *mon)
|
2004-10-10 19:15:51 +04:00
|
|
|
{
|
2009-04-08 03:17:49 +04:00
|
|
|
monitor_printf(mon, "%s\n", QEMU_VERSION QEMU_PKGVERSION);
|
2004-10-10 19:15:51 +04:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_info_name(Monitor *mon)
|
2007-03-19 18:17:08 +03:00
|
|
|
{
|
|
|
|
if (qemu_name)
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%s\n", qemu_name);
|
2007-03-19 18:17:08 +03:00
|
|
|
}
|
|
|
|
|
2008-12-19 01:42:34 +03:00
|
|
|
#if defined(TARGET_I386)
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_info_hpet(Monitor *mon)
|
2008-12-18 02:28:44 +03:00
|
|
|
{
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "HPET is %s by QEMU\n",
|
|
|
|
(no_hpet) ? "disabled" : "enabled");
|
2008-12-18 02:28:44 +03:00
|
|
|
}
|
2008-12-19 01:42:34 +03:00
|
|
|
#endif
|
2008-12-18 02:28:44 +03:00
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_info_uuid(Monitor *mon)
|
2007-12-02 08:18:19 +03:00
|
|
|
{
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, UUID_FMT "\n", qemu_uuid[0], qemu_uuid[1],
|
|
|
|
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]);
|
2007-12-02 08:18:19 +03:00
|
|
|
}
|
|
|
|
|
2005-11-22 02:25:50 +03:00
|
|
|
/* get the current CPU defined by the user */
|
2007-11-18 04:44:38 +03:00
|
|
|
static int mon_set_cpu(int cpu_index)
|
2005-11-22 02:25:50 +03:00
|
|
|
{
|
|
|
|
CPUState *env;
|
|
|
|
|
|
|
|
for(env = first_cpu; env != NULL; env = env->next_cpu) {
|
|
|
|
if (env->cpu_index == cpu_index) {
|
2009-03-06 02:01:42 +03:00
|
|
|
cur_mon->mon_cpu = env;
|
2005-11-22 02:25:50 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-11-18 04:44:38 +03:00
|
|
|
static CPUState *mon_get_cpu(void)
|
2005-11-22 02:25:50 +03:00
|
|
|
{
|
2009-03-06 02:01:42 +03:00
|
|
|
if (!cur_mon->mon_cpu) {
|
2005-11-22 02:25:50 +03:00
|
|
|
mon_set_cpu(0);
|
|
|
|
}
|
2009-03-12 23:12:57 +03:00
|
|
|
cpu_synchronize_state(cur_mon->mon_cpu, 0);
|
2009-03-06 02:01:42 +03:00
|
|
|
return cur_mon->mon_cpu;
|
2005-11-22 02:25:50 +03:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_info_registers(Monitor *mon)
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
2005-11-22 02:25:50 +03:00
|
|
|
CPUState *env;
|
|
|
|
env = mon_get_cpu();
|
|
|
|
if (!env)
|
|
|
|
return;
|
2004-04-04 16:57:25 +04:00
|
|
|
#ifdef TARGET_I386
|
2009-03-06 02:01:23 +03:00
|
|
|
cpu_dump_state(env, (FILE *)mon, monitor_fprintf,
|
2005-07-04 01:28:00 +04:00
|
|
|
X86_DUMP_FPU);
|
2004-04-04 16:57:25 +04:00
|
|
|
#else
|
2009-03-06 02:01:23 +03:00
|
|
|
cpu_dump_state(env, (FILE *)mon, monitor_fprintf,
|
2004-10-09 22:08:01 +04:00
|
|
|
0);
|
2004-04-04 16:57:25 +04:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_info_cpus(Monitor *mon)
|
2005-11-22 02:25:50 +03:00
|
|
|
{
|
|
|
|
CPUState *env;
|
|
|
|
|
|
|
|
/* just to set the default cpu if not already done */
|
|
|
|
mon_get_cpu();
|
|
|
|
|
|
|
|
for(env = first_cpu; env != NULL; env = env->next_cpu) {
|
2009-03-12 23:12:57 +03:00
|
|
|
cpu_synchronize_state(env, 0);
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%c CPU #%d:",
|
2009-03-06 02:01:42 +03:00
|
|
|
(env == mon->mon_cpu) ? '*' : ' ',
|
2009-03-06 02:01:23 +03:00
|
|
|
env->cpu_index);
|
2005-11-22 02:25:50 +03:00
|
|
|
#if defined(TARGET_I386)
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, " pc=0x" TARGET_FMT_lx,
|
|
|
|
env->eip + env->segs[R_CS].base);
|
2005-11-24 01:05:28 +03:00
|
|
|
#elif defined(TARGET_PPC)
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, " nip=0x" TARGET_FMT_lx, env->nip);
|
2005-12-05 23:31:52 +03:00
|
|
|
#elif defined(TARGET_SPARC)
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, " pc=0x" TARGET_FMT_lx " npc=0x" TARGET_FMT_lx,
|
|
|
|
env->pc, env->npc);
|
2007-09-06 04:18:15 +04:00
|
|
|
#elif defined(TARGET_MIPS)
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, " PC=0x" TARGET_FMT_lx, env->active_tc.PC);
|
2008-05-28 21:14:10 +04:00
|
|
|
#endif
|
2007-09-06 04:18:15 +04:00
|
|
|
if (env->halted)
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, " (halted)");
|
|
|
|
monitor_printf(mon, "\n");
|
2005-11-22 02:25:50 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_cpu_set(Monitor *mon, int index)
|
2005-11-22 02:25:50 +03:00
|
|
|
{
|
|
|
|
if (mon_set_cpu(index) < 0)
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "Invalid CPU index\n");
|
2005-11-22 02:25:50 +03:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_info_jit(Monitor *mon)
|
2005-01-27 01:00:47 +03:00
|
|
|
{
|
2009-03-06 02:01:23 +03:00
|
|
|
dump_exec_info((FILE *)mon, monitor_fprintf);
|
2005-01-27 01:00:47 +03:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_info_history(Monitor *mon)
|
2004-04-04 17:07:25 +04:00
|
|
|
{
|
|
|
|
int i;
|
2004-08-02 01:52:19 +04:00
|
|
|
const char *str;
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2009-03-06 02:01:51 +03:00
|
|
|
if (!mon->rs)
|
|
|
|
return;
|
2004-08-02 01:52:19 +04:00
|
|
|
i = 0;
|
|
|
|
for(;;) {
|
2009-03-06 02:01:42 +03:00
|
|
|
str = readline_get_history(mon->rs, i);
|
2004-08-02 01:52:19 +04:00
|
|
|
if (!str)
|
|
|
|
break;
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%d: '%s'\n", i, str);
|
2004-10-09 21:32:58 +04:00
|
|
|
i++;
|
2004-04-04 17:07:25 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-07 11:32:30 +03:00
|
|
|
#if defined(TARGET_PPC)
|
|
|
|
/* XXX: not implemented in other targets */
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_info_cpu_stats(Monitor *mon)
|
2007-03-07 11:32:30 +03:00
|
|
|
{
|
|
|
|
CPUState *env;
|
|
|
|
|
|
|
|
env = mon_get_cpu();
|
2009-03-06 02:01:23 +03:00
|
|
|
cpu_dump_statistics(env, (FILE *)mon, &monitor_fprintf, 0);
|
2007-03-07 11:32:30 +03:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_quit(Monitor *mon)
|
2004-03-15 00:38:27 +03:00
|
|
|
{
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static int eject_device(Monitor *mon, BlockDriverState *bs, int force)
|
2004-03-15 00:38:27 +03:00
|
|
|
{
|
|
|
|
if (bdrv_is_inserted(bs)) {
|
|
|
|
if (!force) {
|
|
|
|
if (!bdrv_is_removable(bs)) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "device is not removable\n");
|
2004-03-15 00:38:27 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (bdrv_is_locked(bs)) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "device is locked\n");
|
2004-03-15 00:38:27 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bdrv_close(bs);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_eject(Monitor *mon, int force, const char *filename)
|
2004-03-15 00:38:27 +03:00
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
|
2004-04-04 16:57:25 +04:00
|
|
|
bs = bdrv_find(filename);
|
2004-03-15 00:38:27 +03:00
|
|
|
if (!bs) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "device not found\n");
|
2004-03-15 00:38:27 +03:00
|
|
|
return;
|
|
|
|
}
|
2009-03-06 02:01:23 +03:00
|
|
|
eject_device(mon, bs, force);
|
2004-03-15 00:38:27 +03:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_change_block(Monitor *mon, const char *device,
|
|
|
|
const char *filename, const char *fmt)
|
2004-03-15 00:38:27 +03:00
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
2008-06-19 02:10:01 +04:00
|
|
|
BlockDriver *drv = NULL;
|
2004-03-15 00:38:27 +03:00
|
|
|
|
2004-04-04 16:57:25 +04:00
|
|
|
bs = bdrv_find(device);
|
2004-03-15 00:38:27 +03:00
|
|
|
if (!bs) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "device not found\n");
|
2004-03-15 00:38:27 +03:00
|
|
|
return;
|
|
|
|
}
|
2008-06-19 02:10:01 +04:00
|
|
|
if (fmt) {
|
|
|
|
drv = bdrv_find_format(fmt);
|
|
|
|
if (!drv) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "invalid format %s\n", fmt);
|
2008-06-19 02:10:01 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2009-03-06 02:01:23 +03:00
|
|
|
if (eject_device(mon, bs, 0) < 0)
|
2004-03-15 00:38:27 +03:00
|
|
|
return;
|
2008-06-19 02:10:01 +04:00
|
|
|
bdrv_open2(bs, filename, 0, drv);
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_read_bdrv_key_start(mon, bs, NULL, NULL);
|
2009-03-06 02:01:15 +03:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void change_vnc_password_cb(Monitor *mon, const char *password,
|
|
|
|
void *opaque)
|
2009-03-06 02:01:15 +03:00
|
|
|
{
|
|
|
|
if (vnc_display_password(NULL, password) < 0)
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "could not set VNC server password\n");
|
2009-03-06 02:01:15 +03:00
|
|
|
|
2009-03-06 02:01:42 +03:00
|
|
|
monitor_read_command(mon, 1);
|
2004-03-15 00:38:27 +03:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_change_vnc(Monitor *mon, const char *target, const char *arg)
|
2007-08-25 05:36:20 +04:00
|
|
|
{
|
2007-08-25 05:37:05 +04:00
|
|
|
if (strcmp(target, "passwd") == 0 ||
|
2009-03-06 23:27:40 +03:00
|
|
|
strcmp(target, "password") == 0) {
|
|
|
|
if (arg) {
|
2009-03-06 02:01:15 +03:00
|
|
|
char password[9];
|
2009-03-06 23:27:40 +03:00
|
|
|
strncpy(password, arg, sizeof(password));
|
|
|
|
password[sizeof(password) - 1] = '\0';
|
2009-03-06 02:01:23 +03:00
|
|
|
change_vnc_password_cb(mon, password, NULL);
|
2009-03-06 02:01:15 +03:00
|
|
|
} else {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_read_password(mon, change_vnc_password_cb, NULL);
|
2009-03-06 02:01:15 +03:00
|
|
|
}
|
2007-08-25 05:37:05 +04:00
|
|
|
} else {
|
2009-03-06 23:27:40 +03:00
|
|
|
if (vnc_display_open(NULL, target) < 0)
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "could not start VNC server on %s\n", target);
|
2007-08-25 05:37:05 +04:00
|
|
|
}
|
2007-08-25 05:36:20 +04:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_change(Monitor *mon, const char *device, const char *target,
|
|
|
|
const char *arg)
|
2007-08-25 05:36:20 +04:00
|
|
|
{
|
|
|
|
if (strcmp(device, "vnc") == 0) {
|
2009-03-06 23:27:40 +03:00
|
|
|
do_change_vnc(mon, target, arg);
|
2007-08-25 05:36:20 +04:00
|
|
|
} else {
|
2009-03-06 23:27:40 +03:00
|
|
|
do_change_block(mon, device, target, arg);
|
2007-08-25 05:36:20 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_screen_dump(Monitor *mon, const char *filename)
|
2004-03-18 02:17:16 +03:00
|
|
|
{
|
2006-04-09 05:06:34 +04:00
|
|
|
vga_hw_screen_dump(filename);
|
2004-03-18 02:17:16 +03:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_logfile(Monitor *mon, const char *filename)
|
2007-06-30 17:53:24 +04:00
|
|
|
{
|
|
|
|
cpu_set_log_filename(filename);
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_log(Monitor *mon, const char *items)
|
2004-03-21 20:06:25 +03:00
|
|
|
{
|
|
|
|
int mask;
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2004-04-04 16:57:25 +04:00
|
|
|
if (!strcmp(items, "none")) {
|
2004-03-21 20:06:25 +03:00
|
|
|
mask = 0;
|
|
|
|
} else {
|
2004-04-04 16:57:25 +04:00
|
|
|
mask = cpu_str_to_log_mask(items);
|
2004-03-21 20:06:25 +03:00
|
|
|
if (!mask) {
|
2009-03-06 02:01:23 +03:00
|
|
|
help_cmd(mon, "log");
|
2004-03-21 20:06:25 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cpu_set_log(mask);
|
|
|
|
}
|
|
|
|
|
2009-04-06 00:08:59 +04:00
|
|
|
static void do_singlestep(Monitor *mon, const char *option)
|
|
|
|
{
|
|
|
|
if (!option || !strcmp(option, "on")) {
|
|
|
|
singlestep = 1;
|
|
|
|
} else if (!strcmp(option, "off")) {
|
|
|
|
singlestep = 0;
|
|
|
|
} else {
|
|
|
|
monitor_printf(mon, "unexpected option %s\n", option);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_stop(Monitor *mon)
|
2004-03-31 23:00:16 +04:00
|
|
|
{
|
|
|
|
vm_stop(EXCP_INTERRUPT);
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:15 +03:00
|
|
|
static void encrypted_bdrv_it(void *opaque, BlockDriverState *bs);
|
2009-03-06 02:01:01 +03:00
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
struct bdrv_iterate_context {
|
|
|
|
Monitor *mon;
|
|
|
|
int err;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void do_cont(Monitor *mon)
|
2004-03-31 23:00:16 +04:00
|
|
|
{
|
2009-03-06 02:01:23 +03:00
|
|
|
struct bdrv_iterate_context context = { mon, 0 };
|
2009-03-06 02:01:01 +03:00
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
bdrv_iterate(encrypted_bdrv_it, &context);
|
2009-03-06 02:01:01 +03:00
|
|
|
/* only resume the vm if all keys are set and valid */
|
2009-03-06 02:01:23 +03:00
|
|
|
if (!context.err)
|
2009-03-06 02:01:01 +03:00
|
|
|
vm_start();
|
2004-03-31 23:00:16 +04:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:15 +03:00
|
|
|
static void bdrv_key_cb(void *opaque, int err)
|
|
|
|
{
|
2009-03-06 02:01:23 +03:00
|
|
|
Monitor *mon = opaque;
|
|
|
|
|
2009-03-06 02:01:15 +03:00
|
|
|
/* another key was set successfully, retry to continue */
|
|
|
|
if (!err)
|
2009-03-06 02:01:23 +03:00
|
|
|
do_cont(mon);
|
2009-03-06 02:01:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void encrypted_bdrv_it(void *opaque, BlockDriverState *bs)
|
|
|
|
{
|
2009-03-06 02:01:23 +03:00
|
|
|
struct bdrv_iterate_context *context = opaque;
|
2009-03-06 02:01:15 +03:00
|
|
|
|
2009-03-06 02:01:23 +03: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 02:01:15 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-04-01 03:37:16 +04:00
|
|
|
#ifdef CONFIG_GDBSTUB
|
2009-04-05 22:43:41 +04:00
|
|
|
static void do_gdbserver(Monitor *mon, const char *device)
|
|
|
|
{
|
|
|
|
if (!device)
|
|
|
|
device = "tcp::" DEFAULT_GDBSTUB_PORT;
|
|
|
|
if (gdbserver_start(device) < 0) {
|
|
|
|
monitor_printf(mon, "Could not open gdbserver on device '%s'\n",
|
|
|
|
device);
|
|
|
|
} else if (strcmp(device, "none") == 0) {
|
2009-03-28 21:05:53 +03:00
|
|
|
monitor_printf(mon, "Disabled gdbserver\n");
|
2004-03-31 23:00:16 +04:00
|
|
|
} else {
|
2009-04-05 22:43:41 +04:00
|
|
|
monitor_printf(mon, "Waiting for gdb connection on device '%s'\n",
|
|
|
|
device);
|
2004-03-31 23:00:16 +04:00
|
|
|
}
|
|
|
|
}
|
2004-04-01 03:37:16 +04:00
|
|
|
#endif
|
2004-03-31 23:00:16 +04:00
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void monitor_printc(Monitor *mon, int c)
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "'");
|
2004-04-04 16:57:25 +04:00
|
|
|
switch(c) {
|
|
|
|
case '\'':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "\\'");
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case '\\':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "\\\\");
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case '\n':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "\\n");
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case '\r':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "\\r");
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (c >= 32 && c <= 126) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%c", c);
|
2004-04-04 16:57:25 +04:00
|
|
|
} else {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "\\x%02x", c);
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "'");
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void memory_dump(Monitor *mon, int count, int format, int wsize,
|
2007-09-24 22:39:04 +04:00
|
|
|
target_phys_addr_t addr, int is_physical)
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
2005-11-22 02:25:50 +03:00
|
|
|
CPUState *env;
|
2004-04-04 16:57:25 +04:00
|
|
|
int nb_per_line, l, line_size, i, max_digits, len;
|
|
|
|
uint8_t buf[16];
|
|
|
|
uint64_t v;
|
|
|
|
|
|
|
|
if (format == 'i') {
|
|
|
|
int flags;
|
|
|
|
flags = 0;
|
2005-11-22 02:25:50 +03:00
|
|
|
env = mon_get_cpu();
|
|
|
|
if (!env && !is_physical)
|
|
|
|
return;
|
2004-04-04 16:57:25 +04:00
|
|
|
#ifdef TARGET_I386
|
2004-04-25 22:05:08 +04:00
|
|
|
if (wsize == 2) {
|
2004-04-04 16:57:25 +04:00
|
|
|
flags = 1;
|
2004-04-25 22:05:08 +04:00
|
|
|
} else if (wsize == 4) {
|
|
|
|
flags = 0;
|
|
|
|
} else {
|
2006-04-13 01:07:07 +04:00
|
|
|
/* as default we use the current CS size */
|
2004-04-25 22:05:08 +04:00
|
|
|
flags = 0;
|
2006-04-13 01:07:07 +04:00
|
|
|
if (env) {
|
|
|
|
#ifdef TARGET_X86_64
|
2007-09-17 01:08:06 +04:00
|
|
|
if ((env->efer & MSR_EFER_LMA) &&
|
2006-04-13 01:07:07 +04:00
|
|
|
(env->segs[R_CS].flags & DESC_L_MASK))
|
|
|
|
flags = 2;
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
if (!(env->segs[R_CS].flags & DESC_B_MASK))
|
|
|
|
flags = 1;
|
|
|
|
}
|
2004-04-25 22:05:08 +04:00
|
|
|
}
|
|
|
|
#endif
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_disas(mon, env, addr, count, is_physical, flags);
|
2004-04-04 16:57:25 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = wsize * count;
|
|
|
|
if (wsize == 1)
|
|
|
|
line_size = 8;
|
|
|
|
else
|
|
|
|
line_size = 16;
|
|
|
|
nb_per_line = line_size / wsize;
|
|
|
|
max_digits = 0;
|
|
|
|
|
|
|
|
switch(format) {
|
|
|
|
case 'o':
|
|
|
|
max_digits = (wsize * 8 + 2) / 3;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
case 'x':
|
|
|
|
max_digits = (wsize * 8) / 4;
|
|
|
|
break;
|
|
|
|
case 'u':
|
|
|
|
case 'd':
|
|
|
|
max_digits = (wsize * 8 * 10 + 32) / 33;
|
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
wsize = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (len > 0) {
|
2007-09-24 22:39:04 +04:00
|
|
|
if (is_physical)
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, TARGET_FMT_plx ":", addr);
|
2007-09-24 22:39:04 +04:00
|
|
|
else
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, TARGET_FMT_lx ":", (target_ulong)addr);
|
2004-04-04 16:57:25 +04:00
|
|
|
l = len;
|
|
|
|
if (l > line_size)
|
|
|
|
l = line_size;
|
|
|
|
if (is_physical) {
|
|
|
|
cpu_physical_memory_rw(addr, buf, l, 0);
|
|
|
|
} else {
|
2005-11-22 02:25:50 +03:00
|
|
|
env = mon_get_cpu();
|
|
|
|
if (!env)
|
|
|
|
break;
|
2008-08-18 18:00:20 +04:00
|
|
|
if (cpu_memory_rw_debug(env, addr, buf, l, 0) < 0) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, " Cannot access memory\n");
|
2008-08-18 18:00:20 +04:00
|
|
|
break;
|
|
|
|
}
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
2007-09-17 01:08:06 +04:00
|
|
|
i = 0;
|
2004-04-04 16:57:25 +04:00
|
|
|
while (i < l) {
|
|
|
|
switch(wsize) {
|
|
|
|
default:
|
|
|
|
case 1:
|
|
|
|
v = ldub_raw(buf + i);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
v = lduw_raw(buf + i);
|
|
|
|
break;
|
|
|
|
case 4:
|
2005-02-11 01:00:52 +03:00
|
|
|
v = (uint32_t)ldl_raw(buf + i);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
v = ldq_raw(buf + i);
|
|
|
|
break;
|
|
|
|
}
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, " ");
|
2004-04-04 16:57:25 +04:00
|
|
|
switch(format) {
|
|
|
|
case 'o':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%#*" PRIo64, max_digits, v);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case 'x':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "0x%0*" PRIx64, max_digits, v);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case 'u':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%*" PRIu64, max_digits, v);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case 'd':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%*" PRId64, max_digits, v);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case 'c':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printc(mon, v);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
i += wsize;
|
|
|
|
}
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "\n");
|
2004-04-04 16:57:25 +04:00
|
|
|
addr += l;
|
|
|
|
len -= l;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-02-11 01:00:52 +03:00
|
|
|
#if TARGET_LONG_BITS == 64
|
|
|
|
#define GET_TLONG(h, l) (((uint64_t)(h) << 32) | (l))
|
|
|
|
#else
|
|
|
|
#define GET_TLONG(h, l) (l)
|
|
|
|
#endif
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_memory_dump(Monitor *mon, int count, int format, int size,
|
2005-02-11 01:00:52 +03:00
|
|
|
uint32_t addrh, uint32_t addrl)
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
2005-02-11 01:00:52 +03:00
|
|
|
target_long addr = GET_TLONG(addrh, addrl);
|
2009-03-06 02:01:23 +03:00
|
|
|
memory_dump(mon, count, format, size, addr, 0);
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
|
|
|
|
2007-09-24 22:39:04 +04:00
|
|
|
#if TARGET_PHYS_ADDR_BITS > 32
|
|
|
|
#define GET_TPHYSADDR(h, l) (((uint64_t)(h) << 32) | (l))
|
|
|
|
#else
|
|
|
|
#define GET_TPHYSADDR(h, l) (l)
|
|
|
|
#endif
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_physical_memory_dump(Monitor *mon, int count, int format,
|
|
|
|
int size, uint32_t addrh, uint32_t addrl)
|
2005-02-11 01:00:52 +03:00
|
|
|
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
2007-09-24 22:39:04 +04:00
|
|
|
target_phys_addr_t addr = GET_TPHYSADDR(addrh, addrl);
|
2009-03-06 02:01:23 +03:00
|
|
|
memory_dump(mon, count, format, size, addr, 1);
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_print(Monitor *mon, int count, int format, int size,
|
|
|
|
unsigned int valh, unsigned int vall)
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
2007-09-24 22:39:04 +04:00
|
|
|
target_phys_addr_t val = GET_TPHYSADDR(valh, vall);
|
|
|
|
#if TARGET_PHYS_ADDR_BITS == 32
|
2004-04-04 16:57:25 +04:00
|
|
|
switch(format) {
|
|
|
|
case 'o':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%#o", val);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case 'x':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%#x", val);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case 'u':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%u", val);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
case 'd':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%d", val);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case 'c':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printc(mon, val);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
}
|
2005-02-11 01:00:52 +03:00
|
|
|
#else
|
|
|
|
switch(format) {
|
|
|
|
case 'o':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%#" PRIo64, val);
|
2005-02-11 01:00:52 +03:00
|
|
|
break;
|
|
|
|
case 'x':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%#" PRIx64, val);
|
2005-02-11 01:00:52 +03:00
|
|
|
break;
|
|
|
|
case 'u':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%" PRIu64, val);
|
2005-02-11 01:00:52 +03:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
case 'd':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%" PRId64, val);
|
2005-02-11 01:00:52 +03:00
|
|
|
break;
|
|
|
|
case 'c':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printc(mon, val);
|
2005-02-11 01:00:52 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "\n");
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_memory_save(Monitor *mon, unsigned int valh, unsigned int vall,
|
2007-01-03 18:20:39 +03:00
|
|
|
uint32_t size, const char *filename)
|
|
|
|
{
|
|
|
|
FILE *f;
|
|
|
|
target_long addr = GET_TLONG(valh, vall);
|
|
|
|
uint32_t l;
|
|
|
|
CPUState *env;
|
|
|
|
uint8_t buf[1024];
|
|
|
|
|
|
|
|
env = mon_get_cpu();
|
|
|
|
if (!env)
|
|
|
|
return;
|
|
|
|
|
|
|
|
f = fopen(filename, "wb");
|
|
|
|
if (!f) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "could not open '%s'\n", filename);
|
2007-01-03 18:20:39 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
while (size != 0) {
|
|
|
|
l = sizeof(buf);
|
|
|
|
if (l > size)
|
|
|
|
l = size;
|
|
|
|
cpu_memory_rw_debug(env, addr, buf, l, 0);
|
|
|
|
fwrite(buf, 1, l, f);
|
|
|
|
addr += l;
|
|
|
|
size -= l;
|
|
|
|
}
|
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_physical_memory_save(Monitor *mon, unsigned int valh,
|
|
|
|
unsigned int vall, uint32_t size,
|
|
|
|
const char *filename)
|
2008-04-12 01:36:14 +04:00
|
|
|
{
|
|
|
|
FILE *f;
|
|
|
|
uint32_t l;
|
|
|
|
uint8_t buf[1024];
|
2008-04-13 00:14:43 +04:00
|
|
|
target_phys_addr_t addr = GET_TPHYSADDR(valh, vall);
|
2008-04-12 01:36:14 +04:00
|
|
|
|
|
|
|
f = fopen(filename, "wb");
|
|
|
|
if (!f) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "could not open '%s'\n", filename);
|
2008-04-12 01:36:14 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
while (size != 0) {
|
|
|
|
l = sizeof(buf);
|
|
|
|
if (l > size)
|
|
|
|
l = size;
|
|
|
|
cpu_physical_memory_rw(addr, buf, l, 0);
|
|
|
|
fwrite(buf, 1, l, f);
|
|
|
|
fflush(f);
|
|
|
|
addr += l;
|
|
|
|
size -= l;
|
|
|
|
}
|
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_sum(Monitor *mon, uint32_t start, uint32_t size)
|
2005-06-05 00:15:57 +04:00
|
|
|
{
|
|
|
|
uint32_t addr;
|
|
|
|
uint8_t buf[1];
|
|
|
|
uint16_t sum;
|
|
|
|
|
|
|
|
sum = 0;
|
|
|
|
for(addr = start; addr < (start + size); addr++) {
|
|
|
|
cpu_physical_memory_rw(addr, buf, 1, 0);
|
|
|
|
/* BSD sum algorithm ('sum' Unix command) */
|
|
|
|
sum = (sum >> 1) | (sum << 15);
|
|
|
|
sum += buf[0];
|
|
|
|
}
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%05d\n", sum);
|
2005-06-05 00:15:57 +04:00
|
|
|
}
|
|
|
|
|
2004-06-04 15:06:21 +04:00
|
|
|
typedef struct {
|
|
|
|
int keycode;
|
|
|
|
const char *name;
|
|
|
|
} KeyDef;
|
|
|
|
|
|
|
|
static const KeyDef key_defs[] = {
|
|
|
|
{ 0x2a, "shift" },
|
|
|
|
{ 0x36, "shift_r" },
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2004-06-04 15:06:21 +04:00
|
|
|
{ 0x38, "alt" },
|
|
|
|
{ 0xb8, "alt_r" },
|
2008-08-13 16:54:23 +04:00
|
|
|
{ 0x64, "altgr" },
|
|
|
|
{ 0xe4, "altgr_r" },
|
2004-06-04 15:06:21 +04: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-07 22:03:31 +04:00
|
|
|
{ 0x0c, "minus" },
|
|
|
|
{ 0x0d, "equal" },
|
2004-06-04 15:06:21 +04: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 01:46:15 +04:00
|
|
|
{ 0x33, "comma" },
|
|
|
|
{ 0x34, "dot" },
|
|
|
|
{ 0x35, "slash" },
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2008-02-10 19:33:14 +03:00
|
|
|
{ 0x37, "asterisk" },
|
|
|
|
|
2004-06-04 15:06:21 +04:00
|
|
|
{ 0x39, "spc" },
|
2004-06-04 17:25:15 +04:00
|
|
|
{ 0x3a, "caps_lock" },
|
2004-06-04 15:06:21 +04:00
|
|
|
{ 0x3b, "f1" },
|
|
|
|
{ 0x3c, "f2" },
|
|
|
|
{ 0x3d, "f3" },
|
|
|
|
{ 0x3e, "f4" },
|
|
|
|
{ 0x3f, "f5" },
|
|
|
|
{ 0x40, "f6" },
|
|
|
|
{ 0x41, "f7" },
|
|
|
|
{ 0x42, "f8" },
|
|
|
|
{ 0x43, "f9" },
|
|
|
|
{ 0x44, "f10" },
|
2004-06-04 17:25:15 +04:00
|
|
|
{ 0x45, "num_lock" },
|
2004-06-04 15:06:21 +04:00
|
|
|
{ 0x46, "scroll_lock" },
|
|
|
|
|
2006-05-07 22:03:31 +04:00
|
|
|
{ 0xb5, "kp_divide" },
|
|
|
|
{ 0x37, "kp_multiply" },
|
2007-06-23 20:02:43 +04:00
|
|
|
{ 0x4a, "kp_subtract" },
|
2006-05-07 22:03:31 +04:00
|
|
|
{ 0x4e, "kp_add" },
|
|
|
|
{ 0x9c, "kp_enter" },
|
|
|
|
{ 0x53, "kp_decimal" },
|
2008-06-04 14:14:16 +04:00
|
|
|
{ 0x54, "sysrq" },
|
2006-05-07 22:03:31 +04: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 12:09:54 +04:00
|
|
|
|
2004-06-04 15:06:21 +04: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 11:45:42 +04: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 15:06:21 +04:00
|
|
|
{ 0, NULL },
|
|
|
|
};
|
|
|
|
|
|
|
|
static int get_keycode(const char *key)
|
|
|
|
{
|
|
|
|
const KeyDef *p;
|
2006-05-07 22:03:31 +04:00
|
|
|
char *endp;
|
|
|
|
int ret;
|
2004-06-04 15:06:21 +04:00
|
|
|
|
|
|
|
for(p = key_defs; p->name != NULL; p++) {
|
|
|
|
if (!strcmp(key, p->name))
|
|
|
|
return p->keycode;
|
|
|
|
}
|
2006-05-07 22:03:31 +04:00
|
|
|
if (strstart(key, "0x", NULL)) {
|
|
|
|
ret = strtoul(key, &endp, 0);
|
|
|
|
if (*endp == '\0' && ret >= 0x01 && ret <= 0xff)
|
|
|
|
return ret;
|
|
|
|
}
|
2004-06-04 15:06:21 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2008-06-09 02:45:01 +04: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-03-06 02:01:23 +03:00
|
|
|
static void do_sendkey(Monitor *mon, const char *string, int has_hold_time,
|
|
|
|
int hold_time)
|
2004-06-04 15:06:21 +04:00
|
|
|
{
|
2008-06-04 14:05:59 +04:00
|
|
|
char keyname_buf[16];
|
|
|
|
char *separator;
|
|
|
|
int keyname_len, keycode, i;
|
|
|
|
|
2008-06-09 02:45:01 +04: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 14:05:59 +04: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 02:01:23 +03:00
|
|
|
monitor_printf(mon, "invalid key: '%s...'\n", keyname_buf);
|
2008-06-04 14:05:59 +04:00
|
|
|
return;
|
2004-06-04 15:06:21 +04:00
|
|
|
}
|
2008-06-09 02:45:01 +04:00
|
|
|
if (i == MAX_KEYCODES) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "too many keys\n");
|
2008-06-04 14:05:59 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
keyname_buf[keyname_len] = 0;
|
|
|
|
keycode = get_keycode(keyname_buf);
|
|
|
|
if (keycode < 0) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "unknown key: '%s'\n", keyname_buf);
|
2008-06-04 14:05:59 +04:00
|
|
|
return;
|
|
|
|
}
|
2008-06-09 02:45:01 +04:00
|
|
|
keycodes[i++] = keycode;
|
2004-06-04 15:06:21 +04:00
|
|
|
}
|
2008-06-04 14:05:59 +04:00
|
|
|
if (!separator)
|
2004-06-04 15:06:21 +04:00
|
|
|
break;
|
2008-06-04 14:05:59 +04:00
|
|
|
string = separator + 1;
|
2004-06-04 15:06:21 +04:00
|
|
|
}
|
2008-06-09 02:45:01 +04:00
|
|
|
nb_pending_keycodes = i;
|
2004-06-04 15:06:21 +04:00
|
|
|
/* key down events */
|
2008-06-09 02:45:01 +04:00
|
|
|
for (i = 0; i < nb_pending_keycodes; i++) {
|
2004-06-04 15:06:21 +04:00
|
|
|
keycode = keycodes[i];
|
|
|
|
if (keycode & 0x80)
|
|
|
|
kbd_put_keycode(0xe0);
|
|
|
|
kbd_put_keycode(keycode & 0x7f);
|
|
|
|
}
|
2008-06-09 02:45:01 +04:00
|
|
|
/* delayed key up events */
|
2008-06-09 04:03:47 +04:00
|
|
|
qemu_mod_timer(key_timer, qemu_get_clock(vm_clock) +
|
|
|
|
muldiv64(ticks_per_sec, hold_time, 1000));
|
2004-06-04 15:06:21 +04:00
|
|
|
}
|
|
|
|
|
2006-07-15 02:03:35 +04:00
|
|
|
static int mouse_button_state;
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_mouse_move(Monitor *mon, const char *dx_str, const char *dy_str,
|
2006-07-15 02:03:35 +04:00
|
|
|
const char *dz_str)
|
|
|
|
{
|
|
|
|
int dx, dy, dz;
|
|
|
|
dx = strtol(dx_str, NULL, 0);
|
|
|
|
dy = strtol(dy_str, NULL, 0);
|
|
|
|
dz = 0;
|
2007-09-17 01:08:06 +04:00
|
|
|
if (dz_str)
|
2006-07-15 02:03:35 +04:00
|
|
|
dz = strtol(dz_str, NULL, 0);
|
|
|
|
kbd_mouse_event(dx, dy, dz, mouse_button_state);
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_mouse_button(Monitor *mon, int button_state)
|
2006-07-15 02:03:35 +04:00
|
|
|
{
|
|
|
|
mouse_button_state = button_state;
|
|
|
|
kbd_mouse_event(0, 0, 0, mouse_button_state);
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_ioport_read(Monitor *mon, int count, int format, int size,
|
|
|
|
int addr, int has_index, int index)
|
2004-06-08 04:55:58 +04:00
|
|
|
{
|
|
|
|
uint32_t val;
|
|
|
|
int suffix;
|
|
|
|
|
|
|
|
if (has_index) {
|
|
|
|
cpu_outb(NULL, addr & 0xffff, index & 0xff);
|
|
|
|
addr++;
|
|
|
|
}
|
|
|
|
addr &= 0xffff;
|
|
|
|
|
|
|
|
switch(size) {
|
|
|
|
default:
|
|
|
|
case 1:
|
|
|
|
val = cpu_inb(NULL, addr);
|
|
|
|
suffix = 'b';
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
val = cpu_inw(NULL, addr);
|
|
|
|
suffix = 'w';
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
val = cpu_inl(NULL, addr);
|
|
|
|
suffix = 'l';
|
|
|
|
break;
|
|
|
|
}
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "port%c[0x%04x] = %#0*x\n",
|
|
|
|
suffix, addr, size * 2, val);
|
2004-06-08 04:55:58 +04:00
|
|
|
}
|
2004-06-04 15:06:21 +04:00
|
|
|
|
2008-06-20 20:25:06 +04:00
|
|
|
/* boot_set handler */
|
|
|
|
static QEMUBootSetHandler *qemu_boot_set_handler = NULL;
|
|
|
|
static void *boot_opaque;
|
|
|
|
|
|
|
|
void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque)
|
|
|
|
{
|
|
|
|
qemu_boot_set_handler = func;
|
|
|
|
boot_opaque = opaque;
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_boot_set(Monitor *mon, const char *bootdevice)
|
2008-05-05 00:11:34 +04:00
|
|
|
{
|
|
|
|
int res;
|
|
|
|
|
|
|
|
if (qemu_boot_set_handler) {
|
2008-06-20 20:25:06 +04:00
|
|
|
res = qemu_boot_set_handler(boot_opaque, bootdevice);
|
2008-05-05 00:11:34 +04:00
|
|
|
if (res == 0)
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "boot device list now set to %s\n",
|
|
|
|
bootdevice);
|
2008-05-05 00:11:34 +04:00
|
|
|
else
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "setting boot device list failed with "
|
|
|
|
"error %i\n", res);
|
2008-05-05 00:11:34 +04:00
|
|
|
} else {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "no function defined to set boot device list for "
|
|
|
|
"this architecture\n");
|
2008-05-05 00:11:34 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_system_reset(Monitor *mon)
|
2004-06-20 16:35:44 +04:00
|
|
|
{
|
|
|
|
qemu_system_reset_request();
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_system_powerdown(Monitor *mon)
|
2005-07-02 18:31:34 +04:00
|
|
|
{
|
|
|
|
qemu_system_powerdown_request();
|
|
|
|
}
|
|
|
|
|
2004-09-18 23:32:46 +04:00
|
|
|
#if defined(TARGET_I386)
|
2009-03-06 02:01:23 +03:00
|
|
|
static void print_pte(Monitor *mon, uint32_t addr, uint32_t pte, uint32_t mask)
|
2004-09-18 23:32:46 +04:00
|
|
|
{
|
2009-03-06 02:01:23 +03: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-18 23:32:46 +04:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void tlb_info(Monitor *mon)
|
2004-09-18 23:32:46 +04:00
|
|
|
{
|
2005-11-22 02:25:50 +03:00
|
|
|
CPUState *env;
|
2004-09-18 23:32:46 +04:00
|
|
|
int l1, l2;
|
|
|
|
uint32_t pgd, pde, pte;
|
|
|
|
|
2005-11-22 02:25:50 +03:00
|
|
|
env = mon_get_cpu();
|
|
|
|
if (!env)
|
|
|
|
return;
|
|
|
|
|
2004-09-18 23:32:46 +04:00
|
|
|
if (!(env->cr[0] & CR0_PG_MASK)) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "PG disabled\n");
|
2004-09-18 23:32:46 +04: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 02:01:23 +03:00
|
|
|
print_pte(mon, (l1 << 22), pde, ~((1 << 20) - 1));
|
2004-09-18 23:32:46 +04:00
|
|
|
} else {
|
|
|
|
for(l2 = 0; l2 < 1024; l2++) {
|
2007-09-17 01:08:06 +04:00
|
|
|
cpu_physical_memory_read((pde & ~0xfff) + l2 * 4,
|
2004-09-18 23:32:46 +04:00
|
|
|
(uint8_t *)&pte, 4);
|
|
|
|
pte = le32_to_cpu(pte);
|
|
|
|
if (pte & PG_PRESENT_MASK) {
|
2009-03-06 02:01:23 +03:00
|
|
|
print_pte(mon, (l1 << 22) + (l2 << 12),
|
2007-09-17 01:08:06 +04:00
|
|
|
pte & ~PG_PSE_MASK,
|
2004-09-18 23:32:46 +04:00
|
|
|
~0xfff);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void mem_print(Monitor *mon, uint32_t *pstart, int *plast_prot,
|
2004-09-18 23:32:46 +04:00
|
|
|
uint32_t end, int prot)
|
|
|
|
{
|
2004-11-11 21:30:24 +03:00
|
|
|
int prot1;
|
|
|
|
prot1 = *plast_prot;
|
|
|
|
if (prot != prot1) {
|
2004-09-18 23:32:46 +04:00
|
|
|
if (*pstart != -1) {
|
2009-03-06 02:01:23 +03: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-18 23:32:46 +04:00
|
|
|
}
|
|
|
|
if (prot != 0)
|
|
|
|
*pstart = end;
|
|
|
|
else
|
|
|
|
*pstart = -1;
|
|
|
|
*plast_prot = prot;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void mem_info(Monitor *mon)
|
2004-09-18 23:32:46 +04:00
|
|
|
{
|
2005-11-22 02:25:50 +03:00
|
|
|
CPUState *env;
|
2004-09-18 23:32:46 +04:00
|
|
|
int l1, l2, prot, last_prot;
|
|
|
|
uint32_t pgd, pde, pte, start, end;
|
|
|
|
|
2005-11-22 02:25:50 +03:00
|
|
|
env = mon_get_cpu();
|
|
|
|
if (!env)
|
|
|
|
return;
|
|
|
|
|
2004-09-18 23:32:46 +04:00
|
|
|
if (!(env->cr[0] & CR0_PG_MASK)) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "PG disabled\n");
|
2004-09-18 23:32:46 +04: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 02:01:23 +03:00
|
|
|
mem_print(mon, &start, &last_prot, end, prot);
|
2004-09-18 23:32:46 +04:00
|
|
|
} else {
|
|
|
|
for(l2 = 0; l2 < 1024; l2++) {
|
2007-09-17 01:08:06 +04:00
|
|
|
cpu_physical_memory_read((pde & ~0xfff) + l2 * 4,
|
2004-09-18 23:32:46 +04: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 02:01:23 +03:00
|
|
|
mem_print(mon, &start, &last_prot, end, prot);
|
2004-09-18 23:32:46 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
prot = 0;
|
2009-03-06 02:01:23 +03:00
|
|
|
mem_print(mon, &start, &last_prot, end, prot);
|
2004-09-18 23:32:46 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-03-03 09:12:22 +03:00
|
|
|
#if defined(TARGET_SH4)
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void print_tlb(Monitor *mon, int idx, tlb_t *tlb)
|
2009-03-03 09:12:22 +03:00
|
|
|
{
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, " tlb%i:\t"
|
|
|
|
"asid=%hhu vpn=%x\tppn=%x\tsz=%hhu size=%u\t"
|
|
|
|
"v=%hhu shared=%hhu cached=%hhu prot=%hhu "
|
|
|
|
"dirty=%hhu writethrough=%hhu\n",
|
|
|
|
idx,
|
|
|
|
tlb->asid, tlb->vpn, tlb->ppn, tlb->sz, tlb->size,
|
|
|
|
tlb->v, tlb->sh, tlb->c, tlb->pr,
|
|
|
|
tlb->d, tlb->wt);
|
2009-03-03 09:12:22 +03:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void tlb_info(Monitor *mon)
|
2009-03-03 09:12:22 +03:00
|
|
|
{
|
|
|
|
CPUState *env = mon_get_cpu();
|
|
|
|
int i;
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf (mon, "ITLB:\n");
|
2009-03-03 09:12:22 +03:00
|
|
|
for (i = 0 ; i < ITLB_SIZE ; i++)
|
2009-03-06 02:01:23 +03:00
|
|
|
print_tlb (mon, i, &env->itlb[i]);
|
|
|
|
monitor_printf (mon, "UTLB:\n");
|
2009-03-03 09:12:22 +03:00
|
|
|
for (i = 0 ; i < UTLB_SIZE ; i++)
|
2009-03-06 02:01:23 +03:00
|
|
|
print_tlb (mon, i, &env->utlb[i]);
|
2009-03-03 09:12:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_info_kqemu(Monitor *mon)
|
2005-07-24 22:10:56 +04:00
|
|
|
{
|
|
|
|
#ifdef USE_KQEMU
|
2005-11-22 02:25:50 +03:00
|
|
|
CPUState *env;
|
2005-07-24 22:10:56 +04:00
|
|
|
int val;
|
|
|
|
val = 0;
|
2005-11-22 02:25:50 +03:00
|
|
|
env = mon_get_cpu();
|
|
|
|
if (!env) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "No cpu initialized yet");
|
2005-11-22 02:25:50 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
val = env->kqemu_enabled;
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "kqemu support: ");
|
2006-02-09 01:40:15 +03:00
|
|
|
switch(val) {
|
|
|
|
default:
|
|
|
|
case 0:
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "disabled\n");
|
2006-02-09 01:40:15 +03:00
|
|
|
break;
|
|
|
|
case 1:
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "enabled for user code\n");
|
2006-02-09 01:40:15 +03:00
|
|
|
break;
|
|
|
|
case 2:
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "enabled for user and kernel code\n");
|
2006-02-09 01:40:15 +03:00
|
|
|
break;
|
|
|
|
}
|
2005-07-24 22:10:56 +04:00
|
|
|
#else
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "kqemu support: not compiled\n");
|
2005-07-24 22:10:56 +04:00
|
|
|
#endif
|
2007-09-17 01:08:06 +04:00
|
|
|
}
|
2005-07-24 22:10:56 +04:00
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_info_kvm(Monitor *mon)
|
2008-11-05 19:04:33 +03:00
|
|
|
{
|
|
|
|
#ifdef CONFIG_KVM
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "kvm support: ");
|
2008-11-05 19:04:33 +03:00
|
|
|
if (kvm_enabled())
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "enabled\n");
|
2008-11-05 19:04:33 +03:00
|
|
|
else
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "disabled\n");
|
2008-11-05 19:04:33 +03:00
|
|
|
#else
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "kvm support: not compiled\n");
|
2008-11-05 19:04:33 +03:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2006-02-09 01:40:15 +03:00
|
|
|
#ifdef CONFIG_PROFILER
|
|
|
|
|
|
|
|
int64_t kqemu_time;
|
|
|
|
int64_t qemu_time;
|
|
|
|
int64_t kqemu_exec_count;
|
|
|
|
int64_t dev_time;
|
|
|
|
int64_t kqemu_ret_int_count;
|
|
|
|
int64_t kqemu_ret_excp_count;
|
|
|
|
int64_t kqemu_ret_intr_count;
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_info_profile(Monitor *mon)
|
2006-02-09 01:40:15 +03:00
|
|
|
{
|
|
|
|
int64_t total;
|
|
|
|
total = qemu_time;
|
|
|
|
if (total == 0)
|
|
|
|
total = 1;
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "async time %" PRId64 " (%0.3f)\n",
|
|
|
|
dev_time, dev_time / (double)ticks_per_sec);
|
|
|
|
monitor_printf(mon, "qemu time %" PRId64 " (%0.3f)\n",
|
|
|
|
qemu_time, qemu_time / (double)ticks_per_sec);
|
|
|
|
monitor_printf(mon, "kqemu time %" PRId64 " (%0.3f %0.1f%%) count=%"
|
|
|
|
PRId64 " int=%" PRId64 " excp=%" PRId64 " intr=%"
|
|
|
|
PRId64 "\n",
|
|
|
|
kqemu_time, kqemu_time / (double)ticks_per_sec,
|
|
|
|
kqemu_time / (double)total * 100.0,
|
|
|
|
kqemu_exec_count,
|
|
|
|
kqemu_ret_int_count,
|
|
|
|
kqemu_ret_excp_count,
|
|
|
|
kqemu_ret_intr_count);
|
2006-02-09 01:40:15 +03:00
|
|
|
qemu_time = 0;
|
|
|
|
kqemu_time = 0;
|
|
|
|
kqemu_exec_count = 0;
|
|
|
|
dev_time = 0;
|
|
|
|
kqemu_ret_int_count = 0;
|
|
|
|
kqemu_ret_excp_count = 0;
|
|
|
|
kqemu_ret_intr_count = 0;
|
|
|
|
#ifdef USE_KQEMU
|
|
|
|
kqemu_record_dump();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#else
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_info_profile(Monitor *mon)
|
2006-02-09 01:40:15 +03:00
|
|
|
{
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "Internal profiler not compiled\n");
|
2006-02-09 01:40:15 +03:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2006-07-16 22:57:03 +04:00
|
|
|
/* Capture support */
|
|
|
|
static LIST_HEAD (capture_list_head, CaptureState) capture_head;
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_info_capture(Monitor *mon)
|
2006-07-16 22:57:03 +04:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
CaptureState *s;
|
|
|
|
|
|
|
|
for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "[%d]: ", i);
|
2006-07-16 22:57:03 +04:00
|
|
|
s->ops.info (s->opaque);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_stop_capture(Monitor *mon, int n)
|
2006-07-16 22:57:03 +04:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
CaptureState *s;
|
|
|
|
|
|
|
|
for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) {
|
|
|
|
if (i == n) {
|
|
|
|
s->ops.destroy (s->opaque);
|
|
|
|
LIST_REMOVE (s, entries);
|
|
|
|
qemu_free (s);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAS_AUDIO
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_wav_capture(Monitor *mon, const char *path,
|
|
|
|
int has_freq, int freq,
|
|
|
|
int has_bits, int bits,
|
|
|
|
int has_channels, int nchannels)
|
2006-07-16 22:57:03 +04: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 02:01:23 +03:00
|
|
|
monitor_printf(mon, "Faied to add wave capture\n");
|
2006-07-16 22:57:03 +04:00
|
|
|
qemu_free (s);
|
|
|
|
}
|
|
|
|
LIST_INSERT_HEAD (&capture_head, s, entries);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-04-28 03:52:12 +04:00
|
|
|
#if defined(TARGET_I386)
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_inject_nmi(Monitor *mon, int cpu_index)
|
2008-04-28 03:52:12 +04:00
|
|
|
{
|
|
|
|
CPUState *env;
|
|
|
|
|
|
|
|
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-03-06 02:01:23 +03:00
|
|
|
static void do_info_status(Monitor *mon)
|
2008-12-19 01:43:56 +03:00
|
|
|
{
|
2009-04-06 00:08:59 +04:00
|
|
|
if (vm_running) {
|
|
|
|
if (singlestep) {
|
|
|
|
monitor_printf(mon, "VM status: running (single step mode)\n");
|
|
|
|
} else {
|
|
|
|
monitor_printf(mon, "VM status: running\n");
|
|
|
|
}
|
|
|
|
} else
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "VM status: paused\n");
|
2008-12-19 01:43:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_balloon(Monitor *mon, int value)
|
2008-12-04 23:19:35 +03:00
|
|
|
{
|
|
|
|
ram_addr_t target = value;
|
|
|
|
qemu_balloon(target << 20);
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void do_info_balloon(Monitor *mon)
|
2008-12-04 23:19:35 +03:00
|
|
|
{
|
|
|
|
ram_addr_t actual;
|
|
|
|
|
|
|
|
actual = qemu_balloon_status();
|
2008-12-04 23:33:06 +03:00
|
|
|
if (kvm_enabled() && !kvm_has_sync_mmu())
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "Using KVM without synchronous MMU, "
|
|
|
|
"ballooning disabled\n");
|
2008-12-04 23:33:06 +03:00
|
|
|
else if (actual == 0)
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "Ballooning not activated in VM\n");
|
2008-12-04 23:19:35 +03:00
|
|
|
else
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "balloon: actual=%d\n", (int)(actual >> 20));
|
2008-12-04 23:19:35 +03: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-06 23:27:37 +03:00
|
|
|
static void do_acl(Monitor *mon,
|
|
|
|
const char *command,
|
2009-03-06 23:27:40 +03:00
|
|
|
const char *aclname,
|
|
|
|
const char *match,
|
|
|
|
int has_index,
|
|
|
|
int index)
|
Support ACLs for controlling VNC access ("Daniel P. Berrange")
This patch introduces a generic internal API for access control lists
to be used by network servers in QEMU. It adds support for checking
these ACL in the VNC server, in two places. The first ACL is for the
SASL authentication mechanism, checking the SASL username. This ACL
is called 'vnc.username'. The second is for the TLS authentication
mechanism, when x509 client certificates are turned on, checking against
the Distinguished Name of the client. This ACL is called 'vnc.x509dname'
The internal API provides for an ACL with the following characteristics
- A unique name, eg vnc.username, and vnc.x509dname.
- A default policy, allow or deny
- An ordered series of match rules, with allow or deny policy
If none of the match rules apply, then the default policy is
used.
There is a monitor API to manipulate the ACLs, which I'll describe via
examples
(qemu) acl show vnc.username
policy: allow
(qemu) acl policy vnc.username denya
acl: policy set to 'deny'
(qemu) acl allow vnc.username fred
acl: added rule at position 1
(qemu) acl allow vnc.username bob
acl: added rule at position 2
(qemu) acl allow vnc.username joe 1
acl: added rule at position 1
(qemu) acl show vnc.username
policy: deny
0: allow fred
1: allow joe
2: allow bob
(qemu) acl show vnc.x509dname
policy: allow
(qemu) acl policy vnc.x509dname deny
acl: policy set to 'deny'
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=London,CN=*
acl: added rule at position 1
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=Boston,CN=bob
acl: added rule at position 2
(qemu) acl show vnc.x509dname
policy: deny
0: allow C=GB,O=ACME,L=London,CN=*
1: allow C=GB,O=ACME,L=Boston,CN=bob
By default the VNC server will not use any ACLs, allowing access to
the server if the user successfully authenticates. To enable use of
ACLs to restrict user access, the ',acl' flag should be given when
starting QEMU. The initial ACL activated will be a 'deny all' policy
and should be customized using monitor commands.
eg enable SASL auth and ACLs
qemu .... -vnc localhost:1,sasl,acl
The next patch will provide a way to load a pre-defined ACL when
starting up
Makefile | 6 +
b/acl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
b/acl.h | 74 ++++++++++++++++++++++
configure | 18 +++++
monitor.c | 95 ++++++++++++++++++++++++++++
qemu-doc.texi | 49 ++++++++++++++
vnc-auth-sasl.c | 16 +++-
vnc-auth-sasl.h | 7 ++
vnc-tls.c | 19 +++++
vnc-tls.h | 3
vnc.c | 21 ++++++
vnc.h | 3
12 files changed, 491 insertions(+), 5 deletions(-)
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6726 c046a42c-6fe2-441c-8c8c-71466251a162
2009-03-06 23:27:37 +03:00
|
|
|
{
|
|
|
|
qemu_acl *acl;
|
|
|
|
|
|
|
|
acl = qemu_acl_find(aclname);
|
|
|
|
if (!acl) {
|
2009-03-06 23:27:40 +03:00
|
|
|
monitor_printf(mon, "acl: unknown list '%s'\n", aclname);
|
|
|
|
return;
|
Support ACLs for controlling VNC access ("Daniel P. Berrange")
This patch introduces a generic internal API for access control lists
to be used by network servers in QEMU. It adds support for checking
these ACL in the VNC server, in two places. The first ACL is for the
SASL authentication mechanism, checking the SASL username. This ACL
is called 'vnc.username'. The second is for the TLS authentication
mechanism, when x509 client certificates are turned on, checking against
the Distinguished Name of the client. This ACL is called 'vnc.x509dname'
The internal API provides for an ACL with the following characteristics
- A unique name, eg vnc.username, and vnc.x509dname.
- A default policy, allow or deny
- An ordered series of match rules, with allow or deny policy
If none of the match rules apply, then the default policy is
used.
There is a monitor API to manipulate the ACLs, which I'll describe via
examples
(qemu) acl show vnc.username
policy: allow
(qemu) acl policy vnc.username denya
acl: policy set to 'deny'
(qemu) acl allow vnc.username fred
acl: added rule at position 1
(qemu) acl allow vnc.username bob
acl: added rule at position 2
(qemu) acl allow vnc.username joe 1
acl: added rule at position 1
(qemu) acl show vnc.username
policy: deny
0: allow fred
1: allow joe
2: allow bob
(qemu) acl show vnc.x509dname
policy: allow
(qemu) acl policy vnc.x509dname deny
acl: policy set to 'deny'
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=London,CN=*
acl: added rule at position 1
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=Boston,CN=bob
acl: added rule at position 2
(qemu) acl show vnc.x509dname
policy: deny
0: allow C=GB,O=ACME,L=London,CN=*
1: allow C=GB,O=ACME,L=Boston,CN=bob
By default the VNC server will not use any ACLs, allowing access to
the server if the user successfully authenticates. To enable use of
ACLs to restrict user access, the ',acl' flag should be given when
starting QEMU. The initial ACL activated will be a 'deny all' policy
and should be customized using monitor commands.
eg enable SASL auth and ACLs
qemu .... -vnc localhost:1,sasl,acl
The next patch will provide a way to load a pre-defined ACL when
starting up
Makefile | 6 +
b/acl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
b/acl.h | 74 ++++++++++++++++++++++
configure | 18 +++++
monitor.c | 95 ++++++++++++++++++++++++++++
qemu-doc.texi | 49 ++++++++++++++
vnc-auth-sasl.c | 16 +++-
vnc-auth-sasl.h | 7 ++
vnc-tls.c | 19 +++++
vnc-tls.h | 3
vnc.c | 21 ++++++
vnc.h | 3
12 files changed, 491 insertions(+), 5 deletions(-)
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6726 c046a42c-6fe2-441c-8c8c-71466251a162
2009-03-06 23:27:37 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(command, "show") == 0) {
|
2009-03-06 23:27:40 +03:00
|
|
|
int i = 0;
|
|
|
|
qemu_acl_entry *entry;
|
|
|
|
monitor_printf(mon, "policy: %s\n",
|
Support ACLs for controlling VNC access ("Daniel P. Berrange")
This patch introduces a generic internal API for access control lists
to be used by network servers in QEMU. It adds support for checking
these ACL in the VNC server, in two places. The first ACL is for the
SASL authentication mechanism, checking the SASL username. This ACL
is called 'vnc.username'. The second is for the TLS authentication
mechanism, when x509 client certificates are turned on, checking against
the Distinguished Name of the client. This ACL is called 'vnc.x509dname'
The internal API provides for an ACL with the following characteristics
- A unique name, eg vnc.username, and vnc.x509dname.
- A default policy, allow or deny
- An ordered series of match rules, with allow or deny policy
If none of the match rules apply, then the default policy is
used.
There is a monitor API to manipulate the ACLs, which I'll describe via
examples
(qemu) acl show vnc.username
policy: allow
(qemu) acl policy vnc.username denya
acl: policy set to 'deny'
(qemu) acl allow vnc.username fred
acl: added rule at position 1
(qemu) acl allow vnc.username bob
acl: added rule at position 2
(qemu) acl allow vnc.username joe 1
acl: added rule at position 1
(qemu) acl show vnc.username
policy: deny
0: allow fred
1: allow joe
2: allow bob
(qemu) acl show vnc.x509dname
policy: allow
(qemu) acl policy vnc.x509dname deny
acl: policy set to 'deny'
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=London,CN=*
acl: added rule at position 1
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=Boston,CN=bob
acl: added rule at position 2
(qemu) acl show vnc.x509dname
policy: deny
0: allow C=GB,O=ACME,L=London,CN=*
1: allow C=GB,O=ACME,L=Boston,CN=bob
By default the VNC server will not use any ACLs, allowing access to
the server if the user successfully authenticates. To enable use of
ACLs to restrict user access, the ',acl' flag should be given when
starting QEMU. The initial ACL activated will be a 'deny all' policy
and should be customized using monitor commands.
eg enable SASL auth and ACLs
qemu .... -vnc localhost:1,sasl,acl
The next patch will provide a way to load a pre-defined ACL when
starting up
Makefile | 6 +
b/acl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
b/acl.h | 74 ++++++++++++++++++++++
configure | 18 +++++
monitor.c | 95 ++++++++++++++++++++++++++++
qemu-doc.texi | 49 ++++++++++++++
vnc-auth-sasl.c | 16 +++-
vnc-auth-sasl.h | 7 ++
vnc-tls.c | 19 +++++
vnc-tls.h | 3
vnc.c | 21 ++++++
vnc.h | 3
12 files changed, 491 insertions(+), 5 deletions(-)
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6726 c046a42c-6fe2-441c-8c8c-71466251a162
2009-03-06 23:27:37 +03:00
|
|
|
acl->defaultDeny ? "deny" : "allow");
|
2009-03-06 23:27:40 +03:00
|
|
|
TAILQ_FOREACH(entry, &acl->entries, next) {
|
|
|
|
i++;
|
|
|
|
monitor_printf(mon, "%d: %s %s\n", i,
|
Support ACLs for controlling VNC access ("Daniel P. Berrange")
This patch introduces a generic internal API for access control lists
to be used by network servers in QEMU. It adds support for checking
these ACL in the VNC server, in two places. The first ACL is for the
SASL authentication mechanism, checking the SASL username. This ACL
is called 'vnc.username'. The second is for the TLS authentication
mechanism, when x509 client certificates are turned on, checking against
the Distinguished Name of the client. This ACL is called 'vnc.x509dname'
The internal API provides for an ACL with the following characteristics
- A unique name, eg vnc.username, and vnc.x509dname.
- A default policy, allow or deny
- An ordered series of match rules, with allow or deny policy
If none of the match rules apply, then the default policy is
used.
There is a monitor API to manipulate the ACLs, which I'll describe via
examples
(qemu) acl show vnc.username
policy: allow
(qemu) acl policy vnc.username denya
acl: policy set to 'deny'
(qemu) acl allow vnc.username fred
acl: added rule at position 1
(qemu) acl allow vnc.username bob
acl: added rule at position 2
(qemu) acl allow vnc.username joe 1
acl: added rule at position 1
(qemu) acl show vnc.username
policy: deny
0: allow fred
1: allow joe
2: allow bob
(qemu) acl show vnc.x509dname
policy: allow
(qemu) acl policy vnc.x509dname deny
acl: policy set to 'deny'
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=London,CN=*
acl: added rule at position 1
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=Boston,CN=bob
acl: added rule at position 2
(qemu) acl show vnc.x509dname
policy: deny
0: allow C=GB,O=ACME,L=London,CN=*
1: allow C=GB,O=ACME,L=Boston,CN=bob
By default the VNC server will not use any ACLs, allowing access to
the server if the user successfully authenticates. To enable use of
ACLs to restrict user access, the ',acl' flag should be given when
starting QEMU. The initial ACL activated will be a 'deny all' policy
and should be customized using monitor commands.
eg enable SASL auth and ACLs
qemu .... -vnc localhost:1,sasl,acl
The next patch will provide a way to load a pre-defined ACL when
starting up
Makefile | 6 +
b/acl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
b/acl.h | 74 ++++++++++++++++++++++
configure | 18 +++++
monitor.c | 95 ++++++++++++++++++++++++++++
qemu-doc.texi | 49 ++++++++++++++
vnc-auth-sasl.c | 16 +++-
vnc-auth-sasl.h | 7 ++
vnc-tls.c | 19 +++++
vnc-tls.h | 3
vnc.c | 21 ++++++
vnc.h | 3
12 files changed, 491 insertions(+), 5 deletions(-)
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6726 c046a42c-6fe2-441c-8c8c-71466251a162
2009-03-06 23:27:37 +03:00
|
|
|
entry->deny ? "deny" : "allow",
|
|
|
|
entry->match);
|
2009-03-06 23:27:40 +03: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-06 23:27:37 +03:00
|
|
|
} else if (strcmp(command, "reset") == 0) {
|
2009-03-06 23:27:40 +03:00
|
|
|
qemu_acl_reset(acl);
|
|
|
|
monitor_printf(mon, "acl: removed all rules\n");
|
Support ACLs for controlling VNC access ("Daniel P. Berrange")
This patch introduces a generic internal API for access control lists
to be used by network servers in QEMU. It adds support for checking
these ACL in the VNC server, in two places. The first ACL is for the
SASL authentication mechanism, checking the SASL username. This ACL
is called 'vnc.username'. The second is for the TLS authentication
mechanism, when x509 client certificates are turned on, checking against
the Distinguished Name of the client. This ACL is called 'vnc.x509dname'
The internal API provides for an ACL with the following characteristics
- A unique name, eg vnc.username, and vnc.x509dname.
- A default policy, allow or deny
- An ordered series of match rules, with allow or deny policy
If none of the match rules apply, then the default policy is
used.
There is a monitor API to manipulate the ACLs, which I'll describe via
examples
(qemu) acl show vnc.username
policy: allow
(qemu) acl policy vnc.username denya
acl: policy set to 'deny'
(qemu) acl allow vnc.username fred
acl: added rule at position 1
(qemu) acl allow vnc.username bob
acl: added rule at position 2
(qemu) acl allow vnc.username joe 1
acl: added rule at position 1
(qemu) acl show vnc.username
policy: deny
0: allow fred
1: allow joe
2: allow bob
(qemu) acl show vnc.x509dname
policy: allow
(qemu) acl policy vnc.x509dname deny
acl: policy set to 'deny'
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=London,CN=*
acl: added rule at position 1
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=Boston,CN=bob
acl: added rule at position 2
(qemu) acl show vnc.x509dname
policy: deny
0: allow C=GB,O=ACME,L=London,CN=*
1: allow C=GB,O=ACME,L=Boston,CN=bob
By default the VNC server will not use any ACLs, allowing access to
the server if the user successfully authenticates. To enable use of
ACLs to restrict user access, the ',acl' flag should be given when
starting QEMU. The initial ACL activated will be a 'deny all' policy
and should be customized using monitor commands.
eg enable SASL auth and ACLs
qemu .... -vnc localhost:1,sasl,acl
The next patch will provide a way to load a pre-defined ACL when
starting up
Makefile | 6 +
b/acl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
b/acl.h | 74 ++++++++++++++++++++++
configure | 18 +++++
monitor.c | 95 ++++++++++++++++++++++++++++
qemu-doc.texi | 49 ++++++++++++++
vnc-auth-sasl.c | 16 +++-
vnc-auth-sasl.h | 7 ++
vnc-tls.c | 19 +++++
vnc-tls.h | 3
vnc.c | 21 ++++++
vnc.h | 3
12 files changed, 491 insertions(+), 5 deletions(-)
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6726 c046a42c-6fe2-441c-8c8c-71466251a162
2009-03-06 23:27:37 +03:00
|
|
|
} else if (strcmp(command, "policy") == 0) {
|
2009-03-06 23:27:40 +03:00
|
|
|
if (!match) {
|
|
|
|
monitor_printf(mon, "acl: missing policy parameter\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(match, "allow") == 0) {
|
|
|
|
acl->defaultDeny = 0;
|
|
|
|
monitor_printf(mon, "acl: policy set to 'allow'\n");
|
|
|
|
} else if (strcmp(match, "deny") == 0) {
|
|
|
|
acl->defaultDeny = 1;
|
|
|
|
monitor_printf(mon, "acl: policy set to 'deny'\n");
|
|
|
|
} else {
|
|
|
|
monitor_printf(mon, "acl: unknown policy '%s', expected 'deny' or 'allow'\n", match);
|
|
|
|
}
|
Support ACLs for controlling VNC access ("Daniel P. Berrange")
This patch introduces a generic internal API for access control lists
to be used by network servers in QEMU. It adds support for checking
these ACL in the VNC server, in two places. The first ACL is for the
SASL authentication mechanism, checking the SASL username. This ACL
is called 'vnc.username'. The second is for the TLS authentication
mechanism, when x509 client certificates are turned on, checking against
the Distinguished Name of the client. This ACL is called 'vnc.x509dname'
The internal API provides for an ACL with the following characteristics
- A unique name, eg vnc.username, and vnc.x509dname.
- A default policy, allow or deny
- An ordered series of match rules, with allow or deny policy
If none of the match rules apply, then the default policy is
used.
There is a monitor API to manipulate the ACLs, which I'll describe via
examples
(qemu) acl show vnc.username
policy: allow
(qemu) acl policy vnc.username denya
acl: policy set to 'deny'
(qemu) acl allow vnc.username fred
acl: added rule at position 1
(qemu) acl allow vnc.username bob
acl: added rule at position 2
(qemu) acl allow vnc.username joe 1
acl: added rule at position 1
(qemu) acl show vnc.username
policy: deny
0: allow fred
1: allow joe
2: allow bob
(qemu) acl show vnc.x509dname
policy: allow
(qemu) acl policy vnc.x509dname deny
acl: policy set to 'deny'
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=London,CN=*
acl: added rule at position 1
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=Boston,CN=bob
acl: added rule at position 2
(qemu) acl show vnc.x509dname
policy: deny
0: allow C=GB,O=ACME,L=London,CN=*
1: allow C=GB,O=ACME,L=Boston,CN=bob
By default the VNC server will not use any ACLs, allowing access to
the server if the user successfully authenticates. To enable use of
ACLs to restrict user access, the ',acl' flag should be given when
starting QEMU. The initial ACL activated will be a 'deny all' policy
and should be customized using monitor commands.
eg enable SASL auth and ACLs
qemu .... -vnc localhost:1,sasl,acl
The next patch will provide a way to load a pre-defined ACL when
starting up
Makefile | 6 +
b/acl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
b/acl.h | 74 ++++++++++++++++++++++
configure | 18 +++++
monitor.c | 95 ++++++++++++++++++++++++++++
qemu-doc.texi | 49 ++++++++++++++
vnc-auth-sasl.c | 16 +++-
vnc-auth-sasl.h | 7 ++
vnc-tls.c | 19 +++++
vnc-tls.h | 3
vnc.c | 21 ++++++
vnc.h | 3
12 files changed, 491 insertions(+), 5 deletions(-)
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6726 c046a42c-6fe2-441c-8c8c-71466251a162
2009-03-06 23:27:37 +03:00
|
|
|
} else if ((strcmp(command, "allow") == 0) ||
|
2009-03-06 23:27:40 +03:00
|
|
|
(strcmp(command, "deny") == 0)) {
|
|
|
|
int deny = strcmp(command, "deny") == 0 ? 1 : 0;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!match) {
|
|
|
|
monitor_printf(mon, "acl: missing match parameter\n");
|
|
|
|
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);
|
Support ACLs for controlling VNC access ("Daniel P. Berrange")
This patch introduces a generic internal API for access control lists
to be used by network servers in QEMU. It adds support for checking
these ACL in the VNC server, in two places. The first ACL is for the
SASL authentication mechanism, checking the SASL username. This ACL
is called 'vnc.username'. The second is for the TLS authentication
mechanism, when x509 client certificates are turned on, checking against
the Distinguished Name of the client. This ACL is called 'vnc.x509dname'
The internal API provides for an ACL with the following characteristics
- A unique name, eg vnc.username, and vnc.x509dname.
- A default policy, allow or deny
- An ordered series of match rules, with allow or deny policy
If none of the match rules apply, then the default policy is
used.
There is a monitor API to manipulate the ACLs, which I'll describe via
examples
(qemu) acl show vnc.username
policy: allow
(qemu) acl policy vnc.username denya
acl: policy set to 'deny'
(qemu) acl allow vnc.username fred
acl: added rule at position 1
(qemu) acl allow vnc.username bob
acl: added rule at position 2
(qemu) acl allow vnc.username joe 1
acl: added rule at position 1
(qemu) acl show vnc.username
policy: deny
0: allow fred
1: allow joe
2: allow bob
(qemu) acl show vnc.x509dname
policy: allow
(qemu) acl policy vnc.x509dname deny
acl: policy set to 'deny'
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=London,CN=*
acl: added rule at position 1
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=Boston,CN=bob
acl: added rule at position 2
(qemu) acl show vnc.x509dname
policy: deny
0: allow C=GB,O=ACME,L=London,CN=*
1: allow C=GB,O=ACME,L=Boston,CN=bob
By default the VNC server will not use any ACLs, allowing access to
the server if the user successfully authenticates. To enable use of
ACLs to restrict user access, the ',acl' flag should be given when
starting QEMU. The initial ACL activated will be a 'deny all' policy
and should be customized using monitor commands.
eg enable SASL auth and ACLs
qemu .... -vnc localhost:1,sasl,acl
The next patch will provide a way to load a pre-defined ACL when
starting up
Makefile | 6 +
b/acl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
b/acl.h | 74 ++++++++++++++++++++++
configure | 18 +++++
monitor.c | 95 ++++++++++++++++++++++++++++
qemu-doc.texi | 49 ++++++++++++++
vnc-auth-sasl.c | 16 +++-
vnc-auth-sasl.h | 7 ++
vnc-tls.c | 19 +++++
vnc-tls.h | 3
vnc.c | 21 ++++++
vnc.h | 3
12 files changed, 491 insertions(+), 5 deletions(-)
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6726 c046a42c-6fe2-441c-8c8c-71466251a162
2009-03-06 23:27:37 +03:00
|
|
|
} else if (strcmp(command, "remove") == 0) {
|
2009-03-06 23:27:40 +03:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!match) {
|
|
|
|
monitor_printf(mon, "acl: missing match parameter\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = qemu_acl_remove(acl, match);
|
|
|
|
if (ret < 0)
|
|
|
|
monitor_printf(mon, "acl: no matching acl entry\n");
|
|
|
|
else
|
|
|
|
monitor_printf(mon, "acl: removed rule at position %d\n", ret);
|
Support ACLs for controlling VNC access ("Daniel P. Berrange")
This patch introduces a generic internal API for access control lists
to be used by network servers in QEMU. It adds support for checking
these ACL in the VNC server, in two places. The first ACL is for the
SASL authentication mechanism, checking the SASL username. This ACL
is called 'vnc.username'. The second is for the TLS authentication
mechanism, when x509 client certificates are turned on, checking against
the Distinguished Name of the client. This ACL is called 'vnc.x509dname'
The internal API provides for an ACL with the following characteristics
- A unique name, eg vnc.username, and vnc.x509dname.
- A default policy, allow or deny
- An ordered series of match rules, with allow or deny policy
If none of the match rules apply, then the default policy is
used.
There is a monitor API to manipulate the ACLs, which I'll describe via
examples
(qemu) acl show vnc.username
policy: allow
(qemu) acl policy vnc.username denya
acl: policy set to 'deny'
(qemu) acl allow vnc.username fred
acl: added rule at position 1
(qemu) acl allow vnc.username bob
acl: added rule at position 2
(qemu) acl allow vnc.username joe 1
acl: added rule at position 1
(qemu) acl show vnc.username
policy: deny
0: allow fred
1: allow joe
2: allow bob
(qemu) acl show vnc.x509dname
policy: allow
(qemu) acl policy vnc.x509dname deny
acl: policy set to 'deny'
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=London,CN=*
acl: added rule at position 1
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=Boston,CN=bob
acl: added rule at position 2
(qemu) acl show vnc.x509dname
policy: deny
0: allow C=GB,O=ACME,L=London,CN=*
1: allow C=GB,O=ACME,L=Boston,CN=bob
By default the VNC server will not use any ACLs, allowing access to
the server if the user successfully authenticates. To enable use of
ACLs to restrict user access, the ',acl' flag should be given when
starting QEMU. The initial ACL activated will be a 'deny all' policy
and should be customized using monitor commands.
eg enable SASL auth and ACLs
qemu .... -vnc localhost:1,sasl,acl
The next patch will provide a way to load a pre-defined ACL when
starting up
Makefile | 6 +
b/acl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
b/acl.h | 74 ++++++++++++++++++++++
configure | 18 +++++
monitor.c | 95 ++++++++++++++++++++++++++++
qemu-doc.texi | 49 ++++++++++++++
vnc-auth-sasl.c | 16 +++-
vnc-auth-sasl.h | 7 ++
vnc-tls.c | 19 +++++
vnc-tls.h | 3
vnc.c | 21 ++++++
vnc.h | 3
12 files changed, 491 insertions(+), 5 deletions(-)
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6726 c046a42c-6fe2-441c-8c8c-71466251a162
2009-03-06 23:27:37 +03:00
|
|
|
} else {
|
2009-03-06 23:27:40 +03:00
|
|
|
monitor_printf(mon, "acl: unknown command '%s'\n", command);
|
Support ACLs for controlling VNC access ("Daniel P. Berrange")
This patch introduces a generic internal API for access control lists
to be used by network servers in QEMU. It adds support for checking
these ACL in the VNC server, in two places. The first ACL is for the
SASL authentication mechanism, checking the SASL username. This ACL
is called 'vnc.username'. The second is for the TLS authentication
mechanism, when x509 client certificates are turned on, checking against
the Distinguished Name of the client. This ACL is called 'vnc.x509dname'
The internal API provides for an ACL with the following characteristics
- A unique name, eg vnc.username, and vnc.x509dname.
- A default policy, allow or deny
- An ordered series of match rules, with allow or deny policy
If none of the match rules apply, then the default policy is
used.
There is a monitor API to manipulate the ACLs, which I'll describe via
examples
(qemu) acl show vnc.username
policy: allow
(qemu) acl policy vnc.username denya
acl: policy set to 'deny'
(qemu) acl allow vnc.username fred
acl: added rule at position 1
(qemu) acl allow vnc.username bob
acl: added rule at position 2
(qemu) acl allow vnc.username joe 1
acl: added rule at position 1
(qemu) acl show vnc.username
policy: deny
0: allow fred
1: allow joe
2: allow bob
(qemu) acl show vnc.x509dname
policy: allow
(qemu) acl policy vnc.x509dname deny
acl: policy set to 'deny'
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=London,CN=*
acl: added rule at position 1
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=Boston,CN=bob
acl: added rule at position 2
(qemu) acl show vnc.x509dname
policy: deny
0: allow C=GB,O=ACME,L=London,CN=*
1: allow C=GB,O=ACME,L=Boston,CN=bob
By default the VNC server will not use any ACLs, allowing access to
the server if the user successfully authenticates. To enable use of
ACLs to restrict user access, the ',acl' flag should be given when
starting QEMU. The initial ACL activated will be a 'deny all' policy
and should be customized using monitor commands.
eg enable SASL auth and ACLs
qemu .... -vnc localhost:1,sasl,acl
The next patch will provide a way to load a pre-defined ACL when
starting up
Makefile | 6 +
b/acl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
b/acl.h | 74 ++++++++++++++++++++++
configure | 18 +++++
monitor.c | 95 ++++++++++++++++++++++++++++
qemu-doc.texi | 49 ++++++++++++++
vnc-auth-sasl.c | 16 +++-
vnc-auth-sasl.h | 7 ++
vnc-tls.c | 19 +++++
vnc-tls.h | 3
vnc.c | 21 ++++++
vnc.h | 3
12 files changed, 491 insertions(+), 5 deletions(-)
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6726 c046a42c-6fe2-441c-8c8c-71466251a162
2009-03-06 23:27:37 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-24 21:19:25 +03:00
|
|
|
/* Please update qemu-doc.texi when adding or changing commands */
|
2009-03-06 02:01:23 +03:00
|
|
|
static const mon_cmd_t mon_cmds[] = {
|
|
|
|
{ "help|?", "s?", help_cmd,
|
2004-03-15 00:38:27 +03:00
|
|
|
"[cmd]", "show the help" },
|
2007-09-17 01:08:06 +04:00
|
|
|
{ "commit", "s", do_commit,
|
2006-08-01 19:52:40 +04:00
|
|
|
"device|all", "commit changes to the disk images (if -snapshot is used) or backing files" },
|
2004-04-04 16:57:25 +04:00
|
|
|
{ "info", "s?", do_info,
|
2004-03-15 00:38:27 +03:00
|
|
|
"subcommand", "show various information about the system state" },
|
2004-04-04 16:57:25 +04:00
|
|
|
{ "q|quit", "", do_quit,
|
2004-03-15 00:38:27 +03:00
|
|
|
"", "quit the emulator" },
|
2004-07-14 21:21:37 +04:00
|
|
|
{ "eject", "-fB", do_eject,
|
2007-03-30 22:58:01 +04:00
|
|
|
"[-f] device", "eject a removable medium (use -f to force it)" },
|
2008-06-19 02:10:01 +04:00
|
|
|
{ "change", "BFs?", do_change,
|
|
|
|
"device filename [format]", "change a removable medium, optional format" },
|
2007-09-17 01:08:06 +04:00
|
|
|
{ "screendump", "F", do_screen_dump,
|
2004-03-18 02:17:16 +03:00
|
|
|
"filename", "save screen into PPM image 'filename'" },
|
2008-05-25 03:15:46 +04:00
|
|
|
{ "logfile", "F", do_logfile,
|
2007-06-30 17:53:24 +04:00
|
|
|
"filename", "output logs to 'filename'" },
|
2004-04-04 16:57:25 +04:00
|
|
|
{ "log", "s", do_log,
|
2007-09-17 01:08:06 +04:00
|
|
|
"item1[,...]", "activate logging of the specified items to '/tmp/qemu.log'" },
|
2006-08-06 01:31:00 +04:00
|
|
|
{ "savevm", "s?", do_savevm,
|
2007-09-17 01:08:06 +04:00
|
|
|
"tag|id", "save a VM snapshot. If no tag or id are provided, a new snapshot is created" },
|
2006-08-06 01:31:00 +04:00
|
|
|
{ "loadvm", "s", do_loadvm,
|
2007-09-17 01:08:06 +04:00
|
|
|
"tag|id", "restore a VM snapshot from its tag or id" },
|
2006-08-06 01:31:00 +04:00
|
|
|
{ "delvm", "s", do_delvm,
|
2007-09-17 01:08:06 +04:00
|
|
|
"tag|id", "delete a VM snapshot from its tag or id" },
|
2009-04-06 00:08:59 +04:00
|
|
|
{ "singlestep", "s?", do_singlestep,
|
|
|
|
"[on|off]", "run emulation in singlestep mode or switch to normal mode", },
|
2007-09-17 01:08:06 +04:00
|
|
|
{ "stop", "", do_stop,
|
2004-04-04 16:57:25 +04:00
|
|
|
"", "stop emulation", },
|
2007-09-17 01:08:06 +04:00
|
|
|
{ "c|cont", "", do_cont,
|
2004-04-04 16:57:25 +04:00
|
|
|
"", "resume emulation", },
|
2004-04-01 03:37:16 +04:00
|
|
|
#ifdef CONFIG_GDBSTUB
|
2007-09-17 01:08:06 +04:00
|
|
|
{ "gdbserver", "s?", do_gdbserver,
|
2004-04-04 16:57:25 +04:00
|
|
|
"[port]", "start gdbserver session (default port=1234)", },
|
2004-04-01 03:37:16 +04:00
|
|
|
#endif
|
2007-09-17 01:08:06 +04:00
|
|
|
{ "x", "/l", do_memory_dump,
|
2004-04-04 16:57:25 +04:00
|
|
|
"/fmt addr", "virtual memory dump starting at 'addr'", },
|
2007-09-17 01:08:06 +04:00
|
|
|
{ "xp", "/l", do_physical_memory_dump,
|
2004-04-04 16:57:25 +04:00
|
|
|
"/fmt addr", "physical memory dump starting at 'addr'", },
|
2007-09-17 01:08:06 +04:00
|
|
|
{ "p|print", "/l", do_print,
|
2004-04-04 16:57:25 +04:00
|
|
|
"/fmt expr", "print expression value (use $reg for CPU register access)", },
|
2007-09-17 01:08:06 +04:00
|
|
|
{ "i", "/ii.", do_ioport_read,
|
2004-06-08 04:55:58 +04:00
|
|
|
"/fmt addr", "I/O port read" },
|
|
|
|
|
2008-06-09 02:45:01 +04:00
|
|
|
{ "sendkey", "si?", do_sendkey,
|
|
|
|
"keys [hold_ms]", "send keys to the VM (e.g. 'sendkey ctrl-alt-f1', default hold time=100 ms)" },
|
2007-09-17 01:08:06 +04:00
|
|
|
{ "system_reset", "", do_system_reset,
|
2004-06-20 16:35:44 +04:00
|
|
|
"", "reset the system" },
|
2007-09-17 01:08:06 +04:00
|
|
|
{ "system_powerdown", "", do_system_powerdown,
|
2005-07-02 18:31:34 +04:00
|
|
|
"", "send system power down event" },
|
2007-09-17 01:08:06 +04:00
|
|
|
{ "sum", "ii", do_sum,
|
2005-06-05 00:15:57 +04:00
|
|
|
"addr size", "compute the checksum of a memory region" },
|
2005-11-06 19:13:29 +03:00
|
|
|
{ "usb_add", "s", do_usb_add,
|
|
|
|
"device", "add USB device (e.g. 'host:bus.addr' or 'host:vendor_id:product_id')" },
|
|
|
|
{ "usb_del", "s", do_usb_del,
|
|
|
|
"device", "remove USB device 'bus.addr'" },
|
2007-09-17 01:08:06 +04:00
|
|
|
{ "cpu", "i", do_cpu_set,
|
2005-11-22 02:25:50 +03:00
|
|
|
"index", "set the default CPU" },
|
2007-09-17 01:08:06 +04:00
|
|
|
{ "mouse_move", "sss?", do_mouse_move,
|
2006-07-15 02:03:35 +04:00
|
|
|
"dx dy [dz]", "send mouse move events" },
|
2007-09-17 01:08:06 +04:00
|
|
|
{ "mouse_button", "i", do_mouse_button,
|
2006-07-15 02:03:35 +04:00
|
|
|
"state", "change mouse button state (1=L, 2=M, 4=R)" },
|
2007-01-05 19:42:13 +03:00
|
|
|
{ "mouse_set", "i", do_mouse_set,
|
|
|
|
"index", "set which mouse device receives events" },
|
2006-07-16 22:57:03 +04:00
|
|
|
#ifdef HAS_AUDIO
|
|
|
|
{ "wavcapture", "si?i?i?", do_wav_capture,
|
|
|
|
"path [frequency bits channels]",
|
|
|
|
"capture audio to a wave file (default frequency=44100 bits=16 channels=2)" },
|
|
|
|
#endif
|
2009-01-24 21:19:25 +03:00
|
|
|
{ "stopcapture", "i", do_stop_capture,
|
|
|
|
"capture index", "stop capture" },
|
2007-09-17 01:08:06 +04:00
|
|
|
{ "memsave", "lis", do_memory_save,
|
2007-01-03 18:20:39 +03:00
|
|
|
"addr size file", "save to disk virtual memory dump starting at 'addr' of size 'size'", },
|
2008-04-12 01:36:14 +04:00
|
|
|
{ "pmemsave", "lis", do_physical_memory_save,
|
|
|
|
"addr size file", "save to disk physical memory dump starting at 'addr' of size 'size'", },
|
2008-05-05 00:11:34 +04:00
|
|
|
{ "boot_set", "s", do_boot_set,
|
|
|
|
"bootdevice", "define new values for the boot device list" },
|
2008-04-28 03:52:12 +04:00
|
|
|
#if defined(TARGET_I386)
|
|
|
|
{ "nmi", "i", do_inject_nmi,
|
|
|
|
"cpu", "inject an NMI on the given CPU", },
|
|
|
|
#endif
|
2008-10-13 07:12:02 +04:00
|
|
|
{ "migrate", "-ds", do_migrate,
|
|
|
|
"[-d] uri", "migrate to URI (using -d to not wait for completion)" },
|
|
|
|
{ "migrate_cancel", "", do_migrate_cancel,
|
|
|
|
"", "cancel the current VM migration" },
|
|
|
|
{ "migrate_set_speed", "s", do_migrate_set_speed,
|
|
|
|
"value", "set maximum speed (in bytes) for migrations" },
|
2009-02-11 18:21:54 +03:00
|
|
|
#if defined(TARGET_I386)
|
|
|
|
{ "drive_add", "ss", drive_hot_add, "pci_addr=[[<domain>:]<bus>:]<slot>\n"
|
|
|
|
"[file=file][,if=type][,bus=n]\n"
|
|
|
|
"[,unit=m][,media=d][index=i]\n"
|
|
|
|
"[,cyls=c,heads=h,secs=s[,trans=t]]\n"
|
|
|
|
"[snapshot=on|off][,cache=on|off]",
|
|
|
|
"add drive to PCI storage controller" },
|
|
|
|
{ "pci_add", "sss", pci_device_hot_add, "pci_addr=auto|[[<domain>:]<bus>:]<slot> nic|storage [[vlan=n][,macaddr=addr][,model=type]] [file=file][,if=type][,bus=nr]...", "hot-add PCI device" },
|
|
|
|
{ "pci_del", "s", pci_device_hot_remove, "pci_addr=[[<domain>:]<bus>:]<slot>", "hot remove PCI device" },
|
|
|
|
{ "host_net_add", "ss", net_host_device_add,
|
|
|
|
"[tap,user,socket,vde] options", "add host VLAN client" },
|
|
|
|
{ "host_net_remove", "is", net_host_device_remove,
|
|
|
|
"vlan_id name", "remove host VLAN client" },
|
|
|
|
#endif
|
2008-12-04 23:19:35 +03:00
|
|
|
{ "balloon", "i", do_balloon,
|
|
|
|
"target", "request VM to change it's memory allocation (in MB)" },
|
2009-02-11 18:19:16 +03:00
|
|
|
{ "set_link", "ss", do_set_link,
|
|
|
|
"name [up|down]", "change the link status of a network adapter" },
|
Support ACLs for controlling VNC access ("Daniel P. Berrange")
This patch introduces a generic internal API for access control lists
to be used by network servers in QEMU. It adds support for checking
these ACL in the VNC server, in two places. The first ACL is for the
SASL authentication mechanism, checking the SASL username. This ACL
is called 'vnc.username'. The second is for the TLS authentication
mechanism, when x509 client certificates are turned on, checking against
the Distinguished Name of the client. This ACL is called 'vnc.x509dname'
The internal API provides for an ACL with the following characteristics
- A unique name, eg vnc.username, and vnc.x509dname.
- A default policy, allow or deny
- An ordered series of match rules, with allow or deny policy
If none of the match rules apply, then the default policy is
used.
There is a monitor API to manipulate the ACLs, which I'll describe via
examples
(qemu) acl show vnc.username
policy: allow
(qemu) acl policy vnc.username denya
acl: policy set to 'deny'
(qemu) acl allow vnc.username fred
acl: added rule at position 1
(qemu) acl allow vnc.username bob
acl: added rule at position 2
(qemu) acl allow vnc.username joe 1
acl: added rule at position 1
(qemu) acl show vnc.username
policy: deny
0: allow fred
1: allow joe
2: allow bob
(qemu) acl show vnc.x509dname
policy: allow
(qemu) acl policy vnc.x509dname deny
acl: policy set to 'deny'
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=London,CN=*
acl: added rule at position 1
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=Boston,CN=bob
acl: added rule at position 2
(qemu) acl show vnc.x509dname
policy: deny
0: allow C=GB,O=ACME,L=London,CN=*
1: allow C=GB,O=ACME,L=Boston,CN=bob
By default the VNC server will not use any ACLs, allowing access to
the server if the user successfully authenticates. To enable use of
ACLs to restrict user access, the ',acl' flag should be given when
starting QEMU. The initial ACL activated will be a 'deny all' policy
and should be customized using monitor commands.
eg enable SASL auth and ACLs
qemu .... -vnc localhost:1,sasl,acl
The next patch will provide a way to load a pre-defined ACL when
starting up
Makefile | 6 +
b/acl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
b/acl.h | 74 ++++++++++++++++++++++
configure | 18 +++++
monitor.c | 95 ++++++++++++++++++++++++++++
qemu-doc.texi | 49 ++++++++++++++
vnc-auth-sasl.c | 16 +++-
vnc-auth-sasl.h | 7 ++
vnc-tls.c | 19 +++++
vnc-tls.h | 3
vnc.c | 21 ++++++
vnc.h | 3
12 files changed, 491 insertions(+), 5 deletions(-)
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6726 c046a42c-6fe2-441c-8c8c-71466251a162
2009-03-06 23:27:37 +03:00
|
|
|
{ "acl", "sss?i?", do_acl, "<command> <aclname> [<match>] [<index>]\n",
|
|
|
|
"acl show vnc.username\n"
|
|
|
|
"acl policy vnc.username deny\n"
|
|
|
|
"acl allow vnc.username fred\n"
|
|
|
|
"acl deny vnc.username bob\n"
|
|
|
|
"acl reset vnc.username\n" },
|
2007-09-17 01:08:06 +04:00
|
|
|
{ NULL, NULL, },
|
2004-03-15 00:38:27 +03:00
|
|
|
};
|
|
|
|
|
2009-01-24 21:19:25 +03:00
|
|
|
/* Please update qemu-doc.texi when adding or changing commands */
|
2009-03-06 02:01:23 +03:00
|
|
|
static const mon_cmd_t info_cmds[] = {
|
2004-10-10 19:15:51 +04:00
|
|
|
{ "version", "", do_info_version,
|
2009-01-24 21:19:25 +03:00
|
|
|
"", "show the version of QEMU" },
|
2004-04-04 16:57:25 +04:00
|
|
|
{ "network", "", do_info_network,
|
2004-03-15 00:38:27 +03:00
|
|
|
"", "show the network state" },
|
2008-10-31 20:31:29 +03:00
|
|
|
{ "chardev", "", qemu_chr_info,
|
|
|
|
"", "show the character devices" },
|
2009-03-06 02:01:23 +03:00
|
|
|
{ "block", "", bdrv_info,
|
2004-03-15 00:38:27 +03:00
|
|
|
"", "show the block devices" },
|
2009-03-06 02:01:23 +03:00
|
|
|
{ "blockstats", "", bdrv_info_stats,
|
2007-12-02 08:18:19 +03:00
|
|
|
"", "show block device statistics" },
|
2004-04-04 16:57:25 +04:00
|
|
|
{ "registers", "", do_info_registers,
|
|
|
|
"", "show the cpu registers" },
|
2005-11-22 02:25:50 +03:00
|
|
|
{ "cpus", "", do_info_cpus,
|
|
|
|
"", "show infos for each CPU" },
|
2004-04-04 17:07:25 +04:00
|
|
|
{ "history", "", do_info_history,
|
|
|
|
"", "show the command line history", },
|
2004-05-21 15:39:07 +04:00
|
|
|
{ "irq", "", irq_info,
|
|
|
|
"", "show the interrupts statistics (if available)", },
|
2004-04-25 22:05:08 +04:00
|
|
|
{ "pic", "", pic_info,
|
|
|
|
"", "show i8259 (PIC) state", },
|
2004-05-20 16:49:21 +04:00
|
|
|
{ "pci", "", pci_info,
|
|
|
|
"", "show PCI info", },
|
2009-03-03 09:12:22 +03:00
|
|
|
#if defined(TARGET_I386) || defined(TARGET_SH4)
|
2004-09-18 23:32:46 +04:00
|
|
|
{ "tlb", "", tlb_info,
|
|
|
|
"", "show virtual to physical memory mappings", },
|
2009-03-03 09:12:22 +03:00
|
|
|
#endif
|
|
|
|
#if defined(TARGET_I386)
|
2004-09-18 23:32:46 +04:00
|
|
|
{ "mem", "", mem_info,
|
|
|
|
"", "show the active virtual memory mappings", },
|
2008-12-18 02:28:44 +03:00
|
|
|
{ "hpet", "", do_info_hpet,
|
|
|
|
"", "show state of HPET", },
|
2004-09-18 23:32:46 +04:00
|
|
|
#endif
|
2005-01-27 01:00:47 +03:00
|
|
|
{ "jit", "", do_info_jit,
|
|
|
|
"", "show dynamic compiler info", },
|
2005-07-24 22:10:56 +04:00
|
|
|
{ "kqemu", "", do_info_kqemu,
|
2009-01-24 21:19:25 +03:00
|
|
|
"", "show KQEMU information", },
|
2008-11-05 19:04:33 +03:00
|
|
|
{ "kvm", "", do_info_kvm,
|
2009-01-24 21:19:25 +03:00
|
|
|
"", "show KVM information", },
|
2005-11-06 19:13:29 +03:00
|
|
|
{ "usb", "", usb_info,
|
|
|
|
"", "show guest USB devices", },
|
|
|
|
{ "usbhost", "", usb_host_info,
|
|
|
|
"", "show host USB devices", },
|
2006-02-09 01:40:15 +03:00
|
|
|
{ "profile", "", do_info_profile,
|
|
|
|
"", "show profiling information", },
|
2006-07-16 22:57:03 +04:00
|
|
|
{ "capture", "", do_info_capture,
|
2006-09-26 01:33:49 +04:00
|
|
|
"", "show capture information" },
|
2006-08-06 01:31:00 +04:00
|
|
|
{ "snapshots", "", do_info_snapshots,
|
2006-09-26 01:33:49 +04:00
|
|
|
"", "show the currently saved VM snapshots" },
|
2008-12-19 01:43:56 +03:00
|
|
|
{ "status", "", do_info_status,
|
|
|
|
"", "show the current VM status (running|paused)" },
|
2007-04-30 04:51:09 +04:00
|
|
|
{ "pcmcia", "", pcmcia_info,
|
|
|
|
"", "show guest PCMCIA status" },
|
2007-01-05 19:42:13 +03:00
|
|
|
{ "mice", "", do_info_mice,
|
|
|
|
"", "show which guest mouse is receiving events" },
|
2007-02-05 23:20:30 +03:00
|
|
|
{ "vnc", "", do_info_vnc,
|
|
|
|
"", "show the vnc server status"},
|
2007-03-19 18:17:08 +03:00
|
|
|
{ "name", "", do_info_name,
|
|
|
|
"", "show the current VM name" },
|
2008-09-18 22:30:20 +04:00
|
|
|
{ "uuid", "", do_info_uuid,
|
|
|
|
"", "show the current VM UUID" },
|
2007-03-07 11:32:30 +03:00
|
|
|
#if defined(TARGET_PPC)
|
|
|
|
{ "cpustats", "", do_info_cpu_stats,
|
|
|
|
"", "show CPU statistics", },
|
|
|
|
#endif
|
2007-10-26 22:42:59 +04:00
|
|
|
#if defined(CONFIG_SLIRP)
|
|
|
|
{ "slirp", "", do_info_slirp,
|
|
|
|
"", "show SLIRP statistics", },
|
|
|
|
#endif
|
2008-10-13 07:12:02 +04:00
|
|
|
{ "migrate", "", do_info_migrate, "", "show migration status" },
|
2008-12-04 23:19:35 +03:00
|
|
|
{ "balloon", "", do_info_balloon,
|
|
|
|
"", "show balloon information" },
|
2004-03-15 00:38:27 +03:00
|
|
|
{ NULL, NULL, },
|
|
|
|
};
|
|
|
|
|
2004-04-04 16:57:25 +04:00
|
|
|
/*******************************************************************/
|
|
|
|
|
|
|
|
static const char *pch;
|
|
|
|
static jmp_buf expr_env;
|
|
|
|
|
2005-02-11 01:00:52 +03:00
|
|
|
#define MD_TLONG 0
|
|
|
|
#define MD_I32 1
|
|
|
|
|
2004-04-04 16:57:25 +04:00
|
|
|
typedef struct MonitorDef {
|
|
|
|
const char *name;
|
|
|
|
int offset;
|
2008-10-02 22:32:44 +04:00
|
|
|
target_long (*get_value)(const struct MonitorDef *md, int val);
|
2005-02-11 01:00:52 +03:00
|
|
|
int type;
|
2004-04-04 16:57:25 +04:00
|
|
|
} MonitorDef;
|
|
|
|
|
2004-04-25 22:54:52 +04:00
|
|
|
#if defined(TARGET_I386)
|
2008-10-02 22:32:44 +04:00
|
|
|
static target_long monitor_get_pc (const struct MonitorDef *md, int val)
|
2004-04-25 22:54:52 +04:00
|
|
|
{
|
2005-11-22 02:25:50 +03:00
|
|
|
CPUState *env = mon_get_cpu();
|
|
|
|
if (!env)
|
|
|
|
return 0;
|
|
|
|
return env->eip + env->segs[R_CS].base;
|
2004-04-25 22:54:52 +04:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2004-04-13 00:39:29 +04:00
|
|
|
#if defined(TARGET_PPC)
|
2008-10-02 22:32:44 +04:00
|
|
|
static target_long monitor_get_ccr (const struct MonitorDef *md, int val)
|
2004-04-13 00:39:29 +04:00
|
|
|
{
|
2005-11-22 02:25:50 +03:00
|
|
|
CPUState *env = mon_get_cpu();
|
2004-04-13 00:39:29 +04:00
|
|
|
unsigned int u;
|
|
|
|
int i;
|
|
|
|
|
2005-11-22 02:25:50 +03:00
|
|
|
if (!env)
|
|
|
|
return 0;
|
|
|
|
|
2004-04-13 00:39:29 +04:00
|
|
|
u = 0;
|
|
|
|
for (i = 0; i < 8; i++)
|
2009-03-06 23:27:40 +03:00
|
|
|
u |= env->crf[i] << (32 - (4 * i));
|
2004-04-13 00:39:29 +04:00
|
|
|
|
|
|
|
return u;
|
|
|
|
}
|
|
|
|
|
2008-10-02 22:32:44 +04:00
|
|
|
static target_long monitor_get_msr (const struct MonitorDef *md, int val)
|
2004-04-13 00:39:29 +04:00
|
|
|
{
|
2005-11-22 02:25:50 +03:00
|
|
|
CPUState *env = mon_get_cpu();
|
|
|
|
if (!env)
|
|
|
|
return 0;
|
2007-10-26 01:35:50 +04:00
|
|
|
return env->msr;
|
2004-04-13 00:39:29 +04:00
|
|
|
}
|
|
|
|
|
2008-10-02 22:32:44 +04:00
|
|
|
static target_long monitor_get_xer (const struct MonitorDef *md, int val)
|
2004-04-13 00:39:29 +04:00
|
|
|
{
|
2005-11-22 02:25:50 +03:00
|
|
|
CPUState *env = mon_get_cpu();
|
|
|
|
if (!env)
|
|
|
|
return 0;
|
2008-10-21 15:28:46 +04:00
|
|
|
return env->xer;
|
2004-04-13 00:39:29 +04:00
|
|
|
}
|
2004-05-21 16:59:32 +04:00
|
|
|
|
2008-10-02 22:32:44 +04:00
|
|
|
static target_long monitor_get_decr (const struct MonitorDef *md, int val)
|
2004-05-21 16:59:32 +04:00
|
|
|
{
|
2005-11-22 02:25:50 +03:00
|
|
|
CPUState *env = mon_get_cpu();
|
|
|
|
if (!env)
|
|
|
|
return 0;
|
|
|
|
return cpu_ppc_load_decr(env);
|
2004-05-21 16:59:32 +04:00
|
|
|
}
|
|
|
|
|
2008-10-02 22:32:44 +04:00
|
|
|
static target_long monitor_get_tbu (const struct MonitorDef *md, int val)
|
2004-05-21 16:59:32 +04:00
|
|
|
{
|
2005-11-22 02:25:50 +03:00
|
|
|
CPUState *env = mon_get_cpu();
|
|
|
|
if (!env)
|
|
|
|
return 0;
|
|
|
|
return cpu_ppc_load_tbu(env);
|
2004-05-21 16:59:32 +04:00
|
|
|
}
|
|
|
|
|
2008-10-02 22:32:44 +04:00
|
|
|
static target_long monitor_get_tbl (const struct MonitorDef *md, int val)
|
2004-05-21 16:59:32 +04:00
|
|
|
{
|
2005-11-22 02:25:50 +03:00
|
|
|
CPUState *env = mon_get_cpu();
|
|
|
|
if (!env)
|
|
|
|
return 0;
|
|
|
|
return cpu_ppc_load_tbl(env);
|
2004-05-21 16:59:32 +04:00
|
|
|
}
|
2004-04-13 00:39:29 +04:00
|
|
|
#endif
|
|
|
|
|
2004-10-01 02:22:08 +04:00
|
|
|
#if defined(TARGET_SPARC)
|
2005-10-30 20:05:13 +03:00
|
|
|
#ifndef TARGET_SPARC64
|
2008-10-02 22:32:44 +04:00
|
|
|
static target_long monitor_get_psr (const struct MonitorDef *md, int val)
|
2004-10-01 02:22:08 +04:00
|
|
|
{
|
2005-11-22 02:25:50 +03:00
|
|
|
CPUState *env = mon_get_cpu();
|
|
|
|
if (!env)
|
|
|
|
return 0;
|
|
|
|
return GET_PSR(env);
|
2004-10-01 02:22:08 +04:00
|
|
|
}
|
2005-10-30 20:05:13 +03:00
|
|
|
#endif
|
2004-10-01 02:22:08 +04:00
|
|
|
|
2008-10-02 22:32:44 +04:00
|
|
|
static target_long monitor_get_reg(const struct MonitorDef *md, int val)
|
2004-10-01 02:22:08 +04:00
|
|
|
{
|
2005-11-22 02:25:50 +03:00
|
|
|
CPUState *env = mon_get_cpu();
|
|
|
|
if (!env)
|
|
|
|
return 0;
|
|
|
|
return env->regwptr[val];
|
2004-10-01 02:22:08 +04:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-10-02 22:32:44 +04:00
|
|
|
static const MonitorDef monitor_defs[] = {
|
2004-04-04 16:57:25 +04:00
|
|
|
#ifdef TARGET_I386
|
2004-04-25 22:54:52 +04:00
|
|
|
|
|
|
|
#define SEG(name, seg) \
|
2005-02-11 01:00:52 +03:00
|
|
|
{ name, offsetof(CPUState, segs[seg].selector), NULL, MD_I32 },\
|
2004-04-25 22:54:52 +04:00
|
|
|
{ name ".base", offsetof(CPUState, segs[seg].base) },\
|
2005-02-11 01:00:52 +03:00
|
|
|
{ name ".limit", offsetof(CPUState, segs[seg].limit), NULL, MD_I32 },
|
2004-04-25 22:54:52 +04:00
|
|
|
|
2004-04-04 16:57:25 +04: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 01:36:46 +04:00
|
|
|
{ "edi", offsetof(CPUState, regs[7]) },
|
2005-02-11 01:00:52 +03: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 16:57:25 +04:00
|
|
|
{ "eflags", offsetof(CPUState, eflags) },
|
2004-04-25 22:54:52 +04:00
|
|
|
{ "eip", offsetof(CPUState, eip) },
|
|
|
|
SEG("cs", R_CS)
|
|
|
|
SEG("ds", R_DS)
|
|
|
|
SEG("es", R_ES)
|
2004-09-14 01:36:46 +04:00
|
|
|
SEG("ss", R_SS)
|
2004-04-25 22:54:52 +04:00
|
|
|
SEG("fs", R_FS)
|
|
|
|
SEG("gs", R_GS)
|
|
|
|
{ "pc", 0, monitor_get_pc, },
|
2004-04-13 00:39:29 +04:00
|
|
|
#elif defined(TARGET_PPC)
|
2007-09-19 09:49:13 +04:00
|
|
|
/* General purpose registers */
|
2004-04-13 00:39:29 +04: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 09:49:13 +04: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-25 22:54:52 +04:00
|
|
|
{ "nip|pc", offsetof(CPUState, nip) },
|
2004-04-13 00:39:29 +04:00
|
|
|
{ "lr", offsetof(CPUState, lr) },
|
|
|
|
{ "ctr", offsetof(CPUState, ctr) },
|
2004-05-21 16:59:32 +04:00
|
|
|
{ "decr", 0, &monitor_get_decr, },
|
2004-04-13 00:39:29 +04:00
|
|
|
{ "ccr", 0, &monitor_get_ccr, },
|
2007-09-19 09:49:13 +04:00
|
|
|
/* Machine state register */
|
2004-04-13 00:39:29 +04:00
|
|
|
{ "msr", 0, &monitor_get_msr, },
|
|
|
|
{ "xer", 0, &monitor_get_xer, },
|
2004-05-21 16:59:32 +04:00
|
|
|
{ "tbu", 0, &monitor_get_tbu, },
|
|
|
|
{ "tbl", 0, &monitor_get_tbl, },
|
2007-09-19 09:49:13 +04:00
|
|
|
#if defined(TARGET_PPC64)
|
|
|
|
/* Address space register */
|
|
|
|
{ "asr", offsetof(CPUState, asr) },
|
|
|
|
#endif
|
|
|
|
/* Segment registers */
|
2004-04-13 00:39:29 +04: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 02:22:08 +04: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-30 20:05:13 +03:00
|
|
|
#ifndef TARGET_SPARC64
|
2004-10-01 02:22:08 +04:00
|
|
|
{ "psr", 0, &monitor_get_psr, },
|
|
|
|
{ "wim", offsetof(CPUState, wim) },
|
2005-10-30 20:05:13 +03:00
|
|
|
#endif
|
2004-10-01 02:22:08 +04: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-30 20:05:13 +03: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 16:57:25 +04:00
|
|
|
#endif
|
|
|
|
{ NULL },
|
|
|
|
};
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void expr_error(Monitor *mon, const char *msg)
|
2004-03-15 00:38:27 +03:00
|
|
|
{
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%s\n", msg);
|
2004-04-04 16:57:25 +04:00
|
|
|
longjmp(expr_env, 1);
|
|
|
|
}
|
|
|
|
|
2005-11-22 02:25:50 +03:00
|
|
|
/* return 0 if OK, -1 if not found, -2 if no CPU defined */
|
2005-02-11 01:00:52 +03:00
|
|
|
static int get_monitor_def(target_long *pval, const char *name)
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
2008-10-02 22:32:44 +04:00
|
|
|
const MonitorDef *md;
|
2005-02-11 01:00:52 +03:00
|
|
|
void *ptr;
|
|
|
|
|
2004-04-04 16:57:25 +04:00
|
|
|
for(md = monitor_defs; md->name != NULL; md++) {
|
|
|
|
if (compare_cmd(name, md->name)) {
|
|
|
|
if (md->get_value) {
|
2004-10-01 02:22:08 +04:00
|
|
|
*pval = md->get_value(md, md->offset);
|
2004-04-04 16:57:25 +04:00
|
|
|
} else {
|
2005-11-22 02:25:50 +03:00
|
|
|
CPUState *env = mon_get_cpu();
|
|
|
|
if (!env)
|
|
|
|
return -2;
|
|
|
|
ptr = (uint8_t *)env + md->offset;
|
2005-02-11 01:00:52 +03:00
|
|
|
switch(md->type) {
|
|
|
|
case MD_I32:
|
|
|
|
*pval = *(int32_t *)ptr;
|
|
|
|
break;
|
|
|
|
case MD_TLONG:
|
|
|
|
*pval = *(target_long *)ptr;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
*pval = 0;
|
|
|
|
break;
|
|
|
|
}
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void next(void)
|
|
|
|
{
|
|
|
|
if (pch != '\0') {
|
|
|
|
pch++;
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isspace(*pch))
|
2004-04-04 16:57:25 +04:00
|
|
|
pch++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static int64_t expr_sum(Monitor *mon);
|
2004-04-04 16:57:25 +04:00
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static int64_t expr_unary(Monitor *mon)
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
2007-09-25 21:28:42 +04:00
|
|
|
int64_t n;
|
2004-04-04 16:57:25 +04:00
|
|
|
char *p;
|
2005-11-22 02:25:50 +03:00
|
|
|
int ret;
|
2004-04-04 16:57:25 +04:00
|
|
|
|
|
|
|
switch(*pch) {
|
|
|
|
case '+':
|
|
|
|
next();
|
2009-03-06 02:01:23 +03:00
|
|
|
n = expr_unary(mon);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case '-':
|
|
|
|
next();
|
2009-03-06 02:01:23 +03:00
|
|
|
n = -expr_unary(mon);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case '~':
|
|
|
|
next();
|
2009-03-06 02:01:23 +03:00
|
|
|
n = ~expr_unary(mon);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case '(':
|
|
|
|
next();
|
2009-03-06 02:01:23 +03:00
|
|
|
n = expr_sum(mon);
|
2004-04-04 16:57:25 +04:00
|
|
|
if (*pch != ')') {
|
2009-03-06 02:01:23 +03:00
|
|
|
expr_error(mon, "')' expected");
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
|
|
|
next();
|
|
|
|
break;
|
2004-07-14 21:21:37 +04:00
|
|
|
case '\'':
|
|
|
|
pch++;
|
|
|
|
if (*pch == '\0')
|
2009-03-06 02:01:23 +03:00
|
|
|
expr_error(mon, "character constant expected");
|
2004-07-14 21:21:37 +04:00
|
|
|
n = *pch;
|
|
|
|
pch++;
|
|
|
|
if (*pch != '\'')
|
2009-03-06 02:01:23 +03:00
|
|
|
expr_error(mon, "missing terminating \' character");
|
2004-07-14 21:21:37 +04:00
|
|
|
next();
|
|
|
|
break;
|
2004-04-04 16:57:25 +04:00
|
|
|
case '$':
|
|
|
|
{
|
|
|
|
char buf[128], *q;
|
2007-12-17 06:15:52 +03:00
|
|
|
target_long reg=0;
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2004-04-04 16:57:25 +04:00
|
|
|
pch++;
|
|
|
|
q = buf;
|
|
|
|
while ((*pch >= 'a' && *pch <= 'z') ||
|
|
|
|
(*pch >= 'A' && *pch <= 'Z') ||
|
|
|
|
(*pch >= '0' && *pch <= '9') ||
|
2004-04-25 22:54:52 +04:00
|
|
|
*pch == '_' || *pch == '.') {
|
2004-04-04 16:57:25 +04:00
|
|
|
if ((q - buf) < sizeof(buf) - 1)
|
|
|
|
*q++ = *pch;
|
|
|
|
pch++;
|
|
|
|
}
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isspace(*pch))
|
2004-04-04 16:57:25 +04:00
|
|
|
pch++;
|
|
|
|
*q = 0;
|
2007-09-24 22:39:04 +04:00
|
|
|
ret = get_monitor_def(®, buf);
|
2005-11-22 02:25:50 +03:00
|
|
|
if (ret == -1)
|
2009-03-06 02:01:23 +03:00
|
|
|
expr_error(mon, "unknown register");
|
2007-09-17 01:08:06 +04:00
|
|
|
else if (ret == -2)
|
2009-03-06 02:01:23 +03:00
|
|
|
expr_error(mon, "no cpu defined");
|
2007-09-24 22:39:04 +04:00
|
|
|
n = reg;
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '\0':
|
2009-03-06 02:01:23 +03:00
|
|
|
expr_error(mon, "unexpected end of expression");
|
2004-04-04 16:57:25 +04:00
|
|
|
n = 0;
|
|
|
|
break;
|
|
|
|
default:
|
2007-09-24 22:39:04 +04:00
|
|
|
#if TARGET_PHYS_ADDR_BITS > 32
|
2006-06-25 22:28:12 +04:00
|
|
|
n = strtoull(pch, &p, 0);
|
|
|
|
#else
|
2004-04-04 16:57:25 +04:00
|
|
|
n = strtoul(pch, &p, 0);
|
2006-06-25 22:28:12 +04:00
|
|
|
#endif
|
2004-04-04 16:57:25 +04:00
|
|
|
if (pch == p) {
|
2009-03-06 02:01:23 +03:00
|
|
|
expr_error(mon, "invalid char in expression");
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
|
|
|
pch = p;
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isspace(*pch))
|
2004-04-04 16:57:25 +04:00
|
|
|
pch++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static int64_t expr_prod(Monitor *mon)
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
2007-09-25 21:28:42 +04:00
|
|
|
int64_t val, val2;
|
2005-02-11 01:00:52 +03:00
|
|
|
int op;
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
val = expr_unary(mon);
|
2004-04-04 16:57:25 +04:00
|
|
|
for(;;) {
|
|
|
|
op = *pch;
|
|
|
|
if (op != '*' && op != '/' && op != '%')
|
|
|
|
break;
|
|
|
|
next();
|
2009-03-06 02:01:23 +03:00
|
|
|
val2 = expr_unary(mon);
|
2004-04-04 16:57:25 +04:00
|
|
|
switch(op) {
|
|
|
|
default:
|
|
|
|
case '*':
|
|
|
|
val *= val2;
|
|
|
|
break;
|
|
|
|
case '/':
|
|
|
|
case '%':
|
2007-09-17 01:08:06 +04:00
|
|
|
if (val2 == 0)
|
2009-03-06 02:01:23 +03:00
|
|
|
expr_error(mon, "division by zero");
|
2004-04-04 16:57:25 +04:00
|
|
|
if (op == '/')
|
|
|
|
val /= val2;
|
|
|
|
else
|
|
|
|
val %= val2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static int64_t expr_logic(Monitor *mon)
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
2007-09-25 21:28:42 +04:00
|
|
|
int64_t val, val2;
|
2005-02-11 01:00:52 +03:00
|
|
|
int op;
|
2004-04-04 16:57:25 +04:00
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
val = expr_prod(mon);
|
2004-04-04 16:57:25 +04:00
|
|
|
for(;;) {
|
|
|
|
op = *pch;
|
|
|
|
if (op != '&' && op != '|' && op != '^')
|
|
|
|
break;
|
|
|
|
next();
|
2009-03-06 02:01:23 +03:00
|
|
|
val2 = expr_prod(mon);
|
2004-04-04 16:57:25 +04:00
|
|
|
switch(op) {
|
|
|
|
default:
|
|
|
|
case '&':
|
|
|
|
val &= val2;
|
|
|
|
break;
|
|
|
|
case '|':
|
|
|
|
val |= val2;
|
|
|
|
break;
|
|
|
|
case '^':
|
|
|
|
val ^= val2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static int64_t expr_sum(Monitor *mon)
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
2007-09-25 21:28:42 +04:00
|
|
|
int64_t val, val2;
|
2005-02-11 01:00:52 +03:00
|
|
|
int op;
|
2004-04-04 16:57:25 +04:00
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
val = expr_logic(mon);
|
2004-04-04 16:57:25 +04:00
|
|
|
for(;;) {
|
|
|
|
op = *pch;
|
|
|
|
if (op != '+' && op != '-')
|
|
|
|
break;
|
|
|
|
next();
|
2009-03-06 02:01:23 +03:00
|
|
|
val2 = expr_logic(mon);
|
2004-04-04 16:57:25 +04:00
|
|
|
if (op == '+')
|
|
|
|
val += val2;
|
|
|
|
else
|
|
|
|
val -= val2;
|
|
|
|
}
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static int get_expr(Monitor *mon, int64_t *pval, const char **pp)
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
|
|
|
pch = *pp;
|
|
|
|
if (setjmp(expr_env)) {
|
|
|
|
*pp = pch;
|
|
|
|
return -1;
|
|
|
|
}
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isspace(*pch))
|
2004-04-04 16:57:25 +04:00
|
|
|
pch++;
|
2009-03-06 02:01:23 +03:00
|
|
|
*pval = expr_sum(mon);
|
2004-04-04 16:57:25 +04:00
|
|
|
*pp = pch;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int get_str(char *buf, int buf_size, const char **pp)
|
|
|
|
{
|
|
|
|
const char *p;
|
|
|
|
char *q;
|
|
|
|
int c;
|
|
|
|
|
2004-07-14 21:21:37 +04:00
|
|
|
q = buf;
|
2004-04-04 16:57:25 +04:00
|
|
|
p = *pp;
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isspace(*p))
|
2004-04-04 16:57:25 +04:00
|
|
|
p++;
|
|
|
|
if (*p == '\0') {
|
|
|
|
fail:
|
2004-07-14 21:21:37 +04:00
|
|
|
*q = '\0';
|
2004-04-04 16:57:25 +04:00
|
|
|
*pp = p;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (*p == '\"') {
|
|
|
|
p++;
|
|
|
|
while (*p != '\0' && *p != '\"') {
|
|
|
|
if (*p == '\\') {
|
|
|
|
p++;
|
|
|
|
c = *p++;
|
|
|
|
switch(c) {
|
|
|
|
case 'n':
|
|
|
|
c = '\n';
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
c = '\r';
|
|
|
|
break;
|
|
|
|
case '\\':
|
|
|
|
case '\'':
|
|
|
|
case '\"':
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
qemu_printf("unsupported escape code: '\\%c'\n", c);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
if ((q - buf) < buf_size - 1) {
|
|
|
|
*q++ = c;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if ((q - buf) < buf_size - 1) {
|
|
|
|
*q++ = *p;
|
|
|
|
}
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (*p != '\"') {
|
2004-05-23 01:41:05 +04:00
|
|
|
qemu_printf("unterminated string\n");
|
2004-04-04 16:57:25 +04:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
p++;
|
|
|
|
} else {
|
2008-11-16 16:53:32 +03:00
|
|
|
while (*p != '\0' && !qemu_isspace(*p)) {
|
2004-04-04 16:57:25 +04:00
|
|
|
if ((q - buf) < buf_size - 1) {
|
|
|
|
*q++ = *p;
|
|
|
|
}
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
2004-07-14 21:21:37 +04:00
|
|
|
*q = '\0';
|
2004-04-04 16:57:25 +04:00
|
|
|
*pp = p;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int default_fmt_format = 'x';
|
|
|
|
static int default_fmt_size = 4;
|
|
|
|
|
|
|
|
#define MAX_ARGS 16
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void monitor_handle_command(Monitor *mon, const char *cmdline)
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
|
|
|
const char *p, *pstart, *typestr;
|
|
|
|
char *q;
|
|
|
|
int c, nb_args, len, i, has_arg;
|
2009-03-06 02:01:23 +03:00
|
|
|
const mon_cmd_t *cmd;
|
2004-04-04 16:57:25 +04:00
|
|
|
char cmdname[256];
|
|
|
|
char buf[1024];
|
|
|
|
void *str_allocated[MAX_ARGS];
|
|
|
|
void *args[MAX_ARGS];
|
2009-03-06 02:01:23 +03:00
|
|
|
void (*handler_0)(Monitor *mon);
|
|
|
|
void (*handler_1)(Monitor *mon, void *arg0);
|
|
|
|
void (*handler_2)(Monitor *mon, void *arg0, void *arg1);
|
|
|
|
void (*handler_3)(Monitor *mon, void *arg0, void *arg1, void *arg2);
|
|
|
|
void (*handler_4)(Monitor *mon, void *arg0, void *arg1, void *arg2,
|
|
|
|
void *arg3);
|
|
|
|
void (*handler_5)(Monitor *mon, void *arg0, void *arg1, void *arg2,
|
|
|
|
void *arg3, void *arg4);
|
|
|
|
void (*handler_6)(Monitor *mon, void *arg0, void *arg1, void *arg2,
|
|
|
|
void *arg3, void *arg4, void *arg5);
|
|
|
|
void (*handler_7)(Monitor *mon, void *arg0, void *arg1, void *arg2,
|
|
|
|
void *arg3, void *arg4, void *arg5, void *arg6);
|
2004-03-15 00:38:27 +03:00
|
|
|
|
|
|
|
#ifdef DEBUG
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "command='%s'\n", cmdline);
|
2004-03-15 00:38:27 +03:00
|
|
|
#endif
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2004-04-04 16:57:25 +04:00
|
|
|
/* extract the command name */
|
2004-03-15 00:38:27 +03:00
|
|
|
p = cmdline;
|
2004-04-04 16:57:25 +04:00
|
|
|
q = cmdname;
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isspace(*p))
|
2004-04-04 16:57:25 +04:00
|
|
|
p++;
|
|
|
|
if (*p == '\0')
|
|
|
|
return;
|
|
|
|
pstart = p;
|
2008-11-16 16:53:32 +03:00
|
|
|
while (*p != '\0' && *p != '/' && !qemu_isspace(*p))
|
2004-04-04 16:57:25 +04:00
|
|
|
p++;
|
|
|
|
len = p - pstart;
|
|
|
|
if (len > sizeof(cmdname) - 1)
|
|
|
|
len = sizeof(cmdname) - 1;
|
|
|
|
memcpy(cmdname, pstart, len);
|
|
|
|
cmdname[len] = '\0';
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2004-04-04 16:57:25 +04:00
|
|
|
/* find the command */
|
2009-03-06 02:01:23 +03:00
|
|
|
for(cmd = mon_cmds; cmd->name != NULL; cmd++) {
|
2007-09-17 01:08:06 +04:00
|
|
|
if (compare_cmd(cmdname, cmd->name))
|
2004-04-04 16:57:25 +04:00
|
|
|
goto found;
|
|
|
|
}
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "unknown command: '%s'\n", cmdname);
|
2004-04-04 16:57:25 +04:00
|
|
|
return;
|
|
|
|
found:
|
|
|
|
|
|
|
|
for(i = 0; i < MAX_ARGS; i++)
|
|
|
|
str_allocated[i] = NULL;
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2004-04-04 16:57:25 +04:00
|
|
|
/* parse the parameters */
|
|
|
|
typestr = cmd->args_type;
|
|
|
|
nb_args = 0;
|
2004-03-15 00:38:27 +03:00
|
|
|
for(;;) {
|
2004-04-04 16:57:25 +04:00
|
|
|
c = *typestr;
|
|
|
|
if (c == '\0')
|
2004-03-15 00:38:27 +03:00
|
|
|
break;
|
2004-04-04 16:57:25 +04:00
|
|
|
typestr++;
|
|
|
|
switch(c) {
|
|
|
|
case 'F':
|
2004-07-14 21:21:37 +04:00
|
|
|
case 'B':
|
2004-04-04 16:57:25 +04:00
|
|
|
case 's':
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
char *str;
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isspace(*p))
|
2004-04-04 16:57:25 +04:00
|
|
|
p++;
|
|
|
|
if (*typestr == '?') {
|
|
|
|
typestr++;
|
|
|
|
if (*p == '\0') {
|
|
|
|
/* no optional string: NULL argument */
|
|
|
|
str = NULL;
|
|
|
|
goto add_str;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ret = get_str(buf, sizeof(buf), &p);
|
|
|
|
if (ret < 0) {
|
2004-07-14 21:21:37 +04:00
|
|
|
switch(c) {
|
|
|
|
case 'F':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%s: filename expected\n",
|
|
|
|
cmdname);
|
2004-07-14 21:21:37 +04:00
|
|
|
break;
|
|
|
|
case 'B':
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%s: block device name expected\n",
|
|
|
|
cmdname);
|
2004-07-14 21:21:37 +04:00
|
|
|
break;
|
|
|
|
default:
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%s: string expected\n", cmdname);
|
2004-07-14 21:21:37 +04:00
|
|
|
break;
|
|
|
|
}
|
2004-04-04 16:57:25 +04:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
str = qemu_malloc(strlen(buf) + 1);
|
2008-08-21 21:58:08 +04:00
|
|
|
pstrcpy(str, sizeof(buf), buf);
|
2004-04-04 16:57:25 +04:00
|
|
|
str_allocated[nb_args] = str;
|
|
|
|
add_str:
|
|
|
|
if (nb_args >= MAX_ARGS) {
|
|
|
|
error_args:
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%s: too many arguments\n", cmdname);
|
2004-04-04 16:57:25 +04:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
args[nb_args++] = str;
|
|
|
|
}
|
2004-03-15 00:38:27 +03:00
|
|
|
break;
|
2004-04-04 16:57:25 +04:00
|
|
|
case '/':
|
|
|
|
{
|
|
|
|
int count, format, size;
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isspace(*p))
|
2004-04-04 16:57:25 +04:00
|
|
|
p++;
|
|
|
|
if (*p == '/') {
|
|
|
|
/* format found */
|
|
|
|
p++;
|
|
|
|
count = 1;
|
2008-11-16 16:53:32 +03:00
|
|
|
if (qemu_isdigit(*p)) {
|
2004-04-04 16:57:25 +04:00
|
|
|
count = 0;
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isdigit(*p)) {
|
2004-04-04 16:57:25 +04:00
|
|
|
count = count * 10 + (*p - '0');
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
size = -1;
|
|
|
|
format = -1;
|
|
|
|
for(;;) {
|
|
|
|
switch(*p) {
|
|
|
|
case 'o':
|
|
|
|
case 'd':
|
|
|
|
case 'u':
|
|
|
|
case 'x':
|
|
|
|
case 'i':
|
|
|
|
case 'c':
|
|
|
|
format = *p++;
|
|
|
|
break;
|
|
|
|
case 'b':
|
|
|
|
size = 1;
|
|
|
|
p++;
|
|
|
|
break;
|
|
|
|
case 'h':
|
|
|
|
size = 2;
|
|
|
|
p++;
|
|
|
|
break;
|
|
|
|
case 'w':
|
|
|
|
size = 4;
|
|
|
|
p++;
|
|
|
|
break;
|
|
|
|
case 'g':
|
|
|
|
case 'L':
|
|
|
|
size = 8;
|
|
|
|
p++;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
next:
|
2008-11-16 16:53:32 +03:00
|
|
|
if (*p != '\0' && !qemu_isspace(*p)) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "invalid char in format: '%c'\n",
|
|
|
|
*p);
|
2004-04-04 16:57:25 +04:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
if (format < 0)
|
|
|
|
format = default_fmt_format;
|
2004-04-25 22:05:08 +04:00
|
|
|
if (format != 'i') {
|
|
|
|
/* for 'i', not specifying a size gives -1 as size */
|
|
|
|
if (size < 0)
|
|
|
|
size = default_fmt_size;
|
2008-10-02 01:45:51 +04:00
|
|
|
default_fmt_size = size;
|
2004-04-25 22:05:08 +04:00
|
|
|
}
|
2004-04-04 16:57:25 +04:00
|
|
|
default_fmt_format = format;
|
|
|
|
} else {
|
|
|
|
count = 1;
|
|
|
|
format = default_fmt_format;
|
2004-04-25 22:05:08 +04:00
|
|
|
if (format != 'i') {
|
|
|
|
size = default_fmt_size;
|
|
|
|
} else {
|
|
|
|
size = -1;
|
|
|
|
}
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
|
|
|
if (nb_args + 3 > MAX_ARGS)
|
|
|
|
goto error_args;
|
2007-04-14 16:17:59 +04:00
|
|
|
args[nb_args++] = (void*)(long)count;
|
|
|
|
args[nb_args++] = (void*)(long)format;
|
|
|
|
args[nb_args++] = (void*)(long)size;
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
2004-03-15 00:38:27 +03:00
|
|
|
break;
|
2004-04-04 16:57:25 +04:00
|
|
|
case 'i':
|
2005-02-11 01:00:52 +03:00
|
|
|
case 'l':
|
2004-04-04 16:57:25 +04:00
|
|
|
{
|
2007-09-25 21:28:42 +04:00
|
|
|
int64_t val;
|
2007-09-24 22:39:04 +04:00
|
|
|
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isspace(*p))
|
2004-04-04 16:57:25 +04:00
|
|
|
p++;
|
2004-06-08 04:55:58 +04:00
|
|
|
if (*typestr == '?' || *typestr == '.') {
|
|
|
|
if (*typestr == '?') {
|
|
|
|
if (*p == '\0')
|
|
|
|
has_arg = 0;
|
|
|
|
else
|
|
|
|
has_arg = 1;
|
|
|
|
} else {
|
|
|
|
if (*p == '.') {
|
|
|
|
p++;
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isspace(*p))
|
2004-06-08 04:55:58 +04:00
|
|
|
p++;
|
|
|
|
has_arg = 1;
|
|
|
|
} else {
|
|
|
|
has_arg = 0;
|
|
|
|
}
|
|
|
|
}
|
2006-07-15 02:03:35 +04:00
|
|
|
typestr++;
|
2004-04-04 16:57:25 +04:00
|
|
|
if (nb_args >= MAX_ARGS)
|
|
|
|
goto error_args;
|
2007-04-14 16:17:59 +04:00
|
|
|
args[nb_args++] = (void *)(long)has_arg;
|
2004-04-04 16:57:25 +04:00
|
|
|
if (!has_arg) {
|
|
|
|
if (nb_args >= MAX_ARGS)
|
|
|
|
goto error_args;
|
|
|
|
val = -1;
|
|
|
|
goto add_num;
|
|
|
|
}
|
|
|
|
}
|
2009-03-06 02:01:23 +03:00
|
|
|
if (get_expr(mon, &val, &p))
|
2004-04-04 16:57:25 +04:00
|
|
|
goto fail;
|
|
|
|
add_num:
|
2005-02-11 01:00:52 +03:00
|
|
|
if (c == 'i') {
|
|
|
|
if (nb_args >= MAX_ARGS)
|
|
|
|
goto error_args;
|
2007-04-14 16:17:59 +04:00
|
|
|
args[nb_args++] = (void *)(long)val;
|
2005-02-11 01:00:52 +03:00
|
|
|
} else {
|
|
|
|
if ((nb_args + 1) >= MAX_ARGS)
|
|
|
|
goto error_args;
|
2007-09-24 22:39:04 +04:00
|
|
|
#if TARGET_PHYS_ADDR_BITS > 32
|
2007-04-14 16:17:59 +04:00
|
|
|
args[nb_args++] = (void *)(long)((val >> 32) & 0xffffffff);
|
2005-02-11 01:00:52 +03:00
|
|
|
#else
|
|
|
|
args[nb_args++] = (void *)0;
|
|
|
|
#endif
|
2007-04-14 16:17:59 +04:00
|
|
|
args[nb_args++] = (void *)(long)(val & 0xffffffff);
|
2005-02-11 01:00:52 +03:00
|
|
|
}
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '-':
|
|
|
|
{
|
|
|
|
int has_option;
|
|
|
|
/* option */
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2004-04-04 16:57:25 +04:00
|
|
|
c = *typestr++;
|
|
|
|
if (c == '\0')
|
|
|
|
goto bad_type;
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isspace(*p))
|
2004-04-04 16:57:25 +04:00
|
|
|
p++;
|
|
|
|
has_option = 0;
|
|
|
|
if (*p == '-') {
|
|
|
|
p++;
|
|
|
|
if (*p != c) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%s: unsupported option -%c\n",
|
|
|
|
cmdname, *p);
|
2004-04-04 16:57:25 +04:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
p++;
|
|
|
|
has_option = 1;
|
|
|
|
}
|
|
|
|
if (nb_args >= MAX_ARGS)
|
|
|
|
goto error_args;
|
2007-04-14 16:17:59 +04:00
|
|
|
args[nb_args++] = (void *)(long)has_option;
|
2004-04-04 16:57:25 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
bad_type:
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%s: unknown type '%c'\n", cmdname, c);
|
2004-04-04 16:57:25 +04:00
|
|
|
goto fail;
|
|
|
|
}
|
2004-03-15 00:38:27 +03:00
|
|
|
}
|
2004-04-04 16:57:25 +04:00
|
|
|
/* check that all arguments were parsed */
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isspace(*p))
|
2004-04-04 16:57:25 +04:00
|
|
|
p++;
|
|
|
|
if (*p != '\0') {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%s: extraneous characters at the end of line\n",
|
|
|
|
cmdname);
|
2004-04-04 16:57:25 +04:00
|
|
|
goto fail;
|
2004-03-15 00:38:27 +03:00
|
|
|
}
|
2004-04-04 16:57:25 +04:00
|
|
|
|
|
|
|
switch(nb_args) {
|
|
|
|
case 0:
|
2008-08-18 00:21:51 +04:00
|
|
|
handler_0 = cmd->handler;
|
2009-03-06 02:01:23 +03:00
|
|
|
handler_0(mon);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case 1:
|
2008-08-18 00:21:51 +04:00
|
|
|
handler_1 = cmd->handler;
|
2009-03-06 02:01:23 +03:00
|
|
|
handler_1(mon, args[0]);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case 2:
|
2008-08-18 00:21:51 +04:00
|
|
|
handler_2 = cmd->handler;
|
2009-03-06 02:01:23 +03:00
|
|
|
handler_2(mon, args[0], args[1]);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case 3:
|
2008-08-18 00:21:51 +04:00
|
|
|
handler_3 = cmd->handler;
|
2009-03-06 02:01:23 +03:00
|
|
|
handler_3(mon, args[0], args[1], args[2]);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case 4:
|
2008-08-18 00:21:51 +04:00
|
|
|
handler_4 = cmd->handler;
|
2009-03-06 02:01:23 +03:00
|
|
|
handler_4(mon, args[0], args[1], args[2], args[3]);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
|
|
|
case 5:
|
2008-08-18 00:21:51 +04:00
|
|
|
handler_5 = cmd->handler;
|
2009-03-06 02:01:23 +03:00
|
|
|
handler_5(mon, args[0], args[1], args[2], args[3], args[4]);
|
2004-04-04 16:57:25 +04:00
|
|
|
break;
|
2004-06-08 04:55:58 +04:00
|
|
|
case 6:
|
2008-08-18 00:21:51 +04:00
|
|
|
handler_6 = cmd->handler;
|
2009-03-06 02:01:23 +03:00
|
|
|
handler_6(mon, args[0], args[1], args[2], args[3], args[4], args[5]);
|
2004-06-08 04:55:58 +04:00
|
|
|
break;
|
2006-07-16 22:57:03 +04:00
|
|
|
case 7:
|
2008-08-18 00:21:51 +04:00
|
|
|
handler_7 = cmd->handler;
|
2009-03-06 02:01:23 +03:00
|
|
|
handler_7(mon, args[0], args[1], args[2], args[3], args[4], args[5],
|
|
|
|
args[6]);
|
2006-07-16 22:57:03 +04:00
|
|
|
break;
|
2004-04-04 16:57:25 +04:00
|
|
|
default:
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "unsupported number of arguments: %d\n", nb_args);
|
2004-04-04 16:57:25 +04:00
|
|
|
goto fail;
|
2004-03-15 00:38:27 +03:00
|
|
|
}
|
2004-04-04 16:57:25 +04:00
|
|
|
fail:
|
|
|
|
for(i = 0; i < MAX_ARGS; i++)
|
|
|
|
qemu_free(str_allocated[i]);
|
2004-03-15 00:38:27 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2004-07-14 21:21:37 +04:00
|
|
|
static void cmd_completion(const char *name, const char *list)
|
|
|
|
{
|
|
|
|
const char *p, *pstart;
|
|
|
|
char cmd[128];
|
|
|
|
int len;
|
|
|
|
|
|
|
|
p = list;
|
|
|
|
for(;;) {
|
|
|
|
pstart = p;
|
|
|
|
p = strchr(p, '|');
|
|
|
|
if (!p)
|
|
|
|
p = pstart + strlen(pstart);
|
|
|
|
len = p - pstart;
|
|
|
|
if (len > sizeof(cmd) - 2)
|
|
|
|
len = sizeof(cmd) - 2;
|
|
|
|
memcpy(cmd, pstart, len);
|
|
|
|
cmd[len] = '\0';
|
|
|
|
if (name[0] == '\0' || !strncmp(name, cmd, strlen(name))) {
|
2009-03-06 02:01:42 +03:00
|
|
|
readline_add_completion(cur_mon->rs, cmd);
|
2004-07-14 21:21:37 +04:00
|
|
|
}
|
|
|
|
if (*p == '\0')
|
|
|
|
break;
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void file_completion(const char *input)
|
|
|
|
{
|
|
|
|
DIR *ffs;
|
|
|
|
struct dirent *d;
|
|
|
|
char path[1024];
|
|
|
|
char file[1024], file_prefix[1024];
|
|
|
|
int input_path_len;
|
|
|
|
const char *p;
|
|
|
|
|
2007-09-17 01:08:06 +04:00
|
|
|
p = strrchr(input, '/');
|
2004-07-14 21:21:37 +04:00
|
|
|
if (!p) {
|
|
|
|
input_path_len = 0;
|
|
|
|
pstrcpy(file_prefix, sizeof(file_prefix), input);
|
2008-08-21 21:58:08 +04:00
|
|
|
pstrcpy(path, sizeof(path), ".");
|
2004-07-14 21:21:37 +04:00
|
|
|
} else {
|
|
|
|
input_path_len = p - input + 1;
|
|
|
|
memcpy(path, input, input_path_len);
|
|
|
|
if (input_path_len > sizeof(path) - 1)
|
|
|
|
input_path_len = sizeof(path) - 1;
|
|
|
|
path[input_path_len] = '\0';
|
|
|
|
pstrcpy(file_prefix, sizeof(file_prefix), p + 1);
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_COMPLETION
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(cur_mon, "input='%s' path='%s' prefix='%s'\n",
|
|
|
|
input, path, file_prefix);
|
2004-07-14 21:21:37 +04:00
|
|
|
#endif
|
|
|
|
ffs = opendir(path);
|
|
|
|
if (!ffs)
|
|
|
|
return;
|
|
|
|
for(;;) {
|
|
|
|
struct stat sb;
|
|
|
|
d = readdir(ffs);
|
|
|
|
if (!d)
|
|
|
|
break;
|
|
|
|
if (strstart(d->d_name, file_prefix, NULL)) {
|
|
|
|
memcpy(file, input, input_path_len);
|
2008-08-21 21:58:08 +04:00
|
|
|
if (input_path_len < sizeof(file))
|
|
|
|
pstrcpy(file + input_path_len, sizeof(file) - input_path_len,
|
|
|
|
d->d_name);
|
2004-07-14 21:21:37 +04:00
|
|
|
/* stat the file to find out if it's a directory.
|
|
|
|
* In that case add a slash to speed up typing long paths
|
|
|
|
*/
|
|
|
|
stat(file, &sb);
|
|
|
|
if(S_ISDIR(sb.st_mode))
|
2008-08-21 21:58:08 +04:00
|
|
|
pstrcat(file, sizeof(file), "/");
|
2009-03-06 02:01:42 +03:00
|
|
|
readline_add_completion(cur_mon->rs, file);
|
2004-07-14 21:21:37 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
closedir(ffs);
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:00:43 +03:00
|
|
|
static void block_completion_it(void *opaque, BlockDriverState *bs)
|
2004-07-14 21:21:37 +04:00
|
|
|
{
|
2009-03-06 02:00:43 +03:00
|
|
|
const char *name = bdrv_get_device_name(bs);
|
2004-07-14 21:21:37 +04:00
|
|
|
const char *input = opaque;
|
|
|
|
|
|
|
|
if (input[0] == '\0' ||
|
|
|
|
!strncmp(name, (char *)input, strlen(input))) {
|
2009-03-06 02:01:42 +03:00
|
|
|
readline_add_completion(cur_mon->rs, name);
|
2004-07-14 21:21:37 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* NOTE: this parser is an approximate form of the real command parser */
|
|
|
|
static void parse_cmdline(const char *cmdline,
|
|
|
|
int *pnb_args, char **args)
|
|
|
|
{
|
|
|
|
const char *p;
|
|
|
|
int nb_args, ret;
|
|
|
|
char buf[1024];
|
|
|
|
|
|
|
|
p = cmdline;
|
|
|
|
nb_args = 0;
|
|
|
|
for(;;) {
|
2008-11-16 16:53:32 +03:00
|
|
|
while (qemu_isspace(*p))
|
2004-07-14 21:21:37 +04:00
|
|
|
p++;
|
|
|
|
if (*p == '\0')
|
|
|
|
break;
|
|
|
|
if (nb_args >= MAX_ARGS)
|
|
|
|
break;
|
|
|
|
ret = get_str(buf, sizeof(buf), &p);
|
|
|
|
args[nb_args] = qemu_strdup(buf);
|
|
|
|
nb_args++;
|
|
|
|
if (ret < 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
*pnb_args = nb_args;
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:37 +03:00
|
|
|
static void monitor_find_completion(const char *cmdline)
|
2004-07-14 21:21:37 +04:00
|
|
|
{
|
|
|
|
const char *cmdname;
|
|
|
|
char *args[MAX_ARGS];
|
|
|
|
int nb_args, i, len;
|
|
|
|
const char *ptype, *str;
|
2009-03-06 02:01:23 +03:00
|
|
|
const mon_cmd_t *cmd;
|
2006-05-07 22:03:31 +04:00
|
|
|
const KeyDef *key;
|
2004-07-14 21:21:37 +04:00
|
|
|
|
|
|
|
parse_cmdline(cmdline, &nb_args, args);
|
|
|
|
#ifdef DEBUG_COMPLETION
|
|
|
|
for(i = 0; i < nb_args; i++) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(cur_mon, "arg%d = '%s'\n", i, (char *)args[i]);
|
2004-07-14 21:21:37 +04:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* if the line ends with a space, it means we want to complete the
|
|
|
|
next arg */
|
|
|
|
len = strlen(cmdline);
|
2008-11-16 16:53:32 +03:00
|
|
|
if (len > 0 && qemu_isspace(cmdline[len - 1])) {
|
2004-07-14 21:21:37 +04: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 02:01:42 +03:00
|
|
|
readline_set_completion_index(cur_mon->rs, strlen(cmdname));
|
2009-03-06 02:01:23 +03:00
|
|
|
for(cmd = mon_cmds; cmd->name != NULL; cmd++) {
|
2004-07-14 21:21:37 +04:00
|
|
|
cmd_completion(cmdname, cmd->name);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* find the command */
|
2009-03-06 02:01:23 +03:00
|
|
|
for(cmd = mon_cmds; cmd->name != NULL; cmd++) {
|
2004-07-14 21:21:37 +04:00
|
|
|
if (compare_cmd(args[0], cmd->name))
|
|
|
|
goto found;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
found:
|
|
|
|
ptype = cmd->args_type;
|
|
|
|
for(i = 0; i < nb_args - 2; i++) {
|
|
|
|
if (*ptype != '\0') {
|
|
|
|
ptype++;
|
|
|
|
while (*ptype == '?')
|
|
|
|
ptype++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
str = args[nb_args - 1];
|
|
|
|
switch(*ptype) {
|
|
|
|
case 'F':
|
|
|
|
/* file completion */
|
2009-03-06 02:01:42 +03:00
|
|
|
readline_set_completion_index(cur_mon->rs, strlen(str));
|
2004-07-14 21:21:37 +04:00
|
|
|
file_completion(str);
|
|
|
|
break;
|
|
|
|
case 'B':
|
|
|
|
/* block device name completion */
|
2009-03-06 02:01:42 +03:00
|
|
|
readline_set_completion_index(cur_mon->rs, strlen(str));
|
2004-07-14 21:21:37 +04:00
|
|
|
bdrv_iterate(block_completion_it, (void *)str);
|
|
|
|
break;
|
2004-10-09 22:08:01 +04:00
|
|
|
case 's':
|
|
|
|
/* XXX: more generic ? */
|
|
|
|
if (!strcmp(cmd->name, "info")) {
|
2009-03-06 02:01:42 +03:00
|
|
|
readline_set_completion_index(cur_mon->rs, strlen(str));
|
2004-10-09 22:08:01 +04:00
|
|
|
for(cmd = info_cmds; cmd->name != NULL; cmd++) {
|
|
|
|
cmd_completion(str, cmd->name);
|
|
|
|
}
|
2006-05-07 22:03:31 +04:00
|
|
|
} else if (!strcmp(cmd->name, "sendkey")) {
|
2009-03-08 20:42:02 +03:00
|
|
|
char *sep = strrchr(str, '-');
|
|
|
|
if (sep)
|
|
|
|
str = sep + 1;
|
2009-03-06 02:01:42 +03:00
|
|
|
readline_set_completion_index(cur_mon->rs, strlen(str));
|
2006-05-07 22:03:31 +04:00
|
|
|
for(key = key_defs; key->name != NULL; key++) {
|
|
|
|
cmd_completion(str, key->name);
|
|
|
|
}
|
2004-10-09 22:08:01 +04:00
|
|
|
}
|
|
|
|
break;
|
2004-07-14 21:21:37 +04:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for(i = 0; i < nb_args; i++)
|
|
|
|
qemu_free(args[i]);
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:42 +03:00
|
|
|
static int monitor_can_read(void *opaque)
|
2004-03-15 00:38:27 +03:00
|
|
|
{
|
2009-03-06 02:01:42 +03:00
|
|
|
Monitor *mon = opaque;
|
|
|
|
|
|
|
|
return (mon->suspend_cnt == 0) ? 128 : 0;
|
2004-03-15 00:38:27 +03:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:42 +03:00
|
|
|
static void monitor_read(void *opaque, const uint8_t *buf, int size)
|
2004-03-15 00:38:27 +03:00
|
|
|
{
|
2009-03-06 02:01:42 +03:00
|
|
|
Monitor *old_mon = cur_mon;
|
2004-08-02 01:52:19 +04:00
|
|
|
int i;
|
2009-03-06 02:01:23 +03:00
|
|
|
|
2009-03-06 02:01:42 +03:00
|
|
|
cur_mon = opaque;
|
|
|
|
|
2009-03-06 02:01:51 +03:00
|
|
|
if (cur_mon->rs) {
|
|
|
|
for (i = 0; i < size; i++)
|
|
|
|
readline_handle_byte(cur_mon->rs, buf[i]);
|
|
|
|
} else {
|
|
|
|
if (size == 0 || buf[size - 1] != 0)
|
|
|
|
monitor_printf(cur_mon, "corrupted command\n");
|
|
|
|
else
|
|
|
|
monitor_handle_command(cur_mon, (char *)buf);
|
|
|
|
}
|
2004-03-15 00:38:27 +03:00
|
|
|
|
2009-03-06 02:01:42 +03:00
|
|
|
cur_mon = old_mon;
|
|
|
|
}
|
2008-10-06 17:52:44 +04:00
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void monitor_command_cb(Monitor *mon, const char *cmdline, void *opaque)
|
2004-04-04 17:07:25 +04:00
|
|
|
{
|
2009-03-06 02:01:42 +03:00
|
|
|
monitor_suspend(mon);
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_handle_command(mon, cmdline);
|
2009-03-06 02:01:42 +03:00
|
|
|
monitor_resume(mon);
|
2008-10-06 17:52:44 +04:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:51 +03:00
|
|
|
int monitor_suspend(Monitor *mon)
|
2008-10-06 17:52:44 +04:00
|
|
|
{
|
2009-03-06 02:01:51 +03:00
|
|
|
if (!mon->rs)
|
|
|
|
return -ENOTTY;
|
2009-03-06 02:01:42 +03:00
|
|
|
mon->suspend_cnt++;
|
2009-03-06 02:01:51 +03:00
|
|
|
return 0;
|
2008-10-06 17:52:44 +04:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
void monitor_resume(Monitor *mon)
|
2008-10-06 17:52:44 +04:00
|
|
|
{
|
2009-03-06 02:01:51 +03:00
|
|
|
if (!mon->rs)
|
|
|
|
return;
|
2009-03-06 02:01:42 +03:00
|
|
|
if (--mon->suspend_cnt == 0)
|
|
|
|
readline_show_prompt(mon->rs);
|
2004-04-04 17:07:25 +04:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:42 +03:00
|
|
|
static void monitor_event(void *opaque, int event)
|
2007-01-06 01:01:59 +03:00
|
|
|
{
|
2009-03-06 02:01:23 +03:00
|
|
|
Monitor *mon = opaque;
|
|
|
|
|
2009-03-06 02:01:47 +03:00
|
|
|
switch (event) {
|
|
|
|
case CHR_EVENT_MUX_IN:
|
|
|
|
readline_restart(mon->rs);
|
|
|
|
monitor_resume(mon);
|
|
|
|
monitor_flush(mon);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CHR_EVENT_MUX_OUT:
|
|
|
|
if (mon->suspend_cnt == 0)
|
|
|
|
monitor_printf(mon, "\n");
|
|
|
|
monitor_flush(mon);
|
|
|
|
monitor_suspend(mon);
|
|
|
|
break;
|
2007-01-06 01:01:59 +03:00
|
|
|
|
2009-03-06 02:01:47 +03:00
|
|
|
case CHR_EVENT_RESET:
|
|
|
|
monitor_printf(mon, "QEMU %s monitor - type 'help' for more "
|
|
|
|
"information\n", QEMU_VERSION);
|
|
|
|
if (mon->chr->focus == 0)
|
|
|
|
readline_show_prompt(mon->rs);
|
|
|
|
break;
|
|
|
|
}
|
2007-01-06 01:01:59 +03: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-06 23:27:37 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Local variables:
|
|
|
|
* c-indent-level: 4
|
|
|
|
* c-basic-offset: 4
|
|
|
|
* tab-width: 8
|
|
|
|
* End:
|
|
|
|
*/
|
|
|
|
|
2009-03-06 02:01:42 +03:00
|
|
|
void monitor_init(CharDriverState *chr, int flags)
|
2004-04-04 17:07:25 +04:00
|
|
|
{
|
2009-03-06 02:01:42 +03:00
|
|
|
static int is_first_init = 1;
|
2009-03-06 02:01:29 +03:00
|
|
|
Monitor *mon;
|
2007-02-18 20:04:49 +03:00
|
|
|
|
|
|
|
if (is_first_init) {
|
2008-06-09 02:45:01 +04:00
|
|
|
key_timer = qemu_new_timer(vm_clock, release_keys, NULL);
|
2007-02-18 20:04:49 +03:00
|
|
|
is_first_init = 0;
|
|
|
|
}
|
2009-03-06 02:01:29 +03:00
|
|
|
|
|
|
|
mon = qemu_mallocz(sizeof(*mon));
|
2007-02-18 20:04:49 +03:00
|
|
|
|
2009-03-06 02:01:29 +03:00
|
|
|
mon->chr = chr;
|
2009-03-06 02:01:42 +03:00
|
|
|
mon->flags = flags;
|
2009-03-06 02:01:47 +03:00
|
|
|
if (mon->chr->focus != 0)
|
|
|
|
mon->suspend_cnt = 1; /* mux'ed monitors start suspended */
|
2009-03-06 02:01:51 +03:00
|
|
|
if (flags & MONITOR_USE_READLINE) {
|
|
|
|
mon->rs = readline_init(mon, monitor_find_completion);
|
|
|
|
monitor_read_command(mon, 0);
|
|
|
|
}
|
2009-03-06 02:01:29 +03:00
|
|
|
|
2009-03-06 02:01:42 +03:00
|
|
|
qemu_chr_add_handlers(chr, monitor_can_read, monitor_read, monitor_event,
|
|
|
|
mon);
|
2009-03-06 02:01:29 +03:00
|
|
|
|
|
|
|
LIST_INSERT_HEAD(&mon_list, mon, entry);
|
2009-03-06 02:01:42 +03:00
|
|
|
if (!cur_mon || (flags & MONITOR_IS_DEFAULT))
|
2009-03-06 02:01:29 +03:00
|
|
|
cur_mon = mon;
|
2004-04-04 17:07:25 +04:00
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
static void bdrv_password_cb(Monitor *mon, const char *password, void *opaque)
|
2004-07-14 21:21:37 +04:00
|
|
|
{
|
2009-03-06 02:01:15 +03:00
|
|
|
BlockDriverState *bs = opaque;
|
|
|
|
int ret = 0;
|
2004-07-14 21:21:37 +04:00
|
|
|
|
2009-03-06 02:01:15 +03:00
|
|
|
if (bdrv_set_key(bs, password) != 0) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "invalid password\n");
|
2009-03-06 02:01:15 +03:00
|
|
|
ret = -EPERM;
|
2004-03-15 00:38:27 +03:00
|
|
|
}
|
2009-03-06 02:01:42 +03:00
|
|
|
if (mon->password_completion_cb)
|
|
|
|
mon->password_completion_cb(mon->password_opaque, ret);
|
2009-03-06 02:01:15 +03:00
|
|
|
|
2009-03-06 02:01:42 +03:00
|
|
|
monitor_read_command(mon, 1);
|
2004-03-15 00:38:27 +03:00
|
|
|
}
|
2009-03-06 02:01:01 +03:00
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
void monitor_read_bdrv_key_start(Monitor *mon, BlockDriverState *bs,
|
2009-03-06 02:01:15 +03:00
|
|
|
BlockDriverCompletionFunc *completion_cb,
|
|
|
|
void *opaque)
|
2009-03-06 02:01:01 +03:00
|
|
|
{
|
2009-03-06 02:01:51 +03:00
|
|
|
int err;
|
|
|
|
|
2009-03-06 02:01:15 +03:00
|
|
|
if (!bdrv_key_required(bs)) {
|
|
|
|
if (completion_cb)
|
|
|
|
completion_cb(opaque, 0);
|
|
|
|
return;
|
|
|
|
}
|
2009-03-06 02:01:01 +03:00
|
|
|
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "%s (%s) is encrypted.\n", bdrv_get_device_name(bs),
|
|
|
|
bdrv_get_encrypted_filename(bs));
|
2009-03-06 02:01:15 +03:00
|
|
|
|
2009-03-06 02:01:42 +03:00
|
|
|
mon->password_completion_cb = completion_cb;
|
|
|
|
mon->password_opaque = opaque;
|
2009-03-06 02:01:15 +03:00
|
|
|
|
2009-03-06 02:01:51 +03:00
|
|
|
err = monitor_read_password(mon, bdrv_password_cb, bs);
|
|
|
|
|
|
|
|
if (err && completion_cb)
|
|
|
|
completion_cb(opaque, err);
|
2009-03-06 02:01:01 +03:00
|
|
|
}
|