From dd9b3ddb0d597b7526e7e410ebfee53dd1eeeaae Mon Sep 17 00:00:00 2001
From: TuxSH <1922548+TuxSH@users.noreply.github.com>
Date: Tue, 11 Feb 2020 01:50:33 +0000
Subject: [PATCH] thermosphere: irq manager wip
---
.../cpu/hvisor_cpu_debug_register_pair.hpp | 10 +-
.../src/cpu/hvisor_cpu_exception_sysregs.hpp | 2 +-
thermosphere/src/gicv2.h | 145 ---------
thermosphere/src/hvisor_gicv2.hpp | 183 +++++++++++
thermosphere/src/hvisor_irq_manager.cpp | 290 ++++++++++++++++++
thermosphere/src/hvisor_irq_manager.hpp | 128 ++++++++
thermosphere/src/irq.h | 79 -----
.../src/platform/qemu/interrupt_config.h | 8 +-
8 files changed, 610 insertions(+), 235 deletions(-)
delete mode 100644 thermosphere/src/gicv2.h
create mode 100644 thermosphere/src/hvisor_gicv2.hpp
create mode 100644 thermosphere/src/hvisor_irq_manager.cpp
create mode 100644 thermosphere/src/hvisor_irq_manager.hpp
delete mode 100644 thermosphere/src/irq.h
diff --git a/thermosphere/src/cpu/hvisor_cpu_debug_register_pair.hpp b/thermosphere/src/cpu/hvisor_cpu_debug_register_pair.hpp
index 6989c6db4..c9fc1e23c 100644
--- a/thermosphere/src/cpu/hvisor_cpu_debug_register_pair.hpp
+++ b/thermosphere/src/cpu/hvisor_cpu_debug_register_pair.hpp
@@ -26,7 +26,7 @@ namespace ams::hvisor::cpu {
struct DebugRegisterPair {
// For breakpoints only
/// BT[3:1] or res0. BT[0]/WT[0] is "is linked"
- enum BreakpointType {
+ enum BreakpointType : u32 {
AddressMatch = 0,
VheContextIdMatch = 1,
ContextIdMatch = 3,
@@ -40,7 +40,7 @@ namespace ams::hvisor::cpu {
// Refer to "Table D2-9 Summary of breakpoint HMC, SSC, and PMC encodings"
/// Security State Control
- enum SecurityStateControl {
+ enum SecurityStateControl : u32 {
Both = 0,
NonSecure = 1,
Secure = 2,
@@ -48,13 +48,13 @@ namespace ams::hvisor::cpu {
};
/// Higher Mode Control
- enum HigherModeControl {
+ enum HigherModeControl : u32 {
LowerEl = 0,
HigherEl = 1,
};
/// Privilege Mode Control (called PAC for watchpoints)
- enum PrivilegeModeControl {
+ enum PrivilegeModeControl : u32 {
NeitherEl1Nor0 = 0,
El1 = 1,
El0 = 2,
@@ -62,7 +62,7 @@ namespace ams::hvisor::cpu {
};
// Watchpoints only
- enum LoadStoreControl {
+ enum LoadStoreControl : u32 {
NotAWatchpoint = 0,
Load = 1,
Store = 2,
diff --git a/thermosphere/src/cpu/hvisor_cpu_exception_sysregs.hpp b/thermosphere/src/cpu/hvisor_cpu_exception_sysregs.hpp
index 5949d69bc..faa0053fc 100644
--- a/thermosphere/src/cpu/hvisor_cpu_exception_sysregs.hpp
+++ b/thermosphere/src/cpu/hvisor_cpu_exception_sysregs.hpp
@@ -25,7 +25,7 @@ namespace ams::hvisor::cpu {
// FIXME GCC 10
struct ExceptionSyndromeRegister {
- enum ExceptionClass {
+ enum ExceptionClass : u32 {
Uncategorized = 0x0,
WFxTrap = 0x1,
CP15RTTrap = 0x3,
diff --git a/thermosphere/src/gicv2.h b/thermosphere/src/gicv2.h
deleted file mode 100644
index 8a2e6f9ec..000000000
--- a/thermosphere/src/gicv2.h
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (c) 2019 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 "types.h"
-
-#define GIC_IRQID_MAX 1019
-#define GIC_IRQID_RESERVED_START 1020
-
-#define GIC_IRQID_SPURIOUS_GRPNEEDACK (GIC_IRQID_RESERVED_START + 2)
-#define GIC_IRQID_SPURIOUS (GIC_IRQID_RESERVED_START + 3)
-#define GICV_PRIO_LEVELS 32
-#define GICV_IDLE_PRIORITY 0xF8 // sometimes 0xFF
-
-typedef struct ArmGicV2Distributor {
- u32 ctlr;
- u32 typer;
- u32 iidr;
- u8 _0x0c[0x80 - 0x0C];
- // Note: in reality only 512 interrupts max. are defined (nor "reserved") on Gicv2
- u32 igroupr[1024 / 32];
- u32 isenabler[1024 / 32];
- u32 icenabler[1024 / 32];
- u32 ispendr[1024 / 32];
- u32 icpendr[1024 / 32];
- u32 isactiver[1024 / 32];
- u32 icactiver[1024 / 32];
- u8 ipriorityr[1024]; // can be accessed as u8 or u32
- u8 itargetsr[1024]; // can be accessed as u8 or u32
- u32 icfgr[1024 / 16];
- u8 impldef_d00[0xF00 - 0xD00];
- u32 sgir;
- u8 _0xf04[0xF10 - 0xF04];
- u8 cpendsgir[16];
- u8 spendsgir[16];
- u8 _0xf30[0xFE8 - 0xF30];
- u32 icpidr2;
- u8 _0xfec[0x1000 - 0xFEC];
-} ArmGicV2Distributor;
-
-typedef struct ArmGicV2Controller {
- u32 ctlr;
- u32 pmr;
- u32 bpr;
- u32 iar;
- u32 eoir;
- u32 rpr;
- u32 hppir;
- u32 abpr;
- u32 aiar;
- u32 aeoir;
- u32 ahppir;
- u8 _0x2c[0x40 - 0x2C];
- u8 impldef_40[0xD0 - 0x40];
- u32 apr[4];
- u32 nsapr[4];
- u8 _0xf0[0xFC - 0xF0];
- u32 iidr;
- u8 _0x100[0x1000 - 0x100];
- u32 dir;
- u8 _0x1004[0x2000 - 0x1004];
-} ArmGicV2Controller;
-
-typedef struct ArmGicV2HypervisorControlRegister {
- u32 en : 1;
- u32 uie : 1;
- u32 lrenpie : 1;
- u32 npie : 1;
- u32 vgrp0eie : 1;
- u32 vgrp0die : 1;
- u32 vgrp1eie : 1;
- u32 vgrp1die : 1;
- u32 _8 : 19;
- u32 eoiCount : 5;
-} ArmGicV2HypervisorControlRegister;
-
-typedef struct ArmGicV2MaintenanceIntStatRegister {
- u32 eoi : 1;
- u32 u : 1;
- u32 lrenp : 1;
- u32 np : 1;
- u32 vgrp0e : 1;
- u32 vgrp0d : 1;
- u32 vgrp1e : 1;
- u32 vgrp1d : 1;
- u32 _8 : 24;
-} ArmGicV2MaintenanceIntStatRegister;
-
-typedef struct ArmGicV2ListRegister {
- u32 virtualId : 10;
- u32 physicalId : 10; // note: different encoding if hw = 0 (can't represent it in struct)
- u32 sbz2 : 3;
- u32 priority : 5;
- u32 pending : 1;
- u32 active : 1;
- u32 grp1 : 1;
- u32 hw : 1;
-} ArmGicV2ListRegister;
-
-typedef struct ArmGicV2VmControlRegister {
- u32 enableGrp0 : 1;
- u32 enableGrp1 : 1;
- u32 ackCtl : 1;
- u32 fiqEn : 1;
- u32 cbpr : 1;
- u32 _5 : 4;
- u32 eoiMode : 1;
- u32 _10 : 8;
- u32 abpr : 3;
- u32 bpr : 3;
- u32 _24 : 3;
- u32 pmr : 5;
-} ArmGicV2VmControlRegister;
-
-typedef struct ArmGicV2VirtualInterfaceController {
- ArmGicV2HypervisorControlRegister hcr;
- u32 vtr;
- ArmGicV2VmControlRegister vmcr;
- u8 _0x0c[0x10 - 0xC];
- ArmGicV2MaintenanceIntStatRegister misr;
- u8 _0x14[0x20 - 0x14];
- u32 eisr0;
- u32 eisr1;
- u8 _0x28[0x30 - 0x28];
- u32 elsr0;
- u32 elsr1;
- u8 _0x38[0xF0 - 0x38];
- u32 apr;
- u8 _0xf4[0x100 - 0xF4];
- ArmGicV2ListRegister lr[64];
-} ArmGicV2VirtualInterfaceController;
diff --git a/thermosphere/src/hvisor_gicv2.hpp b/thermosphere/src/hvisor_gicv2.hpp
new file mode 100644
index 000000000..0569ef4d0
--- /dev/null
+++ b/thermosphere/src/hvisor_gicv2.hpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2019-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 .
+ */
+
+#pragma once
+
+#include "defines.hpp"
+
+namespace ams::hvisor {
+
+ struct GicV2Distributor {
+ static constexpr u32 maxIrqId = 1019;
+ static constexpr u32 spuriousGrpNeedAckIrqId = 1022;
+ static constexpr u32 spuriousIrqId = 1023;
+
+ u32 ctlr;
+ u32 typer;
+ u32 iidr;
+ u8 _0x0c[0x80 - 0x0C];
+ // Note: in reality only 512 interrupts max. are defined (nor "reserved") on Gicv2
+ u32 igroupr[1024 / 32];
+ u32 isenabler[1024 / 32];
+ u32 icenabler[1024 / 32];
+ u32 ispendr[1024 / 32];
+ u32 icpendr[1024 / 32];
+ u32 isactiver[1024 / 32];
+ u32 icactiver[1024 / 32];
+ u8 ipriorityr[1024]; // can be accessed as u8 or u32
+ u8 itargetsr[1024]; // can be accessed as u8 or u32
+ u32 icfgr[1024 / 16];
+ u8 impldef_d00[0xF00 - 0xD00];
+ u32 sgir;
+ u8 _0xf04[0xF10 - 0xF04];
+ u8 cpendsgir[16];
+ u8 spendsgir[16];
+ u8 _0xf30[0xFE8 - 0xF30];
+ u32 icpidr2;
+ u8 _0xfec[0x1000 - 0xFEC];
+
+ enum SgirTargetListFilter : u32 {
+ ForwardToTargetList = 0,
+ ForwardToAllOthers = 1,
+ ForwardToSelf = 2,
+ };
+
+ static constexpr int GetCfgrShift(u32 id) {
+ return 2 * (id % 16);
+ }
+ };
+
+ struct GicV2Controller {
+ u32 ctlr;
+ u32 pmr;
+ u32 bpr;
+ u32 iar;
+ u32 eoir;
+ u32 rpr;
+ u32 hppir;
+ u32 abpr;
+ u32 aiar;
+ u32 aeoir;
+ u32 ahppir;
+ u8 _0x2c[0x40 - 0x2C];
+ u8 impldef_40[0xD0 - 0x40];
+ u32 apr[4];
+ u32 nsapr[4];
+ u8 _0xf0[0xFC - 0xF0];
+ u32 iidr;
+ u8 _0x100[0x1000 - 0x100];
+ u32 dir;
+ u8 _0x1004[0x2000 - 0x1004];
+ };
+
+ // GICH
+ struct GicV2VirtualInterfaceController {
+ union HypervisorControlRegister {
+ struct {
+ u32 en : 1;
+ u32 uie : 1;
+ u32 lrenpie : 1;
+ u32 npie : 1;
+ u32 vgrp0eie : 1;
+ u32 vgrp0die : 1;
+ u32 vgrp1eie : 1;
+ u32 vgrp1die : 1;
+ u32 _8 : 19;
+ u32 eoiCount : 5;
+ };
+ u32 raw;
+ };
+
+ union VmControlRegister {
+ struct {
+ u32 enableGrp0 : 1;
+ u32 enableGrp1 : 1;
+ u32 ackCtl : 1;
+ u32 fiqEn : 1;
+ u32 cbpr : 1;
+ u32 _5 : 4;
+ u32 eoiMode : 1;
+ u32 _10 : 8;
+ u32 abpr : 3;
+ u32 bpr : 3;
+ u32 _24 : 3;
+ u32 pmr : 5;
+ };
+ u32 raw;
+ };
+
+ union MaintenanceIntStatRegister {
+ struct {
+ u32 eoi : 1;
+ u32 u : 1;
+ u32 lrenp : 1;
+ u32 np : 1;
+ u32 vgrp0e : 1;
+ u32 vgrp0d : 1;
+ u32 vgrp1e : 1;
+ u32 vgrp1d : 1;
+ u32 _8 : 24;
+ };
+ u32 raw;
+ };
+
+ union ListRegister {
+ struct {
+ u32 virtualId : 10;
+ u32 physicalId : 10; // note: different encoding if hw = 0 (can't represent it in struct)
+ u32 sbz2 : 3;
+ u32 priority : 5;
+ u32 pending : 1;
+ u32 active : 1;
+ u32 grp1 : 1;
+ u32 hw : 1;
+ };
+ u32 raw;
+ };
+
+ HypervisorControlRegister hcr;
+ u32 vtr;
+ VmControlRegister vmcr;
+ u8 _0x0c[0x10 - 0xC];
+ MaintenanceIntStatRegister misr;
+ u8 _0x14[0x20 - 0x14];
+ u32 eisr0;
+ u32 eisr1;
+ u8 _0x28[0x30 - 0x28];
+ u32 elsr0;
+ u32 elsr1;
+ u8 _0x38[0xF0 - 0x38];
+ u32 apr;
+ u8 _0xf4[0x100 - 0xF4];
+ ListRegister lr[64];
+ };
+
+ struct GicV2VirtualInterface : public GicV2Controller {
+ // Allowed because no non-static members
+ static constexpr u32 numPriorityLevels = 32;
+ static constexpr u8 idlePriorityLevel = 0xF8;
+ };
+
+ static_assert(std::is_standard_layout_v);
+ static_assert(std::is_standard_layout_v);
+ static_assert(std::is_standard_layout_v);
+ static_assert(std::is_standard_layout_v);
+
+ static_assert(std::is_trivial_v);
+ static_assert(std::is_trivial_v);
+ static_assert(std::is_trivial_v);
+ static_assert(std::is_trivial_v);
+}
diff --git a/thermosphere/src/hvisor_irq_manager.cpp b/thermosphere/src/hvisor_irq_manager.cpp
new file mode 100644
index 000000000..bb5d761ab
--- /dev/null
+++ b/thermosphere/src/hvisor_irq_manager.cpp
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2019-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 .
+ */
+
+#include
+
+#include "hvisor_irq_manager.hpp"
+
+#include "platform/interrupt_config.h"
+#include "core_ctx.h"
+#include "guest_timers.h"
+#include "transport_interface.h"
+#include "timer.h"
+
+#include "vgic.h"
+//#include "debug_manager.h"
+
+namespace {
+
+ inline bool checkRescheduleEmulatedPtimer(ExceptionStackFrame *frame)
+ {
+ // Evaluate if the timer has really expired in the PoV of the guest kernel.
+ // If not, reschedule (add missed time delta) it & exit early
+ u64 cval = currentCoreCtx->emulPtimerCval;
+ u64 vct = computeCntvct(frame);
+
+ if (cval > vct) {
+ // It has not: reschedule the timer
+ // Note: this isn't 100% precise esp. on QEMU so it may take a few tries...
+ writeEmulatedPhysicalCompareValue(frame, cval);
+ return false;
+ }
+
+ return true;
+ }
+
+ inline bool checkGuestTimerInterrupts(ExceptionStackFrame *frame, u32 irqId)
+ {
+ // A thing that might have happened is losing the race vs disabling the guest interrupts
+ // Another thing is that the virtual timer might have fired before us updating voff when executing a top half?
+ if (irqId == TIMER_IRQID(NS_VIRT_TIMER)) {
+ u64 cval = THERMOSPHERE_GET_SYSREG(cntp_cval_el0);
+ return cval <= computeCntvct(frame);
+ } else if (irqId == TIMER_IRQID(NS_PHYS_TIMER)) {
+ return checkRescheduleEmulatedPtimer(frame);
+ } else {
+ return true;
+ }
+ }
+
+}
+
+namespace ams::hvisor {
+
+ bool IrqManager::IsGuestInterrupt(u32 id)
+ {
+ // We don't care about the interrupts we don't use
+
+ bool ret = true;
+ ret = ret && id != GIC_IRQID_MAINTENANCE;
+ ret = ret && id != GIC_IRQID_NS_PHYS_HYP_TIMER;
+
+ ret = ret && transportInterfaceFindByIrqId(id) == NULL;
+ return ret;
+ }
+
+ void IrqManager::InitializeGic()
+ {
+ // Reinits the GICD and GICC (for non-secure mode, obviously)
+ if (currentCoreCtx->isBootCore && !currentCoreCtx->warmboot) {
+ // Disable interrupt handling & global interrupt distribution
+ gicd->ctlr = 0;
+
+ // Get some info
+ m_numSharedInterrupts = 32 * (gicd->typer & 0x1F); // number of interrupt lines / 32
+
+ // unimplemented priority bits (lowest significant) are RAZ/WI
+ gicd->ipriorityr[0] = 0xFF;
+ m_priorityShift = 8 - __builtin_popcount(gicd->ipriorityr[0]);
+ m_numPriorityLevels = static_cast(BIT(__builtin_popcount(gicd->ipriorityr[0])));
+
+ m_numCpuInterfaces = static_cast(1 + ((gicd->typer >> 5) & 7));
+ m_numListRegisters = static_cast(1 + (gich->vtr & 0x3F));
+ }
+
+ // Only one core will reset the GIC state for the shared peripheral interrupts
+
+ u32 numInterrupts = 32;
+ if (currentCoreCtx->isBootCore) {
+ numInterrupts += m_numSharedInterrupts;
+ }
+
+ // Filter all interrupts
+ gicc->pmr = 0;
+
+ // Disable interrupt preemption
+ gicc->bpr = 7;
+
+ // Note: the GICD I...n regs are banked for private interrupts
+
+ // Disable all interrupts, clear active status, clear pending status
+ for (u32 i = 0; i < numInterrupts / 32; i++) {
+ gicd->icenabler[i] = 0xFFFFFFFF;
+ gicd->icactiver[i] = 0xFFFFFFFF;
+ gicd->icpendr[i] = 0xFFFFFFFF;
+ }
+
+ // Set priorities to lowest
+ for (u32 i = 0; i < numInterrupts; i++) {
+ gicd->ipriorityr[i] = 0xFF;
+ }
+
+ // Reset icfgr, itargetsr for shared peripheral interrupts
+ for (u32 i = 32 / 16; i < numInterrupts / 16; i++) {
+ gicd->icfgr[i] = 0x55555555;
+ }
+
+ for (u32 i = 32; i < numInterrupts; i++) {
+ gicd->itargetsr[i] = 0;
+ }
+
+ // Now, reenable interrupts
+
+ // Enable the distributor
+ if (currentCoreCtx->isBootCore) {
+ gicd->ctlr = 1;
+ }
+
+ // Enable the CPU interface. Set EOIModeNS=1 (split prio drop & deactivate priority)
+ gicc->ctlr = BIT(9) | 1;
+
+ // Disable interrupt filtering
+ gicc->pmr = 0xFF;
+ }
+
+ void IrqManager::DoConfigureInterrupt(u32 id, u8 prio, bool isLevelSensitive)
+ {
+ ClearInterruptEnabled(id);
+ ClearInterruptPending(id);
+ if (id >= 32) {
+ SetInterruptMode(id, isLevelSensitive);
+ DoSetInterruptAffinity(id, 0xFF); // all possible processors
+ }
+ SetInterruptShiftedPriority(id, prio << m_priorityShift);
+ SetInterruptEnabled(id);
+ }
+
+ void IrqManager::Initialize()
+ {
+ u64 flags = MaskIrq();
+ m_lock.lock();
+
+ InitializeGic();
+ for (u32 i = 0; i < MaxSgi; i++) {
+ DoConfigureInterrupt(i, hostPriority, false);
+ }
+
+ DoConfigureInterrupt(GIC_IRQID_MAINTENANCE, hostPriority, true);
+
+ vgicInit();
+
+ m_lock.unlock();
+ RestoreInterruptFlags(flags);
+ }
+
+ void IrqManager::ConfigureInterrupt(u32 id, u8 prio, bool isLevelSensitive)
+ {
+ u64 flags = MaskIrq();
+ m_lock.lock();
+ DoConfigureInterrupt(id, prio, isLevelSensitive);
+ m_lock.unlock();
+ RestoreInterruptFlags(flags);
+ }
+
+ void IrqManager::SetInterruptAffinity(u32 id, u8 affinity)
+ {
+ u64 flags = MaskIrq();
+ m_lock.lock();
+ DoSetInterruptAffinity(id, affinity);
+ m_lock.unlock();
+ RestoreInterruptFlags(flags);
+ }
+
+ void IrqManager::HandleInterrupt(ExceptionStackFrame *frame)
+ {
+ // TODO refactor c parts
+
+ // Acknowledge the interrupt. Interrupt goes from pending to active.
+ u32 iar = AcknowledgeIrq();
+ u32 irqId = iar & 0x3FF;
+ u32 srcCore = (iar >> 10) & 7;
+
+ //DEBUG("EL2 [core %d]: Received irq %x\n", (int)currentCoreCtx->coreId, irqId);
+
+ if (irqId == GicV2Distributor::spuriousIrqId) {
+ // Spurious interrupt received
+ return;
+ } else if (!checkGuestTimerInterrupts(frame, irqId)) {
+ // Deactivate the interrupt, return early
+ DropCurrentInterruptPriority(iar);
+ DeactivateCurrentInterrupt(iar);
+ return;
+ }
+
+ bool isGuestInterrupt = false;
+ bool isMaintenanceInterrupt = false;
+ bool isPaused = false;
+ bool hasDebugEvent = false;
+
+ switch (irqId) {
+ case ExecuteFunctionSgi:
+ executeFunctionInterruptHandler(srcCore);
+ break;
+ case VgicUpdateSgi:
+ // Nothing in particular to do here
+ break;
+ case DebugPauseSgi:
+ // TODO debugManagerPauseSgiHandler();
+ break;
+ case ReportDebuggerBreakSgi:
+ case DebuggerContinueSgi:
+ // See bottom halves
+ // Because exceptions (other debug events) are handling w/ interrupts off, if
+ // we get there, there's no race condition possible with debugManagerReportEvent
+ break;
+ case GIC_IRQID_MAINTENANCE:
+ isMaintenanceInterrupt = true;
+ break;
+ case TIMER_IRQID(CURRENT_TIMER):
+ timerInterruptHandler();
+ break;
+ default:
+ isGuestInterrupt = irqId >= 16;
+ break;
+ }
+
+ TransportInterface *transportIface = irqId >= 32 ? transportInterfaceIrqHandlerTopHalf(irqId) : NULL;
+
+ // Priority drop
+ DropCurrentInterruptPriority(iar);
+
+ isGuestInterrupt = isGuestInterrupt && transportIface == NULL && IsGuestInterrupt(irqId);
+
+ instance.m_lock.lock();
+
+ if (!isGuestInterrupt) {
+ if (isMaintenanceInterrupt) {
+ vgicMaintenanceInterruptHandler();
+ }
+ // Deactivate the interrupt
+ DeactivateCurrentInterrupt(iar);
+ } else {
+ vgicEnqueuePhysicalIrq(irqId);
+ }
+
+ // Update vgic state
+ vgicUpdateState();
+
+ instance.m_lock.unlock();
+
+ // TODO
+
+ /*isPaused = debugManagerIsCorePaused(currentCoreCtx->coreId);
+ hasDebugEvent = debugManagerHasDebugEvent(currentCoreCtx->coreId);
+ if (irqId == ThermosphereSgi_ReportDebuggerBreak) DEBUG("debug event=%d\n", (int)debugManagerGetDebugEvent(currentCoreCtx->coreId)->type);
+ // Bottom half part
+ if (transportIface != NULL) {
+ exceptionEnterInterruptibleHypervisorCode();
+ unmaskIrq();
+ transportInterfaceIrqHandlerBottomHalf(transportIface);
+ } else if (irqId == ThermosphereSgi_ReportDebuggerBreak && !hasDebugEvent) {
+ debugManagerReportEvent(DBGEVENT_DEBUGGER_BREAK);
+ } else if (irqId == DebuggerContinueSgi && isPaused) {
+ debugManagerUnpauseCores(BIT(currentCoreCtx->coreId));
+ }*/
+
+ }
+}
diff --git a/thermosphere/src/hvisor_irq_manager.hpp b/thermosphere/src/hvisor_irq_manager.hpp
new file mode 100644
index 000000000..2f37200f0
--- /dev/null
+++ b/thermosphere/src/hvisor_irq_manager.hpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2019-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 .
+ */
+
+#pragma once
+
+#include "hvisor_gicv2.hpp"
+#include "hvisor_synchronization.hpp"
+#include "cpu/hvisor_cpu_sysreg_general.hpp"
+#include "memory_map.h"
+
+#include "exceptions.h" // TODO
+
+namespace ams::hvisor {
+
+ class IrqManager final {
+ NON_COPYABLE(IrqManager);
+ NON_MOVEABLE(IrqManager);
+ friend class VirtualGic;
+ private:
+ static IrqManager instance;
+ static constexpr u8 hostPriority = 0;
+ static constexpr u8 guestPriority = 1;
+
+ static inline volatile auto *const gicd = (volatile GicV2Distributor *)MEMORY_MAP_VA_GICD;
+ static inline volatile auto *const gicc = (volatile GicV2Controller *)MEMORY_MAP_VA_GICC;
+ static inline volatile auto *const gich = (volatile GicV2VirtualInterfaceController *)MEMORY_MAP_VA_GICH;
+
+ static bool IsGuestInterrupt(u32 id);
+
+ static void SetInterruptEnabled(u32 id) { gicd->isenabler[id / 32] = BIT(id % 32); }
+ static void ClearInterruptEnabled(u32 id) { gicd->icenabler[id / 32] = BIT(id % 32); }
+ static void ClearInterruptPending(u32 id) { gicd->icpendr[id / 32] = BIT(id % 32); }
+ static void SetInterruptShiftedPriority(u32 id, u8 prio) { gicd->ipriorityr[id] = prio; }
+ static void DoSetInterruptAffinity(u32 id, u8 targetList) { gicd->itargetsr[id] = targetList; }
+ static bool IsInterruptLevelSensitive(u32 id)
+ {
+ return ((gicd->icfgr[id / 16] >> GicV2Distributor::GetCfgrShift(id)) & 2) != 0;
+ }
+ static void SetInterruptMode(u32 id, bool isLevelSensitive)
+ {
+ u32 cfgw = gicd->icfgr[id / 16];
+ cfgw &= ~(2 << GicV2Distributor::GetCfgrShift(id));
+ cfgw |= (isLevelSensitive ? 2 : 0) << GicV2Distributor::GetCfgrShift(id);
+ gicd->icfgr[id / 16] = cfgw;
+ }
+
+ static u32 AcknowledgeIrq() { return gicc->iar; }
+ static void DropCurrentInterruptPriority(u32 iar) { gicc->eoir = iar; }
+ static void DeactivateCurrentInterrupt(u32 iar) { gicc->dir = iar; }
+
+ private:
+ mutable RecursiveSpinlock m_lock{};
+ u32 m_numSharedInterrupts = 0;
+ u8 m_priorityShift = 0;
+ u8 m_numPriorityLevels = 0;
+ u8 m_numCpuInterfaces = 0;
+ u8 m_numListRegisters = 0;
+
+ private:
+ void InitializeGic();
+ void DoConfigureInterrupt(u32 id, u8 prio, bool isLevelSensitive);
+
+ public:
+ enum ThermosphereSgi : u32 {
+ ExecuteFunctionSgi = 0,
+ VgicUpdateSgi,
+ DebugPauseSgi,
+ ReportDebuggerBreakSgi,
+ DebuggerContinueSgi,
+
+ MaxSgi,
+ };
+
+ static void GenerateSgiForList(ThermosphereSgi id, u32 coreList)
+ {
+ gicd->sgir = GicV2Distributor::ForwardToTargetList << 24 | coreList << 16 | id;
+ }
+ static void GenerateSgiForAllOthers(ThermosphereSgi id)
+ {
+ gicd->sgir = GicV2Distributor::ForwardToAllOthers << 24 | id;
+ }
+
+ static u64 MaskIrq()
+ {
+ u64 daif = THERMOSPHERE_GET_SYSREG(daif);
+ THERMOSPHERE_SET_SYSREG_IMM(daifset, BIT(1));
+ return daif;
+ }
+
+ static u64 UnmaskIrq()
+ {
+ u64 daif = THERMOSPHERE_GET_SYSREG(daif);
+ THERMOSPHERE_SET_SYSREG_IMM(daifclr, BIT(1));
+ return daif;
+ }
+
+ static void RestoreInterruptFlags(u64 flags)
+ {
+ THERMOSPHERE_SET_SYSREG(daif, flags);
+ }
+
+ static IrqManager &GetInstance() { return instance; }
+
+ static void HandleInterrupt(ExceptionStackFrame *frame);
+
+ public:
+ void Initialize();
+ void ConfigureInterrupt(u32 id, u8 prio, bool isLevelSensitive);
+ void SetInterruptAffinity(u32 id, u8 affinityMask);
+
+ public:
+ constexpr IrqManager() = default;
+ };
+
+}
diff --git a/thermosphere/src/irq.h b/thermosphere/src/irq.h
deleted file mode 100644
index f6c07eb92..000000000
--- a/thermosphere/src/irq.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (c) 2019 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 "gicv2.h"
-#include "spinlock.h"
-#include "exceptions.h"
-#include "utils.h"
-#include "platform/interrupt_config.h"
-#include "memory_map.h"
-
-#define IRQ_PRIORITY_HOST 0
-#define IRQ_PRIORITY_GUEST 1
-
-#define IRQ_CFGR_SHIFT(id) (2*((id) % 16))
-
-typedef struct IrqManager {
- RecursiveSpinlock lock;
- u16 numSharedInterrupts;
- u8 priorityShift;
- u8 numPriorityLevels;
- u8 numCpuInterfaces;
- u8 numListRegisters;
-} IrqManager;
-
-typedef enum ThermosphereSgi {
- ThermosphereSgi_ExecuteFunction = 0,
- ThermosphereSgi_VgicUpdate,
- ThermosphereSgi_DebugPause,
- ThermosphereSgi_ReportDebuggerBreak,
- ThermosphereSgi_DebuggerContinue,
-
- ThermosphereSgi_Max,
-} ThermosphereSgi;
-
-static volatile ArmGicV2Distributor *const gicd = (volatile ArmGicV2Distributor *)MEMORY_MAP_VA_GICD;
-static volatile ArmGicV2Controller *const gicc = (volatile ArmGicV2Controller *)MEMORY_MAP_VA_GICC;
-static volatile ArmGicV2VirtualInterfaceController *const gich = (volatile ArmGicV2VirtualInterfaceController *)MEMORY_MAP_VA_GICH;
-
-extern IrqManager g_irqManager;
-
-void initIrq(void);
-void configureInterrupt(u16 id, u8 prio, bool isLevelSensitive);
-bool irqIsGuest(u16 id);
-void irqSetAffinity(u16 id, u8 affinityMask);
-
-static inline void generateSgiForAllOthers(ThermosphereSgi id)
-{
- gicd->sgir = (1 << 24) | ((u32)id & 0xF);
-}
-
-static inline void generateSgiForSelf(ThermosphereSgi id)
-{
- gicd->sgir = (2 << 24) | ((u32)id & 0xF);
-}
-
-static inline void generateSgiForList(ThermosphereSgi id, u32 list)
-{
- gicd->sgir = (0 << 24) | (list << 16) | ((u32)id & 0xF);
-}
-
-static inline void generateSgiForAll(ThermosphereSgi id)
-{
- generateSgiForList(id, MASK(g_irqManager.numCpuInterfaces));
-}
diff --git a/thermosphere/src/platform/qemu/interrupt_config.h b/thermosphere/src/platform/qemu/interrupt_config.h
index dc639c11d..074589bd7 100644
--- a/thermosphere/src/platform/qemu/interrupt_config.h
+++ b/thermosphere/src/platform/qemu/interrupt_config.h
@@ -16,8 +16,6 @@
#pragma once
-#include "../../gicv2.h"
-
#define MEMORY_MAP_PA_GICD 0x08000000ull
#define MEMORY_MAP_PA_GICC 0x08010000ull
#define MEMORY_MAP_PA_GICH 0x08030000ull
@@ -31,8 +29,8 @@
#define GIC_IRQID_NS_PHYS_TIMER 30
-#define GIC_IRQID_NS_VIRT_HYP_TIMER GIC_IRQID_SPURIOUS // SBSA: 28. Unimplemented
-#define GIC_IRQID_SEC_PHYS_HYP_TIMER GIC_IRQID_SPURIOUS // SBSA: 20. Unimplemented
-#define GIC_IRQID_SEC_VIRT_HYP_TIMER GIC_IRQID_SPURIOUS // SBSA: 19. Unimplemented
+#define GIC_IRQID_NS_VIRT_HYP_TIMER 1023 // SBSA: 28. Unimplemented
+#define GIC_IRQID_SEC_PHYS_HYP_TIMER 1023 // SBSA: 20. Unimplemented
+#define GIC_IRQID_SEC_VIRT_HYP_TIMER 1023 // SBSA: 19. Unimplemented
#define GIC_IRQID_UART (32 + 1)