1212//! devices, 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:: collections:: HashMap ;
1920use std:: result;
2021use std:: sync:: Arc ;
2122
23+ use vm_memory:: GuestAddress ;
24+
2225/// Error type for `DeviceManager` usage.
2326#[ derive( Debug ) ]
2427pub enum Error {
@@ -43,14 +46,56 @@ pub struct DeviceDescriptor {
4346 pub parent_bus : Option < Arc < dyn DeviceIo > > ,
4447}
4548
49+ // Structure describing a bus range.
50+ #[ derive( Debug , Copy , Clone ) ]
51+ struct BusRange {
52+ base : IoAddress ,
53+ size : IoSize ,
54+ }
55+
56+ impl BusRange {
57+ fn new_pio_range ( base : u16 , size : u16 ) -> Self {
58+ BusRange {
59+ base : IoAddress :: Pio ( base) ,
60+ size : IoSize :: Pio ( size) ,
61+ }
62+ }
63+ fn new_mmio_range ( base : u64 , size : u64 ) -> Self {
64+ BusRange {
65+ base : IoAddress :: Mmio ( GuestAddress ( base) ) ,
66+ size : IoSize :: Mmio ( size) ,
67+ }
68+ }
69+ }
70+
71+ impl Eq for BusRange { }
72+
73+ impl PartialEq for BusRange {
74+ fn eq ( & self , other : & BusRange ) -> bool {
75+ self . base == other. base
76+ }
77+ }
78+
79+ impl Ord for BusRange {
80+ fn cmp ( & self , other : & BusRange ) -> Ordering {
81+ self . base . cmp ( & other. base )
82+ }
83+ }
84+
85+ impl PartialOrd for BusRange {
86+ fn partial_cmp ( & self , other : & BusRange ) -> Option < Ordering > {
87+ self . base . partial_cmp ( & other. base )
88+ }
89+ }
90+
4691/// System device manager serving for all devices management and VM exit handling.
4792pub struct DeviceManager {
4893 /// Devices information mapped by instance id.
4994 devices : HashMap < u16 , DeviceDescriptor > ,
5095 /// Range mapping for VM exit pio operations.
51- pio_bus : BTreeMap < ( u16 , u16 ) , Arc < dyn DeviceIo > > ,
96+ pio_bus : BTreeMap < BusRange , Arc < dyn DeviceIo > > ,
5297 /// Range mapping for VM exit mmio operations.
53- mmio_bus : BTreeMap < ( u64 , u64 ) , Arc < dyn DeviceIo > > ,
98+ mmio_bus : BTreeMap < BusRange , Arc < dyn DeviceIo > > ,
5499}
55100
56101impl DeviceManager {
@@ -87,7 +132,11 @@ impl DeviceManager {
87132 for ( idx, res) in resources. iter ( ) . enumerate ( ) {
88133 match * res {
89134 Resource :: PioAddressRange { base, size } => {
90- if self . pio_bus . insert ( ( base, size) , device. clone ( ) ) . is_some ( ) {
135+ if self
136+ . pio_bus
137+ . insert ( BusRange :: new_pio_range ( base, size) , device. clone ( ) )
138+ . is_some ( )
139+ {
91140 // Unregister and let VMM free resources.
92141 if idx > 0 {
93142 self . unregister_resources ( & resources[ 0 ..idx] ) ;
@@ -96,7 +145,11 @@ impl DeviceManager {
96145 }
97146 }
98147 Resource :: MmioAddressRange { base, size } => {
99- if self . mmio_bus . insert ( ( base, size) , device. clone ( ) ) . is_some ( ) {
148+ if self
149+ . mmio_bus
150+ . insert ( BusRange :: new_mmio_range ( base, size) , device. clone ( ) )
151+ . is_some ( )
152+ {
100153 // Unregister and let VMM free resources.
101154 if idx > 0 {
102155 self . unregister_resources ( & resources[ 0 ..idx] ) ;
@@ -145,10 +198,10 @@ impl DeviceManager {
145198 for res in resources. iter ( ) {
146199 match * res {
147200 Resource :: PioAddressRange { base, size } => {
148- self . pio_bus . remove ( & ( base, size) ) ;
201+ self . pio_bus . remove ( & BusRange :: new_pio_range ( base, size) ) ;
149202 }
150203 Resource :: MmioAddressRange { base, size } => {
151- self . mmio_bus . remove ( & ( base, size) ) ;
204+ self . mmio_bus . remove ( & BusRange :: new_mmio_range ( base, size) ) ;
152205 }
153206 _ => continue ,
154207 }
@@ -173,4 +226,87 @@ impl DeviceManager {
173226 Err ( Error :: DeviceNotExist )
174227 }
175228 }
229+
230+ fn first_before (
231+ & self ,
232+ addr : IoAddress ,
233+ map : BTreeMap < BusRange , Arc < dyn DeviceIo > > ,
234+ ) -> Option < ( BusRange , Arc < dyn DeviceIo > ) > {
235+ for ( range, dev) in map. iter ( ) . rev ( ) {
236+ if range. base <= addr {
237+ return Some ( ( * range, dev. clone ( ) ) ) ;
238+ }
239+ }
240+ None
241+ }
242+
243+ // Return the Device mapped the address.
244+ fn get_device (
245+ & self ,
246+ addr : IoAddress ,
247+ map : BTreeMap < BusRange , Arc < dyn DeviceIo > > ,
248+ ) -> Option < Arc < dyn DeviceIo > > {
249+ if let Some ( ( range, dev) ) = self . first_before ( addr, map) {
250+ if ( addr. raw_value ( ) - range. base . raw_value ( ) ) < range. size . raw_value ( ) {
251+ return Some ( dev) ;
252+ }
253+ }
254+ None
255+ }
256+
257+ /// A helper function handling PIO read command during VM exit.
258+ /// The virtual device itself provides mutable ability and thead-safe protection.
259+ ///
260+ /// Return error if failed to get the device.
261+ pub fn pio_read ( & self , addr : u16 , data : & mut [ u8 ] ) -> Result < ( ) > {
262+ if let Some ( device) = self . get_device ( IoAddress :: Pio ( addr) , self . pio_bus . clone ( ) ) {
263+ device. read ( IoAddress :: Pio ( addr) , data) ;
264+ Ok ( ( ) )
265+ } else {
266+ Err ( Error :: DeviceNotExist )
267+ }
268+ }
269+
270+ /// A helper function handling PIO write command during VM exit.
271+ /// The virtual device itself provides mutable ability and thead-safe protection.
272+ ///
273+ /// Return error if failed to get the device.
274+ pub fn pio_write ( & self , addr : u16 , data : & [ u8 ] ) -> Result < ( ) > {
275+ if let Some ( device) = self . get_device ( IoAddress :: Pio ( addr) , self . pio_bus . clone ( ) ) {
276+ device. write ( IoAddress :: Pio ( addr) , data) ;
277+ Ok ( ( ) )
278+ } else {
279+ Err ( Error :: DeviceNotExist )
280+ }
281+ }
282+
283+ /// A helper function handling MMIO read command during VM exit.
284+ /// The virtual device itself provides mutable ability and thead-safe protection.
285+ ///
286+ /// Return error if failed to get the device.
287+ pub fn mmio_read ( & self , addr : u64 , data : & mut [ u8 ] ) -> Result < ( ) > {
288+ if let Some ( device) =
289+ self . get_device ( IoAddress :: Mmio ( GuestAddress ( addr) ) , self . mmio_bus . clone ( ) )
290+ {
291+ device. read ( IoAddress :: Mmio ( GuestAddress ( addr) ) , data) ;
292+ Ok ( ( ) )
293+ } else {
294+ Err ( Error :: DeviceNotExist )
295+ }
296+ }
297+
298+ /// A helper function handling MMIO write command during VM exit.
299+ /// The virtual device itself provides mutable ability and thead-safe protection.
300+ ///
301+ /// Return error if failed to get the device.
302+ pub fn mmio_write ( & self , addr : u64 , data : & [ u8 ] ) -> Result < ( ) > {
303+ if let Some ( device) =
304+ self . get_device ( IoAddress :: Mmio ( GuestAddress ( addr) ) , self . mmio_bus . clone ( ) )
305+ {
306+ device. write ( IoAddress :: Mmio ( GuestAddress ( addr) ) , data) ;
307+ Ok ( ( ) )
308+ } else {
309+ Err ( Error :: DeviceNotExist )
310+ }
311+ }
176312}
0 commit comments