align.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:10k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

  1. /* align.c - handle alignment exceptions for the Power PC.
  2.  *
  3.  * Copyright (c) 1996 Paul Mackerras <paulus@cs.anu.edu.au>
  4.  * Copyright (c) 1998-1999 TiVo, Inc.
  5.  *   PowerPC 403GCX modifications.
  6.  * Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu>
  7.  *   PowerPC 403GCX/405GP modifications.
  8.  * Copyright (c) 2001-2002 PPC64 team, IBM Corp
  9.  *   64-bit and Power4 support
  10.  *
  11.  * This program is free software; you can redistribute it and/or
  12.  * modify it under the terms of the GNU General Public License
  13.  * as published by the Free Software Foundation; either version
  14.  * 2 of the License, or (at your option) any later version.
  15.  */
  16. #include <linux/kernel.h>
  17. #include <linux/mm.h>
  18. #include <asm/ptrace.h>
  19. #include <asm/processor.h>
  20. #include <asm/uaccess.h>
  21. #include <asm/system.h>
  22. #include <asm/cache.h>
  23. struct aligninfo {
  24. unsigned char len;
  25. unsigned char flags;
  26. };
  27. #define IS_XFORM(inst) (((inst) >> 26) == 31)
  28. #define IS_DSFORM(inst) (((inst) >> 26) >= 56)
  29. #define INVALID { 0, 0 }
  30. #define LD 1 /* load */
  31. #define ST 2 /* store */
  32. #define SE 4 /* sign-extend value */
  33. #define F 8 /* to/from fp regs */
  34. #define U 0x10 /* update index register */
  35. #define M 0x20 /* multiple load/store */
  36. #define SW 0x40 /* byte swap */
  37. #define DCBZ 0x5f /* 8xx/82xx dcbz faults when cache not enabled */
  38. /*
  39.  * The PowerPC stores certain bits of the instruction that caused the
  40.  * alignment exception in the DSISR register.  This array maps those
  41.  * bits to information about the operand length and what the
  42.  * instruction would do.
  43.  */
  44. static struct aligninfo aligninfo[128] = {
  45. { 4, LD }, /* 00 0 0000: lwz / lwarx */
  46. INVALID, /* 00 0 0001 */
  47. { 4, ST }, /* 00 0 0010: stw */
  48. INVALID, /* 00 0 0011 */
  49. { 2, LD }, /* 00 0 0100: lhz */
  50. { 2, LD+SE }, /* 00 0 0101: lha */
  51. { 2, ST }, /* 00 0 0110: sth */
  52. { 4, LD+M }, /* 00 0 0111: lmw */
  53. { 4, LD+F }, /* 00 0 1000: lfs */
  54. { 8, LD+F }, /* 00 0 1001: lfd */
  55. { 4, ST+F }, /* 00 0 1010: stfs */
  56. { 8, ST+F }, /* 00 0 1011: stfd */
  57. INVALID, /* 00 0 1100 */
  58. { 8, LD }, /* 00 0 1101: ld */
  59. INVALID, /* 00 0 1110 */
  60. { 8, ST }, /* 00 0 1111: std */
  61. { 4, LD+U }, /* 00 1 0000: lwzu */
  62. INVALID, /* 00 1 0001 */
  63. { 4, ST+U }, /* 00 1 0010: stwu */
  64. INVALID, /* 00 1 0011 */
  65. { 2, LD+U }, /* 00 1 0100: lhzu */
  66. { 2, LD+SE+U }, /* 00 1 0101: lhau */
  67. { 2, ST+U }, /* 00 1 0110: sthu */
  68. { 4, ST+M }, /* 00 1 0111: stmw */
  69. { 4, LD+F+U }, /* 00 1 1000: lfsu */
  70. { 8, LD+F+U }, /* 00 1 1001: lfdu */
  71. { 4, ST+F+U }, /* 00 1 1010: stfsu */
  72. { 8, ST+F+U }, /* 00 1 1011: stfdu */
  73. INVALID, /* 00 1 1100 */
  74. INVALID, /* 00 1 1101 */
  75. INVALID, /* 00 1 1110 */
  76. INVALID, /* 00 1 1111 */
  77. { 8, LD }, /* 01 0 0000: ldx */
  78. INVALID, /* 01 0 0001 */
  79. { 8, ST }, /* 01 0 0010: stdx */
  80. INVALID, /* 01 0 0011 */
  81. INVALID, /* 01 0 0100 */
  82. { 4, LD+SE }, /* 01 0 0101: lwax */
  83. INVALID, /* 01 0 0110 */
  84. INVALID, /* 01 0 0111 */
  85. { 0, LD }, /* 01 0 1000: lswx */
  86. { 0, LD }, /* 01 0 1001: lswi */
  87. { 0, ST }, /* 01 0 1010: stswx */
  88. { 0, ST }, /* 01 0 1011: stswi */
  89. INVALID, /* 01 0 1100 */
  90. { 8, LD+U }, /* 01 0 1101: ldu */
  91. INVALID, /* 01 0 1110 */
  92. { 8, ST+U }, /* 01 0 1111: stdu */
  93. { 8, LD+U }, /* 01 1 0000: ldux */
  94. INVALID, /* 01 1 0001 */
  95. { 8, ST+U }, /* 01 1 0010: stdux */
  96. INVALID, /* 01 1 0011 */
  97. INVALID, /* 01 1 0100 */
  98. { 4, LD+SE+U }, /* 01 1 0101: lwaux */
  99. INVALID, /* 01 1 0110 */
  100. INVALID, /* 01 1 0111 */
  101. INVALID, /* 01 1 1000 */
  102. INVALID, /* 01 1 1001 */
  103. INVALID, /* 01 1 1010 */
  104. INVALID, /* 01 1 1011 */
  105. INVALID, /* 01 1 1100 */
  106. INVALID, /* 01 1 1101 */
  107. INVALID, /* 01 1 1110 */
  108. INVALID, /* 01 1 1111 */
  109. INVALID, /* 10 0 0000 */
  110. INVALID, /* 10 0 0001 */
  111. { 0, ST }, /* 10 0 0010: stwcx. */
  112. INVALID, /* 10 0 0011 */
  113. INVALID, /* 10 0 0100 */
  114. INVALID, /* 10 0 0101 */
  115. INVALID, /* 10 0 0110 */
  116. INVALID, /* 10 0 0111 */
  117. { 4, LD+SW }, /* 10 0 1000: lwbrx */
  118. INVALID, /* 10 0 1001 */
  119. { 4, ST+SW }, /* 10 0 1010: stwbrx */
  120. INVALID, /* 10 0 1011 */
  121. { 2, LD+SW }, /* 10 0 1100: lhbrx */
  122. { 4, LD+SE }, /* 10 0 1101  lwa */
  123. { 2, ST+SW }, /* 10 0 1110: sthbrx */
  124. INVALID, /* 10 0 1111 */
  125. INVALID, /* 10 1 0000 */
  126. INVALID, /* 10 1 0001 */
  127. INVALID, /* 10 1 0010 */
  128. INVALID, /* 10 1 0011 */
  129. INVALID, /* 10 1 0100 */
  130. INVALID, /* 10 1 0101 */
  131. INVALID, /* 10 1 0110 */
  132. INVALID, /* 10 1 0111 */
  133. INVALID, /* 10 1 1000 */
  134. INVALID, /* 10 1 1001 */
  135. INVALID, /* 10 1 1010 */
  136. INVALID, /* 10 1 1011 */
  137. INVALID, /* 10 1 1100 */
  138. INVALID, /* 10 1 1101 */
  139. INVALID, /* 10 1 1110 */
  140. { L1_CACHE_BYTES, ST }, /* 10 1 1111: dcbz */
  141. { 4, LD }, /* 11 0 0000: lwzx */
  142. INVALID, /* 11 0 0001 */
  143. { 4, ST }, /* 11 0 0010: stwx */
  144. INVALID, /* 11 0 0011 */
  145. { 2, LD }, /* 11 0 0100: lhzx */
  146. { 2, LD+SE }, /* 11 0 0101: lhax */
  147. { 2, ST }, /* 11 0 0110: sthx */
  148. INVALID, /* 11 0 0111 */
  149. { 4, LD+F }, /* 11 0 1000: lfsx */
  150. { 8, LD+F }, /* 11 0 1001: lfdx */
  151. { 4, ST+F }, /* 11 0 1010: stfsx */
  152. { 8, ST+F }, /* 11 0 1011: stfdx */
  153. INVALID, /* 11 0 1100 */
  154. { 8, LD+M }, /* 11 0 1101: lmd */
  155. INVALID, /* 11 0 1110 */
  156. { 8, ST+M }, /* 11 0 1111: stmd */
  157. { 4, LD+U }, /* 11 1 0000: lwzux */
  158. INVALID, /* 11 1 0001 */
  159. { 4, ST+U }, /* 11 1 0010: stwux */
  160. INVALID, /* 11 1 0011 */
  161. { 2, LD+U }, /* 11 1 0100: lhzux */
  162. { 2, LD+SE+U }, /* 11 1 0101: lhaux */
  163. { 2, ST+U }, /* 11 1 0110: sthux */
  164. INVALID, /* 11 1 0111 */
  165. { 4, LD+F+U }, /* 11 1 1000: lfsux */
  166. { 8, LD+F+U }, /* 11 1 1001: lfdux */
  167. { 4, ST+F+U }, /* 11 1 1010: stfsux */
  168. { 8, ST+F+U }, /* 11 1 1011: stfdux */
  169. INVALID, /* 11 1 1100 */
  170. INVALID, /* 11 1 1101 */
  171. INVALID, /* 11 1 1110 */
  172. INVALID, /* 11 1 1111 */
  173. };
  174. #define SWAP(a, b) (t = (a), (a) = (b), (b) = t)
  175. unsigned static inline make_dsisr( unsigned instr )
  176. {
  177. unsigned dsisr;
  178. /* create a DSISR value from the instruction */
  179. dsisr = (instr & 0x03ff0000) >> 16; /* bits  6:15 --> 22:31 */
  180. if ( IS_XFORM(instr) ) {
  181. dsisr |= (instr & 0x00000006) << 14; /* bits 29:30 --> 15:16 */
  182. dsisr |= (instr & 0x00000040) << 8; /* bit     25 -->    17 */
  183. dsisr |= (instr & 0x00000780) << 3; /* bits 21:24 --> 18:21 */
  184. }
  185. else {
  186. dsisr |= (instr & 0x04000000) >> 12; /* bit      5 -->    17 */
  187. dsisr |= (instr & 0x78000000) >> 17; /* bits  1: 4 --> 18:21 */
  188. if ( IS_DSFORM(instr) ) {
  189. dsisr |= (instr & 0x00000003) << 18; /* bits 30:31 --> 12:13 */
  190. }
  191. }
  192. return dsisr;
  193. }
  194. int
  195. fix_alignment(struct pt_regs *regs)
  196. {
  197. unsigned int instr, nb, flags;
  198. int t;
  199. unsigned long reg, areg;
  200. unsigned long i;
  201. int ret;
  202. unsigned dsisr;
  203. unsigned char *addr, *p;
  204. unsigned long *lp;
  205. union {
  206. long ll;
  207. double dd;
  208. unsigned char v[8];
  209. struct {
  210. unsigned hi32;
  211. int  low32;
  212. } x32;
  213. struct {
  214. unsigned char hi48[6];
  215. short       low16;
  216. } x16;
  217. } data;
  218. /*
  219.  * Return 1 on success
  220.  * Return 0 if unable to handle the interrupt
  221.  * Return -EFAULT if data address is bad
  222.  */
  223. dsisr = regs->dsisr;
  224. /* Power4 doesn't set DSISR for an alignment interrupt */
  225. if (__is_processor(PV_POWER4) || __is_processor(PV_POWER4p))
  226. dsisr = make_dsisr( *((unsigned *)regs->nip) );
  227. /* extract the operation and registers from the dsisr */
  228. reg = (dsisr >> 5) & 0x1f; /* source/dest register */
  229. areg = dsisr & 0x1f; /* register to update */
  230. instr = (dsisr >> 10) & 0x7f;
  231. instr |= (dsisr >> 13) & 0x60;
  232. /* Lookup the operation in our table */
  233. nb = aligninfo[instr].len;
  234. flags = aligninfo[instr].flags;
  235. /* DAR has the operand effective address */
  236. addr = (unsigned char *)regs->dar;
  237. /* A size of 0 indicates an instruction we don't support */
  238. /* we also don't support the multiples (lmw, stmw, lmd, stmd) */
  239. if ((nb == 0) || (flags & M))
  240. return 0; /* too hard or invalid instruction */
  241. /*
  242.  * Special handling for dcbz
  243.  * dcbz may give an alignment exception for accesses to caching inhibited
  244.  * storage
  245.  */
  246. if (instr == DCBZ)
  247. addr = (unsigned char *) ((unsigned long)addr & -L1_CACHE_BYTES);
  248. /* Verify the address of the operand */
  249. if (user_mode(regs)) {
  250. if (verify_area((flags & ST? VERIFY_WRITE: VERIFY_READ), addr, nb))
  251. return -EFAULT; /* bad address */
  252. }
  253. /* Force the fprs into the save area so we can reference them */
  254. if ((flags & F) && (regs->msr & MSR_FP))
  255. giveup_fpu(current);
  256. /* If we are loading, get the data from user space */
  257. if (flags & LD) {
  258. data.ll = 0;
  259. ret = 0;
  260. p = addr;
  261. switch (nb) {
  262. case 8:
  263. ret |= __get_user(data.v[0], p++);
  264. ret |= __get_user(data.v[1], p++);
  265. ret |= __get_user(data.v[2], p++);
  266. ret |= __get_user(data.v[3], p++);
  267. case 4:
  268. ret |= __get_user(data.v[4], p++);
  269. ret |= __get_user(data.v[5], p++);
  270. case 2:
  271. ret |= __get_user(data.v[6], p++);
  272. ret |= __get_user(data.v[7], p++);
  273. if (ret)
  274. return -EFAULT;
  275. }
  276. }
  277. /* If we are storing, get the data from the saved gpr or fpr */
  278. if (flags & ST) {
  279. if (flags & F) {
  280. if (nb == 4) {
  281. /* Doing stfs, have to convert to single */
  282. enable_kernel_fp();
  283. cvt_df(&current->thread.fpr[reg], (float *)&data.v[4], &current->thread.fpscr);
  284. }
  285. else
  286. data.dd = current->thread.fpr[reg];
  287. }
  288. else 
  289. data.ll = regs->gpr[reg];
  290. }
  291. /* Swap bytes as needed */
  292. if (flags & SW) {
  293. if (nb == 2)
  294. SWAP(data.v[6], data.v[7]);
  295. else { /* nb must be 4 */
  296. SWAP(data.v[4], data.v[7]);
  297. SWAP(data.v[5], data.v[6]);
  298. }
  299. }
  300. /* Sign extend as needed */
  301. if (flags & SE) {
  302. if ( nb == 2 )
  303. data.ll = data.x16.low16;
  304. else /* nb must be 4 */
  305. data.ll = data.x32.low32;
  306. }
  307. /* If we are loading, move the data to the gpr or fpr */
  308. if (flags & LD) {
  309. if (flags & F) {
  310. if (nb == 4) {
  311. /* Doing lfs, have to convert to double */
  312. enable_kernel_fp();
  313. cvt_fd((float *)&data.v[4], &current->thread.fpr[reg], &current->thread.fpscr);
  314. }
  315. else
  316. current->thread.fpr[reg] = data.dd;
  317. }
  318. else
  319. regs->gpr[reg] = data.ll;
  320. }
  321. /* If we are storing, copy the data to the user */
  322. if (flags & ST) {
  323. ret = 0;
  324. p = addr;
  325. switch (nb) {
  326. case 128: /* Special case - must be dcbz */
  327. lp = (unsigned long *)p;
  328. for (i = 0; i < L1_CACHE_BYTES / sizeof(long); ++i)
  329. ret |= __put_user(0, lp++);
  330. break;
  331. case 8:
  332. ret |= __put_user(data.v[0], p++);
  333. ret |= __put_user(data.v[1], p++);
  334. ret |= __put_user(data.v[2], p++);
  335. ret |= __put_user(data.v[3], p++);
  336. case 4:
  337. ret |= __put_user(data.v[4], p++);
  338. ret |= __put_user(data.v[5], p++);
  339. case 2:
  340. ret |= __put_user(data.v[6], p++);
  341. ret |= __put_user(data.v[7], p++);
  342. }
  343. if (ret)
  344. return -EFAULT;
  345. }
  346. /* Update RA as needed */
  347. if (flags & U) {
  348. regs->gpr[areg] = regs->dar;
  349. }
  350. return 1;
  351. }