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.1.1.1 2007/08/03 18:52:22 Exp $ 18 */ 19#include <net/checksum.h> 20#include <linux/module.h> 21#include <linux/types.h> 22#include <asm/byteorder.h> 23#include <asm/string.h> 24#include <asm/uaccess.h> 25 26static inline unsigned short from32to16 (unsigned long sum) 27{ 28 unsigned int result; 29 /* 30 %0 %1 31 hsw %1, %0 H L L H 32 add %1, %0 H L H+L+C H+L 33 */ 34 asm ("hsw %1, %0; add %1, %0" : "=&r" (result) : "r" (sum)); 35 return result >> 16; 36} 37 38static inline unsigned int do_csum(const unsigned char * buff, int len) 39{ 40 int odd, count; 41 unsigned int result = 0; 42 43 if (len <= 0) 44 goto out; 45 odd = 1 & (unsigned long) buff; 46 if (odd) { 47 result = be16_to_cpu(*buff); 48 len--; 49 buff++; 50 } 51 count = len >> 1; /* nr of 16-bit words.. */ 52 if (count) { 53 if (2 & (unsigned long) buff) { 54 result += *(unsigned short *) buff; 55 count--; 56 len -= 2; 57 buff += 2; 58 } 59 count >>= 1; /* nr of 32-bit words.. */ 60 if (count) { 61 unsigned int carry = 0; 62 do { 63 unsigned int w = *(unsigned int *) buff; 64 count--; 65 buff += 4; 66 result += carry; 67 result += w; 68 carry = (w > result); 69 } while (count); 70 result += carry; 71 result = (result & 0xffff) + (result >> 16); 72 } 73 if (len & 2) { 74 result += *(unsigned short *) buff; 75 buff += 2; 76 } 77 } 78 if (len & 1) 79 result += le16_to_cpu(*buff); 80 result = from32to16(result); 81 if (odd) 82 result = ((result >> 8) & 0xff) | ((result & 0xff) << 8); 83out: 84 return result; 85} 86 87/* 88 * This is a version of ip_compute_csum() optimized for IP headers, 89 * which always checksum on 4 octet boundaries. 90 */ 91__sum16 ip_fast_csum(const void *iph, unsigned int ihl) 92{ 93 return (__force __sum16)~do_csum(iph,ihl*4); 94} 95 96/* 97 * this routine is used for miscellaneous IP-like checksums, mainly 98 * in icmp.c 99 */ 100__sum16 ip_compute_csum(const void *buff, int len) 101{ 102 return (__force __sum16)~do_csum(buff,len); 103} 104 105/* 106 * computes a partial checksum, e.g. for TCP/UDP fragments 107 */ 108__wsum csum_partial(const void *buff, int len, __wsum sum) 109{ 110 unsigned int result = do_csum(buff, len); 111 112 /* add in old sum, and carry.. */ 113 result += (__force u32)sum; 114 if ((__force u32)sum > result) 115 result += 1; 116 return (__force __wsum)result; 117} 118 119EXPORT_SYMBOL(csum_partial); 120 121/* 122 * copy while checksumming, otherwise like csum_partial 123 */ 124__wsum csum_partial_copy_nocheck(const void *src, void *dst, 125 int len, __wsum sum) 126{ 127 /* 128 * It's 2:30 am and I don't feel like doing it real ... 129 * This is lots slower than the real thing (tm) 130 */ 131 sum = csum_partial(src, len, sum); 132 memcpy(dst, src, len); 133 134 return sum; 135} 136 137/* 138 * Copy from userspace and compute checksum. If we catch an exception 139 * then zero the rest of the buffer. 140 */ 141__wsum csum_partial_copy_from_user (const void *src, 142 void *dst, 143 int len, __wsum sum, 144 int *err_ptr) 145{ 146 int missing; 147 148 missing = copy_from_user(dst, src, len); 149 if (missing) { 150 memset(dst + len - missing, 0, missing); 151 *err_ptr = -EFAULT; 152 } 153 154 return csum_partial(dst, len, sum); 155} 156