diff --git a/mshv-bindings/Cargo.toml b/mshv-bindings/Cargo.toml index 7ae55142..e68a0086 100644 --- a/mshv-bindings/Cargo.toml +++ b/mshv-bindings/Cargo.toml @@ -14,8 +14,8 @@ libc = ">=0.2.39" serde = { version = ">=1.0.27", optional = true } serde_derive = { version = ">=1.0.27", optional = true } vmm-sys-util = ">=0.12.1" - zerocopy = { version = "0.7", features = ["derive"] } +num_enum = "0.7" [dev-dependencies] random-number = "0.1.6" diff --git a/mshv-bindings/src/hvdef.rs b/mshv-bindings/src/hvdef.rs index dddd272a..6b6a1898 100644 --- a/mshv-bindings/src/hvdef.rs +++ b/mshv-bindings/src/hvdef.rs @@ -4,6 +4,7 @@ // #![allow(dead_code)] use zerocopy::{AsBytes, FromBytes, FromZeroes}; +use num_enum::TryFromPrimitive; pub const HV_CPUID_FUNCTION_VERSION_AND_FEATURES: u32 = 0x00000001; pub const HV_CPUID_FUNCTION_HV_VENDOR_AND_MAX_FUNCTION: u32 = 0x40000000; @@ -113,7 +114,7 @@ pub const MSR_HYPERCALL_ADDR_MASK: u64 = !0xfff; pub const MSR_SIEFP_SIMP_ACTIVE: u64 = 1; pub const MSR_SIEFP_SIMP_ADDR_MASK: u64 = !0xfff; -#[derive(Debug)] +#[derive(Debug, Copy, Clone, TryFromPrimitive)] #[repr(u16)] pub enum HvError { InvalidHypercallCode = 0x0002, @@ -172,8 +173,6 @@ pub enum HvError { EventBufferAlreadyFreed = 0x0074, } -pub type HvResult = Result; - #[repr(C)] #[derive(Copy, Clone, AsBytes, Debug, FromBytes, FromZeroes)] pub struct HvMessageHeader { diff --git a/mshv-ioctls/Cargo.toml b/mshv-ioctls/Cargo.toml index 107bfef3..d7d12920 100644 --- a/mshv-ioctls/Cargo.toml +++ b/mshv-ioctls/Cargo.toml @@ -9,3 +9,4 @@ license = "Apache-2.0 OR BSD-3-Clause" libc = ">=0.2.39" mshv-bindings = {path = "../mshv-bindings", features = ["fam-wrappers"]} vmm-sys-util = ">=0.12.1" +thiserror = "1.0" diff --git a/mshv-ioctls/src/ioctls/device.rs b/mshv-ioctls/src/ioctls/device.rs index c5baf2bc..3a843508 100644 --- a/mshv-ioctls/src/ioctls/device.rs +++ b/mshv-ioctls/src/ioctls/device.rs @@ -4,10 +4,9 @@ use std::fs::File; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; -use crate::ioctls::Result; +use crate::ioctls::{MshvError, Result}; use crate::mshv_ioctls::{MSHV_GET_DEVICE_ATTR, MSHV_HAS_DEVICE_ATTR, MSHV_SET_DEVICE_ATTR}; use mshv_bindings::mshv_device_attr; -use vmm_sys_util::errno; use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref}; /// Wrapper over the file descriptor obtained when creating an emulated device in the kernel. @@ -28,7 +27,7 @@ impl DeviceFd { // requirements. let ret = unsafe { ioctl_with_ref(self, MSHV_HAS_DEVICE_ATTR(), device_attr) }; if ret != 0 { - return Err(errno::Error::last()); + return Err(MshvError::from_last_errno()); } Ok(()) } @@ -79,7 +78,7 @@ impl DeviceFd { // requirements. let ret = unsafe { ioctl_with_ref(self, MSHV_SET_DEVICE_ATTR(), device_attr) }; if ret != 0 { - return Err(errno::Error::last()); + return Err(MshvError::from_last_errno()); } Ok(()) } @@ -108,7 +107,7 @@ impl DeviceFd { // requirements. let ret = unsafe { ioctl_with_mut_ref(self, MSHV_GET_DEVICE_ATTR(), device_attr) }; if ret != 0 { - return Err(errno::Error::last()); + return Err(MshvError::from_last_errno()); } Ok(()) } diff --git a/mshv-ioctls/src/ioctls/mod.rs b/mshv-ioctls/src/ioctls/mod.rs index edbfb06a..7eefe57b 100644 --- a/mshv-ioctls/src/ioctls/mod.rs +++ b/mshv-ioctls/src/ioctls/mod.rs @@ -2,13 +2,90 @@ // // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause // +use mshv_bindings::{HvError,mshv_root_hvcall}; +use thiserror::Error; use vmm_sys_util::errno; pub mod device; pub mod system; pub mod vcpu; pub mod vm; + +/// A specialized `Error` type for MSHV ioctls +/// +/// Exposes either a regular linux errno, or details of a hypercall-related +/// error. +/// +/// For convenience, it can always be converted into an errno::Error +#[derive(Error, Debug, Copy, Clone)] +pub enum MshvError { + /// A regular linux errno + #[error("Kernel returned errno: {0}")] + Errno(#[from] errno::Error), + + /// A failed hypercall + /// In case the caller requires an errno, this variant can be converted to + /// an EIO with into(), from(), or errno() for the raw value + #[error("Hypercall {code} failed with status {status:?}")] + Hypercall { + /// The control code, i.e. what type of hypercall it was + code: u16, + /// The status or result code returned from the hypercall + status: HvError, + }, +} + +impl MshvError { + /// Convert to errno + pub fn errno(self: &Self) -> i32 { + errno::Error::from(*self).errno() + } + /// Get the last errno as an MshvError + pub fn from_last_errno() -> Self { + MshvError::Errno(errno::Error::last()) + } +} + +impl From for MshvError { + fn from(ret_args: mshv_root_hvcall) -> Self { + use std::convert::TryFrom; + let err_code = errno::Error::last().errno(); + // EIO signals that the hypercall itself may have failed + if err_code == libc::EIO && ret_args.status != 0 { + match HvError::try_from(ret_args.status) { + Ok(hv_err) => return MshvError::Hypercall { + code: ret_args.code, + status: hv_err, + }, + Err(_) => {} + } + } + err_code.into() + } +} + +impl From for MshvError { + fn from(err: i32) -> Self { + MshvError::Errno(errno::Error::new(err)) + } +} + +impl From for errno::Error { + fn from(err: MshvError) -> Self { + match err { + MshvError::Errno(e) => e, + MshvError::Hypercall{..} => errno::Error::new(libc::EIO), + } + } +} + +impl From for std::io::Error { + fn from(err: MshvError) -> Self { + errno::Error::from(err).into() + } +} + /// A specialized `Result` type for MSHV ioctls. /// /// This typedef is generally used to avoid writing out errno::Error directly and /// is otherwise a direct mapping to Result. -pub type Result = std::result::Result; +pub type Result = std::result::Result; diff --git a/mshv-ioctls/src/ioctls/system.rs b/mshv-ioctls/src/ioctls/system.rs index 70657efa..d7b1e5a8 100644 --- a/mshv-ioctls/src/ioctls/system.rs +++ b/mshv-ioctls/src/ioctls/system.rs @@ -3,14 +3,13 @@ // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause // use crate::ioctls::vm::{new_vmfd, VmFd, VmType}; -use crate::ioctls::Result; +use crate::ioctls::{MshvError, Result}; use crate::mshv_ioctls::*; use libc::{open, O_CLOEXEC, O_NONBLOCK}; use mshv_bindings::*; use std::fs::File; use std::os::raw::c_char; use std::os::unix::io::{FromRawFd, RawFd}; -use vmm_sys_util::errno; use vmm_sys_util::ioctl::ioctl_with_ref; /// Wrapper over MSHV system ioctls. @@ -252,7 +251,7 @@ impl Mshv { // SAFETY: we give a constant null-terminated string and verify the result. let ret = unsafe { open("/dev/mshv\0".as_ptr() as *const c_char, open_flags) }; if ret < 0 { - Err(errno::Error::last()) + Err(MshvError::from_last_errno()) } else { Ok(ret) } @@ -267,7 +266,7 @@ impl Mshv { let vm_file = unsafe { File::from_raw_fd(ret) }; Ok(new_vmfd(vm_file)) } else { - Err(errno::Error::last()) + Err(MshvError::from_last_errno()) } } diff --git a/mshv-ioctls/src/ioctls/vcpu.rs b/mshv-ioctls/src/ioctls/vcpu.rs index ed69768c..08321693 100644 --- a/mshv-ioctls/src/ioctls/vcpu.rs +++ b/mshv-ioctls/src/ioctls/vcpu.rs @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause // -use crate::ioctls::Result; +use crate::ioctls::{MshvError, Result}; use crate::mshv_ioctls::*; use mshv_bindings::*; use std::convert::TryFrom; @@ -10,7 +10,6 @@ use std::fs::File; use std::os::unix::io::{AsRawFd, RawFd}; #[cfg(test)] use std::slice; -use vmm_sys_util::errno; use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref}; // Macro for setting up multiple 64 bit registers together @@ -76,7 +75,7 @@ impl VcpuFd { ioctl_with_mut_ref(self, MSHV_GET_VP_REGISTERS(), &mut mshv_vp_register_args) }; if ret != 0 { - return Err(errno::Error::last()); + return Err(MshvError::from_last_errno()); } Ok(()) } @@ -95,7 +94,7 @@ impl VcpuFd { // SAFETY: IOCTL call with correct types. let ret = unsafe { ioctl_with_ref(self, MSHV_SET_VP_REGISTERS(), &hv_vp_register_args) }; if ret != 0 { - return Err(errno::Error::last()); + return Err(MshvError::from_last_errno()); } Ok(()) } @@ -393,7 +392,7 @@ impl VcpuFd { // we can't do this without the vm fd which isn't available here for bits in &sregs.interrupt_bitmap { if *bits != 0 { - return Err(errno::Error::new(libc::EINVAL)); + return Err(libc::EINVAL.into()); } } @@ -639,7 +638,7 @@ impl VcpuFd { for i in 0..nmsrs { let name = match msr_to_hv_reg_name(msrs.as_slice()[i].index) { Ok(n) => n, - Err(_) => return Err(errno::Error::new(libc::EINVAL)), + Err(_) => return Err(libc::EINVAL.into()), }; reg_assocs.push(hv_register_assoc { name, @@ -669,7 +668,7 @@ impl VcpuFd { for i in 0..nmsrs { let name = match msr_to_hv_reg_name(msrs.as_slice()[i].index) { Ok(n) => n, - Err(_) => return Err(errno::Error::new(libc::EINVAL)), + Err(_) => return Err(libc::EINVAL.into()), }; reg_assocs.push(hv_register_assoc { name, @@ -688,7 +687,7 @@ impl VcpuFd { // SAFETY: we know that our file is a vCPU fd and we verify the return result. let ret = unsafe { ioctl_with_mut_ref(self, MSHV_RUN_VP(), &mut hv_message_input) }; if ret != 0 { - return Err(errno::Error::last()); + return Err(MshvError::from_last_errno()); } Ok(hv_message_input) } @@ -829,7 +828,7 @@ impl VcpuFd { // SAFETY: we know that our file is a vCPU fd and we verify the return result. let ret = unsafe { ioctl_with_mut_ref(self, MSHV_GET_VP_STATE(), state) }; if ret != 0 { - return Err(errno::Error::last()); + return Err(MshvError::from_last_errno()); } Ok(()) } @@ -839,7 +838,7 @@ impl VcpuFd { // SAFETY: IOCTL call with correct types let ret = unsafe { ioctl_with_ref(self, MSHV_SET_VP_STATE(), state) }; if ret != 0 { - return Err(errno::Error::last()); + return Err(MshvError::from_last_errno()); } Ok(()) } @@ -853,7 +852,7 @@ impl VcpuFd { ..Default::default() }; self.get_vp_state_ioctl(&mut vp_state)?; - LapicState::try_from(buffer) + Ok(LapicState::try_from(buffer)?) } /// Sets the state of the LAPIC (Local Advanced Programmable Interrupt Controller). pub fn set_lapic(&self, lapic_state: &LapicState) -> Result<()> { @@ -876,7 +875,7 @@ impl VcpuFd { ..Default::default() }; self.get_vp_state_ioctl(&mut vp_state)?; - XSave::try_from(buffer) + Ok(XSave::try_from(buffer)?) } /// Set the xsave data pub fn set_xsave(&self, data: &XSave) -> Result<()> { @@ -903,7 +902,7 @@ impl VcpuFd { // SAFETY: we know that our file is a vCPU fd, we know the kernel honours its ABI. let ret = unsafe { ioctl_with_mut_ref(self, MSHV_VP_TRANSLATE_GVA(), &mut args) }; if ret != 0 { - return Err(errno::Error::last()); + return Err(MshvError::from_last_errno()); } Ok((gpa, result)) @@ -986,7 +985,7 @@ impl VcpuFd { }; let ret = unsafe { ioctl_with_ref(self, MSHV_VP_REGISTER_INTERCEPT_RESULT(), &args) }; if ret != 0 { - return Err(errno::Error::last()); + return Err(MshvError::from_last_errno()); } Ok(()) @@ -1040,7 +1039,7 @@ impl VcpuFd { // correct amount of memory from our pointer, and we verify the return result. let ret = unsafe { ioctl_with_mut_ref(self, MSHV_GET_VP_CPUID_VALUES(), &mut parms) }; if ret != 0 { - return Err(errno::Error::last()); + return Err(MshvError::from_last_errno()); } Ok([parms.eax, parms.ebx, parms.ecx, parms.edx]) } @@ -1049,7 +1048,7 @@ impl VcpuFd { // SAFETY: we know that our file is a vCPU fd, we know the kernel honours its ABI. let ret = unsafe { ioctl_with_mut_ref(self, MSHV_READ_GPA(), input) }; if ret != 0 { - return Err(errno::Error::last()); + return Err(MshvError::from_last_errno()); } Ok(*input) @@ -1059,7 +1058,7 @@ impl VcpuFd { // SAFETY: we know that our file is a vCPU fd, we know the kernel honours its ABI. let ret = unsafe { ioctl_with_mut_ref(self, MSHV_WRITE_GPA(), input) }; if ret != 0 { - return Err(errno::Error::last()); + return Err(MshvError::from_last_errno()); } Ok(*input) diff --git a/mshv-ioctls/src/ioctls/vm.rs b/mshv-ioctls/src/ioctls/vm.rs index 6b912651..f7ac23bd 100644 --- a/mshv-ioctls/src/ioctls/vm.rs +++ b/mshv-ioctls/src/ioctls/vm.rs @@ -4,7 +4,7 @@ // use crate::ioctls::device::{new_device, DeviceFd}; use crate::ioctls::vcpu::{new_vcpu, VcpuFd}; -use crate::ioctls::Result; +use crate::ioctls::{MshvError, Result}; use crate::mshv_ioctls::*; use mshv_bindings::*; @@ -13,7 +13,6 @@ use std::convert::TryFrom; use std::fs::File; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; -use vmm_sys_util::errno; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref}; @@ -106,7 +105,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(errno::Error::last()) + Err(MshvError::from_last_errno()) } } /// Modify host visibility for a range of GPA @@ -120,7 +119,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(errno::Error::last()) + Err(MshvError::from_last_errno()) } } /// Import the isolated pages @@ -133,7 +132,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(errno::Error::last()) + Err(MshvError::from_last_errno()) } } /// Mark completion of importing the isoalted pages @@ -143,7 +142,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(errno::Error::last()) + Err(MshvError::from_last_errno()) } } /// Issue PSP request from guest side @@ -153,7 +152,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(errno::Error::last()) + Err(MshvError::from_last_errno()) } } /// Create AP threads for SEV-SNP guest @@ -163,7 +162,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(errno::Error::last()) + Err(MshvError::from_last_errno()) } } /// Creates/modifies a guest physical memory. @@ -173,7 +172,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(errno::Error::last()) + Err(MshvError::from_last_errno()) } } /// Unmap a guest physical memory. @@ -183,7 +182,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(errno::Error::last()) + Err(MshvError::from_last_errno()) } } /// Creates a new MSHV vCPU file descriptor @@ -194,7 +193,7 @@ impl VmFd { // SAFETY: IOCTL with correct types let vcpu_fd = unsafe { ioctl_with_ref(&self.vm, MSHV_CREATE_VP(), &vp_arg) }; if vcpu_fd < 0 { - return Err(errno::Error::last()); + return Err(MshvError::from_last_errno()); } // Wrap the vCPU now in case the following ? returns early. This is safe because we verified @@ -230,7 +229,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(errno::Error::last()) + Err(MshvError::from_last_errno()) } } /// @@ -248,7 +247,7 @@ impl VmFd { if ret == 0 { Ok(event_info.newly_signaled != 0) } else { - Err(errno::Error::last()) + Err(MshvError::from_last_errno()) } } /// @@ -266,7 +265,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(errno::Error::last()) + Err(MshvError::from_last_errno()) } } /// @@ -289,7 +288,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(errno::Error::last()) + Err(MshvError::from_last_errno()) } } /// irqfd: Passes in an eventfd which is to be used for injecting @@ -307,7 +306,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(errno::Error::last()) + Err(MshvError::from_last_errno()) } } /// Registers an event that will, when signaled, trigger the `gsi` IRQ. @@ -428,7 +427,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(errno::Error::last()) + Err(MshvError::from_last_errno()) } } @@ -446,7 +445,7 @@ impl VmFd { // let mmio_addr = match addr { IoEventAddress::Pio(_) => { - return Err(errno::Error::new(libc::ENOTSUP)); + return Err(libc::ENOTSUP.into()); } IoEventAddress::Mmio(ref m) => *m, }; @@ -469,7 +468,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(errno::Error::last()) + Err(MshvError::from_last_errno()) } } /// Registers an event to be signaled whenever a certain address is written to. @@ -555,7 +554,7 @@ impl VmFd { if ret == 0 { Ok(property.property_value) } else { - Err(errno::Error::last()) + Err(MshvError::from_last_errno()) } } /// Sets a partion property @@ -569,7 +568,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(errno::Error::last()) + Err(MshvError::from_last_errno()) } } /// Enable dirty page tracking by hypervisor @@ -632,7 +631,7 @@ impl VmFd { if ret == 0 { Ok(gpa_pages_access_state) } else { - Err(errno::Error::last()) + Err(MshvError::from_last_errno()) } } /// Gets the bitmap of pages dirtied since the last call of this function @@ -649,7 +648,7 @@ impl VmFd { // each page. // SAFETY: FFI call to libc let page_size = match unsafe { libc::sysconf(libc::_SC_PAGESIZE) } { - -1 => return Err(errno::Error::last()), + -1 => return Err(MshvError::from_last_errno()), ps => ps as usize, }; @@ -702,7 +701,7 @@ impl VmFd { // SAFETY: fd is valid Ok(new_device(unsafe { File::from_raw_fd(device.fd as i32) })) } else { - Err(errno::Error::last()) + Err(MshvError::from_last_errno()) } } } @@ -925,7 +924,7 @@ mod tests { let res = vm.register_deliverabilty_notifications(0, 1); assert!(res.is_err()); if let Err(e) = res { - assert!(e == Error::new(libc::EINVAL)); + assert!(e == Err(MshvError::from(libc::EINVAL))) } } } diff --git a/mshv-ioctls/src/lib.rs b/mshv-ioctls/src/lib.rs index 0a18fab9..df580e5a 100644 --- a/mshv-ioctls/src/lib.rs +++ b/mshv-ioctls/src/lib.rs @@ -208,6 +208,7 @@ pub use ioctls::vm::IoEventAddress; pub use ioctls::vm::NoDatamatch; pub use ioctls::vm::VmFd; pub use ioctls::vm::VmType; +pub use ioctls::MshvError; #[macro_use] mod mshv_ioctls;