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