#![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;
use alloc::boxed::Box;
mod allocator;
mod component_dispatcher;
mod config_tables;
mod cpu;
#[cfg(feature = "debugger_reload")]
mod debugger_reload;
mod decompress;
mod driver_services;
mod dxe_services;
mod event_db;
mod events;
mod filesystems;
mod gcd;
mod memory_attributes_protocol;
mod memory_manager;
mod misc_boot_services;
mod pecoff;
mod pi_dispatcher;
mod protocol_db;
mod protocols;
mod runtime;
mod systemtables;
mod tpl_mutex;
#[cfg(test)]
pub use {component_dispatcher::MockComponentInfo, cpu::MockCpuInfo};
pub use component_dispatcher::{Add, Component, ComponentInfo, Config, Service};
pub use cpu::{CpuInfo, GicBases};
use spin::Once;
#[cfg(test)]
#[macro_use]
#[coverage(off)]
pub mod test_support;
#[coverage(off)]
mod core_patina_tests;
use core::{
ffi::c_void,
num::NonZeroUsize,
ptr::{self, NonNull},
};
use gcd::SpinLockedGcd;
use memory_manager::CoreMemoryManager;
use patina::{
boot_services::StandardBootServices,
component::IntoComponent,
error::{self, Result},
pi::{
hob::{HobList, get_pi_hob_list_size},
protocols::{bds, status_code},
status_code::{EFI_PROGRESS_CODE, EFI_SOFTWARE_DXE_CORE, EFI_SW_DXE_CORE_PC_HANDOFF_TO_NEXT},
},
runtime_services::StandardRuntimeServices,
};
use patina_ffs::section::SectionExtractor;
use protocols::PROTOCOL_DB;
use r_efi::efi;
use crate::{
component_dispatcher::ComponentDispatcher, config_tables::memory_attributes_table, pi_dispatcher::PiDispatcher,
tpl_mutex::TplMutex,
};
#[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));
#[used]
static DXE_CORE_VERSION: &str = env!("CARGO_PKG_VERSION");
#[cfg_attr(test, mockall::automock)]
pub trait MemoryInfo {
#[inline(always)]
fn prioritize_32_bit_memory() -> bool {
false
}
}
#[cfg_attr(test, mockall::automock(
type Extractor = patina_ffs_extractors::NullSectionExtractor;
type ComponentInfo = MockComponentInfo;
type MemoryInfo = MockMemoryInfo;
type CpuInfo = MockCpuInfo;
))]
pub trait PlatformInfo: 'static {
type MemoryInfo: MemoryInfo;
type CpuInfo: CpuInfo;
type ComponentInfo: ComponentInfo;
type Extractor: SectionExtractor;
}
static __SELF: Once<NonZeroUsize> = Once::new();
#[cfg(test)]
type MockCore = Core<MockPlatformInfo>;
pub struct Core<P: PlatformInfo> {
hob_list: Once<HobList<'static>>,
component_dispatcher: TplMutex<ComponentDispatcher>,
pi_dispatcher: PiDispatcher<P>,
}
#[coverage(off)]
impl<P: PlatformInfo> Core<P> {
pub const fn new(section_extractor: P::Extractor) -> Self {
Self {
hob_list: Once::new(),
component_dispatcher: ComponentDispatcher::new_locked(),
pi_dispatcher: PiDispatcher::new(section_extractor),
}
}
#[must_use]
fn set_instance(&'static self) -> bool {
let physical_address = NonNull::from_ref(self).expose_provenance();
&physical_address == __SELF.call_once(|| physical_address)
}
#[cfg(test)]
fn override_instance(&'static self) {
let physical_address = NonNull::from_ref(self).expose_provenance();
if __SELF.is_completed() {
unsafe {
__SELF.as_mut_ptr().write(physical_address);
}
} else {
__SELF.call_once(|| physical_address);
}
}
#[allow(unused)]
pub(crate) fn instance<'a>() -> &'a Self {
unsafe {
NonNull::<Self>::with_exposed_provenance(*__SELF.get().expect("DXE Core is already initialized.")).as_ref()
}
}
pub fn entry_point(&'static self, physical_hob_list: *const c_void) -> ! {
if !self.set_instance() {
panic!("DXE Core instance was already set!");
}
if physical_hob_list.is_null() {
panic!("DXE Core entry point called with null HOB list pointer!");
}
let relocated_hob_list = self.init_memory(physical_hob_list);
if let Err(err) = self.start_dispatcher(relocated_hob_list) {
log::error!("DXE Core failed to start: {err:?}");
}
call_bds();
}
fn set_hob_list(&self, hob_list: HobList<'static>) -> Result<&HobList<'static>> {
match self.hob_list.is_completed() {
true => Err(error::EfiError::AlreadyStarted),
false => Ok(self.hob_list.call_once(|| hob_list)),
}
}
fn hob_list(&self) -> &HobList<'static> {
self.hob_list.get().expect("HOB list should have been initialized already.")
}
fn init_memory(&self, physical_hob_list: *const c_void) -> *mut c_void {
log::info!("DXE Core Crate v{DXE_CORE_VERSION}");
GCD.prioritize_32_bit_memory(P::MemoryInfo::prioritize_32_bit_memory());
let (cpu, mut interrupt_manager) =
cpu::initialize_cpu_subsystem().expect("Failed to initialize CPU subsystem!");
gcd::init_gcd(physical_hob_list);
log::trace!("Initial GCD:\n{GCD}");
let mut hob_list = HobList::new();
hob_list.discover_hobs(physical_hob_list);
log::trace!("HOB list discovered is:");
log::trace!("{:#x?}", hob_list);
PROTOCOL_DB.init_protocol_db();
allocator::init_memory_support(&hob_list);
let pi_hob_list_size = unsafe { get_pi_hob_list_size(physical_hob_list) };
let pi_hob_slice = unsafe { core::slice::from_raw_parts(physical_hob_list as *const u8, pi_hob_list_size) };
let relocated_hob_list = Box::leak(pi_hob_slice.to_vec().into_boxed_slice()).as_mut_ptr().cast::<c_void>();
hob_list.relocate_hobs();
if self.set_hob_list(hob_list).is_err() {
panic!("HOB list was already set!");
}
patina_debugger::add_monitor_command("gcd", "Prints the GCD", |_, out| {
let _ = write!(out, "GCD -\n{GCD}");
});
#[cfg(feature = "debugger_reload")]
debugger_reload::initialize_debugger_reload(physical_hob_list);
patina_debugger::initialize(
&mut interrupt_manager,
Some(Box::leak(Box::new(cpu::PerfTimer::with_frequency(P::CpuInfo::perf_timer_frequency().unwrap_or(0))))),
);
#[cfg(feature = "debugger_reload")]
debugger_reload::tear_down_debugger_reload();
log::info!("GCD - After memory init:\n{GCD}");
let mut component_dispatcher = self.component_dispatcher.lock();
component_dispatcher.add_service(cpu);
component_dispatcher.add_service(interrupt_manager);
component_dispatcher.add_service(CoreMemoryManager);
component_dispatcher
.add_service(cpu::PerfTimer::with_frequency(P::CpuInfo::perf_timer_frequency().unwrap_or(0)));
relocated_hob_list
}
fn core_dispatcher(&'static self) -> Result<()> {
loop {
let dispatched = self.component_dispatcher.lock().dispatch();
let dispatched = dispatched
|| self
.pi_dispatcher
.dispatch()
.inspect_err(|err| log::error!("UEFI Driver Dispatch error: {err:?}"))?;
if !dispatched {
break;
}
}
Ok(())
}
fn initialize_system_table(&self, physical_hob_list: *mut c_void) -> Result<()> {
systemtables::init_system_table();
let mut st_guard = systemtables::SYSTEM_TABLE.lock();
let st = st_guard.as_mut().expect("System Table not initialized!");
allocator::install_memory_services(st);
gcd::init_paging(self.hob_list());
events::init_events_support(st);
protocols::init_protocol_support(st);
misc_boot_services::init_misc_boot_services_support(st);
config_tables::init_config_tables_support(st);
runtime::init_runtime_support();
self.pi_dispatcher.init(self.hob_list(), st);
self.install_dxe_services_table(st);
driver_services::init_driver_services(st);
memory_attributes_protocol::install_memory_attributes_protocol();
st.checksum_all();
config_tables::core_install_configuration_table(patina::guids::HOB_LIST.into_inner(), physical_hob_list, 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");
memory_attributes_table::init_memory_attributes_table_support();
let boot_services = StandardBootServices::new(st.boot_services().as_mut_ptr());
let runtime_services = StandardRuntimeServices::new(st.runtime_services().as_mut_ptr());
drop(st_guard);
self.component_dispatcher.lock().set_boot_services(boot_services);
self.component_dispatcher.lock().set_runtime_services(runtime_services);
self.component_dispatcher.lock().set_image_handle(protocol_db::DXE_CORE_HANDLE);
Ok(())
}
#[inline(always)]
fn apply_component_info(&self) {
self.component_dispatcher.lock().apply_component_info::<P::ComponentInfo>();
}
#[allow(clippy::default_constructed_unit_structs)]
fn add_core_components(&self) {
let mut dispatcher = self.component_dispatcher.lock();
dispatcher.insert_component(0, decompress::DecompressProtocolInstaller::default().into_component());
dispatcher.insert_component(0, systemtables::SystemTableChecksumInstaller::default().into_component());
dispatcher.insert_component(0, cpu::CpuArchProtocolInstaller::default().into_component());
#[cfg(all(target_os = "uefi", target_arch = "aarch64"))]
dispatcher
.insert_component(0, cpu::HwInterruptProtocolInstaller::new(P::CpuInfo::gic_bases()).into_component());
}
fn start_dispatcher(&'static self, physical_hob_list: *mut c_void) -> Result<()> {
log::info!("Registering platform components");
self.apply_component_info();
log::info!("Finished.");
log::info!("Registering default components");
self.add_core_components();
log::info!("Finished.");
log::info!("Initializing System Table");
self.initialize_system_table(physical_hob_list)?;
log::info!("Finished.");
log::info!("Parsing HOB list for Guided HOBs.");
self.component_dispatcher.lock().insert_hobs(self.hob_list());
log::info!("Finished.");
log::info!("Installing Firmware Volumes from HOB list.");
self.pi_dispatcher.install_firmware_volumes_from_hoblist(self.hob_list())?;
log::info!("Finished.");
log::info!("Dispatching Drivers");
self.core_dispatcher()?;
self.component_dispatcher.lock().lock_configs();
self.core_dispatcher()?;
log::info!("Finished Dispatching Drivers");
self.component_dispatcher.lock().display_not_dispatched();
core_display_missing_arch_protocols();
self.pi_dispatcher.display_discovered_not_dispatched();
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.into_inner()) {
Ok(status_code_ptr) => {
if let Some(status_code_protocol_ptr) = NonNull::new(status_code_ptr) {
let status_code_protocol = unsafe { status_code_protocol_ptr.cast::<status_code::Protocol>().as_ref() };
let dxe_core_guid = patina::guids::DXE_CORE.into_inner();
(status_code_protocol.report_status_code)(
EFI_PROGRESS_CODE,
EFI_SOFTWARE_DXE_CORE | EFI_SW_DXE_CORE_PC_HANDOFF_TO_NEXT,
0,
&dxe_core_guid,
ptr::null(),
);
} else {
log::error!("status_code protocol pointer is NULL")
}
}
Err(err) => log::error!("Unable to locate status code runtime protocol: {err:?}"),
}
match protocols::PROTOCOL_DB.locate_protocol(bds::PROTOCOL_GUID.into_inner()) {
Ok(bds_ptr) => {
if let Some(bds_protocol_ptr) = NonNull::new(bds_ptr) {
let bds_protocol_ptr = bds_protocol_ptr.cast::<bds::Protocol>();
unsafe {
(bds_protocol_ptr.as_ref().entry)(bds_protocol_ptr.as_ptr());
}
} else {
log::error!("bds protocol pointer is NULL")
}
}
Err(err) => log::error!("Unable to locate BDS arch protocol: {err:?}"),
};
unreachable!("BDS arch protocol should be found and should never return.");
}
#[cfg(test)]
#[coverage(off)]
mod tests {
use crate::test_support::with_global_lock;
use super::*;
use core::{any::Any, sync::atomic::AtomicBool};
#[test]
fn test_cannot_set_instance_twice() {
with_global_lock(|| {
static CORE: Core<MockPlatformInfo> =
Core::<MockPlatformInfo>::new(patina_ffs_extractors::NullSectionExtractor);
static CORE2: Core<MockPlatformInfo> =
Core::<MockPlatformInfo>::new(patina_ffs_extractors::NullSectionExtractor);
CORE.override_instance();
if NonNull::from_ref(&CORE) != NonNull::from_ref(Core::<MockPlatformInfo>::instance()) {
panic!("CORE instance mismatch");
}
assert!(CORE.set_instance());
assert!(!CORE2.set_instance());
if NonNull::from_ref(&CORE) != NonNull::from_ref(Core::<MockPlatformInfo>::instance()) {
panic!("CORE instance mismatch after second set_instance");
}
})
.unwrap();
}
#[test]
fn test_trait_defaults_do_not_change() {
struct TestPlatform;
impl MemoryInfo for TestPlatform {}
assert!(!<TestPlatform as MemoryInfo>::prioritize_32_bit_memory());
}
fn with_reset_global_state<F>(f: F) -> core::result::Result<(), Box<dyn Any + Send>>
where
F: Fn() + std::panic::RefUnwindSafe,
{
test_support::with_global_lock(|| {
unsafe {
test_support::init_test_protocol_db();
}
f()
})
}
#[test]
fn test_mock_call_bds_valid_non_null() {
static BDS_CALLED: AtomicBool = AtomicBool::new(false);
extern "efiapi" fn mock_bds(_this: *mut patina::pi::protocols::bds::Protocol) {
BDS_CALLED.store(true, core::sync::atomic::Ordering::Relaxed)
}
assert!(
with_reset_global_state(|| {
let protocol = Box::leak(Box::new(patina::pi::protocols::bds::Protocol { entry: mock_bds }));
protocols::core_install_protocol_interface(
None,
patina::pi::protocols::bds::PROTOCOL_GUID.into_inner(),
protocol as *mut _ as *mut c_void,
)
.unwrap();
call_bds();
})
.is_err_and(|err| {
err.downcast_ref::<&str>()
.unwrap()
.contains("BDS arch protocol should be found and should never return.")
})
);
assert!(BDS_CALLED.load(core::sync::atomic::Ordering::Relaxed))
}
#[test]
fn test_mock_call_bds_valid_null() {
assert!(
with_reset_global_state(|| {
protocols::core_install_protocol_interface(
None,
patina::pi::protocols::bds::PROTOCOL_GUID.into_inner(),
core::ptr::null_mut(),
)
.unwrap();
call_bds();
})
.is_err_and(|err| {
err.downcast_ref::<&str>()
.unwrap()
.contains("BDS arch protocol should be found and should never return.")
})
);
}
#[test]
fn test_mock_call_bds_invalid() {
assert!(
with_reset_global_state(|| {
call_bds();
})
.is_err_and(|err| {
err.downcast_ref::<&str>()
.unwrap()
.contains("BDS arch protocol should be found and should never return.")
})
);
}
#[test]
fn test_mock_call_status_code_valid_non_null() {
static STATUS_CODE_CALLED: AtomicBool = AtomicBool::new(false);
extern "efiapi" fn mock_status_code(
_: u32,
_: u32,
_: u32,
_: *const efi::Guid,
_: *const status_code::EfiStatusCodeData,
) -> efi::Status {
STATUS_CODE_CALLED.store(true, core::sync::atomic::Ordering::Relaxed);
efi::Status::SUCCESS
}
assert!(
with_reset_global_state(|| {
let protocol = Box::leak(Box::new(patina::pi::protocols::status_code::Protocol {
report_status_code: mock_status_code,
}));
protocols::core_install_protocol_interface(
None,
patina::pi::protocols::status_code::PROTOCOL_GUID.into_inner(),
protocol as *mut _ as *mut c_void,
)
.unwrap();
call_bds();
})
.is_err_and(|err| {
err.downcast_ref::<&str>()
.unwrap()
.contains("BDS arch protocol should be found and should never return.")
})
);
assert!(STATUS_CODE_CALLED.load(core::sync::atomic::Ordering::Relaxed))
}
#[test]
fn test_mock_call_status_code_valid_null() {
assert!(
with_reset_global_state(|| {
protocols::core_install_protocol_interface(
None,
patina::pi::protocols::status_code::PROTOCOL_GUID.into_inner(),
core::ptr::null_mut(),
)
.unwrap();
call_bds();
})
.is_err_and(|err| {
err.downcast_ref::<&str>()
.unwrap()
.contains("BDS arch protocol should be found and should never return.")
})
);
}
#[test]
fn test_mock_call_status_code_invalid() {
assert!(
with_reset_global_state(|| {
call_bds();
})
.is_err_and(|err| {
err.downcast_ref::<&str>()
.unwrap()
.contains("BDS arch protocol should be found and should never return.")
})
);
}
}