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 bool insns_not_supported(union loongarch_instruction insn) 137 { 138 switch (insn.reg3_format.opcode) { 139 case amswapw_op ... ammindbdu_op: 140 pr_notice("atomic memory access instructions are not supported\n"); 141 return true; 142 } 143 144 switch (insn.reg2i14_format.opcode) { 145 case llw_op: 146 case lld_op: 147 case scw_op: 148 case scd_op: 149 pr_notice("ll and sc instructions are not supported\n"); 150 return true; 151 } 152 153 switch (insn.reg1i21_format.opcode) { 154 case bceqz_op: 155 pr_notice("bceqz and bcnez instructions are not supported\n"); 156 return true; 157 } 158 159 return false; 160 } 161 162 bool insns_need_simulation(union loongarch_instruction insn) 163 { 164 if (is_pc_ins(&insn)) 165 return true; 166 167 if (is_branch_ins(&insn)) 168 return true; 169 170 return false; 171 } 172 173 void arch_simulate_insn(union loongarch_instruction insn, struct pt_regs *regs) 174 { 175 if (is_pc_ins(&insn)) 176 simu_pc(regs, insn); 177 else if (is_branch_ins(&insn)) 178 simu_branch(regs, insn); 179 } 180 181 int larch_insn_read(void *addr, u32 *insnp) 182 { 183 int ret; 184 u32 val; 185 186 ret = copy_from_kernel_nofault(&val, addr, LOONGARCH_INSN_SIZE); 187 if (!ret) 188 *insnp = val; 189 190 return ret; 191 } 192 193 int larch_insn_write(void *addr, u32 insn) 194 { 195 int ret; 196 unsigned long flags = 0; 197 198 raw_spin_lock_irqsave(&patch_lock, flags); 199 ret = copy_to_kernel_nofault(addr, &insn, LOONGARCH_INSN_SIZE); 200 raw_spin_unlock_irqrestore(&patch_lock, flags); 201 202 return ret; 203 } 204 205 int larch_insn_patch_text(void *addr, u32 insn) 206 { 207 int ret; 208 u32 *tp = addr; 209 210 if ((unsigned long)tp & 3) 211 return -EINVAL; 212 213 ret = larch_insn_write(tp, insn); 214 if (!ret) 215 flush_icache_range((unsigned long)tp, 216 (unsigned long)tp + LOONGARCH_INSN_SIZE); 217 218 return ret; 219 } 220 221 u32 larch_insn_gen_nop(void) 222 { 223 return INSN_NOP; 224 } 225 226 u32 larch_insn_gen_b(unsigned long pc, unsigned long dest) 227 { 228 long offset = dest - pc; 229 union loongarch_instruction insn; 230 231 if ((offset & 3) || offset < -SZ_128M || offset >= SZ_128M) { 232 pr_warn("The generated b instruction is out of range.\n"); 233 return INSN_BREAK; 234 } 235 236 emit_b(&insn, offset >> 2); 237 238 return insn.word; 239 } 240 241 u32 larch_insn_gen_bl(unsigned long pc, unsigned long dest) 242 { 243 long offset = dest - pc; 244 union loongarch_instruction insn; 245 246 if ((offset & 3) || offset < -SZ_128M || offset >= SZ_128M) { 247 pr_warn("The generated bl instruction is out of range.\n"); 248 return INSN_BREAK; 249 } 250 251 emit_bl(&insn, offset >> 2); 252 253 return insn.word; 254 } 255 256 u32 larch_insn_gen_break(int imm) 257 { 258 union loongarch_instruction insn; 259 260 if (imm < 0 || imm >= SZ_32K) { 261 pr_warn("The generated break instruction is out of range.\n"); 262 return INSN_BREAK; 263 } 264 265 emit_break(&insn, imm); 266 267 return insn.word; 268 } 269 270 u32 larch_insn_gen_or(enum loongarch_gpr rd, enum loongarch_gpr rj, enum loongarch_gpr rk) 271 { 272 union loongarch_instruction insn; 273 274 emit_or(&insn, rd, rj, rk); 275 276 return insn.word; 277 } 278 279 u32 larch_insn_gen_move(enum loongarch_gpr rd, enum loongarch_gpr rj) 280 { 281 return larch_insn_gen_or(rd, rj, 0); 282 } 283 284 u32 larch_insn_gen_lu12iw(enum loongarch_gpr rd, int imm) 285 { 286 union loongarch_instruction insn; 287 288 if (imm < -SZ_512K || imm >= SZ_512K) { 289 pr_warn("The generated lu12i.w instruction is out of range.\n"); 290 return INSN_BREAK; 291 } 292 293 emit_lu12iw(&insn, rd, imm); 294 295 return insn.word; 296 } 297 298 u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm) 299 { 300 union loongarch_instruction insn; 301 302 if (imm < -SZ_512K || imm >= SZ_512K) { 303 pr_warn("The generated lu32i.d instruction is out of range.\n"); 304 return INSN_BREAK; 305 } 306 307 emit_lu32id(&insn, rd, imm); 308 309 return insn.word; 310 } 311 312 u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm) 313 { 314 union loongarch_instruction insn; 315 316 if (imm < -SZ_2K || imm >= SZ_2K) { 317 pr_warn("The generated lu52i.d instruction is out of range.\n"); 318 return INSN_BREAK; 319 } 320 321 emit_lu52id(&insn, rd, rj, imm); 322 323 return insn.word; 324 } 325 326 u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm) 327 { 328 union loongarch_instruction insn; 329 330 if ((imm & 3) || imm < -SZ_128K || imm >= SZ_128K) { 331 pr_warn("The generated jirl instruction is out of range.\n"); 332 return INSN_BREAK; 333 } 334 335 emit_jirl(&insn, rj, rd, imm >> 2); 336 337 return insn.word; 338 } 339