From 4fca870f2f9345249fa5df9276d0dc6e9c51c1d4 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 20 Jul 2020 19:59:12 -0700 Subject: [PATCH] kern: fix incorrect cache routines, implement SvcSetProcessMemoryPermission --- .../source/arch/arm64/kern_cpu.cpp | 12 +++--- .../arch/arm64/kern_exception_handlers.cpp | 8 ++++ .../source/kern_k_page_table_base.cpp | 10 +++-- .../source/svc/kern_svc_process_memory.cpp | 38 ++++++++++++++++++- 4 files changed, 57 insertions(+), 11 deletions(-) diff --git a/libraries/libmesosphere/source/arch/arm64/kern_cpu.cpp b/libraries/libmesosphere/source/arch/arm64/kern_cpu.cpp index 60fd0473e..2a443d539 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_cpu.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_cpu.cpp @@ -370,24 +370,24 @@ namespace ams::kern::arch::arm64::cpu { Result StoreDataCache(const void *addr, size_t size) { KScopedCoreMigrationDisable dm; - const uintptr_t start = util::AlignDown(reinterpret_cast(addr), DataCacheLineSize); - const uintptr_t end = util::AlignUp( reinterpret_cast(addr), DataCacheLineSize); + const uintptr_t start = util::AlignDown(reinterpret_cast(addr), DataCacheLineSize); + const uintptr_t end = util::AlignUp( reinterpret_cast(addr) + size, DataCacheLineSize); return StoreDataCacheRange(start, end); } Result FlushDataCache(const void *addr, size_t size) { KScopedCoreMigrationDisable dm; - const uintptr_t start = util::AlignDown(reinterpret_cast(addr), DataCacheLineSize); - const uintptr_t end = util::AlignUp( reinterpret_cast(addr), DataCacheLineSize); + const uintptr_t start = util::AlignDown(reinterpret_cast(addr), DataCacheLineSize); + const uintptr_t end = util::AlignUp( reinterpret_cast(addr) + size, DataCacheLineSize); return FlushDataCacheRange(start, end); } Result InvalidateInstructionCache(void *addr, size_t size) { KScopedCoreMigrationDisable dm; - const uintptr_t start = util::AlignDown(reinterpret_cast(addr), InstructionCacheLineSize); - const uintptr_t end = util::AlignUp( reinterpret_cast(addr), InstructionCacheLineSize); + const uintptr_t start = util::AlignDown(reinterpret_cast(addr), InstructionCacheLineSize); + const uintptr_t end = util::AlignUp( reinterpret_cast(addr) + size, InstructionCacheLineSize); R_TRY(InvalidateInstructionCacheRange(start, end)); diff --git a/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp b/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp index b56945781..dac08a64a 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp @@ -40,6 +40,14 @@ namespace ams::kern::arch::arm64 { MESOSPHERE_LOG("User Exception occurred in %s\n", cur_process->GetName()); + for (size_t i = 0; i < 31; i++) { + MESOSPHERE_LOG("X[%02zu] = %016lx\n", i, context->x[i]); + } + MESOSPHERE_LOG("PC = %016lx\n", context->pc); + MESOSPHERE_LOG("SP = %016lx\n", context->sp); + + MESOSPHERE_PANIC("Unhandled Exception in Supervisor Mode\n"); + const u64 ec = (esr >> 26) & 0x3F; switch (ec) { case 0x0: /* Unknown */ diff --git a/libraries/libmesosphere/source/kern_k_page_table_base.cpp b/libraries/libmesosphere/source/kern_k_page_table_base.cpp index 8062ce54a..0896947e4 100644 --- a/libraries/libmesosphere/source/kern_k_page_table_base.cpp +++ b/libraries/libmesosphere/source/kern_k_page_table_base.cpp @@ -1033,8 +1033,9 @@ namespace ams::kern { /* Determine new perm/state. */ const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm); KMemoryState new_state = old_state; - const bool is_w = (new_perm & KMemoryPermission_UserWrite) == KMemoryPermission_UserWrite; - const bool is_x = (new_perm & KMemoryPermission_UserExecute) == KMemoryPermission_UserExecute; + const bool is_w = (new_perm & KMemoryPermission_UserWrite) == KMemoryPermission_UserWrite; + const bool is_x = (new_perm & KMemoryPermission_UserExecute) == KMemoryPermission_UserExecute; + const bool was_x = (old_perm & KMemoryPermission_UserExecute) == KMemoryPermission_UserExecute; MESOSPHERE_ASSERT(!(is_w && is_x)); if (is_w) { @@ -1050,6 +1051,9 @@ namespace ams::kern { R_TRY(this->MakePageGroup(pg, GetInteger(addr), num_pages)); } + /* Succeed if there's nothing to do. */ + R_SUCCEED_IF(old_perm == new_perm && old_state == new_state); + /* Create an update allocator. */ KMemoryBlockManagerUpdateAllocator allocator(this->memory_block_slab_manager); R_TRY(allocator.GetResult()); @@ -1059,7 +1063,7 @@ namespace ams::kern { /* Perform mapping operation. */ const KPageProperties properties = { new_perm, false, false, false }; - const auto operation = is_x ? OperationType_ChangePermissionsAndRefresh : OperationType_ChangePermissions; + const auto operation = was_x ? OperationType_ChangePermissionsAndRefresh : OperationType_ChangePermissions; R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, Null, false, properties, operation, false)); /* Update the blocks. */ diff --git a/libraries/libmesosphere/source/svc/kern_svc_process_memory.cpp b/libraries/libmesosphere/source/svc/kern_svc_process_memory.cpp index 2e6348493..d02006e68 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_process_memory.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_process_memory.cpp @@ -21,14 +21,48 @@ namespace ams::kern::svc { namespace { + constexpr bool IsValidProcessMemoryPermission(ams::svc::MemoryPermission perm) { + switch (perm) { + case ams::svc::MemoryPermission_None: + case ams::svc::MemoryPermission_Read: + case ams::svc::MemoryPermission_ReadWrite: + case ams::svc::MemoryPermission_ReadExecute: + return true; + default: + return false; + } + } + Result SetProcessMemoryPermission(ams::svc::Handle process_handle, uint64_t address, uint64_t size, ams::svc::MemoryPermission perm) { + /* Validate the 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()); + R_UNLESS(address == static_cast(address), svc::ResultInvalidCurrentMemory()); + R_UNLESS(size == static_cast(size), svc::ResultInvalidCurrentMemory()); + + /* Validate the memory permission. */ + R_UNLESS(IsValidProcessMemoryPermission(perm), svc::ResultInvalidNewMemoryPermission()); + + /* Get the process from its handle. */ + KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject(process_handle); + R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle()); + + /* Validate that the address is in range. */ + auto &page_table = process->GetPageTable(); + R_UNLESS(page_table.Contains(address, size), svc::ResultInvalidCurrentMemory()); + + /* Set the memory permission. */ + return page_table.SetProcessMemoryPermission(address, size, perm); + } } /* ============================= 64 ABI ============================= */ Result SetProcessMemoryPermission64(ams::svc::Handle process_handle, uint64_t address, uint64_t size, ams::svc::MemoryPermission perm) { - MESOSPHERE_PANIC("Stubbed SvcSetProcessMemoryPermission64 was called."); + return SetProcessMemoryPermission(process_handle, address, size, perm); } Result MapProcessMemory64(ams::svc::Address dst_address, ams::svc::Handle process_handle, uint64_t src_address, ams::svc::Size size) { @@ -50,7 +84,7 @@ namespace ams::kern::svc { /* ============================= 64From32 ABI ============================= */ Result SetProcessMemoryPermission64From32(ams::svc::Handle process_handle, uint64_t address, uint64_t size, ams::svc::MemoryPermission perm) { - MESOSPHERE_PANIC("Stubbed SvcSetProcessMemoryPermission64From32 was called."); + return SetProcessMemoryPermission(process_handle, address, size, perm); } Result MapProcessMemory64From32(ams::svc::Address dst_address, ams::svc::Handle process_handle, uint64_t src_address, ams::svc::Size size) {