blob: b6cdd6eec174b9647c1bb5b54f340ca7058a11b1 [file]
// Copyright 2026 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::arch_vm_aspace::ArchMmuFlags;
use crate::vm_object::VmObject;
use core::marker::{PhantomData, PhantomPinned};
use core::ptr::NonNull;
use fbl::RefPtr;
use kalloc::AllocError;
use zr::ToMutPtr;
use zx_status::Status;
unsafe extern "C" {
fn cpp_vm_mapping_get_ref_counted(mapping: *mut VmMapping) -> *mut fbl::RefCounted;
fn cpp_vm_mapping_free(mapping: *mut VmMapping);
fn cpp_vm_mapping_destroy(mapping: *mut VmMapping) -> i32;
fn cpp_vm_mapping_base(mapping: *mut VmMapping) -> usize;
fn cpp_vm_mapping_size(mapping: *mut VmMapping) -> usize;
fn cpp_vm_mapping_flags(mapping: *mut VmMapping) -> u32;
fn cpp_vm_mapping_object_offset(mapping: *mut VmMapping) -> u64;
fn cpp_vm_mapping_decommit_range(mapping: *mut VmMapping, offset: usize, len: usize) -> i32;
fn cpp_vm_mapping_map_range(
mapping: *mut VmMapping,
offset: usize,
len: usize,
commit: bool,
ignore_existing: bool,
) -> i32;
fn cpp_vm_mapping_debug_unmap(mapping: *mut VmMapping, base: usize, size: usize) -> i32;
fn cpp_vm_mapping_debug_protect(
mapping: *mut VmMapping,
base: usize,
size: usize,
new_arch_mmu_flags: ArchMmuFlags,
) -> i32;
fn cpp_vm_mapping_vmo(mapping: *mut VmMapping) -> *const VmObject;
}
/// A leaf mapping that maps a VMO into the address space.
///
/// This is an opaque FFI wrapper around Zircon's C++ `VmMapping` class.
#[repr(C)]
pub struct VmMapping {
_data: [u8; 0],
_marker: PhantomData<PhantomPinned>,
}
impl VmMapping {
/// Destroys this mapping, unmapping all pages and removing dependencies on the underlying VMO.
pub fn destroy(&self) -> Result<(), Status> {
Status::ok(unsafe { cpp_vm_mapping_destroy(self.to_mut_ptr()) })
}
/// Returns the base virtual address of this mapping.
pub fn base(&self) -> usize {
unsafe { cpp_vm_mapping_base(self.to_mut_ptr()) }
}
/// Returns the size in bytes of this mapping.
pub fn size(&self) -> usize {
unsafe { cpp_vm_mapping_size(self.to_mut_ptr()) }
}
/// Returns the creation flags of this mapping.
pub fn flags(&self) -> u32 {
unsafe { cpp_vm_mapping_flags(self.to_mut_ptr()) }
}
/// Returns the offset into the underlying VMO for this mapping.
pub fn object_offset(&self) -> u64 {
unsafe { cpp_vm_mapping_object_offset(self.to_mut_ptr()) }
}
/// Convenience wrapper for vmo()->DecommitRange() with the necessary
/// offset modification and locking.
pub fn decommit_range(&self, offset: usize, len: usize) -> Result<(), Status> {
Status::ok(unsafe { cpp_vm_mapping_decommit_range(self.to_mut_ptr(), offset, len) })
}
/// Map in pages from the underlying vm object, optionally committing pages as it goes.
/// |ignore_existing| controls whether existing hardware mappings in the specified range should
/// be ignored or treated as an error. |ignore_existing| should only be set to true for user
/// mappings where populating mappings may already be racy with multiple threads, and where we
/// are already tolerant of mappings being arbitrarily created and destroyed.
pub fn map_range(
&self,
offset: usize,
len: usize,
commit: bool,
ignore_existing: bool,
) -> Result<(), Status> {
Status::ok(unsafe {
cpp_vm_mapping_map_range(self.to_mut_ptr(), offset, len, commit, ignore_existing)
})
}
/// Unlocked convenience wrapper around unmap for testing.
pub fn debug_unmap(&self, base: usize, size: usize) -> Result<(), Status> {
Status::ok(unsafe { cpp_vm_mapping_debug_unmap(self.to_mut_ptr(), base, size) })
}
/// Unlocked convenience wrapper around protect for testing.
pub fn debug_protect(
&self,
base: usize,
size: usize,
new_arch_mmu_flags: ArchMmuFlags,
) -> Result<(), Status> {
Status::ok(unsafe {
cpp_vm_mapping_debug_protect(self.to_mut_ptr(), base, size, new_arch_mmu_flags)
})
}
/// Returns the underlying VMO backing this mapping.
pub fn vmo(&self) -> Option<RefPtr<VmObject>> {
unsafe { RefPtr::try_from_raw(cpp_vm_mapping_vmo(self.to_mut_ptr())) }
}
}
impl fbl::HasRefCount for VmMapping {
fn ref_count(&self) -> &fbl::RefCounted {
unsafe { &*cpp_vm_mapping_get_ref_counted(self.to_mut_ptr()) }
}
}
unsafe impl fbl::Recyclable for VmMapping {
unsafe fn recycle(ptr: NonNull<Self>) {
unsafe {
cpp_vm_mapping_free(ptr.as_ptr());
}
}
fn allocate(_value: Self) -> Result<NonNull<Self>, AllocError> {
Err(AllocError)
}
}