VirtualBox

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

Last change on this file since 108427 was 108427, checked in by vboxsync, 3 months ago

VMM/GIC: bugref:10404 Extend the interrupt lines to the full extent supported by the GIC spec.
Added range-selector support while broadcasting SGIs.
Added highest priority interrupt calculation (still not complete, binary-point support is still a todo)
Other miscellaneous changes related to the above.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 162.7 KB
Line 
1/* $Id: GICAll.cpp 108427 2025-03-04 08:21:36Z 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.215389.xyz.
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
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_DEV_APIC
33#include "GICInternal.h"
34#include <VBox/vmm/pdmgic.h>
35#include <VBox/vmm/pdmdev.h>
36#include <VBox/vmm/pdmapi.h>
37#include <VBox/vmm/vmcc.h>
38#include <VBox/vmm/vmm.h>
39#include <VBox/vmm/vmcpuset.h>
40#ifdef IN_RING0
41# include <VBox/vmm/gvmm.h>
42#endif
43
44
45/*********************************************************************************************************************************
46* Defined Constants And Macros *
47*********************************************************************************************************************************/
48#define GIC_IS_INTR_SGI(a_uIntId) (a_uIntId - GIC_INTID_RANGE_SGI_START < GIC_INTID_SGI_RANGE_SIZE)
49#define GIC_IS_INTR_PPI(a_uIntId) (a_uIntId - GIC_INTID_RANGE_PPI_START < GIC_INTID_PPI_RANGE_SIZE)
50#define GIC_IS_INTR_SGI_OR_PPI(a_uIntId) (a_uIntId - GIC_INTID_RANGE_SGI_START < GIC_INTID_PPI_RANGE_SIZE)
51#define GIC_IS_INTR_SPI(a_uIntId) (a_uIntId - GIC_INTID_RANGE_SPI_START < GIC_INTID_SPI_RANGE_SIZE)
52#define GIC_IS_INTR_SPECIAL(a_uIntId) (a_uIntId - GIC_INTID_RANGE_SPECIAL_START < GIC_INTID_EXT_PPI_RANGE_SIZE)
53#define GIC_IS_INTR_EXT_PPI(a_uIntId) (a_uIntId - GIC_INTID_RANGE_EXT_PPI_START < GIC_INTID_EXT_PPI_RANGE_SIZE)
54#define GIC_IS_INTR_EXT_SPI(a_uIntId) (a_uIntId - GIC_INTID_RANGE_EXT_SPI_START < GIC_INTID_EXT_SPI_RANGE_SIZE)
55
56
57#ifdef LOG_ENABLED
58/**
59 * Gets the description of a CPU interface register.
60 *
61 * @returns The description.
62 * @param u32Reg The CPU interface register offset.
63 */
64static const char *gicIccGetRegDescription(uint32_t u32Reg)
65{
66 switch (u32Reg)
67 {
68#define GIC_ICC_REG_CASE(a_Reg) case ARMV8_AARCH64_SYSREG_ ## a_Reg: return #a_Reg
69 GIC_ICC_REG_CASE(ICC_PMR_EL1);
70 GIC_ICC_REG_CASE(ICC_IAR0_EL1);
71 GIC_ICC_REG_CASE(ICC_EOIR0_EL1);
72 GIC_ICC_REG_CASE(ICC_HPPIR0_EL1);
73 GIC_ICC_REG_CASE(ICC_BPR0_EL1);
74 GIC_ICC_REG_CASE(ICC_AP0R0_EL1);
75 GIC_ICC_REG_CASE(ICC_AP0R1_EL1);
76 GIC_ICC_REG_CASE(ICC_AP0R2_EL1);
77 GIC_ICC_REG_CASE(ICC_AP0R3_EL1);
78 GIC_ICC_REG_CASE(ICC_AP1R0_EL1);
79 GIC_ICC_REG_CASE(ICC_AP1R1_EL1);
80 GIC_ICC_REG_CASE(ICC_AP1R2_EL1);
81 GIC_ICC_REG_CASE(ICC_AP1R3_EL1);
82 GIC_ICC_REG_CASE(ICC_DIR_EL1);
83 GIC_ICC_REG_CASE(ICC_RPR_EL1);
84 GIC_ICC_REG_CASE(ICC_SGI1R_EL1);
85 GIC_ICC_REG_CASE(ICC_ASGI1R_EL1);
86 GIC_ICC_REG_CASE(ICC_SGI0R_EL1);
87 GIC_ICC_REG_CASE(ICC_IAR1_EL1);
88 GIC_ICC_REG_CASE(ICC_EOIR1_EL1);
89 GIC_ICC_REG_CASE(ICC_HPPIR1_EL1);
90 GIC_ICC_REG_CASE(ICC_BPR1_EL1);
91 GIC_ICC_REG_CASE(ICC_CTLR_EL1);
92 GIC_ICC_REG_CASE(ICC_SRE_EL1);
93 GIC_ICC_REG_CASE(ICC_IGRPEN0_EL1);
94 GIC_ICC_REG_CASE(ICC_IGRPEN1_EL1);
95#undef GIC_ICC_REG_CASE
96 default:
97 break;
98 }
99
100 return "<UNKNOWN>";
101}
102
103
104/**
105 * Gets the description of a distributor register given it's register offset.
106 *
107 * @returns The register description.
108 * @param offReg The distributor register offset.
109 */
110static const char *gicDistGetRegDescription(uint16_t offReg)
111{
112 if (offReg - GIC_DIST_REG_IGROUPRn_OFF_START < GIC_DIST_REG_IGROUPRn_RANGE_SIZE) return "GICD_IGROUPRn";
113 if (offReg - GIC_DIST_REG_IGROUPRnE_OFF_START < GIC_DIST_REG_IGROUPRnE_RANGE_SIZE) return "GICD_IGROUPRnE";
114 if (offReg - GIC_DIST_REG_IROUTERn_OFF_START < GIC_DIST_REG_IROUTERn_RANGE_SIZE) return "GICD_IROUTERn";
115 if (offReg - GIC_DIST_REG_IROUTERnE_OFF_START < GIC_DIST_REG_IROUTERnE_RANGE_SIZE) return "GICD_IROUTERnE";
116 if (offReg - GIC_DIST_REG_ISENABLERn_OFF_START < GIC_DIST_REG_ISENABLERn_RANGE_SIZE) return "GICD_ISENABLERn";
117 if (offReg - GIC_DIST_REG_ISENABLERnE_OFF_START < GIC_DIST_REG_ISENABLERnE_RANGE_SIZE) return "GICD_ISENABLERnE";
118 if (offReg - GIC_DIST_REG_ICENABLERn_OFF_START < GIC_DIST_REG_ICENABLERn_RANGE_SIZE) return "GICD_ICENABLERn";
119 if (offReg - GIC_DIST_REG_ICENABLERnE_OFF_START < GIC_DIST_REG_ICENABLERnE_RANGE_SIZE) return "GICD_ICENABLERnE";
120 if (offReg - GIC_DIST_REG_ISACTIVERn_OFF_START < GIC_DIST_REG_ISACTIVERn_RANGE_SIZE) return "GICD_ISACTIVERn";
121 if (offReg - GIC_DIST_REG_ISACTIVERnE_OFF_START < GIC_DIST_REG_ISACTIVERnE_RANGE_SIZE) return "GICD_ISACTIVERnE";
122 if (offReg - GIC_DIST_REG_ICACTIVERn_OFF_START < GIC_DIST_REG_ICACTIVERn_RANGE_SIZE) return "GICD_ICACTIVERn";
123 if (offReg - GIC_DIST_REG_ICACTIVERnE_OFF_START < GIC_DIST_REG_ICACTIVERnE_RANGE_SIZE) return "GICD_ICACTIVERnE";
124 if (offReg - GIC_DIST_REG_IPRIORITYRn_OFF_START < GIC_DIST_REG_IPRIORITYRn_RANGE_SIZE) return "GICD_IPRIORITYRn";
125 if (offReg - GIC_DIST_REG_IPRIORITYRnE_OFF_START < GIC_DIST_REG_IPRIORITYRnE_RANGE_SIZE) return "GICD_IPRIORITYRnE";
126 if (offReg - GIC_DIST_REG_ISPENDRn_OFF_START < GIC_DIST_REG_ISPENDRn_RANGE_SIZE) return "GICD_ISPENDRn";
127 if (offReg - GIC_DIST_REG_ISPENDRnE_OFF_START < GIC_DIST_REG_ISPENDRnE_RANGE_SIZE) return "GICD_ISPENDRnE";
128 if (offReg - GIC_DIST_REG_ICPENDRn_OFF_START < GIC_DIST_REG_ICPENDRn_RANGE_SIZE) return "GICD_ICPENDRn";
129 if (offReg - GIC_DIST_REG_ICPENDRnE_OFF_START < GIC_DIST_REG_ICPENDRnE_RANGE_SIZE) return "GICD_ICPENDRnE";
130 if (offReg - GIC_DIST_REG_ICFGRn_OFF_START < GIC_DIST_REG_ICFGRn_RANGE_SIZE) return "GICD_ICFGRn";
131 if (offReg - GIC_DIST_REG_ICFGRnE_OFF_START < GIC_DIST_REG_ICFGRnE_RANGE_SIZE) return "GICD_ICFGRnE";
132 switch (offReg)
133 {
134 case GIC_DIST_REG_CTLR_OFF: return "GICD_CTLR";
135 case GIC_DIST_REG_TYPER_OFF: return "GICD_TYPER";
136 case GIC_DIST_REG_STATUSR_OFF: return "GICD_STATUSR";
137 case GIC_DIST_REG_ITARGETSRn_OFF_START: return "GICD_ITARGETSRn";
138 case GIC_DIST_REG_IGRPMODRn_OFF_START: return "GICD_IGRPMODRn";
139 case GIC_DIST_REG_NSACRn_OFF_START: return "GICD_NSACRn";
140 case GIC_DIST_REG_SGIR_OFF: return "GICD_SGIR";
141 case GIC_DIST_REG_CPENDSGIRn_OFF_START: return "GICD_CSPENDSGIRn";
142 case GIC_DIST_REG_SPENDSGIRn_OFF_START: return "GICD_SPENDSGIRn";
143 case GIC_DIST_REG_INMIn_OFF_START: return "GICD_INMIn";
144 case GIC_DIST_REG_PIDR2_OFF: return "GICD_PIDR2";
145 case GIC_DIST_REG_IIDR_OFF: return "GICD_IIDR";
146 case GIC_DIST_REG_TYPER2_OFF: return "GICD_TYPER2";
147 default:
148 return "<UNKNOWN>";
149 }
150}
151
152
153/**
154 * Gets the description of a redistributor register given it's register offset.
155 *
156 * @returns The register description.
157 * @param offReg The redistributor register offset.
158 */
159static const char *gicReDistGetRegDescription(uint16_t offReg)
160{
161 if (offReg - GIC_REDIST_SGI_PPI_REG_IGROUPR0_OFF < GIC_REDIST_SGI_PPI_REG_IGROUPRnE_RANGE_SIZE) return "GICR_IGROUPn";
162 if (offReg - GIC_REDIST_SGI_PPI_REG_ISENABLER0_OFF < GIC_REDIST_SGI_PPI_REG_ISENABLERnE_RANGE_SIZE) return "GICR_ISENABLERn";
163 if (offReg - GIC_REDIST_SGI_PPI_REG_ICENABLER0_OFF < GIC_REDIST_SGI_PPI_REG_ICENABLERnE_RANGE_SIZE) return "GICR_ICENABLERn";
164 if (offReg - GIC_REDIST_SGI_PPI_REG_ISACTIVER0_OFF < GIC_REDIST_SGI_PPI_REG_ISACTIVERnE_RANGE_SIZE) return "GICR_ISACTIVERn";
165 if (offReg - GIC_REDIST_SGI_PPI_REG_ICACTIVER0_OFF < GIC_REDIST_SGI_PPI_REG_ICACTIVERnE_RANGE_SIZE) return "GICR_ICACTIVERn";
166 if (offReg - GIC_REDIST_SGI_PPI_REG_ISPENDR0_OFF < GIC_REDIST_SGI_PPI_REG_ISPENDRnE_RANGE_SIZE) return "GICR_ISPENDRn";
167 if (offReg - GIC_REDIST_SGI_PPI_REG_ICPENDR0_OFF < GIC_REDIST_SGI_PPI_REG_ICPENDRnE_RANGE_SIZE) return "GICR_ICPENDRn";
168 if (offReg - GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START < GIC_REDIST_SGI_PPI_REG_IPRIORITYRnE_RANGE_SIZE) return "GICR_IPREIORITYn";
169 if (offReg - GIC_REDIST_SGI_PPI_REG_ICFGR0_OFF < GIC_REDIST_SGI_PPI_REG_ICFGRnE_RANGE_SIZE) return "GICR_ICFGRn";
170 switch (offReg)
171 {
172 case GIC_REDIST_REG_TYPER_OFF: return "GICR_TYPER";
173 case GIC_REDIST_REG_IIDR_OFF: return "GICR_IIDR";
174 case GIC_REDIST_REG_TYPER_AFFINITY_OFF: return "GICR_TYPER_AFF";
175 case GIC_REDIST_REG_PIDR2_OFF: return "GICR_PIDR2";
176 default:
177 return "<UNKNOWN>";
178 }
179}
180#endif
181
182
183/**
184 * Gets the interrupt ID given a distributor interrupt index.
185 *
186 * @returns The interrupt ID.
187 * @param idxIntr The distributor interrupt index.
188 * @remarks A distributor interrupt is an interrupt type that belong in the
189 * distributor (e.g. SPIs, extended SPIs).
190 */
191DECLHIDDEN(uint16_t) gicDistGetIntIdFromIndex(uint16_t idxIntr)
192{
193 /*
194 * Distributor interrupts bits to interrupt ID mapping:
195 * +--------------------------------------------------------+
196 * | Range (incl) | SGI | PPI | SPI | Ext SPI |
197 * |--------------+--------+--------+----------+------------|
198 * | Bit | 0..15 | 16..31 | 32..1023 | 1024..2047 |
199 * | Int Id | 0..15 | 16..31 | 32..1023 | 4096..5119 |
200 * +--------------------------------------------------------+
201 */
202 uint16_t uIntId;
203 /* SGIs, PPIs, SPIs and specials. */
204 if (idxIntr < 1024)
205 uIntId = idxIntr;
206 /* Extended SPIs. */
207 else if (idxIntr < 2048)
208 uIntId = GIC_INTID_RANGE_EXT_SPI_START + idxIntr - 1024;
209 else
210 {
211 uIntId = 0;
212 AssertReleaseFailed();
213 }
214 Assert( GIC_IS_INTR_SGI_OR_PPI(uIntId)
215 || GIC_IS_INTR_SPI(uIntId)
216 || GIC_IS_INTR_SPECIAL(uIntId)
217 || GIC_IS_INTR_EXT_SPI(uIntId));
218 return uIntId;
219}
220
221
222/**
223 * Gets the distributor interrupt index given an interrupt ID.
224 *
225 * @returns The distributor interrupt index.
226 * @param uIntId The interrupt ID.
227 * @remarks A distributor interrupt is an interrupt type that belong in the
228 * distributor (e.g. SPIs, extended SPIs).
229 */
230static uint16_t gicDistGetIndexFromIntId(uint16_t uIntId)
231{
232 uint16_t idxIntr;
233 /* SGIs, PPIs, SPIs and specials. */
234 if (uIntId <= GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT)
235 idxIntr = uIntId;
236 /* Extended SPIs. */
237 else if (uIntId - GIC_INTID_RANGE_EXT_SPI_START < GIC_INTID_EXT_SPI_RANGE_SIZE)
238 idxIntr = 1024 + uIntId - GIC_INTID_RANGE_EXT_SPI_START;
239 else
240 {
241 idxIntr = 0;
242 AssertReleaseFailed();
243 }
244 Assert(idxIntr < sizeof(GICDEV::bmIntrPending) * 8);
245 return idxIntr;
246}
247
248
249/**
250 * Gets the interrupt ID given a redistributor interrupt index.
251 *
252 * @returns The interrupt ID.
253 * @param idxIntr The redistributor interrupt index.
254 * @remarks A redistributor interrupt is an interrupt type that belong in the
255 * redistributor (e.g. SGIs, PPIs, extended PPIs).
256 */
257DECLHIDDEN(uint16_t) gicReDistGetIntIdFromIndex(uint16_t idxIntr)
258{
259 /*
260 * Redistributor interrupts bits to interrupt ID mapping:
261 * +---------------------------------------------+
262 * | Range (incl) | SGI | PPI | Ext PPI |
263 * +---------------------------------------------+
264 * | Bit | 0..15 | 16..31 | 32..95 |
265 * | Int Id | 0..15 | 16..31 | 1056..1119 |
266 * +---------------------------------------------+
267 */
268 uint16_t uIntId;
269 /* SGIs and PPIs. */
270 if (idxIntr < 32)
271 uIntId = idxIntr;
272 /* Extended PPIs. */
273 else if (idxIntr < 96)
274 uIntId = GIC_INTID_RANGE_PPI_LAST + 1 + idxIntr - GIC_INTID_RANGE_EXT_PPI_START;
275 else
276 {
277 uIntId = 0;
278 AssertReleaseFailed();
279 }
280 Assert(GIC_IS_INTR_SGI_OR_PPI(uIntId) || GIC_IS_INTR_EXT_PPI(uIntId));
281 return uIntId;
282}
283
284
285/**
286 * Gets the redistributor interrupt index given an interrupt ID.
287 *
288 * @returns The interrupt ID.
289 * @param idxIntr
290 * @remarks A redistributor interrupt is an interrupt type that belong in the
291 * redistributor (e.g. SGIs, PPIs, extended PPIs).
292 */
293static uint16_t gicReDistGetIndexFromIntId(uint16_t uIntId)
294{
295 /* SGIs and PPIs. */
296 uint16_t idxIntr;
297 if (uIntId <= GIC_INTID_RANGE_PPI_LAST)
298 idxIntr = uIntId;
299 /* Extended PPIs. */
300 else if (uIntId - GIC_INTID_RANGE_EXT_PPI_START < GIC_INTID_EXT_PPI_RANGE_SIZE)
301 idxIntr = 32 + uIntId - GIC_INTID_RANGE_EXT_PPI_START;
302 else
303 {
304 idxIntr = 0;
305 AssertReleaseFailed();
306 }
307 Assert(idxIntr < sizeof(GICCPU::bmIntrPending) * 8);
308 return idxIntr;
309}
310
311
312/**
313 * Sets the interrupt pending force-flag and pokes the EMT if required.
314 *
315 * @param pVCpu The cross context virtual CPU structure.
316 * @param fIrq Flag whether to assert the IRQ line or leave it alone.
317 * @param fFiq Flag whether to assert the FIQ line or leave it alone.
318 */
319static void gicSetInterruptFF(PVMCPUCC pVCpu, bool fIrq, bool fFiq)
320{
321 LogFlowFunc(("pVCpu=%p{.idCpu=%u} fIrq=%RTbool fFiq=%RTbool\n",
322 pVCpu, pVCpu->idCpu, fIrq, fFiq));
323
324 Assert(fIrq || fFiq);
325
326#ifdef IN_RING3
327 /* IRQ state should be loaded as-is by "LoadExec". Changes can be made from LoadDone. */
328 Assert(pVCpu->pVMR3->enmVMState != VMSTATE_LOADING || PDMR3HasLoadedState(pVCpu->pVMR3));
329#endif
330
331 if (fIrq)
332 VMCPU_FF_SET(pVCpu, VMCPU_FF_INTERRUPT_IRQ);
333 if (fFiq)
334 VMCPU_FF_SET(pVCpu, VMCPU_FF_INTERRUPT_FIQ);
335
336 /*
337 * We need to wake up the target CPU if we're not on EMT.
338 */
339 /** @todo We could just use RTThreadNativeSelf() here, couldn't we? */
340#if defined(IN_RING0)
341# error "Implement me!"
342#elif defined(IN_RING3)
343 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
344 VMCPUID idCpu = pVCpu->idCpu;
345 if (VMMGetCpuId(pVM) != idCpu)
346 {
347 Log7Func(("idCpu=%u enmState=%d\n", idCpu, pVCpu->enmState));
348 VMR3NotifyCpuFFU(pVCpu->pUVCpu, VMNOTIFYFF_FLAGS_POKE);
349 }
350#endif
351}
352
353
354#if 0
355/**
356 * Sets the update interrupt force-flag and pokes the EMT if required.
357 *
358 * @param pVCpu The cross context virtual CPU structure.
359 */
360static void gicSetUpdateInterruptFF(PVMCPUCC pVCpu)
361{
362#ifdef IN_RING3
363 /* IRQ state should be loaded as-is by "LoadExec". Changes can be made from LoadDone. */
364 Assert(pVCpu->pVMR3->enmVMState != VMSTATE_LOADING || PDMR3HasLoadedState(pVCpu->pVMR3));
365#endif
366
367 /* Set the update-interrupt force-flag. */
368 VMCPU_FF_SET(pVCpu, VMCPU_FF_UPDATE_GIC);
369
370 /* Wake up the target CPU if we're not currently on the target EMT. */
371#if defined(IN_RING0)
372# error "Implement me"
373#elif defined(IN_RING3)
374 if (pVCpu->hNativeThread != RTThreadNativeSelf())
375 VMR3NotifyCpuFFU(pVCpu->pUVCpu, VMNOTIFYFF_FLAGS_POKE);
376#endif
377}
378#endif
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 */
432DECLINLINE(void) gicReDistHasIrqPending(PCGICCPU pGicCpu, bool *pfIrq, bool *pfFiq)
433{
434 LogFlowFunc(("\n"));
435#if 0
436 /* Read the interrupt state. */
437 uint32_t u32RegIGrp0 = ASMAtomicReadU32(&pThis->u32RegIGrp0);
438 uint32_t bmIntEnabled = ASMAtomicReadU32(&pThis->bmIntEnabled);
439 uint32_t bmIntPending = ASMAtomicReadU32(&pThis->bmIntPending);
440 uint32_t bmIntActive = ASMAtomicReadU32(&pThis->bmIntActive);
441 bool fIrqGrp0Enabled = ASMAtomicReadBool(&pThis->fIrqGrp0Enabled);
442 bool fIrqGrp1Enabled = ASMAtomicReadBool(&pThis->fIrqGrp1Enabled);
443
444 /* Only allow interrupts with higher priority than the current configured and running one. */
445 uint8_t bPriority = RT_MIN(pThis->bInterruptPriority, pThis->abRunningPriorities[pThis->idxRunningPriority]);
446
447 /* Is anything enabled at all? */
448 uint32_t bmIntForward = (bmIntPending & bmIntEnabled) & ~bmIntActive; /* Exclude the currently active interrupt. */
449 if (bmIntForward)
450 {
451 for (uint32_t i = 0; i < RT_ELEMENTS(pThis->abIntPriority); i++)
452 {
453 Log4(("SGI/PPI %u, configured priority %u, running priority %u\n", i, pThis->abIntPriority[i], bPriority));
454 if ( (bmIntForward & RT_BIT_32(i))
455 && pThis->abIntPriority[i] < bPriority)
456 break;
457 else
458 bmIntForward &= ~RT_BIT_32(i);
459
460 if (!bmIntForward)
461 break;
462 }
463 }
464
465 if (bmIntForward)
466 {
467 /* Determine whether we have to assert the IRQ or FIQ line. */
468 *pfIrq = RT_BOOL(bmIntForward & u32RegIGrp0) && fIrqGrp1Enabled;
469 *pfFiq = RT_BOOL(bmIntForward & ~u32RegIGrp0) && fIrqGrp0Enabled;
470 }
471 else
472 {
473 *pfIrq = false;
474 *pfFiq = false;
475 }
476
477 LogFlowFunc(("pThis=%p bPriority=%u bmIntEnabled=%#x bmIntPending=%#x bmIntActive=%#x fIrq=%RTbool fFiq=%RTbool\n",
478 pThis, bPriority, bmIntEnabled, bmIntPending, bmIntActive, *pfIrq, *pfFiq));
479#else
480 bool const fIsGroup1Enabled = pGicCpu->fIrqGrp1Enabled;
481 bool const fIsGroup0Enabled = pGicCpu->fIrqGrp0Enabled;
482 LogFlowFunc(("fIsGroup0Enabled=%RTbool fIsGroup1Enabled=%RTbool\n", fIsGroup0Enabled, fIsGroup1Enabled));
483
484 uint32_t bmIntrs[3];
485 for (uint8_t i = 0; i < RT_ELEMENTS(bmIntrs); i++)
486 {
487 /* Collect interrupts that are pending, enabled and inactive. */
488 bmIntrs[i] = (pGicCpu->bmIntrPending[i] & pGicCpu->bmIntrEnabled[i]) & ~pGicCpu->bmIntrActive[i];
489
490 /* Discard interrupts if the group they belong to is disabled. */
491 if (!fIsGroup1Enabled)
492 bmIntrs[i] &= ~pGicCpu->bmIntrGroup[i];
493 if (!fIsGroup0Enabled)
494 bmIntrs[i] &= pGicCpu->bmIntrGroup[i];
495 }
496
497 /* Only allow interrupts with higher priority than the current configured and running one. */
498 uint8_t const bPriority = RT_MIN(pGicCpu->bInterruptPriority, pGicCpu->abRunningPriorities[pGicCpu->idxRunningPriority]);
499
500 uint32_t const cIntrs = sizeof(bmIntrs) * 8;
501 int32_t idxIntr = ASMBitFirstSet(&bmIntrs[0], cIntrs);
502 AssertCompile(!(cIntrs % 32));
503 if (idxIntr >= 0)
504 {
505 do
506 {
507 Assert((uint32_t)idxIntr < RT_ELEMENTS(pGicCpu->abIntrPriority));
508 if (pGicCpu->abIntrPriority[idxIntr] < bPriority)
509 {
510 bool const fInGroup1 = ASMBitTest(&pGicCpu->bmIntrGroup[0], idxIntr);
511 bool const fInGroup0 = !fInGroup1;
512 *pfIrq = fInGroup1 && fIsGroup1Enabled;
513 *pfFiq = fInGroup0 && fIsGroup0Enabled;
514 return;
515 }
516 idxIntr = ASMBitNextSet(&bmIntrs[0], cIntrs, idxIntr);
517 } while (idxIntr != -1);
518 }
519 *pfIrq = false;
520 *pfFiq = false;
521#endif
522}
523
524
525/**
526 * Gets whether the distributor has pending interrupts with sufficient priority to
527 * be signalled to the PE.
528 *
529 * @param pGicDev The GIC distributor state.
530 * @param pVCpu The cross context virtual CPU structure.
531 * @param idCpu The ID of the virtual CPU.
532 * @param pfIrq Where to store whether there are IRQs can be signalled.
533 * @param pfFiq Where to store whether there are FIQs can be signalled.
534 */
535DECLINLINE(void) gicDistHasIrqPendingForVCpu(PCGICDEV pGicDev, PCVMCPUCC pVCpu, VMCPUID idCpu, bool *pfIrq, bool *pfFiq)
536{
537 LogFlowFunc(("\n"));
538#if 0
539 /* Read the interrupt state. */
540 uint32_t u32RegIGrp0 = ASMAtomicReadU32(&pThis->u32RegIGrp0);
541 uint32_t bmIntEnabled = ASMAtomicReadU32(&pThis->bmIntEnabled);
542 uint32_t bmIntPending = ASMAtomicReadU32(&pThis->bmIntPending);
543 uint32_t bmIntActive = ASMAtomicReadU32(&pThis->bmIntActive);
544 bool fIrqGrp0Enabled = ASMAtomicReadBool(&pThis->fIrqGrp0Enabled);
545 bool fIrqGrp1Enabled = ASMAtomicReadBool(&pThis->fIrqGrp1Enabled);
546
547 /* Only allow interrupts with higher priority than the current configured and running one. */
548 uint8_t bPriority = RT_MIN(pGicVCpu->bInterruptPriority, pGicVCpu->abRunningPriorities[pGicVCpu->idxRunningPriority]);
549
550 /* Is anything enabled at all? */
551 uint32_t bmIntForward = (bmIntPending & bmIntEnabled) & ~bmIntActive; /* Exclude the currently active interrupt. */
552 if (bmIntForward)
553 {
554 for (uint32_t i = 0; i < RT_ELEMENTS(pThis->abIntPriority); i++)
555 {
556 Log4(("SPI %u, configured priority %u (routing %#x), running priority %u\n", i + GIC_INTID_RANGE_SPI_START, pThis->abIntPriority[i],
557 pThis->au32IntRouting[i], bPriority));
558 if ( (bmIntForward & RT_BIT_32(i))
559 && pThis->abIntPriority[i] < bPriority
560 && pThis->au32IntRouting[i] == idCpu)
561 break;
562 else
563 bmIntForward &= ~RT_BIT_32(i);
564
565 if (!bmIntForward)
566 break;
567 }
568 }
569
570 if (bmIntForward)
571 {
572 /* Determine whether we have to assert the IRQ or FIQ line. */
573 *pfIrq = RT_BOOL(bmIntForward & u32RegIGrp0) && fIrqGrp1Enabled;
574 *pfFiq = RT_BOOL(bmIntForward & ~u32RegIGrp0) && fIrqGrp0Enabled;
575 }
576 else
577 {
578 *pfIrq = false;
579 *pfFiq = false;
580 }
581
582 LogFlowFunc(("pThis=%p bPriority=%u bmIntEnabled=%#x bmIntPending=%#x bmIntActive=%#x fIrq=%RTbool fFiq=%RTbool\n",
583 pThis, bPriority, bmIntEnabled, bmIntPending, bmIntActive, *pfIrq, *pfFiq));
584#else
585 bool const fIsGroup1Enabled = pGicDev->fIrqGrp1Enabled;
586 bool const fIsGroup0Enabled = pGicDev->fIrqGrp0Enabled;
587 LogFlowFunc(("fIsGroup1Enabled=%RTbool fIsGroup0Enabled=%RTbool\n", fIsGroup1Enabled, fIsGroup0Enabled));
588
589 uint32_t bmIntrs[64];
590 for (uint8_t i = 0; i < RT_ELEMENTS(bmIntrs); i++)
591 {
592 /* Collect interrupts that are pending, enabled and inactive. */
593 bmIntrs[i] = (pGicDev->bmIntrPending[i] & pGicDev->bmIntrEnabled[i]) & ~pGicDev->bmIntrActive[i];
594
595 /* Discard interrupts if the group they belong to is disabled. */
596 if (!fIsGroup1Enabled)
597 bmIntrs[i] &= ~pGicDev->bmIntrGroup[i];
598 if (!fIsGroup0Enabled)
599 bmIntrs[i] &= pGicDev->bmIntrGroup[i];
600 }
601
602 /* Only allow interrupts with higher priority than the current configured and running one. */
603 PCGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
604 uint8_t const bPriority = RT_MIN(pGicCpu->bInterruptPriority, pGicCpu->abRunningPriorities[pGicCpu->idxRunningPriority]);
605
606 /*
607 * The distributor's interrupt pending/enabled/active bitmaps have 2048 bits which map
608 * SGIs (16), PPIs (16), SPIs (988), reserved SPIs (4) and extended SPIs (1024).
609 * Of these, the first 16 bits corresponding to SGIs and PPIs are RAZ/WI when affinity
610 * routing is enabled (which it always is in our implementation).
611 */
612 Assert(pGicDev->fAffRoutingEnabled);
613 uint32_t const cIntrs = sizeof(bmIntrs) * 8;
614 int32_t idxIntr = ASMBitFirstSet(&bmIntrs[0], cIntrs);
615 AssertCompile(!(cIntrs % 32));
616 Assert(bmIntrs[0] == 0);
617 if (idxIntr >= 0)
618 {
619 Assert(idxIntr > GIC_INTID_RANGE_PPI_LAST);
620 do
621 {
622 AssertCompile(RT_ELEMENTS(pGicDev->abIntrPriority) == RT_ELEMENTS(pGicDev->au32IntrRouting));
623 Assert((uint32_t)idxIntr < RT_ELEMENTS(pGicDev->abIntrPriority));
624 Assert(idxIntr < GIC_INTID_RANGE_SPECIAL_START || idxIntr > GIC_INTID_RANGE_SPECIAL_LAST);
625 if ( pGicDev->abIntrPriority[idxIntr] < bPriority
626 && pGicDev->au32IntrRouting[idxIntr] == idCpu)
627 {
628 bool const fInGroup1 = ASMBitTest(&pGicDev->bmIntrGroup[0], idxIntr);
629 bool const fInGroup0 = !fInGroup1;
630 *pfFiq = fInGroup0 && fIsGroup0Enabled;
631 *pfIrq = fInGroup1 && fIsGroup1Enabled;
632 return;
633 }
634 idxIntr = ASMBitNextSet(&bmIntrs[0], cIntrs, idxIntr);
635 } while (idxIntr != -1);
636 }
637 *pfIrq = false;
638 *pfFiq = false;
639#endif
640}
641
642
643/**
644 * Updates the internal IRQ state and sets or clears the appropriate force action
645 * flags.
646 *
647 * @returns Strict VBox status code.
648 * @param pGicDev The GIC distributor state.
649 * @param pVCpu The cross context virtual CPU structure.
650 */
651static VBOXSTRICTRC gicReDistUpdateIrqState(PCGICDEV pGicDev, PVMCPUCC pVCpu)
652{
653 LogFlowFunc(("\n"));
654 bool fIrq;
655 bool fFiq;
656 gicReDistHasIrqPending(VMCPU_TO_GICCPU(pVCpu), &fIrq, &fFiq);
657 LogFlowFunc(("fIrq=%RTbool fFiq=%RTbool\n", fIrq, fFiq));
658
659 bool fIrqDist;
660 bool fFiqDist;
661 gicDistHasIrqPendingForVCpu(pGicDev, pVCpu, pVCpu->idCpu, &fIrqDist, &fFiqDist);
662 LogFlowFunc(("fIrqDist=%RTbool fFiqDist=%RTbool\n", fIrqDist, fFiqDist));
663
664 fIrq |= fIrqDist;
665 fFiq |= fFiqDist;
666 gicUpdateInterruptFF(pVCpu, fIrq, fFiq);
667 return VINF_SUCCESS;
668}
669
670
671/**
672 * Updates the internal IRQ state of the distributor and sets or clears the appropirate force action flags.
673 *
674 * @returns Strict VBox status code.
675 * @param pVM The cross context VM state.
676 * @param pGicDev The GIC distributor state.
677 */
678static VBOXSTRICTRC gicDistUpdateIrqState(PCVMCC pVM, PCGICDEV pGicDev)
679{
680 LogFlowFunc(("\n"));
681 for (uint32_t i = 0; i < pVM->cCpus; i++)
682 {
683 PVMCPUCC pVCpu = pVM->CTX_SUFF(apCpus)[i];
684 PCGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
685
686 bool fIrq, fFiq;
687 gicReDistHasIrqPending(pGicCpu, &fIrq, &fFiq);
688
689 bool fIrqDist, fFiqDist;
690 gicDistHasIrqPendingForVCpu(pGicDev, pVCpu, i, &fIrqDist, &fFiqDist);
691 fIrq |= fIrqDist;
692 fFiq |= fFiqDist;
693
694 gicUpdateInterruptFF(pVCpu, fIrq, fFiq);
695 }
696 return VINF_SUCCESS;
697}
698
699
700/**
701 * Reads the distributor's interrupt routing register (GICD_IROUTER).
702 *
703 * @returns Strict VBox status code.
704 * @param pGicDev The GIC distributor state.
705 * @param idxReg The index of the register in the GICD_IROUTER range.
706 * @param puValue Where to store the register's value.
707 */
708static VBOXSTRICTRC gicDistReadIntrRoutingReg(PCGICDEV pGicDev, uint16_t idxReg, uint32_t *puValue)
709{
710 /* When affinity routing is disabled, reads return 0. */
711 Assert(pGicDev->fAffRoutingEnabled);
712
713 /* Hardware does not map the first 32 registers (corresponding to SGIs and PPIs). */
714 idxReg += GIC_INTID_RANGE_SPI_START;
715 AssertReturn(idxReg < RT_ELEMENTS(pGicDev->au32IntrRouting), VERR_BUFFER_OVERFLOW);
716 Assert(idxReg < sizeof(pGicDev->bmIntrRoutingMode) * 8);
717 if (!(idxReg % 2))
718 {
719 /* Lower 32-bits. */
720 uint8_t const fIrm = ASMBitTest(&pGicDev->bmIntrRoutingMode[0], idxReg);
721 *puValue = GIC_DIST_REG_IROUTERn_SET(fIrm, pGicDev->au32IntrRouting[idxReg]);
722 }
723 else
724 {
725 /* Upper 32-bits. */
726 *puValue = pGicDev->au32IntrRouting[idxReg] >> 24;
727 }
728
729 LogFlowFunc(("idxReg=%#x read %#x\n", idxReg, *puValue));
730 return VINF_SUCCESS;
731}
732
733
734/**
735 * Writes the distributor's interrupt routing register (GICD_IROUTER).
736 *
737 * @returns Strict VBox status code.
738 * @param pGicDev The GIC distributor state.
739 * @param offReg The offset of the register in the distributor register map.
740 * @param idxReg The index of the register in the GICD_IROUTER range.
741 * @param uValue The value to write to the register.
742 */
743static VBOXSTRICTRC gicDistWriteIntrRoutingReg(PGICDEV pGicDev, uint16_t offReg, uint16_t idxReg, uint32_t uValue)
744{
745 /* When affinity routing is disabled, writes are ignored. */
746 Assert(pGicDev->fAffRoutingEnabled);
747
748 /* Hardware does not map the first 32 registers (corresponding to SGIs and PPIs). */
749 idxReg += GIC_INTID_RANGE_SPI_START;
750 AssertReturn(idxReg < RT_ELEMENTS(pGicDev->au32IntrRouting), VERR_BUFFER_OVERFLOW);
751 Assert(idxReg < sizeof(pGicDev->bmIntrRoutingMode) * 8);
752 if (!(offReg & 4))
753 {
754 /* Lower 32-bits. */
755 bool const fIrm = GIC_DIST_REG_IROUTERn_IRM_GET(uValue);
756 if (fIrm)
757 ASMBitSet(&pGicDev->bmIntrRoutingMode[0], idxReg);
758 else
759 ASMBitClear(&pGicDev->bmIntrRoutingMode[0], idxReg);
760 uint32_t const fAff3 = pGicDev->au32IntrRouting[idxReg] & 0xff000000;
761 pGicDev->au32IntrRouting[idxReg] = fAff3 | (uValue & 0x00ffffff);
762 }
763 else
764 {
765 /* Upper 32-bits. */
766 uint32_t const fAffOthers = pGicDev->au32IntrRouting[idxReg] & 0x00ffffff;
767 pGicDev->au32IntrRouting[idxReg] = (uValue << 24) | fAffOthers;
768 }
769
770 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicDev->au32IntrRouting[idxReg]));
771 return VINF_SUCCESS;
772}
773
774
775/**
776 * Reads the distributor's interrupt (set/clear) enable register (GICD_ISENABLER and
777 * GICD_ICENABLER).
778 *
779 * @returns Strict VBox status code.
780 * @param pGicDev The GIC distributor state.
781 * @param idxReg The index of the register in the GICD_ISENABLER and
782 * GICD_ICENABLER range.
783 * @param puValue Where to store the register's value.
784 */
785static VBOXSTRICTRC gicDistReadIntrEnableReg(PGICDEV pGicDev, uint16_t idxReg, uint32_t *puValue)
786{
787 Assert(idxReg < RT_ELEMENTS(pGicDev->bmIntrEnabled));
788 *puValue = pGicDev->bmIntrEnabled[idxReg];
789 LogFlowFunc(("idxReg=%#x read %#x\n", idxReg, pGicDev->bmIntrEnabled[idxReg]));
790 return VINF_SUCCESS;
791}
792
793
794/**
795 * Writes the distributor's interrupt set-enable register (GICD_ISENABLER).
796 *
797 * @returns Strict VBox status code.
798 * @param pVM The cross context VM structure.
799 * @param pGicDev The GIC distributor state.
800 * @param idxReg The index of the register in the GICD_ISENABLER range.
801 * @param uValue The value to write to the register.
802 */
803static VBOXSTRICTRC gicDistWriteIntrSetEnableReg(PVM pVM, PGICDEV pGicDev, uint16_t idxReg, uint32_t uValue)
804{
805 /* When affinity routing is enabled, writes to SGIs and PPIs are ignored. */
806 Assert(pGicDev->fAffRoutingEnabled);
807 if (idxReg > 0)
808 {
809 Assert(idxReg < RT_ELEMENTS(pGicDev->bmIntrEnabled));
810 pGicDev->bmIntrEnabled[idxReg] |= uValue;
811 return gicDistUpdateIrqState(pVM, pGicDev);
812 }
813 else
814 AssertReleaseFailed();
815 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicDev->bmIntrEnabled[idxReg]));
816 return VINF_SUCCESS;
817}
818
819
820/**
821 * Writes the distributor's interrupt clear-enable register (GICD_ICENABLER).
822 *
823 * @returns Strict VBox status code.
824 * @param pVM The cross context VM structure.
825 * @param pGicDev The GIC distributor state.
826 * @param idxReg The index of the register in the GICD_ICENABLER range.
827 * @param uValue The value to write to the register.
828 */
829static VBOXSTRICTRC gicDistWriteIntrClearEnableReg(PVM pVM, PGICDEV pGicDev, uint16_t idxReg, uint32_t uValue)
830{
831 /* When affinity routing is enabled, writes to SGIs and PPIs are ignored. */
832 Assert(pGicDev->fAffRoutingEnabled);
833 if (idxReg > 0)
834 {
835 Assert(idxReg < RT_ELEMENTS(pGicDev->bmIntrEnabled));
836 pGicDev->bmIntrEnabled[idxReg] &= ~uValue;
837 return gicDistUpdateIrqState(pVM, pGicDev);
838 }
839 else
840 AssertReleaseFailed();
841 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicDev->bmIntrEnabled[idxReg]));
842 return VINF_SUCCESS;
843}
844
845
846/**
847 * Reads the distributor's interrupt active register (GICD_ISACTIVER and
848 * GICD_ICACTIVER).
849 *
850 * @returns Strict VBox status code.
851 * @param pGicDev The GIC distributor state.
852 * @param idxReg The index of the register in the GICD_ISACTIVER and
853 * GICD_ICACTIVER range.
854 * @param puValue Where to store the register's value.
855 */
856static VBOXSTRICTRC gicDistReadIntrActiveReg(PGICDEV pGicDev, uint16_t idxReg, uint32_t *puValue)
857{
858 Assert(idxReg < RT_ELEMENTS(pGicDev->bmIntrActive));
859 *puValue = pGicDev->bmIntrActive[idxReg];
860 LogFlowFunc(("idxReg=%#x read %#x\n", idxReg, pGicDev->bmIntrActive[idxReg]));
861 return VINF_SUCCESS;
862}
863
864
865/**
866 * Writes the distributor's interrupt set-active register (GICD_ISACTIVER).
867 *
868 * @returns Strict VBox status code.
869 * @param pVM The cross context VM structure.
870 * @param pGicDev The GIC distributor state.
871 * @param idxReg The index of the register in the GICD_ISACTIVER range.
872 * @param uValue The value to write to the register.
873 */
874static VBOXSTRICTRC gicDistWriteIntrSetActiveReg(PVM pVM, PGICDEV pGicDev, uint16_t idxReg, uint32_t uValue)
875{
876 /* When affinity routing is enabled, writes to SGIs and PPIs are ignored. */
877 Assert(pGicDev->fAffRoutingEnabled);
878 if (idxReg > 0)
879 {
880 Assert(idxReg < RT_ELEMENTS(pGicDev->bmIntrActive));
881 pGicDev->bmIntrActive[idxReg] |= uValue;
882 return gicDistUpdateIrqState(pVM, pGicDev);
883 }
884 else
885 AssertReleaseFailed();
886 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicDev->bmIntrActive[idxReg]));
887 return VINF_SUCCESS;
888}
889
890
891/**
892 * Writes the distributor's interrupt clear-active register (GICD_ICACTIVER).
893 *
894 * @returns Strict VBox status code.
895 * @param pVM The cross context VM structure.
896 * @param pGicDev The GIC distributor state.
897 * @param idxReg The index of the register in the GICD_ICACTIVER range.
898 * @param uValue The value to write to the register.
899 */
900static VBOXSTRICTRC gicDistWriteIntrClearActiveReg(PVM pVM, PGICDEV pGicDev, uint16_t idxReg, uint32_t uValue)
901{
902 /* When affinity routing is enabled, writes to SGIs and PPIs are ignored. */
903 Assert(pGicDev->fAffRoutingEnabled);
904 if (idxReg > 0)
905 {
906 Assert(idxReg < RT_ELEMENTS(pGicDev->bmIntrActive));
907 pGicDev->bmIntrActive[idxReg] &= ~uValue;
908 return gicDistUpdateIrqState(pVM, pGicDev);
909 }
910 else
911 AssertReleaseFailed();
912 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicDev->bmIntrActive[idxReg]));
913 return VINF_SUCCESS;
914}
915
916
917/**
918 * Reads the distributor's interrupt priority register (GICD_IPRIORITYR).
919 *
920 * @returns Strict VBox status code.
921 * @param pGicDev The GIC distributor state.
922 * @param idxReg The index of the register in the GICD_IPRIORITY range.
923 * @param puValue Where to store the register's value.
924 */
925static VBOXSTRICTRC gicDistReadIntrPriorityReg(PGICDEV pGicDev, uint16_t idxReg, uint32_t *puValue)
926{
927 /* When affinity routing is enabled, reads to registers 0..7 (pertaining to SGIs and PPIs) return 0. */
928 Assert(pGicDev->fAffRoutingEnabled);
929 Assert(idxReg < RT_ELEMENTS(pGicDev->abIntrPriority) / sizeof(uint32_t));
930 Assert(idxReg != 255);
931 if (idxReg > 7)
932 {
933 uint16_t const idxPriority = idxReg * sizeof(uint32_t);
934 AssertReturn(idxPriority < RT_ELEMENTS(pGicDev->abIntrPriority) - sizeof(uint32_t), VERR_BUFFER_OVERFLOW);
935 AssertCompile(sizeof(*puValue) == sizeof(uint32_t));
936 *puValue = *(uint32_t *)&pGicDev->abIntrPriority[idxPriority];
937 }
938 else
939 {
940 AssertReleaseFailed();
941 *puValue = 0;
942 }
943 LogFlowFunc(("idxReg=%#x read %#x\n", idxReg, *puValue));
944 return VINF_SUCCESS;
945}
946
947
948/**
949 * Writes the distributor's interrupt priority register (GICD_IPRIORITYR).
950 *
951 * @returns Strict VBox status code.
952 * @param pGicDev The GIC distributor state.
953 * @param idxReg The index of the register in the GICD_IPRIORITY range.
954 * @param uValue The value to write to the register.
955 */
956static VBOXSTRICTRC gicDistWriteIntrPriorityReg(PGICDEV pGicDev, uint16_t idxReg, uint32_t uValue)
957{
958 /* When affinity routing is enabled, writes to registers 0..7 are ignored. */
959 Assert(pGicDev->fAffRoutingEnabled);
960 Assert(idxReg < RT_ELEMENTS(pGicDev->abIntrPriority) / sizeof(uint32_t));
961 Assert(idxReg != 255);
962 if (idxReg > 7)
963 {
964 uint16_t const idxPriority = idxReg * sizeof(uint32_t);
965 AssertReturn(idxPriority < RT_ELEMENTS(pGicDev->abIntrPriority) - sizeof(uint32_t), VERR_BUFFER_OVERFLOW);
966 AssertCompile(sizeof(uValue) == sizeof(uint32_t));
967 *(uint32_t *)&pGicDev->abIntrPriority[idxPriority] = uValue;
968 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, *(uint32_t *)&pGicDev->abIntrPriority[idxPriority]));
969 }
970 else
971 AssertReleaseFailed();
972 return VINF_SUCCESS;
973}
974
975
976/**
977 * Reads the distributor's interrupt pending register (GICD_ISPENDR and
978 * GICD_ICPENDR).
979 *
980 * @returns Strict VBox status code.
981 * @param pGicDev The GIC distributor state.
982 * @param idxReg The index of the register in the GICD_ISPENDR and
983 * GICD_ICPENDR range.
984 * @param puValue Where to store the register's value.
985 */
986static VBOXSTRICTRC gicDistReadIntrPendingReg(PGICDEV pGicDev, uint16_t idxReg, uint32_t *puValue)
987{
988 /* When affinity routing is enabled, reads for SGIs and PPIs return 0. */
989 Assert(pGicDev->fAffRoutingEnabled);
990 if (idxReg > 0)
991 {
992 Assert(idxReg < RT_ELEMENTS(pGicDev->bmIntrPending));
993 *puValue = pGicDev->bmIntrPending[idxReg];
994 }
995 else
996 {
997 AssertReleaseFailed();
998 *puValue = 0;
999 }
1000 LogFlowFunc(("idxReg=%#x read %#x\n", idxReg, pGicDev->bmIntrPending[idxReg]));
1001 return VINF_SUCCESS;
1002}
1003
1004
1005/**
1006 * Write's the distributor's interrupt set-pending register (GICD_ISPENDR).
1007 *
1008 * @returns Strict VBox status code.
1009 * @param pVM The cross context VM structure.
1010 * @param pGicDev The GIC distributor state.
1011 * @param idxReg The index of the register in the GICD_ISPENDR range.
1012 * @param uValue The value to write to the register.
1013 */
1014static VBOXSTRICTRC gicDistWriteIntrSetPendingReg(PVMCC pVM, PGICDEV pGicDev, uint16_t idxReg, uint32_t uValue)
1015{
1016 /* When affinity routing is enabled, writes to SGIs and PPIs are ignored. */
1017 Assert(pGicDev->fAffRoutingEnabled);
1018 if (idxReg > 0)
1019 {
1020 Assert(idxReg < RT_ELEMENTS(pGicDev->bmIntrPending));
1021 pGicDev->bmIntrPending[idxReg] |= uValue;
1022 return gicDistUpdateIrqState(pVM, pGicDev);
1023 }
1024 else
1025 AssertReleaseFailed();
1026 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicDev->bmIntrPending[idxReg]));
1027 return VINF_SUCCESS;
1028}
1029
1030
1031/**
1032 * Write's the distributor's interrupt clear-pending register (GICD_ICPENDR).
1033 *
1034 * @returns Strict VBox status code.
1035 * @param pVM The cross context VM structure.
1036 * @param pGicDev The GIC distributor state.
1037 * @param idxReg The index of the register in the GICD_ICPENDR range.
1038 * @param uValue The value to write to the register.
1039 */
1040static VBOXSTRICTRC gicDistWriteIntrClearPendingReg(PVMCC pVM, PGICDEV pGicDev, uint16_t idxReg, uint32_t uValue)
1041{
1042 /* When affinity routing is enabled, writes to SGIs and PPIs are ignored. */
1043 Assert(pGicDev->fAffRoutingEnabled);
1044 if (idxReg > 0)
1045 {
1046 Assert(idxReg < RT_ELEMENTS(pGicDev->bmIntrPending));
1047 pGicDev->bmIntrPending[idxReg] &= ~uValue;
1048 return gicDistUpdateIrqState(pVM, pGicDev);
1049 }
1050 else
1051 AssertReleaseFailed();
1052 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicDev->bmIntrPending[idxReg]));
1053 return VINF_SUCCESS;
1054}
1055
1056
1057/**
1058 * Reads the distributor's interrupt config register (GICD_ICFGR).
1059 *
1060 * @returns Strict VBox status code.
1061 * @param pGicDev The GIC distributor state.
1062 * @param idxReg The index of the register in the GICD_ICFGR range.
1063 * @param puValue Where to store the register's value.
1064 */
1065static VBOXSTRICTRC gicDistReadIntrConfigReg(PCGICDEV pGicDev, uint16_t idxReg, uint32_t *puValue)
1066{
1067 /* SGIs are read-only and are always edge-triggered. */
1068 if (idxReg > 0)
1069 {
1070 Assert(idxReg < RT_ELEMENTS(pGicDev->bmIntrConfig));
1071 *puValue = pGicDev->bmIntrConfig[idxReg];
1072 }
1073 else
1074 *puValue = 0xaaaaaaaa;
1075 LogFlowFunc(("idxReg=%#x read %#x\n", idxReg, pGicDev->bmIntrConfig[idxReg]));
1076 return VINF_SUCCESS;
1077}
1078
1079
1080/**
1081 * Writes the distributor's interrupt config register (GICD_ICFGR).
1082 *
1083 * @returns Strict VBox status code.
1084 * @param pGicDev The GIC distributor state.
1085 * @param idxReg The index of the register in the GICD_ICFGR range.
1086 * @param uValue The value to write to the register.
1087 */
1088static VBOXSTRICTRC gicDistWriteIntrConfigReg(PGICDEV pGicDev, uint16_t idxReg, uint32_t uValue)
1089{
1090 /* Writes to SGIs are ignored. */
1091 if (idxReg > 0)
1092 {
1093 Assert(idxReg < RT_ELEMENTS(pGicDev->bmIntrConfig));
1094 pGicDev->bmIntrConfig[idxReg] = uValue;
1095 }
1096 else
1097 AssertReleaseFailed();
1098 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicDev->bmIntrConfig[idxReg]));
1099 return VINF_SUCCESS;
1100}
1101
1102
1103/**
1104 * Reads the distributor's interrupt config register (GICD_IGROUPR).
1105 *
1106 * @returns Strict VBox status code.
1107 * @param pGicDev The GIC distributor state.
1108 * @param idxReg The index of the register in the GICD_IGROUPR range.
1109 * @param puValue Where to store the register's value.
1110 */
1111static VBOXSTRICTRC gicDistReadIntrGroupReg(PGICDEV pGicDev, uint16_t idxReg, uint32_t *puValue)
1112{
1113 /* When affinity routing is enabled, reads to SGIs and PPIs return 0. */
1114 Assert(pGicDev->fAffRoutingEnabled);
1115 if (idxReg > 0)
1116 {
1117 Assert(idxReg < RT_ELEMENTS(pGicDev->bmIntrGroup));
1118 *puValue = pGicDev->bmIntrGroup[idxReg];
1119 LogFlowFunc(("idxReg=%#x read %#x\n", idxReg, *puValue));
1120 }
1121 else
1122 AssertReleaseFailed();
1123 return VINF_SUCCESS;
1124}
1125
1126
1127/**
1128 * Writes the distributor's interrupt config register (GICD_ICFGR).
1129 *
1130 * @returns Strict VBox status code.
1131 * @param pVM The cross context VM structure.
1132 * @param pGicDev The GIC distributor state.
1133 * @param idxReg The index of the register in the GICD_ICFGR range.
1134 * @param uValue The value to write to the register.
1135 */
1136static VBOXSTRICTRC gicDistWriteIntrGroupReg(PCVM pVM, PGICDEV pGicDev, uint16_t idxReg, uint32_t uValue)
1137{
1138 /* When affinity routing is enabled, writes to SGIs and PPIs are ignored. */
1139 Assert(pGicDev->fAffRoutingEnabled);
1140 if (idxReg > 0)
1141 {
1142 pGicDev->bmIntrGroup[idxReg] = uValue;
1143 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicDev->bmIntrGroup[idxReg]));
1144 }
1145 else
1146 AssertReleaseFailed();
1147 return gicDistUpdateIrqState(pVM, pGicDev);
1148}
1149
1150
1151/**
1152 * Reads the redistributor's interrupt priority register (GICR_IPRIORITYR).
1153 *
1154 * @returns Strict VBox status code.
1155 * @param pGicDev The GIC distributor state.
1156 * @param pGicCpu The GIC redistributor and CPU interface state.
1157 * @param idxReg The index of the register in the GICR_IPRIORITY range.
1158 * @param puValue Where to store the register's value.
1159 */
1160static VBOXSTRICTRC gicReDistReadIntrPriorityReg(PCGICDEV pGicDev, PGICCPU pGicCpu, uint16_t idxReg, uint32_t *puValue)
1161{
1162 /* When affinity routing is disabled, reads return 0. */
1163 Assert(pGicDev->fAffRoutingEnabled);
1164 uint16_t const idxPriority = idxReg * sizeof(uint32_t);
1165 AssertReturn(idxPriority < RT_ELEMENTS(pGicCpu->abIntrPriority) - sizeof(uint32_t), VERR_BUFFER_OVERFLOW);
1166 AssertCompile(sizeof(*puValue) == sizeof(uint32_t));
1167 *puValue = *(uint32_t *)&pGicCpu->abIntrPriority[idxPriority];
1168 LogFlowFunc(("idxReg=%#x read %#x\n", idxReg, *puValue));
1169 return VINF_SUCCESS;
1170}
1171
1172
1173/**
1174 * Writes the redistributor's interrupt priority register (GICR_IPRIORITYR).
1175 *
1176 * @returns Strict VBox status code.
1177 * @param pGicDev The GIC distributor state.
1178 * @param pVCpu The cross context virtual CPU structure.
1179 * @param idxReg The index of the register in the GICR_IPRIORITY range.
1180 * @param uValue The value to write to the register.
1181 */
1182static VBOXSTRICTRC gicReDistWriteIntrPriorityReg(PCGICDEV pGicDev, PVMCPUCC pVCpu, uint16_t idxReg, uint32_t uValue)
1183{
1184 /* When affinity routing is disabled, writes are ignored. */
1185 Assert(pGicDev->fAffRoutingEnabled);
1186 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
1187 uint16_t const idxPriority = idxReg * sizeof(uint32_t);
1188 AssertReturn(idxPriority < RT_ELEMENTS(pGicCpu->abIntrPriority) - sizeof(uint32_t), VERR_BUFFER_OVERFLOW);
1189 AssertCompile(sizeof(uValue) == sizeof(uint32_t));
1190 *(uint32_t *)&pGicCpu->abIntrPriority[idxPriority] = uValue;
1191 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, *(uint32_t *)&pGicCpu->abIntrPriority[idxPriority]));
1192 return VINF_SUCCESS;
1193}
1194
1195
1196/**
1197 * Reads the redistributor's interrupt pending register (GICR_ISPENDR and
1198 * GICR_ICPENDR).
1199 *
1200 * @returns Strict VBox status code.
1201 * @param pGicDev The GIC distributor state.
1202 * @param pGicCpu The GIC redistributor and CPU interface state.
1203 * @param idxReg The index of the register in the GICR_ISPENDR and
1204 * GICR_ICPENDR range.
1205 * @param puValue Where to store the register's value.
1206 */
1207static VBOXSTRICTRC gicReDistReadIntrPendingReg(PCGICDEV pGicDev, PGICCPU pGicCpu, uint16_t idxReg, uint32_t *puValue)
1208{
1209 /* When affinity routing is disabled, reads return 0. */
1210 Assert(pGicDev->fAffRoutingEnabled);
1211 Assert(idxReg < RT_ELEMENTS(pGicCpu->bmIntrPending));
1212 *puValue = pGicCpu->bmIntrPending[idxReg];
1213 LogFlowFunc(("idxReg=%#x read %#x\n", idxReg, pGicCpu->bmIntrPending[idxReg]));
1214 return VINF_SUCCESS;
1215}
1216
1217
1218/**
1219 * Writes the redistributor's interrupt set-pending register (GICR_ISPENDR).
1220 *
1221 * @returns Strict VBox status code.
1222 * @param pGicDev The GIC distributor state.
1223 * @param pVCpu The cross context virtual CPU structure.
1224 * @param idxReg The index of the register in the GICR_ISPENDR range.
1225 * @param uValue The value to write to the register.
1226 */
1227static VBOXSTRICTRC gicReDistWriteIntrSetPendingReg(PCGICDEV pGicDev, PVMCPUCC pVCpu, uint16_t idxReg, uint32_t uValue)
1228{
1229 /* When affinity routing is disabled, writes are ignored. */
1230 Assert(pGicDev->fAffRoutingEnabled);
1231 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
1232 Assert(idxReg < RT_ELEMENTS(pGicCpu->bmIntrPending));
1233 pGicCpu->bmIntrPending[idxReg] |= uValue;
1234 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicCpu->bmIntrPending[idxReg]));
1235 return gicReDistUpdateIrqState(pGicDev, pVCpu);
1236}
1237
1238
1239/**
1240 * Writes the redistributor's interrupt clear-pending register (GICR_ICPENDR).
1241 *
1242 * @returns Strict VBox status code.
1243 * @param pGicDev The GIC distributor state.
1244 * @param pVCpu The cross context virtual CPU structure.
1245 * @param idxReg The index of the register in the GICR_ICPENDR range.
1246 * @param uValue The value to write to the register.
1247 */
1248static VBOXSTRICTRC gicReDistWriteIntrClearPendingReg(PCGICDEV pGicDev, PVMCPUCC pVCpu, uint16_t idxReg, uint32_t uValue)
1249{
1250 /* When affinity routing is disabled, writes are ignored. */
1251 Assert(pGicDev->fAffRoutingEnabled);
1252 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
1253 Assert(idxReg < RT_ELEMENTS(pGicCpu->bmIntrPending));
1254 pGicCpu->bmIntrPending[idxReg] &= ~uValue;
1255 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicCpu->bmIntrPending[idxReg]));
1256 return gicReDistUpdateIrqState(pGicDev, pVCpu);
1257}
1258
1259
1260/**
1261 * Reads the redistributor's interrupt enable register (GICR_ISENABLER and
1262 * GICR_ICENABLER).
1263 *
1264 * @returns Strict VBox status code.
1265 * @param pGicDev The GIC distributor state.
1266 * @param pGicCpu The GIC redistributor and CPU interface state.
1267 * @param idxReg The index of the register in the GICR_ISENABLER and
1268 * GICR_ICENABLER range.
1269 * @param puValue Where to store the register's value.
1270 */
1271static VBOXSTRICTRC gicReDistReadIntrEnableReg(PCGICDEV pGicDev, PGICCPU pGicCpu, uint16_t idxReg, uint32_t *puValue)
1272{
1273 Assert(pGicDev->fAffRoutingEnabled);
1274 Assert(idxReg < RT_ELEMENTS(pGicCpu->bmIntrEnabled));
1275 *puValue = pGicCpu->bmIntrEnabled[idxReg];
1276 LogFlowFunc(("idxReg=%#x read %#x\n", idxReg, pGicCpu->bmIntrEnabled[idxReg]));
1277 return VINF_SUCCESS;
1278}
1279
1280
1281/**
1282 * Writes the redistributor's interrupt set-enable register (GICR_ISENABLER).
1283 *
1284 * @returns Strict VBox status code.
1285 * @param pGicDev The GIC distributor state.
1286 * @param pVCpu The cross context virtual CPU structure.
1287 * @param idxReg The index of the register in the GICR_ISENABLER range.
1288 * @param uValue The value to write to the register.
1289 */
1290static VBOXSTRICTRC gicReDistWriteIntrSetEnableReg(PCGICDEV pGicDev, PVMCPUCC pVCpu, uint16_t idxReg, uint32_t uValue)
1291{
1292 Assert(pGicDev->fAffRoutingEnabled);
1293 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
1294 Assert(idxReg < RT_ELEMENTS(pGicCpu->bmIntrEnabled));
1295 pGicCpu->bmIntrEnabled[idxReg] |= uValue;
1296 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicCpu->bmIntrEnabled[idxReg]));
1297 return gicReDistUpdateIrqState(pGicDev, pVCpu);
1298}
1299
1300
1301/**
1302 * Writes the redistributor's interrupt clear-enable register (GICR_ICENABLER).
1303 *
1304 * @returns Strict VBox status code.
1305 * @param pGicDev The GIC distributor state.
1306 * @param pVCpu The cross context virtual CPU structure.
1307 * @param idxReg The index of the register in the GICR_ICENABLER range.
1308 * @param uValue The value to write to the register.
1309 */
1310static VBOXSTRICTRC gicReDistWriteIntrClearEnableReg(PCGICDEV pGicDev, PVMCPUCC pVCpu, uint16_t idxReg, uint32_t uValue)
1311{
1312 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
1313 Assert(idxReg < RT_ELEMENTS(pGicCpu->bmIntrEnabled));
1314 pGicCpu->bmIntrEnabled[idxReg] &= ~uValue;
1315 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicCpu->bmIntrEnabled[idxReg]));
1316 return gicReDistUpdateIrqState(pGicDev, pVCpu);
1317}
1318
1319
1320/**
1321 * Reads the redistributor's interrupt active register (GICR_ISACTIVER and
1322 * GICR_ICACTIVER).
1323 *
1324 * @returns Strict VBox status code.
1325 * @param pGicCpu The GIC redistributor and CPU interface state.
1326 * @param idxReg The index of the register in the GICR_ISACTIVER and
1327 * GICR_ICACTIVER range.
1328 * @param puValue Where to store the register's value.
1329 */
1330static VBOXSTRICTRC gicReDistReadIntrActiveReg(PGICCPU pGicCpu, uint16_t idxReg, uint32_t *puValue)
1331{
1332 Assert(idxReg < RT_ELEMENTS(pGicCpu->bmIntrActive));
1333 *puValue = pGicCpu->bmIntrActive[idxReg];
1334 LogFlowFunc(("idxReg=%#x read %#x\n", idxReg, pGicCpu->bmIntrActive[idxReg]));
1335 return VINF_SUCCESS;
1336}
1337
1338
1339/**
1340 * Writes the redistributor's interrupt set-active register (GICR_ISACTIVER).
1341 *
1342 * @returns Strict VBox status code.
1343 * @param pGicDev The GIC distributor state.
1344 * @param pVCpu The cross context virtual CPU structure.
1345 * @param idxReg The index of the register in the GICR_ISACTIVER range.
1346 * @param uValue The value to write to the register.
1347 */
1348static VBOXSTRICTRC gicReDistWriteIntrSetActiveReg(PCGICDEV pGicDev, PVMCPUCC pVCpu, uint16_t idxReg, uint32_t uValue)
1349{
1350 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
1351 Assert(idxReg < RT_ELEMENTS(pGicCpu->bmIntrActive));
1352 pGicCpu->bmIntrActive[idxReg] |= uValue;
1353 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicCpu->bmIntrActive[idxReg]));
1354 return gicReDistUpdateIrqState(pGicDev, pVCpu);
1355}
1356
1357
1358/**
1359 * Writes the redistributor's interrupt clear-active register (GICR_ICACTIVER).
1360 *
1361 * @returns Strict VBox status code.
1362 * @param pGicDev The GIC distributor state.
1363 * @param pVCpu The cross context virtual CPU structure.
1364 * @param idxReg The index of the register in the GICR_ICACTIVER range.
1365 * @param uValue The value to write to the register.
1366 */
1367static VBOXSTRICTRC gicReDistWriteIntrClearActiveReg(PCGICDEV pGicDev, PVMCPUCC pVCpu, uint16_t idxReg, uint32_t uValue)
1368{
1369 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
1370 Assert(idxReg < RT_ELEMENTS(pGicCpu->bmIntrActive));
1371 pGicCpu->bmIntrActive[idxReg] &= ~uValue;
1372 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicCpu->bmIntrActive[idxReg]));
1373 return gicReDistUpdateIrqState(pGicDev, pVCpu);
1374}
1375
1376
1377/**
1378 * Reads the redistributor's interrupt config register (GICR_ICFGR).
1379 *
1380 * @returns Strict VBox status code.
1381 * @param pGicDev The GIC distributor state.
1382 * @param pGicCpu The GIC redistributor and CPU interface state.
1383 * @param idxReg The index of the register in the GICR_ICFGR range.
1384 * @param puValue Where to store the register's value.
1385 */
1386static VBOXSTRICTRC gicReDistReadIntrConfigReg(PCGICDEV pGicDev, PGICCPU pGicCpu, uint16_t idxReg, uint32_t *puValue)
1387{
1388 /* When affinity routing is disabled, reads return 0. */
1389 Assert(pGicDev->fAffRoutingEnabled);
1390 if (idxReg > 0)
1391 {
1392 Assert(idxReg < RT_ELEMENTS(pGicCpu->bmIntrConfig));
1393 *puValue = pGicCpu->bmIntrConfig[idxReg];
1394 }
1395 else
1396 {
1397 /* SGIs are read-only and are always edge-triggered. */
1398 *puValue = 0xaaaaaaaa;
1399 }
1400 LogFlowFunc(("idxReg=%#x read %#x\n", idxReg, *puValue));
1401 return VINF_SUCCESS;
1402}
1403
1404
1405/**
1406 * Writes the redistributor's interrupt config register (GICR_ICFGR).
1407 *
1408 * @returns Strict VBox status code.
1409 * @param pGicDev The GIC distributor state.
1410 * @param pVCpu The cross context virtual CPU structure.
1411 * @param idxReg The index of the register in the GICR_ICFGR range.
1412 * @param uValue The value to write to the register.
1413 */
1414static VBOXSTRICTRC gicReDistWriteIntrConfigReg(PCGICDEV pGicDev, PVMCPUCC pVCpu, uint16_t idxReg, uint32_t uValue)
1415{
1416 /* When affinity routing is disabled, writes are ignored. */
1417 Assert(pGicDev->fAffRoutingEnabled);
1418 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
1419 if (idxReg > 0)
1420 {
1421 Assert(idxReg < RT_ELEMENTS(pGicCpu->bmIntrConfig));
1422 pGicCpu->bmIntrConfig[idxReg] = uValue;
1423 }
1424 else
1425 {
1426 /* SGIs are always edge triggered ignore writes, verify value on strict builds (e.g. aarch64 Win11 writes this). */
1427 Assert(uValue == 0xaaaaaaaa);
1428 Assert(pGicCpu->bmIntrConfig[0] == uValue);
1429 }
1430 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicCpu->bmIntrConfig[idxReg]));
1431 return VINF_SUCCESS;
1432}
1433
1434
1435/**
1436 * Reads the redistributor's interrupt group register (GICD_IGROUPR).
1437 *
1438 * @returns Strict VBox status code.
1439 * @param pGicDev The GIC distributor state.
1440 * @param pGicCpu The GIC redistributor and CPU interface state.
1441 * @param idxReg The index of the register in the GICR_IGROUPR range.
1442 * @param puValue Where to store the register's value.
1443 */
1444static VBOXSTRICTRC gicReDistReadIntrGroupReg(PCGICDEV pGicDev, PGICCPU pGicCpu, uint16_t idxReg, uint32_t *puValue)
1445{
1446 /* When affinity routing is disabled, reads return 0. */
1447 Assert(pGicDev->fAffRoutingEnabled);
1448 Assert(idxReg < RT_ELEMENTS(pGicCpu->bmIntrGroup));
1449 *puValue = pGicCpu->bmIntrGroup[idxReg];
1450 LogFlowFunc(("idxReg=%#x read %#x\n", idxReg, pGicCpu->bmIntrGroup[idxReg]));
1451 return VINF_SUCCESS;
1452}
1453
1454
1455/**
1456 * Writes the redistributor's interrupt group register (GICR_IGROUPR).
1457 *
1458 * @returns Strict VBox status code.
1459 * @param pGicDev The GIC distributor state.
1460 * @param pVCpu The cross context virtual CPU structure.
1461 * @param idxReg The index of the register in the GICR_IGROUPR range.
1462 * @param uValue The value to write to the register.
1463 */
1464static VBOXSTRICTRC gicReDistWriteIntrGroupReg(PCGICDEV pGicDev, PVMCPUCC pVCpu, uint16_t idxReg, uint32_t uValue)
1465{
1466 /* When affinity routing is disabled, writes are ignored. */
1467 Assert(pGicDev->fAffRoutingEnabled);
1468 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
1469 Assert(idxReg < RT_ELEMENTS(pGicCpu->bmIntrGroup));
1470 pGicCpu->bmIntrGroup[idxReg] = uValue;
1471 LogFlowFunc(("idxReg=%#x written %#x\n", idxReg, pGicCpu->bmIntrGroup[idxReg]));
1472 return gicReDistUpdateIrqState(pGicDev, pVCpu);
1473}
1474
1475
1476#if 0
1477/**
1478 * Gets the IDB index for a given interrupt ID.
1479 *
1480 * @returns UINT16_MAX is the interrupt ID is invalid or does not map to an index,
1481 * otherwise returns a valid index.
1482 * @param uIntId The interrupt ID.
1483 */
1484static uint16_t gicIntIdToIdbIndex(uint16_t uIntId)
1485{
1486 /*
1487 * Interrupt Delivery Bitmap (IDB) format; bits to interrupt ID mapping:
1488 * +---------------------------------------------------------------------+
1489 * | Incl ranges | SGI | PPI | SPI | Ext PPI | Ext SPI |
1490 * +---------------------------------------------------------------------+
1491 * | Int Id | 0..15 | 16..31 | 32..1019 | 1056..1119 | 4096..5119 |
1492 * | Bit Index | 0..15 | 16..31 | 32..1019 | 1020..1083 | 1084..2107 |
1493 * +---------------------------------------------------------------------+
1494 */
1495 uint16_t const idxIdb;
1496 if (uIntId <= GIC_INTID_RANGE_SPI_LAST)
1497 return uIntId;
1498 if (uIntId - GIC_INTID_RANGE_EXT_PPI_START < GIC_INTID_RANGE_EXT_PPI_RANGE_SIZE)
1499 return GIC_INTID_RANGE_EXT_PPI_START + uIntId - GIC_INTID_RANGE_EXT_PPI_START;
1500 if (uIntId - GIC_INTID_RANGE_EXT_SPI_START < GIC_INTID_RANGE_EXT_SPI_RANGE_SIZE)
1501 return GIC_INTID_RANGE_EXT_SPI_START + uIntId - GIC_INTID_RANGE_EXT_SPI_START;
1502 return UINT16_MAX;
1503}
1504
1505static bool gicIsIntIdValid(uint16_t uIntId)
1506{
1507 if (uIntId <= GIC_INTID_RANGE_SPI_LAST)
1508 return true;
1509 if (uIntIt - GIC_INTID_RANGE_EXT_PPI_START < GIC_INTID_RANGE_EXT_PPI_RANGE_SIZE)
1510 return true;
1511 if (uIntIt - GIC_INTID_RANGE_EXT_SPI_START < GIC_INTID_RANGE_EXT_SPI_RANGE_SIZE)
1512 return true;
1513 return false;
1514}
1515
1516
1517static void gicSetIdxInIdb(PGICIDB pGicIdb, uint16_t idxIdb)
1518{
1519 if (RT_LIKELY(idxIdb < sizeof(pGicIdb->au64IntIdBitmap) * 8))
1520 ASMAtomicBitSet(&pGicIdb->au64IntIdBitmap[0], idxIdb);
1521 else
1522 AssertMsgFailed(("Invalid IDB index %u for INTID %u\n" idxIdb, uIntId));
1523}
1524
1525
1526/**
1527 * Sets an interrupt ID in an Interrupt-Delivery Bitmap (IDB).
1528 *
1529 * @param pGicIdb Pointer to the IDB.
1530 * @param uIntId The interrupt ID.
1531 */
1532static void gicSetIntIdInIdb(PGICIDB pGicIdb, uint16_t uIntId)
1533{
1534 uint16_t const idxIdb = gicIntIdToIdbIndex(uIntId);
1535 gicSetIdxInIdb(pGicIdb, idxIdb);
1536}
1537
1538
1539/**
1540 * Clears an interrupt ID in an Interrupt-Delivery Bitmap (IDB).
1541 *
1542 * @param pGicIdb Pointer to the IDB.
1543 * @param uIntId The interrupt ID.
1544 */
1545static void gicClearIntIdInIdb(PGICIDB pGicIdb, uint16_t uIntId)
1546{
1547 uint16_t const idxIdb = gicIntIdToIdbIndex(uIntId);
1548 if (RT_LIKELY(idxIdb < sizeof(pGicIdb->au64IntIdBitmap) * 8))
1549 ASMAtomicBitClear(&pGicIdb->au64IntIdBitmap[0], idxIdb);
1550 else
1551 AssertMsgFailed(("Invalid IDB index %u for INTID %u\n" idxIdb, uIntId));
1552}
1553
1554
1555/**
1556 * Atomically sets the IDB notification bit.
1557 *
1558 * @returns non-zero if the bit was already set, 0 otherwise.
1559 * @param pGicIdb Pointer to the IDB.
1560 */
1561static uint32_t gicSetNotificationBitInIdb(PGICIDB pGicIdb)
1562{
1563 return ASMAtomicXchgU32(&pGicIdb->fOutstandingNotification, RT_BIT_32(31));
1564}
1565
1566
1567/**
1568 * Atomically tests and clears the IDB notification bit.
1569 *
1570 * @returns non-zero if the bit was already set, 0 otherwise.
1571 * @param pGicIdb Pointer to the IDB.
1572 */
1573static uint32_t gicClearNotificationBitInIdb(PGICIDB pGicIdb)
1574{
1575 return ASMAtomicXchgU32(&pGicIdb->fOutstandingNotification, UINT32_C(0));
1576}
1577
1578
1579static VBOXSTRICTRC gicPostInterrupt(PVMCPUCC pVCpu, PVMCPUSET pCpuSet, uint16_t idxIdb)
1580{
1581 Assert(gicIntIdToIdbIndex(idxIdb) != UINT16_MAX);
1582 for (VMCPUID idCpu = 0; idCpu < cCpus; idCpu++)
1583 {
1584 if (VMCPUSET_IS_PRESENT(&DestCpuSet, idCpu))
1585 {
1586 PVMCPUCC pTargetVCpu = pVCpu->pVMR3->CTX_SUFF(apCpus)[idCpu];
1587 VMCPU_ASSERT_VALID_EXT_RETURN(pVCpu, VERR_INVALID_HANDLE);
1588
1589 PGICCPU pTargetGicCpu = VMCPU_TO_GICCPU(pTargetVCpu);
1590 PGICIDB pTargetGicIdb = &pTargetGicCpu->IntrDeliveryBitmap;
1591 gicSetIdxInIdb(pTargetGicIdb, idxIdb);
1592 uint32_t const fAlreadySet = gicSetNotificationBitInIdb(pTargetGicIdb);
1593 if (!fAlreadySet)
1594 gicSetUpdateInterruptFF(pVCpu);
1595 }
1596 }
1597}
1598#endif
1599
1600
1601/**
1602 * Gets the virtual CPUID given the affinity values.
1603 *
1604 * @returns The virtual CPUID.
1605 * @param idCpuInterface The virtual CPUID within the PE cluster (0..15).
1606 * @param uAff1 The affinity 1 value.
1607 * @param uAff2 The affinity 2 value.
1608 * @param uAff3 The affinity 3 value.
1609 */
1610DECL_FORCE_INLINE(VMCPUID) gicGetCpuIdFromAffinity(uint8_t idCpuInterface, uint8_t uAff1, uint8_t uAff2, uint8_t uAff3)
1611{
1612 AssertReturn(idCpuInterface < 16, VERR_INVALID_PARAMETER);
1613 return (uAff3 * 1048576) + (uAff2 * 4096) + (uAff1 * 16) + idCpuInterface;
1614}
1615
1616
1617#if 0
1618/**
1619 * @interface_method_impl{PDMGICBACKEND,pfnUpdatePendingInterrupts}
1620 */
1621static DECLCALLBACK(void) gicUpdatePendingInterrupts(PVMCPUCC pVCpu)
1622{
1623 VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu);
1624
1625 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
1626
1627 /*
1628 * Delivery interrupts pending in the interrupt-delivery bitmap to the PE.
1629 */
1630 bool fHasPendingIntrs = false;
1631 PGICIDB pIdb = (PGICIDB)pGicCpu->IntrDeliveryBitmap;
1632 for (;;)
1633 {
1634 uint32_t const fAlreadySet = gicClearNotificationBitInIdb(pIdb);
1635 if (!fAlreadySet)
1636 break;
1637
1638 /* SGI and PPIs */
1639 uint32_t const uSgiPpi = ASMAtomicXchgU32(&pIdb->au64IntIdBitmap[0], 0);
1640 if (uSgiPpi)
1641
1642 }
1643
1644 STAM_PROFILE_STOP(&pApicCpu->StatUpdatePendingIntrs, a);
1645 Log3(("APIC%u: apicUpdatePendingInterrupts: fHasPendingIntrs=%RTbool\n", pVCpu->idCpu, fHasPendingIntrs));
1646
1647 if ( fHasPendingIntrs
1648 && !VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC))
1649 apicSignalNextPendingIntr(pVCpu);
1650}
1651#endif
1652
1653
1654#if 0
1655static uint16_t gicGetHighestPrioPendingIntrFrom(const void *pvIntrPending, uint32_t cIntrs, uint8_t *pabIntrPriority,
1656 uint32_t cIntrPriority, uint8_t *pbPriority)
1657{
1658 uint16_t uIntIdHighest = 0;
1659 uint8_t bPriority = UINT8_MAX;
1660
1661 /* Find the INTID and priority of the pending interrupt with the highest priority. */
1662 int32_t idxIntr = ASMBitFirstSet(pvIntrPending, cIntrs);
1663 AssertCompile(!(cIntrs % 32));
1664 if (idxIntr >= 0)
1665 {
1666 do
1667 {
1668 uint16_t const uIntId = gicGetIntIdFromIndex((uint16_t)idxIntr);
1669 AssertRelease(uIntId < cIntrPriority);
1670 if (pabIntrPriority[uIntId] < bPriority)
1671 {
1672 bPriority = pabIntrPriority[uIntId];
1673 uIntIdHighest = uIntId;
1674 }
1675 idxIntr = ASMBitNextSet(pvIntrPending, cIntrs, idxIntr);
1676 } while (idxIntr != -1);
1677 }
1678 else
1679 {
1680 bPriority = UINT8_MAX;
1681 uIntIdHighest = GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT;
1682 }
1683
1684 *pbPriority = bPriority;
1685 return uIntIdHighest;
1686}
1687
1688
1689static uint16_t gicDistGetHighestPrioPendingIntr(PCGICDEV pGicDev, uint8_t *pbPriority)
1690{
1691 uint16_t uIntIdHighest = 0;
1692 uint8_t bPriority = UINT8_MAX;
1693 uint32_t const cIntrs = sizeof(pGicDev->bmIntrPending) * 8;
1694 void const *pvIntrPending = &pGicDev->bmIntrPending[0];
1695
1696 /* Find the INTID and priority of the pending interrupt with the highest priority. */
1697 int32_t idxIntr = ASMBitFirstSet(pvIntrPending, cIntrs);
1698 AssertCompile(!(cIntrs % 32));
1699 if (idxIntr >= 0)
1700 {
1701 Assert(!pGicDev->fAffRoutingEnabled || idxIntr >= GIC_INTID_RANGE_SPI_START);
1702 do
1703 {
1704 uint16_t const uIntId = gicGetIntIdFromIndex((uint16_t)idxIntr);
1705 Assert(uIntId < RT_ELEMENTS(pGicDev->abIntrPriority));
1706 if (pGicDev->abIntrPriority[uIntId] < bPriority)
1707 {
1708 bPriority = pGicDev->abIntrPriority[idxIntr];
1709 uIntIdHighest = uIntId;
1710 }
1711 idxIntr = ASMBitNextSet(pvIntrPending, cIntrs, idxIntr);
1712 } while (idxIntr != -1);
1713 }
1714 else
1715 {
1716 bPriority = UINT8_MAX;
1717 uIntIdHighest = GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT;
1718 }
1719
1720 /* Sanity check if the interrupt ID is plausible. */
1721 Assert( GIC_IS_INTR_SGI_OR_PPI(uIntIdHighest)
1722 || GIC_IS_INTR_SPI(uIntIdHighest)
1723 || GIC_IS_INTR_EXT_PPI(uIntIdHighest)
1724 || GIC_IS_INTR_EXT_SPI(uIntIdHighest)
1725 || uIntIdHighest == GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT);
1726 /* Ensure if no interrupt is pending, priority is appropriate. */
1727 Assert(uIntIdHighest != GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT || bPriority == UINT8_MAX);
1728
1729 *pbPriority = bPriority;
1730 return uIntIdHighest;
1731}
1732
1733
1734static uint16_t gicDistGetHighestPrioPendingGroupIntr(PCGICDEV pGicDev, uint8_t *pbPriority, bool fGroup0)
1735{
1736 uint32_t bmIntrPending[64];
1737 for (uint8_t i = 0; i < RT_ELEMENTS(bmIntrs); i++)
1738 {
1739 bmIntrPending[i] = (pGicDev->bmIntrPending[i] & pGicDev->bmIntrEnabled[i]) & ~pGicDev->bmIntrActive[i];
1740 if (fGroup0)
1741 bmIntrPending[i] &= ~pGicDev->bmIntrGroup[i];
1742 else
1743 bmIntrPending[i] &= pGicDev->bmIntrGroup[i];
1744 }
1745
1746 void const *pvIntrPending = &bmIntrPending[0];
1747 uint32_t const cIntrs = sizeof(bmIntrPending) * 8;
1748 uint16_t const uIntId = gicGetHighestPrioPendingIntrFrom(pvIntrPending, cIntrs, pGicDev->abIntrPriority,
1749 RT_ELEMENTS(pGicDev->abIntrPriority), pbPriority);
1750
1751 /* Sanity check if the interrupt ID is plausible. */
1752 Assert( GIC_IS_INTR_SGI_OR_PPI(uIntIdHighest)
1753 || GIC_IS_INTR_SPI(uIntIdHighest)
1754 || GIC_IS_INTR_EXT_PPI(uIntIdHighest)
1755 || GIC_IS_INTR_EXT_SPI(uIntIdHighest)
1756 || uIntIdHighest == GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT);
1757 /* Ensure if no interrupt is pending, priority is appropriate. */
1758 Assert(uIntIdHighest != GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT || bPriority == UINT8_MAX);
1759
1760 return uIntId;
1761}
1762
1763
1764static uint16_t gicReDistGetHighestPrioPendingGroupIntr(PCGICCPU pGicCpu, uint8_t *pbPriority, bool fGroup0)
1765{
1766 uint32_t bmIntrPending[3];
1767 for (uint8_t i = 0; i < RT_ELEMENTS(bmIntrs); i++)
1768 {
1769 bmIntrPending[i] = (pGicCpu->bmIntrPending[i] & pGicCpu->bmIntrEnabled[i]) & ~pGicCpu->bmIntrActive[i];
1770 if (fGroup0)
1771 bmIntrPending[i] &= ~pGicCpu->bmIntrGroup[i];
1772 else
1773 bmIntrPending[i] &= pGicCpu->bmIntrGroup[i];
1774 }
1775
1776 void const *pvIntrPending = &bmIntrPending[0];
1777 uint32_t const cIntrs = sizeof(bmIntrPending) * 8;
1778 uint16_t const uIntId = gicGetHighestPrioPendingIntrFrom(pvIntrPending, cIntrs, pGicCpu->abIntrPriority,
1779 RT_ELEMENTS(pGicCpu->abIntrPriority), pbPriority);
1780
1781 /* Sanity check if the interrupt ID is plausible. */
1782 Assert( GIC_IS_INTR_SGI_OR_PPI(uIntId)
1783 || GIC_IS_INTR_EXT_PPI(uIntId)
1784 || uIntId == GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT);
1785 /* Ensure if no interrupt is pending, priority is appropriate. */
1786 Assert(uIntId != GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT || *pbPriority == UINT8_MAX);
1787 return uIntId;
1788}
1789
1790static uint16_t gicReDistGetHighestPrioPendingIntr(PCGICCPU pGicCpu, uint8_t *pbPriority)
1791{
1792 uint16_t uIntIdHighest = 0;
1793 uint8_t bPriority = UINT8_MAX;
1794 uint32_t const cIntrs = sizeof(pGicCpu->bmIntrPending) * 8;
1795 void const *pvIntrPending = &pGicCpu->bmIntrPending[0];
1796
1797 /* Find the INTID and priority of the pending interrupt with the highest priority. */
1798 int32_t idxIntr = ASMBitFirstSet(pvIntrPending, cIntrs);
1799 AssertCompile(!(cIntrs % 32));
1800 if (idxIntr >= 0)
1801 {
1802 do
1803 {
1804 uint16_t const uIntId = gicGetIntIdFromIndex((uint16_t)idxIntr);
1805 Assert(uIntId < RT_ELEMENTS(pGicCpu->abIntrPriority));
1806 if (pGicCpu->abIntrPriority[uIntId] < bPriority)
1807 {
1808 bPriority = pGicCpu->abIntrPriority[idxIntr];
1809 uIntIdHighest = uIntId;
1810 }
1811 idxIntr = ASMBitNextSet(pvIntrPending, cIntrs, idxIntr);
1812 } while (idxIntr != -1);
1813 }
1814 else
1815 {
1816 bPriority = UINT8_MAX;
1817 uIntIdHighest = GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT;
1818 }
1819
1820 /* Sanity check if the interrupt ID is plausible. */
1821 Assert( GIC_IS_INTR_SGI_OR_PPI(uIntIdHighest)
1822 || GIC_IS_INTR_EXT_PPI(uIntIdHighest)
1823 || uIntIdHighest == GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT);
1824 /* Ensure if no interrupt is pending, priority is appropriate. */
1825 Assert(uIntIdHighest != GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT || bPriority == UINT8_MAX);
1826
1827 *pbPriority = bPriority;
1828 return uIntIdHighest;
1829}
1830
1831
1832static uint16_t gicGetHighestPrioPendingIntr(PCVMCC pVM, PGICDEV pGicDev)
1833{
1834 /* Get highest priority pending interrupt from the distributor. */
1835 uint8_t bPriority;
1836 uint16_t uIntIdHighest = gicDistGetHighestPrioPendingIntr(pGicDev, &bPriority);
1837
1838 /* Compare with the highest priority pending interrupt from each redistributor. */
1839 uint32_t const cCpus = pVM->cCpus;
1840 for (VMCPUID idCpu = 0; idCpu < cCpus; idCpu++)
1841 {
1842 PCVMCPUCC pVCpu = pVM->CTX_SUFF(apCpus)[idCpu];
1843 PCGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
1844
1845 uint8_t bReDistPriority;
1846 uint16_t const uIntId = gicReDistGetHighestPrioPendingIntr(pGicCpu, &bReDistPriority);
1847 if ( uIntId != GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT
1848 && bReDistPriority < bPriority)
1849 {
1850 bReDistPriority = bPriority;
1851 uIntIdHighest = uIntId;
1852 }
1853 }
1854
1855 /* Sanity check if the interrupt ID is plausible. */
1856 Assert( GIC_IS_INTR_SGI_OR_PPI(uIntIdHighest)
1857 || GIC_IS_INTR_SPI(uIntIdHighest)
1858 || GIC_IS_INTR_EXT_PPI(uIntIdHighest)
1859 || GIC_IS_INTR_EXT_SPI(uIntIdHighest)
1860 || uIntIdHighest == GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT);
1861 /* Ensure if no interrupt is pending, priority is appropriate. */
1862 Assert(uIntIdHighest != GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT || bPriority == UINT8_MAX);
1863
1864 return uIntIdHighest;
1865}
1866#endif
1867
1868
1869/**
1870 * Gets the highest priority pending distributor interrupt that can be forwarded to
1871 * the redistributor.
1872 *
1873 * @returns The interrupt ID or GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT if no interrupt
1874 * is pending or not in a state to be forwarded to the redistributor.
1875 * @param pGicDev The GIC distributor state.
1876 * @param fGroup0 Whether to consider group 0 interrupts.
1877 * @param fGroup1 Whether to consider group 1 interrupts.
1878 * @param pidxIntr Where to store the distributor interrupt index for the
1879 * returned interrupt ID. UINT16_MAX if this function returns
1880 * GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT. Optional, can be
1881 * NULL.
1882 * @param pbPriority Where to store the priority of the returned interrupt ID.
1883 * UINT8_MAX if this function returns
1884 * GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT.
1885 */
1886static uint16_t gicDistGetHighestPrioPendingIntr(PCGICDEV pGicDev, bool fGroup0, bool fGroup1, uint16_t *pidxIntr,
1887 uint8_t *pbPriority)
1888{
1889 /*
1890 * Figure out the highest priority pending interrupt in the distributor.
1891 * We can skip SGIs, PPIs in the distributor as we don't support legacy operation.
1892 *
1893 * See ARM GIC spec. 4.7.2 "Interaction of group and individual interrupt enables".
1894 * See ARM GIC spec. 1.3.5 "GICv3 with no legacy operation".
1895 */
1896 Assert(pGicDev->fAffRoutingEnabled);
1897 uint32_t bmIntrPending[64]; /** @todo SGIs, PPIs: iterate from 1 and initialize bmIntrPending[0] = 0. */
1898 for (uint8_t i = 0; i < RT_ELEMENTS(bmIntrPending); i++)
1899 {
1900 /* Collect interrupts that are pending, enabled and inactive. */
1901 bmIntrPending[i] = (pGicDev->bmIntrPending[i] & pGicDev->bmIntrEnabled[i]) & ~pGicDev->bmIntrActive[i];
1902
1903 /* Discard interrupts if the group they belong to is disabled. */
1904 if (!fGroup1)
1905 bmIntrPending[i] &= ~pGicDev->bmIntrGroup[i];
1906 if (!fGroup0)
1907 bmIntrPending[i] &= pGicDev->bmIntrGroup[i];
1908 }
1909
1910 /* Among the collected interrupts, pick the one with the highest priority. */
1911 uint16_t uIntId;
1912 uint16_t idxHighest;
1913 uint16_t uPriority = UINT16_MAX;
1914 void const *pvIntrs = &bmIntrPending[0];
1915 uint32_t const cIntrs = sizeof(bmIntrPending) * 8;
1916 int32_t idxIntr = ASMBitFirstSet(pvIntrs, cIntrs);
1917 AssertCompile(!(cIntrs % 32));
1918 if (idxIntr >= 0)
1919 {
1920 Assert(idxIntr >= 32); /* We don't support legacy operation. */
1921 do
1922 {
1923 Assert((uint32_t)idxIntr < RT_ELEMENTS(pGicDev->abIntrPriority));
1924 if ((uint16_t)pGicDev->abIntrPriority[idxIntr] < uPriority)
1925 {
1926 idxHighest = (uint16_t)idxIntr;
1927 uPriority = pGicDev->abIntrPriority[idxIntr];
1928 uIntId = gicDistGetIntIdFromIndex(idxIntr);
1929 }
1930 idxIntr = ASMBitNextSet(pvIntrs, cIntrs, idxIntr);
1931 } while (idxIntr != -1);
1932 }
1933 else
1934 {
1935 idxHighest = UINT16_MAX;
1936 uPriority = UINT8_MAX;
1937 uIntId = GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT;
1938 }
1939
1940 *pbPriority = uPriority;
1941 if (pidxIntr)
1942 *pidxIntr = idxHighest;
1943
1944 /* Sanity check if the interrupt ID is within known ranges. */
1945 Assert( GIC_IS_INTR_SPI(uIntId)
1946 || GIC_IS_INTR_EXT_PPI(uIntId)
1947 || GIC_IS_INTR_EXT_SPI(uIntId)
1948 || uIntId == GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT);
1949 /* Ensure that if no interrupt is pending, the priority is appropriate. */
1950 Assert(uIntId != GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT || *pbPriority == UINT8_MAX);
1951
1952 LogFlowFunc(("uIntId=%u [idxIntr=%u uPriority=%u]\n", uIntId, idxIntr, uPriority));
1953 return uIntId;
1954}
1955
1956
1957/**
1958 * Gets the highest priority pending redistributor interrupt that can be signalled
1959 * to the PE.
1960 *
1961 * @returns The interrupt ID or GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT if no interrupt
1962 * is pending or not in a state to be signalled to the PE.
1963 * @param pGicCpu The GIC redistributor and CPU interface state.
1964 * @param fGroup0 Whether to consider group 0 interrupts.
1965 * @param fGroup1 Whether to consider group 1 interrupts.
1966 * @param pidxIntr Where to store the distributor interrupt index for the
1967 * returned interrupt ID. UINT16_MAX if this function returns
1968 * GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT. Optional, can be
1969 * NULL.
1970 * @param pbPriority Where to store the priority of the returned interrupt ID.
1971 * UINT8_MAX if this function returns
1972 * GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT.
1973 */
1974static uint16_t gicReDistGetHighestPrioPendingIntr(PCGICCPU pGicCpu, bool fGroup0, bool fGroup1, uint16_t *pidxIntr,
1975 uint8_t *pbPriority)
1976{
1977 /*
1978 * Figure out the highest priority pending interrupt in the redistributor.
1979 * See ARM GIC spec. 4.7.2 "Interaction of group and individual interrupt enables".
1980 */
1981 uint32_t bmIntrPending[3];
1982 for (uint8_t i = 0 ; i < RT_ELEMENTS(bmIntrPending); i++)
1983 {
1984 /* Collect interrupts that are pending, enabled and inactive. */
1985 bmIntrPending[i] = (pGicCpu->bmIntrPending[i] & pGicCpu->bmIntrEnabled[i]) & ~pGicCpu->bmIntrActive[i];
1986
1987 /* Discard interrupts if the group they belong to is disabled. */
1988 if (!fGroup1)
1989 bmIntrPending[i] &= ~pGicCpu->bmIntrGroup[i];
1990 if (!fGroup0)
1991 bmIntrPending[i] &= pGicCpu->bmIntrGroup[i];
1992 }
1993
1994 /* Among the collected interrupts, pick the one with the highest priority. */
1995 uint16_t uIntId;
1996 uint16_t idxHighest;
1997 uint16_t uPriority = UINT16_MAX;
1998 const void *pvIntrs = &bmIntrPending[0];
1999 uint32_t const cIntrs = sizeof(bmIntrPending) * 8;
2000 int32_t idxIntr = ASMBitFirstSet(pvIntrs, cIntrs);
2001 AssertCompile(!(cIntrs % 32));
2002 if (idxIntr >= 0)
2003 {
2004 do
2005 {
2006 Assert((uint32_t)idxIntr < RT_ELEMENTS(pGicCpu->abIntrPriority));
2007 if ((uint16_t)pGicCpu->abIntrPriority[idxIntr] < uPriority)
2008 {
2009 idxHighest = (uint16_t)idxIntr;
2010 uPriority = pGicCpu->abIntrPriority[idxIntr];
2011 uIntId = gicReDistGetIntIdFromIndex(idxIntr);
2012 }
2013 idxIntr = ASMBitNextSet(pvIntrs, cIntrs, idxIntr);
2014 } while (idxIntr != -1);
2015 }
2016 else
2017 {
2018 idxHighest = UINT16_MAX;
2019 uPriority = UINT8_MAX;
2020 uIntId = GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT;
2021 }
2022
2023 *pbPriority = uPriority;
2024 if (pidxIntr)
2025 *pidxIntr = idxHighest;
2026
2027 /* Sanity check if the interrupt ID is within known ranges. */
2028 Assert( GIC_IS_INTR_SGI_OR_PPI(uIntId)
2029 || GIC_IS_INTR_EXT_PPI(uIntId)
2030 || uIntId == GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT);
2031 /* Ensure that if no interrupt is pending, the priority is appropriate. */
2032 Assert(uIntId != GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT || *pbPriority == UINT8_MAX);
2033
2034 LogFlowFunc(("uIntId=%u [idxIntr=%u uPriority=%u]\n", uIntId, idxIntr, uPriority));
2035 return uIntId;
2036}
2037
2038
2039/**
2040 * Gets the highest priority pending interrupt that can be signalled to the PE.
2041 *
2042 * @returns The interrupt ID or GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT if no interrupt
2043 * is pending or not in a state to be signalled to the PE.
2044 * @param pVM The cross context VM state.
2045 * @param pGicDev The GIC distributor state.
2046 * @param fGroup0 Whether to consider group 0 interrupts.
2047 * @param fGroup1 Whether to consider group 1 interrupts.
2048 */
2049static uint16_t gicGetHighestPrioPendingIntr(PCVM pVM, PCGICDEV pGicDev, bool fGroup0, bool fGroup1)
2050{
2051 /* Get highest priority pending interrupt from the distributor. */
2052 uint8_t bPriority;
2053 uint16_t uIntId = gicDistGetHighestPrioPendingIntr(pGicDev, fGroup0, fGroup1, NULL /*pidxIntr*/, &bPriority);
2054
2055 /* Compare with the highest priority pending interrupt from each redistributor. */
2056 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
2057 {
2058 PCVMCPUCC pVCpu = pVM->CTX_SUFF(apCpus)[idCpu];
2059 PCGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
2060
2061 uint8_t bPriorityRedist;
2062 uint16_t const uIntIdRedist = gicReDistGetHighestPrioPendingIntr(pGicCpu, fGroup0, fGroup1, NULL /*pidxIntr*/,
2063 &bPriorityRedist);
2064 if ( uIntIdRedist != GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT
2065 && bPriorityRedist < bPriority)
2066 {
2067 bPriority = bPriorityRedist;
2068 uIntId = uIntIdRedist;
2069 }
2070 }
2071
2072 /* Sanity check if the interrupt ID is within known ranges. */
2073 Assert( GIC_IS_INTR_SGI_OR_PPI(uIntId)
2074 || GIC_IS_INTR_SPI(uIntId)
2075 || GIC_IS_INTR_EXT_PPI(uIntId)
2076 || GIC_IS_INTR_EXT_SPI(uIntId)
2077 || uIntId == GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT);
2078 /* Ensure that if no interrupt is pending, the priority is appropriate. */
2079 Assert(uIntId != GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT || bPriority == UINT8_MAX);
2080
2081 LogFlowFunc(("uIntId=%u [bPriority=%u]\n", uIntId, bPriority));
2082 return uIntId;
2083}
2084
2085
2086/**
2087 * Get and acknowledge the interrupt ID of a signalled interrupt.
2088 *
2089 * @returns The interrupt ID or GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT no interrupts
2090 * are pending or not in a state to be signalled.
2091 * @param pGicDev The GIC distributor state.
2092 * @param pVCpu The cross context virtual CPU structure.
2093 * @param fGroup0 Whether to consider group 0 interrupts.
2094 * @param fGroup1 Whether to consider group 1 interrupts.
2095 */
2096static uint16_t gicAckHighestPrioPendingIntr(PGICDEV pGicDev, PVMCPUCC pVCpu, bool fGroup0, bool fGroup1)
2097{
2098 Assert(fGroup0 || fGroup1);
2099 LogFlowFunc(("fGroup0=%RTbool fGroup1=%RTbool\n", fGroup0, fGroup1));
2100
2101 /* Get highest priority pending interrupt from the distributor. */
2102 uint8_t bPriority;
2103 uint16_t idxIntr;
2104 uint16_t uIntId = gicDistGetHighestPrioPendingIntr(pGicDev, fGroup0, fGroup1, &idxIntr, &bPriority);
2105
2106 /* Compare with the highest priority pending interrupt from each redistributor. */
2107 bool fIntrInRedist = false;
2108 uint32_t const cCpus = pVCpu->CTX_SUFF(pVM)->cCpus;
2109 for (VMCPUID idCpu = 0; idCpu < cCpus; idCpu++)
2110 {
2111 PCVMCPUCC pVCpuCur = pVCpu->CTX_SUFF(pVM)->CTX_SUFF(apCpus)[idCpu];
2112 PCGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpuCur);
2113
2114 uint8_t bPriorityRedist;
2115 uint16_t idxRedistIntr;
2116 uint16_t const uIntIdRedist = gicReDistGetHighestPrioPendingIntr(pGicCpu, fGroup0, fGroup1, &idxRedistIntr,
2117 &bPriorityRedist);
2118 if ( uIntIdRedist != GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT
2119 && bPriorityRedist < bPriority)
2120 {
2121 fIntrInRedist = true;
2122 bPriority = bPriorityRedist;
2123 uIntId = uIntIdRedist;
2124 idxIntr = idxRedistIntr;
2125 }
2126 }
2127
2128 /* Sanity check if the interrupt ID is within known ranges. */
2129 Assert( GIC_IS_INTR_SGI_OR_PPI(uIntId)
2130 || GIC_IS_INTR_SPI(uIntId)
2131 || GIC_IS_INTR_EXT_PPI(uIntId)
2132 || GIC_IS_INTR_EXT_SPI(uIntId)
2133 || uIntId == GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT);
2134 /* Ensure that if no interrupt is pending, the priority is appropriate. */
2135 Assert(uIntId != GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT || bPriority == UINT8_MAX);
2136
2137 /* Acknowledge the interrupt. */
2138 if (uIntId != GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT)
2139 {
2140 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
2141 if (fIntrInRedist)
2142 {
2143 /* Mark the interrupt as active. */
2144 AssertMsg(idxIntr < sizeof(pGicCpu->bmIntrActive) * 8, ("idxIntr=%u\n", idxIntr));
2145 ASMBitSet(&pGicCpu->bmIntrActive[0], idxIntr);
2146
2147 /* Drop priority. */
2148 Assert(pGicCpu->idxRunningPriority < RT_ELEMENTS(pGicCpu->abRunningPriorities) - 1);
2149
2150 LogFlowFunc(("Dropping interrupt priority from %u -> %u (idxRunningPriority: %u -> %u)\n",
2151 pGicCpu->abRunningPriorities[pGicCpu->idxRunningPriority],
2152 bPriority,
2153 pGicCpu->idxRunningPriority, pGicCpu->idxRunningPriority + 1));
2154 pGicCpu->abRunningPriorities[++pGicCpu->idxRunningPriority] = bPriority;
2155
2156 /* Clear edge level interrupts like SGIs as pending. */
2157 /** @todo do this for all edge-triggered? */
2158 if (idxIntr <= GIC_INTID_RANGE_SGI_LAST)
2159 ASMBitClear(&pGicCpu->bmIntrPending[0], idxIntr);
2160
2161 /* Update the redistributor IRQ state to reflect change in active interrupt. */
2162 gicReDistUpdateIrqState(pGicDev, pVCpu);
2163 }
2164 else
2165 {
2166 /* Mark the interrupt as active. */
2167 Assert(idxIntr < sizeof(pGicDev->bmIntrActive) * 8);
2168 ASMBitSet(&pGicDev->bmIntrActive[0], idxIntr);
2169
2170 /* Drop priority. */
2171 Assert(pGicCpu->idxRunningPriority < RT_ELEMENTS(pGicCpu->abRunningPriorities) - 1);
2172
2173 LogFlowFunc(("Dropping interrupt priority from %u -> %u (idxRunningPriority: %u -> %u)\n",
2174 pGicCpu->abRunningPriorities[pGicCpu->idxRunningPriority],
2175 bPriority,
2176 pGicCpu->idxRunningPriority, pGicCpu->idxRunningPriority + 1));
2177 pGicCpu->abRunningPriorities[++pGicCpu->idxRunningPriority] = bPriority;
2178
2179 /* Update the distributor IRQ state to reflect change in active interrupt. */
2180 gicDistUpdateIrqState(pVCpu->CTX_SUFF(pVM), pGicDev);
2181 }
2182 }
2183
2184 LogFlowFunc(("uIntId=%u\n", uIntId));
2185 return uIntId;
2186}
2187
2188
2189/**
2190 * Reads a distributor register.
2191 *
2192 * @returns VBox status code.
2193 * @param pDevIns The device instance.
2194 * @param pVCpu The cross context virtual CPU structure.
2195 * @param offReg The offset of the register being read.
2196 * @param puValue Where to store the register value.
2197 */
2198DECLINLINE(VBOXSTRICTRC) gicDistReadRegister(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t *puValue)
2199{
2200 VMCPU_ASSERT_EMT(pVCpu);
2201 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
2202 uint16_t const cbReg = sizeof(uint32_t);
2203
2204 /*
2205 * GICD_IGROUPR<n> and GICD_IGROUPR<n>E.
2206 */
2207 {
2208 if (offReg - GIC_DIST_REG_IGROUPRn_OFF_START < GIC_DIST_REG_IGROUPRn_RANGE_SIZE)
2209 {
2210 uint16_t const idxReg = (offReg - GIC_DIST_REG_IGROUPRn_OFF_START) / cbReg;
2211 return gicDistReadIntrGroupReg(pGicDev, idxReg, puValue);
2212 }
2213 if (offReg - GIC_DIST_REG_IGROUPRnE_OFF_START < GIC_DIST_REG_IGROUPRnE_RANGE_SIZE)
2214 {
2215 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrGroup) / 2;
2216 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_IGROUPRnE_OFF_START) / cbReg;
2217 return gicDistReadIntrGroupReg(pGicDev, idxReg, puValue);
2218 }
2219 }
2220
2221 /*
2222 * GICD_IROUTER<n> and GICD_IROUTER<n>E.
2223 */
2224 {
2225 if (offReg - GIC_DIST_REG_IROUTERn_OFF_START < GIC_DIST_REG_IROUTERn_RANGE_SIZE)
2226 {
2227 uint16_t const idxReg = (offReg - GIC_DIST_REG_IROUTERn_OFF_START) / cbReg;
2228 return gicDistReadIntrRoutingReg(pGicDev, idxReg, puValue);
2229 }
2230 if (offReg - GIC_DIST_REG_IROUTERnE_OFF_START < GIC_DIST_REG_IROUTERnE_RANGE_SIZE)
2231 {
2232 uint16_t const idxExt = RT_ELEMENTS(pGicDev->au32IntrRouting) / 2;
2233 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_IROUTERnE_OFF_START) / cbReg;
2234 return gicDistReadIntrRoutingReg(pGicDev, idxReg, puValue);
2235 }
2236 }
2237
2238 /*
2239 * GICD_ISENABLER<n> and GICD_ISENABLER<n>E.
2240 * GICD_ICENABLER<n> and GICD_ICENABLER<n>E.
2241 */
2242 {
2243 if (offReg - GIC_DIST_REG_ISENABLERn_OFF_START < GIC_DIST_REG_ISENABLERn_RANGE_SIZE)
2244 {
2245 uint16_t const idxReg = (offReg - GIC_DIST_REG_ISENABLERn_OFF_START) / cbReg;
2246 return gicDistReadIntrEnableReg(pGicDev, idxReg, puValue);
2247 }
2248 if (offReg - GIC_DIST_REG_ISENABLERnE_OFF_START < GIC_DIST_REG_ISENABLERnE_RANGE_SIZE)
2249 {
2250 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrEnabled) / 2;
2251 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ISENABLERnE_OFF_START) / cbReg;
2252 return gicDistReadIntrEnableReg(pGicDev, idxReg, puValue);
2253 }
2254
2255 if (offReg - GIC_DIST_REG_ICENABLERn_OFF_START < GIC_DIST_REG_ICENABLERn_RANGE_SIZE)
2256 {
2257 uint16_t const idxReg = (offReg - GIC_DIST_REG_ICENABLERn_OFF_START) / cbReg;
2258 return gicDistReadIntrEnableReg(pGicDev, idxReg, puValue);
2259 }
2260 if (offReg - GIC_DIST_REG_ICENABLERnE_OFF_START < GIC_DIST_REG_ICENABLERnE_RANGE_SIZE)
2261 {
2262 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrEnabled) / 2;
2263 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ICENABLERnE_OFF_START) / cbReg;
2264 return gicDistReadIntrEnableReg(pGicDev, idxReg, puValue);
2265 }
2266 }
2267
2268 /*
2269 * GICD_ISACTIVER<n> and GICD_ISACTIVER<n>E.
2270 * GICD_ICACTIVER<n> and GICD_ICACTIVER<n>E.
2271 */
2272 {
2273 if (offReg - GIC_DIST_REG_ISACTIVERn_OFF_START < GIC_DIST_REG_ISACTIVERn_RANGE_SIZE)
2274 {
2275 uint16_t const idxReg = (offReg - GIC_DIST_REG_ISACTIVERn_OFF_START) / cbReg;
2276 return gicDistReadIntrActiveReg(pGicDev, idxReg, puValue);
2277 }
2278 if (offReg - GIC_DIST_REG_ISACTIVERnE_OFF_START < GIC_DIST_REG_ISACTIVERnE_RANGE_SIZE)
2279 {
2280 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrActive) / 2;
2281 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ISACTIVERnE_OFF_START) / cbReg;
2282 return gicDistReadIntrActiveReg(pGicDev, idxReg, puValue);
2283 }
2284
2285 if (offReg - GIC_DIST_REG_ICACTIVERn_OFF_START < GIC_DIST_REG_ICACTIVERn_RANGE_SIZE)
2286 {
2287 uint16_t const idxReg = (offReg - GIC_DIST_REG_ICENABLERn_OFF_START) / cbReg;
2288 return gicDistReadIntrActiveReg(pGicDev, idxReg, puValue);
2289 }
2290 if (offReg - GIC_DIST_REG_ICACTIVERnE_OFF_START < GIC_DIST_REG_ICACTIVERnE_RANGE_SIZE)
2291 {
2292 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrActive) / 2;
2293 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ICACTIVERnE_OFF_START) / cbReg;
2294 return gicDistReadIntrActiveReg(pGicDev, idxReg, puValue);
2295 }
2296 }
2297
2298 /*
2299 * GICD_IPRIORITYR<n> and GICD_IPRIORITYR<n>E.
2300 */
2301 {
2302 if (offReg - GIC_DIST_REG_IPRIORITYRn_OFF_START < GIC_DIST_REG_IPRIORITYRn_RANGE_SIZE)
2303 {
2304 uint16_t const idxReg = (offReg - GIC_DIST_REG_IPRIORITYRn_OFF_START) / cbReg;
2305 return gicDistReadIntrPriorityReg(pGicDev, idxReg, puValue);
2306 }
2307 if (offReg - GIC_DIST_REG_IPRIORITYRnE_OFF_START < GIC_DIST_REG_IPRIORITYRnE_RANGE_SIZE)
2308 {
2309 uint16_t const idxExt = RT_ELEMENTS(pGicDev->abIntrPriority) / (2 * sizeof(uint32_t));
2310 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_IPRIORITYRnE_OFF_START) / cbReg;
2311 return gicDistReadIntrPriorityReg(pGicDev, idxReg, puValue);
2312 }
2313 }
2314
2315 /*
2316 * GICD_ISPENDR<n> and GICD_ISPENDR<n>E.
2317 * GICD_ICPENDR<n> and GICD_ICPENDR<n>E.
2318 */
2319 {
2320 if (offReg - GIC_DIST_REG_ISPENDRn_OFF_START < GIC_DIST_REG_ISPENDRn_RANGE_SIZE)
2321 {
2322 uint16_t const idxReg = (offReg - GIC_DIST_REG_ISPENDRn_OFF_START) / cbReg;
2323 return gicDistReadIntrPendingReg(pGicDev, idxReg, puValue);
2324 }
2325 if (offReg - GIC_DIST_REG_ISPENDRnE_OFF_START < GIC_DIST_REG_ISPENDRnE_RANGE_SIZE)
2326 {
2327 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrPending) / 2;
2328 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ISPENDRnE_OFF_START) / cbReg;
2329 return gicDistReadIntrPendingReg(pGicDev, idxReg, puValue);
2330 }
2331
2332 if (offReg - GIC_DIST_REG_ICPENDRn_OFF_START < GIC_DIST_REG_ICPENDRn_RANGE_SIZE)
2333 {
2334 uint16_t const idxReg = (offReg - GIC_DIST_REG_ICPENDRn_OFF_START) / cbReg;
2335 return gicDistReadIntrPendingReg(pGicDev, idxReg, puValue);
2336 }
2337 if (offReg - GIC_DIST_REG_ICPENDRnE_OFF_START < GIC_DIST_REG_ICPENDRnE_RANGE_SIZE)
2338 {
2339 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrPending) / 2;
2340 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ICPENDRnE_OFF_START) / cbReg;
2341 return gicDistReadIntrPendingReg(pGicDev, idxReg, puValue);
2342 }
2343 }
2344
2345 /*
2346 * GICD_ICFGR<n> and GICD_ICFGR<n>E.
2347 */
2348 {
2349 if (offReg - GIC_DIST_REG_ICFGRn_OFF_START < GIC_DIST_REG_ICFGRn_RANGE_SIZE)
2350 {
2351 uint16_t const idxReg = (offReg - GIC_DIST_REG_ICFGRn_OFF_START) / cbReg;
2352 return gicDistReadIntrConfigReg(pGicDev, idxReg, puValue);
2353 }
2354 if (offReg - GIC_DIST_REG_ICFGRnE_OFF_START < GIC_DIST_REG_ICFGRnE_RANGE_SIZE)
2355 {
2356 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrConfig) / 2;
2357 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ICFGRnE_OFF_START) / cbReg;
2358 return gicDistReadIntrConfigReg(pGicDev, idxReg, puValue);
2359 }
2360 }
2361
2362 switch (offReg)
2363 {
2364 case GIC_DIST_REG_CTLR_OFF:
2365 Assert(pGicDev->fAffRoutingEnabled); /* We don't support GICv2 backwards compatibility, so ARE bit must be set. */
2366 *puValue = (pGicDev->fIrqGrp0Enabled ? GIC_DIST_REG_CTRL_ENABLE_GRP0 : 0)
2367 | (pGicDev->fIrqGrp1Enabled ? GIC_DIST_REG_CTRL_ENABLE_GRP1_NS : 0)
2368 | GIC_DIST_REG_CTRL_DS
2369 | (pGicDev->fAffRoutingEnabled ? GIC_DIST_REG_CTRL_ARE_S : 0);
2370 break;
2371 case GIC_DIST_REG_TYPER_OFF:
2372 Assert(pGicDev->uMaxSpi > 0 && pGicDev->uMaxSpi < GIC_DIST_REG_TYPER_NUM_ITLINES);
2373 *puValue = GIC_DIST_REG_TYPER_NUM_ITLINES_SET(pGicDev->uMaxSpi)
2374 | GIC_DIST_REG_TYPER_NUM_PES_SET(0) /* 1 PE */ /** @todo r=ramshankar: Should this be pVCpu->cCpus? Currently it means 'ARE' must always be used? */
2375 /*| GIC_DIST_REG_TYPER_ESPI*/ /** @todo */
2376 /*| GIC_DIST_REG_TYPER_NMI*/ /** @todo Non-maskable interrupts */
2377 /*| GIC_DIST_REG_TYPER_SECURITY_EXTN */ /** @todo */
2378 /*| GIC_DIST_REG_TYPER_MBIS */ /** @todo Message based interrupts */
2379 /*| GIC_DIST_REG_TYPER_LPIS */ /** @todo Support LPIs */
2380 | (pGicDev->fRangeSelSupport ? GIC_DIST_REG_TYPER_RSS : 0)
2381 | GIC_DIST_REG_TYPER_IDBITS_SET(16);
2382 break;
2383 case GIC_DIST_REG_STATUSR_OFF:
2384 AssertReleaseFailed();
2385 break;
2386#if 0
2387 case GIC_DIST_REG_IGROUPRn_OFF_START: /* Only 32 lines for now. */
2388 AssertReleaseFailed();
2389 break;
2390 case GIC_DIST_REG_ISENABLERn_OFF_START + 4: /* Only 32 lines for now. */
2391 case GIC_DIST_REG_ICENABLERn_OFF_START + 4: /* Only 32 lines for now. */
2392 *puValue = ASMAtomicReadU32(&pThis->bmIntEnabled);
2393 break;
2394#endif
2395 case GIC_DIST_REG_ISPENDRn_OFF_START: /* Only 32 lines for now. */
2396 AssertReleaseFailed();
2397 break;
2398 case GIC_DIST_REG_ICPENDRn_OFF_START: /* Only 32 lines for now. */
2399 AssertReleaseFailed();
2400 break;
2401 case GIC_DIST_REG_ISACTIVERn_OFF_START: /* Only 32 lines for now. */
2402 AssertReleaseFailed();
2403 break;
2404 case GIC_DIST_REG_ICACTIVERn_OFF_START: /* Only 32 lines for now. */
2405 AssertReleaseFailed();
2406 break;
2407#if 0
2408 case GIC_DIST_REG_IPRIORITYRn_OFF_START:
2409 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 4: /* These are banked for the PEs and access the redistributor. */
2410 {
2411 PGICCPU pGicVCpu = VMCPU_TO_GICCPU(pVCpu);
2412
2413 /* Figure out the register which is written. */
2414 uint8_t idxPrio = offReg - GIC_DIST_REG_IPRIORITYRn_OFF_START;
2415 Assert(idxPrio <= RT_ELEMENTS(pThis->abIntPriority) - sizeof(uint32_t));
2416
2417 uint32_t u32Value = 0;
2418 for (uint32_t i = idxPrio; i < idxPrio + sizeof(uint32_t); i++)
2419 u32Value |= pGicVCpu->abIntPriority[i] << ((i - idxPrio) * 8);
2420
2421 *puValue = u32Value;
2422 break;
2423 }
2424 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 32: /* Only 32 lines for now. */
2425 {
2426 /* Figure out the register which is written. */
2427 uint8_t idxPrio = offReg - GIC_DIST_REG_IPRIORITYRn_OFF_START - 32;
2428 Assert(idxPrio <= RT_ELEMENTS(pThis->abIntPriority) - sizeof(uint32_t));
2429
2430 uint32_t u32Value = 0;
2431 for (uint32_t i = idxPrio; i < idxPrio + sizeof(uint32_t); i++)
2432 u32Value |= pThis->abIntPriority[i] << ((i - idxPrio) * 8);
2433
2434 *puValue = u32Value;
2435 break;
2436 }
2437#endif
2438 case GIC_DIST_REG_ITARGETSRn_OFF_START: /* Only 32 lines for now. */
2439 AssertReleaseFailed();
2440 break;
2441 case GIC_DIST_REG_ICFGRn_OFF_START: /* Only 32 lines for now. */
2442 AssertReleaseFailed();
2443 break;
2444 case GIC_DIST_REG_IGRPMODRn_OFF_START: /* Only 32 lines for now. */
2445 AssertReleaseFailed();
2446 break;
2447 case GIC_DIST_REG_NSACRn_OFF_START: /* Only 32 lines for now. */
2448 AssertReleaseFailed();
2449 break;
2450 case GIC_DIST_REG_SGIR_OFF:
2451 AssertReleaseFailed();
2452 break;
2453 case GIC_DIST_REG_CPENDSGIRn_OFF_START:
2454 AssertReleaseFailed();
2455 break;
2456 case GIC_DIST_REG_SPENDSGIRn_OFF_START:
2457 AssertReleaseFailed();
2458 break;
2459 case GIC_DIST_REG_INMIn_OFF_START:
2460 AssertReleaseFailed();
2461 break;
2462 case GIC_DIST_REG_PIDR2_OFF:
2463#if 0
2464 Assert(pThis->uArchRev <= GIC_DIST_REG_PIDR2_ARCH_REV_GICV4);
2465 *puValue = GIC_DIST_REG_PIDR2_ARCH_REV_SET(pThis->uArchRev);
2466#else
2467 Assert(pGicDev->uArchRev <= GIC_DIST_REG_PIDR2_ARCH_REV_GICV4);
2468 *puValue = GIC_DIST_REG_PIDR2_ARCH_REV_SET(pGicDev->uArchRev);
2469#endif
2470 break;
2471 case GIC_DIST_REG_IIDR_OFF:
2472 *puValue = 0x43b; /* JEP106 code 0x43b is an ARM implementation. */
2473 break;
2474 case GIC_DIST_REG_TYPER2_OFF:
2475 *puValue = 0;
2476 break;
2477#if 0
2478 case GIC_DIST_REG_IROUTERn_OFF_START:
2479 AssertFailed();
2480 break;
2481#endif
2482 default:
2483 *puValue = 0;
2484 }
2485 return VINF_SUCCESS;
2486}
2487
2488
2489/**
2490 * Writes a distributor register.
2491 *
2492 * @returns Strict VBox status code.
2493 * @param pDevIns The device instance.
2494 * @param pVCpu The cross context virtual CPU structure.
2495 * @param offReg The offset of the register being written.
2496 * @param uValue The register value.
2497 */
2498DECLINLINE(VBOXSTRICTRC) gicDistWriteRegister(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t uValue)
2499{
2500 VMCPU_ASSERT_EMT(pVCpu);
2501 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
2502 PVMCC pVM = PDMDevHlpGetVM(pDevIns);
2503 uint16_t const cbReg = sizeof(uint32_t);
2504
2505 /*
2506 * GICD_IGROUPR<n> and GICD_IGROUPR<n>E.
2507 */
2508 {
2509 if (offReg - GIC_DIST_REG_IGROUPRn_OFF_START < GIC_DIST_REG_IGROUPRn_RANGE_SIZE)
2510 {
2511 uint16_t const idxReg = (offReg - GIC_DIST_REG_IGROUPRn_OFF_START) / cbReg;
2512 return gicDistWriteIntrGroupReg(pVM, pGicDev, idxReg, uValue);
2513 }
2514 if (offReg - GIC_DIST_REG_IGROUPRnE_OFF_START < GIC_DIST_REG_IGROUPRnE_RANGE_SIZE)
2515 {
2516 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrGroup) / 2;
2517 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_IGROUPRnE_OFF_START) / cbReg;
2518 return gicDistWriteIntrGroupReg(pVM, pGicDev, idxReg, uValue);
2519 }
2520 }
2521
2522 /*
2523 * GICD_IROUTER<n> and GICD_IROUTER<n>E.
2524 */
2525 {
2526 if (offReg - GIC_DIST_REG_IROUTERn_OFF_START < GIC_DIST_REG_IROUTERn_RANGE_SIZE)
2527 {
2528 uint16_t const idxReg = (offReg - GIC_DIST_REG_IROUTERn_OFF_START) / cbReg;
2529 return gicDistWriteIntrRoutingReg(pGicDev, offReg, idxReg, uValue);
2530 }
2531 if (offReg - GIC_DIST_REG_IROUTERnE_OFF_START < GIC_DIST_REG_IROUTERnE_RANGE_SIZE)
2532 {
2533 uint16_t const idxExt = RT_ELEMENTS(pGicDev->au32IntrRouting) / 2;
2534 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_IROUTERnE_OFF_START) / cbReg;
2535 return gicDistWriteIntrRoutingReg(pGicDev, offReg, idxReg, uValue);
2536 }
2537 }
2538
2539 /*
2540 * GICD_ISENABLER<n> and GICD_ISENABLER<n>E.
2541 * GICD_ICENABLER<n> and GICD_ICENABLER<n>E.
2542 */
2543 {
2544 if (offReg - GIC_DIST_REG_ISENABLERn_OFF_START < GIC_DIST_REG_ISENABLERn_RANGE_SIZE)
2545 {
2546 uint16_t const idxReg = (offReg - GIC_DIST_REG_ISENABLERn_OFF_START) / cbReg;
2547 return gicDistWriteIntrSetEnableReg(pVM, pGicDev, idxReg, uValue);
2548 }
2549 if (offReg - GIC_DIST_REG_ISENABLERnE_OFF_START < GIC_DIST_REG_ISENABLERnE_RANGE_SIZE)
2550 {
2551 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrEnabled) / 2;
2552 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ISENABLERnE_OFF_START) / cbReg;
2553 return gicDistWriteIntrSetEnableReg(pVM, pGicDev, idxReg, uValue);
2554 }
2555
2556 if (offReg - GIC_DIST_REG_ICENABLERn_OFF_START < GIC_DIST_REG_ICENABLERn_RANGE_SIZE)
2557 {
2558 uint16_t const idxReg = (offReg - GIC_DIST_REG_ICENABLERn_OFF_START) / cbReg;
2559 return gicDistWriteIntrClearEnableReg(pVM, pGicDev, idxReg, uValue);
2560 }
2561 if (offReg - GIC_DIST_REG_ICENABLERnE_OFF_START < GIC_DIST_REG_ICENABLERnE_RANGE_SIZE)
2562 {
2563 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrEnabled) / 2;
2564 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ICENABLERnE_OFF_START) / cbReg;
2565 return gicDistWriteIntrClearEnableReg(pVM, pGicDev, idxReg, uValue);
2566 }
2567 }
2568
2569 /*
2570 * GICD_ISACTIVER<n> and GICD_ISACTIVER<n>E.
2571 * GICD_ICACTIVER<n> and GICD_ICACTIVER<n>E.
2572 */
2573 {
2574 if (offReg - GIC_DIST_REG_ISACTIVERn_OFF_START < GIC_DIST_REG_ISACTIVERn_RANGE_SIZE)
2575 {
2576 uint16_t const idxReg = (offReg - GIC_DIST_REG_ISACTIVERn_OFF_START) / cbReg;
2577 return gicDistWriteIntrSetActiveReg(pVM, pGicDev, idxReg, uValue);
2578 }
2579 if (offReg - GIC_DIST_REG_ISACTIVERnE_OFF_START < GIC_DIST_REG_ISACTIVERnE_RANGE_SIZE)
2580 {
2581 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrActive) / 2;
2582 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ISACTIVERnE_OFF_START) / cbReg;
2583 return gicDistWriteIntrSetActiveReg(pVM, pGicDev, idxReg, uValue);
2584 }
2585
2586 if (offReg - GIC_DIST_REG_ICACTIVERn_OFF_START < GIC_DIST_REG_ICACTIVERn_RANGE_SIZE)
2587 {
2588 uint16_t const idxReg = (offReg - GIC_DIST_REG_ICACTIVERn_OFF_START) / cbReg;
2589 return gicDistWriteIntrClearActiveReg(pVM, pGicDev, idxReg, uValue);
2590 }
2591 if (offReg - GIC_DIST_REG_ICACTIVERnE_OFF_START < GIC_DIST_REG_ICACTIVERnE_RANGE_SIZE)
2592 {
2593 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrActive) / 2;
2594 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ICACTIVERnE_OFF_START) / cbReg;
2595 return gicDistWriteIntrClearActiveReg(pVM, pGicDev, idxReg, uValue);
2596 }
2597 }
2598
2599 /*
2600 * GICD_IPRIORITYR<n> and GICD_IPRIORITYR<n>E.
2601 */
2602 {
2603 if (offReg - GIC_DIST_REG_IPRIORITYRn_OFF_START < GIC_DIST_REG_IPRIORITYRn_RANGE_SIZE)
2604 {
2605 uint16_t const idxReg = (offReg - GIC_DIST_REG_IPRIORITYRn_OFF_START) / cbReg;
2606 return gicDistWriteIntrPriorityReg(pGicDev, idxReg, uValue);
2607 }
2608 if (offReg - GIC_DIST_REG_IPRIORITYRnE_OFF_START < GIC_DIST_REG_IPRIORITYRnE_RANGE_SIZE)
2609 {
2610 uint16_t const idxExt = RT_ELEMENTS(pGicDev->abIntrPriority) / (2 * sizeof(uint32_t));
2611 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_IPRIORITYRnE_OFF_START) / cbReg;
2612 return gicDistWriteIntrPriorityReg(pGicDev, idxReg, uValue);
2613 }
2614 }
2615
2616 /*
2617 * GICD_ISPENDR<n> and GICD_ISPENDR<n>E.
2618 * GICD_ICPENDR<n> and GICD_ICPENDR<n>E.
2619 */
2620 {
2621 if (offReg - GIC_DIST_REG_ISPENDRn_OFF_START < GIC_DIST_REG_ISPENDRn_RANGE_SIZE)
2622 {
2623 uint16_t const idxReg = (offReg - GIC_DIST_REG_ISPENDRn_OFF_START) / cbReg;
2624 return gicDistWriteIntrSetPendingReg(pVM, pGicDev, idxReg, uValue);
2625 }
2626 if (offReg - GIC_DIST_REG_ISPENDRnE_OFF_START < GIC_DIST_REG_ISPENDRnE_RANGE_SIZE)
2627 {
2628 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrPending) / 2;
2629 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ISPENDRnE_OFF_START) / cbReg;
2630 return gicDistWriteIntrSetPendingReg(pVM, pGicDev, idxReg, uValue);
2631 }
2632
2633 if (offReg - GIC_DIST_REG_ICPENDRn_OFF_START < GIC_DIST_REG_ICPENDRn_RANGE_SIZE)
2634 {
2635 uint16_t const idxReg = (offReg - GIC_DIST_REG_ICPENDRn_OFF_START) / cbReg;
2636 return gicDistWriteIntrClearPendingReg(pVM, pGicDev, idxReg, uValue);
2637 }
2638 if (offReg - GIC_DIST_REG_ICPENDRnE_OFF_START < GIC_DIST_REG_ICPENDRnE_RANGE_SIZE)
2639 {
2640 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrPending) / 2;
2641 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ICPENDRnE_OFF_START) / cbReg;
2642 return gicDistWriteIntrClearPendingReg(pVM, pGicDev, idxReg, uValue);
2643 }
2644 }
2645
2646 /*
2647 * GICD_ICFGR<n> and GICD_ICFGR<n>E.
2648 */
2649 {
2650 if (offReg - GIC_DIST_REG_ICFGRn_OFF_START + cbReg < GIC_DIST_REG_ICFGRn_RANGE_SIZE)
2651 {
2652 uint16_t const idxReg = (offReg - GIC_DIST_REG_ICFGRn_OFF_START) / cbReg;
2653 return gicDistWriteIntrConfigReg(pGicDev, idxReg, uValue);
2654 }
2655 if (offReg - GIC_DIST_REG_ICFGRnE_OFF_START < GIC_DIST_REG_ICFGRnE_RANGE_SIZE)
2656 {
2657 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrConfig) / 2;
2658 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ICFGRnE_OFF_START) / cbReg;
2659 return gicDistWriteIntrConfigReg(pGicDev, idxReg, uValue);
2660 }
2661 }
2662
2663 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
2664 switch (offReg)
2665 {
2666 case GIC_DIST_REG_CTLR_OFF:
2667#if 0
2668 ASMAtomicWriteBool(&pThis->fIrqGrp0Enabled, RT_BOOL(uValue & GIC_DIST_REG_CTRL_ENABLE_GRP0));
2669 ASMAtomicWriteBool(&pThis->fIrqGrp1Enabled, RT_BOOL(uValue & GIC_DIST_REG_CTRL_ENABLE_GRP1_NS));
2670 Assert(!(uValue & GIC_DIST_REG_CTRL_ARE_NS));
2671 rcStrict = gicDistUpdateIrqState(pVM, pThis);
2672#else
2673 Assert(!(uValue & GIC_DIST_REG_CTRL_ARE_NS));
2674 pGicDev->fIrqGrp0Enabled = RT_BOOL(uValue & GIC_DIST_REG_CTRL_ENABLE_GRP0);
2675 pGicDev->fIrqGrp1Enabled = RT_BOOL(uValue & GIC_DIST_REG_CTRL_ENABLE_GRP1_NS);
2676 rcStrict = gicDistUpdateIrqState(pVM, pGicDev);
2677#endif
2678 break;
2679 case GIC_DIST_REG_STATUSR_OFF:
2680 AssertReleaseFailed();
2681 break;
2682 case GIC_DIST_REG_SETSPI_NSR_OFF:
2683 AssertReleaseFailed();
2684 break;
2685 case GIC_DIST_REG_CLRSPI_NSR_OFF:
2686 AssertReleaseFailed();
2687 break;
2688 case GIC_DIST_REG_SETSPI_SR_OFF:
2689 AssertReleaseFailed();
2690 break;
2691 case GIC_DIST_REG_CLRSPI_SR_OFF:
2692 AssertReleaseFailed();
2693 break;
2694#if 0
2695 case GIC_DIST_REG_IGROUPRn_OFF_START: /* Only 32 lines for now. */
2696 AssertReleaseFailed();
2697 break;
2698 case GIC_DIST_REG_IGROUPRn_OFF_START + 4: /* Only 32 lines for now. */
2699 ASMAtomicOrU32(&pThis->u32RegIGrp0, uValue);
2700 rcStrict = gicDistUpdateIrqState(pVM, pThis);
2701 break;
2702 case GIC_DIST_REG_ISENABLERn_OFF_START + 4: /* Only 32 lines for now. */
2703 ASMAtomicOrU32(&pThis->bmIntEnabled, uValue);
2704 rcStrict = gicDistUpdateIrqState(pVM, pThis);
2705 break;
2706 case GIC_DIST_REG_ICENABLERn_OFF_START:
2707 AssertReleaseFailed();
2708 break;
2709 case GIC_DIST_REG_ICENABLERn_OFF_START + 4: /* Only 32 lines for now. */
2710 ASMAtomicAndU32(&pThis->bmIntEnabled, ~uValue);
2711 rcStrict = gicDistUpdateIrqState(pVM, pThis);
2712 break;
2713 case GIC_DIST_REG_ISPENDRn_OFF_START: /* Only 32 lines for now. */
2714 AssertReleaseFailed();
2715 break;
2716 case GIC_DIST_REG_ICPENDRn_OFF_START: /* Only 32 lines for now. */
2717 AssertReleaseFailed();
2718 break;
2719 case GIC_DIST_REG_ISACTIVERn_OFF_START: /* Only 32 lines for now. */
2720 AssertReleaseFailed();
2721 break;
2722 case GIC_DIST_REG_ICACTIVERn_OFF_START + 4: /* Only 32 lines for now. */
2723 ASMAtomicAndU32(&pThis->bmIntActive, ~uValue);
2724 rcStrict = gicDistUpdateIrqState(pVM, pThis);
2725 break;
2726 case GIC_DIST_REG_IPRIORITYRn_OFF_START: /* These are banked for the PEs and access the redistributor. */
2727 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 4:
2728 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 8:
2729 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 12:
2730 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 16:
2731 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 20:
2732 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 24:
2733 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 28:
2734 {
2735 PGICCPU pGicVCpu = VMCPU_TO_GICCPU(pVCpu);
2736
2737 /* Figure out the register which is written. */
2738 uint8_t idxPrio = offReg - GIC_DIST_REG_IPRIORITYRn_OFF_START;
2739 Assert(idxPrio <= RT_ELEMENTS(pGicVCpu->abIntPriority) - sizeof(uint32_t));
2740 for (uint32_t i = idxPrio; i < idxPrio + sizeof(uint32_t); i++)
2741 {
2742 pGicVCpu->abIntPriority[i] = (uint8_t)(uValue & 0xff);
2743 uValue >>= 8;
2744 }
2745 break;
2746 }
2747 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 32: /* Only 32 lines for now. */
2748 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 36:
2749 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 40:
2750 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 44:
2751 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 48:
2752 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 52:
2753 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 56:
2754 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 60:
2755 {
2756 /* Figure out the register which is written. */
2757 uint8_t idxPrio = offReg - GIC_DIST_REG_IPRIORITYRn_OFF_START - 32;
2758 Assert(idxPrio <= RT_ELEMENTS(pThis->abIntPriority) - sizeof(uint32_t));
2759 for (uint32_t i = idxPrio; i < idxPrio + sizeof(uint32_t); i++)
2760 {
2761#if 1
2762 /** @todo r=aeichner This gross hack prevents Windows from hanging during boot because
2763 * it tries to set the interrupt priority for PCI interrupt lines to 0 which will cause an interrupt
2764 * storm later on because the lowest interrupt priority Windows seems to use is 32 for the per vCPU
2765 * timer.
2766 */
2767 if ((uValue & 0xff) == 0)
2768 {
2769 uValue >>= 8;
2770 continue;
2771 }
2772#endif
2773 pThis->abIntPriority[i] = (uint8_t)(uValue & 0xff);
2774 uValue >>= 8;
2775 }
2776 break;
2777 }
2778#endif
2779 case GIC_DIST_REG_ITARGETSRn_OFF_START: /* Only 32 lines for now. */
2780 AssertReleaseFailed();
2781 break;
2782#if 0
2783 case GIC_DIST_REG_ICFGRn_OFF_START + 8: /* Only 32 lines for now. */
2784 ASMAtomicWriteU32(&pThis->u32RegICfg0, uValue);
2785 break;
2786 case GIC_DIST_REG_ICFGRn_OFF_START+ 12:
2787 ASMAtomicWriteU32(&pThis->u32RegICfg1, uValue);
2788 break;
2789#endif
2790 case GIC_DIST_REG_IGRPMODRn_OFF_START: /* Only 32 lines for now. */
2791 AssertReleaseFailed();
2792 break;
2793 case GIC_DIST_REG_NSACRn_OFF_START: /* Only 32 lines for now. */
2794 AssertReleaseFailed();
2795 break;
2796 case GIC_DIST_REG_SGIR_OFF:
2797 AssertReleaseFailed();
2798 break;
2799 case GIC_DIST_REG_CPENDSGIRn_OFF_START:
2800 AssertReleaseFailed();
2801 break;
2802 case GIC_DIST_REG_SPENDSGIRn_OFF_START:
2803 AssertReleaseFailed();
2804 break;
2805 case GIC_DIST_REG_INMIn_OFF_START:
2806 AssertReleaseFailed();
2807 break;
2808 default:
2809 //AssertReleaseFailed();
2810 break;
2811 }
2812
2813 return rcStrict;
2814}
2815
2816
2817/**
2818 * Reads a GIC redistributor register.
2819 *
2820 * @returns VBox status code.
2821 * @param pDevIns The device instance.
2822 * @param pVCpu The cross context virtual CPU structure.
2823 * @param idRedist The redistributor ID.
2824 * @param offReg The offset of the register being read.
2825 * @param puValue Where to store the register value.
2826 */
2827DECLINLINE(VBOXSTRICTRC) gicReDistReadRegister(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint32_t idRedist, uint16_t offReg, uint32_t *puValue)
2828{
2829 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
2830 Assert(idRedist == pVCpu->idCpu); /* Assert for now, maybe remove function parameter later. */
2831 switch (offReg)
2832 {
2833 case GIC_REDIST_REG_TYPER_OFF:
2834 {
2835 PCVMCC pVM = PDMDevHlpGetVM(pDevIns);
2836 *puValue = ((pVCpu->idCpu == pVM->cCpus - 1) ? GIC_REDIST_REG_TYPER_LAST : 0)
2837 | GIC_REDIST_REG_TYPER_CPU_NUMBER_SET(idRedist)
2838 | GIC_REDIST_REG_TYPER_CMN_LPI_AFF_SET(GIC_REDIST_REG_TYPER_CMN_LPI_AFF_ALL);
2839 break;
2840 }
2841 case GIC_REDIST_REG_IIDR_OFF:
2842 *puValue = 0x43b; /* JEP106 code 0x43b is an ARM implementation. */
2843 break;
2844 case GIC_REDIST_REG_TYPER_AFFINITY_OFF:
2845 *puValue = idRedist;
2846 break;
2847 case GIC_REDIST_REG_PIDR2_OFF:
2848 Assert(pGicDev->uArchRev <= GIC_DIST_REG_PIDR2_ARCH_REV_GICV4);
2849 *puValue = GIC_REDIST_REG_PIDR2_ARCH_REV_SET(pGicDev->uArchRev);
2850 break;
2851 default:
2852 *puValue = 0;
2853 }
2854 return VINF_SUCCESS;
2855}
2856
2857
2858/**
2859 * Reads a GIC redistributor SGI/PPI frame register.
2860 *
2861 * @returns VBox status code.
2862 * @param pDevIns The device instance.
2863 * @param pVCpu The cross context virtual CPU structure.
2864 * @param offReg The offset of the register being read.
2865 * @param puValue Where to store the register value.
2866 */
2867DECLINLINE(VBOXSTRICTRC) gicReDistReadSgiPpiRegister(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t *puValue)
2868{
2869 VMCPU_ASSERT_EMT(pVCpu);
2870 RT_NOREF(pDevIns);
2871
2872 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
2873 PCGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
2874 uint16_t const cbReg = sizeof(uint32_t);
2875
2876#if 1
2877 /*
2878 * GICR_IGROUPR0 and GICR_IGROUPR<n>E.
2879 */
2880 {
2881 if (offReg - GIC_REDIST_SGI_PPI_REG_IGROUPR0_OFF < GIC_REDIST_SGI_PPI_REG_IGROUPRnE_RANGE_SIZE)
2882 {
2883 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_IGROUPR0_OFF) / cbReg;
2884 return gicReDistReadIntrGroupReg(pGicDev, pGicCpu, idxReg, puValue);
2885 }
2886 }
2887
2888 /*
2889 * GICR_ISENABLER0 and GICR_ISENABLER<n>E.
2890 * GICR_ICENABLER0 and GICR_ICENABLER<n>E.
2891 */
2892 {
2893 if (offReg - GIC_REDIST_SGI_PPI_REG_ISENABLER0_OFF < GIC_REDIST_SGI_PPI_REG_ISENABLERnE_RANGE_SIZE)
2894 {
2895 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ISENABLER0_OFF) / cbReg;
2896 return gicReDistReadIntrEnableReg(pGicDev, pGicCpu, idxReg, puValue);
2897 }
2898 if (offReg - GIC_REDIST_SGI_PPI_REG_ICENABLER0_OFF < GIC_REDIST_SGI_PPI_REG_ICENABLERnE_RANGE_SIZE)
2899 {
2900 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ICENABLERnE_OFF_START) / cbReg;
2901 return gicReDistReadIntrEnableReg(pGicDev, pGicCpu, idxReg, puValue);
2902 }
2903 }
2904
2905 /*
2906 * GICR_ISACTIVER0 and GICR_ISACTIVER<n>E.
2907 * GICR_ICACTIVER0 and GICR_ICACTIVER<n>E.
2908 */
2909 {
2910 if (offReg - GIC_REDIST_SGI_PPI_REG_ISACTIVER0_OFF < GIC_REDIST_SGI_PPI_REG_ISACTIVERnE_RANGE_SIZE)
2911 {
2912 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ISACTIVER0_OFF) / cbReg;
2913 return gicReDistReadIntrActiveReg(pGicCpu, idxReg, puValue);
2914 }
2915 if (offReg - GIC_REDIST_SGI_PPI_REG_ICACTIVER0_OFF < GIC_REDIST_SGI_PPI_REG_ICACTIVERnE_RANGE_SIZE)
2916 {
2917 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ICACTIVER0_OFF) / cbReg;
2918 return gicReDistReadIntrActiveReg(pGicCpu, idxReg, puValue);
2919 }
2920 }
2921
2922 /*
2923 * GICR_ISPENDR0 and GICR_ISPENDR<n>E.
2924 * GICR_ICPENDR0 and GICR_ICPENDR<n>E.
2925 */
2926 {
2927 if (offReg - GIC_REDIST_SGI_PPI_REG_ISPENDR0_OFF < GIC_REDIST_SGI_PPI_REG_ISPENDRnE_RANGE_SIZE)
2928 {
2929 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ISPENDR0_OFF) / cbReg;
2930 return gicReDistReadIntrPendingReg(pGicDev, pGicCpu, idxReg, puValue);
2931 }
2932 if (offReg - GIC_REDIST_SGI_PPI_REG_ICPENDR0_OFF < GIC_REDIST_SGI_PPI_REG_ICPENDRnE_RANGE_SIZE)
2933 {
2934 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ICPENDR0_OFF) / cbReg;
2935 return gicReDistReadIntrPendingReg(pGicDev, pGicCpu, idxReg, puValue);
2936 }
2937 }
2938
2939 /*
2940 * GICR_IPRIORITYR<n> and GICR_IPRIORITYR<n>E.
2941 */
2942 {
2943 if (offReg - GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START < GIC_REDIST_SGI_PPI_REG_IPRIORITYRnE_RANGE_SIZE)
2944 {
2945 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START) / cbReg;
2946 return gicReDistReadIntrPriorityReg(pGicDev, pGicCpu, idxReg, puValue);
2947 }
2948 }
2949
2950 /*
2951 * GICR_ICFGR0, GICR_ICFGR1 and GICR_ICFGR<n>E.
2952 */
2953 {
2954 if (offReg - GIC_REDIST_SGI_PPI_REG_ICFGR0_OFF < GIC_REDIST_SGI_PPI_REG_ICFGRnE_RANGE_SIZE)
2955 {
2956 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ICFGR0_OFF) / cbReg;
2957 return gicReDistReadIntrConfigReg(pGicDev, pGicCpu, idxReg, puValue);
2958 }
2959 }
2960
2961 AssertReleaseFailed();
2962 *puValue = 0;
2963 return VINF_SUCCESS;
2964#else
2965 switch (offReg)
2966 {
2967 case GIC_REDIST_SGI_PPI_REG_ISENABLER0_OFF:
2968 case GIC_REDIST_SGI_PPI_REG_ICENABLER0_OFF:
2969 *puValue = ASMAtomicReadU32(&pThis->bmIntEnabled);
2970 break;
2971 case GIC_REDIST_SGI_PPI_REG_ISPENDR0_OFF:
2972 case GIC_REDIST_SGI_PPI_REG_ICPENDR0_OFF:
2973 *puValue = ASMAtomicReadU32(&pThis->bmIntPending);
2974 break;
2975 case GIC_REDIST_SGI_PPI_REG_ISACTIVER0_OFF:
2976 case GIC_REDIST_SGI_PPI_REG_ICACTIVER0_OFF:
2977 *puValue = ASMAtomicReadU32(&pThis->bmIntActive);
2978 break;
2979 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START:
2980 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 4:
2981 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 8:
2982 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 12:
2983 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 16:
2984 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 20:
2985 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 24:
2986 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 28:
2987 {
2988 /* Figure out the register which is written. */
2989 uint8_t idxPrio = offReg - GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START;
2990 Assert(idxPrio <= RT_ELEMENTS(pThis->abIntPriority) - sizeof(uint32_t));
2991
2992 uint32_t u32Value = 0;
2993 for (uint32_t i = idxPrio; i < idxPrio + sizeof(uint32_t); i++)
2994 u32Value |= pThis->abIntPriority[i] << ((i - idxPrio) * 8);
2995
2996 *puValue = u32Value;
2997 break;
2998 }
2999 case GIC_REDIST_SGI_PPI_REG_ICFGR0_OFF:
3000 *puValue = ASMAtomicReadU32(&pThis->u32RegICfg0);
3001 break;
3002 case GIC_REDIST_SGI_PPI_REG_ICFGR1_OFF:
3003 *puValue = ASMAtomicReadU32(&pThis->u32RegICfg1);
3004 break;
3005 default:
3006 AssertReleaseFailed();
3007 *puValue = 0;
3008 }
3009
3010 return VINF_SUCCESS;
3011#endif
3012}
3013
3014
3015/**
3016 * Writes a GIC redistributor frame register.
3017 *
3018 * @returns Strict VBox status code.
3019 * @param pDevIns The device instance.
3020 * @param pVCpu The cross context virtual CPU structure.
3021 * @param offReg The offset of the register being written.
3022 * @param uValue The register value.
3023 */
3024DECLINLINE(VBOXSTRICTRC) gicReDistWriteRegister(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t uValue)
3025{
3026 VMCPU_ASSERT_EMT(pVCpu);
3027 RT_NOREF(pDevIns, pVCpu, uValue);
3028
3029 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
3030 switch (offReg)
3031 {
3032 case GIC_REDIST_REG_STATUSR_OFF:
3033 AssertReleaseFailed();
3034 break;
3035 case GIC_REDIST_REG_WAKER_OFF:
3036 Assert(uValue == 0);
3037 break;
3038 case GIC_REDIST_REG_PARTIDR_OFF:
3039 AssertReleaseFailed();
3040 break;
3041 case GIC_REDIST_REG_SETLPIR_OFF:
3042 AssertReleaseFailed();
3043 break;
3044 case GIC_REDIST_REG_CLRLPIR_OFF:
3045 AssertReleaseFailed();
3046 break;
3047 case GIC_REDIST_REG_PROPBASER_OFF:
3048 AssertReleaseFailed();
3049 break;
3050 case GIC_REDIST_REG_PENDBASER_OFF:
3051 AssertReleaseFailed();
3052 break;
3053 case GIC_REDIST_REG_INVLPIR_OFF:
3054 AssertReleaseFailed();
3055 break;
3056 case GIC_REDIST_REG_INVALLR_OFF:
3057 AssertReleaseFailed();
3058 break;
3059 default:
3060 AssertReleaseFailed();
3061 break;
3062 }
3063
3064 return rcStrict;
3065}
3066
3067
3068/**
3069 * Writes a GIC redistributor SGI/PPI frame register.
3070 *
3071 * @returns Strict VBox status code.
3072 * @param pDevIns The device instance.
3073 * @param pVCpu The cross context virtual CPU structure.
3074 * @param offReg The offset of the register being written.
3075 * @param uValue The register value.
3076 */
3077DECLINLINE(VBOXSTRICTRC) gicReDistWriteSgiPpiRegister(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t uValue)
3078{
3079 VMCPU_ASSERT_EMT(pVCpu);
3080
3081#if 1
3082 PCGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PCGICDEV);
3083
3084 /*
3085 * GICR_IGROUPR0 and GICR_IGROUPR<n>E.
3086 */
3087 {
3088 uint16_t const cbReg = sizeof(uint32_t);
3089 if (offReg - GIC_REDIST_SGI_PPI_REG_IGROUPR0_OFF < GIC_REDIST_SGI_PPI_REG_IGROUPRnE_RANGE_SIZE)
3090 {
3091 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_IGROUPR0_OFF) / cbReg;
3092 return gicReDistWriteIntrGroupReg(pGicDev, pVCpu, idxReg, uValue);
3093 }
3094 }
3095
3096 /*
3097 * GICR_ISENABLER0 and GICR_ISENABLER<n>E.
3098 * GICR_ICENABLER0 and GICR_ICENABLER<n>E.
3099 */
3100 {
3101 uint16_t const cbReg = sizeof(uint32_t);
3102 if (offReg - GIC_REDIST_SGI_PPI_REG_ISENABLER0_OFF < GIC_REDIST_SGI_PPI_REG_ISENABLERnE_RANGE_SIZE)
3103 {
3104 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ISENABLER0_OFF) / cbReg;
3105 return gicReDistWriteIntrSetEnableReg(pGicDev, pVCpu, idxReg, uValue);
3106 }
3107 if (offReg - GIC_REDIST_SGI_PPI_REG_ICENABLER0_OFF < GIC_REDIST_SGI_PPI_REG_ICENABLERnE_RANGE_SIZE)
3108 {
3109 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ICENABLER0_OFF) / cbReg;
3110 return gicReDistWriteIntrClearEnableReg(pGicDev, pVCpu, idxReg, uValue);
3111 }
3112 }
3113
3114 /*
3115 * GICR_ISACTIVER0 and GICR_ISACTIVER<n>E.
3116 * GICR_ICACTIVER0 and GICR_ICACTIVER<n>E.
3117 */
3118 {
3119 uint16_t const cbReg = sizeof(uint32_t);
3120 if (offReg - GIC_REDIST_SGI_PPI_REG_ISACTIVER0_OFF < GIC_REDIST_SGI_PPI_REG_ISACTIVERnE_RANGE_SIZE)
3121 {
3122 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ISACTIVER0_OFF) / cbReg;
3123 return gicReDistWriteIntrSetActiveReg(pGicDev, pVCpu, idxReg, uValue);
3124 }
3125 if (offReg - GIC_REDIST_SGI_PPI_REG_ICACTIVER0_OFF < GIC_REDIST_SGI_PPI_REG_ICACTIVERnE_RANGE_SIZE)
3126 {
3127 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ICACTIVER0_OFF) / cbReg;
3128 return gicReDistWriteIntrClearActiveReg(pGicDev, pVCpu, idxReg, uValue);
3129 }
3130 }
3131
3132 /*
3133 * GICR_ISPENDR0 and GICR_ISPENDR<n>E.
3134 * GICR_ICPENDR0 and GICR_ICPENDR<n>E.
3135 */
3136 {
3137 uint16_t const cbReg = sizeof(uint32_t);
3138 if (offReg - GIC_REDIST_SGI_PPI_REG_ISPENDR0_OFF < GIC_REDIST_SGI_PPI_REG_ISPENDRnE_RANGE_SIZE)
3139 {
3140 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ISPENDR0_OFF) / cbReg;
3141 return gicReDistWriteIntrSetPendingReg(pGicDev, pVCpu, idxReg, uValue);
3142 }
3143 if (offReg - GIC_REDIST_SGI_PPI_REG_ICPENDR0_OFF < GIC_REDIST_SGI_PPI_REG_ICPENDRnE_RANGE_SIZE)
3144 {
3145 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ICPENDR0_OFF) / cbReg;
3146 return gicReDistWriteIntrClearPendingReg(pGicDev, pVCpu, idxReg, uValue);
3147 }
3148 }
3149
3150 /*
3151 * GICR_IPRIORITYR<n> and GICR_IPRIORITYR<n>E.
3152 */
3153 {
3154 uint16_t const cbReg = sizeof(uint32_t);
3155 if (offReg - GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START < GIC_REDIST_SGI_PPI_REG_IPRIORITYRnE_RANGE_SIZE)
3156 {
3157 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START) / cbReg;
3158 return gicReDistWriteIntrPriorityReg(pGicDev, pVCpu, idxReg, uValue);
3159 }
3160 }
3161
3162 /*
3163 * GICR_ICFGR0, GIC_ICFGR1 and GICR_ICFGR<n>E.
3164 */
3165 {
3166 uint16_t const cbReg = sizeof(uint32_t);
3167 if (offReg - GIC_REDIST_SGI_PPI_REG_ICFGR0_OFF < GIC_REDIST_SGI_PPI_REG_ICFGRnE_RANGE_SIZE)
3168 {
3169 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ICFGR0_OFF) / cbReg;
3170 return gicReDistWriteIntrConfigReg(pGicDev, pVCpu, idxReg, uValue);
3171 }
3172 }
3173
3174 AssertReleaseFailed();
3175 return VERR_INTERNAL_ERROR_2;
3176#else
3177 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
3178 switch (offReg)
3179 {
3180 case GIC_REDIST_SGI_PPI_REG_IGROUPR0_OFF:
3181 ASMAtomicOrU32(&pThis->u32RegIGrp0, uValue);
3182 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
3183 break;
3184 case GIC_REDIST_SGI_PPI_REG_ISENABLER0_OFF:
3185 ASMAtomicOrU32(&pThis->bmIntEnabled, uValue);
3186 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
3187 break;
3188 case GIC_REDIST_SGI_PPI_REG_ICENABLER0_OFF:
3189 ASMAtomicAndU32(&pThis->bmIntEnabled, ~uValue);
3190 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
3191 break;
3192 case GIC_REDIST_SGI_PPI_REG_ISPENDR0_OFF:
3193 ASMAtomicOrU32(&pThis->bmIntPending, uValue);
3194 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
3195 break;
3196 case GIC_REDIST_SGI_PPI_REG_ICPENDR0_OFF:
3197 ASMAtomicAndU32(&pThis->bmIntPending, ~uValue);
3198 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
3199 break;
3200 case GIC_REDIST_SGI_PPI_REG_ISACTIVER0_OFF:
3201 ASMAtomicOrU32(&pThis->bmIntActive, uValue);
3202 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
3203 break;
3204 case GIC_REDIST_SGI_PPI_REG_ICACTIVER0_OFF:
3205 ASMAtomicAndU32(&pThis->bmIntActive, ~uValue);
3206 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
3207 break;
3208 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START:
3209 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 4:
3210 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 8:
3211 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 12:
3212 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 16:
3213 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 20:
3214 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 24:
3215 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 28:
3216 {
3217 /* Figure out the register which is written. */
3218 uint8_t idxPrio = offReg - GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START;
3219 Assert(idxPrio <= RT_ELEMENTS(pThis->abIntPriority) - sizeof(uint32_t));
3220 for (uint32_t i = idxPrio; i < idxPrio + sizeof(uint32_t); i++)
3221 {
3222 pThis->abIntPriority[i] = (uint8_t)(uValue & 0xff);
3223 uValue >>= 8;
3224 }
3225 break;
3226 }
3227 case GIC_REDIST_SGI_PPI_REG_ICFGR0_OFF:
3228 ASMAtomicWriteU32(&pThis->u32RegICfg0, uValue);
3229 break;
3230 case GIC_REDIST_SGI_PPI_REG_ICFGR1_OFF:
3231 ASMAtomicWriteU32(&pThis->u32RegICfg1, uValue);
3232 break;
3233 default:
3234 //AssertReleaseFailed();
3235 break;
3236 }
3237 return rcStrict;
3238#endif
3239}
3240
3241
3242/**
3243 * @interface_method_impl{PDMGICBACKEND,pfnSetSpi}
3244 */
3245static DECLCALLBACK(int) gicSetSpi(PVMCC pVM, uint32_t uSpiIntId, bool fAsserted)
3246{
3247 LogFlowFunc(("pVM=%p uSpiIntId=%u fAsserted=%RTbool\n",
3248 pVM, uSpiIntId, fAsserted));
3249
3250 PGIC pGic = VM_TO_GIC(pVM);
3251 PPDMDEVINS pDevIns = pGic->CTX_SUFF(pDevIns);
3252 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
3253
3254 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
3255 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
3256
3257#if 0
3258 /* Update the interrupts pending state. */
3259 if (fAsserted)
3260 ASMAtomicOrU32(&pThis->bmIntPending, RT_BIT_32(uIntId));
3261 else
3262 ASMAtomicAndU32(&pThis->bmIntPending, ~RT_BIT_32(uIntId));
3263
3264 int rc = VBOXSTRICTRC_VAL(gicDistUpdateIrqState(pVM, pThis));
3265#else
3266 uint16_t const uIntId = GIC_INTID_RANGE_SPI_START + uSpiIntId;
3267 uint16_t const idxIntr = gicDistGetIndexFromIntId(uIntId);
3268
3269 Assert(idxIntr >= GIC_INTID_RANGE_SPI_START);
3270 AssertMsgReturnStmt(idxIntr < sizeof(pGicDev->bmIntrPending) * 8,
3271 ("out-of-range SPI interrupt ID %RU32 (%RU32)\n", uIntId, uSpiIntId),
3272 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3),
3273 VERR_INVALID_PARAMETER);
3274
3275 /* Update the interrupt pending state. */
3276 if (fAsserted)
3277 ASMBitSet(&pGicDev->bmIntrPending[0], idxIntr);
3278 else
3279 ASMBitClear(&pGicDev->bmIntrPending[0], idxIntr);
3280 int const rc = VBOXSTRICTRC_VAL(gicDistUpdateIrqState(pVM, pGicDev));
3281#endif
3282
3283 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
3284 return rc;
3285}
3286
3287
3288/**
3289 * @interface_method_impl{PDMGICBACKEND,pfnSetPpi}
3290 */
3291static DECLCALLBACK(int) gicSetPpi(PVMCPUCC pVCpu, uint32_t uPpiIntId, bool fAsserted)
3292{
3293 LogFlowFunc(("pVCpu=%p{.idCpu=%u} uPpiIntId=%u fAsserted=%RTbool\n", pVCpu, pVCpu->idCpu, uPpiIntId, fAsserted));
3294
3295 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
3296 PCGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PCGICDEV);
3297 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
3298 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
3299 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
3300
3301#if 0
3302 AssertReturn(uIntId <= (GIC_INTID_RANGE_PPI_LAST - GIC_INTID_RANGE_PPI_START), VERR_INVALID_PARAMETER);
3303 int rc = gicReDistInterruptSet(pVCpu, uIntId + GIC_INTID_RANGE_PPI_START, fAsserted);
3304#else
3305 uint32_t const uIntId = GIC_INTID_RANGE_PPI_START + uPpiIntId;
3306 uint16_t const idxIntr = gicReDistGetIndexFromIntId(uIntId);
3307
3308 Assert(idxIntr >= GIC_INTID_RANGE_PPI_START);
3309 AssertMsgReturnStmt(idxIntr < sizeof(pGicCpu->bmIntrPending) * 8,
3310 ("out-of-range PPI interrupt ID %RU32 (%RU32)\n", uIntId, uPpiIntId),
3311 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3),
3312 VERR_INVALID_PARAMETER);
3313
3314 /* Update the interrupt pending state. */
3315 if (fAsserted)
3316 ASMBitSet(&pGicCpu->bmIntrPending[0], idxIntr);
3317 else
3318 ASMBitClear(&pGicCpu->bmIntrPending[0], idxIntr);
3319 int const rc = VBOXSTRICTRC_VAL(gicReDistUpdateIrqState(pGicDev, pVCpu));
3320#endif
3321
3322 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
3323
3324 return rc;
3325}
3326
3327
3328/**
3329 * Sets the specified software generated interrupt (SGI).
3330 *
3331 * @returns VBox status code.
3332 * @param pVM The cross context VM structure.
3333 * @param pVCpu The cross context virtual CPU structure.
3334 * @param pDestCpuSet Which CPUs to deliver the SGI to.
3335 * @param uIntId The SGI interrupt ID.
3336 */
3337static int gicSetSgi(PCGICDEV pGicDev, PVMCPUCC pVCpu, PCVMCPUSET pDestCpuSet, uint8_t uIntId)
3338{
3339#if 0
3340 LogFlowFunc(("pVCpu=%p{.idCpu=%u} uIntId=%u fAsserted=%RTbool\n",
3341 pVCpu, pVCpu->idCpu, uIntId, fAsserted));
3342
3343 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
3344 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
3345 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
3346
3347 AssertReturn(uIntId <= (GIC_INTID_RANGE_SGI_LAST - GIC_INTID_RANGE_SGI_START), VERR_INVALID_PARAMETER);
3348 int rc = gicReDistInterruptSet(pVCpu, uIntId + GIC_INTID_RANGE_SGI_START, fAsserted);
3349 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
3350#else
3351 LogFlowFunc(("pVCpu=%p{.idCpu=%u} uIntId=%u\n", pVCpu, pVCpu->idCpu, uIntId));
3352
3353 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
3354 PCVMCC pVM = pVCpu->CTX_SUFF(pVM);
3355 uint32_t const cCpus = pVM->cCpus;
3356 AssertReturn(uIntId <= GIC_INTID_RANGE_SGI_LAST, VERR_INVALID_PARAMETER);
3357
3358 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
3359 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
3360
3361 for (VMCPUID idCpu = 0; idCpu < cCpus; idCpu++)
3362 if (VMCPUSET_IS_PRESENT(pDestCpuSet, idCpu))
3363 {
3364 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVM->CTX_SUFF(apCpus)[idCpu]);
3365 pGicCpu->bmIntrPending[0] |= RT_BIT_32(uIntId);
3366 }
3367
3368 int const rc = VBOXSTRICTRC_VAL(gicDistUpdateIrqState(pVM, pGicDev));
3369 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
3370#endif
3371 return rc;
3372}
3373
3374
3375/**
3376 * Writes to the redistributor's SGI group 1 register (ICC_SGI1R_EL1).
3377 *
3378 * @returns Strict VBox status code.
3379 * @param pGicDev The GIC distributor state.
3380 * @param pVCpu The cross context virtual CPU structure.
3381 * @param uValue The value being written to the ICC_SGI1R_EL1 register.
3382 */
3383static VBOXSTRICTRC gicReDistWriteSgiReg(PCGICDEV pGicDev, PVMCPUCC pVCpu, uint64_t uValue)
3384{
3385 PCGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
3386 VMCPUSET DestCpuSet;
3387 if (uValue & ARMV8_ICC_SGI1R_EL1_AARCH64_IRM)
3388 {
3389 /*
3390 * Deliver to all VCPUs but this one.
3391 */
3392 VMCPUSET_FILL(&DestCpuSet);
3393 VMCPUSET_DEL(&DestCpuSet, pVCpu->idCpu);
3394 }
3395 else
3396 {
3397 /*
3398 * Target specific VCPUs.
3399 * See ARM GICv3 and GICv4 Software Overview spec 3.3 "Affinity routing".
3400 */
3401 VMCPUSET_EMPTY(&DestCpuSet);
3402 bool const fRangeSelSupport = RT_BOOL(pGicCpu->uIccCtlr & ARMV8_ICC_CTLR_EL1_AARCH64_RSS);
3403 uint8_t const idRangeStart = ARMV8_ICC_SGI1R_EL1_AARCH64_RS_GET(uValue) * 16;
3404 uint16_t const bmCpuInterfaces = ARMV8_ICC_SGI1R_EL1_AARCH64_TARGET_LIST_GET(uValue);
3405 uint8_t const uAff1 = ARMV8_ICC_SGI1R_EL1_AARCH64_AFF1_GET(uValue);
3406 uint8_t const uAff2 = ARMV8_ICC_SGI1R_EL1_AARCH64_AFF2_GET(uValue);
3407 uint8_t const uAff3 = ARMV8_ICC_SGI1R_EL1_AARCH64_AFF3_GET(uValue);
3408 uint32_t const cCpus = pVCpu->CTX_SUFF(pVM)->cCpus;
3409 for (uint8_t idCpuInterface = 0; idCpuInterface < 16; idCpuInterface++)
3410 {
3411 if (bmCpuInterfaces & RT_BIT(idCpuInterface))
3412 {
3413 VMCPUID idCpuTarget;
3414 if (fRangeSelSupport)
3415 idCpuTarget = RT_MAKE_U32_FROM_U8(idRangeStart + idCpuInterface, uAff1, uAff2, uAff3);
3416 else
3417 idCpuTarget = gicGetCpuIdFromAffinity(idCpuInterface, uAff1, uAff2, uAff3);
3418 if (RT_LIKELY(idCpuTarget < cCpus))
3419 VMCPUSET_ADD(&DestCpuSet, idCpuTarget);
3420 else
3421 AssertReleaseFailed();
3422 }
3423 }
3424 }
3425
3426 if (!VMCPUSET_IS_EMPTY(&DestCpuSet))
3427 {
3428 uint8_t const uSgiIntId = ARMV8_ICC_SGI1R_EL1_AARCH64_INTID_GET(uValue);
3429 Assert(GIC_IS_INTR_SGI(uSgiIntId));
3430#if 0
3431 uint16_t const idxIdb = uSgiIntId;
3432 gicPostInterrupt(pVCpu, &DestCpuSet, idxIdb);
3433#else
3434 gicSetSgi(pGicDev, pVCpu, &DestCpuSet, uSgiIntId);
3435#endif
3436 }
3437 return VINF_SUCCESS;
3438}
3439
3440
3441/**
3442 * @interface_method_impl{PDMGICBACKEND,pfnReadSysReg}
3443 */
3444static DECLCALLBACK(VBOXSTRICTRC) gicReadSysReg(PVMCPUCC pVCpu, uint32_t u32Reg, uint64_t *pu64Value)
3445{
3446 /*
3447 * Validate.
3448 */
3449 VMCPU_ASSERT_EMT(pVCpu);
3450 Assert(pu64Value);
3451
3452 *pu64Value = 0;
3453 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
3454 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
3455 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
3456
3457 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
3458 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
3459
3460 switch (u32Reg)
3461 {
3462 case ARMV8_AARCH64_SYSREG_ICC_PMR_EL1:
3463 *pu64Value = pGicCpu->bInterruptPriority;
3464 break;
3465 case ARMV8_AARCH64_SYSREG_ICC_IAR0_EL1:
3466 AssertReleaseFailed();
3467 break;
3468 case ARMV8_AARCH64_SYSREG_ICC_EOIR0_EL1:
3469 AssertReleaseFailed();
3470 break;
3471 case ARMV8_AARCH64_SYSREG_ICC_HPPIR0_EL1:
3472 AssertReleaseFailed();
3473 break;
3474 case ARMV8_AARCH64_SYSREG_ICC_BPR0_EL1:
3475 *pu64Value = ARMV8_ICC_BPR0_EL1_AARCH64_BINARYPOINT_SET(pGicCpu->bBinaryPointGrp0);
3476 break;
3477 case ARMV8_AARCH64_SYSREG_ICC_AP0R0_EL1:
3478 AssertReleaseFailed();
3479 break;
3480 case ARMV8_AARCH64_SYSREG_ICC_AP0R1_EL1:
3481 AssertReleaseFailed();
3482 break;
3483 case ARMV8_AARCH64_SYSREG_ICC_AP0R2_EL1:
3484 AssertReleaseFailed();
3485 break;
3486 case ARMV8_AARCH64_SYSREG_ICC_AP0R3_EL1:
3487 AssertReleaseFailed();
3488 break;
3489 case ARMV8_AARCH64_SYSREG_ICC_AP1R0_EL1:
3490 AssertReleaseFailed();
3491 break;
3492 case ARMV8_AARCH64_SYSREG_ICC_AP1R1_EL1:
3493 AssertReleaseFailed();
3494 break;
3495 case ARMV8_AARCH64_SYSREG_ICC_AP1R2_EL1:
3496 AssertReleaseFailed();
3497 break;
3498 case ARMV8_AARCH64_SYSREG_ICC_AP1R3_EL1:
3499 AssertReleaseFailed();
3500 break;
3501 case ARMV8_AARCH64_SYSREG_ICC_NMIAR1_EL1:
3502 AssertReleaseFailed();
3503 break;
3504 case ARMV8_AARCH64_SYSREG_ICC_DIR_EL1:
3505 AssertReleaseFailed();
3506 break;
3507 case ARMV8_AARCH64_SYSREG_ICC_RPR_EL1:
3508 *pu64Value = pGicCpu->abRunningPriorities[pGicCpu->idxRunningPriority];
3509 break;
3510 case ARMV8_AARCH64_SYSREG_ICC_SGI1R_EL1:
3511 AssertReleaseFailed();
3512 break;
3513 case ARMV8_AARCH64_SYSREG_ICC_ASGI1R_EL1:
3514 AssertReleaseFailed();
3515 break;
3516 case ARMV8_AARCH64_SYSREG_ICC_SGI0R_EL1:
3517 AssertReleaseFailed();
3518 break;
3519 case ARMV8_AARCH64_SYSREG_ICC_IAR1_EL1:
3520 {
3521#if 0
3522 /** @todo Figure out the highest priority interrupt. */
3523 uint32_t bmIntActive = ASMAtomicReadU32(&pThis->bmIntActive);
3524 uint32_t bmIntEnabled = ASMAtomicReadU32(&pThis->bmIntEnabled);
3525 uint32_t bmPending = (ASMAtomicReadU32(&pThis->bmIntPending) & bmIntEnabled) & ~bmIntActive;
3526 int32_t idxIntPending = ASMBitFirstSet(&bmPending, sizeof(bmPending) * 8);
3527 if (idxIntPending > -1)
3528 {
3529 /* Mark the interrupt as active. */
3530 ASMAtomicOrU32(&pThis->bmIntActive, RT_BIT_32(idxIntPending));
3531 /* Drop priority. */
3532 Assert((uint32_t)idxIntPending < RT_ELEMENTS(pThis->abIntPriority));
3533 Assert(pThis->idxRunningPriority < RT_ELEMENTS(pThis->abRunningPriorities) - 1);
3534
3535 LogFlowFunc(("Dropping interrupt priority from %u -> %u (idxRunningPriority: %u -> %u)\n",
3536 pThis->abRunningPriorities[pThis->idxRunningPriority],
3537 pThis->abIntPriority[idxIntPending],
3538 pThis->idxRunningPriority, pThis->idxRunningPriority + 1));
3539
3540 pThis->abRunningPriorities[++pThis->idxRunningPriority] = pThis->abIntPriority[idxIntPending];
3541
3542 /* Clear edge level interrupts like SGIs as pending. */
3543 if (idxIntPending <= GIC_INTID_RANGE_SGI_LAST)
3544 ASMAtomicBitClear(&pThis->bmIntPending, idxIntPending);
3545 *pu64Value = idxIntPending;
3546 gicReDistUpdateIrqState(pThis, pVCpu);
3547 }
3548 else
3549 {
3550 /** @todo This is wrong as the guest might decide to prioritize PPIs and SPIs differently. */
3551 bmIntActive = ASMAtomicReadU32(&pGicDev->bmIntActive);
3552 bmIntEnabled = ASMAtomicReadU32(&pGicDev->bmIntEnabled);
3553 bmPending = (ASMAtomicReadU32(&pGicDev->bmIntPending) & bmIntEnabled) & ~bmIntActive;
3554 idxIntPending = ASMBitFirstSet(&bmPending, sizeof(bmPending) * 8);
3555 if ( idxIntPending > -1
3556 && pGicDev->abIntPriority[idxIntPending] < pThis->bInterruptPriority)
3557 {
3558 /* Mark the interrupt as active. */
3559 ASMAtomicOrU32(&pGicDev->bmIntActive, RT_BIT_32(idxIntPending));
3560
3561 /* Drop priority. */
3562 Assert((uint32_t)idxIntPending < RT_ELEMENTS(pGicDev->abIntPriority));
3563 Assert(pThis->idxRunningPriority < RT_ELEMENTS(pThis->abRunningPriorities) - 1);
3564
3565 LogFlowFunc(("Dropping interrupt priority from %u -> %u (idxRunningPriority: %u -> %u)\n",
3566 pThis->abRunningPriorities[pThis->idxRunningPriority],
3567 pThis->abIntPriority[idxIntPending],
3568 pThis->idxRunningPriority, pThis->idxRunningPriority + 1));
3569
3570 pThis->abRunningPriorities[++pThis->idxRunningPriority] = pGicDev->abIntPriority[idxIntPending];
3571
3572 *pu64Value = idxIntPending + GIC_INTID_RANGE_SPI_START;
3573 gicReDistUpdateIrqState(pThis, pVCpu);
3574 }
3575 else
3576 *pu64Value = GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT;
3577 }
3578#else
3579 *pu64Value = gicAckHighestPrioPendingIntr(pGicDev, pVCpu, false /*fGroup0*/, true /*fGroup1*/);
3580#endif
3581 break;
3582 }
3583 case ARMV8_AARCH64_SYSREG_ICC_EOIR1_EL1:
3584 AssertReleaseFailed();
3585 break;
3586 case ARMV8_AARCH64_SYSREG_ICC_HPPIR1_EL1:
3587 {
3588#if 0
3589 /** @todo Figure out the highest priority interrupt. */
3590 uint32_t bmIntActive = ASMAtomicReadU32(&pThis->bmIntActive);
3591 uint32_t bmIntEnabled = ASMAtomicReadU32(&pThis->bmIntEnabled);
3592 uint32_t bmPending = (ASMAtomicReadU32(&pThis->bmIntPending) & bmIntEnabled) & ~bmIntActive;
3593 int32_t idxIntPending = ASMBitFirstSet(&bmPending, sizeof(bmPending) * 8);
3594 if (idxIntPending > -1)
3595 *pu64Value = idxIntPending;
3596 else
3597 {
3598 /** @todo This is wrong as the guest might decide to prioritize PPIs and SPIs differently. */
3599 bmIntActive = ASMAtomicReadU32(&pGicDev->bmIntActive);
3600 bmIntEnabled = ASMAtomicReadU32(&pGicDev->bmIntEnabled);
3601 bmPending = (ASMAtomicReadU32(&pGicDev->bmIntPending) & bmIntEnabled) & ~bmIntActive;
3602 idxIntPending = ASMBitFirstSet(&bmPending, sizeof(bmPending) * 8);
3603 if (idxIntPending > -1)
3604 *pu64Value = idxIntPending + GIC_INTID_RANGE_SPI_START;
3605 else
3606 *pu64Value = GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT;
3607 }
3608#else
3609 AssertReleaseFailed();
3610 *pu64Value = gicGetHighestPrioPendingIntr(pVCpu->pVMR3, pGicDev, false /*fGroup0*/, true /*fGroup1*/);
3611#endif
3612 break;
3613 }
3614 case ARMV8_AARCH64_SYSREG_ICC_BPR1_EL1:
3615 *pu64Value = ARMV8_ICC_BPR1_EL1_AARCH64_BINARYPOINT_SET(pGicCpu->bBinaryPointGrp1);
3616 break;
3617 case ARMV8_AARCH64_SYSREG_ICC_CTLR_EL1:
3618 *pu64Value = pGicCpu->uIccCtlr;
3619 break;
3620 case ARMV8_AARCH64_SYSREG_ICC_SRE_EL1:
3621 AssertReleaseFailed();
3622 break;
3623 case ARMV8_AARCH64_SYSREG_ICC_IGRPEN0_EL1:
3624 *pu64Value = pGicCpu->fIrqGrp0Enabled ? ARMV8_ICC_IGRPEN0_EL1_AARCH64_ENABLE : 0;
3625 break;
3626 case ARMV8_AARCH64_SYSREG_ICC_IGRPEN1_EL1:
3627 *pu64Value = pGicCpu->fIrqGrp1Enabled ? ARMV8_ICC_IGRPEN1_EL1_AARCH64_ENABLE : 0;
3628 break;
3629 default:
3630 AssertReleaseFailed();
3631 break;
3632 }
3633
3634 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
3635
3636 LogFlowFunc(("pVCpu=%p u32Reg=%#x{%s} pu64Value=%RX64\n", pVCpu, u32Reg, gicIccGetRegDescription(u32Reg), *pu64Value));
3637 return VINF_SUCCESS;
3638}
3639
3640
3641/**
3642 * @interface_method_impl{PDMGICBACKEND,pfnWriteSysReg}
3643 */
3644static DECLCALLBACK(VBOXSTRICTRC) gicWriteSysReg(PVMCPUCC pVCpu, uint32_t u32Reg, uint64_t u64Value)
3645{
3646 /*
3647 * Validate.
3648 */
3649 VMCPU_ASSERT_EMT(pVCpu);
3650 LogFlowFunc(("pVCpu=%p u32Reg=%#x{%s} u64Value=%RX64\n", pVCpu, u32Reg, gicIccGetRegDescription(u32Reg), u64Value));
3651
3652 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
3653 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
3654 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
3655
3656 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
3657 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
3658
3659 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
3660 switch (u32Reg)
3661 {
3662 case ARMV8_AARCH64_SYSREG_ICC_PMR_EL1:
3663 LogFlowFunc(("ICC_PMR_EL1: Interrupt priority now %u\n", (uint8_t)u64Value));
3664#if 0
3665 ASMAtomicWriteU8(&pThis->bInterruptPriority, (uint8_t)u64Value);
3666 gicReDistUpdateIrqState(pThis, pVCpu);
3667#else
3668 pGicCpu->bInterruptPriority = (uint8_t)u64Value;
3669 rcStrict = gicReDistUpdateIrqState(pGicDev, pVCpu);
3670#endif
3671 break;
3672 case ARMV8_AARCH64_SYSREG_ICC_IAR0_EL1:
3673 AssertReleaseFailed();
3674 break;
3675 case ARMV8_AARCH64_SYSREG_ICC_EOIR0_EL1:
3676 AssertReleaseFailed();
3677 break;
3678 case ARMV8_AARCH64_SYSREG_ICC_HPPIR0_EL1:
3679 AssertReleaseFailed();
3680 break;
3681 case ARMV8_AARCH64_SYSREG_ICC_BPR0_EL1:
3682#if 0
3683 pThis->bBinaryPointGrp0 = (uint8_t)ARMV8_ICC_BPR0_EL1_AARCH64_BINARYPOINT_GET(u64Value);
3684#else
3685 pGicCpu->bBinaryPointGrp0 = (uint8_t)ARMV8_ICC_BPR0_EL1_AARCH64_BINARYPOINT_GET(u64Value);
3686#endif
3687 break;
3688 case ARMV8_AARCH64_SYSREG_ICC_AP0R0_EL1:
3689 /** @todo */
3690 break;
3691 case ARMV8_AARCH64_SYSREG_ICC_AP0R1_EL1:
3692 AssertReleaseFailed();
3693 break;
3694 case ARMV8_AARCH64_SYSREG_ICC_AP0R2_EL1:
3695 AssertReleaseFailed();
3696 break;
3697 case ARMV8_AARCH64_SYSREG_ICC_AP0R3_EL1:
3698 AssertReleaseFailed();
3699 break;
3700 case ARMV8_AARCH64_SYSREG_ICC_AP1R0_EL1:
3701 /** @todo */
3702 break;
3703 case ARMV8_AARCH64_SYSREG_ICC_AP1R1_EL1:
3704 AssertReleaseFailed();
3705 break;
3706 case ARMV8_AARCH64_SYSREG_ICC_AP1R2_EL1:
3707 AssertReleaseFailed();
3708 break;
3709 case ARMV8_AARCH64_SYSREG_ICC_AP1R3_EL1:
3710 AssertReleaseFailed();
3711 break;
3712 case ARMV8_AARCH64_SYSREG_ICC_NMIAR1_EL1:
3713 AssertReleaseFailed();
3714 break;
3715 case ARMV8_AARCH64_SYSREG_ICC_DIR_EL1:
3716 AssertReleaseFailed();
3717 break;
3718 case ARMV8_AARCH64_SYSREG_ICC_RPR_EL1:
3719 AssertReleaseFailed();
3720 break;
3721 case ARMV8_AARCH64_SYSREG_ICC_SGI1R_EL1:
3722 {
3723#if 0
3724 uint32_t uIntId = ARMV8_ICC_SGI1R_EL1_AARCH64_INTID_GET(u64Value) - GIC_INTID_RANGE_SGI_START;
3725 if (u64Value & ARMV8_ICC_SGI1R_EL1_AARCH64_IRM)
3726 {
3727 /* Route to all but this vCPU. */
3728 for (uint32_t i = 0; i < pVCpu->pVMR3->cCpus; i++)
3729 {
3730 if (i != pVCpu->idCpu)
3731 {
3732 PVMCPUCC pVCpuDst = VMMGetCpuById(pVCpu->CTX_SUFF(pVM), i);
3733 if (pVCpuDst)
3734 gicSetSgi(pVCpuDst, uIntId, true /*fAsserted*/);
3735 else
3736 AssertFailed();
3737 }
3738 }
3739 }
3740 else
3741 {
3742 /* Examine target list. */
3743 /** @todo Range selector support. */
3744 VMCPUID idCpu = 0;
3745 uint16_t uTgtList = ARMV8_ICC_SGI1R_EL1_AARCH64_TARGET_LIST_GET(u64Value);
3746 /** @todo rewrite using ASMBitFirstSetU16. */
3747 while (uTgtList)
3748 {
3749 if (uTgtList & 0x1)
3750 {
3751 PVMCPUCC pVCpuDst = VMMGetCpuById(pVCpu->CTX_SUFF(pVM), idCpu);
3752 if (pVCpuDst)
3753 gicSetSgi(pVCpuDst, uIntId, true /*fAsserted*/);
3754 else
3755 AssertFailed();
3756 }
3757 uTgtList >>= 1;
3758 idCpu++;
3759 }
3760 }
3761#else
3762 gicReDistWriteSgiReg(pGicDev, pVCpu, u64Value);
3763#endif
3764 break;
3765 }
3766 case ARMV8_AARCH64_SYSREG_ICC_ASGI1R_EL1:
3767 AssertReleaseFailed();
3768 break;
3769 case ARMV8_AARCH64_SYSREG_ICC_SGI0R_EL1:
3770 AssertReleaseFailed();
3771 break;
3772 case ARMV8_AARCH64_SYSREG_ICC_IAR1_EL1:
3773 AssertReleaseFailed();
3774 break;
3775 case ARMV8_AARCH64_SYSREG_ICC_EOIR1_EL1:
3776 {
3777#if 0
3778 /* Mark the interrupt as not active anymore, though it might still be pending. */
3779 if (u64Value < GIC_INTID_RANGE_SPI_START)
3780 ASMAtomicAndU32(&pThis->bmIntActive, ~RT_BIT_32((uint32_t)u64Value));
3781 else
3782 ASMAtomicAndU32(&pGicDev->bmIntActive, ~RT_BIT_32((uint32_t)(u64Value - GIC_INTID_RANGE_SPI_START)));
3783
3784 /* Restore previous interrupt priority. */
3785 Assert(pThis->idxRunningPriority > 0);
3786 if (RT_LIKELY(pThis->idxRunningPriority))
3787 {
3788 LogFlowFunc(("Restoring interrupt priority from %u -> %u (idxRunningPriority: %u -> %u)\n",
3789 pThis->abRunningPriorities[pThis->idxRunningPriority],
3790 pThis->abRunningPriorities[pThis->idxRunningPriority - 1],
3791 pThis->idxRunningPriority, pThis->idxRunningPriority - 1));
3792 pThis->idxRunningPriority--;
3793 }
3794 gicReDistUpdateIrqState(pThis, pVCpu);
3795#else
3796 /*
3797 * We only support priority drop + interrupt deactivation with writes to this register.
3798 * This avoids an extra access which would be required by software for deactivation.
3799 */
3800 Assert(!(pGicCpu->uIccCtlr & ARMV8_ICC_CTLR_EL1_AARCH64_EOIMODE));
3801
3802 /*
3803 * Mark the interrupt as inactive, though it might still be pending.
3804 * WARNING! The order of the 'if' checks below are crucial.
3805 */
3806 uint16_t const uIntId = (uint16_t)u64Value;
3807 if (uIntId <= GIC_INTID_RANGE_PPI_LAST)
3808 {
3809 /* SGIs and PPIs. */
3810 AssertCompile(GIC_INTID_RANGE_PPI_LAST < 8 * sizeof(pGicDev->bmIntrActive[0]));
3811 Assert(pGicDev->fAffRoutingEnabled);
3812 pGicCpu->bmIntrActive[0] &= ~RT_BIT_32(uIntId);
3813 }
3814 else if (uIntId <= GIC_INTID_RANGE_SPI_LAST)
3815 {
3816 /* SPIs. */
3817 uint16_t const idxIntr = /*gicDistGetIndexFromIntId*/(uIntId);
3818 AssertReturn(idxIntr < sizeof(pGicDev->bmIntrActive) * 8, VERR_BUFFER_OVERFLOW);
3819 ASMBitClear(&pGicDev->bmIntrActive[0], idxIntr);
3820 }
3821 else if (uIntId <= GIC_INTID_RANGE_EXT_PPI_LAST)
3822 {
3823 /* Extended PPIs. */
3824 uint16_t const idxIntr = gicReDistGetIndexFromIntId(uIntId);
3825 AssertReturn(idxIntr < sizeof(pGicCpu->bmIntrActive) * 8, VERR_BUFFER_OVERFLOW);
3826 ASMBitClear(&pGicCpu->bmIntrActive[0], idxIntr);
3827 }
3828 else if (uIntId <= GIC_INTID_RANGE_EXT_SPI_LAST)
3829 {
3830 /* Extended SPIs. */
3831 uint16_t const idxIntr = gicDistGetIndexFromIntId(uIntId);
3832 AssertReturn(idxIntr < sizeof(pGicDev->bmIntrActive) * 8, VERR_BUFFER_OVERFLOW);
3833 ASMBitClear(&pGicDev->bmIntrActive[0], idxIntr);
3834 }
3835 else
3836 {
3837 AssertMsgFailed(("Invalid INTID %u\n", uIntId));
3838 break;
3839 }
3840
3841 /*
3842 * Restore previous interrupt priority.
3843 */
3844 Assert(pGicCpu->idxRunningPriority > 0);
3845 if (RT_LIKELY(pGicCpu->idxRunningPriority))
3846 {
3847 LogFlowFunc(("Restoring interrupt priority from %u -> %u (idxRunningPriority: %u -> %u)\n",
3848 pGicCpu->abRunningPriorities[pGicCpu->idxRunningPriority],
3849 pGicCpu->abRunningPriorities[pGicCpu->idxRunningPriority - 1],
3850 pGicCpu->idxRunningPriority, pGicCpu->idxRunningPriority - 1));
3851 pGicCpu->idxRunningPriority--;
3852 }
3853 rcStrict = gicReDistUpdateIrqState(pGicDev, pVCpu);
3854#endif
3855 break;
3856 }
3857 case ARMV8_AARCH64_SYSREG_ICC_HPPIR1_EL1:
3858 AssertReleaseFailed();
3859 break;
3860 case ARMV8_AARCH64_SYSREG_ICC_BPR1_EL1:
3861 pGicCpu->bBinaryPointGrp1 = (uint8_t)ARMV8_ICC_BPR1_EL1_AARCH64_BINARYPOINT_GET(u64Value);
3862 break;
3863 case ARMV8_AARCH64_SYSREG_ICC_CTLR_EL1:
3864 pGicCpu->uIccCtlr &= ARMV8_ICC_CTLR_EL1_RW;
3865 /** @todo */
3866 break;
3867 case ARMV8_AARCH64_SYSREG_ICC_SRE_EL1:
3868 AssertReleaseFailed();
3869 break;
3870 case ARMV8_AARCH64_SYSREG_ICC_IGRPEN0_EL1:
3871 pGicCpu->fIrqGrp0Enabled = RT_BOOL(u64Value & ARMV8_ICC_IGRPEN0_EL1_AARCH64_ENABLE);
3872 break;
3873 case ARMV8_AARCH64_SYSREG_ICC_IGRPEN1_EL1:
3874 pGicCpu->fIrqGrp1Enabled = RT_BOOL(u64Value & ARMV8_ICC_IGRPEN1_EL1_AARCH64_ENABLE);
3875 break;
3876 default:
3877 AssertReleaseFailed();
3878 break;
3879 }
3880
3881 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
3882 return rcStrict;
3883}
3884
3885
3886/**
3887 * Initializes the GIC distributor state.
3888 *
3889 * @param pDevIns The device instance.
3890 */
3891DECLHIDDEN(void) gicInit(PPDMDEVINS pDevIns)
3892{
3893 LogFlowFunc(("\n"));
3894 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
3895 RT_ZERO(pGicDev->bmIntrGroup);
3896 RT_ZERO(pGicDev->bmIntrConfig);
3897 RT_ZERO(pGicDev->bmIntrEnabled);
3898 RT_ZERO(pGicDev->bmIntrPending);
3899 RT_ZERO(pGicDev->bmIntrActive);
3900 RT_ZERO(pGicDev->abIntrPriority);
3901 RT_ZERO(pGicDev->au32IntrRouting);
3902 RT_ZERO(pGicDev->bmIntrRoutingMode);
3903 pGicDev->fIrqGrp0Enabled = false;
3904 pGicDev->fIrqGrp1Enabled = false;
3905 pGicDev->fAffRoutingEnabled = true; /* GICv2 backwards compatibility is not implemented, so this is RA1/WI. */
3906}
3907
3908
3909/**
3910 * Initialies the GIC redistributor and CPU interface state.
3911 *
3912 * @param pDevIns The device instance.
3913 * @param pVCpu The cross context virtual CPU structure.
3914 */
3915DECLHIDDEN(void) gicInitCpu(PPDMDEVINS pDevIns, PVMCPUCC pVCpu)
3916{
3917 LogFlowFunc(("[%u]\n", pVCpu->idCpu));
3918 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
3919 PGICCPU pGicCpu = &pVCpu->gic.s;
3920 RT_ZERO(pGicCpu->bmIntrGroup);
3921 RT_ZERO(pGicCpu->bmIntrConfig);
3922 RT_ZERO(pGicCpu->bmIntrEnabled);
3923 RT_ZERO(pGicCpu->bmIntrPending);
3924 RT_ZERO(pGicCpu->bmIntrActive);
3925 RT_ZERO(pGicCpu->abIntrPriority);
3926 pGicCpu->fRegWritePending = false;
3927
3928 /* SGIs are always edge-triggered, writes to GICR_ICFGR0 are to be ignored. */
3929 pGicCpu->bmIntrConfig[0] = 0xaaaaaaaa;
3930
3931 pGicCpu->uIccCtlr = ARMV8_ICC_CTLR_EL1_AARCH64_PMHE
3932 | ARMV8_ICC_CTLR_EL1_AARCH64_PRIBITS_SET(4)
3933 | ARMV8_ICC_CTLR_EL1_AARCH64_IDBITS_SET(ARMV8_ICC_CTLR_EL1_AARCH64_IDBITS_16BITS)
3934 | (pGicDev->fRangeSelSupport ? ARMV8_ICC_CTLR_EL1_AARCH64_RSS : 0);
3935
3936 memset((void *)&pGicCpu->abRunningPriorities[0], 0xff, sizeof(pGicCpu->abRunningPriorities));
3937 pGicCpu->idxRunningPriority = 0;
3938 pGicCpu->bInterruptPriority = 0; /* Means no interrupt gets through to the PE. */
3939 pGicCpu->fIrqGrp0Enabled = false;
3940 pGicCpu->fIrqGrp1Enabled = false;
3941
3942 /* The binary point register are undefined on reset, initialized
3943 with arbitrarily chosen values in our implementation. */
3944 pGicCpu->bBinaryPointGrp0 = 3;
3945 pGicCpu->bBinaryPointGrp1 = 3;
3946}
3947
3948
3949/**
3950 * Initializes per-VM GIC to the state following a power-up or hardware
3951 * reset.
3952 *
3953 * @param pVM The cross context VM structure.
3954 */
3955DECLHIDDEN(void) gicReset(PPDMDEVINS pDevIns)
3956{
3957 LogFlowFunc(("\n"));
3958 gicInit(pDevIns);
3959}
3960
3961
3962/**
3963 * Initializes per-VCPU GIC to the state following a power-up or hardware
3964 * reset.
3965 *
3966 * @param pVCpu The cross context virtual CPU structure.
3967 */
3968DECLHIDDEN(void) gicResetCpu(PPDMDEVINS pDevIns, PVMCPUCC pVCpu)
3969{
3970 LogFlowFunc(("[%u]\n", pVCpu->idCpu));
3971 VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu);
3972 gicInitCpu(pDevIns, pVCpu);
3973}
3974
3975
3976/**
3977 * @callback_method_impl{FNIOMMMIONEWREAD}
3978 */
3979DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicDistMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
3980{
3981 NOREF(pvUser);
3982 Assert(!(off & 0x3));
3983 Assert(cb == 4); RT_NOREF_PV(cb);
3984
3985 PVMCPUCC pVCpu = PDMDevHlpGetVMCPU(pDevIns);
3986 uint16_t offReg = off & 0xfffc;
3987 uint32_t uValue = 0;
3988
3989 STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatMmioRead));
3990
3991 VBOXSTRICTRC rc = VBOXSTRICTRC_VAL(gicDistReadRegister(pDevIns, pVCpu, offReg, &uValue));
3992 *(uint32_t *)pv = uValue;
3993
3994 LogFlowFunc(("[%u]: offReg=%#RX16 (%s) uValue=%#RX32\n", pVCpu->idCpu, offReg, gicDistGetRegDescription(offReg), uValue));
3995 return rc;
3996}
3997
3998
3999/**
4000 * @callback_method_impl{FNIOMMMIONEWWRITE}
4001 */
4002DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicDistMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
4003{
4004 NOREF(pvUser);
4005 Assert(!(off & 0x3));
4006 Assert(cb == 4); RT_NOREF_PV(cb);
4007
4008 PVMCPUCC pVCpu = PDMDevHlpGetVMCPU(pDevIns);
4009 uint16_t offReg = off & 0xfffc;
4010 uint32_t uValue = *(uint32_t *)pv;
4011
4012 STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatMmioWrite));
4013 LogFlowFunc(("[%u]: offReg=%#RX16 (%s) uValue=%#RX32\n", pVCpu->idCpu, offReg, gicDistGetRegDescription(offReg), uValue));
4014
4015 return gicDistWriteRegister(pDevIns, pVCpu, offReg, uValue);
4016}
4017
4018
4019/**
4020 * @callback_method_impl{FNIOMMMIONEWREAD}
4021 */
4022DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicReDistMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
4023{
4024 NOREF(pvUser);
4025 Assert(!(off & 0x3));
4026 Assert(cb == 4); RT_NOREF_PV(cb);
4027
4028 /*
4029 * Determine the redistributor being targeted. Each redistributor takes
4030 * GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE bytes
4031 * and the redistributors are adjacent.
4032 */
4033 uint32_t const idReDist = off / (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
4034 off %= (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
4035
4036 PVMCC pVM = PDMDevHlpGetVM(pDevIns);
4037 Assert(idReDist < pVM->cCpus);
4038 PVMCPUCC pVCpu = pVM->CTX_SUFF(apCpus)[idReDist];
4039
4040 STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatMmioRead));
4041
4042 /* Redistributor or SGI/PPI frame? */
4043 uint16_t const offReg = off & 0xfffc;
4044 uint32_t uValue = 0;
4045 VBOXSTRICTRC rcStrict;
4046 if (off < GIC_REDIST_REG_FRAME_SIZE)
4047 rcStrict = gicReDistReadRegister(pDevIns, pVCpu, idReDist, offReg, &uValue);
4048 else
4049 rcStrict = gicReDistReadSgiPpiRegister(pDevIns, pVCpu, offReg, &uValue);
4050
4051 *(uint32_t *)pv = uValue;
4052 LogFlowFunc(("[%u]: off=%RGp idReDist=%u offReg=%#RX16 (%s) uValue=%#RX32 -> %Rrc\n", pVCpu->idCpu, off, idReDist, offReg,
4053 gicReDistGetRegDescription(offReg), uValue, VBOXSTRICTRC_VAL(rcStrict)));
4054 return rcStrict;
4055}
4056
4057
4058/**
4059 * @callback_method_impl{FNIOMMMIONEWWRITE}
4060 */
4061DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicReDistMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
4062{
4063 NOREF(pvUser);
4064 Assert(!(off & 0x3));
4065 Assert(cb == 4); RT_NOREF_PV(cb);
4066
4067 uint32_t uValue = *(uint32_t *)pv;
4068
4069 /*
4070 * Determine the redistributor being targeted. Each redistributor takes
4071 * GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE bytes
4072 * and the redistributors are adjacent.
4073 */
4074 uint32_t const idReDist = off / (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
4075 off %= (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
4076
4077 PCVMCC pVM = PDMDevHlpGetVM(pDevIns);
4078 Assert(idReDist < pVM->cCpus);
4079 PVMCPUCC pVCpu = pVM->CTX_SUFF(apCpus)[idReDist];
4080
4081 STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatMmioWrite));
4082
4083 /* Redistributor or SGI/PPI frame? */
4084 uint16_t const offReg = off & 0xfffc;
4085 VBOXSTRICTRC rcStrict;
4086 if (off < GIC_REDIST_REG_FRAME_SIZE)
4087 rcStrict = gicReDistWriteRegister(pDevIns, pVCpu, offReg, uValue);
4088 else
4089 rcStrict = gicReDistWriteSgiPpiRegister(pDevIns, pVCpu, offReg, uValue);
4090
4091 LogFlowFunc(("[%u]: off=%RGp idReDist=%u offReg=%#RX16 (%s) uValue=%#RX32 -> %Rrc\n", pVCpu->idCpu, off, idReDist, offReg,
4092 gicReDistGetRegDescription(offReg), uValue, VBOXSTRICTRC_VAL(rcStrict)));
4093 return rcStrict;
4094}
4095
4096
4097#ifndef IN_RING3
4098/**
4099 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
4100 */
4101static DECLCALLBACK(int) gicRZConstruct(PPDMDEVINS pDevIns)
4102{
4103 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
4104 AssertReleaseFailed();
4105 return VINF_SUCCESS;
4106}
4107#endif /* !IN_RING3 */
4108
4109
4110/**
4111 * GIC device registration structure.
4112 */
4113const PDMDEVREG g_DeviceGIC =
4114{
4115 /* .u32Version = */ PDM_DEVREG_VERSION,
4116 /* .uReserved0 = */ 0,
4117 /* .szName = */ "gic",
4118 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
4119 /* .fClass = */ PDM_DEVREG_CLASS_PIC,
4120 /* .cMaxInstances = */ 1,
4121 /* .uSharedVersion = */ 42,
4122 /* .cbInstanceShared = */ sizeof(GICDEV),
4123 /* .cbInstanceCC = */ 0,
4124 /* .cbInstanceRC = */ 0,
4125 /* .cMaxPciDevices = */ 0,
4126 /* .cMaxMsixVectors = */ 0,
4127 /* .pszDescription = */ "Generic Interrupt Controller",
4128#if defined(IN_RING3)
4129 /* .szRCMod = */ "VMMRC.rc",
4130 /* .szR0Mod = */ "VMMR0.r0",
4131 /* .pfnConstruct = */ gicR3Construct,
4132 /* .pfnDestruct = */ gicR3Destruct,
4133 /* .pfnRelocate = */ gicR3Relocate,
4134 /* .pfnMemSetup = */ NULL,
4135 /* .pfnPowerOn = */ NULL,
4136 /* .pfnReset = */ gicR3Reset,
4137 /* .pfnSuspend = */ NULL,
4138 /* .pfnResume = */ NULL,
4139 /* .pfnAttach = */ NULL,
4140 /* .pfnDetach = */ NULL,
4141 /* .pfnQueryInterface = */ NULL,
4142 /* .pfnInitComplete = */ NULL,
4143 /* .pfnPowerOff = */ NULL,
4144 /* .pfnSoftReset = */ NULL,
4145 /* .pfnReserved0 = */ NULL,
4146 /* .pfnReserved1 = */ NULL,
4147 /* .pfnReserved2 = */ NULL,
4148 /* .pfnReserved3 = */ NULL,
4149 /* .pfnReserved4 = */ NULL,
4150 /* .pfnReserved5 = */ NULL,
4151 /* .pfnReserved6 = */ NULL,
4152 /* .pfnReserved7 = */ NULL,
4153#elif defined(IN_RING0)
4154 /* .pfnEarlyConstruct = */ NULL,
4155 /* .pfnConstruct = */ gicRZConstruct,
4156 /* .pfnDestruct = */ NULL,
4157 /* .pfnFinalDestruct = */ NULL,
4158 /* .pfnRequest = */ NULL,
4159 /* .pfnReserved0 = */ NULL,
4160 /* .pfnReserved1 = */ NULL,
4161 /* .pfnReserved2 = */ NULL,
4162 /* .pfnReserved3 = */ NULL,
4163 /* .pfnReserved4 = */ NULL,
4164 /* .pfnReserved5 = */ NULL,
4165 /* .pfnReserved6 = */ NULL,
4166 /* .pfnReserved7 = */ NULL,
4167#elif defined(IN_RC)
4168 /* .pfnConstruct = */ gicRZConstruct,
4169 /* .pfnReserved0 = */ NULL,
4170 /* .pfnReserved1 = */ NULL,
4171 /* .pfnReserved2 = */ NULL,
4172 /* .pfnReserved3 = */ NULL,
4173 /* .pfnReserved4 = */ NULL,
4174 /* .pfnReserved5 = */ NULL,
4175 /* .pfnReserved6 = */ NULL,
4176 /* .pfnReserved7 = */ NULL,
4177#else
4178# error "Not in IN_RING3, IN_RING0 or IN_RC!"
4179#endif
4180 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
4181};
4182
4183
4184/**
4185 * The VirtualBox GIC backend.
4186 */
4187const PDMGICBACKEND g_GicBackend =
4188{
4189 /* .pfnReadSysReg = */ gicReadSysReg,
4190 /* .pfnWriteSysReg = */ gicWriteSysReg,
4191 /* .pfnSetSpi = */ gicSetSpi,
4192 /* .pfnSetPpi = */ gicSetPpi,
4193};
4194
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