diff --git a/Makefile.target b/Makefile.target index 15829041c737cdf68cfb56517367965afaa7960f..c94abfd3a4d9d200ccba06fd1a6afe03517dc410 100644 --- a/Makefile.target +++ b/Makefile.target @@ -192,6 +192,9 @@ obj-$(CONFIG_KVM) += kvm.o kvm-all.o obj-$(CONFIG_NO_KVM) += kvm-stub.o obj-$(CONFIG_VGA) += vga.o obj-y += memory.o savevm.o cputlb.o +obj-y += memory_mapping.o +obj-$(CONFIG_HAVE_GET_MEMORY_MAPPING) += arch_memory_mapping.o +obj-$(CONFIG_HAVE_CORE_DUMP) += arch_dump.o LIBS+=-lz obj-i386-$(CONFIG_KVM) += hyperv.o @@ -402,6 +405,8 @@ obj-y += $(addprefix ../, $(trace-obj-y)) endif # CONFIG_SOFTMMU +obj-y += dump.o + ifndef CONFIG_LINUX_USER ifndef CONFIG_BSD_USER # libcacard needs qemu-thread support, and besides is only needed by devices diff --git a/blockdev.c b/blockdev.c index 67895b25d53429238364af468949715cfddce356..622ecba04e37b2a39ab6e96c562934e0087b29de 100644 --- a/blockdev.c +++ b/blockdev.c @@ -569,7 +569,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi) break; case IF_VIRTIO: /* add virtio block device */ - opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0); + opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, NULL); if (arch_type == QEMU_ARCH_S390X) { qemu_opt_set(opts, "driver", "virtio-blk-s390"); } else { diff --git a/configure b/configure index 1f338f8dcee8fecfcf5512902b0bf75c3a33ebdf..76dd57fc2779351977abb4af8e58faa534d51a40 100755 --- a/configure +++ b/configure @@ -3729,6 +3729,10 @@ case "$target_arch2" in fi fi esac +case "$target_arch2" in + i386|x86_64) + echo "CONFIG_HAVE_GET_MEMORY_MAPPING=y" >> $config_target_mak +esac if test "$target_arch2" = "ppc64" -a "$fdt" = "yes"; then echo "CONFIG_PSERIES=y" >> $config_target_mak fi @@ -3744,6 +3748,10 @@ if test "$target_softmmu" = "yes" ; then if test "$smartcard_nss" = "yes" ; then echo "subdir-$target: subdir-libcacard" >> $config_host_mak fi + case "$target_arch2" in + i386|x86_64) + echo "CONFIG_HAVE_CORE_DUMP=y" >> $config_target_mak + esac fi if test "$target_user_only" = "yes" ; then echo "CONFIG_USER_ONLY=y" >> $config_target_mak diff --git a/cpu-all.h b/cpu-all.h index 3a93c0c98ad46749c92b708b5870411ba6fe4ee9..e8749de2b9b3f46a06b93b238676d637f6d9acde 100644 --- a/cpu-all.h +++ b/cpu-all.h @@ -22,6 +22,8 @@ #include "qemu-common.h" #include "qemu-tls.h" #include "cpu-common.h" +#include "memory_mapping.h" +#include "dump.h" /* some important defines: * @@ -523,4 +525,72 @@ void dump_exec_info(FILE *f, fprintf_function cpu_fprintf); int cpu_memory_rw_debug(CPUArchState *env, target_ulong addr, uint8_t *buf, int len, int is_write); +#if defined(CONFIG_HAVE_GET_MEMORY_MAPPING) +int cpu_get_memory_mapping(MemoryMappingList *list, CPUArchState *env); +bool cpu_paging_enabled(CPUArchState *env); +#else +static inline int cpu_get_memory_mapping(MemoryMappingList *list, + CPUArchState *env) +{ + return -1; +} + +static inline bool cpu_paging_enabled(CPUArchState *env) +{ + return true; +} +#endif + +typedef int (*write_core_dump_function)(void *buf, size_t size, void *opaque); +#if defined(CONFIG_HAVE_CORE_DUMP) +int cpu_write_elf64_note(write_core_dump_function f, CPUArchState *env, + int cpuid, void *opaque); +int cpu_write_elf32_note(write_core_dump_function f, CPUArchState *env, + int cpuid, void *opaque); +int cpu_write_elf64_qemunote(write_core_dump_function f, CPUArchState *env, + void *opaque); +int cpu_write_elf32_qemunote(write_core_dump_function f, CPUArchState *env, + void *opaque); +int cpu_get_dump_info(ArchDumpInfo *info); +size_t cpu_get_note_size(int class, int machine, int nr_cpus); +#else +static inline int cpu_write_elf64_note(write_core_dump_function f, + CPUArchState *env, int cpuid, + void *opaque) +{ + return -1; +} + +static inline int cpu_write_elf32_note(write_core_dump_function f, + CPUArchState *env, int cpuid, + void *opaque) +{ + return -1; +} + +static inline int cpu_write_elf64_qemunote(write_core_dump_function f, + CPUArchState *env, + void *opaque) +{ + return -1; +} + +static inline int cpu_write_elf32_qemunote(write_core_dump_function f, + CPUArchState *env, + void *opaque) +{ + return -1; +} + +static inline int cpu_get_dump_info(ArchDumpInfo *info) +{ + return -1; +} + +static inline int cpu_get_note_size(int class, int machine, int nr_cpus) +{ + return -1; +} +#endif + #endif /* CPU_ALL_H */ diff --git a/cpu-common.h b/cpu-common.h index dca5175652ff222f15dd82037f8415f2e0d5b77e..1fe32807011853170d1bbdf392359b0549e40171 100644 --- a/cpu-common.h +++ b/cpu-common.h @@ -71,6 +71,10 @@ void cpu_physical_memory_unmap(void *buffer, target_phys_addr_t len, void *cpu_register_map_client(void *opaque, void (*callback)(void *opaque)); void cpu_unregister_map_client(void *cookie); +#ifndef CONFIG_USER_ONLY +bool cpu_physical_memory_is_io(target_phys_addr_t phys_addr); +#endif + /* Coalesced MMIO regions are areas where write operations can be reordered. * This usually implies that write operations are side-effect free. This allows * batching which can make a major impact on performance when using diff --git a/dump.c b/dump.c new file mode 100644 index 0000000000000000000000000000000000000000..0ca14f87edb4a0a3d747712586a73a9b2a68cf74 --- /dev/null +++ b/dump.c @@ -0,0 +1,883 @@ +/* + * QEMU dump + * + * Copyright Fujitsu, Corp. 2011, 2012 + * + * Authors: + * Wen Congyang + * + * 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 +#include "elf.h" +#include +#include +#include "cpu.h" +#include "cpu-all.h" +#include "targphys.h" +#include "monitor.h" +#include "kvm.h" +#include "dump.h" +#include "sysemu.h" +#include "bswap.h" +#include "memory_mapping.h" +#include "error.h" +#include "qmp-commands.h" +#include "gdbstub.h" + +#if defined(CONFIG_HAVE_CORE_DUMP) +static uint16_t cpu_convert_to_target16(uint16_t val, int endian) +{ + if (endian == ELFDATA2LSB) { + val = cpu_to_le16(val); + } else { + val = cpu_to_be16(val); + } + + return val; +} + +static uint32_t cpu_convert_to_target32(uint32_t val, int endian) +{ + if (endian == ELFDATA2LSB) { + val = cpu_to_le32(val); + } else { + val = cpu_to_be32(val); + } + + return val; +} + +static uint64_t cpu_convert_to_target64(uint64_t val, int endian) +{ + if (endian == ELFDATA2LSB) { + val = cpu_to_le64(val); + } else { + val = cpu_to_be64(val); + } + + return val; +} + +typedef struct DumpState { + ArchDumpInfo dump_info; + MemoryMappingList list; + uint16_t phdr_num; + uint32_t sh_info; + bool have_section; + bool resume; + size_t note_size; + target_phys_addr_t memory_offset; + int fd; + + RAMBlock *block; + ram_addr_t start; + bool has_filter; + int64_t begin; + int64_t length; + Error **errp; +} DumpState; + +static int dump_cleanup(DumpState *s) +{ + int ret = 0; + + memory_mapping_list_free(&s->list); + if (s->fd != -1) { + close(s->fd); + } + if (s->resume) { + vm_start(); + } + + return ret; +} + +static void dump_error(DumpState *s, const char *reason) +{ + dump_cleanup(s); +} + +static int fd_write_vmcore(void *buf, size_t size, void *opaque) +{ + DumpState *s = opaque; + int fd = s->fd; + size_t writen_size; + + /* The fd may be passed from user, and it can be non-blocked */ + while (size) { + writen_size = qemu_write_full(fd, buf, size); + if (writen_size != size && errno != EAGAIN) { + return -1; + } + + buf += writen_size; + size -= writen_size; + } + + return 0; +} + +static int write_elf64_header(DumpState *s) +{ + Elf64_Ehdr elf_header; + int ret; + int endian = s->dump_info.d_endian; + + memset(&elf_header, 0, sizeof(Elf64_Ehdr)); + memcpy(&elf_header, ELFMAG, SELFMAG); + elf_header.e_ident[EI_CLASS] = ELFCLASS64; + elf_header.e_ident[EI_DATA] = s->dump_info.d_endian; + elf_header.e_ident[EI_VERSION] = EV_CURRENT; + elf_header.e_type = cpu_convert_to_target16(ET_CORE, endian); + elf_header.e_machine = cpu_convert_to_target16(s->dump_info.d_machine, + endian); + elf_header.e_version = cpu_convert_to_target32(EV_CURRENT, endian); + elf_header.e_ehsize = cpu_convert_to_target16(sizeof(elf_header), endian); + elf_header.e_phoff = cpu_convert_to_target64(sizeof(Elf64_Ehdr), endian); + elf_header.e_phentsize = cpu_convert_to_target16(sizeof(Elf64_Phdr), + endian); + elf_header.e_phnum = cpu_convert_to_target16(s->phdr_num, endian); + if (s->have_section) { + uint64_t shoff = sizeof(Elf64_Ehdr) + sizeof(Elf64_Phdr) * s->sh_info; + + elf_header.e_shoff = cpu_convert_to_target64(shoff, endian); + elf_header.e_shentsize = cpu_convert_to_target16(sizeof(Elf64_Shdr), + endian); + elf_header.e_shnum = cpu_convert_to_target16(1, endian); + } + + ret = fd_write_vmcore(&elf_header, sizeof(elf_header), s); + if (ret < 0) { + dump_error(s, "dump: failed to write elf header.\n"); + return -1; + } + + return 0; +} + +static int write_elf32_header(DumpState *s) +{ + Elf32_Ehdr elf_header; + int ret; + int endian = s->dump_info.d_endian; + + memset(&elf_header, 0, sizeof(Elf32_Ehdr)); + memcpy(&elf_header, ELFMAG, SELFMAG); + elf_header.e_ident[EI_CLASS] = ELFCLASS32; + elf_header.e_ident[EI_DATA] = endian; + elf_header.e_ident[EI_VERSION] = EV_CURRENT; + elf_header.e_type = cpu_convert_to_target16(ET_CORE, endian); + elf_header.e_machine = cpu_convert_to_target16(s->dump_info.d_machine, + endian); + elf_header.e_version = cpu_convert_to_target32(EV_CURRENT, endian); + elf_header.e_ehsize = cpu_convert_to_target16(sizeof(elf_header), endian); + elf_header.e_phoff = cpu_convert_to_target32(sizeof(Elf32_Ehdr), endian); + elf_header.e_phentsize = cpu_convert_to_target16(sizeof(Elf32_Phdr), + endian); + elf_header.e_phnum = cpu_convert_to_target16(s->phdr_num, endian); + if (s->have_section) { + uint32_t shoff = sizeof(Elf32_Ehdr) + sizeof(Elf32_Phdr) * s->sh_info; + + elf_header.e_shoff = cpu_convert_to_target32(shoff, endian); + elf_header.e_shentsize = cpu_convert_to_target16(sizeof(Elf32_Shdr), + endian); + elf_header.e_shnum = cpu_convert_to_target16(1, endian); + } + + ret = fd_write_vmcore(&elf_header, sizeof(elf_header), s); + if (ret < 0) { + dump_error(s, "dump: failed to write elf header.\n"); + return -1; + } + + return 0; +} + +static int write_elf64_load(DumpState *s, MemoryMapping *memory_mapping, + int phdr_index, target_phys_addr_t offset) +{ + Elf64_Phdr phdr; + int ret; + int endian = s->dump_info.d_endian; + + memset(&phdr, 0, sizeof(Elf64_Phdr)); + phdr.p_type = cpu_convert_to_target32(PT_LOAD, endian); + phdr.p_offset = cpu_convert_to_target64(offset, endian); + phdr.p_paddr = cpu_convert_to_target64(memory_mapping->phys_addr, endian); + if (offset == -1) { + /* When the memory is not stored into vmcore, offset will be -1 */ + phdr.p_filesz = 0; + } else { + phdr.p_filesz = cpu_convert_to_target64(memory_mapping->length, endian); + } + phdr.p_memsz = cpu_convert_to_target64(memory_mapping->length, endian); + phdr.p_vaddr = cpu_convert_to_target64(memory_mapping->virt_addr, endian); + + ret = fd_write_vmcore(&phdr, sizeof(Elf64_Phdr), s); + if (ret < 0) { + dump_error(s, "dump: failed to write program header table.\n"); + return -1; + } + + return 0; +} + +static int write_elf32_load(DumpState *s, MemoryMapping *memory_mapping, + int phdr_index, target_phys_addr_t offset) +{ + Elf32_Phdr phdr; + int ret; + int endian = s->dump_info.d_endian; + + memset(&phdr, 0, sizeof(Elf32_Phdr)); + phdr.p_type = cpu_convert_to_target32(PT_LOAD, endian); + phdr.p_offset = cpu_convert_to_target32(offset, endian); + phdr.p_paddr = cpu_convert_to_target32(memory_mapping->phys_addr, endian); + if (offset == -1) { + /* When the memory is not stored into vmcore, offset will be -1 */ + phdr.p_filesz = 0; + } else { + phdr.p_filesz = cpu_convert_to_target32(memory_mapping->length, endian); + } + phdr.p_memsz = cpu_convert_to_target32(memory_mapping->length, endian); + phdr.p_vaddr = cpu_convert_to_target32(memory_mapping->virt_addr, endian); + + ret = fd_write_vmcore(&phdr, sizeof(Elf32_Phdr), s); + if (ret < 0) { + dump_error(s, "dump: failed to write program header table.\n"); + return -1; + } + + return 0; +} + +static int write_elf64_note(DumpState *s) +{ + Elf64_Phdr phdr; + int endian = s->dump_info.d_endian; + target_phys_addr_t begin = s->memory_offset - s->note_size; + int ret; + + memset(&phdr, 0, sizeof(Elf64_Phdr)); + phdr.p_type = cpu_convert_to_target32(PT_NOTE, endian); + phdr.p_offset = cpu_convert_to_target64(begin, endian); + phdr.p_paddr = 0; + phdr.p_filesz = cpu_convert_to_target64(s->note_size, endian); + phdr.p_memsz = cpu_convert_to_target64(s->note_size, endian); + phdr.p_vaddr = 0; + + ret = fd_write_vmcore(&phdr, sizeof(Elf64_Phdr), s); + if (ret < 0) { + dump_error(s, "dump: failed to write program header table.\n"); + return -1; + } + + return 0; +} + +static int write_elf64_notes(DumpState *s) +{ + CPUArchState *env; + int ret; + int id; + + for (env = first_cpu; env != NULL; env = env->next_cpu) { + id = cpu_index(env); + ret = cpu_write_elf64_note(fd_write_vmcore, env, id, s); + if (ret < 0) { + dump_error(s, "dump: failed to write elf notes.\n"); + return -1; + } + } + + for (env = first_cpu; env != NULL; env = env->next_cpu) { + ret = cpu_write_elf64_qemunote(fd_write_vmcore, env, s); + if (ret < 0) { + dump_error(s, "dump: failed to write CPU status.\n"); + return -1; + } + } + + return 0; +} + +static int write_elf32_note(DumpState *s) +{ + target_phys_addr_t begin = s->memory_offset - s->note_size; + Elf32_Phdr phdr; + int endian = s->dump_info.d_endian; + int ret; + + memset(&phdr, 0, sizeof(Elf32_Phdr)); + phdr.p_type = cpu_convert_to_target32(PT_NOTE, endian); + phdr.p_offset = cpu_convert_to_target32(begin, endian); + phdr.p_paddr = 0; + phdr.p_filesz = cpu_convert_to_target32(s->note_size, endian); + phdr.p_memsz = cpu_convert_to_target32(s->note_size, endian); + phdr.p_vaddr = 0; + + ret = fd_write_vmcore(&phdr, sizeof(Elf32_Phdr), s); + if (ret < 0) { + dump_error(s, "dump: failed to write program header table.\n"); + return -1; + } + + return 0; +} + +static int write_elf32_notes(DumpState *s) +{ + CPUArchState *env; + int ret; + int id; + + for (env = first_cpu; env != NULL; env = env->next_cpu) { + id = cpu_index(env); + ret = cpu_write_elf32_note(fd_write_vmcore, env, id, s); + if (ret < 0) { + dump_error(s, "dump: failed to write elf notes.\n"); + return -1; + } + } + + for (env = first_cpu; env != NULL; env = env->next_cpu) { + ret = cpu_write_elf32_qemunote(fd_write_vmcore, env, s); + if (ret < 0) { + dump_error(s, "dump: failed to write CPU status.\n"); + return -1; + } + } + + return 0; +} + +static int write_elf_section(DumpState *s, int type) +{ + Elf32_Shdr shdr32; + Elf64_Shdr shdr64; + int endian = s->dump_info.d_endian; + int shdr_size; + void *shdr; + int ret; + + if (type == 0) { + shdr_size = sizeof(Elf32_Shdr); + memset(&shdr32, 0, shdr_size); + shdr32.sh_info = cpu_convert_to_target32(s->sh_info, endian); + shdr = &shdr32; + } else { + shdr_size = sizeof(Elf64_Shdr); + memset(&shdr64, 0, shdr_size); + shdr64.sh_info = cpu_convert_to_target32(s->sh_info, endian); + shdr = &shdr64; + } + + ret = fd_write_vmcore(&shdr, shdr_size, s); + if (ret < 0) { + dump_error(s, "dump: failed to write section header table.\n"); + return -1; + } + + return 0; +} + +static int write_data(DumpState *s, void *buf, int length) +{ + int ret; + + ret = fd_write_vmcore(buf, length, s); + if (ret < 0) { + dump_error(s, "dump: failed to save memory.\n"); + return -1; + } + + return 0; +} + +/* write the memroy to vmcore. 1 page per I/O. */ +static int write_memory(DumpState *s, RAMBlock *block, ram_addr_t start, + int64_t size) +{ + int64_t i; + int ret; + + for (i = 0; i < size / TARGET_PAGE_SIZE; i++) { + ret = write_data(s, block->host + start + i * TARGET_PAGE_SIZE, + TARGET_PAGE_SIZE); + if (ret < 0) { + return ret; + } + } + + if ((size % TARGET_PAGE_SIZE) != 0) { + ret = write_data(s, block->host + start + i * TARGET_PAGE_SIZE, + size % TARGET_PAGE_SIZE); + if (ret < 0) { + return ret; + } + } + + return 0; +} + +/* get the memory's offset in the vmcore */ +static target_phys_addr_t get_offset(target_phys_addr_t phys_addr, + DumpState *s) +{ + RAMBlock *block; + target_phys_addr_t offset = s->memory_offset; + int64_t size_in_block, start; + + if (s->has_filter) { + if (phys_addr < s->begin || phys_addr >= s->begin + s->length) { + return -1; + } + } + + QLIST_FOREACH(block, &ram_list.blocks, next) { + if (s->has_filter) { + if (block->offset >= s->begin + s->length || + block->offset + block->length <= s->begin) { + /* This block is out of the range */ + continue; + } + + if (s->begin <= block->offset) { + start = block->offset; + } else { + start = s->begin; + } + + size_in_block = block->length - (start - block->offset); + if (s->begin + s->length < block->offset + block->length) { + size_in_block -= block->offset + block->length - + (s->begin + s->length); + } + } else { + start = block->offset; + size_in_block = block->length; + } + + if (phys_addr >= start && phys_addr < start + size_in_block) { + return phys_addr - start + offset; + } + + offset += size_in_block; + } + + return -1; +} + +static int write_elf_loads(DumpState *s) +{ + target_phys_addr_t offset; + MemoryMapping *memory_mapping; + uint32_t phdr_index = 1; + int ret; + uint32_t max_index; + + if (s->have_section) { + max_index = s->sh_info; + } else { + max_index = s->phdr_num; + } + + QTAILQ_FOREACH(memory_mapping, &s->list.head, next) { + offset = get_offset(memory_mapping->phys_addr, s); + if (s->dump_info.d_class == ELFCLASS64) { + ret = write_elf64_load(s, memory_mapping, phdr_index++, offset); + } else { + ret = write_elf32_load(s, memory_mapping, phdr_index++, offset); + } + + if (ret < 0) { + return -1; + } + + if (phdr_index >= max_index) { + break; + } + } + + return 0; +} + +/* write elf header, PT_NOTE and elf note to vmcore. */ +static int dump_begin(DumpState *s) +{ + int ret; + + /* + * the vmcore's format is: + * -------------- + * | elf header | + * -------------- + * | PT_NOTE | + * -------------- + * | PT_LOAD | + * -------------- + * | ...... | + * -------------- + * | PT_LOAD | + * -------------- + * | sec_hdr | + * -------------- + * | elf note | + * -------------- + * | memory | + * -------------- + * + * we only know where the memory is saved after we write elf note into + * vmcore. + */ + + /* write elf header to vmcore */ + if (s->dump_info.d_class == ELFCLASS64) { + ret = write_elf64_header(s); + } else { + ret = write_elf32_header(s); + } + if (ret < 0) { + return -1; + } + + if (s->dump_info.d_class == ELFCLASS64) { + /* write PT_NOTE to vmcore */ + if (write_elf64_note(s) < 0) { + return -1; + } + + /* write all PT_LOAD to vmcore */ + if (write_elf_loads(s) < 0) { + return -1; + } + + /* write section to vmcore */ + if (s->have_section) { + if (write_elf_section(s, 1) < 0) { + return -1; + } + } + + /* write notes to vmcore */ + if (write_elf64_notes(s) < 0) { + return -1; + } + + } else { + /* write PT_NOTE to vmcore */ + if (write_elf32_note(s) < 0) { + return -1; + } + + /* write all PT_LOAD to vmcore */ + if (write_elf_loads(s) < 0) { + return -1; + } + + /* write section to vmcore */ + if (s->have_section) { + if (write_elf_section(s, 0) < 0) { + return -1; + } + } + + /* write notes to vmcore */ + if (write_elf32_notes(s) < 0) { + return -1; + } + } + + return 0; +} + +/* write PT_LOAD to vmcore */ +static int dump_completed(DumpState *s) +{ + dump_cleanup(s); + return 0; +} + +static int get_next_block(DumpState *s, RAMBlock *block) +{ + while (1) { + block = QLIST_NEXT(block, next); + if (!block) { + /* no more block */ + return 1; + } + + s->start = 0; + s->block = block; + if (s->has_filter) { + if (block->offset >= s->begin + s->length || + block->offset + block->length <= s->begin) { + /* This block is out of the range */ + continue; + } + + if (s->begin > block->offset) { + s->start = s->begin - block->offset; + } + } + + return 0; + } +} + +/* write all memory to vmcore */ +static int dump_iterate(DumpState *s) +{ + RAMBlock *block; + int64_t size; + int ret; + + while (1) { + block = s->block; + + size = block->length; + if (s->has_filter) { + size -= s->start; + if (s->begin + s->length < block->offset + block->length) { + size -= block->offset + block->length - (s->begin + s->length); + } + } + ret = write_memory(s, block, s->start, size); + if (ret == -1) { + return ret; + } + + ret = get_next_block(s, block); + if (ret == 1) { + dump_completed(s); + return 0; + } + } +} + +static int create_vmcore(DumpState *s) +{ + int ret; + + ret = dump_begin(s); + if (ret < 0) { + return -1; + } + + ret = dump_iterate(s); + if (ret < 0) { + return -1; + } + + return 0; +} + +static ram_addr_t get_start_block(DumpState *s) +{ + RAMBlock *block; + + if (!s->has_filter) { + s->block = QLIST_FIRST(&ram_list.blocks); + return 0; + } + + QLIST_FOREACH(block, &ram_list.blocks, next) { + if (block->offset >= s->begin + s->length || + block->offset + block->length <= s->begin) { + /* This block is out of the range */ + continue; + } + + s->block = block; + if (s->begin > block->offset) { + s->start = s->begin - block->offset; + } else { + s->start = 0; + } + return s->start; + } + + return -1; +} + +static int dump_init(DumpState *s, int fd, bool paging, bool has_filter, + int64_t begin, int64_t length, Error **errp) +{ + CPUArchState *env; + int nr_cpus; + int ret; + + if (runstate_is_running()) { + vm_stop(RUN_STATE_SAVE_VM); + s->resume = true; + } else { + s->resume = false; + } + + s->errp = errp; + s->fd = fd; + s->has_filter = has_filter; + s->begin = begin; + s->length = length; + s->start = get_start_block(s); + if (s->start == -1) { + error_set(errp, QERR_INVALID_PARAMETER, "begin"); + goto cleanup; + } + + /* + * get dump info: endian, class and architecture. + * If the target architecture is not supported, cpu_get_dump_info() will + * return -1. + * + * if we use kvm, we should synchronize the register before we get dump + * info. + */ + nr_cpus = 0; + for (env = first_cpu; env != NULL; env = env->next_cpu) { + cpu_synchronize_state(env); + nr_cpus++; + } + + ret = cpu_get_dump_info(&s->dump_info); + if (ret < 0) { + error_set(errp, QERR_UNSUPPORTED); + goto cleanup; + } + + /* get memory mapping */ + memory_mapping_list_init(&s->list); + if (paging) { + qemu_get_guest_memory_mapping(&s->list); + } else { + qemu_get_guest_simple_memory_mapping(&s->list); + } + + if (s->has_filter) { + memory_mapping_filter(&s->list, s->begin, s->length); + } + + /* + * calculate phdr_num + * + * the type of ehdr->e_phnum is uint16_t, so we should avoid overflow + */ + s->phdr_num = 1; /* PT_NOTE */ + if (s->list.num < UINT16_MAX - 2) { + s->phdr_num += s->list.num; + s->have_section = false; + } else { + s->have_section = true; + s->phdr_num = PN_XNUM; + s->sh_info = 1; /* PT_NOTE */ + + /* the type of shdr->sh_info is uint32_t, so we should avoid overflow */ + if (s->list.num <= UINT32_MAX - 1) { + s->sh_info += s->list.num; + } else { + s->sh_info = UINT32_MAX; + } + } + + s->note_size = cpu_get_note_size(s->dump_info.d_class, + s->dump_info.d_machine, nr_cpus); + if (s->dump_info.d_class == ELFCLASS64) { + if (s->have_section) { + s->memory_offset = sizeof(Elf64_Ehdr) + + sizeof(Elf64_Phdr) * s->sh_info + + sizeof(Elf64_Shdr) + s->note_size; + } else { + s->memory_offset = sizeof(Elf64_Ehdr) + + sizeof(Elf64_Phdr) * s->phdr_num + s->note_size; + } + } else { + if (s->have_section) { + s->memory_offset = sizeof(Elf32_Ehdr) + + sizeof(Elf32_Phdr) * s->sh_info + + sizeof(Elf32_Shdr) + s->note_size; + } else { + s->memory_offset = sizeof(Elf32_Ehdr) + + sizeof(Elf32_Phdr) * s->phdr_num + s->note_size; + } + } + + return 0; + +cleanup: + if (s->resume) { + vm_start(); + } + + return -1; +} + +void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin, + int64_t begin, bool has_length, int64_t length, + Error **errp) +{ + const char *p; + int fd = -1; + DumpState *s; + int ret; + + if (has_begin && !has_length) { + error_set(errp, QERR_MISSING_PARAMETER, "length"); + return; + } + if (!has_begin && has_length) { + error_set(errp, QERR_MISSING_PARAMETER, "begin"); + return; + } + +#if !defined(WIN32) + if (strstart(file, "fd:", &p)) { + fd = monitor_get_fd(cur_mon, p); + if (fd == -1) { + error_set(errp, QERR_FD_NOT_FOUND, p); + return; + } + } +#endif + + if (strstart(file, "file:", &p)) { + fd = qemu_open(p, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR); + if (fd < 0) { + error_set(errp, QERR_OPEN_FILE_FAILED, p); + return; + } + } + + if (fd == -1) { + error_set(errp, QERR_INVALID_PARAMETER, "protocol"); + return; + } + + s = g_malloc(sizeof(DumpState)); + + ret = dump_init(s, fd, paging, has_begin, begin, length, errp); + if (ret < 0) { + g_free(s); + return; + } + + if (create_vmcore(s) < 0 && !error_is_set(s->errp)) { + error_set(errp, QERR_IO_ERROR); + } + + g_free(s); +} + +#else +/* we need this function in hmp.c */ +void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin, + int64_t begin, bool has_length, int64_t length, + Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); +} +#endif diff --git a/dump.h b/dump.h new file mode 100644 index 0000000000000000000000000000000000000000..28340cf2710a1f2d6e119eba9ed5a34312122faf --- /dev/null +++ b/dump.h @@ -0,0 +1,23 @@ +/* + * QEMU dump + * + * Copyright Fujitsu, Corp. 2011, 2012 + * + * Authors: + * Wen Congyang + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef DUMP_H +#define DUMP_H + +typedef struct ArchDumpInfo { + int d_machine; /* Architecture */ + int d_endian; /* ELFDATA2LSB or ELFDATA2MSB */ + int d_class; /* ELFCLASS32 or ELFCLASS64 */ +} ArchDumpInfo; + +#endif diff --git a/elf.h b/elf.h index e1422b81ee292615050c360434db073101218ce7..9c9acfaf75da9a1c851fe291557eab98f5fa0caa 100644 --- a/elf.h +++ b/elf.h @@ -1037,6 +1037,11 @@ typedef struct elf64_sym { #define EI_NIDENT 16 +/* Special value for e_phnum. This indicates that the real number of + program headers is too large to fit into e_phnum. Instead the real + value is in the field sh_info of section 0. */ +#define PN_XNUM 0xffff + typedef struct elf32_hdr{ unsigned char e_ident[EI_NIDENT]; Elf32_Half e_type; diff --git a/exec.c b/exec.c index a0494c72bfaa2258bb11131432d19e57791afd36..1b65859e50f711916eb621719e8e78762f15c2ee 100644 --- a/exec.c +++ b/exec.c @@ -4336,3 +4336,15 @@ bool virtio_is_big_endian(void) } #endif + +#ifndef CONFIG_USER_ONLY +bool cpu_physical_memory_is_io(target_phys_addr_t phys_addr) +{ + MemoryRegionSection *section; + + section = phys_page_find(phys_addr >> TARGET_PAGE_BITS); + + return !(memory_region_is_ram(section->mr) || + memory_region_is_romd(section->mr)); +} +#endif diff --git a/gdbstub.c b/gdbstub.c index 6a77a6696b4c62c1f29b114f0db5155362cd55e8..08cf8645d7e301f10b77dd769358a8432e7187ed 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1937,21 +1937,12 @@ static void gdb_set_cpu_pc(GDBState *s, target_ulong pc) #endif } -static inline int gdb_id(CPUArchState *env) -{ -#if defined(CONFIG_USER_ONLY) && defined(CONFIG_USE_NPTL) - return env->host_tid; -#else - return env->cpu_index + 1; -#endif -} - static CPUArchState *find_cpu(uint32_t thread_id) { CPUArchState *env; for (env = first_cpu; env != NULL; env = env->next_cpu) { - if (gdb_id(env) == thread_id) { + if (cpu_index(env) == thread_id) { return env; } } @@ -1979,7 +1970,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) case '?': /* TODO: Make this return the correct value for user-mode. */ snprintf(buf, sizeof(buf), "T%02xthread:%02x;", GDB_SIGNAL_TRAP, - gdb_id(s->c_cpu)); + cpu_index(s->c_cpu)); put_packet(s, buf); /* Remove all the breakpoints when this query is issued, * because gdb is doing and initial connect and the state @@ -2274,7 +2265,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } else if (strcmp(p,"sThreadInfo") == 0) { report_cpuinfo: if (s->query_cpu) { - snprintf(buf, sizeof(buf), "m%x", gdb_id(s->query_cpu)); + snprintf(buf, sizeof(buf), "m%x", cpu_index(s->query_cpu)); put_packet(s, buf); s->query_cpu = s->query_cpu->next_cpu; } else @@ -2422,7 +2413,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) } snprintf(buf, sizeof(buf), "T%02xthread:%02x;%swatch:" TARGET_FMT_lx ";", - GDB_SIGNAL_TRAP, gdb_id(env), type, + GDB_SIGNAL_TRAP, cpu_index(env), type, env->watchpoint_hit->vaddr); env->watchpoint_hit = NULL; goto send_packet; @@ -2455,7 +2446,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) ret = GDB_SIGNAL_UNKNOWN; break; } - snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, gdb_id(env)); + snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, cpu_index(env)); send_packet: put_packet(s, buf); diff --git a/gdbstub.h b/gdbstub.h index b44e27523ade240d07d309c8a4733cba03f696a0..668de660004d840dbf1e21c73863dc95539a3027 100644 --- a/gdbstub.h +++ b/gdbstub.h @@ -30,6 +30,15 @@ void gdb_register_coprocessor(CPUArchState *env, gdb_reg_cb get_reg, gdb_reg_cb set_reg, int num_regs, const char *xml, int g_pos); +static inline int cpu_index(CPUArchState *env) +{ +#if defined(CONFIG_USER_ONLY) && defined(CONFIG_USE_NPTL) + return env->host_tid; +#else + return env->cpu_index + 1; +#endif +} + #endif #ifdef CONFIG_USER_ONLY diff --git a/hmp-commands.hx b/hmp-commands.hx index 18cb415ac442680cd0d63d6335656c3f637f1494..f5d9d91de8b068d156d849e63117bf9746f9224a 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -878,6 +878,34 @@ server will ask the spice/vnc client to automatically reconnect using the new parameters (if specified) once the vm migration finished successfully. ETEXI +#if defined(CONFIG_HAVE_CORE_DUMP) + { + .name = "dump-guest-memory", + .args_type = "paging:-p,protocol:s,begin:i?,length:i?", + .params = "[-p] protocol [begin] [length]", + .help = "dump guest memory to file" + "\n\t\t\t begin(optional): the starting physical address" + "\n\t\t\t length(optional): the memory size, in bytes", + .user_print = monitor_user_noop, + .mhandler.cmd = hmp_dump_guest_memory, + }, + + +STEXI +@item dump-guest-memory [-p] @var{protocol} @var{begin} @var{length} +@findex dump-guest-memory +Dump guest memory to @var{protocol}. The file can be processed with crash or +gdb. + protocol: destination file(started with "file:") or destination file + descriptor (started with "fd:") + paging: do paging to get guest's memory mapping + begin: the starting physical address. It's optional, and should be + specified with length together. + length: the memory size, in bytes. It's optional, and should be specified + with begin together. +ETEXI +#endif + { .name = "snapshot_blkdev", .args_type = "reuse:-n,device:B,snapshot-file:s?,format:s?", @@ -1009,8 +1037,7 @@ ETEXI .args_type = "netdev:O", .params = "[user|tap|socket],id=str[,prop=value][,...]", .help = "add host network device", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_netdev_add, + .mhandler.cmd = hmp_netdev_add, }, STEXI @@ -1024,8 +1051,7 @@ ETEXI .args_type = "id:s", .params = "id", .help = "remove host network device", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_netdev_del, + .mhandler.cmd = hmp_netdev_del, }, STEXI diff --git a/hmp.c b/hmp.c index bb0952e00bf57594b41c03252087b4a42a7fab98..2ce8cb9df1791f669ecbf1536358ecce3211f221 100644 --- a/hmp.c +++ b/hmp.c @@ -14,6 +14,8 @@ */ #include "hmp.h" +#include "net.h" +#include "qemu-option.h" #include "qemu-timer.h" #include "qmp-commands.h" @@ -947,3 +949,53 @@ void hmp_device_del(Monitor *mon, const QDict *qdict) qmp_device_del(id, &err); hmp_handle_error(mon, &err); } + +void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict) +{ + Error *errp = NULL; + int paging = qdict_get_try_bool(qdict, "paging", 0); + const char *file = qdict_get_str(qdict, "protocol"); + bool has_begin = qdict_haskey(qdict, "begin"); + bool has_length = qdict_haskey(qdict, "length"); + int64_t begin = 0; + int64_t length = 0; + + if (has_begin) { + begin = qdict_get_int(qdict, "begin"); + } + if (has_length) { + length = qdict_get_int(qdict, "length"); + } + + qmp_dump_guest_memory(paging, file, has_begin, begin, has_length, length, + &errp); + hmp_handle_error(mon, &errp); +} + +void hmp_netdev_add(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + QemuOpts *opts; + + opts = qemu_opts_from_qdict(qemu_find_opts("netdev"), qdict, &err); + if (error_is_set(&err)) { + goto out; + } + + netdev_add(opts, &err); + if (error_is_set(&err)) { + qemu_opts_del(opts); + } + +out: + hmp_handle_error(mon, &err); +} + +void hmp_netdev_del(Monitor *mon, const QDict *qdict) +{ + const char *id = qdict_get_str(qdict, "id"); + Error *err = NULL; + + qmp_netdev_del(id, &err); + hmp_handle_error(mon, &err); +} diff --git a/hmp.h b/hmp.h index 443b812923070097070de69e8845d3e62aa9f3f8..79d138d3ee8496842cd55a60e586bd3e4e977706 100644 --- a/hmp.h +++ b/hmp.h @@ -61,5 +61,8 @@ void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict); void hmp_block_job_cancel(Monitor *mon, const QDict *qdict); void hmp_migrate(Monitor *mon, const QDict *qdict); void hmp_device_del(Monitor *mon, const QDict *qdict); +void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict); +void hmp_netdev_add(Monitor *mon, const QDict *qdict); +void hmp_netdev_del(Monitor *mon, const QDict *qdict); #endif diff --git a/hw/pci-hotplug.c b/hw/pci-hotplug.c index c55d8b9396cd1fd189c200d013394b3462897662..61257f457bc5acfe8707d61be421410adb99b4ca 100644 --- a/hw/pci-hotplug.c +++ b/hw/pci-hotplug.c @@ -39,6 +39,7 @@ static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon, const char *devaddr, const char *opts_str) { + Error *local_err = NULL; QemuOpts *opts; PCIBus *bus; int ret, devfn; @@ -60,9 +61,12 @@ static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon, qemu_opt_set(opts, "type", "nic"); - ret = net_client_init(mon, opts, 0); - if (ret < 0) + ret = net_client_init(opts, 0, &local_err); + if (error_is_set(&local_err)) { + qerror_report_err(local_err); + error_free(local_err); return NULL; + } if (nd_table[ret].devaddr) { monitor_printf(mon, "Parameter addr not supported\n"); return NULL; diff --git a/hw/qdev-monitor.c b/hw/qdev-monitor.c index eed781d2f08b85c521864ffde0424559f15a1b2c..b01ef0600e60d22c18f136fa4dde49d6cecff562 100644 --- a/hw/qdev-monitor.c +++ b/hw/qdev-monitor.c @@ -554,10 +554,13 @@ void do_info_qdm(Monitor *mon) int do_device_add(Monitor *mon, const QDict *qdict, QObject **ret_data) { + Error *local_err = NULL; QemuOpts *opts; - opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict); - if (!opts) { + opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict, &local_err); + if (error_is_set(&local_err)) { + qerror_report_err(local_err); + error_free(local_err); return -1; } if (!monitor_cur_is_qmp() && qdev_device_help(opts)) { diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index b238a0973d6b34b0bb4cdbec38c724b1b5ab0984..5d2f0982c9b18614856887b871790c92c6934b03 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -1356,6 +1356,7 @@ static int usb_net_initfn(USBDevice *dev) static USBDevice *usb_net_init(USBBus *bus, const char *cmdline) { + Error *local_err = NULL; USBDevice *dev; QemuOpts *opts; int idx; @@ -1367,8 +1368,10 @@ static USBDevice *usb_net_init(USBBus *bus, const char *cmdline) qemu_opt_set(opts, "type", "nic"); qemu_opt_set(opts, "model", "usb"); - idx = net_client_init(NULL, opts, 0); - if (idx == -1) { + idx = net_client_init(opts, 0, &local_err); + if (error_is_set(&local_err)) { + qerror_report_err(local_err); + error_free(local_err); return NULL; } diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index ae22fb1c97bca41d05c00ebae7600aaaa01b1251..a96c0b9e5e2383bfb97cce354283519c3259c342 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -584,7 +584,7 @@ static USBDevice *usb_msd_init(USBBus *bus, const char *filename) /* parse -usbdevice disk: syntax into drive opts */ snprintf(id, sizeof(id), "usb%d", nr++); - opts = qemu_opts_create(qemu_find_opts("drive"), id, 0); + opts = qemu_opts_create(qemu_find_opts("drive"), id, 0, NULL); p1 = strchr(filename, ':'); if (p1++) { diff --git a/hw/watchdog.c b/hw/watchdog.c index 4c189656540c4b9ad1348495fbd47458538304d9..a42124d5205b163aa100c84288bf484c6e4951be 100644 --- a/hw/watchdog.c +++ b/hw/watchdog.c @@ -66,7 +66,7 @@ int select_watchdog(const char *p) QLIST_FOREACH(model, &watchdog_list, entry) { if (strcasecmp(model->wdt_name, p) == 0) { /* add the device */ - opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0); + opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, NULL); qemu_opt_set(opts, "driver", p); return 0; } diff --git a/memory_mapping.c b/memory_mapping.c new file mode 100644 index 0000000000000000000000000000000000000000..8810bb09e3269d18e376f92e5ca66dfcd1ab51c8 --- /dev/null +++ b/memory_mapping.c @@ -0,0 +1,249 @@ +/* + * QEMU memory mapping + * + * Copyright Fujitsu, Corp. 2011, 2012 + * + * Authors: + * Wen Congyang + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include "cpu.h" +#include "cpu-all.h" +#include "memory_mapping.h" + +static void memory_mapping_list_add_mapping_sorted(MemoryMappingList *list, + MemoryMapping *mapping) +{ + MemoryMapping *p; + + QTAILQ_FOREACH(p, &list->head, next) { + if (p->phys_addr >= mapping->phys_addr) { + QTAILQ_INSERT_BEFORE(p, mapping, next); + return; + } + } + QTAILQ_INSERT_TAIL(&list->head, mapping, next); +} + +static void create_new_memory_mapping(MemoryMappingList *list, + target_phys_addr_t phys_addr, + target_phys_addr_t virt_addr, + ram_addr_t length) +{ + MemoryMapping *memory_mapping; + + memory_mapping = g_malloc(sizeof(MemoryMapping)); + memory_mapping->phys_addr = phys_addr; + memory_mapping->virt_addr = virt_addr; + memory_mapping->length = length; + list->last_mapping = memory_mapping; + list->num++; + memory_mapping_list_add_mapping_sorted(list, memory_mapping); +} + +static inline bool mapping_contiguous(MemoryMapping *map, + target_phys_addr_t phys_addr, + target_phys_addr_t virt_addr) +{ + return phys_addr == map->phys_addr + map->length && + virt_addr == map->virt_addr + map->length; +} + +/* + * [map->phys_addr, map->phys_addr + map->length) and + * [phys_addr, phys_addr + length) have intersection? + */ +static inline bool mapping_have_same_region(MemoryMapping *map, + target_phys_addr_t phys_addr, + ram_addr_t length) +{ + return !(phys_addr + length < map->phys_addr || + phys_addr >= map->phys_addr + map->length); +} + +/* + * [map->phys_addr, map->phys_addr + map->length) and + * [phys_addr, phys_addr + length) have intersection. The virtual address in the + * intersection are the same? + */ +static inline bool mapping_conflict(MemoryMapping *map, + target_phys_addr_t phys_addr, + target_phys_addr_t virt_addr) +{ + return virt_addr - map->virt_addr != phys_addr - map->phys_addr; +} + +/* + * [map->virt_addr, map->virt_addr + map->length) and + * [virt_addr, virt_addr + length) have intersection. And the physical address + * in the intersection are the same. + */ +static inline void mapping_merge(MemoryMapping *map, + target_phys_addr_t virt_addr, + ram_addr_t length) +{ + if (virt_addr < map->virt_addr) { + map->length += map->virt_addr - virt_addr; + map->virt_addr = virt_addr; + } + + if ((virt_addr + length) > + (map->virt_addr + map->length)) { + map->length = virt_addr + length - map->virt_addr; + } +} + +void memory_mapping_list_add_merge_sorted(MemoryMappingList *list, + target_phys_addr_t phys_addr, + target_phys_addr_t virt_addr, + ram_addr_t length) +{ + MemoryMapping *memory_mapping, *last_mapping; + + if (QTAILQ_EMPTY(&list->head)) { + create_new_memory_mapping(list, phys_addr, virt_addr, length); + return; + } + + last_mapping = list->last_mapping; + if (last_mapping) { + if (mapping_contiguous(last_mapping, phys_addr, virt_addr)) { + last_mapping->length += length; + return; + } + } + + QTAILQ_FOREACH(memory_mapping, &list->head, next) { + if (mapping_contiguous(memory_mapping, phys_addr, virt_addr)) { + memory_mapping->length += length; + list->last_mapping = memory_mapping; + return; + } + + if (phys_addr + length < memory_mapping->phys_addr) { + /* create a new region before memory_mapping */ + break; + } + + if (mapping_have_same_region(memory_mapping, phys_addr, length)) { + if (mapping_conflict(memory_mapping, phys_addr, virt_addr)) { + continue; + } + + /* merge this region into memory_mapping */ + mapping_merge(memory_mapping, virt_addr, length); + list->last_mapping = memory_mapping; + return; + } + } + + /* this region can not be merged into any existed memory mapping. */ + create_new_memory_mapping(list, phys_addr, virt_addr, length); +} + +void memory_mapping_list_free(MemoryMappingList *list) +{ + MemoryMapping *p, *q; + + QTAILQ_FOREACH_SAFE(p, &list->head, next, q) { + QTAILQ_REMOVE(&list->head, p, next); + g_free(p); + } + + list->num = 0; + list->last_mapping = NULL; +} + +void memory_mapping_list_init(MemoryMappingList *list) +{ + list->num = 0; + list->last_mapping = NULL; + QTAILQ_INIT(&list->head); +} + +#if defined(CONFIG_HAVE_GET_MEMORY_MAPPING) + +static CPUArchState *find_paging_enabled_cpu(CPUArchState *start_cpu) +{ + CPUArchState *env; + + for (env = start_cpu; env != NULL; env = env->next_cpu) { + if (cpu_paging_enabled(env)) { + return env; + } + } + + return NULL; +} + +int qemu_get_guest_memory_mapping(MemoryMappingList *list) +{ + CPUArchState *env, *first_paging_enabled_cpu; + RAMBlock *block; + ram_addr_t offset, length; + int ret; + + first_paging_enabled_cpu = find_paging_enabled_cpu(first_cpu); + if (first_paging_enabled_cpu) { + for (env = first_paging_enabled_cpu; env != NULL; env = env->next_cpu) { + ret = cpu_get_memory_mapping(list, env); + if (ret < 0) { + return -1; + } + } + return 0; + } + + /* + * If the guest doesn't use paging, the virtual address is equal to physical + * address. + */ + QLIST_FOREACH(block, &ram_list.blocks, next) { + offset = block->offset; + length = block->length; + create_new_memory_mapping(list, offset, offset, length); + } + + return 0; +} +#endif + +void qemu_get_guest_simple_memory_mapping(MemoryMappingList *list) +{ + RAMBlock *block; + + QLIST_FOREACH(block, &ram_list.blocks, next) { + create_new_memory_mapping(list, block->offset, 0, block->length); + } +} + +void memory_mapping_filter(MemoryMappingList *list, int64_t begin, + int64_t length) +{ + MemoryMapping *cur, *next; + + QTAILQ_FOREACH_SAFE(cur, &list->head, next, next) { + if (cur->phys_addr >= begin + length || + cur->phys_addr + cur->length <= begin) { + QTAILQ_REMOVE(&list->head, cur, next); + list->num--; + continue; + } + + if (cur->phys_addr < begin) { + cur->length -= begin - cur->phys_addr; + if (cur->virt_addr) { + cur->virt_addr += begin - cur->phys_addr; + } + cur->phys_addr = begin; + } + + if (cur->phys_addr + cur->length > begin + length) { + cur->length -= cur->phys_addr + cur->length - begin - length; + } + } +} diff --git a/memory_mapping.h b/memory_mapping.h new file mode 100644 index 0000000000000000000000000000000000000000..a1aa64f4cad9c2f3e41745460476c8854e976243 --- /dev/null +++ b/memory_mapping.h @@ -0,0 +1,74 @@ +/* + * QEMU memory mapping + * + * Copyright Fujitsu, Corp. 2011, 2012 + * + * Authors: + * Wen Congyang + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef MEMORY_MAPPING_H +#define MEMORY_MAPPING_H + +#include "qemu-queue.h" + +#ifndef CONFIG_USER_ONLY +/* The physical and virtual address in the memory mapping are contiguous. */ +typedef struct MemoryMapping { + target_phys_addr_t phys_addr; + target_ulong virt_addr; + ram_addr_t length; + QTAILQ_ENTRY(MemoryMapping) next; +} MemoryMapping; + +typedef struct MemoryMappingList { + unsigned int num; + MemoryMapping *last_mapping; + QTAILQ_HEAD(, MemoryMapping) head; +} MemoryMappingList; + +/* + * add or merge the memory region [phys_addr, phys_addr + length) into the + * memory mapping's list. The region's virtual address starts with virt_addr, + * and is contiguous. The list is sorted by phys_addr. + */ +void memory_mapping_list_add_merge_sorted(MemoryMappingList *list, + target_phys_addr_t phys_addr, + target_phys_addr_t virt_addr, + ram_addr_t length); + +void memory_mapping_list_free(MemoryMappingList *list); + +void memory_mapping_list_init(MemoryMappingList *list); + +/* + * Return value: + * 0: success + * -1: failed + * -2: unsupported + */ +#if defined(CONFIG_HAVE_GET_MEMORY_MAPPING) +int qemu_get_guest_memory_mapping(MemoryMappingList *list); +#else +static inline int qemu_get_guest_memory_mapping(MemoryMappingList *list) +{ + return -2; +} +#endif + +/* get guest's memory mapping without do paging(virtual address is 0). */ +void qemu_get_guest_simple_memory_mapping(MemoryMappingList *list); + +void memory_mapping_filter(MemoryMappingList *list, int64_t begin, + int64_t length); + +#else + +/* We use MemoryMappingList* in cpu-all.h */ +typedef struct MemoryMappingList MemoryMappingList; +#endif +#endif diff --git a/monitor.c b/monitor.c index 12a6fe25ad568ec27be22dbc1cd4e385a93b997a..a3bc2c725318cd07dffe65c8b283aed8ce7e5a0c 100644 --- a/monitor.c +++ b/monitor.c @@ -422,6 +422,30 @@ static void timestamp_put(QDict *qdict) qdict_put_obj(qdict, "timestamp", obj); } + +static const char *monitor_event_names[] = { + [QEVENT_SHUTDOWN] = "SHUTDOWN", + [QEVENT_RESET] = "RESET", + [QEVENT_POWERDOWN] = "POWERDOWN", + [QEVENT_STOP] = "STOP", + [QEVENT_RESUME] = "RESUME", + [QEVENT_VNC_CONNECTED] = "VNC_CONNECTED", + [QEVENT_VNC_INITIALIZED] = "VNC_INITIALIZED", + [QEVENT_VNC_DISCONNECTED] = "VNC_DISCONNECTED", + [QEVENT_BLOCK_IO_ERROR] = "BLOCK_IO_ERROR", + [QEVENT_RTC_CHANGE] = "RTC_CHANGE", + [QEVENT_WATCHDOG] = "WATCHDOG", + [QEVENT_SPICE_CONNECTED] = "SPICE_CONNECTED", + [QEVENT_SPICE_INITIALIZED] = "SPICE_INITIALIZED", + [QEVENT_SPICE_DISCONNECTED] = "SPICE_DISCONNECTED", + [QEVENT_BLOCK_JOB_COMPLETED] = "BLOCK_JOB_COMPLETED", + [QEVENT_BLOCK_JOB_CANCELLED] = "BLOCK_JOB_CANCELLED", + [QEVENT_DEVICE_TRAY_MOVED] = "DEVICE_TRAY_MOVED", + [QEVENT_SUSPEND] = "SUSPEND", + [QEVENT_WAKEUP] = "WAKEUP", +}; +QEMU_BUILD_BUG_ON(ARRAY_SIZE(monitor_event_names) != QEVENT_MAX) + /** * monitor_protocol_event(): Generate a Monitor event * @@ -435,68 +459,8 @@ void monitor_protocol_event(MonitorEvent event, QObject *data) assert(event < QEVENT_MAX); - switch (event) { - case QEVENT_SHUTDOWN: - event_name = "SHUTDOWN"; - break; - case QEVENT_RESET: - event_name = "RESET"; - break; - case QEVENT_POWERDOWN: - event_name = "POWERDOWN"; - break; - case QEVENT_STOP: - event_name = "STOP"; - break; - case QEVENT_RESUME: - event_name = "RESUME"; - break; - case QEVENT_VNC_CONNECTED: - event_name = "VNC_CONNECTED"; - break; - case QEVENT_VNC_INITIALIZED: - event_name = "VNC_INITIALIZED"; - break; - case QEVENT_VNC_DISCONNECTED: - event_name = "VNC_DISCONNECTED"; - break; - case QEVENT_BLOCK_IO_ERROR: - event_name = "BLOCK_IO_ERROR"; - break; - case QEVENT_RTC_CHANGE: - event_name = "RTC_CHANGE"; - break; - case QEVENT_WATCHDOG: - event_name = "WATCHDOG"; - break; - case QEVENT_SPICE_CONNECTED: - event_name = "SPICE_CONNECTED"; - break; - case QEVENT_SPICE_INITIALIZED: - event_name = "SPICE_INITIALIZED"; - break; - case QEVENT_SPICE_DISCONNECTED: - event_name = "SPICE_DISCONNECTED"; - break; - case QEVENT_BLOCK_JOB_COMPLETED: - event_name = "BLOCK_JOB_COMPLETED"; - break; - case QEVENT_BLOCK_JOB_CANCELLED: - event_name = "BLOCK_JOB_CANCELLED"; - break; - case QEVENT_DEVICE_TRAY_MOVED: - event_name = "DEVICE_TRAY_MOVED"; - break; - case QEVENT_SUSPEND: - event_name = "SUSPEND"; - break; - case QEVENT_WAKEUP: - event_name = "WAKEUP"; - break; - default: - abort(); - break; - } + event_name = monitor_event_names[event]; + assert(event_name != NULL); qmp = qdict_new(); timestamp_put(qmp); @@ -738,6 +702,25 @@ CommandInfoList *qmp_query_commands(Error **errp) return cmd_list; } +EventInfoList *qmp_query_events(Error **errp) +{ + EventInfoList *info, *ev_list = NULL; + MonitorEvent e; + + for (e = 0 ; e < QEVENT_MAX ; e++) { + const char *event_name = monitor_event_names[e]; + assert(event_name != NULL); + info = g_malloc0(sizeof(*info)); + info->value = g_malloc0(sizeof(*info->value)); + info->value->name = g_strdup(event_name); + + info->next = ev_list; + ev_list = info; + } + + return ev_list; +} + /* set the current CPU defined by the user */ int monitor_set_cpu(int cpu_index) { diff --git a/monitor.h b/monitor.h index 0d498006f81d03f27d44c0b097a7d6d83d4eab53..cd1d8786d1f40c76458c4bd260936f719a695e30 100644 --- a/monitor.h +++ b/monitor.h @@ -41,6 +41,10 @@ typedef enum MonitorEvent { QEVENT_DEVICE_TRAY_MOVED, QEVENT_SUSPEND, QEVENT_WAKEUP, + + /* Add to 'monitor_event_names' array in monitor.c when + * defining new events here */ + QEVENT_MAX, } MonitorEvent; diff --git a/net.c b/net.c index 1922d8abd19cdb4e45d6b1fd9a067b2fb9a506de..4aa416cffb743cee3660e5a318d52693232f7bf0 100644 --- a/net.c +++ b/net.c @@ -745,10 +745,7 @@ int net_handle_fd_param(Monitor *mon, const char *param) return fd; } -static int net_init_nic(QemuOpts *opts, - Monitor *mon, - const char *name, - VLANState *vlan) +static int net_init_nic(QemuOpts *opts, const char *name, VLANState *vlan) { int idx; NICInfo *nd; @@ -821,7 +818,6 @@ static int net_init_nic(QemuOpts *opts, } typedef int (*net_client_init_func)(QemuOpts *opts, - Monitor *mon, const char *name, VLANState *vlan); @@ -1085,7 +1081,7 @@ static const struct { #endif /* CONFIG_NET_BRIDGE */ }; -int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev) +int net_client_init(QemuOpts *opts, int is_netdev, Error **errp) { const char *name; const char *type; @@ -1093,7 +1089,7 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev) type = qemu_opt_get(opts, "type"); if (!type) { - qerror_report(QERR_MISSING_PARAMETER, "type"); + error_set(errp, QERR_MISSING_PARAMETER, "type"); return -1; } @@ -1109,21 +1105,21 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev) strcmp(type, "vde") != 0 && #endif strcmp(type, "socket") != 0) { - qerror_report(QERR_INVALID_PARAMETER_VALUE, "type", - "a netdev backend type"); + error_set(errp, QERR_INVALID_PARAMETER_VALUE, "type", + "a netdev backend type"); return -1; } if (qemu_opt_get(opts, "vlan")) { - qerror_report(QERR_INVALID_PARAMETER, "vlan"); + error_set(errp, QERR_INVALID_PARAMETER, "vlan"); return -1; } if (qemu_opt_get(opts, "name")) { - qerror_report(QERR_INVALID_PARAMETER, "name"); + error_set(errp, QERR_INVALID_PARAMETER, "name"); return -1; } if (!qemu_opts_id(opts)) { - qerror_report(QERR_MISSING_PARAMETER, "id"); + error_set(errp, QERR_MISSING_PARAMETER, "id"); return -1; } } @@ -1136,10 +1132,13 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev) for (i = 0; i < NET_CLIENT_TYPE_MAX; i++) { if (net_client_types[i].type != NULL && !strcmp(net_client_types[i].type, type)) { + Error *local_err = NULL; VLANState *vlan = NULL; int ret; - if (qemu_opts_validate(opts, &net_client_types[i].desc[0]) == -1) { + qemu_opts_validate(opts, &net_client_types[i].desc[0], &local_err); + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); return -1; } @@ -1152,10 +1151,10 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev) ret = 0; if (net_client_types[i].init) { - ret = net_client_types[i].init(opts, mon, name, vlan); + ret = net_client_types[i].init(opts, name, vlan); if (ret < 0) { /* TODO push error reporting into init() methods */ - qerror_report(QERR_DEVICE_INIT_FAILED, type); + error_set(errp, QERR_DEVICE_INIT_FAILED, type); return -1; } } @@ -1163,8 +1162,8 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev) } } - qerror_report(QERR_INVALID_PARAMETER_VALUE, "type", - "a network client type"); + error_set(errp, QERR_INVALID_PARAMETER_VALUE, "type", + "a network client type"); return -1; } @@ -1195,6 +1194,7 @@ void net_host_device_add(Monitor *mon, const QDict *qdict) { const char *device = qdict_get_str(qdict, "device"); const char *opts_str = qdict_get_try_str(qdict, "opts"); + Error *local_err = NULL; QemuOpts *opts; if (!net_host_check_device(device)) { @@ -1209,7 +1209,10 @@ void net_host_device_add(Monitor *mon, const QDict *qdict) qemu_opt_set(opts, "type", device); - if (net_client_init(mon, opts, 0) < 0) { + net_client_init(opts, 0, &local_err); + if (error_is_set(&local_err)) { + qerror_report_err(local_err); + error_free(local_err); monitor_printf(mon, "adding host network device %s failed\n", device); } } @@ -1231,37 +1234,53 @@ void net_host_device_remove(Monitor *mon, const QDict *qdict) qemu_del_vlan_client(vc); } -int do_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret_data) +void netdev_add(QemuOpts *opts, Error **errp) { + net_client_init(opts, 1, errp); +} + +int qmp_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret) +{ + Error *local_err = NULL; + QemuOptsList *opts_list; QemuOpts *opts; - int res; - opts = qemu_opts_from_qdict(qemu_find_opts("netdev"), qdict); - if (!opts) { - return -1; + opts_list = qemu_find_opts_err("netdev", &local_err); + if (error_is_set(&local_err)) { + goto exit_err; + } + + opts = qemu_opts_from_qdict(opts_list, qdict, &local_err); + if (error_is_set(&local_err)) { + goto exit_err; } - res = net_client_init(mon, opts, 1); - if (res < 0) { + netdev_add(opts, &local_err); + if (error_is_set(&local_err)) { qemu_opts_del(opts); + goto exit_err; } - return res; + return 0; + +exit_err: + qerror_report_err(local_err); + error_free(local_err); + return -1; } -int do_netdev_del(Monitor *mon, const QDict *qdict, QObject **ret_data) +void qmp_netdev_del(const char *id, Error **errp) { - const char *id = qdict_get_str(qdict, "id"); VLANClientState *vc; vc = qemu_find_netdev(id); if (!vc) { - qerror_report(QERR_DEVICE_NOT_FOUND, id); - return -1; + error_set(errp, QERR_DEVICE_NOT_FOUND, id); + return; } + qemu_del_vlan_client(vc); - qemu_opts_del(qemu_opts_find(qemu_find_opts("netdev"), id)); - return 0; + qemu_opts_del(qemu_opts_find(qemu_find_opts_err("netdev", errp), id)); } static void print_net_client(Monitor *mon, VLANClientState *vc) @@ -1424,14 +1443,31 @@ void net_check_clients(void) static int net_init_client(QemuOpts *opts, void *dummy) { - if (net_client_init(NULL, opts, 0) < 0) + Error *local_err = NULL; + + net_client_init(opts, 0, &local_err); + if (error_is_set(&local_err)) { + qerror_report_err(local_err); + error_free(local_err); return -1; + } + return 0; } static int net_init_netdev(QemuOpts *opts, void *dummy) { - return net_client_init(NULL, opts, 1); + Error *local_err = NULL; + int ret; + + ret = net_client_init(opts, 1, &local_err); + if (error_is_set(&local_err)) { + qerror_report_err(local_err); + error_free(local_err); + return -1; + } + + return ret; } int net_init_clients(void) diff --git a/net.h b/net.h index 64993b43ef35e4964e5cd16503ac649142310511..bdc2a0602d4c3da9c2bf277b6273e4ee94d60f81 100644 --- a/net.h +++ b/net.h @@ -163,15 +163,15 @@ struct HCIInfo *qemu_next_hci(void); extern const char *legacy_tftp_prefix; extern const char *legacy_bootp_filename; -int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev); +int net_client_init(QemuOpts *opts, int is_netdev, Error **errp); int net_client_parse(QemuOptsList *opts_list, const char *str); int net_init_clients(void); void net_check_clients(void); void net_cleanup(void); void net_host_device_add(Monitor *mon, const QDict *qdict); void net_host_device_remove(Monitor *mon, const QDict *qdict); -int do_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret_data); -int do_netdev_del(Monitor *mon, const QDict *qdict, QObject **ret_data); +void netdev_add(QemuOpts *opts, Error **errp); +int qmp_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret); #define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup" #define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown" diff --git a/net/dump.c b/net/dump.c index 4b48d484083ca6d1c5a76b1fcb45ead757c07ca0..f835c51187518d3e675b1f0da1a44d03367168cf 100644 --- a/net/dump.c +++ b/net/dump.c @@ -144,7 +144,7 @@ static int net_dump_init(VLANState *vlan, const char *device, return 0; } -int net_init_dump(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan) +int net_init_dump(QemuOpts *opts, const char *name, VLANState *vlan) { int len; const char *file; diff --git a/net/dump.h b/net/dump.h index fdc91ad6dce8d01e44c085601ffa72ed7c0512b1..2b5d9ba6442515abf0affc1bc20449698b48e294 100644 --- a/net/dump.h +++ b/net/dump.h @@ -27,7 +27,6 @@ #include "net.h" #include "qemu-common.h" -int net_init_dump(QemuOpts *opts, Monitor *mon, - const char *name, VLANState *vlan); +int net_init_dump(QemuOpts *opts, const char *name, VLANState *vlan); #endif /* QEMU_NET_DUMP_H */ diff --git a/net/slirp.c b/net/slirp.c index 96f5032c4c2e960b28785f5fa5cd621293eb9ca1..37b6ccfde9165756f9be0b8406548ae0af1e1044 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -676,10 +676,7 @@ static int net_init_slirp_configs(const char *name, const char *value, void *opa return 0; } -int net_init_slirp(QemuOpts *opts, - Monitor *mon, - const char *name, - VLANState *vlan) +int net_init_slirp(QemuOpts *opts, const char *name, VLANState *vlan) { struct slirp_config_str *config; const char *vhost; diff --git a/net/slirp.h b/net/slirp.h index c17de8ed47c2a9a1ece83da8d12b0e97b20c7ef9..53fe95dc12740186e86c58a4d1d01ee95d89f9ee 100644 --- a/net/slirp.h +++ b/net/slirp.h @@ -30,10 +30,7 @@ #ifdef CONFIG_SLIRP -int net_init_slirp(QemuOpts *opts, - Monitor *mon, - const char *name, - VLANState *vlan); +int net_init_slirp(QemuOpts *opts, const char *name, VLANState *vlan); void net_slirp_hostfwd_add(Monitor *mon, const QDict *qdict); void net_slirp_hostfwd_remove(Monitor *mon, const QDict *qdict); diff --git a/net/socket.c b/net/socket.c index 0bcf229c24dba9402d92a7323c0a7ffa2e3fd2c0..fcd0a3c162e7eb75b12606bc144ed2131ace06c4 100644 --- a/net/socket.c +++ b/net/socket.c @@ -26,6 +26,7 @@ #include "config-host.h" #include "net.h" +#include "monitor.h" #include "qemu-char.h" #include "qemu-common.h" #include "qemu-error.h" @@ -585,10 +586,7 @@ static int net_socket_udp_init(VLANState *vlan, return 0; } -int net_init_socket(QemuOpts *opts, - Monitor *mon, - const char *name, - VLANState *vlan) +int net_init_socket(QemuOpts *opts, const char *name, VLANState *vlan) { if (qemu_opt_get(opts, "fd")) { int fd; @@ -601,7 +599,7 @@ int net_init_socket(QemuOpts *opts, return -1; } - fd = net_handle_fd_param(mon, qemu_opt_get(opts, "fd")); + fd = net_handle_fd_param(cur_mon, qemu_opt_get(opts, "fd")); if (fd == -1) { return -1; } diff --git a/net/socket.h b/net/socket.h index ea46f02dd8a737fa1ea4cdac1851cd0041275304..e1fe9594122f73b5a2fff037e684468bb79e0f61 100644 --- a/net/socket.h +++ b/net/socket.h @@ -27,7 +27,6 @@ #include "net.h" #include "qemu-common.h" -int net_init_socket(QemuOpts *opts, Monitor *mon, - const char *name, VLANState *vlan); +int net_init_socket(QemuOpts *opts, const char *name, VLANState *vlan); #endif /* QEMU_NET_SOCKET_H */ diff --git a/net/tap-win32.c b/net/tap-win32.c index 596132e2725e2d1f5c43f73a924a09806fb6933e..a801a553c4f671c690b72ff50c0b0d04c6ac5157 100644 --- a/net/tap-win32.c +++ b/net/tap-win32.c @@ -699,7 +699,7 @@ static int tap_win32_init(VLANState *vlan, const char *model, return 0; } -int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan) +int net_init_tap(QemuOpts *opts, const char *name, VLANState *vlan) { const char *ifname; diff --git a/net/tap.c b/net/tap.c index f2400283c31ea96d2b49a47cefe44e586aa11e95..5ac4ba33432d7cabbb6dc14e2d0c03732d3b929e 100644 --- a/net/tap.c +++ b/net/tap.c @@ -512,8 +512,7 @@ static int net_bridge_run_helper(const char *helper, const char *bridge) return -1; } -int net_init_bridge(QemuOpts *opts, Monitor *mon, const char *name, - VLANState *vlan) +int net_init_bridge(QemuOpts *opts, const char *name, VLANState *vlan) { TAPState *s; int fd, vnet_hdr; @@ -583,7 +582,7 @@ static int net_tap_init(QemuOpts *opts, int *vnet_hdr) return fd; } -int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan) +int net_init_tap(QemuOpts *opts, const char *name, VLANState *vlan) { TAPState *s; int fd, vnet_hdr = 0; @@ -600,7 +599,7 @@ int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan return -1; } - fd = net_handle_fd_param(mon, qemu_opt_get(opts, "fd")); + fd = net_handle_fd_param(cur_mon, qemu_opt_get(opts, "fd")); if (fd == -1) { return -1; } @@ -687,7 +686,7 @@ int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan int vhostfd, r; bool force = qemu_opt_get_bool(opts, "vhostforce", false); if (qemu_opt_get(opts, "vhostfd")) { - r = net_handle_fd_param(mon, qemu_opt_get(opts, "vhostfd")); + r = net_handle_fd_param(cur_mon, qemu_opt_get(opts, "vhostfd")); if (r == -1) { return -1; } diff --git a/net/tap.h b/net/tap.h index 56c591fc8e308632326d812b9fc7d99420a88724..b2a9450aab65cc187bc06e2874f82ea5406067bd 100644 --- a/net/tap.h +++ b/net/tap.h @@ -32,7 +32,7 @@ #define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup" #define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown" -int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan); +int net_init_tap(QemuOpts *opts, const char *name, VLANState *vlan); int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required); @@ -57,7 +57,6 @@ int tap_get_fd(VLANClientState *vc); struct vhost_net; struct vhost_net *tap_get_vhost_net(VLANClientState *vc); -int net_init_bridge(QemuOpts *opts, Monitor *mon, const char *name, - VLANState *vlan); +int net_init_bridge(QemuOpts *opts, const char *name, VLANState *vlan); #endif /* QEMU_NET_TAP_H */ diff --git a/net/vde.c b/net/vde.c index ac48ab2f0a91509677a0bd086f9353922643569c..6b9d45294a743e7d0e942908e702e2823d4ba970 100644 --- a/net/vde.c +++ b/net/vde.c @@ -110,7 +110,7 @@ static int net_vde_init(VLANState *vlan, const char *model, return 0; } -int net_init_vde(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan) +int net_init_vde(QemuOpts *opts, const char *name, VLANState *vlan) { const char *sock; const char *group; diff --git a/net/vde.h b/net/vde.h index 3e6ca3e876e5676fd5f8b6fa15de3e2ff1b7fb23..732e5756f6d531560cc98291177b351fb96a2933 100644 --- a/net/vde.h +++ b/net/vde.h @@ -29,7 +29,7 @@ #ifdef CONFIG_VDE -int net_init_vde(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan); +int net_init_vde(QemuOpts *opts, const char *name, VLANState *vlan); #endif /* CONFIG_VDE */ diff --git a/qapi-schema.json b/qapi-schema.json index 2ca7195d25b220a2db543eb46b74d808972c28e3..3b6e3468b440b4b681f321c9525a3d83bea2137a 100644 --- a/qapi-schema.json +++ b/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'} } diff --git a/qemu-char.c b/qemu-char.c index fe1126fe8655683cbd069a61b0e1dbdb112c9cb7..0bd903f58c0e27616c6ad5562925e6b942070f3b 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2584,10 +2584,14 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) int pos; const char *p; QemuOpts *opts; + Error *local_err = NULL; - opts = qemu_opts_create(qemu_find_opts("chardev"), label, 1); - if (NULL == opts) + opts = qemu_opts_create(qemu_find_opts("chardev"), label, 1, &local_err); + if (error_is_set(&local_err)) { + qerror_report_err(local_err); + error_free(local_err); return NULL; + } if (strstart(filename, "mon:", &p)) { filename = p; diff --git a/qemu-config.c b/qemu-config.c index be84a0347c22d16fc7ecbbc90dd6b8782fe5972d..bb3bff426a8936554651842b9c966984269bccf9 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -3,6 +3,7 @@ #include "qemu-option.h" #include "qemu-config.h" #include "hw/qdev.h" +#include "error.h" static QemuOptsList qemu_drive_opts = { .name = "drive", @@ -631,7 +632,8 @@ static QemuOptsList *vm_config_groups[32] = { NULL, }; -static QemuOptsList *find_list(QemuOptsList **lists, const char *group) +static QemuOptsList *find_list(QemuOptsList **lists, const char *group, + Error **errp) { int i; @@ -640,14 +642,28 @@ static QemuOptsList *find_list(QemuOptsList **lists, const char *group) break; } if (lists[i] == NULL) { - error_report("there is no option group \"%s\"", group); + error_set(errp, QERR_INVALID_OPTION_GROUP, group); } return lists[i]; } QemuOptsList *qemu_find_opts(const char *group) { - return find_list(vm_config_groups, group); + QemuOptsList *ret; + Error *local_err = NULL; + + ret = find_list(vm_config_groups, group, &local_err); + if (error_is_set(&local_err)) { + error_report("%s\n", error_get_pretty(local_err)); + error_free(local_err); + } + + return ret; +} + +QemuOptsList *qemu_find_opts_err(const char *group, Error **errp) +{ + return find_list(vm_config_groups, group, errp); } void qemu_add_opts(QemuOptsList *list) @@ -709,7 +725,7 @@ int qemu_global_option(const char *str) return -1; } - opts = qemu_opts_create(&qemu_global_opts, NULL, 0); + opts = qemu_opts_create(&qemu_global_opts, NULL, 0, NULL); qemu_opt_set(opts, "driver", driver); qemu_opt_set(opts, "property", property); qemu_opt_set(opts, "value", str+offset+1); @@ -762,6 +778,7 @@ int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname) char line[1024], group[64], id[64], arg[64], value[1024]; Location loc; QemuOptsList *list = NULL; + Error *local_err = NULL; QemuOpts *opts = NULL; int res = -1, lno = 0; @@ -778,18 +795,24 @@ int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname) } if (sscanf(line, "[%63s \"%63[^\"]\"]", group, id) == 2) { /* group with id */ - list = find_list(lists, group); - if (list == NULL) + list = find_list(lists, group, &local_err); + if (error_is_set(&local_err)) { + error_report("%s\n", error_get_pretty(local_err)); + error_free(local_err); goto out; - opts = qemu_opts_create(list, id, 1); + } + opts = qemu_opts_create(list, id, 1, NULL); continue; } if (sscanf(line, "[%63[^]]]", group) == 1) { /* group without id */ - list = find_list(lists, group); - if (list == NULL) + list = find_list(lists, group, &local_err); + if (error_is_set(&local_err)) { + error_report("%s\n", error_get_pretty(local_err)); + error_free(local_err); goto out; - opts = qemu_opts_create(list, NULL, 0); + } + opts = qemu_opts_create(list, NULL, 0, NULL); continue; } if (sscanf(line, " %63s = \"%1023[^\"]\"", arg, value) == 2) { diff --git a/qemu-config.h b/qemu-config.h index 6d7365d35ba9041442ef8f6bdaaf498f387d0ad8..e9f2ef4c7b05c93ae884199602be839fef53b2fa 100644 --- a/qemu-config.h +++ b/qemu-config.h @@ -1,11 +1,14 @@ #ifndef QEMU_CONFIG_H #define QEMU_CONFIG_H +#include "error.h" + extern QemuOptsList qemu_fsdev_opts; extern QemuOptsList qemu_virtfs_opts; extern QemuOptsList qemu_spice_opts; QemuOptsList *qemu_find_opts(const char *group); +QemuOptsList *qemu_find_opts_err(const char *group, Error **errp); void qemu_add_opts(QemuOptsList *list); int qemu_set_option(const char *str); int qemu_global_option(const char *str); diff --git a/qemu-option.c b/qemu-option.c index 35cd609f75988e8382516f224e4a109a525f8837..bb3886c6b9d73103c32d33ccd1c7601329a38c45 100644 --- a/qemu-option.c +++ b/qemu-option.c @@ -30,6 +30,7 @@ #include "qemu-error.h" #include "qemu-objects.h" #include "qemu-option.h" +#include "error.h" #include "qerror.h" /* @@ -168,7 +169,8 @@ QEMUOptionParameter *get_option_parameter(QEMUOptionParameter *list, return NULL; } -static int parse_option_bool(const char *name, const char *value, bool *ret) +static void parse_option_bool(const char *name, const char *value, bool *ret, + Error **errp) { if (value != NULL) { if (!strcmp(value, "on")) { @@ -176,16 +178,15 @@ static int parse_option_bool(const char *name, const char *value, bool *ret) } else if (!strcmp(value, "off")) { *ret = 0; } else { - qerror_report(QERR_INVALID_PARAMETER_VALUE, name, "'on' or 'off'"); - return -1; + error_set(errp,QERR_INVALID_PARAMETER_VALUE, name, "'on' or 'off'"); } } else { *ret = 1; } - return 0; } -static int parse_option_number(const char *name, const char *value, uint64_t *ret) +static void parse_option_number(const char *name, const char *value, + uint64_t *ret, Error **errp) { char *postfix; uint64_t number; @@ -193,18 +194,17 @@ static int parse_option_number(const char *name, const char *value, uint64_t *re if (value != NULL) { number = strtoull(value, &postfix, 0); if (*postfix != '\0') { - qerror_report(QERR_INVALID_PARAMETER_VALUE, name, "a number"); - return -1; + error_set(errp, QERR_INVALID_PARAMETER_VALUE, name, "a number"); + return; } *ret = number; } else { - qerror_report(QERR_INVALID_PARAMETER_VALUE, name, "a number"); - return -1; + error_set(errp, QERR_INVALID_PARAMETER_VALUE, name, "a number"); } - return 0; } -static int parse_option_size(const char *name, const char *value, uint64_t *ret) +static void parse_option_size(const char *name, const char *value, + uint64_t *ret, Error **errp) { char *postfix; double sizef; @@ -230,16 +230,14 @@ static int parse_option_size(const char *name, const char *value, uint64_t *ret) *ret = (uint64_t) sizef; break; default: - qerror_report(QERR_INVALID_PARAMETER_VALUE, name, "a size"); + error_set(errp, QERR_INVALID_PARAMETER_VALUE, name, "a size"); error_printf_unless_qmp("You may use k, M, G or T suffixes for " "kilobytes, megabytes, gigabytes and terabytes.\n"); - return -1; + return; } } else { - qerror_report(QERR_INVALID_PARAMETER_VALUE, name, "a size"); - return -1; + error_set(errp, QERR_INVALID_PARAMETER_VALUE, name, "a size"); } - return 0; } /* @@ -263,6 +261,7 @@ int set_option_parameter(QEMUOptionParameter *list, const char *name, const char *value) { bool flag; + Error *local_err = NULL; // Find a matching parameter list = get_option_parameter(list, name); @@ -274,9 +273,10 @@ int set_option_parameter(QEMUOptionParameter *list, const char *name, // Process parameter switch (list->type) { case OPT_FLAG: - if (parse_option_bool(name, value, &flag) == -1) - return -1; - list->value.n = flag; + parse_option_bool(name, value, &flag, &local_err); + if (!error_is_set(&local_err)) { + list->value.n = flag; + } break; case OPT_STRING: @@ -289,8 +289,7 @@ int set_option_parameter(QEMUOptionParameter *list, const char *name, break; case OPT_SIZE: - if (parse_option_size(name, value, &list->value.n) == -1) - return -1; + parse_option_size(name, value, &list->value.n, &local_err); break; default: @@ -298,6 +297,12 @@ int set_option_parameter(QEMUOptionParameter *list, const char *name, return -1; } + if (error_is_set(&local_err)) { + qerror_report_err(local_err); + error_free(local_err); + return -1; + } + return 0; } @@ -576,20 +581,24 @@ uint64_t qemu_opt_get_size(QemuOpts *opts, const char *name, uint64_t defval) return opt->value.uint; } -static int qemu_opt_parse(QemuOpt *opt) +static void qemu_opt_parse(QemuOpt *opt, Error **errp) { if (opt->desc == NULL) - return 0; + return; + switch (opt->desc->type) { case QEMU_OPT_STRING: /* nothing */ - return 0; + return; case QEMU_OPT_BOOL: - return parse_option_bool(opt->name, opt->str, &opt->value.boolean); + parse_option_bool(opt->name, opt->str, &opt->value.boolean, errp); + break; case QEMU_OPT_NUMBER: - return parse_option_number(opt->name, opt->str, &opt->value.uint); + parse_option_number(opt->name, opt->str, &opt->value.uint, errp); + break; case QEMU_OPT_SIZE: - return parse_option_size(opt->name, opt->str, &opt->value.uint); + parse_option_size(opt->name, opt->str, &opt->value.uint, errp); + break; default: abort(); } @@ -603,11 +612,12 @@ static void qemu_opt_del(QemuOpt *opt) g_free(opt); } -static int opt_set(QemuOpts *opts, const char *name, const char *value, - bool prepend) +static void opt_set(QemuOpts *opts, const char *name, const char *value, + bool prepend, Error **errp) { QemuOpt *opt; const QemuOptDesc *desc = opts->list->desc; + Error *local_err = NULL; int i; for (i = 0; desc[i].name != NULL; i++) { @@ -619,8 +629,8 @@ static int opt_set(QemuOpts *opts, const char *name, const char *value, if (i == 0) { /* empty list -> allow any */; } else { - qerror_report(QERR_INVALID_PARAMETER, name); - return -1; + error_set(errp, QERR_INVALID_PARAMETER, name); + return; } } @@ -638,16 +648,31 @@ static int opt_set(QemuOpts *opts, const char *name, const char *value, if (value) { opt->str = g_strdup(value); } - if (qemu_opt_parse(opt) < 0) { + qemu_opt_parse(opt, &local_err); + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); qemu_opt_del(opt); + } +} + +int qemu_opt_set(QemuOpts *opts, const char *name, const char *value) +{ + Error *local_err = NULL; + + opt_set(opts, name, value, false, &local_err); + if (error_is_set(&local_err)) { + qerror_report_err(local_err); + error_free(local_err); return -1; } + return 0; } -int qemu_opt_set(QemuOpts *opts, const char *name, const char *value) +void qemu_opt_set_err(QemuOpts *opts, const char *name, const char *value, + Error **errp) { - return opt_set(opts, name, value, false); + opt_set(opts, name, value, false, errp); } int qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val) @@ -729,20 +754,21 @@ static int id_wellformed(const char *id) return 1; } -QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, int fail_if_exists) +QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, + int fail_if_exists, Error **errp) { QemuOpts *opts = NULL; if (id) { if (!id_wellformed(id)) { - qerror_report(QERR_INVALID_PARAMETER_VALUE, "id", "an identifier"); + error_set(errp,QERR_INVALID_PARAMETER_VALUE, "id", "an identifier"); error_printf_unless_qmp("Identifiers consist of letters, digits, '-', '.', '_', starting with a letter.\n"); return NULL; } opts = qemu_opts_find(list, id); if (opts != NULL) { if (fail_if_exists && !list->merge_lists) { - qerror_report(QERR_DUPLICATE_ID, id, list->name); + error_set(errp, QERR_DUPLICATE_ID, id, list->name); return NULL; } else { return opts; @@ -783,9 +809,12 @@ int qemu_opts_set(QemuOptsList *list, const char *id, const char *name, const char *value) { QemuOpts *opts; + Error *local_err = NULL; - opts = qemu_opts_create(list, id, 1); - if (opts == NULL) { + opts = qemu_opts_create(list, id, 1, &local_err); + if (error_is_set(&local_err)) { + qerror_report_err(local_err); + error_free(local_err); return -1; } return qemu_opt_set(opts, name, value); @@ -829,6 +858,7 @@ static int opts_do_parse(QemuOpts *opts, const char *params, { char option[128], value[1024]; const char *p,*pe,*pc; + Error *local_err = NULL; for (p = params; *p != '\0'; p++) { pe = strchr(p, '='); @@ -860,7 +890,10 @@ static int opts_do_parse(QemuOpts *opts, const char *params, } if (strcmp(option, "id") != 0) { /* store and parse */ - if (opt_set(opts, option, value, prepend) == -1) { + opt_set(opts, option, value, prepend, &local_err); + if (error_is_set(&local_err)) { + qerror_report_err(local_err); + error_free(local_err); return -1; } } @@ -883,6 +916,7 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params, char value[1024], *id = NULL; const char *p; QemuOpts *opts; + Error *local_err = NULL; assert(!permit_abbrev || list->implied_opt_name); firstname = permit_abbrev ? list->implied_opt_name : NULL; @@ -898,13 +932,18 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params, if (!id && !QTAILQ_EMPTY(&list->head)) { opts = qemu_opts_find(list, NULL); } else { - opts = qemu_opts_create(list, id, 0); + opts = qemu_opts_create(list, id, 0, &local_err); } } else { - opts = qemu_opts_create(list, id, 1); + opts = qemu_opts_create(list, id, 1, &local_err); } - if (opts == NULL) + if (opts == NULL) { + if (error_is_set(&local_err)) { + qerror_report_err(local_err); + error_free(local_err); + } return NULL; + } if (opts_do_parse(opts, params, firstname, defaults) != 0) { qemu_opts_del(opts); @@ -929,13 +968,19 @@ void qemu_opts_set_defaults(QemuOptsList *list, const char *params, assert(opts); } +typedef struct OptsFromQDictState { + QemuOpts *opts; + Error **errp; +} OptsFromQDictState; + static void qemu_opts_from_qdict_1(const char *key, QObject *obj, void *opaque) { + OptsFromQDictState *state = opaque; char buf[32]; const char *value; int n; - if (!strcmp(key, "id")) { + if (!strcmp(key, "id") || error_is_set(state->errp)) { return; } @@ -963,7 +1008,8 @@ static void qemu_opts_from_qdict_1(const char *key, QObject *obj, void *opaque) default: return; } - qemu_opt_set(opaque, key, value); + + qemu_opt_set_err(state->opts, key, value, state->errp); } /* @@ -972,15 +1018,31 @@ static void qemu_opts_from_qdict_1(const char *key, QObject *obj, void *opaque) * Only QStrings, QInts, QFloats and QBools are copied. Entries with * other types are silently ignored. */ -QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict) +QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict, + Error **errp) { + OptsFromQDictState state; + Error *local_err = NULL; QemuOpts *opts; - opts = qemu_opts_create(list, qdict_get_try_str(qdict, "id"), 1); - if (opts == NULL) + opts = qemu_opts_create(list, qdict_get_try_str(qdict, "id"), 1, + &local_err); + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + return NULL; + } + + assert(opts != NULL); + + state.errp = &local_err; + state.opts = opts; + qdict_iter(qdict, qemu_opts_from_qdict_1, &state); + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + qemu_opts_del(opts); return NULL; + } - qdict_iter(qdict, qemu_opts_from_qdict_1, opts); return opts; } @@ -1011,9 +1073,10 @@ QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict) /* Validate parsed opts against descriptions where no * descriptions were provided in the QemuOptsList. */ -int qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc) +void qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc, Error **errp) { QemuOpt *opt; + Error *local_err = NULL; assert(opts->list->desc[0].name == NULL); @@ -1026,18 +1089,18 @@ int qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc) } } if (desc[i].name == NULL) { - qerror_report(QERR_INVALID_PARAMETER, opt->name); - return -1; + error_set(errp, QERR_INVALID_PARAMETER, opt->name); + return; } opt->desc = &desc[i]; - if (qemu_opt_parse(opt) < 0) { - return -1; + qemu_opt_parse(opt, &local_err); + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + return; } } - - return 0; } int qemu_opts_foreach(QemuOptsList *list, qemu_opts_loopfunc func, void *opaque, diff --git a/qemu-option.h b/qemu-option.h index 3ca00c3cec1cea7d27dff9b503a44953893921c2..951dec3cc40937d0f9e543f02e394dfb9ffcd5d8 100644 --- a/qemu-option.h +++ b/qemu-option.h @@ -28,6 +28,7 @@ #include #include "qemu-queue.h" +#include "error.h" #include "qdict.h" enum QEMUOptionParType { @@ -110,25 +111,29 @@ bool qemu_opt_get_bool(QemuOpts *opts, const char *name, bool defval); uint64_t qemu_opt_get_number(QemuOpts *opts, const char *name, uint64_t defval); uint64_t qemu_opt_get_size(QemuOpts *opts, const char *name, uint64_t defval); int qemu_opt_set(QemuOpts *opts, const char *name, const char *value); +void qemu_opt_set_err(QemuOpts *opts, const char *name, const char *value, + Error **errp); int qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val); typedef int (*qemu_opt_loopfunc)(const char *name, const char *value, void *opaque); int qemu_opt_foreach(QemuOpts *opts, qemu_opt_loopfunc func, void *opaque, int abort_on_failure); QemuOpts *qemu_opts_find(QemuOptsList *list, const char *id); -QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, int fail_if_exists); +QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, + int fail_if_exists, Error **errp); void qemu_opts_reset(QemuOptsList *list); void qemu_opts_loc_restore(QemuOpts *opts); int qemu_opts_set(QemuOptsList *list, const char *id, const char *name, const char *value); const char *qemu_opts_id(QemuOpts *opts); void qemu_opts_del(QemuOpts *opts); -int qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc); +void qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc, Error **errp); int qemu_opts_do_parse(QemuOpts *opts, const char *params, const char *firstname); QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params, int permit_abbrev); void qemu_opts_set_defaults(QemuOptsList *list, const char *params, int permit_abbrev); -QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict); +QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict, + Error **errp); QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict); typedef int (*qemu_opts_loopfunc)(QemuOpts *opts, void *opaque); diff --git a/qemu-sockets.c b/qemu-sockets.c index 46c7619c7e6f8fa4860883ccdb8594c803a211fd..2ae715db769be449f8ee9cb0ed4924a1fb7369ae 100644 --- a/qemu-sockets.c +++ b/qemu-sockets.c @@ -461,7 +461,7 @@ int inet_listen(const char *str, char *ostr, int olen, char *optstr; int sock = -1; - opts = qemu_opts_create(&dummy_opts, NULL, 0); + opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL); if (inet_parse(opts, str) == 0) { sock = inet_listen_opts(opts, port_offset, errp); if (sock != -1 && ostr) { @@ -490,7 +490,7 @@ int inet_connect(const char *str, bool block, Error **errp) QemuOpts *opts; int sock = -1; - opts = qemu_opts_create(&dummy_opts, NULL, 0); + opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL); if (inet_parse(opts, str) == 0) { if (block) { qemu_opt_set(opts, "block", "on"); @@ -589,7 +589,7 @@ int unix_listen(const char *str, char *ostr, int olen) char *path, *optstr; int sock, len; - opts = qemu_opts_create(&dummy_opts, NULL, 0); + opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL); optstr = strchr(str, ','); if (optstr) { @@ -617,7 +617,7 @@ int unix_connect(const char *path) QemuOpts *opts; int sock; - opts = qemu_opts_create(&dummy_opts, NULL, 0); + opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL); qemu_opt_set(opts, "path", path); sock = unix_connect_opts(opts); qemu_opts_del(opts); diff --git a/qerror.c b/qerror.c index 5092fe75497106a410a2a0e42cc6f9f25787fc5e..92c4eff1791d65f9a7cd0fa898ff0bc809b6d190 100644 --- a/qerror.c +++ b/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)'", diff --git a/qerror.h b/qerror.h index 4cbba48c5ac1bc87b4190433c811112de4b5849f..b4c8758f400948592e10b963118250f635cd9a19 100644 --- a/qerror.h +++ b/qerror.h @@ -139,6 +139,9 @@ QError *qobject_to_qerror(const QObject *obj); #define QERR_INVALID_BLOCK_FORMAT \ "{ 'class': 'InvalidBlockFormat', 'data': { 'name': %s } }" +#define QERR_INVALID_OPTION_GROUP \ + "{ 'class': 'InvalidOptionGroup', 'data': { 'group': %s } }" + #define QERR_INVALID_PARAMETER \ "{ 'class': 'InvalidParameter', 'data': { 'name': %s } }" diff --git a/qmp-commands.hx b/qmp-commands.hx index db980fa811325aeca8ad43472ba468702d4a25a2..2e1a38e6952122fbf5071e516cc650df5f7ef5c1 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -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 ------------- diff --git a/target-i386/arch_dump.c b/target-i386/arch_dump.c new file mode 100644 index 0000000000000000000000000000000000000000..135d855c4adb574ca02c19e75cd8908f19f27c91 --- /dev/null +++ b/target-i386/arch_dump.c @@ -0,0 +1,449 @@ +/* + * i386 memory mapping + * + * Copyright Fujitsu, Corp. 2011, 2012 + * + * Authors: + * Wen Congyang + * + * 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; +} diff --git a/target-i386/arch_memory_mapping.c b/target-i386/arch_memory_mapping.c new file mode 100644 index 0000000000000000000000000000000000000000..bd50e1143a57a140d83fc014cffea8c7d41f9c6e --- /dev/null +++ b/target-i386/arch_memory_mapping.c @@ -0,0 +1,271 @@ +/* + * i386 memory mapping + * + * Copyright Fujitsu, Corp. 2011, 2012 + * + * Authors: + * Wen Congyang + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include "cpu.h" +#include "cpu-all.h" + +/* PAE Paging or IA-32e Paging */ +static void walk_pte(MemoryMappingList *list, target_phys_addr_t pte_start_addr, + int32_t a20_mask, target_ulong start_line_addr) +{ + target_phys_addr_t pte_addr, start_paddr; + uint64_t pte; + target_ulong start_vaddr; + int i; + + for (i = 0; i < 512; i++) { + pte_addr = (pte_start_addr + i * 8) & a20_mask; + pte = ldq_phys(pte_addr); + if (!(pte & PG_PRESENT_MASK)) { + /* not present */ + continue; + } + + start_paddr = (pte & ~0xfff) & ~(0x1ULL << 63); + if (cpu_physical_memory_is_io(start_paddr)) { + /* I/O region */ + continue; + } + + start_vaddr = start_line_addr | ((i & 0x1fff) << 12); + memory_mapping_list_add_merge_sorted(list, start_paddr, + start_vaddr, 1 << 12); + } +} + +/* 32-bit Paging */ +static void walk_pte2(MemoryMappingList *list, + target_phys_addr_t pte_start_addr, int32_t a20_mask, + target_ulong start_line_addr) +{ + target_phys_addr_t pte_addr, start_paddr; + uint32_t pte; + target_ulong start_vaddr; + int i; + + for (i = 0; i < 1024; i++) { + pte_addr = (pte_start_addr + i * 4) & a20_mask; + pte = ldl_phys(pte_addr); + if (!(pte & PG_PRESENT_MASK)) { + /* not present */ + continue; + } + + start_paddr = pte & ~0xfff; + if (cpu_physical_memory_is_io(start_paddr)) { + /* I/O region */ + continue; + } + + start_vaddr = start_line_addr | ((i & 0x3ff) << 12); + memory_mapping_list_add_merge_sorted(list, start_paddr, + start_vaddr, 1 << 12); + } +} + +/* PAE Paging or IA-32e Paging */ +static void walk_pde(MemoryMappingList *list, target_phys_addr_t pde_start_addr, + int32_t a20_mask, target_ulong start_line_addr) +{ + target_phys_addr_t pde_addr, pte_start_addr, start_paddr; + uint64_t pde; + target_ulong line_addr, start_vaddr; + int i; + + for (i = 0; i < 512; i++) { + pde_addr = (pde_start_addr + i * 8) & a20_mask; + pde = ldq_phys(pde_addr); + if (!(pde & PG_PRESENT_MASK)) { + /* not present */ + continue; + } + + line_addr = start_line_addr | ((i & 0x1ff) << 21); + if (pde & PG_PSE_MASK) { + /* 2 MB page */ + start_paddr = (pde & ~0x1fffff) & ~(0x1ULL << 63); + if (cpu_physical_memory_is_io(start_paddr)) { + /* I/O region */ + continue; + } + start_vaddr = line_addr; + memory_mapping_list_add_merge_sorted(list, start_paddr, + start_vaddr, 1 << 21); + continue; + } + + pte_start_addr = (pde & ~0xfff) & a20_mask; + walk_pte(list, pte_start_addr, a20_mask, line_addr); + } +} + +/* 32-bit Paging */ +static void walk_pde2(MemoryMappingList *list, + target_phys_addr_t pde_start_addr, int32_t a20_mask, + bool pse) +{ + target_phys_addr_t pde_addr, pte_start_addr, start_paddr; + uint32_t pde; + target_ulong line_addr, start_vaddr; + int i; + + for (i = 0; i < 1024; i++) { + pde_addr = (pde_start_addr + i * 4) & a20_mask; + pde = ldl_phys(pde_addr); + if (!(pde & PG_PRESENT_MASK)) { + /* not present */ + continue; + } + + line_addr = (((unsigned int)i & 0x3ff) << 22); + if ((pde & PG_PSE_MASK) && pse) { + /* 4 MB page */ + start_paddr = (pde & ~0x3fffff) | ((pde & 0x1fe000) << 19); + if (cpu_physical_memory_is_io(start_paddr)) { + /* I/O region */ + continue; + } + start_vaddr = line_addr; + memory_mapping_list_add_merge_sorted(list, start_paddr, + start_vaddr, 1 << 22); + continue; + } + + pte_start_addr = (pde & ~0xfff) & a20_mask; + walk_pte2(list, pte_start_addr, a20_mask, line_addr); + } +} + +/* PAE Paging */ +static void walk_pdpe2(MemoryMappingList *list, + target_phys_addr_t pdpe_start_addr, int32_t a20_mask) +{ + target_phys_addr_t pdpe_addr, pde_start_addr; + uint64_t pdpe; + target_ulong line_addr; + int i; + + for (i = 0; i < 4; i++) { + pdpe_addr = (pdpe_start_addr + i * 8) & a20_mask; + pdpe = ldq_phys(pdpe_addr); + if (!(pdpe & PG_PRESENT_MASK)) { + /* not present */ + continue; + } + + line_addr = (((unsigned int)i & 0x3) << 30); + pde_start_addr = (pdpe & ~0xfff) & a20_mask; + walk_pde(list, pde_start_addr, a20_mask, line_addr); + } +} + +#ifdef TARGET_X86_64 +/* IA-32e Paging */ +static void walk_pdpe(MemoryMappingList *list, + target_phys_addr_t pdpe_start_addr, int32_t a20_mask, + target_ulong start_line_addr) +{ + target_phys_addr_t pdpe_addr, pde_start_addr, start_paddr; + uint64_t pdpe; + target_ulong line_addr, start_vaddr; + int i; + + for (i = 0; i < 512; i++) { + pdpe_addr = (pdpe_start_addr + i * 8) & a20_mask; + pdpe = ldq_phys(pdpe_addr); + if (!(pdpe & PG_PRESENT_MASK)) { + /* not present */ + continue; + } + + line_addr = start_line_addr | ((i & 0x1ffULL) << 30); + if (pdpe & PG_PSE_MASK) { + /* 1 GB page */ + start_paddr = (pdpe & ~0x3fffffff) & ~(0x1ULL << 63); + if (cpu_physical_memory_is_io(start_paddr)) { + /* I/O region */ + continue; + } + start_vaddr = line_addr; + memory_mapping_list_add_merge_sorted(list, start_paddr, + start_vaddr, 1 << 30); + continue; + } + + pde_start_addr = (pdpe & ~0xfff) & a20_mask; + walk_pde(list, pde_start_addr, a20_mask, line_addr); + } +} + +/* IA-32e Paging */ +static void walk_pml4e(MemoryMappingList *list, + target_phys_addr_t pml4e_start_addr, int32_t a20_mask) +{ + target_phys_addr_t pml4e_addr, pdpe_start_addr; + uint64_t pml4e; + target_ulong line_addr; + int i; + + for (i = 0; i < 512; i++) { + pml4e_addr = (pml4e_start_addr + i * 8) & a20_mask; + pml4e = ldq_phys(pml4e_addr); + if (!(pml4e & PG_PRESENT_MASK)) { + /* not present */ + continue; + } + + line_addr = ((i & 0x1ffULL) << 39) | (0xffffULL << 48); + pdpe_start_addr = (pml4e & ~0xfff) & a20_mask; + walk_pdpe(list, pdpe_start_addr, a20_mask, line_addr); + } +} +#endif + +int cpu_get_memory_mapping(MemoryMappingList *list, CPUArchState *env) +{ + if (!cpu_paging_enabled(env)) { + /* paging is disabled */ + return 0; + } + + if (env->cr[4] & CR4_PAE_MASK) { +#ifdef TARGET_X86_64 + if (env->hflags & HF_LMA_MASK) { + target_phys_addr_t pml4e_addr; + + pml4e_addr = (env->cr[3] & ~0xfff) & env->a20_mask; + walk_pml4e(list, pml4e_addr, env->a20_mask); + } else +#endif + { + target_phys_addr_t pdpe_addr; + + pdpe_addr = (env->cr[3] & ~0x1f) & env->a20_mask; + walk_pdpe2(list, pdpe_addr, env->a20_mask); + } + } else { + target_phys_addr_t pde_addr; + bool pse; + + pde_addr = (env->cr[3] & ~0xfff) & env->a20_mask; + pse = !!(env->cr[4] & CR4_PSE_MASK); + walk_pde2(list, pde_addr, env->a20_mask, pse); + } + + return 0; +} + +bool cpu_paging_enabled(CPUArchState *env) +{ + return env->cr[0] & CR0_PG_MASK; +} diff --git a/vl.c b/vl.c index 23ab3a34d0ee0e07772bd8a7215a2892e43d6761..148542617dab9203f71db0cb2349e8a4ca91bb7d 100644 --- a/vl.c +++ b/vl.c @@ -1786,7 +1786,7 @@ static int balloon_parse(const char *arg) return -1; } else { /* create empty opts */ - opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0); + opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, NULL); } qemu_opt_set(opts, "driver", "virtio-balloon"); return 0; @@ -1921,7 +1921,7 @@ static void monitor_parse(const char *optarg, const char *mode) } } - opts = qemu_opts_create(qemu_find_opts("mon"), label, 1); + opts = qemu_opts_create(qemu_find_opts("mon"), label, 1, NULL); if (!opts) { fprintf(stderr, "duplicate chardev: %s\n", label); exit(1); @@ -2035,14 +2035,14 @@ static int virtcon_parse(const char *devname) exit(1); } - bus_opts = qemu_opts_create(device, NULL, 0); + bus_opts = qemu_opts_create(device, NULL, 0, NULL); if (arch_type == QEMU_ARCH_S390X) { qemu_opt_set(bus_opts, "driver", "virtio-serial-s390"); } else { qemu_opt_set(bus_opts, "driver", "virtio-serial-pci"); } - dev_opts = qemu_opts_create(device, NULL, 0); + dev_opts = qemu_opts_create(device, NULL, 0, NULL); qemu_opt_set(dev_opts, "driver", "virtconsole"); snprintf(label, sizeof(label), "virtcon%d", index); @@ -2065,7 +2065,7 @@ static int debugcon_parse(const char *devname) if (!qemu_chr_new("debugcon", devname, NULL)) { exit(1); } - opts = qemu_opts_create(qemu_find_opts("device"), "debugcon", 1); + opts = qemu_opts_create(qemu_find_opts("device"), "debugcon", 1, NULL); if (!opts) { fprintf(stderr, "qemu: already have a debugcon device\n"); exit(1); @@ -2813,7 +2813,8 @@ int main(int argc, char **argv, char **envp) exit(1); } fsdev = qemu_opts_create(qemu_find_opts("fsdev"), - qemu_opt_get(opts, "mount_tag"), 1); + qemu_opt_get(opts, "mount_tag"), + 1, NULL); if (!fsdev) { fprintf(stderr, "duplicate fsdev id: %s\n", qemu_opt_get(opts, "mount_tag")); @@ -2845,7 +2846,8 @@ int main(int argc, char **argv, char **envp) qemu_opt_set_bool(fsdev, "readonly", qemu_opt_get_bool(opts, "readonly", 0)); - device = qemu_opts_create(qemu_find_opts("device"), NULL, 0); + device = qemu_opts_create(qemu_find_opts("device"), NULL, 0, + NULL); qemu_opt_set(device, "driver", "virtio-9p-pci"); qemu_opt_set(device, "fsdev", qemu_opt_get(opts, "mount_tag")); @@ -2857,14 +2859,16 @@ int main(int argc, char **argv, char **envp) QemuOpts *fsdev; QemuOpts *device; - fsdev = qemu_opts_create(qemu_find_opts("fsdev"), "v_synth", 1); + fsdev = qemu_opts_create(qemu_find_opts("fsdev"), "v_synth", + 1, NULL); if (!fsdev) { fprintf(stderr, "duplicate option: %s\n", "virtfs_synth"); exit(1); } qemu_opt_set(fsdev, "fsdriver", "synth"); - device = qemu_opts_create(qemu_find_opts("device"), NULL, 0); + device = qemu_opts_create(qemu_find_opts("device"), NULL, 0, + NULL); qemu_opt_set(device, "driver", "virtio-9p-pci"); qemu_opt_set(device, "fsdev", "v_synth"); qemu_opt_set(device, "mount_tag", "v_synth");