VirtualBox

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

Last change on this file since 107631 was 107308, checked in by vboxsync, 5 weeks ago

VMM: bugref:10759 Refactor GIC for use with different backends.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette