xref: /haiku/src/system/kernel/arch/x86/arch_thread.cpp (revision daf10ef34051db2f97ac3a116c86a6828fab9d66)
1 /*
2  * Copyright 2002-2008, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  *
5  * Copyright 2001, Travis Geiselbrecht. All rights reserved.
6  * Distributed under the terms of the NewOS License.
7  */
8 
9 
10 #include <arch/thread.h>
11 
12 #include <string.h>
13 
14 #include <arch_cpu.h>
15 #include <cpu.h>
16 #include <kernel.h>
17 #include <ksignal.h>
18 #include <int.h>
19 #include <team.h>
20 #include <thread.h>
21 #include <tls.h>
22 #include <vm/vm_types.h>
23 #include <vm/VMAddressSpace.h>
24 
25 #include "paging/X86PagingStructures.h"
26 #include "paging/X86VMTranslationMap.h"
27 #include "x86_syscalls.h"
28 
29 
30 // from arch_interrupts.S
31 extern "C" void x86_return_to_userland(iframe* frame);
32 
33 // from arch_cpu.cpp
34 #ifndef __x86_64__
35 extern void (*gX86SwapFPUFunc)(void *oldState, const void *newState);
36 #endif
37 
38 
39 static struct iframe*
40 find_previous_iframe(Thread* thread, addr_t frame)
41 {
42 	// iterate backwards through the stack frames, until we hit an iframe
43 	while (frame >= thread->kernel_stack_base
44 		&& frame < thread->kernel_stack_top) {
45 		addr_t previousFrame = *(addr_t*)frame;
46 		if ((previousFrame & ~(addr_t)IFRAME_TYPE_MASK) == 0) {
47 			if (previousFrame == 0)
48 				return NULL;
49 			return (struct iframe*)frame;
50 		}
51 
52 		frame = previousFrame;
53 	}
54 
55 	return NULL;
56 }
57 
58 
59 static struct iframe*
60 get_previous_iframe(struct iframe* frame)
61 {
62 	if (frame == NULL)
63 		return NULL;
64 
65 	return find_previous_iframe(thread_get_current_thread(), frame->bp);
66 }
67 
68 
69 /*!
70 	Returns the current iframe structure of the running thread.
71 	This function must only be called in a context where it's actually
72 	sure that such iframe exists; ie. from syscalls, but usually not
73 	from standard kernel threads.
74 */
75 static struct iframe*
76 get_current_iframe(void)
77 {
78 	return find_previous_iframe(thread_get_current_thread(),
79 		x86_get_stack_frame());
80 }
81 
82 
83 /*!
84 	\brief Returns the current thread's topmost (i.e. most recent)
85 	userland->kernel transition iframe (usually the first one, save for
86 	interrupts in signal handlers).
87 	\return The iframe, or \c NULL, if there is no such iframe (e.g. when
88 			the thread is a kernel thread).
89 */
90 struct iframe*
91 x86_get_user_iframe(void)
92 {
93 	struct iframe* frame = get_current_iframe();
94 
95 	while (frame != NULL) {
96 		if (IFRAME_IS_USER(frame))
97 			return frame;
98 		frame = get_previous_iframe(frame);
99 	}
100 
101 	return NULL;
102 }
103 
104 
105 /*!	\brief Like x86_get_user_iframe(), just for the given thread.
106 	The thread must not be running and the threads spinlock must be held.
107 */
108 struct iframe*
109 x86_get_thread_user_iframe(Thread *thread)
110 {
111 	if (thread->state == B_THREAD_RUNNING)
112 		return NULL;
113 
114 	// find the user iframe
115 	struct iframe* frame = find_previous_iframe(thread,
116 		thread->arch_info.GetFramePointer());
117 
118 	while (frame != NULL) {
119 		if (IFRAME_IS_USER(frame))
120 			return frame;
121 		frame = get_previous_iframe(frame);
122 	}
123 
124 	return NULL;
125 }
126 
127 
128 struct iframe*
129 x86_get_current_iframe(void)
130 {
131 	return get_current_iframe();
132 }
133 
134 
135 phys_addr_t
136 x86_next_page_directory(Thread* from, Thread* to)
137 {
138 	VMAddressSpace* toAddressSpace = to->team->address_space;
139 	if (from->team->address_space == toAddressSpace) {
140 		// don't change the pgdir, same address space
141 		return 0;
142 	}
143 
144 	if (toAddressSpace == NULL)
145 		toAddressSpace = VMAddressSpace::Kernel();
146 
147 	return static_cast<X86VMTranslationMap*>(toAddressSpace->TranslationMap())
148 		->PagingStructures()->pgdir_phys;
149 }
150 
151 
152 /*!	Returns to the userland environment given by \a frame for a thread not
153 	having been userland before.
154 
155 	Before returning to userland all potentially necessary kernel exit work is
156 	done.
157 
158 	\param thread The current thread.
159 	\param frame The iframe defining the userland environment. Must point to a
160 		location somewhere on the caller's stack (e.g. a local variable).
161 */
162 void
163 x86_initial_return_to_userland(Thread* thread, iframe* frame)
164 {
165 	// disable interrupts and set up CPU specifics for this thread
166 	disable_interrupts();
167 
168 	get_cpu_struct()->arch.tss.sp0 = thread->kernel_stack_top;
169 	x86_set_tls_context(thread);
170 	x86_set_syscall_stack(thread->kernel_stack_top);
171 
172 	// return to userland
173 	x86_return_to_userland(frame);
174 }
175 
176 
177 //	#pragma mark -
178 
179 
180 status_t
181 arch_team_init_team_struct(Team* p, bool kernel)
182 {
183 	return B_OK;
184 }
185 
186 
187 /*!	Initializes the user-space TLS local storage pointer in
188 	the thread structure, and the reserved TLS slots.
189 
190 	Is called from _create_user_thread_kentry().
191 */
192 status_t
193 arch_thread_init_tls(Thread* thread)
194 {
195 	addr_t tls[TLS_FIRST_FREE_SLOT];
196 
197 	thread->user_local_storage = thread->user_stack_base
198 		+ thread->user_stack_size;
199 
200 	// initialize default TLS fields
201 	memset(tls, 0, sizeof(tls));
202 	tls[TLS_BASE_ADDRESS_SLOT] = thread->user_local_storage;
203 	tls[TLS_THREAD_ID_SLOT] = thread->id;
204 	tls[TLS_USER_THREAD_SLOT] = (addr_t)thread->user_thread;
205 
206 	return user_memcpy((void*)thread->user_local_storage, tls, sizeof(tls));
207 }
208 
209 
210 void
211 arch_thread_context_switch(Thread* from, Thread* to)
212 {
213 	cpu_ent* cpuData = to->cpu;
214 
215 	cpuData->arch.tss.sp0 = to->kernel_stack_top;
216 	x86_set_syscall_stack(to->kernel_stack_top);
217 
218 	// set TLS GDT entry to the current thread - since this action is
219 	// dependent on the current CPU, we have to do it here
220 	if (to->user_local_storage != 0)
221 		x86_set_tls_context(to);
222 
223 	X86PagingStructures* activePagingStructures
224 		= cpuData->arch.active_paging_structures;
225 	VMAddressSpace* toAddressSpace = to->team->address_space;
226 
227 	X86PagingStructures* toPagingStructures;
228 	if (toAddressSpace != NULL
229 		&& (toPagingStructures = static_cast<X86VMTranslationMap*>(
230 				toAddressSpace->TranslationMap())->PagingStructures())
231 					!= activePagingStructures) {
232 		// update on which CPUs the address space is used
233 		int cpu = cpuData->cpu_num;
234 		activePagingStructures->active_on_cpus.ClearBitAtomic(cpu);
235 		toPagingStructures->active_on_cpus.SetBitAtomic(cpu);
236 
237 		// assign the new paging structures to the CPU
238 		toPagingStructures->AddReference();
239 		cpuData->arch.active_paging_structures = toPagingStructures;
240 
241 		// set the page directory, if it changes
242 		addr_t newPageDirectory = toPagingStructures->pgdir_phys;
243 		if (newPageDirectory != activePagingStructures->pgdir_phys)
244 			x86_swap_pgdir(newPageDirectory);
245 
246 		// This CPU no longer uses the previous paging structures.
247 		activePagingStructures->RemoveReference();
248 	}
249 
250 #ifndef __x86_64__
251 	gX86SwapFPUFunc(from->arch_info.fpu_state, to->arch_info.fpu_state);
252 #endif
253 	x86_context_switch(&from->arch_info, &to->arch_info);
254 }
255 
256 
257 bool
258 arch_on_signal_stack(Thread *thread)
259 {
260 	struct iframe* frame = get_current_iframe();
261 
262 	return frame->user_sp >= thread->signal_stack_base
263 		&& frame->user_sp < thread->signal_stack_base
264 			+ thread->signal_stack_size;
265 }
266 
267 
268 /*!	Saves everything needed to restore the frame in the child fork in the
269 	arch_fork_arg structure to be passed to arch_restore_fork_frame().
270 	Also makes sure to return the right value.
271 */
272 void
273 arch_store_fork_frame(struct arch_fork_arg* arg)
274 {
275 	struct iframe* frame = x86_get_current_iframe();
276 
277 	// we need to copy the threads current iframe
278 	arg->iframe = *frame;
279 
280 	// we also want fork() to return 0 for the child
281 	arg->iframe.ax = 0;
282 }
283 
284 
285 /*!	Restores the frame from a forked team as specified by the provided
286 	arch_fork_arg structure.
287 	Needs to be called from within the child team, i.e. instead of
288 	arch_thread_enter_userspace() as thread "starter".
289 	This function does not return to the caller, but will enter userland
290 	in the child team at the same position where the parent team left of.
291 
292 	\param arg The architecture specific fork arguments including the
293 		environment to restore. Must point to a location somewhere on the
294 		caller's stack.
295 */
296 void
297 arch_restore_fork_frame(struct arch_fork_arg* arg)
298 {
299 	x86_initial_return_to_userland(thread_get_current_thread(), &arg->iframe);
300 }
301