zx/
vmar.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 vmar objects.
6
7use crate::clock::Clock;
8use crate::iob::Iob;
9use crate::{
10    AsHandleRef, Handle, HandleBased, HandleRef, Koid, Name, ObjectQuery, Status, Timeline, Topic,
11    Vmo, object_get_info, object_get_info_single, object_get_info_vec, ok, sys,
12};
13use bitflags::bitflags;
14use std::mem::MaybeUninit;
15use zerocopy::{FromBytes, Immutable};
16use zx_sys::PadByte;
17
18/// An object representing a Zircon
19/// [virtual memory address region](https://fuchsia.dev/fuchsia-src/concepts/objects/vm_address_region.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 Vmar(Handle);
25impl_handle_based!(Vmar);
26
27sys::zx_info_vmar_t!(VmarInfo);
28
29impl From<sys::zx_info_vmar_t> for VmarInfo {
30    fn from(sys::zx_info_vmar_t { base, len }: sys::zx_info_vmar_t) -> VmarInfo {
31        VmarInfo { base, len }
32    }
33}
34
35// VmarInfo is able to be safely replaced with a byte representation and is a PoD type.
36unsafe impl ObjectQuery for VmarInfo {
37    const TOPIC: Topic = Topic::VMAR;
38    type InfoTy = VmarInfo;
39}
40
41struct VmarMapsInfo;
42unsafe impl ObjectQuery for VmarMapsInfo {
43    const TOPIC: Topic = Topic::VMAR_MAPS;
44    type InfoTy = MapInfo;
45}
46
47static_assert_align!(
48    /// Ergonomic wrapper around `zx_info_maps_t`.
49    #[repr(C)]
50    #[derive(Copy, Clone, FromBytes, Immutable)]
51    <sys::zx_info_maps_t> pub struct MapInfo {
52        pub name <name>: Name,
53        pub base <base>: usize,
54        pub size <size>: usize,
55        pub depth <depth>: usize,
56        r#type <r#type>: sys::zx_info_maps_type_t,
57        u <u>: sys::InfoMapsTypeUnion,
58    }
59);
60
61impl MapInfo {
62    pub fn new(
63        name: Name,
64        base: usize,
65        size: usize,
66        depth: usize,
67        details: MapDetails<'_>,
68    ) -> Result<MapInfo, Status> {
69        let (map_type, map_details_union) = match details {
70            MapDetails::None => (
71                sys::ZX_INFO_MAPS_TYPE_NONE,
72                sys::InfoMapsTypeUnion { mapping: Default::default() },
73            ),
74            MapDetails::AddressSpace => (
75                sys::ZX_INFO_MAPS_TYPE_ASPACE,
76                sys::InfoMapsTypeUnion { mapping: Default::default() },
77            ),
78            MapDetails::Vmar => (
79                sys::ZX_INFO_MAPS_TYPE_VMAR,
80                sys::InfoMapsTypeUnion { mapping: Default::default() },
81            ),
82            MapDetails::Mapping(mapping_details) => (
83                sys::ZX_INFO_MAPS_TYPE_MAPPING,
84                sys::InfoMapsTypeUnion {
85                    mapping: {
86                        let mut mapping: sys::zx_info_maps_mapping_t = Default::default();
87                        mapping.mmu_flags = mapping_details.mmu_flags.bits();
88                        mapping.vmo_koid = mapping_details.vmo_koid.raw_koid();
89                        mapping.vmo_offset = mapping_details.vmo_offset;
90                        mapping.committed_bytes = mapping_details.committed_bytes;
91                        mapping.populated_bytes = mapping_details.populated_bytes;
92                        mapping.committed_private_bytes = mapping_details.committed_private_bytes;
93                        mapping.populated_private_bytes = mapping_details.populated_private_bytes;
94                        mapping.committed_scaled_bytes = mapping_details.committed_scaled_bytes;
95                        mapping.populated_scaled_bytes = mapping_details.populated_scaled_bytes;
96                        mapping.committed_fractional_scaled_bytes =
97                            mapping_details.committed_fractional_scaled_bytes;
98                        mapping.populated_fractional_scaled_bytes =
99                            mapping_details.populated_fractional_scaled_bytes;
100                        mapping
101                    },
102                },
103            ),
104            MapDetails::Unknown => {
105                return Err(Status::INVALID_ARGS);
106            }
107        };
108        Ok(MapInfo { name, base, size, depth, r#type: map_type, u: map_details_union })
109    }
110
111    pub fn details<'a>(&'a self) -> MapDetails<'a> {
112        match self.r#type {
113            sys::ZX_INFO_MAPS_TYPE_NONE => MapDetails::None,
114            sys::ZX_INFO_MAPS_TYPE_ASPACE => MapDetails::AddressSpace,
115            sys::ZX_INFO_MAPS_TYPE_VMAR => MapDetails::Vmar,
116            sys::ZX_INFO_MAPS_TYPE_MAPPING => {
117                // SAFETY: these values are produced by the kernel or `new()` which guarantees the
118                // discriminant matches the union contents.
119                let raw_mapping = unsafe { &self.u.mapping };
120                let mapping_details = zerocopy::transmute_ref!(raw_mapping);
121                MapDetails::Mapping(mapping_details)
122            }
123            _ => MapDetails::Unknown,
124        }
125    }
126}
127
128impl std::cmp::PartialEq for MapInfo {
129    fn eq(&self, other: &Self) -> bool {
130        self.name == other.name
131            && self.base == other.base
132            && self.size == other.size
133            && self.depth == other.depth
134            && self.details() == other.details()
135    }
136}
137impl std::cmp::Eq for MapInfo {}
138
139impl std::fmt::Debug for MapInfo {
140    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141        f.debug_struct("MapInfo")
142            .field("name", &self.name)
143            .field("base", &format_args!("{:#x}", self.base))
144            .field("size", &self.size)
145            .field("depth", &self.depth)
146            .field("details", &self.details())
147            .finish()
148    }
149}
150
151#[derive(Copy, Clone, Debug, Eq, PartialEq)]
152pub enum MapDetails<'a> {
153    /// The underlying value returned by the kernel is unknown.
154    Unknown,
155    None,
156    AddressSpace,
157    Vmar,
158    // Mapping returns a reference to avoid copying data.
159    Mapping(&'a MappingDetails),
160}
161
162impl<'a> MapDetails<'a> {
163    pub fn as_mapping(&'a self) -> Option<&'a MappingDetails> {
164        match self {
165            Self::Mapping(d) => Some(*d),
166            _ => None,
167        }
168    }
169}
170
171static_assert_align!(
172    #[repr(C)]
173    #[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, PartialEq)]
174    <sys::zx_info_maps_mapping_t> pub struct MappingDetails {
175        pub mmu_flags <mmu_flags>: VmarFlagsExtended,
176        padding1: [PadByte; 4],
177        pub vmo_koid <vmo_koid>: Koid,
178        pub vmo_offset <vmo_offset>: u64,
179        pub committed_bytes <committed_bytes>: usize,
180        pub populated_bytes <populated_bytes>: usize,
181        pub committed_private_bytes <committed_private_bytes>: usize,
182        pub populated_private_bytes <populated_private_bytes>: usize,
183        pub committed_scaled_bytes <committed_scaled_bytes>: usize,
184        pub populated_scaled_bytes <populated_scaled_bytes>: usize,
185        pub committed_fractional_scaled_bytes <committed_fractional_scaled_bytes>: u64,
186        pub populated_fractional_scaled_bytes <populated_fractional_scaled_bytes>: u64,
187    }
188);
189
190impl Default for MappingDetails {
191    fn default() -> MappingDetails {
192        Self::from(sys::zx_info_maps_mapping_t::default())
193    }
194}
195
196impl From<sys::zx_info_maps_mapping_t> for MappingDetails {
197    fn from(info: sys::zx_info_maps_mapping_t) -> MappingDetails {
198        zerocopy::transmute!(info)
199    }
200}
201
202impl Vmar {
203    pub fn allocate(
204        &self,
205        offset: usize,
206        size: usize,
207        flags: VmarFlags,
208    ) -> Result<(Vmar, usize), Status> {
209        let mut mapped = 0;
210        let mut handle = 0;
211        let status = unsafe {
212            sys::zx_vmar_allocate(
213                self.raw_handle(),
214                flags.bits(),
215                offset,
216                size,
217                &mut handle,
218                &mut mapped,
219            )
220        };
221        ok(status)?;
222        unsafe { Ok((Vmar::from(Handle::from_raw(handle)), mapped)) }
223    }
224
225    pub fn map(
226        &self,
227        vmar_offset: usize,
228        vmo: &Vmo,
229        vmo_offset: u64,
230        len: usize,
231        flags: VmarFlags,
232    ) -> Result<usize, Status> {
233        let flags = VmarFlagsExtended::from_bits_truncate(flags.bits());
234        unsafe { self.map_unsafe(vmar_offset, vmo, vmo_offset, len, flags) }
235    }
236
237    /// Directly call `zx_vmar_map`.
238    ///
239    /// # Safety
240    ///
241    /// This function is unsafe because certain flags to `zx_vmar_map` may
242    /// replace an existing mapping which is referenced elsewhere.
243    pub unsafe fn map_unsafe(
244        &self,
245        vmar_offset: usize,
246        vmo: &Vmo,
247        vmo_offset: u64,
248        len: usize,
249        flags: VmarFlagsExtended,
250    ) -> Result<usize, Status> {
251        let mut mapped = 0;
252        let status = unsafe {
253            sys::zx_vmar_map(
254                self.0.raw_handle(),
255                flags.bits(),
256                vmar_offset,
257                vmo.raw_handle(),
258                vmo_offset,
259                len,
260                &mut mapped,
261            )
262        };
263        ok(status).map(|_| mapped)
264    }
265
266    /// Directly call `zx_vmar_unmap`.
267    ///
268    /// # Safety
269    ///
270    /// This function is unsafe because unmapping memory regions can arbitrarily
271    /// cause read, write, and execution errors. Among other things, the caller
272    /// must ensure that:
273    ///
274    /// - The region being unmapped will not be accessed after unmapping.
275    /// - All references to memory in the region must be dropped or forgotten
276    ///   prior to calling this method.
277    /// - If the region contained executable code, then code in the region must
278    ///   not be currently executing and may not be executed in the future.
279    ///
280    /// This is not an exhaustive list, as there are many ways to cause memory
281    /// unsafety with memory mappings.
282    pub unsafe fn unmap(&self, addr: usize, len: usize) -> Result<(), Status> {
283        // SAFETY: The caller has guaranteed that unmapping the given region
284        // will not cause undefined behavior.
285        ok(unsafe { sys::zx_vmar_unmap(self.0.raw_handle(), addr, len) })
286    }
287
288    /// Perform an operation on VMOs mapped into this VMAR.
289    ///
290    /// Wraps the
291    /// [zx_vmar_op_range](https://fuchsia.dev/fuchsia-src/reference/syscalls/vmar_op_range.md)
292    /// syscall.
293    pub fn op_range(&self, op: VmarOp, addr: usize, len: usize) -> Result<(), Status> {
294        ok(unsafe {
295            sys::zx_vmar_op_range(
296                self.0.raw_handle(),
297                op.into_raw(),
298                addr,
299                len,
300                std::ptr::null_mut(),
301                0,
302            )
303        })
304    }
305
306    /// Directly call `zx_vmar_protect`.
307    ///
308    /// # Safety
309    ///
310    /// This function is unsafe because changing the access protections for
311    /// memory regions can arbitrarily cause read, write, and execution errors.
312    /// Among other things, the caller must ensure that if a read, write, or
313    /// execute permission is removed from a memory region, it must not read,
314    /// write, or execute it respetively.
315    ///
316    /// This is not an exhaustive list, as there are many ways to cause memory
317    /// unsafety with memory mappings.
318    pub unsafe fn protect(&self, addr: usize, len: usize, flags: VmarFlags) -> Result<(), Status> {
319        // SAFETY: The caller has guaranteed that protecting the given region
320        // will not cause undefined behavior.
321        ok(unsafe { sys::zx_vmar_protect(self.raw_handle(), flags.bits(), addr, len) })
322    }
323
324    /// Directly call `zx_vmar_destroy`.
325    ///
326    /// # Safety
327    ///
328    /// This function is unsafe because destroying a region unmaps all of the
329    /// mappings within it. See [`Vmar::unmap`] for more details on how
330    /// unmapping memory regions can cause memory unsafety.
331    pub unsafe fn destroy(&self) -> Result<(), Status> {
332        // SAFETY: The caller has guaranteed that destroying the given region
333        // will not cause undefined behavior.
334        ok(unsafe { sys::zx_vmar_destroy(self.raw_handle()) })
335    }
336
337    /// Wraps the
338    /// [zx_object_get_info](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_info.md)
339    /// syscall for the ZX_INFO_VMAR topic.
340    pub fn info(&self) -> Result<VmarInfo, Status> {
341        Ok(object_get_info_single::<VmarInfo>(self.as_handle_ref())?)
342    }
343
344    /// Wraps the
345    /// [zx_object_get_info](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_info.md)
346    /// syscall for the ZX_INFO_VMAR_MAPS topic.
347    ///
348    /// Returns an initialized slice of `MapInfo`s, any uninitialized trailing entries, and the
349    /// total number of infos that the kernel had available.
350    pub fn maps<'a>(
351        &self,
352        buf: &'a mut [MaybeUninit<MapInfo>],
353    ) -> Result<(&'a mut [MapInfo], &'a mut [MaybeUninit<MapInfo>], usize), Status> {
354        object_get_info::<VmarMapsInfo>(self.as_handle_ref(), buf)
355    }
356
357    /// Wraps the
358    /// [zx_object_get_info](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_info.md)
359    /// syscall for the ZX_INFO_VMAR_MAPS topic.
360    pub fn maps_vec(&self) -> Result<Vec<MapInfo>, Status> {
361        object_get_info_vec::<VmarMapsInfo>(self.as_handle_ref())
362    }
363
364    /// Wraps the [zx_vmar_map_iob](https://fuchsia.dev/fuchsia-src/reference/syscalls/zx_vmar_map_iob.md)
365    /// syscall.
366    pub fn map_iob(
367        &self,
368        options: VmarFlags,
369        vmar_offset: usize,
370        iob: &Iob,
371        region_index: u32,
372        region_offset: u64,
373        region_len: usize,
374    ) -> Result<usize, Status> {
375        let mut addr = 0;
376        let status = unsafe {
377            sys::zx_vmar_map_iob(
378                self.raw_handle(),
379                options.bits(),
380                vmar_offset,
381                iob.raw_handle(),
382                region_index,
383                region_offset,
384                region_len,
385                &mut addr,
386            )
387        };
388        ok(status)?;
389        Ok(addr)
390    }
391
392    /// Wraps the [zx_vmar_map_clock](https://fuchsia.dev/fuchsia-src/reference/syscalls/zx_vmar_map_clock.md)
393    /// syscall.
394    pub fn map_clock<I: Timeline, O: Timeline>(
395        &self,
396        options: VmarFlags,
397        vmar_offset: usize,
398        clock: &Clock<I, O>,
399        length: usize,
400    ) -> Result<usize, Status> {
401        let mut addr = 0;
402        let status = unsafe {
403            sys::zx_vmar_map_clock(
404                self.raw_handle(),
405                options.bits(),
406                vmar_offset,
407                clock.raw_handle(),
408                length,
409                &mut addr,
410            )
411        };
412        ok(status)?;
413        Ok(addr)
414    }
415}
416
417/// VM Address Range opcodes
418#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
419#[repr(transparent)]
420pub struct VmarOp(u32);
421impl VmarOp {
422    pub fn from_raw(raw: u32) -> Self {
423        Self(raw)
424    }
425    pub fn into_raw(self) -> u32 {
426        self.0
427    }
428}
429
430assoc_values!(VmarOp, [
431    COMMIT =           sys::ZX_VMAR_OP_COMMIT;
432    DECOMMIT =         sys::ZX_VMAR_OP_DECOMMIT;
433    PREFETCH =         sys::ZX_VMAR_OP_PREFETCH;
434    MAP_RANGE =        sys::ZX_VMAR_OP_MAP_RANGE;
435    ZERO =             sys::ZX_VMAR_OP_ZERO;
436    DONT_NEED =        sys::ZX_VMAR_OP_DONT_NEED;
437    ALWAYS_NEED =      sys::ZX_VMAR_OP_ALWAYS_NEED;
438]);
439
440// TODO(smklein): Ideally we would have two separate sets of bitflags,
441// and a union of both of them.
442macro_rules! vmar_flags {
443    (
444        safe: [$($safe_name:ident : $safe_sys_name:ident,)*],
445        extended: [$($ex_name:ident : $ex_sys_name:ident,)*],
446    ) => {
447        /// Flags to VMAR routines which are considered safe.
448        #[repr(transparent)]
449        #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, FromBytes, Immutable)]
450        pub struct VmarFlags(sys::zx_vm_option_t);
451
452        bitflags! {
453            impl VmarFlags: sys::zx_vm_option_t {
454                $(
455                    const $safe_name = sys::$safe_sys_name;
456                )*
457            }
458        }
459
460        impl std::fmt::Debug for VmarFlags {
461            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
462                bitflags::parser::to_writer(self, f)
463            }
464        }
465
466        /// Flags to all VMAR routines.
467        #[repr(transparent)]
468        #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, FromBytes, Immutable, Default)]
469        pub struct VmarFlagsExtended(sys::zx_vm_option_t);
470
471        bitflags! {
472            impl VmarFlagsExtended: sys::zx_vm_option_t {
473                $(
474                    const $safe_name = sys::$safe_sys_name;
475                )*
476                $(
477                    const $ex_name = sys::$ex_sys_name;
478                )*
479            }
480        }
481
482        impl std::fmt::Debug for VmarFlagsExtended {
483            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
484                bitflags::parser::to_writer(self, f)
485            }
486        }
487    };
488}
489
490vmar_flags! {
491    safe: [
492        PERM_READ: ZX_VM_PERM_READ,
493        PERM_WRITE: ZX_VM_PERM_WRITE,
494        PERM_EXECUTE: ZX_VM_PERM_EXECUTE,
495        COMPACT: ZX_VM_COMPACT,
496        SPECIFIC: ZX_VM_SPECIFIC,
497        CAN_MAP_SPECIFIC: ZX_VM_CAN_MAP_SPECIFIC,
498        CAN_MAP_READ: ZX_VM_CAN_MAP_READ,
499        CAN_MAP_WRITE: ZX_VM_CAN_MAP_WRITE,
500        CAN_MAP_EXECUTE: ZX_VM_CAN_MAP_EXECUTE,
501        MAP_RANGE: ZX_VM_MAP_RANGE,
502        REQUIRE_NON_RESIZABLE: ZX_VM_REQUIRE_NON_RESIZABLE,
503        ALLOW_FAULTS: ZX_VM_ALLOW_FAULTS,
504        OFFSET_IS_UPPER_LIMIT: ZX_VM_OFFSET_IS_UPPER_LIMIT,
505        PERM_READ_IF_XOM_UNSUPPORTED: ZX_VM_PERM_READ_IF_XOM_UNSUPPORTED,
506        FAULT_BEYOND_STREAM_SIZE: ZX_VM_FAULT_BEYOND_STREAM_SIZE,
507
508
509        // Alignment options
510        ALIGN_1KB: ZX_VM_ALIGN_1KB,
511        ALIGN_2KB: ZX_VM_ALIGN_2KB,
512        ALIGN_4KB: ZX_VM_ALIGN_4KB,
513        ALIGN_8KB: ZX_VM_ALIGN_8KB,
514        ALIGN_16KB: ZX_VM_ALIGN_16KB,
515        ALIGN_32KB: ZX_VM_ALIGN_32KB,
516        ALIGN_64KB: ZX_VM_ALIGN_64KB,
517        ALIGN_128KB: ZX_VM_ALIGN_128KB,
518        ALIGN_256KB: ZX_VM_ALIGN_256KB,
519        ALIGN_512KB: ZX_VM_ALIGN_512KB,
520        ALIGN_1MB: ZX_VM_ALIGN_1MB,
521        ALIGN_2MB: ZX_VM_ALIGN_2MB,
522        ALIGN_4MB: ZX_VM_ALIGN_4MB,
523        ALIGN_8MB: ZX_VM_ALIGN_8MB,
524        ALIGN_16MB: ZX_VM_ALIGN_16MB,
525        ALIGN_32MB: ZX_VM_ALIGN_32MB,
526        ALIGN_64MB: ZX_VM_ALIGN_64MB,
527        ALIGN_128MB: ZX_VM_ALIGN_128MB,
528        ALIGN_256MB: ZX_VM_ALIGN_256MB,
529        ALIGN_512MB: ZX_VM_ALIGN_512MB,
530        ALIGN_1GB: ZX_VM_ALIGN_1GB,
531        ALIGN_2GB: ZX_VM_ALIGN_2GB,
532        ALIGN_4GB: ZX_VM_ALIGN_4GB,
533    ],
534    extended: [
535        SPECIFIC_OVERWRITE: ZX_VM_SPECIFIC_OVERWRITE,
536    ],
537}
538
539#[cfg(test)]
540mod tests {
541    // The unit tests are built with a different crate name, but fuchsia_runtime returns a "real"
542    // zx::Vmar that we need to use.
543    use zx::{Status, VmarFlags};
544
545    #[test]
546    fn allocate_and_info() -> Result<(), Status> {
547        let size = usize::pow(2, 20); // 1MiB
548        let root_vmar = fuchsia_runtime::vmar_root_self();
549        let (vmar, base) = root_vmar.allocate(0, size, VmarFlags::empty())?;
550
551        let info = vmar.info()?;
552        assert!(info.base == base);
553        assert!(info.len == size);
554        Ok(())
555    }
556
557    #[test]
558    fn root_vmar_info() -> Result<(), Status> {
559        let root_vmar = fuchsia_runtime::vmar_root_self();
560        let info = root_vmar.info()?;
561        assert!(info.base > 0);
562        assert!(info.len > 0);
563        Ok(())
564    }
565
566    #[test]
567    fn root_vmar_maps() {
568        let root_vmar = fuchsia_runtime::vmar_root_self();
569        let info = root_vmar.maps_vec().unwrap();
570        assert!(!info.is_empty());
571    }
572}