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 #include <linux/set_memory.h> 8 #include <linux/stop_machine.h> 9 10 #include <asm/cacheflush.h> 11 #include <asm/inst.h> 12 13 static DEFINE_RAW_SPINLOCK(patch_lock); 14 15 void simu_pc(struct pt_regs *regs, union loongarch_instruction insn) 16 { 17 unsigned long pc = regs->csr_era; 18 unsigned int rd = insn.reg1i20_format.rd; 19 unsigned int imm = insn.reg1i20_format.immediate; 20 21 if (pc & 3) { 22 pr_warn("%s: invalid pc 0x%lx\n", __func__, pc); 23 return; 24 } 25 26 switch (insn.reg1i20_format.opcode) { 27 case pcaddi_op: 28 regs->regs[rd] = pc + sign_extend64(imm << 2, 21); 29 break; 30 case pcaddu12i_op: 31 regs->regs[rd] = pc + sign_extend64(imm << 12, 31); 32 break; 33 case pcaddu18i_op: 34 regs->regs[rd] = pc + sign_extend64(imm << 18, 37); 35 break; 36 case pcalau12i_op: 37 regs->regs[rd] = pc + sign_extend64(imm << 12, 31); 38 regs->regs[rd] &= ~((1 << 12) - 1); 39 break; 40 default: 41 pr_info("%s: unknown opcode\n", __func__); 42 return; 43 } 44 45 regs->csr_era += LOONGARCH_INSN_SIZE; 46 } 47 48 void simu_branch(struct pt_regs *regs, union loongarch_instruction insn) 49 { 50 unsigned int imm, imm_l, imm_h, rd, rj; 51 unsigned long pc = regs->csr_era; 52 53 if (pc & 3) { 54 pr_warn("%s: invalid pc 0x%lx\n", __func__, pc); 55 return; 56 } 57 58 imm_l = insn.reg0i26_format.immediate_l; 59 imm_h = insn.reg0i26_format.immediate_h; 60 switch (insn.reg0i26_format.opcode) { 61 case b_op: 62 regs->csr_era = pc + sign_extend64((imm_h << 16 | imm_l) << 2, 27); 63 return; 64 case bl_op: 65 regs->csr_era = pc + sign_extend64((imm_h << 16 | imm_l) << 2, 27); 66 regs->regs[1] = pc + LOONGARCH_INSN_SIZE; 67 return; 68 } 69 70 imm_l = insn.reg1i21_format.immediate_l; 71 imm_h = insn.reg1i21_format.immediate_h; 72 rj = insn.reg1i21_format.rj; 73 switch (insn.reg1i21_format.opcode) { 74 case beqz_op: 75 if (regs->regs[rj] == 0) 76 regs->csr_era = pc + sign_extend64((imm_h << 16 | imm_l) << 2, 22); 77 else 78 regs->csr_era = pc + LOONGARCH_INSN_SIZE; 79 return; 80 case bnez_op: 81 if (regs->regs[rj] != 0) 82 regs->csr_era = pc + sign_extend64((imm_h << 16 | imm_l) << 2, 22); 83 else 84 regs->csr_era = pc + LOONGARCH_INSN_SIZE; 85 return; 86 } 87 88 imm = insn.reg2i16_format.immediate; 89 rj = insn.reg2i16_format.rj; 90 rd = insn.reg2i16_format.rd; 91 switch (insn.reg2i16_format.opcode) { 92 case beq_op: 93 if (regs->regs[rj] == regs->regs[rd]) 94 regs->csr_era = pc + sign_extend64(imm << 2, 17); 95 else 96 regs->csr_era = pc + LOONGARCH_INSN_SIZE; 97 break; 98 case bne_op: 99 if (regs->regs[rj] != regs->regs[rd]) 100 regs->csr_era = pc + sign_extend64(imm << 2, 17); 101 else 102 regs->csr_era = pc + LOONGARCH_INSN_SIZE; 103 break; 104 case blt_op: 105 if ((long)regs->regs[rj] < (long)regs->regs[rd]) 106 regs->csr_era = pc + sign_extend64(imm << 2, 17); 107 else 108 regs->csr_era = pc + LOONGARCH_INSN_SIZE; 109 break; 110 case bge_op: 111 if ((long)regs->regs[rj] >= (long)regs->regs[rd]) 112 regs->csr_era = pc + sign_extend64(imm << 2, 17); 113 else 114 regs->csr_era = pc + LOONGARCH_INSN_SIZE; 115 break; 116 case bltu_op: 117 if (regs->regs[rj] < regs->regs[rd]) 118 regs->csr_era = pc + sign_extend64(imm << 2, 17); 119 else 120 regs->csr_era = pc + LOONGARCH_INSN_SIZE; 121 break; 122 case bgeu_op: 123 if (regs->regs[rj] >= regs->regs[rd]) 124 regs->csr_era = pc + sign_extend64(imm << 2, 17); 125 else 126 regs->csr_era = pc + LOONGARCH_INSN_SIZE; 127 break; 128 case jirl_op: 129 regs->csr_era = regs->regs[rj] + sign_extend64(imm << 2, 17); 130 regs->regs[rd] = pc + LOONGARCH_INSN_SIZE; 131 break; 132 default: 133 pr_info("%s: unknown opcode\n", __func__); 134 return; 135 } 136 } 137 138 bool insns_not_supported(union loongarch_instruction insn) 139 { 140 switch (insn.reg3_format.opcode) { 141 case amswapw_op ... ammindbdu_op: 142 pr_notice("atomic memory access instructions are not supported\n"); 143 return true; 144 } 145 146 switch (insn.reg2i14_format.opcode) { 147 case llw_op: 148 case lld_op: 149 case scw_op: 150 case scd_op: 151 pr_notice("ll and sc instructions are not supported\n"); 152 return true; 153 } 154 155 switch (insn.reg1i21_format.opcode) { 156 case bceqz_op: 157 pr_notice("bceqz and bcnez instructions are not supported\n"); 158 return true; 159 } 160 161 return false; 162 } 163 164 bool insns_need_simulation(union loongarch_instruction insn) 165 { 166 if (is_pc_ins(&insn)) 167 return true; 168 169 if (is_branch_ins(&insn)) 170 return true; 171 172 return false; 173 } 174 175 void arch_simulate_insn(union loongarch_instruction insn, struct pt_regs *regs) 176 { 177 if (is_pc_ins(&insn)) 178 simu_pc(regs, insn); 179 else if (is_branch_ins(&insn)) 180 simu_branch(regs, insn); 181 } 182 183 int larch_insn_read(void *addr, u32 *insnp) 184 { 185 int ret; 186 u32 val; 187 188 ret = copy_from_kernel_nofault(&val, addr, LOONGARCH_INSN_SIZE); 189 if (!ret) 190 *insnp = val; 191 192 return ret; 193 } 194 195 int larch_insn_write(void *addr, u32 insn) 196 { 197 int ret; 198 unsigned long flags = 0; 199 200 raw_spin_lock_irqsave(&patch_lock, flags); 201 ret = copy_to_kernel_nofault(addr, &insn, LOONGARCH_INSN_SIZE); 202 raw_spin_unlock_irqrestore(&patch_lock, flags); 203 204 return ret; 205 } 206 207 int larch_insn_patch_text(void *addr, u32 insn) 208 { 209 int ret; 210 u32 *tp = addr; 211 212 if ((unsigned long)tp & 3) 213 return -EINVAL; 214 215 ret = larch_insn_write(tp, insn); 216 if (!ret) 217 flush_icache_range((unsigned long)tp, 218 (unsigned long)tp + LOONGARCH_INSN_SIZE); 219 220 return ret; 221 } 222 223 struct insn_copy { 224 void *dst; 225 void *src; 226 size_t len; 227 unsigned int cpu; 228 }; 229 230 static int text_copy_cb(void *data) 231 { 232 int ret = 0; 233 struct insn_copy *copy = data; 234 235 if (smp_processor_id() == copy->cpu) { 236 ret = copy_to_kernel_nofault(copy->dst, copy->src, copy->len); 237 if (ret) 238 pr_err("%s: operation failed\n", __func__); 239 } 240 241 flush_icache_range((unsigned long)copy->dst, (unsigned long)copy->dst + copy->len); 242 243 return ret; 244 } 245 246 int larch_insn_text_copy(void *dst, void *src, size_t len) 247 { 248 int ret = 0; 249 size_t start, end; 250 struct insn_copy copy = { 251 .dst = dst, 252 .src = src, 253 .len = len, 254 .cpu = smp_processor_id(), 255 }; 256 257 start = round_down((size_t)dst, PAGE_SIZE); 258 end = round_up((size_t)dst + len, PAGE_SIZE); 259 260 set_memory_rw(start, (end - start) / PAGE_SIZE); 261 ret = stop_machine(text_copy_cb, ©, cpu_online_mask); 262 set_memory_rox(start, (end - start) / PAGE_SIZE); 263 264 return ret; 265 } 266 267 u32 larch_insn_gen_nop(void) 268 { 269 return INSN_NOP; 270 } 271 272 u32 larch_insn_gen_b(unsigned long pc, unsigned long dest) 273 { 274 long offset = dest - pc; 275 union loongarch_instruction insn; 276 277 if ((offset & 3) || offset < -SZ_128M || offset >= SZ_128M) { 278 pr_warn("The generated b instruction is out of range.\n"); 279 return INSN_BREAK; 280 } 281 282 emit_b(&insn, offset >> 2); 283 284 return insn.word; 285 } 286 287 u32 larch_insn_gen_bl(unsigned long pc, unsigned long dest) 288 { 289 long offset = dest - pc; 290 union loongarch_instruction insn; 291 292 if ((offset & 3) || offset < -SZ_128M || offset >= SZ_128M) { 293 pr_warn("The generated bl instruction is out of range.\n"); 294 return INSN_BREAK; 295 } 296 297 emit_bl(&insn, offset >> 2); 298 299 return insn.word; 300 } 301 302 u32 larch_insn_gen_break(int imm) 303 { 304 union loongarch_instruction insn; 305 306 if (imm < 0 || imm >= SZ_32K) { 307 pr_warn("The generated break instruction is out of range.\n"); 308 return INSN_BREAK; 309 } 310 311 emit_break(&insn, imm); 312 313 return insn.word; 314 } 315 316 u32 larch_insn_gen_or(enum loongarch_gpr rd, enum loongarch_gpr rj, enum loongarch_gpr rk) 317 { 318 union loongarch_instruction insn; 319 320 emit_or(&insn, rd, rj, rk); 321 322 return insn.word; 323 } 324 325 u32 larch_insn_gen_move(enum loongarch_gpr rd, enum loongarch_gpr rj) 326 { 327 return larch_insn_gen_or(rd, rj, 0); 328 } 329 330 u32 larch_insn_gen_lu12iw(enum loongarch_gpr rd, int imm) 331 { 332 union loongarch_instruction insn; 333 334 if (imm < -SZ_512K || imm >= SZ_512K) { 335 pr_warn("The generated lu12i.w instruction is out of range.\n"); 336 return INSN_BREAK; 337 } 338 339 emit_lu12iw(&insn, rd, imm); 340 341 return insn.word; 342 } 343 344 u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm) 345 { 346 union loongarch_instruction insn; 347 348 if (imm < -SZ_512K || imm >= SZ_512K) { 349 pr_warn("The generated lu32i.d instruction is out of range.\n"); 350 return INSN_BREAK; 351 } 352 353 emit_lu32id(&insn, rd, imm); 354 355 return insn.word; 356 } 357 358 u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm) 359 { 360 union loongarch_instruction insn; 361 362 if (imm < -SZ_2K || imm >= SZ_2K) { 363 pr_warn("The generated lu52i.d instruction is out of range.\n"); 364 return INSN_BREAK; 365 } 366 367 emit_lu52id(&insn, rd, rj, imm); 368 369 return insn.word; 370 } 371 372 u32 larch_insn_gen_beq(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm) 373 { 374 union loongarch_instruction insn; 375 376 if ((imm & 3) || imm < -SZ_128K || imm >= SZ_128K) { 377 pr_warn("The generated beq instruction is out of range.\n"); 378 return INSN_BREAK; 379 } 380 381 emit_beq(&insn, rj, rd, imm >> 2); 382 383 return insn.word; 384 } 385 386 u32 larch_insn_gen_bne(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm) 387 { 388 union loongarch_instruction insn; 389 390 if ((imm & 3) || imm < -SZ_128K || imm >= SZ_128K) { 391 pr_warn("The generated bne instruction is out of range.\n"); 392 return INSN_BREAK; 393 } 394 395 emit_bne(&insn, rj, rd, imm >> 2); 396 397 return insn.word; 398 } 399 400 u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm) 401 { 402 union loongarch_instruction insn; 403 404 if ((imm & 3) || imm < -SZ_128K || imm >= SZ_128K) { 405 pr_warn("The generated jirl instruction is out of range.\n"); 406 return INSN_BREAK; 407 } 408 409 emit_jirl(&insn, rd, rj, imm >> 2); 410 411 return insn.word; 412 } 413