From ab96255a5de3649310e4fd9a9236436e35f9ea04 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 22 Jul 2020 18:46:28 -0700 Subject: [PATCH] kern: implement SvcSetMemoryAttribute --- .../arch/arm64/kern_k_process_page_table.hpp | 4 ++ .../mesosphere/kern_k_memory_block.hpp | 2 + .../mesosphere/kern_k_page_table_base.hpp | 1 + .../source/kern_k_page_table_base.cpp | 38 +++++++++++++++++++ .../source/svc/kern_svc_memory.cpp | 24 +++++++++++- 5 files changed, 67 insertions(+), 2 deletions(-) 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 f5a2ee96b..feca2e94a 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 @@ -44,6 +44,10 @@ namespace ams::kern::arch::arm64 { return this->page_table.SetProcessMemoryPermission(addr, size, perm); } + Result SetMemoryAttribute(KProcessAddress addr, size_t size, u32 mask, u32 attr) { + return this->page_table.SetMemoryAttribute(addr, size, mask, attr); + } + Result SetHeapSize(KProcessAddress *out, size_t size) { return this->page_table.SetHeapSize(out, size); } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_memory_block.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_memory_block.hpp index 8595b7936..cd2abaaf1 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_memory_block.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_memory_block.hpp @@ -174,6 +174,8 @@ namespace ams::kern { KMemoryAttribute_Uncached = ams::svc::MemoryAttribute_Uncached, KMemoryAttribute_AnyLocked = 0x80, + + KMemoryAttribute_SetMask = KMemoryAttribute_Uncached, }; struct KMemoryInfo { 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 04b84ce22..80a572dea 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp @@ -271,6 +271,7 @@ namespace ams::kern { Result SetMemoryPermission(KProcessAddress addr, size_t size, ams::svc::MemoryPermission perm); Result SetProcessMemoryPermission(KProcessAddress addr, size_t size, ams::svc::MemoryPermission perm); + Result SetMemoryAttribute(KProcessAddress addr, size_t size, u32 mask, u32 attr); Result SetHeapSize(KProcessAddress *out, size_t size); Result SetMaxHeapSize(size_t size); Result QueryInfo(KMemoryInfo *out_info, ams::svc::PageInfo *out_page_info, KProcessAddress addr) const; diff --git a/libraries/libmesosphere/source/kern_k_page_table_base.cpp b/libraries/libmesosphere/source/kern_k_page_table_base.cpp index 1817d5f4f..d65b2fd41 100644 --- a/libraries/libmesosphere/source/kern_k_page_table_base.cpp +++ b/libraries/libmesosphere/source/kern_k_page_table_base.cpp @@ -1080,6 +1080,44 @@ namespace ams::kern { return ResultSuccess(); } + Result KPageTableBase::SetMemoryAttribute(KProcessAddress addr, size_t size, u32 mask, u32 attr) { + const size_t num_pages = size / PageSize; + MESOSPHERE_ASSERT((mask | KMemoryAttribute_SetMask) == KMemoryAttribute_SetMask); + + /* Lock the table. */ + KScopedLightLock lk(this->general_lock); + + /* Verify we can change the memory attribute. */ + KMemoryState old_state; + KMemoryPermission old_perm; + KMemoryAttribute old_attr; + constexpr u32 AttributeTestMask = ~(KMemoryAttribute_SetMask | KMemoryAttribute_DeviceShared); + R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), std::addressof(old_attr), + addr, size, + KMemoryState_FlagCanChangeAttribute, KMemoryState_FlagCanChangeAttribute, + KMemoryPermission_None, KMemoryPermission_None, + AttributeTestMask, KMemoryAttribute_None, ~AttributeTestMask)); + + /* 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); + + /* Determine the new attribute. */ + const KMemoryAttribute new_attr = static_cast(((old_attr & ~mask) | (attr & mask))); + + /* Perform operation. */ + const KPageProperties properties = { old_perm, false, (new_attr & KMemoryAttribute_Uncached) != 0, false }; + R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, Null, false, properties, OperationType_ChangePermissionsAndRefresh, false)); + + /* Update the blocks. */ + this->memory_block_manager.Update(&allocator, addr, num_pages, old_state, old_perm, new_attr); + + return ResultSuccess(); + } + Result KPageTableBase::SetHeapSize(KProcessAddress *out, size_t size) { /* Lock the physical memory mutex. */ KScopedLightLock map_phys_mem_lk(this->map_physical_memory_lock); diff --git a/libraries/libmesosphere/source/svc/kern_svc_memory.cpp b/libraries/libmesosphere/source/svc/kern_svc_memory.cpp index e15bd6bcb..a4a64bdf9 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_memory.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_memory.cpp @@ -21,6 +21,26 @@ namespace ams::kern::svc { namespace { + Result SetMemoryAttribute(uintptr_t address, size_t size, uint32_t mask, uint32_t attr) { + /* 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()); + + /* Validate the attribute and mask. */ + constexpr u32 SupportedMask = ams::svc::MemoryAttribute_Uncached; + R_UNLESS((mask | attr) == mask, svc::ResultInvalidCombination()); + R_UNLESS((mask | attr | SupportedMask) == SupportedMask, svc::ResultInvalidCombination()); + + /* Validate that the region is in range for the current process. */ + auto &page_table = GetCurrentProcess().GetPageTable(); + R_UNLESS(page_table.Contains(address, size), svc::ResultInvalidCurrentMemory()); + + /* Set the memory attribute. */ + return page_table.SetMemoryAttribute(address, size, mask, attr); + } + Result MapMemory(uintptr_t dst_address, uintptr_t src_address, size_t size) { /* Validate that addresses are page aligned. */ R_UNLESS(util::IsAligned(dst_address, PageSize), svc::ResultInvalidAddress()); @@ -81,7 +101,7 @@ namespace ams::kern::svc { } Result SetMemoryAttribute64(ams::svc::Address address, ams::svc::Size size, uint32_t mask, uint32_t attr) { - MESOSPHERE_PANIC("Stubbed SvcSetMemoryAttribute64 was called."); + return SetMemoryAttribute(address, size, mask, attr); } Result MapMemory64(ams::svc::Address dst_address, ams::svc::Address src_address, ams::svc::Size size) { @@ -99,7 +119,7 @@ namespace ams::kern::svc { } Result SetMemoryAttribute64From32(ams::svc::Address address, ams::svc::Size size, uint32_t mask, uint32_t attr) { - MESOSPHERE_PANIC("Stubbed SvcSetMemoryAttribute64From32 was called."); + return SetMemoryAttribute(address, size, mask, attr); } Result MapMemory64From32(ams::svc::Address dst_address, ams::svc::Address src_address, ams::svc::Size size) {