/* * QEMU monitor * * Copyright (c) 2003-2004 Fabrice Bellard * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "qemu/osdep.h" #include "monitor-internal.h" #include "monitor/qdev.h" #include "exec/gdbstub.h" #include "net/slirp.h" #include "qemu/log.h" #include "sysemu/sysemu.h" #include "sysemu/device_tree.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qerror.h" #include "monitor/hmp-target.h" #include "monitor/hmp.h" #include "exec/address-spaces.h" #include "exec/ioport.h" #include "block/block-hmp-cmds.h" #include "qapi/qapi-commands-control.h" #include "qapi/qapi-commands-misc.h" #include "qapi/qapi-commands-machine.h" #include "qapi/qapi-init-commands.h" #include "qapi/error.h" #include "qemu/cutils.h" #if defined(TARGET_S390X) #include "hw/s390x/storage-keys.h" #include "hw/s390x/storage-attributes.h" #endif /* Make devices configuration available for use in hmp-commands*.hx templates */ #include CONFIG_DEVICES static HMPCommand hmp_info_cmds[]; char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index, int64_t cpu_index, Error **errp) { char *output = NULL; MonitorHMP hmp = {}; monitor_data_init(&hmp.common, false, true, false); if (has_cpu_index) { int ret = monitor_set_cpu(&hmp.common, cpu_index); if (ret < 0) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index", "a CPU number"); goto out; } } handle_hmp_command(&hmp, command_line); WITH_QEMU_LOCK_GUARD(&hmp.common.mon_lock) { output = g_strdup(hmp.common.outbuf->str); } out: monitor_data_destroy(&hmp.common); return output; } /** * Is @name in the '|' separated list of names @list? */ int hmp_compare_cmd(const char *name, const char *list) { const char *p, *pstart; int len; len = strlen(name); p = list; for (;;) { pstart = p; p = qemu_strchrnul(p, '|'); if ((p - pstart) == len && !memcmp(pstart, name, len)) { return 1; } if (*p == '\0') { break; } p++; } return 0; } static void do_help_cmd(Monitor *mon, const QDict *qdict) { hmp_help_cmd(mon, qdict_get_try_str(qdict, "name")); } static void hmp_info_help(Monitor *mon, const QDict *qdict) { hmp_help_cmd(mon, "info"); } static void monitor_init_qmp_commands(void) { /* * Two command lists: * - qmp_commands contains all QMP commands * - qmp_cap_negotiation_commands contains just * "qmp_capabilities", to enforce capability negotiation */ qmp_init_marshal(&qmp_commands); qmp_register_command(&qmp_commands, "device_add", qmp_device_add, 0, 0); QTAILQ_INIT(&qmp_cap_negotiation_commands); qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities", qmp_marshal_qmp_capabilities, QCO_ALLOW_PRECONFIG, 0); } static 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); } static 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++; } } static 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); } } static 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); } } static 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); } } static void do_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"); } static 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); } static 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); } static 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; } } static 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); } } static 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); } /* Please update hmp-commands.hx when adding or changing commands */ static HMPCommand hmp_info_cmds[] = { #include "hmp-commands-info.h" { NULL, NULL, }, }; /* hmp_cmds and hmp_info_cmds would be sorted at runtime */ HMPCommand hmp_cmds[] = { #include "hmp-commands.h" { NULL, NULL, }, }; /* * Set @pval to the value in the register identified by @name. * return 0 if OK, -1 if not found */ int get_monitor_def(Monitor *mon, int64_t *pval, const char *name) { const MonitorDef *md = target_monitor_defs(); CPUState *cs = mon_get_cpu(mon); void *ptr; uint64_t tmp = 0; int ret; if (cs == NULL || md == NULL) { return -1; } for(; md->name != NULL; md++) { if (hmp_compare_cmd(name, md->name)) { if (md->get_value) { *pval = md->get_value(mon, md, md->offset); } else { CPUArchState *env = mon_get_cpu_env(mon); ptr = (uint8_t *)env + md->offset; switch(md->type) { case MD_I32: *pval = *(int32_t *)ptr; break; case MD_TLONG: *pval = *(target_long *)ptr; break; default: *pval = 0; break; } } return 0; } } ret = target_get_monitor_def(cs, name, &tmp); if (!ret) { *pval = (target_long) tmp; } return ret; } static int compare_mon_cmd(const void *a, const void *b) { return strcmp(((const HMPCommand *)a)->name, ((const HMPCommand *)b)->name); } static void sortcmdlist(void) { qsort(hmp_cmds, ARRAY_SIZE(hmp_cmds) - 1, sizeof(*hmp_cmds), compare_mon_cmd); qsort(hmp_info_cmds, ARRAY_SIZE(hmp_info_cmds) - 1, sizeof(*hmp_info_cmds), compare_mon_cmd); } void monitor_register_hmp(const char *name, bool info, void (*cmd)(Monitor *mon, const QDict *qdict)) { HMPCommand *table = info ? hmp_info_cmds : hmp_cmds; while (table->name != NULL) { if (strcmp(table->name, name) == 0) { g_assert(table->cmd == NULL && table->cmd_info_hrt == NULL); table->cmd = cmd; return; } table++; } g_assert_not_reached(); } void monitor_register_hmp_info_hrt(const char *name, HumanReadableText *(*handler)(Error **errp)) { HMPCommand *table = hmp_info_cmds; while (table->name != NULL) { if (strcmp(table->name, name) == 0) { g_assert(table->cmd == NULL && table->cmd_info_hrt == NULL); table->cmd_info_hrt = handler; return; } table++; } g_assert_not_reached(); } void monitor_init_globals(void) { monitor_init_globals_core(); monitor_init_qmp_commands(); sortcmdlist(); }