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