diff --git a/include/uc_priv.h b/include/uc_priv.h index a6c86e70..14ed07bd 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -80,6 +80,10 @@ typedef bool (*uc_write_mem_t)(AddressSpace *as, hwaddr addr, typedef bool (*uc_read_mem_t)(AddressSpace *as, hwaddr addr, uint8_t *buf, int len); +typedef MemoryRegion* (*uc_mem_cow_t)(struct uc_struct *uc, + MemoryRegion *current, hwaddr begin, + size_t size); + typedef void (*uc_args_void_t)(void *); typedef void (*uc_args_uc_t)(struct uc_struct *); @@ -102,6 +106,8 @@ typedef void (*uc_mem_unmap_t)(struct uc_struct *, MemoryRegion *mr); typedef MemoryRegion *(*uc_memory_mapping_t)(struct uc_struct *, hwaddr addr); +typedef void (*uc_memory_filter_t)(MemoryRegion *, int32_t); + typedef void (*uc_readonly_mem_t)(MemoryRegion *mr, bool readonly); typedef int (*uc_cpus_init)(struct uc_struct *, const char *); @@ -267,6 +273,7 @@ struct uc_struct { uc_write_mem_t write_mem; uc_read_mem_t read_mem; + uc_mem_cow_t memory_cow; uc_args_void_t release; // release resource when uc_close() uc_args_uc_u64_t set_pc; // set PC for tracecode uc_get_pc_t get_pc; @@ -280,6 +287,7 @@ struct uc_struct { uc_args_uc_ram_size_t memory_map; uc_args_uc_ram_size_ptr_t memory_map_ptr; uc_memory_mapping_t memory_mapping; + uc_memory_filter_t memory_filter_subregions; uc_mem_unmap_t memory_unmap; uc_readonly_mem_t readonly_mem; uc_cpus_init cpus_init; @@ -403,6 +411,7 @@ struct uc_struct { PVOID seh_handle; void *seh_closure; #endif + int32_t snapshot_level; }; // Metadata stub for the variable-size cpu context used with uc_context_*() diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index 3814b8e0..f2030546 100644 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -1345,6 +1345,12 @@ size_t uc_context_size(uc_engine *uc); UNICORN_EXPORT uc_err uc_context_free(uc_context *context); +UNICORN_EXPORT +uc_err uc_snapshot(uc_engine *uc); + +UNICORN_EXPORT +uc_err uc_restore_latest_snapshot(uc_engine *uc); + #ifdef __cplusplus } #endif diff --git a/qemu/aarch64.h b/qemu/aarch64.h index d7349535..91b27ae5 100644 --- a/qemu/aarch64.h +++ b/qemu/aarch64.h @@ -123,6 +123,7 @@ #define memory_map memory_map_aarch64 #define memory_map_io memory_map_io_aarch64 #define memory_map_ptr memory_map_ptr_aarch64 +#define memory_cow memory_cow_aarch64 #define memory_unmap memory_unmap_aarch64 #define memory_free memory_free_aarch64 #define flatview_unref flatview_unref_aarch64 @@ -144,6 +145,7 @@ #define memory_region_del_subregion memory_region_del_subregion_aarch64 #define memory_region_add_subregion_overlap memory_region_add_subregion_overlap_aarch64 #define memory_region_find memory_region_find_aarch64 +#define memory_region_filter_subregions memory_region_filter_subregions_aarch64 #define memory_listener_register memory_listener_register_aarch64 #define memory_listener_unregister memory_listener_unregister_aarch64 #define address_space_remove_listeners address_space_remove_listeners_aarch64 diff --git a/qemu/accel/tcg/cputlb.c b/qemu/accel/tcg/cputlb.c index e4c2d9a3..c120d27b 100644 --- a/qemu/accel/tcg/cputlb.c +++ b/qemu/accel/tcg/cputlb.c @@ -2155,6 +2155,22 @@ store_helper(CPUArchState *env, target_ulong addr, uint64_t val, } } + if (uc->snapshot_level && mr->ram && mr->priority < uc->snapshot_level) { + mr = memory_cow(uc, mr, addr & TARGET_PAGE_MASK, TARGET_PAGE_SIZE); + if (!mr) { + uc->invalid_addr = paddr; + uc->invalid_error = UC_ERR_NOMEM; + cpu_exit(uc->cpu); + return; + } + /* refill tlb after CoW */ + tlb_fill(env_cpu(env), paddr, size, MMU_DATA_STORE, + mmu_idx, retaddr); + index = tlb_index(env, mmu_idx, addr); + entry = tlb_entry(env, mmu_idx, addr); + tlb_addr = tlb_addr_write(entry); + } + /* Handle anything that isn't just a straight memory access. */ if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) { CPUIOTLBEntry *iotlbentry; diff --git a/qemu/arm.h b/qemu/arm.h index 1fe1fc5c..82b34564 100644 --- a/qemu/arm.h +++ b/qemu/arm.h @@ -123,6 +123,7 @@ #define memory_map memory_map_arm #define memory_map_io memory_map_io_arm #define memory_map_ptr memory_map_ptr_arm +#define memory_cow memory_cow_arm #define memory_unmap memory_unmap_arm #define memory_free memory_free_arm #define flatview_unref flatview_unref_arm @@ -144,6 +145,7 @@ #define memory_region_del_subregion memory_region_del_subregion_arm #define memory_region_add_subregion_overlap memory_region_add_subregion_overlap_arm #define memory_region_find memory_region_find_arm +#define memory_region_filter_subregions memory_region_filter_subregions_arm #define memory_listener_register memory_listener_register_arm #define memory_listener_unregister memory_listener_unregister_arm #define address_space_remove_listeners address_space_remove_listeners_arm diff --git a/qemu/include/exec/memory.h b/qemu/include/exec/memory.h index 88da991e..c2661663 100644 --- a/qemu/include/exec/memory.h +++ b/qemu/include/exec/memory.h @@ -687,6 +687,35 @@ void memory_region_add_subregion(MemoryRegion *mr, hwaddr offset, MemoryRegion *subregion); +/** + * memory_region_add_subregion_overlap: Add a subregion to a container + * with overlap. + * + * Adds a subregion at @offset. The subregion may overlap with other + * subregions. Conflicts are resolved by having a higher @priority hide a + * lower @priority. Subregions without priority are taken as @priority 0. + * A region may only be added once as a subregion (unless removed with + * memory_region_del_subregion()); use memory_region_init_alias() if you + * want a region to be a subregion in multiple locations. + * + * @mr: the region to contain the new subregion; must be a container + * initialized with memory_region_init(). + * @offset: the offset relative to @mr where @subregion is added. + * @subregion: the subregion to be added. + * @priority: used for resolving overlaps; highest priority wins. + */ +void memory_region_add_subregion_overlap(MemoryRegion *mr, + hwaddr offset, + MemoryRegion *subregion, + int priority); + +/** + * memory_region_filter_subregions: filter subregios by priority. + * + * remove all subregions beginning by a specified subregion + */ +void memory_region_filter_subregions(MemoryRegion *mr, int32_t level); + /** * memory_region_get_ram_addr: Get the ram address associated with a memory * region diff --git a/qemu/m68k.h b/qemu/m68k.h index d4a7c133..a2958aa0 100644 --- a/qemu/m68k.h +++ b/qemu/m68k.h @@ -123,6 +123,7 @@ #define memory_map memory_map_m68k #define memory_map_io memory_map_io_m68k #define memory_map_ptr memory_map_ptr_m68k +#define memory_cow memory_cow_m68k #define memory_unmap memory_unmap_m68k #define memory_free memory_free_m68k #define flatview_unref flatview_unref_m68k @@ -144,6 +145,7 @@ #define memory_region_del_subregion memory_region_del_subregion_m68k #define memory_region_add_subregion_overlap memory_region_add_subregion_overlap_m68k #define memory_region_find memory_region_find_m68k +#define memory_region_filter_subregions memory_region_filter_subregions_m68k #define memory_listener_register memory_listener_register_m68k #define memory_listener_unregister memory_listener_unregister_m68k #define address_space_remove_listeners address_space_remove_listeners_m68k diff --git a/qemu/mips.h b/qemu/mips.h index 44d22db0..496bf5b2 100644 --- a/qemu/mips.h +++ b/qemu/mips.h @@ -123,6 +123,7 @@ #define memory_map memory_map_mips #define memory_map_io memory_map_io_mips #define memory_map_ptr memory_map_ptr_mips +#define memory_cow memory_cow_mips #define memory_unmap memory_unmap_mips #define memory_free memory_free_mips #define flatview_unref flatview_unref_mips @@ -144,6 +145,7 @@ #define memory_region_del_subregion memory_region_del_subregion_mips #define memory_region_add_subregion_overlap memory_region_add_subregion_overlap_mips #define memory_region_find memory_region_find_mips +#define memory_region_filter_subregions memory_region_filter_subregions_mips #define memory_listener_register memory_listener_register_mips #define memory_listener_unregister memory_listener_unregister_mips #define address_space_remove_listeners address_space_remove_listeners_mips diff --git a/qemu/mips64.h b/qemu/mips64.h index 759f4552..84bfef10 100644 --- a/qemu/mips64.h +++ b/qemu/mips64.h @@ -123,6 +123,7 @@ #define memory_map memory_map_mips64 #define memory_map_io memory_map_io_mips64 #define memory_map_ptr memory_map_ptr_mips64 +#define memory_cow memory_cow_mips64 #define memory_unmap memory_unmap_mips64 #define memory_free memory_free_mips64 #define flatview_unref flatview_unref_mips64 @@ -144,6 +145,7 @@ #define memory_region_del_subregion memory_region_del_subregion_mips64 #define memory_region_add_subregion_overlap memory_region_add_subregion_overlap_mips64 #define memory_region_find memory_region_find_mips64 +#define memory_region_filter_subregions memory_region_filter_subregions_mips64 #define memory_listener_register memory_listener_register_mips64 #define memory_listener_unregister memory_listener_unregister_mips64 #define address_space_remove_listeners address_space_remove_listeners_mips64 diff --git a/qemu/mips64el.h b/qemu/mips64el.h index 4700a912..0c3af187 100644 --- a/qemu/mips64el.h +++ b/qemu/mips64el.h @@ -123,6 +123,7 @@ #define memory_map memory_map_mips64el #define memory_map_io memory_map_io_mips64el #define memory_map_ptr memory_map_ptr_mips64el +#define memory_cow memory_cow_mips64el #define memory_unmap memory_unmap_mips64el #define memory_free memory_free_mips64el #define flatview_unref flatview_unref_mips64el @@ -144,6 +145,7 @@ #define memory_region_del_subregion memory_region_del_subregion_mips64el #define memory_region_add_subregion_overlap memory_region_add_subregion_overlap_mips64el #define memory_region_find memory_region_find_mips64el +#define memory_region_filter_subregions memory_region_filter_subregions_mips64el #define memory_listener_register memory_listener_register_mips64el #define memory_listener_unregister memory_listener_unregister_mips64el #define address_space_remove_listeners address_space_remove_listeners_mips64el diff --git a/qemu/mipsel.h b/qemu/mipsel.h index f42638b0..be52b6b8 100644 --- a/qemu/mipsel.h +++ b/qemu/mipsel.h @@ -123,6 +123,7 @@ #define memory_map memory_map_mipsel #define memory_map_io memory_map_io_mipsel #define memory_map_ptr memory_map_ptr_mipsel +#define memory_cow memory_cow_mipsel #define memory_unmap memory_unmap_mipsel #define memory_free memory_free_mipsel #define flatview_unref flatview_unref_mipsel @@ -144,6 +145,7 @@ #define memory_region_del_subregion memory_region_del_subregion_mipsel #define memory_region_add_subregion_overlap memory_region_add_subregion_overlap_mipsel #define memory_region_find memory_region_find_mipsel +#define memory_region_filter_subregions memory_region_filter_subregions_mipsel #define memory_listener_register memory_listener_register_mipsel #define memory_listener_unregister memory_listener_unregister_mipsel #define address_space_remove_listeners address_space_remove_listeners_mipsel diff --git a/qemu/ppc.h b/qemu/ppc.h index a2a7a814..e44076db 100644 --- a/qemu/ppc.h +++ b/qemu/ppc.h @@ -123,6 +123,7 @@ #define memory_map memory_map_ppc #define memory_map_io memory_map_io_ppc #define memory_map_ptr memory_map_ptr_ppc +#define memory_cow memory_cow_ppc #define memory_unmap memory_unmap_ppc #define memory_free memory_free_ppc #define flatview_unref flatview_unref_ppc @@ -144,6 +145,7 @@ #define memory_region_del_subregion memory_region_del_subregion_ppc #define memory_region_add_subregion_overlap memory_region_add_subregion_overlap_ppc #define memory_region_find memory_region_find_ppc +#define memory_region_filter_subregions memory_region_filter_subregions_ppc #define memory_listener_register memory_listener_register_ppc #define memory_listener_unregister memory_listener_unregister_ppc #define address_space_remove_listeners address_space_remove_listeners_ppc diff --git a/qemu/ppc64.h b/qemu/ppc64.h index 07f0a5ce..2a3527d4 100644 --- a/qemu/ppc64.h +++ b/qemu/ppc64.h @@ -123,6 +123,7 @@ #define memory_map memory_map_ppc64 #define memory_map_io memory_map_io_ppc64 #define memory_map_ptr memory_map_ptr_ppc64 +#define memory_cow memory_cow_ppc64 #define memory_unmap memory_unmap_ppc64 #define memory_free memory_free_ppc64 #define flatview_unref flatview_unref_ppc64 @@ -144,6 +145,7 @@ #define memory_region_del_subregion memory_region_del_subregion_ppc64 #define memory_region_add_subregion_overlap memory_region_add_subregion_overlap_ppc64 #define memory_region_find memory_region_find_ppc64 +#define memory_region_filter_subregions memory_region_filter_subregions_ppc64 #define memory_listener_register memory_listener_register_ppc64 #define memory_listener_unregister memory_listener_unregister_ppc64 #define address_space_remove_listeners address_space_remove_listeners_ppc64 diff --git a/qemu/riscv32.h b/qemu/riscv32.h index a33a0068..eb978cb1 100644 --- a/qemu/riscv32.h +++ b/qemu/riscv32.h @@ -123,6 +123,7 @@ #define memory_map memory_map_riscv32 #define memory_map_io memory_map_io_riscv32 #define memory_map_ptr memory_map_ptr_riscv32 +#define memory_cow memory_cow_riscv32 #define memory_unmap memory_unmap_riscv32 #define memory_free memory_free_riscv32 #define flatview_unref flatview_unref_riscv32 @@ -144,6 +145,7 @@ #define memory_region_del_subregion memory_region_del_subregion_riscv32 #define memory_region_add_subregion_overlap memory_region_add_subregion_overlap_riscv32 #define memory_region_find memory_region_find_riscv32 +#define memory_region_filter_subregions memory_region_filter_subregions_riscv32 #define memory_listener_register memory_listener_register_riscv32 #define memory_listener_unregister memory_listener_unregister_riscv32 #define address_space_remove_listeners address_space_remove_listeners_riscv32 diff --git a/qemu/riscv64.h b/qemu/riscv64.h index ba926a07..73b2d161 100644 --- a/qemu/riscv64.h +++ b/qemu/riscv64.h @@ -123,6 +123,7 @@ #define memory_map memory_map_riscv64 #define memory_map_io memory_map_io_riscv64 #define memory_map_ptr memory_map_ptr_riscv64 +#define memory_cow memory_cow_riscv64 #define memory_unmap memory_unmap_riscv64 #define memory_free memory_free_riscv64 #define flatview_unref flatview_unref_riscv64 @@ -144,6 +145,7 @@ #define memory_region_del_subregion memory_region_del_subregion_riscv64 #define memory_region_add_subregion_overlap memory_region_add_subregion_overlap_riscv64 #define memory_region_find memory_region_find_riscv64 +#define memory_region_filter_subregions memory_region_filter_subregions_riscv64 #define memory_listener_register memory_listener_register_riscv64 #define memory_listener_unregister memory_listener_unregister_riscv64 #define address_space_remove_listeners address_space_remove_listeners_riscv64 diff --git a/qemu/s390x.h b/qemu/s390x.h index e989949b..da86a856 100644 --- a/qemu/s390x.h +++ b/qemu/s390x.h @@ -123,6 +123,7 @@ #define memory_map memory_map_s390x #define memory_map_io memory_map_io_s390x #define memory_map_ptr memory_map_ptr_s390x +#define memory_cow memory_cow_s390x #define memory_unmap memory_unmap_s390x #define memory_free memory_free_s390x #define flatview_unref flatview_unref_s390x @@ -144,6 +145,7 @@ #define memory_region_del_subregion memory_region_del_subregion_s390x #define memory_region_add_subregion_overlap memory_region_add_subregion_overlap_s390x #define memory_region_find memory_region_find_s390x +#define memory_region_filter_subregions memory_region_filter_subregions_s390x #define memory_listener_register memory_listener_register_s390x #define memory_listener_unregister memory_listener_unregister_s390x #define address_space_remove_listeners address_space_remove_listeners_s390x diff --git a/qemu/softmmu/memory.c b/qemu/softmmu/memory.c index 9e3e9a77..0f0c5ed2 100644 --- a/qemu/softmmu/memory.c +++ b/qemu/softmmu/memory.c @@ -26,6 +26,9 @@ //#define DEBUG_UNASSIGNED +void memory_region_transaction_begin(void); +void memory_region_transaction_commit(MemoryRegion *mr); + typedef struct AddrRange AddrRange; /* @@ -49,7 +52,7 @@ MemoryRegion *memory_map(struct uc_struct *uc, hwaddr begin, size_t size, uint32 return NULL; } - memory_region_add_subregion(uc->system_memory, begin, ram); + memory_region_add_subregion_overlap(uc->system_memory, begin, ram, uc->snapshot_level); if (uc->cpu) { tlb_flush(uc->cpu); @@ -79,6 +82,48 @@ MemoryRegion *memory_map_ptr(struct uc_struct *uc, hwaddr begin, size_t size, ui return ram; } +static void make_contained(struct uc_struct *uc, MemoryRegion *current) +{ + hwaddr addr = current->addr; + MemoryRegion *container = g_new(MemoryRegion, 1); + memory_region_init(uc, container, int128_get64(current->size)); + memory_region_del_subregion(uc->system_memory, current); + memory_region_add_subregion_overlap(container, 0, current, current->priority); + memory_region_add_subregion(uc->system_memory, addr, container); +} + +MemoryRegion *memory_cow(struct uc_struct *uc, MemoryRegion *current, hwaddr begin, size_t size) +{ + hwaddr offset; + hwaddr current_offset; + MemoryRegion *ram = g_new(MemoryRegion, 1); + + if (current->container == uc->system_memory) { + make_contained(uc, current); + } + offset = begin - current->container->addr;; + current_offset = offset - current->addr; + + memory_region_init_ram(uc, ram, size, current->perms); + if (ram->addr == -1 || !ram->ram_block) { + g_free(ram); + return NULL; + } + memory_region_transaction_begin(); + + memcpy(ramblock_ptr(ram->ram_block, 0), ramblock_ptr(current->ram_block, current_offset), size); + memory_region_add_subregion_overlap(current->container, offset, ram, uc->snapshot_level); + + if (uc->cpu) { + tlb_flush(uc->cpu); + } + + uc->memory_region_update_pending = true; + memory_region_transaction_commit(ram); + + return ram; +} + static uint64_t mmio_read_wrapper(struct uc_struct *uc, void *opaque, hwaddr addr, unsigned size) { mmio_cbs* cbs = (mmio_cbs*)opaque; @@ -148,6 +193,21 @@ MemoryRegion *memory_map_io(struct uc_struct *uc, ram_addr_t begin, size_t size, return mmio; } +void memory_region_filter_subregions(MemoryRegion *mr, int32_t level) +{ + MemoryRegion *subregion, *subregion_next; + memory_region_transaction_begin(); + QTAILQ_FOREACH_SAFE(subregion, &mr->subregions, subregions_link, subregion_next) { + if (subregion->priority >= level) { + memory_region_del_subregion(mr, subregion); + subregion->destructor(subregion); + g_free(subregion); + mr->uc->memory_region_update_pending = true; + } + } + memory_region_transaction_commit(mr); +} + void memory_unmap(struct uc_struct *uc, MemoryRegion *mr) { int i; @@ -179,16 +239,15 @@ void memory_unmap(struct uc_struct *uc, MemoryRegion *mr) int memory_free(struct uc_struct *uc) { - int i; - MemoryRegion *mr; + MemoryRegion *subregion, *subregion_next; + MemoryRegion *mr = uc->system_memory; - for (i = 0; i < uc->mapped_block_count; i++) { - mr = uc->mapped_blocks[i]; - mr->enabled = false; - memory_region_del_subregion(uc->system_memory, mr); - mr->destructor(mr); + QTAILQ_FOREACH_SAFE(subregion, &mr->subregions, subregions_link, subregion_next) { + subregion->enabled = false; + memory_region_del_subregion(uc->system_memory, subregion); + subregion->destructor(subregion); /* destroy subregion */ - g_free(mr); + g_free(subregion); } return 0; @@ -886,6 +945,7 @@ static void memory_region_destructor_none(MemoryRegion *mr) static void memory_region_destructor_ram(MemoryRegion *mr) { + memory_region_filter_subregions(mr, 0); qemu_ram_free(mr->uc, mr->ram_block); } diff --git a/qemu/sparc.h b/qemu/sparc.h index d5effee6..e3edae06 100644 --- a/qemu/sparc.h +++ b/qemu/sparc.h @@ -123,6 +123,7 @@ #define memory_map memory_map_sparc #define memory_map_io memory_map_io_sparc #define memory_map_ptr memory_map_ptr_sparc +#define memory_cow memory_cow_sparc #define memory_unmap memory_unmap_sparc #define memory_free memory_free_sparc #define flatview_unref flatview_unref_sparc @@ -144,6 +145,7 @@ #define memory_region_del_subregion memory_region_del_subregion_sparc #define memory_region_add_subregion_overlap memory_region_add_subregion_overlap_sparc #define memory_region_find memory_region_find_sparc +#define memory_region_filter_subregions memory_region_filter_subregions_sparc #define memory_listener_register memory_listener_register_sparc #define memory_listener_unregister memory_listener_unregister_sparc #define address_space_remove_listeners address_space_remove_listeners_sparc diff --git a/qemu/sparc64.h b/qemu/sparc64.h index 462466cf..ac9583bd 100644 --- a/qemu/sparc64.h +++ b/qemu/sparc64.h @@ -123,6 +123,7 @@ #define memory_map memory_map_sparc64 #define memory_map_io memory_map_io_sparc64 #define memory_map_ptr memory_map_ptr_sparc64 +#define memory_cow memory_cow_sparc64 #define memory_unmap memory_unmap_sparc64 #define memory_free memory_free_sparc64 #define flatview_unref flatview_unref_sparc64 @@ -144,6 +145,7 @@ #define memory_region_del_subregion memory_region_del_subregion_sparc64 #define memory_region_add_subregion_overlap memory_region_add_subregion_overlap_sparc64 #define memory_region_find memory_region_find_sparc64 +#define memory_region_filter_subregions memory_region_filter_subregions_sparc64 #define memory_listener_register memory_listener_register_sparc64 #define memory_listener_unregister memory_listener_unregister_sparc64 #define address_space_remove_listeners address_space_remove_listeners_sparc64 diff --git a/qemu/tricore.h b/qemu/tricore.h index 28b6f82d..8dfd48dc 100644 --- a/qemu/tricore.h +++ b/qemu/tricore.h @@ -123,6 +123,7 @@ #define memory_map memory_map_tricore #define memory_map_io memory_map_io_tricore #define memory_map_ptr memory_map_ptr_tricore +#define memory_cow memory_cow_tricore #define memory_unmap memory_unmap_tricore #define memory_free memory_free_tricore #define flatview_unref flatview_unref_tricore @@ -144,6 +145,7 @@ #define memory_region_del_subregion memory_region_del_subregion_tricore #define memory_region_add_subregion_overlap memory_region_add_subregion_overlap_tricore #define memory_region_find memory_region_find_tricore +#define memory_region_filter_subregions memory_region_filter_subregions_tricore #define memory_listener_register memory_listener_register_tricore #define memory_listener_unregister memory_listener_unregister_tricore #define address_space_remove_listeners address_space_remove_listeners_tricore diff --git a/qemu/unicorn_common.h b/qemu/unicorn_common.h index 10252c05..c3ae499e 100644 --- a/qemu/unicorn_common.h +++ b/qemu/unicorn_common.h @@ -137,6 +137,8 @@ static inline void uc_common_init(struct uc_struct* uc) uc->memory_map_io = memory_map_io; uc->set_tlb = uc_set_tlb; uc->memory_mapping = find_memory_mapping; + uc->memory_filter_subregions = memory_region_filter_subregions; + uc->memory_cow = memory_cow; if (!uc->release) uc->release = release_common; diff --git a/qemu/x86_64.h b/qemu/x86_64.h index a118e747..0ce52d2a 100644 --- a/qemu/x86_64.h +++ b/qemu/x86_64.h @@ -123,6 +123,7 @@ #define memory_map memory_map_x86_64 #define memory_map_io memory_map_io_x86_64 #define memory_map_ptr memory_map_ptr_x86_64 +#define memory_cow memory_cow_x86_64 #define memory_unmap memory_unmap_x86_64 #define memory_free memory_free_x86_64 #define flatview_unref flatview_unref_x86_64 @@ -144,6 +145,7 @@ #define memory_region_del_subregion memory_region_del_subregion_x86_64 #define memory_region_add_subregion_overlap memory_region_add_subregion_overlap_x86_64 #define memory_region_find memory_region_find_x86_64 +#define memory_region_filter_subregions memory_region_filter_subregions_x86_64 #define memory_listener_register memory_listener_register_x86_64 #define memory_listener_unregister memory_listener_unregister_x86_64 #define address_space_remove_listeners address_space_remove_listeners_x86_64 diff --git a/symbols.sh b/symbols.sh index 80f213b6..ddea708f 100755 --- a/symbols.sh +++ b/symbols.sh @@ -123,6 +123,7 @@ cpu_inl \ memory_map \ memory_map_io \ memory_map_ptr \ +memory_cow \ memory_unmap \ memory_free \ flatview_unref \ @@ -144,6 +145,7 @@ memory_region_add_subregion \ memory_region_del_subregion \ memory_region_add_subregion_overlap \ memory_region_find \ +memory_region_filter_subregions \ memory_listener_register \ memory_listener_unregister \ address_space_remove_listeners \ diff --git a/tests/unit/test_mem.c b/tests/unit/test_mem.c index 4e2f176a..d25b3448 100644 --- a/tests/unit/test_mem.c +++ b/tests/unit/test_mem.c @@ -275,6 +275,73 @@ static void test_mem_protect_mmio(void) OK(uc_close(uc)); } +static void test_snapshot(void) +{ + uc_engine *uc; + uint32_t mem; + // mov eax, [0x2020]; inc eax; mov [0x2020], eax + char code[] = "\xa1\x20\x20\x00\x00\x00\x00\x00\x00\xff\xc0\xa3\x20\x20\x00" + "\x00\x00\x00\x00\x00"; + + OK(uc_open(UC_ARCH_X86, UC_MODE_64, &uc)); + OK(uc_mem_map(uc, 0x1000, 0x1000, UC_PROT_ALL)); + OK(uc_mem_write(uc, 0x1000, code, sizeof(code) - 1)); + + OK(uc_mem_map(uc, 0x2000, 0x1000, UC_PROT_ALL)); + OK(uc_snapshot(uc)); + + OK(uc_emu_start(uc, 0x1000, 0x1000 + sizeof(code) - 1, 0, 0)); + OK(uc_mem_read(uc, 0x2020, &mem, sizeof(mem))); + TEST_CHECK(mem == 1); + OK(uc_snapshot(uc)); + OK(uc_emu_start(uc, 0x1000, 0x1000 + sizeof(code) - 1, 0, 0)); + OK(uc_mem_read(uc, 0x2020, &mem, sizeof(mem))); + TEST_CHECK(mem == 2); + OK(uc_restore_latest_snapshot(uc)); + //TODO check mem + OK(uc_mem_read(uc, 0x2020, &mem, sizeof(mem))); + TEST_CHECK(mem == 1); + OK(uc_restore_latest_snapshot(uc)); + OK(uc_mem_read(uc, 0x2020, &mem, sizeof(mem))); + TEST_CHECK(mem == 0); + //TODO check mem + + OK(uc_context_free(c0)); + OK(uc_context_free(c1)); + OK(uc_close(uc)); +} + +static void test_context_snapshot(void) +{ + uc_engine *uc; + uc_context *ctx; + uint64_t tmp = 1; + + OK(uc_open(UC_ARCH_X86, UC_MODE_64, &uc)); + OK(uc_ctl_context_use_snapshots(uc, 1)); + OK(uc_mem_map(uc, 0x1000, 0x1000, UC_PROT_ALL)); + OK(uc_context_alloc(uc, &ctx)); + OK(uc_context_save(uc, ctx)); + + OK(uc_mem_write(uc, 0x1000, &tmp, sizeof(tmp))); + OK(uc_mem_read(uc, 0x1000, &tmp, sizeof(tmp))); + TEST_CHECK(tmp == 1); + OK(uc_context_restore(uc, ctx)); + OK(uc_mem_read(uc, 0x1000, &tmp, sizeof(tmp))); + TEST_CHECK(tmp == 0); + + tmp = 2; + OK(uc_mem_write(uc, 0x1000, &tmp, sizeof(tmp))); + OK(uc_mem_read(uc, 0x1000, &tmp, sizeof(tmp))); + TEST_CHECK(tmp == 2); + OK(uc_context_restore(uc, ctx)); + OK(uc_mem_read(uc, 0x1000, &tmp, sizeof(tmp))); + TEST_CHECK(tmp == 0); + + OK(uc_context_free(ctx)); + OK(uc_close(uc)); +} + TEST_LIST = {{"test_map_correct", test_map_correct}, {"test_map_wrapping", test_map_wrapping}, {"test_mem_protect", test_mem_protect}, @@ -286,4 +353,6 @@ TEST_LIST = {{"test_map_correct", test_map_correct}, {"test_map_big_memory", test_map_big_memory}, {"test_mem_protect_remove_exec", test_mem_protect_remove_exec}, {"test_mem_protect_mmio", test_mem_protect_mmio}, + {"test_snapshot", test_snapshot}, + {"test_context_snapshot", test_context_snapshot}, {NULL, NULL}}; diff --git a/uc.c b/uc.c index 7eb4beea..184aba32 100644 --- a/uc.c +++ b/uc.c @@ -31,7 +31,6 @@ #include "qemu-common.h" static void clear_deleted_hooks(uc_engine *uc); -static MemoryRegion *find_memory_region(struct uc_struct *uc, uint64_t address); static void *hook_insert(struct list *l, struct hook *h) { @@ -676,16 +675,27 @@ uc_err uc_reg_write2(uc_engine *uc, int regid, const void *value, size_t *size) return UC_ERR_OK; } +static size_t memory_region_len(uc_engine *uc, MemoryRegion *mr, uint64_t address, size_t count) +{ + hwaddr end = mr->end; + while (mr->container != uc->system_memory) { + mr = mr->container; + end += mr->addr; + } + return (size_t)MIN(count, end - address); +} + // check if a memory area is mapped // this is complicated because an area can overlap adjacent blocks static bool check_mem_area(uc_engine *uc, uint64_t address, size_t size) { size_t count = 0, len; + while (count < size) { - MemoryRegion *mr = find_memory_region(uc, address); + MemoryRegion *mr = uc->memory_mapping(uc, address); if (mr) { - len = (size_t)MIN(size - count, mr->end - address); + len = memory_region_len(uc, mr, address, size - count); count += len; address += len; } else { // this address is not mapped in yet @@ -714,9 +724,9 @@ uc_err uc_mem_read(uc_engine *uc, uint64_t address, void *_bytes, size_t size) // memory area can overlap adjacent memory blocks while (count < size) { - MemoryRegion *mr = find_memory_region(uc, address); + MemoryRegion *mr = uc->memory_mapping(uc, address); if (mr) { - len = (size_t)MIN(size - count, mr->end - address); + len = memory_region_len(uc, mr, address, size - count); if (uc->read_mem(&uc->address_space_memory, address, bytes, len) == false) { break; @@ -755,7 +765,7 @@ uc_err uc_mem_write(uc_engine *uc, uint64_t address, const void *_bytes, // memory area can overlap adjacent memory blocks while (count < size) { - MemoryRegion *mr = find_memory_region(uc, address); + MemoryRegion *mr = uc->memory_mapping(uc, address); if (mr) { uint32_t operms = mr->perms; if (!(operms & UC_PROT_WRITE)) { // write protected @@ -764,7 +774,13 @@ uc_err uc_mem_write(uc_engine *uc, uint64_t address, const void *_bytes, uc->readonly_mem(mr, false); } - len = (size_t)MIN(size - count, mr->end - address); + len = memory_region_len(uc, mr, address, size - count); + if (uc->snapshot_level && uc->snapshot_level > mr->priority) { + mr = uc->memory_cow(uc, mr, address, len); + if (!mr) { + return UC_ERR_NOMEM; + } + } if (uc->write_mem(&uc->address_space_memory, address, bytes, len) == false) { break; @@ -1480,6 +1496,11 @@ uc_err uc_mem_protect(struct uc_struct *uc, uint64_t address, size_t size, UC_INIT(uc); + // snapshot and protection can't be mixed + if (uc->snapshot_level > 0) { + return UC_ERR_ARG; + } + if (size == 0) { // trivial case, no change return UC_ERR_OK; @@ -1501,6 +1522,8 @@ uc_err uc_mem_protect(struct uc_struct *uc, uint64_t address, size_t size, } // check that user's entire requested block is mapped + //TODO check if protected is possible + //deny after cow if (!check_mem_area(uc, address, size)) { return UC_ERR_NOMEM; } @@ -1510,14 +1533,14 @@ uc_err uc_mem_protect(struct uc_struct *uc, uint64_t address, size_t size, addr = address; count = 0; while (count < size) { - mr = find_memory_region(uc, addr); - len = (size_t)MIN(size - count, mr->end - addr); + mr = uc->memory_mapping(uc, addr); + len = memory_region_len(uc, mr, addr, size - count); if (mr->ram) { if (!split_region(uc, mr, addr, len, false)) { return UC_ERR_NOMEM; } - mr = find_memory_region(uc, addr); + mr = uc->memory_mapping(uc, addr); // will this remove EXEC permission? if (((mr->perms & UC_PROT_EXEC) != 0) && ((perms & UC_PROT_EXEC) == 0)) { @@ -1531,7 +1554,7 @@ uc_err uc_mem_protect(struct uc_struct *uc, uint64_t address, size_t size, return UC_ERR_NOMEM; } - mr = find_memory_region(uc, addr); + mr = uc->memory_mapping(uc, addr); mr->perms = perms; } @@ -1561,6 +1584,11 @@ uc_err uc_mem_unmap(struct uc_struct *uc, uint64_t address, size_t size) UC_INIT(uc); + // snapshot and unmapping can't be mixed + if (uc->snapshot_level > 0) { + return UC_ERR_ARG; + } + if (size == 0) { // nothing to unmap return UC_ERR_OK; @@ -1577,6 +1605,7 @@ uc_err uc_mem_unmap(struct uc_struct *uc, uint64_t address, size_t size) } // check that user's entire requested block is mapped + // TODO check for cow if (!check_mem_area(uc, address, size)) { return UC_ERR_NOMEM; } @@ -1586,8 +1615,8 @@ uc_err uc_mem_unmap(struct uc_struct *uc, uint64_t address, size_t size) addr = address; count = 0; while (count < size) { - mr = find_memory_region(uc, addr); - len = (size_t)MIN(size - count, mr->end - addr); + mr = uc->memory_mapping(uc, addr); + len = memory_region_len(uc, mr, addr, size - count); if (!mr->ram) { if (!split_mmio_region(uc, mr, addr, len, true)) { return UC_ERR_NOMEM; @@ -1600,7 +1629,7 @@ uc_err uc_mem_unmap(struct uc_struct *uc, uint64_t address, size_t size) // if we can retrieve the mapping, then no splitting took place // so unmap here - mr = find_memory_region(uc, addr); + mr = uc->memory_mapping(uc, addr); if (mr != NULL) { uc->memory_unmap(uc, mr); } @@ -1611,35 +1640,6 @@ uc_err uc_mem_unmap(struct uc_struct *uc, uint64_t address, size_t size) return UC_ERR_OK; } -// find the memory region of this address -static MemoryRegion *find_memory_region(struct uc_struct *uc, uint64_t address) -{ - unsigned int i; - - if (uc->mapped_block_count == 0) { - return NULL; - } - - // try with the cache index first - i = uc->mapped_block_cache_index; - - if (i < uc->mapped_block_count && address >= uc->mapped_blocks[i]->addr && - address <= uc->mapped_blocks[i]->end - 1) { - return uc->mapped_blocks[i]; - } - - i = bsearch_mapped_blocks(uc, address); - - if (i < uc->mapped_block_count && address >= uc->mapped_blocks[i]->addr && - address <= uc->mapped_blocks[i]->end - 1) { - uc->mapped_block_cache_index = i; - return uc->mapped_blocks[i]; - } - - // not found - return NULL; -} - UNICORN_EXPORT uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, void *user_data, uint64_t begin, uint64_t end, ...) @@ -2598,6 +2598,31 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...) return err; } +UNICORN_EXPORT +uc_err uc_snapshot(struct uc_struct *uc) +{ + if (uc->snapshot_level == INT32_MAX) { + return UC_ERR_RESOURCE; + } + uc->snapshot_level++; + return UC_ERR_OK; +} + +UNICORN_EXPORT +uc_err uc_restore_latest_snapshot(struct uc_struct *uc) +{ + MemoryRegion *subregion, *subregion_next; + + QTAILQ_FOREACH_SAFE(subregion, &uc->system_memory->subregions, subregions_link, subregion_next) { + uc->memory_filter_subregions(subregion, uc->snapshot_level); + if (QTAILQ_EMPTY(&subregion->subregions)) { + uc->memory_unmap(uc, subregion); + } + } + uc->snapshot_level--; + return UC_ERR_OK; +} + #ifdef UNICORN_TRACER uc_tracer *get_tracer() {