blob: d8961bedd2f0f9a4fc03c72048ca88a3bcbcfb19 [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::ffi::c_void;
use core::fmt::{Display, Write};
use core::ptr::null_mut;
use crate::defs::*;
use crate::{map_efi_err, DeviceHandle, EfiEntry, EfiError, EfiResult};
/// 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.
/// EFI_BLOCK_IO_PROTOCOL
pub struct BlockIoProtocol;
impl ProtocolInfo for BlockIoProtocol {
type InterfaceType = EfiBlockIoProtocol;
const GUID: EfiGuid =
EfiGuid::new(0x964e5b21, 0x6459, 0x11d2, [0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b]);
}
// Protocol interface wrappers.
impl Protocol<'_, BlockIoProtocol> {
/// Wrapper of `EFI_BLOCK_IO_PROTOCOL.read_blocks()`
pub fn read_blocks(&self, lba: u64, buffer: &mut [u8]) -> EfiResult<()> {
// SAFETY:
// `self.interface()?` guarantees self.interface is non-null and points to a valid object
// established by `Protocol::new()`.
// `self.interface` is input parameter and will not be retained. It outlives the call.
// `buffer` remains valid during the call.
unsafe {
efi_call!(
self.interface()?.read_blocks,
self.interface,
self.media()?.media_id,
lba,
buffer.len(),
buffer.as_mut_ptr() as *mut _
)
}
}
/// Wrapper of `EFI_BLOCK_IO_PROTOCOL.write_blocks()`
pub fn write_blocks(&self, lba: u64, buffer: &[u8]) -> EfiResult<()> {
// SAFETY:
// `self.interface()?` guarantees self.interface is non-null and points to a valid object
// established by `Protocol::new()`.
// `self.interface` is input parameter and will not be retained. It outlives the call.
// `buffer` remains valid during the call.
unsafe {
efi_call!(
self.interface()?.write_blocks,
self.interface,
self.media()?.media_id,
lba,
buffer.len(),
buffer.as_ptr() as *const _
)
}
}
/// Wrapper of `EFI_BLOCK_IO_PROTOCOL.flush_blocks()`
pub fn flush_blocks(&self) -> EfiResult<()> {
// SAFETY:
// `self.interface()?` guarantees `self.interface` is non-null and points to a valid object
// established by `Protocol::new()`.
// `self.interface` is input parameter and will not be retained. It outlives the call.
unsafe { efi_call!(self.interface()?.flush_blocks, self.interface) }
}
/// Wrapper of `EFI_BLOCK_IO_PROTOCOL.reset()`
pub fn reset(&self, extended_verification: bool) -> EfiResult<()> {
// SAFETY:
// `self.interface()?` guarantees `self.interface` is non-null and points to a valid object
// established by `Protocol::new()`.
// `self.interface` is input parameter and will not be retained. It outlives the call.
unsafe { efi_call!(self.interface()?.reset, self.interface, extended_verification) }
}
/// Get a reference to EFI_BLOCK_IO_PROTOCOL.Media structure.
pub fn media(&self) -> EfiResult<&EfiBlockIoMedia> {
let ptr = self.interface()?.media;
// SFETY: Pointers to EFI data structure.
unsafe { ptr.as_ref() }.ok_or_else(|| EFI_STATUS_INVALID_PARAMETER.into())
}
}
/// EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
pub struct SimpleTextOutputProtocol;
impl ProtocolInfo for SimpleTextOutputProtocol {
type InterfaceType = EfiSimpleTextOutputProtocol;
const GUID: EfiGuid =
EfiGuid::new(0x387477c2, 0x69c7, 0x11d2, [0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b]);
}
impl Protocol<'_, SimpleTextOutputProtocol> {
/// Wrapper of `EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString()`
pub fn output_string(&self, msg: *mut char16_t) -> EfiResult<()> {
// SAFETY:
// `self.interface()?` guarantees `self.interface` is non-null and points to a valid object
// established by `Protocol::new()`.
// `self.interface` is input parameter and will not be retained. It outlives the call.
unsafe { efi_call!(self.interface()?.output_string, self.interface, msg) }
}
}
/// Implement formatted write for `EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL`, so that we can print by
/// writing to it. i.e.:
///
/// ```
/// let protocol: Protocol<SimpleTextOutputProtocol> = ...;
/// write!(protocol, "Value = {}\n", 1234);
/// ```
impl Write for Protocol<'_, SimpleTextOutputProtocol> {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
for ch in s.chars() {
// 2 is enough for encode_utf16(). Add an additional one as NULL.
let mut buffer = [0u16; 3];
let char16_msg = ch.encode_utf16(&mut buffer[..]);
self.output_string(char16_msg.as_mut_ptr()).map_err(|_| core::fmt::Error {})?;
}
Ok(())
}
}
// A convenient convert to forward error when using write!() on
// Protocol<SimpleTextOutputProtocol>.
impl From<core::fmt::Error> for EfiError {
fn from(_: core::fmt::Error) -> EfiError {
EFI_STATUS_UNSUPPORTED.into()
}
}
/// EFI_SIMPLE_TEXT_INPUT_PROTOCOL
pub struct SimpleTextInputProtocol;
impl ProtocolInfo for SimpleTextInputProtocol {
type InterfaceType = EfiSimpleTextInputProtocol;
const GUID: EfiGuid =
EfiGuid::new(0x387477c1, 0x69c7, 0x11d2, [0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b]);
}
impl Protocol<'_, SimpleTextInputProtocol> {
/// Wrapper of `EFI_SIMPLE_TEXT_INPUT_PROTOCOL.reset()`
pub fn reset(&self, extendend_verification: bool) -> EfiResult<()> {
// SAFETY:
// `self.interface()?` guarantees `self.interface` is non-null and points to a valid object
// established by `Protocol::new()`.
// `self.interface` is input parameter and will not be retained. It outlives the call.
unsafe { efi_call!(self.interface()?.reset, self.interface, extendend_verification) }
}
/// Wrapper of `EFI_SIMPLE_TEXT_INPUT_PROTOCOL.read_key_stroke()`
///
/// Returns `Ok(Some(EfiInputKey))` if there is a key stroke, Ok(None) if no key stroke is
/// pressed.
pub fn read_key_stroke(&self) -> EfiResult<Option<EfiInputKey>> {
let mut key: EfiInputKey = Default::default();
// SAFETY:
// `self.interface()?` guarantees `self.interface` is non-null and points to a valid object
// established by `Protocol::new()`.
// `self.interface` is input parameter and will not be retained. It outlives the call.
// `key` is an output argument. It outlives the call and will not be taken.
match unsafe { efi_call!(self.interface()?.read_key_stroke, self.interface, &mut key) } {
Ok(()) => Ok(Some(key)),
Err(e) if e.is_efi_err(EFI_STATUS_NOT_READY) => Ok(None),
Err(e) => Err(e),
}
}
}
/// `EFI_DEVICE_PATH_PROTOCOL`
pub struct DevicePathProtocol;
impl ProtocolInfo for DevicePathProtocol {
type InterfaceType = EfiDevicePathProtocol;
const GUID: EfiGuid =
EfiGuid::new(0x09576e91, 0x6d3f, 0x11d2, [0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b]);
}
/// `EFI_DEVICE_PATH_TO_TEXT_PROTOCOL`
pub struct DevicePathToTextProtocol;
impl ProtocolInfo for DevicePathToTextProtocol {
type InterfaceType = EfiDevicePathToTextProtocol;
const GUID: EfiGuid =
EfiGuid::new(0x8b843e20, 0x8132, 0x4852, [0x90, 0xcc, 0x55, 0x1a, 0x4e, 0x4a, 0x7f, 0x1c]);
}
impl<'a> Protocol<'a, DevicePathToTextProtocol> {
/// Wrapper of `EFI_DEVICE_PATH_TO_TEXT_PROTOCOL.ConvertDevicePathToText()`
pub fn convert_device_path_to_text(
&self,
device_path: &Protocol<DevicePathProtocol>,
display_only: bool,
allow_shortcuts: bool,
) -> EfiResult<DevicePathText<'a>> {
let f = self
.interface()?
.convert_device_path_to_text
.as_ref()
.ok_or_else::<EfiError, _>(|| EFI_STATUS_NOT_FOUND.into())?;
// SAFETY:
// `self.interface()?` guarantees `self.interface` is non-null and points to a valid object
// established by `Protocol::new()`.
// `self.interface` is input parameter and will not be retained. It outlives the call.
let res = unsafe { f(device_path.interface_ptr(), display_only, allow_shortcuts) };
Ok(DevicePathText::new(res, self.efi_entry))
}
}
// `DevicePathText` is a wrapper for the return type of
// EFI_DEVICE_PATH_TO_TEXT_PROTOCOL.ConvertDevicePathToText().
pub struct DevicePathText<'a> {
text: Option<&'a [u16]>,
efi_entry: &'a EfiEntry,
}
impl<'a> DevicePathText<'a> {
pub(crate) fn new(text: *mut u16, efi_entry: &'a EfiEntry) -> Self {
if text.is_null() {
return Self { text: None, efi_entry: efi_entry };
}
let mut len: usize = 0;
// SAFETY: UEFI text is NULL terminated.
while unsafe { *text.add(len) } != 0 {
len += 1;
}
Self {
// SAFETY: Pointer is confirmed non-null with known length at this point.
text: Some(unsafe { core::slice::from_raw_parts(text, len) }),
efi_entry: efi_entry,
}
}
/// Get the text as a u16 slice.
pub fn text(&self) -> Option<&[u16]> {
self.text
}
}
impl Display for DevicePathText<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if let Some(text) = self.text {
for c in char::decode_utf16(text.into_iter().map(|v| *v)) {
match c.unwrap_or(char::REPLACEMENT_CHARACTER) {
'\0' => break,
ch => write!(f, "{}", ch)?,
};
}
}
Ok(())
}
}
impl Drop for DevicePathText<'_> {
fn drop(&mut self) {
if let Some(text) = self.text {
self.efi_entry
.system_table()
.boot_services()
.free_pool(text.as_ptr() as *mut _)
.unwrap();
}
}
}
/// EFI_LOADED_IMAGE_PROTOCOL
pub struct LoadedImageProtocol;
impl ProtocolInfo for LoadedImageProtocol {
type InterfaceType = EfiLoadedImageProtocol;
const GUID: EfiGuid =
EfiGuid::new(0x5b1b31a1, 0x9562, 0x11d2, [0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b]);
}
impl<'a> Protocol<'a, LoadedImageProtocol> {
pub fn device_handle(&self) -> EfiResult<DeviceHandle> {
Ok(DeviceHandle(self.interface()?.device_handle))
}
}
/// RISCV_EFI_BOOT_PROTOCOL
pub struct RiscvBootProtocol;
impl ProtocolInfo for RiscvBootProtocol {
type InterfaceType = EfiRiscvBootProtocol;
const GUID: EfiGuid =
EfiGuid::new(0xccd15fec, 0x6f73, 0x4eec, [0x83, 0x95, 0x3e, 0x69, 0xe4, 0xb9, 0x40, 0xbf]);
}
impl<'a> Protocol<'a, RiscvBootProtocol> {
pub fn get_boot_hartid(&self) -> EfiResult<usize> {
let mut boot_hart_id: usize = 0;
// SAFETY:
// `self.interface()?` guarantees `self.interface` is non-null and points to a valid object
// established by `Protocol::new()`.
// `self.interface` is input parameter and will not be retained. It outlives the call.
// `&mut boot_hart_id` is output parameter and will not be retained. It outlives the call.
unsafe {
efi_call!(self.interface()?.get_boot_hartid, self.interface, &mut boot_hart_id)?;
}
Ok(boot_hart_id)
}
pub fn revision(&self) -> EfiResult<u64> {
Ok(self.interface()?.revision)
}
}
/// EFI_SIMPLE_NETWORK_PROTOCOL
pub struct SimpleNetworkProtocol;
impl ProtocolInfo for SimpleNetworkProtocol {
type InterfaceType = EfiSimpleNetworkProtocol;
const GUID: EfiGuid =
EfiGuid::new(0xa19832b9, 0xac25, 0x11d3, [0x9a, 0x2d, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d]);
}
impl<'a> Protocol<'a, SimpleNetworkProtocol> {
/// Wrapper of `EFI_SIMPLE_NETWORK.Start()`
pub fn start(&self) -> EfiResult<()> {
// SAFETY:
// `self.interface()?` guarantees to return a valid object pointer as established by
// `Protocol::new()`.
// `self.interface` outlives the call and will not be retained.
unsafe { efi_call!(self.interface()?.start, self.interface) }
}
/// Wrapper of `EFI_SIMPLE_NETWORK.Stop()`
pub fn stop(&self) -> EfiResult<()> {
// SAFETY: See safety reasoning of `start()`.
unsafe { efi_call!(self.interface()?.stop, self.interface) }
}
/// Wrapper of `EFI_SIMPLE_NETWORK.Initialize()`
pub fn initialize(&self, extra_rx_buf_size: usize, extra_tx_buf_size: usize) -> EfiResult<()> {
// SAFETY: See safety reasoning of `start()`.
unsafe {
efi_call!(
self.interface()?.initialize,
self.interface,
extra_rx_buf_size,
extra_tx_buf_size
)
}
}
/// Wrapper of `EFI_SIMPLE_NETWORK.Reset()`
pub fn reset(&self, extended_verification: bool) -> EfiResult<()> {
// SAFETY: See safety reasoning of `start()`.
unsafe { efi_call!(self.interface()?.reset, self.interface, extended_verification) }
}
/// Wrapper of `EFI_SIMPLE_NETWORK.Shutdown()`
pub fn shutdown(&self) -> EfiResult<()> {
// SAFETY: See safety reasoning of `start()`.
unsafe { efi_call!(self.interface()?.shutdown, self.interface) }
}
/// Wrapper of `EFI_SIMPLE_NETWORK.GetStatus()`
pub fn get_status(
&self,
interrupt_status: Option<&mut u32>,
recycle_buffer: Option<&mut *mut c_void>,
) -> EfiResult<()> {
// SAFETY:
// See safety reasoning of `start()`.
// Pointers to `interrupt_status`, `recycled_buffer` are valid during the call and for
// writing output values only.
unsafe {
efi_call!(
self.interface()?.get_status,
self.interface,
option_ref_mut_to_pointer(interrupt_status),
option_ref_mut_to_pointer(recycle_buffer)
)?;
}
Ok(())
}
/// Wrapper of `EFI_SIMPLE_NETWORK.Transmit()`
///
/// # Safety
///
/// Caller must ensure that `buf` remains valid until either 1) the buffer address re-appears
/// in `recycled_buffer` from `Self::get_status()`, or 2) Self::Shutdown() is called and
/// returns either Ok(()) or EFI_STATUS_NOT_STARTED. Otherwise, the driver may still have
/// modifiable access to the buffer and causes undefined behavior if the buffer goes out of
/// scope earlier.
pub unsafe fn transmit(
&self,
header_size: usize,
buf: &mut [u8],
mut src: EfiMacAddress,
mut dest: EfiMacAddress,
mut protocol: u16,
) -> EfiResult<()> {
// SAFETY:
// See safety reasoning of `start()`.
// All pointers passed are valid, outlive the call and are not retained by the call.
unsafe {
efi_call!(
self.interface()?.transmit,
self.interface,
header_size,
buf.len(),
buf.as_mut_ptr() as *mut _,
&mut src,
&mut dest,
&mut protocol
)
}
}
/// Wrapper of `EFI_SIMPLE_NETWORK.Receive()`.
pub fn receive(
&self,
header_size: Option<&mut usize>,
buf_size: Option<&mut usize>,
buf: &mut [u8],
src: Option<&mut EfiMacAddress>,
dest: Option<&mut EfiMacAddress>,
protocol: Option<&mut u16>,
) -> EfiResult<()> {
// SAFETY:
// See safety reasoning of `start()`.
// All pointers passed are valid, outlive the call and are not retained by the call.
unsafe {
efi_call!(
self.interface()?.receive,
self.interface,
option_ref_mut_to_pointer(header_size),
option_ref_mut_to_pointer(buf_size),
buf.as_mut_ptr() as *mut _,
option_ref_mut_to_pointer(src),
option_ref_mut_to_pointer(dest),
option_ref_mut_to_pointer(protocol)
)?;
}
Ok(())
}
/// Returns `EFI_SIMPLE_NETWORK.Mode` structure
pub fn mode(&self) -> EfiResult<EfiSimpleNetworkMode> {
// SAFETY: Non-null pointer from UEFI interface points to valid object.
unsafe { self.interface()?.mode.as_ref() }.ok_or(EFI_STATUS_NOT_FOUND.into()).copied()
}
}
/// A helper to convert an `Option<&mut T>` to `*mut T`. None maps to NULL.
fn option_ref_mut_to_pointer<T>(option: Option<&mut T>) -> *mut T {
option.map(|t| t as *mut _).unwrap_or(null_mut())
}
#[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::<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);
});
})
}
#[test]
fn test_device_path_text_drop() {
run_test(|image_handle, systab_ptr| {
let efi_entry = EfiEntry { image_handle, systab_ptr };
let mut data: [u16; 4] = [1, 2, 3, 0];
{
let path = DevicePathText::new(data.as_mut_ptr(), &efi_entry);
assert_eq!(path.text.unwrap().to_vec(), vec![1, 2, 3]);
}
efi_call_traces().with(|traces| {
assert_eq!(
traces.borrow_mut().free_pool_trace.inputs,
[data.as_mut_ptr() as *mut _]
);
});
})
}
#[test]
fn test_device_path_text_null() {
run_test(|image_handle, systab_ptr| {
let efi_entry = EfiEntry { image_handle, systab_ptr };
{
assert_eq!(DevicePathText::new(null_mut(), &efi_entry).text(), None);
}
efi_call_traces().with(|traces| {
assert_eq!(traces.borrow_mut().free_pool_trace.inputs.len(), 0);
});
})
}
}