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.
6 * IP/TCP/UDP checksumming routines
8 * Authors: Jorge Cwik, <jorge@laser.satlink.net>
9 * Arnt Gulbrandsen, <agulbra@nvg.unit.no>
10 * Tom May, <ftom@netcom.com>
11 * Andreas Schwab, <schwab@issan.informatik.uni-dortmund.de>
12 * Lots of code moved from tcp.c and ip.c; see those files
15 * 03/02/96 Jes Sorensen, Andreas Schwab, Roman Hodek:
16 * Fixed some nasty bugs, causing some horrible crashes.
17 * A: At some points, the sum (%0) was used as
18 * length-counter instead of the length counter
19 * (%1). Thanks to Roman Hodek for pointing this out.
20 * B: GCC seems to mess up if one uses too many
21 * data-registers to hold input values and one tries to
22 * specify d0 and d1 as scratch registers. Letting gcc
23 * choose these registers itself solves the problem.
25 * This program is free software; you can redistribute it and/or
26 * modify it under the terms of the GNU General Public License
27 * as published by the Free Software Foundation; either version
28 * 2 of the License, or (at your option) any later version.
30 * 1998/8/31 Andreas Schwab:
31 * Zero out rest of buffer on exception in
32 * csum_partial_copy_from_user.
35 #include <linux/module.h>
36 #include <net/checksum.h>
39 * computes a partial checksum, e.g. for TCP/UDP fragments
42 __wsum
csum_partial(const void *buff
, int len
, __wsum sum
)
44 unsigned long tmp1
, tmp2
;
46 * Experiments with ethernet and slip connections show that buff
47 * is aligned on either a 2-byte or 4-byte boundary.
49 __asm__("movel %2,%3\n\t"
50 "btst #1,%3\n\t" /* Check alignment */
52 "subql #2,%1\n\t" /* buff%4==2: treat first word */
54 "addql #2,%1\n\t" /* len was == 2, treat only rest */
57 "addw %2@+,%0\n\t" /* add first word to sum */
59 "addxl %3,%0\n" /* add X bit */
61 /* unrolled loop for the main part: do 8 longs at once */
62 "movel %1,%3\n\t" /* save len in tmp1 */
63 "lsrl #5,%1\n\t" /* len/32 */
64 "jeq 2f\n\t" /* not enough... */
85 "addxl %4,%0\n\t" /* add X bit */
90 "movel %3,%1\n\t" /* restore len from tmp1 */
91 "andw #0x1c,%3\n\t" /* number of rest longs */
96 /* loop for rest longs */
101 "addxl %4,%0\n" /* add X bit */
103 /* now check for rest bytes that do not fit into longs */
106 "clrl %4\n\t" /* clear tmp2 for rest bytes */
109 "movew %2@+,%4\n\t" /* have rest >= 2: get word */
110 "swap %4\n\t" /* into bits 16..31 */
111 "tstw %1\n\t" /* another byte? */
114 "moveb %2@,%4\n\t" /* have odd rest: get byte */
115 "lslw #8,%4\n\t" /* into bits 8..15; 16..31 untouched */
117 "addl %4,%0\n\t" /* now add rest long to sum */
119 "addxl %4,%0\n" /* add X bit */
121 : "=d" (sum
), "=d" (len
), "=a" (buff
),
122 "=&d" (tmp1
), "=&d" (tmp2
)
123 : "0" (sum
), "1" (len
), "2" (buff
)
128 EXPORT_SYMBOL(csum_partial
);
132 * copy from user space while checksumming, with exception handling.
136 csum_partial_copy_from_user(const void __user
*src
, void *dst
,
137 int len
, __wsum sum
, int *csum_err
)
140 * GCC doesn't like more than 10 operands for the asm
141 * statements so we have to use tmp2 for the error
144 unsigned long tmp1
, tmp2
;
146 __asm__("movel %2,%4\n\t"
147 "btst #1,%4\n\t" /* Check alignment */
149 "subql #2,%1\n\t" /* buff%4==2: treat first word */
151 "addql #2,%1\n\t" /* len was == 2, treat only rest */
155 "movesw %2@+,%4\n\t" /* add first word to sum */
159 "addxl %4,%0\n" /* add X bit */
161 /* unrolled loop for the main part: do 8 longs at once */
162 "movel %1,%4\n\t" /* save len in tmp1 */
163 "lsrl #5,%1\n\t" /* len/32 */
164 "jeq 2f\n\t" /* not enough... */
201 "addxl %5,%0\n\t" /* add X bit */
206 "movel %4,%1\n\t" /* restore len from tmp1 */
207 "andw #0x1c,%4\n\t" /* number of rest longs */
212 /* loop for rest longs */
219 "addxl %5,%0\n" /* add X bit */
221 /* now check for rest bytes that do not fit into longs */
224 "clrl %5\n\t" /* clear tmp2 for rest bytes */
228 "movesw %2@+,%5\n\t" /* have rest >= 2: get word */
230 "swap %5\n\t" /* into bits 16..31 */
231 "tstw %1\n\t" /* another byte? */
235 "movesb %2@,%5\n\t" /* have odd rest: get byte */
237 "lslw #8,%5\n\t" /* into bits 8..15; 16..31 untouched */
239 "addl %5,%0\n\t" /* now add rest long to sum */
241 "addxl %5,%0\n\t" /* add X bit */
243 "clrl %5\n" /* no error - clear return value */
245 ".section .fixup,\"ax\"\n"
247 /* If any exception occurs zero out the rest.
248 Similarities with the code above are intentional :-) */
294 #define STR(X) STR1(X)
296 "moveq #-" STR(EFAULT
) ",%5\n\t"
299 ".section __ex_table,\"a\"\n"
313 : "=d" (sum
), "=d" (len
), "=a" (src
), "=a" (dst
),
314 "=&d" (tmp1
), "=d" (tmp2
)
315 : "0" (sum
), "1" (len
), "2" (src
), "3" (dst
)
323 EXPORT_SYMBOL(csum_partial_copy_from_user
);
327 * copy from kernel space while checksumming, otherwise like csum_partial
331 csum_partial_copy_nocheck(const void *src
, void *dst
, int len
, __wsum sum
)
333 unsigned long tmp1
, tmp2
;
334 __asm__("movel %2,%4\n\t"
335 "btst #1,%4\n\t" /* Check alignment */
337 "subql #2,%1\n\t" /* buff%4==2: treat first word */
339 "addql #2,%1\n\t" /* len was == 2, treat only rest */
342 "movew %2@+,%4\n\t" /* add first word to sum */
346 "addxl %4,%0\n" /* add X bit */
348 /* unrolled loop for the main part: do 8 longs at once */
349 "movel %1,%4\n\t" /* save len in tmp1 */
350 "lsrl #5,%1\n\t" /* len/32 */
351 "jeq 2f\n\t" /* not enough... */
380 "addxl %5,%0\n\t" /* add X bit */
385 "movel %4,%1\n\t" /* restore len from tmp1 */
386 "andw #0x1c,%4\n\t" /* number of rest longs */
391 /* loop for rest longs */
397 "addxl %5,%0\n" /* add X bit */
399 /* now check for rest bytes that do not fit into longs */
402 "clrl %5\n\t" /* clear tmp2 for rest bytes */
405 "movew %2@+,%5\n\t" /* have rest >= 2: get word */
407 "swap %5\n\t" /* into bits 16..31 */
408 "tstw %1\n\t" /* another byte? */
411 "moveb %2@,%5\n\t" /* have odd rest: get byte */
413 "lslw #8,%5\n" /* into bits 8..15; 16..31 untouched */
415 "addl %5,%0\n\t" /* now add rest long to sum */
417 "addxl %5,%0\n" /* add X bit */
419 : "=d" (sum
), "=d" (len
), "=a" (src
), "=a" (dst
),
420 "=&d" (tmp1
), "=&d" (tmp2
)
421 : "0" (sum
), "1" (len
), "2" (src
), "3" (dst
)
425 EXPORT_SYMBOL(csum_partial_copy_nocheck
);