| /* | |
| * 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 "qemu/base-arch-defs.h" | |
| #include "system/address-spaces.h" | |
| #include "system/ioport.h" | |
| #include "exec/gdbstub.h" | |
| #include "exec/target_page.h" | |
| #include "gdbstub/enums.h" | |
| #include "monitor/hmp.h" | |
| #include "qemu/help_option.h" | |
| #include "monitor/hmp.h" | |
| #include "monitor/hmp-completion.h" | |
| #include "monitor/monitor-internal.h" | |
| #include "monitor/monitor-hmp-internal.h" | |
| #include "monitor/qdev.h" | |
| #include "qapi/error.h" | |
| #include "qapi/qapi-commands-control.h" | |
| #include "qapi/qapi-commands-machine.h" | |
| #include "qapi/qapi-commands-misc.h" | |
| #include "block/block-hmp-cmds.h" | |
| #include "qobject/qdict.h" | |
| #include "qemu/cutils.h" | |
| #include "qemu/log.h" | |
| #include "net/slirp.h" | |
| #include "system/device_tree.h" | |
| #include "system/hw_accel.h" | |
| #include "system/memory.h" | |
| #include "system/system.h" | |
| #include "system/tcg.h" | |
| #include "disas/disas.h" | |
| /* Please update hmp-commands.hx when adding or changing commands */ | |
| #ifdef CONFIG_HMP | |
| static HMPCommand hmp_info_cmds[] = { | |
| #include "hmp-commands-info.h" | |
| { NULL, NULL, }, | |
| }; | |
| /* hmp_cmds and hmp_info_cmds would be sorted at runtime */ | |
| static HMPCommand hmp_cmds[] = { | |
| #include "hmp-commands.h" | |
| { NULL, NULL, }, | |
| }; | |
| #else | |
| static HMPCommand hmp_info_cmds[] = { { NULL, NULL, }, }; | |
| static HMPCommand hmp_cmds[] = { { NULL, NULL, }, }; | |
| #endif | |
| HMPCommand *hmp_cmds_for_target(bool info_command) | |
| { | |
| return info_command ? hmp_info_cmds : hmp_cmds; | |
| } | |
| static int | |
| compare_mon_cmd(const void *a, const void *b) | |
| { | |
| return strcmp(((const HMPCommand *)a)->name, | |
| ((const HMPCommand *)b)->name); | |
| } | |
| static void __attribute__((__constructor__)) 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); | |
| } | |
| bool hmp_handle_error(MonitorHMP *hmp, 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(MonitorHMP *hmp, const QDict *qdict) | |
| { | |
| NameInfo *info; | |
| info = qmp_query_name(NULL); | |
| if (info->name) { | |
| monitor_hmp_printf(hmp, "%s\n", info->name); | |
| } | |
| qapi_free_NameInfo(info); | |
| } | |
| void hmp_info_version(MonitorHMP *hmp, const QDict *qdict) | |
| { | |
| VersionInfo *info; | |
| info = qmp_query_version(NULL); | |
| monitor_hmp_printf(hmp, "%" PRId64 ".%" PRId64 ".%" PRId64 "%s\n", | |
| info->qemu->major, info->qemu->minor, info->qemu->micro, | |
| info->package); | |
| qapi_free_VersionInfo(info); | |
| } | |
| void hmp_quit(MonitorHMP *hmp, const QDict *qdict) | |
| { | |
| Monitor *mon = MONITOR(hmp); | |
| if (hmp->use_readline) { | |
| monitor_suspend(mon); | |
| } | |
| qmp_quit(NULL); | |
| } | |
| void hmp_stop(MonitorHMP *hmp, const QDict *qdict) | |
| { | |
| qmp_stop(NULL); | |
| } | |
| void hmp_sync_profile(MonitorHMP *hmp, const QDict *qdict) | |
| { | |
| const char *op = qdict_get_try_str(qdict, "op"); | |
| if (op == NULL) { | |
| bool on = qsp_is_enabled(); | |
| monitor_hmp_printf(hmp, "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(hmp, err); | |
| } | |
| } | |
| void hmp_exit_preconfig(MonitorHMP *hmp, const QDict *qdict) | |
| { | |
| Error *err = NULL; | |
| qmp_x_exit_preconfig(&err); | |
| hmp_handle_error(hmp, err); | |
| } | |
| void hmp_cpu(MonitorHMP *hmp, const QDict *qdict) | |
| { | |
| int64_t cpu_index; | |
| /* XXX: drop the monitor_hmp_set_cpu() usage when all HMP commands that | |
| use it are converted to the QAPI */ | |
| cpu_index = qdict_get_int(qdict, "index"); | |
| if (monitor_hmp_set_cpu(hmp, cpu_index) < 0) { | |
| monitor_hmp_printf(hmp, "invalid CPU index\n"); | |
| } | |
| } | |
| void hmp_cont(MonitorHMP *hmp, const QDict *qdict) | |
| { | |
| Error *err = NULL; | |
| qmp_cont(&err); | |
| hmp_handle_error(hmp, err); | |
| } | |
| void hmp_change(MonitorHMP *hmp, 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(hmp, device, target, arg, read_only, force, &err); | |
| } else | |
| #endif | |
| { | |
| hmp_change_medium(hmp, device, target, arg, read_only, force, &err); | |
| } | |
| hmp_handle_error(hmp, err); | |
| } | |
| #ifdef CONFIG_POSIX | |
| void hmp_getfd(MonitorHMP *hmp, const QDict *qdict) | |
| { | |
| const char *fdname = qdict_get_str(qdict, "fdname"); | |
| Error *err = NULL; | |
| qmp_getfd(fdname, &err); | |
| hmp_handle_error(hmp, err); | |
| } | |
| #endif | |
| void hmp_closefd(MonitorHMP *hmp, const QDict *qdict) | |
| { | |
| const char *fdname = qdict_get_str(qdict, "fdname"); | |
| Error *err = NULL; | |
| qmp_closefd(fdname, &err); | |
| hmp_handle_error(hmp, err); | |
| } | |
| void hmp_info_iothreads(MonitorHMP *hmp, 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_hmp_printf(hmp, "%s:\n", value->id); | |
| monitor_hmp_printf(hmp, " thread_id=%" PRId64 "\n", value->thread_id); | |
| monitor_hmp_printf(hmp, " poll-max-ns=%" PRId64 "\n", value->poll_max_ns); | |
| monitor_hmp_printf(hmp, " poll-grow=%" PRId64 "\n", value->poll_grow); | |
| monitor_hmp_printf(hmp, " poll-shrink=%" PRId64 "\n", value->poll_shrink); | |
| monitor_hmp_printf(hmp, " poll-weight=%" PRId64 "\n", value->poll_weight); | |
| monitor_hmp_printf(hmp, " aio-max-batch=%" PRId64 "\n", | |
| value->aio_max_batch); | |
| } | |
| qapi_free_IOThreadInfoList(info_list); | |
| } | |
| void hmp_help(MonitorHMP *hmp, const QDict *qdict) | |
| { | |
| hmp_help_cmd(hmp, qdict_get_try_str(qdict, "name")); | |
| } | |
| void hmp_clear(MonitorHMP *hmp, const QDict *qdict) | |
| { | |
| /* | |
| * Send an ANSI escape sequence: | |
| * "\x1b[H" - move cursor to top-left | |
| * "\x1b[2J" - clear visible screen | |
| * "\x1b[3J" - clear scrollback | |
| */ | |
| monitor_hmp_printf(hmp, "\x1b[H\x1b[2J\x1b[3J"); | |
| } | |
| void hmp_info_help(MonitorHMP *hmp, const QDict *qdict) | |
| { | |
| hmp_help_cmd(hmp, "info"); | |
| } | |
| void hmp_info_sync_profile(MonitorHMP *hmp, 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(MonitorHMP *hmp, const QDict *qdict) | |
| { | |
| int i; | |
| const char *str; | |
| if (!hmp->rs) { | |
| return; | |
| } | |
| i = 0; | |
| for(;;) { | |
| str = readline_get_history(hmp->rs, i); | |
| if (!str) { | |
| break; | |
| } | |
| monitor_hmp_printf(hmp, "%d: '%s'\n", i, str); | |
| i++; | |
| } | |
| } | |
| void hmp_logfile(MonitorHMP *hmp, const QDict *qdict) | |
| { | |
| Error *err = NULL; | |
| if (!qemu_set_log_filename(qdict_get_str(qdict, "filename"), &err)) { | |
| error_report_err(err); | |
| } | |
| } | |
| void hmp_log(MonitorHMP *hmp, 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(hmp, "log"); | |
| return; | |
| } | |
| } | |
| if (!qemu_set_log(mask, &err)) { | |
| error_report_err(err); | |
| return; | |
| } | |
| /* CPU_LOG_TB_NOCHAIN feeds into the per-CPU cflags. */ | |
| if (tcg_enabled()) { | |
| tcg_update_all_cflags(); | |
| } | |
| } | |
| void hmp_gdbserver(MonitorHMP *hmp, const QDict *qdict) | |
| { | |
| Error *err = NULL; | |
| const char *device = qdict_get_try_str(qdict, "device"); | |
| if (!device) { | |
| device = "tcp::" DEFAULT_GDBSTUB_PORT; | |
| } | |
| if (!gdbserver_start(device, &err)) { | |
| error_report_err(err); | |
| } else if (strcmp(device, "none") == 0) { | |
| monitor_hmp_printf(hmp, "Disabled gdbserver\n"); | |
| } else { | |
| monitor_hmp_printf(hmp, "Waiting for gdb connection on device '%s'\n", | |
| device); | |
| } | |
| } | |
| void hmp_print(MonitorHMP *hmp, const QDict *qdict) | |
| { | |
| int format = qdict_get_int(qdict, "format"); | |
| hwaddr val = qdict_get_int(qdict, "val"); | |
| switch(format) { | |
| case 'o': | |
| monitor_hmp_printf(hmp, "0x%" HWADDR_PRIo, val); | |
| break; | |
| case 'x': | |
| monitor_hmp_printf(hmp, "0x%" HWADDR_PRIx, val); | |
| break; | |
| case 'u': | |
| monitor_hmp_printf(hmp, "%" HWADDR_PRIu, val); | |
| break; | |
| default: | |
| case 'd': | |
| monitor_hmp_printf(hmp, "%" HWADDR_PRId, val); | |
| break; | |
| case 'c': | |
| monitor_hmp_printc(hmp, val); | |
| break; | |
| } | |
| monitor_hmp_printf(hmp, "\n"); | |
| } | |
| void hmp_sum(MonitorHMP *hmp, 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_hmp_printf(hmp, "%05d\n", sum); | |
| } | |
| void hmp_ioport_read(MonitorHMP *hmp, 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_hmp_printf(hmp, "port%c[0x%04x] = 0x%0*x\n", | |
| suffix, addr, size * 2, val); | |
| } | |
| void hmp_ioport_write(MonitorHMP *hmp, 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(MonitorHMP *hmp, 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_hmp_printf(hmp, "boot device list now set to %s\n", bootdevice); | |
| } | |
| } | |
| void hmp_info_mtree(MonitorHMP *hmp, 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(MonitorHMP *hmp, const QDict *qdict) | |
| { | |
| const char *filename = qdict_get_str(qdict, "filename"); | |
| Error *local_err = NULL; | |
| qmp_dumpdtb(filename, &local_err); | |
| if (hmp_handle_error(hmp, local_err)) { | |
| return; | |
| } | |
| monitor_hmp_printf(hmp, "DTB dumped to '%s'\n", filename); | |
| } | |
| #endif | |
| /* Set the current CPU defined by the user. Callers must hold BQL. */ | |
| int monitor_hmp_set_cpu(MonitorHMP *hmp, int cpu_index) | |
| { | |
| CPUState *cpu; | |
| cpu = qemu_get_cpu(cpu_index); | |
| if (cpu == NULL) { | |
| return -1; | |
| } | |
| g_free(hmp->mon_cpu_path); | |
| hmp->mon_cpu_path = object_get_canonical_path(OBJECT(cpu)); | |
| return 0; | |
| } | |
| /* Callers must hold BQL. */ | |
| static CPUState *monitor_hmp_get_cpu_sync(MonitorHMP *hmp, bool synchronize) | |
| { | |
| CPUState *cpu = NULL; | |
| if (hmp->mon_cpu_path) { | |
| cpu = (CPUState *) object_resolve_path_type(hmp->mon_cpu_path, | |
| TYPE_CPU, NULL); | |
| if (!cpu) { | |
| g_free(hmp->mon_cpu_path); | |
| hmp->mon_cpu_path = NULL; | |
| } | |
| } | |
| if (!hmp->mon_cpu_path) { | |
| if (!first_cpu) { | |
| return NULL; | |
| } | |
| monitor_hmp_set_cpu(hmp, first_cpu->cpu_index); | |
| cpu = first_cpu; | |
| } | |
| assert(cpu != NULL); | |
| if (synchronize) { | |
| cpu_synchronize_state(cpu); | |
| } | |
| return cpu; | |
| } | |
| CPUState *monitor_hmp_get_cpu(MonitorHMP *hmp) | |
| { | |
| return monitor_hmp_get_cpu_sync(hmp, true); | |
| } | |
| CPUArchState *monitor_hmp_get_cpu_env(MonitorHMP *hmp) | |
| { | |
| CPUState *cs = monitor_hmp_get_cpu(hmp); | |
| return cs ? cpu_env(cs) : NULL; | |
| } | |
| int monitor_hmp_get_cpu_index(MonitorHMP *hmp) | |
| { | |
| CPUState *cs = monitor_hmp_get_cpu_sync(hmp, false); | |
| return cs ? cs->cpu_index : UNASSIGNED_CPU_INDEX; | |
| } | |
| void hmp_info_registers(MonitorHMP *hmp, const QDict *qdict) | |
| { | |
| bool all_cpus = qdict_get_try_bool(qdict, "cpustate_all", false); | |
| int vcpu = qdict_get_try_int(qdict, "vcpu", -1); | |
| CPUState *cs; | |
| if (all_cpus) { | |
| CPU_FOREACH(cs) { | |
| monitor_hmp_printf(hmp, "\nCPU#%d\n", cs->cpu_index); | |
| cpu_dump_state(cs, NULL, CPU_DUMP_FPU | CPU_DUMP_VPU); | |
| } | |
| } else { | |
| cs = vcpu >= 0 ? qemu_get_cpu(vcpu) : monitor_hmp_get_cpu(hmp); | |
| if (!cs) { | |
| if (vcpu >= 0) { | |
| monitor_hmp_printf(hmp, "CPU#%d not available\n", vcpu); | |
| } else { | |
| monitor_hmp_printf(hmp, "No CPU available\n"); | |
| } | |
| return; | |
| } | |
| monitor_hmp_printf(hmp, "\nCPU#%d\n", cs->cpu_index); | |
| cpu_dump_state(cs, NULL, CPU_DUMP_FPU | CPU_DUMP_VPU); | |
| } | |
| } | |
| static void memory_dump(MonitorHMP *hmp, int count, int format, int wsize, | |
| uint64_t addr, bool is_physical) | |
| { | |
| int l, line_size, i, max_digits, len; | |
| uint8_t buf[16]; | |
| uint64_t v; | |
| CPUState *cs = monitor_hmp_get_cpu(hmp); | |
| const unsigned int addr_width = is_physical ? 8 : (target_long_bits() / 4); | |
| const bool big_endian = target_big_endian(); | |
| if (!cs && (format == 'i' || !is_physical)) { | |
| monitor_hmp_printf(hmp, "Can not dump without CPU\n"); | |
| return; | |
| } | |
| if (format == 'i') { | |
| monitor_disas(hmp, cs, addr, count, is_physical); | |
| return; | |
| } | |
| len = wsize * count; | |
| if (wsize == 1) { | |
| line_size = 8; | |
| } else { | |
| line_size = 16; | |
| } | |
| max_digits = 0; | |
| switch (format) { | |
| case 'o': | |
| max_digits = DIV_ROUND_UP(wsize * 8, 3); | |
| break; | |
| default: | |
| case 'x': | |
| max_digits = (wsize * 8) / 4; | |
| break; | |
| case 'u': | |
| case 'd': | |
| max_digits = DIV_ROUND_UP(wsize * 8 * 10, 33); | |
| break; | |
| case 'c': | |
| wsize = 1; | |
| break; | |
| } | |
| while (len > 0) { | |
| monitor_hmp_printf(hmp, "%0*" PRIx64 ":", addr_width, addr); | |
| l = len; | |
| if (l > line_size) { | |
| l = line_size; | |
| } | |
| if (is_physical) { | |
| AddressSpace *as = cs ? cs->as : &address_space_memory; | |
| MemTxResult r = address_space_read(as, addr, | |
| MEMTXATTRS_UNSPECIFIED, buf, l); | |
| if (r != MEMTX_OK) { | |
| monitor_hmp_printf(hmp, " Cannot access memory\n"); | |
| break; | |
| } | |
| } else { | |
| if (cpu_memory_rw_debug(cs, addr, buf, l, 0) < 0) { | |
| monitor_hmp_printf(hmp, " Cannot access memory\n"); | |
| break; | |
| } | |
| } | |
| i = 0; | |
| while (i < l) { | |
| switch (wsize) { | |
| default: | |
| case 1: | |
| v = ldub_p(buf + i); | |
| break; | |
| case 2: | |
| v = (big_endian ? lduw_be_p : lduw_le_p)(buf + i); | |
| break; | |
| case 4: | |
| v = (uint32_t)(big_endian ? ldl_be_p : ldl_le_p)(buf + i); | |
| break; | |
| case 8: | |
| v = (big_endian ? ldq_be_p : ldq_le_p)(buf + i); | |
| break; | |
| } | |
| monitor_hmp_printf(hmp, " "); | |
| switch (format) { | |
| case 'o': | |
| monitor_hmp_printf(hmp, "0%*" PRIo64, max_digits, v); | |
| break; | |
| case 'x': | |
| monitor_hmp_printf(hmp, "0x%0*" PRIx64, max_digits, v); | |
| break; | |
| case 'u': | |
| monitor_hmp_printf(hmp, "%*" PRIu64, max_digits, v); | |
| break; | |
| case 'd': | |
| monitor_hmp_printf(hmp, "%*" PRId64, max_digits, v); | |
| break; | |
| case 'c': | |
| monitor_hmp_printc(hmp, v); | |
| break; | |
| } | |
| i += wsize; | |
| } | |
| monitor_hmp_printf(hmp, "\n"); | |
| addr += l; | |
| len -= l; | |
| } | |
| } | |
| void hmp_memory_dump(MonitorHMP *hmp, const QDict *qdict) | |
| { | |
| int count = qdict_get_int(qdict, "count"); | |
| int format = qdict_get_int(qdict, "format"); | |
| int size = qdict_get_int(qdict, "size"); | |
| vaddr addr = qdict_get_int(qdict, "addr"); | |
| memory_dump(hmp, count, format, size, addr, false); | |
| } | |
| void hmp_physical_memory_dump(MonitorHMP *hmp, const QDict *qdict) | |
| { | |
| int count = qdict_get_int(qdict, "count"); | |
| int format = qdict_get_int(qdict, "format"); | |
| int size = qdict_get_int(qdict, "size"); | |
| hwaddr addr = qdict_get_int(qdict, "addr"); | |
| memory_dump(hmp, count, format, size, addr, true); | |
| } | |
| void hmp_gpa2hva(MonitorHMP *hmp, const QDict *qdict) | |
| { | |
| hwaddr addr = qdict_get_int(qdict, "addr"); | |
| Error *local_err = NULL; | |
| MemoryRegion *mr = NULL; | |
| void *ptr; | |
| ptr = gpa2hva(&mr, addr, 1, &local_err); | |
| if (local_err) { | |
| error_report_err(local_err); | |
| return; | |
| } | |
| monitor_hmp_printf(hmp, "Host virtual address for 0x%" HWADDR_PRIx | |
| " (%s) is %p\n", | |
| addr, mr->name, ptr); | |
| memory_region_unref(mr); | |
| } | |
| void hmp_gva2gpa(MonitorHMP *hmp, const QDict *qdict) | |
| { | |
| vaddr addr = qdict_get_int(qdict, "addr"); | |
| CPUState *cs = monitor_hmp_get_cpu(hmp); | |
| TranslateForDebugResult tres; | |
| if (!cs) { | |
| monitor_hmp_printf(hmp, "No cpu\n"); | |
| return; | |
| } | |
| if (!cpu_translate_for_debug(cs, addr, &tres)) { | |
| monitor_hmp_printf(hmp, "Unmapped\n"); | |
| } else { | |
| monitor_hmp_printf(hmp, "gpa: 0x%" HWADDR_PRIx "\n", tres.physaddr); | |
| } | |
| } | |
| #ifdef CONFIG_LINUX | |
| static uint64_t vtop(void *ptr, Error **errp) | |
| { | |
| uint64_t pinfo; | |
| uint64_t ret = -1; | |
| uintptr_t addr = (uintptr_t) ptr; | |
| uintptr_t pagesize = qemu_real_host_page_size(); | |
| off_t offset = addr / pagesize * sizeof(pinfo); | |
| int fd; | |
| fd = open("/proc/self/pagemap", O_RDONLY); | |
| if (fd == -1) { | |
| error_setg_file_open(errp, errno, "/proc/self/pagemap"); | |
| return -1; | |
| } | |
| /* Force copy-on-write if necessary. */ | |
| qatomic_add((uint8_t *)ptr, 0); | |
| if (pread(fd, &pinfo, sizeof(pinfo), offset) != sizeof(pinfo)) { | |
| error_setg_errno(errp, errno, "Cannot read pagemap"); | |
| goto out; | |
| } | |
| if ((pinfo & (1ull << 63)) == 0) { | |
| error_setg(errp, "Page not present"); | |
| goto out; | |
| } | |
| ret = (pinfo & 0x007fffffffffffffull) * pagesize; | |
| ret |= addr & (pagesize - 1); | |
| out: | |
| close(fd); | |
| return ret; | |
| } | |
| void hmp_gpa2hpa(MonitorHMP *hmp, const QDict *qdict) | |
| { | |
| hwaddr addr = qdict_get_int(qdict, "addr"); | |
| Error *local_err = NULL; | |
| MemoryRegion *mr = NULL; | |
| void *ptr; | |
| uint64_t physaddr; | |
| ptr = gpa2hva(&mr, addr, 1, &local_err); | |
| if (local_err) { | |
| error_report_err(local_err); | |
| return; | |
| } | |
| physaddr = vtop(ptr, &local_err); | |
| if (local_err) { | |
| error_report_err(local_err); | |
| } else { | |
| monitor_hmp_printf(hmp, "Host physical address for 0x%" HWADDR_PRIx | |
| " (%s) is 0x%" PRIx64 "\n", | |
| addr, mr->name, (uint64_t) physaddr); | |
| } | |
| memory_region_unref(mr); | |
| } | |
| #endif |