zx/
iob.rs

1// Copyright 2025 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 iobuffer objects.
6
7use crate::{ok, sys, AsHandleRef, Handle, HandleBased, HandleRef, Status};
8use bitflags::bitflags;
9
10// TODO(https://fxbug.dev/389788832): Point this at the right place when the documentation is fixed.
11/// An object representing a Zircon
12/// [IOBuffer](https://fuchsia.dev/reference/syscalls/iob_create).
13///
14/// As essentially a subtype of `Handle`, it can be freely interconverted.
15#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
16#[repr(transparent)]
17pub struct Iob(Handle);
18impl_handle_based!(Iob);
19
20#[derive(Default)]
21pub struct IobOptions;
22
23#[derive(Clone, Copy)]
24pub enum IobRegionType {
25    Private { size: u64, options: IobRegionPrivateOptions },
26}
27
28impl IobRegionType {
29    fn to_raw(&self) -> (sys::zx_iob_region_type_t, sys::zx_iob_region_extension_t) {
30        match self {
31            IobRegionType::Private { .. } => (
32                sys::ZX_IOB_REGION_TYPE_PRIVATE,
33                sys::zx_iob_region_extension_t { private_region: Default::default() },
34            ),
35        }
36    }
37}
38
39#[derive(Clone, Copy, Default)]
40pub struct IobRegionPrivateOptions;
41
42pub struct IobRegion {
43    pub region_type: IobRegionType,
44    pub access: IobAccess,
45    pub discipline: IobDiscipline,
46}
47
48bitflags! {
49    #[repr(transparent)]
50    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
51    pub struct IobAccess: u32 {
52        const EP0_CAN_MAP_READ = sys::ZX_IOB_ACCESS_EP0_CAN_MAP_READ;
53        const EP0_CAN_MAP_WRITE = sys::ZX_IOB_ACCESS_EP0_CAN_MAP_WRITE;
54        const EP0_CAN_MEDIATED_READ = sys::ZX_IOB_ACCESS_EP0_CAN_MEDIATED_READ;
55        const EP0_CAN_MEDIATED_WRITE = sys::ZX_IOB_ACCESS_EP0_CAN_MEDIATED_WRITE;
56        const EP1_CAN_MAP_READ = sys::ZX_IOB_ACCESS_EP1_CAN_MAP_READ;
57        const EP1_CAN_MAP_WRITE = sys::ZX_IOB_ACCESS_EP1_CAN_MAP_WRITE;
58        const EP1_CAN_MEDIATED_READ = sys::ZX_IOB_ACCESS_EP1_CAN_MEDIATED_READ;
59        const EP1_CAN_MEDIATED_WRITE = sys::ZX_IOB_ACCESS_EP1_CAN_MEDIATED_WRITE;
60    }
61}
62
63#[derive(Clone, Copy)]
64pub enum IobDiscipline {
65    None,
66}
67
68impl IobDiscipline {
69    fn to_raw(&self) -> sys::zx_iob_discipline_t {
70        match self {
71            IobDiscipline::None => sys::zx_iob_discipline_t {
72                r#type: sys::ZX_IOB_DISCIPLINE_TYPE_NONE,
73                ..Default::default()
74            },
75        }
76    }
77}
78
79impl Iob {
80    /// Creates an IOBuffer.
81    ///
82    /// Wraps the [zx_iob_create](https://fuchsia.dev/reference/syscalls/iob_create) syscall.
83    pub fn create(_options: IobOptions, regions: &[IobRegion]) -> Result<(Iob, Iob), Status> {
84        let raw_regions: Vec<_> = regions
85            .iter()
86            .map(|r| {
87                let (r#type, extension) = r.region_type.to_raw();
88                sys::zx_iob_region_t {
89                    r#type,
90                    access: r.access.bits(),
91                    size: match &r.region_type {
92                        IobRegionType::Private { size, .. } => *size,
93                    },
94                    discipline: r.discipline.to_raw(),
95                    extension,
96                }
97            })
98            .collect();
99        let mut handle1 = 0;
100        let mut handle2 = 0;
101        let status = unsafe {
102            sys::zx_iob_create(
103                0,
104                raw_regions.as_ptr() as *const u8,
105                raw_regions.len(),
106                &mut handle1,
107                &mut handle2,
108            )
109        };
110        ok(status)?;
111        unsafe { Ok((Iob::from(Handle::from_raw(handle1)), Iob::from(Handle::from_raw(handle2)))) }
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::{Iob, IobAccess, IobDiscipline, IobRegion, IobRegionType};
118    use crate::{Unowned, Vmar, VmarFlags};
119    use std::sync::atomic::{AtomicU64, Ordering};
120
121    #[test]
122    fn test_create_iob() {
123        let region_size = zx::system_get_page_size() as usize * 8;
124        let (ep0, ep1) = Iob::create(
125            Default::default(),
126            &[IobRegion {
127                region_type: IobRegionType::Private {
128                    size: region_size as u64,
129                    options: Default::default(),
130                },
131                access: IobAccess::EP0_CAN_MAP_READ
132                    | IobAccess::EP0_CAN_MAP_WRITE
133                    | IobAccess::EP1_CAN_MAP_READ,
134                discipline: IobDiscipline::None,
135            }],
136        )
137        .expect("create failed");
138
139        // We can't use fuchsia_runtime other than to get the handle, because its Vmar type is
140        // considered distinct from crate::Vmar.
141        let root_vmar =
142            unsafe { Unowned::<Vmar>::from_raw_handle(fuchsia_runtime::zx_vmar_root_self()) };
143
144        let write_addr = root_vmar
145            .map_iob(VmarFlags::PERM_READ | VmarFlags::PERM_WRITE, 0, &ep0, 0, 0, region_size)
146            .expect("map_iob failed");
147        let read_addr = root_vmar
148            .map_iob(VmarFlags::PERM_READ, 0, &ep1, 0, 0, region_size)
149            .expect("map_iob failed");
150
151        const VALUE: u64 = 0x123456789abcdef;
152
153        unsafe { &*(write_addr as *const AtomicU64) }.store(VALUE, Ordering::Relaxed);
154
155        assert_eq!(unsafe { &*(read_addr as *const AtomicU64) }.load(Ordering::Relaxed), VALUE);
156
157        unsafe {
158            root_vmar.unmap(write_addr, region_size).expect("unmap failed");
159            root_vmar.unmap(read_addr, region_size).expect("unmap failed");
160        }
161    }
162}