use crate::{
object_get_info, object_get_info_single, object_get_info_vec, ok, sys, AsHandleRef, Handle,
HandleBased, HandleRef, Koid, Name, ObjectQuery, Status, Topic, Vmo,
};
use bitflags::bitflags;
use std::mem::MaybeUninit;
use zerocopy::{FromBytes, Immutable};
use zx_sys::PadByte;
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct Vmar(Handle);
impl_handle_based!(Vmar);
sys::zx_info_vmar_t!(VmarInfo);
impl From<sys::zx_info_vmar_t> for VmarInfo {
fn from(sys::zx_info_vmar_t { base, len }: sys::zx_info_vmar_t) -> VmarInfo {
VmarInfo { base, len }
}
}
unsafe impl ObjectQuery for VmarInfo {
const TOPIC: Topic = Topic::VMAR;
type InfoTy = VmarInfo;
}
struct VmarMapsInfo;
unsafe impl ObjectQuery for VmarMapsInfo {
const TOPIC: Topic = Topic::VMAR_MAPS;
type InfoTy = MapInfo;
}
static_assert_align!(
#[repr(C)]
#[derive(Copy, Clone, FromBytes, Immutable)]
<sys::zx_info_maps_t> pub struct MapInfo {
pub name <name>: Name,
pub base <base>: usize,
pub size <size>: usize,
pub depth <depth>: usize,
r#type <r#type>: sys::zx_info_maps_type_t,
u <u>: sys::InfoMapsTypeUnion,
}
);
impl MapInfo {
pub fn new(
name: Name,
base: usize,
size: usize,
depth: usize,
details: MapDetails<'_>,
) -> Result<MapInfo, Status> {
let (map_type, map_details_union) = match details {
MapDetails::None => (
sys::ZX_INFO_MAPS_TYPE_NONE,
sys::InfoMapsTypeUnion { mapping: Default::default() },
),
MapDetails::AddressSpace => (
sys::ZX_INFO_MAPS_TYPE_ASPACE,
sys::InfoMapsTypeUnion { mapping: Default::default() },
),
MapDetails::Vmar => (
sys::ZX_INFO_MAPS_TYPE_VMAR,
sys::InfoMapsTypeUnion { mapping: Default::default() },
),
MapDetails::Mapping(mapping_details) => (
sys::ZX_INFO_MAPS_TYPE_MAPPING,
sys::InfoMapsTypeUnion {
mapping: {
let mut mapping: sys::zx_info_maps_mapping_t = Default::default();
mapping.mmu_flags = mapping_details.mmu_flags.bits();
mapping.vmo_koid = mapping_details.vmo_koid.raw_koid();
mapping.vmo_offset = mapping_details.vmo_offset;
mapping.committed_bytes = mapping_details.committed_bytes;
mapping.populated_bytes = mapping_details.populated_bytes;
mapping.committed_private_bytes = mapping_details.committed_private_bytes;
mapping.populated_private_bytes = mapping_details.populated_private_bytes;
mapping.committed_scaled_bytes = mapping_details.committed_scaled_bytes;
mapping.populated_scaled_bytes = mapping_details.populated_scaled_bytes;
mapping.committed_fractional_scaled_bytes =
mapping_details.committed_fractional_scaled_bytes;
mapping.populated_fractional_scaled_bytes =
mapping_details.populated_fractional_scaled_bytes;
mapping
},
},
),
MapDetails::Unknown => {
return Err(Status::INVALID_ARGS);
}
};
Ok(MapInfo { name, base, size, depth, r#type: map_type, u: map_details_union })
}
pub fn details<'a>(&'a self) -> MapDetails<'a> {
match self.r#type {
sys::ZX_INFO_MAPS_TYPE_NONE => MapDetails::None,
sys::ZX_INFO_MAPS_TYPE_ASPACE => MapDetails::AddressSpace,
sys::ZX_INFO_MAPS_TYPE_VMAR => MapDetails::Vmar,
sys::ZX_INFO_MAPS_TYPE_MAPPING => {
let raw_mapping = unsafe { &self.u.mapping };
let mapping_details = zerocopy::transmute_ref!(raw_mapping);
MapDetails::Mapping(mapping_details)
}
_ => MapDetails::Unknown,
}
}
}
impl std::fmt::Debug for MapInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MapInfo")
.field("name", &self.name)
.field("base", &format_args!("{:#x}", self.base))
.field("size", &self.size)
.field("depth", &self.depth)
.field("details", &self.details())
.finish()
}
}
#[derive(Copy, Clone, Debug)]
pub enum MapDetails<'a> {
Unknown,
None,
AddressSpace,
Vmar,
Mapping(&'a MappingDetails),
}
impl<'a> MapDetails<'a> {
pub fn as_mapping(&'a self) -> Option<&'a MappingDetails> {
match self {
Self::Mapping(d) => Some(*d),
_ => None,
}
}
}
static_assert_align!(
#[repr(C)]
#[derive(Copy, Clone, Debug, FromBytes, Immutable)]
<sys::zx_info_maps_mapping_t> pub struct MappingDetails {
pub mmu_flags <mmu_flags>: VmarFlagsExtended,
padding1: [PadByte; 4],
pub vmo_koid <vmo_koid>: Koid,
pub vmo_offset <vmo_offset>: u64,
pub committed_bytes <committed_bytes>: usize,
pub populated_bytes <populated_bytes>: usize,
pub committed_private_bytes <committed_private_bytes>: usize,
pub populated_private_bytes <populated_private_bytes>: usize,
pub committed_scaled_bytes <committed_scaled_bytes>: usize,
pub populated_scaled_bytes <populated_scaled_bytes>: usize,
pub committed_fractional_scaled_bytes <committed_fractional_scaled_bytes>: u64,
pub populated_fractional_scaled_bytes <populated_fractional_scaled_bytes>: u64,
}
);
impl Default for MappingDetails {
fn default() -> MappingDetails {
Self::from(sys::zx_info_maps_mapping_t::default())
}
}
impl From<sys::zx_info_maps_mapping_t> for MappingDetails {
fn from(info: sys::zx_info_maps_mapping_t) -> MappingDetails {
zerocopy::transmute!(info)
}
}
impl Vmar {
pub fn allocate(
&self,
offset: usize,
size: usize,
flags: VmarFlags,
) -> Result<(Vmar, usize), Status> {
let mut mapped = 0;
let mut handle = 0;
let status = unsafe {
sys::zx_vmar_allocate(
self.raw_handle(),
flags.bits(),
offset,
size,
&mut handle,
&mut mapped,
)
};
ok(status)?;
unsafe { Ok((Vmar::from(Handle::from_raw(handle)), mapped)) }
}
pub fn map(
&self,
vmar_offset: usize,
vmo: &Vmo,
vmo_offset: u64,
len: usize,
flags: VmarFlags,
) -> Result<usize, Status> {
let flags = VmarFlagsExtended::from_bits_truncate(flags.bits());
unsafe { self.map_unsafe(vmar_offset, vmo, vmo_offset, len, flags) }
}
pub unsafe fn map_unsafe(
&self,
vmar_offset: usize,
vmo: &Vmo,
vmo_offset: u64,
len: usize,
flags: VmarFlagsExtended,
) -> Result<usize, Status> {
let mut mapped = 0;
let status = sys::zx_vmar_map(
self.0.raw_handle(),
flags.bits(),
vmar_offset,
vmo.raw_handle(),
vmo_offset,
len,
&mut mapped,
);
ok(status).map(|_| mapped)
}
pub unsafe fn unmap(&self, addr: usize, len: usize) -> Result<(), Status> {
ok(unsafe { sys::zx_vmar_unmap(self.0.raw_handle(), addr, len) })
}
pub fn op_range(&self, op: VmarOp, addr: usize, len: usize) -> Result<(), Status> {
ok(unsafe {
sys::zx_vmar_op_range(
self.0.raw_handle(),
op.into_raw(),
addr,
len,
std::ptr::null_mut(),
0,
)
})
}
pub unsafe fn protect(&self, addr: usize, len: usize, flags: VmarFlags) -> Result<(), Status> {
ok(unsafe { sys::zx_vmar_protect(self.raw_handle(), flags.bits(), addr, len) })
}
pub unsafe fn destroy(&self) -> Result<(), Status> {
ok(unsafe { sys::zx_vmar_destroy(self.raw_handle()) })
}
pub fn info(&self) -> Result<VmarInfo, Status> {
Ok(object_get_info_single::<VmarInfo>(self.as_handle_ref())?)
}
pub fn info_maps<'a>(
&self,
buf: &'a mut [MaybeUninit<MapInfo>],
) -> Result<(&'a mut [MapInfo], &'a mut [MaybeUninit<MapInfo>], usize), Status> {
object_get_info::<VmarMapsInfo>(self.as_handle_ref(), buf)
}
pub fn info_maps_vec(&self) -> Result<Vec<MapInfo>, Status> {
object_get_info_vec::<VmarMapsInfo>(self.as_handle_ref())
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct VmarOp(u32);
impl VmarOp {
pub fn from_raw(raw: u32) -> Self {
Self(raw)
}
pub fn into_raw(self) -> u32 {
self.0
}
}
assoc_values!(VmarOp, [
COMMIT = sys::ZX_VMAR_OP_COMMIT;
DECOMMIT = sys::ZX_VMAR_OP_DECOMMIT;
PREFETCH = sys::ZX_VMAR_OP_PREFETCH;
MAP_RANGE = sys::ZX_VMAR_OP_MAP_RANGE;
DONT_NEED = sys::ZX_VMAR_OP_DONT_NEED;
ALWAYS_NEED = sys::ZX_VMAR_OP_ALWAYS_NEED;
]);
macro_rules! vmar_flags {
(
safe: [$($safe_name:ident : $safe_sys_name:ident,)*],
extended: [$($ex_name:ident : $ex_sys_name:ident,)*],
) => {
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, FromBytes, Immutable)]
pub struct VmarFlags(sys::zx_vm_option_t);
bitflags! {
impl VmarFlags: sys::zx_vm_option_t {
$(
const $safe_name = sys::$safe_sys_name;
)*
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, FromBytes, Immutable, Default)]
pub struct VmarFlagsExtended(sys::zx_vm_option_t);
bitflags! {
impl VmarFlagsExtended: sys::zx_vm_option_t {
$(
const $safe_name = sys::$safe_sys_name;
)*
$(
const $ex_name = sys::$ex_sys_name;
)*
}
}
};
}
vmar_flags! {
safe: [
PERM_READ: ZX_VM_PERM_READ,
PERM_WRITE: ZX_VM_PERM_WRITE,
PERM_EXECUTE: ZX_VM_PERM_EXECUTE,
COMPACT: ZX_VM_COMPACT,
SPECIFIC: ZX_VM_SPECIFIC,
CAN_MAP_SPECIFIC: ZX_VM_CAN_MAP_SPECIFIC,
CAN_MAP_READ: ZX_VM_CAN_MAP_READ,
CAN_MAP_WRITE: ZX_VM_CAN_MAP_WRITE,
CAN_MAP_EXECUTE: ZX_VM_CAN_MAP_EXECUTE,
MAP_RANGE: ZX_VM_MAP_RANGE,
REQUIRE_NON_RESIZABLE: ZX_VM_REQUIRE_NON_RESIZABLE,
ALLOW_FAULTS: ZX_VM_ALLOW_FAULTS,
OFFSET_IS_UPPER_LIMIT: ZX_VM_OFFSET_IS_UPPER_LIMIT,
PERM_READ_IF_XOM_UNSUPPORTED: ZX_VM_PERM_READ_IF_XOM_UNSUPPORTED,
ALIGN_1KB: ZX_VM_ALIGN_1KB,
ALIGN_2KB: ZX_VM_ALIGN_2KB,
ALIGN_4KB: ZX_VM_ALIGN_4KB,
ALIGN_8KB: ZX_VM_ALIGN_8KB,
ALIGN_16KB: ZX_VM_ALIGN_16KB,
ALIGN_32KB: ZX_VM_ALIGN_32KB,
ALIGN_64KB: ZX_VM_ALIGN_64KB,
ALIGN_128KB: ZX_VM_ALIGN_128KB,
ALIGN_256KB: ZX_VM_ALIGN_256KB,
ALIGN_512KB: ZX_VM_ALIGN_512KB,
ALIGN_1MB: ZX_VM_ALIGN_1MB,
ALIGN_2MB: ZX_VM_ALIGN_2MB,
ALIGN_4MB: ZX_VM_ALIGN_4MB,
ALIGN_8MB: ZX_VM_ALIGN_8MB,
ALIGN_16MB: ZX_VM_ALIGN_16MB,
ALIGN_32MB: ZX_VM_ALIGN_32MB,
ALIGN_64MB: ZX_VM_ALIGN_64MB,
ALIGN_128MB: ZX_VM_ALIGN_128MB,
ALIGN_256MB: ZX_VM_ALIGN_256MB,
ALIGN_512MB: ZX_VM_ALIGN_512MB,
ALIGN_1GB: ZX_VM_ALIGN_1GB,
ALIGN_2GB: ZX_VM_ALIGN_2GB,
ALIGN_4GB: ZX_VM_ALIGN_4GB,
],
extended: [
SPECIFIC_OVERWRITE: ZX_VM_SPECIFIC_OVERWRITE,
],
}
#[cfg(test)]
mod tests {
use zx::{Status, VmarFlags};
#[test]
fn allocate_and_info() -> Result<(), Status> {
let size = usize::pow(2, 20); let root_vmar = fuchsia_runtime::vmar_root_self();
let (vmar, base) = root_vmar.allocate(0, size, VmarFlags::empty())?;
let info = vmar.info()?;
assert!(info.base == base);
assert!(info.len == size);
Ok(())
}
#[test]
fn root_vmar_info() -> Result<(), Status> {
let root_vmar = fuchsia_runtime::vmar_root_self();
let info = root_vmar.info()?;
assert!(info.base > 0);
assert!(info.len > 0);
Ok(())
}
#[test]
fn root_vmar_maps() {
let root_vmar = fuchsia_runtime::vmar_root_self();
let info = root_vmar.info_maps_vec().unwrap();
assert!(!info.is_empty());
}
}