1
2
3
4
5
6
7
8
9
10
11
12#include <linux/uaccess.h>
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34unsigned long __copy_user(void __user *pdst, const void *psrc, unsigned long pn)
35{
36
37
38
39
40
41
42
43
44 register char *dst __asm__ ("r13") = pdst;
45 register const char *src __asm__ ("r11") = psrc;
46 register int n __asm__ ("r12") = pn;
47 register int retn __asm__ ("r10") = 0;
48
49
50
51
52
53 if (((unsigned long) dst & 3) != 0
54
55
56 && n >= 3)
57 {
58 if ((unsigned long) dst & 1)
59 {
60 __asm_copy_to_user_1 (dst, src, retn);
61 n--;
62 }
63
64 if ((unsigned long) dst & 2)
65 {
66 __asm_copy_to_user_2 (dst, src, retn);
67 n -= 2;
68 }
69 }
70
71
72 if (n >= 44*2)
73
74 {
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91 __asm__ volatile ("\
92 .ifnc %0%1%2%3,$r13$r11$r12$r10 \n\
93 .err \n\
94 .endif \n\
95 \n\
96 ;; Save the registers we'll use in the movem process \n\
97 ;; on the stack. \n\
98 subq 11*4,$sp \n\
99 movem $r10,[$sp] \n\
100 \n\
101 ;; Now we've got this: \n\
102 ;; r11 - src \n\
103 ;; r13 - dst \n\
104 ;; r12 - n \n\
105 \n\
106 ;; Update n for the first loop \n\
107 subq 44,$r12 \n\
108 \n\
109; Since the noted PC of a faulting instruction in a delay-slot of a taken \n\
110; branch, is that of the branch target, we actually point at the from-movem \n\
111; for this case. There is no ambiguity here; if there was a fault in that \n\
112; instruction (meaning a kernel oops), the faulted PC would be the address \n\
113; after *that* movem. \n\
114 \n\
1150: \n\
116 movem [$r11+],$r10 \n\
117 subq 44,$r12 \n\
118 bge 0b \n\
119 movem $r10,[$r13+] \n\
1201: \n\
121 addq 44,$r12 ;; compensate for last loop underflowing n \n\
122 \n\
123 ;; Restore registers from stack \n\
124 movem [$sp+],$r10 \n\
1252: \n\
126 .section .fixup,\"ax\" \n\
127 \n\
128; To provide a correct count in r10 of bytes that failed to be copied, \n\
129; we jump back into the loop if the loop-branch was taken. There is no \n\
130; performance penalty for sany use; the program will segfault soon enough.\n\
131 \n\
1323: \n\
133 move.d [$sp],$r10 \n\
134 addq 44,$r10 \n\
135 move.d $r10,[$sp] \n\
136 jump 0b \n\
1374: \n\
138 movem [$sp+],$r10 \n\
139 addq 44,$r10 \n\
140 addq 44,$r12 \n\
141 jump 2b \n\
142 \n\
143 .previous \n\
144 .section __ex_table,\"a\" \n\
145 .dword 0b,3b \n\
146 .dword 1b,4b \n\
147 .previous"
148
149 : "=r" (dst), "=r" (src), "=r" (n), "=r" (retn)
150 : "0" (dst), "1" (src), "2" (n), "3" (retn));
151
152 }
153
154
155
156
157
158
159 while (n >= 16)
160 {
161 __asm_copy_to_user_16 (dst, src, retn);
162 n -= 16;
163 }
164
165
166
167 while (n >= 4)
168 {
169 __asm_copy_to_user_4 (dst, src, retn);
170 n -= 4;
171 }
172
173 switch (n)
174 {
175 case 0:
176 break;
177 case 1:
178 __asm_copy_to_user_1 (dst, src, retn);
179 break;
180 case 2:
181 __asm_copy_to_user_2 (dst, src, retn);
182 break;
183 case 3:
184 __asm_copy_to_user_3 (dst, src, retn);
185 break;
186 }
187
188 return retn;
189}
190EXPORT_SYMBOL(__copy_user);
191
192
193
194
195unsigned long __copy_user_in(void *pdst, const void __user *psrc,
196 unsigned long pn)
197{
198
199
200
201
202
203
204
205
206 register char *dst __asm__ ("r13") = pdst;
207 register const char *src __asm__ ("r11") = psrc;
208 register int n __asm__ ("r12") = pn;
209 register int retn __asm__ ("r10") = 0;
210
211
212
213
214 if (((unsigned long) src & 3) != 0)
215 {
216 if (((unsigned long) src & 1) && n != 0)
217 {
218 __asm_copy_from_user_1 (dst, src, retn);
219 n--;
220 if (retn)
221 goto exception;
222 }
223
224 if (((unsigned long) src & 2) && n >= 2)
225 {
226 __asm_copy_from_user_2 (dst, src, retn);
227 n -= 2;
228 if (retn)
229 goto exception;
230 }
231 }
232
233
234 if (n >= 44*2)
235
236
237 {
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254 __asm__ volatile ("\n\
255 .ifnc %0%1%2%3,$r13$r11$r12$r10 \n\
256 .err \n\
257 .endif \n\
258 \n\
259 ;; Save the registers we'll use in the movem process \n\
260 ;; on the stack. \n\
261 subq 11*4,$sp \n\
262 movem $r10,[$sp] \n\
263 \n\
264 ;; Now we've got this: \n\
265 ;; r11 - src \n\
266 ;; r13 - dst \n\
267 ;; r12 - n \n\
268 \n\
269 ;; Update n for the first loop \n\
270 subq 44,$r12 \n\
2710: \n\
272 movem [$r11+],$r10 \n\
2731: \n\
274 subq 44,$r12 \n\
275 bge 0b \n\
276 movem $r10,[$r13+] \n\
277 \n\
278 addq 44,$r12 ;; compensate for last loop underflowing n \n\
279 \n\
280 ;; Restore registers from stack \n\
281 movem [$sp+],$r10 \n\
2824: \n\
283 .section .fixup,\"ax\" \n\
284 \n\
285;; Do not jump back into the loop if we fail. For some uses, we get a \n\
286;; page fault somewhere on the line. Without checking for page limits, \n\
287;; we don't know where, but we need to copy accurately and keep an \n\
288;; accurate count; not just clear the whole line. To do that, we fall \n\
289;; down in the code below, proceeding with smaller amounts. It should \n\
290;; be kept in mind that we have to cater to code like what at one time \n\
291;; was in fs/super.c: \n\
292;; i = size - copy_from_user((void *)page, data, size); \n\
293;; which would cause repeated faults while clearing the remainder of \n\
294;; the SIZE bytes at PAGE after the first fault. \n\
295;; A caveat here is that we must not fall through from a failing page \n\
296;; to a valid page. \n\
297 \n\
2983: \n\
299 movem [$sp+],$r10 \n\
300 addq 44,$r12 ;; Get back count before faulting point. \n\
301 subq 44,$r11 ;; Get back pointer to faulting movem-line. \n\
302 jump 4b ;; Fall through, pretending the fault didn't happen.\n\
303 \n\
304 .previous \n\
305 .section __ex_table,\"a\" \n\
306 .dword 1b,3b \n\
307 .previous"
308
309 : "=r" (dst), "=r" (src), "=r" (n), "=r" (retn)
310 : "0" (dst), "1" (src), "2" (n), "3" (retn));
311
312 }
313
314
315
316
317
318
319
320
321
322
323 while (n >= 4)
324 {
325 __asm_copy_from_user_4 (dst, src, retn);
326 n -= 4;
327
328 if (retn)
329 goto exception;
330 }
331
332
333 switch (n)
334 {
335
336
337
338 case 0:
339
340
341 break;
342 case 1:
343 __asm_copy_from_user_1 (dst, src, retn);
344 break;
345 case 2:
346 __asm_copy_from_user_2 (dst, src, retn);
347 break;
348 case 3:
349 __asm_copy_from_user_3 (dst, src, retn);
350 break;
351 }
352
353
354
355 return retn;
356
357exception:
358 return retn + n;
359}
360EXPORT_SYMBOL(__copy_user_in);
361
362
363unsigned long __do_clear_user(void __user *pto, unsigned long pn)
364{
365
366
367
368
369
370
371
372
373 register char *dst __asm__ ("r13") = pto;
374 register int n __asm__ ("r12") = pn;
375 register int retn __asm__ ("r10") = 0;
376
377
378 if (((unsigned long) dst & 3) != 0
379
380 && n >= 3)
381 {
382 if ((unsigned long) dst & 1)
383 {
384 __asm_clear_1 (dst, retn);
385 n--;
386 }
387
388 if ((unsigned long) dst & 2)
389 {
390 __asm_clear_2 (dst, retn);
391 n -= 2;
392 }
393 }
394
395
396
397 if (n >= (1*48))
398 {
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415 __asm__ volatile ("\n\
416 .ifnc %0%1%2,$r13$r12$r10 \n\
417 .err \n\
418 .endif \n\
419 \n\
420 ;; Save the registers we'll clobber in the movem process \n\
421 ;; on the stack. Don't mention them to gcc, it will only be \n\
422 ;; upset. \n\
423 subq 11*4,$sp \n\
424 movem $r10,[$sp] \n\
425 \n\
426 clear.d $r0 \n\
427 clear.d $r1 \n\
428 clear.d $r2 \n\
429 clear.d $r3 \n\
430 clear.d $r4 \n\
431 clear.d $r5 \n\
432 clear.d $r6 \n\
433 clear.d $r7 \n\
434 clear.d $r8 \n\
435 clear.d $r9 \n\
436 clear.d $r10 \n\
437 clear.d $r11 \n\
438 \n\
439 ;; Now we've got this: \n\
440 ;; r13 - dst \n\
441 ;; r12 - n \n\
442 \n\
443 ;; Update n for the first loop \n\
444 subq 12*4,$r12 \n\
4450: \n\
446 subq 12*4,$r12 \n\
447 bge 0b \n\
448 movem $r11,[$r13+] \n\
4491: \n\
450 addq 12*4,$r12 ;; compensate for last loop underflowing n\n\
451 \n\
452 ;; Restore registers from stack \n\
453 movem [$sp+],$r10 \n\
4542: \n\
455 .section .fixup,\"ax\" \n\
4563: \n\
457 move.d [$sp],$r10 \n\
458 addq 12*4,$r10 \n\
459 move.d $r10,[$sp] \n\
460 clear.d $r10 \n\
461 jump 0b \n\
462 \n\
4634: \n\
464 movem [$sp+],$r10 \n\
465 addq 12*4,$r10 \n\
466 addq 12*4,$r12 \n\
467 jump 2b \n\
468 \n\
469 .previous \n\
470 .section __ex_table,\"a\" \n\
471 .dword 0b,3b \n\
472 .dword 1b,4b \n\
473 .previous"
474
475 : "=r" (dst), "=r" (n), "=r" (retn)
476 : "0" (dst), "1" (n), "2" (retn)
477 : "r11");
478 }
479
480 while (n >= 16)
481 {
482 __asm_clear_16 (dst, retn);
483 n -= 16;
484 }
485
486
487
488 while (n >= 4)
489 {
490 __asm_clear_4 (dst, retn);
491 n -= 4;
492 }
493
494 switch (n)
495 {
496 case 0:
497 break;
498 case 1:
499 __asm_clear_1 (dst, retn);
500 break;
501 case 2:
502 __asm_clear_2 (dst, retn);
503 break;
504 case 3:
505 __asm_clear_3 (dst, retn);
506 break;
507 }
508
509 return retn;
510}
511EXPORT_SYMBOL(__do_clear_user);
512