if (read_user_insn(pc, &insn) != 0)
{
regs->pc += 4;
return false;
}
// --- 第一部分:跳转 / 调用指令 ---
if ((insn & 0xFC000000) == 0x14000000) { regs->pc = pc + sign_extend64((s64)(insn & 0x3FFFFFF) << 2, 27); return true; }
if ((insn & 0xFC000000) == 0x94000000) { regs->regs[30] = pc + 4; regs->pc = pc + sign_extend64((s64)(insn & 0x3FFFFFF) << 2, 27); return true; }
if ((insn & 0xFE1F03E0) == 0xD61F0000) {
u32 rn = (insn >> 5) & 0x1F, opc = (insn >> 21) & 0x3;
if (opc == 1) regs->regs[30] = pc + 4;
if (opc <= 2) { regs->pc = regs->regs[rn]; return true; }
}
if ((insn & 0xFF000010) == 0x54000000) {
s64 offset = sign_extend64((s64)((insn >> 5) & 0x7FFFF) << 2, 20);
regs->pc = eval_cond(regs->pstate, insn & 0xF) ? (pc + offset) : (pc + 4);
return true;
}
if ((insn & 0x7E000000) == 0x34000000) {
u32 rt = insn & 0x1F; u64 val = ((insn >> 31) & 1) ? REG_READ(regs, rt) : (u32)REG_READ(regs, rt);
bool jump = ((insn >> 24) & 1) ? (val != 0) : (val == 0);
regs->pc = jump ? (pc + sign_extend64((s64)((insn >> 5) & 0x7FFFF) << 2, 20)) : (pc + 4);
return true;
}
if ((insn & 0x7E000000) == 0x36000000) {
u32 rt = insn & 0x1F, bit_pos = ((insn >> 31) & 1) << 5 | ((insn >> 19) & 0x1F);
bool bit_set = (REG_READ(regs, rt) >> bit_pos) & 1;
bool jump = ((insn >> 24) & 1) ? bit_set : !bit_set;
regs->pc = jump ? (pc + sign_extend64((s64)((insn >> 5) & 0x3FFF) << 2, 15)) : (pc + 4);
return true;
}
// --- 第二部分:Load/Store 指令 ---
if ((insn & 0x0A000000) == 0x08000000)
{
u32 size = (insn >> 30) & 0x3;
bool is_simd = ((insn >> 26) & 1) != 0;
// LDR literal (PC relative)
if (!is_simd && (insn & 0x3B000000) == 0x18000000) {
u32 rt = insn & 0x1F;
u64 addr = pc + sign_extend64((s64)((insn >> 5) & 0x7FFFF) << 2, 20);
if (((insn >> 30) & 0x3) == 2) { u32 t; if (user_read_u32(addr, &t)) goto fault; REG_WRITE(regs, rt, (s64)(s32)t, 1); }
else if ((insn >> 30) & 1) { u64 t; if (user_read_u64(addr, &t)) goto fault; REG_WRITE(regs, rt, t, 1); }
else { u32 t; if (user_read_u32(addr, &t)) goto fault; REG_WRITE(regs, rt, t, 0); }
regs->pc += 4; return true;
}
// LDP / STP
if ((insn & 0x3A000000) == (is_simd ? 0x2C000000 : 0x28000000)) {
u32 opc_pair = (insn >> 30) & 0x3, l = (insn >> 22) & 1, idx = (insn >> 23) & 0x3;
u32 rn = (insn >> 5) & 0x1F, rt = insn & 0x1F, rt2 = (insn >> 10) & 0x1F;
int pair_bytes = 0;
if (is_simd) {
if (opc_pair == 0) pair_bytes = 4; else if (opc_pair == 1) pair_bytes = 8; else if (opc_pair == 2) pair_bytes = 16; else goto fault;
} else {
pair_bytes = (opc_pair == 2) ? 8 : 4;
}
s64 offset = sign_extend64((s64)((insn >> 15) & 0x7F), 6) * pair_bytes;
u64 base = ADDR_REG(regs, rn), addr;
if (idx == 1) addr = base; else if (idx == 2 || idx == 3) addr = base + offset; else goto fault;
if (l) { // Load
if (is_simd) {
if (pair_bytes == 4) { u32 v1, v2; if (user_read_u32(addr, &v1) || user_read_u32(addr+4, &v2)) goto fault; VREG_WRITE_SCALAR(vregs, rt, v1); VREG_WRITE_SCALAR(vregs, rt2, v2); }
else if (pair_bytes == 8) { u64 v1, v2; if (user_read_u64(addr, &v1) || user_read_u64(addr+8, &v2)) goto fault; VREG_WRITE_SCALAR(vregs, rt, v1); VREG_WRITE_SCALAR(vregs, rt2, v2); }
else { u64 tl, th, tl2, th2; if (user_read_u64(addr, &tl) || user_read_u64(addr+8, &th) || user_read_u64(addr+16, &tl2) || user_read_u64(addr+24, &th2)) goto fault; VREG_WRITE_Q(vregs, rt, tl, th); VREG_WRITE_Q(vregs, rt2, tl2, th2); }
} else {
if (opc_pair == 2) { u64 v1, v2; if (user_read_u64(addr, &v1) || user_read_u64(addr+8, &v2)) goto fault; REG_WRITE(regs, rt, v1, 1); REG_WRITE(regs, rt2, v2, 1); }
else if (opc_pair == 1) { u32 v1, v2; if (user_read_u32(addr, &v1) || user_read_u32(addr+4, &v2)) goto fault; REG_WRITE(regs, rt, (s64)(s32)v1, 1); REG_WRITE(regs, rt2, (s64)(s32)v2, 1); }
else { u32 v1, v2; if (user_read_u32(addr, &v1) || user_read_u32(addr+4, &v2)) goto fault; REG_WRITE(regs, rt, v1, 0); REG_WRITE(regs, rt2, v2, 0); }
}
} else { // Store
if (is_simd) {
if (pair_bytes == 4) { if (user_write_u32(addr, (u32)VREG_READ_LO(vregs, rt)) || user_write_u32(addr+4, (u32)VREG_READ_LO(vregs, rt2))) goto fault; }
else if (pair_bytes == 8) { if (user_write_u64(addr, VREG_READ_LO(vregs, rt)) || user_write_u64(addr+8, VREG_READ_LO(vregs, rt2))) goto fault; }
else { if (user_write_u64(addr, VREG_READ_LO(vregs, rt)) || user_write_u64(addr+8, VREG_READ_HI(vregs, rt)) || user_write_u64(addr+16, VREG_READ_LO(vregs, rt2)) || user_write_u64(addr+24, VREG_READ_HI(vregs, rt2))) goto fault; }
} else {
if (opc_pair == 2) { if (user_write_u64(addr, REG_READ(regs, rt)) || user_write_u64(addr+8, REG_READ(regs, rt2))) goto fault; }
else { if (user_write_u32(addr, (u32)REG_READ(regs, rt)) || user_write_u32(addr+4, (u32)REG_READ(regs, rt2))) goto fault; }
}
}
if (idx == 1 || idx == 3) ADDR_REG_WRITE(regs, rn, base + offset);
regs->pc += 4; return true;
}
// LDR / STR (Single)
if ((insn & 0x3E000000) == 0x38000000 || (insn & 0x3E000000) == 0x3A000000 ||
(insn & 0x3E000000) == 0x3C000000 || (insn & 0x3E000000) == 0x3E000000 ||
(insn & 0x3F000000) == 0x39000000 || (insn & 0x3F000000) == 0x79000000 ||
(insn & 0x3F000000) == 0xB9000000 || (insn & 0x3F000000) == 0xF9000000 ||
(is_simd && ((insn & 0x3B000000) == 0x39000000 || (insn & 0x3B000000) == 0x3D000000)))
{
u32 rn = (insn >> 5) & 0x1F, rt = insn & 0x1F, opc = (insn >> 22) & 0x3;
bool is_load, sign_ext = false, dest_64 = false; int access_bytes;
if (is_simd) {
is_load = (opc & 1) != 0;
if (((insn >> 23) & 1) == 1) access_bytes = 16; else access_bytes = (1 << size);
} else {
is_load = (opc != 0); sign_ext = (opc == 2) || (opc == 3);
dest_64 = (size == 3) || (opc == 2); access_bytes = (1 << size);
}
u64 base = ADDR_REG(regs, rn), addr;
if (((insn >> 24) & 1) == 1) { addr = base + ((insn >> 10) & 0xFFF) * (u64)access_bytes; }
else {
u32 idx = (insn >> 10) & 0x3;
if (idx == 0) { addr = base + sign_extend64((s64)((insn >> 12) & 0x1FF), 8); }
else if (idx == 1) { addr = base; ADDR_REG_WRITE(regs, rn, base + sign_extend64((s64)((insn >> 12) & 0x1FF), 8)); }
else if (idx == 3) { addr = base + sign_extend64((s64)((insn >> 12) & 0x1FF), 8); ADDR_REG_WRITE(regs, rn, addr); }
else if (idx == 2) {
if (((insn >> 21) & 1) == 1) {
u32 rm = (insn >> 16) & 0x1F, option = (insn >> 13) & 0x7; s64 ext_val; u64 rm_val = REG_READ(regs, rm);
switch (option) { case 2: ext_val=(s64)(u32)rm_val; break; case 3: ext_val=(s64)rm_val; break; case 6: ext_val=(s64)(s32)rm_val; break; case 7: ext_val=(s64)rm_val; break; default: ext_val=(s64)rm_val; break; }
if ((insn >> 12) & 1) ext_val <<= size;
addr = base + (u64)ext_val;
} else goto fault;
} else goto fault;
}
if (is_load) {
if (is_simd) {
u64 vl = 0;
if (access_bytes <= 8) {
switch(access_bytes) { case 1:{u8 t;if(user_read_u8(addr,&t))goto fault;vl=t;break;} case 2:{u16 t;if(user_read_u16(addr,&t))goto fault;vl=t;break;} case 4:{u32 t;if(user_read_u32(addr,&t))goto fault;vl=t;break;} case 8:{u64 t;if(user_read_u64(addr,&t))goto fault;vl=t;break;} }
VREG_WRITE_SCALAR(vregs, rt, vl);
} else { u64 tl, th; if(user_read_u64(addr, &tl) || user_read_u64(addr+8, &th))goto fault; VREG_WRITE_Q(vregs, rt, tl, th); }
} else {
u64 val = 0;
switch(access_bytes) { case 1:{u8 t;if(user_read_u8(addr,&t))goto fault;val=t;break;} case 2:{u16 t;if(user_read_u16(addr,&t))goto fault;val=t;break;} case 4:{u32 t;if(user_read_u32(addr,&t))goto fault;val=t;break;} case 8:{u64 t;if(user_read_u64(addr,&t))goto fault;val=t;break;} }
if (sign_ext) { int bits = access_bytes * 8 - 1; if (val & (1ULL << bits)) val |= ~((1ULL << (bits + 1)) - 1); }
REG_WRITE(regs, rt, val, dest_64 ? 1 : 0);
}
} else {
if (is_simd) {
if (access_bytes <= 8) {
u64 v = VREG_READ_LO(vregs, rt);
switch(access_bytes) { case 1:if(user_write_u8(addr,(u8)v))goto fault;break; case 2:if(user_write_u16(addr,(u16)v))goto fault;break; case 4:if(user_write_u32(addr,(u32)v))goto fault;break; case 8:if(user_write_u64(addr,v))goto fault;break; }
} else { if(user_write_u64(addr, VREG_READ_LO(vregs, rt)) || user_write_u64(addr+8, VREG_READ_HI(vregs, rt))) goto fault; }
} else {
u64 v = REG_READ(regs, rt);
switch(access_bytes) { case 1:if(user_write_u8(addr,(u8)v))goto fault;break; case 2:if(user_write_u16(addr,(u16)v))goto fault;break; case 4:if(user_write_u32(addr,(u32)v))goto fault;break; case 8:if(user_write_u64(addr,v))goto fault;break; }
}
}
regs->pc += 4; return true;
}
}
// --- 第三部分:基础算术指令 (整数 ALU) ---
// ADD/SUB (immediate)
if ((insn & 0x1F000000) == 0x11000000) {
bool sf = (insn >> 31) & 1, op = (insn >> 30) & 1, S = (insn >> 29) & 1;
u32 shift = (insn >> 22) & 0x3, rn = (insn >> 5) & 0x1F, rd = insn & 0x1F;
u64 imm = (insn >> 10) & 0xFFF;
if (shift == 1) imm <<= 12; else if (shift > 1) goto unsupported_insn;
u64 op1 = (rn == 31) ? regs->sp : REG_READ(regs, rn);
if (!sf) { op1 = (u32)op1; imm = (u32)imm; }
u64 result = op ? (op1 - imm) : (op1 + imm);
if (S) {
u32 n, z, c, v;
if (!sf) { u32 r32=(u32)result; n=(r32>>31)&1; z=(r32==0); if(!op){c=(r32<(u32)op1);v=(((u32)op1^~((u32)imm))&((u32)op1^r32))>>31;}else{c=((u32)op1>=(u32)imm);v=(((u32)op1^(u32)imm)&((u32)op1^r32))>>31;} }
else { n=(result>>63)&1; z=(result==0); if(!op){c=(result<op1);v=((op1^~imm)&(op1^result))>>63;}else{c=(op1>=imm);v=((op1^imm)&(op1^result))>>63;} }
regs->pstate = (regs->pstate & ~0xF0000000ULL) | ((u64)n<<31)|((u64)z<<30)|((u64)c<<29)|((u64)v<<28);
}
if (rd != 31 || !S) { if (rd == 31) regs->sp = result; else REG_WRITE(regs, rd, result, sf); }
regs->pc += 4; return true;
}
// ADD/SUB (shifted register)
if ((insn & 0x1F200000) == 0x0B000000) {
bool sf = (insn >> 31) & 1, op = (insn >> 30) & 1, S = (insn >> 29) & 1;
u32 shift = (insn >> 22) & 0x3, rm = (insn >> 16) & 0x1F, imm6 = (insn >> 10) & 0x3F, rn = (insn >> 5) & 0x1F, rd = insn & 0x1F;
u64 op1 = REG_READ(regs, rn), op2 = REG_READ(regs, rm);
if (!sf) { op1 = (u32)op1; op2 = (u32)op2; }
if (shift == 0) op2 <<= imm6; else if (shift == 1) op2 = sf ? (op2 >> imm6) : ((u32)op2 >> imm6); else if (shift == 2) op2 = sf ? ((s64)op2 >> imm6) : (u64)((s32)op2 >> imm6); else goto unsupported_insn;
if (!sf) op2 = (u32)op2;
u64 result = op ? (op1 - op2) : (op1 + op2);
if (rd != 31 || !S) REG_WRITE(regs, rd, result, sf);
regs->pc += 4; return true;
}
// --- 第四部分:浮点/SIMD 算术指令 (利用硬件计算) ---
// FADD, FSUB, FMUL, FDIV (Scalar)
if ((insn & 0x5F200800) == 0x1E200800) {
u32 type = (insn >> 22) & 0x3;
u32 opcode = (insn >> 12) & 0xF;
u32 rm = (insn >> 16) & 0x1F, rn = (insn >> 5) & 0x1F, rd = insn & 0x1F;
if (type > 1) goto unsupported_insn; // Only S and D
u64 op1_val = VREG_READ_LO(vregs, rn), op2_val = VREG_READ_LO(vregs, rm), res_val = 0;
if (type == 0) { // Single precision (32-bit)
u32 op1_s = (u32)op1_val, op2_s = (u32)op2_val, res_s = 0;
switch (opcode) {
case 0x0: asm volatile(".arch_extension fp\n fmov s1, %w1\n fmov s2, %w2\n fmul s0, s1, s2\n fmov %w0, s0\n" : "=r"(res_s) : "r"(op1_s), "r"(op2_s) : "s0", "s1", "s2"); break;
case 0x1: asm volatile(".arch_extension fp\n fmov s1, %w1\n fmov s2, %w2\n fdiv s0, s1, s2\n fmov %w0, s0\n" : "=r"(res_s) : "r"(op1_s), "r"(op2_s) : "s0", "s1", "s2"); break;
case 0x2: asm volatile(".arch_extension fp\n fmov s1, %w1\n fmov s2, %w2\n fadd s0, s1, s2\n fmov %w0, s0\n" : "=r"(res_s) : "r"(op1_s), "r"(op2_s) : "s0", "s1", "s2"); break;
case 0x3: asm volatile(".arch_extension fp\n fmov s1, %w1\n fmov s2, %w2\n fsub s0, s1, s2\n fmov %w0, s0\n" : "=r"(res_s) : "r"(op1_s), "r"(op2_s) : "s0", "s1", "s2"); break;
default: goto unsupported_insn;
}
res_val = res_s;
} else { // Double precision (64-bit)
switch (opcode) {
case 0x0: asm volatile(".arch_extension fp\n fmov d1, %1\n fmov d2, %2\n fmul d0, d1, d2\n fmov %0, d0\n" : "=r"(res_val) : "r"(op1_val), "r"(op2_val) : "d0", "d1", "d2"); break;
case 0x1: asm volatile(".arch_extension fp\n fmov d1, %1\n fmov d2, %2\n fdiv d0, d1, d2\n fmov %0, d0\n" : "=r"(res_val) : "r"(op1_val), "r"(op2_val) : "d0", "d1", "d2"); break;
case 0x2: asm volatile(".arch_extension fp\n fmov d1, %1\n fmov d2, %2\n fadd d0, d1, d2\n fmov %0, d0\n" : "=r"(res_val) : "r"(op1_val), "r"(op2_val) : "d0", "d1", "d2"); break;
case 0x3: asm volatile(".arch_extension fp\n fmov d1, %1\n fmov d2, %2\n fsub d0, d1, d2\n fmov %0, d0\n" : "=r"(res_val) : "r"(op1_val), "r"(op2_val) : "d0", "d1", "d2"); break;
default: goto unsupported_insn;
}
}
VREG_WRITE_SCALAR(vregs, rd, res_val);
regs->pc += 4; return true;
}
// ============================================================
// ARM64 指令模拟器
//
// 覆盖范围:
// 1. 跳转/调用类:b / bl / br / blr / ret / b.cond / cbz / cbnz / tbz / tbnz
// 2. Load/Store 类:
// - LDR/STR 立即数偏移 / 寄存器偏移 / unscaled / pre / post
// - LDP/STP 寄存器对
// - 支持通用寄存器 (GPR) 和 浮点寄存器 (FP/SIMD: B,H,S,D,Q)
// 3. 基础整数算术类 (ALU):
// - ADD, SUB, ADDS, SUBS (CMP) 的立即数与寄存器版本
// 4. 基础浮点算术类 (FPU硬件辅助计算):
// - FADD, FSUB, FMUL, FDIV (单精度 S / 双精度 D)
// ============================================================
#ifndef EMULATE_INSN_H
#define EMULATE_INSN_H
#include <linux/uaccess.h>
#include <asm/ptrace.h>
#include <asm/insn.h>
// ---- 通用寄存器辅助宏 ---------------------------------------
#define REG_READ(regs, n) ((n) == 31 ? 0ULL : (regs)->regs[(n)])
#define REG_WRITE(regs, n, val, sf)
do
{
if ((n) != 31)
(regs)->regs[(n)] = (sf) ? (u64)(val)
: (u64)(u32)(val);
} while (0)
#define ADDR_REG(regs, n) ((n) == 31 ? (regs)->sp : (regs)->regs[(n)])
#define ADDR_REG_WRITE(regs, n, val)
do
{
if ((n) == 31)
(regs)->sp = (u64)(val);
else
(regs)->regs[(n)] = (u64)(val);
} while (0)
// ---- FP/SIMD 寄存器辅助宏 -----------------------------------
#define VREG_READ_LO(vregs, n) ((vregs)[(n) * 2])
#define VREG_READ_HI(vregs, n) ((vregs)[(n) * 2 + 1])
#define VREG_WRITE_SCALAR(vregs, n, val)
do {
(vregs)[(n) * 2] = (u64)(val);
(vregs)[(n) * 2 + 1] = 0;
} while (0)
#define VREG_WRITE_Q(vregs, n, val_lo, val_hi)
do {
(vregs)[(n) * 2] = (val_lo);
(vregs)[(n) * 2 + 1] = (val_hi);
} while (0)
// ---- 状态寄存器辅助宏 ---------------------------------------
#define PSTATE_N(pstate) (((pstate) >> 31) & 1)
#define PSTATE_Z(pstate) (((pstate) >> 30) & 1)
#define PSTATE_C(pstate) (((pstate) >> 29) & 1)
#define PSTATE_V(pstate) (((pstate) >> 28) & 1)
static bool eval_cond(u64 pstate, u32 cond)
{
bool n = PSTATE_N(pstate), z = PSTATE_Z(pstate);
bool c = PSTATE_C(pstate), v = PSTATE_V(pstate), result;
switch (cond >> 1)
{
case 0: result = z; break;
case 1: result = c; break;
case 2: result = n; break;
case 3: result = v; break;
case 4: result = c && !z; break;
case 5: result = (n == v); break;
case 6: result = (n == v) && !z; break;
case 7: result = true; break;
default: result = false; break;
}
if ((cond & 1) && (cond != 0xf))
result = !result;
return result;
}
// ---- 用户态内存访问包装 -------------------------------------
static inline int user_read_u8(u64 addr, u8 *val) { return get_user(*val, (u8 __user *)(uintptr_t)addr); }
static inline int user_read_u16(u64 addr, u16 *val) { return get_user(*val, (u16 __user *)(uintptr_t)addr); }
static inline int user_read_u32(u64 addr, u32 *val) { return get_user(*val, (u32 __user *)(uintptr_t)addr); }
static inline int user_read_u64(u64 addr, u64 *val) { return get_user(*val, (u64 __user *)(uintptr_t)addr); }
static inline int user_write_u8(u64 addr, u8 val) { return put_user(val, (u8 __user *)(uintptr_t)addr); }
static inline int user_write_u16(u64 addr, u16 val) { return put_user(val, (u16 __user *)(uintptr_t)addr); }
static inline int user_write_u32(u64 addr, u32 val) { return put_user(val, (u32 __user *)(uintptr_t)addr); }
static inline int user_write_u64(u64 addr, u64 val) { return put_user(val, (u64 __user *)(uintptr_t)addr); }
static inline int read_user_insn(u64 pc, u32 *insn) { return get_user(*insn, (u32 __user *)(uintptr_t)pc); }
// ============================================================
// 模拟执行 ARM64 指令
// @regs: 普通寄存器上下文
// @vregs: FP/SIMD 寄存器数组 (长度至少 64 的 u64 数组)
// ============================================================
static bool emulate_insn(struct pt_regs *regs, u64 *vregs)
{
u32 insn;
u64 pc = regs->pc;
unsupported_insn:
// 其他未支持的指令退化为 PC+4 跳过
pr_debug("hwbp: 不支持模拟 insn=0x%08x pc=0x%llx,退化为 pc+4\n", insn, pc);
regs->pc += 4;
return false;
fault:
pr_debug("hwbp: 模拟访存失败 insn=0x%08x pc=0x%llx\n", insn, pc);
return false;
}
#endif // EMULATE_INSN_H