diff options
author | vboxsync <vboxsync@cfe28804-0f27-0410-a406-dd0f0b0b656f> | 2023-05-10 17:28:24 +0000 |
---|---|---|
committer | vboxsync <vboxsync@cfe28804-0f27-0410-a406-dd0f0b0b656f> | 2023-05-10 17:28:24 +0000 |
commit | d3bd81c0c3fc003a91f8b3109777bb3df4a01e30 (patch) | |
tree | 86ff4a51d8f9153a4c9b894e8ba988179dc11eda | |
parent | c03068b5b05c37277ce58920c715eb0ef002441e (diff) | |
download | VirtualBox-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.h | 45 | ||||
-rw-r--r-- | include/VBox/vmm/gic.h | 5 | ||||
-rw-r--r-- | src/VBox/VMM/VMMAll/GICAll.cpp | 598 | ||||
-rw-r--r-- | src/VBox/VMM/include/GICInternal.h | 39 |
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; |