This trick turns out also to be needed by the abort/join code. Promote it to a more formal-looking internal API and clean up the documentation to (hopefully) clarify the exact behavior and better explain the need. This is one of the more... enchanted bits of the scheduler, and while the trick is IMHO pretty clean, it remains a big SMP footgun. Signed-off-by: Andy Ross <andyross@google.com>
574 lines
17 KiB
C
574 lines
17 KiB
C
/* swap_macros.h - helper macros for context switch */
|
|
|
|
/*
|
|
* Copyright (c) 2014 Wind River Systems, Inc.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#ifndef ZEPHYR_ARCH_ARC_INCLUDE_SWAP_MACROS_H_
|
|
#define ZEPHYR_ARCH_ARC_INCLUDE_SWAP_MACROS_H_
|
|
|
|
#include <zephyr/kernel_structs.h>
|
|
#include <offsets_short.h>
|
|
#include <zephyr/toolchain.h>
|
|
#include <zephyr/arch/cpu.h>
|
|
#include <zephyr/arch/arc/tool-compat.h>
|
|
#include <zephyr/arch/arc/asm-compat/assembler.h>
|
|
#include <zephyr/kernel.h>
|
|
#include "../core/dsp/swap_dsp_macros.h"
|
|
|
|
#ifdef _ASMLANGUAGE
|
|
|
|
/* save callee regs of current thread in r2 */
|
|
.macro _save_callee_saved_regs
|
|
|
|
SUBR sp, sp, ___callee_saved_stack_t_SIZEOF
|
|
|
|
/* save regs on stack */
|
|
STR r13, sp, ___callee_saved_stack_t_r13_OFFSET
|
|
STR r14, sp, ___callee_saved_stack_t_r14_OFFSET
|
|
STR r15, sp, ___callee_saved_stack_t_r15_OFFSET
|
|
STR r16, sp, ___callee_saved_stack_t_r16_OFFSET
|
|
STR r17, sp, ___callee_saved_stack_t_r17_OFFSET
|
|
STR r18, sp, ___callee_saved_stack_t_r18_OFFSET
|
|
STR r19, sp, ___callee_saved_stack_t_r19_OFFSET
|
|
STR r20, sp, ___callee_saved_stack_t_r20_OFFSET
|
|
STR r21, sp, ___callee_saved_stack_t_r21_OFFSET
|
|
STR r22, sp, ___callee_saved_stack_t_r22_OFFSET
|
|
STR r23, sp, ___callee_saved_stack_t_r23_OFFSET
|
|
STR r24, sp, ___callee_saved_stack_t_r24_OFFSET
|
|
STR r25, sp, ___callee_saved_stack_t_r25_OFFSET
|
|
STR r26, sp, ___callee_saved_stack_t_r26_OFFSET
|
|
STR fp, sp, ___callee_saved_stack_t_fp_OFFSET
|
|
|
|
#ifdef CONFIG_USERSPACE
|
|
#ifdef CONFIG_ARC_HAS_SECURE
|
|
#ifdef CONFIG_ARC_SECURE_FIRMWARE
|
|
lr r13, [_ARC_V2_SEC_U_SP]
|
|
st_s r13, [sp, ___callee_saved_stack_t_user_sp_OFFSET]
|
|
lr r13, [_ARC_V2_SEC_K_SP]
|
|
st_s r13, [sp, ___callee_saved_stack_t_kernel_sp_OFFSET]
|
|
#else
|
|
lr r13, [_ARC_V2_USER_SP]
|
|
st_s r13, [sp, ___callee_saved_stack_t_user_sp_OFFSET]
|
|
lr r13, [_ARC_V2_KERNEL_SP]
|
|
st_s r13, [sp, ___callee_saved_stack_t_kernel_sp_OFFSET]
|
|
#endif /* CONFIG_ARC_SECURE_FIRMWARE */
|
|
#else
|
|
lr r13, [_ARC_V2_USER_SP]
|
|
st_s r13, [sp, ___callee_saved_stack_t_user_sp_OFFSET]
|
|
#endif
|
|
#endif
|
|
STR r30, sp, ___callee_saved_stack_t_r30_OFFSET
|
|
|
|
#ifdef CONFIG_ARC_HAS_ACCL_REGS
|
|
STR r58, sp, ___callee_saved_stack_t_r58_OFFSET
|
|
#ifndef CONFIG_64BIT
|
|
STR r59, sp, ___callee_saved_stack_t_r59_OFFSET
|
|
#endif /* !CONFIG_64BIT */
|
|
#endif
|
|
|
|
#ifdef CONFIG_FPU_SHARING
|
|
ld_s r13, [r2, ___thread_base_t_user_options_OFFSET]
|
|
bbit0 r13, K_FP_IDX, fpu_skip_save
|
|
lr r13, [_ARC_V2_FPU_STATUS]
|
|
st_s r13, [sp, ___callee_saved_stack_t_fpu_status_OFFSET]
|
|
lr r13, [_ARC_V2_FPU_CTRL]
|
|
st_s r13, [sp, ___callee_saved_stack_t_fpu_ctrl_OFFSET]
|
|
|
|
#ifdef CONFIG_FP_FPU_DA
|
|
lr r13, [_ARC_V2_FPU_DPFP1L]
|
|
st_s r13, [sp, ___callee_saved_stack_t_dpfp1l_OFFSET]
|
|
lr r13, [_ARC_V2_FPU_DPFP1H]
|
|
st_s r13, [sp, ___callee_saved_stack_t_dpfp1h_OFFSET]
|
|
lr r13, [_ARC_V2_FPU_DPFP2L]
|
|
st_s r13, [sp, ___callee_saved_stack_t_dpfp2l_OFFSET]
|
|
lr r13, [_ARC_V2_FPU_DPFP2H]
|
|
st_s r13, [sp, ___callee_saved_stack_t_dpfp2h_OFFSET]
|
|
#endif
|
|
#endif
|
|
fpu_skip_save :
|
|
_save_dsp_regs
|
|
/* save stack pointer in struct k_thread */
|
|
STR sp, r2, _thread_offset_to_sp
|
|
.endm
|
|
|
|
/* load the callee regs of thread (in r2)*/
|
|
.macro _load_callee_saved_regs
|
|
/* restore stack pointer from struct k_thread */
|
|
LDR sp, r2, _thread_offset_to_sp
|
|
|
|
#ifdef CONFIG_ARC_HAS_ACCL_REGS
|
|
LDR r58, sp, ___callee_saved_stack_t_r58_OFFSET
|
|
#ifndef CONFIG_64BIT
|
|
LDR r59, sp, ___callee_saved_stack_t_r59_OFFSET
|
|
#endif /* !CONFIG_64BIT */
|
|
#endif
|
|
|
|
#ifdef CONFIG_FPU_SHARING
|
|
ld_s r13, [r2, ___thread_base_t_user_options_OFFSET]
|
|
bbit0 r13, K_FP_IDX, fpu_skip_load
|
|
|
|
ld_s r13, [sp, ___callee_saved_stack_t_fpu_status_OFFSET]
|
|
sr r13, [_ARC_V2_FPU_STATUS]
|
|
ld_s r13, [sp, ___callee_saved_stack_t_fpu_ctrl_OFFSET]
|
|
sr r13, [_ARC_V2_FPU_CTRL]
|
|
|
|
#ifdef CONFIG_FP_FPU_DA
|
|
ld_s r13, [sp, ___callee_saved_stack_t_dpfp1l_OFFSET]
|
|
sr r13, [_ARC_V2_FPU_DPFP1L]
|
|
ld_s r13, [sp, ___callee_saved_stack_t_dpfp1h_OFFSET]
|
|
sr r13, [_ARC_V2_FPU_DPFP1H]
|
|
ld_s r13, [sp, ___callee_saved_stack_t_dpfp2l_OFFSET]
|
|
sr r13, [_ARC_V2_FPU_DPFP2L]
|
|
ld_s r13, [sp, ___callee_saved_stack_t_dpfp2h_OFFSET]
|
|
sr r13, [_ARC_V2_FPU_DPFP2H]
|
|
#endif
|
|
#endif
|
|
fpu_skip_load :
|
|
_load_dsp_regs
|
|
#ifdef CONFIG_USERSPACE
|
|
#ifdef CONFIG_ARC_HAS_SECURE
|
|
#ifdef CONFIG_ARC_SECURE_FIRMWARE
|
|
ld_s r13, [sp, ___callee_saved_stack_t_user_sp_OFFSET]
|
|
sr r13, [_ARC_V2_SEC_U_SP]
|
|
ld_s r13, [sp, ___callee_saved_stack_t_kernel_sp_OFFSET]
|
|
sr r13, [_ARC_V2_SEC_K_SP]
|
|
#else
|
|
ld_s r13, [sp, ___callee_saved_stack_t_user_sp_OFFSET]
|
|
sr r13, [_ARC_V2_USER_SP]
|
|
ld_s r13, [sp, ___callee_saved_stack_t_kernel_sp_OFFSET]
|
|
sr r13, [_ARC_V2_KERNEL_SP]
|
|
#endif /* CONFIG_ARC_SECURE_FIRMWARE */
|
|
#else
|
|
ld_s r13, [sp, ___callee_saved_stack_t_user_sp_OFFSET]
|
|
sr r13, [_ARC_V2_USER_SP]
|
|
#endif
|
|
#endif
|
|
|
|
LDR r13, sp, ___callee_saved_stack_t_r13_OFFSET
|
|
LDR r14, sp, ___callee_saved_stack_t_r14_OFFSET
|
|
LDR r15, sp, ___callee_saved_stack_t_r15_OFFSET
|
|
LDR r16, sp, ___callee_saved_stack_t_r16_OFFSET
|
|
LDR r17, sp, ___callee_saved_stack_t_r17_OFFSET
|
|
LDR r18, sp, ___callee_saved_stack_t_r18_OFFSET
|
|
LDR r19, sp, ___callee_saved_stack_t_r19_OFFSET
|
|
LDR r20, sp, ___callee_saved_stack_t_r20_OFFSET
|
|
LDR r21, sp, ___callee_saved_stack_t_r21_OFFSET
|
|
LDR r22, sp, ___callee_saved_stack_t_r22_OFFSET
|
|
LDR r23, sp, ___callee_saved_stack_t_r23_OFFSET
|
|
LDR r24, sp, ___callee_saved_stack_t_r24_OFFSET
|
|
LDR r25, sp, ___callee_saved_stack_t_r25_OFFSET
|
|
LDR r26, sp, ___callee_saved_stack_t_r26_OFFSET
|
|
LDR fp, sp, ___callee_saved_stack_t_fp_OFFSET
|
|
LDR r30, sp, ___callee_saved_stack_t_r30_OFFSET
|
|
|
|
ADDR sp, sp, ___callee_saved_stack_t_SIZEOF
|
|
|
|
.endm
|
|
|
|
/* discard callee regs */
|
|
.macro _discard_callee_saved_regs
|
|
ADDR sp, sp, ___callee_saved_stack_t_SIZEOF
|
|
.endm
|
|
|
|
/*
|
|
* Must be called with interrupts locked or in P0.
|
|
* Upon exit, sp will be pointing to the stack frame.
|
|
*/
|
|
.macro _create_irq_stack_frame
|
|
|
|
SUBR sp, sp, ___isf_t_SIZEOF
|
|
|
|
STR blink, sp, ___isf_t_blink_OFFSET
|
|
|
|
/* store these right away so we can use them if needed */
|
|
|
|
STR r13, sp, ___isf_t_r13_OFFSET
|
|
STR r12, sp, ___isf_t_r12_OFFSET
|
|
STR r11, sp, ___isf_t_r11_OFFSET
|
|
STR r10, sp, ___isf_t_r10_OFFSET
|
|
STR r9, sp, ___isf_t_r9_OFFSET
|
|
STR r8, sp, ___isf_t_r8_OFFSET
|
|
STR r7, sp, ___isf_t_r7_OFFSET
|
|
STR r6, sp, ___isf_t_r6_OFFSET
|
|
STR r5, sp, ___isf_t_r5_OFFSET
|
|
STR r4, sp, ___isf_t_r4_OFFSET
|
|
STR r3, sp, ___isf_t_r3_OFFSET
|
|
STR r2, sp, ___isf_t_r2_OFFSET
|
|
STR r1, sp, ___isf_t_r1_OFFSET
|
|
STR r0, sp, ___isf_t_r0_OFFSET
|
|
|
|
#ifdef CONFIG_ARC_HAS_ZOL
|
|
MOVR r0, lp_count
|
|
STR r0, sp, ___isf_t_lp_count_OFFSET
|
|
LRR r1, [_ARC_V2_LP_START]
|
|
LRR r0, [_ARC_V2_LP_END]
|
|
STR r1, sp, ___isf_t_lp_start_OFFSET
|
|
STR r0, sp, ___isf_t_lp_end_OFFSET
|
|
#endif /* CONFIG_ARC_HAS_ZOL */
|
|
|
|
#ifdef CONFIG_CODE_DENSITY
|
|
lr r1, [_ARC_V2_JLI_BASE]
|
|
lr r0, [_ARC_V2_LDI_BASE]
|
|
lr r2, [_ARC_V2_EI_BASE]
|
|
st_s r1, [sp, ___isf_t_jli_base_OFFSET]
|
|
st_s r0, [sp, ___isf_t_ldi_base_OFFSET]
|
|
st_s r2, [sp, ___isf_t_ei_base_OFFSET]
|
|
#endif
|
|
|
|
.endm
|
|
|
|
/*
|
|
* Must be called with interrupts locked or in P0.
|
|
* sp must be pointing the to stack frame.
|
|
*/
|
|
.macro _pop_irq_stack_frame
|
|
|
|
LDR blink, sp, ___isf_t_blink_OFFSET
|
|
|
|
#ifdef CONFIG_CODE_DENSITY
|
|
ld_s r1, [sp, ___isf_t_jli_base_OFFSET]
|
|
ld_s r0, [sp, ___isf_t_ldi_base_OFFSET]
|
|
ld_s r2, [sp, ___isf_t_ei_base_OFFSET]
|
|
sr r1, [_ARC_V2_JLI_BASE]
|
|
sr r0, [_ARC_V2_LDI_BASE]
|
|
sr r2, [_ARC_V2_EI_BASE]
|
|
#endif
|
|
|
|
#ifdef CONFIG_ARC_HAS_ZOL
|
|
LDR r0, sp, ___isf_t_lp_count_OFFSET
|
|
MOVR lp_count, r0
|
|
LDR r1, sp, ___isf_t_lp_start_OFFSET
|
|
LDR r0, sp, ___isf_t_lp_end_OFFSET
|
|
SRR r1, [_ARC_V2_LP_START]
|
|
SRR r0, [_ARC_V2_LP_END]
|
|
#endif /* CONFIG_ARC_HAS_ZOL */
|
|
|
|
LDR r13, sp, ___isf_t_r13_OFFSET
|
|
LDR r12, sp, ___isf_t_r12_OFFSET
|
|
LDR r11, sp, ___isf_t_r11_OFFSET
|
|
LDR r10, sp, ___isf_t_r10_OFFSET
|
|
LDR r9, sp, ___isf_t_r9_OFFSET
|
|
LDR r8, sp, ___isf_t_r8_OFFSET
|
|
LDR r7, sp, ___isf_t_r7_OFFSET
|
|
LDR r6, sp, ___isf_t_r6_OFFSET
|
|
LDR r5, sp, ___isf_t_r5_OFFSET
|
|
LDR r4, sp, ___isf_t_r4_OFFSET
|
|
LDR r3, sp, ___isf_t_r3_OFFSET
|
|
LDR r2, sp, ___isf_t_r2_OFFSET
|
|
LDR r1, sp, ___isf_t_r1_OFFSET
|
|
LDR r0, sp, ___isf_t_r0_OFFSET
|
|
|
|
|
|
/*
|
|
* All gprs have been reloaded, the only one that is still usable is
|
|
* ilink.
|
|
*
|
|
* The pc and status32 values will still be on the stack. We cannot
|
|
* pop them yet because the callers of _pop_irq_stack_frame must reload
|
|
* status32 differently depending on the execution context they are
|
|
* running in (arch_switch(), firq or exception).
|
|
*/
|
|
ADDR sp, sp, ___isf_t_SIZEOF
|
|
|
|
.endm
|
|
|
|
/*
|
|
* To use this macro, r2 should have the value of thread struct pointer to
|
|
* _kernel.current. r3 is a scratch reg.
|
|
*/
|
|
.macro _load_stack_check_regs
|
|
#if defined(CONFIG_ARC_SECURE_FIRMWARE)
|
|
ld r3, [r2, _thread_offset_to_k_stack_base]
|
|
sr r3, [_ARC_V2_S_KSTACK_BASE]
|
|
ld r3, [r2, _thread_offset_to_k_stack_top]
|
|
sr r3, [_ARC_V2_S_KSTACK_TOP]
|
|
#ifdef CONFIG_USERSPACE
|
|
ld r3, [r2, _thread_offset_to_u_stack_base]
|
|
sr r3, [_ARC_V2_S_USTACK_BASE]
|
|
ld r3, [r2, _thread_offset_to_u_stack_top]
|
|
sr r3, [_ARC_V2_S_USTACK_TOP]
|
|
#endif
|
|
#else /* CONFIG_ARC_HAS_SECURE */
|
|
ld r3, [r2, _thread_offset_to_k_stack_base]
|
|
sr r3, [_ARC_V2_KSTACK_BASE]
|
|
ld r3, [r2, _thread_offset_to_k_stack_top]
|
|
sr r3, [_ARC_V2_KSTACK_TOP]
|
|
#ifdef CONFIG_USERSPACE
|
|
ld r3, [r2, _thread_offset_to_u_stack_base]
|
|
sr r3, [_ARC_V2_USTACK_BASE]
|
|
ld r3, [r2, _thread_offset_to_u_stack_top]
|
|
sr r3, [_ARC_V2_USTACK_TOP]
|
|
#endif
|
|
#endif /* CONFIG_ARC_SECURE_FIRMWARE */
|
|
.endm
|
|
|
|
/* check and increase the interrupt nest counter
|
|
* after increase, check whether nest counter == 1
|
|
* the result will be EQ bit of status32
|
|
* two temp regs are needed
|
|
*/
|
|
.macro _check_and_inc_int_nest_counter, reg1, reg2
|
|
#ifdef CONFIG_SMP
|
|
/* get pointer to _cpu_t of this CPU */
|
|
_get_cpu_id MACRO_ARG(reg1)
|
|
ASLR MACRO_ARG(reg1), MACRO_ARG(reg1), ARC_REGSHIFT
|
|
LDR MACRO_ARG(reg1), MACRO_ARG(reg1), _curr_cpu
|
|
/* _cpu_t.nested is 32 bit despite of platform bittnes */
|
|
ld MACRO_ARG(reg2), [MACRO_ARG(reg1), ___cpu_t_nested_OFFSET]
|
|
#else
|
|
MOVR MACRO_ARG(reg1), _kernel
|
|
/* z_kernel.nested is 32 bit despite of platform bittnes */
|
|
ld MACRO_ARG(reg2), [MACRO_ARG(reg1), _kernel_offset_to_nested]
|
|
#endif
|
|
add MACRO_ARG(reg2), MACRO_ARG(reg2), 1
|
|
#ifdef CONFIG_SMP
|
|
st MACRO_ARG(reg2), [MACRO_ARG(reg1), ___cpu_t_nested_OFFSET]
|
|
#else
|
|
st MACRO_ARG(reg2), [MACRO_ARG(reg1), _kernel_offset_to_nested]
|
|
#endif
|
|
cmp MACRO_ARG(reg2), 1
|
|
.endm
|
|
|
|
/* decrease interrupt stack nest counter
|
|
* the counter > 0, interrupt stack is used, or
|
|
* not used
|
|
*/
|
|
.macro _dec_int_nest_counter, reg1, reg2
|
|
#ifdef CONFIG_SMP
|
|
/* get pointer to _cpu_t of this CPU */
|
|
_get_cpu_id MACRO_ARG(reg1)
|
|
ASLR MACRO_ARG(reg1), MACRO_ARG(reg1), ARC_REGSHIFT
|
|
LDR MACRO_ARG(reg1), MACRO_ARG(reg1), _curr_cpu
|
|
/* _cpu_t.nested is 32 bit despite of platform bittnes */
|
|
ld MACRO_ARG(reg2), [MACRO_ARG(reg1), ___cpu_t_nested_OFFSET]
|
|
#else
|
|
MOVR MACRO_ARG(reg1), _kernel
|
|
/* z_kernel.nested is 32 bit despite of platform bittnes */
|
|
ld MACRO_ARG(reg2), [MACRO_ARG(reg1), _kernel_offset_to_nested]
|
|
#endif
|
|
sub MACRO_ARG(reg2), MACRO_ARG(reg2), 1
|
|
#ifdef CONFIG_SMP
|
|
st MACRO_ARG(reg2), [MACRO_ARG(reg1), ___cpu_t_nested_OFFSET]
|
|
#else
|
|
st MACRO_ARG(reg2), [MACRO_ARG(reg1), _kernel_offset_to_nested]
|
|
#endif
|
|
.endm
|
|
|
|
/* If multi bits in IRQ_ACT are set, i.e. last bit != fist bit, it's
|
|
* in nest interrupt. The result will be EQ bit of status32
|
|
* need two temp reg to do this
|
|
*/
|
|
.macro _check_nest_int_by_irq_act, reg1, reg2
|
|
lr MACRO_ARG(reg1), [_ARC_V2_AUX_IRQ_ACT]
|
|
#ifdef CONFIG_ARC_SECURE_FIRMWARE
|
|
and MACRO_ARG(reg1), MACRO_ARG(reg1), ((1 << ARC_N_IRQ_START_LEVEL) - 1)
|
|
#else
|
|
and MACRO_ARG(reg1), MACRO_ARG(reg1), 0xffff
|
|
#endif
|
|
ffs MACRO_ARG(reg2), MACRO_ARG(reg1)
|
|
fls MACRO_ARG(reg1), MACRO_ARG(reg1)
|
|
cmp MACRO_ARG(reg1), MACRO_ARG(reg2)
|
|
.endm
|
|
|
|
|
|
/* macro to get id of current cpu
|
|
* the result will be in reg (a reg)
|
|
*/
|
|
.macro _get_cpu_id, reg
|
|
LRR MACRO_ARG(reg), [_ARC_V2_IDENTITY]
|
|
xbfu MACRO_ARG(reg), MACRO_ARG(reg), 0xe8
|
|
.endm
|
|
|
|
/* macro to get the interrupt stack of current cpu
|
|
* the result will be in irq_sp (a reg)
|
|
*/
|
|
.macro _get_curr_cpu_irq_stack, irq_sp
|
|
#ifdef CONFIG_SMP
|
|
/* get pointer to _cpu_t of this CPU */
|
|
_get_cpu_id MACRO_ARG(irq_sp)
|
|
ASLR MACRO_ARG(irq_sp), MACRO_ARG(irq_sp), ARC_REGSHIFT
|
|
LDR MACRO_ARG(irq_sp), MACRO_ARG(irq_sp), _curr_cpu
|
|
/* get pointer to irq_stack itself */
|
|
LDR MACRO_ARG(irq_sp), MACRO_ARG(irq_sp), ___cpu_t_irq_stack_OFFSET
|
|
#else
|
|
MOVR MACRO_ARG(irq_sp), _kernel
|
|
LDR MACRO_ARG(irq_sp), MACRO_ARG(irq_sp), _kernel_offset_to_irq_stack
|
|
#endif
|
|
.endm
|
|
|
|
/* macro to push aux reg through reg */
|
|
.macro PUSHAX, reg, aux
|
|
LRR MACRO_ARG(reg), [MACRO_ARG(aux)]
|
|
PUSHR MACRO_ARG(reg)
|
|
.endm
|
|
|
|
/* macro to pop aux reg through reg */
|
|
.macro POPAX, reg, aux
|
|
POPR MACRO_ARG(reg)
|
|
SRR MACRO_ARG(reg), [MACRO_ARG(aux)]
|
|
.endm
|
|
|
|
|
|
/* macro to store old thread call regs */
|
|
.macro _store_old_thread_callee_regs
|
|
|
|
_save_callee_saved_regs
|
|
/* Save old thread into switch handle which is required by z_sched_switch_spin.
|
|
* NOTE: we shouldn't save anything related to old thread context after this point!
|
|
* TODO: we should add SMP write-after-write data memory barrier here, as we want all
|
|
* previous writes completed before setting switch_handle which is polled by other cores
|
|
* in z_sched_switch_spin in case of SMP. Though it's not likely that this issue
|
|
* will reproduce in real world as there is some gap before reading switch_handle and
|
|
* reading rest of the data we've stored before.
|
|
*/
|
|
STR r2, r2, ___thread_t_switch_handle_OFFSET
|
|
.endm
|
|
|
|
/* macro to store old thread call regs in interrupt*/
|
|
.macro _irq_store_old_thread_callee_regs
|
|
#if defined(CONFIG_USERSPACE)
|
|
/*
|
|
* when USERSPACE is enabled, according to ARCv2 ISA, SP will be switched
|
|
* if interrupt comes out in user mode, and will be recorded in bit 31
|
|
* (U bit) of IRQ_ACT. when interrupt exits, SP will be switched back
|
|
* according to U bit.
|
|
*
|
|
* need to remember the user/kernel status of interrupted thread, will be
|
|
* restored when thread switched back
|
|
*
|
|
*/
|
|
lr r1, [_ARC_V2_AUX_IRQ_ACT]
|
|
and r3, r1, 0x80000000
|
|
push_s r3
|
|
|
|
bclr r1, r1, 31
|
|
sr r1, [_ARC_V2_AUX_IRQ_ACT]
|
|
#endif
|
|
_store_old_thread_callee_regs
|
|
.endm
|
|
|
|
/* macro to load new thread callee regs */
|
|
.macro _load_new_thread_callee_regs
|
|
#ifdef CONFIG_ARC_STACK_CHECKING
|
|
_load_stack_check_regs
|
|
#endif
|
|
/*
|
|
* _load_callee_saved_regs expects incoming thread in r2.
|
|
* _load_callee_saved_regs restores the stack pointer.
|
|
*/
|
|
_load_callee_saved_regs
|
|
|
|
#if defined(CONFIG_MPU_STACK_GUARD) || defined(CONFIG_USERSPACE)
|
|
push_s r2
|
|
bl configure_mpu_thread
|
|
pop_s r2
|
|
#endif
|
|
/* _thread_arch.relinquish_cause is 32 bit despite of platform bittnes */
|
|
ld r3, [r2, _thread_offset_to_relinquish_cause]
|
|
.endm
|
|
|
|
|
|
/* when switch to thread caused by coop, some status regs need to set */
|
|
.macro _set_misc_regs_irq_switch_from_coop
|
|
#ifdef CONFIG_ARC_SECURE_FIRMWARE
|
|
/* must return to secure mode, so set IRM bit to 1 */
|
|
lr r0, [_ARC_V2_SEC_STAT]
|
|
bset r0, r0, _ARC_V2_SEC_STAT_IRM_BIT
|
|
sflag r0
|
|
#endif
|
|
.endm
|
|
|
|
/* when switch to thread caused by irq, some status regs need to set */
|
|
.macro _set_misc_regs_irq_switch_from_irq
|
|
#if defined(CONFIG_USERSPACE)
|
|
/*
|
|
* need to recover the user/kernel status of interrupted thread
|
|
*/
|
|
pop_s r3
|
|
lr r2, [_ARC_V2_AUX_IRQ_ACT]
|
|
or r2, r2, r3
|
|
sr r2, [_ARC_V2_AUX_IRQ_ACT]
|
|
#endif
|
|
|
|
#ifdef CONFIG_ARC_SECURE_FIRMWARE
|
|
/* here need to recover SEC_STAT.IRM bit */
|
|
pop_s r3
|
|
sflag r3
|
|
#endif
|
|
.endm
|
|
|
|
/* macro to get next switch handle in assembly */
|
|
.macro _get_next_switch_handle
|
|
PUSHR r2
|
|
MOVR r0, sp
|
|
bl z_arch_get_next_switch_handle
|
|
POPR r2
|
|
.endm
|
|
|
|
/* macro to disable stack checking in assembly, need a GPR
|
|
* to do this
|
|
*/
|
|
.macro _disable_stack_checking, reg
|
|
#ifdef CONFIG_ARC_STACK_CHECKING
|
|
#ifdef CONFIG_ARC_SECURE_FIRMWARE
|
|
lr MACRO_ARG(reg), [_ARC_V2_SEC_STAT]
|
|
bclr MACRO_ARG(reg), MACRO_ARG(reg), _ARC_V2_SEC_STAT_SSC_BIT
|
|
sflag MACRO_ARG(reg)
|
|
|
|
#else
|
|
lr MACRO_ARG(reg), [_ARC_V2_STATUS32]
|
|
bclr MACRO_ARG(reg), MACRO_ARG(reg), _ARC_V2_STATUS32_SC_BIT
|
|
kflag MACRO_ARG(reg)
|
|
#endif
|
|
#endif
|
|
.endm
|
|
|
|
/* macro to enable stack checking in assembly, need a GPR
|
|
* to do this
|
|
*/
|
|
.macro _enable_stack_checking, reg
|
|
#ifdef CONFIG_ARC_STACK_CHECKING
|
|
#ifdef CONFIG_ARC_SECURE_FIRMWARE
|
|
lr MACRO_ARG(reg), [_ARC_V2_SEC_STAT]
|
|
bset MACRO_ARG(reg), MACRO_ARG(reg), _ARC_V2_SEC_STAT_SSC_BIT
|
|
sflag MACRO_ARG(reg)
|
|
#else
|
|
lr MACRO_ARG(reg), [_ARC_V2_STATUS32]
|
|
bset MACRO_ARG(reg), MACRO_ARG(reg), _ARC_V2_STATUS32_SC_BIT
|
|
kflag MACRO_ARG(reg)
|
|
#endif
|
|
#endif
|
|
.endm
|
|
|
|
|
|
#define __arc_u9_max (255)
|
|
#define __arc_u9_min (-256)
|
|
#define __arc_ldst32_as_shift 2
|
|
|
|
/*
|
|
* When we accessing bloated struct member we can exceed u9 operand in store
|
|
* instruction. So we can use _st32_huge_offset macro instead
|
|
*/
|
|
.macro _st32_huge_offset, d, s, offset, temp
|
|
.if MACRO_ARG(offset) <= __arc_u9_max && MACRO_ARG(offset) >= __arc_u9_min
|
|
st MACRO_ARG(d), [MACRO_ARG(s), MACRO_ARG(offset)]
|
|
/* Technically we can optimize with .as both big positive and negative offsets here, but
|
|
* as we use only positive offsets in hand-written assembly code we keep only
|
|
* positive offset case here for simplicity.
|
|
*/
|
|
.elseif !(MACRO_ARG(offset) % (1 << __arc_ldst32_as_shift)) && \
|
|
MACRO_ARG(offset) <= (__arc_u9_max << __arc_ldst32_as_shift) && \
|
|
MACRO_ARG(offset) >= 0
|
|
st.as MACRO_ARG(d), [MACRO_ARG(s), MACRO_ARG(offset) >> __arc_ldst32_as_shift]
|
|
.else
|
|
ADDR MACRO_ARG(temp), MACRO_ARG(s), MACRO_ARG(offset)
|
|
st MACRO_ARG(d), [MACRO_ARG(temp)]
|
|
.endif
|
|
.endm
|
|
|
|
#endif /* _ASMLANGUAGE */
|
|
|
|
#endif /* ZEPHYR_ARCH_ARC_INCLUDE_SWAP_MACROS_H_ */
|