|
10 | 10 | //! - More information about protocols can be found [here](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/3_foundation/36_protocols_and_handles)
|
11 | 11 |
|
12 | 12 | use r_efi::efi::{self, Guid};
|
13 |
| -use r_efi::protocols::{device_path, device_path_to_text, shell}; |
| 13 | +use r_efi::protocols::{device_path, device_path_to_text, service_binding, shell}; |
14 | 14 |
|
15 | 15 | use crate::ffi::{OsStr, OsString};
|
16 | 16 | use crate::io::{self, const_error};
|
@@ -500,3 +500,62 @@ pub(crate) fn get_device_path_from_map(map: &Path) -> io::Result<BorrowedDeviceP
|
500 | 500 |
|
501 | 501 | Ok(BorrowedDevicePath::new(protocol))
|
502 | 502 | }
|
| 503 | + |
| 504 | +/// Helper for UEFI Protocols which are created and destroyed using |
| 505 | +/// [EFI_SERVICE_BINDING_PROTCOL](https://uefi.org/specs/UEFI/2.11/11_Protocols_UEFI_Driver_Model.html#efi-service-binding-protocol) |
| 506 | +pub(crate) struct ServiceProtocol { |
| 507 | + service_guid: r_efi::efi::Guid, |
| 508 | + handle: NonNull<crate::ffi::c_void>, |
| 509 | + child_handle: NonNull<crate::ffi::c_void>, |
| 510 | +} |
| 511 | + |
| 512 | +impl ServiceProtocol { |
| 513 | + #[expect(dead_code)] |
| 514 | + pub(crate) fn open(service_guid: r_efi::efi::Guid) -> io::Result<Self> { |
| 515 | + let handles = locate_handles(service_guid)?; |
| 516 | + |
| 517 | + for handle in handles { |
| 518 | + if let Ok(protocol) = open_protocol::<service_binding::Protocol>(handle, service_guid) { |
| 519 | + let Ok(child_handle) = Self::create_child(protocol) else { |
| 520 | + continue; |
| 521 | + }; |
| 522 | + |
| 523 | + return Ok(Self { service_guid, handle, child_handle }); |
| 524 | + } |
| 525 | + } |
| 526 | + |
| 527 | + Err(io::const_error!(io::ErrorKind::NotFound, "no service binding protocol found")) |
| 528 | + } |
| 529 | + |
| 530 | + #[expect(dead_code)] |
| 531 | + pub(crate) fn child_handle(&self) -> NonNull<crate::ffi::c_void> { |
| 532 | + self.child_handle |
| 533 | + } |
| 534 | + |
| 535 | + fn create_child( |
| 536 | + sbp: NonNull<service_binding::Protocol>, |
| 537 | + ) -> io::Result<NonNull<crate::ffi::c_void>> { |
| 538 | + let mut child_handle: r_efi::efi::Handle = crate::ptr::null_mut(); |
| 539 | + // SAFETY: A new handle is allocated if a pointer to NULL is passed. |
| 540 | + let r = unsafe { ((*sbp.as_ptr()).create_child)(sbp.as_ptr(), &mut child_handle) }; |
| 541 | + |
| 542 | + if r.is_error() { |
| 543 | + Err(crate::io::Error::from_raw_os_error(r.as_usize())) |
| 544 | + } else { |
| 545 | + NonNull::new(child_handle) |
| 546 | + .ok_or(const_error!(io::ErrorKind::Other, "null child handle")) |
| 547 | + } |
| 548 | + } |
| 549 | +} |
| 550 | + |
| 551 | +impl Drop for ServiceProtocol { |
| 552 | + fn drop(&mut self) { |
| 553 | + if let Ok(sbp) = open_protocol::<service_binding::Protocol>(self.handle, self.service_guid) |
| 554 | + { |
| 555 | + // SAFETY: Child handle must be allocated by the current service binding protocol. |
| 556 | + let _ = unsafe { |
| 557 | + ((*sbp.as_ptr()).destroy_child)(sbp.as_ptr(), self.child_handle.as_ptr()) |
| 558 | + }; |
| 559 | + } |
| 560 | + } |
| 561 | +} |
0 commit comments