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