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
31extern "C" void x86_return_to_userland(iframe* frame);
32
33// from arch_cpu.cpp
34extern void (*gX86SwapFPUFunc)(void *oldState, const void *newState);
35
36
37static struct iframe*
38find_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
57static struct iframe*
58get_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*/
73static struct iframe*
74get_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*/
88struct iframe*
89x86_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*/
106struct iframe*
107x86_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
126struct iframe*
127x86_get_current_iframe(void)
128{
129	return get_current_iframe();
130}
131
132
133phys_addr_t
134x86_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*/
160void
161x86_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
178status_t
179arch_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*/
190status_t
191arch_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
208void
209arch_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
254bool
255arch_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*/
269void
270arch_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*/
293void
294arch_restore_fork_frame(struct arch_fork_arg* arg)
295{
296	x86_initial_return_to_userland(thread_get_current_thread(), &arg->iframe);
297}
298