diff --git a/include/uc_priv.h b/include/uc_priv.h index 7af9c615..030fadce 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -168,6 +168,16 @@ typedef void (*uc_add_inline_hook_t)(struct uc_struct *uc, struct hook *hk, // Delete a hook from helper_table typedef void (*uc_del_inline_hook_t)(struct uc_struct *uc, struct hook *hk); +// Return the size of a CPU context +typedef size_t (*uc_context_size_t)(struct uc_struct *uc); + +// Generate a CPU context +typedef uc_err (*uc_context_save_t)(struct uc_struct *uc, uc_context *context); + +// Restore a CPU context +typedef uc_err (*uc_context_restore_t)(struct uc_struct *uc, + uc_context *context); + // hook list offsets // // The lowest 6 bits are used for hook type index while the others @@ -285,6 +295,10 @@ struct uc_struct { uc_add_inline_hook_t add_inline_hook; uc_del_inline_hook_t del_inline_hook; + uc_context_size_t context_size; + uc_context_save_t context_save; + uc_context_restore_t context_restore; + /* only 1 cpu in unicorn, do not need current_cpu to handle current running cpu. */ CPUState *cpu; diff --git a/qemu/target/arm/unicorn_arm.c b/qemu/target/arm/unicorn_arm.c index c8531426..bb39b348 100644 --- a/qemu/target/arm/unicorn_arm.c +++ b/qemu/target/arm/unicorn_arm.c @@ -621,6 +621,145 @@ static int arm_cpus_init(struct uc_struct *uc, const char *cpu_model) return 0; } +static size_t uc_arm_context_size(struct uc_struct *uc) +{ + size_t ret = offsetof(CPUARMState, cpu_watchpoint); + ARMCPU *cpu = (ARMCPU *)uc->cpu; + CPUARMState *env = (CPUARMState *)&cpu->env; + uint32_t nr; + +#define ARM_ENV_CHECK(field) \ + if (field) { \ + ret += sizeof(uint32_t) * (nr + 1); \ + } else { \ + ret += sizeof(uint32_t); \ + } + + // /* PMSAv7 MPU */ + // struct { + // uint32_t *drbar; + // uint32_t *drsr; + // uint32_t *dracr; + // uint32_t rnr[M_REG_NUM_BANKS]; + // } pmsav7; + // /* PMSAv8 MPU */ + // struct { + // /* The PMSAv8 implementation also shares some PMSAv7 config + // * and state: + // * pmsav7.rnr (region number register) + // * pmsav7_dregion (number of configured regions) + // */ + // uint32_t *rbar[M_REG_NUM_BANKS]; + // uint32_t *rlar[M_REG_NUM_BANKS]; + // uint32_t mair0[M_REG_NUM_BANKS]; + // uint32_t mair1[M_REG_NUM_BANKS]; + // } pmsav8; + nr = cpu->pmsav7_dregion; + ARM_ENV_CHECK(env->pmsav7.drbar) + ARM_ENV_CHECK(env->pmsav7.drsr) + ARM_ENV_CHECK(env->pmsav7.dracr) + ARM_ENV_CHECK(env->pmsav8.rbar[M_REG_NS]) + ARM_ENV_CHECK(env->pmsav8.rbar[M_REG_S]) + ARM_ENV_CHECK(env->pmsav8.rlar[M_REG_NS]) + ARM_ENV_CHECK(env->pmsav8.rlar[M_REG_S]) + + // /* v8M SAU */ + // struct { + // uint32_t *rbar; + // uint32_t *rlar; + // uint32_t rnr; + // uint32_t ctrl; + // } sau; + nr = cpu->sau_sregion; + ARM_ENV_CHECK(env->sau.rbar) + ARM_ENV_CHECK(env->sau.rlar) +#undef ARM_ENV_CHECK + // These fields are never used: + // void *nvic; + // const struct arm_boot_info *boot_info; + // void *gicv3state; + return ret; +} + +static uc_err uc_arm_context_save(struct uc_struct *uc, uc_context *context) +{ + char *p = NULL; + ARMCPU *cpu = (ARMCPU *)uc->cpu; + CPUARMState *env = (CPUARMState *)&cpu->env; + uint32_t nr = 0; + +#define ARM_ENV_SAVE(field) \ + if (!field) { \ + *(uint32_t *)p = 0; \ + p += sizeof(uint32_t); \ + } else { \ + *(uint32_t *)p = nr; \ + p += sizeof(uint32_t); \ + memcpy(p, (void *)field, sizeof(uint32_t) * nr); \ + p += sizeof(uint32_t) * nr; \ + } + p = context->data; + memcpy(p, uc->cpu->env_ptr, uc->cpu_context_size); + p += uc->cpu_context_size; + + nr = cpu->pmsav7_dregion; + ARM_ENV_SAVE(env->pmsav7.drbar) + ARM_ENV_SAVE(env->pmsav7.drsr) + ARM_ENV_SAVE(env->pmsav7.dracr) + ARM_ENV_SAVE(env->pmsav8.rbar[M_REG_NS]) + ARM_ENV_SAVE(env->pmsav8.rbar[M_REG_S]) + ARM_ENV_SAVE(env->pmsav8.rlar[M_REG_NS]) + ARM_ENV_SAVE(env->pmsav8.rlar[M_REG_S]) + + nr = cpu->sau_sregion; + ARM_ENV_SAVE(env->sau.rbar) + ARM_ENV_SAVE(env->sau.rlar) + +#undef ARM_ENV_SAVE + return UC_ERR_OK; +} + +static uc_err uc_arm_context_restore(struct uc_struct *uc, uc_context *context) +{ + char *p = NULL; + ARMCPU *cpu = (ARMCPU *)uc->cpu; + CPUARMState *env = (CPUARMState *)&cpu->env; + uint32_t nr, ctx_nr; + +#define ARM_ENV_RESTORE(field) \ + ctx_nr = *(uint32_t *)p; \ + if (ctx_nr != 0) { \ + p += sizeof(uint32_t); \ + if (field && ctx_nr == nr) { \ + memcpy(field, p, sizeof(uint32_t) * ctx_nr); \ + } \ + p += sizeof(uint32_t) * ctx_nr; \ + } else { \ + p += sizeof(uint32_t); \ + } + + p = context->data; + memcpy(uc->cpu->env_ptr, p, uc->cpu_context_size); + p += uc->cpu_context_size; + + nr = cpu->pmsav7_dregion; + ARM_ENV_RESTORE(env->pmsav7.drbar) + ARM_ENV_RESTORE(env->pmsav7.drsr) + ARM_ENV_RESTORE(env->pmsav7.dracr) + ARM_ENV_RESTORE(env->pmsav8.rbar[M_REG_NS]) + ARM_ENV_RESTORE(env->pmsav8.rbar[M_REG_S]) + ARM_ENV_RESTORE(env->pmsav8.rlar[M_REG_NS]) + ARM_ENV_RESTORE(env->pmsav8.rlar[M_REG_S]) + + nr = cpu->sau_sregion; + ARM_ENV_RESTORE(env->sau.rbar) + ARM_ENV_RESTORE(env->sau.rlar) + +#undef ARM_ENV_RESTORE + + return UC_ERR_OK; +} + void arm_uc_init(struct uc_struct *uc) { uc->reg_read = arm_reg_read; @@ -634,5 +773,8 @@ void arm_uc_init(struct uc_struct *uc) uc->cpus_init = arm_cpus_init; uc->opcode_hook_invalidate = arm_opcode_hook_invalidate; uc->cpu_context_size = offsetof(CPUARMState, cpu_watchpoint); + uc->context_size = uc_arm_context_size; + uc->context_save = uc_arm_context_save; + uc->context_restore = uc_arm_context_restore; uc_common_init(uc); } diff --git a/tests/unit/test_arm.c b/tests/unit/test_arm.c index 1efd5682..66898e62 100644 --- a/tests/unit/test_arm.c +++ b/tests/unit/test_arm.c @@ -751,6 +751,30 @@ static void test_armeb_ldrb(void) OK(uc_close(uc)); } +static void test_arm_context_save(void) +{ + uc_engine *uc; + uc_engine *uc2; + char code[] = "\x83\xb0"; // sub sp, #0xc + uc_context *ctx; + + uc_common_setup(&uc, UC_ARCH_ARM, UC_MODE_THUMB, code, sizeof(code) - 1, + UC_CPU_ARM_CORTEX_R5); + + OK(uc_context_alloc(uc, &ctx)); + OK(uc_context_save(uc, ctx)); + OK(uc_context_restore(uc, ctx)); + + uc_common_setup(&uc2, UC_ARCH_ARM, UC_MODE_THUMB, code, sizeof(code) - 1, + UC_CPU_ARM_CORTEX_A7); // Note the different CPU model + + OK(uc_context_restore(uc2, ctx)); + + OK(uc_context_free(ctx)); + OK(uc_close(uc)); + OK(uc_close(uc2)); +} + TEST_LIST = {{"test_arm_nop", test_arm_nop}, {"test_arm_thumb_sub", test_arm_thumb_sub}, {"test_armeb_sub", test_armeb_sub}, @@ -773,4 +797,5 @@ TEST_LIST = {{"test_arm_nop", test_arm_nop}, {"test_arm_be_cpsr_sctlr", test_arm_be_cpsr_sctlr}, {"test_arm_switch_endian", test_arm_switch_endian}, {"test_armeb_ldrb", test_armeb_ldrb}, + {"test_arm_context_save", test_arm_context_save}, {NULL, NULL}}; \ No newline at end of file diff --git a/uc.c b/uc.c index 82471965..f69cf3a7 100644 --- a/uc.c +++ b/uc.c @@ -2,6 +2,7 @@ /* By Nguyen Anh Quynh , 2015 */ /* Modified for Unicorn Engine by Chen Huitao, 2020 */ +#include "unicorn/unicorn.h" #if defined(UNICORN_HAS_OSXKERNEL) #include #else @@ -1861,7 +1862,7 @@ uc_err uc_context_alloc(uc_engine *uc, uc_context **context) *_context = g_malloc(size); if (*_context) { - (*_context)->context_size = uc->cpu_context_size; + (*_context)->context_size = size - sizeof(uc_context); (*_context)->arch = uc->arch; (*_context)->mode = uc->mode; return UC_ERR_OK; @@ -1881,8 +1882,13 @@ 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; + + if (!uc->context_size) { + // return the total size of struct uc_context + return sizeof(uc_context) + uc->cpu_context_size; + } else { + return sizeof(uc_context) + uc->context_size(uc); + } } UNICORN_EXPORT @@ -1890,9 +1896,12 @@ uc_err uc_context_save(uc_engine *uc, uc_context *context) { UC_INIT(uc); - memcpy(context->data, uc->cpu->env_ptr, context->context_size); - - return UC_ERR_OK; + if (!uc->context_save) { + memcpy(context->data, uc->cpu->env_ptr, context->context_size); + return UC_ERR_OK; + } else { + return uc->context_save(uc, context); + } } UNICORN_EXPORT @@ -2064,9 +2073,12 @@ uc_err uc_context_restore(uc_engine *uc, uc_context *context) { UC_INIT(uc); - memcpy(uc->cpu->env_ptr, context->data, context->context_size); - - return UC_ERR_OK; + if (!uc->context_restore) { + memcpy(uc->cpu->env_ptr, context->data, context->context_size); + return UC_ERR_OK; + } else { + return uc->context_restore(uc, context); + } } UNICORN_EXPORT