Skip to content

Commit 6123b74

Browse files
committed
devices/legacy: add support for KVM GICv2
In the past, we had support for both requesting KVM the instantiation of a GICv3 or a GICv2. We lost this ability with the interrupt controller refactoring, supporting only GICv2. Turns out the SoC on some aarch64 boards (namely, the Raspberry Pi family) doesn't implement an architected vGIC, which is required by the GICv3 implementation in the KVM. As RPi boards are very popular, and the maintenance cost for this device is very small, let's reimplement support for requesting KVM the instantiation of a GICv2. Signed-off-by: Sergio Lopez <[email protected]>
1 parent 357ec63 commit 6123b74

File tree

2 files changed

+150
-0
lines changed

2 files changed

+150
-0
lines changed

src/devices/src/legacy/kvmgicv2.rs

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// Copyright 2025 The libkrun Authors. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use std::io;
5+
6+
use crate::bus::BusDevice;
7+
use crate::legacy::gic::GICDevice;
8+
use crate::legacy::irqchip::IrqChipT;
9+
use crate::Error as DeviceError;
10+
11+
use kvm_ioctls::{DeviceFd, VmFd};
12+
use utils::eventfd::EventFd;
13+
14+
const KVM_VGIC_V2_DIST_SIZE: u64 = 0x1000;
15+
const KVM_VGIC_V2_CPU_SIZE: u64 = 0x2000;
16+
17+
// Device trees specific constants
18+
const ARCH_GIC_V2_MAINT_IRQ: u32 = 8;
19+
20+
pub struct KvmGicV2 {
21+
_device_fd: DeviceFd,
22+
23+
/// GIC device properties, to be used for setting up the fdt entry
24+
properties: [u64; 4],
25+
26+
/// Number of CPUs handled by the device
27+
vcpu_count: u64,
28+
}
29+
30+
impl KvmGicV2 {
31+
pub fn new(vm: &VmFd, vcpu_count: u64) -> Self {
32+
let dist_size = KVM_VGIC_V2_DIST_SIZE;
33+
let dist_addr = arch::MMIO_MEM_START - dist_size;
34+
let cpu_size = KVM_VGIC_V2_CPU_SIZE;
35+
let cpu_addr = dist_addr - cpu_size;
36+
37+
let mut gic_device = kvm_bindings::kvm_create_device {
38+
type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2,
39+
fd: 0,
40+
flags: 0,
41+
};
42+
let device_fd = vm.create_device(&mut gic_device).unwrap();
43+
44+
let attr = kvm_bindings::kvm_device_attr {
45+
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
46+
attr: u64::from(kvm_bindings::KVM_VGIC_V2_ADDR_TYPE_DIST),
47+
addr: &dist_addr as *const u64 as u64,
48+
flags: 0,
49+
};
50+
device_fd.set_device_attr(&attr).unwrap();
51+
52+
let attr = kvm_bindings::kvm_device_attr {
53+
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
54+
attr: u64::from(kvm_bindings::KVM_VGIC_V2_ADDR_TYPE_CPU),
55+
addr: &cpu_addr as *const u64 as u64,
56+
flags: 0,
57+
};
58+
device_fd.set_device_attr(&attr).unwrap();
59+
60+
let nr_irqs: u32 = arch::aarch64::layout::IRQ_MAX - arch::aarch64::layout::IRQ_BASE + 1;
61+
let nr_irqs_ptr = &nr_irqs as *const u32;
62+
let attr = kvm_bindings::kvm_device_attr {
63+
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
64+
attr: 0,
65+
addr: nr_irqs_ptr as u64,
66+
flags: 0,
67+
};
68+
device_fd.set_device_attr(&attr).unwrap();
69+
70+
let attr = kvm_bindings::kvm_device_attr {
71+
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
72+
attr: u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
73+
addr: 0,
74+
flags: 0,
75+
};
76+
device_fd.set_device_attr(&attr).unwrap();
77+
78+
Self {
79+
_device_fd: device_fd,
80+
properties: [dist_addr, dist_size, cpu_addr, cpu_size],
81+
vcpu_count,
82+
}
83+
}
84+
}
85+
86+
impl IrqChipT for KvmGicV2 {
87+
fn get_mmio_addr(&self) -> u64 {
88+
0
89+
}
90+
91+
fn get_mmio_size(&self) -> u64 {
92+
0
93+
}
94+
95+
fn set_irq(
96+
&self,
97+
_irq_line: Option<u32>,
98+
interrupt_evt: Option<&EventFd>,
99+
) -> Result<(), DeviceError> {
100+
if let Some(interrupt_evt) = interrupt_evt {
101+
if let Err(e) = interrupt_evt.write(1) {
102+
error!("Failed to signal used queue: {e:?}");
103+
return Err(DeviceError::FailedSignalingUsedQueue(e));
104+
}
105+
} else {
106+
error!("EventFd not set up for irq line");
107+
return Err(DeviceError::FailedSignalingUsedQueue(io::Error::new(
108+
io::ErrorKind::NotFound,
109+
"EventFd not set up for irq line".to_string(),
110+
)));
111+
}
112+
Ok(())
113+
}
114+
}
115+
116+
impl BusDevice for KvmGicV2 {
117+
fn read(&mut self, _vcpuid: u64, _offset: u64, _data: &mut [u8]) {
118+
unreachable!("MMIO operations are managed in-kernel");
119+
}
120+
121+
fn write(&mut self, _vcpuid: u64, _offset: u64, _data: &[u8]) {
122+
unreachable!("MMIO operations are managed in-kernel");
123+
}
124+
}
125+
126+
impl GICDevice for KvmGicV2 {
127+
fn device_properties(&self) -> Vec<u64> {
128+
self.properties.to_vec()
129+
}
130+
131+
fn vcpu_count(&self) -> u64 {
132+
self.vcpu_count
133+
}
134+
135+
fn fdt_compatibility(&self) -> String {
136+
"arm,gic-400".to_string()
137+
}
138+
139+
fn fdt_maint_irq(&self) -> u32 {
140+
ARCH_GIC_V2_MAINT_IRQ
141+
}
142+
143+
fn version(&self) -> u32 {
144+
kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2
145+
}
146+
}

src/devices/src/legacy/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ mod irqchip;
1818
#[cfg(all(target_os = "linux", target_arch = "riscv64"))]
1919
mod kvmaia;
2020
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
21+
mod kvmgicv2;
22+
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
2123
mod kvmgicv3;
2224
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
2325
mod kvmioapic;
@@ -56,6 +58,8 @@ pub use self::irqchip::{IrqChip, IrqChipDevice, IrqChipT};
5658
#[cfg(all(target_os = "linux", target_arch = "riscv64"))]
5759
pub use self::kvmaia::KvmAia;
5860
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
61+
pub use self::kvmgicv2::KvmGicV2;
62+
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
5963
pub use self::kvmgicv3::KvmGicV3;
6064
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
6165
pub use self::kvmioapic::KvmIoapic;

0 commit comments

Comments
 (0)