1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Handle unaligned accesses by emulation.
4 *
5 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
6 *
7 * Derived from MIPS:
8 * Copyright (C) 1996, 1998, 1999, 2002 by Ralf Baechle
9 * Copyright (C) 1999 Silicon Graphics, Inc.
10 * Copyright (C) 2014 Imagination Technologies Ltd.
11 */
12 #include <linux/mm.h>
13 #include <linux/sched.h>
14 #include <linux/signal.h>
15 #include <linux/debugfs.h>
16 #include <linux/perf_event.h>
17
18 #include <asm/asm.h>
19 #include <asm/branch.h>
20 #include <asm/fpu.h>
21 #include <asm/inst.h>
22
23 #include "access-helper.h"
24
25 #ifdef CONFIG_DEBUG_FS
26 static u32 unaligned_instructions_user;
27 static u32 unaligned_instructions_kernel;
28 #endif
29
read_fpr(unsigned int idx)30 static inline u64 read_fpr(unsigned int idx)
31 {
32 #ifdef CONFIG_64BIT
33 #define READ_FPR(idx, __value) \
34 __asm__ __volatile__("movfr2gr.d %0, $f"#idx"\n\t" : "=r"(__value));
35 #else
36 #define READ_FPR(idx, __value) \
37 { \
38 u32 __value_lo, __value_hi; \
39 __asm__ __volatile__("movfr2gr.s %0, $f"#idx"\n\t" : "=r"(__value_lo)); \
40 __asm__ __volatile__("movfrh2gr.s %0, $f"#idx"\n\t" : "=r"(__value_hi)); \
41 __value = (__value_lo | ((u64)__value_hi << 32)); \
42 }
43 #endif
44 u64 __value;
45
46 switch (idx) {
47 case 0:
48 READ_FPR(0, __value);
49 break;
50 case 1:
51 READ_FPR(1, __value);
52 break;
53 case 2:
54 READ_FPR(2, __value);
55 break;
56 case 3:
57 READ_FPR(3, __value);
58 break;
59 case 4:
60 READ_FPR(4, __value);
61 break;
62 case 5:
63 READ_FPR(5, __value);
64 break;
65 case 6:
66 READ_FPR(6, __value);
67 break;
68 case 7:
69 READ_FPR(7, __value);
70 break;
71 case 8:
72 READ_FPR(8, __value);
73 break;
74 case 9:
75 READ_FPR(9, __value);
76 break;
77 case 10:
78 READ_FPR(10, __value);
79 break;
80 case 11:
81 READ_FPR(11, __value);
82 break;
83 case 12:
84 READ_FPR(12, __value);
85 break;
86 case 13:
87 READ_FPR(13, __value);
88 break;
89 case 14:
90 READ_FPR(14, __value);
91 break;
92 case 15:
93 READ_FPR(15, __value);
94 break;
95 case 16:
96 READ_FPR(16, __value);
97 break;
98 case 17:
99 READ_FPR(17, __value);
100 break;
101 case 18:
102 READ_FPR(18, __value);
103 break;
104 case 19:
105 READ_FPR(19, __value);
106 break;
107 case 20:
108 READ_FPR(20, __value);
109 break;
110 case 21:
111 READ_FPR(21, __value);
112 break;
113 case 22:
114 READ_FPR(22, __value);
115 break;
116 case 23:
117 READ_FPR(23, __value);
118 break;
119 case 24:
120 READ_FPR(24, __value);
121 break;
122 case 25:
123 READ_FPR(25, __value);
124 break;
125 case 26:
126 READ_FPR(26, __value);
127 break;
128 case 27:
129 READ_FPR(27, __value);
130 break;
131 case 28:
132 READ_FPR(28, __value);
133 break;
134 case 29:
135 READ_FPR(29, __value);
136 break;
137 case 30:
138 READ_FPR(30, __value);
139 break;
140 case 31:
141 READ_FPR(31, __value);
142 break;
143 default:
144 panic("unexpected idx '%d'", idx);
145 }
146 #undef READ_FPR
147 return __value;
148 }
149
write_fpr(unsigned int idx,u64 value)150 static inline void write_fpr(unsigned int idx, u64 value)
151 {
152 #ifdef CONFIG_64BIT
153 #define WRITE_FPR(idx, value) \
154 __asm__ __volatile__("movgr2fr.d $f"#idx", %0\n\t" :: "r"(value));
155 #else
156 #define WRITE_FPR(idx, value) \
157 { \
158 u32 value_lo = value; \
159 u32 value_hi = value >> 32; \
160 __asm__ __volatile__("movgr2fr.w $f"#idx", %0\n\t" :: "r"(value_lo)); \
161 __asm__ __volatile__("movgr2frh.w $f"#idx", %0\n\t" :: "r"(value_hi)); \
162 }
163 #endif
164 switch (idx) {
165 case 0:
166 WRITE_FPR(0, value);
167 break;
168 case 1:
169 WRITE_FPR(1, value);
170 break;
171 case 2:
172 WRITE_FPR(2, value);
173 break;
174 case 3:
175 WRITE_FPR(3, value);
176 break;
177 case 4:
178 WRITE_FPR(4, value);
179 break;
180 case 5:
181 WRITE_FPR(5, value);
182 break;
183 case 6:
184 WRITE_FPR(6, value);
185 break;
186 case 7:
187 WRITE_FPR(7, value);
188 break;
189 case 8:
190 WRITE_FPR(8, value);
191 break;
192 case 9:
193 WRITE_FPR(9, value);
194 break;
195 case 10:
196 WRITE_FPR(10, value);
197 break;
198 case 11:
199 WRITE_FPR(11, value);
200 break;
201 case 12:
202 WRITE_FPR(12, value);
203 break;
204 case 13:
205 WRITE_FPR(13, value);
206 break;
207 case 14:
208 WRITE_FPR(14, value);
209 break;
210 case 15:
211 WRITE_FPR(15, value);
212 break;
213 case 16:
214 WRITE_FPR(16, value);
215 break;
216 case 17:
217 WRITE_FPR(17, value);
218 break;
219 case 18:
220 WRITE_FPR(18, value);
221 break;
222 case 19:
223 WRITE_FPR(19, value);
224 break;
225 case 20:
226 WRITE_FPR(20, value);
227 break;
228 case 21:
229 WRITE_FPR(21, value);
230 break;
231 case 22:
232 WRITE_FPR(22, value);
233 break;
234 case 23:
235 WRITE_FPR(23, value);
236 break;
237 case 24:
238 WRITE_FPR(24, value);
239 break;
240 case 25:
241 WRITE_FPR(25, value);
242 break;
243 case 26:
244 WRITE_FPR(26, value);
245 break;
246 case 27:
247 WRITE_FPR(27, value);
248 break;
249 case 28:
250 WRITE_FPR(28, value);
251 break;
252 case 29:
253 WRITE_FPR(29, value);
254 break;
255 case 30:
256 WRITE_FPR(30, value);
257 break;
258 case 31:
259 WRITE_FPR(31, value);
260 break;
261 default:
262 panic("unexpected idx '%d'", idx);
263 }
264 #undef WRITE_FPR
265 }
266
emulate_load_store_insn(struct pt_regs * regs,void __user * addr,unsigned int * pc)267 void emulate_load_store_insn(struct pt_regs *regs, void __user *addr, unsigned int *pc)
268 {
269 bool fp = false;
270 bool sign, write;
271 bool user = user_mode(regs);
272 unsigned int res, size = 0;
273 u64 value = 0;
274 union loongarch_instruction insn;
275
276 perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0);
277
278 __get_inst(&insn.word, pc, user);
279
280 switch (insn.reg2i12_format.opcode) {
281 case ldh_op:
282 size = 2;
283 sign = true;
284 write = false;
285 break;
286 case ldhu_op:
287 size = 2;
288 sign = false;
289 write = false;
290 break;
291 case sth_op:
292 size = 2;
293 sign = true;
294 write = true;
295 break;
296 case ldw_op:
297 size = 4;
298 sign = true;
299 write = false;
300 break;
301 case ldwu_op:
302 size = 4;
303 sign = false;
304 write = false;
305 break;
306 case stw_op:
307 size = 4;
308 sign = true;
309 write = true;
310 break;
311 case ldd_op:
312 size = 8;
313 sign = true;
314 write = false;
315 break;
316 case std_op:
317 size = 8;
318 sign = true;
319 write = true;
320 break;
321 case flds_op:
322 size = 4;
323 fp = true;
324 sign = true;
325 write = false;
326 break;
327 case fsts_op:
328 size = 4;
329 fp = true;
330 sign = true;
331 write = true;
332 break;
333 case fldd_op:
334 size = 8;
335 fp = true;
336 sign = true;
337 write = false;
338 break;
339 case fstd_op:
340 size = 8;
341 fp = true;
342 sign = true;
343 write = true;
344 break;
345 }
346
347 switch (insn.reg2i14_format.opcode) {
348 case ldptrw_op:
349 size = 4;
350 sign = true;
351 write = false;
352 break;
353 case stptrw_op:
354 size = 4;
355 sign = true;
356 write = true;
357 break;
358 case ldptrd_op:
359 size = 8;
360 sign = true;
361 write = false;
362 break;
363 case stptrd_op:
364 size = 8;
365 sign = true;
366 write = true;
367 break;
368 }
369
370 switch (insn.reg3_format.opcode) {
371 case ldxh_op:
372 size = 2;
373 sign = true;
374 write = false;
375 break;
376 case ldxhu_op:
377 size = 2;
378 sign = false;
379 write = false;
380 break;
381 case stxh_op:
382 size = 2;
383 sign = true;
384 write = true;
385 break;
386 case ldxw_op:
387 size = 4;
388 sign = true;
389 write = false;
390 break;
391 case ldxwu_op:
392 size = 4;
393 sign = false;
394 write = false;
395 break;
396 case stxw_op:
397 size = 4;
398 sign = true;
399 write = true;
400 break;
401 case ldxd_op:
402 size = 8;
403 sign = true;
404 write = false;
405 break;
406 case stxd_op:
407 size = 8;
408 sign = true;
409 write = true;
410 break;
411 case fldxs_op:
412 size = 4;
413 fp = true;
414 sign = true;
415 write = false;
416 break;
417 case fstxs_op:
418 size = 4;
419 fp = true;
420 sign = true;
421 write = true;
422 break;
423 case fldxd_op:
424 size = 8;
425 fp = true;
426 sign = true;
427 write = false;
428 break;
429 case fstxd_op:
430 size = 8;
431 fp = true;
432 sign = true;
433 write = true;
434 break;
435 }
436
437 if (!size)
438 goto sigbus;
439 if (user && !access_ok(addr, size))
440 goto sigbus;
441
442 if (!write) {
443 res = unaligned_read(addr, &value, size, sign);
444 if (res)
445 goto fault;
446
447 /* Rd is the same field in any formats */
448 if (!fp)
449 regs->regs[insn.reg3_format.rd] = value;
450 else {
451 if (is_fpu_owner())
452 write_fpr(insn.reg3_format.rd, value);
453 else
454 set_fpr64(¤t->thread.fpu.fpr[insn.reg3_format.rd], 0, value);
455 }
456 } else {
457 /* Rd is the same field in any formats */
458 if (!fp)
459 value = regs->regs[insn.reg3_format.rd];
460 else {
461 if (is_fpu_owner())
462 value = read_fpr(insn.reg3_format.rd);
463 else
464 value = get_fpr64(¤t->thread.fpu.fpr[insn.reg3_format.rd], 0);
465 }
466
467 res = unaligned_write(addr, value, size);
468 if (res)
469 goto fault;
470 }
471
472 #ifdef CONFIG_DEBUG_FS
473 if (user)
474 unaligned_instructions_user++;
475 else
476 unaligned_instructions_kernel++;
477 #endif
478
479 compute_return_era(regs);
480
481 return;
482
483 fault:
484 /* Did we have an exception handler installed? */
485 if (fixup_exception(regs))
486 return;
487
488 die_if_kernel("Unhandled kernel unaligned access", regs);
489 force_sig(SIGSEGV);
490
491 return;
492
493 sigbus:
494 die_if_kernel("Unhandled kernel unaligned access", regs);
495 force_sig(SIGBUS);
496
497 return;
498 }
499
500 #ifdef CONFIG_DEBUG_FS
debugfs_unaligned(void)501 static int __init debugfs_unaligned(void)
502 {
503 debugfs_create_u32("unaligned_instructions_user",
504 S_IRUGO, arch_debugfs_dir, &unaligned_instructions_user);
505 debugfs_create_u32("unaligned_instructions_kernel",
506 S_IRUGO, arch_debugfs_dir, &unaligned_instructions_kernel);
507
508 return 0;
509 }
510 arch_initcall(debugfs_unaligned);
511 #endif
512