228 lines
6.6 KiB
ArmAsm
228 lines
6.6 KiB
ArmAsm
/*
|
|
* Copyright (c) 2016 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/toolchain.h>
|
|
#include <zephyr/linker/sections.h>
|
|
#include <offsets_short.h>
|
|
|
|
/* exports */
|
|
GTEXT(_exception)
|
|
|
|
/* import */
|
|
GTEXT(_Fault)
|
|
GTEXT(arch_swap)
|
|
#ifdef CONFIG_IRQ_OFFLOAD
|
|
GTEXT(z_irq_do_offload)
|
|
GTEXT(_offload_routine)
|
|
#endif
|
|
|
|
/* Allows use of r1/at register, otherwise reserved for assembler use */
|
|
.set noat
|
|
|
|
/* Placed into special 'exception' section so that the linker can put this code
|
|
* at ALT_CPU_EXCEPTION_ADDR defined in system.h
|
|
*
|
|
* This is the common entry point for processor exceptions and interrupts from
|
|
* the Internal Interrupt Controller (IIC).
|
|
*
|
|
* If the External (EIC) controller is in use, then we will never get here on
|
|
* behalf of an interrupt, instead the EIC driver will have set up a vector
|
|
* table and the processor will jump directly into the appropriate table
|
|
* entry.
|
|
*/
|
|
SECTION_FUNC(exception.entry, _exception)
|
|
/* Reserve thread stack space for saving context */
|
|
subi sp, sp, __struct_arch_esf_SIZEOF
|
|
|
|
/* Preserve all caller-saved registers onto the thread's stack */
|
|
stw ra, __struct_arch_esf_ra_OFFSET(sp)
|
|
stw r1, __struct_arch_esf_r1_OFFSET(sp)
|
|
stw r2, __struct_arch_esf_r2_OFFSET(sp)
|
|
stw r3, __struct_arch_esf_r3_OFFSET(sp)
|
|
stw r4, __struct_arch_esf_r4_OFFSET(sp)
|
|
stw r5, __struct_arch_esf_r5_OFFSET(sp)
|
|
stw r6, __struct_arch_esf_r6_OFFSET(sp)
|
|
stw r7, __struct_arch_esf_r7_OFFSET(sp)
|
|
stw r8, __struct_arch_esf_r8_OFFSET(sp)
|
|
stw r9, __struct_arch_esf_r9_OFFSET(sp)
|
|
stw r10, __struct_arch_esf_r10_OFFSET(sp)
|
|
stw r11, __struct_arch_esf_r11_OFFSET(sp)
|
|
stw r12, __struct_arch_esf_r12_OFFSET(sp)
|
|
stw r13, __struct_arch_esf_r13_OFFSET(sp)
|
|
stw r14, __struct_arch_esf_r14_OFFSET(sp)
|
|
stw r15, __struct_arch_esf_r15_OFFSET(sp)
|
|
|
|
/* Store value of estatus control register */
|
|
rdctl et, estatus
|
|
stw et, __struct_arch_esf_estatus_OFFSET(sp)
|
|
|
|
/* ea-4 is the address of the instruction when the exception happened,
|
|
* put this in the stack frame as well
|
|
*/
|
|
addi r15, ea, -4
|
|
stw r15, __struct_arch_esf_instr_OFFSET(sp)
|
|
|
|
/* Figure out whether we are here because of an interrupt or an
|
|
* exception. If an interrupt, switch stacks and enter IRQ handling
|
|
* code. If an exception, remain on current stack and enter exception
|
|
* handing code. From the CPU manual, ipending must be nonzero and
|
|
* estatis.PIE must be enabled for this to be considered an interrupt.
|
|
*
|
|
* Stick ipending in r4 since it will be an arg for _enter_irq
|
|
*/
|
|
rdctl r4, ipending
|
|
beq r4, zero, not_interrupt
|
|
/* We stashed estatus in et earlier */
|
|
andi r15, et, 1
|
|
beq r15, zero, not_interrupt
|
|
|
|
is_interrupt:
|
|
/* If we get here, this is an interrupt */
|
|
|
|
/* Grab a reference to _kernel in r10 so we can determine the
|
|
* current irq stack pointer
|
|
*/
|
|
movhi r10, %hi(_kernel)
|
|
ori r10, r10, %lo(_kernel)
|
|
|
|
/* Stash a copy of thread's sp in r12 so that we can put it on the IRQ
|
|
* stack
|
|
*/
|
|
mov r12, sp
|
|
|
|
/* Switch to interrupt stack */
|
|
ldw sp, _kernel_offset_to_irq_stack(r10)
|
|
|
|
/* Store thread stack pointer onto IRQ stack */
|
|
addi sp, sp, -4
|
|
stw r12, 0(sp)
|
|
|
|
on_irq_stack:
|
|
|
|
/* Enter C interrupt handling code. Value of ipending will be the
|
|
* function parameter since we put it in r4
|
|
*/
|
|
call _enter_irq
|
|
|
|
/* Interrupt handler finished and the interrupt should be serviced
|
|
* now, the appropriate bits in ipending should be cleared */
|
|
|
|
/* Get a reference to _kernel again in r10 */
|
|
movhi r10, %hi(_kernel)
|
|
ori r10, r10, %lo(_kernel)
|
|
|
|
#ifdef CONFIG_PREEMPT_ENABLED
|
|
ldw r11, _kernel_offset_to_current(r10)
|
|
/* Determine whether the exception of the ISR requires context
|
|
* switch
|
|
*/
|
|
|
|
/* Call into the kernel to see if a scheduling decision is necessary */
|
|
ldw r2, _kernel_offset_to_ready_q_cache(r10)
|
|
beq r2, r11, no_reschedule
|
|
|
|
/*
|
|
* A context reschedule is required: keep the volatile registers of
|
|
* the interrupted thread on the context's stack. Utilize
|
|
* the existing arch_swap() primitive to save the remaining
|
|
* thread's registers (including floating point) and perform
|
|
* a switch to the new thread.
|
|
*/
|
|
|
|
/* We put the thread stack pointer on top of the IRQ stack before
|
|
* we switched stacks. Restore it to go back to thread stack
|
|
*/
|
|
ldw sp, 0(sp)
|
|
|
|
/* Argument to Swap() is estatus since that's the state of the
|
|
* status register before the exception happened. When coming
|
|
* out of the context switch we need this info to restore
|
|
* IRQ lock state. We put this value in et earlier.
|
|
*/
|
|
mov r4, et
|
|
|
|
call arch_swap
|
|
jmpi _exception_exit
|
|
#else
|
|
jmpi no_reschedule
|
|
#endif /* CONFIG_PREEMPT_ENABLED */
|
|
|
|
not_interrupt:
|
|
|
|
/* Since this wasn't an interrupt we're not going to restart the
|
|
* faulting instruction.
|
|
*
|
|
* We earlier put ea - 4 in the stack frame, replace it with just ea
|
|
*/
|
|
stw ea, __struct_arch_esf_instr_OFFSET(sp)
|
|
|
|
#ifdef CONFIG_IRQ_OFFLOAD
|
|
/* Check the contents of _offload_routine. If non-NULL, jump into
|
|
* the interrupt code anyway.
|
|
*/
|
|
movhi r10, %hi(_offload_routine)
|
|
ori r10, r10, %lo(_offload_routine)
|
|
ldw r11, (r10)
|
|
bne r11, zero, is_interrupt
|
|
#endif
|
|
|
|
_exception_enter_fault:
|
|
/* If we get here, the exception wasn't in interrupt or an
|
|
* invocation of irq_oflload(). Let _Fault() handle it in
|
|
* C domain
|
|
*/
|
|
|
|
mov r4, sp
|
|
call _Fault
|
|
jmpi _exception_exit
|
|
|
|
no_reschedule:
|
|
|
|
/* We put the thread stack pointer on top of the IRQ stack before
|
|
* we switched stacks. Restore it to go back to thread stack
|
|
*/
|
|
ldw sp, 0(sp)
|
|
|
|
/* Fall through */
|
|
|
|
_exception_exit:
|
|
/* We are on the thread stack. Restore all saved registers
|
|
* and return to the interrupted context */
|
|
|
|
/* Return address from the exception */
|
|
ldw ea, __struct_arch_esf_instr_OFFSET(sp)
|
|
|
|
/* Restore estatus
|
|
* XXX is this right??? */
|
|
ldw r5, __struct_arch_esf_estatus_OFFSET(sp)
|
|
wrctl estatus, r5
|
|
|
|
/* Restore caller-saved registers */
|
|
ldw ra, __struct_arch_esf_ra_OFFSET(sp)
|
|
ldw r1, __struct_arch_esf_r1_OFFSET(sp)
|
|
ldw r2, __struct_arch_esf_r2_OFFSET(sp)
|
|
ldw r3, __struct_arch_esf_r3_OFFSET(sp)
|
|
ldw r4, __struct_arch_esf_r4_OFFSET(sp)
|
|
ldw r5, __struct_arch_esf_r5_OFFSET(sp)
|
|
ldw r6, __struct_arch_esf_r6_OFFSET(sp)
|
|
ldw r7, __struct_arch_esf_r7_OFFSET(sp)
|
|
ldw r8, __struct_arch_esf_r8_OFFSET(sp)
|
|
ldw r9, __struct_arch_esf_r9_OFFSET(sp)
|
|
ldw r10, __struct_arch_esf_r10_OFFSET(sp)
|
|
ldw r11, __struct_arch_esf_r11_OFFSET(sp)
|
|
ldw r12, __struct_arch_esf_r12_OFFSET(sp)
|
|
ldw r13, __struct_arch_esf_r13_OFFSET(sp)
|
|
ldw r14, __struct_arch_esf_r14_OFFSET(sp)
|
|
ldw r15, __struct_arch_esf_r15_OFFSET(sp)
|
|
|
|
/* Put the stack pointer back where it was when we entered
|
|
* exception state
|
|
*/
|
|
addi sp, sp, __struct_arch_esf_SIZEOF
|
|
|
|
/* All done, copy estatus into status and transfer to ea */
|
|
eret
|