thermosphere: vgic: fix critical bug in vgicUpdateState, add more checks

Yikes.
This commit is contained in:
TuxSH 2020-01-02 18:45:30 +00:00
parent b0d258209c
commit 0fb5f81e8a

View file

@ -22,6 +22,8 @@
#include "debug_log.h" #include "debug_log.h"
#define MAX_NUM_INTERRUPTS (512 - 32 + 32 * 4) #define MAX_NUM_INTERRUPTS (512 - 32 + 32 * 4)
#define VIRQLIST_END_ID MAX_NUM_INTERRUPTS
#define VIRQLIST_INVALID_ID (VIRQLIST_END_ID + 1)
#define GICDOFF(field) (offsetof(ArmGicV2Distributor, field)) #define GICDOFF(field) (offsetof(ArmGicV2Distributor, field))
@ -70,7 +72,12 @@ static inline VirqState *vgicGetPrevQueuedVirqState(VirqState *cur)
static inline VirqState *vgicGetQueueEnd(void) static inline VirqState *vgicGetQueueEnd(void)
{ {
return &g_virqStates[MAX_NUM_INTERRUPTS]; return &g_virqStates[VIRQLIST_END_ID];
}
static inline bool vgicIsStateQueued(VirqState *state)
{
return state->listPrev != VIRQLIST_INVALID_ID && state->listNext != VIRQLIST_INVALID_ID;
} }
static inline u32 vgicGetVirqStateIndex(VirqState *cur) static inline u32 vgicGetVirqStateIndex(VirqState *cur)
@ -81,12 +88,10 @@ static inline u32 vgicGetVirqStateIndex(VirqState *cur)
static inline u16 vgicGetVirqStateInterruptId(VirqState *cur) static inline u16 vgicGetVirqStateInterruptId(VirqState *cur)
{ {
u32 idx = vgicGetVirqStateIndex(cur); u32 idx = vgicGetVirqStateIndex(cur);
/*if (idx == MAX_NUM_INTERRUPTS) { if (idx >= 512 - 32) {
return GIC_IRQID_SPURIOUS;
} else*/ if (idx >= 512 - 32) {
return (idx - 512 + 32) % 32; return (idx - 512 + 32) % 32;
} else { } else {
return idx; return 32 + idx;
} }
} }
@ -105,16 +110,45 @@ static inline u32 vgicGetSgiCurrentSourceCoreId(VirqState *cur)
return cur->coreId; return cur->coreId;
} }
// export symbol to avoid build warnings
void vgicDebugPrintList(VirqStateList *list)
{
(void)list;
DEBUG("[");
for (VirqState *pos = list->first; pos != vgicGetQueueEnd(); pos = vgicGetNextQueuedVirqState(pos)) {
DEBUG("%u,", vgicGetVirqStateIndex(pos));
}
DEBUG("]\n");
}
// export symbol to avoid build warnings
void vgicDebugPrintLrList(void)
{
DEBUG("core %u lr [", currentCoreCtx->coreId);
for (u32 i = 0; i < g_irqManager.numListRegisters; i++) {
if (g_vgicUsedLrMap[currentCoreCtx->coreId] & BITL(i)) {
DEBUG("%u,", vgicGetVirqStateIndex(vgicGetVirqState(currentCoreCtx->coreId, g_irqManager.gic.gich->lr[i].virtualId)));
} else {
DEBUG("-,");
}
}
DEBUG("]\n");
}
// Note: ordered by priority // Note: ordered by priority
static void vgicEnqueueVirqState(VirqStateList *list, VirqState *elem) static void vgicEnqueueVirqState(VirqStateList *list, VirqState *elem)
{ {
VirqState *pos; VirqState *pos;
if (elem->listNext != VIRQLIST_INVALID_ID || elem->listPrev != VIRQLIST_INVALID_ID) {
PANIC("vgicEnqueueVirqState: unsanitized argument\n");
}
++list->size; ++list->size;
// Empty list // Empty list
if (list->first == vgicGetQueueEnd()) { if (list->first == vgicGetQueueEnd()) {
list->first = list->last = elem; list->first = list->last = elem;
elem->listPrev = elem->listNext = VIRQLIST_END_ID;
return; return;
} }
@ -149,7 +183,6 @@ static void vgicEnqueueVirqState(VirqStateList *list, VirqState *elem)
VirqState *prev = vgicGetPrevQueuedVirqState(pos); VirqState *prev = vgicGetPrevQueuedVirqState(pos);
prev->listNext = idx; prev->listNext = idx;
} }
} }
} }
@ -158,6 +191,10 @@ static void vgicDequeueVirqState(VirqStateList *list, VirqState *elem)
VirqState *prev = vgicGetPrevQueuedVirqState(elem); VirqState *prev = vgicGetPrevQueuedVirqState(elem);
VirqState *next = vgicGetNextQueuedVirqState(elem); VirqState *next = vgicGetNextQueuedVirqState(elem);
if (!vgicIsStateQueued(elem)) {
PANIC("vgicDequeueVirqState: invalid id %x\n", vgicGetVirqStateIndex(elem));
}
--list->size; --list->size;
if (prev != vgicGetQueueEnd()) { if (prev != vgicGetQueueEnd()) {
prev->listNext = elem->listNext; prev->listNext = elem->listNext;
@ -170,6 +207,8 @@ static void vgicDequeueVirqState(VirqStateList *list, VirqState *elem)
} else { } else {
list->last = prev; list->last = prev;
} }
elem->listPrev = elem->listNext = VIRQLIST_INVALID_ID;
} }
static inline void vgicNotifyOtherCoreList(u32 coreList) static inline void vgicNotifyOtherCoreList(u32 coreList)
@ -375,18 +414,16 @@ static inline u32 vgicGetInterruptConfigByte(u16 id, u32 config)
static void vgicSetSgiPendingState(u16 id, u32 coreId, u32 srcCoreId) static void vgicSetSgiPendingState(u16 id, u32 coreId, u32 srcCoreId)
{ {
DEBUG("EL2 [core %u]: sending vSGI %hu to core %u\n", srcCoreId, id, coreId); //DEBUG("EL2 [core %u]: sending vSGI %hu to core %u\n", srcCoreId, id, coreId);
VirqState *state = vgicGetVirqState(coreId, id); VirqState *state = vgicGetVirqState(coreId, id);
g_vgicIncomingSgiPendingSources[coreId][id] |= BIT(srcCoreId); g_vgicIncomingSgiPendingSources[coreId][id] |= BIT(srcCoreId);
if (!state->handled && !vgicIsVirqPending(state)) { if (!state->handled && !vgicIsStateQueued(state)) {
// The SGI was inactive on the target core... // The SGI was inactive on the target core...
state->pendingLatch = true; state->pendingLatch = true;
state->coreId = srcCoreId; state->coreId = srcCoreId;
g_vgicIncomingSgiPendingSources[coreId][id] &= ~BIT(srcCoreId); g_vgicIncomingSgiPendingSources[coreId][id] &= ~BIT(srcCoreId);
vgicEnqueueVirqState(&g_virqPendingQueue, state); vgicEnqueueVirqState(&g_virqPendingQueue, state);
vgicNotifyOtherCoreList(BIT(coreId)); vgicNotifyOtherCoreList(BIT(coreId));
} else if (!state->handled) {
vgicNotifyOtherCoreList(BIT(coreId));
} }
} }
@ -731,6 +768,10 @@ static bool vgicUpdateListRegister(volatile ArmGicV2ListRegister *lr)
u32 srcCoreId = state->coreId; u32 srcCoreId = state->coreId;
u32 coreId = currentCoreCtx->coreId; u32 coreId = currentCoreCtx->coreId;
if (!state->handled) {
PANIC("vgicUpdateListRegister: improper previous state for now pending irq idx %u, active=%d\n", vgicGetVirqStateIndex(state), (int)state->active);
}
state->active = lrCopy.active; state->active = lrCopy.active;
if (lrCopy.active) { if (lrCopy.active) {
@ -803,9 +844,7 @@ void vgicUpdateState(void)
vgicChoosePendingInterrupts(&numChosen, chosen, numFreeLr); vgicChoosePendingInterrupts(&numChosen, chosen, numFreeLr);
// ...and push them // ...and push them
for (size_t i = 0; i < numChosen; i++) {
vgicPushListRegisters(chosen, numChosen); vgicPushListRegisters(chosen, numChosen);
}
// Apparently, the following is not needed because the GIC generates it for us // Apparently, the following is not needed because the GIC generates it for us
@ -848,8 +887,7 @@ void vgicMaintenanceInterruptHandler(void)
} }
if (misr.lrenp) { if (misr.lrenp) {
DEBUG("EL2 [core %d]: List Register Entry Not Present maintenance interrupt!\n", currentCoreCtx->coreId); PANIC("EL2 [core %d]: List Register Entry Not Present maintenance interrupt!\n", currentCoreCtx->coreId);
panic();
} }
if (misr.eoi) { if (misr.eoi) {
@ -918,7 +956,7 @@ void vgicInit(void)
g_virqPendingQueue.first = g_virqPendingQueue.last = vgicGetQueueEnd(); g_virqPendingQueue.first = g_virqPendingQueue.last = vgicGetQueueEnd();
for (u32 i = 0; i < 512 - 32 + 32 * 4; i++) { for (u32 i = 0; i < 512 - 32 + 32 * 4; i++) {
g_virqStates[i].listNext = g_virqStates[i].listPrev = MAX_NUM_INTERRUPTS; g_virqStates[i].listNext = g_virqStates[i].listPrev = VIRQLIST_INVALID_ID;
g_virqStates[i].priority = 0x1F; g_virqStates[i].priority = 0x1F;
} }
} }