11use std:: cmp:: min;
22use std:: mem:: size_of;
33
4- use crate :: common:: GuestAddr ;
5- use crate :: common:: PAGE_SIZE ;
4+ use crate :: common:: { GuestAddr , GuestRegion , PAGE_SIZE } ;
65use crate :: vmm:: MemCtx ;
76
87use super :: bits:: * ;
@@ -186,14 +185,19 @@ impl NvmeCtrl {
186185 cmd : & cmds:: GetLogPageCmd ,
187186 mem : & MemCtx ,
188187 ) -> cmds:: Completion {
189- assert ! ( ( cmd. len as usize ) < PAGE_SIZE ) ;
190- let buf = cmd
188+ if let Some ( regions) = cmd
191189 . data ( mem)
192- . next ( )
193- . expect ( "missing prp entry for log page response" ) ;
194- // TODO: actually keep a log that we can write back instead of all zeros
195- assert ! ( mem. write_byte( buf. 0 , 0 , cmd. len as usize ) ) ;
196- cmds:: Completion :: success ( )
190+ . map ( |r| mem. writable_region ( & r) )
191+ . collect :: < Option < Vec < _ > > > ( )
192+ {
193+ // TODO: Keep a log to write back instead of 0s
194+ for region in regions {
195+ let _ = region. write_byte ( 0 , region. len ( ) ) ;
196+ }
197+ cmds:: Completion :: success ( )
198+ } else {
199+ cmds:: Completion :: generic_err ( STS_DATA_XFER_ERR )
200+ }
197201 }
198202
199203 /// Service Identify command.
@@ -208,12 +212,16 @@ impl NvmeCtrl {
208212 IDENT_CNS_NAMESPACE => match cmd. nsid {
209213 1 => {
210214 assert ! ( size_of:: <IdentifyNamespace >( ) <= PAGE_SIZE ) ;
211- let buf = cmd
212- . data ( mem)
213- . next ( )
214- . expect ( "missing prp entry for ident response" ) ;
215- assert ! ( mem. write( buf. 0 , & self . ns_ident) ) ;
216- cmds:: Completion :: success ( )
215+ match Self :: write_admin_result (
216+ cmd. data ( mem) ,
217+ & self . ns_ident ,
218+ mem,
219+ ) {
220+ Some ( _) => cmds:: Completion :: success ( ) ,
221+ None => {
222+ cmds:: Completion :: generic_err ( STS_DATA_XFER_ERR )
223+ }
224+ }
217225 }
218226 // 0 is not a valid NSID (See NVMe 1.0e, Section 6.1 Namespaces)
219227 // We also don't currently support namespace management
@@ -224,12 +232,15 @@ impl NvmeCtrl {
224232 } ,
225233 IDENT_CNS_CONTROLLER => {
226234 assert ! ( size_of:: <IdentifyController >( ) <= PAGE_SIZE ) ;
227- let buf = cmd
228- . data ( mem)
229- . next ( )
230- . expect ( "missing prp entry for ident response" ) ;
231- assert ! ( mem. write( buf. 0 , & self . ctrl_ident) ) ;
232- cmds:: Completion :: success ( )
235+
236+ match Self :: write_admin_result (
237+ cmd. data ( mem) ,
238+ & self . ctrl_ident ,
239+ mem,
240+ ) {
241+ Some ( _) => cmds:: Completion :: success ( ) ,
242+ None => cmds:: Completion :: generic_err ( STS_DATA_XFER_ERR ) ,
243+ }
233244 }
234245 // We currently present NVMe version 1.0 in which CNS is a 1-bit field
235246 // and hence only need to support the NAMESPACE and CONTROLLER cases
@@ -282,4 +293,58 @@ impl NvmeCtrl {
282293 }
283294 }
284295 }
296+
297+ /// Write result data from an admin command into host memory
298+ ///
299+ /// The `data` type must be `repr(packed(1))`
300+ ///
301+ /// Returns `Some(())` if successful, else None
302+ fn write_admin_result < T : Copy > (
303+ prp : cmds:: PrpIter ,
304+ data : & T ,
305+ mem : & MemCtx ,
306+ ) -> Option < ( ) > {
307+ let bufs: Vec < GuestRegion > = prp. collect ( ) ;
308+ if size_of :: < T > ( ) > bufs. iter ( ) . map ( |r| r. 1 ) . sum :: < usize > ( ) {
309+ // Not enough space
310+ return None ;
311+ }
312+ let regions = bufs
313+ . into_iter ( )
314+ . map ( |r| mem. writable_region ( & r) )
315+ . collect :: < Option < Vec < _ > > > ( ) ?;
316+ if regions. len ( ) == 1 {
317+ // Can be copied to one contiguous page
318+ regions[ 0 ] . write ( data) . ok ( ) ?;
319+ Some ( ( ) )
320+ } else {
321+ // Split across multiple pages
322+
323+ // Safety:
324+ //
325+ // We expect and demand that the resulting structs written through
326+ // this function are packed, such that there is no padding to risk
327+ // UB through the [u8] slice creation.
328+ let mut raw = unsafe {
329+ std:: slice:: from_raw_parts (
330+ data as * const T as * const u8 ,
331+ size_of :: < T > ( ) ,
332+ )
333+ } ;
334+ let mut copied = 0 ;
335+ for region in regions {
336+ let write_len = usize:: min ( region. len ( ) , raw. len ( ) ) ;
337+
338+ let to_copy;
339+ ( to_copy, raw) = raw. split_at ( write_len) ;
340+ copied += region. write_bytes ( & to_copy) . ok ( ) ?;
341+
342+ if raw. is_empty ( ) {
343+ break ;
344+ }
345+ }
346+ assert_eq ! ( copied, size_of:: <T >( ) ) ;
347+ Some ( ( ) )
348+ }
349+ }
285350}
0 commit comments