csum_partial_copy.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. /*
  2. * csum_partial_copy - do IP checksumming and copy
  3. *
  4. * (C) Copyright 1996 Linus Torvalds
  5. * accelerated versions (and 21264 assembly versions ) contributed by
  6. * Rick Gorton <rick.gorton@alpha-processor.com>
  7. *
  8. * Don't look at this too closely - you'll go mad. The things
  9. * we do for performance..
  10. */
  11. #include <linux/types.h>
  12. #include <linux/string.h>
  13. #include <asm/uaccess.h>
  14. #define ldq_u(x,y) \
  15. __asm__ __volatile__("ldq_u %0,%1":"=r" (x):"m" (*(const unsigned long *)(y)))
  16. #define stq_u(x,y) \
  17. __asm__ __volatile__("stq_u %1,%0":"=m" (*(unsigned long *)(y)):"r" (x))
  18. #define extql(x,y,z) \
  19. __asm__ __volatile__("extql %1,%2,%0":"=r" (z):"r" (x),"r" (y))
  20. #define extqh(x,y,z) \
  21. __asm__ __volatile__("extqh %1,%2,%0":"=r" (z):"r" (x),"r" (y))
  22. #define mskql(x,y,z) \
  23. __asm__ __volatile__("mskql %1,%2,%0":"=r" (z):"r" (x),"r" (y))
  24. #define mskqh(x,y,z) \
  25. __asm__ __volatile__("mskqh %1,%2,%0":"=r" (z):"r" (x),"r" (y))
  26. #define insql(x,y,z) \
  27. __asm__ __volatile__("insql %1,%2,%0":"=r" (z):"r" (x),"r" (y))
  28. #define insqh(x,y,z) \
  29. __asm__ __volatile__("insqh %1,%2,%0":"=r" (z):"r" (x),"r" (y))
  30. #define __get_user_u(x,ptr) \
  31. ({ \
  32. long __guu_err; \
  33. __asm__ __volatile__( \
  34. "1: ldq_u %0,%2\n" \
  35. "2:\n" \
  36. ".section __ex_table,\"a\"\n" \
  37. " .long 1b - .\n" \
  38. " lda %0,2b-1b(%1)\n" \
  39. ".previous" \
  40. : "=r"(x), "=r"(__guu_err) \
  41. : "m"(__m(ptr)), "1"(0)); \
  42. __guu_err; \
  43. })
  44. #define __put_user_u(x,ptr) \
  45. ({ \
  46. long __puu_err; \
  47. __asm__ __volatile__( \
  48. "1: stq_u %2,%1\n" \
  49. "2:\n" \
  50. ".section __ex_table,\"a\"\n" \
  51. " .long 1b - ." \
  52. " lda $31,2b-1b(%0)\n" \
  53. ".previous" \
  54. : "=r"(__puu_err) \
  55. : "m"(__m(addr)), "rJ"(x), "0"(0)); \
  56. __puu_err; \
  57. })
  58. static inline unsigned short from64to16(unsigned long x)
  59. {
  60. /* Using extract instructions is a bit more efficient
  61. than the original shift/bitmask version. */
  62. union {
  63. unsigned long ul;
  64. unsigned int ui[2];
  65. unsigned short us[4];
  66. } in_v, tmp_v, out_v;
  67. in_v.ul = x;
  68. tmp_v.ul = (unsigned long) in_v.ui[0] + (unsigned long) in_v.ui[1];
  69. /* Since the bits of tmp_v.sh[3] are going to always be zero,
  70. we don't have to bother to add that in. */
  71. out_v.ul = (unsigned long) tmp_v.us[0] + (unsigned long) tmp_v.us[1]
  72. + (unsigned long) tmp_v.us[2];
  73. /* Similarly, out_v.us[2] is always zero for the final add. */
  74. return out_v.us[0] + out_v.us[1];
  75. }
  76. /*
  77. * Ok. This isn't fun, but this is the EASY case.
  78. */
  79. static inline unsigned long
  80. csum_partial_cfu_aligned(const unsigned long __user *src, unsigned long *dst,
  81. long len, unsigned long checksum,
  82. int *errp)
  83. {
  84. unsigned long carry = 0;
  85. int err = 0;
  86. while (len >= 0) {
  87. unsigned long word;
  88. err |= __get_user(word, src);
  89. checksum += carry;
  90. src++;
  91. checksum += word;
  92. len -= 8;
  93. carry = checksum < word;
  94. *dst = word;
  95. dst++;
  96. }
  97. len += 8;
  98. checksum += carry;
  99. if (len) {
  100. unsigned long word, tmp;
  101. err |= __get_user(word, src);
  102. tmp = *dst;
  103. mskql(word, len, word);
  104. checksum += word;
  105. mskqh(tmp, len, tmp);
  106. carry = checksum < word;
  107. *dst = word | tmp;
  108. checksum += carry;
  109. }
  110. if (err && errp) *errp = err;
  111. return checksum;
  112. }
  113. /*
  114. * This is even less fun, but this is still reasonably
  115. * easy.
  116. */
  117. static inline unsigned long
  118. csum_partial_cfu_dest_aligned(const unsigned long __user *src,
  119. unsigned long *dst,
  120. unsigned long soff,
  121. long len, unsigned long checksum,
  122. int *errp)
  123. {
  124. unsigned long first;
  125. unsigned long word, carry;
  126. unsigned long lastsrc = 7+len+(unsigned long)src;
  127. int err = 0;
  128. err |= __get_user_u(first,src);
  129. carry = 0;
  130. while (len >= 0) {
  131. unsigned long second;
  132. err |= __get_user_u(second, src+1);
  133. extql(first, soff, word);
  134. len -= 8;
  135. src++;
  136. extqh(second, soff, first);
  137. checksum += carry;
  138. word |= first;
  139. first = second;
  140. checksum += word;
  141. *dst = word;
  142. dst++;
  143. carry = checksum < word;
  144. }
  145. len += 8;
  146. checksum += carry;
  147. if (len) {
  148. unsigned long tmp;
  149. unsigned long second;
  150. err |= __get_user_u(second, lastsrc);
  151. tmp = *dst;
  152. extql(first, soff, word);
  153. extqh(second, soff, first);
  154. word |= first;
  155. mskql(word, len, word);
  156. checksum += word;
  157. mskqh(tmp, len, tmp);
  158. carry = checksum < word;
  159. *dst = word | tmp;
  160. checksum += carry;
  161. }
  162. if (err && errp) *errp = err;
  163. return checksum;
  164. }
  165. /*
  166. * This is slightly less fun than the above..
  167. */
  168. static inline unsigned long
  169. csum_partial_cfu_src_aligned(const unsigned long __user *src,
  170. unsigned long *dst,
  171. unsigned long doff,
  172. long len, unsigned long checksum,
  173. unsigned long partial_dest,
  174. int *errp)
  175. {
  176. unsigned long carry = 0;
  177. unsigned long word;
  178. unsigned long second_dest;
  179. int err = 0;
  180. mskql(partial_dest, doff, partial_dest);
  181. while (len >= 0) {
  182. err |= __get_user(word, src);
  183. len -= 8;
  184. insql(word, doff, second_dest);
  185. checksum += carry;
  186. stq_u(partial_dest | second_dest, dst);
  187. src++;
  188. checksum += word;
  189. insqh(word, doff, partial_dest);
  190. carry = checksum < word;
  191. dst++;
  192. }
  193. len += 8;
  194. if (len) {
  195. checksum += carry;
  196. err |= __get_user(word, src);
  197. mskql(word, len, word);
  198. len -= 8;
  199. checksum += word;
  200. insql(word, doff, second_dest);
  201. len += doff;
  202. carry = checksum < word;
  203. partial_dest |= second_dest;
  204. if (len >= 0) {
  205. stq_u(partial_dest, dst);
  206. if (!len) goto out;
  207. dst++;
  208. insqh(word, doff, partial_dest);
  209. }
  210. doff = len;
  211. }
  212. ldq_u(second_dest, dst);
  213. mskqh(second_dest, doff, second_dest);
  214. stq_u(partial_dest | second_dest, dst);
  215. out:
  216. checksum += carry;
  217. if (err && errp) *errp = err;
  218. return checksum;
  219. }
  220. /*
  221. * This is so totally un-fun that it's frightening. Don't
  222. * look at this too closely, you'll go blind.
  223. */
  224. static inline unsigned long
  225. csum_partial_cfu_unaligned(const unsigned long __user * src,
  226. unsigned long * dst,
  227. unsigned long soff, unsigned long doff,
  228. long len, unsigned long checksum,
  229. unsigned long partial_dest,
  230. int *errp)
  231. {
  232. unsigned long carry = 0;
  233. unsigned long first;
  234. unsigned long lastsrc;
  235. int err = 0;
  236. err |= __get_user_u(first, src);
  237. lastsrc = 7+len+(unsigned long)src;
  238. mskql(partial_dest, doff, partial_dest);
  239. while (len >= 0) {
  240. unsigned long second, word;
  241. unsigned long second_dest;
  242. err |= __get_user_u(second, src+1);
  243. extql(first, soff, word);
  244. checksum += carry;
  245. len -= 8;
  246. extqh(second, soff, first);
  247. src++;
  248. word |= first;
  249. first = second;
  250. insql(word, doff, second_dest);
  251. checksum += word;
  252. stq_u(partial_dest | second_dest, dst);
  253. carry = checksum < word;
  254. insqh(word, doff, partial_dest);
  255. dst++;
  256. }
  257. len += doff;
  258. checksum += carry;
  259. if (len >= 0) {
  260. unsigned long second, word;
  261. unsigned long second_dest;
  262. err |= __get_user_u(second, lastsrc);
  263. extql(first, soff, word);
  264. extqh(second, soff, first);
  265. word |= first;
  266. first = second;
  267. mskql(word, len-doff, word);
  268. checksum += word;
  269. insql(word, doff, second_dest);
  270. carry = checksum < word;
  271. stq_u(partial_dest | second_dest, dst);
  272. if (len) {
  273. ldq_u(second_dest, dst+1);
  274. insqh(word, doff, partial_dest);
  275. mskqh(second_dest, len, second_dest);
  276. stq_u(partial_dest | second_dest, dst+1);
  277. }
  278. checksum += carry;
  279. } else {
  280. unsigned long second, word;
  281. unsigned long second_dest;
  282. err |= __get_user_u(second, lastsrc);
  283. extql(first, soff, word);
  284. extqh(second, soff, first);
  285. word |= first;
  286. ldq_u(second_dest, dst);
  287. mskql(word, len-doff, word);
  288. checksum += word;
  289. mskqh(second_dest, len, second_dest);
  290. carry = checksum < word;
  291. insql(word, doff, word);
  292. stq_u(partial_dest | word | second_dest, dst);
  293. checksum += carry;
  294. }
  295. if (err && errp) *errp = err;
  296. return checksum;
  297. }
  298. __wsum
  299. csum_partial_copy_from_user(const void __user *src, void *dst, int len,
  300. __wsum sum, int *errp)
  301. {
  302. unsigned long checksum = (__force u32) sum;
  303. unsigned long soff = 7 & (unsigned long) src;
  304. unsigned long doff = 7 & (unsigned long) dst;
  305. if (len) {
  306. if (!access_ok(VERIFY_READ, src, len)) {
  307. if (errp) *errp = -EFAULT;
  308. memset(dst, 0, len);
  309. return sum;
  310. }
  311. if (!doff) {
  312. if (!soff)
  313. checksum = csum_partial_cfu_aligned(
  314. (const unsigned long __user *) src,
  315. (unsigned long *) dst,
  316. len-8, checksum, errp);
  317. else
  318. checksum = csum_partial_cfu_dest_aligned(
  319. (const unsigned long __user *) src,
  320. (unsigned long *) dst,
  321. soff, len-8, checksum, errp);
  322. } else {
  323. unsigned long partial_dest;
  324. ldq_u(partial_dest, dst);
  325. if (!soff)
  326. checksum = csum_partial_cfu_src_aligned(
  327. (const unsigned long __user *) src,
  328. (unsigned long *) dst,
  329. doff, len-8, checksum,
  330. partial_dest, errp);
  331. else
  332. checksum = csum_partial_cfu_unaligned(
  333. (const unsigned long __user *) src,
  334. (unsigned long *) dst,
  335. soff, doff, len-8, checksum,
  336. partial_dest, errp);
  337. }
  338. checksum = from64to16 (checksum);
  339. }
  340. return (__force __wsum)checksum;
  341. }
  342. EXPORT_SYMBOL(csum_partial_copy_from_user);
  343. __wsum
  344. csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum)
  345. {
  346. __wsum checksum;
  347. mm_segment_t oldfs = get_fs();
  348. set_fs(KERNEL_DS);
  349. checksum = csum_partial_copy_from_user((__force const void __user *)src,
  350. dst, len, sum, NULL);
  351. set_fs(oldfs);
  352. return checksum;
  353. }
  354. EXPORT_SYMBOL(csum_partial_copy_nocheck);