1212//! devices IO ranges, 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.
2528 DeviceOverlap ,
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 {
@@ -60,7 +107,11 @@ impl IoManager {
60107 for ( idx, res) in resources. iter ( ) . enumerate ( ) {
61108 match * res {
62109 Resource :: PioAddressRange { base, size } => {
63- if self . pio_bus . insert ( ( base, size) , device. clone ( ) ) . is_some ( ) {
110+ if self
111+ . pio_bus
112+ . insert ( IoRange :: new_pio_range ( base, size) , device. clone ( ) )
113+ . is_some ( )
114+ {
64115 // Unregister registered resources.
65116 self . unregister_device_io ( & resources[ 0 ..idx] )
66117 . expect ( "failed to unregister devices" ) ;
@@ -69,7 +120,11 @@ impl IoManager {
69120 }
70121 }
71122 Resource :: MmioAddressRange { base, size } => {
72- if self . mmio_bus . insert ( ( base, size) , device. clone ( ) ) . is_some ( ) {
123+ if self
124+ . mmio_bus
125+ . insert ( IoRange :: new_mmio_range ( base, size) , device. clone ( ) )
126+ . is_some ( )
127+ {
73128 // Unregister registered resources.
74129 self . unregister_device_io ( & resources[ 0 ..idx] )
75130 . expect ( "failed to unregister devices" ) ;
@@ -95,14 +150,89 @@ impl IoManager {
95150 for res in resources. iter ( ) {
96151 match * res {
97152 Resource :: PioAddressRange { base, size } => {
98- self . pio_bus . remove ( & ( base, size) ) ;
153+ self . pio_bus . remove ( & IoRange :: new_pio_range ( base, size) ) ;
99154 }
100155 Resource :: MmioAddressRange { base, size } => {
101- self . mmio_bus . remove ( & ( base, size) ) ;
156+ self . mmio_bus . remove ( & IoRange :: new_mmio_range ( base, size) ) ;
102157 }
103158 _ => continue ,
104159 }
105160 }
106161 Ok ( ( ) )
107162 }
163+
164+ fn get_entry ( & self , addr : IoAddress ) -> Option < ( & IoRange , & Arc < dyn DeviceIo > ) > {
165+ match addr {
166+ IoAddress :: Pio ( a) => self
167+ . pio_bus
168+ . range ( ..=& IoRange :: new_pio_range ( a, 0 ) )
169+ . nth_back ( 0 ) ,
170+ IoAddress :: Mmio ( GuestAddress ( a) ) => self
171+ . mmio_bus
172+ . range ( ..=& IoRange :: new_mmio_range ( a, 0 ) )
173+ . nth_back ( 0 ) ,
174+ }
175+ }
176+
177+ // Return the Device mapped the address.
178+ fn get_device ( & self , addr : IoAddress ) -> Option < & Arc < dyn DeviceIo > > {
179+ if let Some ( ( range, dev) ) = self . get_entry ( addr) {
180+ if ( addr. raw_value ( ) - range. base . raw_value ( ) ) < range. size . raw_value ( ) {
181+ return Some ( dev) ;
182+ }
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+ }
108238}
0 commit comments