Skip to content

Commit 129e2d2

Browse files
MrXinWangandreeaflorescu
authored andcommitted
Add ioctl KVM_IRQ_LINE from CrosVM to rust-vmm
1. Added implementation and documentation of ioctl KVM_IRQ_LINE as function set_irq_line for both x86 and Arm architectures. 2. Added unit tests of ioctl KVM_IRQ_LINE for x86, x86_64 and aarch64 architectures. 3. Move common code to helper functions `set_supported_nr_irqs` and `request_gic_init`. Signed-off-by: Henry Wang <[email protected]>
1 parent 001403b commit 129e2d2

File tree

2 files changed

+164
-21
lines changed

2 files changed

+164
-21
lines changed

src/ioctls/vm.rs

Lines changed: 156 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,74 @@ impl VmFd {
891891
}
892892
}
893893

894+
/// Sets the level on the given irq to 1 if `active` is true, and 0 otherwise.
895+
///
896+
/// # Arguments
897+
///
898+
/// * `irq` - IRQ to be set.
899+
/// * `active` - Level of the IRQ input.
900+
///
901+
/// # Errors
902+
///
903+
/// Returns an io::Error when the irq field is invalid
904+
///
905+
/// # Examples
906+
///
907+
/// ```rust
908+
/// # extern crate kvm_ioctls;
909+
/// # extern crate libc;
910+
/// # extern crate vmm_sys_util;
911+
/// # use kvm_ioctls::{Kvm, VmFd};
912+
/// # use libc::EFD_NONBLOCK;
913+
/// # use vmm_sys_util::eventfd::EventFd;
914+
/// fn arch_setup(vm_fd: &VmFd) {
915+
/// // Arch-specific setup:
916+
/// // For x86 architectures, it simply means calling vm.create_irq_chip().unwrap().
917+
/// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
918+
/// # vm_fd.create_irq_chip().unwrap();
919+
/// // For Arm architectures, the IRQ controllers need to be setup first.
920+
/// // Details please refer to the kernel documentation.
921+
/// // https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt
922+
/// # #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] {
923+
/// # vm_fd.create_vcpu(0).unwrap();
924+
/// # // ... rest of setup for Arm goes here
925+
/// # }
926+
/// }
927+
///
928+
/// let kvm = Kvm::new().unwrap();
929+
/// let vm = kvm.create_vm().unwrap();
930+
/// arch_setup(&vm);
931+
/// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
932+
/// vm.set_irq_line(4, true);
933+
/// // ...
934+
/// }
935+
/// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] {
936+
/// vm.set_irq_line(0x01_00_0020, true);
937+
/// // ....
938+
/// }
939+
/// ```
940+
///
941+
#[cfg(any(
942+
target_arch = "x86",
943+
target_arch = "x86_64",
944+
target_arch = "arm",
945+
target_arch = "aarch64"
946+
))]
947+
pub fn set_irq_line(&self, irq: u32, active: bool) -> Result<()> {
948+
let mut irq_level = kvm_irq_level::default();
949+
irq_level.__bindgen_anon_1.irq = irq;
950+
irq_level.level = if active { 1 } else { 0 };
951+
952+
// Safe because we know that our file is a VM fd, we know the kernel will only read the
953+
// correct amount of memory from our pointer, and we verify the return result.
954+
let ret = unsafe { ioctl_with_ref(self, KVM_IRQ_LINE(), &irq_level) };
955+
if ret == 0 {
956+
Ok(())
957+
} else {
958+
Err(errno::Error::last())
959+
}
960+
}
961+
894962
/// Creates a new KVM vCPU file descriptor and maps the memory corresponding
895963
/// its `kvm_run` structure.
896964
///
@@ -1119,7 +1187,7 @@ impl AsRawFd for VmFd {
11191187
}
11201188
}
11211189

1122-
/// Creates a dummy GIC device.
1190+
/// Create a dummy GIC device.
11231191
///
11241192
/// # Arguments
11251193
///
@@ -1145,6 +1213,43 @@ pub(crate) fn create_gic_device(vm: &VmFd, flags: u32) -> DeviceFd {
11451213
device_fd
11461214
}
11471215

1216+
/// Set supported number of IRQs for vGIC.
1217+
///
1218+
/// # Arguments
1219+
///
1220+
/// * `vgic` - The vGIC file descriptor.
1221+
/// * `nr_irqs` - Number of IRQs.
1222+
///
1223+
#[cfg(test)]
1224+
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
1225+
pub(crate) fn set_supported_nr_irqs(vgic: &DeviceFd, nr_irqs: u32) {
1226+
let vgic_attr = kvm_bindings::kvm_device_attr {
1227+
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
1228+
attr: 0,
1229+
addr: &nr_irqs as *const u32 as u64,
1230+
flags: 0,
1231+
};
1232+
assert!(vgic.set_device_attr(&vgic_attr).is_ok());
1233+
}
1234+
1235+
/// Request the initialization of the vGIC.
1236+
///
1237+
/// # Arguments
1238+
///
1239+
/// * `vgic` - The vGIC file descriptor.
1240+
///
1241+
#[cfg(test)]
1242+
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
1243+
pub(crate) fn request_gic_init(vgic: &DeviceFd) {
1244+
let vgic_attr = kvm_bindings::kvm_device_attr {
1245+
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
1246+
attr: u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
1247+
addr: 0,
1248+
flags: 0,
1249+
};
1250+
assert!(vgic.set_device_attr(&vgic_attr).is_ok());
1251+
}
1252+
11481253
#[cfg(test)]
11491254
mod tests {
11501255
use super::*;
@@ -1356,26 +1461,10 @@ mod tests {
13561461
// Create the vGIC device.
13571462
let vgic_fd = create_gic_device(&vm_fd, 0);
13581463

1359-
// Dummy interrupt for testing on aarch64.
1360-
let nr_irqs: u32 = 128;
1361-
1362-
// We need to tell the kernel how many irqs to support with this vgic.
1363-
let vgic_attr = kvm_bindings::kvm_device_attr {
1364-
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
1365-
attr: 0,
1366-
addr: &nr_irqs as *const u32 as u64,
1367-
flags: 0,
1368-
};
1369-
assert!(vgic_fd.set_device_attr(&vgic_attr).is_ok());
1370-
1371-
// Finalize the GIC.
1372-
let vgic_attr = kvm_bindings::kvm_device_attr {
1373-
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
1374-
attr: u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
1375-
addr: 0,
1376-
flags: 0,
1377-
};
1378-
assert!(vgic_fd.set_device_attr(&vgic_attr).is_ok());
1464+
// Set supported number of IRQs.
1465+
set_supported_nr_irqs(&vgic_fd, 128);
1466+
// Request the initialization of the vGIC.
1467+
request_gic_init(&vgic_fd);
13791468

13801469
assert!(vm_fd.register_irqfd(&evtfd1, 4).is_ok());
13811470
assert!(vm_fd.register_irqfd(&evtfd2, 8).is_ok());
@@ -1393,6 +1482,52 @@ mod tests {
13931482
assert!(vm_fd.unregister_irqfd(&evtfd3, 5).is_ok());
13941483
}
13951484

1485+
#[test]
1486+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
1487+
fn test_set_irq_line() {
1488+
let kvm = Kvm::new().unwrap();
1489+
let vm_fd = kvm.create_vm().unwrap();
1490+
1491+
assert!(vm_fd.create_irq_chip().is_ok());
1492+
1493+
assert!(vm_fd.set_irq_line(4, true).is_ok());
1494+
assert!(vm_fd.set_irq_line(4, false).is_ok());
1495+
assert!(vm_fd.set_irq_line(4, true).is_ok());
1496+
}
1497+
1498+
#[test]
1499+
#[cfg(target_arch = "aarch64")]
1500+
fn test_set_irq_line() {
1501+
let kvm = Kvm::new().unwrap();
1502+
let vm_fd = kvm.create_vm().unwrap();
1503+
// Create a vcpu for test case 2 of the KVM_IRQ_LINE API on aarch64.
1504+
vm_fd.create_vcpu(0).unwrap();
1505+
1506+
// Create the vGIC device.
1507+
let vgic_fd = create_gic_device(&vm_fd, 0);
1508+
// Set supported number of IRQs.
1509+
set_supported_nr_irqs(&vgic_fd, 128);
1510+
// Request the initialization of the vGIC.
1511+
request_gic_init(&vgic_fd);
1512+
1513+
// On arm/aarch64, irq field is interpreted like this:
1514+
// bits: | 31 ... 24 | 23 ... 16 | 15 ... 0 |
1515+
// field: | irq_type | vcpu_index | irq_id |
1516+
// The irq_type field has the following values:
1517+
// - irq_type[0]: out-of-kernel GIC: irq_id 0 is IRQ, irq_id 1 is FIQ
1518+
// - irq_type[1]: in-kernel GIC: SPI, irq_id between 32 and 1019 (incl.) (the vcpu_index field is ignored)
1519+
// - irq_type[2]: in-kernel GIC: PPI, irq_id between 16 and 31 (incl.)
1520+
// Hence, using irq_type = 1, irq_id = 32 (decimal), the irq field in hex is: 0x01_00_0020
1521+
assert!(vm_fd.set_irq_line(0x01_00_0020, true).is_ok());
1522+
assert!(vm_fd.set_irq_line(0x01_00_0020, false).is_ok());
1523+
assert!(vm_fd.set_irq_line(0x01_00_0020, true).is_ok());
1524+
1525+
// Case 2: using irq_type = 2, vcpu_index = 0, irq_id = 16 (decimal), the irq field in hex is: 0x02_00_0010
1526+
assert!(vm_fd.set_irq_line(0x02_00_0010, true).is_ok());
1527+
assert!(vm_fd.set_irq_line(0x02_00_0010, false).is_ok());
1528+
assert!(vm_fd.set_irq_line(0x02_00_0010, true).is_ok());
1529+
}
1530+
13961531
#[test]
13971532
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
13981533
fn test_faulty_vm_fd() {

src/kvm_ioctls.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ ioctl_io_nr!(KVM_SET_TSS_ADDR, KVMIO, 0x47);
4646
target_arch = "s390"
4747
))]
4848
ioctl_io_nr!(KVM_CREATE_IRQCHIP, KVMIO, 0x60);
49+
/* Available with KVM_CAP_IRQCHIP */
50+
#[cfg(any(
51+
target_arch = "x86",
52+
target_arch = "x86_64",
53+
target_arch = "arm",
54+
target_arch = "aarch64"
55+
))]
56+
ioctl_iow_nr!(KVM_IRQ_LINE, KVMIO, 0x61, kvm_irq_level);
4957
/* Available with KVM_CAP_IRQ_ROUTING */
5058
#[cfg(any(
5159
target_arch = "x86",

0 commit comments

Comments
 (0)