Implement UC_HOOK_INSN for aarch64 MRS/MSR/SYS/SYSL
This commit is contained in:
parent
8e2f70a35b
commit
e5207a1363
@ -358,6 +358,28 @@ typedef enum uc_arm64_reg {
|
||||
UC_ARM64_REG_LR = UC_ARM64_REG_X30,
|
||||
} uc_arm64_reg;
|
||||
|
||||
// Callback function for tracing MRS/MSR/SYS/SYSL. If this callback returns
|
||||
// true, the read/write to system registers would be skipped. Note one callback
|
||||
// per instruction is allowed.
|
||||
// @reg: The source/destination register.
|
||||
// @cp_reg: The source/destincation system register.
|
||||
// @user_data: The user data.
|
||||
typedef uint32_t (*uc_cb_insn_sys_t)(uc_engine *uc, uc_arm64_reg reg,
|
||||
const uc_arm64_cp_reg *cp_reg,
|
||||
void *user_data);
|
||||
|
||||
//> ARM64 instructions
|
||||
typedef enum uc_arm64_insn {
|
||||
UC_ARM64_INS_INVALID = 0,
|
||||
|
||||
UC_ARM64_INS_MRS,
|
||||
UC_ARM64_INS_MSR,
|
||||
UC_ARM64_INS_SYS,
|
||||
UC_ARM64_INS_SYSL,
|
||||
|
||||
UC_ARM64_INS_ENDING
|
||||
} uc_arm64_insn;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -2976,4 +2976,5 @@
|
||||
#define ssra_op ssra_op_aarch64
|
||||
#define aarch64_translator_ops aarch64_translator_ops_aarch64
|
||||
#define pred_esz_masks pred_esz_masks_aarch64
|
||||
#define helper_uc_hooksys64 helper_uc_hooksys64_aarch64
|
||||
#endif
|
||||
|
@ -83,6 +83,7 @@ DEF_HELPER_3(set_cp_reg, void, env, ptr, i32)
|
||||
DEF_HELPER_2(get_cp_reg, i32, env, ptr)
|
||||
DEF_HELPER_3(set_cp_reg64, void, env, ptr, i64)
|
||||
DEF_HELPER_2(get_cp_reg64, i64, env, ptr)
|
||||
DEF_HELPER_3(uc_hooksys64, i32, env, i32, ptr)
|
||||
|
||||
DEF_HELPER_2(get_r13_banked, i32, env, i32)
|
||||
DEF_HELPER_3(set_r13_banked, void, env, i32, i32)
|
||||
|
@ -932,3 +932,38 @@ uint32_t HELPER(ror_cc)(CPUARMState *env, uint32_t x, uint32_t i)
|
||||
return ((uint32_t)x >> shift) | (x << (32 - shift));
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t HELPER(uc_hooksys64)(CPUARMState *env, uint32_t insn, void *hk)
|
||||
{
|
||||
uc_arm64_reg uc_rt;
|
||||
struct hook *hook = (struct hook*)hk;
|
||||
uc_arm64_cp_reg cp_reg;
|
||||
uint32_t rt;
|
||||
|
||||
if (hook->to_delete) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rt = extract32(insn, 0, 5);
|
||||
cp_reg.op0 = extract32(insn, 19, 2);
|
||||
cp_reg.op1 = extract32(insn, 16, 3);
|
||||
cp_reg.crn = extract32(insn, 12, 4);
|
||||
cp_reg.crm = extract32(insn, 8, 4);
|
||||
cp_reg.op2 = extract32(insn, 5, 3);
|
||||
|
||||
if (rt <= 28 && rt >= 0) {
|
||||
uc_rt = UC_ARM64_REG_X0 + rt;
|
||||
cp_reg.val = env->xregs[rt];
|
||||
} else if (rt == 29 ) {
|
||||
uc_rt = UC_ARM64_REG_X29;
|
||||
cp_reg.val = env->xregs[29];
|
||||
} else if (rt == 30) {
|
||||
uc_rt = UC_ARM64_REG_X30;
|
||||
cp_reg.val = env->xregs[30];
|
||||
} else {
|
||||
uc_rt = UC_ARM64_REG_XZR;
|
||||
cp_reg.val = 0;
|
||||
}
|
||||
|
||||
return ((uc_cb_insn_sys_t)(hook->callback))(env->uc, uc_rt, &cp_reg, hook->user_data);
|
||||
}
|
@ -1748,6 +1748,38 @@ static void gen_set_nzcv(TCGContext *tcg_ctx, TCGv_i64 tcg_rt)
|
||||
tcg_temp_free_i32(tcg_ctx, nzcv);
|
||||
}
|
||||
|
||||
|
||||
static TCGLabel *gen_hook_sys(DisasContext *s, uint32_t insn, struct hook *hk)
|
||||
{
|
||||
uc_engine *uc = s->uc;
|
||||
TCGContext *tcg_ctx = uc->tcg_ctx;
|
||||
TCGLabel *label = gen_new_label(tcg_ctx);
|
||||
TCGv_i32 tcg_skip, tcg_insn;
|
||||
TCGv_ptr tcg_hk;
|
||||
|
||||
tcg_skip = tcg_temp_new_i32(tcg_ctx);
|
||||
tcg_insn = tcg_const_i32(tcg_ctx, insn);
|
||||
tcg_hk = tcg_const_ptr(tcg_ctx, (void*)hk);
|
||||
|
||||
// Only one hook per instruction for SYS/SYSL/MRS/MSR is allowed.
|
||||
// This is intended and may be extended if it's really necessary.
|
||||
gen_helper_uc_hooksys64(tcg_ctx, tcg_skip, tcg_ctx->cpu_env, tcg_insn, tcg_hk);
|
||||
|
||||
tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, tcg_skip, 0, label);
|
||||
|
||||
tcg_temp_free_i32(tcg_ctx, tcg_skip);
|
||||
tcg_temp_free_i32(tcg_ctx, tcg_insn);
|
||||
tcg_temp_free_ptr(tcg_ctx, tcg_hk);
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
static void may_gen_set_label(DisasContext *s, TCGLabel *label) {
|
||||
if (label) {
|
||||
gen_set_label(s->uc->tcg_ctx, label);
|
||||
}
|
||||
}
|
||||
|
||||
/* MRS - move from system register
|
||||
* MSR (register) - move to system register
|
||||
* SYS
|
||||
@ -1762,6 +1794,52 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread,
|
||||
TCGContext *tcg_ctx = s->uc->tcg_ctx;
|
||||
const ARMCPRegInfo *ri;
|
||||
TCGv_i64 tcg_rt;
|
||||
uc_engine *uc = s->uc;
|
||||
TCGLabel *label = NULL;
|
||||
struct hook *hook;
|
||||
HOOK_FOREACH_VAR_DECLARE;
|
||||
|
||||
HOOK_FOREACH(uc, hook, UC_HOOK_INSN) {
|
||||
if (hook->to_delete)
|
||||
continue;
|
||||
|
||||
if (!HOOK_BOUND_CHECK(hook, s->pc_curr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (hook->insn) {
|
||||
case UC_ARM64_INS_MRS: {
|
||||
if (isread && (op0 == 2 || op0 == 3)) {
|
||||
label = gen_hook_sys(s, insn, hook);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case UC_ARM64_INS_MSR: {
|
||||
if (!isread && (op0 == 2 || op0 == 3)) {
|
||||
label = gen_hook_sys(s, insn, hook);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case UC_ARM64_INS_SYSL: {
|
||||
if (isread && op0 == 1) {
|
||||
label = gen_hook_sys(s, insn, hook);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case UC_ARM64_INS_SYS: {
|
||||
if (!isread && op0 == 1) {
|
||||
label = gen_hook_sys(s, insn, hook);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (label) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ri = get_arm_cp_reginfo(s->cp_regs,
|
||||
ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP,
|
||||
@ -1775,12 +1853,14 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread,
|
||||
"system register op0:%d op1:%d crn:%d crm:%d op2:%d\n",
|
||||
isread ? "read" : "write", op0, op1, crn, crm, op2);
|
||||
unallocated_encoding(s);
|
||||
may_gen_set_label(s, label);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check access permissions */
|
||||
if (!cp_access_ok(s->current_el, ri, isread)) {
|
||||
unallocated_encoding(s);
|
||||
may_gen_set_label(s, label);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1812,6 +1892,7 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread,
|
||||
/* Handle special cases first */
|
||||
switch (ri->type & ~(ARM_CP_FLAG_MASK & ~ARM_CP_SPECIAL)) {
|
||||
case ARM_CP_NOP:
|
||||
may_gen_set_label(s, label);
|
||||
return;
|
||||
case ARM_CP_NZCV:
|
||||
tcg_rt = cpu_reg(s, rt);
|
||||
@ -1820,6 +1901,7 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread,
|
||||
} else {
|
||||
gen_set_nzcv(tcg_ctx, tcg_rt);
|
||||
}
|
||||
may_gen_set_label(s, label);
|
||||
return;
|
||||
case ARM_CP_CURRENTEL:
|
||||
/* Reads as current EL value from pstate, which is
|
||||
@ -1827,18 +1909,22 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread,
|
||||
*/
|
||||
tcg_rt = cpu_reg(s, rt);
|
||||
tcg_gen_movi_i64(tcg_ctx, tcg_rt, s->current_el << 2);
|
||||
may_gen_set_label(s, label);
|
||||
return;
|
||||
case ARM_CP_DC_ZVA:
|
||||
/* Writes clear the aligned block of memory which rt points into. */
|
||||
tcg_rt = clean_data_tbi(s, cpu_reg(s, rt));
|
||||
gen_helper_dc_zva(tcg_ctx, tcg_ctx->cpu_env, tcg_rt);
|
||||
may_gen_set_label(s, label);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if ((ri->type & ARM_CP_FPU) && !fp_access_check(s)) {
|
||||
may_gen_set_label(s, label);
|
||||
return;
|
||||
} else if ((ri->type & ARM_CP_SVE) && !sve_access_check(s)) {
|
||||
may_gen_set_label(s, label);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1858,6 +1944,7 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread,
|
||||
} else {
|
||||
if (ri->type & ARM_CP_CONST) {
|
||||
/* If not forbidden by access permissions, treat as WI */
|
||||
may_gen_set_label(s, label);
|
||||
return;
|
||||
} else if (ri->writefn) {
|
||||
TCGv_ptr tmpptr;
|
||||
@ -1888,6 +1975,8 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread,
|
||||
*/
|
||||
s->base.is_jmp = DISAS_UPDATE;
|
||||
}
|
||||
|
||||
may_gen_set_label(s, label);
|
||||
}
|
||||
|
||||
/* System
|
||||
|
@ -4297,6 +4297,7 @@ usra_op \
|
||||
ssra_op \
|
||||
aarch64_translator_ops \
|
||||
pred_esz_masks \
|
||||
helper_uc_hooksys64 \
|
||||
"
|
||||
|
||||
riscv32_SYMBOLS="
|
||||
|
@ -159,9 +159,46 @@ static void test_arm64_read_sctlr()
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static uint32_t test_arm64_mrs_hook_cb(uc_engine *uc, uc_arm64_reg reg,
|
||||
const uc_arm64_cp_reg *cp_reg)
|
||||
{
|
||||
uint64_t r_x2 = 0x114514;
|
||||
|
||||
OK(uc_reg_write(uc, reg, &r_x2));
|
||||
|
||||
// Skip
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void test_arm64_mrs_hook()
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_hook hk;
|
||||
uint64_t r_x2;
|
||||
// mrs x2, tpidrro_el0
|
||||
char code[] = "\x62\xd0\x3b\xd5";
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_ARM64, UC_MODE_LITTLE_ENDIAN | UC_MODE_ARM,
|
||||
code, sizeof(code) - 1, UC_CPU_AARCH64_A72);
|
||||
|
||||
OK(uc_hook_add(uc, &hk, UC_HOOK_INSN, (void *)test_arm64_mrs_hook_cb, NULL,
|
||||
1, 0, UC_ARM64_INS_MRS));
|
||||
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM64_REG_X2, &r_x2));
|
||||
|
||||
TEST_CHECK(r_x2 == 0x114514);
|
||||
|
||||
OK(uc_hook_del(uc, hk));
|
||||
|
||||
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},
|
||||
{"test_arm64_mrs_hook", test_arm64_mrs_hook},
|
||||
{NULL, NULL}};
|
||||
|
Loading…
Reference in New Issue
Block a user