/*
 * The vectors page. Includes all exception handlers.
 *
 * Copyright (C) 2007 Bahadir Balban
 */

#include INC_ARCH(asm.h)

.balign 4096
.section .data.vectors
__vector_vaddr:

BEGIN_PROC(arm_high_vector)
	b	arm_reset_exception
	b	arm_undef_exception
	b	arm_swi_exception
	b	arm_prefetch_abort_exception_reentrant
	b	arm_data_abort_exception_reentrant
	nop
	b	arm_irq_exception_reentrant_with_schedule
	b	arm_fiq_exception
END_PROC(arm_high_vector)

.balign 4

/*
 * vect_reset
 *
 * Upon Entry:
 * - All registers are undefined and insignificant,
 * - FIQ/IRQs are disabled.
 * - PC:	0x00000000
 *
 *
 * PURPOSE:
 * CPU always starts executing from this vector
 * upon a HW reset. It may also be used as a SW reset.
 */
BEGIN_PROC(arm_reset_exception)
END_PROC(arm_reset_exception)

/*
 * vect_undef
 *
 * Upon Entry:
 * - R14:	Address of next instruction after undefined instruction
 * - PC:	0x00000004
 * - IRQs are disabled (CPSR[7] = 1)
 *
 *
 * PURPOSE:
 * A co-processor instruction not supported by the core can be
 * emulated here. Also unrecognised/invalid instructions are handled.
 */
BEGIN_PROC(arm_undef_exception)
	sub	lr, lr, #4
	mov	r0, lr		@ Get undefined abort address
	mrs	r1, spsr	@ Get previous abort state
	mov	r5, lr		@ Save it in r5 in case r0 is trashed
	mov	lr, pc		@ Save return address
	ldr	pc, =dump_undef_abort
1:
	b	1b
END_PROC(arm_undef_exception)

	.macro disable_irqs rx
		mrs	\rx, cpsr_fc
		orr	\rx, #ARM_IRQ_BIT
		msr	cpsr_fc, \rx
	.endm
	.macro enable_irqs rx
		mrs	\rx, cpsr_fc
		bic	\rx, #ARM_IRQ_BIT
		msr	cpsr_fc, \rx
	.endm
	/* Only works in SVC MODE. Know what you are doing! */
	.macro get_current rx
		bic	\rx, sp, #0xFF0
		bic	\rx, \rx, #0xF
	.endm
	/* Saves the address of system call argument registers pushed to stack
	 * to the current task's ktcb. */
	.macro	ktcb_ref_saved_regs regs_addr, ktcb, regs_off
		get_current \ktcb
		ldr	\regs_off, =syscall_regs_offset
		ldr	\regs_off, [\regs_off]
		str	\regs_addr, [\ktcb, \regs_off]
	.endm
/*
 * vect_swi
 *
 * Upon Entry:
 * - R14:	Address of next instruction after the SWI
 * - PC:	0x00000008
 * - R0-R12:	Depending on the system call some of them contain
 *		indicators of what the exception means.
 * - IRQs are disabled (CPSR[7] = 1)
 * - SWI instruction's bits [7:0] may contain SWI indicator
 *
 * PURPOSE:
 * Used for trapping into a debugger or OS kernel via system calls.
 * Argument registers from R0 up to R12 and [7:0] of the causing SWI
 * instruction contains hints of what to do with this exception. What
 * R0-R12 contains depends on what userspace has put in them. Note this
 * is the only exception that userspace can generate and thus has control
 * on what it put into r0-rx.
 *
 * RECAP:
 * Normally across a function call, only r0-r3 are used for passing parameters.
 * Why r0-r3 only but not r4, r5...? See APCS (ARM procedure call standard)
 * Short answer: r4-r12 must be preserved across procedures but r0-r3 can be
 * trashed because they're set aside for argument passing. Arguments more than 4
 * go on the stack. Note APCS is a *suggestion*, rather than enforcement. So if
 * a userspace stub library is created that say, preserves and uses r0-r9 for a
 * system call, and the system call handler (this) knows about it, it is a
 * perfectly valid setup. In fact this is what we do here, we don't strictly use
 * r0-r3. Depending on the system call, the set of input registers (and output
 * registers to return results from the system call) may be redefined. These are
 * documented for each system call in the reference manual.
 * Another caveat to note in SWI usage is that we use the address offset of the
 * SWI instruction to see which offset it has in the system call vector, to
 * determine the correct system call, rather than [7:0] bits of the SWI.
 */
BEGIN_PROC(arm_swi_exception)
	sub	lr, lr, #4	@ Get address of swi instruction user executed.
	stmfd	sp, {r0-r12,sp,lr}^ @ Push arguments, LR_USR and SP_USR to stack.
	nop

	@ Future optimisation 1:
	@ For all syscalls we need not push any more than r8 but we push up to
	@ r12 because upon a fork, a child's easiest way to restore user
	@ registers is to pop it from stack during return_from_syscall. In future
	@ fork function could return back to here, save all context into child
	@ from actual registers instead of reading from stack, and then return.

	@ Future optimisation 2:
	@ SP_USR MUST be pushed here, otherwise a kernel preemption could
	@ cause user mode of another process to overwrite SP_USR. The reason we
	@ save it here is because the preemption path does not currently save it
	@ if it is a kernel preemption. User SP can also be used here, as the
	@ user might have pushed data to its stack to be used by system calls.
	@ But we dont plan to pass data to kernel in this way, so saving of
	@ SP_USR can be done in preemption path as an optimisation.

	/*
	 * The LR_usr is important here, because the user application uses a BL
	 * to jump to the system call SWI, so the LR_usr contains the return
	 * address, i.e. the next instruction after the *jumping* instruction to
	 * the system call SWI (not the one after the swi itself, which is in
	 * LR_svc).
	 */

	sub	sp, sp, #60	@ stmfd on user registers can't writeback the SP. We do it manually.
	mrs	r0, spsr_fc	@ psr also need saving in case this context is interrupted.
	stmfd	sp!, {r0}
	enable_irqs r0
	mov	r0, sp		@ Current SP has pointer to all saved context.
	ktcb_ref_saved_regs r0, r1, r2 @ Save syscall context pointer in ktcb
	mov	r1, lr		@ Pass swi instruction address in LR as arg1
	mov 	lr, pc
	ldr 	pc, =syscall

.global return_from_syscall;    @ Newly created threads use this path to return,
return_from_syscall:		@ if they duplicated another thread's address space.
	disable_irqs r1		@ Not disabling irqs at this point causes the SP_USR and spsr
				@ to get corrupt causing havoc.
	ldmfd	sp!, {r1}
	msr	spsr, r1
	add	sp, sp, #4	@ Skip, r0's location, since r0 already has returned result.
				@ Note we're obliged to preserve at least r3-r8 because they're MRs.
	ldmfd	sp!, {r1-r12}	@ Restore r1-r8 pushed to stack earlier. r0 already has return result.
	ldmfd	sp, {sp}^	@ Restore user stack pointer, which might have been corrupt on preemption
	nop
	add	sp, sp, #4	@ Update sp.
	ldmfd	sp!, {lr}	@ Load userspace return address
	movs	pc, lr
END_PROC(arm_swi_exception)

/* Minimal abort state saved on data abort stack right after abort vector enters: */
#define	ABT_R0		0
#define ABT_SPSR	-4
#define ABT_R14		-8

/* Minimal prefetch abort state saved on abort stack upon entry. */
#define	ABT_R0		0
#define ABT_SPSR	-4
#define ABT_R14		-8

	/* Depending on the SPSR condition determines whether irqs should be enabled
	 * during abort handling. If abort occured in userspace it orders irqs
	 * should be enabled. Else if irqs come from kernel mode, it orders irqs are
	 * enabled only if they were alreday enabled before the abort. */
	.macro	can_abort_enable_irqs temp1, r_spsr
		and \temp1, \r_spsr, #ARM_MODE_MASK
		cmp \temp1, #ARM_MODE_USR	  @ Usermode indicates irqs can be enabled.
		beq 1f			    	  @ Z flag set. Which indicates "can enable"
		and \temp1, \r_spsr, #ARM_IRQ_BIT @ Clear irq bit indicates irqs were enabled
		cmp \temp1, #0		    	  @ before the abort and can be safely enabled.
	1:					  @ Z flag must be set for "can enable" here.
	.endm

	/* Pushes the user sp and lr to stack, updates the stack pointer */
	.macro push_user_sp_lr sp
		@ stack state: (Low) |..|..|->(Original)| (High)
		stmfd	\sp, {sp, lr}^	@ Push USR banked regs to stack.
		nop			@ Need a NOOP after push/popping user registers.
		@ stack state: (Low) |SP_USR|LR_USR|->(Original)| (High)
		sub	\sp, \sp, #8	@ Adjust SP, since stack op on banked regs is no writeback.
		@ stack state: (Low) |->SP_USR|LR_USR|(Original)| (High)
	.endm

	.macro is_psr_usr rx
	and	\rx, \rx, #ARM_MODE_MASK
	cmp	\rx, #ARM_MODE_USR
	.endm

/*
 * vect_pabt
 *
 * Upon Entry:
 * - R14_svc:	Address of next instruction after aborted instruction
 * - R14_usr:	Address of return instruction in last function call**
 * - PC:	0x0000000c
 * - IRQs are disabled (CPSR[7] = 1)
 *
 *
 * PURPOSE:
 * Used for handling instructions that caused *memory aborts* during
 * the *prefetching* of the instruction. The instruction is also marked
 * as invalid by the core. It handles the cause for the memory abort.
 *
 * (One reason why a memory abort would occur is when we were entering
 * into a new page region that contained executable code and was not
 * present in memory, or its physical-to-virtual translation was not
 * present in the page tables. See other causes for memory aborts)
 *
 * **In case abort occured in userspace. This is useful if the abort
 * was due to a null/invalid function pointer call. Since R14_abt
 * includes the aborting instruction itself, R14_usr gives the clue to
 * where this call came from.
 */
BEGIN_PROC(arm_prefetch_abort_exception_reentrant)
	sub	lr, lr, #4		@ lr-4 points at aborted instruction
	str	lr, [r13, #ABT_R14]	@ Store abort address.
	mrs	lr, spsr		@ Get SPSR
	str	lr, [r13, #ABT_SPSR]	@ Store SPSR
	str	r0, [r13, #ABT_R0]	@ Store R0 to use as temp register.
	mov	r0, r13			@ SP to R0
	mrs	lr, cpsr		@ Change to SVC mode.
	bic	lr, #ARM_MODE_MASK
	orr	lr, lr, #ARM_MODE_SVC
	msr	cpsr_fc, r14
	@ FIXME: Ensure 8-byte stack here.
	str	lr, [sp, #-8]!	@ NOTE: Switched mode! Save LR_SVC 2 words down from SP_SVC.
transfer_pabt_state_to_svc: 	@ Move data saved on PABT stack to SVC stack.
	ldr	lr, [r0, #ABT_R14]
	str	lr, [sp, #4]
	@ Stack state: |LR_SVC<-|LR_PABT|{original SP_SVC}|
	ldr	lr, [r0, #ABT_SPSR]
	ldr	r0, [r0, #ABT_R0]
	stmfd	sp!, {r0-r3,r12,lr}
	@ Stack state:  |R0<-|R1|R2|R3|R12|PABT_SPSR|LR_SVC|LR_PABT|{original SP_SVC}|
	push_user_sp_lr sp
	@ Stack state:  |SP_USR<-|LR_USR|R0|R1|R2|R3|R12|PABT_SPSR|LR_SVC|LR_PABT|{original SP_SVC}|
read_pabt_state:
	mrc	p15, 0, r1, c5, c0, 0	@ Read FSR (Tells why the fault occured) FIXME: Do we need this in pabt?
	mrc	p15, 0, r2, c6, c0, 0	@ Read FAR (Contains the faulted data address)  Do we need this in pabt?
	@ All abort state and (FAR/FSR) saved. Can safely enable irqs here, if need be.
	ldr	r3, [sp, #28]		@ Load PABT_SPSR
	can_abort_enable_irqs r0, r3	@ Judge if irqs can be enabled depending on prev state.
	bne	1f			@ Branch here based on previous irq judgement.
	enable_irqs r3
1:
	/* Now check in what mode abort occured, and return that mode's LR in R4 */
	ldr	r0, [sp, #28]		@ Load PABT_SPSR
	is_psr_usr r0			@ Test if PABT_SPSR was user mode.
	ldrne	r3, [sp, #32]		@ Abort occured in kernel, load LR_SVC
	ldreq	r3, [sp, #4]		@ Abort occured in user, load LR_USR
	ldr	r0, [sp, #36]		@ Load LR_PABT saved previously.
	mov	lr, pc
	ldr	pc, =prefetch_abort_handler @ Jump to function outside this page.
	disable_irqs r0			@ Disable irqs to avoid corrupting spsr.
					@ (i.e. an interrupt could overwrite spsr with current psr)
	ldmfd	sp, {sp, lr}^		@ Restore user sp and lr which might have been corrupt on preemption
	nop				@ User reg mod requires nop
	add	sp, sp, #8		@ Update SP.
	ldmfd	sp!, {r0-r3,r12,lr}	@ Restore previous context. (note, lr has spsr)
	msr	spsr_cxsf, r14		@ Restore spsr register from lr.
	@ Stack state: |LR_SVC<-|LR_PREV(PABT)|{original SP_SVC}|
	ldmfd	r13!, {r14, pc}^	@ Return, restoring cpsr. Note r14 gets r14_svc,
					@ and pc gets lr_dabt. Saved at #4 and #8 offsets
					@ down from where svc stack had left.
END_PROC(arm_prefetch_abort_exception_reentrant)

/*
 * vect_dabt
 *
 * Upon Entry:
 * - R14_abt:	Address of next instruction after aborted instruction
 * - PC:	0x00000010
 * - IRQs are disabled (CPSR[7] = 1)
 *
 *
 * PURPOSE:
 * Used for handling instructions that caused *memory aborts* during
 * the *execution* of the current instruction. This may happen if the
 * instruction accessed a memory address (e.g LDR/STR) that is not
 * defined as part of the currently executing process (aka illegal
 * access). Another possibility is the address is within the address
 * space of the process, but it is not mapped, i.e. does not have
 * physical-to-virtual translation entry in the page tables.
 */
BEGIN_PROC(arm_data_abort_exception)
	sub	lr, lr, #8	@ lr-8 points at aborted instruction
	mrc	p15, 0, r2, c5, c0, 0 @ Read FSR
	mrc	p15, 0, r1, c6, c0, 0 @ Read FAR
	mov	r0, lr		@ Get data abort address
	mov	r5, lr		@ Save it in r5 in case r0 will get trashed
	mov	lr, pc		@ Save return address
	ldr	pc, =data_abort_handler @ Jump to function outside this page.
1:
	b	1b
END_PROC(arm_data_abort_exception)
/*
 * The method of saving abort state to svc stack is identical with that of
 * reentrant irq vector. Natural to this, Restoring of the previous state
 * is also identical.
 */
BEGIN_PROC(arm_data_abort_exception_reentrant)
	sub	lr, lr, #8		@ Get abort address
	str	lr, [r13, #ABT_R14]	@ Store abort address
	mrs	lr, spsr		@ Get SPSR
	str	lr, [r13, #ABT_SPSR]	@ Store SPSR
	str	r0, [r13, #ABT_R0]	@ Store r0
	@ NOTE: Can increase data abort nest here.
	mov	r0, r13			@ Keep current sp point in R0
	mrs	lr, cpsr		@ Change to SVC mode.
	bic	lr, #ARM_MODE_MASK
	orr	lr, lr, #ARM_MODE_SVC
	msr	cpsr_fc, r14
	@ FIXME: Ensure 8-byte stack here.
	str	lr, [sp, #-8]!	@ Save lr_svc 2 words down from interrupted SP_SVC
transfer_dabt_state_to_svc:
	ldr	lr, [r0, #ABT_R14]
	str	lr, [sp, #4]
	@ Stack state:  |LR_SVC<-|LR_DABT|{original SP_SVC}|
	ldr	lr, [r0, #ABT_SPSR]
	ldr	r0, [r0, #ABT_R0]
	stmfd	sp!, {r0-r3,r12,lr}
	@ Stack state:  |R0<-|R1|R2|R3|R12|DABT_SPSR|LR_SVC|LR_DABT|{original SP_SVC}|
	push_user_sp_lr sp
	@ Stack state:  |SP_USR<-|LR_USR|R0|R1|R2|R3|R12|DABT_SPSR|LR_SVC|LR_DABT|{original SP_SVC}|
read_dabt_state:
	mrc	p15, 0, r1, c5, c0, 0	@ Read FSR (Tells why the fault occured)
	mrc	p15, 0, r2, c6, c0, 0	@ Read FAR (Contains the faulted data address)
	@ All abort state and (FAR/FSR) saved. Can safely enable irqs here, if need be.
	ldr	r3, [sp, #28]		@ Load DABT_SPSR
	can_abort_enable_irqs r0, r3	@ Judge if irqs can be enabled depending on prev state.
	bne	1f			@ Branch here based on previous irq judgement.
	enable_irqs r3
1:
	ldr	r0, [sp, #36]		@ Load LR_DABT saved previously.
	mov	lr, pc
	ldr	pc, =data_abort_handler @ Jump to function outside this page.
	disable_irqs r0			@ Disable irqs to avoid corrupting spsr.
	ldmfd	sp, {sp, lr}^		@ Restore user sp and lr which might have been corrupt on preemption
	nop				@ User reg mod requires nop
	add	sp, sp, #8		@ Update SP.
	ldmfd	sp!, {r0-r3,r12,lr}	@ Restore previous context. (note, lr has spsr)
	msr	spsr_cxsf, r14		@ Restore spsr register from lr.
	@ Stack state: |LR_SVC<-|LR_PREV(DABT)|{original SP_SVC}|
	ldmfd	r13!, {r14, pc}^	@ Return, restoring cpsr. Note r14 gets r14_svc,
					@ and pc gets lr_dabt. Saved at #4 and #8 offsets
					@ down from where svc stack had left.
END_PROC(arm_data_abort_exception_reentrant)

/*
 * vect_irq
 *
 * Upon Entry:
 * - R14:	Address of next instruction after interrupted instruction.
 * - PC:	0x00000018
 * - IRQs are disabled (CPSR[7] = 1)
 * - A vectored interrupt controller would also provide where to jump in
 *   order to handle the interrupt, or an irq controller in general would
 *   provide registers that indicate what kind of interrupt has occured.
 *
 *
 * PURPOSE:
 * Used for handling IRQs. IRQs have lower priority compared to other
 * types of exceptions.
 */

/* The most basic handler where neither context switching nor re-entry can occur. */
BEGIN_PROC(arm_irq_exception_basic)
	sub	lr, lr, #4
	stmfd	sp!, {r0-r3,lr}
	mov	lr, pc
	ldr	pc, =do_irq
	ldmfd	sp!, {r0-r3, pc}^
END_PROC(arm_irq_exception)

/* Minimal IRQ state saved on irq stack right after irq vector enters: */
#define	IRQ_R0		0
#define IRQ_SPSR	-4
#define IRQ_R14		-8

/* A reentrant handler that uses svc mode stack to prevent banked lr_irq corruption. */
BEGIN_PROC(arm_irq_exception_reentrant)
	sub	lr, lr, #4
@ Save minimal state to irq stack:
	str	r14, [r13, #IRQ_R14]	@ Save lr_irq
	mrs	r14, spsr		@ Copy spsr
	str	r14, [r13, #IRQ_SPSR]	@ Save spsr on irq stack
	str	r0, [r13, #IRQ_R0]	@ Save r0.
	mov	r0, r13			@ Using r0 to keep banked sp_irq when mode is switched.
	mrs	r14, cpsr		@ Get current psr (irq)
	bic	r14, #ARM_MODE_MASK	@ Clear mode part from psr
	orr	r14, r14, #ARM_MODE_SVC	@ Write SVC mode bits.
	msr	cpsr_fc, r14		@ Change to SVC mode.
	str	r14, [r13, #-8]!	@ Save lr_svc 2 words down from where svc stack left.
@ Transfer minimal irq state saved to svc stack:
	ldr	r14, [r0, #IRQ_R14]	@ Load lr_irq to lr using r0 that contains sp_irq.
	str	r14, [r13, #4]		@ Save lr_irq 1 word down from where svc stack left.
	ldr	r14, [r0, #IRQ_SPSR]	@ Load irq spsr.
	ldr	r0, [r0, #IRQ_R0]	@ Restore r0.
	stmfd	sp!, {r0-r3,r12,lr}	@ Save all of rest of irq context to svc stack.
	bl	do_irq			@ Read irq number etc. Free to re-enable irqs here.
	ldmfd	sp!, {r0-r3-r12,lr}	@ Restore previous context. (note, lr has spsr)
	msr	spsr_cxsf, lr		@ Restore spsr register from lr.
	ldmfd	r13!, {r14, pc}^	@ Return, restoring cpsr. Note r14 gets r14_svc,
					@ and pc gets lr_irq. Saved at #4 and #8 offsets
					@ down from where svc stack had left.
END_PROC(arm_irq_exception_reentrant)

	.macro	was_irq_mode rx
	mrs	rx, spsr_fc
	and	rx, rx, 0x1F
	cmp	rx, #ARM_MODE_IRQ
	.endm

	.macro	need_resched rx, ry
	get_current \rx
	ldr	\ry, =need_resched_offset
	ldr	\ry, [\ry]
	ldr	\ry, [\rx, \ry]
	cmp	\ry, #1
	.endm

/*
 * Keeps the PSR of the last pre-empted process. This helps to tell
 * what mode the process was in when it was preempted.
 */
.global preempted_psr;
preempted_psr:
.word	0

/* Keeps track of how many nests of irqs have happened. */
.global current_irq_nest_count;
current_irq_nest_count:
.word	0

/*
 * FIXME: current_irq_nest_count also counts for any preempt_disable() calls.
 * However this nesting check assumes all nests come from real irqs.
 * We should make this check just the real ones.
 */
#define IRQ_NESTING_MAX			15
	.macro	inc_irq_cnt_with_overnest_check rx, ry
	ldr	\rx, =current_irq_nest_count	@ Load the irq nest status word.
	ldr	\ry, [\rx]
	add	\ry, \ry, #1			@ No need for atomic inc since irqs are disabled.
	str	\ry, [\rx]
	cmp	\ry, #IRQ_NESTING_MAX		@ Check no more than max nests, and die miserably if so.
	ldrge	pc, =irq_overnest_error
	.endm

	@ This decrement need not be atomic because if you are *decrementing* this, then it means
	@ Preemption is already *disabled*. Ruling out preemption, only race could be against irqs.
	@ If an irq preempts it during decrement and modifies it, it is still responsible to change
	@ it back to the original value as it was when we read it, before it returns. So effectively
	@ anything that runs during the decrement does not affect the value of the count.
	.macro	dec_irq_nest_cnt rx, ry
	ldr	\ry, =current_irq_nest_count
	ldr	\rx, [\ry]
	sub	\rx, \rx, #1
	str	\rx, [\ry]
	.endm
	.macro in_process_context rx
	ldr	\rx, =current_irq_nest_count
	ldr	\rx, [\rx]
	cmp	\rx, #0
	.endm
	/* If interrupted a process (as opposed to another irq), saves spsr value to preempted_psr */
	.macro cmp_and_save_process_psr rx, process_psr
	in_process_context \rx		@ If nest count is 0, a running process is preempted.
	ldreq	\rx, =preempted_psr
	streq	\process_psr, [\rx]
	.endm

#define	CONTEXT_PSR		0
#define CONTEXT_R0		4
#define CONTEXT_R1		8
#define CONTEXT_R2		12
#define CONTEXT_R3		16
#define CONTEXT_R4		20
#define CONTEXT_R5		24
#define CONTEXT_R6		28
#define CONTEXT_R7		32
#define CONTEXT_R8		36
#define CONTEXT_r9		40
#define CONTEXT_R10		44
#define CONTEXT_R11		48
#define CONTEXT_R12		52
#define CONTEXT_R13		56
#define CONTEXT_R14		60
#define CONTEXT_PC		64

BEGIN_PROC(arm_irq_exception_reentrant_with_schedule)
	sub	lr, lr, #4
	str	lr, [r13, #IRQ_R14]	@ Save lr_irq
	mrs	r14, spsr		@ Copy spsr
	str	r14, [r13, #IRQ_SPSR]	@ Save spsr on irq stack
	str	r0, [r13, #IRQ_R0]	@ Save r0.
	cmp_and_save_process_psr r0, r14 @ R14 should have spsr here.
	inc_irq_cnt_with_overnest_check r0, r14
	mov	r0, r13			@ Using r0 to keep banked sp_irq when mode is switched.
	mrs	r14, cpsr		@ Get current psr (irq)
	bic	r14, #ARM_MODE_MASK	@ Clear mode part from psr
	orr	r14, r14, #ARM_MODE_SVC	@ Write SVC mode bits.
	msr	cpsr_fc, r14		@ Change to SVC mode.
	@ FIXME: Ensure 8-byte aligned stack here! Make sure to restore original state later!
	str	r14, [r13, #-8]!	@ Save lr_svc 2 words down from where svc stack left. SP updated.
@ Transfer minimal irq state to svc stack:
	ldr	r14, [r0, #IRQ_R14]	@ Load lr_irq to lr using r0 that contains sp_irq.
	str	r14, [r13, #4]		@ Save lr_irq 1 word down from where svc stack left.
	ldr	r14, [r0, #IRQ_SPSR]	@ Load irq spsr.
	ldr	r0, [r0, #IRQ_R0]	@ Restore r0.
	stmfd	sp!, {r0-r3,r12,lr}	@ Save all of rest of irq context to svc stack.
	mov	lr, pc
	ldr	pc, =do_irq		@ Read irq number etc. Free to re-enable irqs here.
	@ stack state: (Low) r0|r1|r2|r3|r12|SPSR|LR_SVC|LR_IRQ| (High)
	ldr	r0, =current_irq_nest_count
	ldr	r0, [r0]
	cmp	r0, #1			@ Expect 1 as lowest since each irq increase preempt cnt by 1.
	bgt	return_to_prev_context	@ if (irq_nest > 1) return_to_prev_context();
	need_resched r0, r1		@ if (irq_nest == 1 && need_resched) schedule();
	beq	preemption_path		@ if (irq_nest == 1 && !need_resched) return_to_prev_context();
return_to_prev_context:
	dec_irq_nest_cnt r0, r1
	disable_irqs r0			@ Disable irqs to avoid corrupting spsr.
	ldmfd	sp!, {r0-r3,r12,lr}	@ Restore previous context. (note, lr has spsr)
	msr	spsr_cxsf, r14		@ Restore spsr register from lr.
	@ stack state: (Low) |LR_SVC<-|LR_PREV(IRQ)|{original SP_SVC}| (High)
	ldmfd	r13!, {r14, pc}^	@ Return, restoring cpsr. Note r14 gets r14_svc,
					@ and pc gets lr_irq. Saved at #4 and #8 offsets
					@ down from where svc stack had left.
preemption_path:
	disable_irqs r0			@ Interrupts can corrupt stack state.
	get_current r0			@ Get the interrupted process
	@ stack state: (Low) |->r0|r1|r2|r3|r12|SPSR|LR_SVC|LR_IRQ(<return_address>)| (High)
save_interrupted_context:
	add	sp, sp, #4
	@ stack state: (Low) |r0|->r1|r2|r3|r12|SPSR|LR_SVC|LR_IRQ(<return_address>)| (High)
	ldmfd	sp!, {r1-r3, r12, lr}
	@ stack state: (Low) |r0|..|..|..|..|..|->LR_SVC|LR_IRQ(<return_address>)| (High)
	str	lr, [r0, #CONTEXT_PSR]
	is_psr_usr lr
	add	r0, r0, #CONTEXT_R1	@ Points at register save location for #CONTEXT_R1
	stmia	r0!, {r1-r12}
	ldmfd	sp!, {r1-r2}		@ At this point SP_SVC is at its original svc location.
	@ stack state: (Low) |r0|..|..|..|..|..|..|..|->(Original)| (High)
	@ register state: r0 = (register save loc for #CONTEXT_R13) r1 = LR_SVC, r2 = LR_IRQ
	beq	save_usr_context
save_svc_context:
	stmib	r0, {r1-r2}		@ Save LR_SVC and LR_RETURN in advancing locations.
	str	sp, [r0]		@ Current sp is where sp_svc has left, and r0 at #CONTEXT_SP loc.
	sub	r0, r0, #CONTEXT_R13	@ Go back to first word from SP position.
	ldr	r1, [sp, #-32]		@ Load r0 from stack
	str	r1, [r0, #CONTEXT_R0]	@ Save r0
	b 	prepare_schedule	@ All registers saved.
save_usr_context:
	sub	r0, r0, #CONTEXT_R13
	str	r2, [r0, #CONTEXT_PC]	@ Save Program counter
	@ LR_SVC need restoring because it won't be pushed to context frame. SP_SVC is already up-to-date.
	mov	lr, r1
	stmfd	sp, {sp, lr}^	@ Push USR banked regs to stack.
	@ stack state: (Low) |r0|..|..|..|..|..|SP_USR|LR_USR|->(Original)| (High)
	nop			@ Need a NOP after twiddling with usr registers.
	sub	sp, sp, #8	@ Adjust SP, since stack op on banked regs is no writeback.
	@ stack state: (Low) |r0|..|..|..|..|..|->SP_USR|LR_USR|(Original)| (High)
	ldmfd	sp!, {r1-r2} 	@ Pop USR Banked regs.
	@ stack state: (Low) |r0|..|..|..|..|..|..|..|->(Original)| (High)
	str	r1, [r0, #CONTEXT_R13]	@ Save SP_USR to context frame.
	str	r2, [r0, #CONTEXT_R14]	@ Save LR_USR to context frame.
	ldr	r1, [sp, #-32]
	str	r1, [r0, #CONTEXT_R0]
	@ stack state: (Low) |..|..|..|..|..|..|..|..|->(Original)| (High)
prepare_schedule:
	mov lr, pc
	ldr pc, =schedule
1:
	b	1b	/* To catch if schedule returns in irq mode */
END_PROC(arm_irq_exception_reentrant_with_schedule)

/*
 * Context switch implementation.
 *
 * Upon entry:
 *
 * - r0 = current ktcb ptr, r1 = next ktcb ptr. r2 and r3 = insignificant.
 * - The current mode is always SVC, but the call may be coming from interrupt
 *   or process context.
 * - If coming from interrupt, the interrupted context is already copied to current
 *   ktcb in the irq handler, before coming here. Interrupted context can be SVC or USR.
 *
 * PURPOSE: Handles all paths from irq exception, thread_switch system call,
 * and sleeping in the kernel.
 *
 * NOTES:
 * - If coming from interrupt, the interrupted context is already copied to current
 *   ktcb in the irq handler, before coming here. Interrupted context can be SVC or USR.
 * - If coming from a process context, the current process context need saving here.
 * - From irq contexts, preemption is disabled, i.e. preemption count is 1. This is because
 *   irqs naturally increase preemption count. From process context preemption count is 0.
 *   Process context disables preemption during schedule(), but re-enables before calling
 *   switch_to(). Irq and process contexts are distinguished by preemption_count.
 *   Furthermore, irqs are also disabled shortly before calling switch_to() from both contexts.
 *   This happens at points where stack state would be irrecoverable if an irq occured.
 */
BEGIN_PROC(arch_switch)
	in_process_context r2		@ Note this depends on preempt count being 0.
	beq	save_process_context	@ Voluntary switch needs explicit saving of current state.
	dec_irq_nest_cnt r2, r3		@ Soon leaving irq context, so reduce preempt count here.
	b 	load_next_context	@ Interrupted context already saved by irq handler.
save_process_context:		@ Voluntary process schedules enter here:
	mrs	r2, cpsr_fc
	str	r2, [r0]
	stmib	r0, {r0-r14}	@ Voluntary scheduling always in SVC mode, so using svc regs.
	str	r14, [r0, #CONTEXT_PC]	@ Store R15 as R14. R14 has return address for switch_to().
load_next_context:
	@ stack state: (Low) |..|..|..|..|..|..|..|..|..|->(Original)| (High)
	mov	sp, r1
	ldr	r0, [sp, #CONTEXT_PSR]	@ Load r0 with SPSR
	bic	r0, r0, #ARM_IRQ_BIT	@ Enable irqs on will-be-restored context.
	msr	spsr_fcxs, r0		@ Restore spsr from r0.
	is_psr_usr r0
	bne load_next_context_svc	@ Loading user context is different than svc.
load_next_context_usr:
	ldmib	sp, {r0-r14}^		@ Load all including banked user regs.
	ldr	lr, [sp, #CONTEXT_PC]	@ Load value of PC to r14
	orr	sp, sp, #0xFF0
	orr	sp, sp, #0x8		@ 8-byte aligned.
	movs	pc, lr			@ Jump to user changing modes.
load_next_context_svc:
	ldmib	sp, {r0-r15}^		@ Switch to svc context and jump, loading R13 and R14 from stack.
					@ This is OK since the jump is to current context.
END_PROC(arch_switch)


/*
 * vect_fiq
 *
 * Upon Entry:
 * - R14:	Address of next instruction after interrupted instruction.
 * - PC:	0x00000014
 * - FIQs are disabled (CPSR[6] = 1)
 * - IRQs are disabled (CPSR[7] = 1)
 * - As in IRQ, the irq controller would provide registers that indicate
 *   what kind of interrupt has occured.
 *
 * PURPOSE:
 * Handling of high-priority interrupts. FIQs have highest priority after
 * reset and data abort exceptions. They're mainly used for achieving
 * low-latency interrupts, e.g. for DMA.
 */
BEGIN_PROC(arm_fiq_exception)
END_PROC(arm_fiq_exception)

/* * * * * * * * * * * * * * * * * * * * * * * *
 * External functions with absolute addresses  *
 * * * * * * * * * * * * * * * * * * * * * * * */

/*
 * NOTE: Notes on relative and absolute symbols on this file:
 *
 * Note that branches (B and BL) are *RELATIVE* on ARM. So no need to take any
 * special action to access symbols within this file, even though this page
 * (in virtual memory) is relocated to another address at run-time (high or low
 * vectors) - this is an address other than where it is linked at, at
 * compile-time.
 *
 * To access external symbols from this file, (e.g. calling some function in the
 * kernel) one needs to use the: `LDR, pc, =external_symbol' pseudo-instruction,
 * (note the "=") and use absolute addressing. This automatically generates an
 * inline data word within the current module and indirectly loads the value in
 * that word to resolve the undefined reference. All other methods, (LDR, B
 * instructions, or ADR pseudoinstruction) generate relative addresses, and they
 * will complain for external symbols because a relative offset cannot be
 * calculated for an unknown distance. In conclusion, relative branches are
 * useful for accessing symbols on this page, but they mean nothing outside this
 * page, because the page is relocated at run-time. So, wherever you access
 * *relatively* outside this page, would be *relative* to where this page is at
 * that moment.
 */

/* * * * * * * * * * * * * * * * *
 * Stacks for Exception Vectors  *
 * * * * * * * * * * * * * * * * */
.global	__stacks_end;
.global __abt_stack_high;
.global __irq_stack_high;
.global __fiq_stack_high;
.global __und_stack_high;

/*
 * These are also linked at high vectors, just as any other symbol
 * on this page.
 */
.balign 4
.equ __abt_stack_high, (__abt_stack - __vector_vaddr + 0xFFFF0000);
.equ __irq_stack_high, (__irq_stack - __vector_vaddr + 0xFFFF0000);
.equ __fiq_stack_high, (__fiq_stack - __vector_vaddr + 0xFFFF0000);
.equ __und_stack_high, (__und_stack - __vector_vaddr + 0xFFFF0000);

/*
 * NOTE: This could be cache line aligned.
 * (use a macro, e.g. ____arm_asm_cache_aligned)
 */
.balign 4
__stacks_end:	.space 256
__abt_stack:	.space 256
__irq_stack:	.space 256
__fiq_stack:	.space 256
__und_stack:	.space 256

.balign 4096

