2 * arch/ubicom32/kernel/unaligned_trap.c
3 * Handle unaligned traps in both user or kernel space.
5 * (C) Copyright 2009, Ubicom, Inc.
7 * This file is part of the Ubicom32 Linux Kernel Port.
9 * The Ubicom32 Linux Kernel Port is free software: you can redistribute
10 * it and/or modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation, either version 2 of the
12 * License, or (at your option) any later version.
14 * The Ubicom32 Linux Kernel Port is distributed in the hope that it
15 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
16 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
17 * the GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with the Ubicom32 Linux Kernel Port. If not,
21 * see <http://www.gnu.org/licenses/>.
23 * Ubicom32 implementation derived from (with many thanks):
29 #include <linux/types.h>
30 #include <linux/kernel.h>
31 #include <asm/cacheflush.h>
32 #include <asm/traps.h>
37 /* no possible trap */
39 /* possible source operand trap */
42 /* possible destination operand trap */
45 /* can be either source or destination or both */
49 /* TODO: What is the real value here, put something in to make it compile for
57 static int op_format
[32] =
89 UNUSED
, /* unaligned CALLI will not be fixed. */
93 static int op_0_format
[32] =
99 UNUSED
, /* 0x04 - ret don't fix - bad ret is always wrong */
110 UNUSED
, /* .1 can't trap */
126 DEST
, /* all lea have 32-bit destination */
129 static int op_2_format
[32] =
139 UNUSED
, /* 0x08 crcgen is .1 */
165 static int op_6_format
[32] =
175 SRC
, /* 0x08 MULS.4 */
202 * unaligned_get_address()
203 * get an address using save_an and save_dn registers, and updates save_an
206 unsigned char *unaligned_get_address(int thread
, int specifier
, int four_byte
,
207 unsigned int save_an
[],
208 unsigned int save_dn
[], int *write_back_an
)
210 unsigned char *address
;
212 int areg
= (specifier
>> 5) & 7;
213 if ((specifier
>> 8) == 2) {
214 int offset
= specifier
& 0xf;
215 offset
= ((offset
<< 28) >> 28);
216 if (likely(four_byte
)) {
221 if (specifier
& 0x10) {
222 address
= (unsigned char *)(save_an
[areg
] + offset
);
224 address
= (unsigned char *)save_an
[areg
];
226 save_an
[areg
] = save_an
[areg
] + offset
;
229 * Let caller know An registers have been modified.
232 } else if ((specifier
>> 8) == 3) {
233 int dreg
= specifier
& 0xf;
234 if (likely(four_byte
)) {
235 address
= (unsigned char *)(save_an
[areg
] +
236 (save_dn
[dreg
] << 2));
238 address
= (unsigned char *)(save_an
[areg
] +
239 (save_dn
[dreg
] << 1));
242 int offset
= ((specifier
>> 3) & 0x60) | (specifier
& 0x1f);
243 if (likely(four_byte
)) {
244 address
= (unsigned char *)(save_an
[areg
] +
247 address
= (unsigned char *)(save_an
[areg
] +
255 static int save_dn
[16];
256 static int save_an
[8];
257 static int save_acc
[5];
260 * unaligned_emulate()
261 * emulate the instruction at thread's pc that has taken an unaligned data
264 * source or destination or both might be unaligned
265 * the instruction must have a memory source or destination or both
266 * the emulated instruction is copied and executed in this thread
268 * TODO: Protection is handled outside of this function
269 * TODO: handling simultaneous unaligned and memory protection traps
272 * the PC and instruction (and local copy, emulate_inst), and An
274 * All implicit soruce state (source3, CSR, accumulators)
276 * if the instruction has a memory source
277 * Use the instruction, An and Dn registers to form src_address
278 * get unaligned source data from src_address (usually sign
280 * (2 bytes, with or without sign extension, or 4 bytes)
281 * modify emulate_inst to use d0 as source
283 * get the soure operand from one of thread's registers
284 * if instruction has a memory destination
285 * Use the instruction, An and Dn registers to form dest_address
286 * modify emulate_inst to use d0 as destination
287 * if there was a memory source
288 * put the source data in thread's d0
289 * get the source-2 Dn operand and source 3 operand from thread
290 * execute modified inst
291 * (save it, flush caches, set up local values for implicit
292 * sources, execute, save explicit and implicit results)
293 * if inst has destination address
294 * copy result to dest_address, possibly unaligned, 1, 2, or 4
296 * restore thread's implicit results (modified address registers, CSR,
297 * accumulators) add 4 to thread's pc
299 void unaligned_emulate(unsigned int thread
)
306 unsigned int emulate_inst
;
308 int src_operand
, dest_operand
;
311 unsigned int source1
;
312 unsigned int source_data
;
313 unsigned char *dest_address
= NULL
;
316 unsigned int write_back_an
= 0;
317 unsigned int chip_id_copy
;
319 extern unsigned int trap_emulate
;
320 extern unsigned int ubicom32_emulate_insn(int source1
, int source2
,
321 int source3
, int *save_acc
,
328 " move.4 %0, chip_id \n\t" /* get chip_id. */
337 " move.4 CSR, %1 \n\t" /* set source thread in
339 " setcsr_flush 0 \n\t"
340 " move.4 %0, pc \n\t"
341 " move.4 CSR, #0 \n\t" /* restore CSR */
342 " setcsr_flush 0 \n\t"
344 : "d" ((1 << 8) | (thread
<< 9))
348 inst
= *((unsigned int *)pc
);
350 if (unlikely(op
== 2 || op
== 6)) {
351 subop
= (inst
>> 21) & 0x1f;
353 subop
= (inst
>> 11) & 0x1f;
355 format
= op_format
[op
];
359 format
= op_0_format
[subop
];
360 } else if (op
== 2) {
361 format
= op_2_format
[subop
];
362 } else if (op
== 6) {
363 format
= op_6_format
[subop
];
366 if (unlikely(format
== UNUSED
)) {
368 * We are not going to emulate this. Bump PC by 4 and move on.
371 " move.4 CSR, %0 \n\t"
372 " setcsr_flush 0 \n\t"
373 " move.4 pc, %1 \n\t"
375 " setcsr_flush 0 \n\t"
377 : "d"((1 << 14) | (thread
<< 15)), "d"(pc
+ 4)
383 four_byte
= (format
== TWO_OP
|| format
== DEST
|| format
== SRC
);
386 * source or destination memory operand needs emulation
388 src_operand
= (format
== SRC
||
391 format
== TWO_OP_2
) &&
392 ((inst
>> 8) & 7) > 1;
394 dest_operand
= (format
== DEST
||
397 format
== TWO_OP_2
) &&
398 ((inst
>> 24) & 7) > 1;
401 * get thread's implicit sources (not covered by source context select).
402 * data and address registers and CSR (for flag bits) and src3 and
406 " move.4 CSR, %2 \n\t" /* set source thread in
408 " setcsr_flush 0 \n\t"
409 " move.4 (%3), d0 \n\t" /* get dn registers */
410 " move.4 4(%3), d1 \n\t"
411 " move.4 8(%3), d2 \n\t"
412 " move.4 12(%3), d3 \n\t"
413 " move.4 16(%3), d4 \n\t"
414 " move.4 20(%3), d5 \n\t"
415 " move.4 24(%3), d6 \n\t"
416 " move.4 28(%3), d7 \n\t"
417 " move.4 32(%3), d8 \n\t"
418 " move.4 36(%3), d9 \n\t"
419 " move.4 40(%3), d10 \n\t"
420 " move.4 44(%3), d11 \n\t"
421 " move.4 48(%3), d12 \n\t"
422 " move.4 52(%3), d13 \n\t"
423 " move.4 56(%3), d14 \n\t"
424 " move.4 60(%3), d15 \n\t"
425 " move.4 (%4), a0 \n\t" /* get an registers */
426 " move.4 4(%4), a1 \n\t"
427 " move.4 8(%4), a2 \n\t"
428 " move.4 12(%4), a3 \n\t"
429 " move.4 16(%4), a4 \n\t"
430 " move.4 20(%4), a5 \n\t"
431 " move.4 24(%4), a6 \n\t"
432 " move.4 28(%4), a7 \n\t"
433 " move.4 %0, CSR \n\t" /* get csr and source3
434 * implicit operands */
435 " move.4 %1, source3 \n\t"
436 " move.4 (%5), acc0_lo \n\t" /* get accumulators */
437 " move.4 4(%5), acc0_hi \n\t"
438 " move.4 8(%5), acc1_lo \n\t"
439 " move.4 12(%5), acc1_hi \n\t"
440 " move.4 16(%5), mac_rc16 \n\t"
441 " move.4 CSR, #0 \n\t" /* restore CSR */
442 " setcsr_flush 0 \n\t"
443 : "=m"(save_csr
), "=m"(source3
)
444 : "d"((1 << 8) | (thread
<< 9)),
445 "a"(save_dn
), "a"(save_an
), "a"(save_acc
)
450 * turn off thread select bits if they were on
452 BUG_ON((save_csr
& 0x04100) != 0);
453 if (unlikely(save_csr
& 0x04100)) {
455 * Things are in funny state as thread select bits are on in
458 panic("In unaligned trap handler. Trap thread CSR has thread "
459 "select bits on.\n");
462 save_csr
= save_csr
& 0x1000ff;
465 * get the source1 operand
469 unsigned char *src_address
;
472 * source1 comes from memory
474 BUG_ON(!(format
== TWO_OP
|| format
== TWO_OP_2
||
475 format
== SRC
|| format
== SRC_2
));
476 src_address
= unaligned_get_address(thread
, inst
& 0x7ff,
478 save_dn
, &write_back_an
);
481 * get data (possibly unaligned)
483 if (likely(four_byte
)) {
484 source_data
= (*src_address
<< 24) |
485 (*(src_address
+ 1) << 16) |
486 (*(src_address
+ 2) << 8) |
488 source1
= source_data
;
490 source1
= *src_address
<< 8 |
494 * Source is not extended if the instrution is MOVE.2 or
495 * if the cpu CHIP_ID >= 0x30000 and the instruction is
496 * either LSL.2 or LSR.2. All other cases have to be
499 if ((!(op
== 2 && subop
== MOVE_2
)) &&
500 (!((chip_id_copy
>= 0x30000) &&
501 (subop
== LSL_2
|| subop
== LSR_2
)))) {
503 * Have to sign extend the .2 entry.
505 source1
= ((unsigned int)
507 ((signed short) source1
)));
510 } else if (likely(op
!= MOVEI
)) {
512 * source1 comes from a register, using move.4 d0, src1
513 * unaligned_emulate_get_source is pointer to code to insert remulated instruction
515 extern unsigned int unaligned_emulate_get_src
;
516 *((int *)&unaligned_emulate_get_src
) &= ~(0x7ff);
517 *((int *)&unaligned_emulate_get_src
) |= (inst
& 0x7ff);
518 flush_dcache_range((unsigned long)(&unaligned_emulate_get_src
),
519 (unsigned long)(&unaligned_emulate_get_src
) + 4);
522 /* source1 uses thread's registers */
523 " move.4 CSR, %1 \n\t"
524 " setcsr_flush 0 \n\t"
525 "unaligned_emulate_get_src: \n\t"
526 " move.4 %0, #0 \n\t"
528 " setcsr_flush 0 \n\t"
530 : "d" ((1 << 8) | (thread
<< 9))
536 * get the destination address
539 BUG_ON(!(format
== TWO_OP
|| format
== TWO_OP_2
||
540 format
== DEST
|| format
== DEST_2
));
541 dest_address
= unaligned_get_address(thread
,
542 ((inst
>> 16) & 0x7ff),
544 save_dn
, &write_back_an
);
549 * restore any modified An registers
552 " move.4 CSR, %0 \n\t"
553 " setcsr_flush 0 \n\t"
554 " move.4 a0, (%1) \n\t"
555 " move.4 a1, 4(%1) \n\t"
556 " move.4 a2, 8(%1) \n\t"
557 " move.4 a3, 12(%1) \n\t"
558 " move.4 a4, 16(%1) \n\t"
559 " move.4 a5, 20(%1) \n\t"
560 " move.4 a6, 24(%1) \n\t"
561 " move.4 a7, 28(%1) \n\t"
563 " setcsr_flush 0 \n\t"
565 : "d" ((1 << 14) | (thread
<< 15)), "a" (save_an
)
571 * get source 2 register if needed, and modify inst to use d1 for
572 * source-2 source-2 will come from this thread, not the trapping thread
575 if ((op
>= 8 && op
<= 0x17) ||
576 ((op
== 2 || op
== 6) && (inst
& 0x4000000))) {
577 int src_dn
= (inst
>> 11) & 0xf;
578 source2
= save_dn
[src_dn
];
580 * force the emulated instruction to use d1 for source2 operand
582 emulate_inst
= (emulate_inst
& 0xffff07ff) | 0x800;
585 if (likely(op
!= MOVEI
)) {
587 * change emulated instruction source1 to d0
589 emulate_inst
&= ~0x7ff;
590 emulate_inst
|= 1 << 8;
593 if (unlikely(op
== 6 || op
== 2)) {
595 * Set destination to d0
597 emulate_inst
&= ~(0xf << 16);
598 } else if (likely(op
!= CMPI
)) {
600 * Set general destination field to d0.
602 emulate_inst
&= ~(0x7ff << 16);
603 emulate_inst
|= 1 << 24;
607 * execute emulated instruction d0, to d0, no memory access
608 * source2 if needed will be in d1
609 * source3, CSR, and accumulators are set up before execution
611 *((unsigned int *)&trap_emulate
) = emulate_inst
;
612 flush_dcache_range((unsigned long)(&trap_emulate
),
613 (unsigned long)(&trap_emulate
) + 4);
615 result
= ubicom32_emulate_insn(source1
, source2
, source3
,
616 save_acc
, &save_csr
);
619 * set the result value
623 * copy result to memory
627 (unsigned char)((result
>> 24) & 0xff);
629 (unsigned char)((result
>> 16) & 0xff);
631 *dest_address
++ = (unsigned char)((result
>> 8) & 0xff);
632 *dest_address
= (unsigned char)(result
& 0xff);
633 } else if (likely(op
!= CMPI
)) {
635 * copy result to a register, using move.4 dest, result
637 extern unsigned int unaligned_trap_set_result
;
638 *((unsigned int *)&unaligned_trap_set_result
) &= ~0x7ff0000;
640 if (op
== 2 || op
== 6) {
641 *((unsigned int *)&unaligned_trap_set_result
) |=
642 ((inst
& 0x000f0000) | 0x01000000);
644 *((unsigned int *)&unaligned_trap_set_result
) |=
647 flush_dcache_range((unsigned long)&unaligned_trap_set_result
,
648 ((unsigned long)(&unaligned_trap_set_result
) + 4));
651 /* result uses thread's registers */
652 " move.4 CSR, %1 \n\t"
653 " setcsr_flush 0 \n\t"
654 "unaligned_trap_set_result: \n\t"
655 " move.4 #0, %0 \n\t"
657 " setcsr_flush 0 \n\t"
659 : "d"(result
), "d" ((1 << 14) | (thread
<< 15))
665 * bump PC in thread and restore implicit register changes
668 " move.4 CSR, %0 \n\t"
669 " setcsr_flush 0 \n\t"
670 " move.4 pc, %1 \n\t"
671 " move.4 acc0_lo, (%3) \n\t"
672 " move.4 acc0_hi, 4(%3) \n\t"
673 " move.4 acc1_lo, 8(%3) \n\t"
674 " move.4 acc1_hi, 12(%3) \n\t"
675 " move.4 mac_rc16, 16(%3) \n\t"
676 " move.4 CSR, %2 \n\t"
678 " setcsr_flush 0 \n\t"
680 : "d"((1 << 14) | (thread
<< 15)),
681 "d"(pc
+ 4), "d"(save_csr
), "a"(save_acc
)
688 * Return true if either of the unaligned causes are set (and no others).
690 int unaligned_only(unsigned int cause
)
692 unsigned int unaligned_cause_mask
=
693 (1 << TRAP_CAUSE_DST_MISALIGNED
) |
694 (1 << TRAP_CAUSE_SRC1_MISALIGNED
);
697 return (cause
& unaligned_cause_mask
) == cause
;