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