1/* 2 * Copyright 2013, winocm. <winocm@icloud.com> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without modification, 6 * are permitted provided that the following conditions are met: 7 * 8 * Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * Redistributions in binary form must reproduce the above copyright notice, this 12 * list of conditions and the following disclaimer in the documentation and/or 13 * other materials provided with the distribution. 14 * 15 * If you are going to use this software in any form that does not involve 16 * releasing the source to this project or improving it, let me know beforehand. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29/* 30 * ARM thread support 31 */ 32 33/* 34 * this is not smp safe at /all/. 35 */ 36 37#include <mach/mach_types.h> 38#include <mach/vm_param.h> 39#include <kern/debug.h> 40#include <arm/cpu_data.h> 41#include <kern/thread.h> 42#include <arm/misc_protos.h> 43#include <kern/kalloc.h> 44#include <vm/vm_kern.h> 45#include <vm/vm_map.h> 46 47thread_t CurrentThread; 48 49/* etimer is *still* broken and so are threads. :| */ 50 51#define kprintf(args...) 52/* 53 * These are defined in ctxswitch.s. 54 */ 55thread_t Switch_context(thread_t old_thread, thread_continue_t continuation, thread_t new_thread); 56void Call_continuation(thread_continue_t continuation, void *parameter, wait_result_t wresult); 57 58static void save_vfp_context(thread_t thread); 59 60/** 61 * arm_set_threadpid_user_readonly 62 * 63 * Set current thread identifier to the cthread identifier. 64 */ 65void arm_set_threadpid_user_readonly(uint64_t* address) { 66 __asm__ __volatile__("msr tpidrro_el0, %0" :: "r"(address)); 67} 68 69/** 70 * arm_set_threadpid_priv_readwrite 71 * 72 * Set current thread identifier to the specified thread_t. 73 */ 74void arm_set_threadpid_priv_readwrite(uint64_t* address) { 75 __asm__ __volatile__("msr tpidr_el1, %0" :: "r"(address)); 76} 77 78/** 79 * machine_set_current_thread 80 * 81 * Set current thread to the specified thread_t. 82 */ 83void machine_set_current_thread(thread_t thread) 84{ 85 /* Set the current thread. */ 86 CurrentThread = thread; 87 88 arm_set_threadpid_user_readonly((uint64_t*)CurrentThread->machine.cthread_self); 89 arm_set_threadpid_priv_readwrite((uint64_t*)CurrentThread); 90} 91 92void 93thread_set_cthread_self(uint64_t cthr) 94{ 95 thread_t curthr = current_thread(); 96 assert(curthr); 97 curthr->machine.cthread_self = cthr; 98 arm_set_threadpid_user_readonly((uint64_t*)curthr->machine.cthread_self); 99} 100 101uint64_t 102thread_get_cthread_self(void) 103{ 104 thread_t curthr = current_thread(); 105 assert(curthr); 106 return curthr->machine.cthread_self; 107} 108 109/** 110 * machine_thread_inherit_taskwide 111 */ 112kern_return_t machine_thread_inherit_taskwide(thread_t thread, task_t parent_task) 113{ 114 return KERN_FAILURE; 115} 116 117/** 118 * machine_thread_create 119 * 120 * Do machine-dependent initialization for a thread. 121 */ 122kern_return_t machine_thread_create(thread_t thread, task_t task) 123{ 124 arm_saved_state_t* sv = NULL; 125 126 /* Create a thread and set the members in the pcb. */ 127 assert(thread != NULL); 128 assert(thread->machine.iss == NULL); 129 130 if (thread->machine.iss == NULL) { 131 kmem_alloc_kobject(kernel_map, &sv, sizeof(arm_saved_state_t)); 132 if(!sv) 133 panic("couldn't alloc savearea for thread\n"); 134 } 135 136 /* Okay, got one. */ 137 bzero(sv, sizeof(arm_saved_state_t)); 138 139 /* Set the members now. */ 140 thread->machine.iss = sv; 141 thread->machine.preempt_count = 0; 142 thread->machine.cpu_data = cpu_datap(cpu_number()); 143 144 /* Also kernel threads */ 145 thread->machine.uss = thread->machine.iss; 146 147 return KERN_SUCCESS; 148} 149 150/** 151 * machine_switch_context 152 * 153 * Switch the current executing machine context to a new one. 154 */ 155thread_t machine_switch_context(thread_t old, thread_continue_t continuation, thread_t new) 156{ 157 pmap_t new_pmap; 158 cpu_data_t* datap; 159 register thread_t retval; 160 161 kprintf("machine_switch_context: %p -> %p (cont: %p)\n", old, new, continuation); 162 163#if 0 164 if (old == new) 165 panic("machine_switch_context: old = new thread (%p %p)", old, new); 166#endif 167 168 datap = cpu_datap(cpu_number()); 169 assert(datap != NULL); 170 171 datap->old_thread = old; 172 173 save_vfp_context(old); 174 175 new_pmap = new->map->pmap; 176 if ((old->map->pmap != new_pmap)) { 177 if(new_pmap != NULL) { 178 pmap_switch(new_pmap); 179 } 180 } 181 182 /* VFP save needed */ 183 184 retval = Switch_context(old, continuation, new); 185 assert(retval != NULL); 186 187 return retval; 188} 189 190 191void 192machine_stack_handoff(thread_t old, 193 thread_t new) 194{ 195 vm_offset_t stack; 196 pmap_t new_pmap; 197 198 assert(new); 199 assert(old); 200 201 if (old == new) 202 panic("machine_stack_handoff"); 203 204 kprintf("machine_stack_handoff: %p = old, %p = new\n", old, new); 205 206 save_vfp_context(old); 207 208 stack = machine_stack_detach(old); 209 new->kernel_stack = stack; 210 211 uint64_t *kstack = (uint64_t*)STACK_IKS(stack); 212 new->machine.iss = (arm_saved_state_t*)kstack; 213 new->machine.iss->sp = (uint64_t)kstack - sizeof(arm_saved_state_t); 214 215 old->machine.iss = 0; 216 217 if (stack == old->reserved_stack) { 218 assert(new->reserved_stack); 219 old->reserved_stack = new->reserved_stack; 220 new->reserved_stack = stack; 221 } 222 223 /* 224 * A full call to machine_stack_attach() is unnecessry 225 * because old stack is already initialized. 226 */ 227 228 new_pmap = new->map->pmap; 229 if ((old->map->pmap != new_pmap)) { 230 if(new_pmap != NULL) { 231 pmap_switch(new_pmap); 232 } 233 } 234 235 machine_set_current_thread(new); 236 237 return; 238} 239 240/** 241 * machine_thread_init 242 */ 243void machine_thread_init(void) 244{ 245 return; 246} 247 248/** 249 * save_vfp_context 250 * 251 * Saves current vfp context into thread state. 252 */ 253static void save_vfp_context(thread_t thread) 254{ 255 assert(thread); 256 if(thread->machine.vfp_enable && !thread->machine.vfp_dirty) { 257 vfp_context_save(thread->machine.vfp_regs); 258 vfp_enable_exception(FALSE); 259 } 260} 261 262/** 263 * machine_stack_attach 264 * 265 * Attach a stack to a thread and populate its members. 266 */ 267void machine_stack_attach(thread_t thread, vm_offset_t stack) 268{ 269 assert(stack != NULL); 270 assert(thread != NULL); 271 272 kprintf("machine_stack_attach: setting stack %p for thread %p\n", 273 stack, thread); 274 275 uint64_t *kstack = (uint64_t*)STACK_IKS(stack); 276 277 thread->kernel_stack = stack; 278 279 thread->machine.iss = (arm_saved_state_t*)kstack; 280 281 thread->machine.iss->r[0] = (uint64_t)thread; 282 thread->machine.iss->lr = (uint64_t)thread_continue; 283 thread->machine.iss->sp = (uint64_t)kstack - sizeof(arm_saved_state_t); 284 285 thread->machine.uss = thread->machine.iss; 286 287 return; 288} 289 290/** 291 * machine_stack_detach 292 * 293 * Kill the current stack. 294 */ 295vm_offset_t machine_stack_detach(thread_t thread) 296{ 297 vm_offset_t stack; 298 299 assert(thread != NULL); 300 301 kprintf("machine_stack_detach: killing stack for thread %p\n", 302 thread); 303 304 stack = thread->kernel_stack; 305 306 return stack; 307} 308 309/* 310 * Only one real processor. 311 */ 312processor_t machine_choose_processor(processor_set_t pset, processor_t preferred) 313{ 314 return (cpu_datap(cpu_number())->cpu_processor); 315} 316 317/** 318 * call_continuation. 319 * 320 * Call the continuation routine for a thread, really is a shim for the 321 * assembly routine. 322 */ 323void call_continuation(thread_continue_t continuation, void *parameter, wait_result_t wresult) 324{ 325 thread_t self = current_thread(); 326 327 assert(self->machine.iss != NULL); 328 assert(self->kernel_stack); 329 330 kprintf("call_continuation: calling continuation on thread %p\n", self); 331 332 Call_continuation(continuation, parameter, wresult); 333 334 return; 335} 336 337/** 338 * machine_thread_destroy 339 * 340 * Destroy the machine specific thread context block. 341 */ 342void 343machine_thread_destroy(thread_t thread) 344{ 345 return; 346} 347 348/* 349 * This is where registers that are not normally specified by the mach-o 350 * file on an execve would be nullified, perhaps to avoid a covert channel. 351 */ 352kern_return_t 353machine_thread_state_initialize(thread_t thread) 354{ 355 return KERN_SUCCESS; 356} 357 358/* 359 * This is called when a task is terminated, and also on exec(). 360 * Clear machine-dependent state that is stored on the task. 361 */ 362void 363machine_task_terminate(task_t task) 364{ 365 return; 366} 367