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/*
31 * ARM thread support
32 */
33
34/*
35 * this is not smp safe at /all/.
36 */
37
38#include <mach/mach_types.h>
39#include <mach/vm_param.h>
40#include <kern/debug.h>
41#include <arm/cpu_data.h>
42#include <kern/thread.h>
43#include <arm/misc_protos.h>
44#include <kern/kalloc.h>
45#include <vm/vm_kern.h>
46#include <vm/vm_map.h>
47
48thread_t CurrentThread;
49
50/*
51 * Maps state flavor to number of words in the state:
52 */
53uint32_t _MachineStateCount[] = {
54    /* FLAVOR_LIST */
55    0,
56    ARM_THREAD_STATE_COUNT,
57    ARM_VFP_STATE_COUNT,
58    ARM_EXCEPTION_STATE_COUNT,
59    ARM_DEBUG_STATE_COUNT,
60};
61
62/* etimer is *still* broken and so are threads. :| */
63#define kprintf(args...)
64
65/*
66 * These are defined in ctxswitch.s.
67 */
68thread_t Switch_context(thread_t old_thread, thread_continue_t continuation,
69                        thread_t new_thread);
70void Call_continuation(thread_continue_t continuation, void *parameter,
71                       wait_result_t wresult, vm_offset_t stack);
72
73static void save_vfp_context(thread_t thread);
74
75/**
76 * arm_set_threadpid_user_readonly
77 *
78 * Set current thread identifier to the cthread identifier.
79 */
80void arm_set_threadpid_user_readonly(uint32_t * address)
81{
82    __asm__ __volatile__("mcr p15, 0, %0, c13, c0, 3"::"r"(address));
83}
84
85/**
86 * arm_set_threadpid_priv_readwrite
87 *
88 * Set current thread identifier to the specified thread_t.
89 */
90void arm_set_threadpid_priv_readwrite(uint32_t * address)
91{
92    __asm__ __volatile__("mcr p15, 0, %0, c13, c0, 4"::"r"(address));
93}
94
95/**
96 * machine_set_current_thread
97 *
98 * Set current thread to the specified thread_t.
99 */
100void machine_set_current_thread(thread_t thread)
101{
102    /*
103     * Set the current thread.
104     */
105    CurrentThread = thread;
106
107    arm_set_threadpid_user_readonly((uint32_t *) thread->machine.cthread_self);
108    arm_set_threadpid_priv_readwrite((uint32_t *) thread);
109}
110
111void thread_set_cthread_self(uint32_t cthr)
112{
113    thread_t curthr = current_thread();
114    assert(curthr);
115    curthr->machine.cthread_self = cthr;
116    arm_set_threadpid_user_readonly((uint32_t *) curthr->machine.cthread_self);
117}
118
119uint64_t thread_get_cthread_self(void)
120{
121    thread_t curthr = current_thread();
122    assert(curthr);
123    return curthr->machine.cthread_self;
124}
125
126/**
127 * machine_thread_inherit_taskwide
128 */
129kern_return_t machine_thread_inherit_taskwide(thread_t thread, task_t parent_task)
130{
131    return KERN_FAILURE;
132}
133
134/**
135 * machine_thread_create
136 *
137 * Do machine-dependent initialization for a thread.
138 */
139kern_return_t machine_thread_create(thread_t thread, task_t task)
140{
141    arm_saved_state_t *sv = NULL;
142
143    /*
144     * Create a thread and set the members in the pcb.
145     */
146    assert(thread != NULL);
147    assert(thread->machine.iss == NULL);
148
149    /*
150     * Zero out the register save areas.
151     */
152    bzero(&thread->machine.vfp_regs, sizeof(arm_vfp_state_t));
153    bzero(&thread->machine.user_regs, sizeof(arm_saved_state_t));
154
155    /*
156     * Set the members now.
157     */
158    thread->machine.preempt_count = 0;
159    thread->machine.cpu_data = cpu_datap(cpu_number());
160    thread->machine.vfp_enable = 0;
161    thread->machine.vfp_dirty = 0;
162
163    /*
164     * Also kernel threads
165     */
166    thread->machine.uss = &thread->machine.user_regs;
167
168    return KERN_SUCCESS;
169}
170
171/**
172 * machine_switch_context
173 *
174 * Switch the current executing machine context to a new one.
175 */
176thread_t machine_switch_context(thread_t old, thread_continue_t continuation,
177                                thread_t new)
178{
179    pmap_t new_pmap;
180    cpu_data_t *datap;
181    register thread_t retval;
182
183    kprintf("machine_switch_context: %p -> %p (cont: %p)\n", old, new, continuation);
184
185    if (old == new)
186        panic("machine_switch_context: old = new thread (%p %p)", old, new);
187
188    datap = cpu_datap(cpu_number());
189    assert(datap != NULL);
190
191    datap->old_thread = old;
192
193    save_vfp_context(old);
194
195    new_pmap = new->map->pmap;
196    if ((old->map->pmap != new_pmap)) {
197        if (new_pmap != NULL) {
198            pmap_switch(new_pmap);
199        }
200    }
201
202    /*
203     * VFP save needed
204     */
205
206    retval = Switch_context(old, continuation, new);
207    assert(retval != NULL);
208
209    return retval;
210}
211
212void machine_stack_handoff(thread_t old, thread_t new)
213{
214    vm_offset_t stack;
215    pmap_t new_pmap;
216
217    assert(new);
218    assert(old);
219
220    if (old == new)
221        panic("machine_stack_handoff");
222
223    kprintf("machine_stack_handoff: %p = old, %p = new\n", old, new);
224
225    save_vfp_context(old);
226
227    stack = machine_stack_detach(old);
228    new->kernel_stack = stack;
229
230    uint32_t *kstack = (uint32_t *) STACK_IKS(stack);
231    new->machine.iss = (arm_saved_state_t *) kstack;
232    new->machine.iss->sp = (uint32_t) kstack - sizeof(arm_saved_state_t);
233
234    old->machine.iss = 0;
235
236    if (stack == old->reserved_stack) {
237        assert(new->reserved_stack);
238        old->reserved_stack = new->reserved_stack;
239        new->reserved_stack = stack;
240    }
241
242    /*
243     * A full call to machine_stack_attach() is unnecessry
244     * because old stack is already initialized.
245     */
246
247    new_pmap = new->map->pmap;
248    if ((old->map->pmap != new_pmap)) {
249        if (new_pmap != NULL) {
250            pmap_switch(new_pmap);
251        }
252    }
253
254    machine_set_current_thread(new);
255
256    return;
257}
258
259/**
260 * machine_thread_init
261 */
262void machine_thread_init(void)
263{
264    return;
265}
266
267/**
268 * save_vfp_context
269 *
270 * Saves current vfp context into thread state.
271 */
272static void save_vfp_context(thread_t thread)
273{
274    if (thread->machine.vfp_enable && !thread->machine.vfp_dirty) {
275        vfp_context_save(&thread->machine.vfp_regs);
276        vfp_enable_exception(FALSE);
277        thread->machine.vfp_enable = FALSE;
278    }
279}
280
281/**
282 * machine_stack_attach
283 *
284 * Attach a stack to a thread and populate its members.
285 */
286void machine_stack_attach(thread_t thread, vm_offset_t stack)
287{
288    assert(stack != NULL);
289    assert(thread != NULL);
290
291    kprintf("machine_stack_attach: setting stack %p for thread %p\n", stack, thread);
292
293    uint32_t *kstack = (uint32_t *) STACK_IKS(stack);
294
295    thread->kernel_stack = stack;
296    thread->machine.iss = (arm_saved_state_t *) kstack;
297    thread->machine.iss->r[0] = (uint32_t) thread;
298    thread->machine.iss->lr = (uint32_t) thread_continue;
299    thread->machine.iss->sp = (uint32_t) kstack - sizeof(arm_saved_state_t);
300
301    return;
302}
303
304/**
305 * machine_stack_detach
306 *
307 * Kill the current stack.
308 */
309vm_offset_t machine_stack_detach(thread_t thread)
310{
311    vm_offset_t stack;
312
313    assert(thread != NULL);
314
315    kprintf("machine_stack_detach: killing stack for thread %p\n", thread);
316
317    stack = thread->kernel_stack;
318    thread->kernel_stack = 0;
319
320    return stack;
321}
322
323/*
324 * Only one real processor.
325 */
326processor_t machine_choose_processor(processor_set_t pset, processor_t preferred)
327{
328    return preferred;
329}
330
331/**
332 * call_continuation.
333 *
334 * Call the continuation routine for a thread, really is a shim for the
335 * assembly routine.
336 */
337void call_continuation(thread_continue_t continuation, void *parameter,
338                       wait_result_t wresult)
339{
340    thread_t self = current_thread();
341
342    assert(self->machine.iss != NULL);
343    assert(self->kernel_stack);
344    assert(continuation);
345
346    kprintf("call_continuation: calling continuation on thread %p\n", self);
347
348    uint32_t kss;
349    kss = (uint32_t) STACK_IKS(self->kernel_stack);
350
351    Call_continuation(continuation, parameter, wresult, kss);
352
353    return;
354}
355
356/**
357 * machine_thread_destroy
358 *
359 * Destroy the machine specific thread context block.
360 */
361void machine_thread_destroy(thread_t thread)
362{
363    return;
364}
365
366/*
367 * This is where registers that are not normally specified by the mach-o
368 * file on an execve would be nullified, perhaps to avoid a covert channel.
369 */
370kern_return_t machine_thread_state_initialize(thread_t thread)
371{
372    return KERN_SUCCESS;
373}
374
375/*
376 * This is called when a task is terminated, and also on exec().
377 * Clear machine-dependent state that is stored on the task.
378 */
379void machine_task_terminate(task_t task)
380{
381    return;
382}
383
384void *find_user_regs(thread_t thread)
385{
386    assert(thread->machine.uss == &thread->machine.user_regs);
387    return (void *) thread->machine.uss;
388}
389
390kern_return_t machine_thread_dup(thread_t self, thread_t target)
391{
392    /*
393     * Copy user registers.
394     */
395    bcopy(self->machine.uss, target->machine.uss, sizeof(arm_saved_state_t));
396
397    /*
398     * Save FP registers and copy.
399     */
400    save_vfp_context(self);
401    bcopy(&self->machine.vfp_regs, &target->machine.vfp_regs, sizeof(arm_vfp_state_t));
402    target->machine.cthread_self = self->machine.cthread_self;
403    return KERN_SUCCESS;
404}
405
406/*
407 * consider_machine_collect:
408 *
409 *  Try to collect machine-dependent pages
410 */
411void consider_machine_collect(void)
412{
413}
414
415void consider_machine_adjust(void)
416{
417}
418
419unsigned int get_useraddr(void)
420{
421    thread_t thr_act = current_thread();
422
423    return (thr_act->machine.iss->pc);
424}
425