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,15 +107,23 @@ 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
67118 return Err ( Error :: DeviceOverlap ) ;
68119 }
69120 }
70121 Resource :: MmioAddressRange { base, size } => {
71- if self . mmio_bus . insert ( ( base, size) , device. clone ( ) ) . is_some ( ) {
122+ if self
123+ . mmio_bus
124+ . insert ( IoRange :: new_mmio_range ( base, size) , device. clone ( ) )
125+ . is_some ( )
126+ {
72127 // Unregister registered resources.
73128 self . unregister_device_io ( & resources[ 0 ..idx] ) ?;
74129
@@ -93,14 +148,90 @@ impl IoManager {
93148 for res in resources. iter ( ) {
94149 match * res {
95150 Resource :: PioAddressRange { base, size } => {
96- self . pio_bus . remove ( & ( base, size) ) ;
151+ self . pio_bus . remove ( & IoRange :: new_pio_range ( base, size) ) ;
97152 }
98153 Resource :: MmioAddressRange { base, size } => {
99- self . mmio_bus . remove ( & ( base, size) ) ;
154+ self . mmio_bus . remove ( & IoRange :: new_mmio_range ( base, size) ) ;
100155 }
101156 _ => continue ,
102157 }
103158 }
104159 Ok ( ( ) )
105160 }
161+
162+ fn get_entry ( & self , addr : IoAddress ) -> Option < ( & IoRange , & Arc < dyn DeviceIo > ) > {
163+ match addr {
164+ IoAddress :: Pio ( a) => self
165+ . pio_bus
166+ . range ( ..=& IoRange :: new_pio_range ( a, 0 ) )
167+ . nth_back ( 0 ) ,
168+ IoAddress :: Mmio ( GuestAddress ( a) ) => self
169+ . mmio_bus
170+ . range ( ..=& IoRange :: new_mmio_range ( a, 0 ) )
171+ . nth_back ( 0 ) ,
172+ }
173+ }
174+
175+ // Return the Device mapped the address.
176+ fn get_device ( & self , addr : IoAddress ) -> Option < & Arc < dyn DeviceIo > > {
177+ if let Some ( ( range, dev) ) = self . get_entry ( addr) {
178+ if ( addr. raw_value ( ) - range. base . raw_value ( ) ) < range. size . raw_value ( ) {
179+ return Some ( dev) ;
180+ }
181+ return None ;
182+ }
183+ None
184+ }
185+
186+ /// A helper function handling PIO read command during VM exit.
187+ /// The virtual device itself provides mutable ability and thead-safe protection.
188+ ///
189+ /// Return error if failed to get the device.
190+ pub fn pio_read ( & self , addr : u16 , data : & mut [ u8 ] ) -> Result < ( ) > {
191+ if let Some ( device) = self . get_device ( IoAddress :: Pio ( addr) ) {
192+ device. read ( IoAddress :: Pio ( addr) , data) ;
193+ Ok ( ( ) )
194+ } else {
195+ Err ( Error :: DeviceNotExist )
196+ }
197+ }
198+
199+ /// A helper function handling PIO write command during VM exit.
200+ /// The virtual device itself provides mutable ability and thead-safe protection.
201+ ///
202+ /// Return error if failed to get the device.
203+ pub fn pio_write ( & self , addr : u16 , data : & [ u8 ] ) -> Result < ( ) > {
204+ if let Some ( device) = self . get_device ( IoAddress :: Pio ( addr) ) {
205+ device. write ( IoAddress :: Pio ( addr) , data) ;
206+ Ok ( ( ) )
207+ } else {
208+ Err ( Error :: DeviceNotExist )
209+ }
210+ }
211+
212+ /// A helper function handling MMIO read command during VM exit.
213+ /// The virtual device itself provides mutable ability and thead-safe protection.
214+ ///
215+ /// Return error if failed to get the device.
216+ pub fn mmio_read ( & self , addr : u64 , data : & mut [ u8 ] ) -> Result < ( ) > {
217+ if let Some ( device) = self . get_device ( IoAddress :: Mmio ( GuestAddress ( addr) ) ) {
218+ device. read ( IoAddress :: Mmio ( GuestAddress ( addr) ) , data) ;
219+ Ok ( ( ) )
220+ } else {
221+ Err ( Error :: DeviceNotExist )
222+ }
223+ }
224+
225+ /// A helper function handling MMIO write command during VM exit.
226+ /// The virtual device itself provides mutable ability and thead-safe protection.
227+ ///
228+ /// Return error if failed to get the device.
229+ pub fn mmio_write ( & self , addr : u64 , data : & [ u8 ] ) -> Result < ( ) > {
230+ if let Some ( device) = self . get_device ( IoAddress :: Mmio ( GuestAddress ( addr) ) ) {
231+ device. write ( IoAddress :: Mmio ( GuestAddress ( addr) ) , data) ;
232+ Ok ( ( ) )
233+ } else {
234+ Err ( Error :: DeviceNotExist )
235+ }
236+ }
106237}
0 commit comments