File: /usr/src/linux/arch/s390x/kernel/mathemu.c
1 /*
2 * arch/s390/kernel/mathemu.c
3 *
4 * S390 version
5 * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
6 * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
7 *
8 * 'mathemu.c' handles IEEE instructions on a S390 processor
9 * that does not have the IEEE fpu
10 */
11
12 #include <linux/config.h>
13 #include <linux/kernel.h>
14 #include <linux/errno.h>
15 #include <linux/ptrace.h>
16
17 #include <asm/uaccess.h>
18 #include <asm/mathemu.h>
19
20 #ifdef CONFIG_SYSCTL
21 int sysctl_ieee_emulation_warnings=1;
22 #endif
23
24 static void display_emulation_not_implemented(char *instr)
25 {
26 struct pt_regs *regs;
27 __u16 *location;
28
29 #if CONFIG_SYSCTL
30 if(sysctl_ieee_emulation_warnings)
31 #endif
32 {
33 regs=current->thread.regs;
34 location = (__u16 *)(regs->psw.addr-S390_lowcore.pgm_ilc);
35 printk("%s ieee fpu instruction not emulated process name: %s pid: %d \n",
36 instr,
37 current->comm, current->pid);
38 printk("%s's PSW: %08lx %08lx\n",instr,
39 (unsigned long) regs->psw.mask,
40 (unsigned long) location);
41 }
42 }
43
44
45 static void set_CC_df(__u64 val1,__u64 val2) {
46 int rc;
47 rc = __cmpdf2(val1,val2);
48 current->thread.regs->psw.mask &= 0xFFFFCFFFFFFFFFFFL;
49 switch (rc) {
50 case -1:
51 current->thread.regs->psw.mask |= 0x0000100000000000L;
52 break;
53 case 1:
54 current->thread.regs->psw.mask |= 0x0000200000000000L;
55 break;
56 }
57 }
58
59 static void set_CC_sf(__u32 val1,__u32 val2) {
60 int rc;
61 rc = __cmpsf2(val1,val2);
62 current->thread.regs->psw.mask &= 0xFFFFCFFFFFFFFFFF;
63 switch (rc) {
64 case -1:
65 current->thread.regs->psw.mask |= 0x0000100000000000L;
66 break;
67 case 1:
68 current->thread.regs->psw.mask |= 0x0000200000000000L;
69 break;
70 }
71 }
72
73
74 static void emu_adb (int rx, __u64 val) {
75 current->thread.fp_regs.fprs[rx].d = __adddf3(current->thread.fp_regs.fprs[rx].d,val);
76 set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL);
77 }
78
79 static void emu_adbr (int rx, int ry) {
80 current->thread.fp_regs.fprs[rx].d = __adddf3(current->thread.fp_regs.fprs[rx].d,
81 current->thread.fp_regs.fprs[ry].d);
82 set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL);
83 }
84
85 static void emu_aeb (int rx, __u32 val) {
86 current->thread.fp_regs.fprs[rx].f = __addsf3(current->thread.fp_regs.fprs[rx].f,val);
87 set_CC_sf(current->thread.fp_regs.fprs[rx].f,0);
88 }
89
90 static void emu_aebr (int rx, int ry) {
91 current->thread.fp_regs.fprs[rx].f = __addsf3(current->thread.fp_regs.fprs[rx].f,
92 current->thread.fp_regs.fprs[ry].f);
93 set_CC_sf(current->thread.fp_regs.fprs[rx].f,0);
94 }
95
96 static void emu_axbr (int rx, int ry) {
97 display_emulation_not_implemented("axbr");
98 }
99
100 static void emu_cdb (int rx, __u64 val) {
101 set_CC_df(current->thread.fp_regs.fprs[rx].d,val);
102 }
103
104 static void emu_cdbr (int rx, int ry) {
105 set_CC_df(current->thread.fp_regs.fprs[rx].d,current->thread.fp_regs.fprs[ry].d);
106 }
107
108 static void emu_cdfbr (int rx, int ry) {
109 current->thread.fp_regs.fprs[rx].d =
110 __floatsidf(current->thread.regs->gprs[ry]);
111 }
112
113 static void emu_ceb (int rx, __u32 val) {
114 set_CC_sf(current->thread.fp_regs.fprs[rx].f,val);
115 }
116
117 static void emu_cebr (int rx, int ry) {
118 set_CC_sf(current->thread.fp_regs.fprs[rx].f,current->thread.fp_regs.fprs[ry].f);
119 }
120
121 static void emu_cefbr (int rx, int ry) {
122 current->thread.fp_regs.fprs[rx].f =
123 __floatsisf(current->thread.regs->gprs[ry]);
124 }
125
126 static void emu_cfdbr (int rx, int ry, int mask) {
127 current->thread.regs->gprs[rx] =
128 __fixdfsi(current->thread.fp_regs.fprs[ry].d);
129 }
130
131 static void emu_cfebr (int rx, int ry, int mask) {
132 current->thread.regs->gprs[rx] =
133 __fixsfsi(current->thread.fp_regs.fprs[ry].f);
134 }
135
136 static void emu_cfxbr (int rx, int ry, int mask) {
137 display_emulation_not_implemented("cfxbr");
138 }
139
140 static void emu_cxbr (int rx, int ry) {
141 display_emulation_not_implemented("cxbr");
142 }
143
144 static void emu_cxfbr (int rx, int ry) {
145 display_emulation_not_implemented("cxfbr");
146 }
147
148 static void emu_ddb (int rx, __u64 val) {
149 current->thread.fp_regs.fprs[rx].d = __divdf3(current->thread.fp_regs.fprs[rx].d,val);
150 set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL);
151 }
152
153 static void emu_ddbr (int rx, int ry) {
154 current->thread.fp_regs.fprs[rx].d = __divdf3(current->thread.fp_regs.fprs[rx].d,
155 current->thread.fp_regs.fprs[ry].d);
156 set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL);
157 }
158
159 static void emu_deb (int rx, __u32 val) {
160 current->thread.fp_regs.fprs[rx].f = __divsf3(current->thread.fp_regs.fprs[rx].f,val);
161 set_CC_sf(current->thread.fp_regs.fprs[rx].f,0);
162 }
163
164 static void emu_debr (int rx, int ry) {
165 current->thread.fp_regs.fprs[rx].f = __divsf3(current->thread.fp_regs.fprs[rx].f,
166 current->thread.fp_regs.fprs[ry].f);
167 set_CC_sf(current->thread.fp_regs.fprs[rx].f,0);
168 }
169
170 static void emu_didbr (int rx, int ry, int mask) {
171 display_emulation_not_implemented("didbr");
172 }
173
174 static void emu_diebr (int rx, int ry, int mask) {
175 display_emulation_not_implemented("diebr");
176 }
177
178 static void emu_dxbr (int rx, int ry) {
179 display_emulation_not_implemented("dxbr");
180 }
181
182 static void emu_efpc (int rx, int ry) {
183 display_emulation_not_implemented("efpc");
184 }
185
186 static void emu_fidbr (int rx, int ry, int mask) {
187 display_emulation_not_implemented("fidbr");
188 }
189
190 static void emu_fiebr (int rx, int ry, int mask) {
191 display_emulation_not_implemented("fiebr");
192 }
193
194 static void emu_fixbr (int rx, int ry, int mask) {
195 display_emulation_not_implemented("fixbr");
196 }
197
198 static void emu_kdb (int rx, __u64 val) {
199 display_emulation_not_implemented("kdb");
200 }
201
202 static void emu_kdbr (int rx, int ry) {
203 display_emulation_not_implemented("kdbr");
204 }
205
206 static void emu_keb (int rx, __u32 val) {
207 display_emulation_not_implemented("keb");
208 }
209
210 static void emu_kebr (int rx, int ry) {
211 display_emulation_not_implemented("kebr");
212 }
213
214 static void emu_kxbr (int rx, int ry) {
215 display_emulation_not_implemented("kxbr");
216 }
217
218 static void emu_lcdbr (int rx, int ry) {
219 current->thread.fp_regs.fprs[rx].d =
220 __negdf2(current->thread.fp_regs.fprs[ry].d);
221 set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL);
222 }
223
224 static void emu_lcebr (int rx, int ry) {
225 current->thread.fp_regs.fprs[rx].f =
226 __negsf2(current->thread.fp_regs.fprs[ry].f);
227 set_CC_sf(current->thread.fp_regs.fprs[rx].f,0);
228 }
229
230 static void emu_lcxbr (int rx, int ry) {
231 display_emulation_not_implemented("lcxbr");
232 }
233
234 static void emu_ldeb (int rx, __u32 val) {
235 current->thread.fp_regs.fprs[rx].d = __extendsfdf2(val);
236 }
237
238 static void emu_ldebr (int rx, int ry) {
239 current->thread.fp_regs.fprs[rx].d =
240 __extendsfdf2(current->thread.fp_regs.fprs[ry].f);
241 }
242
243 static void emu_ldxbr (int rx, int ry) {
244 display_emulation_not_implemented("ldxbr");
245 }
246
247 static void emu_ledbr (int rx, int ry) {
248 current->thread.fp_regs.fprs[rx].f = __truncdfsf2(current->thread.fp_regs.fprs[ry].d);
249 set_CC_sf(current->thread.fp_regs.fprs[rx].f,0);
250 }
251
252 static void emu_lexbr (int rx, int ry) {
253 display_emulation_not_implemented("lexbr");
254 }
255
256 static void emu_lndbr (int rx, int ry) {
257 display_emulation_not_implemented("lndbr");
258 }
259
260 static void emu_lnebr (int rx, int ry) {
261 display_emulation_not_implemented("lnebr");
262 }
263
264 static void emu_lnxbr (int rx, int ry) {
265 display_emulation_not_implemented("lnxbr");
266 }
267
268 static void emu_lpdbr (int rx, int ry) {
269 current->thread.fp_regs.fprs[rx].d = __absdf2(current->thread.fp_regs.fprs[ry].d);
270 set_CC_df(current->thread.fp_regs.fprs[rx].d,0);
271 }
272
273 static void emu_lpebr (int rx, int ry) {
274 current->thread.fp_regs.fprs[rx].f = __abssf2(current->thread.fp_regs.fprs[ry].f);
275 set_CC_sf(current->thread.fp_regs.fprs[rx].f,0);
276 }
277
278 static void emu_lpxbr (int rx, int ry) {
279 display_emulation_not_implemented("lpxbr");
280 }
281
282 static void emu_ltdbr (int rx, int ry) {
283 current->thread.fp_regs.fprs[rx].d = current->thread.fp_regs.fprs[ry].d;
284 set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL);
285 }
286
287 static void emu_ltebr (int rx, int ry) {
288 current->thread.fp_regs.fprs[rx].f = current->thread.fp_regs.fprs[ry].f;
289 set_CC_sf(current->thread.fp_regs.fprs[rx].f,0);
290 }
291
292 static void emu_ltxbr (int rx, int ry) {
293 display_emulation_not_implemented("ltxbr");
294 }
295
296 static void emu_lxdb (int rx, __u64 val) {
297 display_emulation_not_implemented("lxdb");
298 }
299
300 static void emu_lxdbr (int rx, int ry) {
301 display_emulation_not_implemented("lxdbr");
302 }
303
304 static void emu_lxeb (int rx, __u32 val) {
305 display_emulation_not_implemented("lxeb");
306 }
307
308 static void emu_lxebr (int rx, int ry) {
309 display_emulation_not_implemented("lxebr");
310 }
311
312 static void emu_madb (int rx, __u64 val, int mask) {
313 display_emulation_not_implemented("madb");
314 }
315
316 static void emu_madbr (int rx, int ry, int mask) {
317 display_emulation_not_implemented("madbr");
318 }
319
320 static void emu_maeb (int rx, __u32 val, int mask) {
321 display_emulation_not_implemented("maeb");
322 }
323
324 static void emu_maebr (int rx, int ry, int mask) {
325 display_emulation_not_implemented("maebr");
326 }
327
328 static void emu_mdb (int rx, __u64 val) {
329 current->thread.fp_regs.fprs[rx].d = __muldf3(current->thread.fp_regs.fprs[rx].d,val);
330 set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL);
331 }
332
333 static void emu_mdbr (int rx, int ry) {
334 current->thread.fp_regs.fprs[rx].d = __muldf3(current->thread.fp_regs.fprs[rx].d,
335 current->thread.fp_regs.fprs[ry].d);
336 set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL);
337 }
338
339 static void emu_mdeb (int rx, __u32 val) {
340 display_emulation_not_implemented("mdeb");
341 }
342
343 static void emu_mdebr (int rx, int ry) {
344 display_emulation_not_implemented("mdebr");
345 }
346
347 static void emu_meeb (int rx, __u32 val) {
348 current->thread.fp_regs.fprs[rx].f = __mulsf3(current->thread.fp_regs.fprs[rx].f,
349 val);
350 set_CC_sf(current->thread.fp_regs.fprs[rx].f,0);
351 }
352
353 static void emu_meebr (int rx, int ry) {
354 current->thread.fp_regs.fprs[rx].f = __mulsf3(current->thread.fp_regs.fprs[rx].f,
355 current->thread.fp_regs.fprs[ry].f);
356 set_CC_sf(current->thread.fp_regs.fprs[rx].f,0);
357 }
358
359 static void emu_msdb (int rx, __u64 val, int mask) {
360 display_emulation_not_implemented("msdb");
361 }
362
363 static void emu_msdbr (int rx, int ry, int mask) {
364 display_emulation_not_implemented("msdbr");
365 }
366
367 static void emu_mseb (int rx, __u32 val, int mask) {
368 display_emulation_not_implemented("mseb");
369 }
370
371 static void emu_msebr (int rx, int ry, int mask) {
372 display_emulation_not_implemented("msebr");
373 }
374
375 static void emu_mxbr (int rx, int ry) {
376 display_emulation_not_implemented("mxbr");
377 }
378
379 static void emu_mxdb (int rx, __u64 val) {
380 display_emulation_not_implemented("mxdb");
381 }
382
383 static void emu_mxdbr (int rx, int ry) {
384 display_emulation_not_implemented("mxdbr");
385 }
386
387 static void emu_sdb (int rx, __u64 val) {
388 current->thread.fp_regs.fprs[rx].d = __subdf3(current->thread.fp_regs.fprs[rx].d,
389 val);
390 set_CC_sf(current->thread.fp_regs.fprs[rx].d,0ULL);
391 }
392
393 static void emu_sdbr (int rx, int ry) {
394 current->thread.fp_regs.fprs[rx].d = __subdf3(current->thread.fp_regs.fprs[rx].d,
395 current->thread.fp_regs.fprs[ry].d);
396 set_CC_sf(current->thread.fp_regs.fprs[rx].d,0ULL);
397 }
398
399 static void emu_seb (int rx, __u32 val) {
400 current->thread.fp_regs.fprs[rx].f = __subsf3(current->thread.fp_regs.fprs[rx].f,
401 val);
402 set_CC_sf(current->thread.fp_regs.fprs[rx].f,0);
403 }
404
405 static void emu_sebr (int rx, int ry) {
406 current->thread.fp_regs.fprs[rx].f = __subsf3(current->thread.fp_regs.fprs[rx].f,
407 current->thread.fp_regs.fprs[ry].f);
408 set_CC_sf(current->thread.fp_regs.fprs[rx].f,0);
409 }
410
411 static void emu_sfpc (int rx, int ry) {
412 display_emulation_not_implemented("sfpc");
413 }
414
415 static void emu_sqdb (int rx, __u64 val) {
416 display_emulation_not_implemented("sqdb");
417 }
418
419 static void emu_sqdbr (int rx, int ry) {
420 display_emulation_not_implemented("sqdbr");
421 }
422
423 static void emu_sqeb (int rx, __u32 val) {
424 display_emulation_not_implemented("sqeb");
425 }
426
427 static void emu_sqebr (int rx, int ry) {
428 display_emulation_not_implemented("sqebr");
429 }
430
431 static void emu_sqxbr (int rx, int ry) {
432 display_emulation_not_implemented("sqxbr");
433 }
434
435 static void emu_sxbr (int rx, int ry) {
436 display_emulation_not_implemented("sxbr");
437 }
438
439 static void emu_tcdb (int rx, __u64 val) {
440 display_emulation_not_implemented("tcdb");
441 }
442
443 static void emu_tceb (int rx, __u32 val) {
444 display_emulation_not_implemented("tceb");
445 }
446
447 static void emu_tcxb (int rx, __u64 val) {
448 display_emulation_not_implemented("tcxb");
449 }
450
451
452 static inline void emu_load_regd(int reg) {
453 if ((reg&9) == 0) { /* test if reg in {0,2,4,6} */
454 __asm__ __volatile ( /* load reg from fp_regs.fprs[reg] */
455 " bras 1,0f\n"
456 " ld 0,0(%1)\n"
457 "0: ex %0,0(1)"
458 : /* no output */
459 : "a" (reg<<4), "a" (¤t->thread.fp_regs.fprs[reg].d)
460 : "1" );
461 }
462 }
463
464 static inline void emu_load_rege(int reg) {
465 if ((reg&9) == 0) { /* test if reg in {0,2,4,6} */
466 __asm__ __volatile ( /* load reg from fp_regs.fprs[reg] */
467 " bras 1,0f\n"
468 " le 0,0(%1)\n"
469 "0: ex %0,0(1)"
470 : /* no output */
471 : "a" (reg<<4), "a" (¤t->thread.fp_regs.fprs[reg].f)
472 : "1" );
473 }
474 }
475
476 static inline void emu_store_regd(int reg) {
477 if ((reg&9) == 0) { /* test if reg in {0,2,4,6} */
478 __asm__ __volatile ( /* store reg to fp_regs.fprs[reg] */
479 " bras 1,0f\n"
480 " std 0,0(%1)\n"
481 "0: ex %0,0(1)"
482 : /* no output */
483 : "a" (reg<<4), "a" (¤t->thread.fp_regs.fprs[reg].d)
484 : "1" );
485 }
486 }
487
488
489 static inline void emu_store_rege(int reg) {
490 if ((reg&9) == 0) { /* test if reg in {0,2,4,6} */
491 __asm__ __volatile ( /* store reg to fp_regs.fprs[reg] */
492 " bras 1,0f\n"
493 " ste 0,0(%1)\n"
494 "0: ex %0,0(1)"
495 : /* no output */
496 : "a" (reg<<4), "a" (¤t->thread.fp_regs.fprs[reg].f)
497 : "1" );
498 }
499 }
500
501 int math_emu_b3(__u8 *opcode, struct pt_regs * regs) {
502 static const __u8 format_table[] = {
503 2, 2, 2, 2, 9, 1, 2, 1, 2, 2, 2, 2, 9, 2, 4, 4,
504 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3, 3,
505 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
506 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
507 1, 1, 1, 1,10, 1, 1, 3, 1, 1, 1, 1, 1, 1, 0, 0,
508 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 3, 0, 0, 0, 3,
509 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
510 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
511 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0,
512 0, 0, 0, 0, 5, 6, 6, 0, 7, 8, 8, 0, 0, 0, 0, 0,
513 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
514 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
515 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
516 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
517 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
518 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
519 };
520 static const void *jump_table[]= {
521 emu_lpebr, emu_lnebr, emu_ltebr, emu_lcebr,
522 emu_ldebr, emu_lxdbr, emu_lxebr, emu_mxdbr,
523 emu_kebr, emu_cebr, emu_aebr, emu_sebr,
524 emu_mdebr, emu_debr, emu_maebr, emu_msebr,
525 emu_lpdbr, emu_lndbr, emu_ltdbr, emu_lcdbr,
526 emu_sqebr, emu_sqdbr, emu_sqxbr, emu_meebr,
527 emu_kdbr, emu_cdbr, emu_adbr, emu_sdbr,
528 emu_mdbr, emu_ddbr, emu_madbr, emu_msdbr,
529 NULL, NULL, NULL, NULL,
530 NULL, NULL, NULL, NULL,
531 NULL, NULL, NULL, NULL,
532 NULL, NULL, NULL, NULL,
533 NULL, NULL, NULL, NULL,
534 NULL, NULL, NULL, NULL,
535 NULL, NULL, NULL, NULL,
536 NULL, NULL, NULL, NULL,
537 emu_lpxbr, emu_lnxbr, emu_ltxbr, emu_lcxbr,
538 emu_ledbr, emu_ldxbr, emu_lexbr, emu_fixbr,
539 emu_kxbr, emu_cxbr, emu_axbr, emu_sxbr,
540 emu_mxbr, emu_dxbr, NULL, NULL,
541 NULL, NULL, NULL, emu_diebr,
542 NULL, NULL, NULL, emu_fiebr,
543 NULL, NULL, NULL, emu_didbr,
544 NULL, NULL, NULL, emu_fidbr,
545 NULL, NULL, NULL, NULL,
546 NULL, NULL, NULL, NULL,
547 NULL, NULL, NULL, NULL,
548 NULL, NULL, NULL, NULL,
549 NULL, NULL, NULL, NULL,
550 NULL, NULL, NULL, NULL,
551 NULL, NULL, NULL, NULL,
552 NULL, NULL, NULL, NULL,
553 NULL, NULL, NULL, NULL,
554 emu_sfpc, NULL, NULL, NULL,
555 NULL, NULL, NULL, NULL,
556 emu_efpc, NULL, NULL, NULL,
557 NULL, NULL, NULL, NULL,
558 emu_cefbr, emu_cdfbr, emu_cxfbr, NULL,
559 emu_cfebr, emu_cfdbr, emu_cfxbr
560 };
561
562 switch (format_table[opcode[1]]) {
563 case 1: /* RRE format, double operation */
564 emu_store_regd((opcode[3]>>4)&15);
565 emu_store_regd(opcode[3]&15);
566 /* call the emulation function */
567 ((void (*)(int, int))jump_table[opcode[1]])
568 (opcode[3]>>4,opcode[3]&15);
569 emu_load_regd((opcode[3]>>4)&15);
570 emu_load_regd(opcode[3]&15);
571 return 0;
572 case 2: /* RRE format, float operation */
573 emu_store_rege((opcode[3]>>4)&15);
574 emu_store_rege(opcode[3]&15);
575 /* call the emulation function */
576 ((void (*)(int, int))jump_table[opcode[1]])
577 (opcode[3]>>4,opcode[3]&15);
578 emu_load_rege((opcode[3]>>4)&15);
579 emu_load_rege(opcode[3]&15);
580 return 0;
581 case 3: /* RRF format, double operation */
582 emu_store_regd((opcode[3]>>4)&15);
583 emu_store_regd(opcode[3]&15);
584 /* call the emulation function */
585 ((void (*)(int, int, int))jump_table[opcode[1]])
586 (opcode[3]>>4,opcode[3]&15,opcode[2]>>4);
587 emu_load_regd((opcode[3]>>4)&15);
588 emu_load_regd(opcode[3]&15);
589 return 0;
590 case 4: /* RRF format, float operation */
591 emu_store_rege((opcode[3]>>4)&15);
592 emu_store_rege(opcode[3]&15);
593 /* call the emulation function */
594 ((void (*)(int, int, int))jump_table[opcode[1]])
595 (opcode[3]>>4,opcode[3]&15,opcode[2]>>4);
596 emu_load_rege((opcode[3]>>4)&15);
597 emu_load_rege(opcode[3]&15);
598 return 0;
599 case 5: /* RRE format, cefbr instruction */
600 emu_store_rege((opcode[3]>>4)&15);
601 /* call the emulation function */
602 ((void (*)(int, int))jump_table[opcode[1]])
603 (opcode[3]>>4,opcode[3]&15);
604 emu_load_rege((opcode[3]>>4)&15);
605 return 0;
606 case 6: /* RRE format, cdfbr & cxfbr instruction */
607 emu_store_regd((opcode[3]>>4)&15);
608 /* call the emulation function */
609 ((void (*)(int, int))jump_table[opcode[1]])
610 (opcode[3]>>4,opcode[3]&15);
611 emu_load_regd((opcode[3]>>4)&15);
612 return 0;
613 /* FIXME !! */
614 return 0;
615 case 7: /* RRF format, cfebr instruction */
616 emu_store_rege(opcode[3]&15);
617 /* call the emulation function */
618 ((void (*)(int, int, int))jump_table[opcode[1]])
619 (opcode[3]>>4,opcode[3]&15,opcode[2]>>4);
620 return 0;
621 case 8: /* RRF format, cfdbr & cfxbr instruction */
622 emu_store_regd(opcode[3]&15);
623 /* call the emulation function */
624 ((void (*)(int, int, int))jump_table[opcode[1]])
625 (opcode[3]>>4,opcode[3]&15,opcode[2]>>4);
626 return 0;
627 case 9: /* RRE format, ldebr & mdebr instruction */
628 /* float store but double load */
629 emu_store_rege((opcode[3]>>4)&15);
630 emu_store_rege(opcode[3]&15);
631 /* call the emulation function */
632 ((void (*)(int, int))jump_table[opcode[1]])
633 (opcode[3]>>4,opcode[3]&15);
634 emu_load_regd((opcode[3]>>4)&15);
635 return 0;
636 case 10: /* RRE format, ledbr instruction */
637 /* double store but float load */
638 emu_store_regd((opcode[3]>>4)&15);
639 emu_store_regd(opcode[3]&15);
640 /* call the emulation function */
641 ((void (*)(int, int))jump_table[opcode[1]])
642 (opcode[3]>>4,opcode[3]&15);
643 emu_load_rege((opcode[3]>>4)&15);
644 return 0;
645 default:
646 return 1;
647 }
648 }
649
650 static void* calc_addr(struct pt_regs *regs,int rx,int rb,int disp)
651 {
652 rx &= 0xf;
653 rb &= 0xf;
654 disp &= 0xfff;
655 return (void*) ((rx != 0 ? regs->gprs[rx] : 0) + /* index */
656 (rb != 0 ? regs->gprs[rb] : 0) + /* base */
657 disp);
658 }
659
660 int math_emu_ed(__u8 *opcode, struct pt_regs * regs) {
661 static const __u8 format_table[] = {
662 0, 0, 0, 0, 5, 1, 2, 1, 2, 2, 2, 2, 5, 2, 4, 4,
663 2, 1, 1, 0, 2, 1, 0, 2, 1, 1, 1, 1, 1, 1, 3, 3,
664 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
665 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
666 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
667 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
668 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
669 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
670 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
671 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
672 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
673 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
674 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
675 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
676 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
677 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
678 };
679 static const void *jump_table[]= {
680 NULL, NULL, NULL, NULL,
681 emu_ldeb, emu_lxdb, emu_lxeb, emu_mxdb,
682 emu_keb, emu_ceb, emu_aeb, emu_seb,
683 emu_mdeb, emu_deb, emu_maeb, emu_mseb,
684 emu_tceb, emu_tcdb, emu_tcxb, NULL,
685 emu_sqeb, emu_sqdb, NULL, emu_meeb,
686 emu_kdb, emu_cdb, emu_adb, emu_sdb,
687 emu_mdb, emu_ddb, emu_madb, emu_msdb
688 };
689
690 switch (format_table[opcode[5]]) {
691 case 1: /* RXE format, __u64 constant */ {
692 __u64 *dxb, temp;
693 __u32 opc;
694
695 emu_store_regd((opcode[1]>>4)&15);
696 opc = *((__u32 *) opcode);
697 dxb = (__u64 *) calc_addr(regs,opc>>16,opc>>12,opc);
698 /* FIXME: how to react if copy_from_user fails ? */
699 copy_from_user(&temp, dxb, 8);
700 /* call the emulation function */
701 ((void (*)(int, __u64))jump_table[opcode[5]])
702 (opcode[1]>>4,temp);
703 emu_load_regd((opcode[1]>>4)&15);
704 return 0;
705 }
706 case 2: /* RXE format, __u32 constant */ {
707 __u32 *dxb, temp;
708 __u32 opc;
709
710 emu_store_rege((opcode[1]>>4)&15);
711 opc = *((__u32 *) opcode);
712 dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc);
713 /* FIXME: how to react if get_user fails ? */
714 get_user(temp, dxb);
715 /* call the emulation function */
716 ((void (*)(int, __u32))jump_table[opcode[5]])
717 (opcode[1]>>4,temp);
718 emu_load_rege((opcode[1]>>4)&15);
719 return 0;
720 }
721 case 3: /* RXF format, __u64 constant */ {
722 __u32 *dxb, temp;
723 __u32 opc;
724
725 emu_store_regd((opcode[1]>>4)&15);
726 opc = *((__u32 *) opcode);
727 dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc);
728 /* FIXME: how to react if copy_from_user fails ? */
729 copy_from_user(&temp, dxb, 8);
730 /* call the emulation function */
731 ((void (*)(int, __u32, int))jump_table[opcode[5]])
732 (opcode[1]>>4,temp,opcode[4]>>4);
733 emu_load_regd((opcode[1]>>4)&15);
734 return 0;
735 }
736 case 4: /* RXF format, __u32 constant */ {
737 __u32 *dxb, temp;
738 __u32 opc;
739
740 emu_store_rege((opcode[1]>>4)&15);
741 opc = *((__u32 *) opcode);
742 dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc);
743 /* FIXME: how to react if get_user fails ? */
744 get_user(temp, dxb);
745 /* call the emulation function */
746 ((void (*)(int, __u32, int))jump_table[opcode[5]])
747 (opcode[1]>>4,temp,opcode[4]>>4);
748 emu_load_rege((opcode[1]>>4)&15);
749 return 0;
750 }
751 case 5: /* RXE format, __u32 constant */
752 /* store_rege and load_regd */
753 {
754 __u32 *dxb, temp;
755 __u32 opc;
756 emu_store_rege((opcode[1]>>4)&15);
757 opc = *((__u32 *) opcode);
758 dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc);
759 /* FIXME: how to react if get_user fails ? */
760 get_user(temp, dxb);
761 /* call the emulation function */
762 ((void (*)(int, __u32))jump_table[opcode[5]])
763 (opcode[1]>>4,temp);
764 emu_load_regd((opcode[1]>>4)&15);
765 return 0;
766 }
767 default:
768 return 1;
769 }
770 }
771
772 /*
773 * Emulate LDR Rx,Ry with Rx or Ry not in {0, 2, 4, 6}
774 */
775 void math_emu_ldr(__u8 *opcode) {
776 __u16 opc = *((__u16 *) opcode);
777
778 if ((opc & 0x0090) == 0) { /* test if rx in {0,2,4,6} */
779 /* we got an exception therfore ry can't be in {0,2,4,6} */
780 __asm__ __volatile ( /* load rx from fp_regs.fprs[ry] */
781 " bras 1,0f\n"
782 " ld 0,0(%1)\n"
783 "0: ex %0,0(1)"
784 : /* no output */
785 : "a" (opc&0x00f0),
786 "a" (¤t->thread.fp_regs.fprs[opc&0x000f].d)
787 : "1" );
788 } else if ((opc & 0x0009) == 0) { /* test if ry in {0,2,4,6} */
789 __asm__ __volatile ( /* store ry to fp_regs.fprs[rx] */
790 " bras 1,0f\n"
791 " std 0,0(%1)\n"
792 "0: ex %0,0(1)"
793 : /* no output */
794 : "a" ((opc&0x000f)<<4),
795 "a" (¤t->thread.fp_regs.fprs[(opc&0x00f0)>>4].d)
796 : "1" );
797 } else { /* move fp_regs.fprs[ry] to fp_regs.fprs[rx] */
798 current->thread.fp_regs.fprs[(opc&0x00f0)>>4] =
799 current->thread.fp_regs.fprs[opc&0x000f];
800 }
801 }
802
803 /*
804 * Emulate LER Rx,Ry with Rx or Ry not in {0, 2, 4, 6}
805 */
806 void math_emu_ler(__u8 *opcode) {
807 __u16 opc = *((__u16 *) opcode);
808
809 if ((opc & 0x0090) == 0) { /* test if rx in {0,2,4,6} */
810 /* we got an exception therfore ry can't be in {0,2,4,6} */
811 __asm__ __volatile ( /* load rx from fp_regs.fprs[ry] */
812 " bras 1,0f\n"
813 " le 0,0(%1)\n"
814 "0: ex %0,0(1)"
815 : /* no output */
816 : "a" (opc&0x00f0),
817 "a" (¤t->thread.fp_regs.fprs[opc&0x000f].f)
818 : "1" );
819 } else if ((opc & 0x0009) == 0) { /* test if ry in {0,2,4,6} */
820 __asm__ __volatile ( /* store ry to fp_regs.fprs[rx] */
821 " bras 1,0f\n"
822 " ste 0,0(%1)\n"
823 "0: ex %0,0(1)"
824 : /* no output */
825 : "a" ((opc&0x000f)<<4),
826 "a" (¤t->thread.fp_regs.fprs[(opc&0x00f0)>>4].f)
827 : "1" );
828 } else { /* move fp_regs.fprs[ry] to fp_regs.fprs[rx] */
829 current->thread.fp_regs.fprs[(opc&0x00f0)>>4] =
830 current->thread.fp_regs.fprs[opc&0x000f];
831 }
832 }
833
834 /*
835 * Emulate LD R,D(X,B) with R not in {0, 2, 4, 6}
836 */
837 void math_emu_ld(__u8 *opcode, struct pt_regs * regs) {
838 __u32 opc = *((__u32 *) opcode);
839 __u64 *dxb;
840
841 dxb = (__u64 *) calc_addr(regs,opc>>16,opc>>12,opc);
842 /* FIXME: how to react if copy_from_user fails ? */
843 copy_from_user(¤t->thread.fp_regs.fprs[(opc>>20)&15].d, dxb, 8);
844 }
845
846 /*
847 * Emulate LE R,D(X,B) with R not in {0, 2, 4, 6}
848 */
849 void math_emu_le(__u8 *opcode, struct pt_regs * regs) {
850 __u32 opc = *((__u32 *) opcode);
851 __u32 *mem, *dxb;
852
853 dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc);
854 /* FIXME: how to react if get_user fails ? */
855 mem = (__u32 *) (¤t->thread.fp_regs.fprs[(opc>>20)&15].f);
856 get_user(mem[0], dxb);
857 }
858
859 /*
860 * Emulate STD R,D(X,B) with R not in {0, 2, 4, 6}
861 */
862 void math_emu_std(__u8 *opcode, struct pt_regs * regs) {
863 __u32 opc = *((__u32 *) opcode);
864 __u64 *dxb;
865 dxb = (__u64 *) calc_addr(regs,opc>>16,opc>>12,opc);
866 /* FIXME: how to react if copy_to_user fails ? */
867 copy_to_user(dxb, ¤t->thread.fp_regs.fprs[(opc>>20)&15].d, 8);
868 }
869
870 /*
871 * Emulate STE R,D(X,B) with R not in {0, 2, 4, 6}
872 */
873 void math_emu_ste(__u8 *opcode, struct pt_regs * regs) {
874 __u32 opc = *((__u32 *) opcode);
875 __u32 *mem, *dxb;
876 dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc);
877 /* FIXME: how to react if put_user fails ? */
878 mem = (__u32 *) (¤t->thread.fp_regs.fprs[(opc>>20)&15].f);
879 put_user(mem[0], dxb);
880 }
881
882 /*
883 * Emulate LFPC D(B)
884 */
885 int math_emu_lfpc(__u8 *opcode, struct pt_regs *regs) {
886 /* FIXME: how to do that ?!? */
887 return 0;
888 }
889
890 /*
891 * Emulate STFPC D(B)
892 */
893 int math_emu_stfpc(__u8 *opcode, struct pt_regs *regs) {
894 /* FIXME: how to do that ?!? */
895 return 0;
896 }
897
898 /*
899 * Emulate SRNM D(B)
900 */
901 int math_emu_srnm(__u8 *opcode, struct pt_regs *regs) {
902 /* FIXME: how to do that ?!? */
903 return 0;
904 }
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921