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 16 #include <net/checksum.h> 17 #include <asm/byteorder.h> 18 #include <asm/string.h> 19 #include <linux/uaccess.h> 20 21 #define addc(_t,_r) \ 22 __asm__ __volatile__ ( \ 23 " add %0, %1, %0\n" \ 24 " addc %0, %%r0, %0\n" \ 25 : "=r"(_t) \ 26 : "r"(_r), "0"(_t)); 27 28 static inline unsigned short from32to16(unsigned int x) 29 { 30 /* 32 bits --> 16 bits + carry */ 31 x = (x & 0xffff) + (x >> 16); 32 /* 16 bits + carry --> 16 bits including carry */ 33 x = (x & 0xffff) + (x >> 16); 34 return (unsigned short)x; 35 } 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 42 if (len <= 0) 43 goto out; 44 odd = 1 & (unsigned long) buff; 45 if (odd) { 46 result = be16_to_cpu(*buff); 47 len--; 48 buff++; 49 } 50 count = len >> 1; /* nr of 16-bit words.. */ 51 if (count) { 52 if (2 & (unsigned long) buff) { 53 result += *(unsigned short *) buff; 54 count--; 55 len -= 2; 56 buff += 2; 57 } 58 count >>= 1; /* nr of 32-bit words.. */ 59 if (count) { 60 while (count >= 4) { 61 unsigned int r1, r2, r3, r4; 62 r1 = *(unsigned int *)(buff + 0); 63 r2 = *(unsigned int *)(buff + 4); 64 r3 = *(unsigned int *)(buff + 8); 65 r4 = *(unsigned int *)(buff + 12); 66 addc(result, r1); 67 addc(result, r2); 68 addc(result, r3); 69 addc(result, r4); 70 count -= 4; 71 buff += 16; 72 } 73 while (count) { 74 unsigned int w = *(unsigned int *) buff; 75 count--; 76 buff += 4; 77 addc(result, w); 78 } 79 result = (result & 0xffff) + (result >> 16); 80 } 81 if (len & 2) { 82 result += *(unsigned short *) buff; 83 buff += 2; 84 } 85 } 86 if (len & 1) 87 result += le16_to_cpu(*buff); 88 result = from32to16(result); 89 if (odd) 90 result = swab16(result); 91 out: 92 return result; 93 } 94 95 /* 96 * computes a partial checksum, e.g. for TCP/UDP fragments 97 */ 98 /* 99 * why bother folding? 100 */ 101 __wsum csum_partial(const void *buff, int len, __wsum sum) 102 { 103 unsigned int result = do_csum(buff, len); 104 addc(result, sum); 105 return (__force __wsum)from32to16(result); 106 } 107 108 EXPORT_SYMBOL(csum_partial); 109 110 /* 111 * copy while checksumming, otherwise like csum_partial 112 */ 113 __wsum csum_partial_copy_nocheck(const void *src, void *dst, 114 int len, __wsum sum) 115 { 116 /* 117 * It's 2:30 am and I don't feel like doing it real ... 118 * This is lots slower than the real thing (tm) 119 */ 120 sum = csum_partial(src, len, sum); 121 memcpy(dst, src, len); 122 123 return sum; 124 } 125 EXPORT_SYMBOL(csum_partial_copy_nocheck); 126 127 /* 128 * Copy from userspace and compute checksum. If we catch an exception 129 * then zero the rest of the buffer. 130 */ 131 __wsum csum_partial_copy_from_user(const void __user *src, 132 void *dst, int len, 133 __wsum sum, int *err_ptr) 134 { 135 int missing; 136 137 missing = copy_from_user(dst, src, len); 138 if (missing) { 139 memset(dst + len - missing, 0, missing); 140 *err_ptr = -EFAULT; 141 } 142 143 return csum_partial(dst, len, sum); 144 } 145 EXPORT_SYMBOL(csum_partial_copy_from_user); 146