From df18756234ec5614d0194d7225ae2af3c279d59c Mon Sep 17 00:00:00 2001 From: "Takacs, Philipp" Date: Tue, 30 May 2023 16:18:17 +0200 Subject: [PATCH] implement uc_mem_unmap with snapshots still has todos and need tests --- include/uc_priv.h | 3 ++ qemu/aarch64.h | 2 ++ qemu/arm.h | 2 ++ qemu/include/exec/memory.h | 2 ++ qemu/m68k.h | 2 ++ qemu/mips.h | 2 ++ qemu/mips64.h | 2 ++ qemu/mips64el.h | 2 ++ qemu/mipsel.h | 2 ++ qemu/ppc.h | 2 ++ qemu/ppc64.h | 2 ++ qemu/riscv32.h | 2 ++ qemu/riscv64.h | 2 ++ qemu/s390x.h | 2 ++ qemu/softmmu/memory.c | 74 +++++++++++++++++++++++++++++++------- qemu/sparc.h | 2 ++ qemu/sparc64.h | 2 ++ qemu/tricore.h | 2 ++ qemu/unicorn_common.h | 2 ++ qemu/x86_64.h | 2 ++ symbols.sh | 2 ++ tests/unit/test_mem.c | 34 ++++++++++++++++++ uc.c | 70 ++++++++++++++++++++++++++++++++---- 23 files changed, 200 insertions(+), 19 deletions(-) diff --git a/include/uc_priv.h b/include/uc_priv.h index c2b7baa0..a9dca9fb 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -289,6 +289,8 @@ struct uc_struct { uc_memory_mapping_t memory_mapping; uc_memory_filter_t memory_filter_subregions; uc_mem_unmap_t memory_unmap; + uc_mem_unmap_t memory_moveout; + uc_mem_unmap_t memory_movein; uc_readonly_mem_t readonly_mem; uc_cpus_init cpus_init; uc_target_page_init target_page; @@ -412,6 +414,7 @@ struct uc_struct { PVOID seh_handle; void *seh_closure; #endif + GArray *unmapped_regions; int32_t snapshot_level; }; diff --git a/qemu/aarch64.h b/qemu/aarch64.h index 91b27ae5..106ffd2f 100644 --- a/qemu/aarch64.h +++ b/qemu/aarch64.h @@ -125,6 +125,8 @@ #define memory_map_ptr memory_map_ptr_aarch64 #define memory_cow memory_cow_aarch64 #define memory_unmap memory_unmap_aarch64 +#define memory_moveout memory_moveout_aarch64 +#define memory_movein memory_movein_aarch64 #define memory_free memory_free_aarch64 #define flatview_unref flatview_unref_aarch64 #define address_space_get_flatview address_space_get_flatview_aarch64 diff --git a/qemu/arm.h b/qemu/arm.h index 82b34564..42ad9c51 100644 --- a/qemu/arm.h +++ b/qemu/arm.h @@ -125,6 +125,8 @@ #define memory_map_ptr memory_map_ptr_arm #define memory_cow memory_cow_arm #define memory_unmap memory_unmap_arm +#define memory_moveout memory_moveout_arm +#define memory_movein memory_movein_arm #define memory_free memory_free_arm #define flatview_unref flatview_unref_arm #define address_space_get_flatview address_space_get_flatview_arm diff --git a/qemu/include/exec/memory.h b/qemu/include/exec/memory.h index c2661663..8a8d13c3 100644 --- a/qemu/include/exec/memory.h +++ b/qemu/include/exec/memory.h @@ -1217,6 +1217,8 @@ MemoryRegion *memory_map_ptr(struct uc_struct *uc, hwaddr begin, size_t size, ui uc_cb_mmio_write_t write_cb, void *user_data_read, void *user_data_write); MemoryRegion *memory_cow(struct uc_struct *uc, MemoryRegion *parrent, hwaddr begin, size_t size); void memory_unmap(struct uc_struct *uc, MemoryRegion *mr); +void memory_moveout(struct uc_struct *uc, MemoryRegion *mr); +void memory_movein(struct uc_struct *uc, MemoryRegion *mr); int memory_free(struct uc_struct *uc); #endif diff --git a/qemu/m68k.h b/qemu/m68k.h index a2958aa0..a743b019 100644 --- a/qemu/m68k.h +++ b/qemu/m68k.h @@ -125,6 +125,8 @@ #define memory_map_ptr memory_map_ptr_m68k #define memory_cow memory_cow_m68k #define memory_unmap memory_unmap_m68k +#define memory_moveout memory_moveout_m68k +#define memory_movein memory_movein_m68k #define memory_free memory_free_m68k #define flatview_unref flatview_unref_m68k #define address_space_get_flatview address_space_get_flatview_m68k diff --git a/qemu/mips.h b/qemu/mips.h index 496bf5b2..af08a938 100644 --- a/qemu/mips.h +++ b/qemu/mips.h @@ -125,6 +125,8 @@ #define memory_map_ptr memory_map_ptr_mips #define memory_cow memory_cow_mips #define memory_unmap memory_unmap_mips +#define memory_moveout memory_moveout_mips +#define memory_movein memory_movein_mips #define memory_free memory_free_mips #define flatview_unref flatview_unref_mips #define address_space_get_flatview address_space_get_flatview_mips diff --git a/qemu/mips64.h b/qemu/mips64.h index 84bfef10..0b46bae1 100644 --- a/qemu/mips64.h +++ b/qemu/mips64.h @@ -125,6 +125,8 @@ #define memory_map_ptr memory_map_ptr_mips64 #define memory_cow memory_cow_mips64 #define memory_unmap memory_unmap_mips64 +#define memory_moveout memory_moveout_mips64 +#define memory_movein memory_movein_mips64 #define memory_free memory_free_mips64 #define flatview_unref flatview_unref_mips64 #define address_space_get_flatview address_space_get_flatview_mips64 diff --git a/qemu/mips64el.h b/qemu/mips64el.h index 0c3af187..dd28ff03 100644 --- a/qemu/mips64el.h +++ b/qemu/mips64el.h @@ -125,6 +125,8 @@ #define memory_map_ptr memory_map_ptr_mips64el #define memory_cow memory_cow_mips64el #define memory_unmap memory_unmap_mips64el +#define memory_moveout memory_moveout_mips64el +#define memory_movein memory_movein_mips64el #define memory_free memory_free_mips64el #define flatview_unref flatview_unref_mips64el #define address_space_get_flatview address_space_get_flatview_mips64el diff --git a/qemu/mipsel.h b/qemu/mipsel.h index be52b6b8..864857c8 100644 --- a/qemu/mipsel.h +++ b/qemu/mipsel.h @@ -125,6 +125,8 @@ #define memory_map_ptr memory_map_ptr_mipsel #define memory_cow memory_cow_mipsel #define memory_unmap memory_unmap_mipsel +#define memory_moveout memory_moveout_mipsel +#define memory_movein memory_movein_mipsel #define memory_free memory_free_mipsel #define flatview_unref flatview_unref_mipsel #define address_space_get_flatview address_space_get_flatview_mipsel diff --git a/qemu/ppc.h b/qemu/ppc.h index e44076db..710231b8 100644 --- a/qemu/ppc.h +++ b/qemu/ppc.h @@ -125,6 +125,8 @@ #define memory_map_ptr memory_map_ptr_ppc #define memory_cow memory_cow_ppc #define memory_unmap memory_unmap_ppc +#define memory_moveout memory_moveout_ppc +#define memory_movein memory_movein_ppc #define memory_free memory_free_ppc #define flatview_unref flatview_unref_ppc #define address_space_get_flatview address_space_get_flatview_ppc diff --git a/qemu/ppc64.h b/qemu/ppc64.h index 2a3527d4..41a4a3e3 100644 --- a/qemu/ppc64.h +++ b/qemu/ppc64.h @@ -125,6 +125,8 @@ #define memory_map_ptr memory_map_ptr_ppc64 #define memory_cow memory_cow_ppc64 #define memory_unmap memory_unmap_ppc64 +#define memory_moveout memory_moveout_ppc64 +#define memory_movein memory_movein_ppc64 #define memory_free memory_free_ppc64 #define flatview_unref flatview_unref_ppc64 #define address_space_get_flatview address_space_get_flatview_ppc64 diff --git a/qemu/riscv32.h b/qemu/riscv32.h index eb978cb1..e4e24f52 100644 --- a/qemu/riscv32.h +++ b/qemu/riscv32.h @@ -125,6 +125,8 @@ #define memory_map_ptr memory_map_ptr_riscv32 #define memory_cow memory_cow_riscv32 #define memory_unmap memory_unmap_riscv32 +#define memory_moveout memory_moveout_riscv32 +#define memory_movein memory_movein_riscv32 #define memory_free memory_free_riscv32 #define flatview_unref flatview_unref_riscv32 #define address_space_get_flatview address_space_get_flatview_riscv32 diff --git a/qemu/riscv64.h b/qemu/riscv64.h index 73b2d161..95a4b6ed 100644 --- a/qemu/riscv64.h +++ b/qemu/riscv64.h @@ -125,6 +125,8 @@ #define memory_map_ptr memory_map_ptr_riscv64 #define memory_cow memory_cow_riscv64 #define memory_unmap memory_unmap_riscv64 +#define memory_moveout memory_moveout_riscv64 +#define memory_movein memory_movein_riscv64 #define memory_free memory_free_riscv64 #define flatview_unref flatview_unref_riscv64 #define address_space_get_flatview address_space_get_flatview_riscv64 diff --git a/qemu/s390x.h b/qemu/s390x.h index da86a856..97e8ecd7 100644 --- a/qemu/s390x.h +++ b/qemu/s390x.h @@ -125,6 +125,8 @@ #define memory_map_ptr memory_map_ptr_s390x #define memory_cow memory_cow_s390x #define memory_unmap memory_unmap_s390x +#define memory_moveout memory_moveout_s390x +#define memory_movein memory_movein_s390x #define memory_free memory_free_s390x #define flatview_unref flatview_unref_s390x #define address_space_get_flatview address_space_get_flatview_s390x diff --git a/qemu/softmmu/memory.c b/qemu/softmmu/memory.c index 0f0c5ed2..2c52539c 100644 --- a/qemu/softmmu/memory.c +++ b/qemu/softmmu/memory.c @@ -208,9 +208,69 @@ void memory_region_filter_subregions(MemoryRegion *mr, int32_t level) memory_region_transaction_commit(mr); } +static void memory_region_remove_mapped_block(struct uc_struct *uc, MemoryRegion *mr, bool free) +{ + size_t i; + for (i = 0; i < uc->mapped_block_count; i++) { + if (uc->mapped_blocks[i] == mr) { + uc->mapped_block_count--; + //shift remainder of array down over deleted pointer + memmove(&uc->mapped_blocks[i], &uc->mapped_blocks[i + 1], sizeof(MemoryRegion*) * (uc->mapped_block_count - i)); + if (free) { + mr->destructor(mr); + g_free(mr); + } + break; + } + } +} + +void memory_moveout(struct uc_struct *uc, MemoryRegion *mr) +{ + hwaddr addr; + /* A bit dirty, but it works. + * The first subregion will be the one with the smalest priority. + * In case of CoW this will always be the region which is mapped initial and later be moved in the subregion of the container. + * The initial subregion is the one stored in mapped_blocks + * Because CoW is done after the snapshot level is increased there is only on subregion with + */ + memory_region_transaction_begin(); + MemoryRegion *mr_block = QTAILQ_FIRST(&mr->subregions); + + if (!mr_block) { + mr_block = mr; + } + + if (uc->cpu) { + // We also need to remove all tb cache + uc->uc_invalidate_tb(uc, mr->addr, int128_get64(mr->size)); + + // Make sure all pages associated with the MemoryRegion are flushed + // Only need to do this if we are in a running state + for (addr = mr->addr; (int64_t)(mr->end - addr) > 0; addr += uc->target_page_size) { + tlb_flush_page(uc->cpu, addr); + } + } + + memory_region_del_subregion(uc->system_memory, mr); + g_array_append_val(uc->unmapped_regions, mr); + memory_region_remove_mapped_block(uc, mr_block, false); + uc->memory_region_update_pending = true; + memory_region_transaction_commit(uc->system_memory); + /* dirty hack to save the snapshot level */ + mr->container = (void *)(intptr_t)uc->snapshot_level; +} + +void memory_movein(struct uc_struct *uc, MemoryRegion *mr) +{ + memory_region_transaction_begin(); + memory_region_add_subregion_overlap(uc->system_memory, mr->addr, mr, mr->priority); + uc->memory_region_update_pending = true; + memory_region_transaction_commit(uc->system_memory); +} + void memory_unmap(struct uc_struct *uc, MemoryRegion *mr) { - int i; hwaddr addr; if (uc->cpu) { @@ -224,17 +284,7 @@ void memory_unmap(struct uc_struct *uc, MemoryRegion *mr) } } memory_region_del_subregion(uc->system_memory, mr); - - for (i = 0; i < uc->mapped_block_count; i++) { - if (uc->mapped_blocks[i] == mr) { - uc->mapped_block_count--; - //shift remainder of array down over deleted pointer - memmove(&uc->mapped_blocks[i], &uc->mapped_blocks[i + 1], sizeof(MemoryRegion*) * (uc->mapped_block_count - i)); - mr->destructor(mr); - g_free(mr); - break; - } - } + memory_region_remove_mapped_block(uc, mr, true); } int memory_free(struct uc_struct *uc) diff --git a/qemu/sparc.h b/qemu/sparc.h index e3edae06..51d623da 100644 --- a/qemu/sparc.h +++ b/qemu/sparc.h @@ -125,6 +125,8 @@ #define memory_map_ptr memory_map_ptr_sparc #define memory_cow memory_cow_sparc #define memory_unmap memory_unmap_sparc +#define memory_moveout memory_moveout_sparc +#define memory_movein memory_movein_sparc #define memory_free memory_free_sparc #define flatview_unref flatview_unref_sparc #define address_space_get_flatview address_space_get_flatview_sparc diff --git a/qemu/sparc64.h b/qemu/sparc64.h index ac9583bd..5779f315 100644 --- a/qemu/sparc64.h +++ b/qemu/sparc64.h @@ -125,6 +125,8 @@ #define memory_map_ptr memory_map_ptr_sparc64 #define memory_cow memory_cow_sparc64 #define memory_unmap memory_unmap_sparc64 +#define memory_moveout memory_moveout_sparc64 +#define memory_movein memory_movein_sparc64 #define memory_free memory_free_sparc64 #define flatview_unref flatview_unref_sparc64 #define address_space_get_flatview address_space_get_flatview_sparc64 diff --git a/qemu/tricore.h b/qemu/tricore.h index 8dfd48dc..5fc51625 100644 --- a/qemu/tricore.h +++ b/qemu/tricore.h @@ -125,6 +125,8 @@ #define memory_map_ptr memory_map_ptr_tricore #define memory_cow memory_cow_tricore #define memory_unmap memory_unmap_tricore +#define memory_moveout memory_moveout_tricore +#define memory_movein memory_movein_tricore #define memory_free memory_free_tricore #define flatview_unref flatview_unref_tricore #define address_space_get_flatview address_space_get_flatview_tricore diff --git a/qemu/unicorn_common.h b/qemu/unicorn_common.h index c3ae499e..def70a9e 100644 --- a/qemu/unicorn_common.h +++ b/qemu/unicorn_common.h @@ -130,6 +130,8 @@ static inline void uc_common_init(struct uc_struct* uc) uc->memory_map = memory_map; uc->memory_map_ptr = memory_map_ptr; uc->memory_unmap = memory_unmap; + uc->memory_moveout = memory_moveout; + uc->memory_movein = memory_movein; uc->readonly_mem = memory_region_set_readonly; uc->target_page = target_page_init; uc->softfloat_initialize = softfloat_init; diff --git a/qemu/x86_64.h b/qemu/x86_64.h index 0ce52d2a..be528692 100644 --- a/qemu/x86_64.h +++ b/qemu/x86_64.h @@ -125,6 +125,8 @@ #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_moveout memory_moveout_x86_64 +#define memory_movein memory_movein_x86_64 #define memory_free memory_free_x86_64 #define flatview_unref flatview_unref_x86_64 #define address_space_get_flatview address_space_get_flatview_x86_64 diff --git a/symbols.sh b/symbols.sh index ddea708f..e038b46f 100755 --- a/symbols.sh +++ b/symbols.sh @@ -125,6 +125,8 @@ memory_map_io \ memory_map_ptr \ memory_cow \ memory_unmap \ +memory_moveout \ +memory_movein \ memory_free \ flatview_unref \ address_space_get_flatview \ diff --git a/tests/unit/test_mem.c b/tests/unit/test_mem.c index 8f6bcf4f..0069c507 100644 --- a/tests/unit/test_mem.c +++ b/tests/unit/test_mem.c @@ -346,6 +346,39 @@ static void test_context_snapshot(void) OK(uc_close(uc)); } +static void test_snapshot_unmap(void) +{ + uc_engine *uc; + uc_context *ctx; + uint64_t tmp; + + OK(uc_open(UC_ARCH_X86, UC_MODE_64, &uc)); + OK(uc_ctl_context_mode(uc, UC_CTL_CONTEXT_MEMORY|UC_CTL_CONTEXT_CPU)); + OK(uc_mem_map(uc, 0x1000, 0x2000, UC_PROT_ALL)); + + tmp = 1; + OK(uc_mem_write(uc, 0x1000, &tmp, sizeof(tmp))); + tmp = 2; + OK(uc_mem_write(uc, 0x2000, &tmp, sizeof(tmp))); + + OK(uc_context_alloc(uc, &ctx)); + OK(uc_context_save(uc, ctx)); + + uc_assert_err(UC_ERR_ARG, uc_mem_unmap(uc, 0x1000, 0x1000)); + OK(uc_mem_unmap(uc, 0x1000, 0x2000)); + uc_assert_err(UC_ERR_READ_UNMAPPED, uc_mem_read(uc, 0x1000, &tmp, sizeof(tmp))); + uc_assert_err(UC_ERR_READ_UNMAPPED, uc_mem_read(uc, 0x2000, &tmp, sizeof(tmp))); + + OK(uc_context_restore(uc, ctx)); + OK(uc_mem_read(uc, 0x1000, &tmp, sizeof(tmp))); + TEST_CHECK(tmp == 1); + OK(uc_mem_read(uc, 0x2000, &tmp, sizeof(tmp))); + TEST_CHECK(tmp == 2); + + 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}, @@ -359,4 +392,5 @@ TEST_LIST = {{"test_map_correct", test_map_correct}, {"test_mem_protect_mmio", test_mem_protect_mmio}, {"test_snapshot", test_snapshot}, {"test_context_snapshot", test_context_snapshot}, + {"test_snapshot_unmap", test_snapshot_unmap}, {NULL, NULL}}; diff --git a/uc.c b/uc.c index 90bc779e..ab620b55 100644 --- a/uc.c +++ b/uc.c @@ -260,6 +260,8 @@ static uc_err uc_init_engine(uc_engine *uc) uc->context_content = UC_CTL_CONTEXT_CPU; + uc->unmapped_regions = g_array_new(false, false, sizeof(MemoryRegion*)); + uc->init_done = true; return UC_ERR_OK; @@ -490,6 +492,12 @@ uc_err uc_close(uc_engine *uc) mr->destructor(mr); g_free(uc->system_memory); g_free(uc->system_io); + for (size_t i = 0; i < uc->unmapped_regions->len; i++) { + mr = g_array_index(uc->unmapped_regions, MemoryRegion *, i); + mr->destructor(mr); + g_free(mr); + } + g_array_free(uc->unmapped_regions, true); // Thread relateds. if (uc->qemu_thread_data) { @@ -1579,6 +1587,29 @@ uc_err uc_mem_protect(struct uc_struct *uc, uint64_t address, size_t size, return UC_ERR_OK; } +static +uc_err uc_mem_unmap_snapshot(struct uc_struct *uc, uint64_t address, size_t size, MemoryRegion **ret) +{ + MemoryRegion *mr; + + mr = uc->memory_mapping(uc, address); + while (mr->container != uc->system_memory) { + mr = mr->container; + } + + if (mr->addr != address || int128_get64(mr->size) != size) { + return UC_ERR_ARG; + } + + if (ret) { + *ret = mr; + } + + uc->memory_moveout(uc, mr); + + return UC_ERR_OK; +} + UNICORN_EXPORT uc_err uc_mem_unmap(struct uc_struct *uc, uint64_t address, size_t size) { @@ -1588,11 +1619,6 @@ 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; @@ -1609,11 +1635,14 @@ 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; } + if (uc->snapshot_level > 0) { + return uc_mem_unmap_snapshot(uc, address, size, NULL); + } + // Now we know entire region is mapped, so do the unmap // We may need to split regions if this area spans adjacent regions addr = address; @@ -2652,7 +2681,8 @@ static uc_err uc_snapshot(struct uc_struct *uc) static uc_err uc_restore_latest_snapshot(struct uc_struct *uc) { - MemoryRegion *subregion, *subregion_next; + MemoryRegion *subregion, *subregion_next, *mr, *initial_mr; + int level; QTAILQ_FOREACH_SAFE(subregion, &uc->system_memory->subregions, subregions_link, subregion_next) { uc->memory_filter_subregions(subregion, uc->snapshot_level); @@ -2660,6 +2690,32 @@ static uc_err uc_restore_latest_snapshot(struct uc_struct *uc) uc->memory_unmap(uc, subregion); } } + + for (size_t i = uc->unmapped_regions->len; i-- > 0;) { + mr = g_array_index(uc->unmapped_regions, MemoryRegion *, i); + // same dirty hack as in memory_moveout see qemu/softmmu/memory.c + initial_mr = QTAILQ_FIRST(&mr->subregions); + if (!initial_mr) { + initial_mr = mr; + } + /* same dirty hack as in memory_moveout see qemu/softmmu/memory.c */ + level = (intptr_t)mr->container; + mr->container = NULL; + + if (level < uc->snapshot_level) { + break; + } + if (memory_overlap(uc, mr->addr, int128_get64(mr->size))) { + return UC_ERR_MAP; + } + uc->memory_movein(uc, mr); + uc->memory_filter_subregions(mr, uc->snapshot_level); + if (initial_mr != mr && QTAILQ_EMPTY(&mr->subregions)) { + uc->memory_unmap(uc, subregion); + } + mem_map(uc, initial_mr); + g_array_remove_range(uc->unmapped_regions, i, 1); + } uc->snapshot_level--; return UC_ERR_OK; }