Skip to content

Commit

Permalink
Add support for KVM_ENABLE_CAP ioctl
Browse files Browse the repository at this point in the history
By adding the support for the ioctl KVM_ENABLE_CAP, this commit will
allow consumers of the kvm-ioctls crate to enable any KVM capability
they would be missing from their VMM implementation.

One example of capability that can be interesting to enable is the
newly added KVM_CAP_SPLIT_IRQCHIP, that allows the consumer to prevent
KVM from creating a virtual IOAPIC and PIC. This capability can let
a VMM provide its own userspace implementation of those components for
security purposes.

Unfortunately, we couldn't test enabling a capability for arm/aarch64
since there is no capability available for these architectures.

Signed-off-by: Sebastien Boeuf <[email protected]>
  • Loading branch information
Sebastien Boeuf committed Jun 12, 2019
1 parent 8589f89 commit c620b6a
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/cap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,5 +132,7 @@ pub enum Cap {
PpcEnableHcall = KVM_CAP_PPC_ENABLE_HCALL,
CheckExtensionVm = KVM_CAP_CHECK_EXTENSION_VM,
S390UserSigp = KVM_CAP_S390_USER_SIGP,
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
SplitIrqchip = KVM_CAP_SPLIT_IRQCHIP,
ImmediateExit = KVM_CAP_IMMEDIATE_EXIT,
}
89 changes: 89 additions & 0 deletions src/ioctls/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,62 @@ impl VmFd {
Ok(())
}

/// Enable the specified capability as per the `KVM_ENABLE_CAP` ioctl.
///
/// See the documentation for `KVM_ENABLE_CAP`.
///
/// Returns an io::Error when the capability could not be enabled.
///
/// # Arguments
///
/// * kvm_enable_cap - KVM capability structure. For details check the `kvm_enable_cap`
/// structure in the
/// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt).
///
/// # Example
///
/// ```rust
/// # extern crate kvm_ioctls;
/// extern crate kvm_bindings;
///
/// # use kvm_ioctls::{Cap, Kvm, VmFd};
/// use kvm_bindings::{kvm_enable_cap, KVM_CAP_SPLIT_IRQCHIP};
///
/// let kvm = Kvm::new().unwrap();
/// let vm = kvm.create_vm().unwrap();
/// let mut cap: kvm_enable_cap = Default::default();
/// // This example cannot enable an arm/aarch64 capability since there
/// // is no capability available for these architectures.
/// if cfg!(target_arch = "x86") || cfg!(target_arch = "x86_64") {
/// cap.cap = KVM_CAP_SPLIT_IRQCHIP;
/// // As per the KVM documentation, KVM_CAP_SPLIT_IRQCHIP only emulates
/// // the local APIC in kernel, expecting that a userspace IOAPIC will
/// // be implemented by the VMM.
/// // Along with this capability, the user needs to specify the number
/// // of pins reserved for the userspace IOAPIC. This number needs to be
/// // provided through the first argument of the capability structure, as
/// // specified in KVM documentation:
/// // args[0] - number of routes reserved for userspace IOAPICs
/// //
/// // Because an IOAPIC supports 24 pins, that's the reason why this test
/// // picked this number as reference.
/// cap.args[0] = 24;
/// vm.enable_cap(&cap).unwrap();
/// }
/// ```
///
#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
pub fn enable_cap(&self, cap: &kvm_enable_cap) -> Result<()> {
// The ioctl is safe because we allocated the struct and we know the
// kernel will write exactly the size of the struct.
let ret = unsafe { ioctl_with_ref(self, KVM_ENABLE_CAP(), cap) };
if ret == 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
}
}

/// Get the `kvm_run` size.
pub fn run_size(&self) -> usize {
self.run_size
Expand Down Expand Up @@ -812,4 +868,37 @@ mod tests {
let msi = kvm_msi::default();
assert!(vm.signal_msi(msi).is_err());
}

#[test]
#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
fn test_enable_cap_failure() {
let kvm = Kvm::new().unwrap();
let vm = kvm.create_vm().unwrap();
let cap: kvm_enable_cap = Default::default();
// Providing the `kvm_enable_cap` structure filled with default() should
// always result in a failure as it is not a valid capability.
assert!(vm.enable_cap(&cap).is_err());
}

#[test]
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn test_enable_split_irqchip_cap() {
let kvm = Kvm::new().unwrap();
let vm = kvm.create_vm().unwrap();
let mut cap: kvm_enable_cap = Default::default();
cap.cap = KVM_CAP_SPLIT_IRQCHIP;
// As per the KVM documentation, KVM_CAP_SPLIT_IRQCHIP only emulates
// the local APIC in kernel, expecting that a userspace IOAPIC will
// be implemented by the VMM.
// Along with this capability, the user needs to specify the number
// of pins reserved for the userspace IOAPIC. This number needs to be
// provided through the first argument of the capability structure, as
// specified in KVM documentation:
// args[0] - number of routes reserved for userspace IOAPICs
//
// Because an IOAPIC supports 24 pins, that's the reason why this test
// picked this number as reference.
cap.args[0] = 24;
assert!(vm.enable_cap(&cap).is_ok());
}
}
2 changes: 2 additions & 0 deletions src/kvm_ioctls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ ioctl_iow_nr!(KVM_SET_FPU, KVMIO, 0x8d, kvm_fpu);
ioctl_ior_nr!(KVM_GET_LAPIC, KVMIO, 0x8e, kvm_lapic_state);
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
ioctl_iow_nr!(KVM_SET_LAPIC, KVMIO, 0x8f, kvm_lapic_state);
#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
ioctl_iow_nr!(KVM_ENABLE_CAP, KVMIO, 0xa3, kvm_enable_cap);
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
Expand Down
2 changes: 1 addition & 1 deletion tests/coverage
Original file line number Diff line number Diff line change
@@ -1 +1 @@
91.2
91.3

0 comments on commit c620b6a

Please sign in to comment.