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