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