#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
#![feature(alloc_error_handler)]
#![feature(c_variadic)]
#![feature(allocator_api)]
#![feature(coverage_attribute)]
extern crate alloc;
mod allocator;
mod config_tables;
mod cpu_arch_protocol;
mod decompress;
mod dispatcher;
mod driver_services;
mod dxe_services;
mod event_db;
mod events;
mod filesystems;
mod fv;
mod gcd;
#[cfg(all(target_os = "uefi", target_arch = "aarch64"))]
mod hw_interrupt_protocol;
mod image;
mod memory_attributes_protocol;
mod memory_manager;
mod misc_boot_services;
mod pecoff;
mod protocol_db;
mod protocols;
mod runtime;
mod systemtables;
mod tpl_lock;
#[cfg(test)]
#[macro_use]
#[coverage(off)]
pub mod test_support;
use core::{ffi::c_void, ptr, str::FromStr};
use alloc::{boxed::Box, vec::Vec};
use gcd::SpinLockedGcd;
use memory_manager::CoreMemoryManager;
use mu_rust_helpers::{function, guid::CALLER_ID};
use patina::{
boot_services::StandardBootServices,
component::{Component, IntoComponent, Storage, service::IntoService},
error::{self, Result},
performance::{
logging::{perf_function_begin, perf_function_end},
measurement::create_performance_measurement,
},
runtime_services::StandardRuntimeServices,
};
use patina_ffs::section::SectionExtractor;
use patina_internal_cpu::{cpu::EfiCpu, interrupts::Interrupts};
use patina_pi::{
hob::{HobList, get_c_hob_list_size},
protocols::{bds, status_code},
status_code::{EFI_PROGRESS_CODE, EFI_SOFTWARE_DXE_CORE, EFI_SW_DXE_CORE_PC_HANDOFF_TO_NEXT},
};
use protocols::PROTOCOL_DB;
use r_efi::efi;
use crate::config_tables::memory_attributes_table;
#[doc(hidden)]
#[macro_export]
macro_rules! ensure {
($condition:expr, $err:expr) => {{
if !($condition) {
error!($err);
}
}};
}
#[doc(hidden)]
#[macro_export]
macro_rules! error {
($err:expr) => {{
return Err($err.into()).into();
}};
}
pub(crate) static GCD: SpinLockedGcd = SpinLockedGcd::new(Some(events::gcd_map_change));
#[derive(Debug, PartialEq)]
pub struct GicBases(pub u64, pub u64);
impl GicBases {
pub fn new(gicd_base: u64, gicr_base: u64) -> Self {
GicBases(gicd_base, gicr_base)
}
}
impl Default for GicBases {
fn default() -> Self {
panic!("GicBases `Config` must be manually initialized and registered with the Core using `with_config`.");
}
}
#[doc(hidden)]
pub struct Alloc;
#[doc(hidden)]
pub struct NoAlloc;
pub struct Core<MemoryState> {
physical_hob_list: *const c_void,
hob_list: HobList<'static>,
components: Vec<Box<dyn Component>>,
storage: Storage,
_memory_state: core::marker::PhantomData<MemoryState>,
}
impl Default for Core<NoAlloc> {
fn default() -> Self {
Core {
physical_hob_list: core::ptr::null(),
hob_list: HobList::default(),
components: Vec::new(),
storage: Storage::new(),
_memory_state: core::marker::PhantomData,
}
}
}
impl Core<NoAlloc> {
pub fn init_memory(mut self, physical_hob_list: *const c_void) -> Core<Alloc> {
log::info!("DXE Core Crate v{}", env!("CARGO_PKG_VERSION"));
let mut cpu = EfiCpu::default();
cpu.initialize().expect("Failed to initialize CPU!");
let mut interrupt_manager = Interrupts::default();
interrupt_manager.initialize().expect("Failed to initialize Interrupts!");
if physical_hob_list.is_null() {
panic!("HOB list pointer is null!");
}
gcd::init_gcd(physical_hob_list);
log::trace!("Initial GCD:\n{GCD}");
self.hob_list.discover_hobs(physical_hob_list);
log::trace!("HOB list discovered is:");
log::trace!("{:#x?}", self.hob_list);
PROTOCOL_DB.init_protocol_db();
allocator::init_memory_support(&self.hob_list);
self.hob_list.relocate_hobs();
patina_debugger::add_monitor_command("gcd", "Prints the GCD", |_, out| {
let _ = write!(out, "GCD -\n{GCD}");
});
patina_debugger::initialize(&mut interrupt_manager);
log::info!("GCD - After memory init:\n{GCD}");
self.storage.add_service(cpu);
self.storage.add_service(interrupt_manager);
self.storage.add_service(CoreMemoryManager);
Core {
physical_hob_list,
hob_list: self.hob_list,
components: self.components,
storage: self.storage,
_memory_state: core::marker::PhantomData,
}
}
pub fn prioritize_32_bit_memory(self) -> Self {
GCD.prioritize_32_bit_memory(true);
self
}
}
impl Core<Alloc> {
#[inline(always)]
pub fn with_service(mut self, service: impl IntoService + 'static) -> Self {
self.storage.add_service(service);
self
}
#[inline(always)]
pub fn with_component<I>(mut self, component: impl IntoComponent<I>) -> Self {
self.insert_component(self.components.len(), component.into_component());
self
}
fn insert_component(&mut self, idx: usize, mut component: Box<dyn Component>) {
component.initialize(&mut self.storage);
self.components.insert(idx, component);
}
pub fn with_config<C: Default + 'static>(mut self, config: C) -> Self {
self.storage.add_config(config);
self
}
fn parse_hobs(&mut self) {
for hob in self.hob_list.iter() {
if let patina_pi::hob::Hob::GuidHob(guid, data) = hob {
let parser_funcs = self.storage.get_hob_parsers(&patina::OwnedGuid::from(guid.name));
if parser_funcs.is_empty() {
let (f0, f1, f2, f3, f4, &[f5, f6, f7, f8, f9, f10]) = guid.name.as_fields();
let name = alloc::format!(
"{f0:08x}-{f1:04x}-{f2:04x}-{f3:02x}{f4:02x}-{f5:02x}{f6:02x}{f7:02x}{f8:02x}{f9:02x}{f10:02x}"
);
log::warn!(
"No parser registered for HOB: GuidHob {{ {:?}, name: Guid {{ {} }} }}",
guid.header,
name
);
} else {
for parser_func in parser_funcs {
parser_func(data, &mut self.storage);
}
}
}
}
}
fn dispatch_components(&mut self) -> bool {
let len = self.components.len();
self.components.retain_mut(|component| {
let name = component.metadata().name();
log::trace!("Dispatch Start: Id = [{name:?}]");
!match component.run(&mut self.storage) {
Ok(true) => {
log::info!("Dispatched: Id = [{name:?}] Status = [Success]");
true
}
Ok(false) => false,
Err(err) => {
log::error!("Dispatched: Id = [{name:?}] Status = [Failed] Error = [{err:?}]");
debug_assert!(false);
true }
}
});
len != self.components.len()
}
fn core_dispatcher(&mut self) -> Result<()> {
perf_function_begin(function!(), &CALLER_ID, create_performance_measurement);
loop {
let dispatched = self.dispatch_components();
let dispatched = dispatched
|| dispatcher::dispatch().inspect_err(|err| log::error!("UEFI Driver Dispatch error: {err:?}"))?;
if !dispatched {
break;
}
}
perf_function_end(function!(), &CALLER_ID, create_performance_measurement);
Ok(())
}
fn display_components_not_dispatched(&self) {
if !self.components.is_empty() {
let name_len = "name".len();
let param_len = "failed_param".len();
let max_name_len = self.components.iter().map(|c| c.metadata().name().len()).max().unwrap_or(name_len);
let max_param_len = self
.components
.iter()
.map(|c| c.metadata().failed_param().map(|s| s.len()).unwrap_or(0))
.max()
.unwrap_or(param_len);
log::warn!("Components not dispatched:");
log::warn!("{:-<max_name_len$} {:-<max_param_len$}", "", "");
log::warn!("{:<max_name_len$} {:<max_param_len$}", "name", "failed_param");
for component in &self.components {
let metadata = component.metadata();
log::warn!(
"{:<max_name_len$} {:<max_param_len$}",
metadata.name(),
metadata.failed_param().unwrap_or("")
);
}
}
}
fn get_hob_list_len(hob_list: *const c_void) -> usize {
unsafe { get_c_hob_list_size(hob_list) }
}
fn initialize_system_table(&mut self) -> Result<()> {
let hob_list_slice = unsafe {
core::slice::from_raw_parts(
self.physical_hob_list as *const u8,
Self::get_hob_list_len(self.physical_hob_list),
)
};
let relocated_c_hob_list = hob_list_slice.to_vec().into_boxed_slice();
systemtables::init_system_table();
{
let mut st = systemtables::SYSTEM_TABLE.lock();
let st = st.as_mut().expect("System Table not initialized!");
allocator::install_memory_services(st.boot_services_mut());
gcd::init_paging(&self.hob_list);
events::init_events_support(st.boot_services_mut());
protocols::init_protocol_support(st.boot_services_mut());
misc_boot_services::init_misc_boot_services_support(st.boot_services_mut());
config_tables::init_config_tables_support(st.boot_services_mut());
runtime::init_runtime_support(st.runtime_services_mut());
image::init_image_support(&self.hob_list, st);
dispatcher::init_dispatcher();
dxe_services::init_dxe_services(st);
driver_services::init_driver_services(st.boot_services_mut());
memory_attributes_protocol::install_memory_attributes_protocol();
st.checksum_all();
let (a, b, c, &[d0, d1, d2, d3, d4, d5, d6, d7]) =
uuid::Uuid::from_str("7739F24C-93D7-11D4-9A3A-0090273FC14D").expect("Invalid UUID format.").as_fields();
let hob_list_guid: efi::Guid = efi::Guid::from_fields(a, b, c, d0, d1, &[d2, d3, d4, d5, d6, d7]);
config_tables::core_install_configuration_table(
hob_list_guid,
Box::leak(relocated_c_hob_list).as_mut_ptr() as *mut c_void,
st,
)
.expect("Unable to create configuration table due to invalid table entry.");
allocator::install_memory_type_info_table(st).expect("Unable to create Memory Type Info Table");
}
let boot_services_ptr;
let runtime_services_ptr;
{
let mut st = systemtables::SYSTEM_TABLE.lock();
let st = st.as_mut().expect("System Table is not initialized!");
boot_services_ptr = st.boot_services_mut() as *mut efi::BootServices;
runtime_services_ptr = st.runtime_services_mut() as *mut efi::RuntimeServices;
}
tpl_lock::init_boot_services(boot_services_ptr);
memory_attributes_table::init_memory_attributes_table_support();
unsafe {
self.storage.set_boot_services(StandardBootServices::new(&*boot_services_ptr));
self.storage.set_runtime_services(StandardRuntimeServices::new(&*runtime_services_ptr));
}
Ok(())
}
#[allow(clippy::default_constructed_unit_structs)]
fn add_core_components(&mut self) {
self.insert_component(0, decompress::DecompressProtocolInstaller::default().into_component());
self.insert_component(0, systemtables::SystemTableChecksumInstaller::default().into_component());
self.insert_component(0, cpu_arch_protocol::CpuArchProtocolInstaller::default().into_component());
#[cfg(all(target_os = "uefi", target_arch = "aarch64"))]
self.insert_component(0, hw_interrupt_protocol::HwInterruptProtocolInstaller::default().into_component());
}
pub fn start(mut self) -> Result<()> {
log::info!("Registering default components");
self.add_core_components();
log::info!("Finished.");
log::info!("Initializing System Table");
self.initialize_system_table()?;
log::info!("Finished.");
log::info!("Parsing HOB list for Guided HOBs.");
self.parse_hobs();
log::info!("Finished.");
if let Some(extractor) = self.storage.get_service::<dyn SectionExtractor>() {
log::debug!("Section Extractor service found, registering with FV and Dispatcher.");
dispatcher::register_section_extractor(extractor.clone());
fv::register_section_extractor(extractor);
}
log::info!("Parsing FVs from FV HOBs");
fv::parse_hob_fvs(&self.hob_list)?;
log::info!("Finished.");
log::info!("Dispatching Drivers");
self.core_dispatcher()?;
self.storage.lock_configs();
self.core_dispatcher()?;
log::info!("Finished Dispatching Drivers");
self.display_components_not_dispatched();
core_display_missing_arch_protocols();
dispatcher::display_discovered_not_dispatched();
call_bds();
log::info!("Finished");
Ok(())
}
}
const ARCH_PROTOCOLS: &[(uuid::Uuid, &str)] = &[
(uuid::uuid!("a46423e3-4617-49f1-b9ff-d1bfa9115839"), "Security"),
(uuid::uuid!("26baccb1-6f42-11d4-bce7-0080c73c8881"), "Cpu"),
(uuid::uuid!("26baccb2-6f42-11d4-bce7-0080c73c8881"), "Metronome"),
(uuid::uuid!("26baccb3-6f42-11d4-bce7-0080c73c8881"), "Timer"),
(uuid::uuid!("665e3ff6-46cc-11d4-9a38-0090273fc14d"), "Bds"),
(uuid::uuid!("665e3ff5-46cc-11d4-9a38-0090273fc14d"), "Watchdog"),
(uuid::uuid!("b7dfb4e1-052f-449f-87be-9818fc91b733"), "Runtime"),
(uuid::uuid!("1e5668e2-8481-11d4-bcf1-0080c73c8881"), "Variable"),
(uuid::uuid!("6441f818-6362-4e44-b570-7dba31dd2453"), "Variable Write"),
(uuid::uuid!("5053697e-2cbc-4819-90d9-0580deee5754"), "Capsule"),
(uuid::uuid!("1da97072-bddc-4b30-99f1-72a0b56fff2a"), "Monotonic Counter"),
(uuid::uuid!("27cfac88-46cc-11d4-9a38-0090273fc14d"), "Reset"),
(uuid::uuid!("27cfac87-46cc-11d4-9a38-0090273fc14d"), "Real Time Clock"),
];
fn core_display_missing_arch_protocols() {
for (uuid, name) in ARCH_PROTOCOLS {
let guid = efi::Guid::from_bytes(&uuid.to_bytes_le());
if protocols::PROTOCOL_DB.locate_protocol(guid).is_err() {
log::warn!("Missing architectural protocol: {uuid:?}, {name:?}");
}
}
}
fn call_bds() {
match protocols::PROTOCOL_DB.locate_protocol(status_code::PROTOCOL_GUID) {
Ok(status_code_ptr) => {
let status_code_protocol = unsafe { (status_code_ptr as *mut status_code::Protocol).as_mut() }.unwrap();
(status_code_protocol.report_status_code)(
EFI_PROGRESS_CODE,
EFI_SOFTWARE_DXE_CORE | EFI_SW_DXE_CORE_PC_HANDOFF_TO_NEXT,
0,
&patina::guids::DXE_CORE,
ptr::null(),
);
}
Err(err) => log::error!("Unable to locate status code runtime protocol: {err:?}"),
};
if let Ok(protocol) = protocols::PROTOCOL_DB.locate_protocol(bds::PROTOCOL_GUID) {
let bds = protocol as *mut bds::Protocol;
unsafe {
((*bds).entry)(bds);
}
}
}