vgic: fix multiple bugs

This commit is contained in:
TuxSH 2019-12-26 00:33:38 +00:00
parent f3ad62d1b8
commit fe0662a75d
2 changed files with 40 additions and 6 deletions

View file

@ -99,7 +99,7 @@ typedef struct ArmGicV2MaintenanceIntStatRegister {
} ArmGicV2MaintenanceIntStatRegister; } ArmGicV2MaintenanceIntStatRegister;
typedef struct ArmGicV2ListRegister { typedef struct ArmGicV2ListRegister {
u32 virtualId : 9; u32 virtualId : 10;
u32 physicalId : 10; // note: different encoding if hw = 0 (can't represent it in struct) u32 physicalId : 10; // note: different encoding if hw = 0 (can't represent it in struct)
u32 sbz2 : 3; u32 sbz2 : 3;
u32 priority : 5; u32 priority : 5;

View file

@ -735,7 +735,7 @@ static inline volatile ArmGicV2ListRegister *vgicGetFreeListRegister(void)
static void vgicPushListRegisters(VirqState *chosen[], size_t num) static void vgicPushListRegisters(VirqState *chosen[], size_t num)
{ {
for (size_t i = 0; i < num; num++) { for (size_t i = 0; i < num; i++) {
VirqState *state = chosen[i]; VirqState *state = chosen[i];
u16 irqId = vgicGetVirqStateInterruptId(state); u16 irqId = vgicGetVirqStateInterruptId(state);
@ -820,19 +820,29 @@ void vgicUpdateState(void)
// Choose interrupts... // Choose interrupts...
newHiPrio = vgicChoosePendingInterrupts(&numChosen, chosen, numFreeLr); newHiPrio = vgicChoosePendingInterrupts(&numChosen, chosen, numFreeLr);
(void)newHiPrio;
// ...and push them // ...and push them
for (size_t i = 0; i < numChosen; i++) { for (size_t i = 0; i < numChosen; i++) {
vgicPushListRegisters(chosen, numChosen); vgicPushListRegisters(chosen, numChosen);
} }
/*
// Raise vIRQ when applicable. We only need to check for the highest priority // Raise vIRQ when applicable. We only need to check for the highest priority
// TRM: "The GIC always masks an interrupt that has the largest supported priority field value. // TRM: "The GIC always masks an interrupt that has the largest supported priority field value.
// This provides an additional means of preventing an interrupt being signaled to any processor" // This provides an additional means of preventing an interrupt being signaled to any processor"
if (newHiPrio < 0x1F && vgicIsInterruptRaisable(newHiPrio)) { if (false && newHiPrio < 0x1F && vgicIsInterruptRaisable(newHiPrio)) {
DEBUG("enablegrp0 %d\n", (int)gich->vmcr.enableGrp0);
DEBUG("enablegrp1 %d\n", (int)gich->vmcr.enableGrp1);
gich->hcr.npie = true;
u32 hcr = GET_SYSREG(hcr_el2); u32 hcr = GET_SYSREG(hcr_el2);
SET_SYSREG(hcr_el2, hcr | HCR_VI); SET_SYSREG(hcr_el2, hcr | HCR_VI);
} else {
//DEBUG("unraising\n");
gich->hcr.npie = false;
u32 hcr = GET_SYSREG(hcr_el2);
SET_SYSREG(hcr_el2, hcr & ~HCR_VI);
} }
*/
// Enable underflow interrupt when appropriate to do so // Enable underflow interrupt when appropriate to do so
if (vgicGetNumberOfFreeListRegisters() != g_irqManager.numListRegisters) { if (vgicGetNumberOfFreeListRegisters() != g_irqManager.numListRegisters) {
@ -844,13 +854,28 @@ void vgicUpdateState(void)
void vgicMaintenanceInterruptHandler(void) void vgicMaintenanceInterruptHandler(void)
{ {
ArmGicV2MaintenanceIntStatRegister misr = g_irqManager.gic.gich->misr; volatile ArmGicV2VirtualInterfaceController *gich = g_irqManager.gic.gich;
ArmGicV2MaintenanceIntStatRegister misr = g_irqManager.gic.gich->misr;
DEBUG("maintenance\n");
// Force GICV_CTRL to behave like ns-GICC_CTLR, with group 1 being replaced by group 0 // Force GICV_CTRL to behave like ns-GICC_CTLR, with group 1 being replaced by group 0
// Ensure we aren't spammed by maintenance interrupts, either.
if (misr.vgrp0e || misr.vgrp0d || misr.vgrp1e || misr.vgrp1d) { if (misr.vgrp0e || misr.vgrp0d || misr.vgrp1e || misr.vgrp1d) {
g_irqManager.gic.gicv->ctlr &= BIT(9) | BIT(0); g_irqManager.gic.gicv->ctlr &= BIT(9) | BIT(0);
} }
if (misr.vgrp0e) {
gich->hcr.vgrp0eie = false;
gich->hcr.vgrp0die = true;
} else if (misr.vgrp0d) {
gich->hcr.vgrp0eie = true;
gich->hcr.vgrp0die = false;
}
if (misr.vgrp1e) {
// Nothing to do since we unset the bit asap
}
if (misr.lrenp) { if (misr.lrenp) {
DEBUG("VGIC: List Register Entry Not Present maintenance interrupt!"); DEBUG("VGIC: List Register Entry Not Present maintenance interrupt!");
panic(); panic();
@ -858,6 +883,7 @@ void vgicMaintenanceInterruptHandler(void)
// The rest should be handled by the main loop... // The rest should be handled by the main loop...
} }
void handleVgicdMmio(ExceptionStackFrame *frame, DataAbortIss dabtIss, size_t offset) void handleVgicdMmio(ExceptionStackFrame *frame, DataAbortIss dabtIss, size_t offset)
{ {
size_t sz = BITL(dabtIss.sas); size_t sz = BITL(dabtIss.sas);
@ -918,5 +944,13 @@ void vgicInit(void)
} }
} }
g_irqManager.gic.gich->hcr.en = true; // Deassert vIRQ line, just in case
// Enable a few maintenance interrupts
ArmGicV2HypervisorControlRegister hcr = {
.vgrp1eie = true,
.vgrp0eie = true,
.lrenpie = true,
.en = true,
};
g_irqManager.gic.gich->hcr = hcr;
} }