From 8bc1489210f24b2b64b505af048a10da0b25fbea Mon Sep 17 00:00:00 2001 From: lazymio Date: Fri, 11 Feb 2022 21:45:37 +0100 Subject: [PATCH] Implement coprocessor register read/write for arm --- include/unicorn/arm.h | 20 +++++++-- qemu/target/arm/unicorn_arm.c | 80 ++++++++++++++++++++++++++++++++--- samples/sample_arm.c | 37 ++++++++++++++++ tests/unit/test_arm.c | 24 +++++++++++ 4 files changed, 152 insertions(+), 9 deletions(-) diff --git a/include/unicorn/arm.h b/include/unicorn/arm.h index a5b88563..9d352734 100644 --- a/include/unicorn/arm.h +++ b/include/unicorn/arm.h @@ -53,6 +53,19 @@ typedef enum uc_cpu_arm { UC_CPU_ARM_MAX } uc_cpu_arm; +// ARM coprocessor registers, use this with UC_ARM_REG_CP_REG to +// in call to uc_reg_write/read() to access the registers. +typedef struct uc_arm_cp_reg { + int cp; // The coprocessor identifier + int is64; // Is it a 64 bit control register + int sec; // Security state + int crn; // Coprocessor register number + int crm; // Coprocessor register number + int opc1; // Opcode1 + int opc2; // Opcode2 + uint64_t val; // The value to read/write +} uc_arm_cp_reg; + //> ARM registers typedef enum uc_arm_reg { UC_ARM_REG_INVALID = 0, @@ -167,9 +180,9 @@ typedef enum uc_arm_reg { UC_ARM_REG_S30, UC_ARM_REG_S31, - UC_ARM_REG_C1_C0_2, - UC_ARM_REG_C13_C0_2, - UC_ARM_REG_C13_C0_3, + UC_ARM_REG_C1_C0_2, // Depreciated, use UC_ARM_REG_CP_REG instead + UC_ARM_REG_C13_C0_2, // Depreciated, use UC_ARM_REG_CP_REG instead + UC_ARM_REG_C13_C0_3, // Depreciated, use UC_ARM_REG_CP_REG instead UC_ARM_REG_IPSR, UC_ARM_REG_MSP, @@ -196,6 +209,7 @@ typedef enum uc_arm_reg { UC_ARM_REG_XPSR_NZCVQ, UC_ARM_REG_XPSR_G, UC_ARM_REG_XPSR_NZCVQG, + UC_ARM_REG_CP_REG, UC_ARM_REG_ENDING, // <-- mark the end of the list or registers //> alias registers diff --git a/qemu/target/arm/unicorn_arm.c b/qemu/target/arm/unicorn_arm.c index 04c19b37..6d6ae322 100644 --- a/qemu/target/arm/unicorn_arm.c +++ b/qemu/target/arm/unicorn_arm.c @@ -147,8 +147,60 @@ static void v7m_msr_xpsr(CPUARMState *env, uint32_t mask, uint32_t reg, xpsr_write(env, val, xpsrmask); } -static void reg_read(CPUARMState *env, unsigned int regid, void *value) +static uc_err read_cp_reg(CPUARMState *env, uc_arm_cp_reg *cp) { + ARMCPU *cpu = ARM_CPU(env->uc->cpu); + const ARMCPRegInfo *ri = get_arm_cp_reginfo( + cpu->cp_regs, ENCODE_CP_REG(cp->cp, cp->is64, cp->sec, cp->crn, cp->crm, + cp->opc1, cp->opc2)); + + if (!ri) { + return UC_ERR_ARG; + } + + cp->val = read_raw_cp_reg(env, ri); + + if (!cp->is64) { + cp->val = cp->val & 0xFFFFFFFF; + } + + return UC_ERR_OK; +} + +static uc_err write_cp_reg(CPUARMState *env, uc_arm_cp_reg *cp) +{ + ARMCPU *cpu = ARM_CPU(env->uc->cpu); + const ARMCPRegInfo *ri = get_arm_cp_reginfo( + cpu->cp_regs, ENCODE_CP_REG(cp->cp, cp->is64, cp->sec, cp->crn, cp->crm, + cp->opc1, cp->opc2)); + + if (!ri) { + return UC_ERR_ARG; + } + + if (!cp->is64) { + cp->val = cp->val & 0xFFFFFFFF; + } + + if (ri->raw_writefn) { + ri->raw_writefn(env, ri, cp->val); + } else if (ri->writefn) { + ri->writefn(env, ri, cp->val); + } else { + if (cpreg_field_is_64bit(ri)) { + CPREG_FIELD64(env, ri) = cp->val; + } else { + CPREG_FIELD32(env, ri) = cp->val; + } + } + + return UC_ERR_OK; +} + +static uc_err reg_read(CPUARMState *env, unsigned int regid, void *value) +{ + uc_err ret = UC_ERR_OK; + if (regid >= UC_ARM_REG_R0 && regid <= UC_ARM_REG_R12) { *(int32_t *)value = env->regs[regid - UC_ARM_REG_R0]; } else if (regid >= UC_ARM_REG_D0 && regid <= UC_ARM_REG_D31) { @@ -233,14 +285,19 @@ static void reg_read(CPUARMState *env, unsigned int regid, void *value) case UC_ARM_REG_CONTROL: *(uint32_t *)value = helper_v7m_mrs(env, 20); break; + case UC_ARM_REG_CP_REG: + ret = read_cp_reg(env, (uc_arm_cp_reg *)value); + break; } } - return; + return ret; } -static void reg_write(CPUARMState *env, unsigned int regid, const void *value) +static uc_err reg_write(CPUARMState *env, unsigned int regid, const void *value) { + uc_err ret = UC_ERR_OK; + if (regid >= UC_ARM_REG_R0 && regid <= UC_ARM_REG_R12) { env->regs[regid - UC_ARM_REG_R0] = *(uint32_t *)value; } else if (regid >= UC_ARM_REG_D0 && regid <= UC_ARM_REG_D31) { @@ -363,10 +420,13 @@ static void reg_write(CPUARMState *env, unsigned int regid, const void *value) case UC_ARM_REG_XPSR_NZCVQG: v7m_msr_xpsr(env, 0b1100, 3, *(uint32_t *)value); break; + case UC_ARM_REG_CP_REG: + ret = write_cp_reg(env, (uc_arm_cp_reg *)value); + break; } } - return; + return ret; } int arm_reg_read(struct uc_struct *uc, unsigned int *regs, void **vals, @@ -374,11 +434,15 @@ int arm_reg_read(struct uc_struct *uc, unsigned int *regs, void **vals, { CPUARMState *env = &(ARM_CPU(uc->cpu)->env); int i; + uc_err err; for (i = 0; i < count; i++) { unsigned int regid = regs[i]; void *value = vals[i]; - reg_read(env, regid, value); + err = reg_read(env, regid, value); + if (err) { + return err; + } } return 0; @@ -389,11 +453,15 @@ int arm_reg_write(struct uc_struct *uc, unsigned int *regs, void *const *vals, { CPUArchState *env = &(ARM_CPU(uc->cpu)->env); int i; + uc_err err; for (i = 0; i < count; i++) { unsigned int regid = regs[i]; const void *value = vals[i]; - reg_write(env, regid, value); + err = reg_write(env, regid, value); + if (err) { + return err; + } if (regid == UC_ARM_REG_R15) { // force to quit execution and flush TB uc->quit_request = true; diff --git a/samples/sample_arm.c b/samples/sample_arm.c index 40156890..8635a9e8 100644 --- a/samples/sample_arm.c +++ b/samples/sample_arm.c @@ -390,6 +390,40 @@ static void test_thumb_ite() } } +static void test_read_sctlr() +{ + uc_engine *uc; + uc_err err; + uc_arm_cp_reg reg; + + printf("Read the SCTLR register.\n"); + + err = uc_open(UC_ARCH_ARM, UC_MODE_ARM, &uc); + if (err != UC_ERR_OK) { + printf("Failed on uc_emu_start() with error returned: %u\n", err); + } + + // SCTLR. See arm reference. + reg.cp = 15; + reg.is64 = 0; + reg.sec = 0; + reg.crn = 1; + reg.crm = 0; + reg.opc1 = 0; + reg.opc2 = 0; + + err = uc_reg_read(uc, UC_ARM_REG_CP_REG, ®); + if (err != UC_ERR_OK) { + printf("Failed on uc_reg_read() with error returned: %u\n", err); + } + + printf(">>> SCTLR = 0x%" PRIx32 "\n", (uint32_t)reg.val); + printf(">>> SCTLR.IE = %" PRId32 "\n", (uint32_t)((reg.val >> 31) & 1)); + printf(">>> SCTLR.B = %" PRId32 "\n", (uint32_t)((reg.val >> 7) & 1)); + + uc_close(uc); +} + int main(int argc, char **argv, char **envp) { test_arm(); @@ -409,5 +443,8 @@ int main(int argc, char **argv, char **envp) printf("==========================\n"); test_thumb_ite(); + printf("==========================\n"); + test_read_sctlr(); + return 0; } diff --git a/tests/unit/test_arm.c b/tests/unit/test_arm.c index b9dbfc52..2edcb56f 100644 --- a/tests/unit/test_arm.c +++ b/tests/unit/test_arm.c @@ -592,6 +592,29 @@ static void test_arm_mem_access_abort() OK(uc_close(uc)); } +static void test_arm_read_sctlr() +{ + uc_engine *uc; + uc_arm_cp_reg reg; + + OK(uc_open(UC_ARCH_ARM, UC_MODE_ARM, &uc)); + + // SCTLR. See arm reference. + reg.cp = 15; + reg.is64 = 0; + reg.sec = 0; + reg.crn = 1; + reg.crm = 0; + reg.opc1 = 0; + reg.opc2 = 0; + + OK(uc_reg_read(uc, UC_ARM_REG_CP_REG, ®)); + + TEST_CHECK((uint32_t)((reg.val >> 31) & 1) == 0); + + OK(uc_close(uc)); +} + TEST_LIST = {{"test_arm_nop", test_arm_nop}, {"test_arm_thumb_sub", test_arm_thumb_sub}, {"test_armeb_sub", test_armeb_sub}, @@ -609,4 +632,5 @@ TEST_LIST = {{"test_arm_nop", test_arm_nop}, {"test_arm_mrc", test_arm_mrc}, {"test_arm_hflags_rebuilt", test_arm_hflags_rebuilt}, {"test_arm_mem_access_abort", test_arm_mem_access_abort}, + {"test_arm_read_sctlr", test_arm_read_sctlr}, {NULL, NULL}}; \ No newline at end of file