diff options
Diffstat (limited to 'include/asm-s390/checksum.h')
-rw-r--r-- | include/asm-s390/checksum.h | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/include/asm-s390/checksum.h b/include/asm-s390/checksum.h new file mode 100644 index 000000000000..471f2af2b16a --- /dev/null +++ b/include/asm-s390/checksum.h | |||
@@ -0,0 +1,264 @@ | |||
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 | |||