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