1 // Copyright 2020 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 use crate::MappedRegion; 6 use std::fmt::{self, Display}; 7 8 #[derive(Debug, Eq, PartialEq)] 9 pub enum Error { 10 // A null address is typically bad. mmap allows it, but not external libraries 11 NullAddress, 12 // For external mappings that have weird sizes 13 InvalidSize, 14 // External library failed to map 15 LibraryError(i32), 16 // If external mapping is unsupported. 17 Unsupported, 18 } 19 20 impl Display for Error { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result21 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 22 use self::Error::*; 23 24 match self { 25 NullAddress => write!(f, "null address returned"), 26 InvalidSize => write!(f, "invalid size returned"), 27 LibraryError(ret) => write!(f, "library failed to map with {}", ret), 28 Unsupported => write!(f, "external mapping unsupported"), 29 } 30 } 31 } 32 33 pub type Result<T> = std::result::Result<T, Error>; 34 35 // Maps a external library resource given an id, returning address and size upon success 36 pub type Map = fn(u32) -> Result<(u64, usize)>; 37 // Unmaps the resource given a resource id. 38 pub type Unmap = fn(u32); 39 40 /// ExternalMapping wraps an external library mapping. This is useful in cases where where the 41 /// device memory is not compatible with the mmap interface, such as Vulkan VkDeviceMemory in the 42 /// non-exportable case or when exported as an opaque fd. 43 #[derive(Debug, PartialEq)] 44 pub struct ExternalMapping { 45 resource_id: u32, 46 ptr: u64, 47 size: usize, 48 unmap: Unmap, 49 } 50 51 unsafe impl Send for ExternalMapping {} 52 unsafe impl Sync for ExternalMapping {} 53 impl ExternalMapping { 54 /// Creates an ExternalMapping given a library-specific resource id and map/unmap functions. 55 /// 56 /// # Safety 57 /// 58 /// The map function must return a valid host memory region. In addition, callers of the 59 /// function must guarantee that the map and unmap functions are thread-safe, never return a 60 /// region overlapping already Rust referenced-data, and the backing store of the resource 61 /// doesn't disappear before the unmap function is called. new(resource_id: u32, map: Map, unmap: Unmap) -> Result<ExternalMapping>62 pub unsafe fn new(resource_id: u32, map: Map, unmap: Unmap) -> Result<ExternalMapping> { 63 let (ptr, size) = map(resource_id)?; 64 65 if (ptr as *mut u8).is_null() { 66 return Err(Error::NullAddress); 67 } 68 if size == 0 { 69 return Err(Error::InvalidSize); 70 } 71 72 Ok(ExternalMapping { 73 resource_id, 74 ptr, 75 size, 76 unmap, 77 }) 78 } 79 } 80 81 unsafe impl MappedRegion for ExternalMapping { 82 /// used for passing this region to ioctls for setting guest memory. as_ptr(&self) -> *mut u883 fn as_ptr(&self) -> *mut u8 { 84 self.ptr as *mut u8 85 } 86 87 /// Returns the size of the memory region in bytes. size(&self) -> usize88 fn size(&self) -> usize { 89 self.size 90 } 91 } 92 93 impl Drop for ExternalMapping { drop(&mut self)94 fn drop(&mut self) { 95 // This is safe because we own this memory range, and nobody else is holding a reference to 96 // it. 97 (self.unmap)(self.resource_id) 98 } 99 } 100 101 #[cfg(test)] 102 mod tests { 103 use super::*; 104 105 #[test] check_valid_external_map()106 fn check_valid_external_map() { 107 let map1: Map = |_resource_id| Ok((0xAAAABBBB, 500)); 108 let map2: Map = |_resource_id| Ok((0xBBBBAAAA, 1000)); 109 let unmap: Unmap = |_resource_id| {}; 110 let external_map1 = unsafe { ExternalMapping::new(0, map1, unmap).unwrap() }; 111 let external_map2 = unsafe { ExternalMapping::new(0, map2, unmap).unwrap() }; 112 113 assert_eq!(external_map1.as_ptr(), 0xAAAABBBB as *mut u8); 114 assert_eq!(external_map1.size(), 500); 115 assert_eq!(external_map2.as_ptr(), 0xBBBBAAAA as *mut u8); 116 assert_eq!(external_map2.size(), 1000); 117 } 118 119 #[test] check_invalid_external_map()120 fn check_invalid_external_map() { 121 let map1: Map = |_resource_id| Ok((0xAAAABBBB, 0)); 122 let map2: Map = |_resource_id| Ok((0, 500)); 123 let unmap: Unmap = |_resource_id| {}; 124 125 assert_eq!( 126 unsafe { ExternalMapping::new(0, map1, unmap) }, 127 Err(Error::InvalidSize) 128 ); 129 130 assert_eq!( 131 unsafe { ExternalMapping::new(0, map2, unmap) }, 132 Err(Error::NullAddress) 133 ); 134 } 135 136 #[test] 137 #[should_panic] check_external_map_drop()138 fn check_external_map_drop() { 139 let map = |_resource_id| Ok((0xAAAABBBB, 500)); 140 let unmap = |_resource_id| panic!(); 141 let _external_map = unsafe { ExternalMapping::new(0, map, unmap) }; 142 } 143 } 144