1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited 4 */ 5 #include <linux/sizes.h> 6 #include <linux/uaccess.h> 7 8 #include <asm/cacheflush.h> 9 #include <asm/inst.h> 10 11 static DEFINE_RAW_SPINLOCK(patch_lock); 12 13 void simu_pc(struct pt_regs *regs, union loongarch_instruction insn) 14 { 15 unsigned long pc = regs->csr_era; 16 unsigned int rd = insn.reg1i20_format.rd; 17 unsigned int imm = insn.reg1i20_format.immediate; 18 19 if (pc & 3) { 20 pr_warn("%s: invalid pc 0x%lx\n", __func__, pc); 21 return; 22 } 23 24 switch (insn.reg1i20_format.opcode) { 25 case pcaddi_op: 26 regs->regs[rd] = pc + sign_extend64(imm << 2, 21); 27 break; 28 case pcaddu12i_op: 29 regs->regs[rd] = pc + sign_extend64(imm << 12, 31); 30 break; 31 case pcaddu18i_op: 32 regs->regs[rd] = pc + sign_extend64(imm << 18, 37); 33 break; 34 case pcalau12i_op: 35 regs->regs[rd] = pc + sign_extend64(imm << 12, 31); 36 regs->regs[rd] &= ~((1 << 12) - 1); 37 break; 38 default: 39 pr_info("%s: unknown opcode\n", __func__); 40 return; 41 } 42 43 regs->csr_era += LOONGARCH_INSN_SIZE; 44 } 45 46 void simu_branch(struct pt_regs *regs, union loongarch_instruction insn) 47 { 48 unsigned int imm, imm_l, imm_h, rd, rj; 49 unsigned long pc = regs->csr_era; 50 51 if (pc & 3) { 52 pr_warn("%s: invalid pc 0x%lx\n", __func__, pc); 53 return; 54 } 55 56 imm_l = insn.reg0i26_format.immediate_l; 57 imm_h = insn.reg0i26_format.immediate_h; 58 switch (insn.reg0i26_format.opcode) { 59 case b_op: 60 regs->csr_era = pc + sign_extend64((imm_h << 16 | imm_l) << 2, 27); 61 return; 62 case bl_op: 63 regs->csr_era = pc + sign_extend64((imm_h << 16 | imm_l) << 2, 27); 64 regs->regs[1] = pc + LOONGARCH_INSN_SIZE; 65 return; 66 } 67 68 imm_l = insn.reg1i21_format.immediate_l; 69 imm_h = insn.reg1i21_format.immediate_h; 70 rj = insn.reg1i21_format.rj; 71 switch (insn.reg1i21_format.opcode) { 72 case beqz_op: 73 if (regs->regs[rj] == 0) 74 regs->csr_era = pc + sign_extend64((imm_h << 16 | imm_l) << 2, 22); 75 else 76 regs->csr_era = pc + LOONGARCH_INSN_SIZE; 77 return; 78 case bnez_op: 79 if (regs->regs[rj] != 0) 80 regs->csr_era = pc + sign_extend64((imm_h << 16 | imm_l) << 2, 22); 81 else 82 regs->csr_era = pc + LOONGARCH_INSN_SIZE; 83 return; 84 } 85 86 imm = insn.reg2i16_format.immediate; 87 rj = insn.reg2i16_format.rj; 88 rd = insn.reg2i16_format.rd; 89 switch (insn.reg2i16_format.opcode) { 90 case beq_op: 91 if (regs->regs[rj] == regs->regs[rd]) 92 regs->csr_era = pc + sign_extend64(imm << 2, 17); 93 else 94 regs->csr_era = pc + LOONGARCH_INSN_SIZE; 95 break; 96 case bne_op: 97 if (regs->regs[rj] != regs->regs[rd]) 98 regs->csr_era = pc + sign_extend64(imm << 2, 17); 99 else 100 regs->csr_era = pc + LOONGARCH_INSN_SIZE; 101 break; 102 case blt_op: 103 if ((long)regs->regs[rj] < (long)regs->regs[rd]) 104 regs->csr_era = pc + sign_extend64(imm << 2, 17); 105 else 106 regs->csr_era = pc + LOONGARCH_INSN_SIZE; 107 break; 108 case bge_op: 109 if ((long)regs->regs[rj] >= (long)regs->regs[rd]) 110 regs->csr_era = pc + sign_extend64(imm << 2, 17); 111 else 112 regs->csr_era = pc + LOONGARCH_INSN_SIZE; 113 break; 114 case bltu_op: 115 if (regs->regs[rj] < regs->regs[rd]) 116 regs->csr_era = pc + sign_extend64(imm << 2, 17); 117 else 118 regs->csr_era = pc + LOONGARCH_INSN_SIZE; 119 break; 120 case bgeu_op: 121 if (regs->regs[rj] >= regs->regs[rd]) 122 regs->csr_era = pc + sign_extend64(imm << 2, 17); 123 else 124 regs->csr_era = pc + LOONGARCH_INSN_SIZE; 125 break; 126 case jirl_op: 127 regs->csr_era = regs->regs[rj] + sign_extend64(imm << 2, 17); 128 regs->regs[rd] = pc + LOONGARCH_INSN_SIZE; 129 break; 130 default: 131 pr_info("%s: unknown opcode\n", __func__); 132 return; 133 } 134 } 135 136 int larch_insn_read(void *addr, u32 *insnp) 137 { 138 int ret; 139 u32 val; 140 141 ret = copy_from_kernel_nofault(&val, addr, LOONGARCH_INSN_SIZE); 142 if (!ret) 143 *insnp = val; 144 145 return ret; 146 } 147 148 int larch_insn_write(void *addr, u32 insn) 149 { 150 int ret; 151 unsigned long flags = 0; 152 153 raw_spin_lock_irqsave(&patch_lock, flags); 154 ret = copy_to_kernel_nofault(addr, &insn, LOONGARCH_INSN_SIZE); 155 raw_spin_unlock_irqrestore(&patch_lock, flags); 156 157 return ret; 158 } 159 160 int larch_insn_patch_text(void *addr, u32 insn) 161 { 162 int ret; 163 u32 *tp = addr; 164 165 if ((unsigned long)tp & 3) 166 return -EINVAL; 167 168 ret = larch_insn_write(tp, insn); 169 if (!ret) 170 flush_icache_range((unsigned long)tp, 171 (unsigned long)tp + LOONGARCH_INSN_SIZE); 172 173 return ret; 174 } 175 176 u32 larch_insn_gen_nop(void) 177 { 178 return INSN_NOP; 179 } 180 181 u32 larch_insn_gen_b(unsigned long pc, unsigned long dest) 182 { 183 long offset = dest - pc; 184 union loongarch_instruction insn; 185 186 if ((offset & 3) || offset < -SZ_128M || offset >= SZ_128M) { 187 pr_warn("The generated b instruction is out of range.\n"); 188 return INSN_BREAK; 189 } 190 191 emit_b(&insn, offset >> 2); 192 193 return insn.word; 194 } 195 196 u32 larch_insn_gen_bl(unsigned long pc, unsigned long dest) 197 { 198 long offset = dest - pc; 199 union loongarch_instruction insn; 200 201 if ((offset & 3) || offset < -SZ_128M || offset >= SZ_128M) { 202 pr_warn("The generated bl instruction is out of range.\n"); 203 return INSN_BREAK; 204 } 205 206 emit_bl(&insn, offset >> 2); 207 208 return insn.word; 209 } 210 211 u32 larch_insn_gen_or(enum loongarch_gpr rd, enum loongarch_gpr rj, enum loongarch_gpr rk) 212 { 213 union loongarch_instruction insn; 214 215 emit_or(&insn, rd, rj, rk); 216 217 return insn.word; 218 } 219 220 u32 larch_insn_gen_move(enum loongarch_gpr rd, enum loongarch_gpr rj) 221 { 222 return larch_insn_gen_or(rd, rj, 0); 223 } 224 225 u32 larch_insn_gen_lu12iw(enum loongarch_gpr rd, int imm) 226 { 227 union loongarch_instruction insn; 228 229 emit_lu12iw(&insn, rd, imm); 230 231 return insn.word; 232 } 233 234 u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm) 235 { 236 union loongarch_instruction insn; 237 238 emit_lu32id(&insn, rd, imm); 239 240 return insn.word; 241 } 242 243 u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm) 244 { 245 union loongarch_instruction insn; 246 247 emit_lu52id(&insn, rd, rj, imm); 248 249 return insn.word; 250 } 251 252 u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, unsigned long pc, unsigned long dest) 253 { 254 union loongarch_instruction insn; 255 256 emit_jirl(&insn, rj, rd, (dest - pc) >> 2); 257 258 return insn.word; 259 } 260