77// Use of this source code is governed by a BSD-style license that can be
88// found in the THIRD-PARTY file.
99
10+ #[ cfg( target_arch = "x86_64" ) ]
11+ use bitflags:: bitflags;
1012use kvm_bindings:: * ;
1113use std:: fs:: File ;
1214use std:: os:: raw:: c_void;
@@ -54,6 +56,48 @@ impl From<NoDatamatch> for u64 {
5456 }
5557}
5658
59+ /// Helper structure describing one MSR filter range consumed by
60+ /// [`VmFd::set_msr_filter`].
61+ #[ cfg( target_arch = "x86_64" ) ]
62+ #[ derive( Clone , Copy , Debug , Eq , PartialEq ) ]
63+ pub struct MsrFilterRange < ' a > {
64+ /// Flags specifying which MSR operations are filtered in this range.
65+ pub flags : MsrFilterRangeFlags ,
66+ /// Base MSR index of the range.
67+ pub base : u32 ,
68+ /// Number of MSRs in the range.
69+ pub msr_count : u32 ,
70+ /// Bitmap specifying allowed operations for each MSR in the range.
71+ pub bitmap : & ' a [ u8 ] ,
72+ }
73+
74+ #[ cfg( target_arch = "x86_64" ) ]
75+ bitflags ! {
76+ /// Flags selecting which MSR operations are filtered for a range.
77+ #[ derive( Clone , Copy , Debug , Eq , PartialEq ) ]
78+ pub struct MsrFilterRangeFlags : u32 {
79+ /// Filter read accesses to MSRs using the given bitmap.
80+ /// A 0 in the bitmap indicates that read accesses should be denied,
81+ /// while a 1 indicates that a read for a particular MSR should be allowed regardless of the default filter action.
82+ const READ = KVM_MSR_FILTER_READ ;
83+ /// Filter write accesses to MSRs using the given bitmap.
84+ /// A 0 in the bitmap indicates that write accesses should be denied,
85+ /// while a 1 indicates that a write for a particular MSR should be allowed regardless of the default filter action.
86+ const WRITE = KVM_MSR_FILTER_WRITE ;
87+ }
88+ }
89+
90+ /// Default action for MSR filtering.
91+ #[ cfg( target_arch = "x86_64" ) ]
92+ #[ repr( u32 ) ]
93+ #[ derive( Clone , Copy , Debug , Eq , PartialEq ) ]
94+ pub enum MsrFilterDefaultAction {
95+ /// If no filter range matches an MSR index that is getting accessed, KVM will allow accesses to all MSRs by default.
96+ ALLOW = KVM_MSR_FILTER_DEFAULT_ALLOW ,
97+ /// If no filter range matches an MSR index that is getting accessed, KVM will deny accesses to all MSRs by default.
98+ DENY = KVM_MSR_FILTER_DEFAULT_DENY ,
99+ }
100+
57101/// Wrapper over KVM VM ioctls.
58102#[ derive( Debug ) ]
59103pub struct VmFd {
@@ -544,23 +588,21 @@ impl VmFd {
544588 ///
545589 /// # Safety
546590 ///
547- /// The caller must ensure that the given `kvm_msr_filter` is valid. Specifically any `bitmap` pointers in the `kvm_msr_filter_range`
548- /// structures within `filter.ranges` must point to valid memory of sufficient size.
591+ /// This is the unsafe version of [`VmFd::set_msr_filter`]. The caller must ensure that the given `kvm_msr_filter` is valid.
592+ /// Specifically any `bitmap` pointers in the `kvm_msr_filter_range` structures within `filter.ranges` must point to valid memory of sufficient size.
549593 /// # Example
550594 ///
551595 /// ```rust
552- /// # extern crate kvm_bindings;
553- /// # extern crate kvm_ioctls;
554596 /// # use kvm_bindings::kvm_msr_filter;
555597 /// # use kvm_ioctls::Kvm;
556598 /// let kvm = Kvm::new().unwrap();
557599 /// let vm = kvm.create_vm().unwrap();
558- /// let mut filter = kvm_msr_filter::default();
600+ /// let filter = kvm_msr_filter::default();
559601 /// // Safety: filter is valid
560- /// unsafe { vm.set_msr_filter(&mut filter).unwrap() };
602+ /// unsafe { vm.set_msr_filter_unchecked(& filter).unwrap() };
561603 /// ```
562604 #[ cfg( target_arch = "x86_64" ) ]
563- pub unsafe fn set_msr_filter ( & self , filter : & kvm_msr_filter ) -> Result < ( ) > {
605+ pub unsafe fn set_msr_filter_unchecked ( & self , filter : & kvm_msr_filter ) -> Result < ( ) > {
564606 // SAFETY: Safe because we call this with a Vm fd and we trust the kernel, and the caller
565607 // has promised validity of the filter structure.
566608 let ret = unsafe { ioctl_with_ref ( self , KVM_SET_MSR_FILTER ( ) , filter) } ;
@@ -571,6 +613,68 @@ impl VmFd {
571613 }
572614 }
573615
616+ /// Sets the MSR filter as per the `KVM_X86_SET_MSR_FILTER` ioctl.
617+ ///
618+ /// See the documentation for `KVM_X86_SET_MSR_FILTER` in the
619+ /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt).
620+ ///
621+ /// # Arguments
622+ ///
623+ /// * `filter` - MSR filter configuration to be set.
624+ ///
625+ /// # Example
626+ ///
627+ /// ```rust
628+ /// # use kvm_ioctls::Kvm;
629+ /// # use kvm_ioctls::{
630+ /// # MsrFilterDefaultAction,
631+ /// # MsrFilterRange,
632+ /// # MsrFilterRangeFlags,
633+ /// # };
634+ /// let kvm = Kvm::new().unwrap();
635+ /// let vm = kvm.create_vm().unwrap();
636+ /// let mut bitmap = [0xffu8; 4];
637+ /// let mut range = MsrFilterRange {
638+ /// flags: MsrFilterRangeFlags::READ | MsrFilterRangeFlags::WRITE,
639+ /// base: 0,
640+ /// msr_count: 32,
641+ /// bitmap: &bitmap,
642+ /// };
643+ /// vm.set_msr_filter(MsrFilterDefaultAction::DENY, &[range])
644+ /// .unwrap();
645+ /// ```
646+ #[ cfg( target_arch = "x86_64" ) ]
647+ pub fn set_msr_filter (
648+ & self ,
649+ default_action : MsrFilterDefaultAction ,
650+ ranges : & [ MsrFilterRange < ' _ > ] ,
651+ ) -> Result < ( ) > {
652+ if ranges. len ( ) > KVM_MSR_FILTER_MAX_RANGES as usize {
653+ return Err ( errno:: Error :: new ( libc:: EINVAL ) ) ;
654+ }
655+
656+ let mut raw_filter = kvm_msr_filter {
657+ flags : default_action as u32 ,
658+ ..Default :: default ( )
659+ } ;
660+
661+ for ( dst, src) in raw_filter. ranges . iter_mut ( ) . zip ( ranges. iter ( ) ) {
662+ // Validate that the provided bitmap is large enough to hold the specified number of MSRs.
663+ let required_bytes = src. msr_count . div_ceil ( 8 ) as usize ;
664+ if src. bitmap . len ( ) < required_bytes {
665+ return Err ( errno:: Error :: new ( libc:: EINVAL ) ) ;
666+ }
667+
668+ dst. flags = src. flags . bits ( ) ;
669+ dst. nmsrs = src. msr_count ;
670+ dst. base = src. base ;
671+ dst. bitmap = src. bitmap . as_ptr ( ) as * mut u8 ; // TODO: is this cast ok? kvm_msr_filter_range.bitmap is __*mut__ u8. Ideally I don't want to require input parameters to be mutable unless it's necessary
672+ }
673+
674+ // SAFETY: We checked the length of all bitmaps above.
675+ unsafe { self . set_msr_filter_unchecked ( & raw_filter) }
676+ }
677+
574678 /// Directly injects a MSI message as per the `KVM_SIGNAL_MSI` ioctl.
575679 ///
576680 /// See the documentation for `KVM_SIGNAL_MSI`.
@@ -2941,20 +3045,20 @@ mod tests {
29413045
29423046 #[ test]
29433047 #[ cfg( target_arch = "x86_64" ) ]
2944- fn test_set_msr_filter ( ) {
3048+ fn test_set_msr_filter_unchecked ( ) {
29453049 let kvm = Kvm :: new ( ) . unwrap ( ) ;
2946- let vm = kvm. create_vm ( ) . unwrap ( ) ;
2947-
29483050 if !kvm. check_extension ( Cap :: X86MsrFilter ) {
29493051 return ;
29503052 }
29513053
3054+ let vm = kvm. create_vm ( ) . unwrap ( ) ;
3055+
29523056 let empty_filter = kvm_msr_filter {
29533057 flags : KVM_MSR_FILTER_DEFAULT_ALLOW ,
29543058 ranges : [ kvm_msr_filter_range:: default ( ) ; 16 ] ,
29553059 } ;
29563060 // Safety: empty_filter is valid
2957- unsafe { vm. set_msr_filter ( & empty_filter) . unwrap ( ) } ;
3061+ unsafe { vm. set_msr_filter_unchecked ( & empty_filter) . unwrap ( ) } ;
29583062
29593063 // From KVM API:
29603064 // Calling this ioctl with an empty set of ranges (all nmsrs == 0) disables MSR filtering. In that mode, KVM_MSR_FILTER_DEFAULT_DENY is invalid and causes an error.
@@ -2963,7 +3067,7 @@ mod tests {
29633067 ranges : [ kvm_msr_filter_range:: default ( ) ; 16 ] ,
29643068 } ;
29653069 // Safety: empty_deny_filter is invalid
2966- unsafe { vm. set_msr_filter ( & empty_deny_filter) . unwrap_err ( ) } ;
3070+ unsafe { vm. set_msr_filter_unchecked ( & empty_deny_filter) . unwrap_err ( ) } ;
29673071
29683072 // disable access to all except 1 MSR
29693073 let mut filter = kvm_msr_filter {
@@ -2976,6 +3080,45 @@ mod tests {
29763080 filter. ranges [ 0 ] . nmsrs = 1 ;
29773081 filter. ranges [ 0 ] . bitmap = & mut bitmap;
29783082 // Safety: bitmap is valid 8 bits, and nmsrs is 1
2979- unsafe { vm. set_msr_filter ( & filter) . unwrap ( ) } ;
3083+ unsafe { vm. set_msr_filter_unchecked ( & filter) . unwrap ( ) } ;
3084+ }
3085+
3086+ #[ test]
3087+ #[ cfg( target_arch = "x86_64" ) ]
3088+ fn test_set_msr_filter_safe ( ) {
3089+ let kvm = Kvm :: new ( ) . unwrap ( ) ;
3090+ if !kvm. check_extension ( Cap :: X86MsrFilter ) {
3091+ return ;
3092+ }
3093+
3094+ let vm = kvm. create_vm ( ) . unwrap ( ) ;
3095+
3096+ // Empty ranges with the default allow action disable filtering.
3097+ vm. set_msr_filter ( MsrFilterDefaultAction :: ALLOW , & [ ] )
3098+ . unwrap ( ) ;
3099+
3100+ // Empty ranges with default deny should surface an error from KVM.
3101+ vm. set_msr_filter ( MsrFilterDefaultAction :: DENY , & [ ] )
3102+ . unwrap_err ( ) ;
3103+
3104+ // A bitmap that is too short for the requested range is rejected locally.
3105+ let bad_range = MsrFilterRange {
3106+ flags : MsrFilterRangeFlags :: READ ,
3107+ base : 0 ,
3108+ msr_count : 9 ,
3109+ bitmap : & [ 0xff ] ,
3110+ } ;
3111+ vm. set_msr_filter ( MsrFilterDefaultAction :: ALLOW , & [ bad_range] )
3112+ . unwrap_err ( ) ;
3113+
3114+ // Valid deny-list that allows a single MSR succeeds.
3115+ let allow_range = MsrFilterRange {
3116+ flags : MsrFilterRangeFlags :: READ | MsrFilterRangeFlags :: WRITE ,
3117+ base : 0x10 ,
3118+ msr_count : 1 ,
3119+ bitmap : & [ 0x1 ] ,
3120+ } ;
3121+ vm. set_msr_filter ( MsrFilterDefaultAction :: DENY , & [ allow_range] )
3122+ . unwrap ( ) ;
29803123 }
29813124}
0 commit comments