Skip to main content

mapped_vmo/
lib.rs

1// Copyright 2018 The Fuchsia 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//! A convenience crate for Zircon VMO objects mapped into memory.
6
7#![deny(missing_docs)]
8
9use fuchsia_runtime::vmar_root_self;
10use shared_buffer::SharedBuffer;
11use std::ops::{Deref, DerefMut};
12
13mod immutable;
14pub use immutable::{Error as ImmutableMappingError, ImmutableMapping};
15
16mod truncated_immutable;
17pub use truncated_immutable::{Error as TruncatedImmutableMappingError, TruncatedImmutableMapping};
18
19/// A safe wrapper around a mapped region of memory.
20///
21/// Note: this type implements `Deref`/`DerefMut` to the `SharedBuffer`
22/// type, which allows reading/writing from the underlying memory.
23/// Aside from creation and the `Drop` impl, all of the interesting
24/// functionality of this type is offered via `SharedBuffer`.
25#[derive(Debug)]
26pub struct Mapping {
27    buffer: SharedBuffer,
28    vmar: zx::Vmar,
29}
30
31impl Deref for Mapping {
32    type Target = SharedBuffer;
33    fn deref(&self) -> &Self::Target {
34        &self.buffer
35    }
36}
37
38impl DerefMut for Mapping {
39    fn deref_mut(&mut self) -> &mut Self::Target {
40        &mut self.buffer
41    }
42}
43
44impl Mapping {
45    /// Create a `Mapping` and map it in the root address space.
46    /// Returns the VMO that was mapped.
47    ///
48    /// The resulting VMO will not be resizeable.
49    pub fn allocate(size: usize) -> Result<(Self, zx::Vmo), zx::Status> {
50        let vmo = zx::Vmo::create(size as u64)?;
51        let flags = zx::VmarFlags::PERM_READ
52            | zx::VmarFlags::PERM_WRITE
53            | zx::VmarFlags::MAP_RANGE
54            | zx::VmarFlags::REQUIRE_NON_RESIZABLE;
55        let mapping = Self::create_from_vmo(&vmo, size, flags)?;
56        Ok((mapping, vmo))
57    }
58
59    /// Create a `Mapping` and map it in the root address space.
60    /// Returns the VMO that was mapped.
61    ///
62    /// The resulting VMO will not be resizeable.
63    pub fn allocate_with_name(size: usize, name: &str) -> Result<(Self, zx::Vmo), zx::Status> {
64        let name = zx::Name::new(name)?;
65        let vmo = zx::Vmo::create(size as u64)?;
66        vmo.set_name(&name)?;
67        let flags = zx::VmarFlags::PERM_READ
68            | zx::VmarFlags::PERM_WRITE
69            | zx::VmarFlags::MAP_RANGE
70            | zx::VmarFlags::REQUIRE_NON_RESIZABLE;
71        let mapping = Self::create_from_vmo(&vmo, size, flags)?;
72        Ok((mapping, vmo))
73    }
74
75    /// Create a `Mapping` from an existing VMO.
76    ///
77    /// Requires that the VMO was not created with the `RESIZABLE`
78    /// option, and returns `ZX_ERR_NOT_SUPPORTED` otherwise.
79    pub fn create_from_vmo(
80        vmo: &zx::Vmo,
81        size: usize,
82        flags: zx::VmarFlags,
83    ) -> Result<Self, zx::Status> {
84        Self::create_from_vmo_with_vmar_and_offset(vmo, &vmar_root_self(), 0, size, flags)
85    }
86
87    /// Create a `Mapping` from an existing VMO at `vmo_offset`.
88    ///
89    /// `vmo_offset` must be page-aligned. Requires that the VMO was not created
90    /// with the `RESIZABLE` option, and returns `ZX_ERR_NOT_SUPPORTED` otherwise.
91    pub fn create_from_vmo_with_offset(
92        vmo: &zx::Vmo,
93        vmo_offset: u64,
94        size: usize,
95        flags: zx::VmarFlags,
96    ) -> Result<Self, zx::Status> {
97        Self::create_from_vmo_with_vmar_and_offset(vmo, &vmar_root_self(), vmo_offset, size, flags)
98    }
99
100    /// Create a `Mapping` from an existing VMO in the specified `vmar`.
101    ///
102    /// Requires that the VMO was not created with the `RESIZABLE`
103    /// option, and returns `ZX_ERR_NOT_SUPPORTED` otherwise.
104    pub fn create_from_vmo_with_vmar(
105        vmo: &zx::Vmo,
106        vmar: &zx::Vmar,
107        size: usize,
108        flags: zx::VmarFlags,
109    ) -> Result<Self, zx::Status> {
110        Self::create_from_vmo_with_vmar_and_offset(vmo, vmar, 0, size, flags)
111    }
112
113    /// Create a `Mapping` from an existing VMO in the specified `vmar` at `vmo_offset`.
114    ///
115    /// `vmo_offset` must be page-aligned. Requires that the VMO was not created
116    /// with the `RESIZABLE` option, and returns `ZX_ERR_NOT_SUPPORTED` otherwise.
117    pub fn create_from_vmo_with_vmar_and_offset(
118        vmo: &zx::Vmo,
119        vmar: &zx::Vmar,
120        vmo_offset: u64,
121        size: usize,
122        flags: zx::VmarFlags,
123    ) -> Result<Self, zx::Status> {
124        let vmar = vmar.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
125        let flags = flags | zx::VmarFlags::REQUIRE_NON_RESIZABLE;
126        let addr = vmar.map(0, vmo, vmo_offset, size, flags)?;
127
128        // Safety:
129        //
130        // The memory behind this `SharedBuffer` is only accessible via
131        // methods on `SharedBuffer`.
132        //
133        // The underlying memory is accessible during any accesses to `SharedBuffer`:
134        // - It is only unmapped on `drop`
135        // - `SharedBuffer` is never exposed in a way that would allow it to live longer than
136        //   the `Mapping` itself
137        // - The underlying VMO is non-resizeable.
138        let buffer = unsafe { SharedBuffer::new(addr as *mut u8, size) };
139        Ok(Mapping { buffer, vmar })
140    }
141
142    /// Return the size of the mapping.
143    pub fn len(&self) -> usize {
144        self.buffer.len()
145    }
146}
147
148impl Drop for Mapping {
149    fn drop(&mut self) {
150        let (addr, size): (*mut u8, usize) = self.buffer.as_ptr_len();
151        let addr = addr as usize;
152
153        // Safety:
154        //
155        // The memory behind this `SharedBuffer` is only accessible
156        // via references to the internal `SharedBuffer`, which must
157        // have all been invalidated at this point. The memory is
158        // therefore safe to unmap.
159        unsafe {
160            let _ = self.vmar.unmap(addr, size);
161        }
162    }
163}
164
165#[cfg(test)]
166mod tests {
167    use super::*;
168
169    const PAGE_SIZE: usize = 4096;
170
171    #[test]
172    fn test_create() {
173        let size = PAGE_SIZE;
174        let (mapping, _vmo) = Mapping::allocate(size).unwrap();
175        assert_eq!(size, mapping.len());
176    }
177
178    #[test]
179    fn test_create_from_vmo() {
180        let size = PAGE_SIZE;
181        let flags = zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE;
182        {
183            // Mapping::create_from_vmo requires a non-resizable vmo
184            let vmo = zx::Vmo::create_with_opts(zx::VmoOptions::RESIZABLE, size as u64).unwrap();
185            let status = Mapping::create_from_vmo(&vmo, size, flags).unwrap_err();
186            assert_eq!(status, zx::Status::NOT_SUPPORTED);
187        }
188        {
189            let vmo = zx::Vmo::create(size as u64).unwrap();
190            let mapping = Mapping::create_from_vmo(&vmo, size, flags).unwrap();
191            assert_eq!(size, mapping.len());
192        }
193    }
194
195    #[test]
196    fn test_create_from_vmo_with_vmar() {
197        let size = PAGE_SIZE;
198        let flags = zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE;
199        let (child_vmar, _base) = vmar_root_self()
200            .allocate(
201                0,
202                size,
203                zx::VmarFlags::CAN_MAP_READ
204                    | zx::VmarFlags::CAN_MAP_WRITE
205                    | zx::VmarFlags::CAN_MAP_SPECIFIC,
206            )
207            .unwrap();
208        let vmo = zx::Vmo::create(size as u64).unwrap();
209        let mapping = Mapping::create_from_vmo_with_vmar(&vmo, &child_vmar, size, flags).unwrap();
210        assert_eq!(size, mapping.len());
211
212        let s = b"Hello custom VMAR";
213        mapping.write(s);
214        let mut buf = [0u8; 17];
215        vmo.read(&mut buf, 0).unwrap();
216        assert_eq!(s, &buf);
217    }
218
219    #[test]
220    fn test_create_from_vmo_with_offset() {
221        let flags = zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE;
222        let vmo = zx::Vmo::create((PAGE_SIZE * 2) as u64).unwrap();
223        let s = b"Hello non-zero VMO offset";
224        vmo.write(s, PAGE_SIZE as u64).unwrap();
225
226        let mapping =
227            Mapping::create_from_vmo_with_offset(&vmo, PAGE_SIZE as u64, PAGE_SIZE, flags).unwrap();
228        assert_eq!(PAGE_SIZE, mapping.len());
229
230        let mut buf = [0u8; 25];
231        mapping.read(&mut buf);
232        assert_eq!(s, &buf);
233
234        let unaligned_err =
235            Mapping::create_from_vmo_with_offset(&vmo, 1, PAGE_SIZE, flags).unwrap_err();
236        assert_eq!(zx::Status::INVALID_ARGS, unaligned_err);
237    }
238
239    #[test]
240    fn test_create_with_name() {
241        let size = PAGE_SIZE;
242        let (mapping, vmo) = Mapping::allocate_with_name(size, "TestName").unwrap();
243        assert_eq!(size, mapping.len());
244        assert_eq!("TestName", vmo.get_name().expect("Has name"));
245        let res = Mapping::allocate_with_name(size, "Invalid\0TestName");
246        assert_eq!(zx::Status::INVALID_ARGS, res.unwrap_err());
247    }
248
249    #[test]
250    fn test_mapping_read_write() {
251        let size = PAGE_SIZE;
252        let (mapping, vmo) = Mapping::allocate(size).unwrap();
253
254        let mut buf = [0; 128];
255
256        // We can write to the Vmo, and see the results in the mapping.
257        let s = b"Hello world";
258        vmo.write(s, 0).unwrap();
259        let slice = &mut buf[0..s.len()];
260        mapping.read(slice);
261        assert_eq!(s, slice);
262
263        // We can write to the mapping, and see the results in the Vmo.
264        let s = b"Goodbye world";
265        mapping.write(s);
266        let slice = &mut buf[0..s.len()];
267        vmo.read(slice, 0).unwrap();
268        assert_eq!(s, slice);
269    }
270}