Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Tiezhu Yang | 948 | 58.05% | 5 | 35.71% |
Qing Zhang | 361 | 22.11% | 4 | 28.57% |
Huacai Chen | 202 | 12.37% | 3 | 21.43% |
Rui Y Wang | 73 | 4.47% | 1 | 7.14% |
Youling Tang | 49 | 3.00% | 1 | 7.14% |
Total | 1633 | 14 |
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2020-2022 Loongson Technology Corporation Limited */ #include <linux/sizes.h> #include <linux/uaccess.h> #include <asm/cacheflush.h> #include <asm/inst.h> 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; } } bool insns_not_supported(union loongarch_instruction insn) { switch (insn.reg3_format.opcode) { case amswapw_op ... ammindbdu_op: pr_notice("atomic memory access instructions are not supported\n"); return true; } switch (insn.reg2i14_format.opcode) { case llw_op: case lld_op: case scw_op: case scd_op: pr_notice("ll and sc instructions are not supported\n"); return true; } switch (insn.reg1i21_format.opcode) { case bceqz_op: pr_notice("bceqz and bcnez instructions are not supported\n"); return true; } return false; } bool insns_need_simulation(union loongarch_instruction insn) { if (is_pc_ins(&insn)) return true; if (is_branch_ins(&insn)) return true; return false; } void arch_simulate_insn(union loongarch_instruction insn, struct pt_regs *regs) { if (is_pc_ins(&insn)) simu_pc(regs, insn); else if (is_branch_ins(&insn)) simu_branch(regs, insn); } int larch_insn_read(void *addr, u32 *insnp) { int ret; u32 val; ret = copy_from_kernel_nofault(&val, addr, LOONGARCH_INSN_SIZE); if (!ret) *insnp = val; return ret; } int larch_insn_write(void *addr, u32 insn) { int ret; unsigned long flags = 0; raw_spin_lock_irqsave(&patch_lock, flags); ret = copy_to_kernel_nofault(addr, &insn, LOONGARCH_INSN_SIZE); raw_spin_unlock_irqrestore(&patch_lock, flags); return ret; } int larch_insn_patch_text(void *addr, u32 insn) { int ret; u32 *tp = addr; if ((unsigned long)tp & 3) return -EINVAL; ret = larch_insn_write(tp, insn); if (!ret) flush_icache_range((unsigned long)tp, (unsigned long)tp + LOONGARCH_INSN_SIZE); return ret; } u32 larch_insn_gen_nop(void) { return INSN_NOP; } u32 larch_insn_gen_b(unsigned long pc, unsigned long dest) { long offset = dest - pc; union loongarch_instruction insn; if ((offset & 3) || offset < -SZ_128M || offset >= SZ_128M) { pr_warn("The generated b instruction is out of range.\n"); return INSN_BREAK; } emit_b(&insn, offset >> 2); return insn.word; } u32 larch_insn_gen_bl(unsigned long pc, unsigned long dest) { long offset = dest - pc; union loongarch_instruction insn; if ((offset & 3) || offset < -SZ_128M || offset >= SZ_128M) { pr_warn("The generated bl instruction is out of range.\n"); return INSN_BREAK; } emit_bl(&insn, offset >> 2); return insn.word; } u32 larch_insn_gen_break(int imm) { union loongarch_instruction insn; if (imm < 0 || imm >= SZ_32K) { pr_warn("The generated break instruction is out of range.\n"); return INSN_BREAK; } emit_break(&insn, imm); return insn.word; } u32 larch_insn_gen_or(enum loongarch_gpr rd, enum loongarch_gpr rj, enum loongarch_gpr rk) { union loongarch_instruction insn; emit_or(&insn, rd, rj, rk); return insn.word; } u32 larch_insn_gen_move(enum loongarch_gpr rd, enum loongarch_gpr rj) { return larch_insn_gen_or(rd, rj, 0); } u32 larch_insn_gen_lu12iw(enum loongarch_gpr rd, int imm) { union loongarch_instruction insn; if (imm < -SZ_512K || imm >= SZ_512K) { pr_warn("The generated lu12i.w instruction is out of range.\n"); return INSN_BREAK; } emit_lu12iw(&insn, rd, imm); return insn.word; } u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm) { union loongarch_instruction insn; if (imm < -SZ_512K || imm >= SZ_512K) { pr_warn("The generated lu32i.d instruction is out of range.\n"); return INSN_BREAK; } emit_lu32id(&insn, rd, imm); return insn.word; } u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm) { union loongarch_instruction insn; if (imm < -SZ_2K || imm >= SZ_2K) { pr_warn("The generated lu52i.d instruction is out of range.\n"); return INSN_BREAK; } emit_lu52id(&insn, rd, rj, imm); return insn.word; } u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm) { union loongarch_instruction insn; if ((imm & 3) || imm < -SZ_128K || imm >= SZ_128K) { pr_warn("The generated jirl instruction is out of range.\n"); return INSN_BREAK; } emit_jirl(&insn, rj, rd, imm >> 2); return insn.word; }
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1