mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 20:31:14 +00:00
kern: improve KMemoryManager pool detection
This commit is contained in:
parent
1e9a3c3f91
commit
cc7cf49c88
3 changed files with 70 additions and 40 deletions
|
@ -70,11 +70,13 @@ namespace ams::kern {
|
||||||
public:
|
public:
|
||||||
Impl() : heap(), page_reference_counts(), management_region(), pool(), next(), prev() { /* ... */ }
|
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); }
|
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 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 InitializeOptimizedMemory() { std::memset(GetVoidPointer(this->management_region), 0, CalculateOptimizedProcessOverheadSize(this->heap.GetSize())); }
|
||||||
|
|
||||||
void TrackUnoptimizedAllocation(KVirtualAddress block, size_t num_pages);
|
void TrackUnoptimizedAllocation(KVirtualAddress block, size_t num_pages);
|
||||||
|
|
|
@ -26,7 +26,9 @@ namespace ams::kern {
|
||||||
};
|
};
|
||||||
|
|
||||||
KVirtualAddress GetInitialProcessBinaryAddress() {
|
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) {
|
void LoadInitialProcessBinaryHeader(InitialProcessBinaryHeader *header) {
|
||||||
|
@ -97,8 +99,11 @@ namespace ams::kern {
|
||||||
/* Ensure that we do not leak pages. */
|
/* Ensure that we do not leak pages. */
|
||||||
ON_SCOPE_EXIT { pg.Close(); };
|
ON_SCOPE_EXIT { pg.Close(); };
|
||||||
|
|
||||||
/* Map the process's memory into the temporary region. */
|
/* Get the temporary region. */
|
||||||
const auto &temp_region = KMemoryLayout::GetTempRegion();
|
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<KProcessAddress>;
|
KProcessAddress temp_address = Null<KProcessAddress>;
|
||||||
MESOSPHERE_R_ABORT_UNLESS(Kernel::GetKernelPageTable().MapPageGroup(std::addressof(temp_address), pg, temp_region.GetAddress(), temp_region.GetSize() / PageSize, KMemoryState_Kernel, KMemoryPermission_KernelReadWrite));
|
MESOSPHERE_R_ABORT_UNLESS(Kernel::GetKernelPageTable().MapPageGroup(std::addressof(temp_address), pg, temp_region.GetAddress(), temp_region.GetSize() / PageSize, KMemoryState_Kernel, KMemoryPermission_KernelReadWrite));
|
||||||
|
|
||||||
|
|
|
@ -20,12 +20,16 @@ namespace ams::kern {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr KMemoryManager::Pool GetPoolFromMemoryRegionType(u32 type) {
|
constexpr KMemoryManager::Pool GetPoolFromMemoryRegionType(u32 type) {
|
||||||
switch (type) {
|
if ((type | KMemoryRegionType_VirtualDramApplicationPool) == type) {
|
||||||
case KMemoryRegionType_VirtualDramApplicationPool: return KMemoryManager::Pool_Application;
|
return KMemoryManager::Pool_Application;
|
||||||
case KMemoryRegionType_VirtualDramAppletPool: return KMemoryManager::Pool_Applet;
|
} else if ((type | KMemoryRegionType_VirtualDramAppletPool) == type) {
|
||||||
case KMemoryRegionType_VirtualDramSystemPool: return KMemoryManager::Pool_System;
|
return KMemoryManager::Pool_Applet;
|
||||||
case KMemoryRegionType_VirtualDramSystemNonSecurePool: return KMemoryManager::Pool_SystemNonSecure;
|
} else if ((type | KMemoryRegionType_VirtualDramSystemPool) == type) {
|
||||||
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
|
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);
|
std::memset(GetVoidPointer(management_region), 0, management_region_size);
|
||||||
|
|
||||||
/* Traverse the virtual memory layout tree, initializing each manager as appropriate. */
|
/* 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. */
|
/* 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()) {
|
for (const auto &it : KMemoryLayout::GetVirtualMemoryRegionTree()) {
|
||||||
/* We only care about regions that we need to create managers for. */
|
/* We only care about regions that we need to create managers for. */
|
||||||
if (!it.IsDerivedFrom(KMemoryRegionType_VirtualDramUserPool)) {
|
if (!it.IsDerivedFrom(KMemoryRegionType_VirtualDramUserPool)) {
|
||||||
|
@ -51,39 +57,62 @@ namespace ams::kern {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
region = std::addressof(it);
|
/* Validate the region. */
|
||||||
break;
|
MESOSPHERE_ABORT_UNLESS(it.GetEndAddress() != 0);
|
||||||
|
MESOSPHERE_ASSERT(it.GetAddress() != Null<decltype(it.GetAddress())>);
|
||||||
|
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 we didn't find a region, we're done. */
|
||||||
if (region == nullptr) {
|
if (region_size == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ensure that the region is correct. */
|
|
||||||
MESOSPHERE_ASSERT(region->GetAddress() != Null<decltype(region->GetAddress())>);
|
|
||||||
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. */
|
/* Initialize a new manager for the region. */
|
||||||
const Pool pool = GetPoolFromMemoryRegionType(region->GetType());
|
|
||||||
Impl *manager = std::addressof(this->managers[this->num_managers++]);
|
Impl *manager = std::addressof(this->managers[this->num_managers++]);
|
||||||
MESOSPHERE_ABORT_UNLESS(this->num_managers <= util::size(this->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;
|
management_region += cur_size;
|
||||||
MESOSPHERE_ABORT_UNLESS(management_region <= management_region_end);
|
MESOSPHERE_ABORT_UNLESS(management_region <= management_region_end);
|
||||||
|
|
||||||
/* Insert the manager into the pool list. */
|
/* Insert the manager into the pool list. */
|
||||||
if (this->pool_managers_tail[pool] == nullptr) {
|
if (this->pool_managers_tail[region_pool] == nullptr) {
|
||||||
this->pool_managers_head[pool] = manager;
|
this->pool_managers_head[region_pool] = manager;
|
||||||
} else {
|
} else {
|
||||||
this->pool_managers_tail[pool]->SetNext(manager);
|
this->pool_managers_tail[region_pool]->SetNext(manager);
|
||||||
manager->SetPrev(this->pool_managers_tail[pool]);
|
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();
|
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. */
|
/* Calculate management sizes. */
|
||||||
const size_t ref_count_size = (region->GetSize() / PageSize) * sizeof(u16);
|
const size_t ref_count_size = (size / PageSize) * sizeof(u16);
|
||||||
const size_t optimize_map_size = CalculateOptimizedProcessOverheadSize(region->GetSize());
|
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 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;
|
const size_t total_management_size = manager_size + page_heap_size;
|
||||||
MESOSPHERE_ABORT_UNLESS(manager_size <= total_management_size);
|
MESOSPHERE_ABORT_UNLESS(manager_size <= total_management_size);
|
||||||
MESOSPHERE_ABORT_UNLESS(management + total_management_size <= management_end);
|
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));
|
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(this->management_region), PageSize));
|
||||||
|
|
||||||
/* Initialize the manager's KPageHeap. */
|
/* Initialize the manager's KPageHeap. */
|
||||||
this->heap.Initialize(region->GetAddress(), region->GetSize(), management + manager_size, page_heap_size);
|
this->heap.Initialize(address, size, 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();
|
|
||||||
|
|
||||||
return total_management_size;
|
return total_management_size;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue