VirtualBox

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

Last change on this file since 107044 was 106952, checked in by vboxsync, 11 days ago

VMM/ARM: Implement configuring the GICv3 emulated by Hyper-V. This API is available starting with Windows build 27744 but there is no public SDK for this yet so we have to resort to defining these structures ourselves, bugref:10392

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.7 KB
Line 
1/* $Id: GICR3Nem-win.cpp 106952 2024-11-12 09:51:44Z vboxsync $ */
2/** @file
3 * GIC - Generic Interrupt Controller Architecture (GICv3) - 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 <WinHvPlatform.h>
36
37#include <VBox/log.h>
38#include "GICInternal.h"
39#include "NEMInternal.h" /* Need access to the partition handle. */
40#include <VBox/vmm/gic.h>
41#include <VBox/vmm/cpum.h>
42#include <VBox/vmm/hm.h>
43#include <VBox/vmm/mm.h>
44#include <VBox/vmm/pdmdev.h>
45#include <VBox/vmm/ssm.h>
46#include <VBox/vmm/vm.h>
47
48#include <iprt/armv8.h>
49
50#ifndef VBOX_DEVICE_STRUCT_TESTCASE
51
52
53/*********************************************************************************************************************************
54* Defined Constants And Macros *
55*********************************************************************************************************************************/
56
57
58/*********************************************************************************************************************************
59* Structures and Typedefs *
60*********************************************************************************************************************************/
61
62/**
63 * GICHv PDM instance data (per-VM).
64 */
65typedef struct GICHVDEV
66{
67 /** Pointer to the PDM device instance. */
68 PPDMDEVINSR3 pDevIns;
69 /** The partition handle grabbed from NEM. */
70 WHV_PARTITION_HANDLE hPartition;
71} GICHVDEV;
72/** Pointer to a GIC KVM device. */
73typedef GICHVDEV *PGICHVDEV;
74/** Pointer to a const GIC KVM device. */
75typedef GICHVDEV const *PCGICHVDEV;
76
77
78/*
79 * The following definitions appeared in build 27744 allow interacting with the GICv3 controller,
80 * (there is no official SDK for this yet).
81 */
82/** @todo Better way of defining these which doesn't require casting later on when calling APIs. */
83#define MY_WHV_ARM64_IINTERRUPT_TYPE_FIXED UINT32_C(0)
84
85typedef union MY_WHV_INTERRUPT_CONTROL2
86{
87 UINT64 AsUINT64;
88 struct
89 {
90 uint32_t InterruptType;
91 UINT32 Reserved1:2;
92 UINT32 Asserted:1;
93 UINT32 Retarget:1;
94 UINT32 Reserved2:28;
95 };
96} MY_WHV_INTERRUPT_CONTROL2;
97
98
99typedef struct MY_WHV_INTERRUPT_CONTROL
100{
101 UINT64 TargetPartition;
102 MY_WHV_INTERRUPT_CONTROL2 InterruptControl;
103 UINT64 DestinationAddress;
104 UINT32 RequestedVector;
105 UINT8 TargetVtl;
106 UINT8 ReservedZ0;
107 UINT16 ReservedZ1;
108} MY_WHV_INTERRUPT_CONTROL;
109AssertCompileSize(MY_WHV_INTERRUPT_CONTROL, 32);
110
111
112/*********************************************************************************************************************************
113* Global Variables *
114*********************************************************************************************************************************/
115extern decltype(WHvRequestInterrupt) * g_pfnWHvRequestInterrupt;
116
117/*
118 * Let the preprocessor alias the APIs to import variables for better autocompletion.
119 */
120#ifndef IN_SLICKEDIT
121# define WHvRequestInterrupt g_pfnWHvRequestInterrupt
122#endif
123
124
125/**
126 * Common worker for GICR3KvmSpiSet() and GICR3KvmPpiSet().
127 *
128 * @returns VBox status code.
129 * @param pDevIns The PDM KVM GIC device instance.
130 * @param idCpu The CPU ID for which the interrupt is updated (only valid for PPIs).
131 * @param fPpi Flag whether this is a PPI or SPI.
132 * @param uIntId The interrupt ID to update.
133 * @param fAsserted Flag whether the interrupt is asserted (true) or not (false).
134 */
135DECLINLINE(int) gicR3HvSetIrq(PPDMDEVINS pDevIns, VMCPUID idCpu, bool fPpi, uint32_t uIntId, bool fAsserted)
136{
137 LogFlowFunc(("pDevIns=%p idCpu=%u fPpi=%RTbool uIntId=%u fAsserted=%RTbool\n",
138 pDevIns, idCpu, fPpi, uIntId, fAsserted));
139
140 PGICHVDEV pThis = PDMDEVINS_2_DATA(pDevIns, PGICHVDEV);
141
142 MY_WHV_INTERRUPT_CONTROL IntrCtrl;
143 IntrCtrl.TargetPartition = 0;
144 IntrCtrl.InterruptControl.InterruptType = MY_WHV_ARM64_IINTERRUPT_TYPE_FIXED;
145 IntrCtrl.InterruptControl.Reserved1 = 0;
146 IntrCtrl.InterruptControl.Asserted = fAsserted ? 1 : 0;
147 IntrCtrl.InterruptControl.Retarget = 0;
148 IntrCtrl.InterruptControl.Reserved2 = 0;
149 IntrCtrl.DestinationAddress = fPpi ? RT_BIT(idCpu) : 0; /* SGI1R_EL1 */
150 IntrCtrl.RequestedVector = fPpi ? uIntId : uIntId;
151 IntrCtrl.TargetVtl = 0;
152 IntrCtrl.ReservedZ0 = 0;
153 IntrCtrl.ReservedZ1 = 0;
154 HRESULT hrc = WHvRequestInterrupt(pThis->hPartition, (const WHV_INTERRUPT_CONTROL *)&IntrCtrl, sizeof(IntrCtrl));
155 if (SUCCEEDED(hrc))
156 return VINF_SUCCESS;
157
158 AssertFailed();
159 LogFlowFunc(("WHvRequestInterrupt() failed with %Rhrc (Last=%#x/%u)\n", hrc, RTNtLastStatusValue(), RTNtLastErrorValue()));
160 return VERR_NEM_IPE_9; /** @todo */
161}
162
163
164/**
165 * Sets the given SPI inside the in-kernel KVM GIC.
166 *
167 * @returns VBox status code.
168 * @param pVM The VM instance.
169 * @param uIntId The SPI ID to update.
170 * @param fAsserted Flag whether the interrupt is asserted (true) or not (false).
171 */
172VMMR3_INT_DECL(int) GICR3NemSpiSet(PVMCC pVM, uint32_t uIntId, bool fAsserted)
173{
174 PGIC pGic = VM_TO_GIC(pVM);
175 PPDMDEVINS pDevIns = pGic->CTX_SUFF(pDevIns);
176
177 /* idCpu is ignored for SPI interrupts. */
178 return gicR3HvSetIrq(pDevIns, 0 /*idCpu*/, false /*fPpi*/,
179 uIntId + GIC_INTID_RANGE_SPI_START, fAsserted);
180}
181
182
183/**
184 * Sets the given PPI inside the in-kernel KVM GIC.
185 *
186 * @returns VBox status code.
187 * @param pVCpu The vCPU for whih the PPI state is updated.
188 * @param uIntId The PPI ID to update.
189 * @param fAsserted Flag whether the interrupt is asserted (true) or not (false).
190 */
191VMMR3_INT_DECL(int) GICR3NemPpiSet(PVMCPUCC pVCpu, uint32_t uIntId, bool fAsserted)
192{
193 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
194
195 return gicR3HvSetIrq(pDevIns, pVCpu->idCpu, true /*fPpi*/,
196 uIntId + GIC_INTID_RANGE_PPI_START, fAsserted);
197}
198
199
200/**
201 * @interface_method_impl{PDMDEVREG,pfnReset}
202 */
203DECLCALLBACK(void) gicR3HvReset(PPDMDEVINS pDevIns)
204{
205 PVM pVM = PDMDevHlpGetVM(pDevIns);
206 VM_ASSERT_EMT0(pVM);
207 VM_ASSERT_IS_NOT_RUNNING(pVM);
208
209 RT_NOREF(pVM);
210
211 LogFlow(("GIC: gicR3HvReset\n"));
212}
213
214
215/**
216 * @interface_method_impl{PDMDEVREG,pfnDestruct}
217 */
218DECLCALLBACK(int) gicR3HvDestruct(PPDMDEVINS pDevIns)
219{
220 LogFlowFunc(("pDevIns=%p\n", pDevIns));
221 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
222
223 return VINF_SUCCESS;
224}
225
226
227/**
228 * @interface_method_impl{PDMDEVREG,pfnConstruct}
229 */
230DECLCALLBACK(int) gicR3HvConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
231{
232 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
233 PGICHVDEV pThis = PDMDEVINS_2_DATA(pDevIns, PGICHVDEV);
234 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
235 PVM pVM = PDMDevHlpGetVM(pDevIns);
236 PGIC pGic = VM_TO_GIC(pVM);
237 Assert(iInstance == 0); NOREF(iInstance);
238
239 /*
240 * Init the data.
241 */
242 pGic->pDevInsR3 = pDevIns;
243 pGic->fNemGic = true;
244 pThis->pDevIns = pDevIns;
245 pThis->hPartition = pVM->nem.s.hPartition;
246
247 /*
248 * Validate GIC settings.
249 */
250 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "DistributorMmioBase|RedistributorMmioBase|ItsMmioBase", "");
251
252 /*
253 * Disable automatic PDM locking for this device.
254 */
255 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
256 AssertRCReturn(rc, rc);
257
258 /*
259 * Register the GIC with PDM.
260 */
261 rc = PDMDevHlpApicRegister(pDevIns);
262 AssertLogRelRCReturn(rc, rc);
263
264 /*
265 * Query the MMIO ranges.
266 */
267 RTGCPHYS GCPhysMmioBaseDist = 0;
268 rc = pHlp->pfnCFGMQueryU64(pCfg, "DistributorMmioBase", &GCPhysMmioBaseDist);
269 if (RT_FAILURE(rc))
270 return PDMDEV_SET_ERROR(pDevIns, rc,
271 N_("Configuration error: Failed to get the \"DistributorMmioBase\" value"));
272
273 RTGCPHYS GCPhysMmioBaseReDist = 0;
274 rc = pHlp->pfnCFGMQueryU64(pCfg, "RedistributorMmioBase", &GCPhysMmioBaseReDist);
275 if (RT_FAILURE(rc))
276 return PDMDEV_SET_ERROR(pDevIns, rc,
277 N_("Configuration error: Failed to get the \"RedistributorMmioBase\" value"));
278
279 gicR3HvReset(pDevIns);
280 return VINF_SUCCESS;
281}
282
283
284/**
285 * GIC device registration structure.
286 */
287const PDMDEVREG g_DeviceGICNem =
288{
289 /* .u32Version = */ PDM_DEVREG_VERSION,
290 /* .uReserved0 = */ 0,
291 /* .szName = */ "gic-nem",
292 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE,
293 /* .fClass = */ PDM_DEVREG_CLASS_PIC,
294 /* .cMaxInstances = */ 1,
295 /* .uSharedVersion = */ 42,
296 /* .cbInstanceShared = */ sizeof(GICHVDEV),
297 /* .cbInstanceCC = */ 0,
298 /* .cbInstanceRC = */ 0,
299 /* .cMaxPciDevices = */ 0,
300 /* .cMaxMsixVectors = */ 0,
301 /* .pszDescription = */ "Generic Interrupt Controller",
302#if defined(IN_RING3)
303 /* .szRCMod = */ "VMMRC.rc",
304 /* .szR0Mod = */ "VMMR0.r0",
305 /* .pfnConstruct = */ gicR3HvConstruct,
306 /* .pfnDestruct = */ gicR3HvDestruct,
307 /* .pfnRelocate = */ NULL,
308 /* .pfnMemSetup = */ NULL,
309 /* .pfnPowerOn = */ NULL,
310 /* .pfnReset = */ gicR3HvReset,
311 /* .pfnSuspend = */ NULL,
312 /* .pfnResume = */ NULL,
313 /* .pfnAttach = */ NULL,
314 /* .pfnDetach = */ NULL,
315 /* .pfnQueryInterface = */ NULL,
316 /* .pfnInitComplete = */ NULL,
317 /* .pfnPowerOff = */ NULL,
318 /* .pfnSoftReset = */ NULL,
319 /* .pfnReserved0 = */ NULL,
320 /* .pfnReserved1 = */ NULL,
321 /* .pfnReserved2 = */ NULL,
322 /* .pfnReserved3 = */ NULL,
323 /* .pfnReserved4 = */ NULL,
324 /* .pfnReserved5 = */ NULL,
325 /* .pfnReserved6 = */ NULL,
326 /* .pfnReserved7 = */ NULL,
327#else
328# error "Not in IN_RING3!"
329#endif
330 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
331};
332
333#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
334
Note: See TracBrowser for help on using the repository browser.

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