~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/mips/loongson64/cop2-ex.c

Version: ~ [ linux-5.16-rc3 ] ~ [ linux-5.15.5 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.82 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.162 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.218 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.256 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.291 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.293 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.18.140 ] ~ [ linux-3.16.85 ] ~ [ linux-3.14.79 ] ~ [ linux-3.12.74 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  * This file is subject to the terms and conditions of the GNU General Public
  3  * License.  See the file "COPYING" in the main directory of this archive
  4  * for more details.
  5  *
  6  * Copyright (C) 2014 Lemote Corporation.
  7  *   written by Huacai Chen <chenhc@lemote.com>
  8  *
  9  * based on arch/mips/cavium-octeon/cpu.c
 10  * Copyright (C) 2009 Wind River Systems,
 11  *   written by Ralf Baechle <ralf@linux-mips.org>
 12  */
 13 #include <linux/init.h>
 14 #include <linux/sched.h>
 15 #include <linux/notifier.h>
 16 #include <linux/ptrace.h>
 17 #include <linux/uaccess.h>
 18 #include <linux/sched/signal.h>
 19 
 20 #include <asm/fpu.h>
 21 #include <asm/cop2.h>
 22 #include <asm/inst.h>
 23 #include <asm/branch.h>
 24 #include <asm/current.h>
 25 #include <asm/mipsregs.h>
 26 #include <asm/unaligned-emul.h>
 27 
 28 static int loongson_cu2_call(struct notifier_block *nfb, unsigned long action,
 29         void *data)
 30 {
 31         unsigned int res, fpu_owned;
 32         unsigned long ra, value, value_next;
 33         union mips_instruction insn;
 34         int fr = !test_thread_flag(TIF_32BIT_FPREGS);
 35         struct pt_regs *regs = (struct pt_regs *)data;
 36         void __user *addr = (void __user *)regs->cp0_badvaddr;
 37         unsigned int __user *pc = (unsigned int __user *)exception_epc(regs);
 38 
 39         ra = regs->regs[31];
 40         __get_user(insn.word, pc);
 41 
 42         switch (action) {
 43         case CU2_EXCEPTION:
 44                 preempt_disable();
 45                 fpu_owned = __is_fpu_owner();
 46                 if (!fr)
 47                         set_c0_status(ST0_CU1 | ST0_CU2);
 48                 else
 49                         set_c0_status(ST0_CU1 | ST0_CU2 | ST0_FR);
 50                 enable_fpu_hazard();
 51                 KSTK_STATUS(current) |= (ST0_CU1 | ST0_CU2);
 52                 if (fr)
 53                         KSTK_STATUS(current) |= ST0_FR;
 54                 else
 55                         KSTK_STATUS(current) &= ~ST0_FR;
 56                 /* If FPU is owned, we needn't init or restore fp */
 57                 if (!fpu_owned) {
 58                         set_thread_flag(TIF_USEDFPU);
 59                         init_fp_ctx(current);
 60                         _restore_fp(current);
 61                 }
 62                 preempt_enable();
 63 
 64                 return NOTIFY_STOP;     /* Don't call default notifier */
 65 
 66         case CU2_LWC2_OP:
 67                 if (insn.loongson3_lswc2_format.ls == 0)
 68                         goto sigbus;
 69 
 70                 if (insn.loongson3_lswc2_format.fr == 0) {      /* gslq */
 71                         if (!access_ok(addr, 16))
 72                                 goto sigbus;
 73 
 74                         LoadDW(addr, value, res);
 75                         if (res)
 76                                 goto fault;
 77 
 78                         LoadDW(addr + 8, value_next, res);
 79                         if (res)
 80                                 goto fault;
 81 
 82                         regs->regs[insn.loongson3_lswc2_format.rt] = value;
 83                         regs->regs[insn.loongson3_lswc2_format.rq] = value_next;
 84                         compute_return_epc(regs);
 85                 } else {                                        /* gslqc1 */
 86                         if (!access_ok(addr, 16))
 87                                 goto sigbus;
 88 
 89                         lose_fpu(1);
 90                         LoadDW(addr, value, res);
 91                         if (res)
 92                                 goto fault;
 93 
 94                         LoadDW(addr + 8, value_next, res);
 95                         if (res)
 96                                 goto fault;
 97 
 98                         set_fpr64(&current->thread.fpu.fpr[insn.loongson3_lswc2_format.rt], 0, value);
 99                         set_fpr64(&current->thread.fpu.fpr[insn.loongson3_lswc2_format.rq], 0, value_next);
100                         compute_return_epc(regs);
101                         own_fpu(1);
102                 }
103                 return NOTIFY_STOP;     /* Don't call default notifier */
104 
105         case CU2_SWC2_OP:
106                 if (insn.loongson3_lswc2_format.ls == 0)
107                         goto sigbus;
108 
109                 if (insn.loongson3_lswc2_format.fr == 0) {      /* gssq */
110                         if (!access_ok(addr, 16))
111                                 goto sigbus;
112 
113                         /* write upper 8 bytes first */
114                         value_next = regs->regs[insn.loongson3_lswc2_format.rq];
115 
116                         StoreDW(addr + 8, value_next, res);
117                         if (res)
118                                 goto fault;
119                         value = regs->regs[insn.loongson3_lswc2_format.rt];
120 
121                         StoreDW(addr, value, res);
122                         if (res)
123                                 goto fault;
124 
125                         compute_return_epc(regs);
126                 } else {                                        /* gssqc1 */
127                         if (!access_ok(addr, 16))
128                                 goto sigbus;
129 
130                         lose_fpu(1);
131                         value_next = get_fpr64(&current->thread.fpu.fpr[insn.loongson3_lswc2_format.rq], 0);
132 
133                         StoreDW(addr + 8, value_next, res);
134                         if (res)
135                                 goto fault;
136 
137                         value = get_fpr64(&current->thread.fpu.fpr[insn.loongson3_lswc2_format.rt], 0);
138 
139                         StoreDW(addr, value, res);
140                         if (res)
141                                 goto fault;
142 
143                         compute_return_epc(regs);
144                         own_fpu(1);
145                 }
146                 return NOTIFY_STOP;     /* Don't call default notifier */
147 
148         case CU2_LDC2_OP:
149                 switch (insn.loongson3_lsdc2_format.opcode1) {
150                 /*
151                  * Loongson-3 overridden ldc2 instructions.
152                  * opcode1              instruction
153                  *   0x1          gslhx: load 2 bytes to GPR
154                  *   0x2          gslwx: load 4 bytes to GPR
155                  *   0x3          gsldx: load 8 bytes to GPR
156                  *   0x6          gslwxc1: load 4 bytes to FPR
157                  *   0x7          gsldxc1: load 8 bytes to FPR
158                  */
159                 case 0x1:
160                         if (!access_ok(addr, 2))
161                                 goto sigbus;
162 
163                         LoadHW(addr, value, res);
164                         if (res)
165                                 goto fault;
166 
167                         compute_return_epc(regs);
168                         regs->regs[insn.loongson3_lsdc2_format.rt] = value;
169                         break;
170                 case 0x2:
171                         if (!access_ok(addr, 4))
172                                 goto sigbus;
173 
174                         LoadW(addr, value, res);
175                         if (res)
176                                 goto fault;
177 
178                         compute_return_epc(regs);
179                         regs->regs[insn.loongson3_lsdc2_format.rt] = value;
180                         break;
181                 case 0x3:
182                         if (!access_ok(addr, 8))
183                                 goto sigbus;
184 
185                         LoadDW(addr, value, res);
186                         if (res)
187                                 goto fault;
188 
189                         compute_return_epc(regs);
190                         regs->regs[insn.loongson3_lsdc2_format.rt] = value;
191                         break;
192                 case 0x6:
193                         die_if_kernel("Unaligned FP access in kernel code", regs);
194                         BUG_ON(!used_math());
195                         if (!access_ok(addr, 4))
196                                 goto sigbus;
197 
198                         lose_fpu(1);
199                         LoadW(addr, value, res);
200                         if (res)
201                                 goto fault;
202 
203                         set_fpr64(&current->thread.fpu.fpr[insn.loongson3_lsdc2_format.rt], 0, value);
204                         compute_return_epc(regs);
205                         own_fpu(1);
206 
207                         break;
208                 case 0x7:
209                         die_if_kernel("Unaligned FP access in kernel code", regs);
210                         BUG_ON(!used_math());
211                         if (!access_ok(addr, 8))
212                                 goto sigbus;
213 
214                         lose_fpu(1);
215                         LoadDW(addr, value, res);
216                         if (res)
217                                 goto fault;
218 
219                         set_fpr64(&current->thread.fpu.fpr[insn.loongson3_lsdc2_format.rt], 0, value);
220                         compute_return_epc(regs);
221                         own_fpu(1);
222                         break;
223 
224                 }
225                 return NOTIFY_STOP;     /* Don't call default notifier */
226 
227         case CU2_SDC2_OP:
228                 switch (insn.loongson3_lsdc2_format.opcode1) {
229                 /*
230                  * Loongson-3 overridden sdc2 instructions.
231                  * opcode1              instruction
232                  *   0x1          gsshx: store 2 bytes from GPR
233                  *   0x2          gsswx: store 4 bytes from GPR
234                  *   0x3          gssdx: store 8 bytes from GPR
235                  *   0x6          gsswxc1: store 4 bytes from FPR
236                  *   0x7          gssdxc1: store 8 bytes from FPR
237                  */
238                 case 0x1:
239                         if (!access_ok(addr, 2))
240                                 goto sigbus;
241 
242                         compute_return_epc(regs);
243                         value = regs->regs[insn.loongson3_lsdc2_format.rt];
244 
245                         StoreHW(addr, value, res);
246                         if (res)
247                                 goto fault;
248 
249                         break;
250                 case 0x2:
251                         if (!access_ok(addr, 4))
252                                 goto sigbus;
253 
254                         compute_return_epc(regs);
255                         value = regs->regs[insn.loongson3_lsdc2_format.rt];
256 
257                         StoreW(addr, value, res);
258                         if (res)
259                                 goto fault;
260 
261                         break;
262                 case 0x3:
263                         if (!access_ok(addr, 8))
264                                 goto sigbus;
265 
266                         compute_return_epc(regs);
267                         value = regs->regs[insn.loongson3_lsdc2_format.rt];
268 
269                         StoreDW(addr, value, res);
270                         if (res)
271                                 goto fault;
272 
273                         break;
274 
275                 case 0x6:
276                         die_if_kernel("Unaligned FP access in kernel code", regs);
277                         BUG_ON(!used_math());
278 
279                         if (!access_ok(addr, 4))
280                                 goto sigbus;
281 
282                         lose_fpu(1);
283                         value = get_fpr64(&current->thread.fpu.fpr[insn.loongson3_lsdc2_format.rt], 0);
284 
285                         StoreW(addr, value, res);
286                         if (res)
287                                 goto fault;
288 
289                         compute_return_epc(regs);
290                         own_fpu(1);
291 
292                         break;
293                 case 0x7:
294                         die_if_kernel("Unaligned FP access in kernel code", regs);
295                         BUG_ON(!used_math());
296 
297                         if (!access_ok(addr, 8))
298                                 goto sigbus;
299 
300                         lose_fpu(1);
301                         value = get_fpr64(&current->thread.fpu.fpr[insn.loongson3_lsdc2_format.rt], 0);
302 
303                         StoreDW(addr, value, res);
304                         if (res)
305                                 goto fault;
306 
307                         compute_return_epc(regs);
308                         own_fpu(1);
309 
310                         break;
311                 }
312                 return NOTIFY_STOP;     /* Don't call default notifier */
313         }
314 
315         return NOTIFY_OK;               /* Let default notifier send signals */
316 
317 fault:
318         /* roll back jump/branch */
319         regs->regs[31] = ra;
320         regs->cp0_epc = (unsigned long)pc;
321         /* Did we have an exception handler installed? */
322         if (fixup_exception(regs))
323                 return NOTIFY_STOP;     /* Don't call default notifier */
324 
325         die_if_kernel("Unhandled kernel unaligned access", regs);
326         force_sig(SIGSEGV);
327 
328         return NOTIFY_STOP;     /* Don't call default notifier */
329 
330 sigbus:
331         die_if_kernel("Unhandled kernel unaligned access", regs);
332         force_sig(SIGBUS);
333 
334         return NOTIFY_STOP;     /* Don't call default notifier */
335 }
336 
337 static int __init loongson_cu2_setup(void)
338 {
339         return cu2_notifier(loongson_cu2_call, 0);
340 }
341 early_initcall(loongson_cu2_setup);
342 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp