diff --git a/include/uc_priv.h b/include/uc_priv.h index 686ab9e5..7e76513a 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -47,6 +47,8 @@ typedef void (*uc_args_uc_u64_t)(struct uc_struct *, uint64_t addr); typedef MemoryRegion* (*uc_args_uc_ram_size_t)(struct uc_struct*, ram_addr_t begin, size_t size, uint32_t perms); +typedef void (*uc_mem_unmap_t)(struct uc_struct*, MemoryRegion *mr); + typedef void (*uc_readonly_mem_t)(MemoryRegion *mr, bool readonly); // which interrupt should make emulation stop? @@ -90,6 +92,7 @@ struct uc_struct { uc_args_tcg_enable_t tcg_enabled; uc_args_uc_long_t tcg_exec_init; uc_args_uc_ram_size_t memory_map; + uc_mem_unmap_t memory_unmap; uc_readonly_mem_t readonly_mem; // list of cpu void* cpu; diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index 991db568..fee75297 100644 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -422,9 +422,9 @@ uc_err uc_mem_map(uch handle, uint64_t address, size_t size, uint32_t perms); This API changes permissions on an existing memory region. @handle: handle returned by uc_open() - @start: starting address of the memory region to be modified. + @address: starting address of the memory region to be modified. This address must be aligned to 4KB, or this will return with UC_ERR_MAP error. - @block_size: size of the memory region to be modified. + @size: size of the memory region to be modified. This size must be multiple of 4KB, or this will return with UC_ERR_MAP error. @perms: New permissions for the mapped region. This must be some combination of UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, @@ -434,7 +434,23 @@ uc_err uc_mem_map(uch handle, uint64_t address, size_t size, uint32_t perms); for detailed error). */ UNICORN_EXPORT -uc_err uc_mem_protect(uch handle, uint64_t start, size_t block_size, uint32_t perms); +uc_err uc_mem_protect(uch handle, uint64_t address, size_t size, uint32_t perms); + +/* + Unmap a region of emulation memory. + This API deletes a memory mapping from the emulation memory space. + + @handle: handle returned by uc_open() + @address: starting address of the memory region to be unmapped. + This address must be aligned to 4KB, or this will return with UC_ERR_MAP error. + @size: size of the memory region to be modified. + This size must be multiple of 4KB, or this will return with UC_ERR_MAP error. + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_mem_unmap(uch handle, uint64_t address, size_t size); #ifdef __cplusplus } diff --git a/qemu/aarch64.h b/qemu/aarch64.h index 9ab06273..77de1644 100644 --- a/qemu/aarch64.h +++ b/qemu/aarch64.h @@ -7,6 +7,7 @@ #define phys_mem_clean phys_mem_clean_aarch64 #define tb_cleanup tb_cleanup_aarch64 #define memory_map memory_map_aarch64 +#define memory_unmap memory_unmap_aarch64 #define memory_free memory_free_aarch64 #define helper_raise_exception helper_raise_exception_aarch64 #define tcg_enabled tcg_enabled_aarch64 diff --git a/qemu/arm.h b/qemu/arm.h index c24329b3..3d405fac 100644 --- a/qemu/arm.h +++ b/qemu/arm.h @@ -7,6 +7,7 @@ #define phys_mem_clean phys_mem_clean_arm #define tb_cleanup tb_cleanup_arm #define memory_map memory_map_arm +#define memory_unmap memory_unmap_arm #define memory_free memory_free_arm #define helper_raise_exception helper_raise_exception_arm #define tcg_enabled tcg_enabled_arm diff --git a/qemu/header_gen.py b/qemu/header_gen.py index ff8a1ca5..67982502 100644 --- a/qemu/header_gen.py +++ b/qemu/header_gen.py @@ -13,6 +13,7 @@ symbols = ( 'phys_mem_clean', 'tb_cleanup', 'memory_map', + 'memory_unmap', 'memory_free', 'helper_raise_exception', 'tcg_enabled', diff --git a/qemu/include/exec/memory.h b/qemu/include/exec/memory.h index 4df8bd85..45c51e4d 100644 --- a/qemu/include/exec/memory.h +++ b/qemu/include/exec/memory.h @@ -939,6 +939,7 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, void memory_register_types(struct uc_struct *uc); MemoryRegion *memory_map(struct uc_struct *uc, ram_addr_t begin, size_t size, uint32_t perms); +void memory_unmap(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 5dbefab7..4be757ba 100644 --- a/qemu/m68k.h +++ b/qemu/m68k.h @@ -7,6 +7,7 @@ #define phys_mem_clean phys_mem_clean_m68k #define tb_cleanup tb_cleanup_m68k #define memory_map memory_map_m68k +#define memory_unmap memory_unmap_m68k #define memory_free memory_free_m68k #define helper_raise_exception helper_raise_exception_m68k #define tcg_enabled tcg_enabled_m68k diff --git a/qemu/memory.c b/qemu/memory.c index 3f8169d9..95336f5f 100644 --- a/qemu/memory.c +++ b/qemu/memory.c @@ -45,6 +45,18 @@ MemoryRegion *memory_map(struct uc_struct *uc, ram_addr_t begin, size_t size, ui return uc->ram; } +void memory_unmap(struct uc_struct *uc, MemoryRegion *mr) +{ + targer_ulong addr; + //make sure all pages associated with the MemoryRegion are flushed + for (addr = mr->addr; addr < mr->end; addr += 0x1000) { + tlb_flush_page(uc->current_cpu, addr); + } + mr->enabled = false; + memory_region_del_subregion(get_system_memory(uc), mr); + g_free(mr); +} + int memory_free(struct uc_struct *uc) { int i; diff --git a/qemu/mips.h b/qemu/mips.h index 059995e5..7a3e308a 100644 --- a/qemu/mips.h +++ b/qemu/mips.h @@ -7,6 +7,7 @@ #define phys_mem_clean phys_mem_clean_mips #define tb_cleanup tb_cleanup_mips #define memory_map memory_map_mips +#define memory_unmap memory_unmap_mips #define memory_free memory_free_mips #define helper_raise_exception helper_raise_exception_mips #define tcg_enabled tcg_enabled_mips diff --git a/qemu/mips64.h b/qemu/mips64.h index 74daddbc..9870cb15 100644 --- a/qemu/mips64.h +++ b/qemu/mips64.h @@ -7,6 +7,7 @@ #define phys_mem_clean phys_mem_clean_mips64 #define tb_cleanup tb_cleanup_mips64 #define memory_map memory_map_mips64 +#define memory_unmap memory_unmap_mips64 #define memory_free memory_free_mips64 #define helper_raise_exception helper_raise_exception_mips64 #define tcg_enabled tcg_enabled_mips64 diff --git a/qemu/mips64el.h b/qemu/mips64el.h index 6ffc2dbc..5fde9e53 100644 --- a/qemu/mips64el.h +++ b/qemu/mips64el.h @@ -7,6 +7,7 @@ #define phys_mem_clean phys_mem_clean_mips64el #define tb_cleanup tb_cleanup_mips64el #define memory_map memory_map_mips64el +#define memory_unmap memory_unmap_mips64el #define memory_free memory_free_mips64el #define helper_raise_exception helper_raise_exception_mips64el #define tcg_enabled tcg_enabled_mips64el diff --git a/qemu/mipsel.h b/qemu/mipsel.h index 94c4fdf7..caf1fe4d 100644 --- a/qemu/mipsel.h +++ b/qemu/mipsel.h @@ -7,6 +7,7 @@ #define phys_mem_clean phys_mem_clean_mipsel #define tb_cleanup tb_cleanup_mipsel #define memory_map memory_map_mipsel +#define memory_unmap memory_unmap_mipsel #define memory_free memory_free_mipsel #define helper_raise_exception helper_raise_exception_mipsel #define tcg_enabled tcg_enabled_mipsel diff --git a/qemu/powerpc.h b/qemu/powerpc.h index fd627665..92e614e1 100644 --- a/qemu/powerpc.h +++ b/qemu/powerpc.h @@ -7,6 +7,7 @@ #define phys_mem_clean phys_mem_clean_powerpc #define tb_cleanup tb_cleanup_powerpc #define memory_map memory_map_powerpc +#define memory_unmap memory_unmap_powerpc #define memory_free memory_free_powerpc #define helper_raise_exception helper_raise_exception_powerpc #define tcg_enabled tcg_enabled_powerpc diff --git a/qemu/sparc.h b/qemu/sparc.h index 64803c11..6aa47aa5 100644 --- a/qemu/sparc.h +++ b/qemu/sparc.h @@ -7,6 +7,7 @@ #define phys_mem_clean phys_mem_clean_sparc #define tb_cleanup tb_cleanup_sparc #define memory_map memory_map_sparc +#define memory_unmap memory_unmap_sparc #define memory_free memory_free_sparc #define helper_raise_exception helper_raise_exception_sparc #define tcg_enabled tcg_enabled_sparc diff --git a/qemu/sparc64.h b/qemu/sparc64.h index 5042c38d..6d3d2a1d 100644 --- a/qemu/sparc64.h +++ b/qemu/sparc64.h @@ -7,6 +7,7 @@ #define phys_mem_clean phys_mem_clean_sparc64 #define tb_cleanup tb_cleanup_sparc64 #define memory_map memory_map_sparc64 +#define memory_unmap memory_unmap_sparc64 #define memory_free memory_free_sparc64 #define helper_raise_exception helper_raise_exception_sparc64 #define tcg_enabled tcg_enabled_sparc64 diff --git a/qemu/unicorn_common.h b/qemu/unicorn_common.h index 176900cb..23ef0acb 100644 --- a/qemu/unicorn_common.h +++ b/qemu/unicorn_common.h @@ -73,6 +73,7 @@ static inline void uc_common_init(struct uc_struct* uc) uc->pause_all_vcpus = pause_all_vcpus; uc->vm_start = vm_start; uc->memory_map = memory_map; + uc->memory_unmap = memory_unmap; uc->readonly_mem = memory_region_set_readonly; if (!uc->release) diff --git a/qemu/x86_64.h b/qemu/x86_64.h index 9a148e95..ac9f34f0 100644 --- a/qemu/x86_64.h +++ b/qemu/x86_64.h @@ -7,6 +7,7 @@ #define phys_mem_clean phys_mem_clean_x86_64 #define tb_cleanup tb_cleanup_x86_64 #define memory_map memory_map_x86_64 +#define memory_unmap memory_unmap_x86_64 #define memory_free memory_free_x86_64 #define helper_raise_exception helper_raise_exception_x86_64 #define tcg_enabled tcg_enabled_x86_64 diff --git a/uc.c b/uc.c index 3c46e689..ec85ea61 100644 --- a/uc.c +++ b/uc.c @@ -643,87 +643,130 @@ uc_err uc_mem_map(uch handle, uint64_t address, size_t size, uint32_t perms) } UNICORN_EXPORT -uc_err uc_mem_protect(uch handle, uint64_t start, size_t block_size, uint32_t perms) +uc_err uc_mem_protect(uch handle, uint64_t address, size_t size, uint32_t perms) { - uint64_t address; - uint64_t size; struct uc_struct* uc = (struct uc_struct *)handle; + MemoryRegion *mr; if (handle == 0) // invalid handle return UC_ERR_UCH; - if (block_size == 0) + if (size == 0) // invalid memory mapping return UC_ERR_MAP; // address must be aligned to 4KB - if ((start & (4*1024 - 1)) != 0) + if ((address & (4*1024 - 1)) != 0) return UC_ERR_MAP; // size must be multiple of 4KB - if ((block_size & (4*1024 - 1)) != 0) + if ((size & (4*1024 - 1)) != 0) return UC_ERR_MAP; // check for only valid permissions if ((perms & ~(UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC)) != 0) return UC_ERR_MAP; - //check that users entire requested block is mapped - address = start; - size = block_size; - while (size > 0) { - uint64_t region_size; - MemoryRegion *mr = memory_mapping(uc, address); - if (mr == NULL) { - return UC_ERR_MAP; - } - region_size = int128_get64(mr->size); - if (address > mr->addr) { - //in case start address is not aligned with start of region - region_size -= address - mr->addr; - } - if (size < region_size) { - //entire region is covered - break; - } - size -= region_size; - address += region_size; - } + //check that user's entire requested block is mapped + if (!check_mem_area(uc, address, size)) + return UC_ERR_MAP; //Now we know entire region is mapped, so change permissions - address = start; - size = block_size; - while (size > 0) { - MemoryRegion *mr = memory_mapping(uc, address); - uint64_t region_size = int128_get64(mr->size); - if (address > mr->addr) { - //in case start address is not aligned with start of region - region_size -= address - mr->addr; - //TODO Learn how to split regions - //In this case some proper subset of the region is having it's permissions changed - //need to split region and add new portions into uc->mapped_blocks list - //In this case, there is a portion of the region with original perms: mr->addr..start - //and a portion getting new perms: start..start+block_size - - //split the block and stay in the loop - } - if (size < int128_get64(mr->size)) { - //TODO Learn how to split regions - //In this case some proper subset of the region is having it's permissions changed - //need to split region and add new portions into uc->mapped_blocks list - //In this case, there is a portion of the region with new perms: start..start+block_size - //and a portion getting new perms: mr->addr+size..mr->addr+mr->size - - //split the block and break - break; - } - size -= int128_get64(mr->size); - address += int128_get64(mr->size); + //check trivial case first + mr = memory_mapping(uc, address); + if (address == mr->addr && size == int128_get64(mr->size)) { + //regions exactly matches an existing region just change perms mr->perms = perms; uc->readonly_mem(mr, (perms & UC_PROT_WRITE) == 0); } - return UC_ERR_OK; + else { + //ouch, we are going to need to subdivide blocks +/* + address = start; + size = block_size; + while (size > 0) { + MemoryRegion *mr = memory_mapping(uc, address); + uint64_t region_size = int128_get64(mr->size); + if (address > mr->addr) { + //in case start address is not aligned with start of region + region_size -= address - mr->addr; + //TODO Learn how to split regions + //In this case some proper subset of the region is having it's permissions changed + //need to split region and add new portions into uc->mapped_blocks list + //In this case, there is a portion of the region with original perms: mr->addr..start + //and a portion getting new perms: start..start+block_size + + //split the block and stay in the loop + } + if (size < int128_get64(mr->size)) { + //TODO Learn how to split regions + //In this case some proper subset of the region is having it's permissions changed + //need to split region and add new portions into uc->mapped_blocks list + //In this case, there is a portion of the region with new perms: start..start+block_size + //and a portion getting new perms: mr->addr+size..mr->addr+mr->size + + //split the block and break + break; + } + size -= int128_get64(mr->size); + address += int128_get64(mr->size); + mr->perms = perms; + uc->readonly_mem(mr, (perms & UC_PROT_WRITE) == 0); + } +*/ + } + return UC_ERR_OK; +} + +UNICORN_EXPORT +uc_err uc_mem_unmap(uch handle, uint64_t address, size_t size) +{ + MemoryRegion *mr; + unsigned int i; + struct uc_struct* uc = (struct uc_struct *)handle; + + if (handle == 0) + // invalid handle + return UC_ERR_UCH; + + if (size == 0) + // nothing to unmap + return UC_ERR_OK; + + // address must be aligned to 4KB + if ((address & (4*1024 - 1)) != 0) + return UC_ERR_MAP; + + // size must be multiple of 4KB + if ((size & (4*1024 - 1)) != 0) + return UC_ERR_MAP; + + //check that user's entire requested block is mapped + if (!check_mem_area(uc, address, size)) + return UC_ERR_MAP; + + //Now we know entire region is mapped, so change permissions + //check trivial case first + mr = memory_mapping(uc, address); + if (address == mr->addr && size == int128_get64(mr->size)) { + //regions exactly matches an existing region just unmap it + uc->memory_unmap(uc, 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 + memcpy(&uc->mapped_blocks[i], &uc->mapped_blocks[i + 1], sizeof(MemoryRegion*) * (uc->mapped_block_count - i)); + break; + } + } + return UC_ERR_OK; + } + else { + //ouch, we are going to need to subdivide blocks + } + + return UC_ERR_MAP; } MemoryRegion *memory_mapping(struct uc_struct* uc, uint64_t address)