VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/GICR3Kvm.cpp@ 104386

Last change on this file since 104386 was 104386, checked in by vboxsync, 11 months ago

VMM/GIC: Add a dedicated GIC device implementation for linux.arm64 which interfaces with the in-kernel KVM GIC device emulation, bugref:10391

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