diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp index 20840daac..966f28266 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp @@ -227,6 +227,7 @@ namespace ams::kern::arch::arm64 { bool Contains(KProcessAddress addr, size_t size) const { return this->page_table.Contains(addr, size); } bool IsInAliasRegion(KProcessAddress addr, size_t size) const { return this->page_table.IsInAliasRegion(addr, size); } + bool IsInUnsafeAliasRegion(KProcessAddress addr, size_t size) const { return this->page_table.IsInUnsafeAliasRegion(addr, size); } bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const { return this->page_table.CanContain(addr, size, state); } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp index 85e219748..c1854f15f 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp @@ -184,6 +184,11 @@ namespace ams::kern { return this->Contains(addr, size) && this->alias_region_start <= addr && addr + size - 1 <= this->alias_region_end - 1; } + bool IsInUnsafeAliasRegion(KProcessAddress addr, size_t size) const { + /* Even though Unsafe physical memory is KMemoryState_Normal, it must be mapped inside the alias code region. */ + return this->CanContain(addr, size, KMemoryState_AliasCode); + } + KProcessAddress GetRegionAddress(KMemoryState state) const; size_t GetRegionSize(KMemoryState state) const; bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp index 2dba56e5c..2bd7d2df0 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp @@ -153,6 +153,8 @@ namespace ams::kern { constexpr KProcessAddress GetEntryPoint() const { return this->code_address; } + constexpr KMemoryManager::Pool GetMemoryPool() const { return this->memory_pool; } + constexpr u64 GetRandomEntropy(size_t i) const { return this->entropy[i]; } constexpr bool IsApplication() const { return this->is_application; } diff --git a/libraries/libmesosphere/include/mesosphere/svc/kern_svc_results.hpp b/libraries/libmesosphere/include/mesosphere/svc/kern_svc_results.hpp index f53ba6d0a..d2a05efc4 100644 --- a/libraries/libmesosphere/include/mesosphere/svc/kern_svc_results.hpp +++ b/libraries/libmesosphere/include/mesosphere/svc/kern_svc_results.hpp @@ -62,6 +62,7 @@ namespace ams::kern::svc { /* 131 */ using ::ams::svc::ResultPortClosed; /* 132 */ using ::ams::svc::ResultLimitReached; + /* 133 */ using ::ams::svc::ResultInvalidMemoryPool; /* 258 */ using ::ams::svc::ResultReceiveListBroken; /* 259 */ using ::ams::svc::ResultOutOfAddressSpace; diff --git a/libraries/libmesosphere/source/kern_k_page_table_base.cpp b/libraries/libmesosphere/source/kern_k_page_table_base.cpp index e90470465..3097e7eba 100644 --- a/libraries/libmesosphere/source/kern_k_page_table_base.cpp +++ b/libraries/libmesosphere/source/kern_k_page_table_base.cpp @@ -3567,11 +3567,91 @@ namespace ams::kern { } Result KPageTableBase::MapPhysicalMemoryUnsafe(KProcessAddress address, size_t size) { - MESOSPHERE_UNIMPLEMENTED(); + /* Try to reserve the unsafe memory. */ + R_UNLESS(Kernel::GetUnsafeMemory().TryReserve(size), svc::ResultLimitReached()); + + /* Ensure we release our reservation on failure. */ + auto reserve_guard = SCOPE_GUARD { Kernel::GetUnsafeMemory().Release(size); }; + + /* Create a page group for the new memory. */ + KPageGroup pg(this->block_info_manager); + + /* Allocate the new memory. */ + const size_t num_pages = size / PageSize; + R_TRY(Kernel::GetMemoryManager().Allocate(std::addressof(pg), num_pages, KMemoryManager::EncodeOption(KMemoryManager::Pool_Unsafe, KMemoryManager::Direction_FromFront))); + + /* Open the page group, and close it when we're done with it. */ + pg.Open(); + ON_SCOPE_EXIT { pg.Close(); }; + + /* Clear the new memory. */ + for (const auto &block : pg) { + std::memset(GetVoidPointer(block.GetAddress()), this->heap_fill_value, block.GetSize()); + } + + /* Map the new memory. */ + { + /* Lock the table. */ + KScopedLightLock lk(this->general_lock); + + /* Check the memory state. */ + R_TRY(this->CheckMemoryState(address, size, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, 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); + + /* Map the pages. */ + const KPageProperties map_properties = { KMemoryPermission_UserReadWrite, false, false, false }; + R_TRY(this->Operate(updater.GetPageList(), address, num_pages, pg, map_properties, OperationType_MapGroup, false)); + + /* Apply the memory block update. */ + this->memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState_Normal, KMemoryPermission_UserReadWrite, KMemoryAttribute_None); + + /* Update our mapped unsafe size. */ + this->mapped_unsafe_physical_memory += size; + + /* We succeeded. */ + reserve_guard.Cancel(); + return ResultSuccess(); + } } Result KPageTableBase::UnmapPhysicalMemoryUnsafe(KProcessAddress address, size_t size) { - MESOSPHERE_UNIMPLEMENTED(); + /* Lock the table. */ + KScopedLightLock lk(this->general_lock); + + /* Check whether we can unmap this much unsafe physical memory. */ + R_UNLESS(size <= this->mapped_unsafe_physical_memory, svc::ResultInvalidCurrentMemory()); + + /* Check the memory state. */ + R_TRY(this->CheckMemoryState(address, size, KMemoryState_All, KMemoryState_Normal, KMemoryPermission_All, KMemoryPermission_UserReadWrite, 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); + + /* Unmap the memory. */ + const size_t num_pages = size / PageSize; + 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)); + + /* Apply the memory block update. */ + this->memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None); + + /* Release the unsafe memory from the limit. */ + Kernel::GetUnsafeMemory().Release(size); + + /* Update our mapped unsafe size. */ + this->mapped_unsafe_physical_memory -= size; + + return ResultSuccess(); } } diff --git a/libraries/libmesosphere/source/svc/kern_svc_physical_memory.cpp b/libraries/libmesosphere/source/svc/kern_svc_physical_memory.cpp index b7f0bef6b..1f97e4aca 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_physical_memory.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_physical_memory.cpp @@ -90,6 +90,45 @@ namespace ams::kern::svc { return ResultSuccess(); } + Result MapPhysicalMemoryUnsafe(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::ResultInvalidCurrentMemory()); + + /* Verify that the region is in range. */ + auto &process = GetCurrentProcess(); + auto &page_table = process.GetPageTable(); + R_UNLESS(page_table.IsInUnsafeAliasRegion(address, size), svc::ResultInvalidMemoryRegion()); + + /* Verify that the process isn't already using the unsafe pool. */ + R_UNLESS(process.GetMemoryPool() != KMemoryManager::Pool_Unsafe, svc::ResultInvalidMemoryPool()); + + /* Map the memory. */ + R_TRY(page_table.MapPhysicalMemoryUnsafe(address, size)); + + return ResultSuccess(); + } + + Result UnmapPhysicalMemoryUnsafe(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::ResultInvalidCurrentMemory()); + + /* Verify that the region is in range. */ + auto &process = GetCurrentProcess(); + auto &page_table = process.GetPageTable(); + R_UNLESS(page_table.IsInUnsafeAliasRegion(address, size), svc::ResultInvalidMemoryRegion()); + + /* Unmap the memory. */ + R_TRY(page_table.UnmapPhysicalMemoryUnsafe(address, size)); + + return ResultSuccess(); + } + } /* ============================= 64 ABI ============================= */ @@ -108,11 +147,11 @@ namespace ams::kern::svc { } Result MapPhysicalMemoryUnsafe64(ams::svc::Address address, ams::svc::Size size) { - MESOSPHERE_PANIC("Stubbed SvcMapPhysicalMemoryUnsafe64 was called."); + return MapPhysicalMemoryUnsafe(address, size); } Result UnmapPhysicalMemoryUnsafe64(ams::svc::Address address, ams::svc::Size size) { - MESOSPHERE_PANIC("Stubbed SvcUnmapPhysicalMemoryUnsafe64 was called."); + return UnmapPhysicalMemoryUnsafe(address, size); } Result SetUnsafeLimit64(ams::svc::Size limit) { @@ -135,11 +174,11 @@ namespace ams::kern::svc { } Result MapPhysicalMemoryUnsafe64From32(ams::svc::Address address, ams::svc::Size size) { - MESOSPHERE_PANIC("Stubbed SvcMapPhysicalMemoryUnsafe64From32 was called."); + return MapPhysicalMemoryUnsafe(address, size); } Result UnmapPhysicalMemoryUnsafe64From32(ams::svc::Address address, ams::svc::Size size) { - MESOSPHERE_PANIC("Stubbed SvcUnmapPhysicalMemoryUnsafe64From32 was called."); + return UnmapPhysicalMemoryUnsafe(address, size); } Result SetUnsafeLimit64From32(ams::svc::Size limit) { diff --git a/libraries/libvapours/include/vapours/results/svc_results.hpp b/libraries/libvapours/include/vapours/results/svc_results.hpp index 2e3a62dd6..7df86bebe 100644 --- a/libraries/libvapours/include/vapours/results/svc_results.hpp +++ b/libraries/libvapours/include/vapours/results/svc_results.hpp @@ -65,6 +65,7 @@ namespace ams::svc { R_DEFINE_ERROR_RESULT(PortClosed, 131); R_DEFINE_ERROR_RESULT(LimitReached, 132); + R_DEFINE_ERROR_RESULT(InvalidMemoryPool, 133); R_DEFINE_ERROR_RESULT(ReceiveListBroken, 258); R_DEFINE_ERROR_RESULT(OutOfAddressSpace, 259);