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