22// Use of this source code is governed by a BSD-style license that can be
33// found in the LICENSE file.
44
5+ use std:: collections:: HashMap ;
56use std:: fmt;
67use std:: sync:: { Arc , Mutex } ;
78
@@ -25,6 +26,8 @@ pub enum Error {
2526 Cmdline ( kernel_cmdline:: Error ) ,
2627 /// No more IRQs are available.
2728 IrqsExhausted ,
29+ /// Failed to update the mmio device.
30+ UpdateFailed ,
2831}
2932
3033impl fmt:: Display for Error {
@@ -37,6 +40,7 @@ impl fmt::Display for Error {
3740 write ! ( f, "unable to add device to kernel command line: {}" , e)
3841 }
3942 & Error :: IrqsExhausted => write ! ( f, "no more IRQs are available" ) ,
43+ & Error :: UpdateFailed => write ! ( f, "failed to update the mmio device" ) ,
4044 }
4145 }
4246}
@@ -54,13 +58,18 @@ const IRQ_BASE: u32 = 5;
5458/// Currently hardcoded to 4K
5559const MMIO_LEN : u64 = 0x1000 ;
5660
61+ /// This represents the offset at which the device should call BusDevice::write in order to write
62+ /// to its configuration space.
63+ const MMIO_CFG_SPACE_OFF : u64 = 0x100 ;
64+
5765/// Manages the complexities of adding a device.
5866pub struct MMIODeviceManager {
5967 pub bus : devices:: Bus ,
6068 pub vm_requests : Vec < VmRequest > ,
6169 guest_mem : GuestMemory ,
6270 mmio_base : u64 ,
6371 irq : u32 ,
72+ id_to_addr_map : HashMap < String , u64 > ,
6473}
6574
6675impl MMIODeviceManager {
@@ -72,6 +81,7 @@ impl MMIODeviceManager {
7281 mmio_base : mmio_base,
7382 irq : IRQ_BASE ,
7483 bus : devices:: Bus :: new ( ) ,
84+ id_to_addr_map : HashMap :: new ( ) ,
7585 }
7686 }
7787
@@ -80,7 +90,8 @@ impl MMIODeviceManager {
8090 & mut self ,
8191 device : Box < devices:: virtio:: VirtioDevice > ,
8292 cmdline : & mut kernel_cmdline:: Cmdline ,
83- ) -> Result < ( ) > {
93+ id : Option < String > ,
94+ ) -> Result < u64 > {
8495 if self . irq > MAX_IRQ {
8596 return Err ( Error :: IrqsExhausted ) ;
8697 }
@@ -111,17 +122,43 @@ impl MMIODeviceManager {
111122 // as per doc, [virtio_mmio.]device=<size>@<baseaddr>:<irq> needs to be appended
112123 // to kernel commandline for virtio mmio devices to get recognized
113124 // the size parameter has to be transformed to KiB, so dividing hexadecimal value in
114- // bytes to 1024; further, the '{}' formatting rust construct will automatically transform it to decimal
125+ // bytes to 1024; further, the '{}' formatting rust construct will automatically
126+ // transform it to decimal
115127 cmdline
116128 . insert (
117129 "virtio_mmio.device" ,
118130 & format ! ( "{}K@0x{:08x}:{}" , MMIO_LEN / 1024 , self . mmio_base, self . irq) ,
119131 )
120132 . map_err ( Error :: Cmdline ) ?;
133+ let ret = self . mmio_base ;
121134 self . mmio_base += MMIO_LEN ;
122135 self . irq += 1 ;
123136
124- Ok ( ( ) )
137+ if let Some ( device_id) = id {
138+ self . id_to_addr_map . insert ( device_id. clone ( ) , ret) ;
139+ }
140+
141+ Ok ( ret)
142+ }
143+
144+ /// Update a drive by rebuilding its config space and rewriting it on the bus.
145+ pub fn update_drive ( & self , addr : u64 , new_size : u64 ) -> Result < ( ) > {
146+ if let Some ( ( _, device) ) = self . bus . get_device ( addr) {
147+ let data = devices:: virtio:: build_config_space ( new_size) ;
148+ let mut busdev = device. lock ( ) . unwrap ( ) ;
149+
150+ busdev. write ( MMIO_CFG_SPACE_OFF , & data[ ..] ) ;
151+ busdev. interrupt ( devices:: virtio:: VIRTIO_MMIO_INT_CONFIG ) ;
152+
153+ Ok ( ( ) )
154+ } else {
155+ Err ( Error :: UpdateFailed )
156+ }
157+ }
158+
159+ /// Gets the address of the specified device on the bus.
160+ pub fn get_address ( & self , id : & String ) -> Option < & u64 > {
161+ return self . id_to_addr_map . get ( id. as_str ( ) ) ;
125162 }
126163}
127164
@@ -176,7 +213,7 @@ mod tests {
176213
177214 assert ! (
178215 device_manager
179- . register_device( dummy_box, & mut cmdline)
216+ . register_device( dummy_box, & mut cmdline, None )
180217 . is_ok( )
181218 ) ;
182219 }
@@ -193,14 +230,14 @@ mod tests {
193230 let dummy_box = Box :: new ( DummyDevice { dummy : 0 } ) ;
194231 for _i in IRQ_BASE ..( MAX_IRQ + 1 ) {
195232 device_manager
196- . register_device ( dummy_box. clone ( ) , & mut cmdline)
233+ . register_device ( dummy_box. clone ( ) , & mut cmdline, None )
197234 . unwrap ( ) ;
198235 }
199236 assert_eq ! (
200237 format!(
201238 "{}" ,
202239 device_manager
203- . register_device( dummy_box. clone( ) , & mut cmdline)
240+ . register_device( dummy_box. clone( ) , & mut cmdline, None )
204241 . unwrap_err( )
205242 ) ,
206243 "no more IRQs are available" . to_string( )
@@ -251,5 +288,43 @@ mod tests {
251288 assert_eq ! ( format!( "{}" , e) , "failed to clone ioeventfd: Error(0)" ) ;
252289 let e = Error :: CloneIrqFd ( sys_util:: Error :: new ( 0 ) ) ;
253290 assert_eq ! ( format!( "{}" , e) , "failed to clone irqfd: Error(0)" ) ;
291+ let e = Error :: UpdateFailed ;
292+ assert_eq ! ( format!( "{}" , e) , "failed to update the mmio device" ) ;
293+ }
294+
295+ #[ test]
296+ fn test_update_drive ( ) {
297+ let start_addr1 = GuestAddress ( 0x0 ) ;
298+ let start_addr2 = GuestAddress ( 0x1000 ) ;
299+ let guest_mem =
300+ GuestMemory :: new ( & vec ! [ ( start_addr1, 0x1000 ) , ( start_addr2, 0x1000 ) ] ) . unwrap ( ) ;
301+ let mut device_manager = MMIODeviceManager :: new ( guest_mem, 0xd0000000 ) ;
302+ let mut cmdline = kernel_cmdline:: Cmdline :: new ( 4096 ) ;
303+ let dummy_box = Box :: new ( DummyDevice { dummy : 0 } ) ;
304+
305+ if let Ok ( addr) =
306+ device_manager. register_device ( dummy_box, & mut cmdline, Some ( String :: from ( "foo" ) ) )
307+ {
308+ assert ! ( device_manager. update_drive( addr, 1048576 ) . is_ok( ) ) ;
309+ }
310+ assert ! ( device_manager. update_drive( 0xbeef , 1048576 ) . is_err( ) ) ;
311+ }
312+
313+ #[ test]
314+ fn test_get_address ( ) {
315+ let start_addr1 = GuestAddress ( 0x0 ) ;
316+ let start_addr2 = GuestAddress ( 0x1000 ) ;
317+ let guest_mem =
318+ GuestMemory :: new ( & vec ! [ ( start_addr1, 0x1000 ) , ( start_addr2, 0x1000 ) ] ) . unwrap ( ) ;
319+ let mut device_manager = MMIODeviceManager :: new ( guest_mem, 0xd0000000 ) ;
320+ let mut cmdline = kernel_cmdline:: Cmdline :: new ( 4096 ) ;
321+ let dummy_box = Box :: new ( DummyDevice { dummy : 0 } ) ;
322+
323+ let id = String :: from ( "foo" ) ;
324+ if let Ok ( addr) = device_manager. register_device ( dummy_box, & mut cmdline, Some ( id. clone ( ) ) )
325+ {
326+ assert_eq ! ( Some ( & addr) , device_manager. get_address( & id) ) ;
327+ }
328+ assert_eq ! ( None , device_manager. get_address( & String :: from( "bar" ) ) ) ;
254329 }
255330}
0 commit comments