diff options
author | 2025-03-08 22:04:20 +0800 | |
---|---|---|
committer | 2025-03-08 22:04:20 +0800 | |
commit | a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a (patch) | |
tree | 84f21bd0bf7071bc5fc7dd989e77d7ceb5476682 /arch/mips/lib | |
download | ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.tar.gz ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.zip |
Initial commit: OpenHarmony-v4.0-ReleaseOpenHarmony-v4.0-Release
Diffstat (limited to 'arch/mips/lib')
-rw-r--r-- | arch/mips/lib/Makefile | 19 | ||||
-rw-r--r-- | arch/mips/lib/bitops.c | 148 | ||||
-rw-r--r-- | arch/mips/lib/bswapdi.c | 17 | ||||
-rw-r--r-- | arch/mips/lib/bswapsi.c | 13 | ||||
-rw-r--r-- | arch/mips/lib/csum_partial.S | 754 | ||||
-rw-r--r-- | arch/mips/lib/delay.c | 69 | ||||
-rw-r--r-- | arch/mips/lib/dump_tlb.c | 199 | ||||
-rw-r--r-- | arch/mips/lib/iomap-pci.c | 46 | ||||
-rw-r--r-- | arch/mips/lib/iomap_copy.c | 29 | ||||
-rw-r--r-- | arch/mips/lib/libgcc.h | 43 | ||||
-rw-r--r-- | arch/mips/lib/memcpy.S | 709 | ||||
-rw-r--r-- | arch/mips/lib/memset.S | 328 | ||||
-rw-r--r-- | arch/mips/lib/mips-atomic.c | 113 | ||||
-rw-r--r-- | arch/mips/lib/multi3.c | 54 | ||||
-rw-r--r-- | arch/mips/lib/r3k_dump_tlb.c | 75 | ||||
-rw-r--r-- | arch/mips/lib/strncpy_user.S | 85 | ||||
-rw-r--r-- | arch/mips/lib/strnlen_user.S | 84 | ||||
-rw-r--r-- | arch/mips/lib/uncached.c | 82 |
18 files changed, 2867 insertions, 0 deletions
diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile new file mode 100644 index 000000000..479f50559 --- /dev/null +++ b/arch/mips/lib/Makefile | |||
@@ -0,0 +1,19 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0 | ||
2 | # | ||
3 | # Makefile for MIPS-specific library files.. | ||
4 | # | ||
5 | |||
6 | lib-y += bitops.o csum_partial.o delay.o memcpy.o memset.o \ | ||
7 | mips-atomic.o strncpy_user.o \ | ||
8 | strnlen_user.o uncached.o | ||
9 | |||
10 | obj-y += iomap_copy.o | ||
11 | obj-$(CONFIG_PCI) += iomap-pci.o | ||
12 | lib-$(CONFIG_GENERIC_CSUM) := $(filter-out csum_partial.o, $(lib-y)) | ||
13 | |||
14 | obj-$(CONFIG_CPU_GENERIC_DUMP_TLB) += dump_tlb.o | ||
15 | obj-$(CONFIG_CPU_R3000) += r3k_dump_tlb.o | ||
16 | obj-$(CONFIG_CPU_TX39XX) += r3k_dump_tlb.o | ||
17 | |||
18 | # libgcc-style stuff needed in the kernel | ||
19 | obj-y += bswapsi.o bswapdi.o multi3.o | ||
diff --git a/arch/mips/lib/bitops.c b/arch/mips/lib/bitops.c new file mode 100644 index 000000000..116d0bd8b --- /dev/null +++ b/arch/mips/lib/bitops.c | |||
@@ -0,0 +1,148 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (c) 1994-1997, 99, 2000, 06, 07 Ralf Baechle (ralf@linux-mips.org) | ||
7 | * Copyright (c) 1999, 2000 Silicon Graphics, Inc. | ||
8 | */ | ||
9 | #include <linux/bitops.h> | ||
10 | #include <linux/bits.h> | ||
11 | #include <linux/irqflags.h> | ||
12 | #include <linux/export.h> | ||
13 | |||
14 | |||
15 | /** | ||
16 | * __mips_set_bit - Atomically set a bit in memory. This is called by | ||
17 | * set_bit() if it cannot find a faster solution. | ||
18 | * @nr: the bit to set | ||
19 | * @addr: the address to start counting from | ||
20 | */ | ||
21 | void __mips_set_bit(unsigned long nr, volatile unsigned long *addr) | ||
22 | { | ||
23 | volatile unsigned long *a = &addr[BIT_WORD(nr)]; | ||
24 | unsigned int bit = nr % BITS_PER_LONG; | ||
25 | unsigned long mask; | ||
26 | unsigned long flags; | ||
27 | |||
28 | mask = 1UL << bit; | ||
29 | raw_local_irq_save(flags); | ||
30 | *a |= mask; | ||
31 | raw_local_irq_restore(flags); | ||
32 | } | ||
33 | EXPORT_SYMBOL(__mips_set_bit); | ||
34 | |||
35 | |||
36 | /** | ||
37 | * __mips_clear_bit - Clears a bit in memory. This is called by clear_bit() if | ||
38 | * it cannot find a faster solution. | ||
39 | * @nr: Bit to clear | ||
40 | * @addr: Address to start counting from | ||
41 | */ | ||
42 | void __mips_clear_bit(unsigned long nr, volatile unsigned long *addr) | ||
43 | { | ||
44 | volatile unsigned long *a = &addr[BIT_WORD(nr)]; | ||
45 | unsigned int bit = nr % BITS_PER_LONG; | ||
46 | unsigned long mask; | ||
47 | unsigned long flags; | ||
48 | |||
49 | mask = 1UL << bit; | ||
50 | raw_local_irq_save(flags); | ||
51 | *a &= ~mask; | ||
52 | raw_local_irq_restore(flags); | ||
53 | } | ||
54 | EXPORT_SYMBOL(__mips_clear_bit); | ||
55 | |||
56 | |||
57 | /** | ||
58 | * __mips_change_bit - Toggle a bit in memory. This is called by change_bit() | ||
59 | * if it cannot find a faster solution. | ||
60 | * @nr: Bit to change | ||
61 | * @addr: Address to start counting from | ||
62 | */ | ||
63 | void __mips_change_bit(unsigned long nr, volatile unsigned long *addr) | ||
64 | { | ||
65 | volatile unsigned long *a = &addr[BIT_WORD(nr)]; | ||
66 | unsigned int bit = nr % BITS_PER_LONG; | ||
67 | unsigned long mask; | ||
68 | unsigned long flags; | ||
69 | |||
70 | mask = 1UL << bit; | ||
71 | raw_local_irq_save(flags); | ||
72 | *a ^= mask; | ||
73 | raw_local_irq_restore(flags); | ||
74 | } | ||
75 | EXPORT_SYMBOL(__mips_change_bit); | ||
76 | |||
77 | |||
78 | /** | ||
79 | * __mips_test_and_set_bit_lock - Set a bit and return its old value. This is | ||
80 | * called by test_and_set_bit_lock() if it cannot find a faster solution. | ||
81 | * @nr: Bit to set | ||
82 | * @addr: Address to count from | ||
83 | */ | ||
84 | int __mips_test_and_set_bit_lock(unsigned long nr, | ||
85 | volatile unsigned long *addr) | ||
86 | { | ||
87 | volatile unsigned long *a = &addr[BIT_WORD(nr)]; | ||
88 | unsigned int bit = nr % BITS_PER_LONG; | ||
89 | unsigned long mask; | ||
90 | unsigned long flags; | ||
91 | int res; | ||
92 | |||
93 | mask = 1UL << bit; | ||
94 | raw_local_irq_save(flags); | ||
95 | res = (mask & *a) != 0; | ||
96 | *a |= mask; | ||
97 | raw_local_irq_restore(flags); | ||
98 | return res; | ||
99 | } | ||
100 | EXPORT_SYMBOL(__mips_test_and_set_bit_lock); | ||
101 | |||
102 | |||
103 | /** | ||
104 | * __mips_test_and_clear_bit - Clear a bit and return its old value. This is | ||
105 | * called by test_and_clear_bit() if it cannot find a faster solution. | ||
106 | * @nr: Bit to clear | ||
107 | * @addr: Address to count from | ||
108 | */ | ||
109 | int __mips_test_and_clear_bit(unsigned long nr, volatile unsigned long *addr) | ||
110 | { | ||
111 | volatile unsigned long *a = &addr[BIT_WORD(nr)]; | ||
112 | unsigned int bit = nr % BITS_PER_LONG; | ||
113 | unsigned long mask; | ||
114 | unsigned long flags; | ||
115 | int res; | ||
116 | |||
117 | mask = 1UL << bit; | ||
118 | raw_local_irq_save(flags); | ||
119 | res = (mask & *a) != 0; | ||
120 | *a &= ~mask; | ||
121 | raw_local_irq_restore(flags); | ||
122 | return res; | ||
123 | } | ||
124 | EXPORT_SYMBOL(__mips_test_and_clear_bit); | ||
125 | |||
126 | |||
127 | /** | ||
128 | * __mips_test_and_change_bit - Change a bit and return its old value. This is | ||
129 | * called by test_and_change_bit() if it cannot find a faster solution. | ||
130 | * @nr: Bit to change | ||
131 | * @addr: Address to count from | ||
132 | */ | ||
133 | int __mips_test_and_change_bit(unsigned long nr, volatile unsigned long *addr) | ||
134 | { | ||
135 | volatile unsigned long *a = &addr[BIT_WORD(nr)]; | ||
136 | unsigned int bit = nr % BITS_PER_LONG; | ||
137 | unsigned long mask; | ||
138 | unsigned long flags; | ||
139 | int res; | ||
140 | |||
141 | mask = 1UL << bit; | ||
142 | raw_local_irq_save(flags); | ||
143 | res = (mask & *a) != 0; | ||
144 | *a ^= mask; | ||
145 | raw_local_irq_restore(flags); | ||
146 | return res; | ||
147 | } | ||
148 | EXPORT_SYMBOL(__mips_test_and_change_bit); | ||
diff --git a/arch/mips/lib/bswapdi.c b/arch/mips/lib/bswapdi.c new file mode 100644 index 000000000..fcef74084 --- /dev/null +++ b/arch/mips/lib/bswapdi.c | |||
@@ -0,0 +1,17 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | #include <linux/export.h> | ||
3 | #include <linux/compiler.h> | ||
4 | |||
5 | unsigned long long notrace __bswapdi2(unsigned long long u) | ||
6 | { | ||
7 | return (((u) & 0xff00000000000000ull) >> 56) | | ||
8 | (((u) & 0x00ff000000000000ull) >> 40) | | ||
9 | (((u) & 0x0000ff0000000000ull) >> 24) | | ||
10 | (((u) & 0x000000ff00000000ull) >> 8) | | ||
11 | (((u) & 0x00000000ff000000ull) << 8) | | ||
12 | (((u) & 0x0000000000ff0000ull) << 24) | | ||
13 | (((u) & 0x000000000000ff00ull) << 40) | | ||
14 | (((u) & 0x00000000000000ffull) << 56); | ||
15 | } | ||
16 | |||
17 | EXPORT_SYMBOL(__bswapdi2); | ||
diff --git a/arch/mips/lib/bswapsi.c b/arch/mips/lib/bswapsi.c new file mode 100644 index 000000000..22d8e4f6d --- /dev/null +++ b/arch/mips/lib/bswapsi.c | |||
@@ -0,0 +1,13 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | #include <linux/export.h> | ||
3 | #include <linux/compiler.h> | ||
4 | |||
5 | unsigned int notrace __bswapsi2(unsigned int u) | ||
6 | { | ||
7 | return (((u) & 0xff000000) >> 24) | | ||
8 | (((u) & 0x00ff0000) >> 8) | | ||
9 | (((u) & 0x0000ff00) << 8) | | ||
10 | (((u) & 0x000000ff) << 24); | ||
11 | } | ||
12 | |||
13 | EXPORT_SYMBOL(__bswapsi2); | ||
diff --git a/arch/mips/lib/csum_partial.S b/arch/mips/lib/csum_partial.S new file mode 100644 index 000000000..a46db0807 --- /dev/null +++ b/arch/mips/lib/csum_partial.S | |||
@@ -0,0 +1,754 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Quick'n'dirty IP checksum ... | ||
7 | * | ||
8 | * Copyright (C) 1998, 1999 Ralf Baechle | ||
9 | * Copyright (C) 1999 Silicon Graphics, Inc. | ||
10 | * Copyright (C) 2007 Maciej W. Rozycki | ||
11 | * Copyright (C) 2014 Imagination Technologies Ltd. | ||
12 | */ | ||
13 | #include <linux/errno.h> | ||
14 | #include <asm/asm.h> | ||
15 | #include <asm/asm-offsets.h> | ||
16 | #include <asm/export.h> | ||
17 | #include <asm/regdef.h> | ||
18 | |||
19 | #ifdef CONFIG_64BIT | ||
20 | /* | ||
21 | * As we are sharing code base with the mips32 tree (which use the o32 ABI | ||
22 | * register definitions). We need to redefine the register definitions from | ||
23 | * the n64 ABI register naming to the o32 ABI register naming. | ||
24 | */ | ||
25 | #undef t0 | ||
26 | #undef t1 | ||
27 | #undef t2 | ||
28 | #undef t3 | ||
29 | #define t0 $8 | ||
30 | #define t1 $9 | ||
31 | #define t2 $10 | ||
32 | #define t3 $11 | ||
33 | #define t4 $12 | ||
34 | #define t5 $13 | ||
35 | #define t6 $14 | ||
36 | #define t7 $15 | ||
37 | |||
38 | #define USE_DOUBLE | ||
39 | #endif | ||
40 | |||
41 | #ifdef USE_DOUBLE | ||
42 | |||
43 | #define LOAD ld | ||
44 | #define LOAD32 lwu | ||
45 | #define ADD daddu | ||
46 | #define NBYTES 8 | ||
47 | |||
48 | #else | ||
49 | |||
50 | #define LOAD lw | ||
51 | #define LOAD32 lw | ||
52 | #define ADD addu | ||
53 | #define NBYTES 4 | ||
54 | |||
55 | #endif /* USE_DOUBLE */ | ||
56 | |||
57 | #define UNIT(unit) ((unit)*NBYTES) | ||
58 | |||
59 | #define ADDC(sum,reg) \ | ||
60 | .set push; \ | ||
61 | .set noat; \ | ||
62 | ADD sum, reg; \ | ||
63 | sltu v1, sum, reg; \ | ||
64 | ADD sum, v1; \ | ||
65 | .set pop | ||
66 | |||
67 | #define ADDC32(sum,reg) \ | ||
68 | .set push; \ | ||
69 | .set noat; \ | ||
70 | addu sum, reg; \ | ||
71 | sltu v1, sum, reg; \ | ||
72 | addu sum, v1; \ | ||
73 | .set pop | ||
74 | |||
75 | #define CSUM_BIGCHUNK1(src, offset, sum, _t0, _t1, _t2, _t3) \ | ||
76 | LOAD _t0, (offset + UNIT(0))(src); \ | ||
77 | LOAD _t1, (offset + UNIT(1))(src); \ | ||
78 | LOAD _t2, (offset + UNIT(2))(src); \ | ||
79 | LOAD _t3, (offset + UNIT(3))(src); \ | ||
80 | ADDC(_t0, _t1); \ | ||
81 | ADDC(_t2, _t3); \ | ||
82 | ADDC(sum, _t0); \ | ||
83 | ADDC(sum, _t2) | ||
84 | |||
85 | #ifdef USE_DOUBLE | ||
86 | #define CSUM_BIGCHUNK(src, offset, sum, _t0, _t1, _t2, _t3) \ | ||
87 | CSUM_BIGCHUNK1(src, offset, sum, _t0, _t1, _t2, _t3) | ||
88 | #else | ||
89 | #define CSUM_BIGCHUNK(src, offset, sum, _t0, _t1, _t2, _t3) \ | ||
90 | CSUM_BIGCHUNK1(src, offset, sum, _t0, _t1, _t2, _t3); \ | ||
91 | CSUM_BIGCHUNK1(src, offset + 0x10, sum, _t0, _t1, _t2, _t3) | ||
92 | #endif | ||
93 | |||
94 | /* | ||
95 | * a0: source address | ||
96 | * a1: length of the area to checksum | ||
97 | * a2: partial checksum | ||
98 | */ | ||
99 | |||
100 | #define src a0 | ||
101 | #define sum v0 | ||
102 | |||
103 | .text | ||
104 | .set noreorder | ||
105 | .align 5 | ||
106 | LEAF(csum_partial) | ||
107 | EXPORT_SYMBOL(csum_partial) | ||
108 | move sum, zero | ||
109 | move t7, zero | ||
110 | |||
111 | sltiu t8, a1, 0x8 | ||
112 | bnez t8, .Lsmall_csumcpy /* < 8 bytes to copy */ | ||
113 | move t2, a1 | ||
114 | |||
115 | andi t7, src, 0x1 /* odd buffer? */ | ||
116 | |||
117 | .Lhword_align: | ||
118 | beqz t7, .Lword_align | ||
119 | andi t8, src, 0x2 | ||
120 | |||
121 | lbu t0, (src) | ||
122 | LONG_SUBU a1, a1, 0x1 | ||
123 | #ifdef __MIPSEL__ | ||
124 | sll t0, t0, 8 | ||
125 | #endif | ||
126 | ADDC(sum, t0) | ||
127 | PTR_ADDU src, src, 0x1 | ||
128 | andi t8, src, 0x2 | ||
129 | |||
130 | .Lword_align: | ||
131 | beqz t8, .Ldword_align | ||
132 | sltiu t8, a1, 56 | ||
133 | |||
134 | lhu t0, (src) | ||
135 | LONG_SUBU a1, a1, 0x2 | ||
136 | ADDC(sum, t0) | ||
137 | sltiu t8, a1, 56 | ||
138 | PTR_ADDU src, src, 0x2 | ||
139 | |||
140 | .Ldword_align: | ||
141 | bnez t8, .Ldo_end_words | ||
142 | move t8, a1 | ||
143 | |||
144 | andi t8, src, 0x4 | ||
145 | beqz t8, .Lqword_align | ||
146 | andi t8, src, 0x8 | ||
147 | |||
148 | LOAD32 t0, 0x00(src) | ||
149 | LONG_SUBU a1, a1, 0x4 | ||
150 | ADDC(sum, t0) | ||
151 | PTR_ADDU src, src, 0x4 | ||
152 | andi t8, src, 0x8 | ||
153 | |||
154 | .Lqword_align: | ||
155 | beqz t8, .Loword_align | ||
156 | andi t8, src, 0x10 | ||
157 | |||
158 | #ifdef USE_DOUBLE | ||
159 | ld t0, 0x00(src) | ||
160 | LONG_SUBU a1, a1, 0x8 | ||
161 | ADDC(sum, t0) | ||
162 | #else | ||
163 | lw t0, 0x00(src) | ||
164 | lw t1, 0x04(src) | ||
165 | LONG_SUBU a1, a1, 0x8 | ||
166 | ADDC(sum, t0) | ||
167 | ADDC(sum, t1) | ||
168 | #endif | ||
169 | PTR_ADDU src, src, 0x8 | ||
170 | andi t8, src, 0x10 | ||
171 | |||
172 | .Loword_align: | ||
173 | beqz t8, .Lbegin_movement | ||
174 | LONG_SRL t8, a1, 0x7 | ||
175 | |||
176 | #ifdef USE_DOUBLE | ||
177 | ld t0, 0x00(src) | ||
178 | ld t1, 0x08(src) | ||
179 | ADDC(sum, t0) | ||
180 | ADDC(sum, t1) | ||
181 | #else | ||
182 | CSUM_BIGCHUNK1(src, 0x00, sum, t0, t1, t3, t4) | ||
183 | #endif | ||
184 | LONG_SUBU a1, a1, 0x10 | ||
185 | PTR_ADDU src, src, 0x10 | ||
186 | LONG_SRL t8, a1, 0x7 | ||
187 | |||
188 | .Lbegin_movement: | ||
189 | beqz t8, 1f | ||
190 | andi t2, a1, 0x40 | ||
191 | |||
192 | .Lmove_128bytes: | ||
193 | CSUM_BIGCHUNK(src, 0x00, sum, t0, t1, t3, t4) | ||
194 | CSUM_BIGCHUNK(src, 0x20, sum, t0, t1, t3, t4) | ||
195 | CSUM_BIGCHUNK(src, 0x40, sum, t0, t1, t3, t4) | ||
196 | CSUM_BIGCHUNK(src, 0x60, sum, t0, t1, t3, t4) | ||
197 | LONG_SUBU t8, t8, 0x01 | ||
198 | .set reorder /* DADDI_WAR */ | ||
199 | PTR_ADDU src, src, 0x80 | ||
200 | bnez t8, .Lmove_128bytes | ||
201 | .set noreorder | ||
202 | |||
203 | 1: | ||
204 | beqz t2, 1f | ||
205 | andi t2, a1, 0x20 | ||
206 | |||
207 | .Lmove_64bytes: | ||
208 | CSUM_BIGCHUNK(src, 0x00, sum, t0, t1, t3, t4) | ||
209 | CSUM_BIGCHUNK(src, 0x20, sum, t0, t1, t3, t4) | ||
210 | PTR_ADDU src, src, 0x40 | ||
211 | |||
212 | 1: | ||
213 | beqz t2, .Ldo_end_words | ||
214 | andi t8, a1, 0x1c | ||
215 | |||
216 | .Lmove_32bytes: | ||
217 | CSUM_BIGCHUNK(src, 0x00, sum, t0, t1, t3, t4) | ||
218 | andi t8, a1, 0x1c | ||
219 | PTR_ADDU src, src, 0x20 | ||
220 | |||
221 | .Ldo_end_words: | ||
222 | beqz t8, .Lsmall_csumcpy | ||
223 | andi t2, a1, 0x3 | ||
224 | LONG_SRL t8, t8, 0x2 | ||
225 | |||
226 | .Lend_words: | ||
227 | LOAD32 t0, (src) | ||
228 | LONG_SUBU t8, t8, 0x1 | ||
229 | ADDC(sum, t0) | ||
230 | .set reorder /* DADDI_WAR */ | ||
231 | PTR_ADDU src, src, 0x4 | ||
232 | bnez t8, .Lend_words | ||
233 | .set noreorder | ||
234 | |||
235 | /* unknown src alignment and < 8 bytes to go */ | ||
236 | .Lsmall_csumcpy: | ||
237 | move a1, t2 | ||
238 | |||
239 | andi t0, a1, 4 | ||
240 | beqz t0, 1f | ||
241 | andi t0, a1, 2 | ||
242 | |||
243 | /* Still a full word to go */ | ||
244 | ulw t1, (src) | ||
245 | PTR_ADDIU src, 4 | ||
246 | #ifdef USE_DOUBLE | ||
247 | dsll t1, t1, 32 /* clear lower 32bit */ | ||
248 | #endif | ||
249 | ADDC(sum, t1) | ||
250 | |||
251 | 1: move t1, zero | ||
252 | beqz t0, 1f | ||
253 | andi t0, a1, 1 | ||
254 | |||
255 | /* Still a halfword to go */ | ||
256 | ulhu t1, (src) | ||
257 | PTR_ADDIU src, 2 | ||
258 | |||
259 | 1: beqz t0, 1f | ||
260 | sll t1, t1, 16 | ||
261 | |||
262 | lbu t2, (src) | ||
263 | nop | ||
264 | |||
265 | #ifdef __MIPSEB__ | ||
266 | sll t2, t2, 8 | ||
267 | #endif | ||
268 | or t1, t2 | ||
269 | |||
270 | 1: ADDC(sum, t1) | ||
271 | |||
272 | /* fold checksum */ | ||
273 | #ifdef USE_DOUBLE | ||
274 | dsll32 v1, sum, 0 | ||
275 | daddu sum, v1 | ||
276 | sltu v1, sum, v1 | ||
277 | dsra32 sum, sum, 0 | ||
278 | addu sum, v1 | ||
279 | #endif | ||
280 | |||
281 | /* odd buffer alignment? */ | ||
282 | #if defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_CPU_MIPSR5) || \ | ||
283 | defined(CONFIG_CPU_LOONGSON64) | ||
284 | .set push | ||
285 | .set arch=mips32r2 | ||
286 | wsbh v1, sum | ||
287 | movn sum, v1, t7 | ||
288 | .set pop | ||
289 | #else | ||
290 | beqz t7, 1f /* odd buffer alignment? */ | ||
291 | lui v1, 0x00ff | ||
292 | addu v1, 0x00ff | ||
293 | and t0, sum, v1 | ||
294 | sll t0, t0, 8 | ||
295 | srl sum, sum, 8 | ||
296 | and sum, sum, v1 | ||
297 | or sum, sum, t0 | ||
298 | 1: | ||
299 | #endif | ||
300 | .set reorder | ||
301 | /* Add the passed partial csum. */ | ||
302 | ADDC32(sum, a2) | ||
303 | jr ra | ||
304 | .set noreorder | ||
305 | END(csum_partial) | ||
306 | |||
307 | |||
308 | /* | ||
309 | * checksum and copy routines based on memcpy.S | ||
310 | * | ||
311 | * csum_partial_copy_nocheck(src, dst, len) | ||
312 | * __csum_partial_copy_kernel(src, dst, len) | ||
313 | * | ||
314 | * See "Spec" in memcpy.S for details. Unlike __copy_user, all | ||
315 | * function in this file use the standard calling convention. | ||
316 | */ | ||
317 | |||
318 | #define src a0 | ||
319 | #define dst a1 | ||
320 | #define len a2 | ||
321 | #define sum v0 | ||
322 | #define odd t8 | ||
323 | |||
324 | /* | ||
325 | * All exception handlers simply return 0. | ||
326 | */ | ||
327 | |||
328 | /* Instruction type */ | ||
329 | #define LD_INSN 1 | ||
330 | #define ST_INSN 2 | ||
331 | #define LEGACY_MODE 1 | ||
332 | #define EVA_MODE 2 | ||
333 | #define USEROP 1 | ||
334 | #define KERNELOP 2 | ||
335 | |||
336 | /* | ||
337 | * Wrapper to add an entry in the exception table | ||
338 | * in case the insn causes a memory exception. | ||
339 | * Arguments: | ||
340 | * insn : Load/store instruction | ||
341 | * type : Instruction type | ||
342 | * reg : Register | ||
343 | * addr : Address | ||
344 | * handler : Exception handler | ||
345 | */ | ||
346 | #define EXC(insn, type, reg, addr) \ | ||
347 | .if \mode == LEGACY_MODE; \ | ||
348 | 9: insn reg, addr; \ | ||
349 | .section __ex_table,"a"; \ | ||
350 | PTR 9b, .L_exc; \ | ||
351 | .previous; \ | ||
352 | /* This is enabled in EVA mode */ \ | ||
353 | .else; \ | ||
354 | /* If loading from user or storing to user */ \ | ||
355 | .if ((\from == USEROP) && (type == LD_INSN)) || \ | ||
356 | ((\to == USEROP) && (type == ST_INSN)); \ | ||
357 | 9: __BUILD_EVA_INSN(insn##e, reg, addr); \ | ||
358 | .section __ex_table,"a"; \ | ||
359 | PTR 9b, .L_exc; \ | ||
360 | .previous; \ | ||
361 | .else; \ | ||
362 | /* EVA without exception */ \ | ||
363 | insn reg, addr; \ | ||
364 | .endif; \ | ||
365 | .endif | ||
366 | |||
367 | #undef LOAD | ||
368 | |||
369 | #ifdef USE_DOUBLE | ||
370 | |||
371 | #define LOADK ld /* No exception */ | ||
372 | #define LOAD(reg, addr) EXC(ld, LD_INSN, reg, addr) | ||
373 | #define LOADBU(reg, addr) EXC(lbu, LD_INSN, reg, addr) | ||
374 | #define LOADL(reg, addr) EXC(ldl, LD_INSN, reg, addr) | ||
375 | #define LOADR(reg, addr) EXC(ldr, LD_INSN, reg, addr) | ||
376 | #define STOREB(reg, addr) EXC(sb, ST_INSN, reg, addr) | ||
377 | #define STOREL(reg, addr) EXC(sdl, ST_INSN, reg, addr) | ||
378 | #define STORER(reg, addr) EXC(sdr, ST_INSN, reg, addr) | ||
379 | #define STORE(reg, addr) EXC(sd, ST_INSN, reg, addr) | ||
380 | #define ADD daddu | ||
381 | #define SUB dsubu | ||
382 | #define SRL dsrl | ||
383 | #define SLL dsll | ||
384 | #define SLLV dsllv | ||
385 | #define SRLV dsrlv | ||
386 | #define NBYTES 8 | ||
387 | #define LOG_NBYTES 3 | ||
388 | |||
389 | #else | ||
390 | |||
391 | #define LOADK lw /* No exception */ | ||
392 | #define LOAD(reg, addr) EXC(lw, LD_INSN, reg, addr) | ||
393 | #define LOADBU(reg, addr) EXC(lbu, LD_INSN, reg, addr) | ||
394 | #define LOADL(reg, addr) EXC(lwl, LD_INSN, reg, addr) | ||
395 | #define LOADR(reg, addr) EXC(lwr, LD_INSN, reg, addr) | ||
396 | #define STOREB(reg, addr) EXC(sb, ST_INSN, reg, addr) | ||
397 | #define STOREL(reg, addr) EXC(swl, ST_INSN, reg, addr) | ||
398 | #define STORER(reg, addr) EXC(swr, ST_INSN, reg, addr) | ||
399 | #define STORE(reg, addr) EXC(sw, ST_INSN, reg, addr) | ||
400 | #define ADD addu | ||
401 | #define SUB subu | ||
402 | #define SRL srl | ||
403 | #define SLL sll | ||
404 | #define SLLV sllv | ||
405 | #define SRLV srlv | ||
406 | #define NBYTES 4 | ||
407 | #define LOG_NBYTES 2 | ||
408 | |||
409 | #endif /* USE_DOUBLE */ | ||
410 | |||
411 | #ifdef CONFIG_CPU_LITTLE_ENDIAN | ||
412 | #define LDFIRST LOADR | ||
413 | #define LDREST LOADL | ||
414 | #define STFIRST STORER | ||
415 | #define STREST STOREL | ||
416 | #define SHIFT_DISCARD SLLV | ||
417 | #define SHIFT_DISCARD_REVERT SRLV | ||
418 | #else | ||
419 | #define LDFIRST LOADL | ||
420 | #define LDREST LOADR | ||
421 | #define STFIRST STOREL | ||
422 | #define STREST STORER | ||
423 | #define SHIFT_DISCARD SRLV | ||
424 | #define SHIFT_DISCARD_REVERT SLLV | ||
425 | #endif | ||
426 | |||
427 | #define FIRST(unit) ((unit)*NBYTES) | ||
428 | #define REST(unit) (FIRST(unit)+NBYTES-1) | ||
429 | |||
430 | #define ADDRMASK (NBYTES-1) | ||
431 | |||
432 | #ifndef CONFIG_CPU_DADDI_WORKAROUNDS | ||
433 | .set noat | ||
434 | #else | ||
435 | .set at=v1 | ||
436 | #endif | ||
437 | |||
438 | .macro __BUILD_CSUM_PARTIAL_COPY_USER mode, from, to | ||
439 | |||
440 | li sum, -1 | ||
441 | move odd, zero | ||
442 | /* | ||
443 | * Note: dst & src may be unaligned, len may be 0 | ||
444 | * Temps | ||
445 | */ | ||
446 | /* | ||
447 | * The "issue break"s below are very approximate. | ||
448 | * Issue delays for dcache fills will perturb the schedule, as will | ||
449 | * load queue full replay traps, etc. | ||
450 | * | ||
451 | * If len < NBYTES use byte operations. | ||
452 | */ | ||
453 | sltu t2, len, NBYTES | ||
454 | and t1, dst, ADDRMASK | ||
455 | bnez t2, .Lcopy_bytes_checklen\@ | ||
456 | and t0, src, ADDRMASK | ||
457 | andi odd, dst, 0x1 /* odd buffer? */ | ||
458 | bnez t1, .Ldst_unaligned\@ | ||
459 | nop | ||
460 | bnez t0, .Lsrc_unaligned_dst_aligned\@ | ||
461 | /* | ||
462 | * use delay slot for fall-through | ||
463 | * src and dst are aligned; need to compute rem | ||
464 | */ | ||
465 | .Lboth_aligned\@: | ||
466 | SRL t0, len, LOG_NBYTES+3 # +3 for 8 units/iter | ||
467 | beqz t0, .Lcleanup_both_aligned\@ # len < 8*NBYTES | ||
468 | nop | ||
469 | SUB len, 8*NBYTES # subtract here for bgez loop | ||
470 | .align 4 | ||
471 | 1: | ||
472 | LOAD(t0, UNIT(0)(src)) | ||
473 | LOAD(t1, UNIT(1)(src)) | ||
474 | LOAD(t2, UNIT(2)(src)) | ||
475 | LOAD(t3, UNIT(3)(src)) | ||
476 | LOAD(t4, UNIT(4)(src)) | ||
477 | LOAD(t5, UNIT(5)(src)) | ||
478 | LOAD(t6, UNIT(6)(src)) | ||
479 | LOAD(t7, UNIT(7)(src)) | ||
480 | SUB len, len, 8*NBYTES | ||
481 | ADD src, src, 8*NBYTES | ||
482 | STORE(t0, UNIT(0)(dst)) | ||
483 | ADDC(t0, t1) | ||
484 | STORE(t1, UNIT(1)(dst)) | ||
485 | ADDC(sum, t0) | ||
486 | STORE(t2, UNIT(2)(dst)) | ||
487 | ADDC(t2, t3) | ||
488 | STORE(t3, UNIT(3)(dst)) | ||
489 | ADDC(sum, t2) | ||
490 | STORE(t4, UNIT(4)(dst)) | ||
491 | ADDC(t4, t5) | ||
492 | STORE(t5, UNIT(5)(dst)) | ||
493 | ADDC(sum, t4) | ||
494 | STORE(t6, UNIT(6)(dst)) | ||
495 | ADDC(t6, t7) | ||
496 | STORE(t7, UNIT(7)(dst)) | ||
497 | ADDC(sum, t6) | ||
498 | .set reorder /* DADDI_WAR */ | ||
499 | ADD dst, dst, 8*NBYTES | ||
500 | bgez len, 1b | ||
501 | .set noreorder | ||
502 | ADD len, 8*NBYTES # revert len (see above) | ||
503 | |||
504 | /* | ||
505 | * len == the number of bytes left to copy < 8*NBYTES | ||
506 | */ | ||
507 | .Lcleanup_both_aligned\@: | ||
508 | #define rem t7 | ||
509 | beqz len, .Ldone\@ | ||
510 | sltu t0, len, 4*NBYTES | ||
511 | bnez t0, .Lless_than_4units\@ | ||
512 | and rem, len, (NBYTES-1) # rem = len % NBYTES | ||
513 | /* | ||
514 | * len >= 4*NBYTES | ||
515 | */ | ||
516 | LOAD(t0, UNIT(0)(src)) | ||
517 | LOAD(t1, UNIT(1)(src)) | ||
518 | LOAD(t2, UNIT(2)(src)) | ||
519 | LOAD(t3, UNIT(3)(src)) | ||
520 | SUB len, len, 4*NBYTES | ||
521 | ADD src, src, 4*NBYTES | ||
522 | STORE(t0, UNIT(0)(dst)) | ||
523 | ADDC(t0, t1) | ||
524 | STORE(t1, UNIT(1)(dst)) | ||
525 | ADDC(sum, t0) | ||
526 | STORE(t2, UNIT(2)(dst)) | ||
527 | ADDC(t2, t3) | ||
528 | STORE(t3, UNIT(3)(dst)) | ||
529 | ADDC(sum, t2) | ||
530 | .set reorder /* DADDI_WAR */ | ||
531 | ADD dst, dst, 4*NBYTES | ||
532 | beqz len, .Ldone\@ | ||
533 | .set noreorder | ||
534 | .Lless_than_4units\@: | ||
535 | /* | ||
536 | * rem = len % NBYTES | ||
537 | */ | ||
538 | beq rem, len, .Lcopy_bytes\@ | ||
539 | nop | ||
540 | 1: | ||
541 | LOAD(t0, 0(src)) | ||
542 | ADD src, src, NBYTES | ||
543 | SUB len, len, NBYTES | ||
544 | STORE(t0, 0(dst)) | ||
545 | ADDC(sum, t0) | ||
546 | .set reorder /* DADDI_WAR */ | ||
547 | ADD dst, dst, NBYTES | ||
548 | bne rem, len, 1b | ||
549 | .set noreorder | ||
550 | |||
551 | /* | ||
552 | * src and dst are aligned, need to copy rem bytes (rem < NBYTES) | ||
553 | * A loop would do only a byte at a time with possible branch | ||
554 | * mispredicts. Can't do an explicit LOAD dst,mask,or,STORE | ||
555 | * because can't assume read-access to dst. Instead, use | ||
556 | * STREST dst, which doesn't require read access to dst. | ||
557 | * | ||
558 | * This code should perform better than a simple loop on modern, | ||
559 | * wide-issue mips processors because the code has fewer branches and | ||
560 | * more instruction-level parallelism. | ||
561 | */ | ||
562 | #define bits t2 | ||
563 | beqz len, .Ldone\@ | ||
564 | ADD t1, dst, len # t1 is just past last byte of dst | ||
565 | li bits, 8*NBYTES | ||
566 | SLL rem, len, 3 # rem = number of bits to keep | ||
567 | LOAD(t0, 0(src)) | ||
568 | SUB bits, bits, rem # bits = number of bits to discard | ||
569 | SHIFT_DISCARD t0, t0, bits | ||
570 | STREST(t0, -1(t1)) | ||
571 | SHIFT_DISCARD_REVERT t0, t0, bits | ||
572 | .set reorder | ||
573 | ADDC(sum, t0) | ||
574 | b .Ldone\@ | ||
575 | .set noreorder | ||
576 | .Ldst_unaligned\@: | ||
577 | /* | ||
578 | * dst is unaligned | ||
579 | * t0 = src & ADDRMASK | ||
580 | * t1 = dst & ADDRMASK; T1 > 0 | ||
581 | * len >= NBYTES | ||
582 | * | ||
583 | * Copy enough bytes to align dst | ||
584 | * Set match = (src and dst have same alignment) | ||
585 | */ | ||
586 | #define match rem | ||
587 | LDFIRST(t3, FIRST(0)(src)) | ||
588 | ADD t2, zero, NBYTES | ||
589 | LDREST(t3, REST(0)(src)) | ||
590 | SUB t2, t2, t1 # t2 = number of bytes copied | ||
591 | xor match, t0, t1 | ||
592 | STFIRST(t3, FIRST(0)(dst)) | ||
593 | SLL t4, t1, 3 # t4 = number of bits to discard | ||
594 | SHIFT_DISCARD t3, t3, t4 | ||
595 | /* no SHIFT_DISCARD_REVERT to handle odd buffer properly */ | ||
596 | ADDC(sum, t3) | ||
597 | beq len, t2, .Ldone\@ | ||
598 | SUB len, len, t2 | ||
599 | ADD dst, dst, t2 | ||
600 | beqz match, .Lboth_aligned\@ | ||
601 | ADD src, src, t2 | ||
602 | |||
603 | .Lsrc_unaligned_dst_aligned\@: | ||
604 | SRL t0, len, LOG_NBYTES+2 # +2 for 4 units/iter | ||
605 | beqz t0, .Lcleanup_src_unaligned\@ | ||
606 | and rem, len, (4*NBYTES-1) # rem = len % 4*NBYTES | ||
607 | 1: | ||
608 | /* | ||
609 | * Avoid consecutive LD*'s to the same register since some mips | ||
610 | * implementations can't issue them in the same cycle. | ||
611 | * It's OK to load FIRST(N+1) before REST(N) because the two addresses | ||
612 | * are to the same unit (unless src is aligned, but it's not). | ||
613 | */ | ||
614 | LDFIRST(t0, FIRST(0)(src)) | ||
615 | LDFIRST(t1, FIRST(1)(src)) | ||
616 | SUB len, len, 4*NBYTES | ||
617 | LDREST(t0, REST(0)(src)) | ||
618 | LDREST(t1, REST(1)(src)) | ||
619 | LDFIRST(t2, FIRST(2)(src)) | ||
620 | LDFIRST(t3, FIRST(3)(src)) | ||
621 | LDREST(t2, REST(2)(src)) | ||
622 | LDREST(t3, REST(3)(src)) | ||
623 | ADD src, src, 4*NBYTES | ||
624 | #ifdef CONFIG_CPU_SB1 | ||
625 | nop # improves slotting | ||
626 | #endif | ||
627 | STORE(t0, UNIT(0)(dst)) | ||
628 | ADDC(t0, t1) | ||
629 | STORE(t1, UNIT(1)(dst)) | ||
630 | ADDC(sum, t0) | ||
631 | STORE(t2, UNIT(2)(dst)) | ||
632 | ADDC(t2, t3) | ||
633 | STORE(t3, UNIT(3)(dst)) | ||
634 | ADDC(sum, t2) | ||
635 | .set reorder /* DADDI_WAR */ | ||
636 | ADD dst, dst, 4*NBYTES | ||
637 | bne len, rem, 1b | ||
638 | .set noreorder | ||
639 | |||
640 | .Lcleanup_src_unaligned\@: | ||
641 | beqz len, .Ldone\@ | ||
642 | and rem, len, NBYTES-1 # rem = len % NBYTES | ||
643 | beq rem, len, .Lcopy_bytes\@ | ||
644 | nop | ||
645 | 1: | ||
646 | LDFIRST(t0, FIRST(0)(src)) | ||
647 | LDREST(t0, REST(0)(src)) | ||
648 | ADD src, src, NBYTES | ||
649 | SUB len, len, NBYTES | ||
650 | STORE(t0, 0(dst)) | ||
651 | ADDC(sum, t0) | ||
652 | .set reorder /* DADDI_WAR */ | ||
653 | ADD dst, dst, NBYTES | ||
654 | bne len, rem, 1b | ||
655 | .set noreorder | ||
656 | |||
657 | .Lcopy_bytes_checklen\@: | ||
658 | beqz len, .Ldone\@ | ||
659 | nop | ||
660 | .Lcopy_bytes\@: | ||
661 | /* 0 < len < NBYTES */ | ||
662 | #ifdef CONFIG_CPU_LITTLE_ENDIAN | ||
663 | #define SHIFT_START 0 | ||
664 | #define SHIFT_INC 8 | ||
665 | #else | ||
666 | #define SHIFT_START 8*(NBYTES-1) | ||
667 | #define SHIFT_INC -8 | ||
668 | #endif | ||
669 | move t2, zero # partial word | ||
670 | li t3, SHIFT_START # shift | ||
671 | #define COPY_BYTE(N) \ | ||
672 | LOADBU(t0, N(src)); \ | ||
673 | SUB len, len, 1; \ | ||
674 | STOREB(t0, N(dst)); \ | ||
675 | SLLV t0, t0, t3; \ | ||
676 | addu t3, SHIFT_INC; \ | ||
677 | beqz len, .Lcopy_bytes_done\@; \ | ||
678 | or t2, t0 | ||
679 | |||
680 | COPY_BYTE(0) | ||
681 | COPY_BYTE(1) | ||
682 | #ifdef USE_DOUBLE | ||
683 | COPY_BYTE(2) | ||
684 | COPY_BYTE(3) | ||
685 | COPY_BYTE(4) | ||
686 | COPY_BYTE(5) | ||
687 | #endif | ||
688 | LOADBU(t0, NBYTES-2(src)) | ||
689 | SUB len, len, 1 | ||
690 | STOREB(t0, NBYTES-2(dst)) | ||
691 | SLLV t0, t0, t3 | ||
692 | or t2, t0 | ||
693 | .Lcopy_bytes_done\@: | ||
694 | ADDC(sum, t2) | ||
695 | .Ldone\@: | ||
696 | /* fold checksum */ | ||
697 | .set push | ||
698 | .set noat | ||
699 | #ifdef USE_DOUBLE | ||
700 | dsll32 v1, sum, 0 | ||
701 | daddu sum, v1 | ||
702 | sltu v1, sum, v1 | ||
703 | dsra32 sum, sum, 0 | ||
704 | addu sum, v1 | ||
705 | #endif | ||
706 | |||
707 | #if defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_CPU_MIPSR5) || \ | ||
708 | defined(CONFIG_CPU_LOONGSON64) | ||
709 | .set push | ||
710 | .set arch=mips32r2 | ||
711 | wsbh v1, sum | ||
712 | movn sum, v1, odd | ||
713 | .set pop | ||
714 | #else | ||
715 | beqz odd, 1f /* odd buffer alignment? */ | ||
716 | lui v1, 0x00ff | ||
717 | addu v1, 0x00ff | ||
718 | and t0, sum, v1 | ||
719 | sll t0, t0, 8 | ||
720 | srl sum, sum, 8 | ||
721 | and sum, sum, v1 | ||
722 | or sum, sum, t0 | ||
723 | 1: | ||
724 | #endif | ||
725 | .set pop | ||
726 | .set reorder | ||
727 | jr ra | ||
728 | .set noreorder | ||
729 | .endm | ||
730 | |||
731 | .set noreorder | ||
732 | .L_exc: | ||
733 | jr ra | ||
734 | li v0, 0 | ||
735 | |||
736 | FEXPORT(__csum_partial_copy_nocheck) | ||
737 | EXPORT_SYMBOL(__csum_partial_copy_nocheck) | ||
738 | #ifndef CONFIG_EVA | ||
739 | FEXPORT(__csum_partial_copy_to_user) | ||
740 | EXPORT_SYMBOL(__csum_partial_copy_to_user) | ||
741 | FEXPORT(__csum_partial_copy_from_user) | ||
742 | EXPORT_SYMBOL(__csum_partial_copy_from_user) | ||
743 | #endif | ||
744 | __BUILD_CSUM_PARTIAL_COPY_USER LEGACY_MODE USEROP USEROP | ||
745 | |||
746 | #ifdef CONFIG_EVA | ||
747 | LEAF(__csum_partial_copy_to_user) | ||
748 | __BUILD_CSUM_PARTIAL_COPY_USER EVA_MODE KERNELOP USEROP | ||
749 | END(__csum_partial_copy_to_user) | ||
750 | |||
751 | LEAF(__csum_partial_copy_from_user) | ||
752 | __BUILD_CSUM_PARTIAL_COPY_USER EVA_MODE USEROP KERNELOP | ||
753 | END(__csum_partial_copy_from_user) | ||
754 | #endif | ||
diff --git a/arch/mips/lib/delay.c b/arch/mips/lib/delay.c new file mode 100644 index 000000000..2e8dfc1d5 --- /dev/null +++ b/arch/mips/lib/delay.c | |||
@@ -0,0 +1,69 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 1994 by Waldorf Electronics | ||
7 | * Copyright (C) 1995 - 2000, 01, 03 by Ralf Baechle | ||
8 | * Copyright (C) 1999, 2000 Silicon Graphics, Inc. | ||
9 | * Copyright (C) 2007, 2014 Maciej W. Rozycki | ||
10 | */ | ||
11 | #include <linux/delay.h> | ||
12 | #include <linux/export.h> | ||
13 | #include <linux/param.h> | ||
14 | #include <linux/smp.h> | ||
15 | #include <linux/stringify.h> | ||
16 | |||
17 | #include <asm/asm.h> | ||
18 | #include <asm/compiler.h> | ||
19 | #include <asm/war.h> | ||
20 | |||
21 | #ifndef CONFIG_CPU_DADDI_WORKAROUNDS | ||
22 | #define GCC_DADDI_IMM_ASM() "I" | ||
23 | #else | ||
24 | #define GCC_DADDI_IMM_ASM() "r" | ||
25 | #endif | ||
26 | |||
27 | #ifndef CONFIG_HAVE_PLAT_DELAY | ||
28 | |||
29 | void __delay(unsigned long loops) | ||
30 | { | ||
31 | __asm__ __volatile__ ( | ||
32 | " .set noreorder \n" | ||
33 | " .align 3 \n" | ||
34 | "1: bnez %0, 1b \n" | ||
35 | " " __stringify(LONG_SUBU) " %0, %1 \n" | ||
36 | " .set reorder \n" | ||
37 | : "=r" (loops) | ||
38 | : GCC_DADDI_IMM_ASM() (1), "0" (loops)); | ||
39 | } | ||
40 | EXPORT_SYMBOL(__delay); | ||
41 | |||
42 | /* | ||
43 | * Division by multiplication: you don't have to worry about | ||
44 | * loss of precision. | ||
45 | * | ||
46 | * Use only for very small delays ( < 1 msec). Should probably use a | ||
47 | * lookup table, really, as the multiplications take much too long with | ||
48 | * short delays. This is a "reasonable" implementation, though (and the | ||
49 | * first constant multiplications gets optimized away if the delay is | ||
50 | * a constant) | ||
51 | */ | ||
52 | |||
53 | void __udelay(unsigned long us) | ||
54 | { | ||
55 | unsigned int lpj = raw_current_cpu_data.udelay_val; | ||
56 | |||
57 | __delay((us * 0x000010c7ull * HZ * lpj) >> 32); | ||
58 | } | ||
59 | EXPORT_SYMBOL(__udelay); | ||
60 | |||
61 | void __ndelay(unsigned long ns) | ||
62 | { | ||
63 | unsigned int lpj = raw_current_cpu_data.udelay_val; | ||
64 | |||
65 | __delay((ns * 0x00000005ull * HZ * lpj) >> 32); | ||
66 | } | ||
67 | EXPORT_SYMBOL(__ndelay); | ||
68 | |||
69 | #endif | ||
diff --git a/arch/mips/lib/dump_tlb.c b/arch/mips/lib/dump_tlb.c new file mode 100644 index 000000000..425642363 --- /dev/null +++ b/arch/mips/lib/dump_tlb.c | |||
@@ -0,0 +1,199 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Dump R4x00 TLB for debugging purposes. | ||
4 | * | ||
5 | * Copyright (C) 1994, 1995 by Waldorf Electronics, written by Ralf Baechle. | ||
6 | * Copyright (C) 1999 by Silicon Graphics, Inc. | ||
7 | */ | ||
8 | #include <linux/kernel.h> | ||
9 | #include <linux/mm.h> | ||
10 | |||
11 | #include <asm/hazards.h> | ||
12 | #include <asm/mipsregs.h> | ||
13 | #include <asm/mmu_context.h> | ||
14 | #include <asm/page.h> | ||
15 | #include <asm/tlbdebug.h> | ||
16 | |||
17 | void dump_tlb_regs(void) | ||
18 | { | ||
19 | const int field = 2 * sizeof(unsigned long); | ||
20 | |||
21 | pr_info("Index : %0x\n", read_c0_index()); | ||
22 | pr_info("PageMask : %0x\n", read_c0_pagemask()); | ||
23 | if (cpu_has_guestid) | ||
24 | pr_info("GuestCtl1: %0x\n", read_c0_guestctl1()); | ||
25 | pr_info("EntryHi : %0*lx\n", field, read_c0_entryhi()); | ||
26 | pr_info("EntryLo0 : %0*lx\n", field, read_c0_entrylo0()); | ||
27 | pr_info("EntryLo1 : %0*lx\n", field, read_c0_entrylo1()); | ||
28 | pr_info("Wired : %0x\n", read_c0_wired()); | ||
29 | switch (current_cpu_type()) { | ||
30 | case CPU_R10000: | ||
31 | case CPU_R12000: | ||
32 | case CPU_R14000: | ||
33 | case CPU_R16000: | ||
34 | pr_info("FrameMask: %0x\n", read_c0_framemask()); | ||
35 | break; | ||
36 | } | ||
37 | if (cpu_has_small_pages || cpu_has_rixi || cpu_has_xpa) | ||
38 | pr_info("PageGrain: %0x\n", read_c0_pagegrain()); | ||
39 | if (cpu_has_htw) { | ||
40 | pr_info("PWField : %0*lx\n", field, read_c0_pwfield()); | ||
41 | pr_info("PWSize : %0*lx\n", field, read_c0_pwsize()); | ||
42 | pr_info("PWCtl : %0x\n", read_c0_pwctl()); | ||
43 | } | ||
44 | } | ||
45 | |||
46 | static inline const char *msk2str(unsigned int mask) | ||
47 | { | ||
48 | switch (mask) { | ||
49 | case PM_4K: return "4kb"; | ||
50 | case PM_16K: return "16kb"; | ||
51 | case PM_64K: return "64kb"; | ||
52 | case PM_256K: return "256kb"; | ||
53 | #ifdef CONFIG_CPU_CAVIUM_OCTEON | ||
54 | case PM_8K: return "8kb"; | ||
55 | case PM_32K: return "32kb"; | ||
56 | case PM_128K: return "128kb"; | ||
57 | case PM_512K: return "512kb"; | ||
58 | case PM_2M: return "2Mb"; | ||
59 | case PM_8M: return "8Mb"; | ||
60 | case PM_32M: return "32Mb"; | ||
61 | #endif | ||
62 | #ifndef CONFIG_CPU_VR41XX | ||
63 | case PM_1M: return "1Mb"; | ||
64 | case PM_4M: return "4Mb"; | ||
65 | case PM_16M: return "16Mb"; | ||
66 | case PM_64M: return "64Mb"; | ||
67 | case PM_256M: return "256Mb"; | ||
68 | case PM_1G: return "1Gb"; | ||
69 | #endif | ||
70 | } | ||
71 | return ""; | ||
72 | } | ||
73 | |||
74 | static void dump_tlb(int first, int last) | ||
75 | { | ||
76 | unsigned long s_entryhi, entryhi, asid, mmid; | ||
77 | unsigned long long entrylo0, entrylo1, pa; | ||
78 | unsigned int s_index, s_pagemask, s_guestctl1 = 0; | ||
79 | unsigned int pagemask, guestctl1 = 0, c0, c1, i; | ||
80 | unsigned long asidmask = cpu_asid_mask(¤t_cpu_data); | ||
81 | int asidwidth = DIV_ROUND_UP(ilog2(asidmask) + 1, 4); | ||
82 | unsigned long s_mmid; | ||
83 | #ifdef CONFIG_32BIT | ||
84 | bool xpa = cpu_has_xpa && (read_c0_pagegrain() & PG_ELPA); | ||
85 | int pwidth = xpa ? 11 : 8; | ||
86 | int vwidth = 8; | ||
87 | #else | ||
88 | bool xpa = false; | ||
89 | int pwidth = 11; | ||
90 | int vwidth = 11; | ||
91 | #endif | ||
92 | |||
93 | s_pagemask = read_c0_pagemask(); | ||
94 | s_entryhi = read_c0_entryhi(); | ||
95 | s_index = read_c0_index(); | ||
96 | |||
97 | if (cpu_has_mmid) | ||
98 | asid = s_mmid = read_c0_memorymapid(); | ||
99 | else | ||
100 | asid = s_entryhi & asidmask; | ||
101 | |||
102 | if (cpu_has_guestid) | ||
103 | s_guestctl1 = read_c0_guestctl1(); | ||
104 | |||
105 | for (i = first; i <= last; i++) { | ||
106 | write_c0_index(i); | ||
107 | mtc0_tlbr_hazard(); | ||
108 | tlb_read(); | ||
109 | tlb_read_hazard(); | ||
110 | pagemask = read_c0_pagemask(); | ||
111 | entryhi = read_c0_entryhi(); | ||
112 | entrylo0 = read_c0_entrylo0(); | ||
113 | entrylo1 = read_c0_entrylo1(); | ||
114 | |||
115 | if (cpu_has_mmid) | ||
116 | mmid = read_c0_memorymapid(); | ||
117 | else | ||
118 | mmid = entryhi & asidmask; | ||
119 | |||
120 | if (cpu_has_guestid) | ||
121 | guestctl1 = read_c0_guestctl1(); | ||
122 | |||
123 | /* EHINV bit marks entire entry as invalid */ | ||
124 | if (cpu_has_tlbinv && entryhi & MIPS_ENTRYHI_EHINV) | ||
125 | continue; | ||
126 | /* | ||
127 | * Prior to tlbinv, unused entries have a virtual address of | ||
128 | * CKSEG0. | ||
129 | */ | ||
130 | if ((entryhi & ~0x1ffffUL) == CKSEG0) | ||
131 | continue; | ||
132 | /* | ||
133 | * ASID takes effect in absence of G (global) bit. | ||
134 | * We check both G bits, even though architecturally they should | ||
135 | * match one another, because some revisions of the SB1 core may | ||
136 | * leave only a single G bit set after a machine check exception | ||
137 | * due to duplicate TLB entry. | ||
138 | */ | ||
139 | if (!((entrylo0 | entrylo1) & ENTRYLO_G) && (mmid != asid)) | ||
140 | continue; | ||
141 | |||
142 | /* | ||
143 | * Only print entries in use | ||
144 | */ | ||
145 | printk("Index: %2d pgmask=%s ", i, msk2str(pagemask)); | ||
146 | |||
147 | c0 = (entrylo0 & ENTRYLO_C) >> ENTRYLO_C_SHIFT; | ||
148 | c1 = (entrylo1 & ENTRYLO_C) >> ENTRYLO_C_SHIFT; | ||
149 | |||
150 | pr_cont("va=%0*lx asid=%0*lx", | ||
151 | vwidth, (entryhi & ~0x1fffUL), | ||
152 | asidwidth, mmid); | ||
153 | if (cpu_has_guestid) | ||
154 | pr_cont(" gid=%02lx", | ||
155 | (guestctl1 & MIPS_GCTL1_RID) | ||
156 | >> MIPS_GCTL1_RID_SHIFT); | ||
157 | /* RI/XI are in awkward places, so mask them off separately */ | ||
158 | pa = entrylo0 & ~(MIPS_ENTRYLO_RI | MIPS_ENTRYLO_XI); | ||
159 | if (xpa) | ||
160 | pa |= (unsigned long long)readx_c0_entrylo0() << 30; | ||
161 | pa = (pa << 6) & PAGE_MASK; | ||
162 | pr_cont("\n\t["); | ||
163 | if (cpu_has_rixi) | ||
164 | pr_cont("ri=%d xi=%d ", | ||
165 | (entrylo0 & MIPS_ENTRYLO_RI) ? 1 : 0, | ||
166 | (entrylo0 & MIPS_ENTRYLO_XI) ? 1 : 0); | ||
167 | pr_cont("pa=%0*llx c=%d d=%d v=%d g=%d] [", | ||
168 | pwidth, pa, c0, | ||
169 | (entrylo0 & ENTRYLO_D) ? 1 : 0, | ||
170 | (entrylo0 & ENTRYLO_V) ? 1 : 0, | ||
171 | (entrylo0 & ENTRYLO_G) ? 1 : 0); | ||
172 | /* RI/XI are in awkward places, so mask them off separately */ | ||
173 | pa = entrylo1 & ~(MIPS_ENTRYLO_RI | MIPS_ENTRYLO_XI); | ||
174 | if (xpa) | ||
175 | pa |= (unsigned long long)readx_c0_entrylo1() << 30; | ||
176 | pa = (pa << 6) & PAGE_MASK; | ||
177 | if (cpu_has_rixi) | ||
178 | pr_cont("ri=%d xi=%d ", | ||
179 | (entrylo1 & MIPS_ENTRYLO_RI) ? 1 : 0, | ||
180 | (entrylo1 & MIPS_ENTRYLO_XI) ? 1 : 0); | ||
181 | pr_cont("pa=%0*llx c=%d d=%d v=%d g=%d]\n", | ||
182 | pwidth, pa, c1, | ||
183 | (entrylo1 & ENTRYLO_D) ? 1 : 0, | ||
184 | (entrylo1 & ENTRYLO_V) ? 1 : 0, | ||
185 | (entrylo1 & ENTRYLO_G) ? 1 : 0); | ||
186 | } | ||
187 | printk("\n"); | ||
188 | |||
189 | write_c0_entryhi(s_entryhi); | ||
190 | write_c0_index(s_index); | ||
191 | write_c0_pagemask(s_pagemask); | ||
192 | if (cpu_has_guestid) | ||
193 | write_c0_guestctl1(s_guestctl1); | ||
194 | } | ||
195 | |||
196 | void dump_tlb_all(void) | ||
197 | { | ||
198 | dump_tlb(0, current_cpu_data.tlbsize - 1); | ||
199 | } | ||
diff --git a/arch/mips/lib/iomap-pci.c b/arch/mips/lib/iomap-pci.c new file mode 100644 index 000000000..210f5a95e --- /dev/null +++ b/arch/mips/lib/iomap-pci.c | |||
@@ -0,0 +1,46 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Implement the default iomap interfaces | ||
4 | * | ||
5 | * (C) Copyright 2004 Linus Torvalds | ||
6 | * (C) Copyright 2006 Ralf Baechle <ralf@linux-mips.org> | ||
7 | * (C) Copyright 2007 MIPS Technologies, Inc. | ||
8 | * written by Ralf Baechle <ralf@linux-mips.org> | ||
9 | */ | ||
10 | #include <linux/pci.h> | ||
11 | #include <linux/export.h> | ||
12 | #include <asm/io.h> | ||
13 | |||
14 | #ifdef CONFIG_PCI_DRIVERS_LEGACY | ||
15 | |||
16 | void __iomem *__pci_ioport_map(struct pci_dev *dev, | ||
17 | unsigned long port, unsigned int nr) | ||
18 | { | ||
19 | struct pci_controller *ctrl = dev->bus->sysdata; | ||
20 | unsigned long base = ctrl->io_map_base; | ||
21 | |||
22 | /* This will eventually become a BUG_ON but for now be gentle */ | ||
23 | if (unlikely(!ctrl->io_map_base)) { | ||
24 | struct pci_bus *bus = dev->bus; | ||
25 | char name[8]; | ||
26 | |||
27 | while (bus->parent) | ||
28 | bus = bus->parent; | ||
29 | |||
30 | ctrl->io_map_base = base = mips_io_port_base; | ||
31 | |||
32 | sprintf(name, "%04x:%02x", pci_domain_nr(bus), bus->number); | ||
33 | printk(KERN_WARNING "io_map_base of root PCI bus %s unset. " | ||
34 | "Trying to continue but you better\nfix this issue or " | ||
35 | "report it to linux-mips@linux-mips.org or your " | ||
36 | "vendor.\n", name); | ||
37 | #ifdef CONFIG_PCI_DOMAINS | ||
38 | panic("To avoid data corruption io_map_base MUST be set with " | ||
39 | "multiple PCI domains."); | ||
40 | #endif | ||
41 | } | ||
42 | |||
43 | return (void __iomem *) (ctrl->io_map_base + port); | ||
44 | } | ||
45 | |||
46 | #endif /* CONFIG_PCI_DRIVERS_LEGACY */ | ||
diff --git a/arch/mips/lib/iomap_copy.c b/arch/mips/lib/iomap_copy.c new file mode 100644 index 000000000..157500a09 --- /dev/null +++ b/arch/mips/lib/iomap_copy.c | |||
@@ -0,0 +1,29 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0-only | ||
2 | |||
3 | #include <linux/export.h> | ||
4 | #include <linux/io.h> | ||
5 | |||
6 | /** | ||
7 | * __ioread64_copy - copy data from MMIO space, in 64-bit units | ||
8 | * @to: destination (must be 64-bit aligned) | ||
9 | * @from: source, in MMIO space (must be 64-bit aligned) | ||
10 | * @count: number of 64-bit quantities to copy | ||
11 | * | ||
12 | * Copy data from MMIO space to kernel space, in units of 32 or 64 bits at a | ||
13 | * time. Order of access is not guaranteed, nor is a memory barrier | ||
14 | * performed afterwards. | ||
15 | */ | ||
16 | void __ioread64_copy(void *to, const void __iomem *from, size_t count) | ||
17 | { | ||
18 | #ifdef CONFIG_64BIT | ||
19 | u64 *dst = to; | ||
20 | const u64 __iomem *src = from; | ||
21 | const u64 __iomem *end = src + count; | ||
22 | |||
23 | while (src < end) | ||
24 | *dst++ = __raw_readq(src++); | ||
25 | #else | ||
26 | __ioread32_copy(to, from, count * 2); | ||
27 | #endif | ||
28 | } | ||
29 | EXPORT_SYMBOL_GPL(__ioread64_copy); | ||
diff --git a/arch/mips/lib/libgcc.h b/arch/mips/lib/libgcc.h new file mode 100644 index 000000000..199a7f962 --- /dev/null +++ b/arch/mips/lib/libgcc.h | |||
@@ -0,0 +1,43 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | #ifndef __ASM_LIBGCC_H | ||
3 | #define __ASM_LIBGCC_H | ||
4 | |||
5 | #include <asm/byteorder.h> | ||
6 | |||
7 | typedef int word_type __attribute__ ((mode (__word__))); | ||
8 | |||
9 | #ifdef __BIG_ENDIAN | ||
10 | struct DWstruct { | ||
11 | int high, low; | ||
12 | }; | ||
13 | |||
14 | struct TWstruct { | ||
15 | long long high, low; | ||
16 | }; | ||
17 | #elif defined(__LITTLE_ENDIAN) | ||
18 | struct DWstruct { | ||
19 | int low, high; | ||
20 | }; | ||
21 | |||
22 | struct TWstruct { | ||
23 | long long low, high; | ||
24 | }; | ||
25 | #else | ||
26 | #error I feel sick. | ||
27 | #endif | ||
28 | |||
29 | typedef union { | ||
30 | struct DWstruct s; | ||
31 | long long ll; | ||
32 | } DWunion; | ||
33 | |||
34 | #if defined(CONFIG_64BIT) && defined(CONFIG_CPU_MIPSR6) | ||
35 | typedef int ti_type __attribute__((mode(TI))); | ||
36 | |||
37 | typedef union { | ||
38 | struct TWstruct s; | ||
39 | ti_type ti; | ||
40 | } TWunion; | ||
41 | #endif | ||
42 | |||
43 | #endif /* __ASM_LIBGCC_H */ | ||
diff --git a/arch/mips/lib/memcpy.S b/arch/mips/lib/memcpy.S new file mode 100644 index 000000000..88065ee43 --- /dev/null +++ b/arch/mips/lib/memcpy.S | |||
@@ -0,0 +1,709 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Unified implementation of memcpy, memmove and the __copy_user backend. | ||
7 | * | ||
8 | * Copyright (C) 1998, 99, 2000, 01, 2002 Ralf Baechle (ralf@gnu.org) | ||
9 | * Copyright (C) 1999, 2000, 01, 2002 Silicon Graphics, Inc. | ||
10 | * Copyright (C) 2002 Broadcom, Inc. | ||
11 | * memcpy/copy_user author: Mark Vandevoorde | ||
12 | * Copyright (C) 2007 Maciej W. Rozycki | ||
13 | * Copyright (C) 2014 Imagination Technologies Ltd. | ||
14 | * | ||
15 | * Mnemonic names for arguments to memcpy/__copy_user | ||
16 | */ | ||
17 | |||
18 | /* | ||
19 | * Hack to resolve longstanding prefetch issue | ||
20 | * | ||
21 | * Prefetching may be fatal on some systems if we're prefetching beyond the | ||
22 | * end of memory on some systems. It's also a seriously bad idea on non | ||
23 | * dma-coherent systems. | ||
24 | */ | ||
25 | #ifdef CONFIG_DMA_NONCOHERENT | ||
26 | #undef CONFIG_CPU_HAS_PREFETCH | ||
27 | #endif | ||
28 | #ifdef CONFIG_MIPS_MALTA | ||
29 | #undef CONFIG_CPU_HAS_PREFETCH | ||
30 | #endif | ||
31 | #ifdef CONFIG_CPU_MIPSR6 | ||
32 | #undef CONFIG_CPU_HAS_PREFETCH | ||
33 | #endif | ||
34 | |||
35 | #include <asm/asm.h> | ||
36 | #include <asm/asm-offsets.h> | ||
37 | #include <asm/export.h> | ||
38 | #include <asm/regdef.h> | ||
39 | |||
40 | #define dst a0 | ||
41 | #define src a1 | ||
42 | #define len a2 | ||
43 | |||
44 | /* | ||
45 | * Spec | ||
46 | * | ||
47 | * memcpy copies len bytes from src to dst and sets v0 to dst. | ||
48 | * It assumes that | ||
49 | * - src and dst don't overlap | ||
50 | * - src is readable | ||
51 | * - dst is writable | ||
52 | * memcpy uses the standard calling convention | ||
53 | * | ||
54 | * __copy_user copies up to len bytes from src to dst and sets a2 (len) to | ||
55 | * the number of uncopied bytes due to an exception caused by a read or write. | ||
56 | * __copy_user assumes that src and dst don't overlap, and that the call is | ||
57 | * implementing one of the following: | ||
58 | * copy_to_user | ||
59 | * - src is readable (no exceptions when reading src) | ||
60 | * copy_from_user | ||
61 | * - dst is writable (no exceptions when writing dst) | ||
62 | * __copy_user uses a non-standard calling convention; see | ||
63 | * include/asm-mips/uaccess.h | ||
64 | * | ||
65 | * When an exception happens on a load, the handler must | ||
66 | # ensure that all of the destination buffer is overwritten to prevent | ||
67 | * leaking information to user mode programs. | ||
68 | */ | ||
69 | |||
70 | /* | ||
71 | * Implementation | ||
72 | */ | ||
73 | |||
74 | /* | ||
75 | * The exception handler for loads requires that: | ||
76 | * 1- AT contain the address of the byte just past the end of the source | ||
77 | * of the copy, | ||
78 | * 2- src_entry <= src < AT, and | ||
79 | * 3- (dst - src) == (dst_entry - src_entry), | ||
80 | * The _entry suffix denotes values when __copy_user was called. | ||
81 | * | ||
82 | * (1) is set up up by uaccess.h and maintained by not writing AT in copy_user | ||
83 | * (2) is met by incrementing src by the number of bytes copied | ||
84 | * (3) is met by not doing loads between a pair of increments of dst and src | ||
85 | * | ||
86 | * The exception handlers for stores adjust len (if necessary) and return. | ||
87 | * These handlers do not need to overwrite any data. | ||
88 | * | ||
89 | * For __rmemcpy and memmove an exception is always a kernel bug, therefore | ||
90 | * they're not protected. | ||
91 | */ | ||
92 | |||
93 | /* Instruction type */ | ||
94 | #define LD_INSN 1 | ||
95 | #define ST_INSN 2 | ||
96 | /* Pretech type */ | ||
97 | #define SRC_PREFETCH 1 | ||
98 | #define DST_PREFETCH 2 | ||
99 | #define LEGACY_MODE 1 | ||
100 | #define EVA_MODE 2 | ||
101 | #define USEROP 1 | ||
102 | #define KERNELOP 2 | ||
103 | |||
104 | /* | ||
105 | * Wrapper to add an entry in the exception table | ||
106 | * in case the insn causes a memory exception. | ||
107 | * Arguments: | ||
108 | * insn : Load/store instruction | ||
109 | * type : Instruction type | ||
110 | * reg : Register | ||
111 | * addr : Address | ||
112 | * handler : Exception handler | ||
113 | */ | ||
114 | |||
115 | #define EXC(insn, type, reg, addr, handler) \ | ||
116 | .if \mode == LEGACY_MODE; \ | ||
117 | 9: insn reg, addr; \ | ||
118 | .section __ex_table,"a"; \ | ||
119 | PTR 9b, handler; \ | ||
120 | .previous; \ | ||
121 | /* This is assembled in EVA mode */ \ | ||
122 | .else; \ | ||
123 | /* If loading from user or storing to user */ \ | ||
124 | .if ((\from == USEROP) && (type == LD_INSN)) || \ | ||
125 | ((\to == USEROP) && (type == ST_INSN)); \ | ||
126 | 9: __BUILD_EVA_INSN(insn##e, reg, addr); \ | ||
127 | .section __ex_table,"a"; \ | ||
128 | PTR 9b, handler; \ | ||
129 | .previous; \ | ||
130 | .else; \ | ||
131 | /* \ | ||
132 | * Still in EVA, but no need for \ | ||
133 | * exception handler or EVA insn \ | ||
134 | */ \ | ||
135 | insn reg, addr; \ | ||
136 | .endif; \ | ||
137 | .endif | ||
138 | |||
139 | /* | ||
140 | * Only on the 64-bit kernel we can made use of 64-bit registers. | ||
141 | */ | ||
142 | #ifdef CONFIG_64BIT | ||
143 | #define USE_DOUBLE | ||
144 | #endif | ||
145 | |||
146 | #ifdef USE_DOUBLE | ||
147 | |||
148 | #define LOADK ld /* No exception */ | ||
149 | #define LOAD(reg, addr, handler) EXC(ld, LD_INSN, reg, addr, handler) | ||
150 | #define LOADL(reg, addr, handler) EXC(ldl, LD_INSN, reg, addr, handler) | ||
151 | #define LOADR(reg, addr, handler) EXC(ldr, LD_INSN, reg, addr, handler) | ||
152 | #define STOREL(reg, addr, handler) EXC(sdl, ST_INSN, reg, addr, handler) | ||
153 | #define STORER(reg, addr, handler) EXC(sdr, ST_INSN, reg, addr, handler) | ||
154 | #define STORE(reg, addr, handler) EXC(sd, ST_INSN, reg, addr, handler) | ||
155 | #define ADD daddu | ||
156 | #define SUB dsubu | ||
157 | #define SRL dsrl | ||
158 | #define SRA dsra | ||
159 | #define SLL dsll | ||
160 | #define SLLV dsllv | ||
161 | #define SRLV dsrlv | ||
162 | #define NBYTES 8 | ||
163 | #define LOG_NBYTES 3 | ||
164 | |||
165 | /* | ||
166 | * As we are sharing code base with the mips32 tree (which use the o32 ABI | ||
167 | * register definitions). We need to redefine the register definitions from | ||
168 | * the n64 ABI register naming to the o32 ABI register naming. | ||
169 | */ | ||
170 | #undef t0 | ||
171 | #undef t1 | ||
172 | #undef t2 | ||
173 | #undef t3 | ||
174 | #define t0 $8 | ||
175 | #define t1 $9 | ||
176 | #define t2 $10 | ||
177 | #define t3 $11 | ||
178 | #define t4 $12 | ||
179 | #define t5 $13 | ||
180 | #define t6 $14 | ||
181 | #define t7 $15 | ||
182 | |||
183 | #else | ||
184 | |||
185 | #define LOADK lw /* No exception */ | ||
186 | #define LOAD(reg, addr, handler) EXC(lw, LD_INSN, reg, addr, handler) | ||
187 | #define LOADL(reg, addr, handler) EXC(lwl, LD_INSN, reg, addr, handler) | ||
188 | #define LOADR(reg, addr, handler) EXC(lwr, LD_INSN, reg, addr, handler) | ||
189 | #define STOREL(reg, addr, handler) EXC(swl, ST_INSN, reg, addr, handler) | ||
190 | #define STORER(reg, addr, handler) EXC(swr, ST_INSN, reg, addr, handler) | ||
191 | #define STORE(reg, addr, handler) EXC(sw, ST_INSN, reg, addr, handler) | ||
192 | #define ADD addu | ||
193 | #define SUB subu | ||
194 | #define SRL srl | ||
195 | #define SLL sll | ||
196 | #define SRA sra | ||
197 | #define SLLV sllv | ||
198 | #define SRLV srlv | ||
199 | #define NBYTES 4 | ||
200 | #define LOG_NBYTES 2 | ||
201 | |||
202 | #endif /* USE_DOUBLE */ | ||
203 | |||
204 | #define LOADB(reg, addr, handler) EXC(lb, LD_INSN, reg, addr, handler) | ||
205 | #define STOREB(reg, addr, handler) EXC(sb, ST_INSN, reg, addr, handler) | ||
206 | |||
207 | #ifdef CONFIG_CPU_HAS_PREFETCH | ||
208 | # define _PREF(hint, addr, type) \ | ||
209 | .if \mode == LEGACY_MODE; \ | ||
210 | kernel_pref(hint, addr); \ | ||
211 | .else; \ | ||
212 | .if ((\from == USEROP) && (type == SRC_PREFETCH)) || \ | ||
213 | ((\to == USEROP) && (type == DST_PREFETCH)); \ | ||
214 | /* \ | ||
215 | * PREFE has only 9 bits for the offset \ | ||
216 | * compared to PREF which has 16, so it may \ | ||
217 | * need to use the $at register but this \ | ||
218 | * register should remain intact because it's \ | ||
219 | * used later on. Therefore use $v1. \ | ||
220 | */ \ | ||
221 | .set at=v1; \ | ||
222 | user_pref(hint, addr); \ | ||
223 | .set noat; \ | ||
224 | .else; \ | ||
225 | kernel_pref(hint, addr); \ | ||
226 | .endif; \ | ||
227 | .endif | ||
228 | #else | ||
229 | # define _PREF(hint, addr, type) | ||
230 | #endif | ||
231 | |||
232 | #define PREFS(hint, addr) _PREF(hint, addr, SRC_PREFETCH) | ||
233 | #define PREFD(hint, addr) _PREF(hint, addr, DST_PREFETCH) | ||
234 | |||
235 | #ifdef CONFIG_CPU_LITTLE_ENDIAN | ||
236 | #define LDFIRST LOADR | ||
237 | #define LDREST LOADL | ||
238 | #define STFIRST STORER | ||
239 | #define STREST STOREL | ||
240 | #define SHIFT_DISCARD SLLV | ||
241 | #else | ||
242 | #define LDFIRST LOADL | ||
243 | #define LDREST LOADR | ||
244 | #define STFIRST STOREL | ||
245 | #define STREST STORER | ||
246 | #define SHIFT_DISCARD SRLV | ||
247 | #endif | ||
248 | |||
249 | #define FIRST(unit) ((unit)*NBYTES) | ||
250 | #define REST(unit) (FIRST(unit)+NBYTES-1) | ||
251 | #define UNIT(unit) FIRST(unit) | ||
252 | |||
253 | #define ADDRMASK (NBYTES-1) | ||
254 | |||
255 | .text | ||
256 | .set noreorder | ||
257 | #ifndef CONFIG_CPU_DADDI_WORKAROUNDS | ||
258 | .set noat | ||
259 | #else | ||
260 | .set at=v1 | ||
261 | #endif | ||
262 | |||
263 | .align 5 | ||
264 | |||
265 | /* | ||
266 | * Macro to build the __copy_user common code | ||
267 | * Arguments: | ||
268 | * mode : LEGACY_MODE or EVA_MODE | ||
269 | * from : Source operand. USEROP or KERNELOP | ||
270 | * to : Destination operand. USEROP or KERNELOP | ||
271 | */ | ||
272 | .macro __BUILD_COPY_USER mode, from, to | ||
273 | |||
274 | /* initialize __memcpy if this the first time we execute this macro */ | ||
275 | .ifnotdef __memcpy | ||
276 | .set __memcpy, 1 | ||
277 | .hidden __memcpy /* make sure it does not leak */ | ||
278 | .endif | ||
279 | |||
280 | /* | ||
281 | * Note: dst & src may be unaligned, len may be 0 | ||
282 | * Temps | ||
283 | */ | ||
284 | #define rem t8 | ||
285 | |||
286 | R10KCBARRIER(0(ra)) | ||
287 | /* | ||
288 | * The "issue break"s below are very approximate. | ||
289 | * Issue delays for dcache fills will perturb the schedule, as will | ||
290 | * load queue full replay traps, etc. | ||
291 | * | ||
292 | * If len < NBYTES use byte operations. | ||
293 | */ | ||
294 | PREFS( 0, 0(src) ) | ||
295 | PREFD( 1, 0(dst) ) | ||
296 | sltu t2, len, NBYTES | ||
297 | and t1, dst, ADDRMASK | ||
298 | PREFS( 0, 1*32(src) ) | ||
299 | PREFD( 1, 1*32(dst) ) | ||
300 | bnez t2, .Lcopy_bytes_checklen\@ | ||
301 | and t0, src, ADDRMASK | ||
302 | PREFS( 0, 2*32(src) ) | ||
303 | PREFD( 1, 2*32(dst) ) | ||
304 | #ifndef CONFIG_CPU_NO_LOAD_STORE_LR | ||
305 | bnez t1, .Ldst_unaligned\@ | ||
306 | nop | ||
307 | bnez t0, .Lsrc_unaligned_dst_aligned\@ | ||
308 | #else /* CONFIG_CPU_NO_LOAD_STORE_LR */ | ||
309 | or t0, t0, t1 | ||
310 | bnez t0, .Lcopy_unaligned_bytes\@ | ||
311 | #endif /* CONFIG_CPU_NO_LOAD_STORE_LR */ | ||
312 | /* | ||
313 | * use delay slot for fall-through | ||
314 | * src and dst are aligned; need to compute rem | ||
315 | */ | ||
316 | .Lboth_aligned\@: | ||
317 | SRL t0, len, LOG_NBYTES+3 # +3 for 8 units/iter | ||
318 | beqz t0, .Lcleanup_both_aligned\@ # len < 8*NBYTES | ||
319 | and rem, len, (8*NBYTES-1) # rem = len % (8*NBYTES) | ||
320 | PREFS( 0, 3*32(src) ) | ||
321 | PREFD( 1, 3*32(dst) ) | ||
322 | .align 4 | ||
323 | 1: | ||
324 | R10KCBARRIER(0(ra)) | ||
325 | LOAD(t0, UNIT(0)(src), .Ll_exc\@) | ||
326 | LOAD(t1, UNIT(1)(src), .Ll_exc_copy\@) | ||
327 | LOAD(t2, UNIT(2)(src), .Ll_exc_copy\@) | ||
328 | LOAD(t3, UNIT(3)(src), .Ll_exc_copy\@) | ||
329 | SUB len, len, 8*NBYTES | ||
330 | LOAD(t4, UNIT(4)(src), .Ll_exc_copy\@) | ||
331 | LOAD(t7, UNIT(5)(src), .Ll_exc_copy\@) | ||
332 | STORE(t0, UNIT(0)(dst), .Ls_exc_p8u\@) | ||
333 | STORE(t1, UNIT(1)(dst), .Ls_exc_p7u\@) | ||
334 | LOAD(t0, UNIT(6)(src), .Ll_exc_copy\@) | ||
335 | LOAD(t1, UNIT(7)(src), .Ll_exc_copy\@) | ||
336 | ADD src, src, 8*NBYTES | ||
337 | ADD dst, dst, 8*NBYTES | ||
338 | STORE(t2, UNIT(-6)(dst), .Ls_exc_p6u\@) | ||
339 | STORE(t3, UNIT(-5)(dst), .Ls_exc_p5u\@) | ||
340 | STORE(t4, UNIT(-4)(dst), .Ls_exc_p4u\@) | ||
341 | STORE(t7, UNIT(-3)(dst), .Ls_exc_p3u\@) | ||
342 | STORE(t0, UNIT(-2)(dst), .Ls_exc_p2u\@) | ||
343 | STORE(t1, UNIT(-1)(dst), .Ls_exc_p1u\@) | ||
344 | PREFS( 0, 8*32(src) ) | ||
345 | PREFD( 1, 8*32(dst) ) | ||
346 | bne len, rem, 1b | ||
347 | nop | ||
348 | |||
349 | /* | ||
350 | * len == rem == the number of bytes left to copy < 8*NBYTES | ||
351 | */ | ||
352 | .Lcleanup_both_aligned\@: | ||
353 | beqz len, .Ldone\@ | ||
354 | sltu t0, len, 4*NBYTES | ||
355 | bnez t0, .Lless_than_4units\@ | ||
356 | and rem, len, (NBYTES-1) # rem = len % NBYTES | ||
357 | /* | ||
358 | * len >= 4*NBYTES | ||
359 | */ | ||
360 | LOAD( t0, UNIT(0)(src), .Ll_exc\@) | ||
361 | LOAD( t1, UNIT(1)(src), .Ll_exc_copy\@) | ||
362 | LOAD( t2, UNIT(2)(src), .Ll_exc_copy\@) | ||
363 | LOAD( t3, UNIT(3)(src), .Ll_exc_copy\@) | ||
364 | SUB len, len, 4*NBYTES | ||
365 | ADD src, src, 4*NBYTES | ||
366 | R10KCBARRIER(0(ra)) | ||
367 | STORE(t0, UNIT(0)(dst), .Ls_exc_p4u\@) | ||
368 | STORE(t1, UNIT(1)(dst), .Ls_exc_p3u\@) | ||
369 | STORE(t2, UNIT(2)(dst), .Ls_exc_p2u\@) | ||
370 | STORE(t3, UNIT(3)(dst), .Ls_exc_p1u\@) | ||
371 | .set reorder /* DADDI_WAR */ | ||
372 | ADD dst, dst, 4*NBYTES | ||
373 | beqz len, .Ldone\@ | ||
374 | .set noreorder | ||
375 | .Lless_than_4units\@: | ||
376 | /* | ||
377 | * rem = len % NBYTES | ||
378 | */ | ||
379 | beq rem, len, .Lcopy_bytes\@ | ||
380 | nop | ||
381 | 1: | ||
382 | R10KCBARRIER(0(ra)) | ||
383 | LOAD(t0, 0(src), .Ll_exc\@) | ||
384 | ADD src, src, NBYTES | ||
385 | SUB len, len, NBYTES | ||
386 | STORE(t0, 0(dst), .Ls_exc_p1u\@) | ||
387 | .set reorder /* DADDI_WAR */ | ||
388 | ADD dst, dst, NBYTES | ||
389 | bne rem, len, 1b | ||
390 | .set noreorder | ||
391 | |||
392 | #ifndef CONFIG_CPU_NO_LOAD_STORE_LR | ||
393 | /* | ||
394 | * src and dst are aligned, need to copy rem bytes (rem < NBYTES) | ||
395 | * A loop would do only a byte at a time with possible branch | ||
396 | * mispredicts. Can't do an explicit LOAD dst,mask,or,STORE | ||
397 | * because can't assume read-access to dst. Instead, use | ||
398 | * STREST dst, which doesn't require read access to dst. | ||
399 | * | ||
400 | * This code should perform better than a simple loop on modern, | ||
401 | * wide-issue mips processors because the code has fewer branches and | ||
402 | * more instruction-level parallelism. | ||
403 | */ | ||
404 | #define bits t2 | ||
405 | beqz len, .Ldone\@ | ||
406 | ADD t1, dst, len # t1 is just past last byte of dst | ||
407 | li bits, 8*NBYTES | ||
408 | SLL rem, len, 3 # rem = number of bits to keep | ||
409 | LOAD(t0, 0(src), .Ll_exc\@) | ||
410 | SUB bits, bits, rem # bits = number of bits to discard | ||
411 | SHIFT_DISCARD t0, t0, bits | ||
412 | STREST(t0, -1(t1), .Ls_exc\@) | ||
413 | jr ra | ||
414 | move len, zero | ||
415 | .Ldst_unaligned\@: | ||
416 | /* | ||
417 | * dst is unaligned | ||
418 | * t0 = src & ADDRMASK | ||
419 | * t1 = dst & ADDRMASK; T1 > 0 | ||
420 | * len >= NBYTES | ||
421 | * | ||
422 | * Copy enough bytes to align dst | ||
423 | * Set match = (src and dst have same alignment) | ||
424 | */ | ||
425 | #define match rem | ||
426 | LDFIRST(t3, FIRST(0)(src), .Ll_exc\@) | ||
427 | ADD t2, zero, NBYTES | ||
428 | LDREST(t3, REST(0)(src), .Ll_exc_copy\@) | ||
429 | SUB t2, t2, t1 # t2 = number of bytes copied | ||
430 | xor match, t0, t1 | ||
431 | R10KCBARRIER(0(ra)) | ||
432 | STFIRST(t3, FIRST(0)(dst), .Ls_exc\@) | ||
433 | beq len, t2, .Ldone\@ | ||
434 | SUB len, len, t2 | ||
435 | ADD dst, dst, t2 | ||
436 | beqz match, .Lboth_aligned\@ | ||
437 | ADD src, src, t2 | ||
438 | |||
439 | .Lsrc_unaligned_dst_aligned\@: | ||
440 | SRL t0, len, LOG_NBYTES+2 # +2 for 4 units/iter | ||
441 | PREFS( 0, 3*32(src) ) | ||
442 | beqz t0, .Lcleanup_src_unaligned\@ | ||
443 | and rem, len, (4*NBYTES-1) # rem = len % 4*NBYTES | ||
444 | PREFD( 1, 3*32(dst) ) | ||
445 | 1: | ||
446 | /* | ||
447 | * Avoid consecutive LD*'s to the same register since some mips | ||
448 | * implementations can't issue them in the same cycle. | ||
449 | * It's OK to load FIRST(N+1) before REST(N) because the two addresses | ||
450 | * are to the same unit (unless src is aligned, but it's not). | ||
451 | */ | ||
452 | R10KCBARRIER(0(ra)) | ||
453 | LDFIRST(t0, FIRST(0)(src), .Ll_exc\@) | ||
454 | LDFIRST(t1, FIRST(1)(src), .Ll_exc_copy\@) | ||
455 | SUB len, len, 4*NBYTES | ||
456 | LDREST(t0, REST(0)(src), .Ll_exc_copy\@) | ||
457 | LDREST(t1, REST(1)(src), .Ll_exc_copy\@) | ||
458 | LDFIRST(t2, FIRST(2)(src), .Ll_exc_copy\@) | ||
459 | LDFIRST(t3, FIRST(3)(src), .Ll_exc_copy\@) | ||
460 | LDREST(t2, REST(2)(src), .Ll_exc_copy\@) | ||
461 | LDREST(t3, REST(3)(src), .Ll_exc_copy\@) | ||
462 | PREFS( 0, 9*32(src) ) # 0 is PREF_LOAD (not streamed) | ||
463 | ADD src, src, 4*NBYTES | ||
464 | #ifdef CONFIG_CPU_SB1 | ||
465 | nop # improves slotting | ||
466 | #endif | ||
467 | STORE(t0, UNIT(0)(dst), .Ls_exc_p4u\@) | ||
468 | STORE(t1, UNIT(1)(dst), .Ls_exc_p3u\@) | ||
469 | STORE(t2, UNIT(2)(dst), .Ls_exc_p2u\@) | ||
470 | STORE(t3, UNIT(3)(dst), .Ls_exc_p1u\@) | ||
471 | PREFD( 1, 9*32(dst) ) # 1 is PREF_STORE (not streamed) | ||
472 | .set reorder /* DADDI_WAR */ | ||
473 | ADD dst, dst, 4*NBYTES | ||
474 | bne len, rem, 1b | ||
475 | .set noreorder | ||
476 | |||
477 | .Lcleanup_src_unaligned\@: | ||
478 | beqz len, .Ldone\@ | ||
479 | and rem, len, NBYTES-1 # rem = len % NBYTES | ||
480 | beq rem, len, .Lcopy_bytes\@ | ||
481 | nop | ||
482 | 1: | ||
483 | R10KCBARRIER(0(ra)) | ||
484 | LDFIRST(t0, FIRST(0)(src), .Ll_exc\@) | ||
485 | LDREST(t0, REST(0)(src), .Ll_exc_copy\@) | ||
486 | ADD src, src, NBYTES | ||
487 | SUB len, len, NBYTES | ||
488 | STORE(t0, 0(dst), .Ls_exc_p1u\@) | ||
489 | .set reorder /* DADDI_WAR */ | ||
490 | ADD dst, dst, NBYTES | ||
491 | bne len, rem, 1b | ||
492 | .set noreorder | ||
493 | |||
494 | #endif /* !CONFIG_CPU_NO_LOAD_STORE_LR */ | ||
495 | .Lcopy_bytes_checklen\@: | ||
496 | beqz len, .Ldone\@ | ||
497 | nop | ||
498 | .Lcopy_bytes\@: | ||
499 | /* 0 < len < NBYTES */ | ||
500 | R10KCBARRIER(0(ra)) | ||
501 | #define COPY_BYTE(N) \ | ||
502 | LOADB(t0, N(src), .Ll_exc\@); \ | ||
503 | SUB len, len, 1; \ | ||
504 | beqz len, .Ldone\@; \ | ||
505 | STOREB(t0, N(dst), .Ls_exc_p1\@) | ||
506 | |||
507 | COPY_BYTE(0) | ||
508 | COPY_BYTE(1) | ||
509 | #ifdef USE_DOUBLE | ||
510 | COPY_BYTE(2) | ||
511 | COPY_BYTE(3) | ||
512 | COPY_BYTE(4) | ||
513 | COPY_BYTE(5) | ||
514 | #endif | ||
515 | LOADB(t0, NBYTES-2(src), .Ll_exc\@) | ||
516 | SUB len, len, 1 | ||
517 | jr ra | ||
518 | STOREB(t0, NBYTES-2(dst), .Ls_exc_p1\@) | ||
519 | .Ldone\@: | ||
520 | jr ra | ||
521 | nop | ||
522 | |||
523 | #ifdef CONFIG_CPU_NO_LOAD_STORE_LR | ||
524 | .Lcopy_unaligned_bytes\@: | ||
525 | 1: | ||
526 | COPY_BYTE(0) | ||
527 | COPY_BYTE(1) | ||
528 | COPY_BYTE(2) | ||
529 | COPY_BYTE(3) | ||
530 | COPY_BYTE(4) | ||
531 | COPY_BYTE(5) | ||
532 | COPY_BYTE(6) | ||
533 | COPY_BYTE(7) | ||
534 | ADD src, src, 8 | ||
535 | b 1b | ||
536 | ADD dst, dst, 8 | ||
537 | #endif /* CONFIG_CPU_NO_LOAD_STORE_LR */ | ||
538 | .if __memcpy == 1 | ||
539 | END(memcpy) | ||
540 | .set __memcpy, 0 | ||
541 | .hidden __memcpy | ||
542 | .endif | ||
543 | |||
544 | .Ll_exc_copy\@: | ||
545 | /* | ||
546 | * Copy bytes from src until faulting load address (or until a | ||
547 | * lb faults) | ||
548 | * | ||
549 | * When reached by a faulting LDFIRST/LDREST, THREAD_BUADDR($28) | ||
550 | * may be more than a byte beyond the last address. | ||
551 | * Hence, the lb below may get an exception. | ||
552 | * | ||
553 | * Assumes src < THREAD_BUADDR($28) | ||
554 | */ | ||
555 | LOADK t0, TI_TASK($28) | ||
556 | nop | ||
557 | LOADK t0, THREAD_BUADDR(t0) | ||
558 | 1: | ||
559 | LOADB(t1, 0(src), .Ll_exc\@) | ||
560 | ADD src, src, 1 | ||
561 | sb t1, 0(dst) # can't fault -- we're copy_from_user | ||
562 | .set reorder /* DADDI_WAR */ | ||
563 | ADD dst, dst, 1 | ||
564 | bne src, t0, 1b | ||
565 | .set noreorder | ||
566 | .Ll_exc\@: | ||
567 | LOADK t0, TI_TASK($28) | ||
568 | nop | ||
569 | LOADK t0, THREAD_BUADDR(t0) # t0 is just past last good address | ||
570 | nop | ||
571 | SUB len, AT, t0 # len number of uncopied bytes | ||
572 | jr ra | ||
573 | nop | ||
574 | |||
575 | #define SEXC(n) \ | ||
576 | .set reorder; /* DADDI_WAR */ \ | ||
577 | .Ls_exc_p ## n ## u\@: \ | ||
578 | ADD len, len, n*NBYTES; \ | ||
579 | jr ra; \ | ||
580 | .set noreorder | ||
581 | |||
582 | SEXC(8) | ||
583 | SEXC(7) | ||
584 | SEXC(6) | ||
585 | SEXC(5) | ||
586 | SEXC(4) | ||
587 | SEXC(3) | ||
588 | SEXC(2) | ||
589 | SEXC(1) | ||
590 | |||
591 | .Ls_exc_p1\@: | ||
592 | .set reorder /* DADDI_WAR */ | ||
593 | ADD len, len, 1 | ||
594 | jr ra | ||
595 | .set noreorder | ||
596 | .Ls_exc\@: | ||
597 | jr ra | ||
598 | nop | ||
599 | .endm | ||
600 | |||
601 | #ifndef CONFIG_HAVE_PLAT_MEMCPY | ||
602 | .align 5 | ||
603 | LEAF(memmove) | ||
604 | EXPORT_SYMBOL(memmove) | ||
605 | ADD t0, a0, a2 | ||
606 | ADD t1, a1, a2 | ||
607 | sltu t0, a1, t0 # dst + len <= src -> memcpy | ||
608 | sltu t1, a0, t1 # dst >= src + len -> memcpy | ||
609 | and t0, t1 | ||
610 | beqz t0, .L__memcpy | ||
611 | move v0, a0 /* return value */ | ||
612 | beqz a2, .Lr_out | ||
613 | END(memmove) | ||
614 | |||
615 | /* fall through to __rmemcpy */ | ||
616 | LEAF(__rmemcpy) /* a0=dst a1=src a2=len */ | ||
617 | sltu t0, a1, a0 | ||
618 | beqz t0, .Lr_end_bytes_up # src >= dst | ||
619 | nop | ||
620 | ADD a0, a2 # dst = dst + len | ||
621 | ADD a1, a2 # src = src + len | ||
622 | |||
623 | .Lr_end_bytes: | ||
624 | R10KCBARRIER(0(ra)) | ||
625 | lb t0, -1(a1) | ||
626 | SUB a2, a2, 0x1 | ||
627 | sb t0, -1(a0) | ||
628 | SUB a1, a1, 0x1 | ||
629 | .set reorder /* DADDI_WAR */ | ||
630 | SUB a0, a0, 0x1 | ||
631 | bnez a2, .Lr_end_bytes | ||
632 | .set noreorder | ||
633 | |||
634 | .Lr_out: | ||
635 | jr ra | ||
636 | move a2, zero | ||
637 | |||
638 | .Lr_end_bytes_up: | ||
639 | R10KCBARRIER(0(ra)) | ||
640 | lb t0, (a1) | ||
641 | SUB a2, a2, 0x1 | ||
642 | sb t0, (a0) | ||
643 | ADD a1, a1, 0x1 | ||
644 | .set reorder /* DADDI_WAR */ | ||
645 | ADD a0, a0, 0x1 | ||
646 | bnez a2, .Lr_end_bytes_up | ||
647 | .set noreorder | ||
648 | |||
649 | jr ra | ||
650 | move a2, zero | ||
651 | END(__rmemcpy) | ||
652 | |||
653 | /* | ||
654 | * A combined memcpy/__copy_user | ||
655 | * __copy_user sets len to 0 for success; else to an upper bound of | ||
656 | * the number of uncopied bytes. | ||
657 | * memcpy sets v0 to dst. | ||
658 | */ | ||
659 | .align 5 | ||
660 | LEAF(memcpy) /* a0=dst a1=src a2=len */ | ||
661 | EXPORT_SYMBOL(memcpy) | ||
662 | move v0, dst /* return value */ | ||
663 | .L__memcpy: | ||
664 | FEXPORT(__copy_user) | ||
665 | EXPORT_SYMBOL(__copy_user) | ||
666 | /* Legacy Mode, user <-> user */ | ||
667 | __BUILD_COPY_USER LEGACY_MODE USEROP USEROP | ||
668 | |||
669 | #endif | ||
670 | |||
671 | #ifdef CONFIG_EVA | ||
672 | |||
673 | /* | ||
674 | * For EVA we need distinct symbols for reading and writing to user space. | ||
675 | * This is because we need to use specific EVA instructions to perform the | ||
676 | * virtual <-> physical translation when a virtual address is actually in user | ||
677 | * space | ||
678 | */ | ||
679 | |||
680 | /* | ||
681 | * __copy_from_user (EVA) | ||
682 | */ | ||
683 | |||
684 | LEAF(__copy_from_user_eva) | ||
685 | EXPORT_SYMBOL(__copy_from_user_eva) | ||
686 | __BUILD_COPY_USER EVA_MODE USEROP KERNELOP | ||
687 | END(__copy_from_user_eva) | ||
688 | |||
689 | |||
690 | |||
691 | /* | ||
692 | * __copy_to_user (EVA) | ||
693 | */ | ||
694 | |||
695 | LEAF(__copy_to_user_eva) | ||
696 | EXPORT_SYMBOL(__copy_to_user_eva) | ||
697 | __BUILD_COPY_USER EVA_MODE KERNELOP USEROP | ||
698 | END(__copy_to_user_eva) | ||
699 | |||
700 | /* | ||
701 | * __copy_in_user (EVA) | ||
702 | */ | ||
703 | |||
704 | LEAF(__copy_in_user_eva) | ||
705 | EXPORT_SYMBOL(__copy_in_user_eva) | ||
706 | __BUILD_COPY_USER EVA_MODE USEROP USEROP | ||
707 | END(__copy_in_user_eva) | ||
708 | |||
709 | #endif | ||
diff --git a/arch/mips/lib/memset.S b/arch/mips/lib/memset.S new file mode 100644 index 000000000..d5449e8a3 --- /dev/null +++ b/arch/mips/lib/memset.S | |||
@@ -0,0 +1,328 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 1998, 1999, 2000 by Ralf Baechle | ||
7 | * Copyright (C) 1999, 2000 Silicon Graphics, Inc. | ||
8 | * Copyright (C) 2007 by Maciej W. Rozycki | ||
9 | * Copyright (C) 2011, 2012 MIPS Technologies, Inc. | ||
10 | */ | ||
11 | #include <asm/asm.h> | ||
12 | #include <asm/asm-offsets.h> | ||
13 | #include <asm/export.h> | ||
14 | #include <asm/regdef.h> | ||
15 | |||
16 | #if LONGSIZE == 4 | ||
17 | #define LONG_S_L swl | ||
18 | #define LONG_S_R swr | ||
19 | #else | ||
20 | #define LONG_S_L sdl | ||
21 | #define LONG_S_R sdr | ||
22 | #endif | ||
23 | |||
24 | #ifdef CONFIG_CPU_MICROMIPS | ||
25 | #define STORSIZE (LONGSIZE * 2) | ||
26 | #define STORMASK (STORSIZE - 1) | ||
27 | #define FILL64RG t8 | ||
28 | #define FILLPTRG t7 | ||
29 | #undef LONG_S | ||
30 | #define LONG_S LONG_SP | ||
31 | #else | ||
32 | #define STORSIZE LONGSIZE | ||
33 | #define STORMASK LONGMASK | ||
34 | #define FILL64RG a1 | ||
35 | #define FILLPTRG t0 | ||
36 | #endif | ||
37 | |||
38 | #define LEGACY_MODE 1 | ||
39 | #define EVA_MODE 2 | ||
40 | |||
41 | /* | ||
42 | * No need to protect it with EVA #ifdefery. The generated block of code | ||
43 | * will never be assembled if EVA is not enabled. | ||
44 | */ | ||
45 | #define __EVAFY(insn, reg, addr) __BUILD_EVA_INSN(insn##e, reg, addr) | ||
46 | #define ___BUILD_EVA_INSN(insn, reg, addr) __EVAFY(insn, reg, addr) | ||
47 | |||
48 | #define EX(insn,reg,addr,handler) \ | ||
49 | .if \mode == LEGACY_MODE; \ | ||
50 | 9: insn reg, addr; \ | ||
51 | .else; \ | ||
52 | 9: ___BUILD_EVA_INSN(insn, reg, addr); \ | ||
53 | .endif; \ | ||
54 | .section __ex_table,"a"; \ | ||
55 | PTR 9b, handler; \ | ||
56 | .previous | ||
57 | |||
58 | .macro f_fill64 dst, offset, val, fixup, mode | ||
59 | EX(LONG_S, \val, (\offset + 0 * STORSIZE)(\dst), \fixup) | ||
60 | EX(LONG_S, \val, (\offset + 1 * STORSIZE)(\dst), \fixup) | ||
61 | EX(LONG_S, \val, (\offset + 2 * STORSIZE)(\dst), \fixup) | ||
62 | EX(LONG_S, \val, (\offset + 3 * STORSIZE)(\dst), \fixup) | ||
63 | #if ((defined(CONFIG_CPU_MICROMIPS) && (LONGSIZE == 4)) || !defined(CONFIG_CPU_MICROMIPS)) | ||
64 | EX(LONG_S, \val, (\offset + 4 * STORSIZE)(\dst), \fixup) | ||
65 | EX(LONG_S, \val, (\offset + 5 * STORSIZE)(\dst), \fixup) | ||
66 | EX(LONG_S, \val, (\offset + 6 * STORSIZE)(\dst), \fixup) | ||
67 | EX(LONG_S, \val, (\offset + 7 * STORSIZE)(\dst), \fixup) | ||
68 | #endif | ||
69 | #if (!defined(CONFIG_CPU_MICROMIPS) && (LONGSIZE == 4)) | ||
70 | EX(LONG_S, \val, (\offset + 8 * STORSIZE)(\dst), \fixup) | ||
71 | EX(LONG_S, \val, (\offset + 9 * STORSIZE)(\dst), \fixup) | ||
72 | EX(LONG_S, \val, (\offset + 10 * STORSIZE)(\dst), \fixup) | ||
73 | EX(LONG_S, \val, (\offset + 11 * STORSIZE)(\dst), \fixup) | ||
74 | EX(LONG_S, \val, (\offset + 12 * STORSIZE)(\dst), \fixup) | ||
75 | EX(LONG_S, \val, (\offset + 13 * STORSIZE)(\dst), \fixup) | ||
76 | EX(LONG_S, \val, (\offset + 14 * STORSIZE)(\dst), \fixup) | ||
77 | EX(LONG_S, \val, (\offset + 15 * STORSIZE)(\dst), \fixup) | ||
78 | #endif | ||
79 | .endm | ||
80 | |||
81 | .align 5 | ||
82 | |||
83 | /* | ||
84 | * Macro to generate the __bzero{,_user} symbol | ||
85 | * Arguments: | ||
86 | * mode: LEGACY_MODE or EVA_MODE | ||
87 | */ | ||
88 | .macro __BUILD_BZERO mode | ||
89 | /* Initialize __memset if this is the first time we call this macro */ | ||
90 | .ifnotdef __memset | ||
91 | .set __memset, 1 | ||
92 | .hidden __memset /* Make sure it does not leak */ | ||
93 | .endif | ||
94 | |||
95 | sltiu t0, a2, STORSIZE /* very small region? */ | ||
96 | .set noreorder | ||
97 | bnez t0, .Lsmall_memset\@ | ||
98 | andi t0, a0, STORMASK /* aligned? */ | ||
99 | .set reorder | ||
100 | |||
101 | #ifdef CONFIG_CPU_MICROMIPS | ||
102 | move t8, a1 /* used by 'swp' instruction */ | ||
103 | move t9, a1 | ||
104 | #endif | ||
105 | .set noreorder | ||
106 | #ifndef CONFIG_CPU_DADDI_WORKAROUNDS | ||
107 | beqz t0, 1f | ||
108 | PTR_SUBU t0, STORSIZE /* alignment in bytes */ | ||
109 | #else | ||
110 | .set noat | ||
111 | li AT, STORSIZE | ||
112 | beqz t0, 1f | ||
113 | PTR_SUBU t0, AT /* alignment in bytes */ | ||
114 | .set at | ||
115 | #endif | ||
116 | .set reorder | ||
117 | |||
118 | #ifndef CONFIG_CPU_NO_LOAD_STORE_LR | ||
119 | R10KCBARRIER(0(ra)) | ||
120 | #ifdef __MIPSEB__ | ||
121 | EX(LONG_S_L, a1, (a0), .Lfirst_fixup\@) /* make word/dword aligned */ | ||
122 | #else | ||
123 | EX(LONG_S_R, a1, (a0), .Lfirst_fixup\@) /* make word/dword aligned */ | ||
124 | #endif | ||
125 | PTR_SUBU a0, t0 /* long align ptr */ | ||
126 | PTR_ADDU a2, t0 /* correct size */ | ||
127 | |||
128 | #else /* CONFIG_CPU_NO_LOAD_STORE_LR */ | ||
129 | #define STORE_BYTE(N) \ | ||
130 | EX(sb, a1, N(a0), .Lbyte_fixup\@); \ | ||
131 | .set noreorder; \ | ||
132 | beqz t0, 0f; \ | ||
133 | PTR_ADDU t0, 1; \ | ||
134 | .set reorder; | ||
135 | |||
136 | PTR_ADDU a2, t0 /* correct size */ | ||
137 | PTR_ADDU t0, 1 | ||
138 | STORE_BYTE(0) | ||
139 | STORE_BYTE(1) | ||
140 | #if LONGSIZE == 4 | ||
141 | EX(sb, a1, 2(a0), .Lbyte_fixup\@) | ||
142 | #else | ||
143 | STORE_BYTE(2) | ||
144 | STORE_BYTE(3) | ||
145 | STORE_BYTE(4) | ||
146 | STORE_BYTE(5) | ||
147 | EX(sb, a1, 6(a0), .Lbyte_fixup\@) | ||
148 | #endif | ||
149 | 0: | ||
150 | ori a0, STORMASK | ||
151 | xori a0, STORMASK | ||
152 | PTR_ADDIU a0, STORSIZE | ||
153 | #endif /* CONFIG_CPU_NO_LOAD_STORE_LR */ | ||
154 | 1: ori t1, a2, 0x3f /* # of full blocks */ | ||
155 | xori t1, 0x3f | ||
156 | andi t0, a2, 0x40-STORSIZE | ||
157 | beqz t1, .Lmemset_partial\@ /* no block to fill */ | ||
158 | |||
159 | PTR_ADDU t1, a0 /* end address */ | ||
160 | 1: PTR_ADDIU a0, 64 | ||
161 | R10KCBARRIER(0(ra)) | ||
162 | f_fill64 a0, -64, FILL64RG, .Lfwd_fixup\@, \mode | ||
163 | bne t1, a0, 1b | ||
164 | |||
165 | .Lmemset_partial\@: | ||
166 | R10KCBARRIER(0(ra)) | ||
167 | PTR_LA t1, 2f /* where to start */ | ||
168 | #ifdef CONFIG_CPU_MICROMIPS | ||
169 | LONG_SRL t7, t0, 1 | ||
170 | #endif | ||
171 | #if LONGSIZE == 4 | ||
172 | PTR_SUBU t1, FILLPTRG | ||
173 | #else | ||
174 | .set noat | ||
175 | LONG_SRL AT, FILLPTRG, 1 | ||
176 | PTR_SUBU t1, AT | ||
177 | .set at | ||
178 | #endif | ||
179 | PTR_ADDU a0, t0 /* dest ptr */ | ||
180 | jr t1 | ||
181 | |||
182 | /* ... but first do longs ... */ | ||
183 | f_fill64 a0, -64, FILL64RG, .Lpartial_fixup\@, \mode | ||
184 | 2: andi a2, STORMASK /* At most one long to go */ | ||
185 | |||
186 | .set noreorder | ||
187 | beqz a2, 1f | ||
188 | #ifndef CONFIG_CPU_NO_LOAD_STORE_LR | ||
189 | PTR_ADDU a0, a2 /* What's left */ | ||
190 | .set reorder | ||
191 | R10KCBARRIER(0(ra)) | ||
192 | #ifdef __MIPSEB__ | ||
193 | EX(LONG_S_R, a1, -1(a0), .Llast_fixup\@) | ||
194 | #else | ||
195 | EX(LONG_S_L, a1, -1(a0), .Llast_fixup\@) | ||
196 | #endif | ||
197 | #else /* CONFIG_CPU_NO_LOAD_STORE_LR */ | ||
198 | PTR_SUBU t0, $0, a2 | ||
199 | .set reorder | ||
200 | move a2, zero /* No remaining longs */ | ||
201 | PTR_ADDIU t0, 1 | ||
202 | STORE_BYTE(0) | ||
203 | STORE_BYTE(1) | ||
204 | #if LONGSIZE == 4 | ||
205 | EX(sb, a1, 2(a0), .Lbyte_fixup\@) | ||
206 | #else | ||
207 | STORE_BYTE(2) | ||
208 | STORE_BYTE(3) | ||
209 | STORE_BYTE(4) | ||
210 | STORE_BYTE(5) | ||
211 | EX(sb, a1, 6(a0), .Lbyte_fixup\@) | ||
212 | #endif | ||
213 | 0: | ||
214 | #endif /* CONFIG_CPU_NO_LOAD_STORE_LR */ | ||
215 | 1: move a2, zero | ||
216 | jr ra | ||
217 | |||
218 | .Lsmall_memset\@: | ||
219 | PTR_ADDU t1, a0, a2 | ||
220 | beqz a2, 2f | ||
221 | |||
222 | 1: PTR_ADDIU a0, 1 /* fill bytewise */ | ||
223 | R10KCBARRIER(0(ra)) | ||
224 | .set noreorder | ||
225 | bne t1, a0, 1b | ||
226 | EX(sb, a1, -1(a0), .Lsmall_fixup\@) | ||
227 | .set reorder | ||
228 | |||
229 | 2: move a2, zero | ||
230 | jr ra /* done */ | ||
231 | .if __memset == 1 | ||
232 | END(memset) | ||
233 | .set __memset, 0 | ||
234 | .hidden __memset | ||
235 | .endif | ||
236 | |||
237 | #ifdef CONFIG_CPU_NO_LOAD_STORE_LR | ||
238 | .Lbyte_fixup\@: | ||
239 | /* | ||
240 | * unset_bytes = (#bytes - (#unaligned bytes)) - (-#unaligned bytes remaining + 1) + 1 | ||
241 | * a2 = a2 - t0 + 1 | ||
242 | */ | ||
243 | PTR_SUBU a2, t0 | ||
244 | PTR_ADDIU a2, 1 | ||
245 | jr ra | ||
246 | #endif /* CONFIG_CPU_NO_LOAD_STORE_LR */ | ||
247 | |||
248 | .Lfirst_fixup\@: | ||
249 | /* unset_bytes already in a2 */ | ||
250 | jr ra | ||
251 | |||
252 | .Lfwd_fixup\@: | ||
253 | /* | ||
254 | * unset_bytes = partial_start_addr + #bytes - fault_addr | ||
255 | * a2 = t1 + (a2 & 3f) - $28->task->BUADDR | ||
256 | */ | ||
257 | PTR_L t0, TI_TASK($28) | ||
258 | andi a2, 0x3f | ||
259 | LONG_L t0, THREAD_BUADDR(t0) | ||
260 | LONG_ADDU a2, t1 | ||
261 | LONG_SUBU a2, t0 | ||
262 | jr ra | ||
263 | |||
264 | .Lpartial_fixup\@: | ||
265 | /* | ||
266 | * unset_bytes = partial_end_addr + #bytes - fault_addr | ||
267 | * a2 = a0 + (a2 & STORMASK) - $28->task->BUADDR | ||
268 | */ | ||
269 | PTR_L t0, TI_TASK($28) | ||
270 | andi a2, STORMASK | ||
271 | LONG_L t0, THREAD_BUADDR(t0) | ||
272 | LONG_ADDU a2, a0 | ||
273 | LONG_SUBU a2, t0 | ||
274 | jr ra | ||
275 | |||
276 | .Llast_fixup\@: | ||
277 | /* unset_bytes already in a2 */ | ||
278 | jr ra | ||
279 | |||
280 | .Lsmall_fixup\@: | ||
281 | /* | ||
282 | * unset_bytes = end_addr - current_addr + 1 | ||
283 | * a2 = t1 - a0 + 1 | ||
284 | */ | ||
285 | PTR_SUBU a2, t1, a0 | ||
286 | PTR_ADDIU a2, 1 | ||
287 | jr ra | ||
288 | |||
289 | .endm | ||
290 | |||
291 | /* | ||
292 | * memset(void *s, int c, size_t n) | ||
293 | * | ||
294 | * a0: start of area to clear | ||
295 | * a1: char to fill with | ||
296 | * a2: size of area to clear | ||
297 | */ | ||
298 | |||
299 | LEAF(memset) | ||
300 | EXPORT_SYMBOL(memset) | ||
301 | move v0, a0 /* result */ | ||
302 | beqz a1, 1f | ||
303 | |||
304 | andi a1, 0xff /* spread fillword */ | ||
305 | LONG_SLL t1, a1, 8 | ||
306 | or a1, t1 | ||
307 | LONG_SLL t1, a1, 16 | ||
308 | #if LONGSIZE == 8 | ||
309 | or a1, t1 | ||
310 | LONG_SLL t1, a1, 32 | ||
311 | #endif | ||
312 | or a1, t1 | ||
313 | 1: | ||
314 | #ifndef CONFIG_EVA | ||
315 | FEXPORT(__bzero) | ||
316 | EXPORT_SYMBOL(__bzero) | ||
317 | #else | ||
318 | FEXPORT(__bzero_kernel) | ||
319 | EXPORT_SYMBOL(__bzero_kernel) | ||
320 | #endif | ||
321 | __BUILD_BZERO LEGACY_MODE | ||
322 | |||
323 | #ifdef CONFIG_EVA | ||
324 | LEAF(__bzero) | ||
325 | EXPORT_SYMBOL(__bzero) | ||
326 | __BUILD_BZERO EVA_MODE | ||
327 | END(__bzero) | ||
328 | #endif | ||
diff --git a/arch/mips/lib/mips-atomic.c b/arch/mips/lib/mips-atomic.c new file mode 100644 index 000000000..a9b72eacf --- /dev/null +++ b/arch/mips/lib/mips-atomic.c | |||
@@ -0,0 +1,113 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 1994, 95, 96, 97, 98, 99, 2003 by Ralf Baechle | ||
7 | * Copyright (C) 1996 by Paul M. Antoine | ||
8 | * Copyright (C) 1999 Silicon Graphics | ||
9 | * Copyright (C) 2000 MIPS Technologies, Inc. | ||
10 | */ | ||
11 | #include <asm/irqflags.h> | ||
12 | #include <asm/hazards.h> | ||
13 | #include <linux/compiler.h> | ||
14 | #include <linux/preempt.h> | ||
15 | #include <linux/export.h> | ||
16 | #include <linux/stringify.h> | ||
17 | |||
18 | #if !defined(CONFIG_CPU_HAS_DIEI) | ||
19 | |||
20 | /* | ||
21 | * For cli() we have to insert nops to make sure that the new value | ||
22 | * has actually arrived in the status register before the end of this | ||
23 | * macro. | ||
24 | * R4000/R4400 need three nops, the R4600 two nops and the R10000 needs | ||
25 | * no nops at all. | ||
26 | */ | ||
27 | /* | ||
28 | * For TX49, operating only IE bit is not enough. | ||
29 | * | ||
30 | * If mfc0 $12 follows store and the mfc0 is last instruction of a | ||
31 | * page and fetching the next instruction causes TLB miss, the result | ||
32 | * of the mfc0 might wrongly contain EXL bit. | ||
33 | * | ||
34 | * ERT-TX49H2-027, ERT-TX49H3-012, ERT-TX49HL3-006, ERT-TX49H4-008 | ||
35 | * | ||
36 | * Workaround: mask EXL bit of the result or place a nop before mfc0. | ||
37 | */ | ||
38 | notrace void arch_local_irq_disable(void) | ||
39 | { | ||
40 | preempt_disable_notrace(); | ||
41 | |||
42 | __asm__ __volatile__( | ||
43 | " .set push \n" | ||
44 | " .set noat \n" | ||
45 | " mfc0 $1,$12 \n" | ||
46 | " ori $1,0x1f \n" | ||
47 | " xori $1,0x1f \n" | ||
48 | " .set noreorder \n" | ||
49 | " mtc0 $1,$12 \n" | ||
50 | " " __stringify(__irq_disable_hazard) " \n" | ||
51 | " .set pop \n" | ||
52 | : /* no outputs */ | ||
53 | : /* no inputs */ | ||
54 | : "memory"); | ||
55 | |||
56 | preempt_enable_notrace(); | ||
57 | } | ||
58 | EXPORT_SYMBOL(arch_local_irq_disable); | ||
59 | |||
60 | notrace unsigned long arch_local_irq_save(void) | ||
61 | { | ||
62 | unsigned long flags; | ||
63 | |||
64 | preempt_disable_notrace(); | ||
65 | |||
66 | __asm__ __volatile__( | ||
67 | " .set push \n" | ||
68 | " .set reorder \n" | ||
69 | " .set noat \n" | ||
70 | " mfc0 %[flags], $12 \n" | ||
71 | " ori $1, %[flags], 0x1f \n" | ||
72 | " xori $1, 0x1f \n" | ||
73 | " .set noreorder \n" | ||
74 | " mtc0 $1, $12 \n" | ||
75 | " " __stringify(__irq_disable_hazard) " \n" | ||
76 | " .set pop \n" | ||
77 | : [flags] "=r" (flags) | ||
78 | : /* no inputs */ | ||
79 | : "memory"); | ||
80 | |||
81 | preempt_enable_notrace(); | ||
82 | |||
83 | return flags; | ||
84 | } | ||
85 | EXPORT_SYMBOL(arch_local_irq_save); | ||
86 | |||
87 | notrace void arch_local_irq_restore(unsigned long flags) | ||
88 | { | ||
89 | unsigned long __tmp1; | ||
90 | |||
91 | preempt_disable_notrace(); | ||
92 | |||
93 | __asm__ __volatile__( | ||
94 | " .set push \n" | ||
95 | " .set noreorder \n" | ||
96 | " .set noat \n" | ||
97 | " mfc0 $1, $12 \n" | ||
98 | " andi %[flags], 1 \n" | ||
99 | " ori $1, 0x1f \n" | ||
100 | " xori $1, 0x1f \n" | ||
101 | " or %[flags], $1 \n" | ||
102 | " mtc0 %[flags], $12 \n" | ||
103 | " " __stringify(__irq_disable_hazard) " \n" | ||
104 | " .set pop \n" | ||
105 | : [flags] "=r" (__tmp1) | ||
106 | : "0" (flags) | ||
107 | : "memory"); | ||
108 | |||
109 | preempt_enable_notrace(); | ||
110 | } | ||
111 | EXPORT_SYMBOL(arch_local_irq_restore); | ||
112 | |||
113 | #endif /* !CONFIG_CPU_HAS_DIEI */ | ||
diff --git a/arch/mips/lib/multi3.c b/arch/mips/lib/multi3.c new file mode 100644 index 000000000..4c2483f41 --- /dev/null +++ b/arch/mips/lib/multi3.c | |||
@@ -0,0 +1,54 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | #include <linux/export.h> | ||
3 | |||
4 | #include "libgcc.h" | ||
5 | |||
6 | /* | ||
7 | * GCC 7 & older can suboptimally generate __multi3 calls for mips64r6, so for | ||
8 | * that specific case only we implement that intrinsic here. | ||
9 | * | ||
10 | * See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82981 | ||
11 | */ | ||
12 | #if defined(CONFIG_64BIT) && defined(CONFIG_CPU_MIPSR6) && (__GNUC__ < 8) | ||
13 | |||
14 | /* multiply 64-bit values, low 64-bits returned */ | ||
15 | static inline long long notrace dmulu(long long a, long long b) | ||
16 | { | ||
17 | long long res; | ||
18 | |||
19 | asm ("dmulu %0,%1,%2" : "=r" (res) : "r" (a), "r" (b)); | ||
20 | return res; | ||
21 | } | ||
22 | |||
23 | /* multiply 64-bit unsigned values, high 64-bits of 128-bit result returned */ | ||
24 | static inline long long notrace dmuhu(long long a, long long b) | ||
25 | { | ||
26 | long long res; | ||
27 | |||
28 | asm ("dmuhu %0,%1,%2" : "=r" (res) : "r" (a), "r" (b)); | ||
29 | return res; | ||
30 | } | ||
31 | |||
32 | /* multiply 128-bit values, low 128-bits returned */ | ||
33 | ti_type notrace __multi3(ti_type a, ti_type b) | ||
34 | { | ||
35 | TWunion res, aa, bb; | ||
36 | |||
37 | aa.ti = a; | ||
38 | bb.ti = b; | ||
39 | |||
40 | /* | ||
41 | * a * b = (a.lo * b.lo) | ||
42 | * + 2^64 * (a.hi * b.lo + a.lo * b.hi) | ||
43 | * [+ 2^128 * (a.hi * b.hi)] | ||
44 | */ | ||
45 | res.s.low = dmulu(aa.s.low, bb.s.low); | ||
46 | res.s.high = dmuhu(aa.s.low, bb.s.low); | ||
47 | res.s.high += dmulu(aa.s.high, bb.s.low); | ||
48 | res.s.high += dmulu(aa.s.low, bb.s.high); | ||
49 | |||
50 | return res.ti; | ||
51 | } | ||
52 | EXPORT_SYMBOL(__multi3); | ||
53 | |||
54 | #endif /* 64BIT && CPU_MIPSR6 && GCC7 */ | ||
diff --git a/arch/mips/lib/r3k_dump_tlb.c b/arch/mips/lib/r3k_dump_tlb.c new file mode 100644 index 000000000..10b4bf7f7 --- /dev/null +++ b/arch/mips/lib/r3k_dump_tlb.c | |||
@@ -0,0 +1,75 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Dump R3000 TLB for debugging purposes. | ||
4 | * | ||
5 | * Copyright (C) 1994, 1995 by Waldorf Electronics, written by Ralf Baechle. | ||
6 | * Copyright (C) 1999 by Silicon Graphics, Inc. | ||
7 | * Copyright (C) 1999 by Harald Koerfgen | ||
8 | */ | ||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/mm.h> | ||
11 | |||
12 | #include <asm/mipsregs.h> | ||
13 | #include <asm/mmu_context.h> | ||
14 | #include <asm/page.h> | ||
15 | #include <asm/tlbdebug.h> | ||
16 | |||
17 | extern int r3k_have_wired_reg; | ||
18 | |||
19 | void dump_tlb_regs(void) | ||
20 | { | ||
21 | pr_info("Index : %0x\n", read_c0_index()); | ||
22 | pr_info("EntryHi : %0lx\n", read_c0_entryhi()); | ||
23 | pr_info("EntryLo : %0lx\n", read_c0_entrylo0()); | ||
24 | if (r3k_have_wired_reg) | ||
25 | pr_info("Wired : %0x\n", read_c0_wired()); | ||
26 | } | ||
27 | |||
28 | static void dump_tlb(int first, int last) | ||
29 | { | ||
30 | int i; | ||
31 | unsigned int asid; | ||
32 | unsigned long entryhi, entrylo0, asid_mask; | ||
33 | |||
34 | asid_mask = cpu_asid_mask(¤t_cpu_data); | ||
35 | asid = read_c0_entryhi() & asid_mask; | ||
36 | |||
37 | for (i = first; i <= last; i++) { | ||
38 | write_c0_index(i<<8); | ||
39 | __asm__ __volatile__( | ||
40 | ".set\tnoreorder\n\t" | ||
41 | "tlbr\n\t" | ||
42 | "nop\n\t" | ||
43 | ".set\treorder"); | ||
44 | entryhi = read_c0_entryhi(); | ||
45 | entrylo0 = read_c0_entrylo0(); | ||
46 | |||
47 | /* Unused entries have a virtual address of KSEG0. */ | ||
48 | if ((entryhi & PAGE_MASK) != KSEG0 && | ||
49 | (entrylo0 & R3K_ENTRYLO_G || | ||
50 | (entryhi & asid_mask) == asid)) { | ||
51 | /* | ||
52 | * Only print entries in use | ||
53 | */ | ||
54 | printk("Index: %2d ", i); | ||
55 | |||
56 | pr_cont("va=%08lx asid=%08lx" | ||
57 | " [pa=%06lx n=%d d=%d v=%d g=%d]", | ||
58 | entryhi & PAGE_MASK, | ||
59 | entryhi & asid_mask, | ||
60 | entrylo0 & PAGE_MASK, | ||
61 | (entrylo0 & R3K_ENTRYLO_N) ? 1 : 0, | ||
62 | (entrylo0 & R3K_ENTRYLO_D) ? 1 : 0, | ||
63 | (entrylo0 & R3K_ENTRYLO_V) ? 1 : 0, | ||
64 | (entrylo0 & R3K_ENTRYLO_G) ? 1 : 0); | ||
65 | } | ||
66 | } | ||
67 | printk("\n"); | ||
68 | |||
69 | write_c0_entryhi(asid); | ||
70 | } | ||
71 | |||
72 | void dump_tlb_all(void) | ||
73 | { | ||
74 | dump_tlb(0, current_cpu_data.tlbsize - 1); | ||
75 | } | ||
diff --git a/arch/mips/lib/strncpy_user.S b/arch/mips/lib/strncpy_user.S new file mode 100644 index 000000000..acdff66bd --- /dev/null +++ b/arch/mips/lib/strncpy_user.S | |||
@@ -0,0 +1,85 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 1996, 1999 by Ralf Baechle | ||
7 | * Copyright (C) 2011 MIPS Technologies, Inc. | ||
8 | */ | ||
9 | #include <linux/errno.h> | ||
10 | #include <asm/asm.h> | ||
11 | #include <asm/asm-offsets.h> | ||
12 | #include <asm/export.h> | ||
13 | #include <asm/regdef.h> | ||
14 | |||
15 | #define EX(insn,reg,addr,handler) \ | ||
16 | 9: insn reg, addr; \ | ||
17 | .section __ex_table,"a"; \ | ||
18 | PTR 9b, handler; \ | ||
19 | .previous | ||
20 | |||
21 | /* | ||
22 | * Returns: -EFAULT if exception before terminator, N if the entire | ||
23 | * buffer filled, else strlen. | ||
24 | */ | ||
25 | |||
26 | /* | ||
27 | * Ugly special case have to check: we might get passed a user space | ||
28 | * pointer which wraps into the kernel space. We don't deal with that. If | ||
29 | * it happens at most some bytes of the exceptions handlers will be copied. | ||
30 | */ | ||
31 | |||
32 | .macro __BUILD_STRNCPY_ASM func | ||
33 | LEAF(__strncpy_from_\func\()_asm) | ||
34 | LONG_L v0, TI_ADDR_LIMIT($28) # pointer ok? | ||
35 | and v0, a1 | ||
36 | bnez v0, .Lfault\@ | ||
37 | |||
38 | move t0, zero | ||
39 | move v1, a1 | ||
40 | .ifeqs "\func","kernel" | ||
41 | 1: EX(lbu, v0, (v1), .Lfault\@) | ||
42 | .else | ||
43 | 1: EX(lbue, v0, (v1), .Lfault\@) | ||
44 | .endif | ||
45 | PTR_ADDIU v1, 1 | ||
46 | R10KCBARRIER(0(ra)) | ||
47 | sb v0, (a0) | ||
48 | beqz v0, 2f | ||
49 | PTR_ADDIU t0, 1 | ||
50 | PTR_ADDIU a0, 1 | ||
51 | bne t0, a2, 1b | ||
52 | 2: PTR_ADDU v0, a1, t0 | ||
53 | xor v0, a1 | ||
54 | bltz v0, .Lfault\@ | ||
55 | move v0, t0 | ||
56 | jr ra # return n | ||
57 | END(__strncpy_from_\func\()_asm) | ||
58 | |||
59 | .Lfault\@: | ||
60 | li v0, -EFAULT | ||
61 | jr ra | ||
62 | |||
63 | .section __ex_table,"a" | ||
64 | PTR 1b, .Lfault\@ | ||
65 | .previous | ||
66 | |||
67 | .endm | ||
68 | |||
69 | #ifndef CONFIG_EVA | ||
70 | /* Set aliases */ | ||
71 | .global __strncpy_from_user_asm | ||
72 | .set __strncpy_from_user_asm, __strncpy_from_kernel_asm | ||
73 | EXPORT_SYMBOL(__strncpy_from_user_asm) | ||
74 | #endif | ||
75 | |||
76 | __BUILD_STRNCPY_ASM kernel | ||
77 | EXPORT_SYMBOL(__strncpy_from_kernel_asm) | ||
78 | |||
79 | #ifdef CONFIG_EVA | ||
80 | .set push | ||
81 | .set eva | ||
82 | __BUILD_STRNCPY_ASM user | ||
83 | .set pop | ||
84 | EXPORT_SYMBOL(__strncpy_from_user_asm) | ||
85 | #endif | ||
diff --git a/arch/mips/lib/strnlen_user.S b/arch/mips/lib/strnlen_user.S new file mode 100644 index 000000000..e1bacf5a3 --- /dev/null +++ b/arch/mips/lib/strnlen_user.S | |||
@@ -0,0 +1,84 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (c) 1996, 1998, 1999, 2004 by Ralf Baechle | ||
7 | * Copyright (c) 1999 Silicon Graphics, Inc. | ||
8 | */ | ||
9 | #include <asm/asm.h> | ||
10 | #include <asm/asm-offsets.h> | ||
11 | #include <asm/export.h> | ||
12 | #include <asm/regdef.h> | ||
13 | |||
14 | #define EX(insn,reg,addr,handler) \ | ||
15 | 9: insn reg, addr; \ | ||
16 | .section __ex_table,"a"; \ | ||
17 | PTR 9b, handler; \ | ||
18 | .previous | ||
19 | |||
20 | /* | ||
21 | * Return the size of a string including the ending NUL character up to a | ||
22 | * maximum of a1 or 0 in case of error. | ||
23 | * | ||
24 | * Note: for performance reasons we deliberately accept that a user may | ||
25 | * make strlen_user and strnlen_user access the first few KSEG0 | ||
26 | * bytes. There's nothing secret there. On 64-bit accessing beyond | ||
27 | * the maximum is a tad hairier ... | ||
28 | */ | ||
29 | .macro __BUILD_STRNLEN_ASM func | ||
30 | LEAF(__strnlen_\func\()_asm) | ||
31 | LONG_L v0, TI_ADDR_LIMIT($28) # pointer ok? | ||
32 | and v0, a0 | ||
33 | bnez v0, .Lfault\@ | ||
34 | |||
35 | move v0, a0 | ||
36 | PTR_ADDU a1, a0 # stop pointer | ||
37 | 1: | ||
38 | #ifdef CONFIG_CPU_DADDI_WORKAROUNDS | ||
39 | .set noat | ||
40 | li AT, 1 | ||
41 | #endif | ||
42 | beq v0, a1, 1f # limit reached? | ||
43 | .ifeqs "\func", "kernel" | ||
44 | EX(lb, t0, (v0), .Lfault\@) | ||
45 | .else | ||
46 | EX(lbe, t0, (v0), .Lfault\@) | ||
47 | .endif | ||
48 | .set noreorder | ||
49 | bnez t0, 1b | ||
50 | 1: | ||
51 | #ifndef CONFIG_CPU_DADDI_WORKAROUNDS | ||
52 | PTR_ADDIU v0, 1 | ||
53 | #else | ||
54 | PTR_ADDU v0, AT | ||
55 | .set at | ||
56 | #endif | ||
57 | .set reorder | ||
58 | PTR_SUBU v0, a0 | ||
59 | jr ra | ||
60 | END(__strnlen_\func\()_asm) | ||
61 | |||
62 | .Lfault\@: | ||
63 | move v0, zero | ||
64 | jr ra | ||
65 | .endm | ||
66 | |||
67 | #ifndef CONFIG_EVA | ||
68 | /* Set aliases */ | ||
69 | .global __strnlen_user_asm | ||
70 | .set __strnlen_user_asm, __strnlen_kernel_asm | ||
71 | EXPORT_SYMBOL(__strnlen_user_asm) | ||
72 | #endif | ||
73 | |||
74 | __BUILD_STRNLEN_ASM kernel | ||
75 | EXPORT_SYMBOL(__strnlen_kernel_asm) | ||
76 | |||
77 | #ifdef CONFIG_EVA | ||
78 | |||
79 | .set push | ||
80 | .set eva | ||
81 | __BUILD_STRNLEN_ASM user | ||
82 | .set pop | ||
83 | EXPORT_SYMBOL(__strnlen_user_asm) | ||
84 | #endif | ||
diff --git a/arch/mips/lib/uncached.c b/arch/mips/lib/uncached.c new file mode 100644 index 000000000..f80a67c09 --- /dev/null +++ b/arch/mips/lib/uncached.c | |||
@@ -0,0 +1,82 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 2005 Thiemo Seufer | ||
7 | * Copyright (C) 2005 MIPS Technologies, Inc. All rights reserved. | ||
8 | * Author: Maciej W. Rozycki <macro@mips.com> | ||
9 | */ | ||
10 | |||
11 | |||
12 | #include <asm/addrspace.h> | ||
13 | #include <asm/bug.h> | ||
14 | #include <asm/cacheflush.h> | ||
15 | |||
16 | #ifndef CKSEG2 | ||
17 | #define CKSEG2 CKSSEG | ||
18 | #endif | ||
19 | #ifndef TO_PHYS_MASK | ||
20 | #define TO_PHYS_MASK -1 | ||
21 | #endif | ||
22 | |||
23 | /* | ||
24 | * FUNC is executed in one of the uncached segments, depending on its | ||
25 | * original address as follows: | ||
26 | * | ||
27 | * 1. If the original address is in CKSEG0 or CKSEG1, then the uncached | ||
28 | * segment used is CKSEG1. | ||
29 | * 2. If the original address is in XKPHYS, then the uncached segment | ||
30 | * used is XKPHYS(2). | ||
31 | * 3. Otherwise it's a bug. | ||
32 | * | ||
33 | * The same remapping is done with the stack pointer. Stack handling | ||
34 | * works because we don't handle stack arguments or more complex return | ||
35 | * values, so we can avoid sharing the same stack area between a cached | ||
36 | * and the uncached mode. | ||
37 | */ | ||
38 | unsigned long run_uncached(void *func) | ||
39 | { | ||
40 | register long ret __asm__("$2"); | ||
41 | long lfunc = (long)func, ufunc; | ||
42 | long usp; | ||
43 | long sp; | ||
44 | |||
45 | __asm__("move %0, $sp" : "=r" (sp)); | ||
46 | |||
47 | if (sp >= (long)CKSEG0 && sp < (long)CKSEG2) | ||
48 | usp = CKSEG1ADDR(sp); | ||
49 | #ifdef CONFIG_64BIT | ||
50 | else if ((long long)sp >= (long long)PHYS_TO_XKPHYS(0, 0) && | ||
51 | (long long)sp < (long long)PHYS_TO_XKPHYS(8, 0)) | ||
52 | usp = PHYS_TO_XKPHYS(K_CALG_UNCACHED, | ||
53 | XKPHYS_TO_PHYS((long long)sp)); | ||
54 | #endif | ||
55 | else { | ||
56 | BUG(); | ||
57 | usp = sp; | ||
58 | } | ||
59 | if (lfunc >= (long)CKSEG0 && lfunc < (long)CKSEG2) | ||
60 | ufunc = CKSEG1ADDR(lfunc); | ||
61 | #ifdef CONFIG_64BIT | ||
62 | else if ((long long)lfunc >= (long long)PHYS_TO_XKPHYS(0, 0) && | ||
63 | (long long)lfunc < (long long)PHYS_TO_XKPHYS(8, 0)) | ||
64 | ufunc = PHYS_TO_XKPHYS(K_CALG_UNCACHED, | ||
65 | XKPHYS_TO_PHYS((long long)lfunc)); | ||
66 | #endif | ||
67 | else { | ||
68 | BUG(); | ||
69 | ufunc = lfunc; | ||
70 | } | ||
71 | |||
72 | __asm__ __volatile__ ( | ||
73 | " move $16, $sp\n" | ||
74 | " move $sp, %1\n" | ||
75 | " jalr %2\n" | ||
76 | " move $sp, $16" | ||
77 | : "=r" (ret) | ||
78 | : "r" (usp), "r" (ufunc) | ||
79 | : "$16", "$31"); | ||
80 | |||
81 | return ret; | ||
82 | } | ||