xref: /linux/arch/loongarch/kernel/unaligned.c (revision 9551a26f17d9445eed497bd7c639d48dfc3c0af4)
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(&current->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(&current->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