18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
28c2ecf20Sopenharmony_ci/* Copyright(c) 2016-2020 Intel Corporation. All rights reserved. */
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/linkage.h>
58c2ecf20Sopenharmony_ci#include <asm/copy_mc_test.h>
68c2ecf20Sopenharmony_ci#include <asm/export.h>
78c2ecf20Sopenharmony_ci#include <asm/asm.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#ifndef CONFIG_UML
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_MCE
128c2ecf20Sopenharmony_ciCOPY_MC_TEST_CTL
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci/*
158c2ecf20Sopenharmony_ci * copy_mc_fragile - copy memory with indication if an exception / fault happened
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * The 'fragile' version is opted into by platform quirks and takes
188c2ecf20Sopenharmony_ci * pains to avoid unrecoverable corner cases like 'fast-string'
198c2ecf20Sopenharmony_ci * instruction sequences, and consuming poison across a cacheline
208c2ecf20Sopenharmony_ci * boundary. The non-fragile version is equivalent to memcpy()
218c2ecf20Sopenharmony_ci * regardless of CPU machine-check-recovery capability.
228c2ecf20Sopenharmony_ci */
238c2ecf20Sopenharmony_ciSYM_FUNC_START(copy_mc_fragile)
248c2ecf20Sopenharmony_ci	cmpl $8, %edx
258c2ecf20Sopenharmony_ci	/* Less than 8 bytes? Go to byte copy loop */
268c2ecf20Sopenharmony_ci	jb .L_no_whole_words
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	/* Check for bad alignment of source */
298c2ecf20Sopenharmony_ci	testl $7, %esi
308c2ecf20Sopenharmony_ci	/* Already aligned */
318c2ecf20Sopenharmony_ci	jz .L_8byte_aligned
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	/* Copy one byte at a time until source is 8-byte aligned */
348c2ecf20Sopenharmony_ci	movl %esi, %ecx
358c2ecf20Sopenharmony_ci	andl $7, %ecx
368c2ecf20Sopenharmony_ci	subl $8, %ecx
378c2ecf20Sopenharmony_ci	negl %ecx
388c2ecf20Sopenharmony_ci	subl %ecx, %edx
398c2ecf20Sopenharmony_ci.L_read_leading_bytes:
408c2ecf20Sopenharmony_ci	movb (%rsi), %al
418c2ecf20Sopenharmony_ci	COPY_MC_TEST_SRC %rsi 1 .E_leading_bytes
428c2ecf20Sopenharmony_ci	COPY_MC_TEST_DST %rdi 1 .E_leading_bytes
438c2ecf20Sopenharmony_ci.L_write_leading_bytes:
448c2ecf20Sopenharmony_ci	movb %al, (%rdi)
458c2ecf20Sopenharmony_ci	incq %rsi
468c2ecf20Sopenharmony_ci	incq %rdi
478c2ecf20Sopenharmony_ci	decl %ecx
488c2ecf20Sopenharmony_ci	jnz .L_read_leading_bytes
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci.L_8byte_aligned:
518c2ecf20Sopenharmony_ci	movl %edx, %ecx
528c2ecf20Sopenharmony_ci	andl $7, %edx
538c2ecf20Sopenharmony_ci	shrl $3, %ecx
548c2ecf20Sopenharmony_ci	jz .L_no_whole_words
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci.L_read_words:
578c2ecf20Sopenharmony_ci	movq (%rsi), %r8
588c2ecf20Sopenharmony_ci	COPY_MC_TEST_SRC %rsi 8 .E_read_words
598c2ecf20Sopenharmony_ci	COPY_MC_TEST_DST %rdi 8 .E_write_words
608c2ecf20Sopenharmony_ci.L_write_words:
618c2ecf20Sopenharmony_ci	movq %r8, (%rdi)
628c2ecf20Sopenharmony_ci	addq $8, %rsi
638c2ecf20Sopenharmony_ci	addq $8, %rdi
648c2ecf20Sopenharmony_ci	decl %ecx
658c2ecf20Sopenharmony_ci	jnz .L_read_words
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	/* Any trailing bytes? */
688c2ecf20Sopenharmony_ci.L_no_whole_words:
698c2ecf20Sopenharmony_ci	andl %edx, %edx
708c2ecf20Sopenharmony_ci	jz .L_done_memcpy_trap
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	/* Copy trailing bytes */
738c2ecf20Sopenharmony_ci	movl %edx, %ecx
748c2ecf20Sopenharmony_ci.L_read_trailing_bytes:
758c2ecf20Sopenharmony_ci	movb (%rsi), %al
768c2ecf20Sopenharmony_ci	COPY_MC_TEST_SRC %rsi 1 .E_trailing_bytes
778c2ecf20Sopenharmony_ci	COPY_MC_TEST_DST %rdi 1 .E_trailing_bytes
788c2ecf20Sopenharmony_ci.L_write_trailing_bytes:
798c2ecf20Sopenharmony_ci	movb %al, (%rdi)
808c2ecf20Sopenharmony_ci	incq %rsi
818c2ecf20Sopenharmony_ci	incq %rdi
828c2ecf20Sopenharmony_ci	decl %ecx
838c2ecf20Sopenharmony_ci	jnz .L_read_trailing_bytes
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	/* Copy successful. Return zero */
868c2ecf20Sopenharmony_ci.L_done_memcpy_trap:
878c2ecf20Sopenharmony_ci	xorl %eax, %eax
888c2ecf20Sopenharmony_ci.L_done:
898c2ecf20Sopenharmony_ci	RET
908c2ecf20Sopenharmony_ciSYM_FUNC_END(copy_mc_fragile)
918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(copy_mc_fragile)
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	.section .fixup, "ax"
948c2ecf20Sopenharmony_ci	/*
958c2ecf20Sopenharmony_ci	 * Return number of bytes not copied for any failure. Note that
968c2ecf20Sopenharmony_ci	 * there is no "tail" handling since the source buffer is 8-byte
978c2ecf20Sopenharmony_ci	 * aligned and poison is cacheline aligned.
988c2ecf20Sopenharmony_ci	 */
998c2ecf20Sopenharmony_ci.E_read_words:
1008c2ecf20Sopenharmony_ci	shll	$3, %ecx
1018c2ecf20Sopenharmony_ci.E_leading_bytes:
1028c2ecf20Sopenharmony_ci	addl	%edx, %ecx
1038c2ecf20Sopenharmony_ci.E_trailing_bytes:
1048c2ecf20Sopenharmony_ci	mov	%ecx, %eax
1058c2ecf20Sopenharmony_ci	jmp	.L_done
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	/*
1088c2ecf20Sopenharmony_ci	 * For write fault handling, given the destination is unaligned,
1098c2ecf20Sopenharmony_ci	 * we handle faults on multi-byte writes with a byte-by-byte
1108c2ecf20Sopenharmony_ci	 * copy up to the write-protected page.
1118c2ecf20Sopenharmony_ci	 */
1128c2ecf20Sopenharmony_ci.E_write_words:
1138c2ecf20Sopenharmony_ci	shll	$3, %ecx
1148c2ecf20Sopenharmony_ci	addl	%edx, %ecx
1158c2ecf20Sopenharmony_ci	movl	%ecx, %edx
1168c2ecf20Sopenharmony_ci	jmp copy_mc_fragile_handle_tail
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	.previous
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	_ASM_EXTABLE_FAULT(.L_read_leading_bytes, .E_leading_bytes)
1218c2ecf20Sopenharmony_ci	_ASM_EXTABLE_FAULT(.L_read_words, .E_read_words)
1228c2ecf20Sopenharmony_ci	_ASM_EXTABLE_FAULT(.L_read_trailing_bytes, .E_trailing_bytes)
1238c2ecf20Sopenharmony_ci	_ASM_EXTABLE(.L_write_leading_bytes, .E_leading_bytes)
1248c2ecf20Sopenharmony_ci	_ASM_EXTABLE(.L_write_words, .E_write_words)
1258c2ecf20Sopenharmony_ci	_ASM_EXTABLE(.L_write_trailing_bytes, .E_trailing_bytes)
1268c2ecf20Sopenharmony_ci#endif /* CONFIG_X86_MCE */
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci/*
1298c2ecf20Sopenharmony_ci * copy_mc_enhanced_fast_string - memory copy with exception handling
1308c2ecf20Sopenharmony_ci *
1318c2ecf20Sopenharmony_ci * Fast string copy + fault / exception handling. If the CPU does
1328c2ecf20Sopenharmony_ci * support machine check exception recovery, but does not support
1338c2ecf20Sopenharmony_ci * recovering from fast-string exceptions then this CPU needs to be
1348c2ecf20Sopenharmony_ci * added to the copy_mc_fragile_key set of quirks. Otherwise, absent any
1358c2ecf20Sopenharmony_ci * machine check recovery support this version should be no slower than
1368c2ecf20Sopenharmony_ci * standard memcpy.
1378c2ecf20Sopenharmony_ci */
1388c2ecf20Sopenharmony_ciSYM_FUNC_START(copy_mc_enhanced_fast_string)
1398c2ecf20Sopenharmony_ci	movq %rdi, %rax
1408c2ecf20Sopenharmony_ci	movq %rdx, %rcx
1418c2ecf20Sopenharmony_ci.L_copy:
1428c2ecf20Sopenharmony_ci	rep movsb
1438c2ecf20Sopenharmony_ci	/* Copy successful. Return zero */
1448c2ecf20Sopenharmony_ci	xorl %eax, %eax
1458c2ecf20Sopenharmony_ci	RET
1468c2ecf20Sopenharmony_ciSYM_FUNC_END(copy_mc_enhanced_fast_string)
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	.section .fixup, "ax"
1498c2ecf20Sopenharmony_ci.E_copy:
1508c2ecf20Sopenharmony_ci	/*
1518c2ecf20Sopenharmony_ci	 * On fault %rcx is updated such that the copy instruction could
1528c2ecf20Sopenharmony_ci	 * optionally be restarted at the fault position, i.e. it
1538c2ecf20Sopenharmony_ci	 * contains 'bytes remaining'. A non-zero return indicates error
1548c2ecf20Sopenharmony_ci	 * to copy_mc_generic() users, or indicate short transfers to
1558c2ecf20Sopenharmony_ci	 * user-copy routines.
1568c2ecf20Sopenharmony_ci	 */
1578c2ecf20Sopenharmony_ci	movq %rcx, %rax
1588c2ecf20Sopenharmony_ci	RET
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	.previous
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	_ASM_EXTABLE_FAULT(.L_copy, .E_copy)
1638c2ecf20Sopenharmony_ci#endif /* !CONFIG_UML */
164