arm64: dts: qcom: sm8550: add TRNG node
[linux-modified.git] / arch / loongarch / kernel / inst.c
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 }