implement host-controlled memory mapping for #261

This commit is contained in:
Ryan Hileman 2015-11-27 17:25:53 -08:00
parent c6b6ba5daa
commit 6d21ebabea
19 changed files with 160 additions and 7 deletions

View file

@ -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;

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -14,6 +14,7 @@ symbols = (
'phys_mem_clean',
'tb_cleanup',
'memory_map',
'memory_map_ptr',
'memory_unmap',
'memory_free',
'helper_raise_exception',

View file

@ -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);

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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 $@ $^

View file

@ -0,0 +1,75 @@
/**
* Unicorn memory API tests
*
* This tests manual pointer-backed memory.
*/
#include "unicorn_test.h"
#include <stdio.h>
#include <string.h>
/* 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);
}

35
uc.c
View file

@ -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)