|  | /* arch/arm26/kernel/entry.S | 
|  | * | 
|  | * Assembled from chunks of code in arch/arm | 
|  | * | 
|  | * Copyright (C) 2003 Ian Molton | 
|  | * Based on the work of RMK. | 
|  | * | 
|  | */ | 
|  |  | 
|  | #include <linux/linkage.h> | 
|  |  | 
|  | #include <asm/assembler.h> | 
|  | #include <asm/asm_offsets.h> | 
|  | #include <asm/errno.h> | 
|  | #include <asm/hardware.h> | 
|  | #include <asm/sysirq.h> | 
|  | #include <asm/thread_info.h> | 
|  | #include <asm/page.h> | 
|  | #include <asm/ptrace.h> | 
|  |  | 
|  | .macro	zero_fp | 
|  | #ifndef CONFIG_NO_FRAME_POINTER | 
|  | mov	fp, #0 | 
|  | #endif | 
|  | .endm | 
|  |  | 
|  | .text | 
|  |  | 
|  | @ Bad Abort numbers | 
|  | @ ----------------- | 
|  | @ | 
|  | #define BAD_PREFETCH	0 | 
|  | #define BAD_DATA	1 | 
|  | #define BAD_ADDREXCPTN	2 | 
|  | #define BAD_IRQ		3 | 
|  | #define BAD_UNDEFINSTR	4 | 
|  |  | 
|  | @ OS version number used in SWIs | 
|  | @  RISC OS is 0 | 
|  | @  RISC iX is 8 | 
|  | @ | 
|  | #define OS_NUMBER	9 | 
|  | #define ARMSWI_OFFSET	0x000f0000 | 
|  |  | 
|  | @ | 
|  | @ Stack format (ensured by USER_* and SVC_*) | 
|  | @ PSR and PC are comined on arm26 | 
|  | @ | 
|  |  | 
|  | #define S_OFF		8 | 
|  |  | 
|  | #define S_OLD_R0	64 | 
|  | #define S_PC		60 | 
|  | #define S_LR		56 | 
|  | #define S_SP		52 | 
|  | #define S_IP		48 | 
|  | #define S_FP		44 | 
|  | #define S_R10		40 | 
|  | #define S_R9		36 | 
|  | #define S_R8		32 | 
|  | #define S_R7		28 | 
|  | #define S_R6		24 | 
|  | #define S_R5		20 | 
|  | #define S_R4		16 | 
|  | #define S_R3		12 | 
|  | #define S_R2		8 | 
|  | #define S_R1		4 | 
|  | #define S_R0		0 | 
|  |  | 
|  | .macro	save_user_regs | 
|  | str	r0, [sp, #-4]!   @ Store SVC r0 | 
|  | str	lr, [sp, #-4]!   @ Store user mode PC | 
|  | sub	sp, sp, #15*4 | 
|  | stmia	sp, {r0 - lr}^   @ Store the other user-mode regs | 
|  | mov	r0, r0 | 
|  | .endm | 
|  |  | 
|  | .macro	slow_restore_user_regs | 
|  | ldmia	sp, {r0 - lr}^   @ restore the user regs not including PC | 
|  | mov	r0, r0 | 
|  | ldr	lr, [sp, #15*4]  @ get user PC | 
|  | add	sp, sp, #15*4+8  @ free stack | 
|  | movs	pc, lr           @ return | 
|  | .endm | 
|  |  | 
|  | .macro	fast_restore_user_regs | 
|  | add	sp, sp, #S_OFF | 
|  | ldmib	sp, {r1 - lr}^ | 
|  | mov	r0, r0 | 
|  | ldr	lr, [sp, #15*4] | 
|  | add	sp, sp, #15*4+8 | 
|  | movs	pc, lr | 
|  | .endm | 
|  |  | 
|  | .macro	save_svc_regs | 
|  | str     sp, [sp, #-16]! | 
|  | str     lr, [sp, #8] | 
|  | str     lr, [sp, #4] | 
|  | stmfd   sp!, {r0 - r12} | 
|  | mov     r0, #-1 | 
|  | str     r0, [sp, #S_OLD_R0] | 
|  | zero_fp | 
|  | .endm | 
|  |  | 
|  | .macro	save_svc_regs_irq | 
|  | str     sp, [sp, #-16]! | 
|  | str     lr, [sp, #4] | 
|  | ldr     lr, .LCirq | 
|  | ldr     lr, [lr] | 
|  | str     lr, [sp, #8] | 
|  | stmfd   sp!, {r0 - r12} | 
|  | mov     r0, #-1 | 
|  | str     r0, [sp, #S_OLD_R0] | 
|  | zero_fp | 
|  | .endm | 
|  |  | 
|  | .macro	restore_svc_regs | 
|  | ldmfd   sp, {r0 - pc}^ | 
|  | .endm | 
|  |  | 
|  | .macro	mask_pc, rd, rm | 
|  | bic	\rd, \rm, #PCMASK | 
|  | .endm | 
|  |  | 
|  | .macro  disable_irqs, temp | 
|  | mov     \temp, pc | 
|  | orr     \temp, \temp, #PSR_I_BIT | 
|  | teqp    \temp, #0 | 
|  | .endm | 
|  |  | 
|  | .macro	enable_irqs, temp | 
|  | mov     \temp, pc | 
|  | and     \temp, \temp, #~PSR_I_BIT | 
|  | teqp	\temp, #0 | 
|  | .endm | 
|  |  | 
|  | .macro	initialise_traps_extra | 
|  | .endm | 
|  |  | 
|  | .macro	get_thread_info, rd | 
|  | mov	\rd, sp, lsr #13 | 
|  | mov	\rd, \rd, lsl #13 | 
|  | .endm | 
|  |  | 
|  | /* | 
|  | * These are the registers used in the syscall handler, and allow us to | 
|  | * have in theory up to 7 arguments to a function - r0 to r6. | 
|  | * | 
|  | * Note that tbl == why is intentional. | 
|  | * | 
|  | * We must set at least "tsk" and "why" when calling ret_with_reschedule. | 
|  | */ | 
|  | scno	.req	r7		@ syscall number | 
|  | tbl	.req	r8		@ syscall table pointer | 
|  | why	.req	r8		@ Linux syscall (!= 0) | 
|  | tsk	.req	r9		@ current thread_info | 
|  |  | 
|  | /* | 
|  | * Get the system call number. | 
|  | */ | 
|  | .macro	get_scno | 
|  | mask_pc	lr, lr | 
|  | ldr	scno, [lr, #-4]		@ get SWI instruction | 
|  | .endm | 
|  | /* | 
|  | *  ----------------------------------------------------------------------- | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * We rely on the fact that R0 is at the bottom of the stack (due to | 
|  | * slow/fast restore user regs). | 
|  | */ | 
|  | #if S_R0 != 0 | 
|  | #error "Please fix" | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | * This is the fast syscall return path.  We do as little as | 
|  | * possible here, and this includes saving r0 back into the SVC | 
|  | * stack. | 
|  | */ | 
|  | ret_fast_syscall: | 
|  | disable_irqs r1				@ disable interrupts | 
|  | ldr	r1, [tsk, #TI_FLAGS] | 
|  | tst	r1, #_TIF_WORK_MASK | 
|  | bne	fast_work_pending | 
|  | fast_restore_user_regs | 
|  |  | 
|  | /* | 
|  | * Ok, we need to do extra processing, enter the slow path. | 
|  | */ | 
|  | fast_work_pending: | 
|  | str	r0, [sp, #S_R0+S_OFF]!		@ returned r0 | 
|  | work_pending: | 
|  | tst	r1, #_TIF_NEED_RESCHED | 
|  | bne	work_resched | 
|  | tst	r1, #_TIF_NOTIFY_RESUME | _TIF_SIGPENDING | 
|  | beq	no_work_pending | 
|  | mov	r0, sp				@ 'regs' | 
|  | mov	r2, why				@ 'syscall' | 
|  | bl	do_notify_resume | 
|  | disable_irqs r1				@ disable interrupts | 
|  | b	no_work_pending | 
|  |  | 
|  | work_resched: | 
|  | bl	schedule | 
|  | /* | 
|  | * "slow" syscall return path.  "why" tells us if this was a real syscall. | 
|  | */ | 
|  | ENTRY(ret_to_user) | 
|  | ret_slow_syscall: | 
|  | disable_irqs r1				@ disable interrupts | 
|  | ldr	r1, [tsk, #TI_FLAGS] | 
|  | tst	r1, #_TIF_WORK_MASK | 
|  | bne	work_pending | 
|  | no_work_pending: | 
|  | slow_restore_user_regs | 
|  |  | 
|  | /* | 
|  | * This is how we return from a fork. | 
|  | */ | 
|  | ENTRY(ret_from_fork) | 
|  | bl	schedule_tail | 
|  | get_thread_info tsk | 
|  | ldr	r1, [tsk, #TI_FLAGS]		@ check for syscall tracing | 
|  | mov	why, #1 | 
|  | tst	r1, #_TIF_SYSCALL_TRACE		@ are we tracing syscalls? | 
|  | beq	ret_slow_syscall | 
|  | mov	r1, sp | 
|  | mov	r0, #1				@ trace exit [IP = 1] | 
|  | bl	syscall_trace | 
|  | b	ret_slow_syscall | 
|  |  | 
|  | // FIXME - is this strictly necessary? | 
|  | #include "calls.S" | 
|  |  | 
|  | /*============================================================================= | 
|  | * SWI handler | 
|  | *----------------------------------------------------------------------------- | 
|  | */ | 
|  |  | 
|  | .align	5 | 
|  | ENTRY(vector_swi) | 
|  | save_user_regs | 
|  | zero_fp | 
|  | get_scno | 
|  |  | 
|  | #ifdef CONFIG_ALIGNMENT_TRAP | 
|  | ldr	ip, __cr_alignment | 
|  | ldr	ip, [ip] | 
|  | mcr	p15, 0, ip, c1, c0		@ update control register | 
|  | #endif | 
|  | enable_irqs ip | 
|  |  | 
|  | str	r4, [sp, #-S_OFF]!		@ push fifth arg | 
|  |  | 
|  | get_thread_info tsk | 
|  | ldr	ip, [tsk, #TI_FLAGS]		@ check for syscall tracing | 
|  | bic	scno, scno, #0xff000000		@ mask off SWI op-code | 
|  | eor	scno, scno, #OS_NUMBER << 20	@ check OS number | 
|  | adr	tbl, sys_call_table		@ load syscall table pointer | 
|  | tst	ip, #_TIF_SYSCALL_TRACE		@ are we tracing syscalls? | 
|  | bne	__sys_trace | 
|  |  | 
|  | adral	lr, ret_fast_syscall            @ set return address | 
|  | orral	lr, lr, #PSR_I_BIT | MODE_SVC26 @ Force SVC mode on return | 
|  | cmp	scno, #NR_syscalls		@ check upper syscall limit | 
|  | ldrcc	pc, [tbl, scno, lsl #2]		@ call sys_* routine | 
|  |  | 
|  | add	r1, sp, #S_OFF | 
|  | 2:	mov	why, #0				@ no longer a real syscall | 
|  | cmp	scno, #ARMSWI_OFFSET | 
|  | eor	r0, scno, #OS_NUMBER << 20	@ put OS number back | 
|  | bcs	arm_syscall | 
|  | b	sys_ni_syscall			@ not private func | 
|  |  | 
|  | /* | 
|  | * This is the really slow path.  We're going to be doing | 
|  | * context switches, and waiting for our parent to respond. | 
|  | */ | 
|  | __sys_trace: | 
|  | add	r1, sp, #S_OFF | 
|  | mov	r0, #0				@ trace entry [IP = 0] | 
|  | bl	syscall_trace | 
|  |  | 
|  | adral   lr, __sys_trace_return          @ set return address | 
|  | orral   lr, lr, #PSR_I_BIT | MODE_SVC26 @ Force SVC mode on return | 
|  | add	r1, sp, #S_R0 + S_OFF		@ pointer to regs | 
|  | cmp	scno, #NR_syscalls		@ check upper syscall limit | 
|  | ldmccia	r1, {r0 - r3}			@ have to reload r0 - r3 | 
|  | ldrcc	pc, [tbl, scno, lsl #2]		@ call sys_* routine | 
|  | b	2b | 
|  |  | 
|  | __sys_trace_return: | 
|  | str	r0, [sp, #S_R0 + S_OFF]!	@ save returned r0 | 
|  | mov	r1, sp | 
|  | mov	r0, #1				@ trace exit [IP = 1] | 
|  | bl	syscall_trace | 
|  | b	ret_slow_syscall | 
|  |  | 
|  | .align	5 | 
|  | #ifdef CONFIG_ALIGNMENT_TRAP | 
|  | .type	__cr_alignment, #object | 
|  | __cr_alignment: | 
|  | .word	cr_alignment | 
|  | #endif | 
|  |  | 
|  | .type	sys_call_table, #object | 
|  | ENTRY(sys_call_table) | 
|  | #include "calls.S" | 
|  |  | 
|  | /*============================================================================ | 
|  | * Special system call wrappers | 
|  | */ | 
|  | @ r0 = syscall number | 
|  | @ r5 = syscall table | 
|  | .type	sys_syscall, #function | 
|  | sys_syscall: | 
|  | eor	scno, r0, #OS_NUMBER << 20 | 
|  | cmp	scno, #NR_syscalls	@ check range | 
|  | stmleia	sp, {r5, r6}		@ shuffle args | 
|  | movle	r0, r1 | 
|  | movle	r1, r2 | 
|  | movle	r2, r3 | 
|  | movle	r3, r4 | 
|  | ldrle	pc, [tbl, scno, lsl #2] | 
|  | b	sys_ni_syscall | 
|  |  | 
|  | sys_fork_wrapper: | 
|  | add	r0, sp, #S_OFF | 
|  | b	sys_fork | 
|  |  | 
|  | sys_vfork_wrapper: | 
|  | add	r0, sp, #S_OFF | 
|  | b	sys_vfork | 
|  |  | 
|  | sys_execve_wrapper: | 
|  | add	r3, sp, #S_OFF | 
|  | b	sys_execve | 
|  |  | 
|  | sys_clone_wapper: | 
|  | add	r2, sp, #S_OFF | 
|  | b	sys_clone | 
|  |  | 
|  | sys_sigsuspend_wrapper: | 
|  | add	r3, sp, #S_OFF | 
|  | b	sys_sigsuspend | 
|  |  | 
|  | sys_rt_sigsuspend_wrapper: | 
|  | add	r2, sp, #S_OFF | 
|  | b	sys_rt_sigsuspend | 
|  |  | 
|  | sys_sigreturn_wrapper: | 
|  | add	r0, sp, #S_OFF | 
|  | b	sys_sigreturn | 
|  |  | 
|  | sys_rt_sigreturn_wrapper: | 
|  | add	r0, sp, #S_OFF | 
|  | b	sys_rt_sigreturn | 
|  |  | 
|  | sys_sigaltstack_wrapper: | 
|  | ldr	r2, [sp, #S_OFF + S_SP] | 
|  | b	do_sigaltstack | 
|  |  | 
|  | /* | 
|  | * Note: off_4k (r5) is always units of 4K.  If we can't do the requested | 
|  | * offset, we return EINVAL.  FIXME - this lost some stuff from arm32 to | 
|  | * ifdefs. check it out. | 
|  | */ | 
|  | sys_mmap2: | 
|  | tst	r5, #((1 << (PAGE_SHIFT - 12)) - 1) | 
|  | moveq	r5, r5, lsr #PAGE_SHIFT - 12 | 
|  | streq	r5, [sp, #4] | 
|  | beq	do_mmap2 | 
|  | mov	r0, #-EINVAL | 
|  | RETINSTR(mov,pc, lr) | 
|  |  | 
|  | /* | 
|  | *  Design issues: | 
|  | *   - We have several modes that each vector can be called from, | 
|  | *     each with its own set of registers.  On entry to any vector, | 
|  | *     we *must* save the registers used in *that* mode. | 
|  | * | 
|  | *   - This code must be as fast as possible. | 
|  | * | 
|  | *  There are a few restrictions on the vectors: | 
|  | *   - the SWI vector cannot be called from *any* non-user mode | 
|  | * | 
|  | *   - the FP emulator is *never* called from *any* non-user mode undefined | 
|  | *     instruction. | 
|  | * | 
|  | */ | 
|  |  | 
|  | .text | 
|  |  | 
|  | .macro handle_irq | 
|  | 1:		mov     r4, #IOC_BASE | 
|  | ldrb    r6, [r4, #0x24]            @ get high priority first | 
|  | adr     r5, irq_prio_h | 
|  | teq     r6, #0 | 
|  | ldreqb  r6, [r4, #0x14]            @ get low priority | 
|  | adreq   r5, irq_prio_l | 
|  |  | 
|  | teq     r6, #0                     @ If an IRQ happened... | 
|  | ldrneb  r0, [r5, r6]               @ get IRQ number | 
|  | movne   r1, sp                     @ get struct pt_regs | 
|  | adrne   lr, 1b                     @ Set return address to 1b | 
|  | orrne   lr, lr, #PSR_I_BIT | MODE_SVC26  @ (and force SVC mode) | 
|  | bne     asm_do_IRQ                 @ process IRQ (if asserted) | 
|  | .endm | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Interrupt table (incorporates priority) | 
|  | */ | 
|  | .macro	irq_prio_table | 
|  | irq_prio_l:	.byte	 0, 0, 1, 0, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 | 
|  | .byte	 4, 0, 1, 0, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 | 
|  | .byte	 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 | 
|  | .byte	 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 | 
|  | .byte	 6, 6, 6, 6, 6, 6, 6, 6, 3, 3, 3, 3, 3, 3, 3, 3 | 
|  | .byte	 6, 6, 6, 6, 6, 6, 6, 6, 3, 3, 3, 3, 3, 3, 3, 3 | 
|  | .byte	 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 | 
|  | .byte	 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 | 
|  | .byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 | 
|  | .byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 | 
|  | .byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 | 
|  | .byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 | 
|  | .byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 | 
|  | .byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 | 
|  | .byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 | 
|  | .byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 | 
|  | irq_prio_h:	.byte	 0, 8, 9, 8,10,10,10,10,11,11,11,11,10,10,10,10 | 
|  | .byte	12, 8, 9, 8,10,10,10,10,11,11,11,11,10,10,10,10 | 
|  | .byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10 | 
|  | .byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10 | 
|  | .byte	14,14,14,14,10,10,10,10,11,11,11,11,10,10,10,10 | 
|  | .byte	14,14,14,14,10,10,10,10,11,11,11,11,10,10,10,10 | 
|  | .byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10 | 
|  | .byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10 | 
|  | .byte	15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10 | 
|  | .byte	15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10 | 
|  | .byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10 | 
|  | .byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10 | 
|  | .byte	15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10 | 
|  | .byte	15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10 | 
|  | .byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10 | 
|  | .byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10 | 
|  | .endm | 
|  |  | 
|  | #if 1 | 
|  | /* | 
|  | * Uncomment these if you wish to get more debugging into about data aborts. | 
|  | * FIXME - I bet we can find a way to encode these and keep performance. | 
|  | */ | 
|  | #define FAULT_CODE_LDRSTRPOST	0x80 | 
|  | #define FAULT_CODE_LDRSTRPRE	0x40 | 
|  | #define FAULT_CODE_LDRSTRREG	0x20 | 
|  | #define FAULT_CODE_LDMSTM	0x10 | 
|  | #define FAULT_CODE_LDCSTC	0x08 | 
|  | #endif | 
|  | #define FAULT_CODE_PREFETCH	0x04 | 
|  | #define FAULT_CODE_WRITE	0x02 | 
|  | #define FAULT_CODE_FORCECOW	0x01 | 
|  |  | 
|  | /*============================================================================= | 
|  | * Undefined FIQs | 
|  | *----------------------------------------------------------------------------- | 
|  | */ | 
|  | _unexp_fiq:	ldr     sp, .LCfiq | 
|  | mov	r12, #IOC_BASE | 
|  | strb	r12, [r12, #0x38]	@ Disable FIQ register | 
|  | teqp	pc, #PSR_I_BIT | PSR_F_BIT | MODE_SVC26 | 
|  | mov	r0, r0 | 
|  | stmfd	sp!, {r0 - r3, ip, lr} | 
|  | adr	r0, Lfiqmsg | 
|  | bl	printk | 
|  | ldmfd	sp!, {r0 - r3, ip, lr} | 
|  | teqp	pc, #PSR_I_BIT | PSR_F_BIT | MODE_FIQ26 | 
|  | mov	r0, r0 | 
|  | movs	pc, lr | 
|  |  | 
|  | Lfiqmsg:	.ascii	"*** Unexpected FIQ\n\0" | 
|  | .align | 
|  |  | 
|  | .LCfiq:		.word	__temp_fiq | 
|  | .LCirq:		.word	__temp_irq | 
|  |  | 
|  | /*============================================================================= | 
|  | * Undefined instruction handler | 
|  | *----------------------------------------------------------------------------- | 
|  | * Handles floating point instructions | 
|  | */ | 
|  | vector_undefinstr: | 
|  | tst	lr, #MODE_SVC26          @ did we come from a non-user mode? | 
|  | bne	__und_svc                @ yes - deal with it. | 
|  | /* Otherwise, fall through for the user-space (common) case. */ | 
|  | save_user_regs | 
|  | zero_fp                                 @ zero frame pointer | 
|  | teqp	pc, #PSR_I_BIT | MODE_SVC26     @ disable IRQs | 
|  | .Lbug_undef: | 
|  | ldr	r4, .LC2 | 
|  | ldr     pc, [r4]         @ Call FP module entry point | 
|  | /* FIXME - should we trap for a null pointer here? */ | 
|  |  | 
|  | /* The SVC mode case */ | 
|  | __und_svc:	save_svc_regs                           @ Non-user mode | 
|  | mask_pc r0, lr | 
|  | and     r2, lr, #3 | 
|  | sub     r0, r0, #4 | 
|  | mov     r1, sp | 
|  | bl      do_undefinstr | 
|  | restore_svc_regs | 
|  |  | 
|  | /* We get here if the FP emulator doesnt handle the undef instr. | 
|  | * If the insn WAS handled, the emulator jumps to ret_from_exception by itself/ | 
|  | */ | 
|  | .globl	fpundefinstr | 
|  | fpundefinstr: | 
|  | mov	r0, lr | 
|  | mov	r1, sp | 
|  | teqp	pc, #MODE_SVC26 | 
|  | bl	do_undefinstr | 
|  | b	ret_from_exception		@ Normal FP exit | 
|  |  | 
|  | #if defined CONFIG_FPE_NWFPE || defined CONFIG_FPE_FASTFPE | 
|  | /* The FPE is always present */ | 
|  | .equ	fpe_not_present, 0 | 
|  | #else | 
|  | /* We get here if an undefined instruction happens and the floating | 
|  | * point emulator is not present.  If the offending instruction was | 
|  | * a WFS, we just perform a normal return as if we had emulated the | 
|  | * operation.  This is a hack to allow some basic userland binaries | 
|  | * to run so that the emulator module proper can be loaded. --philb | 
|  | * FIXME - probably a broken useless hack... | 
|  | */ | 
|  | fpe_not_present: | 
|  | adr	r10, wfs_mask_data | 
|  | ldmia	r10, {r4, r5, r6, r7, r8} | 
|  | ldr	r10, [sp, #S_PC]		@ Load PC | 
|  | sub	r10, r10, #4 | 
|  | mask_pc	r10, r10 | 
|  | ldrt	r10, [r10]			@ get instruction | 
|  | and	r5, r10, r5 | 
|  | teq	r5, r4				@ Is it WFS? | 
|  | beq	ret_from_exception | 
|  | and	r5, r10, r8 | 
|  | teq	r5, r6				@ Is it LDF/STF on sp or fp? | 
|  | teqne	r5, r7 | 
|  | bne	fpundefinstr | 
|  | tst	r10, #0x00200000		@ Does it have WB | 
|  | beq	ret_from_exception | 
|  | and	r4, r10, #255			@ get offset | 
|  | and	r6, r10, #0x000f0000 | 
|  | tst	r10, #0x00800000		@ +/- | 
|  | ldr	r5, [sp, r6, lsr #14]		@ Load reg | 
|  | rsbeq	r4, r4, #0 | 
|  | add	r5, r5, r4, lsl #2 | 
|  | str	r5, [sp, r6, lsr #14]		@ Save reg | 
|  | b	ret_from_exception | 
|  |  | 
|  | wfs_mask_data:	.word	0x0e200110			@ WFS/RFS | 
|  | .word	0x0fef0fff | 
|  | .word	0x0d0d0100			@ LDF [sp]/STF [sp] | 
|  | .word	0x0d0b0100			@ LDF [fp]/STF [fp] | 
|  | .word	0x0f0f0f00 | 
|  | #endif | 
|  |  | 
|  | .LC2:		.word	fp_enter | 
|  |  | 
|  | /*============================================================================= | 
|  | * Prefetch abort handler | 
|  | *----------------------------------------------------------------------------- | 
|  | */ | 
|  | #define DEBUG_UNDEF | 
|  | /* remember: lr = USR pc */ | 
|  | vector_prefetch: | 
|  | sub	lr, lr, #4 | 
|  | tst	lr, #MODE_SVC26 | 
|  | bne	__pabt_invalid | 
|  | save_user_regs | 
|  | teqp	pc, #MODE_SVC26         @ Enable IRQs... | 
|  | mask_pc	r0, lr			@ Address of abort | 
|  | mov	r1, sp			@ Tasks registers | 
|  | bl	do_PrefetchAbort | 
|  | teq	r0, #0			@ If non-zero, we believe this abort.. | 
|  | bne	ret_from_exception | 
|  | #ifdef DEBUG_UNDEF | 
|  | adr	r0, t | 
|  | bl	printk | 
|  | #endif | 
|  | ldr	lr, [sp,#S_PC]		@ FIXME program to test this on.  I think its | 
|  | b	.Lbug_undef		@ broken at the moment though!) | 
|  |  | 
|  | __pabt_invalid:	save_svc_regs | 
|  | mov	r0, sp			@ Prefetch aborts are definitely *not* | 
|  | mov	r1, #BAD_PREFETCH	@ allowed in non-user modes.  We cant | 
|  | and	r2, lr, #3		@ recover from this problem. | 
|  | b	bad_mode | 
|  |  | 
|  | #ifdef DEBUG_UNDEF | 
|  | t:		.ascii "*** undef ***\r\n\0" | 
|  | .align | 
|  | #endif | 
|  |  | 
|  | /*============================================================================= | 
|  | * Address exception handler | 
|  | *----------------------------------------------------------------------------- | 
|  | * These aren't too critical. | 
|  | * (they're not supposed to happen). | 
|  | * In order to debug the reason for address exceptions in non-user modes, | 
|  | * we have to obtain all the registers so that we can see what's going on. | 
|  | */ | 
|  |  | 
|  | vector_addrexcptn: | 
|  | sub	lr, lr, #8 | 
|  | tst	lr, #3 | 
|  | bne	Laddrexcptn_not_user | 
|  | save_user_regs | 
|  | teq	pc, #MODE_SVC26 | 
|  | mask_pc	r0, lr			@ Point to instruction | 
|  | mov	r1, sp			@ Point to registers | 
|  | mov	r2, #0x400 | 
|  | mov	lr, pc | 
|  | bl	do_excpt | 
|  | b	ret_from_exception | 
|  |  | 
|  | Laddrexcptn_not_user: | 
|  | save_svc_regs | 
|  | and	r2, lr, #3 | 
|  | teq	r2, #3 | 
|  | bne	Laddrexcptn_illegal_mode | 
|  | teqp	pc, #MODE_SVC26 | 
|  | mask_pc	r0, lr | 
|  | mov	r1, sp | 
|  | orr	r2, r2, #0x400 | 
|  | bl	do_excpt | 
|  | ldmia	sp, {r0 - lr}		@ I cant remember the reason I changed this... | 
|  | add	sp, sp, #15*4 | 
|  | movs	pc, lr | 
|  |  | 
|  | Laddrexcptn_illegal_mode: | 
|  | mov	r0, sp | 
|  | str	lr, [sp, #-4]! | 
|  | orr	r1, r2, #PSR_I_BIT | PSR_F_BIT | 
|  | teqp	r1, #0			@ change into mode (wont be user mode) | 
|  | mov	r0, r0 | 
|  | mov	r1, r8			@ Any register from r8 - r14 can be banked | 
|  | mov	r2, r9 | 
|  | mov	r3, r10 | 
|  | mov	r4, r11 | 
|  | mov	r5, r12 | 
|  | mov	r6, r13 | 
|  | mov	r7, r14 | 
|  | teqp	pc, #PSR_F_BIT | MODE_SVC26 @ back to svc | 
|  | mov	r0, r0 | 
|  | stmfd	sp!, {r1-r7} | 
|  | ldmia	r0, {r0-r7} | 
|  | stmfd	sp!, {r0-r7} | 
|  | mov	r0, sp | 
|  | mov	r1, #BAD_ADDREXCPTN | 
|  | b	bad_mode | 
|  |  | 
|  | /*============================================================================= | 
|  | * Interrupt (IRQ) handler | 
|  | *----------------------------------------------------------------------------- | 
|  | * Note: if the IRQ was taken whilst in user mode, then *no* kernel routine | 
|  | * is running, so do not have to save svc lr. | 
|  | * | 
|  | * Entered in IRQ mode. | 
|  | */ | 
|  |  | 
|  | vector_IRQ:	ldr     sp, .LCirq         @ Setup some temporary stack | 
|  | sub     lr, lr, #4 | 
|  | str     lr, [sp]           @ push return address | 
|  |  | 
|  | tst     lr, #3 | 
|  | bne	__irq_non_usr | 
|  |  | 
|  | __irq_usr:	teqp	pc, #PSR_I_BIT | MODE_SVC26     @ Enter SVC mode | 
|  | mov	r0, r0 | 
|  |  | 
|  | ldr	lr, .LCirq | 
|  | ldr	lr, [lr]           @ Restore lr for jump back to USR | 
|  |  | 
|  | save_user_regs | 
|  |  | 
|  | handle_irq | 
|  |  | 
|  | mov	why, #0 | 
|  | get_thread_info tsk | 
|  | b	ret_to_user | 
|  |  | 
|  | @ Place the IRQ priority table here so that the handle_irq macros above | 
|  | @ and below here can access it. | 
|  |  | 
|  | irq_prio_table | 
|  |  | 
|  | __irq_non_usr:	teqp	pc, #PSR_I_BIT | MODE_SVC26     @ Enter SVC mode | 
|  | mov	r0, r0 | 
|  |  | 
|  | save_svc_regs_irq | 
|  |  | 
|  | and	r2, lr, #3 | 
|  | teq	r2, #3 | 
|  | bne	__irq_invalid                @ IRQ not from SVC mode | 
|  |  | 
|  | handle_irq | 
|  |  | 
|  | restore_svc_regs | 
|  |  | 
|  | __irq_invalid:	mov	r0, sp | 
|  | mov	r1, #BAD_IRQ | 
|  | b	bad_mode | 
|  |  | 
|  | /*============================================================================= | 
|  | * Data abort handler code | 
|  | *----------------------------------------------------------------------------- | 
|  | * | 
|  | * This handles both exceptions from user and SVC modes, computes the address | 
|  | *  range of the problem, and does any correction that is required.  It then | 
|  | *  calls the kernel data abort routine. | 
|  | * | 
|  | * This is where I wish that the ARM would tell you which address aborted. | 
|  | */ | 
|  |  | 
|  | vector_data:	sub	lr, lr, #8		@ Correct lr | 
|  | tst	lr, #3 | 
|  | bne	Ldata_not_user | 
|  | save_user_regs | 
|  | teqp	pc, #MODE_SVC26 | 
|  | mask_pc	r0, lr | 
|  | bl	Ldata_do | 
|  | b	ret_from_exception | 
|  |  | 
|  | Ldata_not_user: | 
|  | save_svc_regs | 
|  | and	r2, lr, #3 | 
|  | teq	r2, #3 | 
|  | bne	Ldata_illegal_mode | 
|  | tst	lr, #PSR_I_BIT | 
|  | teqeqp	pc, #MODE_SVC26 | 
|  | mask_pc	r0, lr | 
|  | bl	Ldata_do | 
|  | restore_svc_regs | 
|  |  | 
|  | Ldata_illegal_mode: | 
|  | mov	r0, sp | 
|  | mov	r1, #BAD_DATA | 
|  | b	bad_mode | 
|  |  | 
|  | Ldata_do:	mov	r3, sp | 
|  | ldr	r4, [r0]		@ Get instruction | 
|  | mov	r2, #0 | 
|  | tst	r4, #1 << 20		@ Check to see if it is a write instruction | 
|  | orreq	r2, r2, #FAULT_CODE_WRITE @ Indicate write instruction | 
|  | mov	r1, r4, lsr #22		@ Now branch to the relevent processing routine | 
|  | and	r1, r1, #15 << 2 | 
|  | add	pc, pc, r1 | 
|  | movs	pc, lr | 
|  | b	Ldata_unknown | 
|  | b	Ldata_unknown | 
|  | b	Ldata_unknown | 
|  | b	Ldata_unknown | 
|  | b	Ldata_ldrstr_post	@ ldr	rd, [rn], #m | 
|  | b	Ldata_ldrstr_numindex	@ ldr	rd, [rn, #m]	@ RegVal | 
|  | b	Ldata_ldrstr_post	@ ldr	rd, [rn], rm | 
|  | b	Ldata_ldrstr_regindex	@ ldr	rd, [rn, rm] | 
|  | b	Ldata_ldmstm		@ ldm*a	rn, <rlist> | 
|  | b	Ldata_ldmstm		@ ldm*b	rn, <rlist> | 
|  | b	Ldata_unknown | 
|  | b	Ldata_unknown | 
|  | b	Ldata_ldrstr_post	@ ldc	rd, [rn], #m	@ Same as ldr	rd, [rn], #m | 
|  | b	Ldata_ldcstc_pre	@ ldc	rd, [rn, #m] | 
|  | b	Ldata_unknown | 
|  | Ldata_unknown:	@ Part of jumptable | 
|  | mov	r0, r1 | 
|  | mov	r1, r4 | 
|  | mov	r2, r3 | 
|  | b	baddataabort | 
|  |  | 
|  | Ldata_ldrstr_post: | 
|  | mov	r0, r4, lsr #14		@ Get Rn | 
|  | and	r0, r0, #15 << 2	@ Mask out reg. | 
|  | teq	r0, #15 << 2 | 
|  | ldr	r0, [r3, r0]		@ Get register | 
|  | biceq	r0, r0, #PCMASK | 
|  | mov	r1, r0 | 
|  | #ifdef FAULT_CODE_LDRSTRPOST | 
|  | orr	r2, r2, #FAULT_CODE_LDRSTRPOST | 
|  | #endif | 
|  | b	do_DataAbort | 
|  |  | 
|  | Ldata_ldrstr_numindex: | 
|  | mov	r0, r4, lsr #14		@ Get Rn | 
|  | and	r0, r0, #15 << 2	@ Mask out reg. | 
|  | teq	r0, #15 << 2 | 
|  | ldr	r0, [r3, r0]		@ Get register | 
|  | mov	r1, r4, lsl #20 | 
|  | biceq	r0, r0, #PCMASK | 
|  | tst	r4, #1 << 23 | 
|  | addne	r0, r0, r1, lsr #20 | 
|  | subeq	r0, r0, r1, lsr #20 | 
|  | mov	r1, r0 | 
|  | #ifdef FAULT_CODE_LDRSTRPRE | 
|  | orr	r2, r2, #FAULT_CODE_LDRSTRPRE | 
|  | #endif | 
|  | b	do_DataAbort | 
|  |  | 
|  | Ldata_ldrstr_regindex: | 
|  | mov	r0, r4, lsr #14		@ Get Rn | 
|  | and	r0, r0, #15 << 2	@ Mask out reg. | 
|  | teq	r0, #15 << 2 | 
|  | ldr	r0, [r3, r0]		@ Get register | 
|  | and	r7, r4, #15 | 
|  | biceq	r0, r0, #PCMASK | 
|  | teq	r7, #15			@ Check for PC | 
|  | ldr	r7, [r3, r7, lsl #2]	@ Get Rm | 
|  | and	r8, r4, #0x60		@ Get shift types | 
|  | biceq	r7, r7, #PCMASK | 
|  | mov	r9, r4, lsr #7		@ Get shift amount | 
|  | and	r9, r9, #31 | 
|  | teq	r8, #0 | 
|  | moveq	r7, r7, lsl r9 | 
|  | teq	r8, #0x20		@ LSR shift | 
|  | moveq	r7, r7, lsr r9 | 
|  | teq	r8, #0x40		@ ASR shift | 
|  | moveq	r7, r7, asr r9 | 
|  | teq	r8, #0x60		@ ROR shift | 
|  | moveq	r7, r7, ror r9 | 
|  | tst	r4, #1 << 23 | 
|  | addne	r0, r0, r7 | 
|  | subeq	r0, r0, r7		@ Apply correction | 
|  | mov	r1, r0 | 
|  | #ifdef FAULT_CODE_LDRSTRREG | 
|  | orr	r2, r2, #FAULT_CODE_LDRSTRREG | 
|  | #endif | 
|  | b	do_DataAbort | 
|  |  | 
|  | Ldata_ldmstm: | 
|  | mov	r7, #0x11 | 
|  | orr	r7, r7, r7, lsl #8 | 
|  | and	r0, r4, r7 | 
|  | and	r1, r4, r7, lsl #1 | 
|  | add	r0, r0, r1, lsr #1 | 
|  | and	r1, r4, r7, lsl #2 | 
|  | add	r0, r0, r1, lsr #2 | 
|  | and	r1, r4, r7, lsl #3 | 
|  | add	r0, r0, r1, lsr #3 | 
|  | add	r0, r0, r0, lsr #8 | 
|  | add	r0, r0, r0, lsr #4 | 
|  | and	r7, r0, #15		@ r7 = no. of registers to transfer. | 
|  | mov	r5, r4, lsr #14		@ Get Rn | 
|  | and	r5, r5, #15 << 2 | 
|  | ldr	r0, [r3, r5]		@ Get reg | 
|  | eor	r6, r4, r4, lsl #2 | 
|  | tst	r6, #1 << 23		@ Check inc/dec ^ writeback | 
|  | rsbeq	r7, r7, #0 | 
|  | add	r7, r0, r7, lsl #2	@ Do correction (signed) | 
|  | subne	r1, r7, #1 | 
|  | subeq	r1, r0, #1 | 
|  | moveq	r0, r7 | 
|  | tst	r4, #1 << 21		@ Check writeback | 
|  | strne	r7, [r3, r5] | 
|  | eor	r6, r4, r4, lsl #1 | 
|  | tst	r6, #1 << 24		@ Check Pre/Post ^ inc/dec | 
|  | addeq	r0, r0, #4 | 
|  | addeq	r1, r1, #4 | 
|  | teq	r5, #15*4		@ CHECK FOR PC | 
|  | biceq	r1, r1, #PCMASK | 
|  | biceq	r0, r0, #PCMASK | 
|  | #ifdef FAULT_CODE_LDMSTM | 
|  | orr	r2, r2, #FAULT_CODE_LDMSTM | 
|  | #endif | 
|  | b	do_DataAbort | 
|  |  | 
|  | Ldata_ldcstc_pre: | 
|  | mov	r0, r4, lsr #14		@ Get Rn | 
|  | and	r0, r0, #15 << 2	@ Mask out reg. | 
|  | teq	r0, #15 << 2 | 
|  | ldr	r0, [r3, r0]		@ Get register | 
|  | mov	r1, r4, lsl #24		@ Get offset | 
|  | biceq	r0, r0, #PCMASK | 
|  | tst	r4, #1 << 23 | 
|  | addne	r0, r0, r1, lsr #24 | 
|  | subeq	r0, r0, r1, lsr #24 | 
|  | mov	r1, r0 | 
|  | #ifdef FAULT_CODE_LDCSTC | 
|  | orr	r2, r2, #FAULT_CODE_LDCSTC | 
|  | #endif | 
|  | b	do_DataAbort | 
|  |  | 
|  |  | 
|  | /* | 
|  | * This is the return code to user mode for abort handlers | 
|  | */ | 
|  | ENTRY(ret_from_exception) | 
|  | get_thread_info tsk | 
|  | mov	why, #0 | 
|  | b	ret_to_user | 
|  |  | 
|  | .data | 
|  | ENTRY(fp_enter) | 
|  | .word	fpe_not_present | 
|  | .text | 
|  | /* | 
|  | * Register switch for older 26-bit only ARMs | 
|  | */ | 
|  | ENTRY(__switch_to) | 
|  | add	r0, r0, #TI_CPU_SAVE | 
|  | stmia	r0, {r4 - sl, fp, sp, lr} | 
|  | add	r1, r1, #TI_CPU_SAVE | 
|  | ldmia	r1, {r4 - sl, fp, sp, pc}^ | 
|  |  | 
|  | /* | 
|  | *============================================================================= | 
|  | *		Low-level interface code | 
|  | *----------------------------------------------------------------------------- | 
|  | *		Trap initialisation | 
|  | *----------------------------------------------------------------------------- | 
|  | * | 
|  | * Note - FIQ code has changed.  The default is a couple of words in 0x1c, 0x20 | 
|  | * that call _unexp_fiq.  Nowever, we now copy the FIQ routine to 0x1c (removes | 
|  | * some excess cycles). | 
|  | * | 
|  | * What we need to put into 0-0x1c are branches to branch to the kernel. | 
|  | */ | 
|  |  | 
|  | .section ".init.text",#alloc,#execinstr | 
|  |  | 
|  | .Ljump_addresses: | 
|  | swi	SYS_ERROR0 | 
|  | .word	vector_undefinstr	- 12 | 
|  | .word	vector_swi		- 16 | 
|  | .word	vector_prefetch		- 20 | 
|  | .word	vector_data		- 24 | 
|  | .word	vector_addrexcptn	- 28 | 
|  | .word	vector_IRQ		- 32 | 
|  | .word	_unexp_fiq		- 36 | 
|  | b	. + 8 | 
|  | /* | 
|  | * initialise the trap system | 
|  | */ | 
|  | ENTRY(__trap_init) | 
|  | stmfd	sp!, {r4 - r7, lr} | 
|  | adr	r1, .Ljump_addresses | 
|  | ldmia	r1, {r1 - r7, ip, lr} | 
|  | orr	r2, lr, r2, lsr #2 | 
|  | orr	r3, lr, r3, lsr #2 | 
|  | orr	r4, lr, r4, lsr #2 | 
|  | orr	r5, lr, r5, lsr #2 | 
|  | orr	r6, lr, r6, lsr #2 | 
|  | orr	r7, lr, r7, lsr #2 | 
|  | orr	ip, lr, ip, lsr #2 | 
|  | mov	r0, #0 | 
|  | stmia	r0, {r1 - r7, ip} | 
|  | ldmfd	sp!, {r4 - r7, pc}^ | 
|  |  | 
|  | .bss | 
|  | __temp_irq:	.space	4				@ saved lr_irq | 
|  | __temp_fiq:	.space	128 |