From 1de607c1839104d8f0046973b1da6666368d11d6 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 6 Feb 2020 05:34:38 -0800 Subject: [PATCH] kern: implement KResourceLimit --- .../libmesosphere/include/mesosphere.hpp | 1 + .../arch/arm64/kern_k_hardware_timer.hpp | 10 ++ .../kern_k_light_condition_variable.hpp | 66 ++++++++ .../mesosphere/kern_k_memory_layout.hpp | 23 ++- .../mesosphere/kern_k_resource_limit.hpp | 55 ++++++ .../include/mesosphere/kern_k_scheduler.hpp | 2 + ...kern_k_scoped_scheduler_lock_and_sleep.hpp | 55 ++++++ .../include/mesosphere/kern_k_thread.hpp | 14 +- .../mesosphere/kern_k_thread_queue.hpp | 132 +++++++++++++++ .../include/mesosphere/kern_kernel.hpp | 20 ++- .../include/mesosphere/kern_panic.hpp | 4 +- .../nintendo/switch/kern_k_system_control.cpp | 22 ++- .../libmesosphere/source/kern_debug_log.cpp | 7 +- .../source/kern_k_resource_limit.cpp | 160 ++++++++++++++++++ .../libmesosphere/source/kern_k_scheduler.cpp | 10 +- .../libmesosphere/source/kern_kernel.cpp | 6 + libraries/libmesosphere/source/kern_main.cpp | 1 + 17 files changed, 566 insertions(+), 22 deletions(-) create mode 100644 libraries/libmesosphere/include/mesosphere/kern_k_light_condition_variable.hpp create mode 100644 libraries/libmesosphere/include/mesosphere/kern_k_resource_limit.hpp create mode 100644 libraries/libmesosphere/include/mesosphere/kern_k_scoped_scheduler_lock_and_sleep.hpp create mode 100644 libraries/libmesosphere/include/mesosphere/kern_k_thread_queue.hpp create mode 100644 libraries/libmesosphere/source/kern_k_resource_limit.cpp diff --git a/libraries/libmesosphere/include/mesosphere.hpp b/libraries/libmesosphere/include/mesosphere.hpp index 3029090b1..67b6b2e1f 100644 --- a/libraries/libmesosphere/include/mesosphere.hpp +++ b/libraries/libmesosphere/include/mesosphere.hpp @@ -54,6 +54,7 @@ #include #include #include +#include /* Supervisor Calls. */ #include diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_hardware_timer.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_hardware_timer.hpp index 3ce38207e..9b9ee533f 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_hardware_timer.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_hardware_timer.hpp @@ -36,6 +36,16 @@ namespace ams::kern::arm64 { static s64 GetTick() { return GetCount(); } + + void RegisterAbsoluteTask(KTimerTask *task, s64 task_time) { + KScopedDisableDispatch dd; + KScopedSpinLock lk(this->GetLock()); + + if (this->RegisterAbsoluteTaskImpl(task, task_time)) { + SetCompareValue(task_time); + EnableInterrupt(); + } + } private: friend class impl::KHardwareTimerInterruptTask; NOINLINE void DoInterruptTask(); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_light_condition_variable.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_light_condition_variable.hpp new file mode 100644 index 000000000..381881b63 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_k_light_condition_variable.hpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include +#include + +namespace ams::kern { + + class KLightConditionVariable { + private: + KThreadQueue thread_queue; + public: + constexpr ALWAYS_INLINE KLightConditionVariable() : thread_queue() { /* ... */ } + private: + void WaitImpl(KLightLock *lock, s64 timeout) { + KThread *owner = GetCurrentThreadPointer(); + KHardwareTimer *timer; + + /* Sleep the thread. */ + { + KScopedSchedulerLockAndSleep lk(&timer, owner, timeout); + lock->Unlock(); + + if (!this->thread_queue.SleepThread(owner)) { + lk.CancelSleep(); + return; + } + } + + /* Cancel the task that the sleep setup. */ + if (timer != nullptr) { + timer->CancelTask(owner); + } + } + public: + void Wait(KLightLock *lock, s64 timeout) { + this->WaitImpl(lock, timeout); + lock->Lock(); + } + + void Broadcast() { + KScopedSchedulerLock lk; + while (this->thread_queue.WakeupFrontThread() != nullptr) { + /* We want to signal all threads, and so should continue waking up until there's nothing to wake. */ + } + } + + }; + +} diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp index c1c0d2dfb..ce3482a78 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp @@ -82,14 +82,19 @@ namespace ams::kern { KMemoryRegionType_DramLinearMapped = KMemoryRegionType_Dram | KMemoryRegionAttr_LinearMapped, - KMemoryRegionType_DramReservedEarly = 0x16 | KMemoryRegionAttr_NoUserMap, - KMemoryRegionType_DramPoolPartition = 0x26 | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped, + KMemoryRegionType_DramReservedEarly = 0x16 | KMemoryRegionAttr_NoUserMap, + KMemoryRegionType_DramPoolPartition = 0x26 | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped, KMemoryRegionType_DramMetadataPool = 0x166 | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_CarveoutProtected, + + KMemoryRegionType_DramNonKernel = 0x1A6 | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped, + KMemoryRegionType_DramApplicationPool = 0x7A6 | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped, KMemoryRegionType_DramAppletPool = 0xBA6 | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped, KMemoryRegionType_DramSystemNonSecurePool = 0xDA6 | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped, KMemoryRegionType_DramSystemPool = 0x13A6 | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_CarveoutProtected, + + KMemoryRegionType_DramKernel = 0xE | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_CarveoutProtected, KMemoryRegionType_DramKernelCode = 0xCE | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_CarveoutProtected, KMemoryRegionType_DramKernelSlab = 0x14E | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_CarveoutProtected, @@ -274,7 +279,6 @@ namespace ams::kern { MESOSPHERE_INIT_ABORT(); } - DerivedRegionExtents GetDerivedRegionExtents(u32 type_id) { DerivedRegionExtents extents; @@ -462,6 +466,19 @@ namespace ams::kern { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionAttr_CarveoutProtected); } + static NOINLINE std::tuple GetTotalAndKernelMemorySizes() { + size_t total_size = 0, kernel_size = 0; + for (auto it = GetPhysicalMemoryRegionTree().cbegin(); it != GetPhysicalMemoryRegionTree().cend(); it++) { + if (it->IsDerivedFrom(KMemoryRegionType_Dram)) { + total_size += it->GetSize(); + if (!it->IsDerivedFrom(KMemoryRegionType_DramNonKernel)) { + kernel_size += it->GetSize(); + } + } + } + return std::make_tuple(total_size, kernel_size); + } + static void InitializeLinearMemoryRegionTrees(KPhysicalAddress aligned_linear_phys_start, KVirtualAddress linear_virtual_start); }; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_resource_limit.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_resource_limit.hpp new file mode 100644 index 000000000..bfcfcb4fb --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_k_resource_limit.hpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include +#include + +namespace ams::kern { + + class KResourceLimit final : public KAutoObjectWithSlabHeapAndContainer { + MESOSPHERE_AUTOOBJECT_TRAITS(KResourceLimit, KAutoObject); + private: + s64 limit_values[ams::svc::LimitableResource_Count]; + s64 current_values[ams::svc::LimitableResource_Count]; + s64 current_hints[ams::svc::LimitableResource_Count]; + mutable KLightLock lock; + s32 waiter_count; + KLightConditionVariable cond_var; + public: + constexpr ALWAYS_INLINE KResourceLimit() : limit_values(), current_values(), current_hints(), lock(), waiter_count(), cond_var() { /* ... */ } + virtual ~KResourceLimit() { /* ... */ } + + static ALWAYS_INLINE void PostDestroy(uintptr_t arg) { /* ... */ } + + void Initialize(); + virtual void Finalize() override; + + s64 GetLimitValue(ams::svc::LimitableResource which) const; + s64 GetCurrentValue(ams::svc::LimitableResource which) const; + s64 GetFreeValue(ams::svc::LimitableResource which) const; + + Result SetLimitValue(ams::svc::LimitableResource which, s64 value); + + bool Reserve(ams::svc::LimitableResource which, s64 value); + bool Reserve(ams::svc::LimitableResource which, s64 value, s64 timeout); + void Release(ams::svc::LimitableResource which, s64 value); + void Release(ams::svc::LimitableResource which, s64 value, s64 hint); + }; + +} diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp index fe5cc656a..4a4ff27ca 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp @@ -27,6 +27,7 @@ namespace ams::kern { static_assert(KSchedulerPriorityQueue::NumPriority == BITSIZEOF(u64)); class KScopedSchedulerLock; + class KScopedSchedulerLockAndSleep; class KScheduler { NON_COPYABLE(KScheduler); @@ -48,6 +49,7 @@ namespace ams::kern { }; private: friend class KScopedSchedulerLock; + friend class KScopedSchedulerLockAndSleep; static inline bool s_scheduler_update_needed; static inline LockType s_scheduler_lock; static inline KSchedulerPriorityQueue s_priority_queue; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_scoped_scheduler_lock_and_sleep.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_scoped_scheduler_lock_and_sleep.hpp new file mode 100644 index 000000000..f7f973b11 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_k_scoped_scheduler_lock_and_sleep.hpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::kern { + + class KScopedSchedulerLockAndSleep { + private: + s64 timeout_tick; + KThread *thread; + KHardwareTimer *timer; + public: + explicit ALWAYS_INLINE KScopedSchedulerLockAndSleep(KHardwareTimer **out_timer, KThread *t, s64 timeout) : timeout_tick(timeout), thread(t) { + /* Lock the scheduler. */ + KScheduler::s_scheduler_lock.Lock(); + + /* Set our timer only if the absolute time is positive. */ + this->timer = (this->timeout_tick > 0) ? std::addressof(Kernel::GetHardwareTimer()) : nullptr; + + *out_timer = this->timer; + } + + ALWAYS_INLINE ~KScopedSchedulerLockAndSleep() { + /* Register the sleep. */ + if (this->timeout_tick > 0) { + this->timer->RegisterAbsoluteTask(this->thread, this->timeout_tick); + } + + /* Unlock the scheduler. */ + KScheduler::s_scheduler_lock.Unlock(); + } + + ALWAYS_INLINE void CancelSleep() { + this->timeout_tick = 0; + } + }; + +} diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp index b2e37bd0f..ea73235de 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp @@ -24,6 +24,8 @@ namespace ams::kern { + class KThreadQueue; + using KThreadFunction = void (*)(uintptr_t); class KThread final : public KAutoObjectWithSlabHeapAndContainer, public KTimerTask, public KWorkerTask { @@ -133,7 +135,7 @@ namespace ams::kern { s64 last_scheduled_tick; QueueEntry per_core_priority_queue_entry[cpu::NumCores]; QueueEntry sleeping_queue_entry; - void /* TODO KThreadQueue */ *sleeping_queue; + KThreadQueue *sleeping_queue; util::IntrusiveListNode waiter_list_node; util::IntrusiveRedBlackTreeNode condvar_arbiter_tree_node; util::IntrusiveListNode process_list_node; @@ -204,8 +206,8 @@ namespace ams::kern { } public: constexpr ALWAYS_INLINE const KAffinityMask &GetAffinityMask() const { return this->affinity_mask; } - constexpr ALWAYS_INLINE ThreadState GetThreadState() const { return static_cast(this->thread_state & ThreadState_Mask); } - constexpr ALWAYS_INLINE ThreadState GetRawThreadState() const { return this->thread_state; } + constexpr ALWAYS_INLINE ThreadState GetState() const { return static_cast(this->thread_state & ThreadState_Mask); } + constexpr ALWAYS_INLINE ThreadState GetRawState() const { return this->thread_state; } NOINLINE void SetState(ThreadState state); NOINLINE KThreadContext *GetContextForSchedulerLoop(); @@ -217,6 +219,10 @@ namespace ams::kern { constexpr ALWAYS_INLINE QueueEntry &GetPriorityQueueEntry(s32 core) { return this->per_core_priority_queue_entry[core]; } constexpr ALWAYS_INLINE const QueueEntry &GetPriorityQueueEntry(s32 core) const { return this->per_core_priority_queue_entry[core]; } + constexpr ALWAYS_INLINE QueueEntry &GetSleepingQueueEntry() { return this->sleeping_queue_entry; } + constexpr ALWAYS_INLINE const QueueEntry &GetSleepingQueueEntry() const { return this->sleeping_queue_entry; } + constexpr ALWAYS_INLINE void SetSleepingQueue(KThreadQueue *q) { this->sleeping_queue = q; } + constexpr ALWAYS_INLINE s32 GetNumKernelWaiters() const { return this->num_kernel_waiters; } constexpr ALWAYS_INLINE s64 GetLastScheduledTick() const { return this->last_scheduled_tick; } @@ -245,7 +251,7 @@ namespace ams::kern { } ALWAYS_INLINE bool IsTerminationRequested() const { - return this->termination_requested || this->GetRawThreadState() == ThreadState_Terminated; + return this->termination_requested || this->GetRawState() == ThreadState_Terminated; } public: diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_thread_queue.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_thread_queue.hpp new file mode 100644 index 000000000..e56042750 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_k_thread_queue.hpp @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::kern { + + class KThreadQueue { + private: + using Entry = KThread::QueueEntry; + private: + Entry root; + public: + constexpr ALWAYS_INLINE KThreadQueue() : root() { /* ... */ } + + constexpr ALWAYS_INLINE bool IsEmpty() const { return this->root.GetNext() == nullptr; } + + constexpr ALWAYS_INLINE KThread *GetFront() const { return this->root.GetNext(); } + constexpr ALWAYS_INLINE KThread *GetNext(KThread *t) const { return t->GetSleepingQueueEntry().GetNext(); } + private: + constexpr ALWAYS_INLINE KThread *GetBack() const { return this->root.GetPrev(); } + + constexpr ALWAYS_INLINE void Enqueue(KThread *add) { + /* Get the entry associated with the added thread. */ + Entry &add_entry = add->GetSleepingQueueEntry(); + + /* Get the entry associated with the end of the queue. */ + KThread *tail = this->GetBack(); + Entry &tail_entry = (tail != nullptr) ? tail->GetSleepingQueueEntry() : this->root; + + /* Link the entries. */ + add_entry.SetPrev(tail); + add_entry.SetNext(nullptr); + tail_entry.SetNext(add); + this->root.SetPrev(add); + } + + constexpr ALWAYS_INLINE void Remove(KThread *remove) { + /* Get the entry associated with the thread. */ + Entry &remove_entry = remove->GetSleepingQueueEntry(); + + /* Get the entries associated with next and prev. */ + KThread *prev = remove_entry.GetPrev(); + KThread *next = remove_entry.GetNext(); + Entry &prev_entry = (prev != nullptr) ? prev->GetSleepingQueueEntry() : this->root; + Entry &next_entry = (next != nullptr) ? next->GetSleepingQueueEntry() : this->root; + + /* Unlink. */ + prev_entry.SetNext(next); + next_entry.SetPrev(prev); + } + public: + constexpr ALWAYS_INLINE void Dequeue() { + /* Get the front of the queue. */ + KThread *head = this->GetFront(); + if (head == nullptr) { + return; + } + + MESOSPHERE_ASSERT(head->GetState() == KThread::ThreadState_Waiting); + + /* Get the entry for the next head. */ + KThread *next = GetNext(head); + Entry &next_entry = (next != nullptr) ? next->GetSleepingQueueEntry() : this->root; + + /* Link the entries. */ + this->root.SetNext(next); + next_entry.SetPrev(nullptr); + + /* Clear the head's queue. */ + head->SetSleepingQueue(nullptr); + } + + bool SleepThread(KThread *t) { + /* Set the thread's queue and mark it as waiting. */ + t->SetSleepingQueue(this); + t->SetState(KThread::ThreadState_Waiting); + + /* Add the thread to the queue. */ + this->Enqueue(t); + + /* If the thread needs terminating, undo our work. */ + if (t->IsTerminationRequested()) { + this->WakeupThread(t); + return false; + } + + return true; + } + + void WakeupThread(KThread *t) { + MESOSPHERE_ASSERT(t->GetState() == KThread::ThreadState_Waiting); + + /* Remove the thread from the queue. */ + this->Remove(t); + + /* Mark the thread as no longer sleeping. */ + t->SetState(KThread::ThreadState_Runnable); + t->SetSleepingQueue(nullptr); + } + + KThread *WakeupFrontThread() { + KThread *front = this->GetFront(); + if (front != nullptr) { + MESOSPHERE_ASSERT(front->GetState() == KThread::ThreadState_Waiting); + + /* Remove the thread from the queue. */ + this->Dequeue(); + + /* Mark the thread as no longer sleeping. */ + front->SetState(KThread::ThreadState_Runnable); + front->SetSleepingQueue(nullptr); + } + return front; + } + }; + +} diff --git a/libraries/libmesosphere/include/mesosphere/kern_kernel.hpp b/libraries/libmesosphere/include/mesosphere/kern_kernel.hpp index 6f94b3ac3..0289b5ddb 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_kernel.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_kernel.hpp @@ -20,11 +20,16 @@ #include #include #include -#include -#include namespace ams::kern { + class KThread; + class KHardwareTimer; + class KResourceLimit; + class KInterruptManager; + class KInterruptTaskManager; + class KScheduler; + class Kernel { public: enum class State : u8 { @@ -33,9 +38,10 @@ namespace ams::kern { Initialized = 2, }; private: - static inline State s_state = State::Invalid; - static inline KThread s_main_threads[cpu::NumCores]; - static inline KThread s_idle_threads[cpu::NumCores]; + static State s_state; + static KThread s_main_threads[cpu::NumCores]; + static KThread s_idle_threads[cpu::NumCores]; + static KResourceLimit s_system_resource_limit; private: static ALWAYS_INLINE KCoreLocalContext &GetCoreLocalContext() { return reinterpret_cast(cpu::GetCoreLocalRegionAddress())->current.context; @@ -77,6 +83,10 @@ namespace ams::kern { static ALWAYS_INLINE KHardwareTimer &GetHardwareTimer() { return GetCoreLocalContext().hardware_timer; } + + static ALWAYS_INLINE KResourceLimit &GetSystemResourceLimit() { + return s_system_resource_limit; + } }; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_panic.hpp b/libraries/libmesosphere/include/mesosphere/kern_panic.hpp index d7af5acb9..0ce6332f8 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_panic.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_panic.hpp @@ -69,8 +69,8 @@ namespace ams::kern { #define MESOSPHERE_R_ABORT_UNLESS(expr) \ ({ \ - const ::ams::Result _tmp_meso_r_abort_res = static_cast<::ams::Result>(expr); \ - if (AMS_UNLIKELY((R_FAILED(_tmp_meso_r_abort_res))) { \ + const ::ams::Result _tmp_meso_r_abort_res = static_cast<::ams::Result>((expr)); \ + if (AMS_UNLIKELY((R_FAILED(_tmp_meso_r_abort_res)))) { \ MESOSPHERE_PANIC("Result Abort(): %s 2%03d-%04d", #expr, _tmp_meso_r_abort_res.GetModule(), _tmp_meso_r_abort_res.GetDescription()); \ } \ }) diff --git a/libraries/libmesosphere/source/board/nintendo/switch/kern_k_system_control.cpp b/libraries/libmesosphere/source/board/nintendo/switch/kern_k_system_control.cpp index 17f8e1f11..9c6c0c7ee 100644 --- a/libraries/libmesosphere/source/board/nintendo/switch/kern_k_system_control.cpp +++ b/libraries/libmesosphere/source/board/nintendo/switch/kern_k_system_control.cpp @@ -199,7 +199,7 @@ namespace ams::kern { g_call_smc_on_panic = kernel_config.Get(); } - /* Set Program Verification. */ + /* Set Kernel Debugging. */ { /* NOTE: This is used to restrict access to SvcKernelDebug/SvcChangeKernelTraceState. */ /* Mesosphere may wish to not require this, as we'd ideally keep ProgramVerification enabled for userland. */ @@ -212,7 +212,25 @@ namespace ams::kern { smc::ConfigureCarveout(0, carveout.GetAddress(), carveout.GetSize()); } - /* TODO: KResourceLimit initialization. */ + /* System ResourceLimit initialization. */ + { + /* Construct the resource limit object. */ + KResourceLimit &sys_res_limit = Kernel::GetSystemResourceLimit(); + KAutoObject::Create(std::addressof(sys_res_limit)); + sys_res_limit.Initialize(); + + /* Set the initial limits. */ + const auto [total_memory_size, kernel_memory_size] = KMemoryLayout::GetTotalAndKernelMemorySizes(); + const auto &slab_counts = init::GetSlabResourceCounts(); + MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_PhysicalMemoryMax, total_memory_size)); + MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_ThreadCountMax, slab_counts.num_KThread)); + MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_EventCountMax, slab_counts.num_KEvent)); + MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_TransferMemoryCountMax, slab_counts.num_KTransferMemory)); + MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_SessionCountMax, slab_counts.num_KSession)); + + /* Reserve system memory. */ + MESOSPHERE_ABORT_UNLESS(sys_res_limit.Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, kernel_memory_size)); + } } /* Randomness. */ diff --git a/libraries/libmesosphere/source/kern_debug_log.cpp b/libraries/libmesosphere/source/kern_debug_log.cpp index 503313c0b..45a53f534 100644 --- a/libraries/libmesosphere/source/kern_debug_log.cpp +++ b/libraries/libmesosphere/source/kern_debug_log.cpp @@ -17,6 +17,8 @@ #include "kern_debug_log_impl.hpp" namespace ams::kern { + #pragma GCC push_options + #pragma GCC optimize ("-Os") namespace { @@ -328,7 +330,7 @@ namespace ams::kern { n = static_cast(va_arg(vl, signed long long)); } else if (HasFlag(FormatSpecifierFlag_Long)) { n = static_cast(va_arg(vl, signed long)); - } if (HasFlag(FormatSpecifierFlag_Char)) { + } else if (HasFlag(FormatSpecifierFlag_Char)) { n = static_cast(va_arg(vl, signed int)); } else if (HasFlag(FormatSpecifierFlag_Short)) { n = static_cast(va_arg(vl, signed int)); @@ -416,6 +418,9 @@ namespace ams::kern { } + + #pragma GCC pop_options + void KDebugLog::Initialize() { if (KTargetSystem::IsDebugLoggingEnabled()) { KScopedInterruptDisable di; diff --git a/libraries/libmesosphere/source/kern_k_resource_limit.cpp b/libraries/libmesosphere/source/kern_k_resource_limit.cpp new file mode 100644 index 000000000..25ee89f82 --- /dev/null +++ b/libraries/libmesosphere/source/kern_k_resource_limit.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::kern { + + void KResourceLimit::Initialize() { + /* This should be unnecessary for us, because our constructor will clear all fields. */ + /* The following is analagous to what Nintendo's implementation (no constexpr constructor) would do, though. */ + /* + this->waiter_count = 0; + for (size_t i = 0; i < util::size(this->limit_values); i++) { + this->limit_values[i] = 0; + this->current_values[i] = 0; + this->current_hints[i] = 0; + } + */ + } + + void KResourceLimit::Finalize() { + /* ... */ + } + + s64 KResourceLimit::GetLimitValue(ams::svc::LimitableResource which) const { + MESOSPHERE_ASSERT_THIS(); + + s64 value; + { + KScopedLightLock lk(this->lock); + value = this->limit_values[which]; + MESOSPHERE_ASSERT(value >= 0); + MESOSPHERE_ASSERT(this->current_values[which] <= this->limit_values[which]); + MESOSPHERE_ASSERT(this->current_hints[which] <= this->current_values[which]); + } + + return value; + } + + s64 KResourceLimit::GetCurrentValue(ams::svc::LimitableResource which) const { + MESOSPHERE_ASSERT_THIS(); + + s64 value; + { + KScopedLightLock lk(this->lock); + value = this->current_values[which]; + MESOSPHERE_ASSERT(value >= 0); + MESOSPHERE_ASSERT(this->current_values[which] <= this->limit_values[which]); + MESOSPHERE_ASSERT(this->current_hints[which] <= this->current_values[which]); + } + + return value; + } + + s64 KResourceLimit::GetFreeValue(ams::svc::LimitableResource which) const { + MESOSPHERE_ASSERT_THIS(); + + s64 value; + { + KScopedLightLock lk(this->lock); + MESOSPHERE_ASSERT(this->current_values[which] >= 0); + MESOSPHERE_ASSERT(this->current_values[which] <= this->limit_values[which]); + MESOSPHERE_ASSERT(this->current_hints[which] <= this->current_values[which]); + value = this->limit_values[which] - this->current_values[which]; + } + + return value; + } + + Result KResourceLimit::SetLimitValue(ams::svc::LimitableResource which, s64 value) { + MESOSPHERE_ASSERT_THIS(); + + KScopedLightLock lk(this->lock); + R_UNLESS(this->current_values[which] <= value, svc::ResultInvalidState()); + + this->limit_values[which] = value; + + return ResultSuccess(); + } + + bool KResourceLimit::Reserve(ams::svc::LimitableResource which, s64 value) { + /* TODO: constexpr definition for this default timeout (it's 10 seconds) */ + return this->Reserve(which, value, KHardwareTimer::GetTick() + 192'000'000); + } + + bool KResourceLimit::Reserve(ams::svc::LimitableResource which, s64 value, s64 timeout) { + MESOSPHERE_ASSERT_THIS(); + MESOSPHERE_ASSERT(value >= 0); + + KScopedLightLock lk(this->lock); + + MESOSPHERE_ASSERT(this->current_hints[which] <= this->current_values[which]); + if (this->current_hints[which] >= this->limit_values[which]) { + return false; + } + + /* Loop until we reserve or run out of time. */ + while (true) { + MESOSPHERE_ASSERT(this->current_values[which] <= this->limit_values[which]); + MESOSPHERE_ASSERT(this->current_hints[which] <= this->current_values[which]); + + /* If we would overflow, don't allow to succeed. */ + if (this->current_values[which] + value <= this->current_values[which]) { + break; + } + + if (this->current_values[which] + value <= this->limit_values[which]) { + this->current_values[which] += value; + this->current_hints[which] += value; + return true; + } + + if (this->current_hints[which] + value <= this->limit_values[which] && (timeout < 0 || KHardwareTimer::GetTick() < timeout)) { + this->waiter_count++; + this->cond_var.Wait(&this->lock, timeout); + this->waiter_count--; + } else { + break; + } + } + + return false; + } + + void KResourceLimit::Release(ams::svc::LimitableResource which, s64 value) { + this->Release(which, value, value); + } + + void KResourceLimit::Release(ams::svc::LimitableResource which, s64 value, s64 hint) { + MESOSPHERE_ASSERT_THIS(); + MESOSPHERE_ASSERT(value >= 0); + MESOSPHERE_ASSERT(hint >= 0); + + KScopedLightLock lk(this->lock); + MESOSPHERE_ASSERT(this->current_values[which] <= this->limit_values[which]); + MESOSPHERE_ASSERT(this->current_hints[which] <= this->current_values[which]); + MESOSPHERE_ASSERT(value <= this->current_values[which]); + MESOSPHERE_ASSERT(hint <= this->current_hints[which]); + + this->current_values[which] -= value; + this->current_hints[which] -= hint; + + if (this->waiter_count != 0) { + this->cond_var.Broadcast(); + } + } + +} diff --git a/libraries/libmesosphere/source/kern_k_scheduler.cpp b/libraries/libmesosphere/source/kern_k_scheduler.cpp index fa43e595b..a29de6407 100644 --- a/libraries/libmesosphere/source/kern_k_scheduler.cpp +++ b/libraries/libmesosphere/source/kern_k_scheduler.cpp @@ -119,7 +119,7 @@ namespace ams::kern { if (KProcess *parent = top_thread->GetOwnerProcess(); parent != nullptr) { if (KThread *suggested = parent->GetSuggestedTopThread(core_id); suggested != nullptr && suggested != top_thread) { /* We prefer our parent's suggestion whenever possible. However, we also don't want to schedule un-runnable threads. */ - if (suggested->GetRawThreadState() == KThread::ThreadState_Runnable) { + if (suggested->GetRawState() == KThread::ThreadState_Runnable) { top_thread = suggested; } else { top_thread = nullptr; @@ -201,7 +201,7 @@ namespace ams::kern { KThread *task_thread = Kernel::GetInterruptTaskManager().GetThread(); { KScopedSchedulerLock sl; - if (AMS_LIKELY(task_thread->GetThreadState() == KThread::ThreadState_Waiting)) { + if (AMS_LIKELY(task_thread->GetState() == KThread::ThreadState_Waiting)) { task_thread->SetState(KThread::ThreadState_Runnable); } } @@ -259,7 +259,7 @@ namespace ams::kern { MESOSPHERE_ASSERT(IsSchedulerLockedByCurrentThread()); /* Check if the state has changed, because if it hasn't there's nothing to do. */ - const KThread::ThreadState cur_state = thread->GetRawThreadState(); + const KThread::ThreadState cur_state = thread->GetRawState(); if (cur_state == old_state) { return; } @@ -282,7 +282,7 @@ namespace ams::kern { MESOSPHERE_ASSERT(IsSchedulerLockedByCurrentThread()); /* If the thread is runnable, we want to change its priority in the queue. */ - if (thread->GetRawThreadState() == KThread::ThreadState_Runnable) { + if (thread->GetRawState() == KThread::ThreadState_Runnable) { GetPriorityQueue().ChangePriority(old_priority, thread == GetCurrentThreadPointer(), thread); IncrementScheduledCount(thread); SetSchedulerUpdateNeeded(); @@ -293,7 +293,7 @@ namespace ams::kern { MESOSPHERE_ASSERT(IsSchedulerLockedByCurrentThread()); /* If the thread is runnable, we want to change its affinity in the queue. */ - if (thread->GetRawThreadState() == KThread::ThreadState_Runnable) { + if (thread->GetRawState() == KThread::ThreadState_Runnable) { GetPriorityQueue().ChangeAffinityMask(old_core, old_affinity, thread); IncrementScheduledCount(thread); SetSchedulerUpdateNeeded(); diff --git a/libraries/libmesosphere/source/kern_kernel.cpp b/libraries/libmesosphere/source/kern_kernel.cpp index 4d88413cf..869b5ad19 100644 --- a/libraries/libmesosphere/source/kern_kernel.cpp +++ b/libraries/libmesosphere/source/kern_kernel.cpp @@ -17,6 +17,12 @@ namespace ams::kern { + /* Declare kernel data members in kernel TU. */ + Kernel::State Kernel::s_state = Kernel::State::Invalid; + KThread Kernel::s_main_threads[cpu::NumCores]; + KThread Kernel::s_idle_threads[cpu::NumCores]; + KResourceLimit Kernel::s_system_resource_limit; + void Kernel::InitializeCoreLocalRegion(s32 core_id) { /* Construct the core local region object in place. */ KCoreLocalContext *clc = GetPointer(KMemoryLayout::GetCoreLocalRegionAddress()); diff --git a/libraries/libmesosphere/source/kern_main.cpp b/libraries/libmesosphere/source/kern_main.cpp index bf589c3ef..b24fbe1b0 100644 --- a/libraries/libmesosphere/source/kern_main.cpp +++ b/libraries/libmesosphere/source/kern_main.cpp @@ -45,6 +45,7 @@ namespace ams::kern { } /* TODO: Implement more of Main() */ + cpu::SynchronizeAllCores(); while (true) { /* ... */ } }