Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions kvm-ioctls/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## Upcoming Release

### Added

- Support `KVM_GET_DEVICE_ATTR`, `KVM_SET_DEVICE_ATTR` and `KVM_HAS_DEVICE_ATTR`
vcpu ioctls on x86_64 (previously only available on aarch64).

## v0.25.0

### Added
Expand Down
166 changes: 141 additions & 25 deletions kvm-ioctls/src/ioctls/vcpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,25 +254,41 @@ impl VcpuFd {
///
/// ```rust
/// # use kvm_ioctls::Kvm;
/// # use kvm_bindings::{
/// KVM_ARM_VCPU_PMU_V3_CTRL, KVM_ARM_VCPU_PMU_V3_INIT
/// };
/// let kvm = Kvm::new().unwrap();
/// let vm = kvm.create_vm().unwrap();
/// let vcpu = vm.create_vcpu(0).unwrap();
///
/// let dist_attr = kvm_bindings::kvm_device_attr {
/// group: KVM_ARM_VCPU_PMU_V3_CTRL,
/// attr: u64::from(KVM_ARM_VCPU_PMU_V3_INIT),
/// addr: 0x0,
/// flags: 0,
/// };
///
/// if (vcpu.has_device_attr(&dist_attr).is_ok()) {
/// vcpu.set_device_attr(&dist_attr).unwrap();
/// #[cfg(target_arch = "aarch64")]
/// {
/// use kvm_bindings::{KVM_ARM_VCPU_PMU_V3_CTRL, KVM_ARM_VCPU_PMU_V3_INIT};
/// let dist_attr = kvm_bindings::kvm_device_attr {
/// group: KVM_ARM_VCPU_PMU_V3_CTRL,
/// attr: u64::from(KVM_ARM_VCPU_PMU_V3_INIT),
/// addr: 0x0,
/// flags: 0,
/// };
///
/// if vcpu.has_device_attr(&dist_attr).is_ok() {
/// vcpu.set_device_attr(&dist_attr).unwrap();
/// }
/// }
/// #[cfg(target_arch = "x86_64")]
/// {
/// use kvm_bindings::{KVM_VCPU_TSC_CTRL, KVM_VCPU_TSC_OFFSET};
/// let tsc_offset: u64 = 0;
/// let tsc_attr = kvm_bindings::kvm_device_attr {
/// group: KVM_VCPU_TSC_CTRL,
/// attr: u64::from(KVM_VCPU_TSC_OFFSET),
/// addr: &tsc_offset as *const u64 as u64,
/// flags: 0,
/// };
///
/// if vcpu.has_device_attr(&tsc_attr).is_ok() {
/// vcpu.set_device_attr(&tsc_attr).unwrap();
/// }
/// }
/// ```
#[cfg(target_arch = "aarch64")]
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
pub fn set_device_attr(&self, device_attr: &kvm_device_attr) -> Result<()> {
// SAFETY: Safe because we call this with a Vcpu fd and we trust the kernel.
let ret = unsafe { ioctl_with_ref(self, KVM_SET_DEVICE_ATTR(), device_attr) };
Expand All @@ -294,23 +310,36 @@ impl VcpuFd {
///
/// ```rust
/// # use kvm_ioctls::Kvm;
/// # use kvm_bindings::{
/// KVM_ARM_VCPU_PMU_V3_CTRL, KVM_ARM_VCPU_PMU_V3_INIT
/// };
/// let kvm = Kvm::new().unwrap();
/// let vm = kvm.create_vm().unwrap();
/// let vcpu = vm.create_vcpu(0).unwrap();
///
/// let dist_attr = kvm_bindings::kvm_device_attr {
/// group: KVM_ARM_VCPU_PMU_V3_CTRL,
/// attr: u64::from(KVM_ARM_VCPU_PMU_V3_INIT),
/// addr: 0x0,
/// flags: 0,
/// };
///
/// vcpu.has_device_attr(&dist_attr);
/// #[cfg(target_arch = "aarch64")]
/// {
/// use kvm_bindings::{KVM_ARM_VCPU_PMU_V3_CTRL, KVM_ARM_VCPU_PMU_V3_INIT};
/// let dist_attr = kvm_bindings::kvm_device_attr {
/// group: KVM_ARM_VCPU_PMU_V3_CTRL,
/// attr: u64::from(KVM_ARM_VCPU_PMU_V3_INIT),
/// addr: 0x0,
/// flags: 0,
/// };
///
/// vcpu.has_device_attr(&dist_attr).ok();
/// }
/// #[cfg(target_arch = "x86_64")]
/// {
/// use kvm_bindings::{KVM_VCPU_TSC_CTRL, KVM_VCPU_TSC_OFFSET};
/// let tsc_attr = kvm_bindings::kvm_device_attr {
/// group: KVM_VCPU_TSC_CTRL,
/// attr: u64::from(KVM_VCPU_TSC_OFFSET),
/// addr: 0x0,
/// flags: 0,
/// };
///
/// vcpu.has_device_attr(&tsc_attr).ok();
/// }
/// ```
#[cfg(target_arch = "aarch64")]
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
pub fn has_device_attr(&self, device_attr: &kvm_device_attr) -> Result<()> {
// SAFETY: Safe because we call this with a Vcpu fd and we trust the kernel.
let ret = unsafe { ioctl_with_ref(self, KVM_HAS_DEVICE_ATTR(), device_attr) };
Expand All @@ -320,6 +349,60 @@ impl VcpuFd {
Ok(())
}

/// Gets a specified piece of cpu configuration and/or state.
///
/// See the documentation for `KVM_GET_DEVICE_ATTR` in
/// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt)
/// # Arguments
///
/// * `device_attr` - The cpu attribute to be read.
/// Note: This argument serves as both input and output.
/// When calling this function, the user should explicitly provide
/// valid values for the `group` and the `attr` fields, and a valid
/// userspace address (i.e. the `addr` field) to store the returned
/// attribute data.
///
/// # Safety
///
/// The caller is responsible for the validity of the `device_attr` argument,
/// including that it is safe to write to the `addr` member.
///
/// # Example
///
/// ```rust
/// # use kvm_ioctls::Kvm;
/// let kvm = Kvm::new().unwrap();
/// let vm = kvm.create_vm().unwrap();
/// let vcpu = vm.create_vcpu(0).unwrap();
///
/// #[cfg(target_arch = "x86_64")]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we come up with a aarch64 example as well

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, this would be consistent with the rest of the patch.

/// {
/// use kvm_bindings::{KVM_VCPU_TSC_CTRL, KVM_VCPU_TSC_OFFSET};
/// let mut tsc_offset: u64 = 0;
/// let mut tsc_attr = kvm_bindings::kvm_device_attr {
/// group: KVM_VCPU_TSC_CTRL,
/// attr: u64::from(KVM_VCPU_TSC_OFFSET),
/// addr: &mut tsc_offset as *mut u64 as u64,
/// flags: 0,
/// };
///
/// if vcpu.has_device_attr(&tsc_attr).is_ok() {
/// // SAFETY: tsc_attr.addr is safe to write to.
/// unsafe { vcpu.get_device_attr(&mut tsc_attr) }.unwrap();
/// }
/// }
/// ```
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
pub unsafe fn get_device_attr(&self, device_attr: &mut kvm_device_attr) -> Result<()> {
// SAFETY: Caller has ensured device_attr.addr is safe to write to.
// We call this with a Vcpu fd and we trust the kernel.
let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_DEVICE_ATTR(), device_attr) };
if ret != 0 {
return Err(errno::Error::last());
}
Ok(())
}

/// Sets the vCPU general purpose registers using the `KVM_SET_REGS` ioctl.
///
/// # Arguments
Expand Down Expand Up @@ -3219,6 +3302,39 @@ mod tests {
vcpu.set_device_attr(&dist_attr).unwrap();
}

#[test]
#[cfg(target_arch = "x86_64")]
fn test_vcpu_attr() {
let kvm = Kvm::new().unwrap();
let vm = kvm.create_vm().unwrap();
let vcpu = vm.create_vcpu(0).unwrap();

let offset_in: u64 = 1 << 30;
let set_attr = kvm_device_attr {
group: KVM_VCPU_TSC_CTRL,
attr: u64::from(KVM_VCPU_TSC_OFFSET),
addr: &offset_in as *const u64 as u64,
flags: 0,
};

// The TSC offset attribute requires KVM_CAP_VCPU_ATTRIBUTES, which is not
// guaranteed on every host, so skip the round-trip if it is unsupported.
if vcpu.has_device_attr(&set_attr).is_ok() {
vcpu.set_device_attr(&set_attr).unwrap();

let mut offset_out: u64 = 0;
let mut get_attr = kvm_device_attr {
group: KVM_VCPU_TSC_CTRL,
attr: u64::from(KVM_VCPU_TSC_OFFSET),
addr: &mut offset_out as *mut u64 as u64,
flags: 0,
};
// SAFETY: get_attr.addr points to a valid, writable u64.
unsafe { vcpu.get_device_attr(&mut get_attr) }.unwrap();
assert_eq!(offset_out, offset_in);
}
}

#[test]
#[cfg(target_arch = "aarch64")]
fn test_pointer_authentication() {
Expand Down