VirtualBox

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

Last change on this file since 100594 was 100186, checked in by vboxsync, 18 months ago

VMM/GIC: Need to adjust the written value to the actual bit for the SPI when marking the interrupt as active in the distributor, bugref:10404

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