From a1d0ef427d3fbb024bc64f8a5ed868121be44305 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Fri, 17 Nov 2023 10:20:12 +0100 Subject: [PATCH 1/2] Introduce KvmRunWrapper::as_ref() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new method to get an immutable reference to the kvm_run struct. Replace uses of `as_mut_ref()` with `as_ref()` where possible. Signed-off-by: Carlos López --- src/ioctls/mod.rs | 8 ++++++++ src/ioctls/vcpu.rs | 18 +++++++++--------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs index 9079acc7..a0f6ef7c 100644 --- a/src/ioctls/mod.rs +++ b/src/ioctls/mod.rs @@ -86,6 +86,14 @@ impl KvmRunWrapper { } } +impl AsRef for KvmRunWrapper { + fn as_ref(&self) -> &kvm_run { + // SAFETY: Safe because we know we mapped enough memory to hold the kvm_run struct because + // the kernel told us how large it was. + unsafe { &*(self.kvm_run_ptr as *const kvm_run) } + } +} + impl Drop for KvmRunWrapper { fn drop(&mut self) { // SAFETY: This is safe because we mmap the area at kvm_run_ptr ourselves, diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 1154dfca..2e98fa12 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1764,7 +1764,7 @@ impl VcpuFd { /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn sync_regs(&self) -> kvm_sync_regs { - let kvm_run: &mut kvm_run = self.kvm_run_ptr.as_mut_ref(); + let kvm_run = self.kvm_run_ptr.as_ref(); // SAFETY: Accessing this union field could be out of bounds if the `kvm_run` // allocation isn't large enough. The `kvm_run` region is set using @@ -2689,9 +2689,9 @@ mod tests { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); let vcpu = vm.create_vcpu(0).unwrap(); - assert_eq!(vcpu.kvm_run_ptr.as_mut_ref().immediate_exit, 0); + assert_eq!(vcpu.kvm_run_ptr.as_ref().immediate_exit, 0); vcpu.set_kvm_immediate_exit(1); - assert_eq!(vcpu.kvm_run_ptr.as_mut_ref().immediate_exit, 1); + assert_eq!(vcpu.kvm_run_ptr.as_ref().immediate_exit, 1); } #[test] @@ -2763,9 +2763,9 @@ mod tests { ]; for reg in &sync_regs { vcpu.set_sync_valid_reg(*reg); - assert_eq!(vcpu.kvm_run_ptr.as_mut_ref().kvm_valid_regs, *reg as u64); + assert_eq!(vcpu.kvm_run_ptr.as_ref().kvm_valid_regs, *reg as u64); vcpu.clear_sync_valid_reg(*reg); - assert_eq!(vcpu.kvm_run_ptr.as_mut_ref().kvm_valid_regs, 0); + assert_eq!(vcpu.kvm_run_ptr.as_ref().kvm_valid_regs, 0); } // Test that multiple valid SyncRegs can be set at the same time @@ -2773,7 +2773,7 @@ mod tests { vcpu.set_sync_valid_reg(SyncReg::SystemRegister); vcpu.set_sync_valid_reg(SyncReg::VcpuEvents); assert_eq!( - vcpu.kvm_run_ptr.as_mut_ref().kvm_valid_regs, + vcpu.kvm_run_ptr.as_ref().kvm_valid_regs, SyncReg::Register as u64 | SyncReg::SystemRegister as u64 | SyncReg::VcpuEvents as u64 ); @@ -2786,9 +2786,9 @@ mod tests { for reg in &sync_regs { vcpu.set_sync_dirty_reg(*reg); - assert_eq!(vcpu.kvm_run_ptr.as_mut_ref().kvm_dirty_regs, *reg as u64); + assert_eq!(vcpu.kvm_run_ptr.as_ref().kvm_dirty_regs, *reg as u64); vcpu.clear_sync_dirty_reg(*reg); - assert_eq!(vcpu.kvm_run_ptr.as_mut_ref().kvm_dirty_regs, 0); + assert_eq!(vcpu.kvm_run_ptr.as_ref().kvm_dirty_regs, 0); } // Test that multiple dirty SyncRegs can be set at the same time @@ -2796,7 +2796,7 @@ mod tests { vcpu.set_sync_dirty_reg(SyncReg::SystemRegister); vcpu.set_sync_dirty_reg(SyncReg::VcpuEvents); assert_eq!( - vcpu.kvm_run_ptr.as_mut_ref().kvm_dirty_regs, + vcpu.kvm_run_ptr.as_ref().kvm_dirty_regs, SyncReg::Register as u64 | SyncReg::SystemRegister as u64 | SyncReg::VcpuEvents as u64 ); } From d28565ac0b898eb0b23d3b0923e23f268e70c440 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Thu, 16 Nov 2023 17:12:26 +0100 Subject: [PATCH 2/2] Add support for bus lock detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for bus lock detection. The feature can be enabled via KVM_CAP_X86_BUS_LOCK_EXIT, which enables a new exit (KVM_EXIT_X86_BUS_LOCK) and a new flag (KVM_RUN_X86_BUS_LOCK) in the kvm_run->flags field. Add two tests as well to verify that enabling the feature works. Signed-off-by: Carlos López --- CHANGELOG.md | 2 ++ src/cap.rs | 2 ++ src/ioctls/vcpu.rs | 51 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) 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(); + } }