Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0fb19dcb authored by Stuart Menefy's avatar Stuart Menefy Committed by Paul Mundt
Browse files

sh: get_user fixes and nommu consolidation.



When a get_user(to, from++) is called the pointer increment is performed
after its first usage, in the specific after the __add_ok invokation.
This causes a wrong get_user return value, putting a wrong character
in the destination variable. This patch solves the problem using a new
temporary pointer.

Additionally this reworks the use of the register banks, allowing for
consolidation between the MMU and nommu implementations.

Signed-off-by: default avatarCarmelo Amoroso <carmelo.amoroso@st.com>
Signed-off-by: default avatarGiuseppe Condorelli <giuseppe.condorelli@st.com>
Signed-off-by: default avatarStuart Menefy <stuart.menefy@st.com>
Signed-off-by: default avatarPaul Mundt <lethal@linux-sh.org>
parent fc55888f
Loading
Loading
Loading
Loading
+98 −155
Original line number Original line Diff line number Diff line
@@ -73,37 +73,24 @@ static inline int __access_ok(unsigned long addr, unsigned long size)
/*
/*
 * __access_ok: Check if address with size is OK or not.
 * __access_ok: Check if address with size is OK or not.
 *
 *
 * We do three checks:
 * Uhhuh, this needs 33-bit arithmetic. We have a carry..
 * (1) is it user space?
 * (2) addr + size --> carry?
 * (3) addr + size >= 0x80000000  (PAGE_OFFSET)
 *
 *
 * (1) (2) (3) | RESULT
 * sum := addr + size;  carry? --> flag = true;
 *  0   0   0  |  ok
 * if (sum >= addr_limit) flag = true;
 *  0   0   1  |  ok
 *  0   1   0  |  bad
 *  0   1   1  |  bad
 *  1   0   0  |  ok
 *  1   0   1  |  bad
 *  1   1   0  |  bad
 *  1   1   1  |  bad
 */
 */
static inline int __access_ok(unsigned long addr, unsigned long size)
static inline int __access_ok(unsigned long addr, unsigned long size)
{
{
	unsigned long flag, tmp;
	unsigned long flag, sum;


	__asm__("stc	r7_bank, %0\n\t"
	__asm__("clrt\n\t"
		"mov.l	@(8,%0), %0\n\t"
		"addc	%3, %1\n\t"
		"clrt\n\t"
		"movt	%0\n\t"
		"addc	%2, %1\n\t"
		"cmp/hi	%4, %1\n\t"
		"and	%1, %0\n\t"
		"rotcl	%0"
		"rotcl	%0\n\t"
		:"=&r" (flag), "=r" (sum)
		"rotcl	%0\n\t"
		:"1" (addr), "r" (size),
		"and	#3, %0"
		 "r" (current_thread_info()->addr_limit.seg)
		: "=&z" (flag), "=r" (tmp)
		: "r" (addr), "1" (size)
		:"t");
		:"t");

	return flag == 0;
	return flag == 0;
}
}
#endif /* CONFIG_MMU */
#endif /* CONFIG_MMU */
@@ -165,135 +152,47 @@ do { \
#define __get_user_nocheck(x,ptr,size)				\
#define __get_user_nocheck(x,ptr,size)				\
({								\
({								\
	long __gu_err, __gu_val;				\
	long __gu_err, __gu_val;				\
	__get_user_size(__gu_val, (ptr), (size), __gu_err);	\
	__typeof__(*(ptr)) *__pu_addr = (ptr);  \
	__get_user_size(__gu_val, (__pu_addr), (size), __gu_err);	\
	(x) = (__typeof__(*(ptr)))__gu_val;			\
	(x) = (__typeof__(*(ptr)))__gu_val;			\
	__gu_err;						\
	__gu_err;						\
})
})


#ifdef CONFIG_MMU
#define __get_user_check(x,ptr,size)					\
#define __get_user_check(x,ptr,size)					\
({									\
({									\
	long __gu_err, __gu_val;					\
	long __gu_err, __gu_val;					\
	__chk_user_ptr(ptr);					\
	__typeof__(*(ptr)) *__pu_addr = (ptr);				\
	switch (size) {						\
	__chk_user_ptr(__pu_addr);					\
	case 1:							\
	if (likely(__addr_ok((unsigned long)(__pu_addr)))) {		\
		__get_user_1(__gu_val, (ptr), __gu_err);	\
		__get_user_size(__gu_val, (__pu_addr), (size), __gu_err);\
		break;						\
	} else {							\
	case 2:							\
		__gu_err = -EFAULT;					\
		__get_user_2(__gu_val, (ptr), __gu_err);	\
		__gu_val = 0;						\
		break;						\
	case 4:							\
		__get_user_4(__gu_val, (ptr), __gu_err);	\
		break;						\
	default:						\
		__get_user_unknown();				\
		break;						\
	}								\
	}								\
								\
	(x) = (__typeof__(*(ptr)))__gu_val;				\
	(x) = (__typeof__(*(ptr)))__gu_val;				\
	__gu_err;							\
	__gu_err;							\
})
})


#define __get_user_1(x,addr,err) ({		\
__asm__("stc	r7_bank, %1\n\t"		\
	"mov.l	@(8,%1), %1\n\t"		\
	"and	%2, %1\n\t"			\
	"cmp/pz	%1\n\t"				\
	"bt/s	1f\n\t"				\
	" mov	#0, %0\n\t"			\
	"0:\n"					\
	"mov	#-14, %0\n\t"			\
	"bra	2f\n\t"				\
	" mov	#0, %1\n"			\
	"1:\n\t"				\
	"mov.b	@%2, %1\n\t"			\
	"extu.b	%1, %1\n"			\
	"2:\n"					\
	".section	__ex_table,\"a\"\n\t"	\
	".long	1b, 0b\n\t"			\
	".previous"				\
	: "=&r" (err), "=&r" (x)		\
	: "r" (addr)				\
	: "t");					\
})

#define __get_user_2(x,addr,err) ({		\
__asm__("stc	r7_bank, %1\n\t"		\
	"mov.l	@(8,%1), %1\n\t"		\
	"and	%2, %1\n\t"			\
	"cmp/pz	%1\n\t"				\
	"bt/s	1f\n\t"				\
	" mov	#0, %0\n\t"			\
	"0:\n"					\
	"mov	#-14, %0\n\t"			\
	"bra	2f\n\t"				\
	" mov	#0, %1\n"			\
	"1:\n\t"				\
	"mov.w	@%2, %1\n\t"			\
	"extu.w	%1, %1\n"			\
	"2:\n"					\
	".section	__ex_table,\"a\"\n\t"	\
	".long	1b, 0b\n\t"			\
	".previous"				\
	: "=&r" (err), "=&r" (x)		\
	: "r" (addr)				\
	: "t");					\
})

#define __get_user_4(x,addr,err) ({		\
__asm__("stc	r7_bank, %1\n\t"		\
	"mov.l	@(8,%1), %1\n\t"		\
	"and	%2, %1\n\t"			\
	"cmp/pz	%1\n\t"				\
	"bt/s	1f\n\t"				\
	" mov	#0, %0\n\t"			\
	"0:\n"					\
	"mov	#-14, %0\n\t"			\
	"bra	2f\n\t"				\
	" mov	#0, %1\n"			\
	"1:\n\t"				\
	"mov.l	@%2, %1\n\t"			\
	"2:\n"					\
	".section	__ex_table,\"a\"\n\t"	\
	".long	1b, 0b\n\t"			\
	".previous"				\
	: "=&r" (err), "=&r" (x)		\
	: "r" (addr)				\
	: "t");					\
})
#else /* CONFIG_MMU */
#define __get_user_check(x,ptr,size)					\
({									\
	long __gu_err, __gu_val;					\
	if (__access_ok((unsigned long)(ptr), (size))) {		\
		__get_user_size(__gu_val, (ptr), (size), __gu_err);	\
		(x) = (__typeof__(*(ptr)))__gu_val;			\
	} else								\
		__gu_err = -EFAULT;					\
	__gu_err;							\
})
#endif

#define __get_user_asm(x, addr, err, insn) \
#define __get_user_asm(x, addr, err, insn) \
({ \
({ \
__asm__ __volatile__( \
__asm__ __volatile__( \
	"1:\n\t" \
	"1:\n\t" \
	"mov." insn "	%2, %1\n\t" \
	"mov." insn "	%2, %1\n\t" \
	"mov	#0, %0\n" \
	"2:\n" \
	"2:\n" \
	".section	.fixup,\"ax\"\n" \
	".section	.fixup,\"ax\"\n" \
	"3:\n\t" \
	"3:\n\t" \
	"mov	#0, %1\n\t" \
	"mov	#0, %1\n\t" \
	"mov.l	4f, %0\n\t" \
	"mov.l	4f, %0\n\t" \
	"jmp	@%0\n\t" \
	"jmp	@%0\n\t" \
	" mov	%3, %0\n" \
	" mov	%3, %0\n\t" \
	".balign	4\n" \
	"4:	.long	2b\n\t" \
	"4:	.long	2b\n\t" \
	".previous\n" \
	".previous\n" \
	".section	__ex_table,\"a\"\n\t" \
	".section	__ex_table,\"a\"\n\t" \
	".long	1b, 3b\n\t" \
	".long	1b, 3b\n\t" \
	".previous" \
	".previous" \
	:"=&r" (err), "=&r" (x) \
	:"=&r" (err), "=&r" (x) \
	:"m" (__m(addr)), "i" (-EFAULT)); })
	:"m" (__m(addr)), "i" (-EFAULT), "0" (err)); })


extern void __get_user_unknown(void);
extern void __get_user_unknown(void);


@@ -328,11 +227,13 @@ do { \


#define __put_user_check(x,ptr,size)				\
#define __put_user_check(x,ptr,size)				\
({								\
({								\
	long __pu_err = -EFAULT;				\
	long __pu_err;						\
	__typeof__(*(ptr)) __user *__pu_addr = (ptr);		\
	__typeof__(*(ptr)) __user *__pu_addr = (ptr);		\
								\
								\
	if (__access_ok((unsigned long)__pu_addr,size))		\
	if (likely(__addr_ok((unsigned long)__pu_addr)))	\
		__put_user_size((x),__pu_addr,(size),__pu_err);	\
		__put_user_size((x),__pu_addr,(size),__pu_err);	\
	else							\
		__pu_err = -EFAULT;				\
	__pu_err;						\
	__pu_err;						\
})
})


@@ -341,45 +242,43 @@ do { \
__asm__ __volatile__( \
__asm__ __volatile__( \
	"1:\n\t" \
	"1:\n\t" \
	"mov." insn "	%1, %2\n\t" \
	"mov." insn "	%1, %2\n\t" \
	"mov	#0, %0\n" \
	"2:\n" \
	"2:\n" \
	".section	.fixup,\"ax\"\n" \
	".section	.fixup,\"ax\"\n" \
	"3:\n\t" \
	"3:\n\t" \
	"nop\n\t" \
	"mov.l	4f, %0\n\t" \
	"mov.l	4f, %0\n\t" \
	"jmp	@%0\n\t" \
	"jmp	@%0\n\t" \
	"mov	%3, %0\n" \
	" mov	%3, %0\n\t" \
	".balign	4\n" \
	"4:	.long	2b\n\t" \
	"4:	.long	2b\n\t" \
	".previous\n" \
	".previous\n" \
	".section	__ex_table,\"a\"\n\t" \
	".section	__ex_table,\"a\"\n\t" \
	".long	1b, 3b\n\t" \
	".long	1b, 3b\n\t" \
	".previous" \
	".previous" \
	:"=&r" (err) \
	:"=&r" (err) \
	:"r" (x), "m" (__m(addr)), "i" (-EFAULT) \
	:"r" (x), "m" (__m(addr)), "i" (-EFAULT), "0" (err)	\
        :"memory"); })
        :"memory"); })


#if defined(__LITTLE_ENDIAN__)
#if defined(CONFIG_CPU_LITTLE_ENDIAN)
#define __put_user_u64(val,addr,retval) \
#define __put_user_u64(val,addr,retval) \
({ \
({ \
__asm__ __volatile__( \
__asm__ __volatile__( \
	"1:\n\t" \
	"1:\n\t" \
	"mov.l	%R1,%2\n\t" \
	"mov.l	%R1,%2\n\t" \
	"mov.l	%S1,%T2\n\t" \
	"mov.l	%S1,%T2\n\t" \
	"mov	#0,%0\n" \
	"2:\n" \
	"2:\n" \
	".section	.fixup,\"ax\"\n" \
	".section	.fixup,\"ax\"\n" \
	"3:\n\t" \
	"3:\n\t" \
	"nop\n\t" \
	"mov.l	4f,%0\n\t" \
	"mov.l	4f,%0\n\t" \
	"jmp	@%0\n\t" \
	"jmp	@%0\n\t" \
	" mov	%3,%0\n" \
	" mov	%3,%0\n\t" \
	".balign	4\n" \
	"4:	.long	2b\n\t" \
	"4:	.long	2b\n\t" \
	".previous\n" \
	".previous\n" \
	".section	__ex_table,\"a\"\n\t" \
	".section	__ex_table,\"a\"\n\t" \
	".long	1b, 3b\n\t" \
	".long	1b, 3b\n\t" \
	".previous" \
	".previous" \
	: "=r" (retval) \
	: "=r" (retval) \
	: "r" (val), "m" (__m(addr)), "i" (-EFAULT) \
	: "r" (val), "m" (__m(addr)), "i" (-EFAULT), "0" (retval) \
        : "memory"); })
        : "memory"); })
#else
#else
#define __put_user_u64(val,addr,retval) \
#define __put_user_u64(val,addr,retval) \
@@ -388,21 +287,20 @@ __asm__ __volatile__( \
	"1:\n\t" \
	"1:\n\t" \
	"mov.l	%S1,%2\n\t" \
	"mov.l	%S1,%2\n\t" \
	"mov.l	%R1,%T2\n\t" \
	"mov.l	%R1,%T2\n\t" \
	"mov	#0,%0\n" \
	"2:\n" \
	"2:\n" \
	".section	.fixup,\"ax\"\n" \
	".section	.fixup,\"ax\"\n" \
	"3:\n\t" \
	"3:\n\t" \
	"nop\n\t" \
	"mov.l	4f,%0\n\t" \
	"mov.l	4f,%0\n\t" \
	"jmp	@%0\n\t" \
	"jmp	@%0\n\t" \
	" mov	%3,%0\n" \
	" mov	%3,%0\n\t" \
	".balign	4\n" \
	"4:	.long	2b\n\t" \
	"4:	.long	2b\n\t" \
	".previous\n" \
	".previous\n" \
	".section	__ex_table,\"a\"\n\t" \
	".section	__ex_table,\"a\"\n\t" \
	".long	1b, 3b\n\t" \
	".long	1b, 3b\n\t" \
	".previous" \
	".previous" \
	: "=r" (retval) \
	: "=r" (retval) \
	: "r" (val), "m" (__m(addr)), "i" (-EFAULT) \
	: "r" (val), "m" (__m(addr)), "i" (-EFAULT), "0" (retval) \
        : "memory"); })
        : "memory"); })
#endif
#endif


@@ -463,7 +361,7 @@ static __inline__ int
__strncpy_from_user(unsigned long __dest, unsigned long __user __src, int __count)
__strncpy_from_user(unsigned long __dest, unsigned long __user __src, int __count)
{
{
	__kernel_size_t res;
	__kernel_size_t res;
	unsigned long __dummy, _d, _s;
	unsigned long __dummy, _d, _s, _c;


	__asm__ __volatile__(
	__asm__ __volatile__(
		"9:\n"
		"9:\n"
@@ -472,17 +370,17 @@ __strncpy_from_user(unsigned long __dest, unsigned long __user __src, int __coun
		"bt/s	2f\n"
		"bt/s	2f\n"
		"1:\n"
		"1:\n"
		"mov.b	%1, @%3\n\t"
		"mov.b	%1, @%3\n\t"
		"dt	%7\n\t"
		"dt	%4\n\t"
		"bf/s	9b\n\t"
		"bf/s	9b\n\t"
		" add	#1, %3\n\t"
		" add	#1, %3\n\t"
		"2:\n\t"
		"2:\n\t"
		"sub	%7, %0\n"
		"sub	%4, %0\n"
		"3:\n"
		"3:\n"
		".section .fixup,\"ax\"\n"
		".section .fixup,\"ax\"\n"
		"4:\n\t"
		"4:\n\t"
		"mov.l	5f, %1\n\t"
		"mov.l	5f, %1\n\t"
		"jmp	@%1\n\t"
		"jmp	@%1\n\t"
		" mov	%8, %0\n\t"
		" mov	%9, %0\n\t"
		".balign 4\n"
		".balign 4\n"
		"5:	.long 3b\n"
		"5:	.long 3b\n"
		".previous\n"
		".previous\n"
@@ -490,14 +388,32 @@ __strncpy_from_user(unsigned long __dest, unsigned long __user __src, int __coun
		"	.balign 4\n"
		"	.balign 4\n"
		"	.long 9b,4b\n"
		"	.long 9b,4b\n"
		".previous"
		".previous"
		: "=r" (res), "=&z" (__dummy), "=r" (_s), "=r" (_d)
		: "=r" (res), "=&z" (__dummy), "=r" (_s), "=r" (_d), "=r"(_c)
		: "0" (__count), "2" (__src), "3" (__dest), "r" (__count),
		: "0" (__count), "2" (__src), "3" (__dest), "4" (__count),
		  "i" (-EFAULT)
		  "i" (-EFAULT)
		: "memory", "t");
		: "memory", "t");


	return res;
	return res;
}
}


/**
 * strncpy_from_user: - Copy a NUL terminated string from userspace.
 * @dst:   Destination address, in kernel space.  This buffer must be at
 *         least @count bytes long.
 * @src:   Source address, in user space.
 * @count: Maximum number of bytes to copy, including the trailing NUL.
 *
 * Copies a NUL-terminated string from userspace to kernel space.
 *
 * On success, returns the length of the string (not including the trailing
 * NUL).
 *
 * If access to userspace fails, returns -EFAULT (some data may have been
 * copied).
 *
 * If @count is smaller than the length of the string, copies @count bytes
 * and returns @count.
 */
#define strncpy_from_user(dest,src,count) ({ \
#define strncpy_from_user(dest,src,count) ({ \
unsigned long __sfu_src = (unsigned long) (src); \
unsigned long __sfu_src = (unsigned long) (src); \
int __sfu_count = (int) (count); \
int __sfu_count = (int) (count); \
@@ -507,7 +423,8 @@ __sfu_res = __strncpy_from_user((unsigned long) (dest), __sfu_src, __sfu_count);
} __sfu_res; })
} __sfu_res; })


/*
/*
 * Return the size of a string (including the ending 0!)
 * Return the size of a string (including the ending 0 even when we have
 * exceeded the maximum string length).
 */
 */
static __inline__ long __strnlen_user(const char __user *__s, long __n)
static __inline__ long __strnlen_user(const char __user *__s, long __n)
{
{
@@ -515,14 +432,13 @@ static __inline__ long __strnlen_user(const char __user *__s, long __n)
	unsigned long __dummy;
	unsigned long __dummy;


	__asm__ __volatile__(
	__asm__ __volatile__(
		"9:\n"
		"cmp/eq	%4, %0\n\t"
		"bt	2f\n"
		"1:\t"
		"1:\t"
		"mov.b	@(%0,%3), %1\n\t"
		"mov.b	@(%0,%3), %1\n\t"
		"cmp/eq	%4, %0\n\t"
		"bt/s	2f\n\t"
		" add	#1, %0\n\t"
		"tst	%1, %1\n\t"
		"tst	%1, %1\n\t"
		"bf/s	9b\n\t"
		"bf	1b\n\t"
		" add	#1, %0\n"
		"2:\n"
		"2:\n"
		".section .fixup,\"ax\"\n"
		".section .fixup,\"ax\"\n"
		"3:\n\t"
		"3:\n\t"
@@ -542,6 +458,19 @@ static __inline__ long __strnlen_user(const char __user *__s, long __n)
	return res;
	return res;
}
}


/**
 * strnlen_user: - Get the size of a string in user space.
 * @s: The string to measure.
 * @n: The maximum valid length
 *
 * Context: User context only.  This function may sleep.
 *
 * Get the size of a NUL-terminated string in user space.
 *
 * Returns the size of the string INCLUDING the terminating NUL.
 * On exception, returns 0.
 * If the string is too long, returns a value greater than @n.
 */
static __inline__ long strnlen_user(const char __user *s, long n)
static __inline__ long strnlen_user(const char __user *s, long n)
{
{
	if (!__addr_ok(s))
	if (!__addr_ok(s))
@@ -550,6 +479,20 @@ static __inline__ long strnlen_user(const char __user *s, long n)
		return __strnlen_user(s, n);
		return __strnlen_user(s, n);
}
}


/**
 * strlen_user: - Get the size of a string in user space.
 * @str: The string to measure.
 *
 * Context: User context only.  This function may sleep.
 *
 * Get the size of a NUL-terminated string in user space.
 *
 * Returns the size of the string INCLUDING the terminating NUL.
 * On exception, returns 0.
 *
 * If there is a limit on the length of a valid string, you may wish to
 * consider using strnlen_user() instead.
 */
#define strlen_user(str)	strnlen_user(str, ~0UL >> 1)
#define strlen_user(str)	strnlen_user(str, ~0UL >> 1)


/*
/*