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 fea871887..f3a947611 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 @@ -128,6 +128,10 @@ namespace ams::kern::arch::arm64 { return this->page_table.MakeAndOpenPageGroupContiguous(out, address, num_pages, state_mask, state, perm_mask, perm, attr_mask, attr); } + Result InvalidateProcessDataCache(KProcessAddress address, size_t size) { + return this->page_table.InvalidateProcessDataCache(address, size); + } + Result LockForDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned) { return this->page_table.LockForDeviceAddressSpace(out, address, size, perm, is_aligned); } 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 06e29a2b9..358b5b0ef 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp @@ -313,6 +313,8 @@ namespace ams::kern { Result MakeAndOpenPageGroup(KPageGroup *out, KProcessAddress address, size_t num_pages, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr); Result MakeAndOpenPageGroupContiguous(KPageGroup *out, KProcessAddress address, size_t num_pages, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr); + Result InvalidateProcessDataCache(KProcessAddress address, size_t size); + Result LockForDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned); Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size); Result LockForIpcUserBuffer(KPhysicalAddress *out, KProcessAddress address, size_t size); diff --git a/libraries/libmesosphere/source/kern_k_page_table_base.cpp b/libraries/libmesosphere/source/kern_k_page_table_base.cpp index f8a115349..0f4d942c1 100644 --- a/libraries/libmesosphere/source/kern_k_page_table_base.cpp +++ b/libraries/libmesosphere/source/kern_k_page_table_base.cpp @@ -1822,6 +1822,73 @@ namespace ams::kern { return ResultSuccess(); } + Result KPageTableBase::InvalidateProcessDataCache(KProcessAddress address, size_t size) { + /* Check that the region is in range. */ + R_UNLESS(this->Contains(address, size), svc::ResultInvalidCurrentMemory()); + + /* Lock the table. */ + KScopedLightLock lk(this->general_lock); + + /* Check the memory state. */ + R_TRY(this->CheckMemoryStateContiguous(address, size, KMemoryState_FlagReferenceCounted, KMemoryState_FlagReferenceCounted, KMemoryPermission_UserReadWrite, KMemoryPermission_UserReadWrite, KMemoryAttribute_Uncached, KMemoryAttribute_None)); + + /* Get the impl. */ + auto &impl = this->GetImpl(); + + /* Begin traversal. */ + TraversalContext context; + TraversalEntry next_entry; + bool traverse_valid = impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), address); + R_UNLESS(traverse_valid, svc::ResultInvalidCurrentMemory()); + + /* Prepare tracking variables. */ + KPhysicalAddress cur_addr = next_entry.phys_addr; + size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1)); + size_t tot_size = cur_size; + + /* Iterate. */ + while (tot_size < size) { + /* Continue the traversal. */ + traverse_valid = impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); + R_UNLESS(traverse_valid, svc::ResultInvalidCurrentMemory()); + + if (next_entry.phys_addr != (cur_addr + cur_size)) { + /* Check that the pages are linearly mapped. */ + R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), svc::ResultInvalidCurrentMemory()); + + /* Invalidate the block. */ + if (cur_size > 0) { + /* NOTE: Nintendo does not check the result of invalidation. */ + cpu::InvalidateDataCache(GetVoidPointer(GetLinearMappedVirtualAddress(cur_addr)), cur_size); + } + + /* Advance. */ + cur_addr = next_entry.phys_addr; + cur_size = next_entry.block_size; + } else { + cur_size += next_entry.block_size; + } + + tot_size += next_entry.block_size; + } + + /* Ensure we use the right size for the last block. */ + if (tot_size > size) { + cur_size -= (tot_size - size); + } + + /* Check that the last block is linearly mapped. */ + R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), svc::ResultInvalidCurrentMemory()); + + /* Invalidate the last block. */ + if (cur_size > 0) { + /* NOTE: Nintendo does not check the result of invalidation. */ + cpu::InvalidateDataCache(GetVoidPointer(GetLinearMappedVirtualAddress(cur_addr)), cur_size); + } + + return ResultSuccess(); + } + Result KPageTableBase::LockForDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned) { /* Lightly validate the range before doing anything else. */ const size_t num_pages = size / PageSize; diff --git a/libraries/libmesosphere/source/svc/kern_svc_cache.cpp b/libraries/libmesosphere/source/svc/kern_svc_cache.cpp index 9f3c2527f..62487b361 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_cache.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_cache.cpp @@ -73,6 +73,22 @@ namespace ams::kern::svc { return ResultSuccess(); } + Result InvalidateProcessDataCache(ams::svc::Handle process_handle, uint64_t address, uint64_t size) { + /* Validate address/size. */ + R_UNLESS(size > 0, svc::ResultInvalidSize()); + R_UNLESS(address == static_cast(address), svc::ResultInvalidCurrentMemory()); + R_UNLESS(size == static_cast(size), svc::ResultInvalidCurrentMemory()); + + /* Get the process from its handle. */ + KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject(process_handle); + R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle()); + + /* Invalidate the cache. */ + R_TRY(process->GetPageTable().InvalidateProcessDataCache(address, size)); + + return ResultSuccess(); + } + Result StoreProcessDataCache(ams::svc::Handle process_handle, uint64_t address, uint64_t size) { /* Validate address/size. */ R_UNLESS(size > 0, svc::ResultInvalidSize()); @@ -140,7 +156,7 @@ namespace ams::kern::svc { } Result InvalidateProcessDataCache64(ams::svc::Handle process_handle, uint64_t address, uint64_t size) { - MESOSPHERE_PANIC("Stubbed SvcInvalidateProcessDataCache64 was called."); + return InvalidateProcessDataCache(process_handle, address, size); } Result StoreProcessDataCache64(ams::svc::Handle process_handle, uint64_t address, uint64_t size) { @@ -162,7 +178,7 @@ namespace ams::kern::svc { } Result InvalidateProcessDataCache64From32(ams::svc::Handle process_handle, uint64_t address, uint64_t size) { - MESOSPHERE_PANIC("Stubbed SvcInvalidateProcessDataCache64From32 was called."); + return InvalidateProcessDataCache(process_handle, address, size); } Result StoreProcessDataCache64From32(ams::svc::Handle process_handle, uint64_t address, uint64_t size) {