| /* SPDX-License-Identifier: GPL-2.0-only */ | |
| /* Copyright(c) 2016-2020 Intel Corporation. All rights reserved. */ | |
| #include <linux/linkage.h> | |
| #include <asm/asm.h> | |
| #ifndef CONFIG_UML | |
| #ifdef CONFIG_X86_MCE | |
| /* | |
| * copy_mc_fragile - copy memory with indication if an exception / fault happened | |
| * | |
| * The 'fragile' version is opted into by platform quirks and takes | |
| * pains to avoid unrecoverable corner cases like 'fast-string' | |
| * instruction sequences, and consuming poison across a cacheline | |
| * boundary. The non-fragile version is equivalent to memcpy() | |
| * regardless of CPU machine-check-recovery capability. | |
| */ | |
| SYM_FUNC_START(copy_mc_fragile) | |
| cmpl $8, %edx | |
| /* Less than 8 bytes? Go to byte copy loop */ | |
| jb .L_no_whole_words | |
| /* Check for bad alignment of source */ | |
| testl $7, %esi | |
| /* Already aligned */ | |
| jz .L_8byte_aligned | |
| /* Copy one byte at a time until source is 8-byte aligned */ | |
| movl %esi, %ecx | |
| andl $7, %ecx | |
| subl $8, %ecx | |
| negl %ecx | |
| subl %ecx, %edx | |
| .L_read_leading_bytes: | |
| movb (%rsi), %al | |
| .L_write_leading_bytes: | |
| movb %al, (%rdi) | |
| incq %rsi | |
| incq %rdi | |
| decl %ecx | |
| jnz .L_read_leading_bytes | |
| .L_8byte_aligned: | |
| movl %edx, %ecx | |
| andl $7, %edx | |
| shrl $3, %ecx | |
| jz .L_no_whole_words | |
| .L_read_words: | |
| movq (%rsi), %r8 | |
| .L_write_words: | |
| movq %r8, (%rdi) | |
| addq $8, %rsi | |
| addq $8, %rdi | |
| decl %ecx | |
| jnz .L_read_words | |
| /* Any trailing bytes? */ | |
| .L_no_whole_words: | |
| andl %edx, %edx | |
| jz .L_done_memcpy_trap | |
| /* Copy trailing bytes */ | |
| movl %edx, %ecx | |
| .L_read_trailing_bytes: | |
| movb (%rsi), %al | |
| .L_write_trailing_bytes: | |
| movb %al, (%rdi) | |
| incq %rsi | |
| incq %rdi | |
| decl %ecx | |
| jnz .L_read_trailing_bytes | |
| /* Copy successful. Return zero */ | |
| .L_done_memcpy_trap: | |
| xorl %eax, %eax | |
| .L_done: | |
| RET | |
| /* | |
| * Return number of bytes not copied for any failure. Note that | |
| * there is no "tail" handling since the source buffer is 8-byte | |
| * aligned and poison is cacheline aligned. | |
| */ | |
| .E_read_words: | |
| shll $3, %ecx | |
| .E_leading_bytes: | |
| addl %edx, %ecx | |
| .E_trailing_bytes: | |
| mov %ecx, %eax | |
| jmp .L_done | |
| /* | |
| * For write fault handling, given the destination is unaligned, | |
| * we handle faults on multi-byte writes with a byte-by-byte | |
| * copy up to the write-protected page. | |
| */ | |
| .E_write_words: | |
| shll $3, %ecx | |
| addl %edx, %ecx | |
| movl %ecx, %edx | |
| jmp copy_mc_fragile_handle_tail | |
| _ASM_EXTABLE_TYPE(.L_read_leading_bytes, .E_leading_bytes, EX_TYPE_DEFAULT_MCE_SAFE) | |
| _ASM_EXTABLE_TYPE(.L_read_words, .E_read_words, EX_TYPE_DEFAULT_MCE_SAFE) | |
| _ASM_EXTABLE_TYPE(.L_read_trailing_bytes, .E_trailing_bytes, EX_TYPE_DEFAULT_MCE_SAFE) | |
| _ASM_EXTABLE(.L_write_leading_bytes, .E_leading_bytes) | |
| _ASM_EXTABLE(.L_write_words, .E_write_words) | |
| _ASM_EXTABLE(.L_write_trailing_bytes, .E_trailing_bytes) | |
| SYM_FUNC_END(copy_mc_fragile) | |
| #endif /* CONFIG_X86_MCE */ | |
| /* | |
| * copy_mc_enhanced_fast_string - memory copy with exception handling | |
| * | |
| * Fast string copy + fault / exception handling. If the CPU does | |
| * support machine check exception recovery, but does not support | |
| * recovering from fast-string exceptions then this CPU needs to be | |
| * added to the copy_mc_fragile_key set of quirks. Otherwise, absent any | |
| * machine check recovery support this version should be no slower than | |
| * standard memcpy. | |
| */ | |
| SYM_FUNC_START(copy_mc_enhanced_fast_string) | |
| movq %rdi, %rax | |
| movq %rdx, %rcx | |
| .L_copy: | |
| rep movsb | |
| /* Copy successful. Return zero */ | |
| xorl %eax, %eax | |
| RET | |
| .E_copy: | |
| /* | |
| * On fault %rcx is updated such that the copy instruction could | |
| * optionally be restarted at the fault position, i.e. it | |
| * contains 'bytes remaining'. A non-zero return indicates error | |
| * to copy_mc_generic() users, or indicate short transfers to | |
| * user-copy routines. | |
| */ | |
| movq %rcx, %rax | |
| RET | |
| _ASM_EXTABLE_TYPE(.L_copy, .E_copy, EX_TYPE_DEFAULT_MCE_SAFE) | |
| SYM_FUNC_END(copy_mc_enhanced_fast_string) | |
| #endif /* !CONFIG_UML */ |