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