1// Copyright 2016 The Fuchsia Authors
2// Copyright (c) 2008 Travis Geiselbrecht
3//
4// Use of this source code is governed by a MIT-style
5// license that can be found in the LICENSE file or at
6// https://opensource.org/licenses/MIT
7
8#include <arch/arm64.h>
9#include <arch/arm64/mp.h>
10#include <debug.h>
11#include <kernel/thread.h>
12#include <stdlib.h>
13#include <string.h>
14#include <sys/types.h>
15#include <trace.h>
16
17#define LOCAL_TRACE 0
18
19// Register state layout used by arm64_context_switch().
20struct context_switch_frame {
21    uint64_t tpidr_el0;
22    uint64_t tpidrro_el0;
23    uint64_t r19;
24    uint64_t r20;
25    uint64_t r21;
26    uint64_t r22;
27    uint64_t r23;
28    uint64_t r24;
29    uint64_t r25;
30    uint64_t r26;
31    uint64_t r27;
32    uint64_t r28;
33    uint64_t r29;
34    uint64_t lr;
35};
36
37// assert that the context switch frame is a multiple of 16 to maintain
38// stack alignment requirements per ABI
39static_assert(sizeof(context_switch_frame) % 16 == 0, "");
40
41extern void arm64_context_switch(addr_t* old_sp, addr_t new_sp);
42
43void arch_thread_initialize(thread_t* t, vaddr_t entry_point) {
44    // zero out the entire arch state
45    t->arch = {};
46
47    // create a default stack frame on the stack
48    vaddr_t stack_top = t->stack.top;
49
50    // make sure the top of the stack is 16 byte aligned for EABI compliance
51    stack_top = ROUNDDOWN(stack_top, 16);
52    t->stack.top = stack_top;
53
54    struct context_switch_frame* frame = (struct context_switch_frame*)(stack_top);
55    frame--;
56
57    // fill in the entry point
58    frame->lr = entry_point;
59
60    // This is really a global (boot-time) constant value.
61    // But it's stored in each thread struct to satisfy the
62    // compiler ABI (TPIDR_EL1 + ZX_TLS_STACK_GUARD_OFFSET).
63    t->arch.stack_guard = get_current_thread()->arch.stack_guard;
64
65    // set the stack pointer
66    t->arch.sp = (vaddr_t)frame;
67#if __has_feature(safe_stack)
68    t->arch.unsafe_sp =
69        ROUNDDOWN(t->stack.unsafe_base + t->stack.size, 16);
70#endif
71}
72
73__NO_SAFESTACK void arch_thread_construct_first(thread_t* t) {
74    // Propagate the values from the fake arch_thread that the thread
75    // pointer points to now (set up in start.S) into the real thread
76    // structure being set up now.
77    thread_t* fake = get_current_thread();
78    t->arch.stack_guard = fake->arch.stack_guard;
79    t->arch.unsafe_sp = fake->arch.unsafe_sp;
80
81    // make sure the thread saves a copy of the current cpu pointer
82    t->arch.current_percpu_ptr = arm64_read_percpu_ptr();
83
84    // Force the thread pointer immediately to the real struct.  This way
85    // our callers don't have to avoid safe-stack code or risk losing track
86    // of the unsafe_sp value.  The caller's unsafe_sp value is visible at
87    // TPIDR_EL1 + ZX_TLS_UNSAFE_SP_OFFSET as expected, though TPIDR_EL1
88    // happens to have changed.  (We're assuming that the compiler doesn't
89    // decide to cache the TPIDR_EL1 value across this function call, which
90    // would be pointless since it's just one instruction to fetch it afresh.)
91    set_current_thread(t);
92}
93
94__NO_SAFESTACK void arch_context_switch(thread_t* oldthread,
95                                        thread_t* newthread) {
96    LTRACEF("old %p (%s), new %p (%s)\n", oldthread, oldthread->name, newthread, newthread->name);
97    DSB; /* broadcast tlb operations in case the thread moves to another cpu */
98
99    /* set the current cpu pointer in the new thread's structure so it can be
100     * restored on exception entry.
101     */
102    newthread->arch.current_percpu_ptr = arm64_read_percpu_ptr();
103
104    arm64_fpu_context_switch(oldthread, newthread);
105    arm64_context_switch(&oldthread->arch.sp, newthread->arch.sp);
106}
107
108void arch_dump_thread(thread_t* t) {
109    if (t->state != THREAD_RUNNING) {
110        dprintf(INFO, "\tarch: ");
111        dprintf(INFO, "sp 0x%lx\n", t->arch.sp);
112    }
113}
114
115void* arch_thread_get_blocked_fp(struct thread* t) {
116    if (!WITH_FRAME_POINTERS)
117        return nullptr;
118
119    struct context_switch_frame* frame = (struct context_switch_frame*)t->arch.sp;
120
121    return (void*)frame->r29;
122}
123