OpenCores
URL https://opencores.org/ocsvn/c0or1k/c0or1k/trunk

Subversion Repositories c0or1k

[/] [c0or1k/] [trunk/] [src/] [arch/] [arm/] [vectors.S.ARM] - Diff between revs 2 and 6

Show entire file | Details | Blame | View Log

Rev 2 Rev 6
?rev1line?
?rev2line?
 
/*
 
 * The vectors page. Includes all exception handlers.
 
 *
 
 * Copyright (C) 2007 Bahadir Balban
 
 */
 
 
 
#include INC_ARCH(asm.h)
 
#include INC_ARCH(asm-macros.S)
 
 
 
.section .data.vectors
 
__vector_vaddr:
 
 
 
BEGIN_PROC(arm_high_vector)
 
        b       arm_reset_exception
 
        b       arm_undef_exception_reentrant
 
        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)
 
 
 
 
 
#if defined(CONFIG_SUBARCH_V5)
 
        .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
 
#endif
 
 
 
#if defined (CONFIG_SUBARCH_V7) || defined(CONFIG_SUBARCH_V6)
 
        .macro disable_irqs rx
 
                cpsid   ia
 
        .endm
 
        .macro enable_irqs rx
 
                cpsie   ia
 
        .endm
 
#endif
 
 
 
#if defined (CONFIG_SUBARCH_V7)
 
        .macro clear_exclusive
 
                clrex
 
        .endm
 
#else
 
        .macro clear_exclusive
 
        .endm
 
#endif
 
 
 
        /* 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
 
        /* 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
 
 
 
/* These really both read the same unified FSR and FAR registers */
 
#if defined (CONFIG_SUBARCH_V5)
 
        .macro  cp15_read_ifsr rx
 
                mrc     p15, 0, \rx, c5, c0, 0  @ Read FSR (Tells why the fault occured)
 
 
 
        .endm
 
        .macro cp15_read_ifar rx
 
                mrc     p15, 0, \rx, c6, c0, 0  @ Read FAR (Contains the faulted data address)
 
        .endm
 
        .macro  cp15_read_dfsr rx
 
                mrc     p15, 0, \rx, c5, c0, 0  @ Read FSR (Tells why the fault occured)
 
 
 
        .endm
 
        .macro cp15_read_dfar rx
 
                mrc     p15, 0, \rx, c6, c0, 0  @ Read FAR (Contains the faulted data address)
 
        .endm
 
#endif
 
 
 
/* These read the distinguished IFSR, IFAR, DFSR and DFAR registers */
 
#if defined (CONFIG_SUBARCH_V6) || defined (CONFIG_SUBARCH_V7)
 
        .macro  cp15_read_ifsr rx
 
                mrc     p15, 0, \rx, c5, c0, 1  @ Read IFSR (Tells why the fault occured)
 
 
 
        .endm
 
        .macro cp15_read_ifar rx
 
                mrc     p15, 0, \rx, c6, c0, 2  @ Read IFAR (Contains the faulted data address)
 
        .endm
 
        .macro  cp15_read_dfsr rx
 
                mrc     p15, 0, \rx, c5, c0, 0  @ Read DFSR (Tells why the fault occured)
 
 
 
        .endm
 
        .macro cp15_read_dfar rx
 
                mrc     p15, 0, \rx, c6, c0, 0  @ Read DFAR (Contains the faulted data address)
 
        .endm
 
#endif
 
 
 
#define UNDEF_R0        0
 
#define UNDEF_SPSR      -4
 
#define UNDEF_R14       -8
 
 
 
/*
 
 * 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_reentrant)
 
        clear_exclusive
 
        str     lr, [sp, #UNDEF_R14]    @ Store undef address
 
        mrs     lr, spsr                @ Get SPSR
 
        str     lr, [sp, #UNDEF_SPSR]   @ Store SPSR
 
        str     r0, [sp, #UNDEF_R0]     @ Store r0
 
        @ NOTE: Can increase undef nest here.
 
        mov     r0, sp                  @ 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 Undef state to SVC
 
        ldr     lr, [r0, #UNDEF_R14]
 
        str     lr, [sp, #4]
 
        @ Stack state:  |LR_SVC<-|LR_UNDEF|{original SP_SVC}|
 
        ldr     lr, [r0, #UNDEF_SPSR]
 
        ldr     r0, [r0, #UNDEF_R0]
 
        stmfd   sp!, {r0-r3,r12,lr}
 
        @ Stack state:  |R0<-|R1|R2|R3|R12|UNDEF_SPSR|LR_SVC|LR_DUNDEF|{original SP_SVC}|
 
        push_user_sp_lr sp      @ NOTE: These must be pushed to avoid trashing them if preempted
 
        @ Stack state: |SP_USR<-|LR_USR|R0<-|R1|R2|R3|R12|UNDEF_SPSR|LR_SVC|LR_DUNDEF|{original SP_SVC}|
 
 
 
        @ All undef state saved. Can safely enable irqs here, if need be.
 
        ldr     r3, [sp, #28]           @ Load UNDEF_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 exception occured, and return that mode's LR in R4
 
         * Also poplulate r0,r1,r2 parameters for undefined_instr_handler
 
        */
 
        ldr     r1, [sp, #28]           @ Load UNDEF_SPSR
 
        is_psr_usr r0                   @ Test if UNDEF_SPSR was user mode.
 
        ldrne   r2, [sp, #32]           @ Abort occured in kernel, load LR_SVC
 
        ldreq   r2, [sp, #4]            @ Abort occured in user, load LR_USR
 
        ldr     r0, [sp, #36]           @ Load LR_UNDEF saved previously.
 
        mov     lr, pc
 
        ldr     pc, =undefined_instr_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(UNDEF)|{original SP_SVC}|
 
        ldmfd   sp!, {r14, pc}^         @ Return, restoring cpsr. Note r14 gets r14_svc,
 
                                        @ and pc gets lr_undef. Saved at #4 and #8 offsets
 
                                        @ down from where svc stack had left.
 
END_PROC(arm_undef_exception_reentrant)
 
 
 
/*
 
 * 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)
 
        clear_exclusive
 
        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
 
 
 
/*
 
 * vect_pabt
 
 *
 
 * Upon Entry:
 
 * - R14_abt:   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)
 
        clear_exclusive
 
        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      @ NOTE: These must be pushed to avoid trashing if preempted
 
        @ Stack state:  |SP_USR<-|LR_USR|R0|R1|R2|R3|R12|PABT_SPSR|LR_SVC|LR_PABT|{original SP_SVC}|
 
read_pabt_state:
 
        cp15_read_ifsr r1       @ Reads FSR on ARMv5, IFSR on ARMv6-v7. Fault status information
 
        cp15_read_ifar r2       @ Reads FAR on ARMv5, IFAR on ARMv6-v7. Fault address information
 
        @ 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:
 
        ldr     r3, [sp, #28]           @ Load PABT_SPSR to r3, the spsr for the aborted mode
 
        ldr     r0, [sp, #36]           @ Load LR_PABT - 4 saved previously. (Address that aborted)
 
        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)
 
        clear_exclusive
 
        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:
 
        cp15_read_dfsr r1       @ Read DFSR (Tells why the fault occured)
 
        cp15_read_dfar r2       @ Read DFAR (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  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
 
.word   0
 
.word   0
 
.word   0
 
 
 
/* Keeps track of how many nests of irqs have happened. */
 
.global current_irq_nest_count;
 
current_irq_nest_count:
 
.word   0
 
.word   0
 
.word   0
 
.word   0
 
 
 
#if defined (CONFIG_SMP)
 
        @ Rx contains the address of per cpu variable
 
        .macro per_cpu adr, temp, varname
 
                get_cpuid \temp
 
                ldr \adr, =\varname
 
                add \adr, \adr, \temp, lsl #2
 
        .endm
 
#else
 
        .macro per_cpu adr, temp, varname
 
                ldr \adr, =\varname
 
        .endm
 
#endif
 
 
 
/*
 
 * 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                 32
 
        .macro  inc_irq_cnt_with_overnest_check rx, ry
 
        per_cpu \rx, \ry, current_irq_nest_count @ Get per-cpu address of variable
 
        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
 
        per_cpu \ry, \rx, current_irq_nest_count
 
        ldr     \rx, [\ry]
 
        sub     \rx, \rx, #1
 
        str     \rx, [\ry]
 
        .endm
 
        .macro in_process_context rx, ry
 
        per_cpu \rx, \ry, 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, ry
 
        in_process_context \rx, \ry             @ If nest count is 0, a running process is preempted.
 
        bne 9999f                               @ Branch ahead if not a process
 
        per_cpu \rx, \ry, preempted_psr         @ Get per-cpu preempted psr
 
        mrs     \ry, SPSR                       @ Re-read spsr since register was trashed
 
        str     \ry, [\rx]                      @ Store it in per-cpu preempted psr
 
        9999:
 
        .endm
 
 
 
        /*
 
         * Clear irq bits on register.
 
         *
 
         * If ARMv5, only I-bit is cleared, but if ARMv6-v7,
 
         * A-bit is also cleared.
 
         */
 
        .macro clr_irq_bits_on_reg rx
 
                bic     \rx, #ARM_IRQ_BIT
 
#if defined (CONFIG_SUBARCH_V6) || defined (CONFIG_SUBARCH_V7)
 
                bic     \rx, #ARM_A_BIT
 
#endif
 
        .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
 
 
 
/*
 
 * TODO: Optimization:
 
 * May use SRS/RFE on irq exception _only_. But not
 
 * yet aware of its implications. Only irq handler can
 
 * do it because RFE enables interrupts unconditionally.
 
 */
 
BEGIN_PROC(arm_irq_exception_reentrant_with_schedule)
 
        clear_exclusive
 
        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)
 
 
 
/*
 
 * Decision point for taking the preemption path
 
 */
 
#if !defined(CONFIG_PREEMPT_DISABLE)
 
        per_cpu r0, r1, 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();
 
#endif
 
 
 
/*
 
 * Return to previous context path
 
 */
 
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
 
 */
 
#if !defined(CONFIG_PREEMPT_DISABLE)
 
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()| (High)
 
save_interrupted_context:
 
        add     sp, sp, #4
 
        @ stack state: (Low) |r0|->r1|r2|r3|r12|SPSR|LR_SVC|LR_IRQ()| (High)
 
        ldmfd   sp!, {r1-r3, r12, lr}
 
        @ stack state: (Low) |r0|..|..|..|..|..|->LR_SVC|LR_IRQ()| (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 */
 
#endif /* End of !CONFIG_PREEMPT_DISABLE */
 
 
 
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_context_switch)
 
        clear_exclusive
 
        in_process_context r2, r3       @ 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
 
        clr_irq_bits_on_reg r0          @ 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_context_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
 
 
 
/* 16 bytes each per-cpu, up to 8 cpus */
 
__stacks_end:   .space 128
 
__abt_stack:    .space 128
 
__irq_stack:    .space 128
 
__fiq_stack:    .space 128
 
__und_stack:    .space 128
 
 
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.