18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Standard user space access functions based on mvcp/mvcs and doing
48c2ecf20Sopenharmony_ci *  interesting things in the secondary space mode.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *    Copyright IBM Corp. 2006,2014
78c2ecf20Sopenharmony_ci *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
88c2ecf20Sopenharmony_ci *		 Gerald Schaefer (gerald.schaefer@de.ibm.com)
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/jump_label.h>
128c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
138c2ecf20Sopenharmony_ci#include <linux/export.h>
148c2ecf20Sopenharmony_ci#include <linux/errno.h>
158c2ecf20Sopenharmony_ci#include <linux/mm.h>
168c2ecf20Sopenharmony_ci#include <asm/mmu_context.h>
178c2ecf20Sopenharmony_ci#include <asm/facility.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#ifndef CONFIG_HAVE_MARCH_Z10_FEATURES
208c2ecf20Sopenharmony_cistatic DEFINE_STATIC_KEY_FALSE(have_mvcos);
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic int __init uaccess_init(void)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	if (test_facility(27))
258c2ecf20Sopenharmony_ci		static_branch_enable(&have_mvcos);
268c2ecf20Sopenharmony_ci	return 0;
278c2ecf20Sopenharmony_ci}
288c2ecf20Sopenharmony_ciearly_initcall(uaccess_init);
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic inline int copy_with_mvcos(void)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	if (static_branch_likely(&have_mvcos))
338c2ecf20Sopenharmony_ci		return 1;
348c2ecf20Sopenharmony_ci	return 0;
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci#else
378c2ecf20Sopenharmony_cistatic inline int copy_with_mvcos(void)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	return 1;
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci#endif
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_civoid set_fs(mm_segment_t fs)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	current->thread.mm_segment = fs;
468c2ecf20Sopenharmony_ci	if (fs == USER_DS) {
478c2ecf20Sopenharmony_ci		__ctl_load(S390_lowcore.user_asce, 1, 1);
488c2ecf20Sopenharmony_ci		clear_cpu_flag(CIF_ASCE_PRIMARY);
498c2ecf20Sopenharmony_ci	} else {
508c2ecf20Sopenharmony_ci		__ctl_load(S390_lowcore.kernel_asce, 1, 1);
518c2ecf20Sopenharmony_ci		set_cpu_flag(CIF_ASCE_PRIMARY);
528c2ecf20Sopenharmony_ci	}
538c2ecf20Sopenharmony_ci	if (fs & 1) {
548c2ecf20Sopenharmony_ci		if (fs == USER_DS_SACF)
558c2ecf20Sopenharmony_ci			__ctl_load(S390_lowcore.user_asce, 7, 7);
568c2ecf20Sopenharmony_ci		else
578c2ecf20Sopenharmony_ci			__ctl_load(S390_lowcore.kernel_asce, 7, 7);
588c2ecf20Sopenharmony_ci		set_cpu_flag(CIF_ASCE_SECONDARY);
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(set_fs);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cimm_segment_t enable_sacf_uaccess(void)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	mm_segment_t old_fs;
668c2ecf20Sopenharmony_ci	unsigned long asce, cr;
678c2ecf20Sopenharmony_ci	unsigned long flags;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	old_fs = current->thread.mm_segment;
708c2ecf20Sopenharmony_ci	if (old_fs & 1)
718c2ecf20Sopenharmony_ci		return old_fs;
728c2ecf20Sopenharmony_ci	/* protect against a concurrent page table upgrade */
738c2ecf20Sopenharmony_ci	local_irq_save(flags);
748c2ecf20Sopenharmony_ci	current->thread.mm_segment |= 1;
758c2ecf20Sopenharmony_ci	asce = S390_lowcore.kernel_asce;
768c2ecf20Sopenharmony_ci	if (likely(old_fs == USER_DS)) {
778c2ecf20Sopenharmony_ci		__ctl_store(cr, 1, 1);
788c2ecf20Sopenharmony_ci		if (cr != S390_lowcore.kernel_asce) {
798c2ecf20Sopenharmony_ci			__ctl_load(S390_lowcore.kernel_asce, 1, 1);
808c2ecf20Sopenharmony_ci			set_cpu_flag(CIF_ASCE_PRIMARY);
818c2ecf20Sopenharmony_ci		}
828c2ecf20Sopenharmony_ci		asce = S390_lowcore.user_asce;
838c2ecf20Sopenharmony_ci	}
848c2ecf20Sopenharmony_ci	__ctl_store(cr, 7, 7);
858c2ecf20Sopenharmony_ci	if (cr != asce) {
868c2ecf20Sopenharmony_ci		__ctl_load(asce, 7, 7);
878c2ecf20Sopenharmony_ci		set_cpu_flag(CIF_ASCE_SECONDARY);
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci	local_irq_restore(flags);
908c2ecf20Sopenharmony_ci	return old_fs;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(enable_sacf_uaccess);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_civoid disable_sacf_uaccess(mm_segment_t old_fs)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	current->thread.mm_segment = old_fs;
978c2ecf20Sopenharmony_ci	if (old_fs == USER_DS && test_facility(27)) {
988c2ecf20Sopenharmony_ci		__ctl_load(S390_lowcore.user_asce, 1, 1);
998c2ecf20Sopenharmony_ci		clear_cpu_flag(CIF_ASCE_PRIMARY);
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ciEXPORT_SYMBOL(disable_sacf_uaccess);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic inline unsigned long copy_from_user_mvcos(void *x, const void __user *ptr,
1058c2ecf20Sopenharmony_ci						 unsigned long size)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	register unsigned long reg0 asm("0") = 0x01UL;
1088c2ecf20Sopenharmony_ci	unsigned long tmp1, tmp2;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	tmp1 = -4096UL;
1118c2ecf20Sopenharmony_ci	asm volatile(
1128c2ecf20Sopenharmony_ci		"0: .insn ss,0xc80000000000,0(%0,%2),0(%1),0\n"
1138c2ecf20Sopenharmony_ci		"6: jz    4f\n"
1148c2ecf20Sopenharmony_ci		"1: algr  %0,%3\n"
1158c2ecf20Sopenharmony_ci		"   slgr  %1,%3\n"
1168c2ecf20Sopenharmony_ci		"   slgr  %2,%3\n"
1178c2ecf20Sopenharmony_ci		"   j     0b\n"
1188c2ecf20Sopenharmony_ci		"2: la    %4,4095(%1)\n"/* %4 = ptr + 4095 */
1198c2ecf20Sopenharmony_ci		"   nr    %4,%3\n"	/* %4 = (ptr + 4095) & -4096 */
1208c2ecf20Sopenharmony_ci		"   slgr  %4,%1\n"
1218c2ecf20Sopenharmony_ci		"   clgr  %0,%4\n"	/* copy crosses next page boundary? */
1228c2ecf20Sopenharmony_ci		"   jnh   5f\n"
1238c2ecf20Sopenharmony_ci		"3: .insn ss,0xc80000000000,0(%4,%2),0(%1),0\n"
1248c2ecf20Sopenharmony_ci		"7: slgr  %0,%4\n"
1258c2ecf20Sopenharmony_ci		"   j     5f\n"
1268c2ecf20Sopenharmony_ci		"4: slgr  %0,%0\n"
1278c2ecf20Sopenharmony_ci		"5:\n"
1288c2ecf20Sopenharmony_ci		EX_TABLE(0b,2b) EX_TABLE(3b,5b) EX_TABLE(6b,2b) EX_TABLE(7b,5b)
1298c2ecf20Sopenharmony_ci		: "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)
1308c2ecf20Sopenharmony_ci		: "d" (reg0) : "cc", "memory");
1318c2ecf20Sopenharmony_ci	return size;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic inline unsigned long copy_from_user_mvcp(void *x, const void __user *ptr,
1358c2ecf20Sopenharmony_ci						unsigned long size)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	unsigned long tmp1, tmp2;
1388c2ecf20Sopenharmony_ci	mm_segment_t old_fs;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	old_fs = enable_sacf_uaccess();
1418c2ecf20Sopenharmony_ci	tmp1 = -256UL;
1428c2ecf20Sopenharmony_ci	asm volatile(
1438c2ecf20Sopenharmony_ci		"   sacf  0\n"
1448c2ecf20Sopenharmony_ci		"0: mvcp  0(%0,%2),0(%1),%3\n"
1458c2ecf20Sopenharmony_ci		"7: jz    5f\n"
1468c2ecf20Sopenharmony_ci		"1: algr  %0,%3\n"
1478c2ecf20Sopenharmony_ci		"   la    %1,256(%1)\n"
1488c2ecf20Sopenharmony_ci		"   la    %2,256(%2)\n"
1498c2ecf20Sopenharmony_ci		"2: mvcp  0(%0,%2),0(%1),%3\n"
1508c2ecf20Sopenharmony_ci		"8: jnz   1b\n"
1518c2ecf20Sopenharmony_ci		"   j     5f\n"
1528c2ecf20Sopenharmony_ci		"3: la    %4,255(%1)\n"	/* %4 = ptr + 255 */
1538c2ecf20Sopenharmony_ci		"   lghi  %3,-4096\n"
1548c2ecf20Sopenharmony_ci		"   nr    %4,%3\n"	/* %4 = (ptr + 255) & -4096 */
1558c2ecf20Sopenharmony_ci		"   slgr  %4,%1\n"
1568c2ecf20Sopenharmony_ci		"   clgr  %0,%4\n"	/* copy crosses next page boundary? */
1578c2ecf20Sopenharmony_ci		"   jnh   6f\n"
1588c2ecf20Sopenharmony_ci		"4: mvcp  0(%4,%2),0(%1),%3\n"
1598c2ecf20Sopenharmony_ci		"9: slgr  %0,%4\n"
1608c2ecf20Sopenharmony_ci		"   j     6f\n"
1618c2ecf20Sopenharmony_ci		"5: slgr  %0,%0\n"
1628c2ecf20Sopenharmony_ci		"6: sacf  768\n"
1638c2ecf20Sopenharmony_ci		EX_TABLE(0b,3b) EX_TABLE(2b,3b) EX_TABLE(4b,6b)
1648c2ecf20Sopenharmony_ci		EX_TABLE(7b,3b) EX_TABLE(8b,3b) EX_TABLE(9b,6b)
1658c2ecf20Sopenharmony_ci		: "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)
1668c2ecf20Sopenharmony_ci		: : "cc", "memory");
1678c2ecf20Sopenharmony_ci	disable_sacf_uaccess(old_fs);
1688c2ecf20Sopenharmony_ci	return size;
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ciunsigned long raw_copy_from_user(void *to, const void __user *from, unsigned long n)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	if (copy_with_mvcos())
1748c2ecf20Sopenharmony_ci		return copy_from_user_mvcos(to, from, n);
1758c2ecf20Sopenharmony_ci	return copy_from_user_mvcp(to, from, n);
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(raw_copy_from_user);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic inline unsigned long copy_to_user_mvcos(void __user *ptr, const void *x,
1808c2ecf20Sopenharmony_ci					       unsigned long size)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	register unsigned long reg0 asm("0") = 0x010000UL;
1838c2ecf20Sopenharmony_ci	unsigned long tmp1, tmp2;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	tmp1 = -4096UL;
1868c2ecf20Sopenharmony_ci	asm volatile(
1878c2ecf20Sopenharmony_ci		"0: .insn ss,0xc80000000000,0(%0,%1),0(%2),0\n"
1888c2ecf20Sopenharmony_ci		"6: jz    4f\n"
1898c2ecf20Sopenharmony_ci		"1: algr  %0,%3\n"
1908c2ecf20Sopenharmony_ci		"   slgr  %1,%3\n"
1918c2ecf20Sopenharmony_ci		"   slgr  %2,%3\n"
1928c2ecf20Sopenharmony_ci		"   j     0b\n"
1938c2ecf20Sopenharmony_ci		"2: la    %4,4095(%1)\n"/* %4 = ptr + 4095 */
1948c2ecf20Sopenharmony_ci		"   nr    %4,%3\n"	/* %4 = (ptr + 4095) & -4096 */
1958c2ecf20Sopenharmony_ci		"   slgr  %4,%1\n"
1968c2ecf20Sopenharmony_ci		"   clgr  %0,%4\n"	/* copy crosses next page boundary? */
1978c2ecf20Sopenharmony_ci		"   jnh   5f\n"
1988c2ecf20Sopenharmony_ci		"3: .insn ss,0xc80000000000,0(%4,%1),0(%2),0\n"
1998c2ecf20Sopenharmony_ci		"7: slgr  %0,%4\n"
2008c2ecf20Sopenharmony_ci		"   j     5f\n"
2018c2ecf20Sopenharmony_ci		"4: slgr  %0,%0\n"
2028c2ecf20Sopenharmony_ci		"5:\n"
2038c2ecf20Sopenharmony_ci		EX_TABLE(0b,2b) EX_TABLE(3b,5b) EX_TABLE(6b,2b) EX_TABLE(7b,5b)
2048c2ecf20Sopenharmony_ci		: "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)
2058c2ecf20Sopenharmony_ci		: "d" (reg0) : "cc", "memory");
2068c2ecf20Sopenharmony_ci	return size;
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic inline unsigned long copy_to_user_mvcs(void __user *ptr, const void *x,
2108c2ecf20Sopenharmony_ci					      unsigned long size)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	unsigned long tmp1, tmp2;
2138c2ecf20Sopenharmony_ci	mm_segment_t old_fs;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	old_fs = enable_sacf_uaccess();
2168c2ecf20Sopenharmony_ci	tmp1 = -256UL;
2178c2ecf20Sopenharmony_ci	asm volatile(
2188c2ecf20Sopenharmony_ci		"   sacf  0\n"
2198c2ecf20Sopenharmony_ci		"0: mvcs  0(%0,%1),0(%2),%3\n"
2208c2ecf20Sopenharmony_ci		"7: jz    5f\n"
2218c2ecf20Sopenharmony_ci		"1: algr  %0,%3\n"
2228c2ecf20Sopenharmony_ci		"   la    %1,256(%1)\n"
2238c2ecf20Sopenharmony_ci		"   la    %2,256(%2)\n"
2248c2ecf20Sopenharmony_ci		"2: mvcs  0(%0,%1),0(%2),%3\n"
2258c2ecf20Sopenharmony_ci		"8: jnz   1b\n"
2268c2ecf20Sopenharmony_ci		"   j     5f\n"
2278c2ecf20Sopenharmony_ci		"3: la    %4,255(%1)\n" /* %4 = ptr + 255 */
2288c2ecf20Sopenharmony_ci		"   lghi  %3,-4096\n"
2298c2ecf20Sopenharmony_ci		"   nr    %4,%3\n"	/* %4 = (ptr + 255) & -4096 */
2308c2ecf20Sopenharmony_ci		"   slgr  %4,%1\n"
2318c2ecf20Sopenharmony_ci		"   clgr  %0,%4\n"	/* copy crosses next page boundary? */
2328c2ecf20Sopenharmony_ci		"   jnh   6f\n"
2338c2ecf20Sopenharmony_ci		"4: mvcs  0(%4,%1),0(%2),%3\n"
2348c2ecf20Sopenharmony_ci		"9: slgr  %0,%4\n"
2358c2ecf20Sopenharmony_ci		"   j     6f\n"
2368c2ecf20Sopenharmony_ci		"5: slgr  %0,%0\n"
2378c2ecf20Sopenharmony_ci		"6: sacf  768\n"
2388c2ecf20Sopenharmony_ci		EX_TABLE(0b,3b) EX_TABLE(2b,3b) EX_TABLE(4b,6b)
2398c2ecf20Sopenharmony_ci		EX_TABLE(7b,3b) EX_TABLE(8b,3b) EX_TABLE(9b,6b)
2408c2ecf20Sopenharmony_ci		: "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)
2418c2ecf20Sopenharmony_ci		: : "cc", "memory");
2428c2ecf20Sopenharmony_ci	disable_sacf_uaccess(old_fs);
2438c2ecf20Sopenharmony_ci	return size;
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ciunsigned long raw_copy_to_user(void __user *to, const void *from, unsigned long n)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	if (copy_with_mvcos())
2498c2ecf20Sopenharmony_ci		return copy_to_user_mvcos(to, from, n);
2508c2ecf20Sopenharmony_ci	return copy_to_user_mvcs(to, from, n);
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(raw_copy_to_user);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic inline unsigned long copy_in_user_mvcos(void __user *to, const void __user *from,
2558c2ecf20Sopenharmony_ci					       unsigned long size)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	register unsigned long reg0 asm("0") = 0x010001UL;
2588c2ecf20Sopenharmony_ci	unsigned long tmp1, tmp2;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	tmp1 = -4096UL;
2618c2ecf20Sopenharmony_ci	/* FIXME: copy with reduced length. */
2628c2ecf20Sopenharmony_ci	asm volatile(
2638c2ecf20Sopenharmony_ci		"0: .insn ss,0xc80000000000,0(%0,%1),0(%2),0\n"
2648c2ecf20Sopenharmony_ci		"   jz	  2f\n"
2658c2ecf20Sopenharmony_ci		"1: algr  %0,%3\n"
2668c2ecf20Sopenharmony_ci		"   slgr  %1,%3\n"
2678c2ecf20Sopenharmony_ci		"   slgr  %2,%3\n"
2688c2ecf20Sopenharmony_ci		"   j	  0b\n"
2698c2ecf20Sopenharmony_ci		"2:slgr  %0,%0\n"
2708c2ecf20Sopenharmony_ci		"3: \n"
2718c2ecf20Sopenharmony_ci		EX_TABLE(0b,3b)
2728c2ecf20Sopenharmony_ci		: "+a" (size), "+a" (to), "+a" (from), "+a" (tmp1), "=a" (tmp2)
2738c2ecf20Sopenharmony_ci		: "d" (reg0) : "cc", "memory");
2748c2ecf20Sopenharmony_ci	return size;
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic inline unsigned long copy_in_user_mvc(void __user *to, const void __user *from,
2788c2ecf20Sopenharmony_ci					     unsigned long size)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	mm_segment_t old_fs;
2818c2ecf20Sopenharmony_ci	unsigned long tmp1;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	old_fs = enable_sacf_uaccess();
2848c2ecf20Sopenharmony_ci	asm volatile(
2858c2ecf20Sopenharmony_ci		"   sacf  256\n"
2868c2ecf20Sopenharmony_ci		"   aghi  %0,-1\n"
2878c2ecf20Sopenharmony_ci		"   jo	  5f\n"
2888c2ecf20Sopenharmony_ci		"   bras  %3,3f\n"
2898c2ecf20Sopenharmony_ci		"0: aghi  %0,257\n"
2908c2ecf20Sopenharmony_ci		"1: mvc	  0(1,%1),0(%2)\n"
2918c2ecf20Sopenharmony_ci		"   la	  %1,1(%1)\n"
2928c2ecf20Sopenharmony_ci		"   la	  %2,1(%2)\n"
2938c2ecf20Sopenharmony_ci		"   aghi  %0,-1\n"
2948c2ecf20Sopenharmony_ci		"   jnz	  1b\n"
2958c2ecf20Sopenharmony_ci		"   j	  5f\n"
2968c2ecf20Sopenharmony_ci		"2: mvc	  0(256,%1),0(%2)\n"
2978c2ecf20Sopenharmony_ci		"   la	  %1,256(%1)\n"
2988c2ecf20Sopenharmony_ci		"   la	  %2,256(%2)\n"
2998c2ecf20Sopenharmony_ci		"3: aghi  %0,-256\n"
3008c2ecf20Sopenharmony_ci		"   jnm	  2b\n"
3018c2ecf20Sopenharmony_ci		"4: ex	  %0,1b-0b(%3)\n"
3028c2ecf20Sopenharmony_ci		"5: slgr  %0,%0\n"
3038c2ecf20Sopenharmony_ci		"6: sacf  768\n"
3048c2ecf20Sopenharmony_ci		EX_TABLE(1b,6b) EX_TABLE(2b,0b) EX_TABLE(4b,0b)
3058c2ecf20Sopenharmony_ci		: "+a" (size), "+a" (to), "+a" (from), "=a" (tmp1)
3068c2ecf20Sopenharmony_ci		: : "cc", "memory");
3078c2ecf20Sopenharmony_ci	disable_sacf_uaccess(old_fs);
3088c2ecf20Sopenharmony_ci	return size;
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ciunsigned long raw_copy_in_user(void __user *to, const void __user *from, unsigned long n)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	if (copy_with_mvcos())
3148c2ecf20Sopenharmony_ci		return copy_in_user_mvcos(to, from, n);
3158c2ecf20Sopenharmony_ci	return copy_in_user_mvc(to, from, n);
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(raw_copy_in_user);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_cistatic inline unsigned long clear_user_mvcos(void __user *to, unsigned long size)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	register unsigned long reg0 asm("0") = 0x010000UL;
3228c2ecf20Sopenharmony_ci	unsigned long tmp1, tmp2;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	tmp1 = -4096UL;
3258c2ecf20Sopenharmony_ci	asm volatile(
3268c2ecf20Sopenharmony_ci		"0: .insn ss,0xc80000000000,0(%0,%1),0(%4),0\n"
3278c2ecf20Sopenharmony_ci		"   jz	  4f\n"
3288c2ecf20Sopenharmony_ci		"1: algr  %0,%2\n"
3298c2ecf20Sopenharmony_ci		"   slgr  %1,%2\n"
3308c2ecf20Sopenharmony_ci		"   j	  0b\n"
3318c2ecf20Sopenharmony_ci		"2: la	  %3,4095(%1)\n"/* %4 = to + 4095 */
3328c2ecf20Sopenharmony_ci		"   nr	  %3,%2\n"	/* %4 = (to + 4095) & -4096 */
3338c2ecf20Sopenharmony_ci		"   slgr  %3,%1\n"
3348c2ecf20Sopenharmony_ci		"   clgr  %0,%3\n"	/* copy crosses next page boundary? */
3358c2ecf20Sopenharmony_ci		"   jnh	  5f\n"
3368c2ecf20Sopenharmony_ci		"3: .insn ss,0xc80000000000,0(%3,%1),0(%4),0\n"
3378c2ecf20Sopenharmony_ci		"   slgr  %0,%3\n"
3388c2ecf20Sopenharmony_ci		"   j	  5f\n"
3398c2ecf20Sopenharmony_ci		"4: slgr  %0,%0\n"
3408c2ecf20Sopenharmony_ci		"5:\n"
3418c2ecf20Sopenharmony_ci		EX_TABLE(0b,2b) EX_TABLE(3b,5b)
3428c2ecf20Sopenharmony_ci		: "+&a" (size), "+&a" (to), "+a" (tmp1), "=&a" (tmp2)
3438c2ecf20Sopenharmony_ci		: "a" (empty_zero_page), "d" (reg0) : "cc", "memory");
3448c2ecf20Sopenharmony_ci	return size;
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic inline unsigned long clear_user_xc(void __user *to, unsigned long size)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	mm_segment_t old_fs;
3508c2ecf20Sopenharmony_ci	unsigned long tmp1, tmp2;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	old_fs = enable_sacf_uaccess();
3538c2ecf20Sopenharmony_ci	asm volatile(
3548c2ecf20Sopenharmony_ci		"   sacf  256\n"
3558c2ecf20Sopenharmony_ci		"   aghi  %0,-1\n"
3568c2ecf20Sopenharmony_ci		"   jo    5f\n"
3578c2ecf20Sopenharmony_ci		"   bras  %3,3f\n"
3588c2ecf20Sopenharmony_ci		"   xc    0(1,%1),0(%1)\n"
3598c2ecf20Sopenharmony_ci		"0: aghi  %0,257\n"
3608c2ecf20Sopenharmony_ci		"   la    %2,255(%1)\n" /* %2 = ptr + 255 */
3618c2ecf20Sopenharmony_ci		"   srl   %2,12\n"
3628c2ecf20Sopenharmony_ci		"   sll   %2,12\n"	/* %2 = (ptr + 255) & -4096 */
3638c2ecf20Sopenharmony_ci		"   slgr  %2,%1\n"
3648c2ecf20Sopenharmony_ci		"   clgr  %0,%2\n"	/* clear crosses next page boundary? */
3658c2ecf20Sopenharmony_ci		"   jnh   5f\n"
3668c2ecf20Sopenharmony_ci		"   aghi  %2,-1\n"
3678c2ecf20Sopenharmony_ci		"1: ex    %2,0(%3)\n"
3688c2ecf20Sopenharmony_ci		"   aghi  %2,1\n"
3698c2ecf20Sopenharmony_ci		"   slgr  %0,%2\n"
3708c2ecf20Sopenharmony_ci		"   j     5f\n"
3718c2ecf20Sopenharmony_ci		"2: xc    0(256,%1),0(%1)\n"
3728c2ecf20Sopenharmony_ci		"   la    %1,256(%1)\n"
3738c2ecf20Sopenharmony_ci		"3: aghi  %0,-256\n"
3748c2ecf20Sopenharmony_ci		"   jnm   2b\n"
3758c2ecf20Sopenharmony_ci		"4: ex    %0,0(%3)\n"
3768c2ecf20Sopenharmony_ci		"5: slgr  %0,%0\n"
3778c2ecf20Sopenharmony_ci		"6: sacf  768\n"
3788c2ecf20Sopenharmony_ci		EX_TABLE(1b,6b) EX_TABLE(2b,0b) EX_TABLE(4b,0b)
3798c2ecf20Sopenharmony_ci		: "+a" (size), "+a" (to), "=a" (tmp1), "=a" (tmp2)
3808c2ecf20Sopenharmony_ci		: : "cc", "memory");
3818c2ecf20Sopenharmony_ci	disable_sacf_uaccess(old_fs);
3828c2ecf20Sopenharmony_ci	return size;
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ciunsigned long __clear_user(void __user *to, unsigned long size)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	if (copy_with_mvcos())
3888c2ecf20Sopenharmony_ci			return clear_user_mvcos(to, size);
3898c2ecf20Sopenharmony_ci	return clear_user_xc(to, size);
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__clear_user);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic inline unsigned long strnlen_user_srst(const char __user *src,
3948c2ecf20Sopenharmony_ci					      unsigned long size)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	register unsigned long reg0 asm("0") = 0;
3978c2ecf20Sopenharmony_ci	unsigned long tmp1, tmp2;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	asm volatile(
4008c2ecf20Sopenharmony_ci		"   la    %2,0(%1)\n"
4018c2ecf20Sopenharmony_ci		"   la    %3,0(%0,%1)\n"
4028c2ecf20Sopenharmony_ci		"   slgr  %0,%0\n"
4038c2ecf20Sopenharmony_ci		"   sacf  256\n"
4048c2ecf20Sopenharmony_ci		"0: srst  %3,%2\n"
4058c2ecf20Sopenharmony_ci		"   jo    0b\n"
4068c2ecf20Sopenharmony_ci		"   la    %0,1(%3)\n"	/* strnlen_user results includes \0 */
4078c2ecf20Sopenharmony_ci		"   slgr  %0,%1\n"
4088c2ecf20Sopenharmony_ci		"1: sacf  768\n"
4098c2ecf20Sopenharmony_ci		EX_TABLE(0b,1b)
4108c2ecf20Sopenharmony_ci		: "+a" (size), "+a" (src), "=a" (tmp1), "=a" (tmp2)
4118c2ecf20Sopenharmony_ci		: "d" (reg0) : "cc", "memory");
4128c2ecf20Sopenharmony_ci	return size;
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ciunsigned long __strnlen_user(const char __user *src, unsigned long size)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	mm_segment_t old_fs;
4188c2ecf20Sopenharmony_ci	unsigned long len;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	if (unlikely(!size))
4218c2ecf20Sopenharmony_ci		return 0;
4228c2ecf20Sopenharmony_ci	old_fs = enable_sacf_uaccess();
4238c2ecf20Sopenharmony_ci	len = strnlen_user_srst(src, size);
4248c2ecf20Sopenharmony_ci	disable_sacf_uaccess(old_fs);
4258c2ecf20Sopenharmony_ci	return len;
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__strnlen_user);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cilong __strncpy_from_user(char *dst, const char __user *src, long size)
4308c2ecf20Sopenharmony_ci{
4318c2ecf20Sopenharmony_ci	size_t done, len, offset, len_str;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	if (unlikely(size <= 0))
4348c2ecf20Sopenharmony_ci		return 0;
4358c2ecf20Sopenharmony_ci	done = 0;
4368c2ecf20Sopenharmony_ci	do {
4378c2ecf20Sopenharmony_ci		offset = (size_t)src & (L1_CACHE_BYTES - 1);
4388c2ecf20Sopenharmony_ci		len = min(size - done, L1_CACHE_BYTES - offset);
4398c2ecf20Sopenharmony_ci		if (copy_from_user(dst, src, len))
4408c2ecf20Sopenharmony_ci			return -EFAULT;
4418c2ecf20Sopenharmony_ci		len_str = strnlen(dst, len);
4428c2ecf20Sopenharmony_ci		done += len_str;
4438c2ecf20Sopenharmony_ci		src += len_str;
4448c2ecf20Sopenharmony_ci		dst += len_str;
4458c2ecf20Sopenharmony_ci	} while ((len_str == len) && (done < size));
4468c2ecf20Sopenharmony_ci	return done;
4478c2ecf20Sopenharmony_ci}
4488c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__strncpy_from_user);
449