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:
commit
349417004a
@ -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
|
||||
|
@ -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
8
configure
vendored
@ -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
|
||||
|
70
cpu-all.h
70
cpu-all.h
@ -22,6 +22,8 @@
|
||||
#include "qemu-common.h"
|
||||
#include "qemu-tls.h"
|
||||
#include "cpu-common.h"
|
||||
#include "memory_mapping.h"
|
||||
#include "dump.h"
|
||||
|
||||
/* some important defines:
|
||||
*
|
||||
@ -523,4 +525,72 @@ void dump_exec_info(FILE *f, fprintf_function cpu_fprintf);
|
||||
int cpu_memory_rw_debug(CPUArchState *env, target_ulong addr,
|
||||
uint8_t *buf, int len, int is_write);
|
||||
|
||||
#if defined(CONFIG_HAVE_GET_MEMORY_MAPPING)
|
||||
int cpu_get_memory_mapping(MemoryMappingList *list, CPUArchState *env);
|
||||
bool cpu_paging_enabled(CPUArchState *env);
|
||||
#else
|
||||
static inline int cpu_get_memory_mapping(MemoryMappingList *list,
|
||||
CPUArchState *env)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline bool cpu_paging_enabled(CPUArchState *env)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef int (*write_core_dump_function)(void *buf, size_t size, void *opaque);
|
||||
#if defined(CONFIG_HAVE_CORE_DUMP)
|
||||
int cpu_write_elf64_note(write_core_dump_function f, CPUArchState *env,
|
||||
int cpuid, void *opaque);
|
||||
int cpu_write_elf32_note(write_core_dump_function f, CPUArchState *env,
|
||||
int cpuid, void *opaque);
|
||||
int cpu_write_elf64_qemunote(write_core_dump_function f, CPUArchState *env,
|
||||
void *opaque);
|
||||
int cpu_write_elf32_qemunote(write_core_dump_function f, CPUArchState *env,
|
||||
void *opaque);
|
||||
int cpu_get_dump_info(ArchDumpInfo *info);
|
||||
size_t cpu_get_note_size(int class, int machine, int nr_cpus);
|
||||
#else
|
||||
static inline int cpu_write_elf64_note(write_core_dump_function f,
|
||||
CPUArchState *env, int cpuid,
|
||||
void *opaque)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int cpu_write_elf32_note(write_core_dump_function f,
|
||||
CPUArchState *env, int cpuid,
|
||||
void *opaque)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int cpu_write_elf64_qemunote(write_core_dump_function f,
|
||||
CPUArchState *env,
|
||||
void *opaque)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int cpu_write_elf32_qemunote(write_core_dump_function f,
|
||||
CPUArchState *env,
|
||||
void *opaque)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int cpu_get_dump_info(ArchDumpInfo *info)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int cpu_get_note_size(int class, int machine, int nr_cpus)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CPU_ALL_H */
|
||||
|
@ -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
883
dump.c
Normal 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
23
dump.h
Normal 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
5
elf.h
@ -1037,6 +1037,11 @@ typedef struct elf64_sym {
|
||||
|
||||
#define EI_NIDENT 16
|
||||
|
||||
/* Special value for e_phnum. This indicates that the real number of
|
||||
program headers is too large to fit into e_phnum. Instead the real
|
||||
value is in the field sh_info of section 0. */
|
||||
#define PN_XNUM 0xffff
|
||||
|
||||
typedef struct elf32_hdr{
|
||||
unsigned char e_ident[EI_NIDENT];
|
||||
Elf32_Half e_type;
|
||||
|
12
exec.c
12
exec.c
@ -4336,3 +4336,15 @@ bool virtio_is_big_endian(void)
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
bool cpu_physical_memory_is_io(target_phys_addr_t phys_addr)
|
||||
{
|
||||
MemoryRegionSection *section;
|
||||
|
||||
section = phys_page_find(phys_addr >> TARGET_PAGE_BITS);
|
||||
|
||||
return !(memory_region_is_ram(section->mr) ||
|
||||
memory_region_is_romd(section->mr));
|
||||
}
|
||||
#endif
|
||||
|
19
gdbstub.c
19
gdbstub.c
@ -1937,21 +1937,12 @@ static void gdb_set_cpu_pc(GDBState *s, target_ulong pc)
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int gdb_id(CPUArchState *env)
|
||||
{
|
||||
#if defined(CONFIG_USER_ONLY) && defined(CONFIG_USE_NPTL)
|
||||
return env->host_tid;
|
||||
#else
|
||||
return env->cpu_index + 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static CPUArchState *find_cpu(uint32_t thread_id)
|
||||
{
|
||||
CPUArchState *env;
|
||||
|
||||
for (env = first_cpu; env != NULL; env = env->next_cpu) {
|
||||
if (gdb_id(env) == thread_id) {
|
||||
if (cpu_index(env) == thread_id) {
|
||||
return env;
|
||||
}
|
||||
}
|
||||
@ -1979,7 +1970,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
|
||||
case '?':
|
||||
/* TODO: Make this return the correct value for user-mode. */
|
||||
snprintf(buf, sizeof(buf), "T%02xthread:%02x;", GDB_SIGNAL_TRAP,
|
||||
gdb_id(s->c_cpu));
|
||||
cpu_index(s->c_cpu));
|
||||
put_packet(s, buf);
|
||||
/* Remove all the breakpoints when this query is issued,
|
||||
* because gdb is doing and initial connect and the state
|
||||
@ -2274,7 +2265,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
|
||||
} else if (strcmp(p,"sThreadInfo") == 0) {
|
||||
report_cpuinfo:
|
||||
if (s->query_cpu) {
|
||||
snprintf(buf, sizeof(buf), "m%x", gdb_id(s->query_cpu));
|
||||
snprintf(buf, sizeof(buf), "m%x", cpu_index(s->query_cpu));
|
||||
put_packet(s, buf);
|
||||
s->query_cpu = s->query_cpu->next_cpu;
|
||||
} else
|
||||
@ -2422,7 +2413,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state)
|
||||
}
|
||||
snprintf(buf, sizeof(buf),
|
||||
"T%02xthread:%02x;%swatch:" TARGET_FMT_lx ";",
|
||||
GDB_SIGNAL_TRAP, gdb_id(env), type,
|
||||
GDB_SIGNAL_TRAP, cpu_index(env), type,
|
||||
env->watchpoint_hit->vaddr);
|
||||
env->watchpoint_hit = NULL;
|
||||
goto send_packet;
|
||||
@ -2455,7 +2446,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state)
|
||||
ret = GDB_SIGNAL_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, gdb_id(env));
|
||||
snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, cpu_index(env));
|
||||
|
||||
send_packet:
|
||||
put_packet(s, buf);
|
||||
|
@ -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
|
||||
|
@ -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
52
hmp.c
@ -14,6 +14,8 @@
|
||||
*/
|
||||
|
||||
#include "hmp.h"
|
||||
#include "net.h"
|
||||
#include "qemu-option.h"
|
||||
#include "qemu-timer.h"
|
||||
#include "qmp-commands.h"
|
||||
|
||||
@ -947,3 +949,53 @@ void hmp_device_del(Monitor *mon, const QDict *qdict)
|
||||
qmp_device_del(id, &err);
|
||||
hmp_handle_error(mon, &err);
|
||||
}
|
||||
|
||||
void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
Error *errp = NULL;
|
||||
int paging = qdict_get_try_bool(qdict, "paging", 0);
|
||||
const char *file = qdict_get_str(qdict, "protocol");
|
||||
bool has_begin = qdict_haskey(qdict, "begin");
|
||||
bool has_length = qdict_haskey(qdict, "length");
|
||||
int64_t begin = 0;
|
||||
int64_t length = 0;
|
||||
|
||||
if (has_begin) {
|
||||
begin = qdict_get_int(qdict, "begin");
|
||||
}
|
||||
if (has_length) {
|
||||
length = qdict_get_int(qdict, "length");
|
||||
}
|
||||
|
||||
qmp_dump_guest_memory(paging, file, has_begin, begin, has_length, length,
|
||||
&errp);
|
||||
hmp_handle_error(mon, &errp);
|
||||
}
|
||||
|
||||
void hmp_netdev_add(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
Error *err = NULL;
|
||||
QemuOpts *opts;
|
||||
|
||||
opts = qemu_opts_from_qdict(qemu_find_opts("netdev"), qdict, &err);
|
||||
if (error_is_set(&err)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
netdev_add(opts, &err);
|
||||
if (error_is_set(&err)) {
|
||||
qemu_opts_del(opts);
|
||||
}
|
||||
|
||||
out:
|
||||
hmp_handle_error(mon, &err);
|
||||
}
|
||||
|
||||
void hmp_netdev_del(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
const char *id = qdict_get_str(qdict, "id");
|
||||
Error *err = NULL;
|
||||
|
||||
qmp_netdev_del(id, &err);
|
||||
hmp_handle_error(mon, &err);
|
||||
}
|
||||
|
3
hmp.h
3
hmp.h
@ -61,5 +61,8 @@ void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict);
|
||||
void hmp_block_job_cancel(Monitor *mon, const QDict *qdict);
|
||||
void hmp_migrate(Monitor *mon, const QDict *qdict);
|
||||
void hmp_device_del(Monitor *mon, const QDict *qdict);
|
||||
void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict);
|
||||
void hmp_netdev_add(Monitor *mon, const QDict *qdict);
|
||||
void hmp_netdev_del(Monitor *mon, const QDict *qdict);
|
||||
|
||||
#endif
|
||||
|
@ -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;
|
||||
|
@ -554,10 +554,13 @@ void do_info_qdm(Monitor *mon)
|
||||
|
||||
int do_device_add(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
QemuOpts *opts;
|
||||
|
||||
opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict);
|
||||
if (!opts) {
|
||||
opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
return -1;
|
||||
}
|
||||
if (!monitor_cur_is_qmp() && qdev_device_help(opts)) {
|
||||
|
@ -1356,6 +1356,7 @@ static int usb_net_initfn(USBDevice *dev)
|
||||
|
||||
static USBDevice *usb_net_init(USBBus *bus, const char *cmdline)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
USBDevice *dev;
|
||||
QemuOpts *opts;
|
||||
int idx;
|
||||
@ -1367,8 +1368,10 @@ static USBDevice *usb_net_init(USBBus *bus, const char *cmdline)
|
||||
qemu_opt_set(opts, "type", "nic");
|
||||
qemu_opt_set(opts, "model", "usb");
|
||||
|
||||
idx = net_client_init(NULL, opts, 0);
|
||||
if (idx == -1) {
|
||||
idx = net_client_init(opts, 0, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -584,7 +584,7 @@ static USBDevice *usb_msd_init(USBBus *bus, const char *filename)
|
||||
|
||||
/* parse -usbdevice disk: syntax into drive opts */
|
||||
snprintf(id, sizeof(id), "usb%d", nr++);
|
||||
opts = qemu_opts_create(qemu_find_opts("drive"), id, 0);
|
||||
opts = qemu_opts_create(qemu_find_opts("drive"), id, 0, NULL);
|
||||
|
||||
p1 = strchr(filename, ':');
|
||||
if (p1++) {
|
||||
|
@ -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
249
memory_mapping.c
Normal 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
74
memory_mapping.h
Normal 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
107
monitor.c
@ -422,6 +422,30 @@ static void timestamp_put(QDict *qdict)
|
||||
qdict_put_obj(qdict, "timestamp", obj);
|
||||
}
|
||||
|
||||
|
||||
static const char *monitor_event_names[] = {
|
||||
[QEVENT_SHUTDOWN] = "SHUTDOWN",
|
||||
[QEVENT_RESET] = "RESET",
|
||||
[QEVENT_POWERDOWN] = "POWERDOWN",
|
||||
[QEVENT_STOP] = "STOP",
|
||||
[QEVENT_RESUME] = "RESUME",
|
||||
[QEVENT_VNC_CONNECTED] = "VNC_CONNECTED",
|
||||
[QEVENT_VNC_INITIALIZED] = "VNC_INITIALIZED",
|
||||
[QEVENT_VNC_DISCONNECTED] = "VNC_DISCONNECTED",
|
||||
[QEVENT_BLOCK_IO_ERROR] = "BLOCK_IO_ERROR",
|
||||
[QEVENT_RTC_CHANGE] = "RTC_CHANGE",
|
||||
[QEVENT_WATCHDOG] = "WATCHDOG",
|
||||
[QEVENT_SPICE_CONNECTED] = "SPICE_CONNECTED",
|
||||
[QEVENT_SPICE_INITIALIZED] = "SPICE_INITIALIZED",
|
||||
[QEVENT_SPICE_DISCONNECTED] = "SPICE_DISCONNECTED",
|
||||
[QEVENT_BLOCK_JOB_COMPLETED] = "BLOCK_JOB_COMPLETED",
|
||||
[QEVENT_BLOCK_JOB_CANCELLED] = "BLOCK_JOB_CANCELLED",
|
||||
[QEVENT_DEVICE_TRAY_MOVED] = "DEVICE_TRAY_MOVED",
|
||||
[QEVENT_SUSPEND] = "SUSPEND",
|
||||
[QEVENT_WAKEUP] = "WAKEUP",
|
||||
};
|
||||
QEMU_BUILD_BUG_ON(ARRAY_SIZE(monitor_event_names) != QEVENT_MAX)
|
||||
|
||||
/**
|
||||
* monitor_protocol_event(): Generate a Monitor event
|
||||
*
|
||||
@ -435,68 +459,8 @@ void monitor_protocol_event(MonitorEvent event, QObject *data)
|
||||
|
||||
assert(event < QEVENT_MAX);
|
||||
|
||||
switch (event) {
|
||||
case QEVENT_SHUTDOWN:
|
||||
event_name = "SHUTDOWN";
|
||||
break;
|
||||
case QEVENT_RESET:
|
||||
event_name = "RESET";
|
||||
break;
|
||||
case QEVENT_POWERDOWN:
|
||||
event_name = "POWERDOWN";
|
||||
break;
|
||||
case QEVENT_STOP:
|
||||
event_name = "STOP";
|
||||
break;
|
||||
case QEVENT_RESUME:
|
||||
event_name = "RESUME";
|
||||
break;
|
||||
case QEVENT_VNC_CONNECTED:
|
||||
event_name = "VNC_CONNECTED";
|
||||
break;
|
||||
case QEVENT_VNC_INITIALIZED:
|
||||
event_name = "VNC_INITIALIZED";
|
||||
break;
|
||||
case QEVENT_VNC_DISCONNECTED:
|
||||
event_name = "VNC_DISCONNECTED";
|
||||
break;
|
||||
case QEVENT_BLOCK_IO_ERROR:
|
||||
event_name = "BLOCK_IO_ERROR";
|
||||
break;
|
||||
case QEVENT_RTC_CHANGE:
|
||||
event_name = "RTC_CHANGE";
|
||||
break;
|
||||
case QEVENT_WATCHDOG:
|
||||
event_name = "WATCHDOG";
|
||||
break;
|
||||
case QEVENT_SPICE_CONNECTED:
|
||||
event_name = "SPICE_CONNECTED";
|
||||
break;
|
||||
case QEVENT_SPICE_INITIALIZED:
|
||||
event_name = "SPICE_INITIALIZED";
|
||||
break;
|
||||
case QEVENT_SPICE_DISCONNECTED:
|
||||
event_name = "SPICE_DISCONNECTED";
|
||||
break;
|
||||
case QEVENT_BLOCK_JOB_COMPLETED:
|
||||
event_name = "BLOCK_JOB_COMPLETED";
|
||||
break;
|
||||
case QEVENT_BLOCK_JOB_CANCELLED:
|
||||
event_name = "BLOCK_JOB_CANCELLED";
|
||||
break;
|
||||
case QEVENT_DEVICE_TRAY_MOVED:
|
||||
event_name = "DEVICE_TRAY_MOVED";
|
||||
break;
|
||||
case QEVENT_SUSPEND:
|
||||
event_name = "SUSPEND";
|
||||
break;
|
||||
case QEVENT_WAKEUP:
|
||||
event_name = "WAKEUP";
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
break;
|
||||
}
|
||||
event_name = monitor_event_names[event];
|
||||
assert(event_name != NULL);
|
||||
|
||||
qmp = qdict_new();
|
||||
timestamp_put(qmp);
|
||||
@ -738,6 +702,25 @@ CommandInfoList *qmp_query_commands(Error **errp)
|
||||
return cmd_list;
|
||||
}
|
||||
|
||||
EventInfoList *qmp_query_events(Error **errp)
|
||||
{
|
||||
EventInfoList *info, *ev_list = NULL;
|
||||
MonitorEvent e;
|
||||
|
||||
for (e = 0 ; e < QEVENT_MAX ; e++) {
|
||||
const char *event_name = monitor_event_names[e];
|
||||
assert(event_name != NULL);
|
||||
info = g_malloc0(sizeof(*info));
|
||||
info->value = g_malloc0(sizeof(*info->value));
|
||||
info->value->name = g_strdup(event_name);
|
||||
|
||||
info->next = ev_list;
|
||||
ev_list = info;
|
||||
}
|
||||
|
||||
return ev_list;
|
||||
}
|
||||
|
||||
/* set the current CPU defined by the user */
|
||||
int monitor_set_cpu(int cpu_index)
|
||||
{
|
||||
|
@ -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
118
net.c
@ -745,10 +745,7 @@ int net_handle_fd_param(Monitor *mon, const char *param)
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int net_init_nic(QemuOpts *opts,
|
||||
Monitor *mon,
|
||||
const char *name,
|
||||
VLANState *vlan)
|
||||
static int net_init_nic(QemuOpts *opts, const char *name, VLANState *vlan)
|
||||
{
|
||||
int idx;
|
||||
NICInfo *nd;
|
||||
@ -821,7 +818,6 @@ static int net_init_nic(QemuOpts *opts,
|
||||
}
|
||||
|
||||
typedef int (*net_client_init_func)(QemuOpts *opts,
|
||||
Monitor *mon,
|
||||
const char *name,
|
||||
VLANState *vlan);
|
||||
|
||||
@ -1085,7 +1081,7 @@ static const struct {
|
||||
#endif /* CONFIG_NET_BRIDGE */
|
||||
};
|
||||
|
||||
int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev)
|
||||
int net_client_init(QemuOpts *opts, int is_netdev, Error **errp)
|
||||
{
|
||||
const char *name;
|
||||
const char *type;
|
||||
@ -1093,7 +1089,7 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev)
|
||||
|
||||
type = qemu_opt_get(opts, "type");
|
||||
if (!type) {
|
||||
qerror_report(QERR_MISSING_PARAMETER, "type");
|
||||
error_set(errp, QERR_MISSING_PARAMETER, "type");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -1109,21 +1105,21 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev)
|
||||
strcmp(type, "vde") != 0 &&
|
||||
#endif
|
||||
strcmp(type, "socket") != 0) {
|
||||
qerror_report(QERR_INVALID_PARAMETER_VALUE, "type",
|
||||
"a netdev backend type");
|
||||
error_set(errp, QERR_INVALID_PARAMETER_VALUE, "type",
|
||||
"a netdev backend type");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (qemu_opt_get(opts, "vlan")) {
|
||||
qerror_report(QERR_INVALID_PARAMETER, "vlan");
|
||||
error_set(errp, QERR_INVALID_PARAMETER, "vlan");
|
||||
return -1;
|
||||
}
|
||||
if (qemu_opt_get(opts, "name")) {
|
||||
qerror_report(QERR_INVALID_PARAMETER, "name");
|
||||
error_set(errp, QERR_INVALID_PARAMETER, "name");
|
||||
return -1;
|
||||
}
|
||||
if (!qemu_opts_id(opts)) {
|
||||
qerror_report(QERR_MISSING_PARAMETER, "id");
|
||||
error_set(errp, QERR_MISSING_PARAMETER, "id");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -1136,10 +1132,13 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev)
|
||||
for (i = 0; i < NET_CLIENT_TYPE_MAX; i++) {
|
||||
if (net_client_types[i].type != NULL &&
|
||||
!strcmp(net_client_types[i].type, type)) {
|
||||
Error *local_err = NULL;
|
||||
VLANState *vlan = NULL;
|
||||
int ret;
|
||||
|
||||
if (qemu_opts_validate(opts, &net_client_types[i].desc[0]) == -1) {
|
||||
qemu_opts_validate(opts, &net_client_types[i].desc[0], &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -1152,10 +1151,10 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev)
|
||||
|
||||
ret = 0;
|
||||
if (net_client_types[i].init) {
|
||||
ret = net_client_types[i].init(opts, mon, name, vlan);
|
||||
ret = net_client_types[i].init(opts, name, vlan);
|
||||
if (ret < 0) {
|
||||
/* TODO push error reporting into init() methods */
|
||||
qerror_report(QERR_DEVICE_INIT_FAILED, type);
|
||||
error_set(errp, QERR_DEVICE_INIT_FAILED, type);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -1163,8 +1162,8 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev)
|
||||
}
|
||||
}
|
||||
|
||||
qerror_report(QERR_INVALID_PARAMETER_VALUE, "type",
|
||||
"a network client type");
|
||||
error_set(errp, QERR_INVALID_PARAMETER_VALUE, "type",
|
||||
"a network client type");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -1195,6 +1194,7 @@ void net_host_device_add(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
const char *device = qdict_get_str(qdict, "device");
|
||||
const char *opts_str = qdict_get_try_str(qdict, "opts");
|
||||
Error *local_err = NULL;
|
||||
QemuOpts *opts;
|
||||
|
||||
if (!net_host_check_device(device)) {
|
||||
@ -1209,7 +1209,10 @@ void net_host_device_add(Monitor *mon, const QDict *qdict)
|
||||
|
||||
qemu_opt_set(opts, "type", device);
|
||||
|
||||
if (net_client_init(mon, opts, 0) < 0) {
|
||||
net_client_init(opts, 0, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
monitor_printf(mon, "adding host network device %s failed\n", device);
|
||||
}
|
||||
}
|
||||
@ -1231,37 +1234,53 @@ void net_host_device_remove(Monitor *mon, const QDict *qdict)
|
||||
qemu_del_vlan_client(vc);
|
||||
}
|
||||
|
||||
int do_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
||||
void netdev_add(QemuOpts *opts, Error **errp)
|
||||
{
|
||||
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
6
net.h
@ -163,15 +163,15 @@ struct HCIInfo *qemu_next_hci(void);
|
||||
extern const char *legacy_tftp_prefix;
|
||||
extern const char *legacy_bootp_filename;
|
||||
|
||||
int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev);
|
||||
int net_client_init(QemuOpts *opts, int is_netdev, Error **errp);
|
||||
int net_client_parse(QemuOptsList *opts_list, const char *str);
|
||||
int net_init_clients(void);
|
||||
void net_check_clients(void);
|
||||
void net_cleanup(void);
|
||||
void net_host_device_add(Monitor *mon, const QDict *qdict);
|
||||
void net_host_device_remove(Monitor *mon, const QDict *qdict);
|
||||
int do_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret_data);
|
||||
int do_netdev_del(Monitor *mon, const QDict *qdict, QObject **ret_data);
|
||||
void netdev_add(QemuOpts *opts, Error **errp);
|
||||
int qmp_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret);
|
||||
|
||||
#define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup"
|
||||
#define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown"
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
||||
|
107
qapi-schema.json
107
qapi-schema.json
@ -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'} }
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
177
qemu-option.c
177
qemu-option.c
@ -30,6 +30,7 @@
|
||||
#include "qemu-error.h"
|
||||
#include "qemu-objects.h"
|
||||
#include "qemu-option.h"
|
||||
#include "error.h"
|
||||
#include "qerror.h"
|
||||
|
||||
/*
|
||||
@ -168,7 +169,8 @@ QEMUOptionParameter *get_option_parameter(QEMUOptionParameter *list,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int parse_option_bool(const char *name, const char *value, bool *ret)
|
||||
static void parse_option_bool(const char *name, const char *value, bool *ret,
|
||||
Error **errp)
|
||||
{
|
||||
if (value != NULL) {
|
||||
if (!strcmp(value, "on")) {
|
||||
@ -176,16 +178,15 @@ static int parse_option_bool(const char *name, const char *value, bool *ret)
|
||||
} else if (!strcmp(value, "off")) {
|
||||
*ret = 0;
|
||||
} else {
|
||||
qerror_report(QERR_INVALID_PARAMETER_VALUE, name, "'on' or 'off'");
|
||||
return -1;
|
||||
error_set(errp,QERR_INVALID_PARAMETER_VALUE, name, "'on' or 'off'");
|
||||
}
|
||||
} else {
|
||||
*ret = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_option_number(const char *name, const char *value, uint64_t *ret)
|
||||
static void parse_option_number(const char *name, const char *value,
|
||||
uint64_t *ret, Error **errp)
|
||||
{
|
||||
char *postfix;
|
||||
uint64_t number;
|
||||
@ -193,18 +194,17 @@ static int parse_option_number(const char *name, const char *value, uint64_t *re
|
||||
if (value != NULL) {
|
||||
number = strtoull(value, &postfix, 0);
|
||||
if (*postfix != '\0') {
|
||||
qerror_report(QERR_INVALID_PARAMETER_VALUE, name, "a number");
|
||||
return -1;
|
||||
error_set(errp, QERR_INVALID_PARAMETER_VALUE, name, "a number");
|
||||
return;
|
||||
}
|
||||
*ret = number;
|
||||
} else {
|
||||
qerror_report(QERR_INVALID_PARAMETER_VALUE, name, "a number");
|
||||
return -1;
|
||||
error_set(errp, QERR_INVALID_PARAMETER_VALUE, name, "a number");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_option_size(const char *name, const char *value, uint64_t *ret)
|
||||
static void parse_option_size(const char *name, const char *value,
|
||||
uint64_t *ret, Error **errp)
|
||||
{
|
||||
char *postfix;
|
||||
double sizef;
|
||||
@ -230,16 +230,14 @@ static int parse_option_size(const char *name, const char *value, uint64_t *ret)
|
||||
*ret = (uint64_t) sizef;
|
||||
break;
|
||||
default:
|
||||
qerror_report(QERR_INVALID_PARAMETER_VALUE, name, "a size");
|
||||
error_set(errp, QERR_INVALID_PARAMETER_VALUE, name, "a size");
|
||||
error_printf_unless_qmp("You may use k, M, G or T suffixes for "
|
||||
"kilobytes, megabytes, gigabytes and terabytes.\n");
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
qerror_report(QERR_INVALID_PARAMETER_VALUE, name, "a size");
|
||||
return -1;
|
||||
error_set(errp, QERR_INVALID_PARAMETER_VALUE, name, "a size");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -263,6 +261,7 @@ int set_option_parameter(QEMUOptionParameter *list, const char *name,
|
||||
const char *value)
|
||||
{
|
||||
bool flag;
|
||||
Error *local_err = NULL;
|
||||
|
||||
// Find a matching parameter
|
||||
list = get_option_parameter(list, name);
|
||||
@ -274,9 +273,10 @@ int set_option_parameter(QEMUOptionParameter *list, const char *name,
|
||||
// Process parameter
|
||||
switch (list->type) {
|
||||
case OPT_FLAG:
|
||||
if (parse_option_bool(name, value, &flag) == -1)
|
||||
return -1;
|
||||
list->value.n = flag;
|
||||
parse_option_bool(name, value, &flag, &local_err);
|
||||
if (!error_is_set(&local_err)) {
|
||||
list->value.n = flag;
|
||||
}
|
||||
break;
|
||||
|
||||
case OPT_STRING:
|
||||
@ -289,8 +289,7 @@ int set_option_parameter(QEMUOptionParameter *list, const char *name,
|
||||
break;
|
||||
|
||||
case OPT_SIZE:
|
||||
if (parse_option_size(name, value, &list->value.n) == -1)
|
||||
return -1;
|
||||
parse_option_size(name, value, &list->value.n, &local_err);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -298,6 +297,12 @@ int set_option_parameter(QEMUOptionParameter *list, const char *name,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (error_is_set(&local_err)) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -576,20 +581,24 @@ uint64_t qemu_opt_get_size(QemuOpts *opts, const char *name, uint64_t defval)
|
||||
return opt->value.uint;
|
||||
}
|
||||
|
||||
static int qemu_opt_parse(QemuOpt *opt)
|
||||
static void qemu_opt_parse(QemuOpt *opt, Error **errp)
|
||||
{
|
||||
if (opt->desc == NULL)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
switch (opt->desc->type) {
|
||||
case QEMU_OPT_STRING:
|
||||
/* nothing */
|
||||
return 0;
|
||||
return;
|
||||
case QEMU_OPT_BOOL:
|
||||
return parse_option_bool(opt->name, opt->str, &opt->value.boolean);
|
||||
parse_option_bool(opt->name, opt->str, &opt->value.boolean, errp);
|
||||
break;
|
||||
case QEMU_OPT_NUMBER:
|
||||
return parse_option_number(opt->name, opt->str, &opt->value.uint);
|
||||
parse_option_number(opt->name, opt->str, &opt->value.uint, errp);
|
||||
break;
|
||||
case QEMU_OPT_SIZE:
|
||||
return parse_option_size(opt->name, opt->str, &opt->value.uint);
|
||||
parse_option_size(opt->name, opt->str, &opt->value.uint, errp);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
@ -603,11 +612,12 @@ static void qemu_opt_del(QemuOpt *opt)
|
||||
g_free(opt);
|
||||
}
|
||||
|
||||
static int opt_set(QemuOpts *opts, const char *name, const char *value,
|
||||
bool prepend)
|
||||
static void opt_set(QemuOpts *opts, const char *name, const char *value,
|
||||
bool prepend, Error **errp)
|
||||
{
|
||||
QemuOpt *opt;
|
||||
const QemuOptDesc *desc = opts->list->desc;
|
||||
Error *local_err = NULL;
|
||||
int i;
|
||||
|
||||
for (i = 0; desc[i].name != NULL; i++) {
|
||||
@ -619,8 +629,8 @@ static int opt_set(QemuOpts *opts, const char *name, const char *value,
|
||||
if (i == 0) {
|
||||
/* empty list -> allow any */;
|
||||
} else {
|
||||
qerror_report(QERR_INVALID_PARAMETER, name);
|
||||
return -1;
|
||||
error_set(errp, QERR_INVALID_PARAMETER, name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -638,16 +648,31 @@ static int opt_set(QemuOpts *opts, const char *name, const char *value,
|
||||
if (value) {
|
||||
opt->str = g_strdup(value);
|
||||
}
|
||||
if (qemu_opt_parse(opt) < 0) {
|
||||
qemu_opt_parse(opt, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
qemu_opt_del(opt);
|
||||
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,
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
4
qerror.c
4
qerror.c
@ -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)'",
|
||||
|
3
qerror.h
3
qerror.h
@ -139,6 +139,9 @@ QError *qobject_to_qerror(const QObject *obj);
|
||||
#define QERR_INVALID_BLOCK_FORMAT \
|
||||
"{ 'class': 'InvalidBlockFormat', 'data': { 'name': %s } }"
|
||||
|
||||
#define QERR_INVALID_OPTION_GROUP \
|
||||
"{ 'class': 'InvalidOptionGroup', 'data': { 'group': %s } }"
|
||||
|
||||
#define QERR_INVALID_PARAMETER \
|
||||
"{ 'class': 'InvalidParameter', 'data': { 'name': %s } }"
|
||||
|
||||
|
@ -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
449
target-i386/arch_dump.c
Normal 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, ®s, 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;
|
||||
}
|
271
target-i386/arch_memory_mapping.c
Normal file
271
target-i386/arch_memory_mapping.c
Normal 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
22
vl.c
@ -1786,7 +1786,7 @@ static int balloon_parse(const char *arg)
|
||||
return -1;
|
||||
} else {
|
||||
/* create empty opts */
|
||||
opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0);
|
||||
opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, NULL);
|
||||
}
|
||||
qemu_opt_set(opts, "driver", "virtio-balloon");
|
||||
return 0;
|
||||
@ -1921,7 +1921,7 @@ static void monitor_parse(const char *optarg, const char *mode)
|
||||
}
|
||||
}
|
||||
|
||||
opts = qemu_opts_create(qemu_find_opts("mon"), label, 1);
|
||||
opts = qemu_opts_create(qemu_find_opts("mon"), label, 1, NULL);
|
||||
if (!opts) {
|
||||
fprintf(stderr, "duplicate chardev: %s\n", label);
|
||||
exit(1);
|
||||
@ -2035,14 +2035,14 @@ static int virtcon_parse(const char *devname)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
bus_opts = qemu_opts_create(device, NULL, 0);
|
||||
bus_opts = qemu_opts_create(device, NULL, 0, NULL);
|
||||
if (arch_type == QEMU_ARCH_S390X) {
|
||||
qemu_opt_set(bus_opts, "driver", "virtio-serial-s390");
|
||||
} else {
|
||||
qemu_opt_set(bus_opts, "driver", "virtio-serial-pci");
|
||||
}
|
||||
|
||||
dev_opts = qemu_opts_create(device, NULL, 0);
|
||||
dev_opts = qemu_opts_create(device, NULL, 0, NULL);
|
||||
qemu_opt_set(dev_opts, "driver", "virtconsole");
|
||||
|
||||
snprintf(label, sizeof(label), "virtcon%d", index);
|
||||
@ -2065,7 +2065,7 @@ static int debugcon_parse(const char *devname)
|
||||
if (!qemu_chr_new("debugcon", devname, NULL)) {
|
||||
exit(1);
|
||||
}
|
||||
opts = qemu_opts_create(qemu_find_opts("device"), "debugcon", 1);
|
||||
opts = qemu_opts_create(qemu_find_opts("device"), "debugcon", 1, NULL);
|
||||
if (!opts) {
|
||||
fprintf(stderr, "qemu: already have a debugcon device\n");
|
||||
exit(1);
|
||||
@ -2813,7 +2813,8 @@ int main(int argc, char **argv, char **envp)
|
||||
exit(1);
|
||||
}
|
||||
fsdev = qemu_opts_create(qemu_find_opts("fsdev"),
|
||||
qemu_opt_get(opts, "mount_tag"), 1);
|
||||
qemu_opt_get(opts, "mount_tag"),
|
||||
1, NULL);
|
||||
if (!fsdev) {
|
||||
fprintf(stderr, "duplicate fsdev id: %s\n",
|
||||
qemu_opt_get(opts, "mount_tag"));
|
||||
@ -2845,7 +2846,8 @@ int main(int argc, char **argv, char **envp)
|
||||
|
||||
qemu_opt_set_bool(fsdev, "readonly",
|
||||
qemu_opt_get_bool(opts, "readonly", 0));
|
||||
device = qemu_opts_create(qemu_find_opts("device"), NULL, 0);
|
||||
device = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
|
||||
NULL);
|
||||
qemu_opt_set(device, "driver", "virtio-9p-pci");
|
||||
qemu_opt_set(device, "fsdev",
|
||||
qemu_opt_get(opts, "mount_tag"));
|
||||
@ -2857,14 +2859,16 @@ int main(int argc, char **argv, char **envp)
|
||||
QemuOpts *fsdev;
|
||||
QemuOpts *device;
|
||||
|
||||
fsdev = qemu_opts_create(qemu_find_opts("fsdev"), "v_synth", 1);
|
||||
fsdev = qemu_opts_create(qemu_find_opts("fsdev"), "v_synth",
|
||||
1, NULL);
|
||||
if (!fsdev) {
|
||||
fprintf(stderr, "duplicate option: %s\n", "virtfs_synth");
|
||||
exit(1);
|
||||
}
|
||||
qemu_opt_set(fsdev, "fsdriver", "synth");
|
||||
|
||||
device = qemu_opts_create(qemu_find_opts("device"), NULL, 0);
|
||||
device = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
|
||||
NULL);
|
||||
qemu_opt_set(device, "driver", "virtio-9p-pci");
|
||||
qemu_opt_set(device, "fsdev", "v_synth");
|
||||
qemu_opt_set(device, "mount_tag", "v_synth");
|
||||
|
Loading…
Reference in New Issue
Block a user