Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Handle unaligned accesses by emulation. | |
3 | * | |
4 | * This file is subject to the terms and conditions of the GNU General Public | |
5 | * License. See the file "COPYING" in the main directory of this archive | |
6 | * for more details. | |
7 | * | |
8 | * Copyright (C) 1996, 1998, 1999, 2002 by Ralf Baechle | |
9 | * Copyright (C) 1999 Silicon Graphics, Inc. | |
9d8e5736 | 10 | * Copyright (C) 2014 Imagination Technologies Ltd. |
1da177e4 LT |
11 | * |
12 | * This file contains exception handler for address error exception with the | |
13 | * special capability to execute faulting instructions in software. The | |
14 | * handler does not try to handle the case when the program counter points | |
15 | * to an address not aligned to a word boundary. | |
16 | * | |
17 | * Putting data to unaligned addresses is a bad practice even on Intel where | |
18 | * only the performance is affected. Much worse is that such code is non- | |
19 | * portable. Due to several programs that die on MIPS due to alignment | |
20 | * problems I decided to implement this handler anyway though I originally | |
21 | * didn't intend to do this at all for user code. | |
22 | * | |
23 | * For now I enable fixing of address errors by default to make life easier. | |
24 | * I however intend to disable this somewhen in the future when the alignment | |
70342287 | 25 | * problems with user programs have been fixed. For programmers this is the |
1da177e4 LT |
26 | * right way to go. |
27 | * | |
28 | * Fixing address errors is a per process option. The option is inherited | |
70342287 | 29 | * across fork(2) and execve(2) calls. If you really want to use the |
1da177e4 LT |
30 | * option in your user programs - I discourage the use of the software |
31 | * emulation strongly - use the following code in your userland stuff: | |
32 | * | |
33 | * #include <sys/sysmips.h> | |
34 | * | |
35 | * ... | |
36 | * sysmips(MIPS_FIXADE, x); | |
37 | * ... | |
38 | * | |
39 | * The argument x is 0 for disabling software emulation, enabled otherwise. | |
40 | * | |
41 | * Below a little program to play around with this feature. | |
42 | * | |
43 | * #include <stdio.h> | |
44 | * #include <sys/sysmips.h> | |
45 | * | |
46 | * struct foo { | |
70342287 | 47 | * unsigned char bar[8]; |
1da177e4 LT |
48 | * }; |
49 | * | |
50 | * main(int argc, char *argv[]) | |
51 | * { | |
70342287 RB |
52 | * struct foo x = {0, 1, 2, 3, 4, 5, 6, 7}; |
53 | * unsigned int *p = (unsigned int *) (x.bar + 3); | |
54 | * int i; | |
1da177e4 | 55 | * |
70342287 RB |
56 | * if (argc > 1) |
57 | * sysmips(MIPS_FIXADE, atoi(argv[1])); | |
1da177e4 | 58 | * |
70342287 | 59 | * printf("*p = %08lx\n", *p); |
1da177e4 | 60 | * |
70342287 | 61 | * *p = 0xdeadface; |
1da177e4 | 62 | * |
70342287 RB |
63 | * for(i = 0; i <= 7; i++) |
64 | * printf("%02x ", x.bar[i]); | |
65 | * printf("\n"); | |
1da177e4 LT |
66 | * } |
67 | * | |
68 | * Coprocessor loads are not supported; I think this case is unimportant | |
69 | * in the practice. | |
70 | * | |
71 | * TODO: Handle ndc (attempted store to doubleword in uncached memory) | |
70342287 RB |
72 | * exception for the R6000. |
73 | * A store crossing a page boundary might be executed only partially. | |
74 | * Undo the partial store in this case. | |
1da177e4 | 75 | */ |
c3fc5cd5 | 76 | #include <linux/context_tracking.h> |
1da177e4 | 77 | #include <linux/mm.h> |
1da177e4 LT |
78 | #include <linux/signal.h> |
79 | #include <linux/smp.h> | |
e8edc6e0 | 80 | #include <linux/sched.h> |
6312e0ee | 81 | #include <linux/debugfs.h> |
7f788d2d DCZ |
82 | #include <linux/perf_event.h> |
83 | ||
1da177e4 LT |
84 | #include <asm/asm.h> |
85 | #include <asm/branch.h> | |
86 | #include <asm/byteorder.h> | |
69f3a7de | 87 | #include <asm/cop2.h> |
102cedc3 LY |
88 | #include <asm/fpu.h> |
89 | #include <asm/fpu_emulator.h> | |
1da177e4 LT |
90 | #include <asm/inst.h> |
91 | #include <asm/uaccess.h> | |
1da177e4 | 92 | |
70342287 | 93 | #define STR(x) __STR(x) |
1da177e4 LT |
94 | #define __STR(x) #x |
95 | ||
6312e0ee AN |
96 | enum { |
97 | UNALIGNED_ACTION_QUIET, | |
98 | UNALIGNED_ACTION_SIGNAL, | |
99 | UNALIGNED_ACTION_SHOW, | |
100 | }; | |
101 | #ifdef CONFIG_DEBUG_FS | |
102 | static u32 unaligned_instructions; | |
103 | static u32 unaligned_action; | |
104 | #else | |
105 | #define unaligned_action UNALIGNED_ACTION_QUIET | |
1da177e4 | 106 | #endif |
6312e0ee | 107 | extern void show_registers(struct pt_regs *regs); |
1da177e4 | 108 | |
34c2f668 | 109 | #ifdef __BIG_ENDIAN |
eeb53895 | 110 | #define _LoadHW(addr, value, res, type) \ |
3563c32d | 111 | do { \ |
34c2f668 | 112 | __asm__ __volatile__ (".set\tnoat\n" \ |
eeb53895 MC |
113 | "1:\t"type##_lb("%0", "0(%2)")"\n" \ |
114 | "2:\t"type##_lbu("$1", "1(%2)")"\n\t"\ | |
34c2f668 LY |
115 | "sll\t%0, 0x8\n\t" \ |
116 | "or\t%0, $1\n\t" \ | |
117 | "li\t%1, 0\n" \ | |
118 | "3:\t.set\tat\n\t" \ | |
119 | ".insn\n\t" \ | |
120 | ".section\t.fixup,\"ax\"\n\t" \ | |
121 | "4:\tli\t%1, %3\n\t" \ | |
122 | "j\t3b\n\t" \ | |
123 | ".previous\n\t" \ | |
124 | ".section\t__ex_table,\"a\"\n\t" \ | |
125 | STR(PTR)"\t1b, 4b\n\t" \ | |
126 | STR(PTR)"\t2b, 4b\n\t" \ | |
127 | ".previous" \ | |
128 | : "=&r" (value), "=r" (res) \ | |
3563c32d MC |
129 | : "r" (addr), "i" (-EFAULT)); \ |
130 | } while(0) | |
34c2f668 | 131 | |
0593a44c | 132 | #ifndef CONFIG_CPU_MIPSR6 |
eeb53895 | 133 | #define _LoadW(addr, value, res, type) \ |
3563c32d | 134 | do { \ |
34c2f668 | 135 | __asm__ __volatile__ ( \ |
eeb53895 MC |
136 | "1:\t"type##_lwl("%0", "(%2)")"\n" \ |
137 | "2:\t"type##_lwr("%0", "3(%2)")"\n\t"\ | |
34c2f668 LY |
138 | "li\t%1, 0\n" \ |
139 | "3:\n\t" \ | |
140 | ".insn\n\t" \ | |
141 | ".section\t.fixup,\"ax\"\n\t" \ | |
142 | "4:\tli\t%1, %3\n\t" \ | |
143 | "j\t3b\n\t" \ | |
144 | ".previous\n\t" \ | |
145 | ".section\t__ex_table,\"a\"\n\t" \ | |
146 | STR(PTR)"\t1b, 4b\n\t" \ | |
147 | STR(PTR)"\t2b, 4b\n\t" \ | |
148 | ".previous" \ | |
149 | : "=&r" (value), "=r" (res) \ | |
3563c32d MC |
150 | : "r" (addr), "i" (-EFAULT)); \ |
151 | } while(0) | |
152 | ||
0593a44c LY |
153 | #else |
154 | /* MIPSR6 has no lwl instruction */ | |
eeb53895 | 155 | #define _LoadW(addr, value, res, type) \ |
3563c32d | 156 | do { \ |
0593a44c LY |
157 | __asm__ __volatile__ ( \ |
158 | ".set\tpush\n" \ | |
159 | ".set\tnoat\n\t" \ | |
eeb53895 MC |
160 | "1:"type##_lb("%0", "0(%2)")"\n\t" \ |
161 | "2:"type##_lbu("$1", "1(%2)")"\n\t" \ | |
0593a44c LY |
162 | "sll\t%0, 0x8\n\t" \ |
163 | "or\t%0, $1\n\t" \ | |
eeb53895 | 164 | "3:"type##_lbu("$1", "2(%2)")"\n\t" \ |
0593a44c LY |
165 | "sll\t%0, 0x8\n\t" \ |
166 | "or\t%0, $1\n\t" \ | |
eeb53895 | 167 | "4:"type##_lbu("$1", "3(%2)")"\n\t" \ |
0593a44c LY |
168 | "sll\t%0, 0x8\n\t" \ |
169 | "or\t%0, $1\n\t" \ | |
170 | "li\t%1, 0\n" \ | |
171 | ".set\tpop\n" \ | |
172 | "10:\n\t" \ | |
173 | ".insn\n\t" \ | |
174 | ".section\t.fixup,\"ax\"\n\t" \ | |
175 | "11:\tli\t%1, %3\n\t" \ | |
176 | "j\t10b\n\t" \ | |
177 | ".previous\n\t" \ | |
178 | ".section\t__ex_table,\"a\"\n\t" \ | |
179 | STR(PTR)"\t1b, 11b\n\t" \ | |
180 | STR(PTR)"\t2b, 11b\n\t" \ | |
181 | STR(PTR)"\t3b, 11b\n\t" \ | |
182 | STR(PTR)"\t4b, 11b\n\t" \ | |
183 | ".previous" \ | |
184 | : "=&r" (value), "=r" (res) \ | |
3563c32d MC |
185 | : "r" (addr), "i" (-EFAULT)); \ |
186 | } while(0) | |
187 | ||
0593a44c | 188 | #endif /* CONFIG_CPU_MIPSR6 */ |
34c2f668 | 189 | |
eeb53895 | 190 | #define _LoadHWU(addr, value, res, type) \ |
3563c32d | 191 | do { \ |
34c2f668 LY |
192 | __asm__ __volatile__ ( \ |
193 | ".set\tnoat\n" \ | |
eeb53895 MC |
194 | "1:\t"type##_lbu("%0", "0(%2)")"\n" \ |
195 | "2:\t"type##_lbu("$1", "1(%2)")"\n\t"\ | |
34c2f668 LY |
196 | "sll\t%0, 0x8\n\t" \ |
197 | "or\t%0, $1\n\t" \ | |
198 | "li\t%1, 0\n" \ | |
199 | "3:\n\t" \ | |
200 | ".insn\n\t" \ | |
201 | ".set\tat\n\t" \ | |
202 | ".section\t.fixup,\"ax\"\n\t" \ | |
203 | "4:\tli\t%1, %3\n\t" \ | |
204 | "j\t3b\n\t" \ | |
205 | ".previous\n\t" \ | |
206 | ".section\t__ex_table,\"a\"\n\t" \ | |
207 | STR(PTR)"\t1b, 4b\n\t" \ | |
208 | STR(PTR)"\t2b, 4b\n\t" \ | |
209 | ".previous" \ | |
210 | : "=&r" (value), "=r" (res) \ | |
3563c32d MC |
211 | : "r" (addr), "i" (-EFAULT)); \ |
212 | } while(0) | |
34c2f668 | 213 | |
0593a44c | 214 | #ifndef CONFIG_CPU_MIPSR6 |
eeb53895 | 215 | #define _LoadWU(addr, value, res, type) \ |
3563c32d | 216 | do { \ |
34c2f668 | 217 | __asm__ __volatile__ ( \ |
eeb53895 MC |
218 | "1:\t"type##_lwl("%0", "(%2)")"\n" \ |
219 | "2:\t"type##_lwr("%0", "3(%2)")"\n\t"\ | |
34c2f668 LY |
220 | "dsll\t%0, %0, 32\n\t" \ |
221 | "dsrl\t%0, %0, 32\n\t" \ | |
222 | "li\t%1, 0\n" \ | |
223 | "3:\n\t" \ | |
224 | ".insn\n\t" \ | |
225 | "\t.section\t.fixup,\"ax\"\n\t" \ | |
226 | "4:\tli\t%1, %3\n\t" \ | |
227 | "j\t3b\n\t" \ | |
228 | ".previous\n\t" \ | |
229 | ".section\t__ex_table,\"a\"\n\t" \ | |
230 | STR(PTR)"\t1b, 4b\n\t" \ | |
231 | STR(PTR)"\t2b, 4b\n\t" \ | |
232 | ".previous" \ | |
233 | : "=&r" (value), "=r" (res) \ | |
3563c32d MC |
234 | : "r" (addr), "i" (-EFAULT)); \ |
235 | } while(0) | |
34c2f668 | 236 | |
eeb53895 | 237 | #define _LoadDW(addr, value, res) \ |
3563c32d | 238 | do { \ |
34c2f668 LY |
239 | __asm__ __volatile__ ( \ |
240 | "1:\tldl\t%0, (%2)\n" \ | |
241 | "2:\tldr\t%0, 7(%2)\n\t" \ | |
242 | "li\t%1, 0\n" \ | |
243 | "3:\n\t" \ | |
244 | ".insn\n\t" \ | |
245 | "\t.section\t.fixup,\"ax\"\n\t" \ | |
246 | "4:\tli\t%1, %3\n\t" \ | |
247 | "j\t3b\n\t" \ | |
248 | ".previous\n\t" \ | |
249 | ".section\t__ex_table,\"a\"\n\t" \ | |
250 | STR(PTR)"\t1b, 4b\n\t" \ | |
251 | STR(PTR)"\t2b, 4b\n\t" \ | |
252 | ".previous" \ | |
253 | : "=&r" (value), "=r" (res) \ | |
3563c32d MC |
254 | : "r" (addr), "i" (-EFAULT)); \ |
255 | } while(0) | |
256 | ||
0593a44c LY |
257 | #else |
258 | /* MIPSR6 has not lwl and ldl instructions */ | |
eeb53895 | 259 | #define _LoadWU(addr, value, res, type) \ |
3563c32d | 260 | do { \ |
0593a44c LY |
261 | __asm__ __volatile__ ( \ |
262 | ".set\tpush\n\t" \ | |
263 | ".set\tnoat\n\t" \ | |
eeb53895 MC |
264 | "1:"type##_lbu("%0", "0(%2)")"\n\t" \ |
265 | "2:"type##_lbu("$1", "1(%2)")"\n\t" \ | |
0593a44c LY |
266 | "sll\t%0, 0x8\n\t" \ |
267 | "or\t%0, $1\n\t" \ | |
eeb53895 | 268 | "3:"type##_lbu("$1", "2(%2)")"\n\t" \ |
0593a44c LY |
269 | "sll\t%0, 0x8\n\t" \ |
270 | "or\t%0, $1\n\t" \ | |
eeb53895 | 271 | "4:"type##_lbu("$1", "3(%2)")"\n\t" \ |
0593a44c LY |
272 | "sll\t%0, 0x8\n\t" \ |
273 | "or\t%0, $1\n\t" \ | |
274 | "li\t%1, 0\n" \ | |
275 | ".set\tpop\n" \ | |
276 | "10:\n\t" \ | |
277 | ".insn\n\t" \ | |
278 | ".section\t.fixup,\"ax\"\n\t" \ | |
279 | "11:\tli\t%1, %3\n\t" \ | |
280 | "j\t10b\n\t" \ | |
281 | ".previous\n\t" \ | |
282 | ".section\t__ex_table,\"a\"\n\t" \ | |
283 | STR(PTR)"\t1b, 11b\n\t" \ | |
284 | STR(PTR)"\t2b, 11b\n\t" \ | |
285 | STR(PTR)"\t3b, 11b\n\t" \ | |
286 | STR(PTR)"\t4b, 11b\n\t" \ | |
287 | ".previous" \ | |
288 | : "=&r" (value), "=r" (res) \ | |
3563c32d MC |
289 | : "r" (addr), "i" (-EFAULT)); \ |
290 | } while(0) | |
0593a44c | 291 | |
eeb53895 | 292 | #define _LoadDW(addr, value, res) \ |
3563c32d | 293 | do { \ |
0593a44c LY |
294 | __asm__ __volatile__ ( \ |
295 | ".set\tpush\n\t" \ | |
296 | ".set\tnoat\n\t" \ | |
297 | "1:lb\t%0, 0(%2)\n\t" \ | |
298 | "2:lbu\t $1, 1(%2)\n\t" \ | |
299 | "dsll\t%0, 0x8\n\t" \ | |
300 | "or\t%0, $1\n\t" \ | |
301 | "3:lbu\t$1, 2(%2)\n\t" \ | |
302 | "dsll\t%0, 0x8\n\t" \ | |
303 | "or\t%0, $1\n\t" \ | |
304 | "4:lbu\t$1, 3(%2)\n\t" \ | |
305 | "dsll\t%0, 0x8\n\t" \ | |
306 | "or\t%0, $1\n\t" \ | |
307 | "5:lbu\t$1, 4(%2)\n\t" \ | |
308 | "dsll\t%0, 0x8\n\t" \ | |
309 | "or\t%0, $1\n\t" \ | |
310 | "6:lbu\t$1, 5(%2)\n\t" \ | |
311 | "dsll\t%0, 0x8\n\t" \ | |
312 | "or\t%0, $1\n\t" \ | |
313 | "7:lbu\t$1, 6(%2)\n\t" \ | |
314 | "dsll\t%0, 0x8\n\t" \ | |
315 | "or\t%0, $1\n\t" \ | |
316 | "8:lbu\t$1, 7(%2)\n\t" \ | |
317 | "dsll\t%0, 0x8\n\t" \ | |
318 | "or\t%0, $1\n\t" \ | |
319 | "li\t%1, 0\n" \ | |
320 | ".set\tpop\n\t" \ | |
321 | "10:\n\t" \ | |
322 | ".insn\n\t" \ | |
323 | ".section\t.fixup,\"ax\"\n\t" \ | |
324 | "11:\tli\t%1, %3\n\t" \ | |
325 | "j\t10b\n\t" \ | |
326 | ".previous\n\t" \ | |
327 | ".section\t__ex_table,\"a\"\n\t" \ | |
328 | STR(PTR)"\t1b, 11b\n\t" \ | |
329 | STR(PTR)"\t2b, 11b\n\t" \ | |
330 | STR(PTR)"\t3b, 11b\n\t" \ | |
331 | STR(PTR)"\t4b, 11b\n\t" \ | |
332 | STR(PTR)"\t5b, 11b\n\t" \ | |
333 | STR(PTR)"\t6b, 11b\n\t" \ | |
334 | STR(PTR)"\t7b, 11b\n\t" \ | |
335 | STR(PTR)"\t8b, 11b\n\t" \ | |
336 | ".previous" \ | |
337 | : "=&r" (value), "=r" (res) \ | |
3563c32d MC |
338 | : "r" (addr), "i" (-EFAULT)); \ |
339 | } while(0) | |
340 | ||
0593a44c LY |
341 | #endif /* CONFIG_CPU_MIPSR6 */ |
342 | ||
34c2f668 | 343 | |
eeb53895 | 344 | #define _StoreHW(addr, value, res, type) \ |
3563c32d | 345 | do { \ |
34c2f668 LY |
346 | __asm__ __volatile__ ( \ |
347 | ".set\tnoat\n" \ | |
eeb53895 | 348 | "1:\t"type##_sb("%1", "1(%2)")"\n" \ |
34c2f668 | 349 | "srl\t$1, %1, 0x8\n" \ |
eeb53895 | 350 | "2:\t"type##_sb("$1", "0(%2)")"\n" \ |
34c2f668 LY |
351 | ".set\tat\n\t" \ |
352 | "li\t%0, 0\n" \ | |
353 | "3:\n\t" \ | |
354 | ".insn\n\t" \ | |
355 | ".section\t.fixup,\"ax\"\n\t" \ | |
356 | "4:\tli\t%0, %3\n\t" \ | |
357 | "j\t3b\n\t" \ | |
358 | ".previous\n\t" \ | |
359 | ".section\t__ex_table,\"a\"\n\t" \ | |
360 | STR(PTR)"\t1b, 4b\n\t" \ | |
361 | STR(PTR)"\t2b, 4b\n\t" \ | |
362 | ".previous" \ | |
363 | : "=r" (res) \ | |
3563c32d MC |
364 | : "r" (value), "r" (addr), "i" (-EFAULT));\ |
365 | } while(0) | |
34c2f668 | 366 | |
0593a44c | 367 | #ifndef CONFIG_CPU_MIPSR6 |
eeb53895 | 368 | #define _StoreW(addr, value, res, type) \ |
3563c32d | 369 | do { \ |
34c2f668 | 370 | __asm__ __volatile__ ( \ |
eeb53895 MC |
371 | "1:\t"type##_swl("%1", "(%2)")"\n" \ |
372 | "2:\t"type##_swr("%1", "3(%2)")"\n\t"\ | |
34c2f668 LY |
373 | "li\t%0, 0\n" \ |
374 | "3:\n\t" \ | |
375 | ".insn\n\t" \ | |
376 | ".section\t.fixup,\"ax\"\n\t" \ | |
377 | "4:\tli\t%0, %3\n\t" \ | |
378 | "j\t3b\n\t" \ | |
379 | ".previous\n\t" \ | |
380 | ".section\t__ex_table,\"a\"\n\t" \ | |
381 | STR(PTR)"\t1b, 4b\n\t" \ | |
382 | STR(PTR)"\t2b, 4b\n\t" \ | |
383 | ".previous" \ | |
384 | : "=r" (res) \ | |
3563c32d MC |
385 | : "r" (value), "r" (addr), "i" (-EFAULT)); \ |
386 | } while(0) | |
34c2f668 | 387 | |
eeb53895 | 388 | #define _StoreDW(addr, value, res) \ |
3563c32d | 389 | do { \ |
34c2f668 LY |
390 | __asm__ __volatile__ ( \ |
391 | "1:\tsdl\t%1,(%2)\n" \ | |
392 | "2:\tsdr\t%1, 7(%2)\n\t" \ | |
393 | "li\t%0, 0\n" \ | |
394 | "3:\n\t" \ | |
395 | ".insn\n\t" \ | |
396 | ".section\t.fixup,\"ax\"\n\t" \ | |
397 | "4:\tli\t%0, %3\n\t" \ | |
398 | "j\t3b\n\t" \ | |
399 | ".previous\n\t" \ | |
400 | ".section\t__ex_table,\"a\"\n\t" \ | |
401 | STR(PTR)"\t1b, 4b\n\t" \ | |
402 | STR(PTR)"\t2b, 4b\n\t" \ | |
403 | ".previous" \ | |
404 | : "=r" (res) \ | |
3563c32d MC |
405 | : "r" (value), "r" (addr), "i" (-EFAULT)); \ |
406 | } while(0) | |
407 | ||
0593a44c LY |
408 | #else |
409 | /* MIPSR6 has no swl and sdl instructions */ | |
eeb53895 | 410 | #define _StoreW(addr, value, res, type) \ |
3563c32d | 411 | do { \ |
0593a44c LY |
412 | __asm__ __volatile__ ( \ |
413 | ".set\tpush\n\t" \ | |
414 | ".set\tnoat\n\t" \ | |
eeb53895 | 415 | "1:"type##_sb("%1", "3(%2)")"\n\t" \ |
0593a44c | 416 | "srl\t$1, %1, 0x8\n\t" \ |
eeb53895 | 417 | "2:"type##_sb("$1", "2(%2)")"\n\t" \ |
0593a44c | 418 | "srl\t$1, $1, 0x8\n\t" \ |
eeb53895 | 419 | "3:"type##_sb("$1", "1(%2)")"\n\t" \ |
0593a44c | 420 | "srl\t$1, $1, 0x8\n\t" \ |
eeb53895 | 421 | "4:"type##_sb("$1", "0(%2)")"\n\t" \ |
0593a44c LY |
422 | ".set\tpop\n\t" \ |
423 | "li\t%0, 0\n" \ | |
424 | "10:\n\t" \ | |
425 | ".insn\n\t" \ | |
426 | ".section\t.fixup,\"ax\"\n\t" \ | |
427 | "11:\tli\t%0, %3\n\t" \ | |
428 | "j\t10b\n\t" \ | |
429 | ".previous\n\t" \ | |
430 | ".section\t__ex_table,\"a\"\n\t" \ | |
431 | STR(PTR)"\t1b, 11b\n\t" \ | |
432 | STR(PTR)"\t2b, 11b\n\t" \ | |
433 | STR(PTR)"\t3b, 11b\n\t" \ | |
434 | STR(PTR)"\t4b, 11b\n\t" \ | |
435 | ".previous" \ | |
436 | : "=&r" (res) \ | |
437 | : "r" (value), "r" (addr), "i" (-EFAULT) \ | |
3563c32d MC |
438 | : "memory"); \ |
439 | } while(0) | |
0593a44c LY |
440 | |
441 | #define StoreDW(addr, value, res) \ | |
3563c32d | 442 | do { \ |
0593a44c LY |
443 | __asm__ __volatile__ ( \ |
444 | ".set\tpush\n\t" \ | |
445 | ".set\tnoat\n\t" \ | |
446 | "1:sb\t%1, 7(%2)\n\t" \ | |
447 | "dsrl\t$1, %1, 0x8\n\t" \ | |
448 | "2:sb\t$1, 6(%2)\n\t" \ | |
449 | "dsrl\t$1, $1, 0x8\n\t" \ | |
450 | "3:sb\t$1, 5(%2)\n\t" \ | |
451 | "dsrl\t$1, $1, 0x8\n\t" \ | |
452 | "4:sb\t$1, 4(%2)\n\t" \ | |
453 | "dsrl\t$1, $1, 0x8\n\t" \ | |
454 | "5:sb\t$1, 3(%2)\n\t" \ | |
455 | "dsrl\t$1, $1, 0x8\n\t" \ | |
456 | "6:sb\t$1, 2(%2)\n\t" \ | |
457 | "dsrl\t$1, $1, 0x8\n\t" \ | |
458 | "7:sb\t$1, 1(%2)\n\t" \ | |
459 | "dsrl\t$1, $1, 0x8\n\t" \ | |
460 | "8:sb\t$1, 0(%2)\n\t" \ | |
461 | "dsrl\t$1, $1, 0x8\n\t" \ | |
462 | ".set\tpop\n\t" \ | |
463 | "li\t%0, 0\n" \ | |
464 | "10:\n\t" \ | |
465 | ".insn\n\t" \ | |
466 | ".section\t.fixup,\"ax\"\n\t" \ | |
467 | "11:\tli\t%0, %3\n\t" \ | |
468 | "j\t10b\n\t" \ | |
469 | ".previous\n\t" \ | |
470 | ".section\t__ex_table,\"a\"\n\t" \ | |
471 | STR(PTR)"\t1b, 11b\n\t" \ | |
472 | STR(PTR)"\t2b, 11b\n\t" \ | |
473 | STR(PTR)"\t3b, 11b\n\t" \ | |
474 | STR(PTR)"\t4b, 11b\n\t" \ | |
475 | STR(PTR)"\t5b, 11b\n\t" \ | |
476 | STR(PTR)"\t6b, 11b\n\t" \ | |
477 | STR(PTR)"\t7b, 11b\n\t" \ | |
478 | STR(PTR)"\t8b, 11b\n\t" \ | |
479 | ".previous" \ | |
480 | : "=&r" (res) \ | |
481 | : "r" (value), "r" (addr), "i" (-EFAULT) \ | |
3563c32d MC |
482 | : "memory"); \ |
483 | } while(0) | |
484 | ||
0593a44c LY |
485 | #endif /* CONFIG_CPU_MIPSR6 */ |
486 | ||
487 | #else /* __BIG_ENDIAN */ | |
34c2f668 | 488 | |
eeb53895 | 489 | #define _LoadHW(addr, value, res, type) \ |
3563c32d | 490 | do { \ |
34c2f668 | 491 | __asm__ __volatile__ (".set\tnoat\n" \ |
eeb53895 MC |
492 | "1:\t"type##_lb("%0", "1(%2)")"\n" \ |
493 | "2:\t"type##_lbu("$1", "0(%2)")"\n\t"\ | |
34c2f668 LY |
494 | "sll\t%0, 0x8\n\t" \ |
495 | "or\t%0, $1\n\t" \ | |
496 | "li\t%1, 0\n" \ | |
497 | "3:\t.set\tat\n\t" \ | |
498 | ".insn\n\t" \ | |
499 | ".section\t.fixup,\"ax\"\n\t" \ | |
500 | "4:\tli\t%1, %3\n\t" \ | |
501 | "j\t3b\n\t" \ | |
502 | ".previous\n\t" \ | |
503 | ".section\t__ex_table,\"a\"\n\t" \ | |
504 | STR(PTR)"\t1b, 4b\n\t" \ | |
505 | STR(PTR)"\t2b, 4b\n\t" \ | |
506 | ".previous" \ | |
507 | : "=&r" (value), "=r" (res) \ | |
3563c32d MC |
508 | : "r" (addr), "i" (-EFAULT)); \ |
509 | } while(0) | |
34c2f668 | 510 | |
0593a44c | 511 | #ifndef CONFIG_CPU_MIPSR6 |
eeb53895 | 512 | #define _LoadW(addr, value, res, type) \ |
3563c32d | 513 | do { \ |
34c2f668 | 514 | __asm__ __volatile__ ( \ |
eeb53895 MC |
515 | "1:\t"type##_lwl("%0", "3(%2)")"\n" \ |
516 | "2:\t"type##_lwr("%0", "(%2)")"\n\t"\ | |
34c2f668 LY |
517 | "li\t%1, 0\n" \ |
518 | "3:\n\t" \ | |
519 | ".insn\n\t" \ | |
520 | ".section\t.fixup,\"ax\"\n\t" \ | |
521 | "4:\tli\t%1, %3\n\t" \ | |
522 | "j\t3b\n\t" \ | |
523 | ".previous\n\t" \ | |
524 | ".section\t__ex_table,\"a\"\n\t" \ | |
525 | STR(PTR)"\t1b, 4b\n\t" \ | |
526 | STR(PTR)"\t2b, 4b\n\t" \ | |
527 | ".previous" \ | |
528 | : "=&r" (value), "=r" (res) \ | |
3563c32d MC |
529 | : "r" (addr), "i" (-EFAULT)); \ |
530 | } while(0) | |
531 | ||
0593a44c LY |
532 | #else |
533 | /* MIPSR6 has no lwl instruction */ | |
eeb53895 | 534 | #define _LoadW(addr, value, res, type) \ |
3563c32d | 535 | do { \ |
0593a44c LY |
536 | __asm__ __volatile__ ( \ |
537 | ".set\tpush\n" \ | |
538 | ".set\tnoat\n\t" \ | |
eeb53895 MC |
539 | "1:"type##_lb("%0", "3(%2)")"\n\t" \ |
540 | "2:"type##_lbu("$1", "2(%2)")"\n\t" \ | |
0593a44c LY |
541 | "sll\t%0, 0x8\n\t" \ |
542 | "or\t%0, $1\n\t" \ | |
eeb53895 | 543 | "3:"type##_lbu("$1", "1(%2)")"\n\t" \ |
0593a44c LY |
544 | "sll\t%0, 0x8\n\t" \ |
545 | "or\t%0, $1\n\t" \ | |
eeb53895 | 546 | "4:"type##_lbu("$1", "0(%2)")"\n\t" \ |
0593a44c LY |
547 | "sll\t%0, 0x8\n\t" \ |
548 | "or\t%0, $1\n\t" \ | |
549 | "li\t%1, 0\n" \ | |
550 | ".set\tpop\n" \ | |
551 | "10:\n\t" \ | |
552 | ".insn\n\t" \ | |
553 | ".section\t.fixup,\"ax\"\n\t" \ | |
554 | "11:\tli\t%1, %3\n\t" \ | |
555 | "j\t10b\n\t" \ | |
556 | ".previous\n\t" \ | |
557 | ".section\t__ex_table,\"a\"\n\t" \ | |
558 | STR(PTR)"\t1b, 11b\n\t" \ | |
559 | STR(PTR)"\t2b, 11b\n\t" \ | |
560 | STR(PTR)"\t3b, 11b\n\t" \ | |
561 | STR(PTR)"\t4b, 11b\n\t" \ | |
562 | ".previous" \ | |
563 | : "=&r" (value), "=r" (res) \ | |
3563c32d MC |
564 | : "r" (addr), "i" (-EFAULT)); \ |
565 | } while(0) | |
566 | ||
0593a44c LY |
567 | #endif /* CONFIG_CPU_MIPSR6 */ |
568 | ||
34c2f668 | 569 | |
eeb53895 | 570 | #define _LoadHWU(addr, value, res, type) \ |
3563c32d | 571 | do { \ |
34c2f668 LY |
572 | __asm__ __volatile__ ( \ |
573 | ".set\tnoat\n" \ | |
eeb53895 MC |
574 | "1:\t"type##_lbu("%0", "1(%2)")"\n" \ |
575 | "2:\t"type##_lbu("$1", "0(%2)")"\n\t"\ | |
34c2f668 LY |
576 | "sll\t%0, 0x8\n\t" \ |
577 | "or\t%0, $1\n\t" \ | |
578 | "li\t%1, 0\n" \ | |
579 | "3:\n\t" \ | |
580 | ".insn\n\t" \ | |
581 | ".set\tat\n\t" \ | |
582 | ".section\t.fixup,\"ax\"\n\t" \ | |
583 | "4:\tli\t%1, %3\n\t" \ | |
584 | "j\t3b\n\t" \ | |
585 | ".previous\n\t" \ | |
586 | ".section\t__ex_table,\"a\"\n\t" \ | |
587 | STR(PTR)"\t1b, 4b\n\t" \ | |
588 | STR(PTR)"\t2b, 4b\n\t" \ | |
589 | ".previous" \ | |
590 | : "=&r" (value), "=r" (res) \ | |
3563c32d MC |
591 | : "r" (addr), "i" (-EFAULT)); \ |
592 | } while(0) | |
34c2f668 | 593 | |
0593a44c | 594 | #ifndef CONFIG_CPU_MIPSR6 |
eeb53895 | 595 | #define _LoadWU(addr, value, res, type) \ |
3563c32d | 596 | do { \ |
34c2f668 | 597 | __asm__ __volatile__ ( \ |
eeb53895 MC |
598 | "1:\t"type##_lwl("%0", "3(%2)")"\n" \ |
599 | "2:\t"type##_lwr("%0", "(%2)")"\n\t"\ | |
34c2f668 LY |
600 | "dsll\t%0, %0, 32\n\t" \ |
601 | "dsrl\t%0, %0, 32\n\t" \ | |
602 | "li\t%1, 0\n" \ | |
603 | "3:\n\t" \ | |
604 | ".insn\n\t" \ | |
605 | "\t.section\t.fixup,\"ax\"\n\t" \ | |
606 | "4:\tli\t%1, %3\n\t" \ | |
607 | "j\t3b\n\t" \ | |
608 | ".previous\n\t" \ | |
609 | ".section\t__ex_table,\"a\"\n\t" \ | |
610 | STR(PTR)"\t1b, 4b\n\t" \ | |
611 | STR(PTR)"\t2b, 4b\n\t" \ | |
612 | ".previous" \ | |
613 | : "=&r" (value), "=r" (res) \ | |
3563c32d MC |
614 | : "r" (addr), "i" (-EFAULT)); \ |
615 | } while(0) | |
34c2f668 | 616 | |
eeb53895 | 617 | #define _LoadDW(addr, value, res) \ |
3563c32d | 618 | do { \ |
34c2f668 LY |
619 | __asm__ __volatile__ ( \ |
620 | "1:\tldl\t%0, 7(%2)\n" \ | |
621 | "2:\tldr\t%0, (%2)\n\t" \ | |
622 | "li\t%1, 0\n" \ | |
623 | "3:\n\t" \ | |
624 | ".insn\n\t" \ | |
625 | "\t.section\t.fixup,\"ax\"\n\t" \ | |
626 | "4:\tli\t%1, %3\n\t" \ | |
627 | "j\t3b\n\t" \ | |
628 | ".previous\n\t" \ | |
629 | ".section\t__ex_table,\"a\"\n\t" \ | |
630 | STR(PTR)"\t1b, 4b\n\t" \ | |
631 | STR(PTR)"\t2b, 4b\n\t" \ | |
632 | ".previous" \ | |
633 | : "=&r" (value), "=r" (res) \ | |
3563c32d MC |
634 | : "r" (addr), "i" (-EFAULT)); \ |
635 | } while(0) | |
636 | ||
0593a44c LY |
637 | #else |
638 | /* MIPSR6 has not lwl and ldl instructions */ | |
eeb53895 | 639 | #define _LoadWU(addr, value, res, type) \ |
3563c32d | 640 | do { \ |
0593a44c LY |
641 | __asm__ __volatile__ ( \ |
642 | ".set\tpush\n\t" \ | |
643 | ".set\tnoat\n\t" \ | |
eeb53895 MC |
644 | "1:"type##_lbu("%0", "3(%2)")"\n\t" \ |
645 | "2:"type##_lbu("$1", "2(%2)")"\n\t" \ | |
0593a44c LY |
646 | "sll\t%0, 0x8\n\t" \ |
647 | "or\t%0, $1\n\t" \ | |
eeb53895 | 648 | "3:"type##_lbu("$1", "1(%2)")"\n\t" \ |
0593a44c LY |
649 | "sll\t%0, 0x8\n\t" \ |
650 | "or\t%0, $1\n\t" \ | |
eeb53895 | 651 | "4:"type##_lbu("$1", "0(%2)")"\n\t" \ |
0593a44c LY |
652 | "sll\t%0, 0x8\n\t" \ |
653 | "or\t%0, $1\n\t" \ | |
654 | "li\t%1, 0\n" \ | |
655 | ".set\tpop\n" \ | |
656 | "10:\n\t" \ | |
657 | ".insn\n\t" \ | |
658 | ".section\t.fixup,\"ax\"\n\t" \ | |
659 | "11:\tli\t%1, %3\n\t" \ | |
660 | "j\t10b\n\t" \ | |
661 | ".previous\n\t" \ | |
662 | ".section\t__ex_table,\"a\"\n\t" \ | |
663 | STR(PTR)"\t1b, 11b\n\t" \ | |
664 | STR(PTR)"\t2b, 11b\n\t" \ | |
665 | STR(PTR)"\t3b, 11b\n\t" \ | |
666 | STR(PTR)"\t4b, 11b\n\t" \ | |
667 | ".previous" \ | |
668 | : "=&r" (value), "=r" (res) \ | |
3563c32d MC |
669 | : "r" (addr), "i" (-EFAULT)); \ |
670 | } while(0) | |
0593a44c | 671 | |
eeb53895 | 672 | #define _LoadDW(addr, value, res) \ |
3563c32d | 673 | do { \ |
0593a44c LY |
674 | __asm__ __volatile__ ( \ |
675 | ".set\tpush\n\t" \ | |
676 | ".set\tnoat\n\t" \ | |
677 | "1:lb\t%0, 7(%2)\n\t" \ | |
678 | "2:lbu\t$1, 6(%2)\n\t" \ | |
679 | "dsll\t%0, 0x8\n\t" \ | |
680 | "or\t%0, $1\n\t" \ | |
681 | "3:lbu\t$1, 5(%2)\n\t" \ | |
682 | "dsll\t%0, 0x8\n\t" \ | |
683 | "or\t%0, $1\n\t" \ | |
684 | "4:lbu\t$1, 4(%2)\n\t" \ | |
685 | "dsll\t%0, 0x8\n\t" \ | |
686 | "or\t%0, $1\n\t" \ | |
687 | "5:lbu\t$1, 3(%2)\n\t" \ | |
688 | "dsll\t%0, 0x8\n\t" \ | |
689 | "or\t%0, $1\n\t" \ | |
690 | "6:lbu\t$1, 2(%2)\n\t" \ | |
691 | "dsll\t%0, 0x8\n\t" \ | |
692 | "or\t%0, $1\n\t" \ | |
693 | "7:lbu\t$1, 1(%2)\n\t" \ | |
694 | "dsll\t%0, 0x8\n\t" \ | |
695 | "or\t%0, $1\n\t" \ | |
696 | "8:lbu\t$1, 0(%2)\n\t" \ | |
697 | "dsll\t%0, 0x8\n\t" \ | |
698 | "or\t%0, $1\n\t" \ | |
699 | "li\t%1, 0\n" \ | |
700 | ".set\tpop\n\t" \ | |
701 | "10:\n\t" \ | |
702 | ".insn\n\t" \ | |
703 | ".section\t.fixup,\"ax\"\n\t" \ | |
704 | "11:\tli\t%1, %3\n\t" \ | |
705 | "j\t10b\n\t" \ | |
706 | ".previous\n\t" \ | |
707 | ".section\t__ex_table,\"a\"\n\t" \ | |
708 | STR(PTR)"\t1b, 11b\n\t" \ | |
709 | STR(PTR)"\t2b, 11b\n\t" \ | |
710 | STR(PTR)"\t3b, 11b\n\t" \ | |
711 | STR(PTR)"\t4b, 11b\n\t" \ | |
712 | STR(PTR)"\t5b, 11b\n\t" \ | |
713 | STR(PTR)"\t6b, 11b\n\t" \ | |
714 | STR(PTR)"\t7b, 11b\n\t" \ | |
715 | STR(PTR)"\t8b, 11b\n\t" \ | |
716 | ".previous" \ | |
717 | : "=&r" (value), "=r" (res) \ | |
3563c32d MC |
718 | : "r" (addr), "i" (-EFAULT)); \ |
719 | } while(0) | |
0593a44c | 720 | #endif /* CONFIG_CPU_MIPSR6 */ |
34c2f668 | 721 | |
eeb53895 | 722 | #define _StoreHW(addr, value, res, type) \ |
3563c32d | 723 | do { \ |
34c2f668 LY |
724 | __asm__ __volatile__ ( \ |
725 | ".set\tnoat\n" \ | |
eeb53895 | 726 | "1:\t"type##_sb("%1", "0(%2)")"\n" \ |
34c2f668 | 727 | "srl\t$1,%1, 0x8\n" \ |
eeb53895 | 728 | "2:\t"type##_sb("$1", "1(%2)")"\n" \ |
34c2f668 LY |
729 | ".set\tat\n\t" \ |
730 | "li\t%0, 0\n" \ | |
731 | "3:\n\t" \ | |
732 | ".insn\n\t" \ | |
733 | ".section\t.fixup,\"ax\"\n\t" \ | |
734 | "4:\tli\t%0, %3\n\t" \ | |
735 | "j\t3b\n\t" \ | |
736 | ".previous\n\t" \ | |
737 | ".section\t__ex_table,\"a\"\n\t" \ | |
738 | STR(PTR)"\t1b, 4b\n\t" \ | |
739 | STR(PTR)"\t2b, 4b\n\t" \ | |
740 | ".previous" \ | |
741 | : "=r" (res) \ | |
3563c32d MC |
742 | : "r" (value), "r" (addr), "i" (-EFAULT));\ |
743 | } while(0) | |
744 | ||
0593a44c | 745 | #ifndef CONFIG_CPU_MIPSR6 |
eeb53895 | 746 | #define _StoreW(addr, value, res, type) \ |
3563c32d | 747 | do { \ |
34c2f668 | 748 | __asm__ __volatile__ ( \ |
eeb53895 MC |
749 | "1:\t"type##_swl("%1", "3(%2)")"\n" \ |
750 | "2:\t"type##_swr("%1", "(%2)")"\n\t"\ | |
34c2f668 LY |
751 | "li\t%0, 0\n" \ |
752 | "3:\n\t" \ | |
753 | ".insn\n\t" \ | |
754 | ".section\t.fixup,\"ax\"\n\t" \ | |
755 | "4:\tli\t%0, %3\n\t" \ | |
756 | "j\t3b\n\t" \ | |
757 | ".previous\n\t" \ | |
758 | ".section\t__ex_table,\"a\"\n\t" \ | |
759 | STR(PTR)"\t1b, 4b\n\t" \ | |
760 | STR(PTR)"\t2b, 4b\n\t" \ | |
761 | ".previous" \ | |
762 | : "=r" (res) \ | |
3563c32d MC |
763 | : "r" (value), "r" (addr), "i" (-EFAULT)); \ |
764 | } while(0) | |
34c2f668 | 765 | |
eeb53895 | 766 | #define _StoreDW(addr, value, res) \ |
3563c32d | 767 | do { \ |
34c2f668 LY |
768 | __asm__ __volatile__ ( \ |
769 | "1:\tsdl\t%1, 7(%2)\n" \ | |
770 | "2:\tsdr\t%1, (%2)\n\t" \ | |
771 | "li\t%0, 0\n" \ | |
772 | "3:\n\t" \ | |
773 | ".insn\n\t" \ | |
774 | ".section\t.fixup,\"ax\"\n\t" \ | |
775 | "4:\tli\t%0, %3\n\t" \ | |
776 | "j\t3b\n\t" \ | |
777 | ".previous\n\t" \ | |
778 | ".section\t__ex_table,\"a\"\n\t" \ | |
779 | STR(PTR)"\t1b, 4b\n\t" \ | |
780 | STR(PTR)"\t2b, 4b\n\t" \ | |
781 | ".previous" \ | |
782 | : "=r" (res) \ | |
3563c32d MC |
783 | : "r" (value), "r" (addr), "i" (-EFAULT)); \ |
784 | } while(0) | |
785 | ||
0593a44c LY |
786 | #else |
787 | /* MIPSR6 has no swl and sdl instructions */ | |
eeb53895 | 788 | #define _StoreW(addr, value, res, type) \ |
3563c32d | 789 | do { \ |
0593a44c LY |
790 | __asm__ __volatile__ ( \ |
791 | ".set\tpush\n\t" \ | |
792 | ".set\tnoat\n\t" \ | |
eeb53895 | 793 | "1:"type##_sb("%1", "0(%2)")"\n\t" \ |
0593a44c | 794 | "srl\t$1, %1, 0x8\n\t" \ |
eeb53895 | 795 | "2:"type##_sb("$1", "1(%2)")"\n\t" \ |
0593a44c | 796 | "srl\t$1, $1, 0x8\n\t" \ |
eeb53895 | 797 | "3:"type##_sb("$1", "2(%2)")"\n\t" \ |
0593a44c | 798 | "srl\t$1, $1, 0x8\n\t" \ |
eeb53895 | 799 | "4:"type##_sb("$1", "3(%2)")"\n\t" \ |
0593a44c LY |
800 | ".set\tpop\n\t" \ |
801 | "li\t%0, 0\n" \ | |
802 | "10:\n\t" \ | |
803 | ".insn\n\t" \ | |
804 | ".section\t.fixup,\"ax\"\n\t" \ | |
805 | "11:\tli\t%0, %3\n\t" \ | |
806 | "j\t10b\n\t" \ | |
807 | ".previous\n\t" \ | |
808 | ".section\t__ex_table,\"a\"\n\t" \ | |
809 | STR(PTR)"\t1b, 11b\n\t" \ | |
810 | STR(PTR)"\t2b, 11b\n\t" \ | |
811 | STR(PTR)"\t3b, 11b\n\t" \ | |
812 | STR(PTR)"\t4b, 11b\n\t" \ | |
813 | ".previous" \ | |
814 | : "=&r" (res) \ | |
815 | : "r" (value), "r" (addr), "i" (-EFAULT) \ | |
3563c32d MC |
816 | : "memory"); \ |
817 | } while(0) | |
0593a44c | 818 | |
eeb53895 | 819 | #define _StoreDW(addr, value, res) \ |
3563c32d | 820 | do { \ |
0593a44c LY |
821 | __asm__ __volatile__ ( \ |
822 | ".set\tpush\n\t" \ | |
823 | ".set\tnoat\n\t" \ | |
824 | "1:sb\t%1, 0(%2)\n\t" \ | |
825 | "dsrl\t$1, %1, 0x8\n\t" \ | |
826 | "2:sb\t$1, 1(%2)\n\t" \ | |
827 | "dsrl\t$1, $1, 0x8\n\t" \ | |
828 | "3:sb\t$1, 2(%2)\n\t" \ | |
829 | "dsrl\t$1, $1, 0x8\n\t" \ | |
830 | "4:sb\t$1, 3(%2)\n\t" \ | |
831 | "dsrl\t$1, $1, 0x8\n\t" \ | |
832 | "5:sb\t$1, 4(%2)\n\t" \ | |
833 | "dsrl\t$1, $1, 0x8\n\t" \ | |
834 | "6:sb\t$1, 5(%2)\n\t" \ | |
835 | "dsrl\t$1, $1, 0x8\n\t" \ | |
836 | "7:sb\t$1, 6(%2)\n\t" \ | |
837 | "dsrl\t$1, $1, 0x8\n\t" \ | |
838 | "8:sb\t$1, 7(%2)\n\t" \ | |
839 | "dsrl\t$1, $1, 0x8\n\t" \ | |
840 | ".set\tpop\n\t" \ | |
841 | "li\t%0, 0\n" \ | |
842 | "10:\n\t" \ | |
843 | ".insn\n\t" \ | |
844 | ".section\t.fixup,\"ax\"\n\t" \ | |
845 | "11:\tli\t%0, %3\n\t" \ | |
846 | "j\t10b\n\t" \ | |
847 | ".previous\n\t" \ | |
848 | ".section\t__ex_table,\"a\"\n\t" \ | |
849 | STR(PTR)"\t1b, 11b\n\t" \ | |
850 | STR(PTR)"\t2b, 11b\n\t" \ | |
851 | STR(PTR)"\t3b, 11b\n\t" \ | |
852 | STR(PTR)"\t4b, 11b\n\t" \ | |
853 | STR(PTR)"\t5b, 11b\n\t" \ | |
854 | STR(PTR)"\t6b, 11b\n\t" \ | |
855 | STR(PTR)"\t7b, 11b\n\t" \ | |
856 | STR(PTR)"\t8b, 11b\n\t" \ | |
857 | ".previous" \ | |
858 | : "=&r" (res) \ | |
859 | : "r" (value), "r" (addr), "i" (-EFAULT) \ | |
3563c32d MC |
860 | : "memory"); \ |
861 | } while(0) | |
862 | ||
0593a44c | 863 | #endif /* CONFIG_CPU_MIPSR6 */ |
34c2f668 LY |
864 | #endif |
865 | ||
eeb53895 MC |
866 | #define LoadHWU(addr, value, res) _LoadHWU(addr, value, res, kernel) |
867 | #define LoadHWUE(addr, value, res) _LoadHWU(addr, value, res, user) | |
868 | #define LoadWU(addr, value, res) _LoadWU(addr, value, res, kernel) | |
869 | #define LoadWUE(addr, value, res) _LoadWU(addr, value, res, user) | |
870 | #define LoadHW(addr, value, res) _LoadHW(addr, value, res, kernel) | |
871 | #define LoadHWE(addr, value, res) _LoadHW(addr, value, res, user) | |
872 | #define LoadW(addr, value, res) _LoadW(addr, value, res, kernel) | |
873 | #define LoadWE(addr, value, res) _LoadW(addr, value, res, user) | |
874 | #define LoadDW(addr, value, res) _LoadDW(addr, value, res) | |
875 | ||
876 | #define StoreHW(addr, value, res) _StoreHW(addr, value, res, kernel) | |
877 | #define StoreHWE(addr, value, res) _StoreHW(addr, value, res, user) | |
878 | #define StoreW(addr, value, res) _StoreW(addr, value, res, kernel) | |
879 | #define StoreWE(addr, value, res) _StoreW(addr, value, res, user) | |
880 | #define StoreDW(addr, value, res) _StoreDW(addr, value, res) | |
881 | ||
7f18f151 RB |
882 | static void emulate_load_store_insn(struct pt_regs *regs, |
883 | void __user *addr, unsigned int __user *pc) | |
1da177e4 LT |
884 | { |
885 | union mips_instruction insn; | |
886 | unsigned long value; | |
887 | unsigned int res; | |
34c2f668 LY |
888 | unsigned long origpc; |
889 | unsigned long orig31; | |
102cedc3 | 890 | void __user *fault_addr = NULL; |
c1771216 LY |
891 | #ifdef CONFIG_EVA |
892 | mm_segment_t seg; | |
893 | #endif | |
34c2f668 LY |
894 | origpc = (unsigned long)pc; |
895 | orig31 = regs->regs[31]; | |
896 | ||
a8b0ca17 | 897 | perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0); |
7f788d2d | 898 | |
1da177e4 LT |
899 | /* |
900 | * This load never faults. | |
901 | */ | |
fe00f943 | 902 | __get_user(insn.word, pc); |
1da177e4 LT |
903 | |
904 | switch (insn.i_format.opcode) { | |
34c2f668 LY |
905 | /* |
906 | * These are instructions that a compiler doesn't generate. We | |
907 | * can assume therefore that the code is MIPS-aware and | |
908 | * really buggy. Emulating these instructions would break the | |
909 | * semantics anyway. | |
910 | */ | |
1da177e4 LT |
911 | case ll_op: |
912 | case lld_op: | |
913 | case sc_op: | |
914 | case scd_op: | |
915 | ||
34c2f668 LY |
916 | /* |
917 | * For these instructions the only way to create an address | |
918 | * error is an attempted access to kernel/supervisor address | |
919 | * space. | |
920 | */ | |
1da177e4 LT |
921 | case ldl_op: |
922 | case ldr_op: | |
923 | case lwl_op: | |
924 | case lwr_op: | |
925 | case sdl_op: | |
926 | case sdr_op: | |
927 | case swl_op: | |
928 | case swr_op: | |
929 | case lb_op: | |
930 | case lbu_op: | |
931 | case sb_op: | |
932 | goto sigbus; | |
933 | ||
34c2f668 LY |
934 | /* |
935 | * The remaining opcodes are the ones that are really of | |
936 | * interest. | |
937 | */ | |
c1771216 LY |
938 | #ifdef CONFIG_EVA |
939 | case spec3_op: | |
940 | /* | |
941 | * we can land here only from kernel accessing user memory, | |
942 | * so we need to "switch" the address limit to user space, so | |
943 | * address check can work properly. | |
944 | */ | |
945 | seg = get_fs(); | |
946 | set_fs(USER_DS); | |
947 | switch (insn.spec3_format.func) { | |
948 | case lhe_op: | |
949 | if (!access_ok(VERIFY_READ, addr, 2)) { | |
950 | set_fs(seg); | |
951 | goto sigbus; | |
952 | } | |
eeb53895 | 953 | LoadHWE(addr, value, res); |
c1771216 LY |
954 | if (res) { |
955 | set_fs(seg); | |
956 | goto fault; | |
957 | } | |
958 | compute_return_epc(regs); | |
959 | regs->regs[insn.spec3_format.rt] = value; | |
960 | break; | |
961 | case lwe_op: | |
962 | if (!access_ok(VERIFY_READ, addr, 4)) { | |
963 | set_fs(seg); | |
964 | goto sigbus; | |
965 | } | |
eeb53895 | 966 | LoadWE(addr, value, res); |
c1771216 LY |
967 | if (res) { |
968 | set_fs(seg); | |
969 | goto fault; | |
970 | } | |
971 | compute_return_epc(regs); | |
972 | regs->regs[insn.spec3_format.rt] = value; | |
973 | break; | |
974 | case lhue_op: | |
975 | if (!access_ok(VERIFY_READ, addr, 2)) { | |
976 | set_fs(seg); | |
977 | goto sigbus; | |
978 | } | |
eeb53895 | 979 | LoadHWUE(addr, value, res); |
c1771216 LY |
980 | if (res) { |
981 | set_fs(seg); | |
982 | goto fault; | |
983 | } | |
984 | compute_return_epc(regs); | |
985 | regs->regs[insn.spec3_format.rt] = value; | |
986 | break; | |
987 | case she_op: | |
988 | if (!access_ok(VERIFY_WRITE, addr, 2)) { | |
989 | set_fs(seg); | |
990 | goto sigbus; | |
991 | } | |
992 | compute_return_epc(regs); | |
993 | value = regs->regs[insn.spec3_format.rt]; | |
eeb53895 | 994 | StoreHWE(addr, value, res); |
c1771216 LY |
995 | if (res) { |
996 | set_fs(seg); | |
997 | goto fault; | |
998 | } | |
999 | break; | |
1000 | case swe_op: | |
1001 | if (!access_ok(VERIFY_WRITE, addr, 4)) { | |
1002 | set_fs(seg); | |
1003 | goto sigbus; | |
1004 | } | |
1005 | compute_return_epc(regs); | |
1006 | value = regs->regs[insn.spec3_format.rt]; | |
eeb53895 | 1007 | StoreWE(addr, value, res); |
c1771216 LY |
1008 | if (res) { |
1009 | set_fs(seg); | |
1010 | goto fault; | |
1011 | } | |
1012 | break; | |
1013 | default: | |
1014 | set_fs(seg); | |
1015 | goto sigill; | |
1016 | } | |
1017 | set_fs(seg); | |
1018 | break; | |
1019 | #endif | |
1da177e4 LT |
1020 | case lh_op: |
1021 | if (!access_ok(VERIFY_READ, addr, 2)) | |
1022 | goto sigbus; | |
1023 | ||
6eae3548 MC |
1024 | if (config_enabled(CONFIG_EVA)) { |
1025 | if (segment_eq(get_fs(), get_ds())) | |
1026 | LoadHW(addr, value, res); | |
1027 | else | |
1028 | LoadHWE(addr, value, res); | |
1029 | } else { | |
1030 | LoadHW(addr, value, res); | |
1031 | } | |
1032 | ||
1da177e4 LT |
1033 | if (res) |
1034 | goto fault; | |
7f18f151 RB |
1035 | compute_return_epc(regs); |
1036 | regs->regs[insn.i_format.rt] = value; | |
1da177e4 LT |
1037 | break; |
1038 | ||
1039 | case lw_op: | |
1040 | if (!access_ok(VERIFY_READ, addr, 4)) | |
1041 | goto sigbus; | |
1042 | ||
6eae3548 MC |
1043 | if (config_enabled(CONFIG_EVA)) { |
1044 | if (segment_eq(get_fs(), get_ds())) | |
1045 | LoadW(addr, value, res); | |
1046 | else | |
1047 | LoadWE(addr, value, res); | |
1048 | } else { | |
1049 | LoadW(addr, value, res); | |
1050 | } | |
1051 | ||
1da177e4 LT |
1052 | if (res) |
1053 | goto fault; | |
7f18f151 RB |
1054 | compute_return_epc(regs); |
1055 | regs->regs[insn.i_format.rt] = value; | |
1da177e4 LT |
1056 | break; |
1057 | ||
1058 | case lhu_op: | |
1059 | if (!access_ok(VERIFY_READ, addr, 2)) | |
1060 | goto sigbus; | |
1061 | ||
6eae3548 MC |
1062 | if (config_enabled(CONFIG_EVA)) { |
1063 | if (segment_eq(get_fs(), get_ds())) | |
1064 | LoadHWU(addr, value, res); | |
1065 | else | |
1066 | LoadHWUE(addr, value, res); | |
1067 | } else { | |
1068 | LoadHWU(addr, value, res); | |
1069 | } | |
1070 | ||
1da177e4 LT |
1071 | if (res) |
1072 | goto fault; | |
7f18f151 RB |
1073 | compute_return_epc(regs); |
1074 | regs->regs[insn.i_format.rt] = value; | |
1da177e4 LT |
1075 | break; |
1076 | ||
1077 | case lwu_op: | |
875d43e7 | 1078 | #ifdef CONFIG_64BIT |
1da177e4 LT |
1079 | /* |
1080 | * A 32-bit kernel might be running on a 64-bit processor. But | |
1081 | * if we're on a 32-bit processor and an i-cache incoherency | |
1082 | * or race makes us see a 64-bit instruction here the sdl/sdr | |
1083 | * would blow up, so for now we don't handle unaligned 64-bit | |
1084 | * instructions on 32-bit kernels. | |
1085 | */ | |
1086 | if (!access_ok(VERIFY_READ, addr, 4)) | |
1087 | goto sigbus; | |
1088 | ||
34c2f668 | 1089 | LoadWU(addr, value, res); |
1da177e4 LT |
1090 | if (res) |
1091 | goto fault; | |
7f18f151 RB |
1092 | compute_return_epc(regs); |
1093 | regs->regs[insn.i_format.rt] = value; | |
1da177e4 | 1094 | break; |
875d43e7 | 1095 | #endif /* CONFIG_64BIT */ |
1da177e4 LT |
1096 | |
1097 | /* Cannot handle 64-bit instructions in 32-bit kernel */ | |
1098 | goto sigill; | |
1099 | ||
1100 | case ld_op: | |
875d43e7 | 1101 | #ifdef CONFIG_64BIT |
1da177e4 LT |
1102 | /* |
1103 | * A 32-bit kernel might be running on a 64-bit processor. But | |
1104 | * if we're on a 32-bit processor and an i-cache incoherency | |
1105 | * or race makes us see a 64-bit instruction here the sdl/sdr | |
1106 | * would blow up, so for now we don't handle unaligned 64-bit | |
1107 | * instructions on 32-bit kernels. | |
1108 | */ | |
1109 | if (!access_ok(VERIFY_READ, addr, 8)) | |
1110 | goto sigbus; | |
1111 | ||
34c2f668 | 1112 | LoadDW(addr, value, res); |
1da177e4 LT |
1113 | if (res) |
1114 | goto fault; | |
7f18f151 RB |
1115 | compute_return_epc(regs); |
1116 | regs->regs[insn.i_format.rt] = value; | |
1da177e4 | 1117 | break; |
875d43e7 | 1118 | #endif /* CONFIG_64BIT */ |
1da177e4 LT |
1119 | |
1120 | /* Cannot handle 64-bit instructions in 32-bit kernel */ | |
1121 | goto sigill; | |
1122 | ||
1123 | case sh_op: | |
1124 | if (!access_ok(VERIFY_WRITE, addr, 2)) | |
1125 | goto sigbus; | |
1126 | ||
34c2f668 | 1127 | compute_return_epc(regs); |
1da177e4 | 1128 | value = regs->regs[insn.i_format.rt]; |
6eae3548 MC |
1129 | |
1130 | if (config_enabled(CONFIG_EVA)) { | |
1131 | if (segment_eq(get_fs(), get_ds())) | |
1132 | StoreHW(addr, value, res); | |
1133 | else | |
1134 | StoreHWE(addr, value, res); | |
1135 | } else { | |
1136 | StoreHW(addr, value, res); | |
1137 | } | |
1138 | ||
1da177e4 LT |
1139 | if (res) |
1140 | goto fault; | |
1141 | break; | |
1142 | ||
1143 | case sw_op: | |
1144 | if (!access_ok(VERIFY_WRITE, addr, 4)) | |
1145 | goto sigbus; | |
1146 | ||
34c2f668 | 1147 | compute_return_epc(regs); |
1da177e4 | 1148 | value = regs->regs[insn.i_format.rt]; |
6eae3548 MC |
1149 | |
1150 | if (config_enabled(CONFIG_EVA)) { | |
1151 | if (segment_eq(get_fs(), get_ds())) | |
1152 | StoreW(addr, value, res); | |
1153 | else | |
1154 | StoreWE(addr, value, res); | |
1155 | } else { | |
1156 | StoreW(addr, value, res); | |
1157 | } | |
1158 | ||
1da177e4 LT |
1159 | if (res) |
1160 | goto fault; | |
1161 | break; | |
1162 | ||
1163 | case sd_op: | |
875d43e7 | 1164 | #ifdef CONFIG_64BIT |
1da177e4 LT |
1165 | /* |
1166 | * A 32-bit kernel might be running on a 64-bit processor. But | |
1167 | * if we're on a 32-bit processor and an i-cache incoherency | |
1168 | * or race makes us see a 64-bit instruction here the sdl/sdr | |
1169 | * would blow up, so for now we don't handle unaligned 64-bit | |
1170 | * instructions on 32-bit kernels. | |
1171 | */ | |
1172 | if (!access_ok(VERIFY_WRITE, addr, 8)) | |
1173 | goto sigbus; | |
1174 | ||
34c2f668 | 1175 | compute_return_epc(regs); |
1da177e4 | 1176 | value = regs->regs[insn.i_format.rt]; |
34c2f668 | 1177 | StoreDW(addr, value, res); |
1da177e4 LT |
1178 | if (res) |
1179 | goto fault; | |
1180 | break; | |
875d43e7 | 1181 | #endif /* CONFIG_64BIT */ |
1da177e4 LT |
1182 | |
1183 | /* Cannot handle 64-bit instructions in 32-bit kernel */ | |
1184 | goto sigill; | |
1185 | ||
1186 | case lwc1_op: | |
1187 | case ldc1_op: | |
1188 | case swc1_op: | |
1189 | case sdc1_op: | |
102cedc3 LY |
1190 | die_if_kernel("Unaligned FP access in kernel code", regs); |
1191 | BUG_ON(!used_math()); | |
102cedc3 LY |
1192 | |
1193 | lose_fpu(1); /* Save FPU state for the emulator. */ | |
1194 | res = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 1, | |
1195 | &fault_addr); | |
1196 | own_fpu(1); /* Restore FPU state. */ | |
1197 | ||
1198 | /* Signal if something went wrong. */ | |
304acb71 | 1199 | process_fpemu_return(res, fault_addr, 0); |
102cedc3 LY |
1200 | |
1201 | if (res == 0) | |
1202 | break; | |
1203 | return; | |
1da177e4 | 1204 | |
0593a44c | 1205 | #ifndef CONFIG_CPU_MIPSR6 |
69f3a7de RB |
1206 | /* |
1207 | * COP2 is available to implementor for application specific use. | |
1208 | * It's up to applications to register a notifier chain and do | |
1209 | * whatever they have to do, including possible sending of signals. | |
0593a44c LY |
1210 | * |
1211 | * This instruction has been reallocated in Release 6 | |
69f3a7de | 1212 | */ |
1da177e4 | 1213 | case lwc2_op: |
69f3a7de RB |
1214 | cu2_notifier_call_chain(CU2_LWC2_OP, regs); |
1215 | break; | |
1216 | ||
1da177e4 | 1217 | case ldc2_op: |
69f3a7de RB |
1218 | cu2_notifier_call_chain(CU2_LDC2_OP, regs); |
1219 | break; | |
1220 | ||
1da177e4 | 1221 | case swc2_op: |
69f3a7de RB |
1222 | cu2_notifier_call_chain(CU2_SWC2_OP, regs); |
1223 | break; | |
1224 | ||
1da177e4 | 1225 | case sdc2_op: |
69f3a7de RB |
1226 | cu2_notifier_call_chain(CU2_SDC2_OP, regs); |
1227 | break; | |
0593a44c | 1228 | #endif |
1da177e4 LT |
1229 | default: |
1230 | /* | |
1231 | * Pheeee... We encountered an yet unknown instruction or | |
1232 | * cache coherence problem. Die sucker, die ... | |
1233 | */ | |
1234 | goto sigill; | |
1235 | } | |
1236 | ||
6312e0ee | 1237 | #ifdef CONFIG_DEBUG_FS |
1da177e4 LT |
1238 | unaligned_instructions++; |
1239 | #endif | |
1240 | ||
7f18f151 | 1241 | return; |
1da177e4 LT |
1242 | |
1243 | fault: | |
34c2f668 LY |
1244 | /* roll back jump/branch */ |
1245 | regs->cp0_epc = origpc; | |
1246 | regs->regs[31] = orig31; | |
1247 | /* Did we have an exception handler installed? */ | |
1248 | if (fixup_exception(regs)) | |
1249 | return; | |
1250 | ||
1251 | die_if_kernel("Unhandled kernel unaligned access", regs); | |
1252 | force_sig(SIGSEGV, current); | |
1253 | ||
1254 | return; | |
1255 | ||
1256 | sigbus: | |
1257 | die_if_kernel("Unhandled kernel unaligned access", regs); | |
1258 | force_sig(SIGBUS, current); | |
1259 | ||
1260 | return; | |
1261 | ||
1262 | sigill: | |
1263 | die_if_kernel | |
1264 | ("Unhandled kernel unaligned access or invalid instruction", regs); | |
1265 | force_sig(SIGILL, current); | |
1266 | } | |
1267 | ||
1268 | /* Recode table from 16-bit register notation to 32-bit GPR. */ | |
1269 | const int reg16to32[] = { 16, 17, 2, 3, 4, 5, 6, 7 }; | |
1270 | ||
1271 | /* Recode table from 16-bit STORE register notation to 32-bit GPR. */ | |
1272 | const int reg16to32st[] = { 0, 17, 2, 3, 4, 5, 6, 7 }; | |
1273 | ||
74338805 DD |
1274 | static void emulate_load_store_microMIPS(struct pt_regs *regs, |
1275 | void __user *addr) | |
34c2f668 LY |
1276 | { |
1277 | unsigned long value; | |
1278 | unsigned int res; | |
1279 | int i; | |
1280 | unsigned int reg = 0, rvar; | |
1281 | unsigned long orig31; | |
1282 | u16 __user *pc16; | |
1283 | u16 halfword; | |
1284 | unsigned int word; | |
1285 | unsigned long origpc, contpc; | |
1286 | union mips_instruction insn; | |
1287 | struct mm_decoded_insn mminsn; | |
1288 | void __user *fault_addr = NULL; | |
1289 | ||
1290 | origpc = regs->cp0_epc; | |
1291 | orig31 = regs->regs[31]; | |
1292 | ||
1293 | mminsn.micro_mips_mode = 1; | |
1294 | ||
1295 | /* | |
1296 | * This load never faults. | |
1297 | */ | |
1298 | pc16 = (unsigned short __user *)msk_isa16_mode(regs->cp0_epc); | |
1299 | __get_user(halfword, pc16); | |
1300 | pc16++; | |
1301 | contpc = regs->cp0_epc + 2; | |
1302 | word = ((unsigned int)halfword << 16); | |
1303 | mminsn.pc_inc = 2; | |
1304 | ||
1305 | if (!mm_insn_16bit(halfword)) { | |
1306 | __get_user(halfword, pc16); | |
1307 | pc16++; | |
1308 | contpc = regs->cp0_epc + 4; | |
1309 | mminsn.pc_inc = 4; | |
1310 | word |= halfword; | |
1311 | } | |
1312 | mminsn.insn = word; | |
1313 | ||
1314 | if (get_user(halfword, pc16)) | |
1315 | goto fault; | |
1316 | mminsn.next_pc_inc = 2; | |
1317 | word = ((unsigned int)halfword << 16); | |
1318 | ||
1319 | if (!mm_insn_16bit(halfword)) { | |
1320 | pc16++; | |
1321 | if (get_user(halfword, pc16)) | |
1322 | goto fault; | |
1323 | mminsn.next_pc_inc = 4; | |
1324 | word |= halfword; | |
1325 | } | |
1326 | mminsn.next_insn = word; | |
1327 | ||
1328 | insn = (union mips_instruction)(mminsn.insn); | |
1329 | if (mm_isBranchInstr(regs, mminsn, &contpc)) | |
1330 | insn = (union mips_instruction)(mminsn.next_insn); | |
1331 | ||
1332 | /* Parse instruction to find what to do */ | |
1333 | ||
1334 | switch (insn.mm_i_format.opcode) { | |
1335 | ||
1336 | case mm_pool32a_op: | |
1337 | switch (insn.mm_x_format.func) { | |
1338 | case mm_lwxs_op: | |
1339 | reg = insn.mm_x_format.rd; | |
1340 | goto loadW; | |
1341 | } | |
1342 | ||
1343 | goto sigbus; | |
1344 | ||
1345 | case mm_pool32b_op: | |
1346 | switch (insn.mm_m_format.func) { | |
1347 | case mm_lwp_func: | |
1348 | reg = insn.mm_m_format.rd; | |
1349 | if (reg == 31) | |
1350 | goto sigbus; | |
1351 | ||
1352 | if (!access_ok(VERIFY_READ, addr, 8)) | |
1353 | goto sigbus; | |
1354 | ||
1355 | LoadW(addr, value, res); | |
1356 | if (res) | |
1357 | goto fault; | |
1358 | regs->regs[reg] = value; | |
1359 | addr += 4; | |
1360 | LoadW(addr, value, res); | |
1361 | if (res) | |
1362 | goto fault; | |
1363 | regs->regs[reg + 1] = value; | |
1364 | goto success; | |
1365 | ||
1366 | case mm_swp_func: | |
1367 | reg = insn.mm_m_format.rd; | |
1368 | if (reg == 31) | |
1369 | goto sigbus; | |
1370 | ||
1371 | if (!access_ok(VERIFY_WRITE, addr, 8)) | |
1372 | goto sigbus; | |
1373 | ||
1374 | value = regs->regs[reg]; | |
1375 | StoreW(addr, value, res); | |
1376 | if (res) | |
1377 | goto fault; | |
1378 | addr += 4; | |
1379 | value = regs->regs[reg + 1]; | |
1380 | StoreW(addr, value, res); | |
1381 | if (res) | |
1382 | goto fault; | |
1383 | goto success; | |
1384 | ||
1385 | case mm_ldp_func: | |
1386 | #ifdef CONFIG_64BIT | |
1387 | reg = insn.mm_m_format.rd; | |
1388 | if (reg == 31) | |
1389 | goto sigbus; | |
1390 | ||
1391 | if (!access_ok(VERIFY_READ, addr, 16)) | |
1392 | goto sigbus; | |
1393 | ||
1394 | LoadDW(addr, value, res); | |
1395 | if (res) | |
1396 | goto fault; | |
1397 | regs->regs[reg] = value; | |
1398 | addr += 8; | |
1399 | LoadDW(addr, value, res); | |
1400 | if (res) | |
1401 | goto fault; | |
1402 | regs->regs[reg + 1] = value; | |
1403 | goto success; | |
1404 | #endif /* CONFIG_64BIT */ | |
1405 | ||
1406 | goto sigill; | |
1407 | ||
1408 | case mm_sdp_func: | |
1409 | #ifdef CONFIG_64BIT | |
1410 | reg = insn.mm_m_format.rd; | |
1411 | if (reg == 31) | |
1412 | goto sigbus; | |
1413 | ||
1414 | if (!access_ok(VERIFY_WRITE, addr, 16)) | |
1415 | goto sigbus; | |
1416 | ||
1417 | value = regs->regs[reg]; | |
1418 | StoreDW(addr, value, res); | |
1419 | if (res) | |
1420 | goto fault; | |
1421 | addr += 8; | |
1422 | value = regs->regs[reg + 1]; | |
1423 | StoreDW(addr, value, res); | |
1424 | if (res) | |
1425 | goto fault; | |
1426 | goto success; | |
1427 | #endif /* CONFIG_64BIT */ | |
1428 | ||
1429 | goto sigill; | |
1430 | ||
1431 | case mm_lwm32_func: | |
1432 | reg = insn.mm_m_format.rd; | |
1433 | rvar = reg & 0xf; | |
1434 | if ((rvar > 9) || !reg) | |
1435 | goto sigill; | |
1436 | if (reg & 0x10) { | |
1437 | if (!access_ok | |
1438 | (VERIFY_READ, addr, 4 * (rvar + 1))) | |
1439 | goto sigbus; | |
1440 | } else { | |
1441 | if (!access_ok(VERIFY_READ, addr, 4 * rvar)) | |
1442 | goto sigbus; | |
1443 | } | |
1444 | if (rvar == 9) | |
1445 | rvar = 8; | |
1446 | for (i = 16; rvar; rvar--, i++) { | |
1447 | LoadW(addr, value, res); | |
1448 | if (res) | |
1449 | goto fault; | |
1450 | addr += 4; | |
1451 | regs->regs[i] = value; | |
1452 | } | |
1453 | if ((reg & 0xf) == 9) { | |
1454 | LoadW(addr, value, res); | |
1455 | if (res) | |
1456 | goto fault; | |
1457 | addr += 4; | |
1458 | regs->regs[30] = value; | |
1459 | } | |
1460 | if (reg & 0x10) { | |
1461 | LoadW(addr, value, res); | |
1462 | if (res) | |
1463 | goto fault; | |
1464 | regs->regs[31] = value; | |
1465 | } | |
1466 | goto success; | |
1467 | ||
1468 | case mm_swm32_func: | |
1469 | reg = insn.mm_m_format.rd; | |
1470 | rvar = reg & 0xf; | |
1471 | if ((rvar > 9) || !reg) | |
1472 | goto sigill; | |
1473 | if (reg & 0x10) { | |
1474 | if (!access_ok | |
1475 | (VERIFY_WRITE, addr, 4 * (rvar + 1))) | |
1476 | goto sigbus; | |
1477 | } else { | |
1478 | if (!access_ok(VERIFY_WRITE, addr, 4 * rvar)) | |
1479 | goto sigbus; | |
1480 | } | |
1481 | if (rvar == 9) | |
1482 | rvar = 8; | |
1483 | for (i = 16; rvar; rvar--, i++) { | |
1484 | value = regs->regs[i]; | |
1485 | StoreW(addr, value, res); | |
1486 | if (res) | |
1487 | goto fault; | |
1488 | addr += 4; | |
1489 | } | |
1490 | if ((reg & 0xf) == 9) { | |
1491 | value = regs->regs[30]; | |
1492 | StoreW(addr, value, res); | |
1493 | if (res) | |
1494 | goto fault; | |
1495 | addr += 4; | |
1496 | } | |
1497 | if (reg & 0x10) { | |
1498 | value = regs->regs[31]; | |
1499 | StoreW(addr, value, res); | |
1500 | if (res) | |
1501 | goto fault; | |
1502 | } | |
1503 | goto success; | |
1504 | ||
1505 | case mm_ldm_func: | |
1506 | #ifdef CONFIG_64BIT | |
1507 | reg = insn.mm_m_format.rd; | |
1508 | rvar = reg & 0xf; | |
1509 | if ((rvar > 9) || !reg) | |
1510 | goto sigill; | |
1511 | if (reg & 0x10) { | |
1512 | if (!access_ok | |
1513 | (VERIFY_READ, addr, 8 * (rvar + 1))) | |
1514 | goto sigbus; | |
1515 | } else { | |
1516 | if (!access_ok(VERIFY_READ, addr, 8 * rvar)) | |
1517 | goto sigbus; | |
1518 | } | |
1519 | if (rvar == 9) | |
1520 | rvar = 8; | |
1521 | ||
1522 | for (i = 16; rvar; rvar--, i++) { | |
1523 | LoadDW(addr, value, res); | |
1524 | if (res) | |
1525 | goto fault; | |
1526 | addr += 4; | |
1527 | regs->regs[i] = value; | |
1528 | } | |
1529 | if ((reg & 0xf) == 9) { | |
1530 | LoadDW(addr, value, res); | |
1531 | if (res) | |
1532 | goto fault; | |
1533 | addr += 8; | |
1534 | regs->regs[30] = value; | |
1535 | } | |
1536 | if (reg & 0x10) { | |
1537 | LoadDW(addr, value, res); | |
1538 | if (res) | |
1539 | goto fault; | |
1540 | regs->regs[31] = value; | |
1541 | } | |
1542 | goto success; | |
1543 | #endif /* CONFIG_64BIT */ | |
1544 | ||
1545 | goto sigill; | |
1546 | ||
1547 | case mm_sdm_func: | |
1548 | #ifdef CONFIG_64BIT | |
1549 | reg = insn.mm_m_format.rd; | |
1550 | rvar = reg & 0xf; | |
1551 | if ((rvar > 9) || !reg) | |
1552 | goto sigill; | |
1553 | if (reg & 0x10) { | |
1554 | if (!access_ok | |
1555 | (VERIFY_WRITE, addr, 8 * (rvar + 1))) | |
1556 | goto sigbus; | |
1557 | } else { | |
1558 | if (!access_ok(VERIFY_WRITE, addr, 8 * rvar)) | |
1559 | goto sigbus; | |
1560 | } | |
1561 | if (rvar == 9) | |
1562 | rvar = 8; | |
1563 | ||
1564 | for (i = 16; rvar; rvar--, i++) { | |
1565 | value = regs->regs[i]; | |
1566 | StoreDW(addr, value, res); | |
1567 | if (res) | |
1568 | goto fault; | |
1569 | addr += 8; | |
1570 | } | |
1571 | if ((reg & 0xf) == 9) { | |
1572 | value = regs->regs[30]; | |
1573 | StoreDW(addr, value, res); | |
1574 | if (res) | |
1575 | goto fault; | |
1576 | addr += 8; | |
1577 | } | |
1578 | if (reg & 0x10) { | |
1579 | value = regs->regs[31]; | |
1580 | StoreDW(addr, value, res); | |
1581 | if (res) | |
1582 | goto fault; | |
1583 | } | |
1584 | goto success; | |
1585 | #endif /* CONFIG_64BIT */ | |
1586 | ||
1587 | goto sigill; | |
1588 | ||
1589 | /* LWC2, SWC2, LDC2, SDC2 are not serviced */ | |
1590 | } | |
1591 | ||
1592 | goto sigbus; | |
1593 | ||
1594 | case mm_pool32c_op: | |
1595 | switch (insn.mm_m_format.func) { | |
1596 | case mm_lwu_func: | |
1597 | reg = insn.mm_m_format.rd; | |
1598 | goto loadWU; | |
1599 | } | |
1600 | ||
1601 | /* LL,SC,LLD,SCD are not serviced */ | |
1602 | goto sigbus; | |
1603 | ||
1604 | case mm_pool32f_op: | |
1605 | switch (insn.mm_x_format.func) { | |
1606 | case mm_lwxc1_func: | |
1607 | case mm_swxc1_func: | |
1608 | case mm_ldxc1_func: | |
1609 | case mm_sdxc1_func: | |
1610 | goto fpu_emul; | |
1611 | } | |
1612 | ||
1613 | goto sigbus; | |
1614 | ||
1615 | case mm_ldc132_op: | |
1616 | case mm_sdc132_op: | |
1617 | case mm_lwc132_op: | |
1618 | case mm_swc132_op: | |
1619 | fpu_emul: | |
1620 | /* roll back jump/branch */ | |
1621 | regs->cp0_epc = origpc; | |
1622 | regs->regs[31] = orig31; | |
1623 | ||
1624 | die_if_kernel("Unaligned FP access in kernel code", regs); | |
1625 | BUG_ON(!used_math()); | |
1626 | BUG_ON(!is_fpu_owner()); | |
1627 | ||
1628 | lose_fpu(1); /* save the FPU state for the emulator */ | |
1629 | res = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 1, | |
1630 | &fault_addr); | |
1631 | own_fpu(1); /* restore FPU state */ | |
1632 | ||
1633 | /* If something went wrong, signal */ | |
304acb71 | 1634 | process_fpemu_return(res, fault_addr, 0); |
34c2f668 LY |
1635 | |
1636 | if (res == 0) | |
1637 | goto success; | |
1638 | return; | |
1639 | ||
1640 | case mm_lh32_op: | |
1641 | reg = insn.mm_i_format.rt; | |
1642 | goto loadHW; | |
1643 | ||
1644 | case mm_lhu32_op: | |
1645 | reg = insn.mm_i_format.rt; | |
1646 | goto loadHWU; | |
1647 | ||
1648 | case mm_lw32_op: | |
1649 | reg = insn.mm_i_format.rt; | |
1650 | goto loadW; | |
1651 | ||
1652 | case mm_sh32_op: | |
1653 | reg = insn.mm_i_format.rt; | |
1654 | goto storeHW; | |
1655 | ||
1656 | case mm_sw32_op: | |
1657 | reg = insn.mm_i_format.rt; | |
1658 | goto storeW; | |
1659 | ||
1660 | case mm_ld32_op: | |
1661 | reg = insn.mm_i_format.rt; | |
1662 | goto loadDW; | |
1663 | ||
1664 | case mm_sd32_op: | |
1665 | reg = insn.mm_i_format.rt; | |
1666 | goto storeDW; | |
1667 | ||
1668 | case mm_pool16c_op: | |
1669 | switch (insn.mm16_m_format.func) { | |
1670 | case mm_lwm16_op: | |
1671 | reg = insn.mm16_m_format.rlist; | |
1672 | rvar = reg + 1; | |
1673 | if (!access_ok(VERIFY_READ, addr, 4 * rvar)) | |
1674 | goto sigbus; | |
1675 | ||
1676 | for (i = 16; rvar; rvar--, i++) { | |
1677 | LoadW(addr, value, res); | |
1678 | if (res) | |
1679 | goto fault; | |
1680 | addr += 4; | |
1681 | regs->regs[i] = value; | |
1682 | } | |
1683 | LoadW(addr, value, res); | |
1684 | if (res) | |
1685 | goto fault; | |
1686 | regs->regs[31] = value; | |
1687 | ||
1688 | goto success; | |
1689 | ||
1690 | case mm_swm16_op: | |
1691 | reg = insn.mm16_m_format.rlist; | |
1692 | rvar = reg + 1; | |
1693 | if (!access_ok(VERIFY_WRITE, addr, 4 * rvar)) | |
1694 | goto sigbus; | |
1695 | ||
1696 | for (i = 16; rvar; rvar--, i++) { | |
1697 | value = regs->regs[i]; | |
1698 | StoreW(addr, value, res); | |
1699 | if (res) | |
1700 | goto fault; | |
1701 | addr += 4; | |
1702 | } | |
1703 | value = regs->regs[31]; | |
1704 | StoreW(addr, value, res); | |
1705 | if (res) | |
1706 | goto fault; | |
1707 | ||
1708 | goto success; | |
1709 | ||
1710 | } | |
1711 | ||
1712 | goto sigbus; | |
1713 | ||
1714 | case mm_lhu16_op: | |
1715 | reg = reg16to32[insn.mm16_rb_format.rt]; | |
1716 | goto loadHWU; | |
1717 | ||
1718 | case mm_lw16_op: | |
1719 | reg = reg16to32[insn.mm16_rb_format.rt]; | |
1720 | goto loadW; | |
1721 | ||
1722 | case mm_sh16_op: | |
1723 | reg = reg16to32st[insn.mm16_rb_format.rt]; | |
1724 | goto storeHW; | |
1725 | ||
1726 | case mm_sw16_op: | |
1727 | reg = reg16to32st[insn.mm16_rb_format.rt]; | |
1728 | goto storeW; | |
1729 | ||
1730 | case mm_lwsp16_op: | |
1731 | reg = insn.mm16_r5_format.rt; | |
1732 | goto loadW; | |
1733 | ||
1734 | case mm_swsp16_op: | |
1735 | reg = insn.mm16_r5_format.rt; | |
1736 | goto storeW; | |
1737 | ||
1738 | case mm_lwgp16_op: | |
1739 | reg = reg16to32[insn.mm16_r3_format.rt]; | |
1740 | goto loadW; | |
1741 | ||
1742 | default: | |
1743 | goto sigill; | |
1744 | } | |
1745 | ||
1746 | loadHW: | |
1747 | if (!access_ok(VERIFY_READ, addr, 2)) | |
1748 | goto sigbus; | |
1749 | ||
1750 | LoadHW(addr, value, res); | |
1751 | if (res) | |
1752 | goto fault; | |
1753 | regs->regs[reg] = value; | |
1754 | goto success; | |
1755 | ||
1756 | loadHWU: | |
1757 | if (!access_ok(VERIFY_READ, addr, 2)) | |
1758 | goto sigbus; | |
1759 | ||
1760 | LoadHWU(addr, value, res); | |
1761 | if (res) | |
1762 | goto fault; | |
1763 | regs->regs[reg] = value; | |
1764 | goto success; | |
1765 | ||
1766 | loadW: | |
1767 | if (!access_ok(VERIFY_READ, addr, 4)) | |
1768 | goto sigbus; | |
1769 | ||
1770 | LoadW(addr, value, res); | |
1771 | if (res) | |
1772 | goto fault; | |
1773 | regs->regs[reg] = value; | |
1774 | goto success; | |
1775 | ||
1776 | loadWU: | |
1777 | #ifdef CONFIG_64BIT | |
1778 | /* | |
1779 | * A 32-bit kernel might be running on a 64-bit processor. But | |
1780 | * if we're on a 32-bit processor and an i-cache incoherency | |
1781 | * or race makes us see a 64-bit instruction here the sdl/sdr | |
1782 | * would blow up, so for now we don't handle unaligned 64-bit | |
1783 | * instructions on 32-bit kernels. | |
1784 | */ | |
1785 | if (!access_ok(VERIFY_READ, addr, 4)) | |
1786 | goto sigbus; | |
1787 | ||
1788 | LoadWU(addr, value, res); | |
1789 | if (res) | |
1790 | goto fault; | |
1791 | regs->regs[reg] = value; | |
1792 | goto success; | |
1793 | #endif /* CONFIG_64BIT */ | |
1794 | ||
1795 | /* Cannot handle 64-bit instructions in 32-bit kernel */ | |
1796 | goto sigill; | |
1797 | ||
1798 | loadDW: | |
1799 | #ifdef CONFIG_64BIT | |
1800 | /* | |
1801 | * A 32-bit kernel might be running on a 64-bit processor. But | |
1802 | * if we're on a 32-bit processor and an i-cache incoherency | |
1803 | * or race makes us see a 64-bit instruction here the sdl/sdr | |
1804 | * would blow up, so for now we don't handle unaligned 64-bit | |
1805 | * instructions on 32-bit kernels. | |
1806 | */ | |
1807 | if (!access_ok(VERIFY_READ, addr, 8)) | |
1808 | goto sigbus; | |
1809 | ||
1810 | LoadDW(addr, value, res); | |
1811 | if (res) | |
1812 | goto fault; | |
1813 | regs->regs[reg] = value; | |
1814 | goto success; | |
1815 | #endif /* CONFIG_64BIT */ | |
1816 | ||
1817 | /* Cannot handle 64-bit instructions in 32-bit kernel */ | |
1818 | goto sigill; | |
1819 | ||
1820 | storeHW: | |
1821 | if (!access_ok(VERIFY_WRITE, addr, 2)) | |
1822 | goto sigbus; | |
1823 | ||
1824 | value = regs->regs[reg]; | |
1825 | StoreHW(addr, value, res); | |
1826 | if (res) | |
1827 | goto fault; | |
1828 | goto success; | |
1829 | ||
1830 | storeW: | |
1831 | if (!access_ok(VERIFY_WRITE, addr, 4)) | |
1832 | goto sigbus; | |
1833 | ||
1834 | value = regs->regs[reg]; | |
1835 | StoreW(addr, value, res); | |
1836 | if (res) | |
1837 | goto fault; | |
1838 | goto success; | |
1839 | ||
1840 | storeDW: | |
1841 | #ifdef CONFIG_64BIT | |
1842 | /* | |
1843 | * A 32-bit kernel might be running on a 64-bit processor. But | |
1844 | * if we're on a 32-bit processor and an i-cache incoherency | |
1845 | * or race makes us see a 64-bit instruction here the sdl/sdr | |
1846 | * would blow up, so for now we don't handle unaligned 64-bit | |
1847 | * instructions on 32-bit kernels. | |
1848 | */ | |
1849 | if (!access_ok(VERIFY_WRITE, addr, 8)) | |
1850 | goto sigbus; | |
1851 | ||
1852 | value = regs->regs[reg]; | |
1853 | StoreDW(addr, value, res); | |
1854 | if (res) | |
1855 | goto fault; | |
1856 | goto success; | |
1857 | #endif /* CONFIG_64BIT */ | |
1858 | ||
1859 | /* Cannot handle 64-bit instructions in 32-bit kernel */ | |
1860 | goto sigill; | |
1861 | ||
1862 | success: | |
1863 | regs->cp0_epc = contpc; /* advance or branch */ | |
1864 | ||
1865 | #ifdef CONFIG_DEBUG_FS | |
1866 | unaligned_instructions++; | |
1867 | #endif | |
1868 | return; | |
1869 | ||
1870 | fault: | |
1871 | /* roll back jump/branch */ | |
1872 | regs->cp0_epc = origpc; | |
1873 | regs->regs[31] = orig31; | |
1da177e4 LT |
1874 | /* Did we have an exception handler installed? */ |
1875 | if (fixup_exception(regs)) | |
7f18f151 | 1876 | return; |
1da177e4 | 1877 | |
49a89efb | 1878 | die_if_kernel("Unhandled kernel unaligned access", regs); |
a6d5ff04 | 1879 | force_sig(SIGSEGV, current); |
1da177e4 | 1880 | |
7f18f151 | 1881 | return; |
1da177e4 LT |
1882 | |
1883 | sigbus: | |
1884 | die_if_kernel("Unhandled kernel unaligned access", regs); | |
a6d5ff04 | 1885 | force_sig(SIGBUS, current); |
1da177e4 | 1886 | |
7f18f151 | 1887 | return; |
1da177e4 LT |
1888 | |
1889 | sigill: | |
34c2f668 LY |
1890 | die_if_kernel |
1891 | ("Unhandled kernel unaligned access or invalid instruction", regs); | |
a6d5ff04 | 1892 | force_sig(SIGILL, current); |
1da177e4 LT |
1893 | } |
1894 | ||
451b001b SH |
1895 | static void emulate_load_store_MIPS16e(struct pt_regs *regs, void __user * addr) |
1896 | { | |
1897 | unsigned long value; | |
1898 | unsigned int res; | |
1899 | int reg; | |
1900 | unsigned long orig31; | |
1901 | u16 __user *pc16; | |
1902 | unsigned long origpc; | |
1903 | union mips16e_instruction mips16inst, oldinst; | |
1904 | ||
1905 | origpc = regs->cp0_epc; | |
1906 | orig31 = regs->regs[31]; | |
1907 | pc16 = (unsigned short __user *)msk_isa16_mode(origpc); | |
1908 | /* | |
1909 | * This load never faults. | |
1910 | */ | |
1911 | __get_user(mips16inst.full, pc16); | |
1912 | oldinst = mips16inst; | |
1913 | ||
1914 | /* skip EXTEND instruction */ | |
1915 | if (mips16inst.ri.opcode == MIPS16e_extend_op) { | |
1916 | pc16++; | |
1917 | __get_user(mips16inst.full, pc16); | |
1918 | } else if (delay_slot(regs)) { | |
1919 | /* skip jump instructions */ | |
1920 | /* JAL/JALX are 32 bits but have OPCODE in first short int */ | |
1921 | if (mips16inst.ri.opcode == MIPS16e_jal_op) | |
1922 | pc16++; | |
1923 | pc16++; | |
1924 | if (get_user(mips16inst.full, pc16)) | |
1925 | goto sigbus; | |
1926 | } | |
1927 | ||
1928 | switch (mips16inst.ri.opcode) { | |
1929 | case MIPS16e_i64_op: /* I64 or RI64 instruction */ | |
1930 | switch (mips16inst.i64.func) { /* I64/RI64 func field check */ | |
1931 | case MIPS16e_ldpc_func: | |
1932 | case MIPS16e_ldsp_func: | |
1933 | reg = reg16to32[mips16inst.ri64.ry]; | |
1934 | goto loadDW; | |
1935 | ||
1936 | case MIPS16e_sdsp_func: | |
1937 | reg = reg16to32[mips16inst.ri64.ry]; | |
1938 | goto writeDW; | |
1939 | ||
1940 | case MIPS16e_sdrasp_func: | |
1941 | reg = 29; /* GPRSP */ | |
1942 | goto writeDW; | |
1943 | } | |
1944 | ||
1945 | goto sigbus; | |
1946 | ||
1947 | case MIPS16e_swsp_op: | |
1948 | case MIPS16e_lwpc_op: | |
1949 | case MIPS16e_lwsp_op: | |
1950 | reg = reg16to32[mips16inst.ri.rx]; | |
1951 | break; | |
1952 | ||
1953 | case MIPS16e_i8_op: | |
1954 | if (mips16inst.i8.func != MIPS16e_swrasp_func) | |
1955 | goto sigbus; | |
1956 | reg = 29; /* GPRSP */ | |
1957 | break; | |
1958 | ||
1959 | default: | |
1960 | reg = reg16to32[mips16inst.rri.ry]; | |
1961 | break; | |
1962 | } | |
1963 | ||
1964 | switch (mips16inst.ri.opcode) { | |
1965 | ||
1966 | case MIPS16e_lb_op: | |
1967 | case MIPS16e_lbu_op: | |
1968 | case MIPS16e_sb_op: | |
1969 | goto sigbus; | |
1970 | ||
1971 | case MIPS16e_lh_op: | |
1972 | if (!access_ok(VERIFY_READ, addr, 2)) | |
1973 | goto sigbus; | |
1974 | ||
1975 | LoadHW(addr, value, res); | |
1976 | if (res) | |
1977 | goto fault; | |
1978 | MIPS16e_compute_return_epc(regs, &oldinst); | |
1979 | regs->regs[reg] = value; | |
1980 | break; | |
1981 | ||
1982 | case MIPS16e_lhu_op: | |
1983 | if (!access_ok(VERIFY_READ, addr, 2)) | |
1984 | goto sigbus; | |
1985 | ||
1986 | LoadHWU(addr, value, res); | |
1987 | if (res) | |
1988 | goto fault; | |
1989 | MIPS16e_compute_return_epc(regs, &oldinst); | |
1990 | regs->regs[reg] = value; | |
1991 | break; | |
1992 | ||
1993 | case MIPS16e_lw_op: | |
1994 | case MIPS16e_lwpc_op: | |
1995 | case MIPS16e_lwsp_op: | |
1996 | if (!access_ok(VERIFY_READ, addr, 4)) | |
1997 | goto sigbus; | |
1998 | ||
1999 | LoadW(addr, value, res); | |
2000 | if (res) | |
2001 | goto fault; | |
2002 | MIPS16e_compute_return_epc(regs, &oldinst); | |
2003 | regs->regs[reg] = value; | |
2004 | break; | |
2005 | ||
2006 | case MIPS16e_lwu_op: | |
2007 | #ifdef CONFIG_64BIT | |
2008 | /* | |
2009 | * A 32-bit kernel might be running on a 64-bit processor. But | |
2010 | * if we're on a 32-bit processor and an i-cache incoherency | |
2011 | * or race makes us see a 64-bit instruction here the sdl/sdr | |
2012 | * would blow up, so for now we don't handle unaligned 64-bit | |
2013 | * instructions on 32-bit kernels. | |
2014 | */ | |
2015 | if (!access_ok(VERIFY_READ, addr, 4)) | |
2016 | goto sigbus; | |
2017 | ||
2018 | LoadWU(addr, value, res); | |
2019 | if (res) | |
2020 | goto fault; | |
2021 | MIPS16e_compute_return_epc(regs, &oldinst); | |
2022 | regs->regs[reg] = value; | |
2023 | break; | |
2024 | #endif /* CONFIG_64BIT */ | |
2025 | ||
2026 | /* Cannot handle 64-bit instructions in 32-bit kernel */ | |
2027 | goto sigill; | |
2028 | ||
2029 | case MIPS16e_ld_op: | |
2030 | loadDW: | |
2031 | #ifdef CONFIG_64BIT | |
2032 | /* | |
2033 | * A 32-bit kernel might be running on a 64-bit processor. But | |
2034 | * if we're on a 32-bit processor and an i-cache incoherency | |
2035 | * or race makes us see a 64-bit instruction here the sdl/sdr | |
2036 | * would blow up, so for now we don't handle unaligned 64-bit | |
2037 | * instructions on 32-bit kernels. | |
2038 | */ | |
2039 | if (!access_ok(VERIFY_READ, addr, 8)) | |
2040 | goto sigbus; | |
2041 | ||
2042 | LoadDW(addr, value, res); | |
2043 | if (res) | |
2044 | goto fault; | |
2045 | MIPS16e_compute_return_epc(regs, &oldinst); | |
2046 | regs->regs[reg] = value; | |
2047 | break; | |
2048 | #endif /* CONFIG_64BIT */ | |
2049 | ||
2050 | /* Cannot handle 64-bit instructions in 32-bit kernel */ | |
2051 | goto sigill; | |
2052 | ||
2053 | case MIPS16e_sh_op: | |
2054 | if (!access_ok(VERIFY_WRITE, addr, 2)) | |
2055 | goto sigbus; | |
2056 | ||
2057 | MIPS16e_compute_return_epc(regs, &oldinst); | |
2058 | value = regs->regs[reg]; | |
2059 | StoreHW(addr, value, res); | |
2060 | if (res) | |
2061 | goto fault; | |
2062 | break; | |
2063 | ||
2064 | case MIPS16e_sw_op: | |
2065 | case MIPS16e_swsp_op: | |
2066 | case MIPS16e_i8_op: /* actually - MIPS16e_swrasp_func */ | |
2067 | if (!access_ok(VERIFY_WRITE, addr, 4)) | |
2068 | goto sigbus; | |
2069 | ||
2070 | MIPS16e_compute_return_epc(regs, &oldinst); | |
2071 | value = regs->regs[reg]; | |
2072 | StoreW(addr, value, res); | |
2073 | if (res) | |
2074 | goto fault; | |
2075 | break; | |
2076 | ||
2077 | case MIPS16e_sd_op: | |
2078 | writeDW: | |
2079 | #ifdef CONFIG_64BIT | |
2080 | /* | |
2081 | * A 32-bit kernel might be running on a 64-bit processor. But | |
2082 | * if we're on a 32-bit processor and an i-cache incoherency | |
2083 | * or race makes us see a 64-bit instruction here the sdl/sdr | |
2084 | * would blow up, so for now we don't handle unaligned 64-bit | |
2085 | * instructions on 32-bit kernels. | |
2086 | */ | |
2087 | if (!access_ok(VERIFY_WRITE, addr, 8)) | |
2088 | goto sigbus; | |
2089 | ||
2090 | MIPS16e_compute_return_epc(regs, &oldinst); | |
2091 | value = regs->regs[reg]; | |
2092 | StoreDW(addr, value, res); | |
2093 | if (res) | |
2094 | goto fault; | |
2095 | break; | |
2096 | #endif /* CONFIG_64BIT */ | |
2097 | ||
2098 | /* Cannot handle 64-bit instructions in 32-bit kernel */ | |
2099 | goto sigill; | |
2100 | ||
2101 | default: | |
2102 | /* | |
2103 | * Pheeee... We encountered an yet unknown instruction or | |
2104 | * cache coherence problem. Die sucker, die ... | |
2105 | */ | |
2106 | goto sigill; | |
2107 | } | |
2108 | ||
2109 | #ifdef CONFIG_DEBUG_FS | |
2110 | unaligned_instructions++; | |
2111 | #endif | |
2112 | ||
2113 | return; | |
2114 | ||
2115 | fault: | |
2116 | /* roll back jump/branch */ | |
2117 | regs->cp0_epc = origpc; | |
2118 | regs->regs[31] = orig31; | |
2119 | /* Did we have an exception handler installed? */ | |
2120 | if (fixup_exception(regs)) | |
2121 | return; | |
2122 | ||
2123 | die_if_kernel("Unhandled kernel unaligned access", regs); | |
2124 | force_sig(SIGSEGV, current); | |
2125 | ||
2126 | return; | |
2127 | ||
2128 | sigbus: | |
2129 | die_if_kernel("Unhandled kernel unaligned access", regs); | |
2130 | force_sig(SIGBUS, current); | |
2131 | ||
2132 | return; | |
2133 | ||
2134 | sigill: | |
2135 | die_if_kernel | |
2136 | ("Unhandled kernel unaligned access or invalid instruction", regs); | |
2137 | force_sig(SIGILL, current); | |
2138 | } | |
fc192e50 | 2139 | |
1da177e4 LT |
2140 | asmlinkage void do_ade(struct pt_regs *regs) |
2141 | { | |
c3fc5cd5 | 2142 | enum ctx_state prev_state; |
fe00f943 | 2143 | unsigned int __user *pc; |
1da177e4 | 2144 | mm_segment_t seg; |
1da177e4 | 2145 | |
c3fc5cd5 | 2146 | prev_state = exception_enter(); |
7f788d2d | 2147 | perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, |
a8b0ca17 | 2148 | 1, regs, regs->cp0_badvaddr); |
1da177e4 LT |
2149 | /* |
2150 | * Did we catch a fault trying to load an instruction? | |
1da177e4 | 2151 | */ |
34c2f668 | 2152 | if (regs->cp0_badvaddr == regs->cp0_epc) |
1da177e4 LT |
2153 | goto sigbus; |
2154 | ||
293c5bd1 | 2155 | if (user_mode(regs) && !test_thread_flag(TIF_FIXADE)) |
1da177e4 | 2156 | goto sigbus; |
6312e0ee AN |
2157 | if (unaligned_action == UNALIGNED_ACTION_SIGNAL) |
2158 | goto sigbus; | |
1da177e4 LT |
2159 | |
2160 | /* | |
2161 | * Do branch emulation only if we didn't forward the exception. | |
2162 | * This is all so but ugly ... | |
2163 | */ | |
34c2f668 LY |
2164 | |
2165 | /* | |
2166 | * Are we running in microMIPS mode? | |
2167 | */ | |
2168 | if (get_isa16_mode(regs->cp0_epc)) { | |
2169 | /* | |
2170 | * Did we catch a fault trying to load an instruction in | |
2171 | * 16-bit mode? | |
2172 | */ | |
2173 | if (regs->cp0_badvaddr == msk_isa16_mode(regs->cp0_epc)) | |
2174 | goto sigbus; | |
2175 | if (unaligned_action == UNALIGNED_ACTION_SHOW) | |
2176 | show_registers(regs); | |
2177 | ||
2178 | if (cpu_has_mmips) { | |
2179 | seg = get_fs(); | |
2180 | if (!user_mode(regs)) | |
2181 | set_fs(KERNEL_DS); | |
2182 | emulate_load_store_microMIPS(regs, | |
2183 | (void __user *)regs->cp0_badvaddr); | |
2184 | set_fs(seg); | |
2185 | ||
2186 | return; | |
2187 | } | |
2188 | ||
451b001b SH |
2189 | if (cpu_has_mips16) { |
2190 | seg = get_fs(); | |
2191 | if (!user_mode(regs)) | |
2192 | set_fs(KERNEL_DS); | |
2193 | emulate_load_store_MIPS16e(regs, | |
2194 | (void __user *)regs->cp0_badvaddr); | |
2195 | set_fs(seg); | |
2196 | ||
2197 | return; | |
2198 | } | |
2199 | ||
34c2f668 LY |
2200 | goto sigbus; |
2201 | } | |
2202 | ||
2203 | if (unaligned_action == UNALIGNED_ACTION_SHOW) | |
2204 | show_registers(regs); | |
2205 | pc = (unsigned int __user *)exception_epc(regs); | |
2206 | ||
1da177e4 LT |
2207 | seg = get_fs(); |
2208 | if (!user_mode(regs)) | |
2209 | set_fs(KERNEL_DS); | |
7f18f151 | 2210 | emulate_load_store_insn(regs, (void __user *)regs->cp0_badvaddr, pc); |
1da177e4 LT |
2211 | set_fs(seg); |
2212 | ||
2213 | return; | |
2214 | ||
2215 | sigbus: | |
2216 | die_if_kernel("Kernel unaligned instruction access", regs); | |
2217 | force_sig(SIGBUS, current); | |
2218 | ||
2219 | /* | |
2220 | * XXX On return from the signal handler we should advance the epc | |
2221 | */ | |
c3fc5cd5 | 2222 | exception_exit(prev_state); |
1da177e4 | 2223 | } |
6312e0ee AN |
2224 | |
2225 | #ifdef CONFIG_DEBUG_FS | |
2226 | extern struct dentry *mips_debugfs_dir; | |
2227 | static int __init debugfs_unaligned(void) | |
2228 | { | |
2229 | struct dentry *d; | |
2230 | ||
2231 | if (!mips_debugfs_dir) | |
2232 | return -ENODEV; | |
2233 | d = debugfs_create_u32("unaligned_instructions", S_IRUGO, | |
2234 | mips_debugfs_dir, &unaligned_instructions); | |
b517531c Z |
2235 | if (!d) |
2236 | return -ENOMEM; | |
6312e0ee AN |
2237 | d = debugfs_create_u32("unaligned_action", S_IRUGO | S_IWUSR, |
2238 | mips_debugfs_dir, &unaligned_action); | |
b517531c Z |
2239 | if (!d) |
2240 | return -ENOMEM; | |
6312e0ee AN |
2241 | return 0; |
2242 | } | |
2243 | __initcall(debugfs_unaligned); | |
2244 | #endif |