• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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