pub mod gicv2;
pub mod gicv3;
pub mod its;
use std::{boxed::Box, result};
use kvm_ioctls::{DeviceFd, VmFd};
use gicv2::GICv2;
use gicv3::GICv3;
pub const IRQ_BASE: u32 = 32;
pub const IRQ_MAX: u32 = 159;
pub const GIC_REG_END_ADDRESS: u64 = 1 << 30;
#[derive(Debug)]
pub enum Error {
CreateGIC(kvm_ioctls::Error),
SetDeviceAttribute(kvm_ioctls::Error),
InconsistentVcpuCount,
InvalidVgicSysRegState,
CreateITS(kvm_ioctls::Error),
SetITSAttribute(kvm_ioctls::Error),
}
type Result<T> = result::Result<T, Error>;
pub fn save_pending_tables(fd: &DeviceFd) -> Result<()> {
let init_gic_attr = kvm_bindings::kvm_device_attr {
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
attr: u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES),
addr: 0,
flags: 0,
};
fd.set_device_attr(&init_gic_attr)
.map_err(Error::SetDeviceAttribute)
}
pub trait GICDevice: Send {
fn device_fd(&self) -> &DeviceFd;
fn device_properties(&self) -> &[u64];
fn vcpu_count(&self) -> u64;
fn fdt_compatibility(&self) -> &str;
fn fdt_maint_irq(&self) -> u32;
fn get_its_reg_range(&self, _its_type: &its::ItsType) -> Option<[u64; 2]> {
None
}
fn attach_its(&mut self, _vm: &VmFd) -> Result<()> {
Ok(())
}
fn version() -> u32
where
Self: Sized;
fn create_device(fd: DeviceFd, vcpu_count: u64) -> Box<dyn GICDevice>
where
Self: Sized;
fn init_device_attributes(gic_device: &dyn GICDevice) -> Result<()>
where
Self: Sized;
fn init_device(vm: &VmFd) -> Result<DeviceFd>
where
Self: Sized,
{
let mut gic_device = kvm_bindings::kvm_create_device {
type_: Self::version(),
fd: 0,
flags: 0,
};
vm.create_device(&mut gic_device).map_err(Error::CreateGIC)
}
fn set_device_attribute(
fd: &DeviceFd,
group: u32,
attr: u64,
addr: u64,
flags: u32,
) -> Result<()>
where
Self: Sized,
{
let attr = kvm_bindings::kvm_device_attr {
group,
attr,
addr,
flags,
};
fd.set_device_attr(&attr)
.map_err(Error::SetDeviceAttribute)?;
Ok(())
}
fn finalize_device(gic_device: &dyn GICDevice) -> Result<()>
where
Self: Sized,
{
let nr_irqs: u32 = IRQ_MAX - IRQ_BASE + 1;
let nr_irqs_ptr = &nr_irqs as *const u32;
Self::set_device_attribute(
gic_device.device_fd(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
0,
nr_irqs_ptr as u64,
0,
)?;
Self::set_device_attribute(
gic_device.device_fd(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
0,
0,
)?;
Ok(())
}
#[allow(clippy::new_ret_no_self)]
fn new(vm: &VmFd, vcpu_count: u64) -> Result<Box<dyn GICDevice>>
where
Self: Sized,
{
let vgic_fd = Self::init_device(vm)?;
let mut device = Self::create_device(vgic_fd, vcpu_count);
device.attach_its(vm)?;
Self::init_device_attributes(device.as_ref())?;
Self::finalize_device(device.as_ref())?;
Ok(device)
}
}
pub fn create_gic(vm: &VmFd, vcpu_count: u64) -> Result<Box<dyn GICDevice>> {
GICv3::new(vm, vcpu_count).or_else(|_| GICv2::new(vm, vcpu_count))
}
#[cfg(test)]
mod tests {
use super::*;
use kvm_ioctls::Kvm;
#[test]
fn test_create_gic() {
let kvm = Kvm::new().unwrap();
let vm = kvm.create_vm().unwrap();
assert!(create_gic(&vm, 1).is_ok());
}
}