VirtualBox

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

Last change on this file since 108484 was 108403, checked in by vboxsync, 2 months ago

VMM: scm fixes, bugref:10392

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