summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorvboxsync <vboxsync@cfe28804-0f27-0410-a406-dd0f0b0b656f>2023-05-10 17:28:24 +0000
committervboxsync <vboxsync@cfe28804-0f27-0410-a406-dd0f0b0b656f>2023-05-10 17:28:24 +0000
commitd3bd81c0c3fc003a91f8b3109777bb3df4a01e30 (patch)
tree86ff4a51d8f9153a4c9b894e8ba988179dc11eda
parentc03068b5b05c37277ce58920c715eb0ef002441e (diff)
downloadVirtualBox-svn-d3bd81c0c3fc003a91f8b3109777bb3df4a01e30.tar.gz
VMM/GIC: Updates to the implementation, implement forwarding of SGIs and PPIs, bugref:10404
git-svn-id: https://www.virtualbox.org/svn/vbox/trunk@99734 cfe28804-0f27-0410-a406-dd0f0b0b656f
-rw-r--r--include/VBox/gic.h45
-rw-r--r--include/VBox/vmm/gic.h5
-rw-r--r--src/VBox/VMM/VMMAll/GICAll.cpp598
-rw-r--r--src/VBox/VMM/include/GICInternal.h39
4 files changed, 662 insertions, 25 deletions
diff --git a/include/VBox/gic.h b/include/VBox/gic.h
index 34c5d124f3f..7379dcd236e 100644
--- a/include/VBox/gic.h
+++ b/include/VBox/gic.h
@@ -42,6 +42,45 @@
#include <iprt/types.h>
#include <iprt/armv8.h>
+/** @name INTIDs - Interrupt identifier ranges.
+ * @{ */
+/** Start of the SGI (Software Generated Interrupts) range. */
+#define GIC_INTID_RANGE_SGI_START 0
+/** Last valid SGI (Software Generated Interrupts) identifier. */
+#define GIC_INTID_RANGE_SGI_LAST 15
+
+/** Start of the PPI (Private Peripheral Interrupts) range. */
+#define GIC_INTID_RANGE_PPI_START 16
+/** Last valid PPI (Private Peripheral Interrupts) identifier. */
+#define GIC_INTID_RANGE_PPI_LAST 31
+
+/** Start of the SPI (Shared Peripheral Interrupts) range. */
+#define GIC_INTID_RANGE_SPI_START 32
+/** Last valid SPI (Shared Peripheral Interrupts) identifier. */
+#define GIC_INTID_RANGE_SPI_LAST 1019
+
+/** Start of the special interrupt range. */
+#define GIC_INTID_RANGE_SPECIAL_START 1020
+/** Last valid special interrupt identifier. */
+#define GIC_INTID_RANGE_SPECIAL_LAST 1023
+/** Value for an interrupt acknowledge if no pending interrupt with sufficient
+ * priority, security state or interrupt group. */
+# define GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT 1023
+
+/** Start of the extended PPI (Private Peripheral Interrupts) range. */
+#define GIC_INTID_RANGE_EPPI_START 1056
+/** Last valid extended PPI (Private Peripheral Interrupts) identifier. */
+#define GIC_INTID_RANGE_EPPI_LAST 1119
+
+/** Start of the extended SPI (Shared Peripheral Interrupts) range. */
+#define GIC_INTID_RANGE_ESPI_START 4096
+/** Last valid extended SPI (Shared Peripheral Interrupts) identifier. */
+#define GIC_INTID_RANGE_ESPI_LAST 5119
+
+/** Start of the LPI (Locality-specific Peripheral Interrupts) range. */
+#define GIC_INTID_RANGE_LPI_START 8192
+/** @} */
+
/** @name GICD - GIC Distributor registers.
* @{ */
@@ -367,11 +406,11 @@
#define GIC_REDIST_SGI_PPI_REG_ISENABLER2E_OFF 0x0108
/** Interrupt Clear Enable Register 0 - RW. */
-#define GIC_REDIST_SGI_PPI_REG_ICENABLER0_OFF 0x0100
+#define GIC_REDIST_SGI_PPI_REG_ICENABLER0_OFF 0x0180
/** Interrupt Clear Enable Register 1 for extended PPI range - RW. */
-#define GIC_REDIST_SGI_PPI_REG_ICENABLER1E_OFF 0x0104
+#define GIC_REDIST_SGI_PPI_REG_ICENABLER1E_OFF 0x0184
/** Interrupt Clear Enable Register 2 for extended PPI range - RW. */
-#define GIC_REDIST_SGI_PPI_REG_ICENABLER2E_OFF 0x0108
+#define GIC_REDIST_SGI_PPI_REG_ICENABLER2E_OFF 0x0188
/** Interrupt Set Pend Register 0 - RW. */
#define GIC_REDIST_SGI_PPI_REG_ISPENDR0_OFF 0x0200
diff --git a/include/VBox/vmm/gic.h b/include/VBox/vmm/gic.h
index d2ffb4f7c63..c24234a1aaf 100644
--- a/include/VBox/vmm/gic.h
+++ b/include/VBox/vmm/gic.h
@@ -56,9 +56,12 @@ extern const PDMDEVREG g_DeviceGIC;
/* These functions are VMM internal. */
VMM_INT_DECL(VBOXSTRICTRC) GICReadSysReg(PVMCPUCC pVCpu, uint32_t u32Reg, uint64_t *pu64Value);
VMM_INT_DECL(VBOXSTRICTRC) GICWriteSysReg(PVMCPUCC pVCpu, uint32_t u32Reg, uint64_t u64Value);
+VMM_INT_DECL(int) GICSpiSet(PVMCC pVM, uint32_t uIntId, bool fAsserted);
+VMM_INT_DECL(int) GICPpiSet(PVMCPUCC pVCpu, uint32_t uIntId, bool fAsserted);
+VMM_INT_DECL(int) GICSgiSet(PVMCPUCC pVCpu, uint32_t uIntId, bool fAsserted);
#ifdef IN_RING3
-/** @defgroup grp_gic_r3 The APIC Host Context Ring-3 API
+/** @defgroup grp_gic_r3 The GIC Host Context Ring-3 API
* @{
*/
VMMR3_INT_DECL(int) GICR3RegisterDevice(struct PDMDEVREGCB *pCallbacks);
diff --git a/src/VBox/VMM/VMMAll/GICAll.cpp b/src/VBox/VMM/VMMAll/GICAll.cpp
index edfdaed48ec..a71193e2818 100644
--- a/src/VBox/VMM/VMMAll/GICAll.cpp
+++ b/src/VBox/VMM/VMMAll/GICAll.cpp
@@ -50,6 +50,148 @@
/*********************************************************************************************************************************
* Global Variables *
*********************************************************************************************************************************/
+
+/**
+ * Sets the interrupt pending force-flag and pokes the EMT if required.
+ *
+ * @param pVCpu The cross context virtual CPU structure.
+ * @param enmType The IRQ type.
+ */
+static void gicSetInterruptFF(PVMCPUCC pVCpu, bool fIrq, bool fFiq)
+{
+ Assert(fIrq || fFiq);
+
+#ifdef IN_RING3
+ /* IRQ state should be loaded as-is by "LoadExec". Changes can be made from LoadDone. */
+ Assert(pVCpu->pVMR3->enmVMState != VMSTATE_LOADING || PDMR3HasLoadedState(pVCpu->pVMR3));
+#endif
+
+ if (fIrq)
+ VMCPU_FF_SET(pVCpu, VMCPU_FF_INTERRUPT_IRQ);
+ if (fFiq)
+ VMCPU_FF_SET(pVCpu, VMCPU_FF_INTERRUPT_FIQ);
+
+ /*
+ * We need to wake up the target CPU if we're not on EMT.
+ */
+ /** @todo We could just use RTThreadNativeSelf() here, couldn't we? */
+#if defined(IN_RING0)
+ PVMCC pVM = pVCpu->CTX_SUFF(pVM);
+ VMCPUID idCpu = pVCpu->idCpu;
+ if (VMMGetCpuId(pVM) != idCpu)
+ {
+ switch (VMCPU_GET_STATE(pVCpu))
+ {
+ case VMCPUSTATE_STARTED_EXEC:
+ Log7Func(("idCpu=%u VMCPUSTATE_STARTED_EXEC\n", idCpu));
+ GVMMR0SchedPokeNoGVMNoLock(pVM, idCpu);
+ break;
+
+ case VMCPUSTATE_STARTED_HALTED:
+ Log7Func(("idCpu=%u VMCPUSTATE_STARTED_HALTED\n", idCpu));
+ GVMMR0SchedWakeUpNoGVMNoLock(pVM, idCpu);
+ break;
+
+ default:
+ Log7Func(("idCpu=%u enmState=%d\n", idCpu, pVCpu->enmState));
+ break; /* nothing to do in other states. */
+ }
+ }
+#elif defined(IN_RING3)
+ PVMCC pVM = pVCpu->CTX_SUFF(pVM);
+ VMCPUID idCpu = pVCpu->idCpu;
+ if (VMMGetCpuId(pVM) != idCpu)
+ {
+ Log7Func(("idCpu=%u enmState=%d\n", idCpu, pVCpu->enmState));
+ VMR3NotifyCpuFFU(pVCpu->pUVCpu, VMNOTIFYFF_FLAGS_POKE);
+ }
+#endif
+}
+
+
+/**
+ * Clears the interrupt pending force-flag.
+ *
+ * @param pVCpu The cross context virtual CPU structure.
+ * @param fIrq Flag whether to clear the IRQ flag.
+ * @param fFiq Flag whether to clear the FIQ flag.
+ */
+DECLINLINE(void) gicClearInterruptFF(PVMCPUCC pVCpu, bool fIrq, bool fFiq)
+{
+ Assert(fIrq || fFiq);
+
+#ifdef IN_RING3
+ /* IRQ state should be loaded as-is by "LoadExec". Changes can be made from LoadDone. */
+ Assert(pVCpu->pVMR3->enmVMState != VMSTATE_LOADING || PDMR3HasLoadedState(pVCpu->pVMR3));
+#endif
+
+ if (fIrq)
+ VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_IRQ);
+ if (fFiq)
+ VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_FIQ);
+}
+
+
+/**
+ * Updates the internal IRQ state and sets or clears the appropirate force action flags.
+ *
+ * @returns Strict VBox status code.
+ * @param pThis The GIC re-distributor state for the associated vCPU.
+ * @param pVCpu The cross context virtual CPU structure.
+ */
+static VBOXSTRICTRC gicReDistUpdateIrqState(PGICCPU pThis, PVMCPUCC pVCpu)
+{
+ /* Read the interrupt state. */
+ uint32_t u32RegIGrp0 = ASMAtomicReadU32(&pThis->u32RegIGrp0);
+ uint32_t bmIntEnabled = ASMAtomicReadU32(&pThis->bmIntEnabled);
+ uint32_t bmIntPending = ASMAtomicReadU32(&pThis->bmIntPending);
+ uint32_t bmIntActive = ASMAtomicReadU32(&pThis->bmIntActive);
+ bool fIrqGrp0Enabled = ASMAtomicReadBool(&pThis->fIrqGrp0Enabled);
+ bool fIrqGrp1Enabled = ASMAtomicReadBool(&pThis->fIrqGrp1Enabled);
+
+ /* Is anything enabled at all? */
+ uint32_t bmIntForward = (bmIntPending & bmIntEnabled) & ~bmIntActive; /* Exclude the currently active interrupt. */
+ if (bmIntForward)
+ {
+ /* Determine whether we have to assert the IRQ or FIQ line. */
+ bool fIrq = RT_BOOL(bmIntForward & u32RegIGrp0) && fIrqGrp1Enabled;
+ bool fFiq = RT_BOOL(bmIntForward & ~u32RegIGrp0) && fIrqGrp0Enabled;
+
+ if (fIrq || fFiq)
+ gicSetInterruptFF(pVCpu, fIrq, fFiq);
+
+ if (!fIrq || !fFiq)
+ gicClearInterruptFF(pVCpu, !fIrq, !fFiq);
+ }
+ else
+ gicClearInterruptFF(pVCpu, true /*fIrq*/, true /*fFiq*/);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Sets the given SGI/PPI interrupt ID on the re-distributor of the given vCPU.
+ *
+ * @returns VBox status code.
+ * @param pVCpu The cross context virtual CPU structure.
+ * @param uIntId The SGI/PPI interrupt identifier.
+ * @param fAsserted Flag whether the SGI/PPI interrupt is asserted or not.
+ */
+static int gicReDistInterruptSet(PVMCPUCC pVCpu, uint32_t uIntId, bool fAsserted)
+{
+ PGICCPU pThis = VMCPU_TO_GICCPU(pVCpu);
+
+ /* Update the interrupts pending state. */
+ if (fAsserted)
+ ASMAtomicOrU32(&pThis->bmIntPending, RT_BIT_32(uIntId));
+ else
+ ASMAtomicAndU32(&pThis->bmIntPending, ~RT_BIT_32(uIntId));
+
+ return VBOXSTRICTRC_VAL(gicReDistUpdateIrqState(pThis, pVCpu));
+}
+
+
/**
* Reads a GIC distributor register.
*
@@ -67,7 +209,7 @@ DECLINLINE(VBOXSTRICTRC) gicDistRegisterRead(PPDMDEVINS pDevIns, PVMCPUCC pVCpu,
switch (offReg)
{
case GIC_DIST_REG_TYPER_OFF:
- *puValue = GIC_DIST_REG_TYPER_NUM_ITLINES_SET(0) /** @todo 32 SPIs for now. */
+ *puValue = GIC_DIST_REG_TYPER_NUM_ITLINES_SET(1) /** @todo 32 SPIs for now. */
| GIC_DIST_REG_TYPER_NUM_PES_SET(0) /* 1 PE */
/*| GIC_DIST_REG_TYPER_ESPI*/ /** @todo */
/*| GIC_DIST_REG_TYPER_NMI*/ /** @todo Non-maskable interrupts */
@@ -135,7 +277,71 @@ DECLINLINE(VBOXSTRICTRC) gicReDistRegisterRead(PPDMDEVINS pDevIns, PVMCPUCC pVCp
/**
- * Writes a GIC redistributor register.
+ * Reads a GIC redistributor SGI/PPI frame register.
+ *
+ * @returns VBox status code.
+ * @param pDevIns The device instance.
+ * @param pVCpu The cross context virtual CPU structure.
+ * @param offReg The offset of the register being read.
+ * @param puValue Where to store the register value.
+ */
+DECLINLINE(VBOXSTRICTRC) gicReDistSgiPpiRegisterRead(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t *puValue)
+{
+ VMCPU_ASSERT_EMT(pVCpu);
+ RT_NOREF(pDevIns);
+
+ PGICCPU pThis = VMCPU_TO_GICCPU(pVCpu);
+ switch (offReg)
+ {
+ case GIC_REDIST_SGI_PPI_REG_ISENABLER0_OFF:
+ case GIC_REDIST_SGI_PPI_REG_ICENABLER0_OFF:
+ *puValue = ASMAtomicReadU32(&pThis->bmIntEnabled);
+ break;
+ case GIC_REDIST_SGI_PPI_REG_ISPENDR0_OFF:
+ case GIC_REDIST_SGI_PPI_REG_ICPENDR0_OFF:
+ *puValue = ASMAtomicReadU32(&pThis->bmIntPending);
+ break;
+ case GIC_REDIST_SGI_PPI_REG_ISACTIVER0_OFF:
+ case GIC_REDIST_SGI_PPI_REG_ICACTIVER0_OFF:
+ *puValue = ASMAtomicReadU32(&pThis->bmIntActive);
+ break;
+ case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START:
+ case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 4:
+ case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 8:
+ case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 12:
+ case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 16:
+ case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 20:
+ case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 24:
+ case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 28:
+ {
+ /* Figure out the register whch is written. */
+ uint8_t idxPrio = offReg - GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START;
+ Assert(idxPrio <= RT_ELEMENTS(pThis->abIntPriority) - sizeof(uint32_t));
+
+ uint32_t u32Value = 0;
+ for (uint32_t i = idxPrio; i < idxPrio + sizeof(uint32_t); i++)
+ u32Value |= pThis->abIntPriority[i] << ((i - idxPrio) * 8);
+
+ *puValue = u32Value;
+ break;
+ }
+ case GIC_REDIST_SGI_PPI_REG_ICFGR0_OFF:
+ *puValue = ASMAtomicReadU32(&pThis->u32RegICfg0);
+ break;
+ case GIC_REDIST_SGI_PPI_REG_ICFGR1_OFF:
+ *puValue = ASMAtomicReadU32(&pThis->u32RegICfg1);
+ break;
+ default:
+ AssertReleaseFailed();
+ *puValue = 0;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Writes a GIC redistributor frame register.
*
* @returns Strict VBox status code.
* @param pDevIns The device instance.
@@ -154,6 +360,85 @@ DECLINLINE(VBOXSTRICTRC) gicReDistRegisterWrite(PPDMDEVINS pDevIns, PVMCPUCC pVC
/**
+ * Writes a GIC redistributor SGI/PPI frame register.
+ *
+ * @returns Strict VBox status code.
+ * @param pDevIns The device instance.
+ * @param pVCpu The cross context virtual CPU structure.
+ * @param offReg The offset of the register being written.
+ * @param uValue The register value.
+ */
+DECLINLINE(VBOXSTRICTRC) gicReDistSgiPpiRegisterWrite(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t uValue)
+{
+ VMCPU_ASSERT_EMT(pVCpu);
+ RT_NOREF(pDevIns);
+
+ PGICCPU pThis = VMCPU_TO_GICCPU(pVCpu);
+ VBOXSTRICTRC rcStrict = VINF_SUCCESS;
+ switch (offReg)
+ {
+ case GIC_REDIST_SGI_PPI_REG_IGROUPR0_OFF:
+ ASMAtomicOrU32(&pThis->u32RegIGrp0, uValue);
+ rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
+ break;
+ case GIC_REDIST_SGI_PPI_REG_ISENABLER0_OFF:
+ ASMAtomicOrU32(&pThis->bmIntEnabled, uValue);
+ rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
+ break;
+ case GIC_REDIST_SGI_PPI_REG_ICENABLER0_OFF:
+ ASMAtomicAndU32(&pThis->bmIntEnabled, ~uValue);
+ rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
+ break;
+ case GIC_REDIST_SGI_PPI_REG_ISPENDR0_OFF:
+ ASMAtomicOrU32(&pThis->bmIntPending, uValue);
+ rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
+ break;
+ case GIC_REDIST_SGI_PPI_REG_ICPENDR0_OFF:
+ ASMAtomicAndU32(&pThis->bmIntPending, ~uValue);
+ rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
+ break;
+ case GIC_REDIST_SGI_PPI_REG_ISACTIVER0_OFF:
+ ASMAtomicOrU32(&pThis->bmIntActive, uValue);
+ rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
+ break;
+ case GIC_REDIST_SGI_PPI_REG_ICACTIVER0_OFF:
+ ASMAtomicAndU32(&pThis->bmIntActive, ~uValue);
+ rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
+ break;
+ case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START:
+ case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 4:
+ case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 8:
+ case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 12:
+ case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 16:
+ case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 20:
+ case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 24:
+ case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 28:
+ {
+ /* Figure out the register whch is written. */
+ uint8_t idxPrio = offReg - GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START;
+ Assert(idxPrio <= RT_ELEMENTS(pThis->abIntPriority) - sizeof(uint32_t));
+ for (uint32_t i = idxPrio; i < idxPrio + sizeof(uint32_t); i++)
+ {
+ pThis->abIntPriority[i] = (uint8_t)(uValue & 0xff);
+ uValue >>= 8;
+ }
+ break;
+ }
+ case GIC_REDIST_SGI_PPI_REG_ICFGR0_OFF:
+ ASMAtomicWriteU32(&pThis->u32RegICfg0, uValue);
+ break;
+ case GIC_REDIST_SGI_PPI_REG_ICFGR1_OFF:
+ ASMAtomicWriteU32(&pThis->u32RegICfg1, uValue);
+ break;
+ default:
+ AssertReleaseFailed();
+ }
+
+ return rcStrict;
+}
+
+
+/**
* Reads a GIC system register.
*
* @returns Strict VBox status code.
@@ -170,6 +455,109 @@ VMM_INT_DECL(VBOXSTRICTRC) GICReadSysReg(PVMCPUCC pVCpu, uint32_t u32Reg, uint64
Assert(pu64Value);
*pu64Value = 0;
+ PGICCPU pThis = VMCPU_TO_GICCPU(pVCpu);
+ switch (u32Reg)
+ {
+ case ARMV8_AARCH64_SYSREG_ICC_PMR_EL1:
+ *pu64Value = pThis->bInterruptPriority;
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_IAR0_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_EOIR0_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_HPPIR0_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_BPR0_EL1:
+ *pu64Value = pThis->bBinaryPointGrp0 & 0x7;
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_AP0R0_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_AP0R1_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_AP0R2_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_AP0R3_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_AP1R0_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_AP1R1_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_AP1R2_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_AP1R3_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_NMIAR1_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_DIR_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_RPR_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_SGI1R_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_ASGI1R_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_SGI0R_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_IAR1_EL1:
+ {
+ /** @todo Figure out the highest priority interrupt. */
+ uint32_t bmPending = ASMAtomicReadU32(&pThis->bmIntPending);
+ int32_t idxIntPending = ASMBitFirstSet(&bmPending, sizeof(bmPending) * 8);
+ if (idxIntPending > -1)
+ {
+ /* Mark the interrupt as active. */
+ ASMAtomicOrU32(&pThis->bmIntActive, idxIntPending);
+ *pu64Value = idxIntPending;
+ }
+ else
+ *pu64Value = GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT;
+ break;
+ }
+ case ARMV8_AARCH64_SYSREG_ICC_EOIR1_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_HPPIR1_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_BPR1_EL1:
+ *pu64Value = pThis->bBinaryPointGrp1 & 0x7;
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_CTLR_EL1:
+ *pu64Value = ARMV8_ICC_CTLR_EL1_AARCH64_PMHE
+ | ARMV8_ICC_CTLR_EL1_AARCH64_PRIBITS_SET(4)
+ | ARMV8_ICC_CTLR_EL1_AARCH64_IDBITS_SET(ARMV8_ICC_CTLR_EL1_AARCH64_IDBITS_16BITS);
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_SRE_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_IGRPEN0_EL1:
+ *pu64Value = ASMAtomicReadBool(&pThis->fIrqGrp0Enabled) ? ARMV8_ICC_IGRPEN0_EL1_AARCH64_ENABLE : 0;
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_IGRPEN1_EL1:
+ *pu64Value = ASMAtomicReadBool(&pThis->fIrqGrp1Enabled) ? ARMV8_ICC_IGRPEN1_EL1_AARCH64_ENABLE : 0;
+ break;
+ default:
+ AssertReleaseFailed();
+ break;
+ }
+
LogFlowFunc(("pVCpu=%p u32Reg=%#x pu64Value=%RX64\n", pVCpu, u32Reg, *pu64Value));
return VINF_SUCCESS;
}
@@ -192,11 +580,151 @@ VMM_INT_DECL(VBOXSTRICTRC) GICWriteSysReg(PVMCPUCC pVCpu, uint32_t u32Reg, uint6
RT_NOREF(pVCpu, u32Reg, u64Value);
LogFlowFunc(("pVCpu=%p u32Reg=%#x u64Value=%RX64\n", pVCpu, u32Reg, u64Value));
+ PGICCPU pThis = VMCPU_TO_GICCPU(pVCpu);
+ switch (u32Reg)
+ {
+ case ARMV8_AARCH64_SYSREG_ICC_PMR_EL1:
+ ASMAtomicWriteU8(&pThis->bInterruptPriority, (uint8_t)u64Value);
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_IAR0_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_EOIR0_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_HPPIR0_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_BPR0_EL1:
+ pThis->bBinaryPointGrp0 = (uint8_t)(u64Value & 0x7);
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_AP0R0_EL1:
+ /** @todo */
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_AP0R1_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_AP0R2_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_AP0R3_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_AP1R0_EL1:
+ /** @todo */
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_AP1R1_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_AP1R2_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_AP1R3_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_NMIAR1_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_DIR_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_RPR_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_SGI1R_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_ASGI1R_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_SGI0R_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_IAR1_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_EOIR1_EL1:
+ {
+ /* Mark the interrupt as not active anymore, though it might still be pending. */
+ Assert(u64Value < GIC_INTID_RANGE_SPI_START);
+ ASMAtomicAndU32(&pThis->bmIntActive, (uint32_t)u64Value);
+ break;
+ }
+ case ARMV8_AARCH64_SYSREG_ICC_HPPIR1_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_BPR1_EL1:
+ pThis->bBinaryPointGrp0 = (uint8_t)(u64Value & 0x7);
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_CTLR_EL1:
+ u64Value &= ARMV8_ICC_CTLR_EL1_RW;
+ /** @todo */
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_SRE_EL1:
+ AssertReleaseFailed();
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_IGRPEN0_EL1:
+ ASMAtomicWriteBool(&pThis->fIrqGrp0Enabled, RT_BOOL(u64Value & ARMV8_ICC_IGRPEN0_EL1_AARCH64_ENABLE));
+ break;
+ case ARMV8_AARCH64_SYSREG_ICC_IGRPEN1_EL1:
+ ASMAtomicWriteBool(&pThis->fIrqGrp1Enabled, RT_BOOL(u64Value & ARMV8_ICC_IGRPEN1_EL1_AARCH64_ENABLE));
+ break;
+ default:
+ AssertReleaseFailed();
+ break;
+ }
+
return VINF_SUCCESS;
}
/**
+ * Sets the specified shared peripheral interrupt starting.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context virtual machine structure.
+ * @param uIntId The SPI ID (minus GIC_INTID_RANGE_SPI_START) to assert/de-assert.
+ * @param fAsserted Flag whether to mark the interrupt as asserted/de-asserted.
+ */
+VMM_INT_DECL(int) GICSpiSet(PVMCC pVM, uint32_t uIntId, bool fAsserted)
+{
+ RT_NOREF(pVM, uIntId, fAsserted);
+ AssertReleaseFailed();
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+/**
+ * Sets the specified private peripheral interrupt starting.
+ *
+ * @returns VBox status code.
+ * @param pVCpu The cross context virtual CPU structure.
+ * @param uIntId The PPI ID (minus GIC_INTID_RANGE_PPI_START) to assert/de-assert.
+ * @param fAsserted Flag whether to mark the interrupt as asserted/de-asserted.
+ */
+VMM_INT_DECL(int) GICPpiSet(PVMCPUCC pVCpu, uint32_t uIntId, bool fAsserted)
+{
+ AssertReturn(uIntId >= 0 && uIntId <= (GIC_INTID_RANGE_PPI_LAST - GIC_INTID_RANGE_PPI_START), VERR_INVALID_PARAMETER);
+ return gicReDistInterruptSet(pVCpu, uIntId + GIC_INTID_RANGE_PPI_START, fAsserted);
+}
+
+
+/**
+ * Sets the specified software generated interrupt starting.
+ *
+ * @returns VBox status code.
+ * @param pVCpu The cross context virtual CPU structure.
+ * @param uIntId The PPI ID (minus GIC_INTID_RANGE_SGI_START) to assert/de-assert.
+ * @param fAsserted Flag whether to mark the interrupt as asserted/de-asserted.
+ */
+VMM_INT_DECL(int) GICSgiSet(PVMCPUCC pVCpu, uint32_t uIntId, bool fAsserted)
+{
+ AssertReturn(uIntId >= 0 && uIntId <= (GIC_INTID_RANGE_SGI_LAST - GIC_INTID_RANGE_SGI_START), VERR_INVALID_PARAMETER);
+ return gicReDistInterruptSet(pVCpu, uIntId + GIC_INTID_RANGE_SGI_START, fAsserted);
+}
+
+
+/**
* Initializes per-VCPU GIC to the state following a power-up or hardware
* reset.
*
@@ -215,8 +743,8 @@ DECLHIDDEN(void) gicResetCpu(PVMCPUCC pVCpu)
DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicDistMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
{
NOREF(pvUser);
- //Assert(!(off & 0xf));
- //Assert(cb == 4); RT_NOREF_PV(cb);
+ Assert(!(off & 0x3));
+ Assert(cb == 4); RT_NOREF_PV(cb);
PVMCPUCC pVCpu = PDMDevHlpGetVMCPU(pDevIns);
uint16_t offReg = off & 0xfffc;
@@ -238,8 +766,8 @@ DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicDistMmioRead(PPDMDEVINS pDevIns, void *pvU
DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicDistMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
{
NOREF(pvUser);
- //Assert(!(off & 0xf));
- //Assert(cb == 4); RT_NOREF_PV(cb);
+ Assert(!(off & 0x3));
+ Assert(cb == 4); RT_NOREF_PV(cb);
PVMCPUCC pVCpu = PDMDevHlpGetVMCPU(pDevIns);
uint16_t offReg = off & 0xfffc;
@@ -258,20 +786,33 @@ DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicDistMmioWrite(PPDMDEVINS pDevIns, void *pv
DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicReDistMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
{
NOREF(pvUser);
- //Assert(!(off & 0xf));
- //Assert(cb == 4); RT_NOREF_PV(cb);
+ Assert(!(off & 0x3));
+ Assert(cb == 4); RT_NOREF_PV(cb);
PVMCPUCC pVCpu = PDMDevHlpGetVMCPU(pDevIns);
- uint16_t offReg = off & 0xfffc;
- uint32_t uValue = 0;
STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatMmioRead));
- VBOXSTRICTRC rc = VBOXSTRICTRC_VAL(gicReDistRegisterRead(pDevIns, pVCpu, offReg, &uValue));
- *(uint32_t *)pv = uValue;
+ /*
+ * Determine the redistributor being targeted. Each redistributor takes GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE bytes
+ * and the redistributors are adjacent.
+ */
+ uint32_t idReDist = off / (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
+ off %= (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
+
+ /* Redistributor or SGI/PPI frame? */
+ uint16_t offReg = off & 0xfffc;
+ uint32_t uValue = 0;
+ VBOXSTRICTRC rcStrict;
+ if (off < GIC_REDIST_REG_FRAME_SIZE)
+ rcStrict = gicReDistRegisterRead(pDevIns, pVCpu, offReg, &uValue);
+ else
+ rcStrict = gicReDistSgiPpiRegisterRead(pDevIns, pVCpu, offReg, &uValue);
- Log2(("GIC%u: gicReDistMmioRead: offReg=%#RX16 uValue=%#RX32\n", pVCpu->idCpu, offReg, uValue));
- return rc;
+ *(uint32_t *)pv = uValue;
+ Log2(("GICReDist%u: gicReDistMmioRead: off=%RGp idReDist=%u offReg=%#RX16 uValue=%#RX32 -> %Rrc\n",
+ pVCpu->idCpu, off, idReDist, offReg, uValue, VBOXSTRICTRC_VAL(rcStrict)));
+ return rcStrict;
}
@@ -281,17 +822,32 @@ DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicReDistMmioRead(PPDMDEVINS pDevIns, void *p
DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicReDistMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
{
NOREF(pvUser);
- //Assert(!(off & 0xf));
- //Assert(cb == 4); RT_NOREF_PV(cb);
+ Assert(!(off & 0x3));
+ Assert(cb == 4); RT_NOREF_PV(cb);
- PVMCPUCC pVCpu = PDMDevHlpGetVMCPU(pDevIns);
- uint16_t offReg = off & 0xfffc;
- uint32_t uValue = *(uint32_t *)pv;
+ PVMCPUCC pVCpu = PDMDevHlpGetVMCPU(pDevIns);
+ uint32_t uValue = *(uint32_t *)pv;
STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatMmioWrite));
- Log2(("GIC%u: gicReDistMmioWrite: offReg=%#RX16 uValue=%#RX32\n", pVCpu->idCpu, offReg, uValue));
- return gicReDistRegisterWrite(pDevIns, pVCpu, offReg, uValue);
+ /*
+ * Determine the redistributor being targeted. Each redistributor takes GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE bytes
+ * and the redistributors are adjacent.
+ */
+ uint32_t idReDist = off / (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
+ off %= (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
+
+ /* Redistributor or SGI/PPI frame? */
+ uint16_t offReg = off & 0xfffc;
+ VBOXSTRICTRC rcStrict;
+ if (off < GIC_REDIST_REG_FRAME_SIZE)
+ rcStrict = gicReDistRegisterWrite(pDevIns, pVCpu, offReg, uValue);
+ else
+ rcStrict = gicReDistSgiPpiRegisterWrite(pDevIns, pVCpu, offReg, uValue);
+
+ Log2(("GICReDist%u: gicReDistMmioWrite: off=%RGp idReDist=%u offReg=%#RX16 uValue=%#RX32 -> %Rrc\n",
+ pVCpu->idCpu, off, idReDist, offReg, uValue, VBOXSTRICTRC_VAL(rcStrict)));
+ return rcStrict;
}
diff --git a/src/VBox/VMM/include/GICInternal.h b/src/VBox/VMM/include/GICInternal.h
index 8e4bbc3a970..de1818cd45f 100644
--- a/src/VBox/VMM/include/GICInternal.h
+++ b/src/VBox/VMM/include/GICInternal.h
@@ -85,6 +85,45 @@ AssertCompileSizeAlignment(GIC, 8);
*/
typedef struct GICCPU
{
+ /** @name The per vCPU redistributor data is kept here.
+ * @{ */
+
+ /** @name Physical LPI register state.
+ * @{ */
+ /** @} */
+
+ /** @name SGI and PPI redistributor register state.
+ * @{ */
+ /** Interrupt Group 0 Register. */
+ volatile uint32_t u32RegIGrp0;
+ /** Interrupt Configuration Register 0. */
+ volatile uint32_t u32RegICfg0;
+ /** Interrupt Configuration Register 1. */
+ volatile uint32_t u32RegICfg1;
+ /** Interrupt enabled bitmap. */
+ volatile uint32_t bmIntEnabled;
+ /** Current interrupt pending state. */
+ volatile uint32_t bmIntPending;
+ /** The current interrupt active state. */
+ volatile uint32_t bmIntActive;
+ /** The interrupt priority for each of the SGI/PPIs */
+ volatile uint8_t abIntPriority[GIC_INTID_RANGE_PPI_LAST + 1];
+ /** @} */
+
+ /** @name ICC system register state.
+ * @{ */
+ /** Flag whether group 0 interrupts are currently enabled. */
+ volatile bool fIrqGrp0Enabled;
+ /** Flag whether group 1 interrupts are currently enabled. */
+ volatile bool fIrqGrp1Enabled;
+ /** The current interrupt priority, only interrupts with a higher priority get signalled. */
+ volatile uint8_t bInterruptPriority;
+ /** The interrupt controller Binary Point Register for Group 0 interrupts. */
+ uint8_t bBinaryPointGrp0;
+ /** The interrupt controller Binary Point Register for Group 1 interrupts. */
+ uint8_t bBinaryPointGrp1;
+ /** @} */
+
/** @name Log Max counters
* @{ */
uint32_t cLogMaxAccessError;