@@ -35,6 +35,168 @@ GTEXT(_rirq_common_interrupt_swap)
3535 * TODO: Revist this if FIRQ becomes configurable.
3636 * /
3737
38+ / *
39+
40+ ===========================================================
41+ RETURN FROM INTERRUPT TO COOPERATIVE THREAD
42+ ===========================================================
43+
44+ Th at 's a special case because:
45+ 1 . We return from IRQ handler to a cooperative thread
46+ 2 . During IRQ handling context switch did happen
47+ 3 . Returning to a thread which previously gave control
48+ to another thread because of:
49+ - Calling k_sleep()
50+ - Explicitly yielding
51+ - Bumping into locked sync primitive etc
52+
53+ Wh at ( 3 ) means is before passing control to another thread our thread
54+ in question:
55+ a. Stashed all precious caller - saved registers on its stack
56+ b. Pushed return address to the top of the stack as well
57+
58+ Th at 's how thread' s stack looks like right before jumping to another thread:
59+ ----------------------------- > 8 ---------------------------------
60+ PRE - CONTEXT - SWITCH STACK
61+
62+ lower_addr , let's say: 0x1000
63+
64+ --------------------------------------
65+ SP - > | Return address ; PC (Program Counter), in fact value taken from
66+ | BLINK register in z_arch_switch()
67+ --------------------------------------
68+ | STATUS32 value , we explicitly save it here for later usage , read - on
69+ --------------------------------------
70+ | Caller - saved registers: some of R0 - R12
71+ --------------------------------------
72+ |...
73+ |...
74+
75+ higher_addr , let's say: 0x2000
76+ ----------------------------- > 8 ---------------------------------
77+
78+ When context gets switched the kernel saves callee - saved registers in the
79+ thread 's stack right on top of pre-switch contents so that' s wh at we have:
80+ ----------------------------- > 8 ---------------------------------
81+ POST - CONTEXT - SWITCH STACK
82+
83+ lower_addr , let's say: 0x1000
84+
85+ --------------------------------------
86+ SP - > | Callee - saved registers: see struct _callee_saved_stack{}
87+ | | - R13
88+ | | - R14
89+ | | ...
90+ | \ - FP
91+ | ...
92+ --------------------------------------
93+ | Return address ; PC (Program Counter)
94+ --------------------------------------
95+ | STATUS32 value
96+ --------------------------------------
97+ | Caller - saved registers: some of R0 - R12
98+ --------------------------------------
99+ |...
100+ |...
101+
102+ higher_addr , let's say: 0x2000
103+ ----------------------------- > 8 ---------------------------------
104+
105+ So how do we return in such a complex scenario.
106+
107+ First we restore callee - saved regs with help of _load_callee_saved_regs().
108+ Now we're back to PRE - CONTEXT - SWITCH STACK (see above).
109+
110+ Logically our next step is to load return address from the top of the stack
111+ and jump to th at address to continue execution of the desired thread , but
112+ we're still in interrupt handling mode and the only way to return to normal
113+ execution mode is to execute "rtie" instruction. And here we need to deal
114+ with peculiarities of return from IRQ on ARCv2 cores.
115+
116+ Instead of simple jump to a return address stored in the tip of thread's stack
117+ (with subsequent interrupt enable) ARCv2 core additionally automatically
118+ restores some registers from stack. Most important ones are
119+ PC ( "Program Counter" ) which holds address of the next instruction to execute
120+ and STATUS32 which holds imortant flags including global interrupt enable ,
121+ zero , carry etc.
122+
123+ To make things worse depending on ARC core configuration and run - time setup
124+ of certain features different set of registers will be restored.
125+
126+ Typically those same registers are automatically saved on stack on entry to
127+ an interrupt , but remember we're returning to the thread which was
128+ not interrupted by interrupt and so on its stack there're no automatically
129+ saved registers , still inevitably on RTIE execution register restoration
130+ will happen. So if we do nothing special we'll end - up with th at :
131+ ----------------------------- > 8 ---------------------------------
132+ lower_addr , let's say: 0x1000
133+
134+ --------------------------------------
135+ # | Return address ; PC (Program Counter)
136+ | --------------------------------------
137+ | | STATUS32 value
138+ | --------------------------------------
139+ |
140+ sizeof(_irq_stack_frame)
141+ |
142+ | | Caller - saved registers: R0 - R12
143+ V --------------------------------------
144+ |...
145+ SP - > | < Some data on thread's stack>
146+ |...
147+
148+ higher_addr , let's say: 0x2000
149+ ----------------------------- > 8 ---------------------------------
150+
151+ I.e. we'll go much deeper down the stack over needed return address , read
152+ some value from unexpected location in stack and will try to jump there.
153+ Nobody knows were we end - up then.
154+
155+ To work - around th at problem we need to mimic existance of IRQ stack frame
156+ of which we really only need return address obviously to return where we
157+ need to. For th at we just shift SP so th at it points sizeof(_irq_stack_frame)
158+ above like th at :
159+ ----------------------------- > 8 ---------------------------------
160+ lower_addr , let's say: 0x1000
161+
162+ SP - > |
163+ A | < Some unrelated data >
164+ | |
165+ |
166+ sizeof(_irq_stack_frame)
167+ |
168+ | --------------------------------------
169+ | | Return address ; PC (Program Counter)
170+ | --------------------------------------
171+ # | STATUS32 value
172+ --------------------------------------
173+ | Caller - saved registers: R0 - R12
174+ --------------------------------------
175+ |...
176+ | < Some data on thread's stack>
177+ |...
178+
179+ higher_addr , let's say: 0x2000
180+ ----------------------------- > 8 ---------------------------------
181+
182+ Indeed R0 - R13 "restored" from IRQ stack frame will contain garbage but
183+ it makes no difference because we're returning to execution of code as if
184+ we're returning from yet another function call and so we will restore
185+ all needed registers from the stack.
186+
187+ One other important remark here is R13 .
188+
189+ CPU hardware automatically save/restore registers in pairs and since we
190+ wanted to save/restore R12 in IRQ stack frame as a caller - saved register we
191+ just happen to do th at for R13 as well. But given compiler treats it as
192+ a callee - saved register we save/restore it separately in _callee_saved_stack
193+ structure. And when we restore callee - saved registers from stack we among
194+ other registers recover R13 . But later on return from IRQ with RTIE
195+ instruction , R13 will be "restored" again from fake IRQ stack frame and
196+ if we don't copy correct R13 value to fake IRQ stack frame R13 value
197+ will be corrupted.
198+
199+ * /
38200
39201/ **
40202 *
@@ -207,6 +369,12 @@ _rirq_return_from_coop:
207369 bset r0 , r0 , _ARC_V2_SEC_STAT_IRM_BIT
208370 sflag r0
209371#endif
372+
373+ / *
374+ * See verbose explanation of
375+ * RETURN FROM INTERRUPT TO COOPERATIVE THREAD above
376+ * /
377+
210378 / * carve fake stack * /
211379 sub sp , sp , ___isf_t_pc_OFFSET
212380
0 commit comments