ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / arm / mm / alignment.c
1 /*
2  *  linux/arch/arm/mm/alignment.c
3  *
4  *  Copyright (C) 1995  Linus Torvalds
5  *  Modifications for ARM processor (c) 1995-2001 Russell King
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  */
11 #include <linux/config.h>
12 #include <linux/compiler.h>
13 #include <linux/kernel.h>
14 #include <linux/errno.h>
15 #include <linux/string.h>
16 #include <linux/ptrace.h>
17 #include <linux/proc_fs.h>
18 #include <linux/init.h>
19
20 #include <asm/uaccess.h>
21 #include <asm/unaligned.h>
22
23 #include "fault.h"
24
25 /*
26  * 32-bit misaligned trap handler (c) 1998 San Mehat (CCC) -July 1998
27  * /proc/sys/debug/alignment, modified and integrated into
28  * Linux 2.1 by Russell King
29  *
30  * Speed optimisations and better fault handling by Russell King.
31  *
32  * *** NOTE ***
33  * This code is not portable to processors with late data abort handling.
34  */
35 #define CODING_BITS(i)  (i & 0x0e000000)
36
37 #define LDST_I_BIT(i)   (i & (1 << 26))         /* Immediate constant   */
38 #define LDST_P_BIT(i)   (i & (1 << 24))         /* Preindex             */
39 #define LDST_U_BIT(i)   (i & (1 << 23))         /* Add offset           */
40 #define LDST_W_BIT(i)   (i & (1 << 21))         /* Writeback            */
41 #define LDST_L_BIT(i)   (i & (1 << 20))         /* Load                 */
42
43 #define LDST_P_EQ_U(i)  ((((i) ^ ((i) >> 1)) & (1 << 23)) == 0)
44
45 #define LDSTH_I_BIT(i)  (i & (1 << 22))         /* half-word immed      */
46 #define LDM_S_BIT(i)    (i & (1 << 22))         /* write CPSR from SPSR */
47
48 #define RN_BITS(i)      ((i >> 16) & 15)        /* Rn                   */
49 #define RD_BITS(i)      ((i >> 12) & 15)        /* Rd                   */
50 #define RM_BITS(i)      (i & 15)                /* Rm                   */
51
52 #define REGMASK_BITS(i) (i & 0xffff)
53 #define OFFSET_BITS(i)  (i & 0x0fff)
54
55 #define IS_SHIFT(i)     (i & 0x0ff0)
56 #define SHIFT_BITS(i)   ((i >> 7) & 0x1f)
57 #define SHIFT_TYPE(i)   (i & 0x60)
58 #define SHIFT_LSL       0x00
59 #define SHIFT_LSR       0x20
60 #define SHIFT_ASR       0x40
61 #define SHIFT_RORRRX    0x60
62
63 static unsigned long ai_user;
64 static unsigned long ai_sys;
65 static unsigned long ai_skipped;
66 static unsigned long ai_half;
67 static unsigned long ai_word;
68 static unsigned long ai_multi;
69 static int ai_usermode;
70
71 #ifdef CONFIG_PROC_FS
72 static const char *usermode_action[] = {
73         "ignored",
74         "warn",
75         "fixup",
76         "fixup+warn",
77         "signal",
78         "signal+warn"
79 };
80
81 static int
82 proc_alignment_read(char *page, char **start, off_t off, int count, int *eof,
83                     void *data)
84 {
85         char *p = page;
86         int len;
87
88         p += sprintf(p, "User:\t\t%lu\n", ai_user);
89         p += sprintf(p, "System:\t\t%lu\n", ai_sys);
90         p += sprintf(p, "Skipped:\t%lu\n", ai_skipped);
91         p += sprintf(p, "Half:\t\t%lu\n", ai_half);
92         p += sprintf(p, "Word:\t\t%lu\n", ai_word);
93         p += sprintf(p, "Multi:\t\t%lu\n", ai_multi);
94         p += sprintf(p, "User faults:\t%i (%s)\n", ai_usermode,
95                         usermode_action[ai_usermode]);
96
97         len = (p - page) - off;
98         if (len < 0)
99                 len = 0;
100
101         *eof = (len <= count) ? 1 : 0;
102         *start = page + off;
103
104         return len;
105 }
106
107 static int proc_alignment_write(struct file *file, const char __user *buffer,
108                                unsigned long count, void *data)
109 {
110         char mode;
111
112         if (count > 0) {
113                 if (get_user(mode, buffer))
114                         return -EFAULT;
115                 if (mode >= '0' && mode <= '5')
116                            ai_usermode = mode - '0';
117         }
118         return count;
119 }
120
121 #endif /* CONFIG_PROC_FS */
122
123 union offset_union {
124         unsigned long un;
125           signed long sn;
126 };
127
128 #define TYPE_ERROR      0
129 #define TYPE_FAULT      1
130 #define TYPE_LDST       2
131 #define TYPE_DONE       3
132
133 #define __get8_unaligned_check(ins,val,addr,err)        \
134         __asm__(                                        \
135         "1:     "ins"   %1, [%2], #1\n"                 \
136         "2:\n"                                          \
137         "       .section .fixup,\"ax\"\n"               \
138         "       .align  2\n"                            \
139         "3:     mov     %0, #1\n"                       \
140         "       b       2b\n"                           \
141         "       .previous\n"                            \
142         "       .section __ex_table,\"a\"\n"            \
143         "       .align  3\n"                            \
144         "       .long   1b, 3b\n"                       \
145         "       .previous\n"                            \
146         : "=r" (err), "=&r" (val), "=r" (addr)          \
147         : "0" (err), "2" (addr))
148
149 #define __get16_unaligned_check(ins,val,addr)                   \
150         do {                                                    \
151                 unsigned int err = 0, v, a = addr;              \
152                 __get8_unaligned_check(ins,val,a,err);          \
153                 __get8_unaligned_check(ins,v,a,err);            \
154                 val |= v << 8;                                  \
155                 if (err)                                        \
156                         goto fault;                             \
157         } while (0)
158
159 #define get16_unaligned_check(val,addr) \
160         __get16_unaligned_check("ldrb",val,addr)
161
162 #define get16t_unaligned_check(val,addr) \
163         __get16_unaligned_check("ldrbt",val,addr)
164
165 #define __get32_unaligned_check(ins,val,addr)                   \
166         do {                                                    \
167                 unsigned int err = 0, v, a = addr;              \
168                 __get8_unaligned_check(ins,val,a,err);          \
169                 __get8_unaligned_check(ins,v,a,err);            \
170                 val |= v << 8;                                  \
171                 __get8_unaligned_check(ins,v,a,err);            \
172                 val |= v << 16;                                 \
173                 __get8_unaligned_check(ins,v,a,err);            \
174                 val |= v << 24;                                 \
175                 if (err)                                        \
176                         goto fault;                             \
177         } while (0)
178
179 #define get32_unaligned_check(val,addr) \
180         __get32_unaligned_check("ldrb",val,addr)
181
182 #define get32t_unaligned_check(val,addr) \
183         __get32_unaligned_check("ldrbt",val,addr)
184
185 #define __put16_unaligned_check(ins,val,addr)                   \
186         do {                                                    \
187                 unsigned int err = 0, v = val, a = addr;        \
188                 __asm__(                                        \
189                 "1:     "ins"   %1, [%2], #1\n"                 \
190                 "       mov     %1, %1, lsr #8\n"               \
191                 "2:     "ins"   %1, [%2]\n"                     \
192                 "3:\n"                                          \
193                 "       .section .fixup,\"ax\"\n"               \
194                 "       .align  2\n"                            \
195                 "4:     mov     %0, #1\n"                       \
196                 "       b       3b\n"                           \
197                 "       .previous\n"                            \
198                 "       .section __ex_table,\"a\"\n"            \
199                 "       .align  3\n"                            \
200                 "       .long   1b, 4b\n"                       \
201                 "       .long   2b, 4b\n"                       \
202                 "       .previous\n"                            \
203                 : "=r" (err), "=&r" (v), "=&r" (a)              \
204                 : "0" (err), "1" (v), "2" (a));                 \
205                 if (err)                                        \
206                         goto fault;                             \
207         } while (0)
208
209 #define put16_unaligned_check(val,addr)  \
210         __put16_unaligned_check("strb",val,addr)
211
212 #define put16t_unaligned_check(val,addr) \
213         __put16_unaligned_check("strbt",val,addr)
214
215 #define __put32_unaligned_check(ins,val,addr)                   \
216         do {                                                    \
217                 unsigned int err = 0, v = val, a = addr;        \
218                 __asm__(                                        \
219                 "1:     "ins"   %1, [%2], #1\n"                 \
220                 "       mov     %1, %1, lsr #8\n"               \
221                 "2:     "ins"   %1, [%2], #1\n"                 \
222                 "       mov     %1, %1, lsr #8\n"               \
223                 "3:     "ins"   %1, [%2], #1\n"                 \
224                 "       mov     %1, %1, lsr #8\n"               \
225                 "4:     "ins"   %1, [%2]\n"                     \
226                 "5:\n"                                          \
227                 "       .section .fixup,\"ax\"\n"               \
228                 "       .align  2\n"                            \
229                 "6:     mov     %0, #1\n"                       \
230                 "       b       5b\n"                           \
231                 "       .previous\n"                            \
232                 "       .section __ex_table,\"a\"\n"            \
233                 "       .align  3\n"                            \
234                 "       .long   1b, 6b\n"                       \
235                 "       .long   2b, 6b\n"                       \
236                 "       .long   3b, 6b\n"                       \
237                 "       .long   4b, 6b\n"                       \
238                 "       .previous\n"                            \
239                 : "=r" (err), "=&r" (v), "=&r" (a)              \
240                 : "0" (err), "1" (v), "2" (a));                 \
241                 if (err)                                        \
242                         goto fault;                             \
243         } while (0)
244
245 #define put32_unaligned_check(val,addr)  \
246         __put32_unaligned_check("strb", val, addr)
247
248 #define put32t_unaligned_check(val,addr) \
249         __put32_unaligned_check("strbt", val, addr)
250
251 static void
252 do_alignment_finish_ldst(unsigned long addr, unsigned long instr, struct pt_regs *regs, union offset_union offset)
253 {
254         if (!LDST_U_BIT(instr))
255                 offset.un = -offset.un;
256
257         if (!LDST_P_BIT(instr))
258                 addr += offset.un;
259
260         if (!LDST_P_BIT(instr) || LDST_W_BIT(instr))
261                 regs->uregs[RN_BITS(instr)] = addr;
262 }
263
264 static int
265 do_alignment_ldrhstrh(unsigned long addr, unsigned long instr, struct pt_regs *regs)
266 {
267         unsigned int rd = RD_BITS(instr);
268
269         if ((instr & 0x01f00ff0) == 0x01000090)
270                 goto swp;
271
272         if ((instr & 0x90) != 0x90 || (instr & 0x60) == 0)
273                 goto bad;
274
275         ai_half += 1;
276
277         if (user_mode(regs))
278                 goto user;
279
280         if (LDST_L_BIT(instr)) {
281                 unsigned long val;
282                 get16_unaligned_check(val, addr);
283
284                 /* signed half-word? */
285                 if (instr & 0x40)
286                         val = (signed long)((signed short) val);
287
288                 regs->uregs[rd] = val;
289         } else
290                 put16_unaligned_check(regs->uregs[rd], addr);
291
292         return TYPE_LDST;
293
294  user:
295         if (LDST_L_BIT(instr)) {
296                 unsigned long val;
297                 get16t_unaligned_check(val, addr);
298
299                 /* signed half-word? */
300                 if (instr & 0x40)
301                         val = (signed long)((signed short) val);
302
303                 regs->uregs[rd] = val;
304         } else
305                 put16t_unaligned_check(regs->uregs[rd], addr);
306
307         return TYPE_LDST;
308
309  swp:
310         printk(KERN_ERR "Alignment trap: not handling swp instruction\n");
311  bad:
312         return TYPE_ERROR;
313
314  fault:
315         return TYPE_FAULT;
316 }
317
318 static int
319 do_alignment_ldrstr(unsigned long addr, unsigned long instr, struct pt_regs *regs)
320 {
321         unsigned int rd = RD_BITS(instr);
322
323         ai_word += 1;
324
325         if ((!LDST_P_BIT(instr) && LDST_W_BIT(instr)) || user_mode(regs))
326                 goto trans;
327
328         if (LDST_L_BIT(instr)) {
329                 unsigned int val;
330                 get32_unaligned_check(val, addr);
331                 regs->uregs[rd] = val;
332         } else
333                 put32_unaligned_check(regs->uregs[rd], addr);
334         return TYPE_LDST;
335
336  trans:
337         if (LDST_L_BIT(instr)) {
338                 unsigned int val;
339                 get32t_unaligned_check(val, addr);
340                 regs->uregs[rd] = val;
341         } else
342                 put32t_unaligned_check(regs->uregs[rd], addr);
343         return TYPE_LDST;
344
345  fault:
346         return TYPE_FAULT;
347 }
348
349 /*
350  * LDM/STM alignment handler.
351  *
352  * There are 4 variants of this instruction:
353  *
354  * B = rn pointer before instruction, A = rn pointer after instruction
355  *              ------ increasing address ----->
356  *              |    | r0 | r1 | ... | rx |    |
357  * PU = 01             B                    A
358  * PU = 11        B                    A
359  * PU = 00        A                    B
360  * PU = 10             A                    B
361  */
362 static int
363 do_alignment_ldmstm(unsigned long addr, unsigned long instr, struct pt_regs *regs)
364 {
365         unsigned int rd, rn, correction, nr_regs, regbits;
366         unsigned long eaddr, newaddr;
367
368         if (LDM_S_BIT(instr))
369                 goto bad;
370
371         correction = 4; /* processor implementation defined */
372         regs->ARM_pc += correction;
373
374         ai_multi += 1;
375
376         /* count the number of registers in the mask to be transferred */
377         nr_regs = hweight16(REGMASK_BITS(instr)) * 4;
378
379         rn = RN_BITS(instr);
380         newaddr = eaddr = regs->uregs[rn];
381
382         if (!LDST_U_BIT(instr))
383                 nr_regs = -nr_regs;
384         newaddr += nr_regs;
385         if (!LDST_U_BIT(instr))
386                 eaddr = newaddr;
387
388         if (LDST_P_EQ_U(instr)) /* U = P */
389                 eaddr += 4;
390
391         /* 
392          * For alignment faults on the ARM922T/ARM920T the MMU  makes
393          * the FSR (and hence addr) equal to the updated base address
394          * of the multiple access rather than the restored value.
395          * Switch this message off if we've got a ARM92[02], otherwise
396          * [ls]dm alignment faults are noisy!
397          */
398 #if !(defined CONFIG_CPU_ARM922T)  && !(defined CONFIG_CPU_ARM920T)
399         /*
400          * This is a "hint" - we already have eaddr worked out by the
401          * processor for us.
402          */
403         if (addr != eaddr) {
404                 printk(KERN_ERR "LDMSTM: PC = %08lx, instr = %08lx, "
405                         "addr = %08lx, eaddr = %08lx\n",
406                          instruction_pointer(regs), instr, addr, eaddr);
407                 show_regs(regs);
408         }
409 #endif
410
411         if (user_mode(regs)) {
412                 for (regbits = REGMASK_BITS(instr), rd = 0; regbits;
413                      regbits >>= 1, rd += 1)
414                         if (regbits & 1) {
415                                 if (LDST_L_BIT(instr)) {
416                                         unsigned int val;
417                                         get32t_unaligned_check(val, eaddr);
418                                         regs->uregs[rd] = val;
419                                 } else
420                                         put32t_unaligned_check(regs->uregs[rd], eaddr);
421                                 eaddr += 4;
422                         }
423         } else {
424                 for (regbits = REGMASK_BITS(instr), rd = 0; regbits;
425                      regbits >>= 1, rd += 1)
426                         if (regbits & 1) {
427                                 if (LDST_L_BIT(instr)) {
428                                         unsigned int val;
429                                         get32_unaligned_check(val, eaddr);
430                                         regs->uregs[rd] = val;
431                                 } else
432                                         put32_unaligned_check(regs->uregs[rd], eaddr);
433                                 eaddr += 4;
434                         }
435         }
436
437         if (LDST_W_BIT(instr))
438                 regs->uregs[rn] = newaddr;
439         if (!LDST_L_BIT(instr) || !(REGMASK_BITS(instr) & (1 << 15)))
440                 regs->ARM_pc -= correction;
441         return TYPE_DONE;
442
443 fault:
444         regs->ARM_pc -= correction;
445         return TYPE_FAULT;
446
447 bad:
448         printk(KERN_ERR "Alignment trap: not handling ldm with s-bit set\n");
449         return TYPE_ERROR;
450 }
451
452 static int
453 do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
454 {
455         union offset_union offset;
456         unsigned long instr, instrptr;
457         int (*handler)(unsigned long addr, unsigned long instr, struct pt_regs *regs);
458         unsigned int type;
459
460         instrptr = instruction_pointer(regs);
461         instr = *(unsigned long *)instrptr;
462
463         if (user_mode(regs))
464                 goto user;
465
466         ai_sys += 1;
467
468  fixup:
469
470         regs->ARM_pc += 4;
471
472         switch (CODING_BITS(instr)) {
473         case 0x00000000:        /* ldrh or strh */
474                 if (LDSTH_I_BIT(instr))
475                         offset.un = (instr & 0xf00) >> 4 | (instr & 15);
476                 else
477                         offset.un = regs->uregs[RM_BITS(instr)];
478                 handler = do_alignment_ldrhstrh;
479                 break;
480
481         case 0x04000000:        /* ldr or str immediate */
482                 offset.un = OFFSET_BITS(instr);
483                 handler = do_alignment_ldrstr;
484                 break;
485
486         case 0x06000000:        /* ldr or str register */
487                 offset.un = regs->uregs[RM_BITS(instr)];
488
489                 if (IS_SHIFT(instr)) {
490                         unsigned int shiftval = SHIFT_BITS(instr);
491
492                         switch(SHIFT_TYPE(instr)) {
493                         case SHIFT_LSL:
494                                 offset.un <<= shiftval;
495                                 break;
496
497                         case SHIFT_LSR:
498                                 offset.un >>= shiftval;
499                                 break;
500
501                         case SHIFT_ASR:
502                                 offset.sn >>= shiftval;
503                                 break;
504
505                         case SHIFT_RORRRX:
506                                 if (shiftval == 0) {
507                                         offset.un >>= 1;
508                                         if (regs->ARM_cpsr & PSR_C_BIT)
509                                                 offset.un |= 1 << 31;
510                                 } else
511                                         offset.un = offset.un >> shiftval |
512                                                           offset.un << (32 - shiftval);
513                                 break;
514                         }
515                 }
516                 handler = do_alignment_ldrstr;
517                 break;
518
519         case 0x08000000:        /* ldm or stm */
520                 handler = do_alignment_ldmstm;
521                 break;
522
523         default:
524                 goto bad;
525         }
526
527         type = handler(addr, instr, regs);
528
529         if (type == TYPE_ERROR || type == TYPE_FAULT)
530                 goto bad_or_fault;
531
532         if (type == TYPE_LDST)
533                 do_alignment_finish_ldst(addr, instr, regs, offset);
534
535         return 0;
536
537  bad_or_fault:
538         if (type == TYPE_ERROR)
539                 goto bad;
540         regs->ARM_pc -= 4;
541         /*
542          * We got a fault - fix it up, or die.
543          */
544         do_bad_area(current, current->mm, addr, fsr, regs);
545         return 0;
546
547  bad:
548         /*
549          * Oops, we didn't handle the instruction.
550          */
551         printk(KERN_ERR "Alignment trap: not handling instruction "
552                 "%08lx at [<%08lx>]\n", instr, instrptr);
553         ai_skipped += 1;
554         return 1;
555
556  user:
557         ai_user += 1;
558
559         if (ai_usermode & 1)
560                 printk("Alignment trap: %s (%d) PC=0x%08lx Instr=0x%08lx "
561                        "Address=0x%08lx FSR 0x%03x\n", current->comm,
562                         current->pid, instrptr, instr, addr, fsr);
563
564         if (ai_usermode & 2)
565                 goto fixup;
566
567         if (ai_usermode & 4)
568                 force_sig(SIGBUS, current);
569         else
570                 set_cr(cr_no_alignment);
571
572         return 0;
573 }
574
575 /*
576  * This needs to be done after sysctl_init, otherwise sys/ will be
577  * overwritten.  Actually, this shouldn't be in sys/ at all since
578  * it isn't a sysctl, and it doesn't contain sysctl information.
579  * We now locate it in /proc/cpu/alignment instead.
580  */
581 static int __init alignment_init(void)
582 {
583 #ifdef CONFIG_PROC_FS
584         struct proc_dir_entry *res;
585
586         res = proc_mkdir("cpu", NULL);
587         if (!res)
588                 return -ENOMEM;
589
590         res = create_proc_entry("alignment", S_IWUSR | S_IRUGO, res);
591         if (!res)
592                 return -ENOMEM;
593
594         res->read_proc = proc_alignment_read;
595         res->write_proc = proc_alignment_write;
596 #endif
597
598         hook_fault_code(1, do_alignment, SIGILL, "alignment exception");
599         hook_fault_code(3, do_alignment, SIGILL, "alignment exception");
600
601         return 0;
602 }
603
604 fs_initcall(alignment_init);