arch : arc: clean up of assembly codes

* update comments to match latest codes
* add extra comments for some assembly, macros
* use macro to replace duplcated codes
* remove unused codes, lables, symobols

Signed-off-by: Wayne Ren <wei.ren@synopsys.com>
This commit is contained in:
Wayne Ren 2020-02-29 22:58:22 +08:00 committed by Anas Nashif
commit 84cdfa271d
6 changed files with 196 additions and 213 deletions

View file

@ -19,7 +19,6 @@
#include <syscall.h>
GTEXT(_Fault)
GTEXT(z_do_kernel_oops)
GTEXT(__reset)
GTEXT(__memory_error)
GTEXT(__instruction_error)
@ -38,6 +37,17 @@ GTEXT(__ev_maligned)
GTEXT(z_irq_do_offload);
#endif
.macro _save_exc_regs_into_stack
#ifdef CONFIG_ARC_HAS_SECURE
/* ERSEC_STAT is IOW/RAZ in normal mode */
lr r0,[_ARC_V2_ERSEC_STAT]
st_s r0, [sp, ___isf_t_sec_stat_OFFSET]
#endif
lr r0,[_ARC_V2_ERET]
st_s r0, [sp, ___isf_t_pc_OFFSET]
lr r0,[_ARC_V2_ERSTATUS]
st_s r0, [sp, ___isf_t_status32_OFFSET]
.endm
/*
* The exception handling will use top part of interrupt stack to
@ -89,15 +99,7 @@ _exc_entry:
*/
_create_irq_stack_frame
#ifdef CONFIG_ARC_HAS_SECURE
/* ERSEC_STAT is IOW/RAZ in normal mode */
lr r0,[_ARC_V2_ERSEC_STAT]
st_s r0, [sp, ___isf_t_sec_stat_OFFSET]
#endif
lr r0,[_ARC_V2_ERSTATUS]
st_s r0, [sp, ___isf_t_status32_OFFSET]
lr r0,[_ARC_V2_ERET]
st_s r0, [sp, ___isf_t_pc_OFFSET] /* eret into pc */
_save_exc_regs_into_stack
/* sp is parameter of _Fault */
mov_s r0, sp
@ -174,10 +176,13 @@ _exc_return:
mov r2, ilink
#endif
/* Assumption: r2 has next thread */
/* Assumption: r2 has next thread */
b _rirq_newthread_switch
_exc_return_from_exc:
/* exception handler may change return address.
* reload it
*/
ld_s r0, [sp, ___isf_t_pc_OFFSET]
sr r0, [_ARC_V2_ERET]
@ -185,7 +190,7 @@ _exc_return_from_exc:
mov_s sp, ilink
rtie
/* separated entry for trap which may be used by irq_offload, USERPSACE */
SECTION_SUBSEC_FUNC(TEXT,__fault,__ev_trap)
/* get the id of trap_s */
lr ilink, [_ARC_V2_ECR]
@ -208,15 +213,12 @@ valid_syscall_id:
*/
_create_irq_stack_frame
#ifdef CONFIG_ARC_SECURE_FIRMWARE
/* ERSEC_STAT is IOW/RAZ in normal mode */
lr r0, [_ARC_V2_ERSEC_STAT]
st_s r0, [sp, ___isf_t_sec_stat_OFFSET]
#endif
lr r0,[_ARC_V2_ERET]
st_s r0, [sp, ___isf_t_pc_OFFSET] /* eret into pc */
lr r0,[_ARC_V2_ERSTATUS]
st_s r0, [sp, ___isf_t_status32_OFFSET]
_save_exc_regs_into_stack
/* exc return and do sys call in kernel mode,
* so need to clear U bit, r0 is already loaded
* with ERSTATUS in _save_exc_regs_into_stack
*/
bclr r0, r0, _ARC_V2_STATUS32_U_BIT
sr r0, [_ARC_V2_ERSTATUS]
@ -239,15 +241,7 @@ _do_non_syscall_trap:
/* save caller saved registers */
_create_irq_stack_frame
#ifdef CONFIG_ARC_HAS_SECURE
lr r0,[_ARC_V2_ERSEC_STAT]
st_s r0, [sp, ___isf_t_sec_stat_OFFSET]
#endif
lr r0,[_ARC_V2_ERSTATUS]
st_s r0, [sp, ___isf_t_status32_OFFSET]
lr r0,[_ARC_V2_ERET]
st_s r0, [sp, ___isf_t_pc_OFFSET] /* eret into pc */
_save_exc_regs_into_stack
/* check whether irq stack is used */
_check_and_inc_int_nest_counter r0, r1
@ -266,6 +260,8 @@ exc_nest_handle:
_dec_int_nest_counter r0, r1
_pop_irq_stack_frame
/* ERSTATUS, ERET are not changed, so ok to rtie */
rtie
#endif /* CONFIG_IRQ_OFFLOAD */
b _exc_entry

View file

@ -35,37 +35,35 @@ _rirq_enter/_firq_enter: they are jump points.
The flow is the following:
ISR -> _isr_wrapper -- + -> _rirq_enter -> _isr_demux -> ISR -> _rirq_exit
|
+ -> _firq_enter -> _isr_demux -> ISR -> _firq_exit
|
+ -> _firq_enter -> _isr_demux -> ISR -> _firq_exit
Context switch explanation:
The context switch code is spread in these files:
isr_wrapper.s, switch.s, swap_macros.s, fast_irq.s, regular_irq.s
isr_wrapper.s, switch.s, swap_macros.h, fast_irq.s, regular_irq.s
IRQ stack frame layout:
high address
high address
status32
pc
lp_count
lp_start
lp_end
blink
r13
...
sp -> r0
status32
pc
lp_count
lp_start
lp_end
blink
r13
...
sp -> r0
low address
low address
The context switch code adopts this standard so that it is easier to follow:
- r1 contains _kernel ASAP and is not overwritten over the lifespan of
the functions.
- r2 contains _kernel.current ASAP, and the incoming thread when we
transition from outgoing thread to incoming thread
- r2 contains _kernel.current ASAP, and the incoming thread when we
transition from outgoing thread to incoming thread
Not loading _kernel into r0 allows loading _kernel without stomping on
the parameter in r0 in arch_switch().
@ -100,47 +98,49 @@ done upfront, and the rest is done when needed:
o RIRQ
All needed registers to run C code in the ISR are saved automatically
on the outgoing thread's stack: loop, status32, pc, and the caller-
saved GPRs. That stack frame layout is pre-determined. If returning
to a thread, the stack is popped and no registers have to be saved by
the kernel. If a context switch is required, the callee-saved GPRs
are then saved in the thread control structure (TCS).
All needed registers to run C code in the ISR are saved automatically
on the outgoing thread's stack: loop, status32, pc, and the caller-
saved GPRs. That stack frame layout is pre-determined. If returning
to a thread, the stack is popped and no registers have to be saved by
the kernel. If a context switch is required, the callee-saved GPRs
are then saved in the thread's stack.
o FIRQ
First, a FIRQ can be interrupting a lower-priority RIRQ: if this is the case,
the FIRQ does not take a scheduling decision and leaves it the RIRQ to
handle. This limits the amount of code that has to run at interrupt-level.
First, a FIRQ can be interrupting a lower-priority RIRQ: if this is
the case, the FIRQ does not take a scheduling decision and leaves it
the RIRQ to handle. This limits the amount of code that has to run at
interrupt-level.
CONFIG_RGF_NUM_BANKS==1 case:
Registers are saved on the stack frame just as they are for RIRQ.
Context switch can happen just as it does in the RIRQ case, however,
if the FIRQ interrupted a RIRQ, the FIRQ will return from interrupt and
let the RIRQ do the context switch. At entry, one register is needed in order
to have code to save other registers. r0 is saved first in a global called
saved_r0.
CONFIG_RGF_NUM_BANKS==1 case:
Registers are saved on the stack frame just as they are for RIRQ.
Context switch can happen just as it does in the RIRQ case, however,
if the FIRQ interrupted a RIRQ, the FIRQ will return from interrupt
and let the RIRQ do the context switch. At entry, one register is
needed in order to have code to save other registers. r0 is saved
first in the stack and restored later
CONFIG_RGF_NUM_BANKS!=1 case:
During early initialization, the sp in the 2nd register bank is made to
refer to _firq_stack. This allows for the FIRQ handler to use its own stack.
GPRs are banked, loop registers are saved in unused callee saved regs upon
interrupt entry. If returning to a thread, loop registers are restored and the
CPU switches back to bank 0 for the GPRs. If a context switch is
needed, at this point only are all the registers saved. First, a
stack frame with the same layout as the automatic RIRQ one is created
and then the callee-saved GPRs are saved in the TCS. status32_p0 and
ilink are saved in this case, not status32 and pc.
To create the stack frame, the FIRQ handling code must first go back to using
bank0 of registers, since that is where the registers containing the exiting
thread are saved. Care must be taken not to touch any register before saving
them: the only one usable at that point is the stack pointer.
CONFIG_RGF_NUM_BANKS!=1 case:
During early initialization, the sp in the 2nd register bank is made to
refer to _firq_stack. This allows for the FIRQ handler to use its own
stack. GPRs are banked, loop registers are saved in unused callee saved
regs upon interrupt entry. If returning to a thread, loop registers are
restored and the CPU switches back to bank 0 for the GPRs. If a context
switch is needed, at this point only are all the registers saved.
First, a stack frame with the same layout as the automatic RIRQ one is
created and then the callee-saved GPRs are saved in the stack.
status32_p0 and ilink are saved in this case, not status32 and pc.
To create the stack frame, the FIRQ handling code must first go back to
using bank0 of registers, since that is where the registers containing
the exiting thread are saved. Care must be taken not to touch any
register before saving them: the only one usable at that point is the
stack pointer.
o coop
When a coop context switch is done, the callee-saved registers are
saved in the TCS. The other GPRs do not need to be saved, since the
compiler has already placed them on the stack.
When a coop context switch is done, the callee-saved registers are
saved in the stack. The other GPRs do not need to be saved, since the
compiler has already placed them on the stack.
For restoring the contexts, there are six cases. In all cases, the
callee-saved registers of the incoming thread have to be restored. Then, there
@ -148,57 +148,56 @@ are specifics for each case:
From coop:
o to coop
o to coop
Do a normal function call return.
Do a normal function call return.
o to any irq
o to any irq
The incoming interrupted thread has an IRQ stack frame containing the
caller-saved registers that has to be popped. status32 has to be restored,
then we jump to the interrupted instruction.
The incoming interrupted thread has an IRQ stack frame containing the
caller-saved registers that has to be popped. status32 has to be
restored, then we jump to the interrupted instruction.
From FIRQ:
When CONFIG_RGF_NUM_BANKS==1, context switch is done as it is for RIRQ.
When CONFIG_RGF_NUM_BANKS!=1, the processor is put back to using bank0,
not bank1 anymore, because it had to save the outgoing context from bank0,
and now has to load the incoming one
into bank0.
When CONFIG_RGF_NUM_BANKS==1, context switch is done as it is for RIRQ.
When CONFIG_RGF_NUM_BANKS!=1, the processor is put back to using bank0,
not bank1 anymore, because it had to save the outgoing context from
bank0, and now has to load the incoming one into bank0.
o to coop
o to coop
The address of the returning instruction from arch_switch() is loaded
in ilink and the saved status32 in status32_p0.
The address of the returning instruction from arch_switch() is loaded
in ilink and the saved status32 in status32_p0.
o to any irq
o to any irq
The IRQ has saved the caller-saved registers in a stack frame, which must be
popped, and status32 and pc loaded in status32_p0 and ilink.
The IRQ has saved the caller-saved registers in a stack frame, which
must be popped, and status32 and pc loaded in status32_p0 and ilink.
From RIRQ:
o to coop
o to coop
The interrupt return mechanism in the processor expects a stack frame, but
the outgoing context did not create one. A fake one is created here, with
only the relevant values filled in: pc, status32.
The interrupt return mechanism in the processor expects a stack frame,
but the outgoing context did not create one. A fake one is created
here, with only the relevant values filled in: pc, status32.
There is a discrepancy between the ABI from the ARCv2 docs, including the
way the processor pushes GPRs in pairs in the IRQ stack frame, and the ABI
GCC uses. r13 should be a callee-saved register, but GCC treats it as
caller-saved. This means that the processor pushes it in the stack frame
along with r12, but the compiler does not save it before entering a
function. So, it is saved as part of the callee-saved registers, and
restored there, but the processor restores it _a second time_ when popping
the IRQ stack frame. Thus, the correct value must also be put in the fake
stack frame when returning to a thread that context switched out
cooperatively.
There is a discrepancy between the ABI from the ARCv2 docs,
including the way the processor pushes GPRs in pairs in the IRQ stack
frame, and the ABI GCC uses. r13 should be a callee-saved register,
but GCC treats it as caller-saved. This means that the processor pushes
it in the stack frame along with r12, but the compiler does not save it
before entering a function. So, it is saved as part of the callee-saved
registers, and restored there, but the processor restores it _a second
time_ when popping the IRQ stack frame. Thus, the correct value must
also be put in the fake stack frame when returning to a thread that
context switched out cooperatively.
o to any irq
o to any irq
Both types of IRQs already have an IRQ stack frame: simply return from
interrupt.
Both types of IRQs already have an IRQ stack frame: simply return from
interrupt.
*/
SECTION_FUNC(TEXT, _isr_wrapper)
@ -251,8 +250,6 @@ GTEXT(sys_trace_isr_enter)
#if defined(CONFIG_SYS_POWER_MANAGEMENT)
.macro exit_tickless_idle
clri r0 /* do not interrupt exiting tickless idle operations */
push_s r1
push_s r0
mov_s r1, _kernel
ld_s r0, [r1, _kernel_offset_to_idle] /* requested idle duration */
breq r0, 0, _skip_sys_power_save_idle_exit
@ -263,8 +260,6 @@ GTEXT(sys_trace_isr_enter)
pop_s blink
_skip_sys_power_save_idle_exit:
pop_s r0
pop_s r1
seti r0
.endm
#else

View file

@ -25,17 +25,6 @@ GTEXT(_rirq_enter)
GTEXT(_rirq_exit)
GTEXT(_rirq_newthread_switch)
#if 0 /* TODO: when FIRQ is not present, all would be regular */
#define NUM_REGULAR_IRQ_PRIO_LEVELS CONFIG_NUM_IRQ_PRIO_LEVELS
#else
#define NUM_REGULAR_IRQ_PRIO_LEVELS (CONFIG_NUM_IRQ_PRIO_LEVELS-1)
#endif
/* note: the above define assumes that prio 0 IRQ is for FIRQ, and
* that all others are regular interrupts.
* TODO: Revist this if FIRQ becomes configurable.
*/
/*
===========================================================
@ -214,23 +203,16 @@ will be corrupted.
SECTION_FUNC(TEXT, _rirq_enter)
#ifdef CONFIG_ARC_STACK_CHECKING
#ifdef CONFIG_ARC_SECURE_FIRMWARE
lr r2, [_ARC_V2_SEC_STAT]
bclr r2, r2, _ARC_V2_SEC_STAT_SSC_BIT
sflag r2
#else
/* disable stack checking */
lr r2, [_ARC_V2_STATUS32]
bclr r2, r2, _ARC_V2_STATUS32_SC_BIT
kflag r2
#endif
#endif
/* the ISR will be handled in separate interrupt stack,
* so stack checking must be diabled, or exception will
* be caused
*/
_disable_stack_checking r2
clri
/* check whether irq stack is used */
/* check whether irq stack is used, if
* not switch to isr stack
*/
_check_and_inc_int_nest_counter r0, r1
bne.d rirq_nest
@ -302,6 +284,9 @@ _rirq_newthread_switch:
.balign 4
_rirq_switch_from_coop:
/* for a cooperative switch, it's not in irq, so
* need to set some regs for irq return
*/
_set_misc_regs_irq_switch_from_coop
/*

View file

@ -96,18 +96,10 @@ SECTION_FUNC(TEXT, arch_switch)
_store_old_thread_callee_regs
#ifdef CONFIG_ARC_STACK_CHECKING
/* disable stack checking here, as sp will be changed to target
* thread'sp
*/
#if defined(CONFIG_ARC_HAS_SECURE) && defined(CONFIG_ARC_SECURE_FIRMWARE)
bclr r3, r3, _ARC_V2_SEC_STAT_SSC_BIT
sflag r3
#else
bclr r3, r3, _ARC_V2_STATUS32_SC_BIT
kflag r3
#endif
#endif
_disable_stack_checking r3
mov_s r2, r0
@ -134,9 +126,12 @@ _switch_return_from_coop:
kflag r3 /* write status32 */
#ifdef CONFIG_EXECUTION_BENCHMARKING
b _capture_value_for_benchmarking
#endif
return_loc:
push_s blink
bl read_timer_end_of_swap
pop_s blink
#endif /* CONFIG_EXECUTION_BENCHMARKING */
j_s [blink]
@ -167,14 +162,3 @@ _switch_return_from_firq:
sr r3, [_ARC_V2_AUX_IRQ_ACT]
#endif
rtie
#ifdef CONFIG_EXECUTION_BENCHMARKING
.balign 4
_capture_value_for_benchmarking:
push_s blink
bl read_timer_end_of_swap
pop_s blink
b return_loc
#endif /* CONFIG_EXECUTION_BENCHMARKING */

View file

@ -93,22 +93,13 @@ SECTION_FUNC(TEXT, z_arc_userspace_enter)
/*
* In ARCv2, the U bit can only be set through exception return
*/
#ifdef CONFIG_ARC_STACK_CHECKING
/* disable stack checking as the stack should be initialized */
#ifdef CONFIG_ARC_SECURE_FIRMWARE
lr blink, [_ARC_V2_SEC_STAT]
bclr blink, blink, _ARC_V2_SEC_STAT_SSC_BIT
sflag blink
#else
lr blink, [_ARC_V2_STATUS32]
bclr blink, blink, _ARC_V2_STATUS32_SC_BIT
kflag blink
#endif
#endif
_disable_stack_checking blink
/* the end of user stack in r5 */
add r5, r4, r5
/* start of privilege stack */
add blink, r5, CONFIG_PRIVILEGED_STACK_SIZE+STACK_GUARD_SIZE
add blink, r5, CONFIG_PRIVILEGED_STACK_SIZE + STACK_GUARD_SIZE
mov_s sp, r5
push_s r0
@ -118,6 +109,9 @@ SECTION_FUNC(TEXT, z_arc_userspace_enter)
mov r5, sp /* skip r0, r1, r2, r3 */
/* to avoid the leakage of kernel info, the thread stack needs to be
* re-initialized
*/
#ifdef CONFIG_INIT_STACKS
mov_s r0, 0xaaaaaaaa
#else
@ -128,23 +122,22 @@ _clear_user_stack:
cmp r4, r5
jlt _clear_user_stack
/* reload the stack checking regs as the original kernel stack
* becomess user stack
*/
#ifdef CONFIG_ARC_STACK_CHECKING
mov_s r1, _kernel
ld_s r2, [r1, _kernel_offset_to_current]
_load_stack_check_regs
#ifdef CONFIG_ARC_SECURE_FIRMWARE
lr r0, [_ARC_V2_SEC_STAT]
bset r0, r0, _ARC_V2_SEC_STAT_SSC_BIT
sflag r0
#else
lr r0, [_ARC_V2_STATUS32]
bset r0, r0, _ARC_V2_STATUS32_SC_BIT
kflag r0
#endif
_enable_stack_checking r0
#endif
/* the following codes are used to switch from kernel mode
* to user mode by fake exception, because U bit can only be set
* by exception
*/
_arc_go_to_user_space:
lr r0, [_ARC_V2_STATUS32]
bset r0, r0, _ARC_V2_STATUS32_U_BIT
@ -185,9 +178,8 @@ _arc_go_to_user_space:
mov_s blink, 0
#ifdef CONFIG_EXECUTION_BENCHMARKING
b _capture_value_for_benchmarking_userspace
return_loc_userspace_enter:
#endif /* CONFIG_EXECUTION_BENCHMARKING */
bl read_timer_end_of_userspace_enter
#endif
rtie
@ -298,20 +290,3 @@ inc_len:
/* increment length measurement, loop again */
add_s r0, r0, 1
b_s strlen_loop
#ifdef CONFIG_EXECUTION_BENCHMARKING
.balign 4
_capture_value_for_benchmarking_userspace:
mov r1, _kernel
ld_s r2, [r1, _kernel_offset_to_current]
_save_callee_saved_regs
push_s blink
bl read_timer_end_of_userspace_enter
pop_s blink
mov r1, _kernel
ld_s r2, [r1, _kernel_offset_to_current]
_load_callee_saved_regs
b return_loc_userspace_enter
#endif

View file

@ -16,7 +16,7 @@
#ifdef _ASMLANGUAGE
/* entering this macro, current is in r2 */
/* save callee regs of current thread in r2 */
.macro _save_callee_saved_regs
sub_s sp, sp, ___callee_saved_stack_t_SIZEOF
@ -89,7 +89,7 @@
st sp, [r2, _thread_offset_to_sp]
.endm
/* entering this macro, current is in r2 */
/* load the callee regs of thread (in r2)*/
.macro _load_callee_saved_regs
/* restore stack pointer from struct k_thread */
ld sp, [r2, _thread_offset_to_sp]
@ -162,6 +162,7 @@
.endm
/* discard callee regs */
.macro _discard_callee_saved_regs
add_s sp, sp, ___callee_saved_stack_t_SIZEOF
.endm
@ -265,7 +266,7 @@
.endm
/*
* To use this macor, r2 should have the value of thread struct pointer to
* 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
@ -297,6 +298,7 @@
/* 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
@ -316,7 +318,10 @@
cmp \reg2, 1
.endm
/* decrease interrupt nest counter */
/* 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_cpu_id \reg1
@ -336,6 +341,7 @@
/* 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 \reg1, [_ARC_V2_AUX_IRQ_ACT]
@ -349,11 +355,18 @@
cmp \reg1, \reg2
.endm
/* macro to get id of current cpu
* the result will be in reg (a reg)
*/
.macro _get_cpu_id reg
lr \reg, [_ARC_V2_IDENTITY]
xbfu \reg, \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_cpu_id \irq_sp
@ -478,6 +491,41 @@
pop_s 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 \reg, [_ARC_V2_SEC_STAT]
bclr \reg, \reg, _ARC_V2_SEC_STAT_SSC_BIT
sflag \reg
#else
lr \reg, [_ARC_V2_STATUS32]
bclr \reg, \reg, _ARC_V2_STATUS32_SC_BIT
kflag \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 \reg, [_ARC_V2_SEC_STAT]
bset \reg, \reg, _ARC_V2_SEC_STAT_SSC_BIT
sflag \reg
#else
lr \reg, [_ARC_V2_STATUS32]
bset \reg, \reg, _ARC_V2_STATUS32_SC_BIT
kflag \reg
#endif
#endif
.endm
#endif /* _ASMLANGUAGE */
#endif /* ZEPHYR_ARCH_ARC_INCLUDE_SWAP_MACROS_H_ */