VirtualBox

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

Last change on this file since 99748 was 99737, checked in by vboxsync, 22 months ago

VMM/GIC: Updates to the implementation, implement forwarding of SGIs and PPIs, bugref:10404 [doxygen]

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.6 KB
Line 
1/* $Id: GICAll.cpp 99737 2023-05-10 17:48:08Z vboxsync $ */
2/** @file
3 * GIC - Generic Interrupt Controller Architecture (GICv3) - All Contexts.
4 */
5
6/*
7 * Copyright (C) 2023 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/gic.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/**
55 * Sets the interrupt pending force-flag and pokes the EMT if required.
56 *
57 * @param pVCpu The cross context virtual CPU structure.
58 * @param fIrq Flag whether to assert the IRQ line or leave it alone.
59 * @param fFiq Flag whether to assert the FIQ line or leave it alone.
60 */
61static void gicSetInterruptFF(PVMCPUCC pVCpu, bool fIrq, bool fFiq)
62{
63 Assert(fIrq || fFiq);
64
65#ifdef IN_RING3
66 /* IRQ state should be loaded as-is by "LoadExec". Changes can be made from LoadDone. */
67 Assert(pVCpu->pVMR3->enmVMState != VMSTATE_LOADING || PDMR3HasLoadedState(pVCpu->pVMR3));
68#endif
69
70 if (fIrq)
71 VMCPU_FF_SET(pVCpu, VMCPU_FF_INTERRUPT_IRQ);
72 if (fFiq)
73 VMCPU_FF_SET(pVCpu, VMCPU_FF_INTERRUPT_FIQ);
74
75 /*
76 * We need to wake up the target CPU if we're not on EMT.
77 */
78 /** @todo We could just use RTThreadNativeSelf() here, couldn't we? */
79#if defined(IN_RING0)
80 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
81 VMCPUID idCpu = pVCpu->idCpu;
82 if (VMMGetCpuId(pVM) != idCpu)
83 {
84 switch (VMCPU_GET_STATE(pVCpu))
85 {
86 case VMCPUSTATE_STARTED_EXEC:
87 Log7Func(("idCpu=%u VMCPUSTATE_STARTED_EXEC\n", idCpu));
88 GVMMR0SchedPokeNoGVMNoLock(pVM, idCpu);
89 break;
90
91 case VMCPUSTATE_STARTED_HALTED:
92 Log7Func(("idCpu=%u VMCPUSTATE_STARTED_HALTED\n", idCpu));
93 GVMMR0SchedWakeUpNoGVMNoLock(pVM, idCpu);
94 break;
95
96 default:
97 Log7Func(("idCpu=%u enmState=%d\n", idCpu, pVCpu->enmState));
98 break; /* nothing to do in other states. */
99 }
100 }
101#elif defined(IN_RING3)
102 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
103 VMCPUID idCpu = pVCpu->idCpu;
104 if (VMMGetCpuId(pVM) != idCpu)
105 {
106 Log7Func(("idCpu=%u enmState=%d\n", idCpu, pVCpu->enmState));
107 VMR3NotifyCpuFFU(pVCpu->pUVCpu, VMNOTIFYFF_FLAGS_POKE);
108 }
109#endif
110}
111
112
113/**
114 * Clears the interrupt pending force-flag.
115 *
116 * @param pVCpu The cross context virtual CPU structure.
117 * @param fIrq Flag whether to clear the IRQ flag.
118 * @param fFiq Flag whether to clear the FIQ flag.
119 */
120DECLINLINE(void) gicClearInterruptFF(PVMCPUCC pVCpu, bool fIrq, bool fFiq)
121{
122 Assert(fIrq || fFiq);
123
124#ifdef IN_RING3
125 /* IRQ state should be loaded as-is by "LoadExec". Changes can be made from LoadDone. */
126 Assert(pVCpu->pVMR3->enmVMState != VMSTATE_LOADING || PDMR3HasLoadedState(pVCpu->pVMR3));
127#endif
128
129 if (fIrq)
130 VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_IRQ);
131 if (fFiq)
132 VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_FIQ);
133}
134
135
136/**
137 * Updates the internal IRQ state and sets or clears the appropirate force action flags.
138 *
139 * @returns Strict VBox status code.
140 * @param pThis The GIC re-distributor state for the associated vCPU.
141 * @param pVCpu The cross context virtual CPU structure.
142 */
143static VBOXSTRICTRC gicReDistUpdateIrqState(PGICCPU pThis, PVMCPUCC pVCpu)
144{
145 /* Read the interrupt state. */
146 uint32_t u32RegIGrp0 = ASMAtomicReadU32(&pThis->u32RegIGrp0);
147 uint32_t bmIntEnabled = ASMAtomicReadU32(&pThis->bmIntEnabled);
148 uint32_t bmIntPending = ASMAtomicReadU32(&pThis->bmIntPending);
149 uint32_t bmIntActive = ASMAtomicReadU32(&pThis->bmIntActive);
150 bool fIrqGrp0Enabled = ASMAtomicReadBool(&pThis->fIrqGrp0Enabled);
151 bool fIrqGrp1Enabled = ASMAtomicReadBool(&pThis->fIrqGrp1Enabled);
152
153 /* Is anything enabled at all? */
154 uint32_t bmIntForward = (bmIntPending & bmIntEnabled) & ~bmIntActive; /* Exclude the currently active interrupt. */
155 if (bmIntForward)
156 {
157 /* Determine whether we have to assert the IRQ or FIQ line. */
158 bool fIrq = RT_BOOL(bmIntForward & u32RegIGrp0) && fIrqGrp1Enabled;
159 bool fFiq = RT_BOOL(bmIntForward & ~u32RegIGrp0) && fIrqGrp0Enabled;
160
161 if (fIrq || fFiq)
162 gicSetInterruptFF(pVCpu, fIrq, fFiq);
163
164 if (!fIrq || !fFiq)
165 gicClearInterruptFF(pVCpu, !fIrq, !fFiq);
166 }
167 else
168 gicClearInterruptFF(pVCpu, true /*fIrq*/, true /*fFiq*/);
169
170 return VINF_SUCCESS;
171}
172
173
174/**
175 * Sets the given SGI/PPI interrupt ID on the re-distributor of the given vCPU.
176 *
177 * @returns VBox status code.
178 * @param pVCpu The cross context virtual CPU structure.
179 * @param uIntId The SGI/PPI interrupt identifier.
180 * @param fAsserted Flag whether the SGI/PPI interrupt is asserted or not.
181 */
182static int gicReDistInterruptSet(PVMCPUCC pVCpu, uint32_t uIntId, bool fAsserted)
183{
184 PGICCPU pThis = VMCPU_TO_GICCPU(pVCpu);
185
186 /* Update the interrupts pending state. */
187 if (fAsserted)
188 ASMAtomicOrU32(&pThis->bmIntPending, RT_BIT_32(uIntId));
189 else
190 ASMAtomicAndU32(&pThis->bmIntPending, ~RT_BIT_32(uIntId));
191
192 return VBOXSTRICTRC_VAL(gicReDistUpdateIrqState(pThis, pVCpu));
193}
194
195
196/**
197 * Reads a GIC distributor register.
198 *
199 * @returns VBox status code.
200 * @param pDevIns The device instance.
201 * @param pVCpu The cross context virtual CPU structure.
202 * @param offReg The offset of the register being read.
203 * @param puValue Where to store the register value.
204 */
205DECLINLINE(VBOXSTRICTRC) gicDistRegisterRead(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t *puValue)
206{
207 VMCPU_ASSERT_EMT(pVCpu);
208 RT_NOREF(pDevIns, pVCpu, offReg);
209
210 switch (offReg)
211 {
212 case GIC_DIST_REG_TYPER_OFF:
213 *puValue = GIC_DIST_REG_TYPER_NUM_ITLINES_SET(1) /** @todo 32 SPIs for now. */
214 | GIC_DIST_REG_TYPER_NUM_PES_SET(0) /* 1 PE */
215 /*| GIC_DIST_REG_TYPER_ESPI*/ /** @todo */
216 /*| GIC_DIST_REG_TYPER_NMI*/ /** @todo Non-maskable interrupts */
217 /*| GIC_DIST_REG_TYPER_SECURITY_EXTN */ /** @todo */
218 /*| GIC_DIST_REG_TYPER_MBIS */ /** @todo Message based interrupts */
219 /*| GIC_DIST_REG_TYPER_LPIS */ /** @todo Support LPIs */
220 | GIC_DIST_REG_TYPER_IDBITS_SET(16);
221 break;
222 case GIC_DIST_REG_PIDR2_OFF:
223 *puValue = GIC_REDIST_REG_PIDR2_ARCH_REV_SET(GIC_REDIST_REG_PIDR2_ARCH_REV_GICV3);
224 break;
225 case GIC_DIST_REG_IIDR_OFF:
226 case GIC_DIST_REG_TYPER2_OFF:
227 default:
228 *puValue = 0;
229 }
230 return VINF_SUCCESS;
231}
232
233
234/**
235 * Writes a GIC distributor register.
236 *
237 * @returns Strict VBox status code.
238 * @param pDevIns The device instance.
239 * @param pVCpu The cross context virtual CPU structure.
240 * @param offReg The offset of the register being written.
241 * @param uValue The register value.
242 */
243DECLINLINE(VBOXSTRICTRC) gicDistRegisterWrite(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t uValue)
244{
245 VMCPU_ASSERT_EMT(pVCpu);
246 RT_NOREF(pDevIns, pVCpu, offReg, uValue);
247
248 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
249 return rcStrict;
250}
251
252
253/**
254 * Reads a GIC redistributor register.
255 *
256 * @returns VBox status code.
257 * @param pDevIns The device instance.
258 * @param pVCpu The cross context virtual CPU structure.
259 * @param offReg The offset of the register being read.
260 * @param puValue Where to store the register value.
261 */
262DECLINLINE(VBOXSTRICTRC) gicReDistRegisterRead(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t *puValue)
263{
264 VMCPU_ASSERT_EMT(pVCpu);
265 RT_NOREF(pDevIns, pVCpu, offReg);
266
267 switch (offReg)
268 {
269 case GIC_REDIST_REG_PIDR2_OFF:
270 *puValue = GIC_REDIST_REG_PIDR2_ARCH_REV_SET(GIC_REDIST_REG_PIDR2_ARCH_REV_GICV3);
271 break;
272 default:
273 *puValue = 0;
274 }
275
276 return VINF_SUCCESS;
277}
278
279
280/**
281 * Reads a GIC redistributor SGI/PPI frame register.
282 *
283 * @returns VBox status code.
284 * @param pDevIns The device instance.
285 * @param pVCpu The cross context virtual CPU structure.
286 * @param offReg The offset of the register being read.
287 * @param puValue Where to store the register value.
288 */
289DECLINLINE(VBOXSTRICTRC) gicReDistSgiPpiRegisterRead(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t *puValue)
290{
291 VMCPU_ASSERT_EMT(pVCpu);
292 RT_NOREF(pDevIns);
293
294 PGICCPU pThis = VMCPU_TO_GICCPU(pVCpu);
295 switch (offReg)
296 {
297 case GIC_REDIST_SGI_PPI_REG_ISENABLER0_OFF:
298 case GIC_REDIST_SGI_PPI_REG_ICENABLER0_OFF:
299 *puValue = ASMAtomicReadU32(&pThis->bmIntEnabled);
300 break;
301 case GIC_REDIST_SGI_PPI_REG_ISPENDR0_OFF:
302 case GIC_REDIST_SGI_PPI_REG_ICPENDR0_OFF:
303 *puValue = ASMAtomicReadU32(&pThis->bmIntPending);
304 break;
305 case GIC_REDIST_SGI_PPI_REG_ISACTIVER0_OFF:
306 case GIC_REDIST_SGI_PPI_REG_ICACTIVER0_OFF:
307 *puValue = ASMAtomicReadU32(&pThis->bmIntActive);
308 break;
309 case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START:
310 case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 4:
311 case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 8:
312 case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 12:
313 case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 16:
314 case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 20:
315 case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 24:
316 case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 28:
317 {
318 /* Figure out the register whch is written. */
319 uint8_t idxPrio = offReg - GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START;
320 Assert(idxPrio <= RT_ELEMENTS(pThis->abIntPriority) - sizeof(uint32_t));
321
322 uint32_t u32Value = 0;
323 for (uint32_t i = idxPrio; i < idxPrio + sizeof(uint32_t); i++)
324 u32Value |= pThis->abIntPriority[i] << ((i - idxPrio) * 8);
325
326 *puValue = u32Value;
327 break;
328 }
329 case GIC_REDIST_SGI_PPI_REG_ICFGR0_OFF:
330 *puValue = ASMAtomicReadU32(&pThis->u32RegICfg0);
331 break;
332 case GIC_REDIST_SGI_PPI_REG_ICFGR1_OFF:
333 *puValue = ASMAtomicReadU32(&pThis->u32RegICfg1);
334 break;
335 default:
336 AssertReleaseFailed();
337 *puValue = 0;
338 }
339
340 return VINF_SUCCESS;
341}
342
343
344/**
345 * Writes a GIC redistributor frame register.
346 *
347 * @returns Strict VBox status code.
348 * @param pDevIns The device instance.
349 * @param pVCpu The cross context virtual CPU structure.
350 * @param offReg The offset of the register being written.
351 * @param uValue The register value.
352 */
353DECLINLINE(VBOXSTRICTRC) gicReDistRegisterWrite(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t uValue)
354{
355 VMCPU_ASSERT_EMT(pVCpu);
356 RT_NOREF(pDevIns, pVCpu, offReg, uValue);
357
358 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
359 return rcStrict;
360}
361
362
363/**
364 * Writes a GIC redistributor SGI/PPI frame register.
365 *
366 * @returns Strict VBox status code.
367 * @param pDevIns The device instance.
368 * @param pVCpu The cross context virtual CPU structure.
369 * @param offReg The offset of the register being written.
370 * @param uValue The register value.
371 */
372DECLINLINE(VBOXSTRICTRC) gicReDistSgiPpiRegisterWrite(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t uValue)
373{
374 VMCPU_ASSERT_EMT(pVCpu);
375 RT_NOREF(pDevIns);
376
377 PGICCPU pThis = VMCPU_TO_GICCPU(pVCpu);
378 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
379 switch (offReg)
380 {
381 case GIC_REDIST_SGI_PPI_REG_IGROUPR0_OFF:
382 ASMAtomicOrU32(&pThis->u32RegIGrp0, uValue);
383 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
384 break;
385 case GIC_REDIST_SGI_PPI_REG_ISENABLER0_OFF:
386 ASMAtomicOrU32(&pThis->bmIntEnabled, uValue);
387 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
388 break;
389 case GIC_REDIST_SGI_PPI_REG_ICENABLER0_OFF:
390 ASMAtomicAndU32(&pThis->bmIntEnabled, ~uValue);
391 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
392 break;
393 case GIC_REDIST_SGI_PPI_REG_ISPENDR0_OFF:
394 ASMAtomicOrU32(&pThis->bmIntPending, uValue);
395 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
396 break;
397 case GIC_REDIST_SGI_PPI_REG_ICPENDR0_OFF:
398 ASMAtomicAndU32(&pThis->bmIntPending, ~uValue);
399 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
400 break;
401 case GIC_REDIST_SGI_PPI_REG_ISACTIVER0_OFF:
402 ASMAtomicOrU32(&pThis->bmIntActive, uValue);
403 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
404 break;
405 case GIC_REDIST_SGI_PPI_REG_ICACTIVER0_OFF:
406 ASMAtomicAndU32(&pThis->bmIntActive, ~uValue);
407 rcStrict = gicReDistUpdateIrqState(pThis, pVCpu);
408 break;
409 case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START:
410 case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 4:
411 case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 8:
412 case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 12:
413 case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 16:
414 case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 20:
415 case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 24:
416 case GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START + 28:
417 {
418 /* Figure out the register whch is written. */
419 uint8_t idxPrio = offReg - GIC_REDIST_SGI_PPI_REG_IPRIORITYn_OFF_START;
420 Assert(idxPrio <= RT_ELEMENTS(pThis->abIntPriority) - sizeof(uint32_t));
421 for (uint32_t i = idxPrio; i < idxPrio + sizeof(uint32_t); i++)
422 {
423 pThis->abIntPriority[i] = (uint8_t)(uValue & 0xff);
424 uValue >>= 8;
425 }
426 break;
427 }
428 case GIC_REDIST_SGI_PPI_REG_ICFGR0_OFF:
429 ASMAtomicWriteU32(&pThis->u32RegICfg0, uValue);
430 break;
431 case GIC_REDIST_SGI_PPI_REG_ICFGR1_OFF:
432 ASMAtomicWriteU32(&pThis->u32RegICfg1, uValue);
433 break;
434 default:
435 AssertReleaseFailed();
436 }
437
438 return rcStrict;
439}
440
441
442/**
443 * Reads a GIC system register.
444 *
445 * @returns Strict VBox status code.
446 * @param pVCpu The cross context virtual CPU structure.
447 * @param u32Reg The system register being read.
448 * @param pu64Value Where to store the read value.
449 */
450VMM_INT_DECL(VBOXSTRICTRC) GICReadSysReg(PVMCPUCC pVCpu, uint32_t u32Reg, uint64_t *pu64Value)
451{
452 /*
453 * Validate.
454 */
455 VMCPU_ASSERT_EMT(pVCpu);
456 Assert(pu64Value);
457
458 *pu64Value = 0;
459 PGICCPU pThis = VMCPU_TO_GICCPU(pVCpu);
460 switch (u32Reg)
461 {
462 case ARMV8_AARCH64_SYSREG_ICC_PMR_EL1:
463 *pu64Value = pThis->bInterruptPriority;
464 break;
465 case ARMV8_AARCH64_SYSREG_ICC_IAR0_EL1:
466 AssertReleaseFailed();
467 break;
468 case ARMV8_AARCH64_SYSREG_ICC_EOIR0_EL1:
469 AssertReleaseFailed();
470 break;
471 case ARMV8_AARCH64_SYSREG_ICC_HPPIR0_EL1:
472 AssertReleaseFailed();
473 break;
474 case ARMV8_AARCH64_SYSREG_ICC_BPR0_EL1:
475 *pu64Value = pThis->bBinaryPointGrp0 & 0x7;
476 break;
477 case ARMV8_AARCH64_SYSREG_ICC_AP0R0_EL1:
478 AssertReleaseFailed();
479 break;
480 case ARMV8_AARCH64_SYSREG_ICC_AP0R1_EL1:
481 AssertReleaseFailed();
482 break;
483 case ARMV8_AARCH64_SYSREG_ICC_AP0R2_EL1:
484 AssertReleaseFailed();
485 break;
486 case ARMV8_AARCH64_SYSREG_ICC_AP0R3_EL1:
487 AssertReleaseFailed();
488 break;
489 case ARMV8_AARCH64_SYSREG_ICC_AP1R0_EL1:
490 AssertReleaseFailed();
491 break;
492 case ARMV8_AARCH64_SYSREG_ICC_AP1R1_EL1:
493 AssertReleaseFailed();
494 break;
495 case ARMV8_AARCH64_SYSREG_ICC_AP1R2_EL1:
496 AssertReleaseFailed();
497 break;
498 case ARMV8_AARCH64_SYSREG_ICC_AP1R3_EL1:
499 AssertReleaseFailed();
500 break;
501 case ARMV8_AARCH64_SYSREG_ICC_NMIAR1_EL1:
502 AssertReleaseFailed();
503 break;
504 case ARMV8_AARCH64_SYSREG_ICC_DIR_EL1:
505 AssertReleaseFailed();
506 break;
507 case ARMV8_AARCH64_SYSREG_ICC_RPR_EL1:
508 AssertReleaseFailed();
509 break;
510 case ARMV8_AARCH64_SYSREG_ICC_SGI1R_EL1:
511 AssertReleaseFailed();
512 break;
513 case ARMV8_AARCH64_SYSREG_ICC_ASGI1R_EL1:
514 AssertReleaseFailed();
515 break;
516 case ARMV8_AARCH64_SYSREG_ICC_SGI0R_EL1:
517 AssertReleaseFailed();
518 break;
519 case ARMV8_AARCH64_SYSREG_ICC_IAR1_EL1:
520 {
521 /** @todo Figure out the highest priority interrupt. */
522 uint32_t bmPending = ASMAtomicReadU32(&pThis->bmIntPending);
523 int32_t idxIntPending = ASMBitFirstSet(&bmPending, sizeof(bmPending) * 8);
524 if (idxIntPending > -1)
525 {
526 /* Mark the interrupt as active. */
527 ASMAtomicOrU32(&pThis->bmIntActive, idxIntPending);
528 *pu64Value = idxIntPending;
529 }
530 else
531 *pu64Value = GIC_INTID_RANGE_SPECIAL_NO_INTERRUPT;
532 break;
533 }
534 case ARMV8_AARCH64_SYSREG_ICC_EOIR1_EL1:
535 AssertReleaseFailed();
536 break;
537 case ARMV8_AARCH64_SYSREG_ICC_HPPIR1_EL1:
538 AssertReleaseFailed();
539 break;
540 case ARMV8_AARCH64_SYSREG_ICC_BPR1_EL1:
541 *pu64Value = pThis->bBinaryPointGrp1 & 0x7;
542 break;
543 case ARMV8_AARCH64_SYSREG_ICC_CTLR_EL1:
544 *pu64Value = ARMV8_ICC_CTLR_EL1_AARCH64_PMHE
545 | ARMV8_ICC_CTLR_EL1_AARCH64_PRIBITS_SET(4)
546 | ARMV8_ICC_CTLR_EL1_AARCH64_IDBITS_SET(ARMV8_ICC_CTLR_EL1_AARCH64_IDBITS_16BITS);
547 break;
548 case ARMV8_AARCH64_SYSREG_ICC_SRE_EL1:
549 AssertReleaseFailed();
550 break;
551 case ARMV8_AARCH64_SYSREG_ICC_IGRPEN0_EL1:
552 *pu64Value = ASMAtomicReadBool(&pThis->fIrqGrp0Enabled) ? ARMV8_ICC_IGRPEN0_EL1_AARCH64_ENABLE : 0;
553 break;
554 case ARMV8_AARCH64_SYSREG_ICC_IGRPEN1_EL1:
555 *pu64Value = ASMAtomicReadBool(&pThis->fIrqGrp1Enabled) ? ARMV8_ICC_IGRPEN1_EL1_AARCH64_ENABLE : 0;
556 break;
557 default:
558 AssertReleaseFailed();
559 break;
560 }
561
562 LogFlowFunc(("pVCpu=%p u32Reg=%#x pu64Value=%RX64\n", pVCpu, u32Reg, *pu64Value));
563 return VINF_SUCCESS;
564}
565
566
567/**
568 * Writes an GIC system register.
569 *
570 * @returns Strict VBox status code.
571 * @param pVCpu The cross context virtual CPU structure.
572 * @param u32Reg The system register being written (IPRT system register identifier).
573 * @param u64Value The value to write.
574 */
575VMM_INT_DECL(VBOXSTRICTRC) GICWriteSysReg(PVMCPUCC pVCpu, uint32_t u32Reg, uint64_t u64Value)
576{
577 /*
578 * Validate.
579 */
580 VMCPU_ASSERT_EMT(pVCpu);
581 RT_NOREF(pVCpu, u32Reg, u64Value);
582 LogFlowFunc(("pVCpu=%p u32Reg=%#x u64Value=%RX64\n", pVCpu, u32Reg, u64Value));
583
584 PGICCPU pThis = VMCPU_TO_GICCPU(pVCpu);
585 switch (u32Reg)
586 {
587 case ARMV8_AARCH64_SYSREG_ICC_PMR_EL1:
588 ASMAtomicWriteU8(&pThis->bInterruptPriority, (uint8_t)u64Value);
589 break;
590 case ARMV8_AARCH64_SYSREG_ICC_IAR0_EL1:
591 AssertReleaseFailed();
592 break;
593 case ARMV8_AARCH64_SYSREG_ICC_EOIR0_EL1:
594 AssertReleaseFailed();
595 break;
596 case ARMV8_AARCH64_SYSREG_ICC_HPPIR0_EL1:
597 AssertReleaseFailed();
598 break;
599 case ARMV8_AARCH64_SYSREG_ICC_BPR0_EL1:
600 pThis->bBinaryPointGrp0 = (uint8_t)(u64Value & 0x7);
601 break;
602 case ARMV8_AARCH64_SYSREG_ICC_AP0R0_EL1:
603 /** @todo */
604 break;
605 case ARMV8_AARCH64_SYSREG_ICC_AP0R1_EL1:
606 AssertReleaseFailed();
607 break;
608 case ARMV8_AARCH64_SYSREG_ICC_AP0R2_EL1:
609 AssertReleaseFailed();
610 break;
611 case ARMV8_AARCH64_SYSREG_ICC_AP0R3_EL1:
612 AssertReleaseFailed();
613 break;
614 case ARMV8_AARCH64_SYSREG_ICC_AP1R0_EL1:
615 /** @todo */
616 break;
617 case ARMV8_AARCH64_SYSREG_ICC_AP1R1_EL1:
618 AssertReleaseFailed();
619 break;
620 case ARMV8_AARCH64_SYSREG_ICC_AP1R2_EL1:
621 AssertReleaseFailed();
622 break;
623 case ARMV8_AARCH64_SYSREG_ICC_AP1R3_EL1:
624 AssertReleaseFailed();
625 break;
626 case ARMV8_AARCH64_SYSREG_ICC_NMIAR1_EL1:
627 AssertReleaseFailed();
628 break;
629 case ARMV8_AARCH64_SYSREG_ICC_DIR_EL1:
630 AssertReleaseFailed();
631 break;
632 case ARMV8_AARCH64_SYSREG_ICC_RPR_EL1:
633 AssertReleaseFailed();
634 break;
635 case ARMV8_AARCH64_SYSREG_ICC_SGI1R_EL1:
636 AssertReleaseFailed();
637 break;
638 case ARMV8_AARCH64_SYSREG_ICC_ASGI1R_EL1:
639 AssertReleaseFailed();
640 break;
641 case ARMV8_AARCH64_SYSREG_ICC_SGI0R_EL1:
642 AssertReleaseFailed();
643 break;
644 case ARMV8_AARCH64_SYSREG_ICC_IAR1_EL1:
645 AssertReleaseFailed();
646 break;
647 case ARMV8_AARCH64_SYSREG_ICC_EOIR1_EL1:
648 {
649 /* Mark the interrupt as not active anymore, though it might still be pending. */
650 Assert(u64Value < GIC_INTID_RANGE_SPI_START);
651 ASMAtomicAndU32(&pThis->bmIntActive, (uint32_t)u64Value);
652 break;
653 }
654 case ARMV8_AARCH64_SYSREG_ICC_HPPIR1_EL1:
655 AssertReleaseFailed();
656 break;
657 case ARMV8_AARCH64_SYSREG_ICC_BPR1_EL1:
658 pThis->bBinaryPointGrp0 = (uint8_t)(u64Value & 0x7);
659 break;
660 case ARMV8_AARCH64_SYSREG_ICC_CTLR_EL1:
661 u64Value &= ARMV8_ICC_CTLR_EL1_RW;
662 /** @todo */
663 break;
664 case ARMV8_AARCH64_SYSREG_ICC_SRE_EL1:
665 AssertReleaseFailed();
666 break;
667 case ARMV8_AARCH64_SYSREG_ICC_IGRPEN0_EL1:
668 ASMAtomicWriteBool(&pThis->fIrqGrp0Enabled, RT_BOOL(u64Value & ARMV8_ICC_IGRPEN0_EL1_AARCH64_ENABLE));
669 break;
670 case ARMV8_AARCH64_SYSREG_ICC_IGRPEN1_EL1:
671 ASMAtomicWriteBool(&pThis->fIrqGrp1Enabled, RT_BOOL(u64Value & ARMV8_ICC_IGRPEN1_EL1_AARCH64_ENABLE));
672 break;
673 default:
674 AssertReleaseFailed();
675 break;
676 }
677
678 return VINF_SUCCESS;
679}
680
681
682/**
683 * Sets the specified shared peripheral interrupt starting.
684 *
685 * @returns VBox status code.
686 * @param pVM The cross context virtual machine structure.
687 * @param uIntId The SPI ID (minus GIC_INTID_RANGE_SPI_START) to assert/de-assert.
688 * @param fAsserted Flag whether to mark the interrupt as asserted/de-asserted.
689 */
690VMM_INT_DECL(int) GICSpiSet(PVMCC pVM, uint32_t uIntId, bool fAsserted)
691{
692 RT_NOREF(pVM, uIntId, fAsserted);
693 AssertReleaseFailed();
694 return VERR_NOT_IMPLEMENTED;
695}
696
697
698/**
699 * Sets the specified private peripheral interrupt starting.
700 *
701 * @returns VBox status code.
702 * @param pVCpu The cross context virtual CPU structure.
703 * @param uIntId The PPI ID (minus GIC_INTID_RANGE_PPI_START) to assert/de-assert.
704 * @param fAsserted Flag whether to mark the interrupt as asserted/de-asserted.
705 */
706VMM_INT_DECL(int) GICPpiSet(PVMCPUCC pVCpu, uint32_t uIntId, bool fAsserted)
707{
708 AssertReturn(uIntId >= 0 && uIntId <= (GIC_INTID_RANGE_PPI_LAST - GIC_INTID_RANGE_PPI_START), VERR_INVALID_PARAMETER);
709 return gicReDistInterruptSet(pVCpu, uIntId + GIC_INTID_RANGE_PPI_START, fAsserted);
710}
711
712
713/**
714 * Sets the specified software generated interrupt starting.
715 *
716 * @returns VBox status code.
717 * @param pVCpu The cross context virtual CPU structure.
718 * @param uIntId The PPI ID (minus GIC_INTID_RANGE_SGI_START) to assert/de-assert.
719 * @param fAsserted Flag whether to mark the interrupt as asserted/de-asserted.
720 */
721VMM_INT_DECL(int) GICSgiSet(PVMCPUCC pVCpu, uint32_t uIntId, bool fAsserted)
722{
723 AssertReturn(uIntId >= 0 && uIntId <= (GIC_INTID_RANGE_SGI_LAST - GIC_INTID_RANGE_SGI_START), VERR_INVALID_PARAMETER);
724 return gicReDistInterruptSet(pVCpu, uIntId + GIC_INTID_RANGE_SGI_START, fAsserted);
725}
726
727
728/**
729 * Initializes per-VCPU GIC to the state following a power-up or hardware
730 * reset.
731 *
732 * @param pVCpu The cross context virtual CPU structure.
733 */
734DECLHIDDEN(void) gicResetCpu(PVMCPUCC pVCpu)
735{
736 LogFlowFunc(("GIC%u\n", pVCpu->idCpu));
737 VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu);
738}
739
740
741/**
742 * @callback_method_impl{FNIOMMMIONEWREAD}
743 */
744DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicDistMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
745{
746 NOREF(pvUser);
747 Assert(!(off & 0x3));
748 Assert(cb == 4); RT_NOREF_PV(cb);
749
750 PVMCPUCC pVCpu = PDMDevHlpGetVMCPU(pDevIns);
751 uint16_t offReg = off & 0xfffc;
752 uint32_t uValue = 0;
753
754 STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatMmioRead));
755
756 VBOXSTRICTRC rc = VBOXSTRICTRC_VAL(gicDistRegisterRead(pDevIns, pVCpu, offReg, &uValue));
757 *(uint32_t *)pv = uValue;
758
759 Log2(("GIC%u: gicDistMmioRead: offReg=%#RX16 uValue=%#RX32\n", pVCpu->idCpu, offReg, uValue));
760 return rc;
761}
762
763
764/**
765 * @callback_method_impl{FNIOMMMIONEWWRITE}
766 */
767DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicDistMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
768{
769 NOREF(pvUser);
770 Assert(!(off & 0x3));
771 Assert(cb == 4); RT_NOREF_PV(cb);
772
773 PVMCPUCC pVCpu = PDMDevHlpGetVMCPU(pDevIns);
774 uint16_t offReg = off & 0xfffc;
775 uint32_t uValue = *(uint32_t *)pv;
776
777 STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatMmioWrite));
778
779 Log2(("GIC%u: gicDistMmioWrite: offReg=%#RX16 uValue=%#RX32\n", pVCpu->idCpu, offReg, uValue));
780 return gicDistRegisterWrite(pDevIns, pVCpu, offReg, uValue);
781}
782
783
784/**
785 * @callback_method_impl{FNIOMMMIONEWREAD}
786 */
787DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicReDistMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
788{
789 NOREF(pvUser);
790 Assert(!(off & 0x3));
791 Assert(cb == 4); RT_NOREF_PV(cb);
792
793 PVMCPUCC pVCpu = PDMDevHlpGetVMCPU(pDevIns);
794
795 STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatMmioRead));
796
797 /*
798 * Determine the redistributor being targeted. Each redistributor takes GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE bytes
799 * and the redistributors are adjacent.
800 */
801 uint32_t idReDist = off / (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
802 off %= (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
803
804 /* Redistributor or SGI/PPI frame? */
805 uint16_t offReg = off & 0xfffc;
806 uint32_t uValue = 0;
807 VBOXSTRICTRC rcStrict;
808 if (off < GIC_REDIST_REG_FRAME_SIZE)
809 rcStrict = gicReDistRegisterRead(pDevIns, pVCpu, offReg, &uValue);
810 else
811 rcStrict = gicReDistSgiPpiRegisterRead(pDevIns, pVCpu, offReg, &uValue);
812
813 *(uint32_t *)pv = uValue;
814 Log2(("GICReDist%u: gicReDistMmioRead: off=%RGp idReDist=%u offReg=%#RX16 uValue=%#RX32 -> %Rrc\n",
815 pVCpu->idCpu, off, idReDist, offReg, uValue, VBOXSTRICTRC_VAL(rcStrict)));
816 return rcStrict;
817}
818
819
820/**
821 * @callback_method_impl{FNIOMMMIONEWWRITE}
822 */
823DECL_HIDDEN_CALLBACK(VBOXSTRICTRC) gicReDistMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
824{
825 NOREF(pvUser);
826 Assert(!(off & 0x3));
827 Assert(cb == 4); RT_NOREF_PV(cb);
828
829 PVMCPUCC pVCpu = PDMDevHlpGetVMCPU(pDevIns);
830 uint32_t uValue = *(uint32_t *)pv;
831
832 STAM_COUNTER_INC(&pVCpu->gic.s.CTX_SUFF_Z(StatMmioWrite));
833
834 /*
835 * Determine the redistributor being targeted. Each redistributor takes GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE bytes
836 * and the redistributors are adjacent.
837 */
838 uint32_t idReDist = off / (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
839 off %= (GIC_REDIST_REG_FRAME_SIZE + GIC_REDIST_SGI_PPI_REG_FRAME_SIZE);
840
841 /* Redistributor or SGI/PPI frame? */
842 uint16_t offReg = off & 0xfffc;
843 VBOXSTRICTRC rcStrict;
844 if (off < GIC_REDIST_REG_FRAME_SIZE)
845 rcStrict = gicReDistRegisterWrite(pDevIns, pVCpu, offReg, uValue);
846 else
847 rcStrict = gicReDistSgiPpiRegisterWrite(pDevIns, pVCpu, offReg, uValue);
848
849 Log2(("GICReDist%u: gicReDistMmioWrite: off=%RGp idReDist=%u offReg=%#RX16 uValue=%#RX32 -> %Rrc\n",
850 pVCpu->idCpu, off, idReDist, offReg, uValue, VBOXSTRICTRC_VAL(rcStrict)));
851 return rcStrict;
852}
853
854
855#ifndef IN_RING3
856
857/**
858 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
859 */
860static DECLCALLBACK(int) gicRZConstruct(PPDMDEVINS pDevIns)
861{
862 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
863 AssertReleaseFailed();
864 return VINF_SUCCESS;
865}
866#endif /* !IN_RING3 */
867
868/**
869 * GIC device registration structure.
870 */
871const PDMDEVREG g_DeviceGIC =
872{
873 /* .u32Version = */ PDM_DEVREG_VERSION,
874 /* .uReserved0 = */ 0,
875 /* .szName = */ "gic",
876 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
877 /* .fClass = */ PDM_DEVREG_CLASS_PIC,
878 /* .cMaxInstances = */ 1,
879 /* .uSharedVersion = */ 42,
880 /* .cbInstanceShared = */ sizeof(GICDEV),
881 /* .cbInstanceCC = */ 0,
882 /* .cbInstanceRC = */ 0,
883 /* .cMaxPciDevices = */ 0,
884 /* .cMaxMsixVectors = */ 0,
885 /* .pszDescription = */ "Generic Interrupt Controller",
886#if defined(IN_RING3)
887 /* .szRCMod = */ "VMMRC.rc",
888 /* .szR0Mod = */ "VMMR0.r0",
889 /* .pfnConstruct = */ gicR3Construct,
890 /* .pfnDestruct = */ gicR3Destruct,
891 /* .pfnRelocate = */ gicR3Relocate,
892 /* .pfnMemSetup = */ NULL,
893 /* .pfnPowerOn = */ NULL,
894 /* .pfnReset = */ gicR3Reset,
895 /* .pfnSuspend = */ NULL,
896 /* .pfnResume = */ NULL,
897 /* .pfnAttach = */ NULL,
898 /* .pfnDetach = */ NULL,
899 /* .pfnQueryInterface = */ NULL,
900 /* .pfnInitComplete = */ NULL,
901 /* .pfnPowerOff = */ NULL,
902 /* .pfnSoftReset = */ NULL,
903 /* .pfnReserved0 = */ NULL,
904 /* .pfnReserved1 = */ NULL,
905 /* .pfnReserved2 = */ NULL,
906 /* .pfnReserved3 = */ NULL,
907 /* .pfnReserved4 = */ NULL,
908 /* .pfnReserved5 = */ NULL,
909 /* .pfnReserved6 = */ NULL,
910 /* .pfnReserved7 = */ NULL,
911#elif defined(IN_RING0)
912 /* .pfnEarlyConstruct = */ NULL,
913 /* .pfnConstruct = */ gicRZConstruct,
914 /* .pfnDestruct = */ NULL,
915 /* .pfnFinalDestruct = */ NULL,
916 /* .pfnRequest = */ NULL,
917 /* .pfnReserved0 = */ NULL,
918 /* .pfnReserved1 = */ NULL,
919 /* .pfnReserved2 = */ NULL,
920 /* .pfnReserved3 = */ NULL,
921 /* .pfnReserved4 = */ NULL,
922 /* .pfnReserved5 = */ NULL,
923 /* .pfnReserved6 = */ NULL,
924 /* .pfnReserved7 = */ NULL,
925#elif defined(IN_RC)
926 /* .pfnConstruct = */ gicRZConstruct,
927 /* .pfnReserved0 = */ NULL,
928 /* .pfnReserved1 = */ NULL,
929 /* .pfnReserved2 = */ NULL,
930 /* .pfnReserved3 = */ NULL,
931 /* .pfnReserved4 = */ NULL,
932 /* .pfnReserved5 = */ NULL,
933 /* .pfnReserved6 = */ NULL,
934 /* .pfnReserved7 = */ NULL,
935#else
936# error "Not in IN_RING3, IN_RING0 or IN_RC!"
937#endif
938 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
939};
940
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