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