Commit | Line | Data |
---|---|---|
867e359b CM |
1 | /* |
2 | * Copyright 2010 Tilera Corporation. All Rights Reserved. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or | |
5 | * modify it under the terms of the GNU General Public License | |
6 | * as published by the Free Software Foundation, version 2. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, but | |
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | |
11 | * NON INFRINGEMENT. See the GNU General Public License for | |
12 | * more details. | |
13 | */ | |
14 | ||
15 | #include <linux/linkage.h> | |
16 | #include <asm/errno.h> | |
17 | #include <asm/cache.h> | |
18 | #include <arch/chip.h> | |
19 | ||
20 | /* Access user memory, but use MMU to avoid propagating kernel exceptions. */ | |
21 | ||
867e359b CM |
22 | /* |
23 | * strnlen_user_asm takes the pointer in r0, and the length bound in r1. | |
24 | * It returns the length, including the terminating NUL, or zero on exception. | |
25 | * If length is greater than the bound, returns one plus the bound. | |
26 | */ | |
27 | STD_ENTRY(strnlen_user_asm) | |
28 | { bz r1, 2f; addi r3, r0, -1 } /* bias down to include NUL */ | |
29 | 1: { lb_u r4, r0; addi r1, r1, -1 } | |
30 | bz r4, 2f | |
31 | { bnzt r1, 1b; addi r0, r0, 1 } | |
32 | 2: { sub r0, r0, r3; jrp lr } | |
33 | STD_ENDPROC(strnlen_user_asm) | |
34 | .pushsection .fixup,"ax" | |
35 | strnlen_user_fault: | |
36 | { move r0, zero; jrp lr } | |
37 | ENDPROC(strnlen_user_fault) | |
38 | .section __ex_table,"a" | |
d4d9eab4 | 39 | .align 4 |
867e359b CM |
40 | .word 1b, strnlen_user_fault |
41 | .popsection | |
42 | ||
43 | /* | |
44 | * strncpy_from_user_asm takes the kernel target pointer in r0, | |
45 | * the userspace source pointer in r1, and the length bound (including | |
46 | * the trailing NUL) in r2. On success, it returns the string length | |
47 | * (not including the trailing NUL), or -EFAULT on failure. | |
48 | */ | |
49 | STD_ENTRY(strncpy_from_user_asm) | |
50 | { bz r2, 2f; move r3, r0 } | |
c0f06010 | 51 | 1: { lb_u r4, r1; addi r1, r1, 1; addi r2, r2, -1 } |
867e359b | 52 | { sb r0, r4; addi r0, r0, 1 } |
c0f06010 CM |
53 | bz r4, 2f |
54 | bnzt r2, 1b | |
55 | { sub r0, r0, r3; jrp lr } | |
56 | 2: addi r0, r0, -1 /* don't count the trailing NUL */ | |
57 | { sub r0, r0, r3; jrp lr } | |
867e359b CM |
58 | STD_ENDPROC(strncpy_from_user_asm) |
59 | .pushsection .fixup,"ax" | |
60 | strncpy_from_user_fault: | |
61 | { movei r0, -EFAULT; jrp lr } | |
62 | ENDPROC(strncpy_from_user_fault) | |
63 | .section __ex_table,"a" | |
d4d9eab4 | 64 | .align 4 |
867e359b CM |
65 | .word 1b, strncpy_from_user_fault |
66 | .popsection | |
67 | ||
68 | /* | |
69 | * clear_user_asm takes the user target address in r0 and the | |
70 | * number of bytes to zero in r1. | |
71 | * It returns the number of uncopiable bytes (hopefully zero) in r0. | |
72 | * Note that we don't use a separate .fixup section here since we fall | |
73 | * through into the "fixup" code as the last straight-line bundle anyway. | |
74 | */ | |
75 | STD_ENTRY(clear_user_asm) | |
76 | { bz r1, 2f; or r2, r0, r1 } | |
77 | andi r2, r2, 3 | |
78 | bzt r2, .Lclear_aligned_user_asm | |
79 | 1: { sb r0, zero; addi r0, r0, 1; addi r1, r1, -1 } | |
80 | bnzt r1, 1b | |
81 | 2: { move r0, r1; jrp lr } | |
82 | .pushsection __ex_table,"a" | |
d4d9eab4 | 83 | .align 4 |
867e359b CM |
84 | .word 1b, 2b |
85 | .popsection | |
86 | ||
87 | .Lclear_aligned_user_asm: | |
88 | 1: { sw r0, zero; addi r0, r0, 4; addi r1, r1, -4 } | |
89 | bnzt r1, 1b | |
90 | 2: { move r0, r1; jrp lr } | |
91 | STD_ENDPROC(clear_user_asm) | |
92 | .pushsection __ex_table,"a" | |
d4d9eab4 | 93 | .align 4 |
867e359b CM |
94 | .word 1b, 2b |
95 | .popsection | |
96 | ||
97 | /* | |
98 | * flush_user_asm takes the user target address in r0 and the | |
99 | * number of bytes to flush in r1. | |
100 | * It returns the number of unflushable bytes (hopefully zero) in r0. | |
101 | */ | |
102 | STD_ENTRY(flush_user_asm) | |
103 | bz r1, 2f | |
104 | { movei r2, L2_CACHE_BYTES; add r1, r0, r1 } | |
105 | { sub r2, zero, r2; addi r1, r1, L2_CACHE_BYTES-1 } | |
106 | { and r0, r0, r2; and r1, r1, r2 } | |
107 | { sub r1, r1, r0 } | |
108 | 1: { flush r0; addi r1, r1, -CHIP_FLUSH_STRIDE() } | |
109 | { addi r0, r0, CHIP_FLUSH_STRIDE(); bnzt r1, 1b } | |
110 | 2: { move r0, r1; jrp lr } | |
111 | STD_ENDPROC(flush_user_asm) | |
112 | .pushsection __ex_table,"a" | |
d4d9eab4 | 113 | .align 4 |
867e359b CM |
114 | .word 1b, 2b |
115 | .popsection | |
116 | ||
867e359b CM |
117 | /* |
118 | * finv_user_asm takes the user target address in r0 and the | |
119 | * number of bytes to flush-invalidate in r1. | |
120 | * It returns the number of not finv'able bytes (hopefully zero) in r0. | |
121 | */ | |
122 | STD_ENTRY(finv_user_asm) | |
123 | bz r1, 2f | |
124 | { movei r2, L2_CACHE_BYTES; add r1, r0, r1 } | |
125 | { sub r2, zero, r2; addi r1, r1, L2_CACHE_BYTES-1 } | |
126 | { and r0, r0, r2; and r1, r1, r2 } | |
127 | { sub r1, r1, r0 } | |
128 | 1: { finv r0; addi r1, r1, -CHIP_FINV_STRIDE() } | |
129 | { addi r0, r0, CHIP_FINV_STRIDE(); bnzt r1, 1b } | |
130 | 2: { move r0, r1; jrp lr } | |
131 | STD_ENDPROC(finv_user_asm) | |
132 | .pushsection __ex_table,"a" | |
d4d9eab4 | 133 | .align 4 |
867e359b CM |
134 | .word 1b, 2b |
135 | .popsection |