diff --git a/CHANGELOG.md b/CHANGELOG.md index d558de4c..1b994701 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ reg_size as a public method. userspace MSR handling. - [[#246](https://github.com/rust-vmm/kvm-ioctls/pull/246)] Add support for userspace NMI injection (`KVM_NMI` ioctl). +- [[#245](https://github.com/rust-vmm/kvm-ioctls/pull/245)] x86: add support + for bus lock detection (`KVM_CAP_X86_BUS_LOCK_EXIT` capability). # v0.15.0 diff --git a/src/cap.rs b/src/cap.rs index b8bfd15b..e59757fb 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -166,4 +166,6 @@ pub enum Cap { ArmPtrAuthGeneric = KVM_CAP_ARM_PTRAUTH_GENERIC, #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] X86UserSpaceMsr = KVM_CAP_X86_USER_SPACE_MSR, + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + X86BusLockExit = KVM_CAP_X86_BUS_LOCK_EXIT, } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 2e98fa12..af1e2531 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -158,6 +158,8 @@ pub enum VcpuExit<'a> { X86Rdmsr(ReadMsrExit<'a>), /// Corresponds to KVM_EXIT_X86_WRMSR. X86Wrmsr(WriteMsrExit<'a>), + /// Corresponds to KVM_EXIT_X86_BUS_LOCK. + X86BusLock, /// Corresponds to an exit reason that is unknown from the current version /// of the kvm-ioctls crate. Let the consumer decide about what to do with /// it. @@ -1544,6 +1546,7 @@ impl VcpuFd { Ok(VcpuExit::IoapicEoi(eoi.vector)) } KVM_EXIT_HYPERV => Ok(VcpuExit::Hyperv), + KVM_EXIT_X86_BUS_LOCK => Ok(VcpuExit::X86BusLock), r => Ok(VcpuExit::Unsupported(r)), } } else { @@ -1849,6 +1852,19 @@ impl VcpuFd { _ => Err(errno::Error::last()), } } + + /// If [`Cap::X86BusLockExit`](crate::Cap::X86BusLockExit) was enabled, + /// checks whether a bus lock was detected on the last VM exit. This may + /// return `true` even if the corresponding exit was not + /// [`VcpuExit::X86BusLock`], as a different VM exit may have preempted + /// it. + /// + /// See the API documentation for `KVM_CAP_X86_BUS_LOCK_EXIT`. + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn bus_lock_detected(&self) -> bool { + let kvm_run = self.kvm_run_ptr.as_ref(); + kvm_run.flags as u32 & KVM_RUN_X86_BUS_LOCK != 0 + } } /// Helper function to create a new `VcpuFd`. @@ -3075,4 +3091,39 @@ mod tests { e => panic!("Unexpected exit: {:?}", e), } } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[test] + fn test_enable_bus_lock_detection() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + if !vm.check_extension(Cap::X86BusLockExit) { + return; + } + let args = KVM_BUS_LOCK_DETECTION_EXIT; + let cap = kvm_enable_cap { + cap: Cap::X86BusLockExit as u32, + args: [args as u64, 0, 0, 0], + ..Default::default() + }; + vm.enable_cap(&cap).unwrap(); + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[test] + fn test_enable_bus_lock_detection_invalid() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + if !vm.check_extension(Cap::X86BusLockExit) { + return; + } + // These flags should be mutually exclusive + let args = KVM_BUS_LOCK_DETECTION_OFF | KVM_BUS_LOCK_DETECTION_EXIT; + let cap = kvm_enable_cap { + cap: Cap::X86BusLockExit as u32, + args: [args as u64, 0, 0, 0], + ..Default::default() + }; + vm.enable_cap(&cap).unwrap_err(); + } }