| /* $Id: checksum.S,v 1.10 2001/07/06 13:11:32 gniibe Exp $ | |
| * | |
| * INET An implementation of the TCP/IP protocol suite for the LINUX | |
| * operating system. INET is implemented using the BSD Socket | |
| * interface as the means of communication with the user level. | |
| * | |
| * IP/TCP/UDP checksumming routines | |
| * | |
| * Authors: Jorge Cwik, <jorge@laser.satlink.net> | |
| * Arnt Gulbrandsen, <agulbra@nvg.unit.no> | |
| * Tom May, <ftom@netcom.com> | |
| * Pentium Pro/II routines: | |
| * Alexander Kjeldaas <astor@guardian.no> | |
| * Finn Arne Gangstad <finnag@guardian.no> | |
| * Lots of code moved from tcp.c and ip.c; see those files | |
| * for more names. | |
| * | |
| * Changes: Ingo Molnar, converted csum_partial_copy() to 2.1 exception | |
| * handling. | |
| * Andi Kleen, add zeroing on error | |
| * converted to pure assembler | |
| * | |
| * SuperH version: Copyright (C) 1999 Niibe Yutaka | |
| * | |
| * This program is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU General Public License | |
| * as published by the Free Software Foundation; either version | |
| * 2 of the License, or (at your option) any later version. | |
| */ | |
| #include <asm/errno.h> | |
| #include <linux/linkage.h> | |
| /* | |
| * computes a partial checksum, e.g. for TCP/UDP fragments | |
| */ | |
| /* | |
| * asmlinkage __wsum csum_partial(const void *buf, int len, __wsum sum); | |
| */ | |
| .text | |
| ENTRY(csum_partial) | |
| /* | |
| * Experiments with Ethernet and SLIP connections show that buff | |
| * is aligned on either a 2-byte or 4-byte boundary. We get at | |
| * least a twofold speedup on 486 and Pentium if it is 4-byte aligned. | |
| * Fortunately, it is easy to convert 2-byte alignment to 4-byte | |
| * alignment for the unrolled loop. | |
| */ | |
| mov r4, r0 | |
| tst #3, r0 ! Check alignment. | |
| bt/s 2f ! Jump if alignment is ok. | |
| mov r4, r7 ! Keep a copy to check for alignment | |
| ! | |
| tst #1, r0 ! Check alignment. | |
| bt 21f ! Jump if alignment is boundary of 2bytes. | |
| ! buf is odd | |
| tst r5, r5 | |
| add #-1, r5 | |
| bt 9f | |
| mov.b @r4+, r0 | |
| extu.b r0, r0 | |
| addc r0, r6 ! t=0 from previous tst | |
| mov r6, r0 | |
| shll8 r6 | |
| shlr16 r0 | |
| shlr8 r0 | |
| or r0, r6 | |
| mov r4, r0 | |
| tst #2, r0 | |
| bt 2f | |
| 21: | |
| ! buf is 2 byte aligned (len could be 0) | |
| add #-2, r5 ! Alignment uses up two bytes. | |
| cmp/pz r5 ! | |
| bt/s 1f ! Jump if we had at least two bytes. | |
| clrt | |
| bra 6f | |
| add #2, r5 ! r5 was < 2. Deal with it. | |
| 1: | |
| mov.w @r4+, r0 | |
| extu.w r0, r0 | |
| addc r0, r6 | |
| bf 2f | |
| add #1, r6 | |
| 2: | |
| ! buf is 4 byte aligned (len could be 0) | |
| mov r5, r1 | |
| mov #-5, r0 | |
| shld r0, r1 | |
| tst r1, r1 | |
| bt/s 4f ! if it's =0, go to 4f | |
| clrt | |
| .align 2 | |
| 3: | |
| mov.l @r4+, r0 | |
| mov.l @r4+, r2 | |
| mov.l @r4+, r3 | |
| addc r0, r6 | |
| mov.l @r4+, r0 | |
| addc r2, r6 | |
| mov.l @r4+, r2 | |
| addc r3, r6 | |
| mov.l @r4+, r3 | |
| addc r0, r6 | |
| mov.l @r4+, r0 | |
| addc r2, r6 | |
| mov.l @r4+, r2 | |
| addc r3, r6 | |
| addc r0, r6 | |
| addc r2, r6 | |
| movt r0 | |
| dt r1 | |
| bf/s 3b | |
| cmp/eq #1, r0 | |
| ! here, we know r1==0 | |
| addc r1, r6 ! add carry to r6 | |
| 4: | |
| mov r5, r0 | |
| and #0x1c, r0 | |
| tst r0, r0 | |
| bt 6f | |
| ! 4 bytes or more remaining | |
| mov r0, r1 | |
| shlr2 r1 | |
| mov #0, r2 | |
| 5: | |
| addc r2, r6 | |
| mov.l @r4+, r2 | |
| movt r0 | |
| dt r1 | |
| bf/s 5b | |
| cmp/eq #1, r0 | |
| addc r2, r6 | |
| addc r1, r6 ! r1==0 here, so it means add carry-bit | |
| 6: | |
| ! 3 bytes or less remaining | |
| mov #3, r0 | |
| and r0, r5 | |
| tst r5, r5 | |
| bt 9f ! if it's =0 go to 9f | |
| mov #2, r1 | |
| cmp/hs r1, r5 | |
| bf 7f | |
| mov.w @r4+, r0 | |
| extu.w r0, r0 | |
| cmp/eq r1, r5 | |
| bt/s 8f | |
| clrt | |
| shll16 r0 | |
| addc r0, r6 | |
| 7: | |
| mov.b @r4+, r0 | |
| extu.b r0, r0 | |
| #ifndef __LITTLE_ENDIAN__ | |
| shll8 r0 | |
| #endif | |
| 8: | |
| addc r0, r6 | |
| mov #0, r0 | |
| addc r0, r6 | |
| 9: | |
| ! Check if the buffer was misaligned, if so realign sum | |
| mov r7, r0 | |
| tst #1, r0 | |
| bt 10f | |
| mov r6, r0 | |
| shll8 r6 | |
| shlr16 r0 | |
| shlr8 r0 | |
| or r0, r6 | |
| 10: | |
| rts | |
| mov r6, r0 | |
| /* | |
| unsigned int csum_partial_copy_generic (const char *src, char *dst, int len, | |
| int sum, int *src_err_ptr, int *dst_err_ptr) | |
| */ | |
| /* | |
| * Copy from ds while checksumming, otherwise like csum_partial | |
| * | |
| * The macros SRC and DST specify the type of access for the instruction. | |
| * thus we can call a custom exception handler for all access types. | |
| * | |
| * FIXME: could someone double-check whether I haven't mixed up some SRC and | |
| * DST definitions? It's damn hard to trigger all cases. I hope I got | |
| * them all but there's no guarantee. | |
| */ | |
| #define SRC(...) \ | |
| 9999: __VA_ARGS__ ; \ | |
| .section __ex_table, "a"; \ | |
| .long 9999b, 6001f ; \ | |
| .previous | |
| #define DST(...) \ | |
| 9999: __VA_ARGS__ ; \ | |
| .section __ex_table, "a"; \ | |
| .long 9999b, 6002f ; \ | |
| .previous | |
| ! | |
| ! r4: const char *SRC | |
| ! r5: char *DST | |
| ! r6: int LEN | |
| ! r7: int SUM | |
| ! | |
| ! on stack: | |
| ! int *SRC_ERR_PTR | |
| ! int *DST_ERR_PTR | |
| ! | |
| ENTRY(csum_partial_copy_generic) | |
| mov.l r5,@-r15 | |
| mov.l r6,@-r15 | |
| mov #3,r0 ! Check src and dest are equally aligned | |
| mov r4,r1 | |
| and r0,r1 | |
| and r5,r0 | |
| cmp/eq r1,r0 | |
| bf 3f ! Different alignments, use slow version | |
| tst #1,r0 ! Check dest word aligned | |
| bf 3f ! If not, do it the slow way | |
| mov #2,r0 | |
| tst r0,r5 ! Check dest alignment. | |
| bt 2f ! Jump if alignment is ok. | |
| add #-2,r6 ! Alignment uses up two bytes. | |
| cmp/pz r6 ! Jump if we had at least two bytes. | |
| bt/s 1f | |
| clrt | |
| add #2,r6 ! r6 was < 2. Deal with it. | |
| bra 4f | |
| mov r6,r2 | |
| 3: ! Handle different src and dest alignments. | |
| ! This is not common, so simple byte by byte copy will do. | |
| mov r6,r2 | |
| shlr r6 | |
| tst r6,r6 | |
| bt 4f | |
| clrt | |
| .align 2 | |
| 5: | |
| SRC( mov.b @r4+,r1 ) | |
| SRC( mov.b @r4+,r0 ) | |
| extu.b r1,r1 | |
| DST( mov.b r1,@r5 ) | |
| DST( mov.b r0,@(1,r5) ) | |
| extu.b r0,r0 | |
| add #2,r5 | |
| #ifdef __LITTLE_ENDIAN__ | |
| shll8 r0 | |
| #else | |
| shll8 r1 | |
| #endif | |
| or r1,r0 | |
| addc r0,r7 | |
| movt r0 | |
| dt r6 | |
| bf/s 5b | |
| cmp/eq #1,r0 | |
| mov #0,r0 | |
| addc r0, r7 | |
| mov r2, r0 | |
| tst #1, r0 | |
| bt 7f | |
| bra 5f | |
| clrt | |
| ! src and dest equally aligned, but to a two byte boundary. | |
| ! Handle first two bytes as a special case | |
| .align 2 | |
| 1: | |
| SRC( mov.w @r4+,r0 ) | |
| DST( mov.w r0,@r5 ) | |
| add #2,r5 | |
| extu.w r0,r0 | |
| addc r0,r7 | |
| mov #0,r0 | |
| addc r0,r7 | |
| 2: | |
| mov r6,r2 | |
| mov #-5,r0 | |
| shld r0,r6 | |
| tst r6,r6 | |
| bt/s 2f | |
| clrt | |
| .align 2 | |
| 1: | |
| SRC( mov.l @r4+,r0 ) | |
| SRC( mov.l @r4+,r1 ) | |
| addc r0,r7 | |
| DST( mov.l r0,@r5 ) | |
| DST( mov.l r1,@(4,r5) ) | |
| addc r1,r7 | |
| SRC( mov.l @r4+,r0 ) | |
| SRC( mov.l @r4+,r1 ) | |
| addc r0,r7 | |
| DST( mov.l r0,@(8,r5) ) | |
| DST( mov.l r1,@(12,r5) ) | |
| addc r1,r7 | |
| SRC( mov.l @r4+,r0 ) | |
| SRC( mov.l @r4+,r1 ) | |
| addc r0,r7 | |
| DST( mov.l r0,@(16,r5) ) | |
| DST( mov.l r1,@(20,r5) ) | |
| addc r1,r7 | |
| SRC( mov.l @r4+,r0 ) | |
| SRC( mov.l @r4+,r1 ) | |
| addc r0,r7 | |
| DST( mov.l r0,@(24,r5) ) | |
| DST( mov.l r1,@(28,r5) ) | |
| addc r1,r7 | |
| add #32,r5 | |
| movt r0 | |
| dt r6 | |
| bf/s 1b | |
| cmp/eq #1,r0 | |
| mov #0,r0 | |
| addc r0,r7 | |
| 2: mov r2,r6 | |
| mov #0x1c,r0 | |
| and r0,r6 | |
| cmp/pl r6 | |
| bf/s 4f | |
| clrt | |
| shlr2 r6 | |
| 3: | |
| SRC( mov.l @r4+,r0 ) | |
| addc r0,r7 | |
| DST( mov.l r0,@r5 ) | |
| add #4,r5 | |
| movt r0 | |
| dt r6 | |
| bf/s 3b | |
| cmp/eq #1,r0 | |
| mov #0,r0 | |
| addc r0,r7 | |
| 4: mov r2,r6 | |
| mov #3,r0 | |
| and r0,r6 | |
| cmp/pl r6 | |
| bf 7f | |
| mov #2,r1 | |
| cmp/hs r1,r6 | |
| bf 5f | |
| SRC( mov.w @r4+,r0 ) | |
| DST( mov.w r0,@r5 ) | |
| extu.w r0,r0 | |
| add #2,r5 | |
| cmp/eq r1,r6 | |
| bt/s 6f | |
| clrt | |
| shll16 r0 | |
| addc r0,r7 | |
| 5: | |
| SRC( mov.b @r4+,r0 ) | |
| DST( mov.b r0,@r5 ) | |
| extu.b r0,r0 | |
| #ifndef __LITTLE_ENDIAN__ | |
| shll8 r0 | |
| #endif | |
| 6: addc r0,r7 | |
| mov #0,r0 | |
| addc r0,r7 | |
| 7: | |
| 5000: | |
| # Exception handler: | |
| .section .fixup, "ax" | |
| 6001: | |
| mov.l @(8,r15),r0 ! src_err_ptr | |
| mov #-EFAULT,r1 | |
| mov.l r1,@r0 | |
| ! zero the complete destination - computing the rest | |
| ! is too much work | |
| mov.l @(4,r15),r5 ! dst | |
| mov.l @r15,r6 ! len | |
| mov #0,r7 | |
| 1: mov.b r7,@r5 | |
| dt r6 | |
| bf/s 1b | |
| add #1,r5 | |
| mov.l 8000f,r0 | |
| jmp @r0 | |
| nop | |
| .align 2 | |
| 8000: .long 5000b | |
| 6002: | |
| mov.l @(12,r15),r0 ! dst_err_ptr | |
| mov #-EFAULT,r1 | |
| mov.l r1,@r0 | |
| mov.l 8001f,r0 | |
| jmp @r0 | |
| nop | |
| .align 2 | |
| 8001: .long 5000b | |
| .previous | |
| add #8,r15 | |
| rts | |
| mov r7,r0 |