Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/mips/loongson64/cop2-ex.c
26424 views
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 <[email protected]>
8
*
9
* based on arch/mips/cavium-octeon/cpu.c
10
* Copyright (C) 2009 Wind River Systems,
11
* written by Ralf Baechle <[email protected]>
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
343