fdf/
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    Driver(Driver),
89    Zircon(Zircon),
90}
91
92impl From<MixedHandle> for MixedHandleType<DriverHandle, ZirconHandle> {
93    fn from(value: MixedHandle) -> Self {
94        value.resolve()
95    }
96}
97
98/// A handle that might be either a [`DriverHandle`] or a [`ZirconHandle`], depending on its
99/// bit pattern.
100#[derive(Debug)]
101#[repr(C)]
102pub struct MixedHandle(NonZero<zx_handle_t>);
103
104impl MixedHandle {
105    /// Makes a `MixedHandle` from an existing raw handle.
106    ///
107    /// # Safety
108    ///
109    /// The handle must be valid and unowned, as this will take ownership
110    /// of the handle and drop it when this object drops.
111    pub unsafe fn from_raw(handle: NonZero<fdf_handle_t>) -> Self {
112        Self(handle)
113    }
114
115    /// Makes a `MixedHandle` from an existing raw handle that might be
116    /// zeroed (invalid).
117    ///
118    /// # Safety
119    ///
120    /// The handle must be valid and unowned, as this will take ownership
121    /// of the handle and drop it when this object drops.
122    pub unsafe fn try_from_raw(handle: fdf_handle_t) -> Option<Self> {
123        NonZero::new(handle).map(|handle| {
124            // SAFETY: the caller promises this is valid and unowned
125            unsafe { Self::from_raw(handle) }
126        })
127    }
128
129    /// Makes a `MixedHandle` from an existing [`ZirconHandle`]. Returns
130    /// [`None`] if the handle is invalid.
131    pub fn from_zircon_handle(handle: ZirconHandle) -> Option<Self> {
132        if handle.is_invalid() {
133            None
134        } else {
135            // SAFETY: if `ZirconHandle::is_invalid` returns false, then the
136            // handle is `NonZero`.
137            Some(Self(unsafe { NonZero::new_unchecked(handle.into_raw()) }))
138        }
139    }
140
141    /// Evaluates whether the contained handle is a driver handle or not
142    pub fn is_driver(&self) -> bool {
143        self.0.get() & 0b1 == 0b0
144    }
145
146    /// Resolves the handle to the appropriate real handle type
147    pub fn resolve(self) -> MixedHandleType<DriverHandle, ZirconHandle> {
148        let res = if self.is_driver() {
149            MixedHandleType::Driver(DriverHandle(self.0))
150        } else {
151            // SAFETY: any non-zero handle that isn't a driver handle must be a
152            // zircon handle of some sort.
153            MixedHandleType::Zircon(unsafe { ZirconHandle::from_raw(self.0.get()) })
154        };
155        // forget self so we don't try to drop the handle we just put
156        // in the enum
157        core::mem::forget(self);
158        res
159    }
160
161    /// Resolves the handle to an appropriate real unowned handle type
162    pub fn resolve_ref(&self) -> MixedHandleType<DriverHandleRef<'_>, ZirconHandleRef<'_>> {
163        if self.is_driver() {
164            MixedHandleType::Driver(DriverHandleRef(
165                ManuallyDrop::new(DriverHandle(self.0)),
166                PhantomData,
167            ))
168        } else {
169            // SAFETY: any non-zero handle that isn't a driver handle must
170            // be a zircon handle of some sort.
171            MixedHandleType::Zircon(unsafe { ZirconHandleRef::from_raw_handle(self.0.get()) })
172        }
173    }
174}
175
176impl From<DriverHandle> for MixedHandle {
177    fn from(value: DriverHandle) -> Self {
178        let handle = value.0;
179        // SAFETY: the handle is valid by construction since it was taken
180        // from a correctly created `DriverHandle`, and we `forget` the
181        // `DriverHandle` so we can take ownership of the handle.
182        unsafe {
183            core::mem::forget(value);
184            MixedHandle::from_raw(handle)
185        }
186    }
187}
188
189impl Drop for MixedHandle {
190    fn drop(&mut self) {
191        let handle = if self.is_driver() {
192            MixedHandleType::Driver(DriverHandle(self.0))
193        } else {
194            // SAFETY: any non-zero handle that isn't a driver handle must
195            // be a zircon handle of some sort.
196            MixedHandleType::Zircon(unsafe { ZirconHandle::from_raw(self.0.get()) })
197        };
198        drop(handle)
199    }
200}
201
202#[cfg(test)]
203mod tests {
204    use zx::{Port, Status};
205
206    use super::*;
207
208    /// Creates a valid `DriverHandle` by creating a driver channel pair and returning one of them.
209    fn make_driver_handle() -> DriverHandle {
210        let (mut left, mut right) = Default::default();
211        Status::ok(unsafe { fdf_channel_create(0, &mut left, &mut right) }).unwrap();
212        unsafe { fdf_handle_close(right) };
213
214        DriverHandle(NonZero::new(left).unwrap())
215    }
216
217    #[test]
218    fn handle_sizes() {
219        assert_eq!(size_of::<fdf_handle_t>(), size_of::<Option<DriverHandle>>());
220        assert_eq!(size_of::<fdf_handle_t>(), size_of::<Option<MixedHandle>>());
221    }
222
223    #[test]
224    fn driver_handle_roundtrip() {
225        let handle = make_driver_handle();
226        let mixed_handle = unsafe { MixedHandle::from_raw(handle.into_raw()) };
227        assert!(mixed_handle.is_driver());
228
229        let MixedHandleType::Driver(_handle) = mixed_handle.resolve() else {
230            panic!("driver handle did not translate back to a driver handle");
231        };
232    }
233
234    #[test]
235    fn zircon_handle_roundtrip() {
236        let handle = Port::create();
237        let mixed_handle =
238            unsafe { MixedHandle::from_raw(NonZero::new(handle.into_raw()).unwrap()) };
239        assert!(!mixed_handle.is_driver());
240
241        let MixedHandleType::Zircon(_handle) = mixed_handle.resolve() else {
242            panic!("zircon handle did not translate back to a zircon handle");
243        };
244    }
245}