322 строки
6.8 KiB
ArmAsm
322 строки
6.8 KiB
ArmAsm
/*
|
|
* 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
|
|
* Hirokazu Takata,Hiroyuki Kondo rewrite for the m32r architecture.
|
|
*
|
|
* 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.
|
|
*/
|
|
/* $Id$ */
|
|
|
|
|
|
#include <linux/linkage.h>
|
|
#include <asm/assembler.h>
|
|
#include <asm/errno.h>
|
|
|
|
/*
|
|
* computes a partial checksum, e.g. for TCP/UDP fragments
|
|
*/
|
|
|
|
/*
|
|
unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum)
|
|
*/
|
|
|
|
|
|
#ifdef CONFIG_ISA_DUAL_ISSUE
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
.text
|
|
ENTRY(csum_partial)
|
|
; Function args
|
|
; r0: unsigned char *buff
|
|
; r1: int len
|
|
; r2: unsigned int sum
|
|
|
|
push r2 || ldi r2, #0
|
|
and3 r7, r0, #1 ; Check alignment.
|
|
beqz r7, 1f ; Jump if alignment is ok.
|
|
; 1-byte mis aligned
|
|
ldub r4, @r0 || addi r0, #1
|
|
; clear c-bit || Alignment uses up bytes.
|
|
cmp r0, r0 || addi r1, #-1
|
|
ldi r3, #0 || addx r2, r4
|
|
addx r2, r3
|
|
.fillinsn
|
|
1:
|
|
and3 r4, r0, #2 ; Check alignment.
|
|
beqz r4, 2f ; Jump if alignment is ok.
|
|
; clear c-bit || Alignment uses up two bytes.
|
|
cmp r0, r0 || addi r1, #-2
|
|
bgtz r1, 1f ; Jump if we had at least two bytes.
|
|
bra 4f || addi r1, #2
|
|
.fillinsn ; len(r1) was < 2. Deal with it.
|
|
1:
|
|
; 2-byte aligned
|
|
lduh r4, @r0 || ldi r3, #0
|
|
addx r2, r4 || addi r0, #2
|
|
addx r2, r3
|
|
.fillinsn
|
|
2:
|
|
; 4-byte aligned
|
|
cmp r0, r0 ; clear c-bit
|
|
srl3 r6, r1, #5
|
|
beqz r6, 2f
|
|
.fillinsn
|
|
|
|
1: ld r3, @r0+
|
|
ld r4, @r0+ ; +4
|
|
ld r5, @r0+ ; +8
|
|
ld r3, @r0+ || addx r2, r3 ; +12
|
|
ld r4, @r0+ || addx r2, r4 ; +16
|
|
ld r5, @r0+ || addx r2, r5 ; +20
|
|
ld r3, @r0+ || addx r2, r3 ; +24
|
|
ld r4, @r0+ || addx r2, r4 ; +28
|
|
addx r2, r5 || addi r6, #-1
|
|
addx r2, r3
|
|
addx r2, r4
|
|
bnez r6, 1b
|
|
|
|
addx r2, r6 ; r6=0
|
|
cmp r0, r0 ; This clears c-bit
|
|
.fillinsn
|
|
2: and3 r6, r1, #0x1c ; withdraw len
|
|
beqz r6, 4f
|
|
srli r6, #2
|
|
.fillinsn
|
|
|
|
3: ld r4, @r0+ || addi r6, #-1
|
|
addx r2, r4
|
|
bnez r6, 3b
|
|
|
|
addx r2, r6 ; r6=0
|
|
cmp r0, r0 ; This clears c-bit
|
|
.fillinsn
|
|
4: and3 r1, r1, #3
|
|
beqz r1, 7f ; if len == 0 goto end
|
|
and3 r6, r1, #2
|
|
beqz r6, 5f ; if len < 2 goto 5f(1byte)
|
|
lduh r4, @r0 || addi r0, #2
|
|
addi r1, #-2 || slli r4, #16
|
|
addx r2, r4
|
|
beqz r1, 6f
|
|
.fillinsn
|
|
5: ldub r4, @r0 || ldi r1, #0
|
|
#ifndef __LITTLE_ENDIAN__
|
|
slli r4, #8
|
|
#endif
|
|
addx r2, r4
|
|
.fillinsn
|
|
6: addx r2, r1
|
|
.fillinsn
|
|
7:
|
|
and3 r0, r2, #0xffff
|
|
srli r2, #16
|
|
add r0, r2
|
|
srl3 r2, r0, #16
|
|
beqz r2, 1f
|
|
addi r0, #1
|
|
and3 r0, r0, #0xffff
|
|
.fillinsn
|
|
1:
|
|
beqz r7, 1f ; swap the upper byte for the lower
|
|
and3 r2, r0, #0xff
|
|
srl3 r0, r0, #8
|
|
slli r2, #8
|
|
or r0, r2
|
|
.fillinsn
|
|
1:
|
|
pop r2 || cmp r0, r0
|
|
addx r0, r2 || ldi r2, #0
|
|
addx r0, r2
|
|
jmp r14
|
|
|
|
#else /* not CONFIG_ISA_DUAL_ISSUE */
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
.text
|
|
ENTRY(csum_partial)
|
|
; Function args
|
|
; r0: unsigned char *buff
|
|
; r1: int len
|
|
; r2: unsigned int sum
|
|
|
|
push r2
|
|
ldi r2, #0
|
|
and3 r7, r0, #1 ; Check alignment.
|
|
beqz r7, 1f ; Jump if alignment is ok.
|
|
; 1-byte mis aligned
|
|
ldub r4, @r0
|
|
addi r0, #1
|
|
addi r1, #-1 ; Alignment uses up bytes.
|
|
cmp r0, r0 ; clear c-bit
|
|
ldi r3, #0
|
|
addx r2, r4
|
|
addx r2, r3
|
|
.fillinsn
|
|
1:
|
|
and3 r4, r0, #2 ; Check alignment.
|
|
beqz r4, 2f ; Jump if alignment is ok.
|
|
addi r1, #-2 ; Alignment uses up two bytes.
|
|
cmp r0, r0 ; clear c-bit
|
|
bgtz r1, 1f ; Jump if we had at least two bytes.
|
|
addi r1, #2 ; len(r1) was < 2. Deal with it.
|
|
bra 4f
|
|
.fillinsn
|
|
1:
|
|
; 2-byte aligned
|
|
lduh r4, @r0
|
|
addi r0, #2
|
|
ldi r3, #0
|
|
addx r2, r4
|
|
addx r2, r3
|
|
.fillinsn
|
|
2:
|
|
; 4-byte aligned
|
|
cmp r0, r0 ; clear c-bit
|
|
srl3 r6, r1, #5
|
|
beqz r6, 2f
|
|
.fillinsn
|
|
|
|
1: ld r3, @r0+
|
|
ld r4, @r0+ ; +4
|
|
ld r5, @r0+ ; +8
|
|
addx r2, r3
|
|
addx r2, r4
|
|
addx r2, r5
|
|
ld r3, @r0+ ; +12
|
|
ld r4, @r0+ ; +16
|
|
ld r5, @r0+ ; +20
|
|
addx r2, r3
|
|
addx r2, r4
|
|
addx r2, r5
|
|
ld r3, @r0+ ; +24
|
|
ld r4, @r0+ ; +28
|
|
addi r6, #-1
|
|
addx r2, r3
|
|
addx r2, r4
|
|
bnez r6, 1b
|
|
addx r2, r6 ; r6=0
|
|
cmp r0, r0 ; This clears c-bit
|
|
.fillinsn
|
|
|
|
2: and3 r6, r1, #0x1c ; withdraw len
|
|
beqz r6, 4f
|
|
srli r6, #2
|
|
.fillinsn
|
|
|
|
3: ld r4, @r0+
|
|
addi r6, #-1
|
|
addx r2, r4
|
|
bnez r6, 3b
|
|
addx r2, r6 ; r6=0
|
|
cmp r0, r0 ; This clears c-bit
|
|
.fillinsn
|
|
|
|
4: and3 r1, r1, #3
|
|
beqz r1, 7f ; if len == 0 goto end
|
|
and3 r6, r1, #2
|
|
beqz r6, 5f ; if len < 2 goto 5f(1byte)
|
|
|
|
lduh r4, @r0
|
|
addi r0, #2
|
|
addi r1, #-2
|
|
slli r4, #16
|
|
addx r2, r4
|
|
beqz r1, 6f
|
|
.fillinsn
|
|
5: ldub r4, @r0
|
|
#ifndef __LITTLE_ENDIAN__
|
|
slli r4, #8
|
|
#endif
|
|
addx r2, r4
|
|
.fillinsn
|
|
6: ldi r5, #0
|
|
addx r2, r5
|
|
.fillinsn
|
|
7:
|
|
and3 r0, r2, #0xffff
|
|
srli r2, #16
|
|
add r0, r2
|
|
srl3 r2, r0, #16
|
|
beqz r2, 1f
|
|
addi r0, #1
|
|
and3 r0, r0, #0xffff
|
|
.fillinsn
|
|
1:
|
|
beqz r7, 1f
|
|
mv r2, r0
|
|
srl3 r0, r2, #8
|
|
and3 r2, r2, #0xff
|
|
slli r2, #8
|
|
or r0, r2
|
|
.fillinsn
|
|
1:
|
|
pop r2
|
|
cmp r0, r0
|
|
addx r0, r2
|
|
ldi r2, #0
|
|
addx r0, r2
|
|
jmp r14
|
|
|
|
#endif /* not CONFIG_ISA_DUAL_ISSUE */
|
|
|
|
/*
|
|
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.
|
|
*/
|
|
|
|
ENTRY(csum_partial_copy_generic)
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
jmp r14
|
|
nop
|
|
nop
|
|
nop
|
|
|