| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- /* SPDX-License-Identifier: GPL-2.0-only */
- /*
- * Copyright 2023 Linus Torvalds <torvalds@linux-foundation.org>
- */
- #include <linux/export.h>
- #include <linux/linkage.h>
- #include <asm/asm.h>
- /*
- * copy_user_nocache - Uncached memory copy with exception handling
- *
- * This copies from user space into kernel space, but the kernel
- * space accesses can take a machine check exception, so they too
- * need exception handling.
- *
- * Note: only 32-bit and 64-bit stores have non-temporal versions,
- * and we only use aligned versions. Any unaligned parts at the
- * start or end of the copy will be done using normal cached stores.
- *
- * Input:
- * rdi destination
- * rsi source
- * edx count
- *
- * Output:
- * rax uncopied bytes or 0 if successful.
- */
- SYM_FUNC_START(__copy_user_nocache)
- /* If destination is not 7-byte aligned, we'll have to align it */
- testb $7,%dil
- jne .Lalign
- .Lis_aligned:
- cmp $64,%edx
- jb .Lquadwords
- .p2align 4,0x90
- .Lunrolled:
- 10: movq (%rsi),%r8
- 11: movq 8(%rsi),%r9
- 12: movq 16(%rsi),%r10
- 13: movq 24(%rsi),%r11
- 20: movnti %r8,(%rdi)
- 21: movnti %r9,8(%rdi)
- 22: movnti %r10,16(%rdi)
- 23: movnti %r11,24(%rdi)
- 30: movq 32(%rsi),%r8
- 31: movq 40(%rsi),%r9
- 32: movq 48(%rsi),%r10
- 33: movq 56(%rsi),%r11
- 40: movnti %r8,32(%rdi)
- 41: movnti %r9,40(%rdi)
- 42: movnti %r10,48(%rdi)
- 43: movnti %r11,56(%rdi)
- addq $64,%rsi
- addq $64,%rdi
- sub $64,%edx
- cmp $64,%edx
- jae .Lunrolled
- /*
- * First set of user mode loads have been done
- * without any stores, so if they fail, we can
- * just try the non-unrolled loop.
- */
- _ASM_EXTABLE_UA(10b, .Lquadwords)
- _ASM_EXTABLE_UA(11b, .Lquadwords)
- _ASM_EXTABLE_UA(12b, .Lquadwords)
- _ASM_EXTABLE_UA(13b, .Lquadwords)
- /*
- * The second set of user mode loads have been
- * done with 32 bytes stored to the destination,
- * so we need to take that into account before
- * falling back to the unrolled loop.
- */
- _ASM_EXTABLE_UA(30b, .Lfixup32)
- _ASM_EXTABLE_UA(31b, .Lfixup32)
- _ASM_EXTABLE_UA(32b, .Lfixup32)
- _ASM_EXTABLE_UA(33b, .Lfixup32)
- /*
- * An exception on a write means that we're
- * done, but we need to update the count
- * depending on where in the unrolled loop
- * we were.
- */
- _ASM_EXTABLE_UA(20b, .Ldone0)
- _ASM_EXTABLE_UA(21b, .Ldone8)
- _ASM_EXTABLE_UA(22b, .Ldone16)
- _ASM_EXTABLE_UA(23b, .Ldone24)
- _ASM_EXTABLE_UA(40b, .Ldone32)
- _ASM_EXTABLE_UA(41b, .Ldone40)
- _ASM_EXTABLE_UA(42b, .Ldone48)
- _ASM_EXTABLE_UA(43b, .Ldone56)
- .Lquadwords:
- cmp $8,%edx
- jb .Llong
- 50: movq (%rsi),%rax
- 51: movnti %rax,(%rdi)
- addq $8,%rsi
- addq $8,%rdi
- sub $8,%edx
- jmp .Lquadwords
- /*
- * If we fail on the last full quadword, we will
- * not try to do any byte-wise cached accesses.
- * We will try to do one more 4-byte uncached
- * one, though.
- */
- _ASM_EXTABLE_UA(50b, .Llast4)
- _ASM_EXTABLE_UA(51b, .Ldone0)
- .Llong:
- test $4,%dl
- je .Lword
- 60: movl (%rsi),%eax
- 61: movnti %eax,(%rdi)
- addq $4,%rsi
- addq $4,%rdi
- sub $4,%edx
- .Lword:
- sfence
- test $2,%dl
- je .Lbyte
- 70: movw (%rsi),%ax
- 71: movw %ax,(%rdi)
- addq $2,%rsi
- addq $2,%rdi
- sub $2,%edx
- .Lbyte:
- test $1,%dl
- je .Ldone
- 80: movb (%rsi),%al
- 81: movb %al,(%rdi)
- dec %edx
- .Ldone:
- mov %edx,%eax
- RET
- /*
- * If we fail on the last four bytes, we won't
- * bother with any fixups. It's dead, Jim. Note
- * that there's no need for 'sfence' for any
- * of this, since the exception will have been
- * serializing.
- */
- _ASM_EXTABLE_UA(60b, .Ldone)
- _ASM_EXTABLE_UA(61b, .Ldone)
- _ASM_EXTABLE_UA(70b, .Ldone)
- _ASM_EXTABLE_UA(71b, .Ldone)
- _ASM_EXTABLE_UA(80b, .Ldone)
- _ASM_EXTABLE_UA(81b, .Ldone)
- /*
- * This is the "head needs aliging" case when
- * the destination isn't 8-byte aligned. The
- * 4-byte case can be done uncached, but any
- * smaller alignment is done with regular stores.
- */
- .Lalign:
- test $1,%dil
- je .Lalign_word
- test %edx,%edx
- je .Ldone
- 90: movb (%rsi),%al
- 91: movb %al,(%rdi)
- inc %rsi
- inc %rdi
- dec %edx
- .Lalign_word:
- test $2,%dil
- je .Lalign_long
- cmp $2,%edx
- jb .Lbyte
- 92: movw (%rsi),%ax
- 93: movw %ax,(%rdi)
- addq $2,%rsi
- addq $2,%rdi
- sub $2,%edx
- .Lalign_long:
- test $4,%dil
- je .Lis_aligned
- cmp $4,%edx
- jb .Lword
- 94: movl (%rsi),%eax
- 95: movnti %eax,(%rdi)
- addq $4,%rsi
- addq $4,%rdi
- sub $4,%edx
- jmp .Lis_aligned
- /*
- * If we fail on the initial alignment accesses,
- * we're all done. Again, no point in trying to
- * do byte-by-byte probing if the 4-byte load
- * fails - we're not doing any uncached accesses
- * any more.
- */
- _ASM_EXTABLE_UA(90b, .Ldone)
- _ASM_EXTABLE_UA(91b, .Ldone)
- _ASM_EXTABLE_UA(92b, .Ldone)
- _ASM_EXTABLE_UA(93b, .Ldone)
- _ASM_EXTABLE_UA(94b, .Ldone)
- _ASM_EXTABLE_UA(95b, .Ldone)
- /*
- * Exception table fixups for faults in the middle
- */
- .Ldone56: sub $8,%edx
- .Ldone48: sub $8,%edx
- .Ldone40: sub $8,%edx
- .Ldone32: sub $8,%edx
- .Ldone24: sub $8,%edx
- .Ldone16: sub $8,%edx
- .Ldone8: sub $8,%edx
- .Ldone0:
- mov %edx,%eax
- RET
- .Lfixup32:
- addq $32,%rsi
- addq $32,%rdi
- sub $32,%edx
- jmp .Lquadwords
- .Llast4:
- 52: movl (%rsi),%eax
- 53: movnti %eax,(%rdi)
- sfence
- sub $4,%edx
- mov %edx,%eax
- RET
- _ASM_EXTABLE_UA(52b, .Ldone0)
- _ASM_EXTABLE_UA(53b, .Ldone0)
- SYM_FUNC_END(__copy_user_nocache)
- EXPORT_SYMBOL(__copy_user_nocache)
|