Atmosphere/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp

195 lines
7.5 KiB
C++

/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <mesosphere.hpp>
namespace ams::kern::arch::arm64 {
namespace {
constexpr u32 GetInstructionData(const KExceptionContext *context, u64 esr) {
/* Check for THUMB usermode */
if ((context->psr & 0x3F) == 0x30) {
u32 insn = *reinterpret_cast<u16 *>(context->pc & ~0x1);
/* Check if the instruction was 32-bit. */
if ((esr >> 25) & 1) {
insn = (insn << 16) | *reinterpret_cast<u16 *>((context->pc & ~0x1) + sizeof(u16));
}
return insn;
} else {
/* Not thumb, so just get the instruction. */
return *reinterpret_cast<u32 *>(context->pc);
}
}
void HandleUserException(KExceptionContext *context, u64 esr, u64 far, u64 afsr0, u64 afsr1, u32 data) {
KProcess *cur_process = GetCurrentProcessPointer();
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 */
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; */
}
break;
}
if (should_process_user_exception) {
MESOSPHERE_TODO("Process the user exception.");
}
{
MESOSPHERE_TODO("Process for KDebug.");
MESOSPHERE_TODO("cur_process->GetProgramId()");
MESOSPHERE_RELEASE_LOG("Exception occurred. %016lx\n", 0ul);
MESOSPHERE_TODO("if (!svc::ResultNotHandled::Includes(res)) { debug process }.");
}
MESOSPHERE_TODO("cur_process->Exit();");
(void)cur_process;
}
}
/* NOTE: This function is called from ASM. */
void FpuContextSwitchHandler() {
KThreadContext::FpuContextSwitchHandler(GetCurrentThreadPointer());
}
/* NOTE: This function is called from ASM. */
void HandleException(KExceptionContext *context) {
MESOSPHERE_ASSERT(!KInterruptManager::AreInterruptsEnabled());
/* Retrieve information about the exception. */
const u64 esr = cpu::GetEsrEl1();
const u64 afsr0 = cpu::GetAfsr0El1();
const u64 afsr1 = cpu::GetAfsr1El1();
u64 far = 0;
u32 data = 0;
/* 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 */
far = context->pc;
data = GetInstructionData(context, esr);
break;
case 0x11: /* SVC instruction from Aarch32 */
if (context->psr & 0x20) {
/* Thumb mode. */
context->pc -= 2;
} else {
/* ARM mode. */
context->pc -= 4;
}
far = context->pc;
break;
case 0x15: /* SVC instruction from Aarch64 */
context->pc -= 4;
far = context->pc;
break;
case 0x30: /* Breakpoint from lower EL */
far = context->pc;
break;
default:
far = cpu::GetFarEl1();
break;
}
/* Note that we're in an exception handler. */
GetCurrentThread().SetInExceptionHandler();
{
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().GetUserDisableCount() != 0 && GetCurrentProcess().GetPinnedThread(GetCurrentCoreId()) == nullptr) {
KScopedSchedulerLock lk;
/* Pin the current thread. */
KScheduler::PinCurrentThread(GetCurrentProcessPointer());
/* Set the interrupt flag for the thread. */
GetCurrentThread().SetInterruptFlag();
}
/* Enable interrupts while we process the usermode exception. */
{
KScopedInterruptEnable ei;
HandleUserException(context, esr, far, afsr0, afsr1, data);
}
} else {
const s32 core_id = GetCurrentCoreId();
MESOSPHERE_LOG("%d: Unhandled Exception in Supervisor Mode\n", core_id);
if (GetCurrentProcessPointer() != nullptr) {
MESOSPHERE_LOG("%d: Current Process = %s\n", core_id, GetCurrentProcess().GetName());
}
for (size_t i = 0; i < 31; i++) {
MESOSPHERE_LOG("%d: X[%02zu] = %016lx\n", core_id, i, context->x[i]);
}
MESOSPHERE_LOG("%d: PC = %016lx\n", core_id, context->pc);
MESOSPHERE_LOG("%d: SP = %016lx\n", core_id, context->sp);
MESOSPHERE_PANIC("Unhandled Exception in Supervisor Mode\n");
}
MESOSPHERE_ASSERT(!KInterruptManager::AreInterruptsEnabled());
/* Handle any DPC requests. */
while (GetCurrentThread().HasDpc()) {
KDpcManager::HandleDpc();
}
}
/* Note that we're no longer in an exception handler. */
GetCurrentThread().ClearInExceptionHandler();
}
}