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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
|
/***************************************************************************
* Copyright (c) 2024 Microsoft Corporation
* Copyright (c) 2026-present Eclipse ThreadX contributors
*
* This program and the accompanying materials are made available under the
* terms of the MIT License which is available at
* https://opensource.org/licenses/MIT.
*
* SPDX-License-Identifier: MIT
**************************************************************************/
/**************************************************************************/
/**************************************************************************/
/** */
/** ThreadX Component */
/** */
/** Thread */
/** */
/**************************************************************************/
/**************************************************************************/
#ifdef TX_INCLUDE_USER_DEFINE_FILE
#include "tx_user.h"
#endif
.text
.align 3
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _tx_thread_context_restore ARMv8-A */
/* 6.3.0 */
/* AUTHOR */
/* */
/* William E. Lamie, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function restores the interrupt context if it is processing a */
/* nested interrupt. If not, it returns to the interrupt thread if no */
/* preemption is necessary. Otherwise, if preemption is necessary or */
/* if no thread was running, the function returns to the scheduler. */
/* */
/* INPUT */
/* */
/* None */
/* */
/* OUTPUT */
/* */
/* None */
/* */
/* CALLS */
/* */
/* _tx_thread_schedule Thread scheduling routine */
/* */
/* CALLED BY */
/* */
/* ISRs Interrupt Service Routines */
/* */
/**************************************************************************/
// VOID _tx_thread_context_restore(VOID)
// {
.global _tx_thread_context_restore
.type _tx_thread_context_restore, @function
_tx_thread_context_restore:
/* Lockout interrupts. */
MSR DAIFSet, 0x3 // Lockout interrupts
#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE))
/* Call the ISR exit function to indicate an ISR is complete. */
BL _tx_execution_isr_exit // Call the ISR exit function
#endif
/* Determine if interrupts are nested. */
// if (--_tx_thread_system_state)
// {
LDR x3, =_tx_thread_system_state // Pickup address of system state var
LDR w2, [x3, #0] // Pickup system state
SUB w2, w2, #1 // Decrement the counter
STR w2, [x3, #0] // Store the counter
CMP w2, #0 // Was this the first interrupt?
BEQ __tx_thread_not_nested_restore // If so, not a nested restore
/* Interrupts are nested. */
/* Just recover the saved registers and return to the point of
interrupt. */
LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL
#ifdef EL1
MSR SPSR_EL1, x4 // Setup SPSR for return
MSR ELR_EL1, x5 // Setup point of interrupt
#else
#ifdef EL2
MSR SPSR_EL2, x4 // Setup SPSR for return
MSR ELR_EL2, x5 // Setup point of interrupt
#else
MSR SPSR_EL3, x4 // Setup SPSR for return
MSR ELR_EL3, x5 // Setup point of interrupt
#endif
#endif
LDP x18, x19, [sp], #16 // Recover x18, x19
LDP x16, x17, [sp], #16 // Recover x16, x17
LDP x14, x15, [sp], #16 // Recover x14, x15
LDP x12, x13, [sp], #16 // Recover x12, x13
LDP x10, x11, [sp], #16 // Recover x10, x11
LDP x8, x9, [sp], #16 // Recover x8, x9
LDP x6, x7, [sp], #16 // Recover x6, x7
LDP x4, x5, [sp], #16 // Recover x4, x5
LDP x2, x3, [sp], #16 // Recover x2, x3
LDP x0, x1, [sp], #16 // Recover x0, x1
LDP x29, x30, [sp], #16 // Recover x29, x30
ERET // Return to point of interrupt
// }
__tx_thread_not_nested_restore:
/* Determine if a thread was interrupted and no preemption is required. */
// else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr)
// || (_tx_thread_preempt_disable))
// {
LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr
LDR x0, [x1, #0] // Pickup actual current thread pointer
CMP x0, #0 // Is it NULL?
BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted
LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address
LDR w2, [x3, #0] // Pickup actual preempt disable flag
CMP w2, #0 // Is it set?
BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread
LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr
LDR x2, [x3, #0] // Pickup actual execute thread pointer
CMP x0, x2 // Is the same thread highest priority?
BNE __tx_thread_preempt_restore // No, preemption needs to happen
__tx_thread_no_preempt_restore:
/* Restore interrupted thread or ISR. */
/* Pickup the saved stack pointer. */
// sp = _tx_thread_current_ptr -> tx_thread_stack_ptr;
LDR x4, [x0, #8] // Switch to thread stack pointer
MOV sp, x4 //
/* Recover the saved context and return to the point of interrupt. */
LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1
#ifdef EL1
MSR SPSR_EL1, x4 // Setup SPSR for return
MSR ELR_EL1, x5 // Setup point of interrupt
#else
#ifdef EL2
MSR SPSR_EL2, x4 // Setup SPSR for return
MSR ELR_EL2, x5 // Setup point of interrupt
#else
MSR SPSR_EL3, x4 // Setup SPSR for return
MSR ELR_EL3, x5 // Setup point of interrupt
#endif
#endif
LDP x18, x19, [sp], #16 // Recover x18, x19
LDP x16, x17, [sp], #16 // Recover x16, x17
LDP x14, x15, [sp], #16 // Recover x14, x15
LDP x12, x13, [sp], #16 // Recover x12, x13
LDP x10, x11, [sp], #16 // Recover x10, x11
LDP x8, x9, [sp], #16 // Recover x8, x9
LDP x6, x7, [sp], #16 // Recover x6, x7
LDP x4, x5, [sp], #16 // Recover x4, x5
LDP x2, x3, [sp], #16 // Recover x2, x3
LDP x0, x1, [sp], #16 // Recover x0, x1
LDP x29, x30, [sp], #16 // Recover x29, x30
ERET // Return to point of interrupt
// }
// else
// {
__tx_thread_preempt_restore:
LDR x4, [x0, #8] // Switch to thread stack pointer
MOV sp, x4 //
LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1
STP x20, x21, [sp, #-16]! // Save x20, x21
STP x22, x23, [sp, #-16]! // Save x22, x23
STP x24, x25, [sp, #-16]! // Save x24, x25
STP x26, x27, [sp, #-16]! // Save x26, x27
STP x28, x29, [sp, #-16]! // Save x28, x29
#ifdef ENABLE_ARM_FP
LDR w3, [x0, #248] // Pickup FP enable flag
CMP w3, #0 // Is FP enabled?
BEQ _skip_fp_save // No, skip FP save
STP q0, q1, [sp, #-32]! // Save q0, q1
STP q2, q3, [sp, #-32]! // Save q2, q3
STP q4, q5, [sp, #-32]! // Save q4, q5
STP q6, q7, [sp, #-32]! // Save q6, q7
STP q8, q9, [sp, #-32]! // Save q8, q9
STP q10, q11, [sp, #-32]! // Save q10, q11
STP q12, q13, [sp, #-32]! // Save q12, q13
STP q14, q15, [sp, #-32]! // Save q14, q15
STP q16, q17, [sp, #-32]! // Save q16, q17
STP q18, q19, [sp, #-32]! // Save q18, q19
STP q20, q21, [sp, #-32]! // Save q20, q21
STP q22, q23, [sp, #-32]! // Save q22, q23
STP q24, q25, [sp, #-32]! // Save q24, q25
STP q26, q27, [sp, #-32]! // Save q26, q27
STP q28, q29, [sp, #-32]! // Save q28, q29
STP q30, q31, [sp, #-32]! // Save q30, q31
MRS x2, FPSR // Pickup FPSR
MRS x3, FPCR // Pickup FPCR
STP x2, x3, [sp, #-16]! // Save FPSR, FPCR
_skip_fp_save:
#endif
STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3)
MOV x3, sp // Move sp into x3
STR x3, [x0, #8] // Save stack pointer in thread control
// block
LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack
LDR x4, [x3, #0] // Pickup system stack pointer
MOV sp, x4 // Setup system stack pointer
/* Save the remaining time-slice and disable it. */
// if (_tx_timer_time_slice)
// {
LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address
LDR w2, [x3, #0] // Pickup time-slice
CMP w2, #0 // Is it active?
BEQ __tx_thread_dont_save_ts // No, don't save it
// _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice;
// _tx_timer_time_slice = 0;
STR w2, [x0, #36] // Save thread's time-slice
MOV w2, #0 // Clear value
STR w2, [x3, #0] // Disable global time-slice flag
// }
__tx_thread_dont_save_ts:
/* Clear the current task pointer. */
/* _tx_thread_current_ptr = TX_NULL; */
MOV x0, #0 // NULL value
STR x0, [x1, #0] // Clear current thread pointer
/* Return to the scheduler. */
// _tx_thread_schedule();
// }
__tx_thread_idle_system_restore:
/* Just return back to the scheduler! */
LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule
#ifdef EL1
MSR ELR_EL1, x1 // Setup point of interrupt
// MOV x1, #0x4 // Setup EL1 return
// MSR spsr_el1, x1 // Move into SPSR
#else
#ifdef EL2
MSR ELR_EL2, x1 // Setup point of interrupt
// MOV x1, #0x8 // Setup EL2 return
// MSR spsr_el2, x1 // Move into SPSR
#else
MSR ELR_EL3, x1 // Setup point of interrupt
// MOV x1, #0xC // Setup EL3 return
// MSR spsr_el3, x1 // Move into SPSR
#endif
#endif
ERET // Return to scheduler
// }
|