Skip to content

Commit

Permalink
Add support for KVM_GET_REG_LIST ioctl
Browse files Browse the repository at this point in the history
The `KVM_GET_REG_LIST` ioctl is used to extract
a list containing the id of all registers on aarch64 architecture.
We need this in order to be able to save/restore them.

Fixes: rust-vmm#104

Signed-off-by: Diana Popa <dpopa@amazon.com>
  • Loading branch information
dianpopa authored and alxiord committed Jul 22, 2020
1 parent d8f78a1 commit bc85674
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 0 deletions.
69 changes: 69 additions & 0 deletions src/ioctls/vcpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -959,6 +959,46 @@ impl VcpuFd {
Ok(())
}

/// Returns the guest registers that are supported for the
/// KVM_GET_ONE_REG/KVM_SET_ONE_REG calls.
///
/// # Arguments
///
/// * `reg_list` - list of registers (input/output). For details check the `kvm_reg_list`
/// 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::Kvm;
/// # use kvm_bindings::RegList;
/// let kvm = Kvm::new().unwrap();
/// let vm = kvm.create_vm().unwrap();
/// let vcpu = vm.create_vcpu(0).unwrap();
///
/// // KVM_GET_REG_LIST demands that the vcpus be initalized.
/// let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default();
/// vm.get_preferred_target(&mut kvi).unwrap();
/// vcpu.vcpu_init(&kvi).expect("Cannot initialize vcpu");
///
/// let mut reg_list = RegList::new(500);
/// vcpu.get_reg_list(&mut reg_list).unwrap();
/// assert!(reg_list.as_fam_struct_ref().n > 0);
/// ```
///
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
pub fn get_reg_list(&self, reg_list: &mut RegList) -> Result<()> {
let ret =
unsafe { ioctl_with_mut_ref(self, KVM_GET_REG_LIST(), reg_list.as_mut_fam_struct()) };
if ret < 0 {
return Err(errno::Error::last());
}
Ok(())
}

/// Sets the value of one register for this vCPU.
///
/// The id of the register is encoded as specified in the kernel documentation
Expand Down Expand Up @@ -1838,6 +1878,35 @@ mod tests {
);
}

#[test]
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
fn test_get_reg_list() {
let kvm = Kvm::new().unwrap();
let vm = kvm.create_vm().unwrap();
let vcpu = vm.create_vcpu(0).unwrap();

let mut reg_list = RegList::new(1);
// KVM_GET_REG_LIST demands that the vcpus be initalized, so we expect this to fail.
let err = vcpu.get_reg_list(&mut reg_list).unwrap_err();
assert!(err.errno() == libc::ENOEXEC);

let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default();
vm.get_preferred_target(&mut kvi)
.expect("Cannot get preferred target");
vcpu.vcpu_init(&kvi).expect("Cannot initialize vcpu");

// KVM_GET_REG_LIST offers us a number of registers for which we have
// not allocated memory, so the first time it fails.
let err = vcpu.get_reg_list(&mut reg_list).unwrap_err();
assert!(err.errno() == libc::E2BIG);
assert!(reg_list.as_mut_fam_struct().n > 0);

// We make use of the number of registers returned to allocate memory and
// try one more time.
let mut reg_list = RegList::new(reg_list.as_mut_fam_struct().n as usize);
assert!(vcpu.get_reg_list(&mut reg_list).is_ok());
}

#[test]
fn set_kvm_immediate_exit() {
let kvm = Kvm::new().unwrap();
Expand Down
2 changes: 2 additions & 0 deletions src/kvm_ioctls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,8 @@ ioctl_iow_nr!(KVM_SET_ONE_REG, KVMIO, 0xac, kvm_one_reg);
ioctl_iow_nr!(KVM_ARM_VCPU_INIT, KVMIO, 0xae, kvm_vcpu_init);
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
ioctl_ior_nr!(KVM_ARM_PREFERRED_TARGET, KVMIO, 0xaf, kvm_vcpu_init);
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
ioctl_iowr_nr!(KVM_GET_REG_LIST, KVMIO, 0xb0, kvm_reg_list);

// Device ioctls.

Expand Down

0 comments on commit bc85674

Please sign in to comment.