#![no_std]
macro_rules! align_up {
($value:expr, $alignment:expr) => {
(($value - 1) | ($alignment - 1)) + 1
};
}
use crate::{
bootservices::*, configtable::*, loadedimage::*, memattr::*, memmap::*, memorytype::*,
peloader::*, rng::*, runtimeservices::*, simpletext::*, systemtable::*, EfiMemoryType::*,
};
use core::cell::*;
use core::mem::*;
use core::ops::*;
use core::pin::*;
use core::ptr::*;
use core::sync::atomic::{AtomicUsize, Ordering};
extern crate alloc;
use alloc::boxed::Box;
use alloc::collections::BTreeMap;
use once_cell::unsync::OnceCell;
const UEFI_REVISION: u32 = (2 << 16) | 100;
pub mod blockio;
pub mod bootservices;
#[cfg(target_arch = "aarch64")]
mod cmo;
mod configtable;
pub mod devicepath;
mod initrdloadfile2;
mod loadedimage;
mod memattr;
pub mod memmap;
pub mod memorytype;
mod peloader;
mod poolalloc;
mod rng;
pub mod runtimeservices;
mod simpletext;
pub mod status;
mod systemtable;
mod tableheader;
pub type Bool = u8;
pub type Char16 = u16;
type PhysicalAddress = u64;
type VirtualAddress = u64;
pub type Handle = usize;
type Tpl = usize;
#[repr(transparent)]
pub struct Event(*mut ());
type EventNotify = extern "efiapi" fn(Event, *const ());
pub type Lba = u64;
pub(crate) fn new_handle() -> usize {
static COUNTER: AtomicUsize = AtomicUsize::new(1);
COUNTER.fetch_add(1, Ordering::AcqRel)
}
const TPL_APPLICATION: Tpl = 4;
#[allow(dead_code)]
const TPL_CALLBACK: Tpl = 8;
#[allow(dead_code)]
const TPL_NOTIFY: Tpl = 16;
#[allow(dead_code)]
const TPL_HIGH_LEVEL: Tpl = 31;
#[derive(PartialEq, PartialOrd, Eq, Ord, Clone, Copy, Debug)]
#[repr(C)]
pub struct Guid {
pub data1: u32,
pub data2: u16,
pub data3: u16,
pub data4: [u8; 8],
}
#[macro_export]
macro_rules! guid {
($a:literal, $b:literal, $c: literal, $d:expr) => {
Guid {
data1: $a,
data2: $b,
data3: $c,
data4: $d,
}
};
}
pub trait SimpleConsole {
fn write_string(&self, s: &str);
fn read_byte(&self) -> Option<u8>;
}
pub trait MemoryMapper {
fn remap_range(&self, range: &Range<usize>, set: u64, clr: u64) -> Result<(), &str>;
fn query_range(&self, range: &Range<usize>) -> Option<u64>;
}
pub trait FileLoader {
fn get_size(&self) -> usize;
fn load_file<'a>(&self, loadbuffer: &'a mut [MaybeUninit<u8>]) -> Result<&'a [u8], &str>;
unsafe fn load_range<'a>(
&self,
loadbuffer: *mut (),
offset: usize,
size: usize,
) -> Result<(), &str>;
}
pub trait Random {
fn get_entropy(&self, bytes: &mut [u8], use_raw: bool) -> bool;
}
const EFI_RT_PROPERTIES_TABLE_GUID: Guid = guid!(
0xeb66918a,
0x7eef,
0x402a,
[0x84, 0x2e, 0x93, 0x1d, 0x21, 0xc3, 0x8a, 0xe9]
);
const EFI_RT_SUPPORTED_GET_TIME: u32 = 0x0001;
const EFI_RT_SUPPORTED_SET_TIME: u32 = 0x0002;
const EFI_RT_SUPPORTED_GET_VARIABLE: u32 = 0x0010;
const EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME: u32 = 0x0020;
const EFI_RT_SUPPORTED_RESET_SYSTEM: u32 = 0x0400;
#[repr(C)]
struct RtPropertiesTable {
version: u16,
length: u16,
supported_mask: u32,
}
pub trait EfiProtocol {
fn as_proto_ptr(&self) -> *const () {
self as *const _ as *const ()
}
fn guid(&self) -> &Guid;
}
pub(crate) type ProtocolDb = BTreeMap<(Handle, Guid), Pin<Box<dyn EfiProtocol + Send>>>;
pub struct EfiContext {
cfgtable: ConfigTableDb,
pub(crate) protocol_db: RefCell<ProtocolDb>,
pub(crate) con: Option<&'static dyn SimpleConsole>,
pub(crate) memmap: MemoryMap,
pub(crate) mapper: Box<dyn MemoryMapper>,
pub(crate) rng: Option<Box<dyn Random>>,
bs: RefCell<Box<BootServices>>,
rt: RefCell<PoolBox<RuntimeServices>>,
pub(crate) st: RefCell<PoolBox<SystemTable>>,
}
static EFI: EfiContextHolder = EfiContextHolder(OnceCell::new());
struct EfiContextHolder(OnceCell<EfiContext>);
unsafe impl Sync for EfiContextHolder {}
impl Deref for EfiContextHolder {
type Target = EfiContext;
fn deref(&self) -> &Self::Target {
&self.0.get().expect("efiloader::init() has not been called yet")
}
}
pub(crate) fn efi_system_table() -> *const SystemTable {
&**EFI.st.borrow()
}
pub fn init(
con: Option<&'static (dyn SimpleConsole)>,
memmap: MemoryMap,
mapper: impl MemoryMapper + 'static,
rng: Option<impl Random + 'static>,
) -> Result<&'static EfiContext, ()> {
EFI.0.get_or_try_init(move || {
let conhandle = new_handle();
let inp = SimpleTextInput::new(EfiContext::read_byte);
let out = SimpleTextOutput::new(EfiContext::write_string);
let bs = Box::new(BootServices::new());
let rt = memmap
.box_new(EfiRuntimeServicesData, RuntimeServices::new())
.or(Err(()))?;
let st = memmap
.box_new(
EfiRuntimeServicesData,
SystemTable::new(&*bs, &*rt, &inp.text_input, &out.text_output, conhandle),
)
.or(Err(()))?;
let ctx = EfiContext {
cfgtable: ConfigTableDb::new(),
protocol_db: RefCell::new(BTreeMap::new()),
con: con,
memmap: memmap,
mapper: Box::new(mapper),
rng: rng.map(|r| Box::new(r) as _),
bs: RefCell::new(bs),
rt: RefCell::new(rt),
st: RefCell::new(st),
};
ctx.install_pinned_protocol(conhandle, inp);
ctx.install_pinned_protocol(conhandle, out);
ctx.install_protocol(None, EfiMemoryAttribute::new());
ctx.install_protocol(None, EfiRng::new());
let rtprop = ctx
.memmap
.box_new(
EfiACPIReclaimMemory,
RtPropertiesTable {
version: 1,
length: core::mem::size_of::<RtPropertiesTable>() as _,
supported_mask: EFI_RT_SUPPORTED_GET_TIME
| EFI_RT_SUPPORTED_SET_TIME
| EFI_RT_SUPPORTED_GET_VARIABLE
| EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME
| EFI_RT_SUPPORTED_RESET_SYSTEM,
},
)
.or(Err(()))?;
ctx.install_configtable(&EFI_RT_PROPERTIES_TABLE_GUID, rtprop);
Ok(ctx)
})
}
impl EfiContext {
pub(crate) fn install_pinned_protocol<T: EfiProtocol + Send + 'static>(
&self,
handle: Handle,
protocol: Pin<Box<T>>,
) {
self.protocol_db
.borrow_mut()
.insert((handle, *protocol.guid()), protocol);
}
pub fn install_protocol<T: EfiProtocol + Send + 'static>(
&self,
handle: Option<Handle>,
protocol: T,
) -> Handle {
let handle = handle.unwrap_or_else(|| new_handle());
self.install_pinned_protocol(handle, Box::pin(protocol));
handle
}
pub fn uninstall_protocol<T>(&self, handle: Handle, guid: &Guid, _protocol: &T) {
self.protocol_db.borrow_mut().remove(&(handle, *guid));
}
pub fn install_configtable<T>(&self, guid: &Guid, table: T)
where
T: Into<ConfigurationTablePointer>,
{
self.cfgtable.install(guid, table, self)
}
pub fn set_initrd_loader(&self, initrd: impl FileLoader + Send + 'static) {
initrdloadfile2::install(self, initrd);
}
pub(crate) fn get_entropy(&self, buf: &mut [u8], use_raw: bool) -> bool {
self.rng
.as_ref()
.map(|r| r.get_entropy(buf, use_raw))
.unwrap_or(false)
}
pub fn load_image<'a>(&'static self, loader: &'a dyn FileLoader) -> Option<LoadedImageData> {
let pe_ldr = PeLoader::new(loader, self)?;
let align = EFI_PAGE_SIZE.max(pe_ldr.section_alignment()) as u64;
let mut seed: [u8; 4] = [0; 4];
let (placement, randomized) = if self.get_entropy(&mut seed, false) {
(Placement::Random(u32::from_le_bytes(seed), align), true)
} else {
(Placement::Aligned(align), false)
};
let pe_image = pe_ldr.load(EfiLoaderCode, placement, &*self.mapper)?;
Some(LoadedImageData::new(
&self,
&pe_image,
EfiLoaderCode,
EfiLoaderData,
randomized,
))
}
pub fn override_time_handler(&self, get: GetTime, set: Option<SetTime>) {
let mut rt = self.rt.borrow_mut();
rt.get_time = get;
set.map(|s| rt.set_time = s);
rt.hdr.update_crc();
}
pub fn override_variable_handler(
&self,
get: GetVariable,
get_next: GetNextVariableName,
set: Option<SetVariable>,
) {
let mut rt = self.rt.borrow_mut();
rt.get_variable = get;
rt.get_next_variable_name = get_next;
set.map(|s| rt.set_variable = s);
rt.hdr.update_crc();
}
pub fn override_reset_handler(&self, f: ResetSystem) {
let mut rt = self.rt.borrow_mut();
rt.reset_system = f;
rt.hdr.update_crc();
}
pub fn override_stall_handler(&self, f: Stall) {
let mut bs = self.bs.borrow_mut();
bs.stall = f;
bs.hdr.update_crc();
}
fn write_string(s: &str) {
EFI.con.map(|c| c.write_string(s));
}
fn read_byte() -> Option<u8> {
EFI.con?.read_byte()
}
pub fn allocate_pool(&self, pool_type: EfiMemoryType, size: usize) -> Result<NonNull<u8>, ()> {
self.memmap
.allocate_pool::<u8>(pool_type, size)
.map_err(|e| log::debug!("Allocate pool failed {e}"))
}
pub fn free_pool(&self, buffer: *const u8) -> Result<(), ()> {
self.memmap.free_pool(buffer)
}
pub fn allocate_pages(
&self,
pages: usize,
_type: EfiMemoryType,
placement: Placement,
) -> Option<&'static mut [MaybeUninit<u8>]> {
self.memmap.allocate_pages(pages, _type, placement)
}
pub fn free_pages(&self, base: u64, pages: usize) -> Result<(), ()> {
self.memmap.free_pages(base, pages)
}
}