diff --git a/openhcl/hcl/src/ioctl.rs b/openhcl/hcl/src/ioctl.rs index a01e4e293e..7bc4a7569e 100644 --- a/openhcl/hcl/src/ioctl.rs +++ b/openhcl/hcl/src/ioctl.rs @@ -46,6 +46,7 @@ use hvdef::HvMessage; use hvdef::HvRegisterName; use hvdef::HvRegisterValue; use hvdef::HvRegisterVsmPartitionConfig; +use hvdef::HvStatus; use hvdef::HvX64RegisterName; use hvdef::HvX64RegisterPage; use hvdef::HypercallCode; @@ -171,10 +172,9 @@ pub enum HypercallError { impl HypercallError { pub(crate) fn check(r: Result) -> Result<(), Self> { match r { - Ok(0) => Ok(()), - Ok(n) => Err(Self::Hypervisor(HvError( - n.try_into().expect("hypervisor result out of range"), - ))), + Ok(n) => HvStatus(n.try_into().expect("hypervisor result out of range")) + .result() + .map_err(Self::Hypervisor), Err(err) => Err(Self::Ioctl(IoctlError(err))), } } @@ -1040,7 +1040,7 @@ impl MshvHvcall { .map_err(HvcallError::HypercallIoctlFailed)?; } - if call_object.status.call_status() == HvError::Timeout.0 { + if call_object.status.call_status() == Err(HvError::Timeout).into() { // Any hypercall can timeout, even one that doesn't have reps. Continue processing // from wherever the hypervisor left off. The rep start index isn't checked for // validity, since it is only being used as an input to the untrusted hypervisor. diff --git a/openhcl/sidecar/src/arch/x86_64/vp.rs b/openhcl/sidecar/src/arch/x86_64/vp.rs index d875bfb4e8..f1942ac59c 100644 --- a/openhcl/sidecar/src/arch/x86_64/vp.rs +++ b/openhcl/sidecar/src/arch/x86_64/vp.rs @@ -23,7 +23,7 @@ use core::sync::atomic::Ordering::Release; use hvdef::hypercall::HvInputVtl; use hvdef::hypercall::HvRegisterAssoc; use hvdef::hypercall::TranslateVirtualAddressX64; -use hvdef::HvError; +use hvdef::HvStatus; use hvdef::HvVtlEntryReason; use hvdef::HvX64RegisterName; use hvdef::HypercallCode; @@ -400,7 +400,7 @@ fn get_vp_registers(command_page: &mut CommandPage) { count, target_vtl, rsvd: _, - ref mut result, + ref mut status, rsvd2: _, regs: [], } = FromBytes::mut_from(request).unwrap(); @@ -413,7 +413,7 @@ fn get_vp_registers(command_page: &mut CommandPage) { return; }; - *result = HvError(0); + *status = HvStatus::SUCCESS; for &mut HvRegisterAssoc { name, pad: _, @@ -434,7 +434,7 @@ fn get_vp_registers(command_page: &mut CommandPage) { match r { Ok(v) => *value = v, Err(err) => { - *result = err; + *status = Err(err).into(); break; } }; @@ -450,7 +450,7 @@ fn set_vp_registers(command_page: &mut CommandPage) { count, target_vtl, rsvd: _, - ref mut result, + ref mut status, rsvd2: _, regs: [], } = FromBytes::mut_from(request).unwrap(); @@ -463,7 +463,7 @@ fn set_vp_registers(command_page: &mut CommandPage) { return; }; - *result = HvError(0); + *status = HvStatus::SUCCESS; for &HvRegisterAssoc { name, value, @@ -482,8 +482,8 @@ fn set_vp_registers(command_page: &mut CommandPage) { set_hv_vp_register(target_vtl, name, value) }; - if let Err(err) = r { - *result = err; + if r.is_err() { + *status = r.into(); break; } } @@ -509,17 +509,16 @@ fn translate_gva(command_page: &mut CommandPage) { } let result = hypercall(HypercallCode::HvCallTranslateVirtualAddressEx, 0); - let (result, output) = match result { - Ok(()) => { - // SAFETY: the output is not concurrently accessed - let output = unsafe { &*addr_space::hypercall_output() }; - (HvError(0), FromBytes::read_from_prefix(output).unwrap()) - } - Err(err) => (err, FromZeroes::new_zeroed()), + let output = if result.is_ok() { + // SAFETY: the output is not concurrently accessed + let output = unsafe { &*addr_space::hypercall_output() }; + FromBytes::read_from_prefix(output).unwrap() + } else { + FromZeroes::new_zeroed() }; TranslateGvaResponse { - result, + status: result.into(), rsvd: [0; 7], output, } diff --git a/openhcl/sidecar_client/src/lib.rs b/openhcl/sidecar_client/src/lib.rs index 07c69eb7b7..983d92f022 100644 --- a/openhcl/sidecar_client/src/lib.rs +++ b/openhcl/sidecar_client/src/lib.rs @@ -15,6 +15,7 @@ use hvdef::hypercall::HvRegisterAssoc; use hvdef::hypercall::TranslateVirtualAddressExOutputX64; use hvdef::HvError; use hvdef::HvMessage; +use hvdef::HvStatus; use pal_async::driver::PollImpl; use pal_async::driver::SpawnDriver; use pal_async::fd::PollFdReady; @@ -434,7 +435,7 @@ impl<'a> SidecarVp<'a> { count: regs.len() as u16, target_vtl, rsvd: 0, - result: HvError(0), + status: HvStatus::SUCCESS, rsvd2: [0; 10], regs: [], }, @@ -442,11 +443,9 @@ impl<'a> SidecarVp<'a> { ); buf.copy_from_slice(regs); self.run_sync()?; - let (&GetSetVpRegisterRequest { result, .. }, buf) = + let (&GetSetVpRegisterRequest { status, .. }, buf) = self.command_result::<_, HvRegisterAssoc>(regs.len())?; - if result != HvError(0) { - return Err(SidecarError::Hypervisor(result)); - } + status.result().map_err(SidecarError::Hypervisor)?; regs.copy_from_slice(buf); } Ok(()) @@ -466,7 +465,7 @@ impl<'a> SidecarVp<'a> { count: regs.len() as u16, target_vtl, rsvd: 0, - result: HvError(0), + status: HvStatus::SUCCESS, rsvd2: [0; 10], regs: [], }, @@ -474,10 +473,8 @@ impl<'a> SidecarVp<'a> { ); buf.copy_from_slice(regs); self.run_sync()?; - let &GetSetVpRegisterRequest { result, .. } = self.command_result::<_, u8>(0)?.0; - if result != HvError(0) { - return Err(SidecarError::Hypervisor(result)); - } + let &GetSetVpRegisterRequest { status, .. } = self.command_result::<_, u8>(0)?.0; + status.result().map_err(SidecarError::Hypervisor)?; } Ok(()) } @@ -491,16 +488,14 @@ impl<'a> SidecarVp<'a> { ) -> Result { tracing::trace!("translate gva"); let &TranslateGvaResponse { - result, + status, rsvd: _, output, } = self.dispatch_sync( SidecarCommand::TRANSLATE_GVA, TranslateGvaRequest { gvn, control_flags }, )?; - if result != HvError(0) { - return Err(SidecarError::Hypervisor(result)); - } + status.result().map_err(SidecarError::Hypervisor)?; Ok(output) } diff --git a/openhcl/sidecar_defs/src/lib.rs b/openhcl/sidecar_defs/src/lib.rs index ce42d18a1c..fe93520c1d 100644 --- a/openhcl/sidecar_defs/src/lib.rs +++ b/openhcl/sidecar_defs/src/lib.rs @@ -10,8 +10,8 @@ use core::sync::atomic::AtomicU32; use core::sync::atomic::AtomicU8; use hvdef::hypercall::HvInputVtl; -use hvdef::HvError; use hvdef::HvMessage; +use hvdef::HvStatus; use open_enum::open_enum; use zerocopy::AsBytes; use zerocopy::FromBytes; @@ -214,7 +214,7 @@ pub struct GetSetVpRegisterRequest { /// Reserved. pub rsvd: u8, /// The hypervisor result. - pub result: HvError, + pub status: HvStatus, /// Reserved. pub rsvd2: [u8; 10], /// Alignment field. @@ -243,7 +243,7 @@ pub struct TranslateGvaRequest { #[derive(Debug, Copy, Clone, AsBytes, FromBytes, FromZeroes)] pub struct TranslateGvaResponse { /// The hypervisor result. - pub result: HvError, + pub status: HvStatus, /// Reserved. pub rsvd: [u16; 7], /// The output of the translation. diff --git a/vm/hv1/hv1_hypercall/src/support.rs b/vm/hv1/hv1_hypercall/src/support.rs index 85ea1f4b7c..a764c092a5 100644 --- a/vm/hv1/hv1_hypercall/src/support.rs +++ b/vm/hv1/hv1_hypercall/src/support.rs @@ -145,7 +145,7 @@ impl<'a, T: HypercallIo> InnerDispatcher<'a, T> { /// Complete hypercall handling. fn complete(&mut self, output: Option) { if let Some(output) = output { - if output.call_status() == HvError::Timeout.0 { + if output.call_status() == Err(HvError::Timeout).into() { self.handler.retry( self.control .with_rep_start(output.elements_processed()) @@ -282,7 +282,7 @@ impl<'a, T: HypercallIo> InnerDispatcher<'a, T> { // which is handled as a failure), nothing is written back. let output_end = if out_elem_size > 0 { out_elem_size * ret.elements_processed() - } else if ret.call_status() == 0 { + } else if ret.call_status().is_ok() { output_len } else { 0 @@ -338,7 +338,7 @@ impl<'a, T: HypercallIo> InnerDispatcher<'a, T> { // which is handled as a failure), nothing is written back. let output_end = if out_elem_size > 0 { out_elem_size * ret.elements_processed() - } else if ret.call_status() == 0 { + } else if ret.call_status().is_ok() { output_len } else { 0 @@ -354,7 +354,7 @@ impl<'a, T: HypercallIo> InnerDispatcher<'a, T> { ret }; - if ret.call_status() == 0 { + if ret.call_status().is_ok() { debug_assert_eq!(ret.elements_processed(), control.rep_count()); } diff --git a/vm/hv1/hv1_hypercall/src/tests.rs b/vm/hv1/hv1_hypercall/src/tests.rs index 053bd78592..05bffdd760 100644 --- a/vm/hv1/hv1_hypercall/src/tests.rs +++ b/vm/hv1/hv1_hypercall/src/tests.rs @@ -302,13 +302,13 @@ impl From for HypercallOutput { match result { TestResult::Simple(SimpleResult::Success) => HypercallOutput::new(), TestResult::Simple(SimpleResult::Failure(err)) => { - HypercallOutput::new().with_call_status(err.0) + HypercallOutput::new().with_call_status(Err(err).into()) } TestResult::Rep(RepResult::Success(rep_count)) => { HypercallOutput::new().with_elements_processed(rep_count) } TestResult::Rep(RepResult::Failure(err, rep_count)) => HypercallOutput::new() - .with_call_status(err.0) + .with_call_status(Err(err).into()) .with_elements_processed(rep_count), _ => panic!("Should not be invoked for VTL"), } @@ -1371,7 +1371,7 @@ where let mut io = io_gen(&mut handler); let result = HypercallOutput::from(io.get_result()); let control = Control::from(io.control()); - let call_status = HvError(result.call_status()); + let call_status = result.call_status(); // Copy the output back. Note that in the case of errors the hypercall parser may not have // actually modified the output buffers/registers, and it is the responsibility of the test @@ -1382,8 +1382,8 @@ where // that in cases where this routine does not modify the output, the hypercall parser does // not do so either. if is_timeout - || (call_status != HvError::InvalidHypercallInput - && call_status != HvError::InvalidAlignment) + || (call_status != Err(HvError::InvalidHypercallInput).into() + && call_status != Err(HvError::InvalidAlignment).into()) { let mut output_buffer; let (hdr, reps) = if !params.fast { @@ -1559,7 +1559,7 @@ fn hypercall_simple(test_params: TestParams) { check_test_result(&test_params, result, control); - let expected_output_size = if result.call_status() == 0 { + let expected_output_size = if result.call_status().is_ok() { assert_eq!( output.as_bytes(), TestController::generate_test_output::().as_bytes() @@ -1779,7 +1779,7 @@ fn hypercall_variable(test_params: TestParams) { check_test_result(&test_params, result, control); - let expected_output_size = if result.call_status() == 0 { + let expected_output_size = if result.call_status().is_ok() { assert_eq!( output.as_bytes(), TestController::generate_test_output::().as_bytes() diff --git a/vm/hv1/hvdef/src/lib.rs b/vm/hv1/hvdef/src/lib.rs index 8894b96e69..c1ab316479 100644 --- a/vm/hv1/hvdef/src/lib.rs +++ b/vm/hv1/hvdef/src/lib.rs @@ -342,146 +342,246 @@ pub const HV_X64_MSR_GUEST_CRASH_CTL: u32 = 0x40000105; pub const HV_X64_GUEST_CRASH_PARAMETER_MSRS: usize = 5; -open_enum! { - #[derive(AsBytes, FromBytes, FromZeroes)] - pub enum HvError: u16 { - #![allow(non_upper_case_globals)] +/// A hypervisor status code. +/// +/// The non-success status codes are defined in [`HvError`]. +#[derive(Copy, Clone, AsBytes, FromBytes, FromZeroes, PartialEq, Eq)] +#[repr(transparent)] +pub struct HvStatus(pub u16); + +impl HvStatus { + /// The success status code. + pub const SUCCESS: Self = Self(0); + + /// Returns `Ok(())` if this is `HvStatus::SUCCESS`, otherwise returns an + /// `Err(err)` where `err` is the corresponding `HvError`. + pub fn result(self) -> HvResult<()> { + if let Ok(err) = self.0.try_into() { + Err(HvError(err)) + } else { + Ok(()) + } + } + + /// Returns true if this is `HvStatus::SUCCESS`. + pub fn is_ok(self) -> bool { + self == Self::SUCCESS + } + + /// Returns true if this is not `HvStatus::SUCCESS`. + pub fn is_err(self) -> bool { + self != Self::SUCCESS + } + + const fn from_bits(bits: u16) -> Self { + Self(bits) + } + + const fn into_bits(self) -> u16 { + self.0 + } +} + +impl From> for HvStatus { + fn from(err: Result<(), HvError>) -> Self { + err.err().map_or(Self::SUCCESS, |err| Self(err.0.get())) + } +} + +impl Debug for HvStatus { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self.result() { + Ok(()) => f.write_str("Success"), + Err(err) => Debug::fmt(&err, f), + } + } +} + +/// An [`HvStatus`] value representing an error. +// +// DEVNOTE: use `NonZeroU16` to get a niche optimization, since 0 is reserved +// for success. +#[derive(Copy, Clone, PartialEq, Eq, AsBytes)] +#[repr(transparent)] +pub struct HvError(core::num::NonZeroU16); + +impl From for HvError { + fn from(err: core::num::NonZeroU16) -> Self { + Self(err) + } +} - InvalidHypercallCode = 0x0002, - InvalidHypercallInput = 0x0003, - InvalidAlignment = 0x0004, - InvalidParameter = 0x0005, - AccessDenied = 0x0006, - InvalidPartitionState = 0x0007, - OperationDenied = 0x0008, - UnknownProperty = 0x0009, - PropertyValueOutOfRange = 0x000A, - InsufficientMemory = 0x000B, - PartitionTooDeep = 0x000C, - InvalidPartitionId = 0x000D, - InvalidVpIndex = 0x000E, - NotFound = 0x0010, - InvalidPortId = 0x0011, - InvalidConnectionId = 0x0012, - InsufficientBuffers = 0x0013, - NotAcknowledged = 0x0014, - InvalidVpState = 0x0015, - Acknowledged = 0x0016, - InvalidSaveRestoreState = 0x0017, - InvalidSynicState = 0x0018, - ObjectInUse = 0x0019, - InvalidProximityDomainInfo = 0x001A, - NoData = 0x001B, - Inactive = 0x001C, - NoResources = 0x001D, - FeatureUnavailable = 0x001E, - PartialPacket = 0x001F, - ProcessorFeatureNotSupported = 0x0020, - ProcessorCacheLineFlushSizeIncompatible = 0x0030, - InsufficientBuffer = 0x0033, - IncompatibleProcessor = 0x0037, - InsufficientDeviceDomains = 0x0038, - CpuidFeatureValidationError = 0x003C, - CpuidXsaveFeatureValidationError = 0x003D, - ProcessorStartupTimeout = 0x003E, - SmxEnabled = 0x003F, - InvalidLpIndex = 0x0041, - InvalidRegisterValue = 0x0050, - InvalidVtlState = 0x0051, - NxNotDetected = 0x0055, - InvalidDeviceId = 0x0057, - InvalidDeviceState = 0x0058, - PendingPageRequests = 0x0059, - PageRequestInvalid = 0x0060, - KeyAlreadyExists = 0x0065, - DeviceAlreadyInDomain = 0x0066, - InvalidCpuGroupId = 0x006F, - InvalidCpuGroupState = 0x0070, - OperationFailed = 0x0071, - NotAllowedWithNestedVirtActive = 0x0072, - InsufficientRootMemory = 0x0073, - EventBufferAlreadyFreed = 0x0074, - Timeout = 0x0078, - VtlAlreadyEnabled = 0x0086, - UnknownRegisterName = 0x0087, +impl Debug for HvError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self.debug_name() { + Some(name) => f.pad(name), + None => Debug::fmt(&self.0.get(), f), + } } } impl core::fmt::Display for HvError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let error_str = match *self { - HvError::InvalidHypercallCode => "Invalid hypercall code", - HvError::InvalidHypercallInput => "Invalid hypercall input", - HvError::InvalidAlignment => "Invalid alignment", - HvError::InvalidParameter => "Invalid parameter", - HvError::AccessDenied => "Access denied", - HvError::InvalidPartitionState => "Invalid partition state", - HvError::OperationDenied => "Operation denied", - HvError::UnknownProperty => "Unknown property", - HvError::PropertyValueOutOfRange => "Property value out of range", - HvError::InsufficientMemory => "Insufficient memory", - HvError::PartitionTooDeep => "Partition too deep", - HvError::InvalidPartitionId => "Invalid partition ID", - HvError::InvalidVpIndex => "Invalid VP index", - HvError::NotFound => "Not found", - HvError::InvalidPortId => "Invalid port ID", - HvError::InvalidConnectionId => "Invalid connection ID", - HvError::InsufficientBuffers => "Insufficient buffers", - HvError::NotAcknowledged => "Not acknowledged", - HvError::InvalidVpState => "Invalid VP state", - HvError::Acknowledged => "Acknowledged", - HvError::InvalidSaveRestoreState => "Invalid save restore state", - HvError::InvalidSynicState => "Invalid SynIC state", - HvError::ObjectInUse => "Object in use", - HvError::InvalidProximityDomainInfo => "Invalid proximity domain info", - HvError::NoData => "No data", - HvError::Inactive => "Inactive", - HvError::NoResources => "No resources", - HvError::FeatureUnavailable => "Feature unavailable", - HvError::PartialPacket => "Partial packet", - HvError::ProcessorFeatureNotSupported => "Processor feature not supported", - HvError::ProcessorCacheLineFlushSizeIncompatible => { - "Processor cache line flush size incompatible" - } - HvError::InsufficientBuffer => "Insufficient buffer", - HvError::IncompatibleProcessor => "Incompatible processor", - HvError::InsufficientDeviceDomains => "Insufficient device domains", - HvError::CpuidFeatureValidationError => "CPUID feature validation error", - HvError::CpuidXsaveFeatureValidationError => "CPUID XSAVE feature validation error", - HvError::ProcessorStartupTimeout => "Processor startup timeout", - HvError::SmxEnabled => "SMX enabled", - HvError::InvalidLpIndex => "Invalid LP index", - HvError::InvalidRegisterValue => "Invalid register value", - HvError::InvalidVtlState => "Invalid VTL state", - HvError::NxNotDetected => "NX not detected", - HvError::InvalidDeviceId => "Invalid device ID", - HvError::InvalidDeviceState => "Invalid device state", - HvError::PendingPageRequests => "Pending page requests", - HvError::PageRequestInvalid => "Page request invalid", - HvError::KeyAlreadyExists => "Key already exists", - HvError::DeviceAlreadyInDomain => "Device already in domain", - HvError::InvalidCpuGroupId => "Invalid CPU group ID", - HvError::InvalidCpuGroupState => "Invalid CPU group state", - HvError::OperationFailed => "Operation failed", - HvError::NotAllowedWithNestedVirtActive => { - "Not allowed with nested virtualization active" - } - HvError::InsufficientRootMemory => "Insufficient root memory", - HvError::EventBufferAlreadyFreed => "Event buffer already freed", - HvError::Timeout => "The specified timeout expired before the operation completed.", - HvError::VtlAlreadyEnabled => { - "The VTL specified for the operation is already in an enabled state." - } - other => return write!(f, "Hypervisor error {:#06x}", other.0), - }; - f.write_str(error_str) + match self.doc_str() { + Some(s) => f.write_str(s), + None => write!(f, "Hypervisor error {:#06x}", self.0), + } } } impl core::error::Error for HvError {} -/// Hypervisor result type for simple hypercalls, or code where only an HV_STATUS is to be returned. -/// The error is an `HvError` and the success value `T` is the output data of the hypercall. +macro_rules! hv_error { + ($ty:ty, $(#[doc = $doc:expr] $ident:ident = $val:expr),* $(,)?) => { + + #[allow(non_upper_case_globals)] + impl $ty { + $( + #[doc = $doc] + pub const $ident: Self = Self(core::num::NonZeroU16::new($val).unwrap()); + )* + + fn debug_name(&self) -> Option<&'static str> { + Some(match self.0.get() { + $( + $val => stringify!($ident), + )* + _ => return None, + }) + } + + fn doc_str(&self) -> Option<&'static str> { + Some(match self.0.get() { + $( + $val => $doc, + )* + _ => return None, + }) + } + } + }; +} + +// DEVNOTE: the doc comments here are also used as the runtime error strings. +hv_error! { + HvError, + /// Invalid hypercall code + InvalidHypercallCode = 0x0002, + /// Invalid hypercall input + InvalidHypercallInput = 0x0003, + /// Invalid alignment + InvalidAlignment = 0x0004, + /// Invalid parameter + InvalidParameter = 0x0005, + /// Access denied + AccessDenied = 0x0006, + /// Invalid partition state + InvalidPartitionState = 0x0007, + /// Operation denied + OperationDenied = 0x0008, + /// Unknown property + UnknownProperty = 0x0009, + /// Property value out of range + PropertyValueOutOfRange = 0x000A, + /// Insufficient memory + InsufficientMemory = 0x000B, + /// Partition too deep + PartitionTooDeep = 0x000C, + /// Invalid partition ID + InvalidPartitionId = 0x000D, + /// Invalid VP index + InvalidVpIndex = 0x000E, + /// Not found + NotFound = 0x0010, + /// Invalid port ID + InvalidPortId = 0x0011, + /// Invalid connection ID + InvalidConnectionId = 0x0012, + /// Insufficient buffers + InsufficientBuffers = 0x0013, + /// Not acknowledged + NotAcknowledged = 0x0014, + /// Invalid VP state + InvalidVpState = 0x0015, + /// Acknowledged + Acknowledged = 0x0016, + /// Invalid save restore state + InvalidSaveRestoreState = 0x0017, + /// Invalid SynIC state + InvalidSynicState = 0x0018, + /// Object in use + ObjectInUse = 0x0019, + /// Invalid proximity domain info + InvalidProximityDomainInfo = 0x001A, + /// No data + NoData = 0x001B, + /// Inactive + Inactive = 0x001C, + /// No resources + NoResources = 0x001D, + /// Feature unavailable + FeatureUnavailable = 0x001E, + /// Partial packet + PartialPacket = 0x001F, + /// Processor feature not supported + ProcessorFeatureNotSupported = 0x0020, + /// Processor cache line flush size incompatible + ProcessorCacheLineFlushSizeIncompatible = 0x0030, + /// Insufficient buffer + InsufficientBuffer = 0x0033, + /// Incompatible processor + IncompatibleProcessor = 0x0037, + /// Insufficient device domains + InsufficientDeviceDomains = 0x0038, + /// CPUID feature validation error + CpuidFeatureValidationError = 0x003C, + /// CPUID XSAVE feature validation error + CpuidXsaveFeatureValidationError = 0x003D, + /// Processor startup timeout + ProcessorStartupTimeout = 0x003E, + /// SMX enabled + SmxEnabled = 0x003F, + /// Invalid LP index + InvalidLpIndex = 0x0041, + /// Invalid register value + InvalidRegisterValue = 0x0050, + /// Invalid VTL state + InvalidVtlState = 0x0051, + /// NX not detected + NxNotDetected = 0x0055, + /// Invalid device ID + InvalidDeviceId = 0x0057, + /// Invalid device state + InvalidDeviceState = 0x0058, + /// Pending page requests + PendingPageRequests = 0x0059, + /// Page request invalid + PageRequestInvalid = 0x0060, + /// Key already exists + KeyAlreadyExists = 0x0065, + /// Device already in domain + DeviceAlreadyInDomain = 0x0066, + /// Invalid CPU group ID + InvalidCpuGroupId = 0x006F, + /// Invalid CPU group state + InvalidCpuGroupState = 0x0070, + /// Operation failed + OperationFailed = 0x0071, + /// Not allowed with nested virtualization active + NotAllowedWithNestedVirtActive = 0x0072, + /// Insufficient root memory + InsufficientRootMemory = 0x0073, + /// Event buffer already freed + EventBufferAlreadyFreed = 0x0074, + /// The specified timeout expired before the operation completed. + Timeout = 0x0078, + /// The VTL specified for the operation is already in an enabled state. + VtlAlreadyEnabled = 0x0086, + /// Unknown register name + UnknownRegisterName = 0x0087, +} + +/// A useful result type for hypervisor operations. pub type HvResult = Result; #[repr(u8)] @@ -760,8 +860,8 @@ pub mod hypercall { #[derive(AsBytes, FromBytes, FromZeroes)] #[must_use] pub struct HypercallOutput { - /// The HV_STATUS returned by the hypervisor. - pub call_status: u16, + #[bits(16)] + pub call_status: HvStatus, pub rsvd: u16, #[bits(12)] pub elements_processed: usize, @@ -771,7 +871,7 @@ pub mod hypercall { impl From for HypercallOutput { fn from(e: HvError) -> Self { - Self::new().with_call_status(e.0) + Self::new().with_call_status(Err(e).into()) } } @@ -780,11 +880,7 @@ pub mod hypercall { pub const SUCCESS: Self = Self::new(); pub fn result(&self) -> Result<(), HvError> { - if self.call_status() == 0 { - Ok(()) - } else { - Err(HvError(self.call_status())) - } + self.call_status().result() } } diff --git a/vm/whp/src/lib.rs b/vm/whp/src/lib.rs index e093ca01e4..20621980f1 100644 --- a/vm/whp/src/lib.rs +++ b/vm/whp/src/lib.rs @@ -20,6 +20,8 @@ use std::alloc::Layout; use std::ffi::c_void; use std::fmt; use std::fmt::Debug; +use std::num::NonZeroI32; +use std::num::NonZeroU16; use std::os::windows::prelude::*; use std::ptr::null; use std::ptr::null_mut; @@ -160,20 +162,26 @@ pub struct SyntheticProcessorFeatures { } #[derive(Clone, Eq, PartialEq)] -pub struct WHvError(i32); +pub struct WHvError(NonZeroI32); impl WHvError { - pub const WHV_E_UNKNOWN_CAPABILITY: Self = Self(api::WHV_E_UNKNOWN_CAPABILITY); + pub const WHV_E_UNKNOWN_CAPABILITY: Self = + Self(NonZeroI32::new(api::WHV_E_UNKNOWN_CAPABILITY).unwrap()); + + const WHV_E_INSUFFICIENT_BUFFER: Self = + Self(NonZeroI32::new(api::WHV_E_INSUFFICIENT_BUFFER).unwrap()); + + const ERROR_BAD_PATHNAME: Self = Self(NonZeroI32::new(ERROR_BAD_PATHNAME as i32).unwrap()); pub fn code(&self) -> i32 { - self.0 + self.0.get() } /// Returns the underlying hypervisor error code, if there is one. - pub fn hv_result(&self) -> Option { - if self.0 & 0x3fff0000 == 0x00350000 { + pub fn hv_result(&self) -> Option { + if self.0.get() & 0x3fff0000 == 0x00350000 { // This is a hypervisor facility code. - Some(self.0 as u16) + Some(NonZeroU16::new(self.0.get() as u16).unwrap()) } else { None } @@ -182,7 +190,7 @@ impl WHvError { impl fmt::Display for WHvError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&std::io::Error::from_raw_os_error(self.0), f) + fmt::Display::fmt(&std::io::Error::from_raw_os_error(self.0.get()), f) } } @@ -196,7 +204,7 @@ impl std::error::Error for WHvError {} impl From for std::io::Error { fn from(err: WHvError) -> Self { - std::io::Error::from_raw_os_error(err.0) + std::io::Error::from_raw_os_error(err.0.get()) } } @@ -301,7 +309,7 @@ fn check_hresult(hr: i32) -> Result<()> { if hr >= 0 { Ok(()) } else { - Err(WHvError(hr)) + Err(WHvError(NonZeroI32::new(hr).unwrap())) } } @@ -965,7 +973,7 @@ impl VpciResource { let mut path16: Vec<_> = path.encode_utf16().collect(); path16.push(0); if path16.len() > sriov.PnpInstanceId.len() { - return Err(WHvError(ERROR_BAD_PATHNAME as i32)); + return Err(WHvError::ERROR_BAD_PATHNAME); } sriov.PnpInstanceId[..path16.len()].copy_from_slice(&path16); std::slice::from_raw_parts( @@ -1316,7 +1324,7 @@ impl Device<'_> { &mut size, )) .unwrap_err(); - if err != WHvError(api::WHV_E_INSUFFICIENT_BUFFER) { + if err != WHvError::WHV_E_INSUFFICIENT_BUFFER { return Err(err); } let layout = Layout::from_size_align( @@ -1512,7 +1520,7 @@ impl<'a> Processor<'a> { r.set_len(n as usize); break; } - Err(WHvError(api::WHV_E_INSUFFICIENT_BUFFER)) => { + Err(WHvError::WHV_E_INSUFFICIENT_BUFFER) => { r.reserve(n as usize); } Err(err) => return Err(err), diff --git a/vmm_core/virt_whp/src/hypercalls.rs b/vmm_core/virt_whp/src/hypercalls.rs index b0f367e615..ccb078859d 100644 --- a/vmm_core/virt_whp/src/hypercalls.rs +++ b/vmm_core/virt_whp/src/hypercalls.rs @@ -199,7 +199,7 @@ impl hv1_hypercall::SignalEventDirect for WhpHypercallExit<'_, '_, T> target_vp .whp(vtl) .signal_synic_event(sint, flag) - .map_err(|err| match err.hv_result().map(HvError) { + .map_err(|err| match err.hv_result().map(HvError::from) { Some(err @ HvError::InvalidSynicState) => err, _ => { tracing::error!( @@ -1835,7 +1835,8 @@ mod aarch64 { self.current_whp() .get_registers(&[reg], &mut value) .map_err(|err| { - err.hv_result().map_or(HvError::InvalidParameter, HvError) + err.hv_result() + .map_or(HvError::InvalidParameter, HvError::from) })?; unsafe { std::mem::transmute::( @@ -1863,7 +1864,10 @@ mod aarch64 { }; self.current_whp() .set_registers(&[reg], &[value]) - .map_err(|err| err.hv_result().map_or(HvError::InvalidParameter, HvError))?; + .map_err(|err| { + err.hv_result() + .map_or(HvError::InvalidParameter, HvError::from) + })?; Ok(()) } else { diff --git a/vmm_core/virt_whp/src/synic.rs b/vmm_core/virt_whp/src/synic.rs index 47570de053..88da55b38e 100644 --- a/vmm_core/virt_whp/src/synic.rs +++ b/vmm_core/virt_whp/src/synic.rs @@ -210,7 +210,7 @@ impl GuestEventPort for OffloadedGuestEventPortNoTrigger { vpref.ensure_vtl_runnable(vtl); } } - Err(err) => match err.hv_result().map(HvError) { + Err(err) => match err.hv_result().map(HvError::from) { Some(err @ HvError::InvalidSynicState) => { tracing::debug!( vp = vp.index(), diff --git a/vmm_core/virt_whp/src/vp.rs b/vmm_core/virt_whp/src/vp.rs index 6476f47e80..fc558093ca 100644 --- a/vmm_core/virt_whp/src/vp.rs +++ b/vmm_core/virt_whp/src/vp.rs @@ -234,7 +234,10 @@ impl<'a> WhpProcessor<'a> { self.vp .whp(vtl) .post_synic_message(sint, message.as_bytes()) - .map_err(|err| err.hv_result().map_or(HvError::InvalidParameter, HvError)) + .map_err(|err| { + err.hv_result() + .map_or(HvError::InvalidParameter, HvError::from) + }) } }