diff --git a/include/unicorn/arm64.h b/include/unicorn/arm64.h index 9adece5d..6290a8fb 100644 --- a/include/unicorn/arm64.h +++ b/include/unicorn/arm64.h @@ -23,6 +23,17 @@ typedef enum uc_cpu_aarch64 { UC_CPU_AARCH64_MAX } uc_cpu_aarch64; +// ARM64 coprocessor registers, use this with UC_ARM64_REG_CP_REG to +// in call to uc_reg_write/read() to access the registers. +typedef struct uc_arm64_cp_reg { + int crn; // Coprocessor register number + int crm; // Coprocessor register number + int op0; // Opcode0 + int op1; // Opcode1 + int op2; // Opcode2 + uint64_t val; // The value to read/write +} uc_arm64_cp_reg; + //> ARM64 registers typedef enum uc_arm64_reg { UC_ARM64_REG_INVALID = 0, @@ -293,26 +304,26 @@ typedef enum uc_arm64_reg { UC_ARM64_REG_CPACR_EL1, - //> thread registers + //> thread registers, depreciated, use UC_ARM64_REG_CP_REG instead UC_ARM64_REG_TPIDR_EL0, UC_ARM64_REG_TPIDRRO_EL0, UC_ARM64_REG_TPIDR_EL1, UC_ARM64_REG_PSTATE, - //> exception link registers + //> exception link registers, depreciated, use UC_ARM64_REG_CP_REG instead UC_ARM64_REG_ELR_EL0, UC_ARM64_REG_ELR_EL1, UC_ARM64_REG_ELR_EL2, UC_ARM64_REG_ELR_EL3, - //> stack pointers registers + //> stack pointers registers, depreciated, use UC_ARM64_REG_CP_REG instead UC_ARM64_REG_SP_EL0, UC_ARM64_REG_SP_EL1, UC_ARM64_REG_SP_EL2, UC_ARM64_REG_SP_EL3, - //> other CP15 registers + //> other CP15 registers, depreciated, use UC_ARM64_REG_CP_REG instead UC_ARM64_REG_TTBR0_EL1, UC_ARM64_REG_TTBR1_EL1, @@ -335,6 +346,8 @@ typedef enum uc_arm64_reg { UC_ARM64_REG_VBAR_EL2, UC_ARM64_REG_VBAR_EL3, + UC_ARM64_REG_CP_REG, + UC_ARM64_REG_ENDING, // <-- mark the end of the list of registers //> alias registers diff --git a/qemu/target/arm/unicorn_aarch64.c b/qemu/target/arm/unicorn_aarch64.c index f9ba80e0..691ed4cb 100644 --- a/qemu/target/arm/unicorn_aarch64.c +++ b/qemu/target/arm/unicorn_aarch64.c @@ -6,6 +6,7 @@ #include "unicorn/unicorn.h" #include "sysemu/cpus.h" #include "cpu.h" +#include "kvm-consts.h" #include "unicorn_common.h" #include "uc_priv.h" #include "unicorn.h" @@ -90,8 +91,52 @@ void arm64_reg_reset(struct uc_struct *uc) env->pc = 0; } -static void reg_read(CPUARMState *env, unsigned int regid, void *value) +static uc_err read_cp_reg(CPUARMState *env, uc_arm64_cp_reg *cp) { + ARMCPU *cpu = ARM_CPU(env->uc->cpu); + const ARMCPRegInfo *ri = get_arm_cp_reginfo( + cpu->cp_regs, ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP, cp->crn, + cp->crm, cp->op0, cp->op1, cp->op2)); + + if (!ri) { + return UC_ERR_ARG; + } + + cp->val = read_raw_cp_reg(env, ri); + + return UC_ERR_OK; +} + +static uc_err write_cp_reg(CPUARMState *env, uc_arm64_cp_reg *cp) +{ + ARMCPU *cpu = ARM_CPU(env->uc->cpu); + const ARMCPRegInfo *ri = get_arm_cp_reginfo( + cpu->cp_regs, ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP, cp->crn, + cp->crm, cp->op0, cp->op1, cp->op2)); + + if (!ri) { + return UC_ERR_ARG; + } + + 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_ARM64_REG_V0 && regid <= UC_ARM64_REG_V31) { regid += UC_ARM64_REG_Q0 - UC_ARM64_REG_V0; } @@ -172,14 +217,19 @@ static void reg_read(CPUARMState *env, unsigned int regid, void *value) case UC_ARM64_REG_MAIR_EL1: *(uint64_t *)value = env->cp15.mair_el[1]; break; + case UC_ARM64_REG_CP_REG: + ret = read_cp_reg(env, (uc_arm64_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_ARM64_REG_V0 && regid <= UC_ARM64_REG_V31) { regid += UC_ARM64_REG_Q0 - UC_ARM64_REG_V0; } @@ -260,10 +310,13 @@ static void reg_write(CPUARMState *env, unsigned int regid, const void *value) case UC_ARM64_REG_MAIR_EL1: env->cp15.mair_el[1] = *(uint64_t *)value; break; + case UC_ARM64_REG_CP_REG: + ret = write_cp_reg(env, (uc_arm64_cp_reg *)value); + break; } } - return; + return ret; } int arm64_reg_read(struct uc_struct *uc, unsigned int *regs, void **vals, @@ -271,14 +324,18 @@ int arm64_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; + return UC_ERR_OK; } int arm64_reg_write(struct uc_struct *uc, unsigned int *regs, void *const *vals, @@ -286,11 +343,15 @@ int arm64_reg_write(struct uc_struct *uc, unsigned int *regs, void *const *vals, { CPUARMState *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_ARM64_REG_PC) { // force to quit execution and flush TB uc->quit_request = true; @@ -298,7 +359,7 @@ int arm64_reg_write(struct uc_struct *uc, unsigned int *regs, void *const *vals, } } - return 0; + return UC_ERR_OK; } DEFAULT_VISIBILITY @@ -312,11 +373,15 @@ int arm64_context_reg_read(struct uc_context *ctx, unsigned int *regs, { CPUARMState *env = (CPUARMState *)ctx->data; 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; @@ -333,11 +398,15 @@ int arm64_context_reg_write(struct uc_context *ctx, unsigned int *regs, { CPUARMState *env = (CPUARMState *)ctx->data; 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; + } } return 0; diff --git a/samples/sample_arm64.c b/samples/sample_arm64.c index ba09b77a..0cfb2d67 100644 --- a/samples/sample_arm64.c +++ b/samples/sample_arm64.c @@ -194,6 +194,44 @@ static void test_arm64eb(void) uc_close(uc); } +static void test_arm64_sctlr() +{ + uc_engine *uc; + uc_err err; + uc_arm64_cp_reg reg; + + printf("Read the SCTLR register.\n"); + + err = uc_open(UC_ARCH_ARM64, UC_MODE_LITTLE_ENDIAN | UC_MODE_ARM, &uc); + if (err != UC_ERR_OK) { + printf("Failed on uc_emu_start() with error returned: %u\n", err); + } + + // SCTLR_EL1. See arm reference. + reg.crn = 1; + reg.crm = 0; + reg.op0 = 0b11; + reg.op1 = 0; + reg.op2 = 0; + + err = uc_reg_read(uc, UC_ARM64_REG_CP_REG, ®); + if (err != UC_ERR_OK) { + printf("Failed on uc_reg_read() with error returned: %u\n", err); + } + + printf(">>> SCTLR_EL1 = 0x%" PRIx64 "\n", reg.val); + + reg.op1 = 0b100; + err = uc_reg_read(uc, UC_ARM64_REG_CP_REG, ®); + if (err != UC_ERR_OK) { + printf("Failed on uc_reg_read() with error returned: %u\n", err); + } + + printf(">>> SCTLR_EL2 = 0x%" PRIx64 "\n", reg.val); + + uc_close(uc); +} + int main(int argc, char **argv, char **envp) { test_arm64_mem_fetch(); @@ -202,5 +240,8 @@ int main(int argc, char **argv, char **envp) printf("-------------------------\n"); test_arm64eb(); + printf("-------------------------\n"); + test_arm64_sctlr(); + return 0; } diff --git a/tests/unit/test_arm64.c b/tests/unit/test_arm64.c index 0c177164..ac75d34f 100644 --- a/tests/unit/test_arm64.c +++ b/tests/unit/test_arm64.c @@ -138,8 +138,30 @@ static void test_arm64_v8_pac() OK(uc_close(uc)); } +static void test_arm64_read_sctlr() +{ + uc_engine *uc; + uc_arm64_cp_reg reg; + + OK(uc_open(UC_ARCH_ARM64, UC_MODE_LITTLE_ENDIAN | UC_MODE_ARM, &uc)); + + // SCTLR_EL1. See arm reference. + reg.crn = 1; + reg.crm = 0; + reg.op0 = 0b11; + reg.op1 = 0; + reg.op2 = 0; + + OK(uc_reg_read(uc, UC_ARM64_REG_CP_REG, ®)); + + TEST_CHECK((reg.val >> 58) == 0); + + OK(uc_close(uc)); +} + TEST_LIST = {{"test_arm64_until", test_arm64_until}, {"test_arm64_code_patching", test_arm64_code_patching}, {"test_arm64_code_patching_count", test_arm64_code_patching_count}, {"test_arm64_v8_pac", test_arm64_v8_pac}, + {"test_arm64_read_sctlr", test_arm64_read_sctlr}, {NULL, NULL}};