[go: up one dir, main page]

magenta/
vmo.rs

1// Copyright 2017 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//! Type-safe bindings for Magenta vmo objects.
6
7use {AsHandleRef, Cookied, HandleBased, Handle, HandleRef, Status};
8use {sys, into_result};
9use std::{mem, ptr};
10
11/// An object representing a Magenta
12/// [virtual memory object](https://fuchsia.googlesource.com/magenta/+/master/docs/objects/vm_object.md).
13///
14/// As essentially a subtype of `Handle`, it can be freely interconverted.
15#[derive(Debug, Eq, PartialEq)]
16pub struct Vmo(Handle);
17impl_handle_based!(Vmo);
18impl Cookied for Vmo {}
19
20impl Vmo {
21    /// Create a virtual memory object.
22    ///
23    /// Wraps the
24    /// `mx_vmo_create`
25    /// syscall. See the
26    /// [Shared Memory: Virtual Memory Objects (VMOs)](https://fuchsia.googlesource.com/magenta/+/master/docs/concepts.md#Shared-Memory_Virtual-Memory-Objects-VMOs)
27    /// for more information.
28    pub fn create(size: u64, options: VmoOpts) -> Result<Vmo, Status> {
29        let mut handle = 0;
30        let status = unsafe { sys::mx_vmo_create(size, options as u32, &mut handle) };
31        into_result(status, ||
32            Vmo::from(Handle(handle)))
33    }
34
35    /// Read from a virtual memory object.
36    ///
37    /// Wraps the `mx_vmo_read` syscall.
38    pub fn read(&self, data: &mut [u8], offset: u64) -> Result<usize, Status> {
39        unsafe {
40            let mut actual = 0;
41            let status = sys::mx_vmo_read(self.raw_handle(), data.as_mut_ptr(),
42                offset, data.len(), &mut actual);
43            into_result(status, || actual)
44        }
45    }
46
47    /// Write to a virtual memory object.
48    ///
49    /// Wraps the `mx_vmo_write` syscall.
50    pub fn write(&self, data: &[u8], offset: u64) -> Result<usize, Status> {
51        unsafe {
52            let mut actual = 0;
53            let status = sys::mx_vmo_write(self.raw_handle(), data.as_ptr(),
54                offset, data.len(), &mut actual);
55            into_result(status, || actual)
56        }
57    }
58
59    /// Get the size of a virtual memory object.
60    ///
61    /// Wraps the `mx_vmo_get_size` syscall.
62    pub fn get_size(&self) -> Result<u64, Status> {
63        let mut size = 0;
64        let status = unsafe { sys::mx_vmo_get_size(self.raw_handle(), &mut size) };
65        into_result(status, || size)
66    }
67
68    /// Attempt to change the size of a virtual memory object.
69    ///
70    /// Wraps the `mx_vmo_set_size` syscall.
71    pub fn set_size(&self, size: u64) -> Result<(), Status> {
72        let status = unsafe { sys::mx_vmo_set_size(self.raw_handle(), size) };
73        into_result(status, || ())
74    }
75
76    /// Perform an operation on a range of a virtual memory object.
77    ///
78    /// Wraps the
79    /// [mx_vmo_op_range](https://fuchsia.googlesource.com/magenta/+/master/docs/syscalls/vmo_op_range.md)
80    /// syscall.
81    pub fn op_range(&self, op: VmoOp, offset: u64, size: u64) -> Result<(), Status> {
82        let status = unsafe {
83            sys::mx_vmo_op_range(self.raw_handle(), op as u32, offset, size, ptr::null_mut(), 0)
84        };
85        into_result(status, || ())
86    }
87
88    /// Look up a list of physical addresses corresponding to the pages held by the VMO from
89    /// `offset` to `offset`+`size`, and store them in `buffer`.
90    ///
91    /// Wraps the
92    /// [mx_vmo_op_range](https://fuchsia.googlesource.com/magenta/+/master/docs/syscalls/vmo_op_range.md)
93    /// syscall with MX_VMO_OP_LOOKUP.
94    pub fn lookup(&self, offset: u64, size: u64, buffer: &mut [sys::mx_paddr_t])
95        -> Result<(), Status>
96    {
97        let status = unsafe {
98            sys::mx_vmo_op_range(self.raw_handle(), sys::MX_VMO_OP_LOOKUP, offset, size,
99                buffer.as_mut_ptr() as *mut u8, buffer.len() * mem::size_of::<sys::mx_paddr_t>())
100        };
101        into_result(status, || ())
102    }
103
104    /// Create a new virtual memory object that clones a range of this one.
105    ///
106    /// Wraps the
107    /// [mx_vmo_clone](https://fuchsia.googlesource.com/magenta/+/master/docs/syscalls/vmo_clone.md)
108    /// syscall.
109    pub fn clone(&self, options: VmoCloneOpts, offset: u64, size: u64) -> Result<Vmo, Status> {
110        let mut out = 0;
111        let status = unsafe {
112            sys::mx_vmo_clone(self.raw_handle(), options as u32, offset, size, &mut out)
113        };
114        into_result(status, || Vmo::from(Handle(out)))
115    }
116}
117
118/// Options for creating virtual memory objects. None supported yet.
119#[repr(u32)]
120#[derive(Debug, Copy, Clone, Eq, PartialEq)]
121pub enum VmoOpts {
122    /// Default options.
123    Default = 0,
124}
125
126impl Default for VmoOpts {
127    fn default() -> Self {
128        VmoOpts::Default
129    }
130}
131
132#[repr(u32)]
133#[derive(Debug, Copy, Clone, Eq, PartialEq)]
134pub enum VmoOp {
135    /// Commit `size` bytes worth of pages starting at byte `offset` for the VMO.
136    Commit = sys::MX_VMO_OP_COMMIT,
137    /// Release a range of pages previously committed to the VMO from `offset` to `offset`+`size`.
138    Decommit = sys::MX_VMO_OP_DECOMMIT,
139    // Presently unsupported.
140    Lock = sys::MX_VMO_OP_LOCK,
141    // Presently unsupported.
142    Unlock = sys::MX_VMO_OP_UNLOCK,
143    /// Perform a cache sync operation.
144    CacheSync = sys::MX_VMO_OP_CACHE_SYNC,
145    /// Perform a cache invalidation operation.
146    CacheInvalidate = sys::MX_VMO_OP_CACHE_INVALIDATE,
147    /// Perform a cache clean operation.
148    CacheClean = sys::MX_VMO_OP_CACHE_CLEAN,
149    /// Perform cache clean and invalidation operations together.
150    CacheCleanInvalidate = sys::MX_VMO_OP_CACHE_CLEAN_INVALIDATE,
151}
152
153#[repr(u32)]
154#[derive(Debug, Copy, Clone, Eq, PartialEq)]
155pub enum VmoCloneOpts {
156    /// Create a copy-on-write clone.
157    CopyOnWrite = sys::MX_VMO_CLONE_COPY_ON_WRITE,
158}
159
160impl Default for VmoCloneOpts {
161    fn default() -> Self {
162        VmoCloneOpts::CopyOnWrite
163    }
164}
165
166#[cfg(test)]
167mod tests {
168    use super::*;
169
170    #[test]
171    fn vmo_get_size() {
172        let size = 16 * 1024 * 1024;
173        let vmo = Vmo::create(size, VmoOpts::Default).unwrap();
174        assert_eq!(size, vmo.get_size().unwrap());
175    }
176
177    #[test]
178    fn vmo_set_size() {
179        let start_size = 12;
180        let vmo = Vmo::create(start_size, VmoOpts::Default).unwrap();
181        assert_eq!(start_size, vmo.get_size().unwrap());
182
183        // Change the size and make sure the new size is reported
184        let new_size = 23;
185        assert!(vmo.set_size(new_size).is_ok());
186        assert_eq!(new_size, vmo.get_size().unwrap());
187    }
188
189    #[test]
190    fn vmo_read_write() {
191        let mut vec1 = vec![0; 16];
192        let vmo = Vmo::create(vec1.len() as u64, VmoOpts::Default).unwrap();
193        assert_eq!(vmo.write(b"abcdef", 0), Ok(6));
194        assert_eq!(16, vmo.read(&mut vec1, 0).unwrap());
195        assert_eq!(b"abcdef", &vec1[0..6]);
196        assert_eq!(vmo.write(b"123", 2), Ok(3));
197        assert_eq!(16, vmo.read(&mut vec1, 0).unwrap());
198        assert_eq!(b"ab123f", &vec1[0..6]);
199        assert_eq!(15, vmo.read(&mut vec1, 1).unwrap());
200        assert_eq!(b"b123f", &vec1[0..5]);
201    }
202
203    #[test]
204    fn vmo_op_range_unsupported() {
205        let vmo = Vmo::create(12, VmoOpts::Default).unwrap();
206        assert_eq!(vmo.op_range(VmoOp::Lock, 0, 1), Err(Status::ErrNotSupported));
207        assert_eq!(vmo.op_range(VmoOp::Unlock, 0, 1), Err(Status::ErrNotSupported));
208    }
209
210    #[test]
211    fn vmo_lookup() {
212        let vmo = Vmo::create(12, VmoOpts::Default).unwrap();
213        let mut buffer = vec![0; 2];
214
215        // Lookup will fail as it is not committed yet.
216        assert_eq!(vmo.lookup(0, 12, &mut buffer), Err(Status::ErrNoMemory));
217
218        // Commit and try again.
219        assert_eq!(vmo.op_range(VmoOp::Commit, 0, 12), Ok(()));
220        assert_eq!(vmo.lookup(0, 12, &mut buffer), Ok(()));
221        assert_ne!(buffer[0], 0);
222        assert_eq!(buffer[1], 0);
223
224        // If we decommit then lookup should go back to failing.
225        assert_eq!(vmo.op_range(VmoOp::Decommit, 0, 12), Ok(()));
226        assert_eq!(vmo.lookup(0, 12, &mut buffer), Err(Status::ErrNoMemory));
227    }
228
229    #[test]
230    fn vmo_cache() {
231        let vmo = Vmo::create(12, VmoOpts::Default).unwrap();
232
233        // Cache operations should all succeed.
234        assert_eq!(vmo.op_range(VmoOp::CacheSync, 0, 12), Ok(()));
235        assert_eq!(vmo.op_range(VmoOp::CacheInvalidate, 0, 12), Ok(()));
236        assert_eq!(vmo.op_range(VmoOp::CacheClean, 0, 12), Ok(()));
237        assert_eq!(vmo.op_range(VmoOp::CacheCleanInvalidate, 0, 12), Ok(()));
238    }
239
240    #[test]
241    fn vmo_clone() {
242        let original = Vmo::create(12, VmoOpts::Default).unwrap();
243        assert_eq!(original.write(b"one", 0), Ok(3));
244
245        // Clone the VMO, and make sure it contains what we expect.
246        let clone = original.clone(VmoCloneOpts::CopyOnWrite, 0, 10).unwrap();
247        let mut read_buffer = vec![0; 16];
248        assert_eq!(clone.read(&mut read_buffer, 0), Ok(10));
249        assert_eq!(&read_buffer[0..3], b"one");
250
251        // Writing to the original will affect the clone too, surprisingly.
252        assert_eq!(original.write(b"two", 0), Ok(3));
253        assert_eq!(original.read(&mut read_buffer, 0), Ok(12));
254        assert_eq!(&read_buffer[0..3], b"two");
255        assert_eq!(clone.read(&mut read_buffer, 0), Ok(10));
256        assert_eq!(&read_buffer[0..3], b"two");
257
258        // However, writing to the clone will not affect the original
259        assert_eq!(clone.write(b"three", 0), Ok(5));
260        assert_eq!(original.read(&mut read_buffer, 0), Ok(12));
261        assert_eq!(&read_buffer[0..3], b"two");
262        assert_eq!(clone.read(&mut read_buffer, 0), Ok(10));
263        assert_eq!(&read_buffer[0..5], b"three");
264
265        // And now that the copy-on-write has happened, writing to the original will not affect the
266        // clone. How bizarre.
267        assert_eq!(original.write(b"four", 0), Ok(4));
268        assert_eq!(original.read(&mut read_buffer, 0), Ok(12));
269        assert_eq!(&read_buffer[0..4], b"four");
270        assert_eq!(clone.read(&mut read_buffer, 0), Ok(10));
271        assert_eq!(&read_buffer[0..5], b"three");
272    }
273}