VirtualBox

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

Last change on this file since 109033 was 109033, checked in by vboxsync, 2 weeks ago

VMM/GIC: bugref:10877 Some GIC, ITS cleanup.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 145.0 KB
Line 
1/* $Id: GICAll.cpp 109033 2025-04-21 07:01:04Z 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 break;
2115 }
2116 case GIC_DIST_REG_PIDR2_OFF:
2117 Assert(pGicDev->uArchRev <= GIC_DIST_REG_PIDR2_ARCHREV_GICV4);
2118 *puValue = GIC_DIST_REG_PIDR2_ARCHREV_SET(pGicDev->uArchRev);
2119 break;
2120 case GIC_DIST_REG_IIDR_OFF:
2121 *puValue = GIC_DIST_REG_IIDR_IMPL_SET(GIC_JEDEC_JEP106_IDENTIFICATION_CODE, GIC_JEDEC_JEP106_CONTINUATION_CODE);
2122 break;
2123 case GIC_DIST_REG_TYPER2_OFF:
2124 *puValue = 0;
2125 break;
2126 default:
2127 AssertReleaseMsgFailed(("offReg=%#x\n", offReg));
2128 *puValue = 0;
2129 break;
2130 }
2131 return VINF_SUCCESS;
2132}
2133
2134
2135/**
2136 * Writes a distributor register.
2137 *
2138 * @returns Strict VBox status code.
2139 * @param pDevIns The device instance.
2140 * @param pVCpu The cross context virtual CPU structure.
2141 * @param offReg The offset of the register being written.
2142 * @param uValue The register value.
2143 */
2144DECLINLINE(VBOXSTRICTRC) gicDistWriteRegister(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t uValue)
2145{
2146 VMCPU_ASSERT_EMT(pVCpu); RT_NOREF(pVCpu);
2147 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
2148 PVMCC pVM = PDMDevHlpGetVM(pDevIns);
2149
2150 /*
2151 * 64-bit registers.
2152 */
2153 {
2154 /*
2155 * GICD_IROUTER<n> and GICD_IROUTER<n>E.
2156 */
2157 uint16_t const cbReg = sizeof(uint64_t);
2158 if (GIC_IS_REG_IN_RANGE(offReg, GIC_DIST_REG_IROUTERn_OFF_START, GIC_DIST_REG_IROUTERn_RANGE_SIZE))
2159 {
2160 /* Hardware does not map the first 32 registers (corresponding to SGIs and PPIs). */
2161 uint16_t const idxExt = GIC_INTID_RANGE_SPI_START;
2162 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_IROUTERn_OFF_START) / cbReg;
2163 return gicDistWriteIntrRoutingReg(pGicDev, idxReg, uValue);
2164 }
2165 if (GIC_IS_REG_IN_RANGE(offReg, GIC_DIST_REG_IROUTERnE_OFF_START, GIC_DIST_REG_IROUTERnE_RANGE_SIZE))
2166 {
2167 uint16_t const idxExt = RT_ELEMENTS(pGicDev->au32IntrRouting) / 2;
2168 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_IROUTERnE_OFF_START) / cbReg;
2169 return gicDistWriteIntrRoutingReg(pGicDev, idxReg, uValue);
2170 }
2171
2172 }
2173
2174 /*
2175 * 32-bit registers.
2176 */
2177 {
2178 /*
2179 * GICD_IGROUPR<n> and GICD_IGROUPR<n>E.
2180 */
2181 uint16_t const cbReg = sizeof(uint32_t);
2182 if (GIC_IS_REG_IN_RANGE(offReg, GIC_DIST_REG_IGROUPRn_OFF_START, GIC_DIST_REG_IGROUPRn_RANGE_SIZE))
2183 {
2184 uint16_t const idxReg = (offReg - GIC_DIST_REG_IGROUPRn_OFF_START) / cbReg;
2185 return gicDistWriteIntrGroupReg(pVM, pGicDev, idxReg, uValue);
2186 }
2187 if (GIC_IS_REG_IN_RANGE(offReg, GIC_DIST_REG_IGROUPRnE_OFF_START, GIC_DIST_REG_IGROUPRnE_RANGE_SIZE))
2188 {
2189 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrGroup) / 2;
2190 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_IGROUPRnE_OFF_START) / cbReg;
2191 return gicDistWriteIntrGroupReg(pVM, pGicDev, idxReg, uValue);
2192 }
2193
2194 /*
2195 * GICD_ISENABLER<n> and GICD_ISENABLER<n>E.
2196 * GICD_ICENABLER<n> and GICD_ICENABLER<n>E.
2197 */
2198 if (GIC_IS_REG_IN_RANGE(offReg, GIC_DIST_REG_ISENABLERn_OFF_START, GIC_DIST_REG_ISENABLERn_RANGE_SIZE))
2199 {
2200 uint16_t const idxReg = (offReg - GIC_DIST_REG_ISENABLERn_OFF_START) / cbReg;
2201 return gicDistWriteIntrSetEnableReg(pVM, pGicDev, idxReg, uValue);
2202 }
2203 if (GIC_IS_REG_IN_RANGE(offReg, GIC_DIST_REG_ISENABLERnE_OFF_START, GIC_DIST_REG_ISENABLERnE_RANGE_SIZE))
2204 {
2205 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrEnabled) / 2;
2206 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ISENABLERnE_OFF_START) / cbReg;
2207 return gicDistWriteIntrSetEnableReg(pVM, pGicDev, idxReg, uValue);
2208 }
2209 if (GIC_IS_REG_IN_RANGE(offReg, GIC_DIST_REG_ICENABLERn_OFF_START, GIC_DIST_REG_ICENABLERn_RANGE_SIZE))
2210 {
2211 uint16_t const idxReg = (offReg - GIC_DIST_REG_ICENABLERn_OFF_START) / cbReg;
2212 return gicDistWriteIntrClearEnableReg(pVM, pGicDev, idxReg, uValue);
2213 }
2214 if (GIC_IS_REG_IN_RANGE(offReg, GIC_DIST_REG_ICENABLERnE_OFF_START, GIC_DIST_REG_ICENABLERnE_RANGE_SIZE))
2215 {
2216 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrEnabled) / 2;
2217 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ICENABLERnE_OFF_START) / cbReg;
2218 return gicDistWriteIntrClearEnableReg(pVM, pGicDev, idxReg, uValue);
2219 }
2220
2221 /*
2222 * GICD_ISACTIVER<n> and GICD_ISACTIVER<n>E.
2223 * GICD_ICACTIVER<n> and GICD_ICACTIVER<n>E.
2224 */
2225 if (GIC_IS_REG_IN_RANGE(offReg, GIC_DIST_REG_ISACTIVERn_OFF_START, GIC_DIST_REG_ISACTIVERn_RANGE_SIZE))
2226 {
2227 uint16_t const idxReg = (offReg - GIC_DIST_REG_ISACTIVERn_OFF_START) / cbReg;
2228 return gicDistWriteIntrSetActiveReg(pVM, pGicDev, idxReg, uValue);
2229 }
2230 if (GIC_IS_REG_IN_RANGE(offReg, GIC_DIST_REG_ISACTIVERnE_OFF_START, GIC_DIST_REG_ISACTIVERnE_RANGE_SIZE))
2231 {
2232 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrActive) / 2;
2233 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ISACTIVERnE_OFF_START) / cbReg;
2234 return gicDistWriteIntrSetActiveReg(pVM, pGicDev, idxReg, uValue);
2235 }
2236 if (GIC_IS_REG_IN_RANGE(offReg, GIC_DIST_REG_ICACTIVERn_OFF_START, GIC_DIST_REG_ICACTIVERn_RANGE_SIZE))
2237 {
2238 uint16_t const idxReg = (offReg - GIC_DIST_REG_ICACTIVERn_OFF_START) / cbReg;
2239 return gicDistWriteIntrClearActiveReg(pVM, pGicDev, idxReg, uValue);
2240 }
2241 if (GIC_IS_REG_IN_RANGE(offReg, GIC_DIST_REG_ICACTIVERnE_OFF_START, GIC_DIST_REG_ICACTIVERnE_RANGE_SIZE))
2242 {
2243 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrActive) / 2;
2244 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ICACTIVERnE_OFF_START) / cbReg;
2245 return gicDistWriteIntrClearActiveReg(pVM, pGicDev, idxReg, uValue);
2246 }
2247
2248 /*
2249 * GICD_IPRIORITYR<n> and GICD_IPRIORITYR<n>E.
2250 */
2251 if (GIC_IS_REG_IN_RANGE(offReg, GIC_DIST_REG_IPRIORITYRn_OFF_START, GIC_DIST_REG_IPRIORITYRn_RANGE_SIZE))
2252 {
2253 uint16_t const idxReg = (offReg - GIC_DIST_REG_IPRIORITYRn_OFF_START) / cbReg;
2254 return gicDistWriteIntrPriorityReg(pGicDev, idxReg, uValue);
2255 }
2256 if (GIC_IS_REG_IN_RANGE(offReg, GIC_DIST_REG_IPRIORITYRnE_OFF_START, GIC_DIST_REG_IPRIORITYRnE_RANGE_SIZE))
2257 {
2258 uint16_t const idxExt = RT_ELEMENTS(pGicDev->abIntrPriority) / (2 * sizeof(uint32_t));
2259 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_IPRIORITYRnE_OFF_START) / cbReg;
2260 return gicDistWriteIntrPriorityReg(pGicDev, idxReg, uValue);
2261 }
2262
2263 /*
2264 * GICD_ISPENDR<n> and GICD_ISPENDR<n>E.
2265 * GICD_ICPENDR<n> and GICD_ICPENDR<n>E.
2266 */
2267 if (GIC_IS_REG_IN_RANGE(offReg, GIC_DIST_REG_ISPENDRn_OFF_START, GIC_DIST_REG_ISPENDRn_RANGE_SIZE))
2268 {
2269 uint16_t const idxReg = (offReg - GIC_DIST_REG_ISPENDRn_OFF_START) / cbReg;
2270 return gicDistWriteIntrSetPendingReg(pVM, pGicDev, idxReg, uValue);
2271 }
2272 if (GIC_IS_REG_IN_RANGE(offReg, GIC_DIST_REG_ISPENDRnE_OFF_START, GIC_DIST_REG_ISPENDRnE_RANGE_SIZE))
2273 {
2274 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrPending) / 2;
2275 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ISPENDRnE_OFF_START) / cbReg;
2276 return gicDistWriteIntrSetPendingReg(pVM, pGicDev, idxReg, uValue);
2277 }
2278 if (GIC_IS_REG_IN_RANGE(offReg, GIC_DIST_REG_ICPENDRn_OFF_START, GIC_DIST_REG_ICPENDRn_RANGE_SIZE))
2279 {
2280 uint16_t const idxReg = (offReg - GIC_DIST_REG_ICPENDRn_OFF_START) / cbReg;
2281 return gicDistWriteIntrClearPendingReg(pVM, pGicDev, idxReg, uValue);
2282 }
2283 if (GIC_IS_REG_IN_RANGE(offReg, GIC_DIST_REG_ICPENDRnE_OFF_START, GIC_DIST_REG_ICPENDRnE_RANGE_SIZE))
2284 {
2285 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrPending) / 2;
2286 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ICPENDRnE_OFF_START) / cbReg;
2287 return gicDistWriteIntrClearPendingReg(pVM, pGicDev, idxReg, uValue);
2288 }
2289
2290 /*
2291 * GICD_ICFGR<n> and GICD_ICFGR<n>E.
2292 */
2293 if (GIC_IS_REG_IN_RANGE(offReg, GIC_DIST_REG_ICFGRn_OFF_START, GIC_DIST_REG_ICFGRn_RANGE_SIZE))
2294 {
2295 uint16_t const idxReg = (offReg - GIC_DIST_REG_ICFGRn_OFF_START) / cbReg;
2296 return gicDistWriteIntrConfigReg(pGicDev, idxReg, uValue);
2297 }
2298 if (GIC_IS_REG_IN_RANGE(offReg, GIC_DIST_REG_ICFGRnE_OFF_START, GIC_DIST_REG_ICFGRnE_RANGE_SIZE))
2299 {
2300 uint16_t const idxExt = RT_ELEMENTS(pGicDev->bmIntrConfig) / 2;
2301 uint16_t const idxReg = idxExt + (offReg - GIC_DIST_REG_ICFGRnE_OFF_START) / cbReg;
2302 return gicDistWriteIntrConfigReg(pGicDev, idxReg, uValue);
2303 }
2304 }
2305
2306 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
2307 switch (offReg)
2308 {
2309 case GIC_DIST_REG_CTLR_OFF:
2310 Assert(!(uValue & GIC_DIST_REG_CTRL_ARE_NS));
2311 pGicDev->fIntrGroup0Enabled = RT_BOOL(uValue & GIC_DIST_REG_CTRL_ENABLE_GRP0);
2312 pGicDev->fIntrGroup1Enabled = RT_BOOL(uValue & GIC_DIST_REG_CTRL_ENABLE_GRP1_NS);
2313 rcStrict = gicDistUpdateIrqState(pVM, pGicDev);
2314 break;
2315 default:
2316 {
2317 /* Windows 11 arm64 (24H2) writes zeroes into these reserved registers. We ignore them. */
2318 if (offReg >= 0x7fe0 && offReg <= 0x7ffc)
2319 LogFlowFunc(("Bad guest writing to reserved GIC distributor register space [0x7fe0..0x7ffc] -- ignoring!"));
2320 else
2321 AssertReleaseMsgFailed(("offReg=%#x uValue=%#RX32\n", offReg, uValue));
2322 break;
2323 }
2324 }
2325
2326 return rcStrict;
2327}
2328
2329
2330/**
2331 * Reads a GIC redistributor register.
2332 *
2333 * @returns VBox status code.
2334 * @param pDevIns The device instance.
2335 * @param pVCpu The cross context virtual CPU structure.
2336 * @param idRedist The redistributor ID.
2337 * @param offReg The offset of the register being read.
2338 * @param puValue Where to store the register value.
2339 */
2340DECLINLINE(VBOXSTRICTRC) gicReDistReadRegister(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint32_t idRedist, uint16_t offReg, uint32_t *puValue)
2341{
2342 PCVMCC pVM = pVCpu->CTX_SUFF(pVM);
2343 PCGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
2344 Assert(idRedist == pVCpu->idCpu);
2345
2346 switch (offReg)
2347 {
2348 case GIC_REDIST_REG_TYPER_OFF:
2349 *puValue = (pVCpu->idCpu == pVM->cCpus - 1 ? GIC_REDIST_REG_TYPER_LAST : 0)
2350 | GIC_REDIST_REG_TYPER_CPU_NUMBER_SET(idRedist)
2351 | GIC_REDIST_REG_TYPER_CMN_LPI_AFF_SET(GIC_REDIST_REG_TYPER_CMN_LPI_AFF_ALL)
2352 | (pGicDev->fExtPpi ? GIC_REDIST_REG_TYPER_PPI_NUM_SET(pGicDev->uMaxExtPpi) : 0)
2353 | (pGicDev->fLpi ? GIC_REDIST_REG_TYPER_PLPIS : 0);
2354 Assert(!pGicDev->fExtPpi || pGicDev->uMaxExtPpi > 0);
2355 break;
2356 case GIC_REDIST_REG_WAKER_OFF:
2357 *puValue = 0;
2358 break;
2359 case GIC_REDIST_REG_IIDR_OFF:
2360 *puValue = GIC_REDIST_REG_IIDR_IMPL_SET(GIC_JEDEC_JEP106_IDENTIFICATION_CODE, GIC_JEDEC_JEP106_CONTINUATION_CODE);
2361 break;
2362 case GIC_REDIST_REG_TYPER_AFFINITY_OFF:
2363 *puValue = idRedist;
2364 break;
2365 case GIC_REDIST_REG_PIDR2_OFF:
2366 Assert(pGicDev->uArchRev <= GIC_DIST_REG_PIDR2_ARCHREV_GICV4);
2367 *puValue = GIC_REDIST_REG_PIDR2_ARCHREV_SET(pGicDev->uArchRev);
2368 break;
2369 case GIC_REDIST_REG_CTLR_OFF:
2370 *puValue = (pGicDev->fEnableLpis ? GIC_REDIST_REG_CTLR_ENABLE_LPI : 0)
2371 | GIC_REDIST_REG_CTLR_CES_SET(1);
2372 break;
2373 case GIC_REDIST_REG_PROPBASER_OFF:
2374 *puValue = pGicDev->uLpiConfigBaseReg.s.Lo;
2375 break;
2376 case GIC_REDIST_REG_PROPBASER_OFF + 4:
2377 *puValue = pGicDev->uLpiConfigBaseReg.s.Hi;
2378 break;
2379 case GIC_REDIST_REG_PENDBASER_OFF:
2380 *puValue = pGicDev->uLpiPendingBaseReg.s.Lo;
2381 break;
2382 case GIC_REDIST_REG_PENDBASER_OFF + 4:
2383 *puValue = pGicDev->uLpiPendingBaseReg.s.Hi;
2384 break;
2385 default:
2386 AssertReleaseMsgFailed(("offReg=%#x\n", offReg));
2387 *puValue = 0;
2388 break;
2389 }
2390 return VINF_SUCCESS;
2391}
2392
2393
2394/**
2395 * Reads a GIC redistributor SGI/PPI frame register.
2396 *
2397 * @returns VBox status code.
2398 * @param pDevIns The device instance.
2399 * @param pVCpu The cross context virtual CPU structure.
2400 * @param offReg The offset of the register being read.
2401 * @param puValue Where to store the register value.
2402 */
2403DECLINLINE(VBOXSTRICTRC) gicReDistReadSgiPpiRegister(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t *puValue)
2404{
2405 VMCPU_ASSERT_EMT(pVCpu);
2406 RT_NOREF(pDevIns);
2407
2408 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
2409 PCGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
2410 uint16_t const cbReg = sizeof(uint32_t);
2411
2412 /*
2413 * GICR_IGROUPR0 and GICR_IGROUPR<n>E.
2414 */
2415 if (GIC_IS_REG_IN_RANGE(offReg, GIC_REDIST_SGI_PPI_REG_IGROUPR0_OFF, GIC_REDIST_SGI_PPI_REG_IGROUPRnE_RANGE_SIZE))
2416 {
2417 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_IGROUPR0_OFF) / cbReg;
2418 return gicReDistReadIntrGroupReg(pGicDev, pGicCpu, idxReg, puValue);
2419 }
2420
2421 /*
2422 * GICR_ISENABLER0 and GICR_ISENABLER<n>E.
2423 * GICR_ICENABLER0 and GICR_ICENABLER<n>E.
2424 */
2425 if (GIC_IS_REG_IN_RANGE(offReg, GIC_REDIST_SGI_PPI_REG_ISENABLER0_OFF, GIC_REDIST_SGI_PPI_REG_ISENABLERnE_RANGE_SIZE))
2426 {
2427 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ISENABLER0_OFF) / cbReg;
2428 return gicReDistReadIntrEnableReg(pGicDev, pGicCpu, idxReg, puValue);
2429 }
2430 if (GIC_IS_REG_IN_RANGE(offReg, GIC_REDIST_SGI_PPI_REG_ICENABLER0_OFF, GIC_REDIST_SGI_PPI_REG_ICENABLERnE_RANGE_SIZE))
2431 {
2432 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ICENABLERnE_OFF_START) / cbReg;
2433 return gicReDistReadIntrEnableReg(pGicDev, pGicCpu, idxReg, puValue);
2434 }
2435
2436 /*
2437 * GICR_ISACTIVER0 and GICR_ISACTIVER<n>E.
2438 * GICR_ICACTIVER0 and GICR_ICACTIVER<n>E.
2439 */
2440 if (GIC_IS_REG_IN_RANGE(offReg, GIC_REDIST_SGI_PPI_REG_ISACTIVER0_OFF, GIC_REDIST_SGI_PPI_REG_ISACTIVERnE_RANGE_SIZE))
2441 {
2442 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ISACTIVER0_OFF) / cbReg;
2443 return gicReDistReadIntrActiveReg(pGicCpu, idxReg, puValue);
2444 }
2445 if (GIC_IS_REG_IN_RANGE(offReg, GIC_REDIST_SGI_PPI_REG_ICACTIVER0_OFF, GIC_REDIST_SGI_PPI_REG_ICACTIVERnE_RANGE_SIZE))
2446 {
2447 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ICACTIVER0_OFF) / cbReg;
2448 return gicReDistReadIntrActiveReg(pGicCpu, idxReg, puValue);
2449 }
2450
2451 /*
2452 * GICR_ISPENDR0 and GICR_ISPENDR<n>E.
2453 * GICR_ICPENDR0 and GICR_ICPENDR<n>E.
2454 */
2455 if (GIC_IS_REG_IN_RANGE(offReg, GIC_REDIST_SGI_PPI_REG_ISPENDR0_OFF, GIC_REDIST_SGI_PPI_REG_ISPENDRnE_RANGE_SIZE))
2456 {
2457 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ISPENDR0_OFF) / cbReg;
2458 return gicReDistReadIntrPendingReg(pGicDev, pGicCpu, idxReg, puValue);
2459 }
2460 if (GIC_IS_REG_IN_RANGE(offReg, GIC_REDIST_SGI_PPI_REG_ICPENDR0_OFF, GIC_REDIST_SGI_PPI_REG_ICPENDRnE_RANGE_SIZE))
2461 {
2462 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ICPENDR0_OFF) / cbReg;
2463 return gicReDistReadIntrPendingReg(pGicDev, pGicCpu, idxReg, puValue);
2464 }
2465
2466 /*
2467 * GICR_IPRIORITYR<n> and GICR_IPRIORITYR<n>E.
2468 */
2469 if (GIC_IS_REG_IN_RANGE(offReg, GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START, GIC_REDIST_SGI_PPI_REG_IPRIORITYRnE_RANGE_SIZE))
2470 {
2471 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START) / cbReg;
2472 return gicReDistReadIntrPriorityReg(pGicDev, pGicCpu, idxReg, puValue);
2473 }
2474
2475 /*
2476 * GICR_ICFGR0, GICR_ICFGR1 and GICR_ICFGR<n>E.
2477 */
2478 if (GIC_IS_REG_IN_RANGE(offReg, GIC_REDIST_SGI_PPI_REG_ICFGR0_OFF, GIC_REDIST_SGI_PPI_REG_ICFGRnE_RANGE_SIZE))
2479 {
2480 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ICFGR0_OFF) / cbReg;
2481 return gicReDistReadIntrConfigReg(pGicDev, pGicCpu, idxReg, puValue);
2482 }
2483
2484 AssertReleaseMsgFailed(("offReg=%#x (%s)\n", offReg, gicReDistGetSgiPpiRegDescription(offReg)));
2485 *puValue = 0;
2486 return VINF_SUCCESS;
2487}
2488
2489
2490/**
2491 * Writes a GIC redistributor frame register.
2492 *
2493 * @returns Strict VBox status code.
2494 * @param pDevIns The device instance.
2495 * @param pVCpu The cross context virtual CPU structure.
2496 * @param offReg The offset of the register being written.
2497 * @param uValue The register value.
2498 */
2499DECLINLINE(VBOXSTRICTRC) gicReDistWriteRegister(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t uValue)
2500{
2501 VMCPU_ASSERT_EMT(pVCpu);
2502 RT_NOREF(pVCpu, uValue);
2503
2504 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
2505 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
2506 switch (offReg)
2507 {
2508 case GIC_REDIST_REG_WAKER_OFF:
2509 Assert(uValue == 0);
2510 break;
2511 case GIC_REDIST_REG_CTLR_OFF:
2512 {
2513 /* Check if LPIs are supported and whether the enable LPI bit changed. */
2514 uint32_t const uOldCtlr = pGicDev->fEnableLpis ? GIC_REDIST_REG_CTLR_ENABLE_LPI : 0;
2515 uint32_t const uNewCtlr = uValue & GIC_REDIST_REG_CTLR_ENABLE_LPI;
2516 if ( pGicDev->fLpi
2517 && ((uNewCtlr ^ uOldCtlr) & GIC_REDIST_REG_CTLR_ENABLE_LPI))
2518 {
2519 pGicDev->fEnableLpis = RT_BOOL(uNewCtlr & GIC_REDIST_REG_CTLR_ENABLE_LPI);
2520 if (pGicDev->fEnableLpis)
2521 {
2522 gicDistReadLpiConfigTableFromMem(pDevIns);
2523 gicReDistReadLpiPendingBitmapFromMem(pDevIns, pVCpu, pGicDev);
2524 }
2525 else
2526 {
2527 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
2528 RT_ZERO(pGicCpu->bmLpiPending);
2529 }
2530 }
2531 break;
2532 }
2533 case GIC_REDIST_REG_PROPBASER_OFF:
2534 pGicDev->uLpiConfigBaseReg.s.Lo = uValue & RT_LO_U32(GIC_REDIST_REG_PROPBASER_RW_MASK);
2535 break;
2536 case GIC_REDIST_REG_PROPBASER_OFF + 4:
2537 pGicDev->uLpiConfigBaseReg.s.Hi = uValue & RT_HI_U32(GIC_REDIST_REG_PROPBASER_RW_MASK);
2538 break;
2539 case GIC_REDIST_REG_PENDBASER_OFF:
2540 pGicDev->uLpiPendingBaseReg.s.Lo = uValue & RT_LO_U32(GIC_REDIST_REG_PENDBASER_RW_MASK);
2541 break;
2542 case GIC_REDIST_REG_PENDBASER_OFF + 4:
2543 pGicDev->uLpiPendingBaseReg.s.Hi = uValue & RT_HI_U32(GIC_REDIST_REG_PENDBASER_RW_MASK);
2544 break;
2545 default:
2546 AssertReleaseMsgFailed(("offReg=%#x (%s) uValue=%#RX32\n", offReg, gicReDistGetRegDescription(offReg), uValue));
2547 break;
2548 }
2549
2550 return rcStrict;
2551}
2552
2553
2554/**
2555 * Writes a GIC redistributor SGI/PPI frame register.
2556 *
2557 * @returns Strict VBox status code.
2558 * @param pDevIns The device instance.
2559 * @param pVCpu The cross context virtual CPU structure.
2560 * @param offReg The offset of the register being written.
2561 * @param uValue The register value.
2562 */
2563DECLINLINE(VBOXSTRICTRC) gicReDistWriteSgiPpiRegister(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t uValue)
2564{
2565 VMCPU_ASSERT_EMT(pVCpu);
2566 PCGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PCGICDEV);
2567 uint16_t const cbReg = sizeof(uint32_t);
2568
2569 /*
2570 * GICR_IGROUPR0 and GICR_IGROUPR<n>E.
2571 */
2572 if (GIC_IS_REG_IN_RANGE(offReg, GIC_REDIST_SGI_PPI_REG_IGROUPR0_OFF, GIC_REDIST_SGI_PPI_REG_IGROUPRnE_RANGE_SIZE))
2573 {
2574 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_IGROUPR0_OFF) / cbReg;
2575 return gicReDistWriteIntrGroupReg(pGicDev, pVCpu, idxReg, uValue);
2576 }
2577
2578 /*
2579 * GICR_ISENABLER0 and GICR_ISENABLER<n>E.
2580 * GICR_ICENABLER0 and GICR_ICENABLER<n>E.
2581 */
2582 if (GIC_IS_REG_IN_RANGE(offReg, GIC_REDIST_SGI_PPI_REG_ISENABLER0_OFF, GIC_REDIST_SGI_PPI_REG_ISENABLERnE_RANGE_SIZE))
2583 {
2584 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ISENABLER0_OFF) / cbReg;
2585 return gicReDistWriteIntrSetEnableReg(pGicDev, pVCpu, idxReg, uValue);
2586 }
2587 if (GIC_IS_REG_IN_RANGE(offReg, GIC_REDIST_SGI_PPI_REG_ICENABLER0_OFF, GIC_REDIST_SGI_PPI_REG_ICENABLERnE_RANGE_SIZE))
2588 {
2589 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ICENABLER0_OFF) / cbReg;
2590 return gicReDistWriteIntrClearEnableReg(pGicDev, pVCpu, idxReg, uValue);
2591 }
2592
2593 /*
2594 * GICR_ISACTIVER0 and GICR_ISACTIVER<n>E.
2595 * GICR_ICACTIVER0 and GICR_ICACTIVER<n>E.
2596 */
2597 if (GIC_IS_REG_IN_RANGE(offReg, GIC_REDIST_SGI_PPI_REG_ISACTIVER0_OFF, GIC_REDIST_SGI_PPI_REG_ISACTIVERnE_RANGE_SIZE))
2598 {
2599 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ISACTIVER0_OFF) / cbReg;
2600 return gicReDistWriteIntrSetActiveReg(pGicDev, pVCpu, idxReg, uValue);
2601 }
2602 if (GIC_IS_REG_IN_RANGE(offReg, GIC_REDIST_SGI_PPI_REG_ICACTIVER0_OFF, GIC_REDIST_SGI_PPI_REG_ICACTIVERnE_RANGE_SIZE))
2603 {
2604 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ICACTIVER0_OFF) / cbReg;
2605 return gicReDistWriteIntrClearActiveReg(pGicDev, pVCpu, idxReg, uValue);
2606 }
2607
2608 /*
2609 * GICR_ISPENDR0 and GICR_ISPENDR<n>E.
2610 * GICR_ICPENDR0 and GICR_ICPENDR<n>E.
2611 */
2612 if (GIC_IS_REG_IN_RANGE(offReg, GIC_REDIST_SGI_PPI_REG_ISPENDR0_OFF, GIC_REDIST_SGI_PPI_REG_ISPENDRnE_RANGE_SIZE))
2613 {
2614 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ISPENDR0_OFF) / cbReg;
2615 return gicReDistWriteIntrSetPendingReg(pGicDev, pVCpu, idxReg, uValue);
2616 }
2617 if (GIC_IS_REG_IN_RANGE(offReg, GIC_REDIST_SGI_PPI_REG_ICPENDR0_OFF, GIC_REDIST_SGI_PPI_REG_ICPENDRnE_RANGE_SIZE))
2618 {
2619 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ICPENDR0_OFF) / cbReg;
2620 return gicReDistWriteIntrClearPendingReg(pGicDev, pVCpu, idxReg, uValue);
2621 }
2622
2623 /*
2624 * GICR_IPRIORITYR<n> and GICR_IPRIORITYR<n>E.
2625 */
2626 if (GIC_IS_REG_IN_RANGE(offReg, GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START, GIC_REDIST_SGI_PPI_REG_IPRIORITYRnE_RANGE_SIZE))
2627 {
2628 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START) / cbReg;
2629 return gicReDistWriteIntrPriorityReg(pGicDev, pVCpu, idxReg, uValue);
2630 }
2631
2632 /*
2633 * GICR_ICFGR0, GIC_ICFGR1 and GICR_ICFGR<n>E.
2634 */
2635 if (GIC_IS_REG_IN_RANGE(offReg, GIC_REDIST_SGI_PPI_REG_ICFGR0_OFF, GIC_REDIST_SGI_PPI_REG_ICFGRnE_RANGE_SIZE))
2636 {
2637 uint16_t const idxReg = (offReg - GIC_REDIST_SGI_PPI_REG_ICFGR0_OFF) / cbReg;
2638 return gicReDistWriteIntrConfigReg(pGicDev, pVCpu, idxReg, uValue);
2639 }
2640
2641 AssertReleaseMsgFailed(("offReg=%#RX16 (%s)\n", offReg, gicReDistGetSgiPpiRegDescription(offReg)));
2642 return VERR_INTERNAL_ERROR_2;
2643}
2644
2645
2646/**
2647 * @interface_method_impl{PDMGICBACKEND,pfnSetSpi}
2648 */
2649static DECLCALLBACK(int) gicSetSpi(PVMCC pVM, uint32_t uSpiIntId, bool fAsserted)
2650{
2651 LogFlowFunc(("pVM=%p uSpiIntId=%u fAsserted=%RTbool\n",
2652 pVM, uSpiIntId, fAsserted));
2653
2654 PGIC pGic = VM_TO_GIC(pVM);
2655 PPDMDEVINS pDevIns = pGic->CTX_SUFF(pDevIns);
2656 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
2657
2658#ifdef VBOX_WITH_STATISTICS
2659 PVMCPU pVCpu = VMMGetCpuById(pVM, 0);
2660 STAM_COUNTER_INC(&pVCpu->gic.s.StatSetSpi);
2661 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
2662#endif
2663 STAM_PROFILE_START(&pGicCpu->StatProfSetSpi, a);
2664
2665 uint16_t const uIntId = GIC_INTID_RANGE_SPI_START + uSpiIntId;
2666 uint16_t const idxIntr = gicDistGetIndexFromIntId(uIntId);
2667
2668 Assert(idxIntr >= GIC_INTID_RANGE_SPI_START);
2669 AssertMsgReturn(idxIntr < sizeof(pGicDev->bmIntrPending) * 8,
2670 ("out-of-range SPI interrupt ID %RU32 (%RU32)\n", uIntId, uSpiIntId),
2671 VERR_INVALID_PARAMETER);
2672
2673 GIC_CRIT_SECT_ENTER(pDevIns);
2674
2675 /* Update the interrupt pending state. */
2676 if (fAsserted)
2677 ASMBitSet(&pGicDev->bmIntrPending[0], idxIntr);
2678 else
2679 ASMBitClear(&pGicDev->bmIntrPending[0], idxIntr);
2680
2681 int const rc = VBOXSTRICTRC_VAL(gicDistUpdateIrqState(pVM, pGicDev));
2682 STAM_PROFILE_STOP(&pGicCpu->StatProfSetSpi, a);
2683
2684 GIC_CRIT_SECT_LEAVE(pDevIns);
2685 return rc;
2686}
2687
2688
2689/**
2690 * @interface_method_impl{PDMGICBACKEND,pfnSetPpi}
2691 */
2692static DECLCALLBACK(int) gicSetPpi(PVMCPUCC pVCpu, uint32_t uPpiIntId, bool fAsserted)
2693{
2694 LogFlowFunc(("pVCpu=%p{.idCpu=%u} uPpiIntId=%u fAsserted=%RTbool\n", pVCpu, pVCpu->idCpu, uPpiIntId, fAsserted));
2695
2696 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
2697 PCGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PCGICDEV);
2698 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
2699
2700 STAM_COUNTER_INC(&pVCpu->gic.s.StatSetPpi);
2701 STAM_PROFILE_START(&pGicCpu->StatProfSetPpi, b);
2702
2703 uint32_t const uIntId = GIC_INTID_RANGE_PPI_START + uPpiIntId;
2704 uint16_t const idxIntr = gicReDistGetIndexFromIntId(uIntId);
2705
2706 Assert(idxIntr >= GIC_INTID_RANGE_PPI_START);
2707 AssertMsgReturn(idxIntr < sizeof(pGicCpu->bmIntrPending) * 8,
2708 ("out-of-range PPI interrupt ID %RU32 (%RU32)\n", uIntId, uPpiIntId),
2709 VERR_INVALID_PARAMETER);
2710
2711 GIC_CRIT_SECT_ENTER(pDevIns);
2712
2713 /* Update the interrupt pending state. */
2714 if (fAsserted)
2715 ASMBitSet(&pGicCpu->bmIntrPending[0], idxIntr);
2716 else
2717 ASMBitClear(&pGicCpu->bmIntrPending[0], idxIntr);
2718
2719 int const rc = VBOXSTRICTRC_VAL(gicReDistUpdateIrqState(pGicDev, pVCpu));
2720 STAM_PROFILE_STOP(&pGicCpu->StatProfSetPpi, b);
2721
2722 GIC_CRIT_SECT_LEAVE(pDevIns);
2723 return rc;
2724}
2725
2726
2727/**
2728 * Sets the specified software generated interrupt (SGI).
2729 *
2730 * @returns Strict VBox status code.
2731 * @param pGicDev The GIC distributor state.
2732 * @param pVCpu The cross context virtual CPU structure.
2733 * @param pDestCpuSet Which CPUs to deliver the SGI to.
2734 * @param uIntId The SGI interrupt ID.
2735 */
2736static VBOXSTRICTRC gicSetSgi(PCGICDEV pGicDev, PVMCPUCC pVCpu, PCVMCPUSET pDestCpuSet, uint8_t uIntId)
2737{
2738 LogFlowFunc(("pVCpu=%p{.idCpu=%u} uIntId=%u\n", pVCpu, pVCpu->idCpu, uIntId));
2739
2740 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
2741 PCVMCC pVM = pVCpu->CTX_SUFF(pVM);
2742 uint32_t const cCpus = pVM->cCpus;
2743 AssertReturn(uIntId <= GIC_INTID_RANGE_SGI_LAST, VERR_INVALID_PARAMETER);
2744 Assert(GIC_CRIT_SECT_IS_OWNER(pDevIns)); NOREF(pDevIns);
2745
2746 for (VMCPUID idCpu = 0; idCpu < cCpus; idCpu++)
2747 if (VMCPUSET_IS_PRESENT(pDestCpuSet, idCpu))
2748 {
2749 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVM->CTX_SUFF(apCpus)[idCpu]);
2750 pGicCpu->bmIntrPending[0] |= RT_BIT_32(uIntId);
2751 }
2752
2753 return gicDistUpdateIrqState(pVM, pGicDev);
2754}
2755
2756
2757/**
2758 * Writes to the redistributor's SGI group 1 register (ICC_SGI1R_EL1).
2759 *
2760 * @returns Strict VBox status code.
2761 * @param pGicDev The GIC distributor state.
2762 * @param pVCpu The cross context virtual CPU structure.
2763 * @param uValue The value being written to the ICC_SGI1R_EL1 register.
2764 */
2765static VBOXSTRICTRC gicReDistWriteSgiReg(PCGICDEV pGicDev, PVMCPUCC pVCpu, uint64_t uValue)
2766{
2767#ifdef VBOX_WITH_STATISTICS
2768 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
2769 STAM_COUNTER_INC(&pVCpu->gic.s.StatSetSgi);
2770 STAM_PROFILE_START(&pGicCpu->StatProfSetSgi, c);
2771#else
2772 PCGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
2773#endif
2774
2775 VMCPUSET DestCpuSet;
2776 if (uValue & ARMV8_ICC_SGI1R_EL1_AARCH64_IRM)
2777 {
2778 /*
2779 * Deliver to all VCPUs but this one.
2780 */
2781 VMCPUSET_FILL(&DestCpuSet);
2782 VMCPUSET_DEL(&DestCpuSet, pVCpu->idCpu);
2783 }
2784 else
2785 {
2786 /*
2787 * Target specific VCPUs.
2788 * See ARM GICv3 and GICv4 Software Overview spec 3.3 "Affinity routing".
2789 */
2790 VMCPUSET_EMPTY(&DestCpuSet);
2791 bool const fRangeSelSupport = RT_BOOL(pGicCpu->uIccCtlr & ARMV8_ICC_CTLR_EL1_AARCH64_RSS);
2792 uint8_t const idRangeStart = ARMV8_ICC_SGI1R_EL1_AARCH64_RS_GET(uValue) * 16;
2793 uint16_t const bmCpuInterfaces = ARMV8_ICC_SGI1R_EL1_AARCH64_TARGET_LIST_GET(uValue);
2794 uint8_t const uAff1 = ARMV8_ICC_SGI1R_EL1_AARCH64_AFF1_GET(uValue);
2795 uint8_t const uAff2 = ARMV8_ICC_SGI1R_EL1_AARCH64_AFF2_GET(uValue);
2796 uint8_t const uAff3 = (pGicCpu->uIccCtlr & ARMV8_ICC_CTLR_EL1_AARCH64_A3V)
2797 ? ARMV8_ICC_SGI1R_EL1_AARCH64_AFF3_GET(uValue)
2798 : 0;
2799 uint32_t const cCpus = pVCpu->CTX_SUFF(pVM)->cCpus;
2800 for (uint8_t idCpuInterface = 0; idCpuInterface < 16; idCpuInterface++)
2801 {
2802 if (bmCpuInterfaces & RT_BIT(idCpuInterface))
2803 {
2804 VMCPUID idCpuTarget;
2805 if (fRangeSelSupport)
2806 idCpuTarget = RT_MAKE_U32_FROM_U8(idRangeStart + idCpuInterface, uAff1, uAff2, uAff3);
2807 else
2808 idCpuTarget = gicGetCpuIdFromAffinity(idCpuInterface, uAff1, uAff2, uAff3);
2809 if (RT_LIKELY(idCpuTarget < cCpus))
2810 VMCPUSET_ADD(&DestCpuSet, idCpuTarget);
2811 else
2812 AssertReleaseMsgFailed(("VCPU ID out-of-bounds %RU32, must be < %u\n", idCpuTarget, cCpus));
2813 }
2814 }
2815 }
2816
2817 if (!VMCPUSET_IS_EMPTY(&DestCpuSet))
2818 {
2819 uint8_t const uSgiIntId = ARMV8_ICC_SGI1R_EL1_AARCH64_INTID_GET(uValue);
2820 Assert(GIC_IS_INTR_SGI(uSgiIntId));
2821 VBOXSTRICTRC const rcStrict = gicSetSgi(pGicDev, pVCpu, &DestCpuSet, uSgiIntId);
2822 Assert(RT_SUCCESS(rcStrict)); RT_NOREF_PV(rcStrict);
2823 }
2824
2825 STAM_PROFILE_STOP(&pGicCpu->StatProfSetSgi, c);
2826 return VINF_SUCCESS;
2827}
2828
2829
2830/**
2831 * @interface_method_impl{PDMGICBACKEND,pfnReadSysReg}
2832 */
2833static DECLCALLBACK(VBOXSTRICTRC) gicReadSysReg(PVMCPUCC pVCpu, uint32_t u32Reg, uint64_t *pu64Value)
2834{
2835 /*
2836 * Validate.
2837 */
2838 VMCPU_ASSERT_EMT(pVCpu);
2839 Assert(pu64Value);
2840
2841 STAM_COUNTER_INC(&pVCpu->gic.s.StatSysRegRead);
2842
2843 *pu64Value = 0;
2844 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
2845 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
2846 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
2847
2848 GIC_CRIT_SECT_ENTER(pDevIns);
2849
2850 switch (u32Reg)
2851 {
2852 case ARMV8_AARCH64_SYSREG_ICC_PMR_EL1:
2853 *pu64Value = pGicCpu->bIntrPriorityMask;
2854 break;
2855 case ARMV8_AARCH64_SYSREG_ICC_IAR0_EL1:
2856 AssertReleaseFailed();
2857 break;
2858 case ARMV8_AARCH64_SYSREG_ICC_EOIR0_EL1:
2859 AssertReleaseFailed();
2860 break;
2861 case ARMV8_AARCH64_SYSREG_ICC_HPPIR0_EL1:
2862 AssertReleaseFailed();
2863 break;
2864 case ARMV8_AARCH64_SYSREG_ICC_BPR0_EL1:
2865 *pu64Value = ARMV8_ICC_BPR0_EL1_AARCH64_BINARYPOINT_SET(pGicCpu->bBinaryPtGroup0);
2866 break;
2867 case ARMV8_AARCH64_SYSREG_ICC_AP0R0_EL1:
2868 AssertReleaseFailed();
2869 *pu64Value = pGicCpu->bmActivePriorityGroup0[0];
2870 break;
2871 case ARMV8_AARCH64_SYSREG_ICC_AP0R1_EL1:
2872 AssertReleaseFailed();
2873 *pu64Value = pGicCpu->bmActivePriorityGroup0[1];
2874 break;
2875 case ARMV8_AARCH64_SYSREG_ICC_AP0R2_EL1:
2876 AssertReleaseFailed();
2877 *pu64Value = pGicCpu->bmActivePriorityGroup0[2];
2878 break;
2879 case ARMV8_AARCH64_SYSREG_ICC_AP0R3_EL1:
2880 AssertReleaseFailed();
2881 *pu64Value = pGicCpu->bmActivePriorityGroup0[3];
2882 break;
2883 case ARMV8_AARCH64_SYSREG_ICC_AP1R0_EL1:
2884 AssertReleaseFailed();
2885 *pu64Value = pGicCpu->bmActivePriorityGroup1[0];
2886 break;
2887 case ARMV8_AARCH64_SYSREG_ICC_AP1R1_EL1:
2888 AssertReleaseFailed();
2889 *pu64Value = pGicCpu->bmActivePriorityGroup1[1];
2890 break;
2891 case ARMV8_AARCH64_SYSREG_ICC_AP1R2_EL1:
2892 AssertReleaseFailed();
2893 *pu64Value = pGicCpu->bmActivePriorityGroup1[2];
2894 break;
2895 case ARMV8_AARCH64_SYSREG_ICC_AP1R3_EL1:
2896 AssertReleaseFailed();
2897 *pu64Value = pGicCpu->bmActivePriorityGroup1[3];
2898 break;
2899 case ARMV8_AARCH64_SYSREG_ICC_NMIAR1_EL1:
2900 AssertReleaseFailed();
2901 break;
2902 case ARMV8_AARCH64_SYSREG_ICC_DIR_EL1:
2903 AssertReleaseFailed();
2904 break;
2905 case ARMV8_AARCH64_SYSREG_ICC_RPR_EL1:
2906 *pu64Value = pGicCpu->abRunningPriorities[pGicCpu->idxRunningPriority];
2907 break;
2908 case ARMV8_AARCH64_SYSREG_ICC_SGI1R_EL1:
2909 AssertReleaseFailed();
2910 break;
2911 case ARMV8_AARCH64_SYSREG_ICC_ASGI1R_EL1:
2912 AssertReleaseFailed();
2913 break;
2914 case ARMV8_AARCH64_SYSREG_ICC_SGI0R_EL1:
2915 AssertReleaseFailed();
2916 break;
2917 case ARMV8_AARCH64_SYSREG_ICC_IAR1_EL1:
2918 *pu64Value = gicAckHighestPriorityPendingIntr(pGicDev, pVCpu, false /*fGroup0*/, true /*fGroup1*/);
2919 break;
2920 case ARMV8_AARCH64_SYSREG_ICC_EOIR1_EL1:
2921 AssertReleaseFailed();
2922 break;
2923 case ARMV8_AARCH64_SYSREG_ICC_HPPIR1_EL1:
2924 {
2925 AssertReleaseFailed();
2926 *pu64Value = gicGetHighestPriorityPendingIntr(pGicDev, pGicCpu, false /*fGroup0*/, true /*fGroup1*/,
2927 NULL /*pidxIntr*/, NULL /*pbPriority*/);
2928 break;
2929 }
2930 case ARMV8_AARCH64_SYSREG_ICC_BPR1_EL1:
2931 *pu64Value = ARMV8_ICC_BPR1_EL1_AARCH64_BINARYPOINT_SET(pGicCpu->bBinaryPtGroup1);
2932 break;
2933 case ARMV8_AARCH64_SYSREG_ICC_CTLR_EL1:
2934 *pu64Value = pGicCpu->uIccCtlr;
2935 break;
2936 case ARMV8_AARCH64_SYSREG_ICC_SRE_EL1:
2937 AssertReleaseFailed();
2938 break;
2939 case ARMV8_AARCH64_SYSREG_ICC_IGRPEN0_EL1:
2940 *pu64Value = pGicCpu->fIntrGroup0Enabled ? ARMV8_ICC_IGRPEN0_EL1_AARCH64_ENABLE : 0;
2941 break;
2942 case ARMV8_AARCH64_SYSREG_ICC_IGRPEN1_EL1:
2943 *pu64Value = pGicCpu->fIntrGroup1Enabled ? ARMV8_ICC_IGRPEN1_EL1_AARCH64_ENABLE : 0;
2944 break;
2945 default:
2946 AssertReleaseMsgFailed(("u32Reg=%#RX32\n", u32Reg));
2947 break;
2948 }
2949
2950 GIC_CRIT_SECT_LEAVE(pDevIns);
2951
2952 LogFlowFunc(("pVCpu=%p u32Reg=%#x{%s} pu64Value=%RX64\n", pVCpu, u32Reg, gicIccGetRegDescription(u32Reg), *pu64Value));
2953 return VINF_SUCCESS;
2954}
2955
2956
2957/**
2958 * @interface_method_impl{PDMGICBACKEND,pfnWriteSysReg}
2959 */
2960static DECLCALLBACK(VBOXSTRICTRC) gicWriteSysReg(PVMCPUCC pVCpu, uint32_t u32Reg, uint64_t u64Value)
2961{
2962 /*
2963 * Validate.
2964 */
2965 VMCPU_ASSERT_EMT(pVCpu);
2966 LogFlowFunc(("pVCpu=%p u32Reg=%#x{%s} u64Value=%RX64\n", pVCpu, u32Reg, gicIccGetRegDescription(u32Reg), u64Value));
2967
2968 STAM_COUNTER_INC(&pVCpu->gic.s.StatSysRegWrite);
2969
2970 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
2971 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
2972 PGICCPU pGicCpu = VMCPU_TO_GICCPU(pVCpu);
2973
2974 GIC_CRIT_SECT_ENTER(pDevIns);
2975
2976 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
2977 switch (u32Reg)
2978 {
2979 case ARMV8_AARCH64_SYSREG_ICC_PMR_EL1:
2980 LogFlowFunc(("ICC_PMR_EL1: Interrupt priority now %u\n", (uint8_t)u64Value));
2981 pGicCpu->bIntrPriorityMask = (uint8_t)u64Value;
2982 rcStrict = gicReDistUpdateIrqState(pGicDev, pVCpu);
2983 break;
2984 case ARMV8_AARCH64_SYSREG_ICC_IAR0_EL1:
2985 AssertReleaseFailed();
2986 break;
2987 case ARMV8_AARCH64_SYSREG_ICC_EOIR0_EL1:
2988 AssertReleaseFailed();
2989 break;
2990 case ARMV8_AARCH64_SYSREG_ICC_HPPIR0_EL1:
2991 AssertReleaseFailed();
2992 break;
2993 case ARMV8_AARCH64_SYSREG_ICC_BPR0_EL1:
2994 pGicCpu->bBinaryPtGroup0 = (uint8_t)ARMV8_ICC_BPR0_EL1_AARCH64_BINARYPOINT_GET(u64Value);
2995 break;
2996 case ARMV8_AARCH64_SYSREG_ICC_AP0R0_EL1:
2997 case ARMV8_AARCH64_SYSREG_ICC_AP0R1_EL1:
2998 case ARMV8_AARCH64_SYSREG_ICC_AP0R2_EL1:
2999 case ARMV8_AARCH64_SYSREG_ICC_AP0R3_EL1:
3000 case ARMV8_AARCH64_SYSREG_ICC_AP1R0_EL1:
3001 case ARMV8_AARCH64_SYSREG_ICC_AP1R1_EL1:
3002 case ARMV8_AARCH64_SYSREG_ICC_AP1R2_EL1:
3003 case ARMV8_AARCH64_SYSREG_ICC_AP1R3_EL1:
3004 /* Writes ignored, well behaving guest would write all 0s or the last read value of the register. */
3005 break;
3006 case ARMV8_AARCH64_SYSREG_ICC_NMIAR1_EL1:
3007 AssertReleaseFailed();
3008 break;
3009 case ARMV8_AARCH64_SYSREG_ICC_DIR_EL1:
3010 AssertReleaseFailed();
3011 break;
3012 case ARMV8_AARCH64_SYSREG_ICC_RPR_EL1:
3013 AssertReleaseFailed();
3014 break;
3015 case ARMV8_AARCH64_SYSREG_ICC_SGI1R_EL1:
3016 {
3017 gicReDistWriteSgiReg(pGicDev, pVCpu, u64Value);
3018 break;
3019 }
3020 case ARMV8_AARCH64_SYSREG_ICC_ASGI1R_EL1:
3021 AssertReleaseFailed();
3022 break;
3023 case ARMV8_AARCH64_SYSREG_ICC_SGI0R_EL1:
3024 AssertReleaseFailed();
3025 break;
3026 case ARMV8_AARCH64_SYSREG_ICC_IAR1_EL1:
3027 AssertReleaseFailed();
3028 break;
3029 case ARMV8_AARCH64_SYSREG_ICC_EOIR1_EL1:
3030 {
3031 /*
3032 * We only support priority drop + interrupt deactivation with writes to this register.
3033 * This avoids an extra access which would be required by software for deactivation.
3034 */
3035 Assert(!(pGicCpu->uIccCtlr & ARMV8_ICC_CTLR_EL1_AARCH64_EOIMODE));
3036
3037 /*
3038 * Mark the interrupt as inactive, though it might still be pending.
3039 * It is up to the guest to ensure the interrupt ID belongs to the right group as
3040 * failure to do so results in unpredictable behavior.
3041 *
3042 * See ARM GIC spec. 12.2.10 "ICC_EOIR1_EL1, Interrupt Controller End Of Interrupt Register 1".
3043 * NOTE! The order of the 'if' checks below are crucial.
3044 */
3045 uint16_t const uIntId = (uint16_t)u64Value;
3046 if (uIntId <= GIC_INTID_RANGE_PPI_LAST)
3047 {
3048 /* SGIs and PPIs. */
3049 AssertCompile(GIC_INTID_RANGE_PPI_LAST < 8 * sizeof(pGicDev->bmIntrActive[0]));
3050 Assert(pGicDev->fAffRoutingEnabled);
3051 pGicCpu->bmIntrActive[0] &= ~RT_BIT_32(uIntId);
3052 }
3053 else if (uIntId <= GIC_INTID_RANGE_SPI_LAST)
3054 {
3055 /* SPIs. */
3056 uint16_t const idxIntr = /*gicDistGetIndexFromIntId*/(uIntId);
3057 AssertReturn(idxIntr < sizeof(pGicDev->bmIntrActive) * 8, VERR_BUFFER_OVERFLOW);
3058 ASMBitClear(&pGicDev->bmIntrActive[0], idxIntr);
3059 }
3060 else if (uIntId <= GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT)
3061 {
3062 /* Special interrupt IDs, ignored. */
3063 Log(("Ignoring write to EOI with special interrupt ID.\n"));
3064 break;
3065 }
3066 else if (uIntId <= GIC_INTID_RANGE_EXT_PPI_LAST)
3067 {
3068 /* Extended PPIs. */
3069 uint16_t const idxIntr = gicReDistGetIndexFromIntId(uIntId);
3070 AssertReturn(idxIntr < sizeof(pGicCpu->bmIntrActive) * 8, VERR_BUFFER_OVERFLOW);
3071 ASMBitClear(&pGicCpu->bmIntrActive[0], idxIntr);
3072 }
3073 else if (uIntId <= GIC_INTID_RANGE_EXT_SPI_LAST)
3074 {
3075 /* Extended SPIs. */
3076 uint16_t const idxIntr = gicDistGetIndexFromIntId(uIntId);
3077 AssertReturn(idxIntr < sizeof(pGicDev->bmIntrActive) * 8, VERR_BUFFER_OVERFLOW);
3078 ASMBitClear(&pGicDev->bmIntrActive[0], idxIntr);
3079 }
3080 else
3081 {
3082 AssertMsgFailed(("Invalid INTID %u\n", uIntId));
3083 break;
3084 }
3085
3086 /*
3087 * Drop priority by restoring previous interrupt.
3088 */
3089 if (RT_LIKELY(pGicCpu->idxRunningPriority))
3090 {
3091 LogFlowFunc(("Restoring interrupt priority from %u -> %u (idxRunningPriority: %u -> %u)\n",
3092 pGicCpu->abRunningPriorities[pGicCpu->idxRunningPriority],
3093 pGicCpu->abRunningPriorities[pGicCpu->idxRunningPriority - 1],
3094 pGicCpu->idxRunningPriority, pGicCpu->idxRunningPriority - 1));
3095
3096 /*
3097 * Clear the interrupt priority from the active priorities bitmap.
3098 * It is up to the guest to ensure that writes to EOI registers are done in the exact
3099 * reverse order of the reads from the IAR registers.
3100 *
3101 * See ARM GIC spec 4.1.1 "Physical CPU interface".
3102 */
3103 uint8_t const idxPreemptionLevel = pGicCpu->abRunningPriorities[pGicCpu->idxRunningPriority] >> 1;
3104 AssertCompile(sizeof(pGicCpu->bmActivePriorityGroup1) * 8 >= 128);
3105 ASMBitClear(&pGicCpu->bmActivePriorityGroup1[0], idxPreemptionLevel);
3106
3107 pGicCpu->idxRunningPriority--;
3108 Assert(pGicCpu->abRunningPriorities[0] == GIC_IDLE_PRIORITY);
3109 }
3110 else
3111 AssertReleaseMsgFailed(("Index of running-priority interrupt out-of-bounds %u\n", pGicCpu->idxRunningPriority));
3112 rcStrict = gicReDistUpdateIrqState(pGicDev, pVCpu);
3113 break;
3114 }
3115 case ARMV8_AARCH64_SYSREG_ICC_HPPIR1_EL1:
3116 AssertReleaseFailed();
3117 break;
3118 case ARMV8_AARCH64_SYSREG_ICC_BPR1_EL1:
3119 pGicCpu->bBinaryPtGroup1 = (uint8_t)ARMV8_ICC_BPR1_EL1_AARCH64_BINARYPOINT_GET(u64Value);
3120 break;
3121 case ARMV8_AARCH64_SYSREG_ICC_CTLR_EL1:
3122 pGicCpu->uIccCtlr &= ARMV8_ICC_CTLR_EL1_RW;
3123 /** @todo */
3124 break;
3125 case ARMV8_AARCH64_SYSREG_ICC_SRE_EL1:
3126 AssertReleaseFailed();
3127 break;
3128 case ARMV8_AARCH64_SYSREG_ICC_IGRPEN0_EL1:
3129 pGicCpu->fIntrGroup0Enabled = RT_BOOL(u64Value & ARMV8_ICC_IGRPEN0_EL1_AARCH64_ENABLE);
3130 break;
3131 case ARMV8_AARCH64_SYSREG_ICC_IGRPEN1_EL1:
3132 pGicCpu->fIntrGroup1Enabled = RT_BOOL(u64Value & ARMV8_ICC_IGRPEN1_EL1_AARCH64_ENABLE);
3133 break;
3134 default:
3135 AssertReleaseMsgFailed(("u32Reg=%#RX32\n", u32Reg));
3136 break;
3137 }
3138
3139 GIC_CRIT_SECT_LEAVE(pDevIns);
3140 return rcStrict;
3141}
3142
3143
3144/**
3145 * Initializes the GIC distributor state.
3146 *
3147 * @param pDevIns The device instance.
3148 * @remarks This is also called during VM reset, so do NOT remove values that are
3149 * cleared to zero!
3150 */
3151static void gicInit(PPDMDEVINS pDevIns)
3152{
3153 LogFlowFunc(("\n"));
3154 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
3155
3156 /* Distributor. */
3157 RT_ZERO(pGicDev->bmIntrGroup);
3158 RT_ZERO(pGicDev->bmIntrConfig);
3159 RT_ZERO(pGicDev->bmIntrEnabled);
3160 RT_ZERO(pGicDev->bmIntrPending);
3161 RT_ZERO(pGicDev->bmIntrActive);
3162 RT_ZERO(pGicDev->abIntrPriority);
3163 RT_ZERO(pGicDev->au32IntrRouting);
3164 RT_ZERO(pGicDev->bmIntrRoutingMode);
3165 pGicDev->fIntrGroup0Enabled = false;
3166 pGicDev->fIntrGroup1Enabled = false;
3167 pGicDev->fAffRoutingEnabled = true; /* GICv2 backwards compatibility is not implemented, so this is RA1/WI. */
3168
3169 /* GITS. */
3170 PGITSDEV pGitsDev = &pGicDev->Gits;
3171 gitsInit(pGitsDev);
3172
3173 /* LPIs. */
3174 RT_ZERO(pGicDev->abLpiConfig);
3175 pGicDev->uLpiConfigBaseReg.u = 0;
3176 pGicDev->uLpiPendingBaseReg.u = 0;
3177 pGicDev->fEnableLpis = false;
3178}
3179
3180
3181/**
3182 * Initialies the GIC redistributor and CPU interface state.
3183 *
3184 * @param pDevIns The device instance.
3185 * @param pVCpu The cross context virtual CPU structure.
3186 * @remarks This is also called during VM reset, so do NOT remove values that are
3187 * cleared to zero!
3188 */
3189static void gicInitCpu(PPDMDEVINS pDevIns, PVMCPUCC pVCpu)
3190{
3191 LogFlowFunc(("[%u]\n", pVCpu->idCpu));
3192 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
3193 PGICCPU pGicCpu = &pVCpu->gic.s;
3194
3195 RT_ZERO(pGicCpu->bmIntrGroup);
3196 RT_ZERO(pGicCpu->bmIntrConfig);
3197 /* SGIs are always edge-triggered, writes to GICR_ICFGR0 are to be ignored. */
3198 pGicCpu->bmIntrConfig[0] = 0xaaaaaaaa;
3199 RT_ZERO(pGicCpu->bmIntrEnabled);
3200 RT_ZERO(pGicCpu->bmIntrPending);
3201 RT_ZERO(pGicCpu->bmIntrActive);
3202 RT_ZERO(pGicCpu->abIntrPriority);
3203
3204 pGicCpu->uIccCtlr = ARMV8_ICC_CTLR_EL1_AARCH64_PMHE
3205 | ARMV8_ICC_CTLR_EL1_AARCH64_PRIBITS_SET(4)
3206 | ARMV8_ICC_CTLR_EL1_AARCH64_IDBITS_SET(ARMV8_ICC_CTLR_EL1_AARCH64_IDBITS_16BITS)
3207 | (pGicDev->fRangeSel ? ARMV8_ICC_CTLR_EL1_AARCH64_RSS : 0)
3208 | (pGicDev->fAff3Levels ? ARMV8_ICC_CTLR_EL1_AARCH64_A3V : 0)
3209 | (pGicDev->fExtPpi || pGicDev->fExtSpi ? ARMV8_ICC_CTLR_EL1_AARCH64_EXTRANGE : 0);
3210
3211 pGicCpu->bIntrPriorityMask = 0; /* Means no interrupt gets through to the PE. */
3212 pGicCpu->idxRunningPriority = 0;
3213 memset((void *)&pGicCpu->abRunningPriorities[0], 0xff, sizeof(pGicCpu->abRunningPriorities));
3214 RT_ZERO(pGicCpu->bmActivePriorityGroup0);
3215 RT_ZERO(pGicCpu->bmActivePriorityGroup1);
3216 pGicCpu->bBinaryPtGroup0 = 0;
3217 pGicCpu->bBinaryPtGroup1 = 0;
3218 pGicCpu->fIntrGroup0Enabled = false;
3219 pGicCpu->fIntrGroup1Enabled = false;
3220 RT_ZERO(pGicCpu->bmLpiPending);
3221}
3222
3223
3224/**
3225 * Initializes per-VM GIC to the state following a power-up or hardware
3226 * reset.
3227 *
3228 * @param pDevIns The device instance.
3229 */
3230DECLHIDDEN(void) gicReset(PPDMDEVINS pDevIns)
3231{
3232 LogFlowFunc(("\n"));
3233 gicInit(pDevIns);
3234}
3235
3236
3237/**
3238 * Initializes per-VCPU GIC to the state following a power-up or hardware
3239 * reset.
3240 *
3241 * @param pDevIns The device instance.
3242 * @param pVCpu The cross context virtual CPU structure.
3243 */
3244DECLHIDDEN(void) gicResetCpu(PPDMDEVINS pDevIns, PVMCPUCC pVCpu)
3245{
3246 LogFlowFunc(("[%u]\n", pVCpu->idCpu));
3247 VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu);
3248 gicInitCpu(pDevIns, pVCpu);
3249}
3250
3251
3252/**
3253 * @callback_method_impl{FNIOMMMIONEWREAD}
3254 */
3255DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicDistMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
3256{
3257 NOREF(pvUser);
3258 Assert(!(off & 0x3));
3259 Assert(cb == 4); RT_NOREF_PV(cb);
3260
3261 PVMCPUCC pVCpu = PDMDevHlpGetVMCPU(pDevIns);
3262 uint16_t offReg = off & 0xfffc;
3263 uint32_t uValue = 0;
3264
3265 STAM_COUNTER_INC(&pVCpu->gic.s.StatMmioRead);
3266
3267 VBOXSTRICTRC rc = VBOXSTRICTRC_VAL(gicDistReadRegister(pDevIns, pVCpu, offReg, &uValue));
3268 *(uint32_t *)pv = uValue;
3269
3270 LogFlowFunc(("[%u]: offReg=%#RX16 (%s) uValue=%#RX32\n", pVCpu->idCpu, offReg, gicDistGetRegDescription(offReg), uValue));
3271 return rc;
3272}
3273
3274
3275/**
3276 * @callback_method_impl{FNIOMMMIONEWWRITE}
3277 */
3278DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicDistMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
3279{
3280 NOREF(pvUser);
3281 Assert(!(off & 0x3));
3282 Assert(cb == 4); RT_NOREF_PV(cb);
3283
3284 PVMCPUCC pVCpu = PDMDevHlpGetVMCPU(pDevIns);
3285 uint16_t offReg = off & 0xfffc;
3286 uint32_t uValue = *(uint32_t *)pv;
3287
3288 STAM_COUNTER_INC(&pVCpu->gic.s.StatMmioWrite);
3289 LogFlowFunc(("[%u]: offReg=%#RX16 (%s) uValue=%#RX32\n", pVCpu->idCpu, offReg, gicDistGetRegDescription(offReg), uValue));
3290
3291 return gicDistWriteRegister(pDevIns, pVCpu, offReg, uValue);
3292}
3293
3294
3295/**
3296 * @callback_method_impl{FNIOMMMIONEWREAD}
3297 */
3298DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicReDistMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
3299{
3300 NOREF(pvUser);
3301 Assert(!(off & 0x3));
3302 Assert(cb == 4); RT_NOREF_PV(cb);
3303
3304 /*
3305 * Determine the redistributor being targeted. Each redistributor takes
3306 * GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE bytes
3307 * and the redistributors are adjacent.
3308 */
3309 uint32_t const idReDist = off / (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
3310 off %= (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
3311
3312 PVMCC pVM = PDMDevHlpGetVM(pDevIns);
3313 Assert(idReDist < pVM->cCpus);
3314 PVMCPUCC pVCpu = pVM->CTX_SUFF(apCpus)[idReDist];
3315
3316 STAM_COUNTER_INC(&pVCpu->gic.s.StatMmioRead);
3317
3318 /* Redistributor or SGI/PPI frame? */
3319 uint16_t const offReg = off & 0xfffc;
3320 uint32_t uValue = 0;
3321 VBOXSTRICTRC rcStrict;
3322 if (off < GIC_REDIST_REG_FRAME_SIZE)
3323 rcStrict = gicReDistReadRegister(pDevIns, pVCpu, idReDist, offReg, &uValue);
3324 else
3325 rcStrict = gicReDistReadSgiPpiRegister(pDevIns, pVCpu, offReg, &uValue);
3326
3327 *(uint32_t *)pv = uValue;
3328 LogFlowFunc(("[%u]: off=%RGp idReDist=%u offReg=%#RX16 (%s) uValue=%#RX32 -> %Rrc\n", pVCpu->idCpu, off, idReDist, offReg,
3329 gicReDistGetRegDescription(offReg), uValue, VBOXSTRICTRC_VAL(rcStrict)));
3330 return rcStrict;
3331}
3332
3333
3334/**
3335 * @callback_method_impl{FNIOMMMIONEWWRITE}
3336 */
3337DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicReDistMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
3338{
3339 NOREF(pvUser);
3340 Assert(!(off & 0x3));
3341 Assert(cb == 4); RT_NOREF_PV(cb);
3342
3343 uint32_t uValue = *(uint32_t *)pv;
3344
3345 /*
3346 * Determine the redistributor being targeted. Each redistributor takes
3347 * GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE bytes
3348 * and the redistributors are adjacent.
3349 */
3350 uint32_t const idReDist = off / (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
3351 off %= (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
3352
3353 PCVMCC pVM = PDMDevHlpGetVM(pDevIns);
3354 Assert(idReDist < pVM->cCpus);
3355 PVMCPUCC pVCpu = pVM->CTX_SUFF(apCpus)[idReDist];
3356
3357 STAM_COUNTER_INC(&pVCpu->gic.s.StatMmioWrite);
3358
3359 /* Redistributor or SGI/PPI frame? */
3360 uint16_t const offReg = off & 0xfffc;
3361 VBOXSTRICTRC rcStrict;
3362 if (off < GIC_REDIST_REG_FRAME_SIZE)
3363 rcStrict = gicReDistWriteRegister(pDevIns, pVCpu, offReg, uValue);
3364 else
3365 rcStrict = gicReDistWriteSgiPpiRegister(pDevIns, pVCpu, offReg, uValue);
3366
3367 LogFlowFunc(("[%u]: off=%RGp idReDist=%u offReg=%#RX16 (%s) uValue=%#RX32 -> %Rrc\n", pVCpu->idCpu, off, idReDist, offReg,
3368 gicReDistGetRegDescription(offReg), uValue, VBOXSTRICTRC_VAL(rcStrict)));
3369 return rcStrict;
3370}
3371
3372
3373/**
3374 * @callback_method_impl{FNIOMMMIONEWREAD}
3375 */
3376DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicItsMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
3377{
3378 RT_NOREF_PV(pvUser);
3379 Assert(!(off & 0x3));
3380 Assert(cb == 8 || cb == 4);
3381
3382 PCGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PCGICDEV);
3383 PCGITSDEV pGitsDev = &pGicDev->Gits;
3384 uint64_t uReg;
3385 if (off < GITS_REG_FRAME_SIZE)
3386 {
3387 /* Control registers space. */
3388 uint16_t const offReg = off & 0xfffc;
3389 uReg = gitsMmioReadCtrl(pGitsDev, offReg, cb);
3390 LogFlowFunc(("offReg=%#RX16 (%s) read %#RX64\n", offReg, gitsGetCtrlRegDescription(offReg), uReg));
3391 }
3392 else
3393 {
3394 /* Translation registers space. */
3395 uint16_t const offReg = (off - GITS_REG_FRAME_SIZE) & 0xfffc;
3396 uReg = gitsMmioReadTranslate(pGitsDev, offReg, cb);
3397 LogFlowFunc(("offReg=%#RX16 (%s) read %#RX64\n", offReg, gitsGetTranslationRegDescription(offReg), uReg));
3398 }
3399
3400 if (cb == 8)
3401 *(uint64_t *)pv = uReg;
3402 else
3403 *(uint32_t *)pv = uReg;
3404 return VINF_SUCCESS;
3405}
3406
3407
3408/**
3409 * @callback_method_impl{FNIOMMMIONEWWRITE}
3410 */
3411DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicItsMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
3412{
3413 RT_NOREF_PV(pvUser);
3414 Assert(!(off & 0x3));
3415 Assert(cb == 8 || cb == 4);
3416
3417 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
3418 PGITSDEV pGitsDev = &pGicDev->Gits;
3419
3420 uint64_t const uValue = cb == 8 ? *(uint64_t *)pv : *(uint32_t *)pv;
3421 if (off < GITS_REG_FRAME_SIZE)
3422 {
3423 /* Control registers space. */
3424 uint16_t const offReg = off & 0xfffc;
3425 gitsMmioWriteCtrl(pDevIns, pGitsDev, offReg, uValue, cb);
3426 LogFlowFunc(("offReg=%#RX16 (%s) written %#RX64\n", offReg, gitsGetCtrlRegDescription(offReg), uValue));
3427 }
3428 else
3429 {
3430 /* Translation registers space. */
3431 uint16_t const offReg = (off - GITS_REG_FRAME_SIZE) & 0xfffc;
3432 gitsMmioWriteTranslate(pGitsDev, offReg, uValue, cb);
3433 LogFlowFunc(("offReg=%#RX16 (%s) written %#RX64\n", offReg, gitsGetTranslationRegDescription(offReg), uValue));
3434 }
3435 return VINF_SUCCESS;
3436}
3437
3438
3439/**
3440 * GIC device registration structure.
3441 */
3442const PDMDEVREG g_DeviceGIC =
3443{
3444 /* .u32Version = */ PDM_DEVREG_VERSION,
3445 /* .uReserved0 = */ 0,
3446 /* .szName = */ "gic",
3447 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
3448 /* .fClass = */ PDM_DEVREG_CLASS_PIC,
3449 /* .cMaxInstances = */ 1,
3450 /* .uSharedVersion = */ 42,
3451 /* .cbInstanceShared = */ sizeof(GICDEV),
3452 /* .cbInstanceCC = */ 0,
3453 /* .cbInstanceRC = */ 0,
3454 /* .cMaxPciDevices = */ 0,
3455 /* .cMaxMsixVectors = */ 0,
3456 /* .pszDescription = */ "Generic Interrupt Controller",
3457#if defined(IN_RING3)
3458 /* .szRCMod = */ "VMMRC.rc",
3459 /* .szR0Mod = */ "VMMR0.r0",
3460 /* .pfnConstruct = */ gicR3Construct,
3461 /* .pfnDestruct = */ gicR3Destruct,
3462 /* .pfnRelocate = */ NULL,
3463 /* .pfnMemSetup = */ NULL,
3464 /* .pfnPowerOn = */ NULL,
3465 /* .pfnReset = */ gicR3Reset,
3466 /* .pfnSuspend = */ NULL,
3467 /* .pfnResume = */ NULL,
3468 /* .pfnAttach = */ NULL,
3469 /* .pfnDetach = */ NULL,
3470 /* .pfnQueryInterface = */ NULL,
3471 /* .pfnInitComplete = */ NULL,
3472 /* .pfnPowerOff = */ NULL,
3473 /* .pfnSoftReset = */ NULL,
3474 /* .pfnReserved0 = */ NULL,
3475 /* .pfnReserved1 = */ NULL,
3476 /* .pfnReserved2 = */ NULL,
3477 /* .pfnReserved3 = */ NULL,
3478 /* .pfnReserved4 = */ NULL,
3479 /* .pfnReserved5 = */ NULL,
3480 /* .pfnReserved6 = */ NULL,
3481 /* .pfnReserved7 = */ NULL,
3482#elif defined(IN_RING0)
3483 /* .pfnEarlyConstruct = */ NULL,
3484 /* .pfnConstruct = */ NULL,
3485 /* .pfnDestruct = */ NULL,
3486 /* .pfnFinalDestruct = */ NULL,
3487 /* .pfnRequest = */ NULL,
3488 /* .pfnReserved0 = */ NULL,
3489 /* .pfnReserved1 = */ NULL,
3490 /* .pfnReserved2 = */ NULL,
3491 /* .pfnReserved3 = */ NULL,
3492 /* .pfnReserved4 = */ NULL,
3493 /* .pfnReserved5 = */ NULL,
3494 /* .pfnReserved6 = */ NULL,
3495 /* .pfnReserved7 = */ NULL,
3496#elif defined(IN_RC)
3497 /* .pfnConstruct = */ NULL,
3498 /* .pfnReserved0 = */ NULL,
3499 /* .pfnReserved1 = */ NULL,
3500 /* .pfnReserved2 = */ NULL,
3501 /* .pfnReserved3 = */ NULL,
3502 /* .pfnReserved4 = */ NULL,
3503 /* .pfnReserved5 = */ NULL,
3504 /* .pfnReserved6 = */ NULL,
3505 /* .pfnReserved7 = */ NULL,
3506#else
3507# error "Not in IN_RING3, IN_RING0 or IN_RC!"
3508#endif
3509 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
3510};
3511
3512
3513/**
3514 * The VirtualBox GIC backend.
3515 */
3516const PDMGICBACKEND g_GicBackend =
3517{
3518 /* .pfnReadSysReg = */ gicReadSysReg,
3519 /* .pfnWriteSysReg = */ gicWriteSysReg,
3520 /* .pfnSetSpi = */ gicSetSpi,
3521 /* .pfnSetPpi = */ gicSetPpi,
3522 /* .pfnSendMsi = */ gitsSendMsi,
3523};
3524
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