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