* Copyright 2003-2023, Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Axel Dörfler <axeld@pinc-software.de>
* Ingo Weinhold <bonefish@cs.tu-berlin.de>
* François Revol <revol@free.fr>
*
* Copyright 2001, Travis Geiselbrecht. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
#include <thread.h>
#include <arch_thread.h>
#include <arch_cpu.h>
#include <arch/thread.h>
#include <boot/stage2.h>
#include <commpage.h>
#include <kernel.h>
#include <thread.h>
#include <vm/vm_types.h>
#include <vm/VMAddressSpace.h>
#include <arch_vm.h>
#include <arch/vm_translation_map.h>
#include <string.h>
#include "ARMPagingStructures.h"
#include "ARMVMTranslationMap.h"
#ifdef TRACE_ARCH_THREAD
# define TRACE(x...) dprintf(x)
#else
# define TRACE(x...) ;
#endif
static struct arch_thread sInitialState;
void
arm_push_iframe(struct iframe_stack *stack, struct iframe *frame)
{
ASSERT(stack->index < IFRAME_TRACE_DEPTH);
stack->frames[stack->index++] = frame;
}
void
arm_pop_iframe(struct iframe_stack *stack)
{
ASSERT(stack->index > 0);
stack->index--;
}
status_t
arch_thread_init(struct kernel_args *args)
{
return B_OK;
}
status_t
arch_team_init_team_struct(Team *team, bool kernel)
{
return B_OK;
}
status_t
arch_thread_init_thread_struct(Thread *thread)
{
memcpy(&thread->arch_info, &sInitialState, sizeof(struct arch_thread));
return B_OK;
}
void
arch_thread_init_kthread_stack(Thread* thread, void* _stack, void* _stackTop,
void (*function)(void*), const void* data)
{
addr_t* stackTop = (addr_t*)_stackTop;
TRACE("arch_thread_init_kthread_stack(%s): stack top %p, function %p, data: "
"%p\n", thread->name, stackTop, function, data);
*--stackTop = (addr_t)function;
for (int i = 1; i <= 12; i++)
*--stackTop = 0;
*--stackTop = (addr_t)data;
thread->arch_info.sp = stackTop;
}
status_t
arch_thread_init_tls(Thread *thread)
{
thread->user_local_storage =
thread->user_stack_base + thread->user_stack_size;
return B_OK;
}
void
arm_swap_pgdir(uint32_t pageDirectoryAddress)
{
arm_set_ttbr0(pageDirectoryAddress);
isb();
arch_cpu_global_TLB_invalidate();
}
void
arch_thread_context_switch(Thread *from, Thread *to)
{
arm_set_tpidruro(to->user_local_storage);
VMAddressSpace *oldAddressSpace = from->team->address_space;
VMTranslationMap *oldTranslationMap = oldAddressSpace->TranslationMap();
phys_addr_t oldPageDirectoryAddress =
((ARMVMTranslationMap *)oldTranslationMap)->PagingStructures()->pgdir_phys;
VMAddressSpace *newAddressSpace = to->team->address_space;
VMTranslationMap *newTranslationMap = newAddressSpace->TranslationMap();
phys_addr_t newPageDirectoryAddress =
((ARMVMTranslationMap *)newTranslationMap)->PagingStructures()->pgdir_phys;
if (oldPageDirectoryAddress != newPageDirectoryAddress) {
TRACE("arch_thread_context_switch: swap pgdir: "
"0x%08" B_PRIxPHYSADDR " -> 0x%08" B_PRIxPHYSADDR "\n",
oldPageDirectoryAddress, newPageDirectoryAddress);
arm_swap_pgdir(newPageDirectoryAddress);
}
TRACE("arch_thread_context_switch: %p(%s/%p) -> %p(%s/%p)\n",
from, from->name, from->arch_info.sp, to, to->name, to->arch_info.sp);
arm_save_fpu(&from->arch_info.fpuContext);
arm_restore_fpu(&to->arch_info.fpuContext);
arm_context_switch(&from->arch_info, &to->arch_info);
TRACE("arch_thread_context_switch %p %p\n", to, from);
}
void
arch_thread_dump_info(void *info)
{
struct arch_thread *at = (struct arch_thread *)info;
dprintf("\tsp: %p\n", at->sp);
}
status_t
arch_thread_enter_userspace(Thread *thread, addr_t entry,
void *args1, void *args2)
{
arm_set_tpidruro(thread->user_local_storage);
addr_t stackTop = thread->user_stack_base + thread->user_stack_size;
TRACE("arch_thread_enter_userspace: entry 0x%" B_PRIxADDR ", args %p %p, "
"ustack_top 0x%" B_PRIxADDR "\n", entry, args1, args2, stackTop);
addr_t commPageAddress = (addr_t)thread->team->commpage_address;
disable_interrupts();
iframe frame = {};
frame.r0 = (uint32)args1;
frame.r1 = (uint32)args2;
frame.usr_sp = stackTop;
frame.usr_lr = ((addr_t*)commPageAddress)[COMMPAGE_ENTRY_ARM_THREAD_EXIT]
+ commPageAddress;
frame.pc = entry;
arch_return_to_userland(&frame);
return B_ERROR;
}
bool
arch_on_signal_stack(Thread *thread)
{
struct iframe* frame = thread->arch_info.userFrame;
if (frame == NULL) {
panic("arch_on_signal_stack(): No user iframe!");
return false;
}
return frame->usr_sp >= thread->signal_stack_base
&& frame->usr_sp < thread->signal_stack_base
+ thread->signal_stack_size;
}
static uint8*
get_signal_stack(Thread* thread, struct iframe* frame,
struct sigaction* action, size_t spaceNeeded)
{
if (thread->signal_stack_enabled && (action->sa_flags & SA_ONSTACK) != 0
&& (frame->usr_sp < thread->signal_stack_base
|| frame->usr_sp >= thread->signal_stack_base + thread->signal_stack_size)) {
addr_t stackTop = thread->signal_stack_base + thread->signal_stack_size;
return (uint8*)ROUNDDOWN(stackTop - spaceNeeded, 16);
}
return (uint8*)ROUNDDOWN(frame->usr_sp - spaceNeeded, 16);
}
status_t
arch_setup_signal_frame(Thread *thread, struct sigaction *sa,
struct signal_frame_data *signalFrameData)
{
iframe* frame = thread->arch_info.userFrame;
if (frame == NULL) {
panic("arch_setup_signal_frame(): No user iframe!");
return B_ERROR;
}
signalFrameData->context.uc_mcontext.r0 = frame->r0;
signalFrameData->context.uc_mcontext.r1 = frame->r1;
signalFrameData->context.uc_mcontext.r2 = frame->r2;
signalFrameData->context.uc_mcontext.r3 = frame->r3;
signalFrameData->context.uc_mcontext.r4 = frame->r4;
signalFrameData->context.uc_mcontext.r5 = frame->r5;
signalFrameData->context.uc_mcontext.r6 = frame->r6;
signalFrameData->context.uc_mcontext.r7 = frame->r7;
signalFrameData->context.uc_mcontext.r8 = frame->r8;
signalFrameData->context.uc_mcontext.r9 = frame->r9;
signalFrameData->context.uc_mcontext.r10 = frame->r10;
signalFrameData->context.uc_mcontext.r11 = frame->r11;
signalFrameData->context.uc_mcontext.r12 = frame->r12;
signalFrameData->context.uc_mcontext.r13 = frame->usr_sp;
signalFrameData->context.uc_mcontext.r14 = frame->usr_lr;
signalFrameData->context.uc_mcontext.r15 = frame->pc;
signalFrameData->context.uc_mcontext.cpsr = frame->spsr;
arm_save_fpu((arch_fpu_context*)&signalFrameData->context.uc_mcontext.d[0]);
signal_get_user_stack(frame->usr_sp, &signalFrameData->context.uc_stack);
signalFrameData->syscall_restart_return_value = thread->arch_info.oldR0;
uint8* userStack = get_signal_stack(thread, frame, sa,
sizeof(*signalFrameData));
status_t res = user_memcpy(userStack, signalFrameData,
sizeof(*signalFrameData));
if (res < B_OK)
return res;
addr_t commpageAddr = (addr_t)thread->team->commpage_address;
addr_t signalHandlerAddr;
ASSERT(user_memcpy(&signalHandlerAddr,
&((addr_t*)commpageAddr)[COMMPAGE_ENTRY_ARM_SIGNAL_HANDLER],
sizeof(signalHandlerAddr)) >= B_OK);
signalHandlerAddr += commpageAddr;
frame->usr_lr = frame->pc;
frame->usr_sp = (addr_t)userStack;
frame->pc = signalHandlerAddr;
frame->r0 = frame->usr_sp;
return B_OK;
}
int64
arch_restore_signal_frame(struct signal_frame_data* signalFrameData)
{
iframe* frame = thread_get_current_thread()->arch_info.userFrame;
if (frame == NULL) {
panic("arch_restore_signal_frame(): No user iframe!");
return 0;
}
thread_get_current_thread()->arch_info.oldR0
= signalFrameData->syscall_restart_return_value;
frame->r0 = signalFrameData->context.uc_mcontext.r0;
frame->r1 = signalFrameData->context.uc_mcontext.r1;
frame->r2 = signalFrameData->context.uc_mcontext.r2;
frame->r3 = signalFrameData->context.uc_mcontext.r3;
frame->r4 = signalFrameData->context.uc_mcontext.r4;
frame->r5 = signalFrameData->context.uc_mcontext.r5;
frame->r6 = signalFrameData->context.uc_mcontext.r6;
frame->r7 = signalFrameData->context.uc_mcontext.r7;
frame->r8 = signalFrameData->context.uc_mcontext.r8;
frame->r9 = signalFrameData->context.uc_mcontext.r9;
frame->r10 = signalFrameData->context.uc_mcontext.r10;
frame->r11 = signalFrameData->context.uc_mcontext.r11;
frame->r12 = signalFrameData->context.uc_mcontext.r12;
frame->usr_sp = signalFrameData->context.uc_mcontext.r13;
frame->usr_lr = signalFrameData->context.uc_mcontext.r14;
frame->pc = signalFrameData->context.uc_mcontext.r15;
frame->spsr = signalFrameData->context.uc_mcontext.cpsr;
arm_restore_fpu((arch_fpu_context*)&signalFrameData->context.uc_mcontext.d[0]);
return frame->r0;
}
* arch_fork_arg structure to be passed to arch_restore_fork_frame().
* Also makes sure to return the right value.
*/
void
arch_store_fork_frame(struct arch_fork_arg *arg)
{
struct iframe* frame = thread_get_current_thread()->arch_info.userFrame;
if (frame == NULL) {
panic("arch_store_fork_frame(): No user iframe!");
}
arg->frame = *frame;
arg->frame.r0 = 0;
}
* arch_fork_arg structure.
* Needs to be called from within the child team, ie. instead of
* arch_thread_enter_uspace() as thread "starter".
* This function does not return to the caller, but will enter userland
* in the child team at the same position where the parent team left of.
*/
void
arch_restore_fork_frame(struct arch_fork_arg *arg)
{
disable_interrupts();
arch_return_to_userland(&arg->frame);
}