2 * linux/arch/arm/mm/alignment.c
4 * Copyright (C) 1995 Linus Torvalds
5 * Modifications for ARM processor (c) 1995-2001 Russell King
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.
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>
20 #include <asm/uaccess.h>
21 #include <asm/unaligned.h>
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
30 * Speed optimisations and better fault handling by Russell King.
33 * This code is not portable to processors with late data abort handling.
35 #define CODING_BITS(i) (i & 0x0e000000)
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 */
43 #define LDST_P_EQ_U(i) ((((i) ^ ((i) >> 1)) & (1 << 23)) == 0)
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 */
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 */
52 #define REGMASK_BITS(i) (i & 0xffff)
53 #define OFFSET_BITS(i) (i & 0x0fff)
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
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;
72 static const char *usermode_action[] = {
82 proc_alignment_read(char *page, char **start, off_t off, int count, int *eof,
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]);
97 len = (p - page) - off;
101 *eof = (len <= count) ? 1 : 0;
107 static int proc_alignment_write(struct file *file, const char __user *buffer,
108 unsigned long count, void *data)
113 if (get_user(mode, buffer))
115 if (mode >= '0' && mode <= '5')
116 ai_usermode = mode - '0';
121 #endif /* CONFIG_PROC_FS */
133 #define __get8_unaligned_check(ins,val,addr,err) \
135 "1: "ins" %1, [%2], #1\n" \
137 " .section .fixup,\"ax\"\n" \
142 " .section __ex_table,\"a\"\n" \
146 : "=r" (err), "=&r" (val), "=r" (addr) \
147 : "0" (err), "2" (addr))
149 #define __get16_unaligned_check(ins,val,addr) \
151 unsigned int err = 0, v, a = addr; \
152 __get8_unaligned_check(ins,val,a,err); \
153 __get8_unaligned_check(ins,v,a,err); \
159 #define get16_unaligned_check(val,addr) \
160 __get16_unaligned_check("ldrb",val,addr)
162 #define get16t_unaligned_check(val,addr) \
163 __get16_unaligned_check("ldrbt",val,addr)
165 #define __get32_unaligned_check(ins,val,addr) \
167 unsigned int err = 0, v, a = addr; \
168 __get8_unaligned_check(ins,val,a,err); \
169 __get8_unaligned_check(ins,v,a,err); \
171 __get8_unaligned_check(ins,v,a,err); \
173 __get8_unaligned_check(ins,v,a,err); \
179 #define get32_unaligned_check(val,addr) \
180 __get32_unaligned_check("ldrb",val,addr)
182 #define get32t_unaligned_check(val,addr) \
183 __get32_unaligned_check("ldrbt",val,addr)
185 #define __put16_unaligned_check(ins,val,addr) \
187 unsigned int err = 0, v = val, a = addr; \
189 "1: "ins" %1, [%2], #1\n" \
190 " mov %1, %1, lsr #8\n" \
191 "2: "ins" %1, [%2]\n" \
193 " .section .fixup,\"ax\"\n" \
198 " .section __ex_table,\"a\"\n" \
203 : "=r" (err), "=&r" (v), "=&r" (a) \
204 : "0" (err), "1" (v), "2" (a)); \
209 #define put16_unaligned_check(val,addr) \
210 __put16_unaligned_check("strb",val,addr)
212 #define put16t_unaligned_check(val,addr) \
213 __put16_unaligned_check("strbt",val,addr)
215 #define __put32_unaligned_check(ins,val,addr) \
217 unsigned int err = 0, v = val, a = addr; \
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" \
227 " .section .fixup,\"ax\"\n" \
232 " .section __ex_table,\"a\"\n" \
239 : "=r" (err), "=&r" (v), "=&r" (a) \
240 : "0" (err), "1" (v), "2" (a)); \
245 #define put32_unaligned_check(val,addr) \
246 __put32_unaligned_check("strb", val, addr)
248 #define put32t_unaligned_check(val,addr) \
249 __put32_unaligned_check("strbt", val, addr)
252 do_alignment_finish_ldst(unsigned long addr, unsigned long instr, struct pt_regs *regs, union offset_union offset)
254 if (!LDST_U_BIT(instr))
255 offset.un = -offset.un;
257 if (!LDST_P_BIT(instr))
260 if (!LDST_P_BIT(instr) || LDST_W_BIT(instr))
261 regs->uregs[RN_BITS(instr)] = addr;
265 do_alignment_ldrhstrh(unsigned long addr, unsigned long instr, struct pt_regs *regs)
267 unsigned int rd = RD_BITS(instr);
269 if ((instr & 0x01f00ff0) == 0x01000090)
272 if ((instr & 0x90) != 0x90 || (instr & 0x60) == 0)
280 if (LDST_L_BIT(instr)) {
282 get16_unaligned_check(val, addr);
284 /* signed half-word? */
286 val = (signed long)((signed short) val);
288 regs->uregs[rd] = val;
290 put16_unaligned_check(regs->uregs[rd], addr);
295 if (LDST_L_BIT(instr)) {
297 get16t_unaligned_check(val, addr);
299 /* signed half-word? */
301 val = (signed long)((signed short) val);
303 regs->uregs[rd] = val;
305 put16t_unaligned_check(regs->uregs[rd], addr);
310 printk(KERN_ERR "Alignment trap: not handling swp instruction\n");
319 do_alignment_ldrstr(unsigned long addr, unsigned long instr, struct pt_regs *regs)
321 unsigned int rd = RD_BITS(instr);
325 if ((!LDST_P_BIT(instr) && LDST_W_BIT(instr)) || user_mode(regs))
328 if (LDST_L_BIT(instr)) {
330 get32_unaligned_check(val, addr);
331 regs->uregs[rd] = val;
333 put32_unaligned_check(regs->uregs[rd], addr);
337 if (LDST_L_BIT(instr)) {
339 get32t_unaligned_check(val, addr);
340 regs->uregs[rd] = val;
342 put32t_unaligned_check(regs->uregs[rd], addr);
350 * LDM/STM alignment handler.
352 * There are 4 variants of this instruction:
354 * B = rn pointer before instruction, A = rn pointer after instruction
355 * ------ increasing address ----->
356 * | | r0 | r1 | ... | rx | |
363 do_alignment_ldmstm(unsigned long addr, unsigned long instr, struct pt_regs *regs)
365 unsigned int rd, rn, correction, nr_regs, regbits;
366 unsigned long eaddr, newaddr;
368 if (LDM_S_BIT(instr))
371 correction = 4; /* processor implementation defined */
372 regs->ARM_pc += correction;
376 /* count the number of registers in the mask to be transferred */
377 nr_regs = hweight16(REGMASK_BITS(instr)) * 4;
380 newaddr = eaddr = regs->uregs[rn];
382 if (!LDST_U_BIT(instr))
385 if (!LDST_U_BIT(instr))
388 if (LDST_P_EQ_U(instr)) /* U = P */
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!
398 #if !(defined CONFIG_CPU_ARM922T) && !(defined CONFIG_CPU_ARM920T)
400 * This is a "hint" - we already have eaddr worked out by the
404 printk(KERN_ERR "LDMSTM: PC = %08lx, instr = %08lx, "
405 "addr = %08lx, eaddr = %08lx\n",
406 instruction_pointer(regs), instr, addr, eaddr);
411 if (user_mode(regs)) {
412 for (regbits = REGMASK_BITS(instr), rd = 0; regbits;
413 regbits >>= 1, rd += 1)
415 if (LDST_L_BIT(instr)) {
417 get32t_unaligned_check(val, eaddr);
418 regs->uregs[rd] = val;
420 put32t_unaligned_check(regs->uregs[rd], eaddr);
424 for (regbits = REGMASK_BITS(instr), rd = 0; regbits;
425 regbits >>= 1, rd += 1)
427 if (LDST_L_BIT(instr)) {
429 get32_unaligned_check(val, eaddr);
430 regs->uregs[rd] = val;
432 put32_unaligned_check(regs->uregs[rd], eaddr);
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;
444 regs->ARM_pc -= correction;
448 printk(KERN_ERR "Alignment trap: not handling ldm with s-bit set\n");
453 do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
455 union offset_union offset;
456 unsigned long instr, instrptr;
457 int (*handler)(unsigned long addr, unsigned long instr, struct pt_regs *regs);
460 instrptr = instruction_pointer(regs);
461 instr = *(unsigned long *)instrptr;
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);
477 offset.un = regs->uregs[RM_BITS(instr)];
478 handler = do_alignment_ldrhstrh;
481 case 0x04000000: /* ldr or str immediate */
482 offset.un = OFFSET_BITS(instr);
483 handler = do_alignment_ldrstr;
486 case 0x06000000: /* ldr or str register */
487 offset.un = regs->uregs[RM_BITS(instr)];
489 if (IS_SHIFT(instr)) {
490 unsigned int shiftval = SHIFT_BITS(instr);
492 switch(SHIFT_TYPE(instr)) {
494 offset.un <<= shiftval;
498 offset.un >>= shiftval;
502 offset.sn >>= shiftval;
508 if (regs->ARM_cpsr & PSR_C_BIT)
509 offset.un |= 1 << 31;
511 offset.un = offset.un >> shiftval |
512 offset.un << (32 - shiftval);
516 handler = do_alignment_ldrstr;
519 case 0x08000000: /* ldm or stm */
520 handler = do_alignment_ldmstm;
527 type = handler(addr, instr, regs);
529 if (type == TYPE_ERROR || type == TYPE_FAULT)
532 if (type == TYPE_LDST)
533 do_alignment_finish_ldst(addr, instr, regs, offset);
538 if (type == TYPE_ERROR)
542 * We got a fault - fix it up, or die.
544 do_bad_area(current, current->mm, addr, fsr, regs);
549 * Oops, we didn't handle the instruction.
551 printk(KERN_ERR "Alignment trap: not handling instruction "
552 "%08lx at [<%08lx>]\n", instr, instrptr);
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);
568 force_sig(SIGBUS, current);
570 set_cr(cr_no_alignment);
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.
581 static int __init alignment_init(void)
583 #ifdef CONFIG_PROC_FS
584 struct proc_dir_entry *res;
586 res = proc_mkdir("cpu", NULL);
590 res = create_proc_entry("alignment", S_IWUSR | S_IRUGO, res);
594 res->read_proc = proc_alignment_read;
595 res->write_proc = proc_alignment_write;
598 hook_fault_code(1, do_alignment, SIGILL, "alignment exception");
599 hook_fault_code(3, do_alignment, SIGILL, "alignment exception");
604 fs_initcall(alignment_init);