VirtualBox

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

Last change on this file since 108748 was 108748, checked in by vboxsync, 3 weeks ago

VMM/GICR3Nem-win.cpp: Adapt to the latest Hyper-V API changes in Windows 11 build 27813, bugref:10392

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.8 KB
Line 
1/* $Id: GICR3Nem-win.cpp 108748 2025-03-26 07:48:57Z 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
586 /*
587 * Disable automatic PDM locking for this device.
588 */
589 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
590 AssertRCReturn(rc, rc);
591
592 /*
593 * Register the GIC with PDM.
594 */
595 rc = PDMDevHlpIcRegister(pDevIns);
596 AssertLogRelRCReturn(rc, rc);
597
598 rc = PDMGicRegisterBackend(pVM, PDMGICBACKENDTYPE_HYPERV, &g_GicHvBackend);
599 AssertLogRelRCReturn(rc, rc);
600
601 /*
602 * Query the MMIO ranges.
603 */
604 RTGCPHYS GCPhysMmioBaseDist = 0;
605 rc = pHlp->pfnCFGMQueryU64(pCfg, "DistributorMmioBase", &GCPhysMmioBaseDist);
606 if (RT_FAILURE(rc))
607 return PDMDEV_SET_ERROR(pDevIns, rc,
608 N_("Configuration error: Failed to get the \"DistributorMmioBase\" value"));
609
610 RTGCPHYS GCPhysMmioBaseReDist = 0;
611 rc = pHlp->pfnCFGMQueryU64(pCfg, "RedistributorMmioBase", &GCPhysMmioBaseReDist);
612 if (RT_FAILURE(rc))
613 return PDMDEV_SET_ERROR(pDevIns, rc,
614 N_("Configuration error: Failed to get the \"RedistributorMmioBase\" value"));
615
616 /*
617 * Register saved state callbacks.
618 */
619 rc = PDMDevHlpSSMRegister(pDevIns, GIC_NEM_SAVED_STATE_VERSION, 0 /*cbGuess*/, gicR3HvSaveExec, gicR3HvLoadExec);
620 AssertRCReturn(rc, rc);
621
622 gicR3HvReset(pDevIns);
623 return VINF_SUCCESS;
624}
625
626
627/**
628 * GIC device registration structure.
629 */
630const PDMDEVREG g_DeviceGICNem =
631{
632 /* .u32Version = */ PDM_DEVREG_VERSION,
633 /* .uReserved0 = */ 0,
634 /* .szName = */ "gic-nem",
635 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE,
636 /* .fClass = */ PDM_DEVREG_CLASS_PIC,
637 /* .cMaxInstances = */ 1,
638 /* .uSharedVersion = */ 42,
639 /* .cbInstanceShared = */ sizeof(GICHVDEV),
640 /* .cbInstanceCC = */ 0,
641 /* .cbInstanceRC = */ 0,
642 /* .cMaxPciDevices = */ 0,
643 /* .cMaxMsixVectors = */ 0,
644 /* .pszDescription = */ "Generic Interrupt Controller",
645#if defined(IN_RING3)
646 /* .szRCMod = */ "VMMRC.rc",
647 /* .szR0Mod = */ "VMMR0.r0",
648 /* .pfnConstruct = */ gicR3HvConstruct,
649 /* .pfnDestruct = */ gicR3HvDestruct,
650 /* .pfnRelocate = */ NULL,
651 /* .pfnMemSetup = */ NULL,
652 /* .pfnPowerOn = */ NULL,
653 /* .pfnReset = */ gicR3HvReset,
654 /* .pfnSuspend = */ NULL,
655 /* .pfnResume = */ NULL,
656 /* .pfnAttach = */ NULL,
657 /* .pfnDetach = */ NULL,
658 /* .pfnQueryInterface = */ NULL,
659 /* .pfnInitComplete = */ NULL,
660 /* .pfnPowerOff = */ NULL,
661 /* .pfnSoftReset = */ NULL,
662 /* .pfnReserved0 = */ NULL,
663 /* .pfnReserved1 = */ NULL,
664 /* .pfnReserved2 = */ NULL,
665 /* .pfnReserved3 = */ NULL,
666 /* .pfnReserved4 = */ NULL,
667 /* .pfnReserved5 = */ NULL,
668 /* .pfnReserved6 = */ NULL,
669 /* .pfnReserved7 = */ NULL,
670#else
671# error "Not in IN_RING3!"
672#endif
673 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
674};
675
676/**
677 * The Hyper-V GIC backend.
678 */
679const PDMGICBACKEND g_GicHvBackend =
680{
681 /* .pfnReadSysReg = */ NULL,
682 /* .pfnWriteSysReg = */ NULL,
683 /* .pfnSetSpi = */ gicR3HvSetSpi,
684 /* .pfnSetPpi = */ gicR3HvSetPpi,
685 /* .pfnSendMsi = */ NULL,
686};
687
688#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
689
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