Merge remote-tracking branch 'qmp/queue/qmp' into staging

* qmp/queue/qmp: (29 commits)
  Add 'query-events' command to QMP to query async events
  qapi: convert netdev_del
  qapi: convert netdev_add
  net: net_client_init(): use error_set()
  net: purge the monitor object from all init functions
  qemu-config: introduce qemu_find_opts_err()
  qemu-config: find_list(): use error_set()
  qerror: introduce QERR_INVALID_OPTION_GROUP
  qemu-option: qemu_opts_from_qdict(): use error_set()
  qemu-option: introduce qemu_opt_set_err()
  qemu-option: opt_set(): use error_set()
  qemu-option: qemu_opts_validate(): use error_set()
  qemu-option: qemu_opt_parse(): use error_set()
  qemu-option: parse_option_size(): use error_set()
  qemu-option: parse_option_bool(): use error_set()
  qemu-option: parse_option_number(): use error_set()
  qemu-option: qemu_opts_create(): use error_set()
  introduce a new monitor command 'dump-guest-memory' to dump guest's memory
  make gdb_id() generally avialable and rename it to cpu_index()
  target-i386: Add API to get note's size
  ...
This commit is contained in:
Anthony Liguori 2012-06-06 20:57:56 +08:00
commit 349417004a
49 changed files with 2693 additions and 255 deletions

View file

@ -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

View file

@ -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 {

8
configure vendored
View file

@ -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

View file

@ -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 */

View file

@ -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

883
dump.c Normal file
View file

@ -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

23
dump.h Normal file
View file

@ -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

5
elf.h
View file

@ -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;

12
exec.c
View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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

52
hmp.c
View file

@ -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);
}

3
hmp.h
View file

@ -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

View file

@ -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;

View file

@ -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)) {

View file

@ -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;
}

View file

@ -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++) {

View file

@ -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;
}

249
memory_mapping.c Normal file
View file

@ -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;
}
}
}

74
memory_mapping.h Normal file
View file

@ -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

107
monitor.c
View file

@ -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)
{

View file

@ -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;

118
net.c
View file

@ -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)
{
QemuOpts *opts;
int res;
opts = qemu_opts_from_qdict(qemu_find_opts("netdev"), qdict);
if (!opts) {
return -1;
}
res = net_client_init(mon, opts, 1);
if (res < 0) {
qemu_opts_del(opts);
}
return res;
net_client_init(opts, 1, errp);
}
int do_netdev_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
int qmp_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret)
{
Error *local_err = NULL;
QemuOptsList *opts_list;
QemuOpts *opts;
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;
}
netdev_add(opts, &local_err);
if (error_is_set(&local_err)) {
qemu_opts_del(opts);
goto exit_err;
}
return 0;
exit_err:
qerror_report_err(local_err);
error_free(local_err);
return -1;
}
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)

6
net.h
View file

@ -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"

View file

@ -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;

View file

@ -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 */

View file

@ -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;

View file

@ -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);

View file

@ -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;
}

View file

@ -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 */

View file

@ -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;

View file

@ -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;
}

View file

@ -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 */

View file

@ -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;

View file

@ -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 */

View file

@ -227,6 +227,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
#
@ -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'} }

View file

@ -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;

View file

@ -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) {

View file

@ -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);

View file

@ -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);
return -1;
}
return 0;
}
int qemu_opt_set(QemuOpts *opts, const char *name, const char *value)
{
return opt_set(opts, name, value, false);
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;
}
void qemu_opt_set_err(QemuOpts *opts, const char *name, const char *value,
Error **errp)
{
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,

View file

@ -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);

View file

@ -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);

View file

@ -155,6 +155,10 @@ static const QErrorStringTable qerror_table[] = {
.error_fmt = QERR_INVALID_BLOCK_FORMAT,
.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)'",

View file

@ -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 } }"

View file

@ -601,15 +601,48 @@ Example:
"port": 1234 } }
<- { "return": {} }
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
@ -1178,6 +1208,43 @@ EQMP
.mhandler.cmd_new = qmp_marshal_input_query_commands,
},
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
-------------

449
target-i386/arch_dump.c Normal file
View file

@ -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;
}

View file

@ -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;
}

22
vl.c
View file

@ -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");