VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/GICR3Nem-win.cpp

Last change on this file was 108845, checked in by vboxsync, 4 weeks ago

VMM/GIC: bugref:10877 Missing CFGM keys ('Lpi' and 'Mbi' would be added when ITS is enabled, 'ArchRevMinor' shouldn't be getting inserted since we only support GICv3.1 for now and would default to value of 1).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.0 KB
Line 
1/* $Id: GICR3Nem-win.cpp 108845 2025-04-04 08:46:54Z vboxsync $ */
2/** @file
3 * GIC - Generic Interrupt Controller Architecture (GIC) - Hyper-V interface.
4 */
5
6/*
7 * Copyright (C) 2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_DEV_GIC
33#include <iprt/nt/nt-and-windows.h>
34#include <iprt/nt/hyperv.h>
35#include <iprt/mem.h>
36#include <WinHvPlatform.h>
37
38#include <VBox/log.h>
39#include "GICInternal.h"
40#include "NEMInternal.h" /* Need access to the partition handle. */
41#include <VBox/vmm/pdmgic.h>
42#include <VBox/vmm/cpum.h>
43#include <VBox/vmm/hm.h>
44#include <VBox/vmm/mm.h>
45#include <VBox/vmm/pdmdev.h>
46#include <VBox/vmm/ssm.h>
47#include <VBox/vmm/vm.h>
48
49#include <iprt/armv8.h>
50
51#ifndef VBOX_DEVICE_STRUCT_TESTCASE
52
53
54/*********************************************************************************************************************************
55* Defined Constants And Macros *
56*********************************************************************************************************************************/
57/** The current GIC saved state version. */
58#define GIC_NEM_SAVED_STATE_VERSION 1
59
60
61/*********************************************************************************************************************************
62* Structures and Typedefs *
63*********************************************************************************************************************************/
64
65/**
66 * GICHv PDM instance data (per-VM).
67 */
68typedef struct GICHVDEV
69{
70 /** Pointer to the PDM device instance. */
71 PPDMDEVINSR3 pDevIns;
72 /** The partition handle grabbed from NEM. */
73 WHV_PARTITION_HANDLE hPartition;
74} GICHVDEV;
75/** Pointer to a GIC Hyper-V device. */
76typedef GICHVDEV *PGICHVDEV;
77/** Pointer to a const GIC Hyper-V device. */
78typedef GICHVDEV const *PCGICHVDEV;
79
80
81/*
82 * The following definitions appeared in build 27744 allow interacting with the GIC controller,
83 * since 27813 the API is public with some changes and available under:
84 * https://github.com/MicrosoftDocs/Virtualization-Documentation/blob/main/virtualization/api/hypervisor-platform/headers/WinHvPlatformDefs.h .
85 */
86/** @todo Better way of defining these which doesn't require casting later on when calling APIs. */
87#define MY_WHV_ARM64_IINTERRUPT_TYPE_FIXED UINT32_C(0)
88
89typedef union MY_WHV_INTERRUPT_CONTROL2
90{
91 UINT64 AsUINT64;
92 struct
93 {
94 uint32_t InterruptType;
95 UINT32 Reserved1:2;
96 UINT32 Asserted:1;
97 UINT32 Retarget:1;
98 UINT32 Reserved2:28;
99 };
100} MY_WHV_INTERRUPT_CONTROL2;
101
102
103typedef struct MY_WHV_INTERRUPT_CONTROL
104{
105 UINT64 TargetPartition;
106 MY_WHV_INTERRUPT_CONTROL2 InterruptControl;
107 UINT64 DestinationAddress;
108 UINT32 RequestedVector;
109 UINT8 TargetVtl;
110 UINT8 ReservedZ0;
111 UINT16 ReservedZ1;
112} MY_WHV_INTERRUPT_CONTROL;
113AssertCompileSize(MY_WHV_INTERRUPT_CONTROL, 32);
114
115
116typedef struct MY_WHV_INTERRUPT_STATE
117{
118 uint8_t fState;
119 uint8_t bIPriorityCfg;
120 uint8_t bIPriorityActive;
121 uint8_t bRsvd0;
122} MY_WHV_INTERRUPT_STATE;
123AssertCompileSize(MY_WHV_INTERRUPT_STATE, sizeof(uint32_t));
124
125#define WHV_INTERRUPT_STATE_F_ENABLED RT_BIT(0)
126#define WHV_INTERRUPT_STATE_F_EDGE_TRIGGERED RT_BIT(1)
127#define WHV_INTERRUPT_STATE_F_ASSERTED RT_BIT(2)
128#define WHV_INTERRUPT_STATE_F_SET_PENDING RT_BIT(3)
129#define WHV_INTERRUPT_STATE_F_ACTIVE RT_BIT(4)
130#define WHV_INTERRUPT_STATE_F_DIRECT RT_BIT(5)
131
132
133typedef struct MY_WHV_GLOBAL_INTERRUPT_STATE
134{
135 uint32_t u32IntId;
136 uint32_t idActiveVp;
137 uint32_t u32TargetMpidrOrVpIndex;
138 MY_WHV_INTERRUPT_STATE State;
139} MY_WHV_GLOBAL_INTERRUPT_STATE;
140AssertCompileSize(MY_WHV_GLOBAL_INTERRUPT_STATE, 4 * sizeof(uint32_t));
141
142
143typedef struct MY_WHV_GLOBAL_INTERRUPT_CONTROLLER_STATE
144{
145 uint8_t bVersion;
146 uint8_t bGicVersion;
147 uint8_t abPad[2];
148
149 uint32_t cInterrupts;
150 uint64_t u64RegGicdCtrlEnableGrp1A;
151
152 MY_WHV_GLOBAL_INTERRUPT_STATE aSpis[1]; /* Flexible */
153} MY_WHV_GLOBAL_INTERRUPT_CONTROLLER_STATE;
154AssertCompileSize(MY_WHV_GLOBAL_INTERRUPT_CONTROLLER_STATE, 2 * sizeof(uint32_t) + sizeof(uint64_t) + sizeof(MY_WHV_GLOBAL_INTERRUPT_STATE));
155
156#define MY_WHV_GLOBAL_INTERRUPT_CONTROLLER_STATE_VERSION 1
157
158
159typedef struct MY_WHV_LOCAL_INTERRUPT_CONTROLLER_STATE
160{
161 uint8_t bVersion;
162 uint8_t bGicVersion;
163 uint8_t abPad[6];
164
165 uint64_t u64RegIccIGrpEn1El1;
166 uint64_t u64RegGicrCtrlEnableLpis;
167 uint64_t u64RegIccBprEl1;
168 uint64_t u64RegIccPmrEl1;
169 uint64_t u64RegGicrPropBase;
170 uint64_t u64RegGicrPendBase;
171
172 uint32_t au32RegIchAp1REl2[4];
173
174 MY_WHV_INTERRUPT_STATE aPpiStates[32];
175} MY_WHV_LOCAL_INTERRUPT_CONTROLLER_STATE;
176AssertCompileSize(MY_WHV_LOCAL_INTERRUPT_CONTROLLER_STATE, 7 * sizeof(uint64_t) + 4 * sizeof(uint32_t) + 32 * sizeof(MY_WHV_INTERRUPT_STATE));
177
178#define MY_WHV_LOCAL_INTERRUPT_CONTROLLER_STATE_VERSION 1
179
180#define WHV_VIRTUAL_PROCESSOR_STATE_TYPE_PFN RT_BIT_32(31)
181#define WHV_VIRTUAL_PROCESSOR_STATE_TYPE_ANY_VP RT_BIT_32(30)
182
183#define WHvVirtualProcessorStateTypeInterruptControllerState (WHV_VIRTUAL_PROCESSOR_STATE_TYPE)(0 | WHV_VIRTUAL_PROCESSOR_STATE_TYPE_PFN)
184#define WHvVirtualProcessorStateTypeGlobalInterruptState (WHV_VIRTUAL_PROCESSOR_STATE_TYPE)(6 | WHV_VIRTUAL_PROCESSOR_STATE_TYPE_PFN | WHV_VIRTUAL_PROCESSOR_STATE_TYPE_ANY_VP)
185
186
187/*********************************************************************************************************************************
188* Global Variables *
189*********************************************************************************************************************************/
190extern decltype(WHvGetVirtualProcessorState) * g_pfnWHvGetVirtualProcessorState;
191extern decltype(WHvSetVirtualProcessorState) * g_pfnWHvSetVirtualProcessorState;
192extern decltype(WHvRequestInterrupt) * g_pfnWHvRequestInterrupt;
193
194/*
195 * Let the preprocessor alias the APIs to import variables for better autocompletion.
196 */
197#ifndef IN_SLICKEDIT
198# define WHvGetVirtualProcessorState g_pfnWHvGetVirtualProcessorState
199# define WHvSetVirtualProcessorState g_pfnWHvSetVirtualProcessorState
200# define WHvRequestInterrupt g_pfnWHvRequestInterrupt
201#endif
202
203
204/** Saved state field descriptors for the global interrupt state. */
205static const SSMFIELD g_aWHvGicGlobalInterruptState[] =
206{
207 SSMFIELD_ENTRY(MY_WHV_GLOBAL_INTERRUPT_STATE, u32IntId),
208 SSMFIELD_ENTRY(MY_WHV_GLOBAL_INTERRUPT_STATE, idActiveVp),
209 SSMFIELD_ENTRY(MY_WHV_GLOBAL_INTERRUPT_STATE, u32TargetMpidrOrVpIndex),
210 SSMFIELD_ENTRY(MY_WHV_GLOBAL_INTERRUPT_STATE, State.fState),
211 SSMFIELD_ENTRY(MY_WHV_GLOBAL_INTERRUPT_STATE, State.bIPriorityCfg),
212 SSMFIELD_ENTRY(MY_WHV_GLOBAL_INTERRUPT_STATE, State.bIPriorityActive),
213 SSMFIELD_ENTRY_TERM()
214};
215
216
217/** Saved state field descriptors for the global GIC state (sans the flexible interrupts array. */
218static const SSMFIELD g_aWHvGicGlobalState[] =
219{
220 SSMFIELD_ENTRY(MY_WHV_GLOBAL_INTERRUPT_CONTROLLER_STATE, bGicVersion),
221 SSMFIELD_ENTRY(MY_WHV_GLOBAL_INTERRUPT_CONTROLLER_STATE, cInterrupts),
222 SSMFIELD_ENTRY(MY_WHV_GLOBAL_INTERRUPT_CONTROLLER_STATE, u64RegGicdCtrlEnableGrp1A),
223 SSMFIELD_ENTRY_TERM()
224};
225
226
227#define GIC_NEM_HV_PPI_STATE(a_idx) \
228 SSMFIELD_ENTRY(MY_WHV_LOCAL_INTERRUPT_CONTROLLER_STATE, aPpiStates[a_idx].fState), \
229 SSMFIELD_ENTRY(MY_WHV_LOCAL_INTERRUPT_CONTROLLER_STATE, aPpiStates[a_idx].bIPriorityCfg), \
230 SSMFIELD_ENTRY(MY_WHV_LOCAL_INTERRUPT_CONTROLLER_STATE, aPpiStates[a_idx].bIPriorityActive)
231
232
233/** Saved state field descriptors for the local interrupt controller state. */
234static const SSMFIELD g_aWHvGicLocalInterruptState[] =
235{
236 SSMFIELD_ENTRY(MY_WHV_LOCAL_INTERRUPT_CONTROLLER_STATE, bGicVersion),
237 SSMFIELD_ENTRY(MY_WHV_LOCAL_INTERRUPT_CONTROLLER_STATE, u64RegIccIGrpEn1El1),
238 SSMFIELD_ENTRY(MY_WHV_LOCAL_INTERRUPT_CONTROLLER_STATE, u64RegGicrCtrlEnableLpis),
239 SSMFIELD_ENTRY(MY_WHV_LOCAL_INTERRUPT_CONTROLLER_STATE, u64RegIccBprEl1),
240 SSMFIELD_ENTRY(MY_WHV_LOCAL_INTERRUPT_CONTROLLER_STATE, u64RegIccPmrEl1),
241 SSMFIELD_ENTRY(MY_WHV_LOCAL_INTERRUPT_CONTROLLER_STATE, u64RegGicrPropBase),
242 SSMFIELD_ENTRY(MY_WHV_LOCAL_INTERRUPT_CONTROLLER_STATE, u64RegGicrPendBase),
243 SSMFIELD_ENTRY(MY_WHV_LOCAL_INTERRUPT_CONTROLLER_STATE, au32RegIchAp1REl2[0]),
244 SSMFIELD_ENTRY(MY_WHV_LOCAL_INTERRUPT_CONTROLLER_STATE, au32RegIchAp1REl2[1]),
245 SSMFIELD_ENTRY(MY_WHV_LOCAL_INTERRUPT_CONTROLLER_STATE, au32RegIchAp1REl2[2]),
246 SSMFIELD_ENTRY(MY_WHV_LOCAL_INTERRUPT_CONTROLLER_STATE, au32RegIchAp1REl2[3]),
247 GIC_NEM_HV_PPI_STATE(0),
248 GIC_NEM_HV_PPI_STATE(1),
249 GIC_NEM_HV_PPI_STATE(2),
250 GIC_NEM_HV_PPI_STATE(3),
251 GIC_NEM_HV_PPI_STATE(4),
252 GIC_NEM_HV_PPI_STATE(5),
253 GIC_NEM_HV_PPI_STATE(6),
254 GIC_NEM_HV_PPI_STATE(7),
255 GIC_NEM_HV_PPI_STATE(8),
256 GIC_NEM_HV_PPI_STATE(9),
257 GIC_NEM_HV_PPI_STATE(10),
258 GIC_NEM_HV_PPI_STATE(11),
259 GIC_NEM_HV_PPI_STATE(12),
260 GIC_NEM_HV_PPI_STATE(13),
261 GIC_NEM_HV_PPI_STATE(14),
262 GIC_NEM_HV_PPI_STATE(15),
263 GIC_NEM_HV_PPI_STATE(16),
264 GIC_NEM_HV_PPI_STATE(17),
265 GIC_NEM_HV_PPI_STATE(18),
266 GIC_NEM_HV_PPI_STATE(19),
267 GIC_NEM_HV_PPI_STATE(20),
268 GIC_NEM_HV_PPI_STATE(21),
269 GIC_NEM_HV_PPI_STATE(22),
270 GIC_NEM_HV_PPI_STATE(23),
271 GIC_NEM_HV_PPI_STATE(24),
272 GIC_NEM_HV_PPI_STATE(25),
273 GIC_NEM_HV_PPI_STATE(26),
274 GIC_NEM_HV_PPI_STATE(27),
275 GIC_NEM_HV_PPI_STATE(28),
276 GIC_NEM_HV_PPI_STATE(29),
277 GIC_NEM_HV_PPI_STATE(30),
278 GIC_NEM_HV_PPI_STATE(31),
279 SSMFIELD_ENTRY_TERM()
280};
281
282
283/**
284 * Common worker for gicR3HvSetSpi() and gicR3HvSetPpi().
285 *
286 * @returns VBox status code.
287 * @param pDevIns The PDM Hyper-V GIC device instance.
288 * @param idCpu The CPU ID for which the interrupt is updated (only valid for PPIs).
289 * @param fPpi Flag whether this is a PPI or SPI.
290 * @param uIntId The interrupt ID to update.
291 * @param fAsserted Flag whether the interrupt is asserted (true) or not (false).
292 */
293DECLINLINE(int) gicR3HvSetIrq(PPDMDEVINS pDevIns, VMCPUID idCpu, bool fPpi, uint32_t uIntId, bool fAsserted)
294{
295 LogFlowFunc(("pDevIns=%p idCpu=%u fPpi=%RTbool uIntId=%u fAsserted=%RTbool\n",
296 pDevIns, idCpu, fPpi, uIntId, fAsserted));
297
298 PGICHVDEV pThis = PDMDEVINS_2_DATA(pDevIns, PGICHVDEV);
299
300 MY_WHV_INTERRUPT_CONTROL IntrCtrl;
301 IntrCtrl.TargetPartition = 0;
302 IntrCtrl.InterruptControl.InterruptType = MY_WHV_ARM64_IINTERRUPT_TYPE_FIXED;
303 IntrCtrl.InterruptControl.Reserved1 = 0;
304 IntrCtrl.InterruptControl.Asserted = fAsserted ? 1 : 0;
305 IntrCtrl.InterruptControl.Retarget = 0;
306 IntrCtrl.InterruptControl.Reserved2 = 0;
307 IntrCtrl.DestinationAddress = fPpi ? RT_BIT(idCpu) : 0; /* SGI1R_EL1 */
308 IntrCtrl.RequestedVector = fPpi ? uIntId : uIntId;
309 IntrCtrl.TargetVtl = 0;
310 IntrCtrl.ReservedZ0 = 0;
311 IntrCtrl.ReservedZ1 = 0;
312 HRESULT hrc = WHvRequestInterrupt(pThis->hPartition, (const WHV_INTERRUPT_CONTROL *)&IntrCtrl, sizeof(IntrCtrl));
313 if (SUCCEEDED(hrc))
314 return VINF_SUCCESS;
315
316 AssertFailed();
317 LogFlowFunc(("WHvRequestInterrupt() failed with %Rhrc (Last=%#x/%u)\n", hrc, RTNtLastStatusValue(), RTNtLastErrorValue()));
318 return VERR_NEM_IPE_9; /** @todo */
319}
320
321
322/**
323 * Sets the given SPI inside the in-kernel Hyper-V GIC.
324 *
325 * @returns VBox status code.
326 * @param pVM The VM instance.
327 * @param uIntId The SPI ID to update.
328 * @param fAsserted Flag whether the interrupt is asserted (true) or not (false).
329 */
330static DECLCALLBACK(int) gicR3HvSetSpi(PVMCC pVM, uint32_t uIntId, bool fAsserted)
331{
332 PGIC pGic = VM_TO_GIC(pVM);
333 PPDMDEVINS pDevIns = pGic->CTX_SUFF(pDevIns);
334
335 /* idCpu is ignored for SPI interrupts. */
336 return gicR3HvSetIrq(pDevIns, 0 /*idCpu*/, false /*fPpi*/,
337 uIntId + GIC_INTID_RANGE_SPI_START, fAsserted);
338}
339
340
341/**
342 * Sets the given PPI inside the in-kernel Hyper-V GIC.
343 *
344 * @returns VBox status code.
345 * @param pVCpu The vCPU for whih the PPI state is updated.
346 * @param uIntId The PPI ID to update.
347 * @param fAsserted Flag whether the interrupt is asserted (true) or not (false).
348 */
349static DECLCALLBACK(int) gicR3HvSetPpi(PVMCPUCC pVCpu, uint32_t uIntId, bool fAsserted)
350{
351 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
352
353 return gicR3HvSetIrq(pDevIns, pVCpu->idCpu, true /*fPpi*/,
354 uIntId + GIC_INTID_RANGE_PPI_START, fAsserted);
355}
356
357
358/**
359 * @copydoc FNSSMDEVSAVEEXEC
360 */
361static DECLCALLBACK(int) gicR3HvSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
362{
363 PGICHVDEV pThis = PDMDEVINS_2_DATA(pDevIns, PGICHVDEV);
364 PVM pVM = PDMDevHlpGetVM(pDevIns);
365 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
366
367 AssertReturn(pVM, VERR_INVALID_VM_HANDLE);
368
369 LogFlowFunc(("Enter\n"));
370
371 /*
372 * Save the global interrupt state first.
373 */
374 /** @todo The saved state is not final because it would be great if we could have
375 * a compatible saved state format between all possible GIC variants (no idea whether this is feasible).
376 */
377 uint32_t cbWritten = 0;
378 HRESULT hrc = WHvGetVirtualProcessorState(pThis->hPartition, WHV_ANY_VP, WHvVirtualProcessorStateTypeGlobalInterruptState,
379 NULL, 0, &cbWritten);
380 AssertLogRelMsgReturn(hrc == WHV_E_INSUFFICIENT_BUFFER,
381 ("WHvGetVirtualProcessorState(%p, WHV_ANY_VP, WHvVirtualProcessorStateTypeGlobalInterruptState,) -> %Rhrc (Last=%#x/%u)\n",
382 pVM->nem.s.hPartition, hrc, RTNtLastStatusValue(), RTNtLastErrorValue())
383 , VERR_NEM_GET_REGISTERS_FAILED);
384
385 /* Allocate a buffer to write the whole state to based on the amount of interrupts indicated. */
386 uint32_t const cbState = cbWritten;
387 MY_WHV_GLOBAL_INTERRUPT_CONTROLLER_STATE *pState = (MY_WHV_GLOBAL_INTERRUPT_CONTROLLER_STATE *)RTMemTmpAllocZ(cbState);
388 AssertLogRelMsgReturn(pState, ("Allocating %u bytes of memory for the global interrupt state buffer failed\n", cbState),
389 VERR_NO_MEMORY);
390
391 hrc = WHvGetVirtualProcessorState(pThis->hPartition, WHV_ANY_VP, WHvVirtualProcessorStateTypeGlobalInterruptState,
392 pState, cbState, &cbWritten);
393 AssertLogRelMsg(SUCCEEDED(hrc),
394 ("WHvGetVirtualProcessorState(%p, WHV_ANY_VP, WHvVirtualProcessorStateTypeGlobalInterruptState, %p, %u) -> %Rhrc (Last=%#x/%u)\n",
395 pVM->nem.s.hPartition, pState, cbState, hrc, RTNtLastStatusValue(), RTNtLastErrorValue()));
396 AssertLogRelMsgReturn(cbWritten == cbState,
397 ("WHvGetVirtualProcessorState(%p, WHV_ANY_VP, WHvVirtualProcessorStateTypeGlobalInterruptState,) -> cbWritten=%u vs expected=%u\n",
398 pVM->nem.s.hPartition, cbWritten, cbState)
399 , VERR_NEM_GET_REGISTERS_FAILED);
400 AssertLogRelMsgReturn(pState->bVersion == MY_WHV_GLOBAL_INTERRUPT_CONTROLLER_STATE_VERSION,
401 ("WHvGetVirtualProcessorState(%p, WHV_ANY_VP, WHvVirtualProcessorStateTypeGlobalInterruptState,) -> bVersion=%u vs expected=%u\n",
402 pVM->nem.s.hPartition, pState->bVersion, MY_WHV_GLOBAL_INTERRUPT_CONTROLLER_STATE_VERSION)
403 , VERR_NEM_GET_REGISTERS_FAILED);
404
405 if (SUCCEEDED(hrc))
406 {
407 pHlp->pfnSSMPutStruct(pSSM, (const void *)pState, &g_aWHvGicGlobalState[0]);
408 for (uint32_t i = 0; i < pState->cInterrupts; i++)
409 pHlp->pfnSSMPutStruct(pSSM, (const void *)&pState->aSpis[i], &g_aWHvGicGlobalInterruptState[0]);
410 }
411
412 RTMemTmpFree(pState);
413 if (FAILED(hrc))
414 return VERR_NEM_GET_REGISTERS_FAILED;
415
416 /*
417 * Now for the local interrupt state for each vCPU.
418 */
419 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
420 {
421 MY_WHV_LOCAL_INTERRUPT_CONTROLLER_STATE LocalState;
422
423 hrc = WHvGetVirtualProcessorState(pThis->hPartition, idCpu, WHvVirtualProcessorStateTypeInterruptControllerState,
424 &LocalState, sizeof(LocalState), &cbWritten);
425 AssertLogRelMsgReturn(SUCCEEDED(hrc),
426 ("WHvGetVirtualProcessorState(%p, WHV_ANY_VP, WHvVirtualProcessorStateTypeInterruptControllerState2,) -> %Rhrc (Last=%#x/%u)\n",
427 pVM->nem.s.hPartition, hrc, RTNtLastStatusValue(), RTNtLastErrorValue())
428 , VERR_NEM_GET_REGISTERS_FAILED);
429 AssertLogRelMsgReturn(cbWritten == sizeof(LocalState),
430 ("WHvGetVirtualProcessorState(%p, WHV_ANY_VP, WHvVirtualProcessorStateTypeInterruptControllerState2,) -> cbWritten=%u vs expected=%u\n",
431 pVM->nem.s.hPartition, cbWritten, sizeof(LocalState))
432 , VERR_NEM_GET_REGISTERS_FAILED);
433 AssertLogRelMsgReturn(LocalState.bVersion == MY_WHV_LOCAL_INTERRUPT_CONTROLLER_STATE_VERSION,
434 ("WHvGetVirtualProcessorState(%p, %u, WHvVirtualProcessorStateTypeInterruptControllerState2,) -> bVersion=%u vs expected=%u\n",
435 pVM->nem.s.hPartition, idCpu, LocalState.bVersion, MY_WHV_LOCAL_INTERRUPT_CONTROLLER_STATE_VERSION)
436 , VERR_NEM_GET_REGISTERS_FAILED);
437
438 pHlp->pfnSSMPutStruct(pSSM, (const void *)&LocalState, &g_aWHvGicLocalInterruptState[0]);
439
440 /*
441 * Check that we're still good wrt restored data.
442 */
443 int rc = pHlp->pfnSSMHandleGetStatus(pSSM);
444 AssertRCReturn(rc, rc);
445 }
446
447 return VINF_SUCCESS;
448}
449
450
451/**
452 * @copydoc FNSSMDEVLOADEXEC
453 */
454static DECLCALLBACK(int) gicR3HvLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
455{
456 PGICHVDEV pThis = PDMDEVINS_2_DATA(pDevIns, PGICHVDEV);
457 PVM pVM = PDMDevHlpGetVM(pDevIns);
458 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
459
460 AssertReturn(pVM, VERR_INVALID_VM_HANDLE);
461 AssertReturn(uPass == SSM_PASS_FINAL, VERR_WRONG_ORDER);
462
463 LogFlowFunc(("uVersion=%u uPass=%#x\n", uVersion, uPass));
464
465 /* Weed out invalid versions. */
466 if (uVersion != GIC_NEM_SAVED_STATE_VERSION)
467 {
468 LogRel(("GIC: gicR3HvLoadExec: Invalid/unrecognized saved-state version %u (%#x)\n", uVersion, uVersion));
469 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
470 }
471
472 /*
473 * Restore the global state.
474 */
475 MY_WHV_GLOBAL_INTERRUPT_CONTROLLER_STATE GlobalState; RT_ZERO(GlobalState);
476 int rc = pHlp->pfnSSMGetStruct(pSSM, &GlobalState, &g_aWHvGicGlobalState[0]);
477 AssertRCReturn(rc, rc);
478
479 if (GlobalState.cInterrupts >= _64K) /* Interrupt IDs are 16-bit. */
480 return VERR_INVALID_PARAMETER;
481
482 /* Calculate size of the final buffer and allocate. */
483 uint32_t const cbState = RT_UOFFSETOF_DYN(MY_WHV_GLOBAL_INTERRUPT_CONTROLLER_STATE, aSpis[GlobalState.cInterrupts]);
484 MY_WHV_GLOBAL_INTERRUPT_CONTROLLER_STATE *pState = (MY_WHV_GLOBAL_INTERRUPT_CONTROLLER_STATE *)RTMemTmpAllocZ(cbState);
485 AssertLogRelMsgReturn(pState, ("Allocating %u bytes of memory for the global interrupt state buffer failed\n", cbState),
486 VERR_NO_MEMORY);
487
488 pState->bVersion = MY_WHV_GLOBAL_INTERRUPT_CONTROLLER_STATE_VERSION;
489 pState->bGicVersion = GlobalState.bGicVersion;
490 pState->cInterrupts = GlobalState.cInterrupts;
491 pState->u64RegGicdCtrlEnableGrp1A = GlobalState.u64RegGicdCtrlEnableGrp1A;
492 for (uint32_t i = 0; i < pState->cInterrupts; i++)
493 {
494 rc = pHlp->pfnSSMGetStruct(pSSM, &pState->aSpis[i], &g_aWHvGicGlobalInterruptState[0]);
495 if (RT_FAILURE(rc))
496 break;
497 }
498 AssertRCReturnStmt(rc, RTMemTmpFree(pState), rc);
499
500 HRESULT hrc = WHvSetVirtualProcessorState(pThis->hPartition, WHV_ANY_VP, WHvVirtualProcessorStateTypeGlobalInterruptState,
501 pState, cbState);
502 RTMemTmpFree(pState);
503 pState = NULL;
504
505 AssertLogRelMsgReturn(SUCCEEDED(hrc),
506 ("WHvSetVirtualProcessorState(%p, WHV_ANY_VP, WHvVirtualProcessorStateTypeGlobalInterruptState,,%u) -> %Rhrc (Last=%#x/%u)\n",
507 pVM->nem.s.hPartition, cbState, hrc, RTNtLastStatusValue(), RTNtLastErrorValue())
508 , VERR_NEM_SET_REGISTERS_FAILED);
509
510 /*
511 * Restore per CPU state.
512 */
513 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
514 {
515 MY_WHV_LOCAL_INTERRUPT_CONTROLLER_STATE LocalState;
516 RT_ZERO(LocalState);
517
518 rc = pHlp->pfnSSMGetStruct(pSSM, &LocalState, &g_aWHvGicLocalInterruptState[0]);
519 AssertRCReturn(rc, rc);
520
521 LocalState.bVersion = MY_WHV_LOCAL_INTERRUPT_CONTROLLER_STATE_VERSION;
522
523 hrc = WHvSetVirtualProcessorState(pThis->hPartition, idCpu, WHvVirtualProcessorStateTypeInterruptControllerState,
524 &LocalState, sizeof(LocalState));
525 AssertLogRelMsgReturn(SUCCEEDED(hrc),
526 ("WHvSetVirtualProcessorState(%p, %u, WHvVirtualProcessorStateTypeInterruptControllerState2,) -> %Rhrc (Last=%#x/%u)\n",
527 pVM->nem.s.hPartition, idCpu, hrc, RTNtLastStatusValue(), RTNtLastErrorValue())
528 , VERR_NEM_SET_REGISTERS_FAILED);
529 }
530
531 return VINF_SUCCESS;
532}
533
534
535/**
536 * @interface_method_impl{PDMDEVREG,pfnReset}
537 */
538DECLCALLBACK(void) gicR3HvReset(PPDMDEVINS pDevIns)
539{
540 PVM pVM = PDMDevHlpGetVM(pDevIns);
541 VM_ASSERT_EMT0(pVM);
542 VM_ASSERT_IS_NOT_RUNNING(pVM);
543
544 RT_NOREF(pVM);
545
546 LogFlow(("GIC: gicR3HvReset\n"));
547}
548
549
550/**
551 * @interface_method_impl{PDMDEVREG,pfnDestruct}
552 */
553DECLCALLBACK(int) gicR3HvDestruct(PPDMDEVINS pDevIns)
554{
555 LogFlowFunc(("pDevIns=%p\n", pDevIns));
556 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
557
558 return VINF_SUCCESS;
559}
560
561
562/**
563 * @interface_method_impl{PDMDEVREG,pfnConstruct}
564 */
565DECLCALLBACK(int) gicR3HvConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
566{
567 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
568 PGICHVDEV pThis = PDMDEVINS_2_DATA(pDevIns, PGICHVDEV);
569 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
570 PVM pVM = PDMDevHlpGetVM(pDevIns);
571 PGIC pGic = VM_TO_GIC(pVM);
572 Assert(iInstance == 0); NOREF(iInstance);
573
574 /*
575 * Init the data.
576 */
577 pGic->pDevInsR3 = pDevIns;
578 pThis->pDevIns = pDevIns;
579 pThis->hPartition = pVM->nem.s.hPartition;
580
581 /*
582 * Validate GIC settings.
583 */
584 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "DistributorMmioBase|RedistributorMmioBase|ItsMmioBase"
585 "|ArchRev"
586 "|Lpi"
587 "|Mbi", "");
588
589 /*
590 * Disable automatic PDM locking for this device.
591 */
592 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
593 AssertRCReturn(rc, rc);
594
595 /*
596 * Register the GIC with PDM.
597 */
598 rc = PDMDevHlpIcRegister(pDevIns);
599 AssertLogRelRCReturn(rc, rc);
600
601 rc = PDMGicRegisterBackend(pVM, PDMGICBACKENDTYPE_HYPERV, &g_GicHvBackend);
602 AssertLogRelRCReturn(rc, rc);
603
604 /*
605 * Query the MMIO ranges.
606 */
607 RTGCPHYS GCPhysMmioBaseDist = 0;
608 rc = pHlp->pfnCFGMQueryU64(pCfg, "DistributorMmioBase", &GCPhysMmioBaseDist);
609 if (RT_FAILURE(rc))
610 return PDMDEV_SET_ERROR(pDevIns, rc,
611 N_("Configuration error: Failed to get the \"DistributorMmioBase\" value"));
612
613 RTGCPHYS GCPhysMmioBaseReDist = 0;
614 rc = pHlp->pfnCFGMQueryU64(pCfg, "RedistributorMmioBase", &GCPhysMmioBaseReDist);
615 if (RT_FAILURE(rc))
616 return PDMDEV_SET_ERROR(pDevIns, rc,
617 N_("Configuration error: Failed to get the \"RedistributorMmioBase\" value"));
618
619 /*
620 * Register saved state callbacks.
621 */
622 rc = PDMDevHlpSSMRegister(pDevIns, GIC_NEM_SAVED_STATE_VERSION, 0 /*cbGuess*/, gicR3HvSaveExec, gicR3HvLoadExec);
623 AssertRCReturn(rc, rc);
624
625 gicR3HvReset(pDevIns);
626 return VINF_SUCCESS;
627}
628
629
630/**
631 * GIC device registration structure.
632 */
633const PDMDEVREG g_DeviceGICNem =
634{
635 /* .u32Version = */ PDM_DEVREG_VERSION,
636 /* .uReserved0 = */ 0,
637 /* .szName = */ "gic-nem",
638 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE,
639 /* .fClass = */ PDM_DEVREG_CLASS_PIC,
640 /* .cMaxInstances = */ 1,
641 /* .uSharedVersion = */ 42,
642 /* .cbInstanceShared = */ sizeof(GICHVDEV),
643 /* .cbInstanceCC = */ 0,
644 /* .cbInstanceRC = */ 0,
645 /* .cMaxPciDevices = */ 0,
646 /* .cMaxMsixVectors = */ 0,
647 /* .pszDescription = */ "Generic Interrupt Controller",
648#if defined(IN_RING3)
649 /* .szRCMod = */ "VMMRC.rc",
650 /* .szR0Mod = */ "VMMR0.r0",
651 /* .pfnConstruct = */ gicR3HvConstruct,
652 /* .pfnDestruct = */ gicR3HvDestruct,
653 /* .pfnRelocate = */ NULL,
654 /* .pfnMemSetup = */ NULL,
655 /* .pfnPowerOn = */ NULL,
656 /* .pfnReset = */ gicR3HvReset,
657 /* .pfnSuspend = */ NULL,
658 /* .pfnResume = */ NULL,
659 /* .pfnAttach = */ NULL,
660 /* .pfnDetach = */ NULL,
661 /* .pfnQueryInterface = */ NULL,
662 /* .pfnInitComplete = */ NULL,
663 /* .pfnPowerOff = */ NULL,
664 /* .pfnSoftReset = */ NULL,
665 /* .pfnReserved0 = */ NULL,
666 /* .pfnReserved1 = */ NULL,
667 /* .pfnReserved2 = */ NULL,
668 /* .pfnReserved3 = */ NULL,
669 /* .pfnReserved4 = */ NULL,
670 /* .pfnReserved5 = */ NULL,
671 /* .pfnReserved6 = */ NULL,
672 /* .pfnReserved7 = */ NULL,
673#else
674# error "Not in IN_RING3!"
675#endif
676 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
677};
678
679/**
680 * The Hyper-V GIC backend.
681 */
682const PDMGICBACKEND g_GicHvBackend =
683{
684 /* .pfnReadSysReg = */ NULL,
685 /* .pfnWriteSysReg = */ NULL,
686 /* .pfnSetSpi = */ gicR3HvSetSpi,
687 /* .pfnSetPpi = */ gicR3HvSetPpi,
688 /* .pfnSendMsi = */ NULL,
689};
690
691#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
692
Note: See TracBrowser for help on using the repository browser.

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