88use std:: cmp;
99use std:: convert:: From ;
1010use std:: fs:: { File , OpenOptions } ;
11- use std:: io:: { Seek , SeekFrom , Write } ;
11+ use std:: io:: { Seek , SeekFrom } ;
1212use std:: os:: linux:: fs:: MetadataExt ;
1313use std:: path:: PathBuf ;
1414use std:: sync:: Arc ;
1515
1616use block_io:: FileEngine ;
1717use serde:: { Deserialize , Serialize } ;
18+ use vm_memory:: ByteValued ;
1819use vmm_sys_util:: eventfd:: EventFd ;
1920
2021use super :: io:: async_io;
2122use super :: request:: * ;
22- use super :: {
23- io as block_io, VirtioBlockError , BLOCK_CONFIG_SPACE_SIZE , BLOCK_QUEUE_SIZES , SECTOR_SHIFT ,
24- SECTOR_SIZE ,
25- } ;
23+ use super :: { io as block_io, VirtioBlockError , BLOCK_QUEUE_SIZES , SECTOR_SHIFT , SECTOR_SIZE } ;
2624use crate :: devices:: virtio:: block:: virtio:: metrics:: { BlockDeviceMetrics , BlockMetricsPerDevice } ;
2725use crate :: devices:: virtio:: block:: CacheType ;
2826use crate :: devices:: virtio:: device:: { DeviceState , IrqTrigger , IrqType , VirtioDevice } ;
@@ -155,20 +153,17 @@ impl DiskProperties {
155153 }
156154 default_id
157155 }
156+ }
158157
159- /// Provides vec containing the virtio block configuration space
160- /// buffer. The config space is populated with the disk size based
161- /// on the backing file size.
162- pub fn virtio_block_config_space ( & self ) -> Vec < u8 > {
163- // The config space is little endian.
164- let mut config = Vec :: with_capacity ( BLOCK_CONFIG_SPACE_SIZE ) ;
165- for i in 0 ..BLOCK_CONFIG_SPACE_SIZE {
166- config. push ( ( ( self . nsectors >> ( 8 * i) ) & 0xff ) as u8 ) ;
167- }
168- config
169- }
158+ #[ derive( Debug , Default , Clone , Copy , Eq , PartialEq ) ]
159+ #[ repr( C ) ]
160+ pub struct ConfigSpace {
161+ pub capacity : u64 ,
170162}
171163
164+ // SAFETY: `ConfigSpace` contains only PODs in `repr(C)` or `repr(transparent)`, without padding.
165+ unsafe impl ByteValued for ConfigSpace { }
166+
172167/// Use this structure to set up the Block Device before booting the kernel.
173168#[ derive( Debug , PartialEq , Eq , Deserialize , Serialize ) ]
174169#[ serde( deny_unknown_fields) ]
@@ -246,7 +241,7 @@ pub struct VirtioBlock {
246241 // Virtio fields.
247242 pub avail_features : u64 ,
248243 pub acked_features : u64 ,
249- pub config_space : Vec < u8 > ,
244+ pub config_space : ConfigSpace ,
250245 pub activate_evt : EventFd ,
251246
252247 // Transport related fields.
@@ -313,10 +308,14 @@ impl VirtioBlock {
313308
314309 let queues = BLOCK_QUEUE_SIZES . iter ( ) . map ( |& s| Queue :: new ( s) ) . collect ( ) ;
315310
311+ let config_space = ConfigSpace {
312+ capacity : disk_properties. nsectors . to_le ( ) ,
313+ } ;
314+
316315 Ok ( VirtioBlock {
317316 avail_features,
318317 acked_features : 0u64 ,
319- config_space : disk_properties . virtio_block_config_space ( ) ,
318+ config_space,
320319 activate_evt : EventFd :: new ( libc:: EFD_NONBLOCK ) . map_err ( VirtioBlockError :: EventFd ) ?,
321320
322321 queues,
@@ -524,7 +523,7 @@ impl VirtioBlock {
524523 /// Update the backing file and the config space of the block device.
525524 pub fn update_disk_image ( & mut self , disk_image_path : String ) -> Result < ( ) , VirtioBlockError > {
526525 self . disk . update ( disk_image_path, self . read_only ) ?;
527- self . config_space = self . disk . virtio_block_config_space ( ) ;
526+ self . config_space . capacity = self . disk . nsectors . to_le ( ) ; // virtio_block_config_space();
528527
529528 // Kick the driver to pick up the changes.
530529 self . irq_trigger . trigger_irq ( IrqType :: Config ) . unwrap ( ) ;
@@ -598,28 +597,23 @@ impl VirtioDevice for VirtioBlock {
598597 & self . irq_trigger
599598 }
600599
601- fn read_config ( & self , offset : u64 , mut data : & mut [ u8 ] ) {
602- let config_len = self . config_space . len ( ) as u64 ;
603- if offset >= config_len {
600+ fn read_config ( & self , offset : u64 , data : & mut [ u8 ] ) {
601+ if let Some ( config_space_bytes) = self . config_space . as_slice ( ) . get ( u64_to_usize ( offset) ..) {
602+ let len = config_space_bytes. len ( ) . min ( data. len ( ) ) ;
603+ data[ ..len] . copy_from_slice ( & config_space_bytes[ ..len] ) ;
604+ } else {
604605 error ! ( "Failed to read config space" ) ;
605606 self . metrics . cfg_fails . inc ( ) ;
606- return ;
607- }
608- if let Some ( end) = offset. checked_add ( data. len ( ) as u64 ) {
609- // This write can't fail, offset and end are checked against config_len.
610- data. write_all (
611- & self . config_space [ u64_to_usize ( offset) ..u64_to_usize ( cmp:: min ( end, config_len) ) ] ,
612- )
613- . unwrap ( ) ;
614607 }
615608 }
616609
617610 fn write_config ( & mut self , offset : u64 , data : & [ u8 ] ) {
611+ let config_space_bytes = self . config_space . as_mut_slice ( ) ;
618612 let start = usize:: try_from ( offset) . ok ( ) ;
619613 let end = start. and_then ( |s| s. checked_add ( data. len ( ) ) ) ;
620614 let Some ( dst) = start
621615 . zip ( end)
622- . and_then ( |( start, end) | self . config_space . get_mut ( start..end) )
616+ . and_then ( |( start, end) | config_space_bytes . get_mut ( start..end) )
623617 else {
624618 error ! ( "Failed to write config space" ) ;
625619 self . metrics . cfg_fails . inc ( ) ;
@@ -673,7 +667,7 @@ impl Drop for VirtioBlock {
673667#[ cfg( test) ]
674668mod tests {
675669 use std:: fs:: metadata;
676- use std:: io:: Read ;
670+ use std:: io:: { Read , Write } ;
677671 use std:: os:: unix:: ffi:: OsStrExt ;
678672 use std:: thread;
679673 use std:: time:: Duration ;
@@ -755,11 +749,6 @@ mod tests {
755749
756750 assert_eq ! ( size, u64 :: from( SECTOR_SIZE ) * num_sectors) ;
757751 assert_eq ! ( disk_properties. nsectors, num_sectors) ;
758- let cfg = disk_properties. virtio_block_config_space ( ) ;
759- assert_eq ! ( cfg. len( ) , BLOCK_CONFIG_SPACE_SIZE ) ;
760- for ( i, byte) in cfg. iter ( ) . enumerate ( ) {
761- assert_eq ! ( * byte, ( ( num_sectors >> ( 8 * i) ) & 0xff ) as u8 ) ;
762- }
763752 // Testing `backing_file.virtio_block_disk_image_id()` implies
764753 // duplicating that logic in tests, so skipping it.
765754
@@ -803,20 +792,21 @@ mod tests {
803792 for engine in [ FileEngineType :: Sync , FileEngineType :: Async ] {
804793 let block = default_block ( engine) ;
805794
806- let mut actual_config_space = [ 0u8 ; BLOCK_CONFIG_SPACE_SIZE ] ;
807- block. read_config ( 0 , & mut actual_config_space) ;
795+ let mut actual_config_space = ConfigSpace :: default ( ) ;
796+ block. read_config ( 0 , actual_config_space. as_mut_slice ( ) ) ;
808797 // This will read the number of sectors.
809798 // The block's backing file size is 0x1000, so there are 8 (4096/512) sectors.
810799 // The config space is little endian.
811- let expected_config_space: [ u8 ; BLOCK_CONFIG_SPACE_SIZE ] =
812- [ 0x08 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ] ;
800+ let expected_config_space = ConfigSpace { capacity : 8 } ;
813801 assert_eq ! ( actual_config_space, expected_config_space) ;
814802
815803 // Invalid read.
816- let expected_config_space: [ u8 ; BLOCK_CONFIG_SPACE_SIZE ] =
817- [ 0xd , 0xe , 0xa , 0xd , 0xb , 0xe , 0xe , 0xf ] ;
804+ let expected_config_space = ConfigSpace { capacity : 696969 } ;
818805 actual_config_space = expected_config_space;
819- block. read_config ( BLOCK_CONFIG_SPACE_SIZE as u64 + 1 , & mut actual_config_space) ;
806+ block. read_config (
807+ std:: mem:: size_of :: < ConfigSpace > ( ) as u64 + 1 ,
808+ actual_config_space. as_mut_slice ( ) ,
809+ ) ;
820810
821811 // Validate read failed (the config space was not updated).
822812 assert_eq ! ( actual_config_space, expected_config_space) ;
@@ -828,33 +818,37 @@ mod tests {
828818 for engine in [ FileEngineType :: Sync , FileEngineType :: Async ] {
829819 let mut block = default_block ( engine) ;
830820
831- let expected_config_space: [ u8 ; BLOCK_CONFIG_SPACE_SIZE ] =
832- [ 0x00 , 0x50 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ] ;
833- block. write_config ( 0 , & expected_config_space) ;
821+ let expected_config_space = ConfigSpace { capacity : 696969 } ;
822+ block. write_config ( 0 , expected_config_space. as_slice ( ) ) ;
834823
835- let mut actual_config_space = [ 0u8 ; BLOCK_CONFIG_SPACE_SIZE ] ;
836- block. read_config ( 0 , & mut actual_config_space) ;
824+ let mut actual_config_space = ConfigSpace :: default ( ) ;
825+ block. read_config ( 0 , actual_config_space. as_mut_slice ( ) ) ;
837826 assert_eq ! ( actual_config_space, expected_config_space) ;
838827
839828 // If priviledged user writes to `/dev/mem`, in block config space - byte by byte.
840- let expected_config_space = [ 0x11 , 0x22 , 0x33 , 0x44 , 0x55 , 0x66 , 0x00 , 0x11 ] ;
841- for i in 0 ..expected_config_space. len ( ) {
842- block. write_config ( i as u64 , & expected_config_space[ i..=i] ) ;
829+ let expected_config_space = ConfigSpace {
830+ capacity : 0x1122334455667788 ,
831+ } ;
832+ let expected_config_space_slice = expected_config_space. as_slice ( ) ;
833+ for ( i, b) in expected_config_space_slice. iter ( ) . enumerate ( ) {
834+ block. write_config ( i as u64 , & [ * b] ) ;
843835 }
844- block. read_config ( 0 , & mut actual_config_space) ;
836+ block. read_config ( 0 , actual_config_space. as_mut_slice ( ) ) ;
845837 assert_eq ! ( actual_config_space, expected_config_space) ;
846838
847839 // Invalid write.
848- let new_config_space = [ 0xd , 0xe , 0xa , 0xd , 0xb , 0xe , 0xe , 0xf ] ;
849- block. write_config ( 5 , & new_config_space) ;
840+ let new_config_space = ConfigSpace {
841+ capacity : 0xDEADBEEF ,
842+ } ;
843+ block. write_config ( 5 , new_config_space. as_slice ( ) ) ;
850844 // Make sure nothing got written.
851- block. read_config ( 0 , & mut actual_config_space) ;
845+ block. read_config ( 0 , actual_config_space. as_mut_slice ( ) ) ;
852846 assert_eq ! ( actual_config_space, expected_config_space) ;
853847
854848 // Large offset that may cause an overflow.
855- block. write_config ( u64:: MAX , & new_config_space) ;
849+ block. write_config ( u64:: MAX , new_config_space. as_slice ( ) ) ;
856850 // Make sure nothing got written.
857- block. read_config ( 0 , & mut actual_config_space) ;
851+ block. read_config ( 0 , actual_config_space. as_mut_slice ( ) ) ;
858852 assert_eq ! ( actual_config_space, expected_config_space) ;
859853 }
860854 }
0 commit comments