VirtualBox

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

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

VMM/GIC: bugref:10404 Some register renaming, minor bits.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 60.1 KB
Line 
1/* $Id: GICAll.cpp 107957 2025-01-28 08:38:40Z 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
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_DEV_APIC
33#include "GICInternal.h"
34#include <VBox/vmm/pdmgic.h>
35#include <VBox/vmm/pdmdev.h>
36#include <VBox/vmm/pdmapi.h>
37#include <VBox/vmm/vmcc.h>
38#include <VBox/vmm/vmm.h>
39#include <VBox/vmm/vmcpuset.h>
40#ifdef IN_RING0
41# include <VBox/vmm/gvmm.h>
42#endif
43
44
45/*********************************************************************************************************************************
46* Internal Functions *
47*********************************************************************************************************************************/
48
49
50/*********************************************************************************************************************************
51* Global Variables *
52*********************************************************************************************************************************/
53
54#ifdef LOG_ENABLED
55/**
56 * Returns a human readable string of the given exception class.
57 *
58 * @returns Pointer to the string matching the given EC.
59 * @param u32Ec The exception class to return the string for.
60 */
61static const char *gicIccRegisterStringify(uint32_t u32Reg)
62{
63 switch (u32Reg)
64 {
65#define GIC_ICC_REG_CASE(a_Reg) case ARMV8_AARCH64_SYSREG_ ## a_Reg: return #a_Reg
66 GIC_ICC_REG_CASE(ICC_PMR_EL1);
67 GIC_ICC_REG_CASE(ICC_IAR0_EL1);
68 GIC_ICC_REG_CASE(ICC_EOIR0_EL1);
69 GIC_ICC_REG_CASE(ICC_HPPIR0_EL1);
70 GIC_ICC_REG_CASE(ICC_BPR0_EL1);
71 GIC_ICC_REG_CASE(ICC_AP0R0_EL1);
72 GIC_ICC_REG_CASE(ICC_AP0R1_EL1);
73 GIC_ICC_REG_CASE(ICC_AP0R2_EL1);
74 GIC_ICC_REG_CASE(ICC_AP0R3_EL1);
75 GIC_ICC_REG_CASE(ICC_AP1R0_EL1);
76 GIC_ICC_REG_CASE(ICC_AP1R1_EL1);
77 GIC_ICC_REG_CASE(ICC_AP1R2_EL1);
78 GIC_ICC_REG_CASE(ICC_AP1R3_EL1);
79 GIC_ICC_REG_CASE(ICC_DIR_EL1);
80 GIC_ICC_REG_CASE(ICC_RPR_EL1);
81 GIC_ICC_REG_CASE(ICC_SGI1R_EL1);
82 GIC_ICC_REG_CASE(ICC_ASGI1R_EL1);
83 GIC_ICC_REG_CASE(ICC_SGI0R_EL1);
84 GIC_ICC_REG_CASE(ICC_IAR1_EL1);
85 GIC_ICC_REG_CASE(ICC_EOIR1_EL1);
86 GIC_ICC_REG_CASE(ICC_HPPIR1_EL1);
87 GIC_ICC_REG_CASE(ICC_BPR1_EL1);
88 GIC_ICC_REG_CASE(ICC_CTLR_EL1);
89 GIC_ICC_REG_CASE(ICC_SRE_EL1);
90 GIC_ICC_REG_CASE(ICC_IGRPEN0_EL1);
91 GIC_ICC_REG_CASE(ICC_IGRPEN1_EL1);
92#undef GIC_ICC_REG_CASE
93 default:
94 break;
95 }
96
97 return "<UNKNOWN>";
98}
99#endif
100
101
102/**
103 * Sets the interrupt pending force-flag and pokes the EMT if required.
104 *
105 * @param pVCpu The cross context virtual CPU structure.
106 * @param fIrq Flag whether to assert the IRQ line or leave it alone.
107 * @param fFiq Flag whether to assert the FIQ line or leave it alone.
108 */
109static void gicSetInterruptFF(PVMCPUCC pVCpu, bool fIrq, bool fFiq)
110{
111 LogFlowFunc(("pVCpu=%p{.idCpu=%u} fIrq=%RTbool fFiq=%RTbool\n",
112 pVCpu, pVCpu->idCpu, fIrq, fFiq));
113
114 Assert(fIrq || fFiq);
115
116#ifdef IN_RING3
117 /* IRQ state should be loaded as-is by "LoadExec". Changes can be made from LoadDone. */
118 Assert(pVCpu->pVMR3->enmVMState != VMSTATE_LOADING || PDMR3HasLoadedState(pVCpu->pVMR3));
119#endif
120
121 if (fIrq)
122 VMCPU_FF_SET(pVCpu, VMCPU_FF_INTERRUPT_IRQ);
123 if (fFiq)
124 VMCPU_FF_SET(pVCpu, VMCPU_FF_INTERRUPT_FIQ);
125
126 /*
127 * We need to wake up the target CPU if we're not on EMT.
128 */
129 /** @todo We could just use RTThreadNativeSelf() here, couldn't we? */
130#if defined(IN_RING0)
131 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
132 VMCPUID idCpu = pVCpu->idCpu;
133 if (VMMGetCpuId(pVM) != idCpu)
134 {
135 switch (VMCPU_GET_STATE(pVCpu))
136 {
137 case VMCPUSTATE_STARTED_EXEC:
138 Log7Func(("idCpu=%u VMCPUSTATE_STARTED_EXEC\n", idCpu));
139 GVMMR0SchedPokeNoGVMNoLock(pVM, idCpu);
140 break;
141
142 case VMCPUSTATE_STARTED_HALTED:
143 Log7Func(("idCpu=%u VMCPUSTATE_STARTED_HALTED\n", idCpu));
144 GVMMR0SchedWakeUpNoGVMNoLock(pVM, idCpu);
145 break;
146
147 default:
148 Log7Func(("idCpu=%u enmState=%d\n", idCpu, pVCpu->enmState));
149 break; /* nothing to do in other states. */
150 }
151 }
152#elif defined(IN_RING3)
153 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
154 VMCPUID idCpu = pVCpu->idCpu;
155 if (VMMGetCpuId(pVM) != idCpu)
156 {
157 Log7Func(("idCpu=%u enmState=%d\n", idCpu, pVCpu->enmState));
158 VMR3NotifyCpuFFU(pVCpu->pUVCpu, VMNOTIFYFF_FLAGS_POKE);
159 }
160#endif
161}
162
163
164/**
165 * Clears the interrupt pending force-flag.
166 *
167 * @param pVCpu The cross context virtual CPU structure.
168 * @param fIrq Flag whether to clear the IRQ flag.
169 * @param fFiq Flag whether to clear the FIQ flag.
170 */
171DECLINLINE(void) gicClearInterruptFF(PVMCPUCC pVCpu, bool fIrq, bool fFiq)
172{
173 LogFlowFunc(("pVCpu=%p{.idCpu=%u} fIrq=%RTbool fFiq=%RTbool\n",
174 pVCpu, pVCpu->idCpu, fIrq, fFiq));
175
176 Assert(fIrq || fFiq);
177
178#ifdef IN_RING3
179 /* IRQ state should be loaded as-is by "LoadExec". Changes can be made from LoadDone. */
180 Assert(pVCpu->pVMR3->enmVMState != VMSTATE_LOADING || PDMR3HasLoadedState(pVCpu->pVMR3));
181#endif
182
183 if (fIrq)
184 VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_IRQ);
185 if (fFiq)
186 VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_FIQ);
187}
188
189
190DECLINLINE(void) gicUpdateInterruptFF(PVMCPUCC pVCpu, bool fIrq, bool fFiq)
191{
192 LogFlowFunc(("pVCpu=%p{.idCpu=%u} fIrq=%RTbool fFiq=%RTbool\n",
193 pVCpu, pVCpu->idCpu, fIrq, fFiq));
194
195 if (fIrq || fFiq)
196 gicSetInterruptFF(pVCpu, fIrq, fFiq);
197
198 if (!fIrq || !fFiq)
199 gicClearInterruptFF(pVCpu, !fIrq, !fFiq);
200}
201
202
203DECLINLINE(void) gicReDistHasIrqPending(PGICCPU pThis, bool *pfIrq, bool *pfFiq)
204{
205 /* Read the interrupt state. */
206 uint32_t u32RegIGrp0 = ASMAtomicReadU32(&pThis->u32RegIGrp0);
207 uint32_t bmIntEnabled = ASMAtomicReadU32(&pThis->bmIntEnabled);
208 uint32_t bmIntPending = ASMAtomicReadU32(&pThis->bmIntPending);
209 uint32_t bmIntActive = ASMAtomicReadU32(&pThis->bmIntActive);
210 bool fIrqGrp0Enabled = ASMAtomicReadBool(&pThis->fIrqGrp0Enabled);
211 bool fIrqGrp1Enabled = ASMAtomicReadBool(&pThis->fIrqGrp1Enabled);
212
213 /* Only allow interrupts with higher priority than the current configured and running one. */
214 uint8_t bPriority = RT_MIN(pThis->bInterruptPriority, pThis->abRunningPriorities[pThis->idxRunningPriority]);
215
216 /* Is anything enabled at all? */
217 uint32_t bmIntForward = (bmIntPending & bmIntEnabled) & ~bmIntActive; /* Exclude the currently active interrupt. */
218 if (bmIntForward)
219 {
220 for (uint32_t i = 0; i < RT_ELEMENTS(pThis->abIntPriority); i++)
221 {
222 Log4(("SGI/PPI %u, configured priority %u, running priority %u\n", i, pThis->abIntPriority[i], bPriority));
223 if ( (bmIntForward & RT_BIT_32(i))
224 && pThis->abIntPriority[i] < bPriority)
225 break;
226 else
227 bmIntForward &= ~RT_BIT_32(i);
228
229 if (!bmIntForward)
230 break;
231 }
232 }
233
234 if (bmIntForward)
235 {
236 /* Determine whether we have to assert the IRQ or FIQ line. */
237 *pfIrq = RT_BOOL(bmIntForward & u32RegIGrp0) && fIrqGrp1Enabled;
238 *pfFiq = RT_BOOL(bmIntForward & ~u32RegIGrp0) && fIrqGrp0Enabled;
239 }
240 else
241 {
242 *pfIrq = false;
243 *pfFiq = false;
244 }
245
246 LogFlowFunc(("pThis=%p bPriority=%u bmIntEnabled=%#x bmIntPending=%#x bmIntActive=%#x fIrq=%RTbool fFiq=%RTbool\n",
247 pThis, bPriority, bmIntEnabled, bmIntPending, bmIntActive, *pfIrq, *pfFiq));
248}
249
250
251DECLINLINE(void) gicDistHasIrqPendingForVCpu(PGICDEV pThis, PGICCPU pGicVCpu, VMCPUID idCpu, bool *pfIrq, bool *pfFiq)
252{
253 /* Read the interrupt state. */
254 uint32_t u32RegIGrp0 = ASMAtomicReadU32(&pThis->u32RegIGrp0);
255 uint32_t bmIntEnabled = ASMAtomicReadU32(&pThis->bmIntEnabled);
256 uint32_t bmIntPending = ASMAtomicReadU32(&pThis->bmIntPending);
257 uint32_t bmIntActive = ASMAtomicReadU32(&pThis->bmIntActive);
258 bool fIrqGrp0Enabled = ASMAtomicReadBool(&pThis->fIrqGrp0Enabled);
259 bool fIrqGrp1Enabled = ASMAtomicReadBool(&pThis->fIrqGrp1Enabled);
260
261 /* Only allow interrupts with higher priority than the current configured and running one. */
262 uint8_t bPriority = RT_MIN(pGicVCpu->bInterruptPriority, pGicVCpu->abRunningPriorities[pGicVCpu->idxRunningPriority]);
263
264 /* Is anything enabled at all? */
265 uint32_t bmIntForward = (bmIntPending & bmIntEnabled) & ~bmIntActive; /* Exclude the currently active interrupt. */
266 if (bmIntForward)
267 {
268 for (uint32_t i = 0; i < RT_ELEMENTS(pThis->abIntPriority); i++)
269 {
270 Log4(("SPI %u, configured priority %u (routing %#x), running priority %u\n", i + GIC_INTID_RANGE_SPI_START, pThis->abIntPriority[i],
271 pThis->au32IntRouting[i], bPriority));
272 if ( (bmIntForward & RT_BIT_32(i))
273 && pThis->abIntPriority[i] < bPriority
274 && pThis->au32IntRouting[i] == idCpu)
275 break;
276 else
277 bmIntForward &= ~RT_BIT_32(i);
278
279 if (!bmIntForward)
280 break;
281 }
282 }
283
284 if (bmIntForward)
285 {
286 /* Determine whether we have to assert the IRQ or FIQ line. */
287 *pfIrq = RT_BOOL(bmIntForward & u32RegIGrp0) && fIrqGrp1Enabled;
288 *pfFiq = RT_BOOL(bmIntForward & ~u32RegIGrp0) && fIrqGrp0Enabled;
289 }
290 else
291 {
292 *pfIrq = false;
293 *pfFiq = false;
294 }
295
296 LogFlowFunc(("pThis=%p bPriority=%u bmIntEnabled=%#x bmIntPending=%#x bmIntActive=%#x fIrq=%RTbool fFiq=%RTbool\n",
297 pThis, bPriority, bmIntEnabled, bmIntPending, bmIntActive, *pfIrq, *pfFiq));
298}
299
300
301/**
302 * Updates the internal IRQ state and sets or clears the appropriate force action
303 * flags.
304 *
305 * @returns Strict VBox status code.
306 * @param pThis The GIC re-distributor state for the associated vCPU.
307 * @param pVCpu The cross context virtual CPU structure.
308 */
309static VBOXSTRICTRC gicReDistUpdateIrqState(PGICCPU pThis, PVMCPUCC pVCpu)
310{
311 bool fIrq, fFiq;
312 gicReDistHasIrqPending(pThis, &fIrq, &fFiq);
313
314 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
315 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
316 bool fIrqDist, fFiqDist;
317 gicDistHasIrqPendingForVCpu(pGicDev, pThis, pVCpu->idCpu, &fIrqDist, &fFiqDist);
318 fIrq |= fIrqDist;
319 fFiq |= fFiqDist;
320
321 gicUpdateInterruptFF(pVCpu, fIrq, fFiq);
322 return VINF_SUCCESS;
323}
324
325
326/**
327 * Updates the internal IRQ state of the distributor and sets or clears the appropirate force action flags.
328 *
329 * @returns Strict VBox status code.
330 * @param pVM The cross context VM state.
331 * @param pThis The GIC distributor state.
332 */
333static VBOXSTRICTRC gicDistUpdateIrqState(PVMCC pVM, PGICDEV pThis)
334{
335 for (uint32_t i = 0; i < pVM->cCpus; i++)
336 {
337 PVMCPUCC pVCpu = pVM->CTX_SUFF(apCpus)[i];
338 PGICCPU pGicVCpu = VMCPU_TO_GICCPU(pVCpu);
339
340 bool fIrq, fFiq;
341 gicReDistHasIrqPending(pGicVCpu, &fIrq, &fFiq);
342
343 bool fIrqDist, fFiqDist;
344 gicDistHasIrqPendingForVCpu(pThis, pGicVCpu, i, &fIrqDist, &fFiqDist);
345 fIrq |= fIrqDist;
346 fFiq |= fFiqDist;
347
348 gicUpdateInterruptFF(pVCpu, fIrq, fFiq);
349 }
350 return VINF_SUCCESS;
351}
352
353
354/**
355 * Sets the given SGI/PPI interrupt ID on the re-distributor of the given vCPU.
356 *
357 * @returns VBox status code.
358 * @param pVCpu The cross context virtual CPU structure.
359 * @param uIntId The SGI/PPI interrupt identifier.
360 * @param fAsserted Flag whether the SGI/PPI interrupt is asserted or not.
361 */
362static int gicReDistInterruptSet(PVMCPUCC pVCpu, uint32_t uIntId, bool fAsserted)
363{
364 PGICCPU pThis = VMCPU_TO_GICCPU(pVCpu);
365
366 /* Update the interrupts pending state. */
367 if (fAsserted)
368 ASMAtomicOrU32(&pThis->bmIntPending, RT_BIT_32(uIntId));
369 else
370 ASMAtomicAndU32(&pThis->bmIntPending, ~RT_BIT_32(uIntId));
371
372 return VBOXSTRICTRC_VAL(gicReDistUpdateIrqState(pThis, pVCpu));
373}
374
375
376/**
377 * Reads a GIC distributor register.
378 *
379 * @returns VBox status code.
380 * @param pDevIns The device instance.
381 * @param pVCpu The cross context virtual CPU structure.
382 * @param offReg The offset of the register being read.
383 * @param puValue Where to store the register value.
384 */
385DECLINLINE(VBOXSTRICTRC) gicDistRegisterRead(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t *puValue)
386{
387 VMCPU_ASSERT_EMT(pVCpu);
388 PGICDEV pThis = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
389
390 if (offReg >= GIC_DIST_REG_IROUTERn_OFF_START && offReg <= GIC_DIST_REG_IROUTERn_OFF_LAST)
391 {
392 uint32_t idxEntry = (offReg - GIC_DIST_REG_IROUTERn_OFF_START) / 4;
393 if (RT_LIKELY(idxEntry < RT_ELEMENTS(pThis->au32IntRouting)))
394 *puValue = pThis->au32IntRouting[idxEntry];
395 else
396 *puValue = 0;
397 return VINF_SUCCESS;
398 }
399
400 switch (offReg)
401 {
402 case GIC_DIST_REG_CTLR_OFF:
403 *puValue = (ASMAtomicReadBool(&pThis->fIrqGrp0Enabled) ? GIC_DIST_REG_CTRL_ENABLE_GRP0 : 0)
404 | (ASMAtomicReadBool(&pThis->fIrqGrp1Enabled) ? GIC_DIST_REG_CTRL_ENABLE_GRP1_NS : 0)
405 | GIC_DIST_REG_CTRL_DS
406 | GIC_DIST_REG_CTRL_ARE_S;
407 break;
408 case GIC_DIST_REG_TYPER_OFF:
409 *puValue = GIC_DIST_REG_TYPER_NUM_ITLINES_SET(pThis->uItLinesNumber)
410 | GIC_DIST_REG_TYPER_NUM_PES_SET(0) /* 1 PE */ /** @todo r=ramshankar: Should this be pVCpu->cCpus? Currently it means 'ARE' must always be used? */
411 /*| GIC_DIST_REG_TYPER_ESPI*/ /** @todo */
412 /*| GIC_DIST_REG_TYPER_NMI*/ /** @todo Non-maskable interrupts */
413 /*| GIC_DIST_REG_TYPER_SECURITY_EXTN */ /** @todo */
414 /*| GIC_DIST_REG_TYPER_MBIS */ /** @todo Message based interrupts */
415 /*| GIC_DIST_REG_TYPER_LPIS */ /** @todo Support LPIs */
416 | GIC_DIST_REG_TYPER_IDBITS_SET(16);
417 break;
418 case GIC_DIST_REG_STATUSR_OFF:
419 AssertReleaseFailed();
420 break;
421 case GIC_DIST_REG_IGROUPRn_OFF_START: /* Only 32 lines for now. */
422 AssertReleaseFailed();
423 break;
424 case GIC_DIST_REG_ISENABLERn_OFF_START + 4: /* Only 32 lines for now. */
425 case GIC_DIST_REG_ICENABLERn_OFF_START + 4: /* Only 32 lines for now. */
426 *puValue = ASMAtomicReadU32(&pThis->bmIntEnabled);
427 break;
428 case GIC_DIST_REG_ISPENDRn_OFF_START: /* Only 32 lines for now. */
429 AssertReleaseFailed();
430 break;
431 case GIC_DIST_REG_ICPENDRn_OFF_START: /* Only 32 lines for now. */
432 AssertReleaseFailed();
433 break;
434 case GIC_DIST_REG_ISACTIVERn_OFF_START: /* Only 32 lines for now. */
435 AssertReleaseFailed();
436 break;
437 case GIC_DIST_REG_ICACTIVERn_OFF_START: /* Only 32 lines for now. */
438 AssertReleaseFailed();
439 break;
440 case GIC_DIST_REG_IPRIORITYRn_OFF_START:
441 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 4: /* These are banked for the PEs and access the redistributor. */
442 {
443 PGICCPU pGicVCpu = VMCPU_TO_GICCPU(pVCpu);
444
445 /* Figure out the register which is written. */
446 uint8_t idxPrio = offReg - GIC_DIST_REG_IPRIORITYRn_OFF_START;
447 Assert(idxPrio <= RT_ELEMENTS(pThis->abIntPriority) - sizeof(uint32_t));
448
449 uint32_t u32Value = 0;
450 for (uint32_t i = idxPrio; i < idxPrio + sizeof(uint32_t); i++)
451 u32Value |= pGicVCpu->abIntPriority[i] << ((i - idxPrio) * 8);
452
453 *puValue = u32Value;
454 break;
455 }
456 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 32: /* Only 32 lines for now. */
457 {
458 /* Figure out the register which is written. */
459 uint8_t idxPrio = offReg - GIC_DIST_REG_IPRIORITYRn_OFF_START - 32;
460 Assert(idxPrio <= RT_ELEMENTS(pThis->abIntPriority) - sizeof(uint32_t));
461
462 uint32_t u32Value = 0;
463 for (uint32_t i = idxPrio; i < idxPrio + sizeof(uint32_t); i++)
464 u32Value |= pThis->abIntPriority[i] << ((i - idxPrio) * 8);
465
466 *puValue = u32Value;
467 break;
468 }
469 case GIC_DIST_REG_ITARGETSRn_OFF_START: /* Only 32 lines for now. */
470 AssertReleaseFailed();
471 break;
472 case GIC_DIST_REG_ICFGRn_OFF_START: /* Only 32 lines for now. */
473 AssertReleaseFailed();
474 break;
475 case GIC_DIST_REG_IGRPMODRn_OFF_START: /* Only 32 lines for now. */
476 AssertReleaseFailed();
477 break;
478 case GIC_DIST_REG_NSACRn_OFF_START: /* Only 32 lines for now. */
479 AssertReleaseFailed();
480 break;
481 case GIC_DIST_REG_SGIR_OFF:
482 AssertReleaseFailed();
483 break;
484 case GIC_DIST_REG_CPENDSGIRn_OFF_START:
485 AssertReleaseFailed();
486 break;
487 case GIC_DIST_REG_SPENDSGIRn_OFF_START:
488 AssertReleaseFailed();
489 break;
490 case GIC_DIST_REG_INMIn_OFF_START:
491 AssertReleaseFailed();
492 break;
493 case GIC_DIST_REG_PIDR2_OFF:
494 Assert(pThis->uArchRev <= GIC_DIST_REG_PIDR2_ARCH_REV_GICV4);
495 *puValue = GIC_DIST_REG_PIDR2_ARCH_REV_SET(pThis->uArchRev);
496 break;
497 case GIC_DIST_REG_IIDR_OFF:
498 case GIC_DIST_REG_TYPER2_OFF:
499 *puValue = 0;
500 break;
501 case GIC_DIST_REG_IROUTERn_OFF_START:
502 AssertFailed();
503 break;
504 default:
505 *puValue = 0;
506 }
507 return VINF_SUCCESS;
508}
509
510
511/**
512 * Writes a GIC distributor register.
513 *
514 * @returns Strict VBox status code.
515 * @param pDevIns The device instance.
516 * @param pVCpu The cross context virtual CPU structure.
517 * @param offReg The offset of the register being written.
518 * @param uValue The register value.
519 */
520DECLINLINE(VBOXSTRICTRC) gicDistRegisterWrite(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t uValue)
521{
522 VMCPU_ASSERT_EMT(pVCpu); RT_NOREF(pVCpu);
523 PGICDEV pThis = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
524 PVMCC pVM = PDMDevHlpGetVM(pDevIns);
525
526 if (offReg >= GIC_DIST_REG_IROUTERn_OFF_START && offReg <= GIC_DIST_REG_IROUTERn_OFF_LAST)
527 {
528 uint32_t idxReg = (offReg - GIC_DIST_REG_IROUTERn_OFF_START) / 4;
529 LogFlowFunc(("GicDist: idxIRouter=%u uValue=%#x\n", idxReg, uValue));
530 if (idxReg < RT_ELEMENTS(pThis->au32IntRouting))
531 pThis->au32IntRouting[idxReg] = uValue;
532 return VINF_SUCCESS;
533 }
534
535 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
536 switch (offReg)
537 {
538 case GIC_DIST_REG_CTLR_OFF:
539 ASMAtomicWriteBool(&pThis->fIrqGrp0Enabled, RT_BOOL(uValue & GIC_DIST_REG_CTRL_ENABLE_GRP0));
540 ASMAtomicWriteBool(&pThis->fIrqGrp1Enabled, RT_BOOL(uValue & GIC_DIST_REG_CTRL_ENABLE_GRP1_NS));
541 Assert(!(uValue & GIC_DIST_REG_CTRL_ARE_NS));
542 rcStrict = gicDistUpdateIrqState(pVM, pThis);
543 break;
544 case GIC_DIST_REG_STATUSR_OFF:
545 AssertReleaseFailed();
546 break;
547 case GIC_DIST_REG_SETSPI_NSR_OFF:
548 AssertReleaseFailed();
549 break;
550 case GIC_DIST_REG_CLRSPI_NSR_OFF:
551 AssertReleaseFailed();
552 break;
553 case GIC_DIST_REG_SETSPI_SR_OFF:
554 AssertReleaseFailed();
555 break;
556 case GIC_DIST_REG_CLRSPI_SR_OFF:
557 AssertReleaseFailed();
558 break;
559 case GIC_DIST_REG_IGROUPRn_OFF_START: /* Only 32 lines for now. */
560 AssertReleaseFailed();
561 break;
562 case GIC_DIST_REG_IGROUPRn_OFF_START + 4: /* Only 32 lines for now. */
563 ASMAtomicOrU32(&pThis->u32RegIGrp0, uValue);
564 rcStrict = gicDistUpdateIrqState(pVM, pThis);
565 break;
566 case GIC_DIST_REG_ISENABLERn_OFF_START + 4: /* Only 32 lines for now. */
567 ASMAtomicOrU32(&pThis->bmIntEnabled, uValue);
568 rcStrict = gicDistUpdateIrqState(pVM, pThis);
569 break;
570 case GIC_DIST_REG_ICENABLERn_OFF_START:
571 AssertReleaseFailed();
572 break;
573 case GIC_DIST_REG_ICENABLERn_OFF_START + 4: /* Only 32 lines for now. */
574 ASMAtomicAndU32(&pThis->bmIntEnabled, ~uValue);
575 rcStrict = gicDistUpdateIrqState(pVM, pThis);
576 break;
577 case GIC_DIST_REG_ISPENDRn_OFF_START: /* Only 32 lines for now. */
578 AssertReleaseFailed();
579 break;
580 case GIC_DIST_REG_ICPENDRn_OFF_START: /* Only 32 lines for now. */
581 AssertReleaseFailed();
582 break;
583 case GIC_DIST_REG_ISACTIVERn_OFF_START: /* Only 32 lines for now. */
584 AssertReleaseFailed();
585 break;
586 case GIC_DIST_REG_ICACTIVERn_OFF_START + 4: /* Only 32 lines for now. */
587 ASMAtomicAndU32(&pThis->bmIntActive, ~uValue);
588 rcStrict = gicDistUpdateIrqState(pVM, pThis);
589 break;
590 case GIC_DIST_REG_IPRIORITYRn_OFF_START: /* These are banked for the PEs and access the redistributor. */
591 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 4:
592 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 8:
593 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 12:
594 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 16:
595 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 20:
596 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 24:
597 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 28:
598 {
599 PGICCPU pGicVCpu = VMCPU_TO_GICCPU(pVCpu);
600
601 /* Figure out the register which is written. */
602 uint8_t idxPrio = offReg - GIC_DIST_REG_IPRIORITYRn_OFF_START;
603 Assert(idxPrio <= RT_ELEMENTS(pGicVCpu->abIntPriority) - sizeof(uint32_t));
604 for (uint32_t i = idxPrio; i < idxPrio + sizeof(uint32_t); i++)
605 {
606 pGicVCpu->abIntPriority[i] = (uint8_t)(uValue & 0xff);
607 uValue >>= 8;
608 }
609 break;
610 }
611 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 32: /* Only 32 lines for now. */
612 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 36:
613 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 40:
614 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 44:
615 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 48:
616 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 52:
617 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 56:
618 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 60:
619 {
620 /* Figure out the register which is written. */
621 uint8_t idxPrio = offReg - GIC_DIST_REG_IPRIORITYRn_OFF_START - 32;
622 Assert(idxPrio <= RT_ELEMENTS(pThis->abIntPriority) - sizeof(uint32_t));
623 for (uint32_t i = idxPrio; i < idxPrio + sizeof(uint32_t); i++)
624 {
625#if 1
626 /** @todo r=aeichner This gross hack prevents Windows from hanging during boot because
627 * it tries to set the interrupt priority for PCI interrupt lines to 0 which will cause an interrupt
628 * storm later on because the lowest interrupt priority Windows seems to use is 32 for the per vCPU
629 * timer.
630 */
631 if ((uValue & 0xff) == 0)
632 {
633 uValue >>= 8;
634 continue;
635 }
636#endif
637 pThis->abIntPriority[i] = (uint8_t)(uValue & 0xff);
638 uValue >>= 8;
639 }
640 break;
641 }
642 case GIC_DIST_REG_ITARGETSRn_OFF_START: /* Only 32 lines for now. */
643 AssertReleaseFailed();
644 break;
645 case GIC_DIST_REG_ICFGRn_OFF_START + 8: /* Only 32 lines for now. */
646 ASMAtomicWriteU32(&pThis->u32RegICfg0, uValue);
647 break;
648 case GIC_DIST_REG_ICFGRn_OFF_START+ 12:
649 ASMAtomicWriteU32(&pThis->u32RegICfg1, uValue);
650 break;
651 case GIC_DIST_REG_IGRPMODRn_OFF_START: /* Only 32 lines for now. */
652 AssertReleaseFailed();
653 break;
654 case GIC_DIST_REG_NSACRn_OFF_START: /* Only 32 lines for now. */
655 AssertReleaseFailed();
656 break;
657 case GIC_DIST_REG_SGIR_OFF:
658 AssertReleaseFailed();
659 break;
660 case GIC_DIST_REG_CPENDSGIRn_OFF_START:
661 AssertReleaseFailed();
662 break;
663 case GIC_DIST_REG_SPENDSGIRn_OFF_START:
664 AssertReleaseFailed();
665 break;
666 case GIC_DIST_REG_INMIn_OFF_START:
667 AssertReleaseFailed();
668 break;
669 default:
670 //AssertReleaseFailed();
671 break;
672 }
673
674 return rcStrict;
675}
676
677
678/**
679 * Reads a GIC redistributor register.
680 *
681 * @returns VBox status code.
682 * @param pDevIns The device instance.
683 * @param pVCpu The cross context virtual CPU structure.
684 * @param idRedist The redistributor ID.
685 * @param offReg The offset of the register being read.
686 * @param puValue Where to store the register value.
687 */
688DECLINLINE(VBOXSTRICTRC) gicReDistRegisterRead(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint32_t idRedist, uint16_t offReg, uint32_t *puValue)
689{
690 PGICDEV pThis = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
691
692 switch (offReg)
693 {
694 case GIC_REDIST_REG_TYPER_OFF:
695 {
696 PVMCC pVM = PDMDevHlpGetVM(pDevIns);
697 *puValue = ((pVCpu->idCpu == pVM->cCpus - 1) ? GIC_REDIST_REG_TYPER_LAST : 0)
698 | GIC_REDIST_REG_TYPER_CPU_NUMBER_SET(idRedist)
699 | GIC_REDIST_REG_TYPER_CMN_LPI_AFF_SET(GIC_REDIST_REG_TYPER_CMN_LPI_AFF_ALL);
700 break;
701 }
702 case GIC_REDIST_REG_TYPER_AFFINITY_OFF:
703 *puValue = idRedist;
704 break;
705 case GIC_REDIST_REG_PIDR2_OFF:
706 Assert(pThis->uArchRev <= GIC_DIST_REG_PIDR2_ARCH_REV_GICV4);
707 *puValue = GIC_REDIST_REG_PIDR2_ARCH_REV_SET(pThis->uArchRev);
708 break;
709 default:
710 *puValue = 0;
711 }
712
713 return VINF_SUCCESS;
714}
715
716
717/**
718 * Reads a GIC redistributor SGI/PPI frame register.
719 *
720 * @returns VBox status code.
721 * @param pDevIns The device instance.
722 * @param pVCpu The cross context virtual CPU structure.
723 * @param offReg The offset of the register being read.
724 * @param puValue Where to store the register value.
725 */
726DECLINLINE(VBOXSTRICTRC) gicReDistSgiPpiRegisterRead(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t *puValue)
727{
728 VMCPU_ASSERT_EMT(pVCpu);
729 RT_NOREF(pDevIns);
730
731 PGICCPU pThis = VMCPU_TO_GICCPU(pVCpu);
732 switch (offReg)
733 {
734 case GIC_REDIST_SGI_PPI_REG_ISENABLER0_OFF:
735 case GIC_REDIST_SGI_PPI_REG_ICENABLER0_OFF:
736 *puValue = ASMAtomicReadU32(&pThis->bmIntEnabled);
737 break;
738 case GIC_REDIST_SGI_PPI_REG_ISPENDR0_OFF:
739 case GIC_REDIST_SGI_PPI_REG_ICPENDR0_OFF:
740 *puValue = ASMAtomicReadU32(&pThis->bmIntPending);
741 break;
742 case GIC_REDIST_SGI_PPI_REG_ISACTIVER0_OFF:
743 case GIC_REDIST_SGI_PPI_REG_ICACTIVER0_OFF:
744 *puValue = ASMAtomicReadU32(&pThis->bmIntActive);
745 break;
746 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START:
747 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 4:
748 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 8:
749 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 12:
750 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 16:
751 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 20:
752 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 24:
753 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 28:
754 {
755 /* Figure out the register which is written. */
756 uint8_t idxPrio = offReg - GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START;
757 Assert(idxPrio <= RT_ELEMENTS(pThis->abIntPriority) - sizeof(uint32_t));
758
759 uint32_t u32Value = 0;
760 for (uint32_t i = idxPrio; i < idxPrio + sizeof(uint32_t); i++)
761 u32Value |= pThis->abIntPriority[i] << ((i - idxPrio) * 8);
762
763 *puValue = u32Value;
764 break;
765 }
766 case GIC_REDIST_SGI_PPI_REG_ICFGR0_OFF:
767 *puValue = ASMAtomicReadU32(&pThis->u32RegICfg0);
768 break;
769 case GIC_REDIST_SGI_PPI_REG_ICFGR1_OFF:
770 *puValue = ASMAtomicReadU32(&pThis->u32RegICfg1);
771 break;
772 default:
773 AssertReleaseFailed();
774 *puValue = 0;
775 }
776
777 return VINF_SUCCESS;
778}
779
780
781/**
782 * Writes a GIC redistributor frame register.
783 *
784 * @returns Strict VBox status code.
785 * @param pDevIns The device instance.
786 * @param pVCpu The cross context virtual CPU structure.
787 * @param offReg The offset of the register being written.
788 * @param uValue The register value.
789 */
790DECLINLINE(VBOXSTRICTRC) gicReDistRegisterWrite(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t uValue)
791{
792 VMCPU_ASSERT_EMT(pVCpu);
793 RT_NOREF(pDevIns, pVCpu, uValue);
794
795 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
796 switch (offReg)
797 {
798 case GIC_REDIST_REG_STATUSR_OFF:
799 AssertReleaseFailed();
800 break;
801 case GIC_REDIST_REG_WAKER_OFF:
802 Assert(uValue == 0);
803 break;
804 case GIC_REDIST_REG_PARTIDR_OFF:
805 AssertReleaseFailed();
806 break;
807 case GIC_REDIST_REG_SETLPIR_OFF:
808 AssertReleaseFailed();
809 break;
810 case GIC_REDIST_REG_CLRLPIR_OFF:
811 AssertReleaseFailed();
812 break;
813 case GIC_REDIST_REG_PROPBASER_OFF:
814 AssertReleaseFailed();
815 break;
816 case GIC_REDIST_REG_PENDBASER_OFF:
817 AssertReleaseFailed();
818 break;
819 case GIC_REDIST_REG_INVLPIR_OFF:
820 AssertReleaseFailed();
821 break;
822 case GIC_REDIST_REG_INVALLR_OFF:
823 AssertReleaseFailed();
824 break;
825 default:
826 AssertReleaseFailed();
827 break;
828 }
829
830 return rcStrict;
831}
832
833
834/**
835 * Writes a GIC redistributor SGI/PPI frame register.
836 *
837 * @returns Strict VBox status code.
838 * @param pDevIns The device instance.
839 * @param pVCpu The cross context virtual CPU structure.
840 * @param offReg The offset of the register being written.
841 * @param uValue The register value.
842 */
843DECLINLINE(VBOXSTRICTRC) gicReDistSgiPpiRegisterWrite(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t uValue)
844{
845 VMCPU_ASSERT_EMT(pVCpu);
846 RT_NOREF(pDevIns);
847
848 PGICCPU pThis = VMCPU_TO_GICCPU(pVCpu);
849 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
850 switch (offReg)
851 {
852 case GIC_REDIST_SGI_PPI_REG_IGROUPR0_OFF:
853 ASMAtomicOrU32(&pThis->u32RegIGrp0, uValue);
854 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
855 break;
856 case GIC_REDIST_SGI_PPI_REG_ISENABLER0_OFF:
857 ASMAtomicOrU32(&pThis->bmIntEnabled, uValue);
858 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
859 break;
860 case GIC_REDIST_SGI_PPI_REG_ICENABLER0_OFF:
861 ASMAtomicAndU32(&pThis->bmIntEnabled, ~uValue);
862 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
863 break;
864 case GIC_REDIST_SGI_PPI_REG_ISPENDR0_OFF:
865 ASMAtomicOrU32(&pThis->bmIntPending, uValue);
866 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
867 break;
868 case GIC_REDIST_SGI_PPI_REG_ICPENDR0_OFF:
869 ASMAtomicAndU32(&pThis->bmIntPending, ~uValue);
870 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
871 break;
872 case GIC_REDIST_SGI_PPI_REG_ISACTIVER0_OFF:
873 ASMAtomicOrU32(&pThis->bmIntActive, uValue);
874 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
875 break;
876 case GIC_REDIST_SGI_PPI_REG_ICACTIVER0_OFF:
877 ASMAtomicAndU32(&pThis->bmIntActive, ~uValue);
878 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
879 break;
880 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START:
881 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 4:
882 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 8:
883 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 12:
884 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 16:
885 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 20:
886 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 24:
887 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 28:
888 {
889 /* Figure out the register which is written. */
890 uint8_t idxPrio = offReg - GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START;
891 Assert(idxPrio <= RT_ELEMENTS(pThis->abIntPriority) - sizeof(uint32_t));
892 for (uint32_t i = idxPrio; i < idxPrio + sizeof(uint32_t); i++)
893 {
894 pThis->abIntPriority[i] = (uint8_t)(uValue & 0xff);
895 uValue >>= 8;
896 }
897 break;
898 }
899 case GIC_REDIST_SGI_PPI_REG_ICFGR0_OFF:
900 ASMAtomicWriteU32(&pThis->u32RegICfg0, uValue);
901 break;
902 case GIC_REDIST_SGI_PPI_REG_ICFGR1_OFF:
903 ASMAtomicWriteU32(&pThis->u32RegICfg1, uValue);
904 break;
905 default:
906 //AssertReleaseFailed();
907 break;
908 }
909
910 return rcStrict;
911}
912
913
914/**
915 * @interface_method_impl{PDMGICBACKEND,pfnSetSpi}
916 */
917static DECLCALLBACK(int) gicSetSpi(PVMCC pVM, uint32_t uIntId, bool fAsserted)
918{
919 LogFlowFunc(("pVM=%p uIntId=%u fAsserted=%RTbool\n",
920 pVM, uIntId, fAsserted));
921
922 AssertReturn(uIntId < GIC_SPI_MAX, VERR_INVALID_PARAMETER);
923
924 PGIC pGic = VM_TO_GIC(pVM);
925 PPDMDEVINS pDevIns = pGic->CTX_SUFF(pDevIns);
926 PGICDEV pThis = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
927
928 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
929 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
930
931 /* Update the interrupts pending state. */
932 if (fAsserted)
933 ASMAtomicOrU32(&pThis->bmIntPending, RT_BIT_32(uIntId));
934 else
935 ASMAtomicAndU32(&pThis->bmIntPending, ~RT_BIT_32(uIntId));
936
937 int rc = VBOXSTRICTRC_VAL(gicDistUpdateIrqState(pVM, pThis));
938 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
939 return rc;
940}
941
942
943/**
944 * @interface_method_impl{PDMGICBACKEND,pfnSetPpi}
945 */
946static DECLCALLBACK(int) gicSetPpi(PVMCPUCC pVCpu, uint32_t uIntId, bool fAsserted)
947{
948 LogFlowFunc(("pVCpu=%p{.idCpu=%u} uIntId=%u fAsserted=%RTbool\n",
949 pVCpu, pVCpu->idCpu, uIntId, fAsserted));
950
951 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
952 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
953 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
954
955 AssertReturn(uIntId <= (GIC_INTID_RANGE_PPI_LAST - GIC_INTID_RANGE_PPI_START), VERR_INVALID_PARAMETER);
956 int rc = gicReDistInterruptSet(pVCpu, uIntId + GIC_INTID_RANGE_PPI_START, fAsserted);
957 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
958
959 return rc;
960}
961
962
963/**
964 * Sets the specified software generated interrupt starting.
965 *
966 * @returns VBox status code.
967 * @param pVCpu The cross context virtual CPU structure.
968 * @param uIntId The PPI ID (minus GIC_INTID_RANGE_SGI_START) to assert/de-assert.
969 * @param fAsserted Flag whether to mark the interrupt as asserted/de-asserted.
970 */
971static int gicSetSgi(PVMCPUCC pVCpu, uint32_t uIntId, bool fAsserted)
972{
973 LogFlowFunc(("pVCpu=%p{.idCpu=%u} uIntId=%u fAsserted=%RTbool\n",
974 pVCpu, pVCpu->idCpu, uIntId, fAsserted));
975
976 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
977 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
978 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
979
980 AssertReturn(uIntId <= (GIC_INTID_RANGE_SGI_LAST - GIC_INTID_RANGE_SGI_START), VERR_INVALID_PARAMETER);
981 int rc = gicReDistInterruptSet(pVCpu, uIntId + GIC_INTID_RANGE_SGI_START, fAsserted);
982 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
983
984 return rc;
985}
986
987
988/**
989 * @interface_method_impl{PDMGICBACKEND,pfnReadSysReg}
990 */
991static DECLCALLBACK(VBOXSTRICTRC) gicReadSysReg(PVMCPUCC pVCpu, uint32_t u32Reg, uint64_t *pu64Value)
992{
993 /*
994 * Validate.
995 */
996 VMCPU_ASSERT_EMT(pVCpu);
997 Assert(pu64Value);
998
999 *pu64Value = 0;
1000 PGICCPU pThis = VMCPU_TO_GICCPU(pVCpu);
1001 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
1002 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
1003
1004 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
1005 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
1006
1007 switch (u32Reg)
1008 {
1009 case ARMV8_AARCH64_SYSREG_ICC_PMR_EL1:
1010 *pu64Value = pThis->bInterruptPriority;
1011 break;
1012 case ARMV8_AARCH64_SYSREG_ICC_IAR0_EL1:
1013 AssertReleaseFailed();
1014 break;
1015 case ARMV8_AARCH64_SYSREG_ICC_EOIR0_EL1:
1016 AssertReleaseFailed();
1017 break;
1018 case ARMV8_AARCH64_SYSREG_ICC_HPPIR0_EL1:
1019 AssertReleaseFailed();
1020 break;
1021 case ARMV8_AARCH64_SYSREG_ICC_BPR0_EL1:
1022 *pu64Value = pThis->bBinaryPointGrp0 & 0x7;
1023 break;
1024 case ARMV8_AARCH64_SYSREG_ICC_AP0R0_EL1:
1025 AssertReleaseFailed();
1026 break;
1027 case ARMV8_AARCH64_SYSREG_ICC_AP0R1_EL1:
1028 AssertReleaseFailed();
1029 break;
1030 case ARMV8_AARCH64_SYSREG_ICC_AP0R2_EL1:
1031 AssertReleaseFailed();
1032 break;
1033 case ARMV8_AARCH64_SYSREG_ICC_AP0R3_EL1:
1034 AssertReleaseFailed();
1035 break;
1036 case ARMV8_AARCH64_SYSREG_ICC_AP1R0_EL1:
1037 AssertReleaseFailed();
1038 break;
1039 case ARMV8_AARCH64_SYSREG_ICC_AP1R1_EL1:
1040 AssertReleaseFailed();
1041 break;
1042 case ARMV8_AARCH64_SYSREG_ICC_AP1R2_EL1:
1043 AssertReleaseFailed();
1044 break;
1045 case ARMV8_AARCH64_SYSREG_ICC_AP1R3_EL1:
1046 AssertReleaseFailed();
1047 break;
1048 case ARMV8_AARCH64_SYSREG_ICC_NMIAR1_EL1:
1049 AssertReleaseFailed();
1050 break;
1051 case ARMV8_AARCH64_SYSREG_ICC_DIR_EL1:
1052 AssertReleaseFailed();
1053 break;
1054 case ARMV8_AARCH64_SYSREG_ICC_RPR_EL1:
1055 *pu64Value = pThis->abRunningPriorities[pThis->idxRunningPriority];
1056 break;
1057 case ARMV8_AARCH64_SYSREG_ICC_SGI1R_EL1:
1058 AssertReleaseFailed();
1059 break;
1060 case ARMV8_AARCH64_SYSREG_ICC_ASGI1R_EL1:
1061 AssertReleaseFailed();
1062 break;
1063 case ARMV8_AARCH64_SYSREG_ICC_SGI0R_EL1:
1064 AssertReleaseFailed();
1065 break;
1066 case ARMV8_AARCH64_SYSREG_ICC_IAR1_EL1:
1067 {
1068 /** @todo Figure out the highest priority interrupt. */
1069 uint32_t bmIntActive = ASMAtomicReadU32(&pThis->bmIntActive);
1070 uint32_t bmIntEnabled = ASMAtomicReadU32(&pThis->bmIntEnabled);
1071 uint32_t bmPending = (ASMAtomicReadU32(&pThis->bmIntPending) & bmIntEnabled) & ~bmIntActive;
1072 int32_t idxIntPending = ASMBitFirstSet(&bmPending, sizeof(bmPending) * 8);
1073 if (idxIntPending > -1)
1074 {
1075 /* Mark the interrupt as active. */
1076 ASMAtomicOrU32(&pThis->bmIntActive, RT_BIT_32(idxIntPending));
1077 /* Drop priority. */
1078 Assert((uint32_t)idxIntPending < RT_ELEMENTS(pThis->abIntPriority));
1079 Assert(pThis->idxRunningPriority < RT_ELEMENTS(pThis->abRunningPriorities) - 1);
1080
1081 LogFlowFunc(("Dropping interrupt priority from %u -> %u (idxRunningPriority: %u -> %u)\n",
1082 pThis->abRunningPriorities[pThis->idxRunningPriority],
1083 pThis->abIntPriority[idxIntPending],
1084 pThis->idxRunningPriority, pThis->idxRunningPriority + 1));
1085
1086 pThis->abRunningPriorities[++pThis->idxRunningPriority] = pThis->abIntPriority[idxIntPending];
1087
1088 /* Clear edge level interrupts like SGIs as pending. */
1089 if (idxIntPending <= GIC_INTID_RANGE_SGI_LAST)
1090 ASMAtomicBitClear(&pThis->bmIntPending, idxIntPending);
1091 *pu64Value = idxIntPending;
1092 gicReDistUpdateIrqState(pThis, pVCpu);
1093 }
1094 else
1095 {
1096 /** @todo This is wrong as the guest might decide to prioritize PPIs and SPIs differently. */
1097 bmIntActive = ASMAtomicReadU32(&pGicDev->bmIntActive);
1098 bmIntEnabled = ASMAtomicReadU32(&pGicDev->bmIntEnabled);
1099 bmPending = (ASMAtomicReadU32(&pGicDev->bmIntPending) & bmIntEnabled) & ~bmIntActive;
1100 idxIntPending = ASMBitFirstSet(&bmPending, sizeof(bmPending) * 8);
1101 if ( idxIntPending > -1
1102 && pGicDev->abIntPriority[idxIntPending] < pThis->bInterruptPriority)
1103 {
1104 /* Mark the interrupt as active. */
1105 ASMAtomicOrU32(&pGicDev->bmIntActive, RT_BIT_32(idxIntPending));
1106
1107 /* Drop priority. */
1108 Assert((uint32_t)idxIntPending < RT_ELEMENTS(pGicDev->abIntPriority));
1109 Assert(pThis->idxRunningPriority < RT_ELEMENTS(pThis->abRunningPriorities) - 1);
1110
1111 LogFlowFunc(("Dropping interrupt priority from %u -> %u (idxRunningPriority: %u -> %u)\n",
1112 pThis->abRunningPriorities[pThis->idxRunningPriority],
1113 pThis->abIntPriority[idxIntPending],
1114 pThis->idxRunningPriority, pThis->idxRunningPriority + 1));
1115
1116 pThis->abRunningPriorities[++pThis->idxRunningPriority] = pGicDev->abIntPriority[idxIntPending];
1117
1118 *pu64Value = idxIntPending + GIC_INTID_RANGE_SPI_START;
1119 gicReDistUpdateIrqState(pThis, pVCpu);
1120 }
1121 else
1122 *pu64Value = GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT;
1123 }
1124 break;
1125 }
1126 case ARMV8_AARCH64_SYSREG_ICC_EOIR1_EL1:
1127 AssertReleaseFailed();
1128 break;
1129 case ARMV8_AARCH64_SYSREG_ICC_HPPIR1_EL1:
1130 {
1131 /** @todo Figure out the highest priority interrupt. */
1132 uint32_t bmIntActive = ASMAtomicReadU32(&pThis->bmIntActive);
1133 uint32_t bmIntEnabled = ASMAtomicReadU32(&pThis->bmIntEnabled);
1134 uint32_t bmPending = (ASMAtomicReadU32(&pThis->bmIntPending) & bmIntEnabled) & ~bmIntActive;
1135 int32_t idxIntPending = ASMBitFirstSet(&bmPending, sizeof(bmPending) * 8);
1136 if (idxIntPending > -1)
1137 *pu64Value = idxIntPending;
1138 else
1139 {
1140 /** @todo This is wrong as the guest might decide to prioritize PPIs and SPIs differently. */
1141 bmIntActive = ASMAtomicReadU32(&pGicDev->bmIntActive);
1142 bmIntEnabled = ASMAtomicReadU32(&pGicDev->bmIntEnabled);
1143 bmPending = (ASMAtomicReadU32(&pGicDev->bmIntPending) & bmIntEnabled) & ~bmIntActive;
1144 idxIntPending = ASMBitFirstSet(&bmPending, sizeof(bmPending) * 8);
1145 if (idxIntPending > -1)
1146 *pu64Value = idxIntPending + GIC_INTID_RANGE_SPI_START;
1147 else
1148 *pu64Value = GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT;
1149 }
1150 break;
1151 }
1152 case ARMV8_AARCH64_SYSREG_ICC_BPR1_EL1:
1153 *pu64Value = ARMV8_ICC_BPR1_EL1_AARCH64_BINARYPOINT_SET(pThis->bBinaryPointGrp1);
1154 break;
1155 case ARMV8_AARCH64_SYSREG_ICC_CTLR_EL1:
1156 *pu64Value = ARMV8_ICC_CTLR_EL1_AARCH64_PMHE
1157 | ARMV8_ICC_CTLR_EL1_AARCH64_PRIBITS_SET(4)
1158 | ARMV8_ICC_CTLR_EL1_AARCH64_IDBITS_SET(ARMV8_ICC_CTLR_EL1_AARCH64_IDBITS_16BITS);
1159 break;
1160 case ARMV8_AARCH64_SYSREG_ICC_SRE_EL1:
1161 AssertReleaseFailed();
1162 break;
1163 case ARMV8_AARCH64_SYSREG_ICC_IGRPEN0_EL1:
1164 *pu64Value = ASMAtomicReadBool(&pThis->fIrqGrp0Enabled) ? ARMV8_ICC_IGRPEN0_EL1_AARCH64_ENABLE : 0;
1165 break;
1166 case ARMV8_AARCH64_SYSREG_ICC_IGRPEN1_EL1:
1167 *pu64Value = ASMAtomicReadBool(&pThis->fIrqGrp1Enabled) ? ARMV8_ICC_IGRPEN1_EL1_AARCH64_ENABLE : 0;
1168 break;
1169 default:
1170 AssertReleaseFailed();
1171 break;
1172 }
1173
1174 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
1175
1176 LogFlowFunc(("pVCpu=%p u32Reg=%#x{%s} pu64Value=%RX64\n", pVCpu, u32Reg, gicIccRegisterStringify(u32Reg), *pu64Value));
1177 return VINF_SUCCESS;
1178}
1179
1180
1181/**
1182 * @interface_method_impl{PDMGICBACKEND,pfnWriteSysReg}
1183 */
1184static DECLCALLBACK(VBOXSTRICTRC) gicWriteSysReg(PVMCPUCC pVCpu, uint32_t u32Reg, uint64_t u64Value)
1185{
1186 /*
1187 * Validate.
1188 */
1189 VMCPU_ASSERT_EMT(pVCpu);
1190 LogFlowFunc(("pVCpu=%p u32Reg=%#x{%s} u64Value=%RX64\n", pVCpu, u32Reg, gicIccRegisterStringify(u32Reg), u64Value));
1191
1192 PGICCPU pThis = VMCPU_TO_GICCPU(pVCpu);
1193 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
1194 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
1195
1196 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
1197 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
1198
1199 switch (u32Reg)
1200 {
1201 case ARMV8_AARCH64_SYSREG_ICC_PMR_EL1:
1202 LogFlowFunc(("ICC_PMR_EL1: Interrupt priority now %u\n", (uint8_t)u64Value));
1203 ASMAtomicWriteU8(&pThis->bInterruptPriority, (uint8_t)u64Value);
1204 gicReDistUpdateIrqState(pThis, pVCpu);
1205 break;
1206 case ARMV8_AARCH64_SYSREG_ICC_IAR0_EL1:
1207 AssertReleaseFailed();
1208 break;
1209 case ARMV8_AARCH64_SYSREG_ICC_EOIR0_EL1:
1210 AssertReleaseFailed();
1211 break;
1212 case ARMV8_AARCH64_SYSREG_ICC_HPPIR0_EL1:
1213 AssertReleaseFailed();
1214 break;
1215 case ARMV8_AARCH64_SYSREG_ICC_BPR0_EL1:
1216 pThis->bBinaryPointGrp0 = (uint8_t)ARMV8_ICC_BPR0_EL1_AARCH64_BINARYPOINT_GET(u64Value);
1217 break;
1218 case ARMV8_AARCH64_SYSREG_ICC_AP0R0_EL1:
1219 /** @todo */
1220 break;
1221 case ARMV8_AARCH64_SYSREG_ICC_AP0R1_EL1:
1222 AssertReleaseFailed();
1223 break;
1224 case ARMV8_AARCH64_SYSREG_ICC_AP0R2_EL1:
1225 AssertReleaseFailed();
1226 break;
1227 case ARMV8_AARCH64_SYSREG_ICC_AP0R3_EL1:
1228 AssertReleaseFailed();
1229 break;
1230 case ARMV8_AARCH64_SYSREG_ICC_AP1R0_EL1:
1231 /** @todo */
1232 break;
1233 case ARMV8_AARCH64_SYSREG_ICC_AP1R1_EL1:
1234 AssertReleaseFailed();
1235 break;
1236 case ARMV8_AARCH64_SYSREG_ICC_AP1R2_EL1:
1237 AssertReleaseFailed();
1238 break;
1239 case ARMV8_AARCH64_SYSREG_ICC_AP1R3_EL1:
1240 AssertReleaseFailed();
1241 break;
1242 case ARMV8_AARCH64_SYSREG_ICC_NMIAR1_EL1:
1243 AssertReleaseFailed();
1244 break;
1245 case ARMV8_AARCH64_SYSREG_ICC_DIR_EL1:
1246 AssertReleaseFailed();
1247 break;
1248 case ARMV8_AARCH64_SYSREG_ICC_RPR_EL1:
1249 AssertReleaseFailed();
1250 break;
1251 case ARMV8_AARCH64_SYSREG_ICC_SGI1R_EL1:
1252 {
1253 uint32_t uIntId = ARMV8_ICC_SGI1R_EL1_AARCH64_INTID_GET(u64Value) - GIC_INTID_RANGE_SGI_START;
1254 if (u64Value & ARMV8_ICC_SGI1R_EL1_AARCH64_IRM)
1255 {
1256 /* Route to all but this vCPU. */
1257 for (uint32_t i = 0; i < pVCpu->pVMR3->cCpus; i++)
1258 {
1259 if (i != pVCpu->idCpu)
1260 {
1261 PVMCPUCC pVCpuDst = VMMGetCpuById(pVCpu->CTX_SUFF(pVM), i);
1262 if (pVCpuDst)
1263 gicSetSgi(pVCpuDst, uIntId, true /*fAsserted*/);
1264 else
1265 AssertFailed();
1266 }
1267 }
1268 }
1269 else
1270 {
1271 /* Examine target list. */
1272 /** @todo Range selector support. */
1273 VMCPUID idCpu = 0;
1274 uint16_t uTgtList = ARMV8_ICC_SGI1R_EL1_AARCH64_TARGET_LIST_GET(u64Value);
1275 /** @todo rewrite using ASMBitFirstSetU16. */
1276 while (uTgtList)
1277 {
1278 if (uTgtList & 0x1)
1279 {
1280 PVMCPUCC pVCpuDst = VMMGetCpuById(pVCpu->CTX_SUFF(pVM), idCpu);
1281 if (pVCpuDst)
1282 gicSetSgi(pVCpuDst, uIntId, true /*fAsserted*/);
1283 else
1284 AssertFailed();
1285 }
1286 uTgtList >>= 1;
1287 idCpu++;
1288 }
1289 }
1290 break;
1291 }
1292 case ARMV8_AARCH64_SYSREG_ICC_ASGI1R_EL1:
1293 AssertReleaseFailed();
1294 break;
1295 case ARMV8_AARCH64_SYSREG_ICC_SGI0R_EL1:
1296 AssertReleaseFailed();
1297 break;
1298 case ARMV8_AARCH64_SYSREG_ICC_IAR1_EL1:
1299 AssertReleaseFailed();
1300 break;
1301 case ARMV8_AARCH64_SYSREG_ICC_EOIR1_EL1:
1302 {
1303 /* Mark the interrupt as not active anymore, though it might still be pending. */
1304 if (u64Value < GIC_INTID_RANGE_SPI_START)
1305 ASMAtomicAndU32(&pThis->bmIntActive, ~RT_BIT_32((uint32_t)u64Value));
1306 else
1307 ASMAtomicAndU32(&pGicDev->bmIntActive, ~RT_BIT_32((uint32_t)(u64Value - GIC_INTID_RANGE_SPI_START)));
1308
1309 /* Restore previous interrupt priority. */
1310 Assert(pThis->idxRunningPriority > 0);
1311 if (RT_LIKELY(pThis->idxRunningPriority))
1312 {
1313 LogFlowFunc(("Restoring interrupt priority from %u -> %u (idxRunningPriority: %u -> %u)\n",
1314 pThis->abRunningPriorities[pThis->idxRunningPriority],
1315 pThis->abRunningPriorities[pThis->idxRunningPriority - 1],
1316 pThis->idxRunningPriority, pThis->idxRunningPriority - 1));
1317 pThis->idxRunningPriority--;
1318 }
1319 gicReDistUpdateIrqState(pThis, pVCpu);
1320 break;
1321 }
1322 case ARMV8_AARCH64_SYSREG_ICC_HPPIR1_EL1:
1323 AssertReleaseFailed();
1324 break;
1325 case ARMV8_AARCH64_SYSREG_ICC_BPR1_EL1:
1326 pThis->bBinaryPointGrp1 = (uint8_t)ARMV8_ICC_BPR1_EL1_AARCH64_BINARYPOINT_GET(u64Value);
1327 break;
1328 case ARMV8_AARCH64_SYSREG_ICC_CTLR_EL1:
1329 u64Value &= ARMV8_ICC_CTLR_EL1_RW;
1330 /** @todo */
1331 break;
1332 case ARMV8_AARCH64_SYSREG_ICC_SRE_EL1:
1333 AssertReleaseFailed();
1334 break;
1335 case ARMV8_AARCH64_SYSREG_ICC_IGRPEN0_EL1:
1336 ASMAtomicWriteBool(&pThis->fIrqGrp0Enabled, RT_BOOL(u64Value & ARMV8_ICC_IGRPEN0_EL1_AARCH64_ENABLE));
1337 break;
1338 case ARMV8_AARCH64_SYSREG_ICC_IGRPEN1_EL1:
1339 ASMAtomicWriteBool(&pThis->fIrqGrp1Enabled, RT_BOOL(u64Value & ARMV8_ICC_IGRPEN1_EL1_AARCH64_ENABLE));
1340 break;
1341 default:
1342 AssertReleaseFailed();
1343 break;
1344 }
1345
1346 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
1347 return VINF_SUCCESS;
1348}
1349
1350
1351/**
1352 * Initializes per-VCPU GIC to the state following a power-up or hardware
1353 * reset.
1354 *
1355 * @param pVCpu The cross context virtual CPU structure.
1356 */
1357DECLHIDDEN(void) gicResetCpu(PVMCPUCC pVCpu)
1358{
1359 LogFlowFunc(("GIC%u\n", pVCpu->idCpu));
1360 VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu);
1361
1362 memset((void *)&pVCpu->gic.s.abRunningPriorities[0], 0xff, sizeof(pVCpu->gic.s.abRunningPriorities));
1363 pVCpu->gic.s.idxRunningPriority = 0;
1364 pVCpu->gic.s.bInterruptPriority = 0; /* Means no interrupt gets through to the PE. */
1365}
1366
1367
1368/**
1369 * @callback_method_impl{FNIOMMMIONEWREAD}
1370 */
1371DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicDistMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
1372{
1373 NOREF(pvUser);
1374 Assert(!(off & 0x3));
1375 Assert(cb == 4); RT_NOREF_PV(cb);
1376
1377 PVMCPUCC pVCpu = PDMDevHlpGetVMCPU(pDevIns);
1378 uint16_t offReg = off & 0xfffc;
1379 uint32_t uValue = 0;
1380
1381 STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatMmioRead));
1382
1383 VBOXSTRICTRC rc = VBOXSTRICTRC_VAL(gicDistRegisterRead(pDevIns, pVCpu, offReg, &uValue));
1384 *(uint32_t *)pv = uValue;
1385
1386 Log2(("GIC%u: gicDistMmioRead: offReg=%#RX16 uValue=%#RX32\n", pVCpu->idCpu, offReg, uValue));
1387 return rc;
1388}
1389
1390
1391/**
1392 * @callback_method_impl{FNIOMMMIONEWWRITE}
1393 */
1394DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicDistMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
1395{
1396 NOREF(pvUser);
1397 Assert(!(off & 0x3));
1398 Assert(cb == 4); RT_NOREF_PV(cb);
1399
1400 PVMCPUCC pVCpu = PDMDevHlpGetVMCPU(pDevIns);
1401 uint16_t offReg = off & 0xfffc;
1402 uint32_t uValue = *(uint32_t *)pv;
1403
1404 STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatMmioWrite));
1405
1406 Log2(("GIC%u: gicDistMmioWrite: offReg=%#RX16 uValue=%#RX32\n", pVCpu->idCpu, offReg, uValue));
1407 return gicDistRegisterWrite(pDevIns, pVCpu, offReg, uValue);
1408}
1409
1410
1411/**
1412 * @callback_method_impl{FNIOMMMIONEWREAD}
1413 */
1414DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicReDistMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
1415{
1416 NOREF(pvUser);
1417 Assert(!(off & 0x3));
1418 Assert(cb == 4); RT_NOREF_PV(cb);
1419
1420 /*
1421 * Determine the redistributor being targeted. Each redistributor takes
1422 * GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE bytes
1423 * and the redistributors are adjacent.
1424 */
1425 uint32_t idReDist = off / (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
1426 off %= (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
1427
1428 PVMCC pVM = PDMDevHlpGetVM(pDevIns);
1429 Assert(idReDist < pVM->cCpus);
1430 PVMCPUCC pVCpu = pVM->apCpusR3[idReDist];
1431
1432 STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatMmioRead));
1433
1434 /* Redistributor or SGI/PPI frame? */
1435 uint16_t offReg = off & 0xfffc;
1436 uint32_t uValue = 0;
1437 VBOXSTRICTRC rcStrict;
1438 if (off < GIC_REDIST_REG_FRAME_SIZE)
1439 rcStrict = gicReDistRegisterRead(pDevIns, pVCpu, idReDist, offReg, &uValue);
1440 else
1441 rcStrict = gicReDistSgiPpiRegisterRead(pDevIns, pVCpu, offReg, &uValue);
1442
1443 *(uint32_t *)pv = uValue;
1444 Log2(("GICReDist%u: gicReDistMmioRead: off=%RGp idReDist=%u offReg=%#RX16 uValue=%#RX32 -> %Rrc\n",
1445 pVCpu->idCpu, off, idReDist, offReg, uValue, VBOXSTRICTRC_VAL(rcStrict)));
1446 return rcStrict;
1447}
1448
1449
1450/**
1451 * @callback_method_impl{FNIOMMMIONEWWRITE}
1452 */
1453DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicReDistMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
1454{
1455 NOREF(pvUser);
1456 Assert(!(off & 0x3));
1457 Assert(cb == 4); RT_NOREF_PV(cb);
1458
1459 uint32_t uValue = *(uint32_t *)pv;
1460
1461 /*
1462 * Determine the redistributor being targeted. Each redistributor takes
1463 * GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE bytes
1464 * and the redistributors are adjacent.
1465 */
1466 uint32_t idReDist = off / (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
1467 off %= (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
1468
1469 PVMCC pVM = PDMDevHlpGetVM(pDevIns);
1470 Assert(idReDist < pVM->cCpus);
1471 PVMCPUCC pVCpu = pVM->apCpusR3[idReDist];
1472
1473 STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatMmioWrite));
1474
1475 /* Redistributor or SGI/PPI frame? */
1476 uint16_t offReg = off & 0xfffc;
1477 VBOXSTRICTRC rcStrict;
1478 if (off < GIC_REDIST_REG_FRAME_SIZE)
1479 rcStrict = gicReDistRegisterWrite(pDevIns, pVCpu, offReg, uValue);
1480 else
1481 rcStrict = gicReDistSgiPpiRegisterWrite(pDevIns, pVCpu, offReg, uValue);
1482
1483 Log2(("GICReDist%u: gicReDistMmioWrite: off=%RGp idReDist=%u offReg=%#RX16 uValue=%#RX32 -> %Rrc\n",
1484 pVCpu->idCpu, off, idReDist, offReg, uValue, VBOXSTRICTRC_VAL(rcStrict)));
1485 return rcStrict;
1486}
1487
1488
1489#ifndef IN_RING3
1490
1491/**
1492 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
1493 */
1494static DECLCALLBACK(int) gicRZConstruct(PPDMDEVINS pDevIns)
1495{
1496 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1497 AssertReleaseFailed();
1498 return VINF_SUCCESS;
1499}
1500#endif /* !IN_RING3 */
1501
1502/**
1503 * GIC device registration structure.
1504 */
1505const PDMDEVREG g_DeviceGIC =
1506{
1507 /* .u32Version = */ PDM_DEVREG_VERSION,
1508 /* .uReserved0 = */ 0,
1509 /* .szName = */ "gic",
1510 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
1511 /* .fClass = */ PDM_DEVREG_CLASS_PIC,
1512 /* .cMaxInstances = */ 1,
1513 /* .uSharedVersion = */ 42,
1514 /* .cbInstanceShared = */ sizeof(GICDEV),
1515 /* .cbInstanceCC = */ 0,
1516 /* .cbInstanceRC = */ 0,
1517 /* .cMaxPciDevices = */ 0,
1518 /* .cMaxMsixVectors = */ 0,
1519 /* .pszDescription = */ "Generic Interrupt Controller",
1520#if defined(IN_RING3)
1521 /* .szRCMod = */ "VMMRC.rc",
1522 /* .szR0Mod = */ "VMMR0.r0",
1523 /* .pfnConstruct = */ gicR3Construct,
1524 /* .pfnDestruct = */ gicR3Destruct,
1525 /* .pfnRelocate = */ gicR3Relocate,
1526 /* .pfnMemSetup = */ NULL,
1527 /* .pfnPowerOn = */ NULL,
1528 /* .pfnReset = */ gicR3Reset,
1529 /* .pfnSuspend = */ NULL,
1530 /* .pfnResume = */ NULL,
1531 /* .pfnAttach = */ NULL,
1532 /* .pfnDetach = */ NULL,
1533 /* .pfnQueryInterface = */ NULL,
1534 /* .pfnInitComplete = */ NULL,
1535 /* .pfnPowerOff = */ NULL,
1536 /* .pfnSoftReset = */ NULL,
1537 /* .pfnReserved0 = */ NULL,
1538 /* .pfnReserved1 = */ NULL,
1539 /* .pfnReserved2 = */ NULL,
1540 /* .pfnReserved3 = */ NULL,
1541 /* .pfnReserved4 = */ NULL,
1542 /* .pfnReserved5 = */ NULL,
1543 /* .pfnReserved6 = */ NULL,
1544 /* .pfnReserved7 = */ NULL,
1545#elif defined(IN_RING0)
1546 /* .pfnEarlyConstruct = */ NULL,
1547 /* .pfnConstruct = */ gicRZConstruct,
1548 /* .pfnDestruct = */ NULL,
1549 /* .pfnFinalDestruct = */ NULL,
1550 /* .pfnRequest = */ NULL,
1551 /* .pfnReserved0 = */ NULL,
1552 /* .pfnReserved1 = */ NULL,
1553 /* .pfnReserved2 = */ NULL,
1554 /* .pfnReserved3 = */ NULL,
1555 /* .pfnReserved4 = */ NULL,
1556 /* .pfnReserved5 = */ NULL,
1557 /* .pfnReserved6 = */ NULL,
1558 /* .pfnReserved7 = */ NULL,
1559#elif defined(IN_RC)
1560 /* .pfnConstruct = */ gicRZConstruct,
1561 /* .pfnReserved0 = */ NULL,
1562 /* .pfnReserved1 = */ NULL,
1563 /* .pfnReserved2 = */ NULL,
1564 /* .pfnReserved3 = */ NULL,
1565 /* .pfnReserved4 = */ NULL,
1566 /* .pfnReserved5 = */ NULL,
1567 /* .pfnReserved6 = */ NULL,
1568 /* .pfnReserved7 = */ NULL,
1569#else
1570# error "Not in IN_RING3, IN_RING0 or IN_RC!"
1571#endif
1572 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
1573};
1574
1575/**
1576 * The VirtualBox GIC backend.
1577 */
1578const PDMGICBACKEND g_GicBackend =
1579{
1580 /* .pfnReadSysReg = */ gicReadSysReg,
1581 /* .pfnWriteSysReg = */ gicWriteSysReg,
1582 /* .pfnSetSpi = */ gicSetSpi,
1583 /* .pfnSetPpi = */ gicSetPpi,
1584};
1585
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