arch/tile: core support for Tilera 32-bit chips.
[linux-3.10.git] / arch / tile / lib / usercopy_32.S
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