checksum.h 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. #ifndef _S390_CHECKSUM_H
  2. #define _S390_CHECKSUM_H
  3. /*
  4. * include/asm-s390/checksum.h
  5. * S390 fast network checksum routines
  6. * see also arch/S390/lib/checksum.c
  7. *
  8. * S390 version
  9. * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
  10. * Author(s): Ulrich Hild (first version)
  11. * Martin Schwidefsky (heavily optimized CKSM version)
  12. * D.J. Barrow (third attempt)
  13. */
  14. #include <asm/uaccess.h>
  15. /*
  16. * computes the checksum of a memory block at buff, length len,
  17. * and adds in "sum" (32-bit)
  18. *
  19. * returns a 32-bit number suitable for feeding into itself
  20. * or csum_tcpudp_magic
  21. *
  22. * this function must be called with even lengths, except
  23. * for the last fragment, which may be odd
  24. *
  25. * it's best to have buff aligned on a 32-bit boundary
  26. */
  27. static inline unsigned int
  28. csum_partial(const unsigned char * buff, int len, unsigned int sum)
  29. {
  30. /*
  31. * Experiments with ethernet and slip connections show that buf
  32. * is aligned on either a 2-byte or 4-byte boundary.
  33. */
  34. #ifndef __s390x__
  35. register_pair rp;
  36. rp.subreg.even = (unsigned long) buff;
  37. rp.subreg.odd = (unsigned long) len;
  38. __asm__ __volatile__ (
  39. "0: cksm %0,%1\n" /* do checksum on longs */
  40. " jo 0b\n"
  41. : "+&d" (sum), "+&a" (rp) : : "cc", "memory" );
  42. #else /* __s390x__ */
  43. __asm__ __volatile__ (
  44. " lgr 2,%1\n" /* address in gpr 2 */
  45. " lgfr 3,%2\n" /* length in gpr 3 */
  46. "0: cksm %0,2\n" /* do checksum on longs */
  47. " jo 0b\n"
  48. : "+&d" (sum)
  49. : "d" (buff), "d" (len)
  50. : "cc", "memory", "2", "3" );
  51. #endif /* __s390x__ */
  52. return sum;
  53. }
  54. /*
  55. * csum_partial as an inline function
  56. */
  57. static inline unsigned int
  58. csum_partial_inline(const unsigned char * buff, int len, unsigned int sum)
  59. {
  60. #ifndef __s390x__
  61. register_pair rp;
  62. rp.subreg.even = (unsigned long) buff;
  63. rp.subreg.odd = (unsigned long) len;
  64. __asm__ __volatile__ (
  65. "0: cksm %0,%1\n" /* do checksum on longs */
  66. " jo 0b\n"
  67. : "+&d" (sum), "+&a" (rp) : : "cc", "memory" );
  68. #else /* __s390x__ */
  69. __asm__ __volatile__ (
  70. " lgr 2,%1\n" /* address in gpr 2 */
  71. " lgfr 3,%2\n" /* length in gpr 3 */
  72. "0: cksm %0,2\n" /* do checksum on longs */
  73. " jo 0b\n"
  74. : "+&d" (sum)
  75. : "d" (buff), "d" (len)
  76. : "cc", "memory", "2", "3" );
  77. #endif /* __s390x__ */
  78. return sum;
  79. }
  80. /*
  81. * the same as csum_partial_copy, but copies from user space.
  82. *
  83. * here even more important to align src and dst on a 32-bit (or even
  84. * better 64-bit) boundary
  85. *
  86. * Copy from userspace and compute checksum. If we catch an exception
  87. * then zero the rest of the buffer.
  88. */
  89. static inline unsigned int
  90. csum_partial_copy_from_user(const char __user *src, char *dst,
  91. int len, unsigned int sum,
  92. int *err_ptr)
  93. {
  94. int missing;
  95. missing = copy_from_user(dst, src, len);
  96. if (missing) {
  97. memset(dst + len - missing, 0, missing);
  98. *err_ptr = -EFAULT;
  99. }
  100. return csum_partial(dst, len, sum);
  101. }
  102. static inline unsigned int
  103. csum_partial_copy_nocheck (const char *src, char *dst, int len, unsigned int sum)
  104. {
  105. memcpy(dst,src,len);
  106. return csum_partial_inline(dst, len, sum);
  107. }
  108. /*
  109. * Fold a partial checksum without adding pseudo headers
  110. */
  111. static inline unsigned short
  112. csum_fold(unsigned int sum)
  113. {
  114. #ifndef __s390x__
  115. register_pair rp;
  116. __asm__ __volatile__ (
  117. " slr %N1,%N1\n" /* %0 = H L */
  118. " lr %1,%0\n" /* %0 = H L, %1 = H L 0 0 */
  119. " srdl %1,16\n" /* %0 = H L, %1 = 0 H L 0 */
  120. " alr %1,%N1\n" /* %0 = H L, %1 = L H L 0 */
  121. " alr %0,%1\n" /* %0 = H+L+C L+H */
  122. " srl %0,16\n" /* %0 = H+L+C */
  123. : "+&d" (sum), "=d" (rp) : : "cc" );
  124. #else /* __s390x__ */
  125. __asm__ __volatile__ (
  126. " sr 3,3\n" /* %0 = H*65536 + L */
  127. " lr 2,%0\n" /* %0 = H L, R2/R3 = H L / 0 0 */
  128. " srdl 2,16\n" /* %0 = H L, R2/R3 = 0 H / L 0 */
  129. " alr 2,3\n" /* %0 = H L, R2/R3 = L H / L 0 */
  130. " alr %0,2\n" /* %0 = H+L+C L+H */
  131. " srl %0,16\n" /* %0 = H+L+C */
  132. : "+&d" (sum) : : "cc", "2", "3");
  133. #endif /* __s390x__ */
  134. return ((unsigned short) ~sum);
  135. }
  136. /*
  137. * This is a version of ip_compute_csum() optimized for IP headers,
  138. * which always checksum on 4 octet boundaries.
  139. *
  140. */
  141. static inline unsigned short
  142. ip_fast_csum(unsigned char *iph, unsigned int ihl)
  143. {
  144. unsigned long sum;
  145. #ifndef __s390x__
  146. register_pair rp;
  147. rp.subreg.even = (unsigned long) iph;
  148. rp.subreg.odd = (unsigned long) ihl*4;
  149. __asm__ __volatile__ (
  150. " sr %0,%0\n" /* set sum to zero */
  151. "0: cksm %0,%1\n" /* do checksum on longs */
  152. " jo 0b\n"
  153. : "=&d" (sum), "+&a" (rp) : : "cc", "memory" );
  154. #else /* __s390x__ */
  155. __asm__ __volatile__ (
  156. " slgr %0,%0\n" /* set sum to zero */
  157. " lgr 2,%1\n" /* address in gpr 2 */
  158. " lgfr 3,%2\n" /* length in gpr 3 */
  159. "0: cksm %0,2\n" /* do checksum on ints */
  160. " jo 0b\n"
  161. : "=&d" (sum)
  162. : "d" (iph), "d" (ihl*4)
  163. : "cc", "memory", "2", "3" );
  164. #endif /* __s390x__ */
  165. return csum_fold(sum);
  166. }
  167. /*
  168. * computes the checksum of the TCP/UDP pseudo-header
  169. * returns a 32-bit checksum
  170. */
  171. static inline unsigned int
  172. csum_tcpudp_nofold(unsigned long saddr, unsigned long daddr,
  173. unsigned short len, unsigned short proto,
  174. unsigned int sum)
  175. {
  176. #ifndef __s390x__
  177. __asm__ __volatile__ (
  178. " alr %0,%1\n" /* sum += saddr */
  179. " brc 12,0f\n"
  180. " ahi %0,1\n" /* add carry */
  181. "0:"
  182. : "+&d" (sum) : "d" (saddr) : "cc" );
  183. __asm__ __volatile__ (
  184. " alr %0,%1\n" /* sum += daddr */
  185. " brc 12,1f\n"
  186. " ahi %0,1\n" /* add carry */
  187. "1:"
  188. : "+&d" (sum) : "d" (daddr) : "cc" );
  189. __asm__ __volatile__ (
  190. " alr %0,%1\n" /* sum += (len<<16) + (proto<<8) */
  191. " brc 12,2f\n"
  192. " ahi %0,1\n" /* add carry */
  193. "2:"
  194. : "+&d" (sum)
  195. : "d" (((unsigned int) len<<16) + (unsigned int) proto)
  196. : "cc" );
  197. #else /* __s390x__ */
  198. __asm__ __volatile__ (
  199. " lgfr %0,%0\n"
  200. " algr %0,%1\n" /* sum += saddr */
  201. " brc 12,0f\n"
  202. " aghi %0,1\n" /* add carry */
  203. "0: algr %0,%2\n" /* sum += daddr */
  204. " brc 12,1f\n"
  205. " aghi %0,1\n" /* add carry */
  206. "1: algfr %0,%3\n" /* sum += (len<<16) + proto */
  207. " brc 12,2f\n"
  208. " aghi %0,1\n" /* add carry */
  209. "2: srlg 0,%0,32\n"
  210. " alr %0,0\n" /* fold to 32 bits */
  211. " brc 12,3f\n"
  212. " ahi %0,1\n" /* add carry */
  213. "3: llgfr %0,%0"
  214. : "+&d" (sum)
  215. : "d" (saddr), "d" (daddr),
  216. "d" (((unsigned int) len<<16) + (unsigned int) proto)
  217. : "cc", "0" );
  218. #endif /* __s390x__ */
  219. return sum;
  220. }
  221. /*
  222. * computes the checksum of the TCP/UDP pseudo-header
  223. * returns a 16-bit checksum, already complemented
  224. */
  225. static inline unsigned short int
  226. csum_tcpudp_magic(unsigned long saddr, unsigned long daddr,
  227. unsigned short len, unsigned short proto,
  228. unsigned int sum)
  229. {
  230. return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
  231. }
  232. /*
  233. * this routine is used for miscellaneous IP-like checksums, mainly
  234. * in icmp.c
  235. */
  236. static inline unsigned short
  237. ip_compute_csum(unsigned char * buff, int len)
  238. {
  239. return csum_fold(csum_partial(buff, len, 0));
  240. }
  241. #endif /* _S390_CHECKSUM_H */