7
7
8
8
use epoll;
9
9
use std:: cmp;
10
+ use std:: convert:: From ;
10
11
use std:: fs:: File ;
11
12
use std:: io:: { self , Read , Seek , SeekFrom , Write } ;
12
13
use std:: os:: linux:: fs:: MetadataExt ;
@@ -17,7 +18,7 @@ use std::sync::mpsc;
17
18
use std:: sync:: Arc ;
18
19
19
20
use logger:: { Metric , METRICS } ;
20
- use memory_model:: { GuestAddress , GuestMemory , GuestMemoryError } ;
21
+ use memory_model:: { DataInit , GuestAddress , GuestMemory , GuestMemoryError } ;
21
22
use rate_limiter:: { RateLimiter , TokenType } ;
22
23
use sys_util:: EventFd ;
23
24
use virtio_gen:: virtio_blk:: * ;
@@ -46,8 +47,6 @@ pub const BLOCK_EVENTS_COUNT: usize = 2;
46
47
enum Error {
47
48
/// Guest gave us bad memory addresses.
48
49
GuestMemory ( GuestMemoryError ) ,
49
- /// Guest gave us offsets that would have overflowed a usize.
50
- CheckedOffset ( GuestAddress , usize ) ,
51
50
/// Guest gave us a write only descriptor that protocol says to read from.
52
51
UnexpectedWriteOnlyDescriptor ,
53
52
/// Guest gave us a read only descriptor that protocol says to write to.
@@ -94,29 +93,18 @@ enum RequestType {
94
93
Unsupported ( u32 ) ,
95
94
}
96
95
97
- fn request_type ( mem : & GuestMemory , desc_addr : GuestAddress ) -> result:: Result < RequestType , Error > {
98
- let type_ = mem
99
- . read_obj_from_addr ( desc_addr)
100
- . map_err ( Error :: GuestMemory ) ?;
101
- match type_ {
102
- VIRTIO_BLK_T_IN => Ok ( RequestType :: In ) ,
103
- VIRTIO_BLK_T_OUT => Ok ( RequestType :: Out ) ,
104
- VIRTIO_BLK_T_FLUSH => Ok ( RequestType :: Flush ) ,
105
- VIRTIO_BLK_T_GET_ID => Ok ( RequestType :: GetDeviceID ) ,
106
- t => Ok ( RequestType :: Unsupported ( t) ) ,
96
+ impl From < u32 > for RequestType {
97
+ fn from ( value : u32 ) -> Self {
98
+ match value {
99
+ VIRTIO_BLK_T_IN => RequestType :: In ,
100
+ VIRTIO_BLK_T_OUT => RequestType :: Out ,
101
+ VIRTIO_BLK_T_FLUSH => RequestType :: Flush ,
102
+ VIRTIO_BLK_T_GET_ID => RequestType :: GetDeviceID ,
103
+ t => RequestType :: Unsupported ( t) ,
104
+ }
107
105
}
108
106
}
109
107
110
- fn sector ( mem : & GuestMemory , desc_addr : GuestAddress ) -> result:: Result < u64 , Error > {
111
- const SECTOR_OFFSET : usize = 8 ;
112
- let addr = match mem. checked_offset ( desc_addr, SECTOR_OFFSET ) {
113
- Some ( v) => v,
114
- None => return Err ( Error :: CheckedOffset ( desc_addr, SECTOR_OFFSET ) ) ,
115
- } ;
116
-
117
- mem. read_obj_from_addr ( addr) . map_err ( Error :: GuestMemory )
118
- }
119
-
120
108
fn build_device_id ( disk_image : & File ) -> result:: Result < String , Error > {
121
109
let blk_metadata = match disk_image. metadata ( ) {
122
110
Err ( _) => return Err ( Error :: GetFileMetadata ) ,
@@ -158,16 +146,54 @@ struct Request {
158
146
status_addr : GuestAddress ,
159
147
}
160
148
149
+ /// The request header represents the mandatory fields of each block device request.
150
+ ///
151
+ /// A request header contains the following fields:
152
+ /// * request_type: an u32 value mapping to a read, write or flush operation.
153
+ /// * reserved: 32 bits are reserved for future extensions of the Virtio Spec.
154
+ /// * sector: an u64 value representing the offset where a read/write is to occur.
155
+ ///
156
+ /// The header simplifies reading the request from memory as all request follow
157
+ /// the same memory layout.
158
+ #[ derive( Copy , Clone ) ]
159
+ #[ repr( C ) ]
160
+ struct RequestHeader {
161
+ request_type : u32 ,
162
+ _reserved : u32 ,
163
+ sector : u64 ,
164
+ }
165
+
166
+ // Safe because RequestHeader only contains plain data.
167
+ unsafe impl DataInit for RequestHeader { }
168
+
169
+ impl RequestHeader {
170
+ /// Reads the request header from GuestMemory starting at `addr`.
171
+ ///
172
+ /// Virtio 1.0 specifies that the data is transmitted by the driver in little-endian
173
+ /// format. Firecracker currently runs only on little endian platforms so we don't
174
+ /// need to do an explicit little endian read as all reads are little endian by default.
175
+ /// When running on a big endian platform, this code should not compile, and support
176
+ /// for explicit little endian reads is required.
177
+ #[ cfg( target_endian = "little" ) ]
178
+ fn read_from ( memory : & GuestMemory , addr : GuestAddress ) -> result:: Result < Self , Error > {
179
+ let request_header: RequestHeader = memory
180
+ . read_obj_from_addr ( addr)
181
+ . map_err ( Error :: GuestMemory ) ?;
182
+ Ok ( request_header)
183
+ }
184
+ }
185
+
161
186
impl Request {
162
187
fn parse ( avail_desc : & DescriptorChain , mem : & GuestMemory ) -> result:: Result < Request , Error > {
163
188
// The head contains the request type which MUST be readable.
164
189
if avail_desc. is_write_only ( ) {
165
190
return Err ( Error :: UnexpectedWriteOnlyDescriptor ) ;
166
191
}
167
192
193
+ let request_header = RequestHeader :: read_from ( mem, avail_desc. addr ) ?;
168
194
let mut req = Request {
169
- request_type : request_type ( & mem , avail_desc . addr ) ? ,
170
- sector : sector ( & mem , avail_desc . addr ) ? ,
195
+ request_type : RequestType :: from ( request_header . request_type ) ,
196
+ sector : request_header . sector ,
171
197
data_addr : GuestAddress ( 0 ) ,
172
198
data_len : 0 ,
173
199
status_addr : GuestAddress ( 0 ) ,
@@ -777,57 +803,52 @@ mod tests {
777
803
assert_eq ! ( h. interrupt_evt. read( ) . unwrap( ) , 2 ) ;
778
804
}
779
805
780
- #[ test]
781
- fn test_request_type ( ) {
782
- let m = & GuestMemory :: new ( & [ ( GuestAddress ( 0 ) , 0x1000 ) ] ) . unwrap ( ) ;
783
- let a = GuestAddress ( 0 ) ;
784
-
785
- // We write values associated with different request type at an address in memory,
786
- // and verify the request type is parsed correctly.
806
+ // Writes at address 0x0 the request_type, reserved, sector.
807
+ fn write_request_header ( mem : & GuestMemory , request_type : u32 , sector : u64 ) {
808
+ let addr = GuestAddress ( 0 ) ;
787
809
788
- m. write_obj_at_addr :: < u32 > ( VIRTIO_BLK_T_IN , a) . unwrap ( ) ;
789
- assert_eq ! ( request_type( m, a) . unwrap( ) , RequestType :: In ) ;
790
-
791
- m. write_obj_at_addr :: < u32 > ( VIRTIO_BLK_T_OUT , a) . unwrap ( ) ;
792
- assert_eq ! ( request_type( m, a) . unwrap( ) , RequestType :: Out ) ;
793
-
794
- m. write_obj_at_addr :: < u32 > ( VIRTIO_BLK_T_FLUSH , a) . unwrap ( ) ;
795
- assert_eq ! ( request_type( m, a) . unwrap( ) , RequestType :: Flush ) ;
796
-
797
- m. write_obj_at_addr :: < u32 > ( VIRTIO_BLK_T_GET_ID , a) . unwrap ( ) ;
798
- assert_eq ! ( request_type( m, a) . unwrap( ) , RequestType :: GetDeviceID ) ;
799
-
800
- // The value written here should be invalid.
801
- m. write_obj_at_addr :: < u32 > ( VIRTIO_BLK_T_FLUSH + 10 , a)
810
+ mem. write_obj_at_addr :: < u32 > ( request_type, addr) . unwrap ( ) ;
811
+ mem. write_obj_at_addr :: < u64 > ( sector, addr. checked_add ( 8 ) . unwrap ( ) )
802
812
. unwrap ( ) ;
803
- assert_eq ! (
804
- request_type( m, a) . unwrap( ) ,
805
- RequestType :: Unsupported ( VIRTIO_BLK_T_FLUSH + 10 )
806
- ) ;
807
-
808
- // The provided address cannot be read, as it's outside the memory space.
809
- let a = GuestAddress ( 0x1000 ) ;
810
- assert ! ( request_type( m, a) . is_err( ) )
811
813
}
812
814
813
815
#[ test]
814
- fn test_sector ( ) {
815
- let m = & GuestMemory :: new ( & [ ( GuestAddress ( 0 ) , 0x1000 ) ] ) . unwrap ( ) ;
816
- let a = GuestAddress ( 0 ) ;
817
-
818
- // Here we test that a sector number is parsed correctly from memory. The actual sector
819
- // number is expected to be found 8 bytes after the address provided as parameter to the
820
- // sector() function.
821
-
822
- m. write_obj_at_addr :: < u64 > ( 123_454_321 , a. checked_add ( 8 ) . unwrap ( ) )
823
- . unwrap ( ) ;
824
- assert_eq ! ( sector( m, a) . unwrap( ) , 123_454_321 ) ;
816
+ fn test_read_request_header ( ) {
817
+ let mem = GuestMemory :: new ( & [ ( GuestAddress ( 0 ) , 0x1000 ) ] ) . unwrap ( ) ;
818
+ let addr = GuestAddress ( 0 ) ;
819
+ let sector = 123_454_321 ;
820
+
821
+ // Test that all supported request types are read correctly from memory.
822
+ let supported_request_types = vec ! [
823
+ VIRTIO_BLK_T_IN ,
824
+ VIRTIO_BLK_T_OUT ,
825
+ VIRTIO_BLK_T_FLUSH ,
826
+ VIRTIO_BLK_T_GET_ID ,
827
+ ] ;
828
+
829
+ for request_type in supported_request_types {
830
+ write_request_header ( & mem, request_type, sector) ;
831
+
832
+ let request_header = RequestHeader :: read_from ( & mem, addr) . unwrap ( ) ;
833
+ assert_eq ! ( request_header. request_type, request_type) ;
834
+ assert_eq ! ( request_header. sector, sector) ;
835
+ }
825
836
826
- // Reading from a slightly different address should not lead a correct result in this case.
827
- assert_ne ! ( sector( m, a. checked_add( 1 ) . unwrap( ) ) . unwrap( ) , 123_454_321 ) ;
837
+ // Test that trying to read a request header that goes outside of the
838
+ // memory boundary fails.
839
+ assert ! ( RequestHeader :: read_from( & mem, GuestAddress ( 0x1000 ) ) . is_err( ) ) ;
840
+ }
828
841
829
- // The provided address is outside the valid memory range.
830
- assert ! ( sector( m, a. checked_add( 0x1000 ) . unwrap( ) ) . is_err( ) ) ;
842
+ #[ test]
843
+ fn test_request_type_from ( ) {
844
+ assert_eq ! ( RequestType :: from( VIRTIO_BLK_T_IN ) , RequestType :: In ) ;
845
+ assert_eq ! ( RequestType :: from( VIRTIO_BLK_T_OUT ) , RequestType :: Out ) ;
846
+ assert_eq ! ( RequestType :: from( VIRTIO_BLK_T_FLUSH ) , RequestType :: Flush ) ;
847
+ assert_eq ! (
848
+ RequestType :: from( VIRTIO_BLK_T_GET_ID ) ,
849
+ RequestType :: GetDeviceID
850
+ ) ;
851
+ assert_eq ! ( RequestType :: from( 42 ) , RequestType :: Unsupported ( 42 ) ) ;
831
852
}
832
853
833
854
#[ test]
0 commit comments