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
43 csum_partial (const unsigned char *buff
, int len
, unsigned int sum
)
45 unsigned long tmp1
, tmp2
;
47 * Experiments with ethernet and slip connections show that buff
48 * is aligned on either a 2-byte or 4-byte boundary.
50 __asm__("movel %2,%3\n\t"
51 "btst #1,%3\n\t" /* Check alignment */
53 "subql #2,%1\n\t" /* buff%4==2: treat first word */
55 "addql #2,%1\n\t" /* len was == 2, treat only rest */
58 "addw %2@+,%0\n\t" /* add first word to sum */
60 "addxl %3,%0\n" /* add X bit */
62 /* unrolled loop for the main part: do 8 longs at once */
63 "movel %1,%3\n\t" /* save len in tmp1 */
64 "lsrl #5,%1\n\t" /* len/32 */
65 "jeq 2f\n\t" /* not enough... */
86 "addxl %4,%0\n\t" /* add X bit */
91 "movel %3,%1\n\t" /* restore len from tmp1 */
92 "andw #0x1c,%3\n\t" /* number of rest longs */
97 /* loop for rest longs */
102 "addxl %4,%0\n" /* add X bit */
104 /* now check for rest bytes that do not fit into longs */
107 "clrl %4\n\t" /* clear tmp2 for rest bytes */
110 "movew %2@+,%4\n\t" /* have rest >= 2: get word */
111 "swap %4\n\t" /* into bits 16..31 */
112 "tstw %1\n\t" /* another byte? */
115 "moveb %2@,%4\n\t" /* have odd rest: get byte */
116 "lslw #8,%4\n\t" /* into bits 8..15; 16..31 untouched */
118 "addl %4,%0\n\t" /* now add rest long to sum */
120 "addxl %4,%0\n" /* add X bit */
122 : "=d" (sum
), "=d" (len
), "=a" (buff
),
123 "=&d" (tmp1
), "=&d" (tmp2
)
124 : "0" (sum
), "1" (len
), "2" (buff
)
129 EXPORT_SYMBOL(csum_partial
);
133 * copy from user space while checksumming, with exception handling.
137 csum_partial_copy_from_user(const unsigned char *src
, unsigned char *dst
,
138 int len
, int sum
, int *csum_err
)
141 * GCC doesn't like more than 10 operands for the asm
142 * statements so we have to use tmp2 for the error
145 unsigned long tmp1
, tmp2
;
147 __asm__("movel %2,%4\n\t"
148 "btst #1,%4\n\t" /* Check alignment */
150 "subql #2,%1\n\t" /* buff%4==2: treat first word */
152 "addql #2,%1\n\t" /* len was == 2, treat only rest */
156 "movesw %2@+,%4\n\t" /* add first word to sum */
160 "addxl %4,%0\n" /* add X bit */
162 /* unrolled loop for the main part: do 8 longs at once */
163 "movel %1,%4\n\t" /* save len in tmp1 */
164 "lsrl #5,%1\n\t" /* len/32 */
165 "jeq 2f\n\t" /* not enough... */
202 "addxl %5,%0\n\t" /* add X bit */
207 "movel %4,%1\n\t" /* restore len from tmp1 */
208 "andw #0x1c,%4\n\t" /* number of rest longs */
213 /* loop for rest longs */
220 "addxl %5,%0\n" /* add X bit */
222 /* now check for rest bytes that do not fit into longs */
225 "clrl %5\n\t" /* clear tmp2 for rest bytes */
229 "movesw %2@+,%5\n\t" /* have rest >= 2: get word */
231 "swap %5\n\t" /* into bits 16..31 */
232 "tstw %1\n\t" /* another byte? */
236 "movesb %2@,%5\n\t" /* have odd rest: get byte */
238 "lslw #8,%5\n\t" /* into bits 8..15; 16..31 untouched */
240 "addl %5,%0\n\t" /* now add rest long to sum */
242 "addxl %5,%0\n\t" /* add X bit */
244 "clrl %5\n" /* no error - clear return value */
246 ".section .fixup,\"ax\"\n"
248 /* If any exception occurs zero out the rest.
249 Similarities with the code above are intentional :-) */
295 #define STR(X) STR1(X)
297 "moveq #-" STR(EFAULT
) ",%5\n\t"
300 ".section __ex_table,\"a\"\n"
314 : "=d" (sum
), "=d" (len
), "=a" (src
), "=a" (dst
),
315 "=&d" (tmp1
), "=d" (tmp2
)
316 : "0" (sum
), "1" (len
), "2" (src
), "3" (dst
)
325 * copy from kernel space while checksumming, otherwise like csum_partial
329 csum_partial_copy_nocheck(const unsigned char *src
, unsigned char *dst
, int len
, int sum
)
331 unsigned long tmp1
, tmp2
;
332 __asm__("movel %2,%4\n\t"
333 "btst #1,%4\n\t" /* Check alignment */
335 "subql #2,%1\n\t" /* buff%4==2: treat first word */
337 "addql #2,%1\n\t" /* len was == 2, treat only rest */
340 "movew %2@+,%4\n\t" /* add first word to sum */
344 "addxl %4,%0\n" /* add X bit */
346 /* unrolled loop for the main part: do 8 longs at once */
347 "movel %1,%4\n\t" /* save len in tmp1 */
348 "lsrl #5,%1\n\t" /* len/32 */
349 "jeq 2f\n\t" /* not enough... */
378 "addxl %5,%0\n\t" /* add X bit */
383 "movel %4,%1\n\t" /* restore len from tmp1 */
384 "andw #0x1c,%4\n\t" /* number of rest longs */
389 /* loop for rest longs */
395 "addxl %5,%0\n" /* add X bit */
397 /* now check for rest bytes that do not fit into longs */
400 "clrl %5\n\t" /* clear tmp2 for rest bytes */
403 "movew %2@+,%5\n\t" /* have rest >= 2: get word */
405 "swap %5\n\t" /* into bits 16..31 */
406 "tstw %1\n\t" /* another byte? */
409 "moveb %2@,%5\n\t" /* have odd rest: get byte */
411 "lslw #8,%5\n" /* into bits 8..15; 16..31 untouched */
413 "addl %5,%0\n\t" /* now add rest long to sum */
415 "addxl %5,%0\n" /* add X bit */
417 : "=d" (sum
), "=d" (len
), "=a" (src
), "=a" (dst
),
418 "=&d" (tmp1
), "=&d" (tmp2
)
419 : "0" (sum
), "1" (len
), "2" (src
), "3" (dst
)