diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_thread_context.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_thread_context.hpp index fe46d2f25..23a8229e1 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_thread_context.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_thread_context.hpp @@ -66,6 +66,14 @@ namespace ams::kern::arch::arm64 { static void FpuContextSwitchHandler(KThread *thread); + u32 GetFpcr() const { return this->fpcr; } + u32 GetFpsr() const { return this->fpsr; } + + void SetFpcr(u32 v) { this->fpcr = v; } + void SetFpsr(u32 v) { this->fpsr = v; } + + void CloneFpuStatus(); + /* TODO: More methods (especially FPU management) */ }; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp index 13f3fa802..6974eb40c 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp @@ -64,7 +64,7 @@ namespace ams::kern { size_t system_resource_num_pages{}; size_t memory_release_hint{}; State state{}; - KLightLock lock{}; + KLightLock state_lock{}; KLightLock list_lock{}; KConditionVariable cond_var{}; KAddressArbiter address_arbiter{}; @@ -133,8 +133,11 @@ namespace ams::kern { constexpr u64 GetCoreMask() const { return this->capabilities.GetCoreMask(); } constexpr u64 GetPriorityMask() const { return this->capabilities.GetPriorityMask(); } + constexpr s32 GetIdealCoreId() const { return this->ideal_core_id; } constexpr void SetIdealCoreId(s32 core_id) { this->ideal_core_id = core_id; } + constexpr bool CheckThreadPriority(s32 prio) const { return ((1ul << prio) & this->GetPriorityMask()) != 0; } + constexpr bool Is64Bit() const { return this->flags & ams::svc::CreateProcessFlag_Is64Bit; } constexpr KProcessAddress GetEntryPoint() const { return this->code_address; } @@ -159,6 +162,9 @@ namespace ams::kern { void ReleaseResource(ams::svc::LimitableResource which, s64 value); void ReleaseResource(ams::svc::LimitableResource which, s64 value, s64 hint); + constexpr KLightLock &GetStateLock() { return this->state_lock; } + constexpr KLightLock &GetListLock() { return this->list_lock; } + constexpr KProcessPageTable &GetPageTable() { return this->page_table; } constexpr const KProcessPageTable &GetPageTable() const { return this->page_table; } diff --git a/libraries/libmesosphere/source/arch/arm64/kern_k_thread_context.cpp b/libraries/libmesosphere/source/arch/arm64/kern_k_thread_context.cpp index 67a18aee3..93cfc6c8f 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_k_thread_context.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_k_thread_context.cpp @@ -173,4 +173,19 @@ namespace ams::kern::arch::arm64 { } } + void KThreadContext::CloneFpuStatus() { + u64 pcr, psr; + cpu::InstructionMemoryBarrier(); + if (IsFpuEnabled()) { + __asm__ __volatile__("mrs %[pcr], fpcr" : [pcr]"=r"(pcr) :: "memory"); + __asm__ __volatile__("mrs %[psr], fpsr" : [psr]"=r"(psr) :: "memory"); + } else { + pcr = GetCurrentThread().GetContext().GetFpcr(); + psr = GetCurrentThread().GetContext().GetFpsr(); + } + + this->SetFpcr(pcr); + this->SetFpsr(psr); + } + } diff --git a/libraries/libmesosphere/source/kern_k_process.cpp b/libraries/libmesosphere/source/kern_k_process.cpp index 1268c1e4d..e178730d5 100644 --- a/libraries/libmesosphere/source/kern_k_process.cpp +++ b/libraries/libmesosphere/source/kern_k_process.cpp @@ -306,7 +306,7 @@ namespace ams::kern { MESOSPHERE_ASSERT_THIS(); /* Lock ourselves, to prevent concurrent access. */ - KScopedLightLock lk(this->lock); + KScopedLightLock lk(this->state_lock); /* Validate that we're in a state where we can initialize. */ const auto state = this->state; diff --git a/libraries/libmesosphere/source/svc/kern_svc_thread.cpp b/libraries/libmesosphere/source/svc/kern_svc_thread.cpp index 8e920b799..6c4da6d7a 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_thread.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_thread.cpp @@ -21,6 +21,66 @@ namespace ams::kern::svc { namespace { + constexpr bool IsValidCoreId(int32_t core_id) { + return (0 <= core_id && core_id < static_cast(cpu::NumCores)); + } + + Result CreateThread(ams::svc::Handle *out, ams::svc::ThreadFunc f, uintptr_t arg, uintptr_t stack_bottom, int32_t priority, int32_t core_id) { + /* Adjust core id, if it's the default magic. */ + KProcess &process = GetCurrentProcess(); + if (core_id == ams::svc::IdealCoreUseProcessValue) { + core_id = process.GetIdealCoreId(); + } + + /* Validate arguments. */ + R_UNLESS(IsValidCoreId(core_id), svc::ResultInvalidCoreId()); + R_UNLESS(((1ul << core_id) & process.GetCoreMask()) != 0, svc::ResultInvalidCoreId()); + + R_UNLESS(ams::svc::HighestThreadPriority <= priority && priority <= ams::svc::LowestThreadPriority, svc::ResultInvalidPriority()); + R_UNLESS(process.CheckThreadPriority(priority), svc::ResultInvalidPriority()); + + /* Reserve a new session from the process resource limit (waiting up to 100ms). */ + KScopedResourceReservation thread_reservation(std::addressof(process), ams::svc::LimitableResource_ThreadCountMax, 1, KHardwareTimer::GetTick() + ams::svc::Tick(TimeSpan::FromMilliSeconds(100))); + R_UNLESS(thread_reservation.Succeeded(), svc::ResultLimitReached()); + + /* Create the thread. */ + KScopedAutoObject thread = KThread::Create(); + R_UNLESS(thread.IsNotNull(), svc::ResultOutOfResource()); + + /* Initialize the thread. */ + { + KScopedLightLock lk(process.GetStateLock()); + R_TRY(KThread::InitializeUserThread(thread.GetPointerUnsafe(), reinterpret_cast(static_cast(f)), arg, stack_bottom, priority, core_id, std::addressof(process))); + } + + /* Commit the thread reservation. */ + thread_reservation.Commit(); + + /* Clone the current fpu status to the new thread. */ + thread->GetContext().CloneFpuStatus(); + + /* Register the new thread. */ + R_TRY(KThread::Register(thread.GetPointerUnsafe())); + + /* Add the thread to the handle table. */ + R_TRY(process.GetHandleTable().Add(out, thread.GetPointerUnsafe())); + + return ResultSuccess(); + } + + Result StartThread(ams::svc::Handle thread_handle) { + /* Get the thread from its handle. */ + KScopedAutoObject thread = GetCurrentProcess().GetHandleTable().GetObject(thread_handle); + R_UNLESS(thread.IsNotNull(), svc::ResultInvalidHandle()); + + /* Try to start the thread. */ + R_TRY(thread->Run()); + + /* If we succeeded, persist a reference to the thread. */ + thread->Open(); + return ResultSuccess(); + } + Result GetThreadPriority(int32_t *out_priority, ams::svc::Handle thread_handle) { /* Get the thread from its handle. */ KScopedAutoObject thread = GetCurrentProcess().GetHandleTable().GetObject(thread_handle); @@ -46,11 +106,11 @@ namespace ams::kern::svc { /* ============================= 64 ABI ============================= */ Result CreateThread64(ams::svc::Handle *out_handle, ams::svc::ThreadFunc func, ams::svc::Address arg, ams::svc::Address stack_bottom, int32_t priority, int32_t core_id) { - MESOSPHERE_PANIC("Stubbed SvcCreateThread64 was called."); + return CreateThread(out_handle, func, arg, stack_bottom, priority, core_id); } Result StartThread64(ams::svc::Handle thread_handle) { - MESOSPHERE_PANIC("Stubbed SvcStartThread64 was called."); + return StartThread(thread_handle); } void ExitThread64() { @@ -100,11 +160,11 @@ namespace ams::kern::svc { /* ============================= 64From32 ABI ============================= */ Result CreateThread64From32(ams::svc::Handle *out_handle, ams::svc::ThreadFunc func, ams::svc::Address arg, ams::svc::Address stack_bottom, int32_t priority, int32_t core_id) { - MESOSPHERE_PANIC("Stubbed SvcCreateThread64From32 was called."); + return CreateThread(out_handle, func, arg, stack_bottom, priority, core_id); } Result StartThread64From32(ams::svc::Handle thread_handle) { - MESOSPHERE_PANIC("Stubbed SvcStartThread64From32 was called."); + return StartThread(thread_handle); } void ExitThread64From32() {