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