From 6d21ebabea3f4ad2cae3da63249af24155215738 Mon Sep 17 00:00:00 2001 From: Ryan Hileman Date: Fri, 27 Nov 2015 17:25:53 -0800 Subject: [PATCH] implement host-controlled memory mapping for #261 --- include/uc_priv.h | 3 ++ include/unicorn/unicorn.h | 18 +++++++++ qemu/aarch64.h | 1 + qemu/arm.h | 1 + qemu/header_gen.py | 1 + qemu/include/exec/memory.h | 1 + qemu/m68k.h | 1 + qemu/memory.c | 17 ++++++++ qemu/mips.h | 1 + qemu/mips64.h | 1 + qemu/mips64el.h | 1 + qemu/mipsel.h | 1 + qemu/sparc.h | 1 + qemu/sparc64.h | 1 + qemu/unicorn_common.h | 1 + qemu/x86_64.h | 1 + tests/unit/Makefile | 6 +-- tests/unit/test_mem_map_ptr.c | 75 +++++++++++++++++++++++++++++++++++ uc.c | 35 ++++++++++++++-- 19 files changed, 160 insertions(+), 7 deletions(-) create mode 100644 tests/unit/test_mem_map_ptr.c diff --git a/include/uc_priv.h b/include/uc_priv.h index 1493a7fd..c24a21f9 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -48,6 +48,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 MemoryRegion* (*uc_args_uc_ram_size_ptr_t)(struct uc_struct*, ram_addr_t begin, size_t size, void *ptr); + typedef void (*uc_mem_unmap_t)(struct uc_struct*, MemoryRegion *mr); typedef void (*uc_readonly_mem_t)(MemoryRegion *mr, bool readonly); @@ -97,6 +99,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_args_uc_ram_size_ptr_t memory_map_ptr; uc_mem_unmap_t memory_unmap; uc_readonly_mem_t readonly_mem; uc_mem_redirect_t mem_redirect; diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index a7a13b60..561e48b6 100644 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -446,6 +446,24 @@ typedef enum uc_prot { UNICORN_EXPORT uc_err uc_mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t perms); +/* + Map existing host memory in for emulation. + This API adds a memory region that can be used by emulation. + + @uc: handle returned by uc_open() + @address: starting address of the new memory region to be mapped in. + This address must be aligned to 4KB, or this will return with UC_ERR_ARG error. + @size: size of the new memory region to be mapped in. + This size must be multiple of 4KB, or this will return with UC_ERR_ARG error. + @ptr: pointer to host memory backing the newly mapped memory. Existing host + memory permissions are preserved. + + @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_map_ptr(uc_engine *uc, uint64_t address, size_t size, void *ptr); + /* Unmap a region of emulation memory. This API deletes a memory mapping from the emulation memory space. diff --git a/qemu/aarch64.h b/qemu/aarch64.h index b888e1cd..b919a355 100644 --- a/qemu/aarch64.h +++ b/qemu/aarch64.h @@ -8,6 +8,7 @@ #define phys_mem_clean phys_mem_clean_aarch64 #define tb_cleanup tb_cleanup_aarch64 #define memory_map memory_map_aarch64 +#define memory_map_ptr memory_map_ptr_aarch64 #define memory_unmap memory_unmap_aarch64 #define memory_free memory_free_aarch64 #define helper_raise_exception helper_raise_exception_aarch64 diff --git a/qemu/arm.h b/qemu/arm.h index 5f70b6bf..bf858302 100644 --- a/qemu/arm.h +++ b/qemu/arm.h @@ -8,6 +8,7 @@ #define phys_mem_clean phys_mem_clean_arm #define tb_cleanup tb_cleanup_arm #define memory_map memory_map_arm +#define memory_map_ptr memory_map_ptr_arm #define memory_unmap memory_unmap_arm #define memory_free memory_free_arm #define helper_raise_exception helper_raise_exception_arm diff --git a/qemu/header_gen.py b/qemu/header_gen.py index c5133381..021f8a65 100644 --- a/qemu/header_gen.py +++ b/qemu/header_gen.py @@ -14,6 +14,7 @@ symbols = ( 'phys_mem_clean', 'tb_cleanup', 'memory_map', + 'memory_map_ptr', 'memory_unmap', 'memory_free', 'helper_raise_exception', diff --git a/qemu/include/exec/memory.h b/qemu/include/exec/memory.h index 45c51e4d..b6f7d1b3 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); +MemoryRegion *memory_map_ptr(struct uc_struct *uc, ram_addr_t begin, size_t size, void *ptr); void memory_unmap(struct uc_struct *uc, MemoryRegion *mr); int memory_free(struct uc_struct *uc); diff --git a/qemu/m68k.h b/qemu/m68k.h index 8a344cbb..ceb5ca47 100644 --- a/qemu/m68k.h +++ b/qemu/m68k.h @@ -8,6 +8,7 @@ #define phys_mem_clean phys_mem_clean_m68k #define tb_cleanup tb_cleanup_m68k #define memory_map memory_map_m68k +#define memory_map_ptr memory_map_ptr_m68k #define memory_unmap memory_unmap_m68k #define memory_free memory_free_m68k #define helper_raise_exception helper_raise_exception_m68k diff --git a/qemu/memory.c b/qemu/memory.c index eaab3ba6..eb66518c 100644 --- a/qemu/memory.c +++ b/qemu/memory.c @@ -48,6 +48,23 @@ MemoryRegion *memory_map(struct uc_struct *uc, ram_addr_t begin, size_t size, ui return ram; } +MemoryRegion *memory_map_ptr(struct uc_struct *uc, ram_addr_t begin, size_t size, void *ptr) +{ + MemoryRegion *ram = g_new(MemoryRegion, 1); + + memory_region_init_ram_ptr(uc, ram, NULL, "pc.ram", size, ptr); + if (ram->ram_addr == -1) + // out of memory + return NULL; + + memory_region_add_subregion(get_system_memory(uc), begin, ram); + + if (uc->current_cpu) + tlb_flush(uc->current_cpu, 1); + + return ram; +} + void memory_unmap(struct uc_struct *uc, MemoryRegion *mr) { int i; diff --git a/qemu/mips.h b/qemu/mips.h index 21258098..3c789c04 100644 --- a/qemu/mips.h +++ b/qemu/mips.h @@ -8,6 +8,7 @@ #define phys_mem_clean phys_mem_clean_mips #define tb_cleanup tb_cleanup_mips #define memory_map memory_map_mips +#define memory_map_ptr memory_map_ptr_mips #define memory_unmap memory_unmap_mips #define memory_free memory_free_mips #define helper_raise_exception helper_raise_exception_mips diff --git a/qemu/mips64.h b/qemu/mips64.h index d6863cc3..8fb5418e 100644 --- a/qemu/mips64.h +++ b/qemu/mips64.h @@ -8,6 +8,7 @@ #define phys_mem_clean phys_mem_clean_mips64 #define tb_cleanup tb_cleanup_mips64 #define memory_map memory_map_mips64 +#define memory_map_ptr memory_map_ptr_mips64 #define memory_unmap memory_unmap_mips64 #define memory_free memory_free_mips64 #define helper_raise_exception helper_raise_exception_mips64 diff --git a/qemu/mips64el.h b/qemu/mips64el.h index 07118619..8a668325 100644 --- a/qemu/mips64el.h +++ b/qemu/mips64el.h @@ -8,6 +8,7 @@ #define phys_mem_clean phys_mem_clean_mips64el #define tb_cleanup tb_cleanup_mips64el #define memory_map memory_map_mips64el +#define memory_map_ptr memory_map_ptr_mips64el #define memory_unmap memory_unmap_mips64el #define memory_free memory_free_mips64el #define helper_raise_exception helper_raise_exception_mips64el diff --git a/qemu/mipsel.h b/qemu/mipsel.h index 901fb181..0def605f 100644 --- a/qemu/mipsel.h +++ b/qemu/mipsel.h @@ -8,6 +8,7 @@ #define phys_mem_clean phys_mem_clean_mipsel #define tb_cleanup tb_cleanup_mipsel #define memory_map memory_map_mipsel +#define memory_map_ptr memory_map_ptr_mipsel #define memory_unmap memory_unmap_mipsel #define memory_free memory_free_mipsel #define helper_raise_exception helper_raise_exception_mipsel diff --git a/qemu/sparc.h b/qemu/sparc.h index 82bab3a2..59a835f6 100644 --- a/qemu/sparc.h +++ b/qemu/sparc.h @@ -8,6 +8,7 @@ #define phys_mem_clean phys_mem_clean_sparc #define tb_cleanup tb_cleanup_sparc #define memory_map memory_map_sparc +#define memory_map_ptr memory_map_ptr_sparc #define memory_unmap memory_unmap_sparc #define memory_free memory_free_sparc #define helper_raise_exception helper_raise_exception_sparc diff --git a/qemu/sparc64.h b/qemu/sparc64.h index ac1b66e6..043736b6 100644 --- a/qemu/sparc64.h +++ b/qemu/sparc64.h @@ -8,6 +8,7 @@ #define phys_mem_clean phys_mem_clean_sparc64 #define tb_cleanup tb_cleanup_sparc64 #define memory_map memory_map_sparc64 +#define memory_map_ptr memory_map_ptr_sparc64 #define memory_unmap memory_unmap_sparc64 #define memory_free memory_free_sparc64 #define helper_raise_exception helper_raise_exception_sparc64 diff --git a/qemu/unicorn_common.h b/qemu/unicorn_common.h index adfb5f05..2df9ccef 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_map_ptr = memory_map_ptr; uc->memory_unmap = memory_unmap; uc->readonly_mem = memory_region_set_readonly; diff --git a/qemu/x86_64.h b/qemu/x86_64.h index 9c133773..b2656541 100644 --- a/qemu/x86_64.h +++ b/qemu/x86_64.h @@ -8,6 +8,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_map_ptr memory_map_ptr_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 diff --git a/tests/unit/Makefile b/tests/unit/Makefile index ae66d08e..ea3c43f9 100644 --- a/tests/unit/Makefile +++ b/tests/unit/Makefile @@ -4,7 +4,7 @@ CFLAGS += -L ../../ CFLAGS += -lcmocka -lunicorn CFLAGS += -I ../../include -ALL_TESTS = test_sanity test_x86 test_mem_map +ALL_TESTS = test_sanity test_x86 test_mem_map test_mem_map_ptr .PHONY: all all: ${ALL_TESTS} @@ -23,9 +23,7 @@ test: ${ALL_TESTS} test_sanity: test_sanity.c test_x86: test_x86.c test_mem_map: test_mem_map.c +test_mem_map_ptr: test_mem_map_ptr.c ${ALL_TESTS}: gcc ${CFLAGS} -o $@ $^ - - - diff --git a/tests/unit/test_mem_map_ptr.c b/tests/unit/test_mem_map_ptr.c new file mode 100644 index 00000000..1b91211f --- /dev/null +++ b/tests/unit/test_mem_map_ptr.c @@ -0,0 +1,75 @@ +/** + * Unicorn memory API tests + * + * This tests manual pointer-backed memory. + */ +#include "unicorn_test.h" +#include +#include + +/* Called before every test to set up a new instance */ +static int setup(void **state) +{ + uc_engine *uc; + + uc_assert_success(uc_open(UC_ARCH_X86, UC_MODE_32, &uc)); + + *state = uc; + return 0; +} + +/* Called after every test to clean up */ +static int teardown(void **state) +{ + uc_engine *uc = *state; + + uc_assert_success(uc_close(uc)); + + *state = NULL; + return 0; +} + +/******************************************************************************/ + + +/** + * A basic test showing mapping of memory, and reading/writing it + */ +static void test_basic(void **state) +{ + uc_engine *uc = *state; + const uint64_t mem_start = 0x1000; + const uint64_t mem_len = 0x1000; + const uint64_t test_addr = mem_start; + + void *host_mem = calloc(1, mem_len); + + /* Map a region */ + uc_assert_success(uc_mem_map_ptr(uc, mem_start, mem_len, host_mem)); + + /* Write some data to it */ + uc_assert_success(uc_mem_write(uc, test_addr, "test", 4)); + + uint8_t buf[4]; + memset(buf, 0xCC, sizeof(buf)); + + /* Read it back */ + uc_assert_success(uc_mem_read(uc, test_addr, buf, sizeof(buf))); + + /* And make sure it matches what we expect */ + assert_memory_equal(buf, "test", 4); + + /* Unmap the region */ + uc_assert_success(uc_mem_unmap(uc, mem_start, mem_len)); + + assert_memory_equal(buf, host_mem, 4); +} + +int main(void) { +#define test(x) cmocka_unit_test_setup_teardown(x, setup, teardown) + const struct CMUnitTest tests[] = { + test(test_basic), + }; +#undef test + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/uc.c b/uc.c index 7ff96368..3ec5951a 100644 --- a/uc.c +++ b/uc.c @@ -570,8 +570,8 @@ static uc_err _hook_mem_access(uc_engine *uc, uc_hook_type type, return UC_ERR_OK; } -UNICORN_EXPORT -uc_err uc_mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t perms) +// common setup/error checking shared between uc_mem_map and uc_mem_map_ptr +static uc_err mem_map_start(uc_engine *uc, uint64_t address, size_t size, uint32_t perms) { MemoryRegion **regions; @@ -600,7 +600,13 @@ uc_err uc_mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t perms) uc->mapped_blocks = regions; } - uc->mapped_blocks[uc->mapped_block_count] = uc->memory_map(uc, address, size, perms); + return UC_ERR_OK; +} + +// common final step shared by uc_mem_map and uc_mem_map_ptr +static uc_err mem_map_finish(uc_engine *uc, MemoryRegion *block) +{ + uc->mapped_blocks[uc->mapped_block_count] = block; if (uc->mapped_blocks[uc->mapped_block_count] == NULL) return UC_ERR_NOMEM; @@ -609,6 +615,29 @@ uc_err uc_mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t perms) return UC_ERR_OK; } +UNICORN_EXPORT +uc_err uc_mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t perms) +{ + uc_err err; + if ((err = mem_map_start(uc, address, size, perms)) != UC_ERR_OK) + return err; + + return mem_map_finish(uc, uc->memory_map(uc, address, size, perms)); +} + +UNICORN_EXPORT +uc_err uc_mem_map_ptr(uc_engine *uc, uint64_t address, size_t size, void *ptr) +{ + if (ptr == NULL) + return UC_ERR_ARG; + + uc_err err; + if ((err = mem_map_start(uc, address, size, UC_PROT_ALL)) != UC_ERR_OK) + return err; + + return mem_map_finish(uc, uc->memory_map_ptr(uc, address, size, ptr)); +} + // Create a backup copy of the indicated MemoryRegion. // Generally used in prepartion for splitting a MemoryRegion. static uint8_t *copy_region(struct uc_struct *uc, MemoryRegion *mr)