2 * csum_partial_copy - do IP checksumming and copy
4 * (C) Copyright 1996 Linus Torvalds
6 * Don't look at this too closely - you'll go mad. The things
7 * we do for performance..
10 #include <linux/types.h>
11 #include <linux/string.h>
12 #include <asm/uaccess.h>
16 __asm__ __volatile__("ldq_u %0,%1":"=r" (x):"m" (*(const unsigned long *)(y)))
19 __asm__ __volatile__("stq_u %1,%0":"=m" (*(unsigned long *)(y)):"r" (x))
21 #define extql(x,y,z) \
22 __asm__ __volatile__("extql %1,%2,%0":"=r" (z):"r" (x),"r" (y))
24 #define extqh(x,y,z) \
25 __asm__ __volatile__("extqh %1,%2,%0":"=r" (z):"r" (x),"r" (y))
27 #define mskql(x,y,z) \
28 __asm__ __volatile__("mskql %1,%2,%0":"=r" (z):"r" (x),"r" (y))
30 #define mskqh(x,y,z) \
31 __asm__ __volatile__("mskqh %1,%2,%0":"=r" (z):"r" (x),"r" (y))
33 #define insql(x,y,z) \
34 __asm__ __volatile__("insql %1,%2,%0":"=r" (z):"r" (x),"r" (y))
36 #define insqh(x,y,z) \
37 __asm__ __volatile__("insqh %1,%2,%0":"=r" (z):"r" (x),"r" (y))
40 #define __get_user_u(x,ptr) \
43 __asm__ __volatile__( \
46 ".section __ex_table,\"a\"\n" \
48 " lda %0,2b-1b(%1)\n" \
50 : "=r"(x), "=r"(__guu_err) \
51 : "m"(__m(ptr)), "1"(0)); \
55 #define __put_user_u(x,ptr) \
58 __asm__ __volatile__( \
61 ".section __ex_table,\"a\"\n" \
63 " lda $31,2b-1b(%0)\n" \
66 : "m"(__m(addr)), "rJ"(x), "0"(0)); \
72 * Ok. This isn't fun, but this is the EASY case.
74 static inline unsigned long
75 csum_partial_cfu_aligned(const unsigned long *src
, unsigned long *dst
,
76 long len
, unsigned long checksum
,
79 unsigned long carry
= 0;
84 err
|= __get_user(word
, src
);
89 carry
= checksum
< word
;
96 unsigned long word
, tmp
;
97 err
|= __get_user(word
, src
);
99 mskql(word
, len
, word
);
101 mskqh(tmp
, len
, tmp
);
102 carry
= checksum
< word
;
106 if (err
) *errp
= err
;
111 * This is even less fun, but this is still reasonably
114 static inline unsigned long
115 csum_partial_cfu_dest_aligned(const unsigned long *src
, unsigned long *dst
,
117 long len
, unsigned long checksum
,
121 unsigned long word
, carry
;
122 unsigned long lastsrc
= 7+len
+(unsigned long)src
;
125 err
|= __get_user_u(first
,src
);
128 unsigned long second
;
130 err
|= __get_user_u(second
, src
+1);
131 extql(first
, soff
, word
);
134 extqh(second
, soff
, first
);
141 carry
= checksum
< word
;
147 unsigned long second
;
148 err
|= __get_user_u(second
, lastsrc
);
150 extql(first
, soff
, word
);
151 extqh(second
, soff
, first
);
153 mskql(word
, len
, word
);
155 mskqh(tmp
, len
, tmp
);
156 carry
= checksum
< word
;
160 if (err
) *errp
= err
;
165 * This is slightly less fun than the above..
167 static inline unsigned long
168 csum_partial_cfu_src_aligned(const unsigned long *src
, unsigned long *dst
,
170 long len
, unsigned long checksum
,
171 unsigned long partial_dest
,
174 unsigned long carry
= 0;
178 mskql(partial_dest
, doff
, partial_dest
);
180 unsigned long second_dest
;
181 err
|= __get_user(word
, src
);
183 insql(word
, doff
, second_dest
);
185 stq_u(partial_dest
| second_dest
, dst
);
188 insqh(word
, doff
, partial_dest
);
189 carry
= checksum
< word
;
195 unsigned long second_dest
;
196 err
|= __get_user(word
, src
);
197 mskql(word
, len
-doff
, word
);
199 insql(word
, doff
, second_dest
);
200 stq_u(partial_dest
| second_dest
, dst
);
201 carry
= checksum
< word
;
203 ldq_u(second_dest
, dst
+1);
204 insqh(word
, doff
, partial_dest
);
205 mskqh(second_dest
, len
, second_dest
);
206 stq_u(partial_dest
| second_dest
, dst
+1);
209 } else if (len
& 7) {
210 unsigned long second_dest
;
211 err
|= __get_user(word
, src
);
212 ldq_u(second_dest
, dst
);
213 mskql(word
, len
-doff
, word
);
215 mskqh(second_dest
, len
, second_dest
);
216 carry
= checksum
< word
;
217 insql(word
, doff
, word
);
218 stq_u(partial_dest
| word
| second_dest
, dst
);
221 if (err
) *errp
= err
;
226 * This is so totally un-fun that it's frightening. Don't
227 * look at this too closely, you'll go blind.
229 static inline unsigned long
230 csum_partial_cfu_unaligned(const unsigned long * src
, unsigned long * dst
,
231 unsigned long soff
, unsigned long doff
,
232 long len
, unsigned long checksum
,
233 unsigned long partial_dest
,
236 unsigned long carry
= 0;
238 unsigned long lastsrc
;
241 err
|= __get_user_u(first
, src
);
242 lastsrc
= 7+len
+(unsigned long)src
;
243 mskql(partial_dest
, doff
, partial_dest
);
245 unsigned long second
, word
;
246 unsigned long second_dest
;
248 err
|= __get_user_u(second
, src
+1);
249 extql(first
, soff
, word
);
252 extqh(second
, soff
, first
);
256 insql(word
, doff
, second_dest
);
258 stq_u(partial_dest
| second_dest
, dst
);
259 carry
= checksum
< word
;
260 insqh(word
, doff
, partial_dest
);
266 unsigned long second
, word
;
267 unsigned long second_dest
;
269 err
|= __get_user_u(second
, lastsrc
);
270 extql(first
, soff
, word
);
271 extqh(second
, soff
, first
);
274 mskql(word
, len
-doff
, word
);
276 insql(word
, doff
, second_dest
);
277 carry
= checksum
< word
;
278 stq_u(partial_dest
| second_dest
, dst
);
280 ldq_u(second_dest
, dst
+1);
281 insqh(word
, doff
, partial_dest
);
282 mskqh(second_dest
, len
, second_dest
);
283 stq_u(partial_dest
| second_dest
, dst
+1);
286 } else if (len
& 7) {
287 unsigned long second
, word
;
288 unsigned long second_dest
;
290 err
|= __get_user_u(second
, lastsrc
);
291 extql(first
, soff
, word
);
292 extqh(second
, soff
, first
);
294 ldq_u(second_dest
, dst
);
295 mskql(word
, len
-doff
, word
);
297 mskqh(second_dest
, len
, second_dest
);
298 carry
= checksum
< word
;
299 insql(word
, doff
, word
);
300 stq_u(partial_dest
| word
| second_dest
, dst
);
303 if (err
) *errp
= err
;
308 do_csum_partial_copy_from_user(const char *src
, char *dst
, int len
,
309 unsigned int sum
, int *errp
)
311 unsigned long checksum
= (unsigned) sum
;
312 unsigned long soff
= 7 & (unsigned long) src
;
313 unsigned long doff
= 7 & (unsigned long) dst
;
318 checksum
= csum_partial_cfu_aligned(
319 (const unsigned long *) src
,
320 (unsigned long *) dst
,
321 len
-8, checksum
, errp
);
323 checksum
= csum_partial_cfu_dest_aligned(
324 (const unsigned long *) src
,
325 (unsigned long *) dst
,
326 soff
, len
-8, checksum
, errp
);
328 unsigned long partial_dest
;
329 ldq_u(partial_dest
, dst
);
331 checksum
= csum_partial_cfu_src_aligned(
332 (const unsigned long *) src
,
333 (unsigned long *) dst
,
334 doff
, len
-8, checksum
,
337 checksum
= csum_partial_cfu_unaligned(
338 (const unsigned long *) src
,
339 (unsigned long *) dst
,
340 soff
, doff
, len
-8, checksum
,
344 checksum
= (checksum
& 0xffffffff) + (checksum
>> 32);
345 /* 33 -> < 32 bits */
346 checksum
= (checksum
& 0xffff) + (checksum
>> 16);
348 checksum
= (checksum
& 0xffff) + (checksum
>> 16);
349 checksum
= (checksum
& 0xffff) + (checksum
>> 16);
355 csum_partial_copy_from_user(const char *src
, char *dst
, int len
,
356 unsigned int sum
, int *errp
)
358 if (!access_ok(src
, len
, VERIFY_READ
)) {
364 return do_csum_partial_copy_from_user(src
, dst
, len
, sum
, errp
);
368 csum_partial_copy (const char *src
, char *dst
, int len
, unsigned int sum
)
373 ret
= do_csum_partial_copy_from_user(src
, dst
, len
, sum
, &error
);
375 printk("csum_partial_copy_old(): tell mingo to convert me!\n");