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)