LoongArch: Simulate branch and PC* instructions
According to LoongArch Reference Manual, simulate branch and PC* instructions, this is preparation for later patch. Link: https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#branch-instructions Link: https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#_pcaddi_pcaddu121_pcaddu18l_pcalau12i Tested-by: Jeff Xie <xiehuan09@gmail.com> Co-developed-by: Jinyang He <hejinyang@loongson.cn> Signed-off-by: Jinyang He <hejinyang@loongson.cn> Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
This commit is contained in:
Родитель
424421a7f3
Коммит
9b3441a6b0
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <linux/types.h>
|
||||
#include <asm/asm.h>
|
||||
#include <asm/ptrace.h>
|
||||
|
||||
#define INSN_NOP 0x03400000
|
||||
#define INSN_BREAK 0x002a0000
|
||||
|
@ -32,6 +33,7 @@ enum reg1i20_op {
|
|||
lu12iw_op = 0x0a,
|
||||
lu32id_op = 0x0b,
|
||||
pcaddi_op = 0x0c,
|
||||
pcalau12i_op = 0x0d,
|
||||
pcaddu12i_op = 0x0e,
|
||||
pcaddu18i_op = 0x0f,
|
||||
};
|
||||
|
@ -389,6 +391,9 @@ static inline bool is_self_loop_ins(union loongarch_instruction *ip, struct pt_r
|
|||
return false;
|
||||
}
|
||||
|
||||
void simu_pc(struct pt_regs *regs, union loongarch_instruction insn);
|
||||
void simu_branch(struct pt_regs *regs, union loongarch_instruction insn);
|
||||
|
||||
int larch_insn_read(void *addr, u32 *insnp);
|
||||
int larch_insn_write(void *addr, u32 insn);
|
||||
int larch_insn_patch_text(void *addr, u32 insn);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#define _ASM_PTRACE_H
|
||||
|
||||
#include <asm/page.h>
|
||||
#include <asm/irqflags.h>
|
||||
#include <asm/thread_info.h>
|
||||
#include <uapi/asm/ptrace.h>
|
||||
|
||||
|
|
|
@ -10,6 +10,129 @@
|
|||
|
||||
static DEFINE_RAW_SPINLOCK(patch_lock);
|
||||
|
||||
void simu_pc(struct pt_regs *regs, union loongarch_instruction insn)
|
||||
{
|
||||
unsigned long pc = regs->csr_era;
|
||||
unsigned int rd = insn.reg1i20_format.rd;
|
||||
unsigned int imm = insn.reg1i20_format.immediate;
|
||||
|
||||
if (pc & 3) {
|
||||
pr_warn("%s: invalid pc 0x%lx\n", __func__, pc);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (insn.reg1i20_format.opcode) {
|
||||
case pcaddi_op:
|
||||
regs->regs[rd] = pc + sign_extend64(imm << 2, 21);
|
||||
break;
|
||||
case pcaddu12i_op:
|
||||
regs->regs[rd] = pc + sign_extend64(imm << 12, 31);
|
||||
break;
|
||||
case pcaddu18i_op:
|
||||
regs->regs[rd] = pc + sign_extend64(imm << 18, 37);
|
||||
break;
|
||||
case pcalau12i_op:
|
||||
regs->regs[rd] = pc + sign_extend64(imm << 12, 31);
|
||||
regs->regs[rd] &= ~((1 << 12) - 1);
|
||||
break;
|
||||
default:
|
||||
pr_info("%s: unknown opcode\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
regs->csr_era += LOONGARCH_INSN_SIZE;
|
||||
}
|
||||
|
||||
void simu_branch(struct pt_regs *regs, union loongarch_instruction insn)
|
||||
{
|
||||
unsigned int imm, imm_l, imm_h, rd, rj;
|
||||
unsigned long pc = regs->csr_era;
|
||||
|
||||
if (pc & 3) {
|
||||
pr_warn("%s: invalid pc 0x%lx\n", __func__, pc);
|
||||
return;
|
||||
}
|
||||
|
||||
imm_l = insn.reg0i26_format.immediate_l;
|
||||
imm_h = insn.reg0i26_format.immediate_h;
|
||||
switch (insn.reg0i26_format.opcode) {
|
||||
case b_op:
|
||||
regs->csr_era = pc + sign_extend64((imm_h << 16 | imm_l) << 2, 27);
|
||||
return;
|
||||
case bl_op:
|
||||
regs->csr_era = pc + sign_extend64((imm_h << 16 | imm_l) << 2, 27);
|
||||
regs->regs[1] = pc + LOONGARCH_INSN_SIZE;
|
||||
return;
|
||||
}
|
||||
|
||||
imm_l = insn.reg1i21_format.immediate_l;
|
||||
imm_h = insn.reg1i21_format.immediate_h;
|
||||
rj = insn.reg1i21_format.rj;
|
||||
switch (insn.reg1i21_format.opcode) {
|
||||
case beqz_op:
|
||||
if (regs->regs[rj] == 0)
|
||||
regs->csr_era = pc + sign_extend64((imm_h << 16 | imm_l) << 2, 22);
|
||||
else
|
||||
regs->csr_era = pc + LOONGARCH_INSN_SIZE;
|
||||
return;
|
||||
case bnez_op:
|
||||
if (regs->regs[rj] != 0)
|
||||
regs->csr_era = pc + sign_extend64((imm_h << 16 | imm_l) << 2, 22);
|
||||
else
|
||||
regs->csr_era = pc + LOONGARCH_INSN_SIZE;
|
||||
return;
|
||||
}
|
||||
|
||||
imm = insn.reg2i16_format.immediate;
|
||||
rj = insn.reg2i16_format.rj;
|
||||
rd = insn.reg2i16_format.rd;
|
||||
switch (insn.reg2i16_format.opcode) {
|
||||
case beq_op:
|
||||
if (regs->regs[rj] == regs->regs[rd])
|
||||
regs->csr_era = pc + sign_extend64(imm << 2, 17);
|
||||
else
|
||||
regs->csr_era = pc + LOONGARCH_INSN_SIZE;
|
||||
break;
|
||||
case bne_op:
|
||||
if (regs->regs[rj] != regs->regs[rd])
|
||||
regs->csr_era = pc + sign_extend64(imm << 2, 17);
|
||||
else
|
||||
regs->csr_era = pc + LOONGARCH_INSN_SIZE;
|
||||
break;
|
||||
case blt_op:
|
||||
if ((long)regs->regs[rj] < (long)regs->regs[rd])
|
||||
regs->csr_era = pc + sign_extend64(imm << 2, 17);
|
||||
else
|
||||
regs->csr_era = pc + LOONGARCH_INSN_SIZE;
|
||||
break;
|
||||
case bge_op:
|
||||
if ((long)regs->regs[rj] >= (long)regs->regs[rd])
|
||||
regs->csr_era = pc + sign_extend64(imm << 2, 17);
|
||||
else
|
||||
regs->csr_era = pc + LOONGARCH_INSN_SIZE;
|
||||
break;
|
||||
case bltu_op:
|
||||
if (regs->regs[rj] < regs->regs[rd])
|
||||
regs->csr_era = pc + sign_extend64(imm << 2, 17);
|
||||
else
|
||||
regs->csr_era = pc + LOONGARCH_INSN_SIZE;
|
||||
break;
|
||||
case bgeu_op:
|
||||
if (regs->regs[rj] >= regs->regs[rd])
|
||||
regs->csr_era = pc + sign_extend64(imm << 2, 17);
|
||||
else
|
||||
regs->csr_era = pc + LOONGARCH_INSN_SIZE;
|
||||
break;
|
||||
case jirl_op:
|
||||
regs->csr_era = regs->regs[rj] + sign_extend64(imm << 2, 17);
|
||||
regs->regs[rd] = pc + LOONGARCH_INSN_SIZE;
|
||||
break;
|
||||
default:
|
||||
pr_info("%s: unknown opcode\n", __func__);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int larch_insn_read(void *addr, u32 *insnp)
|
||||
{
|
||||
int ret;
|
||||
|
|
Загрузка…
Ссылка в новой задаче