mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'qmp/queue/qmp' into staging
* qmp/queue/qmp: (29 commits) Add 'query-events' command to QMP to query async events qapi: convert netdev_del qapi: convert netdev_add net: net_client_init(): use error_set() net: purge the monitor object from all init functions qemu-config: introduce qemu_find_opts_err() qemu-config: find_list(): use error_set() qerror: introduce QERR_INVALID_OPTION_GROUP qemu-option: qemu_opts_from_qdict(): use error_set() qemu-option: introduce qemu_opt_set_err() qemu-option: opt_set(): use error_set() qemu-option: qemu_opts_validate(): use error_set() qemu-option: qemu_opt_parse(): use error_set() qemu-option: parse_option_size(): use error_set() qemu-option: parse_option_bool(): use error_set() qemu-option: parse_option_number(): use error_set() qemu-option: qemu_opts_create(): use error_set() introduce a new monitor command 'dump-guest-memory' to dump guest's memory make gdb_id() generally avialable and rename it to cpu_index() target-i386: Add API to get note's size ...
This commit is contained in:
@@ -192,6 +192,9 @@ obj-$(CONFIG_KVM) += kvm.o kvm-all.o
|
||||
obj-$(CONFIG_NO_KVM) += kvm-stub.o
|
||||
obj-$(CONFIG_VGA) += vga.o
|
||||
obj-y += memory.o savevm.o cputlb.o
|
||||
obj-y += memory_mapping.o
|
||||
obj-$(CONFIG_HAVE_GET_MEMORY_MAPPING) += arch_memory_mapping.o
|
||||
obj-$(CONFIG_HAVE_CORE_DUMP) += arch_dump.o
|
||||
LIBS+=-lz
|
||||
|
||||
obj-i386-$(CONFIG_KVM) += hyperv.o
|
||||
@@ -402,6 +405,8 @@ obj-y += $(addprefix ../, $(trace-obj-y))
|
||||
|
||||
endif # CONFIG_SOFTMMU
|
||||
|
||||
obj-y += dump.o
|
||||
|
||||
ifndef CONFIG_LINUX_USER
|
||||
ifndef CONFIG_BSD_USER
|
||||
# libcacard needs qemu-thread support, and besides is only needed by devices
|
||||
|
||||
+1
-1
@@ -569,7 +569,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
break;
|
||||
case IF_VIRTIO:
|
||||
/* add virtio block device */
|
||||
opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0);
|
||||
opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, NULL);
|
||||
if (arch_type == QEMU_ARCH_S390X) {
|
||||
qemu_opt_set(opts, "driver", "virtio-blk-s390");
|
||||
} else {
|
||||
|
||||
@@ -3729,6 +3729,10 @@ case "$target_arch2" in
|
||||
fi
|
||||
fi
|
||||
esac
|
||||
case "$target_arch2" in
|
||||
i386|x86_64)
|
||||
echo "CONFIG_HAVE_GET_MEMORY_MAPPING=y" >> $config_target_mak
|
||||
esac
|
||||
if test "$target_arch2" = "ppc64" -a "$fdt" = "yes"; then
|
||||
echo "CONFIG_PSERIES=y" >> $config_target_mak
|
||||
fi
|
||||
@@ -3744,6 +3748,10 @@ if test "$target_softmmu" = "yes" ; then
|
||||
if test "$smartcard_nss" = "yes" ; then
|
||||
echo "subdir-$target: subdir-libcacard" >> $config_host_mak
|
||||
fi
|
||||
case "$target_arch2" in
|
||||
i386|x86_64)
|
||||
echo "CONFIG_HAVE_CORE_DUMP=y" >> $config_target_mak
|
||||
esac
|
||||
fi
|
||||
if test "$target_user_only" = "yes" ; then
|
||||
echo "CONFIG_USER_ONLY=y" >> $config_target_mak
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
#include "qemu-common.h"
|
||||
#include "qemu-tls.h"
|
||||
#include "cpu-common.h"
|
||||
#include "memory_mapping.h"
|
||||
#include "dump.h"
|
||||
|
||||
/* some important defines:
|
||||
*
|
||||
@@ -523,4 +525,72 @@ void dump_exec_info(FILE *f, fprintf_function cpu_fprintf);
|
||||
int cpu_memory_rw_debug(CPUArchState *env, target_ulong addr,
|
||||
uint8_t *buf, int len, int is_write);
|
||||
|
||||
#if defined(CONFIG_HAVE_GET_MEMORY_MAPPING)
|
||||
int cpu_get_memory_mapping(MemoryMappingList *list, CPUArchState *env);
|
||||
bool cpu_paging_enabled(CPUArchState *env);
|
||||
#else
|
||||
static inline int cpu_get_memory_mapping(MemoryMappingList *list,
|
||||
CPUArchState *env)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline bool cpu_paging_enabled(CPUArchState *env)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef int (*write_core_dump_function)(void *buf, size_t size, void *opaque);
|
||||
#if defined(CONFIG_HAVE_CORE_DUMP)
|
||||
int cpu_write_elf64_note(write_core_dump_function f, CPUArchState *env,
|
||||
int cpuid, void *opaque);
|
||||
int cpu_write_elf32_note(write_core_dump_function f, CPUArchState *env,
|
||||
int cpuid, void *opaque);
|
||||
int cpu_write_elf64_qemunote(write_core_dump_function f, CPUArchState *env,
|
||||
void *opaque);
|
||||
int cpu_write_elf32_qemunote(write_core_dump_function f, CPUArchState *env,
|
||||
void *opaque);
|
||||
int cpu_get_dump_info(ArchDumpInfo *info);
|
||||
size_t cpu_get_note_size(int class, int machine, int nr_cpus);
|
||||
#else
|
||||
static inline int cpu_write_elf64_note(write_core_dump_function f,
|
||||
CPUArchState *env, int cpuid,
|
||||
void *opaque)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int cpu_write_elf32_note(write_core_dump_function f,
|
||||
CPUArchState *env, int cpuid,
|
||||
void *opaque)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int cpu_write_elf64_qemunote(write_core_dump_function f,
|
||||
CPUArchState *env,
|
||||
void *opaque)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int cpu_write_elf32_qemunote(write_core_dump_function f,
|
||||
CPUArchState *env,
|
||||
void *opaque)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int cpu_get_dump_info(ArchDumpInfo *info)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int cpu_get_note_size(int class, int machine, int nr_cpus)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CPU_ALL_H */
|
||||
|
||||
@@ -71,6 +71,10 @@ void cpu_physical_memory_unmap(void *buffer, target_phys_addr_t len,
|
||||
void *cpu_register_map_client(void *opaque, void (*callback)(void *opaque));
|
||||
void cpu_unregister_map_client(void *cookie);
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
bool cpu_physical_memory_is_io(target_phys_addr_t phys_addr);
|
||||
#endif
|
||||
|
||||
/* Coalesced MMIO regions are areas where write operations can be reordered.
|
||||
* This usually implies that write operations are side-effect free. This allows
|
||||
* batching which can make a major impact on performance when using
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* QEMU dump
|
||||
*
|
||||
* Copyright Fujitsu, Corp. 2011, 2012
|
||||
*
|
||||
* Authors:
|
||||
* Wen Congyang <wency@cn.fujitsu.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DUMP_H
|
||||
#define DUMP_H
|
||||
|
||||
typedef struct ArchDumpInfo {
|
||||
int d_machine; /* Architecture */
|
||||
int d_endian; /* ELFDATA2LSB or ELFDATA2MSB */
|
||||
int d_class; /* ELFCLASS32 or ELFCLASS64 */
|
||||
} ArchDumpInfo;
|
||||
|
||||
#endif
|
||||
@@ -1037,6 +1037,11 @@ typedef struct elf64_sym {
|
||||
|
||||
#define EI_NIDENT 16
|
||||
|
||||
/* Special value for e_phnum. This indicates that the real number of
|
||||
program headers is too large to fit into e_phnum. Instead the real
|
||||
value is in the field sh_info of section 0. */
|
||||
#define PN_XNUM 0xffff
|
||||
|
||||
typedef struct elf32_hdr{
|
||||
unsigned char e_ident[EI_NIDENT];
|
||||
Elf32_Half e_type;
|
||||
|
||||
@@ -4336,3 +4336,15 @@ bool virtio_is_big_endian(void)
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
bool cpu_physical_memory_is_io(target_phys_addr_t phys_addr)
|
||||
{
|
||||
MemoryRegionSection *section;
|
||||
|
||||
section = phys_page_find(phys_addr >> TARGET_PAGE_BITS);
|
||||
|
||||
return !(memory_region_is_ram(section->mr) ||
|
||||
memory_region_is_romd(section->mr));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1937,21 +1937,12 @@ static void gdb_set_cpu_pc(GDBState *s, target_ulong pc)
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int gdb_id(CPUArchState *env)
|
||||
{
|
||||
#if defined(CONFIG_USER_ONLY) && defined(CONFIG_USE_NPTL)
|
||||
return env->host_tid;
|
||||
#else
|
||||
return env->cpu_index + 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static CPUArchState *find_cpu(uint32_t thread_id)
|
||||
{
|
||||
CPUArchState *env;
|
||||
|
||||
for (env = first_cpu; env != NULL; env = env->next_cpu) {
|
||||
if (gdb_id(env) == thread_id) {
|
||||
if (cpu_index(env) == thread_id) {
|
||||
return env;
|
||||
}
|
||||
}
|
||||
@@ -1979,7 +1970,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
|
||||
case '?':
|
||||
/* TODO: Make this return the correct value for user-mode. */
|
||||
snprintf(buf, sizeof(buf), "T%02xthread:%02x;", GDB_SIGNAL_TRAP,
|
||||
gdb_id(s->c_cpu));
|
||||
cpu_index(s->c_cpu));
|
||||
put_packet(s, buf);
|
||||
/* Remove all the breakpoints when this query is issued,
|
||||
* because gdb is doing and initial connect and the state
|
||||
@@ -2274,7 +2265,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
|
||||
} else if (strcmp(p,"sThreadInfo") == 0) {
|
||||
report_cpuinfo:
|
||||
if (s->query_cpu) {
|
||||
snprintf(buf, sizeof(buf), "m%x", gdb_id(s->query_cpu));
|
||||
snprintf(buf, sizeof(buf), "m%x", cpu_index(s->query_cpu));
|
||||
put_packet(s, buf);
|
||||
s->query_cpu = s->query_cpu->next_cpu;
|
||||
} else
|
||||
@@ -2422,7 +2413,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state)
|
||||
}
|
||||
snprintf(buf, sizeof(buf),
|
||||
"T%02xthread:%02x;%swatch:" TARGET_FMT_lx ";",
|
||||
GDB_SIGNAL_TRAP, gdb_id(env), type,
|
||||
GDB_SIGNAL_TRAP, cpu_index(env), type,
|
||||
env->watchpoint_hit->vaddr);
|
||||
env->watchpoint_hit = NULL;
|
||||
goto send_packet;
|
||||
@@ -2455,7 +2446,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state)
|
||||
ret = GDB_SIGNAL_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, gdb_id(env));
|
||||
snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, cpu_index(env));
|
||||
|
||||
send_packet:
|
||||
put_packet(s, buf);
|
||||
|
||||
@@ -30,6 +30,15 @@ void gdb_register_coprocessor(CPUArchState *env,
|
||||
gdb_reg_cb get_reg, gdb_reg_cb set_reg,
|
||||
int num_regs, const char *xml, int g_pos);
|
||||
|
||||
static inline int cpu_index(CPUArchState *env)
|
||||
{
|
||||
#if defined(CONFIG_USER_ONLY) && defined(CONFIG_USE_NPTL)
|
||||
return env->host_tid;
|
||||
#else
|
||||
return env->cpu_index + 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
|
||||
+30
-4
@@ -878,6 +878,34 @@ server will ask the spice/vnc client to automatically reconnect using the
|
||||
new parameters (if specified) once the vm migration finished successfully.
|
||||
ETEXI
|
||||
|
||||
#if defined(CONFIG_HAVE_CORE_DUMP)
|
||||
{
|
||||
.name = "dump-guest-memory",
|
||||
.args_type = "paging:-p,protocol:s,begin:i?,length:i?",
|
||||
.params = "[-p] protocol [begin] [length]",
|
||||
.help = "dump guest memory to file"
|
||||
"\n\t\t\t begin(optional): the starting physical address"
|
||||
"\n\t\t\t length(optional): the memory size, in bytes",
|
||||
.user_print = monitor_user_noop,
|
||||
.mhandler.cmd = hmp_dump_guest_memory,
|
||||
},
|
||||
|
||||
|
||||
STEXI
|
||||
@item dump-guest-memory [-p] @var{protocol} @var{begin} @var{length}
|
||||
@findex dump-guest-memory
|
||||
Dump guest memory to @var{protocol}. The file can be processed with crash or
|
||||
gdb.
|
||||
protocol: destination file(started with "file:") or destination file
|
||||
descriptor (started with "fd:")
|
||||
paging: do paging to get guest's memory mapping
|
||||
begin: the starting physical address. It's optional, and should be
|
||||
specified with length together.
|
||||
length: the memory size, in bytes. It's optional, and should be specified
|
||||
with begin together.
|
||||
ETEXI
|
||||
#endif
|
||||
|
||||
{
|
||||
.name = "snapshot_blkdev",
|
||||
.args_type = "reuse:-n,device:B,snapshot-file:s?,format:s?",
|
||||
@@ -1009,8 +1037,7 @@ ETEXI
|
||||
.args_type = "netdev:O",
|
||||
.params = "[user|tap|socket],id=str[,prop=value][,...]",
|
||||
.help = "add host network device",
|
||||
.user_print = monitor_user_noop,
|
||||
.mhandler.cmd_new = do_netdev_add,
|
||||
.mhandler.cmd = hmp_netdev_add,
|
||||
},
|
||||
|
||||
STEXI
|
||||
@@ -1024,8 +1051,7 @@ ETEXI
|
||||
.args_type = "id:s",
|
||||
.params = "id",
|
||||
.help = "remove host network device",
|
||||
.user_print = monitor_user_noop,
|
||||
.mhandler.cmd_new = do_netdev_del,
|
||||
.mhandler.cmd = hmp_netdev_del,
|
||||
},
|
||||
|
||||
STEXI
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
*/
|
||||
|
||||
#include "hmp.h"
|
||||
#include "net.h"
|
||||
#include "qemu-option.h"
|
||||
#include "qemu-timer.h"
|
||||
#include "qmp-commands.h"
|
||||
|
||||
@@ -947,3 +949,53 @@ void hmp_device_del(Monitor *mon, const QDict *qdict)
|
||||
qmp_device_del(id, &err);
|
||||
hmp_handle_error(mon, &err);
|
||||
}
|
||||
|
||||
void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
Error *errp = NULL;
|
||||
int paging = qdict_get_try_bool(qdict, "paging", 0);
|
||||
const char *file = qdict_get_str(qdict, "protocol");
|
||||
bool has_begin = qdict_haskey(qdict, "begin");
|
||||
bool has_length = qdict_haskey(qdict, "length");
|
||||
int64_t begin = 0;
|
||||
int64_t length = 0;
|
||||
|
||||
if (has_begin) {
|
||||
begin = qdict_get_int(qdict, "begin");
|
||||
}
|
||||
if (has_length) {
|
||||
length = qdict_get_int(qdict, "length");
|
||||
}
|
||||
|
||||
qmp_dump_guest_memory(paging, file, has_begin, begin, has_length, length,
|
||||
&errp);
|
||||
hmp_handle_error(mon, &errp);
|
||||
}
|
||||
|
||||
void hmp_netdev_add(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
Error *err = NULL;
|
||||
QemuOpts *opts;
|
||||
|
||||
opts = qemu_opts_from_qdict(qemu_find_opts("netdev"), qdict, &err);
|
||||
if (error_is_set(&err)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
netdev_add(opts, &err);
|
||||
if (error_is_set(&err)) {
|
||||
qemu_opts_del(opts);
|
||||
}
|
||||
|
||||
out:
|
||||
hmp_handle_error(mon, &err);
|
||||
}
|
||||
|
||||
void hmp_netdev_del(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
const char *id = qdict_get_str(qdict, "id");
|
||||
Error *err = NULL;
|
||||
|
||||
qmp_netdev_del(id, &err);
|
||||
hmp_handle_error(mon, &err);
|
||||
}
|
||||
|
||||
@@ -61,5 +61,8 @@ void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict);
|
||||
void hmp_block_job_cancel(Monitor *mon, const QDict *qdict);
|
||||
void hmp_migrate(Monitor *mon, const QDict *qdict);
|
||||
void hmp_device_del(Monitor *mon, const QDict *qdict);
|
||||
void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict);
|
||||
void hmp_netdev_add(Monitor *mon, const QDict *qdict);
|
||||
void hmp_netdev_del(Monitor *mon, const QDict *qdict);
|
||||
|
||||
#endif
|
||||
|
||||
+6
-2
@@ -39,6 +39,7 @@ static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon,
|
||||
const char *devaddr,
|
||||
const char *opts_str)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
QemuOpts *opts;
|
||||
PCIBus *bus;
|
||||
int ret, devfn;
|
||||
@@ -60,9 +61,12 @@ static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon,
|
||||
|
||||
qemu_opt_set(opts, "type", "nic");
|
||||
|
||||
ret = net_client_init(mon, opts, 0);
|
||||
if (ret < 0)
|
||||
ret = net_client_init(opts, 0, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
return NULL;
|
||||
}
|
||||
if (nd_table[ret].devaddr) {
|
||||
monitor_printf(mon, "Parameter addr not supported\n");
|
||||
return NULL;
|
||||
|
||||
+5
-2
@@ -554,10 +554,13 @@ void do_info_qdm(Monitor *mon)
|
||||
|
||||
int do_device_add(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
QemuOpts *opts;
|
||||
|
||||
opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict);
|
||||
if (!opts) {
|
||||
opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
return -1;
|
||||
}
|
||||
if (!monitor_cur_is_qmp() && qdev_device_help(opts)) {
|
||||
|
||||
@@ -1356,6 +1356,7 @@ static int usb_net_initfn(USBDevice *dev)
|
||||
|
||||
static USBDevice *usb_net_init(USBBus *bus, const char *cmdline)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
USBDevice *dev;
|
||||
QemuOpts *opts;
|
||||
int idx;
|
||||
@@ -1367,8 +1368,10 @@ static USBDevice *usb_net_init(USBBus *bus, const char *cmdline)
|
||||
qemu_opt_set(opts, "type", "nic");
|
||||
qemu_opt_set(opts, "model", "usb");
|
||||
|
||||
idx = net_client_init(NULL, opts, 0);
|
||||
if (idx == -1) {
|
||||
idx = net_client_init(opts, 0, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -584,7 +584,7 @@ static USBDevice *usb_msd_init(USBBus *bus, const char *filename)
|
||||
|
||||
/* parse -usbdevice disk: syntax into drive opts */
|
||||
snprintf(id, sizeof(id), "usb%d", nr++);
|
||||
opts = qemu_opts_create(qemu_find_opts("drive"), id, 0);
|
||||
opts = qemu_opts_create(qemu_find_opts("drive"), id, 0, NULL);
|
||||
|
||||
p1 = strchr(filename, ':');
|
||||
if (p1++) {
|
||||
|
||||
+1
-1
@@ -66,7 +66,7 @@ int select_watchdog(const char *p)
|
||||
QLIST_FOREACH(model, &watchdog_list, entry) {
|
||||
if (strcasecmp(model->wdt_name, p) == 0) {
|
||||
/* add the device */
|
||||
opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0);
|
||||
opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, NULL);
|
||||
qemu_opt_set(opts, "driver", p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* QEMU memory mapping
|
||||
*
|
||||
* Copyright Fujitsu, Corp. 2011, 2012
|
||||
*
|
||||
* Authors:
|
||||
* Wen Congyang <wency@cn.fujitsu.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "cpu.h"
|
||||
#include "cpu-all.h"
|
||||
#include "memory_mapping.h"
|
||||
|
||||
static void memory_mapping_list_add_mapping_sorted(MemoryMappingList *list,
|
||||
MemoryMapping *mapping)
|
||||
{
|
||||
MemoryMapping *p;
|
||||
|
||||
QTAILQ_FOREACH(p, &list->head, next) {
|
||||
if (p->phys_addr >= mapping->phys_addr) {
|
||||
QTAILQ_INSERT_BEFORE(p, mapping, next);
|
||||
return;
|
||||
}
|
||||
}
|
||||
QTAILQ_INSERT_TAIL(&list->head, mapping, next);
|
||||
}
|
||||
|
||||
static void create_new_memory_mapping(MemoryMappingList *list,
|
||||
target_phys_addr_t phys_addr,
|
||||
target_phys_addr_t virt_addr,
|
||||
ram_addr_t length)
|
||||
{
|
||||
MemoryMapping *memory_mapping;
|
||||
|
||||
memory_mapping = g_malloc(sizeof(MemoryMapping));
|
||||
memory_mapping->phys_addr = phys_addr;
|
||||
memory_mapping->virt_addr = virt_addr;
|
||||
memory_mapping->length = length;
|
||||
list->last_mapping = memory_mapping;
|
||||
list->num++;
|
||||
memory_mapping_list_add_mapping_sorted(list, memory_mapping);
|
||||
}
|
||||
|
||||
static inline bool mapping_contiguous(MemoryMapping *map,
|
||||
target_phys_addr_t phys_addr,
|
||||
target_phys_addr_t virt_addr)
|
||||
{
|
||||
return phys_addr == map->phys_addr + map->length &&
|
||||
virt_addr == map->virt_addr + map->length;
|
||||
}
|
||||
|
||||
/*
|
||||
* [map->phys_addr, map->phys_addr + map->length) and
|
||||
* [phys_addr, phys_addr + length) have intersection?
|
||||
*/
|
||||
static inline bool mapping_have_same_region(MemoryMapping *map,
|
||||
target_phys_addr_t phys_addr,
|
||||
ram_addr_t length)
|
||||
{
|
||||
return !(phys_addr + length < map->phys_addr ||
|
||||
phys_addr >= map->phys_addr + map->length);
|
||||
}
|
||||
|
||||
/*
|
||||
* [map->phys_addr, map->phys_addr + map->length) and
|
||||
* [phys_addr, phys_addr + length) have intersection. The virtual address in the
|
||||
* intersection are the same?
|
||||
*/
|
||||
static inline bool mapping_conflict(MemoryMapping *map,
|
||||
target_phys_addr_t phys_addr,
|
||||
target_phys_addr_t virt_addr)
|
||||
{
|
||||
return virt_addr - map->virt_addr != phys_addr - map->phys_addr;
|
||||
}
|
||||
|
||||
/*
|
||||
* [map->virt_addr, map->virt_addr + map->length) and
|
||||
* [virt_addr, virt_addr + length) have intersection. And the physical address
|
||||
* in the intersection are the same.
|
||||
*/
|
||||
static inline void mapping_merge(MemoryMapping *map,
|
||||
target_phys_addr_t virt_addr,
|
||||
ram_addr_t length)
|
||||
{
|
||||
if (virt_addr < map->virt_addr) {
|
||||
map->length += map->virt_addr - virt_addr;
|
||||
map->virt_addr = virt_addr;
|
||||
}
|
||||
|
||||
if ((virt_addr + length) >
|
||||
(map->virt_addr + map->length)) {
|
||||
map->length = virt_addr + length - map->virt_addr;
|
||||
}
|
||||
}
|
||||
|
||||
void memory_mapping_list_add_merge_sorted(MemoryMappingList *list,
|
||||
target_phys_addr_t phys_addr,
|
||||
target_phys_addr_t virt_addr,
|
||||
ram_addr_t length)
|
||||
{
|
||||
MemoryMapping *memory_mapping, *last_mapping;
|
||||
|
||||
if (QTAILQ_EMPTY(&list->head)) {
|
||||
create_new_memory_mapping(list, phys_addr, virt_addr, length);
|
||||
return;
|
||||
}
|
||||
|
||||
last_mapping = list->last_mapping;
|
||||
if (last_mapping) {
|
||||
if (mapping_contiguous(last_mapping, phys_addr, virt_addr)) {
|
||||
last_mapping->length += length;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH(memory_mapping, &list->head, next) {
|
||||
if (mapping_contiguous(memory_mapping, phys_addr, virt_addr)) {
|
||||
memory_mapping->length += length;
|
||||
list->last_mapping = memory_mapping;
|
||||
return;
|
||||
}
|
||||
|
||||
if (phys_addr + length < memory_mapping->phys_addr) {
|
||||
/* create a new region before memory_mapping */
|
||||
break;
|
||||
}
|
||||
|
||||
if (mapping_have_same_region(memory_mapping, phys_addr, length)) {
|
||||
if (mapping_conflict(memory_mapping, phys_addr, virt_addr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* merge this region into memory_mapping */
|
||||
mapping_merge(memory_mapping, virt_addr, length);
|
||||
list->last_mapping = memory_mapping;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* this region can not be merged into any existed memory mapping. */
|
||||
create_new_memory_mapping(list, phys_addr, virt_addr, length);
|
||||
}
|
||||
|
||||
void memory_mapping_list_free(MemoryMappingList *list)
|
||||
{
|
||||
MemoryMapping *p, *q;
|
||||
|
||||
QTAILQ_FOREACH_SAFE(p, &list->head, next, q) {
|
||||
QTAILQ_REMOVE(&list->head, p, next);
|
||||
g_free(p);
|
||||
}
|
||||
|
||||
list->num = 0;
|
||||
list->last_mapping = NULL;
|
||||
}
|
||||
|
||||
void memory_mapping_list_init(MemoryMappingList *list)
|
||||
{
|
||||
list->num = 0;
|
||||
list->last_mapping = NULL;
|
||||
QTAILQ_INIT(&list->head);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_HAVE_GET_MEMORY_MAPPING)
|
||||
|
||||
static CPUArchState *find_paging_enabled_cpu(CPUArchState *start_cpu)
|
||||
{
|
||||
CPUArchState *env;
|
||||
|
||||
for (env = start_cpu; env != NULL; env = env->next_cpu) {
|
||||
if (cpu_paging_enabled(env)) {
|
||||
return env;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int qemu_get_guest_memory_mapping(MemoryMappingList *list)
|
||||
{
|
||||
CPUArchState *env, *first_paging_enabled_cpu;
|
||||
RAMBlock *block;
|
||||
ram_addr_t offset, length;
|
||||
int ret;
|
||||
|
||||
first_paging_enabled_cpu = find_paging_enabled_cpu(first_cpu);
|
||||
if (first_paging_enabled_cpu) {
|
||||
for (env = first_paging_enabled_cpu; env != NULL; env = env->next_cpu) {
|
||||
ret = cpu_get_memory_mapping(list, env);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the guest doesn't use paging, the virtual address is equal to physical
|
||||
* address.
|
||||
*/
|
||||
QLIST_FOREACH(block, &ram_list.blocks, next) {
|
||||
offset = block->offset;
|
||||
length = block->length;
|
||||
create_new_memory_mapping(list, offset, offset, length);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void qemu_get_guest_simple_memory_mapping(MemoryMappingList *list)
|
||||
{
|
||||
RAMBlock *block;
|
||||
|
||||
QLIST_FOREACH(block, &ram_list.blocks, next) {
|
||||
create_new_memory_mapping(list, block->offset, 0, block->length);
|
||||
}
|
||||
}
|
||||
|
||||
void memory_mapping_filter(MemoryMappingList *list, int64_t begin,
|
||||
int64_t length)
|
||||
{
|
||||
MemoryMapping *cur, *next;
|
||||
|
||||
QTAILQ_FOREACH_SAFE(cur, &list->head, next, next) {
|
||||
if (cur->phys_addr >= begin + length ||
|
||||
cur->phys_addr + cur->length <= begin) {
|
||||
QTAILQ_REMOVE(&list->head, cur, next);
|
||||
list->num--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cur->phys_addr < begin) {
|
||||
cur->length -= begin - cur->phys_addr;
|
||||
if (cur->virt_addr) {
|
||||
cur->virt_addr += begin - cur->phys_addr;
|
||||
}
|
||||
cur->phys_addr = begin;
|
||||
}
|
||||
|
||||
if (cur->phys_addr + cur->length > begin + length) {
|
||||
cur->length -= cur->phys_addr + cur->length - begin - length;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user