mirror of
https://github.com/yuzu-emu/unicorn
synced 2024-11-24 05:28:29 +00:00
implement host-controlled memory mapping for #261
This commit is contained in:
parent
c6b6ba5daa
commit
6d21ebabea
19 changed files with 160 additions and 7 deletions
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -14,6 +14,7 @@ symbols = (
|
|||
'phys_mem_clean',
|
||||
'tb_cleanup',
|
||||
'memory_map',
|
||||
'memory_map_ptr',
|
||||
'memory_unmap',
|
||||
'memory_free',
|
||||
'helper_raise_exception',
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 $@ $^
|
||||
|
||||
|
||||
|
||||
|
|
75
tests/unit/test_mem_map_ptr.c
Normal file
75
tests/unit/test_mem_map_ptr.c
Normal 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
35
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)
|
||||
|
|
Loading…
Reference in a new issue