diff options
author | Greentime Hu <greentime@andestech.com> | 2017-10-25 02:27:22 -0400 |
---|---|---|
committer | Greentime Hu <greentime@andestech.com> | 2018-02-21 21:44:33 -0500 |
commit | ace02e2badd12e34d781780517614c42dbbf8068 (patch) | |
tree | dcc51bcd5f0af76c52ef6e2e7276bae881967af6 | |
parent | 7ecbac743a58ae2ed9851aa7ec81d4bf2ca2cffe (diff) |
nds32: Library functions
This patch add support for various library functions.
Signed-off-by: Vincent Chen <vincentc@andestech.com>
Signed-off-by: Greentime Hu <greentime@andestech.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
-rw-r--r-- | arch/nds32/include/asm/string.h | 17 | ||||
-rw-r--r-- | arch/nds32/include/asm/swab.h | 35 | ||||
-rw-r--r-- | arch/nds32/include/asm/uaccess.h | 283 | ||||
-rw-r--r-- | arch/nds32/kernel/nds32_ksyms.c | 31 | ||||
-rw-r--r-- | arch/nds32/lib/Makefile | 3 | ||||
-rw-r--r-- | arch/nds32/lib/clear_user.S | 42 | ||||
-rw-r--r-- | arch/nds32/lib/copy_from_user.S | 45 | ||||
-rw-r--r-- | arch/nds32/lib/copy_template.S | 69 | ||||
-rw-r--r-- | arch/nds32/lib/copy_to_user.S | 45 | ||||
-rw-r--r-- | arch/nds32/lib/memcpy.S | 30 | ||||
-rw-r--r-- | arch/nds32/lib/memmove.S | 70 | ||||
-rw-r--r-- | arch/nds32/lib/memset.S | 33 | ||||
-rw-r--r-- | arch/nds32/lib/memzero.S | 18 |
13 files changed, 721 insertions, 0 deletions
diff --git a/arch/nds32/include/asm/string.h b/arch/nds32/include/asm/string.h new file mode 100644 index 000000000000..179272caa540 --- /dev/null +++ b/arch/nds32/include/asm/string.h | |||
@@ -0,0 +1,17 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2005-2017 Andes Technology Corporation | ||
3 | |||
4 | #ifndef __ASM_NDS32_STRING_H | ||
5 | #define __ASM_NDS32_STRING_H | ||
6 | |||
7 | #define __HAVE_ARCH_MEMCPY | ||
8 | extern void *memcpy(void *, const void *, __kernel_size_t); | ||
9 | |||
10 | #define __HAVE_ARCH_MEMMOVE | ||
11 | extern void *memmove(void *, const void *, __kernel_size_t); | ||
12 | |||
13 | #define __HAVE_ARCH_MEMSET | ||
14 | extern void *memset(void *, int, __kernel_size_t); | ||
15 | |||
16 | extern void *memzero(void *ptr, __kernel_size_t n); | ||
17 | #endif | ||
diff --git a/arch/nds32/include/asm/swab.h b/arch/nds32/include/asm/swab.h new file mode 100644 index 000000000000..e01a755a37d2 --- /dev/null +++ b/arch/nds32/include/asm/swab.h | |||
@@ -0,0 +1,35 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2005-2017 Andes Technology Corporation | ||
3 | |||
4 | #ifndef __NDS32_SWAB_H__ | ||
5 | #define __NDS32_SWAB_H__ | ||
6 | |||
7 | #include <linux/types.h> | ||
8 | #include <linux/compiler.h> | ||
9 | |||
10 | static __inline__ __attribute_const__ __u32 ___arch__swab32(__u32 x) | ||
11 | { | ||
12 | __asm__("wsbh %0, %0\n\t" /* word swap byte within halfword */ | ||
13 | "rotri %0, %0, #16\n" | ||
14 | :"=r"(x) | ||
15 | :"0"(x)); | ||
16 | return x; | ||
17 | } | ||
18 | |||
19 | static __inline__ __attribute_const__ __u16 ___arch__swab16(__u16 x) | ||
20 | { | ||
21 | __asm__("wsbh %0, %0\n" /* word swap byte within halfword */ | ||
22 | :"=r"(x) | ||
23 | :"0"(x)); | ||
24 | return x; | ||
25 | } | ||
26 | |||
27 | #define __arch_swab32(x) ___arch__swab32(x) | ||
28 | #define __arch_swab16(x) ___arch__swab16(x) | ||
29 | |||
30 | #if !defined(__STRICT_ANSI__) || defined(__KERNEL__) | ||
31 | #define __BYTEORDER_HAS_U64__ | ||
32 | #define __SWAB_64_THRU_32__ | ||
33 | #endif | ||
34 | |||
35 | #endif /* __NDS32_SWAB_H__ */ | ||
diff --git a/arch/nds32/include/asm/uaccess.h b/arch/nds32/include/asm/uaccess.h new file mode 100644 index 000000000000..18a009f3804d --- /dev/null +++ b/arch/nds32/include/asm/uaccess.h | |||
@@ -0,0 +1,283 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2005-2017 Andes Technology Corporation | ||
3 | |||
4 | #ifndef _ASMANDES_UACCESS_H | ||
5 | #define _ASMANDES_UACCESS_H | ||
6 | |||
7 | /* | ||
8 | * User space memory access functions | ||
9 | */ | ||
10 | #include <linux/sched.h> | ||
11 | #include <asm/errno.h> | ||
12 | #include <asm/memory.h> | ||
13 | #include <asm/types.h> | ||
14 | #include <linux/mm.h> | ||
15 | |||
16 | #define VERIFY_READ 0 | ||
17 | #define VERIFY_WRITE 1 | ||
18 | |||
19 | #define __asmeq(x, y) ".ifnc " x "," y " ; .err ; .endif\n\t" | ||
20 | |||
21 | /* | ||
22 | * The exception table consists of pairs of addresses: the first is the | ||
23 | * address of an instruction that is allowed to fault, and the second is | ||
24 | * the address at which the program should continue. No registers are | ||
25 | * modified, so it is entirely up to the continuation code to figure out | ||
26 | * what to do. | ||
27 | * | ||
28 | * All the routines below use bits of fixup code that are out of line | ||
29 | * with the main instruction path. This means when everything is well, | ||
30 | * we don't even have to jump over them. Further, they do not intrude | ||
31 | * on our cache or tlb entries. | ||
32 | */ | ||
33 | |||
34 | struct exception_table_entry { | ||
35 | unsigned long insn, fixup; | ||
36 | }; | ||
37 | |||
38 | extern int fixup_exception(struct pt_regs *regs); | ||
39 | |||
40 | #define KERNEL_DS ((mm_segment_t) { ~0UL }) | ||
41 | #define USER_DS ((mm_segment_t) {TASK_SIZE - 1}) | ||
42 | |||
43 | #define get_ds() (KERNEL_DS) | ||
44 | #define get_fs() (current_thread_info()->addr_limit) | ||
45 | #define user_addr_max get_fs | ||
46 | |||
47 | static inline void set_fs(mm_segment_t fs) | ||
48 | { | ||
49 | current_thread_info()->addr_limit = fs; | ||
50 | } | ||
51 | |||
52 | #define segment_eq(a, b) ((a) == (b)) | ||
53 | |||
54 | #define __range_ok(addr, size) (size <= get_fs() && addr <= (get_fs() -size)) | ||
55 | |||
56 | #define access_ok(type, addr, size) \ | ||
57 | __range_ok((unsigned long)addr, (unsigned long)size) | ||
58 | /* | ||
59 | * Single-value transfer routines. They automatically use the right | ||
60 | * size if we just have the right pointer type. Note that the functions | ||
61 | * which read from user space (*get_*) need to take care not to leak | ||
62 | * kernel data even if the calling code is buggy and fails to check | ||
63 | * the return value. This means zeroing out the destination variable | ||
64 | * or buffer on error. Normally this is done out of line by the | ||
65 | * fixup code, but there are a few places where it intrudes on the | ||
66 | * main code path. When we only write to user space, there is no | ||
67 | * problem. | ||
68 | * | ||
69 | * The "__xxx" versions of the user access functions do not verify the | ||
70 | * address space - it must have been done previously with a separate | ||
71 | * "access_ok()" call. | ||
72 | * | ||
73 | * The "xxx_error" versions set the third argument to EFAULT if an | ||
74 | * error occurs, and leave it unchanged on success. Note that these | ||
75 | * versions are void (ie, don't return a value as such). | ||
76 | */ | ||
77 | |||
78 | #define get_user(x,p) \ | ||
79 | ({ \ | ||
80 | long __e = -EFAULT; \ | ||
81 | if(likely(access_ok(VERIFY_READ, p, sizeof(*p)))) { \ | ||
82 | __e = __get_user(x,p); \ | ||
83 | } else \ | ||
84 | x = 0; \ | ||
85 | __e; \ | ||
86 | }) | ||
87 | #define __get_user(x,ptr) \ | ||
88 | ({ \ | ||
89 | long __gu_err = 0; \ | ||
90 | __get_user_err((x),(ptr),__gu_err); \ | ||
91 | __gu_err; \ | ||
92 | }) | ||
93 | |||
94 | #define __get_user_error(x,ptr,err) \ | ||
95 | ({ \ | ||
96 | __get_user_err((x),(ptr),err); \ | ||
97 | (void) 0; \ | ||
98 | }) | ||
99 | |||
100 | #define __get_user_err(x,ptr,err) \ | ||
101 | do { \ | ||
102 | unsigned long __gu_addr = (unsigned long)(ptr); \ | ||
103 | unsigned long __gu_val; \ | ||
104 | __chk_user_ptr(ptr); \ | ||
105 | switch (sizeof(*(ptr))) { \ | ||
106 | case 1: \ | ||
107 | __get_user_asm("lbi",__gu_val,__gu_addr,err); \ | ||
108 | break; \ | ||
109 | case 2: \ | ||
110 | __get_user_asm("lhi",__gu_val,__gu_addr,err); \ | ||
111 | break; \ | ||
112 | case 4: \ | ||
113 | __get_user_asm("lwi",__gu_val,__gu_addr,err); \ | ||
114 | break; \ | ||
115 | case 8: \ | ||
116 | __get_user_asm_dword(__gu_val,__gu_addr,err); \ | ||
117 | break; \ | ||
118 | default: \ | ||
119 | BUILD_BUG(); \ | ||
120 | break; \ | ||
121 | } \ | ||
122 | (x) = (__typeof__(*(ptr)))__gu_val; \ | ||
123 | } while (0) | ||
124 | |||
125 | #define __get_user_asm(inst,x,addr,err) \ | ||
126 | asm volatile( \ | ||
127 | "1: "inst" %1,[%2]\n" \ | ||
128 | "2:\n" \ | ||
129 | " .section .fixup,\"ax\"\n" \ | ||
130 | " .align 2\n" \ | ||
131 | "3: move %0, %3\n" \ | ||
132 | " move %1, #0\n" \ | ||
133 | " b 2b\n" \ | ||
134 | " .previous\n" \ | ||
135 | " .section __ex_table,\"a\"\n" \ | ||
136 | " .align 3\n" \ | ||
137 | " .long 1b, 3b\n" \ | ||
138 | " .previous" \ | ||
139 | : "+r" (err), "=&r" (x) \ | ||
140 | : "r" (addr), "i" (-EFAULT) \ | ||
141 | : "cc") | ||
142 | |||
143 | #ifdef __NDS32_EB__ | ||
144 | #define __gu_reg_oper0 "%H1" | ||
145 | #define __gu_reg_oper1 "%L1" | ||
146 | #else | ||
147 | #define __gu_reg_oper0 "%L1" | ||
148 | #define __gu_reg_oper1 "%H1" | ||
149 | #endif | ||
150 | |||
151 | #define __get_user_asm_dword(x, addr, err) \ | ||
152 | asm volatile( \ | ||
153 | "\n1:\tlwi " __gu_reg_oper0 ",[%2]\n" \ | ||
154 | "\n2:\tlwi " __gu_reg_oper1 ",[%2+4]\n" \ | ||
155 | "3:\n" \ | ||
156 | " .section .fixup,\"ax\"\n" \ | ||
157 | " .align 2\n" \ | ||
158 | "4: move %0, %3\n" \ | ||
159 | " b 3b\n" \ | ||
160 | " .previous\n" \ | ||
161 | " .section __ex_table,\"a\"\n" \ | ||
162 | " .align 3\n" \ | ||
163 | " .long 1b, 4b\n" \ | ||
164 | " .long 2b, 4b\n" \ | ||
165 | " .previous" \ | ||
166 | : "+r"(err), "=&r"(x) \ | ||
167 | : "r"(addr), "i"(-EFAULT) \ | ||
168 | : "cc") | ||
169 | #define put_user(x,p) \ | ||
170 | ({ \ | ||
171 | long __e = -EFAULT; \ | ||
172 | if(likely(access_ok(VERIFY_WRITE, p, sizeof(*p)))) { \ | ||
173 | __e = __put_user(x,p); \ | ||
174 | } \ | ||
175 | __e; \ | ||
176 | }) | ||
177 | #define __put_user(x,ptr) \ | ||
178 | ({ \ | ||
179 | long __pu_err = 0; \ | ||
180 | __put_user_err((x),(ptr),__pu_err); \ | ||
181 | __pu_err; \ | ||
182 | }) | ||
183 | |||
184 | #define __put_user_error(x,ptr,err) \ | ||
185 | ({ \ | ||
186 | __put_user_err((x),(ptr),err); \ | ||
187 | (void) 0; \ | ||
188 | }) | ||
189 | |||
190 | #define __put_user_err(x,ptr,err) \ | ||
191 | do { \ | ||
192 | unsigned long __pu_addr = (unsigned long)(ptr); \ | ||
193 | __typeof__(*(ptr)) __pu_val = (x); \ | ||
194 | __chk_user_ptr(ptr); \ | ||
195 | switch (sizeof(*(ptr))) { \ | ||
196 | case 1: \ | ||
197 | __put_user_asm("sbi",__pu_val,__pu_addr,err); \ | ||
198 | break; \ | ||
199 | case 2: \ | ||
200 | __put_user_asm("shi",__pu_val,__pu_addr,err); \ | ||
201 | break; \ | ||
202 | case 4: \ | ||
203 | __put_user_asm("swi",__pu_val,__pu_addr,err); \ | ||
204 | break; \ | ||
205 | case 8: \ | ||
206 | __put_user_asm_dword(__pu_val,__pu_addr,err); \ | ||
207 | break; \ | ||
208 | default: \ | ||
209 | BUILD_BUG(); \ | ||
210 | break; \ | ||
211 | } \ | ||
212 | } while (0) | ||
213 | |||
214 | #define __put_user_asm(inst,x,addr,err) \ | ||
215 | asm volatile( \ | ||
216 | "1: "inst" %1,[%2]\n" \ | ||
217 | "2:\n" \ | ||
218 | " .section .fixup,\"ax\"\n" \ | ||
219 | " .align 2\n" \ | ||
220 | "3: move %0, %3\n" \ | ||
221 | " b 2b\n" \ | ||
222 | " .previous\n" \ | ||
223 | " .section __ex_table,\"a\"\n" \ | ||
224 | " .align 3\n" \ | ||
225 | " .long 1b, 3b\n" \ | ||
226 | " .previous" \ | ||
227 | : "+r" (err) \ | ||
228 | : "r" (x), "r" (addr), "i" (-EFAULT) \ | ||
229 | : "cc") | ||
230 | |||
231 | #ifdef __NDS32_EB__ | ||
232 | #define __pu_reg_oper0 "%H2" | ||
233 | #define __pu_reg_oper1 "%L2" | ||
234 | #else | ||
235 | #define __pu_reg_oper0 "%L2" | ||
236 | #define __pu_reg_oper1 "%H2" | ||
237 | #endif | ||
238 | |||
239 | #define __put_user_asm_dword(x, addr, err) \ | ||
240 | asm volatile( \ | ||
241 | "\n1:\tswi " __pu_reg_oper0 ",[%1]\n" \ | ||
242 | "\n2:\tswi " __pu_reg_oper1 ",[%1+4]\n" \ | ||
243 | "3:\n" \ | ||
244 | " .section .fixup,\"ax\"\n" \ | ||
245 | " .align 2\n" \ | ||
246 | "4: move %0, %3\n" \ | ||
247 | " b 3b\n" \ | ||
248 | " .previous\n" \ | ||
249 | " .section __ex_table,\"a\"\n" \ | ||
250 | " .align 3\n" \ | ||
251 | " .long 1b, 4b\n" \ | ||
252 | " .long 2b, 4b\n" \ | ||
253 | " .previous" \ | ||
254 | : "+r"(err) \ | ||
255 | : "r"(addr), "r"(x), "i"(-EFAULT) \ | ||
256 | : "cc") | ||
257 | extern unsigned long __arch_clear_user(void __user * addr, unsigned long n); | ||
258 | extern long strncpy_from_user(char *dest, const char __user * src, long count); | ||
259 | extern __must_check long strlen_user(const char __user * str); | ||
260 | extern __must_check long strnlen_user(const char __user * str, long n); | ||
261 | extern unsigned long __arch_copy_from_user(void *to, const void __user * from, | ||
262 | unsigned long n); | ||
263 | extern unsigned long __arch_copy_to_user(void __user * to, const void *from, | ||
264 | unsigned long n); | ||
265 | |||
266 | #define raw_copy_from_user __arch_copy_from_user | ||
267 | #define raw_copy_to_user __arch_copy_to_user | ||
268 | |||
269 | #define INLINE_COPY_FROM_USER | ||
270 | #define INLINE_COPY_TO_USER | ||
271 | static inline unsigned long clear_user(void __user * to, unsigned long n) | ||
272 | { | ||
273 | if (access_ok(VERIFY_WRITE, to, n)) | ||
274 | n = __arch_clear_user(to, n); | ||
275 | return n; | ||
276 | } | ||
277 | |||
278 | static inline unsigned long __clear_user(void __user * to, unsigned long n) | ||
279 | { | ||
280 | return __arch_clear_user(to, n); | ||
281 | } | ||
282 | |||
283 | #endif /* _ASMNDS32_UACCESS_H */ | ||
diff --git a/arch/nds32/kernel/nds32_ksyms.c b/arch/nds32/kernel/nds32_ksyms.c new file mode 100644 index 000000000000..5ecebd0e60cb --- /dev/null +++ b/arch/nds32/kernel/nds32_ksyms.c | |||
@@ -0,0 +1,31 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2005-2017 Andes Technology Corporation | ||
3 | |||
4 | #include <linux/module.h> | ||
5 | #include <linux/string.h> | ||
6 | #include <linux/delay.h> | ||
7 | #include <linux/in6.h> | ||
8 | #include <linux/syscalls.h> | ||
9 | #include <linux/uaccess.h> | ||
10 | |||
11 | #include <asm/checksum.h> | ||
12 | #include <asm/io.h> | ||
13 | #include <asm/ftrace.h> | ||
14 | #include <asm/proc-fns.h> | ||
15 | |||
16 | /* mem functions */ | ||
17 | EXPORT_SYMBOL(memset); | ||
18 | EXPORT_SYMBOL(memcpy); | ||
19 | EXPORT_SYMBOL(memmove); | ||
20 | EXPORT_SYMBOL(memzero); | ||
21 | |||
22 | /* user mem (segment) */ | ||
23 | EXPORT_SYMBOL(__arch_copy_from_user); | ||
24 | EXPORT_SYMBOL(__arch_copy_to_user); | ||
25 | EXPORT_SYMBOL(__arch_clear_user); | ||
26 | |||
27 | /* cache handling */ | ||
28 | EXPORT_SYMBOL(cpu_icache_inval_all); | ||
29 | EXPORT_SYMBOL(cpu_dcache_wbinval_all); | ||
30 | EXPORT_SYMBOL(cpu_dma_inval_range); | ||
31 | EXPORT_SYMBOL(cpu_dma_wb_range); | ||
diff --git a/arch/nds32/lib/Makefile b/arch/nds32/lib/Makefile new file mode 100644 index 000000000000..0f9840103f03 --- /dev/null +++ b/arch/nds32/lib/Makefile | |||
@@ -0,0 +1,3 @@ | |||
1 | lib-y := copy_page.o memcpy.o memmove.o \ | ||
2 | memset.o memzero.o \ | ||
3 | copy_from_user.o copy_to_user.o clear_user.o | ||
diff --git a/arch/nds32/lib/clear_user.S b/arch/nds32/lib/clear_user.S new file mode 100644 index 000000000000..805dfcd25bf8 --- /dev/null +++ b/arch/nds32/lib/clear_user.S | |||
@@ -0,0 +1,42 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2005-2017 Andes Technology Corporation | ||
3 | |||
4 | #include <linux/linkage.h> | ||
5 | #include <asm/assembler.h> | ||
6 | #include <asm/errno.h> | ||
7 | |||
8 | /* Prototype: int __arch_clear_user(void *addr, size_t sz) | ||
9 | * Purpose : clear some user memory | ||
10 | * Params : addr - user memory address to clear | ||
11 | * : sz - number of bytes to clear | ||
12 | * Returns : number of bytes NOT cleared | ||
13 | */ | ||
14 | .text | ||
15 | .align 5 | ||
16 | ENTRY(__arch_clear_user) | ||
17 | add $r5, $r0, $r1 | ||
18 | beqz $r1, clear_exit | ||
19 | xor $p1, $p1, $p1 ! Use $p1=0 to clear mem | ||
20 | srli $p0, $r1, #2 ! $p0 = number of word to clear | ||
21 | andi $r1, $r1, #3 ! Bytes less than a word to copy | ||
22 | beqz $p0, byte_clear ! Only less than a word to clear | ||
23 | word_clear: | ||
24 | USER( smw.bim,$p1, [$r0], $p1) ! Clear the word | ||
25 | addi $p0, $p0, #-1 ! Decrease word count | ||
26 | bnez $p0, word_clear ! Continue looping to clear all words | ||
27 | beqz $r1, clear_exit ! No left bytes to copy | ||
28 | byte_clear: | ||
29 | USER( sbi.bi, $p1, [$r0], #1) ! Clear the byte | ||
30 | addi $r1, $r1, #-1 ! Decrease byte count | ||
31 | bnez $r1, byte_clear ! Continue looping to clear all left bytes | ||
32 | clear_exit: | ||
33 | move $r0, $r1 ! Set return value | ||
34 | ret | ||
35 | |||
36 | .section .fixup,"ax" | ||
37 | .align 0 | ||
38 | 9001: | ||
39 | sub $r0, $r5, $r0 ! Bytes left to copy | ||
40 | ret | ||
41 | .previous | ||
42 | ENDPROC(__arch_clear_user) | ||
diff --git a/arch/nds32/lib/copy_from_user.S b/arch/nds32/lib/copy_from_user.S new file mode 100644 index 000000000000..ad1857b20067 --- /dev/null +++ b/arch/nds32/lib/copy_from_user.S | |||
@@ -0,0 +1,45 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2005-2017 Andes Technology Corporation | ||
3 | |||
4 | #include <linux/linkage.h> | ||
5 | #include <asm/assembler.h> | ||
6 | #include <asm/errno.h> | ||
7 | |||
8 | .macro lbi1 dst, addr, adj | ||
9 | USER( lbi.bi, \dst, [\addr], \adj) | ||
10 | .endm | ||
11 | |||
12 | .macro sbi1 src, addr, adj | ||
13 | sbi.bi \src, [\addr], \adj | ||
14 | .endm | ||
15 | |||
16 | .macro lmw1 start_reg, addr, end_reg | ||
17 | USER( lmw.bim, \start_reg, [\addr], \end_reg) | ||
18 | .endm | ||
19 | |||
20 | .macro smw1 start_reg, addr, end_reg | ||
21 | smw.bim \start_reg, [\addr], \end_reg | ||
22 | .endm | ||
23 | |||
24 | |||
25 | /* Prototype: int __arch_copy_from_user(void *to, const char *from, size_t n) | ||
26 | * Purpose : copy a block from user memory to kernel memory | ||
27 | * Params : to - kernel memory | ||
28 | * : from - user memory | ||
29 | * : n - number of bytes to copy | ||
30 | * Returns : Number of bytes NOT copied. | ||
31 | */ | ||
32 | |||
33 | .text | ||
34 | ENTRY(__arch_copy_from_user) | ||
35 | add $r5, $r0, $r2 | ||
36 | #include "copy_template.S" | ||
37 | move $r0, $r2 | ||
38 | ret | ||
39 | .section .fixup,"ax" | ||
40 | .align 2 | ||
41 | 9001: | ||
42 | sub $r0, $r5, $r0 | ||
43 | ret | ||
44 | .previous | ||
45 | ENDPROC(__arch_copy_from_user) | ||
diff --git a/arch/nds32/lib/copy_template.S b/arch/nds32/lib/copy_template.S new file mode 100644 index 000000000000..3a9a2de468c2 --- /dev/null +++ b/arch/nds32/lib/copy_template.S | |||
@@ -0,0 +1,69 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2005-2017 Andes Technology Corporation | ||
3 | |||
4 | |||
5 | beq $r1, $r0, quit_memcpy | ||
6 | beqz $r2, quit_memcpy | ||
7 | srli $r3, $r2, #5 ! check if len < cache-line size 32 | ||
8 | beqz $r3, word_copy_entry | ||
9 | andi $r4, $r0, #0x3 ! check byte-align | ||
10 | beqz $r4, unalign_word_copy_entry | ||
11 | |||
12 | addi $r4, $r4,#-4 | ||
13 | abs $r4, $r4 ! check how many un-align byte to copy | ||
14 | sub $r2, $r2, $r4 ! update $R2 | ||
15 | |||
16 | unalign_byte_copy: | ||
17 | lbi1 $r3, $r1, #1 | ||
18 | addi $r4, $r4, #-1 | ||
19 | sbi1 $r3, $r0, #1 | ||
20 | bnez $r4, unalign_byte_copy | ||
21 | beqz $r2, quit_memcpy | ||
22 | |||
23 | unalign_word_copy_entry: | ||
24 | andi $r3, $r0, 0x1f ! check cache-line unaligncount | ||
25 | beqz $r3, cache_copy | ||
26 | |||
27 | addi $r3, $r3, #-32 | ||
28 | abs $r3, $r3 | ||
29 | sub $r2, $r2, $r3 ! update $R2 | ||
30 | |||
31 | unalign_word_copy: | ||
32 | lmw1 $r4, $r1, $r4 | ||
33 | addi $r3, $r3, #-4 | ||
34 | smw1 $r4, $r0, $r4 | ||
35 | bnez $r3, unalign_word_copy | ||
36 | beqz $r2, quit_memcpy | ||
37 | |||
38 | addi $r3, $r2, #-32 ! to check $r2< cache_line , than go to word_copy | ||
39 | bltz $r3, word_copy_entry | ||
40 | cache_copy: | ||
41 | srli $r3, $r2, #5 | ||
42 | beqz $r3, word_copy_entry | ||
43 | 3: | ||
44 | lmw1 $r17, $r1, $r24 | ||
45 | addi $r3, $r3, #-1 | ||
46 | smw1 $r17, $r0, $r24 | ||
47 | bnez $r3, 3b | ||
48 | |||
49 | word_copy_entry: | ||
50 | andi $r2, $r2, #31 | ||
51 | |||
52 | beqz $r2, quit_memcpy | ||
53 | 5: | ||
54 | srli $r3, $r2, #2 | ||
55 | beqz $r3, byte_copy | ||
56 | word_copy: | ||
57 | lmw1 $r4, $r1, $r4 | ||
58 | addi $r3, $r3, #-1 | ||
59 | smw1 $r4, $r0, $r4 | ||
60 | bnez $r3, word_copy | ||
61 | andi $r2, $r2, #3 | ||
62 | beqz $r2, quit_memcpy | ||
63 | byte_copy: | ||
64 | lbi1 $r3, $r1, #1 | ||
65 | addi $r2, $r2, #-1 | ||
66 | |||
67 | sbi1 $r3, $r0, #1 | ||
68 | bnez $r2, byte_copy | ||
69 | quit_memcpy: | ||
diff --git a/arch/nds32/lib/copy_to_user.S b/arch/nds32/lib/copy_to_user.S new file mode 100644 index 000000000000..3230044dcfb8 --- /dev/null +++ b/arch/nds32/lib/copy_to_user.S | |||
@@ -0,0 +1,45 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2005-2017 Andes Technology Corporation | ||
3 | |||
4 | #include <linux/linkage.h> | ||
5 | #include <asm/assembler.h> | ||
6 | #include <asm/errno.h> | ||
7 | |||
8 | .macro lbi1 dst, addr, adj | ||
9 | lbi.bi \dst, [\addr], \adj | ||
10 | .endm | ||
11 | |||
12 | .macro sbi1 src, addr, adj | ||
13 | USER( sbi.bi, \src, [\addr], \adj) | ||
14 | .endm | ||
15 | |||
16 | .macro lmw1 start_reg, addr, end_reg | ||
17 | lmw.bim \start_reg, [\addr], \end_reg | ||
18 | .endm | ||
19 | |||
20 | .macro smw1 start_reg, addr, end_reg | ||
21 | USER( smw.bim, \start_reg, [\addr], \end_reg) | ||
22 | .endm | ||
23 | |||
24 | |||
25 | /* Prototype: int __arch_copy_to_user(void *to, const char *from, size_t n) | ||
26 | * Purpose : copy a block to user memory from kernel memory | ||
27 | * Params : to - user memory | ||
28 | * : from - kernel memory | ||
29 | * : n - number of bytes to copy | ||
30 | * Returns : Number of bytes NOT copied. | ||
31 | */ | ||
32 | |||
33 | .text | ||
34 | ENTRY(__arch_copy_to_user) | ||
35 | add $r5, $r0, $r2 | ||
36 | #include "copy_template.S" | ||
37 | move $r0, $r2 | ||
38 | ret | ||
39 | .section .fixup,"ax" | ||
40 | .align 2 | ||
41 | 9001: | ||
42 | sub $r0, $r5, $r0 | ||
43 | ret | ||
44 | .previous | ||
45 | ENDPROC(__arch_copy_to_user) | ||
diff --git a/arch/nds32/lib/memcpy.S b/arch/nds32/lib/memcpy.S new file mode 100644 index 000000000000..a2345ea721e4 --- /dev/null +++ b/arch/nds32/lib/memcpy.S | |||
@@ -0,0 +1,30 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2005-2017 Andes Technology Corporation | ||
3 | |||
4 | #include <linux/linkage.h> | ||
5 | |||
6 | |||
7 | .macro lbi1 dst, addr, adj | ||
8 | lbi.bi \dst, [\addr], \adj | ||
9 | .endm | ||
10 | |||
11 | .macro sbi1 src, addr, adj | ||
12 | sbi.bi \src, [\addr], \adj | ||
13 | .endm | ||
14 | |||
15 | .macro lmw1 start_reg, addr, end_reg | ||
16 | lmw.bim \start_reg, [\addr], \end_reg | ||
17 | .endm | ||
18 | |||
19 | .macro smw1 start_reg, addr, end_reg | ||
20 | smw.bim \start_reg, [\addr], \end_reg | ||
21 | .endm | ||
22 | |||
23 | .text | ||
24 | ENTRY(memcpy) | ||
25 | move $r5, $r0 | ||
26 | #include "copy_template.S" | ||
27 | move $r0, $r5 | ||
28 | ret | ||
29 | |||
30 | ENDPROC(memcpy) | ||
diff --git a/arch/nds32/lib/memmove.S b/arch/nds32/lib/memmove.S new file mode 100644 index 000000000000..c823aada2271 --- /dev/null +++ b/arch/nds32/lib/memmove.S | |||
@@ -0,0 +1,70 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2005-2017 Andes Technology Corporation | ||
3 | |||
4 | #include <linux/linkage.h> | ||
5 | |||
6 | /* | ||
7 | void *memmove(void *dst, const void *src, int n); | ||
8 | |||
9 | dst: $r0 | ||
10 | src: $r1 | ||
11 | n : $r2 | ||
12 | ret: $r0 - pointer to the memory area dst. | ||
13 | */ | ||
14 | .text | ||
15 | |||
16 | ENTRY(memmove) | ||
17 | move $r5, $r0 ! Set return value = det | ||
18 | beq $r0, $r1, exit_memcpy ! Exit when det = src | ||
19 | beqz $r2, exit_memcpy ! Exit when n = 0 | ||
20 | pushm $t0, $t1 ! Save reg | ||
21 | srli $p1, $r2, #2 ! $p1 is how many words to copy | ||
22 | |||
23 | ! Avoid data lost when memory overlap | ||
24 | ! Copy data reversely when src < dst | ||
25 | slt $p0, $r0, $r1 ! check if $r0 < $r1 | ||
26 | beqz $p0, do_reverse ! branch if dst > src | ||
27 | |||
28 | ! No reverse, dst < src | ||
29 | andi $r2, $r2, #3 ! How many bytes are less than a word | ||
30 | li $t0, #1 ! Determining copy direction in byte_cpy | ||
31 | beqz $p1, byte_cpy ! When n is less than a word | ||
32 | |||
33 | word_cpy: | ||
34 | lmw.bim $p0, [$r1], $p0 ! Read a word from src | ||
35 | addi $p1, $p1, #-1 ! How many words left to copy | ||
36 | smw.bim $p0, [$r0], $p0 ! Copy the word to det | ||
37 | bnez $p1, word_cpy ! If remained words > 0 | ||
38 | beqz $r2, end_memcpy ! No left bytes to copy | ||
39 | b byte_cpy | ||
40 | |||
41 | do_reverse: | ||
42 | add $r0, $r0, $r2 ! Start with the end of $r0 | ||
43 | add $r1, $r1, $r2 ! Start with the end of $r1 | ||
44 | andi $r2, $r2, #3 ! How many bytes are less than a word | ||
45 | li $t0, #-1 ! Determining copy direction in byte_cpy | ||
46 | beqz $p1, reverse_byte_cpy ! When n is less than a word | ||
47 | |||
48 | reverse_word_cpy: | ||
49 | lmw.adm $p0, [$r1], $p0 ! Read a word from src | ||
50 | addi $p1, $p1, #-1 ! How many words left to copy | ||
51 | smw.adm $p0, [$r0], $p0 ! Copy the word to det | ||
52 | bnez $p1, reverse_word_cpy ! If remained words > 0 | ||
53 | beqz $r2, end_memcpy ! No left bytes to copy | ||
54 | |||
55 | reverse_byte_cpy: | ||
56 | addi $r0, $r0, #-1 | ||
57 | addi $r1, $r1, #-1 | ||
58 | byte_cpy: ! Less than 4 bytes to copy now | ||
59 | lb.bi $p0, [$r1], $t0 ! Read a byte from src | ||
60 | addi $r2, $r2, #-1 ! How many bytes left to copy | ||
61 | sb.bi $p0, [$r0], $t0 ! copy the byte to det | ||
62 | bnez $r2, byte_cpy ! If remained bytes > 0 | ||
63 | |||
64 | end_memcpy: | ||
65 | popm $t0, $t1 | ||
66 | exit_memcpy: | ||
67 | move $r0, $r5 | ||
68 | ret | ||
69 | |||
70 | ENDPROC(memmove) | ||
diff --git a/arch/nds32/lib/memset.S b/arch/nds32/lib/memset.S new file mode 100644 index 000000000000..193cb6ce21a9 --- /dev/null +++ b/arch/nds32/lib/memset.S | |||
@@ -0,0 +1,33 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2005-2017 Andes Technology Corporation | ||
3 | |||
4 | #include <linux/linkage.h> | ||
5 | |||
6 | .text | ||
7 | ENTRY(memset) | ||
8 | move $r5, $r0 ! Return value | ||
9 | beqz $r2, end_memset ! Exit when len = 0 | ||
10 | srli $p1, $r2, 2 ! $p1 is how many words to copy | ||
11 | andi $r2, $r2, 3 ! How many bytes are less than a word | ||
12 | beqz $p1, byte_set ! When n is less than a word | ||
13 | |||
14 | ! set $r1 from ??????ab to abababab | ||
15 | andi $r1, $r1, #0x00ff ! $r1 = 000000ab | ||
16 | slli $p0, $r1, #8 ! $p0 = 0000ab00 | ||
17 | or $r1, $r1, $p0 ! $r1 = 0000abab | ||
18 | slli $p0, $r1, #16 ! $p0 = abab0000 | ||
19 | or $r1, $r1, $p0 ! $r1 = abababab | ||
20 | word_set: | ||
21 | addi $p1, $p1, #-1 ! How many words left to copy | ||
22 | smw.bim $r1, [$r0], $r1 ! Copy the word to det | ||
23 | bnez $p1, word_set ! Still words to set, continue looping | ||
24 | beqz $r2, end_memset ! No left byte to set | ||
25 | byte_set: ! Less than 4 bytes left to set | ||
26 | addi $r2, $r2, #-1 ! Decrease len by 1 | ||
27 | sbi.bi $r1, [$r0], #1 ! Set data of the next byte to $r1 | ||
28 | bnez $r2, byte_set ! Still bytes left to set | ||
29 | end_memset: | ||
30 | move $r0, $r5 | ||
31 | ret | ||
32 | |||
33 | ENDPROC(memset) | ||
diff --git a/arch/nds32/lib/memzero.S b/arch/nds32/lib/memzero.S new file mode 100644 index 000000000000..f055972c9343 --- /dev/null +++ b/arch/nds32/lib/memzero.S | |||
@@ -0,0 +1,18 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2005-2017 Andes Technology Corporation | ||
3 | |||
4 | #include <linux/linkage.h> | ||
5 | |||
6 | .text | ||
7 | ENTRY(memzero) | ||
8 | beqz $r1, 1f | ||
9 | push $lp | ||
10 | move $r2, $r1 | ||
11 | move $r1, #0 | ||
12 | push $r0 | ||
13 | bal memset | ||
14 | pop $r0 | ||
15 | pop $lp | ||
16 | 1: | ||
17 | ret | ||
18 | ENDPROC(memzero) | ||