xref: /openbmc/linux/arch/x86/lib/copy_user_64.S (revision 42a886af)
1185f3d38SThomas Gleixner/* Copyright 2002 Andi Kleen, SuSE Labs.
2185f3d38SThomas Gleixner * Subject to the GNU Public License v2.
3185f3d38SThomas Gleixner *
4185f3d38SThomas Gleixner * Functions to copy from and to user space.
5185f3d38SThomas Gleixner */
6185f3d38SThomas Gleixner
7185f3d38SThomas Gleixner#include <linux/linkage.h>
8185f3d38SThomas Gleixner#include <asm/dwarf2.h>
9185f3d38SThomas Gleixner
10185f3d38SThomas Gleixner#define FIX_ALIGNMENT 1
11185f3d38SThomas Gleixner
12185f3d38SThomas Gleixner#include <asm/current.h>
13185f3d38SThomas Gleixner#include <asm/asm-offsets.h>
14185f3d38SThomas Gleixner#include <asm/thread_info.h>
15185f3d38SThomas Gleixner#include <asm/cpufeature.h>
16185f3d38SThomas Gleixner
17185f3d38SThomas Gleixner	.macro ALTERNATIVE_JUMP feature,orig,alt
18185f3d38SThomas Gleixner0:
19185f3d38SThomas Gleixner	.byte 0xe9	/* 32bit jump */
20185f3d38SThomas Gleixner	.long \orig-1f	/* by default jump to orig */
21185f3d38SThomas Gleixner1:
22185f3d38SThomas Gleixner	.section .altinstr_replacement,"ax"
23185f3d38SThomas Gleixner2:	.byte 0xe9	             /* near jump with 32bit immediate */
24185f3d38SThomas Gleixner	.long \alt-1b /* offset */   /* or alternatively to alt */
25185f3d38SThomas Gleixner	.previous
26185f3d38SThomas Gleixner	.section .altinstructions,"a"
27185f3d38SThomas Gleixner	.align 8
28185f3d38SThomas Gleixner	.quad  0b
29185f3d38SThomas Gleixner	.quad  2b
30185f3d38SThomas Gleixner	.byte  \feature		     /* when feature is set */
31185f3d38SThomas Gleixner	.byte  5
32185f3d38SThomas Gleixner	.byte  5
33185f3d38SThomas Gleixner	.previous
34185f3d38SThomas Gleixner	.endm
35185f3d38SThomas Gleixner
36185f3d38SThomas Gleixner/* Standard copy_to_user with segment limit checking */
37185f3d38SThomas GleixnerENTRY(copy_to_user)
38185f3d38SThomas Gleixner	CFI_STARTPROC
39185f3d38SThomas Gleixner	GET_THREAD_INFO(%rax)
40185f3d38SThomas Gleixner	movq %rdi,%rcx
41185f3d38SThomas Gleixner	addq %rdx,%rcx
42185f3d38SThomas Gleixner	jc  bad_to_user
43185f3d38SThomas Gleixner	cmpq threadinfo_addr_limit(%rax),%rcx
44185f3d38SThomas Gleixner	jae bad_to_user
45185f3d38SThomas Gleixner	xorl %eax,%eax	/* clear zero flag */
46185f3d38SThomas Gleixner	ALTERNATIVE_JUMP X86_FEATURE_REP_GOOD,copy_user_generic_unrolled,copy_user_generic_string
47185f3d38SThomas Gleixner	CFI_ENDPROC
48185f3d38SThomas Gleixner
49185f3d38SThomas GleixnerENTRY(copy_user_generic)
50185f3d38SThomas Gleixner	CFI_STARTPROC
51185f3d38SThomas Gleixner	movl $1,%ecx	/* set zero flag */
52185f3d38SThomas Gleixner	ALTERNATIVE_JUMP X86_FEATURE_REP_GOOD,copy_user_generic_unrolled,copy_user_generic_string
53185f3d38SThomas Gleixner	CFI_ENDPROC
54185f3d38SThomas Gleixner
55185f3d38SThomas GleixnerENTRY(__copy_from_user_inatomic)
56185f3d38SThomas Gleixner	CFI_STARTPROC
57185f3d38SThomas Gleixner	xorl %ecx,%ecx	/* clear zero flag */
58185f3d38SThomas Gleixner	ALTERNATIVE_JUMP X86_FEATURE_REP_GOOD,copy_user_generic_unrolled,copy_user_generic_string
59185f3d38SThomas Gleixner	CFI_ENDPROC
60185f3d38SThomas Gleixner
61185f3d38SThomas Gleixner/* Standard copy_from_user with segment limit checking */
62185f3d38SThomas GleixnerENTRY(copy_from_user)
63185f3d38SThomas Gleixner	CFI_STARTPROC
64185f3d38SThomas Gleixner	GET_THREAD_INFO(%rax)
65185f3d38SThomas Gleixner	movq %rsi,%rcx
66185f3d38SThomas Gleixner	addq %rdx,%rcx
67185f3d38SThomas Gleixner	jc  bad_from_user
68185f3d38SThomas Gleixner	cmpq threadinfo_addr_limit(%rax),%rcx
69185f3d38SThomas Gleixner	jae  bad_from_user
70185f3d38SThomas Gleixner	movl $1,%ecx	/* set zero flag */
71185f3d38SThomas Gleixner	ALTERNATIVE_JUMP X86_FEATURE_REP_GOOD,copy_user_generic_unrolled,copy_user_generic_string
72185f3d38SThomas Gleixner	CFI_ENDPROC
73185f3d38SThomas GleixnerENDPROC(copy_from_user)
74185f3d38SThomas Gleixner
75185f3d38SThomas Gleixner	.section .fixup,"ax"
76185f3d38SThomas Gleixner	/* must zero dest */
77185f3d38SThomas Gleixnerbad_from_user:
78185f3d38SThomas Gleixner	CFI_STARTPROC
79185f3d38SThomas Gleixner	movl %edx,%ecx
80185f3d38SThomas Gleixner	xorl %eax,%eax
81185f3d38SThomas Gleixner	rep
82185f3d38SThomas Gleixner	stosb
83185f3d38SThomas Gleixnerbad_to_user:
84185f3d38SThomas Gleixner	movl	%edx,%eax
85185f3d38SThomas Gleixner	ret
86185f3d38SThomas Gleixner	CFI_ENDPROC
87185f3d38SThomas GleixnerEND(bad_from_user)
88185f3d38SThomas Gleixner	.previous
89185f3d38SThomas Gleixner
90185f3d38SThomas Gleixner
91185f3d38SThomas Gleixner/*
92185f3d38SThomas Gleixner * copy_user_generic_unrolled - memory copy with exception handling.
93185f3d38SThomas Gleixner * This version is for CPUs like P4 that don't have efficient micro code for rep movsq
94185f3d38SThomas Gleixner *
95185f3d38SThomas Gleixner * Input:
96185f3d38SThomas Gleixner * rdi destination
97185f3d38SThomas Gleixner * rsi source
98185f3d38SThomas Gleixner * rdx count
99185f3d38SThomas Gleixner * ecx zero flag -- if true zero destination on error
100185f3d38SThomas Gleixner *
101185f3d38SThomas Gleixner * Output:
102185f3d38SThomas Gleixner * eax uncopied bytes or 0 if successful.
103185f3d38SThomas Gleixner */
104185f3d38SThomas GleixnerENTRY(copy_user_generic_unrolled)
105185f3d38SThomas Gleixner	CFI_STARTPROC
106185f3d38SThomas Gleixner	pushq %rbx
107185f3d38SThomas Gleixner	CFI_ADJUST_CFA_OFFSET 8
108185f3d38SThomas Gleixner	CFI_REL_OFFSET rbx, 0
109185f3d38SThomas Gleixner	pushq %rcx
110185f3d38SThomas Gleixner	CFI_ADJUST_CFA_OFFSET 8
111185f3d38SThomas Gleixner	CFI_REL_OFFSET rcx, 0
112185f3d38SThomas Gleixner	xorl %eax,%eax		/*zero for the exception handler */
113185f3d38SThomas Gleixner
114185f3d38SThomas Gleixner#ifdef FIX_ALIGNMENT
115185f3d38SThomas Gleixner	/* check for bad alignment of destination */
116185f3d38SThomas Gleixner	movl %edi,%ecx
117185f3d38SThomas Gleixner	andl $7,%ecx
118185f3d38SThomas Gleixner	jnz  .Lbad_alignment
119185f3d38SThomas Gleixner.Lafter_bad_alignment:
120185f3d38SThomas Gleixner#endif
121185f3d38SThomas Gleixner
122185f3d38SThomas Gleixner	movq %rdx,%rcx
123185f3d38SThomas Gleixner
124185f3d38SThomas Gleixner	movl $64,%ebx
125185f3d38SThomas Gleixner	shrq $6,%rdx
126185f3d38SThomas Gleixner	decq %rdx
127185f3d38SThomas Gleixner	js   .Lhandle_tail
128185f3d38SThomas Gleixner
129185f3d38SThomas Gleixner	.p2align 4
130185f3d38SThomas Gleixner.Lloop:
131185f3d38SThomas Gleixner.Ls1:	movq (%rsi),%r11
132185f3d38SThomas Gleixner.Ls2:	movq 1*8(%rsi),%r8
133185f3d38SThomas Gleixner.Ls3:	movq 2*8(%rsi),%r9
134185f3d38SThomas Gleixner.Ls4:	movq 3*8(%rsi),%r10
135185f3d38SThomas Gleixner.Ld1:	movq %r11,(%rdi)
136185f3d38SThomas Gleixner.Ld2:	movq %r8,1*8(%rdi)
137185f3d38SThomas Gleixner.Ld3:	movq %r9,2*8(%rdi)
138185f3d38SThomas Gleixner.Ld4:	movq %r10,3*8(%rdi)
139185f3d38SThomas Gleixner
140185f3d38SThomas Gleixner.Ls5:	movq 4*8(%rsi),%r11
141185f3d38SThomas Gleixner.Ls6:	movq 5*8(%rsi),%r8
142185f3d38SThomas Gleixner.Ls7:	movq 6*8(%rsi),%r9
143185f3d38SThomas Gleixner.Ls8:	movq 7*8(%rsi),%r10
144185f3d38SThomas Gleixner.Ld5:	movq %r11,4*8(%rdi)
145185f3d38SThomas Gleixner.Ld6:	movq %r8,5*8(%rdi)
146185f3d38SThomas Gleixner.Ld7:	movq %r9,6*8(%rdi)
147185f3d38SThomas Gleixner.Ld8:	movq %r10,7*8(%rdi)
148185f3d38SThomas Gleixner
149185f3d38SThomas Gleixner	decq %rdx
150185f3d38SThomas Gleixner
151185f3d38SThomas Gleixner	leaq 64(%rsi),%rsi
152185f3d38SThomas Gleixner	leaq 64(%rdi),%rdi
153185f3d38SThomas Gleixner
154185f3d38SThomas Gleixner	jns  .Lloop
155185f3d38SThomas Gleixner
156185f3d38SThomas Gleixner	.p2align 4
157185f3d38SThomas Gleixner.Lhandle_tail:
158185f3d38SThomas Gleixner	movl %ecx,%edx
159185f3d38SThomas Gleixner	andl $63,%ecx
160185f3d38SThomas Gleixner	shrl $3,%ecx
161185f3d38SThomas Gleixner	jz   .Lhandle_7
162185f3d38SThomas Gleixner	movl $8,%ebx
163185f3d38SThomas Gleixner	.p2align 4
164185f3d38SThomas Gleixner.Lloop_8:
165185f3d38SThomas Gleixner.Ls9:	movq (%rsi),%r8
166185f3d38SThomas Gleixner.Ld9:	movq %r8,(%rdi)
167185f3d38SThomas Gleixner	decl %ecx
168185f3d38SThomas Gleixner	leaq 8(%rdi),%rdi
169185f3d38SThomas Gleixner	leaq 8(%rsi),%rsi
170185f3d38SThomas Gleixner	jnz .Lloop_8
171185f3d38SThomas Gleixner
172185f3d38SThomas Gleixner.Lhandle_7:
173185f3d38SThomas Gleixner	movl %edx,%ecx
174185f3d38SThomas Gleixner	andl $7,%ecx
175185f3d38SThomas Gleixner	jz   .Lende
176185f3d38SThomas Gleixner	.p2align 4
177185f3d38SThomas Gleixner.Lloop_1:
178185f3d38SThomas Gleixner.Ls10:	movb (%rsi),%bl
179185f3d38SThomas Gleixner.Ld10:	movb %bl,(%rdi)
180185f3d38SThomas Gleixner	incq %rdi
181185f3d38SThomas Gleixner	incq %rsi
182185f3d38SThomas Gleixner	decl %ecx
183185f3d38SThomas Gleixner	jnz .Lloop_1
184185f3d38SThomas Gleixner
185185f3d38SThomas Gleixner	CFI_REMEMBER_STATE
186185f3d38SThomas Gleixner.Lende:
187185f3d38SThomas Gleixner	popq %rcx
188185f3d38SThomas Gleixner	CFI_ADJUST_CFA_OFFSET -8
189185f3d38SThomas Gleixner	CFI_RESTORE rcx
190185f3d38SThomas Gleixner	popq %rbx
191185f3d38SThomas Gleixner	CFI_ADJUST_CFA_OFFSET -8
192185f3d38SThomas Gleixner	CFI_RESTORE rbx
193185f3d38SThomas Gleixner	ret
194185f3d38SThomas Gleixner	CFI_RESTORE_STATE
195185f3d38SThomas Gleixner
196185f3d38SThomas Gleixner#ifdef FIX_ALIGNMENT
197185f3d38SThomas Gleixner	/* align destination */
198185f3d38SThomas Gleixner	.p2align 4
199185f3d38SThomas Gleixner.Lbad_alignment:
200185f3d38SThomas Gleixner	movl $8,%r9d
201185f3d38SThomas Gleixner	subl %ecx,%r9d
202185f3d38SThomas Gleixner	movl %r9d,%ecx
203185f3d38SThomas Gleixner	cmpq %r9,%rdx
204185f3d38SThomas Gleixner	jz   .Lhandle_7
205185f3d38SThomas Gleixner	js   .Lhandle_7
206185f3d38SThomas Gleixner.Lalign_1:
207185f3d38SThomas Gleixner.Ls11:	movb (%rsi),%bl
208185f3d38SThomas Gleixner.Ld11:	movb %bl,(%rdi)
209185f3d38SThomas Gleixner	incq %rsi
210185f3d38SThomas Gleixner	incq %rdi
211185f3d38SThomas Gleixner	decl %ecx
212185f3d38SThomas Gleixner	jnz .Lalign_1
213185f3d38SThomas Gleixner	subq %r9,%rdx
214185f3d38SThomas Gleixner	jmp .Lafter_bad_alignment
215185f3d38SThomas Gleixner#endif
216185f3d38SThomas Gleixner
217185f3d38SThomas Gleixner	/* table sorted by exception address */
218185f3d38SThomas Gleixner	.section __ex_table,"a"
219185f3d38SThomas Gleixner	.align 8
22042a886afSLinus Torvalds	.quad .Ls1,.Ls1e	/* Ls1-Ls4 have copied zero bytes */
22142a886afSLinus Torvalds	.quad .Ls2,.Ls1e
22242a886afSLinus Torvalds	.quad .Ls3,.Ls1e
22342a886afSLinus Torvalds	.quad .Ls4,.Ls1e
22442a886afSLinus Torvalds	.quad .Ld1,.Ls1e	/* Ld1-Ld4 have copied 0-24 bytes */
225185f3d38SThomas Gleixner	.quad .Ld2,.Ls2e
226185f3d38SThomas Gleixner	.quad .Ld3,.Ls3e
227185f3d38SThomas Gleixner	.quad .Ld4,.Ls4e
22842a886afSLinus Torvalds	.quad .Ls5,.Ls5e	/* Ls5-Ls8 have copied 32 bytes */
22942a886afSLinus Torvalds	.quad .Ls6,.Ls5e
23042a886afSLinus Torvalds	.quad .Ls7,.Ls5e
23142a886afSLinus Torvalds	.quad .Ls8,.Ls5e
23242a886afSLinus Torvalds	.quad .Ld5,.Ls5e	/* Ld5-Ld8 have copied 32-56 bytes */
233185f3d38SThomas Gleixner	.quad .Ld6,.Ls6e
234185f3d38SThomas Gleixner	.quad .Ld7,.Ls7e
235185f3d38SThomas Gleixner	.quad .Ld8,.Ls8e
236185f3d38SThomas Gleixner	.quad .Ls9,.Le_quad
237185f3d38SThomas Gleixner	.quad .Ld9,.Le_quad
238185f3d38SThomas Gleixner	.quad .Ls10,.Le_byte
239185f3d38SThomas Gleixner	.quad .Ld10,.Le_byte
240185f3d38SThomas Gleixner#ifdef FIX_ALIGNMENT
241185f3d38SThomas Gleixner	.quad .Ls11,.Lzero_rest
242185f3d38SThomas Gleixner	.quad .Ld11,.Lzero_rest
243185f3d38SThomas Gleixner#endif
244185f3d38SThomas Gleixner	.quad .Le5,.Le_zero
245185f3d38SThomas Gleixner	.previous
246185f3d38SThomas Gleixner
247185f3d38SThomas Gleixner	/* eax: zero, ebx: 64 */
24842a886afSLinus Torvalds.Ls1e: 	addl $8,%eax		/* eax is bytes left uncopied within the loop (Ls1e: 64 .. Ls8e: 8) */
249185f3d38SThomas Gleixner.Ls2e: 	addl $8,%eax
250185f3d38SThomas Gleixner.Ls3e: 	addl $8,%eax
251185f3d38SThomas Gleixner.Ls4e: 	addl $8,%eax
252185f3d38SThomas Gleixner.Ls5e: 	addl $8,%eax
253185f3d38SThomas Gleixner.Ls6e: 	addl $8,%eax
254185f3d38SThomas Gleixner.Ls7e: 	addl $8,%eax
255185f3d38SThomas Gleixner.Ls8e: 	addl $8,%eax
256185f3d38SThomas Gleixner	addq %rbx,%rdi	/* +64 */
257185f3d38SThomas Gleixner	subq %rax,%rdi  /* correct destination with computed offset */
258185f3d38SThomas Gleixner
259185f3d38SThomas Gleixner	shlq $6,%rdx	/* loop counter * 64 (stride length) */
260185f3d38SThomas Gleixner	addq %rax,%rdx	/* add offset to loopcnt */
261185f3d38SThomas Gleixner	andl $63,%ecx	/* remaining bytes */
262185f3d38SThomas Gleixner	addq %rcx,%rdx	/* add them */
263185f3d38SThomas Gleixner	jmp .Lzero_rest
264185f3d38SThomas Gleixner
265185f3d38SThomas Gleixner	/* exception on quad word loop in tail handling */
266185f3d38SThomas Gleixner	/* ecx:	loopcnt/8, %edx: length, rdi: correct */
267185f3d38SThomas Gleixner.Le_quad:
268185f3d38SThomas Gleixner	shll $3,%ecx
269185f3d38SThomas Gleixner	andl $7,%edx
270185f3d38SThomas Gleixner	addl %ecx,%edx
271185f3d38SThomas Gleixner	/* edx: bytes to zero, rdi: dest, eax:zero */
272185f3d38SThomas Gleixner.Lzero_rest:
273185f3d38SThomas Gleixner	cmpl $0,(%rsp)
274185f3d38SThomas Gleixner	jz   .Le_zero
275185f3d38SThomas Gleixner	movq %rdx,%rcx
276185f3d38SThomas Gleixner.Le_byte:
277185f3d38SThomas Gleixner	xorl %eax,%eax
278185f3d38SThomas Gleixner.Le5:	rep
279185f3d38SThomas Gleixner	stosb
280185f3d38SThomas Gleixner	/* when there is another exception while zeroing the rest just return */
281185f3d38SThomas Gleixner.Le_zero:
282185f3d38SThomas Gleixner	movq %rdx,%rax
283185f3d38SThomas Gleixner	jmp .Lende
284185f3d38SThomas Gleixner	CFI_ENDPROC
285185f3d38SThomas GleixnerENDPROC(copy_user_generic)
286185f3d38SThomas Gleixner
287185f3d38SThomas Gleixner
288185f3d38SThomas Gleixner	/* Some CPUs run faster using the string copy instructions.
289185f3d38SThomas Gleixner	   This is also a lot simpler. Use them when possible.
290185f3d38SThomas Gleixner	   Patch in jmps to this code instead of copying it fully
291185f3d38SThomas Gleixner	   to avoid unwanted aliasing in the exception tables. */
292185f3d38SThomas Gleixner
293185f3d38SThomas Gleixner /* rdi	destination
294185f3d38SThomas Gleixner  * rsi source
295185f3d38SThomas Gleixner  * rdx count
296185f3d38SThomas Gleixner  * ecx zero flag
297185f3d38SThomas Gleixner  *
298185f3d38SThomas Gleixner  * Output:
299185f3d38SThomas Gleixner  * eax uncopied bytes or 0 if successfull.
300185f3d38SThomas Gleixner  *
301185f3d38SThomas Gleixner  * Only 4GB of copy is supported. This shouldn't be a problem
302185f3d38SThomas Gleixner  * because the kernel normally only writes from/to page sized chunks
303185f3d38SThomas Gleixner  * even if user space passed a longer buffer.
304185f3d38SThomas Gleixner  * And more would be dangerous because both Intel and AMD have
305185f3d38SThomas Gleixner  * errata with rep movsq > 4GB. If someone feels the need to fix
306185f3d38SThomas Gleixner  * this please consider this.
307185f3d38SThomas Gleixner  */
308185f3d38SThomas GleixnerENTRY(copy_user_generic_string)
309185f3d38SThomas Gleixner	CFI_STARTPROC
310185f3d38SThomas Gleixner	movl %ecx,%r8d		/* save zero flag */
311185f3d38SThomas Gleixner	movl %edx,%ecx
312185f3d38SThomas Gleixner	shrl $3,%ecx
313185f3d38SThomas Gleixner	andl $7,%edx
314185f3d38SThomas Gleixner	jz   10f
315185f3d38SThomas Gleixner1:	rep
316185f3d38SThomas Gleixner	movsq
317185f3d38SThomas Gleixner	movl %edx,%ecx
318185f3d38SThomas Gleixner2:	rep
319185f3d38SThomas Gleixner	movsb
320185f3d38SThomas Gleixner9:	movl %ecx,%eax
321185f3d38SThomas Gleixner	ret
322185f3d38SThomas Gleixner
323185f3d38SThomas Gleixner	/* multiple of 8 byte */
324185f3d38SThomas Gleixner10:	rep
325185f3d38SThomas Gleixner	movsq
326185f3d38SThomas Gleixner	xor %eax,%eax
327185f3d38SThomas Gleixner	ret
328185f3d38SThomas Gleixner
329185f3d38SThomas Gleixner	/* exception handling */
330185f3d38SThomas Gleixner3:      lea (%rdx,%rcx,8),%rax	/* exception on quad loop */
331185f3d38SThomas Gleixner	jmp 6f
332185f3d38SThomas Gleixner5:	movl %ecx,%eax		/* exception on byte loop */
333185f3d38SThomas Gleixner	/* eax: left over bytes */
334185f3d38SThomas Gleixner6:	testl %r8d,%r8d		/* zero flag set? */
335185f3d38SThomas Gleixner	jz 7f
336185f3d38SThomas Gleixner	movl %eax,%ecx		/* initialize x86 loop counter */
337185f3d38SThomas Gleixner	push %rax
338185f3d38SThomas Gleixner	xorl %eax,%eax
339185f3d38SThomas Gleixner8:	rep
340185f3d38SThomas Gleixner	stosb 			/* zero the rest */
341185f3d38SThomas Gleixner11:	pop %rax
342185f3d38SThomas Gleixner7:	ret
343185f3d38SThomas Gleixner	CFI_ENDPROC
344185f3d38SThomas GleixnerEND(copy_user_generic_c)
345185f3d38SThomas Gleixner
346185f3d38SThomas Gleixner	.section __ex_table,"a"
347185f3d38SThomas Gleixner	.quad 1b,3b
348185f3d38SThomas Gleixner	.quad 2b,5b
349185f3d38SThomas Gleixner	.quad 8b,11b
350185f3d38SThomas Gleixner	.quad 10b,3b
351185f3d38SThomas Gleixner	.previous
352