thermosphere: handle physical IRQs

This commit is contained in:
TuxSH 2019-08-11 00:56:49 +02:00
parent 271d2a0ddb
commit 0a9a8c2f15
10 changed files with 267 additions and 12 deletions

View file

@ -145,7 +145,7 @@ export QEMU := qemu-system-aarch64
#export QEMU := ~/qemu/aarch64-softmmu/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\ 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! # NOTE: copy bl1.bin, bl2.bin, bl31.bin from your own build of Arm Trusted Firmware!

View file

@ -23,8 +23,9 @@ typedef struct CoreCtx {
u8 *crashStack; // @0x10 u8 *crashStack; // @0x10
u64 scratch; // @0x18 u64 scratch; // @0x18
u32 coreId; // @0x20 u32 coreId; // @0x20
bool isBootCore; // @0x24 u8 gicInterfaceId; // @0x24
bool warmboot; // @0x25 bool isBootCore; // @0x25
bool warmboot; // @0x26
} CoreCtx; } CoreCtx;
extern CoreCtx g_coreCtxs[4]; extern CoreCtx g_coreCtxs[4];

View file

@ -216,13 +216,20 @@ vector_entry synch_spx
mov x0, sp mov x0, sp
mrs x1, esr_el2 mrs x1, esr_el2
bl handleSameElSyncException bl handleSameElSyncException
b . b .
check_vector_size synch_spx check_vector_size synch_spx
vector_entry irq_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 check_vector_size irq_spx
vector_entry fiq_spx vector_entry fiq_spx
@ -239,14 +246,20 @@ vector_entry synch_a64
mov x0, sp mov x0, sp
mrs x1, esr_el2 mrs x1, esr_el2
bl handleLowerElSyncException bl handleLowerElSyncException
b _restore_all_regs b _restore_all_regs
check_vector_size synch_a64 check_vector_size synch_a64
vector_entry irq_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 check_vector_size irq_a64
vector_entry fiq_a64 vector_entry fiq_a64
@ -264,7 +277,14 @@ vector_entry synch_a32
check_vector_size synch_a32 check_vector_size synch_a32
vector_entry irq_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 check_vector_size irq_a32
vector_entry fiq_a32 vector_entry fiq_a32

View file

@ -18,6 +18,10 @@
#include "types.h" #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 { typedef struct ArmGicV2Distributor {
u32 ctlr; u32 ctlr;
u32 typer; u32 typer;

163
thermosphere/src/irq.c Normal file
View 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
View 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));
}

View file

@ -12,6 +12,8 @@
#include "breakpoints.h" #include "breakpoints.h"
#include "watchpoints.h" #include "watchpoints.h"
#include "irq.h"
extern const u8 __start__[]; extern const u8 __start__[];
static void loadKernelViaSemihosting(void) static void loadKernelViaSemihosting(void)
@ -41,6 +43,7 @@ void main(ExceptionStackFrame *frame)
{ {
enableTraps(); enableTraps();
enableBreakpointsAndWatchpoints(); enableBreakpointsAndWatchpoints();
initIrq();
if (currentCoreCtx->isBootCore) { if (currentCoreCtx->isBootCore) {
uartInit(115200); uartInit(115200);
@ -74,4 +77,8 @@ void main(ExceptionStackFrame *frame)
// Test // Test
singleStepSetNextState(frame, SingleStepState_ActivePending); singleStepSetNextState(frame, SingleStepState_ActivePending);
// Test
unmaskIrq();
generateSgiForAll(0);
} }

View file

@ -21,7 +21,9 @@
// For both guest and host // For both guest and host
#define MAX_NUM_REGISTERED_INTERRUPTS 512 #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->gicd = (volatile ArmGicV2Distributor *)0x08000000ull;
gic->gicc = (volatile ArmGicV2Controller *)0x08010000ull; gic->gicc = (volatile ArmGicV2Controller *)0x08010000ull;

View file

@ -21,7 +21,9 @@
// For both guest and host // For both guest and host
#define MAX_NUM_REGISTERED_INTERRUPTS 512 #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->gicd = (volatile ArmGicV2Distributor *)0x50041000ull;
gic->gicc = (volatile ArmGicV2Controller *)0x50042000ull; gic->gicc = (volatile ArmGicV2Controller *)0x50042000ull;

View file

@ -59,7 +59,7 @@ static inline u64 spinlockLockMaskIrq(Spinlock *lock)
return ret; return ret;
} }
static inline void spinlockLockRestoreIrq(Spinlock *lock, u64 flags) static inline void spinlockUnlockRestoreIrq(Spinlock *lock, u64 flags)
{ {
spinlockUnlock(lock); spinlockUnlock(lock);
restoreInterruptFlags(flags); restoreInterruptFlags(flags);
@ -91,7 +91,7 @@ static inline u64 recursiveSpinlockLockMaskIrq(RecursiveSpinlock *lock)
return ret; return ret;
} }
static inline void recursiveSpinlockLockRestoreIrq(RecursiveSpinlock *lock, u64 flags) static inline void recursiveSpinlockUnlockRestoreIrq(RecursiveSpinlock *lock, u64 flags)
{ {
recursiveSpinlockUnlock(lock); recursiveSpinlockUnlock(lock);
restoreInterruptFlags(flags); restoreInterruptFlags(flags);