mirror of
https://gitlab.com/qemu-project/qemu
synced 2024-09-06 18:05:37 +00:00
![Philippe Mathieu-Daudé](/assets/img/avatar_default.png)
This is a counterpart to the HMP "info pic" command. It is being added with an "x-" prefix because this QMP command is intended as an adhoc debugging tool and will thus not be modelled in QAPI as fully structured data, nor will it have long term guaranteed stability. The existing HMP command is rewritten to call the QMP command. Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Message-Id: <20240610063518.50680-3-philmd@linaro.org>
436 lines
10 KiB
C
436 lines
10 KiB
C
/*
|
|
* Human Monitor Interface commands
|
|
*
|
|
* Copyright IBM, Corp. 2011
|
|
*
|
|
* Authors:
|
|
* Anthony Liguori <aliguori@us.ibm.com>
|
|
*
|
|
* This work is licensed under the terms of the GNU GPL, version 2. See
|
|
* the COPYING file in the top-level directory.
|
|
*
|
|
* Contributions after 2012-01-13 are licensed under the terms of the
|
|
* GNU GPL, version 2 or (at your option) any later version.
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "exec/address-spaces.h"
|
|
#include "exec/gdbstub.h"
|
|
#include "exec/ioport.h"
|
|
#include "monitor/hmp.h"
|
|
#include "qemu/help_option.h"
|
|
#include "monitor/monitor-internal.h"
|
|
#include "qapi/error.h"
|
|
#include "qapi/qapi-commands-control.h"
|
|
#include "qapi/qapi-commands-machine.h"
|
|
#include "qapi/qapi-commands-misc.h"
|
|
#include "qapi/qmp/qdict.h"
|
|
#include "qemu/cutils.h"
|
|
#include "qemu/log.h"
|
|
#include "sysemu/sysemu.h"
|
|
|
|
bool hmp_handle_error(Monitor *mon, Error *err)
|
|
{
|
|
if (err) {
|
|
error_reportf_err(err, "Error: ");
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Split @str at comma.
|
|
* A null @str defaults to "".
|
|
*/
|
|
strList *hmp_split_at_comma(const char *str)
|
|
{
|
|
char **split = g_strsplit(str ?: "", ",", -1);
|
|
strList *res = NULL;
|
|
strList **tail = &res;
|
|
int i;
|
|
|
|
for (i = 0; split[i]; i++) {
|
|
QAPI_LIST_APPEND(tail, split[i]);
|
|
}
|
|
|
|
g_free(split);
|
|
return res;
|
|
}
|
|
|
|
void hmp_info_name(Monitor *mon, const QDict *qdict)
|
|
{
|
|
NameInfo *info;
|
|
|
|
info = qmp_query_name(NULL);
|
|
if (info->name) {
|
|
monitor_printf(mon, "%s\n", info->name);
|
|
}
|
|
qapi_free_NameInfo(info);
|
|
}
|
|
|
|
void hmp_info_version(Monitor *mon, const QDict *qdict)
|
|
{
|
|
VersionInfo *info;
|
|
|
|
info = qmp_query_version(NULL);
|
|
|
|
monitor_printf(mon, "%" PRId64 ".%" PRId64 ".%" PRId64 "%s\n",
|
|
info->qemu->major, info->qemu->minor, info->qemu->micro,
|
|
info->package);
|
|
|
|
qapi_free_VersionInfo(info);
|
|
}
|
|
|
|
void hmp_quit(Monitor *mon, const QDict *qdict)
|
|
{
|
|
monitor_suspend(mon);
|
|
qmp_quit(NULL);
|
|
}
|
|
|
|
void hmp_stop(Monitor *mon, const QDict *qdict)
|
|
{
|
|
qmp_stop(NULL);
|
|
}
|
|
|
|
void hmp_sync_profile(Monitor *mon, const QDict *qdict)
|
|
{
|
|
const char *op = qdict_get_try_str(qdict, "op");
|
|
|
|
if (op == NULL) {
|
|
bool on = qsp_is_enabled();
|
|
|
|
monitor_printf(mon, "sync-profile is %s\n", on ? "on" : "off");
|
|
return;
|
|
}
|
|
if (!strcmp(op, "on")) {
|
|
qsp_enable();
|
|
} else if (!strcmp(op, "off")) {
|
|
qsp_disable();
|
|
} else if (!strcmp(op, "reset")) {
|
|
qsp_reset();
|
|
} else {
|
|
Error *err = NULL;
|
|
|
|
error_setg(&err, "invalid parameter '%s',"
|
|
" expecting 'on', 'off', or 'reset'", op);
|
|
hmp_handle_error(mon, err);
|
|
}
|
|
}
|
|
|
|
void hmp_exit_preconfig(Monitor *mon, const QDict *qdict)
|
|
{
|
|
Error *err = NULL;
|
|
|
|
qmp_x_exit_preconfig(&err);
|
|
hmp_handle_error(mon, err);
|
|
}
|
|
|
|
void hmp_cpu(Monitor *mon, const QDict *qdict)
|
|
{
|
|
int64_t cpu_index;
|
|
|
|
/* XXX: drop the monitor_set_cpu() usage when all HMP commands that
|
|
use it are converted to the QAPI */
|
|
cpu_index = qdict_get_int(qdict, "index");
|
|
if (monitor_set_cpu(mon, cpu_index) < 0) {
|
|
monitor_printf(mon, "invalid CPU index\n");
|
|
}
|
|
}
|
|
|
|
void hmp_cont(Monitor *mon, const QDict *qdict)
|
|
{
|
|
Error *err = NULL;
|
|
|
|
qmp_cont(&err);
|
|
hmp_handle_error(mon, err);
|
|
}
|
|
|
|
void hmp_change(Monitor *mon, const QDict *qdict)
|
|
{
|
|
const char *device = qdict_get_str(qdict, "device");
|
|
const char *target = qdict_get_str(qdict, "target");
|
|
const char *arg = qdict_get_try_str(qdict, "arg");
|
|
const char *read_only = qdict_get_try_str(qdict, "read-only-mode");
|
|
bool force = qdict_get_try_bool(qdict, "force", false);
|
|
Error *err = NULL;
|
|
|
|
#ifdef CONFIG_VNC
|
|
if (strcmp(device, "vnc") == 0) {
|
|
hmp_change_vnc(mon, device, target, arg, read_only, force, &err);
|
|
} else
|
|
#endif
|
|
{
|
|
hmp_change_medium(mon, device, target, arg, read_only, force, &err);
|
|
}
|
|
|
|
hmp_handle_error(mon, err);
|
|
}
|
|
|
|
#ifdef CONFIG_POSIX
|
|
void hmp_getfd(Monitor *mon, const QDict *qdict)
|
|
{
|
|
const char *fdname = qdict_get_str(qdict, "fdname");
|
|
Error *err = NULL;
|
|
|
|
qmp_getfd(fdname, &err);
|
|
hmp_handle_error(mon, err);
|
|
}
|
|
#endif
|
|
|
|
void hmp_closefd(Monitor *mon, const QDict *qdict)
|
|
{
|
|
const char *fdname = qdict_get_str(qdict, "fdname");
|
|
Error *err = NULL;
|
|
|
|
qmp_closefd(fdname, &err);
|
|
hmp_handle_error(mon, err);
|
|
}
|
|
|
|
void hmp_info_iothreads(Monitor *mon, const QDict *qdict)
|
|
{
|
|
IOThreadInfoList *info_list = qmp_query_iothreads(NULL);
|
|
IOThreadInfoList *info;
|
|
IOThreadInfo *value;
|
|
|
|
for (info = info_list; info; info = info->next) {
|
|
value = info->value;
|
|
monitor_printf(mon, "%s:\n", value->id);
|
|
monitor_printf(mon, " thread_id=%" PRId64 "\n", value->thread_id);
|
|
monitor_printf(mon, " poll-max-ns=%" PRId64 "\n", value->poll_max_ns);
|
|
monitor_printf(mon, " poll-grow=%" PRId64 "\n", value->poll_grow);
|
|
monitor_printf(mon, " poll-shrink=%" PRId64 "\n", value->poll_shrink);
|
|
monitor_printf(mon, " aio-max-batch=%" PRId64 "\n",
|
|
value->aio_max_batch);
|
|
}
|
|
|
|
qapi_free_IOThreadInfoList(info_list);
|
|
}
|
|
|
|
void hmp_help(Monitor *mon, const QDict *qdict)
|
|
{
|
|
hmp_help_cmd(mon, qdict_get_try_str(qdict, "name"));
|
|
}
|
|
|
|
void hmp_info_help(Monitor *mon, const QDict *qdict)
|
|
{
|
|
hmp_help_cmd(mon, "info");
|
|
}
|
|
|
|
void hmp_info_sync_profile(Monitor *mon, const QDict *qdict)
|
|
{
|
|
int64_t max = qdict_get_try_int(qdict, "max", 10);
|
|
bool mean = qdict_get_try_bool(qdict, "mean", false);
|
|
bool coalesce = !qdict_get_try_bool(qdict, "no_coalesce", false);
|
|
enum QSPSortBy sort_by;
|
|
|
|
sort_by = mean ? QSP_SORT_BY_AVG_WAIT_TIME : QSP_SORT_BY_TOTAL_WAIT_TIME;
|
|
qsp_report(max, sort_by, coalesce);
|
|
}
|
|
|
|
void hmp_info_history(Monitor *mon, const QDict *qdict)
|
|
{
|
|
MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
|
|
int i;
|
|
const char *str;
|
|
|
|
if (!hmp_mon->rs) {
|
|
return;
|
|
}
|
|
i = 0;
|
|
for(;;) {
|
|
str = readline_get_history(hmp_mon->rs, i);
|
|
if (!str) {
|
|
break;
|
|
}
|
|
monitor_printf(mon, "%d: '%s'\n", i, str);
|
|
i++;
|
|
}
|
|
}
|
|
|
|
void hmp_logfile(Monitor *mon, const QDict *qdict)
|
|
{
|
|
Error *err = NULL;
|
|
|
|
if (!qemu_set_log_filename(qdict_get_str(qdict, "filename"), &err)) {
|
|
error_report_err(err);
|
|
}
|
|
}
|
|
|
|
void hmp_log(Monitor *mon, const QDict *qdict)
|
|
{
|
|
int mask;
|
|
const char *items = qdict_get_str(qdict, "items");
|
|
Error *err = NULL;
|
|
|
|
if (!strcmp(items, "none")) {
|
|
mask = 0;
|
|
} else {
|
|
mask = qemu_str_to_log_mask(items);
|
|
if (!mask) {
|
|
hmp_help_cmd(mon, "log");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!qemu_set_log(mask, &err)) {
|
|
error_report_err(err);
|
|
}
|
|
}
|
|
|
|
void hmp_gdbserver(Monitor *mon, const QDict *qdict)
|
|
{
|
|
const char *device = qdict_get_try_str(qdict, "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) {
|
|
monitor_printf(mon, "Disabled gdbserver\n");
|
|
} else {
|
|
monitor_printf(mon, "Waiting for gdb connection on device '%s'\n",
|
|
device);
|
|
}
|
|
}
|
|
|
|
void hmp_print(Monitor *mon, const QDict *qdict)
|
|
{
|
|
int format = qdict_get_int(qdict, "format");
|
|
hwaddr val = qdict_get_int(qdict, "val");
|
|
|
|
switch(format) {
|
|
case 'o':
|
|
monitor_printf(mon, "%#" HWADDR_PRIo, val);
|
|
break;
|
|
case 'x':
|
|
monitor_printf(mon, "%#" HWADDR_PRIx, val);
|
|
break;
|
|
case 'u':
|
|
monitor_printf(mon, "%" HWADDR_PRIu, val);
|
|
break;
|
|
default:
|
|
case 'd':
|
|
monitor_printf(mon, "%" HWADDR_PRId, val);
|
|
break;
|
|
case 'c':
|
|
monitor_printc(mon, val);
|
|
break;
|
|
}
|
|
monitor_printf(mon, "\n");
|
|
}
|
|
|
|
void hmp_sum(Monitor *mon, const QDict *qdict)
|
|
{
|
|
uint32_t addr;
|
|
uint16_t sum;
|
|
uint32_t start = qdict_get_int(qdict, "start");
|
|
uint32_t size = qdict_get_int(qdict, "size");
|
|
|
|
sum = 0;
|
|
for(addr = start; addr < (start + size); addr++) {
|
|
uint8_t val = address_space_ldub(&address_space_memory, addr,
|
|
MEMTXATTRS_UNSPECIFIED, NULL);
|
|
/* BSD sum algorithm ('sum' Unix command) */
|
|
sum = (sum >> 1) | (sum << 15);
|
|
sum += val;
|
|
}
|
|
monitor_printf(mon, "%05d\n", sum);
|
|
}
|
|
|
|
void hmp_ioport_read(Monitor *mon, const QDict *qdict)
|
|
{
|
|
int size = qdict_get_int(qdict, "size");
|
|
int addr = qdict_get_int(qdict, "addr");
|
|
int has_index = qdict_haskey(qdict, "index");
|
|
uint32_t val;
|
|
int suffix;
|
|
|
|
if (has_index) {
|
|
int index = qdict_get_int(qdict, "index");
|
|
cpu_outb(addr & IOPORTS_MASK, index & 0xff);
|
|
addr++;
|
|
}
|
|
addr &= 0xffff;
|
|
|
|
switch(size) {
|
|
default:
|
|
case 1:
|
|
val = cpu_inb(addr);
|
|
suffix = 'b';
|
|
break;
|
|
case 2:
|
|
val = cpu_inw(addr);
|
|
suffix = 'w';
|
|
break;
|
|
case 4:
|
|
val = cpu_inl(addr);
|
|
suffix = 'l';
|
|
break;
|
|
}
|
|
monitor_printf(mon, "port%c[0x%04x] = 0x%0*x\n",
|
|
suffix, addr, size * 2, val);
|
|
}
|
|
|
|
void hmp_ioport_write(Monitor *mon, const QDict *qdict)
|
|
{
|
|
int size = qdict_get_int(qdict, "size");
|
|
int addr = qdict_get_int(qdict, "addr");
|
|
int val = qdict_get_int(qdict, "val");
|
|
|
|
addr &= IOPORTS_MASK;
|
|
|
|
switch (size) {
|
|
default:
|
|
case 1:
|
|
cpu_outb(addr, val);
|
|
break;
|
|
case 2:
|
|
cpu_outw(addr, val);
|
|
break;
|
|
case 4:
|
|
cpu_outl(addr, val);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void hmp_boot_set(Monitor *mon, const QDict *qdict)
|
|
{
|
|
Error *local_err = NULL;
|
|
const char *bootdevice = qdict_get_str(qdict, "bootdevice");
|
|
|
|
qemu_boot_set(bootdevice, &local_err);
|
|
if (local_err) {
|
|
error_report_err(local_err);
|
|
} else {
|
|
monitor_printf(mon, "boot device list now set to %s\n", bootdevice);
|
|
}
|
|
}
|
|
|
|
void hmp_info_mtree(Monitor *mon, const QDict *qdict)
|
|
{
|
|
bool flatview = qdict_get_try_bool(qdict, "flatview", false);
|
|
bool dispatch_tree = qdict_get_try_bool(qdict, "dispatch_tree", false);
|
|
bool owner = qdict_get_try_bool(qdict, "owner", false);
|
|
bool disabled = qdict_get_try_bool(qdict, "disabled", false);
|
|
|
|
mtree_info(flatview, dispatch_tree, owner, disabled);
|
|
}
|
|
|
|
#if defined(CONFIG_FDT)
|
|
void hmp_dumpdtb(Monitor *mon, const QDict *qdict)
|
|
{
|
|
const char *filename = qdict_get_str(qdict, "filename");
|
|
Error *local_err = NULL;
|
|
|
|
qmp_dumpdtb(filename, &local_err);
|
|
|
|
if (hmp_handle_error(mon, local_err)) {
|
|
return;
|
|
}
|
|
|
|
monitor_printf(mon, "dtb dumped to %s", filename);
|
|
}
|
|
#endif
|