vm/vm_aspace.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, ArchVmAspace, NonTerminalAction, TerminalAction};
6use crate::vm_address_region::VmAddressRegion;
7use core::ffi::{CStr, c_char, c_void};
8use core::marker::{PhantomData, PhantomPinned};
9use core::ptr::NonNull;
10use fbl::RefPtr;
11use kalloc::AllocError;
12use kernel::thread::ThreadPtr;
13use kernel::types::PAddr;
14use zx_status::Status;
15
16#[repr(u8)]
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum Type {
19 User = 0,
20 Kernel = 1,
21 /// You probably do not want to use `LowKernel`. It is primarily used for SMP bootstrap or mexec
22 /// to allow mappings of very low memory using the standard VMM subsystem.
23 LowKernel = 2,
24 /// Used to construct an address space representing hypervisor guest memory.
25 GuestPhysical = 3,
26}
27
28#[repr(i32)]
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum ShareOpt {
31 /// A normal independent address space initialized using [`ArchVmAspace::init`].
32 None = 0,
33 /// A restricted address space whose underlying [`ArchVmAspace`] will be initialized using
34 /// `init_restricted`.
35 Restricted = 1,
36 /// A shared address space whose underlying [`ArchVmAspace`] will be initialized using
37 /// `init_shared`.
38 Shared = 2,
39}
40
41unsafe extern "C" {
42 fn cpp_vm_aspace_get_ref_counted(aspace: *mut VmAspace) -> *mut fbl::RefCounted;
43 fn cpp_vm_aspace_create(type_: Type, name: *const c_char) -> *mut VmAspace;
44 fn cpp_vm_aspace_create_with_opts(
45 base: usize,
46 size: usize,
47 type_: Type,
48 name: *const c_char,
49 share_opt: ShareOpt,
50 ) -> *mut VmAspace;
51 fn cpp_vm_aspace_create_unified(
52 shared: *mut VmAspace,
53 restricted: *mut VmAspace,
54 name: *const c_char,
55 ) -> *mut VmAspace;
56 fn cpp_vm_aspace_arch_aspace(aspace: *mut VmAspace) -> *mut ArchVmAspace;
57 fn cpp_vm_aspace_kernel_aspace() -> *mut VmAspace;
58 fn cpp_vm_aspace_root_vmar(aspace: *mut VmAspace) -> *mut VmAddressRegion;
59 fn cpp_vm_aspace_base(aspace: *mut VmAspace) -> usize;
60 fn cpp_vm_aspace_size(aspace: *mut VmAspace) -> usize;
61 fn cpp_vm_aspace_name(aspace: *mut VmAspace) -> *const c_char;
62 fn cpp_vm_aspace_is_user(aspace: *mut VmAspace) -> bool;
63 fn cpp_vm_aspace_is_aslr_enabled(aspace: *mut VmAspace) -> bool;
64 fn cpp_vm_aspace_is_destroyed(aspace: *mut VmAspace) -> bool;
65 fn cpp_vm_aspace_destroy(aspace: *mut VmAspace) -> i32;
66 fn cpp_vm_aspace_rename(aspace: *mut VmAspace, name: *const c_char);
67 fn cpp_vm_aspace_dump(aspace: *mut VmAspace, verbose: bool);
68 fn cpp_vm_aspace_attach_to_thread(aspace: *mut VmAspace, thread: *mut c_void);
69 fn cpp_vm_aspace_vdso_base_address(aspace: *mut VmAspace) -> usize;
70 fn cpp_vm_aspace_vdso_code_address(aspace: *mut VmAspace) -> usize;
71 fn cpp_vm_aspace_is_high_memory_priority(aspace: *mut VmAspace) -> bool;
72 fn cpp_vm_aspace_accessed_fault(aspace: *mut VmAspace, va: usize) -> i32;
73 fn cpp_vm_aspace_page_fault(aspace: *mut VmAspace, va: usize, flags: u32) -> i32;
74 fn cpp_vm_aspace_soft_fault(aspace: *mut VmAspace, va: usize, flags: u32) -> i32;
75 fn cpp_vm_aspace_soft_fault_in_range(
76 aspace: *mut VmAspace,
77 va: usize,
78 flags: u32,
79 len: usize,
80 ) -> i32;
81 fn cpp_vm_aspace_drop_user_page_tables(aspace: *mut VmAspace);
82 fn cpp_vm_aspace_drop_all_user_page_tables();
83 fn cpp_vm_aspace_dump_all_aspaces(verbose: bool);
84 fn cpp_vm_aspace_harvest_all_user_accessed_bits(
85 non_terminal_action: NonTerminalAction,
86 terminal_action: TerminalAction,
87 );
88 fn cpp_vm_aspace_alloc_physical(
89 aspace: *mut VmAspace,
90 name: *const c_char,
91 size: usize,
92 ptr: *mut *mut c_void,
93 align_pow2: u8,
94 paddr: PAddr,
95 vmm_flags: u32,
96 arch_mmu_flags: ArchMmuFlags,
97 ) -> i32;
98 fn cpp_vm_aspace_alloc_contiguous(
99 aspace: *mut VmAspace,
100 name: *const c_char,
101 size: usize,
102 ptr: *mut *mut c_void,
103 align_pow2: u8,
104 vmm_flags: u32,
105 arch_mmu_flags: ArchMmuFlags,
106 ) -> i32;
107 fn cpp_vm_aspace_free_region(aspace: *mut VmAspace, va: usize) -> i32;
108 fn cpp_vm_aspace_free(aspace: *mut VmAspace);
109}
110
111#[repr(C)]
112pub struct VmAspace {
113 // TODO(https://fxbug.dev/529915725): Use zx::OpaqueBytes with the correct size and alignment of
114 // the underlying structure to allow for correct Rust allocation.
115 _data: [u8; 0],
116 // Mark with PhantomPinned as this is a shared C++ struct and this object is not trivially
117 // movable. Wrap it with PhantomData to allow for FFI usage.
118 _marker: PhantomData<PhantomPinned>,
119}
120
121impl VmAspace {
122 fn as_mut_ptr(&self) -> *mut VmAspace {
123 self as *const VmAspace as *mut VmAspace
124 }
125
126 /// Creates an address space of the type specified in `type_` with name `name`.
127 ///
128 /// Although reference counted, the returned [`VmAspace`] must be explicitly destroyed via
129 /// [`destroy`](Self::destroy).
130 ///
131 /// Returns `None` on failure (e.g. due to resource starvation).
132 pub fn create(type_: Type, name: &CStr) -> Option<RefPtr<VmAspace>> {
133 unsafe { RefPtr::try_from_raw(cpp_vm_aspace_create(type_, name.as_ptr())) }
134 }
135
136 /// Creates an address space of the type specified in `type_` with name `name`.
137 ///
138 /// The returned aspace will start at `base` and span `size`.
139 ///
140 /// If `share_opt` is [`ShareOpt::Shared`], we're creating a shared address space, and the
141 /// underlying [`ArchVmAspace`] will be initialized using the
142 /// [`init_shared`](ArchVmAspace::init_shared) method instead of the normal
143 /// [`init`](ArchVmAspace::init) method.
144 ///
145 /// If `share_opt` is [`ShareOpt::Restricted`], we're creating a restricted address space, and
146 /// the underlying [`ArchVmAspace`] will be initialized using the
147 /// [`init_restricted`](ArchVmAspace::init_restricted) method.
148 ///
149 /// Although reference counted, the returned [`VmAspace`] must be explicitly destroyed via
150 /// [`destroy`](Self::destroy).
151 ///
152 /// Returns `None` on failure (e.g. due to resource starvation).
153 pub fn create_with_opts(
154 base: usize,
155 size: usize,
156 type_: Type,
157 name: &CStr,
158 share_opt: ShareOpt,
159 ) -> Option<RefPtr<VmAspace>> {
160 unsafe {
161 RefPtr::try_from_raw(cpp_vm_aspace_create_with_opts(
162 base,
163 size,
164 type_,
165 name.as_ptr(),
166 share_opt,
167 ))
168 }
169 }
170
171 /// Creates a unified address space that consists of the given constituent address spaces.
172 ///
173 /// The passed in address spaces must meet the following criteria:
174 /// 1. They must manage non-overlapping regions.
175 /// 2. The shared [`VmAspace`] must have been created with the shared argument set to true.
176 ///
177 /// Although reference counted, the returned [`VmAspace`] must be explicitly destroyed via
178 /// [`destroy`](Self::destroy). Note that it must be destroyed before the shared and
179 /// restricted [`VmAspace`]s; destroying the constituent [`VmAspace`]s before destroying
180 /// this one will trigger asserts.
181 ///
182 /// Returns `None` on failure (e.g. due to resource starvation).
183 ///
184 /// # Safety
185 ///
186 /// The caller must ensure that `shared` and `restricted` are valid pointers to address
187 /// spaces initialized as shared and restricted respectively.
188 pub unsafe fn create_unified(
189 shared: *mut VmAspace,
190 restricted: *mut VmAspace,
191 name: &CStr,
192 ) -> Option<RefPtr<VmAspace>> {
193 unsafe {
194 RefPtr::try_from_raw(cpp_vm_aspace_create_unified(shared, restricted, name.as_ptr()))
195 }
196 }
197
198 /// Destroys this address space.
199 ///
200 /// `destroy` does not free this object, but rather allows it to be freed when the last
201 /// retaining `RefPtr` is destroyed.
202 pub fn destroy(&self) -> Result<(), Status> {
203 Status::ok(unsafe { cpp_vm_aspace_destroy(self.as_mut_ptr()) })
204 }
205
206 /// Renames this address space.
207 pub fn rename(&self, name: &CStr) {
208 unsafe { cpp_vm_aspace_rename(self.as_mut_ptr(), name.as_ptr()) }
209 }
210
211 /// Returns the base virtual address of this address space.
212 pub fn base(&self) -> usize {
213 unsafe { cpp_vm_aspace_base(self.as_mut_ptr()) }
214 }
215
216 /// Returns the size in bytes of this address space.
217 pub fn size(&self) -> usize {
218 unsafe { cpp_vm_aspace_size(self.as_mut_ptr()) }
219 }
220
221 /// Returns the name of this address space.
222 pub fn name(&self) -> &CStr {
223 unsafe { CStr::from_ptr(cpp_vm_aspace_name(self.as_mut_ptr())) }
224 }
225
226 /// Returns a reference to the architecturally specific part of the address space
227 /// (`ArchVmAspace`). This is internally locked and does not need to be guarded by `lock_`.
228 pub fn arch_aspace(&self) -> &ArchVmAspace {
229 unsafe { &*cpp_vm_aspace_arch_aspace(self.as_mut_ptr()) }
230 }
231
232 /// Returns true if this is a user address space (`Type::User`).
233 pub fn is_user(&self) -> bool {
234 unsafe { cpp_vm_aspace_is_user(self.as_mut_ptr()) }
235 }
236
237 /// Returns true if ASLR is enabled for this address space.
238 pub fn is_aslr_enabled(&self) -> bool {
239 unsafe { cpp_vm_aspace_is_aslr_enabled(self.as_mut_ptr()) }
240 }
241
242 /// Returns true if this address space has been destroyed.
243 pub fn is_destroyed(&self) -> bool {
244 unsafe { cpp_vm_aspace_is_destroyed(self.as_mut_ptr()) }
245 }
246
247 /// Returns the singleton kernel address space.
248 pub fn kernel_aspace() -> Option<RefPtr<VmAspace>> {
249 unsafe { RefPtr::try_from_raw(cpp_vm_aspace_kernel_aspace()) }
250 }
251
252 /// Returns the root address region (`RootVmar`) for this address space.
253 pub fn root_vmar(&self) -> Option<RefPtr<VmAddressRegion>> {
254 unsafe { RefPtr::try_from_raw(cpp_vm_aspace_root_vmar(self.as_mut_ptr())) }
255 }
256
257 /// Sets the per-thread address space pointer to this address space.
258 pub fn attach_to_thread(&self, thread: ThreadPtr) {
259 unsafe { cpp_vm_aspace_attach_to_thread(self.as_mut_ptr(), thread.as_raw()) }
260 }
261
262 /// Dumps information about this address space to the debug log.
263 pub fn dump(&self, verbose: bool) {
264 unsafe { cpp_vm_aspace_dump(self.as_mut_ptr(), verbose) }
265 }
266
267 /// Drops all user page tables across all user address spaces.
268 pub fn drop_all_user_page_tables() {
269 unsafe { cpp_vm_aspace_drop_all_user_page_tables() }
270 }
271
272 /// Drops all user page tables for this address space.
273 pub fn drop_user_page_tables(&self) {
274 unsafe { cpp_vm_aspace_drop_user_page_tables(self.as_mut_ptr()) }
275 }
276
277 /// Dumps all address spaces in the system.
278 pub fn dump_all_aspaces(verbose: bool) {
279 unsafe { cpp_vm_aspace_dump_all_aspaces(verbose) }
280 }
281
282 /// Harvests all accessed information across all user mappings and updates any page age
283 /// information for terminal mappings, and potentially harvests page tables depending on the
284 /// passed in action.
285 ///
286 /// This requires holding `aspaces_list_lock_` over the entire duration and
287 /// whilst not a commonly used lock this function should still only be called infrequently to
288 /// avoid monopolizing the lock.
289 pub fn harvest_all_user_accessed_bits(
290 non_terminal_action: NonTerminalAction,
291 terminal_action: TerminalAction,
292 ) {
293 unsafe {
294 cpp_vm_aspace_harvest_all_user_accessed_bits(non_terminal_action, terminal_action)
295 }
296 }
297
298 /// Generates a soft fault against this address space.
299 ///
300 /// This is similar to `page_fault` except:
301 /// * This address space may not currently be active and this does not have to be called from
302 /// the hardware exception handler.
303 /// * May be invoked spuriously in situations where the hardware mappings would have prevented a
304 /// real `page_fault` from occurring.
305 ///
306 /// May block on page requests and must be called without locks held.
307 pub fn soft_fault(&self, va: usize, flags: u32) -> Result<(), Status> {
308 Status::ok(unsafe { cpp_vm_aspace_soft_fault(self.as_mut_ptr(), va, flags) })
309 }
310
311 /// Similar to `soft_fault`, but additionally takes a length indicating that the range of
312 /// `[va, va+len)` is expected to be accessed with `flags` after resolving this fault. The
313 /// address space can take this range as a hint to attempt to preemptively avoid future faults.
314 ///
315 /// There are no alignment restrictions on `va` or `len`, although it is assumed that `len` is
316 /// greater than zero.
317 pub fn soft_fault_in_range(&self, va: usize, flags: u32, len: usize) -> Result<(), Status> {
318 Status::ok(unsafe { cpp_vm_aspace_soft_fault_in_range(self.as_mut_ptr(), va, flags, len) })
319 }
320
321 /// Generates an accessed flag fault against this address space.
322 ///
323 /// This is a specialized version of `soft_fault` that will only resolve a potential missing
324 /// access flag and nothing else.
325 pub fn accessed_fault(&self, va: usize) -> Result<(), Status> {
326 Status::ok(unsafe { cpp_vm_aspace_accessed_fault(self.as_mut_ptr(), va) })
327 }
328
329 /// Page fault routine.
330 ///
331 /// Should only be called by the hypervisor or by `Thread::Current::Fault`.
332 pub fn page_fault(&self, va: usize, flags: u32) -> Result<(), Status> {
333 Status::ok(unsafe { cpp_vm_aspace_page_fault(self.as_mut_ptr(), va, flags) })
334 }
335
336 /// Legacy function to assist in the transition to VMARs.
337 ///
338 /// Assumes a flat VMAR structure in which all VMOs are mapped as children of the root.
339 /// Will assert if used on user address spaces.
340 ///
341 /// # Safety
342 ///
343 /// The caller must ensure `ptr` points to a valid memory location that can hold the allocated
344 /// address or specific starting address.
345 pub unsafe fn alloc_physical(
346 &self,
347 name: &CStr,
348 size: usize,
349 ptr: *mut *mut c_void,
350 align_pow2: u8,
351 paddr: PAddr,
352 vmm_flags: u32,
353 arch_mmu_flags: ArchMmuFlags,
354 ) -> Result<(), Status> {
355 Status::ok(unsafe {
356 cpp_vm_aspace_alloc_physical(
357 self.as_mut_ptr(),
358 name.as_ptr(),
359 size,
360 ptr,
361 align_pow2,
362 paddr,
363 vmm_flags,
364 arch_mmu_flags,
365 )
366 })
367 }
368
369 /// Legacy function to assist in the transition to VMARs.
370 ///
371 /// Assumes a flat VMAR structure in which all VMOs are mapped as children of the root.
372 /// Will assert if used on user address spaces.
373 ///
374 /// # Safety
375 ///
376 /// The caller must ensure `ptr` points to a valid memory location that can hold the allocated
377 /// address or specific starting address.
378 pub unsafe fn alloc_contiguous(
379 &self,
380 name: &CStr,
381 size: usize,
382 ptr: *mut *mut c_void,
383 align_pow2: u8,
384 vmm_flags: u32,
385 arch_mmu_flags: ArchMmuFlags,
386 ) -> Result<(), Status> {
387 Status::ok(unsafe {
388 cpp_vm_aspace_alloc_contiguous(
389 self.as_mut_ptr(),
390 name.as_ptr(),
391 size,
392 ptr,
393 align_pow2,
394 vmm_flags,
395 arch_mmu_flags,
396 )
397 })
398 }
399
400 /// Legacy function to assist in the transition to VMARs.
401 ///
402 /// Assumes a flat VMAR structure in which all VMOs are mapped as children of the root.
403 /// Will assert if used on user address spaces.
404 ///
405 /// # Safety
406 ///
407 /// The caller must ensure that the virtual address range being freed is no longer in use.
408 pub unsafe fn free_region(&self, va: usize) -> Result<(), Status> {
409 Status::ok(unsafe { cpp_vm_aspace_free_region(self.as_mut_ptr(), va) })
410 }
411
412 /// Returns the vDSO base address for this address space.
413 pub fn vdso_base_address(&self) -> usize {
414 unsafe { cpp_vm_aspace_vdso_base_address(self.as_mut_ptr()) }
415 }
416
417 /// Returns the vDSO code address for this address space.
418 pub fn vdso_code_address(&self) -> usize {
419 unsafe { cpp_vm_aspace_vdso_code_address(self.as_mut_ptr()) }
420 }
421
422 /// Returns whether this address space is currently set to be a high memory priority.
423 pub fn is_high_memory_priority(&self) -> bool {
424 unsafe { cpp_vm_aspace_is_high_memory_priority(self.as_mut_ptr()) }
425 }
426}
427
428impl fbl::HasRefCount for VmAspace {
429 fn ref_count(&self) -> &fbl::RefCounted {
430 unsafe { &*cpp_vm_aspace_get_ref_counted(self.as_mut_ptr()) }
431 }
432}
433
434unsafe impl fbl::Recyclable for VmAspace {
435 unsafe fn recycle(ptr: NonNull<Self>) {
436 unsafe {
437 cpp_vm_aspace_free(ptr.as_ptr());
438 }
439 }
440
441 fn allocate(_value: Self) -> Result<NonNull<Self>, AllocError> {
442 Err(AllocError)
443 }
444}