18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * __get_user functions.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * (C) Copyright 1998 Linus Torvalds
68c2ecf20Sopenharmony_ci * (C) Copyright 2005 Andi Kleen
78c2ecf20Sopenharmony_ci * (C) Copyright 2008 Glauber Costa
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * These functions have a non-standard call interface
108c2ecf20Sopenharmony_ci * to make them more efficient, especially as they
118c2ecf20Sopenharmony_ci * return an error value in addition to the "real"
128c2ecf20Sopenharmony_ci * return value.
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci/*
168c2ecf20Sopenharmony_ci * __get_user_X
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * Inputs:	%[r|e]ax contains the address.
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * Outputs:	%[r|e]ax is error code (0 or -EFAULT)
218c2ecf20Sopenharmony_ci *		%[r|e]dx contains zero-extended value
228c2ecf20Sopenharmony_ci *		%ecx contains the high half for 32-bit __get_user_8
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci *
258c2ecf20Sopenharmony_ci * These functions should not modify any other registers,
268c2ecf20Sopenharmony_ci * as they get called from within inline assembly.
278c2ecf20Sopenharmony_ci */
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#include <linux/linkage.h>
308c2ecf20Sopenharmony_ci#include <asm/page_types.h>
318c2ecf20Sopenharmony_ci#include <asm/errno.h>
328c2ecf20Sopenharmony_ci#include <asm/asm-offsets.h>
338c2ecf20Sopenharmony_ci#include <asm/thread_info.h>
348c2ecf20Sopenharmony_ci#include <asm/asm.h>
358c2ecf20Sopenharmony_ci#include <asm/smap.h>
368c2ecf20Sopenharmony_ci#include <asm/export.h>
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define ASM_BARRIER_NOSPEC ALTERNATIVE "", "lfence", X86_FEATURE_LFENCE_RDTSC
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_5LEVEL
418c2ecf20Sopenharmony_ci#define LOAD_TASK_SIZE_MINUS_N(n) \
428c2ecf20Sopenharmony_ci	ALTERNATIVE __stringify(mov $((1 << 47) - 4096 - (n)),%rdx), \
438c2ecf20Sopenharmony_ci		    __stringify(mov $((1 << 56) - 4096 - (n)),%rdx), X86_FEATURE_LA57
448c2ecf20Sopenharmony_ci#else
458c2ecf20Sopenharmony_ci#define LOAD_TASK_SIZE_MINUS_N(n) \
468c2ecf20Sopenharmony_ci	mov $(TASK_SIZE_MAX - (n)),%_ASM_DX
478c2ecf20Sopenharmony_ci#endif
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	.text
508c2ecf20Sopenharmony_ciSYM_FUNC_START(__get_user_1)
518c2ecf20Sopenharmony_ci	LOAD_TASK_SIZE_MINUS_N(0)
528c2ecf20Sopenharmony_ci	cmp %_ASM_DX,%_ASM_AX
538c2ecf20Sopenharmony_ci	jae bad_get_user
548c2ecf20Sopenharmony_ci	sbb %_ASM_DX, %_ASM_DX		/* array_index_mask_nospec() */
558c2ecf20Sopenharmony_ci	and %_ASM_DX, %_ASM_AX
568c2ecf20Sopenharmony_ci	ASM_STAC
578c2ecf20Sopenharmony_ci1:	movzbl (%_ASM_AX),%edx
588c2ecf20Sopenharmony_ci	xor %eax,%eax
598c2ecf20Sopenharmony_ci	ASM_CLAC
608c2ecf20Sopenharmony_ci	RET
618c2ecf20Sopenharmony_ciSYM_FUNC_END(__get_user_1)
628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__get_user_1)
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ciSYM_FUNC_START(__get_user_2)
658c2ecf20Sopenharmony_ci	LOAD_TASK_SIZE_MINUS_N(1)
668c2ecf20Sopenharmony_ci	cmp %_ASM_DX,%_ASM_AX
678c2ecf20Sopenharmony_ci	jae bad_get_user
688c2ecf20Sopenharmony_ci	sbb %_ASM_DX, %_ASM_DX		/* array_index_mask_nospec() */
698c2ecf20Sopenharmony_ci	and %_ASM_DX, %_ASM_AX
708c2ecf20Sopenharmony_ci	ASM_STAC
718c2ecf20Sopenharmony_ci2:	movzwl (%_ASM_AX),%edx
728c2ecf20Sopenharmony_ci	xor %eax,%eax
738c2ecf20Sopenharmony_ci	ASM_CLAC
748c2ecf20Sopenharmony_ci	RET
758c2ecf20Sopenharmony_ciSYM_FUNC_END(__get_user_2)
768c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__get_user_2)
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ciSYM_FUNC_START(__get_user_4)
798c2ecf20Sopenharmony_ci	LOAD_TASK_SIZE_MINUS_N(3)
808c2ecf20Sopenharmony_ci	cmp %_ASM_DX,%_ASM_AX
818c2ecf20Sopenharmony_ci	jae bad_get_user
828c2ecf20Sopenharmony_ci	sbb %_ASM_DX, %_ASM_DX		/* array_index_mask_nospec() */
838c2ecf20Sopenharmony_ci	and %_ASM_DX, %_ASM_AX
848c2ecf20Sopenharmony_ci	ASM_STAC
858c2ecf20Sopenharmony_ci3:	movl (%_ASM_AX),%edx
868c2ecf20Sopenharmony_ci	xor %eax,%eax
878c2ecf20Sopenharmony_ci	ASM_CLAC
888c2ecf20Sopenharmony_ci	RET
898c2ecf20Sopenharmony_ciSYM_FUNC_END(__get_user_4)
908c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__get_user_4)
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ciSYM_FUNC_START(__get_user_8)
938c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_64
948c2ecf20Sopenharmony_ci	LOAD_TASK_SIZE_MINUS_N(7)
958c2ecf20Sopenharmony_ci	cmp %_ASM_DX,%_ASM_AX
968c2ecf20Sopenharmony_ci	jae bad_get_user
978c2ecf20Sopenharmony_ci	sbb %_ASM_DX, %_ASM_DX		/* array_index_mask_nospec() */
988c2ecf20Sopenharmony_ci	and %_ASM_DX, %_ASM_AX
998c2ecf20Sopenharmony_ci	ASM_STAC
1008c2ecf20Sopenharmony_ci4:	movq (%_ASM_AX),%rdx
1018c2ecf20Sopenharmony_ci	xor %eax,%eax
1028c2ecf20Sopenharmony_ci	ASM_CLAC
1038c2ecf20Sopenharmony_ci	RET
1048c2ecf20Sopenharmony_ci#else
1058c2ecf20Sopenharmony_ci	LOAD_TASK_SIZE_MINUS_N(7)
1068c2ecf20Sopenharmony_ci	cmp %_ASM_DX,%_ASM_AX
1078c2ecf20Sopenharmony_ci	jae bad_get_user_8
1088c2ecf20Sopenharmony_ci	sbb %_ASM_DX, %_ASM_DX		/* array_index_mask_nospec() */
1098c2ecf20Sopenharmony_ci	and %_ASM_DX, %_ASM_AX
1108c2ecf20Sopenharmony_ci	ASM_STAC
1118c2ecf20Sopenharmony_ci4:	movl (%_ASM_AX),%edx
1128c2ecf20Sopenharmony_ci5:	movl 4(%_ASM_AX),%ecx
1138c2ecf20Sopenharmony_ci	xor %eax,%eax
1148c2ecf20Sopenharmony_ci	ASM_CLAC
1158c2ecf20Sopenharmony_ci	RET
1168c2ecf20Sopenharmony_ci#endif
1178c2ecf20Sopenharmony_ciSYM_FUNC_END(__get_user_8)
1188c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__get_user_8)
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci/* .. and the same for __get_user, just without the range checks */
1218c2ecf20Sopenharmony_ciSYM_FUNC_START(__get_user_nocheck_1)
1228c2ecf20Sopenharmony_ci	ASM_STAC
1238c2ecf20Sopenharmony_ci	ASM_BARRIER_NOSPEC
1248c2ecf20Sopenharmony_ci6:	movzbl (%_ASM_AX),%edx
1258c2ecf20Sopenharmony_ci	xor %eax,%eax
1268c2ecf20Sopenharmony_ci	ASM_CLAC
1278c2ecf20Sopenharmony_ci	RET
1288c2ecf20Sopenharmony_ciSYM_FUNC_END(__get_user_nocheck_1)
1298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__get_user_nocheck_1)
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ciSYM_FUNC_START(__get_user_nocheck_2)
1328c2ecf20Sopenharmony_ci	ASM_STAC
1338c2ecf20Sopenharmony_ci	ASM_BARRIER_NOSPEC
1348c2ecf20Sopenharmony_ci7:	movzwl (%_ASM_AX),%edx
1358c2ecf20Sopenharmony_ci	xor %eax,%eax
1368c2ecf20Sopenharmony_ci	ASM_CLAC
1378c2ecf20Sopenharmony_ci	RET
1388c2ecf20Sopenharmony_ciSYM_FUNC_END(__get_user_nocheck_2)
1398c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__get_user_nocheck_2)
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ciSYM_FUNC_START(__get_user_nocheck_4)
1428c2ecf20Sopenharmony_ci	ASM_STAC
1438c2ecf20Sopenharmony_ci	ASM_BARRIER_NOSPEC
1448c2ecf20Sopenharmony_ci8:	movl (%_ASM_AX),%edx
1458c2ecf20Sopenharmony_ci	xor %eax,%eax
1468c2ecf20Sopenharmony_ci	ASM_CLAC
1478c2ecf20Sopenharmony_ci	RET
1488c2ecf20Sopenharmony_ciSYM_FUNC_END(__get_user_nocheck_4)
1498c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__get_user_nocheck_4)
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ciSYM_FUNC_START(__get_user_nocheck_8)
1528c2ecf20Sopenharmony_ci	ASM_STAC
1538c2ecf20Sopenharmony_ci	ASM_BARRIER_NOSPEC
1548c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_64
1558c2ecf20Sopenharmony_ci9:	movq (%_ASM_AX),%rdx
1568c2ecf20Sopenharmony_ci#else
1578c2ecf20Sopenharmony_ci9:	movl (%_ASM_AX),%edx
1588c2ecf20Sopenharmony_ci10:	movl 4(%_ASM_AX),%ecx
1598c2ecf20Sopenharmony_ci#endif
1608c2ecf20Sopenharmony_ci	xor %eax,%eax
1618c2ecf20Sopenharmony_ci	ASM_CLAC
1628c2ecf20Sopenharmony_ci	RET
1638c2ecf20Sopenharmony_ciSYM_FUNC_END(__get_user_nocheck_8)
1648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__get_user_nocheck_8)
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ciSYM_CODE_START_LOCAL(.Lbad_get_user_clac)
1688c2ecf20Sopenharmony_ci	ASM_CLAC
1698c2ecf20Sopenharmony_cibad_get_user:
1708c2ecf20Sopenharmony_ci	xor %edx,%edx
1718c2ecf20Sopenharmony_ci	mov $(-EFAULT),%_ASM_AX
1728c2ecf20Sopenharmony_ci	RET
1738c2ecf20Sopenharmony_ciSYM_CODE_END(.Lbad_get_user_clac)
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_32
1768c2ecf20Sopenharmony_ciSYM_CODE_START_LOCAL(.Lbad_get_user_8_clac)
1778c2ecf20Sopenharmony_ci	ASM_CLAC
1788c2ecf20Sopenharmony_cibad_get_user_8:
1798c2ecf20Sopenharmony_ci	xor %edx,%edx
1808c2ecf20Sopenharmony_ci	xor %ecx,%ecx
1818c2ecf20Sopenharmony_ci	mov $(-EFAULT),%_ASM_AX
1828c2ecf20Sopenharmony_ci	RET
1838c2ecf20Sopenharmony_ciSYM_CODE_END(.Lbad_get_user_8_clac)
1848c2ecf20Sopenharmony_ci#endif
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci/* get_user */
1878c2ecf20Sopenharmony_ci	_ASM_EXTABLE_UA(1b, .Lbad_get_user_clac)
1888c2ecf20Sopenharmony_ci	_ASM_EXTABLE_UA(2b, .Lbad_get_user_clac)
1898c2ecf20Sopenharmony_ci	_ASM_EXTABLE_UA(3b, .Lbad_get_user_clac)
1908c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_64
1918c2ecf20Sopenharmony_ci	_ASM_EXTABLE_UA(4b, .Lbad_get_user_clac)
1928c2ecf20Sopenharmony_ci#else
1938c2ecf20Sopenharmony_ci	_ASM_EXTABLE_UA(4b, .Lbad_get_user_8_clac)
1948c2ecf20Sopenharmony_ci	_ASM_EXTABLE_UA(5b, .Lbad_get_user_8_clac)
1958c2ecf20Sopenharmony_ci#endif
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci/* __get_user */
1988c2ecf20Sopenharmony_ci	_ASM_EXTABLE_UA(6b, .Lbad_get_user_clac)
1998c2ecf20Sopenharmony_ci	_ASM_EXTABLE_UA(7b, .Lbad_get_user_clac)
2008c2ecf20Sopenharmony_ci	_ASM_EXTABLE_UA(8b, .Lbad_get_user_clac)
2018c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_64
2028c2ecf20Sopenharmony_ci	_ASM_EXTABLE_UA(9b, .Lbad_get_user_clac)
2038c2ecf20Sopenharmony_ci#else
2048c2ecf20Sopenharmony_ci	_ASM_EXTABLE_UA(9b, .Lbad_get_user_8_clac)
2058c2ecf20Sopenharmony_ci	_ASM_EXTABLE_UA(10b, .Lbad_get_user_8_clac)
2068c2ecf20Sopenharmony_ci#endif
207