xref: /kernel/linux/linux-6.6/arch/s390/lib/uaccess.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Standard user space access functions based on mvcp/mvcs and doing
462306a36Sopenharmony_ci *  interesting things in the secondary space mode.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *    Copyright IBM Corp. 2006,2014
762306a36Sopenharmony_ci *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
862306a36Sopenharmony_ci *		 Gerald Schaefer (gerald.schaefer@de.ibm.com)
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/uaccess.h>
1262306a36Sopenharmony_ci#include <linux/export.h>
1362306a36Sopenharmony_ci#include <linux/mm.h>
1462306a36Sopenharmony_ci#include <asm/asm-extable.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_ENTRY
1762306a36Sopenharmony_civoid debug_user_asce(int exit)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	unsigned long cr1, cr7;
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	__ctl_store(cr1, 1, 1);
2262306a36Sopenharmony_ci	__ctl_store(cr7, 7, 7);
2362306a36Sopenharmony_ci	if (cr1 == S390_lowcore.kernel_asce && cr7 == S390_lowcore.user_asce)
2462306a36Sopenharmony_ci		return;
2562306a36Sopenharmony_ci	panic("incorrect ASCE on kernel %s\n"
2662306a36Sopenharmony_ci	      "cr1:    %016lx cr7:  %016lx\n"
2762306a36Sopenharmony_ci	      "kernel: %016llx user: %016llx\n",
2862306a36Sopenharmony_ci	      exit ? "exit" : "entry", cr1, cr7,
2962306a36Sopenharmony_ci	      S390_lowcore.kernel_asce, S390_lowcore.user_asce);
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci#endif /*CONFIG_DEBUG_ENTRY */
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic unsigned long raw_copy_from_user_key(void *to, const void __user *from,
3462306a36Sopenharmony_ci					    unsigned long size, unsigned long key)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	unsigned long rem;
3762306a36Sopenharmony_ci	union oac spec = {
3862306a36Sopenharmony_ci		.oac2.key = key,
3962306a36Sopenharmony_ci		.oac2.as = PSW_BITS_AS_SECONDARY,
4062306a36Sopenharmony_ci		.oac2.k = 1,
4162306a36Sopenharmony_ci		.oac2.a = 1,
4262306a36Sopenharmony_ci	};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	asm volatile(
4562306a36Sopenharmony_ci		"	lr	0,%[spec]\n"
4662306a36Sopenharmony_ci		"0:	mvcos	0(%[to]),0(%[from]),%[size]\n"
4762306a36Sopenharmony_ci		"1:	jz	5f\n"
4862306a36Sopenharmony_ci		"	algr	%[size],%[val]\n"
4962306a36Sopenharmony_ci		"	slgr	%[from],%[val]\n"
5062306a36Sopenharmony_ci		"	slgr	%[to],%[val]\n"
5162306a36Sopenharmony_ci		"	j	0b\n"
5262306a36Sopenharmony_ci		"2:	la	%[rem],4095(%[from])\n"	/* rem = from + 4095 */
5362306a36Sopenharmony_ci		"	nr	%[rem],%[val]\n"	/* rem = (from + 4095) & -4096 */
5462306a36Sopenharmony_ci		"	slgr	%[rem],%[from]\n"
5562306a36Sopenharmony_ci		"	clgr	%[size],%[rem]\n"	/* copy crosses next page boundary? */
5662306a36Sopenharmony_ci		"	jnh	6f\n"
5762306a36Sopenharmony_ci		"3:	mvcos	0(%[to]),0(%[from]),%[rem]\n"
5862306a36Sopenharmony_ci		"4:	slgr	%[size],%[rem]\n"
5962306a36Sopenharmony_ci		"	j	6f\n"
6062306a36Sopenharmony_ci		"5:	slgr	%[size],%[size]\n"
6162306a36Sopenharmony_ci		"6:\n"
6262306a36Sopenharmony_ci		EX_TABLE(0b, 2b)
6362306a36Sopenharmony_ci		EX_TABLE(1b, 2b)
6462306a36Sopenharmony_ci		EX_TABLE(3b, 6b)
6562306a36Sopenharmony_ci		EX_TABLE(4b, 6b)
6662306a36Sopenharmony_ci		: [size] "+&a" (size), [from] "+&a" (from), [to] "+&a" (to), [rem] "=&a" (rem)
6762306a36Sopenharmony_ci		: [val] "a" (-4096UL), [spec] "d" (spec.val)
6862306a36Sopenharmony_ci		: "cc", "memory", "0");
6962306a36Sopenharmony_ci	return size;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ciunsigned long raw_copy_from_user(void *to, const void __user *from, unsigned long n)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	return raw_copy_from_user_key(to, from, n, 0);
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ciEXPORT_SYMBOL(raw_copy_from_user);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ciunsigned long _copy_from_user_key(void *to, const void __user *from,
7962306a36Sopenharmony_ci				  unsigned long n, unsigned long key)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	unsigned long res = n;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	might_fault();
8462306a36Sopenharmony_ci	if (!should_fail_usercopy()) {
8562306a36Sopenharmony_ci		instrument_copy_from_user_before(to, from, n);
8662306a36Sopenharmony_ci		res = raw_copy_from_user_key(to, from, n, key);
8762306a36Sopenharmony_ci		instrument_copy_from_user_after(to, from, n, res);
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci	if (unlikely(res))
9062306a36Sopenharmony_ci		memset(to + (n - res), 0, res);
9162306a36Sopenharmony_ci	return res;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ciEXPORT_SYMBOL(_copy_from_user_key);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic unsigned long raw_copy_to_user_key(void __user *to, const void *from,
9662306a36Sopenharmony_ci					  unsigned long size, unsigned long key)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	unsigned long rem;
9962306a36Sopenharmony_ci	union oac spec = {
10062306a36Sopenharmony_ci		.oac1.key = key,
10162306a36Sopenharmony_ci		.oac1.as = PSW_BITS_AS_SECONDARY,
10262306a36Sopenharmony_ci		.oac1.k = 1,
10362306a36Sopenharmony_ci		.oac1.a = 1,
10462306a36Sopenharmony_ci	};
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	asm volatile(
10762306a36Sopenharmony_ci		"	lr	0,%[spec]\n"
10862306a36Sopenharmony_ci		"0:	mvcos	0(%[to]),0(%[from]),%[size]\n"
10962306a36Sopenharmony_ci		"1:	jz	5f\n"
11062306a36Sopenharmony_ci		"	algr	%[size],%[val]\n"
11162306a36Sopenharmony_ci		"	slgr	%[to],%[val]\n"
11262306a36Sopenharmony_ci		"	slgr	%[from],%[val]\n"
11362306a36Sopenharmony_ci		"	j	0b\n"
11462306a36Sopenharmony_ci		"2:	la	%[rem],4095(%[to])\n"	/* rem = to + 4095 */
11562306a36Sopenharmony_ci		"	nr	%[rem],%[val]\n"	/* rem = (to + 4095) & -4096 */
11662306a36Sopenharmony_ci		"	slgr	%[rem],%[to]\n"
11762306a36Sopenharmony_ci		"	clgr	%[size],%[rem]\n"	/* copy crosses next page boundary? */
11862306a36Sopenharmony_ci		"	jnh	6f\n"
11962306a36Sopenharmony_ci		"3:	mvcos	0(%[to]),0(%[from]),%[rem]\n"
12062306a36Sopenharmony_ci		"4:	slgr	%[size],%[rem]\n"
12162306a36Sopenharmony_ci		"	j	6f\n"
12262306a36Sopenharmony_ci		"5:	slgr	%[size],%[size]\n"
12362306a36Sopenharmony_ci		"6:\n"
12462306a36Sopenharmony_ci		EX_TABLE(0b, 2b)
12562306a36Sopenharmony_ci		EX_TABLE(1b, 2b)
12662306a36Sopenharmony_ci		EX_TABLE(3b, 6b)
12762306a36Sopenharmony_ci		EX_TABLE(4b, 6b)
12862306a36Sopenharmony_ci		: [size] "+&a" (size), [to] "+&a" (to), [from] "+&a" (from), [rem] "=&a" (rem)
12962306a36Sopenharmony_ci		: [val] "a" (-4096UL), [spec] "d" (spec.val)
13062306a36Sopenharmony_ci		: "cc", "memory", "0");
13162306a36Sopenharmony_ci	return size;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ciunsigned long raw_copy_to_user(void __user *to, const void *from, unsigned long n)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	return raw_copy_to_user_key(to, from, n, 0);
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ciEXPORT_SYMBOL(raw_copy_to_user);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ciunsigned long _copy_to_user_key(void __user *to, const void *from,
14162306a36Sopenharmony_ci				unsigned long n, unsigned long key)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	might_fault();
14462306a36Sopenharmony_ci	if (should_fail_usercopy())
14562306a36Sopenharmony_ci		return n;
14662306a36Sopenharmony_ci	instrument_copy_to_user(to, from, n);
14762306a36Sopenharmony_ci	return raw_copy_to_user_key(to, from, n, key);
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ciEXPORT_SYMBOL(_copy_to_user_key);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ciunsigned long __clear_user(void __user *to, unsigned long size)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	unsigned long rem;
15462306a36Sopenharmony_ci	union oac spec = {
15562306a36Sopenharmony_ci		.oac1.as = PSW_BITS_AS_SECONDARY,
15662306a36Sopenharmony_ci		.oac1.a = 1,
15762306a36Sopenharmony_ci	};
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	asm volatile(
16062306a36Sopenharmony_ci		"	lr	0,%[spec]\n"
16162306a36Sopenharmony_ci		"0:	mvcos	0(%[to]),0(%[zeropg]),%[size]\n"
16262306a36Sopenharmony_ci		"1:	jz	5f\n"
16362306a36Sopenharmony_ci		"	algr	%[size],%[val]\n"
16462306a36Sopenharmony_ci		"	slgr	%[to],%[val]\n"
16562306a36Sopenharmony_ci		"	j	0b\n"
16662306a36Sopenharmony_ci		"2:	la	%[rem],4095(%[to])\n"	/* rem = to + 4095 */
16762306a36Sopenharmony_ci		"	nr	%[rem],%[val]\n"	/* rem = (to + 4095) & -4096 */
16862306a36Sopenharmony_ci		"	slgr	%[rem],%[to]\n"
16962306a36Sopenharmony_ci		"	clgr	%[size],%[rem]\n"	/* copy crosses next page boundary? */
17062306a36Sopenharmony_ci		"	jnh	6f\n"
17162306a36Sopenharmony_ci		"3:	mvcos	0(%[to]),0(%[zeropg]),%[rem]\n"
17262306a36Sopenharmony_ci		"4:	slgr	%[size],%[rem]\n"
17362306a36Sopenharmony_ci		"	j	6f\n"
17462306a36Sopenharmony_ci		"5:	slgr	%[size],%[size]\n"
17562306a36Sopenharmony_ci		"6:\n"
17662306a36Sopenharmony_ci		EX_TABLE(0b, 2b)
17762306a36Sopenharmony_ci		EX_TABLE(1b, 2b)
17862306a36Sopenharmony_ci		EX_TABLE(3b, 6b)
17962306a36Sopenharmony_ci		EX_TABLE(4b, 6b)
18062306a36Sopenharmony_ci		: [size] "+&a" (size), [to] "+&a" (to), [rem] "=&a" (rem)
18162306a36Sopenharmony_ci		: [val] "a" (-4096UL), [zeropg] "a" (empty_zero_page), [spec] "d" (spec.val)
18262306a36Sopenharmony_ci		: "cc", "memory", "0");
18362306a36Sopenharmony_ci	return size;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ciEXPORT_SYMBOL(__clear_user);
186