18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (C) 2009 Michal Simek <monstr@monstr.eu>
38c2ecf20Sopenharmony_ci * Copyright (C) 2009 PetaLogix
48c2ecf20Sopenharmony_ci * Copyright (C) 2007 LynuxWorks, Inc.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
78c2ecf20Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
88c2ecf20Sopenharmony_ci * for more details.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/errno.h>
128c2ecf20Sopenharmony_ci#include <linux/linkage.h>
138c2ecf20Sopenharmony_ci#include <asm/page.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci/*
168c2ecf20Sopenharmony_ci * int __strncpy_user(char *to, char *from, int len);
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * Returns:
198c2ecf20Sopenharmony_ci *  -EFAULT  for an exception
208c2ecf20Sopenharmony_ci *  len      if we hit the buffer limit
218c2ecf20Sopenharmony_ci *  bytes copied
228c2ecf20Sopenharmony_ci */
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	.text
258c2ecf20Sopenharmony_ci.globl __strncpy_user;
268c2ecf20Sopenharmony_ci.type  __strncpy_user, @function
278c2ecf20Sopenharmony_ci.align 4;
288c2ecf20Sopenharmony_ci__strncpy_user:
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	/*
318c2ecf20Sopenharmony_ci	 * r5 - to
328c2ecf20Sopenharmony_ci	 * r6 - from
338c2ecf20Sopenharmony_ci	 * r7 - len
348c2ecf20Sopenharmony_ci	 * r3 - temp count
358c2ecf20Sopenharmony_ci	 * r4 - temp val
368c2ecf20Sopenharmony_ci	 */
378c2ecf20Sopenharmony_ci	beqid	r7,3f
388c2ecf20Sopenharmony_ci	addik	r3,r7,0		/* temp_count = len */
398c2ecf20Sopenharmony_ci1:
408c2ecf20Sopenharmony_ci	lbu	r4,r6,r0
418c2ecf20Sopenharmony_ci	beqid	r4,2f
428c2ecf20Sopenharmony_ci	sb	r4,r5,r0
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	addik	r5,r5,1
458c2ecf20Sopenharmony_ci	addik	r6,r6,1		/* delay slot */
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	addik	r3,r3,-1
488c2ecf20Sopenharmony_ci	bnei	r3,1b		/* break on len */
498c2ecf20Sopenharmony_ci2:
508c2ecf20Sopenharmony_ci	rsubk	r3,r3,r7	/* temp_count = len - temp_count */
518c2ecf20Sopenharmony_ci3:
528c2ecf20Sopenharmony_ci	rtsd	r15,8
538c2ecf20Sopenharmony_ci	nop
548c2ecf20Sopenharmony_ci	.size   __strncpy_user, . - __strncpy_user
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	.section	.fixup, "ax"
578c2ecf20Sopenharmony_ci	.align	2
588c2ecf20Sopenharmony_ci4:
598c2ecf20Sopenharmony_ci	brid	3b
608c2ecf20Sopenharmony_ci	addik	r3,r0, -EFAULT
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	.section	__ex_table, "a"
638c2ecf20Sopenharmony_ci	.word	1b,4b
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/*
668c2ecf20Sopenharmony_ci * int __strnlen_user(char __user *str, int maxlen);
678c2ecf20Sopenharmony_ci *
688c2ecf20Sopenharmony_ci * Returns:
698c2ecf20Sopenharmony_ci *  0 on error
708c2ecf20Sopenharmony_ci *  maxlen + 1  if no NUL byte found within maxlen bytes
718c2ecf20Sopenharmony_ci *  size of the string (including NUL byte)
728c2ecf20Sopenharmony_ci */
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	.text
758c2ecf20Sopenharmony_ci.globl __strnlen_user;
768c2ecf20Sopenharmony_ci.type  __strnlen_user, @function
778c2ecf20Sopenharmony_ci.align 4;
788c2ecf20Sopenharmony_ci__strnlen_user:
798c2ecf20Sopenharmony_ci	beqid	r6,3f
808c2ecf20Sopenharmony_ci	addik	r3,r6,0
818c2ecf20Sopenharmony_ci1:
828c2ecf20Sopenharmony_ci	lbu	r4,r5,r0
838c2ecf20Sopenharmony_ci	beqid	r4,2f		/* break on NUL */
848c2ecf20Sopenharmony_ci	addik	r3,r3,-1	/* delay slot */
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	bneid	r3,1b
878c2ecf20Sopenharmony_ci	addik	r5,r5,1		/* delay slot */
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	addik	r3,r3,-1	/* for break on len */
908c2ecf20Sopenharmony_ci2:
918c2ecf20Sopenharmony_ci	rsubk	r3,r3,r6
928c2ecf20Sopenharmony_ci3:
938c2ecf20Sopenharmony_ci	rtsd	r15,8
948c2ecf20Sopenharmony_ci	nop
958c2ecf20Sopenharmony_ci	.size   __strnlen_user, . - __strnlen_user
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	.section	.fixup,"ax"
988c2ecf20Sopenharmony_ci4:
998c2ecf20Sopenharmony_ci	brid	3b
1008c2ecf20Sopenharmony_ci	addk	r3,r0,r0
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	.section	__ex_table,"a"
1038c2ecf20Sopenharmony_ci	.word	1b,4b
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci/* Loop unrolling for __copy_tofrom_user */
1068c2ecf20Sopenharmony_ci#define COPY(offset)	\
1078c2ecf20Sopenharmony_ci1:	lwi	r4 , r6, 0x0000 + offset;	\
1088c2ecf20Sopenharmony_ci2:	lwi	r19, r6, 0x0004 + offset;	\
1098c2ecf20Sopenharmony_ci3:	lwi	r20, r6, 0x0008 + offset;	\
1108c2ecf20Sopenharmony_ci4:	lwi	r21, r6, 0x000C + offset;	\
1118c2ecf20Sopenharmony_ci5:	lwi	r22, r6, 0x0010 + offset;	\
1128c2ecf20Sopenharmony_ci6:	lwi	r23, r6, 0x0014 + offset;	\
1138c2ecf20Sopenharmony_ci7:	lwi	r24, r6, 0x0018 + offset;	\
1148c2ecf20Sopenharmony_ci8:	lwi	r25, r6, 0x001C + offset;	\
1158c2ecf20Sopenharmony_ci9:	swi	r4 , r5, 0x0000 + offset;	\
1168c2ecf20Sopenharmony_ci10:	swi	r19, r5, 0x0004 + offset;	\
1178c2ecf20Sopenharmony_ci11:	swi	r20, r5, 0x0008 + offset;	\
1188c2ecf20Sopenharmony_ci12:	swi	r21, r5, 0x000C + offset;	\
1198c2ecf20Sopenharmony_ci13:	swi	r22, r5, 0x0010 + offset;	\
1208c2ecf20Sopenharmony_ci14:	swi	r23, r5, 0x0014 + offset;	\
1218c2ecf20Sopenharmony_ci15:	swi	r24, r5, 0x0018 + offset;	\
1228c2ecf20Sopenharmony_ci16:	swi	r25, r5, 0x001C + offset;	\
1238c2ecf20Sopenharmony_ci	.section __ex_table,"a";		\
1248c2ecf20Sopenharmony_ci	.word	1b, 33f;			\
1258c2ecf20Sopenharmony_ci	.word	2b, 33f;			\
1268c2ecf20Sopenharmony_ci	.word	3b, 33f;			\
1278c2ecf20Sopenharmony_ci	.word	4b, 33f;			\
1288c2ecf20Sopenharmony_ci	.word	5b, 33f;			\
1298c2ecf20Sopenharmony_ci	.word	6b, 33f;			\
1308c2ecf20Sopenharmony_ci	.word	7b, 33f;			\
1318c2ecf20Sopenharmony_ci	.word	8b, 33f;			\
1328c2ecf20Sopenharmony_ci	.word	9b, 33f;			\
1338c2ecf20Sopenharmony_ci	.word	10b, 33f;			\
1348c2ecf20Sopenharmony_ci	.word	11b, 33f;			\
1358c2ecf20Sopenharmony_ci	.word	12b, 33f;			\
1368c2ecf20Sopenharmony_ci	.word	13b, 33f;			\
1378c2ecf20Sopenharmony_ci	.word	14b, 33f;			\
1388c2ecf20Sopenharmony_ci	.word	15b, 33f;			\
1398c2ecf20Sopenharmony_ci	.word	16b, 33f;			\
1408c2ecf20Sopenharmony_ci	.text
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci#define COPY_80(offset)	\
1438c2ecf20Sopenharmony_ci	COPY(0x00 + offset);\
1448c2ecf20Sopenharmony_ci	COPY(0x20 + offset);\
1458c2ecf20Sopenharmony_ci	COPY(0x40 + offset);\
1468c2ecf20Sopenharmony_ci	COPY(0x60 + offset);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci/*
1498c2ecf20Sopenharmony_ci * int __copy_tofrom_user(char *to, char *from, int len)
1508c2ecf20Sopenharmony_ci * Return:
1518c2ecf20Sopenharmony_ci *   0 on success
1528c2ecf20Sopenharmony_ci *   number of not copied bytes on error
1538c2ecf20Sopenharmony_ci */
1548c2ecf20Sopenharmony_ci	.text
1558c2ecf20Sopenharmony_ci.globl __copy_tofrom_user;
1568c2ecf20Sopenharmony_ci.type  __copy_tofrom_user, @function
1578c2ecf20Sopenharmony_ci.align 4;
1588c2ecf20Sopenharmony_ci__copy_tofrom_user:
1598c2ecf20Sopenharmony_ci	/*
1608c2ecf20Sopenharmony_ci	 * r5 - to
1618c2ecf20Sopenharmony_ci	 * r6 - from
1628c2ecf20Sopenharmony_ci	 * r7, r3 - count
1638c2ecf20Sopenharmony_ci	 * r4 - tempval
1648c2ecf20Sopenharmony_ci	 */
1658c2ecf20Sopenharmony_ci	beqid	r7, 0f /* zero size is not likely */
1668c2ecf20Sopenharmony_ci	or	r3, r5, r6 /* find if is any to/from unaligned */
1678c2ecf20Sopenharmony_ci	or	r3, r3, r7 /* find if count is unaligned */
1688c2ecf20Sopenharmony_ci	andi	r3, r3, 0x3 /* mask last 3 bits */
1698c2ecf20Sopenharmony_ci	bneid	r3, bu1 /* if r3 is not zero then byte copying */
1708c2ecf20Sopenharmony_ci	or	r3, r0, r0
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	rsubi	r3, r7, PAGE_SIZE /* detect PAGE_SIZE */
1738c2ecf20Sopenharmony_ci	beqid	r3, page;
1748c2ecf20Sopenharmony_ci	or	r3, r0, r0
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ciw1:	lw	r4, r6, r3 /* at least one 4 byte copy */
1778c2ecf20Sopenharmony_ciw2:	sw	r4, r5, r3
1788c2ecf20Sopenharmony_ci	addik	r7, r7, -4
1798c2ecf20Sopenharmony_ci	bneid	r7, w1
1808c2ecf20Sopenharmony_ci	addik	r3, r3, 4
1818c2ecf20Sopenharmony_ci	addik	r3, r7, 0
1828c2ecf20Sopenharmony_ci	rtsd	r15, 8
1838c2ecf20Sopenharmony_ci	nop
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	.section	__ex_table,"a"
1868c2ecf20Sopenharmony_ci	.word	w1, 0f;
1878c2ecf20Sopenharmony_ci	.word	w2, 0f;
1888c2ecf20Sopenharmony_ci	.text
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci.align 4 /* Alignment is important to keep icache happy */
1918c2ecf20Sopenharmony_cipage:	/* Create room on stack and save registers for storign values */
1928c2ecf20Sopenharmony_ci	addik   r1, r1, -40
1938c2ecf20Sopenharmony_ci	swi	r5, r1, 0
1948c2ecf20Sopenharmony_ci	swi	r6, r1, 4
1958c2ecf20Sopenharmony_ci	swi	r7, r1, 8
1968c2ecf20Sopenharmony_ci	swi	r19, r1, 12
1978c2ecf20Sopenharmony_ci	swi	r20, r1, 16
1988c2ecf20Sopenharmony_ci	swi	r21, r1, 20
1998c2ecf20Sopenharmony_ci	swi	r22, r1, 24
2008c2ecf20Sopenharmony_ci	swi	r23, r1, 28
2018c2ecf20Sopenharmony_ci	swi	r24, r1, 32
2028c2ecf20Sopenharmony_ci	swi	r25, r1, 36
2038c2ecf20Sopenharmony_ciloop:	/* r4, r19, r20, r21, r22, r23, r24, r25 are used for storing values */
2048c2ecf20Sopenharmony_ci	/* Loop unrolling to get performance boost */
2058c2ecf20Sopenharmony_ci	COPY_80(0x000);
2068c2ecf20Sopenharmony_ci	COPY_80(0x080);
2078c2ecf20Sopenharmony_ci	COPY_80(0x100);
2088c2ecf20Sopenharmony_ci	COPY_80(0x180);
2098c2ecf20Sopenharmony_ci	/* copy loop */
2108c2ecf20Sopenharmony_ci	addik   r6, r6, 0x200
2118c2ecf20Sopenharmony_ci	addik   r7, r7, -0x200
2128c2ecf20Sopenharmony_ci	bneid   r7, loop
2138c2ecf20Sopenharmony_ci	addik   r5, r5, 0x200
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	/* Restore register content */
2168c2ecf20Sopenharmony_ci	lwi	r5, r1, 0
2178c2ecf20Sopenharmony_ci	lwi	r6, r1, 4
2188c2ecf20Sopenharmony_ci	lwi	r7, r1, 8
2198c2ecf20Sopenharmony_ci	lwi	r19, r1, 12
2208c2ecf20Sopenharmony_ci	lwi	r20, r1, 16
2218c2ecf20Sopenharmony_ci	lwi	r21, r1, 20
2228c2ecf20Sopenharmony_ci	lwi	r22, r1, 24
2238c2ecf20Sopenharmony_ci	lwi	r23, r1, 28
2248c2ecf20Sopenharmony_ci	lwi	r24, r1, 32
2258c2ecf20Sopenharmony_ci	lwi	r25, r1, 36
2268c2ecf20Sopenharmony_ci	addik   r1, r1, 40
2278c2ecf20Sopenharmony_ci	/* return back */
2288c2ecf20Sopenharmony_ci	addik	r3, r0, 0
2298c2ecf20Sopenharmony_ci	rtsd	r15, 8
2308c2ecf20Sopenharmony_ci	nop
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci/* Fault case - return temp count */
2338c2ecf20Sopenharmony_ci33:
2348c2ecf20Sopenharmony_ci	addik	r3, r7, 0
2358c2ecf20Sopenharmony_ci	/* Restore register content */
2368c2ecf20Sopenharmony_ci	lwi	r5, r1, 0
2378c2ecf20Sopenharmony_ci	lwi	r6, r1, 4
2388c2ecf20Sopenharmony_ci	lwi	r7, r1, 8
2398c2ecf20Sopenharmony_ci	lwi	r19, r1, 12
2408c2ecf20Sopenharmony_ci	lwi	r20, r1, 16
2418c2ecf20Sopenharmony_ci	lwi	r21, r1, 20
2428c2ecf20Sopenharmony_ci	lwi	r22, r1, 24
2438c2ecf20Sopenharmony_ci	lwi	r23, r1, 28
2448c2ecf20Sopenharmony_ci	lwi	r24, r1, 32
2458c2ecf20Sopenharmony_ci	lwi	r25, r1, 36
2468c2ecf20Sopenharmony_ci	addik   r1, r1, 40
2478c2ecf20Sopenharmony_ci	/* return back */
2488c2ecf20Sopenharmony_ci	rtsd	r15, 8
2498c2ecf20Sopenharmony_ci	nop
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci.align 4 /* Alignment is important to keep icache happy */
2528c2ecf20Sopenharmony_cibu1:	lbu	r4,r6,r3
2538c2ecf20Sopenharmony_cibu2:	sb	r4,r5,r3
2548c2ecf20Sopenharmony_ci	addik	r7,r7,-1
2558c2ecf20Sopenharmony_ci	bneid	r7,bu1
2568c2ecf20Sopenharmony_ci	addik	r3,r3,1		/* delay slot */
2578c2ecf20Sopenharmony_ci0:
2588c2ecf20Sopenharmony_ci	addik	r3,r7,0
2598c2ecf20Sopenharmony_ci	rtsd	r15,8
2608c2ecf20Sopenharmony_ci	nop
2618c2ecf20Sopenharmony_ci	.size   __copy_tofrom_user, . - __copy_tofrom_user
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	.section	__ex_table,"a"
2648c2ecf20Sopenharmony_ci	.word	bu1, 0b;
2658c2ecf20Sopenharmony_ci	.word	bu2, 0b;
2668c2ecf20Sopenharmony_ci	.text
267