/* * Copyright (c) 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 . */ #pragma once #include #include #include #include #include namespace ams::kern::arch::arm64 { class KInterruptManager { NON_COPYABLE(KInterruptManager); NON_MOVEABLE(KInterruptManager); private: struct KCoreLocalInterruptEntry { KInterruptHandler *handler; bool manually_cleared; bool needs_clear; u8 priority; constexpr KCoreLocalInterruptEntry() : handler(nullptr), manually_cleared(false), needs_clear(false), priority(KInterruptController::PriorityLevel_Low) { /* ... */ } }; struct KGlobalInterruptEntry { KInterruptHandler *handler; bool manually_cleared; bool needs_clear; constexpr KGlobalInterruptEntry() : handler(nullptr), manually_cleared(false), needs_clear(false) { /* ... */ } }; private: KCoreLocalInterruptEntry m_core_local_interrupts[cpu::NumCores][KInterruptController::NumLocalInterrupts]{}; KInterruptController m_interrupt_controller{}; KInterruptController::LocalState m_local_states[cpu::NumCores]{}; bool m_local_state_saved[cpu::NumCores]{}; mutable KSpinLock m_global_interrupt_lock{}; KGlobalInterruptEntry m_global_interrupts[KInterruptController::NumGlobalInterrupts]{}; KInterruptController::GlobalState m_global_state{}; bool m_global_state_saved{}; private: ALWAYS_INLINE KSpinLock &GetGlobalInterruptLock() const { return m_global_interrupt_lock; } ALWAYS_INLINE KGlobalInterruptEntry &GetGlobalInterruptEntry(s32 irq) { return m_global_interrupts[KInterruptController::GetGlobalInterruptIndex(irq)]; } ALWAYS_INLINE KCoreLocalInterruptEntry &GetLocalInterruptEntry(s32 irq) { return m_core_local_interrupts[GetCurrentCoreId()][KInterruptController::GetLocalInterruptIndex(irq)]; } bool OnHandleInterrupt(); public: constexpr KInterruptManager() = default; NOINLINE void Initialize(s32 core_id); NOINLINE void Finalize(s32 core_id); NOINLINE void Save(s32 core_id); NOINLINE void Restore(s32 core_id); bool IsInterruptDefined(s32 irq) const { return m_interrupt_controller.IsInterruptDefined(irq); } bool IsGlobal(s32 irq) const { return m_interrupt_controller.IsGlobal(irq); } bool IsLocal(s32 irq) const { return m_interrupt_controller.IsLocal(irq); } NOINLINE Result BindHandler(KInterruptHandler *handler, s32 irq, s32 core_id, s32 priority, bool manual_clear, bool level); NOINLINE Result UnbindHandler(s32 irq, s32 core); NOINLINE Result ClearInterrupt(s32 irq, s32 core_id); ALWAYS_INLINE void SendInterProcessorInterrupt(s32 irq, u64 core_mask) { m_interrupt_controller.SendInterProcessorInterrupt(irq, core_mask); } ALWAYS_INLINE void SendInterProcessorInterrupt(s32 irq) { m_interrupt_controller.SendInterProcessorInterrupt(irq); } static void HandleInterrupt(bool user_mode); private: Result BindGlobal(KInterruptHandler *handler, s32 irq, s32 core_id, s32 priority, bool manual_clear, bool level); Result BindLocal(KInterruptHandler *handler, s32 irq, s32 priority, bool manual_clear); Result UnbindGlobal(s32 irq); Result UnbindLocal(s32 irq); Result ClearGlobal(s32 irq); Result ClearLocal(s32 irq); private: [[nodiscard]] static ALWAYS_INLINE u32 GetInterruptsEnabledState() { u64 intr_state; __asm__ __volatile__("mrs %[intr_state], daif\n" "ubfx %[intr_state], %[intr_state], #7, #1" : [intr_state]"=r"(intr_state) :: "memory"); return intr_state; } public: static ALWAYS_INLINE void EnableInterrupts() { __asm__ __volatile__("msr daifclr, #2" ::: "memory"); } static ALWAYS_INLINE void DisableInterrupts() { __asm__ __volatile__("msr daifset, #2" ::: "memory"); } [[nodiscard]] static ALWAYS_INLINE u32 GetInterruptsEnabledStateAndDisableInterrupts() { const auto intr_state = GetInterruptsEnabledState(); DisableInterrupts(); return intr_state; } [[nodiscard]] static ALWAYS_INLINE u32 GetInterruptsEnabledStateAndEnableInterrupts() { const auto intr_state = GetInterruptsEnabledState(); EnableInterrupts(); return intr_state; } static ALWAYS_INLINE void RestoreInterrupts(u32 intr_state) { u64 tmp; __asm__ __volatile__("mrs %[tmp], daif\n" "bfi %[tmp], %[intr_state], #7, #1\n" "msr daif, %[tmp]" : [tmp]"=&r"(tmp) : [intr_state]"r"(intr_state) : "memory"); } static ALWAYS_INLINE bool AreInterruptsEnabled() { return GetInterruptsEnabledState() == 0; } }; }