mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2025-01-03 11:11:14 +00:00
thermosphere: handle physical IRQs
This commit is contained in:
parent
271d2a0ddb
commit
0a9a8c2f15
10 changed files with 267 additions and 12 deletions
|
@ -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!
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
163
thermosphere/src/irq.c
Normal file
163
thermosphere/src/irq.c
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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
|
||||
}
|
||||
}
|
56
thermosphere/src/irq.h
Normal file
56
thermosphere/src/irq.h
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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));
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue