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