blob: a6092b512e7119a448f28d926d2e2c7e28ac7f9a [file] [log] [blame]
// Copyright 2023, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use core::ptr::null_mut;
use crate::defs::*;
use crate::{DeviceHandle, EfiEntry, EfiResult};
pub mod android_boot;
pub mod block_io;
pub mod device_path;
pub mod loaded_image;
pub mod riscv;
pub mod simple_network;
pub mod simple_text_input;
pub mod simple_text_output;
/// ProtocolInfo provides GUID info and the EFI data structure type for a protocol.
pub trait ProtocolInfo {
/// Data structure type of the interface.
type InterfaceType;
/// GUID of the protocol.
const GUID: EfiGuid;
}
/// A generic type for representing an EFI protcol.
pub struct Protocol<'a, T: ProtocolInfo> {
// The handle to the device offering the protocol. It's needed for closing the protocol.
device: DeviceHandle,
// The interface protocol itself.
interface: *mut T::InterfaceType,
// The `EfiEntry` data
efi_entry: &'a EfiEntry,
}
/// A base implementation for Protocol<T>.
/// Protocol<T> will have additional implementation based on type `T`.
impl<'a, T: ProtocolInfo> Protocol<'a, T> {
/// Create a new instance with the given device handle, interface pointer and `EfiEntry` data.
///
/// # Safety
///
/// Caller needs to ensure that
///
/// * `interface` points to a valid object of type T::InterfaceType.
///
/// * Object pointed to by `interface` must live as long as the create `Protocol` or 'a.
pub(crate) unsafe fn new(
device: DeviceHandle,
interface: *mut T::InterfaceType,
efi_entry: &'a EfiEntry,
) -> Self {
Self { device, interface, efi_entry }
}
/// Returns the EFI data structure for the protocol interface.
pub fn interface(&self) -> EfiResult<&T::InterfaceType> {
// SAFETY: EFI protocol interface data structure.
unsafe { self.interface.as_ref() }.ok_or_else(|| EFI_STATUS_INVALID_PARAMETER.into())
}
/// Returns the mutable pointer of the interface. Invisible from outside. Application should
/// not have any need to alter the content of interface data.
pub(crate) fn interface_ptr(&self) -> *mut T::InterfaceType {
self.interface
}
}
impl<T: ProtocolInfo> Drop for Protocol<'_, T> {
fn drop(&mut self) {
// If the device handle is not specified when creating the Protocol<T>, treat the
// handle as a static permanent reference and don't close it. An example is
// `EFI_SYSTEM_TABLE.ConOut`.
if self.device.0 != null_mut() {
self.efi_entry.system_table().boot_services().close_protocol::<T>(self.device).unwrap();
}
}
}
impl EfiGuid {
pub const fn new(data1: u32, data2: u16, data3: u16, data4: [u8; 8usize]) -> Self {
EfiGuid { data1, data2, data3, data4 }
}
}
#[macro_export]
macro_rules! efi_call {
( $method:expr, $($x:expr),*$(,)? ) => {
{
let res: EfiResult<()> = match $method {
None => Err(EFI_STATUS_NOT_FOUND.into()),
Some(f) => map_efi_err(f($($x,)*))
};
res
}
};
}
// Following are protocol specific implementations for Protocol<T>.
// TODO(300168989): Consdier splitting each protocol into separate file as we add more protocols.
#[cfg(test)]
mod test {
use super::*;
use crate::test::*;
#[test]
fn test_dont_close_protocol_without_device_handle() {
run_test(|image_handle, systab_ptr| {
let efi_entry = EfiEntry { image_handle, systab_ptr };
let mut block_io: EfiBlockIoProtocol = Default::default();
// SAFETY: `block_io` is a EfiBlockIoProtocol and out lives the created Protocol.
unsafe {
Protocol::<block_io::BlockIoProtocol>::new(
DeviceHandle(null_mut()),
&mut block_io as *mut _,
&efi_entry,
);
}
efi_call_traces().with(|traces| {
assert_eq!(traces.borrow_mut().close_protocol_trace.inputs.len(), 0);
});
})
}
}