1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * INET An implementation of the TCP/IP protocol suite for the LINUX |
4 | * operating system. INET is implemented using the BSD Socket |
5 | * interface as the means of communication with the user level. |
6 | * |
7 | * IP/TCP/UDP checksumming routines |
8 | * |
9 | * Authors: Jorge Cwik, <jorge@laser.satlink.net> |
10 | * Arnt Gulbrandsen, <agulbra@nvg.unit.no> |
11 | * Tom May, <ftom@netcom.com> |
12 | * Andreas Schwab, <schwab@issan.informatik.uni-dortmund.de> |
13 | * Lots of code moved from tcp.c and ip.c; see those files |
14 | * for more names. |
15 | * |
16 | * 03/02/96 Jes Sorensen, Andreas Schwab, Roman Hodek: |
17 | * Fixed some nasty bugs, causing some horrible crashes. |
18 | * A: At some points, the sum (%0) was used as |
19 | * length-counter instead of the length counter |
20 | * (%1). Thanks to Roman Hodek for pointing this out. |
21 | * B: GCC seems to mess up if one uses too many |
22 | * data-registers to hold input values and one tries to |
23 | * specify d0 and d1 as scratch registers. Letting gcc |
24 | * choose these registers itself solves the problem. |
25 | * |
26 | * 1998/8/31 Andreas Schwab: |
27 | * Zero out rest of buffer on exception in |
28 | * csum_partial_copy_from_user. |
29 | */ |
30 | |
31 | #include <linux/module.h> |
32 | #include <net/checksum.h> |
33 | |
34 | /* |
35 | * computes a partial checksum, e.g. for TCP/UDP fragments |
36 | */ |
37 | |
38 | __wsum csum_partial(const void *buff, int len, __wsum sum) |
39 | { |
40 | unsigned long tmp1, tmp2; |
41 | /* |
42 | * Experiments with ethernet and slip connections show that buff |
43 | * is aligned on either a 2-byte or 4-byte boundary. |
44 | */ |
45 | __asm__("movel %2,%3\n\t" |
46 | "btst #1,%3\n\t" /* Check alignment */ |
47 | "jeq 2f\n\t" |
48 | "subql #2,%1\n\t" /* buff%4==2: treat first word */ |
49 | "jgt 1f\n\t" |
50 | "addql #2,%1\n\t" /* len was == 2, treat only rest */ |
51 | "jra 4f\n" |
52 | "1:\t" |
53 | "addw %2@+,%0\n\t" /* add first word to sum */ |
54 | "clrl %3\n\t" |
55 | "addxl %3,%0\n" /* add X bit */ |
56 | "2:\t" |
57 | /* unrolled loop for the main part: do 8 longs at once */ |
58 | "movel %1,%3\n\t" /* save len in tmp1 */ |
59 | "lsrl #5,%1\n\t" /* len/32 */ |
60 | "jeq 2f\n\t" /* not enough... */ |
61 | "subql #1,%1\n" |
62 | "1:\t" |
63 | "movel %2@+,%4\n\t" |
64 | "addxl %4,%0\n\t" |
65 | "movel %2@+,%4\n\t" |
66 | "addxl %4,%0\n\t" |
67 | "movel %2@+,%4\n\t" |
68 | "addxl %4,%0\n\t" |
69 | "movel %2@+,%4\n\t" |
70 | "addxl %4,%0\n\t" |
71 | "movel %2@+,%4\n\t" |
72 | "addxl %4,%0\n\t" |
73 | "movel %2@+,%4\n\t" |
74 | "addxl %4,%0\n\t" |
75 | "movel %2@+,%4\n\t" |
76 | "addxl %4,%0\n\t" |
77 | "movel %2@+,%4\n\t" |
78 | "addxl %4,%0\n\t" |
79 | "dbra %1,1b\n\t" |
80 | "clrl %4\n\t" |
81 | "addxl %4,%0\n\t" /* add X bit */ |
82 | "clrw %1\n\t" |
83 | "subql #1,%1\n\t" |
84 | "jcc 1b\n" |
85 | "2:\t" |
86 | "movel %3,%1\n\t" /* restore len from tmp1 */ |
87 | "andw #0x1c,%3\n\t" /* number of rest longs */ |
88 | "jeq 4f\n\t" |
89 | "lsrw #2,%3\n\t" |
90 | "subqw #1,%3\n" |
91 | "3:\t" |
92 | /* loop for rest longs */ |
93 | "movel %2@+,%4\n\t" |
94 | "addxl %4,%0\n\t" |
95 | "dbra %3,3b\n\t" |
96 | "clrl %4\n\t" |
97 | "addxl %4,%0\n" /* add X bit */ |
98 | "4:\t" |
99 | /* now check for rest bytes that do not fit into longs */ |
100 | "andw #3,%1\n\t" |
101 | "jeq 7f\n\t" |
102 | "clrl %4\n\t" /* clear tmp2 for rest bytes */ |
103 | "subqw #2,%1\n\t" |
104 | "jlt 5f\n\t" |
105 | "movew %2@+,%4\n\t" /* have rest >= 2: get word */ |
106 | "swap %4\n\t" /* into bits 16..31 */ |
107 | "tstw %1\n\t" /* another byte? */ |
108 | "jeq 6f\n" |
109 | "5:\t" |
110 | "moveb %2@,%4\n\t" /* have odd rest: get byte */ |
111 | "lslw #8,%4\n\t" /* into bits 8..15; 16..31 untouched */ |
112 | "6:\t" |
113 | "addl %4,%0\n\t" /* now add rest long to sum */ |
114 | "clrl %4\n\t" |
115 | "addxl %4,%0\n" /* add X bit */ |
116 | "7:\t" |
117 | : "=d" (sum), "=d" (len), "=a" (buff), |
118 | "=&d" (tmp1), "=&d" (tmp2) |
119 | : "0" (sum), "1" (len), "2" (buff) |
120 | ); |
121 | return(sum); |
122 | } |
123 | |
124 | EXPORT_SYMBOL(csum_partial); |
125 | |
126 | |
127 | /* |
128 | * copy from user space while checksumming, with exception handling. |
129 | */ |
130 | |
131 | __wsum |
132 | csum_and_copy_from_user(const void __user *src, void *dst, int len) |
133 | { |
134 | /* |
135 | * GCC doesn't like more than 10 operands for the asm |
136 | * statements so we have to use tmp2 for the error |
137 | * code. |
138 | */ |
139 | unsigned long tmp1, tmp2; |
140 | __wsum sum = ~0U; |
141 | |
142 | __asm__("movel %2,%4\n\t" |
143 | "btst #1,%4\n\t" /* Check alignment */ |
144 | "jeq 2f\n\t" |
145 | "subql #2,%1\n\t" /* buff%4==2: treat first word */ |
146 | "jgt 1f\n\t" |
147 | "addql #2,%1\n\t" /* len was == 2, treat only rest */ |
148 | "jra 4f\n" |
149 | "1:\n" |
150 | "10:\t" |
151 | "movesw %2@+,%4\n\t" /* add first word to sum */ |
152 | "addw %4,%0\n\t" |
153 | "movew %4,%3@+\n\t" |
154 | "clrl %4\n\t" |
155 | "addxl %4,%0\n" /* add X bit */ |
156 | "2:\t" |
157 | /* unrolled loop for the main part: do 8 longs at once */ |
158 | "movel %1,%4\n\t" /* save len in tmp1 */ |
159 | "lsrl #5,%1\n\t" /* len/32 */ |
160 | "jeq 2f\n\t" /* not enough... */ |
161 | "subql #1,%1\n" |
162 | "1:\n" |
163 | "11:\t" |
164 | "movesl %2@+,%5\n\t" |
165 | "addxl %5,%0\n\t" |
166 | "movel %5,%3@+\n\t" |
167 | "12:\t" |
168 | "movesl %2@+,%5\n\t" |
169 | "addxl %5,%0\n\t" |
170 | "movel %5,%3@+\n\t" |
171 | "13:\t" |
172 | "movesl %2@+,%5\n\t" |
173 | "addxl %5,%0\n\t" |
174 | "movel %5,%3@+\n\t" |
175 | "14:\t" |
176 | "movesl %2@+,%5\n\t" |
177 | "addxl %5,%0\n\t" |
178 | "movel %5,%3@+\n\t" |
179 | "15:\t" |
180 | "movesl %2@+,%5\n\t" |
181 | "addxl %5,%0\n\t" |
182 | "movel %5,%3@+\n\t" |
183 | "16:\t" |
184 | "movesl %2@+,%5\n\t" |
185 | "addxl %5,%0\n\t" |
186 | "movel %5,%3@+\n\t" |
187 | "17:\t" |
188 | "movesl %2@+,%5\n\t" |
189 | "addxl %5,%0\n\t" |
190 | "movel %5,%3@+\n\t" |
191 | "18:\t" |
192 | "movesl %2@+,%5\n\t" |
193 | "addxl %5,%0\n\t" |
194 | "movel %5,%3@+\n\t" |
195 | "dbra %1,1b\n\t" |
196 | "clrl %5\n\t" |
197 | "addxl %5,%0\n\t" /* add X bit */ |
198 | "clrw %1\n\t" |
199 | "subql #1,%1\n\t" |
200 | "jcc 1b\n" |
201 | "2:\t" |
202 | "movel %4,%1\n\t" /* restore len from tmp1 */ |
203 | "andw #0x1c,%4\n\t" /* number of rest longs */ |
204 | "jeq 4f\n\t" |
205 | "lsrw #2,%4\n\t" |
206 | "subqw #1,%4\n" |
207 | "3:\n" |
208 | /* loop for rest longs */ |
209 | "19:\t" |
210 | "movesl %2@+,%5\n\t" |
211 | "addxl %5,%0\n\t" |
212 | "movel %5,%3@+\n\t" |
213 | "dbra %4,3b\n\t" |
214 | "clrl %5\n\t" |
215 | "addxl %5,%0\n" /* add X bit */ |
216 | "4:\t" |
217 | /* now check for rest bytes that do not fit into longs */ |
218 | "andw #3,%1\n\t" |
219 | "jeq 7f\n\t" |
220 | "clrl %5\n\t" /* clear tmp2 for rest bytes */ |
221 | "subqw #2,%1\n\t" |
222 | "jlt 5f\n\t" |
223 | "20:\t" |
224 | "movesw %2@+,%5\n\t" /* have rest >= 2: get word */ |
225 | "movew %5,%3@+\n\t" |
226 | "swap %5\n\t" /* into bits 16..31 */ |
227 | "tstw %1\n\t" /* another byte? */ |
228 | "jeq 6f\n" |
229 | "5:\n" |
230 | "21:\t" |
231 | "movesb %2@,%5\n\t" /* have odd rest: get byte */ |
232 | "moveb %5,%3@+\n\t" |
233 | "lslw #8,%5\n\t" /* into bits 8..15; 16..31 untouched */ |
234 | "6:\t" |
235 | "addl %5,%0\n\t" /* now add rest long to sum */ |
236 | "clrl %5\n\t" |
237 | "addxl %5,%0\n\t" /* add X bit */ |
238 | "7:\t" |
239 | ".section .fixup,\"ax\"\n" |
240 | ".even\n" |
241 | /* If any exception occurs, return 0 */ |
242 | "90:\t" |
243 | "clrl %0\n" |
244 | "jra 7b\n" |
245 | ".previous\n" |
246 | ".section __ex_table,\"a\"\n" |
247 | ".long 10b,90b\n" |
248 | ".long 11b,90b\n" |
249 | ".long 12b,90b\n" |
250 | ".long 13b,90b\n" |
251 | ".long 14b,90b\n" |
252 | ".long 15b,90b\n" |
253 | ".long 16b,90b\n" |
254 | ".long 17b,90b\n" |
255 | ".long 18b,90b\n" |
256 | ".long 19b,90b\n" |
257 | ".long 20b,90b\n" |
258 | ".long 21b,90b\n" |
259 | ".previous" |
260 | : "=d" (sum), "=d" (len), "=a" (src), "=a" (dst), |
261 | "=&d" (tmp1), "=d" (tmp2) |
262 | : "0" (sum), "1" (len), "2" (src), "3" (dst) |
263 | ); |
264 | |
265 | return sum; |
266 | } |
267 | |
268 | |
269 | /* |
270 | * copy from kernel space while checksumming, otherwise like csum_partial |
271 | */ |
272 | |
273 | __wsum |
274 | csum_partial_copy_nocheck(const void *src, void *dst, int len) |
275 | { |
276 | unsigned long tmp1, tmp2; |
277 | __wsum sum = 0; |
278 | __asm__("movel %2,%4\n\t" |
279 | "btst #1,%4\n\t" /* Check alignment */ |
280 | "jeq 2f\n\t" |
281 | "subql #2,%1\n\t" /* buff%4==2: treat first word */ |
282 | "jgt 1f\n\t" |
283 | "addql #2,%1\n\t" /* len was == 2, treat only rest */ |
284 | "jra 4f\n" |
285 | "1:\t" |
286 | "movew %2@+,%4\n\t" /* add first word to sum */ |
287 | "addw %4,%0\n\t" |
288 | "movew %4,%3@+\n\t" |
289 | "clrl %4\n\t" |
290 | "addxl %4,%0\n" /* add X bit */ |
291 | "2:\t" |
292 | /* unrolled loop for the main part: do 8 longs at once */ |
293 | "movel %1,%4\n\t" /* save len in tmp1 */ |
294 | "lsrl #5,%1\n\t" /* len/32 */ |
295 | "jeq 2f\n\t" /* not enough... */ |
296 | "subql #1,%1\n" |
297 | "1:\t" |
298 | "movel %2@+,%5\n\t" |
299 | "addxl %5,%0\n\t" |
300 | "movel %5,%3@+\n\t" |
301 | "movel %2@+,%5\n\t" |
302 | "addxl %5,%0\n\t" |
303 | "movel %5,%3@+\n\t" |
304 | "movel %2@+,%5\n\t" |
305 | "addxl %5,%0\n\t" |
306 | "movel %5,%3@+\n\t" |
307 | "movel %2@+,%5\n\t" |
308 | "addxl %5,%0\n\t" |
309 | "movel %5,%3@+\n\t" |
310 | "movel %2@+,%5\n\t" |
311 | "addxl %5,%0\n\t" |
312 | "movel %5,%3@+\n\t" |
313 | "movel %2@+,%5\n\t" |
314 | "addxl %5,%0\n\t" |
315 | "movel %5,%3@+\n\t" |
316 | "movel %2@+,%5\n\t" |
317 | "addxl %5,%0\n\t" |
318 | "movel %5,%3@+\n\t" |
319 | "movel %2@+,%5\n\t" |
320 | "addxl %5,%0\n\t" |
321 | "movel %5,%3@+\n\t" |
322 | "dbra %1,1b\n\t" |
323 | "clrl %5\n\t" |
324 | "addxl %5,%0\n\t" /* add X bit */ |
325 | "clrw %1\n\t" |
326 | "subql #1,%1\n\t" |
327 | "jcc 1b\n" |
328 | "2:\t" |
329 | "movel %4,%1\n\t" /* restore len from tmp1 */ |
330 | "andw #0x1c,%4\n\t" /* number of rest longs */ |
331 | "jeq 4f\n\t" |
332 | "lsrw #2,%4\n\t" |
333 | "subqw #1,%4\n" |
334 | "3:\t" |
335 | /* loop for rest longs */ |
336 | "movel %2@+,%5\n\t" |
337 | "addxl %5,%0\n\t" |
338 | "movel %5,%3@+\n\t" |
339 | "dbra %4,3b\n\t" |
340 | "clrl %5\n\t" |
341 | "addxl %5,%0\n" /* add X bit */ |
342 | "4:\t" |
343 | /* now check for rest bytes that do not fit into longs */ |
344 | "andw #3,%1\n\t" |
345 | "jeq 7f\n\t" |
346 | "clrl %5\n\t" /* clear tmp2 for rest bytes */ |
347 | "subqw #2,%1\n\t" |
348 | "jlt 5f\n\t" |
349 | "movew %2@+,%5\n\t" /* have rest >= 2: get word */ |
350 | "movew %5,%3@+\n\t" |
351 | "swap %5\n\t" /* into bits 16..31 */ |
352 | "tstw %1\n\t" /* another byte? */ |
353 | "jeq 6f\n" |
354 | "5:\t" |
355 | "moveb %2@,%5\n\t" /* have odd rest: get byte */ |
356 | "moveb %5,%3@+\n\t" |
357 | "lslw #8,%5\n" /* into bits 8..15; 16..31 untouched */ |
358 | "6:\t" |
359 | "addl %5,%0\n\t" /* now add rest long to sum */ |
360 | "clrl %5\n\t" |
361 | "addxl %5,%0\n" /* add X bit */ |
362 | "7:\t" |
363 | : "=d" (sum), "=d" (len), "=a" (src), "=a" (dst), |
364 | "=&d" (tmp1), "=&d" (tmp2) |
365 | : "0" (sum), "1" (len), "2" (src), "3" (dst) |
366 | ); |
367 | return(sum); |
368 | } |
369 | EXPORT_SYMBOL(csum_partial_copy_nocheck); |
370 | |