memory_mapped_vmo/
memory_mapped_vmo.rs1use std::mem::{ManuallyDrop, align_of, size_of};
6use std::ops::Deref;
7use zerocopy::FromBytes;
8
9use crate::Error;
10
11#[repr(C)]
13pub struct MemoryMappedVmo<const WRITABLE: bool> {
14 map_addr: usize,
15 vmo_size: usize,
16}
17
18impl<const WRITABLE: bool> MemoryMappedVmo<WRITABLE> {
19 pub unsafe fn new(vmo: &zx::Vmo) -> Result<Self, zx::Status> {
25 let vmo_size = vmo.get_content_size()? as usize;
26
27 let mut flags = zx::VmarFlags::PERM_READ
28 | zx::VmarFlags::ALLOW_FAULTS
29 | zx::VmarFlags::REQUIRE_NON_RESIZABLE;
30 if WRITABLE {
31 flags |= zx::VmarFlags::PERM_WRITE;
32 }
33
34 let map_addr = fuchsia_runtime::vmar_root_self().map(0, vmo, 0, vmo_size, flags)?;
35 Ok(MemoryMappedVmo { map_addr, vmo_size })
36 }
37
38 pub(crate) unsafe fn create_alias_with_manually_drop(&self) -> ManuallyDrop<Self> {
45 ManuallyDrop::new(MemoryMappedVmo { map_addr: self.map_addr, vmo_size: self.vmo_size })
46 }
47
48 pub fn vmo_size(&self) -> usize {
51 self.vmo_size
52 }
53
54 pub(crate) fn validate_and_get_ptr<T>(
58 &self,
59 byte_offset: usize,
60 num_elements: usize,
61 ) -> Result<*const T, Error> {
62 if byte_offset % align_of::<T>() == 0 {
63 if let Some(num_bytes) = size_of::<T>().checked_mul(num_elements) {
64 if let Some(end) = byte_offset.checked_add(num_bytes) {
65 if end <= self.vmo_size {
66 return Ok((self.map_addr + byte_offset) as *const T);
67 }
68 }
69 }
70 }
71
72 Err(Error::InvalidInput)
73 }
74
75 pub fn get_slice<'a, T: FromBytes>(
79 &'a self,
80 byte_offset: usize,
81 num_elements: usize,
82 ) -> Result<&'a [T], Error> {
83 let ptr = self.validate_and_get_ptr(byte_offset, num_elements)?;
84 unsafe { Ok(std::slice::from_raw_parts(ptr, num_elements)) }
85 }
86
87 pub fn get_object<'a, T: FromBytes>(&'a self, byte_offset: usize) -> Result<&'a T, Error> {
91 let ptr = self.validate_and_get_ptr(byte_offset, 1)?;
92 unsafe { Ok(&*ptr) }
93 }
94}
95
96impl MemoryMappedVmo<true> {
97 pub(crate) fn validate_and_get_mut_ptr<T>(
99 &mut self,
100 byte_offset: usize,
101 num_elements: usize,
102 ) -> Result<*mut T, Error> {
103 Ok(self.validate_and_get_ptr::<T>(byte_offset, num_elements)? as *mut T)
104 }
105
106 pub fn get_slice_mut<'a, T: FromBytes>(
110 &'a mut self,
111 byte_offset: usize,
112 num_elements: usize,
113 ) -> Result<&'a mut [T], Error> {
114 let ptr = self.validate_and_get_mut_ptr(byte_offset, num_elements)?;
115 unsafe { Ok(std::slice::from_raw_parts_mut(ptr, num_elements)) }
116 }
117
118 pub fn get_object_mut<'a, T: FromBytes>(
122 &'a mut self,
123 byte_offset: usize,
124 ) -> Result<&'a mut T, Error> {
125 let ptr = self.validate_and_get_mut_ptr(byte_offset, 1)?;
126 unsafe { Ok(&mut *ptr) }
127 }
128
129 pub fn into_readonly(self) -> MemoryMappedVmo<false> {
131 let this = ManuallyDrop::new(self);
132 MemoryMappedVmo { map_addr: this.map_addr, vmo_size: this.vmo_size }
133 }
134}
135
136impl Deref for MemoryMappedVmo<true> {
137 type Target = MemoryMappedVmo<false>;
138
139 fn deref(&self) -> &Self::Target {
140 unsafe { &*(self as *const MemoryMappedVmo<true> as *const MemoryMappedVmo<false>) }
143 }
144}
145
146impl<const WRITABLE: bool> Drop for MemoryMappedVmo<WRITABLE> {
147 fn drop(&mut self) {
148 unsafe {
150 fuchsia_runtime::vmar_root_self()
151 .unmap(self.map_addr, self.vmo_size)
152 .expect("failed to unmap MemoryMappedVmo");
153 }
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use crate::{Error, ReadOnlyMemoryMappedVmo, ReadWriteMemoryMappedVmo};
160 use assert_matches::assert_matches;
161 use std::mem::size_of;
162
163 const TEST_DATA: [u64; 4] = [11, 22, 33, 44];
165 const TEST_DATA_SIZE: usize = size_of::<u64>() * TEST_DATA.len();
166
167 #[test]
168 fn test_vmo_size() {
169 let vmo = zx::Vmo::create(TEST_DATA_SIZE as u64).unwrap();
170 let m = unsafe { ReadWriteMemoryMappedVmo::new(&vmo) }.unwrap();
171
172 assert_eq!(m.vmo_size(), TEST_DATA_SIZE);
173 }
174
175 #[test]
176 fn test_write_objects_read_slice() {
177 let vmo = zx::Vmo::create(TEST_DATA_SIZE as u64).unwrap();
178
179 {
181 let mut m = unsafe { ReadWriteMemoryMappedVmo::new(&vmo) }.unwrap();
182 for (i, val) in TEST_DATA.iter().enumerate() {
183 *m.get_object_mut(size_of::<u64>() * i).unwrap() = *val;
184 }
185 }
186
187 {
189 let m = unsafe { ReadOnlyMemoryMappedVmo::new(&vmo) }.unwrap();
190 assert_eq!(*m.get_slice::<u64>(0, 4).unwrap(), TEST_DATA);
191 }
192 }
193
194 #[test]
195 fn test_write_slice_read_objects() {
196 let vmo = zx::Vmo::create(TEST_DATA_SIZE as u64).unwrap();
197
198 {
200 let mut m = unsafe { ReadWriteMemoryMappedVmo::new(&vmo) }.unwrap();
201 m.get_slice_mut(0, 4).unwrap().copy_from_slice(&TEST_DATA);
202 }
203
204 {
206 let m = unsafe { ReadOnlyMemoryMappedVmo::new(&vmo) }.unwrap();
207 for (i, expected_val) in TEST_DATA.iter().enumerate() {
208 let actual_val: &u64 = m.get_object(size_of::<u64>() * i).unwrap();
209 assert_eq!(*actual_val, *expected_val, "value mismatch at i={}", i);
210 }
211 }
212 }
213
214 #[test]
215 fn test_write_slice_read_subslices() {
216 const COUNT: usize = 4;
217 let vmo = zx::Vmo::create((size_of::<u64>() * COUNT) as u64).unwrap();
218
219 let mut m = unsafe { ReadWriteMemoryMappedVmo::new(&vmo) }.unwrap();
221 m.get_slice_mut::<u64>(0, COUNT).unwrap().copy_from_slice(&[11, 22, 33, 44]);
222
223 const SECOND_ELEM_BYTE_OFFSET: usize = size_of::<u64>();
225 assert_eq!(*m.get_slice::<u64>(SECOND_ELEM_BYTE_OFFSET, 0).unwrap(), []);
226 assert_eq!(*m.get_slice::<u64>(SECOND_ELEM_BYTE_OFFSET, 1).unwrap(), [22]);
227 assert_eq!(*m.get_slice::<u64>(SECOND_ELEM_BYTE_OFFSET, 2).unwrap(), [22, 33]);
228 assert_eq!(*m.get_slice::<u64>(SECOND_ELEM_BYTE_OFFSET, 3).unwrap(), [22, 33, 44]);
229 }
230
231 #[test]
232 fn test_uninitialized_is_zero() {
233 const COUNT: usize = 4;
234 let vmo = zx::Vmo::create((size_of::<u64>() * COUNT) as u64).unwrap();
235 let m = unsafe { ReadOnlyMemoryMappedVmo::new(&vmo) }.unwrap();
236
237 assert_eq!(*m.get_slice::<u64>(0, COUNT).unwrap(), [0; COUNT]);
239 }
240
241 #[test]
242 fn test_range_errors() {
243 const COUNT: usize = 4;
244 let vmo = zx::Vmo::create((size_of::<u64>() * COUNT) as u64).unwrap();
245 let m = unsafe { ReadOnlyMemoryMappedVmo::new(&vmo) }.unwrap();
246
247 const MISALIGNED_OFFSET: usize = size_of::<u64>() - 1;
249 assert_matches!(m.get_object::<u64>(MISALIGNED_OFFSET), Err(Error::InvalidInput));
250
251 const SECOND_ELEM_BYTE_OFFSET: usize = size_of::<u64>();
253 assert_matches!(
254 m.get_slice::<u64>(SECOND_ELEM_BYTE_OFFSET, COUNT),
255 Err(Error::InvalidInput)
256 );
257 }
258
259 #[test]
260 fn test_into_readonly() {
261 let vmo = zx::Vmo::create(TEST_DATA_SIZE as u64).unwrap();
262 let mut m = unsafe { ReadWriteMemoryMappedVmo::new(&vmo) }.unwrap();
263 m.get_slice_mut(0, 4).unwrap().copy_from_slice(&TEST_DATA);
264
265 let m: ReadOnlyMemoryMappedVmo = m.into_readonly();
266 assert_eq!(*m.get_slice::<u64>(0, 4).unwrap(), TEST_DATA);
267 }
268}