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 unsigned int immediate_l, immediate_h; 62 union loongarch_instruction insn; 63 64 if ((offset & 3) || offset < -SZ_128M || offset >= SZ_128M) { 65 pr_warn("The generated b instruction is out of range.\n"); 66 return INSN_BREAK; 67 } 68 69 offset >>= 2; 70 71 immediate_l = offset & 0xffff; 72 offset >>= 16; 73 immediate_h = offset & 0x3ff; 74 75 insn.reg0i26_format.opcode = b_op; 76 insn.reg0i26_format.immediate_l = immediate_l; 77 insn.reg0i26_format.immediate_h = immediate_h; 78 79 return insn.word; 80 } 81 82 u32 larch_insn_gen_bl(unsigned long pc, unsigned long dest) 83 { 84 long offset = dest - pc; 85 unsigned int immediate_l, immediate_h; 86 union loongarch_instruction insn; 87 88 if ((offset & 3) || offset < -SZ_128M || offset >= SZ_128M) { 89 pr_warn("The generated bl instruction is out of range.\n"); 90 return INSN_BREAK; 91 } 92 93 offset >>= 2; 94 95 immediate_l = offset & 0xffff; 96 offset >>= 16; 97 immediate_h = offset & 0x3ff; 98 99 insn.reg0i26_format.opcode = bl_op; 100 insn.reg0i26_format.immediate_l = immediate_l; 101 insn.reg0i26_format.immediate_h = immediate_h; 102 103 return insn.word; 104 } 105 106 u32 larch_insn_gen_or(enum loongarch_gpr rd, enum loongarch_gpr rj, enum loongarch_gpr rk) 107 { 108 union loongarch_instruction insn; 109 110 insn.reg3_format.opcode = or_op; 111 insn.reg3_format.rd = rd; 112 insn.reg3_format.rj = rj; 113 insn.reg3_format.rk = rk; 114 115 return insn.word; 116 } 117 118 u32 larch_insn_gen_move(enum loongarch_gpr rd, enum loongarch_gpr rj) 119 { 120 return larch_insn_gen_or(rd, rj, 0); 121 } 122 123 u32 larch_insn_gen_lu12iw(enum loongarch_gpr rd, int imm) 124 { 125 union loongarch_instruction insn; 126 127 insn.reg1i20_format.opcode = lu12iw_op; 128 insn.reg1i20_format.rd = rd; 129 insn.reg1i20_format.immediate = imm; 130 131 return insn.word; 132 } 133 134 u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm) 135 { 136 union loongarch_instruction insn; 137 138 insn.reg1i20_format.opcode = lu32id_op; 139 insn.reg1i20_format.rd = rd; 140 insn.reg1i20_format.immediate = imm; 141 142 return insn.word; 143 } 144 145 u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm) 146 { 147 union loongarch_instruction insn; 148 149 insn.reg2i12_format.opcode = lu52id_op; 150 insn.reg2i12_format.rd = rd; 151 insn.reg2i12_format.rj = rj; 152 insn.reg2i12_format.immediate = imm; 153 154 return insn.word; 155 } 156 157 u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, unsigned long pc, unsigned long dest) 158 { 159 union loongarch_instruction insn; 160 161 insn.reg2i16_format.opcode = jirl_op; 162 insn.reg2i16_format.rd = rd; 163 insn.reg2i16_format.rj = rj; 164 insn.reg2i16_format.immediate = (dest - pc) >> 2; 165 166 return insn.word; 167 } 168