mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-15 09:36:35 +00:00
thermosphere: irq manager wip
This commit is contained in:
parent
785b7e1a37
commit
dd9b3ddb0d
8 changed files with 610 additions and 235 deletions
|
@ -26,7 +26,7 @@ namespace ams::hvisor::cpu {
|
||||||
struct DebugRegisterPair {
|
struct DebugRegisterPair {
|
||||||
// For breakpoints only
|
// For breakpoints only
|
||||||
/// BT[3:1] or res0. BT[0]/WT[0] is "is linked"
|
/// BT[3:1] or res0. BT[0]/WT[0] is "is linked"
|
||||||
enum BreakpointType {
|
enum BreakpointType : u32 {
|
||||||
AddressMatch = 0,
|
AddressMatch = 0,
|
||||||
VheContextIdMatch = 1,
|
VheContextIdMatch = 1,
|
||||||
ContextIdMatch = 3,
|
ContextIdMatch = 3,
|
||||||
|
@ -40,7 +40,7 @@ namespace ams::hvisor::cpu {
|
||||||
// Refer to "Table D2-9 Summary of breakpoint HMC, SSC, and PMC encodings"
|
// Refer to "Table D2-9 Summary of breakpoint HMC, SSC, and PMC encodings"
|
||||||
|
|
||||||
/// Security State Control
|
/// Security State Control
|
||||||
enum SecurityStateControl {
|
enum SecurityStateControl : u32 {
|
||||||
Both = 0,
|
Both = 0,
|
||||||
NonSecure = 1,
|
NonSecure = 1,
|
||||||
Secure = 2,
|
Secure = 2,
|
||||||
|
@ -48,13 +48,13 @@ namespace ams::hvisor::cpu {
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Higher Mode Control
|
/// Higher Mode Control
|
||||||
enum HigherModeControl {
|
enum HigherModeControl : u32 {
|
||||||
LowerEl = 0,
|
LowerEl = 0,
|
||||||
HigherEl = 1,
|
HigherEl = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Privilege Mode Control (called PAC for watchpoints)
|
/// Privilege Mode Control (called PAC for watchpoints)
|
||||||
enum PrivilegeModeControl {
|
enum PrivilegeModeControl : u32 {
|
||||||
NeitherEl1Nor0 = 0,
|
NeitherEl1Nor0 = 0,
|
||||||
El1 = 1,
|
El1 = 1,
|
||||||
El0 = 2,
|
El0 = 2,
|
||||||
|
@ -62,7 +62,7 @@ namespace ams::hvisor::cpu {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Watchpoints only
|
// Watchpoints only
|
||||||
enum LoadStoreControl {
|
enum LoadStoreControl : u32 {
|
||||||
NotAWatchpoint = 0,
|
NotAWatchpoint = 0,
|
||||||
Load = 1,
|
Load = 1,
|
||||||
Store = 2,
|
Store = 2,
|
||||||
|
|
|
@ -25,7 +25,7 @@ namespace ams::hvisor::cpu {
|
||||||
// FIXME GCC 10
|
// FIXME GCC 10
|
||||||
|
|
||||||
struct ExceptionSyndromeRegister {
|
struct ExceptionSyndromeRegister {
|
||||||
enum ExceptionClass {
|
enum ExceptionClass : u32 {
|
||||||
Uncategorized = 0x0,
|
Uncategorized = 0x0,
|
||||||
WFxTrap = 0x1,
|
WFxTrap = 0x1,
|
||||||
CP15RTTrap = 0x3,
|
CP15RTTrap = 0x3,
|
||||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#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;
|
|
183
thermosphere/src/hvisor_gicv2.hpp
Normal file
183
thermosphere/src/hvisor_gicv2.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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<GicV2Distributor>);
|
||||||
|
static_assert(std::is_standard_layout_v<GicV2Controller>);
|
||||||
|
static_assert(std::is_standard_layout_v<GicV2VirtualInterfaceController>);
|
||||||
|
static_assert(std::is_standard_layout_v<GicV2VirtualInterface>);
|
||||||
|
|
||||||
|
static_assert(std::is_trivial_v<GicV2Distributor>);
|
||||||
|
static_assert(std::is_trivial_v<GicV2Controller>);
|
||||||
|
static_assert(std::is_trivial_v<GicV2VirtualInterfaceController>);
|
||||||
|
static_assert(std::is_trivial_v<GicV2VirtualInterface>);
|
||||||
|
}
|
290
thermosphere/src/hvisor_irq_manager.cpp
Normal file
290
thermosphere/src/hvisor_irq_manager.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#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<u8>(BIT(__builtin_popcount(gicd->ipriorityr[0])));
|
||||||
|
|
||||||
|
m_numCpuInterfaces = static_cast<u8>(1 + ((gicd->typer >> 5) & 7));
|
||||||
|
m_numListRegisters = static_cast<u8>(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));
|
||||||
|
}*/
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
128
thermosphere/src/hvisor_irq_manager.hpp
Normal file
128
thermosphere/src/hvisor_irq_manager.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#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));
|
|
||||||
}
|
|
|
@ -16,8 +16,6 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../../gicv2.h"
|
|
||||||
|
|
||||||
#define MEMORY_MAP_PA_GICD 0x08000000ull
|
#define MEMORY_MAP_PA_GICD 0x08000000ull
|
||||||
#define MEMORY_MAP_PA_GICC 0x08010000ull
|
#define MEMORY_MAP_PA_GICC 0x08010000ull
|
||||||
#define MEMORY_MAP_PA_GICH 0x08030000ull
|
#define MEMORY_MAP_PA_GICH 0x08030000ull
|
||||||
|
@ -31,8 +29,8 @@
|
||||||
#define GIC_IRQID_NS_PHYS_TIMER 30
|
#define GIC_IRQID_NS_PHYS_TIMER 30
|
||||||
|
|
||||||
|
|
||||||
#define GIC_IRQID_NS_VIRT_HYP_TIMER GIC_IRQID_SPURIOUS // SBSA: 28. Unimplemented
|
#define GIC_IRQID_NS_VIRT_HYP_TIMER 1023 // SBSA: 28. Unimplemented
|
||||||
#define GIC_IRQID_SEC_PHYS_HYP_TIMER GIC_IRQID_SPURIOUS // SBSA: 20. Unimplemented
|
#define GIC_IRQID_SEC_PHYS_HYP_TIMER 1023 // SBSA: 20. Unimplemented
|
||||||
#define GIC_IRQID_SEC_VIRT_HYP_TIMER GIC_IRQID_SPURIOUS // SBSA: 19. Unimplemented
|
#define GIC_IRQID_SEC_VIRT_HYP_TIMER 1023 // SBSA: 19. Unimplemented
|
||||||
|
|
||||||
#define GIC_IRQID_UART (32 + 1)
|
#define GIC_IRQID_UART (32 + 1)
|
||||||
|
|
Loading…
Reference in a new issue