From 80167a8a31d7c42e3e33ec23592eb14938c88098 Mon Sep 17 00:00:00 2001 From: Wen Congyang Date: Mon, 7 May 2012 12:03:46 +0800 Subject: [PATCH 01/29] Add API to create memory mapping list The memory mapping list stores virtual address and physical address mapping. The virtual address and physical address are contiguous in the mapping. The folloing patch will use this information to create PT_LOAD in the vmcore. Signed-off-by: Wen Congyang Signed-off-by: Luiz Capitulino --- Makefile.target | 1 + memory_mapping.c | 166 +++++++++++++++++++++++++++++++++++++++++++++++ memory_mapping.h | 47 ++++++++++++++ 3 files changed, 214 insertions(+) create mode 100644 memory_mapping.c create mode 100644 memory_mapping.h diff --git a/Makefile.target b/Makefile.target index 15829041c7..005fc49f75 100644 --- a/Makefile.target +++ b/Makefile.target @@ -192,6 +192,7 @@ 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 LIBS+=-lz obj-i386-$(CONFIG_KVM) += hyperv.o diff --git a/memory_mapping.c b/memory_mapping.c new file mode 100644 index 0000000000..718f271849 --- /dev/null +++ b/memory_mapping.c @@ -0,0 +1,166 @@ +/* + * 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); +} diff --git a/memory_mapping.h b/memory_mapping.h new file mode 100644 index 0000000000..836b0472b6 --- /dev/null +++ b/memory_mapping.h @@ -0,0 +1,47 @@ +/* + * 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" + +/* 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); + +#endif From 76f3553883b868e99c78212b9d12309e8e1e5d81 Mon Sep 17 00:00:00 2001 From: Wen Congyang Date: Mon, 7 May 2012 12:04:18 +0800 Subject: [PATCH 02/29] Add API to check whether a physical address is I/O address This API will be used in the following patch. Signed-off-by: Wen Congyang Signed-off-by: Luiz Capitulino --- cpu-common.h | 4 ++++ exec.c | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/cpu-common.h b/cpu-common.h index dca5175652..1fe3280701 100644 --- a/cpu-common.h +++ b/cpu-common.h @@ -71,6 +71,10 @@ void cpu_physical_memory_unmap(void *buffer, target_phys_addr_t len, void *cpu_register_map_client(void *opaque, void (*callback)(void *opaque)); void cpu_unregister_map_client(void *cookie); +#ifndef CONFIG_USER_ONLY +bool cpu_physical_memory_is_io(target_phys_addr_t phys_addr); +#endif + /* Coalesced MMIO regions are areas where write operations can be reordered. * This usually implies that write operations are side-effect free. This allows * batching which can make a major impact on performance when using diff --git a/exec.c b/exec.c index a0494c72bf..1b65859e50 100644 --- a/exec.c +++ b/exec.c @@ -4336,3 +4336,15 @@ bool virtio_is_big_endian(void) } #endif + +#ifndef CONFIG_USER_ONLY +bool cpu_physical_memory_is_io(target_phys_addr_t phys_addr) +{ + MemoryRegionSection *section; + + section = phys_page_find(phys_addr >> TARGET_PAGE_BITS); + + return !(memory_region_is_ram(section->mr) || + memory_region_is_romd(section->mr)); +} +#endif From fae001f55190b4de511269ca63eb635646d1c7c9 Mon Sep 17 00:00:00 2001 From: Wen Congyang Date: Mon, 7 May 2012 12:04:57 +0800 Subject: [PATCH 03/29] implement cpu_get_memory_mapping() Walk cpu's page table and collect all virtual address and physical address mapping. Then, add these mapping into memory mapping list. If the guest does not use paging, it will do nothing. Note: the I/O memory will be skipped. Signed-off-by: Wen Congyang Signed-off-by: Luiz Capitulino --- Makefile.target | 1 + configure | 4 + cpu-all.h | 11 ++ memory_mapping.h | 6 + target-i386/arch_memory_mapping.c | 266 ++++++++++++++++++++++++++++++ 5 files changed, 288 insertions(+) create mode 100644 target-i386/arch_memory_mapping.c diff --git a/Makefile.target b/Makefile.target index 005fc49f75..18ffaefc50 100644 --- a/Makefile.target +++ b/Makefile.target @@ -193,6 +193,7 @@ 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 LIBS+=-lz obj-i386-$(CONFIG_KVM) += hyperv.o diff --git a/configure b/configure index 1f338f8dce..20608c55e5 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 diff --git a/cpu-all.h b/cpu-all.h index 028528f0be..2688baca8c 100644 --- a/cpu-all.h +++ b/cpu-all.h @@ -22,6 +22,7 @@ #include "qemu-common.h" #include "qemu-tls.h" #include "cpu-common.h" +#include "memory_mapping.h" /* some important defines: * @@ -524,4 +525,14 @@ 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); +#else +static inline int cpu_get_memory_mapping(MemoryMappingList *list, + CPUArchState *env) +{ + return -1; +} +#endif + #endif /* CPU_ALL_H */ diff --git a/memory_mapping.h b/memory_mapping.h index 836b0472b6..e486d1056e 100644 --- a/memory_mapping.h +++ b/memory_mapping.h @@ -16,6 +16,7 @@ #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; @@ -44,4 +45,9 @@ void memory_mapping_list_free(MemoryMappingList *list); void memory_mapping_list_init(MemoryMappingList *list); +#else + +/* We use MemoryMappingList* in cpu-all.h */ +typedef struct MemoryMappingList MemoryMappingList; +#endif #endif diff --git a/target-i386/arch_memory_mapping.c b/target-i386/arch_memory_mapping.c new file mode 100644 index 0000000000..dd64becf73 --- /dev/null +++ b/target-i386/arch_memory_mapping.c @@ -0,0 +1,266 @@ +/* + * 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 (!(env->cr[0] & CR0_PG_MASK)) { + /* 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; +} From 31a2207a8e1c39bcf88e527ac62f4e12316920a4 Mon Sep 17 00:00:00 2001 From: Wen Congyang Date: Mon, 7 May 2012 12:05:42 +0800 Subject: [PATCH 04/29] Add API to check whether paging mode is enabled This API will be used in the following patch. Signed-off-by: Wen Congyang Signed-off-by: Luiz Capitulino --- cpu-all.h | 6 ++++++ target-i386/arch_memory_mapping.c | 7 ++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/cpu-all.h b/cpu-all.h index 2688baca8c..76439b4221 100644 --- a/cpu-all.h +++ b/cpu-all.h @@ -527,12 +527,18 @@ int cpu_memory_rw_debug(CPUArchState *env, target_ulong addr, #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 #endif /* CPU_ALL_H */ diff --git a/target-i386/arch_memory_mapping.c b/target-i386/arch_memory_mapping.c index dd64becf73..bd50e1143a 100644 --- a/target-i386/arch_memory_mapping.c +++ b/target-i386/arch_memory_mapping.c @@ -233,7 +233,7 @@ static void walk_pml4e(MemoryMappingList *list, int cpu_get_memory_mapping(MemoryMappingList *list, CPUArchState *env) { - if (!(env->cr[0] & CR0_PG_MASK)) { + if (!cpu_paging_enabled(env)) { /* paging is disabled */ return 0; } @@ -264,3 +264,8 @@ int cpu_get_memory_mapping(MemoryMappingList *list, CPUArchState *env) return 0; } + +bool cpu_paging_enabled(CPUArchState *env) +{ + return env->cr[0] & CR0_PG_MASK; +} From c517076dae5540241a5d8947471b46fe3248f6cd Mon Sep 17 00:00:00 2001 From: Wen Congyang Date: Mon, 7 May 2012 12:06:40 +0800 Subject: [PATCH 05/29] Add API to get memory mapping Add API to get all virtual address and physical address mapping. If the guest doesn't use paging, the virtual address is equal to the phyical address. The virtual address and physical address mapping is for gdb's user, and it does not include the memory that is not referenced by the page table. So if you want to use crash to anaylze the vmcore, please do not specify -p option. the reason why the -p option is not default explicitly: guest machine in a catastrophic state can have corrupted memory, which we cannot trust. Signed-off-by: Wen Congyang Signed-off-by: Luiz Capitulino --- memory_mapping.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ memory_mapping.h | 15 +++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/memory_mapping.c b/memory_mapping.c index 718f271849..627397a0d5 100644 --- a/memory_mapping.c +++ b/memory_mapping.c @@ -164,3 +164,50 @@ void memory_mapping_list_init(MemoryMappingList *list) 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 diff --git a/memory_mapping.h b/memory_mapping.h index e486d1056e..7f3c256548 100644 --- a/memory_mapping.h +++ b/memory_mapping.h @@ -45,6 +45,21 @@ 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 + #else /* We use MemoryMappingList* in cpu-all.h */ From 2b05ab5229f880720ab6ca0e52577089889c10f9 Mon Sep 17 00:00:00 2001 From: Wen Congyang Date: Mon, 7 May 2012 12:07:07 +0800 Subject: [PATCH 06/29] Add API to get memory mapping without do paging crash does not need the virtual address and physical address mapping, and the mapping does not include the memory that is not referenced by the page table. crash does not use the virtual address, so we can create the mapping for all physical memory(virtual address is always 0). This patch provides a API to do this thing, and it will be used in the following patch. Signed-off-by: Wen Congyang Signed-off-by: Luiz Capitulino --- memory_mapping.c | 9 +++++++++ memory_mapping.h | 3 +++ 2 files changed, 12 insertions(+) diff --git a/memory_mapping.c b/memory_mapping.c index 627397a0d5..adb159577d 100644 --- a/memory_mapping.c +++ b/memory_mapping.c @@ -211,3 +211,12 @@ int qemu_get_guest_memory_mapping(MemoryMappingList *list) 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); + } +} diff --git a/memory_mapping.h b/memory_mapping.h index 7f3c256548..190de12453 100644 --- a/memory_mapping.h +++ b/memory_mapping.h @@ -60,6 +60,9 @@ static inline int qemu_get_guest_memory_mapping(MemoryMappingList *list) } #endif +/* get guest's memory mapping without do paging(virtual address is 0). */ +void qemu_get_guest_simple_memory_mapping(MemoryMappingList *list); + #else /* We use MemoryMappingList* in cpu-all.h */ From 9fecbed0c03ddad63c27f1622e2002fdfc2f45e4 Mon Sep 17 00:00:00 2001 From: Wen Congyang Date: Mon, 7 May 2012 12:07:48 +0800 Subject: [PATCH 07/29] target-i386: Add API to write elf notes to core file The core file contains register's value. These APIs write registers to core file, and them will be called in the following patch. Signed-off-by: Wen Congyang Signed-off-by: Luiz Capitulino --- Makefile.target | 1 + configure | 4 + cpu-all.h | 22 ++++ target-i386/arch_dump.c | 233 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 260 insertions(+) create mode 100644 target-i386/arch_dump.c diff --git a/Makefile.target b/Makefile.target index 18ffaefc50..cf05831456 100644 --- a/Makefile.target +++ b/Makefile.target @@ -194,6 +194,7 @@ 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 diff --git a/configure b/configure index 20608c55e5..76dd57fc27 100755 --- a/configure +++ b/configure @@ -3748,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 76439b4221..a71e88718a 100644 --- a/cpu-all.h +++ b/cpu-all.h @@ -541,4 +541,26 @@ static inline bool cpu_paging_enabled(CPUArchState *env) } #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); +#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; +} +#endif + #endif /* CPU_ALL_H */ diff --git a/target-i386/arch_dump.c b/target-i386/arch_dump.c new file mode 100644 index 0000000000..d55c2ce8c7 --- /dev/null +++ b/target-i386/arch_dump.c @@ -0,0 +1,233 @@ +/* + * 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 "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; +} From 90166b71c4d75823b48a132e9394af35b9c90aaf Mon Sep 17 00:00:00 2001 From: Wen Congyang Date: Mon, 7 May 2012 12:08:22 +0800 Subject: [PATCH 08/29] target-i386: Add API to write cpu status to core file The core file has register's value. But it does not include all registers value. Store the cpu status into QEMU note, and the user can get more information from vmcore. If you change QEMUCPUState, please count up QEMUCPUSTATE_VERSION. Signed-off-by: Wen Congyang Signed-off-by: Luiz Capitulino --- cpu-all.h | 18 +++++ target-i386/arch_dump.c | 149 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+) diff --git a/cpu-all.h b/cpu-all.h index a71e88718a..a167bacd6d 100644 --- a/cpu-all.h +++ b/cpu-all.h @@ -547,6 +547,10 @@ 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); #else static inline int cpu_write_elf64_note(write_core_dump_function f, CPUArchState *env, int cpuid, @@ -561,6 +565,20 @@ static inline int cpu_write_elf32_note(write_core_dump_function f, { 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; +} #endif #endif /* CPU_ALL_H */ diff --git a/target-i386/arch_dump.c b/target-i386/arch_dump.c index d55c2ce8c7..ddbe20c32a 100644 --- a/target-i386/arch_dump.c +++ b/target-i386/arch_dump.c @@ -231,3 +231,152 @@ int cpu_write_elf32_note(write_core_dump_function f, CPUArchState *env, 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); +} From 25ae9c1d8bbfeb6c00eccb4297a5937d8375a44a Mon Sep 17 00:00:00 2001 From: Wen Congyang Date: Mon, 7 May 2012 12:08:56 +0800 Subject: [PATCH 09/29] target-i386: add API to get dump info Dump info contains: endian, class and architecture. The next patch will use these information to create vmcore. Note: on x86 box, the class is ELFCLASS64 if the memory is larger than 4G. Signed-off-by: Wen Congyang Signed-off-by: Luiz Capitulino --- cpu-all.h | 7 +++++++ dump.h | 23 +++++++++++++++++++++++ target-i386/arch_dump.c | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 dump.h diff --git a/cpu-all.h b/cpu-all.h index a167bacd6d..7e07c91275 100644 --- a/cpu-all.h +++ b/cpu-all.h @@ -23,6 +23,7 @@ #include "qemu-tls.h" #include "cpu-common.h" #include "memory_mapping.h" +#include "dump.h" /* some important defines: * @@ -551,6 +552,7 @@ 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); #else static inline int cpu_write_elf64_note(write_core_dump_function f, CPUArchState *env, int cpuid, @@ -579,6 +581,11 @@ static inline int cpu_write_elf32_qemunote(write_core_dump_function f, { return -1; } + +static inline int cpu_get_dump_info(ArchDumpInfo *info) +{ + return -1; +} #endif #endif /* CPU_ALL_H */ diff --git a/dump.h b/dump.h new file mode 100644 index 0000000000..28340cf271 --- /dev/null +++ b/dump.h @@ -0,0 +1,23 @@ +/* + * QEMU dump + * + * Copyright Fujitsu, Corp. 2011, 2012 + * + * Authors: + * Wen Congyang + * + * 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/target-i386/arch_dump.c b/target-i386/arch_dump.c index ddbe20c32a..e37857968d 100644 --- a/target-i386/arch_dump.c +++ b/target-i386/arch_dump.c @@ -13,6 +13,7 @@ #include "cpu.h" #include "cpu-all.h" +#include "dump.h" #include "elf.h" #ifdef TARGET_X86_64 @@ -380,3 +381,36 @@ int cpu_write_elf32_qemunote(write_core_dump_function f, CPUArchState *env, { 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; +} From 0038ffb09692955b8f8f363958cbf58c205ae772 Mon Sep 17 00:00:00 2001 From: Wen Congyang Date: Mon, 7 May 2012 12:09:36 +0800 Subject: [PATCH 10/29] target-i386: Add API to get note's size We should know where the note and memory is stored before writing them to vmcore. If we know this, we can avoid using lseek() when creating vmcore. Signed-off-by: Wen Congyang Signed-off-by: Luiz Capitulino --- cpu-all.h | 6 ++++++ target-i386/arch_dump.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/cpu-all.h b/cpu-all.h index 7e07c91275..2b77f66b35 100644 --- a/cpu-all.h +++ b/cpu-all.h @@ -553,6 +553,7 @@ int cpu_write_elf64_qemunote(write_core_dump_function f, CPUArchState *env, 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, @@ -586,6 +587,11 @@ 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/target-i386/arch_dump.c b/target-i386/arch_dump.c index e37857968d..135d855c4a 100644 --- a/target-i386/arch_dump.c +++ b/target-i386/arch_dump.c @@ -414,3 +414,36 @@ int cpu_get_dump_info(ArchDumpInfo *info) 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; +} From 68f4730c714b7c30ca912d7c03e199f181739da0 Mon Sep 17 00:00:00 2001 From: Wen Congyang Date: Mon, 7 May 2012 12:10:05 +0800 Subject: [PATCH 11/29] make gdb_id() generally avialable and rename it to cpu_index() The following patch also needs this API, so make it generally avialable. The function gdb_id() will not be used in gdbstub.c now, so its name is not suitable, and rename it to cpu_index() Signed-off-by: Wen Congyang Signed-off-by: Luiz Capitulino --- gdbstub.c | 19 +++++-------------- gdbstub.h | 9 +++++++++ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 6a77a6696b..08cf8645d7 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1937,21 +1937,12 @@ static void gdb_set_cpu_pc(GDBState *s, target_ulong pc) #endif } -static inline int gdb_id(CPUArchState *env) -{ -#if defined(CONFIG_USER_ONLY) && defined(CONFIG_USE_NPTL) - return env->host_tid; -#else - return env->cpu_index + 1; -#endif -} - static CPUArchState *find_cpu(uint32_t thread_id) { CPUArchState *env; for (env = first_cpu; env != NULL; env = env->next_cpu) { - if (gdb_id(env) == thread_id) { + if (cpu_index(env) == thread_id) { return env; } } @@ -1979,7 +1970,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) case '?': /* TODO: Make this return the correct value for user-mode. */ snprintf(buf, sizeof(buf), "T%02xthread:%02x;", GDB_SIGNAL_TRAP, - gdb_id(s->c_cpu)); + cpu_index(s->c_cpu)); put_packet(s, buf); /* Remove all the breakpoints when this query is issued, * because gdb is doing and initial connect and the state @@ -2274,7 +2265,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } else if (strcmp(p,"sThreadInfo") == 0) { report_cpuinfo: if (s->query_cpu) { - snprintf(buf, sizeof(buf), "m%x", gdb_id(s->query_cpu)); + snprintf(buf, sizeof(buf), "m%x", cpu_index(s->query_cpu)); put_packet(s, buf); s->query_cpu = s->query_cpu->next_cpu; } else @@ -2422,7 +2413,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) } snprintf(buf, sizeof(buf), "T%02xthread:%02x;%swatch:" TARGET_FMT_lx ";", - GDB_SIGNAL_TRAP, gdb_id(env), type, + GDB_SIGNAL_TRAP, cpu_index(env), type, env->watchpoint_hit->vaddr); env->watchpoint_hit = NULL; goto send_packet; @@ -2455,7 +2446,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) ret = GDB_SIGNAL_UNKNOWN; break; } - snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, gdb_id(env)); + snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, cpu_index(env)); send_packet: put_packet(s, buf); diff --git a/gdbstub.h b/gdbstub.h index b44e27523a..668de66000 100644 --- a/gdbstub.h +++ b/gdbstub.h @@ -30,6 +30,15 @@ void gdb_register_coprocessor(CPUArchState *env, gdb_reg_cb get_reg, gdb_reg_cb set_reg, int num_regs, const char *xml, int g_pos); +static inline int cpu_index(CPUArchState *env) +{ +#if defined(CONFIG_USER_ONLY) && defined(CONFIG_USE_NPTL) + return env->host_tid; +#else + return env->cpu_index + 1; +#endif +} + #endif #ifdef CONFIG_USER_ONLY From 783e9b4826b95e53e33c42db6b4bd7d89bdff147 Mon Sep 17 00:00:00 2001 From: Wen Congyang Date: Mon, 7 May 2012 12:10:47 +0800 Subject: [PATCH 12/29] introduce a new monitor command 'dump-guest-memory' to dump guest's memory The command's usage: dump-guest-memory [-p] protocol [begin] [length] 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. Note: 1. If you want to use gdb to process the core, please specify -p option. The reason why the -p option is not default is: a. guest machine in a catastrophic state can have corrupted memory, which we cannot trust. b. 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. 2. If you don't want to dump all guest's memory, please specify the start physical address and the length. Signed-off-by: Wen Congyang Signed-off-by: Luiz Capitulino --- Makefile.target | 2 + dump.c | 883 +++++++++++++++++++++++++++++++++++++++++++++++ elf.h | 5 + hmp-commands.hx | 28 ++ hmp.c | 22 ++ hmp.h | 1 + memory_mapping.c | 27 ++ memory_mapping.h | 3 + qapi-schema.json | 43 +++ qmp-commands.hx | 36 ++ 10 files changed, 1050 insertions(+) create mode 100644 dump.c diff --git a/Makefile.target b/Makefile.target index cf05831456..c94abfd3a4 100644 --- a/Makefile.target +++ b/Makefile.target @@ -405,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/dump.c b/dump.c new file mode 100644 index 0000000000..0ca14f87ed --- /dev/null +++ b/dump.c @@ -0,0 +1,883 @@ +/* + * QEMU dump + * + * Copyright Fujitsu, Corp. 2011, 2012 + * + * Authors: + * Wen Congyang + * + * 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/elf.h b/elf.h index e1422b81ee..9c9acfaf75 100644 --- a/elf.h +++ b/elf.h @@ -1037,6 +1037,11 @@ typedef struct elf64_sym { #define EI_NIDENT 16 +/* Special value for e_phnum. This indicates that the real number of + program headers is too large to fit into e_phnum. Instead the real + value is in the field sh_info of section 0. */ +#define PN_XNUM 0xffff + typedef struct elf32_hdr{ unsigned char e_ident[EI_NIDENT]; Elf32_Half e_type; diff --git a/hmp-commands.hx b/hmp-commands.hx index 18cb415ac4..81723c87cc 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?", diff --git a/hmp.c b/hmp.c index bb0952e00b..42ced2a202 100644 --- a/hmp.c +++ b/hmp.c @@ -947,3 +947,25 @@ 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); +} diff --git a/hmp.h b/hmp.h index 443b812923..5cf3241af0 100644 --- a/hmp.h +++ b/hmp.h @@ -61,5 +61,6 @@ 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); #endif diff --git a/memory_mapping.c b/memory_mapping.c index adb159577d..8810bb09e3 100644 --- a/memory_mapping.c +++ b/memory_mapping.c @@ -220,3 +220,30 @@ void qemu_get_guest_simple_memory_mapping(MemoryMappingList *list) 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 index 190de12453..a1aa64f4ca 100644 --- a/memory_mapping.h +++ b/memory_mapping.h @@ -63,6 +63,9 @@ static inline int qemu_get_guest_memory_mapping(MemoryMappingList *list) /* 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 */ diff --git a/qapi-schema.json b/qapi-schema.json index 2ca7195d25..dfa0e1f47a 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -1755,3 +1755,46 @@ # 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' } } diff --git a/qmp-commands.hx b/qmp-commands.hx index db980fa811..2aa64add35 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -601,6 +601,42 @@ 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 { From 8be7e7e4c72c048b90e3482557954a24bba43ba7 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Tue, 20 Mar 2012 15:51:57 -0300 Subject: [PATCH 13/29] qemu-option: qemu_opts_create(): use error_set() This commit converts qemu_opts_create() from qerror_report() to error_set(). Currently, most calls to qemu_opts_create() can't fail, so most callers don't need any changes. The two cases where code checks for qemu_opts_create() erros are: 1. Initialization code in vl.c. All of them print their own error messages directly to stderr, no need to pass the Error object 2. The functions opts_parse(), qemu_opts_from_qdict() and qemu_chr_parse_compat() make use of the error information and they can be called from HMP or QMP. In this case, to allow for incremental conversion, we propagate the error up using qerror_report_err(), which keeps the QError semantics Signed-off-by: Luiz Capitulino Reviewed-By: Laszlo Ersek --- blockdev.c | 2 +- hw/usb/dev-storage.c | 2 +- hw/watchdog.c | 2 +- qemu-char.c | 8 ++++++-- qemu-config.c | 6 +++--- qemu-option.c | 37 +++++++++++++++++++++++++++---------- qemu-option.h | 4 +++- qemu-sockets.c | 8 ++++---- vl.c | 22 +++++++++++++--------- 9 files changed, 59 insertions(+), 32 deletions(-) diff --git a/blockdev.c b/blockdev.c index 67895b25d5..622ecba04e 100644 --- a/blockdev.c +++ b/blockdev.c @@ -569,7 +569,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi) break; case IF_VIRTIO: /* add virtio block device */ - opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0); + opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, NULL); if (arch_type == QEMU_ARCH_S390X) { qemu_opt_set(opts, "driver", "virtio-blk-s390"); } else { diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index ae22fb1c97..a96c0b9e5e 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -584,7 +584,7 @@ static USBDevice *usb_msd_init(USBBus *bus, const char *filename) /* parse -usbdevice disk: syntax into drive opts */ snprintf(id, sizeof(id), "usb%d", nr++); - opts = qemu_opts_create(qemu_find_opts("drive"), id, 0); + opts = qemu_opts_create(qemu_find_opts("drive"), id, 0, NULL); p1 = strchr(filename, ':'); if (p1++) { diff --git a/hw/watchdog.c b/hw/watchdog.c index 4c18965654..a42124d520 100644 --- a/hw/watchdog.c +++ b/hw/watchdog.c @@ -66,7 +66,7 @@ int select_watchdog(const char *p) QLIST_FOREACH(model, &watchdog_list, entry) { if (strcasecmp(model->wdt_name, p) == 0) { /* add the device */ - opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0); + opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, NULL); qemu_opt_set(opts, "driver", p); return 0; } diff --git a/qemu-char.c b/qemu-char.c index fe1126fe86..0bd903f58c 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2584,10 +2584,14 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) int pos; const char *p; QemuOpts *opts; + Error *local_err = NULL; - opts = qemu_opts_create(qemu_find_opts("chardev"), label, 1); - if (NULL == opts) + opts = qemu_opts_create(qemu_find_opts("chardev"), label, 1, &local_err); + if (error_is_set(&local_err)) { + qerror_report_err(local_err); + error_free(local_err); return NULL; + } if (strstart(filename, "mon:", &p)) { filename = p; diff --git a/qemu-config.c b/qemu-config.c index be84a0347c..f8766468bb 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -709,7 +709,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); @@ -781,7 +781,7 @@ int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname) list = find_list(lists, group); if (list == NULL) goto out; - opts = qemu_opts_create(list, id, 1); + opts = qemu_opts_create(list, id, 1, NULL); continue; } if (sscanf(line, "[%63[^]]]", group) == 1) { @@ -789,7 +789,7 @@ int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname) list = find_list(lists, group); if (list == NULL) 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-option.c b/qemu-option.c index 35cd609f75..9f531c8d01 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" /* @@ -729,20 +730,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 +785,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); @@ -883,6 +888,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 +904,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); @@ -975,11 +986,17 @@ static void qemu_opts_from_qdict_1(const char *key, QObject *obj, void *opaque) QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict) { QemuOpts *opts; + Error *local_err = NULL; - 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)) { + qerror_report_err(local_err); + error_free(local_err); return NULL; + } + assert(opts != NULL); qdict_iter(qdict, qemu_opts_from_qdict_1, opts); return opts; } diff --git a/qemu-option.h b/qemu-option.h index 3ca00c3cec..4d5b3d3f42 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 { @@ -116,7 +117,8 @@ 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, diff --git a/qemu-sockets.c b/qemu-sockets.c index 46c7619c7e..2ae715db76 100644 --- a/qemu-sockets.c +++ b/qemu-sockets.c @@ -461,7 +461,7 @@ int inet_listen(const char *str, char *ostr, int olen, char *optstr; int sock = -1; - opts = qemu_opts_create(&dummy_opts, NULL, 0); + opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL); if (inet_parse(opts, str) == 0) { sock = inet_listen_opts(opts, port_offset, errp); if (sock != -1 && ostr) { @@ -490,7 +490,7 @@ int inet_connect(const char *str, bool block, Error **errp) QemuOpts *opts; int sock = -1; - opts = qemu_opts_create(&dummy_opts, NULL, 0); + opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL); if (inet_parse(opts, str) == 0) { if (block) { qemu_opt_set(opts, "block", "on"); @@ -589,7 +589,7 @@ int unix_listen(const char *str, char *ostr, int olen) char *path, *optstr; int sock, len; - opts = qemu_opts_create(&dummy_opts, NULL, 0); + opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL); optstr = strchr(str, ','); if (optstr) { @@ -617,7 +617,7 @@ int unix_connect(const char *path) QemuOpts *opts; int sock; - opts = qemu_opts_create(&dummy_opts, NULL, 0); + opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL); qemu_opt_set(opts, "path", path); sock = unix_connect_opts(opts); qemu_opts_del(opts); diff --git a/vl.c b/vl.c index 23ab3a34d0..148542617d 100644 --- a/vl.c +++ b/vl.c @@ -1786,7 +1786,7 @@ static int balloon_parse(const char *arg) return -1; } else { /* create empty opts */ - opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0); + opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, NULL); } qemu_opt_set(opts, "driver", "virtio-balloon"); return 0; @@ -1921,7 +1921,7 @@ static void monitor_parse(const char *optarg, const char *mode) } } - opts = qemu_opts_create(qemu_find_opts("mon"), label, 1); + opts = qemu_opts_create(qemu_find_opts("mon"), label, 1, NULL); if (!opts) { fprintf(stderr, "duplicate chardev: %s\n", label); exit(1); @@ -2035,14 +2035,14 @@ static int virtcon_parse(const char *devname) exit(1); } - bus_opts = qemu_opts_create(device, NULL, 0); + bus_opts = qemu_opts_create(device, NULL, 0, NULL); if (arch_type == QEMU_ARCH_S390X) { qemu_opt_set(bus_opts, "driver", "virtio-serial-s390"); } else { qemu_opt_set(bus_opts, "driver", "virtio-serial-pci"); } - dev_opts = qemu_opts_create(device, NULL, 0); + dev_opts = qemu_opts_create(device, NULL, 0, NULL); qemu_opt_set(dev_opts, "driver", "virtconsole"); snprintf(label, sizeof(label), "virtcon%d", index); @@ -2065,7 +2065,7 @@ static int debugcon_parse(const char *devname) if (!qemu_chr_new("debugcon", devname, NULL)) { exit(1); } - opts = qemu_opts_create(qemu_find_opts("device"), "debugcon", 1); + opts = qemu_opts_create(qemu_find_opts("device"), "debugcon", 1, NULL); if (!opts) { fprintf(stderr, "qemu: already have a debugcon device\n"); exit(1); @@ -2813,7 +2813,8 @@ int main(int argc, char **argv, char **envp) exit(1); } fsdev = qemu_opts_create(qemu_find_opts("fsdev"), - qemu_opt_get(opts, "mount_tag"), 1); + qemu_opt_get(opts, "mount_tag"), + 1, NULL); if (!fsdev) { fprintf(stderr, "duplicate fsdev id: %s\n", qemu_opt_get(opts, "mount_tag")); @@ -2845,7 +2846,8 @@ int main(int argc, char **argv, char **envp) qemu_opt_set_bool(fsdev, "readonly", qemu_opt_get_bool(opts, "readonly", 0)); - device = qemu_opts_create(qemu_find_opts("device"), NULL, 0); + device = qemu_opts_create(qemu_find_opts("device"), NULL, 0, + NULL); qemu_opt_set(device, "driver", "virtio-9p-pci"); qemu_opt_set(device, "fsdev", qemu_opt_get(opts, "mount_tag")); @@ -2857,14 +2859,16 @@ int main(int argc, char **argv, char **envp) QemuOpts *fsdev; QemuOpts *device; - fsdev = qemu_opts_create(qemu_find_opts("fsdev"), "v_synth", 1); + fsdev = qemu_opts_create(qemu_find_opts("fsdev"), "v_synth", + 1, NULL); if (!fsdev) { fprintf(stderr, "duplicate option: %s\n", "virtfs_synth"); exit(1); } qemu_opt_set(fsdev, "fsdriver", "synth"); - device = qemu_opts_create(qemu_find_opts("device"), NULL, 0); + device = qemu_opts_create(qemu_find_opts("device"), NULL, 0, + NULL); qemu_opt_set(device, "driver", "virtio-9p-pci"); qemu_opt_set(device, "fsdev", "v_synth"); qemu_opt_set(device, "mount_tag", "v_synth"); From 2f39df5b0da6a030cc04c80e4e78ccd7002176b0 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Wed, 21 Mar 2012 14:37:44 -0300 Subject: [PATCH 14/29] qemu-option: parse_option_number(): use error_set() Note that qemu_opt_parse() callers still expect automatic error reporting with QError, so qemu_opts_parse() calls qerror_report_err() to keep the same semantics. Signed-off-by: Luiz Capitulino Reviewed-By: Laszlo Ersek --- qemu-option.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/qemu-option.c b/qemu-option.c index 9f531c8d01..72dcb566d8 100644 --- a/qemu-option.c +++ b/qemu-option.c @@ -186,7 +186,8 @@ static int parse_option_bool(const char *name, const char *value, bool *ret) 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; @@ -194,15 +195,13 @@ 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) @@ -579,8 +578,11 @@ uint64_t qemu_opt_get_size(QemuOpts *opts, const char *name, uint64_t defval) static int qemu_opt_parse(QemuOpt *opt) { + Error *local_err = NULL; + if (opt->desc == NULL) return 0; + switch (opt->desc->type) { case QEMU_OPT_STRING: /* nothing */ @@ -588,12 +590,22 @@ static int qemu_opt_parse(QemuOpt *opt) case QEMU_OPT_BOOL: return parse_option_bool(opt->name, opt->str, &opt->value.boolean); 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, + &local_err); + break; case QEMU_OPT_SIZE: return parse_option_size(opt->name, opt->str, &opt->value.uint); default: abort(); } + + if (error_is_set(&local_err)) { + qerror_report_err(local_err); + error_free(local_err); + return -1; + } + + return 0; } static void qemu_opt_del(QemuOpt *opt) From cf62adfabf29c0b7833a5306023cab5c0bc3b6d5 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Wed, 21 Mar 2012 14:54:42 -0300 Subject: [PATCH 15/29] qemu-option: parse_option_bool(): use error_set() Note that set_option_parameter() callers still expect automatic error reporting with QError, so set_option_parameter() calls qerror_report_err() to keep the same semantics. Signed-off-by: Luiz Capitulino Reviewed-By: Laszlo Ersek --- qemu-option.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/qemu-option.c b/qemu-option.c index 72dcb566d8..b5da116b1f 100644 --- a/qemu-option.c +++ b/qemu-option.c @@ -169,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")) { @@ -177,13 +178,11 @@ 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 void parse_option_number(const char *name, const char *value, @@ -263,6 +262,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 +274,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: @@ -298,6 +299,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; } @@ -588,7 +595,8 @@ static int qemu_opt_parse(QemuOpt *opt) /* nothing */ return 0; 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, &local_err); + break; case QEMU_OPT_NUMBER: parse_option_number(opt->name, opt->str, &opt->value.uint, &local_err); From ec7b2ccb4a6366784ff2c793b43b4c0940909f44 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Wed, 21 Mar 2012 15:03:45 -0300 Subject: [PATCH 16/29] qemu-option: parse_option_size(): use error_set() Signed-off-by: Luiz Capitulino Reviewed-By: Laszlo Ersek --- qemu-option.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/qemu-option.c b/qemu-option.c index b5da116b1f..42bb6854af 100644 --- a/qemu-option.c +++ b/qemu-option.c @@ -203,7 +203,8 @@ static void parse_option_number(const char *name, const char *value, } } -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; @@ -229,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; } /* @@ -290,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: @@ -602,7 +600,8 @@ static int qemu_opt_parse(QemuOpt *opt) &local_err); 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, &local_err); + break; default: abort(); } From 6c5194046a34ff9f84e294d4ef926ec509741493 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Wed, 21 Mar 2012 15:21:26 -0300 Subject: [PATCH 17/29] qemu-option: qemu_opt_parse(): use error_set() The functions opt_set() and qemu_opts_validate() both call qemu_opt_parse(), but their callers expect QError semantics. Thus, both functions call qerro_report_err() to keep the expected semantics. Signed-off-by: Luiz Capitulino Reviewed-By: Laszlo Ersek --- qemu-option.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/qemu-option.c b/qemu-option.c index 42bb6854af..6d3697003b 100644 --- a/qemu-option.c +++ b/qemu-option.c @@ -581,38 +581,27 @@ 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) { - Error *local_err = NULL; - if (opt->desc == NULL) - return 0; + return; switch (opt->desc->type) { case QEMU_OPT_STRING: /* nothing */ - return 0; + return; case QEMU_OPT_BOOL: - parse_option_bool(opt->name, opt->str, &opt->value.boolean, &local_err); + parse_option_bool(opt->name, opt->str, &opt->value.boolean, errp); break; case QEMU_OPT_NUMBER: - parse_option_number(opt->name, opt->str, &opt->value.uint, - &local_err); + parse_option_number(opt->name, opt->str, &opt->value.uint, errp); break; case QEMU_OPT_SIZE: - parse_option_size(opt->name, opt->str, &opt->value.uint, &local_err); + parse_option_size(opt->name, opt->str, &opt->value.uint, errp); break; default: abort(); } - - if (error_is_set(&local_err)) { - qerror_report_err(local_err); - error_free(local_err); - return -1; - } - - return 0; } static void qemu_opt_del(QemuOpt *opt) @@ -628,6 +617,7 @@ static int opt_set(QemuOpts *opts, const char *name, const char *value, { QemuOpt *opt; const QemuOptDesc *desc = opts->list->desc; + Error *local_err = NULL; int i; for (i = 0; desc[i].name != NULL; i++) { @@ -658,10 +648,14 @@ 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)) { + qerror_report_err(local_err); + error_free(local_err); qemu_opt_del(opt); return -1; } + return 0; } @@ -1050,6 +1044,7 @@ QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict) int qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc) { QemuOpt *opt; + Error *local_err = NULL; assert(opts->list->desc[0].name == NULL); @@ -1068,7 +1063,10 @@ int qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc) opt->desc = &desc[i]; - if (qemu_opt_parse(opt) < 0) { + qemu_opt_parse(opt, &local_err); + if (error_is_set(&local_err)) { + qerror_report_err(local_err); + error_free(local_err); return -1; } } From 299528668c759f40b2cc78914e2ca2c832f82834 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Thu, 12 Apr 2012 11:58:57 -0300 Subject: [PATCH 18/29] qemu-option: qemu_opts_validate(): use error_set() net_client_init() propagates the error up by calling qerror_report_err(), because its users expect QError semantics. Signed-off-by: Luiz Capitulino Reviewed-By: Laszlo Ersek --- net.c | 6 +++++- qemu-option.c | 13 +++++-------- qemu-option.h | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/net.c b/net.c index 1922d8abd1..f5d9cc75b8 100644 --- a/net.c +++ b/net.c @@ -1136,10 +1136,14 @@ 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)) { + qerror_report_err(local_err); + error_free(local_err); return -1; } diff --git a/qemu-option.c b/qemu-option.c index 6d3697003b..eee3b45d23 100644 --- a/qemu-option.c +++ b/qemu-option.c @@ -1041,7 +1041,7 @@ 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; @@ -1057,21 +1057,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]; qemu_opt_parse(opt, &local_err); if (error_is_set(&local_err)) { - qerror_report_err(local_err); - error_free(local_err); - return -1; + 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 4d5b3d3f42..e9fbbb56b6 100644 --- a/qemu-option.h +++ b/qemu-option.h @@ -125,7 +125,7 @@ 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, From 584d4064c6de5c9f2f555d7afebbbde59c741b8f Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Wed, 21 Mar 2012 16:13:24 -0300 Subject: [PATCH 19/29] qemu-option: opt_set(): use error_set() The functions qemu_opt_set() and opts_do_parse() both call opt_set(), but their callers expect QError semantics. Thus, both functions call qerro_report_err() to keep the expected semantics. Signed-off-by: Luiz Capitulino Reviewed-By: Laszlo Ersek --- qemu-option.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/qemu-option.c b/qemu-option.c index eee3b45d23..7af2b50a43 100644 --- a/qemu-option.c +++ b/qemu-option.c @@ -612,8 +612,8 @@ 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; @@ -629,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; } } @@ -650,18 +650,23 @@ static int opt_set(QemuOpts *opts, const char *name, const char *value, } qemu_opt_parse(opt, &local_err); if (error_is_set(&local_err)) { - qerror_report_err(local_err); - error_free(local_err); + error_propagate(errp, local_err); qemu_opt_del(opt); - return -1; } - - return 0; } int qemu_opt_set(QemuOpts *opts, const char *name, const char *value) { - return opt_set(opts, name, value, false); + Error *local_err = NULL; + + opt_set(opts, name, value, false, &local_err); + if (error_is_set(&local_err)) { + qerror_report_err(local_err); + error_free(local_err); + return -1; + } + + return 0; } int qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val) @@ -847,6 +852,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, '='); @@ -878,7 +884,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; } } From 384f2139ff681cd3c26de365417cff46c186b5f0 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Wed, 21 Mar 2012 16:17:21 -0300 Subject: [PATCH 20/29] qemu-option: introduce qemu_opt_set_err() This is like qemu_opt_set(), except that it takes an Error argument. This new function allows for a incremental conversion of code using qemu_opt_set(). Signed-off-by: Luiz Capitulino Reviewed-By: Laszlo Ersek --- qemu-option.c | 6 ++++++ qemu-option.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/qemu-option.c b/qemu-option.c index 7af2b50a43..1cddb9d545 100644 --- a/qemu-option.c +++ b/qemu-option.c @@ -669,6 +669,12 @@ int qemu_opt_set(QemuOpts *opts, const char *name, const char *value) return 0; } +void qemu_opt_set_err(QemuOpts *opts, const char *name, const char *value, + Error **errp) +{ + opt_set(opts, name, value, false, errp); +} + int qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val) { QemuOpt *opt; diff --git a/qemu-option.h b/qemu-option.h index e9fbbb56b6..c0e022bf40 100644 --- a/qemu-option.h +++ b/qemu-option.h @@ -111,6 +111,8 @@ 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, From 4e89978e2021c0431aad9898823eb9d3526e9018 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Wed, 18 Apr 2012 17:24:01 -0300 Subject: [PATCH 21/29] qemu-option: qemu_opts_from_qdict(): use error_set() do_device_add() and do_netdev_add() call qerror_report_err() to maintain their QError semantics. Signed-off-by: Luiz Capitulino Reviewed-By: Laszlo Ersek --- hw/qdev-monitor.c | 7 +++++-- net.c | 5 ++++- qemu-option.c | 31 ++++++++++++++++++++++++------- qemu-option.h | 3 ++- 4 files changed, 35 insertions(+), 11 deletions(-) diff --git a/hw/qdev-monitor.c b/hw/qdev-monitor.c index eed781d2f0..b01ef0600e 100644 --- a/hw/qdev-monitor.c +++ b/hw/qdev-monitor.c @@ -554,10 +554,13 @@ void do_info_qdm(Monitor *mon) int do_device_add(Monitor *mon, const QDict *qdict, QObject **ret_data) { + Error *local_err = NULL; QemuOpts *opts; - opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict); - if (!opts) { + opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict, &local_err); + if (error_is_set(&local_err)) { + qerror_report_err(local_err); + error_free(local_err); return -1; } if (!monitor_cur_is_qmp() && qdev_device_help(opts)) { diff --git a/net.c b/net.c index f5d9cc75b8..246209f690 100644 --- a/net.c +++ b/net.c @@ -1237,11 +1237,14 @@ void net_host_device_remove(Monitor *mon, const QDict *qdict) int do_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret_data) { + Error *local_err = NULL; QemuOpts *opts; int res; - opts = qemu_opts_from_qdict(qemu_find_opts("netdev"), qdict); + opts = qemu_opts_from_qdict(qemu_find_opts("netdev"), qdict, &local_err); if (!opts) { + qerror_report_err(local_err); + error_free(local_err); return -1; } diff --git a/qemu-option.c b/qemu-option.c index 1cddb9d545..bb3886c6b9 100644 --- a/qemu-option.c +++ b/qemu-option.c @@ -968,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; } @@ -1002,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); } /* @@ -1011,21 +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) { - QemuOpts *opts; + OptsFromQDictState state; Error *local_err = NULL; + QemuOpts *opts; opts = qemu_opts_create(list, qdict_get_try_str(qdict, "id"), 1, &local_err); if (error_is_set(&local_err)) { - qerror_report_err(local_err); - error_free(local_err); + error_propagate(errp, local_err); return NULL; } assert(opts != NULL); - qdict_iter(qdict, qemu_opts_from_qdict_1, opts); + + 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; + } + return opts; } diff --git a/qemu-option.h b/qemu-option.h index c0e022bf40..951dec3cc4 100644 --- a/qemu-option.h +++ b/qemu-option.h @@ -132,7 +132,8 @@ 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); From 8ff15d4a2dec25167013d222f1c4aaef8595e02e Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Wed, 28 Mar 2012 14:01:15 -0300 Subject: [PATCH 22/29] qerror: introduce QERR_INVALID_OPTION_GROUP Signed-off-by: Luiz Capitulino Reviewed-By: Laszlo Ersek --- qerror.c | 4 ++++ qerror.h | 3 +++ 2 files changed, 7 insertions(+) diff --git a/qerror.c b/qerror.c index 5092fe7549..92c4eff179 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 4cbba48c5a..b4c8758f40 100644 --- a/qerror.h +++ b/qerror.h @@ -139,6 +139,9 @@ QError *qobject_to_qerror(const QObject *obj); #define QERR_INVALID_BLOCK_FORMAT \ "{ 'class': 'InvalidBlockFormat', 'data': { 'name': %s } }" +#define QERR_INVALID_OPTION_GROUP \ + "{ 'class': 'InvalidOptionGroup', 'data': { 'group': %s } }" + #define QERR_INVALID_PARAMETER \ "{ 'class': 'InvalidParameter', 'data': { 'name': %s } }" From 2ac20613384c1c66bc27302e1e38698fa238bb2f Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Wed, 28 Mar 2012 14:14:17 -0300 Subject: [PATCH 23/29] qemu-config: find_list(): use error_set() Note that qemu_find_opts() and qemu_config_parse() need to call error_report() to maintain their semantics on error. Signed-off-by: Luiz Capitulino Reviewed-By: Laszlo Ersek --- qemu-config.c | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/qemu-config.c b/qemu-config.c index f8766468bb..bdb381da4e 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,23 @@ 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; } void qemu_add_opts(QemuOptsList *list) @@ -762,6 +773,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,17 +790,23 @@ 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, 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, NULL); continue; } From 60d5666f7d4eec6df4a3d066769009f0c1a685bf Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Wed, 28 Mar 2012 14:16:37 -0300 Subject: [PATCH 24/29] qemu-config: introduce qemu_find_opts_err() This is like qemu_find_opts(), except that it takes an Error argument. This new function allows for a incremental conversion of code using qemu_find_opts(). Signed-off-by: Luiz Capitulino Reviewed-By: Laszlo Ersek --- qemu-config.c | 5 +++++ qemu-config.h | 3 +++ 2 files changed, 8 insertions(+) diff --git a/qemu-config.c b/qemu-config.c index bdb381da4e..bb3bff426a 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -661,6 +661,11 @@ QemuOptsList *qemu_find_opts(const char *group) 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) { int entries, i; diff --git a/qemu-config.h b/qemu-config.h index 6d7365d35b..e9f2ef4c7b 100644 --- a/qemu-config.h +++ b/qemu-config.h @@ -1,11 +1,14 @@ #ifndef QEMU_CONFIG_H #define QEMU_CONFIG_H +#include "error.h" + extern QemuOptsList qemu_fsdev_opts; extern QemuOptsList qemu_virtfs_opts; extern QemuOptsList qemu_spice_opts; QemuOptsList *qemu_find_opts(const char *group); +QemuOptsList *qemu_find_opts_err(const char *group, Error **errp); void qemu_add_opts(QemuOptsList *list); int qemu_set_option(const char *str); int qemu_global_option(const char *str); From 42dcc547e1982f31d0f678e02f0aae5be570384a Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Thu, 12 Apr 2012 13:20:40 -0300 Subject: [PATCH 25/29] net: purge the monitor object from all init functions The only backend that really uses it is the socket one, which calls monitor_get_fd(). But it can use 'cur_mon' instead. Signed-off-by: Luiz Capitulino Reviewed-By: Laszlo Ersek --- hw/pci-hotplug.c | 2 +- hw/usb/dev-network.c | 2 +- net.c | 18 +++++++----------- net.h | 2 +- net/dump.c | 2 +- net/dump.h | 3 +-- net/slirp.c | 5 +---- net/slirp.h | 5 +---- net/socket.c | 8 +++----- net/socket.h | 3 +-- net/tap-win32.c | 2 +- net/tap.c | 9 ++++----- net/tap.h | 5 ++--- net/vde.c | 2 +- net/vde.h | 2 +- 15 files changed, 27 insertions(+), 43 deletions(-) diff --git a/hw/pci-hotplug.c b/hw/pci-hotplug.c index c55d8b9396..785eb3d889 100644 --- a/hw/pci-hotplug.c +++ b/hw/pci-hotplug.c @@ -60,7 +60,7 @@ static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon, qemu_opt_set(opts, "type", "nic"); - ret = net_client_init(mon, opts, 0); + ret = net_client_init(opts, 0); if (ret < 0) return NULL; if (nd_table[ret].devaddr) { diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index b238a0973d..4e26e64d63 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -1367,7 +1367,7 @@ 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); + idx = net_client_init(opts, 0); if (idx == -1) { return NULL; } diff --git a/net.c b/net.c index 246209f690..adb7e20899 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) { const char *name; const char *type; @@ -1156,7 +1152,7 @@ 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); @@ -1213,7 +1209,7 @@ void net_host_device_add(Monitor *mon, const QDict *qdict) qemu_opt_set(opts, "type", device); - if (net_client_init(mon, opts, 0) < 0) { + if (net_client_init(opts, 0) < 0) { monitor_printf(mon, "adding host network device %s failed\n", device); } } @@ -1248,7 +1244,7 @@ int do_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret_data) return -1; } - res = net_client_init(mon, opts, 1); + res = net_client_init(opts, 1); if (res < 0) { qemu_opts_del(opts); } @@ -1431,14 +1427,14 @@ void net_check_clients(void) static int net_init_client(QemuOpts *opts, void *dummy) { - if (net_client_init(NULL, opts, 0) < 0) + if (net_client_init(opts, 0) < 0) return -1; return 0; } static int net_init_netdev(QemuOpts *opts, void *dummy) { - return net_client_init(NULL, opts, 1); + return net_client_init(opts, 1); } int net_init_clients(void) diff --git a/net.h b/net.h index 64993b43ef..9d1ed93b69 100644 --- a/net.h +++ b/net.h @@ -163,7 +163,7 @@ 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); int net_client_parse(QemuOptsList *opts_list, const char *str); int net_init_clients(void); void net_check_clients(void); diff --git a/net/dump.c b/net/dump.c index 4b48d48408..f835c51187 100644 --- a/net/dump.c +++ b/net/dump.c @@ -144,7 +144,7 @@ static int net_dump_init(VLANState *vlan, const char *device, return 0; } -int net_init_dump(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan) +int net_init_dump(QemuOpts *opts, const char *name, VLANState *vlan) { int len; const char *file; diff --git a/net/dump.h b/net/dump.h index fdc91ad6dc..2b5d9ba644 100644 --- a/net/dump.h +++ b/net/dump.h @@ -27,7 +27,6 @@ #include "net.h" #include "qemu-common.h" -int net_init_dump(QemuOpts *opts, Monitor *mon, - const char *name, VLANState *vlan); +int net_init_dump(QemuOpts *opts, const char *name, VLANState *vlan); #endif /* QEMU_NET_DUMP_H */ diff --git a/net/slirp.c b/net/slirp.c index 96f5032c4c..37b6ccfde9 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -676,10 +676,7 @@ static int net_init_slirp_configs(const char *name, const char *value, void *opa return 0; } -int net_init_slirp(QemuOpts *opts, - Monitor *mon, - const char *name, - VLANState *vlan) +int net_init_slirp(QemuOpts *opts, const char *name, VLANState *vlan) { struct slirp_config_str *config; const char *vhost; diff --git a/net/slirp.h b/net/slirp.h index c17de8ed47..53fe95dc12 100644 --- a/net/slirp.h +++ b/net/slirp.h @@ -30,10 +30,7 @@ #ifdef CONFIG_SLIRP -int net_init_slirp(QemuOpts *opts, - Monitor *mon, - const char *name, - VLANState *vlan); +int net_init_slirp(QemuOpts *opts, const char *name, VLANState *vlan); void net_slirp_hostfwd_add(Monitor *mon, const QDict *qdict); void net_slirp_hostfwd_remove(Monitor *mon, const QDict *qdict); diff --git a/net/socket.c b/net/socket.c index 0bcf229c24..fcd0a3c162 100644 --- a/net/socket.c +++ b/net/socket.c @@ -26,6 +26,7 @@ #include "config-host.h" #include "net.h" +#include "monitor.h" #include "qemu-char.h" #include "qemu-common.h" #include "qemu-error.h" @@ -585,10 +586,7 @@ static int net_socket_udp_init(VLANState *vlan, return 0; } -int net_init_socket(QemuOpts *opts, - Monitor *mon, - const char *name, - VLANState *vlan) +int net_init_socket(QemuOpts *opts, const char *name, VLANState *vlan) { if (qemu_opt_get(opts, "fd")) { int fd; @@ -601,7 +599,7 @@ int net_init_socket(QemuOpts *opts, return -1; } - fd = net_handle_fd_param(mon, qemu_opt_get(opts, "fd")); + fd = net_handle_fd_param(cur_mon, qemu_opt_get(opts, "fd")); if (fd == -1) { return -1; } diff --git a/net/socket.h b/net/socket.h index ea46f02dd8..e1fe959412 100644 --- a/net/socket.h +++ b/net/socket.h @@ -27,7 +27,6 @@ #include "net.h" #include "qemu-common.h" -int net_init_socket(QemuOpts *opts, Monitor *mon, - const char *name, VLANState *vlan); +int net_init_socket(QemuOpts *opts, const char *name, VLANState *vlan); #endif /* QEMU_NET_SOCKET_H */ diff --git a/net/tap-win32.c b/net/tap-win32.c index 596132e272..a801a553c4 100644 --- a/net/tap-win32.c +++ b/net/tap-win32.c @@ -699,7 +699,7 @@ static int tap_win32_init(VLANState *vlan, const char *model, return 0; } -int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan) +int net_init_tap(QemuOpts *opts, const char *name, VLANState *vlan) { const char *ifname; diff --git a/net/tap.c b/net/tap.c index f2400283c3..5ac4ba3343 100644 --- a/net/tap.c +++ b/net/tap.c @@ -512,8 +512,7 @@ static int net_bridge_run_helper(const char *helper, const char *bridge) return -1; } -int net_init_bridge(QemuOpts *opts, Monitor *mon, const char *name, - VLANState *vlan) +int net_init_bridge(QemuOpts *opts, const char *name, VLANState *vlan) { TAPState *s; int fd, vnet_hdr; @@ -583,7 +582,7 @@ static int net_tap_init(QemuOpts *opts, int *vnet_hdr) return fd; } -int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan) +int net_init_tap(QemuOpts *opts, const char *name, VLANState *vlan) { TAPState *s; int fd, vnet_hdr = 0; @@ -600,7 +599,7 @@ int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan return -1; } - fd = net_handle_fd_param(mon, qemu_opt_get(opts, "fd")); + fd = net_handle_fd_param(cur_mon, qemu_opt_get(opts, "fd")); if (fd == -1) { return -1; } @@ -687,7 +686,7 @@ int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan int vhostfd, r; bool force = qemu_opt_get_bool(opts, "vhostforce", false); if (qemu_opt_get(opts, "vhostfd")) { - r = net_handle_fd_param(mon, qemu_opt_get(opts, "vhostfd")); + r = net_handle_fd_param(cur_mon, qemu_opt_get(opts, "vhostfd")); if (r == -1) { return -1; } diff --git a/net/tap.h b/net/tap.h index 56c591fc8e..b2a9450aab 100644 --- a/net/tap.h +++ b/net/tap.h @@ -32,7 +32,7 @@ #define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup" #define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown" -int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan); +int net_init_tap(QemuOpts *opts, const char *name, VLANState *vlan); int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required); @@ -57,7 +57,6 @@ int tap_get_fd(VLANClientState *vc); struct vhost_net; struct vhost_net *tap_get_vhost_net(VLANClientState *vc); -int net_init_bridge(QemuOpts *opts, Monitor *mon, const char *name, - VLANState *vlan); +int net_init_bridge(QemuOpts *opts, const char *name, VLANState *vlan); #endif /* QEMU_NET_TAP_H */ diff --git a/net/vde.c b/net/vde.c index ac48ab2f0a..6b9d45294a 100644 --- a/net/vde.c +++ b/net/vde.c @@ -110,7 +110,7 @@ static int net_vde_init(VLANState *vlan, const char *model, return 0; } -int net_init_vde(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan) +int net_init_vde(QemuOpts *opts, const char *name, VLANState *vlan) { const char *sock; const char *group; diff --git a/net/vde.h b/net/vde.h index 3e6ca3e876..732e5756f6 100644 --- a/net/vde.h +++ b/net/vde.h @@ -29,7 +29,7 @@ #ifdef CONFIG_VDE -int net_init_vde(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan); +int net_init_vde(QemuOpts *opts, const char *name, VLANState *vlan); #endif /* CONFIG_VDE */ From 4559a1dbcc15a10482de822e2839645a1819ef6f Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Fri, 20 Apr 2012 16:50:25 -0300 Subject: [PATCH 26/29] net: net_client_init(): use error_set() Callers are changed to use qerror_report_err() to keep their QError semantics. Signed-off-by: Luiz Capitulino Reviewed-By: Laszlo Ersek --- hw/pci-hotplug.c | 8 +++++-- hw/usb/dev-network.c | 7 ++++-- net.c | 54 +++++++++++++++++++++++++++++++------------- net.h | 2 +- 4 files changed, 50 insertions(+), 21 deletions(-) diff --git a/hw/pci-hotplug.c b/hw/pci-hotplug.c index 785eb3d889..61257f457b 100644 --- a/hw/pci-hotplug.c +++ b/hw/pci-hotplug.c @@ -39,6 +39,7 @@ static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon, const char *devaddr, const char *opts_str) { + Error *local_err = NULL; QemuOpts *opts; PCIBus *bus; int ret, devfn; @@ -60,9 +61,12 @@ static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon, qemu_opt_set(opts, "type", "nic"); - ret = net_client_init(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/usb/dev-network.c b/hw/usb/dev-network.c index 4e26e64d63..5d2f0982c9 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -1356,6 +1356,7 @@ static int usb_net_initfn(USBDevice *dev) static USBDevice *usb_net_init(USBBus *bus, const char *cmdline) { + Error *local_err = NULL; USBDevice *dev; QemuOpts *opts; int idx; @@ -1367,8 +1368,10 @@ static USBDevice *usb_net_init(USBBus *bus, const char *cmdline) qemu_opt_set(opts, "type", "nic"); qemu_opt_set(opts, "model", "usb"); - idx = net_client_init(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/net.c b/net.c index adb7e20899..8f7cb40f79 100644 --- a/net.c +++ b/net.c @@ -1081,7 +1081,7 @@ static const struct { #endif /* CONFIG_NET_BRIDGE */ }; -int net_client_init(QemuOpts *opts, int is_netdev) +int net_client_init(QemuOpts *opts, int is_netdev, Error **errp) { const char *name; const char *type; @@ -1089,7 +1089,7 @@ int net_client_init(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; } @@ -1105,21 +1105,21 @@ int net_client_init(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; } } @@ -1138,8 +1138,7 @@ int net_client_init(QemuOpts *opts, int is_netdev) qemu_opts_validate(opts, &net_client_types[i].desc[0], &local_err); if (error_is_set(&local_err)) { - qerror_report_err(local_err); - error_free(local_err); + error_propagate(errp, local_err); return -1; } @@ -1155,7 +1154,7 @@ int net_client_init(QemuOpts *opts, int is_netdev) 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(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(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); } } @@ -1244,8 +1247,10 @@ int do_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret_data) return -1; } - res = net_client_init(opts, 1); + res = net_client_init(opts, 1, &local_err); if (res < 0) { + qerror_report_err(local_err); + error_free(local_err); qemu_opts_del(opts); } @@ -1427,14 +1432,31 @@ void net_check_clients(void) static int net_init_client(QemuOpts *opts, void *dummy) { - if (net_client_init(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(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 9d1ed93b69..7ee97e9fe2 100644 --- a/net.h +++ b/net.h @@ -163,7 +163,7 @@ struct HCIInfo *qemu_next_hci(void); extern const char *legacy_tftp_prefix; extern const char *legacy_bootp_filename; -int net_client_init(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); From 928059a37b8e1f152962431fb846f39597b63c36 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Wed, 18 Apr 2012 17:34:15 -0300 Subject: [PATCH 27/29] qapi: convert netdev_add This is not a full QAPI conversion, but an intermediate step. In essence, do_netdev_add() is split into three functions: 1. netdev_add(): performs the actual work. This function is fully converted to Error (thus, it's "qapi-friendly") 2. qmp_netdev_add(): the QMP front-end for netdev_add(). This is coded by hand and not auto-generated (gen=no in the schema). The reason for this it's a lot easier and simpler to with QemuOpts this way 3. hmp_netdev_add(): HMP front-end. This design was suggested by Paolo Bonzini. Signed-off-by: Luiz Capitulino Reviewed-By: Laszlo Ersek --- hmp-commands.hx | 3 +-- hmp.c | 21 +++++++++++++++++++++ hmp.h | 1 + net.c | 36 ++++++++++++++++++++++++------------ net.h | 3 ++- qapi-schema.json | 28 ++++++++++++++++++++++++++++ qmp-commands.hx | 5 +---- 7 files changed, 78 insertions(+), 19 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 81723c87cc..d0ce6a5970 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1037,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 diff --git a/hmp.c b/hmp.c index 42ced2a202..7a4e25f30c 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" @@ -969,3 +971,22 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict) &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); +} diff --git a/hmp.h b/hmp.h index 5cf3241af0..017df87623 100644 --- a/hmp.h +++ b/hmp.h @@ -62,5 +62,6 @@ 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); #endif diff --git a/net.c b/net.c index 8f7cb40f79..5f0c53cf72 100644 --- a/net.c +++ b/net.c @@ -1234,27 +1234,39 @@ 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, &local_err); - if (!opts) { - qerror_report_err(local_err); - error_free(local_err); - return -1; + opts_list = qemu_find_opts_err("netdev", &local_err); + if (error_is_set(&local_err)) { + goto exit_err; } - res = net_client_init(opts, 1, &local_err); - if (res < 0) { - qerror_report_err(local_err); - error_free(local_err); + opts = qemu_opts_from_qdict(opts_list, qdict, &local_err); + if (error_is_set(&local_err)) { + goto exit_err; + } + + netdev_add(opts, &local_err); + if (error_is_set(&local_err)) { qemu_opts_del(opts); + goto exit_err; } - return 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) diff --git a/net.h b/net.h index 7ee97e9fe2..1eb92800ec 100644 --- a/net.h +++ b/net.h @@ -170,7 +170,8 @@ 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); +void netdev_add(QemuOpts *opts, Error **errp); +int qmp_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret); int do_netdev_del(Monitor *mon, const QDict *qdict, QObject **ret_data); #define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup" diff --git a/qapi-schema.json b/qapi-schema.json index dfa0e1f47a..69fcd8e3e6 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -1798,3 +1798,31 @@ { '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' } diff --git a/qmp-commands.hx b/qmp-commands.hx index 2aa64add35..f6550cb9bd 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -642,10 +642,7 @@ 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 From 5f96415527e4f25dc83cfc458c46e5bcbfee79ba Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Mon, 16 Apr 2012 14:36:32 -0300 Subject: [PATCH 28/29] qapi: convert netdev_del Signed-off-by: Anthony Liguori Signed-off-by: Luiz Capitulino Reviewed-By: Laszlo Ersek --- hmp-commands.hx | 3 +-- hmp.c | 9 +++++++++ hmp.h | 1 + net.c | 11 +++++------ net.h | 1 - qapi-schema.json | 14 ++++++++++++++ qmp-commands.hx | 5 +---- 7 files changed, 31 insertions(+), 13 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index d0ce6a5970..f5d9d91de8 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1051,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 7a4e25f30c..2ce8cb9df1 100644 --- a/hmp.c +++ b/hmp.c @@ -990,3 +990,12 @@ void hmp_netdev_add(Monitor *mon, const QDict *qdict) 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 017df87623..79d138d3ee 100644 --- a/hmp.h +++ b/hmp.h @@ -63,5 +63,6 @@ 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/net.c b/net.c index 5f0c53cf72..4aa416cffb 100644 --- a/net.c +++ b/net.c @@ -1269,19 +1269,18 @@ exit_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) diff --git a/net.h b/net.h index 1eb92800ec..bdc2a0602d 100644 --- a/net.h +++ b/net.h @@ -172,7 +172,6 @@ void net_host_device_add(Monitor *mon, const QDict *qdict); void net_host_device_remove(Monitor *mon, const QDict *qdict); void netdev_add(QemuOpts *opts, Error **errp); int qmp_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret); -int do_netdev_del(Monitor *mon, const QDict *qdict, QObject **ret_data); #define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup" #define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown" diff --git a/qapi-schema.json b/qapi-schema.json index 69fcd8e3e6..bb1f806532 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -1826,3 +1826,17 @@ { '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/qmp-commands.hx b/qmp-commands.hx index f6550cb9bd..57ea803b14 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -671,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 From 4860853d60ecea44b65e9cdefce980de3a641dce Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Mon, 21 May 2012 17:59:51 +0100 Subject: [PATCH 29/29] Add 'query-events' command to QMP to query async events Sometimes it is neccessary for an application to determine whether a particular QMP event is available, so they can decide whether to use compatibility code instead. This introduces a new 'query-events' command to QMP to do just that { "execute": "query-events" } {"return": [{"name": "WAKEUP"}, {"name": "SUSPEND"}, {"name": "DEVICE_TRAY_MOVED"}, {"name": "BLOCK_JOB_CANCELLED"}, {"name": "BLOCK_JOB_COMPLETED"}, ...snip... {"name": "SHUTDOWN"}]} * monitor.c: Turn MonitorEvent -> string conversion into a lookup from a static table of constant strings. Add impl of qmp_query_events monitor command handler * qapi-schema.json, qmp-commands.hx: Define contract of query-events command Signed-off-by: Daniel P. Berrange Signed-off-by: Luiz Capitulino --- monitor.c | 107 ++++++++++++++++++++--------------------------- monitor.h | 4 ++ qapi-schema.json | 22 ++++++++++ qmp-commands.hx | 37 ++++++++++++++++ 4 files changed, 108 insertions(+), 62 deletions(-) diff --git a/monitor.c b/monitor.c index 12a6fe25ad..a3bc2c7253 100644 --- a/monitor.c +++ b/monitor.c @@ -422,6 +422,30 @@ static void timestamp_put(QDict *qdict) qdict_put_obj(qdict, "timestamp", obj); } + +static const char *monitor_event_names[] = { + [QEVENT_SHUTDOWN] = "SHUTDOWN", + [QEVENT_RESET] = "RESET", + [QEVENT_POWERDOWN] = "POWERDOWN", + [QEVENT_STOP] = "STOP", + [QEVENT_RESUME] = "RESUME", + [QEVENT_VNC_CONNECTED] = "VNC_CONNECTED", + [QEVENT_VNC_INITIALIZED] = "VNC_INITIALIZED", + [QEVENT_VNC_DISCONNECTED] = "VNC_DISCONNECTED", + [QEVENT_BLOCK_IO_ERROR] = "BLOCK_IO_ERROR", + [QEVENT_RTC_CHANGE] = "RTC_CHANGE", + [QEVENT_WATCHDOG] = "WATCHDOG", + [QEVENT_SPICE_CONNECTED] = "SPICE_CONNECTED", + [QEVENT_SPICE_INITIALIZED] = "SPICE_INITIALIZED", + [QEVENT_SPICE_DISCONNECTED] = "SPICE_DISCONNECTED", + [QEVENT_BLOCK_JOB_COMPLETED] = "BLOCK_JOB_COMPLETED", + [QEVENT_BLOCK_JOB_CANCELLED] = "BLOCK_JOB_CANCELLED", + [QEVENT_DEVICE_TRAY_MOVED] = "DEVICE_TRAY_MOVED", + [QEVENT_SUSPEND] = "SUSPEND", + [QEVENT_WAKEUP] = "WAKEUP", +}; +QEMU_BUILD_BUG_ON(ARRAY_SIZE(monitor_event_names) != QEVENT_MAX) + /** * monitor_protocol_event(): Generate a Monitor event * @@ -435,68 +459,8 @@ void monitor_protocol_event(MonitorEvent event, QObject *data) assert(event < QEVENT_MAX); - switch (event) { - case QEVENT_SHUTDOWN: - event_name = "SHUTDOWN"; - break; - case QEVENT_RESET: - event_name = "RESET"; - break; - case QEVENT_POWERDOWN: - event_name = "POWERDOWN"; - break; - case QEVENT_STOP: - event_name = "STOP"; - break; - case QEVENT_RESUME: - event_name = "RESUME"; - break; - case QEVENT_VNC_CONNECTED: - event_name = "VNC_CONNECTED"; - break; - case QEVENT_VNC_INITIALIZED: - event_name = "VNC_INITIALIZED"; - break; - case QEVENT_VNC_DISCONNECTED: - event_name = "VNC_DISCONNECTED"; - break; - case QEVENT_BLOCK_IO_ERROR: - event_name = "BLOCK_IO_ERROR"; - break; - case QEVENT_RTC_CHANGE: - event_name = "RTC_CHANGE"; - break; - case QEVENT_WATCHDOG: - event_name = "WATCHDOG"; - break; - case QEVENT_SPICE_CONNECTED: - event_name = "SPICE_CONNECTED"; - break; - case QEVENT_SPICE_INITIALIZED: - event_name = "SPICE_INITIALIZED"; - break; - case QEVENT_SPICE_DISCONNECTED: - event_name = "SPICE_DISCONNECTED"; - break; - case QEVENT_BLOCK_JOB_COMPLETED: - event_name = "BLOCK_JOB_COMPLETED"; - break; - case QEVENT_BLOCK_JOB_CANCELLED: - event_name = "BLOCK_JOB_CANCELLED"; - break; - case QEVENT_DEVICE_TRAY_MOVED: - event_name = "DEVICE_TRAY_MOVED"; - break; - case QEVENT_SUSPEND: - event_name = "SUSPEND"; - break; - case QEVENT_WAKEUP: - event_name = "WAKEUP"; - break; - default: - abort(); - break; - } + event_name = monitor_event_names[event]; + assert(event_name != NULL); qmp = qdict_new(); timestamp_put(qmp); @@ -738,6 +702,25 @@ CommandInfoList *qmp_query_commands(Error **errp) return cmd_list; } +EventInfoList *qmp_query_events(Error **errp) +{ + EventInfoList *info, *ev_list = NULL; + MonitorEvent e; + + for (e = 0 ; e < QEVENT_MAX ; e++) { + const char *event_name = monitor_event_names[e]; + assert(event_name != NULL); + info = g_malloc0(sizeof(*info)); + info->value = g_malloc0(sizeof(*info->value)); + info->value->name = g_strdup(event_name); + + info->next = ev_list; + ev_list = info; + } + + return ev_list; +} + /* set the current CPU defined by the user */ int monitor_set_cpu(int cpu_index) { diff --git a/monitor.h b/monitor.h index 0d498006f8..cd1d8786d1 100644 --- a/monitor.h +++ b/monitor.h @@ -41,6 +41,10 @@ typedef enum MonitorEvent { QEVENT_DEVICE_TRAY_MOVED, QEVENT_SUSPEND, QEVENT_WAKEUP, + + /* Add to 'monitor_event_names' array in monitor.c when + * defining new events here */ + QEVENT_MAX, } MonitorEvent; diff --git a/qapi-schema.json b/qapi-schema.json index bb1f806532..3b6e3468b4 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 # diff --git a/qmp-commands.hx b/qmp-commands.hx index 57ea803b14..2e1a38e695 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -1208,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 -------------