File Index
Files and directories
Sign in
FILE

unaligned.c

File preview with syntax highlighting.

linux-6.19.14/arch/loongarch/kernel/unaligned.c C
  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
 26static u32 unaligned_instructions_user;
 27static u32 unaligned_instructions_kernel;
 28#endif
 29
 30static 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
150static 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
267void 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
483fault:
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
493sigbus:
494	die_if_kernel("Unhandled kernel unaligned access", regs);
495	force_sig(SIGBUS);
496
497	return;
498}
499
500#ifdef CONFIG_DEBUG_FS
501static 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}
510arch_initcall(debugfs_unaligned);
511#endif
~/ Read-only file preview.