1 /*
2 * Copyright (c) 2016 Wind River Systems, Inc.
3 * Copyright (c) 2016 Cadence Design Systems, Inc.
4 * Copyright (c) 2020 Intel Corporation
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 /* this file is only meant to be included by kernel_structs.h */
9
10 #ifndef ZEPHYR_ARCH_XTENSA_INCLUDE_KERNEL_ARCH_FUNC_H_
11 #define ZEPHYR_ARCH_XTENSA_INCLUDE_KERNEL_ARCH_FUNC_H_
12
13 #ifndef _ASMLANGUAGE
14 #include <kernel_internal.h>
15 #include <string.h>
16 #include <zephyr/cache.h>
17 #include <zephyr/platform/hooks.h>
18 #include <zephyr/zsr.h>
19
20 #ifdef __cplusplus
21 extern "C" {
22 #endif
23
24 K_KERNEL_STACK_ARRAY_DECLARE(z_interrupt_stacks, CONFIG_MP_MAX_NUM_CPUS,
25 CONFIG_ISR_STACK_SIZE);
26
arch_kernel_init(void)27 static ALWAYS_INLINE void arch_kernel_init(void)
28 {
29 #ifdef CONFIG_SOC_PER_CORE_INIT_HOOK
30 soc_per_core_init_hook();
31 #endif /* CONFIG_SOC_PER_CORE_INIT_HOOK */
32 }
33
34 void xtensa_switch(void *switch_to, void **switched_from);
35
arch_switch(void * switch_to,void ** switched_from)36 static ALWAYS_INLINE void arch_switch(void *switch_to, void **switched_from)
37 {
38 xtensa_switch(switch_to, switched_from);
39 }
40
41 #ifdef CONFIG_KERNEL_COHERENCE
42 /**
43 * @brief Invalidate cache between two stack addresses.
44 *
45 * This invalidates the cache lines between two stack addresses,
46 * beginning with the cache line including the start address, up to
47 * but not including the cache line containing the end address.
48 * Not invalidating the last cache line is due to the usage in
49 * arch_cohere_stacks() where it invalidates the unused portion of
50 * stack. If the stack pointer happens to be in the middle of
51 * a cache line, the cache line containing the stack pointer
52 * address will be flushed, and then immediately invalidated.
53 * If we are swapping back into the same thread (e.g. after
54 * handling interrupt), that cache line, being invalidated, needs
55 * to be retrieved from main memory. This creates unnecessary
56 * data move between main memory and cache.
57 *
58 * @param s_addr Starting address of memory region to have cache invalidated.
59 * @param e_addr Ending address of memory region to have cache invalidated.
60 */
xtensa_cohere_stacks_cache_invd(size_t s_addr,size_t e_addr)61 static ALWAYS_INLINE void xtensa_cohere_stacks_cache_invd(size_t s_addr, size_t e_addr)
62 {
63 const size_t first = ROUND_DOWN(s_addr, XCHAL_DCACHE_LINESIZE);
64 const size_t last = ROUND_DOWN(e_addr, XCHAL_DCACHE_LINESIZE);
65 size_t line;
66
67 for (line = first; line < last; line += XCHAL_DCACHE_LINESIZE) {
68 __asm__ volatile("dhi %0, 0" :: "r"(line));
69 }
70 }
71
72 /**
73 * @brief Flush cache between two stack addresses.
74 *
75 * This flushes the cache lines between two stack addresses,
76 * beginning with the cache line including the start address,
77 * and ending with the cache line including the end address.
78 * Note that, contrary to xtensa_cohere_stacks_cache_invd(),
79 * the last cache line will be flushed instead of being
80 * ignored.
81 *
82 * @param s_addr Starting address of memory region to have cache invalidated.
83 * @param e_addr Ending address of memory region to have cache invalidated.
84 */
xtensa_cohere_stacks_cache_flush(size_t s_addr,size_t e_addr)85 static ALWAYS_INLINE void xtensa_cohere_stacks_cache_flush(size_t s_addr, size_t e_addr)
86 {
87 const size_t first = ROUND_DOWN(s_addr, XCHAL_DCACHE_LINESIZE);
88 const size_t last = ROUND_UP(e_addr, XCHAL_DCACHE_LINESIZE);
89 size_t line;
90
91 for (line = first; line < last; line += XCHAL_DCACHE_LINESIZE) {
92 __asm__ volatile("dhwb %0, 0" :: "r"(line));
93 }
94 }
95
96 /**
97 * @brief Flush and invalidate cache between two stack addresses.
98 *
99 * This flushes the cache lines between two stack addresses,
100 * beginning with the cache line including the start address,
101 * and ending with the cache line including the end address.
102 * Note that, contrary to xtensa_cohere_stacks_cache_invd(),
103 * the last cache line will be flushed and invalidated instead
104 * of being ignored.
105 *
106 * @param s_addr Starting address of memory region to have cache manipulated.
107 * @param e_addr Ending address of memory region to have cache manipulated.
108 */
xtensa_cohere_stacks_cache_flush_invd(size_t s_addr,size_t e_addr)109 static ALWAYS_INLINE void xtensa_cohere_stacks_cache_flush_invd(size_t s_addr, size_t e_addr)
110 {
111 const size_t first = ROUND_DOWN(s_addr, XCHAL_DCACHE_LINESIZE);
112 const size_t last = ROUND_UP(e_addr, XCHAL_DCACHE_LINESIZE);
113 size_t line;
114
115 for (line = first; line < last; line += XCHAL_DCACHE_LINESIZE) {
116 __asm__ volatile("dhwbi %0, 0" :: "r"(line));
117 }
118 }
119
arch_cohere_stacks(struct k_thread * old_thread,void * old_switch_handle,struct k_thread * new_thread)120 static ALWAYS_INLINE void arch_cohere_stacks(struct k_thread *old_thread,
121 void *old_switch_handle,
122 struct k_thread *new_thread)
123 {
124 #ifdef CONFIG_SCHED_CPU_MASK_PIN_ONLY
125 ARG_UNUSED(old_thread);
126 ARG_UNUSED(old_switch_handle);
127 ARG_UNUSED(new_thread);
128
129 /* This kconfig option ensures that a living thread will never
130 * be executed in a different CPU so we can safely return without
131 * invalidate and/or flush threads cache.
132 */
133 return;
134 #endif /* CONFIG_SCHED_CPU_MASK_PIN_ONLY */
135
136 #if !defined(CONFIG_SCHED_CPU_MASK_PIN_ONLY)
137 int32_t curr_cpu = _current_cpu->id;
138
139 size_t ostack = old_thread->stack_info.start;
140 size_t oend = ostack + old_thread->stack_info.size;
141 size_t osp = (size_t) old_switch_handle;
142
143 size_t nstack = new_thread->stack_info.start;
144 size_t nend = nstack + new_thread->stack_info.size;
145 size_t nsp = (size_t) new_thread->switch_handle;
146
147 uint32_t flush_end = 0;
148
149 #ifdef CONFIG_USERSPACE
150 /* End of old_thread privileged stack. */
151 void *o_psp_end = old_thread->arch.psp;
152 #endif
153
154 __asm__ volatile("wsr %0, " ZSR_FLUSH_STR :: "r"(flush_end));
155
156 if (old_switch_handle != NULL) {
157 int32_t a0save;
158
159 __asm__ volatile("mov %0, a0;"
160 "call0 xtensa_spill_reg_windows;"
161 "mov a0, %0"
162 : "=r"(a0save));
163 }
164
165 /* The "live" area (the region between the switch handle,
166 * which is the stack pointer, and the top of the stack
167 * memory) of the inbound stack needs to be invalidated if we
168 * last ran on another cpu: it may contain data that was
169 * modified there, and our cache may be stale.
170 *
171 * The corresponding "dead area" of the inbound stack can be
172 * ignored. We may have cached data in that region, but by
173 * definition any unused stack memory will always be written
174 * before being read (well, unless the code has an
175 * uninitialized data error) so our stale cache will be
176 * automatically overwritten as needed.
177 */
178 if (curr_cpu != new_thread->arch.last_cpu) {
179 xtensa_cohere_stacks_cache_invd(nsp, nend);
180 }
181 old_thread->arch.last_cpu = curr_cpu;
182
183 /* Dummy threads appear at system initialization, but don't
184 * have stack_info data and will never be saved. Ignore.
185 */
186 if (old_thread->base.thread_state & _THREAD_DUMMY) {
187 return;
188 }
189
190 /* For the outbound thread, we obviousy want to flush any data
191 * in the live area (for the benefit of whichever CPU runs
192 * this thread next). But we ALSO have to invalidate the dead
193 * region of the stack. Those lines may have DIRTY data in
194 * our own cache, and we cannot be allowed to write them back
195 * later on top of the stack's legitimate owner!
196 *
197 * This work comes in two flavors. In interrupts, the
198 * outgoing context has already been saved for us, so we can
199 * do the flush right here. In direct context switches, we
200 * are still using the stack, so we do the invalidate of the
201 * bottom here, (and flush the line containing SP to handle
202 * the overlap). The remaining flush of the live region
203 * happens in the assembly code once the context is pushed, up
204 * to the stack top stashed in a special register.
205 */
206 if (old_switch_handle != NULL) {
207 #ifdef CONFIG_USERSPACE
208 if (o_psp_end == NULL)
209 #endif
210 {
211 xtensa_cohere_stacks_cache_flush(osp, oend);
212 xtensa_cohere_stacks_cache_invd(ostack, osp);
213 }
214 } else {
215 /* When in a switch, our current stack is the outbound
216 * stack. Flush the single line containing the stack
217 * bottom (which is live data) before invalidating
218 * everything below that. Remember that the 16 bytes
219 * below our SP are the calling function's spill area
220 * and may be live too.
221 */
222 __asm__ volatile("mov %0, a1" : "=r"(osp));
223 osp -= 16;
224 xtensa_cohere_stacks_cache_flush(osp, osp + 16);
225
226 #ifdef CONFIG_USERSPACE
227 if (o_psp_end == NULL)
228 #endif
229 {
230 xtensa_cohere_stacks_cache_invd(ostack, osp);
231
232 flush_end = oend;
233 }
234 }
235
236 #ifdef CONFIG_USERSPACE
237 /* User threads need a bit more processing due to having
238 * privileged stack for handling syscalls. The privileged
239 * stack always immediately precedes the thread stack.
240 *
241 * Note that, with userspace enabled, we need to swap
242 * page table during context switch via function calls.
243 * This means that the stack is being actively used
244 * unlike the non-userspace case mentioned above.
245 * Therefore we need to set ZSR_FLUSH_STR to make sure
246 * we flush the cached data in the stack.
247 */
248 if (o_psp_end != NULL) {
249 /* Start of old_thread privileged stack.
250 *
251 * struct xtensa_thread_stack_header wholly contains
252 * a array for the privileged stack, so we can use
253 * its size to calculate where the start is.
254 */
255 size_t o_psp_start = (size_t)o_psp_end - sizeof(struct xtensa_thread_stack_header);
256
257 if ((osp >= ostack) && (osp < oend)) {
258 /* osp in user stack. */
259 xtensa_cohere_stacks_cache_invd(o_psp_start, osp);
260
261 flush_end = oend;
262 } else if ((osp >= o_psp_start) && (osp < ostack)) {
263 /* osp in privileged stack. */
264 xtensa_cohere_stacks_cache_flush(ostack, oend);
265 xtensa_cohere_stacks_cache_invd(o_psp_start, osp);
266
267 flush_end = (size_t)old_thread->arch.psp;
268 }
269 }
270 #endif /* CONFIG_USERSPACE */
271
272 flush_end = ROUND_DOWN(flush_end, XCHAL_DCACHE_LINESIZE);
273 __asm__ volatile("wsr %0, " ZSR_FLUSH_STR :: "r"(flush_end));
274
275 #endif /* !CONFIG_SCHED_CPU_MASK_PIN_ONLY */
276 }
277 #endif
278
arch_is_in_isr(void)279 static inline bool arch_is_in_isr(void)
280 {
281 uint32_t nested;
282
283 #if defined(CONFIG_SMP)
284 /*
285 * Lock interrupts on SMP to ensure that the caller does not migrate
286 * to another CPU before we get to read the nested field.
287 */
288 unsigned int key;
289
290 key = arch_irq_lock();
291 #endif
292
293 nested = arch_curr_cpu()->nested;
294
295 #if defined(CONFIG_SMP)
296 arch_irq_unlock(key);
297 #endif
298
299 return nested != 0U;
300 }
301
302 #ifdef __cplusplus
303 }
304 #endif
305
306 #endif /* _ASMLANGUAGE */
307
308 #endif /* ZEPHYR_ARCH_XTENSA_INCLUDE_KERNEL_ARCH_FUNC_H_ */
309