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 | ||
22 | .pushsection .fixup,"ax" | |
23 | ||
24 | get_user_fault: | |
25 | { move r0, zero; move r1, zero } | |
26 | { movei r2, -EFAULT; jrp lr } | |
27 | ENDPROC(get_user_fault) | |
28 | ||
29 | put_user_fault: | |
30 | { movei r0, -EFAULT; jrp lr } | |
31 | ENDPROC(put_user_fault) | |
32 | ||
33 | .popsection | |
34 | ||
35 | /* | |
36 | * __get_user_N functions take a pointer in r0, and return 0 in r2 | |
37 | * on success, with the value in r0; or else -EFAULT in r2. | |
38 | */ | |
39 | #define __get_user_N(bytes, LOAD) \ | |
40 | STD_ENTRY(__get_user_##bytes); \ | |
41 | 1: { LOAD r0, r0; move r1, zero; move r2, zero }; \ | |
42 | jrp lr; \ | |
43 | STD_ENDPROC(__get_user_##bytes); \ | |
44 | .pushsection __ex_table,"a"; \ | |
45 | .word 1b, get_user_fault; \ | |
46 | .popsection | |
47 | ||
48 | __get_user_N(1, lb_u) | |
49 | __get_user_N(2, lh_u) | |
50 | __get_user_N(4, lw) | |
51 | ||
52 | /* | |
53 | * __get_user_8 takes a pointer in r0, and returns 0 in r2 | |
54 | * on success, with the value in r0/r1; or else -EFAULT in r2. | |
55 | */ | |
56 | STD_ENTRY(__get_user_8); | |
57 | 1: { lw r0, r0; addi r1, r0, 4 }; | |
58 | 2: { lw r1, r1; move r2, zero }; | |
59 | jrp lr; | |
60 | STD_ENDPROC(__get_user_8); | |
61 | .pushsection __ex_table,"a"; | |
62 | .word 1b, get_user_fault; | |
63 | .word 2b, get_user_fault; | |
64 | .popsection | |
65 | ||
66 | /* | |
67 | * __put_user_N functions take a value in r0 and a pointer in r1, | |
68 | * and return 0 in r0 on success or -EFAULT on failure. | |
69 | */ | |
70 | #define __put_user_N(bytes, STORE) \ | |
71 | STD_ENTRY(__put_user_##bytes); \ | |
72 | 1: { STORE r1, r0; move r0, zero }; \ | |
73 | jrp lr; \ | |
74 | STD_ENDPROC(__put_user_##bytes); \ | |
75 | .pushsection __ex_table,"a"; \ | |
76 | .word 1b, put_user_fault; \ | |
77 | .popsection | |
78 | ||
79 | __put_user_N(1, sb) | |
80 | __put_user_N(2, sh) | |
81 | __put_user_N(4, sw) | |
82 | ||
83 | /* | |
84 | * __put_user_8 takes a value in r0/r1 and a pointer in r2, | |
85 | * and returns 0 in r0 on success or -EFAULT on failure. | |
86 | */ | |
87 | STD_ENTRY(__put_user_8) | |
88 | 1: { sw r2, r0; addi r2, r2, 4 } | |
89 | 2: { sw r2, r1; move r0, zero } | |
90 | jrp lr | |
91 | STD_ENDPROC(__put_user_8) | |
92 | .pushsection __ex_table,"a" | |
93 | .word 1b, put_user_fault | |
94 | .word 2b, put_user_fault | |
95 | .popsection | |
96 | ||
97 | ||
98 | /* | |
99 | * strnlen_user_asm takes the pointer in r0, and the length bound in r1. | |
100 | * It returns the length, including the terminating NUL, or zero on exception. | |
101 | * If length is greater than the bound, returns one plus the bound. | |
102 | */ | |
103 | STD_ENTRY(strnlen_user_asm) | |
104 | { bz r1, 2f; addi r3, r0, -1 } /* bias down to include NUL */ | |
105 | 1: { lb_u r4, r0; addi r1, r1, -1 } | |
106 | bz r4, 2f | |
107 | { bnzt r1, 1b; addi r0, r0, 1 } | |
108 | 2: { sub r0, r0, r3; jrp lr } | |
109 | STD_ENDPROC(strnlen_user_asm) | |
110 | .pushsection .fixup,"ax" | |
111 | strnlen_user_fault: | |
112 | { move r0, zero; jrp lr } | |
113 | ENDPROC(strnlen_user_fault) | |
114 | .section __ex_table,"a" | |
115 | .word 1b, strnlen_user_fault | |
116 | .popsection | |
117 | ||
118 | /* | |
119 | * strncpy_from_user_asm takes the kernel target pointer in r0, | |
120 | * the userspace source pointer in r1, and the length bound (including | |
121 | * the trailing NUL) in r2. On success, it returns the string length | |
122 | * (not including the trailing NUL), or -EFAULT on failure. | |
123 | */ | |
124 | STD_ENTRY(strncpy_from_user_asm) | |
125 | { bz r2, 2f; move r3, r0 } | |
126 | 1: { lb_u r4, r1; addi r1, r1, 1; addi r2, r2, -1 } | |
127 | { sb r0, r4; addi r0, r0, 1 } | |
128 | bz r2, 2f | |
129 | bnzt r4, 1b | |
130 | addi r0, r0, -1 /* don't count the trailing NUL */ | |
131 | 2: { sub r0, r0, r3; jrp lr } | |
132 | STD_ENDPROC(strncpy_from_user_asm) | |
133 | .pushsection .fixup,"ax" | |
134 | strncpy_from_user_fault: | |
135 | { movei r0, -EFAULT; jrp lr } | |
136 | ENDPROC(strncpy_from_user_fault) | |
137 | .section __ex_table,"a" | |
138 | .word 1b, strncpy_from_user_fault | |
139 | .popsection | |
140 | ||
141 | /* | |
142 | * clear_user_asm takes the user target address in r0 and the | |
143 | * number of bytes to zero in r1. | |
144 | * It returns the number of uncopiable bytes (hopefully zero) in r0. | |
145 | * Note that we don't use a separate .fixup section here since we fall | |
146 | * through into the "fixup" code as the last straight-line bundle anyway. | |
147 | */ | |
148 | STD_ENTRY(clear_user_asm) | |
149 | { bz r1, 2f; or r2, r0, r1 } | |
150 | andi r2, r2, 3 | |
151 | bzt r2, .Lclear_aligned_user_asm | |
152 | 1: { sb r0, zero; addi r0, r0, 1; addi r1, r1, -1 } | |
153 | bnzt r1, 1b | |
154 | 2: { move r0, r1; jrp lr } | |
155 | .pushsection __ex_table,"a" | |
156 | .word 1b, 2b | |
157 | .popsection | |
158 | ||
159 | .Lclear_aligned_user_asm: | |
160 | 1: { sw r0, zero; addi r0, r0, 4; addi r1, r1, -4 } | |
161 | bnzt r1, 1b | |
162 | 2: { move r0, r1; jrp lr } | |
163 | STD_ENDPROC(clear_user_asm) | |
164 | .pushsection __ex_table,"a" | |
165 | .word 1b, 2b | |
166 | .popsection | |
167 | ||
168 | /* | |
169 | * flush_user_asm takes the user target address in r0 and the | |
170 | * number of bytes to flush in r1. | |
171 | * It returns the number of unflushable bytes (hopefully zero) in r0. | |
172 | */ | |
173 | STD_ENTRY(flush_user_asm) | |
174 | bz r1, 2f | |
175 | { movei r2, L2_CACHE_BYTES; add r1, r0, r1 } | |
176 | { sub r2, zero, r2; addi r1, r1, L2_CACHE_BYTES-1 } | |
177 | { and r0, r0, r2; and r1, r1, r2 } | |
178 | { sub r1, r1, r0 } | |
179 | 1: { flush r0; addi r1, r1, -CHIP_FLUSH_STRIDE() } | |
180 | { addi r0, r0, CHIP_FLUSH_STRIDE(); bnzt r1, 1b } | |
181 | 2: { move r0, r1; jrp lr } | |
182 | STD_ENDPROC(flush_user_asm) | |
183 | .pushsection __ex_table,"a" | |
184 | .word 1b, 2b | |
185 | .popsection | |
186 | ||
187 | /* | |
188 | * inv_user_asm takes the user target address in r0 and the | |
189 | * number of bytes to invalidate in r1. | |
190 | * It returns the number of not inv'able bytes (hopefully zero) in r0. | |
191 | */ | |
192 | STD_ENTRY(inv_user_asm) | |
193 | bz r1, 2f | |
194 | { movei r2, L2_CACHE_BYTES; add r1, r0, r1 } | |
195 | { sub r2, zero, r2; addi r1, r1, L2_CACHE_BYTES-1 } | |
196 | { and r0, r0, r2; and r1, r1, r2 } | |
197 | { sub r1, r1, r0 } | |
198 | 1: { inv r0; addi r1, r1, -CHIP_INV_STRIDE() } | |
199 | { addi r0, r0, CHIP_INV_STRIDE(); bnzt r1, 1b } | |
200 | 2: { move r0, r1; jrp lr } | |
201 | STD_ENDPROC(inv_user_asm) | |
202 | .pushsection __ex_table,"a" | |
203 | .word 1b, 2b | |
204 | .popsection | |
205 | ||
206 | /* | |
207 | * finv_user_asm takes the user target address in r0 and the | |
208 | * number of bytes to flush-invalidate in r1. | |
209 | * It returns the number of not finv'able bytes (hopefully zero) in r0. | |
210 | */ | |
211 | STD_ENTRY(finv_user_asm) | |
212 | bz r1, 2f | |
213 | { movei r2, L2_CACHE_BYTES; add r1, r0, r1 } | |
214 | { sub r2, zero, r2; addi r1, r1, L2_CACHE_BYTES-1 } | |
215 | { and r0, r0, r2; and r1, r1, r2 } | |
216 | { sub r1, r1, r0 } | |
217 | 1: { finv r0; addi r1, r1, -CHIP_FINV_STRIDE() } | |
218 | { addi r0, r0, CHIP_FINV_STRIDE(); bnzt r1, 1b } | |
219 | 2: { move r0, r1; jrp lr } | |
220 | STD_ENDPROC(finv_user_asm) | |
221 | .pushsection __ex_table,"a" | |
222 | .word 1b, 2b | |
223 | .popsection |