1 // SPDX-License-Identifier: GPL-2.0
3 * Handle unaligned accesses by emulation.
5 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
8 * Copyright (C) 1996, 1998, 1999, 2002 by Ralf Baechle
9 * Copyright (C) 1999 Silicon Graphics, Inc.
10 * Copyright (C) 2014 Imagination Technologies Ltd.
13 #include <linux/sched.h>
14 #include <linux/signal.h>
15 #include <linux/debugfs.h>
16 #include <linux/perf_event.h>
19 #include <asm/branch.h>
23 #include "access-helper.h"
25 #ifdef CONFIG_DEBUG_FS
26 static u32 unaligned_instructions_user;
27 static u32 unaligned_instructions_kernel;
30 static inline unsigned long read_fpr(unsigned int idx)
32 #define READ_FPR(idx, __value) \
33 __asm__ __volatile__("movfr2gr.d %0, $f"#idx"\n\t" : "=r"(__value));
35 unsigned long __value;
69 READ_FPR(10, __value);
72 READ_FPR(11, __value);
75 READ_FPR(12, __value);
78 READ_FPR(13, __value);
81 READ_FPR(14, __value);
84 READ_FPR(15, __value);
87 READ_FPR(16, __value);
90 READ_FPR(17, __value);
93 READ_FPR(18, __value);
96 READ_FPR(19, __value);
99 READ_FPR(20, __value);
102 READ_FPR(21, __value);
105 READ_FPR(22, __value);
108 READ_FPR(23, __value);
111 READ_FPR(24, __value);
114 READ_FPR(25, __value);
117 READ_FPR(26, __value);
120 READ_FPR(27, __value);
123 READ_FPR(28, __value);
126 READ_FPR(29, __value);
129 READ_FPR(30, __value);
132 READ_FPR(31, __value);
135 panic("unexpected idx '%d'", idx);
141 static inline void write_fpr(unsigned int idx, unsigned long value)
143 #define WRITE_FPR(idx, value) \
144 __asm__ __volatile__("movgr2fr.d $f"#idx", %0\n\t" :: "r"(value));
178 WRITE_FPR(10, value);
181 WRITE_FPR(11, value);
184 WRITE_FPR(12, value);
187 WRITE_FPR(13, value);
190 WRITE_FPR(14, value);
193 WRITE_FPR(15, value);
196 WRITE_FPR(16, value);
199 WRITE_FPR(17, value);
202 WRITE_FPR(18, value);
205 WRITE_FPR(19, value);
208 WRITE_FPR(20, value);
211 WRITE_FPR(21, value);
214 WRITE_FPR(22, value);
217 WRITE_FPR(23, value);
220 WRITE_FPR(24, value);
223 WRITE_FPR(25, value);
226 WRITE_FPR(26, value);
229 WRITE_FPR(27, value);
232 WRITE_FPR(28, value);
235 WRITE_FPR(29, value);
238 WRITE_FPR(30, value);
241 WRITE_FPR(31, value);
244 panic("unexpected idx '%d'", idx);
249 void emulate_load_store_insn(struct pt_regs *regs, void __user *addr, unsigned int *pc)
253 bool user = user_mode(regs);
254 unsigned int res, size = 0;
255 unsigned long value = 0;
256 union loongarch_instruction insn;
258 perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0);
260 __get_inst(&insn.word, pc, user);
262 switch (insn.reg2i12_format.opcode) {
329 switch (insn.reg2i14_format.opcode) {
352 switch (insn.reg3_format.opcode) {
421 if (user && !access_ok(addr, size))
425 res = unaligned_read(addr, &value, size, sign);
429 /* Rd is the same field in any formats */
431 regs->regs[insn.reg3_format.rd] = value;
434 write_fpr(insn.reg3_format.rd, value);
436 set_fpr64(¤t->thread.fpu.fpr[insn.reg3_format.rd], 0, value);
439 /* Rd is the same field in any formats */
441 value = regs->regs[insn.reg3_format.rd];
444 value = read_fpr(insn.reg3_format.rd);
446 value = get_fpr64(¤t->thread.fpu.fpr[insn.reg3_format.rd], 0);
449 res = unaligned_write(addr, value, size);
454 #ifdef CONFIG_DEBUG_FS
456 unaligned_instructions_user++;
458 unaligned_instructions_kernel++;
461 compute_return_era(regs);
466 /* Did we have an exception handler installed? */
467 if (fixup_exception(regs))
470 die_if_kernel("Unhandled kernel unaligned access", regs);
476 die_if_kernel("Unhandled kernel unaligned access", regs);
482 #ifdef CONFIG_DEBUG_FS
483 static int __init debugfs_unaligned(void)
487 d = debugfs_create_dir("loongarch", NULL);
489 debugfs_create_u32("unaligned_instructions_user",
490 S_IRUGO, d, &unaligned_instructions_user);
491 debugfs_create_u32("unaligned_instructions_kernel",
492 S_IRUGO, d, &unaligned_instructions_kernel);
496 arch_initcall(debugfs_unaligned);