Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | /*
* Copyright (c) 2014-2015 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Thread context switching
*
* This module implements the routines necessary for thread context switching
* on ARCv2 CPUs.
*
* See isr_wrapper.S for details.
*/
#include <kernel_structs.h>
#include <offsets_short.h>
#include <toolchain.h>
#include <arch/cpu.h>
#include <v2/irq.h>
#include <swap_macros.h>
GTEXT(__swap)
GDATA(_k_neg_eagain)
GDATA(_kernel)
/**
*
* @brief Initiate a cooperative context switch
*
* The __swap() routine is invoked by various kernel services to effect
* a cooperative context switch. Prior to invoking __swap(), the caller
* disables interrupts via irq_lock() and the return 'key' is passed as a
* parameter to __swap(). The key is in fact the value stored in the register
* operand of a CLRI instruction.
*
* It stores the intlock key parameter into current->intlock_key.
* Given that __swap() is called to effect a cooperative context switch,
* the caller-saved integer registers are saved on the stack by the function
* call preamble to __swap(). This creates a custom stack frame that will be
* popped when returning from __swap(), but is not suitable for handling a
* return from an exception. Thus, the fact that the thread is pending because
* of a cooperative call to __swap() has to be recorded via the _CAUSE_COOP code
* in the relinquish_cause of the thread's k_thread structure. The
* _IrqExit()/_FirqExit() code will take care of doing the right thing to
* restore the thread status.
*
* When __swap() is invoked, we know the decision to perform a context switch or
* not has already been taken and a context switch must happen.
*
* @return may contain a return value setup by a call to
* _set_thread_return_value()
*
* C function prototype:
*
* unsigned int __swap (unsigned int key);
*
*/
SECTION_FUNC(TEXT, __swap)
/* interrupts are locked, interrupt key is in r0 */
mov r1, _kernel
ld_s r2, [r1, _kernel_offset_to_current]
/* save intlock key */
st_s r0, [r2, _thread_offset_to_intlock_key]
st _CAUSE_COOP, [r2, _thread_offset_to_relinquish_cause]
/*
* Carve space for the return value. Setting it to a default of
* -EAGAIN eliminates the need for the timeout code to set it.
* If another value is ever needed, it can be modified with
* _set_thread_return_value().
*/
ld r3, [_k_neg_eagain]
st_s r3, [r2, _thread_offset_to_return_value]
/*
* Save status32 and blink on the stack before the callee-saved registers.
* This is the same layout as the start of an IRQ stack frame.
*/
lr r3, [_ARC_V2_STATUS32]
push_s r3
#ifdef CONFIG_ARC_STACK_CHECKING
/* disable stack checking during swap */
bclr r3, r3, _ARC_V2_STATUS32_SC_BIT
kflag r3
#endif
#ifdef CONFIG_ARC_HAS_SECURE
lr r3, [_ARC_V2_SEC_STAT]
push_s r3
#endif
push_s blink
_save_callee_saved_regs
/* get the cached thread to run */
ld_s r2, [r1, _kernel_offset_to_ready_q_cache]
/* entering here, r2 contains the new current thread */
#ifdef CONFIG_ARC_STACK_CHECKING
_load_stack_check_regs
#endif
/* XXX - can be moved to delay slot of _CAUSE_RIRQ ? */
st_s r2, [r1, _kernel_offset_to_current]
_load_callee_saved_regs
#if defined(CONFIG_MPU_STACK_GUARD) || defined(CONFIG_USERSPACE)
push_s r2
mov r0, r2
bl configure_mpu_thread
pop_s r2
#endif
ld_s r3, [r2, _thread_offset_to_relinquish_cause]
breq r3, _CAUSE_RIRQ, _swap_return_from_rirq
nop
breq r3, _CAUSE_FIRQ, _swap_return_from_firq
nop
/* fall through to _swap_return_from_coop */
.balign 4
_swap_return_from_coop:
ld_s r1, [r2, _thread_offset_to_intlock_key]
st 0, [r2, _thread_offset_to_intlock_key]
ld_s r0, [r2, _thread_offset_to_return_value]
lr ilink, [_ARC_V2_STATUS32]
bbit1 ilink, _ARC_V2_STATUS32_AE_BIT, _return_from_exc
pop_s blink /* pc into blink */
#ifdef CONFIG_ARC_HAS_SECURE
pop_s r3 /* pop SEC_STAT */
/* sflag r3 */
/* sflag instruction is not supported in current ARC GNU */
.long 0x00ff302f
#endif
pop_s r3 /* status32 into r3 */
kflag r3 /* write status32 */
j_s.d [blink] /* always execute delay slot */
seti r1 /* delay slot */
.balign 4
_swap_return_from_rirq:
_swap_return_from_firq:
lr r3, [_ARC_V2_STATUS32]
bbit1 r3, _ARC_V2_STATUS32_AE_BIT, _return_from_exc_irq
/* pretend interrupt happened to use rtie instruction */
#ifdef CONFIG_ARC_HAS_SECURE
lr r3, [_ARC_V2_SEC_STAT]
/* set SEC_STAT.IRM = SECURE for interrupt return */
bset r3, r3, 3
/* sflag r3 */
/* sflag instruction is not supported in current ARC GNU */
.long 0x00ff302f
#endif
lr r3, [_ARC_V2_AUX_IRQ_ACT]
brne r3, 0, _swap_already_in_irq
/* use lowest interrupt priority */
or r3, r3, (1<<(CONFIG_NUM_IRQ_PRIO_LEVELS-1))
sr r3, [_ARC_V2_AUX_IRQ_ACT]
_swap_already_in_irq:
rtie
.balign 4
_return_from_exc_irq:
_pop_irq_stack_frame
sub_s sp, sp, ___isf_t_status32_OFFSET - ___isf_t_pc_OFFSET + 4
_return_from_exc:
/* put the return address to eret */
ld ilink, [sp] /* pc into ilink */
sr ilink, [_ARC_V2_ERET]
/* SEC_STAT is bypassed when CONFIG_ARC_HAS_SECURE */
/* put status32 into estatus */
ld ilink, [sp, ___isf_t_status32_OFFSET - ___isf_t_pc_OFFSET]
sr ilink, [_ARC_V2_ERSTATUS]
add_s sp, sp, ___isf_t_status32_OFFSET - ___isf_t_pc_OFFSET + 4
rtie
|