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 Zircon vmo objects.
6 
7 use {AsHandleRef, Cookied, HandleBased, Handle, HandleRef, Status};
8 use {sys, ok};
9 use std::{mem, ptr};
10 
11 /// An object representing a Zircon
12 /// [virtual memory object](https://fuchsia.googlesource.com/zircon/+/master/docs/objects/vm_object.md).
13 ///
14 /// As essentially a subtype of `Handle`, it can be freely interconverted.
15 #[derive(Debug, Eq, PartialEq)]
16 pub struct Vmo(Handle);
17 impl_handle_based!(Vmo);
18 impl Cookied for Vmo {}
19 
20 impl Vmo {
21     /// Create a virtual memory object.
22     ///
23     /// Wraps the
24     /// `zx_vmo_create`
25     /// syscall. See the
26     /// [Shared Memory: Virtual Memory Objects (VMOs)](https://fuchsia.googlesource.com/zircon/+/master/docs/concepts.md#Shared-Memory_Virtual-Memory-Objects-VMOs)
27     /// for more information.
create(size: u64) -> Result<Vmo, Status>28     pub fn create(size: u64) -> Result<Vmo, Status> {
29         let mut handle = 0;
30         let opts = 0;
31         let status = unsafe { sys::zx_vmo_create(size, opts, &mut handle) };
32         ok(status)?;
33         unsafe {
34             Ok(Vmo::from(Handle::from_raw(handle)))
35         }
36     }
37 
38     /// Read from a virtual memory object.
39     ///
40     /// Wraps the `zx_vmo_read` syscall.
read(&self, data: &mut [u8], offset: u64) -> Result<usize, Status>41     pub fn read(&self, data: &mut [u8], offset: u64) -> Result<usize, Status> {
42         unsafe {
43             let mut actual = 0;
44             let status = sys::zx_vmo_read(self.raw_handle(), data.as_mut_ptr(),
45                 offset, data.len(), &mut actual);
46             ok(status).map(|()| actual)
47         }
48     }
49 
50     /// Write to a virtual memory object.
51     ///
52     /// Wraps the `zx_vmo_write` syscall.
write(&self, data: &[u8], offset: u64) -> Result<usize, Status>53     pub fn write(&self, data: &[u8], offset: u64) -> Result<usize, Status> {
54         unsafe {
55             let mut actual = 0;
56             let status = sys::zx_vmo_write(self.raw_handle(), data.as_ptr(),
57                 offset, data.len(), &mut actual);
58             ok(status).map(|()| actual)
59         }
60     }
61 
62     /// Get the size of a virtual memory object.
63     ///
64     /// Wraps the `zx_vmo_get_size` syscall.
get_size(&self) -> Result<u64, Status>65     pub fn get_size(&self) -> Result<u64, Status> {
66         let mut size = 0;
67         let status = unsafe { sys::zx_vmo_get_size(self.raw_handle(), &mut size) };
68         ok(status).map(|()| size)
69     }
70 
71     /// Attempt to change the size of a virtual memory object.
72     ///
73     /// Wraps the `zx_vmo_set_size` syscall.
set_size(&self, size: u64) -> Result<(), Status>74     pub fn set_size(&self, size: u64) -> Result<(), Status> {
75         let status = unsafe { sys::zx_vmo_set_size(self.raw_handle(), size) };
76         ok(status)
77     }
78 
79     /// Perform an operation on a range of a virtual memory object.
80     ///
81     /// Wraps the
82     /// [zx_vmo_op_range](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/vmo_op_range.md)
83     /// syscall.
op_range(&self, op: VmoOp, offset: u64, size: u64) -> Result<(), Status>84     pub fn op_range(&self, op: VmoOp, offset: u64, size: u64) -> Result<(), Status> {
85         let status = unsafe {
86             sys::zx_vmo_op_range(self.raw_handle(), op.into_raw(), offset, size, ptr::null_mut(), 0)
87         };
88         ok(status)
89     }
90 
91     /// Look up a list of physical addresses corresponding to the pages held by the VMO from
92     /// `offset` to `offset`+`size`, and store them in `buffer`.
93     ///
94     /// Wraps the
95     /// [zx_vmo_op_range](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/vmo_op_range.md)
96     /// syscall with ZX_VMO_OP_LOOKUP.
lookup(&self, offset: u64, size: u64, buffer: &mut [sys::zx_paddr_t]) -> Result<(), Status>97     pub fn lookup(&self, offset: u64, size: u64, buffer: &mut [sys::zx_paddr_t])
98         -> Result<(), Status>
99     {
100         let status = unsafe {
101             sys::zx_vmo_op_range(self.raw_handle(), VmoOp::LOOKUP.into_raw(), offset, size,
102                 buffer.as_mut_ptr() as *mut u8, buffer.len() * mem::size_of::<sys::zx_paddr_t>())
103         };
104         ok(status)
105     }
106 
107     /// Create a new virtual memory object that clones a range of this one.
108     ///
109     /// Wraps the
110     /// [zx_vmo_clone](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/vmo_clone.md)
111     /// syscall.
clone(&self, offset: u64, size: u64) -> Result<Vmo, Status>112     pub fn clone(&self, offset: u64, size: u64) -> Result<Vmo, Status> {
113         let mut out = 0;
114         let opts = sys::ZX_VMO_CLONE_COPY_ON_WRITE;
115         let status = unsafe {
116             sys::zx_vmo_clone(self.raw_handle(), opts, offset, size, &mut out)
117         };
118         ok(status)?;
119         unsafe { Ok(Vmo::from(Handle::from_raw(out))) }
120     }
121 }
122 
123 /// VM Object opcodes
124 #[repr(C)]
125 #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
126 pub struct VmoOp(u32);
127 impl VmoOp {
from_raw(raw: u32) -> VmoOp128     pub fn from_raw(raw: u32) -> VmoOp {
129         VmoOp(raw)
130     }
into_raw(self) -> u32131     pub fn into_raw(self) -> u32 {
132         self.0
133     }
134 }
135 
136 assoc_consts!(VmoOp, [
137     COMMIT =           sys::ZX_VMO_OP_COMMIT;
138     DECOMMIT =         sys::ZX_VMO_OP_DECOMMIT;
139     LOCK =             sys::ZX_VMO_OP_LOCK;
140     UNLOCK =           sys::ZX_VMO_OP_UNLOCK;
141     LOOKUP =           sys::ZX_VMO_OP_LOOKUP;
142     CACHE_SYNC =       sys::ZX_VMO_OP_CACHE_SYNC;
143     CACHE_INVALIDATE = sys::ZX_VMO_OP_CACHE_INVALIDATE;
144     CACHE_CLEAN =      sys::ZX_VMO_OP_CACHE_CLEAN;
145     CACHE_CLEAN_INVALIDATE = sys::ZX_VMO_OP_CACHE_CLEAN_INVALIDATE;
146 ]);
147 
148 
149 #[cfg(test)]
150 mod tests {
151     use super::*;
152 
153     #[test]
vmo_get_size()154     fn vmo_get_size() {
155         let size = 16 * 1024 * 1024;
156         let vmo = Vmo::create(size).unwrap();
157         assert_eq!(size, vmo.get_size().unwrap());
158     }
159 
160     #[test]
vmo_set_size()161     fn vmo_set_size() {
162         let start_size = 12;
163         let vmo = Vmo::create(start_size).unwrap();
164         assert_eq!(start_size, vmo.get_size().unwrap());
165 
166         // Change the size and make sure the new size is reported
167         let new_size = 23;
168         assert!(vmo.set_size(new_size).is_ok());
169         assert_eq!(new_size, vmo.get_size().unwrap());
170     }
171 
172     #[test]
vmo_read_write()173     fn vmo_read_write() {
174         let mut vec1 = vec![0; 16];
175         let vmo = Vmo::create(vec1.len() as u64).unwrap();
176         assert_eq!(vmo.write(b"abcdef", 0), Ok(6));
177         assert_eq!(16, vmo.read(&mut vec1, 0).unwrap());
178         assert_eq!(b"abcdef", &vec1[0..6]);
179         assert_eq!(vmo.write(b"123", 2), Ok(3));
180         assert_eq!(16, vmo.read(&mut vec1, 0).unwrap());
181         assert_eq!(b"ab123f", &vec1[0..6]);
182         assert_eq!(15, vmo.read(&mut vec1, 1).unwrap());
183         assert_eq!(b"b123f", &vec1[0..5]);
184     }
185 
186     #[test]
vmo_op_range_unsupported()187     fn vmo_op_range_unsupported() {
188         let vmo = Vmo::create(12).unwrap();
189         assert_eq!(vmo.op_range(VmoOp::LOCK, 0, 1), Err(Status::NOT_SUPPORTED));
190         assert_eq!(vmo.op_range(VmoOp::UNLOCK, 0, 1), Err(Status::NOT_SUPPORTED));
191     }
192 
193     #[test]
vmo_lookup()194     fn vmo_lookup() {
195         let vmo = Vmo::create(12).unwrap();
196         let mut buffer = vec![0; 2];
197 
198         // Lookup will fail as it is not committed yet.
199         assert_eq!(vmo.lookup(0, 12, &mut buffer), Err(Status::NO_MEMORY));
200 
201         // COMMIT and try again.
202         assert_eq!(vmo.op_range(VmoOp::COMMIT, 0, 12), Ok(()));
203         assert_eq!(vmo.lookup(0, 12, &mut buffer), Ok(()));
204         assert_ne!(buffer[0], 0);
205         assert_eq!(buffer[1], 0);
206 
207         // If we decommit then lookup should go back to failing.
208         assert_eq!(vmo.op_range(VmoOp::DECOMMIT, 0, 12), Ok(()));
209         assert_eq!(vmo.lookup(0, 12, &mut buffer), Err(Status::NO_MEMORY));
210     }
211 
212     #[test]
vmo_cache()213     fn vmo_cache() {
214         let vmo = Vmo::create(12).unwrap();
215 
216         // Cache operations should all succeed.
217         assert_eq!(vmo.op_range(VmoOp::CACHE_SYNC, 0, 12), Ok(()));
218         assert_eq!(vmo.op_range(VmoOp::CACHE_INVALIDATE, 0, 12), Ok(()));
219         assert_eq!(vmo.op_range(VmoOp::CACHE_CLEAN, 0, 12), Ok(()));
220         assert_eq!(vmo.op_range(VmoOp::CACHE_CLEAN_INVALIDATE, 0, 12), Ok(()));
221     }
222 
223     #[test]
vmo_clone()224     fn vmo_clone() {
225         let original = Vmo::create(12).unwrap();
226         assert_eq!(original.write(b"one", 0), Ok(3));
227 
228         // Clone the VMO, and make sure it contains what we expect.
229         let clone = original.clone(0, 10).unwrap();
230         let mut read_buffer = vec![0; 16];
231         assert_eq!(clone.read(&mut read_buffer, 0), Ok(10));
232         assert_eq!(&read_buffer[0..3], b"one");
233 
234         // Writing to the original will affect the clone too, surprisingly.
235         assert_eq!(original.write(b"two", 0), Ok(3));
236         assert_eq!(original.read(&mut read_buffer, 0), Ok(12));
237         assert_eq!(&read_buffer[0..3], b"two");
238         assert_eq!(clone.read(&mut read_buffer, 0), Ok(10));
239         assert_eq!(&read_buffer[0..3], b"two");
240 
241         // However, writing to the clone will not affect the original
242         assert_eq!(clone.write(b"three", 0), Ok(5));
243         assert_eq!(original.read(&mut read_buffer, 0), Ok(12));
244         assert_eq!(&read_buffer[0..3], b"two");
245         assert_eq!(clone.read(&mut read_buffer, 0), Ok(10));
246         assert_eq!(&read_buffer[0..5], b"three");
247 
248         // And now that the copy-on-write has happened, writing to the original will not affect the
249         // clone. How bizarre.
250         assert_eq!(original.write(b"four", 0), Ok(4));
251         assert_eq!(original.read(&mut read_buffer, 0), Ok(12));
252         assert_eq!(&read_buffer[0..4], b"four");
253         assert_eq!(clone.read(&mut read_buffer, 0), Ok(10));
254         assert_eq!(&read_buffer[0..5], b"three");
255     }
256 }
257