pub use uefi_raw::table::boot::{
EventType, MemoryAttribute, MemoryDescriptor, MemoryType, PAGE_SIZE, Tpl,
};
use crate::data_types::PhysicalAddress;
use crate::mem::memory_map::{MemoryMapBackingMemory, MemoryMapKey, MemoryMapMeta, MemoryMapOwned};
use crate::polyfill::maybe_uninit_slice_assume_init_ref;
#[cfg(doc)]
use crate::proto::device_path::LoadedImageDevicePath;
use crate::proto::device_path::{DevicePath, FfiDevicePath};
use crate::proto::loaded_image::LoadedImage;
use crate::proto::media::fs::SimpleFileSystem;
use crate::proto::{BootPolicy, Protocol, ProtocolPointer};
use crate::runtime::{self, ResetType};
use crate::table::Revision;
use crate::util::opt_nonnull_to_ptr;
use crate::{Char16, Error, Event, Guid, Handle, Result, Status, StatusExt, table};
use core::ffi::c_void;
use core::fmt::{Display, Formatter};
use core::mem::MaybeUninit;
use core::ops::{Deref, DerefMut};
use core::ptr::{self, NonNull};
use core::sync::atomic::{AtomicPtr, Ordering};
use core::time::Duration;
use core::{mem, slice};
use uefi_raw::table::boot::{AllocateType as RawAllocateType, InterfaceType, TimerDelay};
#[cfg(feature = "alloc")]
use {alloc::vec::Vec, uefi::ResultExt};
static IMAGE_HANDLE: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut());
#[must_use]
pub fn image_handle() -> Handle {
let ptr = IMAGE_HANDLE.load(Ordering::Acquire);
unsafe { Handle::from_ptr(ptr) }.expect("set_image_handle has not been called")
}
pub unsafe fn set_image_handle(image_handle: Handle) {
IMAGE_HANDLE.store(image_handle.as_ptr(), Ordering::Release);
}
pub(crate) fn are_boot_services_active() -> bool {
let Some(st) = table::system_table_raw() else {
return false;
};
let st = unsafe { st.as_ref() };
!st.boot_services.is_null()
}
fn boot_services_raw_panicking() -> NonNull<uefi_raw::table::boot::BootServices> {
let st = table::system_table_raw_panicking();
let st = unsafe { st.as_ref() };
NonNull::new(st.boot_services).expect("boot services are not active")
}
#[must_use]
pub unsafe fn raise_tpl(tpl: Tpl) -> TplGuard {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
TplGuard {
old_tpl: unsafe { (bt.raise_tpl)(tpl) },
}
}
pub fn allocate_pages(
allocation_type: AllocateType,
memory_type: MemoryType,
count: usize,
) -> Result<NonNull<u8>> {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
let (ty, initial_addr) = match allocation_type {
AllocateType::AnyPages => (RawAllocateType::ANY_PAGES, 0),
AllocateType::MaxAddress(addr) => (RawAllocateType::MAX_ADDRESS, addr),
AllocateType::Address(addr) => (RawAllocateType::ADDRESS, addr),
};
let mut addr1 = initial_addr;
unsafe { (bt.allocate_pages)(ty, memory_type, count, &mut addr1) }.to_result()?;
if let Some(ptr) = NonNull::new(addr1 as *mut u8) {
return Ok(ptr);
}
let mut addr2 = initial_addr;
let r = unsafe { (bt.allocate_pages)(ty, memory_type, count, &mut addr2) }.to_result();
let _unused = unsafe { (bt.free_pages)(addr1, count) };
r?;
if let Some(ptr) = NonNull::new(addr2 as *mut u8) {
Ok(ptr)
} else {
Err(Status::OUT_OF_RESOURCES.into())
}
}
pub unsafe fn free_pages(ptr: NonNull<u8>, count: usize) -> Result {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
let addr = ptr.as_ptr() as PhysicalAddress;
unsafe { (bt.free_pages)(addr, count) }.to_result()
}
pub fn allocate_pool(memory_type: MemoryType, size: usize) -> Result<NonNull<u8>> {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
let mut buffer = ptr::null_mut();
let ptr = unsafe { (bt.allocate_pool)(memory_type, size, &mut buffer) }
.to_result_with_val(|| buffer)?;
NonNull::new(ptr).ok_or_else(|| Status::OUT_OF_RESOURCES.into())
}
pub unsafe fn free_pool(ptr: NonNull<u8>) -> Result {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
unsafe { (bt.free_pool)(ptr.as_ptr()) }.to_result()
}
#[must_use]
pub(crate) fn memory_map_size() -> MemoryMapMeta {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
let mut map_size = 0;
let mut map_key = MemoryMapKey(0);
let mut desc_size = 0;
let mut desc_version = 0;
let status = unsafe {
(bt.get_memory_map)(
&mut map_size,
ptr::null_mut(),
&mut map_key.0,
&mut desc_size,
&mut desc_version,
)
};
assert_eq!(status, Status::BUFFER_TOO_SMALL);
assert_eq!(
map_size % desc_size,
0,
"Memory map must be a multiple of the reported descriptor size."
);
let mmm = MemoryMapMeta {
desc_size,
map_size,
map_key,
desc_version,
};
mmm.assert_sanity_checks();
mmm
}
pub fn memory_map(mt: MemoryType) -> Result<MemoryMapOwned> {
let mut buffer = MemoryMapBackingMemory::new(mt)?;
let meta = get_memory_map(buffer.as_mut_slice());
if let Err(e) = &meta {
if e.status() == Status::BUFFER_TOO_SMALL {
panic!("Failed to get a proper allocation for the memory map");
}
}
let meta = meta?;
Ok(MemoryMapOwned::from_initialized_mem(buffer, meta))
}
pub(crate) fn get_memory_map(buf: &mut [u8]) -> Result<MemoryMapMeta> {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
let mut map_size = buf.len();
let map_buffer = buf.as_mut_ptr().cast::<MemoryDescriptor>();
let mut map_key = MemoryMapKey(0);
let mut desc_size = 0;
let mut desc_version = 0;
assert_eq!(
(map_buffer as usize) % align_of::<MemoryDescriptor>(),
0,
"Memory map buffers must be aligned like a MemoryDescriptor"
);
unsafe {
(bt.get_memory_map)(
&mut map_size,
map_buffer,
&mut map_key.0,
&mut desc_size,
&mut desc_version,
)
}
.to_result_with_val(|| MemoryMapMeta {
map_size,
desc_size,
map_key,
desc_version,
})
}
pub unsafe fn create_event(
event_ty: EventType,
notify_tpl: Tpl,
notify_fn: Option<EventNotifyFn>,
notify_ctx: Option<NonNull<c_void>>,
) -> Result<Event> {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
let mut event = ptr::null_mut();
let notify_fn: Option<uefi_raw::table::boot::EventNotifyFn> =
unsafe { mem::transmute(notify_fn) };
let notify_ctx = opt_nonnull_to_ptr(notify_ctx);
unsafe { (bt.create_event)(event_ty, notify_tpl, notify_fn, notify_ctx, &mut event) }
.to_result_with_val(
|| unsafe { Event::from_ptr(event) }.unwrap(),
)
}
pub unsafe fn create_event_ex(
event_type: EventType,
notify_tpl: Tpl,
notify_fn: Option<EventNotifyFn>,
notify_ctx: Option<NonNull<c_void>>,
event_group: Option<NonNull<Guid>>,
) -> Result<Event> {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
if bt.header.revision < Revision::EFI_2_00 {
return Err(Status::UNSUPPORTED.into());
}
let mut event = ptr::null_mut();
let notify_fn: Option<uefi_raw::table::boot::EventNotifyFn> =
unsafe { mem::transmute(notify_fn) };
unsafe {
(bt.create_event_ex)(
event_type,
notify_tpl,
notify_fn,
opt_nonnull_to_ptr(notify_ctx),
opt_nonnull_to_ptr(event_group),
&mut event,
)
}
.to_result_with_val(
|| unsafe { Event::from_ptr(event) }.unwrap(),
)
}
pub fn check_event(event: &Event) -> Result<bool> {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
let status = unsafe { (bt.check_event)(event.as_ptr()) };
match status {
Status::SUCCESS => Ok(true),
Status::NOT_READY => Ok(false),
_ => Err(status.into()),
}
}
pub fn signal_event(event: &Event) -> Result {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
unsafe { (bt.signal_event)(event.as_ptr()) }.to_result()
}
pub fn close_event(event: Event) -> Result {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
unsafe { (bt.close_event)(event.as_ptr()) }.to_result()
}
pub fn set_timer(event: &Event, trigger_time: TimerTrigger) -> Result {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
let (ty, time) = match trigger_time {
TimerTrigger::Cancel => (TimerDelay::CANCEL, 0),
TimerTrigger::Periodic(period) => (TimerDelay::PERIODIC, period),
TimerTrigger::Relative(delay) => (TimerDelay::RELATIVE, delay),
};
unsafe { (bt.set_timer)(event.as_ptr(), ty, time) }.to_result()
}
pub fn wait_for_event(events: &mut [Event]) -> Result<usize, Option<usize>> {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
let number_of_events = events.len();
let events: *mut uefi_raw::Event = events.as_mut_ptr().cast();
let mut index = 0;
unsafe { (bt.wait_for_event)(number_of_events, events, &mut index) }.to_result_with(
|| index,
|s| {
if s == Status::INVALID_PARAMETER {
Some(index)
} else {
None
}
},
)
}
pub fn connect_controller(
controller: Handle,
driver_image: Option<Handle>,
remaining_device_path: Option<&DevicePath>,
recursive: bool,
) -> Result {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
unsafe {
(bt.connect_controller)(
controller.as_ptr(),
Handle::opt_to_ptr(driver_image),
remaining_device_path
.map(|dp| dp.as_ffi_ptr())
.unwrap_or(ptr::null())
.cast(),
recursive.into(),
)
}
.to_result_with_err(|_| ())
}
pub fn disconnect_controller(
controller: Handle,
driver_image: Option<Handle>,
child: Option<Handle>,
) -> Result {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
unsafe {
(bt.disconnect_controller)(
controller.as_ptr(),
Handle::opt_to_ptr(driver_image),
Handle::opt_to_ptr(child),
)
}
.to_result_with_err(|_| ())
}
pub unsafe fn install_protocol_interface(
handle: Option<Handle>,
protocol: &Guid,
interface: *const c_void,
) -> Result<Handle> {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
let mut handle = Handle::opt_to_ptr(handle);
unsafe {
(bt.install_protocol_interface)(
&mut handle,
protocol,
InterfaceType::NATIVE_INTERFACE,
interface,
)
}
.to_result_with_val(|| unsafe { Handle::from_ptr(handle) }.unwrap())
}
pub unsafe fn reinstall_protocol_interface(
handle: Handle,
protocol: &Guid,
old_interface: *const c_void,
new_interface: *const c_void,
) -> Result<()> {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
unsafe {
(bt.reinstall_protocol_interface)(handle.as_ptr(), protocol, old_interface, new_interface)
}
.to_result()
}
pub unsafe fn uninstall_protocol_interface(
handle: Handle,
protocol: &Guid,
interface: *const c_void,
) -> Result<()> {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
unsafe { (bt.uninstall_protocol_interface)(handle.as_ptr(), protocol, interface).to_result() }
}
pub fn register_protocol_notify(
protocol: &'static Guid,
event: &Event,
) -> Result<SearchType<'static>> {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
let mut key = ptr::null();
unsafe { (bt.register_protocol_notify)(protocol, event.as_ptr(), &mut key) }.to_result_with_val(
|| {
SearchType::ByRegisterNotify(ProtocolSearchKey(NonNull::new(key.cast_mut()).unwrap()))
},
)
}
pub fn protocols_per_handle(handle: Handle) -> Result<ProtocolsPerHandle> {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
let mut protocols = ptr::null_mut();
let mut count = 0;
unsafe { (bt.protocols_per_handle)(handle.as_ptr(), &mut protocols, &mut count) }
.to_result_with_val(|| ProtocolsPerHandle {
count,
protocols: NonNull::new(protocols)
.expect("protocols_per_handle must not return a null pointer"),
})
}
pub fn locate_device_path<P: ProtocolPointer + ?Sized>(
device_path: &mut &DevicePath,
) -> Result<Handle> {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
let mut handle = ptr::null_mut();
let mut device_path_ptr: *const uefi_raw::protocol::device_path::DevicePathProtocol =
device_path.as_ffi_ptr().cast();
unsafe {
(bt.locate_device_path)(&P::GUID, &mut device_path_ptr, &mut handle).to_result_with_val(
|| {
*device_path = DevicePath::from_ffi_ptr(device_path_ptr.cast());
Handle::from_ptr(handle).unwrap()
},
)
}
}
pub fn locate_handle<'buf>(
search_ty: SearchType,
buffer: &'buf mut [MaybeUninit<Handle>],
) -> Result<&'buf [Handle], Option<usize>> {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
let (ty, guid, key) = match search_ty {
SearchType::AllHandles => (0, ptr::null(), ptr::null()),
SearchType::ByRegisterNotify(registration) => {
(1, ptr::null(), registration.0.as_ptr().cast_const())
}
SearchType::ByProtocol(guid) => (2, ptr::from_ref(guid), ptr::null()),
};
let mut buffer_size = buffer.len() * size_of::<Handle>();
let status =
unsafe { (bt.locate_handle)(ty, guid, key, &mut buffer_size, buffer.as_mut_ptr().cast()) };
let num_handles = buffer_size / size_of::<Handle>();
match status {
Status::SUCCESS => {
let buffer = &buffer[..num_handles];
let handles = unsafe { maybe_uninit_slice_assume_init_ref(buffer) };
Ok(handles)
}
Status::BUFFER_TOO_SMALL => Err(Error::new(status, Some(num_handles))),
_ => Err(Error::new(status, None)),
}
}
pub fn locate_handle_buffer(search_ty: SearchType) -> Result<HandleBuffer> {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
let (ty, guid, key) = match search_ty {
SearchType::AllHandles => (0, ptr::null(), ptr::null()),
SearchType::ByRegisterNotify(registration) => {
(1, ptr::null(), registration.0.as_ptr().cast_const())
}
SearchType::ByProtocol(guid) => (2, ptr::from_ref(guid), ptr::null()),
};
let mut num_handles: usize = 0;
let mut buffer: *mut uefi_raw::Handle = ptr::null_mut();
unsafe { (bt.locate_handle_buffer)(ty, guid, key, &mut num_handles, &mut buffer) }
.to_result_with_val(|| HandleBuffer {
count: num_handles,
buffer: NonNull::new(buffer.cast())
.expect("locate_handle_buffer must not return a null pointer"),
})
}
#[cfg(feature = "alloc")]
pub fn find_handles<P: ProtocolPointer + ?Sized>() -> Result<Vec<Handle>> {
let search_type = SearchType::from_proto::<P>();
let num_handles = match locate_handle(search_type, &mut []) {
Err(err) => {
if err.status() == Status::BUFFER_TOO_SMALL {
err.data().expect("error data is missing")
} else {
return Err(err.to_err_without_payload());
}
}
Ok(_) => panic!("locate_handle should not return success with empty buffer"),
};
let mut handles = Vec::with_capacity(num_handles);
let num_handles = locate_handle(search_type, handles.spare_capacity_mut())
.discard_errdata()?
.len();
unsafe {
handles.set_len(num_handles);
}
Ok(handles)
}
pub fn get_handle_for_protocol<P: ProtocolPointer + ?Sized>() -> Result<Handle> {
locate_handle_buffer(SearchType::ByProtocol(&P::GUID))?
.first()
.cloned()
.ok_or_else(|| Status::NOT_FOUND.into())
}
#[doc(alias = "handle_protocol")]
pub unsafe fn open_protocol<P: ProtocolPointer + ?Sized>(
params: OpenProtocolParams,
attributes: OpenProtocolAttributes,
) -> Result<ScopedProtocol<P>> {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
let mut interface = ptr::null_mut();
unsafe {
(bt.open_protocol)(
params.handle.as_ptr(),
&P::GUID,
&mut interface,
params.agent.as_ptr(),
Handle::opt_to_ptr(params.controller),
attributes as u32,
)
}
.to_result_with_val(|| {
let interface = if interface.is_null() {
None
} else {
NonNull::new(unsafe { P::mut_ptr_from_ffi(interface) })
};
ScopedProtocol {
interface,
open_params: params,
}
})
}
#[doc(alias = "handle_protocol")]
pub fn open_protocol_exclusive<P: ProtocolPointer + ?Sized>(
handle: Handle,
) -> Result<ScopedProtocol<P>> {
unsafe {
open_protocol::<P>(
OpenProtocolParams {
handle,
agent: image_handle(),
controller: None,
},
OpenProtocolAttributes::Exclusive,
)
}
}
pub fn test_protocol<P: ProtocolPointer + ?Sized>(params: OpenProtocolParams) -> Result<bool> {
const TEST_PROTOCOL: u32 = 0x04;
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
let mut interface = ptr::null_mut();
let status = unsafe {
(bt.open_protocol)(
params.handle.as_ptr(),
&P::GUID,
&mut interface,
params.agent.as_ptr(),
Handle::opt_to_ptr(params.controller),
TEST_PROTOCOL,
)
};
match status {
Status::SUCCESS => Ok(true),
Status::UNSUPPORTED => Ok(false),
_ => Err(Error::from(status)),
}
}
pub fn load_image(parent_image_handle: Handle, source: LoadImageSource) -> Result<Handle> {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
let (boot_policy, device_path, source_buffer, source_size) = source.to_ffi_params();
let mut image_handle = ptr::null_mut();
unsafe {
(bt.load_image)(
boot_policy.into(),
parent_image_handle.as_ptr(),
device_path.cast(),
source_buffer,
source_size,
&mut image_handle,
)
.to_result_with_val(
|| Handle::from_ptr(image_handle).unwrap(),
)
}
}
pub fn unload_image(image_handle: Handle) -> Result {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
unsafe { (bt.unload_image)(image_handle.as_ptr()) }.to_result()
}
pub fn start_image(image_handle: Handle) -> Result {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
let mut exit_data_size: usize = 0;
let mut exit_data: *mut u16 = ptr::null_mut();
unsafe {
(bt.start_image)(image_handle.as_ptr(), &mut exit_data_size, &mut exit_data).to_result()
}
}
pub unsafe fn exit(
image_handle: Handle,
exit_status: Status,
exit_data_size: usize,
exit_data: *mut Char16,
) -> ! {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
unsafe {
(bt.exit)(
image_handle.as_ptr(),
exit_status,
exit_data_size,
exit_data.cast(),
)
}
}
unsafe fn get_memory_map_and_exit_boot_services(buf: &mut [u8]) -> Result<MemoryMapMeta> {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
let memory_map = get_memory_map(buf)?;
unsafe { (bt.exit_boot_services)(image_handle().as_ptr(), memory_map.map_key.0) }
.to_result_with_val(|| memory_map)
}
#[must_use]
pub unsafe fn exit_boot_services(custom_memory_type: Option<MemoryType>) -> MemoryMapOwned {
let memory_type = custom_memory_type.unwrap_or(MemoryType::LOADER_DATA);
crate::helpers::exit();
let mut buf = MemoryMapBackingMemory::new(memory_type).expect("Failed to allocate memory");
let mut status = Status::ABORTED;
for _ in 0..2 {
match unsafe { get_memory_map_and_exit_boot_services(buf.as_mut_slice()) } {
Ok(memory_map) => {
return MemoryMapOwned::from_initialized_mem(buf, memory_map);
}
Err(err) => {
log::error!("Error retrieving the memory map for exiting the boot services");
status = err.status()
}
}
}
log::warn!("Resetting the machine");
runtime::reset(ResetType::COLD, status, None);
}
pub unsafe fn install_configuration_table(
guid_entry: &'static Guid,
table_ptr: *const c_void,
) -> Result {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
unsafe { (bt.install_configuration_table)(guid_entry, table_ptr) }.to_result()
}
pub fn set_watchdog_timer(
timeout_in_seconds: usize,
watchdog_code: u64,
data: Option<&mut [u16]>,
) -> Result {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
let (data_len, data) = data
.map(|d| {
assert!(
d.contains(&0),
"Watchdog data must start with a null-terminated string"
);
(d.len(), d.as_mut_ptr())
})
.unwrap_or((0, ptr::null_mut()));
unsafe { (bt.set_watchdog_timer)(timeout_in_seconds, watchdog_code, data_len, data) }
.to_result()
}
pub fn stall(duration: Duration) {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
let microseconds = duration.as_micros() as usize;
unsafe {
let _ = (bt.stall)(microseconds);
}
}
pub fn get_image_file_system(image_handle: Handle) -> Result<ScopedProtocol<SimpleFileSystem>> {
let loaded_image = open_protocol_exclusive::<LoadedImage>(image_handle)?;
let device_handle = loaded_image
.device()
.ok_or(Error::new(Status::UNSUPPORTED, ()))?;
let device_path = open_protocol_exclusive::<DevicePath>(device_handle)?;
let device_handle = locate_device_path::<SimpleFileSystem>(&mut &*device_path)?;
open_protocol_exclusive(device_handle)
}
pub fn calculate_crc32(data: &[u8]) -> Result<u32> {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
let mut crc = 0u32;
unsafe { (bt.calculate_crc32)(data.as_ptr().cast(), data.len(), &mut crc) }
.to_result_with_val(|| crc)
}
#[derive(Debug)]
pub struct ProtocolsPerHandle {
protocols: NonNull<*const Guid>,
count: usize,
}
impl Drop for ProtocolsPerHandle {
fn drop(&mut self) {
let _ = unsafe { free_pool(self.protocols.cast::<u8>()) };
}
}
impl Deref for ProtocolsPerHandle {
type Target = [&'static Guid];
fn deref(&self) -> &Self::Target {
let ptr: *const &'static Guid = self.protocols.as_ptr().cast();
unsafe { slice::from_raw_parts(ptr, self.count) }
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct HandleBuffer {
count: usize,
buffer: NonNull<Handle>,
}
impl Drop for HandleBuffer {
fn drop(&mut self) {
let _ = unsafe { free_pool(self.buffer.cast::<u8>()) };
}
}
impl Deref for HandleBuffer {
type Target = [Handle];
fn deref(&self) -> &Self::Target {
unsafe { slice::from_raw_parts(self.buffer.as_ptr(), self.count) }
}
}
#[derive(Debug)]
pub struct ScopedProtocol<P: Protocol + ?Sized> {
interface: Option<NonNull<P>>,
open_params: OpenProtocolParams,
}
impl<P: Protocol + ?Sized> ScopedProtocol<P> {
#[must_use]
pub const fn open_params(&self) -> OpenProtocolParams {
self.open_params
}
}
impl<P: Protocol + ?Sized + Display> Display for ScopedProtocol<P> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self.get() {
Some(proto) => {
write!(f, "{proto}")
}
None => {
write!(f, "<none>")
}
}
}
}
impl<P: Protocol + ?Sized> Drop for ScopedProtocol<P> {
fn drop(&mut self) {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
let status = unsafe {
(bt.close_protocol)(
self.open_params.handle.as_ptr(),
&P::GUID,
self.open_params.agent.as_ptr(),
Handle::opt_to_ptr(self.open_params.controller),
)
};
assert_eq!(status, Status::SUCCESS);
}
}
impl<P: Protocol + ?Sized> Deref for ScopedProtocol<P> {
type Target = P;
#[track_caller]
fn deref(&self) -> &Self::Target {
unsafe { self.interface.unwrap().as_ref() }
}
}
impl<P: Protocol + ?Sized> DerefMut for ScopedProtocol<P> {
#[track_caller]
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { self.interface.unwrap().as_mut() }
}
}
impl<P: Protocol + ?Sized> ScopedProtocol<P> {
#[must_use]
pub fn get(&self) -> Option<&P> {
self.interface.map(|p| unsafe { p.as_ref() })
}
#[must_use]
pub fn get_mut(&mut self) -> Option<&mut P> {
self.interface.map(|mut p| unsafe { p.as_mut() })
}
}
#[derive(Debug)]
pub struct TplGuard {
old_tpl: Tpl,
}
impl TplGuard {
#[must_use]
pub const fn old_tpl(&self) -> Tpl {
self.old_tpl
}
}
impl Drop for TplGuard {
fn drop(&mut self) {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
unsafe {
(bt.restore_tpl)(self.old_tpl);
}
}
}
#[repr(u32)]
#[derive(Debug)]
pub enum OpenProtocolAttributes {
GetProtocol = 0x02,
ByChildController = 0x08,
ByDriver = 0x10,
Exclusive = 0x20,
ByDriverExclusive = 0x30,
}
#[derive(Clone, Copy, Debug)]
pub struct OpenProtocolParams {
pub handle: Handle,
pub agent: Handle,
pub controller: Option<Handle>,
}
#[derive(Debug)]
pub enum LoadImageSource<'a> {
FromBuffer {
buffer: &'a [u8],
file_path: Option<&'a DevicePath>,
},
FromDevicePath {
device_path: &'a DevicePath,
boot_policy: BootPolicy,
},
}
impl LoadImageSource<'_> {
#[must_use]
pub(crate) fn to_ffi_params(
&self,
) -> (
BootPolicy,
*const FfiDevicePath,
*const u8, /* buffer */
usize, /* buffer length */
) {
let boot_policy;
let device_path;
let source_buffer;
let source_size;
match self {
LoadImageSource::FromBuffer { buffer, file_path } => {
boot_policy = BootPolicy::default();
device_path = file_path.map(|p| p.as_ffi_ptr()).unwrap_or(ptr::null());
source_buffer = buffer.as_ptr();
source_size = buffer.len();
}
LoadImageSource::FromDevicePath {
device_path: d_path,
boot_policy: b_policy,
} => {
boot_policy = *b_policy;
device_path = d_path.as_ffi_ptr();
source_buffer = ptr::null();
source_size = 0;
}
};
(boot_policy, device_path, source_buffer, source_size)
}
}
#[derive(Debug, Copy, Clone)]
pub enum AllocateType {
AnyPages,
MaxAddress(PhysicalAddress),
Address(PhysicalAddress),
}
#[derive(Debug, Copy, Clone)]
pub enum SearchType<'guid> {
AllHandles,
ByProtocol(&'guid Guid),
ByRegisterNotify(ProtocolSearchKey),
}
impl SearchType<'_> {
#[must_use]
pub const fn from_proto<P: ProtocolPointer + ?Sized>() -> Self {
SearchType::ByProtocol(&P::GUID)
}
}
pub type EventNotifyFn = unsafe extern "efiapi" fn(event: Event, context: Option<NonNull<c_void>>);
#[derive(Debug)]
pub enum TimerTrigger {
Cancel,
Periodic(
u64,
),
Relative(
u64,
),
}
#[derive(Debug, Clone, Copy)]
#[repr(transparent)]
pub struct ProtocolSearchKey(pub(crate) NonNull<c_void>);