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