From 494e026ca715bd6370ee3842afca90abdf5217a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Holz?= Date: Tue, 6 Feb 2024 21:00:30 +0100 Subject: [PATCH] Kernel/riscv64: Implement Processor::init_context This code is based on the aarch64 implementation. --- Kernel/Arch/riscv64/Processor.cpp | 58 +++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/Kernel/Arch/riscv64/Processor.cpp b/Kernel/Arch/riscv64/Processor.cpp index 4ed0895ad3..b19e0c43f9 100644 --- a/Kernel/Arch/riscv64/Processor.cpp +++ b/Kernel/Arch/riscv64/Processor.cpp @@ -215,9 +215,63 @@ void ProcessorBase::assume_context(Thread&, InterruptsState) } template -FlatPtr ProcessorBase::init_context(Thread&, bool) +FlatPtr ProcessorBase::init_context(Thread& thread, bool leave_crit) { - TODO_RISCV64(); + VERIFY(g_scheduler_lock.is_locked()); + if (leave_crit) { + // Leave the critical section we set up in Process::exec, + // but because we still have the scheduler lock we should end up with 1 + VERIFY(in_critical() == 2); + m_in_critical = 1; // leave it without triggering anything or restoring flags + } + + u64 kernel_stack_top = thread.kernel_stack_top(); + + // Add a random offset between 0-256 (16-byte aligned) + kernel_stack_top -= round_up_to_power_of_two(get_fast_random(), 16); + + u64 stack_top = kernel_stack_top; + + auto& thread_regs = thread.regs(); + + // Push a RegisterState and TrapFrame onto the stack, which will be popped of the stack and restored into the + // state of the processor by restore_previous_context. + stack_top -= sizeof(RegisterState); + RegisterState& frame = *reinterpret_cast(stack_top); + memcpy(frame.x, thread_regs.x, sizeof(thread_regs.x)); + + // We don't overwrite the return address register if it's not 0, since that means this thread's register state was already initialized with + // an existing return address register value (e.g. it was fork()'ed), so we assume exit_kernel_thread is already saved as previous RA on the + // stack somewhere. + if (frame.x[0] == 0x0) { + // x1 is the return address register for the riscv64 ABI, so this will return to exit_kernel_thread when main thread function returns. + frame.x[0] = FlatPtr(&exit_kernel_thread); + } + frame.sepc = thread_regs.pc; + frame.set_userspace_sp(thread_regs.sp()); + frame.sstatus = thread_regs.sstatus; + + // Push a TrapFrame onto the stack + stack_top -= sizeof(TrapFrame); + TrapFrame& trap = *reinterpret_cast(stack_top); + trap.regs = &frame; + trap.next_trap = nullptr; + + if constexpr (CONTEXT_SWITCH_DEBUG) { + dbgln("init_context {} ({}) set up to execute at ip={}, sp={}, stack_top={}", + thread, + VirtualAddress(&thread), + VirtualAddress(thread_regs.pc), + VirtualAddress(thread_regs.sp()), + VirtualAddress(stack_top)); + } + + // This make sure the thread first executes thread_context_first_enter, which will actually call restore_previous_context + // which restores the context set up above. + thread_regs.set_sp(stack_top); + thread_regs.set_ip(FlatPtr(&thread_context_first_enter)); + + return stack_top; } // FIXME: Figure out if we can fully share this code with x86.