use crate::event::Event;
use crate::{
current_image, Device, Guid, Image, Pages, Path, Status, TableHeader, IMAGE, PAGE_SIZE,
};
use alloc::vec::Vec;
use bitflags::bitflags;
use core::mem::size_of;
use core::ptr::{null, null_mut};
#[repr(C)]
pub struct BootServices {
hdr: TableHeader,
raise_tpl: fn(),
restore_tpl: fn(),
allocate_pages: unsafe extern "efiapi" fn(AllocateType, MemoryType, usize, *mut u64) -> Status,
free_pages: unsafe extern "efiapi" fn(u64, usize) -> Status,
get_memory_map: unsafe extern "efiapi" fn(
*mut usize,
*mut MemoryDescriptor,
*mut usize,
*mut usize,
*mut u32,
) -> Status,
allocate_pool: unsafe extern "efiapi" fn(MemoryType, usize, *mut *mut u8) -> Status,
free_pool: unsafe extern "efiapi" fn(*mut u8) -> Status,
create_event: fn(),
set_timer: fn(),
wait_for_event: unsafe extern "efiapi" fn(usize, *const Event, *mut usize) -> Status,
signal_event: fn(),
close_event: fn(),
check_event: fn(),
install_protocol_interface: fn(),
reinstall_protocol_interface: fn(),
uninstall_protocol_interface: fn(),
handle_protocol: fn(),
reserved: usize,
register_protocol_notify: fn(),
locate_handle: fn(),
locate_device_path:
unsafe extern "efiapi" fn(*const Guid, *mut *const u8, *mut *const ()) -> Status,
install_configuration_table: fn(),
load_image: fn(),
start_image: fn(),
exit: fn(),
unload_image: fn(),
exit_boot_services: extern "efiapi" fn(&Image, usize) -> Status,
get_next_monotonic_count: fn(),
stall: fn(),
set_watchdog_timer: fn(),
connect_controller: fn(),
disconnect_controller: fn(),
open_protocol: unsafe extern "efiapi" fn(
*const (),
*const Guid,
*mut *const (),
*const (),
*const (),
OpenProtocolAttributes,
) -> Status,
}
impl BootServices {
pub fn allocate_pages(
&self,
at: AllocateType,
mt: MemoryType,
pages: usize,
mut addr: u64,
) -> Result<Pages, Status> {
let status = unsafe { (self.allocate_pages)(at, mt, pages, &mut addr) };
if status != Status::SUCCESS {
Err(status)
} else {
Ok(unsafe { Pages::new(addr as _, pages * PAGE_SIZE) })
}
}
pub unsafe fn free_pages(&self, base: *mut u8, pages: usize) -> Result<(), Status> {
let status = (self.free_pages)(base as _, pages);
if status != Status::SUCCESS {
Err(status)
} else {
Ok(())
}
}
pub fn get_memory_map(&self) -> Result<(Vec<MemoryDescriptor>, usize), Status> {
let mut len = 1;
loop {
let mut size = len * size_of::<MemoryDescriptor>();
let mut map: Vec<MemoryDescriptor> = Vec::with_capacity(len);
let mut key = 0;
let mut dsize = 0;
let mut dver = 0;
let status = unsafe {
(self.get_memory_map)(
&mut size,
map.spare_capacity_mut().as_mut_ptr() as _,
&mut key,
&mut dsize,
&mut dver,
)
};
len = size / size_of::<MemoryDescriptor>();
match status {
Status::SUCCESS => {
assert_eq!(dsize, size_of::<MemoryDescriptor>());
assert_eq!(dver, 1);
unsafe { map.set_len(len) };
break Ok((map, key));
}
Status::BUFFER_TOO_SMALL => continue,
v => break Err(v),
}
}
}
pub fn allocate_pool(&self, ty: MemoryType, size: usize) -> Result<*mut u8, Status> {
let mut mem = null_mut();
let status = unsafe { (self.allocate_pool)(ty, size, &mut mem) };
if status != Status::SUCCESS {
Err(status)
} else {
Ok(mem)
}
}
pub unsafe fn free_pool(&self, mem: *mut u8) -> Result<(), Status> {
(self.free_pool)(mem).err_or(())
}
pub(crate) fn wait_for_event(&self, events: &[Event]) -> Result<usize, Status> {
let mut index = 0;
let status = unsafe { (self.wait_for_event)(events.len(), events.as_ptr(), &mut index) };
if status != Status::SUCCESS {
Err(status)
} else {
Ok(index)
}
}
pub fn locate_device_path<'a>(
&self,
proto: &Guid,
path: &'a Path,
) -> Result<(&'static Device, &'a Path), Status> {
let mut path = path.as_bytes().as_ptr();
let mut device = null();
let status = unsafe { (self.locate_device_path)(proto, &mut path, &mut device) };
if status != Status::SUCCESS {
Err(status)
} else {
Ok(unsafe { (&*(device as *const Device), Path::from_ptr(path)) })
}
}
pub unsafe fn exit_boot_services(&self, map_key: usize) -> Result<(), Status> {
let status = (self.exit_boot_services)(current_image(), map_key);
if status != Status::SUCCESS {
Err(status)
} else {
Ok(())
}
}
pub unsafe fn get_protocol(&self, handle: *const (), proto: &Guid) -> Option<*const ()> {
let agent = IMAGE.cast();
let attrs = OpenProtocolAttributes::GET_PROTOCOL;
match self.open_protocol(handle, proto, agent, null(), attrs) {
Ok(v) => Some(v),
Err(e) => {
assert_eq!(e, Status::UNSUPPORTED);
None
}
}
}
pub unsafe fn open_protocol(
&self,
handle: *const (),
proto: &Guid,
agent: *const (),
controller: *const (),
attrs: OpenProtocolAttributes,
) -> Result<*const (), Status> {
let mut interface = null();
let status = (self.open_protocol)(handle, proto, &mut interface, agent, controller, attrs);
if status != Status::SUCCESS {
Err(status)
} else {
Ok(interface)
}
}
}
#[repr(C)]
pub enum AllocateType {
AnyPages,
MaxAddress,
Address,
}
#[repr(C)]
pub enum MemoryType {
Reserved,
LoaderCode,
LoaderData,
BootServicesCode,
BootServicesData,
RuntimeServicesCode,
RuntimeServicesData,
ConventionalMemory,
UnusableMemory,
AcpiReclaimMemory,
AcpiMemoryNvs,
MemoryMappedIo,
MemoryMappedIoPortSpace,
PalCode,
PersistentMemory,
Unaccepted,
}
#[repr(C)]
pub struct MemoryDescriptor {
ty: u32,
physical_start: u64,
virtual_start: u64,
number_of_pages: u64,
attribute: u64,
}
bitflags! {
#[repr(transparent)]
pub struct OpenProtocolAttributes: u32 {
const GET_PROTOCOL = 0x00000002;
}
}