Root/target/linux/ubicom32/files/arch/ubicom32/kernel/unaligned_trap.c

1/*
2 * arch/ubicom32/kernel/unaligned_trap.c
3 * Handle unaligned traps in both user or kernel space.
4 *
5 * (C) Copyright 2009, Ubicom, Inc.
6 *
7 * This file is part of the Ubicom32 Linux Kernel Port.
8 *
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.
13 *
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.
18 *
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/>.
22 *
23 * Ubicom32 implementation derived from (with many thanks):
24 * arch/m68knommu
25 * arch/blackfin
26 * arch/parisc
27 */
28
29#include <linux/types.h>
30#include <linux/kernel.h>
31#include <asm/cacheflush.h>
32#include <asm/traps.h>
33
34#define FALSE 0
35#define TRUE 1
36
37/* no possible trap */
38#define UNUSED 0
39/* possible source operand trap */
40#define SRC 1
41#define SRC_2 2
42/* possible destination operand trap */
43#define DEST 3
44#define DEST_2 4
45/* can be either source or destination or both */
46#define TWO_OP 5
47#define TWO_OP_2 6
48
49/* TODO: What is the real value here, put something in to make it compile for
50 * now */
51#define MOVE_2 0x0d
52#define LSL_2 0x11
53#define LSR_2 0x13
54#define MOVEI 0x19
55#define CMPI 0x18
56
57static int op_format[32] =
58{
59    TWO_OP, /* 0x00 */
60    UNUSED,
61    SRC,
62    UNUSED,
63    TWO_OP, /* 0x04 */
64    TWO_OP,
65    SRC,
66    UNUSED,
67    TWO_OP_2, /* 0x08 */
68    TWO_OP,
69    TWO_OP_2,
70    TWO_OP,
71    TWO_OP_2, /* 0x0C */
72    TWO_OP,
73    TWO_OP_2,
74    TWO_OP,
75    TWO_OP, /* 0x10 */
76    TWO_OP_2,
77    TWO_OP,
78    TWO_OP,
79    UNUSED, /* 0x14 */
80    UNUSED,
81    UNUSED,
82    UNUSED,
83    SRC_2, /* 0x18 */
84    DEST_2,
85    UNUSED,
86    UNUSED,
87    UNUSED, /* 0x1C */
88    UNUSED,
89    UNUSED, /* unaligned CALLI will not be fixed. */
90    UNUSED
91};
92
93static int op_0_format[32] =
94{
95    UNUSED, /* 0x00 */
96    UNUSED,
97    UNUSED,
98    UNUSED,
99    UNUSED, /* 0x04 - ret don't fix - bad ret is always wrong */
100    UNUSED,
101    UNUSED,
102    UNUSED,
103    UNUSED, /* 0x08 */
104    UNUSED,
105    TWO_OP,
106    TWO_OP_2,
107    TWO_OP, /* 0x0c */
108    TWO_OP_2,
109    TWO_OP,
110    UNUSED, /* .1 can't trap */
111    UNUSED, /* 0x10 */
112    UNUSED,
113    SRC,
114    UNUSED,
115    UNUSED, /* 0x14 */
116    TWO_OP_2,
117    UNUSED,
118    UNUSED,
119    UNUSED, /* 0x18 */
120    UNUSED,
121    UNUSED,
122    UNUSED,
123    DEST, /* 0x1c */
124    DEST,
125    DEST,
126    DEST, /* all lea have 32-bit destination */
127};
128
129static int op_2_format[32] =
130{
131    UNUSED, /* 0x00 */
132    UNUSED,
133    UNUSED,
134    UNUSED,
135    UNUSED, /* 0x04 */
136    UNUSED,
137    SRC,
138    UNUSED,
139    UNUSED, /* 0x08 crcgen is .1 */
140    UNUSED,
141    UNUSED,
142    UNUSED,
143    UNUSED, /* 0x0c */
144    UNUSED,
145    UNUSED,
146    UNUSED,
147    SRC, /* 0x10 */
148    SRC_2,
149    SRC,
150    SRC_2,
151    SRC, /* 0x14 */
152    SRC_2,
153    SRC,
154    UNUSED,
155    UNUSED, /* 0x18 */
156    UNUSED,
157    SRC,
158    UNUSED,
159    SRC, /* 0x1c */
160    UNUSED,
161    SRC_2,
162    UNUSED,
163};
164
165static int op_6_format[32] =
166{
167    SRC_2, /* 0x00 */
168    SRC_2,
169    SRC_2,
170    SRC_2,
171    SRC_2, /* 0x04 */
172    SRC_2,
173    UNUSED,
174    SRC_2,
175    SRC, /* 0x08 MULS.4 */
176    SRC_2,
177    SRC,
178    UNUSED,
179    UNUSED, /* 0x0c */
180    UNUSED,
181    UNUSED,
182    UNUSED,
183    SRC, /* 0x10 */
184    SRC_2,
185    SRC,
186    SRC_2,
187    UNUSED, /* 0x14 */
188    UNUSED,
189    UNUSED,
190    UNUSED,
191    UNUSED, /* 0x18 */
192    UNUSED,
193    UNUSED,
194    UNUSED,
195    UNUSED, /* 0x1c */
196    UNUSED,
197    UNUSED,
198    UNUSED,
199};
200
201/*
202 * unaligned_get_address()
203 * get an address using save_an and save_dn registers, and updates save_an
204 * with side effects
205 */
206unsigned 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)
209{
210    unsigned char *address;
211
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)) {
217            offset <<= 2;
218        } else {
219            offset <<= 1;
220        }
221        if (specifier & 0x10) {
222            address = (unsigned char *)(save_an[areg] + offset);
223        } else {
224            address = (unsigned char *)save_an[areg];
225        }
226        save_an[areg] = save_an[areg] + offset;
227
228        /*
229         * Let caller know An registers have been modified.
230         */
231        *write_back_an = 1;
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));
237        } else {
238            address = (unsigned char *)(save_an[areg] +
239                            (save_dn[dreg] << 1));
240        }
241    } else {
242        int offset = ((specifier >> 3) & 0x60) | (specifier & 0x1f);
243        if (likely(four_byte)) {
244            address = (unsigned char *)(save_an[areg] +
245                            (offset << 2));
246        } else {
247            address = (unsigned char *)(save_an[areg] +
248                            (offset << 1));
249        }
250    }
251
252    return address;
253}
254
255static int save_dn[16];
256static int save_an[8];
257static int save_acc[5];
258
259/*
260 * unaligned_emulate()
261 * emulate the instruction at thread's pc that has taken an unaligned data
262 * trap.
263 *
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
267 *
268 * TODO: Protection is handled outside of this function
269 * TODO: handling simultaneous unaligned and memory protection traps
270 *
271 * Get thread state
272 * the PC and instruction (and local copy, emulate_inst), and An
273 * and Dn registers
274 * All implicit soruce state (source3, CSR, accumulators)
275
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
279 * extended)
280 * (2 bytes, with or without sign extension, or 4 bytes)
281 * modify emulate_inst to use d0 as source
282 * else
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
295 * bytes
296 * restore thread's implicit results (modified address registers, CSR,
297 * accumulators) add 4 to thread's pc
298 */
299void unaligned_emulate(unsigned int thread)
300{
301    unsigned int pc;
302    unsigned int inst;
303    unsigned int op;
304    unsigned int subop;
305    int format;
306    unsigned int emulate_inst;
307    int four_byte;
308    int src_operand, dest_operand;
309    int save_csr;
310    int source3;
311    unsigned int source1;
312    unsigned int source_data;
313    unsigned char *dest_address = NULL;
314    int source2 = 0;
315    unsigned int result;
316    unsigned int write_back_an = 0;
317    unsigned int chip_id_copy;
318
319    extern unsigned int trap_emulate;
320    extern unsigned int ubicom32_emulate_insn(int source1, int source2,
321                          int source3, int *save_acc,
322                          int *save_csr);
323
324    /*
325     * get the chip_id
326     */
327    asm volatile (
328    " move.4 %0, chip_id \n\t" /* get chip_id. */
329        : "=r"(chip_id_copy)
330        :
331    );
332
333    /*
334     * get the pc
335     */
336    asm volatile (
337    " move.4 CSR, %1 \n\t" /* set source thread in
338                               * CSR */
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"
343        : "=a"(pc)
344        : "d" ((1 << 8) | (thread << 9))
345        : "cc"
346    );
347
348    inst = *((unsigned int *)pc);
349    op = inst >> 27;
350    if (unlikely(op == 2 || op == 6)) {
351        subop = (inst >> 21) & 0x1f;
352    } else {
353        subop = (inst >> 11) & 0x1f;
354    }
355    format = op_format[op];
356    emulate_inst = inst;
357
358    if (op == 0) {
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];
364    }
365
366    if (unlikely(format == UNUSED)) {
367        /*
368         * We are not going to emulate this. Bump PC by 4 and move on.
369         */
370        asm volatile (
371        " move.4 CSR, %0 \n\t"
372        " setcsr_flush 0 \n\t"
373        " move.4 pc, %1 \n\t"
374        " setcsr #0 \n\t"
375        " setcsr_flush 0 \n\t"
376            :
377            : "d"((1 << 14) | (thread << 15)), "d"(pc + 4)
378            : "cc"
379        );
380        return;
381    }
382
383    four_byte = (format == TWO_OP || format == DEST || format == SRC);
384
385    /*
386     * source or destination memory operand needs emulation
387     */
388    src_operand = (format == SRC ||
389               format == SRC_2 ||
390               format == TWO_OP ||
391               format == TWO_OP_2) &&
392        ((inst >> 8) & 7) > 1;
393
394    dest_operand = (format == DEST ||
395            format == DEST_2 ||
396            format == TWO_OP ||
397            format == TWO_OP_2) &&
398        ((inst >> 24) & 7) > 1;
399
400    /*
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
403     * accumulators
404     */
405    asm volatile (
406    " move.4 CSR, %2 \n\t" /* set source thread in
407                             * CSR */
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)
446        : "cc"
447    );
448
449    /*
450     * turn off thread select bits if they were on
451     */
452    BUG_ON((save_csr & 0x04100) != 0);
453    if (unlikely(save_csr & 0x04100)) {
454        /*
455         * Things are in funny state as thread select bits are on in
456         * csr. PANIC.
457         */
458        panic("In unaligned trap handler. Trap thread CSR has thread "
459              "select bits on.\n");
460    }
461
462    save_csr = save_csr & 0x1000ff;
463
464    /*
465     * get the source1 operand
466     */
467    source1 = 0;
468    if (src_operand) {
469        unsigned char *src_address;
470
471        /*
472         * source1 comes from memory
473         */
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,
477                            four_byte, save_an,
478                            save_dn, &write_back_an);
479
480        /*
481         * get data (possibly unaligned)
482         */
483        if (likely(four_byte)) {
484            source_data = (*src_address << 24) |
485                (*(src_address + 1) << 16) |
486                (*(src_address + 2) << 8) |
487                *(src_address + 3);
488            source1 = source_data;
489        } else {
490            source1 = *src_address << 8 |
491                *(src_address + 1);
492
493            /*
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
497             * sign extended.
498             */
499            if ((!(op == 2 && subop == MOVE_2)) &&
500                (!((chip_id_copy >= 0x30000) &&
501                   (subop == LSL_2 || subop == LSR_2)))) {
502                /*
503                 * Have to sign extend the .2 entry.
504                 */
505                source1 = ((unsigned int)
506                       ((signed int)
507                        ((signed short) source1)));
508            }
509        }
510    } else if (likely(op != MOVEI)) {
511        /*
512         * source1 comes from a register, using move.4 d0, src1
513         * unaligned_emulate_get_source is pointer to code to insert remulated instruction
514         */
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);
520
521        asm volatile (
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"
527        " setcsr #0 \n\t"
528        " setcsr_flush 0 \n\t"
529            : "=d" (source1)
530            : "d" ((1 << 8) | (thread << 9))
531            : "cc"
532        );
533    }
534
535    /*
536     * get the destination address
537     */
538    if (dest_operand) {
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),
543                             four_byte, save_an,
544                             save_dn, &write_back_an);
545    }
546
547    if (write_back_an) {
548        /*
549         * restore any modified An registers
550         */
551        asm volatile (
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"
562        " setcsr #0 \n\t"
563        " setcsr_flush 0 \n\t"
564            :
565            : "d" ((1 << 14) | (thread << 15)), "a" (save_an)
566            : "cc"
567        );
568    }
569
570    /*
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
573     */
574    source2 = 0;
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];
579        /*
580         * force the emulated instruction to use d1 for source2 operand
581         */
582        emulate_inst = (emulate_inst & 0xffff07ff) | 0x800;
583    }
584
585    if (likely(op != MOVEI)) {
586        /*
587         * change emulated instruction source1 to d0
588         */
589        emulate_inst &= ~0x7ff;
590        emulate_inst |= 1 << 8;
591    }
592
593    if (unlikely(op == 6 || op == 2)) {
594        /*
595         * Set destination to d0
596         */
597        emulate_inst &= ~(0xf << 16);
598    } else if (likely(op != CMPI)) {
599        /*
600         * Set general destination field to d0.
601         */
602        emulate_inst &= ~(0x7ff << 16);
603        emulate_inst |= 1 << 24;
604    }
605
606    /*
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
610     */
611    *((unsigned int *)&trap_emulate) = emulate_inst;
612    flush_dcache_range((unsigned long)(&trap_emulate),
613               (unsigned long)(&trap_emulate) + 4);
614
615    result = ubicom32_emulate_insn(source1, source2, source3,
616                       save_acc, &save_csr);
617
618    /*
619     * set the result value
620     */
621    if (dest_operand) {
622        /*
623         * copy result to memory
624         */
625        if (four_byte) {
626            *dest_address++ =
627                (unsigned char)((result >> 24) & 0xff);
628            *dest_address++ =
629                (unsigned char)((result >> 16) & 0xff);
630        }
631        *dest_address++ = (unsigned char)((result >> 8) & 0xff);
632        *dest_address = (unsigned char)(result & 0xff);
633    } else if (likely(op != CMPI)) {
634        /*
635         * copy result to a register, using move.4 dest, result
636         */
637        extern unsigned int unaligned_trap_set_result;
638        *((unsigned int *)&unaligned_trap_set_result) &= ~0x7ff0000;
639
640        if (op == 2 || op == 6) {
641            *((unsigned int *)&unaligned_trap_set_result) |=
642                ((inst & 0x000f0000) | 0x01000000);
643        } else {
644            *((unsigned int *)&unaligned_trap_set_result) |=
645                (inst & 0x7ff0000);
646        }
647        flush_dcache_range((unsigned long)&unaligned_trap_set_result,
648                   ((unsigned long)(&unaligned_trap_set_result) + 4));
649
650        asm volatile (
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"
656        " setcsr #0 \n\t"
657        " setcsr_flush 0 \n\t"
658            :
659            : "d"(result), "d" ((1 << 14) | (thread << 15))
660            : "cc"
661        );
662    }
663
664    /*
665     * bump PC in thread and restore implicit register changes
666     */
667    asm volatile (
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"
677    " setcsr #0 \n\t"
678    " setcsr_flush 0 \n\t"
679        :
680        : "d"((1 << 14) | (thread << 15)),
681          "d"(pc + 4), "d"(save_csr), "a"(save_acc)
682        : "cc"
683    );
684}
685
686/*
687 * unaligned_only()
688 * Return true if either of the unaligned causes are set (and no others).
689 */
690int unaligned_only(unsigned int cause)
691{
692    unsigned int unaligned_cause_mask =
693        (1 << TRAP_CAUSE_DST_MISALIGNED) |
694        (1 << TRAP_CAUSE_SRC1_MISALIGNED);
695
696    BUG_ON(cause == 0);
697    return (cause & unaligned_cause_mask) == cause;
698}
699

Archive Download this file



interactive