Skip to main content

vm/
vm_address_region.rs

1// Copyright 2026 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
5use crate::arch_vm_aspace::ArchMmuFlags;
6use crate::vm_mapping::VmMapping;
7use crate::vm_object::VmObject;
8use core::ffi::{CStr, c_char};
9use core::marker::{PhantomData, PhantomPinned};
10use core::ptr::NonNull;
11use fbl::RefPtr;
12use kalloc::AllocError;
13use kernel::types::VAddr;
14use zx_status::Status;
15
16/// When randomly allocating subregions, reduce sprawl by placing allocations near each other.
17pub const VMAR_FLAG_COMPACT: u32 = 1 << 0;
18/// Request that the new region be at the specified offset in its parent region.
19pub const VMAR_FLAG_SPECIFIC: u32 = 1 << 1;
20/// Like `VMAR_FLAG_SPECIFIC`, but permits overwriting existing mappings.
21pub const VMAR_FLAG_SPECIFIC_OVERWRITE: u32 = 1 << 2;
22/// Allow `VmMappings` to be created inside the new region with the `SPECIFIC` or `OFFSET_IS_UPPER_LIMIT` flag.
23pub const VMAR_FLAG_CAN_MAP_SPECIFIC: u32 = 1 << 3;
24/// Allow `VmMappings` to be created inside the region with read permissions.
25pub const VMAR_FLAG_CAN_MAP_READ: u32 = 1 << 4;
26/// Allow `VmMappings` to be created inside the region with write permissions.
27pub const VMAR_FLAG_CAN_MAP_WRITE: u32 = 1 << 5;
28/// Allow `VmMappings` to be created inside the region with execute permissions.
29pub const VMAR_FLAG_CAN_MAP_EXECUTE: u32 = 1 << 6;
30/// Require that VMO backing the mapping is non-resizable.
31pub const VMAR_FLAG_REQUIRE_NON_RESIZABLE: u32 = 1 << 7;
32/// Allow VMO backings that could result in faults.
33pub const VMAR_FLAG_ALLOW_FAULTS: u32 = 1 << 8;
34/// Treat the offset as an upper limit when allocating a VMO or child VMAR.
35pub const VMAR_FLAG_OFFSET_IS_UPPER_LIMIT: u32 = 1 << 9;
36/// Opt this VMAR out of certain debugging checks.
37pub const VMAR_FLAG_DEBUG_DYNAMIC_KERNEL_MAPPING: u32 = 1 << 10;
38/// Memory accesses past the stream size rounded up to the page boundary will fault.
39pub const VMAR_FLAG_FAULT_BEYOND_STREAM_SIZE: u32 = 1 << 11;
40
41/// Mask of read, write, and execute permission flags.
42pub const VMAR_CAN_RWX_FLAGS: u32 =
43    VMAR_FLAG_CAN_MAP_READ | VMAR_FLAG_CAN_MAP_WRITE | VMAR_FLAG_CAN_MAP_EXECUTE;
44
45/// Memory priorities that can be applied to VMARs and mappings.
46#[repr(u8)]
47#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
48pub enum MemoryPriority {
49    /// Default overcommit priority where reclamation is allowed.
50    Default = 0,
51    /// High priority prevents all reclamation.
52    High = 1,
53}
54
55/// Whether to operate on children when unmapping or protecting.
56#[repr(u8)]
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
58pub enum VmAddressRegionOpChildren {
59    Yes = 0,
60    No = 1,
61}
62
63// Verify sizes match C++ 1-byte bool enum representations.
64const _: () = assert!(core::mem::size_of::<MemoryPriority>() == 1);
65const _: () = assert!(core::mem::size_of::<VmAddressRegionOpChildren>() == 1);
66
67/// Result of calling [`VmAddressRegion::create_vm_mapping`].
68pub struct MapResult {
69    /// The newly created mapping.
70    pub mapping: RefPtr<VmMapping>,
71    /// The virtual address of the mapping at creation time.
72    pub base: usize,
73}
74
75unsafe extern "C" {
76    fn cpp_vm_address_region_get_ref_counted(vmar: *mut VmAddressRegion) -> *mut fbl::RefCounted;
77    fn cpp_vm_address_region_free(vmar: *mut VmAddressRegion);
78    fn cpp_vm_address_region_destroy(vmar: *mut VmAddressRegion) -> i32;
79    fn cpp_vm_address_region_base(vmar: *mut VmAddressRegion) -> VAddr;
80    fn cpp_vm_address_region_size(vmar: *mut VmAddressRegion) -> usize;
81    fn cpp_vm_address_region_flags(vmar: *mut VmAddressRegion) -> u32;
82    fn cpp_vm_address_region_name(vmar: *mut VmAddressRegion) -> *const c_char;
83    fn cpp_vm_address_region_has_parent(vmar: *mut VmAddressRegion) -> bool;
84    fn cpp_vm_address_region_set_memory_priority(
85        vmar: *mut VmAddressRegion,
86        priority: MemoryPriority,
87    ) -> i32;
88    fn cpp_vm_address_region_unmap(
89        vmar: *mut VmAddressRegion,
90        base: VAddr,
91        size: usize,
92        op_children: VmAddressRegionOpChildren,
93    ) -> i32;
94    fn cpp_vm_address_region_protect(
95        vmar: *mut VmAddressRegion,
96        base: VAddr,
97        size: usize,
98        new_arch_mmu_flags: ArchMmuFlags,
99        op_children: VmAddressRegionOpChildren,
100    ) -> i32;
101    fn cpp_vm_address_region_reserve_space(
102        vmar: *mut VmAddressRegion,
103        name: *const c_char,
104        base: usize,
105        size: usize,
106        arch_mmu_flags: ArchMmuFlags,
107    ) -> i32;
108    fn cpp_vm_address_region_create_sub_vmar(
109        vmar: *mut VmAddressRegion,
110        offset: usize,
111        size: usize,
112        align_pow2: u8,
113        vmar_flags: u32,
114        name: *const c_char,
115        out_status: *mut i32,
116    ) -> *mut VmAddressRegion;
117    fn cpp_vm_address_region_create_vm_mapping(
118        vmar: *mut VmAddressRegion,
119        mapping_offset: usize,
120        size: usize,
121        align_pow2: u8,
122        vmar_flags: u32,
123        vmo: *const VmObject,
124        vmo_offset: u64,
125        arch_mmu_flags: ArchMmuFlags,
126        name: *const c_char,
127        out_base: *mut usize,
128        out_status: *mut i32,
129    ) -> *mut VmMapping;
130}
131
132/// A contiguous region of the virtual address space.
133#[repr(C)]
134pub struct VmAddressRegion {
135    _data: [u8; 0],
136    _marker: PhantomData<PhantomPinned>,
137}
138
139impl VmAddressRegion {
140    fn as_mut_ptr(&self) -> *mut VmAddressRegion {
141        self as *const VmAddressRegion as *mut VmAddressRegion
142    }
143
144    /// Creates a subregion of this region.
145    pub fn create_sub_vmar(
146        &self,
147        offset: usize,
148        size: usize,
149        align_pow2: u8,
150        vmar_flags: u32,
151        name: &CStr,
152    ) -> Result<RefPtr<VmAddressRegion>, Status> {
153        let mut status = 0;
154        let raw = unsafe {
155            cpp_vm_address_region_create_sub_vmar(
156                self.as_mut_ptr(),
157                offset,
158                size,
159                align_pow2,
160                vmar_flags,
161                name.as_ptr(),
162                &mut status,
163            )
164        };
165        Status::ok(status)?;
166        unsafe { RefPtr::try_from_raw(raw).ok_or(Status::NO_MEMORY) }
167    }
168
169    /// Creates a [`VmMapping`] within this region.
170    ///
171    /// To avoid leaks, this should be paired with a call to [`VmMapping::destroy`] if desired;
172    /// dropping `MapResult::mapping` will not destroy the mapping.
173    pub fn create_vm_mapping(
174        &self,
175        mapping_offset: usize,
176        size: usize,
177        align_pow2: u8,
178        vmar_flags: u32,
179        vmo: RefPtr<VmObject>,
180        vmo_offset: u64,
181        arch_mmu_flags: ArchMmuFlags,
182        name: &CStr,
183    ) -> Result<MapResult, Status> {
184        let mut base = 0;
185        let mut status = 0;
186        let raw = unsafe {
187            cpp_vm_address_region_create_vm_mapping(
188                self.as_mut_ptr(),
189                mapping_offset,
190                size,
191                align_pow2,
192                vmar_flags,
193                RefPtr::into_raw(vmo),
194                vmo_offset,
195                arch_mmu_flags,
196                name.as_ptr(),
197                &mut base,
198                &mut status,
199            )
200        };
201        Status::ok(status)?;
202        let mapping = unsafe { RefPtr::try_from_raw(raw).ok_or(Status::NO_MEMORY)? };
203        Ok(MapResult { mapping, base })
204    }
205
206    /// Destroys this region and recursively destroys child VMARs.
207    pub fn destroy(&self) -> Result<(), Status> {
208        Status::ok(unsafe { cpp_vm_address_region_destroy(self.as_mut_ptr()) })
209    }
210
211    /// Returns the base address of this region.
212    pub fn base(&self) -> VAddr {
213        unsafe { cpp_vm_address_region_base(self.as_mut_ptr()) }
214    }
215
216    /// Returns the size in bytes of this region.
217    pub fn size(&self) -> usize {
218        unsafe { cpp_vm_address_region_size(self.as_mut_ptr()) }
219    }
220
221    /// Returns the creation flags of this region.
222    pub fn flags(&self) -> u32 {
223        unsafe { cpp_vm_address_region_flags(self.as_mut_ptr()) }
224    }
225
226    /// Returns the name of this region.
227    pub fn name(&self) -> &CStr {
228        unsafe { CStr::from_ptr(cpp_vm_address_region_name(self.as_mut_ptr())) }
229    }
230
231    /// Returns true if this region has a parent region.
232    pub fn has_parent(&self) -> bool {
233        unsafe { cpp_vm_address_region_has_parent(self.as_mut_ptr()) }
234    }
235
236    /// Applies the given memory priority to this region and all subregions.
237    pub fn set_memory_priority(&self, priority: MemoryPriority) -> Result<(), Status> {
238        Status::ok(unsafe {
239            cpp_vm_address_region_set_memory_priority(self.as_mut_ptr(), priority)
240        })
241    }
242
243    /// Unmaps a subset of the region of memory in the containing address space.
244    ///
245    /// # Safety
246    ///
247    /// Caller must ensure the specified virtual address region to unmap is no longer used.
248    pub unsafe fn unmap(
249        &self,
250        base: VAddr,
251        size: usize,
252        op_children: VmAddressRegionOpChildren,
253    ) -> Result<(), Status> {
254        Status::ok(unsafe {
255            cpp_vm_address_region_unmap(self.as_mut_ptr(), base, size, op_children)
256        })
257    }
258
259    /// Changes protections on a subset of the region of memory in the containing address space.
260    pub fn protect(
261        &self,
262        base: VAddr,
263        size: usize,
264        new_arch_mmu_flags: ArchMmuFlags,
265        op_children: VmAddressRegionOpChildren,
266    ) -> Result<(), Status> {
267        Status::ok(unsafe {
268            cpp_vm_address_region_protect(
269                self.as_mut_ptr(),
270                base,
271                size,
272                new_arch_mmu_flags,
273                op_children,
274            )
275        })
276    }
277
278    /// Reserves a memory region within this VMAR without allocating physical pages.
279    pub fn reserve_space(
280        &self,
281        name: &CStr,
282        base: usize,
283        size: usize,
284        arch_mmu_flags: ArchMmuFlags,
285    ) -> Result<(), Status> {
286        Status::ok(unsafe {
287            cpp_vm_address_region_reserve_space(
288                self.as_mut_ptr(),
289                name.as_ptr(),
290                base,
291                size,
292                arch_mmu_flags,
293            )
294        })
295    }
296}
297
298impl fbl::HasRefCount for VmAddressRegion {
299    fn ref_count(&self) -> &fbl::RefCounted {
300        unsafe { &*cpp_vm_address_region_get_ref_counted(self.as_mut_ptr()) }
301    }
302}
303
304unsafe impl fbl::Recyclable for VmAddressRegion {
305    unsafe fn recycle(ptr: NonNull<Self>) {
306        unsafe {
307            cpp_vm_address_region_free(ptr.as_ptr());
308        }
309    }
310
311    fn allocate(_value: Self) -> Result<NonNull<Self>, AllocError> {
312        Err(AllocError)
313    }
314}