diff options
Diffstat (limited to 'arch/arm26/kernel/entry.S')
-rw-r--r-- | arch/arm26/kernel/entry.S | 951 |
1 files changed, 0 insertions, 951 deletions
diff --git a/arch/arm26/kernel/entry.S b/arch/arm26/kernel/entry.S deleted file mode 100644 index 7ffcc6e4770e..000000000000 --- a/arch/arm26/kernel/entry.S +++ /dev/null | |||
@@ -1,951 +0,0 @@ | |||
1 | /* arch/arm26/kernel/entry.S | ||
2 | * | ||
3 | * Assembled from chunks of code in arch/arm | ||
4 | * | ||
5 | * Copyright (C) 2003 Ian Molton | ||
6 | * Based on the work of RMK. | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | #include <linux/linkage.h> | ||
11 | |||
12 | #include <asm/assembler.h> | ||
13 | #include <asm/asm-offsets.h> | ||
14 | #include <asm/errno.h> | ||
15 | #include <asm/hardware.h> | ||
16 | #include <asm/sysirq.h> | ||
17 | #include <asm/thread_info.h> | ||
18 | #include <asm/page.h> | ||
19 | #include <asm/ptrace.h> | ||
20 | |||
21 | .macro zero_fp | ||
22 | #ifndef CONFIG_NO_FRAME_POINTER | ||
23 | mov fp, #0 | ||
24 | #endif | ||
25 | .endm | ||
26 | |||
27 | .text | ||
28 | |||
29 | @ Bad Abort numbers | ||
30 | @ ----------------- | ||
31 | @ | ||
32 | #define BAD_PREFETCH 0 | ||
33 | #define BAD_DATA 1 | ||
34 | #define BAD_ADDREXCPTN 2 | ||
35 | #define BAD_IRQ 3 | ||
36 | #define BAD_UNDEFINSTR 4 | ||
37 | |||
38 | @ OS version number used in SWIs | ||
39 | @ RISC OS is 0 | ||
40 | @ RISC iX is 8 | ||
41 | @ | ||
42 | #define OS_NUMBER 9 | ||
43 | #define ARMSWI_OFFSET 0x000f0000 | ||
44 | |||
45 | @ | ||
46 | @ Stack format (ensured by USER_* and SVC_*) | ||
47 | @ PSR and PC are comined on arm26 | ||
48 | @ | ||
49 | |||
50 | #define S_OFF 8 | ||
51 | |||
52 | #define S_OLD_R0 64 | ||
53 | #define S_PC 60 | ||
54 | #define S_LR 56 | ||
55 | #define S_SP 52 | ||
56 | #define S_IP 48 | ||
57 | #define S_FP 44 | ||
58 | #define S_R10 40 | ||
59 | #define S_R9 36 | ||
60 | #define S_R8 32 | ||
61 | #define S_R7 28 | ||
62 | #define S_R6 24 | ||
63 | #define S_R5 20 | ||
64 | #define S_R4 16 | ||
65 | #define S_R3 12 | ||
66 | #define S_R2 8 | ||
67 | #define S_R1 4 | ||
68 | #define S_R0 0 | ||
69 | |||
70 | .macro save_user_regs | ||
71 | str r0, [sp, #-4]! @ Store SVC r0 | ||
72 | str lr, [sp, #-4]! @ Store user mode PC | ||
73 | sub sp, sp, #15*4 | ||
74 | stmia sp, {r0 - lr}^ @ Store the other user-mode regs | ||
75 | mov r0, r0 | ||
76 | .endm | ||
77 | |||
78 | .macro slow_restore_user_regs | ||
79 | ldmia sp, {r0 - lr}^ @ restore the user regs not including PC | ||
80 | mov r0, r0 | ||
81 | ldr lr, [sp, #15*4] @ get user PC | ||
82 | add sp, sp, #15*4+8 @ free stack | ||
83 | movs pc, lr @ return | ||
84 | .endm | ||
85 | |||
86 | .macro fast_restore_user_regs | ||
87 | add sp, sp, #S_OFF | ||
88 | ldmib sp, {r1 - lr}^ | ||
89 | mov r0, r0 | ||
90 | ldr lr, [sp, #15*4] | ||
91 | add sp, sp, #15*4+8 | ||
92 | movs pc, lr | ||
93 | .endm | ||
94 | |||
95 | .macro save_svc_regs | ||
96 | str sp, [sp, #-16]! | ||
97 | str lr, [sp, #8] | ||
98 | str lr, [sp, #4] | ||
99 | stmfd sp!, {r0 - r12} | ||
100 | mov r0, #-1 | ||
101 | str r0, [sp, #S_OLD_R0] | ||
102 | zero_fp | ||
103 | .endm | ||
104 | |||
105 | .macro save_svc_regs_irq | ||
106 | str sp, [sp, #-16]! | ||
107 | str lr, [sp, #4] | ||
108 | ldr lr, .LCirq | ||
109 | ldr lr, [lr] | ||
110 | str lr, [sp, #8] | ||
111 | stmfd sp!, {r0 - r12} | ||
112 | mov r0, #-1 | ||
113 | str r0, [sp, #S_OLD_R0] | ||
114 | zero_fp | ||
115 | .endm | ||
116 | |||
117 | .macro restore_svc_regs | ||
118 | ldmfd sp, {r0 - pc}^ | ||
119 | .endm | ||
120 | |||
121 | .macro mask_pc, rd, rm | ||
122 | bic \rd, \rm, #PCMASK | ||
123 | .endm | ||
124 | |||
125 | .macro disable_irqs, temp | ||
126 | mov \temp, pc | ||
127 | orr \temp, \temp, #PSR_I_BIT | ||
128 | teqp \temp, #0 | ||
129 | .endm | ||
130 | |||
131 | .macro enable_irqs, temp | ||
132 | mov \temp, pc | ||
133 | and \temp, \temp, #~PSR_I_BIT | ||
134 | teqp \temp, #0 | ||
135 | .endm | ||
136 | |||
137 | .macro initialise_traps_extra | ||
138 | .endm | ||
139 | |||
140 | .macro get_thread_info, rd | ||
141 | mov \rd, sp, lsr #13 | ||
142 | mov \rd, \rd, lsl #13 | ||
143 | .endm | ||
144 | |||
145 | /* | ||
146 | * These are the registers used in the syscall handler, and allow us to | ||
147 | * have in theory up to 7 arguments to a function - r0 to r6. | ||
148 | * | ||
149 | * Note that tbl == why is intentional. | ||
150 | * | ||
151 | * We must set at least "tsk" and "why" when calling ret_with_reschedule. | ||
152 | */ | ||
153 | scno .req r7 @ syscall number | ||
154 | tbl .req r8 @ syscall table pointer | ||
155 | why .req r8 @ Linux syscall (!= 0) | ||
156 | tsk .req r9 @ current thread_info | ||
157 | |||
158 | /* | ||
159 | * Get the system call number. | ||
160 | */ | ||
161 | .macro get_scno | ||
162 | mask_pc lr, lr | ||
163 | ldr scno, [lr, #-4] @ get SWI instruction | ||
164 | .endm | ||
165 | /* | ||
166 | * ----------------------------------------------------------------------- | ||
167 | */ | ||
168 | |||
169 | /* | ||
170 | * We rely on the fact that R0 is at the bottom of the stack (due to | ||
171 | * slow/fast restore user regs). | ||
172 | */ | ||
173 | #if S_R0 != 0 | ||
174 | #error "Please fix" | ||
175 | #endif | ||
176 | |||
177 | /* | ||
178 | * This is the fast syscall return path. We do as little as | ||
179 | * possible here, and this includes saving r0 back into the SVC | ||
180 | * stack. | ||
181 | */ | ||
182 | ret_fast_syscall: | ||
183 | disable_irqs r1 @ disable interrupts | ||
184 | ldr r1, [tsk, #TI_FLAGS] | ||
185 | tst r1, #_TIF_WORK_MASK | ||
186 | bne fast_work_pending | ||
187 | fast_restore_user_regs | ||
188 | |||
189 | /* | ||
190 | * Ok, we need to do extra processing, enter the slow path. | ||
191 | */ | ||
192 | fast_work_pending: | ||
193 | str r0, [sp, #S_R0+S_OFF]! @ returned r0 | ||
194 | work_pending: | ||
195 | tst r1, #_TIF_NEED_RESCHED | ||
196 | bne work_resched | ||
197 | tst r1, #_TIF_SIGPENDING | ||
198 | beq no_work_pending | ||
199 | mov r0, sp @ 'regs' | ||
200 | mov r2, why @ 'syscall' | ||
201 | bl do_notify_resume | ||
202 | disable_irqs r1 @ disable interrupts | ||
203 | b no_work_pending | ||
204 | |||
205 | work_resched: | ||
206 | bl schedule | ||
207 | /* | ||
208 | * "slow" syscall return path. "why" tells us if this was a real syscall. | ||
209 | */ | ||
210 | ENTRY(ret_to_user) | ||
211 | ret_slow_syscall: | ||
212 | disable_irqs r1 @ disable interrupts | ||
213 | ldr r1, [tsk, #TI_FLAGS] | ||
214 | tst r1, #_TIF_WORK_MASK | ||
215 | bne work_pending | ||
216 | no_work_pending: | ||
217 | slow_restore_user_regs | ||
218 | |||
219 | /* | ||
220 | * This is how we return from a fork. | ||
221 | */ | ||
222 | ENTRY(ret_from_fork) | ||
223 | bl schedule_tail | ||
224 | get_thread_info tsk | ||
225 | ldr r1, [tsk, #TI_FLAGS] @ check for syscall tracing | ||
226 | mov why, #1 | ||
227 | tst r1, #_TIF_SYSCALL_TRACE @ are we tracing syscalls? | ||
228 | beq ret_slow_syscall | ||
229 | mov r1, sp | ||
230 | mov r0, #1 @ trace exit [IP = 1] | ||
231 | bl syscall_trace | ||
232 | b ret_slow_syscall | ||
233 | |||
234 | // FIXME - is this strictly necessary? | ||
235 | #include "calls.S" | ||
236 | |||
237 | /*============================================================================= | ||
238 | * SWI handler | ||
239 | *----------------------------------------------------------------------------- | ||
240 | */ | ||
241 | |||
242 | .align 5 | ||
243 | ENTRY(vector_swi) | ||
244 | save_user_regs | ||
245 | zero_fp | ||
246 | get_scno | ||
247 | |||
248 | enable_irqs ip | ||
249 | |||
250 | str r4, [sp, #-S_OFF]! @ push fifth arg | ||
251 | |||
252 | get_thread_info tsk | ||
253 | ldr ip, [tsk, #TI_FLAGS] @ check for syscall tracing | ||
254 | bic scno, scno, #0xff000000 @ mask off SWI op-code | ||
255 | eor scno, scno, #OS_NUMBER << 20 @ check OS number | ||
256 | adr tbl, sys_call_table @ load syscall table pointer | ||
257 | tst ip, #_TIF_SYSCALL_TRACE @ are we tracing syscalls? | ||
258 | bne __sys_trace | ||
259 | |||
260 | adral lr, ret_fast_syscall @ set return address | ||
261 | orral lr, lr, #PSR_I_BIT | MODE_SVC26 @ Force SVC mode on return | ||
262 | cmp scno, #NR_syscalls @ check upper syscall limit | ||
263 | ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine | ||
264 | |||
265 | add r1, sp, #S_OFF | ||
266 | 2: mov why, #0 @ no longer a real syscall | ||
267 | cmp scno, #ARMSWI_OFFSET | ||
268 | eor r0, scno, #OS_NUMBER << 20 @ put OS number back | ||
269 | bcs arm_syscall | ||
270 | b sys_ni_syscall @ not private func | ||
271 | |||
272 | /* | ||
273 | * This is the really slow path. We're going to be doing | ||
274 | * context switches, and waiting for our parent to respond. | ||
275 | */ | ||
276 | __sys_trace: | ||
277 | add r1, sp, #S_OFF | ||
278 | mov r0, #0 @ trace entry [IP = 0] | ||
279 | bl syscall_trace | ||
280 | |||
281 | adral lr, __sys_trace_return @ set return address | ||
282 | orral lr, lr, #PSR_I_BIT | MODE_SVC26 @ Force SVC mode on return | ||
283 | add r1, sp, #S_R0 + S_OFF @ pointer to regs | ||
284 | cmp scno, #NR_syscalls @ check upper syscall limit | ||
285 | ldmccia r1, {r0 - r3} @ have to reload r0 - r3 | ||
286 | ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine | ||
287 | b 2b | ||
288 | |||
289 | __sys_trace_return: | ||
290 | str r0, [sp, #S_R0 + S_OFF]! @ save returned r0 | ||
291 | mov r1, sp | ||
292 | mov r0, #1 @ trace exit [IP = 1] | ||
293 | bl syscall_trace | ||
294 | b ret_slow_syscall | ||
295 | |||
296 | .align 5 | ||
297 | |||
298 | .type sys_call_table, #object | ||
299 | ENTRY(sys_call_table) | ||
300 | #include "calls.S" | ||
301 | |||
302 | /*============================================================================ | ||
303 | * Special system call wrappers | ||
304 | */ | ||
305 | @ r0 = syscall number | ||
306 | @ r5 = syscall table | ||
307 | .type sys_syscall, #function | ||
308 | sys_syscall: | ||
309 | eor scno, r0, #OS_NUMBER << 20 | ||
310 | cmp scno, #NR_syscalls @ check range | ||
311 | stmleia sp, {r5, r6} @ shuffle args | ||
312 | movle r0, r1 | ||
313 | movle r1, r2 | ||
314 | movle r2, r3 | ||
315 | movle r3, r4 | ||
316 | ldrle pc, [tbl, scno, lsl #2] | ||
317 | b sys_ni_syscall | ||
318 | |||
319 | sys_fork_wrapper: | ||
320 | add r0, sp, #S_OFF | ||
321 | b sys_fork | ||
322 | |||
323 | sys_vfork_wrapper: | ||
324 | add r0, sp, #S_OFF | ||
325 | b sys_vfork | ||
326 | |||
327 | sys_execve_wrapper: | ||
328 | add r3, sp, #S_OFF | ||
329 | b sys_execve | ||
330 | |||
331 | sys_clone_wapper: | ||
332 | add r2, sp, #S_OFF | ||
333 | b sys_clone | ||
334 | |||
335 | sys_sigsuspend_wrapper: | ||
336 | add r3, sp, #S_OFF | ||
337 | b sys_sigsuspend | ||
338 | |||
339 | sys_rt_sigsuspend_wrapper: | ||
340 | add r2, sp, #S_OFF | ||
341 | b sys_rt_sigsuspend | ||
342 | |||
343 | sys_sigreturn_wrapper: | ||
344 | add r0, sp, #S_OFF | ||
345 | b sys_sigreturn | ||
346 | |||
347 | sys_rt_sigreturn_wrapper: | ||
348 | add r0, sp, #S_OFF | ||
349 | b sys_rt_sigreturn | ||
350 | |||
351 | sys_sigaltstack_wrapper: | ||
352 | ldr r2, [sp, #S_OFF + S_SP] | ||
353 | b do_sigaltstack | ||
354 | |||
355 | /* | ||
356 | * Note: off_4k (r5) is always units of 4K. If we can't do the requested | ||
357 | * offset, we return EINVAL. FIXME - this lost some stuff from arm32 to | ||
358 | * ifdefs. check it out. | ||
359 | */ | ||
360 | sys_mmap2: | ||
361 | tst r5, #((1 << (PAGE_SHIFT - 12)) - 1) | ||
362 | moveq r5, r5, lsr #PAGE_SHIFT - 12 | ||
363 | streq r5, [sp, #4] | ||
364 | beq do_mmap2 | ||
365 | mov r0, #-EINVAL | ||
366 | RETINSTR(mov,pc, lr) | ||
367 | |||
368 | /* | ||
369 | * Design issues: | ||
370 | * - We have several modes that each vector can be called from, | ||
371 | * each with its own set of registers. On entry to any vector, | ||
372 | * we *must* save the registers used in *that* mode. | ||
373 | * | ||
374 | * - This code must be as fast as possible. | ||
375 | * | ||
376 | * There are a few restrictions on the vectors: | ||
377 | * - the SWI vector cannot be called from *any* non-user mode | ||
378 | * | ||
379 | * - the FP emulator is *never* called from *any* non-user mode undefined | ||
380 | * instruction. | ||
381 | * | ||
382 | */ | ||
383 | |||
384 | .text | ||
385 | |||
386 | .macro handle_irq | ||
387 | 1: mov r4, #IOC_BASE | ||
388 | ldrb r6, [r4, #0x24] @ get high priority first | ||
389 | adr r5, irq_prio_h | ||
390 | teq r6, #0 | ||
391 | ldreqb r6, [r4, #0x14] @ get low priority | ||
392 | adreq r5, irq_prio_l | ||
393 | |||
394 | teq r6, #0 @ If an IRQ happened... | ||
395 | ldrneb r0, [r5, r6] @ get IRQ number | ||
396 | movne r1, sp @ get struct pt_regs | ||
397 | adrne lr, 1b @ Set return address to 1b | ||
398 | orrne lr, lr, #PSR_I_BIT | MODE_SVC26 @ (and force SVC mode) | ||
399 | bne asm_do_IRQ @ process IRQ (if asserted) | ||
400 | .endm | ||
401 | |||
402 | |||
403 | /* | ||
404 | * Interrupt table (incorporates priority) | ||
405 | */ | ||
406 | .macro irq_prio_table | ||
407 | irq_prio_l: .byte 0, 0, 1, 0, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 | ||
408 | .byte 4, 0, 1, 0, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 | ||
409 | .byte 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 | ||
410 | .byte 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 | ||
411 | .byte 6, 6, 6, 6, 6, 6, 6, 6, 3, 3, 3, 3, 3, 3, 3, 3 | ||
412 | .byte 6, 6, 6, 6, 6, 6, 6, 6, 3, 3, 3, 3, 3, 3, 3, 3 | ||
413 | .byte 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 | ||
414 | .byte 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 | ||
415 | .byte 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 | ||
416 | .byte 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 | ||
417 | .byte 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 | ||
418 | .byte 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 | ||
419 | .byte 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 | ||
420 | .byte 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 | ||
421 | .byte 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 | ||
422 | .byte 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 | ||
423 | irq_prio_h: .byte 0, 8, 9, 8,10,10,10,10,11,11,11,11,10,10,10,10 | ||
424 | .byte 12, 8, 9, 8,10,10,10,10,11,11,11,11,10,10,10,10 | ||
425 | .byte 13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10 | ||
426 | .byte 13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10 | ||
427 | .byte 14,14,14,14,10,10,10,10,11,11,11,11,10,10,10,10 | ||
428 | .byte 14,14,14,14,10,10,10,10,11,11,11,11,10,10,10,10 | ||
429 | .byte 13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10 | ||
430 | .byte 13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10 | ||
431 | .byte 15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10 | ||
432 | .byte 15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10 | ||
433 | .byte 13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10 | ||
434 | .byte 13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10 | ||
435 | .byte 15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10 | ||
436 | .byte 15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10 | ||
437 | .byte 13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10 | ||
438 | .byte 13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10 | ||
439 | .endm | ||
440 | |||
441 | #if 1 | ||
442 | /* | ||
443 | * Uncomment these if you wish to get more debugging into about data aborts. | ||
444 | * FIXME - I bet we can find a way to encode these and keep performance. | ||
445 | */ | ||
446 | #define FAULT_CODE_LDRSTRPOST 0x80 | ||
447 | #define FAULT_CODE_LDRSTRPRE 0x40 | ||
448 | #define FAULT_CODE_LDRSTRREG 0x20 | ||
449 | #define FAULT_CODE_LDMSTM 0x10 | ||
450 | #define FAULT_CODE_LDCSTC 0x08 | ||
451 | #endif | ||
452 | #define FAULT_CODE_PREFETCH 0x04 | ||
453 | #define FAULT_CODE_WRITE 0x02 | ||
454 | #define FAULT_CODE_FORCECOW 0x01 | ||
455 | |||
456 | /*============================================================================= | ||
457 | * Undefined FIQs | ||
458 | *----------------------------------------------------------------------------- | ||
459 | */ | ||
460 | _unexp_fiq: ldr sp, .LCfiq | ||
461 | mov r12, #IOC_BASE | ||
462 | strb r12, [r12, #0x38] @ Disable FIQ register | ||
463 | teqp pc, #PSR_I_BIT | PSR_F_BIT | MODE_SVC26 | ||
464 | mov r0, r0 | ||
465 | stmfd sp!, {r0 - r3, ip, lr} | ||
466 | adr r0, Lfiqmsg | ||
467 | bl printk | ||
468 | ldmfd sp!, {r0 - r3, ip, lr} | ||
469 | teqp pc, #PSR_I_BIT | PSR_F_BIT | MODE_FIQ26 | ||
470 | mov r0, r0 | ||
471 | movs pc, lr | ||
472 | |||
473 | Lfiqmsg: .ascii "*** Unexpected FIQ\n\0" | ||
474 | .align | ||
475 | |||
476 | .LCfiq: .word __temp_fiq | ||
477 | .LCirq: .word __temp_irq | ||
478 | |||
479 | /*============================================================================= | ||
480 | * Undefined instruction handler | ||
481 | *----------------------------------------------------------------------------- | ||
482 | * Handles floating point instructions | ||
483 | */ | ||
484 | vector_undefinstr: | ||
485 | tst lr, #MODE_SVC26 @ did we come from a non-user mode? | ||
486 | bne __und_svc @ yes - deal with it. | ||
487 | /* Otherwise, fall through for the user-space (common) case. */ | ||
488 | save_user_regs | ||
489 | zero_fp @ zero frame pointer | ||
490 | teqp pc, #PSR_I_BIT | MODE_SVC26 @ disable IRQs | ||
491 | .Lbug_undef: | ||
492 | ldr r4, .LC2 | ||
493 | ldr pc, [r4] @ Call FP module entry point | ||
494 | /* FIXME - should we trap for a null pointer here? */ | ||
495 | |||
496 | /* The SVC mode case */ | ||
497 | __und_svc: save_svc_regs @ Non-user mode | ||
498 | mask_pc r0, lr | ||
499 | and r2, lr, #3 | ||
500 | sub r0, r0, #4 | ||
501 | mov r1, sp | ||
502 | bl do_undefinstr | ||
503 | restore_svc_regs | ||
504 | |||
505 | /* We get here if the FP emulator doesnt handle the undef instr. | ||
506 | * If the insn WAS handled, the emulator jumps to ret_from_exception by itself/ | ||
507 | */ | ||
508 | .globl fpundefinstr | ||
509 | fpundefinstr: | ||
510 | mov r0, lr | ||
511 | mov r1, sp | ||
512 | teqp pc, #MODE_SVC26 | ||
513 | bl do_undefinstr | ||
514 | b ret_from_exception @ Normal FP exit | ||
515 | |||
516 | #if defined CONFIG_FPE_NWFPE || defined CONFIG_FPE_FASTFPE | ||
517 | /* The FPE is always present */ | ||
518 | .equ fpe_not_present, 0 | ||
519 | #else | ||
520 | /* We get here if an undefined instruction happens and the floating | ||
521 | * point emulator is not present. If the offending instruction was | ||
522 | * a WFS, we just perform a normal return as if we had emulated the | ||
523 | * operation. This is a hack to allow some basic userland binaries | ||
524 | * to run so that the emulator module proper can be loaded. --philb | ||
525 | * FIXME - probably a broken useless hack... | ||
526 | */ | ||
527 | fpe_not_present: | ||
528 | adr r10, wfs_mask_data | ||
529 | ldmia r10, {r4, r5, r6, r7, r8} | ||
530 | ldr r10, [sp, #S_PC] @ Load PC | ||
531 | sub r10, r10, #4 | ||
532 | mask_pc r10, r10 | ||
533 | ldrt r10, [r10] @ get instruction | ||
534 | and r5, r10, r5 | ||
535 | teq r5, r4 @ Is it WFS? | ||
536 | beq ret_from_exception | ||
537 | and r5, r10, r8 | ||
538 | teq r5, r6 @ Is it LDF/STF on sp or fp? | ||
539 | teqne r5, r7 | ||
540 | bne fpundefinstr | ||
541 | tst r10, #0x00200000 @ Does it have WB | ||
542 | beq ret_from_exception | ||
543 | and r4, r10, #255 @ get offset | ||
544 | and r6, r10, #0x000f0000 | ||
545 | tst r10, #0x00800000 @ +/- | ||
546 | ldr r5, [sp, r6, lsr #14] @ Load reg | ||
547 | rsbeq r4, r4, #0 | ||
548 | add r5, r5, r4, lsl #2 | ||
549 | str r5, [sp, r6, lsr #14] @ Save reg | ||
550 | b ret_from_exception | ||
551 | |||
552 | wfs_mask_data: .word 0x0e200110 @ WFS/RFS | ||
553 | .word 0x0fef0fff | ||
554 | .word 0x0d0d0100 @ LDF [sp]/STF [sp] | ||
555 | .word 0x0d0b0100 @ LDF [fp]/STF [fp] | ||
556 | .word 0x0f0f0f00 | ||
557 | #endif | ||
558 | |||
559 | .LC2: .word fp_enter | ||
560 | |||
561 | /*============================================================================= | ||
562 | * Prefetch abort handler | ||
563 | *----------------------------------------------------------------------------- | ||
564 | */ | ||
565 | #define DEBUG_UNDEF | ||
566 | /* remember: lr = USR pc */ | ||
567 | vector_prefetch: | ||
568 | sub lr, lr, #4 | ||
569 | tst lr, #MODE_SVC26 | ||
570 | bne __pabt_invalid | ||
571 | save_user_regs | ||
572 | teqp pc, #MODE_SVC26 @ Enable IRQs... | ||
573 | mask_pc r0, lr @ Address of abort | ||
574 | mov r1, sp @ Tasks registers | ||
575 | bl do_PrefetchAbort | ||
576 | teq r0, #0 @ If non-zero, we believe this abort.. | ||
577 | bne ret_from_exception | ||
578 | #ifdef DEBUG_UNDEF | ||
579 | adr r0, t | ||
580 | bl printk | ||
581 | #endif | ||
582 | ldr lr, [sp,#S_PC] @ FIXME program to test this on. I think its | ||
583 | b .Lbug_undef @ broken at the moment though!) | ||
584 | |||
585 | __pabt_invalid: save_svc_regs | ||
586 | mov r0, sp @ Prefetch aborts are definitely *not* | ||
587 | mov r1, #BAD_PREFETCH @ allowed in non-user modes. We cant | ||
588 | and r2, lr, #3 @ recover from this problem. | ||
589 | b bad_mode | ||
590 | |||
591 | #ifdef DEBUG_UNDEF | ||
592 | t: .ascii "*** undef ***\r\n\0" | ||
593 | .align | ||
594 | #endif | ||
595 | |||
596 | /*============================================================================= | ||
597 | * Address exception handler | ||
598 | *----------------------------------------------------------------------------- | ||
599 | * These aren't too critical. | ||
600 | * (they're not supposed to happen). | ||
601 | * In order to debug the reason for address exceptions in non-user modes, | ||
602 | * we have to obtain all the registers so that we can see what's going on. | ||
603 | */ | ||
604 | |||
605 | vector_addrexcptn: | ||
606 | sub lr, lr, #8 | ||
607 | tst lr, #3 | ||
608 | bne Laddrexcptn_not_user | ||
609 | save_user_regs | ||
610 | teq pc, #MODE_SVC26 | ||
611 | mask_pc r0, lr @ Point to instruction | ||
612 | mov r1, sp @ Point to registers | ||
613 | mov r2, #0x400 | ||
614 | mov lr, pc | ||
615 | bl do_excpt | ||
616 | b ret_from_exception | ||
617 | |||
618 | Laddrexcptn_not_user: | ||
619 | save_svc_regs | ||
620 | and r2, lr, #3 | ||
621 | teq r2, #3 | ||
622 | bne Laddrexcptn_illegal_mode | ||
623 | teqp pc, #MODE_SVC26 | ||
624 | mask_pc r0, lr | ||
625 | mov r1, sp | ||
626 | orr r2, r2, #0x400 | ||
627 | bl do_excpt | ||
628 | ldmia sp, {r0 - lr} @ I cant remember the reason I changed this... | ||
629 | add sp, sp, #15*4 | ||
630 | movs pc, lr | ||
631 | |||
632 | Laddrexcptn_illegal_mode: | ||
633 | mov r0, sp | ||
634 | str lr, [sp, #-4]! | ||
635 | orr r1, r2, #PSR_I_BIT | PSR_F_BIT | ||
636 | teqp r1, #0 @ change into mode (wont be user mode) | ||
637 | mov r0, r0 | ||
638 | mov r1, r8 @ Any register from r8 - r14 can be banked | ||
639 | mov r2, r9 | ||
640 | mov r3, r10 | ||
641 | mov r4, r11 | ||
642 | mov r5, r12 | ||
643 | mov r6, r13 | ||
644 | mov r7, r14 | ||
645 | teqp pc, #PSR_F_BIT | MODE_SVC26 @ back to svc | ||
646 | mov r0, r0 | ||
647 | stmfd sp!, {r1-r7} | ||
648 | ldmia r0, {r0-r7} | ||
649 | stmfd sp!, {r0-r7} | ||
650 | mov r0, sp | ||
651 | mov r1, #BAD_ADDREXCPTN | ||
652 | b bad_mode | ||
653 | |||
654 | /*============================================================================= | ||
655 | * Interrupt (IRQ) handler | ||
656 | *----------------------------------------------------------------------------- | ||
657 | * Note: if the IRQ was taken whilst in user mode, then *no* kernel routine | ||
658 | * is running, so do not have to save svc lr. | ||
659 | * | ||
660 | * Entered in IRQ mode. | ||
661 | */ | ||
662 | |||
663 | vector_IRQ: ldr sp, .LCirq @ Setup some temporary stack | ||
664 | sub lr, lr, #4 | ||
665 | str lr, [sp] @ push return address | ||
666 | |||
667 | tst lr, #3 | ||
668 | bne __irq_non_usr | ||
669 | |||
670 | __irq_usr: teqp pc, #PSR_I_BIT | MODE_SVC26 @ Enter SVC mode | ||
671 | mov r0, r0 | ||
672 | |||
673 | ldr lr, .LCirq | ||
674 | ldr lr, [lr] @ Restore lr for jump back to USR | ||
675 | |||
676 | save_user_regs | ||
677 | |||
678 | handle_irq | ||
679 | |||
680 | mov why, #0 | ||
681 | get_thread_info tsk | ||
682 | b ret_to_user | ||
683 | |||
684 | @ Place the IRQ priority table here so that the handle_irq macros above | ||
685 | @ and below here can access it. | ||
686 | |||
687 | irq_prio_table | ||
688 | |||
689 | __irq_non_usr: teqp pc, #PSR_I_BIT | MODE_SVC26 @ Enter SVC mode | ||
690 | mov r0, r0 | ||
691 | |||
692 | save_svc_regs_irq | ||
693 | |||
694 | and r2, lr, #3 | ||
695 | teq r2, #3 | ||
696 | bne __irq_invalid @ IRQ not from SVC mode | ||
697 | |||
698 | handle_irq | ||
699 | |||
700 | restore_svc_regs | ||
701 | |||
702 | __irq_invalid: mov r0, sp | ||
703 | mov r1, #BAD_IRQ | ||
704 | b bad_mode | ||
705 | |||
706 | /*============================================================================= | ||
707 | * Data abort handler code | ||
708 | *----------------------------------------------------------------------------- | ||
709 | * | ||
710 | * This handles both exceptions from user and SVC modes, computes the address | ||
711 | * range of the problem, and does any correction that is required. It then | ||
712 | * calls the kernel data abort routine. | ||
713 | * | ||
714 | * This is where I wish that the ARM would tell you which address aborted. | ||
715 | */ | ||
716 | |||
717 | vector_data: sub lr, lr, #8 @ Correct lr | ||
718 | tst lr, #3 | ||
719 | bne Ldata_not_user | ||
720 | save_user_regs | ||
721 | teqp pc, #MODE_SVC26 | ||
722 | mask_pc r0, lr | ||
723 | bl Ldata_do | ||
724 | b ret_from_exception | ||
725 | |||
726 | Ldata_not_user: | ||
727 | save_svc_regs | ||
728 | and r2, lr, #3 | ||
729 | teq r2, #3 | ||
730 | bne Ldata_illegal_mode | ||
731 | tst lr, #PSR_I_BIT | ||
732 | teqeqp pc, #MODE_SVC26 | ||
733 | mask_pc r0, lr | ||
734 | bl Ldata_do | ||
735 | restore_svc_regs | ||
736 | |||
737 | Ldata_illegal_mode: | ||
738 | mov r0, sp | ||
739 | mov r1, #BAD_DATA | ||
740 | b bad_mode | ||
741 | |||
742 | Ldata_do: mov r3, sp | ||
743 | ldr r4, [r0] @ Get instruction | ||
744 | mov r2, #0 | ||
745 | tst r4, #1 << 20 @ Check to see if it is a write instruction | ||
746 | orreq r2, r2, #FAULT_CODE_WRITE @ Indicate write instruction | ||
747 | mov r1, r4, lsr #22 @ Now branch to the relevent processing routine | ||
748 | and r1, r1, #15 << 2 | ||
749 | add pc, pc, r1 | ||
750 | movs pc, lr | ||
751 | b Ldata_unknown | ||
752 | b Ldata_unknown | ||
753 | b Ldata_unknown | ||
754 | b Ldata_unknown | ||
755 | b Ldata_ldrstr_post @ ldr rd, [rn], #m | ||
756 | b Ldata_ldrstr_numindex @ ldr rd, [rn, #m] @ RegVal | ||
757 | b Ldata_ldrstr_post @ ldr rd, [rn], rm | ||
758 | b Ldata_ldrstr_regindex @ ldr rd, [rn, rm] | ||
759 | b Ldata_ldmstm @ ldm*a rn, <rlist> | ||
760 | b Ldata_ldmstm @ ldm*b rn, <rlist> | ||
761 | b Ldata_unknown | ||
762 | b Ldata_unknown | ||
763 | b Ldata_ldrstr_post @ ldc rd, [rn], #m @ Same as ldr rd, [rn], #m | ||
764 | b Ldata_ldcstc_pre @ ldc rd, [rn, #m] | ||
765 | b Ldata_unknown | ||
766 | Ldata_unknown: @ Part of jumptable | ||
767 | mov r0, r1 | ||
768 | mov r1, r4 | ||
769 | mov r2, r3 | ||
770 | b baddataabort | ||
771 | |||
772 | Ldata_ldrstr_post: | ||
773 | mov r0, r4, lsr #14 @ Get Rn | ||
774 | and r0, r0, #15 << 2 @ Mask out reg. | ||
775 | teq r0, #15 << 2 | ||
776 | ldr r0, [r3, r0] @ Get register | ||
777 | biceq r0, r0, #PCMASK | ||
778 | mov r1, r0 | ||
779 | #ifdef FAULT_CODE_LDRSTRPOST | ||
780 | orr r2, r2, #FAULT_CODE_LDRSTRPOST | ||
781 | #endif | ||
782 | b do_DataAbort | ||
783 | |||
784 | Ldata_ldrstr_numindex: | ||
785 | mov r0, r4, lsr #14 @ Get Rn | ||
786 | and r0, r0, #15 << 2 @ Mask out reg. | ||
787 | teq r0, #15 << 2 | ||
788 | ldr r0, [r3, r0] @ Get register | ||
789 | mov r1, r4, lsl #20 | ||
790 | biceq r0, r0, #PCMASK | ||
791 | tst r4, #1 << 23 | ||
792 | addne r0, r0, r1, lsr #20 | ||
793 | subeq r0, r0, r1, lsr #20 | ||
794 | mov r1, r0 | ||
795 | #ifdef FAULT_CODE_LDRSTRPRE | ||
796 | orr r2, r2, #FAULT_CODE_LDRSTRPRE | ||
797 | #endif | ||
798 | b do_DataAbort | ||
799 | |||
800 | Ldata_ldrstr_regindex: | ||
801 | mov r0, r4, lsr #14 @ Get Rn | ||
802 | and r0, r0, #15 << 2 @ Mask out reg. | ||
803 | teq r0, #15 << 2 | ||
804 | ldr r0, [r3, r0] @ Get register | ||
805 | and r7, r4, #15 | ||
806 | biceq r0, r0, #PCMASK | ||
807 | teq r7, #15 @ Check for PC | ||
808 | ldr r7, [r3, r7, lsl #2] @ Get Rm | ||
809 | and r8, r4, #0x60 @ Get shift types | ||
810 | biceq r7, r7, #PCMASK | ||
811 | mov r9, r4, lsr #7 @ Get shift amount | ||
812 | and r9, r9, #31 | ||
813 | teq r8, #0 | ||
814 | moveq r7, r7, lsl r9 | ||
815 | teq r8, #0x20 @ LSR shift | ||
816 | moveq r7, r7, lsr r9 | ||
817 | teq r8, #0x40 @ ASR shift | ||
818 | moveq r7, r7, asr r9 | ||
819 | teq r8, #0x60 @ ROR shift | ||
820 | moveq r7, r7, ror r9 | ||
821 | tst r4, #1 << 23 | ||
822 | addne r0, r0, r7 | ||
823 | subeq r0, r0, r7 @ Apply correction | ||
824 | mov r1, r0 | ||
825 | #ifdef FAULT_CODE_LDRSTRREG | ||
826 | orr r2, r2, #FAULT_CODE_LDRSTRREG | ||
827 | #endif | ||
828 | b do_DataAbort | ||
829 | |||
830 | Ldata_ldmstm: | ||
831 | mov r7, #0x11 | ||
832 | orr r7, r7, r7, lsl #8 | ||
833 | and r0, r4, r7 | ||
834 | and r1, r4, r7, lsl #1 | ||
835 | add r0, r0, r1, lsr #1 | ||
836 | and r1, r4, r7, lsl #2 | ||
837 | add r0, r0, r1, lsr #2 | ||
838 | and r1, r4, r7, lsl #3 | ||
839 | add r0, r0, r1, lsr #3 | ||
840 | add r0, r0, r0, lsr #8 | ||
841 | add r0, r0, r0, lsr #4 | ||
842 | and r7, r0, #15 @ r7 = no. of registers to transfer. | ||
843 | mov r5, r4, lsr #14 @ Get Rn | ||
844 | and r5, r5, #15 << 2 | ||
845 | ldr r0, [r3, r5] @ Get reg | ||
846 | eor r6, r4, r4, lsl #2 | ||
847 | tst r6, #1 << 23 @ Check inc/dec ^ writeback | ||
848 | rsbeq r7, r7, #0 | ||
849 | add r7, r0, r7, lsl #2 @ Do correction (signed) | ||
850 | subne r1, r7, #1 | ||
851 | subeq r1, r0, #1 | ||
852 | moveq r0, r7 | ||
853 | tst r4, #1 << 21 @ Check writeback | ||
854 | strne r7, [r3, r5] | ||
855 | eor r6, r4, r4, lsl #1 | ||
856 | tst r6, #1 << 24 @ Check Pre/Post ^ inc/dec | ||
857 | addeq r0, r0, #4 | ||
858 | addeq r1, r1, #4 | ||
859 | teq r5, #15*4 @ CHECK FOR PC | ||
860 | biceq r1, r1, #PCMASK | ||
861 | biceq r0, r0, #PCMASK | ||
862 | #ifdef FAULT_CODE_LDMSTM | ||
863 | orr r2, r2, #FAULT_CODE_LDMSTM | ||
864 | #endif | ||
865 | b do_DataAbort | ||
866 | |||
867 | Ldata_ldcstc_pre: | ||
868 | mov r0, r4, lsr #14 @ Get Rn | ||
869 | and r0, r0, #15 << 2 @ Mask out reg. | ||
870 | teq r0, #15 << 2 | ||
871 | ldr r0, [r3, r0] @ Get register | ||
872 | mov r1, r4, lsl #24 @ Get offset | ||
873 | biceq r0, r0, #PCMASK | ||
874 | tst r4, #1 << 23 | ||
875 | addne r0, r0, r1, lsr #24 | ||
876 | subeq r0, r0, r1, lsr #24 | ||
877 | mov r1, r0 | ||
878 | #ifdef FAULT_CODE_LDCSTC | ||
879 | orr r2, r2, #FAULT_CODE_LDCSTC | ||
880 | #endif | ||
881 | b do_DataAbort | ||
882 | |||
883 | |||
884 | /* | ||
885 | * This is the return code to user mode for abort handlers | ||
886 | */ | ||
887 | ENTRY(ret_from_exception) | ||
888 | get_thread_info tsk | ||
889 | mov why, #0 | ||
890 | b ret_to_user | ||
891 | |||
892 | .data | ||
893 | ENTRY(fp_enter) | ||
894 | .word fpe_not_present | ||
895 | .text | ||
896 | /* | ||
897 | * Register switch for older 26-bit only ARMs | ||
898 | */ | ||
899 | ENTRY(__switch_to) | ||
900 | add r0, r0, #TI_CPU_SAVE | ||
901 | stmia r0, {r4 - sl, fp, sp, lr} | ||
902 | add r1, r1, #TI_CPU_SAVE | ||
903 | ldmia r1, {r4 - sl, fp, sp, pc}^ | ||
904 | |||
905 | /* | ||
906 | *============================================================================= | ||
907 | * Low-level interface code | ||
908 | *----------------------------------------------------------------------------- | ||
909 | * Trap initialisation | ||
910 | *----------------------------------------------------------------------------- | ||
911 | * | ||
912 | * Note - FIQ code has changed. The default is a couple of words in 0x1c, 0x20 | ||
913 | * that call _unexp_fiq. Nowever, we now copy the FIQ routine to 0x1c (removes | ||
914 | * some excess cycles). | ||
915 | * | ||
916 | * What we need to put into 0-0x1c are branches to branch to the kernel. | ||
917 | */ | ||
918 | |||
919 | .section ".init.text",#alloc,#execinstr | ||
920 | |||
921 | .Ljump_addresses: | ||
922 | swi SYS_ERROR0 | ||
923 | .word vector_undefinstr - 12 | ||
924 | .word vector_swi - 16 | ||
925 | .word vector_prefetch - 20 | ||
926 | .word vector_data - 24 | ||
927 | .word vector_addrexcptn - 28 | ||
928 | .word vector_IRQ - 32 | ||
929 | .word _unexp_fiq - 36 | ||
930 | b . + 8 | ||
931 | /* | ||
932 | * initialise the trap system | ||
933 | */ | ||
934 | ENTRY(__trap_init) | ||
935 | stmfd sp!, {r4 - r7, lr} | ||
936 | adr r1, .Ljump_addresses | ||
937 | ldmia r1, {r1 - r7, ip, lr} | ||
938 | orr r2, lr, r2, lsr #2 | ||
939 | orr r3, lr, r3, lsr #2 | ||
940 | orr r4, lr, r4, lsr #2 | ||
941 | orr r5, lr, r5, lsr #2 | ||
942 | orr r6, lr, r6, lsr #2 | ||
943 | orr r7, lr, r7, lsr #2 | ||
944 | orr ip, lr, ip, lsr #2 | ||
945 | mov r0, #0 | ||
946 | stmia r0, {r1 - r7, ip} | ||
947 | ldmfd sp!, {r4 - r7, pc}^ | ||
948 | |||
949 | .bss | ||
950 | __temp_irq: .space 4 @ saved lr_irq | ||
951 | __temp_fiq: .space 128 | ||