diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_debug.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_debug.hpp index 6424d7be6..f06acd990 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_debug.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_debug.hpp @@ -47,6 +47,21 @@ namespace ams::kern::arch::arm64 { static Result BreakIfAttached(ams::svc::BreakReason break_reason, uintptr_t address, size_t size); static Result SetHardwareBreakPoint(ams::svc::HardwareBreakPointRegisterName name, u64 flags, u64 value); + static constexpr bool IsBreakInstruction(u32 insn, u32 psr) { + constexpr u32 BreakInstructionAarch64 = 0xE7FFFFFF; + constexpr u32 BreakInstructionAarch32 = 0xE7FFDEFE; + constexpr u32 BreakInstructionThumb32 = 0xB68E; + if ((psr & 0x10) == 0) { + return insn == BreakInstructionAarch64; + } else { + if ((psr & 0x20) == 0) { + return insn == BreakInstructionAarch32; + } else { + return insn == BreakInstructionThumb32; + } + } + } + /* TODO: This is a placeholder definition. */ }; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp index 8d8492db2..9c590e3db 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp @@ -312,6 +312,32 @@ namespace ams::kern { } } + ALWAYS_INLINE void CopyEnterExceptionSvcPermissionsTo(KThread::StackParameters &sp) { + static_assert(sizeof(svc_access_flags) == sizeof(sp.svc_permission)); + + /* Set ReturnFromException if allowed. */ + if (GetSvcAllowedImpl(this->svc_access_flags, svc::SvcId_ReturnFromException)) { + SetSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException); + } + + /* Set GetInfo if allowed. */ + if (GetSvcAllowedImpl(this->svc_access_flags, svc::SvcId_GetInfo)) { + SetSvcAllowedImpl(sp.svc_permission, svc::SvcId_GetInfo); + } + } + + ALWAYS_INLINE void CopyLeaveExceptionSvcPermissionsTo(KThread::StackParameters &sp) { + static_assert(sizeof(svc_access_flags) == sizeof(sp.svc_permission)); + + /* Clear ReturnFromException. */ + ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException); + + /* If pinned, clear GetInfo. */ + if (sp.is_pinned) { + ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_GetInfo); + } + } + constexpr bool IsPermittedInterrupt(u32 id) const { constexpr size_t BitsPerWord = BITSIZEOF(this->irq_access_flags[0]); if (id < BITSIZEOF(this->irq_access_flags)) { diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp index a7c15ca69..48e6bfe46 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp @@ -233,6 +233,14 @@ namespace ams::kern { this->capabilities.CopyUnpinnedSvcPermissionsTo(sp); } + void CopyEnterExceptionSvcPermissionsTo(KThread::StackParameters &sp) { + this->capabilities.CopyEnterExceptionSvcPermissionsTo(sp); + } + + void CopyLeaveExceptionSvcPermissionsTo(KThread::StackParameters &sp) { + this->capabilities.CopyLeaveExceptionSvcPermissionsTo(sp); + } + constexpr KResourceLimit *GetResourceLimit() const { return this->resource_limit; } bool ReserveResource(ams::svc::LimitableResource which, s64 value); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp index 8f6e515a0..5ffb5b434 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp @@ -230,6 +230,10 @@ namespace ams::kern { const StackParameters &GetStackParameters() const { return *(reinterpret_cast(this->kernel_stack_top) - 1); } + public: + StackParameters &GetStackParametersForExceptionSvcPermission() { + return *(reinterpret_cast(this->kernel_stack_top) - 1); + } public: ALWAYS_INLINE s32 GetDisableDispatchCount() const { MESOSPHERE_ASSERT_THIS(); @@ -251,6 +255,18 @@ namespace ams::kern { void Pin(); void Unpin(); + ALWAYS_INLINE void SaveDebugParams(uintptr_t param1, uintptr_t param2, uintptr_t param3) { + this->debug_params[0] = param1; + this->debug_params[1] = param2; + this->debug_params[2] = param3; + } + + ALWAYS_INLINE void RestoreDebugParams(uintptr_t *param1, uintptr_t *param2, uintptr_t *param3) { + *param1 = this->debug_params[0]; + *param2 = this->debug_params[1]; + *param3 = this->debug_params[2]; + } + NOINLINE void DisableCoreMigration(); NOINLINE void EnableCoreMigration(); diff --git a/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp b/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp index 963a017c9..b728ca612 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp @@ -15,10 +15,52 @@ */ #include +namespace ams::kern::svc { + + void RestoreContext(uintptr_t sp); + +} + namespace ams::kern::arch::arm64 { namespace { + constexpr inline u32 El0PsrMask = 0xFF0FFE20; + + enum EsrEc : u32 { + EsrEc_Unknown = 0b000000, + EsrEc_WaitForInterruptOrEvent = 0b000001, + EsrEc_Cp15McrMrc = 0b000011, + EsrEc_Cp15McrrMrrc = 0b000100, + EsrEc_Cp14McrMrc = 0b000101, + EsrEc_FpAccess = 0b000111, + EsrEc_Cp14Mrrc = 0b001100, + EsrEc_BranchTarget = 0b001101, + EsrEc_IllegalExecution = 0b001110, + EsrEc_Svc32 = 0b010001, + EsrEc_Svc64 = 0b010101, + EsrEc_SystemInstruction64 = 0b011000, + EsrEc_SveZen = 0b011001, + EsrEc_PointerAuthInstruction = 0b011100, + EsrEc_InstructionAbortEl0 = 0b100000, + EsrEc_InstructionAbortEl1 = 0b100001, + EsrEc_PcAlignmentFault = 0b100010, + EsrEc_DataAbortEl0 = 0b100100, + EsrEc_DataAbortEl1 = 0b100101, + EsrEc_SpAlignmentFault = 0b100110, + EsrEc_FpException32 = 0b101000, + EsrEc_FpException64 = 0b101100, + EsrEc_SErrorInterrupt = 0b101111, + EsrEc_BreakPointEl0 = 0b110000, + EsrEc_BreakPointEl1 = 0b110001, + EsrEc_SoftwareStepEl0 = 0b110010, + EsrEc_SoftwareStepEl1 = 0b110011, + EsrEc_WatchPointEl0 = 0b110100, + EsrEc_WatchPointEl1 = 0b110101, + EsrEc_BkptInstruction = 0b111000, + EsrEc_BrkInstruction = 0b111100, + }; + constexpr u32 GetInstructionData(const KExceptionContext *context, u64 esr) { /* Check for THUMB usermode */ if ((context->psr & 0x3F) == 0x30) { @@ -35,61 +77,234 @@ namespace ams::kern::arch::arm64 { } void HandleUserException(KExceptionContext *context, u64 esr, u64 far, u64 afsr0, u64 afsr1, u32 data) { - KProcess *cur_process = GetCurrentProcessPointer(); + KProcess &cur_process = GetCurrentProcess(); bool should_process_user_exception = KTargetSystem::IsUserExceptionHandlersEnabled(); - 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); - - /* Dump the page tables. */ - /* GetCurrentProcess().GetPageTable().DumpTable(); */ - GetCurrentProcess().GetPageTable().DumpMemoryBlocks(); - - MESOSPHERE_PANIC("Unhandled Exception in User Mode\n"); - const u64 ec = (esr >> 26) & 0x3F; switch (ec) { - case 0x0: /* Unknown */ - case 0xE: /* Illegal Execution State */ - case 0x11: /* SVC instruction from Aarch32 */ - case 0x15: /* SVC instruction from Aarch64 */ - case 0x22: /* PC Misalignment */ - case 0x26: /* SP Misalignment */ - case 0x2F: /* SError */ - case 0x30: /* Breakpoint from lower EL */ - case 0x32: /* SoftwareStep from lower EL */ - case 0x34: /* Watchpoint from lower EL */ - case 0x38: /* BKPT instruction */ - case 0x3C: /* BRK instruction */ + case EsrEc_Unknown: + case EsrEc_IllegalExecution: + case EsrEc_Svc32: + case EsrEc_Svc64: + case EsrEc_PcAlignmentFault: + case EsrEc_SpAlignmentFault: + case EsrEc_SErrorInterrupt: + case EsrEc_BreakPointEl0: + case EsrEc_SoftwareStepEl0: + case EsrEc_WatchPointEl0: + case EsrEc_BkptInstruction: + case EsrEc_BrkInstruction: break; default: { - MESOSPHERE_TODO("Get memory state."); - /* If state is KMemoryState_Code and the user can't read it, set should_process_user_exception = true; */ + /* If the fault address's state is KMemoryState_Code and the user can't read the address, force processing exception. */ + KMemoryInfo info; + ams::svc::PageInfo pi; + if (R_SUCCEEDED(cur_process.GetPageTable().QueryInfo(std::addressof(info), std::addressof(pi), far))) { + if (info.GetState() == KMemoryState_Code && ((info.GetPermission() & KMemoryPermission_UserRead) != KMemoryPermission_UserRead)) { + should_process_user_exception = true; + } + } } break; } - if (should_process_user_exception) { - MESOSPHERE_TODO("Process the user exception."); + /* If we should process the user exception (and it's not a breakpoint), try to enter. */ + const bool is_software_break = (ec == EsrEc_Unknown || ec == EsrEc_IllegalExecution || ec == EsrEc_BkptInstruction || ec == EsrEc_BrkInstruction); + const bool is_breakpoint = (ec == EsrEc_BreakPointEl0 || ec == EsrEc_SoftwareStepEl0 || ec == EsrEc_WatchPointEl0); + if ((should_process_user_exception) && + !(is_software_break && cur_process.IsAttachedToDebugger() && KDebug::IsBreakInstruction(data, context->psr)) && + !(is_breakpoint)) + { + if (cur_process.EnterUserException()) { + /* Fill out the exception info. */ + const bool is_aarch64 = (context->psr & 0x10) == 0; + if (is_aarch64) { + /* 64-bit. */ + ams::svc::aarch64::ExceptionInfo *info = std::addressof(GetPointer(cur_process.GetProcessLocalRegionAddress())->exception_info); + + for (size_t i = 0; i < util::size(info->r); ++i) { + info->r[i] = context->x[i]; + } + info->sp = context->sp; + info->lr = context->x[30]; + info->pc = context->pc; + info->pstate = (context->psr & El0PsrMask); + info->afsr0 = afsr0; + info->afsr1 = afsr1; + info->esr = esr; + info->far = far; + } else { + /* 32-bit. */ + ams::svc::aarch32::ExceptionInfo *info = std::addressof(GetPointer(cur_process.GetProcessLocalRegionAddress())->exception_info); + + for (size_t i = 0; i < util::size(info->r); ++i) { + info->r[i] = context->x[i]; + } + info->sp = context->x[13]; + info->lr = context->x[14]; + info->pc = context->pc; + info->flags = 1; + + info->status_64.pstate = (context->psr & El0PsrMask); + info->status_64.afsr0 = afsr0; + info->status_64.afsr1 = afsr1; + info->status_64.esr = esr; + info->status_64.far = far; + } + + /* Save the debug parameters to the current thread. */ + GetCurrentThread().SaveDebugParams(far, esr, data); + + /* Get the exception type. */ + u32 type; + switch (ec) { + case EsrEc_Unknown: + case EsrEc_IllegalExecution: + case EsrEc_Cp15McrMrc: + case EsrEc_Cp15McrrMrrc: + case EsrEc_Cp14McrMrc: + case EsrEc_Cp14Mrrc: + case EsrEc_SystemInstruction64: + case EsrEc_BkptInstruction: + case EsrEc_BrkInstruction: + type = ams::svc::ExceptionType_InstructionAbort; + break; + case EsrEc_PcAlignmentFault: + type = ams::svc::ExceptionType_UnalignedInstruction; + break; + case EsrEc_SpAlignmentFault: + type = ams::svc::ExceptionType_UnalignedData; + break; + case EsrEc_Svc32: + case EsrEc_Svc64: + type = ams::svc::ExceptionType_InvalidSystemCall; + break; + case EsrEc_SErrorInterrupt: + type = ams::svc::ExceptionType_MemorySystemError; + break; + case EsrEc_InstructionAbortEl0: + type = ams::svc::ExceptionType_InstructionAbort; + break; + case EsrEc_DataAbortEl0: + default: + type = ams::svc::ExceptionType_DataAbort; + break; + } + + /* We want to enter at the process entrypoint, with x0 = type. */ + context->pc = GetInteger(cur_process.GetEntryPoint()); + context->x[0] = type; + if (is_aarch64) { + context->x[1] = GetInteger(cur_process.GetProcessLocalRegionAddress() + __builtin_offsetof(ams::svc::aarch64::ProcessLocalRegion, exception_info)); + + auto *plr = GetPointer(cur_process.GetProcessLocalRegionAddress()); + context->sp = util::AlignDown(reinterpret_cast(plr->data) + sizeof(plr->data), 0x10); + context->psr = 0; + } else { + context->x[1] = GetInteger(cur_process.GetProcessLocalRegionAddress() + __builtin_offsetof(ams::svc::aarch32::ProcessLocalRegion, exception_info)); + + auto *plr = GetPointer(cur_process.GetProcessLocalRegionAddress()); + context->x[13] = util::AlignDown(reinterpret_cast(plr->data) + sizeof(plr->data), 0x10); + context->psr = 0x10; + } + + /* Set exception SVC permissions. */ + cur_process.CopyEnterExceptionSvcPermissionsTo(GetCurrentThread().GetStackParametersForExceptionSvcPermission()); + return; + } } { - MESOSPHERE_TODO("Process for KDebug."); + /* Collect additional information based on the ec. */ + ams::svc::DebugException exception; + uintptr_t param2 = 0; + uintptr_t param3 = 0; + switch (ec) { + case EsrEc_Unknown: + case EsrEc_IllegalExecution: + case EsrEc_BkptInstruction: + case EsrEc_BrkInstruction: + { + exception = ams::svc::DebugException_UndefinedInstruction; + param2 = far; + param3 = data; + } + break; + case EsrEc_PcAlignmentFault: + case EsrEc_SpAlignmentFault: + { + exception = ams::svc::DebugException_AlignmentFault; + param2 = far; + } + break; + case EsrEc_Svc32: + case EsrEc_Svc64: + { + exception = ams::svc::DebugException_UndefinedSystemCall; + param2 = far; + param3 = (esr & 0xFF); + } + break; + case EsrEc_BreakPointEl0: + case EsrEc_SoftwareStepEl0: + { + exception = ams::svc::DebugException_BreakPoint; + param2 = far; + param3 = ams::svc::BreakPointType_HardwareInstruction; + } + break; + case EsrEc_WatchPointEl0: + { + exception = ams::svc::DebugException_BreakPoint; + param2 = far; + param3 = ams::svc::BreakPointType_HardwareInstruction; + } + break; + case EsrEc_SErrorInterrupt: + { + exception = ams::svc::DebugException_MemorySystemError; + param2 = far; + } + break; + case EsrEc_InstructionAbortEl0: + { + exception = ams::svc::DebugException_InstructionAbort; + param2 = far; + } + break; + case EsrEc_DataAbortEl0: + default: + { + exception = ams::svc::DebugException_DataAbort; + param2 = far; + } + break; + } - MESOSPHERE_TODO("cur_process->GetProgramId()"); - MESOSPHERE_RELEASE_LOG("Exception occurred. %016lx\n", 0ul); + /* Process the debug event. */ + Result result = KDebug::OnDebugEvent(ams::svc::DebugEvent_Exception, exception, param2, param3); - MESOSPHERE_TODO("if (!svc::ResultNotHandled::Includes(res)) { debug process }."); + /* If we should stop processing the exception, do so. */ + if (svc::ResultStopProcessingException::Includes(result)) { + return; + } + + /* Print that an exception occurred. */ + MESOSPHERE_RELEASE_LOG("Exception occurred. %016lx\n", GetCurrentProcess().GetProgramId()); + + + /* If the SVC is handled, handle it. */ + if (!svc::ResultNotHandled::Includes(result)) { + /* If we successfully enter jit debug, stop processing the exception. */ + if (cur_process.EnterJitDebug(ams::svc::DebugEvent_Exception, exception, param2, param3)) { + return; + } + } } - MESOSPHERE_TODO("cur_process->Exit();"); - (void)cur_process; + /* Exit the current process. */ + cur_process.Exit(); } } @@ -99,6 +314,149 @@ namespace ams::kern::arch::arm64 { KThreadContext::FpuContextSwitchHandler(GetCurrentThreadPointer()); } + /* NOTE: This function is called from ASM. */ + void ReturnFromException(Result user_result) { + /* Get the current thread. */ + KThread *cur_thread = GetCurrentThreadPointer(); + + /* Get the current exception context. */ + KExceptionContext *e_ctx = GetExceptionContext(cur_thread); + + /* Get the current process. */ + KProcess &cur_process = GetCurrentProcess(); + + /* Read the exception info that userland put in tls. */ + union { + ams::svc::aarch64::ExceptionInfo info64; + ams::svc::aarch32::ExceptionInfo info32; + } info = {}; + + + const bool is_aarch64 = (e_ctx->psr & 0x10) == 0; + if (is_aarch64) { + /* We're 64-bit. */ + info.info64 = GetPointer(cur_process.GetProcessLocalRegionAddress())->exception_info; + } else { + /* We're 32-bit. */ + info.info32 = GetPointer(cur_process.GetProcessLocalRegionAddress())->exception_info; + } + + /* Try to leave the user exception. */ + if (cur_process.LeaveUserException()) { + /* We left user exception. Alter our SVC permissions accordingly. */ + cur_process.CopyLeaveExceptionSvcPermissionsTo(cur_thread->GetStackParametersForExceptionSvcPermission()); + + /* Copy the user context to the thread context. */ + if (is_aarch64) { + for (size_t i = 0; i < util::size(info.info64.r); ++i) { + e_ctx->x[i] = info.info64.r[i]; + } + e_ctx->x[30] = info.info64.lr; + e_ctx->sp = info.info64.sp; + e_ctx->pc = info.info64.pc; + e_ctx->psr = (info.info64.pstate & El0PsrMask) | (e_ctx->psr & ~El0PsrMask); + } else { + for (size_t i = 0; i < util::size(info.info32.r); ++i) { + e_ctx->x[i] = info.info32.r[i]; + } + e_ctx->x[14] = info.info32.lr; + e_ctx->x[13] = info.info32.sp; + e_ctx->pc = info.info32.pc; + e_ctx->psr = (info.info32.status_64.pstate & El0PsrMask) | (e_ctx->psr & ~El0PsrMask); + } + + /* Note that PC was adjusted. */ + e_ctx->write = 1; + + if (R_SUCCEEDED(user_result)) { + /* If result handling succeeded, just restore the context. */ + svc::RestoreContext(reinterpret_cast(e_ctx)); + } else { + /* Restore the debug params for the exception. */ + uintptr_t far, esr, data; + GetCurrentThread().RestoreDebugParams(std::addressof(far), std::addressof(esr), std::addressof(data)); + + /* Collect additional information based on the ec. */ + ams::svc::DebugException exception; + uintptr_t param2 = 0; + uintptr_t param3 = 0; + switch ((esr >> 26) & 0x3F) { + case EsrEc_Unknown: + case EsrEc_IllegalExecution: + case EsrEc_BkptInstruction: + case EsrEc_BrkInstruction: + { + exception = ams::svc::DebugException_UndefinedInstruction; + param2 = far; + param3 = data; + } + break; + case EsrEc_PcAlignmentFault: + case EsrEc_SpAlignmentFault: + { + exception = ams::svc::DebugException_AlignmentFault; + param2 = far; + } + break; + case EsrEc_Svc32: + case EsrEc_Svc64: + { + exception = ams::svc::DebugException_UndefinedSystemCall; + param2 = far; + param3 = (esr & 0xFF); + } + break; + case EsrEc_SErrorInterrupt: + { + exception = ams::svc::DebugException_MemorySystemError; + param2 = far; + } + break; + case EsrEc_InstructionAbortEl0: + { + exception = ams::svc::DebugException_InstructionAbort; + param2 = far; + } + break; + case EsrEc_DataAbortEl0: + default: + { + exception = ams::svc::DebugException_DataAbort; + param2 = far; + } + break; + } + + /* Process the debug event. */ + Result result = KDebug::OnDebugEvent(ams::svc::DebugEvent_Exception, exception, param2, param3); + + /* If the SVC is handled, handle it. */ + if (!svc::ResultNotHandled::Includes(result)) { + /* If we should stop processing the exception, restore. */ + if (svc::ResultStopProcessingException::Includes(result)) { + svc::RestoreContext(reinterpret_cast(e_ctx)); + } + + /* If we successfully enter jit debug, restore. */ + if (cur_process.EnterJitDebug(ams::svc::DebugEvent_Exception, exception, param2, param3)) { + svc::RestoreContext(reinterpret_cast(e_ctx)); + } + } + + /* Otherwise, if result debug was returned, restore. */ + if (svc::ResultDebug::Includes(result)) { + svc::RestoreContext(reinterpret_cast(e_ctx)); + } + } + } + + /* Print that an exception occurred. */ + MESOSPHERE_RELEASE_LOG("Exception occurred. %016lx\n", GetCurrentProcess().GetProgramId()); + + /* Exit the current process. */ + GetCurrentProcess().Exit(); + } + /* NOTE: This function is called from ASM. */ void HandleException(KExceptionContext *context) { MESOSPHERE_ASSERT(!KInterruptManager::AreInterruptsEnabled()); @@ -112,14 +470,14 @@ namespace ams::kern::arch::arm64 { /* Collect far and data based on the ec. */ switch ((esr >> 26) & 0x3F) { - case 0x0: /* Unknown */ - case 0xE: /* Illegal Execution State */ - case 0x38: /* BKPT instruction */ - case 0x3C: /* BRK instruction */ + case EsrEc_Unknown: + case EsrEc_IllegalExecution: + case EsrEc_BkptInstruction: + case EsrEc_BrkInstruction: far = context->pc; data = GetInstructionData(context, esr); break; - case 0x11: /* SVC instruction from Aarch32 */ + case EsrEc_Svc32: if (context->psr & 0x20) { /* Thumb mode. */ context->pc -= 2; @@ -129,11 +487,11 @@ namespace ams::kern::arch::arm64 { } far = context->pc; break; - case 0x15: /* SVC instruction from Aarch64 */ + case EsrEc_Svc64: context->pc -= 4; far = context->pc; break; - case 0x30: /* Breakpoint from lower EL */ + case EsrEc_BreakPointEl0: far = context->pc; break; default: @@ -143,6 +501,8 @@ namespace ams::kern::arch::arm64 { /* Note that we're in an exception handler. */ GetCurrentThread().SetInExceptionHandler(); + + /* Verify that spsr's M is allowable (EL0t). */ { const bool is_user_mode = (context->psr & 0xF) == 0; if (is_user_mode) { diff --git a/libraries/libmesosphere/source/arch/arm64/kern_k_thread_context.cpp b/libraries/libmesosphere/source/arch/arm64/kern_k_thread_context.cpp index 83b4707b4..90ad97c8a 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_k_thread_context.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_k_thread_context.cpp @@ -40,6 +40,8 @@ namespace ams::kern::arch::arm64 { namespace { + constexpr inline u32 El0PsrMask = 0xFF0FFE20; + ALWAYS_INLINE bool IsFpuEnabled() { return cpu::ArchitecturalFeatureAccessControlRegisterAccessor().IsFpEnabled(); } @@ -216,7 +218,7 @@ namespace ams::kern::arch::arm64 { out->lr = e_ctx->x[30]; out->sp = e_ctx->sp; out->pc = e_ctx->pc; - out->pstate = e_ctx->psr & 0xFF0FFE20; + out->pstate = e_ctx->psr & El0PsrMask; /* Get the thread's general purpose registers. */ if (thread->IsCallingSvc()) { diff --git a/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_exception_asm.s b/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_exception_asm.s new file mode 100644 index 000000000..c3e2a74d5 --- /dev/null +++ b/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_exception_asm.s @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2018-2020 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 . + */ + +/* ams::kern::svc::CallReturnFromException64(Result result) */ +.section .text._ZN3ams4kern3svc25CallReturnFromException64Ev, "ax", %progbits +.global _ZN3ams4kern3svc25CallReturnFromException64Ev +.type _ZN3ams4kern3svc25CallReturnFromException64Ev, %function +_ZN3ams4kern3svc25CallReturnFromException64Ev: + /* Save registers the SVC entry handler didn't. */ + stp x12, x13, [sp, #(8 * 12)] + stp x14, x15, [sp, #(8 * 14)] + stp x16, x17, [sp, #(8 * 16)] + str x19, [sp, #(8 * 19)] + stp x20, x21, [sp, #(8 * 20)] + stp x22, x23, [sp, #(8 * 22)] + stp x24, x25, [sp, #(8 * 24)] + stp x26, x26, [sp, #(8 * 26)] + stp x28, x29, [sp, #(8 * 28)] + + /* Call ams::kern::arch::arm64::ReturnFromException(result). */ + bl _ZN3ams4kern4arch5arm6419ReturnFromExceptionENS_6ResultE + +0: /* We should never reach this point. */ + b 0b + +/* ams::kern::svc::CallReturnFromException64From32(Result result) */ +.section .text._ZN3ams4kern3svc31CallReturnFromException64From32Ev, "ax", %progbits +.global _ZN3ams4kern3svc31CallReturnFromException64From32Ev +.type _ZN3ams4kern3svc31CallReturnFromException64From32Ev, %function +_ZN3ams4kern3svc31CallReturnFromException64From32Ev: + /* Save registers the SVC entry handler didn't. */ + /* ... */ + + /* Call ams::kern::arch::arm64::ReturnFromException(result). */ + bl _ZN3ams4kern4arch5arm6419ReturnFromExceptionENS_6ResultE + +0: /* We should never reach this point. */ + b 0b + + +/* ams::kern::svc::RestoreContext(uintptr_t sp) */ +.section .text._ZN3ams4kern3svc14RestoreContextEm, "ax", %progbits +.global _ZN3ams4kern3svc14RestoreContextEm +.type _ZN3ams4kern3svc14RestoreContextEm, %function +_ZN3ams4kern3svc14RestoreContextEm: + /* Set the stack pointer, set daif. */ + mov sp, x0 + msr daifset, #2 + +0: /* We should handle DPC. */ + /* Check the dpc flags. */ + ldrb w8, [sp, #(0x120 + 0x10)] + cbz w8, 1f + + /* We have DPC to do! */ + /* Save registers and call ams::kern::KDpcManager::HandleDpc(). */ + sub sp, sp, #0x40 + stp x0, x1, [sp, #(8 * 0)] + stp x2, x3, [sp, #(8 * 2)] + stp x4, x5, [sp, #(8 * 4)] + stp x6, x7, [sp, #(8 * 6)] + bl _ZN3ams4kern11KDpcManager9HandleDpcEv + ldp x0, x1, [sp, #(8 * 0)] + ldp x2, x3, [sp, #(8 * 2)] + ldp x4, x5, [sp, #(8 * 4)] + ldp x6, x7, [sp, #(8 * 6)] + add sp, sp, #0x40 + b 0b + +1: /* We're done with DPC, and should return from the svc. */ + /* Clear our in-SVC note. */ + strb wzr, [sp, #(0x120 + 0x12)] + + /* Restore registers. */ + ldp x30, x8, [sp, #(8 * 30)] + ldp x9, x10, [sp, #(8 * 32)] + ldr x11, [sp, #(8 * 34)] + msr sp_el0, x8 + msr elr_el1, x9 + msr spsr_el1, x10 + msr tpidr_el0, x11 + ldp x0, x1, [sp, #(8 * 0)] + ldp x2, x3, [sp, #(8 * 2)] + ldp x4, x5, [sp, #(8 * 4)] + ldp x6, x7, [sp, #(8 * 6)] + ldp x8, x9, [sp, #(8 * 8)] + ldp x10, x11, [sp, #(8 * 10)] + ldp x12, x13, [sp, #(8 * 12)] + ldp x14, x15, [sp, #(8 * 14)] + ldp x16, x17, [sp, #(8 * 16)] + ldp x18, x19, [sp, #(8 * 18)] + ldp x20, x21, [sp, #(8 * 20)] + ldp x22, x23, [sp, #(8 * 22)] + ldp x24, x25, [sp, #(8 * 24)] + ldp x26, x27, [sp, #(8 * 26)] + ldp x28, x29, [sp, #(8 * 28)] + + /* Return. */ + add sp, sp, #0x120 + eret diff --git a/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_tables.cpp b/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_tables.cpp index 3bb4b84b4..76337bb27 100644 --- a/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_tables.cpp +++ b/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_tables.cpp @@ -29,6 +29,10 @@ namespace ams::kern::svc { void CallReplyAndReceiveLight64(); void CallReplyAndReceiveLight64From32(); + /* Declare special prototypes for ReturnFromException. */ + void CallReturnFromException64(); + void CallReturnFromException64From32(); + namespace { #ifndef MESOSPHERE_USE_STUBBED_SVC_TABLES @@ -69,6 +73,8 @@ namespace ams::kern::svc { table[svc::SvcId_SendSyncRequestLight] = CallSendSyncRequestLight64From32; table[svc::SvcId_ReplyAndReceiveLight] = CallReplyAndReceiveLight64From32; + table[svc::SvcId_ReturnFromException] = CallReturnFromException64From32; + return table; }(); @@ -83,6 +89,8 @@ namespace ams::kern::svc { table[svc::SvcId_SendSyncRequestLight] = CallSendSyncRequestLight64; table[svc::SvcId_ReplyAndReceiveLight] = CallReplyAndReceiveLight64; + table[svc::SvcId_ReturnFromException] = CallReturnFromException64; + return table; }(); diff --git a/libraries/libmesosphere/source/kern_k_process.cpp b/libraries/libmesosphere/source/kern_k_process.cpp index f6179ce2a..051379f32 100644 --- a/libraries/libmesosphere/source/kern_k_process.cpp +++ b/libraries/libmesosphere/source/kern_k_process.cpp @@ -704,7 +704,47 @@ namespace ams::kern { } bool KProcess::EnterUserException() { - MESOSPHERE_UNIMPLEMENTED(); + /* Get the current thread. */ + KThread *cur_thread = GetCurrentThreadPointer(); + MESOSPHERE_ASSERT(this == cur_thread->GetOwnerProcess()); + + /* Try to claim the exception thread. */ + if (this->exception_thread != cur_thread) { + const uintptr_t address_key = reinterpret_cast(std::addressof(this->exception_thread)); + while (true) { + { + KScopedSchedulerLock sl; + + /* If the thread is terminating, it can't enter. */ + if (cur_thread->IsTerminationRequested()) { + return false; + } + + /* If we have no exception thread, we succeeded. */ + if (this->exception_thread == nullptr) { + this->exception_thread = cur_thread; + return true; + } + + /* Otherwise, wait for us to not have an exception thread. */ + cur_thread->SetAddressKey(address_key); + this->exception_thread->AddWaiter(cur_thread); + if (cur_thread->GetState() == KThread::ThreadState_Runnable) { + cur_thread->SetState(KThread::ThreadState_Waiting); + } + } + /* Remove the thread as a waiter from the lock owner. */ + { + KScopedSchedulerLock sl; + KThread *owner_thread = cur_thread->GetLockOwner(); + if (owner_thread != nullptr) { + owner_thread->RemoveWaiter(cur_thread); + } + } + } + } else { + return false; + } } bool KProcess::LeaveUserException() { @@ -715,8 +755,18 @@ namespace ams::kern { KScopedSchedulerLock sl; if (this->exception_thread == thread) { - /* TODO */ - MESOSPHERE_UNIMPLEMENTED(); + this->exception_thread = nullptr; + + /* Remove waiter thread. */ + s32 num_waiters; + KThread *next = thread->RemoveWaiterByKey(std::addressof(num_waiters), reinterpret_cast(std::addressof(this->exception_thread))); + if (next != nullptr) { + if (next->GetState() == KThread::ThreadState_Waiting) { + next->SetState(KThread::ThreadState_Runnable); + } + } + + return true; } else { return false; } diff --git a/libraries/libmesosphere/source/svc/kern_svc_exception.cpp b/libraries/libmesosphere/source/svc/kern_svc_exception.cpp index f4ad17c0e..f5c859a88 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_exception.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_exception.cpp @@ -79,18 +79,10 @@ namespace ams::kern::svc { return Break(break_reason, arg, size); } - void ReturnFromException64(ams::Result result) { - MESOSPHERE_PANIC("Stubbed SvcReturnFromException64 was called."); - } - /* ============================= 64From32 ABI ============================= */ void Break64From32(ams::svc::BreakReason break_reason, ams::svc::Address arg, ams::svc::Size size) { return Break(break_reason, arg, size); } - void ReturnFromException64From32(ams::Result result) { - MESOSPHERE_PANIC("Stubbed SvcReturnFromException64From32 was called."); - } - } diff --git a/libraries/libvapours/include/vapours/svc/svc_types_common.hpp b/libraries/libvapours/include/vapours/svc/svc_types_common.hpp index 13e5d80a9..4b8260b7d 100644 --- a/libraries/libvapours/include/vapours/svc/svc_types_common.hpp +++ b/libraries/libvapours/include/vapours/svc/svc_types_common.hpp @@ -548,6 +548,7 @@ namespace ams::svc { struct ExceptionInfoStatus64 { u32 pstate; u32 afsr0; + u32 afsr1; u32 esr; u32 far; };