Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions src/devices/src/legacy/kvmgicv2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// Copyright 2025 The libkrun Authors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use std::io;

use crate::bus::BusDevice;
use crate::legacy::gic::GICDevice;
use crate::legacy::irqchip::IrqChipT;
use crate::Error as DeviceError;

use kvm_ioctls::{DeviceFd, VmFd};
use utils::eventfd::EventFd;

const KVM_VGIC_V2_DIST_SIZE: u64 = 0x1000;
const KVM_VGIC_V2_CPU_SIZE: u64 = 0x2000;

// Device trees specific constants
const ARCH_GIC_V2_MAINT_IRQ: u32 = 8;

pub struct KvmGicV2 {
_device_fd: DeviceFd,

/// GIC device properties, to be used for setting up the fdt entry
properties: [u64; 4],

/// Number of CPUs handled by the device
vcpu_count: u64,
}

impl KvmGicV2 {
pub fn new(vm: &VmFd, vcpu_count: u64) -> Self {
let dist_size = KVM_VGIC_V2_DIST_SIZE;
let dist_addr = arch::MMIO_MEM_START - dist_size;
let cpu_size = KVM_VGIC_V2_CPU_SIZE;
let cpu_addr = dist_addr - cpu_size;

let mut gic_device = kvm_bindings::kvm_create_device {
type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2,
fd: 0,
flags: 0,
};
let device_fd = vm.create_device(&mut gic_device).unwrap();

let attr = kvm_bindings::kvm_device_attr {
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
attr: u64::from(kvm_bindings::KVM_VGIC_V2_ADDR_TYPE_DIST),
addr: &dist_addr as *const u64 as u64,
flags: 0,
};
device_fd.set_device_attr(&attr).unwrap();

let attr = kvm_bindings::kvm_device_attr {
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
attr: u64::from(kvm_bindings::KVM_VGIC_V2_ADDR_TYPE_CPU),
addr: &cpu_addr as *const u64 as u64,
flags: 0,
};
device_fd.set_device_attr(&attr).unwrap();

let nr_irqs: u32 = arch::aarch64::layout::IRQ_MAX - arch::aarch64::layout::IRQ_BASE + 1;
let nr_irqs_ptr = &nr_irqs as *const u32;
let attr = kvm_bindings::kvm_device_attr {
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
attr: 0,
addr: nr_irqs_ptr as u64,
flags: 0,
};
device_fd.set_device_attr(&attr).unwrap();

let attr = kvm_bindings::kvm_device_attr {
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
attr: u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
addr: 0,
flags: 0,
};
device_fd.set_device_attr(&attr).unwrap();

Self {
_device_fd: device_fd,
properties: [dist_addr, dist_size, cpu_addr, cpu_size],
vcpu_count,
}
}
}

impl IrqChipT for KvmGicV2 {
fn get_mmio_addr(&self) -> u64 {
0
}

fn get_mmio_size(&self) -> u64 {
0
}

fn set_irq(
&self,
_irq_line: Option<u32>,
interrupt_evt: Option<&EventFd>,
) -> Result<(), DeviceError> {
if let Some(interrupt_evt) = interrupt_evt {
if let Err(e) = interrupt_evt.write(1) {
error!("Failed to signal used queue: {e:?}");
return Err(DeviceError::FailedSignalingUsedQueue(e));
}
} else {
error!("EventFd not set up for irq line");
return Err(DeviceError::FailedSignalingUsedQueue(io::Error::new(
io::ErrorKind::NotFound,
"EventFd not set up for irq line".to_string(),
)));
}
Ok(())
}
}

impl BusDevice for KvmGicV2 {
fn read(&mut self, _vcpuid: u64, _offset: u64, _data: &mut [u8]) {
unreachable!("MMIO operations are managed in-kernel");
}

fn write(&mut self, _vcpuid: u64, _offset: u64, _data: &[u8]) {
unreachable!("MMIO operations are managed in-kernel");
}
}

impl GICDevice for KvmGicV2 {
fn device_properties(&self) -> Vec<u64> {
self.properties.to_vec()
}

fn vcpu_count(&self) -> u64 {
self.vcpu_count
}

fn fdt_compatibility(&self) -> String {
"arm,gic-400".to_string()
}

fn fdt_maint_irq(&self) -> u32 {
ARCH_GIC_V2_MAINT_IRQ
}

fn version(&self) -> u32 {
kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2
}
}
18 changes: 9 additions & 9 deletions src/devices/src/legacy/kvmgicv3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::legacy::gic::GICDevice;
use crate::legacy::irqchip::IrqChipT;
use crate::Error as DeviceError;

use kvm_ioctls::{DeviceFd, VmFd};
use kvm_ioctls::{DeviceFd, Error, VmFd};
use utils::eventfd::EventFd;

const KVM_VGIC_V3_BASE_SIZE: u64 = 0x0001_0000;
Expand All @@ -27,7 +27,7 @@ pub struct KvmGicV3 {
}

impl KvmGicV3 {
pub fn new(vm: &VmFd, vcpu_count: u64) -> Self {
pub fn new(vm: &VmFd, vcpu_count: u64) -> Result<Self, Error> {
let dist_size = KVM_VGIC_V3_BASE_SIZE;
let dist_addr = arch::MMIO_MEM_START - dist_size;
let redist_size = 2 * dist_size;
Expand All @@ -39,23 +39,23 @@ impl KvmGicV3 {
fd: 0,
flags: 0,
};
let device_fd = vm.create_device(&mut gic_device).unwrap();
let device_fd = vm.create_device(&mut gic_device)?;

let attr = kvm_bindings::kvm_device_attr {
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
attr: u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_DIST),
addr: &dist_addr as *const u64 as u64,
flags: 0,
};
device_fd.set_device_attr(&attr).unwrap();
device_fd.set_device_attr(&attr)?;

let attr = kvm_bindings::kvm_device_attr {
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
attr: u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST),
addr: &redists_addr as *const u64 as u64,
flags: 0,
};
device_fd.set_device_attr(&attr).unwrap();
device_fd.set_device_attr(&attr)?;

let nr_irqs: u32 = arch::aarch64::layout::IRQ_MAX - arch::aarch64::layout::IRQ_BASE + 1;
let nr_irqs_ptr = &nr_irqs as *const u32;
Expand All @@ -65,21 +65,21 @@ impl KvmGicV3 {
addr: nr_irqs_ptr as u64,
flags: 0,
};
device_fd.set_device_attr(&attr).unwrap();
device_fd.set_device_attr(&attr)?;

let attr = kvm_bindings::kvm_device_attr {
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
attr: u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
addr: 0,
flags: 0,
};
device_fd.set_device_attr(&attr).unwrap();
device_fd.set_device_attr(&attr)?;

Self {
Ok(Self {
_device_fd: device_fd,
properties: [dist_addr, dist_size, redists_addr, redists_size],
vcpu_count,
}
})
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/devices/src/legacy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ mod irqchip;
#[cfg(all(target_os = "linux", target_arch = "riscv64"))]
mod kvmaia;
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
mod kvmgicv2;
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
mod kvmgicv3;
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
mod kvmioapic;
Expand Down Expand Up @@ -56,6 +58,8 @@ pub use self::irqchip::{IrqChip, IrqChipDevice, IrqChipT};
#[cfg(all(target_os = "linux", target_arch = "riscv64"))]
pub use self::kvmaia::KvmAia;
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
pub use self::kvmgicv2::KvmGicV2;
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
pub use self::kvmgicv3::KvmGicV3;
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub use self::kvmioapic::KvmIoapic;
Expand Down
23 changes: 17 additions & 6 deletions src/vmm/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ use crate::vmm_config::external_kernel::{ExternalKernel, KernelFormat};
use crate::vmm_config::net::NetBuilder;
#[cfg(all(target_os = "linux", target_arch = "riscv64"))]
use devices::legacy::KvmAia;
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
use devices::legacy::KvmGicV3;
#[cfg(target_arch = "x86_64")]
use devices::legacy::KvmIoapic;
use devices::legacy::Serial;
Expand All @@ -42,6 +40,8 @@ use devices::legacy::{GicV3, HvfGicV3};
#[cfg(target_arch = "x86_64")]
use devices::legacy::{IoApic, IrqChipT};
use devices::legacy::{IrqChip, IrqChipDevice};
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
use devices::legacy::{KvmGicV2, KvmGicV3};
use devices::virtio::{port_io, MmioTransport, PortDescription, VirtioDevice, Vsock};

#[cfg(feature = "tee")]
Expand Down Expand Up @@ -837,10 +837,21 @@ pub fn build_microvm(
)
.map_err(StartMicrovmError::Internal)?;

intc = Arc::new(Mutex::new(IrqChipDevice::new(Box::new(KvmGicV3::new(
vm.fd(),
vm_resources.vm_config().vcpu_count.unwrap() as u64,
)))));
intc = {
// The SoC in some popular boards (namely, the RPi family) doesn't support an
// architected vGIC, which is required for requesting KVM the instantiation of a
// GICv3. To relieve the users from having to configure the gic version manually,
// try first to instantiate a GICv3, and fall back to a GICv2 if it fails.
let vcpu_count = vm_resources.vm_config().vcpu_count.unwrap() as u64;
let gic = match KvmGicV3::new(vm.fd(), vcpu_count) {
Ok(gicv3) => IrqChipDevice::new(Box::new(gicv3)),
Err(_) => {
warn!("KVM GICv3 creation failed, falling back to KVM GICv2");
IrqChipDevice::new(Box::new(KvmGicV2::new(vm.fd(), vcpu_count)))
}
};
Arc::new(Mutex::new(gic))
};

attach_legacy_devices(
&vm,
Expand Down