diff --git a/thermosphere/src/core_ctx.h b/thermosphere/src/core_ctx.h index 716d20445..959c6d8c0 100644 --- a/thermosphere/src/core_ctx.h +++ b/thermosphere/src/core_ctx.h @@ -25,7 +25,7 @@ typedef struct CoreCtx { u8 *crashStack; // @0x10 u64 scratch; // @0x18 u32 coreId; // @0x20 - u8 gicInterfaceMask; // @0x24 + u8 gicInterfaceMask; // @0x24. Equal to BIT(coreId) anyway bool isBootCore; // @0x25 bool warmboot; // @0x26 diff --git a/thermosphere/src/irq.c b/thermosphere/src/irq.c index fa4127a0b..e7dc8c02d 100644 --- a/thermosphere/src/irq.c +++ b/thermosphere/src/irq.c @@ -181,6 +181,7 @@ void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32) // Deactivate the interrupt gicc->dir = iar; } else { + if (irqId == 30) g_irqManager.gic.gicd->ispendr[0x80/32] = 0xFFFFFFFF; vgicEnqueuePhysicalIrq(irqId); } diff --git a/thermosphere/src/irq.h b/thermosphere/src/irq.h index 69c908aa1..a80997d9f 100644 --- a/thermosphere/src/irq.h +++ b/thermosphere/src/irq.h @@ -70,12 +70,12 @@ static inline void generateSgiForAll(ThermosphereSgi id) generateSgiForList(id, MASK(g_irqManager.numCpuInterfaces)); } -static inline bool irqIsEnabled(u16 id) -{ - return (g_irqManager.gic.gicd->isenabler[id / 32] & BIT(id % 32)) != 0; -} - static inline bool irqIsGuest(u16 id) { + if (id >= 32 + g_irqManager.numSharedInterrupts) { + DEBUG("vgic: %u not supported by physical distributor\n", (u32)id); + return false; + } + return id != GIC_IRQID_MAINTENANCE && id != GIC_IRQID_HYP_TIMER; } diff --git a/thermosphere/src/vgic.c b/thermosphere/src/vgic.c index e51e52139..91cabaf07 100644 --- a/thermosphere/src/vgic.c +++ b/thermosphere/src/vgic.c @@ -38,7 +38,10 @@ typedef struct VirqState { bool active : 1; bool handled : 1; bool pendingLatch : 1; + bool levelSensitive : 1; u32 coreId : 3; // up to 8 cores, but not implemented yet + u32 targetList : 8; + bool enabled : 1; u64 : 0; } VirqState; @@ -281,29 +284,17 @@ static inline void vgicNotifyOtherCoreList(u32 coreList) } } -static inline bool vgicIsVirqEdgeTriggered(u16 id) -{ - // Note: banked per CPU for SGIs and PPIs - // SGIs are *always* edge-triggered, and we decide to keep all PPIs level-sensitive at all times. - - if (id < 16) { - return true; - } else { - return (g_irqManager.gic.gicd->icfgr[id / 16] & (2 << IRQ_CFGR_SHIFT(id))) != 0; - } -} - static inline bool vgicIsVirqPending(VirqState *state) { // In case we emulate ispendr in the future... // Note: this function is not 100% reliable. The interrupt might be active-not-pending or inactive // but it shouldn't matter since where we use it, it would only cause one extraneous SGI. - return state->pendingLatch || (!vgicIsVirqEdgeTriggered(vgicGetVirqStateInterruptId(state)) && state->pending); + return state->pendingLatch || (state->levelSensitive && state->pending); } static inline void vgicSetVirqPendingState(VirqState *state, bool val) { - if (!vgicIsVirqEdgeTriggered(vgicGetVirqStateInterruptId(state))) { + if (state->levelSensitive) { state->pending = val; } else { state->pendingLatch = val; @@ -351,24 +342,28 @@ static inline u32 vgicGetDistributorImplementerIdentificationRegister(void) static void vgicSetInterruptEnabledState(u16 id) { - if (id < 16 || !irqIsGuest(id) || irqIsEnabled(id)) { + VirqState *state = vgicGetVirqState(currentCoreCtx->coreId, id); + + if (id < 16 || !irqIsGuest(id) || state->enabled) { // Nothing to do... // Also, ignore for SGIs return; } // Similar effects to setting the target list to non-0 when it was 0... - VirqState *state = vgicGetVirqState(currentCoreCtx->coreId, id); if (vgicIsVirqPending(state)) { - vgicNotifyOtherCoreList(g_irqManager.gic.gicd->itargetsr[id]); + vgicNotifyOtherCoreList(state->targetList); } + state->enabled = true; g_irqManager.gic.gicd->isenabler[id / 32] = BIT(id % 32); } static void vgicClearInterruptEnabledState(u16 id) { - if (id < 16 || !irqIsGuest(id) || !irqIsEnabled(id)) { + VirqState *state = vgicGetVirqState(currentCoreCtx->coreId, id); + + if (id < 16 || !irqIsGuest(id) || !state->enabled) { // Nothing to do... // Also, ignore for SGIs return; @@ -376,18 +371,18 @@ static void vgicClearInterruptEnabledState(u16 id) // Similar effects to setting the target list to 0, we may need to notify the core // handling the interrupt if it's pending - VirqState *state = vgicGetVirqState(currentCoreCtx->coreId, id); if (state->handled) { vgicNotifyOtherCoreList(BIT(vgicGetVirqStateCoreId(state))); } + state->enabled = false; g_irqManager.gic.gicd->icenabler[id / 32] = BIT(id % 32); } static inline bool vgicGetInterruptEnabledState(u16 id) { // SGIs are always enabled - return id < 16 || (irqIsGuest(id) && irqIsEnabled(id)); + return id < 16 || (irqIsGuest(id) && vgicGetVirqState(currentCoreCtx->coreId, id)->enabled); } static void vgicSetInterruptPriorityByte(u16 id, u8 priority) @@ -412,7 +407,7 @@ static void vgicSetInterruptPriorityByte(u16 id, u8 priority) } state->priority = priority; - u32 targets = g_irqManager.gic.gicd->itargetsr[id]; + u32 targets = state->targetList; if (targets != 0 && vgicIsVirqPending(state)) { vgicNotifyOtherCoreList(targets); } @@ -439,19 +434,21 @@ static void vgicSetInterruptTargets(u16 id, u8 coreList) // Note that we take into account that the interrupt may be disabled. VirqState *state = vgicGetVirqState(currentCoreCtx->coreId, id); if (vgicIsVirqPending(state)) { - u8 oldList = g_irqManager.gic.gicd->itargetsr[id]; + u8 oldList = state->targetList; u8 diffList = (oldList ^ coreList) & getActiveCoreMask(); if (diffList != 0) { vgicNotifyOtherCoreList(diffList); } } - g_irqManager.gic.gicd->itargetsr[id] = coreList; + + state->targetList = coreList; + g_irqManager.gic.gicd->itargetsr[id] = state->targetList; } static inline u8 vgicGetInterruptTargets(u16 id) { // For SGIs & PPIs, itargetsr is banked and contains the CPU ID - return (id < 32 || irqIsGuest(id)) ? g_irqManager.gic.gicd->itargetsr[id] : 0; + return (id < 32 || irqIsGuest(id)) ? vgicGetVirqState(currentCoreCtx->coreId, id)->targetList : 0; } static inline void vgicSetInterruptConfigBits(u16 id, u32 config) @@ -461,17 +458,25 @@ static inline void vgicSetInterruptConfigBits(u16 id, u32 config) return; } + VirqState *state = vgicGetVirqState(currentCoreCtx->coreId, id); + // Expose bit(2n) as nonprogrammable to the guest no matter what the physical distributor actually behaves - u32 cfg = g_irqManager.gic.gicd->icfgr[id / 16]; - cfg &= ~(2 << IRQ_CFGR_SHIFT(id)); - cfg |= (config & 2) << IRQ_CFGR_SHIFT(id); - g_irqManager.gic.gicd->icfgr[id / 16] = cfg; + bool newLvl = ((config & 2) << IRQ_CFGR_SHIFT(id)) == 0; + + if (state->levelSensitive != newLvl) { + u32 cfg = g_irqManager.gic.gicd->icfgr[id / 16]; + cfg &= ~(3 << IRQ_CFGR_SHIFT(id)); + cfg |= (!newLvl ? 3 : 1) << IRQ_CFGR_SHIFT(id); + g_irqManager.gic.gicd->icfgr[id / 16] = cfg; + + state->levelSensitive = newLvl; + } } static inline u32 vgicGetInterruptConfigBits(u16 id) { u32 oneNModel = id < 32 || !irqIsGuest(id) ? 0 : 1; - return (irqIsGuest(id) && vgicIsVirqEdgeTriggered(id)) ? 2 | oneNModel : oneNModel; + return (irqIsGuest(id) && !vgicGetVirqState(currentCoreCtx->coreId, id)->levelSensitive) ? 2 | oneNModel : oneNModel; } static void vgicSetSgiPendingState(u16 id, u32 coreId, u32 srcCoreId) @@ -707,7 +712,7 @@ static void vgicCleanupPendingList(void) coreId = vgicGetVirqStateCoreId(node); if (id < 16) { pending = true; - } else if (!vgicIsVirqEdgeTriggered(id)) { + } else if (node->levelSensitive) { // For hardware interrupts, we have kept the interrupt active on the physical GICD // For level-sensitive interrupts, we need to check if they're also still physically pending (resampling). // If not, there's nothing to service anymore, and therefore we have to deactivate them, so that @@ -750,7 +755,7 @@ static bool vgicTestInterruptEligibility(VirqState *state) return false; } - return vgicGetInterruptEnabledState(id) && (id < 32 || (g_irqManager.gic.gicd->itargetsr[id] & BIT(currentCoreCtx->coreId)) != 0); + return vgicGetInterruptEnabledState(id) && (id < 32 || (state->targetList & BIT(currentCoreCtx->coreId)) != 0); } static void vgicChoosePendingInterrupts(size_t *outNumChosen, VirqState *chosen[], size_t maxNum) @@ -1034,12 +1039,30 @@ void vgicEnqueuePhysicalIrq(u16 irqId) void vgicInit(void) { if (currentCoreCtx->isBootCore) { + // All fields are reset to 0 on reset and deep sleep exit + g_virqPendingQueue.first = g_virqPendingQueue.last = vgicGetQueueEnd(); for (u32 i = 0; i < MAX_NUM_INTERRUPTS; i++) { g_virqStates[i].listNext = g_virqStates[i].listPrev = VIRQLIST_INVALID_ID; g_virqStates[i].priority = 0x1F; } + + for (u32 i = 0; i < 4; i++) { + // SGIs, PPIs + for (u16 j = 0; j < 32; j++) { + VirqState *state = vgicGetVirqState(i, j); + state->targetList = BIT(i); + if (j < 16) { + state->enabled = true; + } else { + state->levelSensitive = (g_irqManager.gic.gicd->icfgr[j / 16] & (2 << IRQ_CFGR_SHIFT(j % 16))) == 0; + } + } + } + + // All guest interrupts are initially configured as disabled + // All guest SPIs are initially configured as edge-triggered with no targets } // Deassert vIRQ line, just in case