diff --git a/litebox_platform_lvbs/Cargo.toml b/litebox_platform_lvbs/Cargo.toml index e4dafb93e..6f0724927 100644 --- a/litebox_platform_lvbs/Cargo.toml +++ b/litebox_platform_lvbs/Cargo.toml @@ -20,6 +20,7 @@ rangemap = { version = "1.5.1", features = ["const_fn"] } thiserror = { version = "2.0.6", default-features = false } num_enum = { version = "0.7.3", default-features = false } once_cell = { version = "1.20.2", default-features = false, features = ["alloc", "race"] } +modular-bitfield = { version = "0.12.0", default-features = false } hashbrown = "0.15.2" elf = { version = "0.7.4", default-features = false } diff --git a/litebox_platform_lvbs/src/mshv/hvcall.rs b/litebox_platform_lvbs/src/mshv/hvcall.rs index 4c9c4e44c..20835b155 100644 --- a/litebox_platform_lvbs/src/mshv/hvcall.rs +++ b/litebox_platform_lvbs/src/mshv/hvcall.rs @@ -129,8 +129,8 @@ pub fn init() -> Result<(), HypervError> { debug_serial_println!("HV_X64_MSR_SIMP: {:#x}", rdmsr(HV_X64_MSR_SIMP)); let mut sint = HvSynicSint::new(); - sint.set_vector(u64::from(HYPERVISOR_CALLBACK_VECTOR)); - sint.set_auto_eoi(); + sint.set_vector(HYPERVISOR_CALLBACK_VECTOR); + sint.set_auto_eoi(true); wrmsr(HV_X64_MSR_SINT0, sint.as_uint64()); if get_core_id() == 0 { diff --git a/litebox_platform_lvbs/src/mshv/hvcall_mm.rs b/litebox_platform_lvbs/src/mshv/hvcall_mm.rs index 73defecfa..e40bf533e 100644 --- a/litebox_platform_lvbs/src/mshv/hvcall_mm.rs +++ b/litebox_platform_lvbs/src/mshv/hvcall_mm.rs @@ -28,7 +28,7 @@ pub fn hv_modify_vtl_protection_mask( hvin.partition_id = HV_PARTITION_ID_SELF; hvin.target_vtl = HvInputVtl::current(); - hvin.map_flags = page_access.bits(); + hvin.map_flags = u32::from(page_access.bits()); let mut total_protected: u64 = 0; while total_protected < num_pages { diff --git a/litebox_platform_lvbs/src/mshv/hvcall_vp.rs b/litebox_platform_lvbs/src/mshv/hvcall_vp.rs index 156d45170..d6da8b326 100644 --- a/litebox_platform_lvbs/src/mshv/hvcall_vp.rs +++ b/litebox_platform_lvbs/src/mshv/hvcall_vp.rs @@ -60,7 +60,7 @@ pub fn hvcall_set_vp_registers(reg_name: u32, value: u64) -> Result Result { - hvcall_set_vp_registers_internal(reg_name, value, HvInputVtl::new(HV_VTL_NORMAL)) + hvcall_set_vp_registers_internal(reg_name, value, HvInputVtl::new_for_vtl(HV_VTL_NORMAL)) } fn hvcall_get_vp_registers_internal( @@ -108,7 +108,7 @@ pub fn hvcall_get_vp_registers(reg_name: u32) -> Result { /// Hyper-V Hypercall to get VTL0's registers like MSR and control registers. #[inline] pub fn hvcall_get_vp_vtl0_registers(reg_name: u32) -> Result { - hvcall_get_vp_registers_internal(reg_name, HvInputVtl::new(HV_VTL_NORMAL)) + hvcall_get_vp_registers_internal(reg_name, HvInputVtl::new_for_vtl(HV_VTL_NORMAL)) } /// Populate the VP context for VTL1 @@ -142,22 +142,22 @@ fn hv_vtl_populate_vp_context(input: &mut HvEnableVpVtl, tss: u64, rip: u64, rsp // We only support 64-bit long mode for now, so most of the segment register fields are ignored. input.vp_context.cs.selector = SegmentSelector::new(1, PrivilegeLevel::Ring0).0; input.vp_context.cs.set_attributes( - SegmentRegisterAttributeFlags::ACCESSED.bits() - | SegmentRegisterAttributeFlags::WRITABLE.bits() - | SegmentRegisterAttributeFlags::EXECUTABLE.bits() - | SegmentRegisterAttributeFlags::USER_SEGMENT.bits() - | SegmentRegisterAttributeFlags::PRESENT.bits() - | SegmentRegisterAttributeFlags::AVAILABLE.bits() - | SegmentRegisterAttributeFlags::LONG_MODE.bits(), + SegmentRegisterAttributeFlags::ACCESSED + | SegmentRegisterAttributeFlags::WRITABLE + | SegmentRegisterAttributeFlags::EXECUTABLE + | SegmentRegisterAttributeFlags::USER_SEGMENT + | SegmentRegisterAttributeFlags::PRESENT + | SegmentRegisterAttributeFlags::AVAILABLE + | SegmentRegisterAttributeFlags::LONG_MODE, ); input.vp_context.ss.selector = SegmentSelector::new(2, PrivilegeLevel::Ring0).0; input.vp_context.ss.set_attributes( - SegmentRegisterAttributeFlags::ACCESSED.bits() - | SegmentRegisterAttributeFlags::WRITABLE.bits() - | SegmentRegisterAttributeFlags::USER_SEGMENT.bits() - | SegmentRegisterAttributeFlags::PRESENT.bits() - | SegmentRegisterAttributeFlags::AVAILABLE.bits(), + SegmentRegisterAttributeFlags::ACCESSED + | SegmentRegisterAttributeFlags::WRITABLE + | SegmentRegisterAttributeFlags::USER_SEGMENT + | SegmentRegisterAttributeFlags::PRESENT + | SegmentRegisterAttributeFlags::AVAILABLE, ); input.vp_context.tr.selector = SegmentSelector::new(3, PrivilegeLevel::Ring0).0; @@ -165,10 +165,10 @@ fn hv_vtl_populate_vp_context(input: &mut HvEnableVpVtl, tss: u64, rip: u64, rsp input.vp_context.tr.limit = u32::try_from(core::mem::size_of::()).unwrap() - 1; input.vp_context.tr.set_attributes( - SegmentRegisterAttributeFlags::ACCESSED.bits() - | SegmentRegisterAttributeFlags::WRITABLE.bits() - | SegmentRegisterAttributeFlags::EXECUTABLE.bits() - | SegmentRegisterAttributeFlags::PRESENT.bits(), + SegmentRegisterAttributeFlags::ACCESSED + | SegmentRegisterAttributeFlags::WRITABLE + | SegmentRegisterAttributeFlags::EXECUTABLE + | SegmentRegisterAttributeFlags::PRESENT, ); } diff --git a/litebox_platform_lvbs/src/mshv/mod.rs b/litebox_platform_lvbs/src/mshv/mod.rs index 6e26776bc..f0e3c0e86 100644 --- a/litebox_platform_lvbs/src/mshv/mod.rs +++ b/litebox_platform_lvbs/src/mshv/mod.rs @@ -11,6 +11,8 @@ pub mod vtl1_mem_layout; pub mod vtl_switch; use crate::mshv::vtl1_mem_layout::PAGE_SIZE; +use modular_bitfield::prelude::*; +use modular_bitfield::specifiers::{B3, B4, B7, B8, B16, B31, B32, B45, B51, B62}; use num_enum::{IntoPrimitive, TryFromPrimitive}; pub const HV_HYPERCALL_REP_COMP_MASK: u64 = 0xfff_0000_0000; @@ -124,7 +126,7 @@ pub const DEFAULT_REG_PIN_MASK: u64 = u64::MAX; bitflags::bitflags! { #[derive(Debug, PartialEq)] - pub struct HvPageProtFlags: u32 { + pub struct HvPageProtFlags: u8 { const HV_PAGE_ACCESS_NONE = 0x0; const HV_PAGE_READABLE = 0x1; const HV_PAGE_WRITABLE = 0x2; @@ -141,7 +143,7 @@ bitflags::bitflags! { } bitflags::bitflags! { - #[derive(Debug, PartialEq)] + #[derive(Debug, PartialEq, Clone, Copy, Default)] pub struct SegmentRegisterAttributeFlags: u16 { const ACCESSED = 1 << 0; const WRITABLE = 1 << 1; @@ -165,13 +167,7 @@ pub struct HvX64SegmentRegister { pub base: u64, pub limit: u32, pub selector: u16, - - _attributes: u16, - // union of - // segment_type: 4, non_system_segment: 1, - // descriptor_privilege_level: 2, present: 1, - // reserved: 4, available: 1, _long: 1, - // _default: 1, granularity: 1 + pub attributes: SegmentRegisterAttributeFlags, } impl HvX64SegmentRegister { @@ -182,9 +178,12 @@ impl HvX64SegmentRegister { } } - #[expect(clippy::used_underscore_binding)] - pub fn set_attributes(&mut self, attrs: u16) { - self._attributes = attrs; + pub fn set_attributes(&mut self, attrs: SegmentRegisterAttributeFlags) { + self.attributes = attrs; + } + + pub fn get_attributes(&self) -> SegmentRegisterAttributeFlags { + self.attributes } } @@ -230,49 +229,27 @@ pub struct HvInitVpContext { pub msr_cr_pat: u64, } -#[derive(Default, Clone, Copy)] -#[repr(C, packed)] +#[bitfield] +#[derive(Clone, Copy, Default)] +#[repr(C)] pub struct HvInputVtl { - _as_uint8: u8, - // union of - // target_vtl: 4, use_target_vtl: 1, - // reserved_z: 3 + pub target_vtl: B4, + pub use_target_vtl: bool, + #[skip] + __: B3, } impl HvInputVtl { - const TARGET_VTL_MASK: u8 = 0xf; - const USE_TARGET_VTL_MASK: u8 = 0x10; - const USE_TARGET_VTL_SHIFT: u8 = 4; - /// `target_vtl` specifies the VTL (0-15) that a Hyper-V hypercall works at. - pub fn new(target_vtl: u8) -> Self { - let mut vtl = HvInputVtl { _as_uint8: 0 }; - vtl.set_target_vtl(target_vtl); - vtl.set_use_target_vtl(true); - vtl + pub fn new_for_vtl(target_vtl: u8) -> Self { + Self::new() + .with_target_vtl(target_vtl) + .with_use_target_vtl(true) } /// use the current VTL pub fn current() -> Self { - let mut vtl = HvInputVtl { _as_uint8: 0 }; - vtl.set_use_target_vtl(false); - vtl - } - - #[expect(clippy::used_underscore_binding)] - pub fn set_target_vtl(&mut self, target_vtl: u8) { - self._as_uint8 |= target_vtl & Self::TARGET_VTL_MASK; - } - - /// set `use_target_vtl` to `true` to let a hypercall work at the target VTL specified by `set_target_vtl`. - /// set `use_target_vtl` to `false` to let a hypercall work at the current VTL. - #[expect(clippy::used_underscore_binding)] - pub fn set_use_target_vtl(&mut self, use_target_vtl: bool) { - if use_target_vtl { - self._as_uint8 |= (1 << Self::USE_TARGET_VTL_SHIFT) & Self::USE_TARGET_VTL_MASK; - } else { - self._as_uint8 &= !((1 << Self::USE_TARGET_VTL_SHIFT) & Self::USE_TARGET_VTL_MASK); - } + Self::new().with_use_target_vtl(false) } } @@ -391,44 +368,22 @@ impl HvGetVpRegistersOutput { } } -#[derive(Default, Clone, Copy)] -#[repr(C, packed)] +#[bitfield] +#[derive(Clone, Copy, Default)] +#[repr(C)] pub struct HvNestedEnlightenmentsControlFeatures { - _raw: u32, - // union of - // directhypercall: 1, reserved: 31 -} - -impl HvNestedEnlightenmentsControlFeatures { - const DIRECTHYPERCALL_MASK: u32 = 0x1; - pub fn new() -> Self { - HvNestedEnlightenmentsControlFeatures { _raw: 0 } - } - - #[expect(clippy::used_underscore_binding)] - pub fn set_direct_hypercall(&mut self, direct_hypercall: u32) { - self._raw |= direct_hypercall & Self::DIRECTHYPERCALL_MASK; - } + pub direct_hypercall: bool, + #[skip] + __: B31, } -#[derive(Default, Clone, Copy)] -#[repr(C, packed)] +#[bitfield] +#[derive(Clone, Copy, Default)] +#[repr(C)] pub struct HvNestedEnlightenmentsControlHypercallControls { - _raw: u32, - // union of - // inter_partition_comm: 1, reserved: 31 -} - -impl HvNestedEnlightenmentsControlHypercallControls { - const INTER_PARTITION_COMM_MASK: u32 = 0x1; - pub fn new() -> Self { - HvNestedEnlightenmentsControlHypercallControls { _raw: 0 } - } - - #[expect(clippy::used_underscore_binding)] - pub fn set_inter_partition_comm(&mut self, inter_partition_comm: u32) { - self._raw |= inter_partition_comm & Self::INTER_PARTITION_COMM_MASK; - } + pub inter_partition_comm: bool, + #[skip] + __: B31, } #[expect(non_snake_case)] @@ -527,177 +482,50 @@ impl Default for HvInputModifyVtlProtectionMask { } } -#[derive(Default, Clone, Copy)] -#[repr(C, packed)] +#[bitfield] +#[derive(Clone, Copy, Default)] +#[repr(C)] pub struct HvRegisterVsmVpSecureVtlConfig { - _as_u64: u64, - // union of - // mbec_enabled : 1; - // tlb_locked : 1; - // reserved: 62; + pub mbec_enabled: bool, + pub tlb_locked: bool, + #[skip] + __: B62, } impl HvRegisterVsmVpSecureVtlConfig { - const MBEC_ENABLED_MASK: u64 = 0x1; - const TLB_LOCKED_MASK: u64 = 0x2; - const MBEC_ENABLED_SHIFT: u64 = 0; - const TLB_LOCKED_SHIFT: u64 = 1; - - pub fn new() -> Self { - HvRegisterVsmVpSecureVtlConfig { - ..Default::default() - } - } - - #[expect(clippy::used_underscore_binding)] pub fn as_u64(&self) -> u64 { - self._as_u64 - } - - #[expect(clippy::used_underscore_binding)] - fn set_sub_config(&mut self, mask: u64, shift: u64, value: u64) { - self._as_u64 |= (value << shift) & mask; - } - - pub fn set_mbec_enabled(&mut self) { - self.set_sub_config(Self::MBEC_ENABLED_MASK, Self::MBEC_ENABLED_SHIFT, 1); - } - - pub fn set_tlb_locked(&mut self) { - self.set_sub_config(Self::TLB_LOCKED_MASK, Self::TLB_LOCKED_SHIFT, 1); + u64::from_le_bytes(self.into_bytes()) } } -#[derive(Default, Clone, Copy)] -#[repr(C, packed)] +#[bitfield] +#[derive(Clone, Copy, Default)] +#[repr(C)] pub struct HvRegisterVsmPartitionConfig { - _as_u64: u64, - // union of - // enable_vtl_protection : 1, - // default_vtl_protection_mask : 4, - // zero_memory_on_reset : 1, - // deny_lower_vtl_startup : 1, - // intercept_acceptance : 1, - // intercept_enable_vtl_protection : 1, - // intercept_vp_startup : 1, - // intercept_cpuid_unimplemented : 1, - // intercept_unrecoverable_exception : 1, - // intercept_page : 1, - // mbz : 51, + pub enable_vtl_protection: bool, + pub default_vtl_protection_mask: B4, + pub zero_memory_on_reset: bool, + pub deny_lower_vtl_startup: bool, + pub intercept_acceptance: bool, + pub intercept_enable_vtl_protection: bool, + pub intercept_vp_startup: bool, + pub intercept_cpuid_unimplemented: bool, + pub intercept_unrecoverable_exception: bool, + pub intercept_page: bool, + #[skip] + __: B51, } impl HvRegisterVsmPartitionConfig { - const ENABLE_VTL_PROTECTION_MASK: u64 = 0x1; - const DEFAULT_VTL_PROTECTION_MASK_MASK: u64 = 0x1e; - const ZERO_MEMORY_ON_RESET_MASK: u64 = 0x20; - const DENY_LOWER_VTL_STARTUP_MASK: u64 = 0x40; - const INTERCEPT_ACCEPTANCE_MASK: u64 = 0x80; - const INTERCEPT_ENABLE_VTL_PROTECTION_MASK: u64 = 0x100; - const INTERCEPT_VP_STARTUP_MASK: u64 = 0x200; - const INTERCEPT_CPUID_UNIMPLEMENTED_MASK: u64 = 0x400; - const INTERCEPT_UNRECOVERABLE_EXCEPTION_MASK: u64 = 0x800; - const INTERCEPT_PAGE_MASK: u64 = 0x1000; - const ENABLE_VTL_PROTECTION_SHIFT: u64 = 0; - const DEFAULT_VTL_PROTECTION_MASK_SHIFT: u64 = 1; - const ZERO_MEMORY_ON_RESET_SHIFT: u64 = 5; - const DENY_LOWER_VTL_STARTUP_SHIFT: u64 = 6; - const INTERCEPT_ACCEPTANCE_SHIFT: u64 = 7; - const INTERCEPT_ENABLE_VTL_PROTECTION_SHIFT: u64 = 8; - const INTERCEPT_VP_STARTUP_SHIFT: u64 = 9; - const INTERCEPT_CPUID_UNIMPLEMENTED_SHIFT: u64 = 10; - const INTERCEPT_UNRECOVERABLE_EXCEPTION_SHIFT: u64 = 11; - const INTERCEPT_PAGE_SHIFT: u64 = 12; - - pub fn new() -> Self { - HvRegisterVsmPartitionConfig { - ..Default::default() - } - } - - #[expect(clippy::used_underscore_binding)] + /// Get the raw u64 value for compatibility with existing code pub fn as_u64(&self) -> u64 { - self._as_u64 - } - - #[expect(clippy::used_underscore_binding)] - fn set_sub_config(&mut self, mask: u64, shift: u64, value: u64) { - self._as_u64 |= (value << shift) & mask; - } - - pub fn set_enable_vtl_protection(&mut self) { - self.set_sub_config( - Self::ENABLE_VTL_PROTECTION_MASK, - Self::ENABLE_VTL_PROTECTION_SHIFT, - 1, - ); - } - - pub fn set_default_vtl_protection_mask(&mut self, mask: u64) { - self.set_sub_config( - Self::DEFAULT_VTL_PROTECTION_MASK_MASK, - Self::DEFAULT_VTL_PROTECTION_MASK_SHIFT, - mask, - ); - } - - pub fn set_zero_memory_on_reset(&mut self) { - self.set_sub_config( - Self::ZERO_MEMORY_ON_RESET_MASK, - Self::ZERO_MEMORY_ON_RESET_SHIFT, - 1, - ); - } - - pub fn set_deny_lower_vtl_startup(&mut self) { - self.set_sub_config( - Self::DENY_LOWER_VTL_STARTUP_MASK, - Self::DENY_LOWER_VTL_STARTUP_SHIFT, - 1, - ); + // Convert the 8-byte array to u64 + u64::from_le_bytes(self.into_bytes()) } - pub fn set_intercept_acceptance(&mut self) { - self.set_sub_config( - Self::INTERCEPT_ACCEPTANCE_MASK, - Self::INTERCEPT_ACCEPTANCE_SHIFT, - 1, - ); - } - - pub fn set_intercept_enable_vtl_protection(&mut self) { - self.set_sub_config( - Self::INTERCEPT_ENABLE_VTL_PROTECTION_MASK, - Self::INTERCEPT_ENABLE_VTL_PROTECTION_SHIFT, - 1, - ); - } - - pub fn set_intercept_vp_startup(&mut self) { - self.set_sub_config( - Self::INTERCEPT_VP_STARTUP_MASK, - Self::INTERCEPT_VP_STARTUP_SHIFT, - 1, - ); - } - - pub fn set_intercept_cpuid_unimplemented(&mut self) { - self.set_sub_config( - Self::INTERCEPT_CPUID_UNIMPLEMENTED_MASK, - Self::INTERCEPT_CPUID_UNIMPLEMENTED_SHIFT, - 1, - ); - } - - pub fn set_intercept_unrecoverable_exception(&mut self) { - self.set_sub_config( - Self::INTERCEPT_UNRECOVERABLE_EXCEPTION_MASK, - Self::INTERCEPT_UNRECOVERABLE_EXCEPTION_SHIFT, - 1, - ); - } - - pub fn set_intercept_page(&mut self) { - self.set_sub_config(Self::INTERCEPT_PAGE_MASK, Self::INTERCEPT_PAGE_SHIFT, 1); + /// Create from a u64 value for compatibility with existing code + pub fn from_u64(value: u64) -> Self { + Self::from_bytes(value.to_le_bytes()) } } @@ -863,59 +691,23 @@ impl HvMessagePage { } } -#[derive(Default, Clone, Copy)] -#[repr(C, packed)] +#[bitfield] +#[derive(Clone, Copy, Default)] +#[repr(C)] pub struct HvSynicSint { - _as_uint64: u64, - // union of - // vector: 8; - // reserved1: 8; - // masked: 1; - // auto_eoi: 1; - // polling: 1; - // reserved2: 45; + pub vector: B8, + #[skip] + __reserved1: B8, + pub masked: bool, + pub auto_eoi: bool, + pub polling: bool, + #[skip] + __reserved2: B45, } impl HvSynicSint { - const VECTOR_MASK: u64 = 0xff; - const MASKED_MASK: u64 = 0x1_0000; - const AUTO_EOI_MASK: u64 = 0x2_0000; - const POLLING_MASK: u64 = 0x4_0000; - const VECTOR_SHIFT: u64 = 0; - const MASKED_SHIFT: u64 = 16; - const AUTO_EOI_SHIFT: u64 = 17; - const POLLING_SHIFT: u64 = 18; - - pub fn new() -> Self { - HvSynicSint { - ..Default::default() - } - } - - #[expect(clippy::used_underscore_binding)] pub fn as_uint64(&self) -> u64 { - self._as_uint64 - } - - #[expect(clippy::used_underscore_binding)] - fn set_sub_config(&mut self, mask: u64, shift: u64, value: u64) { - self._as_uint64 |= (value << shift) & mask; - } - - pub fn set_vector(&mut self, vector: u64) { - self.set_sub_config(Self::VECTOR_MASK, Self::VECTOR_SHIFT, vector); - } - - pub fn set_masked(&mut self) { - self.set_sub_config(Self::MASKED_MASK, Self::MASKED_SHIFT, 1); - } - - pub fn set_auto_eoi(&mut self) { - self.set_sub_config(Self::AUTO_EOI_MASK, Self::AUTO_EOI_SHIFT, 1); - } - - pub fn set_polling(&mut self) { - self.set_sub_config(Self::POLLING_MASK, Self::POLLING_SHIFT, 1); + u64::from_le_bytes(self.into_bytes()) } } @@ -990,69 +782,309 @@ pub struct HvMsrInterceptMessage { pub rax: u64, } -#[derive(Default, Clone, Copy)] -#[repr(C, packed)] +#[bitfield] +#[derive(Clone, Copy, Default)] +#[repr(C)] pub struct HvPendingExceptionEvent { - _as_u64: u64, - // union of - // event_pending: 1; - // event_type: 3; - // reserved_0: 4; - // deliver_error_code: 1; - // reserved_1: 7; - // vector: 16; - // error_code: 32; + pub event_pending: bool, + pub event_type: B3, + #[skip] + __reserved_0: B4, + pub deliver_error_code: bool, + #[skip] + __reserved_1: B7, + pub vector: B16, + pub error_code: B32, } impl HvPendingExceptionEvent { - const EVENT_PENDING_MASK: u64 = 0x1; - const EVENT_TYPE_MASK: u64 = 0xe; - const DELIVER_ERROR_CODE_MASK: u64 = 0x100; - const VECTOR_MASK: u64 = 0xffff_0000; - const ERROR_CODE_MASK: u64 = 0xffff_ffff_0000_0000; - const EVENT_PENDING_SHIFT: u64 = 0; - const EVENT_TYPE_SHIFT: u64 = 1; - const DELIVER_ERROR_CODE_SHIFT: u64 = 3; - const VECTOR_SHIFT: u64 = 16; - const ERROR_CODE_SHIFT: u64 = 32; - - pub fn new() -> Self { - HvPendingExceptionEvent { - ..Default::default() - } - } - - #[expect(clippy::used_underscore_binding)] pub fn as_u64(&self) -> u64 { - self._as_u64 + u64::from_le_bytes(self.into_bytes()) } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_hv_input_vtl_bitfield() { + // Test the new bitfield-based HvInputVtl implementation - #[expect(clippy::used_underscore_binding)] - fn set_sub_config(&mut self, mask: u64, shift: u64, value: u64) { - self._as_u64 |= (value << shift) & mask; + // Test new_for_vtl constructor + let vtl = HvInputVtl::new_for_vtl(5); + assert_eq!(vtl.target_vtl(), 5); + assert!(vtl.use_target_vtl()); + + // Test current constructor + let current_vtl = HvInputVtl::current(); + assert!(!current_vtl.use_target_vtl()); + + // Test individual field manipulation + let mut vtl = HvInputVtl::new(); + vtl.set_target_vtl(10_u8); + vtl.set_use_target_vtl(true); + assert_eq!(vtl.target_vtl(), 10); + assert!(vtl.use_target_vtl()); + + // Test size - should be 1 byte + assert_eq!(core::mem::size_of::(), 1); + + // Test that VTL values are properly bounded to 4 bits (0-15) + let vtl = HvInputVtl::new_for_vtl(15); + assert_eq!(vtl.target_vtl(), 15); + + // Test that Default trait works + let default_vtl = HvInputVtl::default(); + assert_eq!(default_vtl.target_vtl(), 0); + assert!(!default_vtl.use_target_vtl()); } - pub fn set_event_pending(&mut self) { - self.set_sub_config(Self::EVENT_PENDING_MASK, Self::EVENT_PENDING_SHIFT, 1); + #[test] + fn test_hv_register_vsm_partition_config_bitfield() { + // Test the new bitfield-based HvRegisterVsmPartitionConfig implementation + + let mut config = HvRegisterVsmPartitionConfig::new(); + + // Test individual boolean flags + config.set_enable_vtl_protection(true); + assert!(config.enable_vtl_protection()); + + config.set_zero_memory_on_reset(true); + assert!(config.zero_memory_on_reset()); + + config.set_intercept_page(true); + assert!(config.intercept_page()); + + // Test the 4-bit protection mask field + config.set_default_vtl_protection_mask(0b1010_u8); + assert_eq!(u64::from(config.default_vtl_protection_mask()), 0b1010); + + // Test size - should be 8 bytes (64 bits) + assert_eq!(core::mem::size_of::(), 8); + + // Test as_u64 and from_u64 round-trip + let original = config.as_u64(); + let restored = HvRegisterVsmPartitionConfig::from_u64(original); + + assert!(restored.enable_vtl_protection()); + assert!(restored.zero_memory_on_reset()); + assert!(restored.intercept_page()); + assert_eq!(u64::from(restored.default_vtl_protection_mask()), 0b1010); + + // Test that Default trait works + let default_config = HvRegisterVsmPartitionConfig::default(); + assert!(!default_config.enable_vtl_protection()); + assert_eq!(default_config.as_u64(), 0); + + // Test chaining builder-style methods (generated by bitfield macro) + let chained_config = HvRegisterVsmPartitionConfig::new() + .with_enable_vtl_protection(true) + .with_intercept_acceptance(true) + .with_intercept_vp_startup(true); + + assert!(chained_config.enable_vtl_protection()); + assert!(chained_config.intercept_acceptance()); + assert!(chained_config.intercept_vp_startup()); + assert!(!chained_config.zero_memory_on_reset()); } - pub fn set_event_type(&mut self, event_type: u64) { - self.set_sub_config(Self::EVENT_TYPE_MASK, Self::EVENT_TYPE_SHIFT, event_type); + #[test] + fn test_hv_nested_enlightenments_control_features_bitfield() { + // Test the new bitfield-based HvNestedEnlightenmentsControlFeatures implementation + + let mut features = HvNestedEnlightenmentsControlFeatures::new(); + + // Test setting direct hypercall flag + features.set_direct_hypercall(true); + assert!(features.direct_hypercall()); + + // Test direct method + let mut features2 = HvNestedEnlightenmentsControlFeatures::new(); + features2.set_direct_hypercall(true); + assert!(features2.direct_hypercall()); + + features2.set_direct_hypercall(false); + assert!(!features2.direct_hypercall()); + + // Test size - should be 4 bytes (32 bits) + assert_eq!( + core::mem::size_of::(), + 4 + ); + + // Test that Default trait works + let default_features = HvNestedEnlightenmentsControlFeatures::default(); + assert!(!default_features.direct_hypercall()); } - pub fn set_deliver_error_code(&mut self) { - self.set_sub_config( - Self::DELIVER_ERROR_CODE_MASK, - Self::DELIVER_ERROR_CODE_SHIFT, - 1, + #[test] + fn test_hv_nested_enlightenments_control_hypercall_controls_bitfield() { + // Test the new bitfield-based HvNestedEnlightenmentsControlHypercallControls implementation + + let mut controls = HvNestedEnlightenmentsControlHypercallControls::new(); + + // Test setting inter partition comm flag + controls.set_inter_partition_comm(true); + assert!(controls.inter_partition_comm()); + + // Test direct method + let mut controls2 = HvNestedEnlightenmentsControlHypercallControls::new(); + controls2.set_inter_partition_comm(true); + assert!(controls2.inter_partition_comm()); + + controls2.set_inter_partition_comm(false); + assert!(!controls2.inter_partition_comm()); + + // Test size - should be 4 bytes (32 bits) + assert_eq!( + core::mem::size_of::(), + 4 ); + + // Test that Default trait works + let default_controls = HvNestedEnlightenmentsControlHypercallControls::default(); + assert!(!default_controls.inter_partition_comm()); + } + + #[test] + fn test_hv_register_vsm_vp_secure_vtl_config_bitfield() { + // Test the new bitfield-based HvRegisterVsmVpSecureVtlConfig implementation + + let mut config = HvRegisterVsmVpSecureVtlConfig::new(); + + // Test individual boolean flags + config.set_mbec_enabled(true); + assert!(config.mbec_enabled()); + + config.set_tlb_locked(true); + assert!(config.tlb_locked()); + + // Test direct methods + let mut config2 = HvRegisterVsmVpSecureVtlConfig::new(); + config2.set_mbec_enabled(true); + assert!(config2.mbec_enabled()); + + config2.set_tlb_locked(true); + assert!(config2.tlb_locked()); + + // Test size - should be 8 bytes (64 bits) + assert_eq!(core::mem::size_of::(), 8); + + // Test as_u64 method + let config_u64 = config.as_u64(); + assert_ne!(config_u64, 0); // Should have some bits set + + // Test that Default trait works + let default_config = HvRegisterVsmVpSecureVtlConfig::default(); + assert!(!default_config.mbec_enabled()); + assert!(!default_config.tlb_locked()); + assert_eq!(default_config.as_u64(), 0); } - pub fn set_vector(&mut self, vector: u64) { - self.set_sub_config(Self::VECTOR_MASK, Self::VECTOR_SHIFT, vector); + #[test] + fn test_hv_synic_sint_bitfield() { + // Test the new bitfield-based HvSynicSint implementation + + let mut sint = HvSynicSint::new(); + + // Test vector field (8 bits) + sint.set_vector(0xf3_u8); + assert_eq!(sint.vector(), 0xf3); + + // Test boolean flags + sint.set_masked(true); + assert!(sint.masked()); + + sint.set_auto_eoi(true); + assert!(sint.auto_eoi()); + + sint.set_polling(true); + assert!(sint.polling()); + + // Test direct methods + let mut sint2 = HvSynicSint::new(); + sint2.set_vector(0xf3_u8); + assert_eq!(sint2.vector(), 0xf3); + + sint2.set_masked(true); + assert!(sint2.masked()); + + sint2.set_auto_eoi(true); + assert!(sint2.auto_eoi()); + + sint2.set_polling(true); + assert!(sint2.polling()); + + // Test size - should be 8 bytes (64 bits) + assert_eq!(core::mem::size_of::(), 8); + + // Test as_uint64 method + let sint_u64 = sint.as_uint64(); + assert_ne!(sint_u64, 0); // Should have some bits set + + // Test that Default trait works + let default_sint = HvSynicSint::default(); + assert_eq!(default_sint.vector(), 0); + assert!(!default_sint.masked()); + assert!(!default_sint.auto_eoi()); + assert!(!default_sint.polling()); } - pub fn set_error_code(&mut self, error_code: u64) { - self.set_sub_config(Self::ERROR_CODE_MASK, Self::ERROR_CODE_SHIFT, error_code); + #[test] + fn test_hv_pending_exception_event_bitfield() { + // Test the new bitfield-based HvPendingExceptionEvent implementation + + let mut exception = HvPendingExceptionEvent::new(); + + // Test boolean flags + exception.set_event_pending(true); + assert!(exception.event_pending()); + + exception.set_deliver_error_code(true); + assert!(exception.deliver_error_code()); + + // Test multi-bit fields + exception.set_event_type(0b101_u8); // 3 bits + assert_eq!(exception.event_type(), 0b101); + + exception.set_vector(0x1234_u16); // 16 bits + assert_eq!(exception.vector(), 0x1234); + + exception.set_error_code(0x87654321_u32); // 32 bits + assert_eq!(exception.error_code(), 0x87654321); + + // Test direct methods + let mut exception2 = HvPendingExceptionEvent::new(); + exception2.set_event_pending(true); + assert!(exception2.event_pending()); + + exception2.set_deliver_error_code(true); + assert!(exception2.deliver_error_code()); + + exception2.set_event_type(7_u8); + assert_eq!(exception2.event_type(), 7); + + exception2.set_vector(0xabcd_u16); + assert_eq!(exception2.vector(), 0xabcd); + + exception2.set_error_code(0x12345678_u32); + assert_eq!(exception2.error_code(), 0x12345678); + + // Test size - should be 8 bytes (64 bits) + assert_eq!(core::mem::size_of::(), 8); + + // Test as_u64 method + let exception_u64 = exception.as_u64(); + assert_ne!(exception_u64, 0); // Should have some bits set + + // Test that Default trait works + let default_exception = HvPendingExceptionEvent::default(); + assert!(!default_exception.event_pending()); + assert!(!default_exception.deliver_error_code()); + assert_eq!(default_exception.event_type(), 0); + assert_eq!(default_exception.vector(), 0); + assert_eq!(default_exception.error_code(), 0); + assert_eq!(default_exception.as_u64(), 0); } } diff --git a/litebox_platform_lvbs/src/mshv/vsm.rs b/litebox_platform_lvbs/src/mshv/vsm.rs index 1d5ee8a0e..f228ac9f8 100644 --- a/litebox_platform_lvbs/src/mshv/vsm.rs +++ b/litebox_platform_lvbs/src/mshv/vsm.rs @@ -177,8 +177,8 @@ pub fn mshv_vsm_secure_config_vtl0() -> Result { debug_serial_println!("VSM: Secure VTL0 configuration"); let mut config = HvRegisterVsmVpSecureVtlConfig::new(); - config.set_mbec_enabled(); - config.set_tlb_locked(); + config.set_mbec_enabled(true); + config.set_tlb_locked(true); hvcall_set_vp_registers(HV_REGISTER_VSM_VP_SECURE_CONFIG_VTL0, config.as_u64()) .map_err(|_| Errno::EFAULT)?; @@ -191,8 +191,8 @@ pub fn mshv_vsm_configure_partition() -> Result { debug_serial_println!("VSM: Configure partition"); let mut config = HvRegisterVsmPartitionConfig::new(); - config.set_default_vtl_protection_mask(HvPageProtFlags::HV_PAGE_FULL_ACCESS.bits().into()); - config.set_enable_vtl_protection(); + config.set_default_vtl_protection_mask(HvPageProtFlags::HV_PAGE_FULL_ACCESS.bits()); + config.set_enable_vtl_protection(true); hvcall_set_vp_registers(HV_REGISTER_VSM_PARTITION_CONFIG, config.as_u64()) .map_err(|_| Errno::EFAULT)?; diff --git a/litebox_platform_lvbs/src/mshv/vsm_intercept.rs b/litebox_platform_lvbs/src/mshv/vsm_intercept.rs index 6211dd8d7..c339d55c2 100644 --- a/litebox_platform_lvbs/src/mshv/vsm_intercept.rs +++ b/litebox_platform_lvbs/src/mshv/vsm_intercept.rs @@ -147,13 +147,13 @@ fn advance_vtl0_rip(int_msg_hdr: &HvInterceptMessageHeader) -> Result Result { let mut exception = HvPendingExceptionEvent::new(); - exception.set_event_pending(); - exception.set_event_type(0); - exception.set_deliver_error_code(); - exception.set_vector(u64::from( + exception.set_event_pending(true); + exception.set_event_type(0_u8); + exception.set_deliver_error_code(true); + exception.set_vector(u16::from( x86_64::structures::idt::ExceptionVector::GeneralProtection as u8, )); - exception.set_error_code(0); + exception.set_error_code(0_u32); hvcall_set_vp_vtl0_registers(HV_REGISTER_PENDING_EVENT0, exception.as_u64()) }