1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
5 #include <linux/sizes.h>
6 #include <linux/uaccess.h>
8 #include <asm/cacheflush.h>
11 static DEFINE_RAW_SPINLOCK(patch_lock);
13 int larch_insn_read(void *addr, u32 *insnp)
18 ret = copy_from_kernel_nofault(&val, addr, LOONGARCH_INSN_SIZE);
25 int larch_insn_write(void *addr, u32 insn)
28 unsigned long flags = 0;
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);
37 int larch_insn_patch_text(void *addr, u32 insn)
42 if ((unsigned long)tp & 3)
45 ret = larch_insn_write(tp, insn);
47 flush_icache_range((unsigned long)tp,
48 (unsigned long)tp + LOONGARCH_INSN_SIZE);
53 u32 larch_insn_gen_nop(void)
58 u32 larch_insn_gen_b(unsigned long pc, unsigned long dest)
60 long offset = dest - pc;
61 unsigned int immediate_l, immediate_h;
62 union loongarch_instruction insn;
64 if ((offset & 3) || offset < -SZ_128M || offset >= SZ_128M) {
65 pr_warn("The generated b instruction is out of range.\n");
71 immediate_l = offset & 0xffff;
73 immediate_h = offset & 0x3ff;
75 insn.reg0i26_format.opcode = b_op;
76 insn.reg0i26_format.immediate_l = immediate_l;
77 insn.reg0i26_format.immediate_h = immediate_h;
82 u32 larch_insn_gen_bl(unsigned long pc, unsigned long dest)
84 long offset = dest - pc;
85 unsigned int immediate_l, immediate_h;
86 union loongarch_instruction insn;
88 if ((offset & 3) || offset < -SZ_128M || offset >= SZ_128M) {
89 pr_warn("The generated bl instruction is out of range.\n");
95 immediate_l = offset & 0xffff;
97 immediate_h = offset & 0x3ff;
99 insn.reg0i26_format.opcode = bl_op;
100 insn.reg0i26_format.immediate_l = immediate_l;
101 insn.reg0i26_format.immediate_h = immediate_h;
106 u32 larch_insn_gen_or(enum loongarch_gpr rd, enum loongarch_gpr rj, enum loongarch_gpr rk)
108 union loongarch_instruction insn;
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;
118 u32 larch_insn_gen_move(enum loongarch_gpr rd, enum loongarch_gpr rj)
120 return larch_insn_gen_or(rd, rj, 0);
123 u32 larch_insn_gen_lu12iw(enum loongarch_gpr rd, int imm)
125 union loongarch_instruction insn;
127 insn.reg1i20_format.opcode = lu12iw_op;
128 insn.reg1i20_format.rd = rd;
129 insn.reg1i20_format.immediate = imm;
134 u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm)
136 union loongarch_instruction insn;
138 insn.reg1i20_format.opcode = lu32id_op;
139 insn.reg1i20_format.rd = rd;
140 insn.reg1i20_format.immediate = imm;
145 u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm)
147 union loongarch_instruction insn;
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;
157 u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, unsigned long pc, unsigned long dest)
159 union loongarch_instruction insn;
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;