| /* |
| * 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, __z_arch_esf_t_SIZEOF |
| |
| /* Preserve all caller-saved registers onto the thread's stack */ |
| stw ra, __z_arch_esf_t_ra_OFFSET(sp) |
| stw r1, __z_arch_esf_t_r1_OFFSET(sp) |
| stw r2, __z_arch_esf_t_r2_OFFSET(sp) |
| stw r3, __z_arch_esf_t_r3_OFFSET(sp) |
| stw r4, __z_arch_esf_t_r4_OFFSET(sp) |
| stw r5, __z_arch_esf_t_r5_OFFSET(sp) |
| stw r6, __z_arch_esf_t_r6_OFFSET(sp) |
| stw r7, __z_arch_esf_t_r7_OFFSET(sp) |
| stw r8, __z_arch_esf_t_r8_OFFSET(sp) |
| stw r9, __z_arch_esf_t_r9_OFFSET(sp) |
| stw r10, __z_arch_esf_t_r10_OFFSET(sp) |
| stw r11, __z_arch_esf_t_r11_OFFSET(sp) |
| stw r12, __z_arch_esf_t_r12_OFFSET(sp) |
| stw r13, __z_arch_esf_t_r13_OFFSET(sp) |
| stw r14, __z_arch_esf_t_r14_OFFSET(sp) |
| stw r15, __z_arch_esf_t_r15_OFFSET(sp) |
| |
| /* Store value of estatus control register */ |
| rdctl et, estatus |
| stw et, __z_arch_esf_t_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, __z_arch_esf_t_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, __z_arch_esf_t_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, __z_arch_esf_t_instr_OFFSET(sp) |
| |
| /* Restore estatus |
| * XXX is this right??? */ |
| ldw r5, __z_arch_esf_t_estatus_OFFSET(sp) |
| wrctl estatus, r5 |
| |
| /* Restore caller-saved registers */ |
| ldw ra, __z_arch_esf_t_ra_OFFSET(sp) |
| ldw r1, __z_arch_esf_t_r1_OFFSET(sp) |
| ldw r2, __z_arch_esf_t_r2_OFFSET(sp) |
| ldw r3, __z_arch_esf_t_r3_OFFSET(sp) |
| ldw r4, __z_arch_esf_t_r4_OFFSET(sp) |
| ldw r5, __z_arch_esf_t_r5_OFFSET(sp) |
| ldw r6, __z_arch_esf_t_r6_OFFSET(sp) |
| ldw r7, __z_arch_esf_t_r7_OFFSET(sp) |
| ldw r8, __z_arch_esf_t_r8_OFFSET(sp) |
| ldw r9, __z_arch_esf_t_r9_OFFSET(sp) |
| ldw r10, __z_arch_esf_t_r10_OFFSET(sp) |
| ldw r11, __z_arch_esf_t_r11_OFFSET(sp) |
| ldw r12, __z_arch_esf_t_r12_OFFSET(sp) |
| ldw r13, __z_arch_esf_t_r13_OFFSET(sp) |
| ldw r14, __z_arch_esf_t_r14_OFFSET(sp) |
| ldw r15, __z_arch_esf_t_r15_OFFSET(sp) |
| |
| /* Put the stack pointer back where it was when we entered |
| * exception state |
| */ |
| addi sp, sp, __z_arch_esf_t_SIZEOF |
| |
| /* All done, copy estatus into status and transfer to ea */ |
| eret |