Skip to content
Merged
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
12 changes: 12 additions & 0 deletions mshv-bindings/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,15 @@ pub use hvdef::*;

pub mod hvcall;
pub use hvcall::*;

#[derive(Debug)]
pub struct RegisterPage(pub *mut hv_vp_register_page);

// SAFETY: struct is based on register page in the hypervisor,
// safe to Send across threads
unsafe impl Send for RegisterPage {}

// SAFETY: struct is based on Register page in the hypervisor,
// safe to Sync across threads as this is only required for Vcpu trait
// functionally not used anyway
unsafe impl Sync for RegisterPage {}
28 changes: 28 additions & 0 deletions mshv-bindings/src/x86_64/regs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,34 @@ impl AllVpStateComponents {
}
}

#[macro_export]
macro_rules! set_gp_regs_field_ptr {
($this: ident, $name: ident, $value: expr) => {
#[allow(clippy::macro_metavars_in_unsafe)]
// SAFETY: access union fields
unsafe {
(*$this)
.__bindgen_anon_1
.__bindgen_anon_1
.__bindgen_anon_1
.__bindgen_anon_1
.$name = $value;
}
};
}

#[macro_export]
macro_rules! get_gp_regs_field_ptr {
($this: ident, $name: ident) => {
(*$this)
.__bindgen_anon_1
.__bindgen_anon_1
.__bindgen_anon_1
.__bindgen_anon_1
.$name
};
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
105 changes: 100 additions & 5 deletions mshv-ioctls/src/ioctls/vcpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,20 @@ macro_rules! set_registers_64 {
pub struct VcpuFd {
index: u32,
vcpu: File,
vp_page: Option<RegisterPage>,
}

/// Helper function to create a new `VcpuFd`.
///
/// This should not be exported as a public function because the preferred way is to use
/// `create_vcpu` from `VmFd`. The function cannot be part of the `VcpuFd` implementation because
/// then it would be exported with the public `VcpuFd` interface.
pub fn new_vcpu(index: u32, vcpu: File) -> VcpuFd {
VcpuFd { index, vcpu }
pub fn new_vcpu(index: u32, vcpu: File, vp_page: Option<RegisterPage>) -> VcpuFd {
VcpuFd {
index,
vcpu,
vp_page,
}
}

impl AsRawFd for VcpuFd {
Expand All @@ -64,6 +69,11 @@ impl AsRawFd for VcpuFd {
}

impl VcpuFd {
/// Get the reference of VP register page
pub fn get_vp_reg_page(&self) -> Option<&RegisterPage> {
self.vp_page.as_ref()
}

/// Get the register values by providing an array of register names
#[cfg(not(target_arch = "aarch64"))]
pub fn get_reg(&self, reg_names: &mut [hv_register_assoc]) -> Result<()> {
Expand Down Expand Up @@ -153,9 +163,10 @@ impl VcpuFd {

Ok(())
}
/// Sets the vCPU general purpose registers
#[cfg(target_arch = "x86_64")]
pub fn set_regs(&self, regs: &StandardRegisters) -> Result<()> {

/// Sets the vCPU general purpose registers using the IOCTL
#[cfg(not(target_arch = "aarch64"))]
fn set_standard_regs_ioctl(&self, regs: &StandardRegisters) -> Result<()> {
let reg_assocs = [
hv_register_assoc {
name: hv_register_name_HV_X64_REGISTER_RAX,
Expand Down Expand Up @@ -323,9 +334,61 @@ impl VcpuFd {
self.hvcall_set_reg(&reg_assocs)?;
Ok(())
}
/// Sets the vCPU general purpose registers using VP register page
#[cfg(not(target_arch = "aarch64"))]
fn set_standard_regs_vp_page(&self, regs: &StandardRegisters) -> Result<()> {
let vp_reg_page = self.get_vp_reg_page().unwrap().0;
set_gp_regs_field_ptr!(vp_reg_page, rax, regs.rax);
set_gp_regs_field_ptr!(vp_reg_page, rbx, regs.rbx);
set_gp_regs_field_ptr!(vp_reg_page, rcx, regs.rcx);
set_gp_regs_field_ptr!(vp_reg_page, rdx, regs.rdx);
set_gp_regs_field_ptr!(vp_reg_page, rsi, regs.rsi);
set_gp_regs_field_ptr!(vp_reg_page, rdi, regs.rdi);
set_gp_regs_field_ptr!(vp_reg_page, rsp, regs.rsp);
set_gp_regs_field_ptr!(vp_reg_page, rbp, regs.rbp);
set_gp_regs_field_ptr!(vp_reg_page, r8, regs.r8);
set_gp_regs_field_ptr!(vp_reg_page, r9, regs.r9);
set_gp_regs_field_ptr!(vp_reg_page, r10, regs.r10);
set_gp_regs_field_ptr!(vp_reg_page, r11, regs.r11);
set_gp_regs_field_ptr!(vp_reg_page, r12, regs.r12);
set_gp_regs_field_ptr!(vp_reg_page, r13, regs.r13);
set_gp_regs_field_ptr!(vp_reg_page, r14, regs.r14);
set_gp_regs_field_ptr!(vp_reg_page, r15, regs.r15);

// SAFETY: access union fields
unsafe {
(*vp_reg_page).dirty |= 1 << HV_X64_REGISTER_CLASS_GENERAL;
(*vp_reg_page).__bindgen_anon_1.__bindgen_anon_1.rip = regs.rip;
(*vp_reg_page).dirty |= 1 << HV_X64_REGISTER_CLASS_IP;
(*vp_reg_page).__bindgen_anon_1.__bindgen_anon_1.rflags = regs.rflags;
(*vp_reg_page).dirty |= 1 << HV_X64_REGISTER_CLASS_FLAGS;
}
Ok(())
}

/// Sets the vCPU general purpose registers
#[cfg(not(target_arch = "aarch64"))]
pub fn set_regs(&self, regs: &StandardRegisters) -> Result<()> {
if self.vp_page.is_some() {
self.set_standard_regs_vp_page(regs)
} else {
self.set_standard_regs_ioctl(regs)
}
}

/// Returns the vCPU general purpose registers.
#[cfg(target_arch = "x86_64")]
pub fn get_regs(&self) -> Result<StandardRegisters> {
if self.vp_page.is_some() {
self.get_standard_regs_vp_page()
} else {
self.get_standard_regs_ioctl()
}
}

/// Returns the vCPU general purpose registers using IOCTL
#[cfg(not(target_arch = "aarch64"))]
fn get_standard_regs_ioctl(&self) -> Result<StandardRegisters> {
let reg_names = [
hv_register_name_HV_X64_REGISTER_RAX,
hv_register_name_HV_X64_REGISTER_RBX,
Expand Down Expand Up @@ -446,6 +509,37 @@ impl VcpuFd {
}
Ok(ret_regs)
}

/// Returns the vCPU general purpose registers using VP register page
#[cfg(not(target_arch = "aarch64"))]
pub fn get_standard_regs_vp_page(&self) -> Result<StandardRegisters> {
let vp_reg_page = self.get_vp_reg_page().unwrap().0;
let mut ret_regs = StandardRegisters::default();
// SAFETY: access union fields
unsafe {
ret_regs.rax = get_gp_regs_field_ptr!(vp_reg_page, rax);
ret_regs.rbx = get_gp_regs_field_ptr!(vp_reg_page, rbx);
ret_regs.rcx = get_gp_regs_field_ptr!(vp_reg_page, rcx);
ret_regs.rdx = get_gp_regs_field_ptr!(vp_reg_page, rdx);
ret_regs.rsi = get_gp_regs_field_ptr!(vp_reg_page, rsi);
ret_regs.rdi = get_gp_regs_field_ptr!(vp_reg_page, rdi);
ret_regs.rsp = get_gp_regs_field_ptr!(vp_reg_page, rsp);
ret_regs.rbp = get_gp_regs_field_ptr!(vp_reg_page, rbp);
ret_regs.r8 = get_gp_regs_field_ptr!(vp_reg_page, r8);
ret_regs.r9 = get_gp_regs_field_ptr!(vp_reg_page, r9);
ret_regs.r10 = get_gp_regs_field_ptr!(vp_reg_page, r10);
ret_regs.r11 = get_gp_regs_field_ptr!(vp_reg_page, r11);
ret_regs.r12 = get_gp_regs_field_ptr!(vp_reg_page, r12);
ret_regs.r13 = get_gp_regs_field_ptr!(vp_reg_page, r13);
ret_regs.r14 = get_gp_regs_field_ptr!(vp_reg_page, r14);
ret_regs.r15 = get_gp_regs_field_ptr!(vp_reg_page, r15);
ret_regs.rip = (*vp_reg_page).__bindgen_anon_1.__bindgen_anon_1.rip;
ret_regs.rflags = (*vp_reg_page).__bindgen_anon_1.__bindgen_anon_1.rflags;
}

Ok(ret_regs)
}

/// Returns the vCPU special registers.
#[cfg(not(target_arch = "aarch64"))]
pub fn get_sregs(&self) -> Result<SpecialRegisters> {
Expand Down Expand Up @@ -517,6 +611,7 @@ impl VcpuFd {

Ok(ret_regs)
}

/// Sets the vCPU special registers
#[cfg(not(target_arch = "aarch64"))]
pub fn set_sregs(&self, sregs: &SpecialRegisters) -> Result<()> {
Expand Down
26 changes: 25 additions & 1 deletion mshv-ioctls/src/ioctls/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,31 @@ impl VmFd {
// SAFETY: we're sure vcpu_fd is valid.
let vcpu = unsafe { File::from_raw_fd(vcpu_fd) };

Ok(new_vcpu(id as u32, vcpu))
// SAFETY: Safe to call as VCPU has this map already available upon creation
let addr = unsafe {
libc::mmap(
std::ptr::null_mut(),
HV_PAGE_SIZE,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
vcpu_fd,
MSHV_VP_MMAP_OFFSET_REGISTERS as i64 * libc::sysconf(libc::_SC_PAGE_SIZE),
)
};
let vp_page = if addr == libc::MAP_FAILED {
// If the MSHV driver returns ENODEV that means it is not supported
// We just set None in that case.
// Otherise there is an error with mmap, return the error.
let err_no = errno::Error::last();
if err_no.errno() != libc::ENODEV {
return Err(errno::Error::last().into());
}
None
} else {
Some(RegisterPage(addr as *mut hv_vp_register_page))
};

Ok(new_vcpu(id as u32, vcpu, vp_page))
}

/// Inject an interrupt into the guest..
Expand Down