Skip to main content

zx/
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 Zircon vmo objects.
6
7use crate::{
8    Bti, Koid, Name, NullableHandle, ObjectQuery, Resource, Rights, Status, Topic, ok, sys,
9};
10use bitflags::bitflags;
11use std::mem::MaybeUninit;
12use std::ptr;
13use zerocopy::{FromBytes, Immutable};
14use zx_sys::PadByte;
15
16/// An object representing a Zircon
17/// [virtual memory object](https://fuchsia.dev/fuchsia-src/concepts/objects/vm_object.md).
18///
19/// As essentially a subtype of `NullableHandle`, it can be freely interconverted.
20#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
21#[repr(transparent)]
22pub struct Vmo(NullableHandle);
23impl_handle_based!(Vmo);
24
25static_assert_align!(
26    #[doc="Ergonomic equivalent of [sys::zx_info_vmo_t]. Must be ABI-compatible with it."]
27    #[repr(C)]
28    #[derive(Debug, Copy, Clone, Eq, PartialEq, FromBytes, Immutable)]
29    <sys::zx_info_vmo_t> pub struct VmoInfo {
30        pub koid <koid>: Koid,
31        pub name <name>: Name,
32        pub size_bytes <size_bytes>: u64,
33        pub parent_koid <parent_koid>: Koid,
34        pub num_children <num_children>: usize,
35        pub num_mappings <num_mappings>: usize,
36        pub share_count <share_count>: usize,
37        pub flags <flags>: VmoInfoFlags,
38        padding1: [PadByte; 4],
39        pub committed_bytes <committed_bytes>: u64,
40        pub handle_rights <handle_rights>: Rights,
41        cache_policy <cache_policy>: u32,
42        pub metadata_bytes <metadata_bytes>: u64,
43        pub committed_change_events <committed_change_events>: u64,
44        pub populated_bytes <populated_bytes>: u64,
45        pub committed_private_bytes <committed_private_bytes>: u64,
46        pub populated_private_bytes <populated_private_bytes>: u64,
47        pub committed_scaled_bytes <committed_scaled_bytes>: u64,
48        pub populated_scaled_bytes <populated_scaled_bytes>: u64,
49        pub committed_fractional_scaled_bytes <committed_fractional_scaled_bytes>: u64,
50        pub populated_fractional_scaled_bytes <populated_fractional_scaled_bytes>: u64,
51    }
52);
53
54impl VmoInfo {
55    pub fn cache_policy(&self) -> CachePolicy {
56        CachePolicy::from(self.cache_policy)
57    }
58}
59
60impl Default for VmoInfo {
61    fn default() -> VmoInfo {
62        Self::from(sys::zx_info_vmo_t::default())
63    }
64}
65
66impl From<sys::zx_info_vmo_t> for VmoInfo {
67    fn from(info: sys::zx_info_vmo_t) -> VmoInfo {
68        zerocopy::transmute!(info)
69    }
70}
71
72struct VmoInfoQuery;
73unsafe impl ObjectQuery for VmoInfoQuery {
74    const TOPIC: Topic = Topic::VMO;
75    type InfoTy = sys::zx_info_vmo_t;
76}
77
78impl Vmo {
79    /// Create a virtual memory object.
80    ///
81    /// Wraps the
82    /// `zx_vmo_create`
83    /// syscall. See the
84    /// [Shared Memory: Virtual Memory Objects (VMOs)](https://fuchsia.dev/fuchsia-src/concepts/kernel/concepts#shared_memory_virtual_memory_objects_vmos)
85    /// for more information.
86    pub fn create(size: u64) -> Result<Vmo, Status> {
87        Vmo::create_with_opts(VmoOptions::from_bits_truncate(0), size)
88    }
89
90    /// Create a virtual memory object with options.
91    ///
92    /// Wraps the
93    /// `zx_vmo_create`
94    /// syscall, allowing options to be passed.
95    pub fn create_with_opts(opts: VmoOptions, size: u64) -> Result<Vmo, Status> {
96        let mut handle = 0;
97        let status = unsafe { sys::zx_vmo_create(size, opts.bits(), &mut handle) };
98        ok(status)?;
99        unsafe { Ok(Vmo::from(NullableHandle::from_raw(handle))) }
100    }
101
102    /// Create a physically contiguous virtual memory object.
103    ///
104    /// Wraps the
105    /// [`zx_vmo_create_contiguous`](https://fuchsia.dev/fuchsia-src/reference/syscalls/vmo_create_contiguous) syscall.
106    pub fn create_contiguous(bti: &Bti, size: usize, alignment_log2: u32) -> Result<Vmo, Status> {
107        let mut vmo_handle = sys::zx_handle_t::default();
108        let status = unsafe {
109            // SAFETY: regular system call with no unsafe parameters.
110            sys::zx_vmo_create_contiguous(bti.raw_handle(), size, alignment_log2, &mut vmo_handle)
111        };
112        ok(status)?;
113        unsafe {
114            // SAFETY: The syscall docs claim that upon success, vmo_handle will be a valid
115            // handle to a virtual memory object.
116            Ok(Vmo::from(NullableHandle::from_raw(vmo_handle)))
117        }
118    }
119
120    /// Read from a virtual memory object.
121    ///
122    /// Wraps the `zx_vmo_read` syscall.
123    pub fn read(&self, data: &mut [u8], offset: u64) -> Result<(), Status> {
124        unsafe {
125            let status = sys::zx_vmo_read(self.raw_handle(), data.as_mut_ptr(), offset, data.len());
126            ok(status)
127        }
128    }
129
130    /// Provides a very thin wrapper over `zx_vmo_read`.
131    ///
132    /// # Safety
133    ///
134    /// Callers must guarantee that the buffer is valid to write to.
135    pub unsafe fn read_raw<T: FromBytes>(
136        &self,
137        buffer: *mut T,
138        buffer_length: usize,
139        offset: u64,
140    ) -> Result<(), Status> {
141        let status = unsafe {
142            sys::zx_vmo_read(
143                self.raw_handle(),
144                buffer.cast::<u8>(),
145                offset,
146                buffer_length * std::mem::size_of::<T>(),
147            )
148        };
149        ok(status)
150    }
151
152    /// Same as read, but reads into memory that might not be initialized, returning an initialized
153    /// slice of bytes on success.
154    ///
155    /// `Copy` is required to ensure that there are no custom `Drop` implementations. It is
156    /// difficult to correctly run custom drop code after initializing a `MaybeUninit`.
157    pub fn read_uninit<'a, T: Copy + FromBytes>(
158        &self,
159        data: &'a mut [MaybeUninit<T>],
160        offset: u64,
161    ) -> Result<&'a mut [T], Status> {
162        // SAFETY: This system call requires that the pointer and length we pass are valid to write
163        // to, which we guarantee here by getting the pointer and length from a valid slice.
164        unsafe {
165            self.read_raw(
166                // TODO(https://fxbug.dev/42079723) use MaybeUninit::slice_as_mut_ptr when stable
167                data.as_mut_ptr().cast::<T>(),
168                data.len(),
169                offset,
170            )?
171        }
172        // TODO(https://fxbug.dev/42079723) use MaybeUninit::slice_assume_init_mut when stable
173        Ok(
174            // SAFETY: We're converting &mut [MaybeUninit<u8>] back to &mut [u8], which is only
175            // valid to do if all elements of `data` have actually been initialized. Here we
176            // have to trust that the kernel didn't lie when it said it wrote to the entire
177            // buffer, but as long as that assumption is valid them it's safe to assume this
178            // slice is init.
179            unsafe { std::slice::from_raw_parts_mut(data.as_mut_ptr().cast::<T>(), data.len()) },
180        )
181    }
182
183    /// Same as read, but returns a Vec.
184    pub fn read_to_vec<T: Copy + FromBytes>(
185        &self,
186        offset: u64,
187        length: u64,
188    ) -> Result<Vec<T>, Status> {
189        let len = length.try_into().map_err(|_| Status::INVALID_ARGS)?;
190        let mut buffer = Vec::with_capacity(len);
191        self.read_uninit(buffer.spare_capacity_mut(), offset)?;
192        unsafe {
193            // SAFETY: since read_uninit succeeded we know that we can consider the buffer
194            // initialized.
195            buffer.set_len(len);
196        }
197        Ok(buffer)
198    }
199
200    /// Same as read, but returns an array.
201    pub fn read_to_array<T: Copy + FromBytes, const N: usize>(
202        &self,
203        offset: u64,
204    ) -> Result<[T; N], Status> {
205        let mut array: [MaybeUninit<T>; N] = [const { MaybeUninit::uninit() }; N];
206
207        // SAFETY: T is FromBytes, which means that any bit pattern is valid. Interpreting
208        // [MaybeUninit<T>] as [MaybeUninit<u8>] is safe because T's alignment requirements
209        // are larger than u8.
210        //
211        // TODO(https://fxbug.dev/42079727): Use MaybeUninit::slice_as_bytes_mut once stable.
212        let buffer = unsafe {
213            std::slice::from_raw_parts_mut(
214                array.as_mut_ptr().cast::<MaybeUninit<u8>>(),
215                N * std::mem::size_of::<T>(),
216            )
217        };
218
219        self.read_uninit(buffer, offset)?;
220        // SAFETY: This is safe because we have initialized all the elements in
221        // the array (since `read_uninit` returned successfully).
222        //
223        // TODO(https://fxbug.dev/42079725): replace with MaybeUninit::array_assume_init.
224        let buffer = array.map(|a| unsafe { a.assume_init() });
225        Ok(buffer)
226    }
227
228    /// Same as read, but returns a `T`.
229    pub fn read_to_object<T: Copy + FromBytes>(&self, offset: u64) -> Result<T, Status> {
230        let mut object = MaybeUninit::<T>::uninit();
231        // SAFETY: T is FromBytes, which means that any bit pattern is valid. Interpreting
232        // MaybeUninit<T> as [MaybeUninit<u8>] is safe because T's alignment requirements
233        // are larger than, or equal to, u8's.
234        //
235        // TODO(https://fxbug.dev/42079727): Use MaybeUninit::as_bytes_mut once stable.
236        let buffer = unsafe {
237            std::slice::from_raw_parts_mut(
238                object.as_mut_ptr().cast::<MaybeUninit<u8>>(),
239                std::mem::size_of::<T>(),
240            )
241        };
242        self.read_uninit(buffer, offset)?;
243
244        // SAFETY: The call to `read_uninit` succeeded so we know that `object`
245        // has been initialized.
246        let object = unsafe { object.assume_init() };
247        Ok(object)
248    }
249
250    /// Write to a virtual memory object.
251    ///
252    /// Wraps the `zx_vmo_write` syscall.
253    pub fn write(&self, data: &[u8], offset: u64) -> Result<(), Status> {
254        unsafe {
255            let status = sys::zx_vmo_write(self.raw_handle(), data.as_ptr(), offset, data.len());
256            ok(status)
257        }
258    }
259
260    /// Efficiently transfers data from one VMO to another.
261    pub fn transfer_data(
262        &self,
263        options: TransferDataOptions,
264        offset: u64,
265        length: u64,
266        src_vmo: &Vmo,
267        src_offset: u64,
268    ) -> Result<(), Status> {
269        let status = unsafe {
270            sys::zx_vmo_transfer_data(
271                self.raw_handle(),
272                options.bits(),
273                offset,
274                length,
275                src_vmo.raw_handle(),
276                src_offset,
277            )
278        };
279        ok(status)
280    }
281
282    /// Get the size of a virtual memory object.
283    ///
284    /// Wraps the `zx_vmo_get_size` syscall.
285    pub fn get_size(&self) -> Result<u64, Status> {
286        let mut size = 0;
287        let status = unsafe { sys::zx_vmo_get_size(self.raw_handle(), &mut size) };
288        ok(status).map(|()| size)
289    }
290
291    /// Attempt to change the size of a virtual memory object.
292    ///
293    /// Wraps the `zx_vmo_set_size` syscall.
294    pub fn set_size(&self, size: u64) -> Result<(), Status> {
295        let status = unsafe { sys::zx_vmo_set_size(self.raw_handle(), size) };
296        ok(status)
297    }
298
299    /// Get the stream size of a virtual memory object.
300    ///
301    /// Wraps the `zx_vmo_get_stream_size` syscall.
302    pub fn get_stream_size(&self) -> Result<u64, Status> {
303        let mut size = 0;
304        let status = unsafe { sys::zx_vmo_get_stream_size(self.raw_handle(), &mut size) };
305        ok(status).map(|()| size)
306    }
307
308    /// Attempt to set the stream size of a virtual memory object.
309    ///
310    /// Wraps the `zx_vmo_set_stream_size` syscall.
311    pub fn set_stream_size(&self, size: u64) -> Result<(), Status> {
312        let status = unsafe { sys::zx_vmo_set_stream_size(self.raw_handle(), size) };
313        ok(status)
314    }
315
316    /// Attempt to change the cache policy of a virtual memory object.
317    ///
318    /// Wraps the `zx_vmo_set_cache_policy` syscall.
319    pub fn set_cache_policy(&self, cache_policy: CachePolicy) -> Result<(), Status> {
320        let status =
321            unsafe { sys::zx_vmo_set_cache_policy(self.raw_handle(), cache_policy as u32) };
322        ok(status)
323    }
324
325    /// Perform an operation on a range of a virtual memory object.
326    ///
327    /// Wraps the
328    /// [zx_vmo_op_range](https://fuchsia.dev/fuchsia-src/reference/syscalls/vmo_op_range.md)
329    /// syscall.
330    pub fn op_range(&self, op: VmoOp, offset: u64, size: u64) -> Result<(), Status> {
331        let status = unsafe {
332            sys::zx_vmo_op_range(self.raw_handle(), op.into_raw(), offset, size, ptr::null_mut(), 0)
333        };
334        ok(status)
335    }
336
337    /// Wraps the [zx_object_get_info](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_info.md)
338    /// syscall for the ZX_INFO_VMO topic.
339    pub fn info(&self) -> Result<VmoInfo, Status> {
340        Ok(VmoInfo::from(self.0.get_info_single::<VmoInfoQuery>()?))
341    }
342
343    /// Create a new virtual memory object that clones a range of this one.
344    ///
345    /// Wraps the
346    /// [zx_vmo_create_child](https://fuchsia.dev/fuchsia-src/reference/syscalls/vmo_create_child.md)
347    /// syscall.
348    pub fn create_child(
349        &self,
350        opts: VmoChildOptions,
351        offset: u64,
352        size: u64,
353    ) -> Result<Vmo, Status> {
354        let mut out = 0;
355        let status = unsafe {
356            sys::zx_vmo_create_child(self.raw_handle(), opts.bits(), offset, size, &mut out)
357        };
358        ok(status)?;
359        unsafe { Ok(Vmo::from(NullableHandle::from_raw(out))) }
360    }
361
362    /// Replace a VMO, adding execute rights.
363    ///
364    /// Wraps the
365    /// [zx_vmo_replace_as_executable](https://fuchsia.dev/fuchsia-src/reference/syscalls/vmo_replace_as_executable.md)
366    /// syscall.
367    pub fn replace_as_executable(self, vmex: &Resource) -> Result<Vmo, Status> {
368        let mut out = 0;
369        let status = unsafe {
370            sys::zx_vmo_replace_as_executable(self.raw_handle(), vmex.raw_handle(), &mut out)
371        };
372        // zx_vmo_replace_as_executable always invalidates the passed in handle
373        // so we need to forget 'self' without executing its drop which will attempt
374        // to close the now-invalid handle value.
375        std::mem::forget(self);
376        ok(status)?;
377        unsafe { Ok(Vmo::from(NullableHandle::from_raw(out))) }
378    }
379}
380
381bitflags! {
382    /// Options that may be used when creating a `Vmo`.
383    #[repr(transparent)]
384    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
385    pub struct VmoOptions: u32 {
386        const RESIZABLE = sys::ZX_VMO_RESIZABLE;
387        const TRAP_DIRTY = sys::ZX_VMO_TRAP_DIRTY;
388        const UNBOUNDED = sys::ZX_VMO_UNBOUNDED;
389    }
390}
391
392/// Flags that may be set when receiving info on a `Vmo`.
393#[repr(transparent)]
394#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, FromBytes, Immutable)]
395pub struct VmoInfoFlags(u32);
396
397bitflags! {
398    impl VmoInfoFlags : u32 {
399        const PAGED = sys::ZX_INFO_VMO_TYPE_PAGED;
400        const RESIZABLE = sys::ZX_INFO_VMO_RESIZABLE;
401        const IS_COW_CLONE = sys::ZX_INFO_VMO_IS_COW_CLONE;
402        const VIA_HANDLE = sys::ZX_INFO_VMO_VIA_HANDLE;
403        const VIA_MAPPING = sys::ZX_INFO_VMO_VIA_MAPPING;
404        const PAGER_BACKED = sys::ZX_INFO_VMO_PAGER_BACKED;
405        const CONTIGUOUS = sys::ZX_INFO_VMO_CONTIGUOUS;
406        const DISCARDABLE = sys::ZX_INFO_VMO_DISCARDABLE;
407        const IMMUTABLE = sys::ZX_INFO_VMO_IMMUTABLE;
408        const VIA_IOB_HANDLE = sys::ZX_INFO_VMO_VIA_IOB_HANDLE;
409    }
410}
411
412impl std::fmt::Debug for VmoInfoFlags {
413    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
414        bitflags::parser::to_writer(self, f)
415    }
416}
417
418bitflags! {
419    /// Options that may be used when creating a `Vmo` child.
420    #[repr(transparent)]
421    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
422    pub struct VmoChildOptions: u32 {
423        const SNAPSHOT = sys::ZX_VMO_CHILD_SNAPSHOT;
424        const SNAPSHOT_AT_LEAST_ON_WRITE = sys::ZX_VMO_CHILD_SNAPSHOT_AT_LEAST_ON_WRITE;
425        const RESIZABLE = sys::ZX_VMO_CHILD_RESIZABLE;
426        const SLICE = sys::ZX_VMO_CHILD_SLICE;
427        const NO_WRITE = sys::ZX_VMO_CHILD_NO_WRITE;
428        const REFERENCE = sys::ZX_VMO_CHILD_REFERENCE;
429        const SNAPSHOT_MODIFIED = sys::ZX_VMO_CHILD_SNAPSHOT_MODIFIED;
430    }
431}
432
433bitflags! {
434    /// Options that may be used when transferring data between VMOs.
435    #[repr(transparent)]
436    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
437    pub struct TransferDataOptions: u32 {
438    }
439}
440
441/// VM Object opcodes
442#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
443#[repr(transparent)]
444pub struct VmoOp(u32);
445impl VmoOp {
446    pub fn from_raw(raw: u32) -> VmoOp {
447        VmoOp(raw)
448    }
449    pub fn into_raw(self) -> u32 {
450        self.0
451    }
452}
453
454// VM Object Cache Policies.
455#[derive(Debug, Copy, Clone, Eq, PartialEq)]
456#[repr(u32)]
457pub enum CachePolicy {
458    Cached = sys::ZX_CACHE_POLICY_CACHED,
459    UnCached = sys::ZX_CACHE_POLICY_UNCACHED,
460    UnCachedDevice = sys::ZX_CACHE_POLICY_UNCACHED_DEVICE,
461    WriteCombining = sys::ZX_CACHE_POLICY_WRITE_COMBINING,
462    Unknown = u32::MAX,
463}
464
465impl From<u32> for CachePolicy {
466    fn from(v: u32) -> Self {
467        match v {
468            sys::ZX_CACHE_POLICY_CACHED => CachePolicy::Cached,
469            sys::ZX_CACHE_POLICY_UNCACHED => CachePolicy::UnCached,
470            sys::ZX_CACHE_POLICY_UNCACHED_DEVICE => CachePolicy::UnCachedDevice,
471            sys::ZX_CACHE_POLICY_WRITE_COMBINING => CachePolicy::WriteCombining,
472            _ => CachePolicy::Unknown,
473        }
474    }
475}
476
477impl Into<u32> for CachePolicy {
478    fn into(self) -> u32 {
479        match self {
480            CachePolicy::Cached => sys::ZX_CACHE_POLICY_CACHED,
481            CachePolicy::UnCached => sys::ZX_CACHE_POLICY_UNCACHED,
482            CachePolicy::UnCachedDevice => sys::ZX_CACHE_POLICY_UNCACHED_DEVICE,
483            CachePolicy::WriteCombining => sys::ZX_CACHE_POLICY_WRITE_COMBINING,
484            CachePolicy::Unknown => u32::MAX,
485        }
486    }
487}
488
489assoc_values!(VmoOp, [
490    COMMIT =           sys::ZX_VMO_OP_COMMIT;
491    DECOMMIT =         sys::ZX_VMO_OP_DECOMMIT;
492    LOCK =             sys::ZX_VMO_OP_LOCK;
493    UNLOCK =           sys::ZX_VMO_OP_UNLOCK;
494    CACHE_SYNC =       sys::ZX_VMO_OP_CACHE_SYNC;
495    CACHE_INVALIDATE = sys::ZX_VMO_OP_CACHE_INVALIDATE;
496    CACHE_CLEAN =      sys::ZX_VMO_OP_CACHE_CLEAN;
497    CACHE_CLEAN_INVALIDATE = sys::ZX_VMO_OP_CACHE_CLEAN_INVALIDATE;
498    ZERO =             sys::ZX_VMO_OP_ZERO;
499    TRY_LOCK =         sys::ZX_VMO_OP_TRY_LOCK;
500    DONT_NEED =        sys::ZX_VMO_OP_DONT_NEED;
501    ALWAYS_NEED =      sys::ZX_VMO_OP_ALWAYS_NEED;
502    PREFETCH =         sys::ZX_VMO_OP_PREFETCH;
503]);
504
505unsafe_handle_properties!(object: Vmo,
506    props: [
507        {query_ty: VMO_CONTENT_SIZE, tag: VmoContentSizeTag, prop_ty: u64, get:get_content_size, set: set_content_size},
508    ]
509);
510
511#[cfg(test)]
512mod tests {
513    use super::*;
514    use crate::{Iommu, IommuDescStub, ObjectType};
515    use fidl_fuchsia_kernel as fkernel;
516    use fuchsia_component::client::connect_channel_to_protocol;
517    use test_case::test_case;
518    use zerocopy::KnownLayout;
519
520    #[test]
521    fn vmo_create_contiguous() {
522        use zx::{Channel, MonotonicInstant};
523        let (client_end, server_end) = Channel::create();
524        connect_channel_to_protocol::<fkernel::IommuResourceMarker>(server_end).unwrap();
525        let service = fkernel::IommuResourceSynchronousProxy::new(client_end);
526        let resource =
527            service.get(MonotonicInstant::INFINITE).expect("couldn't get iommu resource");
528        // This test and fuchsia-zircon are different crates, so we need
529        // to use from_raw to convert between the zx handle and this test handle.
530        // See https://fxbug.dev/42173139 for details.
531        let resource = unsafe { Resource::from(NullableHandle::from_raw(resource.into_raw())) };
532        let iommu = Iommu::create_stub(&resource, IommuDescStub::default()).unwrap();
533        let bti = Bti::create(&iommu, 0).unwrap();
534
535        let vmo = Vmo::create_contiguous(&bti, 8192, 0).unwrap();
536        let info = vmo.as_handle_ref().basic_info().unwrap();
537        assert_eq!(info.object_type, ObjectType::VMO);
538
539        let vmo_info = vmo.info().unwrap();
540        assert!(vmo_info.flags.contains(VmoInfoFlags::CONTIGUOUS));
541    }
542
543    #[test]
544    fn vmo_get_size() {
545        let size = 16 * 1024 * 1024;
546        let vmo = Vmo::create(size).unwrap();
547        assert_eq!(size, vmo.get_size().unwrap());
548    }
549
550    #[test]
551    fn vmo_set_size() {
552        // Use a multiple of page size to match VMOs page aligned size
553        let start_size = 4096;
554        let vmo = Vmo::create_with_opts(VmoOptions::RESIZABLE, start_size).unwrap();
555        assert_eq!(start_size, vmo.get_size().unwrap());
556
557        // Change the size and make sure the new size is reported
558        let new_size = 8192;
559        assert!(vmo.set_size(new_size).is_ok());
560        assert_eq!(new_size, vmo.get_size().unwrap());
561    }
562
563    #[test]
564    fn vmo_get_info_default() {
565        let size = 4096;
566        let vmo = Vmo::create(size).unwrap();
567        let info = vmo.info().unwrap();
568        assert!(!info.flags.contains(VmoInfoFlags::PAGER_BACKED));
569        assert!(info.flags.contains(VmoInfoFlags::PAGED));
570    }
571
572    #[test]
573    fn vmo_get_child_info() {
574        let size = 4096;
575        let vmo = Vmo::create(size).unwrap();
576        let info = vmo.info().unwrap();
577        assert!(!info.flags.contains(VmoInfoFlags::IS_COW_CLONE));
578
579        let child = vmo.create_child(VmoChildOptions::SNAPSHOT, 0, 512).unwrap();
580        let info = child.info().unwrap();
581        assert!(info.flags.contains(VmoInfoFlags::IS_COW_CLONE));
582
583        let child = vmo.create_child(VmoChildOptions::SNAPSHOT_AT_LEAST_ON_WRITE, 0, 512).unwrap();
584        let info = child.info().unwrap();
585        assert!(info.flags.contains(VmoInfoFlags::IS_COW_CLONE));
586
587        let child = vmo.create_child(VmoChildOptions::SLICE, 0, 512).unwrap();
588        let info = child.info().unwrap();
589        assert!(!info.flags.contains(VmoInfoFlags::IS_COW_CLONE));
590    }
591
592    #[test]
593    fn vmo_set_size_fails_on_non_resizable() {
594        let size = 4096;
595        let vmo = Vmo::create(size).unwrap();
596        assert_eq!(size, vmo.get_size().unwrap());
597
598        let new_size = 8192;
599        assert_eq!(Err(Status::UNAVAILABLE), vmo.set_size(new_size));
600        assert_eq!(size, vmo.get_size().unwrap());
601    }
602
603    #[test_case(0)]
604    #[test_case(1)]
605    fn vmo_read_to_array(read_offset: usize) {
606        const ACTUAL_SIZE: usize = 5;
607        const ACTUAL: [u8; ACTUAL_SIZE] = [1, 2, 3, 4, 5];
608        let vmo = Vmo::create(ACTUAL.len() as u64).unwrap();
609        vmo.write(&ACTUAL, 0).unwrap();
610        let read_len = ACTUAL_SIZE - read_offset;
611        assert_eq!(
612            &vmo.read_to_array::<u8, ACTUAL_SIZE>(read_offset as u64).unwrap()[..read_len],
613            &ACTUAL[read_offset..]
614        );
615    }
616
617    #[test_case(0)]
618    #[test_case(1)]
619    fn vmo_read_to_vec(read_offset: usize) {
620        const ACTUAL_SIZE: usize = 4;
621        const ACTUAL: [u8; ACTUAL_SIZE] = [6, 7, 8, 9];
622        let vmo = Vmo::create(ACTUAL.len() as u64).unwrap();
623        vmo.write(&ACTUAL, 0).unwrap();
624        let read_len = ACTUAL_SIZE - read_offset;
625        assert_eq!(
626            &vmo.read_to_vec::<u8>(read_offset as u64, read_len as u64).unwrap(),
627            &ACTUAL[read_offset..]
628        );
629    }
630
631    #[test_case(0)]
632    #[test_case(1)]
633    fn vmo_read_to_object(read_offset: usize) {
634        #[repr(C)]
635        #[derive(Copy, Clone, Debug, Eq, KnownLayout, FromBytes, PartialEq)]
636        struct Object {
637            a: u8,
638            b: u8,
639        }
640
641        const ACTUAL_SIZE: usize = std::mem::size_of::<Object>();
642        const ACTUAL: [u8; ACTUAL_SIZE + 1] = [10, 11, 12];
643        let vmo = Vmo::create(ACTUAL.len() as u64).unwrap();
644        vmo.write(&ACTUAL, 0).unwrap();
645        assert_eq!(
646            vmo.read_to_object::<Object>(read_offset as u64).unwrap(),
647            Object { a: ACTUAL[read_offset], b: ACTUAL[1 + read_offset] }
648        );
649    }
650
651    #[test]
652    fn vmo_read_write() {
653        let mut vec1 = vec![0; 16];
654        let vmo = Vmo::create(4096 as u64).unwrap();
655        assert!(vmo.write(b"abcdef", 0).is_ok());
656        assert!(vmo.read(&mut vec1, 0).is_ok());
657        assert_eq!(b"abcdef", &vec1[0..6]);
658        assert!(vmo.write(b"123", 2).is_ok());
659        assert!(vmo.read(&mut vec1, 0).is_ok());
660        assert_eq!(b"ab123f", &vec1[0..6]);
661
662        // Read one byte into the vmo.
663        assert!(vmo.read(&mut vec1, 1).is_ok());
664        assert_eq!(b"b123f", &vec1[0..5]);
665
666        assert_eq!(&vmo.read_to_vec::<u8>(0, 6).expect("read_to_vec failed"), b"ab123f");
667    }
668
669    #[test]
670    fn vmo_child_snapshot() {
671        let size = 4096 * 2;
672        let vmo = Vmo::create(size).unwrap();
673
674        vmo.write(&[1; 4096], 0).unwrap();
675        vmo.write(&[2; 4096], 4096).unwrap();
676
677        let child = vmo.create_child(VmoChildOptions::SNAPSHOT, 0, size).unwrap();
678
679        child.write(&[3; 4096], 0).unwrap();
680
681        vmo.write(&[4; 4096], 0).unwrap();
682        vmo.write(&[5; 4096], 4096).unwrap();
683
684        let mut page = [0; 4096];
685
686        // SNAPSHOT child observes no further changes to parent VMO.
687        child.read(&mut page[..], 0).unwrap();
688        assert_eq!(&page[..], &[3; 4096][..]);
689        child.read(&mut page[..], 4096).unwrap();
690        assert_eq!(&page[..], &[2; 4096][..]);
691    }
692
693    #[test]
694    fn vmo_child_snapshot_at_least_on_write() {
695        let size = 4096 * 2;
696        let vmo = Vmo::create(size).unwrap();
697
698        vmo.write(&[1; 4096], 0).unwrap();
699        vmo.write(&[2; 4096], 4096).unwrap();
700
701        let child = vmo.create_child(VmoChildOptions::SNAPSHOT_AT_LEAST_ON_WRITE, 0, size).unwrap();
702
703        child.write(&[3; 4096], 0).unwrap();
704
705        vmo.write(&[4; 4096], 0).unwrap();
706        vmo.write(&[5; 4096], 4096).unwrap();
707
708        let mut page = [0; 4096];
709
710        // SNAPSHOT_AT_LEAST_ON_WRITE child may observe changes to pages it has not yet written to,
711        // but such behavior is not guaranteed.
712        child.read(&mut page[..], 0).unwrap();
713        assert_eq!(&page[..], &[3; 4096][..]);
714        child.read(&mut page[..], 4096).unwrap();
715        assert!(
716            &page[..] == &[2; 4096][..] || &page[..] == &[5; 4096][..],
717            "expected page of 2 or 5, got {:?}",
718            &page[..]
719        );
720    }
721
722    #[test]
723    fn vmo_child_no_write() {
724        let size = 4096;
725        let vmo = Vmo::create(size).unwrap();
726        vmo.write(&[1; 4096], 0).unwrap();
727
728        let child =
729            vmo.create_child(VmoChildOptions::SLICE | VmoChildOptions::NO_WRITE, 0, size).unwrap();
730        assert_eq!(child.write(&[3; 4096], 0), Err(Status::ACCESS_DENIED));
731    }
732
733    #[test]
734    fn vmo_op_range_unsupported() {
735        let vmo = Vmo::create(12).unwrap();
736        assert_eq!(vmo.op_range(VmoOp::LOCK, 0, 1), Err(Status::NOT_SUPPORTED));
737        assert_eq!(vmo.op_range(VmoOp::UNLOCK, 0, 1), Err(Status::NOT_SUPPORTED));
738    }
739
740    #[test]
741    fn vmo_cache() {
742        let vmo = Vmo::create(12).unwrap();
743
744        // Cache operations should all succeed.
745        assert_eq!(vmo.op_range(VmoOp::CACHE_SYNC, 0, 12), Ok(()));
746        assert_eq!(vmo.op_range(VmoOp::CACHE_INVALIDATE, 0, 12), Ok(()));
747        assert_eq!(vmo.op_range(VmoOp::CACHE_CLEAN, 0, 12), Ok(()));
748        assert_eq!(vmo.op_range(VmoOp::CACHE_CLEAN_INVALIDATE, 0, 12), Ok(()));
749    }
750
751    #[test]
752    fn vmo_create_child() {
753        let original = Vmo::create(16).unwrap();
754        assert!(original.write(b"one", 0).is_ok());
755
756        // Clone the VMO, and make sure it contains what we expect.
757        let clone =
758            original.create_child(VmoChildOptions::SNAPSHOT_AT_LEAST_ON_WRITE, 0, 16).unwrap();
759        let mut read_buffer = vec![0; 16];
760        assert!(clone.read(&mut read_buffer, 0).is_ok());
761        assert_eq!(&read_buffer[0..3], b"one");
762
763        // Writing to the original will not affect the clone.
764        assert!(original.write(b"two", 0).is_ok());
765        assert!(original.read(&mut read_buffer, 0).is_ok());
766        assert_eq!(&read_buffer[0..3], b"two");
767        assert!(clone.read(&mut read_buffer, 0).is_ok());
768        assert_eq!(&read_buffer[0..3], b"one");
769
770        // However, writing to the clone will not affect the original.
771        assert!(clone.write(b"three", 0).is_ok());
772        assert!(original.read(&mut read_buffer, 0).is_ok());
773        assert_eq!(&read_buffer[0..3], b"two");
774        assert!(clone.read(&mut read_buffer, 0).is_ok());
775        assert_eq!(&read_buffer[0..5], b"three");
776    }
777
778    #[test]
779    fn vmo_replace_as_executeable() {
780        use zx::{Channel, MonotonicInstant};
781
782        let vmo = Vmo::create(16).unwrap();
783
784        let info = vmo.as_handle_ref().basic_info().unwrap();
785        assert!(!info.rights.contains(Rights::EXECUTE));
786
787        let (client_end, server_end) = Channel::create();
788        connect_channel_to_protocol::<fkernel::VmexResourceMarker>(server_end).unwrap();
789        let service = fkernel::VmexResourceSynchronousProxy::new(client_end);
790        let resource = service.get(MonotonicInstant::INFINITE).expect("couldn't get vmex resource");
791        let resource =
792            unsafe { crate::Resource::from(NullableHandle::from_raw(resource.into_raw())) };
793
794        let exec_vmo = vmo.replace_as_executable(&resource).unwrap();
795        let info = exec_vmo.as_handle_ref().basic_info().unwrap();
796        assert!(info.rights.contains(Rights::EXECUTE));
797    }
798
799    #[test]
800    fn vmo_content_size() {
801        let start_size = 1024;
802        let vmo = Vmo::create_with_opts(VmoOptions::RESIZABLE, start_size).unwrap();
803        assert_eq!(vmo.get_content_size().unwrap(), start_size);
804        vmo.set_content_size(&0).unwrap();
805        assert_eq!(vmo.get_content_size().unwrap(), 0);
806
807        // write should not change content size.
808        let content = b"abcdef";
809        assert!(vmo.write(content, 0).is_ok());
810        assert_eq!(vmo.get_content_size().unwrap(), 0);
811    }
812
813    #[test]
814    fn vmo_zero() {
815        let vmo = Vmo::create(16).unwrap();
816        let content = b"0123456789abcdef";
817        assert!(vmo.write(content, 0).is_ok());
818        let mut buf = vec![0u8; 16];
819        assert!(vmo.read(&mut buf[..], 0).is_ok());
820        assert_eq!(&buf[..], content);
821
822        assert!(vmo.op_range(VmoOp::ZERO, 0, 16).is_ok());
823        assert!(vmo.read(&mut buf[..], 0).is_ok());
824        assert_eq!(&buf[..], &[0u8; 16]);
825    }
826
827    #[test]
828    fn vmo_stream_size() {
829        let start_size = 1300;
830        let vmo = Vmo::create_with_opts(VmoOptions::UNBOUNDED, start_size).unwrap();
831        assert_eq!(vmo.get_stream_size().unwrap(), start_size);
832        vmo.set_stream_size(0).unwrap();
833        assert_eq!(vmo.get_stream_size().unwrap(), 0);
834
835        // write should not change content size.
836        let content = b"abcdef";
837        assert!(vmo.write(content, 0).is_ok());
838        assert_eq!(vmo.get_stream_size().unwrap(), 0);
839
840        // stream size can also grow.
841        let mut buf = vec![1; 6];
842        vmo.set_stream_size(6).unwrap();
843        assert!(vmo.read(&mut buf, 0).is_ok());
844        // growing will zero new bytes.
845        assert_eq!(buf, vec![0; 6]);
846    }
847}