mirror of
				https://github.com/Atmosphere-NX/Atmosphere-libs.git
				synced 2025-10-31 19:45:51 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			134 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			134 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 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 <http://www.gnu.org/licenses/>.
 | |
|  */
 | |
| #include <mesosphere.hpp>
 | |
| 
 | |
| namespace ams::kern {
 | |
| 
 | |
|     namespace {
 | |
| 
 | |
|         class KMemoryRegionAllocator {
 | |
|             NON_COPYABLE(KMemoryRegionAllocator);
 | |
|             NON_MOVEABLE(KMemoryRegionAllocator);
 | |
|             public:
 | |
|                 static constexpr size_t MaxMemoryRegions = 200;
 | |
|             private:
 | |
|                 KMemoryRegion m_region_heap[MaxMemoryRegions];
 | |
|                 size_t m_num_regions;
 | |
|             public:
 | |
|                 constexpr ALWAYS_INLINE KMemoryRegionAllocator() : m_region_heap(), m_num_regions() { /* ... */ }
 | |
|             public:
 | |
|                 template<typename... Args>
 | |
|                 ALWAYS_INLINE KMemoryRegion *Allocate(Args&&... args) {
 | |
|                     /* Ensure we stay within the bounds of our heap. */
 | |
|                     MESOSPHERE_INIT_ABORT_UNLESS(m_num_regions < MaxMemoryRegions);
 | |
| 
 | |
|                     /* Create the new region. */
 | |
|                     KMemoryRegion *region = std::addressof(m_region_heap[m_num_regions++]);
 | |
|                     std::construct_at(region, std::forward<Args>(args)...);
 | |
| 
 | |
|                     return region;
 | |
|                 }
 | |
|         };
 | |
| 
 | |
|         constinit KMemoryRegionAllocator g_memory_region_allocator;
 | |
| 
 | |
|         template<typename... Args>
 | |
|         ALWAYS_INLINE KMemoryRegion *AllocateRegion(Args&&... args) {
 | |
|             return g_memory_region_allocator.Allocate(std::forward<Args>(args)...);
 | |
|         }
 | |
| 
 | |
| 
 | |
|     }
 | |
| 
 | |
|     void KMemoryRegionTree::InsertDirectly(uintptr_t address, uintptr_t last_address, u32 attr, u32 type_id) {
 | |
|         this->insert(*AllocateRegion(address, last_address, attr, type_id));
 | |
|     }
 | |
| 
 | |
|     bool KMemoryRegionTree::Insert(uintptr_t address, size_t size, u32 type_id, u32 new_attr, u32 old_attr) {
 | |
|         /* Locate the memory region that contains the address. */
 | |
|         KMemoryRegion *found = this->FindModifiable(address);
 | |
| 
 | |
|         /* We require that the old attr is correct. */
 | |
|         if (found->GetAttributes() != old_attr) {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         /* We further require that the region can be split from the old region. */
 | |
|         const uintptr_t inserted_region_end = address + size;
 | |
|         const uintptr_t inserted_region_last = inserted_region_end - 1;
 | |
|         if (found->GetLastAddress() < inserted_region_last) {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         /* Further, we require that the type id is a valid transformation. */
 | |
|         if (!found->CanDerive(type_id)) {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         /* Cache information from the region before we remove it. */
 | |
|         const uintptr_t old_address = found->GetAddress();
 | |
|         const uintptr_t old_last    = found->GetLastAddress();
 | |
|         const uintptr_t old_pair    = found->GetPairAddress();
 | |
|         const u32       old_type    = found->GetType();
 | |
| 
 | |
|         /* Erase the existing region from the tree. */
 | |
|         this->erase(this->iterator_to(*found));
 | |
| 
 | |
|         /* Insert the new region into the tree. */
 | |
|         if (old_address == address) {
 | |
|             /* Reuse the old object for the new region, if we can. */
 | |
|             found->Reset(address, inserted_region_last, old_pair, new_attr, type_id);
 | |
|             this->insert(*found);
 | |
|         } else {
 | |
|             /* If we can't re-use, adjust the old region. */
 | |
|             found->Reset(old_address, address - 1, old_pair, old_attr, old_type);
 | |
|             this->insert(*found);
 | |
| 
 | |
|             /* Insert a new region for the split. */
 | |
|             const uintptr_t new_pair = (old_pair != std::numeric_limits<uintptr_t>::max()) ? old_pair + (address - old_address) : old_pair;
 | |
|             this->insert(*AllocateRegion(address, inserted_region_last, new_pair, new_attr, type_id));
 | |
|         }
 | |
| 
 | |
|         /* If we need to insert a region after the region, do so. */
 | |
|         if (old_last != inserted_region_last) {
 | |
|             const uintptr_t after_pair = (old_pair != std::numeric_limits<uintptr_t>::max()) ? old_pair + (inserted_region_end - old_address) : old_pair;
 | |
|             this->insert(*AllocateRegion(inserted_region_end, old_last, after_pair, old_attr, old_type));
 | |
|         }
 | |
| 
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     void KMemoryLayout::InitializeLinearMemoryRegionTrees() {
 | |
|         /* Initialize linear trees. */
 | |
|         for (auto ®ion : GetPhysicalMemoryRegionTree()) {
 | |
|             if (region.HasTypeAttribute(KMemoryRegionAttr_LinearMapped)) {
 | |
|                 GetPhysicalLinearMemoryRegionTree().InsertDirectly(region.GetAddress(), region.GetLastAddress(), region.GetAttributes(), region.GetType());
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         for (auto ®ion : GetVirtualMemoryRegionTree()) {
 | |
|             if (region.IsDerivedFrom(KMemoryRegionType_Dram)) {
 | |
|                 GetVirtualLinearMemoryRegionTree().InsertDirectly(region.GetAddress(), region.GetLastAddress(), region.GetAttributes(), region.GetType());
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     size_t KMemoryLayout::GetResourceRegionSizeForInit(bool use_extra_resource) {
 | |
|         return KernelResourceSize + KSystemControl::SecureAppletMemorySize + (use_extra_resource ? KernelSlabHeapAdditionalSize + KernelPageBufferAdditionalSize : 0);
 | |
|     }
 | |
| 
 | |
| }
 |