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
34#ifndef __x86_64__
35extern void (*gX86SwapFPUFunc)(void *oldState, const void *newState);
36#endif
37
38
39static struct iframe*
40find_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
59static struct iframe*
60get_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*/
75static struct iframe*
76get_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*/
90struct iframe*
91x86_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*/
108struct iframe*
109x86_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
128struct iframe*
129x86_get_current_iframe(void)
130{
131	return get_current_iframe();
132}
133
134
135phys_addr_t
136x86_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*/
162void
163x86_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
180status_t
181arch_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*/
192status_t
193arch_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
210void
211arch_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
257bool
258arch_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*/
272void
273arch_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*/
296void
297arch_restore_fork_frame(struct arch_fork_arg* arg)
298{
299	x86_initial_return_to_userland(thread_get_current_thread(), &arg->iframe);
300}
301