Merge branch 'master'
[linux-2.6] / include / asm-s390 / checksum.h
1 #ifndef _S390_CHECKSUM_H
2 #define _S390_CHECKSUM_H
3
4 /*
5  *  include/asm-s390/checksum.h
6  *    S390 fast network checksum routines
7  *    see also arch/S390/lib/checksum.c
8  *
9  *  S390 version
10  *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
11  *    Author(s): Ulrich Hild        (first version)
12  *               Martin Schwidefsky (heavily optimized CKSM version)
13  *               D.J. Barrow        (third attempt) 
14  */
15
16 #include <asm/uaccess.h>
17
18 /*
19  * computes the checksum of a memory block at buff, length len,
20  * and adds in "sum" (32-bit)
21  *
22  * returns a 32-bit number suitable for feeding into itself
23  * or csum_tcpudp_magic
24  *
25  * this function must be called with even lengths, except
26  * for the last fragment, which may be odd
27  *
28  * it's best to have buff aligned on a 32-bit boundary
29  */
30 static inline unsigned int
31 csum_partial(const unsigned char * buff, int len, unsigned int sum)
32 {
33         /*
34          * Experiments with ethernet and slip connections show that buf
35          * is aligned on either a 2-byte or 4-byte boundary.
36          */
37 #ifndef __s390x__
38         register_pair rp;
39
40         rp.subreg.even = (unsigned long) buff;
41         rp.subreg.odd = (unsigned long) len;
42         __asm__ __volatile__ (
43                 "0:  cksm %0,%1\n"      /* do checksum on longs */
44                 "    jo   0b\n"
45                 : "+&d" (sum), "+&a" (rp) : : "cc", "memory" );
46 #else /* __s390x__ */
47         __asm__ __volatile__ (
48                 "    lgr  2,%1\n"    /* address in gpr 2 */
49                 "    lgfr 3,%2\n"    /* length in gpr 3 */
50                 "0:  cksm %0,2\n"    /* do checksum on longs */
51                 "    jo   0b\n"
52                 : "+&d" (sum)
53                 : "d" (buff), "d" (len)
54                 : "cc", "memory", "2", "3" );
55 #endif /* __s390x__ */
56         return sum;
57 }
58
59 /*
60  * csum_partial as an inline function
61  */
62 static inline unsigned int 
63 csum_partial_inline(const unsigned char * buff, int len, unsigned int sum)
64 {
65 #ifndef __s390x__
66         register_pair rp;
67
68         rp.subreg.even = (unsigned long) buff;
69         rp.subreg.odd = (unsigned long) len;
70         __asm__ __volatile__ (
71                 "0:  cksm %0,%1\n"    /* do checksum on longs */
72                 "    jo   0b\n"
73                 : "+&d" (sum), "+&a" (rp) : : "cc", "memory" );
74 #else /* __s390x__ */
75         __asm__ __volatile__ (
76                 "    lgr  2,%1\n"    /* address in gpr 2 */
77                 "    lgfr 3,%2\n"    /* length in gpr 3 */
78                 "0:  cksm %0,2\n"    /* do checksum on longs */
79                 "    jo   0b\n"
80                 : "+&d" (sum)
81                 : "d" (buff), "d" (len)
82                 : "cc", "memory", "2", "3" );
83 #endif /* __s390x__ */
84         return sum;
85 }
86
87 /*
88  * the same as csum_partial_copy, but copies from user space.
89  *
90  * here even more important to align src and dst on a 32-bit (or even
91  * better 64-bit) boundary
92  *
93  * Copy from userspace and compute checksum.  If we catch an exception
94  * then zero the rest of the buffer.
95  */
96 static inline unsigned int
97 csum_partial_copy_from_user(const char __user *src, char *dst,
98                                           int len, unsigned int sum,
99                                           int *err_ptr)
100 {
101         int missing;
102
103         missing = copy_from_user(dst, src, len);
104         if (missing) {
105                 memset(dst + len - missing, 0, missing);
106                 *err_ptr = -EFAULT;
107         }
108                 
109         return csum_partial(dst, len, sum);
110 }
111
112
113 static inline unsigned int
114 csum_partial_copy_nocheck (const char *src, char *dst, int len, unsigned int sum)
115 {
116         memcpy(dst,src,len);
117         return csum_partial_inline(dst, len, sum);
118 }
119
120 /*
121  *      Fold a partial checksum without adding pseudo headers
122  */
123 static inline unsigned short
124 csum_fold(unsigned int sum)
125 {
126 #ifndef __s390x__
127         register_pair rp;
128
129         __asm__ __volatile__ (
130                 "    slr  %N1,%N1\n" /* %0 = H L */
131                 "    lr   %1,%0\n"   /* %0 = H L, %1 = H L 0 0 */
132                 "    srdl %1,16\n"   /* %0 = H L, %1 = 0 H L 0 */
133                 "    alr  %1,%N1\n"  /* %0 = H L, %1 = L H L 0 */
134                 "    alr  %0,%1\n"   /* %0 = H+L+C L+H */
135                 "    srl  %0,16\n"   /* %0 = H+L+C */
136                 : "+&d" (sum), "=d" (rp) : : "cc" );
137 #else /* __s390x__ */
138         __asm__ __volatile__ (
139                 "    sr   3,3\n"   /* %0 = H*65536 + L */
140                 "    lr   2,%0\n"  /* %0 = H L, R2/R3 = H L / 0 0 */
141                 "    srdl 2,16\n"  /* %0 = H L, R2/R3 = 0 H / L 0 */
142                 "    alr  2,3\n"   /* %0 = H L, R2/R3 = L H / L 0 */
143                 "    alr  %0,2\n"  /* %0 = H+L+C L+H */
144                 "    srl  %0,16\n" /* %0 = H+L+C */
145                 : "+&d" (sum) : : "cc", "2", "3");
146 #endif /* __s390x__ */
147         return ((unsigned short) ~sum);
148 }
149
150 /*
151  *      This is a version of ip_compute_csum() optimized for IP headers,
152  *      which always checksum on 4 octet boundaries.
153  *
154  */
155 static inline unsigned short
156 ip_fast_csum(unsigned char *iph, unsigned int ihl)
157 {
158         unsigned long sum;
159 #ifndef __s390x__
160         register_pair rp;
161
162         rp.subreg.even = (unsigned long) iph;
163         rp.subreg.odd = (unsigned long) ihl*4;
164         __asm__ __volatile__ (
165                 "    sr   %0,%0\n"   /* set sum to zero */
166                 "0:  cksm %0,%1\n"   /* do checksum on longs */
167                 "    jo   0b\n"
168                 : "=&d" (sum), "+&a" (rp) : : "cc", "memory" );
169 #else /* __s390x__ */
170         __asm__ __volatile__ (
171                 "    slgr %0,%0\n"   /* set sum to zero */
172                 "    lgr  2,%1\n"    /* address in gpr 2 */
173                 "    lgfr 3,%2\n"    /* length in gpr 3 */
174                 "0:  cksm %0,2\n"    /* do checksum on ints */
175                 "    jo   0b\n"
176                 : "=&d" (sum)
177                 : "d" (iph), "d" (ihl*4)
178                 : "cc", "memory", "2", "3" );
179 #endif /* __s390x__ */
180         return csum_fold(sum);
181 }
182
183 /*
184  * computes the checksum of the TCP/UDP pseudo-header
185  * returns a 32-bit checksum
186  */
187 static inline unsigned int 
188 csum_tcpudp_nofold(unsigned long saddr, unsigned long daddr,
189                    unsigned short len, unsigned short proto,
190                    unsigned int sum)
191 {
192 #ifndef __s390x__
193         __asm__ __volatile__ (
194                 "    alr   %0,%1\n"  /* sum += saddr */
195                 "    brc   12,0f\n"
196                 "    ahi   %0,1\n"   /* add carry */
197                 "0:"
198                 : "+&d" (sum) : "d" (saddr) : "cc" );
199         __asm__ __volatile__ (
200                 "    alr   %0,%1\n"  /* sum += daddr */
201                 "    brc   12,1f\n"
202                 "    ahi   %0,1\n"   /* add carry */
203                 "1:"
204                 : "+&d" (sum) : "d" (daddr) : "cc" );
205         __asm__ __volatile__ (
206                 "    alr   %0,%1\n"  /* sum += (len<<16) + (proto<<8) */
207                 "    brc   12,2f\n"
208                 "    ahi   %0,1\n"   /* add carry */
209                 "2:"
210                 : "+&d" (sum)
211                 : "d" (((unsigned int) len<<16) + (unsigned int) proto)
212                 : "cc" );
213 #else /* __s390x__ */
214         __asm__ __volatile__ (
215                 "    lgfr  %0,%0\n"
216                 "    algr  %0,%1\n"  /* sum += saddr */
217                 "    brc   12,0f\n"
218                 "    aghi  %0,1\n"   /* add carry */
219                 "0:  algr  %0,%2\n"  /* sum += daddr */
220                 "    brc   12,1f\n"
221                 "    aghi  %0,1\n"   /* add carry */
222                 "1:  algfr %0,%3\n"  /* sum += (len<<16) + proto */
223                 "    brc   12,2f\n"
224                 "    aghi  %0,1\n"   /* add carry */
225                 "2:  srlg  0,%0,32\n"
226                 "    alr   %0,0\n"   /* fold to 32 bits */
227                 "    brc   12,3f\n"
228                 "    ahi   %0,1\n"   /* add carry */
229                 "3:  llgfr %0,%0"
230                 : "+&d" (sum)
231                 : "d" (saddr), "d" (daddr),
232                   "d" (((unsigned int) len<<16) + (unsigned int) proto)
233                 : "cc", "0" );
234 #endif /* __s390x__ */
235         return sum;
236 }
237
238 /*
239  * computes the checksum of the TCP/UDP pseudo-header
240  * returns a 16-bit checksum, already complemented
241  */
242
243 static inline unsigned short int
244 csum_tcpudp_magic(unsigned long saddr, unsigned long daddr,
245                   unsigned short len, unsigned short proto,
246                   unsigned int sum)
247 {
248         return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
249 }
250
251 /*
252  * this routine is used for miscellaneous IP-like checksums, mainly
253  * in icmp.c
254  */
255
256 static inline unsigned short
257 ip_compute_csum(unsigned char * buff, int len)
258 {
259         return csum_fold(csum_partial(buff, len, 0));
260 }
261
262 #endif /* _S390_CHECKSUM_H */
263
264