From 3cf793f87e40a798bedfeab22249eaa0d9c53f62 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 21 Jul 2020 04:58:54 -0700 Subject: [PATCH] kern: implement KThread::Finalize --- .../include/mesosphere/kern_k_process.hpp | 1 + .../source/kern_k_page_table_base.cpp | 26 +++++++++- .../libmesosphere/source/kern_k_process.cpp | 48 +++++++++++++++++++ .../libmesosphere/source/kern_k_thread.cpp | 44 ++++++++++++++++- 4 files changed, 117 insertions(+), 2 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp index a8f82ccc4..780ad96bb 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp @@ -216,6 +216,7 @@ namespace ams::kern { size_t GetTotalNonSystemUserPhysicalMemorySize() const; Result CreateThreadLocalRegion(KProcessAddress *out); + Result DeleteThreadLocalRegion(KProcessAddress addr); void *GetThreadLocalRegionPointer(KProcessAddress addr); constexpr KProcessAddress GetProcessLocalRegionAddress() const { return this->plr_address; } diff --git a/libraries/libmesosphere/source/kern_k_page_table_base.cpp b/libraries/libmesosphere/source/kern_k_page_table_base.cpp index 0896947e4..d3529610f 100644 --- a/libraries/libmesosphere/source/kern_k_page_table_base.cpp +++ b/libraries/libmesosphere/source/kern_k_page_table_base.cpp @@ -1358,7 +1358,31 @@ namespace ams::kern { } Result KPageTableBase::UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state) { - MESOSPHERE_UNIMPLEMENTED(); + /* Check that the unmap is in range. */ + const size_t size = num_pages * PageSize; + R_UNLESS(this->Contains(address, size), svc::ResultInvalidCurrentMemory()); + + /* Lock the table. */ + KScopedLightLock lk(this->general_lock); + + /* Check the memory state. */ + R_TRY(this->CheckMemoryState(address, size, KMemoryState_All, state, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_All, KMemoryAttribute_None)); + + /* Create an update allocator. */ + KMemoryBlockManagerUpdateAllocator allocator(this->memory_block_slab_manager); + R_TRY(allocator.GetResult()); + + /* We're going to perform an update, so create a helper. */ + KScopedPageTableUpdater updater(this); + + /* Perform the unmap. */ + const KPageProperties unmap_properties = { KMemoryPermission_None, false, false, false }; + R_TRY(this->Operate(updater.GetPageList(), address, num_pages, Null, false, unmap_properties, OperationType_Unmap, false)); + + /* Update the blocks. */ + this->memory_block_manager.Update(&allocator, address, num_pages, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None); + + return ResultSuccess(); } Result KPageTableBase::MapPageGroup(KProcessAddress *out_addr, const KPageGroup &pg, KProcessAddress region_start, size_t region_num_pages, KMemoryState state, KMemoryPermission perm) { diff --git a/libraries/libmesosphere/source/kern_k_process.cpp b/libraries/libmesosphere/source/kern_k_process.cpp index b2424ec70..66dbea043 100644 --- a/libraries/libmesosphere/source/kern_k_process.cpp +++ b/libraries/libmesosphere/source/kern_k_process.cpp @@ -212,6 +212,54 @@ namespace ams::kern { return ResultSuccess(); } + Result KProcess::DeleteThreadLocalRegion(KProcessAddress addr) { + KThreadLocalPage *page_to_free = nullptr; + + /* Release the region. */ + { + KScopedSchedulerLock sl; + + /* Try to find the page in the partially used list. */ + auto it = this->partially_used_tlp_tree.find(KThreadLocalPage(util::AlignDown(GetInteger(addr), PageSize))); + if (it == this->partially_used_tlp_tree.end()) { + /* If we don't find it, it has to be in the fully used list. */ + it = this->fully_used_tlp_tree.find(KThreadLocalPage(util::AlignDown(GetInteger(addr), PageSize))); + R_UNLESS(it != this->fully_used_tlp_tree.end(), svc::ResultInvalidAddress()); + + /* Release the region. */ + it->Release(addr); + + /* Move the page out of the fully used list. */ + KThreadLocalPage *tlp = std::addressof(*it); + this->fully_used_tlp_tree.erase(it); + if (tlp->IsAllFree()) { + page_to_free = tlp; + } else { + this->partially_used_tlp_tree.insert(*tlp); + } + } else { + /* Release the region. */ + it->Release(addr); + + /* Handle the all-free case. */ + KThreadLocalPage *tlp = std::addressof(*it); + if (tlp->IsAllFree()) { + this->partially_used_tlp_tree.erase(it); + page_to_free = tlp; + } + } + } + + /* If we should free the page it was in, do so. */ + if (page_to_free != nullptr) { + page_to_free->Finalize(); + + KThreadLocalPage::Free(page_to_free); + } + + return ResultSuccess(); + } + void *KProcess::GetThreadLocalRegionPointer(KProcessAddress addr) { KThreadLocalPage *tlp = nullptr; { diff --git a/libraries/libmesosphere/source/kern_k_thread.cpp b/libraries/libmesosphere/source/kern_k_thread.cpp index ddb7cdbaa..724bab498 100644 --- a/libraries/libmesosphere/source/kern_k_thread.cpp +++ b/libraries/libmesosphere/source/kern_k_thread.cpp @@ -251,7 +251,49 @@ namespace ams::kern { } void KThread::Finalize() { - MESOSPHERE_UNIMPLEMENTED(); + MESOSPHERE_ASSERT_THIS(); + + /* If the thread has an owner process, unregister it. */ + if (this->parent != nullptr) { + this->parent->UnregisterThread(this); + } + + /* If the thread has a local region, delete it. */ + if (this->tls_address != Null) { + MESOSPHERE_R_ABORT_UNLESS(this->parent->DeleteThreadLocalRegion(this->tls_address)); + } + + /* Release any waiters. */ + { + MESOSPHERE_ASSERT(this->lock_owner == nullptr); + KScopedSchedulerLock sl; + + auto it = this->waiter_list.begin(); + while (it != this->waiter_list.end()) { + /* The thread shouldn't be a kernel waiter. */ + MESOSPHERE_ASSERT(!IsKernelAddressKey(it->GetAddressKey())); + it->SetLockOwner(nullptr); + it->SetSyncedObject(nullptr, svc::ResultInvalidState()); + it->Wakeup(); + it = this->waiter_list.erase(it); + } + } + + /* Finalize the thread context. */ + this->thread_context.Finalize(); + + /* Cleanup the kernel stack. */ + if (this->kernel_stack_top != nullptr) { + CleanupKernelStack(reinterpret_cast(this->kernel_stack_top)); + } + + /* Decrement the parent process's thread count. */ + if (this->parent != nullptr) { + this->parent->DecrementThreadCount(); + } + + /* Perform inherited finalization. */ + KAutoObjectWithSlabHeapAndContainer::Finalize(); } bool KThread::IsSignaled() const {