diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_memory_manager.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_memory_manager.hpp index cd31450bb..86d133442 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_memory_manager.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_memory_manager.hpp @@ -70,11 +70,13 @@ namespace ams::kern { public: Impl() : heap(), page_reference_counts(), management_region(), pool(), next(), prev() { /* ... */ } - size_t Initialize(const KMemoryRegion *region, Pool pool, KVirtualAddress management_region, KVirtualAddress management_region_end); + size_t Initialize(uintptr_t address, size_t size, KVirtualAddress management, KVirtualAddress management_end, Pool p); KVirtualAddress AllocateBlock(s32 index, bool random) { return this->heap.AllocateBlock(index, random); } void Free(KVirtualAddress addr, size_t num_pages) { this->heap.Free(addr, num_pages); } + void UpdateUsedHeapSize() { this->heap.UpdateUsedSize(); } + void InitializeOptimizedMemory() { std::memset(GetVoidPointer(this->management_region), 0, CalculateOptimizedProcessOverheadSize(this->heap.GetSize())); } void TrackUnoptimizedAllocation(KVirtualAddress block, size_t num_pages); diff --git a/libraries/libmesosphere/source/kern_initial_process.cpp b/libraries/libmesosphere/source/kern_initial_process.cpp index d05e964f0..1b526ca6d 100644 --- a/libraries/libmesosphere/source/kern_initial_process.cpp +++ b/libraries/libmesosphere/source/kern_initial_process.cpp @@ -26,7 +26,9 @@ namespace ams::kern { }; KVirtualAddress GetInitialProcessBinaryAddress() { - return KMemoryLayout::GetPageTableHeapRegion().GetEndAddress() - InitialProcessBinarySizeMax; + const uintptr_t end_address = KMemoryLayout::GetPageTableHeapRegion().GetEndAddress(); + MESOSPHERE_ABORT_UNLESS(end_address != 0); + return end_address - InitialProcessBinarySizeMax; } void LoadInitialProcessBinaryHeader(InitialProcessBinaryHeader *header) { @@ -97,8 +99,11 @@ namespace ams::kern { /* Ensure that we do not leak pages. */ ON_SCOPE_EXIT { pg.Close(); }; - /* Map the process's memory into the temporary region. */ + /* Get the temporary region. */ const auto &temp_region = KMemoryLayout::GetTempRegion(); + MESOSPHERE_ABORT_UNLESS(temp_region.GetEndAddress() != 0); + + /* Map the process's memory into the temporary region. */ KProcessAddress temp_address = Null; MESOSPHERE_R_ABORT_UNLESS(Kernel::GetKernelPageTable().MapPageGroup(std::addressof(temp_address), pg, temp_region.GetAddress(), temp_region.GetSize() / PageSize, KMemoryState_Kernel, KMemoryPermission_KernelReadWrite)); diff --git a/libraries/libmesosphere/source/kern_k_memory_manager.cpp b/libraries/libmesosphere/source/kern_k_memory_manager.cpp index 23c9b57fd..f6719dbba 100644 --- a/libraries/libmesosphere/source/kern_k_memory_manager.cpp +++ b/libraries/libmesosphere/source/kern_k_memory_manager.cpp @@ -20,12 +20,16 @@ namespace ams::kern { namespace { constexpr KMemoryManager::Pool GetPoolFromMemoryRegionType(u32 type) { - switch (type) { - case KMemoryRegionType_VirtualDramApplicationPool: return KMemoryManager::Pool_Application; - case KMemoryRegionType_VirtualDramAppletPool: return KMemoryManager::Pool_Applet; - case KMemoryRegionType_VirtualDramSystemPool: return KMemoryManager::Pool_System; - case KMemoryRegionType_VirtualDramSystemNonSecurePool: return KMemoryManager::Pool_SystemNonSecure; - MESOSPHERE_UNREACHABLE_DEFAULT_CASE(); + if ((type | KMemoryRegionType_VirtualDramApplicationPool) == type) { + return KMemoryManager::Pool_Application; + } else if ((type | KMemoryRegionType_VirtualDramAppletPool) == type) { + return KMemoryManager::Pool_Applet; + } else if ((type | KMemoryRegionType_VirtualDramSystemPool) == type) { + return KMemoryManager::Pool_System; + } else if ((type | KMemoryRegionType_VirtualDramSystemNonSecurePool) == type) { + return KMemoryManager::Pool_SystemNonSecure; + } else { + MESOSPHERE_PANIC("InvalidMemoryRegionType for conversion to Pool"); } } @@ -37,9 +41,11 @@ namespace ams::kern { std::memset(GetVoidPointer(management_region), 0, management_region_size); /* Traverse the virtual memory layout tree, initializing each manager as appropriate. */ - while (true) { + while (this->num_managers != MaxManagerCount) { /* Locate the region that should initialize the current manager. */ - const KMemoryRegion *region = nullptr; + uintptr_t region_address = 0; + size_t region_size = 0; + Pool region_pool = Pool_Count; for (const auto &it : KMemoryLayout::GetVirtualMemoryRegionTree()) { /* We only care about regions that we need to create managers for. */ if (!it.IsDerivedFrom(KMemoryRegionType_VirtualDramUserPool)) { @@ -51,39 +57,62 @@ namespace ams::kern { continue; } - region = std::addressof(it); - break; + /* Validate the region. */ + MESOSPHERE_ABORT_UNLESS(it.GetEndAddress() != 0); + MESOSPHERE_ASSERT(it.GetAddress() != Null); + MESOSPHERE_ASSERT(it.GetSize() > 0); + + /* Update the region's extents. */ + if (region_address == 0) { + region_address = it.GetAddress(); + region_size = it.GetSize(); + region_pool = GetPoolFromMemoryRegionType(it.GetType()); + } else { + MESOSPHERE_ASSERT(it.GetAddress() > region_address + region_size); + + /* Update the size. */ + region_size = it.GetEndAddress() - region_address; + MESOSPHERE_ABORT_UNLESS(GetPoolFromMemoryRegionType(it.GetType()) == region_pool); + } } - /* If we didn't find a region, then we're done initializing managers. */ - if (region == nullptr) { + /* If we didn't find a region, we're done. */ + if (region_size == 0) { break; } - /* Ensure that the region is correct. */ - MESOSPHERE_ASSERT(region->GetAddress() != NullGetAddress())>); - MESOSPHERE_ASSERT(region->GetSize() > 0); - MESOSPHERE_ASSERT(region->GetEndAddress() >= region->GetAddress()); - MESOSPHERE_ASSERT(region->IsDerivedFrom(KMemoryRegionType_VirtualDramUserPool)); - MESOSPHERE_ASSERT(region->GetAttributes() == this->num_managers); - /* Initialize a new manager for the region. */ - const Pool pool = GetPoolFromMemoryRegionType(region->GetType()); Impl *manager = std::addressof(this->managers[this->num_managers++]); MESOSPHERE_ABORT_UNLESS(this->num_managers <= util::size(this->managers)); - const size_t cur_size = manager->Initialize(region, pool, management_region, management_region_end); + const size_t cur_size = manager->Initialize(region_address, region_size, management_region, management_region_end, region_pool); management_region += cur_size; MESOSPHERE_ABORT_UNLESS(management_region <= management_region_end); /* Insert the manager into the pool list. */ - if (this->pool_managers_tail[pool] == nullptr) { - this->pool_managers_head[pool] = manager; + if (this->pool_managers_tail[region_pool] == nullptr) { + this->pool_managers_head[region_pool] = manager; } else { - this->pool_managers_tail[pool]->SetNext(manager); - manager->SetPrev(this->pool_managers_tail[pool]); + this->pool_managers_tail[region_pool]->SetNext(manager); + manager->SetPrev(this->pool_managers_tail[region_pool]); } - this->pool_managers_tail[pool] = manager; + this->pool_managers_tail[region_pool] = manager; + } + + /* Free each region to its corresponding heap. */ + for (const auto &it : KMemoryLayout::GetVirtualMemoryRegionTree()) { + if (it.IsDerivedFrom(KMemoryRegionType_VirtualDramUserPool)) { + /* Check the region. */ + MESOSPHERE_ABORT_UNLESS(it.GetEndAddress() != 0); + + /* Free the memory to the heap. */ + this->managers[it.GetAttributes()].Free(it.GetAddress(), it.GetSize() / PageSize); + } + } + + /* Update the used size for all managers. */ + for (size_t i = 0; i < this->num_managers; ++i) { + this->managers[i].UpdateUsedHeapSize(); } } @@ -354,12 +383,12 @@ namespace ams::kern { return ResultSuccess(); } - size_t KMemoryManager::Impl::Initialize(const KMemoryRegion *region, Pool p, KVirtualAddress management, KVirtualAddress management_end) { + size_t KMemoryManager::Impl::Initialize(uintptr_t address, size_t size, KVirtualAddress management, KVirtualAddress management_end, Pool p) { /* Calculate management sizes. */ - const size_t ref_count_size = (region->GetSize() / PageSize) * sizeof(u16); - const size_t optimize_map_size = CalculateOptimizedProcessOverheadSize(region->GetSize()); + const size_t ref_count_size = (size / PageSize) * sizeof(u16); + const size_t optimize_map_size = CalculateOptimizedProcessOverheadSize(size); const size_t manager_size = util::AlignUp(optimize_map_size + ref_count_size, PageSize); - const size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(region->GetSize()); + const size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(size); const size_t total_management_size = manager_size + page_heap_size; MESOSPHERE_ABORT_UNLESS(manager_size <= total_management_size); MESOSPHERE_ABORT_UNLESS(management + total_management_size <= management_end); @@ -372,13 +401,7 @@ namespace ams::kern { MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(this->management_region), PageSize)); /* Initialize the manager's KPageHeap. */ - this->heap.Initialize(region->GetAddress(), region->GetSize(), management + manager_size, page_heap_size); - - /* Free the memory to the heap. */ - this->heap.Free(region->GetAddress(), region->GetSize() / PageSize); - - /* Update the heap's used size. */ - this->heap.UpdateUsedSize(); + this->heap.Initialize(address, size, management + manager_size, page_heap_size); return total_management_size; }