1212//! devices IO windows, and finally set resources to virtual device. 
1313
1414use  crate :: resources:: Resource ; 
15- use  crate :: DeviceIo ; 
15+ use  crate :: { DeviceIo ,   IoAddress ,   IoSize } ; 
1616
17+ use  std:: cmp:: { Ord ,  Ordering ,  PartialEq ,  PartialOrd } ; 
1718use  std:: collections:: btree_map:: BTreeMap ; 
1819use  std:: result; 
1920use  std:: sync:: Arc ; 
2021
22+ use  vm_memory:: GuestAddress ; 
23+ 
2124/// Error type for `IoManager` usage. 
2225#[ derive( Debug ) ]  
2326pub  enum  Error  { 
2427    /// The inserting device overlaps with a current device. 
2528DeviceOverlap , 
29+     /// The device doesn't exist. 
30+ DeviceNotExist , 
2631} 
2732
2833/// Simplify the `Result` type. 
2934pub  type  Result < T >  = result:: Result < T ,  Error > ; 
3035
36+ // Structure describing an IO range. 
37+ #[ derive( Debug ,  Copy ,  Clone ) ]  
38+ struct  IoRange  { 
39+     base :  IoAddress , 
40+     size :  IoSize , 
41+ } 
42+ 
43+ impl  IoRange  { 
44+     fn  new_pio_range ( base :  u16 ,  size :  u16 )  -> Self  { 
45+         IoRange  { 
46+             base :  IoAddress :: Pio ( base) , 
47+             size :  IoSize :: Pio ( size) , 
48+         } 
49+     } 
50+     fn  new_mmio_range ( base :  u64 ,  size :  u64 )  -> Self  { 
51+         IoRange  { 
52+             base :  IoAddress :: Mmio ( GuestAddress ( base) ) , 
53+             size :  IoSize :: Mmio ( size) , 
54+         } 
55+     } 
56+ } 
57+ 
58+ impl  Eq  for  IoRange  { } 
59+ 
60+ impl  PartialEq  for  IoRange  { 
61+     fn  eq ( & self ,  other :  & IoRange )  -> bool  { 
62+         self . base  == other. base 
63+     } 
64+ } 
65+ 
66+ impl  Ord  for  IoRange  { 
67+     fn  cmp ( & self ,  other :  & IoRange )  -> Ordering  { 
68+         self . base . cmp ( & other. base ) 
69+     } 
70+ } 
71+ 
72+ impl  PartialOrd  for  IoRange  { 
73+     fn  partial_cmp ( & self ,  other :  & IoRange )  -> Option < Ordering >  { 
74+         self . base . partial_cmp ( & other. base ) 
75+     } 
76+ } 
77+ 
3178/// System IO manager serving for all devices management and VM exit handling. 
3279#[ derive( Default ) ]  
3380pub  struct  IoManager  { 
3481    /// Range mapping for VM exit pio operations. 
35- pio_bus :  BTreeMap < ( u16 ,   u16 ) ,  Arc < dyn  DeviceIo > > , 
82+ pio_bus :  BTreeMap < IoRange ,  Arc < dyn  DeviceIo > > , 
3683    /// Range mapping for VM exit mmio operations. 
37- mmio_bus :  BTreeMap < ( u64 ,   u64 ) ,  Arc < dyn  DeviceIo > > , 
84+ mmio_bus :  BTreeMap < IoRange ,  Arc < dyn  DeviceIo > > , 
3885} 
3986
4087impl  IoManager  { 
@@ -59,15 +106,23 @@ impl IoManager {
59106        for  ( idx,  res)  in  resources. iter ( ) . enumerate ( )  { 
60107            match  * res { 
61108                Resource :: PioAddressRange  {  base,  size }  => { 
62-                     if  self . pio_bus . insert ( ( base,  size) ,  device. clone ( ) ) . is_some ( )  { 
109+                     if  self 
110+                         . pio_bus 
111+                         . insert ( IoRange :: new_pio_range ( base,  size) ,  device. clone ( ) ) 
112+                         . is_some ( ) 
113+                     { 
63114                        // Unregister registered resources. 
64115                        self . unregister_device_io ( & resources[ 0 ..idx] ) ?; 
65116
66117                        return  Err ( Error :: DeviceOverlap ) ; 
67118                    } 
68119                } 
69120                Resource :: MmioAddressRange  {  base,  size }  => { 
70-                     if  self . mmio_bus . insert ( ( base,  size) ,  device. clone ( ) ) . is_some ( )  { 
121+                     if  self 
122+                         . mmio_bus 
123+                         . insert ( IoRange :: new_mmio_range ( base,  size) ,  device. clone ( ) ) 
124+                         . is_some ( ) 
125+                     { 
71126                        // Unregister registered resources. 
72127                        self . unregister_device_io ( & resources[ 0 ..idx] ) ?; 
73128
@@ -92,14 +147,92 @@ impl IoManager {
92147        for  res in  resources. iter ( )  { 
93148            match  * res { 
94149                Resource :: PioAddressRange  {  base,  size }  => { 
95-                     self . pio_bus . remove ( & ( base,  size) ) ; 
150+                     self . pio_bus . remove ( & IoRange :: new_pio_range ( base,  size) ) ; 
96151                } 
97152                Resource :: MmioAddressRange  {  base,  size }  => { 
98-                     self . mmio_bus . remove ( & ( base,  size) ) ; 
153+                     self . mmio_bus . remove ( & IoRange :: new_mmio_range ( base,  size) ) ; 
99154                } 
100155                _ => continue , 
101156            } 
102157        } 
103158        Ok ( ( ) ) 
104159    } 
160+ 
161+     // BTreeMap::range() yields elements from min(inclusive) to max(exclusive). 
162+     // Let's use a fake range for getting inclusive result. 
163+     fn  get_entry ( & self ,  addr :  IoAddress )  -> Option < ( & IoRange ,  & Arc < dyn  DeviceIo > ) >  { 
164+         match  addr { 
165+             IoAddress :: Pio ( a)  => self 
166+                 . pio_bus 
167+                 . range ( ..& IoRange :: new_pio_range ( a + 1 ,  0 ) ) 
168+                 . nth_back ( 0 ) , 
169+             IoAddress :: Mmio ( GuestAddress ( a) )  => self 
170+                 . mmio_bus 
171+                 . range ( ..& IoRange :: new_mmio_range ( a + 1 ,  0 ) ) 
172+                 . nth_back ( 0 ) , 
173+         } 
174+     } 
175+ 
176+     // Return the Device mapped the address. 
177+     fn  get_device ( & self ,  addr :  IoAddress )  -> Option < Arc < dyn  DeviceIo > >  { 
178+         if  let  Some ( ( range,  dev) )  = self . get_entry ( addr)  { 
179+             if  ( addr. raw_value ( )  - range. base . raw_value ( ) )  < range. size . raw_value ( )  { 
180+                 return  Some ( dev. clone ( ) ) ; 
181+             } 
182+             return  None ; 
183+         } 
184+         None 
185+     } 
186+ 
187+     /// A helper function handling PIO read command during VM exit. 
188+ /// The virtual device itself provides mutable ability and thead-safe protection. 
189+ /// 
190+ /// Return error if failed to get the device. 
191+ pub  fn  pio_read ( & self ,  addr :  u16 ,  data :  & mut  [ u8 ] )  -> Result < ( ) >  { 
192+         if  let  Some ( device)  = self . get_device ( IoAddress :: Pio ( addr) )  { 
193+             device. read ( IoAddress :: Pio ( addr) ,  data) ; 
194+             Ok ( ( ) ) 
195+         }  else  { 
196+             Err ( Error :: DeviceNotExist ) 
197+         } 
198+     } 
199+ 
200+     /// A helper function handling PIO write command during VM exit. 
201+ /// The virtual device itself provides mutable ability and thead-safe protection. 
202+ /// 
203+ /// Return error if failed to get the device. 
204+ pub  fn  pio_write ( & self ,  addr :  u16 ,  data :  & [ u8 ] )  -> Result < ( ) >  { 
205+         if  let  Some ( device)  = self . get_device ( IoAddress :: Pio ( addr) )  { 
206+             device. write ( IoAddress :: Pio ( addr) ,  data) ; 
207+             Ok ( ( ) ) 
208+         }  else  { 
209+             Err ( Error :: DeviceNotExist ) 
210+         } 
211+     } 
212+ 
213+     /// A helper function handling MMIO read command during VM exit. 
214+ /// The virtual device itself provides mutable ability and thead-safe protection. 
215+ /// 
216+ /// Return error if failed to get the device. 
217+ pub  fn  mmio_read ( & self ,  addr :  u64 ,  data :  & mut  [ u8 ] )  -> Result < ( ) >  { 
218+         if  let  Some ( device)  = self . get_device ( IoAddress :: Mmio ( GuestAddress ( addr) ) )  { 
219+             device. read ( IoAddress :: Mmio ( GuestAddress ( addr) ) ,  data) ; 
220+             Ok ( ( ) ) 
221+         }  else  { 
222+             Err ( Error :: DeviceNotExist ) 
223+         } 
224+     } 
225+ 
226+     /// A helper function handling MMIO write command during VM exit. 
227+ /// The virtual device itself provides mutable ability and thead-safe protection. 
228+ /// 
229+ /// Return error if failed to get the device. 
230+ pub  fn  mmio_write ( & self ,  addr :  u64 ,  data :  & [ u8 ] )  -> Result < ( ) >  { 
231+         if  let  Some ( device)  = self . get_device ( IoAddress :: Mmio ( GuestAddress ( addr) ) )  { 
232+             device. write ( IoAddress :: Mmio ( GuestAddress ( addr) ) ,  data) ; 
233+             Ok ( ( ) ) 
234+         }  else  { 
235+             Err ( Error :: DeviceNotExist ) 
236+         } 
237+     } 
105238} 
0 commit comments