fdf_core/
handle.rs

1// Copyright 2024 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//! Safe bindings for driver runtime handles and collections of mixed driver and zircon
6//! handles.
7
8use fdf_sys::*;
9
10use core::marker::PhantomData;
11use core::mem::ManuallyDrop;
12use core::num::NonZero;
13use core::ops::Deref;
14
15use zx::HandleBased;
16pub use zx::{Handle as ZirconHandle, HandleRef as ZirconHandleRef};
17
18pub use fdf_sys::fdf_handle_t;
19
20/// A handle representing some resource managed by the driver runtime.
21#[repr(C)]
22#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
23pub struct DriverHandle(NonZero<fdf_handle_t>);
24
25impl DriverHandle {
26    /// Constructs a [`DriverHandle`] for the given [`fdf_handle_t`]
27    ///
28    /// # Safety
29    ///
30    /// The caller is responsible for ensuring that the handle given is a valid driver handle:
31    ///
32    /// - It has the marker bits set correctly
33    /// - It is not "owned" elsewhere, or that it will not call [`Drop::drop`] on this new
34    /// object if it is.
35    pub unsafe fn new_unchecked(handle: NonZero<fdf_handle_t>) -> Self {
36        Self(handle)
37    }
38
39    /// Gets a [`DriverHandleRef`] of this handle
40    pub fn as_handle_ref(&self) -> DriverHandleRef<'_> {
41        DriverHandleRef(ManuallyDrop::new(Self(self.0)), PhantomData)
42    }
43
44    /// Gets the raw handle object
45    ///
46    /// # Safety
47    ///
48    /// The caller must be sure to not let this handle outlive the lifetime of the object it
49    /// came from.
50    pub unsafe fn get_raw(&self) -> NonZero<fdf_handle_t> {
51        self.0
52    }
53
54    /// Turns this handle into its raw handle number, without dropping the handle.
55    /// The caller is responsible for ensuring that the handle is released or reconstituted
56    /// into a [`DriverHandle`].
57    pub fn into_raw(self) -> NonZero<fdf_handle_t> {
58        let handle = self.0;
59        // prevent this from dropping and invalidating the handle
60        core::mem::forget(self);
61        handle
62    }
63}
64
65impl Drop for DriverHandle {
66    fn drop(&mut self) {
67        // SAFETY: We require a nonzero handle to construct this and we should own the
68        // handle, so it should be safe to close it.
69        unsafe { fdf_handle_close(self.0.get()) };
70    }
71}
72
73/// An unowned reference to a driver handle type
74#[derive(Debug)]
75pub struct DriverHandleRef<'a>(ManuallyDrop<DriverHandle>, PhantomData<&'a DriverHandle>);
76
77impl<'a> Deref for DriverHandleRef<'a> {
78    type Target = DriverHandle;
79
80    fn deref(&self) -> &Self::Target {
81        &self.0
82    }
83}
84
85/// An enum of the two types of handles that can be represented in a [`MixedHandle`].
86#[derive(Debug)]
87pub enum MixedHandleType<Driver, Zircon> {
88    /// A driver runtime managed handle
89    Driver(Driver),
90    /// A kernel handle
91    Zircon(Zircon),
92}
93
94impl From<MixedHandle> for MixedHandleType<DriverHandle, ZirconHandle> {
95    fn from(value: MixedHandle) -> Self {
96        value.resolve()
97    }
98}
99
100/// A handle that might be either a [`DriverHandle`] or a [`ZirconHandle`], depending on its
101/// bit pattern.
102#[derive(Debug)]
103#[repr(C)]
104pub struct MixedHandle(NonZero<zx_handle_t>);
105
106impl MixedHandle {
107    /// Makes a `MixedHandle` from an existing raw handle.
108    ///
109    /// # Safety
110    ///
111    /// The handle must be valid and unowned, as this will take ownership
112    /// of the handle and drop it when this object drops.
113    pub unsafe fn from_raw(handle: NonZero<fdf_handle_t>) -> Self {
114        Self(handle)
115    }
116
117    /// Makes a `MixedHandle` from an existing raw handle that might be
118    /// zeroed (invalid).
119    ///
120    /// # Safety
121    ///
122    /// The handle must be valid and unowned, as this will take ownership
123    /// of the handle and drop it when this object drops.
124    pub unsafe fn try_from_raw(handle: fdf_handle_t) -> Option<Self> {
125        NonZero::new(handle).map(|handle| {
126            // SAFETY: the caller promises this is valid and unowned
127            unsafe { Self::from_raw(handle) }
128        })
129    }
130
131    /// Makes a `MixedHandle` from an existing [`ZirconHandle`]. Returns
132    /// [`None`] if the handle is invalid.
133    pub fn from_zircon_handle(handle: ZirconHandle) -> Option<Self> {
134        if handle.is_invalid() {
135            None
136        } else {
137            // SAFETY: if `ZirconHandle::is_invalid` returns false, then the
138            // handle is `NonZero`.
139            Some(Self(unsafe { NonZero::new_unchecked(handle.into_raw()) }))
140        }
141    }
142
143    /// Evaluates whether the contained handle is a driver handle or not
144    pub fn is_driver(&self) -> bool {
145        self.0.get() & 0b1 == 0b0
146    }
147
148    /// Resolves the handle to the appropriate real handle type
149    pub fn resolve(self) -> MixedHandleType<DriverHandle, ZirconHandle> {
150        let res = if self.is_driver() {
151            MixedHandleType::Driver(DriverHandle(self.0))
152        } else {
153            // SAFETY: any non-zero handle that isn't a driver handle must be a
154            // zircon handle of some sort.
155            MixedHandleType::Zircon(unsafe { ZirconHandle::from_raw(self.0.get()) })
156        };
157        // forget self so we don't try to drop the handle we just put
158        // in the enum
159        core::mem::forget(self);
160        res
161    }
162
163    /// Resolves the handle to an appropriate real unowned handle type
164    pub fn resolve_ref(&self) -> MixedHandleType<DriverHandleRef<'_>, ZirconHandleRef<'_>> {
165        if self.is_driver() {
166            MixedHandleType::Driver(DriverHandleRef(
167                ManuallyDrop::new(DriverHandle(self.0)),
168                PhantomData,
169            ))
170        } else {
171            // SAFETY: any non-zero handle that isn't a driver handle must
172            // be a zircon handle of some sort.
173            MixedHandleType::Zircon(unsafe { ZirconHandleRef::from_raw_handle(self.0.get()) })
174        }
175    }
176}
177
178impl From<DriverHandle> for MixedHandle {
179    fn from(value: DriverHandle) -> Self {
180        let handle = value.0;
181        // SAFETY: the handle is valid by construction since it was taken
182        // from a correctly created `DriverHandle`, and we `forget` the
183        // `DriverHandle` so we can take ownership of the handle.
184        unsafe {
185            core::mem::forget(value);
186            MixedHandle::from_raw(handle)
187        }
188    }
189}
190
191impl Drop for MixedHandle {
192    fn drop(&mut self) {
193        let handle = if self.is_driver() {
194            MixedHandleType::Driver(DriverHandle(self.0))
195        } else {
196            // SAFETY: any non-zero handle that isn't a driver handle must
197            // be a zircon handle of some sort.
198            MixedHandleType::Zircon(unsafe { ZirconHandle::from_raw(self.0.get()) })
199        };
200        drop(handle)
201    }
202}
203
204#[cfg(test)]
205mod tests {
206    use zx::{Port, Status};
207
208    use super::*;
209
210    /// Creates a valid `DriverHandle` by creating a driver channel pair and returning one of them.
211    fn make_driver_handle() -> DriverHandle {
212        let (mut left, mut right) = Default::default();
213        Status::ok(unsafe { fdf_channel_create(0, &mut left, &mut right) }).unwrap();
214        unsafe { fdf_handle_close(right) };
215
216        DriverHandle(NonZero::new(left).unwrap())
217    }
218
219    #[test]
220    fn handle_sizes() {
221        assert_eq!(size_of::<fdf_handle_t>(), size_of::<Option<DriverHandle>>());
222        assert_eq!(size_of::<fdf_handle_t>(), size_of::<Option<MixedHandle>>());
223    }
224
225    #[test]
226    fn driver_handle_roundtrip() {
227        let handle = make_driver_handle();
228        let mixed_handle = unsafe { MixedHandle::from_raw(handle.into_raw()) };
229        assert!(mixed_handle.is_driver());
230
231        let MixedHandleType::Driver(_handle) = mixed_handle.resolve() else {
232            panic!("driver handle did not translate back to a driver handle");
233        };
234    }
235
236    #[test]
237    fn zircon_handle_roundtrip() {
238        let handle = Port::create();
239        let mixed_handle =
240            unsafe { MixedHandle::from_raw(NonZero::new(handle.into_raw()).unwrap()) };
241        assert!(!mixed_handle.is_driver());
242
243        let MixedHandleType::Zircon(_handle) = mixed_handle.resolve() else {
244            panic!("zircon handle did not translate back to a zircon handle");
245        };
246    }
247}