~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/loongarch/kernel/inst.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php