4
4
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
5
5
6
6
use std:: collections:: HashMap ;
7
+ use std:: convert:: { TryFrom as _, TryInto as _} ;
7
8
use std:: ffi:: CString ;
8
9
use std:: fs:: { File , OpenOptions } ;
9
10
use std:: mem:: { self , ManuallyDrop } ;
@@ -279,16 +280,36 @@ impl VfioContainer {
279
280
/// Map a region of guest memory regions into the vfio container's iommu table.
280
281
///
281
282
/// # Parameters
283
+ ///
282
284
/// * iova: IO virtual address to mapping the memory.
283
285
/// * size: size of the memory region.
284
286
/// * user_addr: host virtual address for the guest memory region to map.
285
- pub fn vfio_dma_map ( & self , iova : u64 , size : u64 , user_addr : u64 ) -> Result < ( ) > {
287
+ ///
288
+ /// # Safety
289
+ ///
290
+ /// Until [`Self::vfio_dma_unmap`] is successfully called with identical
291
+ /// values for iova and size, or until the entire range of `[user_addr..user_addr+size]`
292
+ /// has been unmapped with successful calls to `munmap` or replaced with successful calls
293
+ /// to `mmap(MAP_FIXED)`, it is not safe to use any of the address range in
294
+ /// `[user_addr..user_addr+size]` for almost any purpose. The only permitted purposes are
295
+ ///
296
+ /// - Atomic and/or volatile operations.
297
+ /// - Assignment to a guest.
298
+ /// - Sharing with another process.
299
+ /// - Passing a raw pointer to functions that only do one of the above things.
300
+ ///
301
+ /// In particular, creating a Rust reference to this memory is instant undefined behavior
302
+ /// due to the Rust aliasing rules. It is also undefined behavior to call this function if
303
+ /// a Rust reference to this memory is live.
304
+ pub unsafe fn vfio_dma_map ( & self , iova : u64 , size : usize , user_addr : * mut u8 ) -> Result < ( ) > {
305
+ const _: ( ) = assert ! ( mem:: size_of:: <u64 >( ) >= mem:: size_of:: <* mut u8 >( ) ) ;
306
+ const _: ( ) = assert ! ( mem:: size_of:: <u64 >( ) >= mem:: size_of:: <usize >( ) ) ;
286
307
let dma_map = vfio_iommu_type1_dma_map {
287
308
argsz : mem:: size_of :: < vfio_iommu_type1_dma_map > ( ) as u32 ,
288
309
flags : VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE ,
289
- vaddr : user_addr,
310
+ vaddr : ( user_addr as usize ) . try_into ( ) . unwrap ( ) ,
290
311
iova,
291
- size,
312
+ size : size . try_into ( ) . unwrap ( ) ,
292
313
} ;
293
314
294
315
vfio_syscall:: map_dma ( self , & dma_map)
@@ -299,16 +320,16 @@ impl VfioContainer {
299
320
/// # Parameters
300
321
/// * iova: IO virtual address to mapping the memory.
301
322
/// * size: size of the memory region.
302
- pub fn vfio_dma_unmap ( & self , iova : u64 , size : u64 ) -> Result < ( ) > {
323
+ pub fn vfio_dma_unmap ( & self , iova : u64 , size : usize ) -> Result < ( ) > {
303
324
let mut dma_unmap = vfio_iommu_type1_dma_unmap {
304
325
argsz : mem:: size_of :: < vfio_iommu_type1_dma_unmap > ( ) as u32 ,
305
326
flags : 0 ,
306
327
iova,
307
- size,
328
+ size : size . try_into ( ) . unwrap ( ) ,
308
329
} ;
309
330
310
331
vfio_syscall:: unmap_dma ( self , & mut dma_unmap) ?;
311
- if dma_unmap. size != size {
332
+ if dma_unmap. size != u64 :: try_from ( size) . unwrap ( ) {
312
333
return Err ( VfioError :: InvalidDmaUnmapSize ) ;
313
334
}
314
335
@@ -319,29 +340,54 @@ impl VfioContainer {
319
340
///
320
341
/// # Parameters
321
342
/// * mem: pinned guest memory which could be accessed by devices binding to the container.
322
- pub fn vfio_map_guest_memory < M : GuestMemory > ( & self , mem : & M ) -> Result < ( ) > {
343
+ ///
344
+ /// # Safety
345
+ ///
346
+ /// You have to promise that the requirements of [`Self::vfio_dma_map`] are upheld for each
347
+ /// of the regions. A future version of the crate will ensure that they are upheld by having
348
+ /// `vfio-ioctls` own a reference to the memory mapped into the guest. You also have to
349
+ /// promise that the [`GuestMemory`] implementation is well-behaved and upholds the contracts
350
+ /// in its documentation.
351
+ ///
352
+ /// # Panics
353
+ ///
354
+ /// Panics if the length of one of the regions overflows `usize`.
355
+ pub unsafe fn vfio_map_guest_memory < M : GuestMemory > ( & self , mem : & M ) -> Result < ( ) > {
323
356
mem. iter ( ) . try_for_each ( |region| {
324
357
let host_addr = region
325
358
. get_host_address ( MemoryRegionAddress ( 0 ) )
326
359
. map_err ( |_| VfioError :: GetHostAddress ) ?;
327
- self . vfio_dma_map (
328
- region. start_addr ( ) . raw_value ( ) ,
329
- region. len ( ) ,
330
- host_addr as u64 ,
331
- )
360
+ // SAFETY: GuestMemory guarantees the requirements
361
+ // are upheld.
362
+ unsafe {
363
+ self . vfio_dma_map (
364
+ region. start_addr ( ) . raw_value ( ) ,
365
+ region. len ( ) . try_into ( ) . unwrap ( ) ,
366
+ host_addr,
367
+ )
368
+ }
332
369
} )
333
370
}
334
371
335
372
/// Remove all guest memory regions from the vfio container's iommu table.
336
373
///
337
- /// The vfio kernel driver and device hardware couldn 't access this guest memory after
338
- /// returning from the function.
374
+ /// The vfio kernel driver and device hardware can 't access this guest memory after
375
+ /// the function returns successfully .
339
376
///
340
377
/// # Parameters
341
378
/// * mem: pinned guest memory which could be accessed by devices binding to the container.
379
+ ///
380
+ /// # Panics
381
+ ///
382
+ /// Panics if the length of any of the regions overflows `usize`. That should have been
383
+ /// caught by [`Self::vfio_map_guest_memory`], so it indicates a bogus [`GuestMemory`]
384
+ /// implementation.
342
385
pub fn vfio_unmap_guest_memory < M : GuestMemory > ( & self , mem : & M ) -> Result < ( ) > {
343
386
mem. iter ( ) . try_for_each ( |region| {
344
- self . vfio_dma_unmap ( region. start_addr ( ) . raw_value ( ) , region. len ( ) )
387
+ self . vfio_dma_unmap (
388
+ region. start_addr ( ) . raw_value ( ) ,
389
+ region. len ( ) . try_into ( ) . unwrap ( ) ,
390
+ )
345
391
} )
346
392
}
347
393
@@ -1358,8 +1404,10 @@ mod tests {
1358
1404
container. put_group ( group3) ;
1359
1405
assert_eq ! ( Arc :: strong_count( & group) , 1 ) ;
1360
1406
1361
- container. vfio_dma_map ( 0x1000 , 0x1000 , 0x8000 ) . unwrap ( ) ;
1362
- container. vfio_dma_map ( 0x2000 , 0x2000 , 0x8000 ) . unwrap_err ( ) ;
1407
+ // SAFETY: this is a test implementation that does not access memory
1408
+ unsafe { container. vfio_dma_map ( 0x1000 , 0x1000 , 0x8000 as _ ) } . unwrap ( ) ;
1409
+ // SAFETY: this is a test implementation that does not access memory
1410
+ unsafe { container. vfio_dma_map ( 0x2000 , 0x2000 , 0x8000 as _ ) } . unwrap_err ( ) ;
1363
1411
container. vfio_dma_unmap ( 0x1000 , 0x1000 ) . unwrap ( ) ;
1364
1412
container. vfio_dma_unmap ( 0x2000 , 0x2000 ) . unwrap_err ( ) ;
1365
1413
}
@@ -1506,7 +1554,9 @@ mod tests {
1506
1554
let mem1 = GuestMemoryMmap :: < ( ) > :: from_ranges ( & [ ( addr1, 0x1000 ) ] ) . unwrap ( ) ;
1507
1555
let container = create_vfio_container ( ) ;
1508
1556
1509
- container. vfio_map_guest_memory ( & mem1) . unwrap ( ) ;
1557
+ // SAFETY: This is a dummy implementation that does not access
1558
+ // memory.
1559
+ unsafe { container. vfio_map_guest_memory ( & mem1) } . unwrap ( ) ;
1510
1560
1511
1561
let addr2 = GuestAddress ( 0x3000 ) ;
1512
1562
let mem2 = GuestMemoryMmap :: < ( ) > :: from_ranges ( & [ ( addr2, 0x1000 ) ] ) . unwrap ( ) ;
0 commit comments