diff options
Diffstat (limited to 'arch/s390/lib/uaccess.c')
| -rw-r--r-- | arch/s390/lib/uaccess.c | 90 | 
1 files changed, 80 insertions, 10 deletions
| diff --git a/arch/s390/lib/uaccess.c b/arch/s390/lib/uaccess.c index 802903c50de1..c4f8039a35e8 100644 --- a/arch/s390/lib/uaccess.c +++ b/arch/s390/lib/uaccess.c @@ -40,10 +40,67 @@ static inline int copy_with_mvcos(void)  }  #endif +void set_fs(mm_segment_t fs) +{ +	current->thread.mm_segment = fs; +	if (fs == USER_DS) { +		__ctl_load(S390_lowcore.user_asce, 1, 1); +		clear_cpu_flag(CIF_ASCE_PRIMARY); +	} else { +		__ctl_load(S390_lowcore.kernel_asce, 1, 1); +		set_cpu_flag(CIF_ASCE_PRIMARY); +	} +	if (fs & 1) { +		if (fs == USER_DS_SACF) +			__ctl_load(S390_lowcore.user_asce, 7, 7); +		else +			__ctl_load(S390_lowcore.kernel_asce, 7, 7); +		set_cpu_flag(CIF_ASCE_SECONDARY); +	} +} +EXPORT_SYMBOL(set_fs); + +mm_segment_t enable_sacf_uaccess(void) +{ +	mm_segment_t old_fs; +	unsigned long asce, cr; + +	old_fs = current->thread.mm_segment; +	if (old_fs & 1) +		return old_fs; +	current->thread.mm_segment |= 1; +	asce = S390_lowcore.kernel_asce; +	if (likely(old_fs == USER_DS)) { +		__ctl_store(cr, 1, 1); +		if (cr != S390_lowcore.kernel_asce) { +			__ctl_load(S390_lowcore.kernel_asce, 1, 1); +			set_cpu_flag(CIF_ASCE_PRIMARY); +		} +		asce = S390_lowcore.user_asce; +	} +	__ctl_store(cr, 7, 7); +	if (cr != asce) { +		__ctl_load(asce, 7, 7); +		set_cpu_flag(CIF_ASCE_SECONDARY); +	} +	return old_fs; +} +EXPORT_SYMBOL(enable_sacf_uaccess); + +void disable_sacf_uaccess(mm_segment_t old_fs) +{ +	current->thread.mm_segment = old_fs; +	if (old_fs == USER_DS && test_facility(27)) { +		__ctl_load(S390_lowcore.user_asce, 1, 1); +		clear_cpu_flag(CIF_ASCE_PRIMARY); +	} +} +EXPORT_SYMBOL(disable_sacf_uaccess); +  static inline unsigned long copy_from_user_mvcos(void *x, const void __user *ptr,  						 unsigned long size)  { -	register unsigned long reg0 asm("0") = 0x81UL; +	register unsigned long reg0 asm("0") = 0x01UL;  	unsigned long tmp1, tmp2;  	tmp1 = -4096UL; @@ -74,8 +131,9 @@ static inline unsigned long copy_from_user_mvcp(void *x, const void __user *ptr,  						unsigned long size)  {  	unsigned long tmp1, tmp2; +	mm_segment_t old_fs; -	load_kernel_asce(); +	old_fs = enable_sacf_uaccess();  	tmp1 = -256UL;  	asm volatile(  		"   sacf  0\n" @@ -102,6 +160,7 @@ static inline unsigned long copy_from_user_mvcp(void *x, const void __user *ptr,  		EX_TABLE(7b,3b) EX_TABLE(8b,3b) EX_TABLE(9b,6b)  		: "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)  		: : "cc", "memory"); +	disable_sacf_uaccess(old_fs);  	return size;  } @@ -116,7 +175,7 @@ EXPORT_SYMBOL(raw_copy_from_user);  static inline unsigned long copy_to_user_mvcos(void __user *ptr, const void *x,  					       unsigned long size)  { -	register unsigned long reg0 asm("0") = 0x810000UL; +	register unsigned long reg0 asm("0") = 0x010000UL;  	unsigned long tmp1, tmp2;  	tmp1 = -4096UL; @@ -147,8 +206,9 @@ static inline unsigned long copy_to_user_mvcs(void __user *ptr, const void *x,  					      unsigned long size)  {  	unsigned long tmp1, tmp2; +	mm_segment_t old_fs; -	load_kernel_asce(); +	old_fs = enable_sacf_uaccess();  	tmp1 = -256UL;  	asm volatile(  		"   sacf  0\n" @@ -175,6 +235,7 @@ static inline unsigned long copy_to_user_mvcs(void __user *ptr, const void *x,  		EX_TABLE(7b,3b) EX_TABLE(8b,3b) EX_TABLE(9b,6b)  		: "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)  		: : "cc", "memory"); +	disable_sacf_uaccess(old_fs);  	return size;  } @@ -189,7 +250,7 @@ EXPORT_SYMBOL(raw_copy_to_user);  static inline unsigned long copy_in_user_mvcos(void __user *to, const void __user *from,  					       unsigned long size)  { -	register unsigned long reg0 asm("0") = 0x810081UL; +	register unsigned long reg0 asm("0") = 0x010001UL;  	unsigned long tmp1, tmp2;  	tmp1 = -4096UL; @@ -212,9 +273,10 @@ static inline unsigned long copy_in_user_mvcos(void __user *to, const void __use  static inline unsigned long copy_in_user_mvc(void __user *to, const void __user *from,  					     unsigned long size)  { +	mm_segment_t old_fs;  	unsigned long tmp1; -	load_kernel_asce(); +	old_fs = enable_sacf_uaccess();  	asm volatile(  		"   sacf  256\n"  		"   aghi  %0,-1\n" @@ -238,6 +300,7 @@ static inline unsigned long copy_in_user_mvc(void __user *to, const void __user  		EX_TABLE(1b,6b) EX_TABLE(2b,0b) EX_TABLE(4b,0b)  		: "+a" (size), "+a" (to), "+a" (from), "=a" (tmp1)  		: : "cc", "memory"); +	disable_sacf_uaccess(old_fs);  	return size;  } @@ -251,7 +314,7 @@ EXPORT_SYMBOL(raw_copy_in_user);  static inline unsigned long clear_user_mvcos(void __user *to, unsigned long size)  { -	register unsigned long reg0 asm("0") = 0x810000UL; +	register unsigned long reg0 asm("0") = 0x010000UL;  	unsigned long tmp1, tmp2;  	tmp1 = -4096UL; @@ -279,9 +342,10 @@ static inline unsigned long clear_user_mvcos(void __user *to, unsigned long size  static inline unsigned long clear_user_xc(void __user *to, unsigned long size)  { +	mm_segment_t old_fs;  	unsigned long tmp1, tmp2; -	load_kernel_asce(); +	old_fs = enable_sacf_uaccess();  	asm volatile(  		"   sacf  256\n"  		"   aghi  %0,-1\n" @@ -310,6 +374,7 @@ static inline unsigned long clear_user_xc(void __user *to, unsigned long size)  		EX_TABLE(1b,6b) EX_TABLE(2b,0b) EX_TABLE(4b,0b)  		: "+a" (size), "+a" (to), "=a" (tmp1), "=a" (tmp2)  		: : "cc", "memory"); +	disable_sacf_uaccess(old_fs);  	return size;  } @@ -345,10 +410,15 @@ static inline unsigned long strnlen_user_srst(const char __user *src,  unsigned long __strnlen_user(const char __user *src, unsigned long size)  { +	mm_segment_t old_fs; +	unsigned long len; +  	if (unlikely(!size))  		return 0; -	load_kernel_asce(); -	return strnlen_user_srst(src, size); +	old_fs = enable_sacf_uaccess(); +	len = strnlen_user_srst(src, size); +	disable_sacf_uaccess(old_fs); +	return len;  }  EXPORT_SYMBOL(__strnlen_user); |