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