VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/GICR3Nem-linux.cpp@ 108959

Last change on this file since 108959 was 108845, checked in by vboxsync, 3 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: 15.4 KB
Line 
1/* $Id: GICR3Nem-linux.cpp 108845 2025-04-04 08:46:54Z vboxsync $ */
2/** @file
3 * GIC - Generic Interrupt Controller Architecture (GIC) - KVM in kernel 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 <VBox/log.h>
34#include "GICInternal.h"
35#include "NEMInternal.h" /* Need access to the VM file descriptor. */
36#include <VBox/vmm/pdmgic.h>
37#include <VBox/vmm/cpum.h>
38#include <VBox/vmm/hm.h>
39#include <VBox/vmm/mm.h>
40#include <VBox/vmm/pdmdev.h>
41#include <VBox/vmm/ssm.h>
42#include <VBox/vmm/vm.h>
43
44#include <iprt/armv8.h>
45
46#include <errno.h>
47#include <unistd.h>
48#include <sys/ioctl.h>
49#include <sys/fcntl.h>
50#include <sys/mman.h>
51#include <linux/kvm.h>
52
53
54#ifndef VBOX_DEVICE_STRUCT_TESTCASE
55
56
57/*********************************************************************************************************************************
58* Defined Constants And Macros *
59*********************************************************************************************************************************/
60
61
62/*********************************************************************************************************************************
63* Structures and Typedefs *
64*********************************************************************************************************************************/
65
66/**
67 * GICKvm PDM instance data (per-VM).
68 */
69typedef struct GICKVMDEV
70{
71 /** Pointer to the PDM device instance. */
72 PPDMDEVINSR3 pDevIns;
73 /** The GIC device file descriptor. */
74 int fdGic;
75 /** The VM file descriptor (for KVM_IRQ_LINE). */
76 int fdKvmVm;
77} GICKVMDEV;
78/** Pointer to a GIC KVM device. */
79typedef GICKVMDEV *PGICKVMDEV;
80/** Pointer to a const GIC KVM device. */
81typedef GICKVMDEV const *PCGICKVMDEV;
82
83
84/*********************************************************************************************************************************
85* Global Variables *
86*********************************************************************************************************************************/
87#if 0
88/**
89 * System register ranges for the GIC.
90 */
91static CPUMSYSREGRANGE const g_aSysRegRanges_GIC[] =
92{
93 GIC_SYSREGRANGE(ARMV8_AARCH64_SYSREG_ICC_PMR_EL1, ARMV8_AARCH64_SYSREG_ICC_PMR_EL1, "ICC_PMR_EL1"),
94 GIC_SYSREGRANGE(ARMV8_AARCH64_SYSREG_ICC_IAR0_EL1, ARMV8_AARCH64_SYSREG_ICC_AP0R3_EL1, "ICC_IAR0_EL1 - ICC_AP0R3_EL1"),
95 GIC_SYSREGRANGE(ARMV8_AARCH64_SYSREG_ICC_AP1R0_EL1, ARMV8_AARCH64_SYSREG_ICC_NMIAR1_EL1, "ICC_AP1R0_EL1 - ICC_NMIAR1_EL1"),
96 GIC_SYSREGRANGE(ARMV8_AARCH64_SYSREG_ICC_DIR_EL1, ARMV8_AARCH64_SYSREG_ICC_SGI0R_EL1, "ICC_DIR_EL1 - ICC_SGI0R_EL1"),
97 GIC_SYSREGRANGE(ARMV8_AARCH64_SYSREG_ICC_IAR1_EL1, ARMV8_AARCH64_SYSREG_ICC_IGRPEN1_EL1, "ICC_IAR1_EL1 - ICC_IGRPEN1_EL1"),
98};
99#endif
100
101
102/**
103 * Common worker for gicR3KvmSpiSet() and gicR3KvmPpiSet().
104 *
105 * @returns VBox status code.
106 * @param pDevIns The PDM KVM GIC device instance.
107 * @param idCpu The CPU ID for which the interrupt is updated (only valid for PPIs).
108 * @param u32IrqType The actual IRQ type (PPI or SPI).
109 * @param uIntId The interrupt ID to update.
110 * @param fAsserted Flag whether the interrupt is asserted (true) or not (false).
111 */
112static int gicR3KvmSetIrq(PPDMDEVINS pDevIns, VMCPUID idCpu, uint32_t u32IrqType, uint32_t uIntId, bool fAsserted)
113{
114 LogFlowFunc(("pDevIns=%p idCpu=%u u32IrqType=%#x uIntId=%u fAsserted=%RTbool\n",
115 pDevIns, idCpu, u32IrqType, uIntId, fAsserted));
116
117 PGICKVMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PGICKVMDEV);
118
119 struct kvm_irq_level IrqLvl;
120 IrqLvl.irq = (u32IrqType << KVM_ARM_IRQ_TYPE_SHIFT)
121 | (idCpu & KVM_ARM_IRQ_VCPU_MASK) << KVM_ARM_IRQ_VCPU_SHIFT
122 | ((idCpu >> 8) & KVM_ARM_IRQ_VCPU2_MASK) << KVM_ARM_IRQ_VCPU2_SHIFT
123 | (uIntId & KVM_ARM_IRQ_NUM_MASK) << KVM_ARM_IRQ_NUM_SHIFT;
124 IrqLvl.level = fAsserted ? 1 : 0;
125 int rcLnx = ioctl(pThis->fdKvmVm, KVM_IRQ_LINE, &IrqLvl);
126 AssertReturn(rcLnx == 0, RTErrConvertFromErrno(errno));
127
128 return VINF_SUCCESS;
129}
130
131
132/**
133 * Sets the given SPI inside the in-kernel KVM GIC.
134 *
135 * @returns VBox status code.
136 * @param pVM The VM instance.
137 * @param uIntId The SPI ID to update.
138 * @param fAsserted Flag whether the interrupt is asserted (true) or not (false).
139 */
140static DECLCALLBACK(int) gicR3KvmSetSpi(PVMCC pVM, uint32_t uIntId, bool fAsserted)
141{
142 PGIC pGic = VM_TO_GIC(pVM);
143 PPDMDEVINS pDevIns = pGic->CTX_SUFF(pDevIns);
144
145 /* idCpu is ignored for SPI interrupts. */
146 return gicR3KvmSetIrq(pDevIns, 0 /*idCpu*/, KVM_ARM_IRQ_TYPE_SPI,
147 uIntId + GIC_INTID_RANGE_SPI_START, fAsserted);
148}
149
150
151/**
152 * Sets the given PPI inside the in-kernel KVM GIC.
153 *
154 * @returns VBox status code.
155 * @param pVCpu The vCPU for whih the PPI state is updated.
156 * @param uIntId The PPI ID to update.
157 * @param fAsserted Flag whether the interrupt is asserted (true) or not (false).
158 */
159static DECLCALLBACK(int) gicR3KvmSetPpi(PVMCPUCC pVCpu, uint32_t uIntId, bool fAsserted)
160{
161 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
162
163 return gicR3KvmSetIrq(pDevIns, pVCpu->idCpu, KVM_ARM_IRQ_TYPE_PPI,
164 uIntId + GIC_INTID_RANGE_PPI_START, fAsserted);
165}
166
167
168/**
169 * Sets the given device attribute in KVM to the given value.
170 *
171 * @returns VBox status code.
172 * @param pThis The KVM GIC device instance.
173 * @param u32Grp The device attribute group being set.
174 * @param u32Attr The actual attribute inside the group being set.
175 * @param pvAttrVal Where the attribute value to set.
176 * @param pszAttribute Attribute description for logging.
177 */
178static int gicR3KvmSetDevAttribute(PGICKVMDEV pThis, uint32_t u32Grp, uint32_t u32Attr, const void *pvAttrVal, const char *pszAttribute)
179{
180 struct kvm_device_attr DevAttr;
181
182 DevAttr.flags = 0;
183 DevAttr.group = u32Grp;
184 DevAttr.attr = u32Attr;
185 DevAttr.addr = (uintptr_t)pvAttrVal;
186 int rcLnx = ioctl(pThis->fdGic, KVM_HAS_DEVICE_ATTR, &DevAttr);
187 if (rcLnx < 0)
188 return PDMDevHlpVMSetError(pThis->pDevIns, RTErrConvertFromErrno(errno), RT_SRC_POS,
189 N_("KVM error: The in-kernel VGICv3 device doesn't support setting the attribute \"%s\" (%d)"),
190 pszAttribute, errno);
191
192 rcLnx = ioctl(pThis->fdGic, KVM_SET_DEVICE_ATTR, &DevAttr);
193 if (rcLnx < 0)
194 return PDMDevHlpVMSetError(pThis->pDevIns, RTErrConvertFromErrno(errno), RT_SRC_POS,
195 N_("KVM error: Setting the attribute \"%s\" for the in-kernel GICv3 failed (%d)"),
196 pszAttribute, errno);
197
198 return VINF_SUCCESS;
199}
200
201
202/**
203 * Queries the value of the given device attribute from KVM.
204 *
205 * @returns VBox status code.
206 * @param pThis The KVM GIC device instance.
207 * @param u32Grp The device attribute group being queried.
208 * @param u32Attr The actual attribute inside the group being queried.
209 * @param pvAttrVal Where the attribute value should be stored upon success.
210 * @param pszAttribute Attribute description for logging.
211 */
212static int gicR3KvmQueryDevAttribute(PGICKVMDEV pThis, uint32_t u32Grp, uint32_t u32Attr, void *pvAttrVal, const char *pszAttribute)
213{
214 struct kvm_device_attr DevAttr;
215
216 DevAttr.flags = 0;
217 DevAttr.group = u32Grp;
218 DevAttr.attr = u32Attr;
219 DevAttr.addr = (uintptr_t)pvAttrVal;
220 int rcLnx = ioctl(pThis->fdGic, KVM_GET_DEVICE_ATTR, &DevAttr);
221 if (rcLnx < 0)
222 return PDMDevHlpVMSetError(pThis->pDevIns, RTErrConvertFromErrno(errno), RT_SRC_POS,
223 N_("KVM error: Failed to query attribute \"%s\" from the in-kernel VGICv3 (%d)"),
224 pszAttribute, errno);
225
226 return VINF_SUCCESS;
227}
228
229
230/**
231 * @interface_method_impl{PDMDEVREG,pfnReset}
232 */
233DECLCALLBACK(void) gicR3KvmReset(PPDMDEVINS pDevIns)
234{
235 PVM pVM = PDMDevHlpGetVM(pDevIns);
236 VM_ASSERT_EMT0(pVM);
237 VM_ASSERT_IS_NOT_RUNNING(pVM);
238
239 RT_NOREF(pVM);
240
241 LogFlow(("GIC: gicR3KvmReset\n"));
242}
243
244
245/**
246 * @interface_method_impl{PDMDEVREG,pfnDestruct}
247 */
248DECLCALLBACK(int) gicR3KvmDestruct(PPDMDEVINS pDevIns)
249{
250 LogFlowFunc(("pDevIns=%p\n", pDevIns));
251 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
252
253 PGICKVMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PGICKVMDEV);
254
255 close(pThis->fdGic);
256 pThis->fdGic = 0;
257
258 return VINF_SUCCESS;
259}
260
261
262/**
263 * @interface_method_impl{PDMDEVREG,pfnConstruct}
264 */
265DECLCALLBACK(int) gicR3KvmConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
266{
267 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
268 PGICKVMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PGICKVMDEV);
269 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
270 PVM pVM = PDMDevHlpGetVM(pDevIns);
271 PGIC pGic = VM_TO_GIC(pVM);
272 Assert(iInstance == 0); NOREF(iInstance);
273
274 /*
275 * Init the data.
276 */
277 pGic->pDevInsR3 = pDevIns;
278 pThis->pDevIns = pDevIns;
279 pThis->fdKvmVm = pVM->nem.s.fdVm;
280
281 /*
282 * Validate GIC settings.
283 */
284 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "DistributorMmioBase|RedistributorMmioBase|ItsMmioBase"
285 "|ArchRev"
286 "|Lpi"
287 "|Mbi", "");
288
289 /*
290 * Disable automatic PDM locking for this device.
291 */
292 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
293 AssertRCReturn(rc, rc);
294
295 /*
296 * Register the GIC with PDM.
297 */
298 rc = PDMDevHlpIcRegister(pDevIns);
299 AssertLogRelRCReturn(rc, rc);
300
301 rc = PDMGicRegisterBackend(pVM, PDMGICBACKENDTYPE_KVM, &g_GicKvmBackend);
302 AssertLogRelRCReturn(rc, rc);
303
304 /*
305 * Query the MMIO ranges.
306 */
307 RTGCPHYS GCPhysMmioBaseDist = 0;
308 rc = pHlp->pfnCFGMQueryU64(pCfg, "DistributorMmioBase", &GCPhysMmioBaseDist);
309 if (RT_FAILURE(rc))
310 return PDMDEV_SET_ERROR(pDevIns, rc,
311 N_("Configuration error: Failed to get the \"DistributorMmioBase\" value"));
312
313 RTGCPHYS GCPhysMmioBaseReDist = 0;
314 rc = pHlp->pfnCFGMQueryU64(pCfg, "RedistributorMmioBase", &GCPhysMmioBaseReDist);
315 if (RT_FAILURE(rc))
316 return PDMDEV_SET_ERROR(pDevIns, rc,
317 N_("Configuration error: Failed to get the \"RedistributorMmioBase\" value"));
318
319 /*
320 * Create the device.
321 */
322 struct kvm_create_device DevCreate;
323 DevCreate.type = KVM_DEV_TYPE_ARM_VGIC_V3;
324 DevCreate.fd = 0;
325 DevCreate.flags = 0;
326 int rcLnx = ioctl(pThis->fdKvmVm, KVM_CREATE_DEVICE, &DevCreate);
327 if (rcLnx < 0)
328 return PDMDevHlpVMSetError(pDevIns, RTErrConvertFromErrno(errno), RT_SRC_POS,
329 N_("KVM error: Creating the in-kernel VGICv3 device failed (%d)"), errno);
330
331 pThis->fdGic = DevCreate.fd;
332
333 /*
334 * Set the distributor and re-distributor base.
335 */
336 rc = gicR3KvmSetDevAttribute(pThis, KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V3_ADDR_TYPE_DIST, &GCPhysMmioBaseDist,
337 "Distributor MMIO base");
338 AssertRCReturn(rc, rc);
339
340 rc = gicR3KvmSetDevAttribute(pThis, KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V3_ADDR_TYPE_REDIST, &GCPhysMmioBaseReDist,
341 "Re-Distributor MMIO base");
342 AssertRCReturn(rc, rc);
343
344 /* Query and log the number of IRQ lines this GIC supports. */
345 uint32_t cIrqs = 0;
346 rc = gicR3KvmQueryDevAttribute(pThis, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0, &cIrqs,
347 "IRQ line count");
348 AssertRCReturn(rc, rc);
349 LogRel(("GICR3Kvm: Supports %u IRQs\n", cIrqs));
350
351 /*
352 * Init the controller.
353 */
354 rc = gicR3KvmSetDevAttribute(pThis, KVM_DEV_ARM_VGIC_GRP_CTRL, KVM_DEV_ARM_VGIC_CTRL_INIT, NULL,
355 "VGIC init");
356 AssertRCReturn(rc, rc);
357
358 gicR3KvmReset(pDevIns);
359 return VINF_SUCCESS;
360}
361
362
363/**
364 * GIC device registration structure.
365 */
366const PDMDEVREG g_DeviceGICNem =
367{
368 /* .u32Version = */ PDM_DEVREG_VERSION,
369 /* .uReserved0 = */ 0,
370 /* .szName = */ "gic-nem",
371 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE,
372 /* .fClass = */ PDM_DEVREG_CLASS_PIC,
373 /* .cMaxInstances = */ 1,
374 /* .uSharedVersion = */ 42,
375 /* .cbInstanceShared = */ sizeof(GICDEV),
376 /* .cbInstanceCC = */ 0,
377 /* .cbInstanceRC = */ 0,
378 /* .cMaxPciDevices = */ 0,
379 /* .cMaxMsixVectors = */ 0,
380 /* .pszDescription = */ "Generic Interrupt Controller",
381#if defined(IN_RING3)
382 /* .szRCMod = */ "VMMRC.rc",
383 /* .szR0Mod = */ "VMMR0.r0",
384 /* .pfnConstruct = */ gicR3KvmConstruct,
385 /* .pfnDestruct = */ gicR3KvmDestruct,
386 /* .pfnRelocate = */ NULL,
387 /* .pfnMemSetup = */ NULL,
388 /* .pfnPowerOn = */ NULL,
389 /* .pfnReset = */ gicR3KvmReset,
390 /* .pfnSuspend = */ NULL,
391 /* .pfnResume = */ NULL,
392 /* .pfnAttach = */ NULL,
393 /* .pfnDetach = */ NULL,
394 /* .pfnQueryInterface = */ NULL,
395 /* .pfnInitComplete = */ NULL,
396 /* .pfnPowerOff = */ NULL,
397 /* .pfnSoftReset = */ NULL,
398 /* .pfnReserved0 = */ NULL,
399 /* .pfnReserved1 = */ NULL,
400 /* .pfnReserved2 = */ NULL,
401 /* .pfnReserved3 = */ NULL,
402 /* .pfnReserved4 = */ NULL,
403 /* .pfnReserved5 = */ NULL,
404 /* .pfnReserved6 = */ NULL,
405 /* .pfnReserved7 = */ NULL,
406#else
407# error "Not in IN_RING3!"
408#endif
409 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
410};
411
412/**
413 * The KVM GIC backend.
414 */
415const PDMGICBACKEND g_GicKvmBackend =
416{
417 /* .pfnReadSysReg = */ NULL,
418 /* .pfnWriteSysReg = */ NULL,
419 /* .pfnSetSpi = */ gicR3KvmSetSpi,
420 /* .pfnSetPpi = */ gicR3KvmSetPpi,
421 /* .pfnSendMsi = */ NULL,
422};
423
424#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
425
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