diff --git a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_memory_layout.hpp b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_memory_layout.hpp index b12e8d02e..4e82780b0 100644 --- a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_memory_layout.hpp +++ b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_memory_layout.hpp @@ -24,4 +24,8 @@ namespace ams::kern { constexpr inline size_t MainMemorySize = 4_GB; constexpr inline size_t MainMemorySizeMax = 8_GB; + constexpr inline u32 MinimumMemoryManagerAlignmentShifts[] = { + 0, 0, 0, 0 + }; + } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_memory_manager.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_memory_manager.hpp index bd596dfd0..99ef1f6b5 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_memory_manager.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_memory_manager.hpp @@ -164,6 +164,7 @@ namespace ams::kern { size_t m_num_managers; u64 m_optimized_process_ids[Pool_Count]; bool m_has_optimized_process[Pool_Count]; + s32 m_min_heap_indexes[Pool_Count]; private: Impl &GetManager(KPhysicalAddress address) { return m_managers[KMemoryLayout::GetPhysicalLinearRegion(address).GetAttributes()]; @@ -188,12 +189,12 @@ namespace ams::kern { Result AllocatePageGroupImpl(KPageGroup *out, size_t num_pages, Pool pool, Direction dir, bool unoptimized, bool random, s32 min_heap_index); public: KMemoryManager() - : m_pool_locks(), m_pool_managers_head(), m_pool_managers_tail(), m_managers(), m_num_managers(), m_optimized_process_ids(), m_has_optimized_process() + : m_pool_locks(), m_pool_managers_head(), m_pool_managers_tail(), m_managers(), m_num_managers(), m_optimized_process_ids(), m_has_optimized_process(), m_min_heap_indexes() { /* ... */ } - NOINLINE void Initialize(KVirtualAddress management_region, size_t management_region_size); + NOINLINE void Initialize(KVirtualAddress management_region, size_t management_region_size, const u32 *min_align_shifts); NOINLINE Result InitializeOptimizedMemory(u64 process_id, Pool pool); NOINLINE void FinalizeOptimizedMemory(u64 process_id, Pool pool); @@ -299,6 +300,10 @@ namespace ams::kern { manager->DumpFreeList(); } } + + size_t GetMinimumAlignment(Pool pool) { + return KPageHeap::GetBlockSize(m_min_heap_indexes[pool]); + } public: static size_t CalculateManagementOverheadSize(size_t region_size) { return Impl::CalculateManagementOverheadSize(region_size); diff --git a/libraries/libmesosphere/source/kern_k_memory_manager.cpp b/libraries/libmesosphere/source/kern_k_memory_manager.cpp index fa533387c..aa5f5bdcc 100644 --- a/libraries/libmesosphere/source/kern_k_memory_manager.cpp +++ b/libraries/libmesosphere/source/kern_k_memory_manager.cpp @@ -35,7 +35,7 @@ namespace ams::kern { } - void KMemoryManager::Initialize(KVirtualAddress management_region, size_t management_region_size) { + void KMemoryManager::Initialize(KVirtualAddress management_region, size_t management_region_size, const u32 *min_align_shifts) { /* Clear the management region to zero. */ const KVirtualAddress management_region_end = management_region + management_region_size; std::memset(GetVoidPointer(management_region), 0, management_region_size); @@ -154,6 +154,17 @@ namespace ams::kern { for (size_t i = 0; i < m_num_managers; ++i) { m_managers[i].SetInitialUsedHeapSize(reserved_sizes[i]); } + + /* Determine the min heap size for all pools. */ + for (size_t i = 0; i < Pool_Count; ++i) { + /* Determine the min alignment for the pool in pages. */ + const size_t min_align_pages = 1 << min_align_shifts[i]; + + /* Determine a heap index. */ + if (const auto heap_index = KPageHeap::GetAlignedBlockIndex(min_align_pages, min_align_pages); heap_index >= 0) { + m_min_heap_indexes[i] = heap_index; + } + } } Result KMemoryManager::InitializeOptimizedMemory(u64 process_id, Pool pool) { @@ -192,8 +203,19 @@ namespace ams::kern { return Null; } - /* Lock the pool that we're allocating from. */ + /* Determine the pool and direction we're allocating from. */ const auto [pool, dir] = DecodeOption(option); + + /* Check that we're allocating a correctly aligned number of pages. */ + const size_t min_align_pages = KPageHeap::GetBlockNumPages(m_min_heap_indexes[pool]); + if (!util::IsAligned(num_pages, min_align_pages)) { + return Null; + } + + /* Update our alignment. */ + align_pages = std::max(align_pages, min_align_pages); + + /* Lock the pool that we're allocating from. */ KScopedLightLock lk(m_pool_locks[pool]); /* Choose a heap based on our page size request. */ @@ -226,6 +248,13 @@ namespace ams::kern { } Result KMemoryManager::AllocatePageGroupImpl(KPageGroup *out, size_t num_pages, Pool pool, Direction dir, bool unoptimized, bool random, s32 min_heap_index) { + /* Check that we're allocating a correctly aligned number of pages. */ + const size_t min_align_pages = KPageHeap::GetBlockNumPages(m_min_heap_indexes[pool]); + R_UNLESS(util::IsAligned(num_pages, min_align_pages), svc::ResultInvalidSize()); + + /* Adjust our min heap index to the pool minimum if needed. */ + min_heap_index = std::max(min_heap_index, m_min_heap_indexes[pool]); + /* Choose a heap based on our page size request. */ const s32 heap_index = KPageHeap::GetBlockIndex(num_pages); R_UNLESS(0 <= heap_index, svc::ResultOutOfMemory()); diff --git a/libraries/libmesosphere/source/kern_k_process.cpp b/libraries/libmesosphere/source/kern_k_process.cpp index 24c6baef5..19a9fd85e 100644 --- a/libraries/libmesosphere/source/kern_k_process.cpp +++ b/libraries/libmesosphere/source/kern_k_process.cpp @@ -924,7 +924,6 @@ namespace ams::kern { MESOSPHERE_ABORT_UNLESS(m_main_thread_stack_size == 0); /* Ensure that we're allocating a valid stack. */ - stack_size = util::AlignUp(stack_size, PageSize); R_UNLESS(stack_size + m_code_size <= m_max_process_memory, svc::ResultOutOfMemory()); R_UNLESS(stack_size + m_code_size >= m_code_size, svc::ResultOutOfMemory()); diff --git a/libraries/libmesosphere/source/kern_main.cpp b/libraries/libmesosphere/source/kern_main.cpp index 0f1782c2a..160bb495d 100644 --- a/libraries/libmesosphere/source/kern_main.cpp +++ b/libraries/libmesosphere/source/kern_main.cpp @@ -56,8 +56,9 @@ namespace ams::kern { { const auto &management_region = KMemoryLayout::GetPoolManagementRegion(); MESOSPHERE_ABORT_UNLESS(management_region.GetEndAddress() != 0); + static_assert(util::size(MinimumMemoryManagerAlignmentShifts) == KMemoryManager::Pool_Count); - Kernel::GetMemoryManager().Initialize(management_region.GetAddress(), management_region.GetSize()); + Kernel::GetMemoryManager().Initialize(management_region.GetAddress(), management_region.GetSize(), MinimumMemoryManagerAlignmentShifts); } /* Copy the Initial Process Binary to safe memory. */ diff --git a/libraries/libmesosphere/source/svc/kern_svc_physical_memory.cpp b/libraries/libmesosphere/source/svc/kern_svc_physical_memory.cpp index fc5414905..e2237877d 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_physical_memory.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_physical_memory.cpp @@ -48,10 +48,11 @@ namespace ams::kern::svc { Result MapPhysicalMemory(uintptr_t address, size_t size) { /* Validate address / size. */ - R_UNLESS(util::IsAligned(address, PageSize), svc::ResultInvalidAddress()); - R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize()); - R_UNLESS(size > 0, svc::ResultInvalidSize()); - R_UNLESS((address < address + size), svc::ResultInvalidMemoryRegion()); + const size_t min_alignment = Kernel::GetMemoryManager().GetMinimumAlignment(GetCurrentProcess().GetMemoryPool()); + R_UNLESS(util::IsAligned(address, min_alignment), svc::ResultInvalidAddress()); + R_UNLESS(util::IsAligned(size, min_alignment), svc::ResultInvalidSize()); + R_UNLESS(size > 0, svc::ResultInvalidSize()); + R_UNLESS((address < address + size), svc::ResultInvalidMemoryRegion()); /* Verify that the process has system resource. */ auto &process = GetCurrentProcess(); @@ -69,10 +70,11 @@ namespace ams::kern::svc { Result UnmapPhysicalMemory(uintptr_t address, size_t size) { /* Validate address / size. */ - R_UNLESS(util::IsAligned(address, PageSize), svc::ResultInvalidAddress()); - R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize()); - R_UNLESS(size > 0, svc::ResultInvalidSize()); - R_UNLESS((address < address + size), svc::ResultInvalidMemoryRegion()); + const size_t min_alignment = Kernel::GetMemoryManager().GetMinimumAlignment(GetCurrentProcess().GetMemoryPool()); + R_UNLESS(util::IsAligned(address, min_alignment), svc::ResultInvalidAddress()); + R_UNLESS(util::IsAligned(size, min_alignment), svc::ResultInvalidSize()); + R_UNLESS(size > 0, svc::ResultInvalidSize()); + R_UNLESS((address < address + size), svc::ResultInvalidMemoryRegion()); /* Verify that the process has system resource. */ auto &process = GetCurrentProcess(); diff --git a/libraries/libmesosphere/source/svc/kern_svc_process.cpp b/libraries/libmesosphere/source/svc/kern_svc_process.cpp index 98c8740f2..27ff14b4f 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_process.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_process.cpp @@ -296,7 +296,9 @@ namespace ams::kern::svc { Result StartProcess(ams::svc::Handle process_handle, int32_t priority, int32_t core_id, uint64_t main_thread_stack_size) { /* Validate stack size. */ - R_UNLESS(main_thread_stack_size == static_cast(main_thread_stack_size), svc::ResultOutOfMemory()); + const uint64_t aligned_stack_size = util::AlignUp(main_thread_stack_size, Kernel::GetMemoryManager().GetMinimumAlignment(GetCurrentProcess().GetMemoryPool())); + R_UNLESS(aligned_stack_size >= main_thread_stack_size, svc::ResultOutOfMemory()); + R_UNLESS(aligned_stack_size == static_cast(aligned_stack_size), svc::ResultOutOfMemory()); /* Get the target process. */ KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject(process_handle); @@ -314,7 +316,7 @@ namespace ams::kern::svc { process->SetIdealCoreId(core_id); /* Run the process. */ - R_RETURN(process->Run(priority, static_cast(main_thread_stack_size))); + R_RETURN(process->Run(priority, static_cast(aligned_stack_size))); } Result TerminateProcess(ams::svc::Handle process_handle) {