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
30extern "C" void x86_return_to_userland(iframe* frame);
31
32// from arch_cpu.cpp
33#ifndef __x86_64__
34extern void (*gX86SwapFPUFunc)(void *oldState, const void *newState);
35#endif
36
37
38static struct iframe*
39find_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
58static struct iframe*
59get_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*/
74static struct iframe*
75get_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*/
89struct iframe*
90x86_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*/
107struct iframe*
108x86_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
127struct iframe*
128x86_get_current_iframe(void)
129{
130	return get_current_iframe();
131}
132
133
134phys_addr_t
135x86_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*/
161void
162x86_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
179status_t
180arch_team_init_team_struct(Team* p, bool kernel)
181{
182	return B_OK;
183}
184
185
186status_t
187arch_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
195void
196arch_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
242bool
243arch_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*/
257void
258arch_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*/
281void
282arch_restore_fork_frame(struct arch_fork_arg* arg)
283{
284	x86_initial_return_to_userland(thread_get_current_thread(), &arg->iframe);
285}
286