checksum.c 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  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, <ralf@waldorf-gmbh.de>
  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 int do_csum(const unsigned char * buff, int len)
  26. {
  27. int odd, count;
  28. unsigned int result = 0;
  29. if (len <= 0)
  30. goto out;
  31. odd = 1 & (unsigned long) buff;
  32. if (odd) {
  33. result = be16_to_cpu(*buff);
  34. len--;
  35. buff++;
  36. }
  37. count = len >> 1; /* nr of 16-bit words.. */
  38. if (count) {
  39. if (2 & (unsigned long) buff) {
  40. result += *(unsigned short *) buff;
  41. count--;
  42. len -= 2;
  43. buff += 2;
  44. }
  45. count >>= 1; /* nr of 32-bit words.. */
  46. if (count) {
  47. while (count >= 4) {
  48. unsigned int r1, r2, r3, r4;
  49. r1 = *(unsigned int *)(buff + 0);
  50. r2 = *(unsigned int *)(buff + 4);
  51. r3 = *(unsigned int *)(buff + 8);
  52. r4 = *(unsigned int *)(buff + 12);
  53. addc(result, r1);
  54. addc(result, r2);
  55. addc(result, r3);
  56. addc(result, r4);
  57. count -= 4;
  58. buff += 16;
  59. }
  60. while (count) {
  61. unsigned int w = *(unsigned int *) buff;
  62. count--;
  63. buff += 4;
  64. addc(result, w);
  65. }
  66. result = (result & 0xffff) + (result >> 16);
  67. }
  68. if (len & 2) {
  69. result += *(unsigned short *) buff;
  70. buff += 2;
  71. }
  72. }
  73. if (len & 1)
  74. result += le16_to_cpu(*buff);
  75. result = csum_from32to16(result);
  76. if (odd)
  77. result = swab16(result);
  78. out:
  79. return result;
  80. }
  81. /*
  82. * computes a partial checksum, e.g. for TCP/UDP fragments
  83. */
  84. /*
  85. * why bother folding?
  86. */
  87. __wsum csum_partial(const void *buff, int len, __wsum sum)
  88. {
  89. unsigned int result = do_csum(buff, len);
  90. addc(result, sum);
  91. return (__force __wsum)csum_from32to16(result);
  92. }
  93. EXPORT_SYMBOL(csum_partial);