diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 052357be45..61e38d5f35 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -167,6 +167,9 @@ add_library(core STATIC hle/api_version.h hle/ipc.h hle/ipc_helpers.h + hle/kernel/arch/arm64/exception_handlers.cpp + hle/kernel/arch/arm64/exception_handlers.h + hle/kernel/arch/arm64/k_exception_context.h hle/kernel/board/nintendo/nx/k_memory_layout.h hle/kernel/board/nintendo/nx/k_system_control.cpp hle/kernel/board/nintendo/nx/k_system_control.h diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 1638bc41db..4f5f8304c4 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -17,6 +17,7 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/debugger/debugger.h" +#include "core/hle/kernel/arch/arm64/exception_handlers.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/svc.h" #include "core/memory.h" @@ -104,11 +105,11 @@ public: case Dynarmic::A32::Exception::NoExecuteFault: LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#08x}", pc); ReturnException(pc, ARM_Interface::no_execute); - return; + break; default: if (debugger_enabled) { ReturnException(pc, ARM_Interface::breakpoint); - return; + break; } parent.LogBacktrace(); @@ -116,6 +117,21 @@ public: "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})", exception, pc, memory.Read32(pc), parent.IsInThumbMode()); } + + // Load KExceptionContext + Kernel::Arch::Arm64::KExceptionContext exception_context{}; + Core::ARM_Interface::ThreadContext32 thread_context{}; + parent.LoadContext(thread_context); + for (size_t index = 0; index < thread_context.cpu_registers.size(); ++index) { + exception_context.x[index] = thread_context.cpu_registers[index]; + } + exception_context.sp = thread_context.cpu_registers[13]; + exception_context.pc = thread_context.cpu_registers[15]; + exception_context.psr = thread_context.cpsr; + exception_context.tpidr = thread_context.tpidr; + + // Kernel to handle exception + Kernel::Arch::Arm64::HandleException(parent.system.Kernel(), exception_context); } void CallSVC(u32 swi) override { diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 921a5a7341..b190c529c0 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -16,6 +16,7 @@ #include "core/core_timing.h" #include "core/debugger/debugger.h" #include "core/hardware_properties.h" +#include "core/hle/kernel/arch/arm64/exception_handlers.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/svc.h" #include "core/memory.h" @@ -142,21 +143,36 @@ public: case Dynarmic::A64::Exception::SendEvent: case Dynarmic::A64::Exception::SendEventLocal: case Dynarmic::A64::Exception::Yield: - return; + break; case Dynarmic::A64::Exception::NoExecuteFault: LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#016x}", pc); ReturnException(pc, ARM_Interface::no_execute); - return; + break; default: if (debugger_enabled) { ReturnException(pc, ARM_Interface::breakpoint); - return; + break; } parent.LogBacktrace(); LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", static_cast(exception), pc, memory.Read32(pc)); } + + // Load KExceptionContext + Kernel::Arch::Arm64::KExceptionContext exception_context{}; + Core::ARM_Interface::ThreadContext64 thread_context{}; + parent.LoadContext(thread_context); + for (size_t index = 0; index < thread_context.cpu_registers.size(); ++index) { + exception_context.x[index] = thread_context.cpu_registers[index]; + } + exception_context.sp = thread_context.sp; + exception_context.pc = thread_context.pc; + exception_context.psr = thread_context.pstate; + exception_context.tpidr = thread_context.tpidr; + + // Kernel to handle exception + Kernel::Arch::Arm64::HandleException(parent.system.Kernel(), exception_context); } void CallSVC(u32 swi) override { diff --git a/src/core/hle/kernel/arch/arm64/exception_handlers.cpp b/src/core/hle/kernel/arch/arm64/exception_handlers.cpp new file mode 100644 index 0000000000..07c618c5a9 --- /dev/null +++ b/src/core/hle/kernel/arch/arm64/exception_handlers.cpp @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/arch/arm64/exception_handlers.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_thread.h" +#include "core/hle/kernel/kernel.h" + +namespace Kernel::Arch::Arm64 { + +void HandleException(KernelCore& kernel, const KExceptionContext& context) { + auto* process = kernel.CurrentProcess(); + if (!process) { + return; + } + + const bool is_user_mode = (context.psr & 0xF) == 0; + if (is_user_mode) { + // If the user disable count is set, we may need to pin the current thread. + if (GetCurrentThread(kernel).GetUserDisableCount() != 0 && + process->GetPinnedThread(GetCurrentCoreId(kernel)) == nullptr) { + KScopedSchedulerLock sl{kernel}; + + // Pin the current thread. + process->PinCurrentThread(GetCurrentCoreId(kernel)); + + // Set the interrupt flag for the thread. + GetCurrentThread(kernel).SetInterruptFlag(); + } + } +} + +} // namespace Kernel::Arch::Arm64 diff --git a/src/core/hle/kernel/arch/arm64/exception_handlers.h b/src/core/hle/kernel/arch/arm64/exception_handlers.h new file mode 100644 index 0000000000..9c913095bd --- /dev/null +++ b/src/core/hle/kernel/arch/arm64/exception_handlers.h @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/kernel/arch/arm64/k_exception_context.h" + +namespace Kernel { +class KernelCore; +} + +namespace Kernel::Arch::Arm64 { +void HandleException(KernelCore& kernel, const KExceptionContext& context); +} diff --git a/src/core/hle/kernel/arch/arm64/k_exception_context.h b/src/core/hle/kernel/arch/arm64/k_exception_context.h new file mode 100644 index 0000000000..57f77dca5c --- /dev/null +++ b/src/core/hle/kernel/arch/arm64/k_exception_context.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" + +namespace Kernel::Arch::Arm64 { + +struct KExceptionContext { + u64 x[(30 - 0) + 1]; + u64 sp; + u64 pc; + u32 psr; + u32 write; + u64 tpidr; + u64 reserved; +}; +static_assert(sizeof(KExceptionContext) == 0x120); + +} // namespace Kernel::Arch::Arm64