Messages in this thread | | | From | Huacai Chen <> | Date | Mon, 14 Nov 2022 12:37:46 +0800 | Subject | Re: [PATCH v2 1/5] LoongArch: Simulate branch and PC instructions |
| |
Hi, Tiezhu,
On Wed, Sep 28, 2022 at 8:50 AM Tiezhu Yang <yangtiezhu@loongson.cn> wrote: > > 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 > > Co-developed-by: Jinyang He <hejinyang@loongson.cn> > Signed-off-by: Jinyang He <hejinyang@loongson.cn> > Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn> > --- > arch/loongarch/include/asm/inst.h | 19 ++++++ > arch/loongarch/include/asm/ptrace.h | 1 + > arch/loongarch/kernel/inst.c | 113 ++++++++++++++++++++++++++++++++++++ > 3 files changed, 133 insertions(+) > > diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h > index fce1843..93d5cd4 100644 > --- a/arch/loongarch/include/asm/inst.h > +++ b/arch/loongarch/include/asm/inst.h > @@ -7,6 +7,7 @@ > > #include <linux/types.h> > #include <asm/asm.h> > +#include <asm/ptrace.h> > > #define INSN_BREAK 0x002a0000 > > @@ -28,6 +29,8 @@ enum reg0i26_op { > enum reg1i20_op { > lu12iw_op = 0x0a, > lu32id_op = 0x0b, > + pcaddi_op = 0x0c, > + pcalau12i_op = 0x0d, > pcaddu12i_op = 0x0e, > pcaddu18i_op = 0x0f, > }; > @@ -313,6 +316,12 @@ static inline bool is_branch_ins(union loongarch_instruction *ip) > ip->reg1i21_format.opcode <= bgeu_op; > } > > +static inline bool is_pc_ins(union loongarch_instruction *ip) > +{ > + return ip->reg1i20_format.opcode >= pcaddi_op && > + ip->reg1i20_format.opcode <= pcaddu18i_op; > +} > + > static inline bool is_ra_save_ins(union loongarch_instruction *ip) > { > /* st.d $ra, $sp, offset */ > @@ -334,6 +343,16 @@ static inline bool is_stack_alloc_ins(union loongarch_instruction *ip) > u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm); > u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm); > u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, unsigned long pc, unsigned long dest); > +void simu_branch(struct pt_regs *regs, union loongarch_instruction insn); > +void simu_pc(struct pt_regs *regs, union loongarch_instruction insn); > + > +static inline unsigned long sign_extended(unsigned long val, unsigned int idx) > +{ > + if (val & (1UL << idx)) > + return ~((1UL << (idx + 1)) - 1) | val; > + else > + return ((1UL << (idx + 1)) - 1) & val; > +} > > static inline bool signed_imm_check(long val, unsigned int bit) > { > diff --git a/arch/loongarch/include/asm/ptrace.h b/arch/loongarch/include/asm/ptrace.h > index 17838c6..eb9538a 100644 > --- a/arch/loongarch/include/asm/ptrace.h > +++ b/arch/loongarch/include/asm/ptrace.h > @@ -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> > > diff --git a/arch/loongarch/kernel/inst.c b/arch/loongarch/kernel/inst.c > index b1df0ec..f5c1eff6 100644 > --- a/arch/loongarch/kernel/inst.c > +++ b/arch/loongarch/kernel/inst.c > @@ -38,3 +38,116 @@ u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, unsigned l > > return insn.word; > } > + > +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; > + In previous versions we checked the alignment of era in simu_branch() and simu_pc(), now they are unnecessary?
Huacai > + 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_extended((imm_h << 16 | imm_l) << 2, 27); > + return; > + case bl_op: > + regs->csr_era = pc + sign_extended((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_extended((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_extended((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_extended(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_extended(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_extended(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_extended(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_extended(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_extended(imm << 2, 17); > + else > + regs->csr_era = pc + LOONGARCH_INSN_SIZE; > + break; > + case jirl_op: > + regs->csr_era = regs->regs[rj] + sign_extended(imm << 2, 17); > + regs->regs[rd] = pc + LOONGARCH_INSN_SIZE; > + break; > + default: > + pr_info("%s: unknown opcode\n", __func__); > + return; > + } > +} > + > +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; > + > + switch (insn.reg1i20_format.opcode) { > + case pcaddi_op: > + regs->regs[rd] = pc + sign_extended(imm << 2, 21); > + break; > + case pcaddu12i_op: > + regs->regs[rd] = pc + sign_extended(imm << 12, 31); > + break; > + case pcaddu18i_op: > + regs->regs[rd] = pc + sign_extended(imm << 18, 37); > + break; > + case pcalau12i_op: > + regs->regs[rd] = pc + sign_extended(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; > +} > -- > 2.1.0 >
| |