Skip to main content

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