diff options
author | Yecheng Zhao <zyecheng@google.com> | 2024-04-08 17:33:11 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2024-04-08 17:33:11 +0000 |
commit | f504debaf0e77297684ed73819159763145bdd38 (patch) | |
tree | 1530f5bd752dbe8b49bab559e687ef3daa2d9712 | |
parent | 79b50a4bc843707be0169ae4aa4b4be208d3a58f (diff) | |
parent | a3c9a6a26a9d9a09966b4af7f898ec866c1d7ff7 (diff) | |
download | libbootloader-f504debaf0e77297684ed73819159763145bdd38.tar.gz |
Merge "Support EFI_ANDROID_BOOT_PROTOCOL" into main
-rw-r--r-- | gbl/efi/src/android_boot.rs | 2 | ||||
-rw-r--r-- | gbl/efi/src/net.rs | 41 | ||||
-rw-r--r-- | gbl/efi/src/utils.rs | 12 | ||||
-rw-r--r-- | gbl/libefi/BUILD | 9 | ||||
-rw-r--r-- | gbl/libefi/defs/efi.h | 1 | ||||
-rw-r--r-- | gbl/libefi/defs/protocols/android_boot_protocol.h | 38 | ||||
-rw-r--r-- | gbl/libefi/src/lib.rs | 17 | ||||
-rw-r--r-- | gbl/libefi/src/protocol.rs | 503 | ||||
-rw-r--r-- | gbl/libefi/src/protocol/android_boot.rs | 93 | ||||
-rw-r--r-- | gbl/libefi/src/protocol/block_io.rs | 96 | ||||
-rw-r--r-- | gbl/libefi/src/protocol/device_path.rs | 157 | ||||
-rw-r--r-- | gbl/libefi/src/protocol/loaded_image.rs | 33 | ||||
-rw-r--r-- | gbl/libefi/src/protocol/riscv.rs | 46 | ||||
-rw-r--r-- | gbl/libefi/src/protocol/simple_network.rs | 169 | ||||
-rw-r--r-- | gbl/libefi/src/protocol/simple_text_input.rs | 58 | ||||
-rw-r--r-- | gbl/libefi/src/protocol/simple_text_output.rs | 68 |
16 files changed, 817 insertions, 526 deletions
diff --git a/gbl/efi/src/android_boot.rs b/gbl/efi/src/android_boot.rs index 85a25b8..ad766f8 100644 --- a/gbl/efi/src/android_boot.rs +++ b/gbl/efi/src/android_boot.rs @@ -413,7 +413,7 @@ pub fn android_boot_demo(entry: EfiEntry) -> Result<()> { let boot_hart_id = entry .system_table() .boot_services() - .find_first_and_open::<efi::RiscvBootProtocol>()? + .find_first_and_open::<efi::protocol::riscv::RiscvBootProtocol>()? .get_boot_hartid()?; efi_println!(entry, "riscv boot_hart_id: {}", boot_hart_id); let _ = exit_boot_services(entry, remains)?; diff --git a/gbl/efi/src/net.rs b/gbl/efi/src/net.rs index 64d69b6..b426882 100644 --- a/gbl/efi/src/net.rs +++ b/gbl/efi/src/net.rs @@ -12,27 +12,32 @@ // See the License for the specific language governing permissions and // limitations under the License. -use alloc::boxed::Box; -use alloc::vec::Vec; -use core::fmt::Write; -use core::sync::atomic::{AtomicU64, Ordering}; -use efi::defs::{ - EfiEvent, EfiMacAddress, EFI_STATUS_ALREADY_STARTED, EFI_STATUS_NOT_STARTED, - EFI_TIMER_DELAY_TIMER_PERIODIC, +use crate::{ + error::{EfiAppError, Result}, + utils::{get_device_path, loop_with_timeout, ms_to_100ns}, +}; +use alloc::{boxed::Box, vec::Vec}; +use core::{ + fmt::Write, + sync::atomic::{AtomicU64, Ordering}, }; use efi::{ - efi_print, efi_println, DeviceHandle, EfiEntry, EventNotify, EventType, Protocol, - SimpleNetworkProtocol, Tpl, + defs::{ + EfiEvent, EfiMacAddress, EFI_STATUS_ALREADY_STARTED, EFI_STATUS_NOT_STARTED, + EFI_TIMER_DELAY_TIMER_PERIODIC, + }, + efi_print, efi_println, + protocol::{simple_network::SimpleNetworkProtocol, Protocol}, + DeviceHandle, EfiEntry, EventNotify, EventType, Tpl, +}; +use smoltcp::{ + iface::{Config, Interface, SocketSet}, + phy, + phy::{Device, DeviceCapabilities, Medium}, + socket::tcp, + time::Instant, + wire::{EthernetAddress, IpAddress, IpCidr, Ipv6Address}, }; -//use fastboot::{Fastboot, FastbootImplementation, FormattedBytes, TcpStream, TransportError}; -use crate::error::{EfiAppError, Result}; -use crate::utils::{get_device_path, loop_with_timeout, ms_to_100ns}; -use smoltcp::iface::{Config, Interface, SocketSet}; -use smoltcp::phy; -use smoltcp::phy::{Device, DeviceCapabilities, Medium}; -use smoltcp::socket::tcp; -use smoltcp::time::Instant; -use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv6Address}; /// Maintains a timestamp needed by smoltcp network. It's updated periodically during timer event. static NETWORK_TIMESTAMP: AtomicU64 = AtomicU64::new(0); diff --git a/gbl/efi/src/utils.rs b/gbl/efi/src/utils.rs index 186c705..e98aec4 100644 --- a/gbl/efi/src/utils.rs +++ b/gbl/efi/src/utils.rs @@ -16,10 +16,16 @@ use alloc::vec::Vec; use core::ffi::CStr; use crate::error::{EfiAppError, Result}; -use efi::defs::{EfiGuid, EFI_TIMER_DELAY_TIMER_RELATIVE}; use efi::{ - BlockIoProtocol, DeviceHandle, DevicePathProtocol, DevicePathText, DevicePathToTextProtocol, - EfiEntry, EventType, LoadedImageProtocol, Protocol, SimpleTextInputProtocol, + defs::{EfiGuid, EFI_TIMER_DELAY_TIMER_RELATIVE}, + protocol::{ + block_io::BlockIoProtocol, + device_path::{DevicePathProtocol, DevicePathText, DevicePathToTextProtocol}, + loaded_image::LoadedImageProtocol, + simple_text_input::SimpleTextInputProtocol, + Protocol, + }, + DeviceHandle, EfiEntry, EventType, }; use fdt::FdtHeader; use gbl_storage::{required_scratch_size, AsBlockDevice, AsMultiBlockDevices, BlockIo}; diff --git a/gbl/libefi/BUILD b/gbl/libefi/BUILD index d24b519..5638114 100644 --- a/gbl/libefi/BUILD +++ b/gbl/libefi/BUILD @@ -35,6 +35,7 @@ cc_library( hdrs = [ "defs/boot_service.h", "defs/efi.h", + "defs/protocols/android_boot_protocol.h", "defs/protocols/block_io_protocol.h", "defs/protocols/device_path_protocol.h", "defs/protocols/loaded_image_protocol.h", @@ -100,6 +101,14 @@ rust_library( "src/defs.rs", # Generated by :efi_defs_genrule "src/lib.rs", "src/protocol.rs", + "src/protocol/android_boot.rs", + "src/protocol/block_io.rs", + "src/protocol/device_path.rs", + "src/protocol/loaded_image.rs", + "src/protocol/riscv.rs", + "src/protocol/simple_network.rs", + "src/protocol/simple_text_input.rs", + "src/protocol/simple_text_output.rs", ], crate_name = "efi", data = [":efi_defs_genrule"], diff --git a/gbl/libefi/defs/efi.h b/gbl/libefi/defs/efi.h index 94ec7d9..2bbe5e3 100644 --- a/gbl/libefi/defs/efi.h +++ b/gbl/libefi/defs/efi.h @@ -23,6 +23,7 @@ #include <stdint.h> #include "boot_service.h" +#include "protocols/android_boot_protocol.h" #include "protocols/block_io_protocol.h" #include "protocols/device_path_protocol.h" #include "protocols/loaded_image_protocol.h" diff --git a/gbl/libefi/defs/protocols/android_boot_protocol.h b/gbl/libefi/defs/protocols/android_boot_protocol.h new file mode 100644 index 0000000..3d4c115 --- /dev/null +++ b/gbl/libefi/defs/protocols/android_boot_protocol.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2024 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. + * + */ + +// This is a custom protocol introduced by GBL. +// See gbl/docs/EFI_ANDROID_BOOT_PROTOCOL.md for details. + +#include "types.h" + +#ifndef __ANDROID_BOOT_PROTOCOL_H__ +#define __ANDROID_BOOT_PROTOCOL_H__ + +typedef struct EfiAndroidBootProtocol { + uint64_t revision; + EfiStatus (*fastboot_usb_interface_start)(struct EfiAndroidBootProtocol* self, + size_t* max_packet_size); + EfiStatus (*fastboot_usb_interface_stop)(struct EfiAndroidBootProtocol* self); + EfiStatus (*fastboot_usb_receive)(struct EfiAndroidBootProtocol* self, + size_t* buffer_size, void* buffer); + EfiStatus (*fastboot_usb_send)(struct EfiAndroidBootProtocol* self, + size_t* buffer_size, void* buffer); + EfiEvent wait_for_send_completion; +} EfiAndroidBootProtocol; + +#endif //__ANDROID_BOOT_PROTOCOL_H__ diff --git a/gbl/libefi/src/lib.rs b/gbl/libefi/src/lib.rs index 6519896..c7889d2 100644 --- a/gbl/libefi/src/lib.rs +++ b/gbl/libefi/src/lib.rs @@ -67,19 +67,9 @@ mod allocation; #[cfg(not(test))] pub use allocation::{efi_free, efi_malloc}; -mod protocol; -// Protocol type and implementation to export. -pub use protocol::BlockIoProtocol; -pub use protocol::DevicePathProtocol; -pub use protocol::DevicePathText; -pub use protocol::DevicePathToTextProtocol; -pub use protocol::LoadedImageProtocol; -pub use protocol::Protocol; -pub use protocol::ProtocolInfo; -pub use protocol::RiscvBootProtocol; -pub use protocol::SimpleNetworkProtocol; -pub use protocol::SimpleTextInputProtocol; -pub use protocol::SimpleTextOutputProtocol; +pub mod protocol; +use protocol::simple_text_output::SimpleTextOutputProtocol; +use protocol::{Protocol, ProtocolInfo}; mod error { use super::defs::EFI_STATUS_SUCCESS; @@ -688,6 +678,7 @@ fn panic(panic: &PanicInfo) -> ! { #[cfg(test)] mod test { use super::*; + use crate::protocol::block_io::BlockIoProtocol; use std::cell::RefCell; use std::collections::VecDeque; use std::mem::size_of; diff --git a/gbl/libefi/src/protocol.rs b/gbl/libefi/src/protocol.rs index ef7c31a..a6092b5 100644 --- a/gbl/libefi/src/protocol.rs +++ b/gbl/libefi/src/protocol.rs @@ -12,12 +12,19 @@ // 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}; +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 { @@ -89,7 +96,7 @@ impl EfiGuid { #[macro_export] macro_rules! efi_call { - ( $method:expr, $($x:expr),* ) => { + ( $method:expr, $($x:expr),*$(,)? ) => { { let res: EfiResult<()> = match $method { None => Err(EFI_STATUS_NOT_FOUND.into()), @@ -103,461 +110,6 @@ macro_rules! efi_call { // 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 - /// - /// * `buf` needs to be a valid buffer. - /// * There should not be any existing references to memory pointed by `buf`. - /// * Because `buf` is internally retained by the network. `buf` should remain valid and not - /// dereferenced 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. - pub unsafe fn transmit( - &self, - header_size: usize, - buf: *mut [u8], - mut src: EfiMacAddress, - mut dest: EfiMacAddress, - mut protocol: u16, - ) -> EfiResult<()> { - let buf = buf.as_mut().unwrap(); - // 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::*; @@ -570,7 +122,7 @@ mod test { let mut block_io: EfiBlockIoProtocol = Default::default(); // SAFETY: `block_io` is a EfiBlockIoProtocol and out lives the created Protocol. unsafe { - Protocol::<BlockIoProtocol>::new( + Protocol::<block_io::BlockIoProtocol>::new( DeviceHandle(null_mut()), &mut block_io as *mut _, &efi_entry, @@ -581,35 +133,4 @@ mod test { }); }) } - - #[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); - }); - }) - } } diff --git a/gbl/libefi/src/protocol/android_boot.rs b/gbl/libefi/src/protocol/android_boot.rs new file mode 100644 index 0000000..f7d9ec8 --- /dev/null +++ b/gbl/libefi/src/protocol/android_boot.rs @@ -0,0 +1,93 @@ +// Copyright 2024, 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 crate::defs::{EfiAndroidBootProtocol, EfiGuid, EFI_STATUS_NOT_FOUND}; +use crate::protocol::{Protocol, ProtocolInfo}; +use crate::{efi_call, map_efi_err, EfiResult}; + +/// EFI_ANDROID_BOOT_PROTOCOL +pub struct AndroidBootProtocol; + +impl ProtocolInfo for AndroidBootProtocol { + type InterfaceType = EfiAndroidBootProtocol; + + const GUID: EfiGuid = + EfiGuid::new(0x6281a893, 0xac23, 0x4ca7, [0xb2, 0x81, 0x34, 0x0e, 0xf8, 0x16, 0x89, 0x55]); +} + +// Protocol interface wrappers. +impl Protocol<'_, AndroidBootProtocol> { + /// Wrapper of `EFI_ANDROID_BOOT_PROTOCOL.fastboot_usb_interface_start()` + pub fn fastboot_usb_interface_start(&self) -> EfiResult<usize> { + let mut max_packet_size = 0; + // SAFETY: + // `self.interface()?` guarantees self.interface is non-null and points to a valid object + // established by `Protocol::new()`. + // `self.interface` and `max_packet_size` are input/output parameters, outlive the call and + // will not be retained. + unsafe { + efi_call!( + self.interface()?.fastboot_usb_interface_start, + self.interface, + &mut max_packet_size, + )?; + } + Ok(max_packet_size) + } + + /// Wrapper of `EFI_ANDROID_BOOT_PROTOCOL.fastboot_usb_interface_stop()` + pub fn fastboot_usb_interface_stop(&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, outlives the call, and will not be retained. + unsafe { efi_call!(self.interface()?.fastboot_usb_interface_stop, self.interface,) } + } + + /// Wrapper of `EFI_ANDROID_BOOT_PROTOCOL.fastboot_usb_receive()` + pub fn fastboot_usb_receive(&self, out: &mut [u8], out_size: &mut usize) -> EfiResult<()> { + *out_size = out.len(); + // SAFETY: + // `self.interface()?` guarantees self.interface is non-null and points to a valid object + // established by `Protocol::new()`. + // `self.interface`, `out_size` and `buffer` are input/output parameters, outlive the call + // and will not be retained. + unsafe { + efi_call!( + self.interface()?.fastboot_usb_receive, + self.interface, + out_size, + out.as_mut_ptr() as _, + ) + } + } + + /// Wrapper of `EFI_ANDROID_BOOT_PROTOCOL.fastboot_usb_send()` + pub fn fastboot_usb_send(&self, data: &mut [u8], out_size: &mut usize) -> EfiResult<()> { + *out_size = data.len(); + // SAFETY: + // `self.interface()?` guarantees self.interface is non-null and points to a valid object + // established by `Protocol::new()`. + // `self.interface`, `out_size` and `buffer` are input/output parameters, outlive the call + // and will not be retained. + unsafe { + efi_call!( + self.interface()?.fastboot_usb_send, + self.interface, + out_size, + data.as_mut_ptr() as _, + ) + } + } +} diff --git a/gbl/libefi/src/protocol/block_io.rs b/gbl/libefi/src/protocol/block_io.rs new file mode 100644 index 0000000..2aea44f --- /dev/null +++ b/gbl/libefi/src/protocol/block_io.rs @@ -0,0 +1,96 @@ +// Copyright 2024, 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 crate::defs::{ + EfiBlockIoMedia, EfiBlockIoProtocol, EfiGuid, EFI_STATUS_INVALID_PARAMETER, + EFI_STATUS_NOT_FOUND, +}; +use crate::protocol::{Protocol, ProtocolInfo}; +use crate::{efi_call, map_efi_err, EfiResult}; + +/// 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 copy to the EFI_BLOCK_IO_PROTOCOL.Media structure. + pub fn media(&self) -> EfiResult<EfiBlockIoMedia> { + let ptr = self.interface()?.media; + // SFETY: Pointers to EFI data structure. + Ok(*unsafe { ptr.as_ref() }.ok_or_else(|| EFI_STATUS_INVALID_PARAMETER)?) + } +} diff --git a/gbl/libefi/src/protocol/device_path.rs b/gbl/libefi/src/protocol/device_path.rs new file mode 100644 index 0000000..2e18247 --- /dev/null +++ b/gbl/libefi/src/protocol/device_path.rs @@ -0,0 +1,157 @@ +// Copyright 2024, 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 crate::defs::{ + EfiDevicePathProtocol, EfiDevicePathToTextProtocol, EfiGuid, EFI_STATUS_NOT_FOUND, +}; +use crate::protocol::{Protocol, ProtocolInfo}; +use crate::{EfiEntry, EfiError, EfiResult}; +use core::fmt::Display; + +/// `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(); + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::test::*; + use core::ptr::null_mut; + + #[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); + }); + }) + } +} diff --git a/gbl/libefi/src/protocol/loaded_image.rs b/gbl/libefi/src/protocol/loaded_image.rs new file mode 100644 index 0000000..bc7cfad --- /dev/null +++ b/gbl/libefi/src/protocol/loaded_image.rs @@ -0,0 +1,33 @@ +// Copyright 2024, 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 crate::defs::{EfiGuid, EfiLoadedImageProtocol}; +use crate::protocol::{Protocol, ProtocolInfo}; +use crate::{DeviceHandle, EfiResult}; + +/// 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)) + } +} diff --git a/gbl/libefi/src/protocol/riscv.rs b/gbl/libefi/src/protocol/riscv.rs new file mode 100644 index 0000000..d3ab138 --- /dev/null +++ b/gbl/libefi/src/protocol/riscv.rs @@ -0,0 +1,46 @@ +// Copyright 2024, 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 crate::defs::{EfiGuid, EfiRiscvBootProtocol, EFI_STATUS_NOT_FOUND}; +use crate::protocol::{Protocol, ProtocolInfo}; +use crate::{efi_call, map_efi_err, EfiResult}; + +/// 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) + } +} diff --git a/gbl/libefi/src/protocol/simple_network.rs b/gbl/libefi/src/protocol/simple_network.rs new file mode 100644 index 0000000..1a621e0 --- /dev/null +++ b/gbl/libefi/src/protocol/simple_network.rs @@ -0,0 +1,169 @@ +// Copyright 2024, 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 crate::defs::{ + EfiGuid, EfiMacAddress, EfiSimpleNetworkMode, EfiSimpleNetworkProtocol, EFI_STATUS_NOT_FOUND, +}; +use crate::protocol::{Protocol, ProtocolInfo}; +use crate::{efi_call, map_efi_err, EfiResult}; +use core::ffi::c_void; +use core::ptr::null_mut; + +/// 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 + /// + /// * `buf` needs to be a valid buffer. + /// * There should not be any existing references to memory pointed by `buf`. + /// * Because `buf` is internally retained by the network. `buf` should remain valid and not + /// dereferenced 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. + pub unsafe fn transmit( + &self, + header_size: usize, + buf: *mut [u8], + mut src: EfiMacAddress, + mut dest: EfiMacAddress, + mut protocol: u16, + ) -> EfiResult<()> { + let buf = buf.as_mut().unwrap(); + // 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()) +} diff --git a/gbl/libefi/src/protocol/simple_text_input.rs b/gbl/libefi/src/protocol/simple_text_input.rs new file mode 100644 index 0000000..61f4be2 --- /dev/null +++ b/gbl/libefi/src/protocol/simple_text_input.rs @@ -0,0 +1,58 @@ +// Copyright 2024, 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 crate::defs::{ + EfiGuid, EfiInputKey, EfiSimpleTextInputProtocol, EFI_STATUS_NOT_FOUND, EFI_STATUS_NOT_READY, +}; +use crate::protocol::{Protocol, ProtocolInfo}; +use crate::{efi_call, map_efi_err, EfiResult}; + +/// 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), + } + } +} diff --git a/gbl/libefi/src/protocol/simple_text_output.rs b/gbl/libefi/src/protocol/simple_text_output.rs new file mode 100644 index 0000000..ac27601 --- /dev/null +++ b/gbl/libefi/src/protocol/simple_text_output.rs @@ -0,0 +1,68 @@ +// Copyright 2024, 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 crate::defs::{ + char16_t, EfiGuid, EfiSimpleTextOutputProtocol, EFI_STATUS_NOT_FOUND, EFI_STATUS_UNSUPPORTED, +}; +use crate::protocol::{Protocol, ProtocolInfo}; +use crate::{efi_call, map_efi_err, EfiError, EfiResult}; +use core::fmt::Write; + +/// 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() + } +} |