summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnthony Liguori <aliguori@us.ibm.com>2012-06-06 20:57:56 +0800
committerAnthony Liguori <aliguori@us.ibm.com>2012-06-06 20:57:56 +0800
commit349417004a0f7cf5518a998dca755cd06f6c212b (patch)
tree70bad616da7eb6d55a1d1781fdd64915a532bbcb
parente1ae9a7a78be8d894c111059955fe0e25ec4c203 (diff)
parent4860853d60ecea44b65e9cdefce980de3a641dce (diff)
downloadqemu-349417004a0f7cf5518a998dca755cd06f6c212b.tar.gz
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 ...
-rw-r--r--Makefile.target5
-rw-r--r--blockdev.c2
-rwxr-xr-xconfigure8
-rw-r--r--cpu-all.h70
-rw-r--r--cpu-common.h4
-rw-r--r--dump.c883
-rw-r--r--dump.h23
-rw-r--r--elf.h5
-rw-r--r--exec.c12
-rw-r--r--gdbstub.c19
-rw-r--r--gdbstub.h9
-rw-r--r--hmp-commands.hx34
-rw-r--r--hmp.c52
-rw-r--r--hmp.h3
-rw-r--r--hw/pci-hotplug.c8
-rw-r--r--hw/qdev-monitor.c7
-rw-r--r--hw/usb/dev-network.c7
-rw-r--r--hw/usb/dev-storage.c2
-rw-r--r--hw/watchdog.c2
-rw-r--r--memory_mapping.c249
-rw-r--r--memory_mapping.h74
-rw-r--r--monitor.c107
-rw-r--r--monitor.h4
-rw-r--r--net.c104
-rw-r--r--net.h6
-rw-r--r--net/dump.c2
-rw-r--r--net/dump.h3
-rw-r--r--net/slirp.c5
-rw-r--r--net/slirp.h5
-rw-r--r--net/socket.c8
-rw-r--r--net/socket.h3
-rw-r--r--net/tap-win32.c2
-rw-r--r--net/tap.c9
-rw-r--r--net/tap.h5
-rw-r--r--net/vde.c2
-rw-r--r--net/vde.h2
-rw-r--r--qapi-schema.json107
-rw-r--r--qemu-char.c8
-rw-r--r--qemu-config.c43
-rw-r--r--qemu-config.h3
-rw-r--r--qemu-option.c175
-rw-r--r--qemu-option.h11
-rw-r--r--qemu-sockets.c8
-rw-r--r--qerror.c4
-rw-r--r--qerror.h3
-rw-r--r--qmp-commands.hx83
-rw-r--r--target-i386/arch_dump.c449
-rw-r--r--target-i386/arch_memory_mapping.c271
-rw-r--r--vl.c22
49 files changed, 2685 insertions, 247 deletions
diff --git a/Makefile.target b/Makefile.target
index 15829041c7..c94abfd3a4 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -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
diff --git a/blockdev.c b/blockdev.c
index 67895b25d5..622ecba04e 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -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 {
diff --git a/configure b/configure
index 1f338f8dce..76dd57fc27 100755
--- a/configure
+++ b/configure
@@ -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
diff --git a/cpu-all.h b/cpu-all.h
index 3a93c0c98a..e8749de2b9 100644
--- a/cpu-all.h
+++ b/cpu-all.h
@@ -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 */
diff --git a/cpu-common.h b/cpu-common.h
index dca5175652..1fe3280701 100644
--- a/cpu-common.h
+++ b/cpu-common.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
diff --git a/dump.c b/dump.c
new file mode 100644
index 0000000000..0ca14f87ed
--- /dev/null
+++ b/dump.c
@@ -0,0 +1,883 @@
+/*
+ * 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.
+ *
+ */
+
+#include "qemu-common.h"
+#include <unistd.h>
+#include "elf.h"
+#include <sys/procfs.h>
+#include <glib.h>
+#include "cpu.h"
+#include "cpu-all.h"
+#include "targphys.h"
+#include "monitor.h"
+#include "kvm.h"
+#include "dump.h"
+#include "sysemu.h"
+#include "bswap.h"
+#include "memory_mapping.h"
+#include "error.h"
+#include "qmp-commands.h"
+#include "gdbstub.h"
+
+#if defined(CONFIG_HAVE_CORE_DUMP)
+static uint16_t cpu_convert_to_target16(uint16_t val, int endian)
+{
+ if (endian == ELFDATA2LSB) {
+ val = cpu_to_le16(val);
+ } else {
+ val = cpu_to_be16(val);
+ }
+
+ return val;
+}
+
+static uint32_t cpu_convert_to_target32(uint32_t val, int endian)
+{
+ if (endian == ELFDATA2LSB) {
+ val = cpu_to_le32(val);
+ } else {
+ val = cpu_to_be32(val);
+ }
+
+ return val;
+}
+
+static uint64_t cpu_convert_to_target64(uint64_t val, int endian)
+{
+ if (endian == ELFDATA2LSB) {
+ val = cpu_to_le64(val);
+ } else {
+ val = cpu_to_be64(val);
+ }
+
+ return val;
+}
+
+typedef struct DumpState {
+ ArchDumpInfo dump_info;
+ MemoryMappingList list;
+ uint16_t phdr_num;
+ uint32_t sh_info;
+ bool have_section;
+ bool resume;
+ size_t note_size;
+ target_phys_addr_t memory_offset;
+ int fd;
+
+ RAMBlock *block;
+ ram_addr_t start;
+ bool has_filter;
+ int64_t begin;
+ int64_t length;
+ Error **errp;
+} DumpState;
+
+static int dump_cleanup(DumpState *s)
+{
+ int ret = 0;
+
+ memory_mapping_list_free(&s->list);
+ if (s->fd != -1) {
+ close(s->fd);
+ }
+ if (s->resume) {
+ vm_start();
+ }
+
+ return ret;
+}
+
+static void dump_error(DumpState *s, const char *reason)
+{
+ dump_cleanup(s);
+}
+
+static int fd_write_vmcore(void *buf, size_t size, void *opaque)
+{
+ DumpState *s = opaque;
+ int fd = s->fd;
+ size_t writen_size;
+
+ /* The fd may be passed from user, and it can be non-blocked */
+ while (size) {
+ writen_size = qemu_write_full(fd, buf, size);
+ if (writen_size != size && errno != EAGAIN) {
+ return -1;
+ }
+
+ buf += writen_size;
+ size -= writen_size;
+ }
+
+ return 0;
+}
+
+static int write_elf64_header(DumpState *s)
+{
+ Elf64_Ehdr elf_header;
+ int ret;
+ int endian = s->dump_info.d_endian;
+
+ memset(&elf_header, 0, sizeof(Elf64_Ehdr));
+ memcpy(&elf_header, ELFMAG, SELFMAG);
+ elf_header.e_ident[EI_CLASS] = ELFCLASS64;
+ elf_header.e_ident[EI_DATA] = s->dump_info.d_endian;
+ elf_header.e_ident[EI_VERSION] = EV_CURRENT;
+ elf_header.e_type = cpu_convert_to_target16(ET_CORE, endian);
+ elf_header.e_machine = cpu_convert_to_target16(s->dump_info.d_machine,
+ endian);
+ elf_header.e_version = cpu_convert_to_target32(EV_CURRENT, endian);
+ elf_header.e_ehsize = cpu_convert_to_target16(sizeof(elf_header), endian);
+ elf_header.e_phoff = cpu_convert_to_target64(sizeof(Elf64_Ehdr), endian);
+ elf_header.e_phentsize = cpu_convert_to_target16(sizeof(Elf64_Phdr),
+ endian);
+ elf_header.e_phnum = cpu_convert_to_target16(s->phdr_num, endian);
+ if (s->have_section) {
+ uint64_t shoff = sizeof(Elf64_Ehdr) + sizeof(Elf64_Phdr) * s->sh_info;
+
+ elf_header.e_shoff = cpu_convert_to_target64(shoff, endian);
+ elf_header.e_shentsize = cpu_convert_to_target16(sizeof(Elf64_Shdr),
+ endian);
+ elf_header.e_shnum = cpu_convert_to_target16(1, endian);
+ }
+
+ ret = fd_write_vmcore(&elf_header, sizeof(elf_header), s);
+ if (ret < 0) {
+ dump_error(s, "dump: failed to write elf header.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int write_elf32_header(DumpState *s)
+{
+ Elf32_Ehdr elf_header;
+ int ret;
+ int endian = s->dump_info.d_endian;
+
+ memset(&elf_header, 0, sizeof(Elf32_Ehdr));
+ memcpy(&elf_header, ELFMAG, SELFMAG);
+ elf_header.e_ident[EI_CLASS] = ELFCLASS32;
+ elf_header.e_ident[EI_DATA] = endian;
+ elf_header.e_ident[EI_VERSION] = EV_CURRENT;
+ elf_header.e_type = cpu_convert_to_target16(ET_CORE, endian);
+ elf_header.e_machine = cpu_convert_to_target16(s->dump_info.d_machine,
+ endian);
+ elf_header.e_version = cpu_convert_to_target32(EV_CURRENT, endian);
+ elf_header.e_ehsize = cpu_convert_to_target16(sizeof(elf_header), endian);
+ elf_header.e_phoff = cpu_convert_to_target32(sizeof(Elf32_Ehdr), endian);
+ elf_header.e_phentsize = cpu_convert_to_target16(sizeof(Elf32_Phdr),
+ endian);
+ elf_header.e_phnum = cpu_convert_to_target16(s->phdr_num, endian);
+ if (s->have_section) {
+ uint32_t shoff = sizeof(Elf32_Ehdr) + sizeof(Elf32_Phdr) * s->sh_info;
+
+ elf_header.e_shoff = cpu_convert_to_target32(shoff, endian);
+ elf_header.e_shentsize = cpu_convert_to_target16(sizeof(Elf32_Shdr),
+ endian);
+ elf_header.e_shnum = cpu_convert_to_target16(1, endian);
+ }
+
+ ret = fd_write_vmcore(&elf_header, sizeof(elf_header), s);
+ if (ret < 0) {
+ dump_error(s, "dump: failed to write elf header.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int write_elf64_load(DumpState *s, MemoryMapping *memory_mapping,
+ int phdr_index, target_phys_addr_t offset)
+{
+ Elf64_Phdr phdr;
+ int ret;
+ int endian = s->dump_info.d_endian;
+
+ memset(&phdr, 0, sizeof(Elf64_Phdr));
+ phdr.p_type = cpu_convert_to_target32(PT_LOAD, endian);
+ phdr.p_offset = cpu_convert_to_target64(offset, endian);
+ phdr.p_paddr = cpu_convert_to_target64(memory_mapping->phys_addr, endian);
+ if (offset == -1) {
+ /* When the memory is not stored into vmcore, offset will be -1 */
+ phdr.p_filesz = 0;
+ } else {
+ phdr.p_filesz = cpu_convert_to_target64(memory_mapping->length, endian);
+ }
+ phdr.p_memsz = cpu_convert_to_target64(memory_mapping->length, endian);
+ phdr.p_vaddr = cpu_convert_to_target64(memory_mapping->virt_addr, endian);
+
+ ret = fd_write_vmcore(&phdr, sizeof(Elf64_Phdr), s);
+ if (ret < 0) {
+ dump_error(s, "dump: failed to write program header table.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int write_elf32_load(DumpState *s, MemoryMapping *memory_mapping,
+ int phdr_index, target_phys_addr_t offset)
+{
+ Elf32_Phdr phdr;
+ int ret;
+ int endian = s->dump_info.d_endian;
+
+ memset(&phdr, 0, sizeof(Elf32_Phdr));
+ phdr.p_type = cpu_convert_to_target32(PT_LOAD, endian);
+ phdr.p_offset = cpu_convert_to_target32(offset, endian);
+ phdr.p_paddr = cpu_convert_to_target32(memory_mapping->phys_addr, endian);
+ if (offset == -1) {
+ /* When the memory is not stored into vmcore, offset will be -1 */
+ phdr.p_filesz = 0;
+ } else {
+ phdr.p_filesz = cpu_convert_to_target32(memory_mapping->length, endian);
+ }
+ phdr.p_memsz = cpu_convert_to_target32(memory_mapping->length, endian);
+ phdr.p_vaddr = cpu_convert_to_target32(memory_mapping->virt_addr, endian);
+
+ ret = fd_write_vmcore(&phdr, sizeof(Elf32_Phdr), s);
+ if (ret < 0) {
+ dump_error(s, "dump: failed to write program header table.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int write_elf64_note(DumpState *s)
+{
+ Elf64_Phdr phdr;
+ int endian = s->dump_info.d_endian;
+ target_phys_addr_t begin = s->memory_offset - s->note_size;
+ int ret;
+
+ memset(&phdr, 0, sizeof(Elf64_Phdr));
+ phdr.p_type = cpu_convert_to_target32(PT_NOTE, endian);
+ phdr.p_offset = cpu_convert_to_target64(begin, endian);
+ phdr.p_paddr = 0;
+ phdr.p_filesz = cpu_convert_to_target64(s->note_size, endian);
+ phdr.p_memsz = cpu_convert_to_target64(s->note_size, endian);
+ phdr.p_vaddr = 0;
+
+ ret = fd_write_vmcore(&phdr, sizeof(Elf64_Phdr), s);
+ if (ret < 0) {
+ dump_error(s, "dump: failed to write program header table.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int write_elf64_notes(DumpState *s)
+{
+ CPUArchState *env;
+ int ret;
+ int id;
+
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ id = cpu_index(env);
+ ret = cpu_write_elf64_note(fd_write_vmcore, env, id, s);
+ if (ret < 0) {
+ dump_error(s, "dump: failed to write elf notes.\n");
+ return -1;
+ }
+ }
+
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ ret = cpu_write_elf64_qemunote(fd_write_vmcore, env, s);
+ if (ret < 0) {
+ dump_error(s, "dump: failed to write CPU status.\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int write_elf32_note(DumpState *s)
+{
+ target_phys_addr_t begin = s->memory_offset - s->note_size;
+ Elf32_Phdr phdr;
+ int endian = s->dump_info.d_endian;
+ int ret;
+
+ memset(&phdr, 0, sizeof(Elf32_Phdr));
+ phdr.p_type = cpu_convert_to_target32(PT_NOTE, endian);
+ phdr.p_offset = cpu_convert_to_target32(begin, endian);
+ phdr.p_paddr = 0;
+ phdr.p_filesz = cpu_convert_to_target32(s->note_size, endian);
+ phdr.p_memsz = cpu_convert_to_target32(s->note_size, endian);
+ phdr.p_vaddr = 0;
+
+ ret = fd_write_vmcore(&phdr, sizeof(Elf32_Phdr), s);
+ if (ret < 0) {
+ dump_error(s, "dump: failed to write program header table.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int write_elf32_notes(DumpState *s)
+{
+ CPUArchState *env;
+ int ret;
+ int id;
+
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ id = cpu_index(env);
+ ret = cpu_write_elf32_note(fd_write_vmcore, env, id, s);
+ if (ret < 0) {
+ dump_error(s, "dump: failed to write elf notes.\n");
+ return -1;
+ }
+ }
+
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ ret = cpu_write_elf32_qemunote(fd_write_vmcore, env, s);
+ if (ret < 0) {
+ dump_error(s, "dump: failed to write CPU status.\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int write_elf_section(DumpState *s, int type)
+{
+ Elf32_Shdr shdr32;
+ Elf64_Shdr shdr64;
+ int endian = s->dump_info.d_endian;
+ int shdr_size;
+ void *shdr;
+ int ret;
+
+ if (type == 0) {
+ shdr_size = sizeof(Elf32_Shdr);
+ memset(&shdr32, 0, shdr_size);
+ shdr32.sh_info = cpu_convert_to_target32(s->sh_info, endian);
+ shdr = &shdr32;
+ } else {
+ shdr_size = sizeof(Elf64_Shdr);
+ memset(&shdr64, 0, shdr_size);
+ shdr64.sh_info = cpu_convert_to_target32(s->sh_info, endian);
+ shdr = &shdr64;
+ }
+
+ ret = fd_write_vmcore(&shdr, shdr_size, s);
+ if (ret < 0) {
+ dump_error(s, "dump: failed to write section header table.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int write_data(DumpState *s, void *buf, int length)
+{
+ int ret;
+
+ ret = fd_write_vmcore(buf, length, s);
+ if (ret < 0) {
+ dump_error(s, "dump: failed to save memory.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* write the memroy to vmcore. 1 page per I/O. */
+static int write_memory(DumpState *s, RAMBlock *block, ram_addr_t start,
+ int64_t size)
+{
+ int64_t i;
+ int ret;
+
+ for (i = 0; i < size / TARGET_PAGE_SIZE; i++) {
+ ret = write_data(s, block->host + start + i * TARGET_PAGE_SIZE,
+ TARGET_PAGE_SIZE);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ if ((size % TARGET_PAGE_SIZE) != 0) {
+ ret = write_data(s, block->host + start + i * TARGET_PAGE_SIZE,
+ size % TARGET_PAGE_SIZE);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/* get the memory's offset in the vmcore */
+static target_phys_addr_t get_offset(target_phys_addr_t phys_addr,
+ DumpState *s)
+{
+ RAMBlock *block;
+ target_phys_addr_t offset = s->memory_offset;
+ int64_t size_in_block, start;
+
+ if (s->has_filter) {
+ if (phys_addr < s->begin || phys_addr >= s->begin + s->length) {
+ return -1;
+ }
+ }
+
+ QLIST_FOREACH(block, &ram_list.blocks, next) {
+ if (s->has_filter) {
+ if (block->offset >= s->begin + s->length ||
+ block->offset + block->length <= s->begin) {
+ /* This block is out of the range */
+ continue;
+ }
+
+ if (s->begin <= block->offset) {
+ start = block->offset;
+ } else {
+ start = s->begin;
+ }
+
+ size_in_block = block->length - (start - block->offset);
+ if (s->begin + s->length < block->offset + block->length) {
+ size_in_block -= block->offset + block->length -
+ (s->begin + s->length);
+ }
+ } else {
+ start = block->offset;
+ size_in_block = block->length;
+ }
+
+ if (phys_addr >= start && phys_addr < start + size_in_block) {
+ return phys_addr - start + offset;
+ }
+
+ offset += size_in_block;
+ }
+
+ return -1;
+}
+
+static int write_elf_loads(DumpState *s)
+{
+ target_phys_addr_t offset;
+ MemoryMapping *memory_mapping;
+ uint32_t phdr_index = 1;
+ int ret;
+ uint32_t max_index;
+
+ if (s->have_section) {
+ max_index = s->sh_info;
+ } else {
+ max_index = s->phdr_num;
+ }
+
+ QTAILQ_FOREACH(memory_mapping, &s->list.head, next) {
+ offset = get_offset(memory_mapping->phys_addr, s);
+ if (s->dump_info.d_class == ELFCLASS64) {
+ ret = write_elf64_load(s, memory_mapping, phdr_index++, offset);
+ } else {
+ ret = write_elf32_load(s, memory_mapping, phdr_index++, offset);
+ }
+
+ if (ret < 0) {
+ return -1;
+ }
+
+ if (phdr_index >= max_index) {
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/* write elf header, PT_NOTE and elf note to vmcore. */
+static int dump_begin(DumpState *s)
+{
+ int ret;
+
+ /*
+ * the vmcore's format is:
+ * --------------
+ * | elf header |
+ * --------------
+ * | PT_NOTE |
+ * --------------
+ * | PT_LOAD |
+ * --------------
+ * | ...... |
+ * --------------
+ * | PT_LOAD |
+ * --------------
+ * | sec_hdr |
+ * --------------
+ * | elf note |
+ * --------------
+ * | memory |
+ * --------------
+ *
+ * we only know where the memory is saved after we write elf note into
+ * vmcore.
+ */
+
+ /* write elf header to vmcore */
+ if (s->dump_info.d_class == ELFCLASS64) {
+ ret = write_elf64_header(s);
+ } else {
+ ret = write_elf32_header(s);
+ }
+ if (ret < 0) {
+ return -1;
+ }
+
+ if (s->dump_info.d_class == ELFCLASS64) {
+ /* write PT_NOTE to vmcore */
+ if (write_elf64_note(s) < 0) {
+ return -1;
+ }
+
+ /* write all PT_LOAD to vmcore */
+ if (write_elf_loads(s) < 0) {
+ return -1;
+ }
+
+ /* write section to vmcore */
+ if (s->have_section) {
+ if (write_elf_section(s, 1) < 0) {
+ return -1;
+ }
+ }
+
+ /* write notes to vmcore */
+ if (write_elf64_notes(s) < 0) {
+ return -1;
+ }
+
+ } else {
+ /* write PT_NOTE to vmcore */
+ if (write_elf32_note(s) < 0) {
+ return -1;
+ }
+
+ /* write all PT_LOAD to vmcore */
+ if (write_elf_loads(s) < 0) {
+ return -1;
+ }
+
+ /* write section to vmcore */
+ if (s->have_section) {
+ if (write_elf_section(s, 0) < 0) {
+ return -1;
+ }
+ }
+
+ /* write notes to vmcore */
+ if (write_elf32_notes(s) < 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/* write PT_LOAD to vmcore */
+static int dump_completed(DumpState *s)
+{
+ dump_cleanup(s);
+ return 0;
+}
+
+static int get_next_block(DumpState *s, RAMBlock *block)
+{
+ while (1) {
+ block = QLIST_NEXT(block, next);
+ if (!block) {
+ /* no more block */
+ return 1;
+ }
+
+ s->start = 0;
+ s->block = block;
+ if (s->has_filter) {
+ if (block->offset >= s->begin + s->length ||
+ block->offset + block->length <= s->begin) {
+ /* This block is out of the range */
+ continue;
+ }
+
+ if (s->begin > block->offset) {
+ s->start = s->begin - block->offset;
+ }
+ }
+
+ return 0;
+ }
+}
+
+/* write all memory to vmcore */
+static int dump_iterate(DumpState *s)
+{
+ RAMBlock *block;
+ int64_t size;
+ int ret;
+
+ while (1) {
+ block = s->block;
+
+ size = block->length;
+ if (s->has_filter) {
+ size -= s->start;
+ if (s->begin + s->length < block->offset + block->length) {
+ size -= block->offset + block->length - (s->begin + s->length);
+ }
+ }
+ ret = write_memory(s, block, s->start, size);
+ if (ret == -1) {
+ return ret;
+ }
+
+ ret = get_next_block(s, block);
+ if (ret == 1) {
+ dump_completed(s);
+ return 0;
+ }
+ }
+}
+
+static int create_vmcore(DumpState *s)
+{
+ int ret;
+
+ ret = dump_begin(s);
+ if (ret < 0) {
+ return -1;
+ }
+
+ ret = dump_iterate(s);
+ if (ret < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static ram_addr_t get_start_block(DumpState *s)
+{
+ RAMBlock *block;
+
+ if (!s->has_filter) {
+ s->block = QLIST_FIRST(&ram_list.blocks);
+ return 0;
+ }
+
+ QLIST_FOREACH(block, &ram_list.blocks, next) {
+ if (block->offset >= s->begin + s->length ||
+ block->offset + block->length <= s->begin) {
+ /* This block is out of the range */
+ continue;
+ }
+
+ s->block = block;
+ if (s->begin > block->offset) {
+ s->start = s->begin - block->offset;
+ } else {
+ s->start = 0;
+ }
+ return s->start;
+ }
+
+ return -1;
+}
+
+static int dump_init(DumpState *s, int fd, bool paging, bool has_filter,
+ int64_t begin, int64_t length, Error **errp)
+{
+ CPUArchState *env;
+ int nr_cpus;
+ int ret;
+
+ if (runstate_is_running()) {
+ vm_stop(RUN_STATE_SAVE_VM);
+ s->resume = true;
+ } else {
+ s->resume = false;
+ }
+
+ s->errp = errp;
+ s->fd = fd;
+ s->has_filter = has_filter;
+ s->begin = begin;
+ s->length = length;
+ s->start = get_start_block(s);
+ if (s->start == -1) {
+ error_set(errp, QERR_INVALID_PARAMETER, "begin");
+ goto cleanup;
+ }
+
+ /*
+ * get dump info: endian, class and architecture.
+ * If the target architecture is not supported, cpu_get_dump_info() will
+ * return -1.
+ *
+ * if we use kvm, we should synchronize the register before we get dump
+ * info.
+ */
+ nr_cpus = 0;
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ cpu_synchronize_state(env);
+ nr_cpus++;
+ }
+
+ ret = cpu_get_dump_info(&s->dump_info);
+ if (ret < 0) {
+ error_set(errp, QERR_UNSUPPORTED);
+ goto cleanup;
+ }
+
+ /* get memory mapping */
+ memory_mapping_list_init(&s->list);
+ if (paging) {
+ qemu_get_guest_memory_mapping(&s->list);
+ } else {
+ qemu_get_guest_simple_memory_mapping(&s->list);
+ }
+
+ if (s->has_filter) {
+ memory_mapping_filter(&s->list, s->begin, s->length);
+ }
+
+ /*
+ * calculate phdr_num
+ *
+ * the type of ehdr->e_phnum is uint16_t, so we should avoid overflow
+ */
+ s->phdr_num = 1; /* PT_NOTE */
+ if (s->list.num < UINT16_MAX - 2) {
+ s->phdr_num += s->list.num;
+ s->have_section = false;
+ } else {
+ s->have_section = true;
+ s->phdr_num = PN_XNUM;
+ s->sh_info = 1; /* PT_NOTE */
+
+ /* the type of shdr->sh_info is uint32_t, so we should avoid overflow */
+ if (s->list.num <= UINT32_MAX - 1) {
+ s->sh_info += s->list.num;
+ } else {
+ s->sh_info = UINT32_MAX;
+ }
+ }
+
+ s->note_size = cpu_get_note_size(s->dump_info.d_class,
+ s->dump_info.d_machine, nr_cpus);
+ if (s->dump_info.d_class == ELFCLASS64) {
+ if (s->have_section) {
+ s->memory_offset = sizeof(Elf64_Ehdr) +
+ sizeof(Elf64_Phdr) * s->sh_info +
+ sizeof(Elf64_Shdr) + s->note_size;
+ } else {
+ s->memory_offset = sizeof(Elf64_Ehdr) +
+ sizeof(Elf64_Phdr) * s->phdr_num + s->note_size;
+ }
+ } else {
+ if (s->have_section) {
+ s->memory_offset = sizeof(Elf32_Ehdr) +
+ sizeof(Elf32_Phdr) * s->sh_info +
+ sizeof(Elf32_Shdr) + s->note_size;
+ } else {
+ s->memory_offset = sizeof(Elf32_Ehdr) +
+ sizeof(Elf32_Phdr) * s->phdr_num + s->note_size;
+ }
+ }
+
+ return 0;
+
+cleanup:
+ if (s->resume) {
+ vm_start();
+ }
+
+ return -1;
+}
+
+void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
+ int64_t begin, bool has_length, int64_t length,
+ Error **errp)
+{
+ const char *p;
+ int fd = -1;
+ DumpState *s;
+ int ret;
+
+ if (has_begin && !has_length) {
+ error_set(errp, QERR_MISSING_PARAMETER, "length");
+ return;
+ }
+ if (!has_begin && has_length) {
+ error_set(errp, QERR_MISSING_PARAMETER, "begin");
+ return;
+ }
+
+#if !defined(WIN32)
+ if (strstart(file, "fd:", &p)) {
+ fd = monitor_get_fd(cur_mon, p);
+ if (fd == -1) {
+ error_set(errp, QERR_FD_NOT_FOUND, p);
+ return;
+ }
+ }
+#endif
+
+ if (strstart(file, "file:", &p)) {
+ fd = qemu_open(p, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR);
+ if (fd < 0) {
+ error_set(errp, QERR_OPEN_FILE_FAILED, p);
+ return;
+ }
+ }
+
+ if (fd == -1) {
+ error_set(errp, QERR_INVALID_PARAMETER, "protocol");
+ return;
+ }
+
+ s = g_malloc(sizeof(DumpState));
+
+ ret = dump_init(s, fd, paging, has_begin, begin, length, errp);
+ if (ret < 0) {
+ g_free(s);
+ return;
+ }
+
+ if (create_vmcore(s) < 0 && !error_is_set(s->errp)) {
+ error_set(errp, QERR_IO_ERROR);
+ }
+
+ g_free(s);
+}
+
+#else
+/* we need this function in hmp.c */
+void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
+ int64_t begin, bool has_length, int64_t length,
+ Error **errp)
+{
+ error_set(errp, QERR_UNSUPPORTED);
+}
+#endif
diff --git a/dump.h b/dump.h
new file mode 100644
index 0000000000..28340cf271
--- /dev/null
+++ b/dump.h
@@ -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
diff --git a/elf.h b/elf.h
index e1422b81ee..9c9acfaf75 100644
--- a/elf.h
+++ b/elf.h
@@ -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;
diff --git a/exec.c b/exec.c
index a0494c72bf..1b65859e50 100644
--- a/exec.c
+++ b/exec.c
@@ -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
diff --git a/gdbstub.c b/gdbstub.c
index 6a77a6696b..08cf8645d7 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -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);
diff --git a/gdbstub.h b/gdbstub.h
index b44e27523a..668de66000 100644
--- a/gdbstub.h
+++ b/gdbstub.h
@@ -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
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 18cb415ac4..f5d9d91de8 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -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
diff --git a/hmp.c b/hmp.c
index bb0952e00b..2ce8cb9df1 100644
--- a/hmp.c
+++ b/hmp.c
@@ -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);
+}
diff --git a/hmp.h b/hmp.h
index 443b812923..79d138d3ee 100644
--- a/hmp.h
+++ b/hmp.h
@@ -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
diff --git a/hw/pci-hotplug.c b/hw/pci-hotplug.c
index c55d8b9396..61257f457b 100644
--- a/hw/pci-hotplug.c
+++ b/hw/pci-hotplug.c
@@ -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;
diff --git a/hw/qdev-monitor.c b/hw/qdev-monitor.c
index eed781d2f0..b01ef0600e 100644
--- a/hw/qdev-monitor.c
+++ b/hw/qdev-monitor.c
@@ -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)) {
diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c
index b238a0973d..5d2f0982c9 100644
--- a/hw/usb/dev-network.c
+++ b/hw/usb/dev-network.c
@@ -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;
}
diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c
index ae22fb1c97..a96c0b9e5e 100644
--- a/hw/usb/dev-storage.c
+++ b/hw/usb/dev-storage.c
@@ -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++) {
diff --git a/hw/watchdog.c b/hw/watchdog.c
index 4c18965654..a42124d520 100644
--- a/hw/watchdog.c
+++ b/hw/watchdog.c
@@ -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;
}
diff --git a/memory_mapping.c b/memory_mapping.c
new file mode 100644
index 0000000000..8810bb09e3
--- /dev/null
+++ b/memory_mapping.c
@@ -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;
+ }
+ }
+}
diff --git a/memory_mapping.h b/memory_mapping.h
new file mode 100644
index 0000000000..a1aa64f4ca
--- /dev/null
+++ b/memory_mapping.h
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef MEMORY_MAPPING_H
+#define MEMORY_MAPPING_H
+
+#include "qemu-queue.h"
+
+#ifndef CONFIG_USER_ONLY
+/* The physical and virtual address in the memory mapping are contiguous. */
+typedef struct MemoryMapping {
+ target_phys_addr_t phys_addr;
+ target_ulong virt_addr;
+ ram_addr_t length;
+ QTAILQ_ENTRY(MemoryMapping) next;
+} MemoryMapping;
+
+typedef struct MemoryMappingList {
+ unsigned int num;
+ MemoryMapping *last_mapping;
+ QTAILQ_HEAD(, MemoryMapping) head;
+} MemoryMappingList;
+
+/*
+ * add or merge the memory region [phys_addr, phys_addr + length) into the
+ * memory mapping's list. The region's virtual address starts with virt_addr,
+ * and is contiguous. The list is sorted by phys_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);
+
+void memory_mapping_list_free(MemoryMappingList *list);
+
+void memory_mapping_list_init(MemoryMappingList *list);
+
+/*
+ * Return value:
+ * 0: success
+ * -1: failed
+ * -2: unsupported
+ */
+#if defined(CONFIG_HAVE_GET_MEMORY_MAPPING)
+int qemu_get_guest_memory_mapping(MemoryMappingList *list);
+#else
+static inline int qemu_get_guest_memory_mapping(MemoryMappingList *list)
+{
+ return -2;
+}
+#endif
+
+/* get guest's memory mapping without do paging(virtual address is 0). */
+void qemu_get_guest_simple_memory_mapping(MemoryMappingList *list);
+
+void memory_mapping_filter(MemoryMappingList *list, int64_t begin,
+ int64_t length);
+
+#else
+
+/* We use MemoryMappingList* in cpu-all.h */
+typedef struct MemoryMappingList MemoryMappingList;
+#endif
+#endif
diff --git a/monitor.c b/monitor.c
index 12a6fe25ad..a3bc2c7253 100644
--- a/monitor.c
+++ b/monitor.c
@@ -422,6 +422,30 @@ static void timestamp_put(QDict *qdict)
qdict_put_obj(qdict, "timestamp", obj);
}
+
+static const char *monitor_event_names[] = {
+ [QEVENT_SHUTDOWN] = "SHUTDOWN",
+ [QEVENT_RESET] = "RESET",
+ [QEVENT_POWERDOWN] = "POWERDOWN",
+ [QEVENT_STOP] = "STOP",
+ [QEVENT_RESUME] = "RESUME",
+ [QEVENT_VNC_CONNECTED] = "VNC_CONNECTED",
+ [QEVENT_VNC_INITIALIZED] = "VNC_INITIALIZED",
+ [QEVENT_VNC_DISCONNECTED] = "VNC_DISCONNECTED",
+ [QEVENT_BLOCK_IO_ERROR] = "BLOCK_IO_ERROR",
+ [QEVENT_RTC_CHANGE] = "RTC_CHANGE",
+ [QEVENT_WATCHDOG] = "WATCHDOG",
+ [QEVENT_SPICE_CONNECTED] = "SPICE_CONNECTED",
+ [QEVENT_SPICE_INITIALIZED] = "SPICE_INITIALIZED",
+ [QEVENT_SPICE_DISCONNECTED] = "SPICE_DISCONNECTED",
+ [QEVENT_BLOCK_JOB_COMPLETED] = "BLOCK_JOB_COMPLETED",
+ [QEVENT_BLOCK_JOB_CANCELLED] = "BLOCK_JOB_CANCELLED",
+ [QEVENT_DEVICE_TRAY_MOVED] = "DEVICE_TRAY_MOVED",
+ [QEVENT_SUSPEND] = "SUSPEND",
+ [QEVENT_WAKEUP] = "WAKEUP",
+};
+QEMU_BUILD_BUG_ON(ARRAY_SIZE(monitor_event_names) != QEVENT_MAX)
+
/**
* monitor_protocol_event(): Generate a Monitor event
*
@@ -435,68 +459,8 @@ void monitor_protocol_event(MonitorEvent event, QObject *data)
assert(event < QEVENT_MAX);
- switch (event) {
- case QEVENT_SHUTDOWN:
- event_name = "SHUTDOWN";
- break;
- case QEVENT_RESET:
- event_name = "RESET";
- break;
- case QEVENT_POWERDOWN:
- event_name = "POWERDOWN";
- break;
- case QEVENT_STOP:
- event_name = "STOP";
- break;
- case QEVENT_RESUME:
- event_name = "RESUME";
- break;
- case QEVENT_VNC_CONNECTED:
- event_name = "VNC_CONNECTED";
- break;
- case QEVENT_VNC_INITIALIZED:
- event_name = "VNC_INITIALIZED";
- break;
- case QEVENT_VNC_DISCONNECTED:
- event_name = "VNC_DISCONNECTED";
- break;
- case QEVENT_BLOCK_IO_ERROR:
- event_name = "BLOCK_IO_ERROR";
- break;
- case QEVENT_RTC_CHANGE:
- event_name = "RTC_CHANGE";
- break;
- case QEVENT_WATCHDOG:
- event_name = "WATCHDOG";
- break;
- case QEVENT_SPICE_CONNECTED:
- event_name = "SPICE_CONNECTED";
- break;
- case QEVENT_SPICE_INITIALIZED:
- event_name = "SPICE_INITIALIZED";
- break;
- case QEVENT_SPICE_DISCONNECTED:
- event_name = "SPICE_DISCONNECTED";
- break;
- case QEVENT_BLOCK_JOB_COMPLETED:
- event_name = "BLOCK_JOB_COMPLETED";
- break;
- case QEVENT_BLOCK_JOB_CANCELLED:
- event_name = "BLOCK_JOB_CANCELLED";
- break;
- case QEVENT_DEVICE_TRAY_MOVED:
- event_name = "DEVICE_TRAY_MOVED";
- break;
- case QEVENT_SUSPEND:
- event_name = "SUSPEND";
- break;
- case QEVENT_WAKEUP:
- event_name = "WAKEUP";
- break;
- default:
- abort();
- break;
- }
+ event_name = monitor_event_names[event];
+ assert(event_name != NULL);
qmp = qdict_new();
timestamp_put(qmp);
@@ -738,6 +702,25 @@ CommandInfoList *qmp_query_commands(Error **errp)
return cmd_list;
}
+EventInfoList *qmp_query_events(Error **errp)
+{
+ EventInfoList *info, *ev_list = NULL;
+ MonitorEvent e;
+
+ for (e = 0 ; e < QEVENT_MAX ; e++) {
+ const char *event_name = monitor_event_names[e];
+ assert(event_name != NULL);
+ info = g_malloc0(sizeof(*info));
+ info->value = g_malloc0(sizeof(*info->value));
+ info->value->name = g_strdup(event_name);
+
+ info->next = ev_list;
+ ev_list = info;
+ }
+
+ return ev_list;
+}
+
/* set the current CPU defined by the user */
int monitor_set_cpu(int cpu_index)
{
diff --git a/monitor.h b/monitor.h
index 0d498006f8..cd1d8786d1 100644
--- a/monitor.h
+++ b/monitor.h
@@ -41,6 +41,10 @@ typedef enum MonitorEvent {
QEVENT_DEVICE_TRAY_MOVED,
QEVENT_SUSPEND,
QEVENT_WAKEUP,
+
+ /* Add to 'monitor_event_names' array in monitor.c when
+ * defining new events here */
+
QEVENT_MAX,
} MonitorEvent;
diff --git a/net.c b/net.c
index 1922d8abd1..4aa416cffb 100644
--- a/net.c
+++ b/net.c
@@ -745,10 +745,7 @@ int net_handle_fd_param(Monitor *mon, const char *param)
return fd;
}
-static int net_init_nic(QemuOpts *opts,
- Monitor *mon,
- const char *name,
- VLANState *vlan)
+static int net_init_nic(QemuOpts *opts, const char *name, VLANState *vlan)
{
int idx;
NICInfo *nd;
@@ -821,7 +818,6 @@ static int net_init_nic(QemuOpts *opts,
}
typedef int (*net_client_init_func)(QemuOpts *opts,
- Monitor *mon,
const char *name,
VLANState *vlan);
@@ -1085,7 +1081,7 @@ static const struct {
#endif /* CONFIG_NET_BRIDGE */
};
-int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev)
+int net_client_init(QemuOpts *opts, int is_netdev, Error **errp)
{
const char *name;
const char *type;
@@ -1093,7 +1089,7 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev)
type = qemu_opt_get(opts, "type");
if (!type) {
- qerror_report(QERR_MISSING_PARAMETER, "type");
+ error_set(errp, QERR_MISSING_PARAMETER, "type");
return -1;
}
@@ -1109,21 +1105,21 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev)
strcmp(type, "vde") != 0 &&
#endif
strcmp(type, "socket") != 0) {
- qerror_report(QERR_INVALID_PARAMETER_VALUE, "type",
- "a netdev backend type");
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, "type",
+ "a netdev backend type");
return -1;
}
if (qemu_opt_get(opts, "vlan")) {
- qerror_report(QERR_INVALID_PARAMETER, "vlan");
+ error_set(errp, QERR_INVALID_PARAMETER, "vlan");
return -1;
}
if (qemu_opt_get(opts, "name")) {
- qerror_report(QERR_INVALID_PARAMETER, "name");
+ error_set(errp, QERR_INVALID_PARAMETER, "name");
return -1;
}
if (!qemu_opts_id(opts)) {
- qerror_report(QERR_MISSING_PARAMETER, "id");
+ error_set(errp, QERR_MISSING_PARAMETER, "id");
return -1;
}
}
@@ -1136,10 +1132,13 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev)
for (i = 0; i < NET_CLIENT_TYPE_MAX; i++) {
if (net_client_types[i].type != NULL &&
!strcmp(net_client_types[i].type, type)) {
+ Error *local_err = NULL;
VLANState *vlan = NULL;
int ret;
- if (qemu_opts_validate(opts, &net_client_types[i].desc[0]) == -1) {
+ qemu_opts_validate(opts, &net_client_types[i].desc[0], &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
return -1;
}
@@ -1152,10 +1151,10 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev)
ret = 0;
if (net_client_types[i].init) {
- ret = net_client_types[i].init(opts, mon, name, vlan);
+ ret = net_client_types[i].init(opts, name, vlan);
if (ret < 0) {
/* TODO push error reporting into init() methods */
- qerror_report(QERR_DEVICE_INIT_FAILED, type);
+ error_set(errp, QERR_DEVICE_INIT_FAILED, type);
return -1;
}
}
@@ -1163,8 +1162,8 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev)
}
}
- qerror_report(QERR_INVALID_PARAMETER_VALUE, "type",
- "a network client type");
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, "type",
+ "a network client type");
return -1;
}
@@ -1195,6 +1194,7 @@ void net_host_device_add(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_str(qdict, "device");
const char *opts_str = qdict_get_try_str(qdict, "opts");
+ Error *local_err = NULL;
QemuOpts *opts;
if (!net_host_check_device(device)) {
@@ -1209,7 +1209,10 @@ void net_host_device_add(Monitor *mon, const QDict *qdict)
qemu_opt_set(opts, "type", device);
- if (net_client_init(mon, opts, 0) < 0) {
+ net_client_init(opts, 0, &local_err);
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
monitor_printf(mon, "adding host network device %s failed\n", device);
}
}
@@ -1231,37 +1234,53 @@ void net_host_device_remove(Monitor *mon, const QDict *qdict)
qemu_del_vlan_client(vc);
}
-int do_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret_data)
+void netdev_add(QemuOpts *opts, Error **errp)
{
+ net_client_init(opts, 1, errp);
+}
+
+int qmp_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret)
+{
+ Error *local_err = NULL;
+ QemuOptsList *opts_list;
QemuOpts *opts;
- int res;
- opts = qemu_opts_from_qdict(qemu_find_opts("netdev"), qdict);
- if (!opts) {
- return -1;
+ opts_list = qemu_find_opts_err("netdev", &local_err);
+ if (error_is_set(&local_err)) {
+ goto exit_err;
+ }
+
+ opts = qemu_opts_from_qdict(opts_list, qdict, &local_err);
+ if (error_is_set(&local_err)) {
+ goto exit_err;
}
- res = net_client_init(mon, opts, 1);
- if (res < 0) {
+ netdev_add(opts, &local_err);
+ if (error_is_set(&local_err)) {
qemu_opts_del(opts);
+ goto exit_err;
}
- return res;
+ return 0;
+
+exit_err:
+ qerror_report_err(local_err);
+ error_free(local_err);
+ return -1;
}
-int do_netdev_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
+void qmp_netdev_del(const char *id, Error **errp)
{
- const char *id = qdict_get_str(qdict, "id");
VLANClientState *vc;
vc = qemu_find_netdev(id);
if (!vc) {
- qerror_report(QERR_DEVICE_NOT_FOUND, id);
- return -1;
+ error_set(errp, QERR_DEVICE_NOT_FOUND, id);
+ return;
}
+
qemu_del_vlan_client(vc);
- qemu_opts_del(qemu_opts_find(qemu_find_opts("netdev"), id));
- return 0;
+ qemu_opts_del(qemu_opts_find(qemu_find_opts_err("netdev", errp), id));
}
static void print_net_client(Monitor *mon, VLANClientState *vc)
@@ -1424,14 +1443,31 @@ void net_check_clients(void)
static int net_init_client(QemuOpts *opts, void *dummy)
{
- if (net_client_init(NULL, opts, 0) < 0)
+ Error *local_err = NULL;
+
+ net_client_init(opts, 0, &local_err);
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
return -1;
+ }
+
return 0;
}
static int net_init_netdev(QemuOpts *opts, void *dummy)
{
- return net_client_init(NULL, opts, 1);
+ Error *local_err = NULL;
+ int ret;
+
+ ret = net_client_init(opts, 1, &local_err);
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ return -1;
+ }
+
+ return ret;
}
int net_init_clients(void)
diff --git a/net.h b/net.h
index 64993b43ef..bdc2a0602d 100644
--- a/net.h
+++ b/net.h
@@ -163,15 +163,15 @@ struct HCIInfo *qemu_next_hci(void);
extern const char *legacy_tftp_prefix;
extern const char *legacy_bootp_filename;
-int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev);
+int net_client_init(QemuOpts *opts, int is_netdev, Error **errp);
int net_client_parse(QemuOptsList *opts_list, const char *str);
int net_init_clients(void);
void net_check_clients(void);
void net_cleanup(void);
void net_host_device_add(Monitor *mon, const QDict *qdict);
void net_host_device_remove(Monitor *mon, const QDict *qdict);
-int do_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret_data);
-int do_netdev_del(Monitor *mon, const QDict *qdict, QObject **ret_data);
+void netdev_add(QemuOpts *opts, Error **errp);
+int qmp_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret);
#define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup"
#define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown"
diff --git a/net/dump.c b/net/dump.c
index 4b48d48408..f835c51187 100644
--- a/net/dump.c
+++ b/net/dump.c
@@ -144,7 +144,7 @@ static int net_dump_init(VLANState *vlan, const char *device,
return 0;
}
-int net_init_dump(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan)
+int net_init_dump(QemuOpts *opts, const char *name, VLANState *vlan)
{
int len;
const char *file;
diff --git a/net/dump.h b/net/dump.h
index fdc91ad6dc..2b5d9ba644 100644
--- a/net/dump.h
+++ b/net/dump.h
@@ -27,7 +27,6 @@
#include "net.h"
#include "qemu-common.h"
-int net_init_dump(QemuOpts *opts, Monitor *mon,
- const char *name, VLANState *vlan);
+int net_init_dump(QemuOpts *opts, const char *name, VLANState *vlan);
#endif /* QEMU_NET_DUMP_H */
diff --git a/net/slirp.c b/net/slirp.c
index 96f5032c4c..37b6ccfde9 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -676,10 +676,7 @@ static int net_init_slirp_configs(const char *name, const char *value, void *opa
return 0;
}
-int net_init_slirp(QemuOpts *opts,
- Monitor *mon,
- const char *name,
- VLANState *vlan)
+int net_init_slirp(QemuOpts *opts, const char *name, VLANState *vlan)
{
struct slirp_config_str *config;
const char *vhost;
diff --git a/net/slirp.h b/net/slirp.h
index c17de8ed47..53fe95dc12 100644
--- a/net/slirp.h
+++ b/net/slirp.h
@@ -30,10 +30,7 @@
#ifdef CONFIG_SLIRP
-int net_init_slirp(QemuOpts *opts,
- Monitor *mon,
- const char *name,
- VLANState *vlan);
+int net_init_slirp(QemuOpts *opts, const char *name, VLANState *vlan);
void net_slirp_hostfwd_add(Monitor *mon, const QDict *qdict);
void net_slirp_hostfwd_remove(Monitor *mon, const QDict *qdict);
diff --git a/net/socket.c b/net/socket.c
index 0bcf229c24..fcd0a3c162 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -26,6 +26,7 @@
#include "config-host.h"
#include "net.h"
+#include "monitor.h"
#include "qemu-char.h"
#include "qemu-common.h"
#include "qemu-error.h"
@@ -585,10 +586,7 @@ static int net_socket_udp_init(VLANState *vlan,
return 0;
}
-int net_init_socket(QemuOpts *opts,
- Monitor *mon,
- const char *name,
- VLANState *vlan)
+int net_init_socket(QemuOpts *opts, const char *name, VLANState *vlan)
{
if (qemu_opt_get(opts, "fd")) {
int fd;
@@ -601,7 +599,7 @@ int net_init_socket(QemuOpts *opts,
return -1;
}
- fd = net_handle_fd_param(mon, qemu_opt_get(opts, "fd"));
+ fd = net_handle_fd_param(cur_mon, qemu_opt_get(opts, "fd"));
if (fd == -1) {
return -1;
}
diff --git a/net/socket.h b/net/socket.h
index ea46f02dd8..e1fe959412 100644
--- a/net/socket.h
+++ b/net/socket.h
@@ -27,7 +27,6 @@
#include "net.h"
#include "qemu-common.h"
-int net_init_socket(QemuOpts *opts, Monitor *mon,
- const char *name, VLANState *vlan);
+int net_init_socket(QemuOpts *opts, const char *name, VLANState *vlan);
#endif /* QEMU_NET_SOCKET_H */
diff --git a/net/tap-win32.c b/net/tap-win32.c
index 596132e272..a801a553c4 100644
--- a/net/tap-win32.c
+++ b/net/tap-win32.c
@@ -699,7 +699,7 @@ static int tap_win32_init(VLANState *vlan, const char *model,
return 0;
}
-int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan)
+int net_init_tap(QemuOpts *opts, const char *name, VLANState *vlan)
{
const char *ifname;
diff --git a/net/tap.c b/net/tap.c
index f2400283c3..5ac4ba3343 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -512,8 +512,7 @@ static int net_bridge_run_helper(const char *helper, const char *bridge)
return -1;
}
-int net_init_bridge(QemuOpts *opts, Monitor *mon, const char *name,
- VLANState *vlan)
+int net_init_bridge(QemuOpts *opts, const char *name, VLANState *vlan)
{
TAPState *s;
int fd, vnet_hdr;
@@ -583,7 +582,7 @@ static int net_tap_init(QemuOpts *opts, int *vnet_hdr)
return fd;
}
-int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan)
+int net_init_tap(QemuOpts *opts, const char *name, VLANState *vlan)
{
TAPState *s;
int fd, vnet_hdr = 0;
@@ -600,7 +599,7 @@ int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan
return -1;
}
- fd = net_handle_fd_param(mon, qemu_opt_get(opts, "fd"));
+ fd = net_handle_fd_param(cur_mon, qemu_opt_get(opts, "fd"));
if (fd == -1) {
return -1;
}
@@ -687,7 +686,7 @@ int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan
int vhostfd, r;
bool force = qemu_opt_get_bool(opts, "vhostforce", false);
if (qemu_opt_get(opts, "vhostfd")) {
- r = net_handle_fd_param(mon, qemu_opt_get(opts, "vhostfd"));
+ r = net_handle_fd_param(cur_mon, qemu_opt_get(opts, "vhostfd"));
if (r == -1) {
return -1;
}
diff --git a/net/tap.h b/net/tap.h
index 56c591fc8e..b2a9450aab 100644
--- a/net/tap.h
+++ b/net/tap.h
@@ -32,7 +32,7 @@
#define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup"
#define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown"
-int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan);
+int net_init_tap(QemuOpts *opts, const char *name, VLANState *vlan);
int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required);
@@ -57,7 +57,6 @@ int tap_get_fd(VLANClientState *vc);
struct vhost_net;
struct vhost_net *tap_get_vhost_net(VLANClientState *vc);
-int net_init_bridge(QemuOpts *opts, Monitor *mon, const char *name,
- VLANState *vlan);
+int net_init_bridge(QemuOpts *opts, const char *name, VLANState *vlan);
#endif /* QEMU_NET_TAP_H */
diff --git a/net/vde.c b/net/vde.c
index ac48ab2f0a..6b9d45294a 100644
--- a/net/vde.c
+++ b/net/vde.c
@@ -110,7 +110,7 @@ static int net_vde_init(VLANState *vlan, const char *model,
return 0;
}
-int net_init_vde(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan)
+int net_init_vde(QemuOpts *opts, const char *name, VLANState *vlan)
{
const char *sock;
const char *group;
diff --git a/net/vde.h b/net/vde.h
index 3e6ca3e876..732e5756f6 100644
--- a/net/vde.h
+++ b/net/vde.h
@@ -29,7 +29,7 @@
#ifdef CONFIG_VDE
-int net_init_vde(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan);
+int net_init_vde(QemuOpts *opts, const char *name, VLANState *vlan);
#endif /* CONFIG_VDE */
diff --git a/qapi-schema.json b/qapi-schema.json
index 2ca7195d25..3b6e3468b4 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -228,6 +228,28 @@
{ 'command': 'query-commands', 'returns': ['CommandInfo'] }
##
+# @EventInfo:
+#
+# Information about a QMP event
+#
+# @name: The event name
+#
+# Since: 1.2.0
+##
+{ 'type': 'EventInfo', 'data': {'name': 'str'} }
+
+##
+# @query-events:
+#
+# Return a list of supported QMP events by this server
+#
+# Returns: A list of @EventInfo for all supported events
+#
+# Since: 1.2.0
+##
+{ 'command': 'query-events', 'returns': ['EventInfo'] }
+
+##
# @MigrationStats
#
# Detailed migration status.
@@ -1755,3 +1777,88 @@
# Since: 0.14.0
##
{ 'command': 'device_del', 'data': {'id': 'str'} }
+
+##
+# @dump-guest-memory
+#
+# Dump guest's memory to vmcore. It is a synchronous operation that can take
+# very long depending on the amount of guest memory. This command is only
+# supported only on i386 and x86_64
+#
+# @paging: if true, do paging to get guest's memory mapping. The @paging's
+# default value of @paging is false, If you want to use gdb to process the
+# core, please set @paging to true. The reason why the @paging's value is
+# false:
+# 1. guest machine in a catastrophic state can have corrupted memory,
+# which we cannot trust.
+# 2. The guest machine can be in read-mode even if paging is enabled.
+# For example: the guest machine uses ACPI to sleep, and ACPI sleep
+# state goes in real-mode
+# @protocol: the filename or file descriptor of the vmcore. The supported
+# protocol can be file or fd:
+# 1. file: the protocol starts with "file:", and the following string is
+# the file's path.
+# 2. fd: the protocol starts with "fd:", and the following string is the
+# fd's name.
+# @begin: #optional if specified, the starting physical address.
+# @length: #optional if specified, the memory size, in bytes. If you don't
+# want to dump all guest's memory, please specify the start @begin and
+# @length
+#
+# Returns: nothing on success
+# If @begin contains an invalid address, InvalidParameter
+# If only one of @begin and @length is specified, MissingParameter
+# If @protocol stats with "fd:", and the fd cannot be found, FdNotFound
+# If @protocol starts with "file:", and the file cannot be
+# opened, OpenFileFailed
+# If @protocol does not start with "fd:" or "file:", InvalidParameter
+# If an I/O error occurs while writing the file, IOError
+# If the target does not support this command, Unsupported
+#
+# Since: 1.2
+##
+{ 'command': 'dump-guest-memory',
+ 'data': { 'paging': 'bool', 'protocol': 'str', '*begin': 'int',
+ '*length': 'int' } }
+##
+# @netdev_add:
+#
+# Add a network backend.
+#
+# @type: the type of network backend. Current valid values are 'user', 'tap',
+# 'vde', 'socket', 'dump' and 'bridge'
+#
+# @id: the name of the new network backend
+#
+# @props: #optional a list of properties to be passed to the backend in
+# the format 'name=value', like 'ifname=tap0,script=no'
+#
+# Notes: The semantics of @props is not well defined. Future commands will be
+# introduced that provide stronger typing for backend creation.
+#
+# Since: 0.14.0
+#
+# Returns: Nothing on success
+# If @type is not a valid network backend, DeviceNotFound
+# If @id is not a valid identifier, InvalidParameterValue
+# if @id already exists, DuplicateId
+# If @props contains an invalid parameter for this backend,
+# InvalidParameter
+##
+{ 'command': 'netdev_add',
+ 'data': {'type': 'str', 'id': 'str', '*props': '**'},
+ 'gen': 'no' }
+
+##
+# @netdev_del:
+#
+# Remove a network backend.
+#
+# @id: the name of the network backend to remove
+#
+# Returns: Nothing on success
+# If @id is not a valid network backend, DeviceNotFound
+#
+# Since: 0.14.0
+##
+{ 'command': 'netdev_del', 'data': {'id': 'str'} }
diff --git a/qemu-char.c b/qemu-char.c
index fe1126fe86..0bd903f58c 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -2584,10 +2584,14 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
int pos;
const char *p;
QemuOpts *opts;
+ Error *local_err = NULL;
- opts = qemu_opts_create(qemu_find_opts("chardev"), label, 1);
- if (NULL == opts)
+ opts = qemu_opts_create(qemu_find_opts("chardev"), label, 1, &local_err);
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
return NULL;
+ }
if (strstart(filename, "mon:", &p)) {
filename = p;
diff --git a/qemu-config.c b/qemu-config.c
index be84a0347c..bb3bff426a 100644
--- a/qemu-config.c
+++ b/qemu-config.c
@@ -3,6 +3,7 @@
#include "qemu-option.h"
#include "qemu-config.h"
#include "hw/qdev.h"
+#include "error.h"
static QemuOptsList qemu_drive_opts = {
.name = "drive",
@@ -631,7 +632,8 @@ static QemuOptsList *vm_config_groups[32] = {
NULL,
};
-static QemuOptsList *find_list(QemuOptsList **lists, const char *group)
+static QemuOptsList *find_list(QemuOptsList **lists, const char *group,
+ Error **errp)
{
int i;
@@ -640,14 +642,28 @@ static QemuOptsList *find_list(QemuOptsList **lists, const char *group)
break;
}
if (lists[i] == NULL) {
- error_report("there is no option group \"%s\"", group);
+ error_set(errp, QERR_INVALID_OPTION_GROUP, group);
}
return lists[i];
}
QemuOptsList *qemu_find_opts(const char *group)
{
- return find_list(vm_config_groups, group);
+ QemuOptsList *ret;
+ Error *local_err = NULL;
+
+ ret = find_list(vm_config_groups, group, &local_err);
+ if (error_is_set(&local_err)) {
+ error_report("%s\n", error_get_pretty(local_err));
+ error_free(local_err);
+ }
+
+ return ret;
+}
+
+QemuOptsList *qemu_find_opts_err(const char *group, Error **errp)
+{
+ return find_list(vm_config_groups, group, errp);
}
void qemu_add_opts(QemuOptsList *list)
@@ -709,7 +725,7 @@ int qemu_global_option(const char *str)
return -1;
}
- opts = qemu_opts_create(&qemu_global_opts, NULL, 0);
+ opts = qemu_opts_create(&qemu_global_opts, NULL, 0, NULL);
qemu_opt_set(opts, "driver", driver);
qemu_opt_set(opts, "property", property);
qemu_opt_set(opts, "value", str+offset+1);
@@ -762,6 +778,7 @@ int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname)
char line[1024], group[64], id[64], arg[64], value[1024];
Location loc;
QemuOptsList *list = NULL;
+ Error *local_err = NULL;
QemuOpts *opts = NULL;
int res = -1, lno = 0;
@@ -778,18 +795,24 @@ int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname)
}
if (sscanf(line, "[%63s \"%63[^\"]\"]", group, id) == 2) {
/* group with id */
- list = find_list(lists, group);
- if (list == NULL)
+ list = find_list(lists, group, &local_err);
+ if (error_is_set(&local_err)) {
+ error_report("%s\n", error_get_pretty(local_err));
+ error_free(local_err);
goto out;
- opts = qemu_opts_create(list, id, 1);
+ }
+ opts = qemu_opts_create(list, id, 1, NULL);
continue;
}
if (sscanf(line, "[%63[^]]]", group) == 1) {
/* group without id */
- list = find_list(lists, group);
- if (list == NULL)
+ list = find_list(lists, group, &local_err);
+ if (error_is_set(&local_err)) {
+ error_report("%s\n", error_get_pretty(local_err));
+ error_free(local_err);
goto out;
- opts = qemu_opts_create(list, NULL, 0);
+ }
+ opts = qemu_opts_create(list, NULL, 0, NULL);
continue;
}
if (sscanf(line, " %63s = \"%1023[^\"]\"", arg, value) == 2) {
diff --git a/qemu-config.h b/qemu-config.h
index 6d7365d35b..e9f2ef4c7b 100644
--- a/qemu-config.h
+++ b/qemu-config.h
@@ -1,11 +1,14 @@
#ifndef QEMU_CONFIG_H
#define QEMU_CONFIG_H
+#include "error.h"
+
extern QemuOptsList qemu_fsdev_opts;
extern QemuOptsList qemu_virtfs_opts;
extern QemuOptsList qemu_spice_opts;
QemuOptsList *qemu_find_opts(const char *group);
+QemuOptsList *qemu_find_opts_err(const char *group, Error **errp);
void qemu_add_opts(QemuOptsList *list);
int qemu_set_option(const char *str);
int qemu_global_option(const char *str);
diff --git a/qemu-option.c b/qemu-option.c
index 35cd609f75..bb3886c6b9 100644
--- a/qemu-option.c
+++ b/qemu-option.c
@@ -30,6 +30,7 @@
#include "qemu-error.h"
#include "qemu-objects.h"
#include "qemu-option.h"
+#include "error.h"
#include "qerror.h"
/*
@@ -168,7 +169,8 @@ QEMUOptionParameter *get_option_parameter(QEMUOptionParameter *list,
return NULL;
}
-static int parse_option_bool(const char *name, const char *value, bool *ret)
+static void parse_option_bool(const char *name, const char *value, bool *ret,
+ Error **errp)
{
if (value != NULL) {
if (!strcmp(value, "on")) {
@@ -176,16 +178,15 @@ static int parse_option_bool(const char *name, const char *value, bool *ret)
} else if (!strcmp(value, "off")) {
*ret = 0;
} else {
- qerror_report(QERR_INVALID_PARAMETER_VALUE, name, "'on' or 'off'");
- return -1;
+ error_set(errp,QERR_INVALID_PARAMETER_VALUE, name, "'on' or 'off'");
}
} else {
*ret = 1;
}
- return 0;
}
-static int parse_option_number(const char *name, const char *value, uint64_t *ret)
+static void parse_option_number(const char *name, const char *value,
+ uint64_t *ret, Error **errp)
{
char *postfix;
uint64_t number;
@@ -193,18 +194,17 @@ static int parse_option_number(const char *name, const char *value, uint64_t *re
if (value != NULL) {
number = strtoull(value, &postfix, 0);
if (*postfix != '\0') {
- qerror_report(QERR_INVALID_PARAMETER_VALUE, name, "a number");
- return -1;
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, name, "a number");
+ return;
}
*ret = number;
} else {
- qerror_report(QERR_INVALID_PARAMETER_VALUE, name, "a number");
- return -1;
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, name, "a number");
}
- return 0;
}
-static int parse_option_size(const char *name, const char *value, uint64_t *ret)
+static void parse_option_size(const char *name, const char *value,
+ uint64_t *ret, Error **errp)
{
char *postfix;
double sizef;
@@ -230,16 +230,14 @@ static int parse_option_size(const char *name, const char *value, uint64_t *ret)
*ret = (uint64_t) sizef;
break;
default:
- qerror_report(QERR_INVALID_PARAMETER_VALUE, name, "a size");
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, name, "a size");
error_printf_unless_qmp("You may use k, M, G or T suffixes for "
"kilobytes, megabytes, gigabytes and terabytes.\n");
- return -1;
+ return;
}
} else {
- qerror_report(QERR_INVALID_PARAMETER_VALUE, name, "a size");
- return -1;
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, name, "a size");
}
- return 0;
}
/*
@@ -263,6 +261,7 @@ int set_option_parameter(QEMUOptionParameter *list, const char *name,
const char *value)
{
bool flag;
+ Error *local_err = NULL;
// Find a matching parameter
list = get_option_parameter(list, name);
@@ -274,9 +273,10 @@ int set_option_parameter(QEMUOptionParameter *list, const char *name,
// Process parameter
switch (list->type) {
case OPT_FLAG:
- if (parse_option_bool(name, value, &flag) == -1)
- return -1;
- list->value.n = flag;
+ parse_option_bool(name, value, &flag, &local_err);
+ if (!error_is_set(&local_err)) {
+ list->value.n = flag;
+ }
break;
case OPT_STRING:
@@ -289,8 +289,7 @@ int set_option_parameter(QEMUOptionParameter *list, const char *name,
break;
case OPT_SIZE:
- if (parse_option_size(name, value, &list->value.n) == -1)
- return -1;
+ parse_option_size(name, value, &list->value.n, &local_err);
break;
default:
@@ -298,6 +297,12 @@ int set_option_parameter(QEMUOptionParameter *list, const char *name,
return -1;
}
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ return -1;
+ }
+
return 0;
}
@@ -576,20 +581,24 @@ uint64_t qemu_opt_get_size(QemuOpts *opts, const char *name, uint64_t defval)
return opt->value.uint;
}
-static int qemu_opt_parse(QemuOpt *opt)
+static void qemu_opt_parse(QemuOpt *opt, Error **errp)
{
if (opt->desc == NULL)
- return 0;
+ return;
+
switch (opt->desc->type) {
case QEMU_OPT_STRING:
/* nothing */
- return 0;
+ return;
case QEMU_OPT_BOOL:
- return parse_option_bool(opt->name, opt->str, &opt->value.boolean);
+ parse_option_bool(opt->name, opt->str, &opt->value.boolean, errp);
+ break;
case QEMU_OPT_NUMBER:
- return parse_option_number(opt->name, opt->str, &opt->value.uint);
+ parse_option_number(opt->name, opt->str, &opt->value.uint, errp);
+ break;
case QEMU_OPT_SIZE:
- return parse_option_size(opt->name, opt->str, &opt->value.uint);
+ parse_option_size(opt->name, opt->str, &opt->value.uint, errp);
+ break;
default:
abort();
}
@@ -603,11 +612,12 @@ static void qemu_opt_del(QemuOpt *opt)
g_free(opt);
}
-static int opt_set(QemuOpts *opts, const char *name, const char *value,
- bool prepend)
+static void opt_set(QemuOpts *opts, const char *name, const char *value,
+ bool prepend, Error **errp)
{
QemuOpt *opt;
const QemuOptDesc *desc = opts->list->desc;
+ Error *local_err = NULL;
int i;
for (i = 0; desc[i].name != NULL; i++) {
@@ -619,8 +629,8 @@ static int opt_set(QemuOpts *opts, const char *name, const char *value,
if (i == 0) {
/* empty list -> allow any */;
} else {
- qerror_report(QERR_INVALID_PARAMETER, name);
- return -1;
+ error_set(errp, QERR_INVALID_PARAMETER, name);
+ return;
}
}
@@ -638,16 +648,31 @@ static int opt_set(QemuOpts *opts, const char *name, const char *value,
if (value) {
opt->str = g_strdup(value);
}
- if (qemu_opt_parse(opt) < 0) {
+ qemu_opt_parse(opt, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
qemu_opt_del(opt);
+ }
+}
+
+int qemu_opt_set(QemuOpts *opts, const char *name, const char *value)
+{
+ Error *local_err = NULL;
+
+ opt_set(opts, name, value, false, &local_err);
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
return -1;
}
+
return 0;
}
-int qemu_opt_set(QemuOpts *opts, const char *name, const char *value)
+void qemu_opt_set_err(QemuOpts *opts, const char *name, const char *value,
+ Error **errp)
{
- return opt_set(opts, name, value, false);
+ opt_set(opts, name, value, false, errp);
}
int qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val)
@@ -729,20 +754,21 @@ static int id_wellformed(const char *id)
return 1;
}
-QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, int fail_if_exists)
+QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id,
+ int fail_if_exists, Error **errp)
{
QemuOpts *opts = NULL;
if (id) {
if (!id_wellformed(id)) {
- qerror_report(QERR_INVALID_PARAMETER_VALUE, "id", "an identifier");
+ error_set(errp,QERR_INVALID_PARAMETER_VALUE, "id", "an identifier");
error_printf_unless_qmp("Identifiers consist of letters, digits, '-', '.', '_', starting with a letter.\n");
return NULL;
}
opts = qemu_opts_find(list, id);
if (opts != NULL) {
if (fail_if_exists && !list->merge_lists) {
- qerror_report(QERR_DUPLICATE_ID, id, list->name);
+ error_set(errp, QERR_DUPLICATE_ID, id, list->name);
return NULL;
} else {
return opts;
@@ -783,9 +809,12 @@ int qemu_opts_set(QemuOptsList *list, const char *id,
const char *name, const char *value)
{
QemuOpts *opts;
+ Error *local_err = NULL;
- opts = qemu_opts_create(list, id, 1);
- if (opts == NULL) {
+ opts = qemu_opts_create(list, id, 1, &local_err);
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
return -1;
}
return qemu_opt_set(opts, name, value);
@@ -829,6 +858,7 @@ static int opts_do_parse(QemuOpts *opts, const char *params,
{
char option[128], value[1024];
const char *p,*pe,*pc;
+ Error *local_err = NULL;
for (p = params; *p != '\0'; p++) {
pe = strchr(p, '=');
@@ -860,7 +890,10 @@ static int opts_do_parse(QemuOpts *opts, const char *params,
}
if (strcmp(option, "id") != 0) {
/* store and parse */
- if (opt_set(opts, option, value, prepend) == -1) {
+ opt_set(opts, option, value, prepend, &local_err);
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
return -1;
}
}
@@ -883,6 +916,7 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
char value[1024], *id = NULL;
const char *p;
QemuOpts *opts;
+ Error *local_err = NULL;
assert(!permit_abbrev || list->implied_opt_name);
firstname = permit_abbrev ? list->implied_opt_name : NULL;
@@ -898,13 +932,18 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
if (!id && !QTAILQ_EMPTY(&list->head)) {
opts = qemu_opts_find(list, NULL);
} else {
- opts = qemu_opts_create(list, id, 0);
+ opts = qemu_opts_create(list, id, 0, &local_err);
}
} else {
- opts = qemu_opts_create(list, id, 1);
+ opts = qemu_opts_create(list, id, 1, &local_err);
}
- if (opts == NULL)
+ if (opts == NULL) {
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ }
return NULL;
+ }
if (opts_do_parse(opts, params, firstname, defaults) != 0) {
qemu_opts_del(opts);
@@ -929,13 +968,19 @@ void qemu_opts_set_defaults(QemuOptsList *list, const char *params,
assert(opts);
}
+typedef struct OptsFromQDictState {
+ QemuOpts *opts;
+ Error **errp;
+} OptsFromQDictState;
+
static void qemu_opts_from_qdict_1(const char *key, QObject *obj, void *opaque)
{
+ OptsFromQDictState *state = opaque;
char buf[32];
const char *value;
int n;
- if (!strcmp(key, "id")) {
+ if (!strcmp(key, "id") || error_is_set(state->errp)) {
return;
}
@@ -963,7 +1008,8 @@ static void qemu_opts_from_qdict_1(const char *key, QObject *obj, void *opaque)
default:
return;
}
- qemu_opt_set(opaque, key, value);
+
+ qemu_opt_set_err(state->opts, key, value, state->errp);
}
/*
@@ -972,15 +1018,31 @@ static void qemu_opts_from_qdict_1(const char *key, QObject *obj, void *opaque)
* Only QStrings, QInts, QFloats and QBools are copied. Entries with
* other types are silently ignored.
*/
-QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict)
+QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict,
+ Error **errp)
{
+ OptsFromQDictState state;
+ Error *local_err = NULL;
QemuOpts *opts;
- opts = qemu_opts_create(list, qdict_get_try_str(qdict, "id"), 1);
- if (opts == NULL)
+ opts = qemu_opts_create(list, qdict_get_try_str(qdict, "id"), 1,
+ &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
+ return NULL;
+ }
+
+ assert(opts != NULL);
+
+ state.errp = &local_err;
+ state.opts = opts;
+ qdict_iter(qdict, qemu_opts_from_qdict_1, &state);
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
+ qemu_opts_del(opts);
return NULL;
+ }
- qdict_iter(qdict, qemu_opts_from_qdict_1, opts);
return opts;
}
@@ -1011,9 +1073,10 @@ QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict)
/* Validate parsed opts against descriptions where no
* descriptions were provided in the QemuOptsList.
*/
-int qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc)
+void qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc, Error **errp)
{
QemuOpt *opt;
+ Error *local_err = NULL;
assert(opts->list->desc[0].name == NULL);
@@ -1026,18 +1089,18 @@ int qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc)
}
}
if (desc[i].name == NULL) {
- qerror_report(QERR_INVALID_PARAMETER, opt->name);
- return -1;
+ error_set(errp, QERR_INVALID_PARAMETER, opt->name);
+ return;
}
opt->desc = &desc[i];
- if (qemu_opt_parse(opt) < 0) {
- return -1;
+ qemu_opt_parse(opt, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
+ return;
}
}
-
- return 0;
}
int qemu_opts_foreach(QemuOptsList *list, qemu_opts_loopfunc func, void *opaque,
diff --git a/qemu-option.h b/qemu-option.h
index 3ca00c3cec..951dec3cc4 100644
--- a/qemu-option.h
+++ b/qemu-option.h
@@ -28,6 +28,7 @@
#include <stdint.h>
#include "qemu-queue.h"
+#include "error.h"
#include "qdict.h"
enum QEMUOptionParType {
@@ -110,25 +111,29 @@ bool qemu_opt_get_bool(QemuOpts *opts, const char *name, bool defval);
uint64_t qemu_opt_get_number(QemuOpts *opts, const char *name, uint64_t defval);
uint64_t qemu_opt_get_size(QemuOpts *opts, const char *name, uint64_t defval);
int qemu_opt_set(QemuOpts *opts, const char *name, const char *value);
+void qemu_opt_set_err(QemuOpts *opts, const char *name, const char *value,
+ Error **errp);
int qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val);
typedef int (*qemu_opt_loopfunc)(const char *name, const char *value, void *opaque);
int qemu_opt_foreach(QemuOpts *opts, qemu_opt_loopfunc func, void *opaque,
int abort_on_failure);
QemuOpts *qemu_opts_find(QemuOptsList *list, const char *id);
-QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, int fail_if_exists);
+QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id,
+ int fail_if_exists, Error **errp);
void qemu_opts_reset(QemuOptsList *list);
void qemu_opts_loc_restore(QemuOpts *opts);
int qemu_opts_set(QemuOptsList *list, const char *id,
const char *name, const char *value);
const char *qemu_opts_id(QemuOpts *opts);
void qemu_opts_del(QemuOpts *opts);
-int qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc);
+void qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc, Error **errp);
int qemu_opts_do_parse(QemuOpts *opts, const char *params, const char *firstname);
QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params, int permit_abbrev);
void qemu_opts_set_defaults(QemuOptsList *list, const char *params,
int permit_abbrev);
-QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict);
+QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict,
+ Error **errp);
QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict);
typedef int (*qemu_opts_loopfunc)(QemuOpts *opts, void *opaque);
diff --git a/qemu-sockets.c b/qemu-sockets.c
index 46c7619c7e..2ae715db76 100644
--- a/qemu-sockets.c
+++ b/qemu-sockets.c
@@ -461,7 +461,7 @@ int inet_listen(const char *str, char *ostr, int olen,
char *optstr;
int sock = -1;
- opts = qemu_opts_create(&dummy_opts, NULL, 0);
+ opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
if (inet_parse(opts, str) == 0) {
sock = inet_listen_opts(opts, port_offset, errp);
if (sock != -1 && ostr) {
@@ -490,7 +490,7 @@ int inet_connect(const char *str, bool block, Error **errp)
QemuOpts *opts;
int sock = -1;
- opts = qemu_opts_create(&dummy_opts, NULL, 0);
+ opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
if (inet_parse(opts, str) == 0) {
if (block) {
qemu_opt_set(opts, "block", "on");
@@ -589,7 +589,7 @@ int unix_listen(const char *str, char *ostr, int olen)
char *path, *optstr;
int sock, len;
- opts = qemu_opts_create(&dummy_opts, NULL, 0);
+ opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
optstr = strchr(str, ',');
if (optstr) {
@@ -617,7 +617,7 @@ int unix_connect(const char *path)
QemuOpts *opts;
int sock;
- opts = qemu_opts_create(&dummy_opts, NULL, 0);
+ opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
qemu_opt_set(opts, "path", path);
sock = unix_connect_opts(opts);
qemu_opts_del(opts);
diff --git a/qerror.c b/qerror.c
index 5092fe7549..92c4eff179 100644
--- a/qerror.c
+++ b/qerror.c
@@ -156,6 +156,10 @@ static const QErrorStringTable qerror_table[] = {
.desc = "Invalid block format '%(name)'",
},
{
+ .error_fmt = QERR_INVALID_OPTION_GROUP,
+ .desc = "There is no option group '%(group)'",
+ },
+ {
.error_fmt = QERR_INVALID_PARAMETER,
.desc = "Invalid parameter '%(name)'",
},
diff --git a/qerror.h b/qerror.h
index 4cbba48c5a..b4c8758f40 100644
--- a/qerror.h
+++ b/qerror.h
@@ -139,6 +139,9 @@ QError *qobject_to_qerror(const QObject *obj);
#define QERR_INVALID_BLOCK_FORMAT \
"{ 'class': 'InvalidBlockFormat', 'data': { 'name': %s } }"
+#define QERR_INVALID_OPTION_GROUP \
+ "{ 'class': 'InvalidOptionGroup', 'data': { 'group': %s } }"
+
#define QERR_INVALID_PARAMETER \
"{ 'class': 'InvalidParameter', 'data': { 'name': %s } }"
diff --git a/qmp-commands.hx b/qmp-commands.hx
index db980fa811..2e1a38e695 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -604,12 +604,45 @@ Example:
EQMP
{
+ .name = "dump-guest-memory",
+ .args_type = "paging:b,protocol:s,begin:i?,end:i?",
+ .params = "-p protocol [begin] [length]",
+ .help = "dump guest memory to file",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = qmp_marshal_input_dump_guest_memory,
+ },
+
+SQMP
+dump
+
+
+Dump guest memory to file. The file can be processed with crash or gdb.
+
+Arguments:
+
+- "paging": do paging to get guest's memory mapping (json-bool)
+- "protocol": destination file(started with "file:") or destination file
+ descriptor (started with "fd:") (json-string)
+- "begin": the starting physical address. It's optional, and should be specified
+ with length together (json-int)
+- "length": the memory size, in bytes. It's optional, and should be specified
+ with begin together (json-int)
+
+Example:
+
+-> { "execute": "dump-guest-memory", "arguments": { "protocol": "fd:dump" } }
+<- { "return": {} }
+
+Notes:
+
+(1) All boolean arguments default to false
+
+EQMP
+
+ {
.name = "netdev_add",
.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_new = qmp_netdev_add,
},
SQMP
@@ -638,10 +671,7 @@ EQMP
{
.name = "netdev_del",
.args_type = "id:s",
- .params = "id",
- .help = "remove host network device",
- .user_print = monitor_user_noop,
- .mhandler.cmd_new = do_netdev_del,
+ .mhandler.cmd_new = qmp_marshal_input_netdev_del,
},
SQMP
@@ -1179,6 +1209,43 @@ EQMP
},
SQMP
+query-events
+--------------
+
+List QMP available events.
+
+Each event is represented by a json-object, the returned value is a json-array
+of all events.
+
+Each json-object contains:
+
+- "name": event's name (json-string)
+
+Example:
+
+-> { "execute": "query-events" }
+<- {
+ "return":[
+ {
+ "name":"SHUTDOWN"
+ },
+ {
+ "name":"RESET"
+ }
+ ]
+ }
+
+Note: This example has been shortened as the real response is too long.
+
+EQMP
+
+ {
+ .name = "query-events",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_query_events,
+ },
+
+SQMP
query-chardev
-------------
diff --git a/target-i386/arch_dump.c b/target-i386/arch_dump.c
new file mode 100644
index 0000000000..135d855c4a
--- /dev/null
+++ b/target-i386/arch_dump.c
@@ -0,0 +1,449 @@
+/*
+ * i386 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 "dump.h"
+#include "elf.h"
+
+#ifdef TARGET_X86_64
+typedef struct {
+ target_ulong r15, r14, r13, r12, rbp, rbx, r11, r10;
+ target_ulong r9, r8, rax, rcx, rdx, rsi, rdi, orig_rax;
+ target_ulong rip, cs, eflags;
+ target_ulong rsp, ss;
+ target_ulong fs_base, gs_base;
+ target_ulong ds, es, fs, gs;
+} x86_64_user_regs_struct;
+
+typedef struct {
+ char pad1[32];
+ uint32_t pid;
+ char pad2[76];
+ x86_64_user_regs_struct regs;
+ char pad3[8];
+} x86_64_elf_prstatus;
+
+static int x86_64_write_elf64_note(write_core_dump_function f,
+ CPUArchState *env, int id,
+ void *opaque)
+{
+ x86_64_user_regs_struct regs;
+ Elf64_Nhdr *note;
+ char *buf;
+ int descsz, note_size, name_size = 5;
+ const char *name = "CORE";
+ int ret;
+
+ regs.r15 = env->regs[15];
+ regs.r14 = env->regs[14];
+ regs.r13 = env->regs[13];
+ regs.r12 = env->regs[12];
+ regs.r11 = env->regs[11];
+ regs.r10 = env->regs[10];
+ regs.r9 = env->regs[9];
+ regs.r8 = env->regs[8];
+ regs.rbp = env->regs[R_EBP];
+ regs.rsp = env->regs[R_ESP];
+ regs.rdi = env->regs[R_EDI];
+ regs.rsi = env->regs[R_ESI];
+ regs.rdx = env->regs[R_EDX];
+ regs.rcx = env->regs[R_ECX];
+ regs.rbx = env->regs[R_EBX];
+ regs.rax = env->regs[R_EAX];
+ regs.rip = env->eip;
+ regs.eflags = env->eflags;
+
+ regs.orig_rax = 0; /* FIXME */
+ regs.cs = env->segs[R_CS].selector;
+ regs.ss = env->segs[R_SS].selector;
+ regs.fs_base = env->segs[R_FS].base;
+ regs.gs_base = env->segs[R_GS].base;
+ regs.ds = env->segs[R_DS].selector;
+ regs.es = env->segs[R_ES].selector;
+ regs.fs = env->segs[R_FS].selector;
+ regs.gs = env->segs[R_GS].selector;
+
+ descsz = sizeof(x86_64_elf_prstatus);
+ note_size = ((sizeof(Elf64_Nhdr) + 3) / 4 + (name_size + 3) / 4 +
+ (descsz + 3) / 4) * 4;
+ note = g_malloc(note_size);
+
+ memset(note, 0, note_size);
+ note->n_namesz = cpu_to_le32(name_size);
+ note->n_descsz = cpu_to_le32(descsz);
+ note->n_type = cpu_to_le32(NT_PRSTATUS);
+ buf = (char *)note;
+ buf += ((sizeof(Elf64_Nhdr) + 3) / 4) * 4;
+ memcpy(buf, name, name_size);
+ buf += ((name_size + 3) / 4) * 4;
+ memcpy(buf + 32, &id, 4); /* pr_pid */
+ buf += descsz - sizeof(x86_64_user_regs_struct)-sizeof(target_ulong);
+ memcpy(buf, &regs, sizeof(x86_64_user_regs_struct));
+
+ ret = f(note, note_size, opaque);
+ g_free(note);
+ if (ret < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+#endif
+
+typedef struct {
+ uint32_t ebx, ecx, edx, esi, edi, ebp, eax;
+ unsigned short ds, __ds, es, __es;
+ unsigned short fs, __fs, gs, __gs;
+ uint32_t orig_eax, eip;
+ unsigned short cs, __cs;
+ uint32_t eflags, esp;
+ unsigned short ss, __ss;
+} x86_user_regs_struct;
+
+typedef struct {
+ char pad1[24];
+ uint32_t pid;
+ char pad2[44];
+ x86_user_regs_struct regs;
+ char pad3[4];
+} x86_elf_prstatus;
+
+static void x86_fill_elf_prstatus(x86_elf_prstatus *prstatus, CPUArchState *env,
+ int id)
+{
+ memset(prstatus, 0, sizeof(x86_elf_prstatus));
+ prstatus->regs.ebp = env->regs[R_EBP] & 0xffffffff;
+ prstatus->regs.esp = env->regs[R_ESP] & 0xffffffff;
+ prstatus->regs.edi = env->regs[R_EDI] & 0xffffffff;
+ prstatus->regs.esi = env->regs[R_ESI] & 0xffffffff;
+ prstatus->regs.edx = env->regs[R_EDX] & 0xffffffff;
+ prstatus->regs.ecx = env->regs[R_ECX] & 0xffffffff;
+ prstatus->regs.ebx = env->regs[R_EBX] & 0xffffffff;
+ prstatus->regs.eax = env->regs[R_EAX] & 0xffffffff;
+ prstatus->regs.eip = env->eip & 0xffffffff;
+ prstatus->regs.eflags = env->eflags & 0xffffffff;
+
+ prstatus->regs.cs = env->segs[R_CS].selector;
+ prstatus->regs.ss = env->segs[R_SS].selector;
+ prstatus->regs.ds = env->segs[R_DS].selector;
+ prstatus->regs.es = env->segs[R_ES].selector;
+ prstatus->regs.fs = env->segs[R_FS].selector;
+ prstatus->regs.gs = env->segs[R_GS].selector;
+
+ prstatus->pid = id;
+}
+
+static int x86_write_elf64_note(write_core_dump_function f, CPUArchState *env,
+ int id, void *opaque)
+{
+ x86_elf_prstatus prstatus;
+ Elf64_Nhdr *note;
+ char *buf;
+ int descsz, note_size, name_size = 5;
+ const char *name = "CORE";
+ int ret;
+
+ x86_fill_elf_prstatus(&prstatus, env, id);
+ descsz = sizeof(x86_elf_prstatus);
+ note_size = ((sizeof(Elf64_Nhdr) + 3) / 4 + (name_size + 3) / 4 +
+ (descsz + 3) / 4) * 4;
+ note = g_malloc(note_size);
+
+ memset(note, 0, note_size);
+ note->n_namesz = cpu_to_le32(name_size);
+ note->n_descsz = cpu_to_le32(descsz);
+ note->n_type = cpu_to_le32(NT_PRSTATUS);
+ buf = (char *)note;
+ buf += ((sizeof(Elf64_Nhdr) + 3) / 4) * 4;
+ memcpy(buf, name, name_size);
+ buf += ((name_size + 3) / 4) * 4;
+ memcpy(buf, &prstatus, sizeof(prstatus));
+
+ ret = f(note, note_size, opaque);
+ g_free(note);
+ if (ret < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int cpu_write_elf64_note(write_core_dump_function f, CPUArchState *env,
+ int cpuid, void *opaque)
+{
+ int ret;
+#ifdef TARGET_X86_64
+ bool lma = !!(first_cpu->hflags & HF_LMA_MASK);
+
+ if (lma) {
+ ret = x86_64_write_elf64_note(f, env, cpuid, opaque);
+ } else {
+#endif
+ ret = x86_write_elf64_note(f, env, cpuid, opaque);
+#ifdef TARGET_X86_64
+ }
+#endif
+
+ return ret;
+}
+
+int cpu_write_elf32_note(write_core_dump_function f, CPUArchState *env,
+ int cpuid, void *opaque)
+{
+ x86_elf_prstatus prstatus;
+ Elf32_Nhdr *note;
+ char *buf;
+ int descsz, note_size, name_size = 5;
+ const char *name = "CORE";
+ int ret;
+
+ x86_fill_elf_prstatus(&prstatus, env, cpuid);
+ descsz = sizeof(x86_elf_prstatus);
+ note_size = ((sizeof(Elf32_Nhdr) + 3) / 4 + (name_size + 3) / 4 +
+ (descsz + 3) / 4) * 4;
+ note = g_malloc(note_size);
+
+ memset(note, 0, note_size);
+ note->n_namesz = cpu_to_le32(name_size);
+ note->n_descsz = cpu_to_le32(descsz);
+ note->n_type = cpu_to_le32(NT_PRSTATUS);
+ buf = (char *)note;
+ buf += ((sizeof(Elf32_Nhdr) + 3) / 4) * 4;
+ memcpy(buf, name, name_size);
+ buf += ((name_size + 3) / 4) * 4;
+ memcpy(buf, &prstatus, sizeof(prstatus));
+
+ ret = f(note, note_size, opaque);
+ g_free(note);
+ if (ret < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * please count up QEMUCPUSTATE_VERSION if you have changed definition of
+ * QEMUCPUState, and modify the tools using this information accordingly.
+ */
+#define QEMUCPUSTATE_VERSION (1)
+
+struct QEMUCPUSegment {
+ uint32_t selector;
+ uint32_t limit;
+ uint32_t flags;
+ uint32_t pad;
+ uint64_t base;
+};
+
+typedef struct QEMUCPUSegment QEMUCPUSegment;
+
+struct QEMUCPUState {
+ uint32_t version;
+ uint32_t size;
+ uint64_t rax, rbx, rcx, rdx, rsi, rdi, rsp, rbp;
+ uint64_t r8, r9, r10, r11, r12, r13, r14, r15;
+ uint64_t rip, rflags;
+ QEMUCPUSegment cs, ds, es, fs, gs, ss;
+ QEMUCPUSegment ldt, tr, gdt, idt;
+ uint64_t cr[5];
+};
+
+typedef struct QEMUCPUState QEMUCPUState;
+
+static void copy_segment(QEMUCPUSegment *d, SegmentCache *s)
+{
+ d->pad = 0;
+ d->selector = s->selector;
+ d->limit = s->limit;
+ d->flags = s->flags;
+ d->base = s->base;
+}
+
+static void qemu_get_cpustate(QEMUCPUState *s, CPUArchState *env)
+{
+ memset(s, 0, sizeof(QEMUCPUState));
+
+ s->version = QEMUCPUSTATE_VERSION;
+ s->size = sizeof(QEMUCPUState);
+
+ s->rax = env->regs[R_EAX];
+ s->rbx = env->regs[R_EBX];
+ s->rcx = env->regs[R_ECX];
+ s->rdx = env->regs[R_EDX];
+ s->rsi = env->regs[R_ESI];
+ s->rdi = env->regs[R_EDI];
+ s->rsp = env->regs[R_ESP];
+ s->rbp = env->regs[R_EBP];
+#ifdef TARGET_X86_64
+ s->r8 = env->regs[8];
+ s->r9 = env->regs[9];
+ s->r10 = env->regs[10];
+ s->r11 = env->regs[11];
+ s->r12 = env->regs[12];
+ s->r13 = env->regs[13];
+ s->r14 = env->regs[14];
+ s->r15 = env->regs[15];
+#endif
+ s->rip = env->eip;
+ s->rflags = env->eflags;
+
+ copy_segment(&s->cs, &env->segs[R_CS]);
+ copy_segment(&s->ds, &env->segs[R_DS]);
+ copy_segment(&s->es, &env->segs[R_ES]);
+ copy_segment(&s->fs, &env->segs[R_FS]);
+ copy_segment(&s->gs, &env->segs[R_GS]);
+ copy_segment(&s->ss, &env->segs[R_SS]);
+ copy_segment(&s->ldt, &env->ldt);
+ copy_segment(&s->tr, &env->tr);
+ copy_segment(&s->gdt, &env->gdt);
+ copy_segment(&s->idt, &env->idt);
+
+ s->cr[0] = env->cr[0];
+ s->cr[1] = env->cr[1];
+ s->cr[2] = env->cr[2];
+ s->cr[3] = env->cr[3];
+ s->cr[4] = env->cr[4];
+}
+
+static inline int cpu_write_qemu_note(write_core_dump_function f,
+ CPUArchState *env,
+ void *opaque,
+ int type)
+{
+ QEMUCPUState state;
+ Elf64_Nhdr *note64;
+ Elf32_Nhdr *note32;
+ void *note;
+ char *buf;
+ int descsz, note_size, name_size = 5, note_head_size;
+ const char *name = "QEMU";
+ int ret;
+
+ qemu_get_cpustate(&state, env);
+
+ descsz = sizeof(state);
+ if (type == 0) {
+ note_head_size = sizeof(Elf32_Nhdr);
+ } else {
+ note_head_size = sizeof(Elf64_Nhdr);
+ }
+ note_size = ((note_head_size + 3) / 4 + (name_size + 3) / 4 +
+ (descsz + 3) / 4) * 4;
+ note = g_malloc(note_size);
+
+ memset(note, 0, note_size);
+ if (type == 0) {
+ note32 = note;
+ note32->n_namesz = cpu_to_le32(name_size);
+ note32->n_descsz = cpu_to_le32(descsz);
+ note32->n_type = 0;
+ } else {
+ note64 = note;
+ note64->n_namesz = cpu_to_le32(name_size);
+ note64->n_descsz = cpu_to_le32(descsz);
+ note64->n_type = 0;
+ }
+ buf = note;
+ buf += ((note_head_size + 3) / 4) * 4;
+ memcpy(buf, name, name_size);
+ buf += ((name_size + 3) / 4) * 4;
+ memcpy(buf, &state, sizeof(state));
+
+ ret = f(note, note_size, opaque);
+ g_free(note);
+ if (ret < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int cpu_write_elf64_qemunote(write_core_dump_function f, CPUArchState *env,
+ void *opaque)
+{
+ return cpu_write_qemu_note(f, env, opaque, 1);
+}
+
+int cpu_write_elf32_qemunote(write_core_dump_function f, CPUArchState *env,
+ void *opaque)
+{
+ return cpu_write_qemu_note(f, env, opaque, 0);
+}
+
+int cpu_get_dump_info(ArchDumpInfo *info)
+{
+ bool lma = false;
+ RAMBlock *block;
+
+#ifdef TARGET_X86_64
+ lma = !!(first_cpu->hflags & HF_LMA_MASK);
+#endif
+
+ if (lma) {
+ info->d_machine = EM_X86_64;
+ } else {
+ info->d_machine = EM_386;
+ }
+ info->d_endian = ELFDATA2LSB;
+
+ if (lma) {
+ info->d_class = ELFCLASS64;
+ } else {
+ info->d_class = ELFCLASS32;
+
+ QLIST_FOREACH(block, &ram_list.blocks, next) {
+ if (block->offset + block->length > UINT_MAX) {
+ /* The memory size is greater than 4G */
+ info->d_class = ELFCLASS64;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+size_t cpu_get_note_size(int class, int machine, int nr_cpus)
+{
+ int name_size = 5; /* "CORE" or "QEMU" */
+ size_t elf_note_size = 0;
+ size_t qemu_note_size = 0;
+ int elf_desc_size = 0;
+ int qemu_desc_size = 0;
+ int note_head_size;
+
+ if (class == ELFCLASS32) {
+ note_head_size = sizeof(Elf32_Nhdr);
+ } else {
+ note_head_size = sizeof(Elf64_Nhdr);
+ }
+
+ if (machine == EM_386) {
+ elf_desc_size = sizeof(x86_elf_prstatus);
+ }
+#ifdef TARGET_X86_64
+ else {
+ elf_desc_size = sizeof(x86_64_elf_prstatus);
+ }
+#endif
+ qemu_desc_size = sizeof(QEMUCPUState);
+
+ elf_note_size = ((note_head_size + 3) / 4 + (name_size + 3) / 4 +
+ (elf_desc_size + 3) / 4) * 4;
+ qemu_note_size = ((note_head_size + 3) / 4 + (name_size + 3) / 4 +
+ (qemu_desc_size + 3) / 4) * 4;
+
+ return (elf_note_size + qemu_note_size) * nr_cpus;
+}
diff --git a/target-i386/arch_memory_mapping.c b/target-i386/arch_memory_mapping.c
new file mode 100644
index 0000000000..bd50e1143a
--- /dev/null
+++ b/target-i386/arch_memory_mapping.c
@@ -0,0 +1,271 @@
+/*
+ * i386 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"
+
+/* PAE Paging or IA-32e Paging */
+static void walk_pte(MemoryMappingList *list, target_phys_addr_t pte_start_addr,
+ int32_t a20_mask, target_ulong start_line_addr)
+{
+ target_phys_addr_t pte_addr, start_paddr;
+ uint64_t pte;
+ target_ulong start_vaddr;
+ int i;
+
+ for (i = 0; i < 512; i++) {
+ pte_addr = (pte_start_addr + i * 8) & a20_mask;
+ pte = ldq_phys(pte_addr);
+ if (!(pte & PG_PRESENT_MASK)) {
+ /* not present */
+ continue;
+ }
+
+ start_paddr = (pte & ~0xfff) & ~(0x1ULL << 63);
+ if (cpu_physical_memory_is_io(start_paddr)) {
+ /* I/O region */
+ continue;
+ }
+
+ start_vaddr = start_line_addr | ((i & 0x1fff) << 12);
+ memory_mapping_list_add_merge_sorted(list, start_paddr,
+ start_vaddr, 1 << 12);
+ }
+}
+
+/* 32-bit Paging */
+static void walk_pte2(MemoryMappingList *list,
+ target_phys_addr_t pte_start_addr, int32_t a20_mask,
+ target_ulong start_line_addr)
+{
+ target_phys_addr_t pte_addr, start_paddr;
+ uint32_t pte;
+ target_ulong start_vaddr;
+ int i;
+
+ for (i = 0; i < 1024; i++) {
+ pte_addr = (pte_start_addr + i * 4) & a20_mask;
+ pte = ldl_phys(pte_addr);
+ if (!(pte & PG_PRESENT_MASK)) {
+ /* not present */
+ continue;
+ }
+
+ start_paddr = pte & ~0xfff;
+ if (cpu_physical_memory_is_io(start_paddr)) {
+ /* I/O region */
+ continue;
+ }
+
+ start_vaddr = start_line_addr | ((i & 0x3ff) << 12);
+ memory_mapping_list_add_merge_sorted(list, start_paddr,
+ start_vaddr, 1 << 12);
+ }
+}
+
+/* PAE Paging or IA-32e Paging */
+static void walk_pde(MemoryMappingList *list, target_phys_addr_t pde_start_addr,
+ int32_t a20_mask, target_ulong start_line_addr)
+{
+ target_phys_addr_t pde_addr, pte_start_addr, start_paddr;
+ uint64_t pde;
+ target_ulong line_addr, start_vaddr;
+ int i;
+
+ for (i = 0; i < 512; i++) {
+ pde_addr = (pde_start_addr + i * 8) & a20_mask;
+ pde = ldq_phys(pde_addr);
+ if (!(pde & PG_PRESENT_MASK)) {
+ /* not present */
+ continue;
+ }
+
+ line_addr = start_line_addr | ((i & 0x1ff) << 21);
+ if (pde & PG_PSE_MASK) {
+ /* 2 MB page */
+ start_paddr = (pde & ~0x1fffff) & ~(0x1ULL << 63);
+ if (cpu_physical_memory_is_io(start_paddr)) {
+ /* I/O region */
+ continue;
+ }
+ start_vaddr = line_addr;
+ memory_mapping_list_add_merge_sorted(list, start_paddr,
+ start_vaddr, 1 << 21);
+ continue;
+ }
+
+ pte_start_addr = (pde & ~0xfff) & a20_mask;
+ walk_pte(list, pte_start_addr, a20_mask, line_addr);
+ }
+}
+
+/* 32-bit Paging */
+static void walk_pde2(MemoryMappingList *list,
+ target_phys_addr_t pde_start_addr, int32_t a20_mask,
+ bool pse)
+{
+ target_phys_addr_t pde_addr, pte_start_addr, start_paddr;
+ uint32_t pde;
+ target_ulong line_addr, start_vaddr;
+ int i;
+
+ for (i = 0; i < 1024; i++) {
+ pde_addr = (pde_start_addr + i * 4) & a20_mask;
+ pde = ldl_phys(pde_addr);
+ if (!(pde & PG_PRESENT_MASK)) {
+ /* not present */
+ continue;
+ }
+
+ line_addr = (((unsigned int)i & 0x3ff) << 22);
+ if ((pde & PG_PSE_MASK) && pse) {
+ /* 4 MB page */
+ start_paddr = (pde & ~0x3fffff) | ((pde & 0x1fe000) << 19);
+ if (cpu_physical_memory_is_io(start_paddr)) {
+ /* I/O region */
+ continue;
+ }
+ start_vaddr = line_addr;
+ memory_mapping_list_add_merge_sorted(list, start_paddr,
+ start_vaddr, 1 << 22);
+ continue;
+ }
+
+ pte_start_addr = (pde & ~0xfff) & a20_mask;
+ walk_pte2(list, pte_start_addr, a20_mask, line_addr);
+ }
+}
+
+/* PAE Paging */
+static void walk_pdpe2(MemoryMappingList *list,
+ target_phys_addr_t pdpe_start_addr, int32_t a20_mask)
+{
+ target_phys_addr_t pdpe_addr, pde_start_addr;
+ uint64_t pdpe;
+ target_ulong line_addr;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ pdpe_addr = (pdpe_start_addr + i * 8) & a20_mask;
+ pdpe = ldq_phys(pdpe_addr);
+ if (!(pdpe & PG_PRESENT_MASK)) {
+ /* not present */
+ continue;
+ }
+
+ line_addr = (((unsigned int)i & 0x3) << 30);
+ pde_start_addr = (pdpe & ~0xfff) & a20_mask;
+ walk_pde(list, pde_start_addr, a20_mask, line_addr);
+ }
+}
+
+#ifdef TARGET_X86_64
+/* IA-32e Paging */
+static void walk_pdpe(MemoryMappingList *list,
+ target_phys_addr_t pdpe_start_addr, int32_t a20_mask,
+ target_ulong start_line_addr)
+{
+ target_phys_addr_t pdpe_addr, pde_start_addr, start_paddr;
+ uint64_t pdpe;
+ target_ulong line_addr, start_vaddr;
+ int i;
+
+ for (i = 0; i < 512; i++) {
+ pdpe_addr = (pdpe_start_addr + i * 8) & a20_mask;
+ pdpe = ldq_phys(pdpe_addr);
+ if (!(pdpe & PG_PRESENT_MASK)) {
+ /* not present */
+ continue;
+ }
+
+ line_addr = start_line_addr | ((i & 0x1ffULL) << 30);
+ if (pdpe & PG_PSE_MASK) {
+ /* 1 GB page */
+ start_paddr = (pdpe & ~0x3fffffff) & ~(0x1ULL << 63);
+ if (cpu_physical_memory_is_io(start_paddr)) {
+ /* I/O region */
+ continue;
+ }
+ start_vaddr = line_addr;
+ memory_mapping_list_add_merge_sorted(list, start_paddr,
+ start_vaddr, 1 << 30);
+ continue;
+ }
+
+ pde_start_addr = (pdpe & ~0xfff) & a20_mask;
+ walk_pde(list, pde_start_addr, a20_mask, line_addr);
+ }
+}
+
+/* IA-32e Paging */
+static void walk_pml4e(MemoryMappingList *list,
+ target_phys_addr_t pml4e_start_addr, int32_t a20_mask)
+{
+ target_phys_addr_t pml4e_addr, pdpe_start_addr;
+ uint64_t pml4e;
+ target_ulong line_addr;
+ int i;
+
+ for (i = 0; i < 512; i++) {
+ pml4e_addr = (pml4e_start_addr + i * 8) & a20_mask;
+ pml4e = ldq_phys(pml4e_addr);
+ if (!(pml4e & PG_PRESENT_MASK)) {
+ /* not present */
+ continue;
+ }
+
+ line_addr = ((i & 0x1ffULL) << 39) | (0xffffULL << 48);
+ pdpe_start_addr = (pml4e & ~0xfff) & a20_mask;
+ walk_pdpe(list, pdpe_start_addr, a20_mask, line_addr);
+ }
+}
+#endif
+
+int cpu_get_memory_mapping(MemoryMappingList *list, CPUArchState *env)
+{
+ if (!cpu_paging_enabled(env)) {
+ /* paging is disabled */
+ return 0;
+ }
+
+ if (env->cr[4] & CR4_PAE_MASK) {
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK) {
+ target_phys_addr_t pml4e_addr;
+
+ pml4e_addr = (env->cr[3] & ~0xfff) & env->a20_mask;
+ walk_pml4e(list, pml4e_addr, env->a20_mask);
+ } else
+#endif
+ {
+ target_phys_addr_t pdpe_addr;
+
+ pdpe_addr = (env->cr[3] & ~0x1f) & env->a20_mask;
+ walk_pdpe2(list, pdpe_addr, env->a20_mask);
+ }
+ } else {
+ target_phys_addr_t pde_addr;
+ bool pse;
+
+ pde_addr = (env->cr[3] & ~0xfff) & env->a20_mask;
+ pse = !!(env->cr[4] & CR4_PSE_MASK);
+ walk_pde2(list, pde_addr, env->a20_mask, pse);
+ }
+
+ return 0;
+}
+
+bool cpu_paging_enabled(CPUArchState *env)
+{
+ return env->cr[0] & CR0_PG_MASK;
+}
diff --git a/vl.c b/vl.c
index 23ab3a34d0..148542617d 100644
--- a/vl.c
+++ b/vl.c
@@ -1786,7 +1786,7 @@ static int balloon_parse(const char *arg)
return -1;
} else {
/* create empty opts */
- 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", "virtio-balloon");
return 0;
@@ -1921,7 +1921,7 @@ static void monitor_parse(const char *optarg, const char *mode)
}
}
- opts = qemu_opts_create(qemu_find_opts("mon"), label, 1);
+ opts = qemu_opts_create(qemu_find_opts("mon"), label, 1, NULL);
if (!opts) {
fprintf(stderr, "duplicate chardev: %s\n", label);
exit(1);
@@ -2035,14 +2035,14 @@ static int virtcon_parse(const char *devname)
exit(1);
}
- bus_opts = qemu_opts_create(device, NULL, 0);
+ bus_opts = qemu_opts_create(device, NULL, 0, NULL);
if (arch_type == QEMU_ARCH_S390X) {
qemu_opt_set(bus_opts, "driver", "virtio-serial-s390");
} else {
qemu_opt_set(bus_opts, "driver", "virtio-serial-pci");
}
- dev_opts = qemu_opts_create(device, NULL, 0);
+ dev_opts = qemu_opts_create(device, NULL, 0, NULL);
qemu_opt_set(dev_opts, "driver", "virtconsole");
snprintf(label, sizeof(label), "virtcon%d", index);
@@ -2065,7 +2065,7 @@ static int debugcon_parse(const char *devname)
if (!qemu_chr_new("debugcon", devname, NULL)) {
exit(1);
}
- opts = qemu_opts_create(qemu_find_opts("device"), "debugcon", 1);
+ opts = qemu_opts_create(qemu_find_opts("device"), "debugcon", 1, NULL);
if (!opts) {
fprintf(stderr, "qemu: already have a debugcon device\n");
exit(1);
@@ -2813,7 +2813,8 @@ int main(int argc, char **argv, char **envp)
exit(1);
}
fsdev = qemu_opts_create(qemu_find_opts("fsdev"),
- qemu_opt_get(opts, "mount_tag"), 1);
+ qemu_opt_get(opts, "mount_tag"),
+ 1, NULL);
if (!fsdev) {
fprintf(stderr, "duplicate fsdev id: %s\n",
qemu_opt_get(opts, "mount_tag"));
@@ -2845,7 +2846,8 @@ int main(int argc, char **argv, char **envp)
qemu_opt_set_bool(fsdev, "readonly",
qemu_opt_get_bool(opts, "readonly", 0));
- device = qemu_opts_create(qemu_find_opts("device"), NULL, 0);
+ device = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
+ NULL);
qemu_opt_set(device, "driver", "virtio-9p-pci");
qemu_opt_set(device, "fsdev",
qemu_opt_get(opts, "mount_tag"));
@@ -2857,14 +2859,16 @@ int main(int argc, char **argv, char **envp)
QemuOpts *fsdev;
QemuOpts *device;
- fsdev = qemu_opts_create(qemu_find_opts("fsdev"), "v_synth", 1);
+ fsdev = qemu_opts_create(qemu_find_opts("fsdev"), "v_synth",
+ 1, NULL);
if (!fsdev) {
fprintf(stderr, "duplicate option: %s\n", "virtfs_synth");
exit(1);
}
qemu_opt_set(fsdev, "fsdriver", "synth");
- device = qemu_opts_create(qemu_find_opts("device"), NULL, 0);
+ device = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
+ NULL);
qemu_opt_set(device, "driver", "virtio-9p-pci");
qemu_opt_set(device, "fsdev", "v_synth");
qemu_opt_set(device, "mount_tag", "v_synth");