Commit 9b3441a6 authored by Tiezhu Yang's avatar Tiezhu Yang Committed by Huacai Chen

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_pcalau12iTested-by: default avatarJeff Xie <xiehuan09@gmail.com>
Co-developed-by: default avatarJinyang He <hejinyang@loongson.cn>
Signed-off-by: default avatarJinyang He <hejinyang@loongson.cn>
Signed-off-by: default avatarTiezhu Yang <yangtiezhu@loongson.cn>
Signed-off-by: default avatarHuacai Chen <chenhuacai@loongson.cn>
parent 424421a7
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment