VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/GICAll.cpp@ 108730

Last change on this file since 108730 was 108730, checked in by vboxsync, 3 weeks ago

VMM/GIC: bugref:10877 Fixes for pedantic sign-compare warnings on GCC.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 139.3 KB
Line 
1/* $Id: GICAll.cpp 108730 2025-03-25 07:14:06Z vboxsync $ */
2/** @file
3 * GIC - Generic Interrupt Controller Architecture (GIC) - All Contexts.
4 */
5
6/*
7 * Copyright (C) 2023-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/** @page pg_gic GIC - Generic Interrupt Controller
29 *
30 * The GIC is an interrupt controller device that lives in VMM but also registers
31 * itself with PDM similar to the APIC. The reason for this is needs to access
32 * per-VCPU data and is an integral part of any ARMv8 VM.
33 *
34 * The GIC is made up of 3 main components:
35 * - Distributor
36 * - Redistributor
37 * - Interrupt Translation Service (ITS)
38 *
39 * The distributor is per-VM while the redistributors are per-VCPU. PEs (Processing
40 * Elements) and CIs (CPU Interfaces) correspond to VCPUs. The distributor and
41 * redistributor each have their memory mapped I/O regions. The redistributor is
42 * accessible via CPU system registers as well. The distributor and redistributor
43 * code lives in GICAll.cpp and GICR3.cpp.
44 *
45 * The ITS is the interrupt translation service component of the GIC and its
46 * presence is optional. It provides MSI support along with routing interrupt
47 * sources to specific PEs. The ITS is only accessible via its memory mapped I/O
48 * region. When the MMIO handle for the its region is NIL_IOMMMIOHANDLE it's
49 * considered to be disabled for the VM. The ITS code lives in GITSAll.cpp.
50 *
51 * This implementation only targets GICv3. This implementation does not support
52 * dual security states, nor does it support exception levels (EL2, EL3). Earlier
53 * versions are considered legacy and not important enough to be emulated.
54 * GICv4 primarily adds support for virtualizing the GIC and its necessity will be
55 * evaluated in the future if/when there is support for nested virtualization on
56 * ARMv8 hosts.
57 */
58
59
60/*********************************************************************************************************************************
61* Header Files *
62*********************************************************************************************************************************/
63#define LOG_GROUP LOG_GROUP_DEV_GIC
64#include "GICInternal.h"
65#include <VBox/vmm/pdmgic.h>
66#include <VBox/vmm/pdmdev.h>
67#include <VBox/vmm/pdmapi.h>
68#include <VBox/vmm/vmcc.h>
69#include <VBox/vmm/vmm.h>
70#include <VBox/vmm/vmcpuset.h>
71
72
73/*********************************************************************************************************************************
74* Defined Constants And Macros *
75*********************************************************************************************************************************/
76#define GIC_IDLE_PRIORITY 0xff
77#define GIC_IS_INTR_SGI(a_uIntId) (a_uIntId - GIC_INTID_RANGE_SGI_START < GIC_INTID_SGI_RANGE_SIZE)
78#define GIC_IS_INTR_PPI(a_uIntId) (a_uIntId - GIC_INTID_RANGE_PPI_START < GIC_INTID_PPI_RANGE_SIZE)
79#define GIC_IS_INTR_SGI_OR_PPI(a_uIntId) (a_uIntId - GIC_INTID_RANGE_SGI_START < GIC_INTID_PPI_RANGE_SIZE)
80#define GIC_IS_INTR_SPI(a_uIntId) (a_uIntId - GIC_INTID_RANGE_SPI_START < GIC_INTID_SPI_RANGE_SIZE)
81#define GIC_IS_INTR_SPECIAL(a_uIntId) (a_uIntId - GIC_INTID_RANGE_SPECIAL_START < GIC_INTID_EXT_PPI_RANGE_SIZE)
82#define GIC_IS_INTR_EXT_PPI(a_uIntId) (a_uIntId - GIC_INTID_RANGE_EXT_PPI_START < GIC_INTID_EXT_PPI_RANGE_SIZE)
83#define GIC_IS_INTR_EXT_SPI(a_uIntId) (a_uIntId - GIC_INTID_RANGE_EXT_SPI_START < GIC_INTID_EXT_SPI_RANGE_SIZE)
84
85
86#ifdef LOG_ENABLED
87/**
88 * Gets the description of a CPU interface register.
89 *
90 * @returns The description.
91 * @param u32Reg The CPU interface register offset.
92 */
93static const char *gicIccGetRegDescription(uint32_t u32Reg)
94{
95 switch (u32Reg)
96 {
97#define GIC_ICC_REG_CASE(a_Reg) case ARMV8_AARCH64_SYSREG_ ## a_Reg: return #a_Reg
98 GIC_ICC_REG_CASE(ICC_PMR_EL1);
99 GIC_ICC_REG_CASE(ICC_IAR0_EL1);
100 GIC_ICC_REG_CASE(ICC_EOIR0_EL1);
101 GIC_ICC_REG_CASE(ICC_HPPIR0_EL1);
102 GIC_ICC_REG_CASE(ICC_BPR0_EL1);
103 GIC_ICC_REG_CASE(ICC_AP0R0_EL1);
104 GIC_ICC_REG_CASE(ICC_AP0R1_EL1);
105 GIC_ICC_REG_CASE(ICC_AP0R2_EL1);
106 GIC_ICC_REG_CASE(ICC_AP0R3_EL1);
107 GIC_ICC_REG_CASE(ICC_AP1R0_EL1);
108 GIC_ICC_REG_CASE(ICC_AP1R1_EL1);
109 GIC_ICC_REG_CASE(ICC_AP1R2_EL1);
110 GIC_ICC_REG_CASE(ICC_AP1R3_EL1);
111 GIC_ICC_REG_CASE(ICC_DIR_EL1);
112 GIC_ICC_REG_CASE(ICC_RPR_EL1);
113 GIC_ICC_REG_CASE(ICC_SGI1R_EL1);
114 GIC_ICC_REG_CASE(ICC_ASGI1R_EL1);
115 GIC_ICC_REG_CASE(ICC_SGI0R_EL1);
116 GIC_ICC_REG_CASE(ICC_IAR1_EL1);
117 GIC_ICC_REG_CASE(ICC_EOIR1_EL1);
118 GIC_ICC_REG_CASE(ICC_HPPIR1_EL1);
119 GIC_ICC_REG_CASE(ICC_BPR1_EL1);
120 GIC_ICC_REG_CASE(ICC_CTLR_EL1);
121 GIC_ICC_REG_CASE(ICC_SRE_EL1);
122 GIC_ICC_REG_CASE(ICC_IGRPEN0_EL1);
123 GIC_ICC_REG_CASE(ICC_IGRPEN1_EL1);
124#undef GIC_ICC_REG_CASE
125 default:
126 return "<UNKNOWN>";
127 }
128}
129
130
131/**
132 * Gets the description of a distributor register given it's register offset.
133 *
134 * @returns The register description.
135 * @param offReg The distributor register offset.
136 */
137static const char *gicDistGetRegDescription(uint16_t offReg)
138{
139 if (offReg - GIC_DIST_REG_IGROUPRn_OFF_START < GIC_DIST_REG_IGROUPRn_RANGE_SIZE) return "GICD_IGROUPRn";
140 if (offReg - GIC_DIST_REG_IGROUPRnE_OFF_START < GIC_DIST_REG_IGROUPRnE_RANGE_SIZE) return "GICD_IGROUPRnE";
141 if (offReg - GIC_DIST_REG_IROUTERn_OFF_START < GIC_DIST_REG_IROUTERn_RANGE_SIZE) return "GICD_IROUTERn";
142 if (offReg - GIC_DIST_REG_IROUTERnE_OFF_START < GIC_DIST_REG_IROUTERnE_RANGE_SIZE) return "GICD_IROUTERnE";
143 if (offReg - GIC_DIST_REG_ISENABLERn_OFF_START < GIC_DIST_REG_ISENABLERn_RANGE_SIZE) return "GICD_ISENABLERn";
144 if (offReg - GIC_DIST_REG_ISENABLERnE_OFF_START < GIC_DIST_REG_ISENABLERnE_RANGE_SIZE) return "GICD_ISENABLERnE";
145 if (offReg - GIC_DIST_REG_ICENABLERn_OFF_START < GIC_DIST_REG_ICENABLERn_RANGE_SIZE) return "GICD_ICENABLERn";
146 if (offReg - GIC_DIST_REG_ICENABLERnE_OFF_START < GIC_DIST_REG_ICENABLERnE_RANGE_SIZE) return "GICD_ICENABLERnE";
147 if (offReg - GIC_DIST_REG_ISACTIVERn_OFF_START < GIC_DIST_REG_ISACTIVERn_RANGE_SIZE) return "GICD_ISACTIVERn";
148 if (offReg - GIC_DIST_REG_ISACTIVERnE_OFF_START < GIC_DIST_REG_ISACTIVERnE_RANGE_SIZE) return "GICD_ISACTIVERnE";
149 if (offReg - GIC_DIST_REG_ICACTIVERn_OFF_START < GIC_DIST_REG_ICACTIVERn_RANGE_SIZE) return "GICD_ICACTIVERn";
150 if (offReg - GIC_DIST_REG_ICACTIVERnE_OFF_START < GIC_DIST_REG_ICACTIVERnE_RANGE_SIZE) return "GICD_ICACTIVERnE";
151 if (offReg - GIC_DIST_REG_IPRIORITYRn_OFF_START < GIC_DIST_REG_IPRIORITYRn_RANGE_SIZE) return "GICD_IPRIORITYRn";
152 if (offReg - GIC_DIST_REG_IPRIORITYRnE_OFF_START < GIC_DIST_REG_IPRIORITYRnE_RANGE_SIZE) return "GICD_IPRIORITYRnE";
153 if (offReg - GIC_DIST_REG_ISPENDRn_OFF_START < GIC_DIST_REG_ISPENDRn_RANGE_SIZE) return "GICD_ISPENDRn";
154 if (offReg - GIC_DIST_REG_ISPENDRnE_OFF_START < GIC_DIST_REG_ISPENDRnE_RANGE_SIZE) return "GICD_ISPENDRnE";
155 if (offReg - GIC_DIST_REG_ICPENDRn_OFF_START < GIC_DIST_REG_ICPENDRn_RANGE_SIZE) return "GICD_ICPENDRn";
156 if (offReg - GIC_DIST_REG_ICPENDRnE_OFF_START < GIC_DIST_REG_ICPENDRnE_RANGE_SIZE) return "GICD_ICPENDRnE";
157 if (offReg - GIC_DIST_REG_ICFGRn_OFF_START < GIC_DIST_REG_ICFGRn_RANGE_SIZE) return "GICD_ICFGRn";
158 if (offReg - GIC_DIST_REG_ICFGRnE_OFF_START < GIC_DIST_REG_ICFGRnE_RANGE_SIZE) return "GICD_ICFGRnE";
159 switch (offReg)
160 {
161 case GIC_DIST_REG_CTLR_OFF: return "GICD_CTLR";
162 case GIC_DIST_REG_TYPER_OFF: return "GICD_TYPER";
163 case GIC_DIST_REG_STATUSR_OFF: return "GICD_STATUSR";
164 case GIC_DIST_REG_ITARGETSRn_OFF_START: return "GICD_ITARGETSRn";
165 case GIC_DIST_REG_IGRPMODRn_OFF_START: return "GICD_IGRPMODRn";
166 case GIC_DIST_REG_NSACRn_OFF_START: return "GICD_NSACRn";
167 case GIC_DIST_REG_SGIR_OFF: return "GICD_SGIR";
168 case GIC_DIST_REG_CPENDSGIRn_OFF_START: return "GICD_CSPENDSGIRn";
169 case GIC_DIST_REG_SPENDSGIRn_OFF_START: return "GICD_SPENDSGIRn";
170 case GIC_DIST_REG_INMIn_OFF_START: return "GICD_INMIn";
171 case GIC_DIST_REG_PIDR2_OFF: return "GICD_PIDR2";
172 case GIC_DIST_REG_IIDR_OFF: return "GICD_IIDR";
173 case GIC_DIST_REG_TYPER2_OFF: return "GICD_TYPER2";
174 default:
175 return "<UNKNOWN>";
176 }
177}
178
179
180/**
181 * Gets the description of a redistributor register given it's register offset.
182 *
183 * @returns The register description.
184 * @param offReg The redistributor register offset.
185 */
186static const char *gicReDistGetRegDescription(uint16_t offReg)
187{
188 if (offReg - GIC_REDIST_SGI_PPI_REG_IGROUPR0_OFF < GIC_REDIST_SGI_PPI_REG_IGROUPRnE_RANGE_SIZE) return "GICR_IGROUPn";
189 if (offReg - GIC_REDIST_SGI_PPI_REG_ISENABLER0_OFF < GIC_REDIST_SGI_PPI_REG_ISENABLERnE_RANGE_SIZE) return "GICR_ISENABLERn";
190 if (offReg - GIC_REDIST_SGI_PPI_REG_ICENABLER0_OFF < GIC_REDIST_SGI_PPI_REG_ICENABLERnE_RANGE_SIZE) return "GICR_ICENABLERn";
191 if (offReg - GIC_REDIST_SGI_PPI_REG_ISACTIVER0_OFF < GIC_REDIST_SGI_PPI_REG_ISACTIVERnE_RANGE_SIZE) return "GICR_ISACTIVERn";
192 if (offReg - GIC_REDIST_SGI_PPI_REG_ICACTIVER0_OFF < GIC_REDIST_SGI_PPI_REG_ICACTIVERnE_RANGE_SIZE) return "GICR_ICACTIVERn";
193 if (offReg - GIC_REDIST_SGI_PPI_REG_ISPENDR0_OFF < GIC_REDIST_SGI_PPI_REG_ISPENDRnE_RANGE_SIZE) return "GICR_ISPENDRn";
194 if (offReg - GIC_REDIST_SGI_PPI_REG_ICPENDR0_OFF < GIC_REDIST_SGI_PPI_REG_ICPENDRnE_RANGE_SIZE) return "GICR_ICPENDRn";
195 if (offReg - GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START < GIC_REDIST_SGI_PPI_REG_IPRIORITYRnE_RANGE_SIZE) return "GICR_IPREIORITYn";
196 if (offReg - GIC_REDIST_SGI_PPI_REG_ICFGR0_OFF < GIC_REDIST_SGI_PPI_REG_ICFGRnE_RANGE_SIZE) return "GICR_ICFGRn";
197 switch (offReg)
198 {
199 case GIC_REDIST_REG_CTLR_OFF: return "GICR_CTLR";
200 case GIC_REDIST_REG_IIDR_OFF: return "GICR_IIDR";
201 case GIC_REDIST_REG_TYPER_OFF: return "GICR_TYPER";
202 case GIC_REDIST_REG_TYPER_AFFINITY_OFF: return "GICR_TYPER_AFF";
203 case GIC_REDIST_REG_STATUSR_OFF: return "GICR_STATUSR";
204 case GIC_REDIST_REG_PIDR2_OFF: return "GICR_PIDR2";
205 default:
206 return "<UNKNOWN>";
207 }
208}
209#endif
210
211
212/**
213 * Gets the interrupt ID given a distributor interrupt index.
214 *
215 * @returns The interrupt ID.
216 * @param idxIntr The distributor interrupt index.
217 * @remarks A distributor interrupt is an interrupt type that belong in the
218 * distributor (e.g. SPIs, extended SPIs).
219 */
220DECLHIDDEN(uint16_t) gicDistGetIntIdFromIndex(uint16_t idxIntr)
221{
222 /*
223 * Distributor interrupts bits to interrupt ID mapping:
224 * +--------------------------------------------------------+
225 * | Range (incl) | SGI | PPI | SPI | Ext SPI |
226 * |--------------+--------+--------+----------+------------|
227 * | Bit | 0..15 | 16..31 | 32..1023 | 1024..2047 |
228 * | Int Id | 0..15 | 16..31 | 32..1023 | 4096..5119 |
229 * +--------------------------------------------------------+
230 */
231 uint16_t uIntId;
232 /* SGIs, PPIs, SPIs and specials. */
233 if (idxIntr < 1024)
234 uIntId = idxIntr;
235 /* Extended SPIs. */
236 else if (idxIntr < 2048)
237 uIntId = GIC_INTID_RANGE_EXT_SPI_START + idxIntr - 1024;
238 else
239 {
240 uIntId = 0;
241 AssertReleaseFailed();
242 }
243 Assert( GIC_IS_INTR_SGI_OR_PPI(uIntId)
244 || GIC_IS_INTR_SPI(uIntId)
245 || GIC_IS_INTR_SPECIAL(uIntId)
246 || GIC_IS_INTR_EXT_SPI(uIntId));
247 return uIntId;
248}
249
250
251/**
252 * Gets the distributor interrupt index given an interrupt ID.
253 *
254 * @returns The distributor interrupt index.
255 * @param uIntId The interrupt ID.
256 * @remarks A distributor interrupt is an interrupt type that belong in the
257 * distributor (e.g. SPIs, extended SPIs).
258 */
259static uint16_t gicDistGetIndexFromIntId(uint16_t uIntId)
260{
261 uint16_t idxIntr;
262 /* SGIs, PPIs, SPIs and specials. */
263 if (uIntId <= GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT)
264 idxIntr = uIntId;
265 /* Extended SPIs. */
266 else if (uIntId - GIC_INTID_RANGE_EXT_SPI_START < GIC_INTID_EXT_SPI_RANGE_SIZE)
267 idxIntr = 1024 + uIntId - GIC_INTID_RANGE_EXT_SPI_START;
268 else
269 {
270 idxIntr = 0;
271 AssertReleaseFailed();
272 }
273 Assert(idxIntr < sizeof(GICDEV::bmIntrPending) * 8);
274 return idxIntr;
275}
276
277
278/**
279 * Gets the interrupt ID given a redistributor interrupt index.
280 *
281 * @returns The interrupt ID.
282 * @param idxIntr The redistributor interrupt index.
283 * @remarks A redistributor interrupt is an interrupt type that belong in the
284 * redistributor (e.g. SGIs, PPIs, extended PPIs).
285 */
286DECLHIDDEN(uint16_t) gicReDistGetIntIdFromIndex(uint16_t idxIntr)
287{
288 /*
289 * Redistributor interrupts bits to interrupt ID mapping:
290 * +---------------------------------------------+
291 * | Range (incl) | SGI | PPI | Ext PPI |
292 * +---------------------------------------------+
293 * | Bit | 0..15 | 16..31 | 32..95 |
294 * | Int Id | 0..15 | 16..31 | 1056..1119 |
295 * +---------------------------------------------+
296 */
297 uint16_t uIntId;
298 /* SGIs and PPIs. */
299 if (idxIntr < 32)
300 uIntId = idxIntr;
301 /* Extended PPIs. */
302 else if (idxIntr < 96)
303 uIntId = GIC_INTID_RANGE_EXT_PPI_START + idxIntr - 32;
304 else
305 {
306 uIntId = 0;
307 AssertReleaseFailed();
308 }
309 Assert(GIC_IS_INTR_SGI_OR_PPI(uIntId) || GIC_IS_INTR_EXT_PPI(uIntId));
310 return uIntId;
311}
312
313
314/**
315 * Gets the redistributor interrupt index given an interrupt ID.
316 *
317 * @returns The interrupt ID.
318 * @param uIntId The interrupt ID.
319 * @remarks A redistributor interrupt is an interrupt type that belong in the
320 * redistributor (e.g. SGIs, PPIs, extended PPIs).
321 */
322static uint16_t gicReDistGetIndexFromIntId(uint16_t uIntId)
323{
324 /* SGIs and PPIs. */
325 uint16_t idxIntr;
326 if (uIntId <= GIC_INTID_RANGE_PPI_LAST)
327 idxIntr = uIntId;
328 /* Extended PPIs. */
329 else if (uIntId - GIC_INTID_RANGE_EXT_PPI_START < GIC_INTID_EXT_PPI_RANGE_SIZE)
330 idxIntr = 32 + uIntId - GIC_INTID_RANGE_EXT_PPI_START;
331 else
332 {
333 idxIntr = 0;
334 AssertReleaseFailed();
335 }
336 Assert(idxIntr < sizeof(GICCPU::bmIntrPending) * 8);
337 return idxIntr;
338}
339
340
341/**
342 * Sets the interrupt pending force-flag and pokes the EMT if required.
343 *
344 * @param pVCpu The cross context virtual CPU structure.
345 * @param fIrq Flag whether to assert the IRQ line or leave it alone.
346 * @param fFiq Flag whether to assert the FIQ line or leave it alone.
347 */
348static void gicSetInterruptFF(PVMCPUCC pVCpu, bool fIrq, bool fFiq)
349{
350 Assert(fIrq || fFiq);
351 LogFlowFunc(("pVCpu=%p{.idCpu=%u} fIrq=%RTbool fFiq=%RTbool\n", pVCpu, pVCpu->idCpu, fIrq, fFiq));
352
353#ifdef IN_RING3
354 /* IRQ state should be loaded as-is by "LoadExec". Changes can be made from LoadDone. */
355 Assert(pVCpu->pVMR3->enmVMState != VMSTATE_LOADING || PDMR3HasLoadedState(pVCpu->pVMR3));
356#endif
357
358 if (fIrq)
359 VMCPU_FF_SET(pVCpu, VMCPU_FF_INTERRUPT_IRQ);
360 if (fFiq)
361 VMCPU_FF_SET(pVCpu, VMCPU_FF_INTERRUPT_FIQ);
362
363 /*
364 * We need to wake up the target CPU if we're not on EMT.
365 */
366 /** @todo We could just use RTThreadNativeSelf() here, couldn't we? */
367#if defined(IN_RING0)
368# error "Implement me!"
369#elif defined(IN_RING3)
370 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
371 VMCPUID idCpu = pVCpu->idCpu;
372 if (VMMGetCpuId(pVM) != idCpu)
373 {
374 Log7Func(("idCpu=%u enmState=%d\n", idCpu, pVCpu->enmState));
375 VMR3NotifyCpuFFU(pVCpu->pUVCpu, VMNOTIFYFF_FLAGS_POKE);
376 }
377#endif
378}
379
380
381/**
382 * Clears the interrupt pending force-flag.
383 *
384 * @param pVCpu The cross context virtual CPU structure.
385 * @param fIrq Flag whether to clear the IRQ flag.
386 * @param fFiq Flag whether to clear the FIQ flag.
387 */
388DECLINLINE(void) gicClearInterruptFF(PVMCPUCC pVCpu, bool fIrq, bool fFiq)
389{
390 Assert(fIrq || fFiq);
391 LogFlowFunc(("pVCpu=%p{.idCpu=%u} fIrq=%RTbool fFiq=%RTbool\n", pVCpu, pVCpu->idCpu, fIrq, fFiq));
392
393#ifdef IN_RING3
394 /* IRQ state should be loaded as-is by "LoadExec". Changes can be made from LoadDone. */
395 Assert(pVCpu->pVMR3->enmVMState != VMSTATE_LOADING || PDMR3HasLoadedState(pVCpu->pVMR3));
396#endif
397
398 if (fIrq)
399 VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_IRQ);
400 if (fFiq)
401 VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_FIQ);
402}
403
404
405/**
406 * Updates the interrupt force-flag.
407 *
408 * @param pVCpu The cross context virtual CPU structure.
409 * @param fIrq Flag whether to clear the IRQ flag.
410 * @param fFiq Flag whether to clear the FIQ flag.
411 */
412DECLINLINE(void) gicUpdateInterruptFF(PVMCPUCC pVCpu, bool fIrq, bool fFiq)
413{
414 LogFlowFunc(("pVCpu=%p{.idCpu=%u} fIrq=%RTbool fFiq=%RTbool\n", pVCpu, pVCpu->idCpu, fIrq, fFiq));
415
416 if (fIrq || fFiq)
417 gicSetInterruptFF(pVCpu, fIrq, fFiq);
418
419 if (!fIrq || !fFiq)
420 gicClearInterruptFF(pVCpu, !fIrq, !fFiq);
421}
422
423
424/**
425 * Gets whether the redistributor has pending interrupts with sufficient priority to
426 * be signalled to the PE.
427 *
428 * @param pGicCpu The GIC redistributor and CPU interface state.
429 * @param pfIrq Where to store whether IRQs can be signalled.
430 * @param pfFiq Where to store whether FIQs can be signalled.
431 */
432static void gicReDistHasIrqPending(PCGICCPU pGicCpu, bool *pfIrq, bool *pfFiq)
433{
434 bool const fIsGroup1Enabled = pGicCpu->fIntrGroup1Enabled;
435 bool const fIsGroup0Enabled = pGicCpu->fIntrGroup0Enabled;
436 LogFlowFunc(("fIsGroup0Enabled=%RTbool fIsGroup1Enabled=%RTbool\n", fIsGroup0Enabled, fIsGroup1Enabled));
437
438# if 1
439 uint32_t bmIntrs[3];
440 for (uint8_t i = 0; i < RT_ELEMENTS(bmIntrs); i++)
441 {
442 /* Collect interrupts that are pending, enabled and inactive. */
443 bmIntrs[i] = (pGicCpu->bmIntrPending[i] & pGicCpu->bmIntrEnabled[i]) & ~pGicCpu->bmIntrActive[i];
444
445 /* Discard interrupts if the group they belong to is disabled. */
446 if (!fIsGroup1Enabled)
447 bmIntrs[i] &= ~pGicCpu->bmIntrGroup[i];
448 if (!fIsGroup0Enabled)
449 bmIntrs[i] &= pGicCpu->bmIntrGroup[i];
450 }
451
452 uint32_t const cIntrs = sizeof(bmIntrs) * 8;
453 int32_t idxIntr = ASMBitFirstSet(&bmIntrs[0], cIntrs);
454 AssertCompile(!(cIntrs % 32));
455 if (idxIntr >= 0)
456 {
457 /* Only allow interrupts with higher priority than the current configured and running one. */
458 uint8_t const bPriority = RT_MIN(pGicCpu->bIntrPriorityMask, pGicCpu->abRunningPriorities[pGicCpu->idxRunningPriority]);
459 do
460 {
461 Assert((uint32_t)idxIntr < RT_ELEMENTS(pGicCpu->abIntrPriority));
462 if (pGicCpu->abIntrPriority[idxIntr] < bPriority)
463 {
464 bool const fInGroup1 = ASMBitTest(&pGicCpu->bmIntrGroup[0], idxIntr);
465 bool const fInGroup0 = !fInGroup1;
466 *pfIrq = fInGroup1 && fIsGroup1Enabled;
467 *pfFiq = fInGroup0 && fIsGroup0Enabled;
468 return;
469 }
470 idxIntr = ASMBitNextSet(&bmIntrs[0], cIntrs, idxIntr);
471 } while (idxIntr != -1);
472 }
473#else /** @todo Measure and pick the faster version. */
474 /* Only allow interrupts with higher priority than the current configured and running one. */
475 uint8_t const bPriority = RT_MIN(pGicCpu->bIntrPriorityMask, pGicCpu->abRunningPriorities[pGicCpu->idxRunningPriority]);
476
477 for (uint8_t i = 0; i < RT_ELEMENTS(pGicCpu->bmIntrPending); i++)
478 {
479 /* Collect interrupts that are pending, enabled and inactive. */
480 uint32_t bmIntr = (pGicCpu->bmIntrPending[i] & pGicCpu->bmIntrEnabled[i]) & ~pGicCpu->bmIntrActive[i];
481
482 /* Discard interrupts if the group they belong to is disabled. */
483 if (!fIsGroup1Enabled)
484 bmIntr &= ~pGicCpu->bmIntrGroup[i];
485 if (!fIsGroup0Enabled)
486 bmIntr &= pGicCpu->bmIntrGroup[i];
487
488 /* If the interrupt is higher priority than the running interrupt, return whether to signal an IRQ, FIQ or neither. */
489 uint16_t const idxPending = ASMBitFirstSetU32(bmIntr);
490 if (idxPending > 0)
491 {
492 uint16_t const idxIntr = 32 * i + idxPending - 1;
493 AssertRelease(idxIntr < RT_ELEMENTS(pGicCpu->abIntrPriority));
494 if (pGicCpu->abIntrPriority[idxIntr] < bPriority)
495 {
496 AssertRelease(idxIntr < sizeof(pGicCpu->bmIntrGroup) * 8);
497 bool const fInGroup1 = ASMBitTest(&pGicCpu->bmIntrGroup[0], idxIntr);
498 bool const fInGroup0 = !fInGroup1;
499 *pfIrq = fInGroup1 && fIsGroup1Enabled;
500 *pfFiq = fInGroup0 && fIsGroup0Enabled;
501 return;
502 }
503 }
504 }
505#endif
506 *pfIrq = false;
507 *pfFiq = false;
508}
509
510
511/**
512 * Gets whether the distributor has pending interrupts with sufficient priority to
513 * be signalled to the PE.
514 *
515 * @param pGicDev The GIC distributor state.
516 * @param pVCpu The cross context virtual CPU structure.
517 * @param idCpu The ID of the virtual CPU.
518 * @param pfIrq Where to store whether there are IRQs can be signalled.
519 * @param pfFiq Where to store whether there are FIQs can be signalled.
520 */
521static void gicDistHasIrqPendingForVCpu(PCGICDEV pGicDev, PCVMCPUCC pVCpu, VMCPUID idCpu, bool *pfIrq, bool *pfFiq)
522{
523 bool const fIsGroup1Enabled = pGicDev->fIntrGroup1Enabled;
524 bool const fIsGroup0Enabled = pGicDev->fIntrGroup0Enabled;
525 LogFlowFunc(("fIsGroup1Enabled=%RTbool fIsGroup0Enabled=%RTbool\n", fIsGroup1Enabled, fIsGroup0Enabled));
526
527#if 1
528 uint32_t bmIntrs[64];
529 for (uint8_t i = 0; i < RT_ELEMENTS(bmIntrs); i++)
530 {
531 /* Collect interrupts that are pending, enabled and inactive. */
532 bmIntrs[i] = (pGicDev->bmIntrPending[i] & pGicDev->bmIntrEnabled[i]) & ~pGicDev->bmIntrActive[i];
533
534 /* Discard interrupts if the group they belong to is disabled. */
535 if (!fIsGroup1Enabled)
536 bmIntrs[i] &= ~pGicDev->bmIntrGroup[i];
537 if (!fIsGroup0Enabled)
538 bmIntrs[i] &= pGicDev->bmIntrGroup[i];
539 }
540
541 /*
542 * The distributor's interrupt pending/enabled/active bitmaps have 2048 bits which map
543 * SGIs (16), PPIs (16), SPIs (988), reserved SPIs (4) and extended SPIs (1024).
544 * Of these, the first 32 bits corresponding to SGIs and PPIs are RAZ/WI when affinity
545 * routing is enabled (which it currently is always enabled in our implementation).
546 */
547 Assert(pGicDev->fAffRoutingEnabled);
548 Assert(bmIntrs[0] == 0);
549 uint32_t const cIntrs = sizeof(bmIntrs) * 8;
550 int32_t idxIntr = ASMBitFirstSet(&bmIntrs[0], cIntrs);
551 AssertCompile(!(cIntrs % 32));
552 if (idxIntr >= 0)
553 {
554 /* Only allow interrupts with higher priority than the current configured and running one. */
555 PCGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
556 uint8_t const bPriority = RT_MIN(pGicCpu->bIntrPriorityMask, pGicCpu->abRunningPriorities[pGicCpu->idxRunningPriority]);
557 do
558 {
559 AssertCompile(RT_ELEMENTS(pGicDev->abIntrPriority) == RT_ELEMENTS(pGicDev->au32IntrRouting));
560 Assert((uint32_t)idxIntr < RT_ELEMENTS(pGicDev->abIntrPriority));
561 Assert(idxIntr < GIC_INTID_RANGE_SPECIAL_START || idxIntr > GIC_INTID_RANGE_SPECIAL_LAST);
562 if ( pGicDev->abIntrPriority[idxIntr] < bPriority
563 && pGicDev->au32IntrRouting[idxIntr] == idCpu)
564 {
565 bool const fInGroup1 = ASMBitTest(&pGicDev->bmIntrGroup[0], idxIntr);
566 bool const fInGroup0 = !fInGroup1;
567 *pfFiq = fInGroup0 && fIsGroup0Enabled;
568 *pfIrq = fInGroup1 && fIsGroup1Enabled;
569 return;
570 }
571 idxIntr = ASMBitNextSet(&bmIntrs[0], cIntrs, idxIntr);
572 } while (idxIntr != -1);
573 }
574#else /** @todo Measure and pick the faster version. */
575 /* Only allow interrupts with higher priority than the running one. */
576 PCGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
577 uint8_t const bPriority = RT_MIN(pGicCpu->bIntrPriorityMask, pGicCpu->abRunningPriorities[pGicCpu->idxRunningPriority]);
578
579 for (uint8_t i = 0; i < RT_ELEMENTS(pGicDev->bmIntrPending); i += 2)
580 {
581 /* Collect interrupts that are pending, enabled and inactive. */
582 uint32_t uLo = (pGicDev->bmIntrPending[i] & pGicDev->bmIntrEnabled[i]) & ~pGicDev->bmIntrActive[i];
583 uint32_t uHi = (pGicDev->bmIntrPending[i + 1] & pGicDev->bmIntrEnabled[i + 1]) & ~pGicDev->bmIntrActive[i + 1];
584
585 /* Discard interrupts if the group they belong to is disabled. */
586 if (!fIsGroup1Enabled)
587 {
588 uLo &= ~pGicDev->bmIntrGroup[i];
589 uHi &= ~pGicDev->bmIntrGroup[i + 1];
590 }
591 if (!fIsGroup0Enabled)
592 {
593 uLo &= pGicDev->bmIntrGroup[i];
594 uHi &= pGicDev->bmIntrGroup[i + 1];
595 }
596
597 /* If the interrupt is higher priority than the running interrupt, return whether to signal an IRQ, FIQ or neither. */
598 Assert(pGicDev->fAffRoutingEnabled);
599 uint64_t const bmIntrPending = RT_MAKE_U64(uLo, uHi);
600 uint16_t const idxPending = ASMBitFirstSetU64(bmIntrPending);
601 if (idxPending > 0)
602 {
603 /*
604 * The distributor's interrupt pending/enabled/active bitmaps have 2048 bits which map
605 * SGIs (16), PPIs (16), SPIs (988), reserved SPIs (4) and extended SPIs (1024).
606 * Of these, the first 32 bits corresponding to SGIs and PPIs are RAZ/WI when affinity
607 * routing is enabled (which it always is in our implementation).
608 */
609 uint32_t const idxIntr = 64 * i + idxPending - 1;
610 AssertRelease(idxIntr < RT_ELEMENTS(pGicDev->abIntrPriority));
611 if ( pGicDev->abIntrPriority[idxIntr] < bPriority
612 && pGicDev->au32IntrRouting[idxIntr] == idCpu)
613 {
614 Assert(idxIntr > GIC_INTID_RANGE_PPI_LAST);
615 AssertRelease(idxIntr < sizeof(pGicDev->bmIntrGroup) * 8);
616 bool const fInGroup1 = ASMBitTest(&pGicDev->bmIntrGroup[0], idxIntr);
617 bool const fInGroup0 = !fInGroup1;
618 *pfFiq = fInGroup0 && fIsGroup0Enabled;
619 *pfIrq = fInGroup1 && fIsGroup1Enabled;
620 return;
621 }
622 }
623 }
624#endif
625 *pfIrq = false;
626 *pfFiq = false;
627}
628
629
630/**
631 * Updates the internal IRQ state and sets or clears the appropriate force action
632 * flags.
633 *
634 * @returns Strict VBox status code.
635 * @param pGicDev The GIC distributor state.
636 * @param pVCpu The cross context virtual CPU structure.
637 */
638static VBOXSTRICTRC gicReDistUpdateIrqState(PCGICDEV pGicDev, PVMCPUCC pVCpu)
639{
640 LogFlowFunc(("\n"));
641 bool fIrq;
642 bool fFiq;
643 gicReDistHasIrqPending(VMCPU_TO_GICCPU(pVCpu), &fIrq, &fFiq);
644
645 bool fIrqDist;
646 bool fFiqDist;
647 gicDistHasIrqPendingForVCpu(pGicDev, pVCpu, pVCpu->idCpu, &fIrqDist, &fFiqDist);
648 LogFlowFunc(("fIrq=%RTbool fFiq=%RTbool fIrqDist=%RTbool fFiqDist=%RTbool\n", fIrq, fFiq, fIrqDist, fFiqDist));
649
650 fIrq |= fIrqDist;
651 fFiq |= fFiqDist;
652 gicUpdateInterruptFF(pVCpu, fIrq, fFiq);
653 return VINF_SUCCESS;
654}
655
656
657/**
658 * Updates the internal IRQ state of the distributor and sets or clears the appropirate force action flags.
659 *
660 * @returns Strict VBox status code.
661 * @param pVM The cross context VM state.
662 * @param pGicDev The GIC distributor state.
663 */
664static VBOXSTRICTRC gicDistUpdateIrqState(PCVMCC pVM, PCGICDEV pGicDev)
665{
666 LogFlowFunc(("\n"));
667 for (uint32_t i = 0; i < pVM->cCpus; i++)
668 {
669 PVMCPUCC pVCpu = pVM->CTX_SUFF(apCpus)[i];
670 PCGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
671
672 bool fIrq, fFiq;
673 gicReDistHasIrqPending(pGicCpu, &fIrq, &fFiq);
674
675 bool fIrqDist, fFiqDist;
676 gicDistHasIrqPendingForVCpu(pGicDev, pVCpu, i, &fIrqDist, &fFiqDist);
677 fIrq |= fIrqDist;
678 fFiq |= fFiqDist;
679
680 gicUpdateInterruptFF(pVCpu, fIrq, fFiq);
681 }
682 return VINF_SUCCESS;
683}
684
685
686/**
687 * Reads the distributor's interrupt routing register (GICD_IROUTER).
688 *
689 * @returns Strict VBox status code.
690 * @param pGicDev The GIC distributor state.
691 * @param idxReg The index of the register in the GICD_IROUTER range.
692 * @param puValue Where to store the register's value.
693 */
694static VBOXSTRICTRC gicDistReadIntrRoutingReg(PCGICDEV pGicDev, uint16_t idxReg, uint32_t *puValue)
695{
696 /* When affinity routing is disabled, reads return 0. */
697 Assert(pGicDev->fAffRoutingEnabled);
698
699 /* Hardware does not map the first 32 registers (corresponding to SGIs and PPIs). */
700 idxReg += GIC_INTID_RANGE_SPI_START;
701 AssertReturn(idxReg < RT_ELEMENTS(pGicDev->au32IntrRouting), VERR_BUFFER_OVERFLOW);
702 Assert(idxReg < sizeof(pGicDev->bmIntrRoutingMode) * 8);
703 if (!(idxReg % 2))
704 {
705 /* Lower 32-bits. */
706 uint8_t const fIrm = ASMBitTest(&pGicDev->bmIntrRoutingMode[0], idxReg);
707 *puValue = GIC_DIST_REG_IROUTERn_SET(fIrm, pGicDev->au32IntrRouting[idxReg]);
708 }
709 else
710 {
711 /* Upper 32-bits. */
712 *puValue = pGicDev->au32IntrRouting[idxReg] >> 24;
713 }
714
715 LogFlowFunc(("idxReg=%#x read %#x\n", idxReg, *puValue));
716 return VINF_SUCCESS;
717}
718
719
720/**
721 * Writes the distributor's interrupt routing register (GICD_IROUTER).
722 *
723 * @returns Strict VBox status code.
724 * @param pGicDev The GIC distributor state.
725 * @param idxReg The index of the register in the GICD_IROUTER range.
726 * @param uValue The value to write to the register.
727 */
728static VBOXSTRICTRC gicDistWriteIntrRoutingReg(PGICDEV pGicDev, uint16_t idxReg, uint32_t uValue)
729{
730 /* When affinity routing is disabled, writes are ignored. */
731 Assert(pGicDev->fAffRoutingEnabled);
732
733 AssertMsgReturn(idxReg < RT_ELEMENTS(pGicDev->au32IntrRouting), ("idxReg=%u\n", idxReg), VERR_BUFFER_OVERFLOW);
734 Assert(idxReg < sizeof(pGicDev->bmIntrRoutingMode) * 8);
735 if (!(idxReg % 2))
736 {
737 /* Lower 32-bits. */
738 bool const fIrm = GIC_DIST_REG_IROUTERn_IRM_GET(uValue);
739 if (fIrm)
740 ASMBitSet(&pGicDev->bmIntrRoutingMode[0], idxReg);
741 else
742 ASMBitClear(&pGicDev->bmIntrRoutingMode[0], idxReg);
743 uint32_t const fAff3 = pGicDev->au32IntrRouting[idxReg] & 0xff000000;
744 pGicDev->au32IntrRouting[idxReg] = fAff3 | (uValue & 0x00ffffff);
745 }
746 else
747 {
748 /* Upper 32-bits. */
749 uint32_t const fAffOthers = pGicDev->au32IntrRouting[idxReg] & 0x00ffffff;
750 pGicDev->au32IntrRouting[idxReg] = (uValue << 24) | fAffOthers;
751 }
752
753 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicDev->au32IntrRouting[idxReg]));
754 return VINF_SUCCESS;
755}
756
757
758/**
759 * Reads the distributor's interrupt (set/clear) enable register (GICD_ISENABLER and
760 * GICD_ICENABLER).
761 *
762 * @returns Strict VBox status code.
763 * @param pGicDev The GIC distributor state.
764 * @param idxReg The index of the register in the GICD_ISENABLER and
765 * GICD_ICENABLER range.
766 * @param puValue Where to store the register's value.
767 */
768static VBOXSTRICTRC gicDistReadIntrEnableReg(PGICDEV pGicDev, uint16_t idxReg, uint32_t *puValue)
769{
770 Assert(idxReg < RT_ELEMENTS(pGicDev->bmIntrEnabled));
771 *puValue = pGicDev->bmIntrEnabled[idxReg];
772 LogFlowFunc(("idxReg=%#x read %#x\n", idxReg, pGicDev->bmIntrEnabled[idxReg]));
773 return VINF_SUCCESS;
774}
775
776
777/**
778 * Writes the distributor's interrupt set-enable register (GICD_ISENABLER).
779 *
780 * @returns Strict VBox status code.
781 * @param pVM The cross context VM structure.
782 * @param pGicDev The GIC distributor state.
783 * @param idxReg The index of the register in the GICD_ISENABLER range.
784 * @param uValue The value to write to the register.
785 */
786static VBOXSTRICTRC gicDistWriteIntrSetEnableReg(PVM pVM, PGICDEV pGicDev, uint16_t idxReg, uint32_t uValue)
787{
788 /* When affinity routing is enabled, writes to SGIs and PPIs are ignored. */
789 Assert(pGicDev->fAffRoutingEnabled);
790 if (idxReg > 0)
791 {
792 Assert(idxReg < RT_ELEMENTS(pGicDev->bmIntrEnabled));
793 pGicDev->bmIntrEnabled[idxReg] |= uValue;
794 return gicDistUpdateIrqState(pVM, pGicDev);
795 }
796 else
797 AssertReleaseFailed();
798 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicDev->bmIntrEnabled[idxReg]));
799 return VINF_SUCCESS;
800}
801
802
803/**
804 * Writes the distributor's interrupt clear-enable register (GICD_ICENABLER).
805 *
806 * @returns Strict VBox status code.
807 * @param pVM The cross context VM structure.
808 * @param pGicDev The GIC distributor state.
809 * @param idxReg The index of the register in the GICD_ICENABLER range.
810 * @param uValue The value to write to the register.
811 */
812static VBOXSTRICTRC gicDistWriteIntrClearEnableReg(PVM pVM, PGICDEV pGicDev, uint16_t idxReg, uint32_t uValue)
813{
814 /* When affinity routing is enabled, writes to SGIs and PPIs are ignored. */
815 Assert(pGicDev->fAffRoutingEnabled);
816 if (idxReg > 0)
817 {
818 Assert(idxReg < RT_ELEMENTS(pGicDev->bmIntrEnabled));
819 pGicDev->bmIntrEnabled[idxReg] &= ~uValue;
820 return gicDistUpdateIrqState(pVM, pGicDev);
821 }
822 else
823 AssertReleaseFailed();
824 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicDev->bmIntrEnabled[idxReg]));
825 return VINF_SUCCESS;
826}
827
828
829/**
830 * Reads the distributor's interrupt active register (GICD_ISACTIVER and
831 * GICD_ICACTIVER).
832 *
833 * @returns Strict VBox status code.
834 * @param pGicDev The GIC distributor state.
835 * @param idxReg The index of the register in the GICD_ISACTIVER and
836 * GICD_ICACTIVER range.
837 * @param puValue Where to store the register's value.
838 */
839static VBOXSTRICTRC gicDistReadIntrActiveReg(PGICDEV pGicDev, uint16_t idxReg, uint32_t *puValue)
840{
841 Assert(idxReg < RT_ELEMENTS(pGicDev->bmIntrActive));
842 *puValue = pGicDev->bmIntrActive[idxReg];
843 LogFlowFunc(("idxReg=%#x read %#x\n", idxReg, pGicDev->bmIntrActive[idxReg]));
844 return VINF_SUCCESS;
845}
846
847
848/**
849 * Writes the distributor's interrupt set-active register (GICD_ISACTIVER).
850 *
851 * @returns Strict VBox status code.
852 * @param pVM The cross context VM structure.
853 * @param pGicDev The GIC distributor state.
854 * @param idxReg The index of the register in the GICD_ISACTIVER range.
855 * @param uValue The value to write to the register.
856 */
857static VBOXSTRICTRC gicDistWriteIntrSetActiveReg(PVM pVM, PGICDEV pGicDev, uint16_t idxReg, uint32_t uValue)
858{
859 /* When affinity routing is enabled, writes to SGIs and PPIs are ignored. */
860 Assert(pGicDev->fAffRoutingEnabled);
861 if (idxReg > 0)
862 {
863 Assert(idxReg < RT_ELEMENTS(pGicDev->bmIntrActive));
864 pGicDev->bmIntrActive[idxReg] |= uValue;
865 return gicDistUpdateIrqState(pVM, pGicDev);
866 }
867 else
868 AssertReleaseFailed();
869 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicDev->bmIntrActive[idxReg]));
870 return VINF_SUCCESS;
871}
872
873
874/**
875 * Writes the distributor's interrupt clear-active register (GICD_ICACTIVER).
876 *
877 * @returns Strict VBox status code.
878 * @param pVM The cross context VM structure.
879 * @param pGicDev The GIC distributor state.
880 * @param idxReg The index of the register in the GICD_ICACTIVER range.
881 * @param uValue The value to write to the register.
882 */
883static VBOXSTRICTRC gicDistWriteIntrClearActiveReg(PVM pVM, PGICDEV pGicDev, uint16_t idxReg, uint32_t uValue)
884{
885 /* When affinity routing is enabled, writes to SGIs and PPIs are ignored. */
886 Assert(pGicDev->fAffRoutingEnabled);
887 if (idxReg > 0)
888 {
889 Assert(idxReg < RT_ELEMENTS(pGicDev->bmIntrActive));
890 pGicDev->bmIntrActive[idxReg] &= ~uValue;
891 return gicDistUpdateIrqState(pVM, pGicDev);
892 }
893 else
894 AssertReleaseFailed();
895 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicDev->bmIntrActive[idxReg]));
896 return VINF_SUCCESS;
897}
898
899
900/**
901 * Reads the distributor's interrupt priority register (GICD_IPRIORITYR).
902 *
903 * @returns Strict VBox status code.
904 * @param pGicDev The GIC distributor state.
905 * @param idxReg The index of the register in the GICD_IPRIORITY range.
906 * @param puValue Where to store the register's value.
907 */
908static VBOXSTRICTRC gicDistReadIntrPriorityReg(PGICDEV pGicDev, uint16_t idxReg, uint32_t *puValue)
909{
910 /* When affinity routing is enabled, reads to registers 0..7 (pertaining to SGIs and PPIs) return 0. */
911 Assert(pGicDev->fAffRoutingEnabled);
912 Assert(idxReg < RT_ELEMENTS(pGicDev->abIntrPriority) / sizeof(uint32_t));
913 Assert(idxReg != 255);
914 if (idxReg > 7)
915 {
916 uint16_t const idxPriority = idxReg * sizeof(uint32_t);
917 AssertReturn(idxPriority <= RT_ELEMENTS(pGicDev->abIntrPriority) - sizeof(uint32_t), VERR_BUFFER_OVERFLOW);
918 AssertCompile(sizeof(*puValue) == sizeof(uint32_t));
919 *puValue = *(uint32_t *)&pGicDev->abIntrPriority[idxPriority];
920 }
921 else
922 {
923 AssertReleaseFailed();
924 *puValue = 0;
925 }
926 LogFlowFunc(("idxReg=%#x read %#x\n", idxReg, *puValue));
927 return VINF_SUCCESS;
928}
929
930
931/**
932 * Writes the distributor's interrupt priority register (GICD_IPRIORITYR).
933 *
934 * @returns Strict VBox status code.
935 * @param pGicDev The GIC distributor state.
936 * @param idxReg The index of the register in the GICD_IPRIORITY range.
937 * @param uValue The value to write to the register.
938 */
939static VBOXSTRICTRC gicDistWriteIntrPriorityReg(PGICDEV pGicDev, uint16_t idxReg, uint32_t uValue)
940{
941 /* When affinity routing is enabled, writes to registers 0..7 are ignored. */
942 Assert(pGicDev->fAffRoutingEnabled);
943 Assert(idxReg < RT_ELEMENTS(pGicDev->abIntrPriority) / sizeof(uint32_t));
944 Assert(idxReg != 255);
945 if (idxReg > 7)
946 {
947 uint16_t const idxPriority = idxReg * sizeof(uint32_t);
948 AssertReturn(idxPriority <= RT_ELEMENTS(pGicDev->abIntrPriority) - sizeof(uint32_t), VERR_BUFFER_OVERFLOW);
949 AssertCompile(sizeof(uValue) == sizeof(uint32_t));
950 *(uint32_t *)&pGicDev->abIntrPriority[idxPriority] = uValue;
951 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, *(uint32_t *)&pGicDev->abIntrPriority[idxPriority]));
952 }
953 else
954 AssertReleaseFailed();
955 return VINF_SUCCESS;
956}
957
958
959/**
960 * Reads the distributor's interrupt pending register (GICD_ISPENDR and
961 * GICD_ICPENDR).
962 *
963 * @returns Strict VBox status code.
964 * @param pGicDev The GIC distributor state.
965 * @param idxReg The index of the register in the GICD_ISPENDR and
966 * GICD_ICPENDR range.
967 * @param puValue Where to store the register's value.
968 */
969static VBOXSTRICTRC gicDistReadIntrPendingReg(PGICDEV pGicDev, uint16_t idxReg, uint32_t *puValue)
970{
971 /* When affinity routing is enabled, reads for SGIs and PPIs return 0. */
972 Assert(pGicDev->fAffRoutingEnabled);
973 if (idxReg > 0)
974 {
975 Assert(idxReg < RT_ELEMENTS(pGicDev->bmIntrPending));
976 *puValue = pGicDev->bmIntrPending[idxReg];
977 }
978 else
979 {
980 AssertReleaseFailed();
981 *puValue = 0;
982 }
983 LogFlowFunc(("idxReg=%#x read %#x\n", idxReg, pGicDev->bmIntrPending[idxReg]));
984 return VINF_SUCCESS;
985}
986
987
988/**
989 * Write's the distributor's interrupt set-pending register (GICD_ISPENDR).
990 *
991 * @returns Strict VBox status code.
992 * @param pVM The cross context VM structure.
993 * @param pGicDev The GIC distributor state.
994 * @param idxReg The index of the register in the GICD_ISPENDR range.
995 * @param uValue The value to write to the register.
996 */
997static VBOXSTRICTRC gicDistWriteIntrSetPendingReg(PVMCC pVM, PGICDEV pGicDev, uint16_t idxReg, uint32_t uValue)
998{
999 /* When affinity routing is enabled, writes to SGIs and PPIs are ignored. */
1000 Assert(pGicDev->fAffRoutingEnabled);
1001 if (idxReg > 0)
1002 {
1003 Assert(idxReg < RT_ELEMENTS(pGicDev->bmIntrPending));
1004 pGicDev->bmIntrPending[idxReg] |= uValue;
1005 return gicDistUpdateIrqState(pVM, pGicDev);
1006 }
1007 else
1008 AssertReleaseFailed();
1009 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicDev->bmIntrPending[idxReg]));
1010 return VINF_SUCCESS;
1011}
1012
1013
1014/**
1015 * Write's the distributor's interrupt clear-pending register (GICD_ICPENDR).
1016 *
1017 * @returns Strict VBox status code.
1018 * @param pVM The cross context VM structure.
1019 * @param pGicDev The GIC distributor state.
1020 * @param idxReg The index of the register in the GICD_ICPENDR range.
1021 * @param uValue The value to write to the register.
1022 */
1023static VBOXSTRICTRC gicDistWriteIntrClearPendingReg(PVMCC pVM, PGICDEV pGicDev, uint16_t idxReg, uint32_t uValue)
1024{
1025 /* When affinity routing is enabled, writes to SGIs and PPIs are ignored. */
1026 Assert(pGicDev->fAffRoutingEnabled);
1027 if (idxReg > 0)
1028 {
1029 Assert(idxReg < RT_ELEMENTS(pGicDev->bmIntrPending));
1030 pGicDev->bmIntrPending[idxReg] &= ~uValue;
1031 return gicDistUpdateIrqState(pVM, pGicDev);
1032 }
1033 else
1034 AssertReleaseFailed();
1035 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicDev->bmIntrPending[idxReg]));
1036 return VINF_SUCCESS;
1037}
1038
1039
1040/**
1041 * Reads the distributor's interrupt config register (GICD_ICFGR).
1042 *
1043 * @returns Strict VBox status code.
1044 * @param pGicDev The GIC distributor state.
1045 * @param idxReg The index of the register in the GICD_ICFGR range.
1046 * @param puValue Where to store the register's value.
1047 */
1048static VBOXSTRICTRC gicDistReadIntrConfigReg(PCGICDEV pGicDev, uint16_t idxReg, uint32_t *puValue)
1049{
1050 /* When affinity routing is enabled SGIs and PPIs, reads to SGIs and PPIs return 0. */
1051 Assert(pGicDev->fAffRoutingEnabled);
1052 if (idxReg >= 2)
1053 {
1054 Assert(idxReg < RT_ELEMENTS(pGicDev->bmIntrConfig));
1055 *puValue = pGicDev->bmIntrConfig[idxReg];
1056 }
1057 else
1058 AssertReleaseFailed();
1059 LogFlowFunc(("idxReg=%#x read %#x\n", idxReg, pGicDev->bmIntrConfig[idxReg]));
1060 return VINF_SUCCESS;
1061}
1062
1063
1064/**
1065 * Writes the distributor's interrupt config register (GICD_ICFGR).
1066 *
1067 * @returns Strict VBox status code.
1068 * @param pGicDev The GIC distributor state.
1069 * @param idxReg The index of the register in the GICD_ICFGR range.
1070 * @param uValue The value to write to the register.
1071 */
1072static VBOXSTRICTRC gicDistWriteIntrConfigReg(PGICDEV pGicDev, uint16_t idxReg, uint32_t uValue)
1073{
1074 /* When affinity routing is enabled SGIs and PPIs, writes to SGIs and PPIs are ignored. */
1075 Assert(pGicDev->fAffRoutingEnabled);
1076 if (idxReg >= 2)
1077 {
1078 Assert(idxReg < RT_ELEMENTS(pGicDev->bmIntrConfig));
1079 pGicDev->bmIntrConfig[idxReg] = uValue & 0xaaaaaaaa;
1080 }
1081 else
1082 AssertReleaseFailed();
1083 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicDev->bmIntrConfig[idxReg]));
1084 return VINF_SUCCESS;
1085}
1086
1087
1088/**
1089 * Reads the distributor's interrupt config register (GICD_IGROUPR).
1090 *
1091 * @returns Strict VBox status code.
1092 * @param pGicDev The GIC distributor state.
1093 * @param idxReg The index of the register in the GICD_IGROUPR range.
1094 * @param puValue Where to store the register's value.
1095 */
1096static VBOXSTRICTRC gicDistReadIntrGroupReg(PGICDEV pGicDev, uint16_t idxReg, uint32_t *puValue)
1097{
1098 /* When affinity routing is enabled, reads to SGIs and PPIs return 0. */
1099 Assert(pGicDev->fAffRoutingEnabled);
1100 if (idxReg > 0)
1101 {
1102 Assert(idxReg < RT_ELEMENTS(pGicDev->bmIntrGroup));
1103 *puValue = pGicDev->bmIntrGroup[idxReg];
1104 }
1105 else
1106 AssertReleaseFailed();
1107 LogFlowFunc(("idxReg=%#x read %#x\n", idxReg, *puValue));
1108 return VINF_SUCCESS;
1109}
1110
1111
1112/**
1113 * Writes the distributor's interrupt config register (GICD_ICFGR).
1114 *
1115 * @returns Strict VBox status code.
1116 * @param pVM The cross context VM structure.
1117 * @param pGicDev The GIC distributor state.
1118 * @param idxReg The index of the register in the GICD_ICFGR range.
1119 * @param uValue The value to write to the register.
1120 */
1121static VBOXSTRICTRC gicDistWriteIntrGroupReg(PCVM pVM, PGICDEV pGicDev, uint16_t idxReg, uint32_t uValue)
1122{
1123 /* When affinity routing is enabled, writes to SGIs and PPIs are ignored. */
1124 Assert(pGicDev->fAffRoutingEnabled);
1125 if (idxReg > 0)
1126 {
1127 pGicDev->bmIntrGroup[idxReg] = uValue;
1128 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicDev->bmIntrGroup[idxReg]));
1129 }
1130 else
1131 AssertReleaseFailed();
1132 return gicDistUpdateIrqState(pVM, pGicDev);
1133}
1134
1135
1136/**
1137 * Reads the redistributor's interrupt priority register (GICR_IPRIORITYR).
1138 *
1139 * @returns Strict VBox status code.
1140 * @param pGicDev The GIC distributor state.
1141 * @param pGicCpu The GIC redistributor and CPU interface state.
1142 * @param idxReg The index of the register in the GICR_IPRIORITY range.
1143 * @param puValue Where to store the register's value.
1144 */
1145static VBOXSTRICTRC gicReDistReadIntrPriorityReg(PCGICDEV pGicDev, PGICCPU pGicCpu, uint16_t idxReg, uint32_t *puValue)
1146{
1147 /* When affinity routing is disabled, reads return 0. */
1148 Assert(pGicDev->fAffRoutingEnabled); RT_NOREF(pGicDev);
1149 uint16_t const idxPriority = idxReg * sizeof(uint32_t);
1150 AssertReturn(idxPriority <= RT_ELEMENTS(pGicCpu->abIntrPriority) - sizeof(uint32_t), VERR_BUFFER_OVERFLOW);
1151 AssertCompile(sizeof(*puValue) == sizeof(uint32_t));
1152 *puValue = *(uint32_t *)&pGicCpu->abIntrPriority[idxPriority];
1153 LogFlowFunc(("idxReg=%#x read %#x\n", idxReg, *puValue));
1154 return VINF_SUCCESS;
1155}
1156
1157
1158/**
1159 * Writes the redistributor's interrupt priority register (GICR_IPRIORITYR).
1160 *
1161 * @returns Strict VBox status code.
1162 * @param pGicDev The GIC distributor state.
1163 * @param pVCpu The cross context virtual CPU structure.
1164 * @param idxReg The index of the register in the GICR_IPRIORITY range.
1165 * @param uValue The value to write to the register.
1166 */
1167static VBOXSTRICTRC gicReDistWriteIntrPriorityReg(PCGICDEV pGicDev, PVMCPUCC pVCpu, uint16_t idxReg, uint32_t uValue)
1168{
1169 /* When affinity routing is disabled, writes are ignored. */
1170 Assert(pGicDev->fAffRoutingEnabled); RT_NOREF(pGicDev);
1171 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
1172 uint16_t const idxPriority = idxReg * sizeof(uint32_t);
1173 AssertReturn(idxPriority <= RT_ELEMENTS(pGicCpu->abIntrPriority) - sizeof(uint32_t), VERR_BUFFER_OVERFLOW);
1174 AssertCompile(sizeof(uValue) == sizeof(uint32_t));
1175 *(uint32_t *)&pGicCpu->abIntrPriority[idxPriority] = uValue;
1176 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, *(uint32_t *)&pGicCpu->abIntrPriority[idxPriority]));
1177 return VINF_SUCCESS;
1178}
1179
1180
1181/**
1182 * Reads the redistributor's interrupt pending register (GICR_ISPENDR and
1183 * GICR_ICPENDR).
1184 *
1185 * @returns Strict VBox status code.
1186 * @param pGicDev The GIC distributor state.
1187 * @param pGicCpu The GIC redistributor and CPU interface state.
1188 * @param idxReg The index of the register in the GICR_ISPENDR and
1189 * GICR_ICPENDR range.
1190 * @param puValue Where to store the register's value.
1191 */
1192static VBOXSTRICTRC gicReDistReadIntrPendingReg(PCGICDEV pGicDev, PGICCPU pGicCpu, uint16_t idxReg, uint32_t *puValue)
1193{
1194 /* When affinity routing is disabled, reads return 0. */
1195 Assert(pGicDev->fAffRoutingEnabled); RT_NOREF(pGicDev);
1196 Assert(idxReg < RT_ELEMENTS(pGicCpu->bmIntrPending));
1197 *puValue = pGicCpu->bmIntrPending[idxReg];
1198 LogFlowFunc(("idxReg=%#x read %#x\n", idxReg, pGicCpu->bmIntrPending[idxReg]));
1199 return VINF_SUCCESS;
1200}
1201
1202
1203/**
1204 * Writes the redistributor's interrupt set-pending register (GICR_ISPENDR).
1205 *
1206 * @returns Strict VBox status code.
1207 * @param pGicDev The GIC distributor state.
1208 * @param pVCpu The cross context virtual CPU structure.
1209 * @param idxReg The index of the register in the GICR_ISPENDR range.
1210 * @param uValue The value to write to the register.
1211 */
1212static VBOXSTRICTRC gicReDistWriteIntrSetPendingReg(PCGICDEV pGicDev, PVMCPUCC pVCpu, uint16_t idxReg, uint32_t uValue)
1213{
1214 /* When affinity routing is disabled, writes are ignored. */
1215 Assert(pGicDev->fAffRoutingEnabled);
1216 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
1217 Assert(idxReg < RT_ELEMENTS(pGicCpu->bmIntrPending));
1218 pGicCpu->bmIntrPending[idxReg] |= uValue;
1219 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicCpu->bmIntrPending[idxReg]));
1220 return gicReDistUpdateIrqState(pGicDev, pVCpu);
1221}
1222
1223
1224/**
1225 * Writes the redistributor's interrupt clear-pending register (GICR_ICPENDR).
1226 *
1227 * @returns Strict VBox status code.
1228 * @param pGicDev The GIC distributor state.
1229 * @param pVCpu The cross context virtual CPU structure.
1230 * @param idxReg The index of the register in the GICR_ICPENDR range.
1231 * @param uValue The value to write to the register.
1232 */
1233static VBOXSTRICTRC gicReDistWriteIntrClearPendingReg(PCGICDEV pGicDev, PVMCPUCC pVCpu, uint16_t idxReg, uint32_t uValue)
1234{
1235 /* When affinity routing is disabled, writes are ignored. */
1236 Assert(pGicDev->fAffRoutingEnabled);
1237 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
1238 Assert(idxReg < RT_ELEMENTS(pGicCpu->bmIntrPending));
1239 pGicCpu->bmIntrPending[idxReg] &= ~uValue;
1240 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicCpu->bmIntrPending[idxReg]));
1241 return gicReDistUpdateIrqState(pGicDev, pVCpu);
1242}
1243
1244
1245/**
1246 * Reads the redistributor's interrupt enable register (GICR_ISENABLER and
1247 * GICR_ICENABLER).
1248 *
1249 * @returns Strict VBox status code.
1250 * @param pGicDev The GIC distributor state.
1251 * @param pGicCpu The GIC redistributor and CPU interface state.
1252 * @param idxReg The index of the register in the GICR_ISENABLER and
1253 * GICR_ICENABLER range.
1254 * @param puValue Where to store the register's value.
1255 */
1256static VBOXSTRICTRC gicReDistReadIntrEnableReg(PCGICDEV pGicDev, PGICCPU pGicCpu, uint16_t idxReg, uint32_t *puValue)
1257{
1258 Assert(pGicDev->fAffRoutingEnabled); RT_NOREF(pGicDev);
1259 Assert(idxReg < RT_ELEMENTS(pGicCpu->bmIntrEnabled));
1260 *puValue = pGicCpu->bmIntrEnabled[idxReg];
1261 LogFlowFunc(("idxReg=%#x read %#x\n", idxReg, pGicCpu->bmIntrEnabled[idxReg]));
1262 return VINF_SUCCESS;
1263}
1264
1265
1266/**
1267 * Writes the redistributor's interrupt set-enable register (GICR_ISENABLER).
1268 *
1269 * @returns Strict VBox status code.
1270 * @param pGicDev The GIC distributor state.
1271 * @param pVCpu The cross context virtual CPU structure.
1272 * @param idxReg The index of the register in the GICR_ISENABLER range.
1273 * @param uValue The value to write to the register.
1274 */
1275static VBOXSTRICTRC gicReDistWriteIntrSetEnableReg(PCGICDEV pGicDev, PVMCPUCC pVCpu, uint16_t idxReg, uint32_t uValue)
1276{
1277 Assert(pGicDev->fAffRoutingEnabled);
1278 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
1279 Assert(idxReg < RT_ELEMENTS(pGicCpu->bmIntrEnabled));
1280 pGicCpu->bmIntrEnabled[idxReg] |= uValue;
1281 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicCpu->bmIntrEnabled[idxReg]));
1282 return gicReDistUpdateIrqState(pGicDev, pVCpu);
1283}
1284
1285
1286/**
1287 * Writes the redistributor's interrupt clear-enable register (GICR_ICENABLER).
1288 *
1289 * @returns Strict VBox status code.
1290 * @param pGicDev The GIC distributor state.
1291 * @param pVCpu The cross context virtual CPU structure.
1292 * @param idxReg The index of the register in the GICR_ICENABLER range.
1293 * @param uValue The value to write to the register.
1294 */
1295static VBOXSTRICTRC gicReDistWriteIntrClearEnableReg(PCGICDEV pGicDev, PVMCPUCC pVCpu, uint16_t idxReg, uint32_t uValue)
1296{
1297 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
1298 Assert(idxReg < RT_ELEMENTS(pGicCpu->bmIntrEnabled));
1299 pGicCpu->bmIntrEnabled[idxReg] &= ~uValue;
1300 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicCpu->bmIntrEnabled[idxReg]));
1301 return gicReDistUpdateIrqState(pGicDev, pVCpu);
1302}
1303
1304
1305/**
1306 * Reads the redistributor's interrupt active register (GICR_ISACTIVER and
1307 * GICR_ICACTIVER).
1308 *
1309 * @returns Strict VBox status code.
1310 * @param pGicCpu The GIC redistributor and CPU interface state.
1311 * @param idxReg The index of the register in the GICR_ISACTIVER and
1312 * GICR_ICACTIVER range.
1313 * @param puValue Where to store the register's value.
1314 */
1315static VBOXSTRICTRC gicReDistReadIntrActiveReg(PGICCPU pGicCpu, uint16_t idxReg, uint32_t *puValue)
1316{
1317 Assert(idxReg < RT_ELEMENTS(pGicCpu->bmIntrActive));
1318 *puValue = pGicCpu->bmIntrActive[idxReg];
1319 LogFlowFunc(("idxReg=%#x read %#x\n", idxReg, pGicCpu->bmIntrActive[idxReg]));
1320 return VINF_SUCCESS;
1321}
1322
1323
1324/**
1325 * Writes the redistributor's interrupt set-active register (GICR_ISACTIVER).
1326 *
1327 * @returns Strict VBox status code.
1328 * @param pGicDev The GIC distributor state.
1329 * @param pVCpu The cross context virtual CPU structure.
1330 * @param idxReg The index of the register in the GICR_ISACTIVER range.
1331 * @param uValue The value to write to the register.
1332 */
1333static VBOXSTRICTRC gicReDistWriteIntrSetActiveReg(PCGICDEV pGicDev, PVMCPUCC pVCpu, uint16_t idxReg, uint32_t uValue)
1334{
1335 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
1336 Assert(idxReg < RT_ELEMENTS(pGicCpu->bmIntrActive));
1337 pGicCpu->bmIntrActive[idxReg] |= uValue;
1338 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicCpu->bmIntrActive[idxReg]));
1339 return gicReDistUpdateIrqState(pGicDev, pVCpu);
1340}
1341
1342
1343/**
1344 * Writes the redistributor's interrupt clear-active register (GICR_ICACTIVER).
1345 *
1346 * @returns Strict VBox status code.
1347 * @param pGicDev The GIC distributor state.
1348 * @param pVCpu The cross context virtual CPU structure.
1349 * @param idxReg The index of the register in the GICR_ICACTIVER range.
1350 * @param uValue The value to write to the register.
1351 */
1352static VBOXSTRICTRC gicReDistWriteIntrClearActiveReg(PCGICDEV pGicDev, PVMCPUCC pVCpu, uint16_t idxReg, uint32_t uValue)
1353{
1354 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
1355 Assert(idxReg < RT_ELEMENTS(pGicCpu->bmIntrActive));
1356 pGicCpu->bmIntrActive[idxReg] &= ~uValue;
1357 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicCpu->bmIntrActive[idxReg]));
1358 return gicReDistUpdateIrqState(pGicDev, pVCpu);
1359}
1360
1361
1362/**
1363 * Reads the redistributor's interrupt config register (GICR_ICFGR).
1364 *
1365 * @returns Strict VBox status code.
1366 * @param pGicDev The GIC distributor state.
1367 * @param pGicCpu The GIC redistributor and CPU interface state.
1368 * @param idxReg The index of the register in the GICR_ICFGR range.
1369 * @param puValue Where to store the register's value.
1370 */
1371static VBOXSTRICTRC gicReDistReadIntrConfigReg(PCGICDEV pGicDev, PGICCPU pGicCpu, uint16_t idxReg, uint32_t *puValue)
1372{
1373 /* When affinity routing is disabled, reads return 0. */
1374 Assert(pGicDev->fAffRoutingEnabled); RT_NOREF(pGicDev);
1375 Assert(idxReg < RT_ELEMENTS(pGicCpu->bmIntrConfig));
1376 *puValue = pGicCpu->bmIntrConfig[idxReg];
1377 /* Ensure SGIs are read-only and remain configured as edge-triggered. */
1378 Assert(idxReg > 0 || *puValue == 0xaaaaaaaa);
1379 LogFlowFunc(("idxReg=%#x read %#x\n", idxReg, *puValue));
1380 return VINF_SUCCESS;
1381}
1382
1383
1384/**
1385 * Writes the redistributor's interrupt config register (GICR_ICFGR).
1386 *
1387 * @returns Strict VBox status code.
1388 * @param pGicDev The GIC distributor state.
1389 * @param pVCpu The cross context virtual CPU structure.
1390 * @param idxReg The index of the register in the GICR_ICFGR range.
1391 * @param uValue The value to write to the register.
1392 */
1393static VBOXSTRICTRC gicReDistWriteIntrConfigReg(PCGICDEV pGicDev, PVMCPUCC pVCpu, uint16_t idxReg, uint32_t uValue)
1394{
1395 /* When affinity routing is disabled, writes are ignored. */
1396 Assert(pGicDev->fAffRoutingEnabled); RT_NOREF(pGicDev);
1397 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
1398 if (idxReg > 0)
1399 {
1400 Assert(idxReg < RT_ELEMENTS(pGicCpu->bmIntrConfig));
1401 pGicCpu->bmIntrConfig[idxReg] = uValue & 0xaaaaaaaa;
1402 }
1403 else
1404 {
1405 /* SGIs are always edge-triggered ignore writes. Windows 11 (24H2) arm64 guests writes these. */
1406 Assert(uValue == 0xaaaaaaaa);
1407 Assert(pGicCpu->bmIntrConfig[0] == uValue);
1408 }
1409 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicCpu->bmIntrConfig[idxReg]));
1410 return VINF_SUCCESS;
1411}
1412
1413
1414/**
1415 * Reads the redistributor's interrupt group register (GICD_IGROUPR).
1416 *
1417 * @returns Strict VBox status code.
1418 * @param pGicDev The GIC distributor state.
1419 * @param pGicCpu The GIC redistributor and CPU interface state.
1420 * @param idxReg The index of the register in the GICR_IGROUPR range.
1421 * @param puValue Where to store the register's value.
1422 */
1423static VBOXSTRICTRC gicReDistReadIntrGroupReg(PCGICDEV pGicDev, PGICCPU pGicCpu, uint16_t idxReg, uint32_t *puValue)
1424{
1425 /* When affinity routing is disabled, reads return 0. */
1426 Assert(pGicDev->fAffRoutingEnabled); RT_NOREF(pGicDev);
1427 Assert(idxReg < RT_ELEMENTS(pGicCpu->bmIntrGroup));
1428 *puValue = pGicCpu->bmIntrGroup[idxReg];
1429 LogFlowFunc(("idxReg=%#x read %#x\n", idxReg, pGicCpu->bmIntrGroup[idxReg]));
1430 return VINF_SUCCESS;
1431}
1432
1433
1434/**
1435 * Writes the redistributor's interrupt group register (GICR_IGROUPR).
1436 *
1437 * @returns Strict VBox status code.
1438 * @param pGicDev The GIC distributor state.
1439 * @param pVCpu The cross context virtual CPU structure.
1440 * @param idxReg The index of the register in the GICR_IGROUPR range.
1441 * @param uValue The value to write to the register.
1442 */
1443static VBOXSTRICTRC gicReDistWriteIntrGroupReg(PCGICDEV pGicDev, PVMCPUCC pVCpu, uint16_t idxReg, uint32_t uValue)
1444{
1445 /* When affinity routing is disabled, writes are ignored. */
1446 Assert(pGicDev->fAffRoutingEnabled);
1447 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
1448 Assert(idxReg < RT_ELEMENTS(pGicCpu->bmIntrGroup));
1449 pGicCpu->bmIntrGroup[idxReg] = uValue;
1450 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicCpu->bmIntrGroup[idxReg]));
1451 return gicReDistUpdateIrqState(pGicDev, pVCpu);
1452}
1453
1454
1455/**
1456 * Gets the virtual CPUID given the affinity values.
1457 *
1458 * @returns The virtual CPUID.
1459 * @param idCpuInterface The virtual CPUID within the PE cluster (0..15).
1460 * @param uAff1 The affinity 1 value.
1461 * @param uAff2 The affinity 2 value.
1462 * @param uAff3 The affinity 3 value.
1463 */
1464DECL_FORCE_INLINE(VMCPUID) gicGetCpuIdFromAffinity(uint8_t idCpuInterface, uint8_t uAff1, uint8_t uAff2, uint8_t uAff3)
1465{
1466 AssertReturn(idCpuInterface < 16, 0);
1467 return (uAff3 * 1048576) + (uAff2 * 4096) + (uAff1 * 16) + idCpuInterface;
1468}
1469
1470
1471/**
1472 * Gets the highest priority pending interrupt that can be signalled to the PE.
1473 *
1474 * @returns The interrupt ID or GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT if no interrupt
1475 * is pending or not in a state to be signalled to the PE.
1476 * @param pGicDev The GIC distributor state.
1477 * @param pGicCpu The GIC redistributor and CPU interface state.
1478 * @param fGroup0 Whether to consider group 0 interrupts.
1479 * @param fGroup1 Whether to consider group 1 interrupts.
1480 * @param pidxIntr Where to store the distributor interrupt index for the
1481 * returned interrupt ID. UINT16_MAX if this function returns
1482 * GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT. Optional, can be
1483 * NULL.
1484 * @param pbPriority Where to store the priority of the returned interrupt ID.
1485 * GIC_IDLE_PRIORITY if this function returns
1486 * GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT.
1487 */
1488static uint16_t gicGetHighestPriorityPendingIntr(PCGICDEV pGicDev, PCGICCPU pGicCpu, bool fGroup0, bool fGroup1,
1489 uint16_t *pidxIntr, uint8_t *pbPriority)
1490{
1491#if 1
1492 uint16_t idxIntr = UINT16_MAX;
1493 uint16_t uIntId = GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT;
1494 uint8_t uPriority = GIC_IDLE_PRIORITY;
1495
1496 /* Redistributor. */
1497 {
1498 uint32_t bmReDistIntrs[RT_ELEMENTS(pGicCpu->bmIntrPending)];
1499 AssertCompile(sizeof(pGicCpu->bmIntrPending) == sizeof(bmReDistIntrs));
1500 for (uint16_t i = 0; i < RT_ELEMENTS(bmReDistIntrs); i++)
1501 {
1502 /* Collect interrupts that are pending, enabled and inactive. */
1503 bmReDistIntrs[i] = (pGicCpu->bmIntrPending[i] & pGicCpu->bmIntrEnabled[i]) & ~pGicCpu->bmIntrActive[i];
1504 /* Discard interrupts if the group they belong to is disabled. */
1505 if (!fGroup1)
1506 bmReDistIntrs[i] &= ~pGicCpu->bmIntrGroup[i];
1507 if (!fGroup0)
1508 bmReDistIntrs[i] &= pGicCpu->bmIntrGroup[i];
1509 }
1510 /* Among the collected interrupts, pick the one with the highest, non-idle priority. */
1511 uint16_t idxHighest = UINT16_MAX;
1512 const void *pvIntrs = &bmReDistIntrs[0];
1513 uint32_t const cIntrs = sizeof(bmReDistIntrs) * 8; AssertCompile(!(cIntrs % 32));
1514 int16_t idxPending = ASMBitFirstSet(pvIntrs, cIntrs);
1515 if (idxPending >= 0)
1516 {
1517 do
1518 {
1519 if (pGicCpu->abIntrPriority[idxPending] < uPriority)
1520 {
1521 idxHighest = (uint16_t)idxPending;
1522 uPriority = pGicCpu->abIntrPriority[idxPending];
1523 }
1524 idxPending = ASMBitNextSet(pvIntrs, cIntrs, idxPending);
1525 } while (idxPending != -1);
1526 if (idxHighest != UINT16_MAX)
1527 {
1528 uIntId = gicReDistGetIntIdFromIndex(idxHighest);
1529 idxIntr = idxHighest;
1530 Assert( GIC_IS_INTR_SGI_OR_PPI(uIntId)
1531 || GIC_IS_INTR_EXT_PPI(uIntId));
1532 }
1533 }
1534 }
1535
1536 /* Distributor */
1537 {
1538 uint32_t bmDistIntrs[RT_ELEMENTS(pGicDev->bmIntrPending)];
1539 AssertCompile(sizeof(pGicDev->bmIntrPending) == sizeof(bmDistIntrs));
1540 for (uint16_t i = 0; i < RT_ELEMENTS(bmDistIntrs); i++)
1541 {
1542 /* Collect interrupts that are pending, enabled and inactive. */
1543 bmDistIntrs[i] = (pGicDev->bmIntrPending[i] & pGicDev->bmIntrEnabled[i]) & ~pGicDev->bmIntrActive[i];
1544 /* Discard interrupts if the group they belong to is disabled. */
1545 if (!fGroup1)
1546 bmDistIntrs[i] &= ~pGicDev->bmIntrGroup[i];
1547 if (!fGroup0)
1548 bmDistIntrs[i] &= pGicDev->bmIntrGroup[i];
1549 }
1550 /* Among the collected interrupts, pick one with priority higher than what we picked from the redistributor. */
1551 {
1552 uint16_t idxHighest = UINT16_MAX;
1553 const void *pvIntrs = &bmDistIntrs[0];
1554 uint32_t const cIntrs = sizeof(bmDistIntrs) * 8; AssertCompile(!(cIntrs % 32));
1555 int16_t idxPending = ASMBitFirstSet(pvIntrs, cIntrs);
1556 if (idxPending >= 0)
1557 {
1558 do
1559 {
1560 if (pGicDev->abIntrPriority[idxPending] < uPriority)
1561 {
1562 idxHighest = (uint16_t)idxPending;
1563 uPriority = pGicDev->abIntrPriority[idxPending];
1564 }
1565 idxPending = ASMBitNextSet(pvIntrs, cIntrs, idxPending);
1566 } while (idxPending != -1);
1567 if (idxHighest != UINT16_MAX)
1568 {
1569 uIntId = gicDistGetIntIdFromIndex(idxHighest);
1570 idxIntr = idxHighest;
1571 Assert( GIC_IS_INTR_SPI(uIntId)
1572 || GIC_IS_INTR_EXT_SPI(uIntId));
1573 }
1574 }
1575 }
1576 }
1577#else /** @todo Measure and pick the faster version. */
1578 /*
1579 * Collect interrupts that are pending, enabled and inactive.
1580 * Discard interrupts if the group they belong to is disabled.
1581 * While collecting the interrupts, pick the one with the highest, non-idle priority.
1582 */
1583 uint16_t uIntId = GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT;
1584 uint16_t idxIntr = UINT16_MAX;
1585 uint8_t uPriority = GIC_IDLE_PRIORITY;
1586
1587 /* Redistributor. */
1588 {
1589 uint16_t idxHighest = UINT16_MAX;
1590 for (uint16_t i = 0; i < RT_ELEMENTS(pGicCpu->bmIntrPending); i++)
1591 {
1592 uint32_t uIntrPending = (pGicCpu->bmIntrPending[i] & pGicCpu->bmIntrEnabled[i]) & ~pGicCpu->bmIntrActive[i];
1593 if (!fGroup1)
1594 uIntrPending &= ~pGicCpu->bmIntrGroup[i];
1595 if (!fGroup0)
1596 uIntrPending &= pGicCpu->bmIntrGroup[i];
1597
1598 uint16_t const idxPending = ASMBitFirstSetU32(uIntrPending);
1599 if (idxPending > 0)
1600 {
1601 uint32_t const idxPriority = 32 * i + idxPending - 1;
1602 Assert(idxPriority < RT_ELEMENTS(pGicCpu->abIntrPriority));
1603 if (pGicCpu->abIntrPriority[idxPriority] < uPriority)
1604 {
1605 idxHighest = idxPriority;
1606 uPriority = pGicCpu->abIntrPriority[idxPriority];
1607 }
1608 }
1609 }
1610 if (idxHighest != UINT16_MAX)
1611 {
1612 idxIntr = idxHighest;
1613 uIntId = gicReDistGetIntIdFromIndex(idxHighest);
1614 Assert( GIC_IS_INTR_SGI_OR_PPI(uIntId)
1615 || GIC_IS_INTR_EXT_PPI(uIntId));
1616 Assert(uPriority != GIC_IDLE_PRIORITY);
1617 }
1618 }
1619
1620 /* Distributor. */
1621 {
1622 uint16_t idxHighest = UINT16_MAX;
1623 for (uint16_t i = 0; i < RT_ELEMENTS(pGicDev->bmIntrPending); i += 2)
1624 {
1625 uint32_t uLo = (pGicDev->bmIntrPending[i] & pGicDev->bmIntrEnabled[i]) & ~pGicDev->bmIntrActive[i];
1626 uint32_t uHi = (pGicDev->bmIntrPending[i + 1] & pGicDev->bmIntrEnabled[i + 1]) & ~pGicDev->bmIntrActive[i + 1];
1627 if (!fGroup1)
1628 {
1629 uLo &= ~pGicDev->bmIntrGroup[i];
1630 uHi &= ~pGicDev->bmIntrGroup[i + 1];
1631 }
1632 if (!fGroup0)
1633 {
1634 uLo &= pGicDev->bmIntrGroup[i];
1635 uHi &= pGicDev->bmIntrGroup[i + 1];
1636 }
1637
1638 uint64_t const uIntrPending = RT_MAKE_U64(uLo, uHi);
1639 uint16_t const idxPending = ASMBitFirstSetU64(uIntrPending);
1640 if (idxPending > 0)
1641 {
1642 uint32_t const idxPriority = 64 * i + idxPending - 1;
1643 if (pGicDev->abIntrPriority[idxPriority] < uPriority)
1644 {
1645 idxHighest = idxPriority;
1646 uPriority = pGicDev->abIntrPriority[idxPriority];
1647 }
1648 }
1649 }
1650 if (idxHighest != UINT16_MAX)
1651 {
1652 idxIntr = idxHighest;
1653 uIntId = gicDistGetIntIdFromIndex(idxHighest);
1654 Assert( GIC_IS_INTR_SPI(uIntId)
1655 || GIC_IS_INTR_EXT_SPI(uIntId));
1656 Assert(uPriority != GIC_IDLE_PRIORITY);
1657 }
1658 }
1659#endif
1660
1661 /* Ensure that if no interrupt is pending, the idle priority is returned. */
1662 Assert(uIntId != GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT || uPriority == GIC_IDLE_PRIORITY);
1663 if (pbPriority)
1664 *pbPriority = uPriority;
1665 if (pidxIntr)
1666 *pidxIntr = idxIntr;
1667
1668 LogFlowFunc(("uIntId=%u [idxIntr=%u uPriority=%u]\n", uIntId, idxIntr, uPriority));
1669 return uIntId;
1670}
1671
1672
1673/**
1674 * Get and acknowledge the interrupt ID of a signalled interrupt.
1675 *
1676 * @returns The interrupt ID or GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT no interrupts
1677 * are pending or not in a state to be signalled.
1678 * @param pGicDev The GIC distributor state.
1679 * @param pVCpu The cross context virtual CPU structure.
1680 * @param fGroup0 Whether to consider group 0 interrupts.
1681 * @param fGroup1 Whether to consider group 1 interrupts.
1682 */
1683static uint16_t gicAckHighestPriorityPendingIntr(PGICDEV pGicDev, PVMCPUCC pVCpu, bool fGroup0, bool fGroup1)
1684{
1685 Assert(fGroup0 || fGroup1);
1686 LogFlowFunc(("fGroup0=%RTbool fGroup1=%RTbool\n", fGroup0, fGroup1));
1687
1688 /*
1689 * Get the pending interrupt with the highest priority for the given group.
1690 */
1691 uint8_t bIntrPriority;
1692 uint16_t idxIntr;
1693 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
1694 STAM_PROFILE_START(&pGicCpu->CTX_SUFF_Z(StatProfIntrAck), x);
1695 uint16_t const uIntId = gicGetHighestPriorityPendingIntr(pGicDev, pGicCpu, fGroup0, fGroup1, &idxIntr, &bIntrPriority);
1696 if (uIntId != GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT)
1697 {
1698 /*
1699 * The interrupt priority must be higher than the priority mask of the CPU interface for the
1700 * interrupt to be signalled/acknowledged. Here, we must NOT use priority grouping when comparing
1701 * the priority of a pending interrupt with this priority mask (threshold).
1702 *
1703 * See ARM GIC spec. 4.8.6 "Priority masking".
1704 */
1705 if (bIntrPriority >= pGicCpu->bIntrPriorityMask)
1706 {
1707 STAM_PROFILE_STOP(&pGicCpu->CTX_SUFF_Z(StatProfIntrAck), x);
1708 return GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT;
1709 }
1710
1711 /*
1712 * The group priority of the pending interrupt must be higher than that of the running priority.
1713 * The number of bits for the group priority depends on the the binary point registers.
1714 * We mask the sub-priority bits and only compare the group priority.
1715 *
1716 * When the binary point registers indicates no preemption, we must allow interrupts that have
1717 * a higher priority than idle. Hence, the use of two different masks below.
1718 *
1719 * See ARM GIC spec. 4.8.3 "Priority grouping".
1720 * See ARM GIC spec. 4.8.5 "Preemption".
1721 */
1722 static uint8_t const s_afGroupPriorityMasks[8] = { 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00 };
1723 static uint8_t const s_afRunningPriorityMasks[8] = { 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0xff };
1724 uint8_t const idxPriorityMask = (fGroup0 || (pGicCpu->uIccCtlr & ARMV8_ICC_CTLR_EL1_AARCH64_CBPR))
1725 ? pGicCpu->bBinaryPtGroup0 & 7
1726 : pGicCpu->bBinaryPtGroup1 & 7;
1727 uint8_t const bRunningPriority = pGicCpu->abRunningPriorities[pGicCpu->idxRunningPriority];
1728 uint8_t const bRunningGroupPriority = bRunningPriority & s_afRunningPriorityMasks[idxPriorityMask];
1729 uint8_t const bIntrGroupPriority = bIntrPriority & s_afGroupPriorityMasks[idxPriorityMask];
1730 if (bIntrGroupPriority >= bRunningGroupPriority)
1731 {
1732 STAM_PROFILE_STOP(&pGicCpu->CTX_SUFF_Z(StatProfIntrAck), x);
1733 return GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT;
1734 }
1735
1736 /*
1737 * Acknowledge the interrupt.
1738 */
1739 bool const fIsRedistIntId = GIC_IS_INTR_SGI_OR_PPI(uIntId) || GIC_IS_INTR_EXT_PPI(uIntId);
1740 if (fIsRedistIntId)
1741 {
1742 /* Mark the interrupt as active. */
1743 AssertMsg(idxIntr < sizeof(pGicCpu->bmIntrActive) * 8, ("idxIntr=%u\n", idxIntr));
1744 ASMBitSet(&pGicCpu->bmIntrActive[0], idxIntr);
1745
1746 /** @todo Duplicate block Id=E5ED12D2-088D-4525-9609-8325C02846C3 (start). */
1747 /* Update the active priorities bitmap. */
1748 AssertCompile(sizeof(pGicCpu->bmActivePriorityGroup0) * 8 >= 128);
1749 AssertCompile(sizeof(pGicCpu->bmActivePriorityGroup1) * 8 >= 128);
1750 uint8_t const idxPreemptionLevel = bIntrPriority >> 1;
1751 if (fGroup0)
1752 ASMBitSet(&pGicCpu->bmActivePriorityGroup0[0], idxPreemptionLevel);
1753 if (fGroup1)
1754 ASMBitSet(&pGicCpu->bmActivePriorityGroup1[0], idxPreemptionLevel);
1755
1756 /* Drop priority. */
1757 if (RT_LIKELY(pGicCpu->idxRunningPriority < RT_ELEMENTS(pGicCpu->abRunningPriorities) - 1))
1758 {
1759 LogFlowFunc(("Dropping interrupt priority from %u -> %u (idxRunningPriority: %u -> %u)\n",
1760 pGicCpu->abRunningPriorities[pGicCpu->idxRunningPriority],
1761 bIntrPriority,
1762 pGicCpu->idxRunningPriority, pGicCpu->idxRunningPriority + 1));
1763 ++pGicCpu->idxRunningPriority;
1764 pGicCpu->abRunningPriorities[pGicCpu->idxRunningPriority] = bIntrPriority;
1765 }
1766 else
1767 AssertReleaseMsgFailed(("Index of running-interrupt priority out-of-bounds %u\n", pGicCpu->idxRunningPriority));
1768 /** @todo Duplicate block Id=E5ED12D2-088D-4525-9609-8325C02846C3 (end). */
1769
1770 /* If it is an edge-triggered interrupt, mark it as no longer pending. */
1771 AssertRelease(UINT32_C(2) * idxIntr + 1 < sizeof(pGicCpu->bmIntrConfig) * 8);
1772 bool const fEdgeTriggered = ASMBitTest(&pGicCpu->bmIntrConfig[0], 2 * idxIntr + 1);
1773 if (fEdgeTriggered)
1774 ASMBitClear(&pGicCpu->bmIntrPending[0], idxIntr);
1775
1776 /* Update the redistributor IRQ state to reflect change to the active interrupt. */
1777 gicReDistUpdateIrqState(pGicDev, pVCpu);
1778 }
1779 else
1780 {
1781 /* Sanity check if the interrupt ID belongs to the distributor. */
1782 Assert(GIC_IS_INTR_SPI(uIntId) || GIC_IS_INTR_EXT_SPI(uIntId));
1783
1784 /* Mark the interrupt as active. */
1785 Assert(idxIntr < sizeof(pGicDev->bmIntrActive) * 8);
1786 ASMBitSet(&pGicDev->bmIntrActive[0], idxIntr);
1787
1788 /** @todo Duplicate block Id=E5ED12D2-088D-4525-9609-8325C02846C3 (start). */
1789 /* Update the active priorities bitmap. */
1790 AssertCompile(sizeof(pGicCpu->bmActivePriorityGroup0) * 8 >= 128);
1791 AssertCompile(sizeof(pGicCpu->bmActivePriorityGroup1) * 8 >= 128);
1792 uint8_t const idxPreemptionLevel = bIntrPriority >> 1;
1793 if (fGroup0)
1794 ASMBitSet(&pGicCpu->bmActivePriorityGroup0[0], idxPreemptionLevel);
1795 if (fGroup1)
1796 ASMBitSet(&pGicCpu->bmActivePriorityGroup1[0], idxPreemptionLevel);
1797
1798 /* Drop priority. */
1799 if (RT_LIKELY(pGicCpu->idxRunningPriority < RT_ELEMENTS(pGicCpu->abRunningPriorities) - 1))
1800 {
1801 LogFlowFunc(("Dropping interrupt priority from %u -> %u (idxRunningPriority: %u -> %u)\n",
1802 pGicCpu->abRunningPriorities[pGicCpu->idxRunningPriority],
1803 bIntrPriority,
1804 pGicCpu->idxRunningPriority, pGicCpu->idxRunningPriority + 1));
1805 ++pGicCpu->idxRunningPriority;
1806 pGicCpu->abRunningPriorities[pGicCpu->idxRunningPriority] = bIntrPriority;
1807 }
1808 else
1809 AssertReleaseMsgFailed(("Index of running-interrupt priority out-of-bounds %u\n", pGicCpu->idxRunningPriority));
1810 /** @todo Duplicate block Id=E5ED12D2-088D-4525-9609-8325C02846C3 (end). */
1811
1812 /* If it is an edge-triggered interrupt, mark it as no longer pending. */
1813 AssertRelease(UINT32_C(2) * idxIntr + 1 < sizeof(pGicDev->bmIntrConfig) * 8);
1814 bool const fEdgeTriggered = ASMBitTest(&pGicDev->bmIntrConfig[0], 2 * idxIntr + 1);
1815 if (fEdgeTriggered)
1816 ASMBitClear(&pGicDev->bmIntrPending[0], idxIntr);
1817
1818 /* Update the distributor IRQ state to reflect change to the active interrupt. */
1819 gicDistUpdateIrqState(pVCpu->CTX_SUFF(pVM), pGicDev);
1820 }
1821 }
1822 else
1823 Assert(bIntrPriority == GIC_IDLE_PRIORITY);
1824
1825 LogFlowFunc(("uIntId=%u\n", uIntId));
1826 STAM_PROFILE_STOP(&pGicCpu->CTX_SUFF_Z(StatProfIntrAck), x);
1827 return uIntId;
1828}
1829
1830
1831/**
1832 * Reads a distributor register.
1833 *
1834 * @returns VBox status code.
1835 * @param pDevIns The device instance.
1836 * @param pVCpu The cross context virtual CPU structure.
1837 * @param offReg The offset of the register being read.
1838 * @param puValue Where to store the register value.
1839 */
1840DECLINLINE(VBOXSTRICTRC) gicDistReadRegister(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t *puValue)
1841{
1842 VMCPU_ASSERT_EMT(pVCpu); RT_NOREF(pVCpu);
1843 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
1844
1845 /*
1846 * 64-bit registers.
1847 */
1848 {
1849 /*
1850 * GICD_IROUTER<n> and GICD_IROUTER<n>E.
1851 */
1852 uint16_t const cbReg = sizeof(uint64_t);
1853 if ((uint32_t)offReg - GIC_DIST_REG_IROUTERn_OFF_START < GIC_DIST_REG_IROUTERn_RANGE_SIZE)
1854 {
1855 /* Hardware does not map the first 32 registers (corresponding to SGIs and PPIs). */
1856 uint16_t const idxExt = GIC_INTID_RANGE_SPI_START;
1857 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_IROUTERn_OFF_START) / cbReg;
1858 return gicDistReadIntrRoutingReg(pGicDev, idxReg, puValue);
1859 }
1860 if ((uint32_t)offReg - GIC_DIST_REG_IROUTERnE_OFF_START < GIC_DIST_REG_IROUTERnE_RANGE_SIZE)
1861 {
1862 uint16_t const idxExt = RT_ELEMENTS(pGicDev->au32IntrRouting) / 2;
1863 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_IROUTERnE_OFF_START) / cbReg;
1864 return gicDistReadIntrRoutingReg(pGicDev, idxReg, puValue);
1865 }
1866 }
1867
1868 /*
1869 * 32-bit registers.
1870 */
1871 {
1872 /*
1873 * GICD_IGROUPR<n> and GICD_IGROUPR<n>E.
1874 */
1875 uint16_t const cbReg = sizeof(uint32_t);
1876 if ((uint32_t)offReg - GIC_DIST_REG_IGROUPRn_OFF_START < GIC_DIST_REG_IGROUPRn_RANGE_SIZE)
1877 {
1878 uint16_t const idxReg = (offReg - GIC_DIST_REG_IGROUPRn_OFF_START) / cbReg;
1879 return gicDistReadIntrGroupReg(pGicDev, idxReg, puValue);
1880 }
1881 if ((uint32_t)offReg - GIC_DIST_REG_IGROUPRnE_OFF_START < GIC_DIST_REG_IGROUPRnE_RANGE_SIZE)
1882 {
1883 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrGroup) / 2;
1884 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_IGROUPRnE_OFF_START) / cbReg;
1885 return gicDistReadIntrGroupReg(pGicDev, idxReg, puValue);
1886 }
1887
1888 /*
1889 * GICD_ISENABLER<n> and GICD_ISENABLER<n>E.
1890 * GICD_ICENABLER<n> and GICD_ICENABLER<n>E.
1891 */
1892 if ((uint32_t)offReg - GIC_DIST_REG_ISENABLERn_OFF_START < GIC_DIST_REG_ISENABLERn_RANGE_SIZE)
1893 {
1894 uint16_t const idxReg = (offReg - GIC_DIST_REG_ISENABLERn_OFF_START) / cbReg;
1895 return gicDistReadIntrEnableReg(pGicDev, idxReg, puValue);
1896 }
1897 if ((uint32_t)offReg - GIC_DIST_REG_ISENABLERnE_OFF_START < GIC_DIST_REG_ISENABLERnE_RANGE_SIZE)
1898 {
1899 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrEnabled) / 2;
1900 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ISENABLERnE_OFF_START) / cbReg;
1901 return gicDistReadIntrEnableReg(pGicDev, idxReg, puValue);
1902 }
1903 if ((uint32_t)offReg - GIC_DIST_REG_ICENABLERn_OFF_START < GIC_DIST_REG_ICENABLERn_RANGE_SIZE)
1904 {
1905 uint16_t const idxReg = (offReg - GIC_DIST_REG_ICENABLERn_OFF_START) / cbReg;
1906 return gicDistReadIntrEnableReg(pGicDev, idxReg, puValue);
1907 }
1908 if ((uint32_t)offReg - GIC_DIST_REG_ICENABLERnE_OFF_START < GIC_DIST_REG_ICENABLERnE_RANGE_SIZE)
1909 {
1910 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrEnabled) / 2;
1911 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ICENABLERnE_OFF_START) / cbReg;
1912 return gicDistReadIntrEnableReg(pGicDev, idxReg, puValue);
1913 }
1914
1915 /*
1916 * GICD_ISACTIVER<n> and GICD_ISACTIVER<n>E.
1917 * GICD_ICACTIVER<n> and GICD_ICACTIVER<n>E.
1918 */
1919 if ((uint32_t)offReg - GIC_DIST_REG_ISACTIVERn_OFF_START < GIC_DIST_REG_ISACTIVERn_RANGE_SIZE)
1920 {
1921 uint16_t const idxReg = (offReg - GIC_DIST_REG_ISACTIVERn_OFF_START) / cbReg;
1922 return gicDistReadIntrActiveReg(pGicDev, idxReg, puValue);
1923 }
1924 if ((uint32_t)offReg - GIC_DIST_REG_ISACTIVERnE_OFF_START < GIC_DIST_REG_ISACTIVERnE_RANGE_SIZE)
1925 {
1926 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrActive) / 2;
1927 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ISACTIVERnE_OFF_START) / cbReg;
1928 return gicDistReadIntrActiveReg(pGicDev, idxReg, puValue);
1929 }
1930 if ((uint32_t)offReg - GIC_DIST_REG_ICACTIVERn_OFF_START < GIC_DIST_REG_ICACTIVERn_RANGE_SIZE)
1931 {
1932 uint16_t const idxReg = (offReg - GIC_DIST_REG_ICENABLERn_OFF_START) / cbReg;
1933 return gicDistReadIntrActiveReg(pGicDev, idxReg, puValue);
1934 }
1935 if ((uint32_t)offReg - GIC_DIST_REG_ICACTIVERnE_OFF_START < GIC_DIST_REG_ICACTIVERnE_RANGE_SIZE)
1936 {
1937 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrActive) / 2;
1938 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ICACTIVERnE_OFF_START) / cbReg;
1939 return gicDistReadIntrActiveReg(pGicDev, idxReg, puValue);
1940 }
1941
1942 /*
1943 * GICD_IPRIORITYR<n> and GICD_IPRIORITYR<n>E.
1944 */
1945 if ((uint32_t)offReg - GIC_DIST_REG_IPRIORITYRn_OFF_START < GIC_DIST_REG_IPRIORITYRn_RANGE_SIZE)
1946 {
1947 uint16_t const idxReg = (offReg - GIC_DIST_REG_IPRIORITYRn_OFF_START) / cbReg;
1948 return gicDistReadIntrPriorityReg(pGicDev, idxReg, puValue);
1949 }
1950 if ((uint32_t)offReg - GIC_DIST_REG_IPRIORITYRnE_OFF_START < GIC_DIST_REG_IPRIORITYRnE_RANGE_SIZE)
1951 {
1952 uint16_t const idxExt = RT_ELEMENTS(pGicDev->abIntrPriority) / (2 * sizeof(uint32_t));
1953 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_IPRIORITYRnE_OFF_START) / cbReg;
1954 return gicDistReadIntrPriorityReg(pGicDev, idxReg, puValue);
1955 }
1956
1957 /*
1958 * GICD_ISPENDR<n> and GICD_ISPENDR<n>E.
1959 * GICD_ICPENDR<n> and GICD_ICPENDR<n>E.
1960 */
1961 if ((uint32_t)offReg - GIC_DIST_REG_ISPENDRn_OFF_START < GIC_DIST_REG_ISPENDRn_RANGE_SIZE)
1962 {
1963 uint16_t const idxReg = (offReg - GIC_DIST_REG_ISPENDRn_OFF_START) / cbReg;
1964 return gicDistReadIntrPendingReg(pGicDev, idxReg, puValue);
1965 }
1966 if ((uint32_t)offReg - GIC_DIST_REG_ISPENDRnE_OFF_START < GIC_DIST_REG_ISPENDRnE_RANGE_SIZE)
1967 {
1968 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrPending) / 2;
1969 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ISPENDRnE_OFF_START) / cbReg;
1970 return gicDistReadIntrPendingReg(pGicDev, idxReg, puValue);
1971 }
1972 if ((uint32_t)offReg - GIC_DIST_REG_ICPENDRn_OFF_START < GIC_DIST_REG_ICPENDRn_RANGE_SIZE)
1973 {
1974 uint16_t const idxReg = (offReg - GIC_DIST_REG_ICPENDRn_OFF_START) / cbReg;
1975 return gicDistReadIntrPendingReg(pGicDev, idxReg, puValue);
1976 }
1977 if ((uint32_t)offReg - GIC_DIST_REG_ICPENDRnE_OFF_START < GIC_DIST_REG_ICPENDRnE_RANGE_SIZE)
1978 {
1979 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrPending) / 2;
1980 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ICPENDRnE_OFF_START) / cbReg;
1981 return gicDistReadIntrPendingReg(pGicDev, idxReg, puValue);
1982 }
1983
1984 /*
1985 * GICD_ICFGR<n> and GICD_ICFGR<n>E.
1986 */
1987 if ((uint32_t)offReg - GIC_DIST_REG_ICFGRn_OFF_START < GIC_DIST_REG_ICFGRn_RANGE_SIZE)
1988 {
1989 uint16_t const idxReg = (offReg - GIC_DIST_REG_ICFGRn_OFF_START) / cbReg;
1990 return gicDistReadIntrConfigReg(pGicDev, idxReg, puValue);
1991 }
1992 if ((uint32_t)offReg - GIC_DIST_REG_ICFGRnE_OFF_START < GIC_DIST_REG_ICFGRnE_RANGE_SIZE)
1993 {
1994 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrConfig) / 2;
1995 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ICFGRnE_OFF_START) / cbReg;
1996 return gicDistReadIntrConfigReg(pGicDev, idxReg, puValue);
1997 }
1998 }
1999
2000 switch (offReg)
2001 {
2002 case GIC_DIST_REG_CTLR_OFF:
2003 Assert(pGicDev->fAffRoutingEnabled);
2004 *puValue = (pGicDev->fIntrGroup0Enabled ? GIC_DIST_REG_CTRL_ENABLE_GRP0 : 0)
2005 | (pGicDev->fIntrGroup1Enabled ? GIC_DIST_REG_CTRL_ENABLE_GRP1_NS : 0)
2006 | GIC_DIST_REG_CTRL_DS /* We don't support multiple security states. */
2007 | GIC_DIST_REG_CTRL_ARE_S; /* We don't support GICv2 backwards compatibility, ARE is always enabled. */
2008 break;
2009 case GIC_DIST_REG_TYPER_OFF:
2010 {
2011 Assert(pGicDev->uMaxSpi > 0 && pGicDev->uMaxSpi <= GIC_DIST_REG_TYPER_NUM_ITLINES);
2012 Assert(pGicDev->fAffRoutingEnabled);
2013 *puValue = GIC_DIST_REG_TYPER_NUM_ITLINES_SET(pGicDev->uMaxSpi)
2014 | GIC_DIST_REG_TYPER_NUM_PES_SET(0) /* Affinity routing is always enabled, hence this MBZ. */
2015 /*| GIC_DIST_REG_TYPER_NMI*/ /** @todo Support non-maskable interrupts */
2016 /*| GIC_DIST_REG_TYPER_SECURITY_EXTN*/ /** @todo Support dual security states. */
2017 | (pGicDev->fMbi ? GIC_DIST_REG_TYPER_MBIS : 0)
2018 | (pGicDev->fRangeSel ? GIC_DIST_REG_TYPER_RSS : 0)
2019 | GIC_DIST_REG_TYPER_IDBITS_SET(16) /* We only support 16-bit interrupt IDs. */
2020 | (pGicDev->fAff3Levels ? GIC_DIST_REG_TYPER_A3V : 0);
2021 if (pGicDev->fExtSpi)
2022 *puValue |= GIC_DIST_REG_TYPER_ESPI
2023 | GIC_DIST_REG_TYPER_ESPI_RANGE_SET(pGicDev->uMaxExtSpi);
2024 if (pGicDev->fLpi)
2025 {
2026 Assert(pGicDev->uMaxLpi - 2 < 13);
2027 Assert(GIC_INTID_RANGE_LPI_START + (UINT32_C(2) << pGicDev->uMaxLpi) <= UINT16_MAX);
2028 *puValue |= GIC_DIST_REG_TYPER_LPIS
2029 | GIC_DIST_REG_TYPER_NUM_LPIS_SET(pGicDev->uMaxLpi);
2030 }
2031 break;
2032 }
2033 case GIC_DIST_REG_STATUSR_OFF:
2034 AssertReleaseFailed();
2035 break;
2036 case GIC_DIST_REG_ITARGETSRn_OFF_START:
2037 AssertReleaseFailed();
2038 break;
2039 case GIC_DIST_REG_IGRPMODRn_OFF_START:
2040 AssertReleaseFailed();
2041 break;
2042 case GIC_DIST_REG_NSACRn_OFF_START:
2043 AssertReleaseFailed();
2044 break;
2045 case GIC_DIST_REG_SGIR_OFF:
2046 AssertReleaseFailed();
2047 break;
2048 case GIC_DIST_REG_CPENDSGIRn_OFF_START:
2049 AssertReleaseFailed();
2050 break;
2051 case GIC_DIST_REG_SPENDSGIRn_OFF_START:
2052 AssertReleaseFailed();
2053 break;
2054 case GIC_DIST_REG_INMIn_OFF_START:
2055 AssertReleaseFailed();
2056 break;
2057 case GIC_DIST_REG_PIDR2_OFF:
2058 Assert(pGicDev->uArchRev <= GIC_DIST_REG_PIDR2_ARCH_REV_GICV4);
2059 *puValue = GIC_DIST_REG_PIDR2_ARCH_REV_SET(pGicDev->uArchRev);
2060 break;
2061 case GIC_DIST_REG_IIDR_OFF:
2062 *puValue = 0x43b; /* JEP106 code 0x43b is an ARM implementation. */
2063 break;
2064 case GIC_DIST_REG_TYPER2_OFF:
2065 *puValue = 0;
2066 break;
2067 default:
2068 *puValue = 0;
2069 }
2070 return VINF_SUCCESS;
2071}
2072
2073
2074/**
2075 * Writes a distributor register.
2076 *
2077 * @returns Strict VBox status code.
2078 * @param pDevIns The device instance.
2079 * @param pVCpu The cross context virtual CPU structure.
2080 * @param offReg The offset of the register being written.
2081 * @param uValue The register value.
2082 */
2083DECLINLINE(VBOXSTRICTRC) gicDistWriteRegister(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t uValue)
2084{
2085 VMCPU_ASSERT_EMT(pVCpu); RT_NOREF(pVCpu);
2086 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
2087 PVMCC pVM = PDMDevHlpGetVM(pDevIns);
2088
2089 /*
2090 * 64-bit registers.
2091 */
2092 {
2093 /*
2094 * GICD_IROUTER<n> and GICD_IROUTER<n>E.
2095 */
2096 uint16_t const cbReg = sizeof(uint64_t);
2097 if ((uint32_t)offReg - GIC_DIST_REG_IROUTERn_OFF_START < GIC_DIST_REG_IROUTERn_RANGE_SIZE)
2098 {
2099 /* Hardware does not map the first 32 registers (corresponding to SGIs and PPIs). */
2100 uint16_t const idxExt = GIC_INTID_RANGE_SPI_START;
2101 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_IROUTERn_OFF_START) / cbReg;
2102 return gicDistWriteIntrRoutingReg(pGicDev, idxReg, uValue);
2103 }
2104 if ((uint32_t)offReg - GIC_DIST_REG_IROUTERnE_OFF_START < GIC_DIST_REG_IROUTERnE_RANGE_SIZE)
2105 {
2106 uint16_t const idxExt = RT_ELEMENTS(pGicDev->au32IntrRouting) / 2;
2107 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_IROUTERnE_OFF_START) / cbReg;
2108 return gicDistWriteIntrRoutingReg(pGicDev, idxReg, uValue);
2109 }
2110
2111 }
2112
2113 /*
2114 * 32-bit registers.
2115 */
2116 {
2117 /*
2118 * GICD_IGROUPR<n> and GICD_IGROUPR<n>E.
2119 */
2120 uint16_t const cbReg = sizeof(uint32_t);
2121 if ((uint32_t)offReg - GIC_DIST_REG_IGROUPRn_OFF_START < GIC_DIST_REG_IGROUPRn_RANGE_SIZE)
2122 {
2123 uint16_t const idxReg = (offReg - GIC_DIST_REG_IGROUPRn_OFF_START) / cbReg;
2124 return gicDistWriteIntrGroupReg(pVM, pGicDev, idxReg, uValue);
2125 }
2126 if ((uint32_t)offReg - GIC_DIST_REG_IGROUPRnE_OFF_START < GIC_DIST_REG_IGROUPRnE_RANGE_SIZE)
2127 {
2128 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrGroup) / 2;
2129 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_IGROUPRnE_OFF_START) / cbReg;
2130 return gicDistWriteIntrGroupReg(pVM, pGicDev, idxReg, uValue);
2131 }
2132
2133 /*
2134 * GICD_ISENABLER<n> and GICD_ISENABLER<n>E.
2135 * GICD_ICENABLER<n> and GICD_ICENABLER<n>E.
2136 */
2137 if ((uint32_t)offReg - GIC_DIST_REG_ISENABLERn_OFF_START < GIC_DIST_REG_ISENABLERn_RANGE_SIZE)
2138 {
2139 uint16_t const idxReg = (offReg - GIC_DIST_REG_ISENABLERn_OFF_START) / cbReg;
2140 return gicDistWriteIntrSetEnableReg(pVM, pGicDev, idxReg, uValue);
2141 }
2142 if ((uint32_t)offReg - GIC_DIST_REG_ISENABLERnE_OFF_START < GIC_DIST_REG_ISENABLERnE_RANGE_SIZE)
2143 {
2144 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrEnabled) / 2;
2145 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ISENABLERnE_OFF_START) / cbReg;
2146 return gicDistWriteIntrSetEnableReg(pVM, pGicDev, idxReg, uValue);
2147 }
2148 if ((uint32_t)offReg - GIC_DIST_REG_ICENABLERn_OFF_START < GIC_DIST_REG_ICENABLERn_RANGE_SIZE)
2149 {
2150 uint16_t const idxReg = (offReg - GIC_DIST_REG_ICENABLERn_OFF_START) / cbReg;
2151 return gicDistWriteIntrClearEnableReg(pVM, pGicDev, idxReg, uValue);
2152 }
2153 if ((uint32_t)offReg - GIC_DIST_REG_ICENABLERnE_OFF_START < GIC_DIST_REG_ICENABLERnE_RANGE_SIZE)
2154 {
2155 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrEnabled) / 2;
2156 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ICENABLERnE_OFF_START) / cbReg;
2157 return gicDistWriteIntrClearEnableReg(pVM, pGicDev, idxReg, uValue);
2158 }
2159
2160 /*
2161 * GICD_ISACTIVER<n> and GICD_ISACTIVER<n>E.
2162 * GICD_ICACTIVER<n> and GICD_ICACTIVER<n>E.
2163 */
2164 if ((uint32_t)offReg - GIC_DIST_REG_ISACTIVERn_OFF_START < GIC_DIST_REG_ISACTIVERn_RANGE_SIZE)
2165 {
2166 uint16_t const idxReg = (offReg - GIC_DIST_REG_ISACTIVERn_OFF_START) / cbReg;
2167 return gicDistWriteIntrSetActiveReg(pVM, pGicDev, idxReg, uValue);
2168 }
2169 if ((uint32_t)offReg - GIC_DIST_REG_ISACTIVERnE_OFF_START < GIC_DIST_REG_ISACTIVERnE_RANGE_SIZE)
2170 {
2171 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrActive) / 2;
2172 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ISACTIVERnE_OFF_START) / cbReg;
2173 return gicDistWriteIntrSetActiveReg(pVM, pGicDev, idxReg, uValue);
2174 }
2175 if ((uint32_t)offReg - GIC_DIST_REG_ICACTIVERn_OFF_START < GIC_DIST_REG_ICACTIVERn_RANGE_SIZE)
2176 {
2177 uint16_t const idxReg = (offReg - GIC_DIST_REG_ICACTIVERn_OFF_START) / cbReg;
2178 return gicDistWriteIntrClearActiveReg(pVM, pGicDev, idxReg, uValue);
2179 }
2180 if ((uint32_t)offReg - GIC_DIST_REG_ICACTIVERnE_OFF_START < GIC_DIST_REG_ICACTIVERnE_RANGE_SIZE)
2181 {
2182 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrActive) / 2;
2183 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ICACTIVERnE_OFF_START) / cbReg;
2184 return gicDistWriteIntrClearActiveReg(pVM, pGicDev, idxReg, uValue);
2185 }
2186
2187 /*
2188 * GICD_IPRIORITYR<n> and GICD_IPRIORITYR<n>E.
2189 */
2190 if ((uint32_t)offReg - GIC_DIST_REG_IPRIORITYRn_OFF_START < GIC_DIST_REG_IPRIORITYRn_RANGE_SIZE)
2191 {
2192 uint16_t const idxReg = (offReg - GIC_DIST_REG_IPRIORITYRn_OFF_START) / cbReg;
2193 return gicDistWriteIntrPriorityReg(pGicDev, idxReg, uValue);
2194 }
2195 if ((uint32_t)offReg - GIC_DIST_REG_IPRIORITYRnE_OFF_START < GIC_DIST_REG_IPRIORITYRnE_RANGE_SIZE)
2196 {
2197 uint16_t const idxExt = RT_ELEMENTS(pGicDev->abIntrPriority) / (2 * sizeof(uint32_t));
2198 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_IPRIORITYRnE_OFF_START) / cbReg;
2199 return gicDistWriteIntrPriorityReg(pGicDev, idxReg, uValue);
2200 }
2201
2202 /*
2203 * GICD_ISPENDR<n> and GICD_ISPENDR<n>E.
2204 * GICD_ICPENDR<n> and GICD_ICPENDR<n>E.
2205 */
2206 if ((uint32_t)offReg - GIC_DIST_REG_ISPENDRn_OFF_START < GIC_DIST_REG_ISPENDRn_RANGE_SIZE)
2207 {
2208 uint16_t const idxReg = (offReg - GIC_DIST_REG_ISPENDRn_OFF_START) / cbReg;
2209 return gicDistWriteIntrSetPendingReg(pVM, pGicDev, idxReg, uValue);
2210 }
2211 if ((uint32_t)offReg - GIC_DIST_REG_ISPENDRnE_OFF_START < GIC_DIST_REG_ISPENDRnE_RANGE_SIZE)
2212 {
2213 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrPending) / 2;
2214 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ISPENDRnE_OFF_START) / cbReg;
2215 return gicDistWriteIntrSetPendingReg(pVM, pGicDev, idxReg, uValue);
2216 }
2217 if ((uint32_t)offReg - GIC_DIST_REG_ICPENDRn_OFF_START < GIC_DIST_REG_ICPENDRn_RANGE_SIZE)
2218 {
2219 uint16_t const idxReg = (offReg - GIC_DIST_REG_ICPENDRn_OFF_START) / cbReg;
2220 return gicDistWriteIntrClearPendingReg(pVM, pGicDev, idxReg, uValue);
2221 }
2222 if ((uint32_t)offReg - GIC_DIST_REG_ICPENDRnE_OFF_START < GIC_DIST_REG_ICPENDRnE_RANGE_SIZE)
2223 {
2224 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrPending) / 2;
2225 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ICPENDRnE_OFF_START) / cbReg;
2226 return gicDistWriteIntrClearPendingReg(pVM, pGicDev, idxReg, uValue);
2227 }
2228
2229 /*
2230 * GICD_ICFGR<n> and GICD_ICFGR<n>E.
2231 */
2232 if ((uint32_t)offReg - GIC_DIST_REG_ICFGRn_OFF_START < GIC_DIST_REG_ICFGRn_RANGE_SIZE)
2233 {
2234 uint16_t const idxReg = (offReg - GIC_DIST_REG_ICFGRn_OFF_START) / cbReg;
2235 return gicDistWriteIntrConfigReg(pGicDev, idxReg, uValue);
2236 }
2237 if ((uint32_t)offReg - GIC_DIST_REG_ICFGRnE_OFF_START < GIC_DIST_REG_ICFGRnE_RANGE_SIZE)
2238 {
2239 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrConfig) / 2;
2240 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ICFGRnE_OFF_START) / cbReg;
2241 return gicDistWriteIntrConfigReg(pGicDev, idxReg, uValue);
2242 }
2243 }
2244
2245 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
2246 switch (offReg)
2247 {
2248 case GIC_DIST_REG_CTLR_OFF:
2249 Assert(!(uValue & GIC_DIST_REG_CTRL_ARE_NS));
2250 pGicDev->fIntrGroup0Enabled = RT_BOOL(uValue & GIC_DIST_REG_CTRL_ENABLE_GRP0);
2251 pGicDev->fIntrGroup1Enabled = RT_BOOL(uValue & GIC_DIST_REG_CTRL_ENABLE_GRP1_NS);
2252 rcStrict = gicDistUpdateIrqState(pVM, pGicDev);
2253 break;
2254 case GIC_DIST_REG_STATUSR_OFF:
2255 AssertReleaseFailed();
2256 break;
2257 case GIC_DIST_REG_SETSPI_NSR_OFF:
2258 AssertReleaseFailed();
2259 break;
2260 case GIC_DIST_REG_CLRSPI_NSR_OFF:
2261 AssertReleaseFailed();
2262 break;
2263 case GIC_DIST_REG_SETSPI_SR_OFF:
2264 AssertReleaseFailed();
2265 break;
2266 case GIC_DIST_REG_CLRSPI_SR_OFF:
2267 AssertReleaseFailed();
2268 break;
2269 case GIC_DIST_REG_ITARGETSRn_OFF_START:
2270 AssertReleaseFailed();
2271 break;
2272 case GIC_DIST_REG_IGRPMODRn_OFF_START:
2273 AssertReleaseFailed();
2274 break;
2275 case GIC_DIST_REG_NSACRn_OFF_START:
2276 AssertReleaseFailed();
2277 break;
2278 case GIC_DIST_REG_SGIR_OFF:
2279 AssertReleaseFailed();
2280 break;
2281 case GIC_DIST_REG_CPENDSGIRn_OFF_START:
2282 AssertReleaseFailed();
2283 break;
2284 case GIC_DIST_REG_SPENDSGIRn_OFF_START:
2285 AssertReleaseFailed();
2286 break;
2287 case GIC_DIST_REG_INMIn_OFF_START:
2288 AssertReleaseFailed();
2289 break;
2290 default:
2291 {
2292 /* Windows 11 arm64 (24H2) writes zeroes into these reserved registers. We ignore them. */
2293 if (offReg >= 0x7fe0 && offReg <= 0x7ffc)
2294 LogFlowFunc(("Bad guest writing to reserved GIC distributor register space [0x7fe0..0x7ffc] -- ignoring!"));
2295 else
2296 AssertReleaseMsgFailed(("offReg=%#x uValue=%#RX32\n", offReg, uValue));
2297 break;
2298 }
2299 }
2300
2301 return rcStrict;
2302}
2303
2304
2305/**
2306 * Reads a GIC redistributor register.
2307 *
2308 * @returns VBox status code.
2309 * @param pDevIns The device instance.
2310 * @param pVCpu The cross context virtual CPU structure.
2311 * @param idRedist The redistributor ID.
2312 * @param offReg The offset of the register being read.
2313 * @param puValue Where to store the register value.
2314 */
2315DECLINLINE(VBOXSTRICTRC) gicReDistReadRegister(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint32_t idRedist, uint16_t offReg, uint32_t *puValue)
2316{
2317 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
2318 AssertRelease(idRedist == pVCpu->idCpu);
2319 switch (offReg)
2320 {
2321 case GIC_REDIST_REG_TYPER_OFF:
2322 {
2323 PCVMCC pVM = pVCpu->CTX_SUFF(pVM);
2324 *puValue = (pVCpu->idCpu == pVM->cCpus - 1 ? GIC_REDIST_REG_TYPER_LAST : 0)
2325 | GIC_REDIST_REG_TYPER_CPU_NUMBER_SET(idRedist)
2326 | GIC_REDIST_REG_TYPER_CMN_LPI_AFF_SET(GIC_REDIST_REG_TYPER_CMN_LPI_AFF_ALL)
2327 | (pGicDev->fExtPpi ? GIC_REDIST_REG_TYPER_PPI_NUM_SET(pGicDev->uMaxExtPpi) : 0)
2328 | (pGicDev->fLpi ? GIC_REDIST_REG_TYPER_PLPIS : 0);
2329 Assert(!pGicDev->fExtPpi || pGicDev->uMaxExtPpi > 0);
2330 break;
2331 }
2332 case GIC_REDIST_REG_IIDR_OFF:
2333 *puValue = 0x43b; /* JEP106 code 0x43b is an ARM implementation. */
2334 break;
2335 case GIC_REDIST_REG_TYPER_AFFINITY_OFF:
2336 *puValue = idRedist;
2337 break;
2338 case GIC_REDIST_REG_PIDR2_OFF:
2339 Assert(pGicDev->uArchRev <= GIC_DIST_REG_PIDR2_ARCH_REV_GICV4);
2340 *puValue = GIC_REDIST_REG_PIDR2_ARCH_REV_SET(pGicDev->uArchRev);
2341 break;
2342 case GIC_REDIST_REG_CTLR_OFF:
2343 *puValue = GIC_REDIST_REG_CTLR_CES_SET(1);
2344 break;
2345 default:
2346 *puValue = 0;
2347 break;
2348 }
2349 return VINF_SUCCESS;
2350}
2351
2352
2353/**
2354 * Reads a GIC redistributor SGI/PPI frame register.
2355 *
2356 * @returns VBox status code.
2357 * @param pDevIns The device instance.
2358 * @param pVCpu The cross context virtual CPU structure.
2359 * @param offReg The offset of the register being read.
2360 * @param puValue Where to store the register value.
2361 */
2362DECLINLINE(VBOXSTRICTRC) gicReDistReadSgiPpiRegister(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t *puValue)
2363{
2364 VMCPU_ASSERT_EMT(pVCpu);
2365 RT_NOREF(pDevIns);
2366
2367 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
2368 PCGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
2369 uint16_t const cbReg = sizeof(uint32_t);
2370
2371 /*
2372 * GICR_IGROUPR0 and GICR_IGROUPR<n>E.
2373 */
2374 if ((uint32_t)offReg - GIC_REDIST_SGI_PPI_REG_IGROUPR0_OFF < GIC_REDIST_SGI_PPI_REG_IGROUPRnE_RANGE_SIZE)
2375 {
2376 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_IGROUPR0_OFF) / cbReg;
2377 return gicReDistReadIntrGroupReg(pGicDev, pGicCpu, idxReg, puValue);
2378 }
2379
2380 /*
2381 * GICR_ISENABLER0 and GICR_ISENABLER<n>E.
2382 * GICR_ICENABLER0 and GICR_ICENABLER<n>E.
2383 */
2384 if ((uint32_t)offReg - GIC_REDIST_SGI_PPI_REG_ISENABLER0_OFF < GIC_REDIST_SGI_PPI_REG_ISENABLERnE_RANGE_SIZE)
2385 {
2386 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ISENABLER0_OFF) / cbReg;
2387 return gicReDistReadIntrEnableReg(pGicDev, pGicCpu, idxReg, puValue);
2388 }
2389 if ((uint32_t)offReg - GIC_REDIST_SGI_PPI_REG_ICENABLER0_OFF < GIC_REDIST_SGI_PPI_REG_ICENABLERnE_RANGE_SIZE)
2390 {
2391 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ICENABLERnE_OFF_START) / cbReg;
2392 return gicReDistReadIntrEnableReg(pGicDev, pGicCpu, idxReg, puValue);
2393 }
2394
2395 /*
2396 * GICR_ISACTIVER0 and GICR_ISACTIVER<n>E.
2397 * GICR_ICACTIVER0 and GICR_ICACTIVER<n>E.
2398 */
2399 if ((uint32_t)offReg - GIC_REDIST_SGI_PPI_REG_ISACTIVER0_OFF < GIC_REDIST_SGI_PPI_REG_ISACTIVERnE_RANGE_SIZE)
2400 {
2401 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ISACTIVER0_OFF) / cbReg;
2402 return gicReDistReadIntrActiveReg(pGicCpu, idxReg, puValue);
2403 }
2404 if ((uint32_t)offReg - GIC_REDIST_SGI_PPI_REG_ICACTIVER0_OFF < GIC_REDIST_SGI_PPI_REG_ICACTIVERnE_RANGE_SIZE)
2405 {
2406 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ICACTIVER0_OFF) / cbReg;
2407 return gicReDistReadIntrActiveReg(pGicCpu, idxReg, puValue);
2408 }
2409
2410 /*
2411 * GICR_ISPENDR0 and GICR_ISPENDR<n>E.
2412 * GICR_ICPENDR0 and GICR_ICPENDR<n>E.
2413 */
2414 if ((uint32_t)offReg - GIC_REDIST_SGI_PPI_REG_ISPENDR0_OFF < GIC_REDIST_SGI_PPI_REG_ISPENDRnE_RANGE_SIZE)
2415 {
2416 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ISPENDR0_OFF) / cbReg;
2417 return gicReDistReadIntrPendingReg(pGicDev, pGicCpu, idxReg, puValue);
2418 }
2419 if ((uint32_t)offReg - GIC_REDIST_SGI_PPI_REG_ICPENDR0_OFF < GIC_REDIST_SGI_PPI_REG_ICPENDRnE_RANGE_SIZE)
2420 {
2421 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ICPENDR0_OFF) / cbReg;
2422 return gicReDistReadIntrPendingReg(pGicDev, pGicCpu, idxReg, puValue);
2423 }
2424
2425 /*
2426 * GICR_IPRIORITYR<n> and GICR_IPRIORITYR<n>E.
2427 */
2428 if ((uint32_t)offReg - GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START < GIC_REDIST_SGI_PPI_REG_IPRIORITYRnE_RANGE_SIZE)
2429 {
2430 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START) / cbReg;
2431 return gicReDistReadIntrPriorityReg(pGicDev, pGicCpu, idxReg, puValue);
2432 }
2433
2434 /*
2435 * GICR_ICFGR0, GICR_ICFGR1 and GICR_ICFGR<n>E.
2436 */
2437 if ((uint32_t)offReg - GIC_REDIST_SGI_PPI_REG_ICFGR0_OFF < GIC_REDIST_SGI_PPI_REG_ICFGRnE_RANGE_SIZE)
2438 {
2439 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ICFGR0_OFF) / cbReg;
2440 return gicReDistReadIntrConfigReg(pGicDev, pGicCpu, idxReg, puValue);
2441 }
2442
2443 AssertReleaseFailed();
2444 *puValue = 0;
2445 return VINF_SUCCESS;
2446}
2447
2448
2449/**
2450 * Writes a GIC redistributor frame register.
2451 *
2452 * @returns Strict VBox status code.
2453 * @param pDevIns The device instance.
2454 * @param pVCpu The cross context virtual CPU structure.
2455 * @param offReg The offset of the register being written.
2456 * @param uValue The register value.
2457 */
2458DECLINLINE(VBOXSTRICTRC) gicReDistWriteRegister(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t uValue)
2459{
2460 VMCPU_ASSERT_EMT(pVCpu);
2461 RT_NOREF(pDevIns, pVCpu, uValue);
2462
2463 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
2464 switch (offReg)
2465 {
2466 case GIC_REDIST_REG_WAKER_OFF:
2467 Assert(uValue == 0);
2468 break;
2469 default:
2470 AssertReleaseFailed();
2471 break;
2472 }
2473
2474 return rcStrict;
2475}
2476
2477
2478/**
2479 * Writes a GIC redistributor SGI/PPI frame register.
2480 *
2481 * @returns Strict VBox status code.
2482 * @param pDevIns The device instance.
2483 * @param pVCpu The cross context virtual CPU structure.
2484 * @param offReg The offset of the register being written.
2485 * @param uValue The register value.
2486 */
2487DECLINLINE(VBOXSTRICTRC) gicReDistWriteSgiPpiRegister(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t uValue)
2488{
2489 VMCPU_ASSERT_EMT(pVCpu);
2490 PCGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PCGICDEV);
2491 uint16_t const cbReg = sizeof(uint32_t);
2492
2493 /*
2494 * GICR_IGROUPR0 and GICR_IGROUPR<n>E.
2495 */
2496 if ((uint32_t)offReg - GIC_REDIST_SGI_PPI_REG_IGROUPR0_OFF < GIC_REDIST_SGI_PPI_REG_IGROUPRnE_RANGE_SIZE)
2497 {
2498 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_IGROUPR0_OFF) / cbReg;
2499 return gicReDistWriteIntrGroupReg(pGicDev, pVCpu, idxReg, uValue);
2500 }
2501
2502 /*
2503 * GICR_ISENABLER0 and GICR_ISENABLER<n>E.
2504 * GICR_ICENABLER0 and GICR_ICENABLER<n>E.
2505 */
2506 if ((uint32_t)offReg - GIC_REDIST_SGI_PPI_REG_ISENABLER0_OFF < GIC_REDIST_SGI_PPI_REG_ISENABLERnE_RANGE_SIZE)
2507 {
2508 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ISENABLER0_OFF) / cbReg;
2509 return gicReDistWriteIntrSetEnableReg(pGicDev, pVCpu, idxReg, uValue);
2510 }
2511 if ((uint32_t)offReg - GIC_REDIST_SGI_PPI_REG_ICENABLER0_OFF < GIC_REDIST_SGI_PPI_REG_ICENABLERnE_RANGE_SIZE)
2512 {
2513 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ICENABLER0_OFF) / cbReg;
2514 return gicReDistWriteIntrClearEnableReg(pGicDev, pVCpu, idxReg, uValue);
2515 }
2516
2517 /*
2518 * GICR_ISACTIVER0 and GICR_ISACTIVER<n>E.
2519 * GICR_ICACTIVER0 and GICR_ICACTIVER<n>E.
2520 */
2521 if ((uint32_t)offReg - GIC_REDIST_SGI_PPI_REG_ISACTIVER0_OFF < GIC_REDIST_SGI_PPI_REG_ISACTIVERnE_RANGE_SIZE)
2522 {
2523 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ISACTIVER0_OFF) / cbReg;
2524 return gicReDistWriteIntrSetActiveReg(pGicDev, pVCpu, idxReg, uValue);
2525 }
2526 if ((uint32_t)offReg - GIC_REDIST_SGI_PPI_REG_ICACTIVER0_OFF < GIC_REDIST_SGI_PPI_REG_ICACTIVERnE_RANGE_SIZE)
2527 {
2528 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ICACTIVER0_OFF) / cbReg;
2529 return gicReDistWriteIntrClearActiveReg(pGicDev, pVCpu, idxReg, uValue);
2530 }
2531
2532 /*
2533 * GICR_ISPENDR0 and GICR_ISPENDR<n>E.
2534 * GICR_ICPENDR0 and GICR_ICPENDR<n>E.
2535 */
2536 if ((uint32_t)offReg - GIC_REDIST_SGI_PPI_REG_ISPENDR0_OFF < GIC_REDIST_SGI_PPI_REG_ISPENDRnE_RANGE_SIZE)
2537 {
2538 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ISPENDR0_OFF) / cbReg;
2539 return gicReDistWriteIntrSetPendingReg(pGicDev, pVCpu, idxReg, uValue);
2540 }
2541 if ((uint32_t)offReg - GIC_REDIST_SGI_PPI_REG_ICPENDR0_OFF < GIC_REDIST_SGI_PPI_REG_ICPENDRnE_RANGE_SIZE)
2542 {
2543 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ICPENDR0_OFF) / cbReg;
2544 return gicReDistWriteIntrClearPendingReg(pGicDev, pVCpu, idxReg, uValue);
2545 }
2546
2547 /*
2548 * GICR_IPRIORITYR<n> and GICR_IPRIORITYR<n>E.
2549 */
2550 if ((uint32_t)offReg - GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START < GIC_REDIST_SGI_PPI_REG_IPRIORITYRnE_RANGE_SIZE)
2551 {
2552 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START) / cbReg;
2553 return gicReDistWriteIntrPriorityReg(pGicDev, pVCpu, idxReg, uValue);
2554 }
2555
2556 /*
2557 * GICR_ICFGR0, GIC_ICFGR1 and GICR_ICFGR<n>E.
2558 */
2559 if ((uint32_t)offReg - GIC_REDIST_SGI_PPI_REG_ICFGR0_OFF < GIC_REDIST_SGI_PPI_REG_ICFGRnE_RANGE_SIZE)
2560 {
2561 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ICFGR0_OFF) / cbReg;
2562 return gicReDistWriteIntrConfigReg(pGicDev, pVCpu, idxReg, uValue);
2563 }
2564
2565 AssertReleaseMsgFailed(("offReg=%#RX16\n", offReg));
2566 return VERR_INTERNAL_ERROR_2;
2567}
2568
2569
2570/**
2571 * @interface_method_impl{PDMGICBACKEND,pfnSetSpi}
2572 */
2573static DECLCALLBACK(int) gicSetSpi(PVMCC pVM, uint32_t uSpiIntId, bool fAsserted)
2574{
2575 LogFlowFunc(("pVM=%p uSpiIntId=%u fAsserted=%RTbool\n",
2576 pVM, uSpiIntId, fAsserted));
2577
2578 PGIC pGic = VM_TO_GIC(pVM);
2579 PPDMDEVINS pDevIns = pGic->CTX_SUFF(pDevIns);
2580 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
2581
2582#ifdef VBOX_WITH_STATISTICS
2583 PVMCPU pVCpu = VMMGetCpuById(pVM, 0);
2584 STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatSetSpi));
2585 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
2586#endif
2587 STAM_PROFILE_START(&pGicCpu->CTX_SUFF_Z(StatProfSetSpi), a);
2588
2589 uint16_t const uIntId = GIC_INTID_RANGE_SPI_START + uSpiIntId;
2590 uint16_t const idxIntr = gicDistGetIndexFromIntId(uIntId);
2591
2592 Assert(idxIntr >= GIC_INTID_RANGE_SPI_START);
2593 AssertMsgReturn(idxIntr < sizeof(pGicDev->bmIntrPending) * 8,
2594 ("out-of-range SPI interrupt ID %RU32 (%RU32)\n", uIntId, uSpiIntId),
2595 VERR_INVALID_PARAMETER);
2596
2597 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
2598 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
2599
2600 /* Update the interrupt pending state. */
2601 if (fAsserted)
2602 ASMBitSet(&pGicDev->bmIntrPending[0], idxIntr);
2603 else
2604 ASMBitClear(&pGicDev->bmIntrPending[0], idxIntr);
2605
2606 int const rc = VBOXSTRICTRC_VAL(gicDistUpdateIrqState(pVM, pGicDev));
2607 STAM_PROFILE_STOP(&pGicCpu->CTX_SUFF_Z(StatProfSetSpi), a);
2608
2609 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
2610 return rc;
2611}
2612
2613
2614/**
2615 * @interface_method_impl{PDMGICBACKEND,pfnSetPpi}
2616 */
2617static DECLCALLBACK(int) gicSetPpi(PVMCPUCC pVCpu, uint32_t uPpiIntId, bool fAsserted)
2618{
2619 LogFlowFunc(("pVCpu=%p{.idCpu=%u} uPpiIntId=%u fAsserted=%RTbool\n", pVCpu, pVCpu->idCpu, uPpiIntId, fAsserted));
2620
2621 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
2622 PCGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PCGICDEV);
2623 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
2624
2625 STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatSetPpi));
2626 STAM_PROFILE_START(&pGicCpu->CTX_SUFF_Z(StatProfSetPpi), b);
2627
2628 uint32_t const uIntId = GIC_INTID_RANGE_PPI_START + uPpiIntId;
2629 uint16_t const idxIntr = gicReDistGetIndexFromIntId(uIntId);
2630
2631 Assert(idxIntr >= GIC_INTID_RANGE_PPI_START);
2632 AssertMsgReturn(idxIntr < sizeof(pGicCpu->bmIntrPending) * 8,
2633 ("out-of-range PPI interrupt ID %RU32 (%RU32)\n", uIntId, uPpiIntId),
2634 VERR_INVALID_PARAMETER);
2635
2636 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
2637 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
2638
2639 /* Update the interrupt pending state. */
2640 if (fAsserted)
2641 ASMBitSet(&pGicCpu->bmIntrPending[0], idxIntr);
2642 else
2643 ASMBitClear(&pGicCpu->bmIntrPending[0], idxIntr);
2644
2645 int const rc = VBOXSTRICTRC_VAL(gicReDistUpdateIrqState(pGicDev, pVCpu));
2646 STAM_PROFILE_STOP(&pGicCpu->CTX_SUFF_Z(StatProfSetPpi), b);
2647
2648 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
2649 return rc;
2650}
2651
2652
2653/**
2654 * Sets the specified software generated interrupt (SGI).
2655 *
2656 * @returns Strict VBox status code.
2657 * @param pGicDev The GIC distributor state.
2658 * @param pVCpu The cross context virtual CPU structure.
2659 * @param pDestCpuSet Which CPUs to deliver the SGI to.
2660 * @param uIntId The SGI interrupt ID.
2661 */
2662static VBOXSTRICTRC gicSetSgi(PCGICDEV pGicDev, PVMCPUCC pVCpu, PCVMCPUSET pDestCpuSet, uint8_t uIntId)
2663{
2664 LogFlowFunc(("pVCpu=%p{.idCpu=%u} uIntId=%u\n", pVCpu, pVCpu->idCpu, uIntId));
2665
2666 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
2667 PCVMCC pVM = pVCpu->CTX_SUFF(pVM);
2668 uint32_t const cCpus = pVM->cCpus;
2669 AssertReturn(uIntId <= GIC_INTID_RANGE_SGI_LAST, VERR_INVALID_PARAMETER);
2670 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->pCritSectRoR3)); RT_NOREF_PV(pDevIns);
2671
2672 for (VMCPUID idCpu = 0; idCpu < cCpus; idCpu++)
2673 if (VMCPUSET_IS_PRESENT(pDestCpuSet, idCpu))
2674 {
2675 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVM->CTX_SUFF(apCpus)[idCpu]);
2676 pGicCpu->bmIntrPending[0] |= RT_BIT_32(uIntId);
2677 }
2678
2679 return gicDistUpdateIrqState(pVM, pGicDev);
2680}
2681
2682
2683/**
2684 * Writes to the redistributor's SGI group 1 register (ICC_SGI1R_EL1).
2685 *
2686 * @returns Strict VBox status code.
2687 * @param pGicDev The GIC distributor state.
2688 * @param pVCpu The cross context virtual CPU structure.
2689 * @param uValue The value being written to the ICC_SGI1R_EL1 register.
2690 */
2691static VBOXSTRICTRC gicReDistWriteSgiReg(PCGICDEV pGicDev, PVMCPUCC pVCpu, uint64_t uValue)
2692{
2693#ifdef VBOX_WITH_STATISTICS
2694 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
2695 STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatSetSgi));
2696 STAM_PROFILE_START(&pGicCpu->CTX_SUFF_Z(StatProfSetSgi), c);
2697#else
2698 PCGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
2699#endif
2700
2701 VMCPUSET DestCpuSet;
2702 if (uValue & ARMV8_ICC_SGI1R_EL1_AARCH64_IRM)
2703 {
2704 /*
2705 * Deliver to all VCPUs but this one.
2706 */
2707 VMCPUSET_FILL(&DestCpuSet);
2708 VMCPUSET_DEL(&DestCpuSet, pVCpu->idCpu);
2709 }
2710 else
2711 {
2712 /*
2713 * Target specific VCPUs.
2714 * See ARM GICv3 and GICv4 Software Overview spec 3.3 "Affinity routing".
2715 */
2716 VMCPUSET_EMPTY(&DestCpuSet);
2717 bool const fRangeSelSupport = RT_BOOL(pGicCpu->uIccCtlr & ARMV8_ICC_CTLR_EL1_AARCH64_RSS);
2718 uint8_t const idRangeStart = ARMV8_ICC_SGI1R_EL1_AARCH64_RS_GET(uValue) * 16;
2719 uint16_t const bmCpuInterfaces = ARMV8_ICC_SGI1R_EL1_AARCH64_TARGET_LIST_GET(uValue);
2720 uint8_t const uAff1 = ARMV8_ICC_SGI1R_EL1_AARCH64_AFF1_GET(uValue);
2721 uint8_t const uAff2 = ARMV8_ICC_SGI1R_EL1_AARCH64_AFF2_GET(uValue);
2722 uint8_t const uAff3 = (pGicCpu->uIccCtlr & ARMV8_ICC_CTLR_EL1_AARCH64_A3V)
2723 ? ARMV8_ICC_SGI1R_EL1_AARCH64_AFF3_GET(uValue)
2724 : 0;
2725 uint32_t const cCpus = pVCpu->CTX_SUFF(pVM)->cCpus;
2726 for (uint8_t idCpuInterface = 0; idCpuInterface < 16; idCpuInterface++)
2727 {
2728 if (bmCpuInterfaces & RT_BIT(idCpuInterface))
2729 {
2730 VMCPUID idCpuTarget;
2731 if (fRangeSelSupport)
2732 idCpuTarget = RT_MAKE_U32_FROM_U8(idRangeStart + idCpuInterface, uAff1, uAff2, uAff3);
2733 else
2734 idCpuTarget = gicGetCpuIdFromAffinity(idCpuInterface, uAff1, uAff2, uAff3);
2735 if (RT_LIKELY(idCpuTarget < cCpus))
2736 VMCPUSET_ADD(&DestCpuSet, idCpuTarget);
2737 else
2738 AssertReleaseFailed();
2739 }
2740 }
2741 }
2742
2743 if (!VMCPUSET_IS_EMPTY(&DestCpuSet))
2744 {
2745 uint8_t const uSgiIntId = ARMV8_ICC_SGI1R_EL1_AARCH64_INTID_GET(uValue);
2746 Assert(GIC_IS_INTR_SGI(uSgiIntId));
2747 VBOXSTRICTRC const rcStrict = gicSetSgi(pGicDev, pVCpu, &DestCpuSet, uSgiIntId);
2748 Assert(RT_SUCCESS(rcStrict)); RT_NOREF_PV(rcStrict);
2749 }
2750
2751 STAM_PROFILE_STOP(&pGicCpu->CTX_SUFF_Z(StatProfSetSgi), c);
2752 return VINF_SUCCESS;
2753}
2754
2755
2756/**
2757 * @interface_method_impl{PDMGICBACKEND,pfnReadSysReg}
2758 */
2759static DECLCALLBACK(VBOXSTRICTRC) gicReadSysReg(PVMCPUCC pVCpu, uint32_t u32Reg, uint64_t *pu64Value)
2760{
2761 /*
2762 * Validate.
2763 */
2764 VMCPU_ASSERT_EMT(pVCpu);
2765 Assert(pu64Value);
2766
2767 STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatSysRegRead));
2768
2769 *pu64Value = 0;
2770 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
2771 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
2772 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
2773
2774 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
2775 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
2776
2777 switch (u32Reg)
2778 {
2779 case ARMV8_AARCH64_SYSREG_ICC_PMR_EL1:
2780 *pu64Value = pGicCpu->bIntrPriorityMask;
2781 break;
2782 case ARMV8_AARCH64_SYSREG_ICC_IAR0_EL1:
2783 AssertReleaseFailed();
2784 break;
2785 case ARMV8_AARCH64_SYSREG_ICC_EOIR0_EL1:
2786 AssertReleaseFailed();
2787 break;
2788 case ARMV8_AARCH64_SYSREG_ICC_HPPIR0_EL1:
2789 AssertReleaseFailed();
2790 break;
2791 case ARMV8_AARCH64_SYSREG_ICC_BPR0_EL1:
2792 *pu64Value = ARMV8_ICC_BPR0_EL1_AARCH64_BINARYPOINT_SET(pGicCpu->bBinaryPtGroup0);
2793 break;
2794 case ARMV8_AARCH64_SYSREG_ICC_AP0R0_EL1:
2795 AssertReleaseFailed();
2796 *pu64Value = pGicCpu->bmActivePriorityGroup0[0];
2797 break;
2798 case ARMV8_AARCH64_SYSREG_ICC_AP0R1_EL1:
2799 AssertReleaseFailed();
2800 *pu64Value = pGicCpu->bmActivePriorityGroup0[1];
2801 break;
2802 case ARMV8_AARCH64_SYSREG_ICC_AP0R2_EL1:
2803 AssertReleaseFailed();
2804 *pu64Value = pGicCpu->bmActivePriorityGroup0[2];
2805 break;
2806 case ARMV8_AARCH64_SYSREG_ICC_AP0R3_EL1:
2807 AssertReleaseFailed();
2808 *pu64Value = pGicCpu->bmActivePriorityGroup0[3];
2809 break;
2810 case ARMV8_AARCH64_SYSREG_ICC_AP1R0_EL1:
2811 AssertReleaseFailed();
2812 *pu64Value = pGicCpu->bmActivePriorityGroup1[0];
2813 break;
2814 case ARMV8_AARCH64_SYSREG_ICC_AP1R1_EL1:
2815 AssertReleaseFailed();
2816 *pu64Value = pGicCpu->bmActivePriorityGroup1[1];
2817 break;
2818 case ARMV8_AARCH64_SYSREG_ICC_AP1R2_EL1:
2819 AssertReleaseFailed();
2820 *pu64Value = pGicCpu->bmActivePriorityGroup1[2];
2821 break;
2822 case ARMV8_AARCH64_SYSREG_ICC_AP1R3_EL1:
2823 AssertReleaseFailed();
2824 *pu64Value = pGicCpu->bmActivePriorityGroup1[3];
2825 break;
2826 case ARMV8_AARCH64_SYSREG_ICC_NMIAR1_EL1:
2827 AssertReleaseFailed();
2828 break;
2829 case ARMV8_AARCH64_SYSREG_ICC_DIR_EL1:
2830 AssertReleaseFailed();
2831 break;
2832 case ARMV8_AARCH64_SYSREG_ICC_RPR_EL1:
2833 *pu64Value = pGicCpu->abRunningPriorities[pGicCpu->idxRunningPriority];
2834 break;
2835 case ARMV8_AARCH64_SYSREG_ICC_SGI1R_EL1:
2836 AssertReleaseFailed();
2837 break;
2838 case ARMV8_AARCH64_SYSREG_ICC_ASGI1R_EL1:
2839 AssertReleaseFailed();
2840 break;
2841 case ARMV8_AARCH64_SYSREG_ICC_SGI0R_EL1:
2842 AssertReleaseFailed();
2843 break;
2844 case ARMV8_AARCH64_SYSREG_ICC_IAR1_EL1:
2845 *pu64Value = gicAckHighestPriorityPendingIntr(pGicDev, pVCpu, false /*fGroup0*/, true /*fGroup1*/);
2846 break;
2847 case ARMV8_AARCH64_SYSREG_ICC_EOIR1_EL1:
2848 AssertReleaseFailed();
2849 break;
2850 case ARMV8_AARCH64_SYSREG_ICC_HPPIR1_EL1:
2851 {
2852 AssertReleaseFailed();
2853 *pu64Value = gicGetHighestPriorityPendingIntr(pGicDev, pGicCpu, false /*fGroup0*/, true /*fGroup1*/,
2854 NULL /*pidxIntr*/, NULL /*pbPriority*/);
2855 break;
2856 }
2857 case ARMV8_AARCH64_SYSREG_ICC_BPR1_EL1:
2858 *pu64Value = ARMV8_ICC_BPR1_EL1_AARCH64_BINARYPOINT_SET(pGicCpu->bBinaryPtGroup1);
2859 break;
2860 case ARMV8_AARCH64_SYSREG_ICC_CTLR_EL1:
2861 *pu64Value = pGicCpu->uIccCtlr;
2862 break;
2863 case ARMV8_AARCH64_SYSREG_ICC_SRE_EL1:
2864 AssertReleaseFailed();
2865 break;
2866 case ARMV8_AARCH64_SYSREG_ICC_IGRPEN0_EL1:
2867 *pu64Value = pGicCpu->fIntrGroup0Enabled ? ARMV8_ICC_IGRPEN0_EL1_AARCH64_ENABLE : 0;
2868 break;
2869 case ARMV8_AARCH64_SYSREG_ICC_IGRPEN1_EL1:
2870 *pu64Value = pGicCpu->fIntrGroup1Enabled ? ARMV8_ICC_IGRPEN1_EL1_AARCH64_ENABLE : 0;
2871 break;
2872 default:
2873 AssertReleaseFailed();
2874 break;
2875 }
2876
2877 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
2878
2879 LogFlowFunc(("pVCpu=%p u32Reg=%#x{%s} pu64Value=%RX64\n", pVCpu, u32Reg, gicIccGetRegDescription(u32Reg), *pu64Value));
2880 return VINF_SUCCESS;
2881}
2882
2883
2884/**
2885 * @interface_method_impl{PDMGICBACKEND,pfnWriteSysReg}
2886 */
2887static DECLCALLBACK(VBOXSTRICTRC) gicWriteSysReg(PVMCPUCC pVCpu, uint32_t u32Reg, uint64_t u64Value)
2888{
2889 /*
2890 * Validate.
2891 */
2892 VMCPU_ASSERT_EMT(pVCpu);
2893 LogFlowFunc(("pVCpu=%p u32Reg=%#x{%s} u64Value=%RX64\n", pVCpu, u32Reg, gicIccGetRegDescription(u32Reg), u64Value));
2894
2895 STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatSysRegWrite));
2896
2897 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
2898 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
2899 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
2900
2901 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
2902 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
2903
2904 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
2905 switch (u32Reg)
2906 {
2907 case ARMV8_AARCH64_SYSREG_ICC_PMR_EL1:
2908 LogFlowFunc(("ICC_PMR_EL1: Interrupt priority now %u\n", (uint8_t)u64Value));
2909 pGicCpu->bIntrPriorityMask = (uint8_t)u64Value;
2910 rcStrict = gicReDistUpdateIrqState(pGicDev, pVCpu);
2911 break;
2912 case ARMV8_AARCH64_SYSREG_ICC_IAR0_EL1:
2913 AssertReleaseFailed();
2914 break;
2915 case ARMV8_AARCH64_SYSREG_ICC_EOIR0_EL1:
2916 AssertReleaseFailed();
2917 break;
2918 case ARMV8_AARCH64_SYSREG_ICC_HPPIR0_EL1:
2919 AssertReleaseFailed();
2920 break;
2921 case ARMV8_AARCH64_SYSREG_ICC_BPR0_EL1:
2922 pGicCpu->bBinaryPtGroup0 = (uint8_t)ARMV8_ICC_BPR0_EL1_AARCH64_BINARYPOINT_GET(u64Value);
2923 break;
2924 case ARMV8_AARCH64_SYSREG_ICC_AP0R0_EL1:
2925 case ARMV8_AARCH64_SYSREG_ICC_AP0R1_EL1:
2926 case ARMV8_AARCH64_SYSREG_ICC_AP0R2_EL1:
2927 case ARMV8_AARCH64_SYSREG_ICC_AP0R3_EL1:
2928 case ARMV8_AARCH64_SYSREG_ICC_AP1R0_EL1:
2929 case ARMV8_AARCH64_SYSREG_ICC_AP1R1_EL1:
2930 case ARMV8_AARCH64_SYSREG_ICC_AP1R2_EL1:
2931 case ARMV8_AARCH64_SYSREG_ICC_AP1R3_EL1:
2932 /* Writes ignored, well behaving guest would write all 0s or the last read value of the register. */
2933 break;
2934 case ARMV8_AARCH64_SYSREG_ICC_NMIAR1_EL1:
2935 AssertReleaseFailed();
2936 break;
2937 case ARMV8_AARCH64_SYSREG_ICC_DIR_EL1:
2938 AssertReleaseFailed();
2939 break;
2940 case ARMV8_AARCH64_SYSREG_ICC_RPR_EL1:
2941 AssertReleaseFailed();
2942 break;
2943 case ARMV8_AARCH64_SYSREG_ICC_SGI1R_EL1:
2944 {
2945 gicReDistWriteSgiReg(pGicDev, pVCpu, u64Value);
2946 break;
2947 }
2948 case ARMV8_AARCH64_SYSREG_ICC_ASGI1R_EL1:
2949 AssertReleaseFailed();
2950 break;
2951 case ARMV8_AARCH64_SYSREG_ICC_SGI0R_EL1:
2952 AssertReleaseFailed();
2953 break;
2954 case ARMV8_AARCH64_SYSREG_ICC_IAR1_EL1:
2955 AssertReleaseFailed();
2956 break;
2957 case ARMV8_AARCH64_SYSREG_ICC_EOIR1_EL1:
2958 {
2959 /*
2960 * We only support priority drop + interrupt deactivation with writes to this register.
2961 * This avoids an extra access which would be required by software for deactivation.
2962 */
2963 Assert(!(pGicCpu->uIccCtlr & ARMV8_ICC_CTLR_EL1_AARCH64_EOIMODE));
2964
2965 /*
2966 * Mark the interrupt as inactive, though it might still be pending.
2967 * It is up to the guest to ensure the interrupt ID belongs to the right group as
2968 * failure to do so results in unpredictable behavior.
2969 *
2970 * See ARM GIC spec. 12.2.10 "ICC_EOIR1_EL1, Interrupt Controller End Of Interrupt Register 1".
2971 * NOTE! The order of the 'if' checks below are crucial.
2972 */
2973 uint16_t const uIntId = (uint16_t)u64Value;
2974 if (uIntId <= GIC_INTID_RANGE_PPI_LAST)
2975 {
2976 /* SGIs and PPIs. */
2977 AssertCompile(GIC_INTID_RANGE_PPI_LAST < 8 * sizeof(pGicDev->bmIntrActive[0]));
2978 Assert(pGicDev->fAffRoutingEnabled);
2979 pGicCpu->bmIntrActive[0] &= ~RT_BIT_32(uIntId);
2980 }
2981 else if (uIntId <= GIC_INTID_RANGE_SPI_LAST)
2982 {
2983 /* SPIs. */
2984 uint16_t const idxIntr = /*gicDistGetIndexFromIntId*/(uIntId);
2985 AssertReturn(idxIntr < sizeof(pGicDev->bmIntrActive) * 8, VERR_BUFFER_OVERFLOW);
2986 ASMBitClear(&pGicDev->bmIntrActive[0], idxIntr);
2987 }
2988 else if (uIntId <= GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT)
2989 {
2990 /* Special interrupt IDs, ignored. */
2991 Log(("Ignoring write to EOI with special interrupt ID.\n"));
2992 break;
2993 }
2994 else if (uIntId <= GIC_INTID_RANGE_EXT_PPI_LAST)
2995 {
2996 /* Extended PPIs. */
2997 uint16_t const idxIntr = gicReDistGetIndexFromIntId(uIntId);
2998 AssertReturn(idxIntr < sizeof(pGicCpu->bmIntrActive) * 8, VERR_BUFFER_OVERFLOW);
2999 ASMBitClear(&pGicCpu->bmIntrActive[0], idxIntr);
3000 }
3001 else if (uIntId <= GIC_INTID_RANGE_EXT_SPI_LAST)
3002 {
3003 /* Extended SPIs. */
3004 uint16_t const idxIntr = gicDistGetIndexFromIntId(uIntId);
3005 AssertReturn(idxIntr < sizeof(pGicDev->bmIntrActive) * 8, VERR_BUFFER_OVERFLOW);
3006 ASMBitClear(&pGicDev->bmIntrActive[0], idxIntr);
3007 }
3008 else
3009 {
3010 AssertMsgFailed(("Invalid INTID %u\n", uIntId));
3011 break;
3012 }
3013
3014 /*
3015 * Drop priority by restoring previous interrupt.
3016 */
3017 if (RT_LIKELY(pGicCpu->idxRunningPriority))
3018 {
3019 LogFlowFunc(("Restoring interrupt priority from %u -> %u (idxRunningPriority: %u -> %u)\n",
3020 pGicCpu->abRunningPriorities[pGicCpu->idxRunningPriority],
3021 pGicCpu->abRunningPriorities[pGicCpu->idxRunningPriority - 1],
3022 pGicCpu->idxRunningPriority, pGicCpu->idxRunningPriority - 1));
3023
3024 /*
3025 * Clear the interrupt priority from the active priorities bitmap.
3026 * It is up to the guest to ensure that writes to EOI registers are done in the exact
3027 * reverse order of the reads from the IAR registers.
3028 *
3029 * See ARM GIC spec 4.1.1 "Physical CPU interface".
3030 */
3031 uint8_t const idxPreemptionLevel = pGicCpu->abRunningPriorities[pGicCpu->idxRunningPriority] >> 1;
3032 AssertCompile(sizeof(pGicCpu->bmActivePriorityGroup1) * 8 >= 128);
3033 ASMBitClear(&pGicCpu->bmActivePriorityGroup1[0], idxPreemptionLevel);
3034
3035 pGicCpu->idxRunningPriority--;
3036 Assert(pGicCpu->abRunningPriorities[0] == GIC_IDLE_PRIORITY);
3037 }
3038 else
3039 AssertReleaseMsgFailed(("Index of running-priority interrupt out-of-bounds %u\n", pGicCpu->idxRunningPriority));
3040 rcStrict = gicReDistUpdateIrqState(pGicDev, pVCpu);
3041 break;
3042 }
3043 case ARMV8_AARCH64_SYSREG_ICC_HPPIR1_EL1:
3044 AssertReleaseFailed();
3045 break;
3046 case ARMV8_AARCH64_SYSREG_ICC_BPR1_EL1:
3047 pGicCpu->bBinaryPtGroup1 = (uint8_t)ARMV8_ICC_BPR1_EL1_AARCH64_BINARYPOINT_GET(u64Value);
3048 break;
3049 case ARMV8_AARCH64_SYSREG_ICC_CTLR_EL1:
3050 pGicCpu->uIccCtlr &= ARMV8_ICC_CTLR_EL1_RW;
3051 /** @todo */
3052 break;
3053 case ARMV8_AARCH64_SYSREG_ICC_SRE_EL1:
3054 AssertReleaseFailed();
3055 break;
3056 case ARMV8_AARCH64_SYSREG_ICC_IGRPEN0_EL1:
3057 pGicCpu->fIntrGroup0Enabled = RT_BOOL(u64Value & ARMV8_ICC_IGRPEN0_EL1_AARCH64_ENABLE);
3058 break;
3059 case ARMV8_AARCH64_SYSREG_ICC_IGRPEN1_EL1:
3060 pGicCpu->fIntrGroup1Enabled = RT_BOOL(u64Value & ARMV8_ICC_IGRPEN1_EL1_AARCH64_ENABLE);
3061 break;
3062 default:
3063 AssertReleaseFailed();
3064 break;
3065 }
3066
3067 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
3068 return rcStrict;
3069}
3070
3071
3072/**
3073 * Initializes the GIC distributor state.
3074 *
3075 * @param pDevIns The device instance.
3076 */
3077static void gicInit(PPDMDEVINS pDevIns)
3078{
3079 LogFlowFunc(("\n"));
3080 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
3081 PGITSDEV pGitsDev = &pGicDev->Gits;
3082
3083 RT_ZERO(pGicDev->bmIntrGroup);
3084 RT_ZERO(pGicDev->bmIntrConfig);
3085 RT_ZERO(pGicDev->bmIntrEnabled);
3086 RT_ZERO(pGicDev->bmIntrPending);
3087 RT_ZERO(pGicDev->bmIntrActive);
3088 RT_ZERO(pGicDev->abIntrPriority);
3089 RT_ZERO(pGicDev->au32IntrRouting);
3090 RT_ZERO(pGicDev->bmIntrRoutingMode);
3091 pGicDev->fIntrGroup0Enabled = false;
3092 pGicDev->fIntrGroup1Enabled = false;
3093 pGicDev->fAffRoutingEnabled = true; /* GICv2 backwards compatibility is not implemented, so this is RA1/WI. */
3094 RT_ZERO(pGicDev->bmLpiPending);
3095 RT_ZERO(pGicDev->abLpiConfig);
3096 gitsInit(pGitsDev);
3097}
3098
3099
3100/**
3101 * Initialies the GIC redistributor and CPU interface state.
3102 *
3103 * @param pDevIns The device instance.
3104 * @param pVCpu The cross context virtual CPU structure.
3105 */
3106static void gicInitCpu(PPDMDEVINS pDevIns, PVMCPUCC pVCpu)
3107{
3108 LogFlowFunc(("[%u]\n", pVCpu->idCpu));
3109 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
3110 PGICCPU pGicCpu = &pVCpu->gic.s;
3111
3112 RT_ZERO(pGicCpu->bmIntrGroup);
3113 RT_ZERO(pGicCpu->bmIntrConfig);
3114 /* SGIs are always edge-triggered, writes to GICR_ICFGR0 are to be ignored. */
3115 pGicCpu->bmIntrConfig[0] = 0xaaaaaaaa;
3116 RT_ZERO(pGicCpu->bmIntrEnabled);
3117 RT_ZERO(pGicCpu->bmIntrPending);
3118 RT_ZERO(pGicCpu->bmIntrActive);
3119 RT_ZERO(pGicCpu->abIntrPriority);
3120
3121 pGicCpu->uIccCtlr = ARMV8_ICC_CTLR_EL1_AARCH64_PMHE
3122 | ARMV8_ICC_CTLR_EL1_AARCH64_PRIBITS_SET(4)
3123 | ARMV8_ICC_CTLR_EL1_AARCH64_IDBITS_SET(ARMV8_ICC_CTLR_EL1_AARCH64_IDBITS_16BITS)
3124 | (pGicDev->fRangeSel ? ARMV8_ICC_CTLR_EL1_AARCH64_RSS : 0)
3125 | (pGicDev->fAff3Levels ? ARMV8_ICC_CTLR_EL1_AARCH64_A3V : 0)
3126 | (pGicDev->fExtPpi || pGicDev->fExtSpi ? ARMV8_ICC_CTLR_EL1_AARCH64_EXTRANGE : 0);
3127
3128 pGicCpu->bIntrPriorityMask = 0; /* Means no interrupt gets through to the PE. */
3129 pGicCpu->idxRunningPriority = 0;
3130 memset((void *)&pGicCpu->abRunningPriorities[0], 0xff, sizeof(pGicCpu->abRunningPriorities));
3131 RT_ZERO(pGicCpu->bmActivePriorityGroup0);
3132 RT_ZERO(pGicCpu->bmActivePriorityGroup1);
3133 pGicCpu->bBinaryPtGroup0 = 0;
3134 pGicCpu->bBinaryPtGroup1 = 0;
3135 pGicCpu->fIntrGroup0Enabled = false;
3136 pGicCpu->fIntrGroup1Enabled = false;
3137}
3138
3139
3140/**
3141 * Initializes per-VM GIC to the state following a power-up or hardware
3142 * reset.
3143 *
3144 * @param pDevIns The device instance.
3145 */
3146DECLHIDDEN(void) gicReset(PPDMDEVINS pDevIns)
3147{
3148 LogFlowFunc(("\n"));
3149 gicInit(pDevIns);
3150}
3151
3152
3153/**
3154 * Initializes per-VCPU GIC to the state following a power-up or hardware
3155 * reset.
3156 *
3157 * @param pDevIns The device instance.
3158 * @param pVCpu The cross context virtual CPU structure.
3159 */
3160DECLHIDDEN(void) gicResetCpu(PPDMDEVINS pDevIns, PVMCPUCC pVCpu)
3161{
3162 LogFlowFunc(("[%u]\n", pVCpu->idCpu));
3163 VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu);
3164 gicInitCpu(pDevIns, pVCpu);
3165}
3166
3167
3168/**
3169 * @callback_method_impl{FNIOMMMIONEWREAD}
3170 */
3171DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicDistMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
3172{
3173 NOREF(pvUser);
3174 Assert(!(off & 0x3));
3175 Assert(cb == 4); RT_NOREF_PV(cb);
3176
3177 PVMCPUCC pVCpu = PDMDevHlpGetVMCPU(pDevIns);
3178 uint16_t offReg = off & 0xfffc;
3179 uint32_t uValue = 0;
3180
3181 STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatMmioRead));
3182
3183 VBOXSTRICTRC rc = VBOXSTRICTRC_VAL(gicDistReadRegister(pDevIns, pVCpu, offReg, &uValue));
3184 *(uint32_t *)pv = uValue;
3185
3186 LogFlowFunc(("[%u]: offReg=%#RX16 (%s) uValue=%#RX32\n", pVCpu->idCpu, offReg, gicDistGetRegDescription(offReg), uValue));
3187 return rc;
3188}
3189
3190
3191/**
3192 * @callback_method_impl{FNIOMMMIONEWWRITE}
3193 */
3194DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicDistMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
3195{
3196 NOREF(pvUser);
3197 Assert(!(off & 0x3));
3198 Assert(cb == 4); RT_NOREF_PV(cb);
3199
3200 PVMCPUCC pVCpu = PDMDevHlpGetVMCPU(pDevIns);
3201 uint16_t offReg = off & 0xfffc;
3202 uint32_t uValue = *(uint32_t *)pv;
3203
3204 STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatMmioWrite));
3205 LogFlowFunc(("[%u]: offReg=%#RX16 (%s) uValue=%#RX32\n", pVCpu->idCpu, offReg, gicDistGetRegDescription(offReg), uValue));
3206
3207 return gicDistWriteRegister(pDevIns, pVCpu, offReg, uValue);
3208}
3209
3210
3211/**
3212 * @callback_method_impl{FNIOMMMIONEWREAD}
3213 */
3214DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicReDistMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
3215{
3216 NOREF(pvUser);
3217 Assert(!(off & 0x3));
3218 Assert(cb == 4); RT_NOREF_PV(cb);
3219
3220 /*
3221 * Determine the redistributor being targeted. Each redistributor takes
3222 * GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE bytes
3223 * and the redistributors are adjacent.
3224 */
3225 uint32_t const idReDist = off / (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
3226 off %= (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
3227
3228 PVMCC pVM = PDMDevHlpGetVM(pDevIns);
3229 Assert(idReDist < pVM->cCpus);
3230 PVMCPUCC pVCpu = pVM->CTX_SUFF(apCpus)[idReDist];
3231
3232 STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatMmioRead));
3233
3234 /* Redistributor or SGI/PPI frame? */
3235 uint16_t const offReg = off & 0xfffc;
3236 uint32_t uValue = 0;
3237 VBOXSTRICTRC rcStrict;
3238 if (off < GIC_REDIST_REG_FRAME_SIZE)
3239 rcStrict = gicReDistReadRegister(pDevIns, pVCpu, idReDist, offReg, &uValue);
3240 else
3241 rcStrict = gicReDistReadSgiPpiRegister(pDevIns, pVCpu, offReg, &uValue);
3242
3243 *(uint32_t *)pv = uValue;
3244 LogFlowFunc(("[%u]: off=%RGp idReDist=%u offReg=%#RX16 (%s) uValue=%#RX32 -> %Rrc\n", pVCpu->idCpu, off, idReDist, offReg,
3245 gicReDistGetRegDescription(offReg), uValue, VBOXSTRICTRC_VAL(rcStrict)));
3246 return rcStrict;
3247}
3248
3249
3250/**
3251 * @callback_method_impl{FNIOMMMIONEWWRITE}
3252 */
3253DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicReDistMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
3254{
3255 NOREF(pvUser);
3256 Assert(!(off & 0x3));
3257 Assert(cb == 4); RT_NOREF_PV(cb);
3258
3259 uint32_t uValue = *(uint32_t *)pv;
3260
3261 /*
3262 * Determine the redistributor being targeted. Each redistributor takes
3263 * GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE bytes
3264 * and the redistributors are adjacent.
3265 */
3266 uint32_t const idReDist = off / (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
3267 off %= (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
3268
3269 PCVMCC pVM = PDMDevHlpGetVM(pDevIns);
3270 Assert(idReDist < pVM->cCpus);
3271 PVMCPUCC pVCpu = pVM->CTX_SUFF(apCpus)[idReDist];
3272
3273 STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatMmioWrite));
3274
3275 /* Redistributor or SGI/PPI frame? */
3276 uint16_t const offReg = off & 0xfffc;
3277 VBOXSTRICTRC rcStrict;
3278 if (off < GIC_REDIST_REG_FRAME_SIZE)
3279 rcStrict = gicReDistWriteRegister(pDevIns, pVCpu, offReg, uValue);
3280 else
3281 rcStrict = gicReDistWriteSgiPpiRegister(pDevIns, pVCpu, offReg, uValue);
3282
3283 LogFlowFunc(("[%u]: off=%RGp idReDist=%u offReg=%#RX16 (%s) uValue=%#RX32 -> %Rrc\n", pVCpu->idCpu, off, idReDist, offReg,
3284 gicReDistGetRegDescription(offReg), uValue, VBOXSTRICTRC_VAL(rcStrict)));
3285 return rcStrict;
3286}
3287
3288
3289/**
3290 * @callback_method_impl{FNIOMMMIONEWREAD}
3291 */
3292DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicItsMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
3293{
3294 RT_NOREF_PV(pvUser);
3295 Assert(!(off & 0x3));
3296 Assert(cb == 4); RT_NOREF_PV(cb);
3297
3298 AssertReleaseFailed();
3299
3300 PCGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PCGICDEV);
3301 PCGITSDEV pGitsDev = &pGicDev->Gits;
3302
3303 VBOXSTRICTRC rcStrict;
3304 if (off < GITS_REG_FRAME_SIZE)
3305 {
3306 /* Control registers space. */
3307 uint16_t const offReg = off & 0xfffc;
3308 uint32_t uValue = 0;
3309 rcStrict = gitsMmioReadCtrl(pGitsDev, offReg, &uValue);
3310 *(uint32_t *)pv = uValue;
3311 LogFlowFunc(("offReg=%#RX16 (%s) read %#RX32\n", offReg, gitsGetCtrlRegDescription(offReg), uValue));
3312 }
3313 else
3314 {
3315 /* Translation registers space. */
3316 off -= GITS_REG_FRAME_SIZE;
3317 uint16_t const offReg = off & 0xfffc;
3318 uint32_t uValue = 0;
3319 rcStrict = gitsMmioReadTranslate(pGitsDev, offReg, &uValue);
3320 *(uint32_t *)pv = uValue;
3321 LogFlowFunc(("offReg=%#RX16 (%s) read %#RX32\n", offReg, gitsGetTranslationRegDescription(offReg), uValue));
3322 }
3323 return rcStrict;
3324}
3325
3326
3327/**
3328 * @callback_method_impl{FNIOMMMIONEWWRITE}
3329 */
3330DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicItsMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
3331{
3332 RT_NOREF_PV(pvUser);
3333 Assert(!(off & 0x3));
3334 Assert(cb == 4); RT_NOREF_PV(cb);
3335
3336 AssertReleaseFailed();
3337
3338 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
3339 PGITSDEV pGitsDev = &pGicDev->Gits;
3340
3341 VBOXSTRICTRC rcStrict;
3342 if (off < GITS_REG_FRAME_SIZE)
3343 {
3344 /* Control registers space. */
3345 uint16_t const offReg = off & 0xfffc;
3346 uint32_t const uValue = *(uint32_t *)pv;
3347 rcStrict = gitsMmioWriteCtrl(pGitsDev, offReg, uValue);
3348 *(uint32_t *)pv = uValue;
3349 LogFlowFunc(("offReg=%#RX16 (%s) written %#RX32\n", offReg, gitsGetCtrlRegDescription(offReg), uValue));
3350 }
3351 else
3352 {
3353 /* Translation registers space. */
3354 off -= GITS_REG_FRAME_SIZE;
3355 uint16_t const offReg = off & 0xfffc;
3356 uint32_t const uValue = *(uint32_t *)pv;
3357 rcStrict = gitsMmioWriteTranslate(pGitsDev, offReg, uValue);
3358 *(uint32_t *)pv = uValue;
3359 LogFlowFunc(("offReg=%#RX16 (%s) written %#RX32\n", offReg, gitsGetTranslationRegDescription(offReg), uValue));
3360 }
3361 return rcStrict;
3362}
3363
3364
3365
3366#ifndef IN_RING3
3367/**
3368 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
3369 */
3370static DECLCALLBACK(int) gicRZConstruct(PPDMDEVINS pDevIns)
3371{
3372 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
3373 AssertReleaseFailed();
3374 return VINF_SUCCESS;
3375}
3376#endif /* !IN_RING3 */
3377
3378
3379/**
3380 * GIC device registration structure.
3381 */
3382const PDMDEVREG g_DeviceGIC =
3383{
3384 /* .u32Version = */ PDM_DEVREG_VERSION,
3385 /* .uReserved0 = */ 0,
3386 /* .szName = */ "gic",
3387 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
3388 /* .fClass = */ PDM_DEVREG_CLASS_PIC,
3389 /* .cMaxInstances = */ 1,
3390 /* .uSharedVersion = */ 42,
3391 /* .cbInstanceShared = */ sizeof(GICDEV),
3392 /* .cbInstanceCC = */ 0,
3393 /* .cbInstanceRC = */ 0,
3394 /* .cMaxPciDevices = */ 0,
3395 /* .cMaxMsixVectors = */ 0,
3396 /* .pszDescription = */ "Generic Interrupt Controller",
3397#if defined(IN_RING3)
3398 /* .szRCMod = */ "VMMRC.rc",
3399 /* .szR0Mod = */ "VMMR0.r0",
3400 /* .pfnConstruct = */ gicR3Construct,
3401 /* .pfnDestruct = */ gicR3Destruct,
3402 /* .pfnRelocate = */ gicR3Relocate,
3403 /* .pfnMemSetup = */ NULL,
3404 /* .pfnPowerOn = */ NULL,
3405 /* .pfnReset = */ gicR3Reset,
3406 /* .pfnSuspend = */ NULL,
3407 /* .pfnResume = */ NULL,
3408 /* .pfnAttach = */ NULL,
3409 /* .pfnDetach = */ NULL,
3410 /* .pfnQueryInterface = */ NULL,
3411 /* .pfnInitComplete = */ NULL,
3412 /* .pfnPowerOff = */ NULL,
3413 /* .pfnSoftReset = */ NULL,
3414 /* .pfnReserved0 = */ NULL,
3415 /* .pfnReserved1 = */ NULL,
3416 /* .pfnReserved2 = */ NULL,
3417 /* .pfnReserved3 = */ NULL,
3418 /* .pfnReserved4 = */ NULL,
3419 /* .pfnReserved5 = */ NULL,
3420 /* .pfnReserved6 = */ NULL,
3421 /* .pfnReserved7 = */ NULL,
3422#elif defined(IN_RING0)
3423 /* .pfnEarlyConstruct = */ NULL,
3424 /* .pfnConstruct = */ gicRZConstruct,
3425 /* .pfnDestruct = */ NULL,
3426 /* .pfnFinalDestruct = */ NULL,
3427 /* .pfnRequest = */ NULL,
3428 /* .pfnReserved0 = */ NULL,
3429 /* .pfnReserved1 = */ NULL,
3430 /* .pfnReserved2 = */ NULL,
3431 /* .pfnReserved3 = */ NULL,
3432 /* .pfnReserved4 = */ NULL,
3433 /* .pfnReserved5 = */ NULL,
3434 /* .pfnReserved6 = */ NULL,
3435 /* .pfnReserved7 = */ NULL,
3436#elif defined(IN_RC)
3437 /* .pfnConstruct = */ gicRZConstruct,
3438 /* .pfnReserved0 = */ NULL,
3439 /* .pfnReserved1 = */ NULL,
3440 /* .pfnReserved2 = */ NULL,
3441 /* .pfnReserved3 = */ NULL,
3442 /* .pfnReserved4 = */ NULL,
3443 /* .pfnReserved5 = */ NULL,
3444 /* .pfnReserved6 = */ NULL,
3445 /* .pfnReserved7 = */ NULL,
3446#else
3447# error "Not in IN_RING3, IN_RING0 or IN_RC!"
3448#endif
3449 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
3450};
3451
3452
3453/**
3454 * The VirtualBox GIC backend.
3455 */
3456const PDMGICBACKEND g_GicBackend =
3457{
3458 /* .pfnReadSysReg = */ gicReadSysReg,
3459 /* .pfnWriteSysReg = */ gicWriteSysReg,
3460 /* .pfnSetSpi = */ gicSetSpi,
3461 /* .pfnSetPpi = */ gicSetPpi,
3462 /* .pfnSendMsi = */ gitsSendMsi,
3463};
3464
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette