diff --git a/thermosphere/Makefile b/thermosphere/Makefile
index 6b5836500..f1c15903d 100644
--- a/thermosphere/Makefile
+++ b/thermosphere/Makefile
@@ -145,7 +145,7 @@ export QEMU := qemu-system-aarch64
#export QEMU := ~/qemu/aarch64-softmmu/qemu-system-aarch64
QEMUFLAGS := -nographic -machine virt,secure=on,virtualization=on,gic-version=2 -cpu cortex-a57 -smp 4 -m 1024\
- -bios bl1.bin -d unimp,int -semihosting-config enable,target=native -serial mon:stdio
+ -bios bl1.bin -d unimp -semihosting-config enable,target=native -serial mon:stdio
# NOTE: copy bl1.bin, bl2.bin, bl31.bin from your own build of Arm Trusted Firmware!
diff --git a/thermosphere/src/core_ctx.h b/thermosphere/src/core_ctx.h
index 2f73bc939..d37fd83a1 100644
--- a/thermosphere/src/core_ctx.h
+++ b/thermosphere/src/core_ctx.h
@@ -23,8 +23,9 @@ typedef struct CoreCtx {
u8 *crashStack; // @0x10
u64 scratch; // @0x18
u32 coreId; // @0x20
- bool isBootCore; // @0x24
- bool warmboot; // @0x25
+ u8 gicInterfaceId; // @0x24
+ bool isBootCore; // @0x25
+ bool warmboot; // @0x26
} CoreCtx;
extern CoreCtx g_coreCtxs[4];
diff --git a/thermosphere/src/exception_vectors.s b/thermosphere/src/exception_vectors.s
index c09ac4b96..4480606f2 100644
--- a/thermosphere/src/exception_vectors.s
+++ b/thermosphere/src/exception_vectors.s
@@ -216,13 +216,20 @@ vector_entry synch_spx
mov x0, sp
mrs x1, esr_el2
-
bl handleSameElSyncException
b .
check_vector_size synch_spx
vector_entry irq_spx
- bl unknown_exception
+ save_all_regs
+
+ mov x0, sp
+ mov w1, wzr
+ mov w2, wzr
+ bl handleIrqException
+
+ b _restore_all_regs
+
check_vector_size irq_spx
vector_entry fiq_spx
@@ -239,14 +246,20 @@ vector_entry synch_a64
mov x0, sp
mrs x1, esr_el2
-
bl handleLowerElSyncException
b _restore_all_regs
check_vector_size synch_a64
vector_entry irq_a64
- bl unknown_exception
+ save_all_regs_reload_x18
+
+ mov x0, sp
+ mov w1, #1
+ mov w2, wzr
+ bl handleIrqException
+
+ b _restore_all_regs
check_vector_size irq_a64
vector_entry fiq_a64
@@ -264,7 +277,14 @@ vector_entry synch_a32
check_vector_size synch_a32
vector_entry irq_a32
- bl unknown_exception
+ save_all_regs_reload_x18
+
+ mov x0, sp
+ mov w1, #1
+ mov w2, #1
+ bl handleIrqException
+
+ b _restore_all_regs
check_vector_size irq_a32
vector_entry fiq_a32
diff --git a/thermosphere/src/gicv2.h b/thermosphere/src/gicv2.h
index ac6358a86..3bb45471a 100644
--- a/thermosphere/src/gicv2.h
+++ b/thermosphere/src/gicv2.h
@@ -18,6 +18,10 @@
#include "types.h"
+#define GIC_IRQID_MAX 1020
+#define GIC_IRQID_SPURIOUS_GRPNEEDACK (GIC_IRQID_MAX + 2)
+#define GIC_IRQID_SPURIOUS (GIC_IRQID_MAX + 3)
+
typedef struct ArmGicV2Distributor {
u32 ctlr;
u32 typer;
diff --git a/thermosphere/src/irq.c b/thermosphere/src/irq.c
new file mode 100644
index 000000000..8e936f1b1
--- /dev/null
+++ b/thermosphere/src/irq.c
@@ -0,0 +1,163 @@
+/*
+ * 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 .
+ */
+
+#include "irq.h"
+#include "platform/interrupt_config.h"
+#include "core_ctx.h"
+#include "debug_log.h"
+
+IrqManager g_irqManager = {0};
+
+static void initGic(void)
+{
+ // Reinits the GICD and GICC (for non-secure mode, obviously)
+ if (currentCoreCtx->isBootCore && !currentCoreCtx->warmboot) {
+ initGicV2Pointers(&g_irqManager.gic);
+
+ // Disable interrupt handling & global interrupt distribution
+ g_irqManager.gic.gicd->ctlr = 0;
+
+ // Get some info
+ g_irqManager.numSharedInterrupts = 32 * (g_irqManager.gic.gicd->typer & 0x1F); // number of interrupt lines / 32
+
+ // unimplemented priority bits (lowest significant) are RAZ/WI
+ g_irqManager.gic.gicd->ipriorityr[0] = 0xFF;
+ g_irqManager.numPriorityLevels = (u8)BIT(__builtin_popcount(g_irqManager.gic.gicd->ipriorityr[0]));
+
+ // same thing for CPU interfaces targets (save for ITARGETSR registers corresponding to SGIs)
+ g_irqManager.gic.gicd->itargetsr[32] = 0xFF;
+ g_irqManager.numCpuInterfaces = (u8)__builtin_popcount(g_irqManager.gic.gicd->itargetsr[32]);
+ }
+
+ volatile ArmGicV2Controller *gicc = g_irqManager.gic.gicc;
+ volatile ArmGicV2Distributor *gicd = g_irqManager.gic.gicd;
+
+ // Only one core will reset the GIC state for the shared peripheral interrupts
+
+ u32 numInterrupts = 32;
+ if (currentCoreCtx->isBootCore) {
+ numInterrupts += g_irqManager.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, also clear secure regs igroupr to be sure
+ for (u32 i = 0; i < numInterrupts / 32; i++) {
+ gicd->icenabler[i] = 0xFFFFFFFF;
+ gicd->icactiver[i] = 0xFFFFFFFF;
+ gicd->icpendr[i] = 0xFFFFFFFF;
+ gicd->igroupr[i] = 0x00000000;
+ }
+
+ // 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] = 0;
+ }
+
+ 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;
+
+ currentCoreCtx->gicInterfaceId = gicd->itargetsr[0];
+}
+
+static void configureInterrupt(u16 id, u8 prio, bool isLevelSensitive)
+{
+ volatile ArmGicV2Distributor *gicd = g_irqManager.gic.gicd;
+ gicd->icenabler[id / 32] |= BIT(id % 32);
+
+ if (id >= 32) {
+ gicd->icfgr[id / 16] &= ~3 << (2 * (id % 16));
+ gicd->icfgr[id / 16] |= (!isLevelSensitive ? 2 : 0) << (2 * (id % 16));
+ }
+
+ if (id >= 16) {
+ gicd->itargetsr[id] |= currentCoreCtx->gicInterfaceId;
+ }
+
+ gicd->icpendr[id / 32] |= BIT(id % 32);
+ gicd->ipriorityr[id] = prio;
+ gicd->isenabler[id / 32] |= BIT(id % 32);
+}
+
+void initIrq(void)
+{
+ u64 flags = recursiveSpinlockLockMaskIrq(&g_irqManager.lock);
+
+ initGic();
+
+ // Configure the interrupts we use here
+ configureInterrupt(0, 0, false);
+ configureInterrupt(GIC_IRQID_MAINTENANCE, 0, true);
+
+ recursiveSpinlockUnlockRestoreIrq(&g_irqManager.lock, flags);
+}
+
+void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32)
+{
+ (void)isLowerEl;
+ (void)isA32;
+ volatile ArmGicV2Controller *gicc = g_irqManager.gic.gicc;
+
+ // Acknowledge the interrupt. Interrupt goes from pending to active.
+ u32 iar = gicc->iar;
+ u32 irqId = iar & 0x3FF;
+
+ DEBUG("Received irq %x\n", irqId);
+
+ if (irqId == GIC_IRQID_SPURIOUS) {
+ // Spurious interrupt received
+ return;
+ }
+
+ bool isGuestInterrupt = false;
+
+ // TODO: handle the interrupt if it's a host interrupt
+
+ // Priority drop
+ gicc->eoir = iar;
+
+ if (!isGuestInterrupt) {
+ // Deactivate the interrupt
+ gicc->dir = iar;
+ } else {
+ // TODO
+ }
+}
diff --git a/thermosphere/src/irq.h b/thermosphere/src/irq.h
new file mode 100644
index 000000000..0f21ebb7b
--- /dev/null
+++ b/thermosphere/src/irq.h
@@ -0,0 +1,56 @@
+/*
+ * 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"
+
+typedef struct IrqManager {
+ RecursiveSpinlock lock;
+ ArmGicV2 gic;
+ u8 numPriorityLevels;
+ u8 numCpuInterfaces;
+ u8 numSharedInterrupts;
+ // Note: we don't store interrupt handlers since we will handle some SGI + uart interrupt(s)...
+} IrqManager;
+
+extern IrqManager g_irqManager;
+
+void initIrq(void);
+void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32);
+
+static inline void generateSgiForAllOthers(u32 id)
+{
+ g_irqManager.gic.gicd->sgir = (1 << 24) | (id & 0xF);
+}
+
+static inline void generateSgiForSelf(u32 id)
+{
+ g_irqManager.gic.gicd->sgir = (2 << 24) | (id & 0xF);
+}
+
+static inline void generateSgiForList(u32 id, u32 list)
+{
+ g_irqManager.gic.gicd->sgir = (0 << 24) | (list << 16) | (id & 0xF);
+}
+
+static inline void generateSgiForAll(u32 id)
+{
+ generateSgiForList(id, MASK(g_irqManager.numCpuInterfaces));
+}
diff --git a/thermosphere/src/main.c b/thermosphere/src/main.c
index 342f78a90..c1b9b3895 100644
--- a/thermosphere/src/main.c
+++ b/thermosphere/src/main.c
@@ -12,6 +12,8 @@
#include "breakpoints.h"
#include "watchpoints.h"
+#include "irq.h"
+
extern const u8 __start__[];
static void loadKernelViaSemihosting(void)
@@ -41,6 +43,7 @@ void main(ExceptionStackFrame *frame)
{
enableTraps();
enableBreakpointsAndWatchpoints();
+ initIrq();
if (currentCoreCtx->isBootCore) {
uartInit(115200);
@@ -74,4 +77,8 @@ void main(ExceptionStackFrame *frame)
// Test
singleStepSetNextState(frame, SingleStepState_ActivePending);
+
+ // Test
+ unmaskIrq();
+ generateSgiForAll(0);
}
diff --git a/thermosphere/src/platform/qemu/interrupt_config.h b/thermosphere/src/platform/qemu/interrupt_config.h
index 84b626c8d..f1b5de3aa 100644
--- a/thermosphere/src/platform/qemu/interrupt_config.h
+++ b/thermosphere/src/platform/qemu/interrupt_config.h
@@ -21,7 +21,9 @@
// For both guest and host
#define MAX_NUM_REGISTERED_INTERRUPTS 512
-static inline void initGicv2Pointers(ArmGicV2 *gic)
+#define GIC_IRQID_MAINTENANCE 25
+
+static inline void initGicV2Pointers(ArmGicV2 *gic)
{
gic->gicd = (volatile ArmGicV2Distributor *)0x08000000ull;
gic->gicc = (volatile ArmGicV2Controller *)0x08010000ull;
diff --git a/thermosphere/src/platform/tegra/interrupt_config.h b/thermosphere/src/platform/tegra/interrupt_config.h
index 0470f1a27..8fcc7c310 100644
--- a/thermosphere/src/platform/tegra/interrupt_config.h
+++ b/thermosphere/src/platform/tegra/interrupt_config.h
@@ -21,7 +21,9 @@
// For both guest and host
#define MAX_NUM_REGISTERED_INTERRUPTS 512
-static inline void initGicv2Pointers(ArmGicV2 *gic)
+#define GIC_IRQID_MAINTENANCE 25
+
+static inline void initGicV2Pointers(ArmGicV2 *gic)
{
gic->gicd = (volatile ArmGicV2Distributor *)0x50041000ull;
gic->gicc = (volatile ArmGicV2Controller *)0x50042000ull;
diff --git a/thermosphere/src/spinlock.h b/thermosphere/src/spinlock.h
index 4fff76cc4..8fea94718 100644
--- a/thermosphere/src/spinlock.h
+++ b/thermosphere/src/spinlock.h
@@ -59,7 +59,7 @@ static inline u64 spinlockLockMaskIrq(Spinlock *lock)
return ret;
}
-static inline void spinlockLockRestoreIrq(Spinlock *lock, u64 flags)
+static inline void spinlockUnlockRestoreIrq(Spinlock *lock, u64 flags)
{
spinlockUnlock(lock);
restoreInterruptFlags(flags);
@@ -91,7 +91,7 @@ static inline u64 recursiveSpinlockLockMaskIrq(RecursiveSpinlock *lock)
return ret;
}
-static inline void recursiveSpinlockLockRestoreIrq(RecursiveSpinlock *lock, u64 flags)
+static inline void recursiveSpinlockUnlockRestoreIrq(RecursiveSpinlock *lock, u64 flags)
{
recursiveSpinlockUnlock(lock);
restoreInterruptFlags(flags);