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