Newer
Older
Atmosphere / libraries / libmesosphere / source / arch / arm64 / kern_exception_handlers.cpp
/*
 * 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();
    }

}