csum_partial_copy.c 8.9 KB

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