alignment.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:14k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

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