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