Skip to content

Commit

Permalink
ioctls: Implement hvcall_ versions of various IOCTLs
Browse files Browse the repository at this point in the history
While leaving existing implementations untouched.

Add:
- VmFd::hvcall_set_partition_property
- VcpuFd::hvcall_translate_gva
- VcpuFd::hvcall_get_cpuid_values
- VcpuFd::hvcall_get_reg
- VcpuFd::hvcall_set_reg

Add tests for the above.

Signed-off-by: Nuno Das Neves <[email protected]>
  • Loading branch information
NunoDasNeves committed Apr 23, 2024
1 parent d8b4a95 commit c03ec9c
Show file tree
Hide file tree
Showing 2 changed files with 210 additions and 36 deletions.
175 changes: 153 additions & 22 deletions mshv-ioctls/src/ioctls/vcpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,44 @@ impl VcpuFd {
}
Ok(())
}
/// Sets a vCPU register to input value.
///
/// # Arguments
///
/// * `reg_name` - general purpose register name.
/// * `reg_value` - register value.
/// Generic hvcall version of get_reg
pub fn hvcall_get_reg(&self, reg_assocs: &mut [hv_register_assoc]) -> Result<()> {
if reg_assocs.is_empty() {
return Err(libc::EINVAL.into());
}
let reg_names: Vec<hv_register_name> = reg_assocs.iter().map(|assoc| assoc.name).collect();
let input = make_rep_input!(
hv_input_get_vp_registers {
vp_index: self.index,
..Default::default()
},
names,
reg_names.as_slice()
);
let mut output: Vec<hv_register_value> = reg_names
.iter()
.map(|_| hv_register_value {
reg128: hv_u128 {
..Default::default()
},
})
.collect();
let output_slice = output.as_mut_slice();

let mut args = make_rep_args!(HVCALL_GET_VP_REGISTERS, input, output_slice);
self.hvcall(&mut args)?;

if args.reps as usize != reg_assocs.len() {
return Err(libc::EINTR.into());
}

for (assoc, value) in reg_assocs.iter_mut().zip(output.iter()) {
assoc.value = *value;
}

Ok(())
}
/// Set vcpu register values by providing an array of register assocs
#[cfg(not(target_arch = "aarch64"))]
pub fn set_reg(&self, regs: &[hv_register_assoc]) -> Result<()> {
let hv_vp_register_args = mshv_vp_registers {
Expand All @@ -98,6 +130,26 @@ impl VcpuFd {
if ret != 0 {
return Err(errno::Error::last().into());
}

Ok(())
}
/// Generic hypercall version of set_reg
pub fn hvcall_set_reg(&self, reg_assocs: &[hv_register_assoc]) -> Result<()> {
let input = make_rep_input!(
hv_input_set_vp_registers {
vp_index: self.index,
..Default::default()
},
elements,
reg_assocs
);
let mut args = make_rep_args!(HVCALL_SET_VP_REGISTERS, input);
self.hvcall(&mut args)?;

if args.reps as usize != reg_assocs.len() {
return Err(libc::EINTR.into());
}

Ok(())
}
/// Sets the vCPU general purpose registers
Expand Down Expand Up @@ -909,6 +961,29 @@ impl VcpuFd {

Ok((gpa, result))
}
/// Generic hvcall version of translate guest virtual address
pub fn hvcall_translate_gva(
&self,
gva: u64,
flags: u64,
) -> Result<(u64, hv_translate_gva_result)> {
let input = hv_input_translate_virtual_address {
vp_index: self.index,
control_flags: flags,
gva_page: gva >> HV_HYP_PAGE_SHIFT,
..Default::default() // NOTE: kernel will populate partition_id field
};
let output = hv_output_translate_virtual_address {
..Default::default()
};
let mut args = make_args!(HVCALL_TRANSLATE_VIRTUAL_ADDRESS, input, output);
self.hvcall(&mut args)?;

let gpa = (output.gpa_page << HV_HYP_PAGE_SHIFT) | (gva & !(1 << HV_HYP_PAGE_SHIFT));

Ok((gpa, output.translation_result))
}

/// X86 specific call that returns the vcpu's current "suspend registers".
#[cfg(not(target_arch = "aarch64"))]
pub fn get_suspend_regs(&self) -> Result<SuspendRegisters> {
Expand Down Expand Up @@ -1045,6 +1120,48 @@ impl VcpuFd {
}
Ok([parms.eax, parms.ebx, parms.ecx, parms.edx])
}
/// Generic hvcall version of get cpuid values
#[cfg(not(target_arch = "aarch64"))]
pub fn hvcall_get_cpuid_values(
&self,
eax: u32,
ecx: u32,
xfem: u64,
xss: u64,
) -> Result<[u32; 4]> {
let mut input = make_rep_input!(
hv_input_get_vp_cpuid_values {
vp_index: self.index,
..Default::default() // NOTE: Kernel will populate partition_id field
},
cpuid_leaf_info,
[hv_cpuid_leaf_info {
eax,
ecx,
xfem,
xss,
}]
);
unsafe {
input
.as_mut_struct_ref()
.flags
.__bindgen_anon_1
.set_use_vp_xfem_xss(1);
input
.as_mut_struct_ref()
.flags
.__bindgen_anon_1
.set_apply_registered_values(1);
}
let mut output_arr: [hv_output_get_vp_cpuid_values; 1] = [Default::default()];
let mut args = make_rep_args!(HVCALL_GET_VP_CPUID_VALUES, input, output_arr);
self.hvcall(&mut args)?;

// SAFETY: The hvcall succeeded, and both fields of the union are
// equivalent. Just return the array instead of taking eax, ebx, etc...
Ok(unsafe { output_arr[0].as_uint32 })
}
/// Read GPA
pub fn gpa_read(&self, input: &mut mshv_read_write_gpa) -> Result<mshv_read_write_gpa> {
// SAFETY: we know that our file is a vCPU fd, we know the kernel honours its ABI.
Expand Down Expand Up @@ -1138,11 +1255,7 @@ mod tests {

#[test]
fn test_set_get_regs() {
let hv = Mshv::new().unwrap();
let vm = hv.create_vm().unwrap();
let vcpu = vm.create_vcpu(0).unwrap();

vcpu.set_reg(&[
let set_reg_assocs: [hv_register_assoc; 2] = [
hv_register_assoc {
name: hv_register_name_HV_X64_REGISTER_RIP,
value: hv_register_value { reg64: 0x1000 },
Expand All @@ -1153,10 +1266,8 @@ mod tests {
value: hv_register_value { reg64: 0x2 },
..Default::default()
},
])
.unwrap();

let mut get_regs: [hv_register_assoc; 2] = [
];
let get_reg_assocs: [hv_register_assoc; 2] = [
hv_register_assoc {
name: hv_register_name_HV_X64_REGISTER_RIP,
..Default::default()
Expand All @@ -1167,12 +1278,30 @@ mod tests {
},
];

vcpu.get_reg(&mut get_regs).unwrap();
for i in [0, 1] {
let hv = Mshv::new().unwrap();
let vm = hv.create_vm().unwrap();
let vcpu = vm.create_vcpu(0).unwrap();

// SAFETY: access union fields
unsafe {
assert!(get_regs[0].value.reg64 == 0x1000);
assert!(get_regs[1].value.reg64 == 0x2);
if i == 0 {
vcpu.set_reg(&set_reg_assocs).unwrap();
} else {
vcpu.hvcall_set_reg(&set_reg_assocs).unwrap();
}

let mut get_regs: [hv_register_assoc; 2] = get_reg_assocs;

if i == 0 {
vcpu.get_reg(&mut get_regs).unwrap();
} else {
vcpu.hvcall_get_reg(&mut get_regs).unwrap();
}

// SAFETY: access union fields
unsafe {
assert!(get_regs[0].value.reg64 == 0x1000);
assert!(get_regs[1].value.reg64 == 0x2);
}
}
}

Expand Down Expand Up @@ -1542,9 +1671,11 @@ mod tests {
let hv = Mshv::new().unwrap();
let vm = hv.create_vm().unwrap();
let vcpu = vm.create_vcpu(0).unwrap();
let res = vcpu.get_cpuid_values(0, 0, 0, 0).unwrap();
let max_function = res[0];
let res_0 = vcpu.get_cpuid_values(0, 0, 0, 0).unwrap();
let max_function = res_0[0];
assert!(max_function >= 1);
let res_1 = vcpu.hvcall_get_cpuid_values(0, 0, 0, 0).unwrap();
assert!(res_1[0] >= 1);
}

#[test]
Expand Down
71 changes: 57 additions & 14 deletions mshv-ioctls/src/ioctls/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,16 @@ impl VmFd {
Err(errno::Error::last().into())
}
}
/// Generic hvcall version of set_partition_property
pub fn hvcall_set_partition_property(&self, code: u32, value: u64) -> Result<()> {
let input = hv_input_set_partition_property {
property_code: code,
property_value: value,
..Default::default() // NOTE: kernel will populate partition_id field
};
let mut args = make_args!(HVCALL_SET_PARTITION_PROPERTY, input);
self.hvcall(&mut args)
}
/// Enable dirty page tracking by hypervisor
/// Flags:
/// bit 1: Enabled
Expand Down Expand Up @@ -798,20 +808,7 @@ mod tests {
assert!(vm.install_intercept(intercept_args).is_ok());
}
#[test]
fn test_setting_immutable_partition_property() {
let hv = Mshv::new().unwrap();
let vm = hv.create_vm().unwrap();
let res = vm.set_partition_property(
hv_partition_property_code_HV_PARTITION_PROPERTY_PRIVILEGE_FLAGS,
0,
);

// We should get an error, because we are trying to change an immutable
// partition property.
assert!(res.is_err())
}
#[test]
fn test_get_set_property() {
fn test_get_property() {
let hv = Mshv::new().unwrap();
let vm = hv.create_vm().unwrap();

Expand Down Expand Up @@ -849,6 +846,52 @@ mod tests {
);
}
#[test]
fn test_set_property() {
let hv = Mshv::new().unwrap();
let vm = hv.create_vm().unwrap();

let code = hv_partition_property_code_HV_PARTITION_PROPERTY_UNIMPLEMENTED_MSR_ACTION;
let ignore =
hv_unimplemented_msr_action_HV_UNIMPLEMENTED_MSR_ACTION_IGNORE_WRITE_READ_ZERO as u64;
let fault = hv_unimplemented_msr_action_HV_UNIMPLEMENTED_MSR_ACTION_FAULT as u64;

vm.set_partition_property(code, ignore).unwrap();
let ignore_ret = vm.get_partition_property(code).unwrap();
assert!(ignore_ret == ignore);

vm.set_partition_property(code, fault).unwrap();
let fault_ret = vm.get_partition_property(code).unwrap();
assert!(fault_ret == fault);

// Test the same with hvcall_ equivalent
vm.hvcall_set_partition_property(code, ignore).unwrap();
let ignore_ret = vm.get_partition_property(code).unwrap();
assert!(ignore_ret == ignore);

vm.hvcall_set_partition_property(code, fault).unwrap();
let fault_ret = vm.get_partition_property(code).unwrap();
assert!(fault_ret == fault);
}
#[test]
fn test_set_partition_property_invalid() {
let hv = Mshv::new().unwrap();
let vm = hv.create_vm().unwrap();
let code = hv_partition_property_code_HV_PARTITION_PROPERTY_PRIVILEGE_FLAGS;

// old IOCTL
let res_0 = vm.set_partition_property(code, 0);
assert!(res_0.is_err());

// generic hvcall
let res_1 = vm.hvcall_set_partition_property(code, 0);
let mshv_err_check = MshvError::Hypercall {
code: HVCALL_SET_PARTITION_PROPERTY as u16,
status_raw: HV_STATUS_INVALID_PARTITION_STATE as u16,
status: Some(HvError::InvalidPartitionState),
};
assert!(res_1.err().unwrap() == mshv_err_check);
}
#[test]
fn test_irqfd() {
use libc::EFD_NONBLOCK;
let hv = Mshv::new().unwrap();
Expand Down

0 comments on commit c03ec9c

Please sign in to comment.