1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (C) 1995, 1996, 1997, 1998, 1999 by Ralf Baechle
7  * Copyright (C) 1999 Silicon Graphics, Inc.
8  * Copyright (C) 2001 Thiemo Seufer.
9  * Copyright (C) 2002  Maciej W. Rozycki
10  */
11 #ifndef _ASM_CHECKSUM_H
12 #define _ASM_CHECKSUM_H
13 
14 #include <asm/uaccess.h>
15 
16 /*
17  * computes the checksum of a memory block at buff, length len,
18  * and adds in "sum" (32-bit)
19  *
20  * returns a 32-bit number suitable for feeding into itself
21  * or csum_tcpudp_magic
22  *
23  * this function must be called with even lengths, except
24  * for the last fragment, which may be odd
25  *
26  * it's best to have buff aligned on a 32-bit boundary
27  */
28 unsigned int csum_partial(const unsigned char *buff, int len, unsigned int sum);
29 
30 /*
31  * this is a new version of the above that records errors it finds in *errp,
32  * but continues and zeros the rest of the buffer.
33  */
34 #define csum_partial_copy_nocheck csum_partial_copy
35 
36 /*
37  * this is a new version of the above that records errors it finds in *errp,
38  * but continues and zeros the rest of the buffer.
39  */
40 unsigned int csum_partial_copy_from_user(const char *src, char *dst, int len,
41                                          unsigned int sum, int *errp);
42 
43 /*
44  * Copy and checksum to user
45  */
46 #define HAVE_CSUM_COPY_USER
csum_and_copy_to_user(const char * src,char * dst,int len,int sum,int * err_ptr)47 static inline unsigned int csum_and_copy_to_user (const char *src, char *dst,
48 						  int len, int sum,
49 						  int *err_ptr)
50 {
51 	sum = csum_partial(src, len, sum);
52 
53 	if (copy_to_user(dst, src, len)) {
54 		*err_ptr = -EFAULT;
55 		return -1;
56 	}
57 
58 	return sum;
59 }
60 
61 /*
62  * the same as csum_partial, but copies from user space (but on MIPS
63  * we have just one address space, so this is identical to the above)
64  *
65  * this is obsolete and will go away.
66  */
67 #define csum_partial_copy_fromuser csum_partial_copy
68 unsigned int csum_partial_copy(const char *src, char *dst, int len,
69 			       unsigned int sum);
70 
71 /*
72  *	Fold a partial checksum without adding pseudo headers
73  */
csum_fold(unsigned int sum)74 static inline unsigned short int csum_fold(unsigned int sum)
75 {
76 	__asm__(
77 	".set\tnoat\t\t\t# csum_fold\n\t"
78 	"sll\t$1,%0,16\n\t"
79 	"addu\t%0,$1\n\t"
80 	"sltu\t$1,%0,$1\n\t"
81 	"srl\t%0,%0,16\n\t"
82 	"addu\t%0,$1\n\t"
83 	"xori\t%0,0xffff\n\t"
84 	".set\tat"
85 	: "=r" (sum)
86 	: "0" (sum));
87 
88 	return sum;
89 }
90 
91 /*
92  *	This is a version of ip_compute_csum() optimized for IP headers,
93  *	which always checksum on 4 octet boundaries.
94  *
95  *	By Jorge Cwik <jorge@laser.satlink.net>, adapted for linux by
96  *	Arnt Gulbrandsen.
97  */
ip_fast_csum(unsigned char * iph,unsigned int ihl)98 static inline unsigned short ip_fast_csum(unsigned char *iph, unsigned int ihl)
99 {
100 	unsigned int *word = (unsigned int *) iph;
101 	unsigned int *stop = word + ihl;
102 	unsigned int csum;
103 	int carry;
104 
105 	csum = word[0];
106 	csum += word[1];
107 	carry = (csum < word[1]);
108 	csum += carry;
109 
110 	csum += word[2];
111 	carry = (csum < word[2]);
112 	csum += carry;
113 
114 	csum += word[3];
115 	carry = (csum < word[3]);
116 	csum += carry;
117 
118 	word += 4;
119 	do {
120 		csum += *word;
121 		carry = (csum < *word);
122 		csum += carry;
123 		word++;
124 	} while (word != stop);
125 
126 	return csum_fold(csum);
127 }
128 
129 /*
130  * Cast unsigned short expressions to unsigned long explicitly
131  * to avoid surprises resulting from implicit promotions to
132  * signed int.  --macro
133  */
csum_tcpudp_nofold(unsigned long saddr,unsigned long daddr,unsigned short len,unsigned short proto,unsigned int sum)134 static inline unsigned long csum_tcpudp_nofold(unsigned long saddr,
135 					       unsigned long daddr,
136 					       unsigned short len,
137 					       unsigned short proto,
138 					       unsigned int sum)
139 {
140 	__asm__(
141 	".set\tnoat\t\t\t# csum_tcpudp_nofold\n\t"
142 	"daddu\t%0, %2\n\t"
143 	"daddu\t%0, %3\n\t"
144 	"daddu\t%0, %4\n\t"
145 	"dsll32\t$1, %0, 0\n\t"
146 	"daddu\t%0, $1\n\t"
147 	"dsrl32\t%0, %0, 0\n\t"
148 	".set\tat"
149 	: "=&r" (sum)
150 	: "0" (daddr), "r"(saddr),
151 #ifdef __MIPSEL__
152 	  "r" (((unsigned long)ntohs(len)<<16)+proto*256),
153 #else
154 	  "r" (((unsigned long)(proto)<<16)+len),
155 #endif
156 	  "r" (sum));
157 
158 	return sum;
159 }
160 
161 /*
162  * computes the checksum of the TCP/UDP pseudo-header
163  * returns a 16-bit checksum, already complemented
164  */
csum_tcpudp_magic(unsigned long saddr,unsigned long daddr,unsigned short len,unsigned short proto,unsigned int sum)165 static inline unsigned short int csum_tcpudp_magic(unsigned long saddr,
166 						   unsigned long daddr,
167 						   unsigned short len,
168 						   unsigned short proto,
169 						   unsigned int sum)
170 {
171 	return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
172 }
173 
174 /*
175  * this routine is used for miscellaneous IP-like checksums, mainly
176  * in icmp.c
177  */
ip_compute_csum(unsigned char * buff,int len)178 static inline unsigned short ip_compute_csum(unsigned char * buff, int len)
179 {
180 	return csum_fold(csum_partial(buff, len, 0));
181 }
182 
183 #define _HAVE_ARCH_IPV6_CSUM
csum_ipv6_magic(struct in6_addr * saddr,struct in6_addr * daddr,__u32 len,unsigned short proto,unsigned int sum)184 static __inline__ unsigned short int csum_ipv6_magic(struct in6_addr *saddr,
185 						     struct in6_addr *daddr,
186 						     __u32 len,
187 						     unsigned short proto,
188 						     unsigned int sum)
189 {
190 	__asm__(
191 	".set\tpush\t\t\t# csum_ipv6_magic\n\t"
192 	".set\tnoreorder\n\t"
193 	".set\tnoat\n\t"
194 	"addu\t%0, %5\t\t\t# proto (long in network byte order)\n\t"
195 	"sltu\t$1, %0, %5\n\t"
196 	"addu\t%0, $1\n\t"
197 
198 	"addu\t%0, %6\t\t\t# csum\n\t"
199 	"sltu\t$1, %0, %6\n\t"
200 	"lw\t%1, 0(%2)\t\t\t# four words source address\n\t"
201 	"addu\t%0, $1\n\t"
202 	"addu\t%0, %1\n\t"
203 	"sltu\t$1, %0, %1\n\t"
204 
205 	"lw\t%1, 4(%2)\n\t"
206 	"addu\t%0, $1\n\t"
207 	"addu\t%0, %1\n\t"
208 	"sltu\t$1, %0, %1\n\t"
209 
210 	"lw\t%1, 8(%2)\n\t"
211 	"addu\t%0, $1\n\t"
212 	"addu\t%0, %1\n\t"
213 	"sltu\t$1, %0, %1\n\t"
214 
215 	"lw\t%1, 12(%2)\n\t"
216 	"addu\t%0, $1\n\t"
217 	"addu\t%0, %1\n\t"
218 	"sltu\t$1, %0, %1\n\t"
219 
220 	"lw\t%1, 0(%3)\n\t"
221 	"addu\t%0, $1\n\t"
222 	"addu\t%0, %1\n\t"
223 	"sltu\t$1, %0, %1\n\t"
224 
225 	"lw\t%1, 4(%3)\n\t"
226 	"addu\t%0, $1\n\t"
227 	"addu\t%0, %1\n\t"
228 	"sltu\t$1, %0, %1\n\t"
229 
230 	"lw\t%1, 8(%3)\n\t"
231 	"addu\t%0, $1\n\t"
232 	"addu\t%0, %1\n\t"
233 	"sltu\t$1, %0, %1\n\t"
234 
235 	"lw\t%1, 12(%3)\n\t"
236 	"addu\t%0, $1\n\t"
237 	"addu\t%0, %1\n\t"
238 	"sltu\t$1, %0, %1\n\t"
239 
240 	"addu\t%0, $1\t\t\t# Add final carry\n\t"
241 	".set\tpop"
242 	: "=&r" (sum), "=&r" (proto)
243 	: "r" (saddr), "r" (daddr),
244 	  "0" (htonl(len)), "1" (htonl(proto)), "r" (sum));
245 
246 	return csum_fold(sum);
247 }
248 
249 #endif /* _ASM_CHECKSUM_H */
250