|
4 | 4 | */ |
5 | 5 | #include <linux/sizes.h> |
6 | 6 | #include <linux/uaccess.h> |
| 7 | +#include <linux/set_memory.h> |
| 8 | +#include <linux/stop_machine.h> |
7 | 9 |
|
8 | 10 | #include <asm/cacheflush.h> |
9 | 11 | #include <asm/inst.h> |
@@ -218,6 +220,50 @@ int larch_insn_patch_text(void *addr, u32 insn) |
218 | 220 | return ret; |
219 | 221 | } |
220 | 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 | + |
221 | 267 | u32 larch_insn_gen_nop(void) |
222 | 268 | { |
223 | 269 | return INSN_NOP; |
@@ -323,6 +369,34 @@ u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm) |
323 | 369 | return insn.word; |
324 | 370 | } |
325 | 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 | + |
326 | 400 | u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm) |
327 | 401 | { |
328 | 402 | union loongarch_instruction insn; |
|
0 commit comments