1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * INET An implementation of the TCP/IP protocol suite for the LINUX
4*4882a593Smuzhiyun * operating system. INET is implemented using the BSD Socket
5*4882a593Smuzhiyun * interface as the means of communication with the user level.
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * MIPS specific IP/TCP/UDP checksumming routines
8*4882a593Smuzhiyun *
9*4882a593Smuzhiyun * Authors: Ralf Baechle, <ralf@waldorf-gmbh.de>
10*4882a593Smuzhiyun * Lots of code moved from tcp.c and ip.c; see those files
11*4882a593Smuzhiyun * for more names.
12*4882a593Smuzhiyun */
13*4882a593Smuzhiyun #include <linux/module.h>
14*4882a593Smuzhiyun #include <linux/types.h>
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun #include <net/checksum.h>
17*4882a593Smuzhiyun #include <asm/byteorder.h>
18*4882a593Smuzhiyun #include <asm/string.h>
19*4882a593Smuzhiyun #include <linux/uaccess.h>
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun #define addc(_t,_r) \
22*4882a593Smuzhiyun __asm__ __volatile__ ( \
23*4882a593Smuzhiyun " add %0, %1, %0\n" \
24*4882a593Smuzhiyun " addc %0, %%r0, %0\n" \
25*4882a593Smuzhiyun : "=r"(_t) \
26*4882a593Smuzhiyun : "r"(_r), "0"(_t));
27*4882a593Smuzhiyun
from32to16(unsigned int x)28*4882a593Smuzhiyun static inline unsigned short from32to16(unsigned int x)
29*4882a593Smuzhiyun {
30*4882a593Smuzhiyun /* 32 bits --> 16 bits + carry */
31*4882a593Smuzhiyun x = (x & 0xffff) + (x >> 16);
32*4882a593Smuzhiyun /* 16 bits + carry --> 16 bits including carry */
33*4882a593Smuzhiyun x = (x & 0xffff) + (x >> 16);
34*4882a593Smuzhiyun return (unsigned short)x;
35*4882a593Smuzhiyun }
36*4882a593Smuzhiyun
do_csum(const unsigned char * buff,int len)37*4882a593Smuzhiyun static inline unsigned int do_csum(const unsigned char * buff, int len)
38*4882a593Smuzhiyun {
39*4882a593Smuzhiyun int odd, count;
40*4882a593Smuzhiyun unsigned int result = 0;
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun if (len <= 0)
43*4882a593Smuzhiyun goto out;
44*4882a593Smuzhiyun odd = 1 & (unsigned long) buff;
45*4882a593Smuzhiyun if (odd) {
46*4882a593Smuzhiyun result = be16_to_cpu(*buff);
47*4882a593Smuzhiyun len--;
48*4882a593Smuzhiyun buff++;
49*4882a593Smuzhiyun }
50*4882a593Smuzhiyun count = len >> 1; /* nr of 16-bit words.. */
51*4882a593Smuzhiyun if (count) {
52*4882a593Smuzhiyun if (2 & (unsigned long) buff) {
53*4882a593Smuzhiyun result += *(unsigned short *) buff;
54*4882a593Smuzhiyun count--;
55*4882a593Smuzhiyun len -= 2;
56*4882a593Smuzhiyun buff += 2;
57*4882a593Smuzhiyun }
58*4882a593Smuzhiyun count >>= 1; /* nr of 32-bit words.. */
59*4882a593Smuzhiyun if (count) {
60*4882a593Smuzhiyun while (count >= 4) {
61*4882a593Smuzhiyun unsigned int r1, r2, r3, r4;
62*4882a593Smuzhiyun r1 = *(unsigned int *)(buff + 0);
63*4882a593Smuzhiyun r2 = *(unsigned int *)(buff + 4);
64*4882a593Smuzhiyun r3 = *(unsigned int *)(buff + 8);
65*4882a593Smuzhiyun r4 = *(unsigned int *)(buff + 12);
66*4882a593Smuzhiyun addc(result, r1);
67*4882a593Smuzhiyun addc(result, r2);
68*4882a593Smuzhiyun addc(result, r3);
69*4882a593Smuzhiyun addc(result, r4);
70*4882a593Smuzhiyun count -= 4;
71*4882a593Smuzhiyun buff += 16;
72*4882a593Smuzhiyun }
73*4882a593Smuzhiyun while (count) {
74*4882a593Smuzhiyun unsigned int w = *(unsigned int *) buff;
75*4882a593Smuzhiyun count--;
76*4882a593Smuzhiyun buff += 4;
77*4882a593Smuzhiyun addc(result, w);
78*4882a593Smuzhiyun }
79*4882a593Smuzhiyun result = (result & 0xffff) + (result >> 16);
80*4882a593Smuzhiyun }
81*4882a593Smuzhiyun if (len & 2) {
82*4882a593Smuzhiyun result += *(unsigned short *) buff;
83*4882a593Smuzhiyun buff += 2;
84*4882a593Smuzhiyun }
85*4882a593Smuzhiyun }
86*4882a593Smuzhiyun if (len & 1)
87*4882a593Smuzhiyun result += le16_to_cpu(*buff);
88*4882a593Smuzhiyun result = from32to16(result);
89*4882a593Smuzhiyun if (odd)
90*4882a593Smuzhiyun result = swab16(result);
91*4882a593Smuzhiyun out:
92*4882a593Smuzhiyun return result;
93*4882a593Smuzhiyun }
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun /*
96*4882a593Smuzhiyun * computes a partial checksum, e.g. for TCP/UDP fragments
97*4882a593Smuzhiyun */
98*4882a593Smuzhiyun /*
99*4882a593Smuzhiyun * why bother folding?
100*4882a593Smuzhiyun */
csum_partial(const void * buff,int len,__wsum sum)101*4882a593Smuzhiyun __wsum csum_partial(const void *buff, int len, __wsum sum)
102*4882a593Smuzhiyun {
103*4882a593Smuzhiyun unsigned int result = do_csum(buff, len);
104*4882a593Smuzhiyun addc(result, sum);
105*4882a593Smuzhiyun return (__force __wsum)from32to16(result);
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun EXPORT_SYMBOL(csum_partial);
109