diff options
Diffstat (limited to 'arch/alpha/lib/csum_partial_copy.c')
-rw-r--r-- | arch/alpha/lib/csum_partial_copy.c | 391 |
1 files changed, 391 insertions, 0 deletions
diff --git a/arch/alpha/lib/csum_partial_copy.c b/arch/alpha/lib/csum_partial_copy.c new file mode 100644 index 000000000000..a37948f3037a --- /dev/null +++ b/arch/alpha/lib/csum_partial_copy.c | |||
@@ -0,0 +1,391 @@ | |||
1 | /* | ||
2 | * csum_partial_copy - do IP checksumming and copy | ||
3 | * | ||
4 | * (C) Copyright 1996 Linus Torvalds | ||
5 | * accellerated versions (and 21264 assembly versions ) contributed by | ||
6 | * Rick Gorton <rick.gorton@alpha-processor.com> | ||
7 | * | ||
8 | * Don't look at this too closely - you'll go mad. The things | ||
9 | * we do for performance.. | ||
10 | */ | ||
11 | |||
12 | #include <linux/types.h> | ||
13 | #include <linux/string.h> | ||
14 | #include <asm/uaccess.h> | ||
15 | |||
16 | |||
17 | #define ldq_u(x,y) \ | ||
18 | __asm__ __volatile__("ldq_u %0,%1":"=r" (x):"m" (*(const unsigned long *)(y))) | ||
19 | |||
20 | #define stq_u(x,y) \ | ||
21 | __asm__ __volatile__("stq_u %1,%0":"=m" (*(unsigned long *)(y)):"r" (x)) | ||
22 | |||
23 | #define extql(x,y,z) \ | ||
24 | __asm__ __volatile__("extql %1,%2,%0":"=r" (z):"r" (x),"r" (y)) | ||
25 | |||
26 | #define extqh(x,y,z) \ | ||
27 | __asm__ __volatile__("extqh %1,%2,%0":"=r" (z):"r" (x),"r" (y)) | ||
28 | |||
29 | #define mskql(x,y,z) \ | ||
30 | __asm__ __volatile__("mskql %1,%2,%0":"=r" (z):"r" (x),"r" (y)) | ||
31 | |||
32 | #define mskqh(x,y,z) \ | ||
33 | __asm__ __volatile__("mskqh %1,%2,%0":"=r" (z):"r" (x),"r" (y)) | ||
34 | |||
35 | #define insql(x,y,z) \ | ||
36 | __asm__ __volatile__("insql %1,%2,%0":"=r" (z):"r" (x),"r" (y)) | ||
37 | |||
38 | #define insqh(x,y,z) \ | ||
39 | __asm__ __volatile__("insqh %1,%2,%0":"=r" (z):"r" (x),"r" (y)) | ||
40 | |||
41 | |||
42 | #define __get_user_u(x,ptr) \ | ||
43 | ({ \ | ||
44 | long __guu_err; \ | ||
45 | __asm__ __volatile__( \ | ||
46 | "1: ldq_u %0,%2\n" \ | ||
47 | "2:\n" \ | ||
48 | ".section __ex_table,\"a\"\n" \ | ||
49 | " .long 1b - .\n" \ | ||
50 | " lda %0,2b-1b(%1)\n" \ | ||
51 | ".previous" \ | ||
52 | : "=r"(x), "=r"(__guu_err) \ | ||
53 | : "m"(__m(ptr)), "1"(0)); \ | ||
54 | __guu_err; \ | ||
55 | }) | ||
56 | |||
57 | #define __put_user_u(x,ptr) \ | ||
58 | ({ \ | ||
59 | long __puu_err; \ | ||
60 | __asm__ __volatile__( \ | ||
61 | "1: stq_u %2,%1\n" \ | ||
62 | "2:\n" \ | ||
63 | ".section __ex_table,\"a\"\n" \ | ||
64 | " .long 1b - ." \ | ||
65 | " lda $31,2b-1b(%0)\n" \ | ||
66 | ".previous" \ | ||
67 | : "=r"(__puu_err) \ | ||
68 | : "m"(__m(addr)), "rJ"(x), "0"(0)); \ | ||
69 | __puu_err; \ | ||
70 | }) | ||
71 | |||
72 | |||
73 | static inline unsigned short from64to16(unsigned long x) | ||
74 | { | ||
75 | /* Using extract instructions is a bit more efficient | ||
76 | than the original shift/bitmask version. */ | ||
77 | |||
78 | union { | ||
79 | unsigned long ul; | ||
80 | unsigned int ui[2]; | ||
81 | unsigned short us[4]; | ||
82 | } in_v, tmp_v, out_v; | ||
83 | |||
84 | in_v.ul = x; | ||
85 | tmp_v.ul = (unsigned long) in_v.ui[0] + (unsigned long) in_v.ui[1]; | ||
86 | |||
87 | /* Since the bits of tmp_v.sh[3] are going to always be zero, | ||
88 | we don't have to bother to add that in. */ | ||
89 | out_v.ul = (unsigned long) tmp_v.us[0] + (unsigned long) tmp_v.us[1] | ||
90 | + (unsigned long) tmp_v.us[2]; | ||
91 | |||
92 | /* Similarly, out_v.us[2] is always zero for the final add. */ | ||
93 | return out_v.us[0] + out_v.us[1]; | ||
94 | } | ||
95 | |||
96 | |||
97 | |||
98 | /* | ||
99 | * Ok. This isn't fun, but this is the EASY case. | ||
100 | */ | ||
101 | static inline unsigned long | ||
102 | csum_partial_cfu_aligned(const unsigned long __user *src, unsigned long *dst, | ||
103 | long len, unsigned long checksum, | ||
104 | int *errp) | ||
105 | { | ||
106 | unsigned long carry = 0; | ||
107 | int err = 0; | ||
108 | |||
109 | while (len >= 0) { | ||
110 | unsigned long word; | ||
111 | err |= __get_user(word, src); | ||
112 | checksum += carry; | ||
113 | src++; | ||
114 | checksum += word; | ||
115 | len -= 8; | ||
116 | carry = checksum < word; | ||
117 | *dst = word; | ||
118 | dst++; | ||
119 | } | ||
120 | len += 8; | ||
121 | checksum += carry; | ||
122 | if (len) { | ||
123 | unsigned long word, tmp; | ||
124 | err |= __get_user(word, src); | ||
125 | tmp = *dst; | ||
126 | mskql(word, len, word); | ||
127 | checksum += word; | ||
128 | mskqh(tmp, len, tmp); | ||
129 | carry = checksum < word; | ||
130 | *dst = word | tmp; | ||
131 | checksum += carry; | ||
132 | } | ||
133 | if (err) *errp = err; | ||
134 | return checksum; | ||
135 | } | ||
136 | |||
137 | /* | ||
138 | * This is even less fun, but this is still reasonably | ||
139 | * easy. | ||
140 | */ | ||
141 | static inline unsigned long | ||
142 | csum_partial_cfu_dest_aligned(const unsigned long __user *src, | ||
143 | unsigned long *dst, | ||
144 | unsigned long soff, | ||
145 | long len, unsigned long checksum, | ||
146 | int *errp) | ||
147 | { | ||
148 | unsigned long first; | ||
149 | unsigned long word, carry; | ||
150 | unsigned long lastsrc = 7+len+(unsigned long)src; | ||
151 | int err = 0; | ||
152 | |||
153 | err |= __get_user_u(first,src); | ||
154 | carry = 0; | ||
155 | while (len >= 0) { | ||
156 | unsigned long second; | ||
157 | |||
158 | err |= __get_user_u(second, src+1); | ||
159 | extql(first, soff, word); | ||
160 | len -= 8; | ||
161 | src++; | ||
162 | extqh(second, soff, first); | ||
163 | checksum += carry; | ||
164 | word |= first; | ||
165 | first = second; | ||
166 | checksum += word; | ||
167 | *dst = word; | ||
168 | dst++; | ||
169 | carry = checksum < word; | ||
170 | } | ||
171 | len += 8; | ||
172 | checksum += carry; | ||
173 | if (len) { | ||
174 | unsigned long tmp; | ||
175 | unsigned long second; | ||
176 | err |= __get_user_u(second, lastsrc); | ||
177 | tmp = *dst; | ||
178 | extql(first, soff, word); | ||
179 | extqh(second, soff, first); | ||
180 | word |= first; | ||
181 | mskql(word, len, word); | ||
182 | checksum += word; | ||
183 | mskqh(tmp, len, tmp); | ||
184 | carry = checksum < word; | ||
185 | *dst = word | tmp; | ||
186 | checksum += carry; | ||
187 | } | ||
188 | if (err) *errp = err; | ||
189 | return checksum; | ||
190 | } | ||
191 | |||
192 | /* | ||
193 | * This is slightly less fun than the above.. | ||
194 | */ | ||
195 | static inline unsigned long | ||
196 | csum_partial_cfu_src_aligned(const unsigned long __user *src, | ||
197 | unsigned long *dst, | ||
198 | unsigned long doff, | ||
199 | long len, unsigned long checksum, | ||
200 | unsigned long partial_dest, | ||
201 | int *errp) | ||
202 | { | ||
203 | unsigned long carry = 0; | ||
204 | unsigned long word; | ||
205 | unsigned long second_dest; | ||
206 | int err = 0; | ||
207 | |||
208 | mskql(partial_dest, doff, partial_dest); | ||
209 | while (len >= 0) { | ||
210 | err |= __get_user(word, src); | ||
211 | len -= 8; | ||
212 | insql(word, doff, second_dest); | ||
213 | checksum += carry; | ||
214 | stq_u(partial_dest | second_dest, dst); | ||
215 | src++; | ||
216 | checksum += word; | ||
217 | insqh(word, doff, partial_dest); | ||
218 | carry = checksum < word; | ||
219 | dst++; | ||
220 | } | ||
221 | len += 8; | ||
222 | if (len) { | ||
223 | checksum += carry; | ||
224 | err |= __get_user(word, src); | ||
225 | mskql(word, len, word); | ||
226 | len -= 8; | ||
227 | checksum += word; | ||
228 | insql(word, doff, second_dest); | ||
229 | len += doff; | ||
230 | carry = checksum < word; | ||
231 | partial_dest |= second_dest; | ||
232 | if (len >= 0) { | ||
233 | stq_u(partial_dest, dst); | ||
234 | if (!len) goto out; | ||
235 | dst++; | ||
236 | insqh(word, doff, partial_dest); | ||
237 | } | ||
238 | doff = len; | ||
239 | } | ||
240 | ldq_u(second_dest, dst); | ||
241 | mskqh(second_dest, doff, second_dest); | ||
242 | stq_u(partial_dest | second_dest, dst); | ||
243 | out: | ||
244 | checksum += carry; | ||
245 | if (err) *errp = err; | ||
246 | return checksum; | ||
247 | } | ||
248 | |||
249 | /* | ||
250 | * This is so totally un-fun that it's frightening. Don't | ||
251 | * look at this too closely, you'll go blind. | ||
252 | */ | ||
253 | static inline unsigned long | ||
254 | csum_partial_cfu_unaligned(const unsigned long __user * src, | ||
255 | unsigned long * dst, | ||
256 | unsigned long soff, unsigned long doff, | ||
257 | long len, unsigned long checksum, | ||
258 | unsigned long partial_dest, | ||
259 | int *errp) | ||
260 | { | ||
261 | unsigned long carry = 0; | ||
262 | unsigned long first; | ||
263 | unsigned long lastsrc; | ||
264 | int err = 0; | ||
265 | |||
266 | err |= __get_user_u(first, src); | ||
267 | lastsrc = 7+len+(unsigned long)src; | ||
268 | mskql(partial_dest, doff, partial_dest); | ||
269 | while (len >= 0) { | ||
270 | unsigned long second, word; | ||
271 | unsigned long second_dest; | ||
272 | |||
273 | err |= __get_user_u(second, src+1); | ||
274 | extql(first, soff, word); | ||
275 | checksum += carry; | ||
276 | len -= 8; | ||
277 | extqh(second, soff, first); | ||
278 | src++; | ||
279 | word |= first; | ||
280 | first = second; | ||
281 | insql(word, doff, second_dest); | ||
282 | checksum += word; | ||
283 | stq_u(partial_dest | second_dest, dst); | ||
284 | carry = checksum < word; | ||
285 | insqh(word, doff, partial_dest); | ||
286 | dst++; | ||
287 | } | ||
288 | len += doff; | ||
289 | checksum += carry; | ||
290 | if (len >= 0) { | ||
291 | unsigned long second, word; | ||
292 | unsigned long second_dest; | ||
293 | |||
294 | err |= __get_user_u(second, lastsrc); | ||
295 | extql(first, soff, word); | ||
296 | extqh(second, soff, first); | ||
297 | word |= first; | ||
298 | first = second; | ||
299 | mskql(word, len-doff, word); | ||
300 | checksum += word; | ||
301 | insql(word, doff, second_dest); | ||
302 | carry = checksum < word; | ||
303 | stq_u(partial_dest | second_dest, dst); | ||
304 | if (len) { | ||
305 | ldq_u(second_dest, dst+1); | ||
306 | insqh(word, doff, partial_dest); | ||
307 | mskqh(second_dest, len, second_dest); | ||
308 | stq_u(partial_dest | second_dest, dst+1); | ||
309 | } | ||
310 | checksum += carry; | ||
311 | } else { | ||
312 | unsigned long second, word; | ||
313 | unsigned long second_dest; | ||
314 | |||
315 | err |= __get_user_u(second, lastsrc); | ||
316 | extql(first, soff, word); | ||
317 | extqh(second, soff, first); | ||
318 | word |= first; | ||
319 | ldq_u(second_dest, dst); | ||
320 | mskql(word, len-doff, word); | ||
321 | checksum += word; | ||
322 | mskqh(second_dest, len, second_dest); | ||
323 | carry = checksum < word; | ||
324 | insql(word, doff, word); | ||
325 | stq_u(partial_dest | word | second_dest, dst); | ||
326 | checksum += carry; | ||
327 | } | ||
328 | if (err) *errp = err; | ||
329 | return checksum; | ||
330 | } | ||
331 | |||
332 | static unsigned int | ||
333 | do_csum_partial_copy_from_user(const char __user *src, char *dst, int len, | ||
334 | unsigned int sum, int *errp) | ||
335 | { | ||
336 | unsigned long checksum = (unsigned) sum; | ||
337 | unsigned long soff = 7 & (unsigned long) src; | ||
338 | unsigned long doff = 7 & (unsigned long) dst; | ||
339 | |||
340 | if (len) { | ||
341 | if (!doff) { | ||
342 | if (!soff) | ||
343 | checksum = csum_partial_cfu_aligned( | ||
344 | (const unsigned long __user *) src, | ||
345 | (unsigned long *) dst, | ||
346 | len-8, checksum, errp); | ||
347 | else | ||
348 | checksum = csum_partial_cfu_dest_aligned( | ||
349 | (const unsigned long __user *) src, | ||
350 | (unsigned long *) dst, | ||
351 | soff, len-8, checksum, errp); | ||
352 | } else { | ||
353 | unsigned long partial_dest; | ||
354 | ldq_u(partial_dest, dst); | ||
355 | if (!soff) | ||
356 | checksum = csum_partial_cfu_src_aligned( | ||
357 | (const unsigned long __user *) src, | ||
358 | (unsigned long *) dst, | ||
359 | doff, len-8, checksum, | ||
360 | partial_dest, errp); | ||
361 | else | ||
362 | checksum = csum_partial_cfu_unaligned( | ||
363 | (const unsigned long __user *) src, | ||
364 | (unsigned long *) dst, | ||
365 | soff, doff, len-8, checksum, | ||
366 | partial_dest, errp); | ||
367 | } | ||
368 | checksum = from64to16 (checksum); | ||
369 | } | ||
370 | return checksum; | ||
371 | } | ||
372 | |||
373 | unsigned int | ||
374 | csum_partial_copy_from_user(const char __user *src, char *dst, int len, | ||
375 | unsigned int sum, int *errp) | ||
376 | { | ||
377 | if (!access_ok(VERIFY_READ, src, len)) { | ||
378 | *errp = -EFAULT; | ||
379 | memset(dst, 0, len); | ||
380 | return sum; | ||
381 | } | ||
382 | |||
383 | return do_csum_partial_copy_from_user(src, dst, len, sum, errp); | ||
384 | } | ||
385 | |||
386 | unsigned int | ||
387 | csum_partial_copy_nocheck(const char __user *src, char *dst, int len, | ||
388 | unsigned int sum) | ||
389 | { | ||
390 | return do_csum_partial_copy_from_user(src, dst, len, sum, NULL); | ||
391 | } | ||