diff --git a/include/uc_priv.h b/include/uc_priv.h index f43e7c5d..d038c51b 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -267,6 +267,7 @@ struct uc_struct { unsigned int alloc_hint; /* qemu/exec-vary.c */ TargetPageBits *init_target_page; + int target_bits; // User defined page bits by uc_ctl BounceBuffer bounce; // qemu/cpu-exec.c volatile sig_atomic_t exit_request; // qemu/cpu-exec.c /* qemu/accel/tcg/cpu-exec-common.c */ @@ -339,6 +340,7 @@ struct uc_struct { struct list saved_contexts; // The contexts saved by this uc_struct. bool no_exit_request; // Disable check_exit_request temporarily. A // workaround to treat the IT block as a whole block. + bool init_done; // Whether the initialization is done. }; // Metadata stub for the variable-size cpu context used with uc_context_*() diff --git a/qemu/exec-vary.c b/qemu/exec-vary.c index d74d791c..91ce1410 100644 --- a/qemu/exec-vary.c +++ b/qemu/exec-vary.c @@ -35,7 +35,17 @@ bool set_preferred_target_page_bits(struct uc_struct *uc, int bits) * a particular size. */ #ifdef TARGET_PAGE_BITS_VARY - assert(bits >= TARGET_PAGE_BITS_MIN); + //assert(bits >= TARGET_PAGE_BITS_MIN); + if (uc->init_target_page == NULL) { + uc->init_target_page = calloc(1, sizeof(TargetPageBits)); + } else { + return false; + } + + if (bits < TARGET_PAGE_BITS_MIN) { + return false; + } + if (uc->init_target_page->bits == 0 || uc->init_target_page->bits > bits) { if (uc->init_target_page->decided) { return false; @@ -50,10 +60,15 @@ void finalize_target_page_bits(struct uc_struct *uc) { #ifdef TARGET_PAGE_BITS_VARY if (uc->init_target_page == NULL) { - uc->init_target_page = g_new0(TargetPageBits, 1); + uc->init_target_page = calloc(1, sizeof(TargetPageBits)); } else { return; } + + if (uc->target_bits != 0) { + uc->init_target_page->bits = uc->target_bits; + } + if (uc->init_target_page->bits == 0) { uc->init_target_page->bits = TARGET_PAGE_BITS_MIN; } diff --git a/tests/unit/test_ctl.c b/tests/unit/test_ctl.c index 31594bfc..c5f464a4 100644 --- a/tests/unit/test_ctl.c +++ b/tests/unit/test_ctl.c @@ -156,10 +156,29 @@ static void test_uc_ctl_tb_cache() OK(uc_close(uc)); } +static void test_uc_ctl_change_page_size() +{ + uc_engine *uc; + uc_engine *uc2; + int r_pc; + + OK(uc_open(UC_ARCH_ARM, UC_MODE_ARM, &uc)); + OK(uc_open(UC_ARCH_ARM, UC_MODE_ARM, &uc2)); + + OK(uc_ctl_set_page_size(uc, 4096)); + + OK(uc_mem_map(uc2, 1 << 10, 1 << 10, UC_PROT_ALL)); + uc_assert_err(UC_ERR_ARG, uc_mem_map(uc, 1 << 10, 1 << 10, UC_PROT_ALL)); + + OK(uc_close(uc)); + OK(uc_close(uc2)); +} + TEST_LIST = {{"test_uc_ctl_mode", test_uc_ctl_mode}, {"test_uc_ctl_page_size", test_uc_ctl_page_size}, {"test_uc_ctl_arch", test_uc_ctl_arch}, {"test_uc_ctl_time_out", test_uc_ctl_time_out}, {"test_uc_ctl_exits", test_uc_ctl_exits}, {"test_uc_ctl_tb_cache", test_uc_ctl_tb_cache}, + {"test_uc_ctl_change_page_size", test_uc_ctl_change_page_size}, {NULL, NULL}}; \ No newline at end of file diff --git a/uc.c b/uc.c index 19635f06..25fbc52a 100644 --- a/uc.c +++ b/uc.c @@ -25,6 +25,7 @@ #include "qemu/target/riscv/unicorn.h" #include "qemu/include/qemu/queue.h" +#include "qemu-common.h" UNICORN_EXPORT unsigned int uc_version(unsigned int *major, unsigned int *minor) @@ -136,6 +137,14 @@ bool uc_arch_supported(uc_arch arch) } } +#define UC_INIT(uc) \ + if (unlikely(!(uc)->init_done)) { \ + int __init_ret = uc_init(uc); \ + if (unlikely(__init_ret != UC_ERR_OK)) { \ + return __init_ret; \ + } \ + } + static gint uc_exits_cmp(gconstpointer a, gconstpointer b, gpointer user_data) { uint64_t lhs = *((uint64_t *)a); @@ -150,6 +159,31 @@ static gint uc_exits_cmp(gconstpointer a, gconstpointer b, gpointer user_data) } } +static uc_err uc_init(uc_engine *uc) +{ + + if (uc->init_done) { + return UC_ERR_HANDLE; + } + + uc->exits = g_tree_new_full(uc_exits_cmp, NULL, g_free, NULL); + + if (machine_initialize(uc)) { + return UC_ERR_RESOURCE; + } + + // init fpu softfloat + uc->softfloat_initialize(); + + if (uc->reg_reset) { + uc->reg_reset(uc); + } + + uc->init_done = true; + + return UC_ERR_OK; +} + UNICORN_EXPORT uc_err uc_open(uc_arch arch, uc_mode mode, uc_engine **result) { @@ -175,8 +209,6 @@ uc_err uc_open(uc_arch arch, uc_mode mode, uc_engine **result) QTAILQ_INIT(&uc->address_spaces); - uc->exits = g_tree_new_full(uc_exits_cmp, NULL, g_free, NULL); - switch (arch) { default: break; @@ -315,19 +347,10 @@ uc_err uc_open(uc_arch arch, uc_mode mode, uc_engine **result) return UC_ERR_ARCH; } - if (machine_initialize(uc)) { - return UC_ERR_RESOURCE; - } - - // init fpu softfloat - uc->softfloat_initialize(); + uc->init_done = false; *result = uc; - if (uc->reg_reset) { - uc->reg_reset(uc); - } - return UC_ERR_OK; } else { return UC_ERR_ARCH; @@ -342,6 +365,11 @@ uc_err uc_close(uc_engine *uc) struct hook *hook; MemoryRegion *mr; + if (!uc->init_done) { + free(uc); + return UC_ERR_OK; + } + // Cleanup internally. if (uc->release) { uc->release(uc->tcg_ctx); @@ -425,6 +453,9 @@ UNICORN_EXPORT uc_err uc_reg_read_batch(uc_engine *uc, int *ids, void **vals, int count) { int ret = UC_ERR_OK; + + UC_INIT(uc); + if (uc->reg_read) { ret = uc->reg_read(uc, (unsigned int *)ids, vals, count); } else { @@ -438,6 +469,9 @@ UNICORN_EXPORT uc_err uc_reg_write_batch(uc_engine *uc, int *ids, void *const *vals, int count) { int ret = UC_ERR_OK; + + UC_INIT(uc); + if (uc->reg_write) { ret = uc->reg_write(uc, (unsigned int *)ids, vals, count); } else { @@ -450,12 +484,14 @@ uc_err uc_reg_write_batch(uc_engine *uc, int *ids, void *const *vals, int count) UNICORN_EXPORT uc_err uc_reg_read(uc_engine *uc, int regid, void *value) { + UC_INIT(uc); return uc_reg_read_batch(uc, ®id, &value, 1); } UNICORN_EXPORT uc_err uc_reg_write(uc_engine *uc, int regid, const void *value) { + UC_INIT(uc); return uc_reg_write_batch(uc, ®id, (void *const *)&value, 1); } @@ -485,6 +521,8 @@ uc_err uc_mem_read(uc_engine *uc, uint64_t address, void *_bytes, size_t size) size_t count = 0, len; uint8_t *bytes = _bytes; + UC_INIT(uc); + // qemu cpu_physical_memory_rw() size is an int if (size > INT_MAX) return UC_ERR_ARG; @@ -528,6 +566,8 @@ uc_err uc_mem_write(uc_engine *uc, uint64_t address, const void *_bytes, size_t count = 0, len; const uint8_t *bytes = _bytes; + UC_INIT(uc); + // qemu cpu_physical_memory_rw() size is an int if (size > INT_MAX) return UC_ERR_ARG; @@ -657,6 +697,8 @@ uc_err uc_emu_start(uc_engine *uc, uint64_t begin, uint64_t until, uc->timed_out = false; uc->first_tb = true; + UC_INIT(uc); + switch (uc->arch) { default: break; @@ -777,6 +819,8 @@ uc_err uc_emu_start(uc_engine *uc, uint64_t begin, uint64_t until, UNICORN_EXPORT uc_err uc_emu_stop(uc_engine *uc) { + UC_INIT(uc); + if (uc->emulation_done) { return UC_ERR_OK; } @@ -918,6 +962,8 @@ uc_err uc_mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t perms) { uc_err res; + UC_INIT(uc); + if (uc->mem_redirect) { address = uc->mem_redirect(address); } @@ -937,6 +983,8 @@ uc_err uc_mem_map_ptr(uc_engine *uc, uint64_t address, size_t size, { uc_err res; + UC_INIT(uc); + if (ptr == NULL) { return UC_ERR_ARG; } @@ -961,6 +1009,8 @@ uc_err uc_mmio_map(uc_engine *uc, uint64_t address, size_t size, { uc_err res; + UC_INIT(uc); + if (uc->mem_redirect) { address = uc->mem_redirect(address); } @@ -1167,6 +1217,8 @@ uc_err uc_mem_protect(struct uc_struct *uc, uint64_t address, size_t size, size_t count, len; bool remove_exec = false; + UC_INIT(uc); + if (size == 0) { // trivial case, no change return UC_ERR_OK; @@ -1237,6 +1289,8 @@ uc_err uc_mem_unmap(struct uc_struct *uc, uint64_t address, size_t size) uint64_t addr; size_t count, len; + UC_INIT(uc); + if (size == 0) { // nothing to unmap return UC_ERR_OK; @@ -1323,6 +1377,8 @@ uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, int ret = UC_ERR_OK; int i = 0; + UC_INIT(uc); + struct hook *hook = calloc(1, sizeof(struct hook)); if (hook == NULL) { return UC_ERR_NOMEM; @@ -1439,6 +1495,8 @@ uc_err uc_hook_del(uc_engine *uc, uc_hook hh) int i; struct hook *hook = (struct hook *)hh; + UC_INIT(uc); + // we can't dereference hook->type if hook is invalid // so for now we need to iterate over all possible types to remove the hook // which is less efficient @@ -1554,6 +1612,8 @@ uc_err uc_mem_regions(uc_engine *uc, uc_mem_region **regions, uint32_t *count) uint32_t i; uc_mem_region *r = NULL; + UC_INIT(uc); + *count = uc->mapped_block_count; if (*count) { @@ -1578,6 +1638,8 @@ uc_err uc_mem_regions(uc_engine *uc, uc_mem_region **regions, uint32_t *count) UNICORN_EXPORT uc_err uc_query(uc_engine *uc, uc_query_type type, size_t *result) { + UC_INIT(uc); + switch (type) { default: return UC_ERR_ARG; @@ -1613,6 +1675,8 @@ uc_err uc_context_alloc(uc_engine *uc, uc_context **context) struct uc_context **_context = context; size_t size = uc_context_size(uc); + UC_INIT(uc); + *_context = g_malloc(size); if (*_context) { (*_context)->jmp_env_size = sizeof(*uc->cpu->jmp_env); @@ -1640,6 +1704,7 @@ uc_err uc_free(void *mem) UNICORN_EXPORT size_t uc_context_size(uc_engine *uc) { + UC_INIT(uc); // return the total size of struct uc_context return sizeof(uc_context) + uc->cpu_context_size + sizeof(*uc->cpu->jmp_env); @@ -1648,6 +1713,8 @@ size_t uc_context_size(uc_engine *uc) UNICORN_EXPORT uc_err uc_context_save(uc_engine *uc, uc_context *context) { + UC_INIT(uc); + memcpy(context->data, uc->cpu->env_ptr, context->context_size); memcpy(context->data + context->context_size, uc->cpu->jmp_env, context->jmp_env_size); @@ -1819,6 +1886,8 @@ uc_err uc_context_reg_read_batch(uc_context *ctx, int *ids, void **vals, UNICORN_EXPORT uc_err uc_context_restore(uc_engine *uc, uc_context *context) { + UC_INIT(uc); + memcpy(uc->cpu->env_ptr, context->data, context->context_size); if (list_exists(&uc->saved_contexts, context)) { memcpy(uc->cpu->jmp_env, context->data + context->context_size, @@ -1899,11 +1968,33 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...) case UC_CTL_UC_PAGE_SIZE: { if (rw == UC_CTL_IO_READ) { + + UC_INIT(uc); + uint32_t *page_size = va_arg(args, uint32_t *); *page_size = uc->target_page_size; } else { - // Not implemented. - err = UC_ERR_ARG; + uint32_t page_size = va_arg(args, uint32_t); + int bits = 0; + + if (uc->arch != UC_ARCH_ARM) { + err = UC_ERR_ARG; + break; + } + + if ((page_size & (page_size - 1))) { + err = UC_ERR_ARG; + break; + } + + while (page_size) { + bits++; + page_size >>= 1; + } + + uc->target_bits = bits; + + err = UC_ERR_OK; } break; } @@ -1918,6 +2009,9 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...) } case UC_CTL_UC_EXITS_CNT: { + + UC_INIT(uc); + if (!uc->use_exits) { err = UC_ERR_ARG; } else if (rw == UC_CTL_IO_READ) { @@ -1930,6 +2024,9 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...) } case UC_CTL_UC_EXITS: { + + UC_INIT(uc); + if (!uc->use_exits) { err = UC_ERR_ARG; } else if (rw == UC_CTL_IO_READ) { @@ -1965,6 +2062,9 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...) break; case UC_CTL_TB_REQUEST_CACHE: { + + UC_INIT(uc); + if (rw == UC_CTL_IO_READ_WRITE) { uint64_t addr = va_arg(args, uint64_t); uc_tb *tb = va_arg(args, uc_tb *); @@ -1976,6 +2076,9 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...) } case UC_CTL_TB_REMOVE_CACHE: { + + UC_INIT(uc); + if (rw == UC_CTL_IO_READ) { uint64_t addr = va_arg(args, uint64_t); uc->uc_invalidate_tb(uc, addr, 1);