blob: 7eaaf0123b4d9a7ff8f1923591346ccd514f041e [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* Copyright 2002 Andi Kleen, SuSE Labs.
2 * Subject to the GNU Public License v2.
3 *
4 * Functions to copy from and to user space.
5 */
6
Jan Beulich8d379da2006-09-26 10:52:32 +02007#include <linux/linkage.h>
8#include <asm/dwarf2.h>
9
Andi Kleen7bcd3f32006-02-03 21:51:02 +010010#define FIX_ALIGNMENT 1
11
Andi Kleen3022d732006-09-26 10:52:39 +020012#include <asm/current.h>
13#include <asm/asm-offsets.h>
14#include <asm/thread_info.h>
15#include <asm/cpufeature.h>
16
17 .macro ALTERNATIVE_JUMP feature,orig,alt
180:
19 .byte 0xe9 /* 32bit jump */
20 .long \orig-1f /* by default jump to orig */
211:
22 .section .altinstr_replacement,"ax"
232: .byte 0xe9 /* near jump with 32bit immediate */
24 .long \alt-1b /* offset */ /* or alternatively to alt */
25 .previous
26 .section .altinstructions,"a"
27 .align 8
28 .quad 0b
29 .quad 2b
30 .byte \feature /* when feature is set */
31 .byte 5
32 .byte 5
33 .previous
34 .endm
Linus Torvalds1da177e2005-04-16 15:20:36 -070035
36/* Standard copy_to_user with segment limit checking */
Jan Beulich8d379da2006-09-26 10:52:32 +020037ENTRY(copy_to_user)
38 CFI_STARTPROC
Linus Torvalds1da177e2005-04-16 15:20:36 -070039 GET_THREAD_INFO(%rax)
40 movq %rdi,%rcx
41 addq %rdx,%rcx
42 jc bad_to_user
Glauber Costa26ccb8a2008-06-24 11:19:35 -030043 cmpq TI_addr_limit(%rax),%rcx
Linus Torvalds1da177e2005-04-16 15:20:36 -070044 jae bad_to_user
Andi Kleen3022d732006-09-26 10:52:39 +020045 xorl %eax,%eax /* clear zero flag */
46 ALTERNATIVE_JUMP X86_FEATURE_REP_GOOD,copy_user_generic_unrolled,copy_user_generic_string
Jan Beulich8d379da2006-09-26 10:52:32 +020047 CFI_ENDPROC
Andi Kleen7bcd3f32006-02-03 21:51:02 +010048
Andi Kleen3022d732006-09-26 10:52:39 +020049ENTRY(copy_user_generic)
50 CFI_STARTPROC
51 movl $1,%ecx /* set zero flag */
52 ALTERNATIVE_JUMP X86_FEATURE_REP_GOOD,copy_user_generic_unrolled,copy_user_generic_string
53 CFI_ENDPROC
54
55ENTRY(__copy_from_user_inatomic)
56 CFI_STARTPROC
57 xorl %ecx,%ecx /* clear zero flag */
58 ALTERNATIVE_JUMP X86_FEATURE_REP_GOOD,copy_user_generic_unrolled,copy_user_generic_string
59 CFI_ENDPROC
Linus Torvalds1da177e2005-04-16 15:20:36 -070060
61/* Standard copy_from_user with segment limit checking */
Jan Beulich8d379da2006-09-26 10:52:32 +020062ENTRY(copy_from_user)
63 CFI_STARTPROC
Linus Torvalds1da177e2005-04-16 15:20:36 -070064 GET_THREAD_INFO(%rax)
65 movq %rsi,%rcx
66 addq %rdx,%rcx
67 jc bad_from_user
Glauber Costa26ccb8a2008-06-24 11:19:35 -030068 cmpq TI_addr_limit(%rax),%rcx
Linus Torvalds1da177e2005-04-16 15:20:36 -070069 jae bad_from_user
Andi Kleen3022d732006-09-26 10:52:39 +020070 movl $1,%ecx /* set zero flag */
71 ALTERNATIVE_JUMP X86_FEATURE_REP_GOOD,copy_user_generic_unrolled,copy_user_generic_string
Jan Beulich8d379da2006-09-26 10:52:32 +020072 CFI_ENDPROC
73ENDPROC(copy_from_user)
Linus Torvalds1da177e2005-04-16 15:20:36 -070074
75 .section .fixup,"ax"
76 /* must zero dest */
77bad_from_user:
Jan Beulich8d379da2006-09-26 10:52:32 +020078 CFI_STARTPROC
Linus Torvalds1da177e2005-04-16 15:20:36 -070079 movl %edx,%ecx
80 xorl %eax,%eax
81 rep
82 stosb
83bad_to_user:
84 movl %edx,%eax
85 ret
Jan Beulich8d379da2006-09-26 10:52:32 +020086 CFI_ENDPROC
87END(bad_from_user)
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 .previous
89
90
91/*
Andi Kleen3022d732006-09-26 10:52:39 +020092 * copy_user_generic_unrolled - memory copy with exception handling.
93 * This version is for CPUs like P4 that don't have efficient micro code for rep movsq
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 *
95 * Input:
96 * rdi destination
97 * rsi source
98 * rdx count
Andi Kleen3022d732006-09-26 10:52:39 +020099 * ecx zero flag -- if true zero destination on error
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100 *
101 * Output:
102 * eax uncopied bytes or 0 if successful.
103 */
Andi Kleen3022d732006-09-26 10:52:39 +0200104ENTRY(copy_user_generic_unrolled)
Jan Beulich8d379da2006-09-26 10:52:32 +0200105 CFI_STARTPROC
Andi Kleen7bcd3f32006-02-03 21:51:02 +0100106 pushq %rbx
Jan Beulich8d379da2006-09-26 10:52:32 +0200107 CFI_ADJUST_CFA_OFFSET 8
108 CFI_REL_OFFSET rbx, 0
Andi Kleen3022d732006-09-26 10:52:39 +0200109 pushq %rcx
110 CFI_ADJUST_CFA_OFFSET 8
111 CFI_REL_OFFSET rcx, 0
Andi Kleen7bcd3f32006-02-03 21:51:02 +0100112 xorl %eax,%eax /*zero for the exception handler */
113
114#ifdef FIX_ALIGNMENT
115 /* check for bad alignment of destination */
116 movl %edi,%ecx
117 andl $7,%ecx
118 jnz .Lbad_alignment
119.Lafter_bad_alignment:
120#endif
121
122 movq %rdx,%rcx
123
124 movl $64,%ebx
125 shrq $6,%rdx
126 decq %rdx
127 js .Lhandle_tail
128
129 .p2align 4
130.Lloop:
131.Ls1: movq (%rsi),%r11
132.Ls2: movq 1*8(%rsi),%r8
133.Ls3: movq 2*8(%rsi),%r9
134.Ls4: movq 3*8(%rsi),%r10
135.Ld1: movq %r11,(%rdi)
136.Ld2: movq %r8,1*8(%rdi)
137.Ld3: movq %r9,2*8(%rdi)
138.Ld4: movq %r10,3*8(%rdi)
139
140.Ls5: movq 4*8(%rsi),%r11
141.Ls6: movq 5*8(%rsi),%r8
142.Ls7: movq 6*8(%rsi),%r9
143.Ls8: movq 7*8(%rsi),%r10
144.Ld5: movq %r11,4*8(%rdi)
145.Ld6: movq %r8,5*8(%rdi)
146.Ld7: movq %r9,6*8(%rdi)
147.Ld8: movq %r10,7*8(%rdi)
148
149 decq %rdx
150
151 leaq 64(%rsi),%rsi
152 leaq 64(%rdi),%rdi
153
154 jns .Lloop
155
156 .p2align 4
157.Lhandle_tail:
158 movl %ecx,%edx
159 andl $63,%ecx
160 shrl $3,%ecx
161 jz .Lhandle_7
162 movl $8,%ebx
163 .p2align 4
164.Lloop_8:
165.Ls9: movq (%rsi),%r8
166.Ld9: movq %r8,(%rdi)
167 decl %ecx
168 leaq 8(%rdi),%rdi
169 leaq 8(%rsi),%rsi
170 jnz .Lloop_8
171
172.Lhandle_7:
173 movl %edx,%ecx
174 andl $7,%ecx
175 jz .Lende
176 .p2align 4
177.Lloop_1:
178.Ls10: movb (%rsi),%bl
179.Ld10: movb %bl,(%rdi)
180 incq %rdi
181 incq %rsi
182 decl %ecx
183 jnz .Lloop_1
184
Jan Beulich8d379da2006-09-26 10:52:32 +0200185 CFI_REMEMBER_STATE
Andi Kleen7bcd3f32006-02-03 21:51:02 +0100186.Lende:
Andi Kleen3022d732006-09-26 10:52:39 +0200187 popq %rcx
188 CFI_ADJUST_CFA_OFFSET -8
189 CFI_RESTORE rcx
Andi Kleen7bcd3f32006-02-03 21:51:02 +0100190 popq %rbx
Jan Beulich8d379da2006-09-26 10:52:32 +0200191 CFI_ADJUST_CFA_OFFSET -8
192 CFI_RESTORE rbx
Andi Kleen7bcd3f32006-02-03 21:51:02 +0100193 ret
Jan Beulich8d379da2006-09-26 10:52:32 +0200194 CFI_RESTORE_STATE
Andi Kleen7bcd3f32006-02-03 21:51:02 +0100195
196#ifdef FIX_ALIGNMENT
197 /* align destination */
198 .p2align 4
199.Lbad_alignment:
200 movl $8,%r9d
201 subl %ecx,%r9d
202 movl %r9d,%ecx
203 cmpq %r9,%rdx
204 jz .Lhandle_7
205 js .Lhandle_7
206.Lalign_1:
207.Ls11: movb (%rsi),%bl
208.Ld11: movb %bl,(%rdi)
209 incq %rsi
210 incq %rdi
211 decl %ecx
212 jnz .Lalign_1
213 subq %r9,%rdx
214 jmp .Lafter_bad_alignment
215#endif
216
217 /* table sorted by exception address */
218 .section __ex_table,"a"
219 .align 8
Linus Torvalds42a886a2008-06-17 17:47:50 -0700220 .quad .Ls1,.Ls1e /* Ls1-Ls4 have copied zero bytes */
221 .quad .Ls2,.Ls1e
222 .quad .Ls3,.Ls1e
223 .quad .Ls4,.Ls1e
224 .quad .Ld1,.Ls1e /* Ld1-Ld4 have copied 0-24 bytes */
Andi Kleen7bcd3f32006-02-03 21:51:02 +0100225 .quad .Ld2,.Ls2e
226 .quad .Ld3,.Ls3e
227 .quad .Ld4,.Ls4e
Linus Torvalds42a886a2008-06-17 17:47:50 -0700228 .quad .Ls5,.Ls5e /* Ls5-Ls8 have copied 32 bytes */
229 .quad .Ls6,.Ls5e
230 .quad .Ls7,.Ls5e
231 .quad .Ls8,.Ls5e
232 .quad .Ld5,.Ls5e /* Ld5-Ld8 have copied 32-56 bytes */
Andi Kleen7bcd3f32006-02-03 21:51:02 +0100233 .quad .Ld6,.Ls6e
234 .quad .Ld7,.Ls7e
235 .quad .Ld8,.Ls8e
236 .quad .Ls9,.Le_quad
237 .quad .Ld9,.Le_quad
238 .quad .Ls10,.Le_byte
239 .quad .Ld10,.Le_byte
240#ifdef FIX_ALIGNMENT
241 .quad .Ls11,.Lzero_rest
242 .quad .Ld11,.Lzero_rest
243#endif
244 .quad .Le5,.Le_zero
245 .previous
246
Andi Kleen7bcd3f32006-02-03 21:51:02 +0100247 /* eax: zero, ebx: 64 */
Linus Torvalds42a886a2008-06-17 17:47:50 -0700248.Ls1e: addl $8,%eax /* eax is bytes left uncopied within the loop (Ls1e: 64 .. Ls8e: 8) */
Andi Kleen7bcd3f32006-02-03 21:51:02 +0100249.Ls2e: addl $8,%eax
250.Ls3e: addl $8,%eax
251.Ls4e: addl $8,%eax
252.Ls5e: addl $8,%eax
253.Ls6e: addl $8,%eax
254.Ls7e: addl $8,%eax
255.Ls8e: addl $8,%eax
256 addq %rbx,%rdi /* +64 */
257 subq %rax,%rdi /* correct destination with computed offset */
258
259 shlq $6,%rdx /* loop counter * 64 (stride length) */
260 addq %rax,%rdx /* add offset to loopcnt */
261 andl $63,%ecx /* remaining bytes */
262 addq %rcx,%rdx /* add them */
263 jmp .Lzero_rest
264
265 /* exception on quad word loop in tail handling */
266 /* ecx: loopcnt/8, %edx: length, rdi: correct */
267.Le_quad:
268 shll $3,%ecx
269 andl $7,%edx
270 addl %ecx,%edx
271 /* edx: bytes to zero, rdi: dest, eax:zero */
272.Lzero_rest:
Andi Kleen3022d732006-09-26 10:52:39 +0200273 cmpl $0,(%rsp)
274 jz .Le_zero
Andi Kleen7bcd3f32006-02-03 21:51:02 +0100275 movq %rdx,%rcx
276.Le_byte:
277 xorl %eax,%eax
278.Le5: rep
279 stosb
280 /* when there is another exception while zeroing the rest just return */
281.Le_zero:
282 movq %rdx,%rax
283 jmp .Lende
Jan Beulich8d379da2006-09-26 10:52:32 +0200284 CFI_ENDPROC
285ENDPROC(copy_user_generic)
286
Andi Kleen7bcd3f32006-02-03 21:51:02 +0100287
288 /* Some CPUs run faster using the string copy instructions.
289 This is also a lot simpler. Use them when possible.
290 Patch in jmps to this code instead of copying it fully
291 to avoid unwanted aliasing in the exception tables. */
292
293 /* rdi destination
294 * rsi source
295 * rdx count
Andi Kleen3022d732006-09-26 10:52:39 +0200296 * ecx zero flag
Andi Kleen7bcd3f32006-02-03 21:51:02 +0100297 *
298 * Output:
299 * eax uncopied bytes or 0 if successfull.
300 *
301 * Only 4GB of copy is supported. This shouldn't be a problem
302 * because the kernel normally only writes from/to page sized chunks
303 * even if user space passed a longer buffer.
304 * And more would be dangerous because both Intel and AMD have
305 * errata with rep movsq > 4GB. If someone feels the need to fix
306 * this please consider this.
Andi Kleen3022d732006-09-26 10:52:39 +0200307 */
308ENTRY(copy_user_generic_string)
Jan Beulich8d379da2006-09-26 10:52:32 +0200309 CFI_STARTPROC
Andi Kleen3022d732006-09-26 10:52:39 +0200310 movl %ecx,%r8d /* save zero flag */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 movl %edx,%ecx
312 shrl $3,%ecx
313 andl $7,%edx
Andi Kleen3022d732006-09-26 10:52:39 +0200314 jz 10f
Linus Torvalds1da177e2005-04-16 15:20:36 -07003151: rep
316 movsq
317 movl %edx,%ecx
3182: rep
319 movsb
Andi Kleen3022d732006-09-26 10:52:39 +02003209: movl %ecx,%eax
Andi Kleen2cbc9ee2006-01-11 22:44:45 +0100321 ret
Andi Kleen3022d732006-09-26 10:52:39 +0200322
323 /* multiple of 8 byte */
32410: rep
325 movsq
326 xor %eax,%eax
Andi Kleen7bcd3f32006-02-03 21:51:02 +0100327 ret
Andi Kleen3022d732006-09-26 10:52:39 +0200328
329 /* exception handling */
3303: lea (%rdx,%rcx,8),%rax /* exception on quad loop */
331 jmp 6f
3325: movl %ecx,%eax /* exception on byte loop */
333 /* eax: left over bytes */
3346: testl %r8d,%r8d /* zero flag set? */
335 jz 7f
336 movl %eax,%ecx /* initialize x86 loop counter */
337 push %rax
338 xorl %eax,%eax
3398: rep
340 stosb /* zero the rest */
34111: pop %rax
3427: ret
Jan Beulich8d379da2006-09-26 10:52:32 +0200343 CFI_ENDPROC
344END(copy_user_generic_c)
Andi Kleen2cbc9ee2006-01-11 22:44:45 +0100345
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 .section __ex_table,"a"
347 .quad 1b,3b
Andi Kleen3022d732006-09-26 10:52:39 +0200348 .quad 2b,5b
349 .quad 8b,11b
350 .quad 10b,3b
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 .previous