VirtualBox

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

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

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

  • 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 104392 2024-04-22 11:09:21Z 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/*********************************************************************************************************************************
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 GICv3.
90 */
91static CPUMSYSREGRANGE const g_aSysRegRanges_GICv3[] =
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 */
140VMMR3_INT_DECL(int) GICR3KvmSpiSet(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 */
159VMMR3_INT_DECL(int) GICR3KvmPpiSet(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_SPI,
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 pGic->fKvmGic = true;
279 pThis->pDevIns = pDevIns;
280 pThis->fdKvmVm = pVM->nem.s.fdVm;
281
282 /*
283 * Validate GIC settings.
284 */
285 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "DistributorMmioBase|RedistributorMmioBase", "");
286
287 /*
288 * Disable automatic PDM locking for this device.
289 */
290 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
291 AssertRCReturn(rc, rc);
292
293 /*
294 * Register the GIC with PDM.
295 */
296 rc = PDMDevHlpApicRegister(pDevIns);
297 AssertLogRelRCReturn(rc, rc);
298
299 /*
300 * Query the MMIO ranges.
301 */
302 RTGCPHYS GCPhysMmioBaseDist = 0;
303 rc = pHlp->pfnCFGMQueryU64(pCfg, "DistributorMmioBase", &GCPhysMmioBaseDist);
304 if (RT_FAILURE(rc))
305 return PDMDEV_SET_ERROR(pDevIns, rc,
306 N_("Configuration error: Failed to get the \"DistributorMmioBase\" value"));
307
308 RTGCPHYS GCPhysMmioBaseReDist = 0;
309 rc = pHlp->pfnCFGMQueryU64(pCfg, "RedistributorMmioBase", &GCPhysMmioBaseReDist);
310 if (RT_FAILURE(rc))
311 return PDMDEV_SET_ERROR(pDevIns, rc,
312 N_("Configuration error: Failed to get the \"RedistributorMmioBase\" value"));
313
314 /*
315 * Create the device.
316 */
317 struct kvm_create_device DevCreate;
318 DevCreate.type = KVM_DEV_TYPE_ARM_VGIC_V3;
319 DevCreate.fd = 0;
320 DevCreate.flags = 0;
321 int rcLnx = ioctl(pThis->fdKvmVm, KVM_CREATE_DEVICE, &DevCreate);
322 if (rcLnx < 0)
323 return PDMDevHlpVMSetError(pDevIns, RTErrConvertFromErrno(errno), RT_SRC_POS,
324 N_("KVM error: Creating the in-kernel VGICv3 device failed (%d)"), errno);
325
326 pThis->fdGic = DevCreate.fd;
327
328 /*
329 * Set the distributor and re-distributor base.
330 */
331 rc = gicR3KvmSetDevAttribute(pThis, KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V3_ADDR_TYPE_DIST, &GCPhysMmioBaseDist,
332 "Distributor MMIO base");
333 AssertRCReturn(rc, rc);
334
335 rc = gicR3KvmSetDevAttribute(pThis, KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V3_ADDR_TYPE_REDIST, &GCPhysMmioBaseReDist,
336 "Re-Distributor MMIO base");
337 AssertRCReturn(rc, rc);
338
339 /* Query and log the number of IRQ lines this GIC supports. */
340 uint32_t cIrqs = 0;
341 rc = gicR3KvmQueryDevAttribute(pThis, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0, &cIrqs,
342 "IRQ line count");
343 AssertRCReturn(rc, rc);
344 LogRel(("GICR3Kvm: Supports %u IRQs\n", cIrqs));
345
346 /*
347 * Init the controller.
348 */
349 rc = gicR3KvmSetDevAttribute(pThis, KVM_DEV_ARM_VGIC_GRP_CTRL, KVM_DEV_ARM_VGIC_CTRL_INIT, NULL,
350 "VGIC init");
351 AssertRCReturn(rc, rc);
352
353 gicR3Reset(pDevIns);
354 return VINF_SUCCESS;
355}
356
357
358/**
359 * GIC device registration structure.
360 */
361const PDMDEVREG g_DeviceGICKvm =
362{
363 /* .u32Version = */ PDM_DEVREG_VERSION,
364 /* .uReserved0 = */ 0,
365 /* .szName = */ "gic-kvm",
366 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE,
367 /* .fClass = */ PDM_DEVREG_CLASS_PIC,
368 /* .cMaxInstances = */ 1,
369 /* .uSharedVersion = */ 42,
370 /* .cbInstanceShared = */ sizeof(GICDEV),
371 /* .cbInstanceCC = */ 0,
372 /* .cbInstanceRC = */ 0,
373 /* .cMaxPciDevices = */ 0,
374 /* .cMaxMsixVectors = */ 0,
375 /* .pszDescription = */ "Generic Interrupt Controller",
376#if defined(IN_RING3)
377 /* .szRCMod = */ "VMMRC.rc",
378 /* .szR0Mod = */ "VMMR0.r0",
379 /* .pfnConstruct = */ gicR3KvmConstruct,
380 /* .pfnDestruct = */ gicR3KvmDestruct,
381 /* .pfnRelocate = */ NULL,
382 /* .pfnMemSetup = */ NULL,
383 /* .pfnPowerOn = */ NULL,
384 /* .pfnReset = */ gicR3KvmReset,
385 /* .pfnSuspend = */ NULL,
386 /* .pfnResume = */ NULL,
387 /* .pfnAttach = */ NULL,
388 /* .pfnDetach = */ NULL,
389 /* .pfnQueryInterface = */ NULL,
390 /* .pfnInitComplete = */ NULL,
391 /* .pfnPowerOff = */ NULL,
392 /* .pfnSoftReset = */ NULL,
393 /* .pfnReserved0 = */ NULL,
394 /* .pfnReserved1 = */ NULL,
395 /* .pfnReserved2 = */ NULL,
396 /* .pfnReserved3 = */ NULL,
397 /* .pfnReserved4 = */ NULL,
398 /* .pfnReserved5 = */ NULL,
399 /* .pfnReserved6 = */ NULL,
400 /* .pfnReserved7 = */ NULL,
401#else
402# error "Not in IN_RING3!"
403#endif
404 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
405};
406
407#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
408
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use