VirtualBox

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

Last change on this file since 109113 was 109113, checked in by vboxsync, 8 days ago

VMM/GIC: bugref:10877 Assert and some fixes to the GITS_CTLR quiescent bit.

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