checksum.c 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. /*
  2. * INET An implementation of the TCP/IP protocol suite for the LINUX
  3. * operating system. INET is implemented using the BSD Socket
  4. * interface as the means of communication with the user level.
  5. *
  6. * MIPS specific IP/TCP/UDP checksumming routines
  7. *
  8. * Authors: Ralf Baechle, <ralf@waldorf-gmbh.de>
  9. * Lots of code moved from tcp.c and ip.c; see those files
  10. * for more names.
  11. *
  12. * This program is free software; you can redistribute it and/or
  13. * modify it under the terms of the GNU General Public License
  14. * as published by the Free Software Foundation; either version
  15. * 2 of the License, or (at your option) any later version.
  16. */
  17. #include <linux/module.h>
  18. #include <linux/types.h>
  19. #include <net/checksum.h>
  20. #include <asm/byteorder.h>
  21. #include <asm/string.h>
  22. #include <asm/uaccess.h>
  23. #define addc(_t,_r) \
  24. __asm__ __volatile__ ( \
  25. " add %0, %1, %0\n" \
  26. " addc %0, %%r0, %0\n" \
  27. : "=r"(_t) \
  28. : "r"(_r), "0"(_t));
  29. static inline unsigned short from32to16(unsigned int x)
  30. {
  31. /* 32 bits --> 16 bits + carry */
  32. x = (x & 0xffff) + (x >> 16);
  33. /* 16 bits + carry --> 16 bits including carry */
  34. x = (x & 0xffff) + (x >> 16);
  35. return (unsigned short)x;
  36. }
  37. static inline unsigned int do_csum(const unsigned char * buff, int len)
  38. {
  39. int odd, count;
  40. unsigned int result = 0;
  41. if (len <= 0)
  42. goto out;
  43. odd = 1 & (unsigned long) buff;
  44. if (odd) {
  45. result = be16_to_cpu(*buff);
  46. len--;
  47. buff++;
  48. }
  49. count = len >> 1; /* nr of 16-bit words.. */
  50. if (count) {
  51. if (2 & (unsigned long) buff) {
  52. result += *(unsigned short *) buff;
  53. count--;
  54. len -= 2;
  55. buff += 2;
  56. }
  57. count >>= 1; /* nr of 32-bit words.. */
  58. if (count) {
  59. while (count >= 4) {
  60. unsigned int r1, r2, r3, r4;
  61. r1 = *(unsigned int *)(buff + 0);
  62. r2 = *(unsigned int *)(buff + 4);
  63. r3 = *(unsigned int *)(buff + 8);
  64. r4 = *(unsigned int *)(buff + 12);
  65. addc(result, r1);
  66. addc(result, r2);
  67. addc(result, r3);
  68. addc(result, r4);
  69. count -= 4;
  70. buff += 16;
  71. }
  72. while (count) {
  73. unsigned int w = *(unsigned int *) buff;
  74. count--;
  75. buff += 4;
  76. addc(result, w);
  77. }
  78. result = (result & 0xffff) + (result >> 16);
  79. }
  80. if (len & 2) {
  81. result += *(unsigned short *) buff;
  82. buff += 2;
  83. }
  84. }
  85. if (len & 1)
  86. result += le16_to_cpu(*buff);
  87. result = from32to16(result);
  88. if (odd)
  89. result = swab16(result);
  90. out:
  91. return result;
  92. }
  93. /*
  94. * computes a partial checksum, e.g. for TCP/UDP fragments
  95. */
  96. /*
  97. * why bother folding?
  98. */
  99. __wsum csum_partial(const void *buff, int len, __wsum sum)
  100. {
  101. unsigned int result = do_csum(buff, len);
  102. addc(result, sum);
  103. return (__force __wsum)from32to16(result);
  104. }
  105. EXPORT_SYMBOL(csum_partial);
  106. /*
  107. * copy while checksumming, otherwise like csum_partial
  108. */
  109. __wsum csum_partial_copy_nocheck(const void *src, void *dst,
  110. int len, __wsum sum)
  111. {
  112. /*
  113. * It's 2:30 am and I don't feel like doing it real ...
  114. * This is lots slower than the real thing (tm)
  115. */
  116. sum = csum_partial(src, len, sum);
  117. memcpy(dst, src, len);
  118. return sum;
  119. }
  120. EXPORT_SYMBOL(csum_partial_copy_nocheck);
  121. /*
  122. * Copy from userspace and compute checksum. If we catch an exception
  123. * then zero the rest of the buffer.
  124. */
  125. __wsum csum_partial_copy_from_user(const void __user *src,
  126. void *dst, int len,
  127. __wsum sum, int *err_ptr)
  128. {
  129. int missing;
  130. missing = copy_from_user(dst, src, len);
  131. if (missing) {
  132. memset(dst + len - missing, 0, missing);
  133. *err_ptr = -EFAULT;
  134. }
  135. return csum_partial(dst, len, sum);
  136. }
  137. EXPORT_SYMBOL(csum_partial_copy_from_user);