summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYecheng Zhao <zyecheng@google.com>2024-04-08 17:33:11 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2024-04-08 17:33:11 +0000
commitf504debaf0e77297684ed73819159763145bdd38 (patch)
tree1530f5bd752dbe8b49bab559e687ef3daa2d9712
parent79b50a4bc843707be0169ae4aa4b4be208d3a58f (diff)
parenta3c9a6a26a9d9a09966b4af7f898ec866c1d7ff7 (diff)
downloadlibbootloader-f504debaf0e77297684ed73819159763145bdd38.tar.gz
Merge "Support EFI_ANDROID_BOOT_PROTOCOL" into main
-rw-r--r--gbl/efi/src/android_boot.rs2
-rw-r--r--gbl/efi/src/net.rs41
-rw-r--r--gbl/efi/src/utils.rs12
-rw-r--r--gbl/libefi/BUILD9
-rw-r--r--gbl/libefi/defs/efi.h1
-rw-r--r--gbl/libefi/defs/protocols/android_boot_protocol.h38
-rw-r--r--gbl/libefi/src/lib.rs17
-rw-r--r--gbl/libefi/src/protocol.rs503
-rw-r--r--gbl/libefi/src/protocol/android_boot.rs93
-rw-r--r--gbl/libefi/src/protocol/block_io.rs96
-rw-r--r--gbl/libefi/src/protocol/device_path.rs157
-rw-r--r--gbl/libefi/src/protocol/loaded_image.rs33
-rw-r--r--gbl/libefi/src/protocol/riscv.rs46
-rw-r--r--gbl/libefi/src/protocol/simple_network.rs169
-rw-r--r--gbl/libefi/src/protocol/simple_text_input.rs58
-rw-r--r--gbl/libefi/src/protocol/simple_text_output.rs68
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()
+ }
+}