mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
390 lines
8.0 KiB
390 lines
8.0 KiB
// SPDX-License-Identifier: GPL-2.0+ |
|
|
|
#include <linux/bitops.h> |
|
#include <linux/kernel.h> |
|
#include <linux/kprobes.h> |
|
|
|
#include "decode-insn.h" |
|
#include "simulate-insn.h" |
|
|
|
static inline bool csky_insn_reg_get_val(struct pt_regs *regs, |
|
unsigned long index, |
|
unsigned long *ptr) |
|
{ |
|
if (index < 14) |
|
*ptr = *(®s->a0 + index); |
|
|
|
if (index > 15 && index < 31) |
|
*ptr = *(®s->exregs[0] + index - 16); |
|
|
|
switch (index) { |
|
case 14: |
|
*ptr = regs->usp; |
|
break; |
|
case 15: |
|
*ptr = regs->lr; |
|
break; |
|
case 31: |
|
*ptr = regs->tls; |
|
break; |
|
default: |
|
goto fail; |
|
} |
|
|
|
return true; |
|
fail: |
|
return false; |
|
} |
|
|
|
static inline bool csky_insn_reg_set_val(struct pt_regs *regs, |
|
unsigned long index, |
|
unsigned long val) |
|
{ |
|
if (index < 14) |
|
*(®s->a0 + index) = val; |
|
|
|
if (index > 15 && index < 31) |
|
*(®s->exregs[0] + index - 16) = val; |
|
|
|
switch (index) { |
|
case 14: |
|
regs->usp = val; |
|
break; |
|
case 15: |
|
regs->lr = val; |
|
break; |
|
case 31: |
|
regs->tls = val; |
|
break; |
|
default: |
|
goto fail; |
|
} |
|
|
|
return true; |
|
fail: |
|
return false; |
|
} |
|
|
|
void __kprobes |
|
simulate_br16(u32 opcode, long addr, struct pt_regs *regs) |
|
{ |
|
instruction_pointer_set(regs, |
|
addr + sign_extend32((opcode & 0x3ff) << 1, 9)); |
|
} |
|
|
|
void __kprobes |
|
simulate_br32(u32 opcode, long addr, struct pt_regs *regs) |
|
{ |
|
instruction_pointer_set(regs, |
|
addr + sign_extend32((opcode & 0xffff0000) >> 15, 15)); |
|
} |
|
|
|
void __kprobes |
|
simulate_bt16(u32 opcode, long addr, struct pt_regs *regs) |
|
{ |
|
if (regs->sr & 1) |
|
instruction_pointer_set(regs, |
|
addr + sign_extend32((opcode & 0x3ff) << 1, 9)); |
|
else |
|
instruction_pointer_set(regs, addr + 2); |
|
} |
|
|
|
void __kprobes |
|
simulate_bt32(u32 opcode, long addr, struct pt_regs *regs) |
|
{ |
|
if (regs->sr & 1) |
|
instruction_pointer_set(regs, |
|
addr + sign_extend32((opcode & 0xffff0000) >> 15, 15)); |
|
else |
|
instruction_pointer_set(regs, addr + 4); |
|
} |
|
|
|
void __kprobes |
|
simulate_bf16(u32 opcode, long addr, struct pt_regs *regs) |
|
{ |
|
if (!(regs->sr & 1)) |
|
instruction_pointer_set(regs, |
|
addr + sign_extend32((opcode & 0x3ff) << 1, 9)); |
|
else |
|
instruction_pointer_set(regs, addr + 2); |
|
} |
|
|
|
void __kprobes |
|
simulate_bf32(u32 opcode, long addr, struct pt_regs *regs) |
|
{ |
|
if (!(regs->sr & 1)) |
|
instruction_pointer_set(regs, |
|
addr + sign_extend32((opcode & 0xffff0000) >> 15, 15)); |
|
else |
|
instruction_pointer_set(regs, addr + 4); |
|
} |
|
|
|
void __kprobes |
|
simulate_jmp16(u32 opcode, long addr, struct pt_regs *regs) |
|
{ |
|
unsigned long tmp = (opcode >> 2) & 0xf; |
|
|
|
csky_insn_reg_get_val(regs, tmp, &tmp); |
|
|
|
instruction_pointer_set(regs, tmp & 0xfffffffe); |
|
} |
|
|
|
void __kprobes |
|
simulate_jmp32(u32 opcode, long addr, struct pt_regs *regs) |
|
{ |
|
unsigned long tmp = opcode & 0x1f; |
|
|
|
csky_insn_reg_get_val(regs, tmp, &tmp); |
|
|
|
instruction_pointer_set(regs, tmp & 0xfffffffe); |
|
} |
|
|
|
void __kprobes |
|
simulate_jsr16(u32 opcode, long addr, struct pt_regs *regs) |
|
{ |
|
unsigned long tmp = (opcode >> 2) & 0xf; |
|
|
|
csky_insn_reg_get_val(regs, tmp, &tmp); |
|
|
|
regs->lr = addr + 2; |
|
|
|
instruction_pointer_set(regs, tmp & 0xfffffffe); |
|
} |
|
|
|
void __kprobes |
|
simulate_jsr32(u32 opcode, long addr, struct pt_regs *regs) |
|
{ |
|
unsigned long tmp = opcode & 0x1f; |
|
|
|
csky_insn_reg_get_val(regs, tmp, &tmp); |
|
|
|
regs->lr = addr + 4; |
|
|
|
instruction_pointer_set(regs, tmp & 0xfffffffe); |
|
} |
|
|
|
void __kprobes |
|
simulate_lrw16(u32 opcode, long addr, struct pt_regs *regs) |
|
{ |
|
unsigned long val; |
|
unsigned long tmp = (opcode & 0x300) >> 3; |
|
unsigned long offset = ((opcode & 0x1f) | tmp) << 2; |
|
|
|
tmp = (opcode & 0xe0) >> 5; |
|
|
|
val = *(unsigned int *)(instruction_pointer(regs) + offset); |
|
|
|
csky_insn_reg_set_val(regs, tmp, val); |
|
} |
|
|
|
void __kprobes |
|
simulate_lrw32(u32 opcode, long addr, struct pt_regs *regs) |
|
{ |
|
unsigned long val; |
|
unsigned long offset = (opcode & 0xffff0000) >> 14; |
|
unsigned long tmp = opcode & 0x0000001f; |
|
|
|
val = *(unsigned int *) |
|
((instruction_pointer(regs) + offset) & 0xfffffffc); |
|
|
|
csky_insn_reg_set_val(regs, tmp, val); |
|
} |
|
|
|
void __kprobes |
|
simulate_pop16(u32 opcode, long addr, struct pt_regs *regs) |
|
{ |
|
unsigned long *tmp = (unsigned long *)regs->usp; |
|
int i; |
|
|
|
for (i = 0; i < (opcode & 0xf); i++) { |
|
csky_insn_reg_set_val(regs, i + 4, *tmp); |
|
tmp += 1; |
|
} |
|
|
|
if (opcode & 0x10) { |
|
csky_insn_reg_set_val(regs, 15, *tmp); |
|
tmp += 1; |
|
} |
|
|
|
regs->usp = (unsigned long)tmp; |
|
|
|
instruction_pointer_set(regs, regs->lr); |
|
} |
|
|
|
void __kprobes |
|
simulate_pop32(u32 opcode, long addr, struct pt_regs *regs) |
|
{ |
|
unsigned long *tmp = (unsigned long *)regs->usp; |
|
int i; |
|
|
|
for (i = 0; i < ((opcode & 0xf0000) >> 16); i++) { |
|
csky_insn_reg_set_val(regs, i + 4, *tmp); |
|
tmp += 1; |
|
} |
|
|
|
if (opcode & 0x100000) { |
|
csky_insn_reg_set_val(regs, 15, *tmp); |
|
tmp += 1; |
|
} |
|
|
|
for (i = 0; i < ((opcode & 0xe00000) >> 21); i++) { |
|
csky_insn_reg_set_val(regs, i + 16, *tmp); |
|
tmp += 1; |
|
} |
|
|
|
if (opcode & 0x1000000) { |
|
csky_insn_reg_set_val(regs, 29, *tmp); |
|
tmp += 1; |
|
} |
|
|
|
regs->usp = (unsigned long)tmp; |
|
|
|
instruction_pointer_set(regs, regs->lr); |
|
} |
|
|
|
void __kprobes |
|
simulate_bez32(u32 opcode, long addr, struct pt_regs *regs) |
|
{ |
|
unsigned long tmp = opcode & 0x1f; |
|
|
|
csky_insn_reg_get_val(regs, tmp, &tmp); |
|
|
|
if (tmp == 0) { |
|
instruction_pointer_set(regs, |
|
addr + sign_extend32((opcode & 0xffff0000) >> 15, 15)); |
|
} else |
|
instruction_pointer_set(regs, addr + 4); |
|
} |
|
|
|
void __kprobes |
|
simulate_bnez32(u32 opcode, long addr, struct pt_regs *regs) |
|
{ |
|
unsigned long tmp = opcode & 0x1f; |
|
|
|
csky_insn_reg_get_val(regs, tmp, &tmp); |
|
|
|
if (tmp != 0) { |
|
instruction_pointer_set(regs, |
|
addr + sign_extend32((opcode & 0xffff0000) >> 15, 15)); |
|
} else |
|
instruction_pointer_set(regs, addr + 4); |
|
} |
|
|
|
void __kprobes |
|
simulate_bnezad32(u32 opcode, long addr, struct pt_regs *regs) |
|
{ |
|
unsigned long tmp = opcode & 0x1f; |
|
long val; |
|
|
|
csky_insn_reg_get_val(regs, tmp, (unsigned long *)&val); |
|
|
|
val -= 1; |
|
|
|
if (val > 0) { |
|
instruction_pointer_set(regs, |
|
addr + sign_extend32((opcode & 0xffff0000) >> 15, 15)); |
|
} else |
|
instruction_pointer_set(regs, addr + 4); |
|
|
|
csky_insn_reg_set_val(regs, tmp, (unsigned long)val); |
|
} |
|
|
|
void __kprobes |
|
simulate_bhsz32(u32 opcode, long addr, struct pt_regs *regs) |
|
{ |
|
unsigned long tmp = opcode & 0x1f; |
|
unsigned long val; |
|
|
|
csky_insn_reg_get_val(regs, tmp, &val); |
|
|
|
if ((long) val >= 0) { |
|
instruction_pointer_set(regs, |
|
addr + sign_extend32((opcode & 0xffff0000) >> 15, 15)); |
|
} else |
|
instruction_pointer_set(regs, addr + 4); |
|
} |
|
|
|
void __kprobes |
|
simulate_bhz32(u32 opcode, long addr, struct pt_regs *regs) |
|
{ |
|
unsigned long tmp = opcode & 0x1f; |
|
unsigned long val; |
|
|
|
csky_insn_reg_get_val(regs, tmp, &val); |
|
|
|
if ((long) val > 0) { |
|
instruction_pointer_set(regs, |
|
addr + sign_extend32((opcode & 0xffff0000) >> 15, 15)); |
|
} else |
|
instruction_pointer_set(regs, addr + 4); |
|
} |
|
|
|
void __kprobes |
|
simulate_blsz32(u32 opcode, long addr, struct pt_regs *regs) |
|
{ |
|
unsigned long tmp = opcode & 0x1f; |
|
unsigned long val; |
|
|
|
csky_insn_reg_get_val(regs, tmp, &val); |
|
|
|
if ((long) val <= 0) { |
|
instruction_pointer_set(regs, |
|
addr + sign_extend32((opcode & 0xffff0000) >> 15, 15)); |
|
} else |
|
instruction_pointer_set(regs, addr + 4); |
|
} |
|
|
|
void __kprobes |
|
simulate_blz32(u32 opcode, long addr, struct pt_regs *regs) |
|
{ |
|
unsigned long tmp = opcode & 0x1f; |
|
unsigned long val; |
|
|
|
csky_insn_reg_get_val(regs, tmp, &val); |
|
|
|
if ((long) val < 0) { |
|
instruction_pointer_set(regs, |
|
addr + sign_extend32((opcode & 0xffff0000) >> 15, 15)); |
|
} else |
|
instruction_pointer_set(regs, addr + 4); |
|
} |
|
|
|
void __kprobes |
|
simulate_bsr32(u32 opcode, long addr, struct pt_regs *regs) |
|
{ |
|
unsigned long tmp; |
|
|
|
tmp = (opcode & 0xffff) << 16; |
|
tmp |= (opcode & 0xffff0000) >> 16; |
|
|
|
instruction_pointer_set(regs, |
|
addr + sign_extend32((tmp & 0x3ffffff) << 1, 15)); |
|
|
|
regs->lr = addr + 4; |
|
} |
|
|
|
void __kprobes |
|
simulate_jmpi32(u32 opcode, long addr, struct pt_regs *regs) |
|
{ |
|
unsigned long val; |
|
unsigned long offset = ((opcode & 0xffff0000) >> 14); |
|
|
|
val = *(unsigned int *) |
|
((instruction_pointer(regs) + offset) & 0xfffffffc); |
|
|
|
instruction_pointer_set(regs, val); |
|
} |
|
|
|
void __kprobes |
|
simulate_jsri32(u32 opcode, long addr, struct pt_regs *regs) |
|
{ |
|
unsigned long val; |
|
unsigned long offset = ((opcode & 0xffff0000) >> 14); |
|
|
|
val = *(unsigned int *) |
|
((instruction_pointer(regs) + offset) & 0xfffffffc); |
|
|
|
regs->lr = addr + 4; |
|
|
|
instruction_pointer_set(regs, val); |
|
}
|
|
|