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 int larch_insn_read(void *addr, u32 *insnp) 14 { 15 int ret; 16 u32 val; 17 18 ret = copy_from_kernel_nofault(&val, addr, LOONGARCH_INSN_SIZE); 19 if (!ret) 20 *insnp = val; 21 22 return ret; 23 } 24 25 int larch_insn_write(void *addr, u32 insn) 26 { 27 int ret; 28 unsigned long flags = 0; 29 30 raw_spin_lock_irqsave(&patch_lock, flags); 31 ret = copy_to_kernel_nofault(addr, &insn, LOONGARCH_INSN_SIZE); 32 raw_spin_unlock_irqrestore(&patch_lock, flags); 33 34 return ret; 35 } 36 37 int larch_insn_patch_text(void *addr, u32 insn) 38 { 39 int ret; 40 u32 *tp = addr; 41 42 if ((unsigned long)tp & 3) 43 return -EINVAL; 44 45 ret = larch_insn_write(tp, insn); 46 if (!ret) 47 flush_icache_range((unsigned long)tp, 48 (unsigned long)tp + LOONGARCH_INSN_SIZE); 49 50 return ret; 51 } 52 53 u32 larch_insn_gen_nop(void) 54 { 55 return INSN_NOP; 56 } 57 58 u32 larch_insn_gen_b(unsigned long pc, unsigned long dest) 59 { 60 long offset = dest - pc; 61 union loongarch_instruction insn; 62 63 if ((offset & 3) || offset < -SZ_128M || offset >= SZ_128M) { 64 pr_warn("The generated b instruction is out of range.\n"); 65 return INSN_BREAK; 66 } 67 68 emit_b(&insn, offset >> 2); 69 70 return insn.word; 71 } 72 73 u32 larch_insn_gen_bl(unsigned long pc, unsigned long dest) 74 { 75 long offset = dest - pc; 76 union loongarch_instruction insn; 77 78 if ((offset & 3) || offset < -SZ_128M || offset >= SZ_128M) { 79 pr_warn("The generated bl instruction is out of range.\n"); 80 return INSN_BREAK; 81 } 82 83 emit_bl(&insn, offset >> 2); 84 85 return insn.word; 86 } 87 88 u32 larch_insn_gen_or(enum loongarch_gpr rd, enum loongarch_gpr rj, enum loongarch_gpr rk) 89 { 90 union loongarch_instruction insn; 91 92 emit_or(&insn, rd, rj, rk); 93 94 return insn.word; 95 } 96 97 u32 larch_insn_gen_move(enum loongarch_gpr rd, enum loongarch_gpr rj) 98 { 99 return larch_insn_gen_or(rd, rj, 0); 100 } 101 102 u32 larch_insn_gen_lu12iw(enum loongarch_gpr rd, int imm) 103 { 104 union loongarch_instruction insn; 105 106 emit_lu12iw(&insn, rd, imm); 107 108 return insn.word; 109 } 110 111 u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm) 112 { 113 union loongarch_instruction insn; 114 115 emit_lu32id(&insn, rd, imm); 116 117 return insn.word; 118 } 119 120 u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm) 121 { 122 union loongarch_instruction insn; 123 124 emit_lu52id(&insn, rd, rj, imm); 125 126 return insn.word; 127 } 128 129 u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, unsigned long pc, unsigned long dest) 130 { 131 union loongarch_instruction insn; 132 133 emit_jirl(&insn, rj, rd, (dest - pc) >> 2); 134 135 return insn.word; 136 } 137