VirtualBox

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

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

VMM/GIC: bugref:10404 CFGM bits.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 60.2 KB
Line 
1/* $Id: GICAll.cpp 107984 2025-01-30 06:43:18Z 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 Assert(pThis->uMaxSpi > 0 && pThis->uMaxSpi < GIC_DIST_REG_TYPER_NUM_ITLINES);
410 *puValue = GIC_DIST_REG_TYPER_NUM_ITLINES_SET(pThis->uMaxSpi)
411 | 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? */
412 /*| GIC_DIST_REG_TYPER_ESPI*/ /** @todo */
413 /*| GIC_DIST_REG_TYPER_NMI*/ /** @todo Non-maskable interrupts */
414 /*| GIC_DIST_REG_TYPER_SECURITY_EXTN */ /** @todo */
415 /*| GIC_DIST_REG_TYPER_MBIS */ /** @todo Message based interrupts */
416 /*| GIC_DIST_REG_TYPER_LPIS */ /** @todo Support LPIs */
417 | GIC_DIST_REG_TYPER_IDBITS_SET(16);
418 break;
419 case GIC_DIST_REG_STATUSR_OFF:
420 AssertReleaseFailed();
421 break;
422 case GIC_DIST_REG_IGROUPRn_OFF_START: /* Only 32 lines for now. */
423 AssertReleaseFailed();
424 break;
425 case GIC_DIST_REG_ISENABLERn_OFF_START + 4: /* Only 32 lines for now. */
426 case GIC_DIST_REG_ICENABLERn_OFF_START + 4: /* Only 32 lines for now. */
427 *puValue = ASMAtomicReadU32(&pThis->bmIntEnabled);
428 break;
429 case GIC_DIST_REG_ISPENDRn_OFF_START: /* Only 32 lines for now. */
430 AssertReleaseFailed();
431 break;
432 case GIC_DIST_REG_ICPENDRn_OFF_START: /* Only 32 lines for now. */
433 AssertReleaseFailed();
434 break;
435 case GIC_DIST_REG_ISACTIVERn_OFF_START: /* Only 32 lines for now. */
436 AssertReleaseFailed();
437 break;
438 case GIC_DIST_REG_ICACTIVERn_OFF_START: /* Only 32 lines for now. */
439 AssertReleaseFailed();
440 break;
441 case GIC_DIST_REG_IPRIORITYRn_OFF_START:
442 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 4: /* These are banked for the PEs and access the redistributor. */
443 {
444 PGICCPU pGicVCpu = VMCPU_TO_GICCPU(pVCpu);
445
446 /* Figure out the register which is written. */
447 uint8_t idxPrio = offReg - GIC_DIST_REG_IPRIORITYRn_OFF_START;
448 Assert(idxPrio <= RT_ELEMENTS(pThis->abIntPriority) - sizeof(uint32_t));
449
450 uint32_t u32Value = 0;
451 for (uint32_t i = idxPrio; i < idxPrio + sizeof(uint32_t); i++)
452 u32Value |= pGicVCpu->abIntPriority[i] << ((i - idxPrio) * 8);
453
454 *puValue = u32Value;
455 break;
456 }
457 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 32: /* Only 32 lines for now. */
458 {
459 /* Figure out the register which is written. */
460 uint8_t idxPrio = offReg - GIC_DIST_REG_IPRIORITYRn_OFF_START - 32;
461 Assert(idxPrio <= RT_ELEMENTS(pThis->abIntPriority) - sizeof(uint32_t));
462
463 uint32_t u32Value = 0;
464 for (uint32_t i = idxPrio; i < idxPrio + sizeof(uint32_t); i++)
465 u32Value |= pThis->abIntPriority[i] << ((i - idxPrio) * 8);
466
467 *puValue = u32Value;
468 break;
469 }
470 case GIC_DIST_REG_ITARGETSRn_OFF_START: /* Only 32 lines for now. */
471 AssertReleaseFailed();
472 break;
473 case GIC_DIST_REG_ICFGRn_OFF_START: /* Only 32 lines for now. */
474 AssertReleaseFailed();
475 break;
476 case GIC_DIST_REG_IGRPMODRn_OFF_START: /* Only 32 lines for now. */
477 AssertReleaseFailed();
478 break;
479 case GIC_DIST_REG_NSACRn_OFF_START: /* Only 32 lines for now. */
480 AssertReleaseFailed();
481 break;
482 case GIC_DIST_REG_SGIR_OFF:
483 AssertReleaseFailed();
484 break;
485 case GIC_DIST_REG_CPENDSGIRn_OFF_START:
486 AssertReleaseFailed();
487 break;
488 case GIC_DIST_REG_SPENDSGIRn_OFF_START:
489 AssertReleaseFailed();
490 break;
491 case GIC_DIST_REG_INMIn_OFF_START:
492 AssertReleaseFailed();
493 break;
494 case GIC_DIST_REG_PIDR2_OFF:
495 Assert(pThis->uArchRev <= GIC_DIST_REG_PIDR2_ARCH_REV_GICV4);
496 *puValue = GIC_DIST_REG_PIDR2_ARCH_REV_SET(pThis->uArchRev);
497 break;
498 case GIC_DIST_REG_IIDR_OFF:
499 case GIC_DIST_REG_TYPER2_OFF:
500 *puValue = 0;
501 break;
502 case GIC_DIST_REG_IROUTERn_OFF_START:
503 AssertFailed();
504 break;
505 default:
506 *puValue = 0;
507 }
508 return VINF_SUCCESS;
509}
510
511
512/**
513 * Writes a GIC distributor register.
514 *
515 * @returns Strict VBox status code.
516 * @param pDevIns The device instance.
517 * @param pVCpu The cross context virtual CPU structure.
518 * @param offReg The offset of the register being written.
519 * @param uValue The register value.
520 */
521DECLINLINE(VBOXSTRICTRC) gicDistRegisterWrite(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t uValue)
522{
523 VMCPU_ASSERT_EMT(pVCpu); RT_NOREF(pVCpu);
524 PGICDEV pThis = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
525 PVMCC pVM = PDMDevHlpGetVM(pDevIns);
526
527 if (offReg >= GIC_DIST_REG_IROUTERn_OFF_START && offReg <= GIC_DIST_REG_IROUTERn_OFF_LAST)
528 {
529 uint32_t idxReg = (offReg - GIC_DIST_REG_IROUTERn_OFF_START) / 4;
530 LogFlowFunc(("GicDist: idxIRouter=%u uValue=%#x\n", idxReg, uValue));
531 if (idxReg < RT_ELEMENTS(pThis->au32IntRouting))
532 pThis->au32IntRouting[idxReg] = uValue;
533 return VINF_SUCCESS;
534 }
535
536 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
537 switch (offReg)
538 {
539 case GIC_DIST_REG_CTLR_OFF:
540 ASMAtomicWriteBool(&pThis->fIrqGrp0Enabled, RT_BOOL(uValue & GIC_DIST_REG_CTRL_ENABLE_GRP0));
541 ASMAtomicWriteBool(&pThis->fIrqGrp1Enabled, RT_BOOL(uValue & GIC_DIST_REG_CTRL_ENABLE_GRP1_NS));
542 Assert(!(uValue & GIC_DIST_REG_CTRL_ARE_NS));
543 rcStrict = gicDistUpdateIrqState(pVM, pThis);
544 break;
545 case GIC_DIST_REG_STATUSR_OFF:
546 AssertReleaseFailed();
547 break;
548 case GIC_DIST_REG_SETSPI_NSR_OFF:
549 AssertReleaseFailed();
550 break;
551 case GIC_DIST_REG_CLRSPI_NSR_OFF:
552 AssertReleaseFailed();
553 break;
554 case GIC_DIST_REG_SETSPI_SR_OFF:
555 AssertReleaseFailed();
556 break;
557 case GIC_DIST_REG_CLRSPI_SR_OFF:
558 AssertReleaseFailed();
559 break;
560 case GIC_DIST_REG_IGROUPRn_OFF_START: /* Only 32 lines for now. */
561 AssertReleaseFailed();
562 break;
563 case GIC_DIST_REG_IGROUPRn_OFF_START + 4: /* Only 32 lines for now. */
564 ASMAtomicOrU32(&pThis->u32RegIGrp0, uValue);
565 rcStrict = gicDistUpdateIrqState(pVM, pThis);
566 break;
567 case GIC_DIST_REG_ISENABLERn_OFF_START + 4: /* Only 32 lines for now. */
568 ASMAtomicOrU32(&pThis->bmIntEnabled, uValue);
569 rcStrict = gicDistUpdateIrqState(pVM, pThis);
570 break;
571 case GIC_DIST_REG_ICENABLERn_OFF_START:
572 AssertReleaseFailed();
573 break;
574 case GIC_DIST_REG_ICENABLERn_OFF_START + 4: /* Only 32 lines for now. */
575 ASMAtomicAndU32(&pThis->bmIntEnabled, ~uValue);
576 rcStrict = gicDistUpdateIrqState(pVM, pThis);
577 break;
578 case GIC_DIST_REG_ISPENDRn_OFF_START: /* Only 32 lines for now. */
579 AssertReleaseFailed();
580 break;
581 case GIC_DIST_REG_ICPENDRn_OFF_START: /* Only 32 lines for now. */
582 AssertReleaseFailed();
583 break;
584 case GIC_DIST_REG_ISACTIVERn_OFF_START: /* Only 32 lines for now. */
585 AssertReleaseFailed();
586 break;
587 case GIC_DIST_REG_ICACTIVERn_OFF_START + 4: /* Only 32 lines for now. */
588 ASMAtomicAndU32(&pThis->bmIntActive, ~uValue);
589 rcStrict = gicDistUpdateIrqState(pVM, pThis);
590 break;
591 case GIC_DIST_REG_IPRIORITYRn_OFF_START: /* These are banked for the PEs and access the redistributor. */
592 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 4:
593 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 8:
594 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 12:
595 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 16:
596 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 20:
597 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 24:
598 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 28:
599 {
600 PGICCPU pGicVCpu = VMCPU_TO_GICCPU(pVCpu);
601
602 /* Figure out the register which is written. */
603 uint8_t idxPrio = offReg - GIC_DIST_REG_IPRIORITYRn_OFF_START;
604 Assert(idxPrio <= RT_ELEMENTS(pGicVCpu->abIntPriority) - sizeof(uint32_t));
605 for (uint32_t i = idxPrio; i < idxPrio + sizeof(uint32_t); i++)
606 {
607 pGicVCpu->abIntPriority[i] = (uint8_t)(uValue & 0xff);
608 uValue >>= 8;
609 }
610 break;
611 }
612 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 32: /* Only 32 lines for now. */
613 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 36:
614 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 40:
615 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 44:
616 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 48:
617 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 52:
618 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 56:
619 case GIC_DIST_REG_IPRIORITYRn_OFF_START + 60:
620 {
621 /* Figure out the register which is written. */
622 uint8_t idxPrio = offReg - GIC_DIST_REG_IPRIORITYRn_OFF_START - 32;
623 Assert(idxPrio <= RT_ELEMENTS(pThis->abIntPriority) - sizeof(uint32_t));
624 for (uint32_t i = idxPrio; i < idxPrio + sizeof(uint32_t); i++)
625 {
626#if 1
627 /** @todo r=aeichner This gross hack prevents Windows from hanging during boot because
628 * it tries to set the interrupt priority for PCI interrupt lines to 0 which will cause an interrupt
629 * storm later on because the lowest interrupt priority Windows seems to use is 32 for the per vCPU
630 * timer.
631 */
632 if ((uValue & 0xff) == 0)
633 {
634 uValue >>= 8;
635 continue;
636 }
637#endif
638 pThis->abIntPriority[i] = (uint8_t)(uValue & 0xff);
639 uValue >>= 8;
640 }
641 break;
642 }
643 case GIC_DIST_REG_ITARGETSRn_OFF_START: /* Only 32 lines for now. */
644 AssertReleaseFailed();
645 break;
646 case GIC_DIST_REG_ICFGRn_OFF_START + 8: /* Only 32 lines for now. */
647 ASMAtomicWriteU32(&pThis->u32RegICfg0, uValue);
648 break;
649 case GIC_DIST_REG_ICFGRn_OFF_START+ 12:
650 ASMAtomicWriteU32(&pThis->u32RegICfg1, uValue);
651 break;
652 case GIC_DIST_REG_IGRPMODRn_OFF_START: /* Only 32 lines for now. */
653 AssertReleaseFailed();
654 break;
655 case GIC_DIST_REG_NSACRn_OFF_START: /* Only 32 lines for now. */
656 AssertReleaseFailed();
657 break;
658 case GIC_DIST_REG_SGIR_OFF:
659 AssertReleaseFailed();
660 break;
661 case GIC_DIST_REG_CPENDSGIRn_OFF_START:
662 AssertReleaseFailed();
663 break;
664 case GIC_DIST_REG_SPENDSGIRn_OFF_START:
665 AssertReleaseFailed();
666 break;
667 case GIC_DIST_REG_INMIn_OFF_START:
668 AssertReleaseFailed();
669 break;
670 default:
671 //AssertReleaseFailed();
672 break;
673 }
674
675 return rcStrict;
676}
677
678
679/**
680 * Reads a GIC redistributor register.
681 *
682 * @returns VBox status code.
683 * @param pDevIns The device instance.
684 * @param pVCpu The cross context virtual CPU structure.
685 * @param idRedist The redistributor ID.
686 * @param offReg The offset of the register being read.
687 * @param puValue Where to store the register value.
688 */
689DECLINLINE(VBOXSTRICTRC) gicReDistRegisterRead(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint32_t idRedist, uint16_t offReg, uint32_t *puValue)
690{
691 PGICDEV pThis = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
692
693 switch (offReg)
694 {
695 case GIC_REDIST_REG_TYPER_OFF:
696 {
697 PVMCC pVM = PDMDevHlpGetVM(pDevIns);
698 *puValue = ((pVCpu->idCpu == pVM->cCpus - 1) ? GIC_REDIST_REG_TYPER_LAST : 0)
699 | GIC_REDIST_REG_TYPER_CPU_NUMBER_SET(idRedist)
700 | GIC_REDIST_REG_TYPER_CMN_LPI_AFF_SET(GIC_REDIST_REG_TYPER_CMN_LPI_AFF_ALL);
701 break;
702 }
703 case GIC_REDIST_REG_TYPER_AFFINITY_OFF:
704 *puValue = idRedist;
705 break;
706 case GIC_REDIST_REG_PIDR2_OFF:
707 Assert(pThis->uArchRev <= GIC_DIST_REG_PIDR2_ARCH_REV_GICV4);
708 *puValue = GIC_REDIST_REG_PIDR2_ARCH_REV_SET(pThis->uArchRev);
709 break;
710 default:
711 *puValue = 0;
712 }
713
714 return VINF_SUCCESS;
715}
716
717
718/**
719 * Reads a GIC redistributor SGI/PPI frame register.
720 *
721 * @returns VBox status code.
722 * @param pDevIns The device instance.
723 * @param pVCpu The cross context virtual CPU structure.
724 * @param offReg The offset of the register being read.
725 * @param puValue Where to store the register value.
726 */
727DECLINLINE(VBOXSTRICTRC) gicReDistSgiPpiRegisterRead(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t *puValue)
728{
729 VMCPU_ASSERT_EMT(pVCpu);
730 RT_NOREF(pDevIns);
731
732 PGICCPU pThis = VMCPU_TO_GICCPU(pVCpu);
733 switch (offReg)
734 {
735 case GIC_REDIST_SGI_PPI_REG_ISENABLER0_OFF:
736 case GIC_REDIST_SGI_PPI_REG_ICENABLER0_OFF:
737 *puValue = ASMAtomicReadU32(&pThis->bmIntEnabled);
738 break;
739 case GIC_REDIST_SGI_PPI_REG_ISPENDR0_OFF:
740 case GIC_REDIST_SGI_PPI_REG_ICPENDR0_OFF:
741 *puValue = ASMAtomicReadU32(&pThis->bmIntPending);
742 break;
743 case GIC_REDIST_SGI_PPI_REG_ISACTIVER0_OFF:
744 case GIC_REDIST_SGI_PPI_REG_ICACTIVER0_OFF:
745 *puValue = ASMAtomicReadU32(&pThis->bmIntActive);
746 break;
747 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START:
748 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 4:
749 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 8:
750 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 12:
751 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 16:
752 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 20:
753 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 24:
754 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 28:
755 {
756 /* Figure out the register which is written. */
757 uint8_t idxPrio = offReg - GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START;
758 Assert(idxPrio <= RT_ELEMENTS(pThis->abIntPriority) - sizeof(uint32_t));
759
760 uint32_t u32Value = 0;
761 for (uint32_t i = idxPrio; i < idxPrio + sizeof(uint32_t); i++)
762 u32Value |= pThis->abIntPriority[i] << ((i - idxPrio) * 8);
763
764 *puValue = u32Value;
765 break;
766 }
767 case GIC_REDIST_SGI_PPI_REG_ICFGR0_OFF:
768 *puValue = ASMAtomicReadU32(&pThis->u32RegICfg0);
769 break;
770 case GIC_REDIST_SGI_PPI_REG_ICFGR1_OFF:
771 *puValue = ASMAtomicReadU32(&pThis->u32RegICfg1);
772 break;
773 default:
774 AssertReleaseFailed();
775 *puValue = 0;
776 }
777
778 return VINF_SUCCESS;
779}
780
781
782/**
783 * Writes a GIC redistributor frame register.
784 *
785 * @returns Strict VBox status code.
786 * @param pDevIns The device instance.
787 * @param pVCpu The cross context virtual CPU structure.
788 * @param offReg The offset of the register being written.
789 * @param uValue The register value.
790 */
791DECLINLINE(VBOXSTRICTRC) gicReDistRegisterWrite(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t uValue)
792{
793 VMCPU_ASSERT_EMT(pVCpu);
794 RT_NOREF(pDevIns, pVCpu, uValue);
795
796 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
797 switch (offReg)
798 {
799 case GIC_REDIST_REG_STATUSR_OFF:
800 AssertReleaseFailed();
801 break;
802 case GIC_REDIST_REG_WAKER_OFF:
803 Assert(uValue == 0);
804 break;
805 case GIC_REDIST_REG_PARTIDR_OFF:
806 AssertReleaseFailed();
807 break;
808 case GIC_REDIST_REG_SETLPIR_OFF:
809 AssertReleaseFailed();
810 break;
811 case GIC_REDIST_REG_CLRLPIR_OFF:
812 AssertReleaseFailed();
813 break;
814 case GIC_REDIST_REG_PROPBASER_OFF:
815 AssertReleaseFailed();
816 break;
817 case GIC_REDIST_REG_PENDBASER_OFF:
818 AssertReleaseFailed();
819 break;
820 case GIC_REDIST_REG_INVLPIR_OFF:
821 AssertReleaseFailed();
822 break;
823 case GIC_REDIST_REG_INVALLR_OFF:
824 AssertReleaseFailed();
825 break;
826 default:
827 AssertReleaseFailed();
828 break;
829 }
830
831 return rcStrict;
832}
833
834
835/**
836 * Writes a GIC redistributor SGI/PPI frame register.
837 *
838 * @returns Strict VBox status code.
839 * @param pDevIns The device instance.
840 * @param pVCpu The cross context virtual CPU structure.
841 * @param offReg The offset of the register being written.
842 * @param uValue The register value.
843 */
844DECLINLINE(VBOXSTRICTRC) gicReDistSgiPpiRegisterWrite(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t uValue)
845{
846 VMCPU_ASSERT_EMT(pVCpu);
847 RT_NOREF(pDevIns);
848
849 PGICCPU pThis = VMCPU_TO_GICCPU(pVCpu);
850 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
851 switch (offReg)
852 {
853 case GIC_REDIST_SGI_PPI_REG_IGROUPR0_OFF:
854 ASMAtomicOrU32(&pThis->u32RegIGrp0, uValue);
855 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
856 break;
857 case GIC_REDIST_SGI_PPI_REG_ISENABLER0_OFF:
858 ASMAtomicOrU32(&pThis->bmIntEnabled, uValue);
859 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
860 break;
861 case GIC_REDIST_SGI_PPI_REG_ICENABLER0_OFF:
862 ASMAtomicAndU32(&pThis->bmIntEnabled, ~uValue);
863 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
864 break;
865 case GIC_REDIST_SGI_PPI_REG_ISPENDR0_OFF:
866 ASMAtomicOrU32(&pThis->bmIntPending, uValue);
867 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
868 break;
869 case GIC_REDIST_SGI_PPI_REG_ICPENDR0_OFF:
870 ASMAtomicAndU32(&pThis->bmIntPending, ~uValue);
871 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
872 break;
873 case GIC_REDIST_SGI_PPI_REG_ISACTIVER0_OFF:
874 ASMAtomicOrU32(&pThis->bmIntActive, uValue);
875 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
876 break;
877 case GIC_REDIST_SGI_PPI_REG_ICACTIVER0_OFF:
878 ASMAtomicAndU32(&pThis->bmIntActive, ~uValue);
879 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
880 break;
881 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START:
882 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 4:
883 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 8:
884 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 12:
885 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 16:
886 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 20:
887 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 24:
888 case GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START + 28:
889 {
890 /* Figure out the register which is written. */
891 uint8_t idxPrio = offReg - GIC_REDIST_SGI_PPI_REG_IPRIORITYRn_OFF_START;
892 Assert(idxPrio <= RT_ELEMENTS(pThis->abIntPriority) - sizeof(uint32_t));
893 for (uint32_t i = idxPrio; i < idxPrio + sizeof(uint32_t); i++)
894 {
895 pThis->abIntPriority[i] = (uint8_t)(uValue & 0xff);
896 uValue >>= 8;
897 }
898 break;
899 }
900 case GIC_REDIST_SGI_PPI_REG_ICFGR0_OFF:
901 ASMAtomicWriteU32(&pThis->u32RegICfg0, uValue);
902 break;
903 case GIC_REDIST_SGI_PPI_REG_ICFGR1_OFF:
904 ASMAtomicWriteU32(&pThis->u32RegICfg1, uValue);
905 break;
906 default:
907 //AssertReleaseFailed();
908 break;
909 }
910
911 return rcStrict;
912}
913
914
915/**
916 * @interface_method_impl{PDMGICBACKEND,pfnSetSpi}
917 */
918static DECLCALLBACK(int) gicSetSpi(PVMCC pVM, uint32_t uIntId, bool fAsserted)
919{
920 LogFlowFunc(("pVM=%p uIntId=%u fAsserted=%RTbool\n",
921 pVM, uIntId, fAsserted));
922
923 AssertReturn(uIntId < GIC_SPI_MAX, VERR_INVALID_PARAMETER);
924
925 PGIC pGic = VM_TO_GIC(pVM);
926 PPDMDEVINS pDevIns = pGic->CTX_SUFF(pDevIns);
927 PGICDEV pThis = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
928
929 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
930 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
931
932 /* Update the interrupts pending state. */
933 if (fAsserted)
934 ASMAtomicOrU32(&pThis->bmIntPending, RT_BIT_32(uIntId));
935 else
936 ASMAtomicAndU32(&pThis->bmIntPending, ~RT_BIT_32(uIntId));
937
938 int rc = VBOXSTRICTRC_VAL(gicDistUpdateIrqState(pVM, pThis));
939 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
940 return rc;
941}
942
943
944/**
945 * @interface_method_impl{PDMGICBACKEND,pfnSetPpi}
946 */
947static DECLCALLBACK(int) gicSetPpi(PVMCPUCC pVCpu, uint32_t uIntId, bool fAsserted)
948{
949 LogFlowFunc(("pVCpu=%p{.idCpu=%u} uIntId=%u fAsserted=%RTbool\n",
950 pVCpu, pVCpu->idCpu, uIntId, fAsserted));
951
952 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
953 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
954 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
955
956 AssertReturn(uIntId <= (GIC_INTID_RANGE_PPI_LAST - GIC_INTID_RANGE_PPI_START), VERR_INVALID_PARAMETER);
957 int rc = gicReDistInterruptSet(pVCpu, uIntId + GIC_INTID_RANGE_PPI_START, fAsserted);
958 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
959
960 return rc;
961}
962
963
964/**
965 * Sets the specified software generated interrupt starting.
966 *
967 * @returns VBox status code.
968 * @param pVCpu The cross context virtual CPU structure.
969 * @param uIntId The PPI ID (minus GIC_INTID_RANGE_SGI_START) to assert/de-assert.
970 * @param fAsserted Flag whether to mark the interrupt as asserted/de-asserted.
971 */
972static int gicSetSgi(PVMCPUCC pVCpu, uint32_t uIntId, bool fAsserted)
973{
974 LogFlowFunc(("pVCpu=%p{.idCpu=%u} uIntId=%u fAsserted=%RTbool\n",
975 pVCpu, pVCpu->idCpu, uIntId, fAsserted));
976
977 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
978 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
979 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
980
981 AssertReturn(uIntId <= (GIC_INTID_RANGE_SGI_LAST - GIC_INTID_RANGE_SGI_START), VERR_INVALID_PARAMETER);
982 int rc = gicReDistInterruptSet(pVCpu, uIntId + GIC_INTID_RANGE_SGI_START, fAsserted);
983 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
984
985 return rc;
986}
987
988
989/**
990 * @interface_method_impl{PDMGICBACKEND,pfnReadSysReg}
991 */
992static DECLCALLBACK(VBOXSTRICTRC) gicReadSysReg(PVMCPUCC pVCpu, uint32_t u32Reg, uint64_t *pu64Value)
993{
994 /*
995 * Validate.
996 */
997 VMCPU_ASSERT_EMT(pVCpu);
998 Assert(pu64Value);
999
1000 *pu64Value = 0;
1001 PGICCPU pThis = VMCPU_TO_GICCPU(pVCpu);
1002 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
1003 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
1004
1005 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
1006 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
1007
1008 switch (u32Reg)
1009 {
1010 case ARMV8_AARCH64_SYSREG_ICC_PMR_EL1:
1011 *pu64Value = pThis->bInterruptPriority;
1012 break;
1013 case ARMV8_AARCH64_SYSREG_ICC_IAR0_EL1:
1014 AssertReleaseFailed();
1015 break;
1016 case ARMV8_AARCH64_SYSREG_ICC_EOIR0_EL1:
1017 AssertReleaseFailed();
1018 break;
1019 case ARMV8_AARCH64_SYSREG_ICC_HPPIR0_EL1:
1020 AssertReleaseFailed();
1021 break;
1022 case ARMV8_AARCH64_SYSREG_ICC_BPR0_EL1:
1023 *pu64Value = pThis->bBinaryPointGrp0 & 0x7;
1024 break;
1025 case ARMV8_AARCH64_SYSREG_ICC_AP0R0_EL1:
1026 AssertReleaseFailed();
1027 break;
1028 case ARMV8_AARCH64_SYSREG_ICC_AP0R1_EL1:
1029 AssertReleaseFailed();
1030 break;
1031 case ARMV8_AARCH64_SYSREG_ICC_AP0R2_EL1:
1032 AssertReleaseFailed();
1033 break;
1034 case ARMV8_AARCH64_SYSREG_ICC_AP0R3_EL1:
1035 AssertReleaseFailed();
1036 break;
1037 case ARMV8_AARCH64_SYSREG_ICC_AP1R0_EL1:
1038 AssertReleaseFailed();
1039 break;
1040 case ARMV8_AARCH64_SYSREG_ICC_AP1R1_EL1:
1041 AssertReleaseFailed();
1042 break;
1043 case ARMV8_AARCH64_SYSREG_ICC_AP1R2_EL1:
1044 AssertReleaseFailed();
1045 break;
1046 case ARMV8_AARCH64_SYSREG_ICC_AP1R3_EL1:
1047 AssertReleaseFailed();
1048 break;
1049 case ARMV8_AARCH64_SYSREG_ICC_NMIAR1_EL1:
1050 AssertReleaseFailed();
1051 break;
1052 case ARMV8_AARCH64_SYSREG_ICC_DIR_EL1:
1053 AssertReleaseFailed();
1054 break;
1055 case ARMV8_AARCH64_SYSREG_ICC_RPR_EL1:
1056 *pu64Value = pThis->abRunningPriorities[pThis->idxRunningPriority];
1057 break;
1058 case ARMV8_AARCH64_SYSREG_ICC_SGI1R_EL1:
1059 AssertReleaseFailed();
1060 break;
1061 case ARMV8_AARCH64_SYSREG_ICC_ASGI1R_EL1:
1062 AssertReleaseFailed();
1063 break;
1064 case ARMV8_AARCH64_SYSREG_ICC_SGI0R_EL1:
1065 AssertReleaseFailed();
1066 break;
1067 case ARMV8_AARCH64_SYSREG_ICC_IAR1_EL1:
1068 {
1069 /** @todo Figure out the highest priority interrupt. */
1070 uint32_t bmIntActive = ASMAtomicReadU32(&pThis->bmIntActive);
1071 uint32_t bmIntEnabled = ASMAtomicReadU32(&pThis->bmIntEnabled);
1072 uint32_t bmPending = (ASMAtomicReadU32(&pThis->bmIntPending) & bmIntEnabled) & ~bmIntActive;
1073 int32_t idxIntPending = ASMBitFirstSet(&bmPending, sizeof(bmPending) * 8);
1074 if (idxIntPending > -1)
1075 {
1076 /* Mark the interrupt as active. */
1077 ASMAtomicOrU32(&pThis->bmIntActive, RT_BIT_32(idxIntPending));
1078 /* Drop priority. */
1079 Assert((uint32_t)idxIntPending < RT_ELEMENTS(pThis->abIntPriority));
1080 Assert(pThis->idxRunningPriority < RT_ELEMENTS(pThis->abRunningPriorities) - 1);
1081
1082 LogFlowFunc(("Dropping interrupt priority from %u -> %u (idxRunningPriority: %u -> %u)\n",
1083 pThis->abRunningPriorities[pThis->idxRunningPriority],
1084 pThis->abIntPriority[idxIntPending],
1085 pThis->idxRunningPriority, pThis->idxRunningPriority + 1));
1086
1087 pThis->abRunningPriorities[++pThis->idxRunningPriority] = pThis->abIntPriority[idxIntPending];
1088
1089 /* Clear edge level interrupts like SGIs as pending. */
1090 if (idxIntPending <= GIC_INTID_RANGE_SGI_LAST)
1091 ASMAtomicBitClear(&pThis->bmIntPending, idxIntPending);
1092 *pu64Value = idxIntPending;
1093 gicReDistUpdateIrqState(pThis, pVCpu);
1094 }
1095 else
1096 {
1097 /** @todo This is wrong as the guest might decide to prioritize PPIs and SPIs differently. */
1098 bmIntActive = ASMAtomicReadU32(&pGicDev->bmIntActive);
1099 bmIntEnabled = ASMAtomicReadU32(&pGicDev->bmIntEnabled);
1100 bmPending = (ASMAtomicReadU32(&pGicDev->bmIntPending) & bmIntEnabled) & ~bmIntActive;
1101 idxIntPending = ASMBitFirstSet(&bmPending, sizeof(bmPending) * 8);
1102 if ( idxIntPending > -1
1103 && pGicDev->abIntPriority[idxIntPending] < pThis->bInterruptPriority)
1104 {
1105 /* Mark the interrupt as active. */
1106 ASMAtomicOrU32(&pGicDev->bmIntActive, RT_BIT_32(idxIntPending));
1107
1108 /* Drop priority. */
1109 Assert((uint32_t)idxIntPending < RT_ELEMENTS(pGicDev->abIntPriority));
1110 Assert(pThis->idxRunningPriority < RT_ELEMENTS(pThis->abRunningPriorities) - 1);
1111
1112 LogFlowFunc(("Dropping interrupt priority from %u -> %u (idxRunningPriority: %u -> %u)\n",
1113 pThis->abRunningPriorities[pThis->idxRunningPriority],
1114 pThis->abIntPriority[idxIntPending],
1115 pThis->idxRunningPriority, pThis->idxRunningPriority + 1));
1116
1117 pThis->abRunningPriorities[++pThis->idxRunningPriority] = pGicDev->abIntPriority[idxIntPending];
1118
1119 *pu64Value = idxIntPending + GIC_INTID_RANGE_SPI_START;
1120 gicReDistUpdateIrqState(pThis, pVCpu);
1121 }
1122 else
1123 *pu64Value = GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT;
1124 }
1125 break;
1126 }
1127 case ARMV8_AARCH64_SYSREG_ICC_EOIR1_EL1:
1128 AssertReleaseFailed();
1129 break;
1130 case ARMV8_AARCH64_SYSREG_ICC_HPPIR1_EL1:
1131 {
1132 /** @todo Figure out the highest priority interrupt. */
1133 uint32_t bmIntActive = ASMAtomicReadU32(&pThis->bmIntActive);
1134 uint32_t bmIntEnabled = ASMAtomicReadU32(&pThis->bmIntEnabled);
1135 uint32_t bmPending = (ASMAtomicReadU32(&pThis->bmIntPending) & bmIntEnabled) & ~bmIntActive;
1136 int32_t idxIntPending = ASMBitFirstSet(&bmPending, sizeof(bmPending) * 8);
1137 if (idxIntPending > -1)
1138 *pu64Value = idxIntPending;
1139 else
1140 {
1141 /** @todo This is wrong as the guest might decide to prioritize PPIs and SPIs differently. */
1142 bmIntActive = ASMAtomicReadU32(&pGicDev->bmIntActive);
1143 bmIntEnabled = ASMAtomicReadU32(&pGicDev->bmIntEnabled);
1144 bmPending = (ASMAtomicReadU32(&pGicDev->bmIntPending) & bmIntEnabled) & ~bmIntActive;
1145 idxIntPending = ASMBitFirstSet(&bmPending, sizeof(bmPending) * 8);
1146 if (idxIntPending > -1)
1147 *pu64Value = idxIntPending + GIC_INTID_RANGE_SPI_START;
1148 else
1149 *pu64Value = GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT;
1150 }
1151 break;
1152 }
1153 case ARMV8_AARCH64_SYSREG_ICC_BPR1_EL1:
1154 *pu64Value = ARMV8_ICC_BPR1_EL1_AARCH64_BINARYPOINT_SET(pThis->bBinaryPointGrp1);
1155 break;
1156 case ARMV8_AARCH64_SYSREG_ICC_CTLR_EL1:
1157 *pu64Value = ARMV8_ICC_CTLR_EL1_AARCH64_PMHE
1158 | ARMV8_ICC_CTLR_EL1_AARCH64_PRIBITS_SET(4)
1159 | ARMV8_ICC_CTLR_EL1_AARCH64_IDBITS_SET(ARMV8_ICC_CTLR_EL1_AARCH64_IDBITS_16BITS);
1160 break;
1161 case ARMV8_AARCH64_SYSREG_ICC_SRE_EL1:
1162 AssertReleaseFailed();
1163 break;
1164 case ARMV8_AARCH64_SYSREG_ICC_IGRPEN0_EL1:
1165 *pu64Value = ASMAtomicReadBool(&pThis->fIrqGrp0Enabled) ? ARMV8_ICC_IGRPEN0_EL1_AARCH64_ENABLE : 0;
1166 break;
1167 case ARMV8_AARCH64_SYSREG_ICC_IGRPEN1_EL1:
1168 *pu64Value = ASMAtomicReadBool(&pThis->fIrqGrp1Enabled) ? ARMV8_ICC_IGRPEN1_EL1_AARCH64_ENABLE : 0;
1169 break;
1170 default:
1171 AssertReleaseFailed();
1172 break;
1173 }
1174
1175 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
1176
1177 LogFlowFunc(("pVCpu=%p u32Reg=%#x{%s} pu64Value=%RX64\n", pVCpu, u32Reg, gicIccRegisterStringify(u32Reg), *pu64Value));
1178 return VINF_SUCCESS;
1179}
1180
1181
1182/**
1183 * @interface_method_impl{PDMGICBACKEND,pfnWriteSysReg}
1184 */
1185static DECLCALLBACK(VBOXSTRICTRC) gicWriteSysReg(PVMCPUCC pVCpu, uint32_t u32Reg, uint64_t u64Value)
1186{
1187 /*
1188 * Validate.
1189 */
1190 VMCPU_ASSERT_EMT(pVCpu);
1191 LogFlowFunc(("pVCpu=%p u32Reg=%#x{%s} u64Value=%RX64\n", pVCpu, u32Reg, gicIccRegisterStringify(u32Reg), u64Value));
1192
1193 PGICCPU pThis = VMCPU_TO_GICCPU(pVCpu);
1194 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
1195 PGICDEV pGicDev = PDMDEVINS_2_DATA(pDevIns, PGICDEV);
1196
1197 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
1198 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
1199
1200 switch (u32Reg)
1201 {
1202 case ARMV8_AARCH64_SYSREG_ICC_PMR_EL1:
1203 LogFlowFunc(("ICC_PMR_EL1: Interrupt priority now %u\n", (uint8_t)u64Value));
1204 ASMAtomicWriteU8(&pThis->bInterruptPriority, (uint8_t)u64Value);
1205 gicReDistUpdateIrqState(pThis, pVCpu);
1206 break;
1207 case ARMV8_AARCH64_SYSREG_ICC_IAR0_EL1:
1208 AssertReleaseFailed();
1209 break;
1210 case ARMV8_AARCH64_SYSREG_ICC_EOIR0_EL1:
1211 AssertReleaseFailed();
1212 break;
1213 case ARMV8_AARCH64_SYSREG_ICC_HPPIR0_EL1:
1214 AssertReleaseFailed();
1215 break;
1216 case ARMV8_AARCH64_SYSREG_ICC_BPR0_EL1:
1217 pThis->bBinaryPointGrp0 = (uint8_t)ARMV8_ICC_BPR0_EL1_AARCH64_BINARYPOINT_GET(u64Value);
1218 break;
1219 case ARMV8_AARCH64_SYSREG_ICC_AP0R0_EL1:
1220 /** @todo */
1221 break;
1222 case ARMV8_AARCH64_SYSREG_ICC_AP0R1_EL1:
1223 AssertReleaseFailed();
1224 break;
1225 case ARMV8_AARCH64_SYSREG_ICC_AP0R2_EL1:
1226 AssertReleaseFailed();
1227 break;
1228 case ARMV8_AARCH64_SYSREG_ICC_AP0R3_EL1:
1229 AssertReleaseFailed();
1230 break;
1231 case ARMV8_AARCH64_SYSREG_ICC_AP1R0_EL1:
1232 /** @todo */
1233 break;
1234 case ARMV8_AARCH64_SYSREG_ICC_AP1R1_EL1:
1235 AssertReleaseFailed();
1236 break;
1237 case ARMV8_AARCH64_SYSREG_ICC_AP1R2_EL1:
1238 AssertReleaseFailed();
1239 break;
1240 case ARMV8_AARCH64_SYSREG_ICC_AP1R3_EL1:
1241 AssertReleaseFailed();
1242 break;
1243 case ARMV8_AARCH64_SYSREG_ICC_NMIAR1_EL1:
1244 AssertReleaseFailed();
1245 break;
1246 case ARMV8_AARCH64_SYSREG_ICC_DIR_EL1:
1247 AssertReleaseFailed();
1248 break;
1249 case ARMV8_AARCH64_SYSREG_ICC_RPR_EL1:
1250 AssertReleaseFailed();
1251 break;
1252 case ARMV8_AARCH64_SYSREG_ICC_SGI1R_EL1:
1253 {
1254 uint32_t uIntId = ARMV8_ICC_SGI1R_EL1_AARCH64_INTID_GET(u64Value) - GIC_INTID_RANGE_SGI_START;
1255 if (u64Value & ARMV8_ICC_SGI1R_EL1_AARCH64_IRM)
1256 {
1257 /* Route to all but this vCPU. */
1258 for (uint32_t i = 0; i < pVCpu->pVMR3->cCpus; i++)
1259 {
1260 if (i != pVCpu->idCpu)
1261 {
1262 PVMCPUCC pVCpuDst = VMMGetCpuById(pVCpu->CTX_SUFF(pVM), i);
1263 if (pVCpuDst)
1264 gicSetSgi(pVCpuDst, uIntId, true /*fAsserted*/);
1265 else
1266 AssertFailed();
1267 }
1268 }
1269 }
1270 else
1271 {
1272 /* Examine target list. */
1273 /** @todo Range selector support. */
1274 VMCPUID idCpu = 0;
1275 uint16_t uTgtList = ARMV8_ICC_SGI1R_EL1_AARCH64_TARGET_LIST_GET(u64Value);
1276 /** @todo rewrite using ASMBitFirstSetU16. */
1277 while (uTgtList)
1278 {
1279 if (uTgtList & 0x1)
1280 {
1281 PVMCPUCC pVCpuDst = VMMGetCpuById(pVCpu->CTX_SUFF(pVM), idCpu);
1282 if (pVCpuDst)
1283 gicSetSgi(pVCpuDst, uIntId, true /*fAsserted*/);
1284 else
1285 AssertFailed();
1286 }
1287 uTgtList >>= 1;
1288 idCpu++;
1289 }
1290 }
1291 break;
1292 }
1293 case ARMV8_AARCH64_SYSREG_ICC_ASGI1R_EL1:
1294 AssertReleaseFailed();
1295 break;
1296 case ARMV8_AARCH64_SYSREG_ICC_SGI0R_EL1:
1297 AssertReleaseFailed();
1298 break;
1299 case ARMV8_AARCH64_SYSREG_ICC_IAR1_EL1:
1300 AssertReleaseFailed();
1301 break;
1302 case ARMV8_AARCH64_SYSREG_ICC_EOIR1_EL1:
1303 {
1304 /* Mark the interrupt as not active anymore, though it might still be pending. */
1305 if (u64Value < GIC_INTID_RANGE_SPI_START)
1306 ASMAtomicAndU32(&pThis->bmIntActive, ~RT_BIT_32((uint32_t)u64Value));
1307 else
1308 ASMAtomicAndU32(&pGicDev->bmIntActive, ~RT_BIT_32((uint32_t)(u64Value - GIC_INTID_RANGE_SPI_START)));
1309
1310 /* Restore previous interrupt priority. */
1311 Assert(pThis->idxRunningPriority > 0);
1312 if (RT_LIKELY(pThis->idxRunningPriority))
1313 {
1314 LogFlowFunc(("Restoring interrupt priority from %u -> %u (idxRunningPriority: %u -> %u)\n",
1315 pThis->abRunningPriorities[pThis->idxRunningPriority],
1316 pThis->abRunningPriorities[pThis->idxRunningPriority - 1],
1317 pThis->idxRunningPriority, pThis->idxRunningPriority - 1));
1318 pThis->idxRunningPriority--;
1319 }
1320 gicReDistUpdateIrqState(pThis, pVCpu);
1321 break;
1322 }
1323 case ARMV8_AARCH64_SYSREG_ICC_HPPIR1_EL1:
1324 AssertReleaseFailed();
1325 break;
1326 case ARMV8_AARCH64_SYSREG_ICC_BPR1_EL1:
1327 pThis->bBinaryPointGrp1 = (uint8_t)ARMV8_ICC_BPR1_EL1_AARCH64_BINARYPOINT_GET(u64Value);
1328 break;
1329 case ARMV8_AARCH64_SYSREG_ICC_CTLR_EL1:
1330 u64Value &= ARMV8_ICC_CTLR_EL1_RW;
1331 /** @todo */
1332 break;
1333 case ARMV8_AARCH64_SYSREG_ICC_SRE_EL1:
1334 AssertReleaseFailed();
1335 break;
1336 case ARMV8_AARCH64_SYSREG_ICC_IGRPEN0_EL1:
1337 ASMAtomicWriteBool(&pThis->fIrqGrp0Enabled, RT_BOOL(u64Value & ARMV8_ICC_IGRPEN0_EL1_AARCH64_ENABLE));
1338 break;
1339 case ARMV8_AARCH64_SYSREG_ICC_IGRPEN1_EL1:
1340 ASMAtomicWriteBool(&pThis->fIrqGrp1Enabled, RT_BOOL(u64Value & ARMV8_ICC_IGRPEN1_EL1_AARCH64_ENABLE));
1341 break;
1342 default:
1343 AssertReleaseFailed();
1344 break;
1345 }
1346
1347 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
1348 return VINF_SUCCESS;
1349}
1350
1351
1352/**
1353 * Initializes per-VCPU GIC to the state following a power-up or hardware
1354 * reset.
1355 *
1356 * @param pVCpu The cross context virtual CPU structure.
1357 */
1358DECLHIDDEN(void) gicResetCpu(PVMCPUCC pVCpu)
1359{
1360 LogFlowFunc(("GIC%u\n", pVCpu->idCpu));
1361 VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu);
1362
1363 memset((void *)&pVCpu->gic.s.abRunningPriorities[0], 0xff, sizeof(pVCpu->gic.s.abRunningPriorities));
1364 pVCpu->gic.s.idxRunningPriority = 0;
1365 pVCpu->gic.s.bInterruptPriority = 0; /* Means no interrupt gets through to the PE. */
1366}
1367
1368
1369/**
1370 * @callback_method_impl{FNIOMMMIONEWREAD}
1371 */
1372DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicDistMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
1373{
1374 NOREF(pvUser);
1375 Assert(!(off & 0x3));
1376 Assert(cb == 4); RT_NOREF_PV(cb);
1377
1378 PVMCPUCC pVCpu = PDMDevHlpGetVMCPU(pDevIns);
1379 uint16_t offReg = off & 0xfffc;
1380 uint32_t uValue = 0;
1381
1382 STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatMmioRead));
1383
1384 VBOXSTRICTRC rc = VBOXSTRICTRC_VAL(gicDistRegisterRead(pDevIns, pVCpu, offReg, &uValue));
1385 *(uint32_t *)pv = uValue;
1386
1387 Log2(("GIC%u: gicDistMmioRead: offReg=%#RX16 uValue=%#RX32\n", pVCpu->idCpu, offReg, uValue));
1388 return rc;
1389}
1390
1391
1392/**
1393 * @callback_method_impl{FNIOMMMIONEWWRITE}
1394 */
1395DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicDistMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
1396{
1397 NOREF(pvUser);
1398 Assert(!(off & 0x3));
1399 Assert(cb == 4); RT_NOREF_PV(cb);
1400
1401 PVMCPUCC pVCpu = PDMDevHlpGetVMCPU(pDevIns);
1402 uint16_t offReg = off & 0xfffc;
1403 uint32_t uValue = *(uint32_t *)pv;
1404
1405 STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatMmioWrite));
1406
1407 Log2(("GIC%u: gicDistMmioWrite: offReg=%#RX16 uValue=%#RX32\n", pVCpu->idCpu, offReg, uValue));
1408 return gicDistRegisterWrite(pDevIns, pVCpu, offReg, uValue);
1409}
1410
1411
1412/**
1413 * @callback_method_impl{FNIOMMMIONEWREAD}
1414 */
1415DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicReDistMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
1416{
1417 NOREF(pvUser);
1418 Assert(!(off & 0x3));
1419 Assert(cb == 4); RT_NOREF_PV(cb);
1420
1421 /*
1422 * Determine the redistributor being targeted. Each redistributor takes
1423 * GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE bytes
1424 * and the redistributors are adjacent.
1425 */
1426 uint32_t idReDist = off / (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
1427 off %= (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
1428
1429 PVMCC pVM = PDMDevHlpGetVM(pDevIns);
1430 Assert(idReDist < pVM->cCpus);
1431 PVMCPUCC pVCpu = pVM->apCpusR3[idReDist];
1432
1433 STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatMmioRead));
1434
1435 /* Redistributor or SGI/PPI frame? */
1436 uint16_t offReg = off & 0xfffc;
1437 uint32_t uValue = 0;
1438 VBOXSTRICTRC rcStrict;
1439 if (off < GIC_REDIST_REG_FRAME_SIZE)
1440 rcStrict = gicReDistRegisterRead(pDevIns, pVCpu, idReDist, offReg, &uValue);
1441 else
1442 rcStrict = gicReDistSgiPpiRegisterRead(pDevIns, pVCpu, offReg, &uValue);
1443
1444 *(uint32_t *)pv = uValue;
1445 Log2(("GICReDist%u: gicReDistMmioRead: off=%RGp idReDist=%u offReg=%#RX16 uValue=%#RX32 -> %Rrc\n",
1446 pVCpu->idCpu, off, idReDist, offReg, uValue, VBOXSTRICTRC_VAL(rcStrict)));
1447 return rcStrict;
1448}
1449
1450
1451/**
1452 * @callback_method_impl{FNIOMMMIONEWWRITE}
1453 */
1454DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicReDistMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
1455{
1456 NOREF(pvUser);
1457 Assert(!(off & 0x3));
1458 Assert(cb == 4); RT_NOREF_PV(cb);
1459
1460 uint32_t uValue = *(uint32_t *)pv;
1461
1462 /*
1463 * Determine the redistributor being targeted. Each redistributor takes
1464 * GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE bytes
1465 * and the redistributors are adjacent.
1466 */
1467 uint32_t idReDist = off / (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
1468 off %= (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
1469
1470 PVMCC pVM = PDMDevHlpGetVM(pDevIns);
1471 Assert(idReDist < pVM->cCpus);
1472 PVMCPUCC pVCpu = pVM->apCpusR3[idReDist];
1473
1474 STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatMmioWrite));
1475
1476 /* Redistributor or SGI/PPI frame? */
1477 uint16_t offReg = off & 0xfffc;
1478 VBOXSTRICTRC rcStrict;
1479 if (off < GIC_REDIST_REG_FRAME_SIZE)
1480 rcStrict = gicReDistRegisterWrite(pDevIns, pVCpu, offReg, uValue);
1481 else
1482 rcStrict = gicReDistSgiPpiRegisterWrite(pDevIns, pVCpu, offReg, uValue);
1483
1484 Log2(("GICReDist%u: gicReDistMmioWrite: off=%RGp idReDist=%u offReg=%#RX16 uValue=%#RX32 -> %Rrc\n",
1485 pVCpu->idCpu, off, idReDist, offReg, uValue, VBOXSTRICTRC_VAL(rcStrict)));
1486 return rcStrict;
1487}
1488
1489
1490#ifndef IN_RING3
1491
1492/**
1493 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
1494 */
1495static DECLCALLBACK(int) gicRZConstruct(PPDMDEVINS pDevIns)
1496{
1497 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1498 AssertReleaseFailed();
1499 return VINF_SUCCESS;
1500}
1501#endif /* !IN_RING3 */
1502
1503/**
1504 * GIC device registration structure.
1505 */
1506const PDMDEVREG g_DeviceGIC =
1507{
1508 /* .u32Version = */ PDM_DEVREG_VERSION,
1509 /* .uReserved0 = */ 0,
1510 /* .szName = */ "gic",
1511 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
1512 /* .fClass = */ PDM_DEVREG_CLASS_PIC,
1513 /* .cMaxInstances = */ 1,
1514 /* .uSharedVersion = */ 42,
1515 /* .cbInstanceShared = */ sizeof(GICDEV),
1516 /* .cbInstanceCC = */ 0,
1517 /* .cbInstanceRC = */ 0,
1518 /* .cMaxPciDevices = */ 0,
1519 /* .cMaxMsixVectors = */ 0,
1520 /* .pszDescription = */ "Generic Interrupt Controller",
1521#if defined(IN_RING3)
1522 /* .szRCMod = */ "VMMRC.rc",
1523 /* .szR0Mod = */ "VMMR0.r0",
1524 /* .pfnConstruct = */ gicR3Construct,
1525 /* .pfnDestruct = */ gicR3Destruct,
1526 /* .pfnRelocate = */ gicR3Relocate,
1527 /* .pfnMemSetup = */ NULL,
1528 /* .pfnPowerOn = */ NULL,
1529 /* .pfnReset = */ gicR3Reset,
1530 /* .pfnSuspend = */ NULL,
1531 /* .pfnResume = */ NULL,
1532 /* .pfnAttach = */ NULL,
1533 /* .pfnDetach = */ NULL,
1534 /* .pfnQueryInterface = */ NULL,
1535 /* .pfnInitComplete = */ NULL,
1536 /* .pfnPowerOff = */ NULL,
1537 /* .pfnSoftReset = */ NULL,
1538 /* .pfnReserved0 = */ NULL,
1539 /* .pfnReserved1 = */ NULL,
1540 /* .pfnReserved2 = */ NULL,
1541 /* .pfnReserved3 = */ NULL,
1542 /* .pfnReserved4 = */ NULL,
1543 /* .pfnReserved5 = */ NULL,
1544 /* .pfnReserved6 = */ NULL,
1545 /* .pfnReserved7 = */ NULL,
1546#elif defined(IN_RING0)
1547 /* .pfnEarlyConstruct = */ NULL,
1548 /* .pfnConstruct = */ gicRZConstruct,
1549 /* .pfnDestruct = */ NULL,
1550 /* .pfnFinalDestruct = */ NULL,
1551 /* .pfnRequest = */ NULL,
1552 /* .pfnReserved0 = */ NULL,
1553 /* .pfnReserved1 = */ NULL,
1554 /* .pfnReserved2 = */ NULL,
1555 /* .pfnReserved3 = */ NULL,
1556 /* .pfnReserved4 = */ NULL,
1557 /* .pfnReserved5 = */ NULL,
1558 /* .pfnReserved6 = */ NULL,
1559 /* .pfnReserved7 = */ NULL,
1560#elif defined(IN_RC)
1561 /* .pfnConstruct = */ gicRZConstruct,
1562 /* .pfnReserved0 = */ NULL,
1563 /* .pfnReserved1 = */ NULL,
1564 /* .pfnReserved2 = */ NULL,
1565 /* .pfnReserved3 = */ NULL,
1566 /* .pfnReserved4 = */ NULL,
1567 /* .pfnReserved5 = */ NULL,
1568 /* .pfnReserved6 = */ NULL,
1569 /* .pfnReserved7 = */ NULL,
1570#else
1571# error "Not in IN_RING3, IN_RING0 or IN_RC!"
1572#endif
1573 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
1574};
1575
1576/**
1577 * The VirtualBox GIC backend.
1578 */
1579const PDMGICBACKEND g_GicBackend =
1580{
1581 /* .pfnReadSysReg = */ gicReadSysReg,
1582 /* .pfnWriteSysReg = */ gicWriteSysReg,
1583 /* .pfnSetSpi = */ gicSetSpi,
1584 /* .pfnSetPpi = */ gicSetPpi,
1585};
1586
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