| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /* 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> | 
					
						
							| 
									
										
										
										
											2005-09-09 20:35:55 +02:00
										 |  |  | #include <asm/asm-offsets.h> | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #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 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	.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 |