8
8
use std:: cmp;
9
9
use std:: convert:: From ;
10
10
use std:: fs:: { File , OpenOptions } ;
11
- use std:: io:: { Seek , SeekFrom , Write } ;
11
+ use std:: io:: { Seek , SeekFrom } ;
12
12
use std:: os:: linux:: fs:: MetadataExt ;
13
13
use std:: path:: PathBuf ;
14
14
use std:: sync:: Arc ;
15
15
16
16
use block_io:: FileEngine ;
17
17
use serde:: { Deserialize , Serialize } ;
18
+ use vm_memory:: ByteValued ;
18
19
use vmm_sys_util:: eventfd:: EventFd ;
19
20
20
21
use super :: io:: async_io;
21
22
use 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 } ;
26
24
use crate :: devices:: virtio:: block:: virtio:: metrics:: { BlockDeviceMetrics , BlockMetricsPerDevice } ;
27
25
use crate :: devices:: virtio:: block:: CacheType ;
28
26
use crate :: devices:: virtio:: device:: { DeviceState , IrqTrigger , IrqType , VirtioDevice } ;
@@ -155,20 +153,17 @@ impl DiskProperties {
155
153
}
156
154
default_id
157
155
}
156
+ }
158
157
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 ,
170
162
}
171
163
164
+ // SAFETY: `ConfigSpace` contains only PODs in `repr(C)` or `repr(transparent)`, without padding.
165
+ unsafe impl ByteValued for ConfigSpace { }
166
+
172
167
/// Use this structure to set up the Block Device before booting the kernel.
173
168
#[ derive( Debug , PartialEq , Eq , Deserialize , Serialize ) ]
174
169
#[ serde( deny_unknown_fields) ]
@@ -246,7 +241,7 @@ pub struct VirtioBlock {
246
241
// Virtio fields.
247
242
pub avail_features : u64 ,
248
243
pub acked_features : u64 ,
249
- pub config_space : Vec < u8 > ,
244
+ pub config_space : ConfigSpace ,
250
245
pub activate_evt : EventFd ,
251
246
252
247
// Transport related fields.
@@ -313,10 +308,14 @@ impl VirtioBlock {
313
308
314
309
let queues = BLOCK_QUEUE_SIZES . iter ( ) . map ( |& s| Queue :: new ( s) ) . collect ( ) ;
315
310
311
+ let config_space = ConfigSpace {
312
+ capacity : disk_properties. nsectors . to_le ( ) ,
313
+ } ;
314
+
316
315
Ok ( VirtioBlock {
317
316
avail_features,
318
317
acked_features : 0u64 ,
319
- config_space : disk_properties . virtio_block_config_space ( ) ,
318
+ config_space,
320
319
activate_evt : EventFd :: new ( libc:: EFD_NONBLOCK ) . map_err ( VirtioBlockError :: EventFd ) ?,
321
320
322
321
queues,
@@ -524,7 +523,7 @@ impl VirtioBlock {
524
523
/// Update the backing file and the config space of the block device.
525
524
pub fn update_disk_image ( & mut self , disk_image_path : String ) -> Result < ( ) , VirtioBlockError > {
526
525
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();
528
527
529
528
// Kick the driver to pick up the changes.
530
529
self . irq_trigger . trigger_irq ( IrqType :: Config ) . unwrap ( ) ;
@@ -598,28 +597,23 @@ impl VirtioDevice for VirtioBlock {
598
597
& self . irq_trigger
599
598
}
600
599
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 {
604
605
error ! ( "Failed to read config space" ) ;
605
606
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 ( ) ;
614
607
}
615
608
}
616
609
617
610
fn write_config ( & mut self , offset : u64 , data : & [ u8 ] ) {
611
+ let config_space_bytes = self . config_space . as_mut_slice ( ) ;
618
612
let start = usize:: try_from ( offset) . ok ( ) ;
619
613
let end = start. and_then ( |s| s. checked_add ( data. len ( ) ) ) ;
620
614
let Some ( dst) = start
621
615
. 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) )
623
617
else {
624
618
error ! ( "Failed to write config space" ) ;
625
619
self . metrics . cfg_fails . inc ( ) ;
@@ -673,7 +667,7 @@ impl Drop for VirtioBlock {
673
667
#[ cfg( test) ]
674
668
mod tests {
675
669
use std:: fs:: metadata;
676
- use std:: io:: Read ;
670
+ use std:: io:: { Read , Write } ;
677
671
use std:: os:: unix:: ffi:: OsStrExt ;
678
672
use std:: thread;
679
673
use std:: time:: Duration ;
@@ -755,11 +749,6 @@ mod tests {
755
749
756
750
assert_eq ! ( size, u64 :: from( SECTOR_SIZE ) * num_sectors) ;
757
751
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
- }
763
752
// Testing `backing_file.virtio_block_disk_image_id()` implies
764
753
// duplicating that logic in tests, so skipping it.
765
754
@@ -803,20 +792,21 @@ mod tests {
803
792
for engine in [ FileEngineType :: Sync , FileEngineType :: Async ] {
804
793
let block = default_block ( engine) ;
805
794
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 ( ) ) ;
808
797
// This will read the number of sectors.
809
798
// The block's backing file size is 0x1000, so there are 8 (4096/512) sectors.
810
799
// 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 } ;
813
801
assert_eq ! ( actual_config_space, expected_config_space) ;
814
802
815
803
// 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 } ;
818
805
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
+ ) ;
820
810
821
811
// Validate read failed (the config space was not updated).
822
812
assert_eq ! ( actual_config_space, expected_config_space) ;
@@ -828,33 +818,37 @@ mod tests {
828
818
for engine in [ FileEngineType :: Sync , FileEngineType :: Async ] {
829
819
let mut block = default_block ( engine) ;
830
820
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 ( ) ) ;
834
823
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 ( ) ) ;
837
826
assert_eq ! ( actual_config_space, expected_config_space) ;
838
827
839
828
// 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] ) ;
843
835
}
844
- block. read_config ( 0 , & mut actual_config_space) ;
836
+ block. read_config ( 0 , actual_config_space. as_mut_slice ( ) ) ;
845
837
assert_eq ! ( actual_config_space, expected_config_space) ;
846
838
847
839
// 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 ( ) ) ;
850
844
// 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 ( ) ) ;
852
846
assert_eq ! ( actual_config_space, expected_config_space) ;
853
847
854
848
// 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 ( ) ) ;
856
850
// 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 ( ) ) ;
858
852
assert_eq ! ( actual_config_space, expected_config_space) ;
859
853
}
860
854
}
0 commit comments