33//! These functions will panic if called after exiting boot services.
44
55use crate :: data_types:: PhysicalAddress ;
6+ use crate :: proto:: { Protocol , ProtocolPointer } ;
67use core:: ffi:: c_void;
7- use core:: ops:: Deref ;
8+ use core:: ops:: { Deref , DerefMut } ;
89use core:: ptr:: { self , NonNull } ;
910use core:: slice;
1011use core:: sync:: atomic:: { AtomicPtr , Ordering } ;
11- use uefi:: { table, Handle , Result , StatusExt } ;
12+ use uefi:: { table, Handle , Result , Status , StatusExt } ;
1213
13- #[ cfg( doc) ]
14- use uefi:: Status ;
15-
16- pub use uefi:: table:: boot:: { AllocateType , SearchType } ;
14+ pub use uefi:: table:: boot:: { AllocateType , OpenProtocolAttributes , OpenProtocolParams , SearchType } ;
1715pub use uefi_raw:: table:: boot:: MemoryType ;
1816
1917/// Global image handle. This is only set by [`set_image_handle`], and it is
@@ -162,6 +160,60 @@ pub fn locate_handle_buffer(search_ty: SearchType) -> Result<HandleBuffer> {
162160 } )
163161}
164162
163+ /// Opens a protocol interface for a handle.
164+ ///
165+ /// See also `open_protocol_exclusive`, which provides a safe subset of this
166+ /// functionality.
167+ ///
168+ /// This function attempts to get the protocol implementation of a handle, based
169+ /// on the [protocol GUID].
170+ ///
171+ /// See [`OpenProtocolParams`] and [`OpenProtocolAttributes`] for details of the
172+ /// input parameters.
173+ ///
174+ /// If successful, a [`ScopedProtocol`] is returned that will automatically
175+ /// close the protocol interface when dropped.
176+ ///
177+ /// [protocol GUID]: uefi::data_types::Identify::GUID
178+ ///
179+ /// # Safety
180+ ///
181+ /// This function is unsafe because it can be used to open a protocol in ways
182+ /// that don't get tracked by the UEFI implementation. This could allow the
183+ /// protocol to be removed from a handle, or for the handle to be deleted
184+ /// entirely, while a reference to the protocol is still active. The caller is
185+ /// responsible for ensuring that the handle and protocol remain valid until the
186+ /// `ScopedProtocol` is dropped.
187+ ///
188+ /// # Errors
189+ ///
190+ /// * [`Status::INVALID_PARAMETER`]: an invalid combination of `params` and
191+ /// `attributes` was provided.
192+ /// * [`Status::UNSUPPORTED`]: the handle does not support the protocol.
193+ /// * [`Status::ACCESS_DENIED`] or [`Status::ALREADY_STARTED`]: the protocol is
194+ /// already open in a way that is incompatible with the new request.
195+ pub unsafe fn open_protocol < P : ProtocolPointer + ?Sized > (
196+ params : OpenProtocolParams ,
197+ attributes : OpenProtocolAttributes ,
198+ ) -> Result < ScopedProtocol < P > > {
199+ let bt = boot_services_raw_panicking ( ) ;
200+ let bt = unsafe { bt. as_ref ( ) } ;
201+
202+ let mut interface = ptr:: null_mut ( ) ;
203+ ( bt. open_protocol ) (
204+ params. handle . as_ptr ( ) ,
205+ & P :: GUID ,
206+ & mut interface,
207+ params. agent . as_ptr ( ) ,
208+ Handle :: opt_to_ptr ( params. controller ) ,
209+ attributes as u32 ,
210+ )
211+ . to_result_with_val ( || ScopedProtocol {
212+ interface : NonNull :: new ( P :: mut_ptr_from_ffi ( interface) ) ,
213+ open_params : params,
214+ } )
215+ }
216+
165217/// A buffer returned by [`locate_handle_buffer`] that contains an array of
166218/// [`Handle`]s that support the requested protocol.
167219#[ derive( Debug , Eq , PartialEq ) ]
@@ -183,3 +235,78 @@ impl Deref for HandleBuffer {
183235 unsafe { slice:: from_raw_parts ( self . buffer . as_ptr ( ) , self . count ) }
184236 }
185237}
238+
239+ /// An open protocol interface. Automatically closes the protocol
240+ /// interface on drop.
241+ ///
242+ /// Most protocols have interface data associated with them. `ScopedProtocol`
243+ /// implements [`Deref`] and [`DerefMut`] to access this data. A few protocols
244+ /// (such as [`DevicePath`] and [`LoadedImageDevicePath`]) may be installed with
245+ /// null interface data, in which case [`Deref`] and [`DerefMut`] will
246+ /// panic. The [`get`] and [`get_mut`] methods may be used to access the
247+ /// optional interface data without panicking.
248+ ///
249+ /// [`DevicePath`]: crate::proto::device_path::DevicePath
250+ /// [`LoadedImageDevicePath`]: crate::proto::device_path::LoadedImageDevicePath
251+ /// [`get`]: ScopedProtocol::get
252+ /// [`get_mut`]: ScopedProtocol::get_mut
253+ #[ derive( Debug ) ]
254+ pub struct ScopedProtocol < P : Protocol + ?Sized > {
255+ /// The protocol interface.
256+ interface : Option < NonNull < P > > ,
257+ open_params : OpenProtocolParams ,
258+ }
259+
260+ impl < P : Protocol + ?Sized > Drop for ScopedProtocol < P > {
261+ fn drop ( & mut self ) {
262+ let bt = boot_services_raw_panicking ( ) ;
263+ let bt = unsafe { bt. as_ref ( ) } ;
264+
265+ let status = unsafe {
266+ ( bt. close_protocol ) (
267+ self . open_params . handle . as_ptr ( ) ,
268+ & P :: GUID ,
269+ self . open_params . agent . as_ptr ( ) ,
270+ Handle :: opt_to_ptr ( self . open_params . controller ) ,
271+ )
272+ } ;
273+ // All of the error cases for close_protocol boil down to
274+ // calling it with a different set of parameters than what was
275+ // passed to open_protocol. The public API prevents such errors,
276+ // and the error can't be propagated out of drop anyway, so just
277+ // assert success.
278+ assert_eq ! ( status, Status :: SUCCESS ) ;
279+ }
280+ }
281+
282+ impl < P : Protocol + ?Sized > Deref for ScopedProtocol < P > {
283+ type Target = P ;
284+
285+ #[ track_caller]
286+ fn deref ( & self ) -> & Self :: Target {
287+ unsafe { self . interface . unwrap ( ) . as_ref ( ) }
288+ }
289+ }
290+
291+ impl < P : Protocol + ?Sized > DerefMut for ScopedProtocol < P > {
292+ #[ track_caller]
293+ fn deref_mut ( & mut self ) -> & mut Self :: Target {
294+ unsafe { self . interface . unwrap ( ) . as_mut ( ) }
295+ }
296+ }
297+
298+ impl < P : Protocol + ?Sized > ScopedProtocol < P > {
299+ /// Get the protocol interface data, or `None` if the open protocol's
300+ /// interface is null.
301+ #[ must_use]
302+ pub fn get ( & self ) -> Option < & P > {
303+ self . interface . map ( |p| unsafe { p. as_ref ( ) } )
304+ }
305+
306+ /// Get the protocol interface data, or `None` if the open protocol's
307+ /// interface is null.
308+ #[ must_use]
309+ pub fn get_mut ( & mut self ) -> Option < & mut P > {
310+ self . interface . map ( |mut p| unsafe { p. as_mut ( ) } )
311+ }
312+ }
0 commit comments