mirror of
https://github.com/Atmosphere-NX/Atmosphere-libs.git
synced 2025-11-17 18:11:17 +01:00
NOTE: This work is not yet fully complete; kernel is done, but it was taking an exceedingly long time to get through libstratosphere. Thus, I've temporarily added -Wno-error=unused-result for libstratosphere/stratosphere. All warnings should be fixed to do the same thing Nintendo does as relevant, but this is taking a phenomenally long time and is not actually the most important work to do, so it can be put off for some time to prioritize other tasks for 21.0.0 support.
152 lines
6.3 KiB
C++
152 lines
6.3 KiB
C++
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#pragma once
|
|
#include <vapours.hpp>
|
|
#include <mesosphere/kern_select_cpu.hpp>
|
|
#include <mesosphere/kern_k_spin_lock.hpp>
|
|
#include <mesosphere/kern_k_interrupt_task.hpp>
|
|
#include <mesosphere/kern_select_interrupt_controller.hpp>
|
|
|
|
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 void UnbindHandler(s32 irq, s32 core);
|
|
|
|
NOINLINE void 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);
|
|
void UnbindGlobal(s32 irq);
|
|
void UnbindLocal(s32 irq);
|
|
void ClearGlobal(s32 irq);
|
|
void 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;
|
|
}
|
|
};
|
|
|
|
}
|