checksum.c 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * INET An implementation of the TCP/IP protocol suite for the LINUX
  4. * operating system. INET is implemented using the BSD Socket
  5. * interface as the means of communication with the user level.
  6. *
  7. * MIPS specific IP/TCP/UDP checksumming routines
  8. *
  9. * Authors: Ralf Baechle, <[email protected]>
  10. * Lots of code moved from tcp.c and ip.c; see those files
  11. * for more names.
  12. */
  13. #include <linux/module.h>
  14. #include <linux/types.h>
  15. #include <net/checksum.h>
  16. #include <asm/byteorder.h>
  17. #include <asm/string.h>
  18. #include <linux/uaccess.h>
  19. #define addc(_t,_r) \
  20. __asm__ __volatile__ ( \
  21. " add %0, %1, %0\n" \
  22. " addc %0, %%r0, %0\n" \
  23. : "=r"(_t) \
  24. : "r"(_r), "0"(_t));
  25. static inline unsigned short from32to16(unsigned int x)
  26. {
  27. /* 32 bits --> 16 bits + carry */
  28. x = (x & 0xffff) + (x >> 16);
  29. /* 16 bits + carry --> 16 bits including carry */
  30. x = (x & 0xffff) + (x >> 16);
  31. return (unsigned short)x;
  32. }
  33. static inline unsigned int do_csum(const unsigned char * buff, int len)
  34. {
  35. int odd, count;
  36. unsigned int result = 0;
  37. if (len <= 0)
  38. goto out;
  39. odd = 1 & (unsigned long) buff;
  40. if (odd) {
  41. result = be16_to_cpu(*buff);
  42. len--;
  43. buff++;
  44. }
  45. count = len >> 1; /* nr of 16-bit words.. */
  46. if (count) {
  47. if (2 & (unsigned long) buff) {
  48. result += *(unsigned short *) buff;
  49. count--;
  50. len -= 2;
  51. buff += 2;
  52. }
  53. count >>= 1; /* nr of 32-bit words.. */
  54. if (count) {
  55. while (count >= 4) {
  56. unsigned int r1, r2, r3, r4;
  57. r1 = *(unsigned int *)(buff + 0);
  58. r2 = *(unsigned int *)(buff + 4);
  59. r3 = *(unsigned int *)(buff + 8);
  60. r4 = *(unsigned int *)(buff + 12);
  61. addc(result, r1);
  62. addc(result, r2);
  63. addc(result, r3);
  64. addc(result, r4);
  65. count -= 4;
  66. buff += 16;
  67. }
  68. while (count) {
  69. unsigned int w = *(unsigned int *) buff;
  70. count--;
  71. buff += 4;
  72. addc(result, w);
  73. }
  74. result = (result & 0xffff) + (result >> 16);
  75. }
  76. if (len & 2) {
  77. result += *(unsigned short *) buff;
  78. buff += 2;
  79. }
  80. }
  81. if (len & 1)
  82. result += le16_to_cpu(*buff);
  83. result = from32to16(result);
  84. if (odd)
  85. result = swab16(result);
  86. out:
  87. return result;
  88. }
  89. /*
  90. * computes a partial checksum, e.g. for TCP/UDP fragments
  91. */
  92. /*
  93. * why bother folding?
  94. */
  95. __wsum csum_partial(const void *buff, int len, __wsum sum)
  96. {
  97. unsigned int result = do_csum(buff, len);
  98. addc(result, sum);
  99. return (__force __wsum)from32to16(result);
  100. }
  101. EXPORT_SYMBOL(csum_partial);