mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-11-27 22:32:15 +00:00
193 lines
7.4 KiB
C++
193 lines
7.4 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(); */
|
|
|
|
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();
|
|
}
|
|
|
|
}
|